]> git.sesse.net Git - pistorm/commitdiff
Adapt picmd to work on PiStorm + A314 emulation
authorbeeanyew <beeanyew@gmail.com>
Tue, 18 May 2021 03:15:42 +0000 (05:15 +0200)
committerbeeanyew <beeanyew@gmail.com>
Tue, 18 May 2021 03:15:42 +0000 (05:15 +0200)
Requires both .cfg files from `files_pi` to be in /etc/opt/a314, and picmd.py needs to be started after the emulator.
I have no idea how to better streamline this, but I'm sure someone will come up with a brilliant solution.

a314/files_pi/a314fs.conf [new file with mode: 0644]
a314/files_pi/picmd.conf [new file with mode: 0644]
a314/files_pi/picmd.py [new file with mode: 0644]
a314/software-amiga/a314d [deleted file]
a314/software-amiga/pi
a314/software-amiga/pi_pistorm/build.bat [new file with mode: 0644]
a314/software-amiga/pi_pistorm/pi.c [new file with mode: 0644]
a314/software-amiga/spi-a314.dtbo [deleted file]

diff --git a/a314/files_pi/a314fs.conf b/a314/files_pi/a314fs.conf
new file mode 100644 (file)
index 0000000..6361277
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "devices": {
+    "PI0": {
+      "volume": "PiDisk",
+      "path": "/home/pi/a314shared"
+    }
+  }
+}
diff --git a/a314/files_pi/picmd.conf b/a314/files_pi/picmd.conf
new file mode 100644 (file)
index 0000000..783403a
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "paths": ["/home/pi/amiga_sdk/vbcc/bin"],
+  "env_vars": {
+    "VBCC": "/home/pi/amiga_sdk/vbcc"
+  },
+  "sgr_map": {
+  }
+}
diff --git a/a314/files_pi/picmd.py b/a314/files_pi/picmd.py
new file mode 100644 (file)
index 0000000..e288f5c
--- /dev/null
@@ -0,0 +1,391 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2018-2021 Niklas Ekström
+
+import select
+import sys
+import socket
+import threading
+import time
+import os
+import struct
+import pty
+import signal
+import termios
+import fcntl
+import logging
+import json
+
+logging.basicConfig(format = '%(levelname)s, %(asctime)s, %(name)s, line %(lineno)d: %(message)s')
+logger = logging.getLogger(__name__)
+logger.setLevel(logging.DEBUG)
+
+FS_CFG_FILE = '/etc/opt/a314/a314fs.conf'
+PICMD_CFG_FILE = '/etc/opt/a314/picmd.conf'
+
+volume_paths = {}
+search_path = ''
+env_vars = {}
+sgr_map = {}
+
+def load_cfg():
+    with open(FS_CFG_FILE, 'rt') as f:
+        cfg = json.load(f)
+        devs = cfg['devices']
+        for _, dev in devs.items():
+            volume_paths[dev['volume']] = dev['path']
+
+    global search_path
+    search_path = os.getenv('PATH')
+
+    with open(PICMD_CFG_FILE, 'rt') as f:
+        cfg = json.load(f)
+
+        if 'paths' in cfg:
+            search_path = ':'.join(cfg['paths']) + ':' + search_path
+            os.environ['PATH'] = search_path
+
+        if 'env_vars' in cfg:
+            for key, val in cfg['env_vars'].items():
+                env_vars[key] = val
+
+        if 'sgr_map' in cfg:
+            for key, val in cfg['sgr_map'].items():
+                sgr_map[key] = str(val)
+
+load_cfg()
+
+MSG_REGISTER_REQ        = 1
+MSG_REGISTER_RES        = 2
+MSG_DEREGISTER_REQ      = 3
+MSG_DEREGISTER_RES      = 4
+MSG_READ_MEM_REQ        = 5
+MSG_READ_MEM_RES        = 6
+MSG_WRITE_MEM_REQ       = 7
+MSG_WRITE_MEM_RES       = 8
+MSG_CONNECT             = 9
+MSG_CONNECT_RESPONSE    = 10
+MSG_DATA                = 11
+MSG_EOS                 = 12
+MSG_RESET               = 13
+
+def wait_for_msg():
+    header = b''
+    while len(header) < 9:
+        data = drv.recv(9 - len(header))
+        if not data:
+            logger.error('Connection to a314d was closed, terminating.')
+            exit(-1)
+        header += data
+    (plen, stream_id, ptype) = struct.unpack('=IIB', header)
+    payload = b''
+    while len(payload) < plen:
+        data = drv.recv(plen - len(payload))
+        if not data:
+            logger.error('Connection to a314d was closed, terminating.')
+            exit(-1)
+        payload += data
+    return (stream_id, ptype, payload)
+
+def send_register_req(name):
+    m = struct.pack('=IIB', len(name), 0, MSG_REGISTER_REQ) + name
+    drv.sendall(m)
+
+def send_read_mem_req(address, length):
+    m = struct.pack('=IIBII', 8, 0, MSG_READ_MEM_REQ, address, length)
+    drv.sendall(m)
+
+def read_mem(address, length):
+    send_read_mem_req(address, length)
+    stream_id, ptype, payload = wait_for_msg()
+    if ptype != MSG_READ_MEM_RES:
+        logger.error('Expected MSG_READ_MEM_RES but got %s. Shutting down.', ptype)
+        exit(-1)
+    return payload
+
+def send_write_mem_req(address, data):
+    m = struct.pack('=IIBI', 4 + len(data), 0, MSG_WRITE_MEM_REQ, address) + data
+    drv.sendall(m)
+
+def write_mem(address, data):
+    send_write_mem_req(address, data)
+    stream_id, ptype, payload = wait_for_msg()
+    if ptype != MSG_WRITE_MEM_RES:
+        logger.error('Expected MSG_WRITE_MEM_RES but got %s. Shutting down.', ptype)
+        exit(-1)
+
+def send_connect_response(stream_id, result):
+    m = struct.pack('=IIBB', 1, stream_id, MSG_CONNECT_RESPONSE, result)
+    drv.sendall(m)
+
+def send_data(stream_id, data):
+    m = struct.pack('=IIB', len(data), stream_id, MSG_DATA) + data
+    drv.sendall(m)
+
+def send_eos(stream_id):
+    m = struct.pack('=IIB', 0, stream_id, MSG_EOS)
+    drv.sendall(m)
+
+def send_reset(stream_id):
+    m = struct.pack('=IIB', 0, stream_id, MSG_RESET)
+    drv.sendall(m)
+
+sessions = {}
+
+class PiCmdSession(object):
+    def __init__(self, stream_id):
+        self.stream_id = stream_id
+        self.pid = 0
+
+        self.first_packet = True
+        self.reset_after = None
+
+        self.rasp_was_esc = False
+        self.rasp_in_cs = False
+        self.rasp_holding = ''
+
+        self.amiga_in_cs = False
+        self.amiga_holding = ''
+
+    def process_amiga_ansi(self, data):
+        data = data.decode('latin-1')
+        out = ''
+        for c in data:
+            if not self.amiga_in_cs:
+                if c == '\x9b':
+                    self.amiga_in_cs = True
+                    self.amiga_holding = '\x1b['
+                else:
+                    out += c
+            else: # self.amiga_in_cs
+                self.amiga_holding += c
+                if c >= chr(0x40) and c <= chr(0x7e):
+                    if c == 'r':
+                        # Window Bounds Report
+                        # ESC[1;1;rows;cols r
+                        rows, cols = map(int, self.amiga_holding[6:-2].split(';'))
+                        winsize = struct.pack('HHHH', rows, cols, 0, 0)
+                        fcntl.ioctl(self.fd, termios.TIOCSWINSZ, winsize)
+                    elif c == '|':
+                        # Input Event Report
+                        # ESC[12;0;0;x;x;x;x;x|
+                        # Window resized
+                        send_data(self.stream_id, b'\x9b' + b'0 q')
+                    else:
+                        out += self.amiga_holding
+                    self.amiga_holding = ''
+                    self.amiga_in_cs = False
+        if len(out) != 0:
+            os.write(self.fd, out.encode('utf-8'))
+
+    def process_msg_data(self, data):
+        if self.first_packet:
+            if len(data) != 8:
+                send_reset(self.stream_id)
+                del sessions[self.stream_id]
+            else:
+                address, length = struct.unpack('>II', data)
+                buf = read_mem(address, length)
+
+                ind = 0
+                rows, cols = struct.unpack('>HH', buf[ind:ind+4])
+                ind += 4
+
+                component_count = buf[ind]
+                ind += 1
+
+                components = []
+                for _ in range(component_count):
+                    n = buf[ind]
+                    ind += 1
+                    components.append(buf[ind:ind+n].decode('latin-1'))
+                    ind += n
+
+                arg_count = buf[ind]
+                ind += 1
+
+                args = []
+                for _ in range(arg_count):
+                    n = buf[ind]
+                    ind += 1
+                    args.append(buf[ind:ind+n].decode('latin-1'))
+                    ind += n
+
+                if arg_count == 0:
+                    args.append('bash')
+
+                self.pid, self.fd = pty.fork()
+                if self.pid == 0:
+                    for key, val in env_vars.items():
+                        os.putenv(key, val)
+                    os.putenv('PATH', search_path)
+                    os.putenv('TERM', 'ansi')
+                    winsize = struct.pack('HHHH', rows, cols, 0, 0)
+                    fcntl.ioctl(sys.stdin, termios.TIOCSWINSZ, winsize)
+                    if component_count != 0 and components[0] in volume_paths:
+                        path = volume_paths[components[0]]
+                        os.chdir(os.path.join(path, *components[1:]))
+                    else:
+                        os.chdir(os.getenv('HOME', '/'))
+                    os.execvp(args[0], args)
+
+                self.first_packet = False
+
+        elif self.pid:
+            self.process_amiga_ansi(data)
+
+    def close(self):
+        if self.pid:
+            os.kill(self.pid, signal.SIGTERM)
+            self.pid = 0
+            os.close(self.fd)
+        del sessions[self.stream_id]
+
+    def process_rasp_ansi(self, text):
+        text = text.decode('utf-8')
+        out = ''
+        for c in text:
+            if not self.rasp_in_cs:
+                if not self.rasp_was_esc:
+                    if c == '\x1b':
+                        self.rasp_was_esc = True
+                    else:
+                        out += c
+                else: # self.rasp_was_esc
+                    if c == '[':
+                        self.rasp_was_esc = False
+                        self.rasp_in_cs = True
+                        self.rasp_holding = '\x1b['
+                    elif c == '\x1b':
+                        out += '\x1b'
+                    else:
+                        out += '\x1b'
+                        out += c
+                        self.rasp_was_esc = False
+            else: # self.rasp_in_cs
+                self.rasp_holding += c
+                if c >= chr(0x40) and c <= chr(0x7e):
+                    if c == 'm':
+                        # Select Graphic Rendition
+                        # ESC[30;37m
+                        attrs = self.rasp_holding[2:-1].split(';')
+                        attrs = [sgr_map[a] if a in sgr_map else a for a in attrs]
+                        out += '\x1b[' + (';'.join(attrs)) + 'm'
+                    else:
+                        out += self.rasp_holding
+                    self.rasp_holding = ''
+                    self.rasp_in_cs = False
+        return out.encode('latin-1', 'replace')
+
+    def handle_text(self):
+        try:
+            text = os.read(self.fd, 1024)
+            text = self.process_rasp_ansi(text)
+            while len(text) > 0:
+                take = min(len(text), 252)
+                send_data(self.stream_id, text[:take])
+                text = text[take:]
+        except:
+            #os.close(self.fd)
+            os.kill(self.pid, signal.SIGTERM)
+            self.pid = 0
+            send_eos(self.stream_id)
+            self.reset_after = time.time() + 10
+
+    def handle_timeout(self):
+        if self.reset_after and self.reset_after < time.time():
+            send_reset(self.stream_id)
+            del sessions[self.stream_id]
+
+    def fileno(self):
+        return self.fd
+
+def process_drv_msg(stream_id, ptype, payload):
+    if ptype == MSG_CONNECT:
+        if payload == b'picmd':
+            s = PiCmdSession(stream_id)
+            sessions[stream_id] = s
+            send_connect_response(stream_id, 0)
+        else:
+            send_connect_response(stream_id, 3)
+    elif stream_id in sessions:
+        s = sessions[stream_id]
+
+        if ptype == MSG_DATA:
+            s.process_msg_data(payload)
+        elif ptype == MSG_EOS:
+            if s.pid:
+                send_eos(s.stream_id)
+            s.close()
+        elif ptype == MSG_RESET:
+            s.close()
+
+done = False
+
+try:
+    idx = sys.argv.index('-ondemand')
+except ValueError:
+    idx = -1
+
+if idx != -1:
+    fd = int(sys.argv[idx + 1])
+    drv = socket.socket(fileno=fd)
+else:
+    drv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    drv.connect(('localhost', 7110))
+    drv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+
+    send_register_req(b'picmd')
+    _, _, payload = wait_for_msg()
+    if payload[0] != 1:
+        logger.error('Unable to register picmd with driver, shutting down')
+        drv.close()
+        done = True
+
+rbuf = b''
+
+if not done:
+    logger.info('picmd server is running')
+
+while not done:
+    sel_fds = [drv] + [s for s in sessions.values() if s.pid]
+    if idx == -1:
+        sel_fds.append(sys.stdin)
+    rfd, wfd, xfd = select.select(sel_fds, [], [], 5.0)
+
+    for fd in rfd:
+        if fd == sys.stdin:
+            line = sys.stdin.readline()
+            if not line or line.startswith('quit'):
+                for s in sessions.values():
+                    s.close()
+                drv.close()
+                done = True
+        elif fd == drv:
+            buf = drv.recv(1024)
+            if not buf:
+                for s in sessions.values():
+                    s.close()
+                drv.close()
+                done = True
+            else:
+                rbuf += buf
+                while True:
+                    if len(rbuf) < 9:
+                        break
+
+                    (plen, stream_id, ptype) = struct.unpack('=IIB', rbuf[:9])
+                    if len(rbuf) < 9 + plen:
+                        break
+
+                    rbuf = rbuf[9:]
+                    payload = rbuf[:plen]
+                    rbuf = rbuf[plen:]
+
+                    process_drv_msg(stream_id, ptype, payload)
+        else:
+            fd.handle_text()
+
+    for s in sessions.values():
+        s.handle_timeout()
diff --git a/a314/software-amiga/a314d b/a314/software-amiga/a314d
deleted file mode 100644 (file)
index 4b3be1f..0000000
Binary files a/a314/software-amiga/a314d and /dev/null differ
index 826b26cd59383ae560e1d72f51ddebebdc230039..474dabecd7d1609de06e2b750dca80a00ff75810 100644 (file)
Binary files a/a314/software-amiga/pi and b/a314/software-amiga/pi differ
diff --git a/a314/software-amiga/pi_pistorm/build.bat b/a314/software-amiga/pi_pistorm/build.bat
new file mode 100644 (file)
index 0000000..c042803
--- /dev/null
@@ -0,0 +1 @@
+vc pi.c -lamiga -o ../pi
diff --git a/a314/software-amiga/pi_pistorm/pi.c b/a314/software-amiga/pi_pistorm/pi.c
new file mode 100644 (file)
index 0000000..2624f8a
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) 2018-2021 Niklas Ekström
+ */
+
+#include <exec/types.h>
+#include <exec/ports.h>
+#include <exec/tasks.h>
+#include <exec/memory.h>
+
+#include <libraries/dos.h>
+#include <libraries/dosextens.h>
+
+#include <proto/dos.h>
+#include <proto/exec.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <clib/alib_protos.h>
+#include <proto/expansion.h>
+#include <clib/expansion_protos.h>
+
+#include "../../a314device/a314.h"
+#include "../../a314device/proto_a314.h"
+
+#define PICMD_SERVICE_NAME "picmd"
+
+#define ID_314_DISK (('3' << 24) | ('1' << 16) | ('4' << 8))
+
+struct MsgPort *sync_mp;
+struct MsgPort *async_mp;
+
+struct A314_IORequest *read_ior;
+struct A314_IORequest *sync_ior;
+
+struct Library *A314Base;
+
+struct FileHandle *con;
+
+ULONG socket;
+
+UBYTE arbuf[256];
+
+struct StandardPacket sync_sp;
+struct StandardPacket wait_sp;
+
+BOOL pending_a314_read = FALSE;
+BOOL pending_con_wait = FALSE;
+BOOL stream_closed = FALSE;
+
+ULONG a314_addr = 0xFFFFFFFF;
+
+//#define DEBUG printf
+#define DEBUG(...)
+
+void put_con_sp(struct MsgPort *reply_port, struct StandardPacket *sp, LONG action, LONG arg1, LONG arg2, LONG arg3)
+{
+       sp->sp_Msg.mn_Node.ln_Type = NT_MESSAGE;
+       sp->sp_Msg.mn_Node.ln_Pri = 0;
+       sp->sp_Msg.mn_Node.ln_Name = (char *)&(sp->sp_Pkt);
+       sp->sp_Msg.mn_Length = sizeof(struct StandardPacket);
+       sp->sp_Msg.mn_ReplyPort = reply_port;
+       sp->sp_Pkt.dp_Link = &(sp->sp_Msg);
+       sp->sp_Pkt.dp_Port = reply_port;
+       sp->sp_Pkt.dp_Type = action;
+       sp->sp_Pkt.dp_Arg1 = arg1;
+       sp->sp_Pkt.dp_Arg2 = arg2;
+       sp->sp_Pkt.dp_Arg3 = arg3;
+       PutMsg(con->fh_Type, &(sp->sp_Msg));
+}
+
+LONG set_screen_mode(LONG mode)
+{
+       put_con_sp(sync_mp, &sync_sp, ACTION_SCREEN_MODE, mode, 0, 0);
+       Wait(1L << sync_mp->mp_SigBit);
+       GetMsg(sync_mp);
+       return sync_sp.sp_Pkt.dp_Res1;
+}
+
+LONG con_write(char *s, int length)
+{
+       put_con_sp(sync_mp, &sync_sp, ACTION_WRITE, con->fh_Arg1, (LONG)s, length);
+       Wait(1L << sync_mp->mp_SigBit);
+       GetMsg(sync_mp);
+       return sync_sp.sp_Pkt.dp_Res1;
+}
+
+LONG con_read(char *s, int length)
+{
+       put_con_sp(sync_mp, &sync_sp, ACTION_READ, con->fh_Arg1, (LONG)s, length);
+       Wait(1L << sync_mp->mp_SigBit);
+       GetMsg(sync_mp);
+       return sync_sp.sp_Pkt.dp_Res1;
+}
+
+void start_con_wait()
+{
+       put_con_sp(async_mp, &wait_sp, ACTION_WAIT_CHAR, 100000, 0, 0);
+       pending_con_wait = TRUE;
+}
+
+void start_a314_cmd(struct MsgPort *reply_port, struct A314_IORequest *ior, UWORD cmd, char *buffer, int length)
+{
+       ior->a314_Request.io_Message.mn_ReplyPort = reply_port;
+       ior->a314_Request.io_Command = cmd;
+       ior->a314_Request.io_Error = 0;
+       ior->a314_Socket = socket;
+       ior->a314_Buffer = buffer;
+       ior->a314_Length = length;
+       SendIO((struct IORequest *)ior);
+}
+
+BYTE a314_connect(char *name)
+{
+       socket = time(NULL);
+       start_a314_cmd(sync_mp, sync_ior, A314_CONNECT, name, strlen(name));
+       Wait(1L << sync_mp->mp_SigBit);
+       GetMsg(sync_mp);
+       return sync_ior->a314_Request.io_Error;
+}
+
+BYTE a314_write(char *buffer, int length)
+{
+       ULONG *bef = (ULONG *)buffer;
+       DEBUG("Buf[0]: %.8X Buf[1]: %.8X\n", bef[0], bef[1]);
+       DEBUG("Len: %d\n", length);
+       start_a314_cmd(sync_mp, sync_ior, A314_WRITE, buffer, length);
+       Wait(1L << sync_mp->mp_SigBit);
+       GetMsg(sync_mp);
+       return sync_ior->a314_Request.io_Error;
+}
+
+BYTE a314_eos()
+{
+       start_a314_cmd(sync_mp, sync_ior, A314_EOS, NULL, 0);
+       Wait(1L << sync_mp->mp_SigBit);
+       GetMsg(sync_mp);
+       return sync_ior->a314_Request.io_Error;
+}
+
+BYTE a314_reset()
+{
+       start_a314_cmd(sync_mp, sync_ior, A314_RESET, NULL, 0);
+       Wait(1L << sync_mp->mp_SigBit);
+       GetMsg(sync_mp);
+       return sync_ior->a314_Request.io_Error;
+}
+
+void start_a314_read()
+{
+       start_a314_cmd(async_mp, read_ior, A314_READ, arbuf, 255);
+       pending_a314_read = TRUE;
+}
+
+void handle_con_wait_completed()
+{
+       DEBUG("handling con wait completed.\n");
+       pending_con_wait = FALSE;
+
+       if (stream_closed)
+               return;
+
+       if (wait_sp.sp_Pkt.dp_Res1 == DOSFALSE)
+       {
+               start_con_wait();
+       }
+       else
+       {
+               unsigned char buf[64];
+               int len = con_read(buf, sizeof(buf));
+
+               if (len == 0 || len == -1)
+               {
+                       a314_reset();
+                       stream_closed = TRUE;
+               }
+               else
+               {
+                       a314_write(buf, len);
+                       start_con_wait();
+               }
+       }
+}
+
+void handle_a314_read_completed()
+{
+       DEBUG("handling read completed.\n");
+       pending_a314_read = FALSE;
+
+       if (stream_closed)
+               return;
+
+       int res = read_ior->a314_Request.io_Error;
+       if (res == A314_READ_OK)
+       {
+               UBYTE *p = read_ior->a314_Buffer;
+               int len = read_ior->a314_Length;
+
+               con_write(p, len);
+               start_a314_read();
+       }
+       else if (res == A314_READ_EOS)
+       {
+               a314_eos();
+               stream_closed = TRUE;
+       }
+       else if (res == A314_READ_RESET)
+       {
+               stream_closed = TRUE;
+       }
+}
+
+UBYTE *create_and_send_start_msg(int *buffer_len, BPTR current_dir, int argc, char **argv, short rows, short cols)
+{
+       int buf_len = 6;
+
+       int component_count = 0;
+       UBYTE *components[20];
+
+       DEBUG("casmm: SetupDir\n");
+       if (current_dir != 0)
+       {
+               struct FileLock *fl = (struct FileLock *)BADDR(current_dir);
+               struct DeviceList *dl = (struct DeviceList *)BADDR(fl->fl_Volume);
+
+               if (dl->dl_DiskType == ID_314_DISK)
+               {
+                       struct FileInfoBlock *fib = AllocMem(sizeof(struct FileInfoBlock), 0);
+
+                       BPTR lock = DupLock(current_dir);
+
+                       while (lock != 0)
+                       {
+                               if (Examine(lock, fib) == 0)
+                               {
+                                       UnLock(lock);
+                                       break;
+                               }
+
+                               int n = strlen(fib->fib_FileName);
+                               UBYTE *p = AllocMem(n + 1, 0);
+                               p[0] = (UBYTE)n;
+                               memcpy(&p[1], fib->fib_FileName, n);
+                               components[component_count++] = p;
+
+                               buf_len += n + 1;
+
+                               BPTR child = lock;
+                               lock = ParentDir(child);
+                               UnLock(child);
+                       }
+
+                       FreeMem(fib, sizeof(struct FileInfoBlock));
+               }
+       }
+
+       DEBUG("casmm: Stage 2\n");
+       for (int i = 1; i < argc; i++)
+               buf_len += strlen(argv[i]) + 1;
+
+       UBYTE *buffer = AllocMem(buf_len, MEMF_FAST);
+
+       UBYTE *p = buffer;
+
+       *(short *)p = rows;
+       p += 2;
+       *(short *)p = cols;
+       p += 2;
+
+       DEBUG("casmm: Stage 3\n");
+       DEBUG("p: %.8X\n", (ULONG)p);
+       DEBUG("component count: %d\n", component_count);
+       *p++ = (UBYTE)component_count;
+       for (int i = 0; i < component_count; i++)
+       {
+               UBYTE *q = components[component_count - 1 - i];
+               int n = *q;
+               memcpy(p, q, n + 1);
+               p += n + 1;
+               FreeMem(q, n + 1);
+       }
+
+       DEBUG("casmm: Stage 4\n");
+       *p++ = (UBYTE)(argc - 1);
+       for (int i = 1; i < argc; i++)
+       {
+               UBYTE *q = (UBYTE *)argv[i];
+               int n = strlen(q);
+               *p++ = (UBYTE)n;
+               memcpy(p, q, n);
+               p += n;
+       }
+
+       DEBUG("casmm: Stage 5\n");
+       ULONG buf_desc[2] = {(ULONG)buffer, buf_len};
+       DEBUG("casmm: Stage 6\n");
+       a314_write((char *)buf_desc, sizeof(buf_desc));
+
+       DEBUG("casmm: Stage 7\n");
+       *buffer_len = buf_len;
+       return buffer;
+}
+
+int main(int argc, char **argv)
+{
+       ULONG board_addr = 0xFFFFFFFF;
+    struct ExpansionBase *ExpansionBase = (struct ExpansionBase *)OpenLibrary((STRPTR)"expansion.library", 0L);
+
+    if (ExpansionBase == NULL) {
+           printf("Failed to open expansion.library.\n");
+               return 0;
+       }
+       else {
+               struct ConfigDev* cd = NULL;
+               cd = (struct ConfigDev*)FindConfigDev(cd, 2011, 0xA3);
+               if (cd != NULL)
+                       board_addr = (unsigned int)cd->cd_BoardAddr;
+               else {
+                       printf ("Failed to find A314 emulation device.\n");
+               CloseLibrary((struct Library *)ExpansionBase);
+                       return 0;
+               }
+        CloseLibrary((struct Library *)ExpansionBase);
+       }
+       printf ("A314 emulation device found at $%.8X\n", board_addr);
+       a314_addr = board_addr;
+
+       sync_mp = CreatePort(NULL, 0);
+       if (sync_mp == NULL)
+       {
+               printf("Unable to create sync reply message port\n");
+               return 0;
+       }
+       DEBUG("Created sync reply message port.\n");
+
+       async_mp = CreatePort(NULL, 0);
+       if (async_mp == NULL)
+       {
+               printf("Unable to create async reply message port\n");
+               DeletePort(sync_mp);
+               return 0;
+       }
+       DEBUG("Created async reply message port.\n");
+
+       sync_ior = (struct A314_IORequest *)CreateExtIO(sync_mp, sizeof(struct A314_IORequest));
+       if (sync_ior == NULL)
+       {
+               printf("Unable to create io request for synchronous commands\n");
+               DeletePort(async_mp);
+               DeletePort(sync_mp);
+               return 0;
+       }
+       DEBUG("Created IORequest for synchronous commands.\n");
+
+       read_ior = (struct A314_IORequest *)CreateExtIO(sync_mp, sizeof(struct A314_IORequest));
+       if (read_ior == NULL)
+       {
+               printf("Unable to create io request for reads\n");
+               DeleteExtIO((struct IORequest *)sync_ior);
+               DeletePort(async_mp);
+               DeletePort(sync_mp);
+               return 0;
+       }
+       DEBUG("Created IORequest for reads.\n");
+
+       if (OpenDevice(A314_NAME, 0, (struct IORequest *)sync_ior, 0) != 0)
+       {
+               printf("Unable to open a314.device\n");
+               DeleteExtIO((struct IORequest *)read_ior);
+               DeleteExtIO((struct IORequest *)sync_ior);
+               DeletePort(async_mp);
+               DeletePort(sync_mp);
+               return 0;
+       }
+       DEBUG("Opened a314.device.\n");
+
+       memcpy(read_ior, sync_ior, sizeof(struct A314_IORequest));
+
+       A314Base = &(sync_ior->a314_Request.io_Device->dd_Library);
+
+       if (a314_connect(PICMD_SERVICE_NAME) != A314_CONNECT_OK)
+       {
+               printf("Unable to connect to picmd service\n");
+               CloseDevice((struct IORequest *)sync_ior);
+               DeleteExtIO((struct IORequest *)read_ior);
+               DeleteExtIO((struct IORequest *)sync_ior);
+               DeletePort(async_mp);
+               DeletePort(sync_mp);
+               return 0;
+       }
+       DEBUG("Connected to picmd service.\n");
+
+       struct Process *proc = (struct Process *)FindTask(NULL);
+       con = (struct FileHandle *)BADDR(proc->pr_CIS);
+
+       set_screen_mode(DOSTRUE);
+       DEBUG("Set screen mode.\n");
+
+       con_write("\x9b" "0 q", 4);
+
+       int len = con_read(arbuf, 32);  // "\x9b" "1;1;33;77 r"
+       if (len < 10 || arbuf[len - 1] != 'r')
+       {
+               printf("Failure to receive window bounds report\n");
+               set_screen_mode(DOSFALSE);
+               a314_reset();
+               CloseDevice((struct IORequest *)sync_ior);
+               DeleteExtIO((struct IORequest *)read_ior);
+               DeleteExtIO((struct IORequest *)sync_ior);
+               DeletePort(async_mp);
+               DeletePort(sync_mp);
+               return 0;
+       }
+       DEBUG("Received window bounds report.\n");
+
+       con_write("\x9b" "12{", 4);
+
+       int start = 5;
+       int ind = start;
+       while (arbuf[ind] != ';')
+               ind++;
+       arbuf[ind] = 0;
+       int rows = atoi(arbuf + start);
+       ind++;
+       start = ind;
+       while (arbuf[ind] != ' ')
+               ind++;
+       arbuf[ind] = 0;
+       int cols = atoi(arbuf + start);
+
+       int start_msg_len;
+       DEBUG("Sending start message.\n");
+       UBYTE *start_msg = create_and_send_start_msg(&start_msg_len, proc->pr_CurrentDir, argc, argv, (short)rows, (short)cols);
+       DEBUG("Sent start message.\n");
+
+       DEBUG("Started con wait.\n");
+       start_con_wait();
+       DEBUG("Started A314 read.\n");
+       start_a314_read();
+
+       ULONG portsig = 1L << async_mp->mp_SigBit;
+
+       DEBUG("Entering main loop.\n");
+       while (TRUE)
+       {
+               ULONG signal = Wait(portsig | SIGBREAKF_CTRL_C);
+
+               if (signal & portsig)
+               {
+                       struct Message *msg;
+                       while (msg = GetMsg(async_mp))
+                       {
+                               if (msg == (struct Message *)&wait_sp)
+                                       handle_con_wait_completed();
+                               else if (msg == (struct Message *)read_ior)
+                                       handle_a314_read_completed();
+                       }
+               }
+
+               if (stream_closed && !pending_a314_read && !pending_con_wait)
+                       break;
+       }
+
+       set_screen_mode(DOSFALSE);
+
+       FreeMem(start_msg, start_msg_len);
+
+       CloseDevice((struct IORequest *)sync_ior);
+       DeleteExtIO((struct IORequest *)read_ior);
+       DeleteExtIO((struct IORequest *)sync_ior);
+       DeletePort(async_mp);
+       DeletePort(sync_mp);
+       return 0;
+}
diff --git a/a314/software-amiga/spi-a314.dtbo b/a314/software-amiga/spi-a314.dtbo
deleted file mode 100644 (file)
index b799fd4..0000000
Binary files a/a314/software-amiga/spi-a314.dtbo and /dev/null differ