--- /dev/null
+#!/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()
--- /dev/null
+/*
+ * 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;
+}