main.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. /*
  2. * meg4/tests/runner/main.c
  3. *
  4. * Copyright (C) 2023 bzt
  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, write to the Free Software
  18. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19. *
  20. * @brief A simple CLI tool to batch test running scripts
  21. *
  22. */
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include "meg4.h"
  26. #include "cpu.h"
  27. int verbose = 2, strace = 0;
  28. /**
  29. * Hooks that libmeg4.a might call and a platform must provide
  30. */
  31. void main_log(int lvl, const char* fmt, ...)
  32. {
  33. __builtin_va_list args;
  34. __builtin_va_start(args, fmt);
  35. if(verbose >= lvl) { printf("meg4: "); vprintf(fmt, args); printf("\r\n"); }
  36. __builtin_va_end(args);
  37. }
  38. uint8_t* main_readfile(char *file, int *size)
  39. {
  40. uint8_t *data = NULL;
  41. FILE *f;
  42. if(!file || !*file || !size) return NULL;
  43. f = fopen(file, "rb");
  44. if(f) {
  45. fseek(f, 0L, SEEK_END);
  46. *size = (int)ftell(f);
  47. fseek(f, 0L, SEEK_SET);
  48. data = (uint8_t*)malloc(*size);
  49. if(data) {
  50. memset(data, 0, *size);
  51. if((int)fread(data, 1, *size, f) != *size) { free(data); data = NULL; *size = 0; }
  52. }
  53. fclose(f);
  54. }
  55. return data;
  56. }
  57. int main_writefile(char *file, uint8_t *buf, int size)
  58. {
  59. int ret = 0;
  60. FILE *f;
  61. if(!file || !*file || !buf || size < 1) return 0;
  62. f = fopen(file, "wb");
  63. if(f) {
  64. ret = ((int)fwrite(buf, 1, size, f) == size);
  65. fclose(f);
  66. }
  67. return ret;
  68. }
  69. /* callbacks that can be empty */
  70. void main_openfile(void) { }
  71. int main_savefile(const char *name, uint8_t *buf, int len) { return main_writefile((char*)name, buf, len); }
  72. char **main_getfloppies(void) { return NULL; }
  73. int main_cfgsave(char *cfg, uint8_t *buf, int len) { (void)cfg; (void)buf; (void)len; return 1; }
  74. uint8_t *main_cfgload(char *cfg, int *len) { (void)cfg; (void)len; return NULL; }
  75. void main_fullscreen(void) { }
  76. char *main_getclipboard(void) { return NULL; }
  77. void main_setclipboard(char *str) { (void)str; }
  78. void main_osk_show(void) { }
  79. void main_osk_hide(void) { }
  80. /* these would be normally displayed by code_view() or debug_view(), but we don't call meg4_redraw() at all... */
  81. uint32_t debug_disasm(uint32_t pc, char *out);
  82. extern int errline, errpos;
  83. extern char errmsg[256];
  84. void print_src(int s, int e)
  85. {
  86. if(!meg4.src || (uint32_t)s > meg4.src_len || s > e) return;
  87. for(; s < e && meg4.src[s] != '\n'; s++)
  88. printf("%c", meg4.src[s]);
  89. printf("\r\n");
  90. }
  91. void print_error()
  92. {
  93. int i, n = 1;
  94. if(errmsg[0]) {
  95. for(i = 0, n = 1; i < (int)meg4.src_len && n != errline; i++)
  96. if(meg4.src[i] == '\n') n++;
  97. errpos -= i;
  98. print_src(i, meg4.src_len);
  99. for(i = 0; i < errpos; i++) printf(" ");
  100. printf("^\r\n");
  101. if(meg4.mode == MEG4_MODE_DEBUG) {
  102. printf("meg4: pc %05X sp %05X bp %05X dp %05X cp %d callstack:\r\n", meg4.pc, meg4.sp, meg4.bp, meg4.dp, meg4.cp);
  103. if(meg4.code && meg4.code[0] && meg4.code[0] < meg4.code_len)
  104. for(i = 0; i < (int)meg4.cp; i++)
  105. printf(" bp %05X pc %05X\r\n", meg4.code[meg4.code[0] + i * 2], meg4.code[meg4.code[0] + i * 2 + 1]);
  106. }
  107. exit(1);
  108. }
  109. }
  110. /**
  111. * The main procedure
  112. */
  113. int main(int argc, char **argv)
  114. {
  115. int i = 1, l, re = 0, disasm = 0;
  116. uint32_t pc;
  117. uint8_t *ptr;
  118. char *fn, tmp[256];
  119. /* "parse" command line arguments */
  120. if(argc < 2 || (argv[1][0] == '-' && argc < 3)) {
  121. printf("MEG-4 Script Runner by bzt Copyright (C) 2023 GPLv3+\r\n\r\n");
  122. printf("%s [-d|-v|-r] <script>\r\n", argv[0]);
  123. return 0;
  124. }
  125. if(argv[1][0] == '-') { i++; if(argv[1][1] == 'v') verbose = 3; else if(argv[1][1] == 'r') re++; else disasm++; }
  126. /* turn on the emulator */
  127. meg4_poweron("en");
  128. /* insert "floppy" */
  129. if((ptr = main_readfile(argv[i], &l))) {
  130. fn = strrchr(argv[i], '/'); if(!fn) fn = argv[i]; else fn++;
  131. meg4_insert(fn, ptr, l);
  132. free(ptr);
  133. }
  134. /* when program source insertion detected, import switches to code editor automatically */
  135. meg4.mode = MEG4_MODE_GAME;
  136. /* compile and run */
  137. i = cpu_compile();
  138. if(re) printf("meg4: first compile, cpu_compile() = %d\r\n", i);
  139. if(!i) print_error();
  140. else if(disasm) {
  141. if(!meg4.code || meg4.code_len < 4 || meg4.code_type >= 0x10) printf("No bytecode?\r\n");
  142. else {
  143. printf("meg4: disassembling...\r\n\r\nHeader (4 words)\r\n");
  144. printf(" %05X: %08x Code debug segment (header + text size)\r\n", 0, meg4.code[0]);
  145. printf(" %05X: %08x Data debug segment\r\n", 1, meg4.code[1]);
  146. printf(" %05X: %08x Entry point (setup)\r\n", 2, meg4.code[2]);
  147. printf(" %05X: %08x Entry point (loop)\r\n", 3, meg4.code[3]);
  148. printf("\r\nText Segment (%u words)\r\n", meg4.code[0] - 4);
  149. for(pc = 4; pc < meg4.code_len && pc < meg4.code[0];) {
  150. printf(" %05X: %08x ", pc, meg4.code[pc]);
  151. /* normally debug_disasm() returns a cropped sw, but here print all case options */
  152. if((meg4.code[pc] & 0xff) == BC_SW) {
  153. l = meg4.code[pc++] >> 8;
  154. printf("sw %d", meg4.code[pc++]);
  155. for(i = 0; i <= l; i++) printf(",%05X", meg4.code[pc++]);
  156. printf("\n");
  157. } else {
  158. for(i = -1, l = meg4.code[0]; (uint32_t)l < meg4.code_len && (uint32_t)l < meg4.code[1]; l += 2)
  159. if(meg4.code[l] == pc) { i = meg4.code[l + 1]; break; }
  160. pc = debug_disasm(pc, tmp);
  161. printf("%-24s", tmp);
  162. if(i != -1) print_src(i, meg4.src_len);
  163. else printf("\r\n");
  164. }
  165. }
  166. if(meg4.code_len > meg4.code[0]) {
  167. if(meg4.dp && meg4.code[1] && meg4.code_len > meg4.code[1]) {
  168. printf("\r\nData Segment (%u bytes)\r\n", meg4.dp);
  169. /* global variables */
  170. for(l = 0, pc = meg4.code[1]; pc < meg4.code_len; pc += 4) {
  171. printf(" %05X:", meg4.code[pc + 2]);
  172. for(i = 0; i < (int)meg4.code[pc + 3]; i++, l++) printf(" %02x", meg4.data[meg4.code[pc + 2] - MEG4_MEM_USER + i]);
  173. printf("\r\n");
  174. }
  175. /* constants */
  176. if(l < (int)meg4.dp) {
  177. printf(" %05X:", MEG4_MEM_USER + l);
  178. for(; l < (int)meg4.dp; l++) printf(" %02x", meg4.data[l]);
  179. printf("\r\n");
  180. }
  181. }
  182. printf("\r\nCode Debug Segment (%u words)\r\n", meg4.code[1] - meg4.code[0]);
  183. for(pc = meg4.code[0]; pc < meg4.code_len && pc < meg4.code[1]; pc += 2) {
  184. printf(" %05X: pc:%05X src:%u\t\t", pc, meg4.code[pc], meg4.code[pc + 1]);
  185. print_src(meg4.code[pc + 1], meg4.src_len);
  186. }
  187. if(meg4.code[1] && meg4.code_len > meg4.code[1]) {
  188. printf("\r\nData Debug Segment (%u words)\r\n", meg4.code_len - meg4.code[1]);
  189. for(pc = meg4.code[1]; pc < meg4.code_len; pc += 4) {
  190. i = meg4.code[pc + 1] & 0xffffff;
  191. printf(" %05X: type:%02x src:%u[%u]\taddr:%05X len:%u\t", pc, meg4.code[pc], i, meg4.code[pc + 1] >> 24,
  192. meg4.code[pc + 2], meg4.code[pc + 3]);
  193. print_src(i, i + ((meg4.code[pc + 1] >> 24) & 0xff));
  194. }
  195. }
  196. }
  197. printf("\r\n");
  198. }
  199. } else {
  200. /* simulate a few calls for testing, in reality this should run at 60 FPS infinitely */
  201. for(i = 0; i < 16; i++) {
  202. printf("\r\n----- iteration %d: setup done %d blocked io %d tmr %d critical %d -----\r\n", i, meg4.flg & 1, meg4.flg & 2, meg4.flg & 4, meg4.flg & 8);
  203. meg4_run();
  204. print_error();
  205. if(i == 2 || i == 4) meg4_pushkey("a");
  206. }
  207. if(re) {
  208. /* try again, globals and blocked state should be reset, and API should be still available */
  209. i = cpu_compile();
  210. printf("meg4: recompiled, cpu_compile() = %d\r\n", i);
  211. if(!i) print_error();
  212. else {
  213. for(i = 0; i < 16; i++) {
  214. printf("\r\n----- iteration %d: setup done %d blocked io %d tmr %d critical %d -----\r\n", i, meg4.flg & 1, meg4.flg & 2, meg4.flg & 4, meg4.flg & 8);
  215. meg4_run();
  216. print_error();
  217. }
  218. }
  219. }
  220. }
  221. /* free resources */
  222. meg4_poweroff();
  223. return 0;
  224. }