deltabeard / Peanut-GB

A Game Boy (DMG) emulator single header library written in C99. Performance is prioritised over accuracy.

Home Page:https://projects.deltabeard.com/peanutgb/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

super mario rendering bug when running and small other

ESPboy-edu opened this issue · comments

greatly improved perfomance as i see in comparition with the version 16 month before :)
thanks for your great work!

but

  1. there is a rendering bug in supermario (many other games are ok and 16 month old peanutGB renders fine but slower).
    PEANUT_GB_HIGH_LCD_ACCURACY 0 or 1 - does not matter.
    static picture is ok, but when running to the right it renders very strange things on the level

  2. prince of persia does not starts the game. only renders initial screen with "virgin games" logo and freezes

here is a one "]" is missing at the end

			if(gb->hram_io[IO_LY +
					(gb->hram_io[IO_LCDC] & LCDC_OBJ_SIZE ? 0 : 8) >= OY ||
					gb->hram_io[IO_LY] + 16 < OY)

here is the array is "uint8_t" but uses negative numbers (Arduino ESP8266 compiler does not allow this), I changed to int8_t

	const uint8_t cart_mbc[] =
	{
		0, 1, 1, 1, -1, 2, 2, -1, 0, 0, -1, 0, 0, 0, -1, 3,
		3, 3, 3, 3, -1, -1, -1, -1, -1, 5, 5, 5, 5, 5, 5, -1
	};

here is compiler says (can not convert "void*" to "sprite_data *")

const struct sprite_data *sd1 = in1, *sd2 = in2;

this fixes the problem

const struct sprite_data *sd1 = (sprite_data *)in1, *sd2 = (sprite_data *)in2;

thanks for the great GB kernel and passion to the old school :)

i try trace into this, SCX is always at 0 and mario constantly set it to 0

game construct background map in not visible area as mario advance but scx still at 0 and so background still show extreme left of the map

SCX is writed here (_gb_write on 0xFF43) :

		case 0x43:
				gb->hram_io[IO_SCX] = val;
			return;

in mario each change on SCX are currently called by the 0xE0 opcode :

	case 0xE0: /* LD (0xFF00+imm), A */
		__gb_write(gb, 0xFF00 | __gb_read(gb, gb->cpu_reg.pc.reg++),
			   gb->cpu_reg.a);
		break;

if i take gnuboy, here is 0xE0 :

case 0xE0: /* LDH (imm),A /
        writehi(FETCH, A); break;

:

void writehi(int a, byte b){
	writeb(a | 0xff00, b);
}

#define FETCH (readb(PC++))

byte readb(int a)
{
    bytep = mbc.rmap[a>>12];
    if (p) return p[a];
    else return mem_read(a);
}

:

struct mbc {
...
	byte *rmap[0x10], *wmap[0x10];
};

and rmap/wmap are generated here :

/*
 * In order to make reads and writes efficient, we keep tables
 * (indexed by the high nibble of the address) specifying which
 * regions can be read/written without a function call. For such
 * ranges, the pointer in the map table points to the base of the
 * region in host system memory. For ranges that require special
 * processing, the pointer is NULL.
 *
 * mem_updatemap is called whenever bank changes or other operations
 * make the old maps potentially invalid.
 */

void mem_updatemap()
{	int n;
	byte **map;

	map = mbc.rmap;
	map[0x0] = rom.bank[0];
	map[0x1] = rom.bank[0];
	map[0x2] = rom.bank[0];
	map[0x3] = rom.bank[0];
	if (mbc.rombank < mbc.romsize)
	{	map[0x4] = rom.bank[mbc.rombank] - 0x4000;
		map[0x5] = rom.bank[mbc.rombank] - 0x4000;
		map[0x6] = rom.bank[mbc.rombank] - 0x4000;
		map[0x7] = rom.bank[mbc.rombank] - 0x4000;
	}
	else map[0x4] = map[0x5] = map[0x6] = map[0x7] = NULL;
	if (0 && (R_STAT & 0x03) == 0x03)
	{
		map[0x8] = NULL;
		map[0x9] = NULL;
	}
	else
	{
/*		map[0x8] = lcd.vbank[R_VBK & 1] - 0x8000;
		map[0x9] = lcd.vbank[R_VBK & 1] - 0x8000;
*/
	// dmg only
	map[0x8] = lcd.vbank - 0x8000;
	map[0x9] = lcd.vbank - 0x8000;
	}
	if (mbc.enableram && !(rtc.sel&8))
	{
		map[0xA] = ram.sbank[mbc.rambank] - 0xA000;
		map[0xB] = ram.sbank[mbc.rambank] - 0xA000;
	}
	else map[0xA] = map[0xB] = NULL;
	map[0xC] = ram.ibank[0] - 0xC000;
	n = R_SVBK & 0x07;
	map[0xD] = ram.ibank[n?n:1] - 0xD000;
	map[0xE] = ram.ibank[0] - 0xE000;
	map[0xF] = NULL;

	map = mbc.wmap;
	map[0x0] = map[0x1] = map[0x2] = map[0x3] = NULL;
	map[0x4] = map[0x5] = map[0x6] = map[0x7] = NULL;
	map[0x8] = map[0x9] = NULL;
	if (mbc.enableram && !(rtc.sel&8))
	{
		map[0xA] = ram.sbank[mbc.rambank] - 0xA000;
		map[0xB] = ram.sbank[mbc.rambank] - 0xA000;
	}
	else map[0xA] = map[0xB] = NULL;
	map[0xC] = ram.ibank[0] - 0xC000;
	n = R_SVBK & 0x07;
	map[0xD] = ram.ibank[n?n:1] - 0xD000;
	map[0xE] = ram.ibank[0] - 0xE000;
	map[0xF] = NULL;
}

now if i trace _gb_read when it's set SCX, it's called on addr 0x8a, code make switch(PEANUT_GB_GET_MSN16(addr)) who develop with >>12 and for here result in 0x0,

and the result case is :

	case 0x0:

	/* TODO: BIOS support. */

Thank you all for the detailed bug report.

The offending commit causing the rendering errors in Super Mario is 6310eb8.

I will look into the other problems described shortly.

Updating the SCX register is being skipped at https://github.com/kaspermeerts/supermarioland/blob/618d00ed6c330928e106719533c6e294ae5d5726/bank0.asm#L94. I haven't been able to figure out why, so I will revert the offending commit and investigate when I more time. 6b4a387

Regarding the other problems listed:

  1. Prince of Persia is working in the latest commit 6b4a387.
    Screenshot of Prince of Persia running in Peanut-SDL

Which peanut-gb revision/commit are you using?

  1. The offending commit is reverted now. I must have forgotten to test with "high LCD accuracy" switched off. I spotted this error two weeks ago and fixed it in the v2 branch at 7dbc700, but I forgot to cherry-pick the fix...

  2. Fixed in 6f9be75.

  3. Fixed in e21823b.

The rendering bug with Super Mario is fixed in commit 9bb2d22. The bug was caused by the overwriting of the STAT register when the LCD was changing modes (HBlank, VBlank, etc). This meant that the check for HBlank would always fail in Super Mario, and the SCX would therefore never be set to the correct value.

The missing square bracket is fixed in 067e5fc.

Screenshot of Peanut-SDL with working Super Mario