Skip intro on Damocles
[mercenary-reloaded.git] / src / mercenary / main.c
1 /* main routine
2  *
3  * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
4  * All Rights Reserved
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <stdio.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include "../libsdl/sdl.h"
26 #include "../libsdl/opengl.h"
27 #include "../libsdl/print.h"
28 #include "../libcpu/m68k.h"
29 #include "../libcpu/execute.h"
30 #include "../libvideo/video.h"
31 #include "../libsound/sound.h"
32 #include "../libjoystick/joystick.h"
33 #include "../libkeyboard/keyboard.h"
34 #include "../libdisk/disk.h"
35 #include "mercenary.h"
36 #include "render.h"
37
38 static int config_amiga_speed = 1;
39 static const char *config_gamesave_dir = ".mercenary";
40 static int config_video_filter = 1;
41 static int config_audio_filter = 1;
42 static int config_render = 0;
43 static int config_skip_intro = 0;
44 static int config_debug_opengl = 0;
45
46 #define CPU_SPEED       7093790.0;
47
48 #define SOUND_SAMPLERATE 48000
49 /* - game IRQ rate
50  * - must be used for sound rendering chunk
51  */
52 #define IRQ_RATE        50
53 /* numbe of buffer chunks to dejitter:
54  * - SDL render interval
55  * - sound rendering interval
56  * - sound card interval
57  * 4 should be minimum, if video rate is >=50 Hz
58  */
59 #define SOUND_CHUNKS    4
60
61 /* test sound to check if buffer does not overflow / underrun */
62 //#define DEBUG_SOUND_BUFFERING
63
64 #define IOSIZE          0x1000 /* bytes in io space */
65 #define MEMORY_SIZE     0x80000
66 static uint8_t *memory = NULL;
67 static uint8_t *stop_event = NULL;
68 static uint8_t *image = NULL;
69 #define SCREEN_WIDTH    320
70 #define SCREEN_HEIGHT   200
71 #define IMAGE_WIDTH     320
72 #define IMAGE_HEIGHT    200
73 #define BENSON_AT_LINE  136
74 #define IMAGE_DIWSTART  0x2c
75 static uint16_t *chipreg = NULL;
76 static stereo_t *sound_buffer = NULL; /* sound buffer memory */
77 static int sound_buffer_size; /* buffer sample size */
78 static int sound_buffer_writep; /* write pointer at buffer */
79 static int sound_buffer_readp; /* read pointer at buffer */
80 static int double_size = 1; /* render in double size, so each pixle is 2*2 pixles wide */
81 static int render_legacy = 0; /* render original amiga screen, if set */
82 static int render_improved = 0; /* render improved image, if set */
83 static int render_debug = 0; /* render both, amiga screen and  improved image, if set */
84
85 static const char *home_dir;
86
87 static int quit = 0;
88
89 int parse_args(int argc, char *argv[])
90 {
91         int i = 1;
92
93         while (argc > i) {
94                 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
95                         print_info("Usage: %s\n", argv[0]);
96                         print_info(" -s --amiga-speed original | full\n");
97                         print_info("        Set speed of rendering to original Amiga or full speed.\n");
98                         print_info(" -v --video-filter on | off\n");
99                         print_info("        Set video filter.\n");
100                         print_info(" -a --audio-filter on | off\n");
101                         print_info("        Set audio filter.\n");
102                         print_info(" -r --render original | opegl\n");
103                         print_info("        Set speed of rendering to original Amiga or full speed.\n");
104                         print_info(" -i --skip-intro\n");
105                         print_info("        Skip intro sequence approaching to Eris space port.\n");
106                         print_info("Debug options:\n");
107                         print_info(" -o --debug-opengl\n");
108                         print_info("        Use split screen to debug opengl rendering vs. amiga rendering.\n");
109                         return -1;
110                 } else
111                 if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--amiga-speed")) {
112                         i++;
113                         if (argc == i) {
114 missing_parameter:
115                                 print_info("Missing parameter, use '--help'!\n");
116                                 return -1;
117                         }
118                         if (!strcmp(argv[i], "original"))
119                                 config_amiga_speed = 1;
120                         else
121                         if (!strcmp(argv[i], "full"))
122                                 config_amiga_speed = 0;
123                         else {
124 illegal_parameter:
125                                 print_info("Illegal parameter, use '--help'!\n");
126                                 return -1;
127                         }
128                 } else
129                 if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--video-filter")) {
130                         i++;
131                         if (argc == i)
132                                 goto missing_parameter;
133                         if (!strcmp(argv[i], "on"))
134                                 config_video_filter = 1;
135                         else
136                         if (!strcmp(argv[i], "off"))
137                                 config_video_filter = 0;
138                         else
139                                 goto illegal_parameter;
140                 } else
141                 if (!strcmp(argv[i], "-a") || !strcmp(argv[i], "--audio-filter")) {
142                         i++;
143                         if (argc == i)
144                                 goto missing_parameter;
145                         if (!strcmp(argv[i], "on"))
146                                 config_audio_filter = 1;
147                         else
148                         if (!strcmp(argv[i], "off"))
149                                 config_audio_filter = 0;
150                         else
151                                 goto illegal_parameter;
152                 } else
153                 if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--render")) {
154                         i++;
155                         if (argc == i)
156                                 goto missing_parameter;
157                         if (!strcmp(argv[i], "original"))
158                                 config_render = 0;
159                         else
160                         if (!strcmp(argv[i], "opengl"))
161                                 config_render = 1;
162                         else
163                                 goto illegal_parameter;
164                 } else
165                 if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--skip-intro")) {
166                         config_skip_intro = 1;
167                 } else
168                 if (!strcmp(argv[i], "-o") || !strcmp(argv[i], "--debug-opengl")) {
169                         config_debug_opengl = 1;
170                 } else {
171                         print_info("Illegal option '%s', use '--help'!\n", argv[i]);
172                         return -1;
173                 }
174                 i++;
175         }
176
177         return 0;
178 }
179
180 static void special_event(int event)
181 {
182         if (render_improved)
183                 render_improved_event(event);
184 }
185
186 static void main_loop(void)
187 {
188         int had_first_irq = 0;
189         static uint32_t current_time, last_time = 0, diff;
190         int i, rc;
191         int space, length;
192         int cycle_count, event;
193         double render_delay = 0.0;
194         uint32_t palette_address;
195         uint16_t palette[16];
196
197         last_time = SDL_GetTicks();
198
199         /* render result on window */
200         while (!quit) {
201                 /* handle SDL events */
202                 rc = event_sdl();
203                 if (rc)
204                         break;
205
206                 /* clear screen */
207                 opengl_clear();
208
209                 /* initialize rendering */
210                 render_debug = config_debug_opengl;
211                 render_legacy = (!config_render) || render_debug;
212                 render_improved = config_render || render_debug;
213                 /* start rendering for improved graphics */
214                 if (render_improved)
215                         opengl_viewport_improved(render_debug, (double_size) ? BENSON_AT_LINE * 2 : BENSON_AT_LINE);
216
217                 /* STEP 1: let the CPU render/process the game */
218                 /* don't render if we still delay */
219                 if (!render_delay) {
220                         /* execute until the rendered image is ready (wait for VBL) */
221                         cycle_count = 0;
222                         do {
223                                 cycle_count += execute_cpu(0, &event);
224                                 /* handle special events */
225                                 if (event != STOP_AT_WAIT_VBL)
226                                         special_event(event);
227                         } while (event != STOP_AT_WAIT_VBL);
228                         /* copy palette */
229                         palette_address = mercenary_palette();
230                         for (i = 0; i < 16; i++)
231                                 palette[i] = m68k_read_memory_16(palette_address + i*2);
232                         /* for amiga speed: set delay by the number of cycles */
233                         if (config_amiga_speed)
234                                 render_delay = (double)cycle_count / CPU_SPEED;
235                 }
236
237                 /* STEP 2: transfer legacy image in memory to OpenGL texture */
238                 if (had_first_irq) {
239                         /* render game view without benson
240                          * because benson is not updated before VBL IRQ, we don't want old image from double buffer
241                          */
242                         if (render_legacy)
243                                 emul_video(image, memory, palette, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DIWSTART, chipreg, 0, BENSON_AT_LINE, double_size);
244                 }
245                 /* render benson on improved rendering, if enabled */
246                 if (render_improved)
247                         opengl_render_benson(image, config_video_filter, (double_size) ? BENSON_AT_LINE * 2 : BENSON_AT_LINE);
248                 /* setup viewport for legacy image and render image, if enabled */
249                 if (render_legacy) {
250                         opengl_viewport_legacy(render_debug);
251                         opengl_render_legacy(image, config_video_filter);
252                 }
253                 swap_sdl();
254
255                 /* STEP 3: execute interrupt at rate of 50Hz, render sound */
256                 current_time = SDL_GetTicks();
257                 diff = current_time - last_time;
258                 /* in case of timer glitch, execute IRQ only by maximum number of SOUND_CHUNKS */
259                 if (diff > 1000 * SOUND_CHUNKS / IRQ_RATE) {
260                         diff = 1000 * SOUND_CHUNKS / IRQ_RATE;
261                         last_time = current_time - 1000 * SOUND_CHUNKS / IRQ_RATE;
262                 }
263                 while (diff > 1000 / IRQ_RATE) {
264                         execute_cpu(3, NULL);
265                         execute_cpu(4, NULL);
266                         had_first_irq = 1;
267                         /* render benson without game view
268                          * because we only got benson refreshed during VBL IRQ
269                          */
270                         emul_video(image, memory, palette, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DIWSTART, chipreg, BENSON_AT_LINE, IMAGE_HEIGHT, double_size);
271                         /* render sound to sound buffer
272                          * buffer pointer read and write is atomic, so no locking required!
273                          */
274                         space = (sound_buffer_readp - sound_buffer_writep - 1 + sound_buffer_size) % sound_buffer_size;
275                         if (space < SOUND_SAMPLERATE / IRQ_RATE) {
276 #ifdef DEBUG_SOUND_BUFFERING
277                                 fprintf(stderr, "sound buffer overflow\n");
278 #endif
279                                 length = space;
280                         } else
281                                 length = SOUND_SAMPLERATE / IRQ_RATE;
282 #ifdef DEBUG_SOUND_BUFFERING
283                         printf("can write %d, write %d\n", space, length);
284                         static int cnt = 0;
285                         int s;
286                         for (s = 0; s < length; s++) {
287                                 sound_buffer[(sound_buffer_writep + s) % sound_buffer_size].left =
288                                 sound_buffer[(sound_buffer_writep + s) % sound_buffer_size].right
289                                         = sin(2 * M_PI * 999 * cnt / SOUND_SAMPLERATE)
290                                         * sin(2 * M_PI * 21 * cnt / SOUND_SAMPLERATE)
291                                         * sin(2 * M_PI * 0.5 * cnt / SOUND_SAMPLERATE);
292                                  cnt++;
293                         }
294 #else
295                         render_sound(memory, sound_buffer, sound_buffer_size, sound_buffer_writep, length, config_audio_filter);
296 #endif
297                         sound_buffer_writep = (sound_buffer_writep + length) % sound_buffer_size;
298                         diff -= 1000 / IRQ_RATE;
299                         last_time += 1000 / IRQ_RATE;
300
301                         /* count down render delay */
302                         if (render_delay) {
303                                 render_delay -= 1.0 / (double)IRQ_RATE;
304                                 if (render_delay < 0.0)
305                                         render_delay = 0.0;
306                         }
307                 }
308         }
309 }
310
311 static uint16_t io_read(uint32_t address)
312 {
313         uint16_t value = 0xffff;
314
315         /* joystick and fire button */
316         if (address == 0xbfe000 || address == 0xdff00c)
317                 value &= emulate_joystick_read(address);
318         /* keyboard */
319         if (address == 0xbfec00 || address == 0xbfed00 || address == 0xbfee00)
320                 value &= emulate_keyboard_read(address);
321         /* diskette */
322         if (address == 0xbfd100 || address == 0xbfe000 || address == 0xdff01a || address == 0xdff01e)
323                 value &= emulate_disk_read(address);
324
325         return value;
326 }
327
328 static void io_write(uint32_t address, uint16_t value)
329 {
330         /* dmacon and sound registers */
331         if (address == 0xdff096 || (address >= 0xdff0a0 && address <= 0xdff0df))
332                 emulate_sound_write(address, value, SOUND_SAMPLERATE);
333         if (address == 0xbfd100 || (address >= 0xdff020 && address <= 0xdff024))
334                 emulate_disk_write(address, value);
335 }
336
337 /* two tracks with 0x1820 words of length */
338 static uint8_t game_save[2][0x1820 << 1];
339 static int last_track = 0;
340
341 /* game reads track with saved game */
342 static void disk_read(int track, int __attribute__((unused)) side, uint32_t data, uint16_t length)
343 {
344         if (length > sizeof(game_save[0])) {
345                 print_error("loading game state failed, because length exceeds game save data size, please fix!\n");
346                 return;
347         }
348
349         /* if even track is selected, load game */
350         if (!(track & 1)) {
351                 char filename[256];
352                 int gamesave_num = (track - 2) >> 1;
353                 int got;
354                 FILE *fp;
355
356                 memset(game_save, 0, sizeof(game_save)); /* clear so make the game fail, if we fail */
357 #if defined(_WIN32)
358                 filename[0] = '\0';
359 #else
360                 sprintf(filename, "%s/%s/", home_dir, config_gamesave_dir);
361                 mkdir(filename, 0777);
362 #endif
363                 sprintf(filename + strlen(filename), "%d%s", gamesave_num, mercenary_gamesavesuffix);
364                 fp = fopen(filename, "r");
365                 if (!fp) {
366 fail:
367                         print_error("failed to load game from '%s'\n", filename);
368                         goto copy;
369                 }
370                 got = fread(game_save, sizeof(game_save[0]), 2, fp);
371                 fclose(fp);
372                 if (got != 2)
373                         goto fail;
374         }
375
376 copy:
377         /* copy track */
378         memcpy(memory + data, game_save[track & 1], length /* sizeof(game_save[0])*/);
379 }
380
381 /* game writes track with saved game */
382 static void disk_write(int track, int __attribute__((unused)) side, uint32_t data, uint16_t length)
383 {
384         /* skip sync info that is provided by game and only relevant for a real disk track */
385         data += 0x200;
386         length -= 0x200;
387
388         if (length != sizeof(game_save[0])) {
389                 print_error("saving game state failed, because length of data does not match, please fix!\n");
390                 return;
391         }
392
393         /* don't save if last track is the same, because disk is written on both sides with the same data */
394         if (track == last_track) {
395                 if (memcmp(memory + data, game_save[track & 1], length)) {
396                         print_error("saving game data on other side of the disk is different, please fix!\n");
397                 }
398                 return;
399         }
400         last_track = track;
401
402         /* save game data */
403         memcpy(game_save[track & 1], memory + data, length);
404
405         /* if done with saving */
406         if ((track & 1)) {
407                 char filename[256];
408                 int gamesave_num = (track - 2) >> 1;
409                 int wrote;
410                 FILE *fp;
411
412 #if defined(_WIN32)
413                 filename[0] = '\0';
414 #else
415                 sprintf(filename, "%s/%s/", home_dir, config_gamesave_dir);
416                 mkdir(filename, 0777);
417 #endif
418                 sprintf(filename + strlen(filename), "%d%s", gamesave_num, mercenary_gamesavesuffix);
419                 fp = fopen(filename, "w");
420                 if (!fp) {
421 fail:
422                         print_error("failed to save game to '%s'\n", filename);
423                         return;
424                 }
425                 print_info("Game state saved to '%s'\n", filename);
426                 wrote = fwrite(game_save, sizeof(game_save[0]), 2, fp);
427                 fclose(fp);
428                 if (wrote != 2)
429                         goto fail;
430         }
431 }
432
433 static int shift = 0, ctrl = 0;
434
435 static void keyboard_sdl(int down, SDL_Keycode sym)
436 {
437         switch (sym) {
438         case SDLK_LCTRL:
439         case SDLK_RCTRL:
440                 ctrl = down;
441                 break;
442         case SDLK_v:
443                 if (down && ctrl) {
444                         config_video_filter ^= 1;
445                         print_info("video filter: %s\n", (config_video_filter) ? "on" : "off");
446                 }
447                 break;
448         case SDLK_a:
449                 if (down && ctrl) {
450                         config_audio_filter ^= 1;
451                         print_info("audio filter: %s\n", (config_audio_filter) ? "on" : "off");
452                 }
453                 break;
454         case SDLK_s:
455                 if (down && ctrl) {
456                         config_amiga_speed ^= 1;
457                         print_info("amiga speed: %s\n", (config_amiga_speed) ? "original" : "full");
458                 }
459                 break;
460         case SDLK_r:
461                 if (down && ctrl) {
462                         config_render ^= 1;
463                         printf("render: %s\n", (config_render) ? "opengl" : "original");
464                 }
465                 break;
466         case SDLK_c:
467                 if (down)
468                         quit = 1;
469                 break;
470         }
471
472         switch (sym) {
473         case SDLK_LSHIFT:
474                 set_key("LSH", down);
475                 shift = down;
476                 break;
477         case SDLK_RSHIFT:
478                 set_key("RSH", down);
479                 shift = down;
480                 break;
481         case SDLK_LEFT:
482                 if (shift && down) {
483                         set_key("LF", down);
484                         set_joystick(-1, -1, -1, -1, -1);
485                         break;
486                 }
487                 set_key("LF", 0);
488                 set_joystick(down, -1, -1, -1, -1);
489                 break;
490         case SDLK_RIGHT:
491                 if (shift && down) {
492                         set_key("RT", down);
493                         set_joystick(-1, -1, -1, -1, -1);
494                         break;
495                 }
496                 set_key("RT", 0);
497                 set_joystick(-1, down, -1, -1, -1);
498                 break;
499         case SDLK_UP:
500                 if (shift && down) {
501                         set_key("UP", down);
502                         set_joystick(-1, -1, -1, -1, -1);
503                         break;
504                 }
505                 set_key("UP", 0);
506                 set_joystick(-1, -1, down, -1, -1);
507                 break;
508         case SDLK_DOWN:
509                 if (shift && down) {
510                         set_key("DN", down);
511                         set_joystick(-1, -1, -1, -1, -1);
512                         break;
513                 }
514                 set_key("DN", 0);
515                 set_joystick(-1, -1, -1, down, -1);
516                 break;
517         case SDLK_END:
518                 set_joystick(-1, -1, -1, -1, down);
519                 break;
520
521         case SDLK_a: set_key("A", down); break;
522         case SDLK_b: set_key("B", down); break;
523         case SDLK_c: set_key("C", down); break;
524         case SDLK_d: set_key("D", down); break;
525         case SDLK_e: set_key("E", down); break;
526         case SDLK_f: set_key("F", down); break;
527         case SDLK_g: set_key("G", down); break;
528         case SDLK_h: set_key("H", down); break;
529         case SDLK_i: set_key("I", down); break;
530         case SDLK_j: set_key("J", down); break;
531         case SDLK_k: set_key("K", down); break;
532         case SDLK_l: set_key("L", down); break;
533         case SDLK_m: set_key("M", down); break;
534         case SDLK_n: set_key("N", down); break;
535         case SDLK_o: set_key("O", down); break;
536         case SDLK_p: set_key("P", down); break;
537         case SDLK_q: set_key("Q", down); break;
538         case SDLK_r: set_key("R", down); break;
539         case SDLK_s: set_key("S", down); break;
540         case SDLK_t: set_key("T", down); break;
541         case SDLK_u: set_key("U", down); break;
542         case SDLK_v: set_key("V", down); break;
543         case SDLK_w: set_key("W", down); break;
544         case SDLK_x: set_key("X", down); break;
545         case SDLK_y: set_key("Y", down); break;
546         case SDLK_z: set_key("Z", down); break;
547
548         case SDLK_0: set_key("0", down); break;
549         case SDLK_1: set_key("1", down); break;
550         case SDLK_2: set_key("2", down); break;
551         case SDLK_3: set_key("3", down); break;
552         case SDLK_4: set_key("4", down); break;
553         case SDLK_5: set_key("5", down); break;
554         case SDLK_6: set_key("6", down); break;
555         case SDLK_7: set_key("7", down); break;
556         case SDLK_8: set_key("8", down); break;
557         case SDLK_9: set_key("9", down); break;
558
559         case SDLK_KP_0: set_key("NP0", down); break;
560         case SDLK_KP_1: set_key("NP1", down); break;
561         case SDLK_KP_2: set_key("NP2", down); break;
562         case SDLK_KP_3: set_key("NP3", down); break;
563         case SDLK_KP_4: set_key("NP4", down); break;
564         case SDLK_KP_5: set_key("NP5", down); break;
565         case SDLK_KP_6: set_key("NP6", down); break;
566         case SDLK_KP_7: set_key("NP7", down); break;
567         case SDLK_KP_8: set_key("NP8", down); break;
568         case SDLK_KP_9: set_key("NP9", down); break;
569         case SDLK_KP_DIVIDE: set_key("NPDIV", down); break;
570         case SDLK_KP_MULTIPLY: set_key("NPMUL", down); break;
571         case SDLK_KP_MINUS: set_key("NPSUB", down); break;
572         case SDLK_KP_PLUS: set_key("NPADD", down); break;
573         case SDLK_KP_PERIOD: set_key("NPDEL", down); break;
574         // NPLPAREN and NPRPAREN are not emulated
575
576         case SDLK_F1: set_key("F1", down); break;
577         case SDLK_F2: set_key("F2", down); break;
578         case SDLK_F3: set_key("F3", down); break;
579         case SDLK_F4: set_key("F4", down); break;
580         case SDLK_F5: set_key("F5", down); break;
581         case SDLK_F6: set_key("F6", down); break;
582         case SDLK_F7: set_key("F7", down); break;
583         case SDLK_F8: set_key("F8", down); break;
584         case SDLK_F9: set_key("F9", down); break;
585         case SDLK_F10: set_key("F10", down); break;
586
587         case SDLK_SPACE: set_key("SPC", down); break;
588         case SDLK_BACKSPACE: set_key("BS", down); break;
589         case SDLK_TAB: set_key("TAB", down); break;
590         case SDLK_KP_ENTER: set_key("ENT", down); break;
591         case SDLK_RETURN: set_key("RET", down); break;
592         case SDLK_ESCAPE: set_key("ESC", down); break;
593         case SDLK_DELETE: set_key("DEL", down); break;
594         case SDLK_INSERT: set_key("HELP", down); break;
595
596         }
597 }
598
599 void audio_sdl(float *data, int length)
600 {
601         int fill, s;
602
603         /* read sound from sound buffer
604          * buffer pointer read and write is atomic, so no locking required!
605          */
606         fill = (sound_buffer_writep - sound_buffer_readp + sound_buffer_size) % sound_buffer_size;
607         if (fill < length) {
608 #ifdef DEBUG_SOUND_BUFFERING
609                 fprintf(stderr, "sound buffer underrun\n");
610 #endif
611                 /* correct read pointer as if the buffer would have 'length' of samples stored inside */
612                 sound_buffer_readp = (sound_buffer_readp + fill - length + sound_buffer_size) % sound_buffer_size;
613         }
614         for (s = 0; s < length; s++) {
615                 *data++ = sound_buffer[(sound_buffer_readp + s) % sound_buffer_size].left;
616                 *data++ = sound_buffer[(sound_buffer_readp + s) % sound_buffer_size].right;
617         }
618 #ifdef DEBUG_SOUND_BUFFERING
619         printf("fill %d = %.4f\n", length, sound_buffer[sound_buffer_readp][0]);
620 #endif
621         sound_buffer_readp =(sound_buffer_readp + length) % sound_buffer_size;
622 }
623
624 int main(int argc, char *argv[])
625 {
626         int rc;
627         int sdl_sound_chunk;
628
629         home_dir = getenv("HOME");
630         if (!home_dir)
631                 home_dir = "";
632
633         rc = parse_args(argc, argv);
634         if (rc)
635                 return 0;
636
637         /* allocate image */
638         image = calloc(IMAGE_WIDTH * IMAGE_HEIGHT * ((double_size) ? 4 : 1), 3);
639         if (!image) {
640                 print_error("Failed to allocate image buffer\n");
641                 goto done;
642         }
643
644         if ((SOUND_SAMPLERATE % IRQ_RATE)) {
645                 print_error("Sample rate must be a multiple of IRQ rate, please fix!\n");
646                 goto done;
647         }
648         if ((1000 % IRQ_RATE)) {
649                 print_error("1000 (Ticks per second) rate must be a multiple of IRQ rate, please fix!\n");
650                 goto done;
651         }
652
653         /* calculate SDL chunk size for audio
654          * it must be a power of two, but not more than the chunk size for each IRQ!
655          */
656         for (sdl_sound_chunk = 2; sdl_sound_chunk <= (SOUND_SAMPLERATE / IRQ_RATE); sdl_sound_chunk <<= 1)
657                 ;
658         sdl_sound_chunk >>= 1;
659 //      printf("samples per IRQ = %d, samples per SDL audio = %d\n", SOUND_SAMPLERATE / IRQ_RATE, sdl_sound_chunk); exit(0);
660
661         /* allocate sound buffers */
662         sound_buffer_size = SOUND_SAMPLERATE / IRQ_RATE * SOUND_CHUNKS;
663         sound_buffer = calloc(sound_buffer_size, sizeof(*sound_buffer));
664         if (!sound_buffer) {
665                 print_error("Failed to allocate image buffer\n");
666                 goto done;
667         }
668
669         /* allocate memory */
670         memory = calloc(MEMORY_SIZE, 1);
671         if (!memory) {
672                 print_error("Failed to allocate cpu's memory\n");
673                 goto done;
674         }
675         stop_event = calloc(MEMORY_SIZE, 1);
676         if (!stop_event) {
677                 print_error("Failed to allocate cpu's stop_event memory\n");
678                 goto done;
679         }
680         chipreg = calloc(IOSIZE, 1);
681         if (!chipreg) {
682                 print_error("Failed to allocate chip register\n");
683                 goto done;
684         }
685
686         /* init cpu code */
687         execute_init(MEMORY_SIZE, memory, stop_event, chipreg, io_read, io_write, mercenary_stop_at);
688
689         /* init disk emulation */
690         disk_init(disk_read, disk_write);
691
692         /* load binary */
693         mercenary_load();
694
695         /* patch some stuff */
696         mercenary_patch();
697
698         /* init SDL and OpenGL */
699         rc = init_sdl(argv[0], (config_debug_opengl) ? SCREEN_WIDTH * 2 : SCREEN_WIDTH * 3, (config_debug_opengl) ? SCREEN_HEIGHT * 4 : SCREEN_HEIGHT * 3, SOUND_SAMPLERATE, sdl_sound_chunk, keyboard_sdl, audio_sdl);
700         if (rc < 0)
701                 goto done;
702         rc = init_opengl((double_size) ? IMAGE_WIDTH * 2 : IMAGE_WIDTH, (double_size) ? IMAGE_HEIGHT * 2 : IMAGE_HEIGHT);
703         if (rc < 0)
704                 goto done;
705         resize_opengl((config_debug_opengl) ? SCREEN_WIDTH * 2 : SCREEN_WIDTH * 3, (config_debug_opengl) ? SCREEN_HEIGHT * 4 : SCREEN_HEIGHT * 3);
706
707         /* init audio filter */
708         sound_init_filter(SOUND_SAMPLERATE);
709
710         /* start cpu */
711         reset_cpu();
712
713         if (config_skip_intro) {
714                 int event;
715
716                 print_info("*** Skipping intro, fast forwarding... ***\n\n");
717                 do {
718                         execute_cpu(0, &event);
719                 } while (event != STOP_AT_CLEAR_SCREEN1);
720                 do {
721                         execute_cpu(0, &event);
722                 } while (event != STOP_AT_WAIT_VBL);
723         }
724
725         print_info("**********************************\n");
726         print_info("* Welcome to Mercenary Reloaded! *\n");
727         print_info("**********************************\n\n");
728         print_info("Press CTRL + cursor keys to select inventory or pickup/drop item.\n");
729         print_info("Press CTRL + f to toggle full screen.\n");
730         print_info("Press CTRL + s to toggle rendering speed.\n");
731         print_info("Press CTRL + v to toggle video filter.\n");
732         print_info("Press CTRL + a to toggle audio filter.\n");
733         print_info("Press CTRL + r to toggle rendering with opengl or orignal code.\n");
734         print_info("Press CTRL + c to exit game.\n\n");
735         print_info("Use '--help' as command line option for configuration settings.\n\n");
736
737         /* run game */
738         main_loop();
739
740 done:
741         exit_opengl();
742         exit_sdl();
743
744         if (chipreg)
745                 free(chipreg);
746         if (stop_event)
747                 free(stop_event);
748         if (memory)
749                 free(memory);
750         if (sound_buffer)
751                 free(sound_buffer);
752         if (image)
753                 free(image);
754
755         return 0;
756 }
757