FoenixToolbox/src/cartridge.c
2024-08-19 22:12:40 -04:00

159 lines
4 KiB
C

/**
* @file cartdridge.c
* @author your name (you@domain.com)
* @brief Support for the flash cartridge
* @version 0.1
* @date 2024-08-11
*
* @copyright Copyright (c) 2024
*
*/
#include <stdbool.h>
#include "cartridge.h"
#include "timers.h"
volatile uint8_t * cartridge = ((volatile uint8_t *)0xf40000);
static short cart_id_memo = CART_ID_UNDEF;
static bool cart_rw_match(uint8_t value) {
*cartridge = value;
uint8_t new_value = *cartridge;
return (new_value == value);
}
/**
* @brief Send a command to the flash chip
*
* @param command the number of the command
*/
static void cart_flash_command(uint8_t command) {
cartridge[0x5555] = 0xaa;
cartridge[0x2aaa] = 0x55;
cartridge[0x5555] = command;
}
/**
* @brief Send the command to enter system ID mode on the flash chip
*
*/
static void cart_flash_system_id_enter() {
// Attempt to enter software ID mode on the flash cartridge
cart_flash_command(0x90);
}
/**
* @brief Send the command to exit the system ID mode on the flash chip
*
*/
static void cart_flash_system_id_exit() {
// Attempt to exit software ID mode on the flash cartridge
cart_flash_command(0xf0);
}
/**
* @brief Return a code describing the cartridge
*
* @return short the code describing the cartridge (-1 for none found)
*/
SYSTEMCALL short cart_id() {
if (cart_id_memo == CART_ID_UNDEF) {
// Start off assuming we don't have anything in the slot
cart_id_memo = -1;
// Check to see if there is RAM at the first byte of the cartridge
if (cart_rw_match(0x12) && cart_rw_match(0x23) && cart_rw_match(0x34)) {
cart_id_memo = CART_ID_RAM;
} else {
// Check to see if we have a flash chip
cart_flash_system_id_enter();
// Check for the manufacturer's ID we expect (BF for Microchip)
if (cartridge[0x0000] == 0xbf) {
// Yes... get the chip ID
uint8_t chip_id = cartridge[0x0001];
if ((chip_id == 0xd5) || (chip_id == 0xd6) || (chip_id == 0xd7)) {
cart_id_memo = CART_ID_FLASH;
}
}
// Leave system ID mode, regardless
cart_flash_system_id_exit();
}
}
return cart_id_memo;
}
/**
* @brief Erase the entire flash memory
*
*/
SYSTEMCALL void cart_erase() {
if (cart_id() == CART_ID_FLASH) {
cart_flash_command(0x80);
cart_flash_command(0x10);
// Wait half a second
long target_jiffies = timers_jiffies() + 30;
while (timers_jiffies() < target_jiffies) ;
}
}
/**
* @brief Write a byte to the flash memory
*
* @param address the address to write to (in CPU address space)
* @param value the byte to write to the address
*/
SYSTEMCALL void cart_write_b(uint32_t address, uint8_t value) {
uint32_t cart_base_address = (uint32_t)cartridge;
uint32_t cart_end_address = 0xf7ffff;
uint8_t current_value = 0;
if (cart_id() == CART_ID_FLASH) {
if ((cart_base_address <= address) && (cart_end_address >= address)) {
cart_flash_command(0xa0);
// Attempt to write the data
volatile uint8_t * dest = ((volatile uint8_t *)address);
*dest = value;
// Wait for the value to show up at the destination
long target_jiffies = timers_jiffies() + 3;
while (timers_jiffies() < target_jiffies) ;
}
}
}
/**
* @brief Write a block of bytes to the flash cartridge (if present)
*
* @param dest the address within the flash cartridge to start writing to
* @param src the address in regular memory to start reading from
* @param count the number of bytes to write
*/
SYSTEMCALL void cart_write(uint32_t dest, uint32_t src, int count) {
uint32_t cart_base_address = (uint32_t)cartridge;
uint32_t cart_end_address = 0xf7ffff;
uint8_t current_value = 0;
if (cart_id() == CART_ID_FLASH) {
if ((cart_base_address <= src) && (cart_end_address >= src + count)) {
for (int x = 0; x < count; x++) {
volatile uint8_t * dest_position = ((volatile uint8_t *)(dest + x));
uint8_t * src_position = (uint8_t *)(src + x);
cart_flash_command(0xa0);
*dest_position = *src_position;
// Wait for the value to show up at the destination
long target_jiffies = timers_jiffies() + 3;
while (timers_jiffies() < target_jiffies) ;
}
}
}
}