Jet-Set Willy II Room Format

Version 0.4: 17 September 1998

by John Elliott

Disclaimer: I do not guarantee this information to be correct. This is my best guess at what is going on inside JSW II, after wandering around inside it with a disassembler.

New for v0.04: More corrections from Andrew Cadley.

New for v0.03: Andrew Cadley has supplied details of the T4, CG0 and CG1 bytes.

1. Terminology

The room layouts in Jet Set Willy I were composed of four elements:

Air
Willy can walk or jump through it, but not stand on it.
Water
As Air, but Willy can also stand on it.
Earth
Willy can stand on it, but not walk or jump through it.
Fire
Willy loses a life if he touches this.

I have retained this system in describing JSW2, although ramps, conveyors and objects are also "elements" now.

2.Snapshot encryption

All information has been deduced from a snapshot of JSW2 taken while the title screen was showing and before a game had been started. JSW2 is encrypted, so .TAP files and snapshots taken before the title screen appeared will not match this pattern.

3. Room entries

At 0BAFDh is a table of 16-bit pointers to room entries:

	DW	ROOM0
	DW	ROOM1
	...
   

Each room entry is formed:

+0	DW	RDATA	;Address of room shape (Room shapes: 
						Section 4)
+2	DB	HBITS	;High bits
		;These are combined with the next 8 bytes to form 9-bit
		;values. Bit 7 of this byte gives Bit 8 of U0;
		;	 Bit 6 of this byte gives Bit 8 of U1;
		;        ...
		;        Bit 0 of this byte gives Bit 8 of U7.
+3	DB	U0	;UDG number for Water (UDGs: Section 5)
+4	DB	U1	;UDG number for Earth
+5	DB	U2	;UDG number for Fire
+6	DB	U3	;UDG number for "/" ramps
+7	DB	U4	;UDG number for conveyor going left
+8	DB	U5	;UDG number for object
+9	DB	U6	;UDG number for "\" ramp
+A	DB	U7	;UDG numer conveyor going right
+B	DB	xname	;Number of spaces to print before the room name 
			;(to position it centrally). This also contains
			;the border colour in bits 2-0.
+C	DB	name	;Variable-length ASCII string, with bit 7 set on
			;the last character. Bytes 1-31 are expanded to
			;common words using a dictionary at 0FA81h, in the
			;same format as the keyword table in the Spectrum
			;ROM.
	DB	left	;Exit left  }
	DB	up	;Exit up    } Room nos.
	DB	right	;Exit right }
	DB	down	;Exit down  }
	DB	T4	;Bit    7: Set if there is a rope in the room
			;Bit    6: Set to animate conveyor belt. Conveyors
			;         should not be animated if there are two or
			;         more conveyors in a room.
			;Bit    5: If conveyor belts are animated:
			;           0: Only the top row of the conveyor
			;              animates.
			;           1: The top and third rows animate. 
                        ;Bit    4: If set of T4 is set, byte T5 is present; 
			;          otherwise byte T5 is assumed to be 0.
			;Bits 0-3: Number of guardian records
			;          that follow.

(	DB	T5	  ;Bits 0-5: Special-case code ID.
			) ;If bit 7 of this byte is set, arrows are present.

	DS	7	;Guardian record 0
	DS	7	;Guardian record 1
	...
	DS	7	;Guardian record {n}

(	DB	AC	;(if arrows present) No. of arrow records that follow 
	DS	2	;Arrow record 1
	DS	2	;Arrow record 2
	...
	DS	2	) ;Arrow record {n}

;
;End of room
;

4. Room shape

The room shape is stored as a stream of bytes, each describing one or more character cells. The decompression code continues expanding until 512 cells been described. Cells are decompressed as horizontal sequences, starting at the top left corner.

Each byte is either:

Special arrangements exist for Room 108, the Cartography Room. Water cells in this room map to rooms in other parts of the game; a table at 0FBE8h gives the mapping (it starts 74h, 75h, 76h; so the first water cell depends on room 74h, the second on room 75h, and the third on room 76h).

5. UDGs

UDGs have a 9-bit number (see above). To find the address of a UDG, multiply this number by 9 and add 8C78h. The UDG is then formed: one attribute byte, and 8 bitmap bytes.

6. Guardians

A compressed guardian record is 7 bytes (CG0-CG6):

  CG0:	} Limits of movement. When you first enter a room a counter is 
  CG1:  } initialized with the value of CG0, this is decremented on each 
          pass through the game loop. Each time the counter reaches 0 it 
          is reloaded from CG1, the guardian's direction is reversed, and 
          the process begins again.
  CG2:  Low 8 bits of sprite number
  CG3:  Movement step (8-bit signed integer). Doubling the movement step 
        makes a sprite go twice as far as well as twice as 
        fast, while reversing its direction swaps the limits of 
        travel over. On horizontal sprites, this also affects the 
        animation step.
  CG4:  Bit    7 is bit 8 of sprite number.  
	Bits 6-0 are initial X coordinate for horizontal guardians;
                             X coordinate for vertical guardians.
  CG5:  Bit    7 is ?
        Bits 6-0 are         Y coordinate for horizontal guardians;
                     initial Y coordinate for vertical guardians.
  CG6:  Bits 1-0 is animation style. This is:
	         0: None
                 1: Frames 1,2     }
                 2: Frames 1,3     } (taking account of reversal, see bit 6)
                 3: Frames 1,2,3,4 }
	Bits 3-2 give colour. This is 
                 0: White
                 1: Yellow
                 2: Cyan
                 3: Blue
        Bits 4-5 give diagonal movement data. See bit 7 below.
        Bit 6    is set to swap between frames 0/1/2/3 and 4/5/6/7 when the
                 guardian reverses.
	Bit 7    is set to move horizontally, clear to move vertically.
		 Combining it with bits 4 and 5 we have:
                 
                 Bit 7  Bit 5  Bit 4
                 ==================================================
                     0      0      0     Vertical
                     0      0      1     45 degrees from horizontal
                     0      1      0     22 degrees from horizontal
                     1      1      1     18 degrees from horizontal
                     1      0      0     Horizontal
                     1      0      1     18 degrees from horizontal
                     1      1      0     22 degrees from horizontal
                     1      1      1     45 degrees from horizontal

                 The setting of bit 7 changes whether the limits affect
                 the X coordinate or the Y coordinate.

  

A compressed arrow record is 2 bytes:

  	DB	AB0	;Arrow X position
	DB	AB1	;Bit    7 set if going left, else right.
			;Bits 6-0 are Y position
  
JSW II decompresses these records to 17 bytes (UG00-UG10). Here is how it does it:

  Byte  Meaning  How set
  ====================================================================
  UG00  Arrow    Address of arrow sprite - EB01h or EB21h. 
  UG01  address           if (AB1 & 80h) then EB01h
  UG02  limit 1  = CG0
  UG03  limit 2  = CG1
  UG04  Y step   If arrow, set to 0
                 If bit 7 of CG6 is 0:   = CG3 (movement step)
                 If bit 7 of CG6 is 1:  = Bits 4 and 5 of CG6 (diag'l step)
  UG05  X        If arrow, AB0
                 else      CG4, bits 6-0 (0-7Fh)
  UG06  X step   If arrow and bit 7 of AB1 is 1: 0FFh
                 If arrow and bit 7 of AB1 is 0: 0
                 If bit 7 of CG6 is 0:  = Bits 4 and 5 of CG6 (diag'l step)
                 If bit 7 of CG6 is 1:  = CG3 (movement step)
  UG07  Y        If arrow: (AB1 & 7Fh)
		 else       CG5, bits 6-0 (0-7Fh)
  UG08  Styles   If arrow: 88h
                 else (CG6 & 0F3h)
  UG09  Sprite   Address of sprite page. Given by ((CG2 | 2*(CG4 & 80h)) *16)
  UG0A  address                                   + 0D4A1h
  UG0B  ?        Not set
  UG0C  ?        Not set
  UG0D  ?        Not set
  UG0E  ?        If arrow: 0
                 If bit 7 of CG5 is 1:  = 0FFh.
                 If bit 7 of CG5 is 0:  = 0.
  UG0F  ?        Not set
  UG10  Animat'n If arrow: 87h
                 If bit 7 of CG6 is 0:  loaded from 70A9 + (CG6 & 3)
                                        This array is {87h, C6h, C5h, C4h}
		 If bit 7 of CG6 is 1:  80h
  

7. Special-case code.

JSW2 has a table of special-case code at 8361h, which has two words for each ID. It starts at ID 1:

	dw	sc1a	;For ID=1
	dw	sc1b
	dw	sc2a	;For ID=2
	dw	sc2b
	...		;etc.
	

The two routines appear to be an initialisation function and a function called once every time the game loop is run.

A routine address can be 0 if only one of the two routines is being supplied.

This code handles things like the Trip Switch, the Rocket Room, the monks in Rigor Mortis, and what happens when you complete the game.

Lifts are also implemented in special-case code. There is a table of lift definitions (in compressed guardian format) at 0FB30h. Lifts go around in pairs, like policemen; so the first two lifts will be for one room, the next two for the next room, and so on.

8. Teleporters

There are four teleporters in the game; you will find their definitions at 7435h. A teleporter definition is 6 bytes long:

	DB	from		;Room to teleport from
	DB	x		;X location within the room
	DB	y		;Y location within the room
	DB	to		;Room to teleport to, plus 1
	DB	x		;New X location 
	DB	y		;New Y location
  
_____________________________________________________________________

John Elliott
17-8-1998

Thanks also to: Andrew Cadley.