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!
Pingback: The Morning Brew - Chris Alcock » The Morning Brew #2057