Skip to content

Instantly share code, notes, and snippets.

@aaronsb
Last active March 13, 2025 16:14
Show Gist options
  • Save aaronsb/1d2c0f13e6f454cd07642408fd13e99e to your computer and use it in GitHub Desktop.
Save aaronsb/1d2c0f13e6f454cd07642408fd13e99e to your computer and use it in GitHub Desktop.
Using qbasic to receive files from a modern linux computer over a serial port

Create these two programs for transferring files from a modern Linux computer to an old Toshiba T3200SX running MS-DOS over a serial connection.

Modern Linux Computer (Sender)

Here's a bash script for the Linux side that opens ttyUSB0 as a serial port and sends a file:

#!/bin/bash

if [ $# -ne 1 ]; then
    echo "Usage: $0 <file_to_send>"
    exit 1
fi

FILE_TO_SEND="$1"
FILE_SIZE=$(stat -c%s "$FILE_TO_SEND")
FILE_NAME=$(basename "$FILE_TO_SEND")

# Configure serial port
stty -F /dev/ttyUSB0 9600 cs8 -cstopb -parenb -echo raw

# Send file metadata first: filename and size
echo -n "$FILE_NAME|$FILE_SIZE|" > /dev/ttyUSB0

# Small delay to ensure metadata is processed
sleep 1

# Send the actual file content
cat "$FILE_TO_SEND" > /dev/ttyUSB0

echo "File $FILE_NAME sent successfully!"

Toshiba T3200SX (Receiver)

Here's a QBasic program for the DOS side that listens on the serial port and saves the incoming file:

' RECEIVE.BAS - File transfer receiver for Toshiba T3200SX
' Run this program before starting the sender

OPEN "COM1:9600,N,8,1" FOR INPUT AS #1
OPEN "SCRATCH.TMP" FOR OUTPUT AS #2

PRINT "Waiting for file transfer..."

' Buffer to store incoming data
DIM Buffer AS STRING * 1
DIM FileName AS STRING
DIM FileSize AS LONG
DIM BytesRead AS LONG
DIM ReadingMetadata AS INTEGER
DIM MetadataBuffer AS STRING

ReadingMetadata = 1
MetadataBuffer = ""
BytesRead = 0

DO
    ' Read one byte at a time
    Buffer = INPUT$(1, #1)
    
    IF ReadingMetadata = 1 THEN
        ' Still reading metadata
        IF Buffer = "|" THEN
            ' First pipe separates filename
            IF FileName = "" THEN
                FileName = MetadataBuffer
                MetadataBuffer = ""
            ELSE
                ' Second pipe marks end of metadata
                FileSize = VAL(MetadataBuffer)
                ReadingMetadata = 0
                
                ' Close the temporary file and open the real output file
                CLOSE #2
                OPEN FileName FOR OUTPUT AS #2
                
                PRINT "Receiving file: "; FileName
                PRINT "File size: "; FileSize; " bytes"
            END IF
        ELSE
            MetadataBuffer = MetadataBuffer + Buffer
        END IF
    ELSE
        ' Reading file content
        PRINT #2, Buffer;
        BytesRead = BytesRead + 1
        
        ' Show progress
        IF BytesRead MOD 1024 = 0 THEN
            PRINT "Received "; BytesRead; " of "; FileSize; " bytes"
        END IF
        
        ' Check if we've received the whole file
        IF BytesRead >= FileSize THEN
            EXIT DO
        END IF
    END IF
LOOP

CLOSE #1
CLOSE #2

PRINT "File transfer complete!"
PRINT "Saved as: "; FileName
END

Here's a GWBasic (MS DOS 3.3 Basic) version

10 REM RECEIVE.BAS - File transfer receiver for Toshiba T3200SX on MS-DOS 3.3
20 REM Run this program before starting the sender
30 CLS
40 PRINT "File Transfer Utility"
50 PRINT "---------------------"
60 PRINT

100 REM Open serial port
110 OPEN "COM1:9600,N,8,1" FOR INPUT AS #1
120 OPEN "SCRATCH.$$$" FOR OUTPUT AS #2

200 REM Initialize variables
210 PRINT "Waiting for file transfer..."
220 FILENAME$ = ""
230 FILESIZE = 0
240 BYTESREAD = 0
250 READMETA = 1
260 META$ = ""

300 REM Main receiving loop
310 WHILE NOT EOF(1)
320   A$ = INPUT$(1, #1)
330   IF READMETA = 1 THEN GOSUB 1000
340   IF READMETA = 0 THEN GOSUB 2000
350 WEND

400 REM Close files
410 CLOSE #1
420 CLOSE #2
430 PRINT
440 PRINT "File transfer complete!"
450 PRINT "Saved as: "; FILENAME$
460 END

1000 REM Process metadata
1010 IF A$ = "|" THEN GOTO 1100
1020 META$ = META$ + A$
1030 RETURN

1100 REM Process metadata separator
1110 IF FILENAME$ = "" THEN GOTO 1200
1120 FILESIZE = VAL(META$)
1130 READMETA = 0
1140 CLOSE #2
1150 OPEN FILENAME$ FOR OUTPUT AS #2
1160 PRINT "Receiving file: "; FILENAME$
1170 PRINT "File size: "; FILESIZE; " bytes"
1180 RETURN

1200 REM First separator - process filename
1210 FILENAME$ = META$
1220 META$ = ""
1230 RETURN

2000 REM Process file data
2010 PRINT #2, A$;
2020 BYTESREAD = BYTESREAD + 1
2030 IF BYTESREAD MOD 512 = 0 THEN PRINT "Received "; BYTESREAD; " of "; FILESIZE; " bytes"
2040 IF BYTESREAD >= FILESIZE THEN WHILE NOT EOF(1): WEND: GOTO 400
2050 RETURN

Implementation Notes:

  1. Linux Side:

    • The script takes a filename as an argument and sends both the filename and file size before the actual data
    • It uses stty to configure the serial port at 9600 baud
    • The format is filename|filesize|[data]
  2. DOS Side:

    • The BASIC program opens COM1 (the standard serial port on the Toshiba)
    • It parses the metadata to get the filename and size
    • It shows progress as it receives data
    • It saves the incoming bytes directly to the file
  3. Connection Requirements:

    • You'll need a USB-to-Serial adapter connected to /dev/ttyUSB0 on Linux
    • The other end would connect to the serial port on the Toshiba
    • Make sure to use a null modem cable or adapter if connecting directly

This simple protocol should work for transferring files of reasonable size. The DOS BASIC program handles the incoming data byte-by-byte, which might be slow for large files, but should be reliable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment