//processb.bc - Lampson&Metcalfe Process Primitives - BCPL

// get "PROCSYMS"
// PROCSYMS - Symbols for process handling in BCPL

//Define process control block structure (PCB)
structure PCB:
	[
	//Dummy frame at base of process's stack
	DummyF word 2 //Dummy frame at top of process's stack

	//Interrupts come here directly for context switch
	Save3 word //Instruction to save ac 3 below
	JSRProcessSave word //Go to shared context switch code

	//State of process if not running
	Saved0 word //Machine context
	Saved1 word
	Saved2 word
	Saved3 word
	SavedCarry word
	SavedPC word
	SavedStackMinimum word //Saved stack minimum
	SavedInterruptsActive word

	//PCB pointer for process interrupted by ours
	PreviousPCBPointer word
	Previous3 word //Used during context switch
	Previous2 word

	//Address of shared context switching code
	AdrProcessSave word //Points to shared context switch code

	StackLow word //Will contain lowest stack pointer yet
	ProcessNumber word
	State word //0 running, 1 suspended, 2 blocked
	PCBPointer word
	LinkPointer word //For stringing PCB's
	]

manifest PCBSize=((size PCB)+15)/16
manifest StackMinimum=#335

manifest msb=#100000

//Define BCPL frame structure for following manipulations
structure Frame:
	[
	CallersF word
	SavedReturn word
	Temp word
	ExtraArgs word
	Formals word
	]

//Queue stuff
structure Queue:
	[
	Head word //Pointer to first item
	Tail word //Pointer to last item
	]

//Scheduler stuff
structure Schedule:
	[
	LinkPointer word //For SchedQueue
	Predicate word //Called with S, boolean returned
	Action word //If predicate true, called with S
	Arg1 word //Arguments to Predicate and Action
	Arg2 word
	Arg3 word
	]

// Outgoing externals
external [
	BeginProcessInit; BCPLStackHandler
	]

// Incoming externals
external [
	GetStackPointer
	BMOVE
	SWAT
	]

// Incoming statics
external [
	PCBTemplate
	ProcessSave
	]


//Create process context and return running in it
let BeginProcessInit(ProcNum,Stack,Size)=valof
	[
	//Compute and check space provided is enough for now
	let MyF=GetStackPointer()
	let CallersF=MyF>>Frame.CallersF
	let CallersFSize=(CallersF>>Frame.CallersF)-CallersF
	if (Size ls (CallersFSize+PCBSize)) then
		SWAT("PCB and starting frame no fit")

	//Move process control block template into space
	let NewPCB=Stack+Size-PCBSize //Process Control Block
	BMOVE(PCBTemplate,NewPCB,PCBSize-1)

	//Copy frame of caller for initial process context
	let ProcessF=NewPCB-CallersFSize //A copy
	BMOVE(CallersF,ProcessF,CallersFSize-1)

	//Link copied frame to dummy in new PCB
	let PCBF=lv (NewPCB>>PCB.DummyF) //Frame in PCB
	ProcessF>>Frame.CallersF=PCBF
	PCBF>>Frame.CallersF=CallersF //For EndProcessInit
	PCBF>>Frame.SavedReturn=SWAT-1 //SWAT if process returns

	//Finish setting up new PCB
	NewPCB>>PCB.PCBPointer=NewPCB //For CurrentPCB
	NewPCB>>PCB.SavedStackMinimum=Stack
	NewPCB>>PCB.Saved2=ProcessF //Context is copied frame
	NewPCB>>PCB.AdrProcessSave=ProcessSave
	NewPCB>>PCB.ProcessNumber=ProcNum
	NewPCB>>PCB.SavedInterruptsActive=(1 lshift ProcNum)-1
	NewPCB>>PCB.StackLow=-1 //Mark unrecorded
	NewPCB>>PCB.State=2 //Mark process blocked
	NewPCB>>PCB.PreviousPCBPointer=0 //No previous (yet)

	if PCBTemplate>>PCB.ProcessNumber eq 0 then		[
	PCBTemplate>>PCB.PCBPointer=PCBTemplate //For CurrentPCB
	  PCBTemplate>>PCB.ProcessNumber=-1
	  PCBTemplate>>PCB.StackLow=-1 //Mark unrecorded
	  PCBTemplate>>PCB.State=0 //Mark process running
	  PCBTemplate>>PCB.PreviousPCBPointer=0 //None
	  PCBTemplate>>PCB.AdrProcessSave=SWAT-1 //Never blocks
	  ]

	//Switch to process's context for initalization
	MyF>>Frame.CallersF=ProcessF //Process's context
	NewPCB>>PCB.SavedStackMinimum=@StackMinimum
	@StackMinimum=Stack //So init can call things
	resultis lv (NewPCB>>PCB.Save3) //Int vec
	]


and BCPLStackHandler() be
	[
	manifest [ AdrGetFrame=#370]
	manifest [ CallSWAT=#77400]
	@((@AdrGetFrame)+30)=CallSWAT
	]