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
Look how it's going on:
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++
}
}
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
}
(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
(it's an open source project; Now I'm working on PPU branch: https://github.com/jonathandasilvasanto ... rc/zerojnt )
Thank you!