📜 ⬆️ ⬇️

Why are Pinky and Inky acting differently when Pac-Man moves up?

In Pac-Man (and in many clones and sequels), it was found that Pinky and Inky's ghosts haunt Pac-Man, determining the point at which to follow, taking into account the direction in which he moves. For example, Pinky usually follows a point that is located four units from Pac-Man´a in the direction of its movement. However, if Pac-Man moves up, this point becomes the point located four units up and four units to the left relative to Pac-Man. Inky's ghost has a similar behavior when Pac-Man moves up. Why does Pinky and Inky have different behaviors when Pac-Man moves up?

In short, in my opinion, because of a programming error. Here is the evidence.


')
Pac-Man encodes directions in at least two ways. One way Pac-Man records directions is using one byte with the following values: Right = 0, Down = 1, Left = 2, and Up = 3.

Another way is when Pac-Man records directions using a two-byte pair to record the direction vector as follows. All values ​​are hexadecimal values ​​prefixed with #, and can be viewed in memory locations # 32FF through # 3306.

Right = (#FF, 00)

Down = (00, 01)

Left = (01, 00)

Up = (00, #FF)

#FF (decimal 255) is used as -1, for 8-bit math Z80. Consider an example:



The coordinates of the points (XX, YY) are given in hexadecimal notation. Let Pac-Man at the point (# 26, # 2F).

The game calculates the positions of the four points surrounding this, adding a two-byte direction vector to the coordinates of this point.

To calculate the position of the point to the right of Pac-Man, you need to add the two-byte number “Right” (# FF, 00) to (# 26, # 2F). Since FF is used as -1, we get the coordinates of the point (# 25, # 2F), which is located to the right of Pac-Man´a. To calculate the point below Pac-Man, you need to add a two-byte number “Down” (00.01) to (# 26, # 2F). We obtain (# 26, # 30), a point that is located directly under Pac-Man. To calculate the position of the point to the left of Pac-Man, you need to add a two-byte number “Left” (01.00) to (# 26, # 2F). We obtain (# 27, # 2F), a point that is located to the left of Pac-Man. To calculate the position of a point above Pac-Man, you need to add a two-byte number “Up” (00, # FF) to (# 26, # 2F). Since FF is used as -1, we get the coordinates of a point (# 26, # 2E), which is located directly above Pac-Man.

All this works correctly only when we work with unit coordinates separately, which is the only correct way to perform these arithmetic operations, taking into account the presence of possible negative numbers. For example, in the code at # 2000, there is a procedure that performs these actions correctly:

 2000 FD7E00 LD A, (IY + # 00);  load Y coordinate to register A
 2003 DD8600 ADD A, (IX + # 00);  add the y-coordinate of the offset vector
 2006 6F LD L, A;  save result to L
 2007 FD7E01 LD A, (IY + # 01);  load X coordinate to register A
 200A DD8601 ADD A, (IX + # 01);  add x-coordinate of offset vector
 200D 67 LD H, A;  save result to H
 200E C9 RET;  return


This procedure is used to load into the two-byte register pair HL a new position on the given reference position specified in the IY register and the direction vector specified in register IX. It works correctly, because the addition of the X and Y coordinates is done separately.

The problem occurs when the game tries to calculate, for example, the target point for Pinky. The game code defines Pinky's goal as a four-point point from Pac-Man, relative to its direction of movement.

To calculate this point, the code takes the Pac-Man´ direction vector and adds it to itself, thereby doubling it. Then it doubles the resulting result, getting a value four times larger than the original. The problem is that the X and Y coordinates do not add up separately, they add up simultaneously. The bug manifests itself when calculating the direction vector "Up". Here is an excerpt from Pac-Man’s assembler code for the Z80, the code that contains the code is highlighted:

 ;  Pac-Man Pinky targeting subroutine

 278E ED5B394D LD DE, (# 4D39);  load Pac position into DE
 2792 2A1C4D LD HL, (# 4D1C);  upload to Pac-Man´s direction vector HL
 2795 29 ADD HL, HL;  [bug !!!] double Pac-Man´s direction vector
 2796 29 ADD HL, HL;  [bug !!!] to quadruple Pac-Man´s direction vector
 2797 19 ADD HL, DE;  [bug !!!] add result to position to get target point


The following table shows the results of this procedure (relative to the Pac-Man´a zero position):
Vector nameInitial HL value and result2xHL and result4xHL and result
To the right# FF00 (-1,0)# FE00 (-2.0)# FC00 (-4,0)
Way down# 0001 (0.1)# 0002 (0,2)# 0004 (0.4)
To the left# 0100 (1.0)# 0200 (2.0)# 0400 (4.0)
Up# 00FF (1, -1)# 01FE (2, -2)# 03FC (4, -4)


HL is a two-byte register pair Z80. In line # 2792, the X coordinate of the vector is loaded into H, the Y coordinate of the vector is loaded into L.

When a vector is processed for the “Right” direction, HL contains # FF00. The instruction in line # 2792 adds this value to itself, after which the register pair HL contains # FE00, and the transfer flag is set, due to the H register overflow. But the program ignores the transfer flag and uses only a numeric value, which is interpreted as -2 . When doubling occurs in line # 2796, the value of # FC00 is in HL, which is the correct value of -4. The carry flag is ignored. When the addition to the Pac-Man position occurs, the transfer flag is set again as a result of the transfer, which, in other matters, is again ignored by the program. The desired result is achieved: the new point is located in 4 units to the right of Pac-Man.

Bug in action



However, when a vector is used for the direction “Up”, in line # 2792, # 00FF is loaded into the register pair HL. When doubling occurs on the next line, HL contains # 01FE. Instead of discarding the overflow flag, as was the case when processing the vector for the “Right” direction, doubling the Y coordinate leads to an overflow with transferring the transfer flag to the X coordinate. This causes damage to the vector, since the vector shifts not only upwards, but also to the left. The next doubling gives the value # 03FC, which, being added to Pac-Man´s coordinates, results in a new point 4 units up and to the left.



I believe that this is a bug, and it is not intentional, because the same command is used for all four directions. In other words, the code does not launch a separate procedure for processing the “Up” direction, however, for the “Up” direction, the procedure gives an unexpected, different from other directions result . Given the low-level nature of the problem, I doubt that programmers were aware of this. If they knew this, then they apparently ignored this problem. Nevertheless, I doubt that they would leave this bug if they knew about it.

Source: https://habr.com/ru/post/109408/


All Articles