/* SPDX-License-Identifier: Unlicense */ #include "bus.hpp" #include "musashi-m68k/m68k.h" #include "utils.hpp" #include "vdp.hpp" #include "io.hpp" #include #include #include #include #include #include #include #if !defined(DEBUG_TRACE_VDP_ACCESS) # define DEBUG_TRACE_VDP_ACCESS 0 #endif namespace Adr { static constexpr uint32_t kZ80BusReq = 0x00a11100; static constexpr uint32_t kZ80Reset = 0x00a11200; } extern void m68k_breakpoint_callback(void); extern unsigned char g_rom[ROM_SIZE]; extern unsigned char g_ram[RAM_SIZE]; extern unsigned char g_sound_ram[SOUND_RAM_SIZE]; extern IO g_io1; extern unsigned char g_io2[VDP_SIZE]; extern unsigned char g_psg[PSG_SIZE]; extern VDP g_vdp; extern std::vector code_bkpts, read_bkpts, write_bkpts, access_bkpts; static void exit_error(const char* fmt, ...) { static bool in_error = false; if (in_error) { fprintf(stderr, "Nested error\n"); return; } in_error = true; va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "\n"); unsigned int pc = m68k_get_reg(NULL, M68K_REG_PPC); char buff[100]{}; m68k_disassemble(buff, pc, M68K_CPU_TYPE_68000); fprintf(stderr, "%08x: %s\n", pc, buff); exit(EXIT_FAILURE); } static void report_error(const char* fmt, ...) { static bool in_error = false; if (in_error) return; in_error = true; va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "\n"); unsigned int pc = m68k_get_reg(NULL, M68K_REG_PPC); char buff[100]{}; m68k_disassemble(buff, pc, M68K_CPU_TYPE_68000); fprintf(stderr, "%08x: %s\n", pc, buff); in_error = false; } static inline bool is_in_range(uint32_t value, uint32_t begin, uint32_t length) { return value >= begin && value <= begin + length; } struct ReadResult { unsigned int result; bool successful; }; static inline unsigned int memory_read_concrete( const enum bitness bitness, unsigned char const * base, const uint32_t address) { switch (bitness) { case BITNESS_8: return base[address]; case BITNESS_16: return (base[address] << 8) | base[address + 1]; case BITNESS_32: return (base[address] << 24) | (base[address + 1] << 16) | (base[address + 2] << 8) | base[address + 3]; } UNREACHABLE; } static inline bool ranges_overlap( const uint32_t a_start, const uint32_t a_len, const uint32_t b_start, const uint32_t b_len) { return (a_start < b_start + b_len && b_start < a_start + a_len); } static inline void m68k_read_callback(const uint32_t address, const uint32_t size) { for (size_t bi = 0; bi < access_bkpts.size(); bi++) { const auto& b = access_bkpts[bi]; if (ranges_overlap(address, size, b.offset, b.length)) { printf("Access watchpoint @ 0x%08x\n", address); m68k_breakpoint_callback(); break; } } for (size_t bi = 0; bi < read_bkpts.size(); bi++) { const auto& b = read_bkpts[bi]; if (ranges_overlap(address, size, b.offset, b.length)) { printf("Read watchpoint @ 0x%08x\n", address); m68k_breakpoint_callback(); break; } } } static inline ReadResult memory_read( const enum bitness bitness, const uint32_t address) { m68k_read_callback(address, bitness); if (is_in_range(address, ROM_START, ROM_SIZE)) { return ReadResult{ memory_read_concrete(bitness, g_rom, address - ROM_START), true, }; } else if (is_in_range(address, RAM_START, RAM_SIZE)) { return ReadResult{ memory_read_concrete(bitness, g_ram, address - RAM_START), true, }; } else if (is_in_range(address, SOUND_RAM_START, SOUND_RAM_SIZE)) { if (DEBUG_TRACE_Z80RAM_ACCESS) { printf( "Z80RAM r%d%s @0x%08x\n", bitness * 8, (bitness <= 1 ? " " : ""), SOUND_RAM_START + address); } return ReadResult{ memory_read_concrete(bitness, g_sound_ram, address - SOUND_RAM_START), true, }; } else if (is_in_range(address, IO1_START, IO1_SIZE)) { return ReadResult{ g_io1.Read(address - g_io1.base_address, bitness), true, }; } else if (is_in_range(address, VDP_START, VDP_SIZE)) { if (address == PSG_START) { if (DEBUG_TRACE_PSG_ACCESS) { printf( "PSG r%d%s @0x%08x\n", bitness * 8, (bitness <= 1 ? " " : ""), PSG_START + address); } // XXX PSG does not seem necessary to implement return ReadResult{ g_psg[0], true }; } return ReadResult{ g_vdp.Read(address - g_vdp.base_address, bitness), true, }; } return ReadResult{0, false}; } static inline void memory_write_concrete( enum bitness bitness, unsigned char * const base, const uint32_t address, const unsigned int value) { switch (bitness) { case BITNESS_8: base[address] = value & 0xff; break; case BITNESS_16: base[address + 0] = (value >> 8) & 0xff; base[address + 1] = value & 0xff; break; case BITNESS_32: base[address + 0] = (value >> 24) & 0xff; base[address + 1] = (value >> 16) & 0xff; base[address + 2] = (value >> 8) & 0xff; base[address + 3] = value & 0xff; break; } } static inline void m68k_write_callback(const uint32_t address, const uint32_t size) { for (size_t bi = 0; bi < access_bkpts.size(); bi++) { const auto& b = access_bkpts[bi]; if (ranges_overlap(address, size, b.offset, b.length)) { printf("Access watchpoint @ 0x%08x\n", address); m68k_breakpoint_callback(); break; } } for (size_t bi = 0; bi < write_bkpts.size(); bi++) { const auto& b = write_bkpts[bi]; if (ranges_overlap(address, size, b.offset, b.length)) { printf("Write watchpoint @ 0x%08x\n", address); m68k_breakpoint_callback(); break; } } } static inline bool memory_write( const enum bitness bitness, const uint32_t address, const unsigned int value) { m68k_write_callback(address, bitness); if (is_in_range(address, ROM_START, ROM_SIZE)) { memory_write_concrete(bitness, g_rom, address - ROM_START, value); return true; } else if (is_in_range(address, RAM_START, RAM_SIZE)) { memory_write_concrete(bitness, g_ram, address - RAM_START, value); return true; } else if (is_in_range(address, SOUND_RAM_START, SOUND_RAM_SIZE)) { if (DEBUG_TRACE_Z80RAM_ACCESS) { printf( "Z80RAM w%d%s @0x%08x 0x%0*x\n", bitness * 8, (bitness <= 1 ? " " : ""), SOUND_RAM_START + address, bitness * 2, value); } memory_write_concrete(bitness, g_sound_ram, address - SOUND_RAM_START, value); return true; } else if (is_in_range(address, IO1_START, IO1_SIZE)) { g_io1.Write(address - g_io1.base_address, bitness, value); return true; } else if (is_in_range(address, VDP_START, VDP_SIZE)) { if (address == PSG_START) { if (DEBUG_TRACE_PSG_ACCESS) { printf( "PSG w%d%s @0x%08x 0x%0*x\n", bitness * 8, (bitness <= 1 ? " " : ""), PSG_START + address, bitness * 2, value); } // XXX PSG does not seem necessary to implement g_psg[0] = value & 0xff; return true; } g_vdp.Write(address - g_vdp.base_address, bitness, value); return true; } return false; } #define MASK_24(X) ((X) & 0x00ffffff) unsigned int m68k_read_memory_8(const unsigned int address_a) { const uint32_t address = MASK_24(address_a); const ReadResult ret = memory_read(BITNESS_8, address); if (!ret.successful) { report_error("Read error u8 @%08x", address); m68k_breakpoint_callback(); } return ret.result; } unsigned int m68k_read_memory_16(const unsigned int address_a) { const uint32_t address = MASK_24(address_a); const ReadResult ret = memory_read(BITNESS_16, address); if (!ret.successful) { report_error("Read error u16 @%08x", address); m68k_breakpoint_callback(); } return ret.result; } unsigned int m68k_read_memory_32(const unsigned int address_a) { const uint32_t address = MASK_24(address_a); const ReadResult ret = memory_read(BITNESS_32, address); if (!ret.successful) { report_error("Read error u32 @%08x", address); m68k_breakpoint_callback(); } return ret.result; } unsigned int m68k_read_disassembler_16(const unsigned int address_a) { const uint32_t address = MASK_24(address_a); const ReadResult ret = memory_read(BITNESS_16, address); if (0 && !ret.successful) exit_error("Disasm read error u16 @0x%08x", address); return ret.result; } unsigned int m68k_read_disassembler_32(const unsigned int address_a) { const uint32_t address = MASK_24(address_a); const ReadResult ret = memory_read(BITNESS_32, address); if (0 && !ret.successful) exit_error("Disasm read error u32 @0x%08x", address); return ret.result; } void m68k_write_memory_8(const unsigned int address_a, const unsigned int value) { const uint32_t address = MASK_24(address_a); const bool successful = memory_write(BITNESS_8, address, value); if (!successful) { report_error("Attempted to write 0x%02x (u8) to address %08x", value&0xff, address); m68k_breakpoint_callback(); } } void m68k_write_memory_16(const unsigned int address_a, const unsigned int value) { const uint32_t address = MASK_24(address_a); const bool successful = memory_write(BITNESS_16, address, value); if (!successful) { report_error("Attempted to write 0x%04x (u16) to address %08x", value&0xff, address); m68k_breakpoint_callback(); } } void m68k_write_memory_32(const unsigned int address_a, const unsigned int value) { const uint32_t address = MASK_24(address_a); const bool successful = memory_write(BITNESS_32, address, value); if (!successful) { report_error("Attempted to write 0x%08x (u32) to address %08x", value&0xff, address); m68k_breakpoint_callback(); } }