#disk #commodore #retro #directory-listing #opencbm #1541

rs1541

Rust bindings and helper functions for accessing Commodore disk drives through OpenCBM

2 releases

new 0.1.1 Jan 18, 2025
0.1.0 Jan 18, 2025

#27 in Emulators

27 downloads per month

GPL-3.0 license

130KB
2.5K SLoC

rs1541

Rust bindings and helper functions for accessing Commodore disk drives through OpenCBM.

Overview

This crate provides idiomatic Rust interfaces to OpenCBM, allowing easy control of Commodore disk drives (like the 1541) using modern USB devices such as the XUM1541. Thread-safe access to OpenCBM through protected mutex handles enables safe usage in multi-threaded and async applications.

Features

  • Safe Rust wrappers around OpenCBM's C interface
  • RAII-based driver management - no manual open/close needed
  • Thread-safe access for multi-threaded and async applications
  • Ergonomic error handling using Rust's Result type
  • Directory parsing with structured data types
  • Strong typing for CBM-specific concepts (error codes, status messages, etc.)

Pre-requisites

  • Rust
  • OpenCBM installed and configured
  • clang (used to generate OpenCBM bindings)
  • XUM1541 (or compatible) USB device
  • Appropriate system permissions for USB access

See Pre-requisities - More Detail to help with these.

Quick Start

Create a new rust project

cargo new my-1541-app
cd my-1541-app

Add the rs1541 dependency

[dependencies]
rs1541 = "0.1"

Replace the contents of main.rs with:

use rs1541::Cbm;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    // Driver automatically opens on creation and closes on drop
    let cbm = Cbm::new()?;

    // Get drive information
    let id = cbm.identify(8)?;
    println!("Drive type at device 8: {}", id);

    // Check drive status
    let status = cbm.get_status(8)?;
    println!("Drive status: {}", status);

    // Read directory
    let dir = cbm.dir(8, None)?;
    println!("Directory listing:\n{}", dir);

    Ok(())
}

Then:

cargo build
cargo run

If you have a 1541 disk drive attached via an XUM1541, with the 1541 demo disk installed you should see:

<details>
<summary>Example Output (with 1541 demo disk)</summary>
Drive type at device 8: CBM 1541: 1540 or 1541
Drive status: 00,OK,00,00
Directory listing:
Drive 0 Header: "test/demo  1/85" ID: 84
Filename: "how to use.prg"           Blocks:  14
Filename: "how part two.prg"         Blocks:   8
Filename: "how part three.prg"       Blocks:   7
Filename: "vic-20 wedge.prg"         Blocks:   4
Filename: "c-64 wedge.prg"           Blocks:   1
Filename: "dos 5.1.prg"              Blocks:   4
Filename: "printer test.prg"         Blocks:   9
Filename: "disk addr change.prg"     Blocks:   6
Filename: "view bam.prg"             Blocks:  12
Filename: "display t&s.prg"          Blocks:  15
Filename: "check disk.prg"           Blocks:   4
Filename: "performance test.prg"     Blocks:  11
Filename: "seq.file.demo.prg"        Blocks:   5
Filename: "rel.file.demo.prg"        Blocks:  18
Filename: "sd.backup.c16.prg"        Blocks:   7
Filename: "sd.backup.plus4.prg"      Blocks:   7
Filename: "sd.backup.c64.prg"        Blocks:  10
Filename: "print.64.util.prg"        Blocks:   7
Filename: "print.c16.util.prg"       Blocks:   7
Filename: "print.+4.util.prg"        Blocks:   7
Filename: "uni-copy.prg"             Blocks:  13
Filename: "c64 basic demo.prg"       Blocks:  30
Filename: "+4 basic demo.prg"        Blocks:  35
Filename: "load address.prg"         Blocks:   8
Filename: "unscratch.prg"            Blocks:   7
Filename: "header change.prg"        Blocks:   5
Free blocks: 403
</details>

Examples

See examples/.

To run examples:

cargo run --example basic
cargo run --example async

Basic Example

The directory listing example shown above.

Async Example

This example demonstrates thread-safe concurrent access to a Commodore disk drive using rs1541's mutex-protected OpenCBM handle. It spawns two concurrent tasks:

  • Task 1 identifies the drive and reads its directory
  • Task 2 polls the drive status multiple times at fixed intervals

Format Example

Formatting a disk

CLI

Provides an interactive CLI to exercise some of the rs1541/OpenCBM functionality

Pre-requisites - More Detail

OpenCBM

rs1541 relies on OpenCBM. You must build and install OpenCBM. I've made a few mods to OpenCBM to make it work more reliably so I suggest you use my form. You can build and isntall it like this:

sudo apt-get install build-essential libusb-1.0-0-dev usbutils cc65 linux-headers-$(uname -r)
git clone https://github.com/piersfinlayson/OpenCBM
cd OpemCBM
make -f LINUX/Makefile plugin
sudo make -f LINUX/Makefile install install-plugin
sudo adduser $USER opencbm

To see if you can access your XUM1541 USB device make sure it's plugged in then:

cbmctrl detect

This should return nothing if you have no drives connected, otherwise a list of detect drives.

See Troubleshooting below if you get errors when running cbmctrl - permission issues are common.

clang

Add clang-dev so that rs1541 can correctly generate the Rust bindings to the OpenCBM library C functions:

sudo apt install build-essential llvm-dev libclang-dev clang

Rust

If you don't already have Rust installed:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env

Troubleshooting

Logging

Use RUST_LOG=<log level> before the 1541fs command. If you're hitting problems then RUST_LOG=debug is a good bet. If 1541fs starts 1541fsd (i.e. it wasn't already running), this log level (via this environment variable) will also be propogated to the invoked 1541fsd.

1541fs logs go to stdout.

1541fsd logs to syslog, so to /var/log/syslog or wherever syslog/rsyslog is configured to output. You can put this in your /etc/rsyslog.conf if you'd like 1541fsd logs to go to /var/log/1541fs.log:

$template CustomFormat,"%TIMESTAMP% %HOSTNAME% %syslogtag% %syslogseverity-text:::uppercase% %msg%\n"
:programname, startswith, "1541fs" -/var/log/1541fs.log;CustomFormat

XUM1541/USB device Permission Issues

If you don't have XUM1541 USB permissions correct on your system you'll probably get something like this:

error: cannot query product name: error sending control message: Operation not permitted
error: no xum1541 device found
error: cannot query product name: error sending control message: Operation not permitted
error: no xum1541 device found
cbmctrl: libusb/xum1541:: Operation not permitted

It can be a bit of a pain getting permissions right for accessing the XUM1541 USB device, unless you want to run everything as sudo (not recommended for security reasons).

If you get any kind of PermissionDenied or Operation not permitted errors I recommend replacing /etc/udev/rules.d/45-opencbm-xum1541.rules with this content:

SUBSYSTEM!="usb_device", ACTION!="add", GOTO="opencbm_rules_end"

# xum1541
SUBSYSTEM=="usb", ATTRS{idVendor}=="16d0", ATTRS{idProduct}=="0504", MODE="0666", GROUP="plugdev", TAG+="uaccess"

# xum1541 in DFU mode
SUBSYSTEM=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ff0", MODE="0666", GROUP="plugdev", TAG+="uaccess"

LABEL="opencbm_rules_end"

Then add your user to the plugdev group:

sudo usermod -a -G plugdev $USER

You may need to restart your shell at this point.

Then reload udev rules:

sudo udevadm control --reload-rules && sudo udevadm trigger

Then reattach your USB device (XUM1541) and try ```cbmctrl detect" again. Until this works you're unlikely to be get rs1541 working.

WSL

You can use WSL (WSL2 to be precise) run rs1541. You must use usbipd in order to connect your XUM1541 USB device to the WSL kernel. I've found that this stops working after a while, and the wsl instance must be shutdown and restarted in order to get it working again.

XUM1541

To see logs from XUM1541 add this to the front of the command you run the daemon with:

XUM1541_DEBUG=10

The XUM1541 sometimes gets into a bad state. You can kill 1541fsd and then run usbreset to reset the device. Run it without arguments to see what deivce number you need. For example:

usbreset 001/011

libusb1.0

While OpenCBM and the XUM1541 code supports both libusb0.1 and 1.0 I strongly recommend you use 1.0 - the apt install command earlier in this file installs the correct libusb1.0 packages.

With libusb0.1 I've seen odd segmentation faults and other issues.

To verify you really are using libusb1.0 run this after installing OpenCBM and the XUM1541 plugin):

ldd /usr/local/lib/opencbm/plugin/libopencbm-xum1541.so

You should see something like this:

linux-vdso.so.1 (0x00007ffc21171000)
        libusb-1.0.so.0 => /lib/x86_64-linux-gnu/libusb-1.0.so.0 (0x00007fa4566ac000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa456483000)
        libudev.so.1 => /lib/x86_64-linux-gnu/libudev.so.1 (0x00007fa456459000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fa4566df000)

In particular libusb.1.0.so.0.

If you haven't linked with the correct version of libusb, try running:

pkg-config --cflags libusb-1.0

OpenCBM XUM1541 uses this within opencbm/Linux/config.make to configure the build, and will fall bck to libusb-0.1 if it doesn't get a sensible response. The response should look something like this:

-I/usr/include/libusb-1.0

Dependencies

~3–11MB
~121K SLoC