a4092flash overhaul

- Implement reading and probing
- Show supported flash part names
This commit is contained in:
Stefan Reinauer 2025-07-13 00:18:14 -07:00
parent 6048c860e7
commit cdd811c8df
6 changed files with 265 additions and 68 deletions

View File

@ -49,6 +49,15 @@ struct Config* configure(int argc, char *argv[]) {
case 'W':
if (i+1 < argc) {
config->scsi_rom_filename = argv[i+1];
config->writeFlash = true;
i++;
}
break;
case 'R':
if (i+1 < argc) {
config->scsi_rom_filename = argv[i+1];
config->readFlash = true;
i++;
}
break;
@ -57,10 +66,14 @@ struct Config* configure(int argc, char *argv[]) {
config->eraseFlash = true;
break;
case 'R':
case 'B':
config->rebootRequired = true;
break;
case 'P':
config->probeFlash = true;
break;
case 'Y':
config->assumeYes = true;
break;
@ -69,7 +82,13 @@ struct Config* configure(int argc, char *argv[]) {
}
}
if (config->scsi_rom_filename == NULL && config->eraseFlash == false) {
if (config->readFlash == false && config->writeFlash == false &&
config->eraseFlash == false && config->probeFlash == false) {
printf("You need to specify one of -E, -R, -W, -P.\n");
error = true;
}
if (config->readFlash == true && config->writeFlash == true) {
printf("a4092flash: -R, -W are mutually exclusive.\n");
error = true;
}
@ -84,9 +103,12 @@ struct Config* configure(int argc, char *argv[]) {
/** usage
* @brief Print the usage information
*/
void usage() {
printf("\nUsage: a4092flash [-I <a4092.rom>]\n\n");
printf(" -W <a4092.rom> - Flash A4092 ROM.\n");
void usage(void) {
printf("\nUsage: a4092flash [-Y] { -R <a4092.rom> | -W <a4092.rom> | -E | -P }\n\n");
printf(" -Y assume YES as answer to all questions\n");
printf(" -R <a4092.rom> - Read A4092 ROM to file\n");
printf(" -W <a4092.rom> - Flash A4092 ROM from file\n");
printf(" -E Erase flash.\n");
printf(" -R reboot.\n");
printf(" -P Probe flash.\n");
printf(" -B Reboot.\n");
}

View File

@ -20,6 +20,9 @@
struct Config {
char *scsi_rom_filename;
bool readFlash;
bool writeFlash;
bool probeFlash;
bool eraseFlash;
bool rebootRequired;
bool assumeYes;

View File

@ -16,6 +16,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <proto/expansion.h>
#include <exec/types.h>
#include <stdbool.h>
@ -23,7 +24,7 @@
#include "flash.h"
#include "flash_constants.h"
volatile ULONG flashbase;
static ULONG flashbase;
/**
* @brief Reads a single byte from the flash memory at the given byte address,
@ -36,7 +37,8 @@ volatile ULONG flashbase;
* @param byte_address The byte address (0x000000 - 0x7FFFFF) to read from.
* @return The UBYTE value read from the flash.
*/
static UBYTE flash_read_byte(ULONG byte_address) {
static UBYTE flash_read_byte(ULONG byte_address)
{
// Calculate the physical 32-bit word address in memory.
// Each byte address maps to a unique 32-bit word in the underlying memory.
volatile ULONG *word_ptr = (volatile ULONG *)(flashbase + (byte_address * 4));
@ -67,7 +69,8 @@ static UBYTE flash_read_byte(ULONG byte_address) {
* @param byte_address The byte address (0x000000 - 0x7FFFFF) to write to.
* @param data The UBYTE value to write to the flash.
*/
static void flash_write_byte(ULONG byte_address, UBYTE data) {
static void flash_write_byte(ULONG byte_address, UBYTE data)
{
// Calculate the physical 32-bit word address in memory.
// Each byte address maps to a unique 32-bit word in the underlying memory.
volatile ULONG *word_ptr = (volatile ULONG *)(flashbase + (byte_address * 4));
@ -91,18 +94,25 @@ static void flash_write_byte(ULONG byte_address, UBYTE data) {
static inline void flash_command(UBYTE);
static inline void flash_poll(ULONG);
static const LONG devices_supported[] = {
0x1F03, // AT49BV512
0xBFB5, // SST39SF010
0xBFB6, // SST39SF020
0xBFB7, // SST39SF040
0xBFD5, // SST39(L/V)F010
0xBFD6, // SST39(L/V)F020
0xBFD7, // SST39(L/V)F040
0xC2A4, // MX29F040C
0x0120, // AM29F010
0x01A4, // AM29F040
0
struct flashchips {
UWORD id;
UWORD size;
const char *vendor;
const char *device;
};
static const struct flashchips devices_supported[] = {
{ 0x0120, 128, "AMD", "AM29F010" },
{ 0x01A4, 512, "AMD", "AM29F040" },
{ 0x1F03, 64, "Atmel", "AT49BV512" },
{ 0xBFB5, 128, "SST", "SST39SF010" },
{ 0xBFB6, 256, "SST", "SST39SF020" },
{ 0xBFB7, 512, "SST", "SST39SF040" },
{ 0xBFD5, 128, "SST", "SST39(L/V)F010" },
{ 0xBFD6, 256, "SST", "SST39(L/V)F020" },
{ 0xBFD7, 512, "SST", "SST39(L/V)F040" },
{ 0xC2A4, 512, "Macronix", "MX29F040C" },
{ 0x0000, 0, NULL, NULL }
};
/** flash_is_supported
@ -112,13 +122,22 @@ static const LONG devices_supported[] = {
* @param device the device id
* @returns boolean result
*/
static bool flash_is_supported(UBYTE manufacturer, UBYTE device) {
static bool flash_is_supported(UBYTE manufacturer, UBYTE device, UWORD *size)
{
ULONG deviceId = (manufacturer << 8) | device;
int i = 0;
while (devices_supported[i] != 0) {
if (devices_supported[i] == deviceId)
if (size) *size = 0;
while (devices_supported[i].id != 0) {
if (devices_supported[i].id == deviceId) {
printf("Flash part: %s %s (%d KB)\n",
devices_supported[i].vendor,
devices_supported[i].device,
devices_supported[i].size);
*size = devices_supported[i].size;
return true;
}
i++;
}
@ -133,7 +152,8 @@ static bool flash_is_supported(UBYTE manufacturer, UBYTE device) {
* @param device the device id
* @returns UWORD sector size in bytes
*/
static UWORD flash_get_sectorSize(UBYTE manufacturer, UBYTE device) {
static UWORD flash_get_sectorSize(UBYTE manufacturer, UBYTE device)
{
ULONG deviceId = (manufacturer << 8) | device;
UWORD sectorSize;
@ -164,7 +184,8 @@ static UWORD flash_get_sectorSize(UBYTE manufacturer, UBYTE device) {
* @param address Address to read from
* @return The data that was read
*/
UBYTE flash_readByte(ULONG address) {
UBYTE flash_readByte(ULONG address)
{
// Mask address to ensure it is within the valid flash size.
address &= (FLASH_SIZE - 1);
@ -179,7 +200,8 @@ UBYTE flash_readByte(ULONG address) {
* @param address Address to write to
* @param data The data to be written
*/
void flash_writeByte(ULONG address, UBYTE data) {
void flash_writeByte(ULONG address, UBYTE data)
{
// Mask address to ensure it is within the valid flash size.
address &= (FLASH_SIZE - 1);
@ -195,7 +217,8 @@ void flash_writeByte(ULONG address, UBYTE data) {
* @brief send a command to the Flash
* @param command The command byte to send.
*/
static inline void flash_command(UBYTE command) {
static inline void flash_command(UBYTE command)
{
// Write command byte to the specific command address
flash_write_byte(ADDR_CMD_STEP_1, command);
return;
@ -205,7 +228,8 @@ static inline void flash_command(UBYTE command) {
*
* @brief Send the SDP command sequence
*/
void flash_unlock_sdp() {
void flash_unlock_sdp(void)
{
// Write the sequence bytes to the specific addresses
flash_write_byte(ADDR_CMD_STEP_1, CMD_SDP_STEP_1);
flash_write_byte(ADDR_CMD_STEP_2, CMD_SDP_STEP_2);
@ -216,7 +240,8 @@ void flash_unlock_sdp() {
*
* @brief Perform a chip erase.
*/
void flash_erase_chip() {
void flash_erase_chip(void)
{
flash_unlock_sdp();
flash_command(CMD_ERASE);
flash_unlock_sdp();
@ -230,7 +255,8 @@ void flash_erase_chip() {
* Erase the currently selected 32KB bank
*
*/
void flash_erase_bank(UWORD sectorSize) {
void flash_erase_bank(UWORD sectorSize)
{
if (sectorSize > 0) {
int count = 32768 / sectorSize;
for (int i = 0; i < count; i++) {
@ -245,7 +271,8 @@ void flash_erase_bank(UWORD sectorSize) {
* @param address Address of sector to erase
*
*/
void flash_erase_sector(ULONG address) {
void flash_erase_sector(ULONG address)
{
// Mask address to ensure it is within the valid flash size.
address &= (FLASH_SIZE - 1);
@ -262,7 +289,8 @@ void flash_erase_sector(ULONG address) {
* @brief Poll the status bits at address, until they indicate that the operation has completed.
* @param address Address to poll
*/
static inline void flash_poll(ULONG address) {
static inline void flash_poll(ULONG address)
{
// Mask address to ensure it is within the valid flash size.
address &= (FLASH_SIZE - 1);
@ -283,11 +311,15 @@ static inline void flash_poll(ULONG address) {
* @param flashbase Pointer to the Flash base address
* @return True if the manufacturer ID matches the expected value and flashbase is valid.
*/
bool flash_init(UBYTE *manuf, UBYTE *devid, ULONG *base, UWORD *sectorSize) {
bool flash_init(UBYTE *manuf, UBYTE *devid, volatile UBYTE *base, ULONG *size, UWORD *sectorSize)
{
bool ret = false;
UBYTE manufId;
UBYTE deviceId;
if (size) *size = 0;
if (sectorSize) *sectorSize = 0;
// Set the global flashbase pointer.
flashbase = (ULONG)base;
@ -306,12 +338,14 @@ bool flash_init(UBYTE *manuf, UBYTE *devid, ULONG *base, UWORD *sectorSize) {
if (devid) *devid = deviceId;
// Check if the device is supported and flashbase is valid.
if (flash_is_supported(manufId, deviceId) && flashbase) {
UWORD flash_size;
if (flash_is_supported(manufId, deviceId, &flash_size) && flashbase) {
// Update size if pointer is valid.
if (size) *size = flash_size * 1024;
// Update the sector size if pointer is valid.
if (sectorSize) *sectorSize = flash_get_sectorSize(manufId, deviceId);
ret = true;
}
// Update the sector size if pointer is valid.
if (sectorSize) *sectorSize = flash_get_sectorSize(manufId, deviceId);
return (ret);
}

View File

@ -22,12 +22,12 @@
#include <exec/types.h>
#include <stdbool.h>
void flash_unlock_sdp();
void flash_erase_chip();
UBYTE flash_readByte(ULONG);
void flash_writeByte(ULONG, UBYTE);
bool flash_init(UBYTE *, UBYTE *, ULONG *, UWORD *);
void flash_erase_sector(ULONG);
void flash_erase_bank(UWORD);
void flash_unlock_sdp(void);
void flash_erase_chip(void);
UBYTE flash_readByte(ULONG address);
void flash_writeByte(ULONG address, UBYTE data);
bool flash_init(UBYTE *manuf, UBYTE *devid, volatile UBYTE *base, ULONG *size, UWORD *sectorSize);
void flash_erase_sector(ULONG address);
void flash_erase_bank(UWORD sectorSize);
#endif

View File

@ -31,7 +31,8 @@
#include "main.h"
#include "config.h"
#define MANUF_ID_COMMODORE 513
#define MANUF_ID_COMMODORE_BRAUNSCHWEIG 513
#define MANUF_ID_COMMODORE 514
#define PROD_ID_A4092 84
@ -49,7 +50,8 @@ bool devsInhibited = false;
* Kickstart V36 (2.0+) and up contain a function for this
* But for 1.3 we will need to provide our own function
*/
static void _ColdReboot() {
static void _ColdReboot(void)
{
// Copied from coldboot.asm
// http://amigadev.elowar.com/read/ADCD_2.1/Hardware_Manual_guide/node02E3.html
asm("move.l 4,a6 \n\t" // SysBase
@ -73,7 +75,8 @@ static void _ColdReboot() {
*
* @param inhibit (bool) True: inhibit, False: uninhibit
*/
bool inhibitDosDevs(bool inhibit) {
bool inhibitDosDevs(bool inhibit)
{
bool success = true;
struct MsgPort *mp = CreatePort(NULL,0);
struct Message msg;
@ -180,8 +183,9 @@ bool inhibitDosDevs(bool inhibit) {
* Configure the board struct for an A4092 Board
* @param board pointer to the board struct
*/
static void setup_a4092_board(struct scsiBoard *board) {
board->flashbase = board->cd->cd_BoardAddr;
static void setup_a4092_board(struct scsiBoard *board)
{
board->flashbase = (volatile UBYTE *)board->cd->cd_BoardAddr;
}
/**
@ -191,7 +195,8 @@ static void setup_a4092_board(struct scsiBoard *board) {
* @param config pointer to the config struct
* @return boolean true / false
*/
static bool promptUser(struct Config *config) {
static bool promptUser(struct Config *config)
{
int c;
char answer = 'y'; // Default to yes
@ -214,6 +219,8 @@ static bool promptUser(struct Config *config) {
return (answer == 'y');
}
static BOOL probeFlash(ULONG romSize);
int main(int argc, char *argv[])
{
SysBase = *((struct ExecBase **)4UL);
@ -230,13 +237,13 @@ int main(int argc, char *argv[])
return(rc);
}
printf("\n==== A4092 Flash Updater ====\n");
printf("\n%s\n\n", VERSION);
struct Task *task = FindTask(0);
SetTaskPri(task,20);
if ((config = configure(argc,argv)) != NULL) {
if (config->scsi_rom_filename) {
if (config->writeFlash && config->scsi_rom_filename) {
romSize = getFileSize(config->scsi_rom_filename);
if (romSize == 0) {
rc = 5;
@ -289,6 +296,7 @@ int main(int argc, char *argv[])
switch (cd->cd_Rom.er_Manufacturer) {
case MANUF_ID_COMMODORE:
case MANUF_ID_COMMODORE_BRAUNSCHWEIG:
if (cd->cd_Rom.er_Product == PROD_ID_A4092) {
printf("Found A4091 / A4092");
setup_a4092_board(&board);
@ -305,34 +313,53 @@ int main(int argc, char *argv[])
boards_found++;
// Ask the user if they wish to update this board
if (!promptUser(config)) continue;
if ((config->writeFlash || config->eraseFlash) && !promptUser(config)) continue;
UBYTE manufId,devId;
UWORD sectorSize;
ULONG flashSize;
if (flash_init(&manufId,&devId,board.flashbase,&sectorSize)) {
if (flash_init(&manufId,&devId,board.flashbase,&flashSize,&sectorSize)) {
if (config->eraseFlash) {
printf("Erasing flash.\n");
printf("Erasing whole flash.\n");
flash_erase_chip();
}
if (config->scsi_rom_filename) {
if (config->writeFlash && config->scsi_rom_filename) {
if (config->eraseFlash == false) {
if (sectorSize > 0) {
printf("Erasing A4092 bank...\n");
printf("Erasing flash bank.\n");
flash_erase_bank(sectorSize);
} else {
printf("Erasing A4092 flash...\n");
printf("Erasing whole flash.\n");
flash_erase_chip();
}
}
printf("Writing A4092 ROM.\n");
writeBufToFlash(&board,driver_buffer,board.flashbase,romSize);
printf("Writing A4092 ROM image to flash memory.\n");
writeBufToFlash(&board, driver_buffer, board.flashbase, romSize);
printf("\n");
}
if (config->readFlash && config->scsi_rom_filename && flashSize) {
printf("Writing A4092 flash memory to file.\n");
if (writeFlashToFile(config->scsi_rom_filename,flashSize) == false) {
rc = 5;
goto exit;
}
}
if (config->probeFlash && flashSize) {
if (probeFlash(flashSize) == false) {
rc = 5;
goto exit;
}
}
} else {
printf("Error: A4092 - Unknown Flash device Manufacturer: %02X Device: %02X\n", manufId, devId);
if (manufId == 0x9f && devId == 0xaf)
printf("This is likely an A4091. Only A4092 has flash memory.\n");
else
printf("Error: A4092 - Unknown Flash device Manufacturer: %02X Device: %02X\n", manufId, devId);
rc = 5;
}
}
@ -377,7 +404,8 @@ exit:
* @param filename file to check the size of
* @returns File size in bytes
*/
ULONG getFileSize(char *filename) {
ULONG getFileSize(char *filename)
{
BPTR fileLock;
ULONG fileSize = 0;
struct FileInfoBlock *FIB;
@ -408,7 +436,8 @@ ULONG getFileSize(char *filename) {
* @param filename Name of the file to open
* @return true on success
*/
BOOL readFileToBuf(char *filename, void *buffer) {
BOOL readFileToBuf(char *filename, void *buffer)
{
ULONG romSize = getFileSize(filename);
BOOL ret = true;
@ -434,6 +463,52 @@ BOOL readFileToBuf(char *filename, void *buffer) {
return ret;
}
/**
* writeFlashToFile()
*
* Write the Flash content to the specified file
*
* @param filename file to write
* @param size number of bytes to write
* @returns true on success
*/
BOOL writeFlashToFile(char *filename, ULONG romSize)
{
BOOL ret = true;
char * buffer;
if (romSize == 0) return false;
fprintf (stdout, "Flash size: %d KB\n", romSize / 1024);
buffer = AllocMem(romSize, 0);
BPTR fh;
if (buffer) {
fprintf(stdout, "Reading Flash...\n");
int i;
for (i=0; i<romSize; i++) {
buffer[i] = flash_readByte(i);
}
fprintf(stdout, "Writing File %s...\n", filename);
fh = Open(filename,MODE_NEWFILE);
if (fh) {
Write(fh,buffer,romSize);
Close(fh);
FreeMem(buffer, romSize);
} else {
printf("Error opening %s\n",filename);
FreeMem(buffer, romSize);
return false;
}
} else {
return false;
}
return ret;
}
/**
* writeBufToFlash()
*
@ -444,7 +519,8 @@ BOOL readFileToBuf(char *filename, void *buffer) {
* @param size number of bytes to write
* @returns true on success
*/
BOOL writeBufToFlash(struct scsiBoard *board, UBYTE *source, UBYTE *dest, ULONG size) {
BOOL writeBufToFlash(struct scsiBoard *board, UBYTE *source, volatile UBYTE *dest, ULONG size)
{
UBYTE *sourcePtr = NULL;
UBYTE destVal = 0;
@ -492,3 +568,63 @@ BOOL writeBufToFlash(struct scsiBoard *board, UBYTE *source, UBYTE *dest, ULONG
fflush(stdout);
return true;
}
static int find_a409x_version(const unsigned char *buffer, ULONG romSize) {
const char *needle = "A4091 scsidisk";
size_t needle_len = strlen(needle);
// We can only search up to (romSize - needle_len)
for (size_t i = 0; i <= romSize - needle_len; i++) {
if (memcmp(buffer + i, needle, needle_len) == 0) {
return (int)i;
}
}
return -1; // Not found
}
/**
* probeFlash()
*
* Analyze flash content
*
* @param size number of bytes to write
* @returns true on success
*/
static BOOL probeFlash(ULONG romSize)
{
BOOL ret = true;
char * buffer;
if (romSize == 0) return false;
buffer = AllocMem(romSize, 0);
if (buffer) {
fprintf(stdout, "Reading Flash...\n");
int i;
for (i=0; i<romSize; i++) {
buffer[i] = flash_readByte(i);
}
ULONG magic1,magic2;
memcpy (&magic1, buffer+0x10000-8, 4);
memcpy (&magic2, buffer+0x10000-4, 4);
if (magic1 == 0xFFFF5352 && magic2 == 0x2F434448) {
printf("Found 64KB A4091/A4092 image.\n");
} else {
printf("Not a standard image.\n");
}
int offset = find_a409x_version(buffer, romSize);
if (offset > 0) {
printf("Version: %s\n", buffer + offset);
} else {
printf("Could not determine version.\n");
}
FreeMem(buffer, romSize);
} else {
ret=false;
}
return ret;
}

View File

@ -24,6 +24,7 @@
#define XSTR(s) STR(s) /* Turn s into a string literal after macro-expanding it. */
#define VERSION_STRING "$VER: a4092flash " XSTR(DEVICE_VERSION) "." XSTR(DEVICE_REVISION) " (" XSTR(BUILD_DATE) ") " XSTR(GIT_REF)
#define VERSION "a4092flash " XSTR(DEVICE_VERSION) "." XSTR(DEVICE_REVISION) " (" XSTR(BUILD_DATE) ") " XSTR(GIT_REF)
#undef FindConfigDev
// NDK 1.3 definition of FindConfigDev is incorrect which causes "makes pointer from integer without a cast" warning
@ -31,7 +32,7 @@ struct ConfigDev* FindConfigDev(struct ConfigDev*, LONG, LONG);
// NDK 1.3 includes lacks these, so define them here
#ifdef __KICK13__
void ColdReboot();
void ColdReboot(void);
struct DosList *LockDosList(ULONG);
struct DosList *NextDosEntry(struct DosList *, ULONG);
void *UnLockDosList(ULONG);
@ -39,7 +40,7 @@ void *UnLockDosList(ULONG);
struct scsiBoard {
struct ConfigDev *cd;
void * volatile flashbase;
volatile UBYTE *flashbase;
};
struct dosDev {
@ -50,6 +51,7 @@ struct dosDev {
ULONG getFileSize(char *);
BOOL readFileToBuf(char *, void *);
BOOL writeBufToFlash(struct scsiBoard *board, UBYTE *source, UBYTE *dest, ULONG size);
BOOL writeFlashToFile(char *filename, ULONG romSize);
BOOL writeBufToFlash(struct scsiBoard *board, UBYTE *source, volatile UBYTE *dest, ULONG size);
#endif