Megaman 2

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Megaman 2
by on (#101765)
Got megaman 2 up and running. Love this game...

https://www.youtube.com/watch?v=ljtxuk9KDCg
Re: Megaman 2
by on (#101766)
Cool. Though the music at the "weapon equip" sequence after the boss fight sounds messed up :P

How do you map the NES RAM and I/O areas to TG16 RAM btw? I'd expect clashes between zeropage RAM, PPU I/O and APU I/O due to the amount of RAM available on the TG16 and how the 6280's paging works. Or do you rely on BRAM or some other kind of expansion RAM (Arcade Card)?
Re: Megaman 2
by on (#101767)
awesome work! :shock:
Re: Megaman 2
by on (#101768)
TG16 lets you remap address space. You can move WRAM, I/O, etc.

Awesome work. What an amazing game to get running on the PCE.
Re: Megaman 2
by on (#101770)
That's awesome! Do you have any documentation on the tool you used for the conversion? Or a description of the process?
Re: Megaman 2
by on (#101771)
mic_ wrote:
Cool. Though the music at the "weapon equip" sequence after the boss fight sounds messed up :P

How do you map the NES RAM and I/O areas to TG16 RAM btw? I'd expect clashes between zeropage RAM, PPU I/O and APU I/O due to the amount of RAM available on the TG16 and how the 6280's paging works. Or do you rely on BRAM or some other kind of expansion RAM (Arcade Card)?


I believe that part of the audio is due to sweep emulation not being right. Heh, I was actually gonna look at your source so see how you handled the audio emulation in your nsf TG16 player.

The game doesn't use any additional ram, so I'm safe with with getting by with the 8k scratch pad ram of hucard setup (though this runs on CD 3.0 too, so I have access to more ram if needed by such NES games). Anyway, I map the 8k ram bank to both MPR0 and MPR1. This takes care of ZP and stack access since it's locked to $2000-21fff logic address range (isn't this what you do with your NSF player on the tg16?). I map the hardware bank ($ff) of the PCE to MPR3 (video ports become $6000,$6002/$6003) since the current NES game has open bus there, but for other games that have expanded ram (like Dragon Warrior which I have running on the PCE CD as well) - I use self modifying code and use the ST0/ST1/ST2 opcodes to write directly to vram and VDC regs without having any hardware bank mapped. The only time I map it in on vblank interrupt call, cause I need to read the status port of the VDC to acknowledge the interrupt. Else, I'm good to go. My backend emulation code (APU/PPU/game/internal) is mapped to MPR2/$4000-$5fff range.

As far as port read and writes, I replace them with JSRs to the backend emulation. I wrote a tool that scans the rom for specific opcodes for those I/O port writes. It only took 4 hours to get Megaman 2 up and running with the existing backend emulation and that including playing and beating the game.
Re: Megaman 2
by on (#101773)
Quote:
TG16 lets you remap address space.

Yes, I'm aware of that ;)
But those three regions all need to be writeable at the same time unless you modify the 6502 code of the NES game, and there's only one physical page of RAM on an unexpanded TG16 regardless of how many logical pages you might've mirrored it to.
Re: Megaman 2
by on (#101774)
Quote:
I believe that part of the audio is due to sweep emulation not being right. Heh, I was actually gonna look at your source so see how you handled the audio emulation in your nsf TG16 player.


I implemented it as it was described on the nesdev wiki. IIRC, something like this (pseudo code):

Code:
wPeriod = [0x4002] & 0x7FF
bShift = [0x4001] & 7
bNeg = [0x4001] & 8
[0x4002] = bNeg ? (wPeriod - (wPeriod>>bShift)) : (wPeriod + (wPeriod>>bShift))


And similarly for square 2. This is updated twice per frame (120 times/second for NTSC).
Re: Megaman 2
by on (#101775)
mic_ wrote:
Quote:
TG16 lets you remap address space.

Yes, I'm aware of that ;)
But those three regions all need to be writeable at the same time unless you modify the 6502 code of the NES game, and there's only one physical page of RAM on an unexpanded TG16 regardless of how many logical pages you might've mirrored it to.


Mapping ram to MPR0 and MPR1 takes care of all ZP and stack related issues (since ZP and stack will go to $2000 range, but since ram is mirrored to $0000 range - regular access to ram is fine as well). If you replace the port read/writes with JSRs (I've yet to see port access that isn't full 16bit address; i.e. lda 2007 or sta $4000,x), the perfect length for a JSR replacement. But yeah, WRAM presents a problem for hucard projects, unless you use the Populous rom layout which gives you an additional 32k of ram after the 4megabit rom address range or you just limit your project to CD (Tailchao made a new hucard mapper for PCE with expand ram and rom, but no emulator supports it yet that I know of).
Re: Megaman 2
by on (#101776)
mic_ wrote:
Quote:
I believe that part of the audio is due to sweep emulation not being right. Heh, I was actually gonna look at your source so see how you handled the audio emulation in your nsf TG16 player.


I implemented it as it was described on the nesdev wiki. IIRC, something like this (pseudo code):

Code:
wPeriod = [0x4002] & 0x7FF
bShift = [0x4001] & 7
bNeg = [0x4001] & 8
[0x4002] = bNeg ? (wPeriod - (wPeriod>>bShift)) : (wPeriod + (wPeriod>>bShift))


And similarly for square 2. This is updated twice per frame (120 times/second for NTSC).


I probably have some $4015 behavior incorrect as well. I need to re-go over my audio emulation code.
Re: Megaman 2
by on (#101784)
tomaitheous wrote:
(Tailchao made a new hucard mapper for PCE with expand ram and rom, but no emulator supports it yet that I know of).

Actually it's partially supported in newer builds of Mednafen. I didn't even find this out until upgrading to 0.9.26 awhile ago, and my builds stopped working.
Reads from any address just return $FF, so if it finds an MCGenjin header in your ROM you're kind of hosed. I think it's just some missing break; statements in ReadTP of mcgenjin.h, but I'll have to test it out later.
Re: Megaman 2
by on (#101799)
:shock:

Damn...
Re: Megaman 2
by on (#101800)
Interesting... Mic_ I didn't know you made your own PCE NSF player too.

I made up one back in 2006 just for fun, but didn't develop it too far because I was working on Tongueman's Logic more earnestly. You can check out how I did the MPR voodoo in the source code.

http://www.chrismcovell.com/data/PCE_NES_Player.zip

Okay, since you posted on your blog about this too, Tomaitheous: Is there any way to eliminate the nasty popping on real PCE hardware when you write new waveforms into WSG RAM? The only thing that doesn't cause popping are waveforms that are 32 bytes of value $10. Even changing the volume registers on the PCE causes popping.
Re: Megaman 2
by on (#101801)
Quote:
Is there any way to eliminate the nasty popping on real PCE hardware when you write new waveforms into WSG RAM?

They don't seem all that noticeable on my TG16. But how about dynamically moving channels around? The TG16 has 6 channels, and the NES only has 4 if you ignore any expansion audio (or 5 if you emulate DMC). So if the duty cycle of Pulse1 or Pulse2 changes, load the waveform that matches the new duty cycle into a currently unused HuC6280 channel (channel 4 or 5), then deactivate the HuC6280 channel currently used and start the new channel. That should at least get rid of any gaps that result from halting the channel while loading the new waveform.
Re: Megaman 2
by on (#101809)
Will the 50% duty cycle square wave get fixed?
Re: Megaman 2
by on (#101829)
Quote:
Will the 50% duty cycle square wave get fixed?

Could you actually hear it or did you look at the apu.asm file? Yeah, I had it at 37.5% instead of 50% for some odd reason. Chris, I'll post later today in reply to you (got run for now).
Re: Megaman 2
by on (#101837)
I clearly heard it when watching the video. I didn't look at any code.
Re: Megaman 2
by on (#101902)
Quote:
I clearly heard it when watching the video. I didn't look at any code.


Good ear :D



I'm completely re-writing the audio emulation code.

ccovell wrote:
Okay, since you posted on your blog about this too, Tomaitheous: Is there any way to eliminate the nasty popping on real PCE hardware when you write new waveforms into WSG RAM? The only thing that doesn't cause popping are waveforms that are 32 bytes of value $10. Even changing the volume registers on the PCE causes popping.


Depends. There are two audible clicks. One is caused by changing of the volume of the channel. The larger the difference, the large the 'pop'. The 'direction' of the pop is directly related to the direction of the volume change, if that makes sense. On the 6280a, this pop (at full volume change) is super tiny and shouldn't be audible unless nothing was playing and all you did was volume changes to channels (and even at that, it's hard to hear). I made a test rom that used a single channel on the 6280a as a 32byte buffer, and using the 7khz timer was able to play 50+khz sample playback without artifacts. But on the original 6280 there was a horrible buzzing sound from the popping of the volume reg. IIRC, clearing both the control bits in $804 also has the same effect as writing 0 to the volume bits. But changing to 10b or 01b or 11b does *not* do this. Thus you can immediately silence a channel by ORing #$c0 with the current volume level without popping artifact (and vice versa). Most 'instruments' start off with a fairly high attack rate in PCE games, so the pop isn't as noticeable since waveform output is accompanying it at the same time.

But changing a waveform mid 'note', gives both a pop related to the volume (assuming non 6280a revision) and a click like artifact because the waveform being updated it almost always out of sync (especially if you're trying to change the timbre over time. Changing the duty cycle of a square waves seem to have the least amount of audible click, for obvious reasons).

So anyway, to get away from the annoying volume pop issue of the original 6280 output - you need a pop in the opposite direction to cancel it out. Thus use two channels. Only one plays at a time, and you always update the alternate channel non playing channel. Turning off one channel cancels out turning on the other channel, though you need to do this part fairly close to each other (even though the volume state of a channel is only updated every 7khz or so). Of course this won't help any for the waveform 'morphing' and such, and is really only practical for outputting samples. But, you can gain back that additional channel for dual channel sample playback by interleaving the sample data in the 32byte buffer (or more, if you want more channels) - but that does have the effect of halving/dividing your sample playback rate.

The SuperGrafx has the 6280a (all of them do AFAIK). And it's been reported that all Core Grafx model 1's have the 6280a as well (makes sense, they were released at the same time). But AFAIK no other models of the PCE/TG/Duo have this chip (not the original Duo nor the later model R/RX). Can't say for the different model handhelds though.
Re: Megaman 2
by on (#101931)
Thanks for the help. I knew about these limitations already... changing the volume makes the channels pop and so does uploading the waveform... Guess there's no instant way of switching waves on a per-channel basis without some artifacts, is there?
Re: Megaman 2
by on (#101933)
ccovell wrote:
Thanks for the help. I knew about these limitations already... changing the volume makes the channels pop and so does uploading the waveform... Guess there's no instant way of switching waves on a per-channel basis without some artifacts, is there?


Yeah, sadly there really isn't. They were so close to having hardware support for Wavetable Synthesis (a limited version of PPG synth), if you could just read the damn waveform pointer position :/

That said, there is a few other things you can do. I did a test rom of hard sync synth effect. I used the TIMER interrupt as the frequency generator for the note, and the channel's period as the 'timbre'. I use a fixed point 16bit counter that when overflows, resets the channel's waveform pointer. The nice thing is that you can do this without any volume related pops. The downside is that 7khz is kind of a low resolution and thus you get a dirty aliasing type of output. But the effect is cool. The resource is closer to that of playing a sample, so... might as well just play samples instead and get a superior sound.

I had a few other ideas of how to make waveform/timbre changing using the 7khz timer and the work, but they have the same aliasing effects because of the low resolution divider (timer interrupt).

You can corrupt the waveform in the channel over time. A few PCE games do this. But the effect is hard to control on a note basis. That is to say, it's hard to get a consistent timbre sounding change for all range of notes. The effect works because you can write to the waveform data port while the channel is playing and it will replace that current sample being pointed to by the waveform playback pointer (it appears to be a shared pointer for writing as well). I did the opposite effect of this and used the channel to add feedback type effects when playing samples at 7khz that would normally be via DDA. I just set the frequency of the channel to something close to the 7khz range.

I did a bloodywolf waveform morph emulator (a test rom) based on what BW does for the 'yeaaahhhh' sound effect. The low frequency of the channel for that effect helps hide some of the artifacts. That and the waveforms are rather 'busy' sounding, so that also helps hide the artifacts.
Here's some examples:
http://www.pcedev.net/audio/bloodywolf/BW_test1.mp3
http://www.pcedev.net/audio/bloodywolf/BW_test2.mp3
http://www.pcedev.net/audio/bloodywolf/BW_test3.mp3

I think waveform updating (on a 60hz counter/divider) would still be decent for sound FX though (artifacts be damned).