Several things needed to be done to achieve these wraparound guardians.
- In the 'Draw a sprite' routine (#9456), I added a command after the code at #9482-85 [original addresses] which moves HL down from one vertical screen-third to the next. The command RES 4, H ensures that the guardian doesn't extend from the secondary pixel-buffer into the primary pixel-buffer, but wraps back round to the top of the former. i.e. a value #70xx is adjusted to #60xx.
- Also in 'Draw a sprite', after the INC L command at #9462 [original address], I made a check to see if the right-hand half of the sprite is wrapping round to the left-hand side of the screen:
If that sets the Zero Flag, then subtract #20 and load back to L, draw the graphic byte under consideration and then add #20 again before moving down to draw the next pair of graphic bytes on the next pixel-row.
- The 'Draw a guardian' routine also needs to be adjusted, in a similar manner to the above, to keep the guardian's colour attributes within the same character row when it is wrapping round past the right-hand edge of the screen, and also to prevent its colour attribute being written permanently onto the primary attribute buffer when it is wrapping past the bottom of the screen. In the latter case, a RES 1, H command is used to adjust a value for the attribute coordinates of #5Exx to #5Cxx, which is back within the valid range.
- In the 'Move the guardians' routine, a simple adjustment has been made for horizontal guardians at #9151 and #9174 [original addresses], in order to prevent the guardians from changing sprite (which previously occurred because the DEC (IX+$02) / INC (IX+$02) commands meant that the adjustment of the x-coordinate in Bits 0-4 was spilling over into the Base Sprite setting in Bits 5-7). Preventing this was achieved by loading the guardian byte definition byte 02 into both the Accumulator and the C register, then adjusting A accordingly for the appropriate direction of movement (e.g. applying a DEC A or an INC A, although in the test file uploaded above I have implemented the shared left/right movement code for guardians), and then doing the following sequence:
before reloading the result in A back to IX+$02. That adjusts the x-coordinate from #00 to #1F or vice versa, whilst leaving the Base Sprite setting unaltered.
- The changes to the vertical guardians part of the 'Move the guardians' routine are the 'piece de resistance'! After the code which updates the animation frame for vertical guardians (or not, depending on the animation speed), I have devised the following [starts at #9193 in the original code], which was built upon an earlier draft of this part of the routine provided by Norman Sword:
(Note that HL is pointing at guardian definition byte 00 going into this, after a pair of commands PUSH IX / POP HL.)
INC HL ;HL points at guardian definition byte 03 (pixel y-coordinate)
LD A, (HL)
INC HL ;HL now points at guardian definition byte 04 (vertical speed)
ADD A, (HL)
LD B, A ;A and B now both hold the updated y-coordinate
LD C, #00 ;C will be used to keep track of various flags
LD D, (IX+$06) ;upper bound of guardian in D
LD E, (IX+$07) ;lower bound of guardian in E
CP D ;has the guardian moved past its upper bound?
RR C ;rotate a raised flag into C if so
CP E ;has the guardian moved past its lower bound?
RR C ;rotate a lowered flag into C if so [or if it is exactly at its upper bound, but that situation is dealt with separately below]
LD A, E ;load the guardian's lower bound into A
CP D ;if this is a normal vertical guardian, then E>D; if it is a wraparound, then D>E
RR C ;rotate a flag into C (raised only for a wraparound vertical guardian)
BIT 7, (HL) ;is the guardian currently moving downwards?
JR Z, down ;if so, skip the next command
LD A, D ;otherwise, load the guardian's upper bound into A (the guardian is currently moving upwards)
CP B ;has the guardian exactly reached the extent of its permitted range for the current direction of travel?
JR Z, change_direction ;if so, then jump to toggle the direction of travel
RRC C ;this command doesn't change the value in C, it merely test the status of the Parity Flag
JP PO, y_update ;If Parity is odd, then jump to update y-coordinate with the value stored in B (the guardian is within its permitted range)
LD B, A ;otherwise, if Parity is even, then the guardian has exceeded its permitted range, so load up B with the extent of its permitted range for the current direction of travel
LD A, (HL) ;load vertical speed into A
NEG ;toggle it to reverse the guardian's direction of travel
LD (HL), A ;and load back into guardian byte 04
DEC HL ;HL points at guardian definition byte 03 again (pixel y-coordinate)
LD (HL), B ;update the guardian's y-coordinate with the value stored in B
That is then directly followed by (or a jump is made to) the existing command LD DE, #0008 at #91B6.
I'll add some commentary about the use of the Parity Flag when I get more time.
Edited by IRF, 08 March 2018 - 06:02 PM.