From be885235d282d9bf53c64f8893800b4b8aa516fe Mon Sep 17 00:00:00 2001
From: mntmn <lukas@mntmn.com>
Date: Wed, 28 Aug 2019 16:44:01 +0200
Subject: [PATCH] add 'zz9k' loader/runner tool

---
 zz9k-loader/build.sh |  10 +
 zz9k-loader/zz9k.c   | 556 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 566 insertions(+)
 create mode 100755 zz9k-loader/build.sh
 create mode 100644 zz9k-loader/zz9k.c

diff --git a/zz9k-loader/build.sh b/zz9k-loader/build.sh
new file mode 100755
index 0000000..e15c18f
--- /dev/null
+++ b/zz9k-loader/build.sh
@@ -0,0 +1,10 @@
+export VBCC=$HOME/code/vbcc
+export INCLUDE=$HOME/code/vbcc/targets/m68k-amigaos/include
+export INCLUDE2=$HOME/code/vbcc/targets/m68k-amigaos/include2
+export PATH=$PATH:$VBCC/bin
+export NAME=zz9k
+
+vc +aos68k -I$INCLUDE -I$INCLUDE2 -I. -c99 -O2 -o $NAME $NAME.c -ldebug -lamiga -lauto
+
+rm $NAME.lha
+lha a0 $NAME.lha $NAME
diff --git a/zz9k-loader/zz9k.c b/zz9k-loader/zz9k.c
new file mode 100644
index 0000000..7e45a67
--- /dev/null
+++ b/zz9k-loader/zz9k.c
@@ -0,0 +1,556 @@
+/*
+ * MNT ZZ9000 Amiga Graphics and ARM Coprocessor SDK
+ *            "zz9k" Amiga CLI tool for loading and executing
+ *            ARM applications
+ *
+ * Copyright (C) 2019, Lukas F. Hartmann <lukas@mntre.com>
+ *                     MNT Research GmbH, Berlin
+ *                     https://mntre.com
+ *
+ * More Info: https://mntre.com/zz9000
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ * GNU General Public License v3.0 or later
+ *
+ * https://spdx.org/licenses/GPL-3.0-or-later.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <proto/expansion.h>
+#include <proto/graphics.h>
+#include <libraries/expansion.h>
+#include <libraries/configvars.h>
+#include <clib/alib_protos.h>
+
+#include <proto/intuition.h>
+#include <intuition/screens.h>
+#include <cybergraphx/cybergraphics.h>
+#include <proto/cybergraphics.h>
+#include <devices/audio.h>
+#include <devices/input.h>
+#include <exec/ports.h>
+#include <exec/interrupts.h>
+#include <exec/memory.h>
+
+#include "zz9000.h"
+
+#define uint8_t unsigned char
+#define int16_t signed short
+#define uint16_t unsigned short
+#define uint32_t unsigned long
+#define int32_t signed long
+
+#define ZZ9K_APP_SPACE 0x03000000
+#define ZZ9K_APP_DATASPACE 0x05000000
+#define ZZ9K_MEM_START 0x00200000
+
+volatile MNTZZ9KRegs* regs;
+
+// lifted from https://stackoverflow.com/a/15171925
+unsigned crc8(unsigned crc, unsigned char const *data, size_t len)
+{
+    if (data == NULL)
+        return 0;
+    crc = ~crc & 0xff;
+    while (len--) {
+        crc ^= *data++;
+        for (unsigned k = 0; k < 8; k++)
+            crc = crc & 1 ? (crc >> 1) ^ 0xb2 : crc >> 1;
+    }
+    return crc ^ 0xff;
+}
+
+struct Screen* zz9k_screen;
+
+void open_screen(int w, int h) {
+  uint32_t bmid=BestCModeIDTags(CYBRBIDTG_NominalWidth, w,
+                                CYBRBIDTG_NominalHeight, h,
+                                CYBRBIDTG_Depth, 24,
+                                TAG_DONE);
+
+  //printf("Mode ID: %lx\n", bmid);
+  
+  zz9k_screen = OpenScreenTags(NULL,
+                               SA_Title,"ZZ9000 ARM Application",
+                               SA_DisplayID, bmid,
+                               SA_Depth, 24,
+                               TAG_DONE);
+
+  /*printf("Planes[0]: %p\n", zz9k_screen->BitMap.Planes[0]);
+  printf("BytesPerRow: %d\n", zz9k_screen->BitMap.BytesPerRow);
+  printf("Rows: %d\n", zz9k_screen->BitMap.Rows);
+  printf("Depth: %d\n", zz9k_screen->BitMap.Depth);*/
+}
+
+__saveds struct InputEvent *input_handler(__reg("a0") struct InputEvent *ielist, __reg("a1") struct MsgPort *port) {
+  struct InputEvent *ie, *prev;
+
+  ie = ielist;
+  prev = NULL;
+
+  while(ie) {
+    if (ie->ie_Class == IECLASS_RAWKEY) {
+      int code = ie->ie_Code;
+      //printf("RAWKEY: %d\n", code);
+      regs->arm_event_code = code;
+    }
+    
+    ie = ie->ie_NextEvent;
+	}
+  
+  return (ielist);
+}
+
+BOOL input_dev_open = FALSE;
+struct MsgPort  *input_port = NULL;
+struct Interrupt *input_irq_handler = NULL;
+struct IOStdReq *input_req = NULL;
+UBYTE NameString[]="zz9k input";
+
+int setup_input() {
+  int rc = 0;
+
+  if (input_port = CreateMsgPort())
+    if (input_irq_handler = AllocVec(sizeof(struct Interrupt),MEMF_CLEAR))
+      if (input_req = (struct IOStdReq *) CreateIORequest(input_port,sizeof(struct IOStdReq)))
+        if (!OpenDevice("input.device",0,(struct IORequest *)input_req,0))
+          input_dev_open = TRUE;
+
+  if (!input_dev_open) {
+    printf("Error: Could not open input.device.\n");
+  } else {
+    ULONG sigs;
+
+    input_irq_handler->is_Code = (APTR)input_handler;
+    input_irq_handler->is_Data = (APTR)input_port;
+    input_irq_handler->is_Node.ln_Name = "";
+    input_irq_handler->is_Node.ln_Pri  = 60;
+    
+    input_req->io_Data = (APTR)input_irq_handler;
+    input_req->io_Command = IND_ADDHANDLER;
+    DoIO((struct IORequest *)input_req);
+
+    rc = 1;
+  }
+
+  return rc;
+}
+
+void cleanup() {
+  if (zz9k_screen) {
+    CloseScreen(zz9k_screen);
+  }
+  
+  if (input_dev_open) {
+    input_req->io_Data = (APTR)input_irq_handler;
+    input_req->io_Command = IND_REMHANDLER;
+    DoIO((struct IORequest *)input_req);
+    
+    CloseDevice((struct IORequest *)input_req);
+    DeleteIORequest((struct IORequest *)input_req);
+    FreeVec(input_irq_handler);
+    DeleteMsgPort(input_port);
+  }
+}
+
+int is_hex(char* str) {
+  if (strlen(str)>2 && str[0]=='0' && str[0]=='x') {
+    return 1;
+  }
+  return 0;
+}
+
+int main(int argc, char** argv) {
+  struct ConfigDev* cd = NULL;
+  uint8_t* memory;
+  uint16_t fw;
+  uint32_t load_offset = 0;
+  uint32_t audio_offset = 0;
+  char* load_filename = NULL;
+  int load_mode = 0;
+  int run_mode = 0;
+  int stop_mode = 0;
+  int screen_mode = 0;
+  int console_mode = 0;
+  int keyboard_mode = 0;
+  int audio_mode = 0;
+  int verbose_mode = 0;
+  int screen_w = 640;
+  int screen_h = 480;
+  uint32_t arm_args[4];
+  int placeholder_arm_argi[4];
+  int placeholder_argi[4];
+  int placeholders = 0;
+  int arm_argc = 0;
+
+  // zz9k load <arm-data-file> [offset hex or decimal]
+  // zz9k run -audio -screen -console 0x1234 45123 0xbeef $screen
+  // zz9k stop
+
+  int syntax_ok = 1;
+  for (int i=1; i<argc; i++) {
+    if (i==1) {
+      // command
+      if (!strcmp("load", argv[1])) {
+        load_mode = 1;
+      } else if (!strcmp("run",argv[1])) {
+        run_mode = 1;
+      } else if (!strcmp("stop",argv[1])) {
+        stop_mode = 1;
+      } else {
+        printf("Unknown command '%s'.\n", argv[1]);
+        syntax_ok = 0;
+        break;
+      }
+    } else {
+      // option
+      if (run_mode) {
+        if (!strcmp("-640x480",argv[i])) {
+          screen_mode = 1;
+          screen_w = 640;
+          screen_h = 480;
+        } else if (!strcmp("-320x240",argv[i])) {
+          screen_mode = 1;
+          screen_w = 320;
+          screen_h = 240;
+        } else if (!strcmp("-audio",argv[i])) {
+          audio_mode = 1;
+        } else if (!strcmp("-keyboard",argv[i])) {
+          keyboard_mode = 1;
+        } else if (!strcmp("-console",argv[i])) {
+          console_mode = 1;
+        } else if (!strcmp("-verbose",argv[i])) {
+          verbose_mode = 1;
+        } else if (argv[i][0] == '-') {
+          printf("Unknown run option '%s'\n", argv[i]);
+          syntax_ok = 0;
+          break;
+        } else {
+          if (arm_argc>4) {
+            printf("Too many arguments for ARM application (maximum 4).\n");
+            syntax_ok = 0;
+            break;
+          }
+          if (is_hex(argv[i])) {
+            // hex arg
+            arm_args[arm_argc++] = strtoul(&argv[i][2], NULL, 16);
+          } else if (argv[i][0] == '!') {
+            // special variable
+            if (placeholders>4) {
+              printf("Too many special variables (maximum 4).\n");
+              syntax_ok = 0;
+              break;
+            }
+            placeholder_argi[placeholders]=i;
+            placeholder_arm_argi[placeholders]=arm_argc++;
+            placeholders++;
+          } else {
+            // decimal arg
+            arm_args[arm_argc++] = strtoul(argv[i], NULL, 10);
+          }
+        }
+      } else if (stop_mode) {
+        printf("Surplus arguments for 'stop' command.\n");
+        syntax_ok = 0;
+        break;
+      } else if (load_mode) {
+        if (i==2) {
+          load_filename = argv[i];
+        } else if (i==3) {
+          if (is_hex(argv[i])) {
+            load_offset = strtoul(&argv[i][2], NULL, 16);
+          } else {
+            load_offset = strtoul(argv[i], NULL, 10);
+          }
+        } else {
+          printf("Surplus arguments for 'load' command.\n");
+          syntax_ok = 0;
+          break;
+        }
+      }
+    }
+  }
+
+  if (argc<2) {
+    syntax_ok = 0;
+  }
+
+  if (audio_mode && argc<3) {
+    printf("Not enough arguments for 'audio' command.\n");
+    syntax_ok = 0;
+  }
+
+  if (!syntax_ok) {
+    printf("\nUsage: zz9k load <arm-data-file> [offset hex or decimal]\n");
+    printf("       zz9k run [mode options] [arg0] ... [arg3]\n");
+    printf("       zz9k stop\n\n");
+    printf("Mode options (can be combined):\n");
+    printf("  -640x480 opens a 640x480x32 screen (bitmap address available in !screen variable)\n");
+    printf("  -320x240 opens a 320x240x32 screen (bitmap address available in !screen variable)\n");
+    printf("  -console attaches ARM application's input and output event streams to Amiga shell, line based\n");
+    printf("  -keyboard sends Amiga keyboard scan codes to ARM application as events\n");
+    printf("* -audio (experimental) streams raw audio generated by ARM application. not ready for use yet.\n\n");
+    
+    printf("arg0 - arg3 are numeric (unsigned 32-bit integer) arguments to the ARM application. For these, you can pass:\n");
+    printf("  a decimal number (e.g. 1280)\n");
+    printf("  a hexadecimal number prefixed with 0x (e.g. 0xdeadcafe)\n"); 
+    printf("  a special variable, one of: !screen !width !height !depth\n\n");
+    exit(1);
+  }
+
+  // find a ZZ9000
+  cd = (struct ConfigDev*)FindConfigDev(cd,0x6d6e,0x4);
+  if (!cd) {
+    cd = (struct ConfigDev*)FindConfigDev(cd,0x6d6e,0x3);
+  }
+  if (!cd) {
+    printf("Error: MNT ZZ9000 not found.\n");
+    exit(1);
+  }
+  
+  memory = (uint8_t*)(cd->cd_BoardAddr)+0x10000;
+  regs = (volatile MNTZZ9KRegs*)(cd->cd_BoardAddr);
+  
+  if (stop_mode) {
+    // reset application ARM core
+    regs->arm_run_hi = 0;
+    regs->arm_run_lo = 0;
+    printf("ARM core reset.\n");
+    exit(0);
+  }
+
+  if (load_mode) {
+    // load a file into ZZ9000 memory
+
+    uint8_t* dest = memory + ZZ9K_APP_SPACE - ZZ9K_MEM_START + load_offset;
+
+    if (verbose_mode) {
+      printf("Loading '%s' to ARM address 0x%lx (Amiga address 0x%lx)\n", argv[2], ZZ9K_APP_SPACE + load_offset, dest);
+    }
+
+    FILE* f = fopen(argv[2],"rb");
+    if (f) {
+      fseek(f, 0, SEEK_END);
+      long fsize = ftell(f);
+      fseek(f, 0, SEEK_SET);
+      size_t bytes_read = fread(dest, 1, fsize, f);
+      fclose(f);
+      printf("%lx\n",bytes_read);
+
+      //printf("CRC: %lx\n",crc8(0,dest,bytes_read));
+    } else {
+      printf("0\n");
+    }
+    
+    exit(0);
+  }
+  
+  if (run_mode) {
+    // TODO encapsulate all this audio junk
+    char* audio_buf = NULL;
+    const int audio_bufsz = 16000;
+    uint32_t t = 0;
+    uint32_t chipoffset = 0;
+    const uint32_t chunksz = 48000*2;
+    char audio_devopened;
+    struct MsgPort *audio_port;
+    struct IOAudio *aio[1];
+    volatile int16_t* src;
+
+    if (screen_mode) {
+      open_screen(screen_w, screen_h);
+
+      if (!zz9k_screen) {
+        printf("Error creating screen.\n");
+        exit(2);
+      }
+
+      // capture keypresses
+      if (keyboard_mode) {
+        setup_input();
+      }
+
+      // get screen bitmap address
+      uint32_t fb = ((uint32_t)zz9k_screen->BitMap.Planes[0])-(uint32_t)memory+(uint32_t)ZZ9K_MEM_START;
+      fb+=zz9k_screen->BarHeight*zz9k_screen->BitMap.BytesPerRow; // skip title bar
+
+      // fill in screen special variables
+      for (int i=0; i<placeholders; i++) {
+        int argi = placeholder_argi[i];
+        int arm_argi = placeholder_arm_argi[i];
+
+        if (!strcmp(argv[argi],"!screen")) {
+          arm_args[arm_argi] = fb;
+        } else if (!strcmp(argv[argi],"!width")) {
+          arm_args[arm_argi] = zz9k_screen->Width;
+        } else if (!strcmp(argv[argi],"!height")) {
+          arm_args[arm_argi] = zz9k_screen->BitMap.Rows;
+        } else if (!strcmp(argv[argi],"!depth")) {
+          arm_args[arm_argi] = zz9k_screen->BitMap.Depth;
+        }
+      }
+    }
+
+    if (audio_mode) {
+      // TODO: this is currently just a sketch. this should loop
+      // over a buffer space and request more buffers via events
+            
+      if (!(audio_port=CreatePort(0,0))) {
+        printf("Error: couldn't create Audio message Port.\n");
+        exit(5);
+      }
+      for (int k=0; k<1; k++) {
+        if (!(aio[k]=(struct IOAudio *)CreateExtIO(audio_port,sizeof(struct IOAudio)))) {
+          printf("Error: couldn't create IOAudio %d\n",k);
+          exit(5);
+        }
+      }
+
+      char channels[] = {1,2,4,8};
+      
+      // Set up request to Audio port
+      aio[0]->ioa_Request.io_Command = ADCMD_ALLOCATE;
+      aio[0]->ioa_Request.io_Flags = ADIOF_NOWAIT;
+      aio[0]->ioa_AllocKey = 0;
+      aio[0]->ioa_Data = channels;
+      aio[0]->ioa_Length = sizeof(channels);
+
+      if (!(OpenDevice("audio.device", 0L, (struct IORequest *) aio[0], 0L)))
+        audio_devopened = TRUE;
+      else {
+        printf("Error: couldn't open audio.device\n");
+        exit(5);
+      }
+
+      // Paula playback buffer in chipmem
+      audio_buf = AllocMem(audio_bufsz,MEMF_CHIP);
+      src = (volatile int16_t*)(((char*)arm_args[2])-(uint32_t)ZZ9K_MEM_START+(uint32_t)memory);
+      
+      if (!audio_buf) {
+        printf("Error: allocating chipmem failed.\n");
+      } else {
+        memset(audio_buf,0,audio_bufsz);
+      }
+    }
+
+    // pass ARM app arguments
+    for (int i=0; i<arm_argc; i++) {
+      if (verbose_mode) {
+        printf("ARM arg%d: %ld (%lx)\n", i, arm_args[i], arm_args[i]);
+      }
+      
+      regs->arm_arg[i*2]   = arm_args[i]>>16;
+      regs->arm_arg[i*2+1] = arm_args[i]&0xffff;
+    }
+    regs->arm_argc   = arm_argc;
+
+    // pass ARM run address
+    regs->arm_run_hi = (ZZ9K_APP_SPACE + load_offset)>>16;
+    regs->arm_run_lo = (ZZ9K_APP_SPACE + load_offset)&0xffff;
+
+    uint16_t last_ser = 0;
+    uint32_t timeout = 0;
+      
+    // main loop
+    while (1) {
+      if (console_mode) {
+        // send kbd, mouse events and receive events
+        // exit on quit event
+        int chr = getc(stdin);
+        if (chr) {
+          regs->arm_event_code = chr;
+        }
+
+        if (chr=='\n') {
+          char done = 0;
+          while (!done) {
+            uint16_t armchr = regs->arm_event_code;
+            uint16_t armser = regs->arm_event_serial;
+            
+            if (armser!=last_ser) {
+              last_ser = armser;
+              putc(armchr, stdout);
+              timeout = 0;
+            }
+            timeout++;
+            if (timeout>=10000) {
+              done = 1;
+              timeout = 0;
+            }
+          }
+        }
+      }
+
+      if (audio_mode) {
+        aio[0]->ioa_Request.io_Command = CMD_WRITE;
+        aio[0]->ioa_Request.io_Flags = ADIOF_PERVOL;
+        aio[0]->ioa_Data = audio_buf+chipoffset; // sample
+        aio[0]->ioa_Length = audio_bufsz/2;
+        aio[0]->ioa_Period = 3546895L/8000;
+        aio[0]->ioa_Volume = 0xff;
+        aio[0]->ioa_Cycles = 1;
+        BeginIO((struct IORequest *)aio[0]);
+
+        if (chipoffset == 0) {
+          chipoffset = audio_bufsz/2;
+        } else {
+          chipoffset = 0;
+        }
+          
+        // downsample 48000 stereo to 8000 mono
+        // FIXME lowpass filter
+        // FIXME let ARM do this
+        uint32_t bi = 0;
+        for (uint32_t i=0; i<chunksz; i+=2*6) {
+          int32_t srcss=src[i+audio_offset];
+          uint32_t srcs=srcss+=65536/2;
+          audio_buf[chipoffset+bi] = (((srcs&0xff)<<8|(srcs&0xff00)>>8))>>8;
+          bi++;
+          if (bi>=audio_bufsz/2) break;
+        }
+        audio_offset+=chunksz;
+        
+        WaitIO((struct IORequest *)aio[0]);
+        
+        // FIXME arbitrary end
+        if (audio_offset>500*chunksz) break;
+      }
+
+      // end on mouse click
+      volatile uint8_t* mreg = (volatile uint8_t*)0xbfe001;
+      if (!(*mreg&(1<<6))) break;
+    }
+
+    // done running
+    // reset application ARM core
+    regs->arm_run_hi = 0;
+    regs->arm_run_lo = 0;
+    if (verbose_mode) {
+      printf("ARM core reset.\n");
+    }
+    
+    cleanup();
+    if (audio_mode) {
+      // FIXME consolidate with cleanup()
+      FreeMem(audio_buf,audio_bufsz);
+      if (audio_devopened) {
+        CloseDevice((struct IORequest *)aio[0]);
+      }
+      for (int k=0; k<1; k++) {
+        if (aio[k]) {
+          DeleteExtIO((struct IORequest *)aio[k]);
+          aio[k] = NULL;
+        }
+      }
+      if (audio_port) {
+        DeletePort(audio_port);
+        audio_port = NULL;
+      }
+    }
+  }
+}
-- 
GitLab