While Mega Man 1 to 5 all got some form of PAL versions, though the first 2 were not all that great, see my blog post about patching those, Mega Man 6 was one of the last nes titles ever released, and so it never even got any PAL port.
This did annoy me just enough to now take a look at it and see if maybe I could at least adjust the music to make that acceptable on a PAL console, gameplay wise none of the others were sped up so the slower gameplay really wouldnt be noticable to me anyways.
To demonstrate what happens when just running the game without any adjustment, have a listen to the games intro as intended, in NTSC mode:
And now when running this exactly as is but in PAL mode instead:
As you can hear, both pitch are too low and speed is too slow as well.
First off, I looked at where the game does its pitch adjustment, cause due to slower PAL clocks, all the sounds will sound pitched down when not doing any calculations. For that I just set a breakpoint to the frequency address of the first instrument, which should lead me into the general right direction:
Thats the function responsible for this, and also what you see on the side there, 1A:, is the rom bank of the music driver, this can be seen by just scrolling up and down its code. Now while I did not really see the exact read from where it gets its pitches from, I did notice several references to some table of numbers around the $8960 region which get stored in variables very close to the ones that get used to call this frequency set function and then modified in some ways further, so at that point I actually had the hopes of the mega man 5 music driver being very similar as it released not too much earlier, so I scrolled to this table in mega man 6 in my hex editor:
and just searched for this pattern in the us version of mega man 5 and wouldnt you know it, its in there exactly the same:
Thats a great start! Now all I had to do was search for that bit before the table in the PAL version of mega man 5 and see if its any different, and guess what, it totally is:
So being pretty hopeful I just copied that PAL table right into mega man 6, fired up the game to take a first listen and...!
YEAAAAA, something isnt quite right here is it? The pitch most certainly changed, but this is not at all what I expected.
This clearly means that somewhere in code there actually is some important change, so I fired up both us and pal versions of mega man 5 and started scrolling through their music driver code for a good 10 minutes until I found this one single line near the end:
See how that says ADC #$07? Well, in the pal version this line is actually different!
A difference by 1, that probably explains why all notes were completely off, so I tracked down that line in mega man 6 and replaced the 7 with a 6, and this is how it then sounds:
now THATS what I want to hear I'm really happy that I was able to just stick to original frequency values made by capcom back in the day rather than calculating my own ones here, it sounds really good I find.
Next up it is time to look at music speed, and for this, I did first look into mega man 5 again, and this is their "speed code":
So let me explain whats done here, first off the variable $C0 is used a bunch by the game, which is why first it takes 6 bits from it and stores them into a "temporary" variable of $C3 which is used by the music driver as temporary as well, so good choice there, then they add 0x4 onto $C0 and then AND it with 0xC, this gives them a total of 4 potential states here, those are 0x0, 0x4, 0x8 and 0xC, after doing so they combine the previous value in $C0 with the now newly added value and store it back into $C0, so on the next update call, they can add to that number again, thus keeping track of how often it ran. Now the next bit of code basically checks if the value is 0x0, and if it is, then it calls the update function $8084 as a function, and right below that call obviously is address $8084 as well, meaning it will basically get executed twice in that cycle, the idea of that is to speed up the music by just calling the update function twice every so often.
Now, while this sounds ok, it actually results in music that is 4% too fast! To explain why, basically for every 4 calls here, the music update function gets called 5 times because of that one double call, PAL runs at 50hz, that means every second, this function gets called 62.5 times (50/4*5). The thing is, NTSC runs at 60hz, not 62.5, so thats where that wrong 4% comes from. So, instead of taking the mega man 5 pal code for mega man 6, I instead just copied my code design from castlevania, see that blog post for how that works, all I did is swap out the unused variable, in castlevania, $F0 was unused, in mega man 6, $DE now goes unused, so I replace the jump in the music driver from 806C:
to now go way up into a small unused area at $8A2C:
Then took that blank memory:
and place in my bit of code into that spot:
Translating into this bit of code now being in place of just the music update function:
It works exactly like it does in castlevania too, just $F0 is swapped with $DE.
So, this now ingame sounds like this:
So, the audio now sounds great, but as you can see, the intro visuals are way too slow, also the same goes for the games credits as well, basically this now just involved me looking for variables that count down on frames, seeing where those variables were originally set, and then reducing them. I wont go into more detail than that because there were a LOT of timing variables to adjust overall for intro and credits, I changed out (if I did not miscount) 32 variables to make those sync up properly to the music. As I said earlier, I did nothing about actual gameplay since that also wasnt done in any of the other games and honestly with how brutal megaman is, having everything be a bit slower isnt a bad thing
Another small thing I changed is the initial screen:
to match how it is in mega man 4 and 5:Warning: Spoilers inside!
Which was just done by looking into whats in the video ram at that point:Warning: Spoilers inside!
search for its content in the game in a hex editor:
and swap out those characters as well as remove the first line from being drawn by moving the memory pointer in the game to just point to the second line immediately.
With those music pitch and timing changes, small screen edit and intro and credits adjustment I released that as v1 of my patch because it seemed to play all just fine in fceux which I used for breakpoints and stuff, so I rushed through the game in it with invincibility on cause and all seemed good, and on my real nes I hopped into every stage shortly and it all worked fine, so I thought it was all good...
Well, later that day, I finished the game properly on my real nes, and in one particular room in one stage, things behaved... rather glitchy. Let me show you what I mean:
As you can see, things move around pretty crazy and not at all as it should, this particular effect is done using the scanline interrupt feature of the mmc3 expansion chip that the game uses. Heres just a quick picture showing where those interrupts and setups happen:
The first setup happens during the vsync phase somewhere outside the drawn picture, this just resets the MMC3 interrupt lines and screen position and tells it to trigger next at position 2.Warning: Spoilers inside!
Once position 2 hits, it calculates how far down that "boat" or whatever it is should be in the water, so it changes the currently drawn screen image to just black and also sets the next interrupt position to the start of where the boat should be drawn. Now this is where this first issue happens, how the MMC3 works is it basically has a counter that counts down once a scanline is drawn, and once it hits 0, it will trigger an interrupt and on the next scanline it will load whatever value the game wants to have it triggering next. The game actually has a wait function it uses at positions 2 and 4 once an interrupt hits, so it wont just set the next thing to draw in the middle of the current scanline but right at the edge. The PAL CPU is just a bit slower than the NTSC one though, so this particular wait code in the game actually sets the next interrupt position too late sometimes, meaning the MMC3 already reloaded the previous scanline value again, which causes that heavy flickering on that particular frame, because it draws the boat and water at completely wrong positions. To fix this, all that has to be changed is this wait call at position 2:
$C163 basically just counts down as many cycles as is given by LDX, in this case, thats 4 cycles, so all I did here is reduce this down to the lowest value I found works on hardware:
And for position 4 the wait originally was 3, there I also changed it to 2 and now with those 2 lines slightly altered, this room looks like this on console:
Now thats SO much better
If you were really observant though, you may have noticed that one some few frames, the water was drawn lower than it should've been, this is due to the music driver updates, normally the game updates everything during/after vsync happened, because once the picture settings are in for a frame, they dont change, this room is special though as it has multiple scanline interrupts, so the music update is actually happening between position 3 and 4.
This means, that every so often when the "boat" thingy is really close to the water, the music driver got called twice to get the speed up to ntsc level AND some note was loaded/sound effect played it can happen that the cpu takes longer to update all that, and the interrupt gets handled after the graphics chip already drew those extra lines you see. So really that little bit of glitching left is not something I can easily fix, but honestly I dont think its an issue at all, especially compared to it being not patched at all as you saw before.
So that is it for now, the patch is available on my github as always:
And because I did not show the fixed intro speed earlier, heres the game intro followed by me playing from my console:
With this patch I guess now after 25 years it is finally possible to play all 6 mega man games properly on a pal console, so thats cool
If you made it this far, thanks for reading this yet again pretty long technical blog post.
You need to be logged in to comment