mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-08-07 06:30:04 +00:00
144 lines
4.3 KiB
C
144 lines
4.3 KiB
C
// Nintendo Entertainment System (NES
|
|
// https://en.wikipedia.org/wiki/Nintendo_Entertainment_System_(Model_NES-101)
|
|
// https://github.com/gregkrsak/first_nes
|
|
#include <nes.h>
|
|
|
|
// Disable audio output
|
|
inline void disableAudioOutput() {
|
|
// Disable APU frame IRQ
|
|
*FR_COUNTER = 0b01000000;
|
|
// Disable digital sound IRQs
|
|
APU->DMC_FREQ = 0b01000000;
|
|
}
|
|
|
|
// Disable video output. This will cause a black screen and disable vblank.
|
|
inline void disableVideoOutput() {
|
|
// Disable vertical blank interrupt
|
|
PPU->PPUCTRL = 0;
|
|
// Disable sprite rendering
|
|
PPU->PPUMASK = 0;
|
|
}
|
|
|
|
// Enable video output. This will enable vblank.
|
|
inline void enableVideoOutput() {
|
|
// Enable vertical blank interrupt
|
|
PPU->PPUCTRL = 0b10000000;
|
|
// Enable sprite and tile rendering
|
|
PPU->PPUMASK = 0b00011000;
|
|
}
|
|
|
|
// Wait for vblank to start
|
|
inline void waitForVBlank() {
|
|
while(!(PPU->PPUSTATUS&0x80)) { }
|
|
}
|
|
|
|
// Clear the vblank flag
|
|
inline void clearVBlankFlag() {
|
|
asm { lda PPU_PPUSTATUS }
|
|
}
|
|
|
|
// Initialize the NES after a RESET.
|
|
// Clears the memory and sets up the stack
|
|
// Note: Calling this will destroy the stack, so it only works if called directly inside the reset routine.
|
|
inline void initNES() {
|
|
// Initialize decimal-mode and stack
|
|
asm {
|
|
cld
|
|
ldx #$ff
|
|
txs
|
|
}
|
|
// Initialize the video & audio
|
|
disableVideoOutput();
|
|
disableAudioOutput();
|
|
// Note: When the system is first turned on or reset, the PPU may not be in a usable state right
|
|
// away. You should wait at least 30,000 (thirty thousand) CPU cycles for the PPU to initialize,
|
|
// which may be accomplished by waiting for 2 (two) vertical blank intervals.
|
|
clearVBlankFlag();
|
|
waitForVBlank();
|
|
// Clear RAM - since it has all variables and the stack it is necesary to do it inline
|
|
char i=0;
|
|
do {
|
|
(MEMORY+0x000)[i] = 0;
|
|
(MEMORY+0x100)[i] = 0;
|
|
(MEMORY+0x200)[i] = 0;
|
|
(MEMORY+0x300)[i] = 0;
|
|
(MEMORY+0x400)[i] = 0;
|
|
(MEMORY+0x500)[i] = 0;
|
|
(MEMORY+0x600)[i] = 0;
|
|
(MEMORY+0x700)[i] = 0;
|
|
} while (++i);
|
|
waitForVBlank();
|
|
// Now the PPU is ready.
|
|
|
|
// Reset the high/low latch to "high"
|
|
asm { lda PPU_PPUSTATUS }
|
|
|
|
}
|
|
|
|
// DMA transfer the entire sprite buffer to the PPU
|
|
// - The Sprite OAM buffer to transfer (must be aligned to $100 in memory)
|
|
inline void ppuSpriteBufferDmaTransfer(struct SpriteData* spriteBuffer) {
|
|
// Set OAM start address to sprite#0
|
|
PPU->OAMADDR = 0;
|
|
// Set the high byte (02) of the RAM address and start the DMA transfer to OAM memory
|
|
APU->OAMDMA = >spriteBuffer;
|
|
}
|
|
|
|
// Prepare for transfering data from the CPU to the PPU
|
|
// - ppuData : Pointer in the PPU memory
|
|
inline void ppuDataPrepare(void* const ppuData) {
|
|
// Write the high byte of PPU address
|
|
PPU->PPUADDR = >ppuData;
|
|
// Write the low byte of PPU address
|
|
PPU->PPUADDR = <ppuData;
|
|
}
|
|
|
|
// Put one byte into PPU memory
|
|
// The byte is put into the current PPU address pointed to by the (autoincrementing) PPU->PPUADDR
|
|
inline void ppuDataPut(char val) {
|
|
// Transfer to PPU
|
|
PPU->PPUDATA = val;
|
|
}
|
|
|
|
// Fill a number of bytes in the PPU memory
|
|
// - ppuData : Pointer in the PPU memory
|
|
// - size : The number of bytes to transfer
|
|
void ppuDataFill(void* const ppuData, char val, unsigned int size) {
|
|
ppuDataPrepare(ppuData);
|
|
// Transfer to PPU
|
|
for(unsigned int i=0;i<size;i++)
|
|
ppuDataPut(val);
|
|
}
|
|
|
|
// Transfer a number of bytes from the CPU memory to the PPU memory
|
|
// - cpuData : Pointer to the CPU memory (RAM of ROM)
|
|
// - ppuData : Pointer in the PPU memory
|
|
// - size : The number of bytes to transfer
|
|
void ppuDataTransfer(void* const ppuData, void* const cpuData, unsigned int size) {
|
|
ppuDataPrepare(ppuData);
|
|
// Transfer to PPU
|
|
char* cpuSrc = (char*)cpuData;
|
|
for(unsigned int i=0;i<size;i++)
|
|
ppuDataPut(*cpuSrc++);
|
|
}
|
|
|
|
// Transfer a 2x2 tile into the PPU memory
|
|
// - ppuData : Pointer in the PPU memory
|
|
// - tile : The tile to transfer
|
|
void ppuDataPutTile(void* const ppuData, char* tile) {
|
|
ppuDataPrepare(ppuData);
|
|
ppuDataPut(tile[0]);
|
|
ppuDataPut(tile[1]);
|
|
ppuDataPrepare((char*)ppuData+32);
|
|
ppuDataPut(tile[2]);
|
|
ppuDataPut(tile[3]);
|
|
}
|
|
|
|
|
|
// Set one byte in PPU memory
|
|
// - ppuData : Pointer in the PPU memory
|
|
// - val : The value to set
|
|
void ppuDataSet(void* const ppuData, char val) {
|
|
ppuDataPrepare(ppuData);
|
|
ppuDataPut(val);
|
|
} |