Resource centre for ZX Spectrum games
      using Manic Miner and Jet Set Willy game engines

 

Archive of the

Manic Miner & Jet Set Willy Yahoo! Group

messages

 

 

 

Message: 7208

Author: ian.rushforth

Date: 07/11/2018

Subject: Re: two other random points

 

I've just read part of an earlier post of mine back, and realised that I'd missed a word out of this paragraph (word now reinstated in capital letters):


> Swapping the relative position of the two corrupted guardians also meant that the vertical guardian is the > one which is turned into a weird rope (I confirmed this by assigning it a green INK colour and a few

> infilled pixels in its bitmap; those static pixels were initially drawn at the left-hand side of the screen

> [incidentally confirming the position of the 'disguised' Skylab, via a burst of green 'lava'!], but those green > pixels DISAPPEARED just at the point when Willy was teleported onto the rope [I still had the path of the > rope cleared at this point, to prevent the instantaneous teleportation to the room above]).



Anyway, onto something more substantial.  This was my previous post in this thread:


>> Immediately after the bug has kicked in:


>> Invisible vertical (#82B7-#82BE): 3A 00 01 C0 00 99 D3 80 >> Colourful worm (#82BF-#82C6):  B3 82 DA D2 44 40 44 EC


> Corrupted bytes in the room buffer are highlighted in bold.  Some of those values may have been edited by

> the time the snapshot was taken, by the enactment of the 'Draw the guardians' routine during the same pass > through the Main Loop.  (In the snapshot, the Program Counter has arrived at #89FE later in the Main Loop, > the LDIR loop which copies the screen buffer to the display file.)


I have had some further thoughts on this enduring mystery:


I believe the snapshot of the corrupted file was taken after one iteration of 'Move the guardians' had taken place subsequent to the corruption taking place.  So some of the eight definition bytes of the newly-created rope (which was created via corruption of the 'colourful worm' horizontal guardian), will have been altered by the 'Move the guardians' routine having moved the rope, or the 'Draw the guardians' routine having drawn it.  Looking at the eight bytes one-by-one (N.B. the 'Adjacent ropes (new)' patch is implemented in Ultimate Manic Miner, so the bytes don't spill beyond the usual eight-byte slot in the guardian buffer):


Byte 0: holds the value #B3 in the snapshot.  Bits 0-3 = 03 (it's now a rope). Bit 5 is set if Willy is on this rope.  Bit 6 is set for a stationary rope,  Bit 7 determines the direction of the rope's swing (left or right).

The 'rope' might (or might not) have changed swing direction since the corruption occurred, and Willy was not on the rope at the time so Bit 5 might have been set by the 'Draw the rope' code, when one of the rope's pixels collided with an on-pixel in a room cell through which it is dangling.

Therefore Byte 0 (the address #82BF) might have originally been overwritten with a value of #13, #33, #93 or #B3 at the instant when the corruption occurred.  Update: in light of the analysis of the Animation Frame Index (see below), the corrupted value could only have been either #93 or #B3.


Byte 1: holds the value #82 in the snapshot.  This is the current Animation Frame Index of the rope.  If one iteration of 'Move the rope' has occurred since the corruption, then in the intervening period it will have been incremented or decremented once (or twice if the rope is near the mid-point of its swing).

Thinking some more about this, the centre of the swing occurs at #80, for a rope swinging from left to right.  If the value of Byte 1 is less than #94 then the rope is swinging rightwards, at the faster rate (i.e. just left-of-centre), so the Animation Frame Index will have been decremented by 4 from its original value.  This suggests that Byte 1 (the address #82C0) would probably have held the value #86 at the time of corruption.


Byte 2: holds the value #DA in the snapshot.  This is the x-coordinate of the top of the rope.  It's an 'invalid' value, but it shouldn't have been edited at all as the rope was moved.  If the value is outside of the range #00-#1F then the top segment of the rope 'wraps around' to be drawn in a lower character row than the top one.  Subsequent segments 'lower down' the rope can then be drawn higher up on the screen because of the way that the raster lines are drawn to the screen.  This might explain why Willy (when he isn't teleported to another room) appears on the rope further down than the top of the screen.  Anyway, the value #DA was probably written to Byte 2 (address #82C1) at the instant of corruption.


Byte 3: holds the value #D2 in the snapshot.  This keeps track of the x-coordinate of the segment of rope being drawn.  When you view a snapshot after all the rope has been drawn, it holds the x-coordinate of the bottom segment of the rope.  For a rope that is positioned left-of centre, it makes sense that it holds a value slightly less than Byte 2 at this point (since each segment is positioned below, slightly to the left, of the one above).  This address #82C2 has been completely overwritten by the enactment of 'Move the rope', and could have held ANY value at the moment of corruption.


Byte 4: holds the value #44 in the snapshot.  This is the length of the rope (number of segments), and isn't edited by either the 'Move the rope' or 'Draw the rope' routines.  So address #82C3 was overwritten with #44 at the moment of corruption.


Byte 5: holds the value #40 in the snapshot.  This is the segment drawing byte, which is initially overwritten with the value #80 by 'Draw the rope', but the single set bit (initially Bit 7) in this byte is rotated (leftwards in the case of a left-of-centre rope) as each consecutive segment of the rope is drawn.  Hence the current value #40 (with Bit 6 set for the bottom segment of the rope).  But the corresponding address #82C4 could have held ANY value at the moment of corruption.


Byte 6: holds the value #44 in the snapshot.  This is the index of the segment of rope being drawn.  When you view a snapshot after the rope has finished been drawn, it holds a value equal to the length of the rope (i.e. it matches the value of Byte 4).   The corresponding address #82C5 could have held ANY value at the moment of corruption.


Byte 7: holds the value #EC in the snapshot.  This is the animation frame at which the rope changes direction.  It is not edited by either the 'Move the rope' or 'Draw the rope' routines.  (It is quite a high value as well, which also confirms that the rope will not have changed direction - by virtue of having exceeded this value - in the intervening period since the corruption occurred.)  The value #EC was probably written to Byte 7 (address #82C6) at the instant of corruption.


The last two bytes of the previous guardian which were corrupted (#82BD-BE) are presumably unchanged since the corruption, since it remains a vertical guardian, and Bytes 6-7 of a vertical guardian are not edited by either 'Move the guardians' or 'Draw the guardians'.

So this is my best guess as to the state of addresses #82BD-#82C6, immediately after they were corrupted (XX denotes an unknown value):


D3 80 93/B3 86 DA XX 44 XX XX EC



> It is notable that the Trigger code (enacted during the 'Move the guardians' routine) starts at #ECDA, and the > values 'EC' and 'DA' are both present in the above.  So the corruption may have occurred at the point when

> the Trigger which comes after the above guardians in the room's guardian list (the one which turns off the

> 'DIE MORTAL' lettering guardians) was triggered?


A while back I was studying John Elliott's code that implements Triggers in JSW64.  It occupies a tight space in the game engine, so in order to fit in the necessary commands, John used an optimisation which made use of a couple of commands that involve the Stack Pointer.  If I recall correctly, it involved temporarily swapping the address held by the Stack Pointer with the value stored in the IX register pair [via the EX (SP), IX command], then after a succession of PUSH and POP commands (to edit the definition bytes of the guardian which is the target of the Trigger), swapping the contents of IX and the Stack Pointer back again.


I wonder if that has something to do with the guardian-buffer corruption that occurs in 'Ultimate Manic Miner', which both myself and SOA independently came across and reported here?  Both instances occurred immediately after a Trigger-related sequence of events (see previous posts in this thread), and both involved a guardian suddely being turned into a rope (causing an 'off-screen' pixel-collision and hence teleportation to the room above).


The above sequence of bytes look like they might be the result of the Stack being sent to the wrong place temporarily, for some reason.  Particularly the fact that the 'base' of the new 'temporary stack' holds the value #ECXX, another entry (stack bytes are paired up, remember) holds #XXDA, and #ECDA is the start of the Trigger code...

 

 

arrowleft
arrowright