-
Posts
5,112 -
Joined
-
Last visited
Everything posted by IRF
-
Looking at the 'Draw the current room to the screen buffer' code, in association with Stuart's POKEs, I believe that the single byte in the existing code at #8D4D is superfluous with Stuart's fix in place, and could be consolidated away. Its original purpose was to pass the value of C on to E, whilst the CPIR loop worked on the BC register pair. Stuart's code (as an alternative to the CPIR loop that yielded the Cell Graphics Bug in the original game) uses the DE register-pair to count (and therefore bypass) the intervening graphic bytes between each attribute byte. It also uses the B register, but not the C register. After an attribute byte match has been found, the program returns back to #8D59, where an identical instruction to that at #8D4D is now located. This is instead of the command in the original code that restores the value of E back to C; the value of C no longer needs to be restored, but the value of E does, and so an LD E, C command is applied - again - and this supercedes the need for the first attempt at passing C's value to E (#8D4D). For reference: http://jswmm.co.uk/topic/93-new-jsw-build-suggestions/?p=798
-
Andy, I believe it would be more appropriate if this topic was located in the 'JSW' rather than 'Remakes' category (along with topics on other Bug Fixes that we've created there), what do you think? Could it be moved? Also, I've just noticed that Richard (SkoolKid) provided an even more efficient way of fixing the bug in his disassembly: http://skoolkid.github.io/jetsetwilly/reference/bugs.html#longDistanceNasties I think if it was inserted via consolidation, then only four bytes would need to be found, which could be done by raiding either the Toilet code or the Flying Pig code - it wouldn't be necessary to do both!
-
JSW disassembly 'Display the title screen...': the side comment at #8820-8827 - it might be worth pointing out that 'DE' isn't defined here, in advance of the LDIR command at #8826 - but that's because it has been left with an 'initial' value of '#5800 (pointing at the attribute file) by the operation of the previous LDIR loop (#8813-881F) that cleared the display file!
-
I should point out that the above patch doesn't resolve the issue which can occur, if you have a guardian that moves between mediums of different Paper colour. If a guardian does that, then the 'Draw the guardians' routine selects the Paper colour of the top-left cell that is occupied by the guardian in a given time-frame, along with its own Ink colour, and uses that attribute for all of the cells that the guardian occupies (i.e. a 2x2 square or a 2x3 rectangle). Because of this, if a guardian moves up/down or left/right past a boundary between two mediums, then the guardian's background can change colour. [Of course, it also changes the Ink colour of any filled-in pixels that fall within its 2x2 or 2x3 'zone of influence'.] To see what I mean, try inserting a Vertical Guardian in The Swimming Pool, such that as it moves it dips in and out of the (Inkless) Water cells. This is in stark contrast to the routine that draws Willy's sprite and colour attributes, in which the 4 (or 6) cells that his sprite occupies are considered in turn - each cell retains its Paper colour (and also its Ink colour unless it is an Air cell). Again, The Swimming Pool provides the best example - as Willy descends down the Ramp into the Water cells, his Ink and Paper colours make a smooth transition.
-
In the Entity Buffer at #8100, the description of Byte 9 of the Rope definition should read: "Index of the segment of rope being drawn (0-32)" As opposed to (0-31) which it currently states. As previously discussed, there are actually 33 segments to the Rope, although only Segments 1-32 are drawn using coordinate offsets drawn from the table at #8300; Segment 0 is drawn at the top with no offsets applied. This may seem 'picky', but the detail is quite crucial to understanding how certain quirky features of the code operate! By the way Richard, can I just reiterate what an invaluable resources your disassemblies have been, as I have endeavoured to get my head round the intricate workings of the JSW/MM game engine!
-
Regarding the above, when Willy jumps off the top of the screen (actually wrapping round to the bottom, as Willy's y-coordinate can't be negative - you can briefly see his head below/within the platform at the bottom of the screen!), it also causes corruption to Room 7: Miner Willy Meets the Kong Beast. See the attached screenshots. This occurs because, in trying to draw Willy at a y-coordinate greater than 224 (#E0), the routine at #927F (that is supposed to draw Willy's sprite to the Occupied Screen Buffer at #6000), over-shoots the bottom of the Screen Buffer Address Lookup Table (#8300-83FF), thus reaching code in the 'The game has just loaded' routine. There it picks up an address (specified at #8404) that corresponds to somewhere in cell-row 6 of page #CC of the code [amongst other addresses, which are mostly in the ROM and are therefore immune from being overwritten, although the stack address at #9CFE - as specified at #8402 - may also be overwritten in the process. I'm not sure whether that would have a detrimental effect on the operation of the program?] The affected addresses are overwritten by some of Willy's sprite's graphic bytes (which are stored in the range #8200-82FF), although the location in Room 7's data to which those graphic bytes are written, means that they are interpreted by the room-drawing code as attribute bytes. More specifically, values of '#3F' and '#F0' are written to #CCCB and #CCCC respectively. The former corresponds to a colour-attribute of 'White Ink on White Paper', hence the solid (non-Bright, non-Flashing) White block. The latter 'F0' corresponds to a colour-attribute of 'Flashing, Bright Black Ink on Yellow Paper'. Because 'F0' also corresponds with a graphics byte specified in the Room 7 data (the top pixel row of the Conveyor tiles), the Cell-Graphics Bug also kicks in, and so the right-hand errant block (at #CCCC) has a non-blank pixel pattern: pixel-rows 0-6 correspond to the 2nd to 8th graphic bytes specified for the Conveyor, whilst the final pixel row of the new block is a graphical representation of the attribute byte for the Nasty 1 tiles (04 - Green Ink on Black Paper). N.B. In terms of Willy's interaction with them, both of the newly-inserted blocks behave as Water cells. See attached rzx recording.
-
I believe that the four bytes at #92B2-92B5 could be freed up, by inserting the Rope Segment Drawing Byte directly into the Rope Definition Bytes at #A00D (and the equivalent location if other Rope Classes are defined), rather than an empty (00) byte being copied across (via the LDIR loop starting at #8922) and then the appropriate byte in the Guardian Buffer being filled by the command at #92B2-92B5. This would also open up the possibility of different Rope Classes having differently defined Rope Segment Drawing Bytes, rather than one generic value. i.e. one Rope Class could start off at the top of the screen with the left-most pixel in its starting column filled in (as is the case for all Ropes currently - the relevant byte is given the value #80), while another Rope Class could have its top segment occupy the right-most pixel. (i.e. By inserting a value of #01 instead of #80 into Byte 5 of the Rope definition.) EDIT: I tried it, and it makes the Rope really 'jerky'. (The top segment, which should be a fixed point, keeps shifting, which also affects the rest of the Rope.). So it doesn't really work, unless you want 'jerky' Ropes. It might be possible, with a rethink, to achieve the 'incidental' result of having the drawing byte definable by Rope Class (instead of being hard-wired into the game engine). However, that probably wouldn't yield a byte saving, which was the original point of the exercise, and it may even require extra bytes. Anyway, you can see the jerky effect in the attached file, in the start-up screen The Beach (or any room with a Rope for that matter, since only one Rope Class is present in the file). Jerky Rope test.z80
-
Something else for the Trivia section (or possibly the Bug section?): The instruction at #8984-8986 has the effect of resetting Willy's 'Rope Status' (to 'Willy is not on a Rope') when he climbs up to the top of a Rope and emerges into the room above. It needs to do this because the Rope-drawing code doesn't deal with this scenario - the 'Draw the Guardians' routine at #91BE only deals with Willy's exit from a Rope when he falls off the bottom of it, whilst when Willy jumps off a Rope, this is dealt with by the code at #8F8F in 'Move Willy (2)'. However, #8984 only does 'half a job', because there is another indicator that the program uses to determine whether Willy is on a Rope - Bit 0 of Byte 11 of a Rope's definition.* That is reset when Willy jumps or falls off a Rope (in the next time-frame, at #9369), but not when he climbs up to the top! (EDIT: Nor is it reset if Willy loses a life whilst he is clinging to a Rope. e.g. if he is struck by an Arrow.) This can on occasion give the program the impression that "Willy is not on a Rope, but he is on this Rope!" - with some rather quirky consequences!! (He said mysteriously...) (*As I previously reported, the 'Entity buffer' section of the JSW disassembly erroneously states that Bit 7 of Byte 11 is used for this purpose.) EDIT: By way of further explanation, Bit 0 of Byte 11 of a Rope's definition is specific to that particular Rope - a kind of 'This Rope Flag' (for want of a better term) - whereas the Rope Status Indicator is more of a generic indicator. Thus the 'This Rope Flag' (or perhaps 'Specific Rope Flag' would be a better name) allows the program to make a distinction as to which Rope Willy is on, in a room with multiple Ropes in a modified game. ADDITIONAL COMMENT: In certain circumstances, the code can be tricked into believing that Willy is on two Ropes at the same time, with yet more quirky effects!
-
In the JSW disassembly 'Move Willy (1)' routine section, the code at 36508 is described as an entry point that is used to update Willy's attribute buffer location when he is on as Rope. Whilst that is true, the same section of code (from 36508 to 36527) performs the same function when Willy is falling (it is 'carried on into' from the code that directly precedes it), and also when Willy is jumping (it is called from 36340, or else from 36550 when Willy's head is being ejected from a wall tile). Perhaps a note could be added to that effect, to clarify things? Also, the routine that starts at #88FC is titled 'Start the Game' in the disassembly, but 99% of the time that it it is called up - via the entry point at #8912 - it does not in fact mark the start of the game, but rather an occasion when Willy enters a new room, or when the room state is reset after Willy has lost a life. So I wonder if there could be a more comprehensive name for that routine?
-
To slow down* the rate at which Willy's spare lives 'dance' in the Status Bar, you can NOP out some or all of the RLCA instructions at #899B-899D in the 'Draw the remaining lives' routine. e.g. if all three RLCA's are NOPped out [replaced with '00'], then the sprites will cycle through all Willy's animation frames once per rendition of the (normal) in-game tune. (*By which I mean slow down the sprites in relation to the speed of the in-game tune. The changes I suggested earlier in this thread slow down both the music and the speed of the 'remaining lives' sprites, in tandem.)
-
Admittedly, I didn't try playing the file in SPIN; we earlier discovered that setting the border colour beyond the usual range of 00-07 meant that sound effects associated with a border colour effect were inaudible (or gave rise to a barely audible 'blip'!) in SPIN...
-
I just tried inserting a value of 'C9' as the Border Colour byte (at Offset #DE) in a test file. It yielded a Blue border, with no apparent adverse effects, so the relevant code must just be selecting the lowest three bits of the hex value C9 (001), when determining what colour to render the border in. Therefore, if you were trying to insert some code into a room's spare definition bytes, but you found you were just short of space [as I did in similar circumstances earlier, when I devised a Patch Vector that just fit into the available space - but then I realised I'd forgotten to account for a terminating Return command!], then you could carry on through the Ramp definition bytes, utilising #DA-DC (though you would have to leave Offset #DD as '00', which is probably interpreted as a NOPped out byte by the program when reading this area as Z80 code, but in parallel it is also interpreted as 'Ramp Length = Zero' by the routine that draws [or rather in this case, doesn't draw] the Ramps). Then whenever the program, whilst implementing the Patch Vector meaning of the code, reached the Border colour byte of 'C9', it would interpret that as a Return command, thereby terminating the routine! The point is that you can pull off a lot of optimisation tricks, as long as you're careful and you know what you're doing!
-
Suggestion for a JSW Trivia entry: The way that the three-component Evil Priest Guardian (The Chapel, Priest's Hole, Entrance to Hades) is compiled is sub-optimal. Three Guardian Classes (Entity Types) are used, whereas it is only necessary to use two - the upper-left and upper-right components could share a Guardian Class, with different Base Sprites selected!
-
You could also fix such a problem at the 'other end of the equation', by inversing both the pixel pattern and the Paper/Ink colours for the Water cells.. You must have also had an 'invalid' value for the Conveyor Direction Byte (at Offset #D6), causing the Water-Conveyor composite cells to turn Sticky! Even more 'fun' could be encountered if you accidentally matched up the Ramp Attribute Byte with the Attribute Byte of one of the other room elements, and you had selected an 'invalid' value for the Ramp Direction Byte (Offset #DA)! Because that could trigger off all manner of 'Invalid Ramp'-related 'special effects', and you know how unpredictable they can be!! :wacko:
-
Excuse my being slow on the uptake, but I've just twigged what the distinction is between the above two examples! The Main-Loop Patch Vector is called from the main loop at #89AD (as it 'says on the tin'!), whereas a Room-Setup Patch Vector would be called up from the routine at #8912 (which initialises upon entry, or resets after a loss of life, the state of the current room). Danny, recalling your early attempts to implement the Screen Flash routine in The Nightmare Room, via a (main-loop) patch vector - they didn't actually flash, because the value of the Screen Flash Counter was being reset to the same chosen value on every pass through the Main Loop. If instead, the Screen Flash Counter had been set to a (non-zero) value via a patch vector that was only called up when Willy first entered The Nightmare Room (i.e. a room-setup patch vector), then the Screen Flash would have descended through the colours in every subsequent game 'tick', until the counter reached zero - which is what we were trying to achieve (and which we later achieved by other methods)!
-
Just to be on the safe side, you should double-check that any Z80 opcode, or operand, or data byte, that has been placed in a slot that is normally reserved for a room cell's attribute byte, does not match any of the attribute bytes for the other cell types for that particular room. i.e. ensure that there is no match between Offsets #A0, #A9, #B2, #BB, #C4 or #CD [unless such a match has been deliberately implemented in a room, in order to create composite cells such as Water-Ramps etc.]
-
One other thought - if a room doesn't contain any collectable items [e.g. the Master Bedroom in most games tends to avoid having items, unless you want to potentially see Maria suddenly vanish if they are the last to be collected!], then the eight bytes that define the item graphic are spare and can be put to other uses. That's Offsets #E1 to #E8.
-
Indeed, you could jump to the remaining chunk of five spare bytes in the guardian data for one of the rooms...
-
Danny, you could go even further with that approach! You could also use the first three of the following four bytes (Offsets #DA-DD - the Ramp definition bytes), as long as the preceding code happened to have a zero at Offset #D9 (corresponding to a Conveyor length of 0). e.g. Consider the sequence of code that draws or writes attributes to an area of the screen, terminating in a LDIR command. If the length parameter of the LDIR loop is less than 256, and the penultimate* instruction is 'LD BC, $0000' which takes the Z80 format '01 XY 00' [length of loop is represented by C=XY; the higher byte B=00]. Thus if the last byte of that command sits at Offset #D9, then the LDIR command (2 bytes) and a terminating 'C9' Return could occupy Offsets #DA-DC. Offset #DD would have to be set to 00 though, to ensure that the Ramp length is zero. (* In some LDIR loops there is also a command such as LD (HL), $00 [Z80 opcode '36'] or LD (HL), A [Z80 opcode '77'] that sets an initial value for e.g. a colour attribute, prior to the loop commencing - but I believe that can be placed before the LD BC command, rather than afterwards, thus facilitating the above suggestion.)
-
I'm not sure this is really a 'novelty' - there must be other examples of JSW games containing eight-frame Vertical Guardians, surely? Andy and I casually inserted one into the (forthcoming) Mini JSW project, without even considering that it might be a novel feature... (I'm referring to the Plant Pot in Top Landing; sprites 'borrowed' from Tech Ted.) I only created a quick example to illustrate the point that the highest bit of the first byte of the guardian definition is brought into play for both horizontal [where it determines the guardian's direction] AND vertical guardians...
-
I think that a kind of 'Butterfly Effect' occurs once the Attic Bug kicks in, with corruption being heaped upon corruption, and code overspill taking place right across the game engine. It seems very unpredictable, and almost impossible to replicate from one occasion to the next. :wacko:
-
I thought I would clarify the likely reasons behind a couple of elements of the JSW code that controls Willy's motion, as I see them: (1) When Willy finishes a jump, the Airborne Status Indicator at #85D1 is changed from 1 (the value that determines that Willy is jumping) to 6 (by the command at #8EB0), rather than a value of 2 which is what it is set to when Willy begins falling after walking off a platform (or dropping off the end of a rope). During a fall, Willy's y-coordinate is increased by four pixels (half a cell-height) in each time-frame, and the Airborne Status Indicator is incremented by 1. So the difference between 2 (at the start of a fall) and 6 (at the end of a jump) represents the two cell-rows of descent that took place during the latter half of the jump. (Actually, Willy's jumps take him a couple of pixels higher than two cell-rows above his starting point in the jump, so it's rounded down slightly.) If it wasn't for this adjustment, i.e. if the Airborne Status Indicator was set to 2 at the end of a jump, then Willy would effectively be able to fall the height of six cells from the apex of his jump, without being killed. (2) The checks at #92C0-92CB determine if and when Willy gets picked up by a Rope - this occurs if the Rope Status Indicator at #85D6 is set to 0 AND if a segment of Rope touches Willy's sprite [or any other filled-in pixel that is drawn before the Rope - which normally means any other room element or guardian, apart from Arrows or collectable Items]. However, when Willy jumps off a Rope (or falls off the bottom of it), the Rope Status Indicator is set to a value of 240 (#F0), or to look at it another way -16 (-#10). This is then incremented by 1 in each subsequent time-frame, until it reaches 256 and wraps round to 00. Thus for the first 16 time-frames after Willy jumps or falls off a Rope, he can't be caught by it again. I believe the rationale behind this is that it gives Willy a chance to get clear of the Rope when he jumps or falls off - otherwise it would be very difficult to gain sufficient clearance when trying to dismount Ropes (an adjacent segment of the Rope could pick him up again in the next time-frame, immediately after he's jumped off). To prove the point, try POKing 36778 (#8FAA) and 37796 (#93A4) to 0 [so that the Rope Status Indicator is set to 0 as soon as Willy jumps or falls off a Rope], and then see how awkward it is to disentangle Willy from a Rope!
-
You could base the item's position on Willy's coordinates in the attribute buffer - it could be set to stubbornly hover above Willy's head, rising up as he tries to jump up to collect it! (Then the way to collect it could be via an Arrow - Willy would have to time his jump precisely, so that the Arrow hits the item but not Willy's head!)
-
Has anyone ever tried to implement moving collectable items in a particular room, by altering the item definition bytes in the Item Table? e.g. if the lowest five bits of the second byte of a particular item's definition was updated, let's say every four time-frames (use the output of applying an AND $FC gate to the 'tick' counter), then the horizontal position of the item on the screen could be adjusted (in whatever way you saw fit) every time Willy's extra lives sprites moved on one 'dance step' (in the example).