MoarNES 0.11.12.12 alpha

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
MoarNES 0.11.12.12 alpha
by on (#87386)
as if the world needs another NES emu, this is the current version of my new attempt at a NES emulator. it's still very much an alpha-quality program, but i thought i would upload it because i really wanted to get some opinions/suggestions/comments on it as-is.

features:
-load/save state from/to files
-quick-save slots (four of them F1 through F4 saves, F5 through F8 loads)
-authentic game genie support (GG ROM is included)
-supports mappers 0, 1, 2, 3, 4, 7, 9, 10, 11, and 13 (i think this covers most of the common ones)


there are a good number of problems/bugs in it still, some of the known ones:
-APU sweeps do not work (i wasn't able to get it to sound right)
-mike tyson's punch out background color is incorrect for some reason. menu and versus screens show some graphical glitching (but the actual fights look fine)
-mapper 4 IRQ stuff doesn't work quite right, but lots of the games still play
-audio output is sometimes a little jittery. it's not the APU code, it's an output buffering problem. it's not that bad.
-was having problems with sprite 0 hit intermittently not working, so there's a cheap band-aid hack for now that always flips the sprite 0 hit flag on the last scanline of the sprite without checking if it collides with a background pixel.
-when you load a saved state from file, usually the audio sounds strange but it does fix itself within a few seconds.

win32 binary: http://rubbermallet.org/MoarNES-0.11.12.12.zip
(it runs great in WINE on linux too, i will eventually make native linux bins)

source (VS 2003 solution): http://rubbermallet.org/MoarNES-0.11.12.12-source.zip


in spite of the problems, lots of games play perfectly. i'd love to hear opinions/ideas if you try it. a couple screenshots:

Image

Image

if you decide to give it a shot, thank you! :D

by on (#87390)
I can clearly see that scrolling is emulated completely incorrectly. The proper way to do it is to just use "loopy_t" and "loopy_v", and the rules for updating/incrementing them. Throw out whatever algorithm you are using, and use that instead.

Battletoads is broken (it's not scrolling), NES Chu Chu Rocket shows a gray screen, Bad News Baseball is screwed up, tons of bugs here.
Super Mario Bros 3's status bar screws up when you fly (Nesticle 0.1's readme also described this bug), and stomped enemies are missing sprites. Sounds like CPU flaws for the missing sprites problem, since other flipped sprites work fine.

Sound is at the wrong pitch, sounds like a PAL system, even though the emulator is emulating an NTSC system.

Right now, it feels like the reincarnation of Nesticle, just without the high performance of Nesticle.

by on (#87395)
Dwedit wrote:
I can clearly see that scrolling is emulated completely incorrectly. The proper way to do it is to just use "loopy_t" and "loopy_v", and the rules for updating/incrementing them. Throw out whatever algorithm you are using, and use that instead.

Battletoads is broken (it's not scrolling), NES Chu Chu Rocket shows a gray screen, Bad News Baseball is screwed up, tons of bugs here.
Super Mario Bros 3's status bar screws up when you fly (Nesticle 0.1's readme also described this bug), and stomped enemies are missing sprites. Sounds like CPU flaws for the missing sprites problem, since other flipped sprites work fine.

Sound is at the wrong pitch, sounds like a PAL system, even though the emulator is emulating an NTSC system.

Right now, it feels like the reincarnation of Nesticle, just without the high performance of Nesticle.


thanks, this is exactly why i posted it. i don't think it's a good emu and i'm not fishing for compliments, you guys will be able to point things out that i didn't realize were bad - i'm pretty new to this.

the SMB3 status bar i was aware of, but was not sure why; i'm not able to find that old version of NESticle anywhere to check the readme, either.

by on (#87397)
Dwedit wrote:
(Nesticle 0.1's readme also described this bug)


I thought 0.2 was the initial version. Do you have a copy of 0.1? If so, could you please upload this piece of antiquity?

miker00lz wrote:
i'm not able to find that old version of NESticle anywhere to check the readme, either.


Here's the link to 0.2:
http://web.archive.org/web/200511151120 ... stc020.zip

by on (#87398)
I didn't remember the version number, so I was guessing. Sorry.

by on (#87524)
alright, well.. a couple things, first of all i'm going for pixel-accuracy now. JUST about got it, as you can see from this marble madness screenshot. as you probably know that game needs very precise timing for the nametables to switch mid-scanline to display the text box at the beginning of levels.

Image


i also understand the relationship between the scroll and address registers now, and i'm going to implement the new scrolling method tomorrow. a bit too tired to get to it now.

the too-low audio pitch is as easy fix as well. i will also search for the CPU bug that causes the SMB3 flipped sprites issue. it seems to only be on crushed enemies.

i think if i get all that taken care of i think i will have a fairly respectable emu here. i think the scroll registers/VRAM pointer stuff will also fix the flashing status bar in SMB1.

my other plan, after that stuff, is to add movie record/playable capability including frame-stepping backwards so it can be used to make "tool-assisted" runs.

by on (#87550)
I tested a few games.
Solstice:
-There seems to be random corrupt graphics appearing on the title screen, which change if another game is loaded first. This suggests that memory is not being cleared properly when a game is unloaded.
-The demo desyncs and freezes
-Something seems to be wrong with the physics code since it's possible to jump on spikes without getting hurt, and getting the first key causes the character to fall through the level and die.
Donkey Kong:
It's impossible to jump. I remember having this problem as well in my emulator and there was a specific CPU bug that caused it.
Teenage Mutant Ninja Turtles:
Backgrounds are missing in the intro. The game sets a negative scroll value so that needs to be handled correctly.
Teenage Mutant Ninja Turtles 3:
Won't start - this has something to do with the APU frame counter IRQ firing too early.
Super Mario Bros:
You're absolutely right that the flickery status bar is due to the fake Sprite 0 hit. Another thing that you might not have noticed is that if you hit a coin block in World 1-2 the sound keeps playing forever. It took me months to find out why that was happening myself: if an APU channel is disabled from the status register, the length counter for that channel needs to immediately be forced to zero.

by on (#87577)
Grapeshot wrote:
I tested a few games.
Solstice:
-There seems to be random corrupt graphics appearing on the title screen, which change if another game is loaded first. This suggests that memory is not being cleared properly when a game is unloaded.
-The demo desyncs and freezes
-Something seems to be wrong with the physics code since it's possible to jump on spikes without getting hurt, and getting the first key causes the character to fall through the level and die.
Donkey Kong:
It's impossible to jump. I remember having this problem as well in my emulator and there was a specific CPU bug that caused it.
Teenage Mutant Ninja Turtles:
Backgrounds are missing in the intro. The game sets a negative scroll value so that needs to be handled correctly.
Teenage Mutant Ninja Turtles 3:
Won't start - this has something to do with the APU frame counter IRQ firing too early.
Super Mario Bros:
You're absolutely right that the flickery status bar is due to the fake Sprite 0 hit. Another thing that you might not have noticed is that if you hit a coin block in World 1-2 the sound keeps playing forever. It took me months to find out why that was happening myself: if an APU channel is disabled from the status register, the length counter for that channel needs to immediately be forced to zero.


thanks for giving it a try!

you don't happen to remember what the bug in your CPU code was, do you? i definitely have an issue with it somewhere, but it passes whatever tests i throw at it. if i drop neil bradley's CPU core into my emu, that donkey kong bug as well as the missing flipped sprites on the crushed enemies in SMB3 are both fixed.

the SMB1 flickering status bar wasn't caused by the fake sprite zero hit, it was actually because the game changes the scroll registers occasionally via port $2006. i didn't realize until recently that writes to $2006 also affect the scroll regs. after adding that, my sprite zero hit works correctly without my cheap hack and the status bar is now solid.

that was also causing the scroll problems in battletoads. i'm getting pretty close to that one working correctly now too.

Image

the status bar isn't visible for some reason though, and sometimes the vscroll is about 10 scanlines off or so. it's progress, at least! the intro sequence and the part where it shows the ship and the toad going down to the surface on the rope is all flawless.

to get the vscroll value for the frame, i'm reading it at 256 PPU clocks into the pre-render scanline. i read that this is correct, but maybe i had bad info. do i have it right?

if i can get battletoads and SMB3 working perfectly, i think that i'll have this emu in decent shape and then i can go from there. BT is supposedly one of the most difficult games to emulate properly. :)

by on (#87587)
Battletoads also requires you to emulate the 1 CPU Cycle Penalty for page crossing nnnn,Y instructions, otherwise the screen shakes.
Then for the Snake Pit level, you need to ignore Sprite 0 hits that happen at X=255.

by on (#87594)
Dwedit wrote:
Then for the Snake Pit level, you need to ignore Sprite 0 hits that happen at X=255.


News to me. Is this a game-specific thing?

by on (#87596)
Zepper wrote:
Dwedit wrote:
Then for the Snake Pit level, you need to ignore Sprite 0 hits that happen at X=255.


News to me. Is this a game-specific thing?


i'd guess this is the only game that it matters for, but the real NES doesn't set the sprite zero hit flag on pixel 255.

by on (#87598)
miker00lz wrote:
Zepper wrote:
Dwedit wrote:
Then for the Snake Pit level, you need to ignore Sprite 0 hits that happen at X=255.


News to me. Is this a game-specific thing?


i'd guess this is the only game that it matters for, but the real NES doesn't set the sprite zero hit flag on pixel 255.


Oh my... you're right.
Code:
06.right_edge
-------------
Tests sprite 0 hit with regard to column 255 (ignored) and off right
edge of screen.

by on (#87627)
I checked in my old code (which took some work because I fixed that bug before I had posted any of my code to SVN), and the CPU bug is that the BIT opcode needs to set the negative flag and overflow flag to bits 7 and 6 (respectively) of the value read from memory. For some reason NESTest doesn't test this, but Blargg's individual instruction tests do. You should try running those, even though the messages for failures are less than helpful.

by on (#87629)
Grapeshot wrote:
I checked in my old code (which took some work because I fixed that bug before I had posted any of my code to SVN), and the CPU bug is that the BIT opcode needs to set the negative flag and overflow flag to bits 7 and 6 (respectively) of the value read from memory. For some reason NESTest doesn't test this, but Blargg's individual instruction tests do. You should try running those, even though the messages for failures are less than helpful.


Image AWESOME Image

that you very much for checking, that was my problem as well. i actually did try to implement that behavior when i first wrote the BIT opcode, but i had made a small typo. it looked like this:

Code:
static void bit() {
    value = getvalue();
    result = (uint16_t)a & value;
   
    zerocalc(result);
    status = (status & 0xBF) | (uint8_t)(value & 0xC0);
}


the error is on that last line... (status & 0xBF) should have been (status & 0x3F) SMB3 and donkey kong now work correctly. another problem it caused was SMB2 would just stuck in an endless loop right after selecting your character. that works now too. a few other games that showed grey screens before (one was 3-D World Runner) now run.

does the AND opcode do the same thing, or is it just BIT? i ask because in the x86 instruction set, AND and TEST (which is like the 6502's BIT) both do identical things to the flags except TEST doesn't store the result anywhere.

by on (#87630)
AND doesn't touch V, and it sets N to the value of bit 7 after the calculation, not the value of bit 7 straight from memory.

by on (#87867)
i'm now modifying the nametable and scroll registers on writes to $2006, which fixes some bugs in games but seems to break things in other games that worked properly before, like the cutscenes in ninja gaiden.

Image

they look correct if i dont change the scroll registers on $2006 writes. am i parsing the values correctly here?

Code:
void writePPUregs(uint16_t addr, uint8_t value) {
     PPU->regs[addr & 7] = value;
    lastwritten = value;
     switch (addr) {
       case 0x2000:
            if (value&128) PPU->nmivblank = 1; else PPU->nmivblank = 0;
            if (value&32) PPU->sprsize = 16; else PPU->sprsize = 8;
            if (value&16) PPU->bgtable = 0x1000; else PPU->bgtable = 0x0000;
            if (value&8) PPU->sprtable = 0x1000; else PPU->sprtable = 0x0000;
            if (value&4) PPU->addrinc = 32; else PPU->addrinc = 1;
            PPU->nametable = value&3;
            break;
       case 0x2001:
            if (value&16) PPU->sprvisible = 1; else PPU->sprvisible = 0;
            if (value&8) PPU->bgvisible = 1; else PPU->bgvisible = 0;
            if (value&4) PPU->sprclip = 0; else PPU->sprclip = 1;
            if (value&2) PPU->bgclip = 0; else PPU->bgclip = 1;
            break;
       case 0x2003:
            OAM->addr = value;
            break;
       case 0x2004:
            OAM->RAM[OAM->addr++] = value;
            break;
       case 0x2005:
            if (PPU->addrlatch == 0) {
              PPU->xscroll = value;
              PPU->addrlatch = 1;
            } else {
              PPU->yscroll = value;
              PPU->addrlatch = 0;
            }
         break;
       case 0x2006:
            if (PPU->addrlatch == 0) {
              PPU->r2006[1] = value;
           PPU->nametable = ((value >> 2) & 3);
           PPU->xscroll = (PPU->xscroll & 0x3F) | (((uint16_t)value & 3) << 6);
           PPU->xscroll = (PPU->xscroll & 0xF8) | (((uint16_t)value >> 4) & 7);
              PPU->addrlatch = 1;
            } else {
              PPU->r2006[0] = value;
           PPU->yscroll = (PPU->yscroll & 7) | ((uint16_t)value << 3);
           PPU->xscroll = (PPU->xscroll & 0xC7) | (((uint16_t)value >> 2) & 0x38);
           PPU->addr = ((uint16_t)PPU->r2006[1] << 8) | (uint16_t)PPU->r2006[0];
              PPU->addrlatch = 0;
            }
         break;
       case 0x2007:
            writePPU(PPU->addr, value);
            PPU->addr = (PPU->addr + PPU->addrinc) & 0x3FFF;
            break;
     }
}

by on (#87876)
Aren't you using loopy's logic?

by on (#87942)
I'm not entirely sure how to explain what you're doing wrong here (since you didn't post your rendering code as well), but the most obvious thing is that you are still storing a separate x scroll and y scroll values as well as the PPU memory address. Writes to $2005 and $2006 should both change different bits of the same counter, as described in loopy's famous documentation.

by on (#87945)
yeah, thanks guys. i had read it before, quite a few times but it never totally made sense to me the way explained it. in the last day or so, it finally clicked in my head so i'm working on changing my code right now actually.

i felt a bit dumb for asking about it even though loopy's doc is there.

by on (#87948)
Loopy's document is also formatted badly...

Code:
           Fine Y
              Nametable Y,X
                Coarse Y
                     Coarse X
         
          .yyyNNYYYYYXXXXX
2000 write:
        t:....xx..........=d:......xx
2005 first write:
        t:...........xxxxx=d:xxxxx...
        x=d:.....xxx
2005 second write:
        t:......xxxxx.....=d:xxxxx...
        t:.xxx............=d:.....xxx
2006 first write:
        t:..xxxxxx........=d:..xxxxxx
        t:xx..............=0
2006 second write:
        t:........xxxxxxxx=d:xxxxxxxx
        v=t
scanline start (if background and sprites are enabled):  (ppu clock 257)
        v:.....x.....xxxxx=t:.....x.....xxxxx
frame start (prerender line, ppu clock 304) (if background and sprites are enabled):
        v=t

Others:
PPU clock 251, if screen is on: Y scroll bits of V are incremented.  If it wraps from 239 to 240, toggle the Y nametable, and set Y to 0.  If it wraps from 255 to 0, DO NOT toggle the Y nametable bit.


by on (#87952)
HOLY SHIT i never thought i'd see the day. MoarNES now runs battletoads perfectly now that i got the loopy scrolling stuff figured out correctly.

Image

still can't believe my eyes. thanks for the help, everybody. i will upload a new version of this probably tomorrow if anybody wants to give it a try again. :twisted:

i've only tried the first level of battletoads, so maybe something screws up later. i've only ever gotten to level 3 before, i suck at battletoads. :x

EDIT: just tried levels 2 and 3, they both work perfectly too.

i think battletoads and ninja gaiden 1 are the hardest video games ever created. i can get to the final jaquio battle in ninja gaiden, but i owned the cart when i was a kid. i never played battletoads before about a year ago.

also, i still don't have APU sweeps working. i can't seem to understand quite how that messes with the period values. once i figure that out, and add movie and AVI recording support, i might just have a respectable emulator here.

by on (#87953)
oh, and this wasn't related to the scrolling thing but i've also fixed the PPU-CPU timing so it's accurate enough to do the marble madness mid-scanline nametable switching on the textboxes. it looks absolutely flawless. i'm now accurate down to the pixel. :)

Image

i've only used a small handful of NES emus that actually showed that part of marble madness correctly.

by on (#87954)
If you need help with the APU sweeps, read the code to Blargg's NES APU. It's very easy to follow.

Most relevant portion:
Code:
void Nes_Square::clock_sweep( int negative_adjust )
{
   int sweep = regs [1];
   
   if ( --sweep_delay < 0 )
   {
      reg_written [1] = true;
      
      int period = this->period();
      int shift = sweep & shift_mask;
      if ( shift && (sweep & 0x80) && period >= 8 )
      {
         int offset = period >> shift;
         
         if ( sweep & negate_flag )
            offset = negative_adjust - offset;
         
         if ( period + offset < 0x800 )
         {
            period += offset;
            // rewrite period
            regs [2] = period & 0xff;
            regs [3] = (regs [3] & ~7) | ((period >> 8) & 7);
         }
      }
   }
   
   if ( reg_written [1] ) {
      reg_written [1] = false;
      sweep_delay = (sweep >> 4) & 7;
   }
}


Sweep is clocked about twice each frame.

BTW, the "negative_adjust" parameter is -1 for Square #1, and 0 for Square #2.

Note that part of the sweep feature is always running no matter what, namely the part which silences the channel when adding the shifted period would make the period become out of range.

by on (#88199)
- MMC3 IRQ is broken.
- SMB1 title screen is broken, suggesting improper $2007 read buffering behavior.
- Batman refuses to boot
- Bee 52 crashes the emulator

Those are the only 4 things I tested (MMC3 with Mega Man 3 boss select screen)