Hi all, some questions here.. I've been out of Nesdev boards for quite a long time, so some of these questions may have already been answered before. If so, just point me out to the corresponding threads. Lots of thanks in advance.
1.- I've added to my emu that sprite stuff discovered by Quietust, reads to PPU reg 3 and such, and this has made disappear almost all black lines in Micromachines. Not all of them, cause my emu still renders a 3 or 4 pixel black line on "Select Game" screen, and a thin 1 pixel one on the screen preceeding every race. The rest of the game is rendered correctly. Should I change something else apart from adding that "if" statement on SPR reads? Am I missing something?
2.- On my emu, "Multidirectional scrolling test" status bar shakes up and down a little while the main screen scrolls, and gets filled with some random tiles. Any idea of what could be causing that? Could this "missing feature" make any other programs not to work? Guardian Legend status bar flikers a lot after playing it for a while, but all the other comercial scrollers I've tested so far seem to work properly, apart from those which rely on IRQ interrupts emulation to run (Amagon slows down while getting in game, Antartic Adventure doesn't boot, Bomberman hangs on title screen, Archong hangs while getting in game, etc..).
3.- Does emulating PPU bus states/cycling make any difference with any game/demo?
4.- What would be a good starting point to learn how to emulate the APU, knowing that I've never done any sound programming before? I know the digital sound basics, I just dunno how to turn them in code..
As always, thanks for your atention.
1. Micro Machines (and some other Camerica games, like Bee 52) do some visual tricks with the background color. They turn off BG and sprites, then fiddle with the palette so that the color that gets drawn changes every scanline or so. If you're getting black lines, it's probably because you're not rendering the right BG color when bg/sprites are switched off.
I'm not 100% sure on the color you're supposed to use for the BG when redering is off (couldn't get a solid answer), but it's based on the last palette entry accessed by the software. What I do (not 100% sure on this, would love confirmation), is I check the PPU address after $2006 and $2007 writes, as well as $2007 reads (whenever the PPU Address changes). If the PPU address is pointing to palette space ($3F00-$3F1F or any mirrored area of that), I set a BG color var to the PPU Address AND $0C:
Code:
if((nPPUAddr & 0x3F00) == 0x3F00)
nBGColor = nPPUAddr & 0x0C;
Then nBGColor is the palette entry (00, 04, 08, or 0C) I use for the BG color when BG/Sprites are switched off. When
either BG or sprites are flipped on, I use palette entry $00 regardless of nBGColor.
I'm still not 100% sure if this is right -- I would love confirmation. Doing it this way seems to have gotten it working. Below are pics of Micro Machines and Bee52 as they run in my emu. See the the fancy color bar beneath the "Micro Machines Challenge" logo -- if you zoom in you can see it's a different color every scanline. Bee52 has a less fancy border at the top of its status bar, but it's done with the same effect.
http://www.geocities.com/disch_/palettepics.png
2. Don't know about your scrolling problem, but the Guardian Legend uses DMC IRQs somehow to keep its screen from shaking like that. If you're having problems it's likely because you're not doing DMC IRQs properly. Bee52, FireHawk, and Mig-29 Soviet Fighters are other games to test for this, since they all rely somehwat heavily on DMC IRQs (especially Mig-29!)
4. BT and Blargg's docs are both very very good. When I first got started I must have re-read BT's doc like 10 times before I really understood it -- after that I just messed around until I got things working. Getting sound playing is the first step -- if I were you I woudn't worry about quality downsampling methods and just worry about getting sound playing so that you grasp the concept. Once you understand it a little better you can go back and write better sound code (you'll probably have to rewrite most of it -- but it's worth it. It's important to have a learning project or two before having your real deal.)
If there's more specific questiosn about sound generating you can post them. Personally I love talking about NES sound so I'll want to help out as much as I can. I'm sure blargg and others will step in and help as well =).
1.- Yey, that solved the problem. Thank you very much for you explanation.
2.- Well, actually I'm not handling DMC IRQs, so I guess that's the main reason (I'm sure it is on 3 games I mentioned before). Anyway, that "multidirectional scrolling" demo does not use IRQ AFAIK, so.. no idea (if anyone could confirm this one, I'd be grateful). Anyway, I'm not too worried about this, it does not seem to affect any other program.
4.- And thanks for this one. I'll have a look at BT and Blargg's docs after doing one or two sound miniprojects, so I can understand what I do. I surely post lots of questions about this topic in a few days, heh..
My advice for sound programming is to go slowly, be sure you understand what's happening before you move on, and have a sound editor to examine the output of your code (don't try to simply listen to debug it).
Thanks for your advice, blargg, I'll make (and wanna be) sure to understand every step I do before moving to the next one.
By the way, I've managed to solve (almost) the problem in point 2, it was just a problem of updating scroll regs at the proper time. The only thing I'm missing here (I hope) is the little delay between when a NMI is signaled and the time when it is actually served. I read somewhere that it's just a few PPU cycles, anybody can tell me for sure how many?
Thanks again.
Muchaserres wrote:
The only thing I'm missing here (I hope) is the little delay between when a NMI is signaled and the time when it is actually served. I read somewhere that it's just a few PPU cycles, anybody can tell me for sure how many?
An interrupt will get serviced right after the CPU finishes its current instruction or the one after that depending on the time of occurrence and how long it takes for the CPU to notice the falling input. Too late would be somewhere around the last one or two cycles in an instruction.
Wow, impressive! Thank you all, now everything works perfectly. I'm just having one problem with MMC1, every game I've tried works fine except Romancia (J). All tiles are messed up. Is it using any MMC1 special feature that any other game uses or something?
And this one.. heh, any ideas on what is that?
Thx in advance.
Muchaserres wrote:
And this one.. heh, any ideas on what is that?
I'm not quite sure; I feel asleep.
Muchaserres wrote:
And this one.. heh, any ideas on what is that?
Thx in advance.
Just some
wacko obviously..
Marty wrote:
Muchaserres wrote:
And this one.. heh, any ideas on what is that?
Thx in advance.
Just some
wacko obviously..
HAhahahahh! Cool stuff! xDD
Is that in the real game on a real NES, or is it a hack or emulator bug? I all of a sudden want to play Zelda 2, even though I have never done so to begin with (it isn't true to the Zelda series IMO).
yes Error is a real guy in the real game. First time you meet him he says "I am Error" but in another town later in the game another person says "Error knows something or other" and if you talk to him again he gives you new info (although it's not really very important info -- it's been a while since I actually triggered those events).
It shows up on the real hardware. It could have been a mistranslation, or did I see a 90 minute documentary on TCM last night about
Error Flynn?
Or "Error" is just the name of the guy, or he would say something like "I'm a bug of the matrix".
Hi all,
Some more questions.. While adding some new mappers to my emu, I've encountered a problem with my write handler. Some mappers (Nina-1 #34, MMC5 #05, FFE F4xxx #06,...) write to their registers on addresses lower than 0x8000. So now, instead of having a "if(address>=0x8000){writetomapper(address,byte);}" I have to "writetomapper(address,byte);" every time a write occurs. My question here is: how are you dealing with this problem? or, is it there a better/faster/smarter way of doing this?
On the other hand, I'm still having problems with Romancia_(J). It shows garbage on title screen.. but I'm almost sure that my MMC1 implementation is oK. Suggestions? Also still having trouble with Bee_52_(U).. may it be missing IRQ handling?
Thanks in advance.
Muchaserres wrote:
So now, instead of having a "if(address>=0x8000){writetomapper(address,byte);}" I have to "writetomapper(address,byte);" every time a write occurs. My question here is: how are you dealing with this problem? or, is it there a better/faster/smarter way of doing this?
This is pretty much the same way I handle it, except instead of having a 'writetomapper(addr,val)' I have 16 'writeToBank()' function
pointers, one for each of $x000-$xFFF (and another set for reads). By default, all of them are set up for system RAM (0000-1FFF), PPU I/O (2000-3FFF), APU I/O (4000-4FFF, though the upper space just attempts to read/write from ROM), and ROM for the rest; when each mapper loads, it simply overrides the default I/O handlers to use its own behavior when reading/writing certain addresses (keeping track of the previous handler for registers at $4xxx so sound/controllers still work).
Muchaserres wrote:
On the other hand, I'm still having problems with Romancia_(J). It shows garbage on title screen.. but I'm almost sure that my MMC1 implementation is oK. Suggestions?
Do you support CHR RAM bankswitching? If not, this would explain why you are having problems - the upper half of the screen swaps in the first 4KB CHR RAM bank at PPU $1000-$1FFF, then switches in the second bank to display the picture at the bottom.
Muchaserres wrote:
Also still having trouble with Bee_52_(U).. may it be missing IRQ handling?
Bee 52's mapper does not use IRQs; rather, the game uses DPCM IRQs as a scanline timer (Fire Hawk also does this) and requires very precise emulation to get it to display just right.
Quietust wrote:
This is pretty much the same way I handle it, except instead of having a 'writetomapper(addr,val)' I have 16 'writeToBank()' function pointers, one for each of $x000-$xFFF (and another set for reads). By default, all of them are set up for system RAM (0000-1FFF), PPU I/O (2000-3FFF), APU I/O (4000-4FFF, though the upper space just attempts to read/write from ROM), and ROM for the rest; when each mapper loads, it simply overrides the default I/O handlers to use its own behavior when reading/writing certain addresses (keeping track of the previous handler for registers at $4xxx so sound/controllers still work).
Neat!
Quietust wrote:
Do you support CHR RAM bankswitching? If not, this would explain why you are having problems - the upper half of the screen swaps in the first 4KB CHR RAM bank at PPU $1000-$1FFF, then switches in the second bank to display the picture at the bottom.
I do so with CHR ROM. I guess the problem is that I'm not doing it when there's no CHR banks. I'll check that.
Quietust wrote:
Bee 52's mapper does not use IRQs; rather, the game uses DPCM IRQs as a scanline timer (Fire Hawk also does this) and requires very precise emulation to get it to display just right.
OK, I'm not emulating the APU yet, so this must be the problem.
Thanks for the quick answer!
The titlescreen in Romancia looks fine here, but it's got problems in-game: the statusbar is garbaged, and scrolls along with the playfield.
Hi all,
So, having some free time this days, I've decided to start implementing the APU into my emu. The first part of it which I am dealing with is DMC, as implementing it will increase my emu's compatibility (cause of IRQ/DMA's, you know). But I'm quite far from understanding how this unit works. I've read several APU docs, but I'm quite lost.. So, is it there any "let's learn how it works step by step" doc, or THE doc, which I could use to get an overall idea about how thinks work?
Thx.
APU emulation without actual sound output is not hard to get running. I think the wiki (
http://nesdev.com/wiki/?page=nesdevWiki )covers it well, plus blargg's new information a few threads back (thanks blargg).
The problem historic hap above you is blabbing about will be fixed with DMC implementation.
OK, so I'll read those sources carefully.
Just one question: as with the PPU, is it there any list of the APU operation on each of its cycles? I mean, how does the APU know what it has to do on each (let's say) CPU cycle?
Thanks.
The closest to this is the frame counter, which times note durations and the volume fading. The APU is basically a collection of many independent units, each generally running all the time, so there isn't a master table of operation. This should be expected since each sound channel must be separate from the others. But even within a sound channel, each sub-unit generally runs at its own rate. Sound is controlled by switching the connections between units on and off, and changing the input parameters. In hardware it's usually easier to simply ignore the output of something than switch it off, so things run all the time.
So, what should be done to keep everything synchronized? I mean, the simplest NES emulator core would run the CPU for some cycles, then the PPU for the equivalent cycles, and then the APU. Should "RunAPUCycles()" just increment/decrement counters? And, if so, how many units per CPU cycle?
Thx again.
The APU only needs to be run when its side-effects matter. The simplest implementation is to run the APU up to the current CPU time whenever the CPU reads or writes to an APU register, when your emulator needs more samples, and when the APU takes a special action on its own.
The special actions the APU carries out are the frame IRQ, DMC IRQ, and DMC wait-states when reading a sample byte. These are somewhat complex to implement properly. They can be efficiently handled by calculating the timestamp of the next APU-generated event, and avoiding running the CPU beyond this. This timestamp might change after an APU write or after one of these events occurs.
I've found it simplest to use a common timebase that is periodically re-zeroed (to prevent overflow). This could be done at the beginning of vertical blanking each frame. In my APU the read and write functions take the timestamp. The APU keeps track of the last time it was run to, allowing it to determine how many clocks it needs to run to catch up to present. It's better to keep this calculation in one place in the APU rather than make the calling code keep track of it.
Within the APU, functions take a count of the number of clocks to run for. They generally contain a loop that emulates the various dividers in the APU. When a divider reloads, the event is emulated. This might clock another unit, usually handled with a clock_x() function.
When I first wrote an APU emulator, I implemented only the frequency of the first square channel. No volume, frame counter, nothing else. It was enjoyable to hear the basic tune once it worked.
In order to do things like that, I'm now modifying my emu so it uses a master timer, as Disch (?) proposed some time ago. My question here is: have any of you had any major problems when recoding your emus that way? Or, what should be changed in the way the emu works? I mean, mine is actually cycle-accurate an runs every game (almost) perfectly, but when rewritting its parts to fit the new scheme (using ideas from other posts), all games get glitchy. I may be missing something. I'm actually multiplying PPU cycles by 5 and CPU cycles by 15, I hope I'm right at least with that.