590 lines
25 KiB
Lua
590 lines
25 KiB
Lua
---------------------------------------------------------------------------
|
|
-- Boulder Dash - Displaying Amoeba Pointer Movements
|
|
-- by AnS, 2012
|
|
-- requested by CtrlAltDestroy
|
|
---------------------------------------------------------------------------
|
|
-- Showcases following functions:
|
|
-- * memory.registerexec()
|
|
---------------------------------------------------------------------------
|
|
-- Usage:
|
|
-- Start playing any level of the BoulderDash that has the Amoeba enemy,
|
|
-- for example Level 2-3, Level 4-1 or Level 6-3.
|
|
-- Come close to the Amoeba enemy and see if you can find any regularities
|
|
-- in its movement. When you are convinced that its behavior is completely
|
|
-- erratic, run the script and unpause emulation. Now the script will draw
|
|
-- various arrows that show you the actual rules of the enemy AI.
|
|
--
|
|
-- You can control the "playback slider" in the bottom of the screen (click
|
|
-- or drag it with mouse). Also you can pause and unpause this mini-playback
|
|
-- while the emulator is still paused. This is necessary because the Amoeba
|
|
-- makes a new set of moves every 8 frames, and this time span is not enough
|
|
-- for you to replay all the logged events.
|
|
---------------------------------------------------------------------------
|
|
-- Explanations:
|
|
-- The Amoeba AI is defined by UpdateAmoeba() function (see the disassembly
|
|
-- code at the end of this script).
|
|
-- The UpdateAmoeba() makes many steps with the Amoeba Pointer (so the arrow
|
|
-- may move from tile to tile 100-200 times within the span of one frame!
|
|
-- This complexity makes it look unpredictable for player, even though the
|
|
-- rules of movement are very simple (move clockwise, rotate anticlockwise).
|
|
--
|
|
-- This script allows you to see all steps of this Pointer, so you can grasp
|
|
-- the logic of Amoeba movement/multiplication. The script registers hooks
|
|
-- to all essential points of the UpdateAmoeba() code, and logs the whole
|
|
-- process of the function execution. Then it pauses emulation and replays
|
|
-- the log of events by drawing arrows over the game sprites.
|
|
-- The log auto-resets at the beginning of every new call of UpdateAmoeba().
|
|
--
|
|
-- Similar approach can be used for displaying complex AI in other games.
|
|
---------------------------------------------------------------------------
|
|
|
|
-- constants
|
|
|
|
CELL_SIZE = 16;
|
|
CELL_HALFSIZE = CELL_SIZE / 2;
|
|
UPPER_BORDER_SIZE = 64;
|
|
LEFT_BORDER_SIZE = 32;
|
|
INTRO_DELAY = 1;
|
|
OUTRO_DELAY = 1;
|
|
MAX_ARROW_SIZE = 9;
|
|
|
|
PAUSE_BUTTON_X = 2;
|
|
PAUSE_BUTTON_Y = 172;
|
|
PAUSE_BUTTON_WIDTH = 16;
|
|
PAUSE_BUTTON_HEIGHT = 16;
|
|
|
|
TIMELINE_X = 20;
|
|
TIMELINE_LENGTH = 232;
|
|
TIMELINE_HEIGHT = 8;
|
|
TIMELINE_Y = 180;
|
|
|
|
ColorStatuses = { "#00A000FF", "#00FF00FF", "#A0A0A0FF", "#00FFFFFF", "#FFFFFFFF", "#C0C000FF" };
|
|
STATUS_NORMAL = 1; -- Green: Normal movements
|
|
STATUS_ROTATED = 2; -- Light-green: just rotated 90 degrees (anticlockwise) relative to current movement direction
|
|
STATUS_BUMPED = 3; -- Grey: bumped into obstacle
|
|
STATUS_SET = 4; -- Blue: Set amoeba
|
|
STATUS_REFUSED = 5; -- White: Refused to set amoeba
|
|
STATUS_END = 6; -- Yellow: RTS from UpdateAmoeba
|
|
|
|
-- variables
|
|
|
|
animation_timer = 0;
|
|
current_step = 0;
|
|
animation_speed = 0.5;
|
|
animation_paused = false;
|
|
mouse_held = false;
|
|
dragging_timeline = false;
|
|
|
|
log_size = 0;
|
|
creation_frame = -1;
|
|
|
|
-- arrays for storing the log data
|
|
|
|
Amoeba_X = {};
|
|
Amoeba_Y = {};
|
|
AmoebaDirection = {};
|
|
AmoebaPhase = {};
|
|
AmoebaTimer = {};
|
|
ColorStatus = {};
|
|
|
|
------------------------------------------------------------------------
|
|
function reset_log()
|
|
log_size = 0;
|
|
animation_timer = 0;
|
|
creation_frame = movie.framecount();
|
|
emu.pause();
|
|
end
|
|
|
|
function append_log_normal()
|
|
log_size = log_size + 1;
|
|
Amoeba_X[log_size] = memory.readbyte(0x0050); -- Temp_X
|
|
Amoeba_Y[log_size] = memory.readbyte(0x004F); -- Temp_Y
|
|
AmoebaDirection[log_size] = memory.readbyte(0x004E); -- Temp_AmoebaDirection
|
|
AmoebaPhase[log_size] = memory.readbyte(0x00B7);
|
|
AmoebaTimer[log_size] = memory.readbyte(0x00BD);
|
|
ColorStatus[log_size] = STATUS_NORMAL; -- Green: Normal movements
|
|
end
|
|
|
|
function append_log_rotated()
|
|
log_size = log_size + 1;
|
|
Amoeba_X[log_size] = memory.readbyte(0x0050); -- Temp_X
|
|
Amoeba_Y[log_size] = memory.readbyte(0x004F); -- Temp_Y
|
|
AmoebaDirection[log_size] = memory.readbyte(0x004E); -- Temp_AmoebaDirection
|
|
AmoebaPhase[log_size] = memory.readbyte(0x00B7);
|
|
AmoebaTimer[log_size] = memory.readbyte(0x00BD);
|
|
ColorStatus[log_size] = STATUS_ROTATED; -- Light-green: just rotated 90 degrees (anticlockwise) relative to current movement direction
|
|
end
|
|
|
|
function append_log_bumped()
|
|
log_size = log_size + 1;
|
|
Amoeba_X[log_size] = memory.readbyte(0x0050); -- Temp_X
|
|
Amoeba_Y[log_size] = memory.readbyte(0x004F); -- Temp_Y
|
|
AmoebaDirection[log_size] = memory.readbyte(0x004E); -- Temp_AmoebaDirection
|
|
AmoebaPhase[log_size] = memory.readbyte(0x00B7);
|
|
AmoebaTimer[log_size] = memory.readbyte(0x00BD);
|
|
ColorStatus[log_size] = STATUS_BUMPED; -- Grey: bumped into obstacle
|
|
end
|
|
|
|
function append_log_set()
|
|
log_size = log_size + 1;
|
|
Amoeba_X[log_size] = memory.readbyte(0x0050); -- Temp_X
|
|
Amoeba_Y[log_size] = memory.readbyte(0x004F); -- Temp_Y
|
|
AmoebaDirection[log_size] = memory.readbyte(0x004E); -- Temp_AmoebaDirection
|
|
AmoebaPhase[log_size] = memory.readbyte(0x00B7);
|
|
AmoebaTimer[log_size] = memory.readbyte(0x00BD);
|
|
ColorStatus[log_size] = STATUS_SET; -- Blue: Set amoeba
|
|
end
|
|
|
|
function append_log_refused()
|
|
log_size = log_size + 1;
|
|
Amoeba_X[log_size] = memory.readbyte(0x0050); -- Temp_X
|
|
Amoeba_Y[log_size] = memory.readbyte(0x004F); -- Temp_Y
|
|
AmoebaDirection[log_size] = memory.readbyte(0x004E); -- Temp_AmoebaDirection
|
|
AmoebaPhase[log_size] = memory.readbyte(0x00B7);
|
|
AmoebaTimer[log_size] = memory.readbyte(0x00BD);
|
|
ColorStatus[log_size] = STATUS_REFUSED; -- White: Refused to set amoeba
|
|
end
|
|
|
|
function append_log_rts()
|
|
log_size = log_size + 1;
|
|
Amoeba_X[log_size] = memory.readbyte(0x00BC);
|
|
AmoebaDirectionAnd_Y = memory.readbyte(0x00BB);
|
|
Amoeba_Y[log_size] = AND(AmoebaDirectionAnd_Y, 0x3F);
|
|
AmoebaDirection[log_size] = AND(AmoebaDirectionAnd_Y, 0xC0);
|
|
AmoebaPhase[log_size] = memory.readbyte(0x00B7);
|
|
AmoebaTimer[log_size] = memory.readbyte(0x00BD);
|
|
ColorStatus[log_size] = STATUS_END; -- Yellow: RTS from UpdateAmoeba
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function DrawArrow(x, y, direction, size, color)
|
|
x = x + CELL_HALFSIZE;
|
|
y = y + CELL_HALFSIZE;
|
|
if (direction == 00) then
|
|
-- Left
|
|
gui.line(x, y - size, x - size, y, color);
|
|
gui.line(x - size, y, x, y + size, color);
|
|
gui.line(x, y + size, x, y - size, color);
|
|
end
|
|
if (direction == 0x40) then
|
|
-- up
|
|
gui.line(x - size, y, x, y - size, color);
|
|
gui.line(x, y - size, x + size, y, color);
|
|
gui.line(x + size, y, x - size, y, color);
|
|
end
|
|
if (direction == 0x80) then
|
|
-- Right
|
|
gui.line(x, y - size, x + size, y, color);
|
|
gui.line(x + size, y, x, y + size, color);
|
|
gui.line(x, y + size, x, y - size, color);
|
|
end
|
|
if (direction == 0xC0) then
|
|
-- Down
|
|
gui.line(x - size, y, x, y + size, color);
|
|
gui.line(x, y + size, x + size, y, color);
|
|
gui.line(x + size, y, x - size, y, color);
|
|
end
|
|
end
|
|
------------------------------------------------------------------------
|
|
-- This function is called every frame when the emulator is unpaused,
|
|
-- and 20 times per second when the emulator is paused.
|
|
|
|
function update()
|
|
AmoebaOrigin_X = memory.readbyte(0x00B9);
|
|
AmoebaOriginDirectionAnd_Y = memory.readbyte(0x00B8);
|
|
AmoebaOrigin_Y = AND(AmoebaOriginDirectionAnd_Y, 0x3F);
|
|
AmoebaOriginDirection = AND(AmoebaOriginDirectionAnd_Y, 0xC0);
|
|
|
|
Camera_X_Lo = memory.readbyte(0x00BE);
|
|
Camera_X_Hi = memory.readbyte(0x00BF);
|
|
Camera_Y_Lo = memory.readbyte(0x00C0);
|
|
Camera_Y_Hi = memory.readbyte(0x00C1);
|
|
Camera_X = (256 * Camera_X_Hi + Camera_X_Lo) - UPPER_BORDER_SIZE;
|
|
Camera_Y = (256 * Camera_Y_Hi + Camera_Y_Lo) - LEFT_BORDER_SIZE;
|
|
|
|
if (log_size > 0) then
|
|
|
|
inp = input.get()
|
|
if (inp.leftclick) then
|
|
xm = inp.xmouse
|
|
ym = inp.ymouse
|
|
-- check click on the Pause/Resume button
|
|
if (not mouse_held) then
|
|
if (xm >= PAUSE_BUTTON_X and ym >= PAUSE_BUTTON_Y and xm < PAUSE_BUTTON_X + PAUSE_BUTTON_WIDTH and ym < PAUSE_BUTTON_Y + PAUSE_BUTTON_HEIGHT) then
|
|
animation_paused = not animation_paused;
|
|
end
|
|
end
|
|
-- check click on the timeline
|
|
if (dragging_timeline or (xm >= TIMELINE_X and ym >= TIMELINE_Y - TIMELINE_HEIGHT and xm < TIMELINE_X + TIMELINE_LENGTH and ym < TIMELINE_Y + TIMELINE_HEIGHT)) then
|
|
shift = xm - TIMELINE_X;
|
|
if (shift < 0) then
|
|
shift = 0;
|
|
else
|
|
if (shift > TIMELINE_LENGTH) then shift = TIMELINE_LENGTH; end
|
|
end
|
|
animation_timer = (INTRO_DELAY + log_size + OUTRO_DELAY) * shift / TIMELINE_LENGTH;
|
|
dragging_timeline = true;
|
|
end
|
|
mouse_held = true;
|
|
else
|
|
mouse_held = false;
|
|
dragging_timeline = false;
|
|
end
|
|
|
|
if (not animation_paused and not mouse_held) then
|
|
-- animate
|
|
animation_timer = animation_timer + animation_speed;
|
|
if (animation_timer > INTRO_DELAY + log_size + OUTRO_DELAY) then
|
|
animation_timer = 0;
|
|
end
|
|
end
|
|
|
|
current_step = math.floor(animation_timer - INTRO_DELAY);
|
|
if (current_step > log_size) then
|
|
current_step = log_size;
|
|
else
|
|
if (current_step < 1) then current_step = 1; end
|
|
end
|
|
|
|
-- show info
|
|
if (ColorStatus[current_step] == STATUS_END) then
|
|
-- emphasize the phase change by yellow
|
|
gui.text(3, 11, "AmoebaPhase = " .. AND(AmoebaPhase[current_step], 3), "yellow");
|
|
else
|
|
gui.text(3, 11, "AmoebaPhase = " .. AND(AmoebaPhase[current_step], 3));
|
|
end
|
|
if ((ColorStatus[current_step] == STATUS_SET) or (ColorStatus[current_step] == STATUS_REFUSED)) then
|
|
-- emphasize the decrement by red
|
|
gui.text(3, 20, "AmoebaTimer = " .. AmoebaTimer[current_step], "red");
|
|
else
|
|
gui.text(3, 20, "AmoebaTimer = " .. AmoebaTimer[current_step]);
|
|
end
|
|
|
|
-- draw origin
|
|
DrawArrow(AmoebaOrigin_X * CELL_SIZE - Camera_X, AmoebaOrigin_Y * CELL_SIZE - Camera_Y, AmoebaOriginDirection, 5, "#00FFFFFF");
|
|
|
|
-- draw tail of pointers
|
|
for i = (MAX_ARROW_SIZE - 1), 0, -1 do
|
|
step = current_step - i;
|
|
if (step > 0) then
|
|
for s = 1, (MAX_ARROW_SIZE - i) do
|
|
-- draw previous position of pointer
|
|
DrawArrow(Amoeba_X[step] * CELL_SIZE - Camera_X, Amoeba_Y[step] * CELL_SIZE - Camera_Y, AmoebaDirection[step], s, ColorStatuses[ColorStatus[step]]);
|
|
end
|
|
end
|
|
end
|
|
-- draw black border around the last (current) pointer
|
|
DrawArrow(Amoeba_X[current_step] * CELL_SIZE - Camera_X, Amoeba_Y[current_step] * CELL_SIZE - Camera_Y, AmoebaDirection[current_step], MAX_ARROW_SIZE + 1, "black");
|
|
|
|
-- draw GUI
|
|
-- Pause/Resume button
|
|
gui.box(PAUSE_BUTTON_X, PAUSE_BUTTON_Y, PAUSE_BUTTON_X + PAUSE_BUTTON_WIDTH, PAUSE_BUTTON_Y + PAUSE_BUTTON_HEIGHT, "grey", "black");
|
|
if (animation_paused) then
|
|
for s = 1, 6 do
|
|
DrawArrow(PAUSE_BUTTON_X - 2, PAUSE_BUTTON_Y, 0x80, s, "black");
|
|
end
|
|
else
|
|
gui.box(PAUSE_BUTTON_X + (PAUSE_BUTTON_WIDTH / 2) - 3, PAUSE_BUTTON_Y + 3, PAUSE_BUTTON_X + (PAUSE_BUTTON_WIDTH / 2) - 1, PAUSE_BUTTON_Y + PAUSE_BUTTON_HEIGHT - 3, "black", "black");
|
|
gui.box(PAUSE_BUTTON_X + (PAUSE_BUTTON_WIDTH / 2) + 3, PAUSE_BUTTON_Y + 3, PAUSE_BUTTON_X + (PAUSE_BUTTON_WIDTH / 2) + 1, PAUSE_BUTTON_Y + PAUSE_BUTTON_HEIGHT - 3, "black", "black");
|
|
end
|
|
|
|
-- line
|
|
gui.box(TIMELINE_X, TIMELINE_Y - TIMELINE_HEIGHT, TIMELINE_X + TIMELINE_LENGTH, TIMELINE_Y + TIMELINE_HEIGHT, "#00000040", "black");
|
|
gui.box(TIMELINE_X, TIMELINE_Y - 1, TIMELINE_X + TIMELINE_LENGTH, TIMELINE_Y + 1, "white", "black");
|
|
-- slider
|
|
shift = TIMELINE_LENGTH * animation_timer / (INTRO_DELAY + log_size + OUTRO_DELAY);
|
|
gui.box(TIMELINE_X + shift - 1, TIMELINE_Y - TIMELINE_HEIGHT, TIMELINE_X + shift + 1, TIMELINE_Y + TIMELINE_HEIGHT, "grey", "black");
|
|
-- info
|
|
gui.text(TIMELINE_X + 80 + creation_frame % 3, TIMELINE_Y + TIMELINE_HEIGHT + 2, "Displaying " .. current_step .. " / " .. log_size);
|
|
gui.text(TIMELINE_X + 80 + creation_frame % 3, TIMELINE_Y + TIMELINE_HEIGHT + 11, "Created at frame " .. creation_frame);
|
|
end
|
|
end
|
|
------------------------------------------------------------------------
|
|
gui.register(update);
|
|
|
|
memory.registerexec(0xCE60, reset_log); -- ReallyUpdateAmoeba
|
|
--memory.registerexec(0xCE66, reset_log); -- RestartFromOrigin
|
|
|
|
memory.registerexec(0xCE95, append_log_normal); -- TryMultiplying_Left
|
|
memory.registerexec(0xCEAE, append_log_normal); -- TryMultiplying_Up
|
|
memory.registerexec(0xCEC7, append_log_normal); -- TryMultiplying_Right
|
|
memory.registerexec(0xCEE0, append_log_normal); -- TryMultiplying_Down
|
|
memory.registerexec(0xCEF9, append_log_normal); -- TryMultiplying_Left2
|
|
memory.registerexec(0xCF12, append_log_normal); -- TryMultiplying_Up2
|
|
memory.registerexec(0xCF2B, append_log_normal); -- TryMultiplying_Right2
|
|
|
|
memory.registerexec(0xCEA3, append_log_rotated); -- Rotated from left to down
|
|
memory.registerexec(0xCEBC, append_log_rotated); -- Rotated from up to left
|
|
memory.registerexec(0xCED5, append_log_rotated); -- Rotated from right to up
|
|
memory.registerexec(0xCEEE, append_log_rotated); -- Rotated from down to right
|
|
memory.registerexec(0xCF07, append_log_rotated); -- Rotated from left to down
|
|
memory.registerexec(0xCF20, append_log_rotated); -- Rotated from up to left
|
|
memory.registerexec(0xCF39, append_log_rotated); -- Rotated from right to up
|
|
|
|
memory.registerexec(0xCE9C, append_log_bumped); -- Bumped into obstacle
|
|
memory.registerexec(0xCEB5, append_log_bumped); -- Bumped into obstacle
|
|
memory.registerexec(0xCECE, append_log_bumped); -- Bumped into obstacle
|
|
memory.registerexec(0xCEE7, append_log_bumped); -- Bumped into obstacle
|
|
memory.registerexec(0xCF00, append_log_bumped); -- Bumped into obstacle
|
|
memory.registerexec(0xCF19, append_log_bumped); -- Bumped into obstacle
|
|
memory.registerexec(0xCF32, append_log_bumped); -- Bumped into obstacle
|
|
|
|
memory.registerexec(0xCF97, append_log_normal); -- Jumped to another amoeba
|
|
memory.registerexec(0xCFA5, append_log_set); -- Set amoeba
|
|
memory.registerexec(0xCFA3, append_log_refused); -- Refused to set amoeba
|
|
memory.registerexec(0xCF5D, append_log_rts); -- RTS from UpdateAmoeba
|
|
|
|
------------------------------------------------------------------------
|
|
-- Disassembly of the BoulderDash code in question:
|
|
--
|
|
-- UpdateAmoeba:
|
|
-- ; Called every frame, but works only once per 8 frames
|
|
-- >01:CE51:A5 B7 LDA AmoebaPhase = #$82
|
|
-- 01:CE53:F0 0A BEQ ExitWithoutUpdate
|
|
-- 01:CE55:C9 FF CMP #$FF
|
|
-- 01:CE57:F0 06 BEQ ExitWithoutUpdate
|
|
-- 01:CE59:A5 FE LDA FrameCounter = #$6A
|
|
-- 01:CE5B:29 07 AND #$07
|
|
-- 01:CE5D:F0 01 BEQ ReallyUpdateAmoeba
|
|
-- ExitWithoutUpdate:
|
|
-- 01:CE5F:60 RTS -----------------------------------------------------------
|
|
-- ReallyUpdateAmoeba:
|
|
-- 01:CE60:A5 B7 LDA AmoebaPhase = #$82
|
|
-- 01:CE62:29 03 AND #$03
|
|
-- 01:CE64:D0 0C BNE PrepareForLoop25
|
|
-- RestartFromOrigin:
|
|
-- ; Every time the amoeba multiplies or fails to multiply within 32 frames, it restarts to the original point
|
|
-- 01:CE66:A9 00 LDA #$00
|
|
-- 01:CE68:85 B7 STA AmoebaPhase = #$82
|
|
-- 01:CE6A:A5 B8 LDA AmoebaOriginDirectionAnd_Y = #$41
|
|
-- 01:CE6C:85 BB STA AmoebaDirectionAnd_Y = #$41
|
|
-- 01:CE6E:A5 B9 LDA AmoebaOrigin_X = #$14
|
|
-- 01:CE70:85 BC STA Amoeba_X = #$18
|
|
-- PrepareForLoop25:
|
|
-- 01:CE72:A9 19 LDA #$19
|
|
-- 01:CE74:85 4D STA 25Tries = #$0A
|
|
-- ; Temp_X = Amoeba_X; Temp_Y = AmoebaDirectionAnd_Y & 00111111b; Temp_AmoebaDirection = AmoebaDirectionAnd_Y & 11000000b
|
|
-- 01:CE76:A5 BB LDA AmoebaDirectionAnd_Y = #$41
|
|
-- 01:CE78:29 3F AND #$3F
|
|
-- 01:CE7A:A8 TAY
|
|
-- 01:CE7B:A5 BB LDA AmoebaDirectionAnd_Y = #$41
|
|
-- 01:CE7D:29 C0 AND #$C0
|
|
-- 01:CE7F:85 4E STA Temp_AmoebaDirection = #$00
|
|
-- 01:CE81:A6 BC LDX Amoeba_X = #$18
|
|
-- 01:CE83:84 4F STY Temp_Y = #$38
|
|
-- 01:CE85:86 50 STX Temp_X = #$0A
|
|
-- Loop25Tries:
|
|
-- 01:CE87:A5 4E LDA Temp_AmoebaDirection = #$00
|
|
-- 01:CE89:C9 40 CMP #$40
|
|
-- 01:CE8B:F0 21 BEQ TryMultiplying_Up
|
|
-- 01:CE8D:C9 80 CMP #$80
|
|
-- 01:CE8F:F0 36 BEQ TryMultiplying_Right
|
|
-- 01:CE91:C9 C0 CMP #$C0
|
|
-- 01:CE93:F0 4B BEQ TryMultiplying_Down
|
|
-- TryMultiplying_Left:
|
|
-- 01:CE95:20 5E CF JSR CheckMultiplying_Left
|
|
-- 01:CE98:B0 05 BCS NextTimeTryDown
|
|
-- 01:CE9A:F0 0A BEQ OkSetAmoeba
|
|
-- 01:CE9C:4C AC CE JMP Restore_X
|
|
-- NextTimeTryDown:
|
|
-- ; Since we just moved the pointer to another amoeba, rotate direction anticlockwise
|
|
-- 01:CE9F:A9 C0 LDA #$C0
|
|
-- 01:CEA1:85 4E STA Temp_AmoebaDirection = #$00
|
|
-- 01:CEA3:4C 44 CF JMP DoUntilAll25TriesExpired
|
|
-- OkSetAmoeba:
|
|
-- 01:CEA6:20 99 CF JSR IntendToSetAmoebaToMap
|
|
-- 01:CEA9:90 01 BCC Restore_X
|
|
-- 01:CEAB:60 RTS -----------------------------------------------------------
|
|
-- Restore_X:
|
|
-- 01:CEAC:E6 50 INC Temp_X = #$0A
|
|
-- TryMultiplying_Up:
|
|
-- 01:CEAE:20 6A CF JSR CheckMultiplying_Up
|
|
-- 01:CEB1:B0 05 BCS NextTimeTryLeft
|
|
-- 01:CEB3:F0 0A BEQ OkSetAmoeba
|
|
-- 01:CEB5:4C C5 CE JMP Restore_Y
|
|
-- NextTimeTryLeft:
|
|
-- ; Since we just moved the pointer to another amoeba, rotate direction anticlockwise
|
|
-- 01:CEB8:A9 00 LDA #$00
|
|
-- 01:CEBA:85 4E STA Temp_AmoebaDirection = #$00
|
|
-- 01:CEBC:4C 44 CF JMP DoUntilAll25TriesExpired
|
|
-- OkSetAmoeba:
|
|
-- 01:CEBF:20 99 CF JSR IntendToSetAmoebaToMap
|
|
-- 01:CEC2:90 01 BCC Restore_Y
|
|
-- 01:CEC4:60 RTS -----------------------------------------------------------
|
|
-- Restore_Y:
|
|
-- 01:CEC5:E6 4F INC Temp_Y = #$38
|
|
-- TryMultiplying_Right:
|
|
-- 01:CEC7:20 76 CF JSR CheckMultiplying_Right
|
|
-- 01:CECA:B0 05 BCS NextTimeTryUp
|
|
-- 01:CECC:F0 0A BEQ OkSetAmoeba
|
|
-- 01:CECE:4C DE CE JMP Restore_X
|
|
-- NextTimeTryUp:
|
|
-- ; Since we just moved the pointer to another amoeba, rotate direction anticlockwise
|
|
-- 01:CED1:A9 40 LDA #$40
|
|
-- 01:CED3:85 4E STA Temp_AmoebaDirection = #$00
|
|
-- 01:CED5:4C 44 CF JMP DoUntilAll25TriesExpired
|
|
-- OkSetAmoeba:
|
|
-- 01:CED8:20 99 CF JSR IntendToSetAmoebaToMap
|
|
-- 01:CEDB:90 01 BCC Restore_X
|
|
-- 01:CEDD:60 RTS -----------------------------------------------------------
|
|
-- Restore_X:
|
|
-- 01:CEDE:C6 50 DEC Temp_X = #$0A
|
|
-- TryMultiplying_Down:
|
|
-- 01:CEE3:B0 05 BCS NextTimeTryRight
|
|
-- 01:CEE5:F0 0A BEQ OkSetAmoeba
|
|
-- 01:CEE7:4C F7 CE JMP Restore_X
|
|
-- NextTimeTryRight:
|
|
-- ; Since we just moved the pointer to another amoeba, rotate direction anticlockwise
|
|
-- 01:CEEA:A9 80 LDA #$80
|
|
-- 01:CEEC:85 4E STA Temp_AmoebaDirection = #$00
|
|
-- 01:CEEE:4C 44 CF JMP DoUntilAll25TriesExpired
|
|
-- OkSetAmoeba:
|
|
-- 01:CEF1:20 99 CF JSR IntendToSetAmoebaToMap
|
|
-- 01:CEF4:90 01 BCC Restore_X
|
|
-- 01:CEF6:60 RTS -----------------------------------------------------------
|
|
-- Restore_X:
|
|
-- 01:CEF7:C6 4F DEC Temp_Y = #$38
|
|
-- TryMultiplying_Left2:
|
|
-- 01:CEF9:20 5E CF JSR CheckMultiplying_Left
|
|
-- 01:CEFC:B0 05 BCS NextTimeTryDown2
|
|
-- 01:CEFE:F0 0A BEQ OkSetAmoeba
|
|
-- 01:CF00:4C 10 CF JMP Restore_X
|
|
-- NextTimeTryDown2:
|
|
-- ; Since we just moved the pointer to another amoeba, rotate direction anticlockwise
|
|
-- 01:CF03:A9 C0 LDA #$C0
|
|
-- 01:CF05:85 4E STA Temp_AmoebaDirection = #$00
|
|
-- 01:CF07:4C 44 CF JMP DoUntilAll25TriesExpired
|
|
-- OkSetAmoeba:
|
|
-- 01:CF0A:20 99 CF JSR IntendToSetAmoebaToMap
|
|
-- 01:CF0D:90 01 BCC Restore_X
|
|
-- 01:CF0F:60 RTS -----------------------------------------------------------
|
|
-- Restore_X:
|
|
-- 01:CF10:E6 50 INC Temp_X = #$0A
|
|
-- TryMultiplying_Up2:
|
|
-- 01:CF12:20 6A CF JSR CheckMultiplying_Up
|
|
-- 01:CF15:B0 05 BCS NextTimeTryLeft2
|
|
-- 01:CF17:F0 0A BEQ OkSetAmoeba
|
|
-- 01:CF19:4C 29 CF JMP Restore_Y
|
|
-- NextTimeTryLeft2:
|
|
-- ; Since we just moved the pointer to another amoeba, rotate direction anticlockwise
|
|
-- 01:CF1C:A9 00 LDA #$00
|
|
-- 01:CF1E:85 4E STA Temp_AmoebaDirection = #$00
|
|
-- 01:CF20:4C 44 CF JMP DoUntilAll25TriesExpired
|
|
-- OkSetAmoeba:
|
|
-- 01:CF23:20 99 CF JSR IntendToSetAmoebaToMap
|
|
-- 01:CF26:90 01 BCC Restore_Y
|
|
-- 01:CF28:60 RTS -----------------------------------------------------------
|
|
-- Restore_Y:
|
|
-- 01:CF29:E6 4F INC Temp_Y = #$38
|
|
-- TryMultiplying_Right2:
|
|
-- 01:CF2B:20 76 CF JSR CheckMultiplying_Right
|
|
-- 01:CF2E:B0 05 BCS NextTimeTryUp2
|
|
-- 01:CF30:F0 0A BEQ OkSetAmoeba
|
|
-- 01:CF32:4C 42 CF JMP Restore_X
|
|
-- NextTimeTryUp2:
|
|
-- ; Since we just moved the pointer to another amoeba, rotate direction anticlockwise
|
|
-- 01:CF35:A9 40 LDA #$40
|
|
-- 01:CF37:85 4E STA Temp_AmoebaDirection = #$00
|
|
-- 01:CF39:4C 44 CF JMP DoUntilAll25TriesExpired
|
|
-- OkSetAmoeba:
|
|
-- 01:CF3C:20 99 CF JSR IntendToSetAmoebaToMap
|
|
-- 01:CF3F:90 01 BCC Restore_X
|
|
-- 01:CF41:60 RTS -----------------------------------------------------------
|
|
-- Restore_X:
|
|
-- 01:CF42:C6 50 DEC Temp_X = #$0A
|
|
-- DoUntilAll25TriesExpired:
|
|
-- 01:CF44:C6 4D DEC 25Tries = #$0A
|
|
-- 01:CF46:F0 03 BEQ All25TriesExpired
|
|
-- 01:CF48:4C 87 CE JMP Loop25Tries
|
|
-- All25TriesExpired:
|
|
-- ; Exits from UpdateAmoeba
|
|
-- 01:CF4B:A5 4F LDA Temp_Y = #$38
|
|
-- 01:CF4D:05 4E ORA Temp_AmoebaDirection = #$00
|
|
-- 01:CF4F:85 BB STA AmoebaDirectionAnd_Y = #$41
|
|
-- 01:CF51:A5 50 LDA Temp_X = #$0A
|
|
-- 01:CF53:85 BC STA Amoeba_X = #$18
|
|
-- 01:CF55:E6 B7 INC AmoebaPhase = #$82
|
|
-- 01:CF57:A5 B7 LDA AmoebaPhase = #$82
|
|
-- 01:CF59:29 83 AND #$83
|
|
-- 01:CF5B:85 B7 STA AmoebaPhase = #$82
|
|
-- 01:CF5D:60 RTS -----------------------------------------------------------
|
|
--
|
|
-- CheckMultiplying_Left:
|
|
-- ; Returns C=1 when the pointer is on another amoeba, Z=1 when can multiply
|
|
-- 01:CF5E:A4 4F LDY Temp_Y = #$38
|
|
-- 01:CF60:C6 50 DEC Temp_X = #$0A
|
|
-- 01:CF62:A6 50 LDX Temp_X = #$0A
|
|
-- 01:CF64:20 7D CC JSR GetObjectFromMap
|
|
-- 01:CF67:4C 8B CF JMP CheckMultiplying
|
|
-- CheckMultiplying_Up:
|
|
-- ; Returns C=1 when the pointer is on another amoeba, Z=1 when can multiply
|
|
-- 01:CF6A:C6 4F DEC Temp_Y = #$38
|
|
-- 01:CF6C:A4 4F LDY Temp_Y = #$38
|
|
-- 01:CF6E:A6 50 LDX Temp_X = #$0A
|
|
-- 01:CF70:20 7D CC JSR GetObjectFromMap
|
|
-- 01:CF73:4C 8B CF JMP CheckMultiplying
|
|
-- CheckMultiplying_Right:
|
|
-- ; Returns C=1 when the pointer is on another amoeba, Z=1 when can multiply
|
|
-- 01:CF76:E6 50 INC Temp_X = #$0A
|
|
-- 01:CF78:A4 4F LDY Temp_Y = #$38
|
|
-- 01:CF7A:A6 50 LDX Temp_X = #$0A
|
|
-- 01:CF7C:20 7D CC JSR GetObjectFromMap
|
|
-- 01:CF7F:4C 8B CF JMP CheckMultiplying
|
|
-- CheckMultiplying_Down:
|
|
-- ; Returns C=1 when the pointer is on another amoeba, Z=1 when can multiply
|
|
-- 01:CF82:E6 4F INC Temp_Y = #$38
|
|
-- 01:CF84:A4 4F LDY Temp_Y = #$38
|
|
-- 01:CF86:A6 50 LDX Temp_X = #$0A
|
|
-- 01:CF88:20 7D CC JSR GetObjectFromMap
|
|
-- CheckMultiplying:
|
|
-- ; Returns C=1 when the pointer is on another amoeba, Z=1 when can multiply
|
|
-- 01:CF8B:C9 D0 CMP #$D0
|
|
-- 01:CF8D:F0 08 BEQ OccupiedByAmoeba
|
|
-- 01:CF8F:C9 00 CMP #$00
|
|
-- 01:CF91:F0 02 BEQ $CF95
|
|
-- 01:CF93:C9 20 CMP #$20
|
|
-- ; Return result in Z flag: Z=1 when can multiply
|
|
-- 01:CF95:18 CLC
|
|
-- 01:CF96:60 RTS -----------------------------------------------------------
|
|
-- OccupiedByAmoeba:
|
|
-- ; The place is occupied by another amoeba, so we can't multiply there
|
|
-- ; but instead we will move the pointer there!
|
|
-- 01:CF97:38 SEC
|
|
-- 01:CF98:60 RTS -----------------------------------------------------------
|
|
--
|
|
-- IntendToSetAmoebaToMap:
|
|
-- ; Uses Temp_X/Temp_Y, returns C=1 on success, and C=0 when --Countdown_BD != 0
|
|
-- 01:CF99:A5 B7 LDA AmoebaPhase = #$82
|
|
-- 01:CF9B:09 80 ORA #$80
|
|
-- 01:CF9D:85 B7 STA AmoebaPhase = #$82
|
|
-- 01:CF9F:C6 BD DEC Amoeba_Timer = #$1D
|
|
-- 01:CFA1:F0 02 BEQ ReallySetAmoebaToMap
|
|
-- DontWantToMultiply:
|
|
-- ; The IntendToSetAmoebaToMap() must be called many times, only then the multiplication will be made
|
|
-- 01:CFA3:18 CLC
|
|
-- 01:CFA4:60 RTS -----------------------------------------------------------
|
|
-- ReallySetAmoebaToMap:
|
|
-- ; Uses Temp_X/Temp_Y, returns C=1 on success
|
|
-- 01:CFA5:A4 4F LDY Temp_Y = #$38
|
|
-- 01:CFA7:A6 50 LDX Temp_X = #$0A
|
|
-- 01:CFA9:A9 D0 LDA #$D0
|
|
-- 01:CFAB:20 77 D0 JSR SetObjectToMap
|
|
-- 01:CFAE:A4 4F LDY Temp_Y = #$38
|
|
-- 01:CFB0:A6 50 LDX Temp_X = #$0A
|
|
-- 01:CFB2:A9 D0 LDA #$D0
|
|
-- 01:CFB4:20 B7 D0 JSR AddObjectToBuffer
|
|
-- SheduleNextTimer:
|
|
-- ; Every time the waiting period decreases by 4
|
|
-- 01:CFB7:A5 BA LDA AmoebaTimerResetValue = #$3F
|
|
-- 01:CFB9:38 SEC
|
|
-- 01:CFBA:E9 04 SBC #$04
|
|
-- 01:CFBC:85 BA STA AmoebaTimerResetValue = #$3F
|
|
-- 01:CFBE:85 BD STA Amoeba_Timer = #$1D
|
|
-- ; Next update should reset the pointer to the origin
|
|
-- 01:CFC0:A9 80 LDA #$80
|
|
-- 01:CFC2:85 B7 STA AmoebaPhase = #$82
|
|
-- 01:CFC4:38 SEC
|
|
-- 01:CFC5:60 RTS -----------------------------------------------------------
|
|
------------------------------------------------------------------------
|
|
|