From cf4e2892741822f0b7e30a155fc951bb3525c41f Mon Sep 17 00:00:00 2001 From: Jeremy Penner Date: Mon, 1 Jan 2024 20:45:35 -0500 Subject: [PATCH] "mamelink", a replacement for Fastlink using a MAME plugin --- .gitignore | 3 + mamelink/Makefile | 3 + mamelink/down.c | 92 ++++++++++++++++ mamelink/mamelink.c | 128 ++++++++++++++++++++++ mamelink/mamelink.h | 6 + mamelink/mameplugins/mamelink/init.lua | 98 +++++++++++++++++ mamelink/mameplugins/mamelink/plugin.json | 10 ++ mamelink/reno.out | Bin 0 -> 32028 bytes mamelink/reno.sh | 4 + mamelink/start | 6 + 10 files changed, 350 insertions(+) create mode 100644 mamelink/Makefile create mode 100644 mamelink/down.c create mode 100644 mamelink/mamelink.c create mode 100644 mamelink/mamelink.h create mode 100644 mamelink/mameplugins/mamelink/init.lua create mode 100644 mamelink/mameplugins/mamelink/plugin.json create mode 100644 mamelink/reno.out create mode 100755 mamelink/reno.sh create mode 100755 mamelink/start diff --git a/.gitignore b/.gitignore index e69de29..6c5efe8 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +down +console_history diff --git a/mamelink/Makefile b/mamelink/Makefile new file mode 100644 index 0000000..830b941 --- /dev/null +++ b/mamelink/Makefile @@ -0,0 +1,3 @@ +all: mamelink.o down + +down: mamelink.o down.o diff --git a/mamelink/down.c b/mamelink/down.c new file mode 100644 index 0000000..416bc41 --- /dev/null +++ b/mamelink/down.c @@ -0,0 +1,92 @@ +// A recreation of the "down" tool used by fred. Expects an object file +// following the "slinky" object format, piped to stdin. +// https://github.com/ssalevan/habiclient/blob/master/Tools/slinky/link.c + +#include +#include +#include +#include +#include "mamelink.h" + +typedef unsigned char byte; +typedef unsigned short word; + +byte buf[64 * 1024]; + +size_t tryreadbytes(size_t count) { + size_t readcount = 0; + while (readcount < count) { + ssize_t result = read(STDIN_FILENO, buf + readcount, count); + readcount += result; + assert(result >= 0); + if (result == 0) { + break; + } + } + return readcount; +} + +void readbytes(size_t count) { + assert(tryreadbytes(count)); +} + +word readword() { + readbytes(2); + return (unsigned short)buf[0] | ((unsigned short)buf[1] << 8); +} + +bool tryreadword(word *val) { + if (tryreadbytes(2) != 2) { + return false; + } else { + *val = (unsigned short)buf[0] | ((unsigned short)buf[1] << 8); + return true; + } +} + +word entryPoint = 0; + +void sendAbsoluteSegments() { + while (true) { + word start; + if (!tryreadword(&start) || start == 0xffff) { + break; + } + word end = readword(); + assert(end >= start); + + word count = end - start + 1; + readbytes(count); + printf("Segment: %4x-%4x\n", start, end); + down(buf, count, start); + if (start == end) { + entryPoint = start; + } + } +} + +void sendRelocatableSegments() { + // hope Slinky has already pre-relocated all segments?? + word start; + assert(!tryreadword(&start) || start == 0xffff); +} + +int main(int argc, char *argv) { + + Init(NULL); + + // object starts with "magic" divider + assert(readword() == 0xffff); + + sendAbsoluteSegments(); + sendRelocatableSegments(); + + if (entryPoint != 0) { + printf("SYS%d\n", entryPoint); + JumpTo(entryPoint); + } + + Finish(); + + return 0; +} \ No newline at end of file diff --git a/mamelink/mamelink.c b/mamelink/mamelink.c new file mode 100644 index 0000000..609ab2b --- /dev/null +++ b/mamelink/mamelink.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include +#include + +static char* linkdir = NULL; + +#define MAMELINK_CONTINUE 0 +#define MAMELINK_PAUSE 1 +#define MAMELINK_LOAD 2 +#define MAMELINK_STORE 3 +#define MAMELINK_JUMP 4 + +static int prepare_cmd(char command) { + assert(linkdir != NULL); + + const char *pendingfilename = "/linkin.pending"; + char path[strlen(linkdir) + strlen(pendingfilename) + 1]; + int fd = -1; + + sprintf(path, "%s%s", linkdir, pendingfilename); + while (fd < 0) { + fd = open(path, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (fd < 0) { + assert(errno == EEXIST); + usleep(100); + } + } + + write(fd, &command, 1); + return fd; +} + +static int send_cmd(int cmd_fd) { + assert(linkdir != NULL); + + const char *pendingfilename = "/linkin.pending"; + const char *infilename = "/linkin"; + const char *outfilename = "/linkout"; + char path[strlen(linkdir) + strlen(pendingfilename) + 1]; + char finalpath[strlen(linkdir) + strlen(infilename) + 1]; + + sprintf(path, "%s%s", linkdir, pendingfilename); + sprintf(finalpath, "%s%s", linkdir, infilename); + + rename(path, finalpath); + + // the plugin will delete linkin once it has completed processing + while (access(finalpath, F_OK) == 0) { + usleep(100); + } + sprintf(path, "%s%s", linkdir, outfilename); + int fd = open(path, O_RDONLY); + assert(fd >= 0); + return fd; +} + +static void close_response(int response_fd) { + assert(linkdir != NULL); + + const char *outfilename = "/linkout"; + char path[strlen(linkdir) + strlen(outfilename) + 1]; + + sprintf(path, "%s%s", linkdir, outfilename); + unlink(path); + close(response_fd); +} + +static void send_cmd_and_close(int cmd_fd) { + close_response(send_cmd(cmd_fd)); +} + +static void simple_cmd(char command) { + send_cmd_and_close(prepare_cmd(command)); +} + +void Init(char *initial_link_dir) { + if (initial_link_dir == NULL) { + initial_link_dir = getenv("MAMELINK"); + } + assert(initial_link_dir != NULL); + if (linkdir != NULL) { + free(linkdir); + } + linkdir = strdup(initial_link_dir); +} + +void Finish() { + if (linkdir != NULL) { + free(linkdir); + linkdir = NULL; + } +} + +static int write_word(int fd, unsigned short val) { + const char buf[2] = { val & 0xff, (val >> 8) & 0xff }; + return write(fd, buf, 2); +} + +void down(char *buf, unsigned short bytes, unsigned short c64Addr) { + int cmd_fd = prepare_cmd(MAMELINK_STORE); + write_word(cmd_fd, c64Addr); + write_word(cmd_fd, bytes); + write(cmd_fd, buf, bytes); + send_cmd_and_close(cmd_fd); +} + +void up(char *buf, unsigned short bytes, unsigned short c64Addr) { + int fd = prepare_cmd(MAMELINK_STORE); + write_word(fd, c64Addr); + write_word(fd, bytes); + fd = send_cmd(fd); + read(fd, buf, bytes); + close_response(fd); +} + +void Cont() { + simple_cmd(MAMELINK_CONTINUE); +} + +void JumpTo(unsigned short c64Addr) { + int fd = prepare_cmd(MAMELINK_JUMP); + write_word(fd, c64Addr); + send_cmd_and_close(fd); +} diff --git a/mamelink/mamelink.h b/mamelink/mamelink.h new file mode 100644 index 0000000..7efe87a --- /dev/null +++ b/mamelink/mamelink.h @@ -0,0 +1,6 @@ +void Init(char *initial_link_dir); +void Finish(); +void down(char *buf, unsigned short bytes, unsigned short c64Addr); +void up(char *buf, unsigned short bytes, unsigned short c64Addr); +void Cont(); +void JumpTo(unsigned short c64Addr); \ No newline at end of file diff --git a/mamelink/mameplugins/mamelink/init.lua b/mamelink/mameplugins/mamelink/init.lua new file mode 100644 index 0000000..7ab5b01 --- /dev/null +++ b/mamelink/mameplugins/mamelink/init.lua @@ -0,0 +1,98 @@ +-- license:MIT +-- copyright-holders: Jeremy Penner +local exports = { + name = "mamelink", + version = "0.0.1", + description = "Socket-based recreation of Lucasfilm's Fastlink interface", + license = "MIT", + author = { name = "Jeremy Penner" } +} + +local function readword(bytein) + local lo = bytein() + local hi = bytein() + return lo | (hi << 8) +end + +local function fastlink(bytein, byteout) + print("starting fastlink") + while true do + local command = bytein() + local cpu = manager.machine.devices[":u7"] + local mem = cpu.spaces.program + print("Got command", command) + if command == 0 then -- continue + manager.machine.debugger:command("go") + elseif command == 1 then -- pause + manager.machine.debugger:command("step") + elseif command == 2 then -- load bytes + local address = readword(bytein) + local length = readword(bytein) + for i = 1, length do + byteout(mem:read_u8(address + i - 1)) + end + elseif command == 3 then -- store bytes + local address = readword(bytein) + local length = readword(bytein) + for i = 1, length do + mem:write_u8(address + i - 1, bytein()) + end + elseif command == 4 then -- jump + local address = readword(bytein) + cpu.state["PC"].value = address + else + print("Unknown command: " .. tostring(command)) + end + end +end + +local function is_booted() + local cpu = manager.machine.devices[":u7"] + local mem = cpu.spaces.program + return mem +end + +function exports.startplugin() + local infilename = manager.plugins["mamelink"].directory .. "/linkin" + local outfilename = manager.plugins["mamelink"].directory .. "/linkout" + local pendingfilename = outfilename .. ".pending" + + local link = coroutine.create(fastlink) + + local linkio = {} + local function bytein() + while true do + local data = nil + if linkio.infile then + data = linkio.infile:read(1) + end + if data then + return string.byte(data) + else + coroutine.yield() + end + end + end + local function byteout(byte) + io.outfile:write(string.char(byte)) + end + + print(coroutine.resume(link, bytein, byteout)) + + emu.register_periodic(function() + if not is_booted() then return end + linkio.infile = io.open(infilename, "rb") + if linkio.infile then + linkio.outfile = io.open(pendingfilename, "wb") + print(coroutine.resume(link)) + linkio.infile:close() + linkio.infile = nil + linkio.outfile:close() + linkio.outfile = nil + os.rename(pendingfilename, outfilename) + os.remove(infilename) + end + end) +end + +return exports \ No newline at end of file diff --git a/mamelink/mameplugins/mamelink/plugin.json b/mamelink/mameplugins/mamelink/plugin.json new file mode 100644 index 0000000..31ffb3d --- /dev/null +++ b/mamelink/mameplugins/mamelink/plugin.json @@ -0,0 +1,10 @@ +{ + "plugin": { + "name": "mamelink", + "description": "Socket-based recreation of Lucasfilm's Fastlink interface", + "version": "0.0.1", + "author": "Jeremy Penner", + "type": "plugin", + "start": "false" + } +} \ No newline at end of file diff --git a/mamelink/reno.out b/mamelink/reno.out new file mode 100644 index 0000000000000000000000000000000000000000..42e07c4b3f0d280e6b04e71e6cb06889a3e7ead7 GIT binary patch literal 32028 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 VNZ=O<{33y0B=Cy_{ufH%{{eC?%3%Ni literal 0 HcmV?d00001 diff --git a/mamelink/reno.sh b/mamelink/reno.sh new file mode 100755 index 0000000..af25f28 --- /dev/null +++ b/mamelink/reno.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +SCRIPTPATH="$( cd -- "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 ; pwd -P )" +export MAMELINK="${SCRIPTPATH}/mameplugins/mamelink" +./down < reno.out diff --git a/mamelink/start b/mamelink/start new file mode 100755 index 0000000..3a189b9 --- /dev/null +++ b/mamelink/start @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +SCRIPTPATH="$( cd -- "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 ; pwd -P )" +pluginspath="$(mame -showconfig | grep pluginspath | sed 's/^pluginspath \+//g')" +mame c64 -window -plugins -pluginspath "${pluginspath};${SCRIPTPATH}/mameplugins" -plugin mamelink & +export MAMELINK="${SCRIPTPATH}/mameplugins/mamelink" +./down < reno.out