diff --git a/NES Emulator/CMakeLists.txt b/NES Emulator/CMakeLists.txt index 79888f4..f37c02f 100644 --- a/NES Emulator/CMakeLists.txt +++ b/NES Emulator/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required (VERSION 3.8) -add_executable (nesemu "main.c" "cpu.h" "types.h" "bus.h" "bus.c" "cartridge.h" "cpu.c" "cartridge.c" "opcodes.c" "log.c") +add_executable (nesemu "main.c" "cpu.h" "types.h" "bus.h" "bus.c" "cartridge.h" "cpu.c" "cartridge.c" "opcodes.c" "log.c" "ppu.h" "ppu.c") if(MSVC) target_compile_definitions(nesemu PUBLIC _CRT_SECURE_NO_WARNINGS) diff --git a/NES Emulator/bus.c b/NES Emulator/bus.c index 9144e5a..19b8a63 100644 --- a/NES Emulator/bus.c +++ b/NES Emulator/bus.c @@ -4,6 +4,7 @@ #include #include "cpu.h" +#include "ppu.h" #include "cartridge.h" struct Bus* createBus() @@ -35,6 +36,9 @@ struct Bus* createBus() // Create CPU and attach it bus->cpu = createCPU(bus); + // Create PPU and attack it + bus->ppu = createPPU(bus); + // Create and insert cartridge bus->cartridge = createCartridge(bus, "roms/nestest.nes"); @@ -46,6 +50,7 @@ struct Bus* createBus() void destroyBus(struct Bus* bus) { destroyCartridge(bus->cartridge); + destroyPPU(bus->ppu); destroyCPU(bus->cpu); free(bus->io); @@ -104,5 +109,8 @@ void writeBus(struct Bus* bus, Word addr, Byte val) void tick(struct Bus* bus) { - tickInstr(bus->cpu); + for (int i = 0; i < 3; i++) + tickPPU(bus->ppu); + + tickCPU(bus->cpu); } \ No newline at end of file diff --git a/NES Emulator/bus.h b/NES Emulator/bus.h index ceafd6a..e9b2d39 100644 --- a/NES Emulator/bus.h +++ b/NES Emulator/bus.h @@ -4,6 +4,7 @@ #include "types.h" struct CPU; +struct PPU; struct Cartridge; // Main communication path for devices and memory in the NES @@ -13,6 +14,7 @@ struct Bus Byte* io; struct CPU* cpu; + struct PPU* ppu; struct Cartridge* cartridge; }; diff --git a/NES Emulator/log.c b/NES Emulator/log.c index eb96188..c373f88 100644 --- a/NES Emulator/log.c +++ b/NES Emulator/log.c @@ -3,6 +3,7 @@ #include #include "bus.h" #include "cpu.h" +#include "ppu.h" void logBusState(struct Bus* bus) { @@ -40,5 +41,11 @@ void logBusState(struct Bus* bus) printf("%-28s", buffer); - printf("A:%02X X:%02X Y:%02X SP:%02X P:%02X CYC:%zu\n", bus->cpu->acc, bus->cpu->x, bus->cpu->y, bus->cpu->sp, bus->cpu->status.raw, bus->cpu->totalCycles); + printf("A:%02X X:%02X Y:%02X SP:%02X P:%02X PPU:%3d,%3d CYC:%zu\n", + bus->cpu->acc, + bus->cpu->x, bus->cpu->y, + bus->cpu->sp, + bus->cpu->status.raw, + bus->ppu->y, bus->ppu->x, + bus->cpu->totalCycles); } \ No newline at end of file diff --git a/NES Emulator/ppu.c b/NES Emulator/ppu.c new file mode 100644 index 0000000..c9a1cbd --- /dev/null +++ b/NES Emulator/ppu.c @@ -0,0 +1,101 @@ +#include "ppu.h" + +#include +#include +#include +#include "bus.h" + +struct PPU* createPPU(struct Bus* parent) +{ + struct PPU* ppu = (struct PPU*)malloc(sizeof(struct PPU)); + if (ppu == NULL) + { + fprintf(stderr, "Failed to allocate memory for PPU object.\n"); + exit(1); + } + + ppu->bus = parent; + + for (int i = 0; i < 2; i++) + { + ppu->patternTables[i] = (Byte*)malloc(0x1000); + if (ppu->patternTables[i] == NULL) + { + fprintf(stderr, "Failed to allocate memory for PPU pattern table #%d object.\n", i); + exit(1); + } + + memset(ppu->patternTables[i], 0, 0x1000); + } + + for (int i = 0; i < 4; i++) + { + ppu->nametables[i] = (Byte*)malloc(0x0400); + if (ppu->nametables[i] == NULL) + { + fprintf(stderr, "Failed to allocate memory for PPU nametable #%d object.\n", i); + exit(1); + } + + memset(ppu->nametables[i], 0, 0x0400); + } + + ppu->paletteIndexes = (Byte*)malloc(0x20); + if (ppu->paletteIndexes == NULL) + { + fprintf(stderr, "Failed to allocate memory for PPU palette RAM indexes.\n"); + exit(1); + } + + ppu->oam = (Byte*)malloc(0x100); + if (ppu->oam == NULL) + { + fprintf(stderr, "Failed to allocate memory for PPU OAM.\n"); + exit(1); + } + + ppu->ppuctrl.raw = 0b00000000; + ppu->ppumask.raw = 0b00000000; + ppu->ppustatus.raw = 0b10100000; + ppu->oamaddr = 0x00; + ppu->ppuAddr = 0x00; + ppu->ppuData = 0x00; + + ppu->x = 0; + ppu->y = 0; + + return ppu; +} + +void destroyPPU(struct PPU* ppu) +{ + free(ppu->oam); + free(ppu->paletteIndexes); + free(ppu->nametables); + free(ppu->patternTables); + + free(ppu); +} + +Byte ppuRead(struct PPU* ppu, Word addr) +{ + return 0x00; +} + +void ppuWrite(struct PPU* ppu, Word addr, Byte val) +{ +} + +void tickPPU(struct PPU* ppu) +{ + ppu->x++; + + if (ppu->x == 341) + { + ppu->x = 0; + ppu->y++; + + if (ppu->y == 261) + ppu->y = 0; + } +} diff --git a/NES Emulator/ppu.h b/NES Emulator/ppu.h new file mode 100644 index 0000000..91ee4dd --- /dev/null +++ b/NES Emulator/ppu.h @@ -0,0 +1,95 @@ +#ifndef _PPU_H_ +#define _PPU_H_ + +#include "types.h" + +struct Bus; + +struct PPU +{ + // REGISTERS + union + { + struct + { + Byte nametable : 2; + Byte increment : 1; + Byte spriteTile : 1; + Byte bgTile : 1; + Byte spriteHeight : 1; + Byte master : 1; + Byte nmiEnable : 1; + }; + + Byte raw; + + } ppuctrl; + + union + { + struct + { + Byte greyscale : 1; + Byte bgLeftColumn : 1; + Byte spriteLeftColumn : 1; + Byte bgEnable : 1; + Byte spriteEnable : 1; + Byte colorEmphasis : 3; + }; + + Byte raw; + + } ppumask; + + union + { + struct + { + Byte padding : 5; + Byte overflow : 1; + Byte spriteZeroHit : 1; + Byte vBlank : 1; + }; + + Byte raw; + + } ppustatus; + + Byte oamaddr; + Byte oamdata; + Byte ppuScroll; + Byte ppuAddr; + Byte ppuData; + Byte oamdma; + + Byte* patternTables[2]; + Byte* nametables[4]; + Byte* paletteIndexes; + + union + { + struct + { + Byte y; + Byte tile; + Byte attr; + Byte x; + }; + + DWord raw; + }* oam; + + Word x, y; + + struct Bus* bus; +}; + +struct PPU* createPPU(struct Bus* parent); +void destroyPPU(struct PPU* ppu); + +Byte ppuRead(struct PPU* ppu, Word addr); +void ppuWrite(struct PPU* ppu, Word addr, Byte val); + +void tickPPU(struct PPU* ppu); + +#endif // _PPU_H_ \ No newline at end of file