Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expert support #75

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ The following cartridge types are currently supported:
* Super Snapshot v5
* Comal-80
* EasyFlash
* Expert Cartridge
* Georam expansion module (only the first 64K is available)

## Supported File Types
The following file types are currently supported:
Expand Down Expand Up @@ -71,6 +73,16 @@ Kung Fu Flash will work with the PAL version of the Commodore 64 or Commodore 12

Disk drive emulation is using kernal vectors and will not work with fast loaders or software that uses direct hardware access which a lot of games does. Currently REL files are not supported and there is only limited write support.

Georam expansion only has 64kbyte due to microcontroller limitations.

## Expansion

Currently 2 expansions can be selected in the setting menu (F5). Doing a kill (F8) will start this cart.

Expert cartridge starts in 'PRG' mode, after the first reset it is switched to 'ON' mode. Jumping into the menu (using the Menu button) will reset the mode to 'PRG' (if F8 is pressed there). To start the payload use the 'RESTORE' key (freeze button does nothing)

Georam only has 64kbyte of memory for now.

## Thanks
Kung Fu Flash was based on or uses other open source projects:

Expand Down
17 changes: 17 additions & 0 deletions firmware/cartridges/cartridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#include "super_snapshot_5.c"
#include "easyflash.c"
#include "comal80.c"
//#include "georam.c"
#include "expert.c"

static void (*crt_get_handler(uint16_t cartridge_type, bool vic_support)) (void)
{
Expand Down Expand Up @@ -83,6 +85,13 @@ static void (*crt_get_handler(uint16_t cartridge_type, bool vic_support)) (void)
{
return ef_sdio_handler;
}

case CRT_EXPERT_CARTRIDGE:
return expert_handler;

// expansions like expert and georam
case EXP_EXPERT:
return expert_handler;
}

return NULL;
Expand Down Expand Up @@ -119,6 +128,14 @@ static void (*crt_get_init(uint16_t cartridge_type)) (void)

case CRT_EASYFLASH:
return ef_init;

case CRT_EXPERT_CARTRIDGE:
return expert_init;

// expansions
case EXP_EXPERT:
return expert_expansion_init;

}

return NULL;
Expand Down
283 changes: 283 additions & 0 deletions firmware/cartridges/expert.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
/*
* KungFuFlash Copyright (c) 2019-2021 Kim Jørgensen
*
* Expert cart support written and (c) 2020-2021 Chris van Dongen
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/

#define EXPERT_PROGRAM 0 // RAM can be programmed
#define EXPERT_IDLE 1 // waiting for button
#define EXPERT_ACTIVE 2 // button pressed
#define EXPERT_ULTIMAX 3 // ultimax
#define EXPERT_OFF 4 // expert is hidden/off

static uint32_t expert_mode;

static uint32_t const expert_modes[5] =
{
STATUS_LED_OFF|CRT_PORT_NONE, // Orogram
STATUS_LED_ON|CRT_PORT_NONE, // Idle
STATUS_LED_ON|CRT_PORT_NONE, // Active
STATUS_LED_ON|CRT_PORT_ULTIMAX, // Memacces at 0xe000 or 0x8000
STATUS_LED_OFF|CRT_PORT_NONE // Hidden/Off
};


// we need some extra cycles to switch out ultimax mode
#define PHI2_CPU_END_EXPERT (PHI2_CPU_END-3)

// we need a special cart handler
#define C64_VIC_BUS_HANDLER_EXPERT(name) \
C64_VIC_BUS_HANDLER_EXPERT_(name##_handler, name##_vic_read_handler, \
name##_read_handler, name##_early_write_handler, \
name##_write_handler, C64_VIC_DELAY)


// This supports VIC-II reads from the cartridge (i.e. character and sprite data)
// but uses 100% CPU - other interrupts are not served due to the interrupt priority
#define C64_VIC_BUS_HANDLER_EXPERT_(name, vic_read_handler, read_handler, \
early_write_handler, write_handler, vic_delay) \
void name(void) \
{ \
/* As we don't return from this handler, we need to do this here */ \
c64_reset(false); \
/* Use debug cycle counter which is faster to access than timer */ \
DWT->CYCCNT = TIM1->CNT; \
__DMB(); \
while (true) \
{ \
/* Wait for CPU cycle */ \
while (DWT->CYCCNT < PHI2_CPU_START); \
uint32_t addr = c64_addr_read(); \
COMPILER_BARRIER(); \
uint32_t control = c64_control_read(); \
/* Check if CPU has the bus (no bad line) */ \
if ((control & (C64_BA|C64_WRITE)) == (C64_BA|C64_WRITE)) \
{ \
if (read_handler(control, addr)) \
{ \
/* Release bus when phi2 is going low */ \
while (DWT->CYCCNT < PHI2_CPU_END_EXPERT); \
c64_data_input(); \
} \
} \
else if (!(control & C64_WRITE)) \
{ \
early_write_handler(); \
uint32_t data = c64_data_read(); \
write_handler(control, addr, data); \
while (DWT->CYCCNT < PHI2_CPU_END_EXPERT); \
} \
/* VIC-II has the bus */ \
else \
{ \
/* Wait for the control bus to become stable */ \
C64_CPU_VIC_DELAY() \
control = c64_control_read(); \
if (vic_read_handler(control, addr)) \
{ \
/* Release bus when phi2 is going low */ \
while (DWT->CYCCNT < PHI2_CPU_END_EXPERT); \
c64_data_input(); \
} \
} \
c64_crt_control(expert_modes[expert_mode]); /* reset ultimax */ \
if (control & MENU_BTN) \
{ \
/* Allow the menu button interrupt handler to run */ \
c64_interface(false); \
break; \
} \
/* Wait for VIC-II cycle */ \
while (TIM1->CNT >= 80); \
DWT->CYCCNT = TIM1->CNT; \
__DMB(); \
while (DWT->CYCCNT < PHI2_VIC_START); \
addr = c64_addr_read(); \
COMPILER_BARRIER(); \
/* Ideally, we would always wait until PHI2_VIC_DELAY here which is */ \
/* required when the VIC-II has the bus, but we need more cycles */ \
/* in C128 2 MHz mode where data is read from flash */ \
vic_delay(); \
control = c64_control_read(); \
if (vic_read_handler(control, addr)) \
{ \
/* Release bus when phi2 is going high */ \
while (DWT->CYCCNT < PHI2_VIC_END); \
c64_data_input(); \
} \
} \
TIM1->SR = ~TIM_SR_CC3IF; \
__DMB(); \
}


/*************************************************
* Make it programmable the first boot; next boot
* it is activated
*************************************************/

#define EXPERT_SIGNATURE "Expert:booted"

// we use scratch_buf+10 as scratch_buff seems to be overwritten
// probably we need a better way?

static inline void set_expert_signature(void)
{
memcpy(scratch_buf+10, EXPERT_SIGNATURE, sizeof(EXPERT_SIGNATURE));
}

static inline bool expert_signature(void)
{
return memcmp(scratch_buf+10, EXPERT_SIGNATURE, sizeof(EXPERT_SIGNATURE)) == 0;
}

static inline void invalidate_expert_signature(void)
{
scratch_buf[10] = 0;
}

/*************************************************
* C64 bus read callback (VIC-II cycle)
*************************************************/
static inline bool expert_vic_read_handler(uint32_t control, uint32_t addr)
{
// no vic read needed

return false;
}

/*************************************************
* C64 bus read callback (CPU cycle)
*************************************************/
static inline bool expert_read_handler(uint32_t control, uint32_t addr)
{
// when active, the kernal and $8000 is presented in ultimax mode
if((expert_mode==EXPERT_ACTIVE)&&(((addr&0xe000)==0xe000)||((addr&0xe000)==0x8000)))
{
c64_crt_control(expert_modes[EXPERT_ULTIMAX]); /* switch to ultimax if we access kernal or 0x8000 */
c64_data_write(crt_ptr[addr & 0x1fff]);
return true;
}

// access to IO1 switch back to EXPERT_IDLE
if ((!(control & C64_IO1))&&(expert_mode!=EXPERT_PROGRAM))
{
expert_mode=EXPERT_IDLE;
}

// handle external NMI
if ((!((GPIOA->IDR)&GPIO_IDR_ID10))&&(expert_mode==EXPERT_IDLE)) // nmi is on gpioa-10
{
freezer_state = FREEZE_START;
}

// special button toggles expert on/off
if (control & SPECIAL_BTN)
{
freezer_button = FREEZE_PRESSED;
}
else if (freezer_button)
{
freezer_button = FREEZE_RELEASED;

if(expert_mode==EXPERT_IDLE) expert_mode=EXPERT_OFF;
else if(expert_mode==EXPERT_OFF) expert_mode=EXPERT_IDLE;
}

return false;
}

/*************************************************
* C64 bus write callback (early)
*************************************************/
static inline void expert_early_write_handler(void)
{
// Use 3 consecutive writes to detect IRQ/NMI
if (freezer_state && ++freezer_state == FREEZE_3_WRITES)
{
expert_mode=EXPERT_ACTIVE;
c64_crt_control(expert_modes[expert_mode]);
freezer_state = FREEZE_RESET;
}
}

/*************************************************
* C64 bus write callback
*************************************************/
static inline void expert_write_handler(uint32_t control, uint32_t addr, uint32_t data)
{
if((expert_mode==EXPERT_ACTIVE)&&(((addr&0xe000)==0x8000)))
{
c64_crt_control(expert_modes[EXPERT_ULTIMAX]); /* switch to ultimax if we access kernal or roml */
crt_ptr[addr & 0x1fff]=data;
}

// access to IO1 switches back to EXPERT_IDLE
if ((!(control & C64_IO1))&&(expert_mode!=EXPERT_PROGRAM))
{
expert_mode=EXPERT_IDLE;
}

// if we are in program mode we are writable at 0x8000
if((expert_mode==EXPERT_PROGRAM)&&((addr&0xe000)==0x8000))
{
crt_ptr[addr & 0x1FFF]=data;
}
}

/*************************************************
* init when loading an expert image
*************************************************/
static void expert_init(void)
{
crt_ptr = crt_banks[0];

expert_mode=EXPERT_ACTIVE;

c64_crt_control(expert_modes[expert_mode]);
}

/*************************************************
* init when called as expansion
*************************************************/

static void expert_expansion_init(void)
{
int i=0;

crt_ptr = (uint8_t*)scratch_buf+8192;

if(expert_signature())
{
expert_mode=EXPERT_ACTIVE;
}
else
{
expert_mode=EXPERT_PROGRAM;
set_expert_signature();

for(i=0; i<8192; i++) crt_ptr[i]=0x00;
}

c64_crt_control(expert_modes[expert_mode]);
}

// contruct Expert handler
C64_VIC_BUS_HANDLER_EXPERT(expert)

Loading