Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
2db2ca5
firmware: share the REU's RAM bank allocation, preload and save
kfox Oct 15, 2026
2759010
firmware: GeoRAM IO handler
kfox Oct 15, 2026
3bfb688
firmware: RAM bank save reports a failed write instead of acking it
kfox Oct 15, 2026
6ecc7db
firmware: drop RAMBanks.c comments that restate the code
kfox Oct 15, 2026
89c578c
firmware: a disabled GeoRAM leaves $DE00 open instead of answering fr…
kfox Oct 15, 2026
8686274
firmware: FreeGeoRAM stops the IO handlers using the banks before fre…
kfox Oct 15, 2026
1c8cf32
firmware: GeoRAM state is file-local
kfox Oct 15, 2026
b27c082
firmware: drop trailing spaces after the FreeGeoRAM calls
kfox Oct 15, 2026
9b91191
firmware: GeoRAM is the last selectable handler, after Debug
kfox Oct 15, 2026
1330587
firmware: a USB mouse read through registers beside GeoRAM and the REU
kfox Oct 16, 2026
d631ef3
tools: a GEOS 128 input driver for the TR's USB mouse
kfox Oct 16, 2026
78cbc05
firmware: the USB mouse poll keeps reports the USB ISR lands mid-read…
kfox Oct 16, 2026
d3990d3
tools: the GEOS 128 mouse driver ignores a page without the TR's ID, …
kfox Oct 16, 2026
50a74e6
tools: trmouse.py puts the class version at +12, and stops on a loope…
kfox Oct 16, 2026
2564b78
firmware: the USB mouse poll fences the IRQ disable, and clears a rep…
kfox Oct 16, 2026
b89f417
tools: the mouse driver's off-pointer comment drops a 1351 comparison…
kfox Oct 16, 2026
20aa955
tools: trmouse.py stops on a directory chain into the header or BAM, …
kfox Oct 16, 2026
caedc75
firmware: the USB mouse poll fences the IRQ re-enable against the com…
kfox Oct 16, 2026
04de5c8
tools: trmouse.py stops on a directory chain that leaves track 40
kfox Oct 16, 2026
f18ee29
tools: trmouse.py takes D64 and D71 boot disks too, follows the heade…
kfox Oct 16, 2026
b17b18b
tools: trmouse.py refuses a BAM that frees a live directory sector or…
kfox Oct 16, 2026
ea12ae5
tools: trmouse.py refuses any undercounted track and any live directo…
kfox Oct 16, 2026
ce6ffef
firmware: the USB mouse registers live only beside GeoRAM, and plain …
kfox Oct 23, 2026
54a7f9a
firmware: GeoRAM polls the USB mouse from its handler instead of ever…
kfox Oct 23, 2026
bb4e2e1
firmware: the USB mouse's X and Y reads drive a byte kept ready, and …
kfox Oct 23, 2026
12d176d
firmware: GeoRAM drops its 256-byte no-RAM page
kfox Oct 23, 2026
9d1c7b9
firmware: GeoRAM saves back to georam.ram, keeping the last one as ge…
kfox Oct 23, 2026
dbc1ba8
firmware: the USB mouse register map says the ID marks the registers,…
kfox Oct 23, 2026
167e779
Merge branch 'georam' into usb-mouse
kfox Oct 23, 2026
9d41320
firmware: GeoRAM rotates georam.ram to .bak only when there is one, a…
kfox Oct 23, 2026
76c13e5
firmware: a remote launch finds its TR menu target before freeing the…
kfox Oct 23, 2026
9f0c8f8
tests: drive the USB mouse registers on the host
kfox Oct 23, 2026
e7d0866
fix: review findings for 9f0c8f8
kfox Oct 23, 2026
48aed54
Merge branch 'georam' into usb-mouse
kfox Oct 23, 2026
5ec90dd
Merge remote-tracking branch 'upstream/main' into georam
kfox Oct 26, 2026
169d10a
Merge branch 'georam' into usb-mouse
kfox Oct 26, 2026
4210842
fix: review findings for 5ec90dd
kfox Oct 26, 2026
2d86bdc
Merge branch 'georam' into usb-mouse
kfox Oct 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Source/Teensy/MinimalBoot/Common/IOHandlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
USBHIDParser hid1(myusbHost);
USBHIDParser hid2(myusbHost);
USBHIDParser hid3(myusbHost); //need all 3?
MouseController usbHostMouse(myusbHost);

USBSerial USBHostSerial(myusbHost); //update HostSerialType to match, defined in PN532_UHSU.h
//USBSerial USBHostSerial(myusbHost); // only for those Serial devices who transfer <=64 bytes (like T3.x, FTDI...)
Expand Down Expand Up @@ -109,6 +110,9 @@ struct stcIOHandlers
#ifndef MinimumBuild
#include "IO_Handlers/IOH_MIDI.c"
#include "IO_Handlers/IOH_Debug.c"
#include "IO_Handlers/RAMBanks.c"
#include "IO_Handlers/USBMouse.c"
#include "IO_Handlers/IOH_GeoRAM.c"
#ifdef Fab04_REU
#include "IO_Handlers/IOH_REU.c"
#endif
Expand Down Expand Up @@ -158,6 +162,7 @@ stcIOHandlers* IOHandler[] = //Synch order/qty with enum enumIOHandlers
&IOHndlr_KernalReplace, //IOH_KernalReplace,
#endif
&IOHndlr_Debug, //IOH_Debug,
&IOHndlr_GeoRAM, //IOH_GeoRAM,

//*not* manually selectable, see LastSelectableIOH

Expand Down
115 changes: 115 additions & 0 deletions Source/Teensy/MinimalBoot/Common/IO_Handlers/IOH_GeoRAM.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// SPDX-License-Identifier: MIT

// Berkeley Softworks geoRAM: a 256-byte window at $DE00 onto the page selected by two
// write-only registers, $DFFE (page 0-63 within a 16K block) and $DFFF (16K block).
// No DMA, so it runs on every TR, in C64 and C128 mode.
// ref: VICE src/c64/cart/georam.c

void IO1Hndlr_GeoRAM(uint8_t Address, bool R_Wn);
void IO2Hndlr_GeoRAM(uint8_t Address, bool R_Wn);
void InitHndlr_GeoRAM();
void PollingHndlr_GeoRAM();
extern void (*fSpecialBtnChange)(bool Up_nDn);

stcIOHandlers IOHndlr_GeoRAM =
{
"GeoRAM", //Name of handler
&InitHndlr_GeoRAM, //Called once at handler startup
&IO1Hndlr_GeoRAM, //IO1 R/W handler
&IO2Hndlr_GeoRAM, //IO2 R/W handler
NULL, //ROML Read handler, in addition to any ROM data sent
NULL, //ROMH Read handler, in addition to any ROM data sent
&PollingHndlr_GeoRAM, //Polled in main routine
NULL, //called at the end of EVERY c64 cycle
};

#define GeoRAM_Filename "SD:/georam.ram"
#define MAX_GEORAM_BANKS 64 // 512K, the original geoRAM
#define MIN_GEORAM_BANKS 16 // 128K
#define GeoRAM_PagesPerBlock 64
#define GeoRAM_BanksPerBlock (16384 / RAM_Bank_Size)

// VICE decodes the registers across $DF80-$DFFF (A0 picks which); $DF00-$DF7F is the USB mouse's.
#define GeoRAM_RegsBase 0x80
#define GeoRAM_RegBlockBit 0x01

// volatile: the IO ISRs read the page only while there are banks, so the two stores
// must reach memory in the order written
static volatile uint8_t NumGeoRAM_Banks = 0;
static uint8_t *GeoRAM_Bank[MAX_GEORAM_BANKS];
static uint8_t GeoRAM_Block, GeoRAM_PageNum;
static uint8_t * volatile GeoRAM_Page;

static void GeoRAM_SelectPage()
{
if (NumGeoRAM_Banks == 0) return;
// the unused high register bits wrap, as on real hardware
uint32_t Offset = ((GeoRAM_Block & (NumGeoRAM_Banks / GeoRAM_BanksPerBlock - 1)) * GeoRAM_PagesPerBlock
+ (GeoRAM_PageNum & (GeoRAM_PagesPerBlock - 1))) * 256;
GeoRAM_Page = GeoRAM_Bank[Offset / RAM_Bank_Size] + (Offset % RAM_Bank_Size);
}

FLASHMEM void FreeGeoRAM()
{
// the handler is still installed and the C64 may be running: the IO ISRs must stop
// touching the banks before the first one is freed
uint8_t NumBanks = NumGeoRAM_Banks;
NumGeoRAM_Banks = 0;
FreeRAMBanks(GeoRAM_Bank, &NumBanks);
}

FLASHMEM void SpecialBtn_GeoRAM(bool Up_nDn)
{
// on button release save GeoRAM contents
if(Up_nDn) SaveRAMBanksReplacing(GeoRAM_Bank, NumGeoRAM_Banks, GeoRAM_Filename);
}

FLASHMEM void InitHndlr_GeoRAM()
{
FreeGeoRAM();
GeoRAM_Block = GeoRAM_PageNum = 0;
InitUSBMouse();
uint8_t NumBanks = AllocRAMBanks(GeoRAM_Bank, MAX_GEORAM_BANKS, MIN_GEORAM_BANKS);
if (NumBanks == 0)
{
Serial.printf("GeoRAM is disabled\n");
return;
}
GeoRAM_Page = GeoRAM_Bank[0];
NumGeoRAM_Banks = NumBanks;
fSpecialBtnChange = &SpecialBtn_GeoRAM;
Serial.printf("GeoRAM Size: %dK bytes\n", NumBanks * RAM_Bank_Size / 1024);
LoadRAMBanks(GeoRAM_Bank, NumBanks, GeoRAM_Filename);
}

FLASHMEM void PollingHndlr_GeoRAM()
{
PollUSBMouse();
}

void IO1Hndlr_GeoRAM(uint8_t Address, bool R_Wn)
{
if (NumGeoRAM_Banks == 0) return; // disabled: $DE00 stays open bus, so software sees no geoRAM
if (R_Wn) DataPortWriteWaitLog(GeoRAM_Page[Address]);
else
{
uint8_t Data = DataPortWaitRead();
GeoRAM_Page[Address] = Data;
TraceLogAddValidData(Data);
}
}

void IO2Hndlr_GeoRAM(uint8_t Address, bool R_Wn)
{
if (Address < GeoRAM_RegsBase)
{
if (R_Wn) USBMouseIORead(Address);
return;
}
if (R_Wn) return; // write-only registers
uint8_t Data = DataPortWaitRead();
if (Address & GeoRAM_RegBlockBit) GeoRAM_Block = Data;
else GeoRAM_PageNum = Data;
GeoRAM_SelectPage();
TraceLogAddValidData(Data);
}
182 changes: 11 additions & 171 deletions Source/Teensy/MinimalBoot/Common/IO_Handlers/IOH_REU.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@

#ifdef USE_RAM12

#define REU_RAM_Bank_Size 0x2000
#define REU_Size (NumREU_Banks * REU_RAM_Bank_Size) //Dynamically allocated based on NumREU_Banks, each bank is 8k (0x2000)
#define REU_Size (NumREU_Banks * RAM_Bank_Size) //Dynamically allocated based on NumREU_Banks, each bank is 8k (0x2000)
#define REU_RAM_READ(a,d) d=REU_Bank[a>>13][a&0x1fff]
#define REU_RAM_WRITE(a,d) REU_Bank[a>>13][a&0x1fff]=d
#define MIN_REU_BANKS 16 // 128K, smallest real REU (1700)
Expand Down Expand Up @@ -76,11 +75,6 @@ stcIOHandlers IOHndlr_REU =
extern bool PerformDMA(DMA_Trans_RnW RnW, uint16_t StartAddr, uint8_t *Buffer, uint32_t Length, DMA_Addr_Mode FixC64Addr);
extern bool CloseDMA();
extern void (*fSpecialBtnChange)(bool Up_nDn); //Pointer to function called when Special Button Changes
extern void EEPreadStr(uint16_t addr, char* buf);
extern RegMenuTypes RegMenuTypeFromFileName(char** ptrptrFileName);
extern bool SDFullInit();
extern bool USBFileSystemWait();
extern FS *FSfromSourceID(RegMenuTypes SourceID);

//ref: https://codebase64.net/doku.php?id=base:reu_registers
//tests:
Expand Down Expand Up @@ -145,17 +139,11 @@ extern bool DMA_RnW, DMA_FixC64Addr;
extern uint32_t DMA_Length, DMA_Count, DMA_StartAddr;
extern bool DMAByte(uint8_t *Data);

extern void FreeDriveDirMenu();
extern uint8_t RAM_Image[];
// CrtChips.h declares the main image's static table and count.
extern StructMenuItem *DriveDirMenu;
extern uint8_t *ptrRAM_ImageEnd; // points to end of CRT-used RAM1 buffer

void FreeREU()
{ //free banks allocated in RAM2 and reset NumREUBanks
for(uint16_t cnt=0; cnt < NumREU_Banks; cnt++)
if((uint32_t)REU_Bank[cnt] >= 0x20200000) free(REU_Bank[cnt]);
NumREU_Banks = 0;
{
FreeRAMBanks(REU_Bank, &NumREU_Banks);
}

void DMAByte_BASkip(uint8_t *Data1)
Expand Down Expand Up @@ -455,83 +443,12 @@ FLASHMEM void ReadWriteREU(bool RnW, uint32_t REUAddr, uint8_t *REUBuf, uint16_t

FLASHMEM void Save_REU()
{
if (NumREU_Banks == 0) return; // nothing to save: probably due to failure to allocate 128K

//Create filename based on current. reu000.reu becomes reu001.reu, etc
#ifndef USE_RAM12
#error Save_REU saves RAM1/2 banks only
#endif
char Filename[MaxPathLength];
EEPreadStr(eepAdREUFilename, Filename);
char *ptrFileName = Filename; //pointer to move past SD/USB/TR:
RegMenuTypes MenuSourceID = RegMenuTypeFromFileName(&ptrFileName);

if (MenuSourceID == rmtSD) SDFullInit(); // SD.begin(BUILTIN_SDCARD); with retry if presence detected
if (MenuSourceID == rmtUSBDrive) USBFileSystemWait(); //wait up to 1.5 sec in case USB drive just changed or powered up
//rmtTeensy not allowed, no reu files in Teensy Mem

FS *sourceFS = FSfromSourceID(MenuSourceID);

char Extension[20];
char *pDot = strrchr(ptrFileName, '.'); //find last dot
if (pDot == NULL) pDot = ptrFileName + strlen(ptrFileName); //if no extension, set to end of filename
strcpy(Extension, pDot); //copy extension
*pDot = 0; //terminate at dot/extension

bool IsNum = true; //are last 3digits of filename all numbers?
if (strlen(ptrFileName)>=3)
{
for(char *charloc=pDot-3; charloc<pDot; charloc++)
{
if (*charloc<'0' || *charloc>'9') IsNum = false;
//Serial.printf("%08x: %c %02x\n", (uint32_t)charloc, *charloc, *charloc);
}
}
else IsNum = false;


uint16_t NewNum = 0;
if (IsNum)
{ //find current number and then terminate it
NewNum = atoi(pDot-3);
*(pDot-3) = 0; //terminate number
}

//now see if the file exists, or itterate until it doesn't
char NewFilename[MaxPathLength];

do sprintf(NewFilename, "%s%03d%s", ptrFileName, NewNum++, Extension);
while (sourceFS->exists(NewFilename));

Serial.printf("Saving REU: %s\n", NewFilename);
File SaveFile = sourceFS->open(NewFilename, FILE_WRITE);
if (!SaveFile)
{
Serial.println("Unable to open!");
return;
}

//uint32_t StartmS = millis();
uint8_t NextByte;

for (uint32_t CharNum=0; CharNum<REU_Size; CharNum++)
{
REU_RAM_READ(CharNum, NextByte);
SaveFile.write(NextByte);
}
//Serial.printf("Saved %lu KBytes in %lumS\n", REU_Size/1024, millis()-StartmS);
Serial.printf("Saved %dKBytes\n", REU_Size/1024);
SaveFile.close();

//Flash LED to ack save
uint32_t LastmS = millis();
bool LEDState = true;

for (uint32_t FlashCount=0; FlashCount<20; FlashCount++)
{
while(millis()-LastmS < 75);
LastmS = millis();
if ((LEDState = !LEDState)) SetLEDOn;
else SetLEDOff;
}

SaveRAMBanks(REU_Bank, NumREU_Banks, Filename);
}

// This only applies to REU-Only mode.
Expand Down Expand Up @@ -559,59 +476,10 @@ FLASHMEM void InitHndlr_REU()
#endif

#ifdef USE_RAM12
//Allocate full REU size in Ram 1 and 2
//FreeCrtChips(); //re-using CrtChips for this, free mem allocated in RAM2 and reset NumCrtChips
FreeDriveDirMenu(); //free/clear prev loaded directory to make space. Doing it regardless to preserve continuity
//Serial.printf("RAM2#x- %d\n", RAM2blocks());

if (NumCrtChips == 0) ptrRAM_ImageEnd = RAM_Image; //If no CRT loaded, RAM1 is entirely free for REU
uint8_t *pRAM_Image = ptrRAM_ImageEnd; //start at end of any CRT-used RAM1

//Serial.printf("%dk REU, (%d banks x %d bytes)\n", REU_Size/1024, REU_Size/REU_RAM_Bank_Size, REU_RAM_Bank_Size);

//First use RAM_Image from RAM1:
while (((uint32_t)pRAM_Image - (uint32_t)RAM_Image + REU_RAM_Bank_Size <= RAM_ImageSize)
&& (NumREU_Banks < MAX_REU_BANKS))
{
//CrtChips[NumCrtChips].ChipROM = pRAM_Image;
REU_Bank[NumREU_Banks] = pRAM_Image;
pRAM_Image += REU_RAM_Bank_Size;
NumREU_Banks++;
}
Printf_dbg_reu("Used %lu/%lu Bytes of RAM1 Image\n", (uint32_t)NumREU_Banks * REU_RAM_Bank_Size, RAM_ImageSize);

//allocate the rest from RAM2
while((NumREU_Banks < MAX_REU_BANKS && // while NumREU_Banks < 64 (512k/8k)
(NULL != (REU_Bank[NumREU_Banks] = (uint8_t*)malloc(REU_RAM_Bank_Size))))) // and allocation of 8k succeeds
{
NumREU_Banks++; // if allocation a success, increment NumREU_Banks
}

// NumREU_Banks = number of banks allocated, one higher than the last index of REU_Bank[] that was allocated

Printf_dbg_reu("After parsing RAM2, %luK bytes allocated\n", (uint32_t)NumREU_Banks * REU_RAM_Bank_Size / 1024);

//resize REU to a power of 2 if needed and free unused banks
if (NumREU_Banks & (NumREU_Banks-1)) //not a power of 2
{
uint8_t unused_bank;
// largest power less than or equal to NumREU_Banks
uint8_t NewNumREU_Banks = 1 << (7 - (__builtin_clz(NumREU_Banks) - 24));
Serial.printf("Resizing REU from %d banks to %d banks\n", NumREU_Banks, NewNumREU_Banks);

for (unused_bank = NewNumREU_Banks; unused_bank < NumREU_Banks; unused_bank++)
if((uint32_t)REU_Bank[unused_bank] >= 0x20200000)
free(REU_Bank[unused_bank]);
NumREU_Banks = NewNumREU_Banks;
}

Printf_dbg_reu("After resizing to power of 2, %luK bytes allocated\n", (uint32_t)NumREU_Banks * REU_RAM_Bank_Size / 1024);

if (NumREU_Banks < MIN_REU_BANKS) // If REU is not 128k or larger
NumREU_Banks = AllocRAMBanks(REU_Bank, MAX_REU_BANKS, MIN_REU_BANKS);
if (NumREU_Banks == 0)
{
Serial.printf("Unable to allocate minimum REU size of 128K\n"
"REU is disabled\n");
FreeREU();
Serial.printf("REU is disabled\n");
return;
}

Expand All @@ -622,37 +490,9 @@ uint8_t *pRAM_Image = ptrRAM_ImageEnd; //start at end of any CRT-used RAM1
fSpecialBtnChange = &SpecialBtn_REU; //REU RAM is ready; enable button now so a failed/missing preload below doesn't skip it
Serial.printf("REU Size: %luK bytes\n", (uint32_t)REU_Size / 1024);

//pre-load REU into RAM here:
char Filename[MaxPathLength];
EEPreadStr(eepAdREUFilename, Filename);
char *ptrFileName = Filename; //pointer to move past SD/USB/TR:
Serial.printf("Loading REU: %s\n", Filename);
RegMenuTypes MenuSourceID = RegMenuTypeFromFileName(&ptrFileName);

if (MenuSourceID == rmtSD) SDFullInit(); // SD.begin(BUILTIN_SDCARD); with retry if presence detected
if (MenuSourceID == rmtUSBDrive) USBFileSystemWait(); //wait up to 1.5 sec in case USB drive just changed or powered up
//rmtTeensy not allowed, no reu files in Teensy Mem

FS *sourceFS = FSfromSourceID(MenuSourceID);
File LoadFile = sourceFS->open(ptrFileName, FILE_READ);
if (!LoadFile)
{
Serial.println("Not found!");
return;
}

if (LoadFile.size() > REU_Size)
Serial.printf("Too Large! Loading first %luK\n", (uint32_t)REU_Size/1024);

//uint32_t StartmS = millis();
uint32_t CharNum = 0;
for (uint8_t Bank = 0; Bank < NumREU_Banks && LoadFile.available(); Bank++)
CharNum += LoadFile.read(REU_Bank[Bank], REU_RAM_Bank_Size);
//Serial.printf("Read %lu Bytes in %lumS\n", CharNum, millis()-StartmS);
Serial.printf("Read %lu Bytes into REU\n", CharNum);
LoadFile.close();
//delay(250);

LoadRAMBanks(REU_Bank, NumREU_Banks, Filename);

#elif defined(USE_PSRAM)
//higher clock speed and shorter CS setup/hold do help, but still not enough for NUVIEs w/ sound
Expand Down
Loading
Loading