Technical notes on JSW64 Manic Miner: James Bond [v1.0] ================================================ Contents -------- * Notation * Reset cells' FLASH-bits (#8A0B-8A1A) * Make the portal turn red instead of FLASH (#85BA-85BD, #8A1B-8A23) * Making the screen red instead of blue after entering the portal (#871E-8720, #970F-9714) * Disabling the room-exits (#89D0-94F8) * Player's sprite-page (#9658-9659, #96F4-9701) * The Kong Beast (#9907-99EB) * 24-hour clock (#8A72-8A93) * A longer Game Over message (#8A94-8AA4, #8CCB-8CE0) * Arrow-patch for "Goldfinger" (#929E-92A0, #9702-970E) * Patch-vector to flip Willy's horizontal position (1:#E100-E10F) * Patch-vector to clear individual pixel-rows on the screen (1:#E900-E929) * Solar power in "Die Another Day" * Endgame-sequence (#9826-9855) Free space ---------- - #8A23-8A24 (2 bytes) have been freed up by the "Reset cells' FLASH-bits" patch; - #8AA5-8AAA (6 bytes) have been freed up by the "24-hour clock" patch; - #948A-94F8 (#6F bytes) have been freed up by "Disabling the room-exits"; - #9715-9717 (3 bytes) are free; - 0:#D700-D73F and #E100-E3FF are reserved for sprites, but currently unused; - 0:#E400-EAFF (#700 bytes) are free, and I would prefer to keep them free for future sprites; - 0:#ED00-F6FF (#A00 bytes) are free. -------- Notation -------- % prefixes a binary number (e.g. %00011111); # prefixes a hexadecimal number (or range, e.g. #8000-FFFF); unprefixed numbers may be assumed to be denary, except in contexts where only hexadecimal numbers are expected (see below). A 128K-memory address is of the form :, e.g. "1:#C000" means offset #C000 in Bank 1. If : is omitted, it's just a standard 48K-memory address. A line of either of the forms...
: +
: + | ; ...means that one or more bytes with the specified HEXADECIMAL values occur from the given address onwards, and if these constitute a machine-code instruction, I give the corresponding assembly-code and a comment to explain what it does. __ denotes a byte which is to be left unchanged when applying the patch in question. Colour-attributes are denoted as quadruples of the form (INK,PAPER,BRIGHT,FLASH), e.g. (2,0,1,0) means INK 2, PAPER 0, BRIGHT 1, FLASH 0. ----------------------- Reset cells' FLASH-bits (#8A0B-8A1A) ----------------------- If the game-engine is patched to set all cells to FLASH 0 just before they are drawn, then two cells can have the same colour-attribute - one with FLASH 0, the other FLASH 1. So I wrote a patch to reset the FLASH-bit of every cell in the secondary attributes-buffer just before the secondary attributes-buffer is written to video-RAM. From http://groups.yahoo.com/group/manicminerandjetsetwilly/message/5757 >>> In an unpatched JSW game-engine, the code at #8A0B-8A25 is used to make the screen colour-cycle according to the byte at #85CD (34253), as Manic Miner does when you get an extra life. But this code is unused in the unpatched JSW game-engine (since the byte at #85CD is never set to anything but 0), so if we're prepared to sacrifice the possibility of using 'extra-life' colour-cycling in another patch, then this is a good place to insert code which recolours the screen. The patch to reset FLASH: #8A0B: 21 00 5C | LD HL,#5C00 ; secondary attributes-buffer #8A0E: 01 00 02 | LD BC,#200 ; going to process 512 bytes (i.e. 32*16 cells) #8A11: CB BE | RES 7,(HL) ; set cell to FLASH 0 (Bit 7 of colour-attribute) #8A13: 23 | INC HL ; next cell #8A14: 0B | DEC BC ; decrement counter #8A15: 78 | LD A,B ; going to check if B and C are both 0... #8A16: B1 | OR C ; ...by bitwise-ORing them together #8A17: 20 F8 | JR NZ,#8A11 ; if BC <> 0 then loop back to process next cell #8A19: 18 0B | JR #8A26 ; skip dead code <<< This frees up #8A1B-8A25 (11 bytes). ----------------------------------------- Make the portal turn red instead of FLASH (#85BA-85BD, #8A1B-8A23) ----------------------------------------- JSW64 uses the following code to make the portal FLASH when all the items in a room are collected, and to test for a FLASHing portal: #85B3: 3A 53 85 | LD A,(#8553) ; counter of items remaining in current room #85B6: B7 | OR A ; test for 0 #85B7: 21 F4 80 | LD HL,#80F4 ; address of portal's colour-attribute #85BA: 20 02 | JR NZ,#85BE ; unless there are items remaining #85BC: CB FE | SET 7,(HL) ; set Bit 7 (FLASH) of portal's colour-attribute #85BE: CB 7E | BIT 7,(HL) ; test Bit 7 of portal's colour-attribute #85C0: 2A F0 80 | LD HL,(#80F0) ; portal's attribute-position #85C3: CA EE 86 | JP Z,#86EE ; if portal is not FLASHing, skip code that tests for player being in it Making the portal FLASH will have no visible effect under the patch to reset cells' FLASH-bits, so instead, JSW64MM:JB changes the paper-colour from white (colour-attribute %01111000) to red (%01010000). Unfortunately this requires more than one instruction to replace SET 7,(HL), so I've created a subroutine to set various bits of the portal's colour-attribute, and put it in the space freed up by the patch to reset cells' FLASH-bits (#8A1B-8A25): #8A1B: CB FE | SET 7,(HL) ; turn FLASH on so as not to affect the code to detect a FLASHing portal #8A1D: CB AE | RES 5,(HL) #8A1F: CB E6 | SET 4,(HL) #8A21: CB 9E | RES 3,(HL) ; set PAPER to %010 (red) #8A23: C9 | RET (#8A23-8A24 are now free.) And in the original code, we need to replace the instructions at #85BA-85BD with a call to this subroutine: #85BA: CC 1A 8A | CALL Z,#8A1A ; unless there are items remaining #85BD: 00 | NOP ; fill the gap by doing nothing --------------------------------------------------------------- Making the screen red instead of blue after entering the portal (#871E-8720, #970F-9714) --------------------------------------------------------------- JSW64 uses the following code to make the screen go blue after entering the portal: #8707: 3E 3F | LD A,#3F ; colour-attribute (7,7,0,0) #8709: 21 00 58 | LD HL,#5800 ; colour-attributes in video-RAM #870C: 11 01 58 | LD DE,#5801 #870F: 01 FF 01 | LD BC,#1FF ; 256 colour-attributes to write #8712: 77 | LD (HL),A ; write colour-attribute #8713: ED B0 | LDIR ; repeat for each colour-attribute #8715: 01 04 00 | LD BC,4 ; } #8718: 10 FE | DJNZ #8718 ; } a little delay #871A: 0D | DEC C ; } #871B: 20 FB | JR NZ,#8718 ; } #871D: 3D | DEC A ; decrement colour-attribute #871E: 20 E9 | JR NZ,#8709 ; repeat until colour-attribute is 0 #8720: C9 | RET So the last attribute it sets the screen to is 1 (1,0,0,0). I want it to stop at 2 instead. This requires extending the above code with: #871E: FE 01 | CP 1 #8720: 20 E7 | JR NZ,#8709 ; repeat until colour-attribute is 1 But of course there isn't room to insert the CP #1 instruction here, so we have to jump to a big enough gap: #871E: C3 0F 97 | JP #970F #970F: FE 01 | CP 1 #9711: C2 09 87 | JP NZ,#8709 #9714: C9 | RET ------------------------ Disabling the room-exits (#89D0-94F8) ------------------------ Jet Set Willy uses four routines to enter the room to the left, right, above or below, located at #948A, #949E, #94B0 and #94D2 respectively. The solution adopted is to NOP out the jumps to these routines so that going off the edges of the screen behaves as it does in Manic Miner. This avoids infinite-death scenarios that could result from reentering a room such as "You Only Live Twice" [4], and also avoids problems such as the player being able to reregister his/her position, to retain any items collected in the room before exiting via the portal, and to reset Crumbly-cells and switches. Original JSW (walking up on a ramp): #89D1: 3A CF 85 | LD A,(#85CF) ; player's row in pixels*2 #89D4: FE E1 | CP #E1 ; on-screen range is 0 to #E0 #89D6: D4 B0 94 | CALL NC,#94B0 ; if row >= #E1 then enter room above The patch: #89D0: __ 00 00 00 00 00 00 00 #89D8: 00 __ __ __ __ __ __ __ Original JSW (jumping up, or going up on a rope): #8DEF: FE F0 | CP #F0 ; A contains player's row in pixels*2 #8DF1: D2 B0 94 | JP NC,#94B0 ; if row >= #F0 then enter room above The patch: #8DE8: __ __ __ __ __ __ __ 00 #8DF0: 00 00 00 00 __ __ __ __ Original JSW (going down): #8E44: CB 4C | BIT 1,H ; HL contains player's attribute-position + 64 #8E46: C2 D2 94 | JP NZ,#94D2 ; if feet are below cell-row 15 then enter room below The patch: #8E40: __ __ __ __ 00 00 00 00 #8E48: 00 __ __ __ __ __ __ __ Original JSW (going left): #900D: 7D | LD A,L ; HL contains player's attribute-position #900E: E6 1F | AND #1F ; the column is in Bits [4:0] #9010: CA 8A 94 | JP Z,#948A ; if 0 then enter room to the left The patch: #9008: __ __ __ __ __ 00 00 00 #9010: 00 00 00 __ __ __ __ __ Original JSW (going right): #907E: 7D | LD A,L ; HL contains player's attribute-position + 2 #907F: E6 1F | AND #1F ; (column + 2) % 32 is in Bits [4:0] #9081: CA 9E 94 | JP Z,#949E ; if column = 30 then enter room to the right The patch: #9078: __ __ __ __ __ __ 00 00 #9080: 00 00 00 00 __ __ __ __ #948A-94F8 (#6F bytes) are now free. -------------------- Player's sprite-page (#9658-9659, #96F4-9701) -------------------- JSW64 uses the following code to override the player's sprite-page (#9D by default) with that specified by Room-offset #ED (if >= #80): #9650: 5F | LD E,A ; low byte of sprite-address #9651: 16 9D | LD D,#9D ; sprite-page = #9D by default #9653: 3A ED 80 | LD A,(#80ED) ; A:= Room-offset #ED from current-room buffer #9656: FE 08 | CP #80 ; if A < #80 #9658: 38 06 | JR C,#9660 ; then skip the following code #965A: 57 | LD D,A ; override sprite-page #965B: B6 | OR (HL) ; #965C: 7B | LD A,E ;} #965D: EE 80 | XOR #80 ;} toggle Bit 7 of sprite-address #965F: 5F | LD E,A ;} Like the original 48K JSW, this has the flaw that #9D00-9D7F are interpreted as the player's right-facing sprites when #9D is used as the player's sprite-page, and as left-facing sprites (the usual convention in JSW) when #9D is used as a horizontal guardian's sprite-page. So if we wish to use sprite-page #9D as both a player-sprite and a horizontal-guardian-sprite, we must /always/ toggle Bit 7 of the sprite-address, which is achieved by changing one instruction: #9658: 38 02 | JR C,#965C Remaining lives --------------- Recall from TECHNICA.TXT of _Jet Set Willy: The Lord of the Rings_ that I wrote a subroutine to select the sprites for the remaining lives. In JSW64MM:JB, this is expanded to use #D9 as the default sprite-page if Offset #ED is less than #80, and this subroutine is located at #96F4-9701: #96F4: EE 80 | XOR #80 ; select other half of sprite-page #96F6: 5F | LD E,A #96F7: 3A ED 80 | LD A,(#80ED) ; get Offset #ED for current room #96FA: FE 80 | CP #80 ; } #96FC: 30 02 | JR NC,#9700 ; } if less than #80 #96FE: 3E 9D | LD A,#9D ; then use standard player sprite-page #9700: 57 | LD D,A #9701: C9 | RET Recall that the call to this subroutine replaces instructions LD E,A and LD D,#9D (at #89A0-89A2 in JSW48). In JSW64, these are located at 0:#EB0F-EB11, so that's where the subroutine-call goes: 0:#EB0F: CD F4 96 | CALL #96F4 -------------- The Kong Beast (#9907-99EB) -------------- Rooms with the Kong Beast in a JSW64:MM game have a room-setup patch-vector of #9800 and a main-loop patch-vector of #9803. [4] "You Only Live Twice" 1:#D0FB: 00 98 03 98 [11] "For Your Eyes Only" 1:#ECFB: 00 98 03 98 The patch-vectors are just jumps to John Elliott's Kong-Beast routines: #9800: JP #9880 ; room-setup patch-vector (#9880-9886) #9803: JP #9887 ; main-loop patch-vector (#9887-9933) So any changes to the Kong-Beast routines will affect every Kong Beast in the game: they all share the same sprite, colour-attributes and coordinates. The colour-attributes of the standing Kong Beast are set by the following instructions: #992A: 21 47 47 | LD HL,#4747 ; bright white ink on black paper #992D: F5 2F 5C | LD (#5C2F),HL ; write to (1,15) and (1,16) in colour-attribute buffer #9930: F5 0F 5C | LD (#5C0F),HL ; write to (0,15) and (0,16) in colour-attribute buffer So the byte at #992B is the colour-attribute of the Kong Beast's left cell-column, and the byte at #992C is the colour-attribute of the Kong Beast's right cell-column. Note that the above instructions set the coordinates of the colour-attributes as addresses in the colour-attribute buffer (#5C00 + #20*y + x), but you'd also have to set the pixel-coordinates to agree with them (for 0 <= y <= 7: #6000 + #20*y + x) by editing the following instruction: #991F: 21 0F 60 | LD HL,#600F And the following instruction sets the Kong-Beast's sprite-page (edit the bytes at #991E and #99EB): #991D: 16 D7 | LD D,#D7 ; sprite-page #D7 for standing Kong Beast (#D7C0-D7FF) #99EA: 16 D7 | LD D,#D7 ; sprite-page #D7 for falling Kong Beast (#D780-D7BF) The colour-attribute of the falling Kong Beast is set by the following instruction (edit the byte at #9908): #9907: 3E 06 | LD A,6 ; yellow ink on black paper ------------- 24-hour clock (#8A72-8A93) ------------- I have applied my 24-hour-clock patch exactly as described in TECHNICA.TXT of Goodnite Luddite: overwriting the code at #8A72-8A93 (which frees up #8A94-8AAA), and moving the clock one cell to the right (#8A36: 7A). The start-time is set as a string "00:00 " in #8585-858A, and the current time in #857F-8584: #857F: 30 30 3A 30 30 20 #8585: 30 30 3A 30 30 20 -------------------------- A longer Game Over message (#8A94-8AA4, #8CCB-8CE2) -------------------------- I want the Game Over screen to say "Goodbye, Mr. Bond!". The original code to say "Game Over" is as follows: #8CCB: DD 21 74 85 | LD IX,#8574 ; start of "Game" string #8CCF: 0E 04 | LD C,4 ; 4 characters to print #8CD1: 11 CA 40 | LD DE,#40CA ; at (6,10) #8CD4: CD 80 96 | CALL #9680 ; print them #8CD7: DD 21 78 75 | LD IX,#8578 ; start of "Over" string #8CDB: 0E 04 | LD C,4 #8CDD: 11 D2 40 | LD DE,#40D2 ; at (6,18) #8CE0: CD 80 96 | CALL #9680 Obviously, there isn't room for my message at #8574-857B, so I've put it at #8A94-8AA4: #8A94: 47 6F 6F 64 62 79 65 2C #8A9C: 4D 72 2E 20 42 6F 6E 64 21 And modified the above code as follows: #8CCB: DD 21 94 8A | LD IX,#8A94 ; start of "Goodbye," string #8CCF: 0E 08 | LD C,8 ; 8 characters to print #8CD1: 11 C6 40 | LD DE,#40C6 ; at (6,6) #8CD7: DD 21 9C 8A | LD IX,#8A9C ; start of "Mr. Bond!" string #8CDB: 0E 09 | LD C,9 This does have the shortcoming that only "bye," and "Mr. " are colour-cycled, but I couldn't be bothered to extend the colour-cycling code (#8CE8-8D2F). ---------------------------- Arrow-patch for "Goldfinger" (#929E-92A0, #9702-970E) ---------------------------- "Goldfinger" [2] represents Oddjob throwing his bowler-hat by an arrow whose bottom pixel-row is wider than its top and middle pixel-rows: ..####.. (#3C) ..####.. (#3C) ######## (#FF) While JSW64 allows the middle pixel-row to be specified by Guardian-byte 3, it still uses Guardian-byte 6 for both the top row and the bottom row, as per the following code. JSW64 uses the following little subroutine to write the pixel-row in Guardian-byte 6 to the address in HL: #95D9: DD 7E 06 | LD A,(IX+6) #95DC: 77 | LD (HL),A #95DD: C9 | RET And it is called twice by the following code: {PRE: HL contains the address to which to write the top row of the arrow; (IX+5) is #FF if it's in a white-ink colour-attribute, otherwise 0} #928E: CD D9 95 | CALL #95D9 ; draw top pixel-row of arrow #9291: 24 | INC H ; HL now points to where to draw the middle pixel-row #9292: 7E | LD A,(HL) ; get pixel-row that's already there #9293: DD A6 05 | AND (IX+5) ; if it contains any white-ink pixels #9296: C2 B7 90 | JP NZ,#90B7 ; then you lose a life #9299: DD 7E 03 | LD A,(IX+3) ; } #929C: 77 | LD (HL),A ; } draw middle pixel-row of arrow #929D: 24 | INC H ; HL now points to where to draw the bottom pixel-row #929E: CD D9 95 | CALL #95D9 ; draw bottom pixel-row of arrow I can think of three solutions for "Goldfinger": (a) A room-setup patch-vector to modify the above code to swap the middle and bottom pixel-rows. This has the drawback that every room other than "Goldfinger" would need a room-setup patch-vector to undo this change. (b) A main-loop patch-vector to draw the bottom pixel-row. This has the drawback that it would have to duplicate the complexity of the existing arrow-handling code - not to mention being so inefficient that it could well slow the game down in "Goldfinger". (c) Replace the second call to #95D9 (to draw the bottom pixel-row) with a subroutine that uses #FF when in "Goldfinger", otherwise Guardian-byte 6 as usual: #929E: CD 02 97 | CALL #9702 #9702: 3A 20 84 | LD A,(#8420) ; current room-number #9705: FE 02 | CP 2 ; are we in "Goldfinger" [2]? #9707: C2 D9 95 | JP NZ,#95D9 ; if not then use Guardian-byte 6 #970A: 3E FF | LD A,#FF ; if so then use #FF #970C: C3 DC 95 | JP #95DC ; go and draw it ------------------------------------------------ Patch-vector to flip Willy's horizontal position (1:#E100-E10F) ------------------------------------------------ First seen in "Willy's Drinking Licence" in Geoff Eddy's _Willy Takes a Trip_, this patch-vector flips Willy's horizontal position every 32 time-frames when he's in "The Man with the Golden Gun" [8] - in "Willy's Drinking Licence" it was every 64 time-frames. The other difference is that I subtract 1 so that the flipping is perfectly symmetrical for the player's width (2 cell-columns, so the left cell-column x should map to 30-x rather than 31-x). Base-address of "The Man with the Golden Gun" [8] = 1:(#C000 + #400*8) = 1:#E000 Variant Z has free space in Room-offsets #100-1FF: 1:#E100: 3A CB 85 | LD A,(#85CB) ; get current time-frame 1:#E103: E6 1F | AND %00011111 ; will be 0 once every 32 time-frames 1:#E105: C0 | RET NZ ; otherwise don't do it 1:#E106: 3A D3 85 | LD A, (#85D3) ; get Willy's current cell-column [Bits 4:0] 1:#E109: EE 1F | XOR %00011111 ; toggle Bits 4:0, leaving 7:5 unchanged 1:#E10B: 3D | DEC A ; subtract 1 1:#E10C: 32 D3 85 | LD (#85D3), A ; set Willy's cell-column 1:#E10F: C9 | RET This code gets copied to #8100-810F when the room is loaded into the current-room buffer, therefore we set the room's main-loop patch-vector to #8100: 1:#E0FD: 00 81 --------------------------------------------------------- Patch-vector to clear individual pixel-rows on the screen (1:#E900-E929) --------------------------------------------------------- Thanks to John Elliott fixing a bug in the JSW64:Z engine, this patch-vector is no longer needed, but this section may be a useful reference for drawing ad-hoc pixels on the screen via the primary pixel-buffer (#7000-7FFF). "Moonraker" [10] uses global cell-class 13 for Earth which is meant to be blank, but due to an bug in the JSW64:Z engine (which has now been fixed), it had the pixel-pattern of cell-class 1 (Water). This was a problem because guardians that are meant to pass harmlessly through these Earth-cells would collide with them. My solution was a main-loop patch-vector that blanked out individual pixel-rows that would cause a collision. At the time that the JSW64 engine calls the main-loop patch-vector, the pixels to be written to the top two thirds of the screen (#4000-4FFF) are in the primary pixel-buffer (#7000-7FFF), so that's where the patch-vector needed to alter them. Following the format of the Spectrum's video-RAM, a byte to be written z pixel-rows {0 <= z <= 7} below character-position (y,x) is at the following address in the primary pixel-buffer: {0 <= y <= 7}: #7000 + #100*z + #20*y + x {8 <= y <= 15}: #7800 + #100*z + #20*(y-8) + x Base-address of "Moonraker" [10] = 1:(#C000 + #400*#A) = 1:#E800 Variant Z has free space in Room-offsets #100-1FF (although "Moonraker" uses an extended guardian-list which has been relocated to 1:#E97F-E9FF): 1:#E900: 3E 00 | LD A,0 ; value to 'POKE'... 1:#E902: 32 4B 76 | LD (#764B),A ; ...to #764B (6 pixels below 2,11) 1:#E905: 32 4E 76 | LD (#764E),A ; ...to #764E (6 pixels below 2,14) 1:#E908: 32 4F 76 | LD (#764F),A ; ...to #764F (6 pixels below 2,15) 1:#E90B: 32 50 76 | LD (#7650),A ; ...to #7650 (6 pixels below 2,16) 1:#E90E: 32 51 76 | LD (#7651),A ; ...to #7651 (6 pixels below 2,17) 1:#E911: 32 89 70 | LD (#7089),A ; ...to #7089 (0 pixels below 4,9) 1:#E914: 32 89 71 | LD (#7189),A ; ...to #7189 (1 pixels below 4,9) 1:#E917: 32 89 72 | LD (#7289),A ; ...to #7289 (2 pixels below 4,9) 1:#E91A: 32 89 73 | LD (#7389),A ; ...to #7389 (3 pixels below 4,9) 1:#E91D: 32 89 74 | LD (#7489),A ; ...to #7489 (4 pixels below 4,9) 1:#E920: 32 89 75 | LD (#7589),A ; ...to #7589 (5 pixels below 4,9) 1:#E923: 32 89 76 | LD (#7689),A ; ...to #7689 (6 pixels below 4,9) 1:#E926: 32 89 77 | LD (#7789),A ; ...to #7789 (7 pixels below 4,9) 1:#E929: C9 | RET This code gets copied to #8100-810F when the room is loaded into the current-room buffer, therefore the room's main-loop patch-vector was set to #8100: 1:#E8FD: 00 81 But now that the bug in the JSW64:Z engine has been fixed, the main-loop patch-vector has been reverted to point to a RET instruction at #869F: 1:#E8FD: 9F86 (The code at #E900-E929 is still present, but is free to overwrite.) -------------------------------- Solar power in "Die Another Day" -------------------------------- In "Die Another Day" [19], it was very tricky setting the cell-classes' colour-attributes so that the solar beam would behave correctly - so much so that it's worth documenting here. The behaviour of the solar beam in JSW64 is as follows: 1. If it goes through a white-INK colour-attribute whose PAPER, BRIGHT and FLASH are the same as Cell-class 0 Air, it saps your oxygen-supply. 2. It passes through any Air-cells without being deflected. 3. It is absorbed by any Water-cells or Earth-cells (but not custom cells, which behave as Water). 4. Any other colour-attribute deflects it. In "Die Another Day", Cell-class 0 Air has the colour-attribute (5,1,1,0) - the cyan ink is for the ghosts of Crumbly cells. This means that the solar beam saps your oxygen-supply if it goes through any colour-attribute (7,1,1,0). I gave Crumbly cells a colour-attribute of (7,1,1,0), but had to change it to (1,7,1,0) to stop them sapping the oxygen-supply! To maintain visual attractiveness, I would like to have inverted the pixel-pattern... ######## ........ ######## ........ ######## ........ ######## ........ ...but this meant that it crumbled after one time-frame rather than two, so I had to leave it as above. I swapped Crumbly's colour-attribute with that of Cell-class 1 Water, but having a colour-attribute of (7,1,1,0) for Water meant that the player absorbed the solar beam as per Rule 3, without sapping the oxygen-supply. So I changed Cell-class 1 Water's colour-attribute to (7,0,1,0) - which does not occur anywhere in the room - leaving the (7,1,1,0) cells undefined, so that they behave as Water and have Cell-class 1's pixel-pattern, but without the player absorbing the solar beam! ---------------- Endgame-sequence (#9826-9855) ---------------- John Elliott's JSW64:MM - upon which this game is based - uses a portal patch-vector at #9806 to implement the swordfish-routine. Because the last room of this game is "Casino Royale" [20], its portal patch-vector is set as follows: 3:#D0F9: 06 98 The patch-vector is just a jump to John Elliott's swordfish-routine (#9818-987F): #9800: JP #9880 The swordfish-routine from the original JSW64:MM is: #9818: 3A E3 85 | LD A,(#85E3) ; WRITETYPER-flag #981B: B7 | OR A ; is 0? #981C: 20 51 | JR NZ,#986F ; if not then skip swordfish-effect #981E: 3A 00 C4 | LD A,(#C400) #9821: B7 | OR A ; PEEK #C400 is 0? #9822: 20 4B | JR NZ,#986F ; if not then skip swordfish-effect #9824: 0E 00 | LD C,0 ; no collision-detection #9826: 11 80 A3 | LD DE,#A380 ; sprite-address #A380 #9829: 21 53 40 | LD HL,#4053 ; to be drawn at cell-coordinates (2,19) in video-RAM #982C: CD 56 94 | CALL #9456 ; draw sprite #982F: 11 80 A2 | LD DE,#A280 ; sprite-address #A280 (swordfish) #9832: 21 B3 40 | LD HL,#40B3 ; to be drawn at cell-coordinates (5,19) in video-RAM #9835: CD 56 94 | CALL #9456 ; draw sprite #9838: 21 2F 2F | LD HL,#2F2F ; colour-attribute #2F (7,5,0,0) #983B: 22 53 58 | LD (#5853),HL ; write it to (2,19) and (2,20) #983E: 21 27 27 | LD HL,#2727 ; colour-attribute #27 (7,4,0,0) #9841: 22 73 58 | LD (#5873),HL ; write it to (3,19) and (3,20) #9844: 21 45 45 | LD HL,#4545 ; colour-attribute #45 (5,0,1,0) #9847: 22 B3 58 | LD (#58B3),HL ; write it to (5,19) and (5,20) #984A: 21 46 47 | LD HL,#4746 ; colour-attributes #47 (7,0,1,0) and #46 (6,0,1,0) #984D: 22 D3 58 | LD (#58D3),HL ; write them to (6,19) and (6,20) #9850: 21 00 00 | LD HL,0 ; colour-attribute 0 (to make player's legs invisible) #9853: 22 F3 58 | LD (#58F3),HL ; write it to (7,19) and (7,20) #986F: 26 A4 | LD H,#A4 ; item-table is in page #A4 #9871: 3A CA 85 | LD A,(#85CA) ; (256 - number of items in the game) #9874: 6F | LD L,A ; HL now holds address of last item #9875: 32 DE 85 | LD (#85DE),A ; reset number of items to collect #9878: CB F6 | SET 6,(HL) ; mark each item as uncollected #987A: 2C | INC L ; previous item #987B: 20 FB | JR NZ,#9878 ; loop back to process the entire item-table #987D: C3 A0 86 | JP #86A0 ; default portal patch-vector The first change that this game makes to the above routine is not to draw the player above the portal or anywhere else, therefore #9826-982E and #9838-9843 are NOPped out (since I couldn't be bothered to defragment the code): #9820: __ __ __ __ __ __ 00 00 #9828: 00 00 00 00 00 00 00 #9838: 00 00 00 00 00 00 00 00 #9840: 00 00 00 00 Instead of the swordfish, this game draws the sprite at #9C60 at (1,13)... #982F: 11 60 9C | LD DE,#9C60 ; sprite-address #A280 (swordfish) #9832: 21 2D 40 | LD HL,#402D ; to be drawn at cell-coordinates (1,13) in video-RAM ...with a colour-attribute of (2,0,1,0): #9844: 21 42 42 | LD HL,#4545 ; colour-attribute #42 (2,0,1,0) #9847: 22 2D 58 | LD (#582D),HL ; write it to (1,13) and (1,14) #984A: 22 4D 58 | LD (#584D),HL ; write them to (2,13) and (2,14) Finally, instead of writing colour-attribute 0 to the two cells below the portal, it writes colour-attribute (4,4,0,0) to the two cells to the left of the portal: #984D: 3E 24 | LD A,#24 ; colour-attribute #24 (to make player's legs invisible) #984F: 32 2C 58 | LD (#582C),A ; write it to (1,12) #9852: 32 4C 58 | LD (#584C),A ; write it to (2,12) #9855: NOP N.B. The instruction formerly at #984A has been deleted because it was redundant, and to make space for the extra instruction at #9852, whilst sacrificing the ability to have different colour-attributes for the two rows.