/* This file is part of Flycast. Flycast is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. Flycast is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Flycast. If not, see . */ #pragma once #include "types.h" #include "serialize.h" #include struct MemChip { u8* data; u32 size; u32 mask; protected: u32 write_protect_size; std::string load_filename; MemChip(u32 size, u32 write_protect_size = 0) { this->data = new u8[size](); this->size = size; this->mask = size - 1; // must be power of 2 this->write_protect_size = write_protect_size; } public: virtual ~MemChip() { delete[] data; } virtual u8 Read8(u32 addr) { return data[addr & mask]; } u32 Read(u32 addr, u32 sz) { addr &= mask; u32 rv = 0; for (u32 i = 0; i < sz; i++) rv |= Read8(addr + i) << (i * 8); return rv; } bool Load(const std::string& file); virtual bool Reload() { return true; } bool Load(const std::string &prefix, const std::string &names_ro, const std::string &title); void Load(const u8 *data, size_t size); void digest(u8 md5Digest[16]); virtual void Reset() {} virtual void Serialize(Serializer& ser) const { } virtual void Deserialize(Deserializer& deser) { } }; struct WritableChip : MemChip { protected: WritableChip(u32 size, u32 write_protect_size = 0) : MemChip(size, write_protect_size) {} public: virtual void Write(u32 addr, u32 data, u32 size) = 0; bool Reload() override { if (load_filename.empty()) return false; return Load(this->load_filename); } void Save(const std::string& file); void Save(const std::string &prefix, const std::string &name_ro, const std::string &title); }; struct RomChip : MemChip { RomChip(u32 sz) : MemChip(sz) {} }; struct SRamChip : WritableChip { SRamChip(u32 sz, u32 write_protect_size = 0) : WritableChip(sz, write_protect_size) {} void Write(u32 addr, u32 val, u32 sz) override { addr &= mask; if (addr < write_protect_size) return; switch (sz) { case 1: data[addr] = (u8)val; return; case 2: *(u16 *)&data[addr] = (u16)val; return; case 4: *(u32 *)&data[addr] = val; return; default: die("invalid access size"); } } void Serialize(Serializer& ser) const override { ser.serialize(&this->data[write_protect_size], size - write_protect_size); } void Deserialize(Deserializer& deser) override { deser.deserialize(&this->data[write_protect_size], size - write_protect_size); } }; // // Flash block handling code borrowed from redream (https://github.com/inolen/redream) // // magic cookie every block-allocated partition begins with #define FLASH_MAGIC_COOKIE "KATANA_FLASH____" #define FLASH_BLOCK_SIZE 0x40 // each bitmap is 64 bytes in length, and each byte can record the state of 8 // physical blocks (one per bit), therefore, each bitmap can represent up to // 512 physical blocks. these 512 blocks are each 64-bytes in length, meaning // each partition would need partition_size / 32768 bitmap blocks to represent // all of its physical blocks #define FLASH_BITMAP_BLOCKS (FLASH_BLOCK_SIZE * 8) #define FLASH_BITMAP_BYTES (FLASH_BITMAP_BLOCKS * 64) // flash partitions #define FLASH_PT_FACTORY 0 #define FLASH_PT_RESERVED 1 #define FLASH_PT_USER 2 #define FLASH_PT_GAME 3 #define FLASH_PT_UNKNOWN 4 #define FLASH_PT_NUM 5 // flash logical blocks #define FLASH_USER_SYSCFG 0x05 #define FLASH_USER_INET 0x80 #define FLASH_USER_ISP1 0xC0 #define FLASH_USER_ISP2 0xC6 struct flash_syscfg_block { u16 block_id; // last set time (seconds since 1/1/1950 00:00) u16 time_lo; u16 time_hi; // in 15 mins increment, from -48 (West) to +52 (East), unused int8_t time_zone; u8 lang; u8 mono; u8 autostart; u8 unknown2[4]; u8 reserved[50]; }; struct flash_isp1_block { u16 block_id; u8 _unknown[4]; char sega[4]; char username[28]; char password[16]; char phone[8]; u16 crc; }; struct flash_isp2_block { u16 block_id; char sega[4]; char username[28]; char password[16]; char phone[12]; u16 crc; }; // header block in block-allocated partition struct flash_header_block { char magic[16]; u8 part_id; u8 version; u8 reserved[46]; }; // user block in block-allocated partition struct flash_user_block { u16 block_id; u8 data[60]; u16 crc; }; // Macronix 29LV160TMC // AtomisWave uses a custom 29L001mc model struct DCFlashChip : WritableChip { DCFlashChip(u32 sz, u32 write_protect_size = 0): WritableChip(sz, write_protect_size), state(FS_Normal) { } enum FlashState { FS_Normal, FS_ReadAMDID1, FS_ReadAMDID2, FS_ByteProgram, FS_EraseAMD1, FS_EraseAMD2, FS_EraseAMD3, FS_SelectMode, }; FlashState state; void Reset() override { //reset the flash chip state state = FS_Normal; } void Write(u32 addr,u32 val,u32 sz) override; u8 Read8(u32 addr) override { if (state == FS_SelectMode) { state = FS_Normal; switch (addr & 0x43) { case 0: // manufacturer's code return 4; // or 0x20 or 1 case 1: // device code return 0xb0; // or 0x40 or 0x3e case 2: // sector protection verification // sector protection DEBUG_LOG(FLASHROM, "Sector protection address %x", addr); return (addr & 0x1e000) == 0x1a000; default: WARN_LOG(FLASHROM, "SelectMode unknown address %x", addr); return 0; } } return MemChip::Read8(addr); } int WriteBlock(u32 part_id, u32 block_id, const void *data); int ReadBlock(u32 part_id, u32 block_id, void *data); void GetPartitionInfo(int part_id, int *offset, int *size) { switch (part_id) { case FLASH_PT_FACTORY: *offset = 0x1a000; *size = 8_KB; break; case FLASH_PT_RESERVED: *offset = 0x18000; *size = 8_KB; break; case FLASH_PT_USER: *offset = 0x1c000; *size = 16_KB; break; case FLASH_PT_GAME: *offset = 0x10000; *size = 32_KB; break; case FLASH_PT_UNKNOWN: *offset = 0x00000; *size = 64_KB; break; default: *offset = 0; *size = 0; die("unknown partition"); break; } } void Validate(); private: int crc_block(struct flash_user_block *block) { const u8 *buf = (const u8 *)block; int size = 62; int n = 0xffff; for (int i = 0; i < size; i++) { n ^= (buf[i] << 8); for (int c = 0; c < 8; c++) { if (n & 0x8000) { n = (n << 1) ^ 4129; } else { n = (n << 1); } } } return (~n) & 0xffff; } int validate_crc(struct flash_user_block *user) { return user->crc == crc_block(user); } inline int num_physical_blocks(u32 size) { return size / FLASH_BLOCK_SIZE; } inline int num_bitmap_blocks(u32 size) { return (int)std::ceil(size / (float)FLASH_BITMAP_BYTES); } inline int num_user_blocks(u32 size) { return num_physical_blocks(size) - num_bitmap_blocks(size) - 1; } inline int is_allocated(const u8 *bitmap, u32 phys_id) { int index = (phys_id - 1) % FLASH_BITMAP_BLOCKS; return (bitmap[index / 8] & (0x80 >> (index % 8))) == 0x0; } inline void set_allocated(u8 *bitmap, u32 phys_id) { int index = (phys_id - 1) % FLASH_BITMAP_BLOCKS; bitmap[index / 8] &= ~(0x80 >> (index % 8)); } void write_physical_block(u32 offset, u32 phys_id, const void *data) { memcpy(&this->data[offset + phys_id * FLASH_BLOCK_SIZE], data, FLASH_BLOCK_SIZE); } void read_physical_block(u32 offset, u32 phys_id, void *data) { memcpy(data, &this->data[offset + phys_id * FLASH_BLOCK_SIZE], FLASH_BLOCK_SIZE); } int validate_header(u32 offset, u32 part_id) { struct flash_header_block header; read_physical_block(offset, 0, &header); if (memcmp(header.magic, FLASH_MAGIC_COOKIE, sizeof(header.magic)) != 0) return 0; if (header.part_id != part_id) return 0; return 1; } int validate_header(u32 part_id) { int offset, size; GetPartitionInfo(part_id, &offset, &size); return validate_header(offset, part_id); } int alloc_block(u32 offset, u32 size); int lookup_block(u32 offset, u32 size, u32 block_id); void Serialize(Serializer& ser) const override { ser << state; ser.serialize(&this->data[write_protect_size], size - write_protect_size); } void Deserialize(Deserializer& deser) override { deser >> state; deser.deserialize(&this->data[write_protect_size], size - write_protect_size); } void erase_partition(u32 part_id) { int offset, size; GetPartitionInfo(part_id, &offset, &size); memset(data + offset, 0xFF, size); } void write_header(int part_id) { int offset, size; GetPartitionInfo(part_id, &offset, &size); struct flash_header_block header; memset(&header, 0xff, sizeof(header)); memcpy(header.magic, FLASH_MAGIC_COOKIE, sizeof(header.magic)); header.part_id = part_id; header.version = 0; write_physical_block(offset, 0, &header); } };