Reservoir south

You are south of a large lake, far too deep and wide to be crossed. Paths lead east, south and southwest.

> go north

You would drown.

> swim in the lake

Swimming isn’t allowed in the lake.

> go east

Fine, be that way.

Code for this and the next several episodes is at

Two instructions are followed by a zstring-encoded string.

let has_text opcode =
  match opcode with
  | OP0_178 | OP0_179 -> true
  | _ -> false

We already have the method we need to read it; we can just call it:

  let decode_text text_address opcode =
    if has_text opcode then Some (read_zstring text_address)
    else None

We’ll need to know how long, in bytes, the zstring encoded text was, so that we know where this instruction ends and the next one begins. I’ve implemented a little helper method, not shown, to do that.

  let get_text_length text_address opcode =
    if has_text opcode then zstring_length text_address
    else 0

And now we can combine the efforts of the last half dozen episodes to decode a single instruction. I am going to represent the instruction using a record type:

type t =
  opcode   : bytecode;
  address  : instruction_address;
  length   : int;
  operands : operand list;
  store    : variable_location option;
  branch   : (bool * branch_address) option;
  text     : string option;

Recall that in OCaml the generic types are constructed backwards, so string option and operand list are “optional string” and “list of operands” respectively.

Let’s put it all together. I have the byte address of the first byte in the instruction stored in addr:

  let form = decode_form addr in
  let op_count = decode_op_count addr form in
  let opcode = decode_opcode addr form op_count in
  let opcode_length = get_opcode_length form in
  let operand_types = decode_operand_types addr form op_count opcode in
  let type_length = get_type_length form opcode in
  let operand_address = inc_byte_addr_by addr (opcode_length + type_length) in
  let operands = decode_operands operand_address operand_types in
  let operand_length = get_operand_length operand_types in
  let store_address = inc_byte_addr_by operand_address operand_length in
  let store = decode_store store_address opcode ver in
  let store_length = get_store_length opcode ver in
  let branch_code_address = inc_byte_addr_by store_address store_length in
  let branch = decode_branch branch_code_address opcode ver in
  let branch_length = get_branch_length branch_code_address opcode ver in
  let (Byte_address ba) = branch_code_address in
  let text_address = Zstring (ba + branch_length) in
  let text = decode_text text_address opcode in
  let text_length = get_text_length text_address opcode in
  let length =
    opcode_length + type_length + operand_length + store_length +
    branch_length + text_length in
  let address = Instruction address in
  { opcode; address; length; operands; store; branch; text }

We have finally done it; we can completely disassemble any instruction, given a pointer to the instruction.

Next time on FAIC: now that we’ve disassembled it, let’s display it as text. This will be shorter, I promise!

1 thought on “Reservoir south

  1. Pingback: The Morning Brew - Chris Alcock » The Morning Brew #2057

Leave a Reply

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

You are commenting using your 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 )

Connecting to %s