[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.
main
Joaquin 3 years ago
parent a525076a00
commit bb61cc15b0
Signed by: puly
GPG Key ID: 9E9299CD96C65EC6
  1. 8
      Makefile
  2. 37
      bus.c
  3. 3
      bus.h
  4. 2
      cartridge.c
  5. 3
      common.h
  6. 19
      cpu.c
  7. 5
      cpu.h
  8. 123
      ppu.c
  9. 7
      ppu.h

@ -6,11 +6,11 @@ OUTFILE = exec
default: all default: all
all: $(OUTFILE) all:
$(CC) bus.c cpu.c cartridge.c -o $(OUTFILE) $(CC) bus.c cpu.c cartridge.c -o $(OUTFILE)
clean: clean:
$(RM) exec $(RM) $(OUTFILE)
debug $(OUTFILE): debug:
$(CC) $(CFLAGS) bus.c cpu.c cartridge.c -o $(OUTFILE) $(CC) $(CFLAGS) bus.c cpu.c ppu.c cartridge.c -o $(OUTFILE)

37
bus.c

@ -9,8 +9,12 @@ unsigned char bus[BUS_SIZE]; //this is the bus array
#include "cpu.h" #include "cpu.h"
#include "ppu.h"
#include "cartridge.h" #include "cartridge.h"
bool activateCpuNmiBool = false;
void busWrite8(word address, word data){ 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 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 #define PROG_START_ADDR 0xC000
int main(int argc, char * argv[]){ int main(int argc, char * argv[]){
activateCpuNmiBool = false;
CPU * cpu = (CPU*)malloc(sizeof(CPU)); //create new CPU CPU * cpu = (CPU*)malloc(sizeof(CPU)); //create new CPU
#ifdef DEBUG #ifdef DEBUG
if(cpu == NULL){ if(cpu == NULL){
fprintf(stderr, "ERR: Out of RAM!\n"); fprintf(stderr, "ERR: Out of RAM!\n");
@ -130,6 +145,24 @@ int main(int argc, char * argv[]){
PClogFILE = fopen("PClogFILE", "w"); PClogFILE = fopen("PClogFILE", "w");
#endif #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++){ for(long iterations = 0; iterations != 9000; iterations++){
#ifdef DEBUG #ifdef DEBUG
@ -140,6 +173,10 @@ int main(int argc, char * argv[]){
fprintf(stderr, "\n\n----\n%i\n-----", iterations); fprintf(stderr, "\n\n----\n%i\n-----", iterations);
#endif #endif
if(activateCpuNmiBool){
cpuNmi(cpu);
activateCpuNmiBool = false;
}
//RUN THE CPU CLOCK ONE TIME //RUN THE CPU CLOCK ONE TIME
cpuClock(cpu); cpuClock(cpu);
} }

@ -1,8 +1,9 @@
#include "common.h" #include "common.h"
#define BUS_SIZE 0xFFFF #define BUS_SIZE 0xFFFF
void activateCpuNmi();
byte debug_read_do_not_use_pls(word address); byte debug_read_do_not_use_pls(word address);
void busWrite8(word address, word data); void busWrite8(word address, word data);

@ -61,7 +61,7 @@ word mapper000_Read(word address, bool ppu){
} }
}else{ //if PPU }else{ //if PPU
if(Header.CHR_BANKS == 0) return not_handling_this; //if no CHR banks, nothing to mirror 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; else return not_handling_this;
} }
} }

@ -5,6 +5,9 @@
typedef unsigned char byte; //8bit (byte) variable type typedef unsigned char byte; //8bit (byte) variable type
typedef unsigned short word; //16bit (2byte) variable type typedef unsigned short word; //16bit (2byte) variable type
typedef enum {false, true} bool; typedef enum {false, true} bool;
#define KB 0x0400;
#endif #endif
#define COMMONH #define COMMONH

19
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 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){ void cpuClock(CPU * cpu){
#ifdef DEBUG #ifdef DEBUG

@ -63,5 +63,8 @@ void raiseError(unsigned int err, CPU * cpu);
void handleErrors(CPU * cpu); void handleErrors(CPU * cpu);
void printRegisters(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 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

123
ppu.c

@ -1,26 +1,65 @@
#include "ppu.h" #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; address -= 0x2000;
switch(address){ switch(address){
case 0: //ppuctrl case 0: //ppuctrl
Ppu->control.full = data; ppu.control.full = data;
break; break;
case 1: //ppumask case 1: //ppumask
Ppu->mask.full = data; ppu.mask.full = data;
break; break;
case 2: //ppustatus case 2: //ppustatus
Ppu->status.full = data; ppu.status.full = data;
break; break;
@ -34,59 +73,63 @@ void ppuWrite(PPU * Ppu, word address, byte data){
case 5: //ppuScroll case 5: //ppuScroll
if(Ppu->scroll.expectingY){ if(ppu.scroll.expectingY){
Ppu->scroll.y = data; ppu.scroll.y = data;
}else{ }else{
Ppu->scroll.x = data; ppu.scroll.x = data;
} }
Ppu->scroll.expectingY = !Ppu->scroll.expectingY; ppu.scroll.expectingY = !ppu.scroll.expectingY;
break; break;
case 6: //ppuAddr case 6: //ppuAddr
if(Ppu->address.expectLsb){ if(ppu.address.expectLsb){
Ppu->address.lsb = data; ppu.address.lsb = data;
}else{ }else{
Ppu->address.msb = data; ppu.address.msb = data;
} }
Ppu->address.expectLsb = !Ppu->address.expectLsb; ppu.address.expectLsb = !ppu.address.expectLsb;
break; break;
case 7: //ppuData case 7: //ppuData
Ppu->bus[Ppu->address.complete] = data; ppuWrite(ppu.address.complete, data);
if(Ppu->control.vramIncrement) Ppu->address.complete += 32; if(ppu.control.vramIncrement) ppu.address.complete += 32;
else Ppu->address.complete++; else ppu.address.complete++;
break; 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; address -= 0x2000;
byte returnData;
switch(address){ switch(address){
case 0: //ppuctrl case 0: //ppuctrl
return Ppu->control.full; return ppu.control.full;
break; break;
case 1: //ppumask case 1: //ppumask
return Ppu->mask.full; return ppu.mask.full;
break; break;
case 2: //ppustatus case 2: //ppustatus
return Ppu->status.full; return ppu.status.full;
Ppu->status.vblank = 0; ppu.status.vblank = 0;
ppu.address.expectLsb = false;
break; break;
@ -113,18 +156,42 @@ byte ppuRead(PPU * Ppu, word address){ //send the registers to the bus so the co
case 7: //ppuData 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; break;
} }
} }
void initPpu(PPU * Ppu){ void dumpPpuBus(){
Ppu->address.expectLsb = 0;
Ppu->scroll.expectingY = 0; for(int i = 0; i < 0x3FFF; i++){
Ppu->control.full = 0; printf("%02X ", ppuRead(i));
Ppu->dataByteBuffer = 0; }
Ppu->mask.full = 0;
Ppu->status.full = 0; }
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
} }

@ -97,12 +97,11 @@ typedef struct{
byte expectLsb : 1; byte expectLsb : 1;
}address; }address;
byte bus[0x3FFF];
byte dataByteBuffer; byte dataByteBuffer;
}PPU; }PPU;
void initPpu(); void initPpu();
void dumpPpuBus();
void ppuWrite(word address, byte data); void ppuRegWrite(word address, byte data);
byte ppuRead(word address); byte ppuRegRead(word address);

Loading…
Cancel
Save