mirror of https://github.com/inolen/redream.git
flash rom command parsing
This commit is contained in:
parent
b74152f1e6
commit
74e34692eb
|
@ -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");
|
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");
|
FILE *fp = fopen(path, "rb");
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
LOG_WARNING("Failed to open boot at \"%s\"", path);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,26 +33,23 @@ static int boot_load(struct boot *boot, const char *path) {
|
||||||
fseek(fp, 0, SEEK_SET);
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
if (size != BIOS_SIZE) {
|
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);
|
fclose(fp);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int n = (int)fread(boot->rom, sizeof(uint8_t), size, fp);
|
int n = (int)fread(boot->rom, sizeof(uint8_t), size, fp);
|
||||||
|
CHECK_EQ(n, size);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
if (n != size) {
|
|
||||||
LOG_WARNING("BIOS read failed");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool boot_init(struct device *dev) {
|
static bool boot_init(struct device *dev) {
|
||||||
struct boot *boot = (struct boot *)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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,33 +2,38 @@
|
||||||
#include "hw/rom/flash.h"
|
#include "hw/rom/flash.h"
|
||||||
#include "core/option.h"
|
#include "core/option.h"
|
||||||
#include "hw/dreamcast.h"
|
#include "hw/dreamcast.h"
|
||||||
|
#include "sys/filesystem.h"
|
||||||
|
|
||||||
DEFINE_OPTION_STRING(flash, "dc_flash.bin", "Path to flash ROM");
|
DEFINE_OPTION_STRING(flash, "dc_flash.bin", "Path to flash ROM");
|
||||||
|
|
||||||
// there doesn't seem to exist any documentation on the actual flashrom used
|
// there doesn't seem to be any documentation on the flash rom used by thae
|
||||||
// by the dreamcast, however, there are several people who have replaced it
|
// dreamcast. however, several people have replaced it with the MX29LV160TMC-90
|
||||||
// with the MX29LV160 successfully
|
// 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 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 flash {
|
||||||
struct device;
|
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];
|
uint8_t rom[FLASH_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint32_t flash_rom_read(struct flash *flash, uint32_t addr,
|
static int flash_load_rom(struct flash *flash, const char *path) {
|
||||||
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) {
|
|
||||||
FILE *fp = fopen(path, "rb");
|
FILE *fp = fopen(path, "rb");
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
LOG_WARNING("Failed to open flash at \"%s\"", path);
|
|
||||||
return 0;
|
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);
|
int n = (int)fread(flash->rom, sizeof(uint8_t), size, fp);
|
||||||
|
CHECK_EQ(n, size);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
if (n != size) {
|
return 1;
|
||||||
LOG_WARNING("Flash read failed");
|
}
|
||||||
|
|
||||||
|
static int flash_save_rom(struct flash *flash, const char *path) {
|
||||||
|
FILE *fp = fopen(path, "wb");
|
||||||
|
if (!fp) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int n = (int)fwrite(flash->rom, 1, FLASH_SIZE, fp);
|
||||||
|
CHECK_EQ(n, FLASH_SIZE);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
return 1;
|
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) {
|
static bool flash_init(struct device *dev) {
|
||||||
struct flash *flash = (struct flash *)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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +180,7 @@ void flash_destroy(struct flash *flash) {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
AM_BEGIN(struct flash, flash_rom_map);
|
AM_BEGIN(struct flash, flash_rom_map);
|
||||||
AM_RANGE(0x00000000, 0x0001ffff) AM_HANDLE("flash rom",
|
AM_RANGE(0x00000000, 0x0001ffff) AM_HANDLE("flash rom",
|
||||||
(mmio_read_cb)&flash_rom_read,
|
(mmio_read_cb)&flash_read,
|
||||||
(mmio_write_cb)&flash_rom_write)
|
(mmio_write_cb)&flash_write)
|
||||||
AM_END();
|
AM_END();
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
|
@ -803,7 +803,7 @@ REG_R32(sh4_cb, PDTRA) {
|
||||||
// 3. When VREG1 = 1 and VREG0 = 1 are written in the AICA register,
|
// 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
|
// VIDEO1 = 0 and VIDEO0 = 0 are output. VIDEO0 is connected to the
|
||||||
// DVE-DACH pin, and handles switching between RGB and NTSC/PAL.
|
// DVE-DACH pin, and handles switching between RGB and NTSC/PAL.
|
||||||
// v |= 0x3 << 8;
|
v |= 0x3 << 8;
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue