flash rom command parsing

This commit is contained in:
Anthony Pesch 2016-10-09 19:37:17 -07:00
parent b74152f1e6
commit 74e34692eb
3 changed files with 130 additions and 30 deletions

View File

@ -22,10 +22,9 @@ static void boot_rom_write(struct boot *boot, uint32_t addr, uint32_t data,
LOG_FATAL("Can't write to boot rom");
}
static int boot_load(struct boot *boot, const char *path) {
static int boot_load_rom(struct boot *boot, const char *path) {
FILE *fp = fopen(path, "rb");
if (!fp) {
LOG_WARNING("Failed to open boot at \"%s\"", path);
return 0;
}
@ -34,26 +33,23 @@ static int boot_load(struct boot *boot, const char *path) {
fseek(fp, 0, SEEK_SET);
if (size != BIOS_SIZE) {
LOG_WARNING("BIOS size mismatch, is %d, expected %d", size, BIOS_SIZE);
LOG_WARNING("Boot rom size mismatch, is %d, expected %d", size, BIOS_SIZE);
fclose(fp);
return 0;
}
int n = (int)fread(boot->rom, sizeof(uint8_t), size, fp);
CHECK_EQ(n, size);
fclose(fp);
if (n != size) {
LOG_WARNING("BIOS read failed");
return 0;
}
return 1;
}
static bool boot_init(struct device *dev) {
struct boot *boot = (struct boot *)dev;
if (!boot_load(boot, OPTION_bios)) {
if (!boot_load_rom(boot, OPTION_bios)) {
LOG_WARNING("Failed to load boot rom");
return false;
}

View File

@ -2,33 +2,38 @@
#include "hw/rom/flash.h"
#include "core/option.h"
#include "hw/dreamcast.h"
#include "sys/filesystem.h"
DEFINE_OPTION_STRING(flash, "dc_flash.bin", "Path to flash ROM");
// there doesn't seem to exist any documentation on the actual flashrom used
// by the dreamcast, however, there are several people who have replaced it
// with the MX29LV160 successfully
// there doesn't seem to be any documentation on the flash rom used by thae
// dreamcast. however, several people have replaced it with the MX29LV160TMC-90
// successfully. the implementation of the command parsing here is based on its
// datasheet. note, the dreamcast seems to only use the word mode command
// sequences, so that is all that is implemented
#define FLASH_SIZE 0x00020000
#define SECTOR_SIZE 0x4000
#define CMD_NONE 0x0
#define CMD_ERASE 0x80
#define CMD_ERASE_CHIP 0x10
#define CMD_ERASE_SECTOR 0x30
#define CMD_PROGRAM 0xa0
struct flash {
struct device;
// path to persistent flash rom kept in the application directory
char app_path[PATH_MAX];
// cmd parsing state
int cmd;
int cmd_state;
// rom data
uint8_t rom[FLASH_SIZE];
};
static uint32_t flash_rom_read(struct flash *flash, uint32_t addr,
uint32_t data_mask) {
return READ_DATA(&flash->rom[addr]);
}
static void flash_rom_write(struct flash *flash, uint32_t addr, uint32_t data,
uint32_t data_mask) {
WRITE_DATA(&flash->rom[addr]);
}
static int flash_load(struct flash *flash, const char *path) {
static int flash_load_rom(struct flash *flash, const char *path) {
FILE *fp = fopen(path, "rb");
if (!fp) {
LOG_WARNING("Failed to open flash at \"%s\"", path);
return 0;
}
@ -43,20 +48,119 @@ static int flash_load(struct flash *flash, const char *path) {
}
int n = (int)fread(flash->rom, sizeof(uint8_t), size, fp);
CHECK_EQ(n, size);
fclose(fp);
if (n != size) {
LOG_WARNING("Flash read failed");
return 1;
}
static int flash_save_rom(struct flash *flash, const char *path) {
FILE *fp = fopen(path, "wb");
if (!fp) {
return 0;
}
int n = (int)fwrite(flash->rom, 1, FLASH_SIZE, fp);
CHECK_EQ(n, FLASH_SIZE);
fclose(fp);
return 1;
}
static uint32_t flash_cmd_read(struct flash *flash, uint32_t addr, uint32_t data_mask) {
uint32_t *mem = (uint32_t *)&flash->rom[addr];
return *mem & data_mask;
}
static void flash_cmd_program(struct flash *flash, uint32_t addr, uint32_t data,
uint32_t data_mask) {
uint32_t *mem = (uint32_t *)&flash->rom[addr];
*mem &= ~data_mask | data;
// update persistent copy of the flash rom
CHECK(flash_save_rom(flash, flash->app_path));
}
static void flash_cmd_erase_chip(struct flash *flash) {
memset(flash->rom, 0xff, FLASH_SIZE);
}
static void flash_cmd_erase_sector(struct flash *flash, uint32_t addr) {
addr &= ~(SECTOR_SIZE - 1);
memset(&flash->rom[addr], 0xff, SECTOR_SIZE);
}
static uint32_t flash_read(struct flash *flash, uint32_t addr,
uint32_t data_mask) {
CHECK_EQ(flash->cmd_state, 0);
return flash_cmd_read(flash, addr, data_mask);
}
static void flash_write(struct flash *flash, uint32_t addr, uint32_t data,
uint32_t data_mask) {
switch (flash->cmd_state) {
case 0: {
CHECK(addr == 0x5555 && data == 0xaa);
flash->cmd_state++;
} break;
case 1: {
CHECK(addr == 0x2aaa && data == 0x55);
flash->cmd_state++;
} break;
case 2: {
CHECK(addr == 0x5555 && (data == CMD_ERASE || data == CMD_PROGRAM));
flash->cmd = data;
flash->cmd_state++;
} break;
case 3: {
if (flash->cmd == CMD_PROGRAM) {
flash_cmd_program(flash, addr, data, data_mask);
flash->cmd_state = 0;
} else {
CHECK_EQ(flash->cmd, CMD_ERASE);
CHECK(addr == 0x5555 && data == 0xaa);
flash->cmd_state++;
}
} break;
case 4: {
CHECK(addr == 0x2aaa && data == 0x55);
flash->cmd_state++;
} break;
case 5: {
if (data == CMD_ERASE_CHIP) {
CHECK(addr == 0x5555);
flash_cmd_erase_chip(flash);
} else {
CHECK_EQ(data, CMD_ERASE_SECTOR);
flash_cmd_erase_sector(flash, addr);
}
flash->cmd_state = 0;
} break;
default:
LOG_FATAL("Unexpected flash command state %d", flash->cmd_state);
break;
}
}
static bool flash_init(struct device *dev) {
struct flash *flash = (struct flash *)dev;
if (!flash_load(flash, OPTION_flash)) {
// keep a persistent copy of the flash rom in the application directory
const char *appdir = fs_appdir();
snprintf(flash->app_path, sizeof(flash->app_path),
"%s" PATH_SEPARATOR "flash.bin", appdir);
// attempt to load flash rom from the application directory first, falling
// back to the command line path if it doesn't exist
if (!(flash_load_rom(flash, flash->app_path) ||
flash_load_rom(flash, OPTION_flash))) {
LOG_WARNING("Failed to load flash rom");
return false;
}
@ -76,7 +180,7 @@ void flash_destroy(struct flash *flash) {
// clang-format off
AM_BEGIN(struct flash, flash_rom_map);
AM_RANGE(0x00000000, 0x0001ffff) AM_HANDLE("flash rom",
(mmio_read_cb)&flash_rom_read,
(mmio_write_cb)&flash_rom_write)
(mmio_read_cb)&flash_read,
(mmio_write_cb)&flash_write)
AM_END();
// clang-format on

View File

@ -803,7 +803,7 @@ REG_R32(sh4_cb, PDTRA) {
// 3. When VREG1 = 1 and VREG0 = 1 are written in the AICA register,
// VIDEO1 = 0 and VIDEO0 = 0 are output. VIDEO0 is connected to the
// DVE-DACH pin, and handles switching between RGB and NTSC/PAL.
// v |= 0x3 << 8;
v |= 0x3 << 8;
return v;
}