ARM application development SDK for MNT ZZ9000 graphics and coprocessor card for classic Amiga computers.
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.
 
 
 
 
 

541 lines
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. }