From b6db38ec7948d4c2076ebcc87c29eb31c11a8e08 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Sun, 12 May 2024 22:39:55 +0200 Subject: [PATCH 1/8] CORE: simple debug hooks into CPU65 emu #11 To allow easier debugger implementation with breakpoints, and such, a new (optional to be used) mechanism is introduced. It allows the CPU65 emulation core to call target emulator defined functions on each op code executions (and in case of IRQ and NMI acceptance). The target emulator itself can decide what to do in the callbacks, it can simply return, or instructing the CPU emulation to be stopped (ie: breakpoint or step based opcode execution during debugging). NOTE: still, the target emulator's main loop must aware of this, the CPU65 core itself alone cannot stop the whole emulation in a sane way. --- xemu/cpu65.c | 49 +++++++++++++++++++++++++++++++++++++-- xemu/cpu65.h | 33 +++++++++++++++++++++++++- xemu/emutools_basicdefs.h | 1 + 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/xemu/cpu65.c b/xemu/cpu65.c index 66567b2f..359774b7 100644 --- a/xemu/cpu65.c +++ b/xemu/cpu65.c @@ -1,5 +1,5 @@ /* 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 IS AN UGLY PIECE OF SOURCE REALLY. @@ -43,6 +43,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ * or visit this page: http://www.gnu.org/licenses/gpl-2.0.html */ +#define IN_CPU65_CORE + #include "xemu/emutools_basicdefs.h" #ifndef CPU_CUSTOM_INCLUDED #include "xemu/cpu65.h" @@ -350,7 +352,28 @@ Uint8 cpu65_get_pf ( void ) { (CPU65.pf_c ? CPU65_PF_C : 0); } + +// optional to be called, cpu_reset() calls this as well. See cpu65_reset() to learn more +void cpu65_init ( void ) { + static bool done = false; + if (done) + return; + done = true; +#ifdef CPU65_EXECUTION_CALLBACK_SUPPORT + cpu65_disable_debug_callbacks(); +#endif +} + + void cpu65_reset ( void ) { + // Because of compatibility with older targets (which does not use the + // init func), we call cpu65_init() from here as well + // Note: cpu65_init() itself is secured to be executed only once, even + // if it's called more times from here in case of multiple resets during + // emulation. I don't want to do things here always, as some may want to + // debug the reset process itself, so some things shouldn't be reseted, + // and those will go into cpu65_init() - which needs to be set only once. + cpu65_init(); cpu65_set_pf(0x34); CPU65.s = 0xFF; CPU65.irqLevel = CPU65.nmiEdge = 0; @@ -813,6 +836,24 @@ static XEMU_INLINE void _RORQ_Q ( void ) { #endif +#ifdef CPU65_EXECUTION_CALLBACK_SUPPORT +# ifdef CPU_STEP_MULTI_OPS +# define ELAPSED_CYCLES_BEFORE_CURRENT_OP all_cycles +# else + // if CPU_STEP_MULTI_OPS is not enabled there was no previous op within the current invokation of CPU65 emulation step +# define ELAPSED_CYCLES_BEFORE_CURRENT_OP 0 +# endif +# define DO_CPU65_EXECUTION_CALLBACK(cb_func) \ + if (XEMU_UNLIKELY(CPU65.execution_debug_callback)) { \ + cb_func(); \ + if (XEMU_UNLIKELY(!CPU65.running)) \ + return ELAPSED_CYCLES_BEFORE_CURRENT_OP; \ + } +#else +# define DO_CPU65_EXECUTION_CALLBACK(cb_func) +#endif + + /* ------------------------------------------------------------------------ * * CPU EMULATION, OPCODE DECODING + RUN * * ------------------------------------------------------------------------ */ @@ -840,6 +881,7 @@ int cpu65_step ( #ifdef DEBUG_CPU DEBUG("CPU: serving NMI on NMI edge at PC $%04X" NL, CPU65.pc); #endif + DO_CPU65_EXECUTION_CALLBACK(cpu65_nmi_debug_callback); CPU65.nmiEdge = 0; pushWord(CPU65.pc); push(cpu65_get_pf()); // no CPU65_PF_B is pushed! @@ -864,6 +906,7 @@ int cpu65_step ( #ifdef DEBUG_CPU DEBUG("CPU: serving IRQ on IRQ level at PC $%04X" NL, CPU65.pc); #endif + DO_CPU65_EXECUTION_CALLBACK(cpu65_irq_debug_callback); pushWord(CPU65.pc); push(cpu65_get_pf()); // no CPU65_PF_B is pushed! CPU65.pf_i = 1; @@ -881,7 +924,9 @@ int cpu65_step ( if (CPU65.pc == 0) DEBUG("CPU: WARN: PC at zero!" NL); #endif - CPU65.op = readByte(CPU65.pc++); + CPU65.op = readByte(CPU65.pc); + DO_CPU65_EXECUTION_CALLBACK(cpu65_execution_debug_callback); + CPU65.pc++; #ifdef DEBUG_CPU DEBUG("CPU: at $%04X opcode = $%02X %s %s A=%02X X=%02X Y=%02X Z=%02X SP=%02X" NL, (CPU65.pc - 1) & 0xFFFF, CPU65.op, opcode_names[CPU65.op], opcode_adm_names[opcode_adms[CPU65.op]], CPU65.a, CPU65.x, CPU65.y, CPU65.z, CPU65.s diff --git a/xemu/cpu65.h b/xemu/cpu65.h index 2f69f978..2c2f30f0 100644 --- a/xemu/cpu65.h +++ b/xemu/cpu65.h @@ -1,5 +1,5 @@ /* 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) | For more information about "cpu65" please also read comments in file cpu65.c | @@ -72,6 +72,12 @@ struct cpu65_st { int multi_step_stop_trigger; // not used, only with multi-op mode but still here because some devices (like DMA) would use it int irqLevel, nmiEdge; int op_cycles; +#ifdef CPU65_EXECUTION_CALLBACK_SUPPORT + bool execution_debug_callback; + // this must be used by the emulator target, CPU65 core will/can set this value, but never uses it: + // >> Must be done by the target to see if it's valid to call cpu65_step() **at all** << + bool running; // this must be used by the emulator target, CPU65 core will/can set this value, but never uses it. Must be done by the target to see if it's valid to call +#endif }; #ifndef CPU65 @@ -108,6 +114,7 @@ extern void cpu65_illegal_opcode_callback ( void ); #endif extern void cpu65_reset ( void ); +extern void cpu65_init ( void ); // optional to be called, cpu_reset() calls this as well. See cpu65.c for more info on this. extern void cpu65_debug_set_pc ( const Uint16 new_pc ); extern int cpu65_step ( #ifdef CPU_STEP_MULTI_OPS @@ -117,6 +124,30 @@ extern int cpu65_step ( #endif ); +#ifdef CPU65_EXECUTION_CALLBACK_SUPPORT +// These must be provided by the target emulator: +extern void cpu65_nmi_debug_callback ( void ); +extern void cpu65_irq_debug_callback ( void ); +extern void cpu65_execution_debug_callback ( void ); +// -- +static XEMU_INLINE void cpu65_stop ( void ) { + CPU65.running = false; +} +static XEMU_INLINE void cpu65_continue ( void ) { + CPU65.running = true; +} +static XEMU_INLINE void cpu65_enable_debug_callbacks ( void ) { + CPU65.execution_debug_callback = true; +} +static XEMU_INLINE void cpu65_disable_debug_callbacks ( void ) { + CPU65.running = true; + CPU65.execution_debug_callback = false; +} +#define CPU65_IS_RUNNING() CPU65.running +#else +#define CPU65_IS_RUNNING() 1 +#endif + #ifdef CPU65_TRAP_OPCODE extern int cpu65_trap_callback ( const Uint8 opcode ); #endif diff --git a/xemu/emutools_basicdefs.h b/xemu/emutools_basicdefs.h index 87b6b374..ef8fbde7 100644 --- a/xemu/emutools_basicdefs.h +++ b/xemu/emutools_basicdefs.h @@ -26,6 +26,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include +#include #define USE_REGPARM From 45f9ebfed919bab759ee329cffebda1b4de15b01 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Sun, 12 May 2024 23:08:51 +0200 Subject: [PATCH 2/8] MEGA65: new memory decoder #209 #378 This quite big change rewrites the MEGA65 emulation memory decoder subsystem. Some of the reasons: * Create a new, not overcomplicated decoder which can be understood at all without major headache, and can be also faster as per #209 * Taking account some new findings about how C65 worked (both Xemu and MEGA65 was wrong here) and adopted since then by MEGA65 as per #378 * More about the future (not ready in this commit): allow functionality for debugger implementation "watchmem" which can monitor about every memory r/w events debugger want to check as per #11 This commit is quite big as was mentioned, and may introduce some regressions. So it's important to have feedback in those cases. Also it changes the behaviour of memory decoding (#378). The new decoder seems to be already faster than the old one and more easy to understand and maintain. Also there is some room for future improvements still in every areas, including optimizations. The "memwatch" part currently is not usable though, but the basis are there to be able to implement that at all (wouldn't be possible with the older scheme). --- targets/mega65/cpu_custom_functions.h | 122 +- targets/mega65/dma65.c | 6 +- targets/mega65/dma65.h | 2 +- targets/mega65/hdos.c | 8 +- targets/mega65/hdos.h | 2 +- targets/mega65/hypervisor.c | 71 +- targets/mega65/hypervisor.h | 4 +- targets/mega65/io_mapper.c | 14 +- targets/mega65/io_mapper.h | 2 +- targets/mega65/matrix_mode.c | 20 +- targets/mega65/mega65.c | 42 +- targets/mega65/memory_mapper.c | 1618 ++++++++++++++----------- targets/mega65/memory_mapper.h | 63 +- targets/mega65/sdcard.c | 5 +- targets/mega65/sdcard.h | 2 +- targets/mega65/ui.c | 4 +- targets/mega65/vic4.c | 21 +- targets/mega65/vic4.h | 5 +- 18 files changed, 1108 insertions(+), 903 deletions(-) diff --git a/targets/mega65/cpu_custom_functions.h b/targets/mega65/cpu_custom_functions.h index 48c1fed1..0b39c3cc 100644 --- a/targets/mega65/cpu_custom_functions.h +++ b/targets/mega65/cpu_custom_functions.h @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2022 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,49 +25,37 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __XEMU_MEGA65_CPU_CUSTOM_FUNCTIONS_H_INCLUDED #define __XEMU_MEGA65_CPU_CUSTOM_FUNCTIONS_H_INCLUDED -#ifdef CPU_CUSTOM_MEMORY_FUNCTIONS_H -#define CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR static XEMU_INLINE -#else -#ifndef ALLOW_CPU_CUSTOM_FUNCTIONS_INCLUDE -#error "cpu_custom_functions.h must not be included by anything other than the CPU emulator and memory_mapper.c" -#endif -#define CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR -#endif +#define MEM_USE_DATA_POINTERS -#if 1 -#define CALL_MEMORY_READER(slot,addr) mem_page_rd_f[slot](mem_page_rd_o[slot] + ((addr) & 0xFF)) -#define CALL_MEMORY_WRITER(slot,addr,data) mem_page_wr_f[slot](mem_page_wr_o[slot] + ((addr) & 0xFF), data) -#define CALL_MEMORY_READER_PAGED(slot,addr) mem_page_rd_f[slot](mem_page_rd_o[slot] + addr) -#define CALL_MEMORY_WRITER_PAGED(slot,addr,data) mem_page_wr_f[slot](mem_page_wr_o[slot] + addr, data) -#define SAVE_USED_SLOT(slot) last_slot_ref = slot -#define MEMORY_HANDLERS_ADDR_TYPE int area_offset -#define GET_READER_OFFSET() area_offset -#define GET_WRITER_OFFSET() area_offset -#define GET_OFFSET_BYTE_ONLY() area_offset -#define GET_USED_SLOT() last_slot_ref +#ifdef MEM_USE_DATA_POINTERS +#define MEM_DATA_POINTER_HINTING_STRENGTH(_condition) XEMU_LIKELY(_condition) +//#define MEM_DATA_POINTER_HINTING_STRENGTH(_condition) (_condition) #endif -#if 0 -#define CALL_MEMORY_READER(slot,addr) mem_page_rd_f[slot](slot, addr) -#define CALL_MEMORY_WRITER(slot,addr,data) mem_page_wr_f[slot](slot, addr, data) -#define CALL_MEMORY_READER_PAGED(slot,addr) mem_page_rd_f[slot](slot, addr) -#define CALL_MEMORY_WRITER_PAGED(slot,addr,data) mem_page_wr_f[slot](slot, addr, data) -#define SAVE_USED_SLOT(slot) -#define MEMORY_HANDLERS_ADDR_TYPE int slot, Uint8 lo_addr -#define GET_READER_OFFSET() (mem_page_rd_o[slot] + lo_addr) -#define GET_WRITER_OFFSET() (mem_page_wr_o[slot] + lo_addr) -#define GET_OFFSET_BYTE_ONLY() lo_addr -#define GET_USED_SLOT() slot +#ifdef CPU_CUSTOM_MEMORY_FUNCTIONS_H +# define CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR static XEMU_INLINE +#else +# ifndef ALLOW_CPU_CUSTOM_FUNCTIONS_INCLUDE +# error "cpu_custom_functions.h must not be included by anything other than the CPU emulator and memory_mapper.c" +# endif +# define CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR #endif +extern Uint32 ref_slot; + +typedef Uint8 (*mem_slot_rd_func_t)(const Uint32 addr32); +typedef void (*mem_slot_wr_func_t)(const Uint32 addr32, const Uint8 data); -typedef Uint8 (*mem_page_rd_f_type)(MEMORY_HANDLERS_ADDR_TYPE); -typedef void (*mem_page_wr_f_type)(MEMORY_HANDLERS_ADDR_TYPE, Uint8 data); +#define MEM_SLOTS_TOTAL_EXPORTED 0x106 +extern mem_slot_rd_func_t mem_slot_rd_func[MEM_SLOTS_TOTAL_EXPORTED]; +extern mem_slot_wr_func_t mem_slot_wr_func[MEM_SLOTS_TOTAL_EXPORTED]; +extern Uint32 mem_slot_rd_addr32[MEM_SLOTS_TOTAL_EXPORTED]; +extern Uint32 mem_slot_wr_addr32[MEM_SLOTS_TOTAL_EXPORTED]; +#ifdef MEM_USE_DATA_POINTERS +extern Uint8 *mem_slot_rd_data[MEM_SLOTS_TOTAL_EXPORTED]; +extern Uint8 *mem_slot_wr_data[MEM_SLOTS_TOTAL_EXPORTED]; +#endif -extern int mem_page_rd_o[]; -extern int mem_page_wr_o[]; -extern mem_page_rd_f_type mem_page_rd_f[]; -extern mem_page_wr_f_type mem_page_wr_f[]; extern int cpu_rmw_old_data; extern void cpu65_write_linear_opcode_callback ( Uint8 data ); @@ -77,20 +65,33 @@ extern Uint32 cpu65_read_linear_long_opcode_callback ( const Uint8 index ); extern void cpu65_illegal_opcode_callback ( void ); -extern int memory_cpurd2linear_xlat ( Uint16 cpu_addr); +extern Uint32 memory_cpu_addr_to_linear ( const Uint16 cpu_addr, Uint32 *wr_addr_p ); +#define memory_cpurd2linear_xlat(_cpu_addr) memory_cpu_addr_to_linear(_cpu_addr,NULL) -CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR Uint8 cpu65_read_callback ( Uint16 addr ) { - return CALL_MEMORY_READER(addr >> 8, addr); -} -CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR void cpu65_write_callback ( Uint16 addr, Uint8 data ) { - CALL_MEMORY_WRITER(addr >> 8, addr, data); -} -CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR Uint8 cpu65_read_paged_callback ( Uint8 page, Uint8 addr8 ) { - return CALL_MEMORY_READER_PAGED(page, addr8); +CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR Uint8 cpu65_read_callback ( const Uint16 addr16 ) +{ +#ifdef MEM_USE_DATA_POINTERS + register const Uint8 *p = mem_slot_rd_data[addr16 >> 8]; + if (MEM_DATA_POINTER_HINTING_STRENGTH(p)) + return p[addr16 & 0xFFU]; +#endif + ref_slot = addr16 >> 8; + return mem_slot_rd_func[ref_slot](mem_slot_rd_addr32[ref_slot] + (addr16 & 0xFFU)); } -CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR void cpu65_write_paged_callback ( Uint8 page, Uint8 addr8, Uint8 data ) { - CALL_MEMORY_WRITER_PAGED(page, addr8, data); + +CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR void cpu65_write_callback ( const Uint16 addr16, const Uint8 data ) +{ +#ifdef MEM_USE_DATA_POINTERS + register Uint8 *p = mem_slot_wr_data[addr16 >> 8]; + if (MEM_DATA_POINTER_HINTING_STRENGTH(p)) { + p[addr16 & 0xFFU] = data; + return; + } +#endif + ref_slot = addr16 >> 8; + mem_slot_wr_func[ref_slot](mem_slot_wr_addr32[ref_slot] + (addr16 & 0xFFU), data); } + // Called in case of an RMW (read-modify-write) opcode write access. // Original NMOS 6502 would write the old_data first, then new_data. // It has no inpact in case of normal RAM, but it *does* with an I/O register in some cases! @@ -98,19 +99,26 @@ CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR void cpu65_write_paged_callback ( Uint8 p // However this leads to incompatibilities, as some software used the RMW behavour by intent. // Thus MEGA65 fixed the problem to "restore" the old way of RMW behaviour. // I also follow this path here, even if it's *NOT* what 65CE02 would do actually! -CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR void cpu65_write_rmw_callback ( Uint16 addr, Uint8 old_data, Uint8 new_data ) { +CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR void cpu65_write_rmw_callback ( const Uint16 addr16, const Uint8 old_data, const Uint8 new_data ) +{ +// Nah! It seems, enabling this, makes some software _slower_ ... Like polymega. Probably it does lots of I/O writes, so this +// extra optimization just takes time to check which won't be used anyway in case of I/O. Oh, well. +#if 0 +#ifdef MEM_USE_DATA_POINTERS + // if there is data pointer, it cannot be I/O anyway, so the whole RMW business shouldn't matter here + register Uint8 *p = mem_slot_wr_data[addr16 >> 8]; + if (p) { + p[addr16 & 0xFFU] = new_data; + return; + } +#endif +#endif cpu_rmw_old_data = old_data; // It's the backend's (which realizes the op) responsibility to handle or not handle the RMW behaviour, // based on the fact if cpu_rmw_old_data is non-negative (being an int type) when it holds the "old_data". - CALL_MEMORY_WRITER(addr >> 8, addr, new_data); + ref_slot = addr16 >> 8; + mem_slot_wr_func[ref_slot](mem_slot_wr_addr32[ref_slot] + (addr16 & 0xFFU), new_data); cpu_rmw_old_data = -1; } -CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR void cpu65_write_rmw_paged_callback ( Uint8 page, Uint8 addr8, Uint8 old_data, Uint8 new_data ) { - cpu_rmw_old_data = old_data; - CALL_MEMORY_WRITER_PAGED(page, addr8, new_data); - cpu_rmw_old_data = -1; -} - -#undef CPU_CUSTOM_FUNCTIONS_INLINE_DECORATOR #endif diff --git a/targets/mega65/dma65.c b/targets/mega65/dma65.c index a5d1d515..053bbfdb 100644 --- a/targets/mega65/dma65.c +++ b/targets/mega65/dma65.c @@ -1,6 +1,6 @@ /* F018 DMA core emulation for MEGA65 Part of the Xemu project. https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -87,13 +87,13 @@ static struct { static inline Uint8 io_dma_reader ( const unsigned int addr ) { - return io_read((addr & 0xFFFU) + (vic_iomode << 12)); + return io_read((addr & 0xFFFU) + (io_mode << 12)); } static inline void io_dma_writer ( const unsigned int addr, Uint8 data ) { - io_write((addr & 0xFFFU) + (vic_iomode << 12), data); + io_write((addr & 0xFFFU) + (io_mode << 12), data); } diff --git a/targets/mega65/dma65.h b/targets/mega65/dma65.h index ae1ed6ca..8eb89e9d 100644 --- a/targets/mega65/dma65.h +++ b/targets/mega65/dma65.h @@ -1,6 +1,6 @@ /* F018 DMA core emulation for MEGA65 Part of the Xemu project. https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/targets/mega65/hdos.c b/targets/mega65/hdos.c index 4b4187f9..e4aacd7f 100644 --- a/targets/mega65/hdos.c +++ b/targets/mega65/hdos.c @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -97,7 +97,7 @@ static int copy_mem_from_user ( Uint8 *target, int max_size, const int terminato // DOS calls should not have user specified data >= $8000! if (source_cpu_addr >= 0x8000) return -1; - const Uint8 byte = memory_debug_read_cpu_addr(source_cpu_addr++); + const Uint8 byte = debug_read_cpu_byte(source_cpu_addr++); *target++ = byte; len++; if (len >= max_size) { @@ -124,7 +124,7 @@ static int copy_mem_to_user ( unsigned int target_cpu_addr, const Uint8 *source, while (size) { if (target_cpu_addr >= 0x8000) return -1; - memory_debug_write_cpu_addr(target_cpu_addr++, *source++); + debug_write_cpu_byte(target_cpu_addr++, *source++); size--; len++; } @@ -465,7 +465,7 @@ static void hdos_virt_loadfile ( const Uint32 addr_base ) break; } for (const Uint8 *b = buffer; ret > 0; ret--, b++, addr_ofs++) - memory_debug_write_phys_addr(addr_base + (addr_ofs & 0xFFFFFF), *b); + debug_write_linear_byte(addr_base + (addr_ofs & 0xFFFFFF), *b); } close(fd); if (ret < 0 || loaded != st.st_size) { diff --git a/targets/mega65/hdos.h b/targets/mega65/hdos.h index 6f54d3ed..dbe8c9fe 100644 --- a/targets/mega65/hdos.h +++ b/targets/mega65/hdos.h @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/targets/mega65/hypervisor.c b/targets/mega65/hypervisor.c index 47b2da61..6dca5ed3 100644 --- a/targets/mega65/hypervisor.c +++ b/targets/mega65/hypervisor.c @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -37,7 +37,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include -int in_hypervisor; // mega65 hypervisor mode +bool in_hypervisor; // mega65 hypervisor mode char hyppo_version_string[64]; int hickup_is_overriden = 0; int hypervisor_is_debugged = 0; @@ -108,7 +108,7 @@ void hypervisor_enter_via_write_trap ( int trapno ) if (do_nop_check) { // FIXME: for real there should be a memory reading function independent to the one used by the CPU, since // this has some side effects to just fetch a byte to check something, which is otherwise used normally to fetch CPU opcodes and such - Uint8 skipped_byte = cpu65_read_callback(cpu65.pc); + Uint8 skipped_byte = debug_read_cpu_byte(cpu65.pc); if (XEMU_UNLIKELY(skipped_byte != 0xEA && skipped_byte != 0xB8)) { // $EA = opcode for NOP, $B8 = opcode for CLV char msg[256]; snprintf(msg, sizeof msg, @@ -160,27 +160,26 @@ void hypervisor_enter ( int trapno ) D6XX_registers[0x4F] = map_megabyte_high >> 20; D6XX_registers[0x50] = memory_get_cpu_io_port(0); D6XX_registers[0x51] = memory_get_cpu_io_port(1); - D6XX_registers[0x52] = vic_iomode; + D6XX_registers[0x52] = io_mode; //D6XX_registers[0x53] = 0; // GS $D653 - Hypervisor DMAgic source MB - *UNUSED* //D6XX_registers[0x54] = 0; // GS $D654 - Hypervisor DMAgic destination MB - *UNUSED* dma_get_list_addr_as_bytes(D6XX_registers + 0x55); // GS $D655-$D658 - Hypervisor DMAGic list address bits 27-0 - // Now entering into hypervisor mode - in_hypervisor = 1; // this will cause apply_memory_config to map hypervisor RAM, also for checks later to out-of-bound execution of hypervisor RAM, etc ... - // In hypervisor mode, VIC4 I/O mode is implied. I also disable $D02F writing to take effect while in hypervisor mode in vic4.c! - vic_iomode = VIC4_IOMODE; - memory_set_cpu_io_port_ddr_and_data(0x3F, 0x35); // sets all-RAM + I/O config up! + // Now entering into hypervisor mode: we use memory_reconfigure() to set all the stuff needed + setting up "in_hypervisor" value as well + memory_reconfigure( + 0, // D030 ROM banking turning off + VIC4_IOMODE, // VIC4 I/O mode to be used (on MEGA65, in hypervisor mode it's always the case! the handler in vic4.c ensures, we cannot even modify this) + 0x3F, 0x35, // set CPU I/O port DDR+DATA: all-RAM + I/O config + map_megabyte_low, map_offset_low, // low mapping is left as-is + 0xFFU << 20, 0xF0000U, // high mapping though is being modified + (map_mask & 0xFU) | 0x30U, // mapping: 0011XXXX (it seems low region map mask is not changed by hypervisor entry) + true // this will sets in_hypervisor to TRUE!!!! + ); cpu65.pf_d = 0; // clear decimal mode ... according to Paul, punnishment will be done, if it's removed :-) cpu65.pf_i = 1; // disable IRQ in hypervisor mode cpu65.pf_e = 1; // 8 bit stack in hypervisor mode cpu65.sphi = 0xBE00; // set a nice shiny stack page cpu65.bphi = 0xBF00; // ... and base page (aka zeropage) cpu65.s = 0xFF; - // Set mapping for the hypervisor - map_mask = (map_mask & 0xF) | 0x30; // mapping: 0011XXXX (it seems low region map mask is not changed by hypervisor entry) - map_megabyte_high = 0xFF << 20; - map_offset_high = 0xF0000; - memory_set_vic3_rom_mapping(0); // for VIC-III rom mapping disable in hypervisor mode - memory_set_do_map(); // now the memory mapping is changed machine_set_speed(0); // set machine speed (hypervisor always runs at M65 fast ... ??) FIXME: check this! cpu65.pc = 0x8000 | (trapno << 2); // load PC with the address assigned for the given trap number DEBUG("HYPERVISOR: entering into hypervisor mode, trap=$%02X (A=$%02X) @ $%04X -> $%04X" NL, trapno, cpu65.a, D6XX_registers[0x48] | (D6XX_registers[0x49] << 8), cpu65.pc); @@ -255,7 +254,15 @@ void hypervisor_start_machine ( void ) hyppo_version_string[0] = '\0'; hdos_init(configdb.hdosvirt, configdb.hdosdir); } - in_hypervisor = 0; + memory_reconfigure( + 0, + VIC4_IOMODE, + 0x3F, 0x35, + 0, 0, + 0, 0, + 0, + false // this will set in_hypervisor to FALSE to ensure hypervisor_enter() won't fail if reset from hypervisor mode + ); hypervisor_queued_trap = -1; hypervisor_is_first_call = 1; execution_range_check_gate = 0; @@ -280,7 +287,7 @@ static inline void first_leave ( void ) cpu65.pc = new_pc; // Since we have overriden ROM, we also must take the responsibility to do what Hyppo would also do: // uploading chargen from the loaded ROM into the "char WOM". - memcpy(char_wom, main_ram + 0x2D000, 0x1000); + memcpy(char_ram, main_ram + 0x2D000, 0x1000); } else { DEBUGPRINT("ROM: no custom force-ROM policy, PC remains at $%04X" NL, cpu65.pc); } @@ -328,21 +335,23 @@ void hypervisor_leave ( void ) cpu65.pc = D6XX_registers[0x48] | (D6XX_registers[0x49] << 8); if (current_hdos_func >= 0) hdos_leave(current_hdos_func); - map_offset_low = ((D6XX_registers[0x4A] & 0xF) << 16) | (D6XX_registers[0x4B] << 8); - map_offset_high = ((D6XX_registers[0x4C] & 0xF) << 16) | (D6XX_registers[0x4D] << 8); - map_mask = (D6XX_registers[0x4A] >> 4) | (D6XX_registers[0x4C] & 0xF0); - map_megabyte_low = D6XX_registers[0x4E] << 20; - map_megabyte_high = D6XX_registers[0x4F] << 20; - memory_set_cpu_io_port_ddr_and_data(D6XX_registers[0x50], D6XX_registers[0x51]); - vic_iomode = D6XX_registers[0x52] & 3; + // Now leaving hypervisor mode: we use memory_reconfigure() to set all the stuff needed + setting up "in_hypervisor" value as well + memory_reconfigure( + vic_registers[0x30], // restore D030 ROM banking + D6XX_registers[0x52] & 3, // restore VIC4 I/O mode + D6XX_registers[0x50], D6XX_registers[0x51], // restore CPU I/O port DDR+DATA + D6XX_registers[0x4E] << 20, // restore MAP low megabyte + ((D6XX_registers[0x4A] & 0xF) << 16) | (D6XX_registers[0x4B] << 8), // restore MAP low offset + D6XX_registers[0x4F] << 20, // restore MAP high megabyte + ((D6XX_registers[0x4C] & 0xF) << 16) | (D6XX_registers[0x4D] << 8), // restore MAP high offset + (D6XX_registers[0x4A] >> 4) | (D6XX_registers[0x4C] & 0xF0), // restore MAP mask + false // this will sets in_hypervisor to FALSE!!!! + ); // GS $D653 - Hypervisor DMAgic source MB - *UNUSED* // GS $D654 - Hypervisor DMAgic destination MB - *UNUSED* dma_set_list_addr_from_bytes(D6XX_registers + 0x55); // GS $D655-$D658 - Hypervisor DMAGic list address bits 27-0 // Now leaving hypervisor mode ... - in_hypervisor = 0; machine_set_speed(0); // restore speed ... - memory_set_vic3_rom_mapping(vic_registers[0x30]); // restore possible active VIC-III mapping - memory_set_do_map(); // restore mapping ... if (XEMU_UNLIKELY(hypervisor_is_first_call)) { if (trap_current != TRAP_RESET) FATAL("First hypervisor TRAP is not RESET?!"); @@ -650,9 +659,9 @@ void hypervisor_debug ( void ) DEBUG("HYPERDEBUG: warning, execution in hypervisor memory without SPHI == $BE but $%02X" NL, cpu65.sphi >> 8); if (XEMU_UNLIKELY(cpu65.bphi != 0xBF00)) DEBUG("HYPERDEBUG: warning, execution in hypervisor memory without BPHI == $BF but $%02X" NL, cpu65.bphi >> 8); - // FIXME: remove this? Reason: this is not even possible as in hypervisor mode vic_iomode cannot be altered via the usual $D02F "KEY" register ... [if there are no bugs ...] - if (XEMU_UNLIKELY(vic_iomode != VIC4_IOMODE)) - DEBUG("HYPERDEBUG: warning, execution in hypervisor memory with VIC I/O mode of %X" NL, iomode_hexdigitids[vic_iomode]); + // FIXME: remove this? Reason: this is not even possible as in hypervisor mode io_mode cannot be altered via the usual $D02F "KEY" register ... [if there are no bugs ...] + if (XEMU_UNLIKELY(io_mode != VIC4_IOMODE)) + DEBUG("HYPERDEBUG: warning, execution in hypervisor memory with VIC I/O mode of %X" NL, iomode_hexdigitids[io_mode]); } const Uint16 now_sp = cpu65.sphi | cpu65.s; int sp_diff = (int)prev_sp - (int)now_sp; @@ -710,7 +719,7 @@ void hypervisor_debug ( void ) (pf & CPU65_PF_I) ? 'I' : 'i', (pf & CPU65_PF_Z) ? 'Z' : 'z', (pf & CPU65_PF_C) ? 'C' : 'c', - iomode_hexdigitids[vic_iomode], + iomode_hexdigitids[io_mode], within_hypervisor_ram ? debug_info[cpu65.pc - 0x8000].src_fn : "", within_hypervisor_ram ? debug_info[cpu65.pc - 0x8000].src_ln : 0, within_hypervisor_ram ? debug_info[cpu65.pc - 0x8000].sym_name : "", diff --git a/targets/mega65/hypervisor.h b/targets/mega65/hypervisor.h index 52721891..d1701b79 100644 --- a/targets/mega65/hypervisor.h +++ b/targets/mega65/hypervisor.h @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define TRAP_FREEZER_RESTORE_PRESS 0x42 #define TRAP_MATRIX 0x43 -extern int in_hypervisor; +extern bool in_hypervisor; extern int hickup_is_overriden; extern int hypervisor_is_debugged; extern char hyppo_version_string[64]; diff --git a/targets/mega65/io_mapper.c b/targets/mega65/io_mapper.c index 32cd4fbc..eec39773 100644 --- a/targets/mega65/io_mapper.c +++ b/targets/mega65/io_mapper.c @@ -1,7 +1,7 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu I/O decoding part (used by memory_mapper.h and DMA mainly) - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -82,6 +82,13 @@ static XEMU_INLINE void update_hw_multiplier ( void ) } +// Writes the colour RAM. !!ONLY!! use this, if it's in the range of the first 2K of the colour RAM though, or you will be in big trouble! +static XEMU_INLINE void write_colour_ram ( const Uint32 addr, const Uint8 data ) +{ + colour_ram[addr] = data; + main_ram[addr + 0x1F800U] = data; +} + /* Internal decoder for I/O reads. Address *must* be within the 0-$3FFF (!!) range. The low 12 bits is the actual address inside the I/O area, while the most significant nibble shows the I/O mode the operation is meant, according to the following table: @@ -458,10 +465,7 @@ void io_write ( unsigned int addr, Uint8 data ) DEBUG("MEGA65: enhanced opcodes have been turned %s." NL, data & 2 ? "ON" : "OFF"); cpu_mega65_opcodes = data & 2; } - if ((data & 4) != rom_protect) { - DEBUG("MEGA65: ROM protection has been turned %s." NL, data & 4 ? "ON" : "OFF"); - rom_protect = data & 4; - } + memory_set_rom_protection(!!(data & 4)); return; case 0x7E: D6XX_registers[0x7E] = 0xFF; // iomap.txt: "Hypervisor already-upgraded bit (sets permanently)" diff --git a/targets/mega65/io_mapper.h b/targets/mega65/io_mapper.h index d906ec69..bd21c71a 100644 --- a/targets/mega65/io_mapper.h +++ b/targets/mega65/io_mapper.h @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore-65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/targets/mega65/matrix_mode.c b/targets/mega65/matrix_mode.c index 5e5dc8ac..687defa1 100644 --- a/targets/mega65/matrix_mode.c +++ b/targets/mega65/matrix_mode.c @@ -271,7 +271,7 @@ static void dump_regs ( const char rot_fig ) (pf & CPU65_PF_I) ? 'I' : 'i', (pf & CPU65_PF_Z) ? 'Z' : 'z', (pf & CPU65_PF_C) ? 'C' : 'c', - iomode_hexdigitids[vic_iomode], + iomode_hexdigitids[io_mode], in_hypervisor ? 'H' : 'U', rot_fig, videostd_id ? "NTSC" : "PAL ", @@ -285,7 +285,7 @@ static void dump_map ( void ) { char desc[10]; for (unsigned int i = 0; i < 16; i++) { - mem_get_4k_region_short_desc(desc, sizeof desc, i); + memory_cpu_addr_to_desc(i << 12, desc, sizeof desc); MATRIX("{%X:}%7s%c", i, desc, (i & 7) == 7 ? ' ' : '|'); } } @@ -408,11 +408,11 @@ static void cmd_write ( char *p ) if (mem_args(p, 1) != 2) return; if (addr_hiword == 0xFFFF) { - cpu65_write_callback(addr_loword, data_byte); + debug_write_cpu_byte(addr_loword, data_byte); return; } const Uint32 addr = ((Uint32)addr_hiword << 16) + (Uint32)addr_loword; - memory_debug_write_phys_addr(addr, data_byte); + debug_write_linear_byte(addr, data_byte); } @@ -421,11 +421,11 @@ static void cmd_show ( char *p ) if (mem_args(p, 0) != 1) return; if (addr_hiword == 0xFFFF) { - MATRIX("[cpu:%04X] = %02X", addr_loword, cpu65_read_callback(addr_loword)); + MATRIX("[cpu:%04X] = %02X", addr_loword, debug_read_cpu_byte(addr_loword)); return; } const Uint32 addr = ((Uint32)addr_hiword << 16) + (Uint32)addr_loword; - MATRIX("[%03X:%04X] = %02X", addr_hiword, addr_loword, memory_debug_read_phys_addr(addr)); + MATRIX("[%03X:%04X] = %02X", addr_hiword, addr_loword, debug_read_linear_byte(addr)); } @@ -443,9 +443,9 @@ static void dump_mem_lines ( int lines, const char sepchr ) for (int i = 0; i < 16; i++) { Uint8 data; if (addr_hiword == 0xFFFF) - data = cpu65_read_callback(addr_loword++); + data = debug_read_cpu_byte(addr_loword++); else { - data = memory_debug_read_phys_addr(((Uint32)addr_hiword << 16) + (Uint32)addr_loword); + data = debug_read_linear_byte(((Uint32)addr_hiword << 16) + (Uint32)addr_loword); addr_loword++; if (!addr_loword) addr_hiword++; @@ -468,7 +468,7 @@ static Uint8 d_bytes[10]; static Uint8 reader_for_disasm_phys ( const unsigned int addr, const unsigned int ofs ) { - const Uint8 b = memory_debug_read_phys_addr((addr + ofs) & 0xFFFFFFU); + const Uint8 b = debug_read_linear_byte((addr + ofs) & 0xFFFFFFU); d_bytes[ofs] = b; return b; } @@ -476,7 +476,7 @@ static Uint8 reader_for_disasm_phys ( const unsigned int addr, const unsigned in static Uint8 reader_for_disasm_cpu ( const unsigned int addr, const unsigned int ofs ) { - const Uint8 b = cpu65_read_callback((addr + ofs) & 0xFFFFU); + const Uint8 b = debug_read_cpu_byte((addr + ofs) & 0xFFFFU); d_bytes[ofs] = b; return b; } diff --git a/targets/mega65/mega65.c b/targets/mega65/mega65.c index 56c4b9a1..426eded0 100644 --- a/targets/mega65/mega65.c +++ b/targets/mega65/mega65.c @@ -323,7 +323,7 @@ static void preinit_memory_for_start ( void ) preinit_memory_item("extonboard", "On-boarding utility", main_ram + 0x40000, meminitdata_onboard, MEMINITDATA_ONBOARD_SIZE, 0x00020, 0x10000, configdb.extonboard); preinit_memory_item("extflashutil", "MEGA-flash utility", main_ram + 0x50000, megaflashutility, sizeof megaflashutility, 0x00020, 0x07D00, configdb.extflashutil); preinit_memory_item("extbanner", "MEGA65 banner", main_ram + 0x57D00, meminitdata_banner, MEMINITDATA_BANNER_SIZE, 0x01000, 0x08300, configdb.extbanner); - preinit_memory_item("extchrwom", "Character-WOM", char_wom, meminitdata_chrwom, MEMINITDATA_CHRWOM_SIZE, 0x01000, 0x01000, configdb.extchrwom); + preinit_memory_item("extchrwom", "Character-WOM", char_ram, meminitdata_chrwom, MEMINITDATA_CHRWOM_SIZE, 0x01000, 0x01000, configdb.extchrwom); preinit_memory_item("extcramutils", "Utils in CRAM", colour_ram, meminitdata_cramutils, MEMINITDATA_CRAMUTILS_SIZE, 0x08000, 0x08000, configdb.extcramutils); hickup_is_overriden = preinit_memory_item("hickup", "Hyppo-Hickup", hypervisor_ram, meminitdata_hickup, MEMINITDATA_HICKUP_SIZE, 0x04000, 0x04000, configdb.hickup); @@ -436,7 +436,7 @@ static void mega65_init ( void ) DEBUGPRINT("SPEED: fast clock is set to %.2fMHz." NL, configdb.fast_mhz); cpu65_init_mega_specific(); cpu65_reset(); // reset CPU (though it fetches its reset vector, we don't use that on M65, but the KS hypervisor trap) - rom_protect = 0; + memory_set_rom_protection(0); hypervisor_start_machine(); speed_current = 0; machine_set_speed(1); @@ -456,9 +456,9 @@ static void mega65_init ( void ) int dump_memory ( const char *fn ) { - if (fn && *fn) { - DEBUGPRINT("MEM: Dumping memory into file: %s" NL, fn); - return xemu_save_file(fn, main_ram, (128 + 256) * 1024, "Cannot dump memory into file"); + if (fn && *fn && main_ram_size) { + DEBUGPRINT("MEM: Dumping memory into file (%uK): %s" NL, main_ram_size >> 10, fn); + return xemu_save_file(fn, main_ram, main_ram_size, "Cannot dump memory into file"); } else { return 0; } @@ -506,7 +506,7 @@ static void shutdown_callback ( void ) #endif hypervisor_hdos_close_descriptors(); if (emulation_is_running) - DEBUGPRINT("CPU: Execution ended at PC=$%04X (linear=%X)" NL, cpu65.pc, memory_cpurd2linear_xlat(cpu65.pc)); + DEBUGPRINT("CPU: Execution ended at PC=$%04X (linear=$%07X)" NL, cpu65.pc, memory_cpurd2linear_xlat(cpu65.pc)); } @@ -523,12 +523,12 @@ void reset_mega65 ( void ) D6XX_registers[0x7D] &= ~16; // FIXME: other default speed controls on reset? c128_d030_reg = 0; machine_set_speed(0); - memory_set_cpu_io_port_ddr_and_data(0xFF, 0xFF); - map_mask = 0; - in_hypervisor = 0; - vic_registers[0x30] = 0; // FIXME: hack! we need this, and memory_set_vic3_rom_mapping above too :( - memory_set_vic3_rom_mapping(0); - memory_set_do_map(); + vic_registers[0x30] = 0; + memory_reconfigure( + 0, io_mode, 0xFF, 0xFF, // D030 value, I/O mode, CPU I/O port 0, CPU I/O port 1 + 0, 0, 0, 0, 0, // MAP MB LO, OFS LO, MB HI, OFS HI, MASK + false // hypervisor + ); vic_reset(); // FIXME: we may need a RESET on VIC-IV what ROM would not initialize but could be used by some MEGA65-aware program? [and hyppo does not care to reset?] cpu65_reset(); dma_reset(); @@ -545,12 +545,12 @@ void reset_mega65_cpu_only ( void ) D6XX_registers[0x7D] &= ~16; // FIXME: other default speed controls on reset? c128_d030_reg = 0; machine_set_speed(0); - memory_set_cpu_io_port_ddr_and_data(0xFF, 0xFF); - map_mask = 0; - in_hypervisor = 0; - vic_registers[0x30] = 0; // FIXME: hack! we need this, and memory_set_vic3_rom_mapping above too :( - memory_set_vic3_rom_mapping(0); - memory_set_do_map(); + vic_registers[0x30] = 0; + memory_reconfigure( + 0, io_mode, 0xFF, 0xFF, // D030 value, I/O mode, CPU I/O port 0, CPU I/O port 1 + 0, 0, 0, 0, 0, // MAP MB LO, OFS LO, MB HI, OFS HI, MASK + false // hypervisor + ); dma_reset(); // We need this: even though it's CPU reset only, DMA is part of the CPU: either DMA or CPU running, resetting in the middle of a DMA session is a disaster cpu65_reset(); } @@ -602,7 +602,7 @@ void m65mon_dumpmem16 ( Uint16 addr ) int n = 16; umon_printf(":000%04X:", addr); while (n--) - umon_printf("%02X", cpu65_read_callback(addr++)); + umon_printf("%02X", debug_read_cpu_byte(addr++)); } void m65mon_dumpmem28 ( int addr ) @@ -611,13 +611,13 @@ void m65mon_dumpmem28 ( int addr ) addr &= 0xFFFFFFF; umon_printf(":%07X:", addr); while (n--) - umon_printf("%02X", memory_debug_read_phys_addr(addr++)); + umon_printf("%02X", debug_read_linear_byte(addr++)); } void m65mon_setmem28 ( int addr, int cnt, Uint8* vals ) { while (--cnt >= 0) - memory_debug_write_phys_addr(addr++, *(vals++)); + debug_write_linear_byte(addr++, *(vals++)); } void m65mon_set_trace ( int m ) diff --git a/targets/mega65/memory_mapper.c b/targets/mega65/memory_mapper.c index cfb99825..9c677bec 100644 --- a/targets/mega65/memory_mapper.c +++ b/targets/mega65/memory_mapper.c @@ -39,182 +39,227 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "configdb.h" #include -#define ALLOW_CPU_CUSTOM_FUNCTIONS_INCLUDE +#define ALLOW_CPU_CUSTOM_FUNCTIONS_INCLUDE #include "cpu_custom_functions.h" +#undef ALLOW_CPU_CUSTOM_FUNCTIONS_INCLUDE //#define DEBUGMEM DEBUG +//#define MEM_USE_HINTS - -// 512K is the max "main" RAM. Currently only 384K is used by M65 +// 512K is the max "main" RAM. Currently only 384K is used by M65. We want to make sure, the _total_ size is power of 2, so we can protect accesses with simple bit masks as a last-resort-protection Uint8 main_ram[512 << 10]; - -// Ugly hack for more RAM! -#define chip_ram (main_ram + 0) -#define fast_ram (main_ram + 0x20000) -#define extra_ram (main_ram + 0x40000) - - -// 128K of "chip-RAM". VIC-IV in M65 can see this, though the last 2K is also covered by the first 2K of the colour RAM. -// that area from chip-RAM cannot be modified by the CPU/DMA/etc though since the colour RAM is there. We emulate anyway -// 128K of chip-RAM so we don't need to check memory access limit all the time in VIC-IV emulation. But it's still true, -// that the last 2K of chip-RAM is a "static" content and not so much useful. -//Uint8 chip_ram[0x20000]; -// 128K of "fast-RAM". In English, this is C65 ROM, but on M65 you can actually write this area too, and you can use it -// as normal RAM. However VIC-IV cannot see this. -//Uint8 fast_ram[0x20000]; // 32K of colour RAM. VIC-IV can see this as for colour information only. The first 2K can be seen at the last 2K of // the chip-RAM. Also, the first 1 or 2K can be seen in the C64-style I/O area too, at $D800 Uint8 colour_ram[0x8000]; -// Write-Only memory (WOM) for character fetch when it would be the ROM (on C64 eg) -// FUN FACT: WOM is not write-only any more :) But for "historical purposes" I containue to name it as "WOM" anyway ;) -Uint8 char_wom[0x2000]; +// RAM of character fetch source when it would be the ROM (on eg. C64) +// FUN FACT: once it was named as "WOM" (Write-Only-Memory) since CPU could only write it. It's not true anymore though +Uint8 char_ram[0x2000]; // 16K of hypervisor RAM, can be only seen in hypervisor mode. Uint8 hypervisor_ram[0x4000]; // I2C registers Uint8 i2c_regs[0x1000]; - -Uint8 slow_ram[SLOW_RAM_SIZE]; +// Attic RAM, aka hyper-RAM (not to be confused with hypervisor RAM!) aka slow-RAM +Uint8 attic_ram[8 << 20]; -struct m65_memory_map_st { - int start, end; // starting and ending physical address of a memory region - mem_page_rd_f_type rd_f; - mem_page_wr_f_type wr_f; -}; +static Uint8 cpu_io_port[2]; +Uint8 io_mode; // "VIC" I/O mode: do not change this value directly, use memory_set_io_mode() instead +static bool rom_protect = false; +static Uint32 mem_legacy_io_addr32; // linear address of the I/O range in use (given by the current I/O mode) +int cpu_rmw_old_data; +Uint32 map_offset_low, map_offset_high, map_megabyte_low, map_megabyte_high; +Uint8 map_mask; +int skip_unhandled_mem = 0; +Uint32 main_ram_size = 0; // will be set by memory_init() after parsing the memory map + +// First 256 slots are for the regular 16 bit CPU addresses, per 256 bytes (256*256=64K) +// All the special ones follows that region: +#define MEM_SLOT_DMA_LIST 0x100 +#define MEM_SLOT_DMA_SOURCE 0x101 +#define MEM_SLOT_DMA_TARGET 0x102 +#define MEM_SLOT_CPU_LINEAR 0x103 +// Last "real" slot, must be defined here again with the name MEM_SLOT_LAST_REAL +#define MEM_SLOT_LAST_REAL 0x103 +// Not "real" slots +#define MEM_SLOT_DEBUG 0x104 +#define MEM_SLOT_SDEBUG 0x105 +// must be +1 of the last one, that is: the total number of slots +#define MEM_SLOTS_TOTAL 0x106 + +// CHECK: If MEM_SLOTS_TOTAL changes, you must modify MEM_SLOTS_TOTAL_EXPORTED in cpu_custom_functions.h to match that! +#if MEM_SLOTS_TOTAL != MEM_SLOTS_TOTAL_EXPORTED +#error "Inconsistency, please modify MEM_SLOTS_TOTAL_EXPORTED in cpu_custom_functions.h to match the value of MEM_SLOTS_TOTAL in memory_mapper.c" +#endif +#define VIC3_ROM_D030_MASK (0x08U|0x10U|0x20U|0x80U) + +// !!!!! +// function pointer types mem_slot_rd_func_t, mem_slot_wr_func_t are in cpu_custom_functions.h + +Uint32 mem_slot_rd_addr32[MEM_SLOTS_TOTAL]; +Uint32 mem_slot_wr_addr32[MEM_SLOTS_TOTAL]; +mem_slot_rd_func_t mem_slot_rd_func[MEM_SLOTS_TOTAL]; +mem_slot_wr_func_t mem_slot_wr_func[MEM_SLOTS_TOTAL]; +static mem_slot_rd_func_t mem_slot_rd_func_real[MEM_SLOTS_TOTAL]; +static mem_slot_wr_func_t mem_slot_wr_func_real[MEM_SLOTS_TOTAL]; +#ifdef MEM_USE_DATA_POINTERS +#warning "Usage of data pointers is an experimental feature (MEM_USE_DATA_POINTERS is defined)!" +Uint8 *mem_slot_rd_data[MEM_SLOTS_TOTAL]; +Uint8 *mem_slot_wr_data[MEM_SLOTS_TOTAL]; +#endif -// table of readers/writers for 256 byte CPU pages are "public" (not static) as will be used -// from inlined function in the CPU emulator core level for better performance. -// The second 256 entries are for unmapped all-RAM cache. -// The extra elements over 0x200 used for ROM and I/O mapping, DMA access decoding, and 32 bit opcodes. - -#define MEM_SLOT_C64_8KROM_A000 0x200 -#define MEM_SLOT_C64_4KROM_D000 0x220 -#define MEM_SLOT_OLD_4K_IO_D000 0x230 -#define MEM_SLOT_C64_8KROM_E000 0x240 -#define MEM_SLOT_C65_8KROM_8000 0x260 -#define MEM_SLOT_C65_8KROM_A000 0x280 -#define MEM_SLOT_C65_8KROM_E000 0x2A0 -#define MEM_SLOT_C65_4KROM_C000 0x2C0 -#define MEM_SLOT_DMA_RD_SRC 0x2D0 -#define MEM_SLOT_DMA_WR_SRC 0x2D1 -#define MEM_SLOT_DMA_RD_DST 0x2D2 -#define MEM_SLOT_DMA_WR_DST 0x2D3 -#define MEM_SLOT_DMA_RD_LST 0x2D4 -#define MEM_SLOT_CPU_32BIT 0x2D5 -#define MEM_SLOT_DEBUG_RESOLVER 0x2D6 -#define MEM_SLOTS 0x2D7 - -#define VIC3_ROM_MASK_8000 0x08 -#define VIC3_ROM_MASK_A000 0x10 -#define VIC3_ROM_MASK_C000 0x20 -#define VIC3_ROM_MASK_E000 0x80 - -#define MAP_MARKER_DUMMY_OFFSET 0x2000 - -static int mem_page_phys[MEM_SLOTS] MAXALIGNED; -int mem_page_rd_o[MEM_SLOTS] MAXALIGNED; -int mem_page_wr_o[MEM_SLOTS] MAXALIGNED; -mem_page_rd_f_type mem_page_rd_f[MEM_SLOTS] MAXALIGNED; -mem_page_wr_f_type mem_page_wr_f[MEM_SLOTS] MAXALIGNED; -static const struct m65_memory_map_st *mem_page_refp[MEM_SLOTS]; +// Very important variable, every slot reader/writer may depend on this to set correctly! +Uint32 ref_slot; -int cpu_rmw_old_data; +#ifdef MEM_WATCH_SUPPORT +static Uint8 memwatch_reader ( const Uint32 addr32 ); +static void memwatch_writer ( const Uint32 addr32, const Uint8 data ); +#define MEM_SLOT_WATCHER_READ 1U +#define MEM_SLOT_WATCHER_WRITE 2U +#define MEM_SLOT_WATCHER_CHANGED 0x80U +static Uint8 mem_slot_watcher[MEM_SLOTS_TOTAL]; +#endif -static int applied_memcfg[9]; // not 8, since one slot is actually halved because of CXXX/DXXX handled differently -static int memcfg_cpu_io_port_policy_A000_to_BFFF; -static int memcfg_cpu_io_port_policy_D000_to_DFFF; -static int memcfg_cpu_io_port_policy_E000_to_FFFF; +typedef enum { + MEM_SLOT_TYPE_UNRESOLVED, // invalidated slots + MEM_SLOT_TYPE_IMPOSSIBLE, // should not happen ever! software bug ... + MEM_SLOT_TYPE_UNDECODED, // there is corresponding memory area + MEM_SLOT_TYPE_DUMMY, // like the above, but should not give you warnings! + // These can occur only in CPU slots, assigned by the cpu address resolver (NOT the linear one!) + MEM_SLOT_TYPE_UNMAPPED, + MEM_SLOT_TYPE_BANKED_ROM, + MEM_SLOT_TYPE_LEGACY_IO, + // These can occur only in MAP'ed slots (or in special slots), assigned by the linear address decoder (NOT the CPU's one!) + MEM_SLOT_TYPE_MAIN_RAM, + MEM_SLOT_TYPE_ROM, // maybe normal RAM, if ROM write protection is off + MEM_SLOT_TYPE_SHARED_RAM, + MEM_SLOT_TYPE_IO, + MEM_SLOT_TYPE_COLOUR_RAM, + MEM_SLOT_TYPE_CHAR_RAM, + MEM_SLOT_TYPE_ETH_BUFFER, + MEM_SLOT_TYPE_I2C, + MEM_SLOT_TYPE_ATTIC_RAM, + MEM_SLOT_TYPE_SLOW_DEVICES, + MEM_SLOT_TYPE_OPL3, + MEM_SLOT_TYPE_1541_RAM, + // All hypervisor dependent slots must be after this mark! AND MEM_SLOT_TYPE_HYPERVISOR_RAM must be the first!!!!!!! + // DO not put other kind of slots here! + MEM_SLOT_TYPE_HYPERVISOR_RAM, + MEM_SLOT_TYPE_DISK_BUFFER, +} mem_slot_type_t; -static const int memcfg_cpu_io_port_policies_A000_to_BFFF[8] = { - 0x1A0, 0x1A0, 0x1A0, MEM_SLOT_C64_8KROM_A000, 0x1A0, 0x1A0, 0x1A0, MEM_SLOT_C64_8KROM_A000 -}; -static const int memcfg_cpu_io_port_policies_D000_to_DFFF[8] = { - 0x1D0, MEM_SLOT_C64_4KROM_D000, MEM_SLOT_C64_4KROM_D000, MEM_SLOT_C64_4KROM_D000, 0x1D0, MEM_SLOT_OLD_4K_IO_D000, MEM_SLOT_OLD_4K_IO_D000, MEM_SLOT_OLD_4K_IO_D000 -}; -static const int memcfg_cpu_io_port_policies_E000_to_FFFF[8] = { - 0x1E0, 0x1E0, MEM_SLOT_C64_8KROM_E000, MEM_SLOT_C64_8KROM_E000, 0x1E0, 0x1E0, MEM_SLOT_C64_8KROM_E000, MEM_SLOT_C64_8KROM_E000 -}; +static mem_slot_type_t mem_slot_type[MEM_SLOTS_TOTAL]; -static Uint8 memcfg_vic3_rom_mapping_last, memcfg_cpu_io_port_last; -static Uint8 cpu_io_port[2]; -int map_mask, map_offset_low, map_offset_high, map_megabyte_low, map_megabyte_high; -static int map_marker_low, map_marker_high; -int rom_protect; -int skip_unhandled_mem = 0; +static Uint32 policy4k_banking[0x10]; // memory policy according to banking only (no MAP effect!): BANK_POLICY_RAM, BANK_POLICY_ROM, BANK_POLICY_IO +static Uint32 policy4k[0x10]; // memory policy with MAP taken account (the real deal!): the same values as above, PLUS special values (see below for the defined values) +// Impossible MAP addresses are used for special purposes above the 28 bit linear address space of MEGA65. BANK_POLICY_INVALID must be the first just above the valid MEGA65 address range (@ $10000000) +#define BANK_POLICY_INVALID 0x10000000U +#define BANK_POLICY_RAM 0x10000001U +#define BANK_POLICY_ROM 0x10000002U +#define BANK_POLICY_IO 0x10000003U -#define DEFINE_READER(name) static Uint8 name ( MEMORY_HANDLERS_ADDR_TYPE ) -#define DEFINE_WRITER(name) static void name ( MEMORY_HANDLERS_ADDR_TYPE, Uint8 data ) +static Uint8 zero_page_reader ( const Uint32 addr32 ); +static void zero_page_writer ( const Uint32 addr32, const Uint8 data ); -DEFINE_READER(zero_physical_page_reader) { - return (XEMU_LIKELY(GET_OFFSET_BYTE_ONLY() > 1)) ? chip_ram[GET_OFFSET_BYTE_ONLY()] : cpu_io_port[GET_OFFSET_BYTE_ONLY()]; +static Uint8 main_ram_reader ( const Uint32 addr32 ) { + return main_ram[addr32]; } -DEFINE_WRITER(zero_physical_page_writer) -{ - if (XEMU_LIKELY(GET_OFFSET_BYTE_ONLY() > 1)) - chip_ram[GET_OFFSET_BYTE_ONLY()] = data; - else - memory_set_cpu_io_port(GET_OFFSET_BYTE_ONLY(), data); +static void main_ram_writer ( const Uint32 addr32, const Uint8 data ) { + main_ram[addr32] = data; } -DEFINE_READER(opl3_reader) { - return 0xFF; +static void shared_main_ram_writer ( const Uint32 addr32, const Uint8 data ) { + main_ram[addr32] = data; + colour_ram[addr32 - 0x1F800U] = data; } -DEFINE_WRITER(opl3_writer) { - audio65_opl3_write(GET_WRITER_OFFSET() & 0xFF, data); +static void shared_colour_ram_writer ( const Uint32 addr32, const Uint8 data ) { + main_ram[addr32 + 0x1F800U - 0xFF80000U] = data; + colour_ram[addr32 - 0xFF80000U] = data; } -DEFINE_READER(chip_ram_from_page1_reader) { - return chip_ram[GET_READER_OFFSET() + 0x100]; +static Uint8 colour_ram_reader ( const Uint32 addr32 ) { + return colour_ram[addr32 - 0xFF80000U]; } -DEFINE_WRITER(chip_ram_from_page1_writer) { - chip_ram[GET_WRITER_OFFSET() + 0x100] = data; +static void colour_ram_writer ( const Uint32 addr32, const Uint8 data ) { + colour_ram[addr32 - 0xFF80000U] = data; } -DEFINE_READER(fast_ram_reader) { - return fast_ram[GET_READER_OFFSET()]; +static Uint8 attic_ram_reader ( const Uint32 addr32 ) { + return attic_ram[addr32 - 0x8000000U]; } -DEFINE_WRITER(fast_ram_writer) { - if (XEMU_LIKELY(!rom_protect)) - fast_ram[GET_WRITER_OFFSET()] = data; +static void attic_ram_writer ( const Uint32 addr32, const Uint8 data ) { + attic_ram[addr32 - 0x8000000U] = data; } -DEFINE_READER(extra_ram_reader) { - return extra_ram[GET_READER_OFFSET()]; +static Uint8 hypervisor_ram_reader ( const Uint32 addr32 ) { + return hypervisor_ram[addr32 - 0xFFF8000U]; } -DEFINE_WRITER(extra_ram_writer) { - extra_ram[GET_WRITER_OFFSET()] = data; +static void hypervisor_ram_writer ( const Uint32 addr32, const Uint8 data ) { + hypervisor_ram[addr32 - 0xFFF8000U] = data; } -DEFINE_READER(colour_ram_reader) { - return colour_ram[GET_READER_OFFSET()]; +static void opl3_writer ( const Uint32 addr32, const Uint8 data ) { + audio65_opl3_write(addr32 & 0xFFU, data); } -DEFINE_WRITER(colour_ram_writer) { - write_colour_ram(GET_WRITER_OFFSET(), data); +static Uint8 io_reader ( const Uint32 addr32 ) { + return io_read(addr32 - 0xFFD0000U); } -DEFINE_READER(dummy_reader) { - return 0xFF; +static void io_writer ( const Uint32 addr32, const Uint8 data ) { + io_write(addr32 - 0xFFD0000U, data); +} +static Uint8 char_ram_reader ( const Uint32 addr32 ) { + return char_ram[addr32 - 0xFF7E000U]; +} +static void char_ram_writer ( const Uint32 addr32, const Uint8 data ) { + char_ram[addr32 - 0xFF7E000U] = data; } -DEFINE_WRITER(dummy_writer) { +static Uint8 disk_buffer_hypervisor_reader ( const Uint32 addr32 ) { + return disk_buffers[addr32 - 0xFFD6000U]; } -DEFINE_READER(hypervisor_ram_reader) { - return (XEMU_LIKELY(in_hypervisor)) ? hypervisor_ram[GET_READER_OFFSET()] : 0xFF; +static void disk_buffer_hypervisor_writer ( const Uint32 addr32, const Uint8 data ) { + disk_buffers[addr32 - 0xFFD6000U] = data; } -DEFINE_WRITER(hypervisor_ram_writer) { - if (XEMU_LIKELY(in_hypervisor)) - hypervisor_ram[GET_WRITER_OFFSET()] = data; +static Uint8 disk_buffer_user_reader ( const Uint32 addr32 ) { + return disk_buffer_cpu_view[(addr32 - 0xFFD6000U) & 0x1FF]; } -DEFINE_READER(char_wom_reader) { - return char_wom[GET_READER_OFFSET()]; +static void disk_buffer_user_writer ( const Uint32 addr32, const Uint8 data ) { + disk_buffer_cpu_view[(addr32 - 0xFFD6000U) & 0x1FF] = data; +} +static Uint8 eth_buffer_reader ( const Uint32 addr32 ) { + return eth65_read_rx_buffer(addr32 - 0xFFDE800U); } -DEFINE_WRITER(char_wom_writer) { - char_wom[GET_WRITER_OFFSET()] = data; +static void eth_buffer_writer ( const Uint32 addr32, const Uint8 data ) { + eth65_write_tx_buffer(addr32 - 0xFFDE800U, data); } -DEFINE_READER(slow_ram_reader) { - return slow_ram[GET_READER_OFFSET()]; +static Uint8 slow_devices_reader ( const Uint32 addr32 ) { + return cart_read_byte(addr32 - 0x4000000U); } -DEFINE_WRITER(slow_ram_writer) { - slow_ram[GET_WRITER_OFFSET()] = data; +static void slow_devices_writer ( const Uint32 addr32, const Uint8 data ) { + cart_write_byte(addr32 - 0x4000000U, data); } -DEFINE_READER(invalid_mem_reader) { +static Uint8 i2c_reader ( const Uint32 addr32 ) { + return i2c_regs[addr32 - 0x0FFD7000U]; +} +static void i2c_writer ( const Uint32 addr32, const Uint8 data ) { + const Uint32 rel = addr32 - 0x0FFD7000U; + if (rel >= I2C_NVRAM_OFFSET && rel < (I2C_NVRAM_OFFSET + I2C_NVRAM_SIZE)) { + //DEBUGPRINT("I2C: NVRAM write ($%02X->$%02X) @ NVRAM+$%X" NL, i2c_regs[rel], data, rel - I2C_NVRAM_OFFSET); + i2c_regs[rel] = data; + } else if (configdb.mega65_model == 3 && rel >= 0x1D0 && rel <= 0x1EF) { // Hyppo needs this on PCB R3 for I2C target setup (audio mixer settings) + // TODO: emulate the mixer stuff + i2c_regs[rel] = data; + } else { + DEBUGPRINT("I2C: unhandled write ($%02X) @ I2C+$%X" NL, data, rel); + } +} +static Uint8 dummy_reader ( const Uint32 addr32 ) { + return 0xFF; +} +static void dummy_writer ( const Uint32 addr32, const Uint8 data ) { + // well, do nothing +} + + +static Uint8 undecoded_reader ( const Uint32 addr32 ) +{ char msg[128]; - sprintf(msg, "Unhandled memory read operation for linear address $%X (PC=$%04X)", GET_READER_OFFSET(), cpu65.pc); + sprintf(msg, "Unhandled memory read operation for linear address $%X (PC=$%04X)", addr32, cpu65.old_pc); if (skip_unhandled_mem <= 1) skip_unhandled_mem = QUESTION_WINDOW("EXIT|Ignore now|Ignore all|Silent ignore all", msg); switch (skip_unhandled_mem) { @@ -231,14 +276,16 @@ DEFINE_READER(invalid_mem_reader) { } return 0xFF; } -DEFINE_WRITER(invalid_mem_writer) { + +static void undecoded_writer ( const Uint32 addr32, const Uint8 data ) +{ char msg[128]; - sprintf(msg, "Unhandled memory write operation for linear address $%X data = $%02X (PC=$%04X)", GET_WRITER_OFFSET(), data, cpu65.pc); + sprintf(msg, "Unhandled memory write operation for linear address $%X (PC=$%04X)", addr32, cpu65.old_pc); if (skip_unhandled_mem <= 1) skip_unhandled_mem = QUESTION_WINDOW("EXIT|Ignore now|Ignore all|Silent ignore all", msg); switch (skip_unhandled_mem) { case 0: - FATAL("Exit on request after illegal memory access"); + XEMUEXIT(1); break; case 1: case 2: @@ -249,565 +296,438 @@ DEFINE_WRITER(invalid_mem_writer) { break; } } -DEFINE_READER(fatal_mem_reader) { - FATAL("Unhandled physical memory mapping on read map. Xemu software bug?"); -} -DEFINE_WRITER(fatal_mem_writer) { - FATAL("Unhandled physical memory mapping on write map. Xemu software bug?"); -} -DEFINE_READER(unreferenced_mem_reader) { - FATAL("Unreferenced physical memory mapping on read map. Xemu software bug?"); -} -DEFINE_WRITER(unreferenced_mem_writer) { - FATAL("Unreferenced physical memory mapping on write map. Xemu software bug?"); -} -DEFINE_READER(m65_io_reader) { - return io_read(GET_READER_OFFSET()); -} -DEFINE_WRITER(m65_io_writer) { - io_write(GET_WRITER_OFFSET(), data); -} -DEFINE_READER(legacy_io_reader) { - return io_read(GET_READER_OFFSET() | (vic_iomode << 12)); -} -DEFINE_WRITER(legacy_io_writer) { - io_write(GET_WRITER_OFFSET() | (vic_iomode << 12), data); -} -DEFINE_READER(eth_buffer_reader) { - return eth65_read_rx_buffer(GET_READER_OFFSET()); -} -DEFINE_WRITER(eth_buffer_writer) { - eth65_write_tx_buffer(GET_WRITER_OFFSET(), data); -} -DEFINE_READER(disk_buffers_reader) { - const unsigned int offs = GET_READER_OFFSET(); - if (in_hypervisor) - return disk_buffers[offs]; - else - return disk_buffer_cpu_view[offs & 0x1FF]; -} -DEFINE_WRITER(disk_buffers_writer) { - const unsigned int offs = GET_WRITER_OFFSET(); - if (in_hypervisor) - disk_buffers[offs] = data; - else - disk_buffer_cpu_view[offs & 0x1FF] = data; -} -DEFINE_READER(i2c_io_reader) { - const unsigned int addr = GET_READER_OFFSET(); - //DEBUGPRINT("I2C: read ($%02X) @ I2C+$%X" NL, i2c_regs[addr], addr); - return i2c_regs[addr]; -} -DEFINE_WRITER(i2c_io_writer) { - const unsigned int addr = GET_WRITER_OFFSET(); - //DEBUGPRINT("I2C: write ($%02X) @ $%X" NL, data, addr); - if (addr >= I2C_NVRAM_OFFSET && addr < (I2C_NVRAM_OFFSET + I2C_NVRAM_SIZE)) { - //DEBUGPRINT("I2C: NVRAM write ($%02X->$%02X) @ NVRAM+$%X" NL, i2c_regs[addr], data, addr - I2C_NVRAM_OFFSET); - i2c_regs[addr] = data; - } else if (configdb.mega65_model == 3 && addr >= 0x1D0 && addr <= 0x1EF) { // Hyppo needs this on PCB R3 for I2C target setup (audio mixer settings) - // TODO: emulate the mixer stuff - i2c_regs[addr] = data; - } else { - DEBUGPRINT("I2C: unhandled write ($%02X) @ I2C+$%X" NL, data, addr); - } -} -// "Slow device" ~ cardridge space -DEFINE_READER(slowdev_reader) { - return cart_read_byte(GET_READER_OFFSET()); -} -DEFINE_WRITER(slowdev_writer) { - cart_write_byte(GET_WRITER_OFFSET(), data); -} - -// Not implemented yet, just here, since freezer accesses this memory area, and without **some** dummy -// support, it would cause "unhandled memory access" warning in Xemu. -DEFINE_READER(mem1541_reader) { - return 0xFF; -} -DEFINE_WRITER(mem1541_writer) { -} - - -// Memory layout table for MEGA65 -// Please note, that for optimization considerations, it should be organized in a way -// to have most common entries first, for faster hit in most cases. -static const struct m65_memory_map_st m65_memory_map[] = { - // 126K chip-RAM (last 2K is not availbale because it's colour RAM), with physical zero page excluded (this is because it needs the CPU port handled with different handler!) - { 0x100, 0x1F7FF, chip_ram_from_page1_reader, chip_ram_from_page1_writer }, - // the "physical" zero page because of CPU port ... - { 0, 0xFF, zero_physical_page_reader, zero_physical_page_writer }, - // 128K of fast-RAM, normally ROM for C65, but can be RAM too! - { 0x20000, 0x3FFFF, fast_ram_reader, fast_ram_writer }, - { 0x40000, 0x5FFFF, extra_ram_reader, extra_ram_writer }, - // the last 2K of the first 128K, being the first 2K of the colour RAM (quite nice sentence in my opinion) - { 0x1F800, 0x1FFFF, colour_ram_reader, colour_ram_writer }, - // As I/O can be handled quite uniformely, and needs other decoding later anyway, we handle the WHOLE I/O area for all modes in once! - // This is 16K space, though one 4K is invalid for I/O modes ($FFD2000-$FFD2FFF), the sequence: C64,C65,INVALID,M65 of 4Ks - { 0xFFD0000, 0xFFD3FFF, m65_io_reader, m65_io_writer }, - // full colour RAM - { 0xFF80000, 0xFF87FFF, colour_ram_reader, colour_ram_writer }, // full colour RAM (32K) - { 0xFFF8000, 0xFFFBFFF, hypervisor_ram_reader, hypervisor_ram_writer }, // 16KB HYPPO hickup/hypervisor ROM - { 0xFF7E000, 0xFF7FFFF, char_wom_reader, char_wom_writer }, // Character "WriteOnlyMemory" (which is not write-only any more, but it was initially, so the name ...) - { 0xFFDE800, 0xFFDEFFF, eth_buffer_reader, eth_buffer_writer }, // ethernet RX/TX buffer, NOTE: the same address, reading is always the RX_read, writing is always TX_write - { 0xFFD6000, 0xFFD6FFF, disk_buffers_reader, disk_buffers_writer }, // disk buffer for SD (can be mapped to I/O space too), F011, and some "3.5K scratch space" [??] - { 0xFFD7000, 0xFFD7FFF, i2c_io_reader, i2c_io_writer }, // I2C devices - { 0x8000000, 0x8000000 + SLOW_RAM_SIZE - 1, slow_ram_reader, slow_ram_writer }, // "slow RAM" also called "hyper RAM" (not to be confused with hypervisor RAM!) - { 0x8000000 + SLOW_RAM_SIZE, 0xFDFFFFF, dummy_reader, dummy_writer }, // ununsed big part of the "slow RAM" or so ... - { 0x4000000, 0x7FFFFFF, slowdev_reader, slowdev_writer }, // slow RAM memory area ~ cartridge - { 0xFE00000, 0xFE000FF, opl3_reader, opl3_writer }, - { 0x60000, 0xFFFFF, dummy_reader, dummy_writer }, // upper "unused" area of C65 (!) memory map. It seems C65 ROMs want it (Expansion RAM?) so we define as unused. - { 0xFFDB000, 0xFFDFFFF, mem1541_reader, mem1541_writer }, // 1541's 16K ROM + 4K RAM, not so much used currently, but freezer seems to access it, for example ... - // the last entry *MUST* include the all possible addressing space to "catch" undecoded memory area accesses!! - { 0, 0xFFFFFFF, invalid_mem_reader, invalid_mem_writer }, - // even after the last entry :-) to filter out programming bugs, catch all possible even not valid M65 physical address space acceses ... - { INT_MIN, INT_MAX, fatal_mem_reader, fatal_mem_writer } -}; -// a mapping item which NEVER matches (ie, starting address of region is higher then ending ...) -static const struct m65_memory_map_st impossible_mapping = { - 0x10000001, 0x10000000, unreferenced_mem_reader, unreferenced_mem_writer -}; - +struct mem_map_st { + Uint32 first; + Uint32 last; + mem_slot_type_t type; +}; +#ifdef MEM_USE_HINTS +#define MEM_HINT_SLOTS (MEM_SLOTS_TOTAL - 0x100 + 2) +static const struct mem_map_st *mem_map_hints[MEM_HINT_SLOTS]; +#endif +// Rules: +// * Every areas must start in address having the least significant byte as zero +// * Every areas must end in address having the least significant byte as $FF +// * Every areas (other than the first) must start on address which is the previous one's last one PLUS 1 +// * The first entry must start at address 0 +// * The last entry must be $10000000 - $FFFFFFFF with type MEM_SLOT_TYPE_IMPOSSIBLE as a safe-guard +static struct mem_map_st mem_map[] = { + { 0x00000000U, 0x0001F7FFU, MEM_SLOT_TYPE_MAIN_RAM }, // OLD memory model used 0-FF as "ZP" to handle CPU I/O port. But now it's VIRTUAL and only exists in CPU's view not in mem-map! + { 0x0001F800U, 0x0001FFFFU, MEM_SLOT_TYPE_SHARED_RAM }, // "shared" area, 2K of coulour RAM [C65 legacy], b/c of performance, we must distribute between both of normal and colour RAM + { 0x00020000U, 0x0003FFFFU, MEM_SLOT_TYPE_ROM }, // though it's called ROM, it can be normal RAM, if "ROM write protection" is off + { 0x00040000U, 0x0005FFFFU, MEM_SLOT_TYPE_MAIN_RAM }, + { 0x00060000U, 0x000FFFFFU, MEM_SLOT_TYPE_DUMMY }, // upper "unused" area of C65 (!) memory map. It seems C65 ROMs want it (Expansion RAM?) so we define as unused. + { 0x00100000U, 0x03FFFFFFU, MEM_SLOT_TYPE_UNDECODED }, + { 0x04000000U, 0x07FFFFFFU, MEM_SLOT_TYPE_SLOW_DEVICES }, + { 0x08000000U, 0x087FFFFFU, MEM_SLOT_TYPE_ATTIC_RAM }, // "slow RAM" also called "hyper RAM" (not to be confused with hypervisor RAM!) or "Attic RAM" + { 0x08800000U, 0x0FDFFFFFU, MEM_SLOT_TYPE_DUMMY }, // ununsed big part of the "slow RAM area" or so ... + { 0x0FE00000U, 0x0FE000FFU, MEM_SLOT_TYPE_OPL3 }, + { 0x0FE00100U, 0x0FF7DFFFU, MEM_SLOT_TYPE_UNDECODED }, + { 0x0FF7E000U, 0x0FF7FFFFU, MEM_SLOT_TYPE_CHAR_RAM }, // Character "WriteOnlyMemory" (which is not write-only any more, but it was initially, so the name ...) + { 0x0FF80000U, 0x0FF87FFFU, MEM_SLOT_TYPE_COLOUR_RAM }, // 32K colour RAM + { 0x0FF88000U, 0x0FFCFFFFU, MEM_SLOT_TYPE_UNDECODED }, + { 0x0FFD0000U, 0x0FFD3FFFU, MEM_SLOT_TYPE_IO }, + { 0x0FFD4000U, 0x0FFD5FFFU, MEM_SLOT_TYPE_UNDECODED }, + { 0x0FFD6000U, 0x0FFD6FFFU, MEM_SLOT_TYPE_DISK_BUFFER }, // disk buffer for SD (can be mapped to I/O space too), F011, and some "3.5K scratch space" [??] + { 0x0FFD7000U, 0x0FFD7FFFU, MEM_SLOT_TYPE_I2C }, // I2C devices + { 0x0FFD8000U, 0x0FFDAFFFU, MEM_SLOT_TYPE_UNDECODED }, + { 0x0FFDB000U, 0x0FFDE7FFU, MEM_SLOT_TYPE_1541_RAM }, // 1541's 16K ROM + 4K RAM, not so much used currently, but freezer seems to access it, for example ... FIXME: wrong area?! + { 0x0FFDE800U, 0x0FFDEFFFU, MEM_SLOT_TYPE_ETH_BUFFER }, // ethernet RX/TX buffer(s) + { 0x0FFDF000U, 0x0FFF7FFFU, MEM_SLOT_TYPE_UNDECODED }, + { 0x0FFF8000U, 0x0FFFBFFFU, MEM_SLOT_TYPE_HYPERVISOR_RAM}, // 16KB HYPPO hickup/hypervisor ROM [do not confuse with Hyper-RAM aka Attic-RAM aka Slow-RAM!] + { 0x0FFFC000U, 0x0FFFFFFFU, MEM_SLOT_TYPE_UNDECODED }, + { 0x10000000U, 0xFFFFFFFFU, MEM_SLOT_TYPE_IMPOSSIBLE } // must be the last item! (above 28 bit address space to the max of 32 bits - because of using Uint32) +}; +#define MEM_MAP_SIZE (sizeof(mem_map) / sizeof(struct mem_map_st)) -static void phys_addr_decoder ( int phys, int slot, int hint_slot ) +static XEMU_INLINE void slot_assignment_postprocessing ( const Uint32 slot ) { - const struct m65_memory_map_st *p; - phys &= 0xFFFFF00; // we map only at 256 bytes boundaries!!!! It also helps to wrap around 28 bit M65 addresses TODO/FIXME: is this correct behaviour? - if (mem_page_phys[slot] == phys) // kind of "mapping cache" for the given cache slot - return; // skip, if the slot already contains info on the current physical address - mem_page_phys[slot] = phys; - // tricky part: if hint_slot is non-negative, it's used for "contiunity" information related to this slot, - // ie check, if the current map request can be fit into the region already mapped by hint_slot, then no - // need for the search loop. hint_slot can be any slot, but logically it's sane to be used when the given - // hint_slot is "likely" to have some contiunity with the slot given by "slot" otherwise it's just makes - // thing worse. If not used, hint_slot should be negative to skip this feature. hint_slot can be even same - // as "slot" if you need a "moving" mapping in a "caching" slot, ie DMA-aux access functions, etc. - if (hint_slot >= 0 && mem_page_refp[hint_slot]->end < 0xFFFFFFF) { // FIXME there was a serious bug here, taking invalid mem slot for anything after hinting that - p = mem_page_refp[hint_slot]; - if (phys >= p->start && phys <= p->end) { -#ifdef DEBUGMEM - DEBUGMEM("MEM: PHYS-MAP: slot#$%03X: slot hint TAKEN :)" NL, slot); + mem_slot_rd_func_real[slot] = mem_slot_rd_func[slot]; + mem_slot_wr_func_real[slot] = mem_slot_wr_func[slot]; +#ifdef MEM_WATCH_SUPPORT + register const Uint8 watcher = mem_slot_watcher[slot]; + if (XEMU_UNLIKELY(watcher)) { + if ((watcher & MEM_SLOT_WATCHER_READ)) { + mem_slot_rd_func[slot] = memwatch_reader; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = NULL; // cannot use the memory-pointer optimization in case of mem-watch, since we need the callback +#endif + if (mem_slot_rd_func_real[slot] == undecoded_reader) + mem_slot_rd_func_real[slot] = dummy_reader; + } + if ((watcher & MEM_SLOT_WATCHER_WRITE)) { + mem_slot_wr_func[slot] = memwatch_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_wr_data[slot] = NULL; // see the comment above at the reader's case #endif - goto found; + if (mem_slot_wr_func_real[slot] == undecoded_writer) + mem_slot_wr_func_real[slot] = dummy_writer; } } - // Scan the memory map, as not found "cached" result on the same slot, or by the hinting slot - for (p = m65_memory_map; phys < p->start || phys > p->end; p++) - ; -found: - mem_page_rd_o[slot] = mem_page_wr_o[slot] = phys - p->start; - mem_page_rd_f[slot] = p->rd_f; - mem_page_wr_f[slot] = p->wr_f; - //if (p->rd_f == invalid_mem_reader) - // FATAL("Invalid memory region is tried to be mapped to slot $%X for phys addr $%X" NL, slot, phys); - mem_page_refp[slot] = p; -#ifdef DEBUGMEM - DEBUGMEM("MEM: PHYS-MAP: slot#$%03X: phys = $%X mapped (area: $%X-$%X, rd_o=%X, wr_o=%X) [hint slot was: %03X]" NL, - slot, - phys, - p->start, p->end, - mem_page_rd_o[slot], mem_page_wr_o[slot], - hint_slot - ); #endif } -static void XEMU_INLINE phys_addr_decoder_array ( int megabyte_offset, int offset, int slot, int slots, int hint_slot ) +// Warning: input linear address must be ranged/normalized (& 0xFFFFF00U) by the caller otherwise bad things will happen (TM) +static void resolve_linear_slot ( const Uint32 slot, const Uint32 addr ) { +#ifdef MEM_USE_HINTS + const Uint32 hint_slot = (slot < 0x100U) ? slot >> 7 : slot - 0x100U + 2U; + const struct mem_map_st *p = mem_map_hints[hint_slot]; +#else + static const struct mem_map_st *p = mem_map; +#endif + //if (addr < p->first) + // p = mem_map; for (;;) { - // we try to use the "hint_slot" feature, which tries to optimize table building with exploiting the - // fact, that "likely" the next page table entry suits into the same physical decoding "entry" just - // with different offset (so we don't need to re-walk the memory configuration table) - phys_addr_decoder(megabyte_offset | (offset & 0xFFFFF), slot, hint_slot); - if (!--slots) - return; - hint_slot = slot++; - offset += 0x100; + if (addr > p->last) + p++; + else if (addr < p->first) + p--; + else + break; } -} - - -#define MEM_TABLE_COPY(to,from,pages) do { \ - memcpy(mem_page_rd_o + (to), mem_page_rd_o + (from), sizeof(int) * (pages)); \ - memcpy(mem_page_wr_o + (to), mem_page_wr_o + (from), sizeof(int) * (pages)); \ - memcpy(mem_page_rd_f + (to), mem_page_rd_f + (from), sizeof(mem_page_rd_f_type) * (pages)); \ - memcpy(mem_page_wr_f + (to), mem_page_wr_f + (from), sizeof(mem_page_wr_f_type) * (pages)); \ - memcpy(mem_page_refp + (to), mem_page_refp + (from), sizeof(const struct m65_memory_map_st*) * (pages)); \ - memcpy(mem_page_phys + (to), mem_page_phys + (from), sizeof(int) * (pages)); \ -} while (0) - - - -// Not a performance critical function, since it's only needed at emulator init time -static void init_helper_custom_memtab_policy ( - int rd_o, // custom read-offset to set, apply value -1 for not to change - mem_page_rd_f_type rd_f, // custom read-function to set, apply NULL for not to change - int wr_o, - mem_page_wr_f_type wr_f, - int slot, // starting slot - int slots // number of slots -) { - while (slots--) { - if (rd_o >= 0) { - mem_page_rd_o[slot] = rd_o; - rd_o += 0x100; - } - if (rd_f) - mem_page_rd_f[slot] = rd_f; - if (wr_o >= 0) { - mem_page_wr_o[slot] = wr_o; - wr_o += 0x100; - } - if (wr_f) - mem_page_wr_f[slot] = wr_f; - mem_page_phys[slot] = 1; // invalidate phys info, to avoid cache-missbehaviour in decoder - mem_page_refp[slot] = &impossible_mapping; // invalidate this too - slot++; + //DEBUGPRINT("PHYS-RAM: $%X linear address is mapped into $%X-$%X in slot $%X" NL, addr, p->first, p->last, slot); // REMOVE +#ifdef MEM_USE_HINTS + mem_map_hints[hint_slot] = p; +#endif + mem_slot_rd_addr32[slot] = mem_slot_wr_addr32[slot] = addr; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = mem_slot_wr_data[slot] = NULL; // by default state: no data pointers, actual case handlers below can override this, of course +#endif + mem_slot_type[slot] = p->type; + switch (p->type) { + case MEM_SLOT_TYPE_MAIN_RAM: + mem_slot_rd_func[slot] = main_ram_reader; + mem_slot_wr_func[slot] = main_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = mem_slot_wr_data[slot] = main_ram + addr; +#endif + break; + case MEM_SLOT_TYPE_ROM: + mem_slot_rd_func[slot] = main_ram_reader; + mem_slot_wr_func[slot] = (rom_protect && slot != MEM_SLOT_SDEBUG) ? dummy_writer : main_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = main_ram + addr; + if (!rom_protect || slot == MEM_SLOT_SDEBUG) + mem_slot_wr_data[slot] = main_ram + addr; +#endif + break; + case MEM_SLOT_TYPE_SHARED_RAM: + mem_slot_rd_func[slot] = main_ram_reader; + mem_slot_wr_func[slot] = shared_main_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = main_ram + addr; // NOTE: writing does NOT have data pointer, as it requires special care +#endif + break; + case MEM_SLOT_TYPE_COLOUR_RAM: + mem_slot_rd_func[slot] = colour_ram_reader; + mem_slot_wr_func[slot] = addr >= 0x0FF80800U ? colour_ram_writer : shared_colour_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = colour_ram + addr - 0x0FF80000U; + if (addr >= 0x0FF80800U) // the first 2K of colour RAM is "shared" thus we don't set data pointer for that: allow the write callback to do that (special case!) + mem_slot_wr_data[slot] = colour_ram + addr - 0x0FF80000U; +#endif + break; + case MEM_SLOT_TYPE_ATTIC_RAM: + mem_slot_rd_func[slot] = attic_ram_reader; + mem_slot_wr_func[slot] = attic_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = mem_slot_wr_data[slot] = attic_ram + addr - 0x08000000U; +#endif + break; + case MEM_SLOT_TYPE_HYPERVISOR_RAM: + if (XEMU_LIKELY(in_hypervisor || slot == MEM_SLOT_SDEBUG)) { + mem_slot_rd_func[slot] = hypervisor_ram_reader; + mem_slot_wr_func[slot] = hypervisor_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = mem_slot_wr_data[slot] = hypervisor_ram + addr - 0xFFF8000U; +#endif + } else { + mem_slot_rd_func[slot] = dummy_reader; + mem_slot_wr_func[slot] = dummy_writer; + } + break; + case MEM_SLOT_TYPE_DISK_BUFFER: + if (in_hypervisor || slot == MEM_SLOT_SDEBUG) { + mem_slot_rd_func[slot] = disk_buffer_hypervisor_reader; + mem_slot_wr_func[slot] = disk_buffer_hypervisor_writer; + } else { + mem_slot_rd_func[slot] = disk_buffer_user_reader; + mem_slot_wr_func[slot] = disk_buffer_user_writer; + } + break; + case MEM_SLOT_TYPE_ETH_BUFFER: + mem_slot_rd_func[slot] = eth_buffer_reader; + mem_slot_wr_func[slot] = eth_buffer_writer; + break; + case MEM_SLOT_TYPE_OPL3: + mem_slot_rd_func[slot] = dummy_reader; // TODO: what should I do here? + mem_slot_wr_func[slot] = opl3_writer; + break; + case MEM_SLOT_TYPE_SLOW_DEVICES: + mem_slot_rd_func[slot] = slow_devices_reader; + mem_slot_wr_func[slot] = slow_devices_writer; + break; + case MEM_SLOT_TYPE_IO: + mem_slot_rd_func[slot] = io_reader; + mem_slot_wr_func[slot] = io_writer; + break; + case MEM_SLOT_TYPE_I2C: + mem_slot_rd_func[slot] = i2c_reader; + mem_slot_wr_func[slot] = i2c_writer; + break; + case MEM_SLOT_TYPE_CHAR_RAM: + mem_slot_rd_func[slot] = char_ram_reader; + mem_slot_wr_func[slot] = char_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = mem_slot_wr_data[slot] = char_ram + addr - 0xFF7E000U; +#endif + break; + case MEM_SLOT_TYPE_1541_RAM: + // TODO: Not implemented yet, just here, since freezer accesses this memory area, and without **some** dummy + // support, it would cause "unhandled memory access" warning in Xemu. + mem_slot_rd_func[slot] = dummy_reader; + mem_slot_wr_func[slot] = dummy_writer; + break; + case MEM_SLOT_TYPE_DUMMY: + mem_slot_rd_func[slot] = dummy_reader; + mem_slot_wr_func[slot] = dummy_writer; + break; + case MEM_SLOT_TYPE_UNDECODED: + if (slot <= MEM_SLOT_LAST_REAL) { + mem_slot_rd_func[slot] = undecoded_reader; + mem_slot_wr_func[slot] = undecoded_writer; + } else { + mem_slot_rd_func[slot] = dummy_reader; + mem_slot_wr_func[slot] = dummy_writer; + } + break; + case MEM_SLOT_TYPE_UNMAPPED: + case MEM_SLOT_TYPE_BANKED_ROM: + case MEM_SLOT_TYPE_LEGACY_IO: + case MEM_SLOT_TYPE_UNRESOLVED: + case MEM_SLOT_TYPE_IMPOSSIBLE: + FATAL("Impossible address ($%X) or bad slot type (%d) error in %s()", addr, p->type, __func__); + break; } + slot_assignment_postprocessing(slot); } -void memory_init ( void ) +static void resolve_cpu_slot ( const Uint8 slot ) { - memset(D6XX_registers, 0, sizeof D6XX_registers); - memset(D7XX, 0xFF, sizeof D7XX); - memset(i2c_regs, 0xFF, sizeof i2c_regs); - // generate UUID. It will be overwritten anyway in mega65.c if there is i2c backup (not totally new Xemu install) - for (int a = I2C_UUID_OFFSET; a < I2C_UUID_OFFSET + I2C_UUID_SIZE; a++) - i2c_regs[a] = rand(); - memset(i2c_regs + I2C_NVRAM_OFFSET, 0, I2C_NVRAM_SIZE); // also fill NVRAM area with 0 (FIXME: needed?) - cart_init(); - rom_protect = 0; - in_hypervisor = 0; - for (int a = 0; a < MEM_SLOTS; a++) { - // First of ALL! Initialize mem_page_phys for an impossible value! or otherwise bad crashes would happen ... - mem_page_phys[a] = 1; // this is cool enough, since phys addr for this func, can be only 256 byte aligned, so it won't find these ever as cached! - phys_addr_decoder((a & 0xFF) << 8, a, -1); // at least we have well defined defaults :) with 'real' and 'virtual' slots as well ... + const Uint32 policy = policy4k[slot >> 4]; + //DEBUGPRINT("CPU-ADDR: slot $%X mapping for policy $%X" NL, slot, policy); // REMOVE + // !!! if MEM_USE_DATA_POINTERS is used, mem_slot_wr_data and mem_slot_rd_data must be ALWAYS set! + switch (policy) { + case BANK_POLICY_RAM: + mem_slot_type[slot] = MEM_SLOT_TYPE_UNMAPPED; + if (XEMU_LIKELY(slot)) { + mem_slot_rd_addr32[slot] = mem_slot_wr_addr32[slot] = slot << 8; + mem_slot_rd_func[slot] = main_ram_reader; + mem_slot_wr_func[slot] = main_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = mem_slot_wr_data[slot] = main_ram + (slot << 8); +#endif + } else { + mem_slot_rd_addr32[0] = mem_slot_wr_addr32[0] = 0U; + mem_slot_rd_func[0] = zero_page_reader; + mem_slot_wr_func[0] = zero_page_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[0] = mem_slot_wr_data[0] = NULL; +#endif + } + break; + case BANK_POLICY_ROM: + mem_slot_type[slot] = MEM_SLOT_TYPE_BANKED_ROM; + mem_slot_rd_addr32[slot] = 0x20000U + (slot << 8); + mem_slot_wr_addr32[slot] = slot << 8 ; + mem_slot_rd_func[slot] = main_ram_reader; + mem_slot_wr_func[slot] = main_ram_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = main_ram + 0x20000U + (slot << 8); + mem_slot_wr_data[slot] = main_ram + (slot << 8); +#endif + break; + case BANK_POLICY_IO: + mem_slot_type[slot] = MEM_SLOT_TYPE_LEGACY_IO; + mem_slot_rd_addr32[slot] = mem_slot_wr_addr32[slot] = mem_legacy_io_addr32 + ((slot - 0xD0U) << 8); // BANK_POLICY_IO can happen only from $D000 + //DEBUGPRINT("Legacy I/O assignment in slot $%X: $%X has been assigned (mem_legacy_io_addr32=$%X)" NL, slot, mem_slot_rd_addr32[slot], mem_legacy_io_addr32); // REMOVE + mem_slot_rd_func[slot] = io_reader; + mem_slot_wr_func[slot] = io_writer; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = mem_slot_wr_data[slot] = NULL; +#endif + break; + default: + // Some mapping for other "policy" values, using that value (non-negative integer!) + // mem_slot_* things will be set up by resolve_linear_addr() in this case + if (XEMU_UNLIKELY(policy >= BANK_POLICY_INVALID)) // these must not happen, which can happen is handled in other "case" branches. FIXME: remove this later? It's a sanity check. + FATAL("Invalid bank_policy4k[%d >> 4] = $%X in %s()!", slot, policy, __func__); + resolve_linear_slot(slot, (policy & 0xFF00000U) + ((policy + (slot << 8)) & 0xFFF00U)); + return; // return, not break! Unlike other cases in this "swhitch" statement. That's important! } - // Generate "templates" for VIC-III ROM mapping entry points - // FIXME: the theory, that VIC-III ROM mapping is not like C64, ie writing a mapped in ROM, would write the ROM, not something "under" as with C64 - // static void XEMU_INLINE phys_addr_decoder_array ( int megabyte_offset, int offset, int slot, int slots, int hint_slot ) - phys_addr_decoder_array(0, 0x38000, MEM_SLOT_C65_8KROM_8000, 32, -1); // 8K(32 pages) C65 VIC-III ROM mapping ($8000) from $38000 - phys_addr_decoder_array(0, 0x3A000, MEM_SLOT_C65_8KROM_A000, 32, -1); // 8K(32 pages) C65 VIC-III ROM mapping ($A000) from $3A000 - phys_addr_decoder_array(0, 0x2C000, MEM_SLOT_C65_4KROM_C000, 16, -1); // 4K(16 pages) C65 VIC-III ROM mapping ($C000) from $2C000 - phys_addr_decoder_array(0, 0x3E000, MEM_SLOT_C65_8KROM_E000, 32, -1); // 8K(32 pages) C65 VIC-III ROM mapping ($E000) from $3E000 - phys_addr_decoder_array(0, 0x2A000, MEM_SLOT_C64_8KROM_A000, 32, -1); // 8K(32 pages) C64 CPU I/O ROM mapping ($A000) from $2A000 [C64 BASIC] - phys_addr_decoder_array(0, 0x2D000, MEM_SLOT_C64_4KROM_D000, 16, -1); // 4K(16 pages) C64 CPU I/O ROM mapping ($D000) from $2D000 [C64 CHARGEN] - phys_addr_decoder_array(0, 0x2E000, MEM_SLOT_C64_8KROM_E000, 32, -1); // 8K(32 pages) C64 CPU I/O ROM mapping ($E000) from $2E000 [C64 KERNAL] - // C64 ROM mappings by CPU I/O port should be "tuned" for the "write through RAM" policy though ... - // we re-use some write-specific info for pre-initialized unmapped RAM for write access here - init_helper_custom_memtab_policy(-1, NULL, mem_page_wr_o[0xA0], mem_page_wr_f[0xA0], MEM_SLOT_C64_8KROM_A000, 32); // for C64 BASIC ROM - init_helper_custom_memtab_policy(-1, NULL, mem_page_wr_o[0xD0], mem_page_wr_f[0xD0], MEM_SLOT_C64_4KROM_D000, 16); // for C64 CHARGEN ROM - init_helper_custom_memtab_policy(-1, NULL, mem_page_wr_o[0xE0], mem_page_wr_f[0xE0], MEM_SLOT_C64_8KROM_E000, 32); // for C64 KERNAL ROM - // The C64/C65-style I/O area is handled in this way: as it is I/O mode dependent unlike M65 high-megabyte areas, - // we maps I/O (any mode) and "customize it" with an offset to transfer into the right mode (or such). - phys_addr_decoder_array(0xFF << 20, 0xD0000, MEM_SLOT_OLD_4K_IO_D000, 16, -1); - init_helper_custom_memtab_policy(-1, legacy_io_reader, -1, legacy_io_writer, MEM_SLOT_OLD_4K_IO_D000, 16); - // Initialize some memory related "caching" stuffs and state etc ... - cpu_rmw_old_data = -1; - memcfg_vic3_rom_mapping_last = 0xFF; - memcfg_cpu_io_port_last = 0xFF; - cpu_io_port[0] = 0; - cpu_io_port[1] = 0; - map_mask = 0; - map_offset_low = 0; - map_offset_high = 0; - map_megabyte_low = 0; - map_megabyte_high = 0; - map_marker_low = MAP_MARKER_DUMMY_OFFSET; - map_marker_high = MAP_MARKER_DUMMY_OFFSET; - //skip_unhandled_mem = 0; - for (int a = 0; a < 9; a++) - applied_memcfg[a] = MAP_MARKER_DUMMY_OFFSET - 1; - // Setting up the default memory configuration for M65 at least! - // Note, the exact order is IMPORTANT as being the first use of memory subsystem, actually these will initialize some things ... - memory_set_cpu_io_port_ddr_and_data(7, 7); - memory_set_vic3_rom_mapping(0); - memory_set_do_map(); - // Initiailize memory content with something ... - memset(main_ram, 0x00, sizeof main_ram); - memset(colour_ram, 0x00, sizeof colour_ram); - memset(slow_ram, 0xFF, sizeof slow_ram); - DEBUG("MEM: End of memory initiailization" NL); + slot_assignment_postprocessing(slot); // NOT for MAP'ed slots (it has its own code path for that). That's the reason for "return" instead of "break" above } - - - -static void apply_memory_config_0000_to_7FFF ( void ) { - int hint_slot = -1; - // 0000 - 1FFF - if (map_mask & 0x01) { - if (applied_memcfg[0] != map_marker_low) { - phys_addr_decoder_array(map_megabyte_low, map_offset_low, 0x00, 0x20, -1); - applied_memcfg[0] = map_marker_low; - } - hint_slot = 0x1F; - } else { - if (applied_memcfg[0]) { - MEM_TABLE_COPY(0x00, 0x100, 0x20); - applied_memcfg[0] = 0; - } - } - // 2000 - 3FFF - if (map_mask & 0x02) { - if (applied_memcfg[1] != map_marker_low) { - phys_addr_decoder_array(map_megabyte_low, map_offset_low + 0x2000, 0x20, 0x20, hint_slot); - applied_memcfg[1] = map_marker_low; - } - hint_slot = 0x3F; - } else { - if (applied_memcfg[1]) { - MEM_TABLE_COPY(0x20, 0x120, 0x20); - applied_memcfg[1] = 0; - } - } - // 4000 - 5FFF - if (map_mask & 0x04) { - if (applied_memcfg[2] != map_marker_low) { - phys_addr_decoder_array(map_megabyte_low, map_offset_low + 0x4000, 0x40, 0x20, hint_slot); - applied_memcfg[2] = map_marker_low; - } - hint_slot = 0x5F; - } else { - if (applied_memcfg[2]) { - MEM_TABLE_COPY(0x40, 0x140, 0x20); - applied_memcfg[2] = 0; - } - } - // 6000 - 7FFF - if (map_mask & 0x08) { - if (applied_memcfg[3] != map_marker_low) { - phys_addr_decoder_array(map_megabyte_low, map_offset_low + 0x6000, 0x60, 0x20, hint_slot); - applied_memcfg[3] = map_marker_low; - } - } else { - if (applied_memcfg[3]) { - MEM_TABLE_COPY(0x60, 0x160, 0x20); - applied_memcfg[3] = 0; - } - } -} -static void apply_memory_config_8000_to_9FFF ( void ) { - if (memcfg_vic3_rom_mapping_last & VIC3_ROM_MASK_8000) { - if (applied_memcfg[4] >= 0) { - MEM_TABLE_COPY(0x80, MEM_SLOT_C65_8KROM_8000, 0x20); - applied_memcfg[4] = -1; - } - } else if (map_mask & 0x10) { - if (applied_memcfg[4] != map_marker_high) { - phys_addr_decoder_array(map_megabyte_high, map_offset_high + 0x8000, 0x80, 0x20, -1); - applied_memcfg[4] = map_marker_high; - } - } else { - if (applied_memcfg[4]) { - MEM_TABLE_COPY(0x80, 0x180, 0x20); - applied_memcfg[4] = 0; - } - } -} -static void apply_memory_config_A000_to_BFFF ( void ) { - if (memcfg_vic3_rom_mapping_last & VIC3_ROM_MASK_A000) { - if (applied_memcfg[5] >= 0) { - MEM_TABLE_COPY(0xA0, MEM_SLOT_C65_8KROM_A000, 0x20); - applied_memcfg[5] = -1; - } - } else if (map_mask & 0x20) { - if (applied_memcfg[5] != map_marker_high) { - phys_addr_decoder_array(map_megabyte_high, map_offset_high + 0xA000, 0xA0, 0x20, -1); - applied_memcfg[5] = map_marker_high; - } - } else { - if (applied_memcfg[5] != memcfg_cpu_io_port_policy_A000_to_BFFF) { - MEM_TABLE_COPY(0xA0, memcfg_cpu_io_port_policy_A000_to_BFFF, 0x20); - applied_memcfg[5] = memcfg_cpu_io_port_policy_A000_to_BFFF; - } - } -} -static void apply_memory_config_C000_to_CFFF ( void ) { - // Special range, just 4K in length! - if (memcfg_vic3_rom_mapping_last & VIC3_ROM_MASK_C000) { - if (applied_memcfg[6] >= 0) { - MEM_TABLE_COPY(0xC0, MEM_SLOT_C65_4KROM_C000, 0x10); - applied_memcfg[6] = -1; - } - } else if (map_mask & 0x40) { - if (applied_memcfg[6] != map_marker_high) { - phys_addr_decoder_array(map_megabyte_high, map_offset_high + 0xC000, 0xC0, 0x10, -1); - applied_memcfg[6] = map_marker_high; - } - } else { - if (applied_memcfg[6]) { - MEM_TABLE_COPY(0xC0, 0x1C0, 0x10); - applied_memcfg[6] = 0; - } - } -} -static void apply_memory_config_D000_to_DFFF ( void ) { - // Special range, just 4K in length! - if (map_mask & 0x40) { - if (applied_memcfg[7] != map_marker_high) { - phys_addr_decoder_array(map_megabyte_high, map_offset_high + 0xD000, 0xD0, 0x10, -1); - applied_memcfg[7] = map_marker_high; - } - } else { - if (applied_memcfg[7] != memcfg_cpu_io_port_policy_D000_to_DFFF) { - MEM_TABLE_COPY(0xD0, memcfg_cpu_io_port_policy_D000_to_DFFF, 0x10); - applied_memcfg[7] = memcfg_cpu_io_port_policy_D000_to_DFFF; - } - } -} -static void apply_memory_config_E000_to_FFFF ( void ) { - if (memcfg_vic3_rom_mapping_last & VIC3_ROM_MASK_E000) { - if (applied_memcfg[8] >= 0) { - MEM_TABLE_COPY(0xE0, MEM_SLOT_C65_8KROM_E000, 0x20); - applied_memcfg[8] = -1; - } - } else if (map_mask & 0x80) { - if (applied_memcfg[8] != map_marker_high) { - phys_addr_decoder_array(map_megabyte_high, map_offset_high + 0xE000, 0xE0, 0x20, -1); - applied_memcfg[8] = map_marker_high; - } - } else { - if (applied_memcfg[8] != memcfg_cpu_io_port_policy_E000_to_FFFF) { - MEM_TABLE_COPY(0xE0, memcfg_cpu_io_port_policy_E000_to_FFFF, 0x20); - applied_memcfg[8] = memcfg_cpu_io_port_policy_E000_to_FFFF; - } - } +// NOTE: since the lazy resolver is intended to resolve the CPU address slot on-demand, this is an exception to the rule: +// the input "addr32" parameter is not really a linear address (the whole purpose here is to GET THAT!) but some fake one. +// Thus only the low 1 byte can be re-used here to form the offset within the "slot" after we resolved the linear start +// address of the slot itself. +static Uint8 lazy_cpu_read_resolver ( const Uint32 addr32 ) +{ + resolve_cpu_slot(ref_slot); + //DEBUGPRINT("LAZY RESOLVER reading at $%X (slot $%X) at PC = $%04X" NL, addr32, ref_slot, cpu65.old_pc); // REMOVE + return mem_slot_rd_func[ref_slot](mem_slot_rd_addr32[ref_slot] + (addr32 & 0xFFU)); // re-using the low byte as the offset within the slot } - - - -// must be called when VIC-III register $D030 is written, with the written value exactly -void memory_set_vic3_rom_mapping ( Uint8 value ) +// See the comments above with the lazy read resolver! +static void lazy_cpu_write_resolver ( const Uint32 addr32, const Uint8 data ) { - // D030 regiser of VIC-III is: - // 7 6 5 4 3 2 1 0 - // | ROM | CROM | ROM | ROM | ROM | PAL | EXT | CRAM | - // | @E000 | @9000 | @C000 | @A000 | @8000 | | SYNC | @DC00 | - if (in_hypervisor) - value = 0; // in hypervisor, VIC-III ROM banking should *not* work (newer M65 change) - else - value &= VIC3_ROM_MASK_8000 | VIC3_ROM_MASK_A000 | VIC3_ROM_MASK_C000 | VIC3_ROM_MASK_E000; // only keep bits we're interested in - if (value != memcfg_vic3_rom_mapping_last) { // only do, if there was a change - Uint8 change = memcfg_vic3_rom_mapping_last ^ value; // change mask, bits have 1 only if there was a change - DEBUG("MEM: VIC-III ROM mapping change $%02X -> %02X" NL, memcfg_vic3_rom_mapping_last, value); - memcfg_vic3_rom_mapping_last = value; // don't forget to store the current state for next check! - // now check bits changed in ROM mapping - if (change & VIC3_ROM_MASK_8000) - apply_memory_config_8000_to_9FFF(); - if (change & VIC3_ROM_MASK_A000) - apply_memory_config_A000_to_BFFF(); - if (change & VIC3_ROM_MASK_C000) - apply_memory_config_C000_to_CFFF(); - if (change & VIC3_ROM_MASK_E000) - apply_memory_config_E000_to_FFFF(); - } + resolve_cpu_slot(ref_slot); + //DEBUGPRINT("LAZY RESOLVER writing at $%X (slot $%X) with data $%02X at PC = $%04X" NL, addr32, ref_slot, data, cpu65.old_pc); // REMOVE + mem_slot_wr_func[ref_slot](mem_slot_wr_addr32[ref_slot] + (addr32 & 0xFFU), data); // re-using the low byte as the offset within the slot } -static void apply_cpu_io_port_config ( void ) +static inline void invalidate_slot ( const unsigned int slot ) { - Uint8 desired = (cpu_io_port[1] | (~cpu_io_port[0])) & 7; - if (desired != memcfg_cpu_io_port_last) { - DEBUG("MEM: CPUIOPORT: port composite value (new one) is %d" NL, desired); - memcfg_cpu_io_port_last = desired; - memcfg_cpu_io_port_policy_A000_to_BFFF = memcfg_cpu_io_port_policies_A000_to_BFFF[desired]; - memcfg_cpu_io_port_policy_D000_to_DFFF = memcfg_cpu_io_port_policies_D000_to_DFFF[desired]; - memcfg_cpu_io_port_policy_E000_to_FFFF = memcfg_cpu_io_port_policies_E000_to_FFFF[desired]; - // check only regions to apply, where CPU I/O port can change anything - apply_memory_config_A000_to_BFFF(); - apply_memory_config_D000_to_DFFF(); - apply_memory_config_E000_to_FFFF(); - DEBUG("MEM: CPUIOPORT: new config had been applied" NL); + // the value here: signal impossibility: >=28 bit address + // WARNING: these offsets are added by the CPU memory handling callback, so the lower bytes should be zero! + mem_slot_rd_addr32[slot] = mem_slot_wr_addr32[slot] = 0x20000000U; + mem_slot_type[slot] = MEM_SLOT_TYPE_UNRESOLVED; +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = mem_slot_wr_data[slot] = NULL; +#endif + if (slot < 0x100U) { + mem_slot_rd_func[slot] = mem_slot_rd_func_real[slot] = lazy_cpu_read_resolver; + mem_slot_wr_func[slot] = mem_slot_wr_func_real[slot] = lazy_cpu_write_resolver; } } -unsigned int mem_get_4k_region_short_desc ( char *p, const unsigned int n, const unsigned int i ) +static inline void invalidate_slot_range ( unsigned int first_slot, const unsigned int last_slot ) { - if (map_mask & (1 << (i >> 1))) - return snprintf(p, n, "%X", (i & 8 ? map_megabyte_high : map_megabyte_low) + (((i & 8 ? map_offset_high : map_offset_low) + (i << 12)) & 0xFFFFF)); - else if ((i == 0x8 || i == 0x9) && (vic_registers[0x30] & 8)) - return snprintf(p, n, "ROM8000"); - else if ((i == 0xA || i == 0xB) && ((vic_registers[0x30] & 16) || memcfg_cpu_io_port_last == 3 || memcfg_cpu_io_port_last == 7)) - return snprintf(p, n, "BASIC"); - else if ((i == 0xC ) && (vic_registers[0x30] & 32)) - return snprintf(p, n, "ROMC000"); - else if ((i == 0xD ) && (memcfg_cpu_io_port_last == 1 || memcfg_cpu_io_port_last == 2 || memcfg_cpu_io_port_last == 3)) - return snprintf(p, n, "CHARGEN"); - else if ( i == 0xD && memcfg_cpu_io_port_last >= 5) - return snprintf(p, n, "IO"); - else if ((i == 0xE || i == 0xF) && ((vic_registers[0x30] & 128) || memcfg_cpu_io_port_last == 2 || memcfg_cpu_io_port_last == 3 || memcfg_cpu_io_port_last == 6 || memcfg_cpu_io_port_last == 7)) - return snprintf(p, n, "KERNAL"); - return snprintf(p, n, "unmap"); + while (first_slot <= last_slot) + invalidate_slot(first_slot++); } -// must be called on CPU I/O port write, addr=0/1 for DDR/DATA -// do not call with other addr than 0/1! -void memory_set_cpu_io_port ( int addr, Uint8 value ) +static void apply_cpu_memory_policy ( Uint8 i ) { - if (XEMU_UNLIKELY((addr == 0) && ((value & 0xFE) == 64))) { // M65-specific speed control stuff! - value &= 1; - if (value != ((D6XX_registers[0x7D] >> 4) & 1)) { - if (value) - D6XX_registers[0x7D] |= 16; - else - D6XX_registers[0x7D] &= ~16; - machine_set_speed(0); + for (; i < 0x10U; i++) { + const Uint32 new_policy = (map_mask & (1U << (i >> 1))) ? + ( (i >= 8) ? map_megabyte_high + map_offset_high : map_megabyte_low + map_offset_low ) : policy4k_banking[i]; + if (new_policy != policy4k[i]) { + policy4k[i] = new_policy; + invalidate_slot_range(i << 4, (i << 4) + 0xF); } - } else { - cpu_io_port[addr] = value; - apply_cpu_io_port_config(); } } -void memory_set_cpu_io_port_ddr_and_data ( Uint8 p0, Uint8 p1 ) +static bool set_banking_config ( Uint8 vic3_d030 ) { - cpu_io_port[0] = p0; - cpu_io_port[1] = p1; - apply_cpu_io_port_config(); -} - - -Uint8 memory_get_cpu_io_port ( int addr ) + static Uint8 c64_cfg_old = 0xFF; + static Uint8 vic3_d030_old = 0xFF; + const Uint8 c64_cfg = (cpu_io_port[1] | (~cpu_io_port[0])) & 7U; + vic3_d030 = in_hypervisor ? 0 : vic3_d030 & VIC3_ROM_D030_MASK; + if (vic3_d030_old == vic3_d030 && c64_cfg_old == c64_cfg) + return false; + c64_cfg_old = c64_cfg; + vic3_d030_old = vic3_d030; + // Let's resolve the banking situation, we must fil policy4k_banking at this point (note: the first 32K - ie first 8 entiries of policy4k_banking[]) can never change + /* -- Port pin (bit) $A000 to $BFFF $D000 to $DFFF $E000 to $FFFF + -- 2 1 0 Read Write Read Write Read Write + -- -------------- ---------------- ---------------- ---------------- + 0 -- 0 0 0 RAM RAM RAM RAM RAM RAM + 1 -- 0 0 1 RAM RAM CHAR-ROM RAM RAM RAM + 2 -- 0 1 0 RAM RAM CHAR-ROM RAM KERNAL-ROM RAM + 3 -- 0 1 1 BASIC-ROM RAM CHAR-ROM RAM KERNAL-ROM RAM + 4 -- 1 0 0 RAM RAM RAM RAM RAM RAM + 5 -- 1 0 1 RAM RAM I/O I/O RAM RAM + 6 -- 1 1 0 RAM RAM I/O I/O KERNAL-ROM RAM + 7 -- 1 1 1 BASIC-ROM RAM I/O I/O KERNAL-ROM RAM + + D030: C65 $D030.0 VIC-III:CRAM2K Map 2nd KB of colour RAM @ $DC00-$DFFF + C65 $D030.1 VIC-III:EXTSYNC Enable external video sync (genlock input) + C65 $D030.2 VIC-III:PAL Use PALETTE ROM (0) or RAM (1) entries for colours 0 - 15 + C65 $D030.3 VIC-III:ROM8 Map C65 ROM @ $8000 + C65 $D030.4 VIC-III:ROMA Map C65 ROM @ $A000 + C65 $D030.5 VIC-III:ROMC Map C65 ROM @ $C000 + C65 $D030.6 VIC-III:CROM9 Select between C64 and C65 charset. + C65 $D030.7 VIC-III:ROME Map C65 ROM @ $E000 */ + policy4k_banking[0x8] = policy4k_banking[0x9] = (vic3_d030 & 0x08) ? BANK_POLICY_ROM : BANK_POLICY_RAM ; + policy4k_banking[0xA] = policy4k_banking[0xB] = (vic3_d030 & 0x10) || (c64_cfg & 3) == 3 ? BANK_POLICY_ROM : BANK_POLICY_RAM ; + policy4k_banking[0xC] = (vic3_d030 & 0x20) ? BANK_POLICY_ROM : BANK_POLICY_RAM ; + policy4k_banking[0xD] = (c64_cfg > 4) ? BANK_POLICY_IO : ( + (c64_cfg & 3) == 0 ? BANK_POLICY_RAM : BANK_POLICY_ROM); + policy4k_banking[0xE] = policy4k_banking[0xF] = (vic3_d030 & 0x80) || (c64_cfg & 2) ? BANK_POLICY_ROM : BANK_POLICY_RAM ; + return true; +} + + +int memory_cpu_addr_to_desc ( const Uint16 cpu_addr, char *p, const unsigned int n ) { - return cpu_io_port[addr]; + const Uint32 policy = policy4k[cpu_addr >> 12]; + switch (policy) { + case BANK_POLICY_RAM: + return snprintf(p, n, "unmap"); + case BANK_POLICY_ROM: + switch (cpu_addr >> 12) { + case 0x8: case 0x9: return snprintf(p, n, "ROM8000"); + case 0xA: case 0xB: return snprintf(p, n, "BASIC"); + case 0xC: return snprintf(p, n, "ROMC000"); + case 0xD: return snprintf(p, n, "CHARGEN"); + case 0xE: case 0xF: return snprintf(p, n, "KERNAL"); + default: goto error; + } + case BANK_POLICY_IO: + return snprintf(p, n, "IO"); + default: + if (policy >= BANK_POLICY_INVALID) + goto error; + return snprintf(p, n, "%X", policy + cpu_addr); + } +error: + FATAL("Invalid bank_policy4k[%u >> 12] = $%X in %s()!", cpu_addr, policy, __func__); + XEMU_UNREACHABLE(); } - - - -// Call this after MAP opcode, map_* variables must be pre-initialized -// Can be also used to set custom mapping (hypervisor enter/leave, maybe snapshot loading) -void memory_set_do_map ( void ) +Uint32 memory_cpu_addr_to_linear ( const Uint16 cpu_addr, Uint32 *wr_addr_p ) { - // map_marker_low and map_maker_high are just articial markers, not so much to do with real offset, used to - // detect already done operations. It must be unique for each possible mappings, that is the only rule. - // to leave room for other values we use both of megabyte info and offset info, but moved from the zero - // reference (WARNING: mapped from zero and unmapped are different states!) to have place for other markers too. - map_marker_low = (map_megabyte_low | map_offset_low ) + MAP_MARKER_DUMMY_OFFSET; - map_marker_high = (map_megabyte_high | map_offset_high) + MAP_MARKER_DUMMY_OFFSET; - // We need to check every possible memory regions for the effect caused by MAPping ... - apply_memory_config_0000_to_7FFF(); - apply_memory_config_8000_to_9FFF(); - apply_memory_config_A000_to_BFFF(); - apply_memory_config_C000_to_CFFF(); - apply_memory_config_D000_to_DFFF(); - apply_memory_config_E000_to_FFFF(); - DEBUG("MEM: memory_set_do_map() applied" NL); + const Uint32 policy = policy4k[cpu_addr >> 12]; + Uint32 wr_addr, rd_addr; + switch (policy) { + case BANK_POLICY_RAM: + rd_addr = wr_addr = cpu_addr; + break; + case BANK_POLICY_ROM: + rd_addr = 0x20000U + cpu_addr; + wr_addr = cpu_addr; + break; + case BANK_POLICY_IO: + rd_addr = wr_addr = mem_legacy_io_addr32 + (cpu_addr & 0xFFFU); + break; + default: + if (XEMU_UNLIKELY(policy >= BANK_POLICY_INVALID)) + FATAL("Invalid bank_policy4k[%u >> 12] = $%X in %s()!", cpu_addr, policy, __func__); + rd_addr = wr_addr = policy + cpu_addr; + break; + } + if (wr_addr_p) + *wr_addr_p = wr_addr; + return rd_addr; } @@ -851,11 +771,10 @@ void cpu65_do_aug_callback ( void ) DEBUG("LOW -OFFSET = $%03X, MB = $%02X" NL, map_offset_low , map_megabyte_low >> 20); DEBUG("HIGH-OFFSET = $%03X, MB = $%02X" NL, map_offset_high, map_megabyte_high >> 20); DEBUG("MASK = $%02X" NL, map_mask); - memory_set_do_map(); + apply_cpu_memory_policy(0); // take new MAP config into account } - // *** Implements the EOM opcode of 4510, called by the 65CE02 emulator void cpu65_do_nop_callback ( void ) { @@ -867,136 +786,393 @@ void cpu65_do_nop_callback ( void ) } -/* For 32 (28 ...) bit linear addressing we use a dedicated mapper slot. Please read - command above, similar situation as with the DMA. However we need to fetch the - base (+Z) from base page, so it can be a bit less efficient if different 32 bit - pointers used all the time in 4510GS code. */ +static Uint8 zero_page_reader ( const Uint32 addr32 ) +{ + // this could be called only with linear addr (addr32) being in the first 256 byte anyway, so it's OK to use addr32 directly to address cpu_io_port, etc ... + //DEBUGPRINT("ZERO PAGE READER: query address $%X (mem_slot_rd_addr32[$%X]=$%X) at PC=$%04X" NL, addr32, ref_slot, mem_slot_rd_addr32[ref_slot], cpu65.old_pc); // REMOVE + return XEMU_LIKELY(addr32 & 0xFEU) ? main_ram[addr32] : cpu_io_port[addr32]; +} -static XEMU_INLINE int cpu_get_flat_addressing_mode_address ( int index ) +static void zero_page_writer ( const Uint32 addr32, const Uint8 data ) +{ + //DEBUGPRINT("ZERO PAGE READER: set address $%X (mem_slot_wr_addr32[$%X]=$%X) to $%X at PC=$%04X" NL, addr32, ref_slot, mem_slot_wr_addr32[ref_slot], data, cpu65.old_pc); // REMOVE + if (XEMU_LIKELY(addr32 & 0xFEU)) { + main_ram[addr32] = data; + } else { + if (XEMU_UNLIKELY(!addr32 && (data == 64 || data == 65))) { // special "magic" values used on MEGA65 to set the speed gate + if (((D6XX_registers[0x7D] >> 4) ^ data) & 1U) { + D6XX_registers[0x7D] ^= 16U; + machine_set_speed(0); + } + } else { + cpu_io_port[addr32] = data; + if (set_banking_config(vic_registers[0x30])) + apply_cpu_memory_policy(8); // start with 4K region 8 (@$8000): banking cannot change the lower 32K + } + } +} + + +Uint8 memory_get_cpu_io_port ( const Uint8 port ) +{ + return cpu_io_port[port & 1U]; +} + + +// Re-set linear address of memory slots given to "legacy I/O" which can be only at the range $D000-$DFFF +// Note: this function happily sets I/O mode even in hypervisor! So only call this, if you know, that's OK! [on MEGA65 hypervisor mode _always_ uses MEGA65 I/O mode!] +void memory_set_io_mode ( const Uint8 new_io_mode ) +{ + if (io_mode == new_io_mode) + return; + io_mode = new_io_mode; + mem_legacy_io_addr32 = 0xFFD0000U + ((unsigned)(new_io_mode) << 12); // this value is used at other places as well + for (Uint8 slot = 0xD0U; slot <= 0xDFU; slot++) + if (mem_slot_type[slot] == MEM_SLOT_TYPE_LEGACY_IO) // if it's a resolved legacy I/O slot @ $DXXX, we re-set the corresponding linear address for those + mem_slot_rd_addr32[slot] = mem_slot_wr_addr32[slot] = mem_legacy_io_addr32 + ((slot - 0xD0U) << 8); + // TODO: I must think of a solution to the DMA I/O mode, how to handle that on I/O mode changes +} + + +void memory_write_d030 ( const Uint8 data ) +{ + if (set_banking_config(data)) + apply_cpu_memory_policy(8); // start with 4K region 8 (@$8000): banking cannot change the lower 32K +} + + +void memory_set_rom_protection ( const bool protect ) +{ + if (protect == rom_protect) + return; + rom_protect = protect; + DEBUGPRINT("MEGA65: ROM protection has been turned %s." NL, rom_protect ? "ON" : "OFF"); // FIXME: should I left this "DEBUGPRINT"? + for (unsigned int slot = 0; slot < MEM_SLOTS_TOTAL; slot++) + if (mem_slot_type[slot] == MEM_SLOT_TYPE_ROM) + invalidate_slot(slot); +} + + +void memory_reconfigure ( + const Uint8 d030_value, const Uint8 new_io_mode, const Uint8 new_cpu_port0, const Uint8 new_cpu_port1, + const Uint32 new_map_mb_lo, const Uint32 new_map_ofs_lo, + const Uint32 new_map_mb_hi, const Uint32 new_map_ofs_hi, + const Uint8 new_map_mask, + const bool new_in_hypervisor +) { + if (new_in_hypervisor != in_hypervisor) { + in_hypervisor = new_in_hypervisor; + // Invalidate slots, where behaviour is hypervisor/user mode dependent! + for (unsigned int slot = 0; slot < MEM_SLOTS_TOTAL; slot++) + if (mem_slot_type[slot] >= MEM_SLOT_TYPE_HYPERVISOR_RAM) // see the mem_slot_type enum type definition for more details. This is kind of abusing enums ... + invalidate_slot(slot); + } + map_megabyte_low = new_map_mb_lo; + map_offset_low = new_map_ofs_lo; + map_megabyte_high = new_map_mb_hi; + map_offset_high = new_map_ofs_hi; + map_mask = new_map_mask; + cpu_io_port[0] = new_cpu_port0; + cpu_io_port[1] = new_cpu_port1; + memory_set_io_mode(new_io_mode); + (void)set_banking_config(d030_value); + apply_cpu_memory_policy(0); +} + + +#define SIZEOF_KILO(b) ((Uint32)sizeof(b) >> 10) + + +void memory_init (void ) +{ + // Check memory map table consistency + for (unsigned int i = 0, start_at = 0; i < MEM_MAP_SIZE; start_at = mem_map[i++].last + 1U) { + if ( + mem_map[i].first != start_at || mem_map[i].last <= mem_map[i].first || + (mem_map[i].first & 0xFFU) != 0 || (mem_map[i].last & 0xFFU) != 0xFFU || + (i == MEM_MAP_SIZE - 1 && (mem_map[i].first != 0x10000000U || mem_map[i].last != 0xFFFFFFFFU || mem_map[i].type != MEM_SLOT_TYPE_IMPOSSIBLE)) || + (i != MEM_MAP_SIZE - 1 && (mem_map[i].first == 0x10000000U || mem_map[i].last == 0xFFFFFFFFU || mem_map[i].type == MEM_SLOT_TYPE_IMPOSSIBLE)) + ) + FATAL("INTERNAL XEMU FATAL ERROR: Bad memory decoding table 'mem_map' at entry #%d ($%X-$%X) [should start at $%X]", i, mem_map[i].first, mem_map[i].last, start_at); + if (mem_map[i].type == MEM_SLOT_TYPE_MAIN_RAM) + main_ram_size = mem_map[i].last + 1; + } + memset(D6XX_registers, 0, sizeof D6XX_registers); + memset(D7XX, 0xFFU, sizeof D7XX); + memset(i2c_regs, 0xFFU, sizeof i2c_regs); + // generate UUID. It will be overwritten anyway in mega65.c if there is i2c backup (not totally new Xemu install) + for (unsigned int i = I2C_UUID_OFFSET; i < I2C_UUID_OFFSET + I2C_UUID_SIZE; i++) + i2c_regs[i] = rand(); + memset(i2c_regs + I2C_NVRAM_OFFSET, 0, I2C_NVRAM_SIZE); // also fill NVRAM area with 0 (FIXME: needed?) + cart_init(); + rom_protect = false; + D6XX_registers[0x7D] &= ~4; + in_hypervisor = false; +#ifdef MEM_USE_HINTS + for (unsigned int i = 0; i < MEM_HINT_SLOTS; i++) + mem_map_hints[i] = mem_map; +#endif + for (unsigned int i = 0; i < 0x10; i++) { + policy4k[i] = BANK_POLICY_INVALID; + policy4k_banking[i] = (i >= 8) ? BANK_POLICY_INVALID : BANK_POLICY_RAM; // lower 32K cannot be banked ever, but we need the value of BANK_POLICY_RAM to simplify logic + } +#ifdef MEM_WATCH_SUPPORT + memset(mem_slot_watcher, 0, sizeof mem_slot_watcher); // initially no watchers at all +#endif + invalidate_slot_range(0, MEM_SLOTS_TOTAL - 1); // make sure we have a consistent state + cpu_rmw_old_data = -1; + vic_registers[0x30] &= ~VIC3_ROM_D030_MASK; + memory_reconfigure( + 0, // $D030 ROM banking + VIC2_IOMODE, + 0, 0, // some initial CPU I/O port values + 0, 0, 0, 0, // MAP lo/hi mb/ofs + 0, // MAP mask + false // not-hypervisor + ); + // Initiailize memory content with something ... + // NOTE: make sure the first 2K of colour_ram is the **SAME** as the 2K part of main_ram at offset $1F800 + memset(main_ram, 0x00, sizeof main_ram); + memset(colour_ram, 0x00, sizeof colour_ram); + memset(attic_ram, 0xFF, sizeof attic_ram); + DEBUGPRINT("MEM: memory decoder initialized, %uK fast, %uK attic, %uK colour, %uK font RAM" NL, + main_ram_size >> 10, + SIZEOF_KILO(attic_ram), + SIZEOF_KILO(colour_ram), + SIZEOF_KILO(char_ram) + ); +} + + +// Warning: this overwrites ref_slot! +static XEMU_INLINE Uint32 cpu_get_flat_addressing_mode_address ( const Uint8 index ) { - register int addr = cpu65_read_callback(cpu65.pc++); // fetch base page address // FIXME: really, BP/ZP is wrapped around in case of linear addressing and eg BP addr of $FF got?????? (I think IT SHOULD BE!) - // FIXME: migrate to cpu_read_paged(), but we need CPU emu core to utilize BP rather than BP << 8, and - // similar older hacks ... - return ( - cpu65_read_callback(cpu65.bphi | addr ) | - (cpu65_read_callback(cpu65.bphi | ((addr + 1) & 0xFF)) << 8) | - (cpu65_read_callback(cpu65.bphi | ((addr + 2) & 0xFF)) << 16) | - (cpu65_read_callback(cpu65.bphi | ((addr + 3) & 0xFF)) << 24) - ) + index; // I don't handle the overflow of 28 bit addr.space situation, as addr will be anyway "trimmed" later in phys_addr_decoder() issued by the user of this func + const Uint8 bp_addr = cpu65_read_callback(cpu65.pc++); // fetch base page address (we plays the role of the CPU here) + ref_slot = cpu65.bphi >> 8; // basically the page number of the base page, what BP would mean (however CPU65 emulator uses BPHI ... the offset of the base page ...) + if (mem_slot_type[ref_slot] == MEM_SLOT_TYPE_UNRESOLVED) // make sure the slot is resolved, otherwise the query of rd_base_addr below can be invalid before the first read!!!!!!! + resolve_cpu_slot(ref_slot); + const Uint32 rd_base_addr = mem_slot_rd_addr32[ref_slot]; + return index + + (mem_slot_rd_func[ref_slot](rd_base_addr + bp_addr ) ) + + (mem_slot_rd_func[ref_slot](rd_base_addr + ((bp_addr + 1) & 0xFFU)) << 8) + + (mem_slot_rd_func[ref_slot](rd_base_addr + ((bp_addr + 2) & 0xFFU)) << 16) + + (mem_slot_rd_func[ref_slot](rd_base_addr + ((bp_addr + 3) & 0xFFU)) << 24) ; +} + + +static XEMU_INLINE void resolve_special_rd_slot_on_demand ( const Uint32 slot, Uint32 addr32 ) +{ + addr32 &= 0xFFFFF00U; + if (XEMU_UNLIKELY(addr32 ^ mem_slot_rd_addr32[slot])) + resolve_linear_slot(slot, addr32); } + +static XEMU_INLINE void resolve_special_wr_slot_on_demand ( const Uint32 slot, Uint32 addr32 ) +{ + addr32 &= 0xFFFFF00U; + if (XEMU_UNLIKELY(addr32 ^ mem_slot_wr_addr32[slot])) + resolve_linear_slot(slot, addr32); +} + + Uint8 cpu65_read_linear_opcode_callback ( void ) { - register int addr = cpu_get_flat_addressing_mode_address(cpu65.z); - phys_addr_decoder(addr, MEM_SLOT_CPU_32BIT, MEM_SLOT_CPU_32BIT); - return CALL_MEMORY_READER(MEM_SLOT_CPU_32BIT, addr); + //DEBUGPRINT("cpu65_read_linear_opcode_callback fires at PC=$%04X" NL, cpu65.old_pc); // REMOVE + register const Uint32 addr32 = cpu_get_flat_addressing_mode_address(cpu65.z) & 0xFFFFFFFU; + //DEBUGPRINT("cpu65_read_linear_opcode_callback fires-2 at PC=$%04X" NL, cpu65.old_pc); // REMOVE + //DEBUGPRINT("cpu65_read_linear_opcode_callback: about to call read for addr = $%02X at PC=$%04X" NL, addr32, cpu65.old_pc); // REMOVE + resolve_special_rd_slot_on_demand(MEM_SLOT_CPU_LINEAR, addr32); + ref_slot = MEM_SLOT_CPU_LINEAR; + return mem_slot_rd_func[MEM_SLOT_CPU_LINEAR](addr32); } -void cpu65_write_linear_opcode_callback ( Uint8 data ) + +void cpu65_write_linear_opcode_callback ( const Uint8 data ) { - register int addr = cpu_get_flat_addressing_mode_address(cpu65.z); - phys_addr_decoder(addr, MEM_SLOT_CPU_32BIT, MEM_SLOT_CPU_32BIT); - CALL_MEMORY_WRITER(MEM_SLOT_CPU_32BIT, addr, data); + register const Uint32 addr32 = cpu_get_flat_addressing_mode_address(cpu65.z) & 0xFFFFFFFU; + resolve_special_wr_slot_on_demand(MEM_SLOT_CPU_LINEAR, addr32); + ref_slot = MEM_SLOT_CPU_LINEAR; + mem_slot_wr_func[MEM_SLOT_CPU_LINEAR](addr32, data); } -// FIXME: very ugly and very slow and maybe very buggy implementation! Should be done in a sane way in the next memory decoder version being developmented ... Uint32 cpu65_read_linear_long_opcode_callback ( const Uint8 index ) { - for (int shift = 0, ret = 0, addr = cpu_get_flat_addressing_mode_address(index) ;; ) { - phys_addr_decoder(addr, MEM_SLOT_CPU_32BIT, MEM_SLOT_CPU_32BIT); - ret += (Uint32)CALL_MEMORY_READER(MEM_SLOT_CPU_32BIT, addr) << shift; - if (shift == 24) - return ret; - addr++; - shift += 8; - } + Uint32 addr32 = cpu_get_flat_addressing_mode_address(index); + ref_slot = MEM_SLOT_CPU_LINEAR; + resolve_special_rd_slot_on_demand(MEM_SLOT_CPU_LINEAR, addr32); + Uint32 data = mem_slot_rd_func[MEM_SLOT_CPU_LINEAR](addr32 & 0xFFFFFFFU) ; + resolve_special_rd_slot_on_demand(MEM_SLOT_CPU_LINEAR, ++addr32); + data += (Uint32)mem_slot_rd_func[MEM_SLOT_CPU_LINEAR](addr32 & 0xFFFFFFFU) << 8; + resolve_special_rd_slot_on_demand(MEM_SLOT_CPU_LINEAR, ++addr32); + data += (Uint32)mem_slot_rd_func[MEM_SLOT_CPU_LINEAR](addr32 & 0xFFFFFFFU) << 16; + resolve_special_rd_slot_on_demand(MEM_SLOT_CPU_LINEAR, ++addr32); + data += (Uint32)mem_slot_rd_func[MEM_SLOT_CPU_LINEAR](addr32 & 0xFFFFFFFU) << 24; + return data; + } -// FIXME: very ugly and very slow and maybe very buggy implementation! Should be done in a sane way in the next memory decoder version being developmented ... -void cpu65_write_linear_long_opcode_callback ( const Uint8 index, Uint32 data ) + +void cpu65_write_linear_long_opcode_callback ( const Uint8 index, const Uint32 data ) { - for (int a = 0, addr = cpu_get_flat_addressing_mode_address(index) ;; ) { - phys_addr_decoder(addr, MEM_SLOT_CPU_32BIT, MEM_SLOT_CPU_32BIT); - CALL_MEMORY_WRITER(MEM_SLOT_CPU_32BIT, addr, data & 0xFF); - if (a == 3) - break; - addr++; - data >>= 8; - a++; + Uint32 addr32 = cpu_get_flat_addressing_mode_address(index); + ref_slot = MEM_SLOT_CPU_LINEAR; + resolve_special_wr_slot_on_demand(MEM_SLOT_CPU_LINEAR, addr32); + mem_slot_wr_func[MEM_SLOT_CPU_LINEAR](addr32 & 0xFFFFFFFU, data & 0xFFU); + resolve_special_wr_slot_on_demand(MEM_SLOT_CPU_LINEAR, ++addr32); + mem_slot_wr_func[MEM_SLOT_CPU_LINEAR](addr32 & 0xFFFFFFFU, (data >> 8) & 0xFFU); + resolve_special_wr_slot_on_demand(MEM_SLOT_CPU_LINEAR, ++addr32); + mem_slot_wr_func[MEM_SLOT_CPU_LINEAR](addr32 & 0xFFFFFFFU, (data >> 16) & 0xFFU); + resolve_special_wr_slot_on_demand(MEM_SLOT_CPU_LINEAR, ++addr32); + mem_slot_wr_func[MEM_SLOT_CPU_LINEAR](addr32 & 0xFFFFFFFU, (data >> 24) & 0xFFU); +} + + +#define CREATE_LINEAR_READER(name,slot) \ + Uint8 name ( const Uint32 addr32 ) { \ + resolve_special_rd_slot_on_demand(slot, addr32); \ + ref_slot = slot; \ + return mem_slot_rd_func[slot](addr32 & 0xFFFFFFFU); \ } -} +#define CREATE_LINEAR_WRITER(name,slot) \ + void name ( const Uint32 addr32, const Uint8 data ) { \ + resolve_special_wr_slot_on_demand(slot, addr32); \ + ref_slot = slot; \ + mem_slot_wr_func[slot](addr32 & 0xFFFFFFFU, data); \ + } -/* DMA related call-backs. We use a dedicated memory mapper "slot" for each DMA functions. - Source can be _written_ too (in case of SWAP operation for example). There are dedicated - slots for each functionality, so we don't need to re-map physical address again and again, - and we can take advantage of using the "cache" provided by phys_addr_decoder() which can - be especially efficient in case of linear operations, what DMA usually does. - Performance analysis (can be applied to other memory operations somewhat too, even CPU): - * if the next access for the given DMA func is in the same 256 page, phys_addr_decoder will return after just a comparsion operation - * if not, the "hint_slot" (3rd paramater) is used, if at least the same physical region (ie also fast-ram, etc) is used, again it's faster than full scan - * if even the previous statment is not true, phys_addr_decoder will scan the phyisical M65 memory layout to find the region, only */ +CREATE_LINEAR_READER(memory_dma_list_reader, MEM_SLOT_DMA_LIST) +CREATE_LINEAR_READER(memory_dma_source_mreader, MEM_SLOT_DMA_SOURCE) +CREATE_LINEAR_WRITER(memory_dma_source_mwriter, MEM_SLOT_DMA_SOURCE) +CREATE_LINEAR_READER(memory_dma_target_mreader, MEM_SLOT_DMA_TARGET) +CREATE_LINEAR_WRITER(memory_dma_target_mwriter, MEM_SLOT_DMA_TARGET) +CREATE_LINEAR_READER(debug_read_linear_byte, MEM_SLOT_DEBUG) +CREATE_LINEAR_WRITER(debug_write_linear_byte, MEM_SLOT_DEBUG) +CREATE_LINEAR_READER(sdebug_read_linear_byte, MEM_SLOT_SDEBUG) +CREATE_LINEAR_WRITER(sdebug_write_linear_byte, MEM_SLOT_SDEBUG) -Uint8 memory_dma_source_mreader ( int addr ) +Uint8 debug_read_cpu_byte ( const Uint16 addr16 ) { - phys_addr_decoder(addr, MEM_SLOT_DMA_RD_SRC, MEM_SLOT_DMA_RD_SRC); - return CALL_MEMORY_READER(MEM_SLOT_DMA_RD_SRC, addr); + ref_slot = addr16 >> 8; + return mem_slot_rd_func_real[ref_slot](mem_slot_rd_addr32[ref_slot] + (addr16 & 0xFFU)); } -void memory_dma_source_mwriter ( int addr, Uint8 data ) +void debug_write_cpu_byte ( const Uint16 addr16, const Uint8 data ) { - phys_addr_decoder(addr, MEM_SLOT_DMA_WR_SRC, MEM_SLOT_DMA_WR_SRC); - CALL_MEMORY_WRITER(MEM_SLOT_DMA_WR_SRC, addr, data); + ref_slot = addr16 >> 8; + mem_slot_wr_func_real[ref_slot](mem_slot_wr_addr32[ref_slot] + (addr16 & 0xFFU), data); } -Uint8 memory_dma_target_mreader ( int addr ) + +#ifdef MEM_WATCH_SUPPORT +static Uint8 memwatch_reader ( const Uint32 addr32 ) { - phys_addr_decoder(addr, MEM_SLOT_DMA_RD_DST, MEM_SLOT_DMA_RD_DST); - return CALL_MEMORY_READER(MEM_SLOT_DMA_RD_DST, addr); + Uint8 data = mem_slot_rd_func_real[ref_slot](addr32); + for (unsigned int i = 0, cpu_addr = (ref_slot << 8) + (addr32 & 0xFFU); i < mem_watchers.cpu_read_nums; i++) + if (cpu_addr >= w->cpu_begin && cpu_addr <= w->cpu_end && addr32 >= w->lin_begin && addr32 <= w->lin_end) + return watchmem_read_callback(i, addr32, cpu_addr, data); + return data; + + + + if (mem_watchers.cpu_read_nums && ref_slot < 0x100U) + for (unsigned int i = 0, cpu_addr = (ref_slot << 8) + (addr32 & 0xFFU); i < mem_watchers.cpu_read_nums; i++) + if (cpu_addr >= mem_watchers.cpu_read_list[i].begin && cpu_addr <= mem_watchers.cpu_read_list[i].end) + return memwatch_cpu_read_callback(i, addr32, cpu_addr, data); + if (mem_watchers.lin_read_nums) + for (unsigned int i = 0; i < mem_watchers.lin_read_nums; i++) + if (addr32 >= mem_watchers.lin_read_list[i].begin && addr32 <= mem_watchers.lin_read_list[i].end) + return memwatch_linear_read_callback(i, addr32, (ref_slot << 8) + (addr32 & 0xFFU), data); + return data; } -void memory_dma_target_mwriter ( int addr, Uint8 data ) + +static void memwatch_writer ( const Uint32 addr32, const Uint8 data ) { - phys_addr_decoder(addr, MEM_SLOT_DMA_WR_DST, MEM_SLOT_DMA_WR_DST); - CALL_MEMORY_WRITER(MEM_SLOT_DMA_WR_DST, addr, data); + if (mem_watchers_cpu_addr_read && ref_slot < 0x100U) { + const Uint16 cpu_addr = (ref_slot << 8) + (addr32 & 0xFFU); + for (unsigned int i = 0; i < mem_watchers_cpu_addr_read; i++) + if (mem_watchers_cpu_addr_read_list[i] == cpu_addr) { + data = debug_memwatch_cpu_read(addr32, cpu_addr, data); + break; + } + } + + + + // TODO: if memwatch-on-write is enabled for the slot - mem_slot_watcher[slot] & MEM_SLOT_WATCHER_WRITE is non-zero - then we should do something here + // mem_slot_wr_func_real[ref_slot](addr32, debug_memwatch_wrtie_callback(addr32, ref_slot, data)); + mem_slot_wr_func_real[ref_slot](addr32, data); } -Uint8 memory_dma_list_reader ( int addr ) +struct mem_watch_list_st { + Uint32 cpu_first, cpu_last, lin_first, lin_last; +}; +static const struct mem_watch_list_st *mem_watch_rd_list; +static const struct mem_watch_list_st *mem_watch_wr_list; + + +void memory_watch_clear_all ( void ) { - phys_addr_decoder(addr, MEM_SLOT_DMA_RD_LST, MEM_SLOT_DMA_RD_LST); - return CALL_MEMORY_READER(MEM_SLOT_DMA_RD_LST, addr); + for (unsigned int slot = 0; slot <= MEM_SLOT_LAST_REAL; slot++) { + if (mem_slot_watch[slot] & 0x7FU) { + mem_slot_watch[slot] = MEM_SLOT_WATCH_CHANGED; + } + } } -/* Debugger (ie: [uart]monitor) for reading/writing physical address */ -Uint8 memory_debug_read_phys_addr ( int addr ) + +void memory_watch_add ( void ) { - phys_addr_decoder(addr, MEM_SLOT_DEBUG_RESOLVER, MEM_SLOT_DEBUG_RESOLVER); - return CALL_MEMORY_READER(MEM_SLOT_DEBUG_RESOLVER, addr); } -void memory_debug_write_phys_addr ( int addr, Uint8 data ) + +void memory_watch_remove ( void ) { - phys_addr_decoder(addr, MEM_SLOT_DEBUG_RESOLVER, MEM_SLOT_DEBUG_RESOLVER); - CALL_MEMORY_WRITER(MEM_SLOT_DEBUG_RESOLVER, addr, data); } -int memory_cpurd2linear_xlat ( Uint16 cpu_addr) + +void memory_watch_start_transaction ( void ) { - int slot = cpu_addr >> 8; - return mem_page_rd_o[slot] + mem_page_refp[slot]->start + (int)(cpu_addr & 0xFF); + for (unsigned int slot = 0; slot <= MEM_SLOT_LAST_REAL; slot++) + mem_slot_watch[slot] &= ~MEM_SLOT_WATCH_CHANGED; } -/* the same as above but for CPU addresses */ -Uint8 memory_debug_read_cpu_addr ( Uint16 addr ) + +void memory_watch_commit ( void ) { - return CALL_MEMORY_READER(addr >> 8, addr); + for (unsigned int slot = 0; slot <= MEM_SLOT_LAST_REAL; slot++) + if (mem_slot_watch[slot] & MEM_SLOT_WATCH_CHANGED) { + mem_slot_watch[slot] &= ~MEM_SLOT_WATCH_CHANGED; + invalidate_slot(slot); + } } -void memory_debug_write_cpu_addr ( Uint16 addr, Uint8 data ) + +void memory_watch_notify_change ( const struct mem_watch_list_st *rd_list, const struct mem_watch_list_st *wr_list ) { - CALL_MEMORY_WRITER(addr >> 8, addr, data); + bool rd_need_cpu = false; + bool rd_need_lin = false; + bool wr_need_cpu = false; + bool wr_need_lin = false; + mem_watch_rd_list = rd_list; + mem_watch_rd_list_size = ... ; + mem_watch_wr_list = wr_list; + mem_watch_wr_list_size = ... ; + if (rd_list) + while (rd_list->cpu_last ) + } + + + for (unsigned int slot = 0; slot <= MEM_SLOT_LAST_REAL; slot++) + if (mem_slot_watcher[slot] & MEM_SLOT_WATCHER_CHANGED) { + mem_slot_watcher[slot] &= ~MEM_SLOT_WATCHER_CHANGED; + invalidate_slot(slot); + } } +#endif diff --git a/targets/mega65/memory_mapper.h b/targets/mega65/memory_mapper.h index 1fc16222..5bfdd3b1 100644 --- a/targets/mega65/memory_mapper.h +++ b/targets/mega65/memory_mapper.h @@ -20,31 +20,47 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define XEMU_MEGA65_MEMORY_MAPPER_H_INCLUDED extern void memory_init ( void ); -extern void memory_set_do_map ( void ); -extern void memory_set_vic3_rom_mapping ( Uint8 value ); -extern void memory_set_cpu_io_port ( int addr, Uint8 value ); -extern void memory_set_cpu_io_port_ddr_and_data ( Uint8 p0, Uint8 p1 ); -extern Uint8 memory_get_cpu_io_port ( int addr ); +extern void memory_set_rom_protection ( const bool protect ); +extern void memory_reconfigure ( + const Uint8 d030_value, const Uint8 new_io_mode, const Uint8 new_cpu_port0, const Uint8 new_cpu_port1, + const Uint32 new_map_mb_lo, const Uint32 new_map_ofs_lo, + const Uint32 new_map_mb_hi, const Uint32 new_map_ofs_hi, + const Uint8 new_map_mask, + const bool new_in_hypervisor +); +extern Uint8 memory_get_cpu_io_port ( const Uint8 addr ); +extern void memory_set_io_mode ( const Uint8 new_io_mode ); +extern void memory_write_d030 ( const Uint8 data ); -extern unsigned int mem_get_4k_region_short_desc ( char *p, const unsigned int n, const unsigned int i ); +extern int memory_cpu_addr_to_desc ( const Uint16 cpu_addr, char *p, const unsigned int n ); +extern Uint32 memory_cpu_addr_to_linear ( const Uint16 cpu_addr, Uint32 *wr_addr_p ); -extern Uint8 memory_debug_read_phys_addr ( int addr ); -extern void memory_debug_write_phys_addr ( int addr, Uint8 data ); -extern Uint8 memory_debug_read_cpu_addr ( Uint16 addr ); -extern void memory_debug_write_cpu_addr ( Uint16 addr, Uint8 data ); +// Non- CPU or DMA emulator memory acceses ("debug" read/write CPU/linear memory bytes) +extern Uint8 debug_read_linear_byte ( const Uint32 addr32 ); +extern Uint8 sdebug_read_linear_byte ( const Uint32 addr32 ); +extern void debug_write_linear_byte ( const Uint32 addr32, const Uint8 data ); +extern void sdebug_write_linear_byte ( const Uint32 addr32, const Uint8 data ); +// debug read/write CPU address functions: other than hardware emulation, these must be used for debug purposes (monitor/debugger, etc) +extern Uint8 debug_read_cpu_byte ( const Uint16 addr16 ); +extern void debug_write_cpu_byte ( const Uint16 addr16, const Uint8 data ); // DMA implementation related, used by dma65.c: -extern Uint8 memory_dma_source_mreader ( int addr ); -extern void memory_dma_source_mwriter ( int addr, Uint8 data ); -extern Uint8 memory_dma_target_mreader ( int addr ); -extern void memory_dma_target_mwriter ( int addr, Uint8 data ); -extern Uint8 memory_dma_list_reader ( int addr ); +extern Uint8 memory_dma_source_mreader ( const Uint32 addr32 ); +extern void memory_dma_source_mwriter ( const Uint32 addr32, const Uint8 data ); +extern Uint8 memory_dma_target_mreader ( const Uint32 addr32 ); +extern void memory_dma_target_mwriter ( const Uint32 addr32, const Uint8 data ); +extern Uint8 memory_dma_list_reader ( const Uint32 addr32 ); -extern int map_mask, map_offset_low, map_offset_high, map_megabyte_low, map_megabyte_high; -extern int rom_protect, skip_unhandled_mem; -extern Uint8 main_ram[512 << 10], colour_ram[0x8000], char_wom[0x2000], hypervisor_ram[0x4000]; +// MAP related variables, do not change these values directly! +extern Uint32 map_offset_low, map_offset_high, map_megabyte_low, map_megabyte_high; +extern Uint8 map_mask; + +extern Uint8 main_ram[512 << 10], colour_ram[0x8000], char_ram[0x2000], hypervisor_ram[0x4000]; +extern Uint32 main_ram_size; #define SLOW_RAM_SIZE (8 << 20) -extern Uint8 slow_ram[SLOW_RAM_SIZE]; +extern Uint8 attic_ram[SLOW_RAM_SIZE]; + +extern Uint8 io_mode; // "VIC" I/O mode: do not change this value directly! #define I2C_UUID_OFFSET 0x100 #define I2C_UUID_SIZE 8 @@ -54,14 +70,7 @@ extern Uint8 slow_ram[SLOW_RAM_SIZE]; #define I2C_NVRAM_SIZE 64 extern Uint8 i2c_regs[0x1000]; +extern int skip_unhandled_mem; extern int cpu_rmw_old_data; -static XEMU_INLINE void write_colour_ram ( const int addr, const Uint8 data ) -{ - colour_ram[addr] = data; - // we also need to update the corresponding part of the main RAM, if it's the first 2K of the colour RAM! - if (addr < 2048) - main_ram[addr + 0x1F800] = data; -} - #endif diff --git a/targets/mega65/sdcard.c b/targets/mega65/sdcard.c index eb298ea1..1caeee2e 100644 --- a/targets/mega65/sdcard.c +++ b/targets/mega65/sdcard.c @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "memcontent.h" #include "hypervisor.h" #include "vic4.h" +#include "memory_mapper.h" #include "configdb.h" #include "xemu/emutools_config.h" #include "xemu/compressed_disk_image.h" @@ -1240,7 +1241,7 @@ Uint8 sdcard_read_register ( const int reg ) // bits 2 and 3 is always zero in Xemu (no drive virtualization for drive 0 and 1) return (vic_registers[0x30] & 1) | // $D68A.0 SD:CDC00 (read only) Set if colour RAM at $DC00 - (vic_iomode & 2) | // $D68A.1 SD:VICIII (read only) Set if VIC-IV or ethernet IO bank visible [same bit pos as in vic_iomode for mode-4 and mode-ETH!] + (io_mode & 2) | // $D68A.1 SD:VICIII (read only) Set if VIC-IV or ethernet IO bank visible [same bit pos as in vic_iomode for mode-4 and mode-ETH!] (data & (128 + 64)); // size info for disk mounting break; case 0xB: diff --git a/targets/mega65/sdcard.h b/targets/mega65/sdcard.h index 3e94d22d..7da158d6 100644 --- a/targets/mega65/sdcard.h +++ b/targets/mega65/sdcard.h @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/targets/mega65/ui.c b/targets/mega65/ui.c index 519beeb8..b193d0b9 100644 --- a/targets/mega65/ui.c +++ b/targets/mega65/ui.c @@ -506,7 +506,7 @@ static void ui_dump_hyperram ( void ) fnbuf, sizeof fnbuf )) { - xemu_save_file(fnbuf, slow_ram, SLOW_RAM_SIZE, "Cannot dump hyperRAM content into file"); + xemu_save_file(fnbuf, attic_ram, SLOW_RAM_SIZE, "Cannot dump hyperRAM content into file"); } } @@ -543,7 +543,7 @@ static void ui_emu_info ( void ) sdcard_get_mount_info(0, NULL), sdcard_get_mount_info(1, NULL), memory_get_cpu_io_port(0) & 7, memory_get_cpu_io_port(1) & 7, cpu65.pc, memory_cpurd2linear_xlat(cpu65.pc), - iomode_names[vic_iomode], videostd_name, (vic_registers[0x5D] & 0x80) ? "enabled" : "disabled", + iomode_names[io_mode], videostd_name, (vic_registers[0x5D] & 0x80) ? "enabled" : "disabled", td_stat_str, xemu_get_uname_string(), emu_fs_is_utf8 ? "UTF8-FS" : "ASCII-FS", PRINTF_U64, PRINTF_X64, PRINTF_S64 ); diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index 5ed8152f..71088dd6 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -43,7 +43,6 @@ static Uint32 *current_pixel; // current_pixel pointer to the rendering targ static Uint32 *pixel_start; // points to the end and start of the buffer static Uint32 *pixel_raster_start; // first pixel of current raster Uint8 vic_registers[0x80]; // VIC4 registers -unsigned int vic_iomode; // VIC2/VIC3/VIC4 mode static int compare_raster; // raster compare (9 bits width) data static int logical_raster = 0; static int interrupt_status; // Interrupt status of VIC @@ -151,7 +150,7 @@ void vic_reset ( void ) { vic_frame_counter = 0; vic_frame_counter_since_boot = 0; - vic_iomode = VIC2_IOMODE; + memory_set_io_mode(VIC2_IOMODE); vic_color_register_mask = 0x0F; interrupt_status = 0; compare_raster = 0; @@ -658,11 +657,11 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) case 0x4554: vic_new_iomode = VIC4ETH_IOMODE; break; case 0x4753: vic_new_iomode = VIC4_IOMODE; break; } - if (vic_new_iomode != vic_iomode) { + if (vic_new_iomode != io_mode) { static const Uint8 color_register_masks[4] = { 0x0F, 0xFF, 0xFF, 0xFF }; - DEBUG("VIC4: changing I/O mode %d(%s) -> %d(%s)" NL, vic_iomode, iomode_names[vic_iomode], vic_new_iomode, iomode_names[vic_new_iomode]); - vic_iomode = vic_new_iomode; - vic_color_register_mask = color_register_masks[vic_iomode]; + DEBUG("VIC4: changing I/O mode %d(%s) -> %d(%s)" NL, io_mode, iomode_names[io_mode], vic_new_iomode, iomode_names[vic_new_iomode]); + memory_set_io_mode(vic_new_iomode); + vic_color_register_mask = color_register_masks[io_mode]; } } else DEBUGPRINT("VIC4: warning: I/O mode KEY $D02F register wanted to be written (with $%02X) in hypervisor mode! PC=$%04X" NL, data, cpu65.old_pc); @@ -676,7 +675,7 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) return; // it IS important to have return here, since it's not a "real" VIC4 mode register's view in another mode!! /* --- NO MORE VIC2 REGS FROM HERE --- */ CASE_VIC_3_4(0x30): - memory_set_vic3_rom_mapping(data); + memory_write_d030(data); check_if_rom_palette(!(data & 4)); break; CASE_VIC_3_4(0x31): @@ -1360,7 +1359,7 @@ static XEMU_INLINE Uint8 *get_charset_effective_addr ( void ) // However it seems even MEGA65 does not support this. // FIXME: how we can be sure, there won't be any out-of-bound access for the relative small WOM then? if (!REG_BMM && (addr == 0x1000 || addr == 0x9000 || addr == 0x1800 || addr == 0x9800)) - return char_wom + (addr & 0xFFF); + return char_ram + (addr & 0xFFF); // FIXME XXX this is a fixed constant for checking. if (XEMU_UNLIKELY(addr > 0x60000)) // this is valid since we still have got some extra unused RAM left to go beyond actual RAM while bulding the frame return main_ram + 0x60000; // give some unused ram array of emulaton, thus whatever high value set by user as ADDR, won't overflow during the frame @@ -1728,8 +1727,8 @@ int vic4_snapshot_load_state ( const struct xemu_snapshot_definition_st *def, st memcpy(vic_palette_bytes_green, buffer + 0x100 + NO_OF_PALETTE_REGS, NO_OF_PALETTE_REGS); memcpy(vic_palette_bytes_blue, buffer + 0x100 + 2 * NO_OF_PALETTE_REGS, NO_OF_PALETTE_REGS); vic4_revalidate_all_palette(); - vic_iomode = buffer[0]; - DEBUG("SNAP: VIC4: changing I/O mode to %d(%s)" NL, vic_iomode, iomode_names[vic_iomode]); + io_mode = buffer[0]; + DEBUG("SNAP: VIC4: changing I/O mode to %d(%s)" NL, io_mode, iomode_names[io_mode]); interrupt_status = (int)P_AS_BE32(buffer + 1); return 0; } @@ -1747,7 +1746,7 @@ int vic4_snapshot_save_state ( const struct xemu_snapshot_definition_st *def ) memcpy(buffer + 0x100 , vic_palette_bytes_red, NO_OF_PALETTE_REGS); memcpy(buffer + 0x100 + NO_OF_PALETTE_REGS, vic_palette_bytes_green, NO_OF_PALETTE_REGS); memcpy(buffer + 0x100 + 2 * NO_OF_PALETTE_REGS, vic_palette_bytes_blue, NO_OF_PALETTE_REGS); - buffer[0] = vic_iomode; + buffer[0] = io_mode; U32_AS_BE(buffer + 1, interrupt_status); return xemusnap_write_sub_block(buffer, sizeof buffer); } diff --git a/targets/mega65/vic4.h b/targets/mega65/vic4.h index 8b5b3061..d4aa242a 100644 --- a/targets/mega65/vic4.h +++ b/targets/mega65/vic4.h @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2023 LGB (Gábor Lénárt) + Copyright (C)2016-2024 LGB (Gábor Lénárt) Copyright (C)2020-2022 Hernán Di Pietro This program is free software; you can redistribute it and/or modify @@ -30,7 +30,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define VIC4_IOMODE 3 // bit1 of IO-mode is set: either VIC4ETH_IOMODE or VIC4_IOMODE -#define VIC4_LIKE_IO_MODE() (vic_iomode & 2U) +#define VIC4_LIKE_IO_MODE() (io_mode & 2U) // Horizontal sync frequencies (in Hertz) for NTSC and PAL video output of MEGA65. Must be float. #define PAL_LINE_FREQ 31250.0 @@ -239,7 +239,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // Current state -extern unsigned int vic_iomode; //extern int scanline; extern Uint8 vic_registers[]; extern Uint8 c128_d030_reg; From 5951ac324a23145009233f45e7e94d113d408753 Mon Sep 17 00:00:00 2001 From: RetroCogs <45956643+RetroCogs@users.noreply.github.com> Date: Mon, 27 May 2024 03:23:52 -0700 Subject: [PATCH 3/8] Add support for RRB feature 796 --- targets/mega65/vic4.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index 71088dd6..e123c42a 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -1397,7 +1397,7 @@ static XEMU_INLINE void vic4_render_char_raster ( void ) current_pixel += (CHARGEN_X_START - border_x_left); xcounter += (CHARGEN_X_START - border_x_left); const int xcounter_start = xcounter; - Uint8 char_fetch_offset = 0; + Sint8 char_fetch_offset = 0; // Chargen starts here. while (line_char_index < REG_CHRCOUNT) { Uint16 color_data = colour_ram[(colour_ram_current_addr++) & 0x07FFF]; @@ -1433,7 +1433,10 @@ static XEMU_INLINE void vic4_render_char_raster ( void ) current_pixel = pixel_raster_start + xcounter; // ---- End of the GOTOX re-positioning functionality implementation ---- line_char_index++; - char_fetch_offset = char_value >> 13; + char_fetch_offset = (char_value >> 13) & 7; + // If ScreenRAMByte1 bit 4 is set then the char_fetch_offset should be subtracted and not added + if (char_value & (1 << 12)) + char_fetch_offset = -char_fetch_offset; if (SXA_VERTICAL_FLIP(color_data)) enable_bg_paint = 0; if (SXA_ATTR_BOLD(color_data) && SXA_ATTR_REVERSE(color_data) && !REG_VICIII_ATTRIBS) From ec2504defc208687b106fdc39e18e87b16396c4b Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 12 Jun 2024 14:06:37 +0200 Subject: [PATCH 4/8] MEGA65: DMA debug fix/optimization #198 Small fix for more sane debug output when DO_DEBUG_DMA is defined for some extra DMA information. Nothing here which should affect an average user, only in a very unlikely scenario: One limitation of DMA emulation in Xemu: enhanced mode option list is read in a blockling fashion, thus an "unended" list will stall the emulator (though it's unlikely not to meet a zero byte sooner or later in the memory). To avoid this (and also warn the user about something fishy is going on) there is a warning window in that case - as the number of enhanced mode options are limited, it's simply insane to have a very large option list, thus it's a certain sign of some bug in the software which produces this: better the user to know. --- targets/mega65/dma65.c | 49 ++++++++++++++++++++++++++---------------- targets/mega65/dma65.h | 2 +- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/targets/mega65/dma65.c b/targets/mega65/dma65.c index 053bbfdb..6788eec1 100644 --- a/targets/mega65/dma65.c +++ b/targets/mega65/dma65.c @@ -33,7 +33,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ # define DEBUGDMA(...) DEBUG(__VA_ARGS__) #endif -int in_dma; // DMA session is in progress if non-zero. Also used by the main emu loop to tell if it needs to call DMA or CPU emu. +Uint8 in_dma; // DMA session is in progress if non-zero. Also used by the main emu loop to tell if it needs to call DMA or CPU emu. // Hacky stuff: // low byte: the transparent byte value @@ -62,6 +62,7 @@ static Uint8 minterms[4]; // Used with MIX DMA command only static Uint8 filler_byte; // byte used for FILL DMA command only static int enhanced_mode; // MEGA65 enhanced mode DMA static int with_io; // legacy MEGA65 stuff, should be removed? 0x80 or 0 +static unsigned int list_entry_pos = 0; // On C65, DMA cannot cross 64K boundaries, so the right mask is 0xFFFF // On MEGA65 it seems to be 1Mbyte, thus the mask should be 0xFFFFF @@ -146,10 +147,6 @@ static XEMU_INLINE void dma_write_target ( const Uint8 data ) } -#ifdef DO_DEBUG_DMA -static int list_entry_pos = 0; -#endif - static Uint8 dma_read_list_next_byte ( void ) { Uint8 data; @@ -162,8 +159,8 @@ static Uint8 dma_read_list_next_byte ( void ) } #ifdef DO_DEBUG_DMA DEBUGPRINT("DMA: reading DMA (rev#%d) list from $%08X [%s] [#%d]: $%02X" NL, session_revision, list_addr, list_addr_policy ? "CPU-addr" : "linear-addr", list_entry_pos, data); - list_entry_pos++; #endif + list_entry_pos++; list_addr++; return data; } @@ -219,9 +216,9 @@ void dma_write_reg ( int addr, Uint8 data ) if (XEMU_UNLIKELY(in_dma)) { // this is just an emergency stuff to disallow DMA to update its own registers ... FIXME: what would be the correct policy? // NOTE: without this, issuing a DMA transfer updating DMA registers would affect an on-going DMA transfer! - static int do_warn = 1; + static bool do_warn = true; if (do_warn) { - do_warn = 0; + do_warn = false; ERROR_WINDOW("DMA writes its own registers, ignoring!\nThere will be no more warning on this!"); } DEBUG("DMA: WARNING: tries to write own register by DMA reg#%d with value of $%02X" NL, addr, data); @@ -321,7 +318,7 @@ int dma_update ( void ) { int cycles = 0; if (XEMU_UNLIKELY(!in_dma)) - FATAL("dma_update() called with no in_dma set!"); + FATAL("dma_update() called without in_dma being set!"); if (XEMU_UNLIKELY(command == -1)) { if (XEMU_UNLIKELY(list_addr_policy == 3)) { list_addr = cpu65.pc; @@ -329,20 +326,23 @@ int dma_update ( void ) list_addr_policy = 2; } if (enhanced_mode) { - Uint8 opt, optval; - do { - opt = dma_read_list_next_byte(); + list_entry_pos = 0; + for (;;) { + const Uint8 opt = dma_read_list_next_byte(); DEBUGDMA("DMA: enhanced option byte $%02X read" NL, opt); cycles++; + if (!opt) { + DEBUGDMA("DMA: end of enhanced options" NL); + break; + } + Uint8 optval; if ((opt & 0x80)) { // all options >= 0x80 have an extra bytes as option parameter optval = dma_read_list_next_byte(); DEBUGDMA("DMA: enhanced option byte parameter $%02X read" NL, optval); cycles++; } switch (opt) { - case 0x00: - DEBUGDMA("DMA: end of enhanced options" NL); - break; + // case 0x00 (end of enhanced option list) is already handled case 0x06: // disable transparency (setting high byte of transparency, thus will never match) transparency |= 0x100; break; @@ -386,14 +386,25 @@ int dma_update ( void ) DEBUGPRINT("DMA: *unknown* enhanced option: $%02X @ PC=$%04X" NL, opt, cpu65.pc); break; } - } while (opt); + if (XEMU_UNLIKELY(list_entry_pos > 255)) { + // FIXME: current design of DMA emulation uses a blocking loop to fetch enhanced mode options + // This is bad, if there is a very long list (which shouldn't be valid anyway though ...) + // Thus I abort the whole DMA session in case of a problem like that. + static bool do_warn = true; + if (do_warn) { + do_warn = false; + ERROR_WINDOW("DMA: Enhanced mode DMA option list is abnormally long (%u bytes).\nAborting DMA session! Buggy software running?\nNo more reports will be produced by Xemu." NL, list_entry_pos); + } + in_dma = 0; + command = -1; + return cycles; + } + } } + list_entry_pos = 0; // command == -1 signals the situation, that the (next) DMA command should be read! // This part is highly incorrect, ie fetching so many bytes in one step only of dma_update() // NOTE: in case of MEGA65: dma_read_list_next_byte() uses the "megabyte" part already (taken from reg#4, in case if that reg is written) -#ifdef DO_DEBUG_DMA - list_entry_pos = 0; -#endif command = dma_read_list_next_byte(); dma_op = (enum dma_op_types)(command & 3); modulo.col_limit = dma_read_list_next_byte(); diff --git a/targets/mega65/dma65.h b/targets/mega65/dma65.h index 8eb89e9d..7ff76cd8 100644 --- a/targets/mega65/dma65.h +++ b/targets/mega65/dma65.h @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef XEMU_MEGA65_DMA_H_INCLUDED #define XEMU_MEGA65_DMA_H_INCLUDED -extern int in_dma; +extern Uint8 in_dma; extern void dma_write_reg ( int addr, Uint8 data ); extern Uint8 dma_read_reg ( int reg ); From ab2290e79f2dbc19dbf815a26f057ab42ba1802c Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 12 Jun 2024 14:12:25 +0200 Subject: [PATCH 5/8] MEGA65: fix alt.palette selection #402 Reported/suggested by Mirage_BD on Discord. Thanks!! --- targets/mega65/vic4.c | 2 +- targets/mega65/vic4.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index e123c42a..b651b5dd 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -1473,7 +1473,7 @@ static XEMU_INLINE void vic4_render_char_raster ( void ) } else if (CHAR_IS256_COLOR(char_id)) { // 256-color character // fgcolor in case of FCM should mean colour index $FF // FIXME: check if the passed palette[color_data & 0xFF] is correct or another index should be used for that $FF colour stuff - const Uint32 *palette_now = SXA_ATTR_ALTPALETTE(color_data) ? altpalette : used_palette; + const Uint32 *palette_now = ((REG_VICIII_ATTRIBS) && SXA_ATTR_ALTPALETTE(color_data)) ? altpalette : used_palette; vic4_render_fullcolor_char_row( main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8)) & 0x7FFFF), 8 - glyph_trim, diff --git a/targets/mega65/vic4.h b/targets/mega65/vic4.h index d4aa242a..e6b892b8 100644 --- a/targets/mega65/vic4.h +++ b/targets/mega65/vic4.h @@ -189,7 +189,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SXA_ATTR_BOLD(cw) ((cw) & 0x0040) #define SXA_ATTR_REVERSE(cw) ((cw) & 0x0020) // BOLD+REVERSE = alternate palette -#define SXA_ATTR_ALTPALETTE(cw) ((cw) & 0x0060) +#define SXA_ATTR_ALTPALETTE(cw) (((cw) & 0x0060) == 0x60) //#define SXA_TRIM_TOP_BOTTOM(cw) (((cw) & 0x0300) >> 8) From 6558f7234d17195569585514034adce0fcde72eb Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Tue, 18 Jun 2024 09:59:35 +0200 Subject: [PATCH 6/8] MEGA65: cleanup, remove snapshot related stuff Emulator snapshots for the MEGA65 emulators are disabled since years for now, and wouldn't even work for multiple reasons in this very form. So it's fine to remove the "considered as dead" code. Even if I want this to work some day, a serious total rewrite would be needed. --- targets/mega65/Makefile | 6 +-- targets/mega65/configdb.c | 4 -- targets/mega65/configdb.h | 4 -- targets/mega65/dma65.c | 55 -------------------- targets/mega65/dma65.h | 6 --- targets/mega65/ethernet65.c | 1 - targets/mega65/io_mapper.c | 2 +- targets/mega65/m65_snapshot.c | 98 ----------------------------------- targets/mega65/m65_snapshot.h | 34 ------------ targets/mega65/mega65.c | 98 ----------------------------------- targets/mega65/sdcard.c | 52 ------------------- targets/mega65/sdcard.h | 6 --- targets/mega65/vic4.c | 53 ------------------- targets/mega65/vic4.h | 6 --- targets/mega65/xemu-target.h | 3 +- 15 files changed, 5 insertions(+), 423 deletions(-) delete mode 100644 targets/mega65/m65_snapshot.c delete mode 100644 targets/mega65/m65_snapshot.h diff --git a/targets/mega65/Makefile b/targets/mega65/Makefile index fefd179a..120c9336 100644 --- a/targets/mega65/Makefile +++ b/targets/mega65/Makefile @@ -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 @@ -20,8 +20,8 @@ TARGET = mega65 PRG_TARGET = xmega65 EMU_DESCRIPTION = MEGA65 -SRCS_TARGET_xmega65 = configdb.c mega65.c sdcard.c uart_monitor.c hypervisor.c m65_snapshot.c memory_mapper.c io_mapper.c vic4.c vic4_palette.c ethernet65.c input_devices.c memcontent.c ui.c fat32.c sdcontent.c audio65.c inject.c dma65.c rom.c hdos.c matrix_mode.c cart.c -SRCS_COMMON_xmega65 = emutools.c cpu65.c cia6526.c emutools_hid.c sid.c f011_core.c c64_kbd_mapping.c emutools_config.c emutools_snapshot.c emutools_files.c emutools_umon.c emutools_socketapi.c ethertap.c d81access.c emutools_gui.c basic_text.c opl3.c lodepng.c compressed_disk_image.c cpu65_disasm.c +SRCS_TARGET_xmega65 = configdb.c mega65.c sdcard.c uart_monitor.c hypervisor.c memory_mapper.c io_mapper.c vic4.c vic4_palette.c ethernet65.c input_devices.c memcontent.c ui.c fat32.c sdcontent.c audio65.c inject.c dma65.c rom.c hdos.c matrix_mode.c cart.c +SRCS_COMMON_xmega65 = emutools.c cpu65.c cia6526.c emutools_hid.c sid.c f011_core.c c64_kbd_mapping.c emutools_config.c emutools_files.c emutools_umon.c emutools_socketapi.c ethertap.c d81access.c emutools_gui.c basic_text.c opl3.c lodepng.c compressed_disk_image.c cpu65_disasm.c CFLAGS_TARGET_xmega65 = $(SDL2_CFLAGS) $(MATH_CFLAGS) $(SOCKET_CFLAGS) $(XEMUGUI_CFLAGS) LDFLAGS_TARGET_xmega65 = $(SDL2_LIBS) $(MATH_LIBS) $(SOCKET_LIBS) $(XEMUGUI_LIBS) LDFLAGS_TARGET_xmega65_ON_html = -s STACK_SIZE=655360 --preload-file=$$HOME/.local/share/xemu-lgb/mega65/mega65.img.compressed3@/files/mega65.img --preload-file=$$HOME/mega65/megapoly.d81@/files/files/hdos/mega65.d81 diff --git a/targets/mega65/configdb.c b/targets/mega65/configdb.c index 31dff606..449a9e4d 100644 --- a/targets/mega65/configdb.c +++ b/targets/mega65/configdb.c @@ -57,10 +57,6 @@ static const struct xemutools_configdef_str_st str_options[] = { { "dumpmem", NULL, "Save memory content on exit", &configdb.dumpmem }, { "dumpscreen", NULL, "Save screen content (ASCII) on exit", &configdb.dumpscreen }, { "screenshot", NULL, "Save screenshot (PNG) on exit and vice-versa (for testing!)", &configdb.screenshot_and_exit }, -#ifdef XEMU_SNAPSHOT_SUPPORT - { "snapload", NULL, "Load a snapshot from the given file", &configdb.snapload }, - { "snapsave", NULL, "Save a snapshot into the given file before Xemu would exit", &configdb.snapsave }, -#endif #ifdef HAS_UARTMON_SUPPORT { "uartmon", NULL, "Sets the name for named unix-domain socket for uartmon, otherwise disabled", &configdb.uartmon }, #endif diff --git a/targets/mega65/configdb.h b/targets/mega65/configdb.h index 23738899..89cef071 100644 --- a/targets/mega65/configdb.h +++ b/targets/mega65/configdb.h @@ -54,10 +54,6 @@ struct configdb_st { char *dumpmem; char *dumpscreen; char *screenshot_and_exit; -#ifdef XEMU_SNAPSHOT_SUPPORT - char *snapload; - char *snapsave; -#endif #ifdef HAS_UARTMON_SUPPORT char *uartmon; #endif diff --git a/targets/mega65/dma65.c b/targets/mega65/dma65.c index 6788eec1..c8191dfb 100644 --- a/targets/mega65/dma65.c +++ b/targets/mega65/dma65.c @@ -665,58 +665,3 @@ void dma_set_list_addr_from_bytes ( const Uint8 *p ) list_addr = p[0] + (p[1] << 8) + (p[2] << 16) + ((p[3] & 0x0F) << 24); DEBUGDMA("DMA: list address is set 'externally' to $%X" NL, list_addr); } - -/* --- SNAPSHOT RELATED --- */ - -#ifdef XEMU_SNAPSHOT_SUPPORT - -// Note: currently state is not saved "within" a DMA operation. It's only a problem, if a DMA -// operation is not handled fully here, but implemented as an iterating update method from the -// emulator code. FIXME. - -#include - -#define SNAPSHOT_DMA_BLOCK_VERSION 2 -#define SNAPSHOT_DMA_BLOCK_SIZE 0x100 - - -int dma_snapshot_load_state ( const struct xemu_snapshot_definition_st *def, struct xemu_snapshot_block_st *block ) -{ - Uint8 buffer[SNAPSHOT_DMA_BLOCK_SIZE]; - int a; - if (block->block_version != SNAPSHOT_DMA_BLOCK_VERSION || block->sub_counter || block->sub_size != sizeof buffer) - RETURN_XSNAPERR_USER("Bad C65 block syntax"); - a = xemusnap_read_file(buffer, sizeof buffer); - if (a) return a; - /* loading state ... */ - memcpy(dma_registers, buffer, sizeof dma_registers); - dma_chip_revision = buffer[0x80]; - dma_chip_initial_revision = buffer[0x81]; - //dma_chip_revision_is_dynamic = buffer[0x82]; - modulo.enabled = buffer[0x83]; - dma_status = buffer[0x84]; - in_dma_update = buffer[0x85]; - return 0; -} - - -int dma_snapshot_save_state ( const struct xemu_snapshot_definition_st *def ) -{ - Uint8 buffer[SNAPSHOT_DMA_BLOCK_SIZE]; - int a = xemusnap_write_block_header(def->idstr, SNAPSHOT_DMA_BLOCK_VERSION); - if (a) return a; - memset(buffer, 0xFF, sizeof buffer); - /* saving state ... */ - memcpy(buffer, dma_registers, sizeof dma_registers); - buffer[0x80] = dma_chip_revision; - buffer[0x81] = dma_chip_initial_revision; - //buffer[0x82] = dma_chip_revision_is_dynamic ? 1 : 0; - buffer[0x83] = modulo.enabled ? 1 : 0; - buffer[0x84] = dma_status; // bit useless to store (see below, actually it's a problem), but to think about the future ... - buffer[0x85] = in_dma_update ? 1 : 0; // -- "" -- - if (dma_status) - WARNING_WINDOW("f018_core DMA snapshot save: snapshot with DMA pending! Snapshot WILL BE incorrect on loading! FIXME!"); // FIXME! - return xemusnap_write_sub_block(buffer, sizeof buffer); -} - -#endif diff --git a/targets/mega65/dma65.h b/targets/mega65/dma65.h index 7ff76cd8..f44d1bbb 100644 --- a/targets/mega65/dma65.h +++ b/targets/mega65/dma65.h @@ -32,10 +32,4 @@ extern int dma_get_revision ( void ); extern void dma_get_list_addr_as_bytes ( Uint8 *p ); extern void dma_set_list_addr_from_bytes ( const Uint8 *p ); -#ifdef XEMU_SNAPSHOT_SUPPORT -#include "xemu/emutools_snapshot.h" -extern int dma_snapshot_load_state ( const struct xemu_snapshot_definition_st *def, struct xemu_snapshot_block_st *block ); -extern int dma_snapshot_save_state ( const struct xemu_snapshot_definition_st *def ); -#endif - #endif diff --git a/targets/mega65/ethernet65.c b/targets/mega65/ethernet65.c index 34709206..31680780 100644 --- a/targets/mega65/ethernet65.c +++ b/targets/mega65/ethernet65.c @@ -36,7 +36,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ would result in quite slow emulation :( * FIXME: it uses thread, and it's horrible code!! there are tons of race conditions, etc ... It's not so nice work to use threads, but it would be hard to do otherwise, sadly. - * FIXME FIXME FIXME: No support for snapshotting ... * Maybe allow TUN devices to be supported. It does not handle ethernet level stuffs, no source and target MAC for example :-O But TUN devices are supported by wider range of OSes (eg: OSX) and we probably can emulate the missing ethernet level stuffs somehow in this source on RX (and chop diff --git a/targets/mega65/io_mapper.c b/targets/mega65/io_mapper.c index eec39773..2d8947a3 100644 --- a/targets/mega65/io_mapper.c +++ b/targets/mega65/io_mapper.c @@ -36,7 +36,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ int fpga_switches = 0; // State of FPGA board switches (bits 0 - 15), set switch 12 (hypervisor serial output) Uint8 D6XX_registers[0x100]; // mega65 specific D6XX range, excluding the UART part (not used here!) -Uint8 D7XX[0x100]; // FIXME: hack for future M65 stuffs like ALU! FIXME: no snapshot on these! +Uint8 D7XX[0x100]; // FIXME: hack for future M65 stuffs like ALU! struct Cia6526 cia1, cia2; // CIA emulation structures for the two CIAs int cpu_mega65_opcodes = 0; // used by the CPU emu as well! static int bigmult_valid_result = 0; diff --git a/targets/mega65/m65_snapshot.c b/targets/mega65/m65_snapshot.c deleted file mode 100644 index 6fb260cb..00000000 --- a/targets/mega65/m65_snapshot.c +++ /dev/null @@ -1,98 +0,0 @@ -/* 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,2017,2021 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 -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#ifdef XEMU_SNAPSHOT_SUPPORT - -#define NEED_SID_H - -#include "xemu/emutools.h" -#include "xemu/emutools_snapshot.h" -#include "xemu/emutools_config.h" -#include "mega65.h" -#include "xemu/cpu65.h" -#include "xemu/cia6526.h" -#include "vic4.h" -#include "dma65.h" -#include "hypervisor.h" -#include "sdcard.h" -#include "xemu/f011_core.h" -#include "m65_snapshot.h" -#include "memory_mapper.h" -#include "audio65.h" -#include "io_mapper.h" -#include - -#define M65_MEMORY_BLOCK_VERSION 1 - -struct memblock_st { - Uint8 *data; - int size; -}; - - -static int snapcallback_memory_loader ( const struct xemu_snapshot_definition_st *def, struct xemu_snapshot_block_st *block ) -{ - const struct memblock_st *mem = (const struct memblock_st *)def->user_data; - if (block->block_version != M65_MEMORY_BLOCK_VERSION || block->sub_counter != 0 || block->sub_size > mem->size) - RETURN_XSNAPERR_USER("Bad memory block syntax @ %s", def->idstr); - memset(mem->data, 0xFF, mem->size); - return xemusnap_read_file(mem->data, block->sub_size); // read that damn memory dump -} - - -static int snapcallback_memory_saver ( const struct xemu_snapshot_definition_st *def ) -{ - const struct memblock_st *mem = (const struct memblock_st *)def->user_data; - int ret = xemusnap_write_block_header(def->idstr, M65_MEMORY_BLOCK_VERSION); - if (ret) return ret; - ret = mem->size - 1; - while (ret && mem->data[ret] == 0xFF) - ret--; - return xemusnap_write_sub_block(mem->data, ret + 1); -} - - -#define DEFINE_SNAPSHOT_MEMORY_BLOCK(name, structure) { "MemoryRegion:" name, (void*)&structure, snapcallback_memory_loader, snapcallback_memory_saver } - - -static const struct memblock_st memblock_main_ram = { main_ram, sizeof main_ram }; -static const struct memblock_st memblock_colour_ram = { colour_ram, sizeof colour_ram }; -static const struct memblock_st memblock_char_wom = { char_wom, sizeof char_wom }; -static const struct memblock_st memblock_hypervisor = { hypervisor_ram, 0x4000 }; - -const struct xemu_snapshot_definition_st m65_snapshot_definition[] = { - { "CPU", NULL, cpu65_snapshot_load_state, cpu65_snapshot_save_state }, - { "CIA#1", &cia1, cia_snapshot_load_state, cia_snapshot_save_state }, - { "CIA#2", &cia2, cia_snapshot_load_state, cia_snapshot_save_state }, - { "VIC-4", NULL, vic4_snapshot_load_state, vic4_snapshot_save_state }, - { "M65", NULL, m65emu_snapshot_load_state, m65emu_snapshot_save_state }, - { "SID#1", &sid[0], sid_snapshot_load_state, sid_snapshot_save_state }, - { "SID#2", &sid[1], sid_snapshot_load_state, sid_snapshot_save_state }, - { "SID#3", &sid[3], sid_snapshot_load_state, sid_snapshot_save_state }, - { "SID#4", &sid[4], sid_snapshot_load_state, sid_snapshot_save_state }, - { "DMAgic", NULL, dma_snapshot_load_state, dma_snapshot_save_state }, - { "SDcard", NULL, sdcard_snapshot_load_state, sdcard_snapshot_save_state }, - { "FDC-F011", NULL, fdc_snapshot_load_state, fdc_snapshot_save_state }, - DEFINE_SNAPSHOT_MEMORY_BLOCK("RAM:Main", memblock_main_ram), - DEFINE_SNAPSHOT_MEMORY_BLOCK("RAM:Colour", memblock_colour_ram), - DEFINE_SNAPSHOT_MEMORY_BLOCK("WOM:Char", memblock_char_wom), - DEFINE_SNAPSHOT_MEMORY_BLOCK("RAM:Hyppo", memblock_hypervisor), - { NULL, NULL, m65emu_snapshot_loading_finalize, NULL } -}; - -#endif diff --git a/targets/mega65/m65_snapshot.h b/targets/mega65/m65_snapshot.h deleted file mode 100644 index 0c5910bb..00000000 --- a/targets/mega65/m65_snapshot.h +++ /dev/null @@ -1,34 +0,0 @@ -/* 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,2017,2021 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 -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#ifndef XEMU_MEGA65_SNAPSHOT_H_INCLUDED -#define XEMU_MEGA65_SNAPSHOT_H_INCLUDED - -#ifdef XEMU_SNAPSHOT_SUPPORT -#include "xemu/emutools_snapshot.h" - -// From other modules ... -extern int m65emu_snapshot_load_state ( const struct xemu_snapshot_definition_st *def, struct xemu_snapshot_block_st *block ); -extern int m65emu_snapshot_save_state ( const struct xemu_snapshot_definition_st *def ); -extern int m65emu_snapshot_loading_finalize ( const struct xemu_snapshot_definition_st *def, struct xemu_snapshot_block_st *block ); - -// From our .c file -extern const struct xemu_snapshot_definition_st m65_snapshot_definition[]; - -#endif -#endif diff --git a/targets/mega65/mega65.c b/targets/mega65/mega65.c index 426eded0..08525218 100644 --- a/targets/mega65/mega65.c +++ b/targets/mega65/mega65.c @@ -30,7 +30,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "xemu/c64_kbd_mapping.h" #include "xemu/emutools_config.h" #include "xemu/emutools_umon.h" -#include "m65_snapshot.h" #include "memory_mapper.h" #include "io_mapper.h" #include "ethernet65.h" @@ -253,19 +252,6 @@ static Uint8 cia2_in_b ( void ) } -#ifdef XEMU_SNAPSHOT_SUPPORT -static void m65_snapshot_saver_on_exit_callback ( void ) -{ - if (!configdb.snapsave) - return; - if (xemusnap_save(configdb.snapsave)) - ERROR_WINDOW("Could not save snapshot \"%s\": %s", configdb.snapsave, xemusnap_error_buffer); - else - INFO_WINDOW("Snapshot has been saved to \"%s\".", configdb.snapsave); -} -#endif - - static int preinit_memory_item ( const char *name, const char *desc, Uint8 *target_ptr, const Uint8 *source_ptr, const int source_size, const int min_size, const int max_size, const char *fn ) { if (source_size < min_size || source_size > max_size || min_size > max_size) @@ -443,14 +429,6 @@ static void mega65_init ( void ) if (configdb.useutilmenu) hwa_kbd_set_fake_key(0x20); DEBUG("INIT: end of initialization!" NL); -#ifdef XEMU_SNAPSHOT_SUPPORT - xemusnap_init(m65_snapshot_definition); - if (configdb.snapload) { - if (xemusnap_load(configdb.snapload)) - FATAL("Couldn't load snapshot \"%s\": %s", configdb.snapload, xemusnap_error_buffer); - } - atexit(m65_snapshot_saver_on_exit_callback); -#endif } @@ -908,79 +886,3 @@ int main ( int argc, char **argv ) XEMU_MAIN_LOOP(emulation_loop, 25, 1); return 0; } - -/* --- SNAPSHOT RELATED --- */ - -#ifdef XEMU_SNAPSHOT_SUPPORT - -#include - -#define SNAPSHOT_M65_BLOCK_VERSION 2 -#define SNAPSHOT_M65_BLOCK_SIZE (0x100 + sizeof(D6XX_registers) + sizeof(D7XX)) - - -int m65emu_snapshot_load_state ( const struct xemu_snapshot_definition_st *def, struct xemu_snapshot_block_st *block ) -{ - Uint8 buffer[SNAPSHOT_M65_BLOCK_SIZE]; - int a; - if (block->block_version != SNAPSHOT_M65_BLOCK_VERSION || block->sub_counter || block->sub_size != sizeof buffer) - RETURN_XSNAPERR_USER("Bad M65 block syntax"); - a = xemusnap_read_file(buffer, sizeof buffer); - if (a) return a; - /* loading state ... */ - memcpy(D6XX_registers, buffer + 0x100, sizeof D6XX_registers); - memcpy(D7XX, buffer + 0x200, sizeof D7XX); - in_hypervisor = 1; // simulate hypervisor mode, to allow to write some regs now instead of causing a TRAP now ... - io_write(0x367D, D6XX_registers[0x7D]); // write $(D)67D in VIC-IV I/O mode! (sets ROM protection, linear addressing mode enable ...) - // TODO FIXME: see if there is a need for other registers from D6XX_registers to write back to take effect on loading snapshot! - // end of spec, hypervisor-needed faked mode for loading snapshot ... - map_mask = (int)P_AS_BE32(buffer + 0); - map_offset_low = (int)P_AS_BE32(buffer + 4); - map_offset_high = (int)P_AS_BE32(buffer + 8); - cpu65.cpu_inhibit_interrupts = (int)P_AS_BE32(buffer + 12); - in_hypervisor = (int)P_AS_BE32(buffer + 16); // sets hypervisor state from snapshot (hypervisor/userspace) - map_megabyte_low = (int)P_AS_BE32(buffer + 20); - map_megabyte_high = (int)P_AS_BE32(buffer + 24); - //force_fast_loaded = (int)P_AS_BE32(buffer + 28); // activated in m65emu_snapshot_loading_finalize() as force_fast can be set at multiple places through loading snapshot! - // +32 is free for 4 bytes now ... can be used later - memory_set_cpu_io_port_ddr_and_data(buffer[36], buffer[37]); - return 0; -} - - -int m65emu_snapshot_save_state ( const struct xemu_snapshot_definition_st *def ) -{ - Uint8 buffer[SNAPSHOT_M65_BLOCK_SIZE]; - int a = xemusnap_write_block_header(def->idstr, SNAPSHOT_M65_BLOCK_VERSION); - if (a) return a; - memset(buffer, 0xFF, sizeof buffer); - /* saving state ... */ - U32_AS_BE(buffer + 0, map_mask); - U32_AS_BE(buffer + 4, map_offset_low); - U32_AS_BE(buffer + 8, map_offset_high); - U32_AS_BE(buffer + 12, cpu65.cpu_inhibit_interrupts); - U32_AS_BE(buffer + 16, in_hypervisor); - U32_AS_BE(buffer + 20, map_megabyte_low); - U32_AS_BE(buffer + 24, map_megabyte_high); - //U32_AS_BE(buffer + 28, force_fast); // see notes on this at load_state and finalize stuff! - // +32 is free for 4 bytes now ... can be used later - buffer[36] = memory_get_cpu_io_port(0); - buffer[37] = memory_get_cpu_io_port(1); - memcpy(buffer + 0x100, D6XX_registers, sizeof D6XX_registers); - memcpy(buffer + 0x200, D7XX, sizeof D7XX); - return xemusnap_write_sub_block(buffer, sizeof buffer); -} - - -int m65emu_snapshot_loading_finalize ( const struct xemu_snapshot_definition_st *def, struct xemu_snapshot_block_st *block ) -{ - DEBUGPRINT("SNAP: loaded (finalize-callback: begin)" NL); - memory_set_vic3_rom_mapping(vic_registers[0x30]); - memory_set_do_map(); - //force_fast = force_fast_loaded; // force_fast is handled through different places, so we must have a "finalize" construct and saved separately to have the actual effect ... - machine_set_speed(1); - DEBUGPRINT("SNAP: loaded (finalize-callback: end)" NL); - OSD(-1, -1, "Snapshot has been loaded."); - return 0; -} -#endif diff --git a/targets/mega65/sdcard.c b/targets/mega65/sdcard.c index 1caeee2e..2823b6c7 100644 --- a/targets/mega65/sdcard.c +++ b/targets/mega65/sdcard.c @@ -1262,55 +1262,3 @@ Uint8 sdcard_read_register ( const int reg ) } return data; } - - -/* --- SNAPSHOT RELATED --- */ - - -#ifdef XEMU_SNAPSHOT_SUPPORT - -#include - -#define SNAPSHOT_SDCARD_BLOCK_VERSION 0 -#define SNAPSHOT_SDCARD_BLOCK_SIZE (0x100 + sizeof(disk_buffers)) - -int sdcard_snapshot_load_state ( const struct xemu_snapshot_definition_st *def, struct xemu_snapshot_block_st *block ) -{ - Uint8 buffer[SNAPSHOT_SDCARD_BLOCK_SIZE]; - int a; - if (block->block_version != SNAPSHOT_SDCARD_BLOCK_VERSION || block->sub_counter || block->sub_size != sizeof buffer) - RETURN_XSNAPERR_USER("Bad SD-Card block syntax"); - a = xemusnap_read_file(buffer, sizeof buffer); - if (a) return a; - /* loading state ... */ - memcpy(sd_sector_registers, buffer, 4); - memcpy(sd_d81_img1_start, buffer + 4, 4); - fd_mounted = (int)P_AS_BE32(buffer + 8); - sd_is_read_only = (int)P_AS_BE32(buffer + 16); - //d81_is_read_only = (int)P_AS_BE32(buffer + 20); - //use_d81 = (int)P_AS_BE32(buffer + 24); - sd_status = buffer[0xFF]; - memcpy(disk_buffers, buffer + 0x100, sizeof disk_buffers); - return 0; -} - - -int sdcard_snapshot_save_state ( const struct xemu_snapshot_definition_st *def ) -{ - Uint8 buffer[SNAPSHOT_SDCARD_BLOCK_SIZE]; - int a = xemusnap_write_block_header(def->idstr, SNAPSHOT_SDCARD_BLOCK_VERSION); - if (a) return a; - memset(buffer, 0xFF, sizeof buffer); - /* saving state ... */ - memcpy(buffer, sd_sector_registers, 4); - memcpy(buffer + 4,sd_d81_img1_start, 4); - U32_AS_BE(buffer + 8, fd_mounted); - U32_AS_BE(buffer + 16, sd_is_read_only); - //U32_AS_BE(buffer + 20, d81_is_read_only); - //U32_AS_BE(buffer + 24, use_d81); - buffer[0xFF] = sd_status; - memcpy(buffer + 0x100, disk_buffers, sizeof disk_buffers); - return xemusnap_write_sub_block(buffer, sizeof buffer); -} - -#endif diff --git a/targets/mega65/sdcard.h b/targets/mega65/sdcard.h index 7da158d6..61d72cf0 100644 --- a/targets/mega65/sdcard.h +++ b/targets/mega65/sdcard.h @@ -64,10 +64,4 @@ static inline int has_block_nonzero_byte ( const Uint8 *p ) return 0; } -#ifdef XEMU_SNAPSHOT_SUPPORT -#include "xemu/emutools_snapshot.h" -extern int sdcard_snapshot_load_state ( const struct xemu_snapshot_definition_st *def , struct xemu_snapshot_block_st *block ); -extern int sdcard_snapshot_save_state ( const struct xemu_snapshot_definition_st *def ); -#endif - #endif diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index b651b5dd..eb869f39 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -1702,56 +1702,3 @@ void vic4_set_emulation_colour_effect ( int val ) vic4_revalidate_all_palette(); } } - - -/* --- SNAPSHOT RELATED --- */ - - -#ifdef XEMU_SNAPSHOT_SUPPORT - -#include - -#define SNAPSHOT_VIC4_BLOCK_VERSION 2 -#define SNAPSHOT_VIC4_BLOCK_SIZE (0x100 + ((NO_OF_PALETTE_REGS) * 3)) - -int vic4_snapshot_load_state ( const struct xemu_snapshot_definition_st *def, struct xemu_snapshot_block_st *block ) -{ - Uint8 buffer[SNAPSHOT_VIC4_BLOCK_SIZE]; - int a; - if (block->block_version != SNAPSHOT_VIC4_BLOCK_VERSION || block->sub_counter || block->sub_size != sizeof buffer) - RETURN_XSNAPERR_USER("Bad VIC4 block syntax"); - a = xemusnap_read_file(buffer, sizeof buffer); - if (a) return a; - /* loading state ... */ - for (a = 0; a < 0x80; a++) - vic_write_reg(a, buffer[a + 0x80]); - c128_d030_reg = buffer[0x7F]; - memcpy(vic_palette_bytes_red, buffer + 0x100 , NO_OF_PALETTE_REGS); - 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(); - 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; -} - - -int vic4_snapshot_save_state ( const struct xemu_snapshot_definition_st *def ) -{ - Uint8 buffer[SNAPSHOT_VIC4_BLOCK_SIZE]; - int a = xemusnap_write_block_header(def->idstr, SNAPSHOT_VIC4_BLOCK_VERSION); - if (a) return a; - memset(buffer, 0xFF, sizeof buffer); - /* saving state ... */ - memcpy(buffer + 0x80, vic_registers, 0x80); // $80 bytes - buffer[0x7F] = c128_d030_reg; - 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] = io_mode; - U32_AS_BE(buffer + 1, interrupt_status); - return xemusnap_write_sub_block(buffer, sizeof buffer); -} - -#endif diff --git a/targets/mega65/vic4.h b/targets/mega65/vic4.h index e6b892b8..dbb5a9c2 100644 --- a/targets/mega65/vic4.h +++ b/targets/mega65/vic4.h @@ -275,10 +275,4 @@ extern char *vic4_textshot ( void ); extern int vic4_textinsert ( const char *text ); extern void vic4_set_emulation_colour_effect ( int val ); -#ifdef XEMU_SNAPSHOT_SUPPORT -#include "xemu/emutools_snapshot.h" -extern int vic4_snapshot_load_state ( const struct xemu_snapshot_definition_st *def , struct xemu_snapshot_block_st *block ); -extern int vic4_snapshot_save_state ( const struct xemu_snapshot_definition_st *def ); -#endif - #endif diff --git a/targets/mega65/xemu-target.h b/targets/mega65/xemu-target.h index 8ac4eb84..2827bede 100644 --- a/targets/mega65/xemu-target.h +++ b/targets/mega65/xemu-target.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 @@ -21,7 +21,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define CPU_65CE02 #define MEGA65 #define CPU65_65CE02_6502NMOS_TIMING_EMULATION -//#define XEMU_SNAPSHOT_SUPPORT "MEGA65" #define CPU_STEP_MULTI_OPS //#define DEBUG_CPU #define CPU_CUSTOM_MEMORY_FUNCTIONS_H "cpu_custom_functions.h" From fbd314f49a152fe5e53879ee9d573f9d090e38d6 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Tue, 18 Jun 2024 10:39:05 +0200 Subject: [PATCH 7/8] MEGA65+CORE: reorg. window title dynamic update --- targets/mega65/matrix_mode.c | 2 +- targets/mega65/mega65.c | 38 +++++++++++++++++++----------------- targets/mega65/mega65.h | 2 +- targets/mega65/xemu-target.h | 5 +++++ xemu/emutools.c | 3 +++ xemu/emutools.h | 4 ++++ 6 files changed, 34 insertions(+), 20 deletions(-) diff --git a/targets/mega65/matrix_mode.c b/targets/mega65/matrix_mode.c index 687defa1..b7c557bf 100644 --- a/targets/mega65/matrix_mode.c +++ b/targets/mega65/matrix_mode.c @@ -275,7 +275,7 @@ static void dump_regs ( const char rot_fig ) in_hypervisor ? 'H' : 'U', rot_fig, videostd_id ? "NTSC" : "PAL ", - cpu_clock_speed_string, + cpu_clock_speed_string_p, (D6XX_registers[0x7D] & 16) ? '!' : ' ' ); } diff --git a/targets/mega65/mega65.c b/targets/mega65/mega65.c index 08525218..681b399f 100644 --- a/targets/mega65/mega65.c +++ b/targets/mega65/mega65.c @@ -66,12 +66,9 @@ static int trace_step_trigger = 0; #ifdef HAS_UARTMON_SUPPORT static void (*m65mon_callback)(void) = NULL; #endif -static const char emulator_paused_title[] = "TRACE/PAUSE"; static char emulator_speed_title[64] = ""; -static char fast_mhz_in_string[16] = ""; -static const char *cpu_clock_speed_strs[4] = { "1MHz", "2MHz", "3.5MHz", fast_mhz_in_string }; -const char *cpu_clock_speed_string = ""; -static unsigned int cpu_clock_speed_str_index = 0; +static char fast_mhz_as_string[16] = ""; +const char *cpu_clock_speed_string_p = ""; static unsigned int cpu_cycles_per_scanline; int cpu_cycles_per_step = 100; // some init value, will be overriden, but it must be greater initially than "only a few" anyway static Uint8 i2c_regs_original[sizeof i2c_regs]; @@ -122,36 +119,46 @@ void machine_set_speed ( int verbose ) case 4: // 100 - 1MHz case 5: // 101 - 1MHz cpu_cycles_per_scanline = (unsigned int)(videostd_1mhz_cycles_per_scanline * (float)(C64_MHZ_CLOCK)); - cpu_clock_speed_str_index = 0; + cpu_clock_speed_string_p = "1MHz"; cpu65_set_timing(0); break; case 0: // 000 - 2MHz cpu_cycles_per_scanline = (unsigned int)(videostd_1mhz_cycles_per_scanline * (float)(C128_MHZ_CLOCK)); - cpu_clock_speed_str_index = 1; + cpu_clock_speed_string_p = "2MHz"; cpu65_set_timing(0); break; case 2: // 010 - 3.5MHz case 6: // 110 - 3.5MHz cpu_cycles_per_scanline = (unsigned int)(videostd_1mhz_cycles_per_scanline * (float)(C65_MHZ_CLOCK)); - cpu_clock_speed_str_index = 2; + cpu_clock_speed_string_p = "3.5MHz"; cpu65_set_timing(1); break; case 1: // 001 - 40MHz (or Xemu specified custom speed) case 3: // 011 - -- "" -- case 7: // 111 - -- "" -- cpu_cycles_per_scanline = (unsigned int)(videostd_1mhz_cycles_per_scanline * (float)(configdb.fast_mhz)); - cpu_clock_speed_str_index = 3; + cpu_clock_speed_string_p = fast_mhz_as_string; cpu65_set_timing(2); break; } - cpu_clock_speed_string = cpu_clock_speed_strs[cpu_clock_speed_str_index]; - DEBUG("SPEED: CPU speed is set to %s, cycles per scanline: %d in %s (1MHz cycles per scanline: %f)" NL, cpu_clock_speed_string, cpu_cycles_per_scanline, videostd_name, videostd_1mhz_cycles_per_scanline); + DEBUG("SPEED: CPU speed is set to %s, cycles per scanline: %d in %s (1MHz cycles per scanline: %f)" NL, cpu_clock_speed_string_p, cpu_cycles_per_scanline, videostd_name, videostd_1mhz_cycles_per_scanline); if (cpu_cycles_per_step > 1 && !hypervisor_is_debugged && !configdb.cpusinglestep) cpu_cycles_per_step = cpu_cycles_per_scanline; // if in trace mode (or hyper-debug ...), do not set this! So set only if non-trace and non-hyper-debug } } +void window_title_pre_update_callback ( void ) +{ + static const char iomode_names[] = {'2', '3', 'E', '4'}; + snprintf(emulator_speed_title, sizeof emulator_speed_title, "%s %s (%c)", + cpu_clock_speed_string_p, videostd_name, + in_hypervisor ? 'H' : iomode_names[io_mode] + ); + window_title_custom_addon = paused ? "TRACE/PAUSE" : NULL; +} + + int mega65_set_model ( const Uint8 id ) { static int first_call = 1; @@ -418,7 +425,7 @@ static void mega65_init ( void ) #ifdef HAS_UARTMON_SUPPORT uartmon_init(configdb.uartmon); #endif - sprintf(fast_mhz_in_string, "%.2fMHz", configdb.fast_mhz); + sprintf(fast_mhz_as_string, "%.2fMHz", configdb.fast_mhz); 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) @@ -685,9 +692,6 @@ static void update_emulator ( void ) // XXX: some things has been moved here from the main loop, however update_emulator is called from other places as well, FIXME check if it causes problems or not! inject_ready_check_do(); audio65_sid_inc_framecount(); - strcpy(emulator_speed_title, cpu_clock_speed_strs[cpu_clock_speed_str_index]); - strcat(emulator_speed_title, " "); - strcat(emulator_speed_title, videostd_name); hid_handle_all_sdl_events(); xemugui_iteration(); nmi_set(IS_RESTORE_PRESSED(), 2); // Custom handling of the restore key ... @@ -747,15 +751,13 @@ static void emulation_loop ( void ) // XXX it's maybe a problem to call this!!! update_emulator() is called here which closes frame but no no reopen then ... FIXME: handle this somehow! update_emulator(); if (trace_step_trigger) { - // if monitor trigges a step, break the pause loop, however we will get back the control on the next + // if monitor triggers a step, break the pause loop, however we will get back the control on the next // iteration of the infinite "for" loop, as "paused" is not altered trace_step_trigger = 0; break; // break the pause loop now } - // Decorate window title about the mode. // If "paused" mode is switched off ie by a monitor command (called from update_emulator() above!) // then it will resets back the the original state, etc - window_title_custom_addon = paused ? (char*)emulator_paused_title : NULL; if (paused != paused_old) { paused_old = paused; if (paused) { diff --git a/targets/mega65/mega65.h b/targets/mega65/mega65.h index bfaf0586..2f7f9948 100644 --- a/targets/mega65/mega65.h +++ b/targets/mega65/mega65.h @@ -78,6 +78,6 @@ extern int dump_screen ( const char *fn ); extern Uint8 last_dd00_bits; extern const char *last_reset_type; extern int cpu_cycles_per_step; -extern const char *cpu_clock_speed_string; +extern const char *cpu_clock_speed_string_p; #endif diff --git a/targets/mega65/xemu-target.h b/targets/mega65/xemu-target.h index 2827bede..a14f4d4b 100644 --- a/targets/mega65/xemu-target.h +++ b/targets/mega65/xemu-target.h @@ -27,6 +27,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define CPU65 cpu65 //#define CPU65_DISCRETE_PF_NZ +// Forces xemu/emutools.c to have a callback on window title update: used in MEGA65 emu +// to set the status of the emulator (ie: paused, running, ...) +// FIXME: define this conditionally when umon/uartmon (?) is enabled +#define WINDOW_TITLE_PRE_UPDATE_CALLBACK window_title_pre_update_callback + // #define DO_NOT_FORCE_UNREACHABLE #define HAVE_XEMU_EXEC_API diff --git a/xemu/emutools.c b/xemu/emutools.c index 6c9db702..247db7b2 100644 --- a/xemu/emutools.c +++ b/xemu/emutools.c @@ -454,6 +454,9 @@ void xemu_timekeeping_delay ( int td_em ) td = get_elapsed_time(et_new, &et_old, &unix_time_tv); seconds_timer_trigger = (unix_time_tv.tv_sec != old_unix_time); if (seconds_timer_trigger) { +#ifdef WINDOW_TITLE_PRE_UPDATE_CALLBACK + WINDOW_TITLE_PRE_UPDATE_CALLBACK(); +#endif snprintf(window_title_buffer_end, 64, " [%d%% %d%%] %s %s", ((td_em_ALL < td_pc_ALL) && td_pc_ALL) ? td_em_ALL * 100 / td_pc_ALL : 100, td_em_ALL ? (td_pc_ALL * 100 / td_em_ALL) : -1, diff --git a/xemu/emutools.h b/xemu/emutools.h index ac041715..029083bb 100644 --- a/xemu/emutools.h +++ b/xemu/emutools.h @@ -176,6 +176,10 @@ extern void *xemu_realloc ( void *p, size_t size ); extern int xemu_is_first_time_user ( void ); +#ifdef WINDOW_TITLE_PRE_UPDATE_CALLBACK +extern void WINDOW_TITLE_PRE_UPDATE_CALLBACK ( void ); +#endif + #if !defined(XEMU_ARCH_HTML) && !defined(XEMU_CPU_ARM) #define HAVE_MM_MALLOC #endif From 74e13c89f3d9406cb857c56e0b41941ec54b7a0f Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Thu, 20 Jun 2024 20:10:19 +0200 Subject: [PATCH 8/8] MEGA65: cleanup and reorg uartmon stuff #11 Moving everything out of mega65.c which is uartmon into uart_monitor.c, well, at least allmost everything ... Still, no new feature is used from CPU65 emulation, just mainly reorganization of the source to look nicer for further hacking/developing. --- targets/mega65/mega65.c | 124 +++------------------------ targets/mega65/mega65.h | 20 ++--- targets/mega65/uart_monitor.c | 156 ++++++++++++++++++++++++++++------ targets/mega65/uart_monitor.h | 22 ++--- 4 files changed, 153 insertions(+), 169 deletions(-) diff --git a/targets/mega65/mega65.c b/targets/mega65/mega65.c index 681b399f..5385e948 100644 --- a/targets/mega65/mega65.c +++ b/targets/mega65/mega65.c @@ -56,16 +56,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ static int nmi_level; // please read the comment at nmi_set() below static int emulation_is_running = 0; static int speed_current = -1; -static int paused = 0, paused_old = 0; -static int breakpoint_pc = -1; +int paused = 0; +static int paused_old = 0; #ifdef TRACE_NEXT_SUPPORT static int orig_sp = 0; static int trace_next_trigger = 0; #endif -static int trace_step_trigger = 0; -#ifdef HAS_UARTMON_SUPPORT -static void (*m65mon_callback)(void) = NULL; -#endif +int trace_step_trigger = 0; static char emulator_speed_title[64] = ""; static char fast_mhz_as_string[16] = ""; const char *cpu_clock_speed_string_p = ""; @@ -422,13 +419,13 @@ static void mega65_init ( void ) cia2.TLAH = 1; // *** Initialize DMA (we rely on memory and I/O decoder provided functions here for the purpose) dma_init(); -#ifdef HAS_UARTMON_SUPPORT - uartmon_init(configdb.uartmon); -#endif sprintf(fast_mhz_as_string, "%.2fMHz", configdb.fast_mhz); 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) +#ifdef HAS_UARTMON_SUPPORT + uartmon_init(configdb.uartmon); +#endif memory_set_rom_protection(0); hypervisor_start_machine(); speed_current = 0; @@ -552,108 +549,7 @@ int reset_mega65_asked ( void ) #ifdef HAS_UARTMON_SUPPORT -void m65mon_show_regs ( void ) -{ - Uint8 pf = cpu65_get_pf(); - umon_printf( - "\r\n" - "PC A X Y Z B SP MAPL MAPH LAST-OP P P-FLAGS RGP uS IO\r\n" - "%04X %02X %02X %02X %02X %02X %04X " // register banned message and things from PC to SP - "%04X %04X %02X %02X %02X " // from MAPL to P - "%c%c%c%c%c%c%c%c ", // P-FLAGS - cpu65.pc, cpu65.a, cpu65.x, cpu65.y, cpu65.z, cpu65.bphi >> 8, cpu65.sphi | cpu65.s, - ((map_mask & 0x0F) << 12) | (map_offset_low >> 8), - ((map_mask & 0xF0) << 8) | (map_offset_high >> 8), - cpu65.op, - pf, 0, // flags - (pf & CPU65_PF_N) ? 'N' : '-', - (pf & CPU65_PF_V) ? 'V' : '-', - (pf & CPU65_PF_E) ? 'E' : '-', - '-', - (pf & CPU65_PF_D) ? 'D' : '-', - (pf & CPU65_PF_I) ? 'I' : '-', - (pf & CPU65_PF_Z) ? 'Z' : '-', - (pf & CPU65_PF_C) ? 'C' : '-' - ); -} - -void m65mon_set_pc ( const Uint16 addr ) -{ - cpu65_debug_set_pc(addr); -} - -void m65mon_dumpmem16 ( Uint16 addr ) -{ - int n = 16; - umon_printf(":000%04X:", addr); - while (n--) - umon_printf("%02X", debug_read_cpu_byte(addr++)); -} - -void m65mon_dumpmem28 ( int addr ) -{ - int n = 16; - addr &= 0xFFFFFFF; - umon_printf(":%07X:", addr); - while (n--) - umon_printf("%02X", debug_read_linear_byte(addr++)); -} - -void m65mon_setmem28 ( int addr, int cnt, Uint8* vals ) -{ - while (--cnt >= 0) - debug_write_linear_byte(addr++, *(vals++)); -} - -void m65mon_set_trace ( int m ) -{ - paused = m; -} - -#ifdef TRACE_NEXT_SUPPORT -void m65mon_do_next ( void ) -{ - if (paused) { - umon_send_ok = 0; // delay command execution! - m65mon_callback = m65mon_show_regs; // register callback - trace_next_trigger = 2; // if JSR, then trigger until RTS to next_addr - orig_sp = cpu65.sphi | cpu65.s; - paused = 0; - } else { - umon_printf(UMON_SYNTAX_ERROR "trace can be used only in trace mode"); - } -} -#endif - -void m65mon_do_trace ( void ) -{ - if (paused) { - umon_send_ok = 0; // delay command execution! - m65mon_callback = m65mon_show_regs; // register callback - trace_step_trigger = 1; // trigger one step - } else { - umon_printf(UMON_SYNTAX_ERROR "trace can be used only in trace mode"); - } -} - -void m65mon_do_trace_c ( void ) -{ - umon_printf(UMON_SYNTAX_ERROR "command 'tc' is not implemented yet"); -} -#ifdef TRACE_NEXT_SUPPORT -void m65mon_next_command ( void ) -{ - if (paused) - m65mon_do_next(); -} -#endif -void m65mon_empty_command ( void ) -{ - if (paused) - m65mon_do_trace(); -} - -void m65mon_breakpoint ( int brk ) +void set_breakpoint ( int brk ) { breakpoint_pc = brk; if (brk < 0) @@ -765,19 +661,25 @@ static void emulation_loop ( void ) cpu_cycles_per_step = 0; } else { DEBUGPRINT("TRACE: leaving trace mode @ $%04X" NL, cpu65.pc); +#ifdef HAS_UARTMON_SUPPORT if (breakpoint_pc < 0) cpu_cycles_per_step = cpu_cycles_per_scanline; else cpu_cycles_per_step = 0; +#else + cpu_cycles_per_step = cpu_cycles_per_scanline; +#endif } } } if (XEMU_UNLIKELY(hypervisor_is_debugged && in_hypervisor)) hypervisor_debug(); +#ifdef HAS_UARTMON_SUPPORT if (XEMU_UNLIKELY(breakpoint_pc == cpu65.pc)) { DEBUGPRINT("TRACE: Breakpoint @ $%04X hit, Xemu moves to trace mode after the execution of this opcode." NL, cpu65.pc); paused = 1; } +#endif cycles += XEMU_UNLIKELY(in_dma) ? dma_update_multi_steps(cpu_cycles_per_scanline) : cpu65_step( #ifdef CPU_STEP_MULTI_OPS cpu_cycles_per_step diff --git a/targets/mega65/mega65.h b/targets/mega65/mega65.h index 2f7f9948..1e98ac7c 100644 --- a/targets/mega65/mega65.h +++ b/targets/mega65/mega65.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 @@ -50,20 +50,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SID_CYCLES_PER_SEC 1000000 #define AUDIO_SAMPLE_FREQ 44100 -extern void m65mon_show_regs ( void ); -extern void m65mon_set_pc ( const Uint16 addr ); -extern void m65mon_dumpmem16 ( Uint16 addr ); -extern void m65mon_dumpmem28 ( int addr ); -extern void m65mon_setmem28 ( int addr, int cnt, Uint8* vals ); -extern void m65mon_set_trace ( int m ); -extern void m65mon_do_trace ( void ); -#ifdef TRACE_NEXT_SUPPORT -extern void m65mon_next_command ( void ); -#endif -extern void m65mon_empty_command ( void ); -extern void m65mon_do_trace_c ( void ); -extern void m65mon_breakpoint ( int brk ); - extern void machine_set_speed ( int verbose ); extern void reset_mega65 ( void ); @@ -75,9 +61,13 @@ extern int mega65_set_model ( const Uint8 id ); extern int dump_memory ( const char *fn ); extern int dump_screen ( const char *fn ); +extern void set_breakpoint ( int brk ); + extern Uint8 last_dd00_bits; extern const char *last_reset_type; extern int cpu_cycles_per_step; extern const char *cpu_clock_speed_string_p; +extern int paused; +extern int trace_step_trigger; #endif diff --git a/targets/mega65/uart_monitor.c b/targets/mega65/uart_monitor.c index 01ab8cb5..82c0d8ba 100644 --- a/targets/mega65/uart_monitor.c +++ b/targets/mega65/uart_monitor.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 @@ -16,44 +16,34 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "xemu/emutools.h" -#include "mega65.h" -#include "uart_monitor.h" - - #if !defined(HAS_UARTMON_SUPPORT) -// Windows is not supported currently, as it does not have POSIX-standard socket interface (?). -// Also, it's pointless for emscripten, for sure. #warning "Platform does not support UMON" #else +#include "xemu/emutools.h" +#include "mega65.h" +#include "uart_monitor.h" +#include "xemu/cpu65.h" +#include "memory_mapper.h" #include "sdcard.h" - #include "xemu/emutools_socketapi.h" - -//#include -//#include -//#include -//#include #include -//#include #ifndef XEMU_ARCH_WIN #include #include -//#include -//#include -//#include #endif -int umon_write_size; -int umon_send_ok; -char umon_write_buffer[UMON_WRITE_BUFFER_SIZE]; - -#define UNCONNECTED XS_INVALID_SOCKET - -#define PRINTF_SOCK PRINTF_S64 - +#define UMON_WRITE_BUFFER_SIZE 0x4000 +#define umon_printf(...) do { \ + if (XEMU_LIKELY(umon_write_size < UMON_WRITE_BUFFER_SIZE - 1)) \ + umon_write_size += snprintf(umon_write_buffer + umon_write_size, UMON_WRITE_BUFFER_SIZE - umon_write_size, __VA_ARGS__); \ + else \ + _umon_write_size_panic(); \ + } while(0) +#define UMON_SYNTAX_ERROR "?SYNTAX ERROR " +#define UNCONNECTED XS_INVALID_SOCKET +#define PRINTF_SOCK PRINTF_S64 static xemusock_socket_t sock_server = UNCONNECTED; static xemusock_socklen_t sock_len; @@ -62,10 +52,120 @@ static xemusock_socket_t sock_client = UNCONNECTED; static int umon_write_pos, umon_read_pos; static int umon_echo; static char umon_read_buffer [0x1000]; +static int umon_write_size; +static int umon_send_ok; +static char umon_write_buffer[UMON_WRITE_BUFFER_SIZE]; + +void (*m65mon_callback)(void) = NULL; +int breakpoint_pc = -1; -// WARNING: This source is pretty ugly, ie not so much check of overflow of the output (write) buffer. +static void _umon_write_size_panic ( void ) +{ + DEBUGPRINT("UARTMON: warning: too long message (%d/%d), cannot fit into the output buffer!" NL, umon_write_pos, UMON_WRITE_BUFFER_SIZE); +} + +static void m65mon_show_regs ( void ) +{ + Uint8 pf = cpu65_get_pf(); + umon_printf( + "\r\n" + "PC A X Y Z B SP MAPL MAPH LAST-OP P P-FLAGS RGP uS IO\r\n" + "%04X %02X %02X %02X %02X %02X %04X " // register banned message and things from PC to SP + "%04X %04X %02X %02X %02X " // from MAPL to P + "%c%c%c%c%c%c%c%c ", // P-FLAGS + cpu65.pc, cpu65.a, cpu65.x, cpu65.y, cpu65.z, cpu65.bphi >> 8, cpu65.sphi | cpu65.s, + ((map_mask & 0x0F) << 12) | (map_offset_low >> 8), + ((map_mask & 0xF0) << 8) | (map_offset_high >> 8), + cpu65.op, + pf, 0, // flags + (pf & CPU65_PF_N) ? 'N' : '-', + (pf & CPU65_PF_V) ? 'V' : '-', + (pf & CPU65_PF_E) ? 'E' : '-', + '-', + (pf & CPU65_PF_D) ? 'D' : '-', + (pf & CPU65_PF_I) ? 'I' : '-', + (pf & CPU65_PF_Z) ? 'Z' : '-', + (pf & CPU65_PF_C) ? 'C' : '-' + ); +} + +static void m65mon_set_pc ( const Uint16 addr ) +{ + cpu65_debug_set_pc(addr); +} + +static void m65mon_dumpmem16 ( Uint16 addr ) +{ + int n = 16; + umon_printf(":000%04X:", addr); + while (n--) + umon_printf("%02X", debug_read_cpu_byte(addr++)); +} + +static void m65mon_dumpmem28 ( int addr ) +{ + int n = 16; + addr &= 0xFFFFFFF; + umon_printf(":%07X:", addr); + while (n--) + umon_printf("%02X", debug_read_linear_byte(addr++)); +} + +static void m65mon_setmem28 ( int addr, int cnt, Uint8* vals ) +{ + while (--cnt >= 0) + debug_write_linear_byte(addr++, *(vals++)); +} + +static void m65mon_set_trace ( int m ) +{ + paused = m; +} + +#ifdef TRACE_NEXT_SUPPORT +static void m65mon_do_next ( void ) +{ + if (paused) { + umon_send_ok = 0; // delay command execution! + m65mon_callback = m65mon_show_regs; // register callback + trace_next_trigger = 2; // if JSR, then trigger until RTS to next_addr + orig_sp = cpu65.sphi | cpu65.s; + paused = 0; + } else { + umon_printf(UMON_SYNTAX_ERROR "trace can be used only in trace mode"); + } +} +#endif + +static void m65mon_do_trace ( void ) +{ + if (paused) { + umon_send_ok = 0; // delay command execution! + m65mon_callback = m65mon_show_regs; // register callback + trace_step_trigger = 1; // trigger one step + } else { + umon_printf(UMON_SYNTAX_ERROR "trace can be used only in trace mode"); + } +} + +static void m65mon_do_trace_c ( void ) +{ + umon_printf(UMON_SYNTAX_ERROR "command 'tc' is not implemented yet"); +} +#ifdef TRACE_NEXT_SUPPORT +static void m65mon_next_command ( void ) +{ + if (paused) + m65mon_do_next(); +} +#endif +static void m65mon_empty_command ( void ) +{ + if (paused) + m65mon_do_trace(); +} static char *parse_hex_arg ( char *p, int *val, int min, int max ) { @@ -210,7 +310,7 @@ static void execute_command ( char *cmd ) case 'b': cmd = parse_hex_arg(cmd, &par1, 0, 0xFFFF); if (cmd && check_end_of_command(cmd, 1)) - m65mon_breakpoint(par1); + set_breakpoint(par1); break; case 'g': cmd = parse_hex_arg(cmd, &par1, 0, 0xFFFF); diff --git a/targets/mega65/uart_monitor.h b/targets/mega65/uart_monitor.h index 31691190..a02d5e80 100644 --- a/targets/mega65/uart_monitor.h +++ b/targets/mega65/uart_monitor.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 @@ -18,26 +18,18 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef XEMU_MEGA65_UART_MONITOR_H_INCLUDED #define XEMU_MEGA65_UART_MONITOR_H_INCLUDED - #ifdef HAS_UARTMON_SUPPORT -#define UMON_SYNTAX_ERROR "?SYNTAX ERROR " - #define UMON_DEFAULT_PORT ":4510" -#define UMON_WRITE_BUFFER_SIZE 0x4000 -#define umon_printf(...) umon_write_size += sprintf(umon_write_buffer + umon_write_size, __VA_ARGS__) +extern void (*m65mon_callback)(void); +extern int breakpoint_pc; -extern int umon_write_size; -extern int umon_send_ok; -extern char umon_write_buffer[UMON_WRITE_BUFFER_SIZE]; - -extern int uartmon_init ( const char *fn ); -extern int uartmon_is_active ( void ); -extern void uartmon_update ( void ); -extern void uartmon_close ( void ); +extern int uartmon_init ( const char *fn ); +extern int uartmon_is_active ( void ); +extern void uartmon_update ( void ); +extern void uartmon_close ( void ); extern void uartmon_finish_command ( void ); #endif - #endif