~groovestomp/gsnes

78fc0c650469517a12bffa13765872240d391b3f — GrooveStomp 1 year, 5 months ago bb81cae
Progress
14 files changed, 491 insertions(+), 276 deletions(-)

M .gitignore
M TODO
M bus.c
M bus.h
M cart.c
M cart.h
M cpu.c
M cpu.h
M main.c
M mapper.h
M mapper000.c
M mapper000.h
M ppu.c
M ppu.h
M .gitignore => .gitignore +1 -1
@@ 2,4 2,4 @@ release
#*
debug
core
nestest.nes
*.nes

M TODO => TODO +3 -3
@@ 1,5 1,4 @@
- Emulation isn't working.
  SP seems to be changing, but nothing else?
- Screen isn't rendering.

- Fuzzy font rendering.
  Font overdraw? Improper buffer clearing?


@@ 9,4 8,5 @@
  This may be the same bug as the fuzzy font rendering.

- Rendering CPU state includes garbage after valid string.
  I think the string capacity is specified incorrectly and/or there is a missing null character.
\ No newline at end of file
  I think the string capacity is specified incorrectly and/or there is a missing null character.
  Look in cpu.c in the debug/disassembly stuff.
\ No newline at end of file

M bus.c => bus.c +10 -7
@@ 4,7 4,7 @@

  File: bus.c
  Created: 2019-10-16
  Updated: 2019-11-21
  Updated: 2019-11-30
  Author: Aaron Oman
  Notice: GNU AGPLv3 License



@@ 13,13 13,14 @@
  This is free software, and you are welcome to redistribute it under certain
  conditions; See LICENSE for details.
 ******************************************************************************/
//! \file bus.c
#include <stdlib.h> // malloc, free

#include "bus.h"
#include "cpu.h"
#include "ppu.h"
#include "cart.h"
//! \file bus.c
#include "util.h"

struct bus {
        struct cpu *cpu;


@@ 31,9 32,11 @@ struct bus {

struct bus *BusInit(struct cpu *cpu, struct ppu *ppu) {
        struct bus *bus = (struct bus *)malloc(sizeof(struct bus));
        if (NULL == bus)
                return NULL;

        bus->cpuRam = (uint8_t *)malloc(64 * 1024);
        for (int i = 0; i < 64 * 1024; i++) {
        bus->cpuRam = (uint8_t *)malloc(KB_AS_B(2));
        for (int i = 0; i < KB_AS_B(2); i++) {
                bus->cpuRam[i] = 0x00;
        }



@@ 66,7 69,7 @@ void BusWrite(struct bus *bus, uint16_t addr, uint8_t data) {
        }
}

uint8_t BusRead(struct bus *bus, uint16_t addr) {
uint8_t BusRead(struct bus *bus, uint16_t addr, bool readOnly) {
        uint8_t data = 0x00;

        if (CartCpuRead(bus->cart, addr, &data)) {


@@ 76,7 79,7 @@ uint8_t BusRead(struct bus *bus, uint16_t addr) {
                data = bus->cpuRam[addr & 0x07FF];
        } else if (addr >= 0x2000 && addr <= 0x3FFF) {
                // PPU address range, mirrored every 8.
                data = PpuReadViaCpu(bus->ppu, addr & 0x0007);
                data = PpuReadViaCpu(bus->ppu, addr & 0x0007, readOnly);
        }

        return data;


@@ 88,7 91,7 @@ void BusAttachCart(struct bus *bus, struct cart *cart) {
}

void BusReset(struct bus *bus) {
        //CartReset(bus->cart);
        CartReset(bus->cart);
        CpuReset(bus->cpu);
        PpuReset(bus->ppu);
        bus->tickCount = 0;

M bus.h => bus.h +5 -4
@@ 4,7 4,7 @@

  File: bus.h
  Created: 2019-10-16
  Updated: 2019-11-19
  Updated: 2019-11-30
  Author: Aaron Oman
  Notice: GNU AGPLv3 License



@@ 13,12 13,13 @@
  This is free software, and you are welcome to redistribute it under certain
  conditions; See LICENSE for details.
 ******************************************************************************/
#include <stdint.h>
//! \file bus.h

#ifndef BUS_VERSION
#define BUS_VERSION "0.1.0"

#include <stdint.h>
#include <stdbool.h>

struct cpu;
struct bus;
struct ppu;


@@ 34,7 35,7 @@ void
BusWrite(struct bus *bus, uint16_t addr, uint8_t data);

uint8_t
BusRead(struct bus *bus, uint16_t addr);
BusRead(struct bus *bus, uint16_t addr, bool readOnly);

void
BusReset(struct bus *bus);

M cart.c => cart.c +57 -27
@@ 4,7 4,7 @@

  File: cart.c
  Created: 2019-11-03
  Updated: 2019-11-21
  Updated: 2019-11-27
  Author: Aaron Oman
  Notice: GNU AGPLv3 License



@@ 32,6 32,9 @@ struct cart {
        uint8_t *prgMem;
        uint8_t *chrMem;
        void *mapper;
        mapper_init_fn mapperInit;
        mapper_deinit_fn mapperDeinit;
        mapper_reset_fn mapperReset;
        map_cpu_read_fn mapCpuRead;
        map_cpu_write_fn mapCpuWrite;
        map_ppu_read_fn mapPpuRead;


@@ 94,9 97,9 @@ struct cart *CartInit(char *filename) {

        if (1 == file_type) {
                cart->prgBanks = header.prgRomChunks;
                cart->prgMem = (uint8_t *)malloc(sizeof(uint8_t) * KB_AS_B(16));
                objs_read = fread(cart->prgMem, 1, KB_AS_B(16), f);
                if (objs_read < KB_AS_B(16)) {
                cart->prgMem = (uint8_t *)malloc(cart->prgBanks * KB_AS_B(16));
                objs_read = fread(cart->prgMem, 1, cart->prgBanks * KB_AS_B(16), f);
                if (objs_read < cart->prgBanks * KB_AS_B(16)) {
                        fclose(f);
                        free(cart);
                        return NULL;


@@ 104,13 107,24 @@ struct cart *CartInit(char *filename) {
                }

                cart->chrBanks = header.chrRomChunks;
                cart->chrMem = (uint8_t *)malloc(sizeof(uint8_t) * KB_AS_B(8));
                objs_read = fread(cart->chrMem, 1, KB_AS_B(8), f);
                if (objs_read < KB_AS_B(8)) {
                        fclose(f);
                        free(cart);
                        return NULL;
                        // TODO Couldn't read data from file - set appropriate error
                if (0 == cart->chrBanks) {
                        cart->chrMem = (uint8_t *)malloc(KB_AS_B(8));
                        objs_read = fread(cart->chrMem, 1, KB_AS_B(8), f);
                        if (objs_read < KB_AS_B(8)) {
                                fclose(f);
                                free(cart);
                                return NULL;
                                // TODO Couldn't read data from file - set appropriate error
                        }
                } else {
                        cart->chrMem = (uint8_t *)malloc(cart->chrBanks * KB_AS_B(8));
                        objs_read = fread(cart->chrMem, 1, cart->chrBanks * KB_AS_B(8), f);
                        if (objs_read < cart->chrBanks * KB_AS_B(8)) {
                                fclose(f);
                                free(cart);
                                return NULL;
                                // TODO Couldn't read data from file - set appropriate error
                        }
                }
        }



@@ 120,14 134,18 @@ struct cart *CartInit(char *filename) {

        switch(cart->mapperId) {
                case 0: {
                        cart->mapper = Mapper000_Init(cart->prgBanks, cart->chrBanks);
                        if (NULL == cart->mapper) {
                                // TODO handle cart->mapper not being initialized
                        }
                        cart->mapperInit = Mapper000_Init;
                        cart->mapperDeinit = Mapper000_Deinit;
                        cart->mapperReset = Mapper000_Reset;
                        cart->mapCpuRead = Mapper000_MapCpuRead;
                        cart->mapCpuWrite = Mapper000_MapCpuWrite;
                        cart->mapPpuRead = Mapper000_MapPpuRead;
                        cart->mapPpuWrite = Mapper000_MapPpuWrite;

                        cart->mapper = cart->mapperInit(cart->prgBanks, cart->chrBanks);
                        if (NULL == cart->mapper) {
                                // TODO handle cart->mapper not being initialized
                        }
                        break;
                }
        }


@@ 148,13 166,16 @@ void CartDeinit(struct cart *cart) {
        if (NULL != cart->prgMem)
                free(cart->prgMem);

        if (NULL != cart->mapper && NULL != cart->mapperDeinit)
                cart->mapperDeinit(cart->mapper);

        free(cart);
}

bool CartCpuRead(struct cart *cart, uint16_t addr, uint8_t *data) {
        uint32_t mapped_addr = 0;
        if (cart->mapCpuRead(cart->mapper, addr, &mapped_addr)) {
                *data = cart->prgMem[mapped_addr];
        uint32_t mappedAddr = 0;
        if (cart->mapCpuRead(cart->mapper, addr, &mappedAddr)) {
                *data = cart->prgMem[mappedAddr];
                return true;
        }



@@ 162,9 183,9 @@ bool CartCpuRead(struct cart *cart, uint16_t addr, uint8_t *data) {
}

bool CartCpuWrite(struct cart *cart, uint16_t addr, uint8_t data) {
        uint32_t mapped_addr = 0;
        if (cart->mapCpuWrite(cart->mapper, addr, &mapped_addr)) {
                cart->prgMem[mapped_addr] = data;
        uint32_t mappedAddr = 0;
        if (cart->mapCpuWrite(cart->mapper, addr, &mappedAddr)) {
                cart->prgMem[mappedAddr] = data;
                return true;
        }



@@ 172,9 193,9 @@ bool CartCpuWrite(struct cart *cart, uint16_t addr, uint8_t data) {
}

bool CartPpuRead(struct cart *cart, uint16_t addr, uint8_t *data) {
        uint32_t mapped_addr = 0;
        if (cart->mapPpuRead(cart->mapper, addr, &mapped_addr)) {
                *data = cart->chrMem[mapped_addr];
        uint32_t mappedAddr = 0;
        if (cart->mapPpuRead(cart->mapper, addr, &mappedAddr)) {
                *data = cart->chrMem[mappedAddr];
                return true;
        }



@@ 182,9 203,9 @@ bool CartPpuRead(struct cart *cart, uint16_t addr, uint8_t *data) {
}

bool CartPpuWrite(struct cart *cart, uint16_t addr, uint8_t data) {
        uint32_t mapped_addr = 0;
        if (cart->mapPpuWrite(cart->mapper, addr, &mapped_addr)) {
                cart->chrMem[mapped_addr] = data;
        uint32_t mappedAddr = 0;
        if (cart->mapPpuWrite(cart->mapper, addr, &mappedAddr)) {
                cart->chrMem[mappedAddr] = data;
                return true;
        }



@@ 194,3 215,12 @@ bool CartPpuWrite(struct cart *cart, uint16_t addr, uint8_t data) {
enum mirror CartMirroring(struct cart *cart) {
        return cart->mirror;
}

bool CartIsImageValid(struct cart *cart) {
        return cart->isImageValid;
}

void CartReset(struct cart *cart) {
        if (NULL != cart->mapper && NULL != cart->mapperReset)
                cart->mapperReset(cart->mapper);
}

M cart.h => cart.h +12 -1
@@ 4,7 4,7 @@

  File: cart.h
  Created: 2019-11-03
  Updated: 2019-11-21
  Updated: 2019-11-27
  Author: Aaron Oman
  Notice: GNU AGPLv3 License



@@ 14,6 14,11 @@
  conditions; See LICENSE for details.
 ******************************************************************************/
//! \file cart.h
//! The cartridge contains game data as well as circuitry.
//! The circuitry is described by the Mapper interface.
//! The cartridge is able to intercept reads and writes by both the CPU and PPU.
//! \see mapper.h

#include <stdint.h>
#include <stdbool.h>



@@ 50,4 55,10 @@ CartPpuWrite(struct cart *cart, uint16_t addr, uint8_t data);
enum mirror
CartMirroring(struct cart *cart);

bool
CartIsImageValid(struct cart *cart);

void
CartReset(struct cart *cart);

#endif // CART_VERSION

M cpu.c => cpu.c +74 -63
@@ 5,7 5,7 @@

  File: cpu.c
  Created: 2019-10-16
  Updated: 2019-11-26
  Updated: 2019-11-30
  Author: Aaron Oman
  Notice: GNU AGPLv3 License



@@ 112,8 112,11 @@ enum status_flags {

struct cpu *CpuInit() {
        struct cpu *cpu = (struct cpu *)malloc(sizeof(struct cpu));
        if (NULL == cpu) {
                return NULL;
        }

        cpu->bus = NULL;
        return cpu;

        cpu->a = 0x00;
        cpu->x = 0x00;


@@ 127,6 130,8 @@ struct cpu *CpuInit() {
        cpu->opcode = 0x00;
        cpu->cycles = 0;
        cpu->tickCount = 0;

        return cpu;
}

void CpuDeinit(struct cpu *cpu) {


@@ 152,7 157,7 @@ static void SetFlag(struct cpu *cpu, enum status_flags f, bool v) {

static uint8_t Fetch(struct cpu* cpu) {
        if (!(instruction_map[cpu->opcode].address == IMP)) {
                cpu->fetched = BusRead(cpu->bus, cpu->addrAbs);
                cpu->fetched = BusRead(cpu->bus, cpu->addrAbs, false);
        }

        return cpu->fetched;


@@ 162,20 167,24 @@ static uint8_t Fetch(struct cpu* cpu) {
void CpuTick(struct cpu *cpu) {
        if (0 == cpu->cycles) {
                // Read the next byte to determine which opcode we are using.
                cpu->opcode = BusRead(cpu->bus, cpu->pc);
                cpu->opcode = BusRead(cpu->bus, cpu->pc, false);
                cpu->pc++;

                SetFlag(cpu, U, 1);

                // Now use the instruction map to get the instruction our opcode is implementing.
                struct instruction *instruction = &instruction_map[cpu->opcode];

                cpu->cycles = instruction->cycles; // Get starting number of cycles
                uint8_t need_more_cycles_1 = instruction->address(cpu);
                uint8_t need_more_cycles_2 = instruction->operate(cpu);
                uint8_t needMoreCycles1 = instruction->address(cpu);
                uint8_t needMoreCycles2 = instruction->operate(cpu);

                // If both the address and operate functions indicate that an
                // additional cycle was required, then increase the number of
                // cycles by 1.
                cpu->cycles += (need_more_cycles_1 & need_more_cycles_2);
                cpu->cycles += (needMoreCycles1 & needMoreCycles2);

                SetFlag(cpu, U, 1);
        }

        cpu->tickCount++;


@@ 187,18 196,18 @@ int CpuIsComplete(struct cpu *cpu) {
}

void CpuReset(struct cpu *cpu) {
        cpu->addrAbs = 0xFFFC;
        uint16_t lo = BusRead(cpu->bus, cpu->addrAbs + 0, false);
        uint16_t hi = BusRead(cpu->bus, cpu->addrAbs + 1, false);

        cpu->pc = (hi << 8) | lo;

        cpu->a = 0;
        cpu->x = 0;
        cpu->y = 0;
        cpu->sp = 0xFD;
        cpu->status = 0x00 | U;

        cpu->addrAbs = 0xFFFC;
        uint16_t lo = BusRead(cpu->bus, cpu->addrAbs + 0);
        uint16_t hi = BusRead(cpu->bus, cpu->addrAbs + 1);

        cpu->pc = (hi << 8) | lo;

        cpu->addrRel = 0x0000;
        cpu->addrAbs = 0x0000;
        cpu->fetched = 0x00;


@@ 220,8 229,8 @@ void Irq(struct cpu *cpu) {
                cpu->sp--;

                cpu->addrAbs = 0xFFFE;
                uint16_t lo = BusRead(cpu->bus, cpu->addrAbs + 0);
                uint16_t hi = BusRead(cpu->bus, cpu->addrAbs + 1);
                uint16_t lo = BusRead(cpu->bus, cpu->addrAbs + 0, false);
                uint16_t hi = BusRead(cpu->bus, cpu->addrAbs + 1, false);
                cpu->pc = (hi << 8) | lo;

                cpu->cycles = 7;


@@ 241,8 250,8 @@ void CpuNmi(struct cpu *cpu) {
        cpu->sp--;

        cpu->addrAbs = 0xFFFA;
        uint16_t lo = BusRead(cpu->bus, cpu->addrAbs + 0);
        uint16_t hi = BusRead(cpu->bus, cpu->addrAbs + 1);
        uint16_t lo = BusRead(cpu->bus, cpu->addrAbs + 0, false);
        uint16_t hi = BusRead(cpu->bus, cpu->addrAbs + 1, false);
        cpu->pc = (hi << 8) | lo;

        cpu->cycles = 8;


@@ 312,9 321,9 @@ uint8_t SBC(struct cpu *cpu) {
//! \param[in,out] cpu
//! \return 0 This addressing mode will take no additional cycles
uint8_t ABS(struct cpu *cpu) {
        uint16_t lo = BusRead(cpu->bus, cpu->pc);
        uint16_t lo = BusRead(cpu->bus, cpu->pc, false);
        cpu->pc++;
        uint16_t hi = BusRead(cpu->bus, cpu->pc);
        uint16_t hi = BusRead(cpu->bus, cpu->pc, false);
        cpu->pc++;

        cpu->addrAbs = (hi << 8) | lo;


@@ 326,9 335,9 @@ uint8_t ABS(struct cpu *cpu) {
//! \param[in,out] cpu
//! \return int 1 if this addressing mode _can_ take another clock cycle, else 0
uint8_t ABX(struct cpu *cpu) {
        uint16_t lo = BusRead(cpu->bus, cpu->pc);
        uint16_t lo = BusRead(cpu->bus, cpu->pc, false);
        cpu->pc++;
        uint16_t hi = BusRead(cpu->bus, cpu->pc);
        uint16_t hi = BusRead(cpu->bus, cpu->pc, false);
        cpu->pc++;

        cpu->addrAbs = (hi << 8) | lo;


@@ 346,9 355,9 @@ uint8_t ABX(struct cpu *cpu) {
//! \param[in,out] cpu
//! \return int 1 if this addressing mode _can_ take another clock cycle, else 0
uint8_t ABY(struct cpu *cpu) {
        uint16_t lo = BusRead(cpu->bus, cpu->pc);
        uint16_t lo = BusRead(cpu->bus, cpu->pc, false);
        cpu->pc++;
        uint16_t hi = BusRead(cpu->bus, cpu->pc);
        uint16_t hi = BusRead(cpu->bus, cpu->pc, false);
        cpu->pc++;

        cpu->addrAbs = (hi << 8) | lo;


@@ 401,17 410,17 @@ uint8_t IMP(struct cpu *cpu) {
//! \param[in,out] cpu
//! \return 0 This addressing mode will take no additional cycles
uint8_t IND(struct cpu *cpu) {
        uint16_t ptr_lo = BusRead(cpu->bus, cpu->pc);
        uint16_t ptr_lo = BusRead(cpu->bus, cpu->pc, false);
        cpu->pc++;
        uint16_t ptr_hi = BusRead(cpu->bus, cpu->pc);
        uint16_t ptr_hi = BusRead(cpu->bus, cpu->pc, false);
        cpu->pc++;

        uint16_t ptr = (ptr_hi << 8) | ptr_lo;

        if (ptr_lo == 0x00FF) { // Simulate page boundary hardware bug
                cpu->addrAbs = (BusRead(cpu->bus, ptr & 0xFF00) << 8) | BusRead(cpu->bus, ptr + 0);
                cpu->addrAbs = (BusRead(cpu->bus, ptr & 0xFF00, false) << 8) | BusRead(cpu->bus, ptr + 0, false);
        } else { // Behave normally
                cpu->addrAbs = (BusRead(cpu->bus, ptr + 1) << 8) | BusRead(cpu->bus, ptr + 0);
                cpu->addrAbs = (BusRead(cpu->bus, ptr + 1, false) << 8) | BusRead(cpu->bus, ptr + 0, false);
        }

        return 0;


@@ 426,12 435,12 @@ uint8_t IND(struct cpu *cpu) {
//! \param[in,out] cpu
//! \return 0 This addressing mode will take no additional cycles
uint8_t IZX(struct cpu *cpu) {
        uint16_t t = BusRead(cpu->bus, cpu->pc);
        uint16_t t = BusRead(cpu->bus, cpu->pc, false);
        cpu->pc++;

        uint16_t offset = t + (uint16_t)(cpu->x);
        uint16_t lo = BusRead(cpu->bus, offset & 0x00FF);
        uint16_t hi = BusRead(cpu->bus, (offset +  1) & 0x00FF);
        uint16_t lo = BusRead(cpu->bus, offset & 0x00FF, false);
        uint16_t hi = BusRead(cpu->bus, (offset +  1) & 0x00FF, false);

        cpu->addrAbs = (hi << 8) | lo;



@@ 452,11 461,11 @@ uint8_t IZX(struct cpu *cpu) {
//! \param[in,out] cpu
//! \return int 1 if this addressing mode _can_ take another clock cycle, else 0
uint8_t IZY(struct cpu *cpu) {
        uint16_t t = BusRead(cpu->bus, cpu->pc);
        uint16_t t = BusRead(cpu->bus, cpu->pc, false);
        cpu->pc++;

        uint16_t lo = BusRead(cpu->bus, t & 0x00FF);
        uint16_t hi = BusRead(cpu->bus, (t + 1) & 0x00FF);
        uint16_t lo = BusRead(cpu->bus, t & 0x00FF, false);
        uint16_t hi = BusRead(cpu->bus, (t + 1) & 0x00FF, false);

        cpu->addrAbs = (hi << 8) | lo;
        cpu->addrAbs += cpu->y;


@@ 476,7 485,7 @@ uint8_t IZY(struct cpu *cpu) {
//! \param[in,out] cpu
//! \return 0 This addressing mode will take no additional cycles
uint8_t REL(struct cpu *cpu) {
        cpu->addrRel = BusRead(cpu->bus, cpu->pc);
        cpu->addrRel = BusRead(cpu->bus, cpu->pc, false);
        cpu->pc++;

        // REL involves signed values for jumps.


@@ 504,7 513,7 @@ uint8_t REL(struct cpu *cpu) {
//! \param[in,out] cpu
//! \return 0 This addressing mode will take no additional cycles
uint8_t ZP0(struct cpu *cpu) {
        cpu->addrAbs = BusRead(cpu->bus, cpu->pc);
        cpu->addrAbs = BusRead(cpu->bus, cpu->pc, false);
        cpu->pc++;
        cpu->addrAbs &= 0x00FF;
        return 0;


@@ 518,7 527,7 @@ uint8_t ZP0(struct cpu *cpu) {
//! \param[in,out] cpu
//! \return 0 This addressing mode will take no additional cycles
uint8_t ZPX(struct cpu *cpu) {
        cpu->addrAbs = BusRead(cpu->bus, cpu->pc) + cpu->x;
        cpu->addrAbs = BusRead(cpu->bus, cpu->pc, false) + cpu->x;
        cpu->pc++;
        cpu->addrAbs &= 0x00FF;
        return 0;


@@ 532,7 541,7 @@ uint8_t ZPX(struct cpu *cpu) {
//! \param[in,out] cpu
//! \return 0 This addressing mode will take no additional cycles
uint8_t ZPY(struct cpu *cpu) {
        cpu->addrAbs = BusRead(cpu->bus, cpu->pc) + cpu->y;
        cpu->addrAbs = BusRead(cpu->bus, cpu->pc, false) + cpu->y;
        cpu->pc++;
        cpu->addrAbs &= 0x00FF;
        return 0;


@@ 726,7 735,7 @@ uint8_t BPL(struct cpu *cpu) {

//! \brief Break
//!
//! //! TODO
//! //! TODO: describe me
//!
//! \param[in,out] cpu
//! \return 0 This instruction will take no additional cycles


@@ 744,7 753,7 @@ uint8_t BRK(struct cpu*cpu) {
        cpu->sp--;
        SetFlag(cpu, B, 0);

        cpu->pc = (uint16_t)BusRead(cpu->bus, 0xFFFE) | ((uint16_t)BusRead(cpu->bus, 0xFFFF) << 8);
        cpu->pc = (uint16_t)BusRead(cpu->bus, 0xFFFE, false) | ((uint16_t)BusRead(cpu->bus, 0xFFFF, false) << 8);
        return 0;
}



@@ 1151,7 1160,7 @@ uint8_t PHP(struct cpu *cpu) {
//! \return 0 This instruction will take no additional cycles
uint8_t PLA(struct cpu *cpu) {
        cpu->sp++;
        cpu->a = BusRead(cpu->bus, 0x0100 + cpu->sp);
        cpu->a = BusRead(cpu->bus, 0x0100 + cpu->sp, false);
        SetFlag(cpu, Z, cpu->a == 0x00);
        SetFlag(cpu, N, cpu->a & 0x80);
        return 0;


@@ 1165,7 1174,7 @@ uint8_t PLA(struct cpu *cpu) {
//! \return 0 This instruction will take no additional cycles
uint8_t PLP(struct cpu *cpu) {
        cpu->sp++;
        cpu->status = BusRead(cpu->bus, 0x0100 + cpu->sp);
        cpu->status = BusRead(cpu->bus, 0x0100 + cpu->sp, false);
        SetFlag(cpu, U, 1);
        return 0;
}


@@ 1217,14 1226,14 @@ uint8_t ROR(struct cpu *cpu) {
//! \return 0 This instruction will take no additional cycles
uint8_t RTI(struct cpu *cpu) {
        cpu->sp++;
        cpu->status = BusRead(cpu->bus, 0x0100 + cpu->sp);
        cpu->status = BusRead(cpu->bus, 0x0100 + cpu->sp, false);
        cpu->status &= ~B;
        cpu->status &= ~U;

        cpu->sp++;
        cpu->pc = (uint16_t)BusRead(cpu->bus, 0x0100 + cpu->sp);
        cpu->pc = (uint16_t)BusRead(cpu->bus, 0x0100 + cpu->sp, false);
        cpu->sp++;
        cpu->pc |= (uint16_t)BusRead(cpu->bus, 0x0100 + cpu->sp) << 8;
        cpu->pc |= (uint16_t)BusRead(cpu->bus, 0x0100 + cpu->sp, false) << 8;

        return 0;
}


@@ 1235,9 1244,9 @@ uint8_t RTI(struct cpu *cpu) {
//! \return 0 This instruction will take no additional cycles
uint8_t RTS(struct cpu *cpu) {
        cpu->sp++;
        cpu->pc = (uint16_t)BusRead(cpu->bus, 0x0100 + cpu->sp);
        cpu->pc = (uint16_t)BusRead(cpu->bus, 0x0100 + cpu->sp, false);
        cpu->sp++;
        cpu->pc |= (uint16_t)BusRead(cpu->bus, 0x0100 + cpu->sp) << 8;
        cpu->pc |= (uint16_t)BusRead(cpu->bus, 0x0100 + cpu->sp, false) << 8;

        cpu->pc++;
        return 0;


@@ 1395,7 1404,7 @@ uint8_t XXX(struct cpu *cpu) {
//-- Debug Structures ----------------------------------------------------------


char **CpuDebugStateInit(struct cpu *cpu) {
char **CpuDebugStateInit(struct cpu *cpu, int *numLines) {
        char **debug = (char **)malloc(sizeof(char *) * 7);

        debug[0] = "        N V - B D I Z C";


@@ 1430,6 1439,8 @@ char **CpuDebugStateInit(struct cpu *cpu) {
        debug[6] = malloc(strlen("sp: $0000") + 1);
        sprintf(debug[6], "SP: $%04X", cpu->sp);

        *numLines = 7;

        return debug;
}



@@ 1513,7 1524,7 @@ struct disassembly *DisassemblyInit(struct cpu *cpu, uint16_t start, uint16_t st
                strncpy(text_cpy, text, strnlen(text, text_len) + 1);

                // Get the readable name of the instruction.
                uint8_t opcode = BusRead(cpu->bus, addr);
                uint8_t opcode = BusRead(cpu->bus, addr, true);
                instruction = instruction_map[opcode];
                addr++;
                text_len += 4; // instruction.name is 3 chars, plus an extra space.


@@ 1523,70 1534,70 @@ struct disassembly *DisassemblyInit(struct cpu *cpu, uint16_t start, uint16_t st
                if (IMP == instruction.address) {
                        snprintf(text, 256, "%s {IMP}", text_cpy);
                } else if (IMM == instruction.address) {
                        value = BusRead(cpu->bus, addr);
                        value = BusRead(cpu->bus, addr, true);
                        addr++;
                        HexToString(value, 2, hex_buf, hex_buf_len);
                        snprintf(text, 256, "%s#$%s {IMM}", text_cpy, hex_buf);
                } else if (ZP0 == instruction.address) {
                        lo = BusRead(cpu->bus, addr);
                        lo = BusRead(cpu->bus, addr, true);
                        addr++;
                        hi = 0x00;
                        HexToString(lo, 2, hex_buf, hex_buf_len);
                        snprintf(text, 256, "%s$%s {ZP0}", text_cpy, hex_buf);
                } else if (ZPX == instruction.address) {
                        lo = BusRead(cpu->bus, addr);
                        lo = BusRead(cpu->bus, addr, true);
                        addr++;
                        hi = 0x00;
                        HexToString(lo, 2, hex_buf, hex_buf_len);
                        snprintf(text, 256, "%s$%s, X {ZPX}", text_cpy, hex_buf);
                } else if (ZPY == instruction.address) {
                        lo = BusRead(cpu->bus, addr);
                        lo = BusRead(cpu->bus, addr, true);
                        addr++;
                        hi = 0x00;
                        HexToString(lo, 2, hex_buf, hex_buf_len);
                        snprintf(text, 256, "%s$%s, Y {ZPY}", text_cpy, hex_buf);
                } else if (IZX == instruction.address) {
                        lo = BusRead(cpu->bus, addr);
                        lo = BusRead(cpu->bus, addr, true);
                        addr++;
                        hi = 0x00;
                        HexToString(lo, 2, hex_buf, hex_buf_len);
                        snprintf(text, 256, "%s($%s, X) {IZX}", text_cpy, hex_buf);
                } else if (IZY == instruction.address) {
                        lo = BusRead(cpu->bus, addr);
                        lo = BusRead(cpu->bus, addr, true);
                        addr++;
                        hi = 0x00;
                        HexToString(lo, 2, hex_buf, hex_buf_len);
                        snprintf(text, 256, "%s($%s, Y) {IZY}", text_cpy, hex_buf);
                } else if (ABS == instruction.address) {
                        lo = BusRead(cpu->bus, addr);
                        lo = BusRead(cpu->bus, addr, true);
                        addr++;
                        hi = BusRead(cpu->bus, addr);
                        hi = BusRead(cpu->bus, addr, true);
                        addr++;
                        HexToString((uint16_t)(hi << 8) | lo, 4, hex_buf, hex_buf_len);
                        snprintf(text, 256, "%s$%s {ABS}", text_cpy, hex_buf);
                } else if (ABX == instruction.address) {
                        lo = BusRead(cpu->bus, addr);
                        lo = BusRead(cpu->bus, addr, true);
                        addr++;
                        hi = BusRead(cpu->bus, addr);
                        hi = BusRead(cpu->bus, addr, true);
                        addr++;
                        HexToString((uint16_t)(hi << 8) | lo, 4, hex_buf, hex_buf_len);
                        snprintf(text, 256, "%s$%s, X {ABX}", text_cpy, hex_buf);
                } else if (ABY == instruction.address) {
                        lo = BusRead(cpu->bus, addr);
                        lo = BusRead(cpu->bus, addr, true);
                        addr++;
                        hi = BusRead(cpu->bus, addr);
                        hi = BusRead(cpu->bus, addr, true);
                        addr++;
                        HexToString((uint16_t)(hi << 8) | lo, 4, hex_buf, hex_buf_len);
                        snprintf(text, 256, "%s$%s, Y {ABY}", text_cpy, hex_buf);
                } else if (IND == instruction.address) {
                        lo = BusRead(cpu->bus, addr);
                        lo = BusRead(cpu->bus, addr, true);
                        addr++;
                        hi = BusRead(cpu->bus, addr);
                        hi = BusRead(cpu->bus, addr, true);
                        addr++;
                        HexToString((uint16_t)(hi << 8) | lo, 4, hex_buf, hex_buf_len);
                        snprintf(text, 256, "%s($%s) {IND}", text_cpy, hex_buf);
                } else if (REL == instruction.address) {
                        value = BusRead(cpu->bus, addr);
                        value = BusRead(cpu->bus, addr, true);
                        addr++;
                        HexToString(value, 2, hex_buf, hex_buf_len);


M cpu.h => cpu.h +2 -2
@@ 4,7 4,7 @@

  File: cpu.h
  Created: 2019-10-16
  Updated: 2019-11-21
  Updated: 2019-11-27
  Author: Aaron Oman
  Notice: GNU AGPLv3 License



@@ 46,7 46,7 @@ CpuNmi(struct cpu *cpu);
//-- Debug ---------------------------------------------------------------------

char **
CpuDebugStateInit(struct cpu *cpu);
CpuDebugStateInit(struct cpu *cpu, int *numLines);

void
CpuDebugStateDeinit(char **debug);

M main.c => main.c +21 -18
@@ 4,7 4,7 @@

  File: main.c
  Created: 2019-10-31
  Updated: 2019-11-26
  Updated: 2019-11-29
  Author: Aaron Oman
  Notice: GNU AGPLv3 License



@@ 72,6 72,10 @@ void Init() {
                fprintf(stderr, "Couldn't load cart");
                Deinit(1);
        }
        if (!CartIsImageValid(cart)) {
                fprintf(stderr, "Couldn't load cart");
                Deinit(1);
        }

        cpu = CpuInit();
        if (NULL == cpu) {


@@ 132,8 136,9 @@ void Init() {

void DrawCpuState(int x, int y) {
        GraphicsDrawText(graphics, x, y, "CPU State", FONT_HEADER_SCALE, 0x000000FF);
        char **cpu_state = CpuDebugStateInit(cpu);
        for (int i = 0; i < 7; i++) {
        int numLines = 0;
        char **cpu_state = CpuDebugStateInit(cpu, &numLines);
        for (int i = 0; i < numLines; i++) {
                GraphicsDrawText(graphics, x, (y - 15) - (18 * i), cpu_state[i], FONT_SCALE, 0x000000FF);
        }
        CpuDebugStateDeinit(cpu_state);


@@ 170,9 175,7 @@ int main(int argc, char **argv) {
        CpuConnectBus(cpu, bus);
        BusAttachCart(bus, cart);

        // Set reset vector.
        BusWrite(bus, 0xFFFC, 0x00);
        BusWrite(bus, 0xFFFD, 0x80);
        BusReset(bus);

        // Disassemble
        struct disassembly *disassembly = DisassemblyInit(cpu, 0x0000, 0xFFFF);


@@ 196,8 199,15 @@ int main(int argc, char **argv) {
                InputProcess(input);
                isRunning = !InputIsQuitRequested(input);

                if (InputGetKey(input, KEY_SPACE).pressed) isEmulating = !isEmulating;
                if (InputGetKey(input, KEY_R).pressed) BusReset(bus);
                if (InputGetKey(input, KEY_P).pressed) {
                        ++selectedPalette;
                        selectedPalette &= 0x07;
                }

                if (isEmulating) {
                        if (0.0f < residualTime) {
                        if (0.0f <= residualTime) {
                                residualTime -= elapsedTime;
                        } else {
                                residualTime += (1.0 / 60.0) - elapsedTime;


@@ 206,7 216,7 @@ int main(int argc, char **argv) {
                        }
                } else {
                        // Emulate code step-by-step.
                        if (InputIsKeyPressed(input, KEY_C)) {
                        if (InputGetKey(input, KEY_C).pressed) {
                                // Tick enough times to execute a whole CPU instruction.
                                do { BusTick(bus); } while (!CpuIsComplete(cpu));



@@ 217,7 227,7 @@ int main(int argc, char **argv) {
                        }

                        // Emulate one whole frame.
                        if (InputIsKeyPressed(input, KEY_F)) {
                        if (InputGetKey(input, KEY_F).pressed) {
                                // Clock enough times to draw a single frame.
                                do { BusTick(bus); } while (!PpuIsFrameComplete(ppu));



@@ 230,13 240,6 @@ int main(int argc, char **argv) {
                        }
                }

                if (InputGetKey(input, KEY_SPACE).pressed) isEmulating = !isEmulating;
                if (InputGetKey(input, KEY_R).pressed) BusReset(bus);
                if (InputGetKey(input, KEY_P).pressed) {
                        ++selectedPalette;
                        selectedPalette &= 0x07;
                }

                GraphicsDrawLine(graphics, NES_SCREEN_WIDTH, 0, NES_SCREEN_WIDTH, HEIGHT, ColorBlack.rgba);
                DrawCpuState(NES_SCREEN_WIDTH + 10, HEIGHT - (FONT_HEADER_SCALE + 5));



@@ 264,8 267,8 @@ int main(int argc, char **argv) {
                /* for (int y = 0; y < 30; y++) { */
                /*         for (int x = 0; x < 32; x++) { */
                /*                 char buf[5]; */
                /*                 //HexToString(PpuNameTableEntry(ppu, 0)[y * 32 + x], 2, buf, 5); */
                /*                 //GraphicsDrawText(graphics, x * 16, y * 16, buf, 15, ColorBlack.rgba); */
                /*                 HexToString(PpuGetNameTable(ppu, 0)[y * 32 + x], 2, buf, 5); */
                /*                 GraphicsDrawText(graphics, x * 16, y * 16, buf, 15, ColorBlack.rgba); */
                /*         } */
                /* } */


M mapper.h => mapper.h +37 -10
@@ 4,7 4,7 @@

  File: mapper.h
  Created: 2019-11-04
  Updated: 2019-11-09
  Updated: 2019-11-27
  Author: Aaron Oman
  Notice: GNU AGPLv3 License



@@ 14,16 14,43 @@
  conditions; See LICENSE for details.
 ******************************************************************************/
//! \file mapper.h
//! This file describes the `mapper` interface.
//! There are seven function pointers defined that must be provided by any
//! concrete implementation of this interface.
#include <stdint.h>

typedef bool (*map_cpu_read_fn)(void *interface, uint16_t addr, uint32_t *mapped_addr);

typedef bool (*map_cpu_write_fn)(void *interface, uint16_t addr, uint32_t *mapped_addr);

typedef bool (*map_ppu_read_fn)(void *interface, uint16_t addr, uint32_t *mapped_addr);

typedef bool (*map_ppu_write_fn)(void *interface, uint16_t addr, uint32_t *mapped_addr);

typedef void *(*mapper_init_fn)(uint8_t prg_banks, uint8_t chr_banks);
//! \brief Initialize the mapper
//! \return Pointer to mapper
typedef void *(*mapper_init_fn)(uint8_t prgBanks, uint8_t chrBanks);

//! \brief De-initialize the mapper
//! \param[in,out] interface the mapper
typedef void (*mapper_deinit_fn)(void *interface);

//! \brief Reset the mapper
//! \param[in,out] interface the mapper
typedef void (*mapper_reset_fn)(void *interface);

//! \brief CPU read intercept
//! \param[in,out] interface the mapper
//! \param[in] addr 16-bit address to read
//! \param[out] mappedAddr the translated address
typedef bool (*map_cpu_read_fn)(void *interface, uint16_t addr, uint32_t *mappedAddr);

//! \brief CPU write intercept
//! \param[in,out] interface the mapper
//! \param[in] addr 16-bit address to read
//! \param[out] mappedAddr the translated address
typedef bool (*map_cpu_write_fn)(void *interface, uint16_t addr, uint32_t *mappedAddr);

//! \brief PPU read intercept
//! \param[in,out] interface the mapper
//! \param[in] addr 16-bit address to read
//! \param[out] mappedAddr the translated address
typedef bool (*map_ppu_read_fn)(void *interface, uint16_t addr, uint32_t *mappedAddr);

//! \brief PPU write intercept
//! \param[in,out] interface the mapper
//! \param[in] addr 16-bit address to read
//! \param[out] mappedAddr the translated address
typedef bool (*map_ppu_write_fn)(void *interface, uint16_t addr, uint32_t *mappedAddr);

M mapper000.c => mapper000.c +21 -74
@@ 4,7 4,7 @@

  File: mapper000.c
  Created: 2019-11-04
  Updated: 2019-11-09
  Updated: 2019-11-27
  Author: Aaron Oman
  Notice: GNU AGPLv3 License



@@ 19,124 19,71 @@
#include "mapper000.h"

struct mapper000 {
        uint8_t prg_banks;
        uint8_t chr_banks;
        uint8_t prgBanks;
        uint8_t chrBanks;
};

void *Mapper000_Init(uint8_t prg_banks, uint8_t chr_banks) {
void *Mapper000_Init(uint8_t prgBanks, uint8_t chrBanks) {
        struct mapper000 *mapper = (struct mapper000 *)malloc(sizeof(struct mapper000));
        if (NULL == mapper) {
                return NULL;
        }

        mapper->prg_banks = prg_banks;
        mapper->chr_banks = chr_banks;
        mapper->prgBanks = prgBanks;
        mapper->chrBanks = chrBanks;

        return (void *)mapper;
}

void Mapper000_Deinit(void *interface) {
        if (NULL == interface) {
        if (NULL == interface)
                return;
        }

        free(interface);
        interface = NULL;
}

//! \brief Map cpu read address to expanded address
//!
//! If PRGRPM is 16KB:
//!   Cpu Address Bus           PRG ROM
//!   ---------------           ---------
//!   0x8000 -> 0xBFFF: Map     0x0000 -> 0x3FFF
//!   0xC000 -> 0xFFFF: Mirror  0x0000 -> 0x3FFF
//!
//! If PRGRPM is 32KB:
//!   Cpu Address Bus           PRG ROM
//!   ---------------           ---------
//!   0x8000 -> 0xBFFF: Map     0x0000 -> 0x7FFF
//!
//! \see Mapper000_MapCpuWrite
//!
//! \param[in,out] mapper
//! \param[in] addr Address to be mapped
//! \param[out] mapped_addr Mapped address
//! \return true if address has been mapped
bool Mapper000_MapCpuRead(void *interface, uint16_t addr, uint32_t *mapped_addr) {
void Mapper000_Reset(void *mapper) {
}

bool Mapper000_MapCpuRead(void *interface, uint16_t addr, uint32_t *mappedAddr) {
        struct mapper000 *mapper = (struct mapper000 *)interface;
        uint8_t mask = (mapper->prg_banks > 1) ? 0x7FFF : 0x3FFF;
        uint8_t mask = (mapper->prgBanks > 1) ? 0x7FFF : 0x3FFF;

        if (addr >= 0x8000 && addr <= 0xFFFF) {
                *mapped_addr = addr & mask;
                *mappedAddr = addr & mask;
                return true;
        }

        return false;
}

//! \brief Map cpu write address to expanded address
//!
//! If PRGRPM is 16KB:
//!   Cpu Address Bus           PRG ROM
//!   ---------------           ---------
//!   0x8000 -> 0xBFFF: Map     0x0000 -> 0x3FFF
//!   0xC000 -> 0xFFFF: Mirror  0x0000 -> 0x3FFF
//!
//! If PRGRPM is 32KB:
//!   Cpu Address Bus           PRG ROM
//!   ---------------           ---------
//!   0x8000 -> 0xBFFF: Map     0x0000 -> 0x7FFF
//!
//! \see Mapper000_MapCpuRead
//!
//! \param[in,out] mapper
//! \param[in] addr Address to be mapped
//! \param[out] mapped_addr Mapped address
//! \return true if address has been mapped
bool Mapper000_MapCpuWrite(void *interface, uint16_t addr, uint32_t *mapped_addr) {
bool Mapper000_MapCpuWrite(void *interface, uint16_t addr, uint32_t *mappedAddr) {
        struct mapper000 *mapper = (struct mapper000 *)interface;
        uint8_t mask = (mapper->prg_banks > 1) ? 0x7FFF : 0x3FFF;
        uint8_t mask = (mapper->prgBanks > 1) ? 0x7FFF : 0x3FFF;

        if (addr >= 0x8000 && addr <= 0xFFFF) {
                *mapped_addr = addr & mask;
                *mappedAddr = addr & mask;
                return true;
        }

        return false;
}

//! \brief Map ppu read address to expanded address
//!
//! \see Mapper000_MapPpuWrite
//!
//! \param[in,out] mapper
//! \param[in] addr Address to be mapped
//! \param[out] mapped_addr Mapped address
//! \return true if address has been mapped
bool Mapper000_MapPpuRead(void *interface, uint16_t addr, uint32_t *mapped_addr) {
bool Mapper000_MapPpuRead(void *interface, uint16_t addr, uint32_t *mappedAddr) {
        if (addr >= 0x0000 && addr <= 0x1FFF) {
                *mapped_addr = addr;
                *mappedAddr = addr;
                return true;
        }

        return false;
}

//! \brief Map ppu write address to expanded address
//!
//! \see Mapper000_MapPpuRead
//!
//! \param[in,out] mapper
//! \param[in] addr Address to be mapped
//! \param[out] mapped_addr Mapped address
//! \return true if address has been mapped
bool Mapper000_MapPpuWrite(void *interface, uint16_t addr, uint32_t *mapped_addr) {
bool Mapper000_MapPpuWrite(void *interface, uint16_t addr, uint32_t *mappedAddr) {
        struct mapper000 *mapper = (struct mapper000 *)interface;
        if (addr >= 0x0000 && addr <= 0x1FFF) {
                if (0 == mapper->chr_banks) {
                if (0 == mapper->chrBanks) {
                        // Treat as RAM.
                        *mapped_addr = addr;
                        *mappedAddr = addr;
                        return true;
                }
        }

M mapper000.h => mapper000.h +63 -6
@@ 4,7 4,7 @@

  File: mapper000.h
  Created: 2019-11-04
  Updated: 2019-11-09
  Updated: 2019-11-27
  Author: Aaron Oman
  Notice: GNU AGPLv3 License



@@ 20,19 20,76 @@
struct mapper000;

void *
Mapper000_Init(uint8_t prg_banks, uint8_t chr_banks);
Mapper000_Init(uint8_t prgBanks, uint8_t chrBanks);

void
Mapper000_Deinit(void *mapper);

void
Mapper000_Reset(void *mapper);

//! \brief Map cpu read address to expanded address
//!
//! If PRGRPM is 16KB:
//!   Cpu Address Bus           PRG ROM
//!   ---------------           ---------
//!   0x8000 -> 0xBFFF: Map     0x0000 -> 0x3FFF
//!   0xC000 -> 0xFFFF: Mirror  0x0000 -> 0x3FFF
//!
//! If PRGRPM is 32KB:
//!   Cpu Address Bus           PRG ROM
//!   ---------------           ---------
//!   0x8000 -> 0xBFFF: Map     0x0000 -> 0x7FFF
//!
//! \see Mapper000_MapCpuWrite
//!
//! \param[in,out] mapper
//! \param[in] addr Address to be mapped
//! \param[out] mappedAddr Mapped address
//! \return true if address has been mapped
bool
Mapper000_MapCpuRead(void *mapper, uint16_t addr, uint32_t *mapped_addr);
Mapper000_MapCpuRead(void *mapper, uint16_t addr, uint32_t *mappedAddr);

//! \brief Map cpu write address to expanded address
//!
//! If PRGRPM is 16KB:
//!   Cpu Address Bus           PRG ROM
//!   ---------------           ---------
//!   0x8000 -> 0xBFFF: Map     0x0000 -> 0x3FFF
//!   0xC000 -> 0xFFFF: Mirror  0x0000 -> 0x3FFF
//!
//! If PRGRPM is 32KB:
//!   Cpu Address Bus           PRG ROM
//!   ---------------           ---------
//!   0x8000 -> 0xBFFF: Map     0x0000 -> 0x7FFF
//!
//! \see Mapper000_MapCpuRead
//!
//! \param[in,out] mapper
//! \param[in] addr Address to be mapped
//! \param[out] mappedAddr Mapped address
//! \return true if address has been mapped
bool
Mapper000_MapCpuWrite(void *mapper, uint16_t addr, uint32_t *mapped_addr);
Mapper000_MapCpuWrite(void *mapper, uint16_t addr, uint32_t *mappedAddr);

//! \brief Map ppu read address to expanded address
//!
//! \see Mapper000_MapPpuWrite
//!
//! \param[in,out] mapper
//! \param[in] addr Address to be mapped
//! \param[out] mappedAddr Mapped address
//! \return true if address has been mapped
bool
Mapper000_MapPpuRead(void *mapper, uint16_t addr, uint32_t *mapped_addr);
Mapper000_MapPpuRead(void *mapper, uint16_t addr, uint32_t *mappedAddr);

//! \brief Map ppu write address to expanded address
//!
//! \see Mapper000_MapPpuRead
//!
//! \param[in,out] mapper
//! \param[in] addr Address to be mapped
//! \param[out] mappedAddr Mapped address
//! \return true if address has been mapped
bool
Mapper000_MapPpuWrite(void *mapper, uint16_t addr, uint32_t *mapped_addr);
Mapper000_MapPpuWrite(void *mapper, uint16_t addr, uint32_t *mappedAddr);

M ppu.c => ppu.c +182 -58
@@ 4,7 4,7 @@

  File: ppu.c
  Created: 2019-11-03
  Updated: 2019-11-26
  Updated: 2019-11-29
  Author: Aaron Oman
  Notice: GNU AGPLv3 License



@@ 23,8 23,10 @@
#include "color.h"
#include "sprite.h"

//! CHR_ROM startx at 0x1000 / 4096 / 4K
//! CHR_ROM starts at 0x1000 == 4096 == 4K
static const int CHR_ROM = 0x1000;
static const int NAME_TABLE_SIZE = 1024;
static const int PATTERN_TABLE_SIZE = 4096;

union loopy_register {
        struct {


@@ 49,7 51,7 @@ struct ppu {
        uint8_t **patternTables; //[2][4096];
        uint8_t *paletteTables; //[32];

        struct color *palette;
        struct color *palette; //[0x40];
        struct sprite *screen;
        struct sprite **nameTableSprites;
        struct sprite **patternTableSprites;


@@ 120,45 122,87 @@ struct ppu *PpuInit() {
                return NULL;
        }

        ppu->palette = (struct color *)calloc(0x40, sizeof(struct color));
        if (NULL == ppu->palette) {
        ppu->screen = SpriteInit(256, 240);
        if (NULL == ppu->screen) {
                PpuDeinit(ppu);
                return NULL;
        }

        ppu->screen = SpriteInit(256, 240);
        if (NULL == ppu->screen) {
        uint8_t *nameTables = (uint8_t *)malloc(2 * NAME_TABLE_SIZE);
        if (NULL == nameTables) {
                PpuDeinit(ppu);
                return NULL;
        }

        ppu->nameTables = (uint8_t **)calloc(2, 1024);
        ppu->nameTables = (uint8_t **)calloc(2, sizeof(uint8_t *));
        if (NULL == ppu->nameTables) {
                free(nameTables);
                PpuDeinit(ppu);
                return NULL;
        }
        ppu->nameTables[0] = &nameTables[0];
        ppu->nameTables[1] = &nameTables[NAME_TABLE_SIZE];

        ppu->patternTables = (uint8_t **)calloc(2, 4096);
        uint8_t *patternTables = (uint8_t *)malloc(2 * PATTERN_TABLE_SIZE);
        if (NULL == patternTables) {
                PpuDeinit(ppu);
                return NULL;
        }
        ppu->patternTables = (uint8_t **)calloc(2, sizeof(uint8_t *));
        if (NULL == ppu->patternTables) {
                free(patternTables);
                PpuDeinit(ppu);
                return NULL;
        }
        ppu->patternTables[0] = &patternTables[0];
        ppu->patternTables[1] = &patternTables[PATTERN_TABLE_SIZE];

        ppu->paletteTables = (uint8_t *)calloc(32, 1);
        ppu->paletteTables = (uint8_t *)calloc(32, sizeof(uint8_t));
        if (NULL == ppu->paletteTables) {
                PpuDeinit(ppu);
                return NULL;
        }

        ppu->nameTableSprites = (struct sprite **)calloc(2, sizeof(struct sprite *));
        if (NULL == ppu->nameTableSprites) {
                PpuDeinit(ppu);
                return NULL;
        }
        ppu->nameTableSprites[0] = SpriteInit(256, 240);
        if (NULL == ppu->nameTableSprites[0]) {
                free(ppu->nameTableSprites);
                ppu->nameTableSprites = NULL;
                PpuDeinit(ppu);
                return NULL;
        }
        ppu->nameTableSprites[1] = SpriteInit(256, 240);
        // TODO: Error handling
        if (NULL == ppu->nameTableSprites[1]) {
                SpriteDeinit(ppu->nameTableSprites[0]);
                free(ppu->nameTableSprites);
                ppu->nameTableSprites = NULL;
                PpuDeinit(ppu);
                return NULL;
        }

        ppu->patternTableSprites = (struct sprite **)calloc(2, sizeof(struct sprite *));
        if (NULL == ppu->patternTableSprites) {
                PpuDeinit(ppu);
                return NULL;
        }
        ppu->patternTableSprites[0] = SpriteInit(128, 128);
        if (NULL == ppu->patternTableSprites[0]) {
                free(ppu->patternTableSprites);
                ppu->patternTableSprites = NULL;
                PpuDeinit(ppu);
                return NULL;
        }
        ppu->patternTableSprites[1] = SpriteInit(128, 128);
        // TODO: Error handling
        if (NULL == ppu->patternTableSprites[1]) {
                SpriteDeinit(ppu->patternTableSprites[0]);
                free(ppu->patternTableSprites);
                ppu->patternTableSprites = NULL;
                PpuDeinit(ppu);
                return NULL;
        }

        ppu->cycle = 0;
        ppu->addressLatch = 0;


@@ 177,6 221,12 @@ struct ppu *PpuInit() {
        ppu->bgShifterAttribLo = 0;
        ppu->bgShifterAttribHi = 0;

        ppu->palette = (struct color *)calloc(0x40, sizeof(struct color));
        if (NULL == ppu->palette) {
                PpuDeinit(ppu);
                return NULL;
        }

        ppu->palette[0x00] = ColorInitInts(84, 84, 84, 255);
	ppu->palette[0x01] = ColorInitInts(0, 30, 116, 255);
	ppu->palette[0x02] = ColorInitInts(8, 16, 144, 255);


@@ 253,6 303,28 @@ void PpuDeinit(struct ppu *ppu) {
                return;
        }

        if (NULL != ppu->patternTables) {
                free(ppu->patternTables[0]);
                free(ppu->patternTables);
        }

        if (NULL != ppu->nameTables) {
                free(ppu->nameTables[0]);
                free(ppu->nameTables);
        }

        if (NULL != ppu->nameTableSprites) {
                SpriteDeinit(ppu->nameTableSprites[0]);
                SpriteDeinit(ppu->nameTableSprites[1]);
                free(ppu->nameTableSprites);
        }

        if (NULL != ppu->patternTableSprites) {
                SpriteDeinit(ppu->patternTableSprites[0]);
                SpriteDeinit(ppu->patternTableSprites[1]);
                free(ppu->nameTableSprites);
        }

        free(ppu);
}



@@ 353,12 425,16 @@ void UpdateShifters(struct ppu *ppu) {
//! \param[in,out] ppu
void PpuTick(struct ppu *ppu) {
        if (ppu->scanline >= -1 && ppu->scanline < 240) {
                if (0 == ppu->scanline && 0 == ppu->cycle) {
                        ppu->cycle = 1;
                }

                // We've exited vblank/nmi and are ready to start rendering again.
                if (-1 == ppu->scanline && 1 == ppu->cycle) {
                        ppu->status.verticalBlank = 0;
                }

                if ((ppu->cycle >= 2 && ppu->cycle <= 258) || (ppu->cycle >= 321 && ppu->cycle < 338)) {
                if ((ppu->cycle >= 2 && ppu->cycle < 258) || (ppu->cycle >= 321 && ppu->cycle < 338)) {
                        UpdateShifters(ppu);

                        switch ((ppu->cycle - 1) % 8) {


@@ 464,9 540,9 @@ void PpuTick(struct ppu *ppu) {
                                        // attribute byte offset.
                                        uint16_t addr =
                                                0x23C0 |
                                                (ppu->vramAddr.nametableY << 1) |
                                                (ppu->vramAddr.nametableY << 11) |
                                                (ppu->vramAddr.nametableX << 10) |
                                                ((ppu->vramAddr.coarseY >> 1) << 3) |
                                                ((ppu->vramAddr.coarseY >> 2) << 3) |
                                                (ppu->vramAddr.coarseX >> 2);

                                        ppu->bgNextTileAttrib = PpuRead(ppu, addr);


@@ 587,10 663,12 @@ void PpuTick(struct ppu *ppu) {
                // Post render scanline - Do nothing!
        }

        // We've entered vblank/nmi.
        if (241 == ppu->scanline && 1 == ppu->cycle) {
                ppu->status.verticalBlank = 1;
                if (ppu->control.enableNmi) ppu->nmi = true;
        if (ppu->scanline >= 241 && ppu->scanline < 261) {
                // We've entered vblank/nmi.
                if (241 == ppu->scanline && 1 == ppu->cycle) {
                        ppu->status.verticalBlank = 1;
                        if (ppu->control.enableNmi) ppu->nmi = true;
                }
        }

        uint8_t bgPixel = 0x00;


@@ 759,7 837,7 @@ void PpuWriteViaCpu(struct ppu *ppu, uint16_t addr, uint8_t data) {

                case 0x0006: // PPU Address
                        if (0 == ppu->addressLatch) {
                                ppu->tramAddr.reg = (ppu->tramAddr.reg & 0x00FF) | (data << 8);
                                ppu->tramAddr.reg = (uint16_t)((data & 0x3F) << 8) | (ppu->tramAddr.reg & 0x00FF);
                                ppu->addressLatch = 1;
                        } else {
                                ppu->tramAddr.reg = (ppu->tramAddr.reg & 0xFF00) | data;


@@ 777,56 855,85 @@ void PpuWriteViaCpu(struct ppu *ppu, uint16_t addr, uint8_t data) {
        }
}

uint8_t PpuReadViaCpu(struct ppu *ppu, uint16_t addr) {
uint8_t PpuReadViaCpu(struct ppu *ppu, uint16_t addr, bool readOnly) {
        uint8_t data = 0x00;
        addr &= 0x3FFF;

        switch(addr) {
                case 0x0000: // Control
                break;
        if (readOnly) {
                switch(addr) {
                        case 0x0000: // Control
                                data = ppu->control.reg;
                                break;

                case 0x0001: // Mask
                break;
                        case 0x0001: // Mask
                                data = ppu->mask.reg;
                                break;

                case 0x0002: // Status
                        // Only the top 3 bits are actual status data.
                        // The remaining 5 bits are garbage - but _most_likely_
                        // whatever was in the data buffer.
                        data = (ppu->status.reg & 0xE0) | (ppu->dataBuffer & 0x1F);
                        case 0x0002: // Status
                                data = ppu->status.reg;
                                break;

                        // Reading status also clears the vertical blank flag.
                        ppu->status.verticalBlank = 0;
                        case 0x0003: // OAM Address
                                break;

                        // Reading status also resets the address latch.
                        ppu->addressLatch = 0;
                break;
                        case 0x0004: // OAM Data
                                break;

                case 0x0003: // OAM Address
                break;
                        case 0x0005: // Scroll
                                break;

                case 0x0004: // OAM Data
                break;
                        case 0x0006: // PPU Address
                                break;

                case 0x0005: // Scroll
                break;
                        case 0x0007: // PPU Data
                                break;
                }
        } else { // Not ReadOnly
                switch (addr) {
                        case 0x000: // Control - Not Readable
                                break;

                case 0x0006: // PPU Address
                break;
                        case 0x001: // Mask - Not Readable
                                break;

                case 0x0007: // PPU Data
                        // Normal reads are delayed by one cycle, so read the
                        // buffer, then refresh the buffer.
                        data = ppu->dataBuffer;
                        ppu->dataBuffer = PpuRead(ppu, ppu->vramAddr.reg);
                        case 0x002: // Status
                                // Only the top 3 bits are actual status data.
                                // The remaining 5 bits are garbage - but _most_likely_
                                // whatever was in the data buffer.
                                data = (ppu->status.reg & 0xE0) | (ppu->dataBuffer & 0x1F);

                                // Reading status also clears the vertical blank flag.
                                ppu->status.verticalBlank = 0;

                                // Reading status also resets the address latch.
                                ppu->addressLatch = 0;
                                break;

                        case 0x003: // OAM Address
                                break;

                        // However, reading palette data is immediate.
                        if (ppu->vramAddr.reg > 0x3F00) {
                        case 0x004: // OAM Data
                                break;

                        case 0x005: // Scroll - Not Readable
                                break;

                        case 0x006: // PPU Address - Not Readable
                                break;

                        case 0x007: // PPU Data TODO
                                // Normal reads are delayed by one cycle, so read the
                                // buffer, then refresh the buffer.
                                data = ppu->dataBuffer;
                        }
                        ppu->vramAddr.reg += (ppu->control.incrementMode ? 32 : 1);
                break;
        }
                                ppu->dataBuffer = PpuRead(ppu, ppu->vramAddr.reg);

                                // However, reading palette data is immediate.
                                if (ppu->vramAddr.reg >= 0x3F00) {
                                        data = ppu->dataBuffer;
                                }
                                ppu->vramAddr.reg += (ppu->control.incrementMode ? 32 : 1);
                                break;
                }
        }
        return data;
}



@@ 891,7 998,24 @@ struct sprite *PpuGetPatternTable(struct ppu *ppu, uint8_t i, uint8_t palette) {
}

void PpuReset(struct ppu *ppu) {
        // TODO PpuReset()
	ppu->fineX = 0x00;
	ppu->addressLatch = 0x00;
	ppu->dataBuffer = 0x00;
	ppu->scanline = 0;
	ppu->cycle = 0;
	ppu->bgNextTileId = 0x00;
	ppu->bgNextTileAttrib = 0x00;
	ppu->bgNextTileLsb = 0x00;
	ppu->bgNextTileMsb = 0x00;
	ppu->bgShifterPatternLo = 0x0000;
	ppu->bgShifterPatternHi = 0x0000;
	ppu->bgShifterAttribLo = 0x0000;
	ppu->bgShifterAttribHi = 0x0000;
	ppu->status.reg = 0x00;
	ppu->mask.reg = 0x00;
	ppu->control.reg = 0x00;
	ppu->vramAddr.reg = 0x0000;
	ppu->tramAddr.reg = 0x0000;
}

int PpuIsFrameComplete(struct ppu *ppu) {

M ppu.h => ppu.h +3 -2
@@ 4,7 4,7 @@

  File: ppu.h
  Created: 2019-11-03
  Updated: 2019-11-25
  Updated: 2019-11-30
  Author: Aaron Oman
  Notice: GNU AGPLv3 License



@@ 18,6 18,7 @@
#define PPU_VERSION "0.1.0"

#include <stdint.h>
#include <stdbool.h>

struct ppu;
struct cart;


@@ 66,7 67,7 @@ struct color *
PpuGetColorFromPaletteRam(struct ppu *ppu, uint8_t palette, uint8_t pixel);

uint8_t
PpuReadViaCpu(struct ppu *ppu, uint16_t addr);
PpuReadViaCpu(struct ppu *ppu, uint16_t addr, bool readOnly);

void
PpuWriteViaCpu(struct ppu *ppu, uint16_t addr, uint8_t data);