Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 5 additions & 4 deletions Source/Teensy/DriveDirLoad.ino
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


#include "MinimalBoot/Common/C128Crt.h"
#ifdef VM_EXTENSIONS_ENABLED
#include "MinimalBoot/Common/VMLaunch.h"
#include "MinimalBoot/Common/VMPackageList.h"
Expand Down Expand Up @@ -303,6 +304,8 @@ FLASHMEM void HandleExecution()
//has to be distilled down to one of these by this point, only ones supported so far.
//Emulate ROM or prep for transfer
uint8_t CartLoaded = false;
//a C128 cart widens these, and a direct remote launch skips SetUpMainMenuROM, which resets them
LOROM_Mask = HIROM_Mask = 0x1fff;

switch(MenuSelCpy.ItemType)
{
Expand Down Expand Up @@ -392,10 +395,7 @@ FLASHMEM void HandleExecution()
CartLoaded=true;
break;
case rtBinC128:
SetGameDeassert;
SetExROMDeassert;
LOROM_Image = MenuSelCpy.Code_Image;
HIROM_Image = NULL;
LaunchC128Cart();
CartLoaded=true;
break;
case rtFilePrg:
Expand Down Expand Up @@ -696,6 +696,7 @@ void FreeCrtChips()
for(uint16_t cnt=0; cnt < NumCrtChips; cnt++)
if((uint32_t)CrtChips[cnt].ChipROM >= 0x20200000) free(CrtChips[cnt].ChipROM);
NumCrtChips = 0;
C128Clock = NULL;
}

uint8_t Assoc_Ext_ItemType(char * FileName)
Expand Down
13 changes: 5 additions & 8 deletions Source/Teensy/FileParsers.ino
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ bool ParseCRTHeader(StructMenuItem* MyMenuItem, uint8_t *EXROM, uint8_t *GAME)
SendMsgPrintfln("Parsing CRT File");
SendMsgPrintfln("CRT image size: %luK $%08x", MyMenuItem->Size/1024, MyMenuItem->Size);

if (memcmp(CRT_Image, "C128 CARTRIDGE", 14)==0) SendMsgPrintfln("C128 crt");
if (DetectC128Crt(CRT_Image)) SendMsgPrintfln("C128 crt");
else if (memcmp(CRT_Image, "C64 CARTRIDGE", 13)==0) SendMsgPrintfln("C64 crt");
else
{
Expand All @@ -76,6 +76,8 @@ bool ParseCRTHeader(StructMenuItem* MyMenuItem, uint8_t *EXROM, uint8_t *GAME)
uint16_t HWType = toU16(CRT_Image+0x16);
SendMsgPrintfln("HW Type: %d ($%04x)", (int16_t)HWType, HWType);

if (!C128HWTypeSupported(HWType)) return false;


#ifdef Fab04_Freezers
// Create pseudo-CRT number (real HWType + 200) to select dual REU & CRT handler
Expand Down Expand Up @@ -465,6 +467,8 @@ bool SetTypeFromCRT(StructMenuItem* MyMenuItem, uint8_t EXROM, uint8_t GAME)

//check configuration

if (CrtIsC128) return SetC128Type(MyMenuItem);

#ifdef Fab04_Freezers
if (IO1[rwRegNextIOHndlr] == IOH_SuperSnapshotV5 ||
IO1[rwRegNextIOHndlr] == IOH_FinalCartridgeIII ||
Expand Down Expand Up @@ -501,13 +505,6 @@ bool SetTypeFromCRT(StructMenuItem* MyMenuItem, uint8_t EXROM, uint8_t GAME)
return true;
}

if(CrtChips[0].LoadAddress == 0x0000 && CrtChips[0].ROMSize == 0x2000 && EXROM==0 && GAME==0)
{
MyMenuItem->ItemType = rtBinC128;
SendMsgPrintfln(" C128 config");
return true;
}

SendMsgPrintfln("HW config unknown");
return false;
}
Expand Down
90 changes: 90 additions & 0 deletions Source/Teensy/MinimalBoot/Common/C128Crt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: MIT
#pragma once
#include <EEPROM.h>
#include <TimeLib.h>
#include "CrtChips.h"

// Included after IOHandlers.h and Common_Defs.h, ahead of the .ino code that calls it: it sets the
// cart lines and ROM pointers those define.

extern StructCrtChip CrtChips[];
extern uint8_t NumCrtChips;

bool CrtIsC128 = false;
static C128CartMap C128Map;

// Record layout: tools/c128/README.md, "The boot clock".
static const uint8_t C128ClockTag[8] = {'T', 'R', 'C', 'L', 'O', 'C', 'K', 1};
static const uint8_t C128ClockLen = 7;
static uint8_t *C128Clock = NULL;
static const int32_t TimezoneStepSecs = 30 * 60;
static int32_t C128ClockLocalOffset;

static bool DetectC128Crt(const uint8_t *CrtHeader)
{
CrtIsC128 = (memcmp(CrtHeader, "C128 CARTRIDGE", 14) == 0);
return CrtIsC128;
}

static bool C128HWTypeSupported(uint16_t HWType)
{ //C128 crts number their HW types separately: 1 is Warpspeed128, not Action Replay
if (!CrtIsC128 || HWType == Cart_Generic) return true;
SendMsgPrintfln("Unsupported C128 HW Type");
return false;
}

// The C128 half of SetTypeFromCRT; keeps the map for LaunchC128Cart.
static bool SetC128Type(StructMenuItem *MyMenuItem)
{
if (!MapC128Cart(CrtChips, NumCrtChips, &C128Map))
{
SendMsgPrintfln("C128 chip layout unsupported");
return false;
}
MyMenuItem->ItemType = rtBinC128;
SendMsgPrintfln(" C128 config");
return true;
}

static uint8_t *FindC128Clock(const uint8_t *Image, uint16_t Mask)
{
if (Image == NULL || ((uint32_t)Image & SwapSeekAddrMask) == SwapSeekAddrMask) return NULL;
for (uint32_t Offset = 0; Offset + sizeof(C128ClockTag) + C128ClockLen <= (uint32_t)Mask + 1; Offset++)
if (memcmp(Image + Offset, C128ClockTag, sizeof(C128ClockTag)) == 0)
return (uint8_t *)Image + Offset + sizeof(C128ClockTag);
return NULL;
}

static void WriteC128Clock()
{
tmElements_t tm;
breakTime(Teensy3Clock.get() + C128ClockLocalOffset, tm);
const uint16_t Year = tmYearToCalendar(tm.Year);
const uint8_t Clock[C128ClockLen] = {(uint8_t)(Year / 100), (uint8_t)(Year % 100),
tm.Month, tm.Day, tm.Hour, tm.Minute, tm.Second};
memcpy(C128Clock, Clock, C128ClockLen);
}

void UpdateC128Clock()
{
static uint32_t LastWrite;
if (C128Clock == NULL || millis() - LastWrite < 100) return;
LastWrite = millis();
WriteC128Clock();
}

static void LaunchC128Cart()
{
SetGameDeassert;
SetExROMDeassert;
LOROM_Image = C128Map.ROML;
HIROM_Image = C128Map.ROMH;
LOROM_Mask = C128Map.ROMLMask;
HIROM_Mask = C128Map.ROMHMask;

C128Clock = FindC128Clock(C128Map.ROML, C128Map.ROMLMask);
if (C128Clock == NULL) C128Clock = FindC128Clock(C128Map.ROMH, C128Map.ROMHMask);
if (C128Clock == NULL) return;
C128ClockLocalOffset = (int8_t)EEPROM.read(eepAdTimezone) * TimezoneStepSecs;
WriteC128Clock();
}
46 changes: 46 additions & 0 deletions Source/Teensy/MinimalBoot/Common/CrtChips.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: MIT
#pragma once
#include <stddef.h>
#include <stdint.h>

// One CHIP packet of a loaded .crt. Pure, so the native tests can hold the layout rules below.
struct StructCrtChip
{
uint8_t *ChipROM;
uint16_t LoadAddress;
uint16_t ROMSize;
uint16_t BankNum;
};

struct C128CartMap
{
const uint8_t *ROML, *ROMH;
uint16_t ROMLMask, ROMHMask;
};

// Generic C128 cart (VICE CRT spec, C128 type 0): ROML is $8000-$BFFF and ROMH $C000-$FFFF, each
// one 8k (mirrored) or 16k chip.
static inline bool MapC128Cart(const StructCrtChip *Chips, uint8_t NumChips, C128CartMap *Map)
{
const StructCrtChip *ROML = NULL, *ROMH = NULL;
for (uint8_t ChipNum = 0; ChipNum < NumChips; ChipNum++)
{
const StructCrtChip *Chip = &Chips[ChipNum];
if (Chip->BankNum != 0 || (Chip->ROMSize != 0x2000 && Chip->ROMSize != 0x4000)) return false;

const StructCrtChip **Slot;
if (Chip->LoadAddress == 0x8000) Slot = &ROML;
else if (Chip->LoadAddress == 0xC000) Slot = &ROMH;
else return false;

if (*Slot != NULL) return false;
*Slot = Chip;
}
if (ROML == NULL && ROMH == NULL) return false;

Map->ROML = ROML ? ROML->ChipROM : NULL;
Map->ROMH = ROMH ? ROMH->ChipROM : NULL;
Map->ROMLMask = ROML ? ROML->ROMSize - 1 : 0x1fff;
Map->ROMHMask = ROMH ? ROMH->ROMSize - 1 : 0x1fff;
return true;
}
8 changes: 1 addition & 7 deletions Source/Teensy/MinimalBoot/Common/DriveDirLoad.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,7 @@ StructHWID_IOH_Assoc HWID_IOH_Assoc[]=
#define SwapSeekAddrMask 0xef000000 //virtual address mask/marker for swap-out blocks, or-ed with file seek address (minimal build only)


struct StructCrtChip
{
uint8_t *ChipROM;
uint16_t LoadAddress;
uint16_t ROMSize;
uint16_t BankNum;
};
#include "CrtChips.h"

#ifndef MinimumBuild
//only needed in full build:
Expand Down
20 changes: 8 additions & 12 deletions Source/Teensy/MinimalBoot/Min_DriveDirLoad.ino
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

uint8_t NumCrtChips = 0;
StructCrtChip CrtChips[MAX_CRT_CHIPS];
#include "Common/C128Crt.h"
File myFile = NULL;

void HandleExecution()
Expand Down Expand Up @@ -62,10 +63,7 @@ void HandleExecution()
CartLoaded=true;
break;
case rtBinC128:
SetGameDeassert;
SetExROMDeassert;
LOROM_Image = MenuSelCpy.Code_Image;
HIROM_Image = NULL;
LaunchC128Cart();
CartLoaded=true;
break;
case rtUnknown: //had to have been marked unknown after check at start
Expand Down Expand Up @@ -237,7 +235,7 @@ bool ParseCRTHeader(StructMenuItem* MyMenuItem, uint8_t *EXROM, uint8_t *GAME)
SendMsgPrintfln("Parsing CRT File");
SendMsgPrintfln("CRT image size: %luK $%08x", MyMenuItem->Size/1024, MyMenuItem->Size);

if (memcmp(CRT_Image, "C128 CARTRIDGE", 14)==0) SendMsgPrintfln("C128 crt");
if (DetectC128Crt(CRT_Image)) SendMsgPrintfln("C128 crt");
else if (memcmp(CRT_Image, "C64 CARTRIDGE", 13)==0) SendMsgPrintfln("C64 crt");
else
{
Expand All @@ -256,6 +254,8 @@ bool ParseCRTHeader(StructMenuItem* MyMenuItem, uint8_t *EXROM, uint8_t *GAME)
uint16_t HWType = toU16(CRT_Image+0x16);
SendMsgPrintfln("HW Type: %d ($%04x)", (int16_t)HWType, HWType);

if (!C128HWTypeSupported(HWType)) return false;

if (HWType != Cart_Generic) //leave IOH as default/user set for generic
{
if (!AssocHWID_IOH(HWType))
Expand Down Expand Up @@ -326,6 +326,7 @@ void FreeCrtChips()
for(uint16_t cnt=0; cnt < NumCrtChips; cnt++)
if((uint32_t)CrtChips[cnt].ChipROM >= 0x20200000 && (uint32_t)CrtChips[cnt].ChipROM < SwapSeekAddrMask) free(CrtChips[cnt].ChipROM);
NumCrtChips = 0;
C128Clock = NULL;
}

bool PathIsRoot()
Expand All @@ -339,6 +340,8 @@ bool SetTypeFromCRT(StructMenuItem* MyMenuItem, uint8_t EXROM, uint8_t GAME)
MyMenuItem->Code_Image = CrtChips[0].ChipROM;

//check configuration

if (CrtIsC128) return SetC128Type(MyMenuItem);

if(CrtChips[0].LoadAddress == 0x8000 && CrtChips[0].ROMSize == 0x2000)
//Usually GAME ==1 and EXROM==0
Expand All @@ -364,13 +367,6 @@ bool SetTypeFromCRT(StructMenuItem* MyMenuItem, uint8_t EXROM, uint8_t GAME)
return true;
}

if(CrtChips[0].LoadAddress == 0x0000 && CrtChips[0].ROMSize == 0x2000 && EXROM==0 && GAME==0)
{
MyMenuItem->ItemType = rtBinC128;
SendMsgPrintfln(" C128 config");
return true;
}

SendMsgPrintfln("HW config unknown");
return false;
}
Expand Down
3 changes: 3 additions & 0 deletions Source/Teensy/MinimalBoot/MinimalBoot.ino
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ StructMenuItem DriveDirMenu;
char DriveDirPath[MaxPathLength];
uint16_t LOROM_Mask, HIROM_Mask;
Stream *CmdChannel = &Serial;
void UpdateC128Clock(); //C128Crt.h, which needs the globals here and comes in with the .ino code

#ifdef FeatTCPListen
#include <NativeEthernet.h>
Expand Down Expand Up @@ -246,6 +247,8 @@ void loop()
runMainTRApp_FromMin();
#endif
}

UpdateC128Clock();

if (doReset)
{
Expand Down
2 changes: 1 addition & 1 deletion Source/Teensy/TRMenuFiles/ROMs/C128_789010.crt.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ PROGMEM static const unsigned char C128_789010_crt[] = {
0x72, 0x69, 0x63, 0x20, 0x43, 0x31, 0x32, 0x38, 0x20, 0x43, 0x61, 0x72,
0x74, 0x72, 0x69, 0x64, 0x67, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x43, 0x48, 0x49, 0x50, 0x00, 0x00, 0x20, 0x10,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x4c, 0x0a, 0x80, 0x4c,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x20, 0x00, 0x4c, 0x0a, 0x80, 0x4c,
0x0a, 0x80, 0x02, 0x43, 0x42, 0x4d, 0x78, 0xa2, 0xff, 0x9a, 0xd8, 0xa9,
0x73, 0x85, 0x01, 0xa9, 0x37, 0x85, 0x00, 0xa9, 0x3a, 0x8d, 0x00, 0xff,
0xa9, 0x06, 0x8d, 0x06, 0xd5, 0xa2, 0x04, 0xbd, 0xf1, 0x98, 0x9d, 0x07,
Expand Down
3 changes: 3 additions & 0 deletions Source/Teensy/Teensy.ino
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ uint8_t nfcState = nfcStateBitDisabled; //default disabled unless set in eeprom
Stream *CmdChannel = &Serial;
bool isFrozen = false;
void (*fSpecialBtnChange)(bool Up_nDn); //Pointer to function called when Special Button Changes
void UpdateC128Clock(); //C128Crt.h, which needs the globals here and comes in with the .ino code

#ifdef Fab04_SpecialButton
Bounce SpecialBtnBounce = Bounce(Special_Btn_In_PIN, 35); //35mS debounce time
Expand Down Expand Up @@ -285,6 +286,8 @@ void loop()
if (IO1[rwRegPwrUpDefaults2] & rpud2NFCEnabled) nfcInit(); //connect to nfc scanner
SetUpMainMenuROM(); //back to main menu, also sets doReset
}

UpdateC128Clock();

if (doReset)
{
Expand Down
3 changes: 2 additions & 1 deletion tools/verify-extensions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -503,8 +503,9 @@ native('fail_test');
native('hello_module_test', [sandbox('hello-sandbox-')]);
native('host_install_test', [hostPackageFixture(sandbox('host-package-'))]);
native('c64spans_test');
native('c128cart_test');
native('transfer_test');

if (keep) console.log(`Artifacts kept in ${output}`);
else fs.rmSync(output, { recursive: true, force: true });
console.log('PASS: extension loader conformance (published host contract, package format, file services, image validation, registry, settings package list, launch routing, listing invalidation, packet scheduler, failure reporting, host-package installation, reference module, C64 span rules)');
console.log('PASS: extension loader conformance (published host contract, package format, file services, image validation, registry, settings package list, launch routing, listing invalidation, packet scheduler, failure reporting, host-package installation, reference module, C64 span rules, C128 cart layouts)');
Loading
Loading