/* MEMORY.C ======== Functions to read/write CPU memory. */ #include #include #include "v9t9_common.h" #include "roms.h" #include "memory.h" #include "vdp.h" #include "grom.h" #include "speech.h" #include "sound.h" #define _L LOG_MEMORY | LOG_INFO int isexpram; /* is there expansion RAM? */ int isenhconsoleram; /* is >8000 - >82FF real RAM? */ /**********************************************/ /* Physical memory array, used for static ROM and RAM. Area handlers may point to other areas of memory, to handle things like bank-switching. */ //u8 physmemory[PHYSMEMORYSIZE]; /* Below we define a table of mrstructs which map CPU addresses to areas of memory, including means of dealing with special memory types through the use of read/write functions. It is advantageous to store memory in the areamemory/arearead/areawrite pointers and let the MEMORY_xxx or memory_xxx routines access it directly, for speed purposes, but for some memory-mapped areas it is saner to have a routine manage reads and writes to the memory. You must use a memory handling routine when: (1) word accesses are not the same as two simultaneous byte accesses (2) reading memory and writing memory are not orthogonal, i.e., writing/reading memory has side effects (memory-mapped) If the contents of memory are bank-switched or toggled on and off (such as the DSR), it's most speed efficient to change handlers to remap the contents (through changing the arearead and areawrite pointers) rather than copying contents in and out of a static areamemory. The emulator will make no assumptions about the semantics of memory which has read or write routines attached to an area. */ /* Area handlers are the main gateway to the memory bus. Each AREASIZE section of memory has various properties which define how to read from and write to it. mrstruct->areamemory contains the memory for the area. Pure RAM will have mrstruct->arearead and mrstruct->areawrite be set to this. Memory mapped I/O areas or ROM will leave one or both of the arearead or areawrite pointers NULL and instead define a routine, {read|write}_{byte|word} to handle the access to areamemory. Note, that while the arearead or areawrite pointers points to an AREASIZE sized block, the read/write byte/word routines are always passed a full 16-bit address. This allows a common function to control access to several contiguous areas. */ mrstruct __areahandlers[NUMDOMAINS][NUMAREAS]; void set_area_handler(mem_domain md, u16 addr, u32 size, mrstruct * handler) { mrstruct tmp = *handler; if ((size < AREASIZE) || (addr & (AREASIZE - 1))) logger(_L | LOG_FATAL, "set_area_handler: attempt made to set a memory handler on an illegal boundary\n" "(0x%04X...0x%04X), the minimum granularity is %d bytes\n", addr, addr + size - 1, AREASIZE); if (handler->arearead == NULL && handler->read_byte == NULL && handler->read_word != NULL) logger(_L | LOG_FATAL, "set_area_handler: cannot have a handler define read_word without read_byte\n"); if (handler->areawrite == NULL && handler->write_byte == NULL && handler->write_word != NULL) logger(_L | LOG_FATAL, "set_area_handler: cannot have a handler define read_word without read_byte\n"); if (!handler->areamemory) logger(_L | LOG_FATAL, "set_area_handler: must have areamemory set\n"); if (md < 0 || md >= NUMDOMAINS) logger(_L | LOG_FATAL, "set_area_handler: domain out of range\n"); if (size > PHYSMEMORYSIZE || addr >= PHYSMEMORYSIZE || addr + size > PHYSMEMORYSIZE) logger(_L | LOG_FATAL, "set_area_handler: illegal address or size\n"); size = (size + AREASIZE - 1) >> AREASHIFT; addr >>= AREASHIFT; while (size--) { __areahandlers[md][addr++] = tmp; /* advance memory pointer(s) */ if (tmp.areamemory) tmp.areamemory += AREASIZE; if (tmp.arearead) tmp.arearead += AREASIZE; if (tmp.areawrite) tmp.areawrite += AREASIZE; } } /*************/ /* CPU RAM */ /*************/ // This emulates the standard memory. static void wwcram(const mrstruct *mr, u32 addr, u16 val) { WORD(mr->areamemory, (addr & 0x00ff) + 0x300) = val; } static u16 wrcram(const mrstruct *mr, u32 addr) { return WORD(mr->areamemory, (addr & 0x00ff) + 0x300); } static void bwcram(const mrstruct *mr, u32 addr, s8 val) { BYTE(mr->areamemory, (addr & 0x00ff) + 0x300) = val; } static s8 brcram(const mrstruct *mr, u32 addr) { return BYTE(mr->areamemory, (addr & 0x00ff) + 0x300); } static u8 std_console_ram[0x400]; mrstruct std_console_ram_handler = { std_console_ram, std_console_ram, std_console_ram, wrcram, brcram, /* for reads, only honor low 8 bits of address */ wwcram, bwcram, /* for writes, only honor low 8 bits of address */ }; /* // Geneve memory static void wwcrame(const mrstruct *mr, u32 addr, u16 val) { WORD(mr->areamemory, (addr & 0x03ff)) = val; } static u16 wrcrame(const mrstruct *mr, u32 addr) { return WORD(mr->areamemory, (addr & 0x03ff)); } static void bwcrame(const mrstruct *mr, u32 addr, s8 val) { BYTE(mr->areamemory, (addr & 0x03ff)) = val; } static s8 brcrame(const mrstruct *mr, u32 addr) { return BYTE(mr->areamemory, (addr & 0x03ff)); } */ mrstruct enh_console_ram_handler = { std_console_ram, std_console_ram, std_console_ram, NULL, NULL, NULL, NULL /* allow direct access to memory */ }; /******************/ /* expansion RAM */ /******************/ static void wweram(const mrstruct *mr, u32 addr, u16 val) { if (isexpram) WORD(mr->areamemory, addr & (AREASIZE - 1)) = val; } static u16 wreram(const mrstruct *mr, u32 addr) { return (isexpram ? WORD(mr->areamemory, addr & (AREASIZE - 1)) : 0); } static void bweram(const mrstruct *mr, u32 addr, s8 val) { if (isexpram) BYTE(mr->areamemory, addr & (AREASIZE - 1)) = val; } static s8 breram(const mrstruct *mr, u32 addr) { return (isexpram ? BYTE(mr->areamemory, addr & (AREASIZE - 1)) : 0); } static u8 low_expansion_memory[0x2000]; mrstruct low_expansion_memory_handler = { low_expansion_memory, low_expansion_memory, low_expansion_memory, wreram, breram, /* only read if expansion memory is on */ wweram, bweram, /* only write if expansion memory is on */ }; static u8 high_expansion_memory[0x6000]; mrstruct high_expansion_memory_handler = { high_expansion_memory, high_expansion_memory, high_expansion_memory, wreram, breram, /* only read if expansion memory is on */ wweram, bweram, /* only write if expansion memory is on */ }; /****************/ /* zero memory */ /****************/ u8 zeroes[PHYSMEMORYSIZE]; mrstruct zero_memory_handler = { zeroes, NULL, NULL, /* can neither read nor write directly */ NULL, NULL, /* for reads, return zero */ NULL, NULL /* for writes, ignore */ }; /***************************************************************************/ static void bwsound(const mrstruct *mr, u32 addr, s8 val) { if (0 == (addr & 1)) sound_mmio_write(val); } mrstruct console_sound_handler = { zeroes, NULL, NULL, /* no RAM */ NULL, NULL, /* no reads */ NULL, bwsound }; static s8 brrvdp(const mrstruct *mr, u32 addr) { if (0 == (addr & 1)) return vdp_mmio_read(addr & 2); else return 0; } mrstruct console_vdp_read_handler = { zeroes, NULL, NULL, /* no RAM */ NULL, brrvdp, NULL, NULL /* no writes */ }; static void bwwvdp(const mrstruct *mr, u32 addr, s8 val) { if (!(addr & 1)) vdp_mmio_write(addr & 2, val); } mrstruct console_vdp_write_handler = { zeroes, NULL, NULL, /* no RAM */ NULL, NULL, /* no reads */ NULL, bwwvdp }; static s8 brrspeech(const mrstruct *mr, u32 addr) { if (0 == (addr & 1)) return speech_mmio_read(); else return 0; } mrstruct console_speech_read_handler = { zeroes, NULL, NULL, /* no RAM */ NULL, brrspeech, NULL, NULL /* no writes */ }; static void bwwspeech(const mrstruct *mr, u32 addr, s8 val) { if (0 == (addr & 1)) speech_mmio_write(val); } mrstruct console_speech_write_handler = { zeroes, NULL, NULL, /* no RAM */ NULL, NULL, /* no reads */ NULL, bwwspeech }; static s8 brrgrom(const mrstruct *mr, u32 addr) { if (0 == (addr & 1)) return grom_mmio_read(addr & 2); else return 0; } mrstruct console_grom_read_handler = { zeroes, NULL, NULL, /* no RAM */ NULL, brrgrom, NULL, NULL /* no writes */ }; static void bwwgrom(const mrstruct *mr, u32 addr, s8 val) { if (!(addr & 1)) grom_mmio_write(addr & 2, val); } mrstruct console_grom_write_handler = { zeroes, NULL, NULL, /* no RAM */ NULL, NULL, /* no reads [should cause a freeze] */ NULL, bwwgrom }; /********************************/ u16 domain_read_word(mem_domain md, u32 addr) { AREA_SETUP(md, addr); if (area->read_word) return area->read_word(area, addr); else if (area->read_byte) return (area->read_byte(area, addr) << 8) | ((area->read_byte(area, addr + 1)) & 0xff); else if (area->arearead) if (md == md_cpu) return WORD(area->arearead, (addr & (AREASIZE - 1))); else { return (FLAT_BYTE(area->arearead, (addr & (AREASIZE - 1))) << 8) + FLAT_BYTE(area->arearead, ((addr+1) & (AREASIZE - 1))); } else return 0; } void domain_write_word(mem_domain md, u32 addr, u16 val) { AREA_SETUP(md, addr); if (area->write_word) area->write_word(area, addr, val); else if (area->write_byte) { area->write_byte(area, addr, val >> 8); area->write_byte(area, addr + 1, (s8) val); } else if (area->areawrite) if (md == md_cpu) WORD(area->areawrite, (addr & (AREASIZE - 1))) = val; else { FLAT_BYTE(area->areawrite, (addr & (AREASIZE - 1))) = (val >> 8) & 0xff; FLAT_BYTE(area->areawrite, ((addr+1) & (AREASIZE - 1))) = val & 0xff; } } s8 domain_read_byte(mem_domain md, u32 addr) { AREA_SETUP(md, addr); if (area->read_byte) return area->read_byte(area, addr); else if (area->arearead) if (md == md_cpu) return BYTE(area->arearead, (addr & (AREASIZE - 1))); else return FLAT_BYTE(area->arearead, (addr & (AREASIZE - 1))); else return 0; } void domain_write_byte(mem_domain md, u32 addr, s8 val) { AREA_SETUP(md, addr); if (area->write_byte) area->write_byte(area, addr, val); else if (area->areawrite) if (md == md_cpu) BYTE(area->areawrite, (addr & (AREASIZE - 1))) = val; else FLAT_BYTE(area->areawrite, (addr & (AREASIZE - 1))) = val; } /***********************************************************************/ MemoryEntry *mementlist; /* active memory list */ /* user directory lists */ char *modulespath, *romspath, *ramspath; // speech.c references romspath /* system directory lists */ char *systemmodulespath, *systemromspath, *systemramspath; #define PATH_FOR_ENT(ent) ((ent->flags & MEMENT_STORED) == MEMENT_STORED ? ramspath : \ (ent->addr == 0) ? romspath : \ modulespath) #define SYS_PATH_FOR_ENT(ent) ((ent->flags & MEMENT_STORED) == MEMENT_STORED ? systemramspath : \ (ent->addr == 0) ? systemromspath : \ systemmodulespath) #define CPU_MEM_ENT(ent) (((ent->flags & MEMENT_DOMAIN) == MEMENT_CONSOLE)) u8 modulerom[16384]; static void memory_dump_list(const char *msg) { MemoryEntry *lst; lst = mementlist; logger(LOG_USER, "\nMemory map (%s):\n", msg); while (lst) { logger(LOG_USER, "'%s': Addr: >%04X, Offs: >%04X, Size: >%04X (>%04X)\n", lst->name, lst->addr, lst->offs, lst->realsize, lst->size); if (lst->filename) logger(LOG_USER, "\tFilename: '%s'\n", lst->filename); logger(LOG_USER, "\tFlags:"); if ((lst->flags & MEMENT_STORED) == MEMENT_STORED) logger( LOG_USER, "STORED, "); else if (lst->flags & MEMENT_RAM) logger( LOG_USER, "RAM, "); else logger( LOG_USER, "ROM, "); if (lst->flags & MEMENT_BANK_1) logger( LOG_USER, "BANK_1, "); else if (lst->flags & MEMENT_BANK_2) logger( LOG_USER, "BANK_2, "); if (lst->flags & MEMENT_CART) logger( LOG_USER, "CART, "); switch (lst->flags & MEMENT_DOMAIN) { case MEMENT_CONSOLE: logger( LOG_USER, "CONSOLE\n"); break; case MEMENT_GRAPHICS: logger( LOG_USER, "GRAPHICS\n"); break; case MEMENT_VIDEO: logger( LOG_USER, "VIDEO\n"); break; case MEMENT_SPEECH: logger( LOG_USER, "SPEECH\n"); break; // case MEMENT_DSR: logger( LOG_USER, "DSR\n"); break; default: logger(LOG_INTERNAL, "invalid MEMENT_xxx: %d\n", (lst->flags & MEMENT_DOMAIN)>>MEMENT_DOMAIN_SHIFT); break; } lst = lst->next; } } /* Free memory list */ void memory_free_list(void) { MemoryEntry *lst = mementlist, *prev = 0L; while (lst) { lst = memory_remove_entry_from_list(prev, lst); } mementlist = NULL; } /* Initialize memory list */ int memory_init_list(void) { memory_free_list(); return 1; } /* Remove an entry from the memory map */ MemoryEntry * memory_remove_entry_from_list(MemoryEntry *prev, MemoryEntry *ent) { MemoryEntry *ret; if (prev) prev->next = ret = ent->next; else mementlist = ret = ent->next; if (ent->flags & MEMENT_USER) memory_destroy_entry(ent); else xfree(ent); if (log_level(LOG_MEMORY) > 1) memory_dump_list("memory_remove_entry_from_list"); return ret; } /* Add entry to memory map and destroy entries this overrides */ int memory_add_entry_to_list(MemoryEntry *ent) { MemoryEntry **lstptr = &mementlist, *lst, *prev = 0L; int ret = 1; /* if size is unknown, assume it covers everything */ if (!ent->realsize) { my_assert(ent->size > 0); ent->realsize = ent->size; } while (*lstptr) { lst = *lstptr; /* This magic tells us that we need to obliterate some memory segment already here. The last two lines say: two different banks can coexist, but banking stuff overrides non-banking stuff, and v.v. */ if ((lst->flags & MEMENT_DOMAIN) == (ent->flags & MEMENT_DOMAIN) && lst->addr >= ent->addr && lst->addr + lst->realsize <= ent->addr + ent->realsize && ((lst->flags & MEMENT_BANKING) == (ent->flags & MEMENT_BANKING) || ((lst->flags ^ ent->flags) & MEMENT_BANKING) != MEMENT_BANKING)) { /* entry will be destroyed or split, save first */ /*ret &=*/ memory_save_entry(lst); /* clear out memory map for new item */ ent->realsize = ent->realsize; memory_unmap_entry(ent); /* split entry if necessary */ if (ent->addr > lst->addr && ent->addr + ent->realsize < lst->addr + lst->realsize) { MemoryEntry *nw = (MemoryEntry *)xmalloc(sizeof(MemoryEntry)); *nw = *lst; /* lst is first chunk */ lst->realsize = ent->addr - lst->addr; /* nw is last chunk */ nw->realsize -= lst->realsize + ent->realsize; nw->offs += (ent->addr + ent->realsize) - nw->addr; nw->addr += ent->addr + ent->realsize; lst->addr = ent->addr + ent->realsize; logger(_L | L_2, "Splitting entry '%s' into >%04X (>%04X), " ">%04X (>%04X), and >%04X (>%04X)\n", lst->name, lst->addr, lst->realsize, nw->addr, nw->realsize, ent->addr, ent->realsize); } else if (lst->addr < ent->addr && lst->realsize > ent->realsize) { /* new entry covers tail of old entry */ lst->realsize = ent->addr - lst->addr; logger(_L | L_2, "Shrinking entry '%s' to >%04X (>%04X)\n", lst->name, lst->addr, lst->realsize); } else if (lst->realsize > ent->realsize) { /* new entry covers front of old entry */ int shrink = ent->realsize; lst->offs += (ent->addr + ent->realsize) - lst->addr; lst->realsize -= shrink; lst->addr = ent->addr + ent->realsize; logger(_L | L_2, "Shrinking entry '%s' to >%04X (>%04X)\n", lst->name, lst->addr, lst->realsize); } else { /* old entry completely overriden */ logger(_L | L_2, "Overriding old entry '%s' at >%04X (>%04X)\n", lst->name, lst->addr, lst->realsize); lst = memory_remove_entry_from_list(prev, lst); lstptr = &lst; /* prev is the same */ continue; } } prev = lst; lstptr = &lst->next; } *lstptr = ent; (*lstptr)->next = 0L; return ret; } /* Save an entry in the memory map to disk */ int memory_save_entry(MemoryEntry *ent) { if ((ent->flags & MEMENT_STORED) == MEMENT_STORED) { my_assert(ent->memact.arearead == ent->memact.areawrite && ent->memact.arearead != NULL && ent->filename); logger(_L | LOG_USER, "Saving memory image to '%s'\n", ent->filename); return savebinary(ramspath, systemramspath, "RAM", ent->filename, ent->fileoffs + ent->offs, ent->memact.areawrite + ent->offs, CPU_MEM_ENT(ent), ent->realsize - ent->offs); } else { /* others are ROM binaries or empty */ return 1; } } /* Load an entry in the memory map from disk */ int memory_load_entry(MemoryEntry *ent) { if ((ent->flags & MEMENT_STORED) == MEMENT_STORED) { my_assert(ent->memact.arearead == ent->memact.areawrite && ent->memact.arearead != NULL && ent->filename && ent->size > 0); logger(_L | LOG_USER, "Restoring memory image from '%s'\n", ent->filename); /* okay for this to fail, the first time at least */ ent->realsize = loadbinary(ramspath, systemramspath, ent->filename, "RAM", ent->memact.areawrite + ent->offs, CPU_MEM_ENT(ent), ent->offs + ent->fileoffs, ent->size - ent->offs, ent->size); if (!ent->realsize) { ent->realsize = ent->size; logger(_L | LOG_USER, "Image '%s' ('%s') not found, resetting to zero\n", ent->filename, ent->name); memset(ent->memact.areawrite, 0, ent->realsize); } } else if (!(ent->flags & MEMENT_RAM)) { /* ROM */ if (ent->filename && ent->memact.arearead && ent->realsize) { logger(_L | L_1, "Reading ROM image '%s' at %d to >%04X, size %d\n", ent->name, ent->fileoffs, ent->addr, ent->size); if (!loadbinary( PATH_FOR_ENT(ent), SYS_PATH_FOR_ENT(ent), ent->filename, "ROM", ent->memact.arearead + ent->offs, CPU_MEM_ENT(ent), ent->fileoffs + ent->offs, ent->realsize - ent->offs, ent->size < 0 ? -ent->size : ent->size == 0 ? 0x10000 - ent->addr : ent->size)) return 0; } } else { /* zero memory (RAM or empty ROM) */ ent->realsize = ent->size < 0 ? 0x10000 - ent->addr : ent->size; logger(_L | L_2, "Zeroes for '%s' at >%04X, size %d\n", ent->name, ent->addr, ent->realsize); } return 1; } /* Unmap an entry from the memory map */ void memory_unmap_entry(MemoryEntry *ent) { /* reset affected memory */ logger(_L | L_2, "Resetting memory area from >%04X to >%04X\n", ent->addr, ent->addr+ent->realsize-1); set_area_handler((ent->flags & MEMENT_DOMAIN) >> MEMENT_DOMAIN_SHIFT, ent->addr, ent->realsize, &zero_memory_handler); } mrstruct *memory_module_bank_handlers[2]; int memory_module_bank; void memory_set_module_bank(u8 bank) { if (bank > 1 || !memory_module_bank_handlers[0] || !memory_module_bank_handlers[1]) return; //logger(LOG_INTERNAL, "memory_set_module_bank: Invalid bank or call to set %d\n", bank); else { SET_AREA_HANDLER(0x6000, 0x2000, memory_module_bank_handlers[bank]); memory_module_bank = bank; } } static void bwmrom_bank(const mrstruct *mr, u32 addr, s8 val) { memory_set_module_bank((addr & 2) >> 1); } /* Map an entry into the memory map */ int memory_map_entry(MemoryEntry *ent) { if (!(ent->flags & MEMENT_RAM)) { /* fudge stuff for the module banks */ if (ent->addr == 0x6000 && (ent->flags & MEMENT_DOMAIN) == MEMENT_CONSOLE) { if (ent->flags & MEMENT_BANK_1) { ent->memact.write_byte = bwmrom_bank; memory_module_bank_handlers[0] = &ent->memact; } else if (ent->flags & MEMENT_BANK_2) { ent->memact.write_byte = bwmrom_bank; memory_module_bank_handlers[1] = &ent->memact; } } } /* setup memory routines for this entry */ set_area_handler((ent->flags & MEMENT_DOMAIN) >> MEMENT_DOMAIN_SHIFT, ent->addr, ent->realsize, &ent->memact); return 1; } /* Save volatile memory associated with loaded module */ void memory_volatile_save(void) { MemoryEntry *lst = mementlist; while (lst) { if ((lst->flags & MEMENT_STORED) == MEMENT_STORED) { memory_save_entry(lst); } lst = lst->next; } } /* Load volatile memory associated with loaded module */ int memory_volatile_load(void) { MemoryEntry *lst = mementlist; while (lst) { if ((lst->flags & MEMENT_STORED) == MEMENT_STORED) { // if ((lst->flags & MEMENT_RAM) == 0) { if (!memory_load_entry(lst)) return 0; } lst = lst->next; } return 1; } /* Load all memory associated with loaded module */ int memory_complete_load(void) { MemoryEntry *lst = mementlist; while (lst) { if ((lst->flags & MEMENT_RAM) == 0) { if (!memory_load_entry(lst)) return 0; } lst = lst->next; } return 1; } /* 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) { MemoryEntry *nw = (MemoryEntry *)xmalloc(sizeof(MemoryEntry)); OSSpec tmpspec; memset((void *)nw, 0, sizeof(MemoryEntry)); my_assert(addr >= 0 && addr < PHYSMEMORYSIZE && !(addr & (AREASIZE-1)) && !(size & (AREASIZE-1)) && size >= -PHYSMEMORYSIZE && size <= PHYSMEMORYSIZE && !((flags & MEMENT_RAM) && (size <= 0))); nw->flags = flags; nw->fileoffs = fileoffs; nw->offs = 0; nw->addr = addr; nw->size = size; nw->realsize = 0; nw->filename = filename ? xstrdup(filename) : NULL; nw->name = name ? xstrdup(name) : NULL; nw->next = 0L; if (nw->filename) { nw->realsize = findbinary( PATH_FOR_ENT(nw), SYS_PATH_FOR_ENT(nw), nw->filename, &tmpspec); if (!nw->realsize && !(nw->flags & MEMENT_RAM)) { logger(_L | LOG_USER | LOG_ERROR, "Can't locate '%s'\n", nw->filename); return 0; } // if (!nw->size && nw->realsize) { // nw->size = nw->realsize; // } /* for large files selected, e.g., by accident */ if (nw->size > 0 && nw->realsize > nw->size) { nw->realsize = nw->size; } else if (nw->size < 0 && nw->realsize > -nw->size) { nw->realsize = -nw->size; } else if (nw->realsize + nw->addr > 0x10000) { nw->realsize = 0x10000 - nw->addr; } } if (memact) { nw->memact = *memact; } else { my_assert(nw->flags & MEMENT_USER); nw->memact.areamemory = (u8 *)xmalloc(nw->realsize); nw->memact.arearead = nw->memact.areamemory; if (nw->flags & MEMENT_RAM) nw->memact.areawrite = nw->memact.areamemory; else nw->memact.areawrite = 0L; } return nw; } void memory_destroy_entry(MemoryEntry *ent) { if (ent->name) xfree(ent->name); if (ent->filename) xfree(ent->filename); if ((ent->flags & MEMENT_USER) && ent->memact.areamemory) xfree(ent->memact.areamemory); xfree(ent); } /* Do it all */ int memory_insert_new_entry(int flags, u32 addr, s32 size, char *name, char *filename, u32 fileoffs, mrstruct *memact) { MemoryEntry *ent = memory_new_entry(flags, addr, size, name, filename, fileoffs, memact); return (ent && memory_add_entry_to_list(ent) && memory_load_entry(ent) && memory_map_entry(ent)); } DECL_SYMBOL_ACTION(memory_define_entry) { char *flagptr, *fnameptr, *nameptr; MemoryEntry *nw; long flags; int addr, size, fileoffs; int state; if (task == csa_READ) { char buf[32], *bptr = buf; static MemoryEntry *ptr; if (iter == 0) { ptr = mementlist; } // the non-user memory items are available by // default; the cartridge items are defined by // ReplaceModule (which also gives the name of the // cart for the UI) while (ptr && ((ptr->flags & MEMENT_USER) == 0 || (ptr->flags & MEMENT_CART) != 0)) ptr = ptr->next; if (ptr == 0L) { return 0; } if ((ptr->flags & MEMENT_STORED) == MEMENT_STORED) *bptr++ = 'S'; else if (ptr->flags & MEMENT_RAM) *bptr++ = 'W'; else *bptr++ = 'R'; if (ptr->flags & MEMENT_CART) *bptr++ = 'M'; if (ptr->flags & MEMENT_BANK_1) *bptr++ = '1'; else if (ptr->flags & MEMENT_BANK_2) *bptr++ = '2'; switch (ptr->flags & MEMENT_DOMAIN) { case MEMENT_CONSOLE: *bptr++ = 'C'; break; case MEMENT_GRAPHICS: *bptr++ = 'G'; break; case MEMENT_VIDEO: *bptr++ = 'V'; break; case MEMENT_SPEECH: *bptr++ = 'S'; break; // case MEMENT_DSR: *bptr++ = 'D'; break; default: logger(LOG_INTERNAL | LOG_FATAL, "invalid MEMENT_xxx: %d\n", (ptr->flags & MEMENT_DOMAIN)>>MEMENT_DOMAIN_SHIFT); break; } *bptr = 0; command_arg_set_string(SYM_ARG_1st, buf); command_arg_set_num(SYM_ARG_2nd, ptr->addr); command_arg_set_num(SYM_ARG_3rd, ptr->size); command_arg_set_string(SYM_ARG_4th, ptr->filename); command_arg_set_num(SYM_ARG_5th, ptr->fileoffs); command_arg_set_string(SYM_ARG_6th, ptr->name); ptr = ptr->next; return 1; } // task == csa_WRITE command_arg_get_string(SYM_ARG_1st, &flagptr); command_arg_get_num(SYM_ARG_2nd, &addr); command_arg_get_num(SYM_ARG_3rd, &size); command_arg_get_string(SYM_ARG_4th, &fnameptr); command_arg_get_num(SYM_ARG_5th, &fileoffs); command_arg_get_string(SYM_ARG_6th, &nameptr); flags = MEMENT_USER; state = 0; while (*flagptr) { if (state == 0) { switch (toupper(*flagptr)) { case 'S': flags |= MEMENT_STORED; break; case 'W': flags |= MEMENT_RAM; break; case 'R': flags &= ~MEMENT_RAM; break; default: logger(_L | LOG_ERROR | LOG_USER, "Unknown memory flag '%c' (at '%s')\n", *flagptr, flagptr); return 0; } state++; } else { switch (toupper(*flagptr)) { case 'M': { // this is such a hack. if we don't clear it // here, we end up generating session files that // might have invalid "LoadModule"/"ReplaceModule" // lines, if v9t9.cnf has a "LoadModule" line, // a session file is loaded without a "LoadModule" // line, and it is saved again. extern void *loaded_module; flags |= MEMENT_CART; loaded_module = 0L; break; } case '1': flags = (flags & ~MEMENT_BANK_2) | MEMENT_BANK_1; break; case '2': flags = (flags & ~MEMENT_BANK_1) | MEMENT_BANK_2; break; case 'C': flags = (flags & ~MEMENT_DOMAIN) | MEMENT_CONSOLE; break; case 'G': flags = (flags & ~MEMENT_DOMAIN) | MEMENT_GRAPHICS; break; case 'V': flags = (flags & ~MEMENT_DOMAIN) | MEMENT_VIDEO; break; case 'S': flags = (flags & ~MEMENT_DOMAIN) | MEMENT_SPEECH; break; // case 'D': flags = (flags & ~MEMENT_DOMAIN) | MEMENT_DSR; break; default: logger(_L | LOG_ERROR | LOG_USER, "Unknown memory flag '%c' (at '%s')\n", *flagptr, flagptr); return 0; } } flagptr++; } if (addr < 0 || addr >= 0x10000) { logger(_L | LOG_ERROR | LOG_USER, "Illegal address (>%04x) for entry\n", addr); return 0; } if (addr & 0x1fff) { if (addr & (AREASIZE-1)) { logger(_L | LOG_ERROR | LOG_USER, "Address is impossible to use, " "minimum granularity is >%04x bytes\n", AREASIZE); return 0; } else { logger(_L | LOG_WARN | LOG_USER, "An address should start on a multiple " "of >2000 bytes (got >%04x)...\n", addr); } } if (size < -0x10000 || size >= 0x10000) { logger(_L | LOG_ERROR | LOG_USER, "Illegal size (%d) for entry, " "should have magnitude between 0 and >FFFF\n", size); return 0; } if ((flags & MEMENT_RAM) && size <= 0) { logger(_L | LOG_ERROR | LOG_USER, "Illegal size for RAM entry, must be >= 0 (%d)\n", size); return 0; } if (size & 0x1fff) { if (size & (AREASIZE-1)) { logger(_L | LOG_ERROR | LOG_USER, "Entry is impossible to allocate, " "minimum granularity is >%04X bytes\n", AREASIZE); return 0; } else { logger(_L | LOG_WARN | LOG_USER, "Entry size should be a multiple " "of >2000 bytes (got >%04x)...\n", size); } } /// nw = memory_new_entry(flags, addr, size, nameptr, fnameptr, fileoffs, 0L); if (!nw) return 0; if (memory_add_entry_to_list(nw) && memory_load_entry(nw) && memory_map_entry(nw)) { if (log_level(LOG_MEMORY) > 1) memory_dump_list("memory_define_entry"); return 1; } else { return 0; } } DECL_SYMBOL_ACTION(memory_dump) { memory_dump_list("memory_dump"); return 1; } static void memory_mmio_init(void); DECL_SYMBOL_ACTION(memory_default_list) { memory_mmio_init(); memory_ram_init(); vdp_memory_init(); gpl_memory_init(); // does nothing currently speech_memory_init(); return 1; } /***********************************************************************/ void memory_ram_init(void) { // if (!low_expansion_memory_handler.areamemory) // low_expansion_memory_handler.areamemory = (u8 *)xmalloc(0x2000); memory_insert_new_entry(MEMENT_CONSOLE | MEMENT_RAM, 0x2000, 0x2000, "Low expansion RAM", 0L /*filename*/, 0L /*fileoffs*/, &low_expansion_memory_handler); // if (!std_console_ram_handler.areamemory) // std_console_ram_handler.areamemory = (u8 *)xmalloc(0x400); if (!isenhconsoleram) memory_insert_new_entry(MEMENT_CONSOLE | MEMENT_RAM, 0x8000, 0x0400, "Console RAM (256 bytes)", 0L /*filename*/, 0L /*fileoffs*/, &std_console_ram_handler); else memory_insert_new_entry(MEMENT_CONSOLE | MEMENT_RAM, 0x8000, 0x0400, "Console RAM (1K)", 0L /*filename*/, 0L /*fileoffs*/, &enh_console_ram_handler); // if (!high_expansion_memory_handler.areamemory) // high_expansion_memory_handler.areamemory = (u8 *)xmalloc(0x6000); memory_insert_new_entry(MEMENT_CONSOLE | MEMENT_RAM, 0xA000, 0x6000, "High expansion RAM", 0L /*filename*/, 0L /*fileoffs*/, &high_expansion_memory_handler); } static void memory_mmio_init(void) { memory_insert_new_entry(MEMENT_CONSOLE, 0x8400, 0x400, "Sound MMIO", 0L /*filename*/, 0L /*fileoffs*/, &console_sound_handler); memory_insert_new_entry(MEMENT_CONSOLE, 0x8800, 0x400, "VDP read MMIO", 0L /*filename*/, 0L /*fileoffs*/, &console_vdp_read_handler); memory_insert_new_entry(MEMENT_CONSOLE, 0x8C00, 0x400, "VDP write MMIO", 0L /*filename*/, 0L /*fileoffs*/, &console_vdp_write_handler); memory_insert_new_entry(MEMENT_CONSOLE, 0x9000, 0x400, "Speech read MMIO", 0L /*filename*/, 0L /*fileoffs*/, &console_speech_read_handler); memory_insert_new_entry(MEMENT_CONSOLE, 0x9400, 0x400, "Speech write MMIO", 0L /*filename*/, 0L /*fileoffs*/, &console_speech_write_handler); memory_insert_new_entry(MEMENT_CONSOLE, 0x9800, 0x400, "GROM read MMIO", 0L /*filename*/, 0L /*fileoffs*/, &console_grom_read_handler); memory_insert_new_entry(MEMENT_CONSOLE, 0x9C00, 0x400, "GROM/GRAM write MMIO", 0L /*filename*/, 0L /*fileoffs*/, &console_grom_write_handler); } void memory_init(void) { logger(_L | L_0, "Initializing memory...\n"); memset((void *)__areahandlers, 0, sizeof(__areahandlers)); memory_init_list(); memory_ram_init(); memory_mmio_init(); vdp_memory_init(); gpl_memory_init(); }