Splitting Apart the Split Screen The Journey to Find, Analyze, and Fix Pac Man’s Split Screen



By Don Hodges

Started 10/28/2007

Posted: 12/8/2007

Last Update: 2/19/2017 4/21/2008: This article is featured on Kotaku's home page http://kotaku.com/ 12/10/2007: This article is featured on MAME World's home page http://www.mameworld.net/ In the previous article on Donkey Kong, we saw that the game designers built in a mechanism to prevent the player from ever passing level 99. After level 99 is reached, it repeats forever, never advancing to 100. However, this is not widely known because a timer bug prevents anyone from ever passing level 22. If only the designers of Pac-Man had done something similar. But they didn’t, and Pac-Man suffers from the well known split screen when level 256 is reached. We will examine how and why this occurs, and create a patch to fix the program’s bug. The Problem The first step is to search the Internet to see if this answer has already been found. We find a lot of good work has been done at Mark Longridge's web site [ http://cubeman.org/arcade-source/pacman.asm ] where Pac Man’s code is disassembled and some comments are added. There is also a more complete disassembly of Ms. Pac Man at Scott Lawrence’s web site http://umlautllama.com/projects/pacdocs/mspac/mspac.asm. Ms. Pac Man shares a great deal of code with the original Pac Man, so some of these comments are useful as well.



Now we need to understand the nature of the split screen bug. It occurs on board 256, presumably because the game is trying to draw 256 fruits. Immediately we see from the source code on Mark Longridge's site a comment which mentions drawing the fruit. 2C03 CD8F2B CALL #2B8F ; Draw fruit Using this we discover the subroutine which draws the fruit. It follows here with my comments. You will probably need some knowledge of assembly language to follow this. 2BF0 3A134E LD A,(#4E13) ; Load A with level number 2BF3 3C INC A ; Increase by one 2BF4 FE08 CP #08 ; Is this level < 8 ? 2BF6 D22E2C JP NC,#2C2E ; No, jump to compute different start for fruit table 2BF9 11083B LD DE,#3B08 ; Yes, load DE with address of cherry in fruit table 2BFC 47 LD B,A ; For B = 1 to level number 2BFD 0E07 LD C,#07 ; C is 7 = the total number of locations to draw 2BFF 210440 LD HL,#4004 ; Load HL with the start of video memory 2C02 1A LD A,(DE) ; Load A with value from fruit table 2C03 CD8F2B CALL #2B8F ; Draw fruit subroutine 2C06 3E04 LD A,#04 ; 2C08 84 ADD A,H ; Add 400 to HL 2C09 67 LD H,A ; HL now points to color memory 2C0A 13 INC DE ; DE now points to color code in fruit table 2C0B 1A LD A,(DE) ; Load A with color code from fruit table 2C0C CD802B CALL #2B80 ; Draw color subroutine 2C0F 3EFC LD A,#FC ; 2C11 84 ADD A,H ; Subtract 4 from H 2C12 67 LD H,A ; HL now points back to video memory 2C13 13 INC DE ; Increase pointer to next fruit in table 2C14 23 INC HL ; 2C15 23 INC HL ; Next starting point is 2 bytes higher 2C16 0D DEC C ; Count down how many clears to draw 2C17 10E9 DJNZ #2C02 ; Next B – loop back and draw next fruit 2C19 0D DEC C ; Count down C. Did C just turn negative? 2C1A F8 RET M ; Yes, return to game, we are done 2C1B CD7E2B CALL #2B7E ; No, call subroutine to draw a clear 2C1E 3E04 LD A,#04 ; 2C20 84 ADD A, H ; 2C21 67 LD H,A ; Increase HL by 400 for color value to be cleared 2C22 AF XOR A ; Load A with 0, the code for black color 2C23 CD802B CALL #2B80 ; Draw color subroutine – draws black color 2C26 3EFC LD A,#FC ; 2C28 84 ADD A,H ; Subtract 4 from H 2C29 67 LD H,A ; HL now points back to video memory 2C2A 23 INC HL ; 2C2B 23 INC HL ; Set next starting point to be 2 bytes more 2C2C 18EB JR #2C19 ; Jump back and draw next clear ; Arrive here when the level is 8 or higher 2C2E FE13 CP #13 ; Is the level > #13 (19 decimal, 7th key) ? 2C30 3802 JR C,#2C34 ; If not, skip next step 2C32 3E13 LD A,#13 ; If yes, treat all levels 19 and up as if they are level 19. 2C34 D607 SUB #07 ; Subtract 7 to point to first value to be drawn 2C36 4F LD C,A ; Copy result to C 2C37 0600 LD B,#00 ; Load B with Zero 2C39 21083B LD HL,#3B08 ; Load HL with pointer to start of fruit table 2C3C 09 ADD HL,BC ; 2C3D 09 ADD HL,BC ; Adjust fruit table pointer, based on current level 2C3E EB EX DE,HL ; Load DE to point to fruit in table 2C3F 0607 LD B,#07 ; Load B counter to draw 7 fruit 2C41 C3FD2B JP #2BFD ; Jump back up to fruit drawing section The Fruit Table: Memory Location Fruit Code Color Code Fruit 3B08 90 14 cherry 3B0A 94 0F strawberry 3B0C 98 15 1st peach 3B0E 98 15 2nd peach 3B10 A0 14 1st apple 3B12 A0 14 2nd apple 3B14 A4 17 1st grape 3B16 A4 17 2nd grape 3B18 A8 09 1st galaxian 3B1A A8 09 2nd galaxian 3B1C 9C 16 1st bell 3B1E 9C 16 2nd bell 3B20 AC 16 1st key 3B22 AC 16 2nd key 3B24 AC 16 3rd key 3B26 AC 16 4th key 3B28 AC 16 5th key 3B2A AC 16 6th key 3B2C AC 16 7th key 3B2E AC 16 8th key Here are the main points of fruit drawing: Load level number and increase its count by 1

If the level is 7 or less, set the pointer to the starting fruit to the cherry

If the level is 8 or more, set the fruit pointer to the starting fruit for the current level.

If the level is 19 or above, set the fruit pointer to the 1st key.

Set up two counters. One is for drawing fruit, the other is for possibly drawing blank spaces in cases where there are less than 7 fruit to draw.

Draw the fruit, color the fruit.

Increase the pointers to the next fruit in the table and the next location on screen.

Loop back up to 7 times to draw the rest of the fruit.

Check to see if any blank spaces are needed. If so, then draw the proper number of blank spaces and color them all black.

Return to the program. We need to know that levels on Pac Man are stored with a starting value of zero. In other words, the first level that we play (cherry) is treated as level zero by the game. The second level (strawberry) is counted as level 1 by the game, and so on. The programmers know this and so when the subroutine is called to draw the fruit, it loads the level counter and then increases it by one. Then it figures out how many and which fruit to draw. When level 256 is reached, it is counted by the game as level 255 (this number is #FF in hexadecimal). The subroutine is called to draw the fruit and the value for the level wraps around to zero when it is incremented. No check is done to see if the Carry flag is set, which would have been one way for the programmers to realize the game has reached this point. So with the counter at zero, this causes the subroutine which draws fruit to think that it is a level less than 7 with only 1 to 7 fruit to draw, in addition to some possible blank spaces. The program starts drawing fruit with the counter in register B set to zero, instead of the expected number from 1 to 7. At the end of the loop, B is decreased by one and checked for zero. If it is not zero then the loop runs again, if it is zero then the subroutine ends. On level 256, B is zero to begin with. It is decremented at the end of the loop, which makes it roll back down to 255, causing the loop to run a total of 256 times, which is why the split screen gets drawn on this level. The result is that memory locations which are up to 512 bytes past the start of the fruit table, which are used for some other totally different part of the program, are drawn on the screen at increasing locations in video memory. The subroutine which draws the fruit is expecting only eight distinct values for the various fruit, and is expecting only seven memory locations where the fruit are drawn at the bottom of the screen. The memories which end up getting fed into this subroutine have different values than the ones the subroutine is expecting. There are two main parts to drawing a fruit. The first part draws the fruit, and the second part draws the color. It turns out that each fruit is actually made up of four separate pieces of graphics, all painted with a single color code. Let’s examine the subroutines which draw the fruit and the colors. The following subroutine draws the four parts of a fruit onscreen. It requires that A is loaded with the code for the first part of the fruit and HL is loaded with the memory address of the first position on screen where it is to be drawn. 2B8F E5 PUSH HL ; Save HL 2B90 D5 PUSH DE ; Save DE 2B91 111F00 LD DE,#001F ; this offset is added later for third part of fruit 2B94 77 LD (HL),A ; Draw first part of fruit code into screen memory 2B95 3C INC A ; Point to second part of fruit 2B96 23 INC HL ; Increment screen memory for second part of fruit 2B97 77 LD (HL),A ; Draw second part of fruit code into screen memory 2B98 3C INC A ; Point to third part of fruit 2B99 19 ADD HL,DE ; Add offset for third part of fruit 2B9A 77 LD (HL),A ; Draw third part of fruit code into screen memory 2B9B 3C INC A ; Point to fourth part of fruit 2B9C 23 INC HL ; Increment screen memory for fourth part of fruit 2B9D 77 LD (HL),A ; Draw fourth part of fruit code into screen memory 2B9E D1 POP DE ; Restore DE 2B9F E1 POP HL ; Restore HL 2BA0 C9 RET ; Return The following subroutine draws colors onscreen for a 2x2 grid. It requires that A is loaded with the code for the color and HL is loaded with the memory address of the position on screen where the first color is to be drawn. If a clear value is to be drawn, the first address is called (#2B7E). If A is preloaded with a color, then the second address is called (#2B80). 2B7E 3E40 LD A,#40 ; Used to draw clear value 2B80 E5 PUSH HL ; Save HL 2B81 D5 PUSH DE ; Save DE 2B82 77 LD (HL),A ; Draw color into first part 2B83 23 INC HL ; Set location to second part of fruit 2B84 77 LD (HL),A ; Draw color into second part 2B85 111F00 LD DE,#001F ; Offset is used for third part 2B88 19 ADD HL,DE ; Set location to third part of fruit 2B89 77 LD (HL),A ; Draw color into third part 2B8A 23 INC HL ; Set location to fourth part of fruit 2B8B 77 LD (HL),A ; Draw color into fourth part 2B8C D1 POP DE ; Restore DE 2B8D E1 POP HL ; Restore HL 2B8E C9 RET ; Return The next part of understanding the split screen is to examine the memory locations of the screen elements. Luckily this has already been done by Bart Grantham. http://www.bartgrantham.com/projects/mspacman/sprite-RAM.html The memory grid has been overlayed with the screen to show this:







The color memory map is the same except all address are #0400 higher. The extreme four corners exist in memory but are not visible on the screen. This is important because some of the fruits end up getting drawn into these invisible memories.



The screen memory starts in the lower right corner, and goes right-to-left for the bottom two rows. After the bottom two rows, the screen memory continues near the upper right corner in location #4040 and then goes top-to-bottom down each strip of the playing field, after each strip the next one starts one column to the left of the previous. After the last position on the playing field, near the bottom left corner, the memories then jump to the top right corner and two rows run across the top, right-to-left, much like the bottom two rows.



Using the cherry as an example, observe how the fruit drawing program works. The subroutine draws the first part of the fruit, then increments both pointers which indicate which graphic to draw and where to draw it. The graphic pointer is incremented but in order to draw the third part of the fruit, the location pointer needs to go down one row and then back one, which is accomplished in the subroutine by adding #1F hexadecimal. Then the third part of the fruit is drawn. The fourth part of the fruit is drawn after incrementing both pointers by one.



Examine for the example of drawing a cherry:

Location 4004. Code drawn is #90 = top right of cherry

Location 4004+1 = 4005. Code drawn is #91 = top left of cherry

Location 4004+1+1E = 4024. Code drawn is #92 = bottom right of cherry

Location 4004+1+1E+1 = 4025. Code drawn is #93 = bottom left of cherry After the cherry is drawn, the program calls the subroutine which draws in the color, which is basically the same except it writes to the color memory instead of character memory, and it writes the same color to all four positions instead of increasing the value each time.



That’s it. Now the process is repeated as necessary, and depending on the level, up to seven fruit are drawn at the bottom of the screen.

The Bug in Action

On level 256, due to the bug in the algorithm, the program attempts to draw 256 fruit instead of a regular amount. The routine continues on and starts grabbing data that is past the fruit table in memory and starts drawing the characters in those memories as if they were fruit, into increasing locations in memory with starting values incremented by two each time.



The routine keeps incrementing the location to draw by two. The first thirteen fruit are drawn along the bottom. The thirteenth fruit, (the first key) is the last drawn on the screen without any corruption. The fourteenth fruit, (the 2nd key), is drawn into the very bottom-left corner of video memory which is not visible on the screen (starting with address #401E). Then the next fruit drawn, (the third key), starts at address #4020 which is the bottom right corner, not visible on screen. But the fruit-drawing routine draws four locations: #4020, #4021, #4040 and #4041. So, the top half of this fruit is not seen onscreen. The bottom half is drawn into the top-right location of the playing field, going top-to-bottom instead of going right-to-left. The next fruit that is drawn, (the fourth key), is visible onscreen, split between the bottom right corner for its top half, and the top right side of the playing field for the bottom half. This is repeated for the entire bottom row, including into the very bottom left corner where the top half memories are not visible onscreen (#403E and #403F), but the bottom half is drawn to the bottom two places in the first column of the playfield (#405E and #045F). Also during this bottom row, after the 8th key is drawn, (the 5th visible being split at the bottom), the fruit table ends and the program starts drawing fruit using values from the memories that come after the fruit table. These values cause the subroutine to draw seemingly random fruit and colors. For example, the values of the two memory locations which follow final key in the fruit table are #73 and #20. So the program tries to draw fruit #73 with color #20 into the next memory locations. The subroutine draws character values #73-#77, which are not visible characters, so it just draws blanks on the screen. The next two values in memory are #00 and #0C. The subroutine draws a “fruit” with these values by drawing the numbers 0, 1, 2, and 3 into these memories onscreen. However, the color subroutine draws color #0C which is black, so these values are erased from view almost instantly after they are drawn. Looking at the last fruit to be split between the bottom of the screen and the right side, we observe the behavior of the 9th fruit after the end of the fruit table. The first value that is drawn is #06 which draws the number 6, 7, 8, and 9 into memory, as seen. Then the color subroutine blanks them all out with a black color. Looking at the next fruit, we see it is split between invisible memories in the bottom left of the screen and the right side. The memory contains #04 which makes the subroutine draws the number 4, 5, 6, and 7 into memory, as seen. After they are drawn, the color subroutine blanks them all out with a black color. After the end of the bottom row, the starting addresses of the fruit shift from #403E, to #4040 which is the top right corner of the playfield. The 11th location after the fruit table is drawn into the top right corner of the playfield, and we can see that since the memories here are arranged differently than at the bottom, the fruits are drawn going down 2 columns instead of across 2 rows. Then the program draws the corrupted fruit values down each column of memory, overwriting the values that it had written on the previous pass for the bottom half of the fruit, with new values for the top half of each fruit as well as its own bottom half in the next column over. These bottom half values keep getting replaced by the next column of fruits, except in the final column when the subroutine ends.



It turns out that some of the memories contain codes that will draw dots onto the screen that Pac Man can eat. If the memory location has a value of #0F or #10, an edible dot will be drawn to the screen. There are several locations in this range that meet this condition, and so several of the characters that are drawn in the split screen are edible dots. If #13 or #14 had existed in any of these memories, an energizer would have been drawn, but these values do not appear in this memory range.



The last memory location drawn is at location #4202, which is #200 more than the location of the first cherry (#4002). It is #200 because the program loops #100 (256 decimal) times, incremented by two each time.



In addition, after the all of these corrupted fruit values are drawn, the program thinks that it has drawn no fruit and proceeds to draw seven blank spaces. This is because the counter that starts at seven and is counted down each time a fruit is drawn, wraps around itself and returns to seven after the 256th fruit is drawn. So after the last fruit, the program draws seven blank spaces and colors them black. This occurs for memory locations #4204 through #4210, the last of which clear off the left side of the center portion of the ghost’s home box. We can see more clearly the data written to the screen by taking a screen shot of the level being cleared by the rack skip dip switch setting. This un-hides most of the data written to the screen that might otherwise be hidden by being colored black.



Finally the subroutine ends and the program continues “normally”.



The game then draws the player’s remaining Pac Men in the lower left of the screen. This process overwrites the top half of the bells, galaxians, and one of the limes that were drawn in those locations, as well as any graphics that were drawn into their bottom halves.



Then the game returns, and Pac Man is doomed because this level is un-finishable. There are not enough dots on the screen to eat to finish the level, even though the game re-draws the split screen each time the player dies, which means the dots in the right hand side reappear and can be eaten again. If the memories contained enough values of #0F or #10, which draw dots to the screen, it is conceivable that this level could have been passable. THE FIX The next step is to fix the code so that this behavior does not occur on level 256.



A close look at the disassembly of Ms. Pac Man at Scott Lawrence’s web site( http://umlautllama.com/projects/pacdocs/mspac/mspac.asm ) shows a “fix” that has been written by Mark Spaeth: HACK8: Mark Spaeth's "20 byte" level 255 Pac-Man rom fix ; level 255 pac fix ; HACK8 ;0A90 C3800F JP #0F88 ;0A93 00 NOP … ;0F88 3A134E LD A,(#4E13) ; board number ;0F8B 3C INC A ;0F8C FEFF CP #FF ;0F8E 2803 JR Z,#0F93 ; don't store level if == 255 ;0F90 32134E LD (#4E13),A ; store new board number ;0F93 C3940A JP #0A94 ; jump back What does this fix do? It overwrites the program that increases the game’s level with a jump command (called a ‘hook’) that takes it out to a new subroutine that has been written and placed in some unused memory. This subroutine really has nothing directly to do with the fruit drawing. It checks the value of the level right after it has been increased after a level is completed. If an overflow occurs, the result is not written back to memory. Then it jumps back to the original program. The result is the game stays forever on game level #FE (the 255th screen) and never goes any higher. Another hack which bypasses the self-test must also be employed with this one, or else the checksum routines that are run when the game powers on will detect a problem and refuse to run the game.



Not being satisfied with Mark Spaeth’s “fix”, we devise one that actually corrects the flawed logic in the fruit drawing subroutine. To be elegant, we will fix the code within the confines of the original code space. Here is the fix. There may also be other ways to implement this.



Here are the first few lines of the original fruit drawing subroutine 2BF0 3A134E LD A,(#4E13) ; Load A with level number 2BF3 3C INC A ; Increase by one 2BF4 FE08 CP #08 ; Is this level < 8 ? 2BF6 D22E2C JP NC,#2C2E ; No, jump to compute different start for fruit table Part of the fix will change the code to read this way: 2BF0 3A134E LD A,(#4E13) ; Load A with level number 2BF3 FE07 CP #07 ; Is this level < 7 ? 2BF5 D22E2C JP NC,#2C2E ; No, jump to compute different start for fruit table 2BF8 3C INC A ; Increase by one So instead of increasing the value by 1 and then checking against 8, we check against 7 and increase only if the original value is less than 7. Now we have to modify the code that starts at #2C2E if the level is greater than 7.



The original code reads 2C2E FE13 CP #13 ; Is the level > #13 (19 decimal, 7th key) ? 2C30 3802 JR C,#2C34 ; No, skip next step 2C32 3E13 LD A,#13 ; Yes, load A with #13 2C34 D607 SUB #07 ; Subtract 7 to point to first value to be drawn We modify this to read: 2C2E FE12 CP #12 ; Is the level > #12 (18 decimal, 7th key) ? 2C30 3802 JR C,#2C34 ; No, skip next step 2C32 3E12 LD A,#12 ; Yes, load A with #12 2C34 D606 SUB #06 ; Subtract 6 to point to first value to be drawn We arrive at this location with A being one less than the program was expecting, because we did not increment it before coming here. So we check against #12 instead of #13, force to #12 instead of #13, and subtract 6 instead of 7 to make up for it. Everything else can remain the same and the bug is fixed.



The last thing that needs to be done is to fix the power-on checksum routine so that this change can be made without causing a memory error. Instead of creating or using another hack to bypass the self-tests, we will add offsets to the end of this memory bank to make the checksums add up properly.



The checksum routines in this game first add up all the even bytes in a memory bank and check against #00, and then repeat this for the odd numbered bytes in the memory bank. We do some quick math and discover that the new program’s even bytes add up to #18B less checksum bytes than the program it replaces, and the new program’s odd bytes add up to #187 more checksum bytes than the program it replaces. To fool the program’s checksum we therefore add #8B to an unused even memory location and #79 to an unused odd memory location. Before: 2FFC: 00 2FFD: 00 After: 2FFC: 8B 2FFD: 79 The total patch is made by changing just nine bytes of the original code, plus two bytes for the checksum fix. This patch can be written into Puckman romset file named [namcopac.6h] into three areas. For continuity, this is padded with four bytes of the original code, making it 15 bytes instead of the expected 11 bytes. 0BF3: FE 07 D2 2E 2C 3C 0C2F: 12 38 02 3E 12 D6 06 0FFC: 8B 79 MAME will complain that the ROM set is bad, because it uses more complicated hashing algorithms to detect errors, but it will still work.



Alternately we can use a MAME cheat code and run it when needed: :puckman:20700001:2BF3:FE07D22E:FFFFFFFF:No Split Screen :puckman:20510001:2BF7:00002C3C:0000FFFF:No Split Screen (2/5) :puckman:20010001:2C2F:00000012:000000FF:No Split Screen (3/5) :puckman:20610001:2C33:0012D606:00FFFFFF:No Split Screen (4/5) :puckman:20510001:2FFC:00008B79:0000FFFF:No Split Screen (5/5) Learn more about cheat codes in MAME at http://cheat.retrogames.com



This cheat will also work for all of the Pac Man ROM sets in MAME, as they all suffer from the split-screen bug. The exception is the bootleg that runs on Galaxian hardware (pacmanbl) which does not suffer from this bug to begin with.



After being patched, the game no longer tries to draw 256 fruit when reaching level 256. It draws seven keys and the level can be finished, at which point the game wraps around back to level 0 (cherry), and will show its intermissions again at the same places. However, the game does not become easy again but stays on the same difficulty level where the energizers have no effect. The player may continue to use the previous 9th key patterns for the rest of the game. Thoughts & Comments It is probably a good thing that Pac Man has this bug in its program. If it didn’t, expert players could conceivably be able to play the game indefinitely, because whenever they get tired they can just park Pac Man in the hiding places and leave the game to go eat, sleep, or whatever, and then return to the game and continue playing. The only limiting factor would have been the length of time that the game could run without a power outage or suffering from some other hardware failure. Experts would have been able to play the game for weeks, months, even years, or more.

