/* MEMORY.H ======== 99/4A-specific memory stuff INVARIANT: memory[] has ALL addressable rom/ram, including DSR, module, etc. */ #ifndef __MEMORY_H__ #define __MEMORY_H__ #include "16bit.h" #include "sysdeps.h" #include "command.h" #include "centry.h" struct mrstruct; typedef u16 (*mrword)(const struct mrstruct *mr, const u32 addr); typedef s8 (*mrbyte)(const struct mrstruct *mr, u32 addr); typedef void (*mwword)(const struct mrstruct *mr, u32 addr,u16 val); typedef void (*mwbyte)(const struct mrstruct *mr, u32 addr,s8 val); typedef struct mrstruct { u8 *areamemory; /* actual memory for area, except for empty mem */ u8 *arearead; /* if non-NULL, we can statically read */ u8 *areawrite; /* if non-NULL, we can statically write */ mrword read_word; /* these routines are used before the */ mrbyte read_byte; /* memory maps when using the memory_xxx_xxx */ mwword write_word; /* functions, but the memory is used first */ mwbyte write_byte; /* when the MEMORY_XXX_XXX macros are used. */ } mrstruct; #define AREA_IS_MEMORY_MAPPED(a) \ ((a)->read_word || (a)->read_byte || \ (a)->write_word || (a)->write_byte) /* This must remain 64K, even if mega-memory expansion is emulated. All the public routines expect to be passed 16-bit addresses. */ #define PHYSMEMORYSIZE 65536 extern u8 zeroes[PHYSMEMORYSIZE]; /* Size of an area of memory. This is the minimum size of memory for which each address therein is expected to act "the same". */ #define AREASIZE 1024 #define AREASHIFT 10 #define NUMAREAS (PHYSMEMORYSIZE >> AREASHIFT) #define NUMDOMAINS 4 typedef enum { md_cpu, md_graphics, md_video, md_speech } mem_domain; extern mrstruct __areahandlers[NUMDOMAINS][NUMAREAS]; // shorthand for CPU memory #define SET_AREA_HANDLER(addr,size,handler) set_area_handler(md_cpu, addr,size,handler) void set_area_handler(mem_domain domain, u16 addr, u32 size, mrstruct *handler); /*********************************************/ /* These routines are global and available for use by any other module. Choose one of the two functions depending on side effects. For routines that are strictly for static reads or writes (for example, in a debugger), use the macro versions. For routines used during emulation, use the function ones. */ // more CPU shorthands #define THE_AREA(dmn, addr) (&__areahandlers[dmn][((addr) & (PHYSMEMORYSIZE-1)) >> AREASHIFT]) #define AREA_SETUP(dmn, addr) \ mrstruct *area = THE_AREA(dmn, addr); \ my_assert((dmn) >= md_cpu && (dmn) < NUMDOMAINS &&(u32)area > addr) #define HAS_RAM_ACCESS(dmn, addr) \ (THE_AREA(dmn, addr)->arearead != NULL && \ THE_AREA(dmn, addr)->areawrite != NULL) #define HAS_ROM_ACCESS(dmn, addr) \ (THE_AREA(dmn, addr)->arearead != NULL) /* These macros are for strict static access to memory. No routines will be called, so there will be no side effects. Unfortunately, though, a lot of memory looks like zeroes. */ #define AREA_READ_WORD(area, addr) \ ((area)->arearead ? \ WORD((area)->arearead, (addr & (AREASIZE-1))) : 0) #define AREA_READ_BYTE(area, addr) \ ((area)->arearead ? \ BYTE((area)->arearead, (addr & (AREASIZE-1))) : 0) #define AREA_WRITE_WORD(area, addr, val) \ do { if ((area)->areawrite) \ WORD((area)->areawrite, (addr & (AREASIZE-1))) = val; } while (0) #define AREA_WRITE_BYTE(area, addr, val) \ do { if ((area)->areawrite) \ BYTE((area)->areawrite, (addr & (AREASIZE-1))) = val; } while (0) #define DOMAIN_READ_WORD(dmn, addr) \ AREA_READ_WORD(THE_AREA(dmn,addr), addr) #define DOMAIN_READ_BYTE(dmn, addr) \ AREA_READ_BYTE(THE_AREA(dmn,addr), addr) #define DOMAIN_WRITE_WORD(dmn, addr, val) \ AREA_WRITE_WORD(THE_AREA(dmn, addr), addr, val) #define DOMAIN_WRITE_BYTE(dmn, addr, val) \ AREA_WRITE_BYTE(THE_AREA(dmn, addr), addr, val) #define MEMORY_READ_WORD(addr) DOMAIN_READ_WORD(md_cpu, addr) #define MEMORY_READ_BYTE(addr) DOMAIN_READ_BYTE(md_cpu, addr) #define MEMORY_WRITE_WORD(addr, val) DOMAIN_WRITE_WORD(md_cpu, addr, val) #define MEMORY_WRITE_BYTE(addr, val) DOMAIN_WRITE_BYTE(md_cpu, addr, val) u16 domain_read_word(mem_domain md, u32 addr); void domain_write_word(mem_domain md, u32 addr, u16 val); s8 domain_read_byte(mem_domain md, u32 addr); void domain_write_byte(mem_domain md, u32 addr, s8 val); INLINE u16 memory_read_word(u32 addr) { return domain_read_word(md_cpu, addr); } INLINE void memory_write_word(u32 addr, u16 val) { domain_write_word(md_cpu, addr, val); } INLINE s8 memory_read_byte(u32 addr) { return domain_read_byte(md_cpu, addr); } INLINE void memory_write_byte(u32 addr, s8 val) { domain_write_byte(md_cpu, addr, val); } // Empty ROM memory handler extern mrstruct zero_memory_handler; /******************************************/ // 32K memory expansion extern int isexpram; /* is there expansion RAM? */ // Alternate >8000->83FF mapping extern int isenhconsoleram; /* is there extra console RAM >8000->82FF? */ extern void memory_init(void); extern void memory_ram_init(void); /**********************************/ #if 0 #define FLAT_MEMORY_PTR(dmn, addr) \ (THE_AREA(dmn, addr)->areamemory ? \ THE_AREA(dmn, addr)->areamemory + ((addr) & (AREASIZE-1)) : \ zeroes) #else #define FLAT_MEMORY_PTR(dmn, addr) \ (THE_AREA(dmn, addr)->areamemory + ((addr) & (AREASIZE-1))) #endif #define registerptr(reg) (u16 *)FLAT_MEMORY_PTR(md_cpu, (wp+reg+reg) & 0xffff) /* INLINE u16 *registerptr(int reg) { extern uaddr wp; mrstruct *area = THE_AREA(md_cpu, wp+reg+reg); return &WORD(area->areamemory, (wp+reg+reg) & (AREASIZE - 1)); } */ /* These enums and struct define a higher-level organization of the memory map, used to allow radical customization of the emulated computer's architecture. A MemoryEntry bridges the gap between ROM, RAM, and nonvolatile RAM. By associating a primary purpose with an area of memory, this allows us to transparently map ROMs from disk into an area of memory as well as maintain on disk an image of an area of RAM. The mementlist can be used to find out what the contents of memory are without knowing about the specific routines referenced in an mrstruct through the use of the flags. */ enum { /* MEMENT_ROM = 0, */ /* entry is ROM */ MEMENT_RAM = 1, /* entry is RAM */ MEMENT_STORED = 3, /* RAM entry stored to disk */ MEMENT_BANKING = 4+8, /* mask for banking */ /* MEMENT_NOT_BANKED = 0, */ MEMENT_BANK_1 = 4, /* bank #0 */ MEMENT_BANK_2 = 8, /* bank #1 */ MEMENT_CART = 16, /* this entry is part of a module */ MEMENT_DOMAIN_SHIFT = 5, MEMENT_DOMAIN = 7<<5, /* mask for domain of memory */ MEMENT_CONSOLE = md_cpu<<5, /* console ROM/RAM */ MEMENT_GRAPHICS = md_graphics<<5, /* GROM/GRAM */ MEMENT_VIDEO = md_video<<5, /* video */ MEMENT_SPEECH = md_speech<<5, /* speech */ // MEMENT_DSR = 4<<5, /* DSR ROM */ MEMENT_USER = 256 /* allocated via DefineMemory, destroy as needed * (when new memory overlaps this), else caller * will destroy */ }; typedef struct MemoryEntry { int flags; /* memory flags MEMENT_xxx */ u32 addr; /* start address */ u32 offs; /* offset of entry into original entry */ s32 size; /* size in bytes, 0 = unknown, -xxx = magnitude of maximum */ u32 realsize; /* size in bytes if loaded */ char *name; /* name of entry for debugging */ char *filename; /* file for loading/saving */ u32 fileoffs; /* offset into file */ mrstruct memact; /* how the memory acts, memory referenced must be malloc'ed */ struct MemoryEntry *next; /* next entry in database */ } MemoryEntry; extern MemoryEntry *mementlist; /* active memory list */ extern char *modulespath, *romspath, *ramspath; extern char *systemmodulespath, *systemromspath, *systemramspath; /* Handlers for bank-switched module */ extern mrstruct *memory_module_bank_handlers[2]; extern int memory_module_bank; void memory_set_module_bank(u8 bank); /* Free memory map */ void memory_free_list(void); /* Initialize memory map */ int memory_init_list(void); /* Create a new memory entry flags: bitmask of MEMENT_xxx addr: address of new memory size: size of memory in bytes, or if negative, the magnitude is the maximum size name: user name for memory segment filename: location of ROM or RAM on disk (MEMENT_ROM/MEMENT_STORED) fileoffs: offset into file where memory is stored memact: actions on memory access to area */ MemoryEntry *memory_new_entry(int flags, u32 addr, s32 size, char *name, char *filename, u32 fileoffs, mrstruct *memact); /* Destroy entry (free memory) */ void memory_destroy_entry(MemoryEntry *ent); /* Save a volatile entry in the memory map to disk */ int memory_save_entry(MemoryEntry *ent); /* Load a volatile entry in the memory map from disk */ int memory_load_entry(MemoryEntry *ent); /* Add entry to memory list and destroy entries this overrides */ int memory_add_entry_to_list(MemoryEntry *ent); /* Remove entry from memory list, return ent->next */ MemoryEntry * memory_remove_entry_from_list(MemoryEntry *prev, MemoryEntry *ent); /* Map an entry into the memory map */ int memory_map_entry(MemoryEntry *ent); /* Unmap an entry from the memory map */ void memory_unmap_entry(MemoryEntry *ent); /* Save volatile memory */ void memory_volatile_save(void); /* Load volatile memory */ int memory_volatile_load(void); /* Load all memory */ int memory_complete_load(void); /* Do it all */ int memory_insert_new_entry(int flags, u32 addr, s32 size, char *name, char *filename, u32 fileoffs, mrstruct *memact); /* DefineMemory */ DECL_SYMBOL_ACTION(memory_define_entry); /* ListMemory */ DECL_SYMBOL_ACTION(memory_dump); /* DefaultMemoryMap */ DECL_SYMBOL_ACTION(memory_default_list); #include "cexit.h" #endif