Geoff Mode

Last update: 23 October 2002

Geoff's homepage ->Jet Set Willy -> Geoff Mode explained

What is "Geoff Mode"?

It's a set of changes to the original JSW game engine, which iscorrespondingly known as "Matthew Mode". There are three types ofchange:

You can also look at a disassembly of theGeoff Mode engine.

Cosmetic changes

These don't affect the gameplay, but are the easiest ways to tell ifyou're playing a Geoff Mode game by just looking at it.

The lives counter

Three changes here:

The time counter

This no longer shows a time, but a 7-digit number which counts downfrom 9999999. This dates from the earliest days of Geoff Mode, and Ican no longer remember why I did it.

The pause colour changes

I think there's a difference here, but I can't remember what!

The tune

In Matthew Mode, the general pitch (i.e. the key) of the tune dropswith each life lost; with sixteen lives, this means that the tune isunlistenable at the beginning. In Geoff Mode, the key depends on theroom number instead (specifically, the room number ANDed with0x1C). If you want to change this, the code takes up 9 bytes at #8B51.

The items

These animate through eight colours as in JSWII, not just four as inMatthew Mode.

Functional modifications

These are either invisible changes which don't affect the gameplay, orbugfixes.

The Block Graphics Bug

In Matthew Mode, the basic room layout is generated by drawing theattributes first, then using a CPIR to find the block design:

8D4E DD7E00   LD   A, (IX+#00)8D51 21A080   LD   HL, #80A08D54 013600   LD   BC, #00368D57 EDB1     CPIR

The bug works like this. Suppose your stair block is yellow on black(i.e. the attribute byte is #06), and your liquid (or water) blockcontains a line like this:

[possibly more rows].....##.    06  <------[possibly more rows]

The CPIR will find the byte marked with the arrow and draw the stairblock using the eight bytes following it, meaning that your staircasewon't look right. The solution is to ensure that the attribute bytefor a given block does not appear in the data for an earlier block.

In Geoff Mode, the room-drawing routine has been completely redrawn,and this bug has been slain. The core of the routine is at #8D96,which prints a single block; it starts with HL pointing at the block'sattribute btye (i.e. #80a0 + 9 * block_number) and DE pointing at theaddress of the attributes on the screen.

A side-effect of this is that you can have two different blocks withthe same attributes, and they'll be displayed differently. But they'llboth behave like each other, so if you do this with stairs andfile/plasma blocks, you'll have two sets of deadly staircaseblocks. There's no easy way to fix this.

Data areas have been moved around

This was done so that the data areas would be less fragmented. Theareas affected are:

Data areaMatthew ModeGeoff Mode
Item count#a3ff#8451
Start address3379234758
Title screen attributes#9800-#9aff#b700-#b9ff
Item table#a400-#a5ff#ba00-#bbff
Guardian classdefinitions#a000-#a3ff#bc00-#bfff


These changes are, of course, your principal reasons for abandoningMatthew Mode for good.

The patch vector

Two bytes within the room data specify thelocation of a subroutine which is called every tick, after Willy andall the guardians, items and arrows have been displayed but before theroom data is finally copied to the screen. This subroutine, which maybe different for each room, is known as the "patch vector", fornow-forgotten reasons.

Patch vectors are potentially very powerful; they are used toimplement the teleport, for example. To see what patch vectors arecapable of, have a look at thedisassemblies of the patch vectors in my ownJSW games. The data at locations #85CB to #85E2 is often useful withinpatch vectors; for example, ANDing the "timer" byte at #85CB withsomething and doing a RET NZ afterwards will execute the patch vectoronce every few ticks. This might be useful, for example, if your patchvector reverses the directions of the conveyors.

Because it's a subroutine, it should end with either a RET as usual,or a jump to a subroutine which ends with a RET.

The teleporter

This facility is most conveniently called within a patch vector, likethis example from "Downhill" in "Willy Takes A Trip"

8728 11D55C   LD   DE, #5CD5872B 0E02     LD   C, #02872D C3B287   JP   #87B2     ; SEE NOTE BELOW!!!

The teleporter is activated when Willy is at the location given by DE;he is transported to the same location in the room specified byC. Obviously, this should be a different room from the one he'scurrently in, otherwise he'll be teleported to where he is over andover again. It is probably possible to alter the routine so thatyou're transported to a different screen address, but I'll leave thisto someone else to implement.

NOTE: the teleporter has been moved to #8C02 in the latest version ofGeoff Mode, and is likely to stay there for the forseeablefuture. #87B2 is currently empty.

Willy's Sprite

As with the lives counter, the sprite which displays Willy on thescreen is specified in the room data, ratherthan being hardcoded to the Nightmare Room as in Matthew Mode. Thesprite may use the entries within a page in one of the followingmodes:

  1. The lower four images.
  2. The upper four images. With this option, and the preceding, Willy will always face in the same direction if you're using eight-position sprites.
  3. All eight images. If you're using four-position sprites, the sprite will change when Willy changes direction.
  4. All eight images, in reverse order. With this you can make Willy go backwards.

Ropes are nicer

In Matthew Mode, because of some lazily inefficient programming, thedata for a "rope" guardian takes up sixteen bytes rather than theusual eight. This means that within the roomdata a rope must be followed by a dummy guardian.

In Geoff Mode, the code has been rewritten so that it (1) takes upless space and (2) only needs the usual eight bytes. So you can haveeight ropes in one room.

NOTE: Strange and rather annoying effects have been noticed with somepathological ropes. I don't remember if this was due to a bug in thecode or weird interactions with other data.

Arrows work differently

In Matthew Mode, the time of firing of any given "arrow" guardian isspecified within the guardian class, and the arrow reappears every 256ticks. In Geoff Mode, the time of firing is specified within theguardian's start byte in the room data, andreappears every 128 ticks. This means that, in practice, you only needone arrow class for each direction (right and left). You also lose theability to specify the arrow's row at pixel level, but I really don'tthink anyone's going to miss that.

Guardians can wrap around

Instead of trotting back and forward as in Matthew Mode, guardians canmove in one direction only and "wrap around" at the end of their path.

Horizontal guardians can go fast

Yes, just like in JSWII, rather than just crawling as in MatthewMode. Actually, speeds of above four times the Matthew Mode crawlaren't terribly useful in practice. See theguardian class format for details.

Horizontal guardians can go diagonally

Yes, just like in JSWII again. See the guardianclass format for details.

New room format

As everybody knows, the data for room N is stored in 256 bytesstarting at #C000 + 256 * N, and is copied to a working area at#8000-#80FF when the room is entered. Changes are highlighted in bluebelow.

Offset (hex)ContentsComments
00-7FBlock layoutEach byte specifies a group of 4adjacent blocks, with two bits per block.
80-9FRoom name32 characters, in ASCII.
A0Gas block attributeAKA "Air".
A1-A8Gas block design Specified by "00" inthe block layout.
A9Liquid block attributeAKA "Water". The ones you canstand on and walk through.
AA-B1Liquid block designSpecified by "01" in the blocklayout.
B2Solid block attributeAKA "Earth". The ones you can'twalk through, but can stand on.
B3-BASolid block designSpecified by "10" in the blocklayout.
BBPlasma block attributeAKA "Fire". The ones thatkill you.
BC-C3Plasma block designSpecified by "10" in the blocklayout.
C4Stair block attribute
C5-CCStair block design
CDConveyor block attribute
CE-D5Conveyor block design
D6Conveyor direction0 = left; 1 = right
D7-D8Location of conveyor left endinattribute buffer at #5c00-#5dff
D9Conveyor lengthShould be <= 32.
DAStair direction0 = \; 1 = /
DB-DCLocation of stair left endinattribute buffer at #5c00-#5dff
DDStair lengthShould be <= 16.
DEBorder colour in bottom 3 bits.
DEWilly on screenmode, in top 2 bits.
DFLives countertop bit = 0 to usebottom half of sprite page, 1 to use top half.
E0Lives countersprite page.
E1-E8Item block design
E9Room to left
EARoom to right
EBRoom above
ECRoom below
EDWilly on screensprite page.
EE-EFAddress of patch vector
F0Class of first guardianor #FF if notpresent
F1Start byte of first guardian
F2-FFData for remaining guardiansinthe same format.

New guardian class format

The data for guardian class N is stored in 8 bytes at #bc00 + 8 *N. The bytes have the following meanings.

Byte 0: diiggttt

d is the starting dirction of the guardian: 0 = left, 1 =right. This is not meaningful for vertical guardians.

iishould be 00. It holds the current phase of the guardian.

gg is only meaningful for horizontal guardians:

ttt specifies the type of the guardian, as follows:

Byte 1: ppphbccc

For ropes, this is the starting position of the rope within itsswing. For arrows, it is ignored. Otherwise:

pppp specifies the number of phases the guardian is animatedwith. The bits should be 111, 011, 001 or 000, which respectivelyspecify 8, 4, 2 or 1 phases.

h is ignored for vertical and purely horizontal guardians. Fordiagonal guardians, it specifies whether the guardian is moved up (if1) or down (if 0) to start with. It is flipped when non-wraparoundguardians change direction.

b is 1 if the guardian is bright, which looks horrible if theroom's backgrouns isn't black, and 0 otherwise.

ccc is the colour of the guardian.

Byte 2: sssxxxxx

NOTE: This byte is taken from the room data,not the guardian data.

For arrows, the top nybble is the y-coordinate of the row (measured bycharacter blocks, not pixels), and the bottom nybble is one-eighth ofthe delay before the arrow appears. For ropes, xxxxx is thex-coordinate of the top pixel, and sss should be000. Otherwise:

sss is the start phase of the guardian. This is ORed with(ppp AND dii) to select the sprite which isdisplayed.

xxxxx is the starting column of the guardian.

Byte 3

For ropes, for some reason this should be #74. For arrows it should be#ff if the arrow moves to the right and #20 if it moves to theleft. Otherwise, it specifies 2 * the starting pixel row of horizontalor vertical guardians, or equivalently 16 * the starting charactercell row.

Byte 4

For horizontal guardians, it gives the speed, which is negative if theguardian initially moves to the left. For vertical guardians it gives2 * the speed, which is negative if the guardian initially movesupwards. For arrows it is ignored; for ropes it gives the length.

Byte 5

For ropes, for some reason this should be #98. For arrows it isignored. Otherwise it specifies the sprite page.

Byte 6

For ropes, for some reason this should be #08. For arrows it specifiesthe bit pattern for the top and bottom of the arrow; the middle uses#7e. Otherwise, it gives the number of ticks before the guardian'spath is reset. This byte is decremented once per tick; when it hitszero, it is loaded again from byte 7.

Byte 7

For ropes, this gives the period of one swing in ticks; note thatshort ropes with short periods won't appear to swing at all. Forarrows it is ignored. Otherwise, it specifies the length of aguardian's path in ticks.


Bytes 6 and 7 have different meanings for horizontal and verticalguardians from Matthew Mode. This change, which follows JSWIIpractice, is necessary because it's otherwise very difficult to workout where to put wraparound diagonal guardians once they've wrappedaround. A side effect of this is that you don't specify a path betweenendpoints as you do in Matthew Mode.

If you're confused...

Do what I do! Set the first four entries of your guardian class tableto this:

bc00  00 00 00 00 00 00 00 00 ........bc08  03 22 09 74 20 98 08 36 .".t ..6bc10  84 00 00 00 ff 00 84 00 ........bc18  04 00 00 00 20 00 21 00 .... .!.

These define, respectively: