summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/intelfsh.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/machine/intelfsh.c')
-rw-r--r--src/emu/machine/intelfsh.c493
1 files changed, 493 insertions, 0 deletions
diff --git a/src/emu/machine/intelfsh.c b/src/emu/machine/intelfsh.c
new file mode 100644
index 00000000000..54b894cb5d7
--- /dev/null
+++ b/src/emu/machine/intelfsh.c
@@ -0,0 +1,493 @@
+/*
+ Flash ROM emulation
+
+ Explicitly supports:
+ Intel 28F016S5 (byte-wide)
+ AMD/Fujitsu 29F016 (byte-wide)
+ Sharp LH28F400 (word-wide)
+
+ Flash ROMs use a standardized command set accross manufacturers,
+ so this emulation should work even for non-Intel and non-Sharp chips
+ as long as the game doesn't query the maker ID.
+*/
+
+#include "driver.h"
+#include "intelfsh.h"
+
+enum
+{
+ FM_NORMAL, // normal read/write
+ FM_READID, // read ID
+ FM_READSTATUS, // read status
+ FM_WRITEPART1, // first half of programming, awaiting second
+ FM_CLEARPART1, // first half of clear, awaiting second
+ FM_SETMASTER, // first half of set master lock, awaiting on/off
+ FM_READAMDID1, // part 1 of alt ID sequence
+ FM_READAMDID2, // part 2 of alt ID sequence
+ FM_READAMDID3, // part 3 of alt ID sequence
+ FM_ERASEAMD1, // part 1 of AMD erase sequence
+ FM_ERASEAMD2, // part 2 of AMD erase sequence
+ FM_ERASEAMD3, // part 3 of AMD erase sequence
+ FM_ERASEAMD4, // part 4 of AMD erase sequence
+ FM_BYTEPROGRAM,
+};
+
+struct flash_chip
+{
+ int type;
+ int size;
+ int bits;
+ int status;
+ INT32 flash_mode;
+ INT32 flash_master_lock;
+ int device_id;
+ int maker_id;
+ emu_timer *timer;
+ void *flash_memory;
+};
+
+static struct flash_chip chips[FLASH_CHIPS_MAX];
+
+static TIMER_CALLBACK_PTR( erase_finished )
+{
+ struct flash_chip *c = param;
+
+ switch( c->flash_mode )
+ {
+ case FM_READSTATUS:
+ c->status = 0x80;
+ break;
+
+ case FM_ERASEAMD4:
+ c->flash_mode = FM_NORMAL;
+ break;
+ }
+}
+
+void* intelflash_getmemptr(int chip)
+{
+ struct flash_chip *c;
+ if( chip >= FLASH_CHIPS_MAX )
+ {
+ logerror( "intelflash_init: invalid chip %d\n", chip );
+ return 0;
+ }
+ c = &chips[ chip ];
+
+ return c->flash_memory;
+}
+
+void intelflash_init(int chip, int type, void *data)
+{
+ struct flash_chip *c;
+ if( chip >= FLASH_CHIPS_MAX )
+ {
+ logerror( "intelflash_init: invalid chip %d\n", chip );
+ return;
+ }
+ c = &chips[ chip ];
+
+ c->type = type;
+ switch( c->type )
+ {
+ case FLASH_INTEL_28F016S5:
+ case FLASH_SHARP_LH28F016S:
+ c->bits = 8;
+ c->size = 0x200000;
+ c->maker_id = 0x89;
+ c->device_id = 0xaa;
+ break;
+ case FLASH_SHARP_LH28F400:
+ c->bits = 16;
+ c->size = 0x80000;
+ c->maker_id = 0xb0;
+ c->device_id = 0xed;
+ break;
+ case FLASH_FUJITSU_29F016A:
+ c->bits = 8;
+ c->size = 0x200000;
+ c->maker_id = 0x04;
+ c->device_id = 0xad;
+ break;
+ case FLASH_INTEL_E28F008SA:
+ c->bits = 8;
+ c->size = 0x100000;
+ c->maker_id = 0x89;
+ c->device_id = 0xa2;
+ break;
+ case FLASH_INTEL_TE28F160:
+ c->bits = 16;
+ c->size = 0x200000;
+ c->maker_id = 0xb0;
+ c->device_id = 0xd0;
+ break;
+ }
+ if( data == NULL )
+ {
+ data = auto_malloc( c->size );
+ memset( data, 0xff, c->size );
+ }
+
+ c->status = 0x80;
+ c->flash_mode = FM_NORMAL;
+ c->flash_master_lock = 0;
+ c->timer = timer_alloc_ptr(erase_finished, c);
+ c->flash_memory = data;
+
+ state_save_register_item( "intelfsh", chip, c->status );
+ state_save_register_item( "intelfsh", chip, c->flash_mode );
+ state_save_register_item( "intelfsh", chip, c->flash_master_lock );
+ state_save_register_memory( "intelfsh", chip, "flash_memory", c->flash_memory, c->bits/8, c->size / (c->bits/8) );
+}
+
+UINT32 intelflash_read(int chip, UINT32 address)
+{
+ UINT32 data = 0;
+ struct flash_chip *c;
+ if( chip >= FLASH_CHIPS_MAX )
+ {
+ logerror( "intelflash_read: invalid chip %d\n", chip );
+ return 0;
+ }
+ c = &chips[ chip ];
+
+ switch( c->flash_mode )
+ {
+ default:
+ case FM_NORMAL:
+ switch( c->bits )
+ {
+ case 8:
+ {
+ UINT8 *flash_memory = c->flash_memory;
+ data = flash_memory[ address ];
+ }
+ break;
+ case 16:
+ {
+ UINT16 *flash_memory = c->flash_memory;
+ data = flash_memory[ address ];
+ }
+ break;
+ }
+ break;
+ case FM_READSTATUS:
+ data = c->status;
+ break;
+ case FM_READAMDID3:
+ switch (address)
+ {
+ case 0: data = c->maker_id; break;
+ case 1: data = c->device_id; break;
+ case 2: data = 0; break;
+ }
+ break;
+ case FM_READID:
+ switch (address)
+ {
+ case 0: // maker ID
+ data = c->maker_id;
+ break;
+ case 1: // chip ID
+ data = c->device_id;
+ break;
+ case 2: // block lock config
+ data = 0; // we don't support this yet
+ break;
+ case 3: // master lock config
+ if (c->flash_master_lock)
+ {
+ data = 1;
+ }
+ else
+ {
+ data = 0;
+ }
+ break;
+ }
+ break;
+ case FM_ERASEAMD4:
+ c->status ^= ( 1 << 6 ) | ( 1 << 2 );
+ data = c->status;
+ break;
+ }
+
+// logerror( "%08x: intelflash_read( %d, %08x ) %08x\n", activecpu_get_pc(), chip, address, data );
+
+ return data;
+}
+
+void intelflash_write(int chip, UINT32 address, UINT32 data)
+{
+ struct flash_chip *c;
+ if( chip >= FLASH_CHIPS_MAX )
+ {
+ logerror( "intelflash_write: invalid chip %d\n", chip );
+ return;
+ }
+ c = &chips[ chip ];
+
+// logerror( "%08x: intelflash_write( %d, %08x, %08x )\n", activecpu_get_pc(), chip, address, data );
+
+ switch( c->flash_mode )
+ {
+ case FM_NORMAL:
+ case FM_READSTATUS:
+ case FM_READID:
+ case FM_READAMDID3:
+ switch( data & 0xff )
+ {
+ case 0xf0:
+ case 0xff: // reset chip mode
+ c->flash_mode = FM_NORMAL;
+ break;
+ case 0x90: // read ID
+ c->flash_mode = FM_READID;
+ break;
+ case 0x40:
+ case 0x10: // program
+ c->flash_mode = FM_WRITEPART1;
+ break;
+ case 0x50: // clear status reg
+ c->status = 0x80;
+ c->flash_mode = FM_READSTATUS;
+ break;
+ case 0x20: // block erase
+ c->flash_mode = FM_CLEARPART1;
+ break;
+ case 0x60: // set master lock
+ c->flash_mode = FM_SETMASTER;
+ break;
+ case 0x70: // read status
+ c->flash_mode = FM_READSTATUS;
+ break;
+ case 0xaa: // AMD ID select part 1
+ if( ( address & 0xffff ) == 0x555 )
+ {
+ c->flash_mode = FM_READAMDID1;
+ }
+ break;
+ default:
+ logerror( "Unknown flash mode byte %x\n", data & 0xff );
+ break;
+ }
+ break;
+ case FM_READAMDID1:
+ if( ( address & 0xffff ) == 0x2aa && ( data & 0xff ) == 0x55 )
+ {
+ c->flash_mode = FM_READAMDID2;
+ }
+ else
+ {
+ logerror( "unexpected %08x=%02x in FM_READAMDID1\n", address, data & 0xff );
+ c->flash_mode = FM_NORMAL;
+ }
+ break;
+ case FM_READAMDID2:
+ if( ( address & 0xffff ) == 0x555 && ( data & 0xff ) == 0x90 )
+ {
+ c->flash_mode = FM_READAMDID3;
+ }
+ else if( ( address & 0xffff ) == 0x555 && ( data & 0xff ) == 0x80 )
+ {
+ c->flash_mode = FM_ERASEAMD1;
+ }
+ else if( ( address & 0xffff ) == 0x555 && ( data & 0xff ) == 0xa0 )
+ {
+ c->flash_mode = FM_BYTEPROGRAM;
+ }
+ else if( ( address & 0xffff ) == 0x555 && ( data & 0xff ) == 0xf0 )
+ {
+ c->flash_mode = FM_NORMAL;
+ }
+ else
+ {
+ logerror( "unexpected %08x=%02x in FM_READAMDID2\n", address, data & 0xff );
+ c->flash_mode = FM_NORMAL;
+ }
+ break;
+ case FM_ERASEAMD1:
+ if( ( address & 0xffff ) == 0x555 && ( data & 0xff ) == 0xaa )
+ {
+ c->flash_mode = FM_ERASEAMD2;
+ }
+ else
+ {
+ logerror( "unexpected %08x=%02x in FM_ERASEAMD1\n", address, data & 0xff );
+ }
+ break;
+ case FM_ERASEAMD2:
+ if( ( address & 0xffff ) == 0x2aa && ( data & 0xff ) == 0x55 )
+ {
+ c->flash_mode = FM_ERASEAMD3;
+ }
+ else
+ {
+ logerror( "unexpected %08x=%02x in FM_ERASEAMD2\n", address, data & 0xff );
+ }
+ break;
+ case FM_ERASEAMD3:
+ if( ( address & 0xffff ) == 0x555 && ( data & 0xff ) == 0x10 )
+ {
+ // chip erase
+ memset( c->flash_memory, 0xff, c->size);
+
+ c->status = 1 << 3;
+ c->flash_mode = FM_ERASEAMD4;
+
+ timer_adjust_ptr( c->timer, ATTOTIME_IN_SEC( 17 ), attotime_zero );
+ }
+ else if( ( data & 0xff ) == 0x30 )
+ {
+ // sector erase
+ // clear the 64k block containing the current address to all 0xffs
+ switch( c->bits )
+ {
+ case 8:
+ {
+ UINT8 *flash_memory = c->flash_memory;
+ memset( &flash_memory[ address & ~0xffff ], 0xff, 64 * 1024 );
+ }
+ break;
+ case 16:
+ {
+ UINT16 *flash_memory = c->flash_memory;
+ memset( &flash_memory[ address & ~0x7fff ], 0xff, 64 * 1024 );
+ }
+ break;
+ }
+
+ c->status = 1 << 3;
+ c->flash_mode = FM_ERASEAMD4;
+
+ timer_adjust_ptr( c->timer, ATTOTIME_IN_SEC( 1 ), attotime_zero );
+ }
+ else
+ {
+ logerror( "unexpected %08x=%02x in FM_ERASEAMD3\n", address, data & 0xff );
+ }
+ break;
+ case FM_BYTEPROGRAM:
+ switch( c->bits )
+ {
+ case 8:
+ {
+ UINT8 *flash_memory = c->flash_memory;
+ flash_memory[ address ] = data;
+ }
+ break;
+ default:
+ logerror( "FM_BYTEPROGRAM not supported when c->bits == %d\n", c->bits );
+ break;
+ }
+ c->flash_mode = FM_NORMAL;
+ break;
+ case FM_WRITEPART1:
+ switch( c->bits )
+ {
+ case 8:
+ {
+ UINT8 *flash_memory = c->flash_memory;
+ flash_memory[ address ] = data;
+ }
+ break;
+ case 16:
+ {
+ UINT16 *flash_memory = c->flash_memory;
+ flash_memory[ address ] = data;
+ }
+ break;
+ default:
+ logerror( "FM_WRITEPART1 not supported when c->bits == %d\n", c->bits );
+ break;
+ }
+ c->status = 0x80;
+ c->flash_mode = FM_READSTATUS;
+ break;
+ case FM_CLEARPART1:
+ if( ( data & 0xff ) == 0xd0 )
+ {
+ // clear the 64k block containing the current address to all 0xffs
+ switch( c->bits )
+ {
+ case 8:
+ {
+ UINT8 *flash_memory = c->flash_memory;
+ memset( &flash_memory[ address & ~0xffff ], 0xff, 64 * 1024 );
+ }
+ break;
+ case 16:
+ {
+ UINT16 *flash_memory = c->flash_memory;
+ memset( &flash_memory[ address & ~0x7fff ], 0xff, 64 * 1024 );
+ }
+ break;
+ default:
+ logerror( "FM_CLEARPART1 not supported when c->bits == %d\n", c->bits );
+ break;
+ }
+ c->status = 0x00;
+ c->flash_mode = FM_READSTATUS;
+
+ timer_adjust_ptr( c->timer, ATTOTIME_IN_SEC( 1 ), attotime_zero );
+ break;
+ }
+ else
+ {
+ logerror( "unexpected %02x in FM_CLEARPART1\n", data & 0xff );
+ }
+ break;
+ case FM_SETMASTER:
+ switch( data & 0xff )
+ {
+ case 0xf1:
+ c->flash_master_lock = 1;
+ break;
+ case 0xd0:
+ c->flash_master_lock = 0;
+ break;
+ default:
+ logerror( "unexpected %08x=%02x in FM_SETMASTER:\n", address, data & 0xff );
+ break;
+ }
+ c->flash_mode = FM_NORMAL;
+ break;
+ }
+}
+
+void nvram_handler_intelflash(running_machine *machine, int chip,mame_file *file,int read_or_write)
+{
+ struct flash_chip *c;
+ if( chip >= FLASH_CHIPS_MAX )
+ {
+ logerror( "intelflash_nvram: invalid chip %d\n", chip );
+ return;
+ }
+ c = &chips[ chip ];
+
+ switch( c->bits )
+ {
+ case 8:
+ if (read_or_write)
+ {
+ mame_fwrite( file, c->flash_memory, c->size );
+ }
+ else if (file)
+ {
+ mame_fread( file, c->flash_memory, c->size );
+ }
+ break;
+ case 16:
+ if (read_or_write)
+ {
+ /// TODO: endian conversion
+ mame_fwrite( file, c->flash_memory, c->size );
+ }
+ else if (file)
+ {
+ mame_fread( file, c->flash_memory, c->size );
+ /// TODO: endian conversion
+ }
+ break;
+ }
+}