summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOxore <oxore@protonmail.com>2022-10-01 19:34:58 +0300
committerOxore <oxore@protonmail.com>2022-10-01 23:09:44 +0300
commit72abe5f3e714df9cc8310a78bdfa648feb79c4d2 (patch)
tree0333ee5e61f02548c7ce9dc7038698d15369acc9
parentf125cc7018ed32b7f919ff71ea3c7d9d2e6565de (diff)
Add more VDP tracing, add VRAM, CRAM, VSRAM and status
-rw-r--r--bus.cpp26
-rw-r--r--bus.hpp11
-rw-r--r--emulator.cpp17
-rw-r--r--m68k_debugging.cpp6
-rw-r--r--vdp.cpp344
-rw-r--r--vdp.hpp69
6 files changed, 438 insertions, 35 deletions
diff --git a/bus.cpp b/bus.cpp
index eb2c83b..32a5b5c 100644
--- a/bus.cpp
+++ b/bus.cpp
@@ -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;
}
diff --git a/bus.hpp b/bus.hpp
index 0492abd..e776d60 100644
--- a/bus.hpp
+++ b/bus.hpp
@@ -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)
{
diff --git a/vdp.cpp b/vdp.cpp
index 632429d..d870fce 100644
--- a/vdp.cpp
+++ b/vdp.cpp
@@ -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";
}
diff --git a/vdp.hpp b/vdp.hpp
index 0e911c9..cd93938 100644
--- a/vdp.hpp
+++ b/vdp.hpp
@@ -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]{};
};