From bd47ac995d3c9c80e6d20412eb6d9c90180e2237 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Mon, 5 Aug 2024 20:45:57 +0200 Subject: [PATCH 1/3] MEGA65: "read of unwritten memory" debug mode #405 Rhialto had the feature request to have a mode, where Xemu/MEGA65 can warn you on reading memory which was not written before, can be useful to find ROM bugs. In this current form, it's quite minimal and simple: 1. Only works on the first 126K of physical RAM 2. Only works from command line, start emulation with paramter: -ramcheckread 3. You need to watch the output of the emulator, so on UNIX-like/Linux/Mac systems, emulator should be started from terminal window, on Windows, Xemu console must be open (start also with parameter: -syscon) 4. Check the output of the emulator, you can find lines like: MEM: DEBUG: main RAM at linear address $101A0 has been read without prior write; PC=$8613 [$20613] The PC value can be off by some bytes, because it may have been incremented already during opcode emulation. The [...] is the linear address for the mentioned CPU PC value. Well, that's it. --- targets/mega65/configdb.c | 3 +- targets/mega65/configdb.h | 1 + targets/mega65/hypervisor.c | 2 ++ targets/mega65/memory_mapper.c | 58 ++++++++++++++++++++++++++++++++++ targets/mega65/memory_mapper.h | 1 + 5 files changed, 64 insertions(+), 1 deletion(-) diff --git a/targets/mega65/configdb.c b/targets/mega65/configdb.c index 449a9e4d..5670f975 100644 --- a/targets/mega65/configdb.c +++ b/targets/mega65/configdb.c @@ -112,6 +112,7 @@ static const struct xemutools_configdef_switch_st switch_options[] = { { "fastboot", "Try to use sleepless emulation mode during booting", &configdb.fastboot }, { "matrixstart", "Start with matrix-mode activated", &configdb.matrixstart }, { "matrixdisable", "Disable the matrix hotkey", &configdb.matrixdisable }, + { "ramcheckread", "Enabled warnings on reading unwritten memory (first 126K only)", &configdb.ramcheckread }, { NULL } }; @@ -152,7 +153,7 @@ static const void *do_not_save_opts[] = { &configdb.matrixstart, &configdb.dumpmem, &configdb.dumpscreen, &configdb.screenshot_and_exit, &configdb.testing, &configdb.hyperdebug, &configdb.hyperdebugfreezer, &configdb.usestubrom, &configdb.useinitrom, &configdb.useutilmenu, - &configdb.cartbin8000, &configdb.winpos, + &configdb.cartbin8000, &configdb.winpos, &configdb.ramcheckread, NULL }; diff --git a/targets/mega65/configdb.h b/targets/mega65/configdb.h index 89cef071..f5f69ca9 100644 --- a/targets/mega65/configdb.h +++ b/targets/mega65/configdb.h @@ -112,6 +112,7 @@ struct configdb_st { int matrixstart; int matrixdisable; char *winpos; + int ramcheckread; }; extern struct configdb_st configdb; diff --git a/targets/mega65/hypervisor.c b/targets/mega65/hypervisor.c index 6dca5ed3..c7f76e52 100644 --- a/targets/mega65/hypervisor.c +++ b/targets/mega65/hypervisor.c @@ -269,6 +269,7 @@ void hypervisor_start_machine ( void ) extract_version_string(hyppo_version_string, sizeof hyppo_version_string); DEBUGPRINT("HYPERVISOR: HYPPO version \"%s\" (%s) starting with TRAP reset (#$%02X)" NL, hyppo_version_string, hickup_is_overriden ? "OVERRIDEN" : "built-in", TRAP_RESET); hypervisor_enter(TRAP_RESET); + memory_reset_unwritten_debug_stat(); } @@ -299,6 +300,7 @@ static inline void first_leave ( void ) hdos_notify_system_start_end(); xemu_sleepless_temporary_mode(0); // turn off temporary sleepless mode which may have been enabled before vic_frame_counter_since_boot = 0; + //memory_reset_unwritten_debug_stat(); // FIXME/TODO: commented out since it generates a **tons** of warnings then with the "unwritten mem read" debug mode (-ramcheckread emu option) DEBUGPRINT("HYPERVISOR: first return after RESET, end of processing workarounds." NL); } diff --git a/targets/mega65/memory_mapper.c b/targets/mega65/memory_mapper.c index 9c677bec..b7f96f11 100644 --- a/targets/mega65/memory_mapper.c +++ b/targets/mega65/memory_mapper.c @@ -48,6 +48,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // 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]; +static Uint8 main_ram_written[0x1F800U]; // 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]; @@ -162,12 +163,38 @@ static Uint32 policy4k[0x10]; // memory policy with MAP taken account (the real static Uint8 zero_page_reader ( const Uint32 addr32 ); static void zero_page_writer ( const Uint32 addr32, const Uint8 data ); + +static void checked_reader_warning ( const Uint32 addr32 ) +{ + DEBUGPRINT("MEM: DEBUG: main RAM at linear address $%X has been read without prior write; PC=$%04X [$%X]" NL, addr32, cpu65.pc, memory_cpu_addr_to_linear(cpu65.pc, NULL)); +} + + +static Uint8 zero_page_checked_reader ( const Uint32 addr32 ) { + if (!main_ram_written[addr32]) + checked_reader_warning(addr32); + return zero_page_reader(addr32); +} +static void zero_page_checked_writer ( const Uint32 addr32, const Uint8 data ) { + main_ram_written[addr32] = 1; + zero_page_writer(addr32, data); +} + static Uint8 main_ram_reader ( const Uint32 addr32 ) { return main_ram[addr32]; } static void main_ram_writer ( const Uint32 addr32, const Uint8 data ) { main_ram[addr32] = data; } +static Uint8 main_ram_checked_reader ( const Uint32 addr32 ) { + if (!main_ram_written[addr32]) + checked_reader_warning(addr32); + return main_ram[addr32]; +} +static void main_ram_checked_writer ( const Uint32 addr32, const Uint8 data ) { + main_ram_written[addr32] = 1; + main_ram[addr32] = data; +} static void shared_main_ram_writer ( const Uint32 addr32, const Uint8 data ) { main_ram[addr32] = data; colour_ram[addr32 - 0x1F800U] = data; @@ -348,6 +375,30 @@ static struct mem_map_st mem_map[] = { static XEMU_INLINE void slot_assignment_postprocessing ( const Uint32 slot ) { + if (XEMU_UNLIKELY(configdb.ramcheckread && mem_slot_rd_addr32[slot] < 0x1F800U)) { + if (mem_slot_rd_func[slot] == main_ram_reader) { +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = NULL; +#endif + mem_slot_rd_func[slot] = main_ram_checked_reader; + } else if (mem_slot_rd_func[slot] == zero_page_reader) { +#ifdef MEM_USE_DATA_POINTERS + mem_slot_rd_data[slot] = NULL; +#endif + mem_slot_rd_func[slot] = zero_page_checked_reader; + } + if (mem_slot_wr_func[slot] == main_ram_writer) { +#ifdef MEM_USE_DATA_POINTERS + mem_slot_wr_data[slot] = NULL; +#endif + mem_slot_wr_func[slot] = main_ram_checked_writer; + } else if (mem_slot_wr_func[slot] == zero_page_writer) { +#ifdef MEM_USE_DATA_POINTERS + mem_slot_wr_data[slot] = NULL; +#endif + mem_slot_wr_func[slot] = zero_page_checked_writer; + } + } 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 @@ -934,6 +985,7 @@ void memory_init (void ) // 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); + memory_reset_unwritten_debug_stat(); 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, @@ -945,6 +997,12 @@ void memory_init (void ) } +void memory_reset_unwritten_debug_stat ( void ) +{ + memset(main_ram_written, 0x00, sizeof main_ram_written); +} + + // Warning: this overwrites ref_slot! static XEMU_INLINE Uint32 cpu_get_flat_addressing_mode_address ( const Uint8 index ) { diff --git a/targets/mega65/memory_mapper.h b/targets/mega65/memory_mapper.h index 5bfdd3b1..bfcaf91a 100644 --- a/targets/mega65/memory_mapper.h +++ b/targets/mega65/memory_mapper.h @@ -20,6 +20,7 @@ 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_reset_unwritten_debug_stat ( void ); 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, From c33bf19cea4c0f33e5e926e0ebfd5763c0871277 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Mon, 19 Aug 2024 12:15:41 +0200 Subject: [PATCH 2/3] MEGA65: first try of line drawing DMA (WIP!!) #404 Incomplete and possible buggy first try implementation of DMA line drawing. Ideas are based on a "draft implementation" by btoschi on Discord (@bjotos on github). The only reason I present this highly incomplete work in the next branch of Xemu (so not in dev) because I am on holiday, so it's a bit random when I have time to do more work. Thus it's better to release it in this unfinished form, I guess, if someone wants to play with it. --- targets/mega65/dma65.c | 116 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 8 deletions(-) diff --git a/targets/mega65/dma65.c b/targets/mega65/dma65.c index c8191dfb..a5bc19cc 100644 --- a/targets/mega65/dma65.c +++ b/targets/mega65/dma65.c @@ -69,8 +69,13 @@ static unsigned int list_entry_pos = 0; // channel.addr is a fixed-point value, with the lower 8 bits being the fractional part #define DMA_ADDRESSING(channel) (((channel.addr >> 8) & 0xFFFFF) + channel.base) +struct ldm_st { + Uint32 x_col, y_col, slope, slope_accu; + Uint8 slope_type; +}; + // source and target DMA "channels": -static struct { +static struct dma_channel_st { int addr; // address of the current operation, it's a fixed-point math value int base; // base address for "addr", always a "pure" number! It also contains the "megabyte selection", pre-shifted by << 20 int step; // step value, zero(HOLD)/negative/positive, this is a fixed point arithmetic value!! @@ -79,6 +84,7 @@ static struct { Uint8 mbyte; // megabyte slice selected during option read int is_modulo; // modulo mode, if it's non-zero int also_io; // channel access I/O instead of memory, if it's non-zero + struct ldm_st ldm; // LDM = Line Drawing Mode } source, target; static struct { @@ -165,17 +171,51 @@ static Uint8 dma_read_list_next_byte ( void ) return data; } + +static XEMU_INLINE void address_stepping ( struct dma_channel_st *const channel ) +{ + if (XEMU_LIKELY(!(channel->ldm.slope_type & 0x80))) { + // normal, non-LDM (line drawning mode) method + channel->addr += channel->step; + return; + } + // otherwise, we must deal with LDM. The following code is based + // on ideas found in a sample C implementation written by btoschi. THANKS!! + // WARNING: in Xemu, I use a single variable for "addr" and lower 8 bit is the fractional part!! + if (channel->ldm.slope_type & 0x40) { + channel->addr += 0x800U; // +8 -> we always step in Y + if (channel->ldm.slope_accu > 0xFFFFU) { + channel->ldm.slope_accu &= 0xFFFFU; + if (channel->ldm.slope_type & 0x20) + channel->addr -= ((channel->addr & 0x700) == 0) ? channel->ldm.x_col + 0x100 : 0x100; + else + channel->addr += ((channel->addr & 0x700) == 0) ? channel->ldm.x_col + 0x100 : 0x100; + } + } else { + channel->addr += ((channel->addr & 0x700) == 0x700) ? channel->ldm.x_col + 0x100 : 0x100; + channel->ldm.slope_accu += channel->ldm.slope; + if (channel->ldm.slope_accu > 0xFFFFU) { + channel->ldm.slope_accu &= 0xFFFFU; + channel->addr += (channel->ldm.slope_type & 0x20) ? -0x800 : 0x800; + } + } +} + + static XEMU_INLINE void copy_next ( void ) { dma_write_target(dma_read_source()); - source.addr += source.step; - target.addr += target.step; + //source.addr += source.step; + //target.addr += target.step; + address_stepping(&source); + address_stepping(&target); } static XEMU_INLINE void fill_next ( void ) { dma_write_target(filler_byte); - target.addr += target.step; + //target.addr += target.step; + address_stepping(&target); } static XEMU_INLINE void swap_next ( void ) @@ -184,8 +224,10 @@ static XEMU_INLINE void swap_next ( void ) Uint8 da = dma_read_target(); dma_write_source(da); dma_write_target(sa); - source.addr += source.step; - target.addr += target.step; + //source.addr += source.step; + //target.addr += target.step; + address_stepping(&source); + address_stepping(&target); } static XEMU_INLINE void mix_next ( void ) @@ -203,8 +245,10 @@ static XEMU_INLINE void mix_next ( void ) ((~sa) & ( da) & minterms[1]) | ((~sa) & (~da) & minterms[0]) ; dma_write_target(da); - source.addr += source.step; - target.addr += target.step; + //source.addr += source.step; + //target.addr += target.step; + address_stepping(&source); + address_stepping(&target); } @@ -291,6 +335,8 @@ void dma_write_reg ( int addr, Uint8 data ) source.mbyte = 0; // source MB target.mbyte = 0; // target MB length_byte3 = 0; // length byte for >=64K DMA sessions + source.ldm.slope_type = 0; // source: line drawing mode, slope type, do not enable line drawing mode by default + target.ldm.slope_type = 0; // target: -- "" -- if (enhanced_mode) DEBUGDMA("DMA: initiation of ENCHANCED MODE DMA!!!!\n"); else @@ -375,9 +421,63 @@ int dma_update ( void ) case 0x86: // byte value to be treated as "transparent" (ie: skip writing that data), if enabled transparency = (transparency & 0x100) | (unsigned int)optval; break; + case 0x87: // DMA line drawing mode TARGET - X col (LSB) + target.ldm.x_col = (target.ldm.x_col & 0xFF0000U) + (optval << 8); // Xemu integer + 8 bit fractional part arithmetic! + break; + case 0x88: // DMA line drawing mode TARGET - X col (MSB) + target.ldm.x_col = (target.ldm.x_col & 0x00FF00U) + (optval << 16); // Xemu integer + 8 bit fractional part arithmetic! + break; + case 0x89: // DMA line drawing mode TARGET - Row Y col (LSB) + target.ldm.y_col = (target.ldm.y_col & 0xFF00U) + optval; + break; + case 0x8A: // DMA line drawing mode TARGET - Row Y col (MSB) + target.ldm.y_col = (target.ldm.y_col & 0x00FFU) + (optval << 8); + break; + case 0x8B: // DMA line drawing mode TARGET - Slope (LSB) + target.ldm.slope = (target.ldm.slope & 0xFF00U) + optval; + break; + case 0x8C: // DMA line drawing mode TARGET - Slope (MSB) + target.ldm.slope = (target.ldm.slope & 0x00FFU) + (optval << 8); + break; + case 0x8D: // DMA line drawing mode TARGET - Slope init value (LSB) + target.ldm.slope_accu = (target.ldm.slope_accu & 0xFF00U) + optval; + break; + case 0x8E: // DMA line drawing mode TARGET - Slope init value (MSB) + target.ldm.slope_accu = (target.ldm.slope_accu & 0x00FFU) + (optval << 8); + break; + case 0x8F: // DMA line drawing mode TARGET - Slope type + target.ldm.slope_type = optval; + break; case 0x90: // extra high byte of DMA length (bits 23-16) to allow to have >64K DMA length_byte3 = optval; break; + case 0x97: // DMA line drawing mode SOURCE - X col (LSB) + source.ldm.x_col = (source.ldm.x_col & 0xFF0000U) + (optval << 8); // Xemu integer + 8 bit fractional part arithmetic! + break; + case 0x98: // DMA line drawing mode SOURCE - X col (MSB) + source.ldm.x_col = (source.ldm.x_col & 0x00FF00U) + (optval << 16); // Xemu integer + 8 bit fractional part arithmetic! + break; + case 0x99: // DMA line drawing mode SOURCE - Row Y col (LSB) + source.ldm.y_col = (source.ldm.y_col & 0xFF00U) + optval; + break; + case 0x9A: // DMA line drawing mode SOURCE - Row Y col (MSB) + source.ldm.y_col = (source.ldm.y_col & 0x00FFU) + (optval << 8); + break; + case 0x9B: // DMA line drawing mode SOURCE - Slope (LSB) + source.ldm.slope = (source.ldm.slope & 0xFF00U) + optval; + break; + case 0x9C: // DMA line drawing mode SOURCE - Slope (MSB) + source.ldm.slope = (source.ldm.slope & 0x00FFU) + (optval << 8); + break; + case 0x9D: // DMA line drawing mode SOURCE - Slope init value (LSB) + source.ldm.slope_accu = (source.ldm.slope_accu & 0xFF00U) + optval; + break; + case 0x9E: // DMA line drawing mode SOURCE - Slope init value (MSB) + source.ldm.slope_accu = (source.ldm.slope_accu & 0x00FFU) + (optval << 8); + break; + case 0x9F: // DMA line drawing mode SOURCE - Slope type + source.ldm.slope_type = optval; + break; default: // maybe later we should keep this quiet ... if ((opt & 0x80)) From 1e1296154dd782e91c26d37295b5ecfb95eb204d Mon Sep 17 00:00:00 2001 From: RetroCogs <45956643+RetroCogs@users.noreply.github.com> Date: Tue, 20 Aug 2024 22:24:26 -0700 Subject: [PATCH 3/3] Update vic4.c --- targets/mega65/vic4.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index eb869f39..c6d7e05f 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -1439,6 +1439,8 @@ static XEMU_INLINE void vic4_render_char_raster ( void ) char_fetch_offset = -char_fetch_offset; if (SXA_VERTICAL_FLIP(color_data)) enable_bg_paint = 0; + else + enable_bg_paint = 1; if (SXA_ATTR_BOLD(color_data) && SXA_ATTR_REVERSE(color_data) && !REG_VICIII_ATTRIBS) used_palette = altpalette; // use the alternate palette from now in the scanline else