U64Manager – Ein VB.NET Tool für den Ultimate 64
Veröffentlicht am 11. Oktober 2026 · Tobonocom’s Retro Journal
Wie alles begann
Wer einen Ultimate 64 besitzt, kennt das Gerät als beeindruckende FPGA-basierte Neuinterpretation des Commodore 64 – mit Netzwerkanbindung, modernem HDMI-Ausgang und einer REST API, die eine vollständige Fernsteuerung ermöglicht. Genau diese API hat mich gereizt: Warum nicht eine eigene Windows-Anwendung bauen, die alle Funktionen komfortabel zugänglich macht?
Das Ergebnis ist der U64Manager – eine Windows Forms Anwendung in Visual Basic .NET, die ich gemeinsam mit Claude entwickelt habe. Der Artikel beschreibt die Entstehung, die Architektur und zeigt konkrete Codebeispiele aus dem Projekt.
Die API des Ultimate 64
Der Ultimate 64 stellt ab Firmware 3.11 eine REST API unter
http://<IP-Adresse>/v1/
bereit. Die wichtigsten Endpunkte sind:
- /v1/version – API-Version und Verbindungstest
- /v1/info – Geräteinformationen (Firmware, FPGA, Hostname)
- /v1/machine – Maschinensteuerung (Reset, Reboot, Speicher lesen/schreiben)
- /v1/runners – Programme und Mediadateien starten (PRG, CRT, SID, MOD)
- /v1/drives – Floppy-Laufwerke verwalten, Disk-Images mounten
- /v1/configs – Konfiguration lesen und schreiben
- /v1/streams – Video- und Audio-Streams starten und stoppen
- /v1/files – Disk-Images erstellen
Ab Firmware 3.12 kann die API zusätzlich mit einem Passwort gesichert werden, das über den
X-Password
HTTP-Header übermittelt wird.
Architektur: Sauber geschichtet von Anfang an
Bevor die erste Zeile Code geschrieben wurde, stand die Entscheidung für eine klare Schichtenarchitektur:
U64Manager/ ├── U64ApiClient.vb ← HTTP-Schicht (GET, PUT, POST, Binary) ├── U64Manager.vb ← Fassade: bündelt alle Services ├── JsonHelper.vb ← JSON-Parsing mit Newtonsoft.Json ├── C64Theme.vb ← Zentrales Farbschema ├── HexViewerControl.vb ← Hex-Viewer UserControl ├── FileBrowserControl.vb ← FTP-Datei-Browser UserControl ├── CreateDiskImageForm.vb← Dialog für Disk-Image-Erstellung ├── MainForm.vb ← Windows Forms GUI ├── Models/ │ ├── ApiResponse.vb ← Einheitliches Antwort-Objekt │ ├── U64Info.vb ← Gerätinfo-Datenmodell │ └── U64DriveInfo.vb ← Laufwerks-Datenmodell └── Services/ ├── AboutService.vb ← /v1/version, /v1/info ├── MachineService.vb ← /v1/machine ├── RunnerService.vb ← /v1/runners ├── DriveService.vb ← /v1/drives ├── ConfigService.vb ← /v1/configs ├── StreamService.vb ← /v1/streams ├── FileService.vb ← /v1/files └── FtpService.vb ← FTP-Zugriff auf Dateisystem
Die zentrale U64ApiClient-Klasse kapselt die gesamte HTTP-Kommunikation. Alle Requests laufen über
HttpClient
– asynchron mit
Async/Await
, damit die GUI immer reaktionsfähig bleibt:
Public Class U64ApiClient Implements IDisposable Private ReadOnly _httpClient As HttpClient Private _baseUrl As String Private _password As String Public Sub New(baseUrl As String, Optional password As String = ”") _baseUrl = baseUrl.TrimEnd(”/”c) _password = password _httpClient = New HttpClient() _httpClient.Timeout = TimeSpan.FromSeconds(10) UpdatePasswordHeader() End Sub Private Sub UpdatePasswordHeader() _httpClient.DefaultRequestHeaders.Remove(”X-Password”) If Not String.IsNullOrEmpty(_password) Then _httpClient.DefaultRequestHeaders.Add(”X-Password”, _password) End If End Sub Public Async Function GetAsync(path As String) As Task(Of ApiResponse) Dim url = BuildUrl(path) Try Dim response = Await _httpClient.GetAsync(url) Return Await BuildApiResponse(response) Catch ex As TaskCanceledException Return ApiResponse.Timeout(url) Catch ex As HttpRequestException Return ApiResponse.FromConnectionError(url, ex.Message) End Try End Function Public Async Function PostBinaryAsync(path As String, fileBytes As Byte(), fileName As String) As Task(Of ApiResponse) Dim url = BuildUrl(path) Dim content = New ByteArrayContent(fileBytes) content.Headers.Add(”Content-Type”, ”application/octet-stream”) content.Headers.Add(”Content-Disposition”, ”attachment; filename=”"” & fileName & ”"”") Dim response = Await _httpClient.PostAsync(url, content) Return Await BuildApiResponse(response) End Function End Class
Ein wichtiger Lernmoment während der Entwicklung: Die U64 API erwartet Binärdateien (SID, PRG, CRT) als direkten
ByteArrayContent
mit
Content-Disposition
Header – nicht als Multipart-Form-Data. Das war zunächst die Ursache für einen hartnäckigen 400-Fehler beim SID-Abspielen.
Die Service-Schicht
Jeder API-Bereich hat seinen eigenen Service. Der MachineService zeigt das Prinzip:
Public Class MachineService
Private ReadOnly _client As U64ApiClient
Public Sub New(client As U64ApiClient)
_client = client
End Sub
Public Async Function ResetAsync() As Task(Of ApiResponse)
Return Await _client.PutAsync(”/v1/machine:reset”)
End Function
Public Async Function WriteMemAsync(address As String, data As String) As Task(Of ApiResponse)
Dim path = $”/v1/machine:writemem?address={address.ToUpper()}&data={data.ToUpper()}”
Return Await _client.PutAsync(path)
End Function
Public Async Function ReadMemAsync(address As String, Optional length As Integer = 256) As Task(Of Byte())
Dim path = $”/v1/machine:readmem?address={address.ToUpper()}&length={length}”
Return Await _client.GetBinaryAsync(path)
End Function
End Class
Die U64Manager-Fassadenklasse bündelt alle Services zu einer einheitlichen Schnittstelle:
‘ Verbindung herstellen Dim u64 As New U64Manager(”http://192.168.2.135”) ‘ Maschine steuern Await u64.Machine.ResetAsync() ‘ Speicher schreiben (Border-Farbe auf Grün setzen) Await u64.Machine.WriteMemAsync(”D020”, ”05”) ‘ SID-Datei von lokalem PC abspielen Await u64.Runners.PlaySidFromFileAsync(”C:SIDCommando.sid”, songNr:=0) ‘ Disk-Image mounten Await u64.Drives.MountImageFromFileAsync(U64Drive.A, ”C:GamesMayhem.d64”) ‘ Ressourcen freigeben u64.Dispose()
JSON-Parsing mit Newtonsoft.Json
Die API liefert JSON-Antworten. Der JsonHelper parst diese in typisierte Objekte. Ein Beispiel für das Gerätinfo-Modell:
Public Class U64Info <JsonProperty(”product”)> Public Property Product As String <JsonProperty(”firmware_version”)> Public Property FirmwareVersion As String <JsonProperty(”fpga_version”)> Public Property FpgaVersion As String <JsonProperty(”hostname”)> Public Property Hostname As String <JsonProperty(”unique_id”)> Public Property UniqueId As String End Class
Die tatsächliche Gerätantwort sieht dann so aus:
[13:18:14] Produkt: C64 Ultimate [13:18:14] Firmware: 3.14 [13:18:14] FPGA: 121 [13:18:14] Core: 1.47 [13:18:14] Hostname: C64-Ultimate [13:18:14] Unique ID: C4E17C
Der Hex-Viewer mit C64-Dekodierung
Eine der interessantesten Funktionen ist der eingebaute Hex-Viewer.
Er liest direkt aus dem C64-Speicher via DMA und stellt die Bytes in drei Spalten dar: Adresse, Hex-Werte und dekodierter Text.
Da der C64 verschiedene Zeichenkodierungen verwendet, unterstützt der Viewer vier Dekodierungsmodi:
- Screencodes ($0400) – für den Bildschirmspeicher. Buchstaben A-Z liegen hier bei den Werten 1-26
- PETSCII ($0200+) – für den Tastaturpuffer und BASIC-Bereich
- ASCII – für Programme die Standard-ASCII verwenden
- Keine (Hex only) – reine Hex-Darstellung ohne Textspalte
Der Screencode-Decoder zeigt wie unterschiedlich die C64-Zeichensätze zur bekannten ASCII-Tabelle sind:
Private Function DecodeScreencode(b As Byte) As String Select Case b Case 32 : Return ” ” ’ Leerzeichen Case 1 To 26 : Return Chr(b + 64) ’ A-Z (Wert 1=A, 2=B, …) Case 65 To 90 : Return Chr(b + 32).ToString().ToLower() ’ a-z Case 48 To 57 : Return Chr(b) ’ 0-9 Case 42 : Return ”*” Case 46 : Return ”.” ’ … Case Else : Return ”.” End Select End Function
Beim Lesen des Bildschirmspeichers ab $0400 im Screencode-Modus wird die C64-Startmeldung direkt lesbar:
**** COMMODORE 64 BASIC V2 ****
Datei-Browser via FTP
Die REST API des U64 bietet kein Verzeichnis-Listing – aber der U64 unterstützt FTP-Zugriff auf sein Dateisystem. Der FtpService nutzt den eingebauten .NET
FtpWebRequest
um das Dateisystem zu durchsuchen. FTP muss im U64-Menü unter Network Settings → FTP Server aktiviert werden.
Der Browser zeigt Dateien mit passenden Icons je nach Typ und ermöglicht per Doppelklick oder Rechtsklick direkte Aktionen:
- 🎵 SID/MOD – direkt abspielen
- 💾 PRG – laden oder starten
- 🎮 CRT – als Cartridge starten
- 💿 D64/D71/D81 – als Laufwerk mounten
Neue Disk-Images können direkt im aktuellen Verzeichnis des Browsers erstellt werden – D64 (35 oder 40 Tracks), D71, D81 und DNP werden unterstützt.
OBS Streaming-Integration
Eine besonders praktische Funktion für Content-Creator und Streamer: Der U64 kann seinen Video- und Audio-Ausgang über das Netzwerk streamen. Mit dem C64Stream Plugin für OBS Studio (github.com/chrisgleissner/c64stream) landet das Bild direkt in OBS – ganz ohne Capture-Karte.
Das Plugin-Installationsverzeichnis unter Windows lautet:
C:ProgramDataobs-studioobs-plugins64bitc64stream.dll
Im Stream-Tab der Anwendung lassen sich Video- (Port 11000) und Audio-Stream (Port 11001) bequem starten und stoppen – einzeln oder zusammen. Ein Live-Status zeigt ob der Stream gerade läuft.
Das C64-Farbthema
Natürlich durfte ein passendes Design nicht fehlen. Das zentrale C64Theme-Modul definiert alle Farben im klassischen C64-Stil und wendet sie rekursiv auf alle Controls an:
Public Module C64Theme Public ReadOnly ColorBackground As Color = Color.FromArgb(0, 0, 210) Public ReadOnly ColorText As Color = Color.FromArgb(170, 170, 255) Public ReadOnly ColorWarning As Color = Color.FromArgb(255, 255, 85) Public ReadOnly ColorSuccess As Color = Color.FromArgb(85, 255, 85) Public ReadOnly FontNormal As New Font(”Consolas”, 11, FontStyle.Regular) Public Sub Apply(frm As Form) frm.BackColor = ColorBackground frm.ForeColor = ColorText frm.Font = FontNormal ApplyToControls(frm.Controls) End Sub End Module
Dunkelblauer Hintergrund, hellblaue Consolas-Schrift, gelbe Gruppenüberschriften mit ▶-Pfeil – der U64Manager sieht aus wie er klingt.
Fazit
Der U64Manager ist aus einem einfachen Wunsch entstanden: Die REST API des Ultimate 64 komfortabel nutzen zu können. Herausgekommen ist eine vollständige Fernsteuerungsanwendung mit sieben Tabs, FTP-Datei-Browser, Hex-Viewer mit C64-Zeichensatzdekodierung und OBS-Integration.
Das Projekt hat gezeigt wie gut sich die moderne .NET-Welt (HttpClient, Async/Await, Newtonsoft.Json) mit dem klassischen Windows Forms Ansatz verbinden lässt. Wer selbst einen Ultimate 64 besitzt und mit dem Gedanken spielt, eine eigene Anwendung zu bauen: Die API-Dokumentation findet sich unter 1541u-documentation.readthedocs.io.
Wer den U64Manager einmal in Aktion sehen möchte kann sich mein Demonstrationsvideo auf meinem Youtube-Kanal ansehen:

