Major chipselect overhaul

Does not compile yet
This commit is contained in:
meepingsnesroms
2018-04-17 12:03:50 -07:00
parent 93d7c4b67c
commit caef2dfe96
5 changed files with 292 additions and 86 deletions

View File

@@ -19,6 +19,7 @@
extern "C" {
#endif
#include "memoryAccess.h"//for size macros
#include "emuFeatureRegistersSpec.h"
//emu errors
@@ -37,16 +38,6 @@ enum{
CARD_MMC
};
//memory banks
enum{
EMPTY_BANK = 0,
RAM_BANK,
ROM_BANK,
REG_BANK,
SED1376_REG_BANK,
SED1376_FB_BANK
};
//types
typedef struct{
bool buttonUp;
@@ -96,28 +87,6 @@ typedef struct{
#define CRYSTAL_FREQUENCY 32768.0
#define CPU_FREQUENCY (palmCrystalCycles * CRYSTAL_FREQUENCY)
//address space
#define NUM_BANKS(areaSize) (areaSize & 0x0000FFFF ? (areaSize >> 16) + 1 : areaSize >> 16)
#define START_BANK(address) (address >> 16)
#define END_BANK(address, size) (START_BANK(address) + NUM_BANKS(size) - 1)
#define BANK_IN_RANGE(bank, address, size) (bank >= START_BANK(address) && bank <= END_BANK(address, size))
#define TOTAL_MEMORY_BANKS 0x10000
//memory chip addresses
#define RAM_START_ADDRESS 0x00000000
#define ROM_START_ADDRESS 0x10000000
#define REG_START_ADDRESS 0xFFFFF000
#define RAM_SIZE (16 * 0x100000)//16mb RAM
#define ROM_SIZE (4 * 0x100000)//4mb ROM
#define REG_SIZE 0xE00
#define BOOTLOADER_SIZE 0x200
//display chip addresses
#define SED1376_REG_START_ADDRESS 0x1FF80000
#define SED1376_FB_START_ADDRESS 0x1FFA0000
#define SED1376_REG_SIZE 0xB4//it has 0x20000 used address space entrys but only 0xB4 registers
#define SED1376_FB_SIZE 0x20000//0x14000 in size, likely also has 0x20000 used address space entrys, using 0x20000 to prevent speed penalty of checking validity on every access
//config options
#define EMU_FPS 60.0
#define SDCARD_STATE_CHUNKS_VECTOR_SIZE 100

View File

@@ -11,6 +11,7 @@
#include "m68k/m68k.h"
chip_t chips[4];
int32_t pllWakeWait;
uint32_t clk32Counter;
double timer1CycleCounter;
@@ -64,6 +65,133 @@ static inline bool pllOn(){
return (registerArrayRead16(PLLCR) & 0x0008) == 0;
}
static inline void setCsa(uint16_t value){
chips[CHIP_A_ROM].enable = CAST_TO_BOOL(value & 0x0001);
chips[CHIP_A_ROM].readOnly = CAST_TO_BOOL(value & 0x8000);
chips[CHIP_A_ROM].size = 0x20000/*128kb*/ << ((value >> 1) & 0x0007);
registerArrayWrite16(CSA, value & 0x81FF);
}
static inline void setCsb(uint16_t value){
uint16_t csControl1 = registerArrayRead16(CSCTRL1);
chips[CHIP_B_SED].enable = CAST_TO_BOOL(value & 0x0001);
chips[CHIP_B_SED].readOnly = CAST_TO_BOOL(value & 0x8000);
chips[CHIP_B_SED].size = 0x20000/*128kb*/ << ((value >> 1) & 0x0007);
//attributes
chips[CHIP_B_SED].supervisorOnlyProtectedMemory = CAST_TO_BOOL(value & 0x4000);
chips[CHIP_B_SED].readOnlyForProtectedMemory = CAST_TO_BOOL(value & 0x2000);
if(csControl1 & 0x4000 && csControl1 & 0x0001)
chips[CHIP_B_SED].unprotectedSize = 0x8000/*32kb*/ << (((value >> 11) & 0x0003) | 0x0004);
else
chips[CHIP_B_SED].unprotectedSize = 0x8000/*32kb*/ << ((value >> 11) & 0x0003);
registerArrayWrite16(CSB, value & 0xF9FF);
}
static inline void setCsc(uint16_t value){
uint16_t csControl1 = registerArrayRead16(CSCTRL1);
chips[CHIP_C_USB].enable = CAST_TO_BOOL(value & 0x0001);
chips[CHIP_C_USB].readOnly = CAST_TO_BOOL(value & 0x8000);
chips[CHIP_C_USB].size = 0x8000/*32kb*/ << ((value >> 1) & 0x0007);
//attributes
chips[CHIP_C_USB].supervisorOnlyProtectedMemory = CAST_TO_BOOL(value & 0x4000);
chips[CHIP_C_USB].readOnlyForProtectedMemory = CAST_TO_BOOL(value & 0x2000);
if(csControl1 & 0x4000 && csControl1 & 0x0004)
chips[CHIP_C_USB].unprotectedSize = 0x8000/*32kb*/ << (((value >> 11) & 0x0003) | 0x0004);
else
chips[CHIP_C_USB].unprotectedSize = 0x8000/*32kb*/ << ((value >> 11) & 0x0003);
registerArrayWrite16(CSC, value & 0xF9FF);
}
static inline void setCsd(uint16_t value){
uint16_t csControl1 = registerArrayRead16(CSCTRL1);
chips[CHIP_D_RAM].enable = CAST_TO_BOOL(value & 0x0001);
chips[CHIP_D_RAM].readOnly = CAST_TO_BOOL(value & 0x8000);
if(csControl1 & 0x0040 && value & 0x0200)
chips[CHIP_D_RAM].size = 0x800000/*8mb*/ << ((value >> 1) & 0x0001);
else
chips[CHIP_D_RAM].size = 0x8000/*32kb*/ << ((value >> 1) & 0x0007);
//attributes
chips[CHIP_D_RAM].supervisorOnlyProtectedMemory = CAST_TO_BOOL(value & 0x4000);
chips[CHIP_D_RAM].readOnlyForProtectedMemory = CAST_TO_BOOL(value & 0x2000);
if(csControl1 & 0x4000 && csControl1 & 0x0010)
chips[CHIP_D_RAM].unprotectedSize = 0x8000/*32kb*/ << (((value >> 11) & 0x0003) | 0x0004);
else
chips[CHIP_D_RAM].unprotectedSize = 0x8000/*32kb*/ << ((value >> 11) & 0x0003);
registerArrayWrite16(CSD, value);
}
static inline void setCsgba(uint16_t value){
uint16_t csugba = registerArrayRead16(CSUGBA);
//add extra address bits if enabled
if(csugba & 0x8000)
chips[CHIP_A_ROM].start = ((csugba >> 12) & 0x0007) << 29 | (value >> 1) << 13;
else
chips[CHIP_A_ROM].start = (value >> 1) << 13;
registerArrayWrite16(CSGBA, value & 0xFFFE);
}
static inline void setCsgbb(uint16_t value){
uint16_t csugba = registerArrayRead16(CSUGBA);
//add extra address bits if enabled
if(csugba & 0x8000)
chips[CHIP_B_SED].start = ((csugba >> 8) & 0x0007) << 29 | (value >> 1) << 13;
else
chips[CHIP_B_SED].start = (value >> 1) << 13;
registerArrayWrite16(CSGBB, value & 0xFFFE);
}
static inline void setCsgbc(uint16_t value){
uint16_t csugba = registerArrayRead16(CSUGBA);
//add extra address bits if enabled
if(csugba & 0x8000)
chips[CHIP_C_USB].start = ((csugba >> 4) & 0x0007) << 29 | (value >> 1) << 13;
else
chips[CHIP_C_USB].start = (value >> 1) << 13;
registerArrayWrite16(CSGBC, value & 0xFFFE);
}
static inline void setCsgbd(uint16_t value){
uint16_t csugba = registerArrayRead16(CSUGBA);
//add extra address bits if enabled
if(csugba & 0x8000)
chips[CHIP_D_RAM].start = (csugba & 0x0007) << 29 | (value >> 1) << 13;
else
chips[CHIP_D_RAM].start = (value >> 1) << 13;
registerArrayWrite16(CSGBD, value & 0xFFFE);
}
static inline void setCsctrl1(uint16_t value){
uint16_t oldCsctrl1 = registerArrayRead16(CSCTRL1);
registerArrayWrite16(CSCTRL1, value & 0x7F55);
if((oldCsctrl1 & 0x4055) != (value & 0x4055)){
//something important changed, update all chipselects
//CSA is not dependant on CSCTRL1
setCsb(registerArrayRead16(CSB));
setCsc(registerArrayRead16(CSC));
setCsd(registerArrayRead16(CSD));
}
}
//csctrl 2 and 3 only deal with timing and bus transfer size
void printUnknownHwAccess(unsigned int address, unsigned int value, unsigned int size, bool isWrite){
if(isWrite){
@@ -358,9 +486,17 @@ static inline void setPllcr(uint16_t value){
static inline void setScr(uint8_t value){
uint8_t oldScr = registerArrayRead8(SCR);
registerArrayWrite8(SCR, value);
if((value & 0x04) != (oldScr & 0x04)){
if(value & 0x04)
uint8_t newScr = value;
//preserve privilege violation, write protect violation and bus error timeout
newScr |= oldScr & 0xE0;
//clear violations on writing 1 to them
newScr &= ~(oldScr & value & 0xE0);
registerArrayWrite8(SCR, newScr);//must be written before calling setRegisterFFFFAccessMode
if((newScr & 0x04) != (oldScr & 0x04)){
if(newScr & 0x04)
setRegisterXXFFAccessMode();
else
setRegisterFFFFAccessMode();
@@ -1019,33 +1155,66 @@ void setHwRegister16(unsigned int address, unsigned int value){
//missing bits 13, 9, 8 and 7
registerArrayWrite16(address, value & 0xDC7F);
break;
/*
case CSA:
setCsa(value);
refreshBankHandlers();
break;
case CSB:
setCsb(value);
refreshBankHandlers();
break;
case CSC:
setCsc(value);
refreshBankHandlers();
break;
case CSD:
setCsd(value);
refreshBankHandlers();
break;
case CSGBA:
//unemulated
//sets the starting location of ROM
registerArrayWrite16(address, value & 0xFFFE);
//sets the starting location of ROM(0x10000000)
setCsgba(value);
refreshBankHandlers();
break;
case CSGBB:
//unemulated
//sets the starting location of the SED1376
registerArrayWrite16(address, value & 0xFFFE);
//sets the starting location of the SED1376(0x1FF80000)
setCsgbb(value);
refreshBankHandlers();
break;
case CSGBC:
//unemulated
//sets the starting location of USBPhilipsPDIUSBD12(address 0x10400000)
//since I dont plan on adding hotsync should be fine to leave unemulated, its unemulated in pose
registerArrayWrite16(address, value & 0xFFFE);
setCsgbc(value);
refreshBankHandlers();
break;
case CSGBD:
//unemulated
//sets the starting location of RAM
registerArrayWrite16(address, value & 0xFFFE);
//sets the starting location of RAM(0x00000000)
setCsgbd(value);
refreshBankHandlers();
break;
case CSUGBA:
registerArrayWrite16(CSUGBA, value);
//refresh all chipselect address lines
setCsgba(registerArrayRead16(CSGBA));
setCsgbb(registerArrayRead16(CSGBB));
setCsgbc(registerArrayRead16(CSGBC));
setCsgbd(registerArrayRead16(CSGBD));
refreshBankHandlers();
break;
case CSCTRL1:
setCsctrl1(value);
refreshBankHandlers();
break;
*/
default:
printUnknownHwAccess(address, value, 16, true);
@@ -1104,6 +1273,22 @@ void resetHwRegisters(){
pllWakeWait = -1;
timer1CycleCounter = 0.0;
timer2CycleCounter = 0.0;
for(uint32_t chip = CHIP_BEGIN; chip < CHIP_END; chip++){
chips[chip].enable = false;
chips[chip].start = 0x00000000;
chips[chip].size = 0x00000000;
chips[chip].mask = 0x00000000;
chips[chip].readOnly = false;
chips[chip].readOnlyForProtectedMemory = false;
chips[chip].supervisorOnlyProtectedMemory = false;
chips[chip].unprotectedSize = 0x00000000;
}
//all chipselects are disabled at boot and CSA is mapped to 0x00000000 and covers the entire address range until CSGBA set otherwise
chips[CHIP_A_ROM].enable = true;
chips[CHIP_A_ROM].start = 0x00000000;
chips[CHIP_A_ROM].size = 0xFFFFFFFF;
chips[CHIP_A_ROM].mask = 0xFFFFFFFF;
//system control
registerArrayWrite8(SCR, 0x1C);

View File

@@ -28,7 +28,32 @@
#define INT_TMR1 0x00000002 //level 6
#define INT_SPI2 0x00000001 //level 4
//chip names
enum{
CHIP_BEGIN = 0,
CHIP_A_ROM = 0,
CHIP_B_SED,
CHIP_C_USB,
CHIP_D_RAM,
CHIP_END
};
//types
typedef struct{
bool enable;
uint32_t start;
uint32_t size;
uint32_t mask;//the address lines the chip responds to, so 0x10000 on an chip with 16 address lines will return the value at 0x0000
//attributes
bool readOnly;
bool readOnlyForProtectedMemory;
bool supervisorOnlyProtectedMemory;
uint32_t unprotectedSize;
}chip_t;
//variables
extern chip_t chips[4];
extern int32_t pllWakeWait;
extern uint32_t clk32Counter;
extern double timer1CycleCounter;

View File

@@ -10,30 +10,30 @@
static memory_access_t bankAccessors[TOTAL_MEMORY_BANKS];//these are not part of savestates because function pointers change with -fPIC
uint8_t bankType[TOTAL_MEMORY_BANKS];//these go in savestates
//used for unmapped address space and writes to ROM
//used for unmapped address space and writes to ROM, should trigger access exceptions if enabled
static unsigned int unmappedRead(unsigned int address){return 0x00000000;}
static void unmappedWrite(unsigned int address, unsigned int value){}
//RAM accesses
static unsigned int ramRead8(unsigned int address){return BUFFER_READ_8(palmRam, address, RAM_START_ADDRESS);}
static unsigned int ramRead16(unsigned int address){return BUFFER_READ_16(palmRam, address, RAM_START_ADDRESS);}
static unsigned int ramRead32(unsigned int address){return BUFFER_READ_32(palmRam, address, RAM_START_ADDRESS);}
static void ramWrite8(unsigned int address, unsigned int value){BUFFER_WRITE_8(palmRam, address, RAM_START_ADDRESS, value);}
static void ramWrite16(unsigned int address, unsigned int value){BUFFER_WRITE_16(palmRam, address, RAM_START_ADDRESS, value);}
static void ramWrite32(unsigned int address, unsigned int value){BUFFER_WRITE_32(palmRam, address, RAM_START_ADDRESS, value);}
static unsigned int ramRead8(unsigned int address){return BUFFER_READ_8(palmRam, address, chips[CHIP_D_RAM].start);}
static unsigned int ramRead16(unsigned int address){return BUFFER_READ_16(palmRam, address, chips[CHIP_D_RAM].start);}
static unsigned int ramRead32(unsigned int address){return BUFFER_READ_32(palmRam, address, chips[CHIP_D_RAM].start);}
static void ramWrite8(unsigned int address, unsigned int value){BUFFER_WRITE_8(palmRam, address, chips[CHIP_D_RAM].start, value);}
static void ramWrite16(unsigned int address, unsigned int value){BUFFER_WRITE_16(palmRam, address, chips[CHIP_D_RAM].start, value);}
static void ramWrite32(unsigned int address, unsigned int value){BUFFER_WRITE_32(palmRam, address, chips[CHIP_D_RAM].start, value);}
//ROM accesses
static unsigned int romRead8(unsigned int address){return BUFFER_READ_8(palmRom, address, ROM_START_ADDRESS);}
static unsigned int romRead16(unsigned int address){return BUFFER_READ_16(palmRom, address, ROM_START_ADDRESS);}
static unsigned int romRead32(unsigned int address){return BUFFER_READ_32(palmRom, address, ROM_START_ADDRESS);}
static unsigned int romRead8(unsigned int address){return BUFFER_READ_8(palmRom, address, chips[CHIP_A_ROM].start);}
static unsigned int romRead16(unsigned int address){return BUFFER_READ_16(palmRom, address, chips[CHIP_A_ROM].start);}
static unsigned int romRead32(unsigned int address){return BUFFER_READ_32(palmRom, address, chips[CHIP_A_ROM].start);}
//SED1376 framebuffer
static unsigned int sed1376FramebufferRead8(unsigned int address){return BUFFER_READ_8(sed1376Framebuffer, address, SED1376_FB_START_ADDRESS);}
static unsigned int sed1376FramebufferRead16(unsigned int address){return BUFFER_READ_16(sed1376Framebuffer, address, SED1376_FB_START_ADDRESS);}
static unsigned int sed1376FramebufferRead32(unsigned int address){return BUFFER_READ_32(sed1376Framebuffer, address, SED1376_FB_START_ADDRESS);}
static void sed1376FramebufferWrite8(unsigned int address, unsigned int value){BUFFER_WRITE_8(sed1376Framebuffer, address, SED1376_FB_START_ADDRESS, value);}
static void sed1376FramebufferWrite16(unsigned int address, unsigned int value){BUFFER_WRITE_16(sed1376Framebuffer, address, SED1376_FB_START_ADDRESS, value);}
static void sed1376FramebufferWrite32(unsigned int address, unsigned int value){BUFFER_WRITE_32(sed1376Framebuffer, address, SED1376_FB_START_ADDRESS, value);}
static unsigned int sed1376FramebufferRead8(unsigned int address){return BUFFER_READ_8(sed1376Framebuffer, address, chips[CHIP_B_SED].start + SED1376_REG_SIZE);}
static unsigned int sed1376FramebufferRead16(unsigned int address){return BUFFER_READ_16(sed1376Framebuffer, address, chips[CHIP_B_SED].start + SED1376_REG_SIZE);}
static unsigned int sed1376FramebufferRead32(unsigned int address){return BUFFER_READ_32(sed1376Framebuffer, address, chips[CHIP_B_SED].start + SED1376_REG_SIZE);}
static void sed1376FramebufferWrite8(unsigned int address, unsigned int value){BUFFER_WRITE_8(sed1376Framebuffer, address, chips[CHIP_B_SED].start + SED1376_REG_SIZE, value);}
static void sed1376FramebufferWrite16(unsigned int address, unsigned int value){BUFFER_WRITE_16(sed1376Framebuffer, address, chips[CHIP_B_SED].start + SED1376_REG_SIZE, value);}
static void sed1376FramebufferWrite32(unsigned int address, unsigned int value){BUFFER_WRITE_32(sed1376Framebuffer, address, chips[CHIP_B_SED].start + SED1376_REG_SIZE, value);}
/* Read from anywhere */
@@ -58,8 +58,6 @@ unsigned int m68k_read_disassembler_32(unsigned int address){return m68k_read_me
static uint8_t getProperBankType(uint16_t bank){
//RAM bank 0x0000 not correct, inaccurate but works fine
//special conditions
if((bank & 0x00FF) == 0x00FF && registersAreXXFFMapped()){
//XXFF register mode
@@ -67,23 +65,21 @@ static uint8_t getProperBankType(uint16_t bank){
}
//normal banks
if(BANK_IN_RANGE(bank, RAM_START_ADDRESS, RAM_SIZE)){
if(BANK_IN_RANGE(bank, chips[CHIP_D_RAM].start, chips[CHIP_D_RAM].size)){
return RAM_BANK;
}
else if(BANK_IN_RANGE(bank, ROM_START_ADDRESS, ROM_SIZE)){
else if(BANK_IN_RANGE(bank, chips[CHIP_A_ROM].start, chips[CHIP_A_ROM].size)){
return ROM_BANK;
}
else if(BANK_IN_RANGE(bank, REG_START_ADDRESS, REG_SIZE)){
return REG_BANK;
}
else if(BANK_IN_RANGE(bank, SED1376_REG_START_ADDRESS, SED1376_REG_SIZE) && sed1376ClockConnected()){
return SED1376_REG_BANK;
}
else if(BANK_IN_RANGE(bank, SED1376_FB_START_ADDRESS, SED1376_FB_SIZE) && sed1376ClockConnected()){
else if(BANK_IN_RANGE(bank, chips[CHIP_B_SED].start, chips[CHIP_B_SED].size) && sed1376ClockConnected()){
if(bank - START_BANK(chips[CHIP_B_SED].start) < NUM_BANKS(SED1376_REG_SIZE))
return SED1376_REG_BANK;
return SED1376_FB_BANK;
}
return EMPTY_BANK;
}
@@ -163,18 +159,15 @@ void setRegisterFFFFAccessMode(){
void setSed1376Attached(bool attached){
if(attached){
for(uint32_t bank = START_BANK(SED1376_REG_START_ADDRESS); bank < END_BANK(SED1376_REG_START_ADDRESS, SED1376_REG_SIZE); bank++){
setBankType(bank, SED1376_REG_BANK);
}
for(uint32_t bank = START_BANK(SED1376_FB_START_ADDRESS); bank < END_BANK(SED1376_FB_START_ADDRESS, SED1376_FB_SIZE); bank++){
setBankType(bank, SED1376_FB_BANK);
for(uint32_t bank = START_BANK(chips[CHIP_B_SED].start); bank < END_BANK(chips[CHIP_B_SED].start, chips[CHIP_B_SED].size); bank++){
if(bank - START_BANK(chips[CHIP_B_SED].start) < NUM_BANKS(SED1376_REG_SIZE))
setBankType(bank, SED1376_REG_BANK);
else
setBankType(bank, SED1376_FB_BANK);
}
}
else{
for(uint32_t bank = START_BANK(SED1376_REG_START_ADDRESS); bank < END_BANK(SED1376_REG_START_ADDRESS, SED1376_REG_SIZE); bank++){
setBankType(bank, EMPTY_BANK);
}
for(uint32_t bank = START_BANK(SED1376_FB_START_ADDRESS); bank < END_BANK(SED1376_FB_START_ADDRESS, SED1376_FB_SIZE); bank++){
for(uint32_t bank = START_BANK(chips[CHIP_B_SED].start); bank < END_BANK(chips[CHIP_B_SED].start, chips[CHIP_B_SED].size); bank++){
setBankType(bank, EMPTY_BANK);
}
}

View File

@@ -2,6 +2,28 @@
#include <stdint.h>
//address space
#define NUM_BANKS(areaSize) (areaSize & 0x0000FFFF ? (areaSize >> 16) + 1 : areaSize >> 16)
#define START_BANK(address) (address >> 16)
#define END_BANK(address, size) (START_BANK(address) + NUM_BANKS(size) - 1)
#define BANK_IN_RANGE(bank, address, size) (bank >= START_BANK(address) && bank <= END_BANK(address, size))
#define TOTAL_MEMORY_BANKS 0x10000
//memory chip addresses
//#define RAM_START_ADDRESS 0x00000000
//#define ROM_START_ADDRESS 0x10000000
#define REG_START_ADDRESS 0xFFFFF000
#define RAM_SIZE (16 * 0x100000)//16mb RAM
#define ROM_SIZE (4 * 0x100000)//4mb ROM
#define REG_SIZE 0xE00
#define BOOTLOADER_SIZE 0x200
//display chip addresses
//#define SED1376_REG_START_ADDRESS 0x1FF80000
//#define SED1376_FB_START_ADDRESS 0x1FFA0000
#define SED1376_REG_SIZE 0x20000//it has 0x20000 used address space entrys but only 0xB4 registers
#define SED1376_FB_SIZE 0x20000//0x14000 in size, likely also has 0x20000 used address space entrys, using 0x20000 to prevent speed penalty of checking validity on every access
//the read/write stuff looks messy here but makes the memory access functions alot cleaner
#define BUFFER_READ_8(segment, accessAddress, startAddress) segment[accessAddress - startAddress]
#define BUFFER_READ_16(segment, accessAddress, startAddress) segment[accessAddress - startAddress] << 8 | segment[accessAddress - startAddress + 1]
@@ -10,6 +32,18 @@
#define BUFFER_WRITE_16(segment, accessAddress, startAddress, value) segment[accessAddress - startAddress] = value >> 8; segment[accessAddress - startAddress + 1] = value & 0xFF
#define BUFFER_WRITE_32(segment, accessAddress, startAddress, value) segment[accessAddress - startAddress] = value >> 24; segment[accessAddress - startAddress + 1] = (value >> 16) & 0xFF; segment[accessAddress - startAddress + 2] = (value >> 8) & 0xFF; segment[accessAddress - startAddress + 3] = value & 0xFF
//memory banks
enum{
EMPTY_BANK = 0,
RAM_BANK,
ROM_BANK,
REG_BANK,
SED1376_REG_BANK,
SED1376_FB_BANK,
UNSAFE_BANK//if a chip select uses base address bits 15 or 14 accesses wont be bank aligned and will use "if(address >= chips[chip].start && address <= chips[chip].start + chips[chip].size)"
};
//types
typedef struct{
unsigned int (*read8)(unsigned int address);
unsigned int (*read16)(unsigned int address);