Replace printf/fprintf with own print_info() / print_error() using SDL_log
[mercenary-reloaded.git] / src / mercenary / main.c
index 10d002a..c15f23e 100644 (file)
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
-#include <endian.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include "../libsdl/sdl.h"
 #include "../libsdl/opengl.h"
+#include "../libsdl/print.h"
 #include "../libcpu/m68k.h"
 #include "../libcpu/execute.h"
 #include "../libvideo/video.h"
@@ -36,7 +36,7 @@
 
 static int config_amiga_speed = 1;
 static const char *config_gamesave_dir = ".mercenary";
-static int config_video_filter = 0;
+static int config_video_filter = 1;
 static int config_audio_filter = 1;
 
 #define CPU_SPEED      7093790.0;
@@ -72,6 +72,7 @@ static float *sound_buffer = NULL; /* sound buffer memory */
 static int sound_buffer_size; /* buffer sample size */
 static int sound_buffer_writep; /* write pointer at buffer */
 static int sound_buffer_readp; /* read pointer at buffer */
+static int double_size = 1; /* render in double size, so each pixle is 2*2 pixles wide */
 
 static const char *home_dir;
 
@@ -83,20 +84,20 @@ int parse_args(int argc, char *argv[])
 
        while (argc > i) {
                if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
-                       printf("Usage: %s\n", argv[0]);
-                       printf(" -s --amiga-speed original | full\n");
-                       printf("        Set speed of rendering to original Amiga or full speed.\n");
-                       printf(" -v --video-filter on | off\n");
-                       printf("        Set video filter.\n");
-                       printf(" -a --audio-filter on | off\n");
-                       printf("        Set audio filter.\n");
+                       print_info("Usage: %s\n", argv[0]);
+                       print_info(" -s --amiga-speed original | full\n");
+                       print_info("        Set speed of rendering to original Amiga or full speed.\n");
+                       print_info(" -v --video-filter on | off\n");
+                       print_info("        Set video filter.\n");
+                       print_info(" -a --audio-filter on | off\n");
+                       print_info("        Set audio filter.\n");
                        return -1;
                } else
                if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--amiga-speed")) {
                        i++;
                        if (argc == i) {
 missing_parameter:
-                               printf("Missing parameter, use '--help'!\n");
+                               print_info("Missing parameter, use '--help'!\n");
                                return -1;
                        }
                        if (!strcmp(argv[i], "original"))
@@ -106,7 +107,7 @@ missing_parameter:
                                config_amiga_speed = 0;
                        else {
 illegal_parameter:
-                               printf("Illegal parameter, use '--help'!\n");
+                               print_info("Illegal parameter, use '--help'!\n");
                                return -1;
                        }
                } else
@@ -134,7 +135,7 @@ illegal_parameter:
                        else
                                goto illegal_parameter;
                } else {
-                       printf("Illegal option '%s', use '--help'!\n", argv[i]);
+                       print_info("Illegal option '%s', use '--help'!\n", argv[i]);
                        return -1;
                }
                i++;
@@ -143,13 +144,17 @@ illegal_parameter:
        return 0;
 }
 
+static void special_event(int event)
+{
+}
+
 static void main_loop(void)
 {
        int had_first_irq = 0;
        static uint32_t current_time, last_time = 0, diff;
        int i, rc;
        int space, length;
-       int cycle_count;
+       int cycle_count, event;
        double render_delay = 0.0;
        uint32_t palette_address;
        uint16_t palette[16];
@@ -161,8 +166,14 @@ static void main_loop(void)
                /* STEP 1: let the CPU render/process the game */
                /* don't render if we still delay */
                if (!render_delay) {
-                       /* execute until the rendered image is ready */
-                       cycle_count = execute_cpu(0, mercenary_stop_at);
+                       /* execute until the rendered image is ready (wait for VBL) */
+                       cycle_count = 0;
+                       do {
+                               cycle_count += execute_cpu(0, mercenary_stop_at, &event);
+                               /* handle special events */
+                               if (event != STOP_AT_WAIT_VBL)
+                                       special_event(event);
+                       } while (event != STOP_AT_WAIT_VBL);
                        /* copy palette */
                        palette_address = mercenary_palette();
                        for (i = 0; i < 16; i++)
@@ -177,7 +188,7 @@ static void main_loop(void)
                        /* render game view without benson
                         * because benson is not updated before VBL IRQ, we don't want old image from double buffer
                         */
-                       emul_video(image, memory, palette, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DIWSTART, chipreg, 0, BENSON_AT_LINE);
+                       emul_video(image, memory, palette, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DIWSTART, chipreg, 0, BENSON_AT_LINE, double_size);
                }
                rc = event_sdl();
                if (rc)
@@ -194,13 +205,13 @@ static void main_loop(void)
                        last_time = current_time - 1000 * SOUND_CHUNKS / IRQ_RATE;
                }
                while (diff > 1000 / IRQ_RATE) {
-                       execute_cpu(3, NULL);
-                       execute_cpu(4, NULL);
+                       execute_cpu(3, NULL, NULL);
+                       execute_cpu(4, NULL, NULL);
                        had_first_irq = 1;
                        /* render benson without game view
                         * because we only got benson refreshed during VBL IRQ
                         */
-                       emul_video(image, memory, palette, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DIWSTART, chipreg, BENSON_AT_LINE, IMAGE_HEIGHT);
+                       emul_video(image, memory, palette, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DIWSTART, chipreg, BENSON_AT_LINE, IMAGE_HEIGHT, double_size);
                        /* render sound to sound buffer
                         * buffer pointer read and write is atomic, so no locking required!
                         */
@@ -240,143 +251,131 @@ static void main_loop(void)
        }
 }
 
-int main(int argc, char *argv[])
+static uint16_t io_read(uint32_t address)
 {
-       int rc;
-       int sdl_sound_chunk;
-
-       home_dir = getenv("HOME");
-       if (!home_dir)
-               home_dir = "";
-
-       rc = parse_args(argc, argv);
-       if (rc)
-               return 0;
+       uint16_t value = 0xffff;
 
-       /* allocate image */
-       image = calloc(IMAGE_WIDTH * IMAGE_HEIGHT, 3);
-       if (!image) {
-               fprintf(stderr, "Failed to allocate image buffer\n");
-               goto done;
-       }
+       /* joystick and fire button */
+       if (address == 0xbfe000 || address == 0xdff00c)
+               value &= emulate_joystick_read(address);
+       /* keyboard */
+       if (address == 0xbfec00 || address == 0xbfed00 || address == 0xbfee00)
+               value &= emulate_keyboard_read(address);
+       /* diskette */
+       if (address == 0xbfd100 || address == 0xbfe000 || address == 0xdff01a || address == 0xdff01e)
+               value &= emulate_disk_read(address);
 
-       if ((SOUND_SAMPLERATE % IRQ_RATE)) {
-               fprintf(stderr, "Sample rate must be a multiple of IRQ rate, please fix!\n");
-               goto done;
-       }
-       if ((1000 % IRQ_RATE)) {
-               fprintf(stderr, "1000 (Ticks per second) rate must be a multiple of IRQ rate, please fix!\n");
-               goto done;
-       }
+       return value;
+}
 
-       /* calculate SDL chunk size for audio
-        * it must be a power of two, but not more than the chunk size for each IRQ!
-        */
-       for (sdl_sound_chunk = 2; sdl_sound_chunk <= (SOUND_SAMPLERATE / IRQ_RATE); sdl_sound_chunk <<= 1)
-               ;
-       sdl_sound_chunk >>= 1;
-//     printf("samples per IRQ = %d, samples per SDL audio = %d\n", SOUND_SAMPLERATE / IRQ_RATE, sdl_sound_chunk); exit(0);
+static void io_write(uint32_t address, uint16_t value)
+{
+       /* dmacon and sound registers */
+       if (address == 0xdff096 || (address >= 0xdff0a0 && address <= 0xdff0df))
+               emulate_sound_write(address, value, SOUND_SAMPLERATE);
+       if (address == 0xbfd100 || (address >= 0xdff020 && address <= 0xdff024))
+               emulate_disk_write(address, value);
+}
 
-       /* allocate sound buffers */
-       sound_buffer_size = SOUND_SAMPLERATE / IRQ_RATE * SOUND_CHUNKS;
-       sound_buffer = calloc(sound_buffer_size, sizeof(*sound_buffer));
-       if (!sound_buffer) {
-               fprintf(stderr, "Failed to allocate image buffer\n");
-               goto done;
-       }
+/* two tracks with 0x1820 words of length */
+static uint8_t game_save[2][0x1820 << 1];
+static int last_track = 0;
 
-       /* allocate memory */
-       memory = calloc(MEMORY_SIZE, 1);
-       if (!memory) {
-               fprintf(stderr, "Failed to allocate cpu's memory\n");
-               goto done;
-       }
-       chipreg = calloc(IOSIZE, 1);
-       if (!chipreg) {
-               fprintf(stderr, "Failed to allocate chip register\n");
-               goto done;
+/* game reads track with saved game */
+static void disk_read(int track, int __attribute__((unused)) side, uint32_t data, uint16_t length)
+{
+       if (length > sizeof(game_save[0])) {
+               print_error("loading game state failed, because length exceeds game save data size, please fix!\n");
+               return;
        }
 
-       /* init cpu code */
-       execute_init(MEMORY_SIZE, memory, chipreg);
-
-       /* load binary */
-       mercenary_load();
-
-       /* patch some stuff */
-       mercenary_patch();
-
-       /* init SDL and OpenGL */
-       rc = init_sdl(argv[0], SCREEN_WIDTH, SCREEN_HEIGHT, SOUND_SAMPLERATE, sdl_sound_chunk);
-       if (rc < 0)
-               goto done;
-       rc = init_opengl(IMAGE_WIDTH, IMAGE_HEIGHT);
-       if (rc < 0)
-               goto done;
-       resize_opengl(SCREEN_WIDTH, SCREEN_HEIGHT);
-
-       /* init audio filter */
-       sound_init_filter(SOUND_SAMPLERATE);
+       /* if even track is selected, load game */
+       if (!(track & 1)) {
+               char filename[256];
+               int gamesave_num = (track - 2) >> 1;
+               int got;
+               FILE *fp;
 
-       /* start cpu */
-       reset_cpu();
+               memset(game_save, 0, sizeof(game_save)); /* clear so make the game fail, if we fail */
+#if defined(_WIN32)
+               filename[0] = '\0';
+#else
+               sprintf(filename, "%s/%s/", home_dir, config_gamesave_dir);
+               mkdir(filename, 0777);
+#endif
+               sprintf(filename + strlen(filename), "%d%s", gamesave_num, mercenary_gamesavesuffix);
+               fp = fopen(filename, "r");
+               if (!fp) {
+fail:
+                       print_error("failed to load game from '%s'\n", filename);
+                       goto copy;
+               }
+               got = fread(game_save, sizeof(game_save[0]), 2, fp);
+               fclose(fp);
+               if (got != 2)
+                       goto fail;
+       }
 
-       printf("**********************************\n");
-       printf("* Welcome to Mercenary Reloaded! *\n");
-       printf("**********************************\n\n");
-       printf("Press CTRL + cursor keys to select inventory or pickup/drop item.\n");
-       printf("Press CTRL + f to toggle full screen.\n");
-       printf("Press CTRL + s to toggle rendering speed.\n");
-       printf("Press CTRL + v to toggle video filter.\n");
-       printf("Press CTRL + a to toggle audio filter.\n");
-       printf("Press CTRL + c to exit game.\n\n");
-       printf("Use '--help' as command line option for configuration settings.\n\n");
+copy:
+       /* copy track */
+       memcpy(memory + data, game_save[track & 1], length /* sizeof(game_save[0])*/);
+}
 
-       /* run game */
-       main_loop();
+/* game writes track with saved game */
+static void disk_write(int track, int __attribute__((unused)) side, uint32_t data, uint16_t length)
+{
+       /* skip sync info that is provided by game and only relevant for a real disk track */
+       data += 0x200;
+       length -= 0x200;
 
-done:
-       exit_opengl();
-       exit_sdl();
+       if (length != sizeof(game_save[0])) {
+               print_error("saving game state failed, because length of data does not match, please fix!\n");
+               return;
+       }
 
-       if (chipreg)
-               free(chipreg);
-       if (memory)
-               free(memory);
-       if (sound_buffer)
-               free(sound_buffer);
-       if (image)
-               free(image);
+       /* don't save if last track is the same, because disk is written on both sides with the same data */
+       if (track == last_track) {
+               if (memcmp(memory + data, game_save[track & 1], length)) {
+                       print_error("saving game data on other side of the disk is different, please fix!\n");
+               }
+               return;
+       }
+       last_track = track;
 
-       return 0;
-}
+       /* save game data */
+       memcpy(game_save[track & 1], memory + data, length);
 
-void audio_sdl(float *data, int length)
-{
-       int fill, s;
+       /* if done with saving */
+       if ((track & 1)) {
+               char filename[256];
+               int gamesave_num = (track - 2) >> 1;
+               int wrote;
+               FILE *fp;
 
-       /* read sound from sound buffer
-        * buffer pointer read and write is atomic, so no locking required!
-        */
-       fill = (sound_buffer_writep - sound_buffer_readp + sound_buffer_size) % sound_buffer_size;
-       if (fill < length) {
-#ifdef DEBUG_SOUND_BUFFERING
-               fprintf(stderr, "sound buffer underrun\n");
+#if defined(_WIN32)
+               filename[0] = '\0';
+#else
+               sprintf(filename, "%s/%s/", home_dir, config_gamesave_dir);
+               mkdir(filename, 0777);
 #endif
-               /* correct read pointer as if the buffer would have 'length' of samples stored inside */
-               sound_buffer_readp = (sound_buffer_readp + fill - length + sound_buffer_size) % sound_buffer_size;
+               sprintf(filename + strlen(filename), "%d%s", gamesave_num, mercenary_gamesavesuffix);
+               fp = fopen(filename, "w");
+               if (!fp) {
+fail:
+                       print_error("failed to save game to '%s'\n", filename);
+                       return;
+               }
+               print_info("Game state saved to '%s'\n", filename);
+               wrote = fwrite(game_save, sizeof(game_save[0]), 2, fp);
+               fclose(fp);
+               if (wrote != 2)
+                       goto fail;
        }
-       for (s = 0; s < length; s++)
-               *data++ = sound_buffer[(sound_buffer_readp + s) % sound_buffer_size];
-#ifdef DEBUG_SOUND_BUFFERING
-       printf("fill %d = %.4f\n", length, sound_buffer[sound_buffer_readp]);
-#endif
-       sound_buffer_readp =(sound_buffer_readp + length) % sound_buffer_size;
 }
 
 static int shift = 0, ctrl = 0;
 
-void keyboard_sdl(int down, SDL_Keycode sym)
+static void keyboard_sdl(int down, SDL_Keycode sym)
 {
        switch (sym) {
        case SDLK_LCTRL:
@@ -386,19 +385,19 @@ void keyboard_sdl(int down, SDL_Keycode sym)
        case SDLK_v:
                if (down && ctrl) {
                        config_video_filter ^= 1;
-                       printf("video filter: %s\n", (config_video_filter) ? "on" : "off");
+                       print_info("video filter: %s\n", (config_video_filter) ? "on" : "off");
                }
                break;
        case SDLK_a:
                if (down && ctrl) {
                        config_audio_filter ^= 1;
-                       printf("audio filter: %s\n", (config_audio_filter) ? "on" : "off");
+                       print_info("audio filter: %s\n", (config_audio_filter) ? "on" : "off");
                }
                break;
        case SDLK_s:
                if (down && ctrl) {
                        config_amiga_speed ^= 1;
-                       printf("amiga speed: %s\n", (config_amiga_speed) ? "original" : "full");
+                       print_info("amiga speed: %s\n", (config_amiga_speed) ? "original" : "full");
                }
                break;
        case SDLK_c:
@@ -534,114 +533,140 @@ void keyboard_sdl(int down, SDL_Keycode sym)
        }
 }
 
-uint16_t emulate_io_read(uint32_t address)
+void audio_sdl(float *data, int length)
 {
-       uint16_t value = 0xffff;
-
-       /* joystick and fire button */
-       if (address == 0xbfe000 || address == 0xdff00c)
-               value &= emulate_joystick_read(address);
-       /* keyboard */
-       if (address == 0xbfec00 || address == 0xbfed00 || address == 0xbfee00)
-               value &= emulate_keyboard_read(address);
-       /* diskette */
-       if (address == 0xbfd100 || address == 0xbfe000 || address == 0xdff01a || address == 0xdff01e)
-               value &= emulate_disk_read(address);
+       int fill, s;
 
-       return value;
+       /* read sound from sound buffer
+        * buffer pointer read and write is atomic, so no locking required!
+        */
+       fill = (sound_buffer_writep - sound_buffer_readp + sound_buffer_size) % sound_buffer_size;
+       if (fill < length) {
+#ifdef DEBUG_SOUND_BUFFERING
+               fprintf(stderr, "sound buffer underrun\n");
+#endif
+               /* correct read pointer as if the buffer would have 'length' of samples stored inside */
+               sound_buffer_readp = (sound_buffer_readp + fill - length + sound_buffer_size) % sound_buffer_size;
+       }
+       for (s = 0; s < length; s++)
+               *data++ = sound_buffer[(sound_buffer_readp + s) % sound_buffer_size];
+#ifdef DEBUG_SOUND_BUFFERING
+       printf("fill %d = %.4f\n", length, sound_buffer[sound_buffer_readp]);
+#endif
+       sound_buffer_readp =(sound_buffer_readp + length) % sound_buffer_size;
 }
 
-void emulate_io_write(uint32_t address, uint16_t value)
+int main(int argc, char *argv[])
 {
-       /* dmacon and sound registers */
-       if (address == 0xdff096 || (address >= 0xdff0a0 && address <= 0xdff0df))
-               emulate_sound_write(address, value, SOUND_SAMPLERATE);
-       if (address == 0xbfd100 || (address >= 0xdff020 && address <= 0xdff024))
-               emulate_disk_write(address, value);
-}
-
-/* two tracks with 0x1820 words of length */
-static uint8_t game_save[2][0x1820 << 1];
-static int last_track = 0;
+       int rc;
+       int sdl_sound_chunk;
 
-/* game reads track with saved game */
-void disk_read(int track, int __attribute__((unused)) side, uint32_t data, uint16_t length)
-{
-       if (length > sizeof(game_save[0])) {
-               fprintf(stderr, "loading game state failed, because length exceeds game save data size, please fix!\n");
-               return;
-       }
+       home_dir = getenv("HOME");
+       if (!home_dir)
+               home_dir = "";
 
-       /* if even track is selected, load game */
-       if (!(track & 1)) {
-               char filename[256];
-               int gamesave_num = (track - 2) >> 1;
-               int got;
-               FILE *fp;
+       rc = parse_args(argc, argv);
+       if (rc)
+               return 0;
 
-               memset(game_save, 0, sizeof(game_save)); /* clear so make the game fail, if we fail */
-               sprintf(filename, "%s/%s/%d.gamesave", home_dir, config_gamesave_dir, gamesave_num);
-               fp = fopen(filename, "r");
-               if (!fp) {
-fail:
-                       fprintf(stderr, "failed to load game from '%s'\n", filename);
-                       goto copy;
-               }
-               got = fread(game_save, sizeof(game_save[0]), 2, fp);
-               fclose(fp);
-               if (got != 2)
-                       goto fail;
+       /* allocate image */
+       image = calloc(IMAGE_WIDTH * IMAGE_HEIGHT * ((double_size) ? 4 : 1), 3);
+       if (!image) {
+               print_error("Failed to allocate image buffer\n");
+               goto done;
        }
 
-copy:
-       /* copy track */
-       memcpy(memory + data, game_save[track & 1], length /* sizeof(game_save[0])*/);
-}
+       if ((SOUND_SAMPLERATE % IRQ_RATE)) {
+               print_error("Sample rate must be a multiple of IRQ rate, please fix!\n");
+               goto done;
+       }
+       if ((1000 % IRQ_RATE)) {
+               print_error("1000 (Ticks per second) rate must be a multiple of IRQ rate, please fix!\n");
+               goto done;
+       }
 
-/* game writes track with saved game */
-void disk_write(int track, int __attribute__((unused)) side, uint32_t data, uint16_t length)
-{
-       /* skip sync info that is provided by game and only relevant for a real disk track */
-       data += 0x200;
-       length -= 0x200;
+       /* calculate SDL chunk size for audio
+        * it must be a power of two, but not more than the chunk size for each IRQ!
+        */
+       for (sdl_sound_chunk = 2; sdl_sound_chunk <= (SOUND_SAMPLERATE / IRQ_RATE); sdl_sound_chunk <<= 1)
+               ;
+       sdl_sound_chunk >>= 1;
+//     printf("samples per IRQ = %d, samples per SDL audio = %d\n", SOUND_SAMPLERATE / IRQ_RATE, sdl_sound_chunk); exit(0);
 
-       if (length != sizeof(game_save[0])) {
-               fprintf(stderr, "saving game state failed, because length of data does not match, please fix!\n");
-               return;
+       /* allocate sound buffers */
+       sound_buffer_size = SOUND_SAMPLERATE / IRQ_RATE * SOUND_CHUNKS;
+       sound_buffer = calloc(sound_buffer_size, sizeof(*sound_buffer));
+       if (!sound_buffer) {
+               print_error("Failed to allocate image buffer\n");
+               goto done;
        }
 
-       /* don't save if last track is the same, because disk is written on both sides with the same data */
-       if (track == last_track) {
-               if (memcmp(memory + data, game_save[track & 1], length)) {
-                       fprintf(stderr, "saving game data on other side of the disk is different, please fix!\n");
-               }
-               return;
+       /* allocate memory */
+       memory = calloc(MEMORY_SIZE, 1);
+       if (!memory) {
+               print_error("Failed to allocate cpu's memory\n");
+               goto done;
+       }
+       chipreg = calloc(IOSIZE, 1);
+       if (!chipreg) {
+               print_error("Failed to allocate chip register\n");
+               goto done;
        }
-       last_track = track;
 
-       /* save game data */
-       memcpy(game_save[track & 1], memory + data, length);
+       /* init cpu code */
+       execute_init(MEMORY_SIZE, memory, chipreg, io_read, io_write);
 
-       /* if done with saving */
-       if ((track & 1)) {
-               char filename[256];
-               int gamesave_num = (track - 2) >> 1;
-               int wrote;
-               FILE *fp;
+       /* init disk emulation */
+       disk_init(disk_read, disk_write);
 
-               sprintf(filename, "%s/%s", home_dir, config_gamesave_dir);
-               mkdir(filename, 0777);
-               sprintf(filename + strlen(filename), "/%d.gamesave", gamesave_num);
-               fp = fopen(filename, "w");
-               if (!fp) {
-fail:
-                       fprintf(stderr, "failed to save game to '%s'\n", filename);
-                       return;
-               }
-               printf("Game state saved to '%s'\n", filename);
-               wrote = fwrite(game_save, sizeof(game_save[0]), 2, fp);
-               fclose(fp);
-               if (wrote != 2)
-                       goto fail;
-       }
+       /* load binary */
+       mercenary_load();
+
+       /* patch some stuff */
+       mercenary_patch();
+
+       /* init SDL and OpenGL */
+       rc = init_sdl(argv[0], SCREEN_WIDTH, SCREEN_HEIGHT, SOUND_SAMPLERATE, sdl_sound_chunk, keyboard_sdl, audio_sdl);
+       if (rc < 0)
+               goto done;
+       rc = init_opengl((double_size) ? IMAGE_WIDTH * 2 : IMAGE_WIDTH, (double_size) ? IMAGE_HEIGHT * 2 : IMAGE_HEIGHT);
+       if (rc < 0)
+               goto done;
+       resize_opengl(SCREEN_WIDTH, SCREEN_HEIGHT);
+
+       /* init audio filter */
+       sound_init_filter(SOUND_SAMPLERATE);
+
+       /* start cpu */
+       reset_cpu();
+
+       print_info("**********************************\n");
+       print_info("* Welcome to Mercenary Reloaded! *\n");
+       print_info("**********************************\n\n");
+       print_info("Press CTRL + cursor keys to select inventory or pickup/drop item.\n");
+       print_info("Press CTRL + f to toggle full screen.\n");
+       print_info("Press CTRL + s to toggle rendering speed.\n");
+       print_info("Press CTRL + v to toggle video filter.\n");
+       print_info("Press CTRL + a to toggle audio filter.\n");
+       print_info("Press CTRL + c to exit game.\n\n");
+       print_info("Use '--help' as command line option for configuration settings.\n\n");
+
+       /* run game */
+       main_loop();
+
+done:
+       exit_opengl();
+       exit_sdl();
+
+       if (chipreg)
+               free(chipreg);
+       if (memory)
+               free(memory);
+       if (sound_buffer)
+               free(sound_buffer);
+       if (image)
+               free(image);
+
+       return 0;
 }
+