Human68k CUI emulator with sound.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.c 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <fcntl.h>
  8. #include <unistd.h>
  9. #include <limits.h>
  10. #include <ao/ao.h>
  11. #include "v68.h"
  12. #include "v68io.h"
  13. #include "tools.h"
  14. #define DEFAULT_CPU_CLOCK 8000000
  15. #define DEFAULT_RAM_SIZE 8 * 1024 * 1024
  16. #define DEFAULT_SAMPLE_RATE 44100
  17. #define DEFAULT_BUFFER_SIZE 4096
  18. int opt_cpu_clock = DEFAULT_CPU_CLOCK;
  19. int opt_ram_size = DEFAULT_RAM_SIZE;
  20. int opt_sample_rate = DEFAULT_SAMPLE_RATE;
  21. int opt_buffer_size = DEFAULT_BUFFER_SIZE;
  22. char *opt_vgm_file = 0;
  23. int opt_utf8 = 0;
  24. int opt_verbosity = 0;
  25. int opt_log_calls = 0;
  26. int opt_dasm = 0;
  27. char cmd_queue[V68_CMD_QUEUE_LEN][1024];
  28. int cmd_queue_pos;
  29. void queue_cmd(char *cmdline) {
  30. if(cmd_queue_pos > V68_CMD_QUEUE_LEN) return;
  31. strncpy(cmd_queue[cmd_queue_pos], cmdline, sizeof(cmd_queue[0]));
  32. cmd_queue_pos++;
  33. }
  34. /* TODO: parse k, M, G etc */
  35. int parse_intval(char *s) {
  36. return atoi(s);
  37. }
  38. int running = 1;
  39. void sighandler(int signum) {
  40. signal(signum, NULL);
  41. printf("Caught signal %d, coming out...\n", signum);
  42. running = 0;
  43. }
  44. void print_help(char *argv0) {
  45. printf("Usage: %s [options] command [args]\n", argv0);
  46. printf("Options:\n");
  47. printf("\t-c <command> Execute this command before main command. Example: -c mxdrv.x.\n");
  48. printf("\t-C <clock> CPU clock. Default is %dMHz.\n", DEFAULT_CPU_CLOCK / 1000000);
  49. printf("\t-M <size> Specify size of RAM. Default is %dM.\n", DEFAULT_RAM_SIZE / 1024 / 1024);
  50. printf("\t-R <rate> Sample rate. Default is %d.\n", DEFAULT_SAMPLE_RATE);
  51. printf("\t-b <size> Audio buffer size. Default is %d.\n", DEFAULT_BUFFER_SIZE);
  52. printf("\t-l <logfile.vgm> VGM file logging.\n");
  53. printf("\t-u Convert output to utf-8.\n");
  54. printf("\t-v Increase verbosity by 1.\n");
  55. printf("\t-t Trace system calls (DOS, FE and IOCS).\n");
  56. printf("\t-d Disassemble instructions as they are executed.\n");
  57. }
  58. int parse_cmdline(int argc, char **argv) {
  59. int first_nonarg_reached = 0;
  60. char cmdbuf[1024];
  61. cmdbuf[0] = 0;
  62. for(int i = 1; i < argc; i++) {
  63. if(!first_nonarg_reached) {
  64. if(!strcmp(argv[i], "-c")) {
  65. if(argc > i+1) {
  66. i++;
  67. queue_cmd(argv[i]);
  68. } else {
  69. fprintf(stderr, "-c requires an argument\n");
  70. }
  71. } else if (!strcmp(argv[i], "-C")) {
  72. i++;
  73. opt_cpu_clock = parse_intval(argv[i]);
  74. } else if (!strcmp(argv[i], "-M")) {
  75. i++;
  76. opt_ram_size = parse_intval(argv[i]);
  77. } else if (!strcmp(argv[i], "-R")) {
  78. i++;
  79. opt_sample_rate = parse_intval(argv[i]);
  80. } else if (!strcmp(argv[i], "-b")) {
  81. i++;
  82. opt_buffer_size = parse_intval(argv[i]);
  83. } else if (!strcmp(argv[i], "-l")) {
  84. i++;
  85. opt_vgm_file = strdup(argv[i]);
  86. } else if (!strcmp(argv[i], "-u")) {
  87. opt_utf8 = 1;
  88. } else if (!strcmp(argv[i], "-vvv")) {
  89. opt_verbosity+=3;
  90. } else if (!strcmp(argv[i], "-vv")) {
  91. opt_verbosity+=2;
  92. } else if (!strcmp(argv[i], "-v")) {
  93. opt_verbosity++;
  94. } else if (!strcmp(argv[i], "-t")) {
  95. opt_log_calls = 1;
  96. } else if (!strcmp(argv[i], "-d")) {
  97. opt_dasm = 1;
  98. } else if(argv[i][0] == '-') {
  99. print_help(argv[0]);
  100. return 1;
  101. } else {
  102. first_nonarg_reached = 1;
  103. }
  104. }
  105. if(first_nonarg_reached) {
  106. strncat(cmdbuf, argv[i], sizeof(cmdbuf) - strlen(cmdbuf));
  107. if(i + 1 < argc)
  108. strncat(cmdbuf, " ", sizeof(cmdbuf) - strlen(cmdbuf));
  109. }
  110. }
  111. if(cmdbuf[0]) {
  112. queue_cmd(cmdbuf);
  113. return 0;
  114. }
  115. print_help(argv[0]);
  116. return 1;
  117. }
  118. int main(int argc, char **argv, char **envp) {
  119. if(parse_cmdline(argc, argv)) {
  120. fprintf(stderr, "Could not parse command line\n");
  121. return -1;
  122. }
  123. int er = v68_init(opt_cpu_clock, opt_ram_size, opt_sample_rate);
  124. if(er) {
  125. fprintf(stderr, "Could not init VM! (%x)\n", er);
  126. return -1;
  127. }
  128. v68.log_calls = opt_log_calls;
  129. v68.log_dasm = opt_dasm;
  130. v68.verbosity = opt_verbosity;
  131. v68_boot();
  132. for(int i = 0; i < cmd_queue_pos; i++) {
  133. v68_queue_command(cmd_queue[i]);
  134. }
  135. v68_io_autodetect_drives();
  136. v68_dump_drives();
  137. char *v68_path = getenv("V68_PATH");
  138. v68_env_set("PATH", v68_path ? v68_path : getenv("PATH"));
  139. for(char **e = envp; *e; e++) {
  140. v68_env_append(*e);
  141. }
  142. v68_run();
  143. if(v68.sound_touched) {
  144. printf("init sound\n");
  145. // AO
  146. ao_initialize();
  147. int default_driver = ao_default_driver_id();
  148. ao_sample_format format;
  149. memset(&format, 0, sizeof(format));
  150. format.bits = 16;
  151. format.channels = 2;
  152. format.rate = opt_sample_rate;
  153. format.byte_format = AO_FMT_LITTLE;
  154. ao_device *device = ao_open_live(default_driver, &format, NULL /* no options */);
  155. if (device == NULL) {
  156. fprintf(stderr, "Error opening device.\n");
  157. return 1;
  158. }
  159. signal(SIGINT, sighandler);
  160. if(opt_vgm_file && opt_vgm_file[0]) {
  161. struct vgm_logger l;
  162. vgm_logger_begin(&l, opt_vgm_file);
  163. v68.logger = &l;
  164. }
  165. #define ALLOCBUF(b, s) \
  166. int16_t *b = malloc(s); \
  167. if(!b) { \
  168. fprintf(stderr, "Could not allocate %lu bytes sound bufer\n", s); \
  169. return 1; \
  170. }
  171. ALLOCBUF(bufL, opt_buffer_size * sizeof(*bufL));
  172. ALLOCBUF(bufR, opt_buffer_size * sizeof(*bufR));
  173. ALLOCBUF(tmpBufL, opt_buffer_size * sizeof(*tmpBufL));
  174. ALLOCBUF(tmpBufR, opt_buffer_size * sizeof(*tmpBufR));
  175. ALLOCBUF(buf, opt_buffer_size * 2 * sizeof(*buf));
  176. /* -- Play some stuff -- */
  177. while(running) {
  178. v68_fill_buffer(opt_buffer_size, bufL, bufR, tmpBufL, tmpBufR);
  179. for(int i = 0; i < opt_buffer_size; i++) {
  180. buf[i * 2] = bufL[i];
  181. buf[i * 2 + 1] = bufR[i];
  182. }
  183. ao_play(device, (char *)buf, opt_buffer_size * format.channels * format.bits / 8);
  184. }
  185. /* -- Close and shutdown -- */
  186. ao_close(device);
  187. ao_shutdown();
  188. if(v68.logger)
  189. vgm_logger_end(v68.logger);
  190. }
  191. v68_shutdown();
  192. return 0;
  193. }