From: beeanyew Date: Tue, 18 May 2021 03:15:42 +0000 (+0200) Subject: Adapt picmd to work on PiStorm + A314 emulation X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=e7e393b324ebea0405eaf1df6b0e083c220909b9;p=pistorm Adapt picmd to work on PiStorm + A314 emulation 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. --- diff --git a/a314/files_pi/a314fs.conf b/a314/files_pi/a314fs.conf new file mode 100644 index 0000000..6361277 --- /dev/null +++ b/a314/files_pi/a314fs.conf @@ -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 index 0000000..783403a --- /dev/null +++ b/a314/files_pi/picmd.conf @@ -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 index 0000000..e288f5c --- /dev/null +++ b/a314/files_pi/picmd.py @@ -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 index 4b3be1f..0000000 Binary files a/a314/software-amiga/a314d and /dev/null differ diff --git a/a314/software-amiga/pi b/a314/software-amiga/pi index 826b26c..474dabe 100644 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 index 0000000..c042803 --- /dev/null +++ b/a314/software-amiga/pi_pistorm/build.bat @@ -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 index 0000000..2624f8a --- /dev/null +++ b/a314/software-amiga/pi_pistorm/pi.c @@ -0,0 +1,476 @@ +/* + * Copyright (c) 2018-2021 Niklas Ekström + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#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 index b799fd4..0000000 Binary files a/a314/software-amiga/spi-a314.dtbo and /dev/null differ