data/claims/pac-man.yamlPac-Man (Mark Burkley's C translation of the ROM)
Game: Pac-Man.
In chase mode each Pac-Man ghost steers toward its own target tile. Blinky targets Pac-Man's tile. Pinky targets Pac-Man's tile plus 4 times his direction vector, and Inky takes Pac-Man's tile plus twice the vector, doubles it, and subtracts Blinky's tile. The Z80 does both sums as 16-bit adds of a packed two-byte tile and vector (add hl,hl, add hl,de), so a carry out of the low byte's axis spills into the other axis; away from such a carry, Pinky's target is 4 tiles ahead of Pac-Man and Inky's starts 2 ahead. Clyde targets Pac-Man's tile while the squared distance between them is 64 or more, that is 8 tiles or farther, and otherwise heads for his scatter corner. The translation reads MAME's pacman set, Midway's release. pac-man/ghost-targets
About: Pac-Man Enemy targeting
Value
{
"pinkyMultiple": 4,
"inkyMultiple": 2,
"clydeRadius": 8
}pacman.c#L11479BLINKY_VECTOR2 = findBestOrientation_2966 (BLINKY_TILE, PACMAN_TILE2, &BLINKY_ORIENTATION);pacman-c: a C translation of the Pac-Man ROM · pacman.c#L11516-L11539ld de,(#4d39)
ld hl,(#4d1c)
add hl,hl
add hl,hl
add hl,de
…
/* Make a target tile for pinky that is 4 times pacman's current direction
* of travel plus pacman's current tile */
…
PINKY_VECTOR2 = findBestOrientation_2966 (PINKY_TILE, target, &PINKY_ORIENTATION);pacman-c: a C translation of the Pac-Man ROM · pacman.c#L11576-L11617ld bc,(#4d0a)
ld de,(#4d39)
ld hl,(#4d1c)
add hl,hl
add hl,de
…
/* Inky tries to get pacman in a pincer movement with blinky so he targets
* a tile that is 2 tiles ahead of pacman's current vector and then twice
* the vector between that tile and blinky's tile */
…
target.y = target.y * 2 - BLINKY_TILE.y;
…
…pacman-c: a C translation of the Pac-Man ROM · pacman.c#L11637-L11682random:
…
XYPOS target = { 0x40, 0x3b };
…
uint16_t dist = computeDistance_29ea (PACMAN_TILE2, CLYDE_TILE);
…
if (dist < 0x40)
goto random;
…
findBestOrientation_2966 (CLYDE_TILE, PACMAN_TILE2, &CLYDE_ORIENTATION);pacman-c: a C translation of the Pac-Man ROM · pacman.c#L12131-L12178uint16_t computeDistance_29ea(XYPOS ix, XYPOS iy)
…
int b = ix.y - iy.y;
…
uint16_t hl = calcSquare_2a12(b);
…
b = ix.x - iy.x;
…
hl += calcSquare_2a12(b);pacman-c: a C translation of the Pac-Man ROM · Makefile#L20-L23pacman.6e \
pacman.6f \
pacman.6h \
pacman.6j \Checked by claude, 2026-10-21 — read the four ScatterOrChase routines (2730-283A) in the kept Z80: Pinky's target is DE + 4×HL from two `add hl,hl` and one `add hl,de` on 16-bit pairs (2795-2797); Inky's is HL×2 + DE at 27D6-27D7, then L and H each doubled less Blinky's byte (27D8-27DF); Clyde compares the squared distance from 29EA with $40 at 281E-2824The C translation computes each axis apart, so it drops the carry between the packed bytes; the statement follows the Z80. Which byte holds which axis, and so which directions carry, isn't read here.
Cited by: Enemy targeting, Pac-Man
findBestOrientation_2966 turns a target into a move: it tries the four directions in turn, skips the reverse of the ghost's current one and any that points into a maze wall, and keeps the one whose next tile has the smallest squared distance to the target; on a tie the later direction wins. pac-man/steering
About: Pac-Man Enemy targeting
Codepacman-c: a C translation of the Pac-Man ROM ·pacman.c#L11993-L12106XYPOS findBestOrientation_2966 (XYPOS hl, XYPOS de, uint8_t *a)
…
OPPOSITE_ORIENTATION = *a ^ 2;
…
MIN_DISTANCE_FOUND = 0xffff;
…
if (OPPOSITE_ORIENTATION != TRIAL_ORIENTATION)
…
/* If the trial orientation points us at a maze wall then
…
uint16_t dist = computeDistance_29ea (DEST_TILE_POS, TMP_RESULT_POS);
…
…Checked by claude, 2026-10-21 — read 2966-29D4 in the kept Z80 and the C; the `sbc hl,de` / `jp c` at 29B7-29B9 keeps a new direction when the stored minimum is greater than or equal to its distanceCited by: Enemy targeting, Pac-Man
A mode counter at $4DC1 runs from 0 to 7: on even values the ghosts head for fixed scatter tiles, on odd values they chase. ghostsChangeOrientation_0e36 counts time and steps the counter when the count meets a 16-bit entry for the current mode, then flags all four ghosts to reverse. At 7 it stops, so the game holds four scatter phases and leaves the fourth chase phase on for good. The count pauses while Pac-Man is powered up. pac-man/modes
About: Pac-Man Enemy targeting
Value
{
"scatterPhases": 4,
"chasePhases": 4
}pacman.c#L4167-L4225void ghostsChangeOrientation_0e36 (void)
…
if (PACMAN_POWEREDUP)
return;
…
if (NONRANDOM_MOVEMENT == 7)
return;
…
ORIENTATION_CHANGE_COUNT++;
…
if (ORIENTATION_CHANGE_COUNT != DIFFICULTY_TABLE[NONRANDOM_MOVEMENT])
return;
…pacman-c: a C translation of the Pac-Man ROM · pacman.c#L11434-L11479if ((NONRANDOM_MOVEMENT & 1) == 0)
…
XYPOS target = { 0x1d, 0x22 };
…
BLINKY_VECTOR2 = findBestOrientation_2966 (BLINKY_TILE, PACMAN_TILE2, &BLINKY_ORIENTATION);Checked by claude, 2026-10-21 — read 0E36-0E6B (the timer, compared with a 16-bit entry indexed by the counter, then `inc a` into $4DC1 and the four reverse flags) and the bit-0 test at the head of each ScatterOrChase routine; Blinky's scatter branch also requires Cruise Elroy offThe C names the counter NONRANDOM_MOVEMENT and the table DIFFICULTY_TABLE; the durations sit in ROM data the repo doesn't include, so the atlas states no times.
Cited by: Enemy targeting, Pac-Man
A frightened ghost picks a random direction from random_2a23, which steps a pointer as and returns the byte at that address, so the game's own first 8 KB of ROM serve as the random table. The direction is the low 2 bits; a reversal or a wall sends it on to try the next one. pac-man/frightened-rng
About: Pac-Man Random number generator
Value
{
"multiplier": 5,
"increment": 1,
"modulus": 8192
}pacman.c#L12227-L12247/* Generates a random number by reading a byte from a randomised ROM address
…
add hl,hl
add hl,hl
add hl,de
inc hl
ld a,h
and #1f
…
RND_VAL_PTR = (RND_VAL_PTR * 5 + 1) & 0x1fff;
return MEM[RND_VAL_PTR];pacman-c: a C translation of the Pac-Man ROM · pacman.c#L11905-L11980XYPOS randomDirection_291e (XYPOS hl, uint8_t *orientation)
…
BEST_ORIENTATION_FOUND = random_2a23() & 3;
…
/* Prohibit ghosts from doing u-turns */
…
/* Ensure this direction is not into a wall */
…
BEST_ORIENTATION_FOUND = (BEST_ORIENTATION_FOUND + 1) & 3;Checked by claude, 2026-10-21 — read 2A23-2A34 (HL×4 + HL + 1, high byte masked to $1F, then `ld a,(hl)`) and 291E-2957; the Z80 map puts ROM at $0000-$3FFF, so addresses below $2000 are program ROMThe repo's comment adds that the movement differs "unless original ROM data is used", since the table is the ROM itself.
Cited by: Pac-Man
Pac-Man awards one extra life when the score reaches the DIP-switch setting: 10,000, 15,000, or 20,000 points, or never. The table holds BCD thousands (10, 15, 20, and $FF for none). After each score change the code compares the score's ten-thousands and thousands digits with the setting, and a flag bit stops a second award. pac-man/bonus-life
About: Pac-Man Extra life
Value
[ 10000, 15000, 20000 ]
pacman.c#L11383BONUS_LIFE = BONUS_LIFE_DATA[DIP_SWITCH_BONUS];pacman-c: a C translation of the Pac-Man ROM · pacman.c#L11419-L11423 — the bytes are the source's listing of the ROM at $2728, kept as comments beside the C/* Bonus life data (in BCD: 10,000, 15,000, 20,000 or none */
…
// 2728 10 15 20 ff 68 00 7d 00pacman-c: a C translation of the Pac-Man ROM · data.h#L35-L36#define BONUS_LIFE_DATA (&ROM[0x2728])
#define DIFFICULTY_DATA ((uint16_t*)(&ROM[0x272c]))pacman-c: a C translation of the Pac-Man ROM · pacman.c#L12359-L12378uint16_t hl = (score[2] << 8) + score[1];
…
hl *= 16;
…
if (hl>>8 > BONUS_LIFE-1)
extraLife_2b33 (&score[2]);pacman-c: a C translation of the Pac-Man ROM · pacman.c#L12637-L12665void extraLife_2b33 (uint8_t *score)
…
if ((score[1] & 1) != 0)
return;
…
score[1] |= 1;
…
P1_REAL_LIVES++;Checked by claude, 2026-10-25 — read 26F9-2704, the data at 2728 (listed as bytes and read through BONUS_LIFE_DATA, data.h L35), the compare at 2A7A-2A83, and 2B33-2B49Cited by: Extra life
Each new level advances a pointer one entry along a difficulty table until the entry reads $14 (20). The DIP switch picks the table: one runs 0, 1, 2, … 20 and reaches 20 on level 21; the other, which the comment calls hard, runs 1, 3, 4, 6, 7, 8, … 17, 20 and reaches 20 on level 16. Each entry selects a 6-byte record at $0796 that sets, among other things, the movement pattern and the ghosts' counters for leaving the house. pac-man/difficulty
About: Pac-Man Difficulty ramp
Value
{
"normal": 21,
"hard": 16
}pacman.c#L273-L282 — the bytes are the source's listing of the ROM at $0068 and $007D, kept as comments beside the C/* Difficulty data. Two sets, easy or hard. Hard ramps up more quickly.
* Each level is more difficult until max difficulty which is 0x14 */
…
// 0068 00 01 02 03 04 05 06 07
// 0070 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14
…
// 007d 01 03 04
// 0080 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 14pacman-c: a C translation of the Pac-Man ROM · pacman.c#L11406DIFFICULTY_PTR = DIFFICULTY_DATA[DIP_SWITCH_DIFFICULTY ^ 1];pacman-c: a C translation of the Pac-Man ROM · pacman.c#L3153-L3172ld hl,#4e13
…
if (ROM[P1_CURR_DIFFICULTY] == 0x14)
return;
…
P1_CURR_DIFFICULTY++;pacman-c: a C translation of the Pac-Man ROM · pacman.c#L2221-L2292void setupGhostTimers_070e (int b)
…
b = ROM[P1_CURR_DIFFICULTY];
…
uint8_t *ix = DATA_0796 + b * 6;
…
setupMovePat_0814(hl);
…
initLeaveHouseCounters_083a(hl);Checked by claude, 2026-10-25 — counted the two tables at 0068 (21 entries) and 007D (16 entries); read 0A90-0A9F, which bumps the level and stops the pointer at $14, and 070E onward, which multiplies the entry by 6 into the record tableCited by: Difficulty ramp
Pac-Man keeps one high score, three BCD bytes at $4E88. After every change to a player's score it compares the score with it from the top byte down and, if the score is higher, copies the 3 bytes over and redraws it. pac-man/high-score
About: Pac-Man High score
Codepacman-c: a C translation of the Pac-Man ROM ·pacman.c#L12389-L12440/* Get Ptr to MSD of high score */
uint8_t *highScore = HIGH_SCORE;
…
if (score[b] < highScore[b])
return;
…
ld de,#4e88
highScore = HIGH_SCORE;
…
memcpy (highScore, score, 3);Checked by claude, 2026-10-21 — read 2A8C-2AAD, the tail of the score-adding routine at 2A5ACited by: High score
The level byte at $4E13 starts at 0 and gains 1 per level, so on level 256 it holds 255. The routine that draws the fruit row loads it and adds 1, and that inc a wraps to 0; 0 passes the "fewer than 8" test, and the draw loop, a djnz counted by that 0, makes 256 passes instead of at most 7. Each pass draws a fruit at HL, writes its color $400 higher, and moves HL on 2 bytes, so from $4004 the loop steps across about 512 bytes of the 1 KB video RAM at $4000 and the matching span of color RAM at $4400. pac-man/level-256
About: Pac-Man Kill screen
Value: 256 fruit-loop passes on level 256
Codepacman-c: a C translation of the Pac-Man ROM ·pacman.c#L2119-L2125xor a
ld (#4e13),a
…
P1_LEVEL = 0;pacman-c: a C translation of the Pac-Man ROM · pacman.c#L3153-L3156ld hl,#4e13
inc (hl)
…
P1_LEVEL++;pacman-c: a C translation of the Pac-Man ROM · pacman.c#L12932-L12947ld a,(#4e13) ; Load level #
inc a ; Increment
cp #08 ; >= 8?
…
ld de,#3b08 ; Fruit table
ld b,apacman-c: a C translation of the Pac-Man ROM · pacman.c#L12955-L12993ld c,#07 ; Fruit count
ld hl,#4004 ; Starting loc
…
call #2b8f ; Draw fruit
…
ld a,#04 ; v
add a,h ; v
…
call #2b80 ; Erase next fruit
…
/* No need to modify hl as we just pass colour hl+= 0x400;*/
drawCharSquare_2b80 (&COLOUR[hl], table[1]);
…pacman-c: a C translation of the Pac-Man ROM · memmap.h#L42-L43uint8_t video[0x400]; // starts at 0x4000
uint8_t colour[0x400]; // starts at 0x4400Checked by claude, 2026-10-21 — traced the kept Z80 at 2BF0-2C17: A = 255 + 1 = 0 (8-bit), `jp nc` not taken since 0 < 8, B = 0, and `djnz` decrements B before testing, so the loop at 2C02 runs 256 times; HL starts at $4004, H gains $04 for the color write at 2C0C and loses it at 2C0F, and HL gains 2 per passThe C translation drops the inc a (it reads int level = P1_LEVEL;), so it wouldn't reproduce this; the claim rests on the kept Z80 lines. How many bytes each fruit draw writes (2B8F, 2B80) isn't read, and what the screen shows on level 256 is not stated here.
Cited by: Kill screen