[PPU Reg] Do you see anything absolutely wrong in this code?

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

Post Reply
zerojnt
Posts: 13
Joined: Wed Sep 14, 2016 1:21 pm

[PPU Reg] Do you see anything absolutely wrong in this code?

Post by zerojnt »

Hi people.

You have been help-me a lot to write my own nes emu. It's just a dummy emulator with an educational purpose. Thank you. Yesterday I got an amazing milestone for me: PPU is working! When I finished to implement the cpu; I feel like: it's alive! But now I can really see that IT'S ALIVE :P

Look how it's going on:
Image

There are some bugs; it's true. Now I'm working comparing the debug trace of Nitendulator and the mine; it's working well. What I want to ask to you is; can you identify something stupidly wrong in the way that I implemented the PPU registers?

I'm not asking for code or a deep analysis, just for a fast check and "Oh my god; You need to re-read everything about XYZ.

Here the functions that Write in PPU REGISTERS:

Code: Select all

func WRITE_PPUCTRL(IO *IOPorts, value byte) {

	if Bit0(value) == 0 && Bit1(value) == 0 {
		IO.PPUCTRL.BASE_NAMETABLE_ADDR = 0x2000
	}
	if Bit0(value) == 1 && Bit1(value) == 0 {
		IO.PPUCTRL.BASE_NAMETABLE_ADDR = 0x2400
	}
	if Bit0(value) == 0 && Bit1(value) == 1 {	
		IO.PPUCTRL.BASE_NAMETABLE_ADDR = 0x2800
	}
	if Bit0(value) == 1 && Bit1(value) == 1 {
		IO.PPUCTRL.BASE_NAMETABLE_ADDR = 0x2C00
	}
	
	if Bit2(value) == 0 {
		IO.PPUCTRL.VRAM_INCREMENT = 1
	} else {
		IO.PPUCTRL.VRAM_INCREMENT = 32
	}
	
	if Bit3(value) == 0 {
		IO.PPUCTRL.SPRITE_8_ADDR = 0x0000
	} else {
		IO.PPUCTRL.SPRITE_8_ADDR = 0x1000
	}
	
	if Bit4(value) == 0 {
		IO.PPUCTRL.BACKGROUND_ADDR = 0x0000
	} else {
		IO.PPUCTRL.BACKGROUND_ADDR = 0x1000
	}
	
	if Bit5(value) == 0 {
		IO.PPUCTRL.SPRITE_SIZE = 8
	} else {
		IO.PPUCTRL.SPRITE_SIZE = 16
	}
	
	if Bit6(value) == 0 {
		IO.PPUCTRL.MASTER_SLAVE_SWITCH = 0
	} else {
		IO.PPUCTRL.MASTER_SLAVE_SWITCH = 1
	}
	
	if Bit7(value) == 0 {
		IO.PPUCTRL.GEN_NMI = false
	} else {
	IO.PPUCTRL.GEN_NMI = true
	}
}

func WRITE_PPUMASK(IO *IOPorts, value byte) {

	if Bit0(value) == 0 {
		IO.PPUMASK.GREYSCALE = false
	} else {
		IO.PPUMASK.GREYSCALE = true
	}
	
	if Bit1(value) == 0 {
		IO.PPUMASK.SHOW_LEFTMOST_8_BACKGROUND = false
	} else {
		IO.PPUMASK.SHOW_LEFTMOST_8_BACKGROUND = true
	}
	
	if Bit2(value) == 0 {
		IO.PPUMASK.SHOW_LEFTMOST_8_SPRITE = false
	} else {
		IO.PPUMASK.SHOW_LEFTMOST_8_SPRITE = true
	}
	
	if Bit3(value) == 0 {
		IO.PPUMASK.SHOW_BACKGROUND = false
	} else {
		IO.PPUMASK.SHOW_BACKGROUND = true
	}
	
	if Bit4(value) == 0 {
		IO.PPUMASK.SHOW_SPRITE = false
	} else {
		IO.PPUMASK.SHOW_SPRITE = true
	}
	
	if Bit5(value) == 0 {
		IO.PPUMASK.RED_BOOST = false
	} else {
		IO.PPUMASK.RED_BOOST = true
	}
	
	if Bit6(value) == 0 {
		IO.PPUMASK.GREEN_BOOST = false
	} else {
		IO.PPUMASK.GREEN_BOOST = true
	}
	
	if Bit7(value) == 0 {
		IO.PPUMASK.BLUE_BOOST = false
	} else {
		IO.PPUMASK.BLUE_BOOST = true
	}
}

func WRITE_OAMADDR(IO *IOPorts, value byte) {
	IO.PPU_OAM_ADDRESS = value
}

func WRITE_OAMDATA(IO *IOPorts, value byte) {
		IO.PPU_OAM[IO.PPU_OAM_ADDRESS] = value
		IO.PPU_OAM_ADDRESS++
}

func WRITE_PPUSCROLL(IO *IOPorts, value byte) {

	if IO.PPUSCROLL.NEXT_WRITES_Y == true {
		IO.PPUSCROLL.Y = value		
	} else {
		IO.PPUSCROLL.X = value		
	}
	IO.PPUSCROLL.NEXT_WRITES_Y = !IO.PPUSCROLL.NEXT_WRITES_Y
}

func WRITE_PPUADDR(IO *IOPorts, value byte) {

	if IO.PPU_MEMORY_STEP == 0 {
		// Records the lower byte
		IO.PPU_MEMORY_HIGHER = value
		IO.PPU_MEMORY_STEP = 1
	} else {
		// Record the Higher Byte
		IO.PPU_MEMORY_LOWER = (value << 2) >> 2
		IO.PPU_MEMORY_STEP = 0
		IO.VRAM_ADDRESS = LE(IO.PPU_MEMORY_LOWER, IO.PPU_MEMORY_HIGHER)
	}
}

func WRITE_PPUDATA(IO *IOPorts, cart *cartridge.Cartridge, value byte) {
	
	
	IO.PPU_RAM[ mapper.PPU(IO.VRAM_ADDRESS) ] = value
	IO.VRAM_ADDRESS += IO.PPUCTRL.VRAM_INCREMENT
}

func WRITE_OAMDMA(IO *IOPorts, cart *cartridge.Cartridge, value byte) {
	
	var cpuaddr uint16 = uint16(value) << 8
	for i:=0; i<256; i++ {
		prgrom, finaladdr := mapper.MemoryMapper(cart, cpuaddr)
		var data byte
		if prgrom == true {
			data = cart.PRG[finaladdr]
		} else {
			data = IO.CPU_RAM[finaladdr]
		}
		
		IO.PPU_OAM[IO.PPU_OAM_ADDRESS] = data
		IO.PPU_OAM_ADDRESS++
		cpuaddr++
	}
}
My functions to READ PPU REGISTERS:

Code: Select all

func READ_PPUSTATUS(IO *IOPorts) byte {

	var result byte = 0
	result = SetBit(result, 0, Bit0(IO.PPUSTATUS.WRITTEN))
	result = SetBit(result, 1, Bit1(IO.PPUSTATUS.WRITTEN))
	result = SetBit(result, 2, Bit2(IO.PPUSTATUS.WRITTEN))
	result = SetBit(result, 3, Bit3(IO.PPUSTATUS.WRITTEN))
	result = SetBit(result, 4, Bit4(IO.PPUSTATUS.WRITTEN))
	
	if IO.PPUSTATUS.SPRITE_OVERFLOW == true {
		result = SetBit(result, 5,1)
	}
	
	if IO.PPUSTATUS.SPRITE_0_BIT == true {
		result = SetBit(result, 6,1)
	}
		
	if IO.PPUSTATUS.NMI_OCCURRED == true {
		result = SetBit(result, 7,1)
	}
	IO.PPUSTATUS.NMI_OCCURRED = false
	
	IO.PPUSCROLL.X = 0
	IO.PPUSTATUS.SPRITE_0_BIT = false
	IO.PPUSCROLL.Y = 0
	IO.VRAM_ADDRESS = 0
	
	return result	
}

func READ_OAMDATA(IO *IOPorts) byte {

		var result byte = IO.PPU_OAM[IO.PPU_OAM_ADDRESS]
		IO.PPU_OAM_ADDRESS++
		return result
}

func READ_PPUDATA(IO *IOPorts, cart *cartridge.Cartridge) byte {

	

	var newaddr uint16 = mapper.PPU (IO.VRAM_ADDRESS)


	var request byte = IO.PPU_RAM[ newaddr ]
	var result byte = IO.PREVIOUS_READ
	
	if newaddr < 0x3F00 {
		IO.PREVIOUS_READ = request
	} else {
		IO.PREVIOUS_READ = IO.PPU_RAM[ newaddr - 0x1000 ]
		result = request
	}
	
	//fmt.Printf("rnd [%x] = %x \n", newaddr, result)	
	IO.VRAM_ADDRESS += IO.PPUCTRL.VRAM_INCREMENT

	return result
}
Is there anything BIZARRE? O_O

(I made a lot of things not optimised; sometimes cause I want to keep it's very readable. Sometimes cause I don't know how to do something better :P

(it's an open source project; Now I'm working on PPU branch: https://github.com/jonathandasilvasanto ... rc/zerojnt )

Thank you!
User avatar
Dwedit
Posts: 4924
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Re: [PPU Reg] Do you see anything absolutely wrong in this c

Post by Dwedit »

The nametable bits of PPUCTRL ($2000) directly affect two bits of loopy_t, there is no actual nametable selection status that you would store into your class.
Likewise, the scroll registers ($2005) also affect Loopy_T only, there's no scroll values or anything to store into your class.

Read this:
http://wiki.nesdev.com/w/index.php/PPU_ ... _registers
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
User avatar
Quietust
Posts: 1920
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: [PPU Reg] Do you see anything absolutely wrong in this c

Post by Quietust »

Also, "IO.PPUSCROLL.NEXT_WRITES_Y" and "IO.PPU_MEMORY_STEP" are the same register, and reading from $2002 needs to clear it.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
zerojnt
Posts: 13
Joined: Wed Sep 14, 2016 1:21 pm

Re: [PPU Reg] Do you see anything absolutely wrong in this c

Post by zerojnt »

Wow! Great! Thank you guys!

This weekend I'm going to re-read everthing that I find about PPU. Now I'm skiping the raster process and I'm showing directly to the screen the background and the sprites according by NAMETABLE and OAM; but I'm looking forward to start to make the things right. :)

Thx!
fred
Posts: 67
Joined: Fri Dec 30, 2011 7:15 am
Location: Sweden

Re: [PPU Reg] Do you see anything absolutely wrong in this c

Post by fred »

Getting the ppu to finally start cooperating is a great feeling lol. Nice work!
Post Reply