This Multics source file was rescued from the messed-up source archive at MIT.

Fault Interceptor Module. This program is entered from the hardware fault vector when the CPU takes a fault.

Back to Multics Source index.

            fim.alm                         11/17/83  0850.4r   11/17/83  0846.2      361530



" FIM - Multics Fault Intercept Module.
"
"   Modification record: (Date and Reason)
"   13 October 1982 (Wednesday, this month) by E. N. Kittlitz for instruction size
"   06 October 1982 by BIM to check for signal_entry fault
"   in fim, running fim in collection 1.
"   24 March 1983 by J. A. Bush for 16/32K cache parity data capture
"   6 April 1983 by E. N. Kittlitz DRL in ring 0 causes crash.
"   sometime by BIM for bootload multics.
"   17 July 1981 by M. Weaver for undefined_pointer
"   04 June 81 by J. A. Bush to fix some unreported bugs
"   3 April 81 by Benson I. Margulies for null_pointer
"   17 Jan 81 by J. Bongiovanni for fault_counters
"   27 August 80 by J. A. Bush for the DPS8/70M
"   25 march 80 by J. A. Bush to fix negative exp. overflow bug
"   15 Jan 80 by J. A. Bush for cache parity error data capture
"   24 July 79 by J. A. Bush for new signal_entry and to merge fim_table  back in.
"   1 Feb 79 by D. Spector for new scs format for 8-cpu cioc
"   2/8/76 by Noel I. Morris for new reconfig
"   10/14/75 by R. Bratt for prelinking
"   6/20/75 by S.Webber for static handlers
"   3/75 by S. Webber for new restarting conventions and returning
"       history registers to users.
"   5/29/74 by M. Weaver to special case out of bounds on stack
"   11/1/73 by Steve Webber to allow truncation (stringsize) faults to be restarted
"   10/15/73 by Steve Webber to correct some illegal procedure "fanout" mapping problems.
"   07/21/71 by Richard H. Gumpertz to use prds_link to get prds segment number
"   5/30/70     - Noel I. Morris


"  ******************************************************
"  *                                                    *
"  *                                                    *
"  * Copyright (c) 1972 by Massachusetts Institute of   *
"  * Technology and Honeywell Information Systems, Inc. *
"  *                                                    *
"  *                                                    *
"  ******************************************************




" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
" FIM is transferred to as the result of a fault. The control
" unit data has been stored either in pds$fim_data or pds$signal_data.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "


    name    fim
    inhibit on  <+><+><+><+><+><+><+><+><+><+><+><+>


" 

    link    prds_link,prds$+0



    bool    lprp_insts,760000
    bool    minus_128,400000
    bool    dp_opcode,2000
    bool    inst_bit27_on,400
    bool    op_code_mask,777400
    bool    fstr_inst,470000
    bool    dfstr_inst,472000
    bool    cu_hreg,40
    bool    apu_hreg,140


    tempd   v_temp      temporary for virtual time computation
    tempd   v_delta     temporary to remember virtual_delta
    tempd   pad(2)
    temp8   mach_cond(6)    machine conditions
    temp8   history_registers(16)
    tempd   mc_ptr      pointer to machine conditions
    tempd   call_ptr        pointer to fault handling routine
    tempd   arglist(4)  argument list
    temp    fcode       fault code
    temp    temp(3)     temporary cells

" 

" ACCESS_VIOLATION_ENTRY - Handle Access Violation Faults.

    segdef  access_violation_entry

    even
access_violation_entry:
    spri    prs,*           save prs
    eppbp   prs,*           bp -> machine conditions
    sreg    bp|mc.regs      save registers
    epplp   lp,*            set lp value
    spl bp|mc.eis_info      save eis ptrs and lengths
    tsx0    fim_util$fim_v_time_init    remember accrued virtual time
    tsx0    fim_util$check_fault  is it OK to take this fault?

    epaq    *       get our segment number
    era bp|mc.scu.ppr.psr_word  compare against procedure segment register
    ana scu.ppr.psr_mask,du were we in the fim?
    tze die_die_die_    suicide

    tra fault_join
" 
" ONC_START_SHUT_ENTRY - entry to handle op-not-complete, startup and shutdown faults

    segdef  onc_start_shut_entry

    even
onc_start_shut_entry:
    spri    prs,*           save prs
    eppbp   prs,*           bp -> machine conditions
    sreg    bp|mc.regs      save registers
    epplp   lp,*            set lp value
    spl bp|mc.eis_info      save eis ptrs and lengths
    tsx0    fim_util$fim_v_time_init    remember accrued virtual time

    tsx0    fim_util$check_fault    kill Multics if not legal fault
    eppbb   pds$history_reg_data    bb -> place to store history regs 
    tsx0    fim_util$check_mct      go save M.C.'s an hregs if required
    tsx0    fim_util$force_hist_regs    (ret ic +1) force save hregs in pds 
    tra onc_par_join        go join common code
"
" drl_entry - entry to handle derail faults, special case of signal_entry.

    segdef  drl_entry

    even
drl_entry:
    spri    sig_prs,*       save prs
    eppbp   sig_prs,*       bp -> machine conditions
    sreg    bp|mc.regs  save registers
    epplp   lp,*        set lp value
    spl bp|mc.eis_info  what the hell...
    ldq bp|mc.scu.ppr.prr_word check whether running in ring-0
    canq    scu.ppr.prr_mask,du ring number in ppr
    tnz drl_join        not ring 0

    stz scs$drl_message_pointer assume the worst
    lda bp|mc.scu.ilc_word
    eppbb   0,au        offset of DRL 
    lda bp|mc.scu.ppr.psr_word
    ana scu.ppr.psr_mask,du
    easpbb  0,au        segno of DRL
    lda bb|0        get the DRL instruction
    ana =o777777,du

    tze *+3     no operand
    eawpbb  0,au        operand of DRL instruction
    sprpbb  scs$drl_message_pointer
    tra fim_util$drl_fault_trouble punt ye system
"
" parity_entry - entry to handle parity faults

    segdef  parity_entry
    even
parity_entry:
    lcpr    cache_off,02        turn cache off in case this is cache parity
    spri    prs,*           save prs
    eppbp   prs,*           bp -> machine conditions
    sreg    bp|mc.regs      save registers
    epplp   lp,*            set lp value
    spl bp|mc.eis_info      save eis ptrs and lengths
    tsx0    fim_util$fim_v_time_init    remember accrued virtual time
    eppbb   pds$history_reg_data    bb -> place to store history regs 
    tsx0    fim_util$check_mct      go save M. C.'s and hregs if required
    tsx0    fim_util$force_hist_regs    (ret ic +1) force save hregs in pds
    lda bp|mc.fault_reg     load fault reg
    cana    12,dl           is it cache store or cache dir parity?
    tnz csd_par         xfer if yes
    tsx0    fim_util$check_fault    no, kill Multics if parity in hardcore etc.
    tra onc_par_join        and join common code

csd_par:    stz pds$cpar_info       initialize cache parity info structure
    cana    4,dl            is it cache store parity?
    tnz cstr_par            xfer if yes
    tsx0    fim_util$check_fault    no cache dir parity
    tsx0    save_cache      go save cache enable bits
    tra onc_par_join        and join common code

cstr_par:   lda bp|mc.scu.cpu_no_word   Get the cpu number in A
    ana scu.cpu_no_mask,dl
    arl scu.cpu_shift       right justify in AL
    ldx1    scs$processor_data,al   Get cache size
    anx1    7,du            in X1
    eax1    -1,1            can't have 0 size at this point
    epaq    fim_abs_seg$        get ptr to abs seg
    als 1           multiply by 2
    eax7    0,au            copy segno to x7
    eppap   fim_abs_seg$        set ap with abs_seg ptr
    eax2    15*2            set initial cu hreg index
cuhrlp: eax5    15*2            set initial apu hreg index
    tsx0    fnd_crd         go check for cache read cycle
    tra apuhrlp         return ic+1, cache read (L68)
    tra dps8_hit            return ic+2, cache read (DPS8)
dec_cux:    eax2    -2,2            return ic+3, not cache read
    tmi no_find         exit if no more cu hregs
    tra cuhrlp          go get next cu hreg

apuhrlp:    ldaq    bb|apu_hreg,5       load next apu hreg
    cana    apuhra.fap+apuhra.fanp,dl   is this final address (fanp or fap)?
    tze dec_apx         xfer if no, skip to next apu hreg
    qrl apuhra.finadd_shift     right justify abs addr
    anq =o777,dl            and out upper addr bits
    cmpq    bp|mc.fim_temp      is this the same as cu addr?
    tze addmatch            xfer if yes
dec_apx:    eax5    -2,5            no, go to nxt apu hreg
    tmi dec_cux         get nxt cu hreg is apu hregs exhausted
    tra apuhrlp         get nxt apu hreg

addmatch:   lda bb|apu_hreg+1,5     matching cu and apu address
    cana    apuhra.encache,dl       is segment encacheable?
    tze dec_cux         xfer if no, go to nxt cu/apu match
dps8_hit:                   " Enter here if dps8 cache read
    arl 12          zero out all but 24 bit address
    sta pds$cpar_info       and save
    eax6    0,al            copy lower 256k address to x6
    anx6    c_256k_mask,1       make mod <cache size> address
    ana c_abs_addr_mask,1       make absaddr mod <cache size>
    als 12          shift into position
    ora sdw.valid,dl        set directed fault bit
    ldq c_sdw_bounds,1      set up rest of sdw
    staq    dseg$,7         set sdw in dseg
    cams                "clear associative memory
    camp
    ldaq    ap|0,6          get memory contents
    staq    pds$cpar_mem_data       and save
    anx6    c_lv_st_mask,1      make address start at level 0
    eax3    -4          x3 counts levels
cparlvlp:   ldi scu.ir.parm,dl      set parity mask
    lcpr    cache_to_reg_mode,02    set cache dump mode
    ldaq    ap|0,6          dump cache contents
    lcpr    cache_off,02        reset cache dump mode
    sti bp|mc.fim_temp      save indicators
    eraq    pds$cpar_mem_data       exclusive or it with data in mem
    staq    pds$cpar_err_data       save it
    ldi 0,dl            reset parity mask
    lda bp|mc.fim_temp      load indicators
    cana    scu.ir.par,dl       parity error?
    tnz cpar_hit            xfer if yes
nxt_lvl:    adx6    c_lv_inc_tab,1      get set for nxt level
    eax3    1,3         any more levels?
    tmi cparlvlp            yes, go to nxt level
    tra dec_cux         no, go check previous read

cpar_hit:   ldi scu.ir.oflm,dl      mask overflows
    lcaq    pds$cpar_err_data       get compliment of XOR data
    ldi 0,dl            reset overflow mask
    anaq    pds$cpar_err_data       and 2s compliment data with itself
    cmpaq   pds$cpar_err_data       if single bit failure, it should compare
    tnz nxt_lvl         multiple bit failure, go try nxt level
    ldi scu.ir.parm,dl      set parity mask
    lcpr    cache_to_reg_mode,02    set cache dump mode
    ldaq    ap|0,6          dump cache contents
    lcpr    cache_off,02        reset cache dump mode
    staq    pds$cpar_err_data       save it
    ldi 0,dl            reset parity mask
    eaa 0,6         copy level info to a
    arl c_lv_shift,1        position per cache size
    ana =o300,du            and out all but level
    ora =o400000,du     set flag for data capture
    orsa    pds$cpar_info       and save
no_find:    lda 0,du
    ldq 0,du            zero out abs seg sdw
    staq    dseg$,7
    cams    4           clear assosiative memory and cache
    camp
    tsx0    save_cache      go save cache enable bits
    tsx0    fim_util$check_fault    now go check to see if fault in hardcore
    tra onc_par_join        and go join common code
" 
" signal_entry - entry to handle faults that are directly signalable
" in the outer rings (not ring 0)

    segdef  signal_entry

    even
signal_entry:
    spri    sig_prs,*           save prs
    eppbp   sig_prs,*           bp -> machine conditions
    sreg    bp|mc.regs      save registers
    epplp   lp,*            set lp value
    spl bp|mc.eis_info      save eis ptrs and lengths
drl_join:
    tsx0    fim_util$fim_v_time_init    remember accrued virtual time
    tsx0    fim_util$check_fault    kill Multics if not legal fault
    lda bp|mc.scu.fault_data_word
    ana scu.fi_num_mask,dl      get fault address
signal_it:
    sta bp|mc.errcode       temporarily save fault code
    arl 1           divide by 2
    aos wired_hardcore_data$fault_counters,al   increment fault counter
    als 1           multiply by 2
    ldaq    fault_table,al      pick up table entry
    anq audit_flags.mask,dl     do we need to audit this event?
    tze no_sig_audit        xfer if no.
    qls 18          put audit flags in upper half
    canq    pds$audit_flags     check appropriate audit flag
    tze no_sig_audit        xfer if not auditing
    tsx1    call_audit      yes, go do it
no_sig_audit:
    tra 0,au            dispatch on entry value

call_signaller:             "most signalable faults come here directly
    eppbb   pds$history_reg_data    bb -> place to store history regs 
    tsx0    fim_util$check_mct      go save M. C.'s and hregs if required
    tsx0    fim_util$hist_regs      (ret ic +1) save hregs in pds
    tsx0    fim_util$reset_mode_reg     (ret ic +2) turn them back on now
    lda bp|mc.errcode       get fault code for retrieving name
    arl 1           divide by 2
    sta bp|mc.fim_temp      save the sct index for signal_
    als 1           multiply by 2
    stz bp|mc.errcode       initialize error code
    lda fault_table+1,al        pick up second word of fault table entry
    eppbb   0,au            get pointer to name from its left half word
    eppab   pds$condition_name      get pointer to place to store name
    ldq bb|0            get first word of string for ACC size
    qrl 27          right justify size of string
    adlq    1,dl            add 1 for count character
    mlr (pr,rl),(pr),fill(0)
    desc9a  bb|0,ql
    desc9a  ab|0,32
    eppsb   pds$stack_0_ptr,*       set SB to the ring 0 stack
    tra signaller$signaller     now give control to the signaller
"
" PRIMARY_FAULT_ENTRY - Most faults arrive at this entry.

    segdef  primary_fault_entry

    even
primary_fault_entry:
    spri    prs,*           save prs
    eppbp   prs,*           bp -> machine conditions
    sreg    bp|mc.regs      save registers
    epplp   lp,*            set lp value
    spl bp|mc.eis_info      save eis ptrs and lengths
    tsx0    fim_util$fim_v_time_init    remember accrued virtual time
    tsx0    fim_util$check_fault    kill Multics if not legal fault
fault_join:
    eppbb   pds$history_reg_data  bb -> place to store history regs 
    tsx0    fim_util$check_mct  go save M.C.'s an hregs if required
    tsx0    fim_util$hist_regs  (ret ic +1) save hregs (if not saved by check_mct)
onc_par_join:
    tsx0    fim_util$reset_mode_reg   (ret ic +2) turn them back on now
    eppsb   pds$stack_0_ptr,*   make FIM's stack frame on the ring 0 stack
    ldx7    push        ..
    tsx0    fim_util$push_stack ..
    eppap   mach_cond       ap -> place for machine conditions
    tsx0    fim_util$copy_mc    copy machine conditions into stack frame
    spribp  mc_ptr      set mc_ptr in stack frame
    ldaq    pds$fim_v_temp  save CPU time at start of fault
    staq    v_temp      ..
    ldaq    pds$fim_v_delta save virtual delta at start of fault
    staq    v_delta     ..
    szn pds$hregs_saved     do we want to copy hregs?
    tze no_hist
    eppap   history_registers   bb -> hregs either in mc_trace buffer or pds
    mlr (pr),(pr)       copy hregs into stack frame
    desc9a  bb|0,128*4
    desc9a  ap|0,128*4
no_hist:    inhibit off <-><-><-><-><-><-><-><-><-><-><-><->

" Dispatch on Fault Code.

    stz bp|mc.errcode   clear the error code
    lda bp|mc.scu.fault_data_word get fault number
    ana scu.fi_num_mask,dl
set_fault:
    sta fcode       save the fault code
    arl 1       divide by 2
    aos wired_hardcore_data$fault_counters,al
    sta bp|mc.fim_temp  save the fault code in the MC
    als 1       multiply by 2
    ldaq    fault_table,al  pick up table entry
    anq audit_flags.mask,dl get special action code for this fault
    tnz audit_ck        we need to audit this event
normal_set_fault:
    tra 0,au        dispatch on entry value

" 

audit_ck:
    qls 18      put audit flags in upper half
    canq    pds$audit_flags Check appropriate audit flag
    tze normal_set_fault    Not auditing, go back
    eppap   mc_ptr      Get machine conditions pointer
    spriap  arglist+2       Stuff it in arg list
    fld =1b24,dl        One argument
    ora =o4,dl      Make arglist header
    staq    arglist     Stuff it where it belongs
    call    protection_audit_$fault(arglist)  Call fault entry in auditor
    lda fcode       Pick up the fault code again
    lda fault_table,al  Pick up the table entry
    tra normal_set_fault    Go back

"

"   Set up the call to the appropriate handler.
"
"   The right half of the accumulator contains an offset
"   in the fim's linkage section of the procedure to call
"   to handle the fault.
"
"   All handlers are called in the following way:
"
"   call handler (mc_ptr)
"
"   where mc_ptr points to the machine conditions, not the
"   SCU data.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "


call_signaller_signal:
    arg call_signaller
    arg call_signal

standard.signal_check_coll:
    eax0    1
    tra standard.check_common

standard.signaller_check_coll:
    eax0    0
standard.check_common:
    ldq sys_info$initialization_state
    cmpq    1,dl
    tmoz    call_signaller_signal,x0*

standard:
    eppap   lp|0,al*        generate pointer to the handler
    spriap  call_ptr        save the handler pointer

    fld =1b24,dl        set up argument list header (1 argument)
    ora =o4,dl
    staq    arglist

    eppap   mc_ptr      set up argument list (1st arg is mc_ptr)
    spriap  arglist+2

    tra call

" 
"   Access Violation faults
"
"   All access violation subcases are separated out
"   in the next sequence of code. Some are signalled, some are handled
"   by the system.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "


access_violation:
    lda bp|mc.scu.fault_data_word get sub-class of access violation fault
    ana -1,du       leave only sub-class bits
    eax7    0        Determine which is highest bit on.
acv_bit_loop:
    cana    =o400000,du  Test bit.
    tnz acv_bit_end

    eax7    2,7      On to next bit.
    als 1
    tnz acv_bit_loop     Unless all bits off.

acv_bit_end:
    eaa avf,7   generate simulated code word
    cmpa    oob,du  see if bound fault (if so make special checks)
    tze check_special_oob   yes, make tests

    tra fixindex_1  must be normal access violation of some type

check_special_oob:
    ldq bp|mc.scu.tpr.tsr_word "fetch the TSR
    anq scu.tpr.tsr_mask,du leave only segment number
    eax7    null        get set for simfault
    cmpq    =o077777,du is it a null pointer?
    tze fixindex        yes, handle the null pointer.
    cmpq    =o077776,du is a terminate process?
    tze term_it     yes, go handle it
    eax7    undefp      get set for undefined_pointer
    cmpq    =o077775,du is it an undefined pointer?
    tze fixindex        yes, handle it
    eax7    stk_oob     get set for out_of_bounds on stack
    erq bp|mc.prs+6*2   exclusive or  stack segno with  tsr
    anq =o077777,du set indicators to note any difference
    tze fixindex        no,  call handler to extend stack
    tra fixindex_1  not special oob, treat as real oob


fixindex:
    eaa 0,7     new index in A
fixindex_1:
    sbla    fault_table,du  get relative address
    arl 18      right-justify
    tra set_fault       back to the dispatch table

"
"   Command faults
"
"   The command fault could be either hardware oriented, configuration
"   oriented, or an attempt to load a faulting packed pointer.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "


command_fault:
    eax7    com assume normal command fault
    ldq bp|mc.scu.port_stat_word check illegal action
    canq    scu.ial_mask,dl
    tnz fixindex        nonzero, treat as normal case
    ldq bp|scu.even_inst_word check for LPRPxx instruction
    anq =o770400,dl
    cmpq    lprp_insts,dl
    tnz fixindex        isn't LPRPxx, treat as normal

"   It is packed pointer fault. See if system version.

    eax7    ppf 
    ldq bp|mc.scu.ca_word   pick up faulting pointer pair
    lda bp|mc.scu.tpr.tsr_word
    ana scu.tpr.tsr_mask,du
    easp3   0,au        generate pointer
    eawp3   0,qu
    lda bb|0        pick up the packed pointer
    cana    =o100000,du check system bit
    tze fixindex        not ON. Treat as normal packed pointer fault
    ana =o000777,du
    cmpa    3,du
    tpl fixindex
    als 1       multiply by 2
    eax7    sppf,au
    tra fixindex
" 
"   Overflow faults
"
"   The overflow fault could result from one of:
"
"   fixedoverflow
"   exponent overflow
"   exponent underflow
"   stringesize (EIS truncation fault)
"
"
"   The following code looks at the indicators to see what kind
"   of overflow fault occured and to remap the fault into the
"   corresponding name.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "


    inhibit on  <+><+><+><+><+><+><+><+><+><+><+><+>
split_overflow:
    ldq bp|mc.scu.indicators_word   get the indicators and ilc
    canq    scu.ir.ovfl,dl      is it "fixedoverflow"?
    tnz ck_eis_size     yes go check for eis size.
    canq    scu.ir.eufl,dl      is it "underflow"?
    tze ck_eovfl            no, go check for exp overflow
    eax7    eufl            yes, set exp underflow index
    tsx1    ck_str_rd           go get op_code from object seg
    tra sig_index           return (ic+1), don't restart EIS
    tra set_und_rfi     return (ic+2) don't increment ic
    adlq    1,du            return (ic+3) increment ic
    stq bp|mc.scu.ilc_word      by one and restore ic word
set_und_rfi:
    lda scu.cu.rfi+scu.cu.if,dl set rfi and if bits
    orsa    bp|mc.scu.cu_stat_word  in cu status word
    stz bp|mc.regs+4        zero out saved a
    stz bp|mc.regs+5        and q regs
    lda minus_128,du        and set e reg to -128
    sta bp|mc.regs+6
    szn pds$exp_undfl_rest      restart fault in the fim?
    tze sig_index           xfer if no, go signal fault
    lda bp|mc.scu.indicators_word   get indicator reg
    ana =o177777,dl     and out negative indicator
    ora scu.ir.zero,dl      and set zero indicator
    stba    bp|mc.scu.indicators_word,14    and restore indicators
und_ovr_rest:
    lca scu.ir.eufl+scu.ir.eovf+1,dl    reset exp over/under flow indicators
    ansa    bp|mc.scu.indicators_word
    tsx0    fim_util$reset_mode_reg turn hregs back on
    sznc    pds$connect_pending     is connect pending?
    tze no_con_pend     if not, skip next
    lxl1    prds$processor_tag      CPU tag in X1
    cioc    scs$cow_ptrs,1*         do a connect now
    lra =0          prevent ring alarm before cioc takes
no_con_pend:
    lreg    bp|mc.regs      restore the registers
    lpri    bp|mc.prs           and the prs
    rcu sig_scu,*           return to faulting location

ck_eovfl:
    canq    scu.ir.eovf,dl      is it "overflow"?
    tze ck_trunc            no, go check truncation
    eax7    eovf            yes, load table index
    tsx1    ck_str_rd           check for fstr/dfstr instr
    tra sig_index           return (ic+1), don't restart EIS
    tra ck_ovfl_rest        return (ic+2), don't increment ic
    adlq    1,du            return (ic+3) increment ic
    stq bp|mc.scu.ilc_word      by one and restore ic word
ck_ovfl_rest:
    szn pds$exp_ovfl_rest       restart fault in fim?
    tze sig_index           xfer if no, go signal fault
    canx5   dp_opcode,du        yes, is this double precision?
    tnz doub_prec           xfer if yes
    fld pds$eovfl_value     no, load sp restart value
    tra str_rest_vlu        and go store it

doub_prec:
    dfld    pds$eovfl_value     load dp restart value
str_rest_vlu:
    szn bp|mc.regs+4        is this a negative overflow?
    tpl no_negate           no, store value as is
    fneg    0           yes, negate the value
no_negate:
    staq    bp|mc.regs+4        and store it
    ste bp|mc.regs+6        store exponent
    lda scu.cu.rfi+scu.cu.if,dl set rfi and if bits
    orsa    bp|mc.scu.cu_stat_word  in cu status word
    tra und_ovr_rest        go restart overflow

ck_eis_size:
    eax7    ovfl            set up for "fixedoverflow"
    canq    scu.ir.mif,dl       is it EIS fault?
    tnz ck_sz_loc           xfer if yes
sig_index:
    eaa 0,7         copy  fault index to a
    sbla    fault_table,du      get relative address
    arl 18          right justify
    tra signal_it           and go signal fault

ck_sz_loc:
    lda bp|scu.even_inst_word   is this potenialy eis instr?
    cana    inst_bit27_on,dl        if bit 27 is not on, can't be eis inst
    tze sig_index           no, signal fixedoverflow
    eax7    size_loc            set size fault index
    tra sig_index           and go signal it

ck_trunc:
    canq    scu.ir.tru,dl       is it truncation?
    tze fim_util$fault_trouble  hardware error id none of above
    eax7    trun            yes, set truncation fault index
"
"   We have a "stringsize" condition. Set up the machine
"   conditions so we can restart the instruction.
"
    szn pds$pl1_machine     can we alter apparent machine ops?
    tze sig_index           xfer if no, bare 6180 machine
    tsx1    get_instruction_size
    adla    bp|mc.scu.ilc_word      increment ic by eis instr size
    sta bp|mc.scu.ilc_word      and restore ic word
    lca scu.ir.mif+1,dl     we must turn OFF this indicator so the next
    ansa    bp|mc.scu.indicators_word   EIS instruction will be correctly initialized
    lda scu.cu.rfi+scu.cu.if,dl set rfi and if bits
    orsa    bp|mc.scu.cu_stat_word  in cu status word
    tra sig_index           go signal "stringsize"

"
"   Illegal procedure faults
"
"   There are several subcases which are distinguished
"   They are:
"       illegal op code
"       illegal address and modifier
"       other illegal procedure faults
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "


illegal_procedure:

    epaq    *       get our segment number
    era bp|mc.scu.ppr.psr_word  compare against procedure segment register
    ana scu.ppr.psr_mask,du were we in the fim?
    tze die_die_die_    suicide
    lda bp|mc.scu.fault_data_word get the fault dataister
    eax7    foc get set for illegal op code
    cana    scu.fd.ioc,du
    tnz sig_index       yes, use the given index

    eax7    bam get set for bad modifier or address
    cana    scu.fd.ia_im,du
    tnz sig_index

    eax7    ipr get set for all other illegal procedure
    tra sig_index
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   fnd_crd - subroutine to check for cache read cycle in current CU history reg
"   for cache parity diagnostics.
"   return is ic+1, if L68 cache read cycle,
"       ic+2, if dps8 cache read cycle (A reg contains abs addr, in upper)
"       ic+3, if not a cache read cycle
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

fnd_crd:    ldaq    bb|cu_hreg,2        load next cu hreg
    cmpx4   1,du            is this a dps8 cpu?
    tze cu_dps8         xfer if yes, otherwise  L68
    canq    cuhra.dir,dl        is this direct cu cycle?
    tnz 2,0         xfer if yes, skip to next cu hreg
    lls 18          position addr in al
    anq =o760000,du     mem cmd in qu
    tze l68_rd_cmd      xfer if single precision read
    cmpq    =o40000,du      no, is it D.P read?
    tze l68_rd_cmd      xfer if yes
    tra 2,0         not cache read, return ic+3

l68_rd_cmd:
    ana =o777,dl            discard upper address
    sta bp|mc.fim_temp      save address
    tra 0,0         return ic+1, L68 cache read

cu_dps8:    cana    cuhr.pia,du     dps8 pia cycle?
    tze 2,0         No, return ic+3
    cana    cuhr.internal,du        dps8 cache cycle?
    tze 2,0         No, return ic+3
    lda bb|cu_hreg+1,2      got a hit, load abs addr in A
    tra 1,0         return ic+2, DPS8 cache read

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   save_cache - subroutine to save current state of cache memory enable bits
"   (csh1 and csh2) located in the prds$cache_luf_reg and then reset the bits
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

save_cache:
    lda prds$cache_luf_reg      get current cache enable bits
    ana =o600000,dl     and out all but csh1 and csh2
    als 10          position
    orsa    pds$cpar_info       and save in pds
    lda 3,dl
    ansa    prds$cache_luf_reg      turn off all cache enable bits
    tra 0,0         return to caller

"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   ck_str_rd - subroutine to check if faulting instruction is a
"   FSTR or DFSTR by looking in the object segment at psr|ic.
"   entry:  tsx1    ck_str_rd
"   in regs:    QR =    ilc,indicator word from scu data
"       BP ->    machine conditions
"   out regs:   QR  is unchanged
"       x5  =   instruction op code
"   return: ic+1 if bit 27 is on (EIS instruction)
"       ic+2 if instruction is fstr or dfstr instruction
"       ic+3 if any other instruction
"
"   NOTE:
"   Theory of touching the text segment, as of 82-10-26.
"   Yes, we might take a page or segment fault on the load
"   of the instruction word.  However, it is VERY unlikely, given
"   selection algorithms for evicting a page/segment.
"   Further, the SDW and PTW are almost certainly in our AM, and
"   we haven't been listening to any connects to get them out.
"   If we take a seg_fault, we'll be using fim_data for machine
"   conditions, and the only fim routines that call this routine
"   are supposed to have come in using signal_data/signal_entry.
"   Thus, we don't have to worry about our data getting clobbered.
"   If we end up taking a seg-fault error (seg deleted), or
"   a page-fault error, we're just going to signal that error
"   anyhow. For anything except the original fault being in
"   ring zero, this means crawlout.  
"   For ring zero, we would conceivably have to worry about
"   someone having a handler for such an error and actually
"   wanting to restart the original fault. "It'll never happen."
"
"   All this makes us think that it isn't worth the expense
"   of moving the machine conditions to a stack frame before
"   touching the text instruction.  
"   
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

ck_str_rd:
    lda bp|mc.scu.ppr.psr_word
    ana scu.ppr.psr_mask,du
    eppab   0,qu            set word offset, pr.ringno=0
    easpab  0,au            get segno from psr
    lxl5    ab|0            load instruction op code in x5
    anx5    op_code_mask,du     just look at op code
    canx5   inst_bit27_on,du        is this an EIS instruction?
    tnz 0,1         yes, return ic+1
    cmpx5   fstr_inst,du        fstr instruction?
    tze 1,1         return ic+2 if yes
    cmpx5   dfstr_inst,du       no, dfstr instruction?
    tze 1,1         return ic+2 if yes
    tra 2,1         no, return ic+3

"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   get_instruction_size - subroutine to determine the size of an instruction
"   by looking in the object segment at psr|ic.
"   entry:  tsx1    get_instruction_size
"   in regs:    QR =    ilc,indicator word from scu data
"       BP ->    machine conditions
"   out regs:   QR  is unchanged
"       X5  =   instruction op code
"       AU =    instruction size 
"
"   See the NOTE in check_str_rd.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

get_instruction_size:
    lda bp|mc.scu.ppr.psr_word
    ana scu.ppr.psr_mask,du
    eppab   0,qu            get word offset from ilc, set PRab.ring = 0
    easpab  0,au            get segno from psr
    lxl5    ab|0            load instruction op code in x5
    anx5    op_code_mask,du     just look at op code
    lda 1,dl            default to 1 word
    canx5   inst_bit27_on,du        is this an EIS instruction?
    tze 0,1         no, return
    eaa gis_table           table pointer

gis_loop:   cmpx5   0,au            gone far enough down the table?
    tnc gis_found           got it
    eaa 1,au            get next entry
    tra gis_loop

gis_found:
    lda 0,au            return the size
    als 18  
    tra 0,1


gis_table:
    vfd 9o/057,1/1,8/0,18/4     mve,mvne
    vfd 9o/117,1/1,8/0,18/3     csl,csr,sztl,sztr,cmpb,mlr,mrl,cmpc
    vfd 9o/127,1/1,8/0,18/4     scd,scdr,scm,scmr
    vfd 9o/157,1/1,8/0,18/1     sptr
    vfd 9o/167,1/1,8/0,18/4     mvt,tct,tctr
    vfd 9o/177,1/1,8/0,18/1     lptr
    vfd 9o/217,1/1,8/0,18/3     ad2d,sb2d,mp2d,dv2d
    vfd 9o/227,1/1,8/0,18/4     ad3d,sb3d,mp3d,dv3d
    vfd 9o/277,1/1,8/0,18/1     lsdr,sbpb0-3,ssdr,lptp
    vfd 9o/307,1/1,8/0,18/3     mvn,btd,cmpn,dtb
    vfd 9o/777,9o/777,18/1      easpN,epbpN,sareg,spl,lareg,
                    " and all the others...

"
call_audit:
    eppsb   pds$stack_0_ptr,*   make FIM's stack frame on the ring 0 stack
    ldx7    push        ..
    tsx0    fim_util$push_stack ..
    eppap   mach_cond       ap -> place for machine conditions
    tsx0    fim_util$copy_mc    copy machine conditions into stack frame
    inhibit off <-><-><-><-><-><-><-><-><-><-><-><->
    spribp  mc_ptr      set mc_ptr in stack frame
    eppap   mc_ptr      Get machine conditions pointer
    spriap  arglist+2       Stuff it in arg list
    fld =1b24,dl        One argument
    ora =o4,dl      Make arglist header
    staq    arglist     Stuff it where it belongs
    call    protection_audit_$fault(arglist)  Call fault entry in auditor
    eppap   pds$signal_data copy M.C.s back to signal data
    tsx0    fim_util$copy_mc
    inhibit on  <+><+><+><+><+><+><+><+><+><+><+><+>
    sprisp  sb|stack_header.stack_end_ptr reset stack end pointer (and pop frame)
    lda bp|mc.errcode   restore the fault code from the MC
    lda fault_table,al  and pick up table entry
    tra 0,1     and return
"
    inhibit off <-><-><-><-><-><-><-><-><-><-><-><->

" Make call to fault handler.

call:
    call    call_ptr,*(arglist) make the call


" Test for error .

    eppbp   mach_cond       bp -> machine conditions
    szn bp|mc.errcode   test for error
    tnz call_signal ..

    inhibit on  <+><+><+><+><+><+><+><+><+><+><+><+>

    eppap   prs,*       ap -> wired-down m.c.
    mlr (pr),(pr)
    desc9a  bp|mc.scu,8*4
    desc9a  ap|mc.scu,8*4

    ldaq    v_temp      restore virtual time parameters
    staq    pds$fim_v_temp  ..
    ldaq    v_delta     ..
    staq    pds$fim_v_delta ..

    lda fcode       get fault code again
    ldaq    fault_table,al  load table entry

    sprisp  sb|stack_header.stack_end_ptr reset stack end pointer

    sznc    pds$connect_pending must we do connect?
    tze no_connect  if not, skip next
    lxl1    prds$processor_tag  CPU tag in X1
    cioc    scs$cow_ptrs,1*     do a connect now
    lra =0      prevent ring alarm before connect takes
no_connect:

    canq    =o1,dl      suspend virtual time?
    tze no_v_time_meter if not, don't meter
    odd
    tsx0    fim_util$fim_v_time_calc  compute virtual delta
no_v_time_meter:

    lpl bp|mc.eis_info      save eis ptrs and lengths
    lreg    bp|mc.regs  restore the registers
    lpri    bp|mc.prs       and the prs
    rcu scu,*       return to faulting location
" 
"   Come here when time to signal the event.


call_signal:
    lda bp|mc.fim_temp  resignal?
    cana    =o400000,dl ..
    tze call_signal.1   no.

    als 1       get new fault code
    stca    fcode,07

call_signal.1:
    lxl7    fcode       get fault code for retrieving name
    lda fault_table+1,7  pick up second word of fault table entry
    eppbb   0,au        get pointer to name from its left half word
    eppab   pds$condition_name  get pointer to place to store name
    ldq bb|0        get first word of string for ACC size
    qrl 27      right justify size of string
    adlq    1,dl        add 1 for count character
    mlr (pr,rl),(pr),fill(0)
    desc9a  bb|0,ql
    desc9a  ab|0,32

    eppap   pds$signal_data
    tsx0    fim_util$copy_mc
    szn pds$hregs_saved
    tze signaller$signaller

    mlr (pr),(pr)
    desc9a  history_registers,128*4
    desc9a  ap|48,128*4
    tra signaller$signaller now give control to the signaller

" 

" Arrive Here If Fault Occurred While Running in the FIM.

die_die_die_:
    eppsb   pds$stack_0_ptr,*   get a pointer to the base of the ring 0 stack
" Try to preserve stack history in case the original fault was
" on the ring 0 stack.

    eppsp   sb|stack_header.stack_end_ptr,*  " where does it think it is?
    eax0    sp|0
    cmpx0   =o30000,du  reasonable ?
    tmoz    die_die_die_.use_end_ptr

    eppsp   sb|stack_header.stack_begin_ptr,* lay down frame at first available place
    sprisp  sb|stack_header.stack_end_ptr

die_die_die_.use_end_ptr:
    ldx7    push        get size of stack frame
    tsx0    fim_util$push_stack make frame at base of pds

    lda -1,du
    tra die_die_die_.fake_ca

term_it:
    lda bp|mc.scu.ca_word   get computed address
die_die_die_.fake_ca:
    ars 18      convert to fixed bin
    sta temp        save in stack (temporary cell)
    szn tc_data$wait_enable
    tze wired_fim$unexp_fault
    eppap   terminate_proc$terminate_proc  kill this process
    spriap  call_ptr        ..
    eppap   temp
    spriap  arglist+2
    fld =1b24,dl        one argument
    ora =o4,dl      make PL/1 calling sequence
    staq    arglist     ..
    tra call        make the call


" 

" Storage and Constants.

illegal_fault:          "transfer vector for illegal fault
    tra fim_util$fault_trouble
    inhibit off <-><-><-><-><-><-><-><-><-><-><-><->

" The following items must be filled in at
" system initialization time.

    segdef  lp,prs,scu,sig_prs,sig_scu

    even
lp: its -1,1        lp value for fim
prs:    its -1,1        pointer to pds$fim_data
scu:    its -1,1        pointer to pds$fim_data+24
sig_prs:    its -1,1        pointer to pds$signal_data
sig_scu:    its -1,1        pointer to pds$signal_data+24

push:   push            " used to get size of stack frame
cache_off:
    vfd 36/3            constant to turn off cache, luf time max
cache_to_reg_mode:
    oct 10003           constant  to dump cache to aq, luf time max
" 
" Tables indexed by cache size used for cache parity error diagnosis
 
c_sdw_bounds:           "template for last half of abs_seg sdw
    vfd 1/,o14/177,3/sdw.read,o18/sdw.unpaged  for 2k (L68) cache
    vfd 1/,o14/777,3/sdw.read,o18/sdw.unpaged  for 8k (DPS8) cache
    vfd 1/,o14/777,3/sdw.read,o18/sdw.unpaged  for 8k (DPS8 VS & SC) cache
    vfd 1/,o14/1777,3/sdw.read,o18/sdw.unpaged  for 16k (DPS8 VS & SC) cache
    vfd 1/,o14/3777,3/sdw.read,o18/sdw.unpaged  for 32k (DPS8 VS & SC) cache
c_lv_inc_tab:           " to increment cache levels
    vfd o18/1000,18/0   for 2k (L68) cache
    vfd o18/4000,18/0   for 8k (DPS8) cache
    vfd o18/4000,18/0   for 8k (DPS8 VS & SC) cache
    vfd o18/10000,18/0  for 16k (DPS8 VS & SC) cache
    vfd o18/20000,18/0  for 32k (DPS8 VS & SC) cache
c_lv_st_mask:           " to mask to level 0 for start
    vfd o18/776,18/0    for 2k (L68) cache
    vfd o18/3776,18/0   for 8k (DPS8) cache
    vfd o18/3776,18/0   for 8k (DPS8 VS & SC) cache
    vfd o18/7776,18/0   for 16k (DPS8 VS & SC) cache
    vfd o18/17776,18/0  for 32k (DPS8 VS & SC) cache
c_256k_mask:            " to mask cache addr within mod cache size
    vfd o18/3776,18/0   for 2k (L68) cache
    vfd o18/17776,18/0  for 8k (DPS8) cache
    vfd o18/17776,18/0  for 8k (DPS8 VS & SC) cache
    vfd o18/37776,18/0  for 16k (DPS8 VS & SC) cache
    vfd o18/77776,18/0  for 32k (DPS8 VS & SC) cache
c_abs_addr_mask:            " to mask abs addr to mod cache size for SDW
    vfd 12/0,o15/77774,9/0  for 2k (L68) cache
    vfd 12/0,o15/77760,9/0  for 8k (DPS8) cache
    vfd 12/0,o15/77760,9/0  for 8k (DPS8 VS & SC) cache
    vfd 12/0,o15/77740,9/0  for 16k (DPS8 VS & SC) cache
    vfd 12/0,o15/77700,9/0  for 32k (DPS8 VS & SC) cache
c_lv_shift:         " to shift level info for saving
    dec 3       for 2k (L68) cache
    dec 5       for 8k (DPS8) cache
    dec 5       for 8k (DPS8 VS & SC) cache
    dec 6       for 16k (DPS8 VS & SC) cache
    dec 7       for 32k (DPS8 VS & SC) cache


"
" Dispatch Table for fim.
"   modified 1/7/76 by D. M. Wells to add neti condition
"   modified 2/26/76 by Noel I. Morris to place in separate segment.
"   modified July 77 by T. Casey to add susp and term.
"   modified Oct 77 by T. Casey to rename them to sus_ and trm_.
"   modified 7/79 by J. A. Bush for new signal_entry of fim and to merge back into the fim



"   Each entry in the dispatch table contains two words. The first
"   word has an entrypoint in the fim to transfer to to handle the fault
"   in the left half and a pointer relative to the linkage section of
"   an external entry to call in the right half. If the left half
"   is not standard, the right half is not defined.
"   The second word contains a relative pointer to the ACC string name
"   of the condition associated with the fault in the left half. The right
"   half contains flags, which currently are used to indicate 
"   the necessity of auditing this fault
"   (one or more bits in the left 15 bits of this field).
"   The presence of the low-order bit indicates that the time
"   for processing this fault should be accrued to virtual time.
"
"   ___________________________________________
"   |                    |                    |
"   |  FIM handler       |  External handler  |
"   _|_____________________|_____________________|
"   |        |        |
"   |  ACC string ptr    |  FLAGS     |
"   _|_____________________|_____________________|
"
"
"   The following macro is used to generate entries in the dispatch table.
"   The unique string (label) is the value of the SCT index.

    macro   table
    zero    &1,&2
    vfd 18/&U,18/&4

    use names
&U: acc "&3"

    use main
"
    &end

    use names
    use main
    join    /text/main,names

" 


" External Links.

    link    am_fault_link,access_viol$am_fault
    link    ring_alarm_fault_link,ring_alarm$fault
    link    seg_fault_link,seg_fault$seg_fault
    link    hardware_fault_link,hardware_fault$hardware_fault
    link    boundfault_link,boundfault$boundfault
    link    linkage_fault_link,link_snap$link_fault
    link    owc_link,outward_call_handler$outward_call_handler
    link    stack_oob_handler,stack_oob_handler$stack_oob_handler


" 

    even
fault_table:
    table   standard,hardware_fault_link,shutdown
    table   call_signaller,,store
    table   call_signaller,,mme1
    table   call_signaller,,fault_tag_1
    table   illegal_fault,,timer_runout
    table   command_fault,,command
    table   call_signaller,,derail
    table   call_signaller,,lockup
    table   illegal_fault,,connect
    table   standard,hardware_fault_link,parity
    table   illegal_procedure,,illegal_procedure
    table   standard,hardware_fault_link,op_not_complete
    table   standard,hardware_fault_link,startup
    table   split_overflow,,ovrflo
    table   call_signaller,,zerodivide
    table   illegal_fault,,execute
    table   standard.signaller_check_coll,seg_fault_link,seg_fault_error,1
pf_loc: table   illegal_fault,,page_fault_error
    table   illegal_fault,,directed_fault_2
    table   call_signaller,,directed_fault_3
    table   access_violation,,accessviolation
    table   call_signaller,,mme2
    table   call_signaller,,mme3
    table   call_signaller,,mme4
    table   standard.signaller_check_coll,linkage_fault_link,linkage_error
    table   call_signaller,,fault_tag_3
    table   illegal_fault,,undefined_fault
    table   illegal_fault,,undefined_fault
    table   illegal_fault,,undefined_fault
    table   illegal_fault,,undefined_fault
    table   illegal_fault,,undefined_fault
    table   illegal_fault,,trouble

" 

" Additional entries for subcases of faults.

foc:    table   call_signaller,,illegal_opcode,audit_flags.ipr
null:   table   call_signal,,null_pointer
bam:    table   call_signaller,,illegal_modifier,audit_flags.ipr
avf:    table   call_signal,,illegal_ring_order,audit_flags.acv_ring
    table   call_signal,,not_in_execute_bracket,audit_flags.acv_ring
    table   call_signal,,no_execute_permission,audit_flags.acv_mode
    table   call_signal,,not_in_read_bracket,audit_flags.acv_ring
    table   call_signal,,no_read_permission,audit_flags.acv_mode
    table   call_signal,,not_in_write_bracket,audit_flags.acv_ring
    table   call_signal,,no_write_permission,audit_flags.acv_mode
    table   call_signal,,not_a_gate,audit_flags.acv_ring
    table   call_signal,,not_in_call_bracket,audit_flags.acv_ring
    table   standard.signal_check_coll,owc_link,outward_call
    table   call_signal,,bad_outward_call,audit_flags.acv_ring
    table   call_signal,,inward_return,audit_flags.acv_ring
    table   call_signal,,cross_ring_transfer
    table   standard.signal_check_coll,ring_alarm_fault_link,ring_alarm_fault,1
    table   standard,am_fault_link,am_fault
oob:    table   standard.signal_check_coll,boundfault_link,out_of_bounds,1
ovfl:   table   call_signaller,,fixedoverflow
eovf:   table   call_signaller,,overflow
eufl:   table   call_signaller,,underflow
trun:   table   call_signaller,,stringsize
ipr:    table   call_signaller,,illegal_procedure
stk_oob:    table   standard.signal_check_coll,stack_oob_handler,storage
ppf:    table   call_signal,,packed_pointer_fault
sppf:   table   call_signal,,lot_fault
    table   call_signal,,isot_fault
    table   call_signal,,system_packed_pointer
quit_loc:   table   call_signal,,quit
alrm_loc:   table   call_signal,,alrm
cput_loc:   table   call_signal,,cput
rqo_loc:    table   call_signal,,record_quota_overflow
size_loc: table call_signaller,,size
    table   call_signal,,neti
com:    table   standard,hardware_fault_link,command
    table   call_signal,,sus_
    table   call_signal,,trm_
    table   call_signal,,wkp_
undefp: table   call_signal,,undefined_pointer


    include mc

    include stack_header

" 

    include audit_flags

    include sdw
" 
    include history_regs_dps8
" 
    include history_regs_l68

    end




            fim_util.alm                    08/11/83  1813.6r   08/11/83  1735.0      149139



" FIM_UTIL - Utility subroutines for the fim, wired_fim, and ii.
"   consolidated 10/18/76 by Noel I. Morris
"   Modified 1 Feb 79 by D. Spector for new scs format for 8-cpu cioc
"   Modified 8/27/79 by J. A. Bush to reorganize the check_fault subroutine
"   Modified 8/25/80 by J. A. Bush for the DPS8/70M CPU
"   Modified 2/23/81 by J. Bongiovanni for fast connect code
"   Modified 3/5/81 by J. Bongiovanni for OOSB on prds
"   Modified 3/23/81 by J. A. Bush to dump extended APU history reg. for DPS8/70M.
"   Modified 9/23/81 by J. A. Bush to not use QU mode when saving AMs
"   Modified 10/28/81 by J. Bongiovanni for bug in check_fault
"   Modified 2/8/82 by J. Bongiovanni to trap on masked in user ring (conditionally)
"   Modified 02/16/82 by J. A. Bush to not trash mc.fault_reg for sys_trouble crash
"   Modified 07/30/82 by J. Bongiovanni for scs$trouble_processid
"   Modified 10/13/82 by E. N. Kittlitz to clear indicators in v_time_init.
"   Modified sometime by B. I. Margulies for bootload multics.
"   Modified 4/7/83 by E. N. Kittlitz for drl_fault_trouble.


    name    fim_util


    segdef  check_fault validate a fault
    segdef  check_interrupt validate an interrupt
    segdef  fault_trouble   illegal fault condition
    segdef  drl_fault_trouble   illegal fault, you say what it is
    segdef  copy_mc     copy machine conditions
    segdef  push_stack  push stack frame
    segdef  push_stack_32   push stack frame with grace area
    segdef  set_mask        set controller mask to sys level
    segdef  restore_mask    restore previous controller mask
    segdef  hist_regs       save history regs and AMs
    segdef  force_hist_regs force save history regs and AMs
    segdef  check_mct       check and trace machine conditions and hregs
    segdef  reset_mode_reg  turn on hist reg and cache
    segdef  v_time_init start virtual time meter
    segdef  fim_v_time_init save virtual time for FIM
    segdef  v_time_calc stop virtual time meter
    segdef  fim_v_time_calc compute virtual time for FIM


    link    prds_link,prds$+0


    inhibit on  <+><+><+><+><+><+><+><+><+><+><+><+>

" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   Procedure to check validity of faults and interrupts
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

check_fault:
    ldaq    pds$apt_ptr get pointer to our APT entry
    cmpaq   prds$idle_ptr   are we in idle process?
    tze idle_trouble    if so, stop the entire system at once

    szn wired_hardcore_data$trap_invalid_masked Are we trapping invalid sys_level
    tze check_interrupt no
    lxl1    prds$processor_tag
    lprpab  scs$mask_ptr,1
    xec scs$read_mask,1
    cmpaq   scs$sys_level   masked to system level
    tnz check_interrupt no
    lda bp|mc.scu.ppr.prr_word
    cana    scu.ppr.prr_mask,du in ring-0
    tnz masked_in_user_ring no--bye-bye
    lda bp|mc.scu.fault_data_word
    cana    scu.fd.ralr,du  possibly RALR (must also be ACV)
    tze check_interrupt no--let it slide
    ana scu.fi_num_mask+scu.fi_flag_mask,dl
    cmpa    2*FAULT_NO_ACV+1,dl is it an ACV
    tnz check_interrupt no
masked_in_user_ring:
    lca trbl_masked_user_ring,dl
    tra check_fault_trouble

check_interrupt:
    lda bp|mc.scu.ppr.prr_word  check whether running in ring-0
    cana    scu.ppr.prr_mask,du ring number in ppr
    tnz not_invalid not ring-0

    eax7    0       nothing invalid yet
    lda bp|mc.prs+6*2   1st word of stack pointer
    ldq =o77777,dl  mask to check seg-no and val. ring
    cmk lp|prds_link    is stack=prds and ring-0
    tnz not_on_prds no
    ldx7    -trbl_prds_fi,du    fault on prds

    lda bp|mc.scu.tpr.tsr_word  now check for stack overflow
    ldq =o777777,dl ring number, segment number in TSR
    cmk lp|prds_link    was target prds
    tnz not_on_prds no
    lda bp|mc.scu.fault_data_word  stack overflow is OOSB
    cana    scu.fd.oosb,du  is it possibly OOSB (must also be acv)
    tze not_on_prds not at all
    ana scu.fi_num_mask+scu.fi_flag_mask,dl  check fault type
    cmpa    2*FAULT_NO_ACV+1,dl fault/access violation
    tnz not_on_prds no
    lca trbl_oosb_prds,dl   yes--oosb on prds (stack overflow)
    tra check_fault_trouble ..
    
not_on_prds: 
    lda pds$processid   do we have ptr locked
    cmpa    sst$ptl
    tnz not_ptl_locked  no
    ldx7    -trbl_ptl_fi,du f/i with ptl locked
    
not_ptl_locked:
    eaa 0,7     any invalid condition found
    tze not_invalid no
    ars 18      set up sys_trouble_pending
    tra check_fault_trouble ..
    
not_invalid:        
    lca scu.ir.parm+1,dl    turn OFF parity mask
    ansa    bp|mc.scu.indicators_word

    tra 0,0     return to caller

idle_trouble:
    lca trbl_idle_flt,dl    fault while in idle process
    tra check_fault_trouble ..

fault_trouble:
    lca trbl_illeg_flt,dl   unrecognized fault
    tra check_fault_trouble

drl_fault_trouble:
    lca trbl_r0_drl_flt,dl  ring 0 derail
    tra check_fault_trouble

check_fault_trouble:
    sta scs$sys_trouble_pending  set trouble flags

    lda pds$processid       save our process ID
    stac    scs$trouble_processid   if we're the first

    tsx1    save_fault_reg      save fault reg and cpu type

    lda sys_info$initialization_state Too early for connect?
    cmpa    =2,dl           Multiprocessing?
    tmoz    sys_trouble$sys_trouble Go directly to jail ...

    lxl1    prds$processor_tag  prepare to send connect
    cioc    scs$cow_ptrs,1*     ZAP

    dis *       cannot inhibit connects at a DIS
    tra -1,ic       STOP
" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   COPY_MC - Procedure to copy machine conditions
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

copy_mc:
    mlr (pr),(pr)       copy machine conditions
    desc9a  bp|0,48*4
    desc9a  ap|0,48*4
    eppbp   ap|0        make bp -> copied conditions

    tra 0,0     return to caller



" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   PUSH_STACK - Procedure to create a stack frame
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

push_stack:
    eppap   sb|stack_header.stack_end_ptr,*  ap -> place for frame
push_join:
    sprisp  ap|stack_frame.prev_sp  save current sp
    eppsp   ap|0            set sp to new frame
    eppap   sp|0,7          ap -> end of frame
    spriap  sp|stack_frame.next_sp  save next sp
    spriap  sb|stack_header.stack_end_ptr   save end pointer
    sprilp  sp|stack_frame.lp_ptr   save our linkage ptr

    tra 0,0         return to caller


push_stack_32:
    eppap   sb|stack_header.stack_end_ptr,*  ap -> place for frame
    eppap   ap|32       leave room for push in progress
    tra push_join       join common code

" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   Procedures to save and restore controller masks
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

set_mask:
    lxl1    prds$processor_tag  processor tag in X1
    lprpab  scs$mask_ptr,1  get pointer for masking
    xec scs$read_mask,1 read mask, if assigned
    staq    bp|mc.mask  save it

    lxl1    prds$processor_tag  CPU tag in X1
    lprpab  scs$mask_ptr,1  get pointer for masking
    ldaq    scs$sys_level   going to mask all interrupts
    xec scs$set_mask,1  only if mask is assigned

    tra 0,0



restore_mask:
    lxl1    prds$processor_tag  CPU tag in X1
    lprpab  scs$mask_ptr,1  get pointer for masking
    ldaq    bp|mc.mask  get original mask
    oraq    channel_mask_set    make sure channel mask is correct
    anaq    scs$open_level  ..
    xec scs$set_mask,1  restore the mask

    tra 0,0     return to caller


    even
channel_mask_set:
    oct 17,17

" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   Procedures to save history regs. Enter with bb -> place to store history regs.
"   hist_regs -     save history regs only if per-system 
"           (wired_hardcore_data$global_hregs) or per-process
"           (pds$save_history_regs) flags are set.
"   force_hist_regs -   save history regs unconditionaly
"
"   X4 is set  with the cpu type (0 = L68/DPS, 1 = DPS8) by the
"   save_fault_reg subroutine  which is called by the check_mct subroutine
"   which is called by all callers of this subroutine.
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

hist_regs:
    szn wired_hardcore_data$global_hregs is global history reg save flag on?
    tmi force_hist_regs     xfer if yes
    szn pds$save_history_regs   no, is per_process hregs switch on?
    tmi force_hist_regs     xfer if yes
    stz pds$hregs_saved     no, zero switch for signaller
    tra 0,0         and return to caller

force_hist_regs:
    lda =o400000,du     set hreg saved switch
    sta pds$hregs_saved     for signaller
    tsx1    save_am         go save associative memory
    ldq 2,du            get a 2 for stepping address
    eax6    4           4 blocks of
scpr1:  eax7    16          16 history registers
    eax3    0           set up for L68 CPU type initally
    cmpx4   1,du            is this a DPS8/70M CPU?
    tnz scpr2           xfer if no, it is L68
    eax3    48          yes, set up to skip first 48 hregs
scpr2:  lda scpr-1,6            get correct instruction
    sta bp|mc.fim_temp      save in stack
scpr3:  xec bp|mc.fim_temp      execute the instruction
    cmpx3   0,du            are we through skipping hregs?
    tze scpr4           yes, go increment address
    eax3    -1,3            no, skip another
    tra scpr3           and go execute scpr again

scpr4:  asq bp|mc.fim_temp      increment address of instruction
    eax7    -1,7            count down
    tnz scpr3           more of this 16 double word block
    eax6    -1,6            count down
    tnz scpr1           another kind of hreg

    eax7    64          initially set clear count to 64
    cmpx4   1,du            is this a DPS8/70M CPU?
    tze *+2         yes, clear all 64 hregs
    eax7    16          no, clear only 16 hregs
    lcpr    0,03            set all history regs to zero
    eax7    -1,7            count down
    tpnz    *-2         xfer if more to do
    tra 0,0         return to caller

scpr:   scpr    bb|0,40         OU History Regs for L68, OU/DU for DPS8
    scpr    bb|32,20            CU History Regs
    scpr    bb|64,10            DU History Regs for L68, extended APU for DPS8
    scpr    bb|96,00            APU History Regs

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   Internal subroutine for saving fault register, extended fault 
"   register and CPU type (from RSW (2)), int the machine condition area.
"   entry via tsx1 save_fault_reg. Exit with x4 containing the CPU type
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

save_fault_reg:
    scpr    bp|mc.fim_temp,01       store the fault register
    ldaq    bp|mc.fim_temp
    sta bp|mc.fault_reg     save M.S. 36 bits
    rsw 2           get cpu type in a
    arl 30          position in al
    ana 3,dl    
    eax4    0,al            save cpu type in x4
    lrl mc.cpu_type_shift       merge with ext. fault reg
    stcq    bp|mc.cpu_type_word,70  and save in M.C.
    tra 0,1         return

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   Internal subroutine for saving associative memory in the prds
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

save_am:
    eax3    4           4 instructions to execute
    eax6    3           initialy set for DPS8 (4 sets of 16)
    cmpx4   1,du            is it a DPS8 CPU?
    tze sam1            yes, go execute code
    eax6    0           no, only 1 set for each type on L68

sam1:   xec samt1-1,3           load LB with proper AM type
    lda samt2-1,3           load instruction to execute in A
    sta bp|mc.fim_temp      and save
    eax7    0,6         initialize counter
sam2:   eaq 0,7         copy to qu
    qls 4           multiply by 16
    cmpx3   2,du            is it for sdw_am_regs?
    tnz *+2         xfwr if no
    qls 1           yes, make it times 32
    stcq    bp|mc.fim_temp,30       set quad offset
    xec bp|mc.fim_temp      execute instruction
    eax7    -1,7            count down
    tpl sam2            do next set
    eax3    -1,3            count down instructions to exucute
    tnz sam1            go do next instuction set
    tra 0,1         return to caller

samt1:  epplb   prds$sdw_am_ptrs
    epplb   prds$sdw_am_regs
    epplb   prds$ptw_am_ptrs
    epplb   prds$ptw_am_regs

samt2:  ssdp    lb|0
    ssdr    lb|0
    sptp    lb|0
    sptr    lb|0

"

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   Procedure to check if conditions are met for machine condition tracing
"   if conditions are not true for machine condition tracing, return is ic +1 (tra  0,0)
"   if conditions are met for machine condition tracing, then
"   return is ic +2 (tra   1,0), bb -> place to pick up history regs and
"   lb -> place to pick up machine conditions directly from machine condition trace buffer
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

check_mct:
    tsx1    save_fault_reg      save fault reg and get CPU type in x4
    szn pds$mc_trace_sw     is this process tracing machine conditions?
    tpl 0,0         return to caller if no
    szn pds$mc_trace_seg        Does user want to trace all M. Cs?
    tze cp_hregs            xfer if seg number zero
    lda bp|mc.scu.ppr.psr_word  look at the psr
    ana scu.ppr.psr_mask,du     and out  everything except psr
    cmpa    pds$mc_trace_seg        compare psr to object we are tracing
    tze cp_hregs            xfer if psr = object we are tracing
    lda bp|mc.scu.tpr.tsr_word  look at tsr
    ana scu.tpr.tsr_mask,du     and out everthing except tsr
    cmpa    pds$mc_trace_seg        compare tsr to object we are tracing
    tnz 0,0         return to caller if psr or tsr don't have seg
cp_hregs:
    lprplb  pds$mc_trace_buf        get packed ptr to wired trace buffer
    stx0    lb|mctseg.x0save        safe store return address
    lxl5    lb|mctseg.hr_nxtad      x5 = rel ptr to next H. R. storage location
    cmpx5   lb|mctseg.hr_lim        do we have to roll over the trace?
    tmi hr_roll         xfer if no
    ldx5    lb|mctseg.hr_strt       yes, pick up initial storage location
    sxl5    lb|mctseg.hr_nxtad      store new location
hr_roll:
    eax5    mctseg.hr_size,5        increment storage location
    sxl5    lb|mctseg.hr_nxtad      set rel ptr to next H. R. storage location
    eppbb   lb|-mctseg.hr_size,5    bb -> current HR storage location
    tsx0    force_hist_regs     go save history regs in mc_trace buf
    lprplb  pds$mc_trace_buf        get packed ptr to wired trace buffer
    ldx0    lb|mctseg.x0save        restore return address
    lxl5    lb|mctseg.mc_nxtad      x5 = rel ptr to next M. C. storage location
    cmpx5   lb|mctseg.mc_lim        do we have to roll over the trace?
    tmi mc_roll         xfer if no
    ldx5    lb|mctseg.mc_strt       yes, pick up initial storage location
    sxl5    lb|mctseg.mc_nxtad      store new location
mc_roll:
    eax5    mctseg.mc_size,5        increment storage location
    sxl5    lb|mctseg.mc_nxtad      set rel ptr to next M. C. storage location
    epplb   lb|-mctseg.mc_size,5    lp -> current MC storage location
    mlr (pr),(pr)           move the data to wired buffer
    desc9a  bp|0,mctseg.mc_size*4
    desc9a  lb|0,mctseg.mc_size*4
    tra 1,0         return to caller at ic +2


" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   Procedure to reset mode registers to continue strobing
"   data into the history registers.
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

reset_mode_reg:
    epplb   prds$cache_luf_reg  point to cache/luf reg setting
    lcpr    lb|0,02     set cache enable and full luf time

    lda prds$mode_reg   retrieve template mode reg
    ora mr.enable_mr+mr.enable_hist,dl  enable mode reg and enable hist regs
    sta prds$mode_reg_enabled   save this mode reg value
    epplb   prds$mode_reg_enabled   get pointer to temp mode reg value
    lcpr    lb|0,04     reload the mode register
    tra 0,0         return to caller

" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   Procedures to suspend virtual time metering
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

    even
v_time_init:
    tsx7    get_times       get fault time and virtual time

    aos pds$vtime_count step virtual time count
    tpnz    0,0     if already set, don't meter

    staq    pds$time_v_temp save for later
    ldi 0,dl        clear indicators as a service to our caller
    tra 0,0



    odd
fim_v_time_init:
    tsx7    get_times       get times

    szn pds$vtime_count metering already?
    tpl 0,0     if so, return

    staq    pds$fim_v_temp  save current virtual time
    ldaq    pds$virtual_delta   save current delta
    staq    pds$fim_v_delta ..
    ldi 0,dl        clear indicators as a service to our caller
    tra 0,0     and return to caller



    even
get_times:
    rccl    sys_info$clock_,*   read the clock
    staq    bp|mc.fault_time    save time of fault/interrupt
    sbaq    pds$cpu_time    compute virtual time
    tra 0,7     return to caller

" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   Procedures to reinstate virtual time metering
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

    even
v_time_calc:
    lca 1,dl        decrement virtual time count
    asa pds$vtime_count ..
    tpl 0,0     if others, don't meter

v_calc_join:
    rccl    sys_info$clock_,*   read the clock
    adl 96,dl       add in correction delta
    sbaq    pds$cpu_time    get current value of CPU time
    sbaq    pds$time_v_temp get time we used
    staq    pds$time_v_temp save temporarily

    adaq    pds$virtual_delta   compute virtual time increment
    staq    pds$virtual_delta   and save

    ldaq    pds$time_v_temp time used in AQ again
    adaq    tc_data$delta_vcpu  compute total vcpu time
    staq    tc_data$delta_vcpu  ..

    tra 0,0



    even
fim_v_time_calc:
    szn pds$vtime_count metering virtual time?
    tpl 0,0     if not, just return

    ldaq    pds$virtual_delta   get current virtual delta
    sbaq    pds$fim_v_delta subtract original delta
    adaq    pds$fim_v_temp  correct FIM time
    staq    pds$time_v_temp store corrected time
    tra v_calc_join join common code

" 

    include stack_frame

" 

    include stack_header
    include mctseg

" 

    include sys_trouble_codes

    include mode_reg

    include mc

" 

    include fault_vector



    end




            iom_interrupt.alm               10/20/82  1323.9r w 10/20/82  1315.7       69345



"  ******************************************************
"  *                                                    *
"  *                                                    *
"  * Copyright (c) 1972 by Massachusetts Institute of   *
"  * Technology and Honeywell Information Systems, Inc. *
"  *                                                    *
"  *                                                    *
"  ******************************************************

" iom_interrupt:  IOM interrupt handler
" Stolen from iom_manager and ii November 1980 by C. Hornig

    name    iom_interrupt

    segdef  interrupt_entry " interrupt vector transfers here
    segdef  iilink      " ITS ptr to linkage section
    segdef  prds_prs        " ITS ptr to space for spri
    segdef  prds_scu        " ITS ptr to space for SCU

    tempd   hlr_arglist(4)  " argument list for interrupt handler
    tempd   time        " time handler called
    tempd   imw_save        " current IMW for scan
    temp    int_level       " interrupt level for handler (1 3 5 7)
    temp    index       " index for this channel
    temp    chantab_base    " offset in chantab for this IOM
    temp    device      " offset in per_device for this channel
    temp    chx     " channel index from chantab
    temp    int_count       " handlers called this time around

" Index register conventions:

"   X0 - internal subroutine calls.
"   X1 - index into devtab.
"   X2 - index into mailbox.
"   X3 - Used to index iomd.per_iom.
"   X4 - interrupt cell #.
"   X5 - interrupt level #.
"   X6 - index in status queue

" Pointer Register Conventions:

"   AP -> machine conditions
"   AB -> inetrnal calls
"   BP -> <iom_data>|0
"   BB -> mailbox for this IOM.
"   LP -> linkage section
"   LB -> status queue


" These are initialized by initialize_faults

    even
iilink: its -1,1        " our LP
prds_prs:   its -1,1        " where to store PRs
prds_scu:   its -1,1        " and CU info

three_args_nd:
    zero    6,4
zero:   zero    0

push_value:
    push

chanpos:                " table of chantab offsets
    zero    0,0*4*per_iom_size+32
    zero    0,1*4*per_iom_size+32
    zero    0,2*4*per_iom_size+32
    zero    0,3*4*per_iom_size+32
    zero    0,0*4*per_iom_size
    zero    0,1*4*per_iom_size
    zero    0,2*4*per_iom_size
    zero    0,3*4*per_iom_size
" 
" This entry is transferred to by the interrupt vector.
" It is responsible for calling the interrupt handlers of various DIM's
" for which interrupts have been stored in the IMW array.
" It calls the interrupt routine as follows:
"   call int_proc (idx, int_level, data_word)
" where int_level (1 3 5 7) corresponds to the level passed by the channel
" to the IOM and data_word in the system fault or special status word.

    inhibit on  <+><+><+><+><+><+><+><+><+><+><+><+><+>

interrupt_entry:
    spri    prds_prs-*,ic*  " save pointer registers
    eppbp   prds_prs-*,ic*  " ab -> machine conditions
    sreg    bp|mc.regs  " save registers
    lxl7    bp|mc.scu.indicators_word
    canx7   scu.ir.mif,du   " check for EIS
    tze 2,ic        " so we can save
    spl bp|mc.eis_info  " the pointers and lengths

    epplp   iilink-*,ic*    " set up LP

    tsx0    fim_util$v_time_init " meter virtual time

    tsx0    fim_util$check_interrupt " make sure we're not not prds

    tsx0    fim_util$check_mct  " go save M.C.s and hregs
                " if trace is on
    nop         " return ic +1

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   Establish a stack frame and mask interrupts.
"
    epbpsb  prds$       " use prds
    ldx7    push_value-*,ic " get size of stack frame
    tsx0    fim_util$push_stack " push the frame

    tsx0    fim_util$set_mask   " mask to sys level

    inhibit off <-><-><-><-><-><-><-><-><-><-><-><->

    tsx0    fim_util$reset_mode_reg
                " turn on hist regs and cache
    ldq bp|mc.scu.fault_data_word
    anq scu.fi_num_mask,dl  " get the interrupt number
    qrl scu.fi_num_shift
    eax4    0,ql        " interrupt cell in X4
" 
    anq =7,dl       " get base channel table position
    lda chanpos,ql
    sta chantab_base    " save it for later

    eaa 0,x4        " get interrupt cell
    arl 18+2        " interrupt level in AL
    ora =1,dl       " always odd
    sta int_level       " save for handler

    ldaq    three_args_nd-*,ic  " Build argument list for handler
    staq    hlr_arglist " call handler (index, int_level, ""b)
    epplb   index
    sprilb  hlr_arglist+2
    epplb   int_level
    sprilb  hlr_arglist+4
    epplb   zero-*,ic
    sprilb  hlr_arglist+6
"
"   set up to search IMW area
"
    eppbp   iom_data$
    stz int_count       " no interrupts yet

    ldac    iom_mailbox$+iom_mailbox_seg.imw_array_word,x4
    tze null_int-*,ic   " no IMW bit set

    ldq 0,du        " make a fake float number
    lde 0,du
    lrl 1
    tra imw_begin-*,ic  " start scanning

" Scan the IMW for any bits on and process these interrupts.

imw_retry:
    dfld    imw_save        " get back remaining bits
    tze interrupt_return    " no bits left

imw_begin:
    ldi 0,dl        " clear hex and overflow bits
    fno
    era =o200000,du " clear the bit
    dfst    imw_save

    lda imw_save        " get channel number
    ars 36-8        " in AL
    neg 0
    ada chantab_base    " add chantab offset
    sta index       " store in case of error

    mrl (pr,al),(pr),fill(0) " Reference the channel table.
    desc9a  bp|iom_data.per_iom+per_iom.chantab_word,1
    desc9a  chx,4       " Word will be leading zero padded.

    eppap   hlr_arglist

    ldq chx     " chx in QL
    tze invalid_int-*,ic    " Ignore interrupt if channel not assigned.
    mpy per_device_size,dl
    eax1    -per_device_size,ql " index into per_device

    lda bp|iom_data.per_device+per_device.flags,x1
                " is there a handler?
    cana    per_device.in_use,du
    tze invalid_int-*,ic    " Return if no handler assigned.

    lda bp|iom_data.per_device+per_device.index,x1
    sta index       " save the handler's index
    stx1    device      " save per_device index for ext. call

    rccl    sys_info$clock_,*   " Get int_time now.
    staq    time        " And save it.

    short_call bp|iom_data.per_device+per_device.handler,x1*
                " Call handler.

    rccl    sys_info$clock_,*   " Get int_time again.
    sbaq    time        " Compute delta.

    eppbp   iom_data$       " re-establish addressability
    ldx1    device      " restore per_device offset
    adaq    bp|iom_data.per_device+per_device.interrupt_time,x1
    staq    bp|iom_data.per_device+per_device.interrupt_time,x1
    ldaq    bp|iom_data.per_device+per_device.interrupts,x1
    adl =1,dl       " update meters
    staq    bp|iom_data.per_device+per_device.interrupts,x1
    aos int_count       " count interrupts

    tra imw_retry-*,ic  " find more interrupts


invalid_int:
    aos bp|iom_data.invalid_interrupts
                " meter unexpected interrupts
    short_call iom_error$invalid_interrupt
                " and tell the world
    eppbp   iom_data$       " re-establish addressability
    tra imw_retry-*,ic  " find more channels
" 
null_int:               " meter interrupts with null IMW
    aos bp|iom_data.null_interrupts

interrupt_return:
    lda int_count       " meter multiple interrupts
    cmpa    =1,dl
    tmoz    2,ic
    aos bp|iom_data.multiple_interrupts

    ldaq    tc_data$interrupt_count
    adl 1,dl        " count interrupts
    staq    tc_data$interrupt_count

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   Relinquish stack frame and open interrupt mask.
"
    eppbp   prds_prs-*,ic*  " restore MC ptr

    tsx0    fim_util$restore_mask
    inhibit on  <+><+><+><+><+><+><+><+><+><+><+><+>

    epbpsb  sp|0        get ptr to base of stack
    sprisp  sb|stack_header.stack_end_ptr  pop our stack frame

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   Perform interrupt metering and exit.
"
    ldaq    bp|mc.fault_time    " get fault time
    ana =o777777,dl " clear CPU type
    staq    prds$iitemp " save for now
    rccl    sys_info$clock_,*   " read the clock
    sbaq    prds$iitemp " compute real time spent
    adaq    tc_data$interrupt_time " add in to total
    staq    tc_data$interrupt_time "    ..

    odd
    tsx0    fim_util$v_time_calc " compute virtual time

    lxl1    bp|mc.scu.indicators_word
    canx1   scu.ir.mif,du   " check for EIS if not don't reload pl
    tze 2,ic
    lpl bp|mc.eis_info  restore pointers and lengths
    lreg    bp|mc.regs  and registers
    lpri    prds_prs-*,ic*  restore prs
    rcu prds_scu-*,ic*  and dismiss the interrupt
" 
    include iom_data
    include mc
    include stack_header

    end




            sys_trouble.alm                 08/11/83  1813.6r   08/11/83  1735.0      111636



" ***********************************************************
" *                                                         *
" * Copyright, (C) Honeywell Information Systems Inc., 1982 *
" *                                                         *
" ***********************************************************
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   SYS_TROUBLE - Handle the system trouble connect
"
"   The system may be in serious trouble when this code
"   is entered.  No stack frame is created and no calls
"   are made.  All other processors are stopped and
"   the bootload processor returns to BOS.  A return
"   from BOS will cause all processors to be restarted.
"   Machine conditions for the system trouble interrupt
"   can be found at prds$sys_trouble_data.
"
"   Coded by Roger R. Schell - September 23, 1969
"   Recoded by Noel I. Morris - July 22, 1971
"   Modified 1 Feb 79 by D. Spector for new scs format for 8-cpu cioc
"   Modified by Noel I. Morris - February 8, 1976
"   Modified for BOS messages by Noel I. Morris - May 5, 1977
"   Modified 80 Sept 2 by Art Beattie - make it shutdown CPUs 4-7.
"   Modified by J. A. Bush 10/06/80 for the DPS8/70M CPU
"   Modified by J. Bongiovanni 3/4/81 for OOSB on prds
"   Modified by J. Bongiovanni 2/24/82 for Masked in User Ring
"   Modified by J. Bongiovanni 1/10/83 to ignore lockup faults absolutely
"   Modified by E. N. Kittlitz 4/11/83 for ring-0 derail crash mechanism.
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "


    name    sys_trouble

    segdef  sys_trouble
    segdef  ignore_data

"  ******************************************************
"  *                                                    *
"  *                                                    *
"  * Copyright (c) 1972 by Massachusetts Institute of   *
"  * Technology and Honeywell Information Systems, Inc. *
"  *                                                    *
"  *                                                    *
"  ******************************************************


    equ bos_xed_loc,4   absolute location for BOS entry

    inhibit on  <+><+><+><+><+><+><+><+><+><+><+><+>

    use main


" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   Before spinning waiting for console I/O to die, and before going to
"   BOS, the fault vector for lockup is patched to do an SCU/RCU
"   in absolute mode. This must be in absolute mode, in case the lockup
"   happens in early BOS. The target of the SCU/RCU is in this program
"   rather than prds$ignore_data, since the latter may not be in the
"   low-order 256K.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
    eight
ignore_data:
    bss ,8      " SCU data for SCU/RCU on lockup fault

ignore_scu_rcu:
    scu 0       " Put into fault vector for lockup fault
    rcu 0       " after absolute address inserted into Y-field

" 

sys_trouble:
    lda prds$processor_pattern  get bit pattern for this CPU
    cana    scs$bos_restart_flags  are we restarting this processor?
    tnz restart     if so, get it running again


"   If this is the first processor to enter this code,
"   a system trouble connect must be sent to all other
"   processors to stop them too.

    lda scs$processor   get flags for all running CPU's
    stac    scs$trouble_flags   are we the first processor?
    tnz *+2     if not, skip broadcast
    tsx0    broadcast       broadcast system trouble connects to others

    tsx0    fim_util$set_mask   save mask and mask down

" 

"   Copy the machine conditions into prds$sys_trouble_data.
"   This prevents overwriting the data when another
"   system trouble interrupt is used to restart CPU's.

    lda bp|mc.scu.fault_data_word  get fault code
    ana scu.fi_num_mask,dl  mask fault code
    arl scu.fi_num_shift    right-justify
    cmpa    FAULT_NO_CON,dl connect fault?
    tnz no_copy     if not, conditions already in trouble_data

    eppap   prds$sys_trouble_data  ap -> cache for machine conditions
    tsx0    fim_util$copy_mc    copy the machine conditions
no_copy:

    eppbb   pds$history_reg_data    bb -> place to store history regs
    tsx0    fim_util$check_mct      go copy cpu type into machine conditions
    tsx0    fim_util$force_hist_regs    save the history registers in pds

    lda prds$processor_tag  CPU tag in A
    als 1       multiply by 2
    sdbr    scs$trouble_dbrs,al save DBR for debugging


"   If this is the bootload CPU, enter BOS.
"   Otherwise, die gracefully.

    lca 1,dl        all one's in A
    era prds$processor_pattern  CPU pattern mask in A
    ansa    scs$processor   indicate that this CPU is stopped

    lda prds$processor_tag  processor tag in A
    cmpa    scs$bos_processor_tag  is this the bootload CPU?
    tze enter_bos       if so, go to BOS

die:
    dis -1,du       stop 
    tra *-1     I said stop!

" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   The second trouble connect for restarting processors
"   causes control to be transferred here.
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

restart:
    lda prds$processor_pattern  get bit for this processor
    orsa    scs$processor   indicate CPU is running again
    era =-1     complement to make a mask
    ansa    scs$bos_restart_flags  indicate processor has been restarted
    ansa    scs$sys_trouble_pending  turn off trouble flag for this processor

    eppbp   wired_fim$trouble_prs,*  bp -> system trouble m.c. area
    tsx0    fim_util$restore_mask  restore original controller mask

    szn scs$faults_initialized  see if system ready for cache
    tze trouble_exit    transfer if not
    tsx0    fim_util$reset_mode_reg  restore mode and cache mode regs

    odd
trouble_exit:
    tsx0    fim_util$v_time_calc  start virtual time meters again

    lpl bp|mc.eis_info  restore ptrs and lgths
    lreg    bp|mc.regs  and regs
    lpri    bp|mc.prs       and prs
    rcu wired_fim$trouble_scuinfo,* get running again

" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   The following code copies an error message into the BOS
"   flagbox message buffer.
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

enter_bos:
    lda scs$sys_trouble_pending  get flags
    als 18      extract low-order
    ars 18      could be negative number
    neg 0       or zero
    tze rtb_no_message  if zero, no message

    eppbb   flagbox$        bb -> BOS flagbox
    mlr (),(pr)     copy program ID
    desc9a  sys_trouble_name,13
    desc9a  bb|fgbx.message,13

    mlr (id),(pr),fill(040) copy error message
    arg trouble_messages-1,al
    desc9a  bb|fgbx.message+3(1),64-13

    cmpa    trbl_r0_drl_flt,dl  is it a ring-0 derail?
    tnz non_drl     nope, that's all 
    szn scs$drl_message_pointer augment the message
    tze non_drl     nothing to say
    lprplb  scs$drl_message_pointer 
    lda lb|0        acc length in upper 9
    arl 27      lower 9, now
    mlr (pr,rl),(pr),fill(040) Your life story in 32 characters.
    desc9a  lb|0(1),al
    desc9a  bb|fgbx.message+8,64-32

non_drl:    ldq fgbx.mess+fgbx.alert,du  set flags for message printing
    orsq    bb|fgbx.rtb ..

    tra rtb_no_message  no, go back to BOS


sys_trouble_name:
    aci "sys_trouble: "

" 

    macro   message
    desc9a  &U,&l1
    maclist off,save
    use message
    maclist restore
&U:
    aci "&1"
    maclist off,save
    use main
    maclist restore
    &end

trouble_messages:
    message (Page fault while on prds.)

    message (Fault/interrupt while on prds.)

    message (Fault in idle process.)

    message (Fault/interrupt with ptl set.)

    message (Unrecognized fault.)

    message (Unexpected fault.)

    message (Execute fault by operator.)

    message (Out-of-Segment-Bounds on prds.)

    message (Interrupts Masked in User Ring.)   

    message (Fault in bound_interceptors.)

    message (Ring 0 derail.)

" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   The following code enters BOS by placing the two
"   absolute mode instructions needed to enter BOS
"   into the fault vector slot for the derail fault.
"   NOTE: bp must be preserved  across call to BOS since
"   we use it to restore pointer registers upon return.
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

rtb_no_message:
    eppbb   fault_vector$+0 bb -> fault vector segment
    ldaq    bb|2*FAULT_NO_LUF+fv.fpair
    staq    lp|save_lockup_fault  save SCU/TRA
    absa    ignore_data abs addr in 0-23
    als 6       abs addr in 0-17 of Areg
    eaq 0,au        abs addr in 0-17 of Qreg
    oraq    ignore_scu_rcu  replace lockup fault vector
    staq    bb|2*FAULT_NO_LUF+fv.fpair

    szn scs$processor   all CPU's stopped?
    tnz *-1     if not, wait here

    lda 4,du        wait for operator's console output to finish
    odd
    sba 1,dl        to allow I/O to drain off
    tnz *-1     ..

    ldaq    bb|2*FAULT_NO_DRL+fv.fpair  grab SCU-TRA pair from fault vector
    staq    lp|save_derail_fault
    ldaq    bos_toehold$+bos_xed_loc  pick up code to enter BOS
    staq    bb|2*FAULT_NO_DRL+fv.fpair  set it in fault vector

drl:    drl 0       ****** BOS is entered here ******

    szn scs$connect_lock    did we enter through pmut call?
    tze drl     if not, cannot restart

    ldac    scs$trouble_flags   get and clear trouble flags
    sta scs$bos_restart_flags  set for restarting CPU's

    ldaq    lp|save_derail_fault
    staq    bb|2*FAULT_NO_DRL+fv.fpair  ..
    ldaq    lp|save_lockup_fault  restore lockup faults
    staq    bb|2*FAULT_NO_LUF+fv.fpair  ..

    tsx0    broadcast       send trouble connects to start CPU's

    tra restart     restart the bootload CPU

" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   BROADCAST - Send system trouble connects to all other
"       processors.
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

broadcast:
    ldq hbound_processor_data,dl    initialize the Q with maximum configurable CPUs
broadcast_loop:
    cmpq    prds$processor_tag  test for ourselves
    tze broadcast_next  don't hit ourselves
    lda scs$processor_data,ql  get processor data for this CPU
    cana    processor_data.online,du  is it configured?
    tze broadcast_next  if not, don't hit it

    cioc    scs$cow_ptrs,ql*    Zap

broadcast_next:
    sbq 1,dl        step to next CPU
    tpl broadcast_loop  if more, get the others
    tra 0,0     return to caller

"


    use internal_static
    join    /link/internal_static

    even
save_lockup_fault:
    bss ,2      place to save lockup fault SCU and TRA

save_derail_fault:
    bss ,2      place to save derail fault SCU and TRA


    include mc

" 

    include scs

    include fgbx

    include sys_trouble_codes

    include fault_vector

" 

" BEGIN MESSAGE DOCUMENTATION
"
" Message:
" sys_trouble: Page fault while on prds.
"
" S:    $crash
"
" T:    $run
"
" M:    $err
"
" A:    $recov
"
"
" Message:
" sys_trouble: Fault/interrupt while on prds.
"
" S:    $crash
"
" T:    $run
"
" M:    $err
"
" A:    $recov
"
"
" Message:
" sys_trouble: Fault in idle process.
"
" S:    $crash
"
" T:    $run
"
" M:    $err
"
" A:    $recov
"
"
" Message:
" sys_trouble: Fault/interrupt with ptl set.
"
" S:    $crash
"
" T:    $run
"
" M:    $err
"
" A:    $recov
"
"
" Message:
" sys_trouble: Unrecognized fault.
"
" S:    $crash
"
" T:    $run
"
" M:    Unexpected or unrecognized fault subcondition.
"   Probable hardware malfunction.
"
" A:    $contact
"
"
" Message:
" sys_trouble: Unexpected fault.
"
" S:    $crash
"
" T:    $init
"
" M:    $err
"
" A:    $recov
"
"
" Message:
" sys_trouble: Execute fault by operator.
"
" S:    $crash
"
" T:    $run
"
" M:    Operator depressed execute pushbutton on processor.
"
" A:    $recov
"
"
" Message:
" sys_trouble:  Out-of-Segment-Bounds on prds.
"
" S:    $crash
"
" T:    $run
"
" M:    While running with the prds as a stack, an attempt was
"   made to reference beyond the end of the prds.  The likely
"   cause was stack overflow, due either to a recursive loop
"   in the procedures running on the prds or insufficient
"   space allocated for the prds.  If the latter, the size of
"   the prds should be increased by means of the TBLS Configuration
"   Card.
"
" A:    $recover
"
"
" Message:
" sys_trouble: Interrupts Masked in User Ring.
"
" S:    $crash
"
" T:    $run
"
" M:    During processing of a fault, it was noticed that interrupts
"   were masked in user-ring, an invalid condition. This is a
"   debug trap crash, enabled by the hidden tuning parameter
"   trap_invalid_masked.
"
" A:    Contact the Multics System Development staff.
"
"
" Message:
" sys_trouble: Fault in bound_interceptors.
"
" S:    $crash
"
" T:    $run
"
" M:    A fault occured while handling another fault.
"
" A:    $recov
"
"
" Message:
" sys_trouble: Ring 0 derail. {MESSAGE}
"
" S:    $crash
"
" T:    $run
"
" M:    A supervisor software module discovered an untenable situation, and
"   crashed the system by executing a derail (DRL) instruction.
"   If MESSAGE is also present, it will be of the form:
"         "module: explanation", and further details can be found in
"   this documentation in the description of "module".
"
" A:    $recov
"
" END MESSAGE DOCUMENTATION



    end




            wired_fim.alm                   09/09/82  1508.6rew 09/09/82  1503.4      121842



" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   wired_fim
"
"   Last Modified: (Date and Reason)
"   Written 1/70 S.Webber
"   Modified by S.Webber 01/01/72 for followon
"   Modified by S.Webber 10/15/73 to add SPL/LPL code for all faults
"   Modified by S.Webber 12/18/73 to remove page fault code
"   Modified by Noel I. Morris 2/11/76 for new connect faults
"   Modified by Mike Grady 6/79 to do stop on prds
"   Modified by J. A. Bush 3/80 to store execute fault time in machine conditions
"   Modified by J. Bongiovanni 1/81 for fault_counters
"   Modified by J. Bongiovanni 2/81 for fast connect code,
"       traffic control race condition
"   Modified by J. A. Bush 6/3/81 to save fault time for unexpected faults
"   Modified by J. Bongiovanni 1/05/82 to fix CPU start wait bug
"
"   The following entries exist within this procedure:
"
"   connect_handler handles inter-processor communication
"
"   pre_empt_return return from pxss after pre-empt
"
"   ignore      certain faults are ignored here
"
"   timer_runout    timer runout faults are mapped into
"           pre-empts
"
"   unexp_fault unexpected faults come here during
"           system initialization
"
"   xec_fault       execute faults will cause return to BOS
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "


    name    wired_fim


"  ******************************************************
"  *                                                    *
"  *                                                    *
"  * Copyright (c) 1972 by Massachusetts Institute of   *
"  * Technology and Honeywell Information Systems, Inc. *
"  *                                                    *
"  *                                                    *
"  ******************************************************


" 

    include pxss_page_stack

" 

    segdef  prs,scuinfo
    segdef  int_scuinfo
    segdef  trouble_prs,trouble_scuinfo
    segdef  ignore_scuinfo
    segdef  my_linkage_ptr

    segdef  pre_empt_return
    segdef  ignore
    segdef  timer_runout
    segdef  connect_handler
    segdef  unexp_fault
    segdef  xec_fault




" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   The following variables are set up during initialization.
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

    even
prs:    bss ,2
scuinfo:    bss ,2

int_scuinfo:
    bss ,2

trouble_prs:
    bss ,2
trouble_scuinfo:
    bss ,2

ignore_scuinfo:
    bss ,2

my_linkage_ptr:
    bss ,2

"

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   CONNECT_HANDLER -- Handle Connect Faults.
"
"   Connect faults may be sent for the following reasons:
"   1.  To cause a system trouble abort.
"   2.  To clear cache and associative memory.
"       (the clearing has already been done by
"        prds$fast_connect_code by the time we get here)
"   3.  To wait for a new CPU to start up.
"   4.  To cause a processor to delete itself.
"   5.  To cause a process pre-empt.
"   6.  To cause a process stop.
"   7.  To cause an IPS event to be signalled.
"
"
"   By the time this entry has been called, the fast connect
"   in the prds has been called.  This code has done the
"   following:
"
"   1.  If scs$fast_cam_pending was set for this cpu,
"       xed  scs$cam_pair
"       stz  scs$fast_cam_pending + <processor tag>
"   2.  Added 1 to the counter
"       wired_hardcore_data$fault_counters + <FAULT_NO_CON>
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

    inhibit on  <+><+><+><+><+><+><+><+><+><+><+><+>
    even
connect_handler:
    spri    prs,*       save prs
    eppbp   prs,*       bp -> place for machine conditions
    sreg    bp|mc.regs  save regs
    spl bp|mc.eis_info  save EIS ptrs and lgths
    epplp   my_linkage_ptr,*    set up linkage ptr

    tsx0    fim_util$v_time_init  start virtual time metering

    lda scs$sys_trouble_pending  system trouble?
    cana    prds$processor_pattern  for this CPU?
    tnz sys_trouble$sys_trouble  go handle it

    tsx0    fim_util$check_mct  go save M.C.s and hregs if trace is on
    nop         " return ic +1
    tsx0    fim_util$reset_mode_reg  start up hist regs again

    lda prds$processor_pattern  see if we're waiting for
    cana    scs$cam_wait        for all cams to clear
    tnz cam_wait            yes--go do it

    cana    scs$processor_start_wait  waiting for CPU to start up?
    tnz start_wait  if so, go wait

    lxl1    prds$processor_tag  CPU tag in X1
    lda scs$processor_data,1  look at CPU flags
    cana    processor_data.delete_cpu,du  is this CPU to delete itself?
    tnz delete_cpu  if so, cause a pre-emption

more:   epplb   pds$apt_ptr,*   lb -> APT entry for this process
    lda lb|apte.flags   look at process flags
    cana    apte.stop_pending,du  stop?
    tnz stop        go handle stop

    cana    apte.pre_empt_pending,du  pre-empt?
    tnz pre_empt        go handle pre-empt

    lda lb|apte.ips_message IPS message?
    tnz ips     go handle IPS
    tra exit        drop through to exit

"

ralr_set_1:
    lda 1,dl        cannot handle fault now
    sta pds$alarm_ring  defer it until later

    odd
exit:
    tsx0    fim_util$v_time_calc  calculate virtual time

exit_1:
    lpl bp|mc.eis_info  restore EIS ptrs and lths
    lra pds$alarm_ring  FIM may have reset this
    lreg    bp|mc.regs  restore regs
    lpri    bp|mc.prs       and prs
    rcu scuinfo,*       good-bye



" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   IGNORE -- several faults are ignored by directing them
"       to this entry.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

ignore:
    rcu ignore_scuinfo,*    restore the machine conditions



" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   XEC_FAULT and UNEXP_FAULT -- These entries cause
"       the system to abort.
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

xec_fault:
    spri    trouble_prs,*   save prs
    eppbp   trouble_prs,*   bp -> machine conditions
    sreg    bp|mc.regs  save regs
    spl bp|mc.eis_info  save EIS ptrs and lths
    epplp   my_linkage_ptr,*    establish linkage ptr
    rccl    sys_info$clock_,*   read the clock
    staq    bp|mc.fault_time    and save in machine conditions

    lca trbl_exec_flt,dl    set sys trouble indicator
    sta scs$sys_trouble_pending  ..
    lda pds$processid   save our process ID
    stac    scs$trouble_processid if we're the first
    tra sys_trouble$sys_trouble  map into sys_trouble code


unexp_fault:
    spri    trouble_prs,*   save prs
    eppbp   trouble_prs,*   bp -> machine conditions
    sreg    bp|mc.regs  save regs
    spl bp|mc.eis_info  save EIS ptrs and lths
    epplp   my_linkage_ptr,*    establish linkage ptr
    rccl    sys_info$clock_,*   read the clock
    staq    bp|mc.fault_time    and save in machine conditions

    lca trbl_unexp_flt,dl   set sys trouble indicator
    sta scs$sys_trouble_pending  ..
    lda pds$processid   save our process ID
    stac    scs$trouble_processid if we're the first
    tra sys_trouble$sys_trouble  map into sys_trouble code

" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   TIMER_RUNOUT - Timer runouts are mapped into pre-empts.
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

    even
timer_runout:
    spri    prs,*       save prs
    eppbp   prs,*       bp -> machine conditions
    sreg    bp|mc.regs  save regs
    spl bp|mc.eis_info  save EIS ptrs and lths
    epplp   my_linkage_ptr,*    establish linkage ptr

    tsx0    fim_util$v_time_init  start virtual time meter

    tsx0    fim_util$check_mct  go save M.Cs ad hregs if trace is on
    nop         " return ic +1
    tsx0    fim_util$reset_mode_reg  start up hist regs

    aos wired_hardcore_data$fault_counters+FAULT_NO_TRO
    
cause_pre_empt:
    epplb   pds$apt_ptr,*   lb -> APT entry for this process
    ldx0    apte.pre_empt_pending,du  get pre-empt indicator flag
    orsx0   lb|apte.flags   set pre-empt flag

    tra pre_empt        join common code



" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   DELETE_CPU - Cause a pre-empt to stop CPU.
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

delete_cpu:
    lca 1,dl        all ones in A
    era prds$processor_pattern  get bit for CPU to be deleted
    ansa    scs$connect_lock    undo the connect lock

    tra cause_pre_empt  now, cause a pre-empt to take place

" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   CAM_WAIT -- Wait for all cpus to clear, and originator to
"         give "Go"
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

cam_wait: 
    eppsb   prds$       push a frame onto the prds
    ldx7    push        ..
    tsx0    fim_util$push_stack_32  ..

    eppap   notify_regs ap -> place to copy conditions
    tsx0    fim_util$copy_mc    copy the conditions into stack

    tsx0    fim_util$set_mask   must uninhibit to prevent lockup
    inhibit off <-><-><-><-><-><-><-><-><-><-><-><->

    lda prds$processor_pattern  get bit for this CPU
    cana    scs$cam_wait    still waiting?
    tze *+4     if not, exit loop
    llr 72
    llr 72
    tra *-4     if so, wait more

    tsx0    fim_util$restore_mask  can inhibit again
    inhibit on  <+><+><+><+><+><+><+><+><+><+><+><+>

    cams    0       now, clear A.M.'s
    camp    0       ..

    eppap   prs,*       prepare to copy machine conditions back
    tsx0    fim_util$copy_mc    ..

    eppap   sp|-32      get original stack end pointer
    spriap  sb|stack_header.stack_end_ptr  pop our stack frame
    tra more        and leave

" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   START_WAIT - Wait until new CPU has started up.
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

start_wait:
    lca 1,dl        all ones in A
    era prds$processor_pattern  turn off bit for this CPU
    ansa    scs$processor_start_wait  check ourselves off

    eppsb   prds$       push a frame onto the prds
    ldx7    push        ..
    tsx0    fim_util$push_stack_32  ..

    eppap   notify_regs ap -> place to copy conditions
    tsx0    fim_util$copy_mc    copy the conditions into stack

    tsx0    fim_util$set_mask   uninhibit to prevent lockups
    inhibit off <-><-><-><-><-><-><-><-><-><-><-><->

    szn scs$connect_lock    test connect lock
    tze *+4     wait until it is cleared
    llr 72
    llr 72
    tra *-4

    tsx0    fim_util$restore_mask  can inhibit again
    inhibit on  <+><+><+><+><+><+><+><+><+><+><+><+>

    eppap   prs,*       prepare to copy machine conditions back
    tsx0    fim_util$copy_mc    ..

    eppap   sp|-32      get original stack end pointer
    spriap  sb|stack_header.stack_end_ptr  pop our stack frame
    tra exit        all finished waiting

" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   PRE_EMPT - Handle process pre-emptions.
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

pre_empt:
    tsx0    ring_test       ring 0 & not idle?
    tra ralr_set_1  if so, delay this for later

    eppap   pds$page_fault_data copy machine conditions
    tsx0    fim_util$copy_mc    ..

    tsx0    fim_util$set_mask   mask system controller

    eppsb   prds$       establish stack frame on the prds
    ldx7    push        ..
    tsx0    fim_util$push_stack ..
    inhibit off <-><-><-><-><-><-><-><-><-><-><-><->

    tra pxss$pre_empt   go to pxss



" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   PRE_EMPT_RETURN -- Transferred to when pre_empt has been processed
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

pre_empt_return:
    epplp   my_linkage_ptr,*
    eppbp   pds$page_fault_data get pointer to machine conditions

    tsx0    fim_util$restore_mask  restore old controller mask
    inhibit on  <+><+><+><+><+><+><+><+><+><+><+><+>

    ldaq    sb|stack_header.stack_begin_ptr  restore prds to virgin state
    staq    sb|stack_header.stack_end_ptr  ..

    odd
    tsx0    fim_util$v_time_calc  compute virtual time

    lpl bp|mc.eis_info  restore EIS ptrs and lths
    lreg    bp|mc.regs  restore regs
    lpri    bp|mc.prs       and prs
    rcu int_scuinfo,*   bye-bye

" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   STOP - Handle process stop
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

stop:
    tsx0    ring_test       ring 0 & not idle?
    tra ralr_set_1  if so, delay handling this

    eppap   pds$page_fault_data copy machine conditions
    tsx0    fim_util$copy_mc    ..

    tsx0    fim_util$set_mask   mask system controller

    eppsb   prds$       Establish stack on prds
    ldx7    push        ..
    tsx0    fim_util$push_stack ..
    inhibit off <-><-><-><-><-><-><-><-><-><-><-><->

    tra pxss$force_stop go to pxss

    inhibit on  <+><+><+><+><+><+><+><+><+><+><+><+>

" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   IPS - Handle Interprocess Signals
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

ips:
    tsx0    ring_test       ring 0 & not idle?
    tra ralr_set_1  we'll hear about this later

    eppap   pds$signal_data copy the machine conditions
    tsx0    fim_util$copy_mc    ..

    eppsb   pds$stack_0_ptr,*   Establish pageable stack.
    ldx7    push        ..
    tsx0    fim_util$push_stack ..

    tsx0    fim_util$v_time_calc  compute virtual time
    inhibit off <-><-><-><-><-><-><-><-><-><-><-><->

    call    proc_int_handler$proc_int_handler

    inhibit on  <+><+><+><+><+><+><+><+><+><+><+><+>
    eppap   prs,*       copy SCU data only back
    mlr (pr),(pr)       ..
    desc9a  bp|mc.scu,8*4
    desc9a  ap|mc.scu,8*4

    sprisp  sb|stack_header.stack_end_ptr  release our stack frame
    tra exit_1      all finished

" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"   RING_TEST - internal subroutine to see if process
"       is in ring 0 and not an idle process
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

ring_test:
    lda bp|mc.scu.ppr.prr_word  get PRR
    ana scu.ppr.prr_mask,du ..
    tnz 1,0     if outer ring, take second return

    lda lb|apte.flags   get process flags
    cana    apte.idle,du    idle process?
    tze 0,0     if not, take first return

    epaq    sp|0        get stack pointer in AQ
    eax7    0,au        stack segno in X7
    cmpx7   trouble_scuinfo is it prds?
    tze 0,0     if so, go away
    tra 1,0     if not, take second return

" 

push:   push


    include mc

" 

    include stack_header

" 

    include scs

    include sys_trouble_codes

" 

    include apte
"
    include fault_vector



    

    end

"This material is presented to ensure dissemination of scholarly and technical work. Copyright and all rights therein are retained by authors or by other copyright holders. All persons copying this information are expected to adhere to the terms and constraints invoked by each author's copyright. In most cases, these works may not be reposted without the explicit permission of the copyright holder."