ARM application development SDK for MNT ZZ9000 graphics and coprocessor card for classic Amiga computers.
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 

541 wiersze
15 KiB

  1. /*
  2. * MNT ZZ9000 Amiga Graphics and ARM Coprocessor SDK
  3. * "zz9k" Amiga CLI tool for loading and executing
  4. * ARM applications
  5. *
  6. * Copyright (C) 2019, Lukas F. Hartmann <lukas@mntre.com>
  7. * MNT Research GmbH, Berlin
  8. * https://mntre.com
  9. *
  10. * More Info: https://mntre.com/zz9000
  11. *
  12. * SPDX-License-Identifier: GPL-3.0-or-later
  13. * GNU General Public License v3.0 or later
  14. *
  15. * https://spdx.org/licenses/GPL-3.0-or-later.html
  16. */
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <proto/exec.h>
  21. #include <proto/dos.h>
  22. #include <proto/expansion.h>
  23. #include <proto/graphics.h>
  24. #include <libraries/expansion.h>
  25. #include <libraries/configvars.h>
  26. #include <clib/alib_protos.h>
  27. #include <proto/intuition.h>
  28. #include <intuition/screens.h>
  29. #include <cybergraphx/cybergraphics.h>
  30. #include <proto/cybergraphics.h>
  31. #include <devices/audio.h>
  32. #include <devices/input.h>
  33. #include <exec/ports.h>
  34. #include <exec/interrupts.h>
  35. #include <exec/memory.h>
  36. #include "zz9000.h"
  37. #define uint8_t unsigned char
  38. #define int16_t signed short
  39. #define uint16_t unsigned short
  40. #define uint32_t unsigned long
  41. #define int32_t signed long
  42. #define ZZ9K_APP_SPACE 0x03000000
  43. #define ZZ9K_APP_DATASPACE 0x05000000
  44. #define ZZ9K_MEM_START 0x00200000
  45. volatile MNTZZ9KRegs* regs;
  46. struct Screen* zz9k_screen;
  47. void open_screen(int w, int h) {
  48. uint32_t bmid=BestCModeIDTags(CYBRBIDTG_NominalWidth, w,
  49. CYBRBIDTG_NominalHeight, h,
  50. CYBRBIDTG_Depth, 24,
  51. TAG_DONE);
  52. //printf("Mode ID: %lx\n", bmid);
  53. zz9k_screen = OpenScreenTags(NULL,
  54. SA_Title,"ZZ9000 ARM Application",
  55. SA_DisplayID, bmid,
  56. SA_Depth, 24,
  57. TAG_DONE);
  58. /*printf("Planes[0]: %p\n", zz9k_screen->BitMap.Planes[0]);
  59. printf("BytesPerRow: %d\n", zz9k_screen->BitMap.BytesPerRow);
  60. printf("Rows: %d\n", zz9k_screen->BitMap.Rows);
  61. printf("Depth: %d\n", zz9k_screen->BitMap.Depth);*/
  62. }
  63. __saveds struct InputEvent *input_handler(__reg("a0") struct InputEvent *ielist, __reg("a1") struct MsgPort *port) {
  64. struct InputEvent *ie, *prev;
  65. ie = ielist;
  66. prev = NULL;
  67. while(ie) {
  68. if (ie->ie_Class == IECLASS_RAWKEY) {
  69. int code = ie->ie_Code;
  70. //printf("RAWKEY: %d\n", code);
  71. regs->arm_event_code = code;
  72. }
  73. ie = ie->ie_NextEvent;
  74. }
  75. return (ielist);
  76. }
  77. BOOL input_dev_open = FALSE;
  78. struct MsgPort *input_port = NULL;
  79. struct Interrupt *input_irq_handler = NULL;
  80. struct IOStdReq *input_req = NULL;
  81. UBYTE NameString[]="zz9k input";
  82. int setup_input() {
  83. int rc = 0;
  84. if (input_port = CreateMsgPort())
  85. if (input_irq_handler = AllocVec(sizeof(struct Interrupt),MEMF_CLEAR))
  86. if (input_req = (struct IOStdReq *) CreateIORequest(input_port,sizeof(struct IOStdReq)))
  87. if (!OpenDevice("input.device",0,(struct IORequest *)input_req,0))
  88. input_dev_open = TRUE;
  89. if (!input_dev_open) {
  90. printf("Error: Could not open input.device.\n");
  91. } else {
  92. input_irq_handler->is_Code = (APTR)input_handler;
  93. input_irq_handler->is_Data = (APTR)input_port;
  94. input_irq_handler->is_Node.ln_Name = "";
  95. input_irq_handler->is_Node.ln_Pri = 60;
  96. input_req->io_Data = (APTR)input_irq_handler;
  97. input_req->io_Command = IND_ADDHANDLER;
  98. DoIO((struct IORequest *)input_req);
  99. rc = 1;
  100. }
  101. return rc;
  102. }
  103. void cleanup() {
  104. if (zz9k_screen) {
  105. CloseScreen(zz9k_screen);
  106. }
  107. if (input_dev_open) {
  108. input_req->io_Data = (APTR)input_irq_handler;
  109. input_req->io_Command = IND_REMHANDLER;
  110. DoIO((struct IORequest *)input_req);
  111. CloseDevice((struct IORequest *)input_req);
  112. DeleteIORequest((struct IORequest *)input_req);
  113. FreeVec(input_irq_handler);
  114. DeleteMsgPort(input_port);
  115. }
  116. }
  117. void reset_arm() {
  118. // reset application ARM core
  119. regs->arm_run_hi = 0;
  120. regs->arm_run_lo = 0x100;
  121. }
  122. int is_hex(char* str) {
  123. if (strlen(str)>2 && str[0]=='0' && str[1]=='x') {
  124. return 1;
  125. }
  126. return 0;
  127. }
  128. int main(int argc, char** argv) {
  129. struct ConfigDev* cd = NULL;
  130. uint8_t* memory;
  131. uint32_t load_offset = 0;
  132. char* load_filename = NULL;
  133. int load_mode = 0;
  134. int run_mode = 0;
  135. int stop_mode = 0;
  136. int screen_mode = 0;
  137. int console_mode = 0;
  138. int keyboard_mode = 0;
  139. int audio_mode = 0;
  140. int verbose_mode = 0;
  141. int screen_w = 640;
  142. int screen_h = 480;
  143. uint32_t arm_args[4];
  144. int placeholder_arm_argi[4];
  145. int placeholder_argi[4];
  146. int placeholders = 0;
  147. int arm_argc = 0;
  148. // zz9k load <arm-data-file> [offset hex or decimal]
  149. // zz9k run -audio -screen -console 0x1234 45123 0xbeef $screen
  150. // zz9k stop
  151. int syntax_ok = 1;
  152. for (int i=1; i<argc; i++) {
  153. if (i==1) {
  154. // command
  155. if (!strcmp("load", argv[1])) {
  156. load_mode = 1;
  157. } else if (!strcmp("run",argv[1])) {
  158. run_mode = 1;
  159. } else if (!strcmp("stop",argv[1])) {
  160. stop_mode = 1;
  161. } else {
  162. printf("Unknown command '%s'.\n", argv[1]);
  163. syntax_ok = 0;
  164. break;
  165. }
  166. } else {
  167. // option
  168. if (run_mode) {
  169. if (!strcmp("-640x480",argv[i])) {
  170. screen_mode = 1;
  171. screen_w = 640;
  172. screen_h = 480;
  173. } else if (!strcmp("-320x240",argv[i])) {
  174. screen_mode = 1;
  175. screen_w = 320;
  176. screen_h = 240;
  177. } else if (!strcmp("-audio",argv[i])) {
  178. audio_mode = 1;
  179. } else if (!strcmp("-keyboard",argv[i])) {
  180. keyboard_mode = 1;
  181. } else if (!strcmp("-console",argv[i])) {
  182. console_mode = 1;
  183. } else if (!strcmp("-verbose",argv[i])) {
  184. verbose_mode = 1;
  185. } else if (argv[i][0] == '-') {
  186. printf("Unknown run option '%s'\n", argv[i]);
  187. syntax_ok = 0;
  188. break;
  189. } else {
  190. if (arm_argc>4) {
  191. printf("Too many arguments for ARM application (maximum 4).\n");
  192. syntax_ok = 0;
  193. break;
  194. }
  195. if (is_hex(argv[i])) {
  196. // hex arg
  197. arm_args[arm_argc++] = strtoul(&argv[i][2], NULL, 16);
  198. } else if (argv[i][0] == '!') {
  199. // special variable
  200. if (placeholders>3) {
  201. printf("Too many special variables (maximum 4).\n");
  202. syntax_ok = 0;
  203. break;
  204. } else {
  205. placeholder_argi[placeholders]=i;
  206. placeholder_arm_argi[placeholders]=arm_argc++;
  207. placeholders++;
  208. }
  209. } else {
  210. // decimal arg
  211. arm_args[arm_argc++] = strtoul(argv[i], NULL, 10);
  212. }
  213. }
  214. } else if (stop_mode) {
  215. printf("Surplus arguments for 'stop' command.\n");
  216. syntax_ok = 0;
  217. break;
  218. } else if (load_mode) {
  219. if (i==2) {
  220. load_filename = argv[i];
  221. } else if (i==3) {
  222. if (is_hex(argv[i])) {
  223. load_offset = strtoul(&argv[i][2], NULL, 16);
  224. } else {
  225. load_offset = strtoul(argv[i], NULL, 10);
  226. }
  227. } else {
  228. printf("Surplus arguments for 'load' command.\n");
  229. syntax_ok = 0;
  230. break;
  231. }
  232. }
  233. }
  234. }
  235. if (argc<2) {
  236. syntax_ok = 0;
  237. }
  238. if (audio_mode && argc<3) {
  239. printf("Not enough arguments for 'audio' command.\n");
  240. syntax_ok = 0;
  241. }
  242. if (!syntax_ok) {
  243. printf("\nUsage: zz9k load <arm-data-file> [offset hex or decimal]\n");
  244. printf(" zz9k run [mode options] [arg0] ... [arg3]\n");
  245. printf(" zz9k stop\n\n");
  246. printf("Mode options (can be combined):\n");
  247. printf(" -640x480 opens a 640x480x32 screen (bitmap address available in !screen variable)\n");
  248. printf(" -320x240 opens a 320x240x32 screen (bitmap address available in !screen variable)\n");
  249. printf(" -console attaches ARM application's input and output event streams to Amiga shell, line based\n");
  250. printf(" -keyboard sends Amiga keyboard scan codes to ARM application as events\n");
  251. printf("* -audio (experimental) streams raw audio generated by ARM application. not ready for use yet.\n\n");
  252. printf("arg0 - arg3 are numeric (unsigned 32-bit integer) arguments to the ARM application. For these, you can pass:\n");
  253. printf(" a decimal number (e.g. 1280)\n");
  254. printf(" a hexadecimal number prefixed with 0x (e.g. 0xdeadcafe)\n");
  255. printf(" a special variable, one of: !screen !width !height !depth\n\n");
  256. exit(1);
  257. }
  258. // find a ZZ9000
  259. cd = (struct ConfigDev*)FindConfigDev(cd,0x6d6e,0x4);
  260. if (!cd) {
  261. cd = (struct ConfigDev*)FindConfigDev(cd,0x6d6e,0x3);
  262. }
  263. if (!cd) {
  264. printf("Error: MNT ZZ9000 not found.\n");
  265. exit(1);
  266. }
  267. memory = (uint8_t*)(cd->cd_BoardAddr)+0x10000;
  268. regs = (volatile MNTZZ9KRegs*)(cd->cd_BoardAddr);
  269. if (stop_mode) {
  270. reset_arm();
  271. printf("ARM core reset.\n");
  272. exit(0);
  273. }
  274. if (load_mode) {
  275. // load a file into ZZ9000 memory
  276. uint8_t* dest = memory + ZZ9K_APP_SPACE - ZZ9K_MEM_START + load_offset;
  277. if (verbose_mode) {
  278. printf("Loading '%s' to ARM address 0x%lx (Amiga address 0x%lx)\n", argv[2], ZZ9K_APP_SPACE + load_offset, (uint32_t)dest);
  279. }
  280. FILE* f = fopen(load_filename, "rb");
  281. if (f) {
  282. fseek(f, 0, SEEK_END);
  283. long fsize = ftell(f);
  284. fseek(f, 0, SEEK_SET);
  285. size_t bytes_read = fread(dest, 1, fsize, f);
  286. fclose(f);
  287. printf("%lx\n",(uint32_t)bytes_read);
  288. } else {
  289. printf("0\n");
  290. }
  291. exit(0);
  292. }
  293. if (run_mode) {
  294. // TODO encapsulate all this audio junk
  295. char* audio_buf = NULL;
  296. const int audio_bufsz = 16000;
  297. uint32_t audio_offset = 0;
  298. uint32_t chipoffset = 0;
  299. const uint32_t chunksz = 48000*2;
  300. char audio_devopened;
  301. struct MsgPort *audio_port;
  302. struct IOAudio *aio[1];
  303. volatile int16_t* src = NULL;
  304. if (screen_mode) {
  305. open_screen(screen_w, screen_h);
  306. if (!zz9k_screen) {
  307. printf("Error creating screen.\n");
  308. exit(2);
  309. }
  310. // capture keypresses
  311. if (keyboard_mode) {
  312. setup_input();
  313. }
  314. // get screen bitmap address
  315. uint32_t fb = ((uint32_t)zz9k_screen->BitMap.Planes[0])-(uint32_t)memory+(uint32_t)ZZ9K_MEM_START;
  316. fb+=zz9k_screen->BarHeight*zz9k_screen->BitMap.BytesPerRow; // skip title bar
  317. // fill in screen special variables
  318. for (int i=0; i<placeholders; i++) {
  319. int argi = placeholder_argi[i];
  320. int arm_argi = placeholder_arm_argi[i];
  321. if (!strcmp(argv[argi],"!screen")) {
  322. arm_args[arm_argi] = fb;
  323. } else if (!strcmp(argv[argi],"!width")) {
  324. arm_args[arm_argi] = zz9k_screen->Width;
  325. } else if (!strcmp(argv[argi],"!height")) {
  326. arm_args[arm_argi] = zz9k_screen->BitMap.Rows;
  327. } else if (!strcmp(argv[argi],"!depth")) {
  328. arm_args[arm_argi] = zz9k_screen->BitMap.Depth;
  329. }
  330. }
  331. }
  332. if (audio_mode) {
  333. // TODO: this is currently just a sketch. this should loop
  334. // over a buffer space and request more buffers via events
  335. if (!(audio_port=CreatePort(0,0))) {
  336. printf("Error: couldn't create Audio message Port.\n");
  337. exit(5);
  338. }
  339. for (int k=0; k<1; k++) {
  340. if (!(aio[k]=(struct IOAudio *)CreateExtIO(audio_port,sizeof(struct IOAudio)))) {
  341. printf("Error: couldn't create IOAudio %d\n",k);
  342. exit(5);
  343. }
  344. }
  345. char channels[] = {1,2,4,8};
  346. // Set up request to Audio port
  347. aio[0]->ioa_Request.io_Command = ADCMD_ALLOCATE;
  348. aio[0]->ioa_Request.io_Flags = ADIOF_NOWAIT;
  349. aio[0]->ioa_AllocKey = 0;
  350. aio[0]->ioa_Data = channels;
  351. aio[0]->ioa_Length = sizeof(channels);
  352. if (!(OpenDevice("audio.device", 0L, (struct IORequest *) aio[0], 0L)))
  353. audio_devopened = TRUE;
  354. else {
  355. printf("Error: couldn't open audio.device\n");
  356. exit(5);
  357. }
  358. // Paula playback buffer in chipmem
  359. audio_buf = AllocMem(audio_bufsz,MEMF_CHIP);
  360. src = (volatile int16_t*)(((char*)arm_args[2])-(uint32_t)ZZ9K_MEM_START+(uint32_t)memory);
  361. if (!audio_buf) {
  362. printf("Error: allocating chipmem failed.\n");
  363. exit(5);
  364. } else {
  365. memset(audio_buf,0,audio_bufsz);
  366. }
  367. }
  368. // pass ARM app arguments
  369. for (int i=0; i<arm_argc; i++) {
  370. if (verbose_mode) {
  371. printf("ARM arg%d: %lu (%lx)\n", i, arm_args[i], arm_args[i]);
  372. }
  373. regs->arm_arg[i*2] = arm_args[i]>>16;
  374. regs->arm_arg[i*2+1] = arm_args[i]&0xffff;
  375. }
  376. regs->arm_argc = arm_argc;
  377. // pass ARM run address
  378. regs->arm_run_hi = (ZZ9K_APP_SPACE + load_offset)>>16;
  379. regs->arm_run_lo = (ZZ9K_APP_SPACE + load_offset)&0xffff;
  380. uint16_t last_ser = 0;
  381. uint32_t timeout = 0;
  382. // main loop
  383. while (1) {
  384. if (console_mode) {
  385. // send kbd, mouse events and receive events
  386. // exit on quit event
  387. int chr = getc(stdin);
  388. if (chr) {
  389. regs->arm_event_code = chr;
  390. }
  391. if (chr=='\n') {
  392. char done = 0;
  393. while (!done) {
  394. uint16_t armchr = regs->arm_event_code;
  395. uint16_t armser = regs->arm_event_serial;
  396. if (armser!=last_ser) {
  397. last_ser = armser;
  398. putc(armchr, stdout);
  399. timeout = 0;
  400. }
  401. timeout++;
  402. if (timeout>=10000) {
  403. done = 1;
  404. timeout = 0;
  405. }
  406. }
  407. }
  408. }
  409. if (audio_mode) {
  410. aio[0]->ioa_Request.io_Command = CMD_WRITE;
  411. aio[0]->ioa_Request.io_Flags = ADIOF_PERVOL;
  412. aio[0]->ioa_Data = audio_buf+chipoffset; // sample
  413. aio[0]->ioa_Length = audio_bufsz/2;
  414. aio[0]->ioa_Period = 3546895L/8000;
  415. aio[0]->ioa_Volume = 0xff;
  416. aio[0]->ioa_Cycles = 1;
  417. BeginIO((struct IORequest *)aio[0]);
  418. if (chipoffset == 0) {
  419. chipoffset = audio_bufsz/2;
  420. } else {
  421. chipoffset = 0;
  422. }
  423. // downsample 48000 stereo to 8000 mono
  424. // FIXME lowpass filter
  425. // FIXME let ARM do this
  426. uint32_t bi = 0;
  427. for (uint32_t i=0; i<chunksz; i+=2*6) {
  428. int32_t srcss=src[i+audio_offset];
  429. uint32_t srcs=srcss+=65536/2;
  430. audio_buf[chipoffset+bi] = (((srcs&0xff)<<8|(srcs&0xff00)>>8))>>8;
  431. bi++;
  432. if (bi>=audio_bufsz/2) break;
  433. }
  434. audio_offset+=chunksz;
  435. WaitIO((struct IORequest *)aio[0]);
  436. // FIXME arbitrary end
  437. if (audio_offset>500*chunksz) break;
  438. }
  439. // end on mouse click
  440. volatile uint8_t* mreg = (volatile uint8_t*)0xbfe001;
  441. if (!(*mreg&(1<<6))) break;
  442. }
  443. // done running
  444. reset_arm();
  445. if (verbose_mode) {
  446. printf("ARM core reset.\n");
  447. }
  448. cleanup();
  449. if (audio_mode) {
  450. // FIXME consolidate with cleanup()
  451. FreeMem(audio_buf,audio_bufsz);
  452. if (audio_devopened) {
  453. CloseDevice((struct IORequest *)aio[0]);
  454. }
  455. for (int k=0; k<1; k++) {
  456. if (aio[k]) {
  457. DeleteExtIO((struct IORequest *)aio[k]);
  458. aio[k] = NULL;
  459. }
  460. }
  461. if (audio_port) {
  462. DeletePort(audio_port);
  463. audio_port = NULL;
  464. }
  465. }
  466. }
  467. }