From 45f9ebfed919bab759ee329cffebda1b4de15b01 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Sun, 12 May 2024 23:08:51 +0200 Subject: [PATCH] MEGA65: new memory decoder #209 #378 This quite big change rewrites the MEGA65 emulation memory decoder subsystem. Some of the reasons: * Create a new, not overcomplicated decoder which can be understood at all without major headache, and can be also faster as per #209 * Taking account some new findings about how C65 worked (both Xemu and MEGA65 was wrong here) and adopted since then by MEGA65 as per #378 * More about the future (not ready in this commit): allow functionality for debugger implementation "watchmem" which can monitor about every memory r/w events debugger want to check as per #11 This commit is quite big as was mentioned, and may introduce some regressions. So it's important to have feedback in those cases. Also it changes the behaviour of memory decoding (#378). The new decoder seems to be already faster than the old one and more easy to understand and maintain. Also there is some room for future improvements still in every areas, including optimizations. The "memwatch" part currently is not usable though, but the basis are there to be able to implement that at all (wouldn't be possible with the older scheme). --- targets/mega65/cpu_custom_functions.h | 122 +- targets/mega65/dma65.c | 6 +- targets/mega65/dma65.h | 2 +- targets/mega65/hdos.c | 8 +- targets/mega65/hdos.h | 2 +- targets/mega65/hypervisor.c | 71 +- targets/mega65/hypervisor.h | 4 +- targets/mega65/io_mapper.c | 14 +- targets/mega65/io_mapper.h | 2 +- targets/mega65/matrix_mode.c | 20 +- targets/mega65/mega65.c | 42 +- targets/mega65/memory_mapper.c | 1618 ++++++++++++++----------- targets/mega65/memory_mapper.h | 63 +- targets/mega65/sdcard.c | 5 +- targets/mega65/sdcard.h | 2 +- targets/mega65/ui.c | 4 +- targets/mega65/vic4.c | 21 +- targets/mega65/vic4.h | 5 +- 18 files changed, 1108 insertions(+), 903 deletions(-) diff --git a/targets/mega65/cpu_custom_functions.h b/targets/mega65/cpu_custom_functions.h index 48c1fed1..0b39c3cc 100644 --- a/targets/mega65/cpu_custom_functions.h +++ b/targets/mega65/cpu_custom_functions.h @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2022 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,49 +25,37 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __XEMU_MEGA65_CPU_CUSTOM_FUNCTIONS_H_INCLUDED #define __XEMU_MEGA65_CPU_CUSTOM_FUNCTIONS_H_INCLUDED -#ifdef CPU_CUSTOM_MEMORY_FUNCTIONS_H -#define CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR static XEMU_INLINE -#else -#ifndef ALLOW_CPU_CUSTOM_FUNCTIONS_INCLUDE -#error "cpu_custom_functions.h must not be included by anything other than the CPU emulator and memory_mapper.c" -#endif -#define CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR -#endif +#define MEM_USE_DATA_POINTERS -#if 1 -#define CALL_MEMORY_READER(slot,addr) mem_page_rd_f[slot](mem_page_rd_o[slot] + ((addr) & 0xFF)) -#define CALL_MEMORY_WRITER(slot,addr,data) mem_page_wr_f[slot](mem_page_wr_o[slot] + ((addr) & 0xFF), data) -#define CALL_MEMORY_READER_PAGED(slot,addr) mem_page_rd_f[slot](mem_page_rd_o[slot] + addr) -#define CALL_MEMORY_WRITER_PAGED(slot,addr,data) mem_page_wr_f[slot](mem_page_wr_o[slot] + addr, data) -#define SAVE_USED_SLOT(slot) last_slot_ref = slot -#define MEMORY_HANDLERS_ADDR_TYPE int area_offset -#define GET_READER_OFFSET() area_offset -#define GET_WRITER_OFFSET() area_offset -#define GET_OFFSET_BYTE_ONLY() area_offset -#define GET_USED_SLOT() last_slot_ref +#ifdef MEM_USE_DATA_POINTERS +#define MEM_DATA_POINTER_HINTING_STRENGTH(_condition) XEMU_LIKELY(_condition) +//#define MEM_DATA_POINTER_HINTING_STRENGTH(_condition) (_condition) #endif -#if 0 -#define CALL_MEMORY_READER(slot,addr) mem_page_rd_f[slot](slot, addr) -#define CALL_MEMORY_WRITER(slot,addr,data) mem_page_wr_f[slot](slot, addr, data) -#define CALL_MEMORY_READER_PAGED(slot,addr) mem_page_rd_f[slot](slot, addr) -#define CALL_MEMORY_WRITER_PAGED(slot,addr,data) mem_page_wr_f[slot](slot, addr, data) -#define SAVE_USED_SLOT(slot) -#define MEMORY_HANDLERS_ADDR_TYPE int slot, Uint8 lo_addr -#define GET_READER_OFFSET() (mem_page_rd_o[slot] + lo_addr) -#define GET_WRITER_OFFSET() (mem_page_wr_o[slot] + lo_addr) -#define GET_OFFSET_BYTE_ONLY() lo_addr -#define GET_USED_SLOT() slot +#ifdef CPU_CUSTOM_MEMORY_FUNCTIONS_H +# define CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR static XEMU_INLINE +#else +# ifndef ALLOW_CPU_CUSTOM_FUNCTIONS_INCLUDE +# error "cpu_custom_functions.h must not be included by anything other than the CPU emulator and memory_mapper.c" +# endif +# define CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR #endif +extern Uint32 ref_slot; + +typedef Uint8 (*mem_slot_rd_func_t)(const Uint32 addr32); +typedef void (*mem_slot_wr_func_t)(const Uint32 addr32, const Uint8 data); -typedef Uint8 (*mem_page_rd_f_type)(MEMORY_HANDLERS_ADDR_TYPE); -typedef void (*mem_page_wr_f_type)(MEMORY_HANDLERS_ADDR_TYPE, Uint8 data); +#define MEM_SLOTS_TOTAL_EXPORTED 0x106 +extern mem_slot_rd_func_t mem_slot_rd_func[MEM_SLOTS_TOTAL_EXPORTED]; +extern mem_slot_wr_func_t mem_slot_wr_func[MEM_SLOTS_TOTAL_EXPORTED]; +extern Uint32 mem_slot_rd_addr32[MEM_SLOTS_TOTAL_EXPORTED]; +extern Uint32 mem_slot_wr_addr32[MEM_SLOTS_TOTAL_EXPORTED]; +#ifdef MEM_USE_DATA_POINTERS +extern Uint8 *mem_slot_rd_data[MEM_SLOTS_TOTAL_EXPORTED]; +extern Uint8 *mem_slot_wr_data[MEM_SLOTS_TOTAL_EXPORTED]; +#endif -extern int mem_page_rd_o[]; -extern int mem_page_wr_o[]; -extern mem_page_rd_f_type mem_page_rd_f[]; -extern mem_page_wr_f_type mem_page_wr_f[]; extern int cpu_rmw_old_data; extern void cpu65_write_linear_opcode_callback ( Uint8 data ); @@ -77,20 +65,33 @@ extern Uint32 cpu65_read_linear_long_opcode_callback ( const Uint8 index ); extern void cpu65_illegal_opcode_callback ( void ); -extern int memory_cpurd2linear_xlat ( Uint16 cpu_addr); +extern Uint32 memory_cpu_addr_to_linear ( const Uint16 cpu_addr, Uint32 *wr_addr_p ); +#define memory_cpurd2linear_xlat(_cpu_addr) memory_cpu_addr_to_linear(_cpu_addr,NULL) -CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR Uint8 cpu65_read_callback ( Uint16 addr ) { - return CALL_MEMORY_READER(addr >> 8, addr); -} -CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR void cpu65_write_callback ( Uint16 addr, Uint8 data ) { - CALL_MEMORY_WRITER(addr >> 8, addr, data); -} -CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR Uint8 cpu65_read_paged_callback ( Uint8 page, Uint8 addr8 ) { - return CALL_MEMORY_READER_PAGED(page, addr8); +CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR Uint8 cpu65_read_callback ( const Uint16 addr16 ) +{ +#ifdef MEM_USE_DATA_POINTERS + register const Uint8 *p = mem_slot_rd_data[addr16 >> 8]; + if (MEM_DATA_POINTER_HINTING_STRENGTH(p)) + return p[addr16 & 0xFFU]; +#endif + ref_slot = addr16 >> 8; + return mem_slot_rd_func[ref_slot](mem_slot_rd_addr32[ref_slot] + (addr16 & 0xFFU)); } -CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR void cpu65_write_paged_callback ( Uint8 page, Uint8 addr8, Uint8 data ) { - CALL_MEMORY_WRITER_PAGED(page, addr8, data); + +CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR void cpu65_write_callback ( const Uint16 addr16, const Uint8 data ) +{ +#ifdef MEM_USE_DATA_POINTERS + register Uint8 *p = mem_slot_wr_data[addr16 >> 8]; + if (MEM_DATA_POINTER_HINTING_STRENGTH(p)) { + p[addr16 & 0xFFU] = data; + return; + } +#endif + ref_slot = addr16 >> 8; + mem_slot_wr_func[ref_slot](mem_slot_wr_addr32[ref_slot] + (addr16 & 0xFFU), data); } + // Called in case of an RMW (read-modify-write) opcode write access. // Original NMOS 6502 would write the old_data first, then new_data. // It has no inpact in case of normal RAM, but it *does* with an I/O register in some cases! @@ -98,19 +99,26 @@ CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR void cpu65_write_paged_callback ( Uint8 p // However this leads to incompatibilities, as some software used the RMW behavour by intent. // Thus MEGA65 fixed the problem to "restore" the old way of RMW behaviour. // I also follow this path here, even if it's *NOT* what 65CE02 would do actually! -CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR void cpu65_write_rmw_callback ( Uint16 addr, Uint8 old_data, Uint8 new_data ) { +CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR void cpu65_write_rmw_callback ( const Uint16 addr16, const Uint8 old_data, const Uint8 new_data ) +{ +// Nah! It seems, enabling this, makes some software _slower_ ... Like polymega. Probably it does lots of I/O writes, so this +// extra optimization just takes time to check which won't be used anyway in case of I/O. Oh, well. +#if 0 +#ifdef MEM_USE_DATA_POINTERS + // if there is data pointer, it cannot be I/O anyway, so the whole RMW business shouldn't matter here + register Uint8 *p = mem_slot_wr_data[addr16 >> 8]; + if (p) { + p[addr16 & 0xFFU] = new_data; + return; + } +#endif +#endif cpu_rmw_old_data = old_data; // It's the backend's (which realizes the op) responsibility to handle or not handle the RMW behaviour, // based on the fact if cpu_rmw_old_data is non-negative (being an int type) when it holds the "old_data". - CALL_MEMORY_WRITER(addr >> 8, addr, new_data); + ref_slot = addr16 >> 8; + mem_slot_wr_func[ref_slot](mem_slot_wr_addr32[ref_slot] + (addr16 & 0xFFU), new_data); cpu_rmw_old_data = -1; } -CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR void cpu65_write_rmw_paged_callback ( Uint8 page, Uint8 addr8, Uint8 old_data, Uint8 new_data ) { - cpu_rmw_old_data = old_data; - CALL_MEMORY_WRITER_PAGED(page, addr8, new_data); - cpu_rmw_old_data = -1; -} - -#undef CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR #endif diff --git a/targets/mega65/dma65.c b/targets/mega65/dma65.c index a5d1d515..053bbfdb 100644 --- a/targets/mega65/dma65.c +++ b/targets/mega65/dma65.c @@ -1,6 +1,6 @@ /* F018 DMA core emulation for MEGA65 Part of the Xemu project. https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -87,13 +87,13 @@ static struct { static inline Uint8 io_dma_reader ( const unsigned int addr ) { - return io_read((addr & 0xFFFU) + (vic_iomode << 12)); + return io_read((addr & 0xFFFU) + (io_mode << 12)); } static inline void io_dma_writer ( const unsigned int addr, Uint8 data ) { - io_write((addr & 0xFFFU) + (vic_iomode << 12), data); + io_write((addr & 0xFFFU) + (io_mode << 12), data); } diff --git a/targets/mega65/dma65.h b/targets/mega65/dma65.h index ae1ed6ca..8eb89e9d 100644 --- a/targets/mega65/dma65.h +++ b/targets/mega65/dma65.h @@ -1,6 +1,6 @@ /* F018 DMA core emulation for MEGA65 Part of the Xemu project. https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/targets/mega65/hdos.c b/targets/mega65/hdos.c index 4b4187f9..e4aacd7f 100644 --- a/targets/mega65/hdos.c +++ b/targets/mega65/hdos.c @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -97,7 +97,7 @@ static int copy_mem_from_user ( Uint8 *target, int max_size, const int terminato // DOS calls should not have user specified data >= $8000! if (source_cpu_addr >= 0x8000) return -1; - const Uint8 byte = memory_debug_read_cpu_addr(source_cpu_addr++); + const Uint8 byte = debug_read_cpu_byte(source_cpu_addr++); *target++ = byte; len++; if (len >= max_size) { @@ -124,7 +124,7 @@ static int copy_mem_to_user ( unsigned int target_cpu_addr, const Uint8 *source, while (size) { if (target_cpu_addr >= 0x8000) return -1; - memory_debug_write_cpu_addr(target_cpu_addr++, *source++); + debug_write_cpu_byte(target_cpu_addr++, *source++); size--; len++; } @@ -465,7 +465,7 @@ static void hdos_virt_loadfile ( const Uint32 addr_base ) break; } for (const Uint8 *b = buffer; ret > 0; ret--, b++, addr_ofs++) - memory_debug_write_phys_addr(addr_base + (addr_ofs & 0xFFFFFF), *b); + debug_write_linear_byte(addr_base + (addr_ofs & 0xFFFFFF), *b); } close(fd); if (ret < 0 || loaded != st.st_size) { diff --git a/targets/mega65/hdos.h b/targets/mega65/hdos.h index 6f54d3ed..dbe8c9fe 100644 --- a/targets/mega65/hdos.h +++ b/targets/mega65/hdos.h @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/targets/mega65/hypervisor.c b/targets/mega65/hypervisor.c index 47b2da61..6dca5ed3 100644 --- a/targets/mega65/hypervisor.c +++ b/targets/mega65/hypervisor.c @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -37,7 +37,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include -int in_hypervisor; // mega65 hypervisor mode +bool in_hypervisor; // mega65 hypervisor mode char hyppo_version_string[64]; int hickup_is_overriden = 0; int hypervisor_is_debugged = 0; @@ -108,7 +108,7 @@ void hypervisor_enter_via_write_trap ( int trapno ) if (do_nop_check) { // FIXME: for real there should be a memory reading function independent to the one used by the CPU, since // this has some side effects to just fetch a byte to check something, which is otherwise used normally to fetch CPU opcodes and such - Uint8 skipped_byte = cpu65_read_callback(cpu65.pc); + Uint8 skipped_byte = debug_read_cpu_byte(cpu65.pc); if (XEMU_UNLIKELY(skipped_byte != 0xEA && skipped_byte != 0xB8)) { // $EA = opcode for NOP, $B8 = opcode for CLV char msg[256]; snprintf(msg, sizeof msg, @@ -160,27 +160,26 @@ void hypervisor_enter ( int trapno ) D6XX_registers[0x4F] = map_megabyte_high >> 20; D6XX_registers[0x50] = memory_get_cpu_io_port(0); D6XX_registers[0x51] = memory_get_cpu_io_port(1); - D6XX_registers[0x52] = vic_iomode; + D6XX_registers[0x52] = io_mode; //D6XX_registers[0x53] = 0; // GS $D653 - Hypervisor DMAgic source MB - *UNUSED* //D6XX_registers[0x54] = 0; // GS $D654 - Hypervisor DMAgic destination MB - *UNUSED* dma_get_list_addr_as_bytes(D6XX_registers + 0x55); // GS $D655-$D658 - Hypervisor DMAGic list address bits 27-0 - // Now entering into hypervisor mode - in_hypervisor = 1; // this will cause apply_memory_config to map hypervisor RAM, also for checks later to out-of-bound execution of hypervisor RAM, etc ... - // In hypervisor mode, VIC4 I/O mode is implied. I also disable $D02F writing to take effect while in hypervisor mode in vic4.c! - vic_iomode = VIC4_IOMODE; - memory_set_cpu_io_port_ddr_and_data(0x3F, 0x35); // sets all-RAM + I/O config up! + // Now entering into hypervisor mode: we use memory_reconfigure() to set all the stuff needed + setting up "in_hypervisor" value as well + memory_reconfigure( + 0, // D030 ROM banking turning off + VIC4_IOMODE, // VIC4 I/O mode to be used (on MEGA65, in hypervisor mode it's always the case! the handler in vic4.c ensures, we cannot even modify this) + 0x3F, 0x35, // set CPU I/O port DDR+DATA: all-RAM + I/O config + map_megabyte_low, map_offset_low, // low mapping is left as-is + 0xFFU << 20, 0xF0000U, // high mapping though is being modified + (map_mask & 0xFU) | 0x30U, // mapping: 0011XXXX (it seems low region map mask is not changed by hypervisor entry) + true // this will sets in_hypervisor to TRUE!!!! + ); cpu65.pf_d = 0; // clear decimal mode ... according to Paul, punnishment will be done, if it's removed :-) cpu65.pf_i = 1; // disable IRQ in hypervisor mode cpu65.pf_e = 1; // 8 bit stack in hypervisor mode cpu65.sphi = 0xBE00; // set a nice shiny stack page cpu65.bphi = 0xBF00; // ... and base page (aka zeropage) cpu65.s = 0xFF; - // Set mapping for the hypervisor - map_mask = (map_mask & 0xF) | 0x30; // mapping: 0011XXXX (it seems low region map mask is not changed by hypervisor entry) - map_megabyte_high = 0xFF << 20; - map_offset_high = 0xF0000; - memory_set_vic3_rom_mapping(0); // for VIC-III rom mapping disable in hypervisor mode - memory_set_do_map(); // now the memory mapping is changed machine_set_speed(0); // set machine speed (hypervisor always runs at M65 fast ... ??) FIXME: check this! cpu65.pc = 0x8000 | (trapno << 2); // load PC with the address assigned for the given trap number DEBUG("HYPERVISOR: entering into hypervisor mode, trap=$%02X (A=$%02X) @ $%04X -> $%04X" NL, trapno, cpu65.a, D6XX_registers[0x48] | (D6XX_registers[0x49] << 8), cpu65.pc); @@ -255,7 +254,15 @@ void hypervisor_start_machine ( void ) hyppo_version_string[0] = '\0'; hdos_init(configdb.hdosvirt, configdb.hdosdir); } - in_hypervisor = 0; + memory_reconfigure( + 0, + VIC4_IOMODE, + 0x3F, 0x35, + 0, 0, + 0, 0, + 0, + false // this will set in_hypervisor to FALSE to ensure hypervisor_enter() won't fail if reset from hypervisor mode + ); hypervisor_queued_trap = -1; hypervisor_is_first_call = 1; execution_range_check_gate = 0; @@ -280,7 +287,7 @@ static inline void first_leave ( void ) cpu65.pc = new_pc; // Since we have overriden ROM, we also must take the responsibility to do what Hyppo would also do: // uploading chargen from the loaded ROM into the "char WOM". - memcpy(char_wom, main_ram + 0x2D000, 0x1000); + memcpy(char_ram, main_ram + 0x2D000, 0x1000); } else { DEBUGPRINT("ROM: no custom force-ROM policy, PC remains at $%04X" NL, cpu65.pc); } @@ -328,21 +335,23 @@ void hypervisor_leave ( void ) cpu65.pc = D6XX_registers[0x48] | (D6XX_registers[0x49] << 8); if (current_hdos_func >= 0) hdos_leave(current_hdos_func); - map_offset_low = ((D6XX_registers[0x4A] & 0xF) << 16) | (D6XX_registers[0x4B] << 8); - map_offset_high = ((D6XX_registers[0x4C] & 0xF) << 16) | (D6XX_registers[0x4D] << 8); - map_mask = (D6XX_registers[0x4A] >> 4) | (D6XX_registers[0x4C] & 0xF0); - map_megabyte_low = D6XX_registers[0x4E] << 20; - map_megabyte_high = D6XX_registers[0x4F] << 20; - memory_set_cpu_io_port_ddr_and_data(D6XX_registers[0x50], D6XX_registers[0x51]); - vic_iomode = D6XX_registers[0x52] & 3; + // Now leaving hypervisor mode: we use memory_reconfigure() to set all the stuff needed + setting up "in_hypervisor" value as well + memory_reconfigure( + vic_registers[0x30], // restore D030 ROM banking + D6XX_registers[0x52] & 3, // restore VIC4 I/O mode + D6XX_registers[0x50], D6XX_registers[0x51], // restore CPU I/O port DDR+DATA + D6XX_registers[0x4E] << 20, // restore MAP low megabyte + ((D6XX_registers[0x4A] & 0xF) << 16) | (D6XX_registers[0x4B] << 8), // restore MAP low offset + D6XX_registers[0x4F] << 20, // restore MAP high megabyte + ((D6XX_registers[0x4C] & 0xF) << 16) | (D6XX_registers[0x4D] << 8), // restore MAP high offset + (D6XX_registers[0x4A] >> 4) | (D6XX_registers[0x4C] & 0xF0), // restore MAP mask + false // this will sets in_hypervisor to FALSE!!!! + ); // GS $D653 - Hypervisor DMAgic source MB - *UNUSED* // GS $D654 - Hypervisor DMAgic destination MB - *UNUSED* dma_set_list_addr_from_bytes(D6XX_registers + 0x55); // GS $D655-$D658 - Hypervisor DMAGic list address bits 27-0 // Now leaving hypervisor mode ... - in_hypervisor = 0; machine_set_speed(0); // restore speed ... - memory_set_vic3_rom_mapping(vic_registers[0x30]); // restore possible active VIC-III mapping - memory_set_do_map(); // restore mapping ... if (XEMU_UNLIKELY(hypervisor_is_first_call)) { if (trap_current != TRAP_RESET) FATAL("First hypervisor TRAP is not RESET?!"); @@ -650,9 +659,9 @@ void hypervisor_debug ( void ) DEBUG("HYPERDEBUG: warning, execution in hypervisor memory without SPHI == $BE but $%02X" NL, cpu65.sphi >> 8); if (XEMU_UNLIKELY(cpu65.bphi != 0xBF00)) DEBUG("HYPERDEBUG: warning, execution in hypervisor memory without BPHI == $BF but $%02X" NL, cpu65.bphi >> 8); - // FIXME: remove this? Reason: this is not even possible as in hypervisor mode vic_iomode cannot be altered via the usual $D02F "KEY" register ... [if there are no bugs ...] - if (XEMU_UNLIKELY(vic_iomode != VIC4_IOMODE)) - DEBUG("HYPERDEBUG: warning, execution in hypervisor memory with VIC I/O mode of %X" NL, iomode_hexdigitids[vic_iomode]); + // FIXME: remove this? Reason: this is not even possible as in hypervisor mode io_mode cannot be altered via the usual $D02F "KEY" register ... [if there are no bugs ...] + if (XEMU_UNLIKELY(io_mode != VIC4_IOMODE)) + DEBUG("HYPERDEBUG: warning, execution in hypervisor memory with VIC I/O mode of %X" NL, iomode_hexdigitids[io_mode]); } const Uint16 now_sp = cpu65.sphi | cpu65.s; int sp_diff = (int)prev_sp - (int)now_sp; @@ -710,7 +719,7 @@ void hypervisor_debug ( void ) (pf & CPU65_PF_I) ? 'I' : 'i', (pf & CPU65_PF_Z) ? 'Z' : 'z', (pf & CPU65_PF_C) ? 'C' : 'c', - iomode_hexdigitids[vic_iomode], + iomode_hexdigitids[io_mode], within_hypervisor_ram ? debug_info[cpu65.pc - 0x8000].src_fn : "", within_hypervisor_ram ? debug_info[cpu65.pc - 0x8000].src_ln : 0, within_hypervisor_ram ? debug_info[cpu65.pc - 0x8000].sym_name : "", diff --git a/targets/mega65/hypervisor.h b/targets/mega65/hypervisor.h index 52721891..d1701b79 100644 --- a/targets/mega65/hypervisor.h +++ b/targets/mega65/hypervisor.h @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define TRAP_FREEZER_RESTORE_PRESS 0x42 #define TRAP_MATRIX 0x43 -extern int in_hypervisor; +extern bool in_hypervisor; extern int hickup_is_overriden; extern int hypervisor_is_debugged; extern char hyppo_version_string[64]; diff --git a/targets/mega65/io_mapper.c b/targets/mega65/io_mapper.c index 32cd4fbc..eec39773 100644 --- a/targets/mega65/io_mapper.c +++ b/targets/mega65/io_mapper.c @@ -1,7 +1,7 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu I/O decoding part (used by memory_mapper.h and DMA mainly) - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -82,6 +82,13 @@ static XEMU_INLINE void update_hw_multiplier ( void ) } +// Writes the colour RAM. !!ONLY!! use this, if it's in the range of the first 2K of the colour RAM though, or you will be in big trouble! +static XEMU_INLINE void write_colour_ram ( const Uint32 addr, const Uint8 data ) +{ + colour_ram[addr] = data; + main_ram[addr + 0x1F800U] = data; +} + /* Internal decoder for I/O reads. Address *must* be within the 0-$3FFF (!!) range. The low 12 bits is the actual address inside the I/O area, while the most significant nibble shows the I/O mode the operation is meant, according to the following table: @@ -458,10 +465,7 @@ void io_write ( unsigned int addr, Uint8 data ) DEBUG("MEGA65: enhanced opcodes have been turned %s." NL, data & 2 ? "ON" : "OFF"); cpu_mega65_opcodes = data & 2; } - if ((data & 4) != rom_protect) { - DEBUG("MEGA65: ROM protection has been turned %s." NL, data & 4 ? "ON" : "OFF"); - rom_protect = data & 4; - } + memory_set_rom_protection(!!(data & 4)); return; case 0x7E: D6XX_registers[0x7E] = 0xFF; // iomap.txt: "Hypervisor already-upgraded bit (sets permanently)" diff --git a/targets/mega65/io_mapper.h b/targets/mega65/io_mapper.h index d906ec69..bd21c71a 100644 --- a/targets/mega65/io_mapper.h +++ b/targets/mega65/io_mapper.h @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore-65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/targets/mega65/matrix_mode.c b/targets/mega65/matrix_mode.c index 5e5dc8ac..687defa1 100644 --- a/targets/mega65/matrix_mode.c +++ b/targets/mega65/matrix_mode.c @@ -271,7 +271,7 @@ static void dump_regs ( const char rot_fig ) (pf & CPU65_PF_I) ? 'I' : 'i', (pf & CPU65_PF_Z) ? 'Z' : 'z', (pf & CPU65_PF_C) ? 'C' : 'c', - iomode_hexdigitids[vic_iomode], + iomode_hexdigitids[io_mode], in_hypervisor ? 'H' : 'U', rot_fig, videostd_id ? "NTSC" : "PAL ", @@ -285,7 +285,7 @@ static void dump_map ( void ) { char desc[10]; for (unsigned int i = 0; i < 16; i++) { - mem_get_4k_region_short_desc(desc, sizeof desc, i); + memory_cpu_addr_to_desc(i << 12, desc, sizeof desc); MATRIX("{%X:}%7s%c", i, desc, (i & 7) == 7 ? ' ' : '|'); } } @@ -408,11 +408,11 @@ static void cmd_write ( char *p ) if (mem_args(p, 1) != 2) return; if (addr_hiword == 0xFFFF) { - cpu65_write_callback(addr_loword, data_byte); + debug_write_cpu_byte(addr_loword, data_byte); return; } const Uint32 addr = ((Uint32)addr_hiword << 16) + (Uint32)addr_loword; - memory_debug_write_phys_addr(addr, data_byte); + debug_write_linear_byte(addr, data_byte); } @@ -421,11 +421,11 @@ static void cmd_show ( char *p ) if (mem_args(p, 0) != 1) return; if (addr_hiword == 0xFFFF) { - MATRIX("[cpu:%04X] = %02X", addr_loword, cpu65_read_callback(addr_loword)); + MATRIX("[cpu:%04X] = %02X", addr_loword, debug_read_cpu_byte(addr_loword)); return; } const Uint32 addr = ((Uint32)addr_hiword << 16) + (Uint32)addr_loword; - MATRIX("[%03X:%04X] = %02X", addr_hiword, addr_loword, memory_debug_read_phys_addr(addr)); + MATRIX("[%03X:%04X] = %02X", addr_hiword, addr_loword, debug_read_linear_byte(addr)); } @@ -443,9 +443,9 @@ static void dump_mem_lines ( int lines, const char sepchr ) for (int i = 0; i < 16; i++) { Uint8 data; if (addr_hiword == 0xFFFF) - data = cpu65_read_callback(addr_loword++); + data = debug_read_cpu_byte(addr_loword++); else { - data = memory_debug_read_phys_addr(((Uint32)addr_hiword << 16) + (Uint32)addr_loword); + data = debug_read_linear_byte(((Uint32)addr_hiword << 16) + (Uint32)addr_loword); addr_loword++; if (!addr_loword) addr_hiword++; @@ -468,7 +468,7 @@ static Uint8 d_bytes[10]; static Uint8 reader_for_disasm_phys ( const unsigned int addr, const unsigned int ofs ) { - const Uint8 b = memory_debug_read_phys_addr((addr + ofs) & 0xFFFFFFU); + const Uint8 b = debug_read_linear_byte((addr + ofs) & 0xFFFFFFU); d_bytes[ofs] = b; return b; } @@ -476,7 +476,7 @@ static Uint8 reader_for_disasm_phys ( const unsigned int addr, const unsigned in static Uint8 reader_for_disasm_cpu ( const unsigned int addr, const unsigned int ofs ) { - const Uint8 b = cpu65_read_callback((addr + ofs) & 0xFFFFU); + const Uint8 b = debug_read_cpu_byte((addr + ofs) & 0xFFFFU); d_bytes[ofs] = b; return b; } diff --git a/targets/mega65/mega65.c b/targets/mega65/mega65.c index 56c4b9a1..426eded0 100644 --- a/targets/mega65/mega65.c +++ b/targets/mega65/mega65.c @@ -323,7 +323,7 @@ static void preinit_memory_for_start ( void ) preinit_memory_item("extonboard", "On-boarding utility", main_ram + 0x40000, meminitdata_onboard, MEMINITDATA_ONBOARD_SIZE, 0x00020, 0x10000, configdb.extonboard); preinit_memory_item("extflashutil", "MEGA-flash utility", main_ram + 0x50000, megaflashutility, sizeof megaflashutility, 0x00020, 0x07D00, configdb.extflashutil); preinit_memory_item("extbanner", "MEGA65 banner", main_ram + 0x57D00, meminitdata_banner, MEMINITDATA_BANNER_SIZE, 0x01000, 0x08300, configdb.extbanner); - preinit_memory_item("extchrwom", "Character-WOM", char_wom, meminitdata_chrwom, MEMINITDATA_CHRWOM_SIZE, 0x01000, 0x01000, configdb.extchrwom); + preinit_memory_item("extchrwom", "Character-WOM", char_ram, meminitdata_chrwom, MEMINITDATA_CHRWOM_SIZE, 0x01000, 0x01000, configdb.extchrwom); preinit_memory_item("extcramutils", "Utils in CRAM", colour_ram, meminitdata_cramutils, MEMINITDATA_CRAMUTILS_SIZE, 0x08000, 0x08000, configdb.extcramutils); hickup_is_overriden = preinit_memory_item("hickup", "Hyppo-Hickup", hypervisor_ram, meminitdata_hickup, MEMINITDATA_HICKUP_SIZE, 0x04000, 0x04000, configdb.hickup); @@ -436,7 +436,7 @@ static void mega65_init ( void ) DEBUGPRINT("SPEED: fast clock is set to %.2fMHz." NL, configdb.fast_mhz); cpu65_init_mega_specific(); cpu65_reset(); // reset CPU (though it fetches its reset vector, we don't use that on M65, but the KS hypervisor trap) - rom_protect = 0; + memory_set_rom_protection(0); hypervisor_start_machine(); speed_current = 0; machine_set_speed(1); @@ -456,9 +456,9 @@ static void mega65_init ( void ) int dump_memory ( const char *fn ) { - if (fn && *fn) { - DEBUGPRINT("MEM: Dumping memory into file: %s" NL, fn); - return xemu_save_file(fn, main_ram, (128 + 256) * 1024, "Cannot dump memory into file"); + if (fn && *fn && main_ram_size) { + DEBUGPRINT("MEM: Dumping memory into file (%uK): %s" NL, main_ram_size >> 10, fn); + return xemu_save_file(fn, main_ram, main_ram_size, "Cannot dump memory into file"); } else { return 0; } @@ -506,7 +506,7 @@ static void shutdown_callback ( void ) #endif hypervisor_hdos_close_descriptors(); if (emulation_is_running) - DEBUGPRINT("CPU: Execution ended at PC=$%04X (linear=%X)" NL, cpu65.pc, memory_cpurd2linear_xlat(cpu65.pc)); + DEBUGPRINT("CPU: Execution ended at PC=$%04X (linear=$%07X)" NL, cpu65.pc, memory_cpurd2linear_xlat(cpu65.pc)); } @@ -523,12 +523,12 @@ void reset_mega65 ( void ) D6XX_registers[0x7D] &= ~16; // FIXME: other default speed controls on reset? c128_d030_reg = 0; machine_set_speed(0); - memory_set_cpu_io_port_ddr_and_data(0xFF, 0xFF); - map_mask = 0; - in_hypervisor = 0; - vic_registers[0x30] = 0; // FIXME: hack! we need this, and memory_set_vic3_rom_mapping above too :( - memory_set_vic3_rom_mapping(0); - memory_set_do_map(); + vic_registers[0x30] = 0; + memory_reconfigure( + 0, io_mode, 0xFF, 0xFF, // D030 value, I/O mode, CPU I/O port 0, CPU I/O port 1 + 0, 0, 0, 0, 0, // MAP MB LO, OFS LO, MB HI, OFS HI, MASK + false // hypervisor + ); vic_reset(); // FIXME: we may need a RESET on VIC-IV what ROM would not initialize but could be used by some MEGA65-aware program? [and hyppo does not care to reset?] cpu65_reset(); dma_reset(); @@ -545,12 +545,12 @@ void reset_mega65_cpu_only ( void ) D6XX_registers[0x7D] &= ~16; // FIXME: other default speed controls on reset? c128_d030_reg = 0; machine_set_speed(0); - memory_set_cpu_io_port_ddr_and_data(0xFF, 0xFF); - map_mask = 0; - in_hypervisor = 0; - vic_registers[0x30] = 0; // FIXME: hack! we need this, and memory_set_vic3_rom_mapping above too :( - memory_set_vic3_rom_mapping(0); - memory_set_do_map(); + vic_registers[0x30] = 0; + memory_reconfigure( + 0, io_mode, 0xFF, 0xFF, // D030 value, I/O mode, CPU I/O port 0, CPU I/O port 1 + 0, 0, 0, 0, 0, // MAP MB LO, OFS LO, MB HI, OFS HI, MASK + false // hypervisor + ); dma_reset(); // We need this: even though it's CPU reset only, DMA is part of the CPU: either DMA or CPU running, resetting in the middle of a DMA session is a disaster cpu65_reset(); } @@ -602,7 +602,7 @@ void m65mon_dumpmem16 ( Uint16 addr ) int n = 16; umon_printf(":000%04X:", addr); while (n--) - umon_printf("%02X", cpu65_read_callback(addr++)); + umon_printf("%02X", debug_read_cpu_byte(addr++)); } void m65mon_dumpmem28 ( int addr ) @@ -611,13 +611,13 @@ void m65mon_dumpmem28 ( int addr ) addr &= 0xFFFFFFF; umon_printf(":%07X:", addr); while (n--) - umon_printf("%02X", memory_debug_read_phys_addr(addr++)); + umon_printf("%02X", debug_read_linear_byte(addr++)); } void m65mon_setmem28 ( int addr, int cnt, Uint8* vals ) { while (--cnt >= 0) - memory_debug_write_phys_addr(addr++, *(vals++)); + debug_write_linear_byte(addr++, *(vals++)); } void m65mon_set_trace ( int m ) diff --git a/targets/mega65/memory_mapper.c b/targets/mega65/memory_mapper.c index cfb99825..9c677bec 100644 --- a/targets/mega65/memory_mapper.c +++ b/targets/mega65/memory_mapper.c @@ -39,182 +39,227 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "configdb.h" #include -#define ALLOW_CPU_CUSTOM_FUNCTIONS_INCLUDE +#define ALLOW_CPU_CUSTOM_FUNCTIONS_INCLUDE #include "cpu_custom_functions.h" +#undef ALLOW_CPU_CUSTOM_FUNCTIONS_INCLUDE //#define DEBUGMEM DEBUG +//#define MEM_USE_HINTS - -// 512K is the max "main" RAM. Currently only 384K is used by M65 +// 512K is the max "main" RAM. Currently only 384K is used by M65. We want to make sure, the _total_ size is power of 2, so we can protect accesses with simple bit masks as a last-resort-protection Uint8 main_ram[512 << 10]; - -// Ugly hack for more RAM! -#define chip_ram (main_ram + 0) -#define fast_ram (main_ram + 0x20000) -#define extra_ram (main_ram + 0x40000) - - -// 128K of "chip-RAM". VIC-IV in M65 can see this, though the last 2K is also covered by the first 2K of the colour RAM. -// that area from chip-RAM cannot be modified by the CPU/DMA/etc though since the colour RAM is there. We emulate anyway -// 128K of chip-RAM so we don't need to check memory access limit all the time in VIC-IV emulation. But it's still true, -// that the last 2K of chip-RAM is a "static" content and not so much useful. -//Uint8 chip_ram[0x20000]; -// 128K of "fast-RAM". In English, this is C65 ROM, but on M65 you can actually write this area too, and you can use it -// as normal RAM. However VIC-IV cannot see this. -//Uint8 fast_ram[0x20000]; // 32K of colour RAM. VIC-IV can see this as for colour information only. The first 2K can be seen at the last 2K of // the chip-RAM. Also, the first 1 or 2K can be seen in the C64-style I/O area too, at $D800 Uint8 colour_ram[0x8000]; -// Write-Only memory (WOM) for character fetch when it would be the ROM (on C64 eg) -// FUN FACT: WOM is not write-only any more :) But for "historical purposes" I containue to name it as "WOM" anyway ;) -Uint8 char_wom[0x2000]; +// RAM of character fetch source when it would be the ROM (on eg. C64) +// FUN FACT: once it was named as "WOM" (Write-Only-Memory) since CPU could only write it. It's not true anymore though +Uint8 char_ram[0x2000]; // 16K of hypervisor RAM, can be only seen in hypervisor mode. Uint8 hypervisor_ram[0x4000]; // I2C registers Uint8 i2c_regs[0x1000]; - -Uint8 slow_ram[SLOW_RAM_SIZE]; +// Attic RAM, aka hyper-RAM (not to be confused with hypervisor RAM!) aka slow-RAM +Uint8 attic_ram[8 << 20]; -struct m65_memory_map_st { - int start, end; // starting and ending physical address of a memory region - mem_page_rd_f_type rd_f; - mem_page_wr_f_type wr_f; -}; +static Uint8 cpu_io_port[2]; +Uint8 io_mode; // "VIC" I/O mode: do not change this value directly, use memory_set_io_mode() instead +static bool rom_protect = false; +static Uint32 mem_legacy_io_addr32; // linear address of the I/O range in use (given by the current I/O mode) +int cpu_rmw_old_data; +Uint32 map_offset_low, map_offset_high, map_megabyte_low, map_megabyte_high; +Uint8 map_mask; +int skip_unhandled_mem = 0; +Uint32 main_ram_size = 0; // will be set by memory_init() after parsing the memory map + +// First 256 slots are for the regular 16 bit CPU addresses, per 256 bytes (256*256=64K) +// All the special ones follows that region: +#define MEM_SLOT_DMA_LIST 0x100 +#define MEM_SLOT_DMA_SOURCE 0x101 +#define MEM_SLOT_DMA_TARGET 0x102 +#define MEM_SLOT_CPU_LINEAR 0x103 +// Last "real" slot, must be defined here again with the name MEM_SLOT_LAST_REAL +#define MEM_SLOT_LAST_REAL 0x103 +// Not "real" slots +#define MEM_SLOT_DEBUG 0x104 +#define MEM_SLOT_SDEBUG 0x105 +// must be +1 of the last one, that is: the total number of slots +#define MEM_SLOTS_TOTAL 0x106 + +// CHECK: If MEM_SLOTS_TOTAL changes, you must modify MEM_SLOTS_TOTAL_EXPORTED in cpu_custom_functions.h to match that! +#if MEM_SLOTS_TOTAL != MEM_SLOTS_TOTAL_EXPORTED +#error "Inconsistency, please modify MEM_SLOTS_TOTAL_EXPORTED in cpu_custom_functions.h to match the value of MEM_SLOTS_TOTAL in memory_mapper.c" +#endif +#define VIC3_ROM_D030_MASK (0x08U|0x10U|0x20U|0x80U) + +// !!!!! +// function pointer types mem_slot_rd_func_t, mem_slot_wr_func_t are in cpu_custom_functions.h + +Uint32 mem_slot_rd_addr32[MEM_SLOTS_TOTAL]; +Uint32 mem_slot_wr_addr32[MEM_SLOTS_TOTAL]; +mem_slot_rd_func_t mem_slot_rd_func[MEM_SLOTS_TOTAL]; +mem_slot_wr_func_t mem_slot_wr_func[MEM_SLOTS_TOTAL]; +static mem_slot_rd_func_t mem_slot_rd_func_real[MEM_SLOTS_TOTAL]; +static mem_slot_wr_func_t mem_slot_wr_func_real[MEM_SLOTS_TOTAL]; +#ifdef MEM_USE_DATA_POINTERS +#warning "Usage of data pointers is an experimental feature (MEM_USE_DATA_POINTERS is defined)!" +Uint8 *mem_slot_rd_data[MEM_SLOTS_TOTAL]; +Uint8 *mem_slot_wr_data[MEM_SLOTS_TOTAL]; +#endif -// table of readers/writers for 256 byte CPU pages are "public" (not static) as will be used -// from inlined function in the CPU emulator core level for better performance. -// The second 256 entries are for unmapped all-RAM cache. -// The extra elements over 0x200 used for ROM and I/O mapping, DMA access decoding, and 32 bit opcodes. - -#define MEM_SLOT_C64_8KROM_A000 0x200 -#define MEM_SLOT_C64_4KROM_D000 0x220 -#define MEM_SLOT_OLD_4K_IO_D000 0x230 -#define MEM_SLOT_C64_8KROM_E000 0x240 -#define MEM_SLOT_C65_8KROM_8000 0x260 -#define MEM_SLOT_C65_8KROM_A000 0x280 -#define MEM_SLOT_C65_8KROM_E000 0x2A0 -#define MEM_SLOT_C65_4KROM_C000 0x2C0 -#define MEM_SLOT_DMA_RD_SRC 0x2D0 -#define MEM_SLOT_DMA_WR_SRC 0x2D1 -#define MEM_SLOT_DMA_RD_DST 0x2D2 -#define MEM_SLOT_DMA_WR_DST 0x2D3 -#define MEM_SLOT_DMA_RD_LST 0x2D4 -#define MEM_SLOT_CPU_32BIT 0x2D5 -#define MEM_SLOT_DEBUG_RESOLVER 0x2D6 -#define MEM_SLOTS 0x2D7 - -#define VIC3_ROM_MASK_8000 0x08 -#define VIC3_ROM_MASK_A000 0x10 -#define VIC3_ROM_MASK_C000 0x20 -#define VIC3_ROM_MASK_E000 0x80 - -#define MAP_MARKER_DUMMY_OFFSET 0x2000 - -static int mem_page_phys[MEM_SLOTS] MAXALIGNED; -int mem_page_rd_o[MEM_SLOTS] MAXALIGNED; -int mem_page_wr_o[MEM_SLOTS] MAXALIGNED; -mem_page_rd_f_type mem_page_rd_f[MEM_SLOTS] MAXALIGNED; -mem_page_wr_f_type mem_page_wr_f[MEM_SLOTS] MAXALIGNED; -static const struct m65_memory_map_st *mem_page_refp[MEM_SLOTS]; +// Very important variable, every slot reader/writer may depend on this to set correctly! +Uint32 ref_slot; -int cpu_rmw_old_data; +#ifdef MEM_WATCH_SUPPORT +static Uint8 memwatch_reader ( const Uint32 addr32 ); +static void memwatch_writer ( const Uint32 addr32, const Uint8 data ); +#define MEM_SLOT_WATCHER_READ 1U +#define MEM_SLOT_WATCHER_WRITE 2U +#define MEM_SLOT_WATCHER_CHANGED 0x80U +static Uint8 mem_slot_watcher[MEM_SLOTS_TOTAL]; +#endif -static int applied_memcfg[9]; // not 8, since one slot is actually halved because of CXXX/DXXX handled differently -static int memcfg_cpu_io_port_policy_A000_to_BFFF; -static int memcfg_cpu_io_port_policy_D000_to_DFFF; -static int memcfg_cpu_io_port_policy_E000_to_FFFF; +typedef enum { + MEM_SLOT_TYPE_UNRESOLVED, // invalidated slots + MEM_SLOT_TYPE_IMPOSSIBLE, // should not happen ever! software bug ... + MEM_SLOT_TYPE_UNDECODED, // there is corresponding memory area + MEM_SLOT_TYPE_DUMMY, // like the above, but should not give you warnings! + // These can occur only in CPU slots, assigned by the cpu address resolver (NOT the linear one!) + MEM_SLOT_TYPE_UNMAPPED, + MEM_SLOT_TYPE_BANKED_ROM, + MEM_SLOT_TYPE_LEGACY_IO, + // These can occur only in MAP'ed slots (or in special slots), assigned by the linear address decoder (NOT the CPU's one!) + MEM_SLOT_TYPE_MAIN_RAM, + MEM_SLOT_TYPE_ROM, // maybe normal RAM, if ROM write protection is off + MEM_SLOT_TYPE_SHARED_RAM, + MEM_SLOT_TYPE_IO, + MEM_SLOT_TYPE_COLOUR_RAM, + MEM_SLOT_TYPE_CHAR_RAM, + MEM_SLOT_TYPE_ETH_BUFFER, + MEM_SLOT_TYPE_I2C, + MEM_SLOT_TYPE_ATTIC_RAM, + MEM_SLOT_TYPE_SLOW_DEVICES, + MEM_SLOT_TYPE_OPL3, + MEM_SLOT_TYPE_1541_RAM, + // All hypervisor dependent slots must be after this mark! AND MEM_SLOT_TYPE_HYPERVISOR_RAM must be the first!!!!!!! + // DO not put other kind of slots here! + MEM_SLOT_TYPE_HYPERVISOR_RAM, + MEM_SLOT_TYPE_DISK_BUFFER, +} mem_slot_type_t; -static const int memcfg_cpu_io_port_policies_A000_to_BFFF[8] = { - 0x1A0, 0x1A0, 0x1A0, MEM_SLOT_C64_8KROM_A000, 0x1A0, 0x1A0, 0x1A0, MEM_SLOT_C64_8KROM_A000 -}; -static const int memcfg_cpu_io_port_policies_D000_to_DFFF[8] = { - 0x1D0, MEM_SLOT_C64_4KROM_D000, MEM_SLOT_C64_4KROM_D000, MEM_SLOT_C64_4KROM_D000, 0x1D0, MEM_SLOT_OLD_4K_IO_D000, MEM_SLOT_OLD_4K_IO_D000, MEM_SLOT_OLD_4K_IO_D000 -}; -static const int memcfg_cpu_io_port_policies_E000_to_FFFF[8] = { - 0x1E0, 0x1E0, MEM_SLOT_C64_8KROM_E000, MEM_SLOT_C64_8KROM_E000, 0x1E0, 0x1E0, MEM_SLOT_C64_8KROM_E000, MEM_SLOT_C64_8KROM_E000 -}; +static mem_slot_type_t mem_slot_type[MEM_SLOTS_TOTAL]; -static Uint8 memcfg_vic3_rom_mapping_last, memcfg_cpu_io_port_last; -static Uint8 cpu_io_port[2]; -int map_mask, map_offset_low, map_offset_high, map_megabyte_low, map_megabyte_high; -static int map_marker_low, map_marker_high; -int rom_protect; -int skip_unhandled_mem = 0; +static Uint32 policy4k_banking[0x10]; // memory policy according to banking only (no MAP effect!): BANK_POLICY_RAM, BANK_POLICY_ROM, BANK_POLICY_IO +static Uint32 policy4k[0x10]; // memory policy with MAP taken account (the real deal!): the same values as above, PLUS special values (see below for the defined values) +// Impossible MAP addresses are used for special purposes above the 28 bit linear address space of MEGA65. BANK_POLICY_INVALID must be the first just above the valid MEGA65 address range (@ $10000000) +#define BANK_POLICY_INVALID 0x10000000U +#define BANK_POLICY_RAM 0x10000001U +#define BANK_POLICY_ROM 0x10000002U +#define BANK_POLICY_IO 0x10000003U -#define DEFINE_READER(name) static Uint8 name ( MEMORY_HANDLERS_ADDR_TYPE ) -#define DEFINE_WRITER(name) static void name ( MEMORY_HANDLERS_ADDR_TYPE, Uint8 data ) +static Uint8 zero_page_reader ( const Uint32 addr32 ); +static void zero_page_writer ( const Uint32 addr32, const Uint8 data ); -DEFINE_READER(zero_physical_page_reader) { - return (XEMU_LIKELY(GET_OFFSET_BYTE_ONLY() > 1)) ? chip_ram[GET_OFFSET_BYTE_ONLY()] : cpu_io_port[GET_OFFSET_BYTE_ONLY()]; +static Uint8 main_ram_reader ( const Uint32 addr32 ) { + return main_ram[addr32]; } -DEFINE_WRITER(zero_physical_page_writer) -{ - if (XEMU_LIKELY(GET_OFFSET_BYTE_ONLY() > 1)) - chip_ram[GET_OFFSET_BYTE_ONLY()] = data; - else - memory_set_cpu_io_port(GET_OFFSET_BYTE_ONLY(), data); +static void main_ram_writer ( const Uint32 addr32, const Uint8 data ) { + main_ram[addr32] = data; } -DEFINE_READER(opl3_reader) { - return 0xFF; +static void shared_main_ram_writer ( const Uint32 addr32, const Uint8 data ) { + main_ram[addr32] = data; + colour_ram[addr32 - 0x1F800U] = data; } -DEFINE_WRITER(opl3_writer) { - audio65_opl3_write(GET_WRITER_OFFSET() & 0xFF, data); +static void shared_colour_ram_writer ( const Uint32 addr32, const Uint8 data ) { + main_ram[addr32 + 0x1F800U - 0xFF80000U] = data; + colour_ram[addr32 - 0xFF80000U] = data; } -DEFINE_READER(chip_ram_from_page1_reader) { - return chip_ram[GET_READER_OFFSET() + 0x100]; +static Uint8 colour_ram_reader ( const Uint32 addr32 ) { + return colour_ram[addr32 - 0xFF80000U]; } -DEFINE_WRITER(chip_ram_from_page1_writer) { - chip_ram[GET_WRITER_OFFSET() + 0x100] = data; +static void colour_ram_writer ( const Uint32 addr32, const Uint8 data ) { + colour_ram[addr32 - 0xFF80000U] = data; } -DEFINE_READER(fast_ram_reader) { - return fast_ram[GET_READER_OFFSET()]; +static Uint8 attic_ram_reader ( const Uint32 addr32 ) { + return attic_ram[addr32 - 0x8000000U]; } -DEFINE_WRITER(fast_ram_writer) { - if (XEMU_LIKELY(!rom_protect)) - fast_ram[GET_WRITER_OFFSET()] = data; +static void attic_ram_writer ( const Uint32 addr32, const Uint8 data ) { + attic_ram[addr32 - 0x8000000U] = data; } -DEFINE_READER(extra_ram_reader) { - return extra_ram[GET_READER_OFFSET()]; +static Uint8 hypervisor_ram_reader ( const Uint32 addr32 ) { + return hypervisor_ram[addr32 - 0xFFF8000U]; } -DEFINE_WRITER(extra_ram_writer) { - extra_ram[GET_WRITER_OFFSET()] = data; +static void hypervisor_ram_writer ( const Uint32 addr32, const Uint8 data ) { + hypervisor_ram[addr32 - 0xFFF8000U] = data; } -DEFINE_READER(colour_ram_reader) { - return colour_ram[GET_READER_OFFSET()]; +static void opl3_writer ( const Uint32 addr32, const Uint8 data ) { + audio65_opl3_write(addr32 & 0xFFU, data); } -DEFINE_WRITER(colour_ram_writer) { - write_colour_ram(GET_WRITER_OFFSET(), data); +static Uint8 io_reader ( const Uint32 addr32 ) { + return io_read(addr32 - 0xFFD0000U); } -DEFINE_READER(dummy_reader) { - return 0xFF; +static void io_writer ( const Uint32 addr32, const Uint8 data ) { + io_write(addr32 - 0xFFD0000U, data); +} +static Uint8 char_ram_reader ( const Uint32 addr32 ) { + return char_ram[addr32 - 0xFF7E000U]; +} +static void char_ram_writer ( const Uint32 addr32, const Uint8 data ) { + char_ram[addr32 - 0xFF7E000U] = data; } -DEFINE_WRITER(dummy_writer) { +static Uint8 disk_buffer_hypervisor_reader ( const Uint32 addr32 ) { + return disk_buffers[addr32 - 0xFFD6000U]; } -DEFINE_READER(hypervisor_ram_reader) { - return (XEMU_LIKELY(in_hypervisor)) ? hypervisor_ram[GET_READER_OFFSET()] : 0xFF; +static void disk_buffer_hypervisor_writer ( const Uint32 addr32, const Uint8 data ) { + disk_buffers[addr32 - 0xFFD6000U] = data; } -DEFINE_WRITER(hypervisor_ram_writer) { - if (XEMU_LIKELY(in_hypervisor)) - hypervisor_ram[GET_WRITER_OFFSET()] = data; +static Uint8 disk_buffer_user_reader ( const Uint32 addr32 ) { + return disk_buffer_cpu_view[(addr32 - 0xFFD6000U) & 0x1FF]; } -DEFINE_READER(char_wom_reader) { - return char_wom[GET_READER_OFFSET()]; +static void disk_buffer_user_writer ( const Uint32 addr32, const Uint8 data ) { + disk_buffer_cpu_view[(addr32 - 0xFFD6000U) & 0x1FF] = data; +} +static Uint8 eth_buffer_reader ( const Uint32 addr32 ) { + return eth65_read_rx_buffer(addr32 - 0xFFDE800U); } -DEFINE_WRITER(char_wom_writer) { - char_wom[GET_WRITER_OFFSET()] = data; +static void eth_buffer_writer ( const Uint32 addr32, const Uint8 data ) { + eth65_write_tx_buffer(addr32 - 0xFFDE800U, data); } -DEFINE_READER(slow_ram_reader) { - return slow_ram[GET_READER_OFFSET()]; +static Uint8 slow_devices_reader ( const Uint32 addr32 ) { + return cart_read_byte(addr32 - 0x4000000U); } -DEFINE_WRITER(slow_ram_writer) { - slow_ram[GET_WRITER_OFFSET()] = data; +static void slow_devices_writer ( const Uint32 addr32, const Uint8 data ) { + cart_write_byte(addr32 - 0x4000000U, data); } -DEFINE_READER(invalid_mem_reader) { +static Uint8 i2c_reader ( const Uint32 addr32 ) { + return i2c_regs[addr32 - 0x0FFD7000U]; +} +static void i2c_writer ( const Uint32 addr32, const Uint8 data ) { + const Uint32 rel = addr32 - 0x0FFD7000U; + if (rel >= I2C_NVRAM_OFFSET && rel < (I2C_NVRAM_OFFSET + I2C_NVRAM_SIZE)) { + //DEBUGPRINT("I2C: NVRAM write ($%02X->$%02X) @ NVRAM+$%X" NL, i2c_regs[rel], data, rel - I2C_NVRAM_OFFSET); + i2c_regs[rel] = data; + } else if (configdb.mega65_model == 3 && rel >= 0x1D0 && rel <= 0x1EF) { // Hyppo needs this on PCB R3 for I2C target setup (audio mixer settings) + // TODO: emulate the mixer stuff + i2c_regs[rel] = data; + } else { + DEBUGPRINT("I2C: unhandled write ($%02X) @ I2C+$%X" NL, data, rel); + } +} +static Uint8 dummy_reader ( const Uint32 addr32 ) { + return 0xFF; +} +static void dummy_writer ( const Uint32 addr32, const Uint8 data ) { + // well, do nothing +} + + +static Uint8 undecoded_reader ( const Uint32 addr32 ) +{ char msg[128]; - sprintf(msg, "Unhandled memory read operation for linear address $%X (PC=$%04X)", GET_READER_OFFSET(), cpu65.pc); + sprintf(msg, "Unhandled memory read operation for linear address $%X (PC=$%04X)", addr32, cpu65.old_pc); if (skip_unhandled_mem <= 1) skip_unhandled_mem = QUESTION_WINDOW("EXIT|Ignore now|Ignore all|Silent ignore all", msg); switch (skip_unhandled_mem) { @@ -231,14 +276,16 @@ DEFINE_READER(invalid_mem_reader) { } return 0xFF; } -DEFINE_WRITER(invalid_mem_writer) { + +static void undecoded_writer ( const Uint32 addr32, const Uint8 data ) +{ char msg[128]; - sprintf(msg, "Unhandled memory write operation for linear address $%X data = $%02X (PC=$%04X)", GET_WRITER_OFFSET(), data, cpu65.pc); + sprintf(msg, "Unhandled memory write operation for linear address $%X (PC=$%04X)", addr32, cpu65.old_pc); if (skip_unhandled_mem <= 1) skip_unhandled_mem = QUESTION_WINDOW("EXIT|Ignore now|Ignore all|Silent ignore all", msg); switch (skip_unhandled_mem) { case 0: - FATAL("Exit on request after illegal memory access"); + XEMUEXIT(1); break; case 1: case 2: @@ -249,565 +296,438 @@ DEFINE_WRITER(invalid_mem_writer) { break; } } -DEFINE_READER(fatal_mem_reader) { - FATAL("Unhandled physical memory mapping on read map. Xemu software bug?"); -} -DEFINE_WRITER(fatal_mem_writer) { - FATAL("Unhandled physical memory mapping on write map. Xemu software bug?"); -} -DEFINE_READER(unreferenced_mem_reader) { - FATAL("Unreferenced physical memory mapping on read map. Xemu software bug?"); -} -DEFINE_WRITER(unreferenced_mem_writer) { - FATAL("Unreferenced physical memory mapping on write map. Xemu software bug?"); -} -DEFINE_READER(m65_io_reader) { - return io_read(GET_READER_OFFSET()); -} -DEFINE_WRITER(m65_io_writer) { - io_write(GET_WRITER_OFFSET(), data); -} -DEFINE_READER(legacy_io_reader) { - return io_read(GET_READER_OFFSET() | (vic_iomode << 12)); -} -DEFINE_WRITER(legacy_io_writer) { - io_write(GET_WRITER_OFFSET() | (vic_iomode << 12), data); -} -DEFINE_READER(eth_buffer_reader) { - return eth65_read_rx_buffer(GET_READER_OFFSET()); -} -DEFINE_WRITER(eth_buffer_writer) { - eth65_write_tx_buffer(GET_WRITER_OFFSET(), data); -} -DEFINE_READER(disk_buffers_reader) { - const unsigned int offs = GET_READER_OFFSET(); - if (in_hypervisor) - return disk_buffers[offs]; - else - return disk_buffer_cpu_view[offs & 0x1FF]; -} -DEFINE_WRITER(disk_buffers_writer) { - const unsigned int offs = GET_WRITER_OFFSET(); - if (in_hypervisor) - disk_buffers[offs] = data; - else - disk_buffer_cpu_view[offs & 0x1FF] = data; -} -DEFINE_READER(i2c_io_reader) { - const unsigned int addr = GET_READER_OFFSET(); - //DEBUGPRINT("I2C: read ($%02X) @ I2C+$%X" NL, i2c_regs[addr], addr); - return i2c_regs[addr]; -} -DEFINE_WRITER(i2c_io_writer) { - const unsigned int addr = GET_WRITER_OFFSET(); - //DEBUGPRINT("I2C: write ($%02X) @ $%X" NL, data, addr); - if (addr >= I2C_NVRAM_OFFSET && addr < (I2C_NVRAM_OFFSET + I2C_NVRAM_SIZE)) { - //DEBUGPRINT("I2C: NVRAM write ($%02X->$%02X) @ NVRAM+$%X" NL, i2c_regs[addr], data, addr - I2C_NVRAM_OFFSET); - i2c_regs[addr] = data; - } else if (configdb.mega65_model == 3 && addr >= 0x1D0 && addr <= 0x1EF) { // Hyppo needs this on PCB R3 for I2C target setup (audio mixer settings) - // TODO: emulate the mixer stuff - i2c_regs[addr] = data; - } else { - DEBUGPRINT("I2C: unhandled write ($%02X) @ I2C+$%X" NL, data, addr); - } -} -// "Slow device" ~ cardridge space -DEFINE_READER(slowdev_reader) { - return cart_read_byte(GET_READER_OFFSET()); -} -DEFINE_WRITER(slowdev_writer) { - cart_write_byte(GET_WRITER_OFFSET(), data); -} - -// Not implemented yet, just here, since freezer accesses this memory area, and without **some** dummy -// support, it would cause "unhandled memory access" warning in Xemu. -DEFINE_READER(mem1541_reader) { - return 0xFF; -} -DEFINE_WRITER(mem1541_writer) { -} - - -// Memory layout table for MEGA65 -// Please note, that for optimization considerations, it should be organized in a way -// to have most common entries first, for faster hit in most cases. -static const struct m65_memory_map_st m65_memory_map[] = { - // 126K chip-RAM (last 2K is not availbale because it's colour RAM), with physical zero page excluded (this is because it needs the CPU port handled with different handler!) - { 0x100, 0x1F7FF, chip_ram_from_page1_reader, chip_ram_from_page1_writer }, - // the "physical" zero page because of CPU port ... - { 0, 0xFF, zero_physical_page_reader, zero_physical_page_writer }, - // 128K of fast-RAM, normally ROM for C65, but can be RAM too! - { 0x20000, 0x3FFFF, fast_ram_reader, fast_ram_writer }, - { 0x40000, 0x5FFFF, extra_ram_reader, extra_ram_writer }, - // the last 2K of the first 128K, being the first 2K of the colour RAM (quite nice sentence in my opinion) - { 0x1F800, 0x1FFFF, colour_ram_reader, colour_ram_writer }, - // As I/O can be handled quite uniformely, and needs other decoding later anyway, we handle the WHOLE I/O area for all modes in once! - // This is 16K space, though one 4K is invalid for I/O modes ($FFD2000-$FFD2FFF), the sequence: C64,C65,INVALID,M65 of 4Ks - { 0xFFD0000, 0xFFD3FFF, m65_io_reader, m65_io_writer }, - // full colour RAM - { 0xFF80000, 0xFF87FFF, colour_ram_reader, colour_ram_writer }, // full colour RAM (32K) - { 0xFFF8000, 0xFFFBFFF, hypervisor_ram_reader, hypervisor_ram_writer }, // 16KB HYPPO hickup/hypervisor ROM - { 0xFF7E000, 0xFF7FFFF, char_wom_reader, char_wom_writer }, // Character "WriteOnlyMemory" (which is not write-only any more, but it was initially, so the name ...) - { 0xFFDE800, 0xFFDEFFF, eth_buffer_reader, eth_buffer_writer }, // ethernet RX/TX buffer, NOTE: the same address, reading is always the RX_read, writing is always TX_write - { 0xFFD6000, 0xFFD6FFF, disk_buffers_reader, disk_buffers_writer }, // disk buffer for SD (can be mapped to I/O space too), F011, and some "3.5K scratch space" [??] - { 0xFFD7000, 0xFFD7FFF, i2c_io_reader, i2c_io_writer }, // I2C devices - { 0x8000000, 0x8000000 + SLOW_RAM_SIZE - 1, slow_ram_reader, slow_ram_writer }, // "slow RAM" also called "hyper RAM" (not to be confused with hypervisor RAM!) - { 0x8000000 + SLOW_RAM_SIZE, 0xFDFFFFF, dummy_reader, dummy_writer }, // ununsed big part of the "slow RAM" or so ... - { 0x4000000, 0x7FFFFFF, slowdev_reader, slowdev_writer }, // slow RAM memory area ~ cartridge - { 0xFE00000, 0xFE000FF, opl3_reader, opl3_writer }, - { 0x60000, 0xFFFFF, dummy_reader, dummy_writer }, // upper "unused" area of C65 (!) memory map. It seems C65 ROMs want it (Expansion RAM?) so we define as unused. - { 0xFFDB000, 0xFFDFFFF, mem1541_reader, mem1541_writer }, // 1541's 16K ROM + 4K RAM, not so much used currently, but freezer seems to access it, for example ... - // the last entry *MUST* include the all possible addressing space to "catch" undecoded memory area accesses!! - { 0, 0xFFFFFFF, invalid_mem_reader, invalid_mem_writer }, - // even after the last entry :-) to filter out programming bugs, catch all possible even not valid M65 physical address space acceses ... - { INT_MIN, INT_MAX, fatal_mem_reader, fatal_mem_writer } -}; -// a mapping item which NEVER matches (ie, starting address of region is higher then ending ...) -static const struct m65_memory_map_st impossible_mapping = { - 0x10000001, 0x10000000, unreferenced_mem_reader, unreferenced_mem_writer -}; - +struct mem_map_st { + Uint32 first; + Uint32 last; + mem_slot_type_t type; +}; +#ifdef MEM_USE_HINTS +#define MEM_HINT_SLOTS (MEM_SLOTS_TOTAL - 0x100 + 2) +static const struct mem_map_st *mem_map_hints[MEM_HINT_SLOTS]; +#endif +// Rules: +// * Every areas must start in address having the least significant byte as zero +// * Every areas must end in address having the least significant byte as $FF +// * Every areas (other than the first) must start on address which is the previous one's last one PLUS 1 +// * The first entry must start at address 0 +// * The last entry must be $10000000 - $FFFFFFFF with type MEM_SLOT_TYPE_IMPOSSIBLE as a safe-guard +static struct mem_map_st mem_map[] = { + { 0x00000000U, 0x0001F7FFU, MEM_SLOT_TYPE_MAIN_RAM }, // OLD memory model used 0-FF as "ZP" to handle CPU I/O port. But now it's VIRTUAL and only exists in CPU's view not in mem-map! + { 0x0001F800U, 0x0001FFFFU, MEM_SLOT_TYPE_SHARED_RAM }, // "shared" area, 2K of coulour RAM [C65 legacy], b/c of performance, we must distribute between both of normal and colour RAM + { 0x00020000U, 0x0003FFFFU, MEM_SLOT_TYPE_ROM }, // though it's called ROM, it can be normal RAM, if "ROM write protection" is off + { 0x00040000U, 0x0005FFFFU, MEM_SLOT_TYPE_MAIN_RAM }, + { 0x00060000U, 0x000FFFFFU, MEM_SLOT_TYPE_DUMMY }, // upper "unused" area of C65 (!) memory map. It seems C65 ROMs want it (Expansion RAM?) so we define as unused. + { 0x00100000U, 0x03FFFFFFU, MEM_SLOT_TYPE_UNDECODED }, + { 0x04000000U, 0x07FFFFFFU, MEM_SLOT_TYPE_SLOW_DEVICES }, + { 0x08000000U, 0x087FFFFFU, MEM_SLOT_TYPE_ATTIC_RAM }, // "slow RAM" also called "hyper RAM" (not to be confused with hypervisor RAM!) or "Attic RAM" + { 0x08800000U, 0x0FDFFFFFU, MEM_SLOT_TYPE_DUMMY }, // ununsed big part of the "slow RAM area" or so ... + { 0x0FE00000U, 0x0FE000FFU, MEM_SLOT_TYPE_OPL3 }, + { 0x0FE00100U, 0x0FF7DFFFU, MEM_SLOT_TYPE_UNDECODED }, + { 0x0FF7E000U, 0x0FF7FFFFU, MEM_SLOT_TYPE_CHAR_RAM }, // Character "WriteOnlyMemory" (which is not write-only any more, but it was initially, so the name ...) + { 0x0FF80000U, 0x0FF87FFFU, MEM_SLOT_TYPE_COLOUR_RAM }, // 32K colour RAM + { 0x0FF88000U, 0x0FFCFFFFU, MEM_SLOT_TYPE_UNDECODED }, + { 0x0FFD0000U, 0x0FFD3FFFU, MEM_SLOT_TYPE_IO }, + { 0x0FFD4000U, 0x0FFD5FFFU, MEM_SLOT_TYPE_UNDECODED }, + { 0x0FFD6000U, 0x0FFD6FFFU, MEM_SLOT_TYPE_DISK_BUFFER }, // disk buffer for SD (can be mapped to I/O space too), F011, and some "3.5K scratch space" [??] + { 0x0FFD7000U, 0x0FFD7FFFU, MEM_SLOT_TYPE_I2C }, // I2C devices + { 0x0FFD8000U, 0x0FFDAFFFU, MEM_SLOT_TYPE_UNDECODED }, + { 0x0FFDB000U, 0x0FFDE7FFU, MEM_SLOT_TYPE_1541_RAM }, // 1541's 16K ROM + 4K RAM, not so much used currently, but freezer seems to access it, for example ... FIXME: wrong area?! + { 0x0FFDE800U, 0x0FFDEFFFU, MEM_SLOT_TYPE_ETH_BUFFER }, // ethernet RX/TX buffer(s) + { 0x0FFDF000U, 0x0FFF7FFFU, MEM_SLOT_TYPE_UNDECODED }, + { 0x0FFF8000U, 0x0FFFBFFFU, MEM_SLOT_TYPE_HYPERVISOR_RAM}, // 16KB HYPPO hickup/hypervisor ROM [do not confuse with Hyper-RAM aka Attic-RAM aka Slow-RAM!] + { 0x0FFFC000U, 0x0FFFFFFFU, MEM_SLOT_TYPE_UNDECODED }, + { 0x10000000U, 0xFFFFFFFFU, MEM_SLOT_TYPE_IMPOSSIBLE } // must be the last item! (above 28 bit address space to the max of 32 bits - because of using Uint32) +}; +#define MEM_MAP_SIZE (sizeof(mem_map) / sizeof(struct mem_map_st)) -static void phys_addr_decoder ( int phys, int slot, int hint_slot ) +static XEMU_INLINE void slot_assignment_postprocessing ( const Uint32 slot ) { - const struct m65_memory_map_st *p; - phys &= 0xFFFFF00; // we map only at 256 bytes boundaries!!!! It also helps to wrap around 28 bit M65 addresses TODO/FIXME: is this correct behaviour? - if (mem_page_phys[slot] == phys) // kind of "mapping cache" for the given cache slot - return; // skip, if the slot already contains info on the current physical address - mem_page_phys[slot] = phys; - // tricky part: if hint_slot is non-negative, it's used for "contiunity" information related to this slot, - // ie check, if the current map request can be fit into the region already mapped by hint_slot, then no - // need for the search loop. hint_slot can be any slot, but logically it's sane to be used when the given - // hint_slot is "likely" to have some contiunity with the slot given by "slot" otherwise it's just makes - // thing worse. If not used, hint_slot should be negative to skip this feature. hint_slot can be even same - // as "slot" if you need a "moving" mapping in a "caching" slot, ie DMA-aux access functions, etc. - if (hint_slot >= 0 && mem_page_refp[hint_slot]->end < 0xFFFFFFF) { // FIXME there was a serious bug here, taking invalid mem slot for anything after hinting that - p = mem_page_refp[hint_slot]; - if (phys >= p->start && phys <= p->end) { -#ifdef DEBUGMEM - DEBUGMEM("MEM: PHYS-MAP: slot#$%03X: slot hint TAKEN :)" NL, slot); + mem_slot_rd_func_real[slot] = mem_slot_rd_func[slot]; + mem_slot_wr_func_real[slot] = mem_slot_wr_func[slot]; +#ifdef MEM_WATCH_SUPPORT + register const Uint8 watcher = mem_slot_watcher[slot]; + if (XEMU_UNLIKELY(watcher)) { + if ((watcher & MEM_SLOT_WATCHER_READ)) { + mem_slot_rd_func[slot] = memwatch_reader; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = NULL; // cannot use the memory-pointer optimization in case of mem-watch, since we need the callback +#endif + if (mem_slot_rd_func_real[slot] == undecoded_reader) + mem_slot_rd_func_real[slot] = dummy_reader; + } + if ((watcher & MEM_SLOT_WATCHER_WRITE)) { + mem_slot_wr_func[slot] = memwatch_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_wr_data[slot] = NULL; // see the comment above at the reader's case #endif - goto found; + if (mem_slot_wr_func_real[slot] == undecoded_writer) + mem_slot_wr_func_real[slot] = dummy_writer; } } - // Scan the memory map, as not found "cached" result on the same slot, or by the hinting slot - for (p = m65_memory_map; phys < p->start || phys > p->end; p++) - ; -found: - mem_page_rd_o[slot] = mem_page_wr_o[slot] = phys - p->start; - mem_page_rd_f[slot] = p->rd_f; - mem_page_wr_f[slot] = p->wr_f; - //if (p->rd_f == invalid_mem_reader) - // FATAL("Invalid memory region is tried to be mapped to slot $%X for phys addr $%X" NL, slot, phys); - mem_page_refp[slot] = p; -#ifdef DEBUGMEM - DEBUGMEM("MEM: PHYS-MAP: slot#$%03X: phys = $%X mapped (area: $%X-$%X, rd_o=%X, wr_o=%X) [hint slot was: %03X]" NL, - slot, - phys, - p->start, p->end, - mem_page_rd_o[slot], mem_page_wr_o[slot], - hint_slot - ); #endif } -static void XEMU_INLINE phys_addr_decoder_array ( int megabyte_offset, int offset, int slot, int slots, int hint_slot ) +// Warning: input linear address must be ranged/normalized (& 0xFFFFF00U) by the caller otherwise bad things will happen (TM) +static void resolve_linear_slot ( const Uint32 slot, const Uint32 addr ) { +#ifdef MEM_USE_HINTS + const Uint32 hint_slot = (slot < 0x100U) ? slot >> 7 : slot - 0x100U + 2U; + const struct mem_map_st *p = mem_map_hints[hint_slot]; +#else + static const struct mem_map_st *p = mem_map; +#endif + //if (addr < p->first) + // p = mem_map; for (;;) { - // we try to use the "hint_slot" feature, which tries to optimize table building with exploiting the - // fact, that "likely" the next page table entry suits into the same physical decoding "entry" just - // with different offset (so we don't need to re-walk the memory configuration table) - phys_addr_decoder(megabyte_offset | (offset & 0xFFFFF), slot, hint_slot); - if (!--slots) - return; - hint_slot = slot++; - offset += 0x100; + if (addr > p->last) + p++; + else if (addr < p->first) + p--; + else + break; } -} - - -#define MEM_TABLE_COPY(to,from,pages) do { \ - memcpy(mem_page_rd_o + (to), mem_page_rd_o + (from), sizeof(int) * (pages)); \ - memcpy(mem_page_wr_o + (to), mem_page_wr_o + (from), sizeof(int) * (pages)); \ - memcpy(mem_page_rd_f + (to), mem_page_rd_f + (from), sizeof(mem_page_rd_f_type) * (pages)); \ - memcpy(mem_page_wr_f + (to), mem_page_wr_f + (from), sizeof(mem_page_wr_f_type) * (pages)); \ - memcpy(mem_page_refp + (to), mem_page_refp + (from), sizeof(const struct m65_memory_map_st*) * (pages)); \ - memcpy(mem_page_phys + (to), mem_page_phys + (from), sizeof(int) * (pages)); \ -} while (0) - - - -// Not a performance critical function, since it's only needed at emulator init time -static void init_helper_custom_memtab_policy ( - int rd_o, // custom read-offset to set, apply value -1 for not to change - mem_page_rd_f_type rd_f, // custom read-function to set, apply NULL for not to change - int wr_o, - mem_page_wr_f_type wr_f, - int slot, // starting slot - int slots // number of slots -) { - while (slots--) { - if (rd_o >= 0) { - mem_page_rd_o[slot] = rd_o; - rd_o += 0x100; - } - if (rd_f) - mem_page_rd_f[slot] = rd_f; - if (wr_o >= 0) { - mem_page_wr_o[slot] = wr_o; - wr_o += 0x100; - } - if (wr_f) - mem_page_wr_f[slot] = wr_f; - mem_page_phys[slot] = 1; // invalidate phys info, to avoid cache-missbehaviour in decoder - mem_page_refp[slot] = &impossible_mapping; // invalidate this too - slot++; + //DEBUGPRINT("PHYS-RAM: $%X linear address is mapped into $%X-$%X in slot $%X" NL, addr, p->first, p->last, slot); // REMOVE +#ifdef MEM_USE_HINTS + mem_map_hints[hint_slot] = p; +#endif + mem_slot_rd_addr32[slot] = mem_slot_wr_addr32[slot] = addr; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = mem_slot_wr_data[slot] = NULL; // by default state: no data pointers, actual case handlers below can override this, of course +#endif + mem_slot_type[slot] = p->type; + switch (p->type) { + case MEM_SLOT_TYPE_MAIN_RAM: + mem_slot_rd_func[slot] = main_ram_reader; + mem_slot_wr_func[slot] = main_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = mem_slot_wr_data[slot] = main_ram + addr; +#endif + break; + case MEM_SLOT_TYPE_ROM: + mem_slot_rd_func[slot] = main_ram_reader; + mem_slot_wr_func[slot] = (rom_protect && slot != MEM_SLOT_SDEBUG) ? dummy_writer : main_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = main_ram + addr; + if (!rom_protect || slot == MEM_SLOT_SDEBUG) + mem_slot_wr_data[slot] = main_ram + addr; +#endif + break; + case MEM_SLOT_TYPE_SHARED_RAM: + mem_slot_rd_func[slot] = main_ram_reader; + mem_slot_wr_func[slot] = shared_main_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = main_ram + addr; // NOTE: writing does NOT have data pointer, as it requires special care +#endif + break; + case MEM_SLOT_TYPE_COLOUR_RAM: + mem_slot_rd_func[slot] = colour_ram_reader; + mem_slot_wr_func[slot] = addr >= 0x0FF80800U ? colour_ram_writer : shared_colour_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = colour_ram + addr - 0x0FF80000U; + if (addr >= 0x0FF80800U) // the first 2K of colour RAM is "shared" thus we don't set data pointer for that: allow the write callback to do that (special case!) + mem_slot_wr_data[slot] = colour_ram + addr - 0x0FF80000U; +#endif + break; + case MEM_SLOT_TYPE_ATTIC_RAM: + mem_slot_rd_func[slot] = attic_ram_reader; + mem_slot_wr_func[slot] = attic_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = mem_slot_wr_data[slot] = attic_ram + addr - 0x08000000U; +#endif + break; + case MEM_SLOT_TYPE_HYPERVISOR_RAM: + if (XEMU_LIKELY(in_hypervisor || slot == MEM_SLOT_SDEBUG)) { + mem_slot_rd_func[slot] = hypervisor_ram_reader; + mem_slot_wr_func[slot] = hypervisor_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = mem_slot_wr_data[slot] = hypervisor_ram + addr - 0xFFF8000U; +#endif + } else { + mem_slot_rd_func[slot] = dummy_reader; + mem_slot_wr_func[slot] = dummy_writer; + } + break; + case MEM_SLOT_TYPE_DISK_BUFFER: + if (in_hypervisor || slot == MEM_SLOT_SDEBUG) { + mem_slot_rd_func[slot] = disk_buffer_hypervisor_reader; + mem_slot_wr_func[slot] = disk_buffer_hypervisor_writer; + } else { + mem_slot_rd_func[slot] = disk_buffer_user_reader; + mem_slot_wr_func[slot] = disk_buffer_user_writer; + } + break; + case MEM_SLOT_TYPE_ETH_BUFFER: + mem_slot_rd_func[slot] = eth_buffer_reader; + mem_slot_wr_func[slot] = eth_buffer_writer; + break; + case MEM_SLOT_TYPE_OPL3: + mem_slot_rd_func[slot] = dummy_reader; // TODO: what should I do here? + mem_slot_wr_func[slot] = opl3_writer; + break; + case MEM_SLOT_TYPE_SLOW_DEVICES: + mem_slot_rd_func[slot] = slow_devices_reader; + mem_slot_wr_func[slot] = slow_devices_writer; + break; + case MEM_SLOT_TYPE_IO: + mem_slot_rd_func[slot] = io_reader; + mem_slot_wr_func[slot] = io_writer; + break; + case MEM_SLOT_TYPE_I2C: + mem_slot_rd_func[slot] = i2c_reader; + mem_slot_wr_func[slot] = i2c_writer; + break; + case MEM_SLOT_TYPE_CHAR_RAM: + mem_slot_rd_func[slot] = char_ram_reader; + mem_slot_wr_func[slot] = char_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = mem_slot_wr_data[slot] = char_ram + addr - 0xFF7E000U; +#endif + break; + case MEM_SLOT_TYPE_1541_RAM: + // TODO: Not implemented yet, just here, since freezer accesses this memory area, and without **some** dummy + // support, it would cause "unhandled memory access" warning in Xemu. + mem_slot_rd_func[slot] = dummy_reader; + mem_slot_wr_func[slot] = dummy_writer; + break; + case MEM_SLOT_TYPE_DUMMY: + mem_slot_rd_func[slot] = dummy_reader; + mem_slot_wr_func[slot] = dummy_writer; + break; + case MEM_SLOT_TYPE_UNDECODED: + if (slot <= MEM_SLOT_LAST_REAL) { + mem_slot_rd_func[slot] = undecoded_reader; + mem_slot_wr_func[slot] = undecoded_writer; + } else { + mem_slot_rd_func[slot] = dummy_reader; + mem_slot_wr_func[slot] = dummy_writer; + } + break; + case MEM_SLOT_TYPE_UNMAPPED: + case MEM_SLOT_TYPE_BANKED_ROM: + case MEM_SLOT_TYPE_LEGACY_IO: + case MEM_SLOT_TYPE_UNRESOLVED: + case MEM_SLOT_TYPE_IMPOSSIBLE: + FATAL("Impossible address ($%X) or bad slot type (%d) error in %s()", addr, p->type, __func__); + break; } + slot_assignment_postprocessing(slot); } -void memory_init ( void ) +static void resolve_cpu_slot ( const Uint8 slot ) { - memset(D6XX_registers, 0, sizeof D6XX_registers); - memset(D7XX, 0xFF, sizeof D7XX); - memset(i2c_regs, 0xFF, sizeof i2c_regs); - // generate UUID. It will be overwritten anyway in mega65.c if there is i2c backup (not totally new Xemu install) - for (int a = I2C_UUID_OFFSET; a < I2C_UUID_OFFSET + I2C_UUID_SIZE; a++) - i2c_regs[a] = rand(); - memset(i2c_regs + I2C_NVRAM_OFFSET, 0, I2C_NVRAM_SIZE); // also fill NVRAM area with 0 (FIXME: needed?) - cart_init(); - rom_protect = 0; - in_hypervisor = 0; - for (int a = 0; a < MEM_SLOTS; a++) { - // First of ALL! Initialize mem_page_phys for an impossible value! or otherwise bad crashes would happen ... - mem_page_phys[a] = 1; // this is cool enough, since phys addr for this func, can be only 256 byte aligned, so it won't find these ever as cached! - phys_addr_decoder((a & 0xFF) << 8, a, -1); // at least we have well defined defaults :) with 'real' and 'virtual' slots as well ... + const Uint32 policy = policy4k[slot >> 4]; + //DEBUGPRINT("CPU-ADDR: slot $%X mapping for policy $%X" NL, slot, policy); // REMOVE + // !!! if MEM_USE_DATA_POINTERS is used, mem_slot_wr_data and mem_slot_rd_data must be ALWAYS set! + switch (policy) { + case BANK_POLICY_RAM: + mem_slot_type[slot] = MEM_SLOT_TYPE_UNMAPPED; + if (XEMU_LIKELY(slot)) { + mem_slot_rd_addr32[slot] = mem_slot_wr_addr32[slot] = slot << 8; + mem_slot_rd_func[slot] = main_ram_reader; + mem_slot_wr_func[slot] = main_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = mem_slot_wr_data[slot] = main_ram + (slot << 8); +#endif + } else { + mem_slot_rd_addr32[0] = mem_slot_wr_addr32[0] = 0U; + mem_slot_rd_func[0] = zero_page_reader; + mem_slot_wr_func[0] = zero_page_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[0] = mem_slot_wr_data[0] = NULL; +#endif + } + break; + case BANK_POLICY_ROM: + mem_slot_type[slot] = MEM_SLOT_TYPE_BANKED_ROM; + mem_slot_rd_addr32[slot] = 0x20000U + (slot << 8); + mem_slot_wr_addr32[slot] = slot << 8 ; + mem_slot_rd_func[slot] = main_ram_reader; + mem_slot_wr_func[slot] = main_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = main_ram + 0x20000U + (slot << 8); + mem_slot_wr_data[slot] = main_ram + (slot << 8); +#endif + break; + case BANK_POLICY_IO: + mem_slot_type[slot] = MEM_SLOT_TYPE_LEGACY_IO; + mem_slot_rd_addr32[slot] = mem_slot_wr_addr32[slot] = mem_legacy_io_addr32 + ((slot - 0xD0U) << 8); // BANK_POLICY_IO can happen only from $D000 + //DEBUGPRINT("Legacy I/O assignment in slot $%X: $%X has been assigned (mem_legacy_io_addr32=$%X)" NL, slot, mem_slot_rd_addr32[slot], mem_legacy_io_addr32); // REMOVE + mem_slot_rd_func[slot] = io_reader; + mem_slot_wr_func[slot] = io_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = mem_slot_wr_data[slot] = NULL; +#endif + break; + default: + // Some mapping for other "policy" values, using that value (non-negative integer!) + // mem_slot_* things will be set up by resolve_linear_addr() in this case + if (XEMU_UNLIKELY(policy >= BANK_POLICY_INVALID)) // these must not happen, which can happen is handled in other "case" branches. FIXME: remove this later? It's a sanity check. + FATAL("Invalid bank_policy4k[%d >> 4] = $%X in %s()!", slot, policy, __func__); + resolve_linear_slot(slot, (policy & 0xFF00000U) + ((policy + (slot << 8)) & 0xFFF00U)); + return; // return, not break! Unlike other cases in this "swhitch" statement. That's important! } - // Generate "templates" for VIC-III ROM mapping entry points - // FIXME: the theory, that VIC-III ROM mapping is not like C64, ie writing a mapped in ROM, would write the ROM, not something "under" as with C64 - // static void XEMU_INLINE phys_addr_decoder_array ( int megabyte_offset, int offset, int slot, int slots, int hint_slot ) - phys_addr_decoder_array(0, 0x38000, MEM_SLOT_C65_8KROM_8000, 32, -1); // 8K(32 pages) C65 VIC-III ROM mapping ($8000) from $38000 - phys_addr_decoder_array(0, 0x3A000, MEM_SLOT_C65_8KROM_A000, 32, -1); // 8K(32 pages) C65 VIC-III ROM mapping ($A000) from $3A000 - phys_addr_decoder_array(0, 0x2C000, MEM_SLOT_C65_4KROM_C000, 16, -1); // 4K(16 pages) C65 VIC-III ROM mapping ($C000) from $2C000 - phys_addr_decoder_array(0, 0x3E000, MEM_SLOT_C65_8KROM_E000, 32, -1); // 8K(32 pages) C65 VIC-III ROM mapping ($E000) from $3E000 - phys_addr_decoder_array(0, 0x2A000, MEM_SLOT_C64_8KROM_A000, 32, -1); // 8K(32 pages) C64 CPU I/O ROM mapping ($A000) from $2A000 [C64 BASIC] - phys_addr_decoder_array(0, 0x2D000, MEM_SLOT_C64_4KROM_D000, 16, -1); // 4K(16 pages) C64 CPU I/O ROM mapping ($D000) from $2D000 [C64 CHARGEN] - phys_addr_decoder_array(0, 0x2E000, MEM_SLOT_C64_8KROM_E000, 32, -1); // 8K(32 pages) C64 CPU I/O ROM mapping ($E000) from $2E000 [C64 KERNAL] - // C64 ROM mappings by CPU I/O port should be "tuned" for the "write through RAM" policy though ... - // we re-use some write-specific info for pre-initialized unmapped RAM for write access here - init_helper_custom_memtab_policy(-1, NULL, mem_page_wr_o[0xA0], mem_page_wr_f[0xA0], MEM_SLOT_C64_8KROM_A000, 32); // for C64 BASIC ROM - init_helper_custom_memtab_policy(-1, NULL, mem_page_wr_o[0xD0], mem_page_wr_f[0xD0], MEM_SLOT_C64_4KROM_D000, 16); // for C64 CHARGEN ROM - init_helper_custom_memtab_policy(-1, NULL, mem_page_wr_o[0xE0], mem_page_wr_f[0xE0], MEM_SLOT_C64_8KROM_E000, 32); // for C64 KERNAL ROM - // The C64/C65-style I/O area is handled in this way: as it is I/O mode dependent unlike M65 high-megabyte areas, - // we maps I/O (any mode) and "customize it" with an offset to transfer into the right mode (or such). - phys_addr_decoder_array(0xFF << 20, 0xD0000, MEM_SLOT_OLD_4K_IO_D000, 16, -1); - init_helper_custom_memtab_policy(-1, legacy_io_reader, -1, legacy_io_writer, MEM_SLOT_OLD_4K_IO_D000, 16); - // Initialize some memory related "caching" stuffs and state etc ... - cpu_rmw_old_data = -1; - memcfg_vic3_rom_mapping_last = 0xFF; - memcfg_cpu_io_port_last = 0xFF; - cpu_io_port[0] = 0; - cpu_io_port[1] = 0; - map_mask = 0; - map_offset_low = 0; - map_offset_high = 0; - map_megabyte_low = 0; - map_megabyte_high = 0; - map_marker_low = MAP_MARKER_DUMMY_OFFSET; - map_marker_high = MAP_MARKER_DUMMY_OFFSET; - //skip_unhandled_mem = 0; - for (int a = 0; a < 9; a++) - applied_memcfg[a] = MAP_MARKER_DUMMY_OFFSET - 1; - // Setting up the default memory configuration for M65 at least! - // Note, the exact order is IMPORTANT as being the first use of memory subsystem, actually these will initialize some things ... - memory_set_cpu_io_port_ddr_and_data(7, 7); - memory_set_vic3_rom_mapping(0); - memory_set_do_map(); - // Initiailize memory content with something ... - memset(main_ram, 0x00, sizeof main_ram); - memset(colour_ram, 0x00, sizeof colour_ram); - memset(slow_ram, 0xFF, sizeof slow_ram); - DEBUG("MEM: End of memory initiailization" NL); + slot_assignment_postprocessing(slot); // NOT for MAP'ed slots (it has its own code path for that). That's the reason for "return" instead of "break" above } - - - -static void apply_memory_config_0000_to_7FFF ( void ) { - int hint_slot = -1; - // 0000 - 1FFF - if (map_mask & 0x01) { - if (applied_memcfg[0] != map_marker_low) { - phys_addr_decoder_array(map_megabyte_low, map_offset_low, 0x00, 0x20, -1); - applied_memcfg[0] = map_marker_low; - } - hint_slot = 0x1F; - } else { - if (applied_memcfg[0]) { - MEM_TABLE_COPY(0x00, 0x100, 0x20); - applied_memcfg[0] = 0; - } - } - // 2000 - 3FFF - if (map_mask & 0x02) { - if (applied_memcfg[1] != map_marker_low) { - phys_addr_decoder_array(map_megabyte_low, map_offset_low + 0x2000, 0x20, 0x20, hint_slot); - applied_memcfg[1] = map_marker_low; - } - hint_slot = 0x3F; - } else { - if (applied_memcfg[1]) { - MEM_TABLE_COPY(0x20, 0x120, 0x20); - applied_memcfg[1] = 0; - } - } - // 4000 - 5FFF - if (map_mask & 0x04) { - if (applied_memcfg[2] != map_marker_low) { - phys_addr_decoder_array(map_megabyte_low, map_offset_low + 0x4000, 0x40, 0x20, hint_slot); - applied_memcfg[2] = map_marker_low; - } - hint_slot = 0x5F; - } else { - if (applied_memcfg[2]) { - MEM_TABLE_COPY(0x40, 0x140, 0x20); - applied_memcfg[2] = 0; - } - } - // 6000 - 7FFF - if (map_mask & 0x08) { - if (applied_memcfg[3] != map_marker_low) { - phys_addr_decoder_array(map_megabyte_low, map_offset_low + 0x6000, 0x60, 0x20, hint_slot); - applied_memcfg[3] = map_marker_low; - } - } else { - if (applied_memcfg[3]) { - MEM_TABLE_COPY(0x60, 0x160, 0x20); - applied_memcfg[3] = 0; - } - } -} -static void apply_memory_config_8000_to_9FFF ( void ) { - if (memcfg_vic3_rom_mapping_last & VIC3_ROM_MASK_8000) { - if (applied_memcfg[4] >= 0) { - MEM_TABLE_COPY(0x80, MEM_SLOT_C65_8KROM_8000, 0x20); - applied_memcfg[4] = -1; - } - } else if (map_mask & 0x10) { - if (applied_memcfg[4] != map_marker_high) { - phys_addr_decoder_array(map_megabyte_high, map_offset_high + 0x8000, 0x80, 0x20, -1); - applied_memcfg[4] = map_marker_high; - } - } else { - if (applied_memcfg[4]) { - MEM_TABLE_COPY(0x80, 0x180, 0x20); - applied_memcfg[4] = 0; - } - } -} -static void apply_memory_config_A000_to_BFFF ( void ) { - if (memcfg_vic3_rom_mapping_last & VIC3_ROM_MASK_A000) { - if (applied_memcfg[5] >= 0) { - MEM_TABLE_COPY(0xA0, MEM_SLOT_C65_8KROM_A000, 0x20); - applied_memcfg[5] = -1; - } - } else if (map_mask & 0x20) { - if (applied_memcfg[5] != map_marker_high) { - phys_addr_decoder_array(map_megabyte_high, map_offset_high + 0xA000, 0xA0, 0x20, -1); - applied_memcfg[5] = map_marker_high; - } - } else { - if (applied_memcfg[5] != memcfg_cpu_io_port_policy_A000_to_BFFF) { - MEM_TABLE_COPY(0xA0, memcfg_cpu_io_port_policy_A000_to_BFFF, 0x20); - applied_memcfg[5] = memcfg_cpu_io_port_policy_A000_to_BFFF; - } - } -} -static void apply_memory_config_C000_to_CFFF ( void ) { - // Special range, just 4K in length! - if (memcfg_vic3_rom_mapping_last & VIC3_ROM_MASK_C000) { - if (applied_memcfg[6] >= 0) { - MEM_TABLE_COPY(0xC0, MEM_SLOT_C65_4KROM_C000, 0x10); - applied_memcfg[6] = -1; - } - } else if (map_mask & 0x40) { - if (applied_memcfg[6] != map_marker_high) { - phys_addr_decoder_array(map_megabyte_high, map_offset_high + 0xC000, 0xC0, 0x10, -1); - applied_memcfg[6] = map_marker_high; - } - } else { - if (applied_memcfg[6]) { - MEM_TABLE_COPY(0xC0, 0x1C0, 0x10); - applied_memcfg[6] = 0; - } - } -} -static void apply_memory_config_D000_to_DFFF ( void ) { - // Special range, just 4K in length! - if (map_mask & 0x40) { - if (applied_memcfg[7] != map_marker_high) { - phys_addr_decoder_array(map_megabyte_high, map_offset_high + 0xD000, 0xD0, 0x10, -1); - applied_memcfg[7] = map_marker_high; - } - } else { - if (applied_memcfg[7] != memcfg_cpu_io_port_policy_D000_to_DFFF) { - MEM_TABLE_COPY(0xD0, memcfg_cpu_io_port_policy_D000_to_DFFF, 0x10); - applied_memcfg[7] = memcfg_cpu_io_port_policy_D000_to_DFFF; - } - } -} -static void apply_memory_config_E000_to_FFFF ( void ) { - if (memcfg_vic3_rom_mapping_last & VIC3_ROM_MASK_E000) { - if (applied_memcfg[8] >= 0) { - MEM_TABLE_COPY(0xE0, MEM_SLOT_C65_8KROM_E000, 0x20); - applied_memcfg[8] = -1; - } - } else if (map_mask & 0x80) { - if (applied_memcfg[8] != map_marker_high) { - phys_addr_decoder_array(map_megabyte_high, map_offset_high + 0xE000, 0xE0, 0x20, -1); - applied_memcfg[8] = map_marker_high; - } - } else { - if (applied_memcfg[8] != memcfg_cpu_io_port_policy_E000_to_FFFF) { - MEM_TABLE_COPY(0xE0, memcfg_cpu_io_port_policy_E000_to_FFFF, 0x20); - applied_memcfg[8] = memcfg_cpu_io_port_policy_E000_to_FFFF; - } - } +// NOTE: since the lazy resolver is intended to resolve the CPU address slot on-demand, this is an exception to the rule: +// the input "addr32" parameter is not really a linear address (the whole purpose here is to GET THAT!) but some fake one. +// Thus only the low 1 byte can be re-used here to form the offset within the "slot" after we resolved the linear start +// address of the slot itself. +static Uint8 lazy_cpu_read_resolver ( const Uint32 addr32 ) +{ + resolve_cpu_slot(ref_slot); + //DEBUGPRINT("LAZY RESOLVER reading at $%X (slot $%X) at PC = $%04X" NL, addr32, ref_slot, cpu65.old_pc); // REMOVE + return mem_slot_rd_func[ref_slot](mem_slot_rd_addr32[ref_slot] + (addr32 & 0xFFU)); // re-using the low byte as the offset within the slot } - - - -// must be called when VIC-III register $D030 is written, with the written value exactly -void memory_set_vic3_rom_mapping ( Uint8 value ) +// See the comments above with the lazy read resolver! +static void lazy_cpu_write_resolver ( const Uint32 addr32, const Uint8 data ) { - // D030 regiser of VIC-III is: - // 7 6 5 4 3 2 1 0 - // | ROM | CROM | ROM | ROM | ROM | PAL | EXT | CRAM | - // | @E000 | @9000 | @C000 | @A000 | @8000 | | SYNC | @DC00 | - if (in_hypervisor) - value = 0; // in hypervisor, VIC-III ROM banking should *not* work (newer M65 change) - else - value &= VIC3_ROM_MASK_8000 | VIC3_ROM_MASK_A000 | VIC3_ROM_MASK_C000 | VIC3_ROM_MASK_E000; // only keep bits we're interested in - if (value != memcfg_vic3_rom_mapping_last) { // only do, if there was a change - Uint8 change = memcfg_vic3_rom_mapping_last ^ value; // change mask, bits have 1 only if there was a change - DEBUG("MEM: VIC-III ROM mapping change $%02X -> %02X" NL, memcfg_vic3_rom_mapping_last, value); - memcfg_vic3_rom_mapping_last = value; // don't forget to store the current state for next check! - // now check bits changed in ROM mapping - if (change & VIC3_ROM_MASK_8000) - apply_memory_config_8000_to_9FFF(); - if (change & VIC3_ROM_MASK_A000) - apply_memory_config_A000_to_BFFF(); - if (change & VIC3_ROM_MASK_C000) - apply_memory_config_C000_to_CFFF(); - if (change & VIC3_ROM_MASK_E000) - apply_memory_config_E000_to_FFFF(); - } + resolve_cpu_slot(ref_slot); + //DEBUGPRINT("LAZY RESOLVER writing at $%X (slot $%X) with data $%02X at PC = $%04X" NL, addr32, ref_slot, data, cpu65.old_pc); // REMOVE + mem_slot_wr_func[ref_slot](mem_slot_wr_addr32[ref_slot] + (addr32 & 0xFFU), data); // re-using the low byte as the offset within the slot } -static void apply_cpu_io_port_config ( void ) +static inline void invalidate_slot ( const unsigned int slot ) { - Uint8 desired = (cpu_io_port[1] | (~cpu_io_port[0])) & 7; - if (desired != memcfg_cpu_io_port_last) { - DEBUG("MEM: CPUIOPORT: port composite value (new one) is %d" NL, desired); - memcfg_cpu_io_port_last = desired; - memcfg_cpu_io_port_policy_A000_to_BFFF = memcfg_cpu_io_port_policies_A000_to_BFFF[desired]; - memcfg_cpu_io_port_policy_D000_to_DFFF = memcfg_cpu_io_port_policies_D000_to_DFFF[desired]; - memcfg_cpu_io_port_policy_E000_to_FFFF = memcfg_cpu_io_port_policies_E000_to_FFFF[desired]; - // check only regions to apply, where CPU I/O port can change anything - apply_memory_config_A000_to_BFFF(); - apply_memory_config_D000_to_DFFF(); - apply_memory_config_E000_to_FFFF(); - DEBUG("MEM: CPUIOPORT: new config had been applied" NL); + // the value here: signal impossibility: >=28 bit address + // WARNING: these offsets are added by the CPU memory handling callback, so the lower bytes should be zero! + mem_slot_rd_addr32[slot] = mem_slot_wr_addr32[slot] = 0x20000000U; + mem_slot_type[slot] = MEM_SLOT_TYPE_UNRESOLVED; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = mem_slot_wr_data[slot] = NULL; +#endif + if (slot < 0x100U) { + mem_slot_rd_func[slot] = mem_slot_rd_func_real[slot] = lazy_cpu_read_resolver; + mem_slot_wr_func[slot] = mem_slot_wr_func_real[slot] = lazy_cpu_write_resolver; } } -unsigned int mem_get_4k_region_short_desc ( char *p, const unsigned int n, const unsigned int i ) +static inline void invalidate_slot_range ( unsigned int first_slot, const unsigned int last_slot ) { - if (map_mask & (1 << (i >> 1))) - return snprintf(p, n, "%X", (i & 8 ? map_megabyte_high : map_megabyte_low) + (((i & 8 ? map_offset_high : map_offset_low) + (i << 12)) & 0xFFFFF)); - else if ((i == 0x8 || i == 0x9) && (vic_registers[0x30] & 8)) - return snprintf(p, n, "ROM8000"); - else if ((i == 0xA || i == 0xB) && ((vic_registers[0x30] & 16) || memcfg_cpu_io_port_last == 3 || memcfg_cpu_io_port_last == 7)) - return snprintf(p, n, "BASIC"); - else if ((i == 0xC ) && (vic_registers[0x30] & 32)) - return snprintf(p, n, "ROMC000"); - else if ((i == 0xD ) && (memcfg_cpu_io_port_last == 1 || memcfg_cpu_io_port_last == 2 || memcfg_cpu_io_port_last == 3)) - return snprintf(p, n, "CHARGEN"); - else if ( i == 0xD && memcfg_cpu_io_port_last >= 5) - return snprintf(p, n, "IO"); - else if ((i == 0xE || i == 0xF) && ((vic_registers[0x30] & 128) || memcfg_cpu_io_port_last == 2 || memcfg_cpu_io_port_last == 3 || memcfg_cpu_io_port_last == 6 || memcfg_cpu_io_port_last == 7)) - return snprintf(p, n, "KERNAL"); - return snprintf(p, n, "unmap"); + while (first_slot <= last_slot) + invalidate_slot(first_slot++); } -// must be called on CPU I/O port write, addr=0/1 for DDR/DATA -// do not call with other addr than 0/1! -void memory_set_cpu_io_port ( int addr, Uint8 value ) +static void apply_cpu_memory_policy ( Uint8 i ) { - if (XEMU_UNLIKELY((addr == 0) && ((value & 0xFE) == 64))) { // M65-specific speed control stuff! - value &= 1; - if (value != ((D6XX_registers[0x7D] >> 4) & 1)) { - if (value) - D6XX_registers[0x7D] |= 16; - else - D6XX_registers[0x7D] &= ~16; - machine_set_speed(0); + for (; i < 0x10U; i++) { + const Uint32 new_policy = (map_mask & (1U << (i >> 1))) ? + ( (i >= 8) ? map_megabyte_high + map_offset_high : map_megabyte_low + map_offset_low ) : policy4k_banking[i]; + if (new_policy != policy4k[i]) { + policy4k[i] = new_policy; + invalidate_slot_range(i << 4, (i << 4) + 0xF); } - } else { - cpu_io_port[addr] = value; - apply_cpu_io_port_config(); } } -void memory_set_cpu_io_port_ddr_and_data ( Uint8 p0, Uint8 p1 ) +static bool set_banking_config ( Uint8 vic3_d030 ) { - cpu_io_port[0] = p0; - cpu_io_port[1] = p1; - apply_cpu_io_port_config(); -} - - -Uint8 memory_get_cpu_io_port ( int addr ) + static Uint8 c64_cfg_old = 0xFF; + static Uint8 vic3_d030_old = 0xFF; + const Uint8 c64_cfg = (cpu_io_port[1] | (~cpu_io_port[0])) & 7U; + vic3_d030 = in_hypervisor ? 0 : vic3_d030 & VIC3_ROM_D030_MASK; + if (vic3_d030_old == vic3_d030 && c64_cfg_old == c64_cfg) + return false; + c64_cfg_old = c64_cfg; + vic3_d030_old = vic3_d030; + // Let's resolve the banking situation, we must fil policy4k_banking at this point (note: the first 32K - ie first 8 entiries of policy4k_banking[]) can never change + /* -- Port pin (bit) $A000 to $BFFF $D000 to $DFFF $E000 to $FFFF + -- 2 1 0 Read Write Read Write Read Write + -- -------------- ---------------- ---------------- ---------------- + 0 -- 0 0 0 RAM RAM RAM RAM RAM RAM + 1 -- 0 0 1 RAM RAM CHAR-ROM RAM RAM RAM + 2 -- 0 1 0 RAM RAM CHAR-ROM RAM KERNAL-ROM RAM + 3 -- 0 1 1 BASIC-ROM RAM CHAR-ROM RAM KERNAL-ROM RAM + 4 -- 1 0 0 RAM RAM RAM RAM RAM RAM + 5 -- 1 0 1 RAM RAM I/O I/O RAM RAM + 6 -- 1 1 0 RAM RAM I/O I/O KERNAL-ROM RAM + 7 -- 1 1 1 BASIC-ROM RAM I/O I/O KERNAL-ROM RAM + + D030: C65 $D030.0 VIC-III:CRAM2K Map 2nd KB of colour RAM @ $DC00-$DFFF + C65 $D030.1 VIC-III:EXTSYNC Enable external video sync (genlock input) + C65 $D030.2 VIC-III:PAL Use PALETTE ROM (0) or RAM (1) entries for colours 0 - 15 + C65 $D030.3 VIC-III:ROM8 Map C65 ROM @ $8000 + C65 $D030.4 VIC-III:ROMA Map C65 ROM @ $A000 + C65 $D030.5 VIC-III:ROMC Map C65 ROM @ $C000 + C65 $D030.6 VIC-III:CROM9 Select between C64 and C65 charset. + C65 $D030.7 VIC-III:ROME Map C65 ROM @ $E000 */ + policy4k_banking[0x8] = policy4k_banking[0x9] = (vic3_d030 & 0x08) ? BANK_POLICY_ROM : BANK_POLICY_RAM ; + policy4k_banking[0xA] = policy4k_banking[0xB] = (vic3_d030 & 0x10) || (c64_cfg & 3) == 3 ? BANK_POLICY_ROM : BANK_POLICY_RAM ; + policy4k_banking[0xC] = (vic3_d030 & 0x20) ? BANK_POLICY_ROM : BANK_POLICY_RAM ; + policy4k_banking[0xD] = (c64_cfg > 4) ? BANK_POLICY_IO : ( + (c64_cfg & 3) == 0 ? BANK_POLICY_RAM : BANK_POLICY_ROM); + policy4k_banking[0xE] = policy4k_banking[0xF] = (vic3_d030 & 0x80) || (c64_cfg & 2) ? BANK_POLICY_ROM : BANK_POLICY_RAM ; + return true; +} + + +int memory_cpu_addr_to_desc ( const Uint16 cpu_addr, char *p, const unsigned int n ) { - return cpu_io_port[addr]; + const Uint32 policy = policy4k[cpu_addr >> 12]; + switch (policy) { + case BANK_POLICY_RAM: + return snprintf(p, n, "unmap"); + case BANK_POLICY_ROM: + switch (cpu_addr >> 12) { + case 0x8: case 0x9: return snprintf(p, n, "ROM8000"); + case 0xA: case 0xB: return snprintf(p, n, "BASIC"); + case 0xC: return snprintf(p, n, "ROMC000"); + case 0xD: return snprintf(p, n, "CHARGEN"); + case 0xE: case 0xF: return snprintf(p, n, "KERNAL"); + default: goto error; + } + case BANK_POLICY_IO: + return snprintf(p, n, "IO"); + default: + if (policy >= BANK_POLICY_INVALID) + goto error; + return snprintf(p, n, "%X", policy + cpu_addr); + } +error: + FATAL("Invalid bank_policy4k[%u >> 12] = $%X in %s()!", cpu_addr, policy, __func__); + XEMU_UNREACHABLE(); } - - - -// Call this after MAP opcode, map_* variables must be pre-initialized -// Can be also used to set custom mapping (hypervisor enter/leave, maybe snapshot loading) -void memory_set_do_map ( void ) +Uint32 memory_cpu_addr_to_linear ( const Uint16 cpu_addr, Uint32 *wr_addr_p ) { - // map_marker_low and map_maker_high are just articial markers, not so much to do with real offset, used to - // detect already done operations. It must be unique for each possible mappings, that is the only rule. - // to leave room for other values we use both of megabyte info and offset info, but moved from the zero - // reference (WARNING: mapped from zero and unmapped are different states!) to have place for other markers too. - map_marker_low = (map_megabyte_low | map_offset_low ) + MAP_MARKER_DUMMY_OFFSET; - map_marker_high = (map_megabyte_high | map_offset_high) + MAP_MARKER_DUMMY_OFFSET; - // We need to check every possible memory regions for the effect caused by MAPping ... - apply_memory_config_0000_to_7FFF(); - apply_memory_config_8000_to_9FFF(); - apply_memory_config_A000_to_BFFF(); - apply_memory_config_C000_to_CFFF(); - apply_memory_config_D000_to_DFFF(); - apply_memory_config_E000_to_FFFF(); - DEBUG("MEM: memory_set_do_map() applied" NL); + const Uint32 policy = policy4k[cpu_addr >> 12]; + Uint32 wr_addr, rd_addr; + switch (policy) { + case BANK_POLICY_RAM: + rd_addr = wr_addr = cpu_addr; + break; + case BANK_POLICY_ROM: + rd_addr = 0x20000U + cpu_addr; + wr_addr = cpu_addr; + break; + case BANK_POLICY_IO: + rd_addr = wr_addr = mem_legacy_io_addr32 + (cpu_addr & 0xFFFU); + break; + default: + if (XEMU_UNLIKELY(policy >= BANK_POLICY_INVALID)) + FATAL("Invalid bank_policy4k[%u >> 12] = $%X in %s()!", cpu_addr, policy, __func__); + rd_addr = wr_addr = policy + cpu_addr; + break; + } + if (wr_addr_p) + *wr_addr_p = wr_addr; + return rd_addr; } @@ -851,11 +771,10 @@ void cpu65_do_aug_callback ( void ) DEBUG("LOW -OFFSET = $%03X, MB = $%02X" NL, map_offset_low , map_megabyte_low >> 20); DEBUG("HIGH-OFFSET = $%03X, MB = $%02X" NL, map_offset_high, map_megabyte_high >> 20); DEBUG("MASK = $%02X" NL, map_mask); - memory_set_do_map(); + apply_cpu_memory_policy(0); // take new MAP config into account } - // *** Implements the EOM opcode of 4510, called by the 65CE02 emulator void cpu65_do_nop_callback ( void ) { @@ -867,136 +786,393 @@ void cpu65_do_nop_callback ( void ) } -/* For 32 (28 ...) bit linear addressing we use a dedicated mapper slot. Please read - command above, similar situation as with the DMA. However we need to fetch the - base (+Z) from base page, so it can be a bit less efficient if different 32 bit - pointers used all the time in 4510GS code. */ +static Uint8 zero_page_reader ( const Uint32 addr32 ) +{ + // this could be called only with linear addr (addr32) being in the first 256 byte anyway, so it's OK to use addr32 directly to address cpu_io_port, etc ... + //DEBUGPRINT("ZERO PAGE READER: query address $%X (mem_slot_rd_addr32[$%X]=$%X) at PC=$%04X" NL, addr32, ref_slot, mem_slot_rd_addr32[ref_slot], cpu65.old_pc); // REMOVE + return XEMU_LIKELY(addr32 & 0xFEU) ? main_ram[addr32] : cpu_io_port[addr32]; +} -static XEMU_INLINE int cpu_get_flat_addressing_mode_address ( int index ) +static void zero_page_writer ( const Uint32 addr32, const Uint8 data ) +{ + //DEBUGPRINT("ZERO PAGE READER: set address $%X (mem_slot_wr_addr32[$%X]=$%X) to $%X at PC=$%04X" NL, addr32, ref_slot, mem_slot_wr_addr32[ref_slot], data, cpu65.old_pc); // REMOVE + if (XEMU_LIKELY(addr32 & 0xFEU)) { + main_ram[addr32] = data; + } else { + if (XEMU_UNLIKELY(!addr32 && (data == 64 || data == 65))) { // special "magic" values used on MEGA65 to set the speed gate + if (((D6XX_registers[0x7D] >> 4) ^ data) & 1U) { + D6XX_registers[0x7D] ^= 16U; + machine_set_speed(0); + } + } else { + cpu_io_port[addr32] = data; + if (set_banking_config(vic_registers[0x30])) + apply_cpu_memory_policy(8); // start with 4K region 8 (@$8000): banking cannot change the lower 32K + } + } +} + + +Uint8 memory_get_cpu_io_port ( const Uint8 port ) +{ + return cpu_io_port[port & 1U]; +} + + +// Re-set linear address of memory slots given to "legacy I/O" which can be only at the range $D000-$DFFF +// Note: this function happily sets I/O mode even in hypervisor! So only call this, if you know, that's OK! [on MEGA65 hypervisor mode _always_ uses MEGA65 I/O mode!] +void memory_set_io_mode ( const Uint8 new_io_mode ) +{ + if (io_mode == new_io_mode) + return; + io_mode = new_io_mode; + mem_legacy_io_addr32 = 0xFFD0000U + ((unsigned)(new_io_mode) << 12); // this value is used at other places as well + for (Uint8 slot = 0xD0U; slot <= 0xDFU; slot++) + if (mem_slot_type[slot] == MEM_SLOT_TYPE_LEGACY_IO) // if it's a resolved legacy I/O slot @ $DXXX, we re-set the corresponding linear address for those + mem_slot_rd_addr32[slot] = mem_slot_wr_addr32[slot] = mem_legacy_io_addr32 + ((slot - 0xD0U) << 8); + // TODO: I must think of a solution to the DMA I/O mode, how to handle that on I/O mode changes +} + + +void memory_write_d030 ( const Uint8 data ) +{ + if (set_banking_config(data)) + apply_cpu_memory_policy(8); // start with 4K region 8 (@$8000): banking cannot change the lower 32K +} + + +void memory_set_rom_protection ( const bool protect ) +{ + if (protect == rom_protect) + return; + rom_protect = protect; + DEBUGPRINT("MEGA65: ROM protection has been turned %s." NL, rom_protect ? "ON" : "OFF"); // FIXME: should I left this "DEBUGPRINT"? + for (unsigned int slot = 0; slot < MEM_SLOTS_TOTAL; slot++) + if (mem_slot_type[slot] == MEM_SLOT_TYPE_ROM) + invalidate_slot(slot); +} + + +void memory_reconfigure ( + const Uint8 d030_value, const Uint8 new_io_mode, const Uint8 new_cpu_port0, const Uint8 new_cpu_port1, + const Uint32 new_map_mb_lo, const Uint32 new_map_ofs_lo, + const Uint32 new_map_mb_hi, const Uint32 new_map_ofs_hi, + const Uint8 new_map_mask, + const bool new_in_hypervisor +) { + if (new_in_hypervisor != in_hypervisor) { + in_hypervisor = new_in_hypervisor; + // Invalidate slots, where behaviour is hypervisor/user mode dependent! + for (unsigned int slot = 0; slot < MEM_SLOTS_TOTAL; slot++) + if (mem_slot_type[slot] >= MEM_SLOT_TYPE_HYPERVISOR_RAM) // see the mem_slot_type enum type definition for more details. This is kind of abusing enums ... + invalidate_slot(slot); + } + map_megabyte_low = new_map_mb_lo; + map_offset_low = new_map_ofs_lo; + map_megabyte_high = new_map_mb_hi; + map_offset_high = new_map_ofs_hi; + map_mask = new_map_mask; + cpu_io_port[0] = new_cpu_port0; + cpu_io_port[1] = new_cpu_port1; + memory_set_io_mode(new_io_mode); + (void)set_banking_config(d030_value); + apply_cpu_memory_policy(0); +} + + +#define SIZEOF_KILO(b) ((Uint32)sizeof(b) >> 10) + + +void memory_init (void ) +{ + // Check memory map table consistency + for (unsigned int i = 0, start_at = 0; i < MEM_MAP_SIZE; start_at = mem_map[i++].last + 1U) { + if ( + mem_map[i].first != start_at || mem_map[i].last <= mem_map[i].first || + (mem_map[i].first & 0xFFU) != 0 || (mem_map[i].last & 0xFFU) != 0xFFU || + (i == MEM_MAP_SIZE - 1 && (mem_map[i].first != 0x10000000U || mem_map[i].last != 0xFFFFFFFFU || mem_map[i].type != MEM_SLOT_TYPE_IMPOSSIBLE)) || + (i != MEM_MAP_SIZE - 1 && (mem_map[i].first == 0x10000000U || mem_map[i].last == 0xFFFFFFFFU || mem_map[i].type == MEM_SLOT_TYPE_IMPOSSIBLE)) + ) + FATAL("INTERNAL XEMU FATAL ERROR: Bad memory decoding table 'mem_map' at entry #%d ($%X-$%X) [should start at $%X]", i, mem_map[i].first, mem_map[i].last, start_at); + if (mem_map[i].type == MEM_SLOT_TYPE_MAIN_RAM) + main_ram_size = mem_map[i].last + 1; + } + memset(D6XX_registers, 0, sizeof D6XX_registers); + memset(D7XX, 0xFFU, sizeof D7XX); + memset(i2c_regs, 0xFFU, sizeof i2c_regs); + // generate UUID. It will be overwritten anyway in mega65.c if there is i2c backup (not totally new Xemu install) + for (unsigned int i = I2C_UUID_OFFSET; i < I2C_UUID_OFFSET + I2C_UUID_SIZE; i++) + i2c_regs[i] = rand(); + memset(i2c_regs + I2C_NVRAM_OFFSET, 0, I2C_NVRAM_SIZE); // also fill NVRAM area with 0 (FIXME: needed?) + cart_init(); + rom_protect = false; + D6XX_registers[0x7D] &= ~4; + in_hypervisor = false; +#ifdef MEM_USE_HINTS + for (unsigned int i = 0; i < MEM_HINT_SLOTS; i++) + mem_map_hints[i] = mem_map; +#endif + for (unsigned int i = 0; i < 0x10; i++) { + policy4k[i] = BANK_POLICY_INVALID; + policy4k_banking[i] = (i >= 8) ? BANK_POLICY_INVALID : BANK_POLICY_RAM; // lower 32K cannot be banked ever, but we need the value of BANK_POLICY_RAM to simplify logic + } +#ifdef MEM_WATCH_SUPPORT + memset(mem_slot_watcher, 0, sizeof mem_slot_watcher); // initially no watchers at all +#endif + invalidate_slot_range(0, MEM_SLOTS_TOTAL - 1); // make sure we have a consistent state + cpu_rmw_old_data = -1; + vic_registers[0x30] &= ~VIC3_ROM_D030_MASK; + memory_reconfigure( + 0, // $D030 ROM banking + VIC2_IOMODE, + 0, 0, // some initial CPU I/O port values + 0, 0, 0, 0, // MAP lo/hi mb/ofs + 0, // MAP mask + false // not-hypervisor + ); + // Initiailize memory content with something ... + // NOTE: make sure the first 2K of colour_ram is the **SAME** as the 2K part of main_ram at offset $1F800 + memset(main_ram, 0x00, sizeof main_ram); + memset(colour_ram, 0x00, sizeof colour_ram); + memset(attic_ram, 0xFF, sizeof attic_ram); + DEBUGPRINT("MEM: memory decoder initialized, %uK fast, %uK attic, %uK colour, %uK font RAM" NL, + main_ram_size >> 10, + SIZEOF_KILO(attic_ram), + SIZEOF_KILO(colour_ram), + SIZEOF_KILO(char_ram) + ); +} + + +// Warning: this overwrites ref_slot! +static XEMU_INLINE Uint32 cpu_get_flat_addressing_mode_address ( const Uint8 index ) { - register int addr = cpu65_read_callback(cpu65.pc++); // fetch base page address // FIXME: really, BP/ZP is wrapped around in case of linear addressing and eg BP addr of $FF got?????? (I think IT SHOULD BE!) - // FIXME: migrate to cpu_read_paged(), but we need CPU emu core to utilize BP rather than BP << 8, and - // similar older hacks ... - return ( - cpu65_read_callback(cpu65.bphi | addr ) | - (cpu65_read_callback(cpu65.bphi | ((addr + 1) & 0xFF)) << 8) | - (cpu65_read_callback(cpu65.bphi | ((addr + 2) & 0xFF)) << 16) | - (cpu65_read_callback(cpu65.bphi | ((addr + 3) & 0xFF)) << 24) - ) + index; // I don't handle the overflow of 28 bit addr.space situation, as addr will be anyway "trimmed" later in phys_addr_decoder() issued by the user of this func + const Uint8 bp_addr = cpu65_read_callback(cpu65.pc++); // fetch base page address (we plays the role of the CPU here) + ref_slot = cpu65.bphi >> 8; // basically the page number of the base page, what BP would mean (however CPU65 emulator uses BPHI ... the offset of the base page ...) + if (mem_slot_type[ref_slot] == MEM_SLOT_TYPE_UNRESOLVED) // make sure the slot is resolved, otherwise the query of rd_base_addr below can be invalid before the first read!!!!!!! + resolve_cpu_slot(ref_slot); + const Uint32 rd_base_addr = mem_slot_rd_addr32[ref_slot]; + return index + + (mem_slot_rd_func[ref_slot](rd_base_addr + bp_addr ) ) + + (mem_slot_rd_func[ref_slot](rd_base_addr + ((bp_addr + 1) & 0xFFU)) << 8) + + (mem_slot_rd_func[ref_slot](rd_base_addr + ((bp_addr + 2) & 0xFFU)) << 16) + + (mem_slot_rd_func[ref_slot](rd_base_addr + ((bp_addr + 3) & 0xFFU)) << 24) ; +} + + +static XEMU_INLINE void resolve_special_rd_slot_on_demand ( const Uint32 slot, Uint32 addr32 ) +{ + addr32 &= 0xFFFFF00U; + if (XEMU_UNLIKELY(addr32 ^ mem_slot_rd_addr32[slot])) + resolve_linear_slot(slot, addr32); } + +static XEMU_INLINE void resolve_special_wr_slot_on_demand ( const Uint32 slot, Uint32 addr32 ) +{ + addr32 &= 0xFFFFF00U; + if (XEMU_UNLIKELY(addr32 ^ mem_slot_wr_addr32[slot])) + resolve_linear_slot(slot, addr32); +} + + Uint8 cpu65_read_linear_opcode_callback ( void ) { - register int addr = cpu_get_flat_addressing_mode_address(cpu65.z); - phys_addr_decoder(addr, MEM_SLOT_CPU_32BIT, MEM_SLOT_CPU_32BIT); - return CALL_MEMORY_READER(MEM_SLOT_CPU_32BIT, addr); + //DEBUGPRINT("cpu65_read_linear_opcode_callback fires at PC=$%04X" NL, cpu65.old_pc); // REMOVE + register const Uint32 addr32 = cpu_get_flat_addressing_mode_address(cpu65.z) & 0xFFFFFFFU; + //DEBUGPRINT("cpu65_read_linear_opcode_callback fires-2 at PC=$%04X" NL, cpu65.old_pc); // REMOVE + //DEBUGPRINT("cpu65_read_linear_opcode_callback: about to call read for addr = $%02X at PC=$%04X" NL, addr32, cpu65.old_pc); // REMOVE + resolve_special_rd_slot_on_demand(MEM_SLOT_CPU_LINEAR, addr32); + ref_slot = MEM_SLOT_CPU_LINEAR; + return mem_slot_rd_func[MEM_SLOT_CPU_LINEAR](addr32); } -void cpu65_write_linear_opcode_callback ( Uint8 data ) + +void cpu65_write_linear_opcode_callback ( const Uint8 data ) { - register int addr = cpu_get_flat_addressing_mode_address(cpu65.z); - phys_addr_decoder(addr, MEM_SLOT_CPU_32BIT, MEM_SLOT_CPU_32BIT); - CALL_MEMORY_WRITER(MEM_SLOT_CPU_32BIT, addr, data); + register const Uint32 addr32 = cpu_get_flat_addressing_mode_address(cpu65.z) & 0xFFFFFFFU; + resolve_special_wr_slot_on_demand(MEM_SLOT_CPU_LINEAR, addr32); + ref_slot = MEM_SLOT_CPU_LINEAR; + mem_slot_wr_func[MEM_SLOT_CPU_LINEAR](addr32, data); } -// FIXME: very ugly and very slow and maybe very buggy implementation! Should be done in a sane way in the next memory decoder version being developmented ... Uint32 cpu65_read_linear_long_opcode_callback ( const Uint8 index ) { - for (int shift = 0, ret = 0, addr = cpu_get_flat_addressing_mode_address(index) ;; ) { - phys_addr_decoder(addr, MEM_SLOT_CPU_32BIT, MEM_SLOT_CPU_32BIT); - ret += (Uint32)CALL_MEMORY_READER(MEM_SLOT_CPU_32BIT, addr) << shift; - if (shift == 24) - return ret; - addr++; - shift += 8; - } + Uint32 addr32 = cpu_get_flat_addressing_mode_address(index); + ref_slot = MEM_SLOT_CPU_LINEAR; + resolve_special_rd_slot_on_demand(MEM_SLOT_CPU_LINEAR, addr32); + Uint32 data = mem_slot_rd_func[MEM_SLOT_CPU_LINEAR](addr32 & 0xFFFFFFFU) ; + resolve_special_rd_slot_on_demand(MEM_SLOT_CPU_LINEAR, ++addr32); + data += (Uint32)mem_slot_rd_func[MEM_SLOT_CPU_LINEAR](addr32 & 0xFFFFFFFU) << 8; + resolve_special_rd_slot_on_demand(MEM_SLOT_CPU_LINEAR, ++addr32); + data += (Uint32)mem_slot_rd_func[MEM_SLOT_CPU_LINEAR](addr32 & 0xFFFFFFFU) << 16; + resolve_special_rd_slot_on_demand(MEM_SLOT_CPU_LINEAR, ++addr32); + data += (Uint32)mem_slot_rd_func[MEM_SLOT_CPU_LINEAR](addr32 & 0xFFFFFFFU) << 24; + return data; + } -// FIXME: very ugly and very slow and maybe very buggy implementation! Should be done in a sane way in the next memory decoder version being developmented ... -void cpu65_write_linear_long_opcode_callback ( const Uint8 index, Uint32 data ) + +void cpu65_write_linear_long_opcode_callback ( const Uint8 index, const Uint32 data ) { - for (int a = 0, addr = cpu_get_flat_addressing_mode_address(index) ;; ) { - phys_addr_decoder(addr, MEM_SLOT_CPU_32BIT, MEM_SLOT_CPU_32BIT); - CALL_MEMORY_WRITER(MEM_SLOT_CPU_32BIT, addr, data & 0xFF); - if (a == 3) - break; - addr++; - data >>= 8; - a++; + Uint32 addr32 = cpu_get_flat_addressing_mode_address(index); + ref_slot = MEM_SLOT_CPU_LINEAR; + resolve_special_wr_slot_on_demand(MEM_SLOT_CPU_LINEAR, addr32); + mem_slot_wr_func[MEM_SLOT_CPU_LINEAR](addr32 & 0xFFFFFFFU, data & 0xFFU); + resolve_special_wr_slot_on_demand(MEM_SLOT_CPU_LINEAR, ++addr32); + mem_slot_wr_func[MEM_SLOT_CPU_LINEAR](addr32 & 0xFFFFFFFU, (data >> 8) & 0xFFU); + resolve_special_wr_slot_on_demand(MEM_SLOT_CPU_LINEAR, ++addr32); + mem_slot_wr_func[MEM_SLOT_CPU_LINEAR](addr32 & 0xFFFFFFFU, (data >> 16) & 0xFFU); + resolve_special_wr_slot_on_demand(MEM_SLOT_CPU_LINEAR, ++addr32); + mem_slot_wr_func[MEM_SLOT_CPU_LINEAR](addr32 & 0xFFFFFFFU, (data >> 24) & 0xFFU); +} + + +#define CREATE_LINEAR_READER(name,slot) \ + Uint8 name ( const Uint32 addr32 ) { \ + resolve_special_rd_slot_on_demand(slot, addr32); \ + ref_slot = slot; \ + return mem_slot_rd_func[slot](addr32 & 0xFFFFFFFU); \ } -} +#define CREATE_LINEAR_WRITER(name,slot) \ + void name ( const Uint32 addr32, const Uint8 data ) { \ + resolve_special_wr_slot_on_demand(slot, addr32); \ + ref_slot = slot; \ + mem_slot_wr_func[slot](addr32 & 0xFFFFFFFU, data); \ + } -/* DMA related call-backs. We use a dedicated memory mapper "slot" for each DMA functions. - Source can be _written_ too (in case of SWAP operation for example). There are dedicated - slots for each functionality, so we don't need to re-map physical address again and again, - and we can take advantage of using the "cache" provided by phys_addr_decoder() which can - be especially efficient in case of linear operations, what DMA usually does. - Performance analysis (can be applied to other memory operations somewhat too, even CPU): - * if the next access for the given DMA func is in the same 256 page, phys_addr_decoder will return after just a comparsion operation - * if not, the "hint_slot" (3rd paramater) is used, if at least the same physical region (ie also fast-ram, etc) is used, again it's faster than full scan - * if even the previous statment is not true, phys_addr_decoder will scan the phyisical M65 memory layout to find the region, only */ +CREATE_LINEAR_READER(memory_dma_list_reader, MEM_SLOT_DMA_LIST) +CREATE_LINEAR_READER(memory_dma_source_mreader, MEM_SLOT_DMA_SOURCE) +CREATE_LINEAR_WRITER(memory_dma_source_mwriter, MEM_SLOT_DMA_SOURCE) +CREATE_LINEAR_READER(memory_dma_target_mreader, MEM_SLOT_DMA_TARGET) +CREATE_LINEAR_WRITER(memory_dma_target_mwriter, MEM_SLOT_DMA_TARGET) +CREATE_LINEAR_READER(debug_read_linear_byte, MEM_SLOT_DEBUG) +CREATE_LINEAR_WRITER(debug_write_linear_byte, MEM_SLOT_DEBUG) +CREATE_LINEAR_READER(sdebug_read_linear_byte, MEM_SLOT_SDEBUG) +CREATE_LINEAR_WRITER(sdebug_write_linear_byte, MEM_SLOT_SDEBUG) -Uint8 memory_dma_source_mreader ( int addr ) +Uint8 debug_read_cpu_byte ( const Uint16 addr16 ) { - phys_addr_decoder(addr, MEM_SLOT_DMA_RD_SRC, MEM_SLOT_DMA_RD_SRC); - return CALL_MEMORY_READER(MEM_SLOT_DMA_RD_SRC, addr); + ref_slot = addr16 >> 8; + return mem_slot_rd_func_real[ref_slot](mem_slot_rd_addr32[ref_slot] + (addr16 & 0xFFU)); } -void memory_dma_source_mwriter ( int addr, Uint8 data ) +void debug_write_cpu_byte ( const Uint16 addr16, const Uint8 data ) { - phys_addr_decoder(addr, MEM_SLOT_DMA_WR_SRC, MEM_SLOT_DMA_WR_SRC); - CALL_MEMORY_WRITER(MEM_SLOT_DMA_WR_SRC, addr, data); + ref_slot = addr16 >> 8; + mem_slot_wr_func_real[ref_slot](mem_slot_wr_addr32[ref_slot] + (addr16 & 0xFFU), data); } -Uint8 memory_dma_target_mreader ( int addr ) + +#ifdef MEM_WATCH_SUPPORT +static Uint8 memwatch_reader ( const Uint32 addr32 ) { - phys_addr_decoder(addr, MEM_SLOT_DMA_RD_DST, MEM_SLOT_DMA_RD_DST); - return CALL_MEMORY_READER(MEM_SLOT_DMA_RD_DST, addr); + Uint8 data = mem_slot_rd_func_real[ref_slot](addr32); + for (unsigned int i = 0, cpu_addr = (ref_slot << 8) + (addr32 & 0xFFU); i < mem_watchers.cpu_read_nums; i++) + if (cpu_addr >= w->cpu_begin && cpu_addr <= w->cpu_end && addr32 >= w->lin_begin && addr32 <= w->lin_end) + return watchmem_read_callback(i, addr32, cpu_addr, data); + return data; + + + + if (mem_watchers.cpu_read_nums && ref_slot < 0x100U) + for (unsigned int i = 0, cpu_addr = (ref_slot << 8) + (addr32 & 0xFFU); i < mem_watchers.cpu_read_nums; i++) + if (cpu_addr >= mem_watchers.cpu_read_list[i].begin && cpu_addr <= mem_watchers.cpu_read_list[i].end) + return memwatch_cpu_read_callback(i, addr32, cpu_addr, data); + if (mem_watchers.lin_read_nums) + for (unsigned int i = 0; i < mem_watchers.lin_read_nums; i++) + if (addr32 >= mem_watchers.lin_read_list[i].begin && addr32 <= mem_watchers.lin_read_list[i].end) + return memwatch_linear_read_callback(i, addr32, (ref_slot << 8) + (addr32 & 0xFFU), data); + return data; } -void memory_dma_target_mwriter ( int addr, Uint8 data ) + +static void memwatch_writer ( const Uint32 addr32, const Uint8 data ) { - phys_addr_decoder(addr, MEM_SLOT_DMA_WR_DST, MEM_SLOT_DMA_WR_DST); - CALL_MEMORY_WRITER(MEM_SLOT_DMA_WR_DST, addr, data); + if (mem_watchers_cpu_addr_read && ref_slot < 0x100U) { + const Uint16 cpu_addr = (ref_slot << 8) + (addr32 & 0xFFU); + for (unsigned int i = 0; i < mem_watchers_cpu_addr_read; i++) + if (mem_watchers_cpu_addr_read_list[i] == cpu_addr) { + data = debug_memwatch_cpu_read(addr32, cpu_addr, data); + break; + } + } + + + + // TODO: if memwatch-on-write is enabled for the slot - mem_slot_watcher[slot] & MEM_SLOT_WATCHER_WRITE is non-zero - then we should do something here + // mem_slot_wr_func_real[ref_slot](addr32, debug_memwatch_wrtie_callback(addr32, ref_slot, data)); + mem_slot_wr_func_real[ref_slot](addr32, data); } -Uint8 memory_dma_list_reader ( int addr ) +struct mem_watch_list_st { + Uint32 cpu_first, cpu_last, lin_first, lin_last; +}; +static const struct mem_watch_list_st *mem_watch_rd_list; +static const struct mem_watch_list_st *mem_watch_wr_list; + + +void memory_watch_clear_all ( void ) { - phys_addr_decoder(addr, MEM_SLOT_DMA_RD_LST, MEM_SLOT_DMA_RD_LST); - return CALL_MEMORY_READER(MEM_SLOT_DMA_RD_LST, addr); + for (unsigned int slot = 0; slot <= MEM_SLOT_LAST_REAL; slot++) { + if (mem_slot_watch[slot] & 0x7FU) { + mem_slot_watch[slot] = MEM_SLOT_WATCH_CHANGED; + } + } } -/* Debugger (ie: [uart]monitor) for reading/writing physical address */ -Uint8 memory_debug_read_phys_addr ( int addr ) + +void memory_watch_add ( void ) { - phys_addr_decoder(addr, MEM_SLOT_DEBUG_RESOLVER, MEM_SLOT_DEBUG_RESOLVER); - return CALL_MEMORY_READER(MEM_SLOT_DEBUG_RESOLVER, addr); } -void memory_debug_write_phys_addr ( int addr, Uint8 data ) + +void memory_watch_remove ( void ) { - phys_addr_decoder(addr, MEM_SLOT_DEBUG_RESOLVER, MEM_SLOT_DEBUG_RESOLVER); - CALL_MEMORY_WRITER(MEM_SLOT_DEBUG_RESOLVER, addr, data); } -int memory_cpurd2linear_xlat ( Uint16 cpu_addr) + +void memory_watch_start_transaction ( void ) { - int slot = cpu_addr >> 8; - return mem_page_rd_o[slot] + mem_page_refp[slot]->start + (int)(cpu_addr & 0xFF); + for (unsigned int slot = 0; slot <= MEM_SLOT_LAST_REAL; slot++) + mem_slot_watch[slot] &= ~MEM_SLOT_WATCH_CHANGED; } -/* the same as above but for CPU addresses */ -Uint8 memory_debug_read_cpu_addr ( Uint16 addr ) + +void memory_watch_commit ( void ) { - return CALL_MEMORY_READER(addr >> 8, addr); + for (unsigned int slot = 0; slot <= MEM_SLOT_LAST_REAL; slot++) + if (mem_slot_watch[slot] & MEM_SLOT_WATCH_CHANGED) { + mem_slot_watch[slot] &= ~MEM_SLOT_WATCH_CHANGED; + invalidate_slot(slot); + } } -void memory_debug_write_cpu_addr ( Uint16 addr, Uint8 data ) + +void memory_watch_notify_change ( const struct mem_watch_list_st *rd_list, const struct mem_watch_list_st *wr_list ) { - CALL_MEMORY_WRITER(addr >> 8, addr, data); + bool rd_need_cpu = false; + bool rd_need_lin = false; + bool wr_need_cpu = false; + bool wr_need_lin = false; + mem_watch_rd_list = rd_list; + mem_watch_rd_list_size = ... ; + mem_watch_wr_list = wr_list; + mem_watch_wr_list_size = ... ; + if (rd_list) + while (rd_list->cpu_last ) + } + + + for (unsigned int slot = 0; slot <= MEM_SLOT_LAST_REAL; slot++) + if (mem_slot_watcher[slot] & MEM_SLOT_WATCHER_CHANGED) { + mem_slot_watcher[slot] &= ~MEM_SLOT_WATCHER_CHANGED; + invalidate_slot(slot); + } } +#endif diff --git a/targets/mega65/memory_mapper.h b/targets/mega65/memory_mapper.h index 1fc16222..5bfdd3b1 100644 --- a/targets/mega65/memory_mapper.h +++ b/targets/mega65/memory_mapper.h @@ -20,31 +20,47 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define XEMU_MEGA65_MEMORY_MAPPER_H_INCLUDED extern void memory_init ( void ); -extern void memory_set_do_map ( void ); -extern void memory_set_vic3_rom_mapping ( Uint8 value ); -extern void memory_set_cpu_io_port ( int addr, Uint8 value ); -extern void memory_set_cpu_io_port_ddr_and_data ( Uint8 p0, Uint8 p1 ); -extern Uint8 memory_get_cpu_io_port ( int addr ); +extern void memory_set_rom_protection ( const bool protect ); +extern void memory_reconfigure ( + const Uint8 d030_value, const Uint8 new_io_mode, const Uint8 new_cpu_port0, const Uint8 new_cpu_port1, + const Uint32 new_map_mb_lo, const Uint32 new_map_ofs_lo, + const Uint32 new_map_mb_hi, const Uint32 new_map_ofs_hi, + const Uint8 new_map_mask, + const bool new_in_hypervisor +); +extern Uint8 memory_get_cpu_io_port ( const Uint8 addr ); +extern void memory_set_io_mode ( const Uint8 new_io_mode ); +extern void memory_write_d030 ( const Uint8 data ); -extern unsigned int mem_get_4k_region_short_desc ( char *p, const unsigned int n, const unsigned int i ); +extern int memory_cpu_addr_to_desc ( const Uint16 cpu_addr, char *p, const unsigned int n ); +extern Uint32 memory_cpu_addr_to_linear ( const Uint16 cpu_addr, Uint32 *wr_addr_p ); -extern Uint8 memory_debug_read_phys_addr ( int addr ); -extern void memory_debug_write_phys_addr ( int addr, Uint8 data ); -extern Uint8 memory_debug_read_cpu_addr ( Uint16 addr ); -extern void memory_debug_write_cpu_addr ( Uint16 addr, Uint8 data ); +// Non- CPU or DMA emulator memory acceses ("debug" read/write CPU/linear memory bytes) +extern Uint8 debug_read_linear_byte ( const Uint32 addr32 ); +extern Uint8 sdebug_read_linear_byte ( const Uint32 addr32 ); +extern void debug_write_linear_byte ( const Uint32 addr32, const Uint8 data ); +extern void sdebug_write_linear_byte ( const Uint32 addr32, const Uint8 data ); +// debug read/write CPU address functions: other than hardware emulation, these must be used for debug purposes (monitor/debugger, etc) +extern Uint8 debug_read_cpu_byte ( const Uint16 addr16 ); +extern void debug_write_cpu_byte ( const Uint16 addr16, const Uint8 data ); // DMA implementation related, used by dma65.c: -extern Uint8 memory_dma_source_mreader ( int addr ); -extern void memory_dma_source_mwriter ( int addr, Uint8 data ); -extern Uint8 memory_dma_target_mreader ( int addr ); -extern void memory_dma_target_mwriter ( int addr, Uint8 data ); -extern Uint8 memory_dma_list_reader ( int addr ); +extern Uint8 memory_dma_source_mreader ( const Uint32 addr32 ); +extern void memory_dma_source_mwriter ( const Uint32 addr32, const Uint8 data ); +extern Uint8 memory_dma_target_mreader ( const Uint32 addr32 ); +extern void memory_dma_target_mwriter ( const Uint32 addr32, const Uint8 data ); +extern Uint8 memory_dma_list_reader ( const Uint32 addr32 ); -extern int map_mask, map_offset_low, map_offset_high, map_megabyte_low, map_megabyte_high; -extern int rom_protect, skip_unhandled_mem; -extern Uint8 main_ram[512 << 10], colour_ram[0x8000], char_wom[0x2000], hypervisor_ram[0x4000]; +// MAP related variables, do not change these values directly! +extern Uint32 map_offset_low, map_offset_high, map_megabyte_low, map_megabyte_high; +extern Uint8 map_mask; + +extern Uint8 main_ram[512 << 10], colour_ram[0x8000], char_ram[0x2000], hypervisor_ram[0x4000]; +extern Uint32 main_ram_size; #define SLOW_RAM_SIZE (8 << 20) -extern Uint8 slow_ram[SLOW_RAM_SIZE]; +extern Uint8 attic_ram[SLOW_RAM_SIZE]; + +extern Uint8 io_mode; // "VIC" I/O mode: do not change this value directly! #define I2C_UUID_OFFSET 0x100 #define I2C_UUID_SIZE 8 @@ -54,14 +70,7 @@ extern Uint8 slow_ram[SLOW_RAM_SIZE]; #define I2C_NVRAM_SIZE 64 extern Uint8 i2c_regs[0x1000]; +extern int skip_unhandled_mem; extern int cpu_rmw_old_data; -static XEMU_INLINE void write_colour_ram ( const int addr, const Uint8 data ) -{ - colour_ram[addr] = data; - // we also need to update the corresponding part of the main RAM, if it's the first 2K of the colour RAM! - if (addr < 2048) - main_ram[addr + 0x1F800] = data; -} - #endif diff --git a/targets/mega65/sdcard.c b/targets/mega65/sdcard.c index eb298ea1..1caeee2e 100644 --- a/targets/mega65/sdcard.c +++ b/targets/mega65/sdcard.c @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "memcontent.h" #include "hypervisor.h" #include "vic4.h" +#include "memory_mapper.h" #include "configdb.h" #include "xemu/emutools_config.h" #include "xemu/compressed_disk_image.h" @@ -1240,7 +1241,7 @@ Uint8 sdcard_read_register ( const int reg ) // bits 2 and 3 is always zero in Xemu (no drive virtualization for drive 0 and 1) return (vic_registers[0x30] & 1) | // $D68A.0 SD:CDC00 (read only) Set if colour RAM at $DC00 - (vic_iomode & 2) | // $D68A.1 SD:VICIII (read only) Set if VIC-IV or ethernet IO bank visible [same bit pos as in vic_iomode for mode-4 and mode-ETH!] + (io_mode & 2) | // $D68A.1 SD:VICIII (read only) Set if VIC-IV or ethernet IO bank visible [same bit pos as in vic_iomode for mode-4 and mode-ETH!] (data & (128 + 64)); // size info for disk mounting break; case 0xB: diff --git a/targets/mega65/sdcard.h b/targets/mega65/sdcard.h index 3e94d22d..7da158d6 100644 --- a/targets/mega65/sdcard.h +++ b/targets/mega65/sdcard.h @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/targets/mega65/ui.c b/targets/mega65/ui.c index 519beeb8..b193d0b9 100644 --- a/targets/mega65/ui.c +++ b/targets/mega65/ui.c @@ -506,7 +506,7 @@ static void ui_dump_hyperram ( void ) fnbuf, sizeof fnbuf )) { - xemu_save_file(fnbuf, slow_ram, SLOW_RAM_SIZE, "Cannot dump hyperRAM content into file"); + xemu_save_file(fnbuf, attic_ram, SLOW_RAM_SIZE, "Cannot dump hyperRAM content into file"); } } @@ -543,7 +543,7 @@ static void ui_emu_info ( void ) sdcard_get_mount_info(0, NULL), sdcard_get_mount_info(1, NULL), memory_get_cpu_io_port(0) & 7, memory_get_cpu_io_port(1) & 7, cpu65.pc, memory_cpurd2linear_xlat(cpu65.pc), - iomode_names[vic_iomode], videostd_name, (vic_registers[0x5D] & 0x80) ? "enabled" : "disabled", + iomode_names[io_mode], videostd_name, (vic_registers[0x5D] & 0x80) ? "enabled" : "disabled", td_stat_str, xemu_get_uname_string(), emu_fs_is_utf8 ? "UTF8-FS" : "ASCII-FS", PRINTF_U64, PRINTF_X64, PRINTF_S64 ); diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index 5ed8152f..71088dd6 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -43,7 +43,6 @@ static Uint32 *current_pixel; // current_pixel pointer to the rendering targ static Uint32 *pixel_start; // points to the end and start of the buffer static Uint32 *pixel_raster_start; // first pixel of current raster Uint8 vic_registers[0x80]; // VIC4 registers -unsigned int vic_iomode; // VIC2/VIC3/VIC4 mode static int compare_raster; // raster compare (9 bits width) data static int logical_raster = 0; static int interrupt_status; // Interrupt status of VIC @@ -151,7 +150,7 @@ void vic_reset ( void ) { vic_frame_counter = 0; vic_frame_counter_since_boot = 0; - vic_iomode = VIC2_IOMODE; + memory_set_io_mode(VIC2_IOMODE); vic_color_register_mask = 0x0F; interrupt_status = 0; compare_raster = 0; @@ -658,11 +657,11 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) case 0x4554: vic_new_iomode = VIC4ETH_IOMODE; break; case 0x4753: vic_new_iomode = VIC4_IOMODE; break; } - if (vic_new_iomode != vic_iomode) { + if (vic_new_iomode != io_mode) { static const Uint8 color_register_masks[4] = { 0x0F, 0xFF, 0xFF, 0xFF }; - DEBUG("VIC4: changing I/O mode %d(%s) -> %d(%s)" NL, vic_iomode, iomode_names[vic_iomode], vic_new_iomode, iomode_names[vic_new_iomode]); - vic_iomode = vic_new_iomode; - vic_color_register_mask = color_register_masks[vic_iomode]; + DEBUG("VIC4: changing I/O mode %d(%s) -> %d(%s)" NL, io_mode, iomode_names[io_mode], vic_new_iomode, iomode_names[vic_new_iomode]); + memory_set_io_mode(vic_new_iomode); + vic_color_register_mask = color_register_masks[io_mode]; } } else DEBUGPRINT("VIC4: warning: I/O mode KEY $D02F register wanted to be written (with $%02X) in hypervisor mode! PC=$%04X" NL, data, cpu65.old_pc); @@ -676,7 +675,7 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) return; // it IS important to have return here, since it's not a "real" VIC4 mode register's view in another mode!! /* --- NO MORE VIC2 REGS FROM HERE --- */ CASE_VIC_3_4(0x30): - memory_set_vic3_rom_mapping(data); + memory_write_d030(data); check_if_rom_palette(!(data & 4)); break; CASE_VIC_3_4(0x31): @@ -1360,7 +1359,7 @@ static XEMU_INLINE Uint8 *get_charset_effective_addr ( void ) // However it seems even MEGA65 does not support this. // FIXME: how we can be sure, there won't be any out-of-bound access for the relative small WOM then? if (!REG_BMM && (addr == 0x1000 || addr == 0x9000 || addr == 0x1800 || addr == 0x9800)) - return char_wom + (addr & 0xFFF); + return char_ram + (addr & 0xFFF); // FIXME XXX this is a fixed constant for checking. if (XEMU_UNLIKELY(addr > 0x60000)) // this is valid since we still have got some extra unused RAM left to go beyond actual RAM while bulding the frame return main_ram + 0x60000; // give some unused ram array of emulaton, thus whatever high value set by user as ADDR, won't overflow during the frame @@ -1728,8 +1727,8 @@ int vic4_snapshot_load_state ( const struct xemu_snapshot_definition_st *def, st memcpy(vic_palette_bytes_green, buffer + 0x100 + NO_OF_PALETTE_REGS, NO_OF_PALETTE_REGS); memcpy(vic_palette_bytes_blue, buffer + 0x100 + 2 * NO_OF_PALETTE_REGS, NO_OF_PALETTE_REGS); vic4_revalidate_all_palette(); - vic_iomode = buffer[0]; - DEBUG("SNAP: VIC4: changing I/O mode to %d(%s)" NL, vic_iomode, iomode_names[vic_iomode]); + io_mode = buffer[0]; + DEBUG("SNAP: VIC4: changing I/O mode to %d(%s)" NL, io_mode, iomode_names[io_mode]); interrupt_status = (int)P_AS_BE32(buffer + 1); return 0; } @@ -1747,7 +1746,7 @@ int vic4_snapshot_save_state ( const struct xemu_snapshot_definition_st *def ) memcpy(buffer + 0x100 , vic_palette_bytes_red, NO_OF_PALETTE_REGS); memcpy(buffer + 0x100 + NO_OF_PALETTE_REGS, vic_palette_bytes_green, NO_OF_PALETTE_REGS); memcpy(buffer + 0x100 + 2 * NO_OF_PALETTE_REGS, vic_palette_bytes_blue, NO_OF_PALETTE_REGS); - buffer[0] = vic_iomode; + buffer[0] = io_mode; U32_AS_BE(buffer + 1, interrupt_status); return xemusnap_write_sub_block(buffer, sizeof buffer); } diff --git a/targets/mega65/vic4.h b/targets/mega65/vic4.h index 8b5b3061..d4aa242a 100644 --- a/targets/mega65/vic4.h +++ b/targets/mega65/vic4.h @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) Copyright (C)2020-2022 Hernán Di Pietro This program is free software; you can redistribute it and/or modify @@ -30,7 +30,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define VIC4_IOMODE 3 // bit1 of IO-mode is set: either VIC4ETH_IOMODE or VIC4_IOMODE -#define VIC4_LIKE_IO_MODE() (vic_iomode & 2U) +#define VIC4_LIKE_IO_MODE() (io_mode & 2U) // Horizontal sync frequencies (in Hertz) for NTSC and PAL video output of MEGA65. Must be float. #define PAL_LINE_FREQ 31250.0 @@ -239,7 +239,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // Current state -extern unsigned int vic_iomode; //extern int scanline; extern Uint8 vic_registers[]; extern Uint8 c128_d030_reg;