Menu

#456 Metroid is broken when launching from command line

closed
nobody
None
5
2012-06-08
2011-10-31
Dan Weiss
No

Whenever I launch Metroid by invoking FCEUX with that rom, it doesn't run at all, just a gray screen. When I open Metroid by dragging the file onto the FCEUX window, or using the Open command, it works fine.

The problem happens whether I launch it from the command line, or from a .NES file association.

I can see that Metroid is doing a lot of strange PPU reads at mirrored addresses (like the entire $2000-$3FFF block) in its boot code. It reads different values depending on whether it was launched from the command line, or opened with the File menu. So you should double check that PPU memory is properly being initialized before the game starts executing.

OS is Windows XP. CRC32 and SHA-1 of the game match Bootgod's database. Problem also happens on the version of Metroid extracted from GBA Metroid Zero Mission.

Discussion

  • zeromus

    zeromus - 2011-10-31

    I was unable to get differences loading by different methods. I am uncertain how that could be making any difference apart from uninitialized memory issues, as you said, which I wasn't able to find any of (and we've scoured the codebase for them many times before). However as of r2333 I fixed a bug in the newppu mode which made metroid fail to boot (potentially depending on the initial CPU conditions, which will vary each time you try it if you use a soft reset). Does this fix your issue? You may monitor your filesystem for activity on fceux.cfg and see if a different fceux configuration is getting used when you launch it different ways.. maybe one of your fceux.cfg was newppu and one was old ppu (which never broke)

     
  • Dan Weiss

    Dan Weiss - 2011-10-31

    About the possibility of it being an incorrect config file:
    I tried using Sysinternals Filemon to see what file accesses were made. It always loads the correct config file, so it's not loading any inconsistent settings.

    I can confirm a sequence of commands that will trash the starting stack value: (it becomes 00 instead of FD)
    Run some game.
    Pause the emulator with the Pause or Frame Advance hotkey.
    Load a ROM. Emulator shows a black screen.
    Open the debugger with the emulator still paused.
    Press "Step Into" in the debugger.
    The game will start with the wrong value in the stack, it will be 00 instead of FD.

    Another way to trigger it:
    Run the game
    Pause the emulator with the Pause or Frame Advance hotkey.
    Do a Hard Reset (Power command), emulator shows a black screen.
    Press "Step Into" in the debugger, emulator has wrong value in the stack.

    If the emulator was paused because the last action was a "Step Into" command, the initial stack value is correct (FD). But if you used the Pause or Frame Advance hotkey, the stack pointer becomes 00 on power up or ROM load. Weird bug.

    By the way, why isn't the PPU RAM/CHR RAM initialized on a hard reset or ROM reload, like the CPU RAM is? It's "random values" from your PC's memory on bootup. (Obviously you don't reset it for that one exercise bike game that uses battery backed CHR-RAM). Once the PPU's power up RAM is initialized to something consistent (even DEADBEEF would be good enough here), I can run a Trace Log and compare the non-working bootup with the working bootup. They differ because they read uninitialized values out of CHR-RAM/PPU RAM, and sometimes differ due to the power on stack bug after resetting a paused emulator.

    I am using the release version of FCEUX 2.1.5.

     
  • Dan Weiss

    Dan Weiss - 2011-10-31

    I can also confirm that Metroid boots correctly on a Visual Studio Debug build from Oct 24 2011, so I think it's due to luck. The initial PPU RAM/CHR RAM is different than what the Release build of 2.1.5 puts in there. You should initialize that stuff to something consistent so games power on consistently, but also leave it "obviously uninitialized" so that homebrew that forgets to clear PPU memory will still show bugs.

     
  • zeromus

    zeromus - 2011-10-31

    First, lets start with one thing. I don't know anything about 2.1.5, it is not exciting to debug old code. I am only testing on the latest code.

    I dont know what PPU ram is, but pal and oam ram are reset in FCEUPPU_Power() which is run whenever a game is loaded or the power command is issued from the NES menu, and it has been like this for a long time.

    chr ram is allocated in mmc1.cpp by FCEU_gmalloc() which zeros out the memory it returns, and it has been like this for a long time.

    i can't see anywhere that fceux sets the stack register to 0xFD on startup. I dont think it does, and I dont think it ever did. You are not supposed to rely on the initial stack register state. Normally the initial stack register will be 0.

    Your difficulty with the unpredictable powerup stack state and the PPU condition both can be explained as follows: when the emulator is paused via frameadvance or pause key, it sits outside the cpu emulation loop. When it is paused as a consequence of stepping, it is frozen within the cpu loop. This kind of sucks. If you poweron a game while stopped here, then the emulator will execute PC=0 instead of the reset vector. This game has a BRK at PC=0. That instruction will be executed and we will have S=0xFD the next time it gets to the debugger escape. If you began execution from outside the main loop, then you will see the stack at its normal state of S=0x00 before the first instruction executes.

    In addition to all of that, you will be in the middle of the PPU timekeeping, since that is what calls into the cpu emulator. This will cause the game to boot up with the PPU on or in a bizarre and unpredictable state.

    The only fix I can see for this is to recover normal executing state correctly when returning from a debugger break with a power-on flag set. In this case, it would mean that you see S=0x00 first in the debugger, and the PPU will be in the same condition every time.

    I'm not sure why more people do not report this bug, I guess its because none of them are trying to debug phantasmal initialization errors (they probably do not exist). But it sure seems annoying to me.

    If you agree with my diagnosis, I will go ahead and try to fix it.

     
  • Dan Weiss

    Dan Weiss - 2011-10-31

    Right now, I've determined that CHR RAM for almost all games is being created by this code in ines.cpp: (line 1405 in October 24 SVN version)
    if((VROM = (uint8 *)FCEU_dmalloc(CHRRAMSize)) == NULL) return 0;
    FCEU_dmalloc doesn't initialize RAM to anything.

    There is code in Mapper1_init that attempts to create CHR-RAM using FCEU_gmalloc, but it is never called.
    In GenMMC1Init, it checks if the CHR size is zero, and creates VRAM using gmalloc. But the code for Mapper1_Init always passes in 256 as the size of CHR. So that code never runs, and thus we have uninitialized CHR-RAM created from ines.cpp.

    Nametable memory is fine, it gets zero-filled like it should.

    As for the initial stack pointer thing:
    The 6502 powers on with the S register at zero. Then when it reads from the reset vector at bootup, it subtracts 3 from the stack pointer, just like a reset. So S should always be FD as it executes the first instruction. Some games don't initialize the stack at all.

     
  • zeromus

    zeromus - 2011-10-31

    OK, youre right about the CHR ram. I just checked in a fix for that. I'll reuse MemoryRand, if you really want DEADBEEF then let me know.

    Regarding the stack, it is correct if it starts at 0 but gets turned to 0xFD before the first instruction executes. What you see in the debugger depends on when the system breaks. If it broke before the first instruction executed, then it would be 0x00, since the IRQ handling will happen immediately before that instruction executes in the CPU loop.

    But thats not where the debugger always breaks. Sometimes it breaks in the middle of the cpu loop--after the IRQ code but before the instruction executes. In those cases you see 0xFD and think it should be working brilliantly, but actually, in those cases, the game has been reset from the middle of the PPU loop and the game is not going to initialize correctly.

     

Log in to post a comment.