#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"
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;
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;
static int quit = 0;
+int parse_args(int argc, char *argv[])
+{
+ int i = 1;
+
+ while (argc > i) {
+ if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
+ 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:
+ print_info("Missing parameter, use '--help'!\n");
+ return -1;
+ }
+ if (!strcmp(argv[i], "original"))
+ config_amiga_speed = 1;
+ else
+ if (!strcmp(argv[i], "full"))
+ config_amiga_speed = 0;
+ else {
+illegal_parameter:
+ print_info("Illegal parameter, use '--help'!\n");
+ return -1;
+ }
+ } else
+ if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--video-filter")) {
+ i++;
+ if (argc == i)
+ goto missing_parameter;
+ if (!strcmp(argv[i], "on"))
+ config_video_filter = 1;
+ else
+ if (!strcmp(argv[i], "off"))
+ config_video_filter = 0;
+ else
+ goto illegal_parameter;
+ } else
+ if (!strcmp(argv[i], "-a") || !strcmp(argv[i], "--audio-filter")) {
+ i++;
+ if (argc == i)
+ goto missing_parameter;
+ if (!strcmp(argv[i], "on"))
+ config_audio_filter = 1;
+ else
+ if (!strcmp(argv[i], "off"))
+ config_audio_filter = 0;
+ else
+ goto illegal_parameter;
+ } else {
+ print_info("Illegal option '%s', use '--help'!\n", argv[i]);
+ return -1;
+ }
+ i++;
+ }
+
+ 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];
/* 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++)
/* 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)
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!
*/
}
}
-int main(int __attribute__((unused)) 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 = "";
+ 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("Welcome to Mercenary Reloaded!\n\n");
- printf("Hold CTRL and use 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");
- printf("\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:
ctrl = down;
break;
case SDLK_v:
- if (down) {
+ if (down && ctrl) {
config_video_filter ^= 1;
- printf("audio filter: %d\n", config_audio_filter);
+ print_info("video filter: %s\n", (config_video_filter) ? "on" : "off");
}
break;
case SDLK_a:
- if (down) {
+ if (down && ctrl) {
config_audio_filter ^= 1;
- printf("video filter: %d\n", config_video_filter);
+ print_info("audio filter: %s\n", (config_audio_filter) ? "on" : "off");
}
break;
case SDLK_s:
- if (down) {
- printf("amiga speed: %d\n", config_amiga_speed);
+ if (down && ctrl) {
config_amiga_speed ^= 1;
+ print_info("amiga speed: %s\n", (config_amiga_speed) ? "original" : "full");
}
break;
case SDLK_c:
}
}
-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;
}
+