The RiftServe64 C# SDK
Writing software for a C64 has historically required deep knowledge of 6502 assembly, cross-compilers, and custom disk filing formats. The RIFT64 SDK abstracts the hardware communication layer entirely.
Our high-performance `RiftServe64` C# SDK allows any developer with modern object-oriented skills to spin up an interactive backend, listen to incoming serial requests, and stream rendering buffers instantly.
1. Spinning up a Socket Server
In just a few lines of code, you can start a RIFT64 listener and wait for connections from original hardware or emulators:
using RiftServe64.Sdk.Protocol;
using RiftServe64.Sdk.Networking;
// Create an async TCP server binding to port 8000
var server = SocketServer.GetOrCreate("0.0.0.0", 8000);
server.ClientConnected += async connection =>
{
// Initialize the RIFT64 protocol protocol codec
var client = new Rift64ProtocolClient(connection);
await client.ClearScreenAsync();
await client.SetColorsAsync(Rift64Color.Black, Rift64Color.Cyan);
await client.WriteAtAsync(10, 5, "RIFT64 NATIVE C# LISTENER");
};
await server.StartAsync();
2. Low-level RAM Manipulation
Need to upload custom sprites, fonts, or assembly code blocks? Use raw byte streams to write directly to C64 RAM, then point a hardware sprite at that block:
// Define a custom 64-byte sprite block (24x21 pixels + padding)
byte[] spritePattern = new byte[64];
spritePattern[10] = 0xAA; // draw a retro stripe
// Upload the sprite pattern into Bank 0 at $3400 (sprite-pointer 0x0D = $0D * 64)
await client.StoreMemoryAsync(0x3400, spritePattern);
// Configure and enable a single hardware sprite
await client.SetSpriteAsync(
spriteId: 0,
x: 140,
y: 120,
color: Rift64Color.LightGreen,
pointer: 0x0D, // 0x0D * 64 = $0340 offset within the active VIC bank
bank: VicBank.Bank0,
enabled: true);
// Or batch-configure several sprites in one socket write:
await client.SetSpriteConfigAsync(new[]
{
new Rift64SpriteConfig(0, 140, 120, (byte)Rift64Color.LightGreen, 0x0D, VicBank.Bank0, true),
new Rift64SpriteConfig(1, 200, 120, (byte)Rift64Color.Cyan, 0x0E, VicBank.Bank0, true),
});
3. A Modern Developer's Guide to 1982 Hardware (FAQ)
If you are a modern web, mobile, or enterprise developer, you are probably used to unlimited memory, layout engines, and HTML canvases. Designing for the Commodore 64 introduces unique, exciting hardware constraints. Here are the most common questions modern developers ask when getting started with RIFT64:
A: The C64 screen operates on a grid-based Screen RAM matrix (40 columns by 25 rows). You draw background tiles, maps, or letters by writing the corresponding character code (0-255) into the target column and row cell of the grid. For pixel-precise, smooth-moving characters (like players or bullets), you bypass Screen RAM and configure the C64's 8 hardware-driven Sprites, which accept absolute (X, Y) pixel coordinates from your server!
A: The C64 has no concept of a modern framebuffer. To show complex imagery, you must switch the VIC-II chip into Multicolor Bitmap Mode. Even then, you are constrained to 160x200 resolution with a maximum of 4 colors per 8x8 block, chosen from the fixed 16-color palette. You must pre-process, dither, and index your image into a 16-color C64-compatible bitmap on your server side, and then stream the raw pixel data and parallel color matrices over the wire!
A: High-speed serial links (like SwiftLink) max out at 38,400 bits per second (about 3.8KB per second). A single full redraw of the C64 screen (1000 character bytes + 1000 color bytes) is 2KB. At 38.4k, sending a full screen takes over half a second! You should never redraw the entire screen every frame. Instead, only stream the character cells that have actually changed (dirty-region tracking), or use built-in regional copy commands like scrolling (G) or block windowing (W) to keep your data stream light and lightning fast.
A: Instead of keeping track of the background cells on your server and manually redrawing them when a popup is dismissed, use RIFT64's built-in off-screen backup buffers! Send the S0 command to instantly backup the active screen & color RAM on the C64, render your popup window, and then send R0 when the user unpauses. The C64 client executes an instantaneous, native 6502 block-copy, restoring your background perfectly without any serial bandwidth overhead!
A: A C64 sprite is a 24x21 pixel grid, packed as exactly 63 bytes (with 1 trailing padding byte = 64 bytes total). You design your sprite patterns on your PC, upload the 64-byte block directly to C64 RAM using StoreMemoryAsync, and set the active sprite pointer register to look at that memory offset. You can then move, expand, and color the sprite instantly using simple C# methods (and since the VIC-II has no native hardware mirroring registers, you can flip them on-the-fly by simply swapping the pointer byte to a pre-mirrored memory block!).
Complete SDK API Reference
This directory lists every single public method and asynchronous API available inside the Rift64ProtocolClient class of the RiftServe64.Sdk namespace, categorized by system tasks:
1. Client Connection & Handshaking
Queries the connected client's capabilities, parses the returned feature string, and returns a verified identity profile.
Sends a raw capabilities query command (?) and waits for a response.
Performs an asynchronous, non-blocking read to capture a single keypress character from the C64's ring buffer.
Performs an asynchronous, non-blocking read to capture a Carriage-Return-terminated string from the C64 input stream.
Safely closes socket connections and releases system resources associated with the client session.
2. Cursor, Screen & Typography
Clears the C64 screen character matrix to spaces (`$20`), resets the hardware cursor to (0,0), and hides the cursor.
Issues the P command to move the text-writing cursor to column x (0-39) and row y (0-24). Out-of-range values are clamped by the C64 client.
Issues the K command. The first nibble is written to both the border ($D020) and background ($D021) registers; the second nibble becomes the default foreground colour used by subsequent text writes.
Streams standard ASCII/PETSCII text at the current cursor offset using the ! command.
Writes a coloured run of text via the T command. The colour byte is sent first (low nibble = palette index 0-15) followed by a $00-terminated string. Convenience overloads accept a Rift64Color enum.
Writes a length-prefixed text block using the L command, verified and safe for binary-containing strings.
Moves the cursor to absolute coordinate (x, y), sets the color, and writes the specified text in a single efficient stream.
Fills exactly count cells starting at the current cursor with spaces (`$20`), then restores the cursor position.
Toggles whether the C64 KERNAL actively flashes the hardware cursor. Highly recommended to hide the cursor during game loops.
3. Windowing, Borders & Block Painting
Batch-writes a 2D rectangular character block to the screen using the W command.
Renders a rectangular character block in the chosen foreground color using the V command.
Sends a rectangular block verified via an 8-bit additive checksum. The C64 client stages the packet in MemoryStoreBuffer before committing.
Writes raw, un-translated screen codes directly to a rectangular screen block, bypassing standard character conversion tables.
Instructs the C64 client to outline a rectangle of size WWHH from the current cursor position using customized border and line character sets.
Issues the Q command to paint a rectangular region of colour RAM ($D800) without altering the underlying screen-character codes. The first overload fills with a single colour; the second streams a row-major byte array, one colour per cell.
Renders a window of a tile-id map into screen and (optionally) colour RAM via the D command. mode selects 1 = raw 1×1 chars, 2 = 2×2 metatiles, 3 = 3×3 metatiles. offX/offY provide sub-tile scroll offsets for smooth scrolling, and colorMode picks 0 = NONE, 1 = FILL, 2 = MAP, 3 = PERCELL (mode-2 only; per-child-cell colour bank) for the colour-RAM pass. Validated with an ACK.
Strongly-typed overload of DrawMetatileAsync that takes a MetatileColorMode enum (None, Fill, Map, MapPerCell) instead of a raw byte. Use this to make the colour-RAM pass selection self-documenting at the call site.
Builds the 1 KB page-aligned colour bank consumed by mode-2 PERCELL rendering. The returned buffer is laid out as four parallel 256-byte slot pages (TL, TR, BL, BR) indexed by tile id — upload it to a page-aligned address with StoreMemoryLargeCheckedAsync and pass that address as colorSrcAddr with color: MetatileColorMode.MapPerCell.
4. Hardware Sprite Manipulation
Pushes a contiguous batch of Y sprite-config records (id, X, Y, color, pointer, VIC bank, enabled) in one socket write — ideal for initialising all 8 hardware sprites at once.
Sends a single Y packet to fully configure one VIC-II hardware sprite (0-7). Automatically handles the X-MSB bit in $D010 when X > 255 and routes the active VIC bank.
An ultra-fast bulk positioning method. Packs multiple active sprite coordinates into a single compact stream, bypassing unnecessary pointer or color writes during heavy game loops.
Sends the U packet, which combines a full single-sprite configuration with the VIC-II multicolor flags ($D01C), expand-X/Y, sprite-foreground priority, and the two shared multicolor registers $D025 / $D026.
5. Memory, Buffer & State Management
Copies raw byte payloads directly to C64 RAM starting at address using the M command. Excellent for injecting fonts, code, or sprite matrices.
Copies raw binary bytes to RAM, validated using an additive 8-bit checksum. The client returns success/failure.
An advanced SDK paging subsystem. Automatically breaks large binary structures into verified 256-byte chunks, uploads them using checked Z writes, and handles automatic retry loops.
Copies the entire active 1000-character screen and 1000-byte color RAM grids instantly into an off-screen backup slot (0 or 1).
Performs an instantaneous, native 6502 block-copy of the screen and color RAM back from backup slot (0 or 1) to active display memory.
Instructs the client to execute high-speed multi-directional subregion scrolling of size WWHH from coordinate XXYY on Screen and Color RAM.
Issues the O command to drive the client's custom restore/transition routine, returning from raster-split or bitmap modes back to the standard text terminal. frameCount and jiffyDelay control the transition pacing; paletteMode selects the colour fade table.
Cancels an in-progress custom-restore transition before its frame count completes. Returns true if the client ACKs the cancel.
Escape hatch for sending an arbitrary opcode + payload and waiting for an ACK/NAK. Use this when prototyping new protocol commands before a typed wrapper exists.
5b. VIC-II-Aware Upload Helpers
These helpers wrap StoreMemoryLargeCheckedAsync with VIC-II memory-layout validation. They take a VicBank and a typed slot enum, validate the buffer size, route around the character-ROM shadow at $1000-$1FFF in banks 0/2, and chunk the upload into checked 256-byte frames. They are the recommended path for uploading sprite, charset, bitmap, and screen-RAM data.
Uploads a 63- or 64-byte sprite frame to slot pointer within bank (effective address BankBase + pointer*64).
Uploads a 2 KB character set into the chosen charset slot. Throws if the destination is hidden by the character-ROM shadow.
Uploads an 8000- or 8192-byte multicolor/hi-res bitmap into the chosen bitmap slot of a VIC bank.
Uploads 1000 (or 1024 padded) screen-character bytes to the chosen screen slot. Pair with SetCharsetBankAsync/ApplyVicLayoutAsync to switch the live screen pointer.
Uploads 1000 colour-RAM bytes to the fixed VIC colour-RAM region at $D800. Only the low nibble of each byte is used.
Writes 1-8 sprite-pointer bytes into the screen-slot tail ($3F8-$3FF) so the VIC-II picks up uploaded sprite frames.
Activates a VicLayout in one round-trip: programs the bank bits, $D018, and (optionally) bitmap/multicolor flags via the I command. Use this after uploading screen / charset / bitmap data to switch the C64 to the new view.
Issues the F command to swap the VIC-II bank (low 2 bits of CIA2 $DD00) and update $D018 — a fast way to switch character sets or screen-RAM locations.
Issues the I command to atomically reprogram $D011 / $D016 / $D018, the border/background colours, and the bitmap/multicolor flags — the typed overload above is preferred for typical use.
6. SID Audio & Graphics Interrupts
Uploads a MiniPlayer2 (.bin) music module to a page-aligned address using checked Z frames and then issues an A5 bind so the resident SID engine starts using it. Returns true only if every chunk and the final bind ACKed.
Low-level wrapper around the A command. subcmd is a digit selecting the music-module operation (stop / start / pause / resume / tempo / bind / volume / state); args are emitted as ASCII hex bytes after the digit. Higher-level helpers exist (StartAudioAsync, StopAudioAsync, PauseAudioAsync, ResumeAudioAsync, SetAudioTempoAsync, SetAudioVolumeAsync, BindAudioModuleAsync, QueryAudioStateAsync).
Configures the client's stable raster IRQ. Programs the desired $D011 / $D016 / $D018 values for both halves of the screen so you can mix, for example, a multicolor bitmap on top with a text terminal underneath.
7. Upstream Telemetry Streams
Enables periodic upstream telemetry packets. divider selects how many frames pass between sends; channels is a bit mask choosing which sources are sampled (joystick port 1, joystick port 2, sprite-sprite collisions, sprite-background collisions). Each packet is a fixed 8-byte frame ($7E $55 seq joy1 joy2 spr_spr spr_bg cksum).
Disables telemetry streaming, freeing up serial bandwidth.
Requests a single immediate telemetry packet without enabling periodic streaming. Useful for polling-style sampling on demand.
Recommended high-level entry point. Starts telemetry and returns an IAsyncDisposable session that parses incoming bytes into typed Rift64TelemetryFrames. Iterate session.Frames(ct) in your game loop, or hook FrameReceived; disposing stops telemetry and drains the wire.
Convenience wrapper around ReadKeyAsync with a default 1-minute timeout. Returns the pressed key, or null if the timeout elapses.