Article 1DXQP Ladder room

Ladder room

by
ericlippert
from Fabulous adventures in coding on (#1DXQP)

At the east end of this narrow passage, a ladder leads upward. There's a strong draft from the west, where the passage narrows even further.
There is a small pile of coal here.

> take the coal

Taken.

> go west

You can't fit through with that load.

> go up
> go east
> go northwest
> go west
> go south

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

Last time we got as far as printing out the copyright message of the game. The serial number is encoded in some words in the header, and the code we crashed on will read them out and do some bit arithmetic. I want to implement a bunch of very easy instructions here, which I present without further comment:

let handle_test bitmap flags interpreter = if (bitmap land flags) = flags then 1 else 0let handle_or a b interpreter = a lor blet handle_and a b interpreter = a land b

While I'm at it I want to also implement two instructions that pop the stack. One pops and returns, the other just pops.

In later versions of the Z-machine the pop instruction opcode was used instead for catch, so I'll implement a stub for that while I'm here:

let handle_ret_popped interpreter instruction = let result = peek_stack interpreter in let popped_interpreter = pop_stack interpreter in interpret_return popped_interpreter resultlet handle_pop interpreter = pop_stack interpreter let handle_catch interpreter = failwith "TODO: catch instruction not yet implemented"

All right, that was boring, but now when we run our test program we get a little farther:

MINI-ZORK I: The Great Underground EmpireCopyright (c) 1988 Infocom, Inc. All rights reserved.ZORK is a registered trademark of Infocom, Inc.Release 34 / Serial number 871124Exception: Failure "TODO: 6bc7: test_attr g00 0d ?6bd4 \n ".

My guess would be that the serial number is in fact the date that the release build was made: November 24th, 1987. We'll discuss the significance of the serial number again later when we implement loading and saving.

The next instruction to implement is test_attr. As we know, objects have properties and attributes; attributes are a fixed set of bit flags that have whatever meaning the game implementers decide. Properties are of arbitrary size, and have default values. Since we're doing bitwise operations anyways in this episode it seems reasonable to implement them here. The instruction handlers are just thin wrappers around the actual code, which I'll put with the rest of the object tree code:

let handle_test_attr obj attr interpreter = let obj = Object obj in let attr = Attribute attr in if Object.attribute interpreter.story obj attr then 1 else 0let handle_set_attr obj attr interpreter = let obj = Object obj in let attr = Attribute attr in { interpreter with story = Object.set_attribute interpreter.story obj attr }let handle_clear_attr obj attr interpreter = let obj = Object obj in let attr = Attribute attr in { interpreter with story = Object.clear_attribute interpreter.story obj attr }

Note that I have of course added another wrapper type Attribute to wrap the attribute number. I've also added a tuple type that keeps track of both a byte address and a bit number, since attributes are stored in a bitfield:

type attribute_address = Attribute_address of byte_address * bit_numbertype local_variable = Local of int

And the code in the object module is straightforward:

let attribute_count story = if Story.v3_or_lower (Story.version story) then 32 else 48let attribute_address story obj (Attribute attribute) = if attribute < 0 || attribute >= (attribute_count story) then failwith "bad attribute" else let offset = attribute / 8 in let (Object_address obj_addr) = address story obj in let bit = Bit_number (7 - (attribute mod 8)) in Attribute_address ((Byte_address (obj_addr + offset)), bit)let attribute story obj attribute = let (Attribute_address (address, bit)) = attribute_address story obj attribute in Story.read_bit story address bitlet set_attribute story obj attribute = let (Attribute_address (address, bit)) = attribute_address story obj attribute in Story.write_set_bit story address bitlet clear_attribute story obj attribute = let (Attribute_address (address, bit)) = attribute_address story obj attribute in Story.write_clear_bit story address bit

I added code to the story memory manager to read and write individual bits. It is super boring so I won't bother to mention it here.

Now if we run our program:

MINI-ZORK I: The Great Underground EmpireCopyright (c) 1988 Infocom, Inc. All rights reserved.ZORK is a registered trademark of Infocom, Inc.Release 34 / Serial number 871124West of HouseException: Failure "TODO: 6bec: get_prop g00 0e ->local2 \n ".

We have made it as far as putting the player in the starting room and printing out its name! This is really coming together here.

Next time on FAIC: properties, obviously.


4472 b.gif?host=ericlippert.com&blog=67759120
External Content
Source RSS or Atom Feed
Feed Location http://ericlippert.com/feed
Feed Title Fabulous adventures in coding
Feed Link https://ericlippert.com/
Reply 0 comments