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.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
 
 
 
 
 

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