Skip to content

Instantly share code, notes, and snippets.

@juj
Created May 17, 2024 18:49
Show Gist options
  • Save juj/9e0e27c58f6b362732338844f4b16a32 to your computer and use it in GitHub Desktop.
Save juj/9e0e27c58f6b362732338844f4b16a32 to your computer and use it in GitHub Desktop.
Commodore 64 REU interface in C/C++
#include "reucpy.h"
static volatile uint8_t *const reu_status = (uint8_t*)0xDF00;
static volatile uint8_t *const reu_command = (uint8_t*)0xDF01;
static volatile uintptr_t *const reu_c64_addr = (uintptr_t*)0xDF02;
static volatile reu_addr_t *const reu_cart_addr = (reu_addr_t*)0xDF04;
static volatile uint16_t *const reu_xfer_length = (uint16_t*)0xDF07;
static volatile uint8_t *const reu_address_ctl = (uint8_t*)0xDF0A;
void reucpy(void *c64_addr, reu_addr_t reu_addr, uint16_t size, uint8_t direction)
{
// Call reu_init() first if this assert() triggers, or if manually
// having done REU accesses in between, be sure to reset bits 7 and 6
// of REU address control register back to zero.
assert((*reu_address_ctl & 0xC0) == 0);
*reu_c64_addr = (uintptr_t)c64_addr;
*reu_cart_addr = reu_addr;
*reu_xfer_length = size;
*reu_command = direction;
}
void reuset(reu_addr_t reu_addr, uint8_t val, uint16_t size)
{
*reu_address_ctl = (*reu_address_ctl & 0x3F) | 0x80; // Fix C64 address, advance REU address
*reu_c64_addr = (uintptr_t)&val;
*reu_cart_addr = reu_addr;
*reu_xfer_length = size;
*reu_command = RAM2REU;
*reu_address_ctl = *reu_address_ctl & 0x3F; // Restore both C64 and REU addresses to advance per each transferred byte
}
int reu_init()
{
*reu_address_ctl = *reu_address_ctl & 0x3F; // Make both C64 and REU addresses advance per each transferred byte
char data[5] = { 'a', 'r', 'e', 'u', 0 };
for(int bank = 255; bank >= 0; --bank)
{
data[0] = (uint8_t)bank;
reucpy(data, (reu_addr_t)bank << 16, 4, RAM2REU);
}
memset(data, 0, sizeof(data));
for(int bank = 0; bank < 256; ++bank)
{
reucpy(data, (reu_addr_t)bank << 16, 4, REU2RAM);
if (data[0] != bank || data[1] != 'r' || data[2] != 'e' || data[3] != 'u') return bank;
}
return 256; // Full 16MB of REU available
}
#pragma once
#define RAM2REU 0x90 // Copy from C64 RAM to REU memory
#define REU2RAM 0x91 // Copy from REU memory to C64 RAM
#define REUSWAP 0x92 // Swap contents between REU and C64 RAM addresses
#define REUCMP 0x93 // Compare contents between REU and C64 RAM
typedef unsigned _BitInt(24) reu_addr_t;
// Like memcpy, but copies data between C64 RAM and REU.
// size: number of bytes to write. Note that 0 is interpreted as 65536 bytes!
// direction: specifies the direction of copy. Use one of the defines above.
void reucpy(void *c64_addr, reu_addr_t reu_addr, uint16_t size, uint8_t direction);
// Like memset, for setting data in REU memory.
// reu_addr: Starting byte address in REU memory.
// val: the byte value to fill.
// size: the number of bytes to fill. Note that 0 is interpreted as 65536 bytes!
void reuset(reu_addr_t reu_addr, uint8_t val, uint16_t size);
// Initializes REU, and returns the number of 64KB REU banks that were detected:
// 0 - no REU detected.
// 1 - 64KB REU detected,
// 256: 16MB REU detected.
// Call this once before calling reucpy().
int reu_init(void);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment