From bb61cc15b08b7d37753d407d4c89be614cd0751e Mon Sep 17 00:00:00 2001 From: Joaquin Date: Mon, 5 Dec 2022 00:34:11 +0200 Subject: [PATCH] [all] bits and pieces in preparation for the PPU Did a bunch of small fixes to the code architecture, most notably wrote the NMI function for the CPU and actually docked the ppu files to the rest of the project. The PPU currently does nothing but process some registers in a basic sense. I (hopefully along with rendev) will start work on the background rendering soon, I also need to fix the registers from their current makeshift solutions to the correct loopy ones, lots of work to be done *sigh*, but the future does seem bright. --- Makefile | 8 ++-- bus.c | 37 ++++++++++++++++ bus.h | 3 +- cartridge.c | 2 +- common.h | 3 ++ cpu.c | 19 ++++++++ cpu.h | 5 ++- ppu.c | 123 ++++++++++++++++++++++++++++++++++++++++------------ ppu.h | 7 ++- 9 files changed, 168 insertions(+), 39 deletions(-) diff --git a/Makefile b/Makefile index ee28b08..4c9c913 100644 --- a/Makefile +++ b/Makefile @@ -6,11 +6,11 @@ OUTFILE = exec default: all -all: $(OUTFILE) +all: $(CC) bus.c cpu.c cartridge.c -o $(OUTFILE) clean: - $(RM) exec + $(RM) $(OUTFILE) -debug $(OUTFILE): - $(CC) $(CFLAGS) bus.c cpu.c cartridge.c -o $(OUTFILE) +debug: + $(CC) $(CFLAGS) bus.c cpu.c ppu.c cartridge.c -o $(OUTFILE) diff --git a/bus.c b/bus.c index 8e72e36..1b97d0b 100644 --- a/bus.c +++ b/bus.c @@ -9,8 +9,12 @@ unsigned char bus[BUS_SIZE]; //this is the bus array #include "cpu.h" +#include "ppu.h" + #include "cartridge.h" +bool activateCpuNmiBool = false; + void busWrite8(word address, word data){ if(!mapper000_Write(address, data, false)){ //first thing we do is we hand the operation to the mapper to resolve any cartridge-side bank switching and mirroring, if the address we wanna write to isnt on the cartridge, we return false and we write to the bus normally @@ -97,10 +101,21 @@ static inline void debug_print_instruction(CPU* __restrict__ cpu, byte opcode){ } +void activateCpuNmi(){ + activateCpuNmiBool = true; +} + + + #define PROG_START_ADDR 0xC000 int main(int argc, char * argv[]){ + + activateCpuNmiBool = false; + CPU * cpu = (CPU*)malloc(sizeof(CPU)); //create new CPU + + #ifdef DEBUG if(cpu == NULL){ fprintf(stderr, "ERR: Out of RAM!\n"); @@ -130,6 +145,24 @@ int main(int argc, char * argv[]){ PClogFILE = fopen("PClogFILE", "w"); #endif + #ifdef DEBUG + //ppu tests + printf("\nCACA\n"); + printf("\nt\n"); + initPpu(); + printf("\nk\n"); + ppuRegWrite(0x2006, 0x00); + printf("\nok\n"); + ppuRegWrite(0x2006, 0x10); + printf("\nok1\n"); + ppuRegWrite(0x2007, 0x22); + printf("\nok2\n"); + ppuRegWrite(0x2007, 0x33); + printf("\nok3\n"); + dumpPpuBus(); + #endif + + for(long iterations = 0; iterations != 9000; iterations++){ #ifdef DEBUG @@ -140,6 +173,10 @@ int main(int argc, char * argv[]){ fprintf(stderr, "\n\n----\n%i\n-----", iterations); #endif + if(activateCpuNmiBool){ + cpuNmi(cpu); + activateCpuNmiBool = false; + } //RUN THE CPU CLOCK ONE TIME cpuClock(cpu); } diff --git a/bus.h b/bus.h index 84742e6..23eec34 100644 --- a/bus.h +++ b/bus.h @@ -1,8 +1,9 @@ #include "common.h" - #define BUS_SIZE 0xFFFF +void activateCpuNmi(); + byte debug_read_do_not_use_pls(word address); void busWrite8(word address, word data); diff --git a/cartridge.c b/cartridge.c index d694f3c..283da3d 100644 --- a/cartridge.c +++ b/cartridge.c @@ -61,7 +61,7 @@ word mapper000_Read(word address, bool ppu){ } }else{ //if PPU if(Header.CHR_BANKS == 0) return not_handling_this; //if no CHR banks, nothing to mirror - else if (address < 0x2000) return CHRROM[address]; + else if (address <= 0x2000) return CHRROM[address - 0x2000]; else return not_handling_this; } } diff --git a/common.h b/common.h index 51f5b28..cdb8722 100644 --- a/common.h +++ b/common.h @@ -5,6 +5,9 @@ typedef unsigned char byte; //8bit (byte) variable type typedef unsigned short word; //16bit (2byte) variable type typedef enum {false, true} bool; + + #define KB 0x0400; + #endif #define COMMONH \ No newline at end of file diff --git a/cpu.c b/cpu.c index 9d81887..f55ca50 100644 --- a/cpu.c +++ b/cpu.c @@ -1910,6 +1910,25 @@ void initCpu(CPU * __restrict__ cpu){ init_opcodereg(cpu);//import the stuff about each microcode, stuff like bytes per instruction, cycles, adressing mode, and operation in the array, where the value in the array is the byte that triggers that action for the CPU } +void cpuNmi(CPU * cpu){ + + //push PC to stack + busWrite8(cpu->SP + STACK_RAM_OFFSET, cpu->PC >> 8); + cpu->SP--; + + busWrite8(cpu->SP + STACK_RAM_OFFSET, cpu->PC & 0x00FF); + cpu->SP--; + + cpu->SR.flags.Break = 0; + cpu->SR.flags.Interrupt = 1; //set flags + cpu->SR.flags.ignored = 1; + + busWrite8(cpu->SP + STACK_RAM_OFFSET, cpu->SR.data); //push status register to stack + cpu->SP--; + + cpu->PC = busRead8(0xFFFE) | (busRead8(0xFFFF) << 8); //read new PC from address +} + void cpuClock(CPU * cpu){ #ifdef DEBUG diff --git a/cpu.h b/cpu.h index de80176..e45b7c9 100644 --- a/cpu.h +++ b/cpu.h @@ -63,5 +63,8 @@ void raiseError(unsigned int err, CPU * cpu); void handleErrors(CPU * cpu); void printRegisters(CPU * cpu); -void cpuClock(CPU * cpu); //tick function + void initCpu(CPU * cpu); //init cpu, allocate memory for opcode register and set flags to initial state +void cpuNmi(CPU * cpu); //process non-maskable interrupt, do not use this to trigger nmi, run the activateCpuNmi function in the bus instead + +void cpuClock(CPU * cpu); //tick function \ No newline at end of file diff --git a/ppu.c b/ppu.c index ba9981a..3516a8a 100644 --- a/ppu.c +++ b/ppu.c @@ -1,26 +1,65 @@ #include "ppu.h" +#include "cartridge.h" //needs acces to the cartridge to load CHR maps, since r/w functions are in-house instead of bus-wide like it is for busRead/Write it needs to be imported here too, quite like there are physical wires connecting the cartridge CHR bank pins to the PPU +byte ppuBus[0x3FFF]; +PPU ppu; -void ppuWrite(PPU * Ppu, word address, byte data){ + +byte ppuRead(word address){ + + word cartResponse; + + if((cartResponse = mapper000_Read(address, true)) == 0x0100){ + + if(address >= 0x3000 && 0x3EFF <= address) //mirrored region + address -= 0x1000; + + if(address >= 0x3F20 && 0x3FFF <= address) //mirrored region + address = (address - 0x3F00) % 0x20 + 0x3F00; + + return ppuBus[address]; + } + + else return cartResponse; + +} + +void ppuWrite(word address, byte data){ + + if(!mapper000_Write(address, data, true)){ + + if(address >= 0x3000 && 0x3EFF <= address) //mirrored region + address -= 0x1000; + + if(address >= 0x3F20 && 0x3FFF <= address) //mirrored region + address = (address - 0x3F00) % 0x20 + 0x3F00; + + ppuBus[address] = data; + } + +} + + +void ppuRegWrite(word address, byte data){ address -= 0x2000; switch(address){ case 0: //ppuctrl - Ppu->control.full = data; + ppu.control.full = data; break; case 1: //ppumask - Ppu->mask.full = data; + ppu.mask.full = data; break; case 2: //ppustatus - Ppu->status.full = data; + ppu.status.full = data; break; @@ -34,59 +73,63 @@ void ppuWrite(PPU * Ppu, word address, byte data){ case 5: //ppuScroll - if(Ppu->scroll.expectingY){ - Ppu->scroll.y = data; + if(ppu.scroll.expectingY){ + ppu.scroll.y = data; }else{ - Ppu->scroll.x = data; + ppu.scroll.x = data; } - Ppu->scroll.expectingY = !Ppu->scroll.expectingY; + ppu.scroll.expectingY = !ppu.scroll.expectingY; break; case 6: //ppuAddr - if(Ppu->address.expectLsb){ - Ppu->address.lsb = data; + if(ppu.address.expectLsb){ + ppu.address.lsb = data; }else{ - Ppu->address.msb = data; + ppu.address.msb = data; } - Ppu->address.expectLsb = !Ppu->address.expectLsb; + ppu.address.expectLsb = !ppu.address.expectLsb; break; case 7: //ppuData - Ppu->bus[Ppu->address.complete] = data; + ppuWrite(ppu.address.complete, data); - if(Ppu->control.vramIncrement) Ppu->address.complete += 32; - else Ppu->address.complete++; + if(ppu.control.vramIncrement) ppu.address.complete += 32; + else ppu.address.complete++; break; } } -byte ppuRead(PPU * Ppu, word address){ //send the registers to the bus so the components can read them +byte ppuRegRead(word address){ //send the registers to the bus so the components can read them address -= 0x2000; + + byte returnData; + switch(address){ case 0: //ppuctrl - return Ppu->control.full; + return ppu.control.full; break; case 1: //ppumask - return Ppu->mask.full; + return ppu.mask.full; break; case 2: //ppustatus - return Ppu->status.full; - Ppu->status.vblank = 0; + return ppu.status.full; + ppu.status.vblank = 0; + ppu.address.expectLsb = false; break; @@ -113,18 +156,42 @@ byte ppuRead(PPU * Ppu, word address){ //send the registers to the bus so the co case 7: //ppuData - //delayed by one cycle + returnData = ppu.dataByteBuffer; //reads are lagged back by one cycle, the data you read is the data of the last query + + ppu.dataByteBuffer = ppuRead(ppu.address.complete); + + if(ppu.address.complete >= 0x3F00 && 0x3FFF <= ppu.address.complete) //except when reading palette info + returnData = ppuRead(ppu.address.complete); //then the query is immediate + + + return returnData; break; } } -void initPpu(PPU * Ppu){ - Ppu->address.expectLsb = 0; - Ppu->scroll.expectingY = 0; - Ppu->control.full = 0; - Ppu->dataByteBuffer = 0; - Ppu->mask.full = 0; - Ppu->status.full = 0; +void dumpPpuBus(){ + + for(int i = 0; i < 0x3FFF; i++){ + printf("%02X ", ppuRead(i)); + } + +} + +void initPpu(){ + ppu.dataByteBuffer = 0; + ppu.address.expectLsb = 0; + ppu.scroll.expectingY = 0; + ppu.control.full = 0; + ppu.dataByteBuffer = 0; + ppu.mask.full = 0; + ppu.status.full = 0; } + +void ppuClock(){ + //one tick of the PPU clock + + + +} \ No newline at end of file diff --git a/ppu.h b/ppu.h index 9e318d7..e4d7a4c 100644 --- a/ppu.h +++ b/ppu.h @@ -97,12 +97,11 @@ typedef struct{ byte expectLsb : 1; }address; - byte bus[0x3FFF]; - byte dataByteBuffer; }PPU; void initPpu(); +void dumpPpuBus(); -void ppuWrite(word address, byte data); -byte ppuRead(word address); \ No newline at end of file +void ppuRegWrite(word address, byte data); +byte ppuRegRead(word address);