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 https://github.com/ericlippert/flathead/tree/blog9
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!