SMB corrupted nametable at 0x2400

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

Post Reply
caiiio
Posts: 3
Joined: Wed Feb 01, 2012 2:20 am

SMB corrupted nametable at 0x2400

Post by caiiio » Wed Dec 19, 2012 5:18 pm

Hi All,

I am trying to make SMB work, but I am having difficulties with the background. The nametable at 0x2400 seems to hold incorrect data. Please, take a look at the attached images - from the game start and the demo run. Any suggestions would be helpful.

Thanks!
Attachments
demo run
demo run
game start
game start

Alegend45
Posts: 43
Joined: Thu Mar 29, 2012 6:10 pm

Re: SMB corrupted nametable at 0x2400

Post by Alegend45 » Wed Dec 19, 2012 5:34 pm

Does your emulator fail any CPU tests? List which ones fail.

EDIT: Just thought of something. Do you have correct mirroring for the nametables?

WedNESday
Posts: 1236
Joined: Thu Sep 15, 2005 9:23 am
Location: Berlin, Germany
Contact:

Re: SMB corrupted nametable at 0x2400

Post by WedNESday » Thu Dec 20, 2012 4:01 am

Alegend45 wrote:Do you have correct mirroring for the nametables?
This.

caiiio
Posts: 3
Joined: Wed Feb 01, 2012 2:20 am

Re: SMB corrupted nametable at 0x2400

Post by caiiio » Thu Dec 20, 2012 5:48 am

The CPU seems to be fine. It passes the nestest tests (with cycle precision).

The mirroring logic also seems fine to me. Here is a snippet

Code: Select all

public class NameTableMirroring {
	public enum NameTableIndex {
		A,
		B,
		C,
		D
	}
	
	public enum NameTableMirroringType {
		HORIZONTAL,
		VERTICAL,
		ONE_SCREEN_A,
		ONE_SCREEN_B,
		FOUR_SCREEN
	}
	
	public static int getNameTableOffset(int address) {
		if (0x2000 <= address && address < 0x2400) {
			return address - 0x2000;
		} else if (0x2400 <= address && address < 0x2800) {
			return address - 0x2400;
		} else if (0x2800 <= address && address < 0x2C00) {
			return address - 0x2800;
		} else { 
			assert (0x2C00 <= address && address < 0x3000);
			return address - 0x2C00;
		}
	}
	
	public static NameTableIndex getNameTableIndex(int address, NameTableMirroringType mirroringType) {
		if (mirroringType == NameTableMirroringType.HORIZONTAL) {
			// Horizontal Mirroring: 
			// [0x2000, 0x23FF] -> NTA, [0x2400, 0x27FF] -> NTA 
			// [0x2800, 0x2BFF] -> NTB, [0x2C00, 0x2FFF] -> NTB
			if (0x2000 <= address && address < 0x2400) {
				return NameTableIndex.A;
			} else if (0x2400 <= address && address < 0x2800) {
				return NameTableIndex.A;
			} else if (0x2800 <= address && address < 0x2C00) {
				return NameTableIndex.B;
			} else { 
				assert(0x2C00 <= address && address < 0x3000);
				return NameTableIndex.B;
			}
		} else if (mirroringType == NameTableMirroringType.VERTICAL) { 
			// Vertical Mirroring: 
			// [0x2000, 0x23FF] -> NTA, [0x2400, 0x27FF] -> NTB 
			// [0x2800, 0x2BFF] -> NTA, [0x2C00, 0x2FFF] -> NTB
			if (0x2000 <= address && address < 0x2400) {
				return NameTableIndex.A;
			} else if (0x2400 <= address && address < 0x2800) {
				return NameTableIndex.B;
			} else if (0x2800 <= address && address < 0x2C00) {
				return NameTableIndex.A;
			} else { 
				assert(0x2C00 <= address && address < 0x3000);
				return NameTableIndex.B;
			}
		} else if (mirroringType == NameTableMirroringType.ONE_SCREEN_A) {
			// One Screen Mirroring: All address points to the same data.
			// [0x2000, 0x23FF] -> NTA, [0x2400, 0x27FF] -> NTA 
			// [0x2800, 0x2BFF] -> NTA, [0x2C00, 0x2FFF] -> NTA
			// Enabled by a mapper usually.
			return NameTableIndex.A;
		} else if (mirroringType == NameTableMirroringType.ONE_SCREEN_B) {
			// One Screen Mirroring: All address points to the same data.
			// [0x2000, 0x23FF] -> NTB, [0x2400, 0x27FF] -> NTB 
			// [0x2800, 0x2BFF] -> NTB, [0x2C00, 0x2FFF] -> NTB
			// Enabled by a mapper usually.
			return NameTableIndex.B;
		} else if (mirroringType == NameTableMirroringType.FOUR_SCREEN) {
			// 4 screen Mirroring: Each addresses have their own memory space.
			// Enable by a mapper usually.
			// [0x2000, 0x23FF] -> NTA, [0x2400, 0x27FF] -> NTB 
			// [0x2800, 0x2BFF] -> NTC, [0x2C00, 0x2FFF] -> NTD
			if (0x2000 <= address && address < 0x2400) {
				return NameTableIndex.A;
			} else if (0x2400 <= address && address < 0x2800) {
				return NameTableIndex.B;
			} else if (0x2800 <= address && address < 0x2C00) {
				return NameTableIndex.C;
			} else { 
				assert(0x2C00 <= address && address < 0x3000);
				return NameTableIndex.D;
			}
		}
		
		// Keep compiler silent
		return NameTableIndex.A;
	}
}
ant the actual read and write to the name tables:

Code: Select all

@Override
	public int readNameTable(int address, int[][] extNTRAM) {
		NameTableIndex nameTableIndex = NameTableMirroring.getNameTableIndex(
				address, getNameTableMirroringType());
		int nameTableOffset = NameTableMirroring.getNameTableOffset(address);
		
		return readNameTable(nameTableIndex, nameTableOffset, extNTRAM);
	}

	@Override
	public void writeNameTable(int address, int value, int[][] extNTRAM) {
		NameTableIndex nameTableIndex = NameTableMirroring.getNameTableIndex(
				address, getNameTableMirroringType());
		int nameTableOffset = NameTableMirroring.getNameTableOffset(address);
		
		writeNameTable(nameTableIndex, nameTableOffset, value, extNTRAM);
	}

	protected NameTableMirroringType getNameTableMirroringType() {
		return _cartridgeMirroringType;
	}

	protected int readNameTable(NameTableIndex nameTableIndex,
			int nameTableOffset, int[][] extNTRAM) {
		switch (nameTableIndex) {
			case A: return extNTRAM[0][nameTableOffset];
			case B: return extNTRAM[1][nameTableOffset];
			case C: assert false; break; //TODO
			case D: assert false; break; //TODO
		}

		return 0;
	}

	protected void writeNameTable(NameTableIndex nameTableIndex,
			int nameTableOffset, int value, int[][] extNTRAM) {
		switch (nameTableIndex) {
			case A: extNTRAM[0][nameTableOffset] = value;
			case B: extNTRAM[1][nameTableOffset] = value;
			case C: assert false; break; //TODO
			case D: assert false; break; //TODO
		}
	}

Alegend45
Posts: 43
Joined: Thu Mar 29, 2012 6:10 pm

Re: SMB corrupted nametable at 0x2400

Post by Alegend45 » Thu Dec 20, 2012 6:12 am

That looks relatively OK, but since the most bugs happen in scrolling, check your scrolling logic.

User avatar
Quietust
Posts: 1684
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: SMB corrupted nametable at 0x2400

Post by Quietust » Thu Dec 20, 2012 9:49 am

My first guess would be that you aren't properly delaying CHR/nametable reads through $2007, but that would have resulted in the titlescreen also being corrupted...
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

crudelios
Posts: 24
Joined: Thu Feb 09, 2012 3:54 am

Re: SMB corrupted nametable at 0x2400

Post by crudelios » Thu Dec 20, 2012 11:17 am

You seem to have a problem in your nametable mirrorring code. The game seems to be writing or reading to the same memory zone.

Other than that, I don't really see anything wrong with your code, but I think you really should revise it. You go to China and back just to write/read a single byte! Right now, not only it is very hard to follow, but it will hurt your performance, and if you want some level of accuracy, you will need all the speed you can get...

As an example, your public static int getNameTableOffset(int address) could be rewritten as:

Code: Select all

   public static int getNameTableOffset(int address) {
        return (address & 0x3FF);
   }

caiiio
Posts: 3
Joined: Wed Feb 01, 2012 2:20 am

Re: SMB corrupted nametable at 0x2400 [Solved]

Post by caiiio » Thu Dec 20, 2012 12:45 pm

Code: Select all

protected void writeNameTable(NameTableIndex nameTableIndex,
         int nameTableOffset, int value, int[][] extNTRAM) {
      switch (nameTableIndex) {
         case A: extNTRAM[0][nameTableOffset] = value;
         case B: extNTRAM[1][nameTableOffset] = value;
         case C: assert false; break; //TODO
         case D: assert false; break; //TODO
      }
   }
Missing "break;" in the above routine ate my head (and caused NT overwrite). Damn. Thanks for the help.

Post Reply