Dr. Andrew Broad
Sinclair ZX Spectrum
Manic Miner/Jet Set Willy
The Bugs in JSW


This page explains some of the bugs in the original JSW game-engine, plus some common (and not-so-common) bugs that can arise through careless hacking:

  1. The Bad Pause-Bug Fix (updated 5th March 2006: now in hexadecimal as well as denary)
  2. The Cell-Graphics Bug (updated 5th March 2006: renamed from Block-Graphics Bug)
  3. The Attic-Bug (updated 5th March 2006: a new and precise explanation!)
  4. A Guardian-Class Table Aberration


The Bad Pause-Bug Fix

There is a fix for the so-called Pause-Bug doing the rounds on the Internet, which causes more problems than it fixes! I've seen so many JSW games which crash when you pause them that I decided to create this web page to explain properly the Pause-Bug and its faulty fix.

The original Pause-Bug in JSW is that if you pause it when Interface I is plugged in (on a real Spectrum), it locks up. Now read carefully. There's a standard fix for this Pause-Bug which can cause a worse problem than it solves, and unfortunately it has found its way into some copies of the original JSW doing the rounds on the Internet, with the result that some people have based their games on such copies without even realising that the Bad Pause-Bug Fix is in. It is due to the Bad Pause-Bug Fix, not to the Pause-Bug itself, that some people's JSW games crash when you pause them (even when you're not playing them on a real Spectrum with Interface I plugged in).

The Bad Pause-Bug Fix makes the following changes to the JSW code, so you can see whether it's in your JSW game from the following:

Address        Bytes        Hex        Disassembly
-------        -----        ---        ------------
35591 (#8B07)  195,240,255  C3,F0,FF   JP 65520    (JP #FFF0)

65520 (#FFF0)  197          C5         PUSH BC
65521 (#FFF1)  33,0,154     21,00,9A   LD HL,39424 (LD HL,#9A00)
65524 (#FFF4)  17,0,90      11,00,5A   LD DE,23040 (LD DE,#5A00)
65527 (#FFF7)  1,0,1        01,00,01   LD BC,256   (LD BC,#100)
65530 (#FFFA)  237,176      ED,B0      LDIR
65532 (#FFFC)  193          C1         POP BC
65533 (#FFFD)  195,18,139   C3,12,8B   JP 35602    (JP #8B12)

The problem with the Bad Pause-Bug Fix is that the routine it calls occupies the same address-space as the Guardian-Instance List for Room 63, i.e. 65520-65535 (#FFF0-FFFF). This means that if you edit Room 63, you'll be in trouble:

So the cure to Pause-Bug misery is to remove the Bad Pause-Bug Fix by replacing the jump instruction at 35591-3 (#8B07-9) with the original code that resides there in JSW, namely [33,0,154] ([#21,00,9A]), and then you can trample over the Room 63 Guardian-Instance List with gay abandon! ;-) If you really want to fix the original Pause-Bug (so that you can pause JSW on a real Spectrum with Interface I plugged in), you could consider relocating the pause-routine from 65520-65535 (#FFF0-FFFF) to a `safe' place in memory such as an unused sprite-page, for example.

In summary: if a JSW game crashes when you pause it, then try the following pokes:

POKE 35591,33
POKE 35592,0
POKE 35593,154 (Hex 8B07: 21 00 9A)


There is another, good Pause-Bug Fix for JSW:

POKE 35537,62                  (Hex 8AD1: 3E)
POKE 35539,219: POKE 35540,254 (Hex 8AD3: DB FE)
POKE 35615,62                  (Hex 8B1F: 3E)
POKE 35620,219: POKE 35621,254 (Hex 8B24: DB FE)


The Cell-Graphics Bug

Sometimes the 8x8 graphics you draw for a room's cell-graphics (Air, Water, Earth, Fire, Ramp, Conveyor) do not turn out as you drew them but appear corrupted (they look like another cell-graphic vertically shifted). This is the Cell-Graphics Bug (fka Block-Graphics Bug).

Manic Miner has almost the same bug, so I refer you to the Cell-Graphics Bug section in my Manic Miner Room Format for a precise explanation which will enable you to pinpoint exactly what is corrupting a given cell-graphic.

My Java toolkit SPECSAISIE has functions BGB_MM and BGB_JSW to detect occurrences of the Cell-Graphics Bug in a given MM/JSW game. For example, in the original JSW, the CGB occurs in "West of Kitchen", "The Nightmare Room", "The Wine Cellar" and "Tool Shed" (it is the conveyor-graphic that is corrupted in each of these rooms).


The Attic-Bug

The original JSW had a bug whereby after playing the game for a while, certain rooms would get corrupted - namely, the guardians in "The Chapel" would disappear, and you would lose all your lives as soon as you entered "The Kitchen", "East Wall Base", "We must perform a quirkafleeg", etc. I used to call it the Fault until I discovered on the Internet that it is commonly known as the Attic-Bug.

It turns out that this bug was due to an invalid arrow in "The Attic", and Software Projects issued an official poke (POKE 59901,82) to cure it (one of the four official Software Projects pokes for JSW). For reference, the original, faulty guardian-instance is (69,213) (Guardian-Class 69 is an arrow) and the fixed guardian instance is (69,82) - the former is faulty because the row of the arrow is off the bottom of the screen.

I decided to investigate the effect of the Attic-Bug by comparing snapshots (using the Compare function of SPECSAISIE) of the original JSW, taken before and after the Attic-Bug strikes. This I have done, and this I have found:

java Compare -hbj ORIGINAL.SNA ATTICBUG.SNA #8000 #FFFF
[#85D5]:   1 -> 0	jump-phase counter

[#85DD]:   1 -> 0	backup of jump-phase counter

[#85E2]:   1 -> 3	music on/off flag
---------------------------------------------------------
[#9F6A]:   0 -> #41	unused
[#9F6B]:   0 -> #41	unused
[#9F6C]:   0 -> #41	unused
[#9F6D]:   0 -> #41	unused
[#9F6E]:   0 -> #41	unused
[#9F6F]:   0 -> #41	unused
[#9F70]:   0 -> #41	unused
[#9F71]:   0 -> #41	unused
[#9F72]:   0 -> #41	unused
[#9F73]:   0 -> #41	unused
[#9F74]:   0 -> #41	unused
[#9F75]:   0 -> #41	unused
[#9F76]:   0 -> #41	unused
[#9F77]:   0 -> #41	unused
[#9F78]:   0 -> #41	unused
[#9F79]:   0 -> #41	unused
[#9F7A]:   0 -> #41	unused
[#9F7B]:   0 -> #41	unused
[#9F7C]:   0 -> #41	unused
[#9F7D]:   0 -> #41	unused
[#9F7E]:   0 -> #41	unused
[#9F7F]:   0 -> #41	unused
[#9F80]:   0 -> #41	unused
[#9F81]:   0 -> #41	unused
[#9F82]:   0 -> #41	unused
[#9F83]:   0 -> #41	unused
[#9F84]:   0 -> #41	unused
[#9F85]:   0 -> #41	unused
[#9F86]:   0 -> #41	unused
[#9F87]:   0 -> #41	unused
[#9F88]:   0 -> #41	unused
[#9F89]:   0 -> #41	unused
---------------------------------------------------------
[#A05A]:   0 -> #A	Guardian-Class #B	Offset 2

[#A06A]:   0 -> #FF	Guardian-Class #D	Offset 2
[#A06B]: #40 -> #FF	Guardian-Class #D	Offset 3
[#A06C]:   0 -> #FF	Guardian-Class #D	Offset 4
[#A06D]: #BC -> #FF	Guardian-Class #D	Offset 5
[#A06E]:   0 -> #FF	Guardian-Class #D	Offset 6
[#A06F]:   9 -> #FF	Guardian-Class #D	Offset 7
[#A070]: #12 -> #FF	Guardian-Class #E	Offset 0
[#A071]: #6C -> #FF	Guardian-Class #E	Offset 1
[#A072]:   0 -> #FF	Guardian-Class #E	Offset 2
[#A073]: #30 -> #FF	Guardian-Class #E	Offset 3
[#A074]:  #C -> #FF	Guardian-Class #E	Offset 4
[#A075]: #B9 -> #FF	Guardian-Class #E	Offset 5
[#A076]:   0 -> #FF	Guardian-Class #E	Offset 6
[#A077]: #C0 -> #FF	Guardian-Class #E	Offset 7
[#A078]: #12 -> #FF	Guardian-Class #F	Offset 0
[#A079]: #27 -> #FF	Guardian-Class #F	Offset 1
[#A07A]:   0 -> #FF	Guardian-Class #F	Offset 2
[#A07B]: #10 -> #FF	Guardian-Class #F	Offset 3
[#A07C]:   4 -> #FF	Guardian-Class #F	Offset 4
[#A07D]: #B0 -> #FF	Guardian-Class #F	Offset 5
[#A07E]:   0 -> #FF	Guardian-Class #F	Offset 6
[#A07F]: #20 -> #FF	Guardian-Class #F	Offset 7
[#A080]:   2 -> #FF	Guardian-Class #10	Offset 0
[#A081]:   4 -> #FF	Guardian-Class #10	Offset 1
[#A082]:   0 -> #FF	Guardian-Class #10	Offset 2
[#A083]: #60 -> #FF	Guardian-Class #10	Offset 3
[#A084]:   2 -> #FF	Guardian-Class #10	Offset 4
[#A085]: #BA -> #FF	Guardian-Class #10	Offset 5
[#A086]:   0 -> #FF	Guardian-Class #10	Offset 6
[#A087]: #B0 -> #FF	Guardian-Class #10	Offset 7
[#A088]:   2 -> #FF	Guardian-Class #11	Offset 0
[#A089]:   4 -> #FF	Guardian-Class #11	Offset 1
[#A08A]:   0 -> #A6	Guardian-Class #11	Offset 2

[#A092]:   0 -> #C5	Guardian-Class #12	Offset 2

[#A09A]:   0 -> #6C	Guardian-Class #13	Offset 2

[#A0AA]:   0 -> #18	Guardian-Class #15	Offset 2

[#A0D2]:   0 -> 8	Guardian-Class #1A	Offset 2

[#A0DA]:   0 -> #92	Guardian-Class #1B	Offset 2

[#A10A]:   0 -> #18	Guardian-Class #21	Offset 2

[#A122]:   0 -> #10	Guardian-Class #24	Offset 2

[#A12A]:   0 -> #16	Guardian-Class #25	Offset 2

[#A132]:   0 -> 4	Guardian-Class #26	Offset 2

[#A142]:   0 -> 7	Guardian-Class #28	Offset 2

[#A14A]:   0 -> #2B	Guardian-Class #29	Offset 2

[#A152]:   0 -> 5	Guardian-Class #2A	Offset 2

[#A16A]:   0 -> #41	Guardian-Class #2D	Offset 2
[#A16B]: #B0 -> #41	Guardian-Class #2D	Offset 3
[#A16C]:   8 -> #41	Guardian-Class #2D	Offset 4
[#A16D]: #BA -> #41	Guardian-Class #2D	Offset 5
[#A16E]:   0 -> #41	Guardian-Class #2D	Offset 6
[#A16F]: #D0 -> #41	Guardian-Class #2D	Offset 7
[#A170]:   1 -> #41	Guardian-Class #2E	Offset 0
[#A171]: #66 -> #41	Guardian-Class #2E	Offset 1
[#A172]:   0 -> #41	Guardian-Class #2E	Offset 2
[#A173]: #A0 -> #41	Guardian-Class #2E	Offset 3
[#A174]:   0 -> #41	Guardian-Class #2E	Offset 4
[#A175]: #BB -> #41	Guardian-Class #2E	Offset 5
[#A176]:   0 -> #41	Guardian-Class #2E	Offset 6
[#A177]: #13 -> #41	Guardian-Class #2E	Offset 7
[#A178]:   1 -> #41	Guardian-Class #2F	Offset 0
[#A179]: #6E -> #41	Guardian-Class #2F	Offset 1
[#A17A]:   0 -> #41	Guardian-Class #2F	Offset 2
[#A17B]: #30 -> #41	Guardian-Class #2F	Offset 3
[#A17C]:   0 -> #41	Guardian-Class #2F	Offset 4
[#A17D]: #BB -> #41	Guardian-Class #2F	Offset 5
[#A17E]: #11 -> #41	Guardian-Class #2F	Offset 6
[#A17F]: #1E -> #41	Guardian-Class #2F	Offset 7
[#A180]:   2 -> #41	Guardian-Class #30	Offset 0
[#A181]: #2B -> #41	Guardian-Class #30	Offset 1
[#A182]:   0 -> #41	Guardian-Class #30	Offset 2
[#A183]: #40 -> #41	Guardian-Class #30	Offset 3
[#A184]:   6 -> #41	Guardian-Class #30	Offset 4
[#A185]: #B7 -> #41	Guardian-Class #30	Offset 5
[#A186]: #10 -> #41	Guardian-Class #30	Offset 6
[#A187]: #D0 -> #41	Guardian-Class #30	Offset 7
[#A188]: #12 -> #41	Guardian-Class #31	Offset 0
[#A189]: #26 -> #41	Guardian-Class #31	Offset 1

[#A1D2]:   0 -> #86	Guardian-Class #3A	Offset 2

[#A1E2]:   0 -> #92	Guardian-Class #3C	Offset 2

[#A21A]:   0 -> #19	Guardian-Class #43	Offset 2

[#A22A]:   0 -> #D5	Guardian-Class #45	Offset 2

[#A232]:   0 -> #94	Guardian-Class #46	Offset 2

[#A23A]:   0 -> #97	Guardian-Class #47	Offset 2

[#A242]:   0 -> #9C	Guardian-Class #48	Offset 2

[#A24A]:   0 -> #D1	Guardian-Class #49	Offset 2

[#A292]:   0 -> 4	Guardian-Class #52	Offset 2

[#A29A]:   0 -> 6	Guardian-Class #53	Offset 2

[#A2A2]:   0 -> 8	Guardian-Class #54	Offset 2

[#A2AA]:   0 -> #A	Guardian-Class #55	Offset 2

[#A2B2]:   0 -> #C	Guardian-Class #56	Offset 2

[#A2BA]:   0 -> #4E	Guardian-Class #57	Offset 2

[#A2D2]:   0 -> 4	Guardian-Class #5A	Offset 2

[#A2DA]:   0 -> #10	Guardian-Class #5B	Offset 2

[#A2E2]:   1 -> #96	Guardian-Class #5C	Offset 2

[#A2EA]:   0 -> 4	Guardian-Class #5D	Offset 2

[#A2F2]:   0 -> #96	Guardian-Class #5E	Offset 2

[#A2FA]:   0 -> #B	Guardian-Class #5F	Offset 2

[#A302]:   0 -> 3	Guardian-Class #60	Offset 2
---------------------------------------------------------

The above output shows that the Attic-Bug has trampled over memory (red marks erroneous modifications) - including parts of the Guardian-Class Table (#A000-A3FF), so some guardian-classes have been corrupted but not others (note that it is normal behaviour for Offset 2 of guardian-classes to be modified during play).

Note the address-ranges that have been erroneously modified: #9F6A-9F89, #A06A-A089 and #A16A-A189 - three blocks of 32 bytes each: one block for each pixel-row of the arrow, and a byte for each cell-column!


A Guardian-Class Table Aberration

Finally, I want to share with you a bizarre phenomenon I encountered while writing my JSW game Goodnite Luddite, which I have now fixed.

In the original Jet Set Willy, We Pretty and Jet Set Willy: The Lord of the Rings, each room's Guardian-Instance List is padded out with [255,0,0,0,...] (in some JSW games, the Guardian-Instance Lists are padded out with [255,0,255,0,...]), but in Goodnite Luddite, this used to cause instances of Guardian-Class 0 to appear after [255,0] which is supposed to terminate the sequence! It's not because GC 0 is defined, because GC 0 is also defined in We Pretty and JSW:LOTR, and they don't have this problem.

It is entirely to do with the contents of the Guardian-Class Table (40960-41983), because I tried the experiment of poking (255,0) into offsets 240 and 241 (i.e. the first guardian-instance) of each room. This removed all guardian-instances from the original JSW and We Pretty (because (255,0) terminates the Guardian-Instance List), but only the first guardian-instance from each room in Goodnite Luddite! (i.e. the first guardian-instance is overwritten with (255,0), but this does not terminate the list).

I tried pasting the Goodnite Luddite Guardian-Class Table into We Pretty and trying the experiment, and this also removed only the first guardian-instance from each room. Therefore, whether or not (255,0) terminates the Guardian-Instance Lists only depends on the Guardian-Class Table - it's not due to any corruption of the game engine.

A hypothesis occurred to me that it might be to do with Guardian-Class 127. I remember once trying to use Guardian-Class 127, and every room that had fewer than eight guardian-instances had an instance of GC 127, because the [255,0] used to terminate the Guardian-Instance List was being interpreted as an instance of GC 127 (i.e. the lower 7 bits of 255). So I tried pasting just Guardian-Classes 0-126 from Goodnite Luddite into We Pretty and repeating the experiment of poking [255,0] into offsets 240 and 241 of each room. Sure enough, there weren't erroneous instances of GC 0 in each room! This showed that the definition of GC 127 was to blame.

So I compared the definition of GC 127 in We Pretty and Goodnite Luddite (i.e. 41976-49183). In We Pretty it was [255,0,0,0,0,0,0,n] (where n is 256 - the number of items, which is held at 41983), whereas in Goodnite Luddite it was [0,0,0,0,0,0,0,n]. So I tried POKE 41976,255 and it cured the problem. What had happened is that I had cleared the whole Guardian-Class Table soon after I started Goodnite Luddite (i.e. FOR n = 40960 TO 41982: POKE n,0: NEXT n), and knocking the 255 out of GC 127 caused [255,0] not to terminate the Guardian-Instance Lists! So the lesson is that while it's okay to use Guardian-Classes 0-126, you should leave GC 127 well alone!


Email me 1