Jump to content
Jet Set Willy & Manic Miner Community

IRF

Contributor
  • Posts

    5,111
  • Joined

  • Last visited

Everything posted by IRF

  1. More pedantry from me! In the entry for Vertical Guardians in the 'Entity Buffer' section: http://skoolkid.github.io/jetsetwilly/asm/8100.html Bit 7 of Byte 0 of the vertical guardian definition is described as 'unused'. I don't believe that is technically the case; it just so happens that in Original JSW it is always reset to 0 at the start of the game. I believe that the value of Bit 7 of Byte 0 (for a vertical guardian' buffer definition entry) actually cycles between values of 1 and 0, every four time-frames [or every eight time-frames, if Bit 4 of Byte 0 is reset], as a result of the regular implementation of the AND A, $20 command at #918E. However, the superimposing of the Animation Frame Mask and Base Sprite Index, by the instructions at #9211-9219, selectively filters the animation frames for all the vertical guardians in Original JSW, in such a way that only four frames of animation are ever displayed. So the changes to the value of Bit 7 of Byte 0 are only occurring 'behind the scenes', and don't affect what is displayed on the screen. But it is quite possible to have vertical guardians with eight frames of animation, by selecting appropriate values for the Animation Frame Mask (4-7) and Base Sprite Index (0-3), in which case the toggling of Bit 7 of Byte 0 is manifested in the change of guardian sprite that you would see on the screen. N.B. This doesn't work in the same way as for horizontal guardians, for which there are (potentially) four frames when the guardian is moving left, and four different ones when it is moving right. The 'Move a vertical guardian' code works differently to the 'Move a horizontal guardian' code. For an eight-frame vertical guardian, all eight frames are toggled through in turn (Frame 0 through to Frame 7, then back to Frame 0), regardless of whether the guardian is moving up or down! EDIT: This could form the basis of fixing the 'not very graceful' vertical guardian in 'On a Branch over the Drive', as follows: http://skoolkid.github.io/jetsetwilly/reference/facts.html#quadridirectionalGuardian (In preparation for the below, shift the 'Flying Saucer' sprites from #B980-B9FF to a spare place in the sprite data, such as #A680-A6FF, and redirect Guardian Class 0E to the appropriate place by changing the value of #A075 from $B9 to $A6.) - Place a laterally inverted version of the sprite currently occupying #B900-B91F, into the addresses at #B980-B99F, by swapping pairs of graphic bytes (e.g. the first two entries for the original sprite are $3C $00, so insert $00 $3C for the first two graphic bytes of the laterally inverted sprite); - Copy the sprite at #B960-B97F into #B9A0-B9BF; - Copy the sprite at #B940-B95F into #B9C0-B9DF; - Copy the sprite at #B920-B93F into #B9E0-B9FF; - Change the Animation Frame Mask for Guardian 3E from 03 to 07 (by changing the value of A1F1 from $65 to $E5). I haven't tried it yet, but hopefully the resultant guardian will be much more 'graceful'!
  2. I thought it could be done with four bytes, so in the file attached above I shifted the four bytes that reset the 'tick' counter when Willy reaches the toilet at the end of the game. Unfortunately (and rather annoyingly!) that didn't work, so I had to consolidate one more byte, which I acquired from the 'Draw Willy to the screen buffer' routine: There are four bytes which ensure that 'Flying Pig Willy' moves forwards in The Nightmare Room (they are NOPped out in JSW The Nightmare Edition, so that Flying Pig Willy moves backwards); in the attached file I used one of those (and NOPped out the other three). So I had to apply consolidation in two directions to achieve the fix (from 'above' and from 'below'), and then ensure that all the relative and absolute jumps were in order. But I got there in the end!
  3. A few other tips about inserting Guardian Class 127: - Since JSWED doesn't recognise G127, it has to be created manually using the hex editor, populating the eight bytes #A3F8-A3FF in the Entity Definitions section. Thus it is most convenient to copy the eight bytes of an existing Arrow definition across to those addresses, and then create the new Guardian Class in the space where the Arrow definition used to sit (e.g. Class 45 in original JSW). That way you can use JSWED's Graphical User Interface to create the new guardian in the normal way. - Each instance of the newly-defined G127 Arrow has to be manually inserted into the Guardian List for each room where it appears, by altering the first specification byte from '45' [or whatever] to '7F'. If you leave the second specification byte unchanged, then the y-coordinate of the G127 Arrows will be the same as before (including Invalid values if required). - Arrows usually appear last in a Guardian List, which is another reason to use Class 127 for Arrows - to minimise the need to manually rearrange the other guardians in the list. (i.e. if you created a Vertical G127 Guardian in a room with an Arrow, you would have to place it before the Arrow and manually insert the Arrow afterwards in the hex code, to prevent a potential fatal collision). - Finally, an 'FF' Terminator byte has to be manually added at the end of any Guardian List that has had a G127 inserted (unless the list is filled with the maximum of eight 'entities'). JSWED normally does this automatically to terminate the list, but it won't in this case - possibly because it thinks that '7F' will do the job of terminating the list (which, to be fair, it used to do prior to the application of the code changes set out in Post #20 of this thread). - Additional point: Make sure you have the Adjacent Ropes Patch (New) in place, so that any data left 'lying around' in the Entity Buffer after the FF Terminator byte, can't trigger the Rope-Teleport Bug!
  4. Although it's one that it might be best not to fix, as it's a useful feature for reducing Infinite Death Scenario falls off the bottom of screens! It took a fair bit of 'unavoidable drudgery' shifting code about to achieve it, too!
  5. See the attached file - I have added five bytes to the 'Set Willy's attributes' routine, immediately after the ADD HL, DE command which - prior to the code changes - used to be at #9614. The five bytes in question are: 7C - LD A, H FE 5E - CP #5E 30 07 - JR NC 07 The effect of this intervention is to disable the 'bug/quirky feature' that causes Willy to die if he tries to fall off the bottom of a screen which has a Fire cell located above him at the top of the screen. The starting point in the file is set to A Bit of Tree, and I have inserted a Fire cell nearby to demonstrate that falling onto a Fire cell within a screen still kills Willy as per usual. However, if you jump Willy up to the screen above, Under the Roof, and then walk rightwards off the platform that he lands on, you will see that he falls off the bottom of Under the Roof without being killed by the Fire cells at the top of that screen due to the 'bug' - only to land (repeatedly!) onto the newly-inserted Fire cell in A Bit of Tree! under roof fall test.z80
  6. The same 'Through the Wall' phenomenon also allows Willy to fall through any standonable block, including Crumbly cells. This fact highlights an asymmetry in the Manic Miner code. If Willy jumps rightwards over a Fire cell that is adjacent to a Crumbly cell beyond it, then he falls through the Crumbly cell without causing it to crumble. Whereas if Willy jumps leftwards over a Fire cell that is adjacent to a Crumbly cell beyond it, then he falls through the Crumbly cell, but one pixel row of that Crumbly cell crumbles away. The difference arises because in the code at #8B10-8B58, the checks for (in turn) Crumbly cells and Fire cells under the left of Willy's sprite take place before the checks for (in turn) Crumbly cells and Fire cells under the right of Willy's sprite. See attached rzx recording. Fire Crumble Jump.rzx
  7. I think I have an answer to the conundrum posed by this speculative comment in the 'Through the Wall' entry of the Bugs section of the Manic Miner disassembly: Perhaps the assumption in this code is that if there is a nasty tile below Willy's sprite in step 16, then he will be killed by the nasty before reaching step 17; however, in this particular case Willy's x-coordinate decreases by one during step 16 (as he transitions from animation frame 0 to frame 3), which moves him out of the nasty's range. I think the reason why a positive response to the check for Fire cells under Willy, causes the jumps to #8BDD [in MM] or #8ED4 [in JSW] to be bypassed, is to stop Willy from bouncing sideways off Fire cells as he crosses a cell boundary. If this wasn't the case - i.e. if the relative jumps at #8B29 and #8B3D (in Manic Miner) or #8E4D and #8E54 (in JSW) weren't present in the code - then Willy would be able to perform the quirky manoeuvres seen in the attached rzx recordings! (which are based on modified files with the above bytes NOPped out). (Note that in the Manic Miner recording, Willy actually jumps briefly off the top of the screen!) MM Fire Bounce.rzx JSW Fire Bounce 1.rzx JSW Fire Bounce 2.rzx
  8. Further to my earlier post, I now can report that I have safely inserted a Guardian of Class 127 into a test file! Everything is working fine, including the Item Drawing and Item Collection loops (which no longer call up the value of #A3FF). However, JSWED doesn't display the first 25 items in the Item Table for some reason* - although they are present and collectable in the game. This occurred in a game with 256 items, so it may not affect games to the same extent where there are fewer items present. In any case, I recommend that you only make the enabling code changes to insert a Guardian Class 127, after you've got to the point where you're happy with the item distribution in a game. (Although subsequent changes to item distribution can of course be made via the hex editor.) * EDIT: I've just realised that it's happening because the last byte of the Guardian Class 127's definition just so happens to be #19 (25 in decimal) - JSWED is still picking up the value of #A3FF in order to display the items! As I said, it doesn't affect the game itself, but to prevent this glitch in JSWED, you could simply ensure that one of your Arrow Guardian Classes occupies slot 127 (#A3F8-A3FF), since the eighth byte of an Arrow's definition is unused. So you could insert the value [256 minus the number of items in the game] at #A3FF, just for the purpose of viewing all the items in JSWED, and it wouldn't affect the Arrows at all. (The eighth byte of an Arrow's definition is normally 0 by default anyway, which suits a game with 256 items, but it doesn't have to be.) Actually, that would negate the need to make the changes to the code at #8802 and #93D3 (although those changes can in themselves provide useful byte-efficiencies at pinch-points in the code). UPDATE: It is far safer to have an Arrow occupy Class 127, as otherwise if you alter the number of items in JSWED, the editor changes the value of the eighth byte of Class 127's definition - the maximum extent of the range of a horizontal/vertical guardian or rope - which could cause the guardian to collide with room elements! Arrows are absolutely fine as Class 127 though! So if you shift one of your existing arrow definitions to #A3F8-A3FF (quite easy to do as half of the bytes are 00), then you'll free up a slot that's easily editable in JSWED for your new guardian class! And then you can safely make changes to the number of items in JSWED; the redundant eighth byte of the Class 127 Arrow definition will be updated, with no impact whatsoever on the function of the Arrow!
  9. Oh, and one more point, returning to Danny's original enquiry: since only #A3FA is overwritten during the course of the game (for the reason described above), the other five unused guardian definition bytes (#A3F9 and #A3FB-E) should be perfectly safe to re-use.
  10. Regarding this post: http://jswmm.co.uk/topic/185-free-space-and-code-optimisation-in-jsw/page-1?do=findComment&comment=3701 I think I've got to the bottom of it! Contrary to the impression you might have got from my previous post, the third byte of each Entity Definition in the #A000-A3FF range is overwritten during the course of the game! (Whilst the third bytes of each entity definition are initialised to zero, and listed as such in the disassembly, you may find that the values are non-zero in snapshot files.) However, this doesn't normally matter, as the values of those bytes are 'instance-specific', and so are regularly updated using the second byte of the appropriate entry in the Entity Specification for each room, each time a guardian is inserted into a room. Now, whenever the routine at #8912 comes across a 'Terminator' byte in a room's Guardian List data, it treats it as a 'Class 127 [#7F]' guardian (because Bit 7 of the first byte of the entity specification is reset at #892E), and so the subsequent value (i.e. the 'Guardian Specification' byte immediately following the 'FF 'Terminator', which would usually be 00 by default - but Danny, if in your WNM-SE file you had 'recycled' some of the spare bytes in unfilled rooms' Guardian List data, for other purposes, then it could be anything and could vary as Willy moves from room to room!) is copied into the address #A3FA (which is the third byte of Guardian Class 127's eight Definition Bytes!) - mystery solved! The way to prevent this would be to tweak the routine at #8912. Immediately after the 'Pick up the first byte of the entity specification' LD L, (IX+$00), remove the RES 7, L and replace it with a check 'Is it #FF?': EX AF, AF'; LD A, L; CP $FF. [if not, skip the next three bytes.] If so, copy 'FF' to the address that is currently pointed at by the 'DE' register pair (i.e. the first byte of the Entity Buffer entry that is currently under consideration) and then jump forward to #894A, bypassing the loops which populate the rest of the entity buffer: JR NZ $03; LD (DE), A; JR $1B; EX AF, AF'. [The purpose of the two bookending EX AF, AF' commands is to retain, in A', the value of the A register which is counting down, from a starting value of 8, the number of entities being considered. Once the loop is exited early by the presence of an 'FF' Terminator byte, the value of A' no longer needs to be retained.] If the guardian specification byte under consideration isn't an 'FF' Terminator, then the existing code is resumed at #8930 (after passing through the second EX AF, AF' command to restore the 'entity counter' to A). N.B. the operand of the relative jump at #8948 would need to be changed accordingly, to ensure that it still loops back to #892B. [in Z80 code: remove 'CB BD' from #892E; insert '08 7D FE FF 20 03 12 18 1B 08'; replace '20 E1' previously at #8948 with '20 D9'.] The above would probably require some consolidation, although a relative jump could be squeezed into the space currently occupied by the RES 7, L command, which might help if the other few bytes could be located within 128 bytes of the source address. In which case, the operands of the new relative jumps above would need to be altered, and a new jump back to #8930 inserted at the end. Alternatively, the nearby efficiencies that we have already identified should just about be enough to allow the additional 8 bytes of code to be inserted in situ i.e. by setting the border colour more efficiently at #8980; swapping the absolute jump at #8988 for a relative jump; removing the 'DI' command at #8898; and swapping the absolute jumps at #8892 and #88AE for relative jumps. [With the above in place, when a 'Terminator' byte is encountered by the program whilst it is filling the entity buffer, the rest of the buffer may retain the data from the previous room, rather than being filled with '00's. However, that won't matter because the 'Move the guardians' and 'Draw the guardians' routines will in any case skip all subsequent addresses in the entity buffer as soon as they encounter a 'FF' value. EDIT: Although a Rope might pick up un-erased values in the second and fourth bytes of the next guardian in the list, potentially triggering the Rope-Teleport Bug, so you should ensure that John Elliott's Adjacent Ropes Patch (New) is in place - but then, I would strongly recommend that you have that in place in any game file in any case!] In theory, the suggested changes to the code would allow Guardian Class 127 to be safely defined, provided that #A3FF's use as an 'Item Index' is reallocated to another address*. It would be entered into a room's guardian list as Class #7F, and (with the RES 7, L command expunged) it would be treated differently to an 'FF' Terminator byte. (* Or the LD A, ($A3FF) commands at #8802 and #93D3 could be replaced with LD A, $00 [the operand being the previous value of #A3FF] - saving two bytes - or in a game with 256 items, simply replaced with XOR A instructions - saving four bytes.) {One other caveat: you would have to make sure that all the 'even' Entity Specification Bytes in every room were within the range 00-7F [apart from the FF Terminator, of course]; otherwise, higher values (with Bit 7 set) would cause the program to try and interpret data in the range #A400-A7FF [item Table data, Toilet graphic bytes, etc] as guardian definition bytes - which could be 'fun'!! There's no reason why that would happen, though, unless you deliberately - manually in the hex editor - enter a Guardian Class 128-254 into a room's Guardian List!}
  11. Don't worry, I just wondered if you could recall off the top of your head. My theory as to what was going on was probably wrong anyway, as it would have involved #A3FB being overwritten (I think!?) EDIT: Actually, I'm not even sure about that now! I was thinking in terms of a rope's data 'overspilling' its eight bytes, but that should only really affect the bytes just after the 'Entity Buffer' at #8100, not the Entity Definitions that are copied into the buffer upon entry to each room. FURTHER THOUGHT: Andrew Broad reported the 'Entity Specification' area of the Room Buffer being affected if Guardian Class 127 was defined (filling rooms that didn't already have 8 guardians with either Class 0 or Class 127 guardians), but again that involves corruption of areas of buffer code, not the 'source data' at #A0xx.
  12. I reckon the third page from the bottom (the one that starts with The Bathroom) is what Willy looks like if you ever see him head on (which we never do!)
  13. Actually, I think that the table is looked up on the 33rd pass through the rope-drawing loop, but the program doesn't actually implement anything with those values, other than 'behind the scenes' updating of a couple of variables, which aren't then used for anything (because immediately afterwards the relative jump at #9356 is triggered, before another pixel is drawn), and the two variables are re-initialised when the rope is redrawn in the next time-frame anyway. So it's probably safe enough to describe the bytes at #8356-83D6 as 'spare bytes'! EDIT: Playing Devil's Advocate, I think the worst that might happen, if the spare bytes were reused for other purposes, is that: If a high value (such as #FF) were to occupy #8356, then the DJNZ loop at #933D / #934E might take a while to bring the B register back down to zero, and that might cause a brief (~1 millisecond?) delay in the game, at the point when the rope reaches its point of maximum deviation from the vertical? Meanwhile, if an odd value were to be inserted at #83D6, then this could, theoretically, cause a similar effect to the Arrow that triggers the Attic Bug, with the 'Draw the Rope' routine overshooting the 'Screen Buffer Address Lookup Table', causing an address beyond the Screen Buffer to be overwritten. However, this would only have an impact on the game if the routine subsequently attempted to insert the Rope Segment Graphic Byte at such a misplaced address. And that won't happen, again because the relative jump at #9353 is triggered straight away, before the routine gets a chance to draw any more Rope segments! [Caveat: if a modified game had a Rope longer than 32 segments, without the maximum swing being reduced accordingly, then the above is something to be mindful of!!]
  14. More suggestions:- 'Display the Title Screen and play the theme tune' http://skoolkid.github.io/jetsetwilly/asm/87CA.html I think it's worth highlighting, in the adjacent sidebar, the role of the AND 01 command at #8872 - it selects which of each pair of Triangle UDGs to draw, depending on whether the current value of the attribute file address being considered is odd or even. Thus, two adjacent attribute bytes of the same value will select graphic bytes starting at #8431 and #8439 respectively, or else #8441 and #8449. Also, the jump at #8864 is quite cunning, as it ensures that all four Triangle UDGs can be drawn in 'Cyan Ink on Green Paper' attributes: - attribute bytes set to 'Green Ink on Cyan Paper' bypass the instruction at #886A, before their attributes are swapped round at #886E-8870; - in contrast, when 'Cyan Ink on Green Paper' attribute bytes are considered, the instruction at #886A is executed but #886E-8870 is bypassed. 'The game has just loaded' http://skoolkid.github.io/jetsetwilly/asm/8400.html The entry at #840C to 8414 - it would be helpful if it said "Set HL=8500 in a roundabout way, in preparation for the [redundant] instructions at #8415 to #841E". i.e. you have already highlighted the redundant nature of the latter chunk of code, but not the former.
  15. The way the rope-drawing loop works is that it draws a segment (initially the 0th segment), then increments the coordinates of the segment (using the table at #8300) in preparation for the next segment being drawn during the next pass through the rope-drawing loop. And at the end of the loop, the check "Have we drawn every segment of the rope yet?" is made before the Segment Counter is incremented. Therefore 33 segments (pixels) are drawn in total, for each value of the Rope Animation Frame Index, although the Rope Animation Table is only looked up 32 times in the process.
  16. Yes, I've thought about it, and in terms of how the algorithm works that points to the correct place in the table, you're right. My initial reading of your explanatory introduction was that the entries at #8355 and #83D5 were unused - but that is because I was thinking of the entries at #8300 and #8380 as being the first entries in their respective half of the table, whereas they are in fact the zeroeth entries. I think the wording in brackets gave rise to my confusion: "(one for each segment of rope)". Perhaps it could be amended to clarify the matter? Something along the lines of "(one for each segment of rope, apart from the 0th segment which is drawn at the top of the screen in the cell-column determined by the second byte* of the entity specification, and which does not have any 'offset' applied to its coordinates)" [* By which I mean Byte 1 of the entity specification for the room in which the rope is being drawn, given that the first byte which specifies that this is a rope is Byte 0!]
  17. Okay, perhaps it should read "From F+1 to F+32", as the table isn't used to calculate an offset when L=0.
  18. 32 offsets during each animation frame. #8300/8380 are used. #8356/83D6 are not. Think of it like this: there are 32 'lengths' of rope, and 33 points separating them (32 dots on the screen). The first 'length' spans between Point 0 (top of screen) and Point 1. The 32nd and final 'length' spans between Point 32 and 33.
  19. Segment 0 is drawn at the top of the screen, in the cell-column determined by the second byte of the Rope specification for the screen. Segment L has horizontal and vertical offsets applied according to F+L. F ranges from 0 to 54. L ranges from 1 to 32. So F+L ranges from 1 to 86. And there are 86 usable entries in each half of the table!
  20. One more thing on the above - Stuart Brady deserves the credit in the Changelog for spotting the phenomenon in the first paragraph, not me. Although I do get the credit for the point in the second paragraph, so if you decide it's worthy of mention in the disassembly, then 'Very Corrupted Conveyor' could have a joint credit! Oh, and one more point of trivia: with the bytes all in the right place, the Cell Graphics Bug doesn't kick in in the first place in The Nightmare Room, because the correct attribute byte for the conveyor doesn't match any of the screen's preceding graphics bytes!
  21. No, they are used. Segment Zero doesn't have an offset applied to it - it is at the top of the screen, in the same horizontal position regardless of the value of the Rope Animation Frame. Bytes #8300 and #8380 are picked up to determine where to draw Segment 1 when the Rope Animation Frame is at 0. It was initially my understanding that the last of the non-zero bytes in each half of the table were unused - until I realised that the Segment Counter increments to 32, not 31!
  22. JSW disassembly Rope animation table #8300: "For a given rope animation frame F (0<=F<=54), the 32 entries from F to F+31 inclusive (one for each segment of rope) in each half of the table are used; thus the batch of entries used 'slides' up and down the table as F increases and decreases." That should read "from F to F+32" - the first segment of the rope is Segment 0, so there are 33 in total, not 32.
  23. In Move Willy (1) of the JSW disassembly, at #8E39 the check determined by an AND #0E command is described as "Does Willy's sprite occupy six cells at the moment?" Now, when Willy is on a ramp (not cell-aligned), you would think that yes, his sprite does occupy six cells. But the check still gives a negative response, and the program proceeds through to #8E3D instead of jumping to #8E62. This is, of course, because what we see is Willy's sprite drawn at his true y-coordinate, whereas the check at #8E39 is looking at his pixel y-coordinate stored at #85CF (which isn't adjusted to account for his precise position on a ramp). Therefore I believe that the wording accompanying the command at #8E39 needs to be re-worded (although I'm not sure exactly what words should be used to clarify the matter!) Possibly the references in the subsequent entries to cells "below Willy's sprite" might need tweaking too (if he's on a ramp, the cells being pointed at may partially contain his legs!)
  24. Out of interest John, how did the 'Old' adjacent ropes patch work? (Or not, as the case may be!?)
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.