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;