/** * @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 #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) ; } } } }