2013-12-19 17:10:14 +00:00
|
|
|
/*
|
2023-01-24 18:17:48 +00:00
|
|
|
This file is part of Flycast.
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2023-01-24 18:17:48 +00:00
|
|
|
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 <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2013-12-19 17:10:14 +00:00
|
|
|
#pragma once
|
|
|
|
#include "types.h"
|
2021-11-13 14:56:42 +00:00
|
|
|
#include "serialize.h"
|
2023-01-24 18:17:48 +00:00
|
|
|
#include <cmath>
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
struct MemChip
|
|
|
|
{
|
|
|
|
u8* data;
|
|
|
|
u32 size;
|
|
|
|
u32 mask;
|
2021-10-16 15:56:21 +00:00
|
|
|
|
|
|
|
protected:
|
2018-11-07 22:27:32 +00:00
|
|
|
u32 write_protect_size;
|
|
|
|
std::string load_filename;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-11-07 22:27:32 +00:00
|
|
|
MemChip(u32 size, u32 write_protect_size = 0)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2020-12-18 12:44:40 +00:00
|
|
|
this->data = new u8[size]();
|
|
|
|
this->size = size;
|
|
|
|
this->mask = size - 1; // must be power of 2
|
2018-11-07 22:27:32 +00:00
|
|
|
this->write_protect_size = write_protect_size;
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
2021-10-16 15:56:21 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
virtual ~MemChip()
|
|
|
|
{
|
|
|
|
delete[] data;
|
|
|
|
}
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2020-06-24 13:41:12 +00:00
|
|
|
virtual u8 Read8(u32 addr)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2019-07-31 15:59:53 +00:00
|
|
|
return data[addr & mask];
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
2023-01-24 18:17:48 +00:00
|
|
|
u32 Read(u32 addr, u32 sz)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2019-07-31 15:59:53 +00:00
|
|
|
addr &= mask;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-07-31 15:59:53 +00:00
|
|
|
u32 rv = 0;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-07-31 15:59:53 +00:00
|
|
|
for (u32 i = 0; i < sz; i++)
|
|
|
|
rv |= Read8(addr + i) << (i * 8);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2021-07-05 17:44:08 +00:00
|
|
|
bool Load(const std::string& file);
|
2021-10-16 15:56:21 +00:00
|
|
|
virtual bool Reload() { return true; }
|
2021-07-05 17:44:08 +00:00
|
|
|
bool Load(const std::string &prefix, const std::string &names_ro,
|
|
|
|
const std::string &title);
|
2023-03-27 14:57:48 +00:00
|
|
|
void Load(const u8 *data, size_t size);
|
2021-10-16 15:56:21 +00:00
|
|
|
void digest(u8 md5Digest[16]);
|
2020-11-26 15:45:57 +00:00
|
|
|
|
2019-05-19 19:34:25 +00:00
|
|
|
virtual void Reset() {}
|
2021-11-13 14:56:42 +00:00
|
|
|
virtual void Serialize(Serializer& ser) const { }
|
|
|
|
virtual void Deserialize(Deserializer& deser) { }
|
2013-12-19 17:10:14 +00:00
|
|
|
};
|
2019-07-09 21:52:19 +00:00
|
|
|
|
2021-10-16 15:56:21 +00:00
|
|
|
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
|
|
|
|
{
|
2023-03-27 14:57:48 +00:00
|
|
|
if (load_filename.empty())
|
|
|
|
return false;
|
2021-10-16 15:56:21 +00:00
|
|
|
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);
|
|
|
|
};
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
struct RomChip : MemChip
|
|
|
|
{
|
2021-10-16 15:56:21 +00:00
|
|
|
RomChip(u32 sz) : MemChip(sz) {}
|
2013-12-19 17:10:14 +00:00
|
|
|
};
|
2019-07-09 21:52:19 +00:00
|
|
|
|
2021-10-16 15:56:21 +00:00
|
|
|
struct SRamChip : WritableChip
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2021-10-16 15:56:21 +00:00
|
|
|
SRamChip(u32 sz, u32 write_protect_size = 0) : WritableChip(sz, write_protect_size) {}
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2023-01-24 18:17:48 +00:00
|
|
|
void Write(u32 addr, u32 val, u32 sz) override
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2023-01-24 18:17:48 +00:00
|
|
|
addr &= mask;
|
2018-11-07 22:27:32 +00:00
|
|
|
if (addr < write_protect_size)
|
|
|
|
return;
|
2013-12-19 17:10:14 +00:00
|
|
|
switch (sz)
|
|
|
|
{
|
|
|
|
case 1:
|
2023-01-24 18:17:48 +00:00
|
|
|
data[addr] = (u8)val;
|
2013-12-19 17:10:14 +00:00
|
|
|
return;
|
|
|
|
case 2:
|
2023-01-24 18:17:48 +00:00
|
|
|
*(u16 *)&data[addr] = (u16)val;
|
2013-12-19 17:10:14 +00:00
|
|
|
return;
|
|
|
|
case 4:
|
2023-01-24 18:17:48 +00:00
|
|
|
*(u32 *)&data[addr] = val;
|
2013-12-19 17:10:14 +00:00
|
|
|
return;
|
2018-11-07 22:27:32 +00:00
|
|
|
default:
|
|
|
|
die("invalid access size");
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
}
|
2019-07-09 21:52:19 +00:00
|
|
|
|
2021-11-13 14:56:42 +00:00
|
|
|
void Serialize(Serializer& ser) const override
|
2019-07-09 21:52:19 +00:00
|
|
|
{
|
2021-11-13 14:56:42 +00:00
|
|
|
ser.serialize(&this->data[write_protect_size], size - write_protect_size);
|
2019-07-09 21:52:19 +00:00
|
|
|
}
|
2021-11-13 14:56:42 +00:00
|
|
|
void Deserialize(Deserializer& deser) override
|
2019-07-09 21:52:19 +00:00
|
|
|
{
|
2021-11-13 14:56:42 +00:00
|
|
|
deser.deserialize(&this->data[write_protect_size], size - write_protect_size);
|
2019-07-09 21:52:19 +00:00
|
|
|
}
|
2013-12-19 17:10:14 +00:00
|
|
|
};
|
2018-11-07 22:27:32 +00:00
|
|
|
|
2018-12-23 11:59:16 +00:00
|
|
|
//
|
|
|
|
// 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
|
2019-08-14 18:05:42 +00:00
|
|
|
#define FLASH_USER_INET 0x80
|
|
|
|
#define FLASH_USER_ISP1 0xC0
|
|
|
|
#define FLASH_USER_ISP2 0xC6
|
2018-12-23 11:59:16 +00:00
|
|
|
|
|
|
|
struct flash_syscfg_block {
|
|
|
|
u16 block_id;
|
|
|
|
// last set time (seconds since 1/1/1950 00:00)
|
|
|
|
u16 time_lo;
|
|
|
|
u16 time_hi;
|
2022-05-21 13:46:22 +00:00
|
|
|
// in 15 mins increment, from -48 (West) to +52 (East), unused
|
|
|
|
int8_t time_zone;
|
2018-12-23 11:59:16 +00:00
|
|
|
u8 lang;
|
|
|
|
u8 mono;
|
|
|
|
u8 autostart;
|
|
|
|
u8 unknown2[4];
|
|
|
|
u8 reserved[50];
|
|
|
|
};
|
|
|
|
|
2019-08-14 18:05:42 +00:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2018-12-23 11:59:16 +00:00
|
|
|
// 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;
|
|
|
|
};
|
|
|
|
|
2018-11-07 22:27:32 +00:00
|
|
|
// Macronix 29LV160TMC
|
|
|
|
// AtomisWave uses a custom 29L001mc model
|
2021-10-16 15:56:21 +00:00
|
|
|
struct DCFlashChip : WritableChip
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2021-10-16 15:56:21 +00:00
|
|
|
DCFlashChip(u32 sz, u32 write_protect_size = 0): WritableChip(sz, write_protect_size), state(FS_Normal) { }
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
enum FlashState
|
|
|
|
{
|
2018-11-07 22:27:32 +00:00
|
|
|
FS_Normal,
|
|
|
|
FS_ReadAMDID1,
|
|
|
|
FS_ReadAMDID2,
|
|
|
|
FS_ByteProgram,
|
|
|
|
FS_EraseAMD1,
|
|
|
|
FS_EraseAMD2,
|
2020-06-24 13:41:12 +00:00
|
|
|
FS_EraseAMD3,
|
|
|
|
FS_SelectMode,
|
2013-12-19 17:10:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
FlashState state;
|
2021-03-13 11:44:59 +00:00
|
|
|
void Reset() override
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
//reset the flash chip state
|
2018-11-07 22:27:32 +00:00
|
|
|
state = FS_Normal;
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
2023-01-24 18:17:48 +00:00
|
|
|
void Write(u32 addr,u32 val,u32 sz) override;
|
2018-12-23 11:59:16 +00:00
|
|
|
|
2020-06-24 13:41:12 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-01-24 18:17:48 +00:00
|
|
|
int WriteBlock(u32 part_id, u32 block_id, const void *data);
|
|
|
|
int ReadBlock(u32 part_id, u32 block_id, void *data);
|
2018-12-23 11:59:16 +00:00
|
|
|
|
2019-07-30 17:04:51 +00:00
|
|
|
void GetPartitionInfo(int part_id, int *offset, int *size)
|
2018-12-23 11:59:16 +00:00
|
|
|
{
|
|
|
|
switch (part_id)
|
|
|
|
{
|
|
|
|
case FLASH_PT_FACTORY:
|
|
|
|
*offset = 0x1a000;
|
2023-07-01 10:47:02 +00:00
|
|
|
*size = 8_KB;
|
2018-12-23 11:59:16 +00:00
|
|
|
break;
|
|
|
|
case FLASH_PT_RESERVED:
|
|
|
|
*offset = 0x18000;
|
2023-07-01 10:47:02 +00:00
|
|
|
*size = 8_KB;
|
2018-12-23 11:59:16 +00:00
|
|
|
break;
|
|
|
|
case FLASH_PT_USER:
|
|
|
|
*offset = 0x1c000;
|
2023-07-01 10:47:02 +00:00
|
|
|
*size = 16_KB;
|
2018-12-23 11:59:16 +00:00
|
|
|
break;
|
|
|
|
case FLASH_PT_GAME:
|
|
|
|
*offset = 0x10000;
|
2023-07-01 10:47:02 +00:00
|
|
|
*size = 32_KB;
|
2018-12-23 11:59:16 +00:00
|
|
|
break;
|
|
|
|
case FLASH_PT_UNKNOWN:
|
|
|
|
*offset = 0x00000;
|
2023-07-01 10:47:02 +00:00
|
|
|
*size = 64_KB;
|
2018-12-23 11:59:16 +00:00
|
|
|
break;
|
|
|
|
default:
|
2021-07-20 17:21:11 +00:00
|
|
|
*offset = 0;
|
|
|
|
*size = 0;
|
2018-12-23 11:59:16 +00:00
|
|
|
die("unknown partition");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-24 18:17:48 +00:00
|
|
|
void Validate();
|
2019-07-31 15:59:53 +00:00
|
|
|
|
2019-07-30 17:04:51 +00:00
|
|
|
private:
|
2018-12-23 11:59:16 +00:00
|
|
|
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)
|
|
|
|
{
|
2023-01-24 18:17:48 +00:00
|
|
|
return (int)std::ceil(size / (float)FLASH_BITMAP_BYTES);
|
2018-12-23 11:59:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline int num_user_blocks(u32 size)
|
|
|
|
{
|
|
|
|
return num_physical_blocks(size) - num_bitmap_blocks(size) - 1;
|
|
|
|
}
|
|
|
|
|
2021-03-14 19:32:14 +00:00
|
|
|
inline int is_allocated(const u8 *bitmap, u32 phys_id)
|
2018-12-23 11:59:16 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-07-31 15:59:53 +00:00
|
|
|
int validate_header(u32 part_id)
|
|
|
|
{
|
|
|
|
int offset, size;
|
|
|
|
GetPartitionInfo(part_id, &offset, &size);
|
|
|
|
|
|
|
|
return validate_header(offset, part_id);
|
|
|
|
}
|
|
|
|
|
2023-01-24 18:17:48 +00:00
|
|
|
int alloc_block(u32 offset, u32 size);
|
|
|
|
int lookup_block(u32 offset, u32 size, u32 block_id);
|
2019-07-09 21:52:19 +00:00
|
|
|
|
2021-11-13 14:56:42 +00:00
|
|
|
void Serialize(Serializer& ser) const override
|
2019-07-09 21:52:19 +00:00
|
|
|
{
|
2021-11-13 14:56:42 +00:00
|
|
|
ser << state;
|
|
|
|
ser.serialize(&this->data[write_protect_size], size - write_protect_size);
|
2019-07-09 21:52:19 +00:00
|
|
|
}
|
|
|
|
|
2021-11-13 14:56:42 +00:00
|
|
|
void Deserialize(Deserializer& deser) override
|
2019-07-09 21:52:19 +00:00
|
|
|
{
|
2021-11-13 14:56:42 +00:00
|
|
|
deser >> state;
|
|
|
|
deser.deserialize(&this->data[write_protect_size], size - write_protect_size);
|
2019-07-09 21:52:19 +00:00
|
|
|
}
|
2019-07-31 15:59:53 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2013-12-19 17:10:14 +00:00
|
|
|
};
|