Hades

You have entered the Land of the Living Dead. Thousands of lost souls can be heard weeping and moaning. In a corner are the remains of previous adventurers less fortunate than yourself. A passage exits to the north.
Lying in one corner is a beautifully carved crystal skull. It appears to be grinning at you rather nastily.

> blow out the candles then drop the bell and the book

The flame is extinguished.
brass bell: Dropped.
black book: Dropped.

> take the skull

Taken.

> go north
> go up
> go west


Code for this episode can be found in https://github.com/ericlippert/flathead/tree/blog12.

A stack frame is just a thin wrapper around a local store and an evaluation stack. A frame is created when a routine is called, so we’ll also need to know what instruction the return must branch to when the current call returns. Also, since a call instruction may have a store, it will be convenient to record here what is going to happen with the returned value; we will make a copy of the store from the call and keep it in the frame.

Note that the store is optional. In version 3 every call has a store, but that is not true in later versions.

type t =
{
  stack : Evaluation_stack.t;
  local_store : Local_store.t;
  resume_at : instruction_address;
  store : variable_location option
}

let resume_at frame =
  frame.resume_at

let store frame =
  frame.store

let peek_stack frame =
  Evaluation_stack.peek frame.stack

let pop_stack frame =
  { frame with stack = Evaluation_stack.pop frame.stack }

let push_stack frame value =
  { frame with stack = Evaluation_stack.push frame.stack value }

let write_local frame local value =
  { frame with local_store = Local_store.write_local frame.local_store local value }

let read_local frame local =
  Local_store.read_local frame.local_store local

let display frame =
  let (Instruction resume_at) = frame.resume_at in
  let locals = Local_store.display frame.local_store in
  let stack = Evaluation_stack.display frame.stack in
  Printf.sprintf "Locals %s\nStack %s\nResume at:%04x\n"
    locals stack resume_at

A set of frames is logically a stack. When I first wrote this I just made it a list, but then I realized that it ought to be impossible for the bottom-most frame on the frame set to ever be popped off, because the main routine never returns in the Z-machine. I therefore made it the initial frame, for the main routine, followed by a list of subsequent frames. This reduced the set of weird failure cases I had to handle to one: trying to remove the initial frame.

Notice how all the setters create a new frame, and then create a new frameset with the new frame on top. Again, this is a purely functional data structure; everything is immutable.

type t =
{
  initial_frame : Frame.t;
  frames : Frame.t list
}

let make initial_frame =
  { initial_frame ; frames = []}

let current_frame frameset =
  match frameset.frames with
  | [] -> frameset.initial_frame
  | h :: _ -> h

let set_current_frame frameset frame =
  match frameset.frames with
  | [] -> { frameset with initial_frame = frame }
  | _ :: t -> { frameset with frames = frame :: t }

let add_frame frameset frame =
  { frameset with frames = frame :: frameset.frames }

let remove_frame frameset =
  match frameset.frames with
  | [] -> failwith "Attempting to remove initial frame"
  | _ :: t -> { frameset with frames = t }

let peek_stack frameset =
  Frame.peek_stack (current_frame frameset)

let pop_stack frameset =
  set_current_frame frameset (Frame.pop_stack (current_frame frameset))

let push_stack frameset value =
  set_current_frame frameset (Frame.push_stack (current_frame frameset) value)

let read_local frameset local =
  Frame.read_local (current_frame frameset) local

let write_local frameset local value =
  set_current_frame frameset (Frame.write_local (current_frame frameset) local value)

let display frameset =
  (accumulate_strings Frame.display frameset.frames) ^ (Frame.display frameset.initial_frame) 

Taking inventory: today we can read and write story memory, decode the dictionary, decode the object tree topology, decode any instruction, decode routines, fill in local variable default values, and we have data structures for all the interpreter state.

Next time on FAIC: At long last, let’s finally start on an interpreter!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s