// 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)
]