Skip to content

Commit b5cb2d5

Browse files
committed
Support read-only 2 MiB and 4 MiB EasyFlash CRTs
1 parent 5b61f3e commit b5cb2d5

14 files changed

Lines changed: 765 additions & 40 deletions

‎Source/Teensy/DriveDirLoad.ino‎

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,14 @@ FLASHMEM void HandleExecution()
264264
Printf_dbg("\n Chp# Length Type Bank Addr Size\n");
265265
while (MenuSelCpy.Code_Image + MenuSelCpy.Size - ptrChipOffset > 1) //allow for off by 1 sometimes caused by bin2header
266266
{
267+
const size_t Remaining = MenuSelCpy.Code_Image + MenuSelCpy.Size - ptrChipOffset;
268+
if (Remaining < CRT_CHIP_HDR_LEN ||
269+
toU16(ptrChipOffset+0x0E) > Remaining-CRT_CHIP_HDR_LEN)
270+
{
271+
FreeCrtChips();
272+
IO1[rwRegNextIOHndlr] = EEPROM.read(eepAdNextIOHndlr);
273+
return;
274+
}
267275
if (!ParseChipHeader(ptrChipOffset, "")) //sends error messages
268276
{
269277
FreeCrtChips();
@@ -496,7 +504,11 @@ bool LoadFile(FS *sourceFS, const char* FilePath, StructMenuItem* MyMenuItem)
496504
}
497505

498506
//load header and parse
499-
for (count = 0; count < CRT_MAIN_HDR_LEN; count++) lclBuf[count]=myFile.read(); //Read main header
507+
if (myFile.read(lclBuf, CRT_MAIN_HDR_LEN) != CRT_MAIN_HDR_LEN)
508+
{
509+
myFile.close();
510+
return false;
511+
}
500512
MyMenuItem->Code_Image = lclBuf;
501513
if (!ParseCRTHeader(MyMenuItem, &EXROM, &GAME)) //sends error messages
502514
{
@@ -510,7 +522,17 @@ bool LoadFile(FS *sourceFS, const char* FilePath, StructMenuItem* MyMenuItem)
510522
Printf_dbg("\n Chp# Length Type Bank Addr Size\n");
511523
while (myFile.available())
512524
{
513-
for (count = 0; count < CRT_CHIP_HDR_LEN; count++) lclBuf[count]=myFile.read(); //Read chip header
525+
if (myFile.size() - myFile.position() < CRT_CHIP_HDR_LEN ||
526+
myFile.read(lclBuf, CRT_CHIP_HDR_LEN) != CRT_CHIP_HDR_LEN ||
527+
toU16(lclBuf+0x0E) > myFile.size() - myFile.position())
528+
{
529+
SendMsgPrintfln("Truncated CRT CHIP packet");
530+
myFile.close();
531+
FreeCrtChips();
532+
IO1[rwRegNextIOHndlr] = EEPROM.read(eepAdNextIOHndlr);
533+
RedirectEmptyDriveDirMenu();
534+
return false;
535+
}
514536
if (!ParseChipHeader(lclBuf, FullFilePath)) //sends error messages
515537
{
516538
myFile.close();
@@ -519,7 +541,16 @@ bool LoadFile(FS *sourceFS, const char* FilePath, StructMenuItem* MyMenuItem)
519541
RedirectEmptyDriveDirMenu();
520542
return false;
521543
}
522-
for (count = 0; count < CrtChips[NumCrtChips].ROMSize; count++) CrtChips[NumCrtChips].ChipROM[count]=myFile.read();//read in ROM info:
544+
if (myFile.read(CrtChips[NumCrtChips].ChipROM, CrtChips[NumCrtChips].ROMSize) != CrtChips[NumCrtChips].ROMSize)
545+
{
546+
// The current allocation is owned even when its payload read fails.
547+
NumCrtChips++;
548+
myFile.close();
549+
FreeCrtChips();
550+
IO1[rwRegNextIOHndlr] = EEPROM.read(eepAdNextIOHndlr);
551+
RedirectEmptyDriveDirMenu();
552+
return false;
553+
}
523554
NumCrtChips++;
524555
}
525556

@@ -695,6 +726,7 @@ void FreeCrtChips()
695726
{ //free chips allocated in RAM2 and reset NumCrtChips
696727
for(uint16_t cnt=0; cnt < NumCrtChips; cnt++)
697728
if((uint32_t)CrtChips[cnt].ChipROM >= 0x20200000) free(CrtChips[cnt].ChipROM);
729+
free(CrtChips); CrtChips = NULL;
698730
NumCrtChips = 0;
699731
}
700732

‎Source/Teensy/FileParsers.ino‎

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,25 @@
1818
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1919

2020
uint8_t *ptrRAM_ImageEnd = NULL; // Made global for REU access
21-
uint8_t NumCrtChips = 0;
22-
StructCrtChip CrtChips[MAX_CRT_CHIPS];
21+
uint16_t NumCrtChips = 0;
22+
bool LoadedEasyFlashROM = false;
23+
StructCrtChip *CrtChips = NULL;
24+
// Reserve cartridge metadata in RAM2 only while a CRT is loaded.
25+
bool EnsureCrtChipTable()
26+
{
27+
if (!CrtChips)
28+
{
29+
const uint16_t Capacity = LoadedEasyFlashROM ? MAX_CRT_CHIPS : MAX_LEGACY_CRT_CHIPS;
30+
CrtChips = (StructCrtChip*)calloc(Capacity, sizeof(StructCrtChip));
31+
if (!CrtChips && DriveDirMenu != NULL)
32+
{
33+
// Match the ROM-payload allocator: a large listing may occupy RAM2.
34+
FreeDriveDirMenu();
35+
CrtChips = (StructCrtChip*)calloc(Capacity, sizeof(StructCrtChip));
36+
}
37+
}
38+
return CrtChips != NULL;
39+
}
2340
char* StrSIDInfo; // allocated to RAM2 via StrSIDInfoSize
2441
char* LatestSIDLoaded; // allocated to RAM2 via MaxPathLength
2542
char StrMachineInfo[21] = ""; //~3 extra
@@ -52,6 +69,7 @@ bool ParseCRTHeader(StructMenuItem* MyMenuItem, uint8_t *EXROM, uint8_t *GAME)
5269
// https://vice-emu.sourceforge.io/vice_17.html#SEC369
5370
// http://ist.uwaterloo.ca/~schepers/formats/CRT.TXT
5471

72+
if (MyMenuItem->Size < CRT_MAIN_HDR_LEN) return false;
5573
uint8_t* CRT_Image = MyMenuItem->Code_Image;
5674

5775
SendMsgPrintfln("Parsing CRT File");
@@ -74,6 +92,7 @@ bool ParseCRTHeader(StructMenuItem* MyMenuItem, uint8_t *EXROM, uint8_t *GAME)
7492
SendMsgPrintfln("Ver: %02x.%02x", CRT_Image[0x14], CRT_Image[0x15]);
7593

7694
uint16_t HWType = toU16(CRT_Image+0x16);
95+
LoadedEasyFlashROM = HWType == Cart_EasyFlash;
7796
SendMsgPrintfln("HW Type: %d ($%04x)", (int16_t)HWType, HWType);
7897

7998

@@ -117,6 +136,9 @@ bool ParseCRTHeader(StructMenuItem* MyMenuItem, uint8_t *EXROM, uint8_t *GAME)
117136

118137
bool ParseChipHeader(uint8_t* ChipHeader, const char *FullFilePath)
119138
{
139+
// Other handlers retain their existing 128-CHIP limit.
140+
if (NumCrtChips >= (LoadedEasyFlashROM ? MAX_CRT_CHIPS : MAX_LEGACY_CRT_CHIPS) ||
141+
!EnsureCrtChipTable()) return false;
120142

121143
if (memcmp(ChipHeader, "CHIP", 4)!=0)
122144
{
@@ -129,6 +151,21 @@ bool ParseChipHeader(uint8_t* ChipHeader, const char *FullFilePath)
129151
NumCrtChips, toU32(ChipHeader+0x04), toU16(ChipHeader+0x08), toU16(ChipHeader+0x0A),
130152
toU16(ChipHeader+0x0C), toU16(ChipHeader+0x0E));
131153

154+
if (LoadedEasyFlashROM)
155+
{
156+
// Read-only EasyFlash extensions retain one 8 KiB CHIP per ROM half.
157+
const uint16_t Bank = toU16(ChipHeader+0x0A);
158+
const uint16_t Address = toU16(ChipHeader+0x0C);
159+
const uint16_t Type = toU16(ChipHeader+0x08);
160+
if (Bank >= 256 || toU16(ChipHeader+0x0E) != 8192 ||
161+
toU32(ChipHeader+0x04) != CRT_CHIP_HDR_LEN+8192u ||
162+
(Type != 0 && Type != 2) ||
163+
(Address != 0x8000 && Address != 0xa000 && Address != 0xe000)) return false;
164+
for (uint16_t Previous=0; Previous<NumCrtChips; Previous++)
165+
if (CrtChips[Previous].BankNum == Bank &&
166+
(CrtChips[Previous].LoadAddress == 0x8000) == (Address == 0x8000)) return false;
167+
}
168+
132169
CrtChips[NumCrtChips].BankNum = toU16(ChipHeader+0x0A);
133170
CrtChips[NumCrtChips].LoadAddress = toU16(ChipHeader+0x0C);
134171
CrtChips[NumCrtChips].ROMSize = toU16(ChipHeader+0x0E);
@@ -461,6 +498,7 @@ bool PathIsRoot()
461498
bool SetTypeFromCRT(StructMenuItem* MyMenuItem, uint8_t EXROM, uint8_t GAME)
462499
{
463500
SendMsgPrintfln("%d Chip(s) found/loaded", NumCrtChips);
501+
if (!NumCrtChips) return false;
464502
MyMenuItem->Code_Image = CrtChips[0].ChipROM;
465503

466504
//check configuration

‎Source/Teensy/MinimalBoot/Common/DriveDirLoad.h‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ StructHWID_IOH_Assoc HWID_IOH_Assoc[]=
179179

180180
#define CRT_MAIN_HDR_LEN 0x40
181181
#define CRT_CHIP_HDR_LEN 0x10
182-
#define MAX_CRT_CHIPS 128
182+
#define MAX_CRT_CHIPS 512
183+
#define MAX_LEGACY_CRT_CHIPS 128
183184

184185
#define SwapSeekAddrMask 0xef000000 //virtual address mask/marker for swap-out blocks, or-ed with file seek address (minimal build only)
185186

@@ -192,6 +193,9 @@ struct StructCrtChip
192193
uint16_t BankNum;
193194
};
194195

196+
bool EnsureCrtChipTable();
197+
void FreeCrtChips();
198+
195199
#ifndef MinimumBuild
196200
//only needed in full build:
197201
struct StructExt_ItemType_Assoc

‎Source/Teensy/MinimalBoot/Common/IO_Handlers/IOH_EasyFlash.c‎

Lines changed: 84 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,18 @@
1818
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1919

2020
#define Printf_Swaps Serial.printf //Printf_dbg //
21+
#ifdef VM_HOST_PROFILE
22+
// VMBoot validates a complete 16 KiB client and mirrors it across 64 banks.
23+
// Its ABI-reserved RAM2 arena cannot host the native CRT erased-page buffer.
2124
#define NumDecodeBanks 64
25+
#else
26+
#define NumDecodeBanks 256
27+
// ROM reads run in the PHI2 ISR: an erased half must live in RAM, never flash.
28+
DMAMEM static uint8_t EasyFlashErasedBank[8192];
29+
#endif
2230
uint8_t *BankDecode[NumDecodeBanks][2];
2331
uint8_t EZFlashRAM[256];
32+
uint8_t EasyFlashBankMask = 0x3f;
2433
#ifdef FeatVMHost
2534
// Defined by the extension image only. Stock builds compile none of this.
2635
uint8_t CurrentEasyFlashBank;
@@ -63,7 +72,7 @@ stcIOHandlers IOHndlr_EasyFlash =
6372
};
6473

6574
#ifdef MinimumBuild
66-
void LoadBank(uint32_t SeekTo, uint8_t* ptrImage)
75+
static bool ReadBank(uint32_t SeekTo, uint8_t* ptrImage)
6776
{
6877
if(!myFile)
6978
{
@@ -81,51 +90,90 @@ void LoadBank(uint32_t SeekTo, uint8_t* ptrImage)
8190
if (!myFile)
8291
{
8392
Printf_dbg("File Not Found");
84-
return;
93+
return false;
8594
}
8695
}
8796
//Printf_dbg("\n %lu mS Open ", millis()-Startms);
8897

89-
myFile.seek(SeekTo);
90-
//One bulk read: SdFat moves whole sectors straight into the buffer instead of 8192 single-byte calls.
91-
// SdFat returns -1 on error (through File's size_t, hence the cast); fill any shortfall with
92-
// 0xff, which is what the byte-wise read() returned past the end of the file.
93-
int BytesRead = (int)myFile.read(ptrImage, 8192);
94-
if (BytesRead < 0) BytesRead = 0;
95-
if (BytesRead < 8192) memset(ptrImage + BytesRead, 0xff, 8192 - BytesRead);
96-
//myFile.close();
97-
//Printf_dbg("\n %lu mS Load ", millis()-Startms);
98+
// Do not publish a stale or partial page after a failed seek/read.
99+
return myFile.seek(SeekTo) && myFile.read(ptrImage, 8192) == 8192;
100+
}
101+
102+
// Magic Desk 2 shares this reader and retains its erased-page fallback.
103+
void LoadBank(uint32_t SeekTo, uint8_t* ptrImage)
104+
{
105+
if (!ReadBank(SeekTo, ptrImage)) memset(ptrImage, 0xff, 8192);
98106
}
107+
108+
static void EasyFlashSwapFailed()
109+
{
110+
LOROM_Image = HIROM_Image = NULL;
111+
SetGameDeassert;
112+
SetExROMDeassert;
113+
doReset = true;
114+
DMA_State = DMA_S_StartDisable;
115+
}
116+
uint8_t* ImageCheckAssign(uint8_t* BankRequested);
99117
#endif
100118

101119
void InitHndlr_EasyFlash()
102120
{
103121

104-
//initialize bank decode pointers
105-
for(uint8_t BankNum = 0; BankNum < NumDecodeBanks; BankNum++) {BankDecode[BankNum][0]=NULL;BankDecode[BankNum][1]=NULL;}
106-
for(uint8_t ChipNum = 0; ChipNum < NumCrtChips; ChipNum++)
122+
EasyFlashBankMask = 0x3f;
123+
#ifdef VM_HOST_PROFILE
124+
uint8_t* ErasedBank = NULL; // native CRT initialization is unused by VMBoot
125+
#else
126+
memset(EasyFlashErasedBank, 0xff, sizeof EasyFlashErasedBank);
127+
uint8_t* ErasedBank = EasyFlashErasedBank;
128+
#endif
129+
// Sparse native images read as erased flash, including an absent ROMH in bank 0.
130+
for(uint16_t BankNum = 0; BankNum < NumDecodeBanks; BankNum++) {BankDecode[BankNum][0]=ErasedBank;BankDecode[BankNum][1]=ErasedBank;}
131+
for(uint16_t ChipNum = 0; ChipNum < NumCrtChips; ChipNum++)
107132
{
108133
Printf_dbg("Bank# %02d,%d chip# %03d addr %08x\n", CrtChips[ChipNum].BankNum, (CrtChips[ChipNum].LoadAddress == 0x8000 ? 0 : 1), ChipNum, (uint32_t)CrtChips[ChipNum].ChipROM);
109134
if (CrtChips[ChipNum].BankNum < NumDecodeBanks)
110-
BankDecode[CrtChips[ChipNum].BankNum][CrtChips[ChipNum].LoadAddress == 0x8000 ? 0 : 1] = CrtChips[ChipNum].ChipROM;
135+
{
136+
const uint16_t Bank = CrtChips[ChipNum].BankNum;
137+
if (Bank >= 128) EasyFlashBankMask = 0xff;
138+
else if (Bank >= 64 && EasyFlashBankMask != 0xff) EasyFlashBankMask = 0x7f;
139+
BankDecode[Bank][CrtChips[ChipNum].LoadAddress == 0x8000 ? 0 : 1] = CrtChips[ChipNum].ChipROM;
140+
}
111141
else Serial.printf("Unexp Bank# (%d) in chip %d\n", CrtChips[ChipNum].BankNum, ChipNum);
112142
}
113143

114144
memset(EZFlashRAM, 0, 256);
115145

116146
#ifdef MinimumBuild
117147
//initialize/invalidate swap buffer, pre-load first swappable chips
118-
uint8_t ChipNum = 0;
148+
uint16_t ChipNum = 0;
119149
for(uint8_t BuffNum = 0; BuffNum < Num8kSwapBuffers; BuffNum++)
120150
{
121151
SwapBuffers[BuffNum].Offset=0; //default to zero/invalid
152+
// Bank 0 must be resident before the C64 is released, even when CHIP
153+
// packets are unordered and its halves were not loaded into RAM.
154+
uint8_t* StartupHalf = BuffNum < 2 ? BankDecode[0][BuffNum] : NULL;
155+
if (((uint32_t)StartupHalf & SwapSeekAddrMask) == SwapSeekAddrMask)
156+
{
157+
if (!ReadBank((uint32_t)StartupHalf & ~SwapSeekAddrMask, SwapBuffers[BuffNum].Image))
158+
{
159+
EasyFlashSwapFailed();
160+
return;
161+
}
162+
SwapBuffers[BuffNum].Offset = (uint32_t)StartupHalf;
163+
continue;
164+
}
122165
//find next chip marked as swappable
123166
while (ChipNum < NumCrtChips)
124167
{
125-
if(((uint32_t)CrtChips[ChipNum].ChipROM & SwapSeekAddrMask) == SwapSeekAddrMask)
168+
if(((uint32_t)CrtChips[ChipNum].ChipROM & SwapSeekAddrMask) == SwapSeekAddrMask &&
169+
CrtChips[ChipNum].BankNum != 0)
126170
{
127171
//pre-load and update
128-
LoadBank((uint32_t)CrtChips[ChipNum].ChipROM & ~SwapSeekAddrMask, SwapBuffers[BuffNum].Image);
172+
if (!ReadBank((uint32_t)CrtChips[ChipNum].ChipROM & ~SwapSeekAddrMask, SwapBuffers[BuffNum].Image))
173+
{
174+
EasyFlashSwapFailed();
175+
return;
176+
}
129177
SwapBuffers[BuffNum].Offset = (uint32_t)CrtChips[ChipNum].ChipROM;
130178
Printf_Swaps("Pre%d: %08x\n", BuffNum, (uint32_t) SwapBuffers[BuffNum].Offset & ~SwapSeekAddrMask);
131179
ChipNum++;
@@ -137,8 +185,13 @@ void InitHndlr_EasyFlash()
137185
#endif
138186

139187
//start with Bank 0:
188+
#ifdef MinimumBuild
189+
LOROM_Image = ImageCheckAssign(BankDecode[0][0]);
190+
HIROM_Image = ImageCheckAssign(BankDecode[0][1]);
191+
#else
140192
LOROM_Image = BankDecode[0][0];
141193
HIROM_Image = BankDecode[0][1];
194+
#endif
142195
//Printf_dbg("Jmp vect: $%02x%02x\n", HIROM_Image[0x1FFD], HIROM_Image[0x1FFC]);
143196
if(memcmp(HIROM_Image+0x1800, "eapi", 4)==0) Serial.printf("EAPI found\n");
144197
else Printf_dbg("EAPI *not* found: %d %d %d %d\n", HIROM_Image[0x1800], HIROM_Image[0x1801], HIROM_Image[0x1802], HIROM_Image[0x1803]);
@@ -182,7 +235,7 @@ void IO1Hndlr_EasyFlash(uint8_t Address, bool R_Wn)
182235
switch (Address)
183236
{
184237
case 0x00: // Register $DE00 – EasyFlash Bank (write-only)
185-
Data &= 0x3f;
238+
Data &= EasyFlashBankMask;
186239
#ifdef FeatVMHost
187240
CurrentEasyFlashBank = Data;
188241
#endif
@@ -248,7 +301,12 @@ void PollingHndlr_EasyFlash()
248301
if(HIROM_Image && HIROM_Image == SwapBuffers[NextSwapBuffNum].Image)
249302
if(++NextSwapBuffNum==Num8kSwapBuffers) NextSwapBuffNum=0;
250303

251-
LoadBank((uint32_t)LOROM_Image & ~SwapSeekAddrMask, SwapBuffers[NextSwapBuffNum].Image);
304+
SwapBuffers[NextSwapBuffNum].Offset = 0;
305+
if (!ReadBank((uint32_t)LOROM_Image & ~SwapSeekAddrMask, SwapBuffers[NextSwapBuffNum].Image))
306+
{
307+
EasyFlashSwapFailed();
308+
return;
309+
}
252310
Printf_Swaps("L%02d: %08x ", NextSwapBuffNum, (uint32_t)LOROM_Image & ~SwapSeekAddrMask);
253311
SwapBuffers[NextSwapBuffNum].Offset = (uint32_t)LOROM_Image;
254312
LOROM_Image = SwapBuffers[NextSwapBuffNum].Image;
@@ -261,7 +319,12 @@ void PollingHndlr_EasyFlash()
261319
if(LOROM_Image && LOROM_Image == SwapBuffers[NextSwapBuffNum].Image)
262320
if(++NextSwapBuffNum==Num8kSwapBuffers) NextSwapBuffNum=0;
263321

264-
LoadBank((uint32_t)HIROM_Image & ~SwapSeekAddrMask, SwapBuffers[NextSwapBuffNum].Image);
322+
SwapBuffers[NextSwapBuffNum].Offset = 0;
323+
if (!ReadBank((uint32_t)HIROM_Image & ~SwapSeekAddrMask, SwapBuffers[NextSwapBuffNum].Image))
324+
{
325+
EasyFlashSwapFailed();
326+
return;
327+
}
265328
Printf_Swaps("H%02d: %08x ", NextSwapBuffNum, (uint32_t)HIROM_Image & ~SwapSeekAddrMask);
266329
SwapBuffers[NextSwapBuffNum].Offset = (uint32_t)HIROM_Image;
267330
HIROM_Image = SwapBuffers[NextSwapBuffNum].Image;

‎Source/Teensy/MinimalBoot/Common/IO_Handlers/IOH_MagicDesk.c‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ stcIOHandlers IOHndlr_MagicDesk =
3232
NULL, //called at the end of EVERY c64 cycle
3333
};
3434

35-
extern StructCrtChip CrtChips[];
36-
extern uint8_t NumCrtChips;
35+
extern StructCrtChip *CrtChips;
36+
extern uint16_t NumCrtChips;
3737

3838
uint8_t BankReg;
3939

‎Source/Teensy/MinimalBoot/Common/IO_Handlers/IOH_REU.c‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ extern bool DMAByte(uint8_t *Data);
147147

148148
extern void FreeDriveDirMenu();
149149
extern uint8_t RAM_Image[];
150-
extern StructCrtChip CrtChips[]; // Removal breaks downstream code, so keep it here for now.
151-
extern uint8_t NumCrtChips;
150+
extern StructCrtChip *CrtChips; // Removal breaks downstream code, so keep it here for now.
151+
extern uint16_t NumCrtChips;
152152
extern StructMenuItem *DriveDirMenu;
153153
extern uint8_t *ptrRAM_ImageEnd; // points to end of CRT-used RAM1 buffer
154154

0 commit comments

Comments
 (0)