diff --git a/NES Emulator/bus.c b/NES Emulator/bus.c index c4259b5..49c8ba3 100644 --- a/NES Emulator/bus.c +++ b/NES Emulator/bus.c @@ -7,7 +7,7 @@ #include "ppu.h" #include "cartridge.h" -struct Bus* createBus() +struct Bus* createBus(SDL_Renderer* renderer) { struct Bus* bus = (struct Bus*)malloc(sizeof(struct Bus)); if (bus == NULL) @@ -40,6 +40,7 @@ struct Bus* createBus() bus->cpu = createCPU(bus); // Create PPU and attack it + bus->screen = renderer; bus->ppu = createPPU(bus); bus->masterClockTimer = 0; diff --git a/NES Emulator/bus.h b/NES Emulator/bus.h index 2c2e3d0..219edcd 100644 --- a/NES Emulator/bus.h +++ b/NES Emulator/bus.h @@ -2,6 +2,7 @@ #define _BUS_H_ #include "types.h" +#include struct CPU; struct PPU; @@ -18,10 +19,12 @@ struct Bus struct Cartridge* cartridge; Byte masterClockTimer; + + SDL_Renderer* screen; }; // Sets up the Bus, allocates memory and creates devices -struct Bus* createBus(); +struct Bus* createBus(SDL_Renderer* renderer); // Destroys the bus, cleans up memory and destroys devices on the Bus void destroyBus(struct Bus* bus); diff --git a/NES Emulator/cartridge.c b/NES Emulator/cartridge.c index ab4e989..bc6c768 100644 --- a/NES Emulator/cartridge.c +++ b/NES Emulator/cartridge.c @@ -4,33 +4,6 @@ #include "bus.h" -struct INES_Header -{ - Byte nestext[4]; - Byte prg_rom_size; - Byte chr_rom_size; - - struct - { - Byte mirror : 1; - Byte battery : 1; - Byte trainer : 1; - Byte ignore_mirror : 1; - Byte mapper_lower_nibble : 4; - } Flags6; - - struct - { - Byte vs : 1; - Byte playchoice : 1; - Byte nes20 : 2; - Byte mapper_upper_nibble : 4; - } Flags7; - - Byte prg_ram_size; - Byte unused[7]; -}; - struct Cartridge* createCartridge(struct Bus* parent, const char* filepath) { struct Cartridge* cartridge = (struct Cartridge*)malloc(sizeof(struct Cartridge)); @@ -42,25 +15,24 @@ struct Cartridge* createCartridge(struct Bus* parent, const char* filepath) FILE* fp = fopen(filepath, "rb"); - struct INES_Header header; - fread(&header, sizeof(header), 1, fp); + fread(&cartridge->header, sizeof(cartridge->header), 1, fp); - cartridge->prg_rom = (Byte*)malloc(0x4000 * (size_t)header.prg_rom_size); + cartridge->prg_rom = (Byte*)malloc(0x4000 * (size_t)cartridge->header.prg_rom_size); if (cartridge->prg_rom == NULL) { fprintf(stderr, "Failed to allocate PRG memory for cartridge, aborting.\n"); exit(1); } - cartridge->chr_rom = (Byte*)malloc(0x2000 * (size_t)header.chr_rom_size); + cartridge->chr_rom = (Byte*)malloc(0x2000 * (size_t)cartridge->header.chr_rom_size); if (cartridge->chr_rom == NULL) { fprintf(stderr, "Failed to allocate CHR memory for cartridge, aborting.\n"); exit(1); } - fread(cartridge->prg_rom, 0x4000, header.prg_rom_size, fp); - fread(cartridge->chr_rom, 0x2000, header.chr_rom_size, fp); + fread(cartridge->prg_rom, 0x4000, cartridge->header.prg_rom_size, fp); + fread(cartridge->chr_rom, 0x2000, cartridge->header.chr_rom_size, fp); cartridge->bus = parent; return cartridge; diff --git a/NES Emulator/cartridge.h b/NES Emulator/cartridge.h index 2ab2c19..4650e01 100644 --- a/NES Emulator/cartridge.h +++ b/NES Emulator/cartridge.h @@ -7,6 +7,33 @@ struct Bus; struct Cartridge { + struct + { + Byte nestext[4]; + Byte prg_rom_size; + Byte chr_rom_size; + + struct + { + Byte mirror : 1; + Byte battery : 1; + Byte trainer : 1; + Byte ignore_mirror : 1; + Byte mapper_lower_nibble : 4; + } Flags6; + + struct + { + Byte vs : 1; + Byte playchoice : 1; + Byte nes20 : 2; + Byte mapper_upper_nibble : 4; + } Flags7; + + Byte prg_ram_size; + Byte unused[7]; + } header; + Byte* prg_rom; Byte* chr_rom; diff --git a/NES Emulator/cpu.c b/NES Emulator/cpu.c index 9042d3e..0ad0cd4 100644 --- a/NES Emulator/cpu.c +++ b/NES Emulator/cpu.c @@ -189,7 +189,7 @@ void fetch(struct CPU* cpu) void execute(struct CPU* cpu) { - // LOG_BUS(cpu->bus); + LOG_BUS(cpu->bus); switch (cpu->currentOpcode->op) { @@ -425,7 +425,7 @@ void execute(struct CPU* cpu) cpu->y--; cpu->status.negative = ((cpu->y & 0x80) == 0x80); - cpu->status.zero = (cpu->y == 0x80); + cpu->status.zero = (cpu->y == 0x00); } break; case EOR: diff --git a/NES Emulator/main.c b/NES Emulator/main.c index c3d7ff5..f5bcc42 100644 --- a/NES Emulator/main.c +++ b/NES Emulator/main.c @@ -1,4 +1,5 @@ #include "bus.h" +#include "ppu.h" #include #include @@ -21,7 +22,7 @@ int main(int argc, char** argv) exit(1); } - struct Bus* bus = createBus(); + struct Bus* bus = createBus(renderer); SDL_Event event; int running = 1; @@ -43,12 +44,36 @@ int main(int argc, char** argv) } } - doFrame(bus); + + SDL_SetRenderDrawColor(renderer, 20, 0, 20, 0); + SDL_RenderClear(renderer); + + SDL_Texture* tableTexture = getPatternTableTexture(bus->ppu, 0); + SDL_Rect target = { 10, 10, 256, 256 }; + SDL_RenderCopy(renderer, tableTexture, NULL, &target); + + tableTexture = getPatternTableTexture(bus->ppu, 1); + target.x = 256 + 10 + 10; + SDL_RenderCopy(renderer, tableTexture, NULL, &target); + + tableTexture = getNameTableTexture(bus->ppu, 0); + target.x = 10; + target.y = 256 + 10 + 10; + SDL_RenderCopy(renderer, tableTexture, NULL, &target); + + tableTexture = getNameTableTexture(bus->ppu, 1); + target.x = 256 + 10 + 10; + SDL_RenderCopy(renderer, tableTexture, NULL, &target); + + SDL_RenderPresent(renderer); } destroyBus(bus); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); return 0; diff --git a/NES Emulator/ppu.c b/NES Emulator/ppu.c index c9d7f8d..02c9ac8 100644 --- a/NES Emulator/ppu.c +++ b/NES Emulator/ppu.c @@ -4,6 +4,7 @@ #include #include #include "bus.h" +#include "cartridge.h" struct PPU* createPPU(struct Bus* parent) { @@ -16,29 +17,39 @@ struct PPU* createPPU(struct Bus* parent) ppu->bus = parent; + + + ppu->patternTables[0] = (Byte*)malloc(0x1000 * 2); + if (ppu->patternTables[0] == NULL) + { + fprintf(stderr, "Failed to allocate memory for PPU pattern tables.\n"); + exit(1); + } + memset(ppu->patternTables[0], 0, 0x1000 * 2); + 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); + ppu->patternTables[i] = ppu->patternTables[0] + ((size_t)0x1000 * i); + ppu->patternTableTextures[i] = SDL_CreateTexture(parent->screen, SDL_PIXELFORMAT_RGB332, SDL_TEXTUREACCESS_STREAMING, 64, 64); } - for (int i = 0; i < 4; i++) + + + ppu->nameTables[0] = (Byte*)malloc(0x0400 * 2); + if (ppu->nameTables[0] == NULL) { - 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); + fprintf(stderr, "Failed to allocate memory for PPU nameTables.\n"); + exit(1); } + memset(ppu->nameTables[0], 0, 0x0400 * 2); + + for (int i = 0; i < 2; i++) + { + ppu->nameTables[i] = ppu->nameTables[0] + ((size_t)0x0400 * i); + ppu->nameTableTextures[i] = SDL_CreateTexture(parent->screen, SDL_PIXELFORMAT_RGB332, SDL_TEXTUREACCESS_STREAMING, 32, 32); + } + + ppu->paletteIndexes = (Byte*)malloc(0x20); if (ppu->paletteIndexes == NULL) @@ -47,6 +58,8 @@ struct PPU* createPPU(struct Bus* parent) exit(1); } + + ppu->oam = (Byte*)malloc(0x100); if (ppu->oam == NULL) { @@ -54,6 +67,9 @@ struct PPU* createPPU(struct Bus* parent) exit(1); } + + ppu->mirroring = parent->cartridge->header.Flags6.mirror; + ppu->ppuCtrl.raw = 0b00000000; ppu->ppuMask.raw = 0b00000000; ppu->ppuStatus.raw = 0b10100000; @@ -64,7 +80,7 @@ struct PPU* createPPU(struct Bus* parent) ppu->scrollWriteTarget = 0; ppu->ppuAddress.raw = 0x00; - ppu->ppuAddressWriteTarget = 0; + ppu->ppuAddressWriteTarget = 1; ppu->x = 0; ppu->y = 0; @@ -76,10 +92,14 @@ void destroyPPU(struct PPU* ppu) { free(ppu->oam); free(ppu->paletteIndexes); - for(int i = 0; i < 4; i++) - free(ppu->nametables[i]); + for (int i = 0; i < 2; i++) - free(ppu->patternTables[i]); + SDL_DestroyTexture(ppu->nameTableTextures[i]); + free(ppu->nameTables[0]); + + for (int i = 0; i < 2; i++) + SDL_DestroyTexture(ppu->patternTableTextures[i]); + free(ppu->patternTables[0]); free(ppu); } @@ -106,8 +126,31 @@ Byte ppuRead(struct PPU* ppu, Word addr) break; case 0x2007: - val = ppu->nametables[ppu->ppuAddress.raw & 0x2000][ppu->ppuAddress.raw & 0x1FFF]; + { + if (ppu->ppuAddress.raw < 0x2000) + { + val = ppu->patternTables[0][ppu->ppuAddress.raw]; + } + else if (0x2000 <= ppu->ppuAddress.raw && ppu->ppuAddress.raw < 0x3F00) + { + Word effectiveAddress = ppu->ppuAddress.raw; + effectiveAddress &= ~(0x0400 + ppu->mirroring * 0x0400); + if (effectiveAddress >= 0x2800) + effectiveAddress -= 0x0400; + + val = ppu->nameTables[0][(effectiveAddress - 0x2000) & 0x0FFF]; + } + else if (0x3F00 <= ppu->ppuAddress.raw && ppu->ppuAddress.raw < 0x4000) + { + val = ppu->paletteIndexes[(ppu->ppuAddress.raw - 0x3F00) & 0x1F]; + } + else + { + fprintf(stderr, "PPU access violation at $%04X", ppu->ppuAddress.raw); + exit(1); + } ppu->ppuAddress.raw += ppu->ppuCtrl.increment; + } break; default: @@ -144,9 +187,32 @@ void ppuWrite(struct PPU* ppu, Word addr, Byte val) break; case 0x2007: - ppu->nametables[ppu->ppuAddress.raw & 0x2000][ppu->ppuAddress.raw & 0x1FFF] = val; - ppu->ppuAddress.raw += ppu->ppuCtrl.increment; + { + if (ppu->ppuAddress.raw < 0x2000) + { + ppu->patternTables[0][ppu->ppuAddress.raw] = val; + } + else if(0x2000 <= ppu->ppuAddress.raw && ppu->ppuAddress.raw < 0x3F00) + { + Word effectiveAddress = ppu->ppuAddress.raw; + effectiveAddress &= ~(0x0400 + ppu->mirroring * 0x0400); + if (effectiveAddress >= 0x2800) + effectiveAddress -= 0x0400; + + ppu->nameTables[0][(effectiveAddress - 0x2000) & 0x0FFF] = val; + } + else if (0x3F00 <= ppu->ppuAddress.raw && ppu->ppuAddress.raw < 0x4000) + { + ppu->paletteIndexes[(ppu->ppuAddress.raw - 0x3F00) & 0x1F] = val; + } + else + { + fprintf(stderr, "PPU access violation at $%04X", ppu->ppuAddress.raw); + exit(1); + } + ppu->ppuAddress.raw += (ppu->ppuCtrl.increment == 0 ? 1 : 32); break; + } default: fprintf(stderr, "Write access violation at PPU register: $%04X", addr); @@ -169,3 +235,25 @@ int tickPPU(struct PPU* ppu) return (ppu->x == 0 && ppu->y == 0); } + +SDL_Texture* getPatternTableTexture(struct PPU* ppu, int index) +{ + SDL_Texture* target = ppu->patternTableTextures[index]; + int pitch; + void* pixels; + SDL_LockTexture(target, NULL, &pixels, &pitch); + SDL_memcpy(pixels, ppu->patternTables[index], 0x1000); + SDL_UnlockTexture(target); + return target; +} + +SDL_Texture* getNameTableTexture(struct PPU* ppu, int index) +{ + SDL_Texture* target = ppu->nameTableTextures[index]; + int pitch; + void* pixels; + SDL_LockTexture(target, NULL, &pixels, &pitch); + SDL_memcpy(pixels, ppu->nameTables[index], 0x0400); + SDL_UnlockTexture(target); + return target; +} \ No newline at end of file diff --git a/NES Emulator/ppu.h b/NES Emulator/ppu.h index eb4a06d..09af5ec 100644 --- a/NES Emulator/ppu.h +++ b/NES Emulator/ppu.h @@ -77,7 +77,12 @@ struct PPU Byte oamdma; Byte* patternTables[2]; - Byte* nametables[4]; + SDL_Texture* patternTableTextures[2]; + + Byte* nameTables[2]; + SDL_Texture* nameTableTextures[2]; + Byte mirroring; + Byte* paletteIndexes; union @@ -106,4 +111,7 @@ void ppuWrite(struct PPU* ppu, Word addr, Byte val); int tickPPU(struct PPU* ppu); +SDL_Texture* getPatternTableTexture(struct PPU* ppu, int index); +SDL_Texture* getNameTableTexture(struct PPU* ppu, int index); + #endif // _PPU_H_ \ No newline at end of file