From e7e393b324ebea0405eaf1df6b0e083c220909b9 Mon Sep 17 00:00:00 2001 From: beeanyew Date: Tue, 18 May 2021 05:15:42 +0200 Subject: [PATCH] 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. --- a314/files_pi/a314fs.conf | 8 + a314/files_pi/picmd.conf | 8 + a314/files_pi/picmd.py | 391 +++++++++++++++++++ a314/software-amiga/a314d | Bin 39244 -> 0 bytes a314/software-amiga/pi | Bin 9720 -> 13580 bytes a314/software-amiga/pi_pistorm/build.bat | 1 + a314/software-amiga/pi_pistorm/pi.c | 476 +++++++++++++++++++++++ a314/software-amiga/spi-a314.dtbo | Bin 262 -> 0 bytes 8 files changed, 884 insertions(+) create mode 100644 a314/files_pi/a314fs.conf create mode 100644 a314/files_pi/picmd.conf create mode 100644 a314/files_pi/picmd.py delete mode 100644 a314/software-amiga/a314d create mode 100644 a314/software-amiga/pi_pistorm/build.bat create mode 100644 a314/software-amiga/pi_pistorm/pi.c delete mode 100644 a314/software-amiga/spi-a314.dtbo 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 4b3be1f274258c30d139674518d2c2106a7e96d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39244 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 CvQ6y( diff --git a/a314/software-amiga/pi b/a314/software-amiga/pi index 826b26cd59383ae560e1d72f51ddebebdc230039..474dabecd7d1609de06e2b750dca80a00ff75810 100644 GIT binary patch literal 13580 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 CbYV9D delta 4314 zcmb7|e{2)y8ONVHpPj?G*d{rG3AS^)9ByLVT$XMbMT|NoV^@>fQDALUA!gImjuaixSs@N1*D{m)QS-jqkGoo(^CzJyt5&mcbRA62WKt@BUxHC#rMPy-n|gZ zXNVhrZSxb{zSl<21sWpxV0d%hB`u{264SlPk7!KszE()~rigZ!d$}cCwOQb*c{*%f z;F{zHBi{_=E%D=UYn%0qn0@?9<}}~Hoiu~CfEl%|;=0XY+h)GJ&z!Ke@X?UjD6ChF z>k6;wBt;KI59qwnZ@X&zgoso0$C=+LnoXL!9J+)a7fIYAZ~NtlnX;?%pZT)b%P6K)h5l2jv$}P!_2a#qQ7m#EFex5amtYO=)CTPopQ^9d_U3} zHmM$yCNIX=Q%c8T2$LI1J5qG_o#YB=iL~A#QO8o<)imlz#0E2t z?CjWJ+J(hyDQV0}$x5BSCpfbn!aW^{_RGXfwH5Qzq*#W8lA-kqGwAWuQ>~FV3L2&B ziz8YOX~kAkJ=Gh9An7*UnWE_y7*snFNj1wA&CjeanU`vkt}Ks1@!mlDWkIqc961u6 z_X6io*>TC6Z#jc}^pH93-0Se@bZ9M^J>_rblr1zAX5I+#gR_6pBQ&C~rrii#L`1t; z=u_t*GpYV0^(*OTCPVVazignkNED^z-*a#JsTBn2&D}xd$Pn$-WHt2Lcq|@_| z&bj&*A=dx$S%v|p8F8f+4%n`yX!7N*M2s@)i~(DJQLXvDt0q@noH$qGS19BS^UQrO zQ6H71^w!^ZSn2c{{j;+vx)Vp51Q^+f_I|V#gGH#M=wCu3OQoE3j{WR~N~LUV9+kHi zucRjG6)Cg97~nrEdKNu^$v8igcbJbWzJ`z9Bynut!2|w0lt|`B?tm6;T1FUjMh{rU zUNN#g#4ODZW2+jS+rzfREi>$?$^Bo!_9mU;MnR{0-+G0vQ=h(+_F$KHvt9nhruqo1 z8gPA`bpoX<;e5LLxxbAfc8&e!cqm^yD{Q4dP$|SI_QFu6Ia%}GqG?o)ZdN%aqeZ2qdK19lM z%eAMp&e-djcq~L>X5#n_lAr$!PjC0fs!mENKQXmk;ZQ{*N{D=8zrzLy1Y^Z_>Kcp- zYTdn_CD(`*EB=1wt}j~|M0((W_NzZ@Qif`q)bubdkrw2kmNM7o%zc5N3EliRslw>%Shw0^(i~|`Y+8;c%a2q*E{NAUewSGz- zWv5wqB%HgicFjGt*z>p>6h?8^ifU^%Z2#!h=-%kwwcFE^$>x5~h3#KuC?f|?mCUzW zkvltHjlQXrBVM1ipzK!UG0z-5ggmxv7_?+lQr6o`%vTlhWR%uVNKkh~ch@~hVjV6a zBxNTHgLM(|peK{8%)@oby5N+qAD&FKWHgH7Iip2XXGT@k;+Ps!IMdM(;^QxxO%0EH zN9;rm$ zZ;+&QD9-e?ENReg9`z3MwH@Y)MLlL-kMZO+&+0+0%KWFUG*K@ekZD01xt zg0bqWL<=iHANW0z8UzTydy{C91fn?p%P2>_I2s$k3jk3DU^oCz1J8m9TmsG!5oFjN zMCm0S21!MR5V-&c1OvPS;DB8QFl0yH0Wk+0a3GV80e}D;7xDK5^p|0txF1aCi017B z7l@pL$U+_&=m2jZViD2%Kqo*3+;C1j3ZO`Y{Jeg!1KBtTP9y3bq6McAc^II*5RQ9a zLd^+)<-h>Dz+r&jiN(mm;vCYjxC;z{v)~H&44HuUz8df@H~|U(sr9eG_eWq7ZA9Wn zBmo!+i~z_6J_J{hHDsc;8LR}oP=-G7$eIhlA^S#T0pq!{VKgwg44#+4aM=W+gHvKB zB8HF~>2^cVdki3I?-c+?7BxXJ3^JwcPd3|X&|c~b82$i^qrVH-@va(tjAeo&0s<4@ zyb#6u3os<~fkChXybi`d0bB#O0iw0TkX->VZ0`V11IXHudixcCtk@BNLjp*j0}eUh zkOOiK=ywc(-+)noU>xVcHLQOb6qkvh5;G`=6LZAg~1X+*=`@li)K9-C?Gl&3W!Hpo?Fz$wNH;lW-u}Kj`t{MiaPXKu1 zTL-#9AAoV+Fu?lx_5&#PodZ|EO>8`5!w*ONaKsM-enjbq<9;~f9{@uDbNz_gkNN&l zfI$3b0c`mZxc@5F-~TZhpMyJK2D?`Th%z97YOori;KR{})h8d~Hmi3rcSm2UwQ`>-oSK>m%*nqi{qiy<_qivp(#7$+%_UQ}o$CE^hw^ Drbw&r 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 b799fd453294dc9a0811d433aa238164f34c8d70..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262 zcmcb>`|m9S10x#)1A_$+KLBD4AQl8-0U!neH6UIAW}y;{P%bl&<^{2nijs46l9F?c zER0Q|a*Sz3iRrnic_j`8U=fg779i&S4+KD(F+M)OEVU>nu`)g$qyQuj0zyE{1jRr# zX_*zJ1;r30U^SvZIXQiV;Z`MyMd_&}RuEeddO4Hxa|;qnGLv#r86Zj+iVHGzlZ$mT OGSf3a?3C0p0|o#Eu`Wsg -- 2.39.2