// STATE.BCPL		Save/Restore state of program
// E. R. Fiala -- last edited January 20, 1977  11:37 AM

// Programs using this package should not call GetFixed until after
// StorageInit is called, else it will be impossible to add initialization
// code at the end of the program to the free storage pool.  StorageInit
// should be called immediately after the Junta (if any).

// The program must pass the address of the first static and last static
// to StorageInit.  These are obtained from the layout vector which is an
// argument to the entry procedure of the program (FirstStatic = Layout!26
// and LastStatic = Layout!27).  It seems wasteful to save all the
// procedure statics, but this is necessary if the Overlay package is
// used, and it is easier to save them all than worry about it.

// All storage to be saved is acquired by the GetStorage routine, so the
// state of the program consists of its statics (all of which are saved),
// its page zero items (explicitly saved by calls on SaveStatics), and the
// core between EndStorage at the time BeginSave is called and EndStorage
// at the time SaveState is called.

// Consecutive calls to GetStorage allocate blocks with decreasing
// addresses.  There is no header word associated with a block, so it
// is impossible to selectively deallocate blocks acquired by GetStorage.
// The current position at which blocks are being obtained is contained
// in the static EndStorage.  Hence, if you want to subsequently free
// all the blocks back to some point, you have to save the value of
// EndStorage at that point and restore later.  If you want to manage
// a region selectively, build a zone from a block acquired by
// GetStorage and use the standard Alloc package.

// After executing an initialization procedure, its code can be added to
// the free storage pool by doing Storage = InitProc.

// The static StateBlockSize must be defined elsewhere in the program

// BeginSave is called to mark the current position in free storage.
// All storage acquired after that time and before SaveState is called
// will be saved by SaveState, in addition to StateBlock, all of the
// program's statics, and all page-zero statics enumerated by calls
// to SaveStatics.  There is no provision for enumerating individual
// blocks of storage outside the region delimited by BeginSave and
// SaveState.

// The first call to SaveState passes a stream argument and a program
// label argument.  The caller's frame and this label are remembered
// for subsequent RestoreState calls.  Later on the program can call
// SaveState again without the label argument.  This will preserve
// the same items as were preserved in the original call to SaveState,
// allowing the program to fire up with different initial information.

// RestoreState accepts a stream argument for the file containing
// information saved by SaveState.  Its second argument is a flag
// which should be true when RestoreState is called during program
// initialization.  The program can reinitialize itself during
// operation by calling RestoreState with the second argument false.

get "state.d"

external [
// OS
	CallSwat; Closes; ReadBlock; WriteBlock
	CallersFrame; GotoLabel; Usc

// Program
	StateBlockSize

// Defined here
	BeginSave; SaveState; RestoreState
	GetStorage; GetEvenStorage; StorageLeft
	EndStorage; Storage

// Defined here for init only
	NoStore; StateBlock; StatePtr
]

static [ StateBlock; StatePtr; EndStorage; Storage; NoStore ]

let GetStorage(Size) = valof
[	EndStorage = EndStorage-Size
	if Usc(Storage,EndStorage) < 0 then resultis EndStorage
	resultis NoStore("Out of storage")
]

and GetEvenStorage(Size) = (GetStorage(Size+1)+1) & 177776B

and StorageLeft() = EndStorage-Storage


//Mark the first location to be saved
and BeginSave() be StateBlock>>State.End = EndStorage


and SaveState(S,Label; numargs NA) be
[	if S eq 0 then CallSwat("No stream for SaveState")
	if NA ge 2 do		//Delimit region saved & where to resume
	[ StateBlock>>State.Ptr = StatePtr
	  StateBlock>>State.CF = CallersFrame()
	  StateBlock>>State.CL = Label
	  StateBlock>>State.Storage = Storage
	  StateBlock>>State.First = EndStorage
	]
	for I = (size State/16) to StatePtr-2 by 2 do  //Get static values
	[ StateBlock!(I+1) = rv StateBlock!I ]
	WriteBlock(S,StateBlock,StateBlockSize)
	WriteBlock(S,StateBlock>>State.FirstS,StateBlock>>State.NStats)
	WriteBlock(S,StateBlock>>State.First,
		StateBlock>>State.End - StateBlock>>State.First)
	Closes(S)
]


and RestoreState(S,reinitFlag)be
[	if S eq 0 then CallSwat("No stream for RestoreState")
	ReadBlock(S,StateBlock,StateBlockSize)
	if not reinitFlag then
	  if EndStorage ne StateBlock>>State.End then
		CallSwat("Program or OS incompatible")
	ReadBlock(S,StateBlock>>State.FirstS,StateBlock>>State.NStats)
	for I = (size State/16) to StatePtr-2 by 2 do
	[ rv (StateBlock!I) = StateBlock!(I+1) ]
	EndStorage = StateBlock>>State.First
	Storage = StateBlock>>State.Storage
	StatePtr = StateBlock>>State.Ptr
	let WC = StateBlock>>State.End - EndStorage
	let N = ReadBlock(S,EndStorage,WC)
	if N ne WC then CallSwat("State file clobbered")
	Closes(S); GotoLabel(StateBlock>>State.CF,StateBlock>>State.CL,0)
]