Additional documentation for @print_table opcode?

The description of @print_table in ZMS 1.1 is quite spare:

Print a rectangle of text on screen spreading right and down from the current cursor position, of given width and height, from the table of ZSCII text given. (Height is optional and defaults to 1.) If a skip value is given, then that many characters of text are skipped over in between each line and the next. (So one could make this display, for instance, a 2 by 3 window onto a giant 40 by 40 character graphics map.)

Its behavior seems to vary across different interpreters, which is perhaps understandable given the above. Is there any additional documentation on this opcode anywhere? If not, can anyone answer:

  1. What kind of memory structure is referred to as a “table” in the above?
  2. What constitutes a “line” in the above?
  3. What does it mean for a character to be “skipped over” in the above?

The terminology of “table” and “line” seems similar to that used when describing the @print_form opcode, but the behavior of the two opcodes is quite different when feeding the same data structure to both (at least on Frotz 2.53 and WinFrotz 1.21), so it’s not clear that it is supposed to be the same kind of table.

  1. A table is a size word followed by content.
  2. A new line is started every width characters.
  3. Skip skips over existing printed characters before printing the text from the table. If skip is 5, then every printed line of the table will begin on the sixth column of the screen.

@Mike_G, thank you for the response. Will you cite an interpreter that behaves as you describe? None of Frotz 2.53, WinFrotz 1.21 or Bocfel 1.3.2 seems to operate in that manner.

Have you got an example showing it not working? As far as I know, @print_table is reasonably well implemented: there’s a test case in Praxix (from Index: if-archive/infocom/interpreters/tools) and there was a Frotz issue related to this opcode not that long ago with test cases (@print_table doesn't handle non-ASCII chars correctly (#226) · Issues · David Griffith / frotz · GitLab).

Here’s the test code I was using to try to figure out what @print_table actually does:

Test Code
Constant Story "print_table test";
Constant Headline "^(an underdocumented Z-machine feature)^";

Include "Parser";
Include "VerbLib";
Include "Grammar";

Constant FORMSIZE = 500;

Array sample_table buffer FORMSIZE;

Global table_pointer = 0;

Array temp_text buffer 150;

Class Room
    has light;

Room Start "Starting Point"
    with    description
                "An uninteresting room.";

[ AddToTable str tmpbuf loc i ;
    str.print_to_array(tmpbuf);         ! temporary storage
    loc = sample_table + table_pointer; ! set pointer to start of entry
    ! print "<tmpbuf-->0 = ", tmpbuf-->0, ">^";
    loc-->0 = tmpbuf-->0;               ! copy length to table
    ! print "<copying: ";
    for (i=0: i<(tmpbuf-->0): i++) {    ! copy data to table
        loc->(i+WORDSIZE) = tmpbuf->(i+WORDSIZE);
    !     print (char) tmpbuf->(i+WORDSIZE);
    ! print ">^";
    table_pointer = table_pointer+WORDSIZE+((sample_table+table_pointer)-->0);
    ! print "<new pointer = ", table_pointer, ">^";

[ Initialise ;

    location = Start;

    AddtoTable("abc", temp_text);
    AddtoTable("123", temp_text);
    AddtoTable("TUVWXYZ", temp_text);

    print "^<print_table 5 x 5>^";
    @print_table sample_table 5 5;

    print "^<print_table 5 x 10>^";
    @print_table sample_table 5 10;

    print "^<print_table 10 x 5>^";
    @print_table sample_table 10 5;

    print "^<print_table 10 x 10>^";
    @print_table sample_table 10 10;

    print "^<print_table 5 x 5 skip 3>^";
    @print_table sample_table 5 5 3;

    print "^<print_table 10 x 10 skip 3>^";
    @print_table sample_table 10 10 3;


In particular, the calls that make use of the skip parameter seem to consistently produce problematic results across multiple interpreters.

Note that the “table” construct is assembled to contain multiple buffer-like arrays laid end-to-end, as would be used with @print_form. If @print_table is supposed to operate on only one such “table” (i.e. one buffer-like array) then presumably anything past the length indicated by the initial word shouldn’t matter.

Ah, okay, the problem is that here “table of zscii text” does not mean “a size word followed by content”. The core point is that the functionality of @print_table is “print this array of ZSCII laid out in tabular form” not “print out these tables”. The use of “table” here is unfortunate, it would be better if the opcode were something like “print_grid” and the specification gave the first argument as “grid of zscii text” or just “array of zscii text”. The first argument should just be an array of the ZSCII characters that form the table / grid.

There is also a nuance not covered by the spec - I thought it was mentioned somewhere, but I can’t find it. Quoting form the x-zip spec:

PRINTT with a height greater than one may only be used in screen 1.

As mentioned in the other thread, I thought to look up @print_table in the Infocom YZIP specification ( In this the opcode is called PRINTT:

PRINTT bytes:tbl,width:int,height:int,skip:int

PRINTT takes a table of bytes, a width (a number of columns) and optionally a height (a number of lines), which is assumed to be one if omitted. It also optionally takes a skip, which is how many bytes of atable to skip over at the end of each line (by default, none).

It prints, in a block at the current cursor position, bytes from the table. Each group of width bytes is printed on a separate line aligned with the first, until height lines have been printed. Each time widthbytes have been printed, skip bytes are skipped over. The skipparameter allows a rectangular block of text from anywhere within a rectangular table (one where the rows are stored) to be printed.

However, in reading this, you’ve got to bear in mind what this document means by “table”. From page 19:

Tables are in fact only a useful logical concept and have no physical form in the Z-machine. (However the assembler, ZAP and debugging ZIPs, do “know” about tables.) Table pointers are simply byte-pointers to appropriate locations in the Z program.

Offsets in tables are zero-based. The first element of a table is element zero, the second is element 1, and so on.

You are right about the unfortunate use of the term table. The standard describes a table is 15.2 as “Specifically, a table is an array of words (in dynamic or static memory) of which the initial entry is the number of subsequent words in the table.

Looking back at the last time I implemented this (a long time ago), I can see it is just raw text with no size indicator as you described.

I wonder if that is general enough to deal with version 6 windows?

No, this is removed in the y-zip spec, as quoted by @DavidK above.

Thank you once again for the critical clarifying information, @DavidK! (And for the link to the YZIP documentation.) It’s now a lot clearer what the intended usage of the opcode is.

I don’t see where the y-zip spec says this opcode was removed. Certainly the standard doesn’t say that.

That’s not what he said. He said the sentence “PRINTT with a height greater than one may only be used in screen 1.” is in the x-zip spec but not in the y-zip spec.


Ok, that makes a lot more sense.

Yes, this is what I meant. Moreover, it isn’t very clear to me what happens when buffering is on and height is > 1 (as I’m not very familiar with the v6 screen model). I remember this led to some peculiar results with Praxix trying to use it in the lower window (v5).