asm {
INT_MP_CRASH_ADDR:: //Forward reference to work around compiler
        DU32    &IntMPCrash;

        PUSH    RDX
        PUSH    RAX
        MOV     EAX, &dev
        MOV     EDX, U32 LAPIC_EOI
        MOV     RAX, U64 CDevGlobals.uncached_alias[RAX]
        MOV     U32 [RAX+RDX], 0
        POP     RAX
        POP     RDX


        MOV     RAX, U64 [RSP]
        MOV     U64[RSI], RAX
        MOV     RAX, U64 16[RSP]
        MOV     U64 CTask.rflags[RSI], RAX
        MOV     RAX, U64 24[RSP]
        MOV     U64 CTask.rsp[RSI], RAX

        XOR     RAX, RAX
        MOV     RDI, U64 GS:CCPU.addr[RAX]
        INC     U64 CCPU.total_jiffies[RDI]

        BT      U64 CTask.task_flags[RSI], TASKf_IDLE
        JNC     @@05
        INC     U64 CCPU.idle_pt_hits[RDI]

@@05:   MOV     RAX, U64 CCPU.profiler_timer_irq[RDI]
        TEST    RAX, RAX
        JZ      @@10
        PUSH    RSI
        CALL    RAX         //See ProfTimerInt().
        JMP     @@15
@@10:   ADD     RSP, 8
@@15:   CLI
        MOV     RAX, U64 CCPU.num[RDI]
        TEST    RAX, RAX
        JZ      @@20

        MOV     EAX, &dev
        MOV     EDX, U32 LAPIC_EOI
        MOV     RAX, U64 CDevGlobals.uncached_alias[RAX]
        MOV     U32 [RAX + RDX], 0
        JMP     @@25

@@20:   CALL    &IntCore0TimerHandler   //Only Core 0 calls this.
@@25:   XOR     RAX, RAX
        CMP     RSI, U64 GS:CCPU.idle_task[RAX]
        JMP     I32 RESTORE_RSI_TASK

        PUSH    RBX
        PUSH    RAX
        MOV     BL, U8 16[RSP]  //We pushed fault_num IntFaultHandlersNew().
        XOR     RAX, RAX
        MOV     FS:U8 CTask.fault_num[RAX], BL
        POP     RAX
        POP     RBX
        ADD     RSP, 8                  //Pop fault_num


        XOR     RDX, RDX
        MOV     U64 CTask.fault_err_code[RSI], RDX
        MOV     EDX, U32 CTask.fault_num[RSI]
        JNC     @@1
        POP     U64 CTask.fault_err_code[RSI]

@@1:    MOV     RAX, U64 [RSP]
        MOV     U64[RSI], RAX
        MOV     RAX, U64 16[RSP]
        MOV     U64 CTask.rflags[RSI], RAX
        MOV     RSP, U64 24[RSP]
        MOV     U64 CTask.rsp[RSI], RSP
        MOV     RBP, CTask.rbp[RSI]
        PUSH    U64 CTask.fault_err_code[RSI]
        PUSH    U64 CTask.fault_num[RSI]
        MOV     RSI, CTask.rsi[RSI]
        CALL    &Fault2                 //See Fault2
        JMP     I32 RESTORE_FS_TASK

        DU32    0x00027D00, 0, 0, 0, 0, 0, 0, 0;

U8 *IntEntryGet(I64 irq)
{//Get interrupt handler.
    I64          handler_addr;
    CIDTEntry   *entry = &dev.idt[irq];

    handler_addr.u16[0] = entry->offset_low;
    handler_addr.u16[1] = entry->offset_mid;
    handler_addr.u32[1] = entry->offset_hi;

    return handler_addr;

U8 *IntEntrySet(I64 irq, U0 (*fp_new_handler)(), I64 type=IDTET_IRQ)
{//Set interrupt handler. Returns old handler. See IDTET_IRQ.
//See ::/Demo/Lectures/InterruptDemo.ZC.
//See ::/Demo/MultiCore/Interrupts.ZC.
    I64          fp = fp_new_handler;
    U8          *old_handler;
    CIDTEntry   *entry;

    old_handler = IntEntryGet(irq);
    entry = &dev.idt[irq];
    entry->seg_select = offset(CGDT.cs64);
    entry->offset_low = fp.u16[0];
    entry->offset_mid = fp.u16[1];
    entry->offset_hi  = fp.u32[1];
    entry->type_attr  = 0x80 + type; //bit 7 is 'segment present'
    entry->ist = entry->zero = 0; //We don't care about the IST mechanism

    return old_handler;

I64 IntEntryAlloc()
{ // 'Allocate' a user irq num.
  // 64 user irqs available, 0xFF <--> 0xBF.
  // Goes backwards from 0xFF.
    I64 i, res = 0;

    for (i = 0xFF; i > 0xFF - 64; i--)
        if (!Bts(&dev.user_int_bitmap, i - 192))
            res = i;

    return res;

U0 IntPICInit()
{//Init 8259
    OutU8(PIC_1, PIC_INIT); //IW (Initialization Word) 1
    OutU8(PIC_2, PIC_INIT); //IW1

    OutU8(PIC_1_DATA, 0x20); //IW2 Moving IRQ base from 0 -> 32 (beyond Intel reserved faults)
    OutU8(PIC_2_DATA, 0x28); //IW2 Moving IRQ base from 8 -> 40
    OutU8(PIC_1_DATA, 0x04); //IW3 Telling PIC_1 PIC_2 exists.
    OutU8(PIC_2_DATA, 0x02); //IW3 Telling PIC_2 its cascade identity.
    OutU8(PIC_1_DATA, 0x0D); //IW4 8086 Mode, Buffered Mode (Master PIC)
    OutU8(PIC_2_DATA, 0x09); //IW4 8086 Mode, Buffered Mode (Slave PIC)
    OutU8(PIC_1_DATA, 0xFA); //Mask all but IRQ0 (timer) and IRQ2 Cascade.
    OutU8(PIC_2_DATA, 0xFF);

interrupt U0 IntNop()
{//Make unplanned IRQs stop by all means!
    OutU8(PIC_2, PIC_EOI);
    OutU8(PIC_1, PIC_EOI);
    *(dev.uncached_alias + LAPIC_EOI)(U32 *) = 0;

I64 IntEntryFree(I64 irq)
{ // 'Free' a user irq num. Unsets bit in user irq bitmap.
    I64 res = 0;

    if (0xFF >= irq >= 0xFF - 64)
        if (res = Btr(&dev.user_int_bitmap, irq - 192))
            IntEntrySet(irq, &IntNop); // clear the irq entry vect

    return res;

interrupt U0 IntDivZero()
    if (Gs->num)
        mp_count = 1;
        debug.mp_crash->cpu_num = Gs->num;
        debug.mp_crash->task = Fs;
        MOV RAX, U64 8[RBP] //Get RIP off of stack.
        debug.mp_crash->rip = RAXGet;
        debug.mp_crash->message = "Div Zero";
        debug.mp_crash->message_num = 0;
        MPInt(I_MP_CRASH, 0);

U8 *IntFaultHandlersNew()
    I64 i;
    U8 *res = MAlloc(256 * 7, Fs->code_heap), *dst = res;

    for (i = 0; i < 256; i++)
        *dst++ = 0x6A; //PUSH I8 xx
        *dst(I8 *)++ = i;
        *dst++ = 0xE9; //JMP    I32 xxxxxxxx
        *dst(I32 *) = INT_FAULT - dst - 4;
        dst += 4;

    return res;

U0 IntInit1()
{//Interrupt descriptor table part1.
    I64             i;
    CSysLimitBase   tmp_ptr;

    if (!Gs->num) //Gs is current CCPU struct
        dev.idt = CAllocAligned(sizeof(CIDTEntry) * 256, 8);
        for (i = 0; i < 256; i++)
            IntEntrySet(i, &IntNop);
    tmp_ptr.limit = 256 * sizeof(CIDTEntry) - 1;
    tmp_ptr.base = dev.idt;
    LIDT U64 [RAX]

U0 IntInit2()
{//Interrupt descriptor table part2: Core 0 Only.
    I64 i;

    IntEntrySet(I_DIV_ZERO, &IntDivZero);
    for (i = 1; i < 32; i++)
        IntEntrySet(i, &debug.int_fault_code[7 * i]);
/*In theory, we use the PIC mask reg to insure we don't get
anything but keyboard, mouse and timer IRQs.    In practice, Terry had
gotten IRQ 0x27, he thought perhaps because he didn't initialize the APIC?
We go ahead and ACK PIC in IntNop().
He had no idea why he got a IRQ 0x27.
    IntEntrySet(I_NMI,       _SYS_HLT);
    IntEntrySet(I_TIMER,     IRQ_TIMER);
    IntEntrySet(I_MP_CRASH, *INT_MP_CRASH_ADDR(U32 *));
    IntEntrySet(I_WAKE,      INT_WAKE);
    IntEntrySet(I_DEBUG,    &debug.int_fault_code[7 * I_DEBUG]);