# Amiga File Transfer
Transfer files between Amiga and Linux, using a MIDI serial link.
# 1 Protocols
Data is packed into MIDI SysEx messages:
0xF0 0x7D data bytes go here 0xF7
Data bytes must have the highest bit clear, as per MIDI standard.
# 1.1 8
Payload not encoded in any way, only safe for 7bit data (eg ASCII text).
# 1.2 4
Each payload 8-bit byte split into two 4-bit nibbles, transmitted as bytes (MSB first). Simple to implement, but inefficient.
# 1.3 7
Each payload 8-bit byte is concatenated into a bitstream, which is split into 7-bit nuggets transmitted in order. Complex to implement, but efficient.
# 2 Prerequisites
# 2.1 Linux
- MIDI interface and cables
-
amidi
tool - C build system
- optional: FS-UAE with VBCC or amiga-gcc (to compile C programs for Amiga)
# 2.2 Amiga
- MIDI interface and cables
- Blitz Basic 2
# 3 Bootstrap
-
run
make
on Linux -
type in
recv8.bb2
to Blitz Basic 2 on Amiga and compile it torecv8
-
run
recv8
on Amiga -
run
./send8.sh < recv4.bb2
on Linux -
move
RAM:out
torecv4.bb2
on Amiga and compile it torecv4
-
run
recv4
on Amiga -
run
./send4.sh < recv7
on Linux -
move
RAM:out
torecv7
on Amiga -
run
recv7 10100 > send7
on Amiga -
run
./send7.sh < send7
on Linux
# 4 Usage
# 4.1 Sending from Linux to Amiga
-
run
recv7 100100 > file
on Amiga, change maximum size if necessary -
run
send7.sh < file
on Linux
# 4.2 Sending from Amiga to Linux
-
run
recv7.sh > file
on Linux -
run
send7 file
on Amiga
# 5 Bugs
- no way to interrupt sending or receiving on the Amiga side
-
recv7
error messages go to output file instead of console -
Linux scripts may need additional arguments (e.g.
amidi -p hw:1,0,0
)
# 6 Source
Download all: amiga-file-transfer.tar.gz
# 6.1 Amiga
# 6.1.1 recv8.bb2
WBStartup
ser.l = OpenSerial("serial.device", 0, 31250, 144)
SetSerialBuffer 0, 100100
out.l = WriteFile(1, "RAM:out")
Repeat
c.w = ReadSerial(0)
Until c.w = $F0
c.w = ReadSerial(0)
c.w = ReadSerial(0)
While c.w <> $F7
char.b = c.w
WriteMem 1, &char.b, 1
c.w = ReadSerial(0)
Wend
CloseFile 1
CloseSerial 0
End
# 6.1.2 recv4.bb2
WBStartup
ser.l = OpenSerial("serial.device", 0, 31250, 144)
SetSerialBuffer 0, 100100
out.l = WriteFile(1, "RAM:out")
Repeat
c.w = ReadSerial(0)
Until c.w = $F0
c.w = ReadSerial(0)
c.w = ReadSerial(0)
While c.w <> $F7
b.w = (c.w & $F) LSL 4
c.w = ReadSerial(0)
If c.w <> $F7
b.w = (c.w & $F) | b.w
char.b = b.w
WriteMem 1, &char.b, 1
c.w = ReadSerial(0)
EndIf
Wend
CloseFile 1
CloseSerial 0
End
# 6.1.3 recv7.c
/*
vc +kick13m -c99 -sc -sd -O2 -o recv7 recv7.c -lamigas
recv7 100100 > path:to/file
*/
#include <proto/exec.h>
#include <proto/dos.h>
#include <clib/alib_protos.h>
#include <dos/dos.h>
#include <devices/serial.h>
struct DosLibrary *DOSBase;
int atoi(const char *s)
{
int n = 0;
while (*s)
{
n = 10 * n + (*s++ - '0');
}
return n;
}
int strlen(const char *s)
{
int n = 0;
while (*s++)
{
n++;
}
return n;
}
LONG read(UBYTE *buffer, LONG bytes);
LONG decode(UBYTE *buffer, LONG bytes);
int main(char *args)
{
int retval = 20;
if ((DOSBase = (struct DosLibrary *) OpenLibrary("dos.library", 0)))
{
LONG bytes = atoi(args);
LONG msgbytes = (((bytes << 3) + 6) / 7) + 3;
UBYTE *buffer;
if ((buffer = AllocMem(msgbytes, 0L)))
{
LONG inbytes = read(buffer, msgbytes);
if (inbytes >= 3)
{
LONG outbytes = decode(buffer, inbytes);
if (outbytes >= 0)
{
Write(Output(), buffer, outbytes);
retval = 0;
}
else
{
Write(Output(), "decode\n", 7);
}
}
else
{
Write(Output(), "read\n", 5);
}
FreeMem(buffer, msgbytes);
}
else
{
Write(Output(), "alloc\n", 6);
}
CloseLibrary((struct Library *) DOSBase);
}
return retval;
}
LONG decode(UBYTE *buffer, LONG bytes)
{
UBYTE *msg = buffer;
UBYTE *out = buffer;
ULONG n = 0;
do
{
if (n++ >= bytes)
{
Write(Output(), "start1\n", 7);
return -1;
}
}
while (*msg++ != (UBYTE)0xF0);
if (n++ >= bytes)
{
return -1;
}
if (*msg++ == (UBYTE)0x7D)
{
UBYTE output = 0;
BYTE shift = -1;
while (n++ < bytes)
{
UBYTE input = *msg++;
if (input == 0xF7)
{
break;
}
if (input & 0x80)
{
Write(Output(), "high\n", 5);
return -1;
}
if (shift == -1)
{
output |= input << 1;
shift = 6;
}
else
{
output |= input >> shift;
*out++ = output;
output = (input & ((1 << shift) - 1)) << (8 - shift);
--shift;
}
}
if (shift == 6)
{
*out++ = output;
}
return out - buffer;
}
else
{
Write(Output(), "start2\n", 7);
}
return -1;
}
LONG read(UBYTE *buffer, LONG bytes)
{
LONG retval = -1;
struct MsgPort *port;
if ((port = CreatePort(NULL, 0)))
{
struct IOExtSer *io;
if ((io = (struct IOExtSer *) CreateExtIO(port, sizeof(struct IOExtSer))))
{
if (0 == OpenDevice("serial.device", 0, (struct IORequest *) io, 0L))
{
struct IOTArray term = { 0xF7F7F7F7, 0xF7F7F7F7 };
io->IOSer.io_Command = SDCMD_SETPARAMS;
io->io_RBufLen = ((bytes + 63) >> 6) << 6;
io->io_Baud = 31250;
io->io_ReadLen = 8;
io->io_StopBits = 1;
io->io_TermArray = term;
io->io_SerFlags = SERF_EOFMODE | SERF_XDISABLED | SERF_RAD_BOOGIE;
if (0 == DoIO((struct IORequest *) io))
{
io->IOSer.io_Command = CMD_READ;
io->IOSer.io_Length = bytes;
io->IOSer.io_Data = buffer;
if (0 == DoIO((struct IORequest *) io))
{
retval = io->IOSer.io_Actual;
}
else
{
Write(Output(), "CMD_READ\n", 9);
}
}
else
{
Write(Output(), "SDCMD_SETPARAMS\n", 16);
}
CloseDevice((struct IORequest *) io);
}
else
{
Write(Output(), "OpenDevice\n", 11);
}
DeleteExtIO((struct IORequest *) io);
}
else
{
Write(Output(), "ExtIO\n", 6);
}
DeletePort(port);
}
else
{
Write(Output(), "Port\n", 5);
}
return retval;
}
# 6.1.4 send7.c
/*
vc +kick13m -c99 -sc -sd -O2 -o send7 send7.c -lamigas
send7 path:to/file
*/
#include <proto/exec.h>
#include <proto/dos.h>
#include <clib/alib_protos.h>
#include <dos/dos.h>
#include <devices/serial.h>
struct DosLibrary *DOSBase;
int strlen(char *s)
{
int n = 0;
while (*s++)
{
n++;
}
return n;
}
int main(char *filename)
{
int retval = 20;
if ((DOSBase = (struct DosLibrary *) OpenLibrary("dos.library", 0)))
{
BPTR lock;
if ((lock = Lock(filename, ACCESS_READ)))
{
struct FileInfoBlock fib;
if (Examine(lock, &fib))
{
if (fib.fib_DirEntryType < 0) /* is a file */
{
LONG inbytes = fib.fib_Size;
LONG msgbytes = (((inbytes << 3) + 6) / 7) + 3;
BPTR file;
if ((file = Open(filename, MODE_OLDFILE)))
{
UBYTE *buffer;
if ((buffer = AllocMem(msgbytes, 0L)))
{
/*
read file into end part of larger message buffer
ensyx and encode7 into earlier part of buffer
*/
UBYTE *msg = buffer;
UBYTE *in = msg + (msgbytes - inbytes);
if (inbytes == Read(file, in, inbytes))
{
*msg++ = 0xF0;
*msg++ = 0x7D;
UBYTE output = 0;
UBYTE shift = 1;
for (LONG i = 0; i < inbytes; ++i)
{
UBYTE input = *in++;
output |= (input >> shift);
*msg++ = output;
output = (input & ((1 << shift) - 1)) << (7 - shift);
if (++shift == 8)
{
*msg++ = output;
shift = 1;
output = 0;
}
}
if (shift != 1)
{
*msg++ = output;
}
}
*msg++ = 0xF7;
/*
send buffer via MIDI
*/
struct MsgPort *port;
if ((port = CreatePort(NULL, 0)))
{
struct IOExtSer *io;
if ((io = (struct IOExtSer *) CreateExtIO(port, sizeof(struct IOExtSer))))
{
if (0 == OpenDevice("serial.device", 0, (struct IORequest *) io, 0L))
{
io->IOSer.io_Command = SDCMD_SETPARAMS;
io->io_Baud = 31250;
io->io_ReadLen = 8;
io->io_StopBits = 1;
io->io_SerFlags = SERF_XDISABLED | SERF_RAD_BOOGIE;
if (0 == DoIO((struct IORequest *) io))
{
BPTR out = Output();
Write(out, "Sending '", 9);
Write(out, filename, strlen(filename));
Write(out, "'... ", 5);
io->IOSer.io_Command = CMD_WRITE;
io->IOSer.io_Length = msgbytes;
io->IOSer.io_Data = buffer;
if (0 == DoIO((struct IORequest *) io))
{
Write(out, "OK!\n", 4);
retval = 0;
}
else
{
Write(out, "FAILED!\n", 8);
}
}
CloseDevice((struct IORequest *) io);
}
DeleteExtIO((struct IORequest *) io);
}
DeletePort(port);
}
FreeMem(buffer, msgbytes);
}
Close(file);
}
}
}
UnLock(lock);
}
CloseLibrary((struct Library *) DOSBase);
}
return retval;
}
# 6.2 Linux
# 6.2.1 ensyx.c
#include <stdio.h>
int main(int argc, char **argv)
{
(void) argc;
(void) argv;
int input;
putchar(0xF0);
putchar(0x7D);
while (EOF != (input = getchar()))
{
if (input & 0x80)
{
fprintf(stderr, "ensyx: warning: high bit ignored\n");
input &= 0x7F;
}
putchar(input);
}
putchar(0xF7);
return 0;
}
# 6.2.2 encode4.c
#include <stdio.h>
int main(int argc, char **argv)
{
(void) argc;
(void) argv;
int input;
while (EOF != (input = getchar()))
{
putchar((input & 0xF0) >> 4);
putchar(input & 0x0F);
}
return 0;
}
# 6.2.3 encode7.c
#include <stdio.h>
int main(int argc, char **argv)
{
(void) argc;
(void) argv;
int input;
int output = 0;
int shift = 1;
while (EOF != (input = getchar()))
{
output |= (input >> shift);
putchar(output);
output = (input & ((1 << shift) - 1)) << (7 - shift);
if (++shift == 8)
{
putchar(output);
shift = 1;
output = 0;
}
}
if (shift != 1)
{
putchar(output);
}
return 0;
}
# 6.2.4 send8.sh
#!/bin/sh
ME="$(dirname "$(readlink -e "${0}")")"
TEMPFILE="$(mktemp)"
"${ME}/ensyx" > "${TEMPFILE}"
amidi -s "${TEMPFILE}"
rm -f "${TEMPFILE}"
# 6.2.5 send4.sh
#!/bin/sh
ME="$(dirname "$(readlink -e "${0}")")"
TEMPFILE="$(mktemp)"
"${ME}/encode4" | "${ME}/ensyx" > "${TEMPFILE}"
amidi -s "${TEMPFILE}"
rm -f "${TEMPFILE}"
# 6.2.6 send7.sh
#!/bin/sh
ME="$(dirname "$(readlink -e "${0}")")"
TEMPFILE="$(mktemp)"
"${ME}/encode7" | "${ME}/ensyx" > "${TEMPFILE}"
amidi -s "${TEMPFILE}"
rm -f "${TEMPFILE}"
# 6.2.7 desyx.c
#include <stdio.h>
int main(int argc, char **argv)
{
(void) argc;
(void) argv;
int input;
if (0xF0 == getchar())
{
if (0x7D == getchar())
{
while (0xF7 != (input = getchar()))
{
if (input == EOF)
{
fprintf(stderr, "desyx: unexpected EOF\n");
return 1;
}
putchar(input);
}
return 0;
}
}
return 1;
}
# 6.2.8 decode4.c
#include <stdio.h>
int main(int argc, char **argv)
{
(void) argc;
(void) argv;
int input;
int output = 0;
int shift = 4;
while (EOF != (input = getchar()))
{
if (input & 0xF0)
{
fprintf(stderr, "decode4: warning: high nibble ignored\n");
input &= 0x0F;
}
output |= input << shift;
if (shift == 0)
{
putchar(output);
output = 0;
}
shift = 4 - shift;
}
return 0;
}
# 6.2.9 decode7.c
#include <stdio.h>
int main(int argc, char **argv)
{
(void) argc;
(void) argv;
int input;
int output = 0;
int shift = -1;
while (EOF != (input = getchar()))
{
if (input & 0x80)
{
fprintf(stderr, "decode7: warning: high bit ignored\n");
input &= 0x7F;
}
if (shift == -1)
{
output |= input << 1;
shift = 6;
}
else
{
output |= input >> shift;
putchar(output);
output = (input & ((1 << shift) - 1)) << (8 - shift);
--shift;
}
}
if (shift == 6)
{
putchar(output);
}
return 0;
}
# 6.2.10 recv8.sh
#!/bin/sh
ME="$(dirname "$(readlink -e "${0}")")"
TEMPFILE="$(mktemp)"
amidi -r "${TEMPFILE}" -t 10
"${ME}/desyx" < "${TEMPFILE}"
rm -f "${TEMPFILE}"
# 6.2.11 recv4.sh
#!/bin/sh
ME="$(dirname "$(readlink -e "${0}")")"
TEMPFILE="$(mktemp)"
amidi -r "${TEMPFILE}" -t 10
"${ME}/desyx" < "${TEMPFILE}" | "${ME}/decode4"
rm -f "${TEMPFILE}"
# 6.2.12 recv7.sh
#!/bin/sh
ME="$(dirname "$(readlink -e "${0}")")"
TEMPFILE="$(mktemp)"
amidi -r "${TEMPFILE}" -t 10
"${ME}/desyx" < "${TEMPFILE}" | "${ME}/decode7"
rm -f "${TEMPFILE}"