diff --git a/README.md b/README.md index d67834b..71c9599 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ This is a CHIP-8 interpreter, built with SDL2. It mimics the CHIP-8 system and i [This general high-level guide](https://tobiasvl.github.io/blog/write-a-chip-8-emulator) is what got me into making this. It was a lot of fun! I suggest you try it too. -[Here](https://github.com/loktar00/chip8/tree/master/roms) is the source of the roms. I do not own or claim ownership of these files. They are only present in this repository for educational purposes. +[Here](https://github.com/loktar00/chip8/tree/master/roms) is the source of the all the roms. I just renamed them to making testing easier. I do not own or claim ownership of these files. They are only present in this repository for educational purposes. ## TODO diff --git a/include/chip8.h b/include/chip8.h index 4a49fac..7dc568c 100644 --- a/include/chip8.h +++ b/include/chip8.h @@ -2,7 +2,7 @@ #define CHIP8_H // CHIP-8 system variables -// (NOTE: chars are 8-bits, shorts are 16-bits) +// NOTE: chars are 8-bits, shorts are 16-bits struct chip8 { unsigned short opcode; // current operation code diff --git a/include/sdlutils.h b/include/sdlutils.h index a858d15..5921301 100644 --- a/include/sdlutils.h +++ b/include/sdlutils.h @@ -8,10 +8,12 @@ typedef struct { SDL_Window *window; SDL_Renderer *renderer; + SDL_AudioDeviceID audio; } SDLComponents; int initSDL(SDLComponents *components); void redrawDisplay(SDLComponents *components, struct chip8 *chip8); void destroySDL(SDLComponents *components); +void playSound(); #endif \ No newline at end of file diff --git a/roms/Clock Program.ch8 b/roms/Clock.ch8 similarity index 100% rename from roms/Clock Program.ch8 rename to roms/Clock.ch8 diff --git a/src/chip8.c b/src/chip8.c index 311013d..017d386 100644 --- a/src/chip8.c +++ b/src/chip8.c @@ -1,6 +1,7 @@ #include #include #include "../include/chip8.h" +#include "../include/sdlutils.h" #define F 0xF // the 16th V register is for the carry flag @@ -169,6 +170,7 @@ void cycle(struct chip8 *chip8) case 0xC000: // CXNN: Sets VX to the result of a bitwise AND operation on a random number and NN // just need to make sure that the random number fits in 8-bits chip8->V[X] = (rand() % 256) & NN; + chip8->pc += 2; break; case 0xD000: // DXYN: Draws a sprite at coordinate (VX, VY) that has a width of 8 pixels and a height of N pixels unsigned short x = chip8->V[X]; @@ -210,6 +212,7 @@ void cycle(struct chip8 *chip8) { case 0x0007: // FX07: Sets VX to the value of the delay timer chip8->V[X] = chip8->delayTimer; + chip8->pc += 2; break; case 0x000A: // FX0A: A key press is awaited, and then stored in VX for (int i = 0; i < 16; i++) @@ -224,17 +227,21 @@ void cycle(struct chip8 *chip8) return; // if no key is pressed, return without incrementing the program counter case 0x0015: // FX15: Sets the delay timer to VX chip8->delayTimer = chip8->V[X]; + chip8->pc += 2; break; case 0x0018: // FX18: Sets the sound timer to VX chip8->soundTimer = chip8->V[X]; + chip8->pc += 2; break; case 0x001E: // FX1E: Adds VX to I chip8->I += chip8->V[X]; + chip8->pc += 2; break; case 0x0029: // FX29: Sets I to the location of the sprite for the character in VX // Characters 0-F (in hexadecimal) are represented by a 4x5 font // so to move the I to the correct location, we need to multiply the value of VX by 5 chip8->I = chip8->V[X] * 0x5; + chip8->pc += 2; break; case 0x0033: // FX33: Stores the binary-coded decimal representation of VX at the addresses I, I plus 1, and I plus 2 chip8->memory[chip8->I] = chip8->V[X] / 100; // fetches the hundreds digit: X00 @@ -272,7 +279,7 @@ void cycle(struct chip8 *chip8) { if (chip8->soundTimer == 1) { - // play a sound + playSound(); } chip8->soundTimer--; } diff --git a/src/main.c b/src/main.c index 75a8774..7bd1558 100644 --- a/src/main.c +++ b/src/main.c @@ -20,7 +20,7 @@ int main() initKeymap(); // load ROM - loadRom(&chip8, "roms/IBMLogo.ch8"); + loadRom(&chip8, "roms/Pong.ch8"); // main loop while (1) @@ -46,7 +46,7 @@ int main() chip8.drawFlag = 0; } - SDL_Delay(100); + SDL_Delay(10); } destroySDL(&components); return 0; diff --git a/src/sdlutils.c b/src/sdlutils.c index 00cc2de..af63ee8 100644 --- a/src/sdlutils.c +++ b/src/sdlutils.c @@ -1,9 +1,15 @@ +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + #include "../include/sdlutils.h" #include "../include/chip8.h" +static SDL_AudioDeviceID *audioPointer; + int initSDL(SDLComponents *components) { - if (SDL_Init(SDL_INIT_VIDEO) < 0) + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); return 1; @@ -31,6 +37,25 @@ int initSDL(SDLComponents *components) return 1; } + // setup audio + SDL_AudioSpec want, have; + SDL_zero(want); + want.freq = 44100; + want.format = AUDIO_S16SYS; + want.channels = 1; + want.samples = 2048; + want.callback = NULL; + + // open audio + components->audio = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0); + if (components->audio == 0) + { + printf("Failed to open audio device! SDL_Error: %s\n", SDL_GetError()); + return 1; + } + + audioPointer = &components->audio; + return 0; } @@ -61,3 +86,28 @@ void destroySDL(SDLComponents *components) SDL_DestroyWindow(components->window); SDL_Quit(); } + +void playSound() +{ + // params + int freq = 440; // frequency + int samples = 2048; // number of samples to generate + int sample_rate = 44100; // sample rate + + // sound generation + int16_t *sound = malloc(samples * sizeof(int16_t)); + for (int i = 0; i < samples; i++) + { + sound[i] = 32767.0 * sin(2.0 * M_PI * freq * i / sample_rate); + } + + // play the sound + SDL_QueueAudio(*audioPointer, sound, samples * sizeof(int16_t)); + SDL_PauseAudioDevice(*audioPointer, 0); + + // wait for the sound to finish playing + SDL_Delay(samples * 1000 / sample_rate); + + // free the sound + free(sound); +} \ No newline at end of file