diff --git a/NES Emulator/CMakeLists.txt b/NES Emulator/CMakeLists.txt index aa29dc1..40e4070 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" "ppu.h" "ppu.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" "mapper.h" "mappers/mapper000.h" "mappers/mapper000.c" "mapper.c") target_include_directories(nesemu PUBLIC SDL2-static) target_link_libraries(nesemu PUBLIC SDL2-static SDL2main) diff --git a/NES Emulator/bus.c b/NES Emulator/bus.c index 49c8ba3..8bf3c9b 100644 --- a/NES Emulator/bus.c +++ b/NES Emulator/bus.c @@ -78,7 +78,7 @@ Byte readBus(struct Bus* bus, Word addr) } else if (0x4020 <= addr && addr <= 0xFFFF) // Cartridge space { - val = readCartridge(bus->cartridge, addr); + val = readCartridgeCPU(bus->cartridge, addr); } else { @@ -106,7 +106,7 @@ void writeBus(struct Bus* bus, Word addr, Byte val) } else if (0x4020 <= addr && addr <= 0xFFFF) // Cartridge space { - writeCartridge(bus->cartridge, addr, val); + writeCartridgeCPU(bus->cartridge, addr, val); } else { diff --git a/NES Emulator/cartridge.c b/NES Emulator/cartridge.c index bc6c768..143f3e0 100644 --- a/NES Emulator/cartridge.c +++ b/NES Emulator/cartridge.c @@ -3,6 +3,7 @@ #include #include "bus.h" +#include "mapper.h" struct Cartridge* createCartridge(struct Bus* parent, const char* filepath) { @@ -17,63 +18,42 @@ struct Cartridge* createCartridge(struct Bus* parent, const char* filepath) fread(&cartridge->header, sizeof(cartridge->header), 1, fp); - 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)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, cartridge->header.prg_rom_size, fp); - fread(cartridge->chr_rom, 0x2000, cartridge->header.chr_rom_size, fp); + Byte mapperID = (cartridge->header.Flags7.mapper_upper_nibble << 4) | cartridge->header.Flags6.mapper_lower_nibble; + cartridge->mapper = createMapper(mapperID, cartridge->header.prg_rom_size, cartridge->header.chr_rom_size, fp); cartridge->bus = parent; + + fclose(fp); return cartridge; } void destroyCartridge(struct Cartridge* cartridge) { - free(cartridge->chr_rom); - free(cartridge->prg_rom); - + destroyMapper(cartridge->mapper); free(cartridge); } -Byte readCartridge(struct Cartridge* cartridge, Word addr) +Byte readCartridgeCPU(struct Cartridge* cartridge, Word addr) { - // TODO: Force M000 mapper for now - Byte val = 0; - - if (0x6000 <= addr && addr <= 0x7FFF) // PRG RAM - { - // do nothing for now - } - else if (0x8000 <= addr && addr <= 0xFFFF) // PRG ROM - { - val = cartridge->prg_rom[addr % 0x4000]; - } - - return val; + return MapperReadCPU(cartridge->mapper, addr); } -void writeCartridge(struct Cartridge* cartridge, Word addr, Byte val) +Byte readCartridgePPU(struct Cartridge* cartridge, Word addr) { - // TODO: Force M000 mapper for now - - if (0x6000 <= addr && addr <= 0x7FFF) // PRG RAM - { - // do nothing for now - } - else if (0x8000 <= addr && addr <= 0xFFFF) // PRG ROM - { - cartridge->prg_rom[addr % 0x4000] = val; - } - + return MapperReadPPU(cartridge->mapper, addr); +} + +void writeCartridgeCPU(struct Cartridge* cartridge, Word addr, Byte val) +{ + MapperWriteCPU(cartridge->mapper, addr, val); +} + +void writeCartridgePPU(struct Cartridge* cartridge, Word addr, Byte val) +{ + MapperReadPPU(cartridge->mapper, addr, val); +} + +void getPatternTableTexture(struct Cartridge* cartridge, SDL_Texture* texture, int index) +{ + GetPatternTableTexture(cartridge->mapper, texture, index); } diff --git a/NES Emulator/cartridge.h b/NES Emulator/cartridge.h index 4650e01..922e006 100644 --- a/NES Emulator/cartridge.h +++ b/NES Emulator/cartridge.h @@ -2,8 +2,10 @@ #define _CARTRIDGE_H_ #include "types.h" +#include "SDL.h" struct Bus; +struct Mapper; struct Cartridge { @@ -34,8 +36,7 @@ struct Cartridge Byte unused[7]; } header; - Byte* prg_rom; - Byte* chr_rom; + struct Mapper* mapper; struct Bus* bus; }; @@ -44,7 +45,11 @@ struct Cartridge* createCartridge(struct Bus* parent, const char* filepath); void destroyCartridge(struct Cartridge* cartridge); // readCartridge/writeCartridge to and from the cartridge -Byte readCartridge(struct Cartridge* cartridge, Word addr); -void writeCartridge(struct Cartridge* cartridge, Word addr, Byte val); +Byte readCartridgeCPU(struct Cartridge* cartridge, Word addr); +Byte readCartridgePPU(struct Cartridge* cartridge, Word addr); +void writeCartridgeCPU(struct Cartridge* cartridge, Word addr, Byte val); +void writeCartridgePPU(struct Cartridge* cartridge, Word addr, Byte val); + +void getPatternTableTexture(struct Cartridge* cartridge, SDL_Texture* texture, int index); #endif //_CARTRIDGE_H_ \ No newline at end of file diff --git a/NES Emulator/cpu.c b/NES Emulator/cpu.c index 8ffe8bd..8746aec 100644 --- a/NES Emulator/cpu.c +++ b/NES Emulator/cpu.c @@ -68,6 +68,7 @@ int tickCPU(struct CPU* cpu) cpu->nmi = 0; + printf("NMI TRIGGERED FROM $%04x\n", cpu->pc.word); cpu->pc.lo = readBus(cpu->bus, 0xFFFA); cpu->pc.hi = readBus(cpu->bus, 0xFFFB); @@ -335,6 +336,19 @@ void execute(struct CPU* cpu) } } break; + case BRK: + { + cpu->pc.word++; + Push(cpu->bus, cpu->pc.hi); + Push(cpu->bus, cpu->pc.lo); + Push(cpu->bus, cpu->status.raw | 0b00110000); + + cpu->status.id = 1; + cpu->pc.hi = readBus(cpu->bus, 0xFFFF); + cpu->pc.lo = readBus(cpu->bus, 0xFFFE); + + } break; + case BVC: { if (!cpu->status.overflow) diff --git a/NES Emulator/main.c b/NES Emulator/main.c index f5bcc42..b367ed3 100644 --- a/NES Emulator/main.c +++ b/NES Emulator/main.c @@ -1,5 +1,6 @@ #include "bus.h" #include "ppu.h" +#include "cartridge.h" #include #include @@ -8,7 +9,7 @@ int main(int argc, char** argv) { SDL_Init(SDL_INIT_VIDEO); - SDL_Window* window = SDL_CreateWindow("NES Emulator", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 800, SDL_WINDOW_SHOWN); + SDL_Window* window = SDL_CreateWindow("NES Emulator", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1400, 800, SDL_WINDOW_SHOWN); if (window == NULL) { fprintf(stderr, "Failed to create SDL_Window.\n"); @@ -24,6 +25,17 @@ int main(int argc, char** argv) struct Bus* bus = createBus(renderer); + SDL_Texture* patternTables[2]; + for (int i = 0; i < 2; i++) + { + patternTables[i] = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB332, SDL_TEXTUREACCESS_STREAMING, 64, 64); + if (patternTables[i] == NULL) + { + fprintf(stderr, "Failed to create pattern table texture\n"); + exit(-1); + } + } + SDL_Event event; int running = 1; while(running) @@ -49,15 +61,15 @@ int main(int argc, char** argv) SDL_SetRenderDrawColor(renderer, 20, 0, 20, 0); SDL_RenderClear(renderer); - SDL_Texture* tableTexture = getPatternTableTexture(bus->ppu, 0); + getPatternTableTexture(bus->cartridge, patternTables[0], 0); SDL_Rect target = { 10, 10, 256, 256 }; - SDL_RenderCopy(renderer, tableTexture, NULL, &target); + SDL_RenderCopy(renderer, patternTables[0], NULL, &target); - tableTexture = getPatternTableTexture(bus->ppu, 1); + getPatternTableTexture(bus->cartridge, patternTables[1], 1); target.x = 256 + 10 + 10; - SDL_RenderCopy(renderer, tableTexture, NULL, &target); + SDL_RenderCopy(renderer, patternTables[1], NULL, &target); - tableTexture = getNameTableTexture(bus->ppu, 0); + SDL_Texture* tableTexture = getNameTableTexture(bus->ppu, 0); target.x = 10; target.y = 256 + 10 + 10; SDL_RenderCopy(renderer, tableTexture, NULL, &target); @@ -66,9 +78,19 @@ int main(int argc, char** argv) target.x = 256 + 10 + 10; SDL_RenderCopy(renderer, tableTexture, NULL, &target); + tableTexture = getScreenTexture(bus->ppu); + target.x = 10 + 256 + 10 + 256 + 10; + target.y = 10; + target.w = 256 * 3; + target.h = 240 * 3; + SDL_RenderCopy(renderer, tableTexture, NULL, &target); + SDL_RenderPresent(renderer); } + for (int i = 0; i < 2; i++) + SDL_DestroyTexture(patternTables[i]); + destroyBus(bus); SDL_DestroyRenderer(renderer); diff --git a/NES Emulator/mapper.c b/NES Emulator/mapper.c new file mode 100644 index 0000000..9174f40 --- /dev/null +++ b/NES Emulator/mapper.c @@ -0,0 +1,47 @@ +#include "mapper.h" + +#include +#include + +#include "mappers/mapper000.h" + +struct Mapper* createMapper(Byte id, Byte prg_rom_size, Byte chr_rom_size, FILE* fp) +{ + struct Mapper* mapper = (struct Mapper*)malloc(sizeof(struct Mapper)); + if (mapper == NULL) + { + fprintf(stderr, "Failed to create Mapper. Aborting\n"); + exit(-1); + } + + mapper->id = id; + + switch (id) + { + case 0: + { + struct Mapper000* mp = createMapper000(prg_rom_size, chr_rom_size, fp); + mapper->mapperStruct = (void*)mp; + + mapper->read_cpu = &Mapper000_ReadCPU; + mapper->read_ppu = &Mapper000_ReadPPU; + mapper->write_cpu = &Mapper000_WriteCPU; + mapper->write_ppu = &Mapper000_WritePPU; + mapper->get_pattern_table_texture = &Mapper000_GetPatternTableTexture; + } break; + + default: + fprintf(stderr, "Mapper with ID %d is not implemented\n", id); + exit(-1); + } +} + +void destroyMapper(struct Mapper* mapper) +{ + switch (mapper->id) + { + case 0: destroyMapper000((struct Mapper000*)mapper->mapperStruct); break; + } + + free(mapper); +} diff --git a/NES Emulator/mapper.h b/NES Emulator/mapper.h new file mode 100644 index 0000000..6abea0a --- /dev/null +++ b/NES Emulator/mapper.h @@ -0,0 +1,31 @@ +#ifndef _MAPPER_H_ +#define _MAPPER_H_ + +#include +#include "types.h" + +struct PPU; + +struct Mapper +{ + Byte id; + void* mapperStruct; + + Byte(*read_cpu)(void*, Word); + Byte(*read_ppu)(void*, Word); + void(*write_cpu)(void*, Word, Byte); + void(*write_ppu)(void*, Word, Byte); + + void(*get_pattern_table_texture)(void*, void*, int); +}; + +#define MapperReadCPU(mapper, addr) mapper->read_cpu(mapper->mapperStruct, addr) +#define MapperReadPPU(mapper, addr) mapper->read_ppu(mapper->mapperStruct, addr) +#define MapperWriteCPU(mapper, addr, val) mapper->write_cpu(mapper->mapperStruct, addr, val) +#define MapperWritePPU(mapper, addr, val) mapper->write_ppu(mapper->mapperStruct, addr, val) +#define GetPatternTableTexture(mapper, texture, index) mapper->get_pattern_table_texture(mapper->mapperStruct, texture, index) + +struct Mapper* createMapper(Byte id, Byte prg_rom_size, Byte chr_rom_size, FILE* fp); +void destroyMapper(struct Mapper* mapper); + +#endif // _MAPPER_H_ \ No newline at end of file diff --git a/NES Emulator/mappers/mapper000.c b/NES Emulator/mappers/mapper000.c new file mode 100644 index 0000000..3e7cdd4 --- /dev/null +++ b/NES Emulator/mappers/mapper000.c @@ -0,0 +1,105 @@ +#include "mapper000.h" + +#include +#include + +struct Mapper000* createMapper000(Byte prg_rom_size, Byte chr_rom_size, FILE* fp) +{ + struct Mapper000* mapper = (struct Mapper000*)malloc(sizeof(struct Mapper000)); + if (mapper == NULL) + { + fprintf(stderr, "Failed to create Mapper 000. Aborting\n"); + exit(-1); + } + + mapper->prg_rom_size = prg_rom_size; + mapper->prg_rom = (Byte*)calloc(prg_rom_size, 0x4000); + if (mapper->prg_rom == NULL) + { + fprintf(stderr, "Failed to allocate cartridge PRG ROM. Aborting\n"); + exit(-1); + } + + mapper->chr_rom_size = chr_rom_size; + mapper->chr_rom = (Byte*)calloc(chr_rom_size, 0x2000); + if (mapper->chr_rom == NULL) + { + fprintf(stderr, "Failed to allocate cartridge CHR ROM. Aborting\n"); + exit(-1); + } + + fread(mapper->prg_rom, 0x4000, prg_rom_size, fp); + fread(mapper->chr_rom, 0x2000, chr_rom_size, fp); + + return mapper; +} + +void destroyMapper000(struct Mapper000* mapper) +{ + free(mapper->chr_rom); + free(mapper->prg_rom); + + free(mapper); +} + +Byte Mapper000_ReadCPU(struct Mapper000* mapper, Word address) +{ + Byte val = 0x00; + + if (address >= 0x6000 && address < 0x8000) + { + fprintf(stderr, "This Mapper 000 implementation doesnt support FamilyBasic PRG RAM\n"); + exit(-1); + } + else if (address >= 0x8000) + { + Word effectiveAddress = address - 0x8000; + effectiveAddress %= 0x4000 * (mapper->prg_rom_size == 1); + + val = mapper->prg_rom[effectiveAddress]; + } + else + { + fprintf(stderr, "Cartridge read access violation by the CPU at $%04X\n", address); + exit(-1); + } + + return val; +} + +Byte Mapper000_ReadPPU(struct Mapper000* mapper, Word address) +{ + Byte val = 0x00; + + if (address >= 0x2000) + { + fprintf(stderr, "Cartridge read access violation by the PPU at $%04X\n", address); + exit(-1); + } + else + { + val = mapper->chr_rom[address]; + } + + return val; +} + +void Mapper000_WriteCPU(struct Mapper000* mapper, Word address, Byte value) +{ + // nothing +} + + +void Mapper000_WritePPU(struct Mapper000* mapper, Word address, Byte value) +{ + // nothing +} + +void Mapper000_GetPatternTableTexture(struct Mapper000* mapper, SDL_Texture* texture, int index) +{ + int pitch; + void* pixels; + SDL_LockTexture(texture, NULL, &pixels, &pitch); + SDL_memcpy(pixels, mapper->chr_rom + 0x1000 * index, 0x1000); + SDL_UnlockTexture(texture); +} diff --git a/NES Emulator/mappers/mapper000.h b/NES Emulator/mappers/mapper000.h new file mode 100644 index 0000000..88e2367 --- /dev/null +++ b/NES Emulator/mappers/mapper000.h @@ -0,0 +1,27 @@ +#ifndef _MAPPER_000_ +#define _MAPPER_000_ + +#include +#include "../types.h" +#include "SDL.h" + +struct Mapper000 +{ + Byte prg_rom_size; + Byte chr_rom_size; + + Byte* prg_rom; + Byte* chr_rom; +}; + +struct Mapper000* createMapper000(Byte prg_rom_size, Byte chr_rom_size, FILE* fp); +void destroyMapper000(struct Mapper000* mapper); + +Byte Mapper000_ReadCPU(struct Mapper000* mapper, Word address); +Byte Mapper000_ReadPPU(struct Mapper000* mapper, Word address); +void Mapper000_WriteCPU(struct Mapper000* mapper, Word address, Byte value); +void Mapper000_WritePPU(struct Mapper000* mapper, Word address, Byte value); + +void Mapper000_GetPatternTableTexture(struct Mapper000* mapper, SDL_Texture* texture, int index); + +#endif _MAPPER_000_ \ No newline at end of file diff --git a/NES Emulator/ppu.c b/NES Emulator/ppu.c index 0bb8c1a..e719996 100644 --- a/NES Emulator/ppu.c +++ b/NES Emulator/ppu.c @@ -18,24 +18,6 @@ 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] = ppu->patternTables[0] + ((size_t)0x1000 * i); - ppu->patternTableTextures[i] = SDL_CreateTexture(parent->screen, SDL_PIXELFORMAT_RGB332, SDL_TEXTUREACCESS_STREAMING, 64, 64); - } - - - ppu->nameTables[0] = (Byte*)malloc(0x0400 * 2); if (ppu->nameTables[0] == NULL) { @@ -68,6 +50,13 @@ struct PPU* createPPU(struct Bus* parent) exit(1); } + ppu->pixels = (struct Pixel*)malloc(256 * 240 * sizeof(struct Pixel)); + ppu->screen = SDL_CreateTexture(parent->screen, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, 256, 240); + if (ppu->screen == NULL) + { + fprintf(stderr, "Failed to create output screen texture\n"); + exit(1); + } ppu->mirroring = parent->cartridge->header.Flags6.mirror; @@ -83,6 +72,15 @@ struct PPU* createPPU(struct Bus* parent) ppu->ppuAddress.raw = 0x00; ppu->ppuAddressWriteTarget = 1; + ppu->nametablePos.x = 0; + ppu->nametablePos.y = 0; + ppu->tilePosY = 0; + + ppu->verticalPhase = Visible; + ppu->horizontalPhase = Idle; + ppu->fetchingPhase = Nametable; + ppu->remainingCycles = 1; + ppu->x = 0; ppu->y = 0; @@ -98,10 +96,6 @@ void destroyPPU(struct PPU* ppu) 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); } @@ -120,6 +114,9 @@ Byte ppuRead(struct PPU* ppu, Word addr) val = ppu->ppuStatus.raw; break; + case 0x2003: + break; + case 0x2005: break; @@ -130,7 +127,7 @@ Byte ppuRead(struct PPU* ppu, Word addr) { if (ppu->ppuAddress.raw < 0x2000) { - val = ppu->patternTables[0][ppu->ppuAddress.raw]; + val = readCartridgePPU(ppu->bus->cartridge, ppu->ppuAddress.raw); } else if (0x2000 <= ppu->ppuAddress.raw && ppu->ppuAddress.raw < 0x3F00) { @@ -155,8 +152,9 @@ Byte ppuRead(struct PPU* ppu, Word addr) break; default: - fprintf(stderr, "Read access violation at PPU register $%04X", addr); - exit(1); + // fprintf(stderr, "Read access violation at PPU register $%04X", addr); + // exit(1); + break; } return val; @@ -191,7 +189,7 @@ void ppuWrite(struct PPU* ppu, Word addr, Byte val) { if (ppu->ppuAddress.raw < 0x2000) { - ppu->patternTables[0][ppu->ppuAddress.raw] = val; + writeCartridgePPU(ppu->bus->cartridge, ppu->ppuAddress.raw, val); } else if(0x2000 <= ppu->ppuAddress.raw && ppu->ppuAddress.raw < 0x3F00) { @@ -216,41 +214,112 @@ void ppuWrite(struct PPU* ppu, Word addr, Byte val) } default: - fprintf(stderr, "Write access violation at PPU register: $%04X", addr); - exit(1); + //fprintf(stderr, "Write access violation at PPU register: $%04X", addr); + //exit(1); + break; } } int tickPPU(struct PPU* ppu) { // Do stuff - if (ppu->x == 0 && ppu->y == 241) - NMI(ppu->bus->cpu); + if(ppu->ppuStatus.vBlank) + if (ppu->x == 1 && ppu->y == 241) + NMI(ppu->bus->cpu); + + switch (ppu->verticalPhase) + { + case PreRender: + break; + + case Visible: + { + // Fetching + switch (ppu->horizontalPhase) + { + case Idle: + break; + + case Fetching: + ppu->remainingCycles--; + if (ppu->remainingCycles == 0) + { + Byte tileY = ppu->y / 8; + Byte tileX = (ppu->x - 1) / 8 + 2; + + switch (ppu->fetchingPhase) + { + case Nametable: + { + if (ppu->x != 1) + { + ppu->hiPatternFIFO.lo = ppu->tileData.tile.hi; + ppu->loPatternFIFO.lo = ppu->tileData.tile.lo; + } + + ppu->tileData.nametable = ppu->nameTables[0][((size_t)0x2000 + (size_t)0x400 * ppu->ppuCtrl.nametable) + (size_t)0x20* tileY + tileX]; + break; + } + + case Attribute: + { + ppu->tileData.attribute = ppu->nameTables[0][0x3C0 + ((tileY >> 2) * 8) + (tileX >> 2)]; + break; + } + + case PatternLow: + { + ppu->tileData.tile.lo = readCartridgePPU(ppu->bus->cartridge, 0x1000 * ppu->ppuCtrl.bgTile + (ppu->tileData.nametable * 16) + (ppu->y % 8)); + break; + } + + case PatternHigh: + { + ppu->tileData.tile.hi = readCartridgePPU(ppu->bus->cartridge, 0x1000 * ppu->ppuCtrl.bgTile + (ppu->tileData.nametable * 16) + (ppu->y % 8) + 8); + break; + } + } + + ppu->remainingCycles = 1; + ppu->fetchingPhase = (ppu->fetchingPhase + 1) % FetchingPhaseSize; + } + + // Rendering (quick and dirty for now) + Byte color = ((ppu->hiPatternFIFO.hi & 0x80) >> 6) | ((ppu->loPatternFIFO.hi & 0x80) >> 7); + size_t index = (size_t)ppu->y * 256 + ppu->x - 1; + // printf("index %d,%d -> %d\n", ppu->y, ppu->x - 1, index); + ppu->pixels[index].r = 50 * color; + ppu->pixels[index].g = 50 * color; + ppu->pixels[index].b = 50 * color; + + ppu->loPatternFIFO.data <<= 1; + ppu->hiPatternFIFO.data <<= 1; + + break; + } + } break; + } // Increment counters ppu->x++; - + if (ppu->x == 341) { ppu->x = 0; ppu->y++; - if (ppu->y == 261) + if (ppu->y == 262) ppu->y = 0; + + if (ppu->y == 261 || ppu->y == 0 || ppu->y == 240 || ppu->y == 241) + ppu->verticalPhase = (ppu->verticalPhase + 1) % VerticalPhaseSize; } - return (ppu->x == 0 && ppu->y == 0); -} + if (ppu->x == 0 || ppu->x == 1 || ppu->x == 257 || ppu->x == 321 || ppu->x == 337) + ppu->horizontalPhase = (ppu->horizontalPhase + 1) % HorizontalPhaseSize; -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; + + return (ppu->x == 0 && ppu->y == 0); } SDL_Texture* getNameTableTexture(struct PPU* ppu, int index) @@ -262,4 +331,15 @@ SDL_Texture* getNameTableTexture(struct PPU* ppu, int index) SDL_memcpy(pixels, ppu->nameTables[index], 0x0400); SDL_UnlockTexture(target); return target; -} \ No newline at end of file +} + +SDL_Texture* getScreenTexture(struct PPU* ppu) +{ + void* pixels; + int stride; + SDL_LockTexture(ppu->screen, NULL, &pixels, &stride); + SDL_memcpy(pixels, ppu->pixels, 256 * 240 * sizeof(struct Pixel)); + SDL_UnlockTexture(ppu->screen); + + return ppu->screen; +} diff --git a/NES Emulator/ppu.h b/NES Emulator/ppu.h index 09af5ec..b27623d 100644 --- a/NES Emulator/ppu.h +++ b/NES Emulator/ppu.h @@ -6,9 +6,32 @@ struct Bus; +struct Pixel +{ + Byte r; + Byte g; + Byte b; +}; + +struct FIFO16 +{ + union + { + struct + { + Byte lo; + Byte hi; + }; + + Word data; + }; +}; + struct PPU { - // REGISTERS + //////////////////////////////////////// + /// REGISTERS /// + //////////////////////////////////////// union { struct @@ -59,6 +82,9 @@ struct PPU Byte oamaddr; Byte oamdata; + //////////////////////////////////////// + /// PSEUDO REGISTERS /// + //////////////////////////////////////// Byte scrollX, scrollY; Byte scrollWriteTarget; @@ -76,8 +102,9 @@ struct PPU Byte oamdma; - Byte* patternTables[2]; - SDL_Texture* patternTableTextures[2]; + //////////////////////////////////////// + /// VRAM /// + //////////////////////////////////////// Byte* nameTables[2]; SDL_Texture* nameTableTextures[2]; @@ -85,6 +112,72 @@ struct PPU Byte* paletteIndexes; + + //////////////////////////////////////// + /// PHASE TRACKERS /// + //////////////////////////////////////// + + enum + { + Visible, + PostRender, + VBlank, + PreRender, + VerticalPhaseSize + } verticalPhase; + + enum + { + Idle, + Fetching, + SpriteFetching, + NextLineFetching, + Unknown, + HorizontalPhaseSize + } horizontalPhase; + + Byte remainingCycles; + + enum + { + Nametable, + Attribute, + PatternLow, + PatternHigh, + FetchingPhaseSize + } fetchingPhase; + + //////////////////////////////////////// + /// TILE DATA /// + //////////////////////////////////////// + + struct + { + Byte x; + Byte y; + } nametablePos; + Byte tilePosY; + + struct + { + Byte nametable; + Byte attribute; + + union + { + struct { + Byte lo; + Byte hi; + }; + + Word raw; + + } tile; + } tileData; + + struct FIFO16 loPatternFIFO; + struct FIFO16 hiPatternFIFO; + union { struct @@ -100,6 +193,8 @@ struct PPU Word x, y; + struct Pixel* pixels; + SDL_Texture* screen; struct Bus* bus; }; @@ -111,7 +206,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); +SDL_Texture* getScreenTexture(struct PPU* ppu); #endif // _PPU_H_ \ No newline at end of file diff --git a/roms/cpu.nes b/roms/cpu.nes new file mode 100644 index 0000000..63cb98d Binary files /dev/null and b/roms/cpu.nes differ diff --git a/roms/donkeykong.nes b/roms/donkeykong.nes new file mode 100644 index 0000000..ecd6f0a Binary files /dev/null and b/roms/donkeykong.nes differ