diff options
author | Oxore <oxore@protonmail.com> | 2022-10-01 19:34:58 +0300 |
---|---|---|
committer | Oxore <oxore@protonmail.com> | 2022-10-01 23:09:44 +0300 |
commit | 72abe5f3e714df9cc8310a78bdfa648feb79c4d2 (patch) | |
tree | 0333ee5e61f02548c7ce9dc7038698d15369acc9 | |
parent | f125cc7018ed32b7f919ff71ea3c7d9d2e6565de (diff) |
Add more VDP tracing, add VRAM, CRAM, VSRAM and status
-rw-r--r-- | bus.cpp | 26 | ||||
-rw-r--r-- | bus.hpp | 11 | ||||
-rw-r--r-- | emulator.cpp | 17 | ||||
-rw-r--r-- | m68k_debugging.cpp | 6 | ||||
-rw-r--r-- | vdp.cpp | 344 | ||||
-rw-r--r-- | vdp.hpp | 69 |
6 files changed, 438 insertions, 35 deletions
@@ -6,12 +6,14 @@ #include "utils.hpp" #include "vdp.hpp" +#include <algorithm> #include <cassert> #include <cstdarg> #include <cstdbool> #include <cstdio> #include <cstdint> #include <cstdlib> +#include <vector> #if !defined(DEBUG_TRACE_VDP_ACCESS) # define DEBUG_TRACE_VDP_ACCESS 0 @@ -23,13 +25,14 @@ static constexpr uint32_t kZ80BusReq = 0x00a11100; extern void m68k_breakpoint_callback(void); -unsigned char g_rom[ROM_SIZE] = {}; -unsigned char g_ram[RAM_SIZE] = {}; -unsigned char g_sound_ram[SOUND_RAM_SIZE] = {}; -unsigned char g_io1[IO1_SIZE] = {}; -std::vector<Breakpoint> code_bkpts{}, read_bkpts{}, write_bkpts{}, access_bkpts{}; - -static VDP g_vdp(VDP_START); +extern unsigned char g_rom[ROM_SIZE]; +extern unsigned char g_ram[RAM_SIZE]; +extern unsigned char g_sound_ram[SOUND_RAM_SIZE]; +extern unsigned char g_io1[IO1_SIZE]; +extern unsigned char g_io2[VDP_SIZE]; +extern unsigned char g_psg[PSG_SIZE]; +extern VDP g_vdp; +extern std::vector<Breakpoint> code_bkpts, read_bkpts, write_bkpts, access_bkpts; static void exit_error(const char* fmt, ...) { @@ -154,6 +157,10 @@ static inline ReadResult memory_read( true, }; } else if (is_in_range(address, VDP_START, VDP_SIZE)) { + if (address == PSG_START) { + // 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, @@ -231,6 +238,11 @@ static inline bool memory_write( } return true; } else if (is_in_range(address, VDP_START, VDP_SIZE)) { + if (address == PSG_START) { + // 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; } @@ -4,8 +4,6 @@ #pragma once #include <cstdint> -#include <vector> -#include <algorithm> #define ROM_START (0) #define ROM_SIZE (0x400000) @@ -17,6 +15,8 @@ #define IO1_SIZE (0x4004) #define VDP_START (0xC00000) #define VDP_SIZE (0x20) +#define PSG_START (0xC00011) +#define PSG_SIZE (0x1) enum bitness { BITNESS_8 = 1, @@ -27,10 +27,3 @@ enum bitness { struct Breakpoint { uint32_t offset, length; }; - -extern unsigned char g_rom[ROM_SIZE]; -extern unsigned char g_ram[RAM_SIZE]; -extern unsigned char g_sound_ram[SOUND_RAM_SIZE]; -extern unsigned char g_io1[IO1_SIZE]; -extern unsigned char g_io2[VDP_SIZE]; -extern std::vector<Breakpoint> code_bkpts, read_bkpts, write_bkpts, access_bkpts; diff --git a/emulator.cpp b/emulator.cpp index 95cb0df..7e6ad25 100644 --- a/emulator.cpp +++ b/emulator.cpp @@ -2,11 +2,14 @@ */ #include "bus.hpp" +#include "vdp.hpp" #include "m68k_debugging.hpp" #include "gdbremote_parser.hpp" #include "utils.hpp" #include "musashi-m68k/m68k.h" +#include <arpa/inet.h> +#include <algorithm> #include <cassert> #include <cstdio> #include <cstdint> @@ -14,10 +17,10 @@ #include <cstring> #include <cstdarg> #include <ctime> +#include <fcntl.h> #include <sys/socket.h> -#include <arpa/inet.h> +#include <memory> #include <unistd.h> -#include <fcntl.h> #if !defined(DEBUG_TRACE_INSTRUCTIONS) # define DEBUG_TRACE_INSTRUCTIONS 0 @@ -29,6 +32,14 @@ #define MESSAGE_BUFFER_SIZE 1024 +unsigned char g_rom[ROM_SIZE] = {}; +unsigned char g_ram[RAM_SIZE] = {}; +unsigned char g_sound_ram[SOUND_RAM_SIZE] = {}; +unsigned char g_io1[IO1_SIZE] = {}; +unsigned char g_psg[PSG_SIZE] = {}; +VDP g_vdp(VDP_START); +std::vector<Breakpoint> code_bkpts{}, read_bkpts{}, write_bkpts{}, access_bkpts{}; + template<typename T, size_t S> struct Backtrace { static_assert(S > 0, "Backtrace size cannot be zero"); @@ -269,7 +280,7 @@ static void exit_error(const char* fmt, ...) /* Called when the CPU pulses the RESET line */ void m68k_reset_callback(void) { - // TODO + g_vdp.Reset(); } /* Called when the CPU acknowledges an interrupt */ diff --git a/m68k_debugging.cpp b/m68k_debugging.cpp index 7b7c84c..2e64417 100644 --- a/m68k_debugging.cpp +++ b/m68k_debugging.cpp @@ -6,8 +6,12 @@ #include "bus.hpp" #include "utils.hpp" -#include <cstdlib> +#include <algorithm> #include <cassert> +#include <cstdlib> +#include <vector> + +extern std::vector<Breakpoint> code_bkpts, read_bkpts, write_bkpts, access_bkpts; static inline m68k_register_t ConvertRegisterId(const M68KRegister register_id) { @@ -3,24 +3,36 @@ #include "vdp.hpp" +#include <cstring> +#include <cstdio> + uint32_t VDP::Read(const uint32_t offset, const enum bitness bitness) { - const uint32_t res{}; if (DEBUG_TRACE_VDP_ACCESS) { printf( - "VDP r%d%s @0x%08x 0x%0*x\n", + "VDP r%d%s @0x%08x\n", bitness * 8, (bitness <= 1 ? " " : ""), - base_address + offset, - bitness * 2, - res); + base_address + offset); } if (bitness == BITNESS_32) { - // TODO + if (offset == 0) { + const auto ret = readData(_address_mode, _address & 0xfffe); + _address += _reg[static_cast<size_t>(RegID::kAutoIncrement)]; + return ret; + } else if (offset == 4) { + return readStatusRegister() | (readStatusRegister() << 16); + } } else if (bitness == BITNESS_16) { - // TODO + if (offset == 0 || offset == 2) { + const auto ret = readData(_address_mode, _address & 0xfffe); + _address += _reg[static_cast<size_t>(RegID::kAutoIncrement)]; + return ret; + } else if (offset == 4 || offset == 6) { + return readStatusRegister(); + } } - // Ignore 8-bit reads for now + // XXX: Ignore 8-bit reads for now return 0; } @@ -36,29 +48,335 @@ void VDP::Write(const uint32_t offset, const enum bitness bitness, const uint32_ value); } if (bitness == BITNESS_32) { - if (offset == 4 || offset == 6) { + if (offset == 0) { + writeData(_address_mode, _address & 0xfffe, (value >> 16) & 0xffff); + _address += _reg[static_cast<size_t>(RegID::kAutoIncrement)]; + writeData(_address_mode, _address & 0xfffe, value & 0xffff); + _address += _reg[static_cast<size_t>(RegID::kAutoIncrement)]; + } else if (offset == 4) { writeControl((value >> 16) & 0xffff); writeControl(value & 0xffff); } } else if (bitness == BITNESS_16) { - if (offset == 4 || offset == 6) { + if (offset == 0 || offset == 2) { + writeData(_address_mode, _address & 0xfffe, value & 0xffff); + _address += _reg[static_cast<size_t>(RegID::kAutoIncrement)]; + } else if (offset == 4 || offset == 6) { writeControl(value & 0xffff); } } + // XXX: Ignore 8-bit writes for now if (DEBUG_TRACE_VDP_ACCESS) { printf("\n"); } - // Ignore 8-bit writes for now +} + +void VDP::Reset() +{ + _status = {}; + _control_write_second_word = {}; + _address_mode = {}; + _address = {}; + memset(_reg, 0, kRegistersCount); + memset(_vram, 0, kVRAMSize); + memset(_cram, 0, kCRAMSize); + memset(_vsram, 0, kVSRAMSize); +} + +static inline bool IsWriteToReg(const uint16_t value) +{ + return ((value >> 13) & 0x7) == 4; +} + +void VDP::writeData(const uint8_t address_mode, const uint16_t address, const uint16_t value) +{ + switch (static_cast<AddressMode>(address_mode & 0xf)) { + case AddressMode::kVRAMWrite: + if (address >= kVRAMSize) { + if (DEBUG_TRACE_VDP_ACCESS) { + printf(": VRAM @0x%04x: invalid address, ignoring", address); + } + return; + } + if (DEBUG_TRACE_VDP_ACCESS) { + printf(": VRAM @0x%04x 0x%04x", address, value); + } + _vram[address] = value; + return; + case AddressMode::kCRAMWrite: + if (address >= kCRAMSize) { + if (DEBUG_TRACE_VDP_ACCESS) { + printf(": CRAM @0x%04x: invalid address, ignoring", address); + } + return; + } + if (DEBUG_TRACE_VDP_ACCESS) { + printf(": CRAM @0x%04x 0x%04x", address, value); + } + _cram[address] = value; + return; + case AddressMode::kVSRAMWrite: + if (address >= kVSRAMSize) { + if (DEBUG_TRACE_VDP_ACCESS) { + printf(": VSRAM @0x%04x: invalid address, ignoring", address); + } + return; + } + if (DEBUG_TRACE_VDP_ACCESS) { + printf(": VSRAM @0x%04x 0x%04x", address, value); + } + _vsram[address] = value; + return; + case AddressMode::kVRAMRead: + case AddressMode::kCRAMRead: + case AddressMode::kVSRAMRead: + break; + } + if (DEBUG_TRACE_VDP_ACCESS) { + printf( + ": 0x%02x(%s): invalid address_mode", + address_mode, + addressModeToString(address_mode)); + } } void VDP::writeControl(const uint16_t value) { - if (((value >> 13) & 0x7) == 4) { + if (_control_write_second_word) { + // Write second word + // 0 0 0 0 0 0 0 0 + // CD5 CD4 CD3 CD2 0 0 A15 A14 + _address |= (value & 0x3) << 14; + _address_mode |= ((value >> 4) & 0xf) << 2; + _control_write_second_word = false; + if (DEBUG_TRACE_VDP_ACCESS) { + printf( + ": New address_mode=0x%02x(%s), address=0x%04x", + _address_mode, + addressModeToString(_address_mode), + _address); + } + } else if (IsWriteToReg(value)) { + // Write register + // 1 0 0 RS4 RS3 RS2 RS1 RS0 + // D7 D6 D5 D4 D3 D2 D1 D0 const uint8_t reg_num = (value >> 8) & 0x1f; const uint8_t reg_val = value & 0xff; if (DEBUG_TRACE_VDP_ACCESS) { - printf(": reg 0x%02x w 0x%02x", reg_num, reg_val); + printf( + ": reg 0x%02x(%s) = 0x%02x", + reg_num, + regIDToString(static_cast<RegID>(reg_num)), + reg_val); } _reg[reg_num] = reg_val; + } else if (!_control_write_second_word) { + // Write first word + // CD1 CD0 A13 A12 A11 A10 A9 A8 + // A7 A6 A5 A4 A3 A2 A1 A0 + _address = value & 0x3f; + _address_mode = (value >> 14) & 0x3; + _control_write_second_word = true; + } +} + +uint16_t VDP::readData(const uint8_t address_mode, const uint16_t address) +{ + switch (static_cast<AddressMode>(address_mode & 0xf)) { + case AddressMode::kVRAMWrite: + case AddressMode::kCRAMWrite: + case AddressMode::kVSRAMWrite: + break; + case AddressMode::kVRAMRead: + if (address >= kVRAMSize) { + if (DEBUG_TRACE_VDP_ACCESS) { + printf(": VRAM @0x%04x: invalid address, ignoring", address); + } + return 0; + } else { + const auto value = _vram[address]; + if (DEBUG_TRACE_VDP_ACCESS) { + printf(": VRAM @0x%04x 0x%04x", address, value); + } + return value; + } + case AddressMode::kCRAMRead: + if (address >= kCRAMSize) { + if (DEBUG_TRACE_VDP_ACCESS) { + printf(": CRAM @0x%04x: invalid address, ignoring", address); + } + return 0; + } else { + const auto value = _cram[address]; + if (DEBUG_TRACE_VDP_ACCESS) { + printf(": CRAM @0x%04x 0x%04x", address, value); + } + return value; + } + case AddressMode::kVSRAMRead: + if (address >= kVSRAMSize) { + if (DEBUG_TRACE_VDP_ACCESS) { + printf(": VSRAM @0x%04x: invalid address, ignoring", address); + } + return 0; + } else { + const auto value = _vsram[address]; + if (DEBUG_TRACE_VDP_ACCESS) { + printf(": VSRAM @0x%04x 0x%04x", address, value); + } + return value; + } + } + if (DEBUG_TRACE_VDP_ACCESS) { + printf( + ": 0x%02x(%s): invalid address_mode", + address_mode, + addressModeToString(address_mode)); + } + return 0; +} + +uint16_t VDP::readStatusRegister() const +{ + return 0 + | (!_status.fifo_not_empty << 9) + | (_status.fifo_full << 8) + | (_status.v_irq << 7) + | (_status.sprites_overflow << 6) + | (_status.sprites_collision << 5) + | (_status.odd_frame << 4) + | (_status.vblank << 3) + | (_status.hblank << 2) + | (_status.dma_busy << 1) + | (_status.pal_mode << 0) + ; +} + +const char* VDP::addressModeToString(const uint8_t address_mode) +{ + const uint8_t dma_address_mode = (address_mode >> 5) & 0x3; + switch (static_cast<AddressMode>(address_mode & 0xf)) { + case AddressMode::kVRAMWrite: + switch (dma_address_mode) { + case 0: + return "VRAM write"; + case 1: + return "DMA Memory to VRAM, VRAM write"; + case 2: + return "DMA VRAM fill, VRAM write"; + case 3: + return "DMA VRAM copy, VRAM write"; + } + break; + case AddressMode::kCRAMWrite: + switch (dma_address_mode) { + case 0: + return "CRAM write"; + case 1: + return "DMA Memory to VRAM, CRAM write"; + case 2: + return "DMA VRAM fill, CRAM write"; + case 3: + return "DMA VRAM copy, CRAM write"; + } + break; + case AddressMode::kVSRAMWrite: + switch (dma_address_mode) { + case 0: + return "VSRAM write"; + case 1: + return "DMA Memory to VRAM, VSRAM write"; + case 2: + return "DMA VRAM fill, VSRAM write"; + case 3: + return "DMA VRAM copy, VSRAM write"; + } + break; + case AddressMode::kVRAMRead: + switch (dma_address_mode) { + case 0: + return "VRAM read"; + case 1: + return "DMA Memory to VRAM, VRAM read"; + case 2: + return "DMA VRAM fill, VRAM read"; + case 3: + return "DMA VRAM copy, VRAM read"; + } + break; + case AddressMode::kCRAMRead: + switch (dma_address_mode) { + case 0: + return "CRAM read"; + case 1: + return "DMA Memory to VRAM, CRAM read"; + case 2: + return "DMA VRAM fill, CRAM read"; + case 3: + return "DMA VRAM copy, CRAM read"; + } + break; + case AddressMode::kVSRAMRead: + switch (dma_address_mode) { + case 0: + return "VSRAM read"; + case 1: + return "DMA Memory to VRAM, VSRAM read"; + case 2: + return "DMA VRAM fill, VSRAM read"; + case 3: + return "DMA VRAM copy, VSRAM read"; + } + break; + } + return "Unknown"; +} + +const char* VDP::regIDToString(const RegID reg_id) +{ + switch (reg_id) { + case RegID::kModeSet1: + return "ModeSet1"; + case RegID::kModeSet2: + return "ModeSet2"; + case RegID::kScrollAAddress: + return "ScrollAAddress"; + case RegID::kWindowAddress: + return "WindowAddress"; + case RegID::kScrollBAddress: + return "ScrollBAddress"; + case RegID::kSpritesTableAddress: + return "SpritesTableAddress"; + case RegID::kBackgroundColor: + return "BackgroundColor"; + case RegID::kHint: + return "Hint"; + case RegID::kModeSet3: + return "ModeSet3"; + case RegID::kModeSet4: + return "ModeSet4"; + case RegID::kHScrollTableAddress: + return "HScrollTableAddress"; + case RegID::kAutoIncrement: + return "AutoIncrement"; + case RegID::kScrollSize: + return "ScrollSize"; + case RegID::kWindowHorizontalPosition: + return "WindowHorizontalPosition"; + case RegID::kWindowVertialPosition: + return "WindowVertialPosition"; + case RegID::kDMACounterLow: + return "DMACounterLow"; + case RegID::kDMACounterHigh: + return "DMACounterHigh"; + case RegID::kDMASourceAddressLow: + return "DMASourceAddressLow"; + case RegID::kDMASourceAddressMid: + return "DMASourceAddressMid"; + case RegID::kDMASourceAddressHigh: + return "DMASourceAddressHigh"; + case RegID::kRegistersCount: + break; } + return "Unknown"; } @@ -5,19 +5,84 @@ #include "bus.hpp" -#include <cstdio> +#include <cstddef> +#include <cstring> class VDP { public: VDP(const uint32_t base_address_a = VDP_START): base_address(base_address_a) {} uint32_t Read(uint32_t offset, enum bitness); void Write(uint32_t offset, enum bitness, uint32_t value); + void Reset(); const uint32_t base_address; private: + struct StatusRegister { + bool fifo_not_empty{}; + bool fifo_full{}; + bool v_irq{}; + bool sprites_overflow{}; + bool sprites_collision{}; + bool odd_frame{}; + bool vblank{}; + bool hblank{}; + bool dma_busy{}; + bool pal_mode{}; + }; + + enum class AddressMode: uint8_t { + kVRAMWrite = 1, + kCRAMWrite = 3, + kVSRAMWrite = 5, + kVRAMRead = 0, + kCRAMRead = 8, + kVSRAMRead = 4, + }; + + enum class RegID: size_t { + kModeSet1 = 0, + kModeSet2 = 1, + kScrollAAddress = 2, + kWindowAddress = 3, + kScrollBAddress = 4, + kSpritesTableAddress = 5, + kBackgroundColor = 7, + kHint = 10, + kModeSet3 = 11, + kModeSet4 = 12, + kHScrollTableAddress = 13, + kAutoIncrement = 15, + kScrollSize = 16, + kWindowHorizontalPosition = 17, + kWindowVertialPosition = 18, + kDMACounterLow = 19, + kDMACounterHigh = 20, + kDMASourceAddressLow = 21, + kDMASourceAddressMid = 22, + kDMASourceAddressHigh = 23, + kRegistersCount, ///< Keep it last + }; + + void writeData(uint8_t address_mode, uint16_t address, uint16_t value); void writeControl(uint16_t value); + uint16_t readData(uint8_t address_mode, uint16_t address); + uint16_t readStatusRegister() const; + + const char* addressModeToString(uint8_t address_mode); + const char* regIDToString(RegID); + + static constexpr size_t kRegistersCount = static_cast<size_t>(RegID::kRegistersCount); + static constexpr size_t kVRAMSize = 64*1024; + static constexpr size_t kCRAMSize = 64*2; + static constexpr size_t kVSRAMSize = 40*2; - static constexpr size_t kRegistersCount = 0x18; + StatusRegister _status{}; + bool _control_write_second_word{}; + uint8_t _address_mode{}; + uint16_t _address{}; uint8_t _reg[kRegistersCount]{}; + uint8_t _vram[kVRAMSize]{}; + uint8_t _cram[kCRAMSize]{}; + uint8_t _vsram[kVSRAMSize]{}; }; |