// InterruptInit.Bcpl (companion to Interrupt.Asm)
//     This code can be wiped out after initialization is complete
// Copyright Xerox Corporation 1979
// Copyright Xerox Corporation 1979

//	Last modified September 4, 1979  2:30 PM by Taft

external
[
// outgoing procedures
InitializeInterrupt; FindInterruptMask

// incoming procedures
SysErr

// incoming statics
lvUserFinishProc	// To clean up interrupt system

// From Interrupt.Asm:
interruptPrologue; interruptFinish
interruptsActive; savedUserFinishProc
]

//---------------------------------------------------------------
structure IST:		// Corresponds to Interrupt.Asm
//---------------------------------------------------------------
[
Prologue word 2
various word 7		// Old state saved here
NewMask word		// New interrupt mask
Stack word		// Stack pointer to use
StackMin word		// Stack bottom to use
InitialPC word		// Procedure to call
StackBot word 0		// First stack location
]

manifest
[
interruptVector = #501
Active = #453

ecInterruptInUse = 2750
ecInterruptMaskZero = 2751
ecInterruptChanZero = 2752
]

//---------------------------------------------------------------
let InitializeInterrupt(region, length, mask, initialPc) = valof
//---------------------------------------------------------------
[
// Trap all finishes and aborts to reset interrupt system
if savedUserFinishProc eq -1 then
   [
   savedUserFinishProc = @lvUserFinishProc
   @lvUserFinishProc = interruptFinish
   ]

// Check validity of mask
mask = MakeOneBit(mask)
if (mask & @Active) ne 0 then SysErr(nil, ecInterruptInUse)

// Install prologue, various stuff in IST
for i  =  0 to (size IST.Prologue/16)-1 do
   region!i = interruptPrologue!i
region>>IST.NewMask = mask-1
region>>IST.InitialPC = initialPc
region>>IST.Stack = region+length-4  // Minimal frame at top
(region>>IST.Stack)!0 = region + (size IST.Prologue/16)
region>>IST.StackMin = (lv region>>IST.StackBot)

// Discover interrupt channel number
let i = 0
while ((1 lshift i) & mask) eq 0 do i = i+1
interruptVector!i = region      //Enter this way
@Active = @Active % mask      //Let him at it
interruptsActive = interruptsActive%mask
resultis mask
]

//---------------------------------------------------------------
and FindInterruptMask(mask) = valof
//---------------------------------------------------------------
// Find legal channel of next lowest priority
[
mask = MakeOneBit(mask)
while mask ne #100000 do
   [
   if (mask&@Active) eq 0 then resultis mask
   mask = mask lshift 1
   ]
resultis 0
]


//---------------------------------------------------------------
and MakeOneBit(mask) = valof
//---------------------------------------------------------------
//From mask, make legal one-bit mask sensing only highest order bit
[
if mask eq 0 then SysErr(nil, ecInterruptMaskZero)
let i = 0
while (mask&#100000) eq 0 do
   [
   mask = mask lshift 1
   i = i+1
   ]
mask = (#100000 rshift i)
if mask eq #100000 then SysErr(nil, ecInterruptChanZero)
resultis mask
]