summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--graphics.cpp12
-rw-r--r--vdp.cpp118
-rw-r--r--vdp.hpp14
3 files changed, 120 insertions, 24 deletions
diff --git a/graphics.cpp b/graphics.cpp
index 03379c5..0a6f699 100644
--- a/graphics.cpp
+++ b/graphics.cpp
@@ -18,8 +18,8 @@ Graphics::Graphics()
_window = SDL_CreateWindow("Gut (SEGA MD/G emulator)",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
- VDP().render_width,
- VDP().render_height,
+ VDP().kRenderWidth,
+ VDP().kRenderHeight,
SDL_WINDOW_RESIZABLE);
if (!_window) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create window: %s\n", SDL_GetError());
@@ -36,8 +36,8 @@ Graphics::Graphics()
_renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
- VDP().render_width,
- VDP().render_height);
+ VDP().kRenderWidth,
+ VDP().kRenderHeight);
if (!_render_texture) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create texture: %s\n", SDL_GetError());
SDL_Quit();
@@ -74,9 +74,9 @@ void Graphics::Render(const VDP& vdp)
// TODO probably should return and propagate error
abort();
}
- for (size_t row = 0; row < VDP().render_height; row++) {
+ for (size_t row = 0; row < VDP().kRenderHeight; row++) {
uint32_t *dst = (uint32_t*)((uint8_t*)pixels + row * pitch);
- memcpy(dst, buffer, VDP().render_width * sizeof(*dst));
+ memcpy(dst, buffer + row * VDP().kRenderWidth, VDP().kRenderWidth * sizeof(*dst));
}
SDL_UnlockTexture(_render_texture);
SDL_RenderClear(_renderer);
diff --git a/vdp.cpp b/vdp.cpp
index 6817c2d..765855c 100644
--- a/vdp.cpp
+++ b/vdp.cpp
@@ -45,6 +45,32 @@
((reg) = ((reg) & ~(MODESET2_M2_MASK << MODESET2_M2_SHIFT)) \
| ((v & MODESET2_M2_MASK) << MODESET2_M2_SHIFT))
+#define SCROLLA_ADDR_MASK (0x38)
+#define SCROLLA_ADDR_SHIFT (10)
+#define SCROLLA_ADDR_GET(reg) \
+ ((((uint16_t)(reg)) & (SCROLLA_ADDR_MASK)) << SCROLLA_ADDR_SHIFT)
+#define SCROLLA_ADDR_SET(reg, v) \
+ ((reg) = ((reg) & ~SCROLLA_ADDR_MASK) \
+ | (((uint16_t)(v) >> SCROLLA_ADDR_SHIFT) & SCROLLA_ADDR_MASK))
+
+// 0: V32 CELL, 1: V64 CELL, 2: Prohibited, 3: V128 CELL
+#define SCROLLSIZE_VCELL_MASK (3)
+#define SCROLLSIZE_VCELL_SHIFT (4)
+#define SCROLLSIZE_VCELL_GET(reg) \
+ (((reg) & (SCROLLSIZE_VCELL_MASK << SCROLLSIZE_VCELL_SHIFT)) >> SCROLLSIZE_VCELL_SHIFT)
+#define SCROLLSIZE_VCELL_SET(reg, v) \
+ ((reg) = ((reg) & ~(SCROLLSIZE_VCELL_MASK << SCROLLSIZE_VCELL_SHIFT)) \
+ | ((v & SCROLLSIZE_VCELL_MASK) << SCROLLSIZE_VCELL_SHIFT))
+
+// 0: H32 CELL, 1: H64 CELL, 2: Prohibited, 3: H128 CELL
+#define SCROLLSIZE_HCELL_MASK (3)
+#define SCROLLSIZE_HCELL_SHIFT (0)
+#define SCROLLSIZE_HCELL_GET(reg) \
+ (((reg) & (SCROLLSIZE_HCELL_MASK << SCROLLSIZE_HCELL_SHIFT)) >> SCROLLSIZE_HCELL_SHIFT)
+#define SCROLLSIZE_HCELL_SET(reg, v) \
+ ((reg) = ((reg) & ~(SCROLLSIZE_HCELL_MASK << SCROLLSIZE_HCELL_SHIFT)) \
+ | ((v & SCROLLSIZE_HCELL_MASK) << SCROLLSIZE_HCELL_SHIFT))
+
#define DMASRCADRHIGH_DMD_MASK (3)
#define DMASRCADRHIGH_DMD_SHIFT (6)
#define DMASRCADRHIGH_DMD_GET(reg) \
@@ -53,6 +79,10 @@
((reg) = ((reg) & ~(DMASRCADRHIGH_DMD_MASK << DMASRCADRHIGH_DMD_SHIFT)) \
| ((v & DMASRCADRHIGH_DMD_MASK) << DMASRCADRHIGH_DMD_SHIFT))
+static constexpr size_t kCellWidthPixels = 8;
+static constexpr size_t kCellHeightPixels = 8;
+static constexpr size_t kPixelsPerByte = 2;
+
uint32_t VDP::Read(const uint32_t offset, const enum bitness bitness)
{
uint32_t ret{};
@@ -139,6 +169,66 @@ void VDP::Write(const uint32_t offset, const enum bitness bitness, const uint32_
}
}
+static inline size_t scrollsize_cells_count(uint8_t xcell_value)
+{
+ assert(xcell_value <= 3);
+ // It actually returns 96 when xcell_value == 2, but it is prohibited and we
+ // just assume that this is not the case.
+ return (xcell_value + 1) * 32;
+}
+
+static inline uint32_t RenderColorFromCRAM(uint16_t color)
+{
+ const uint32_t blue = ((color >> 9) & 7) << 5;
+ const uint32_t green = ((color >> 5) & 7) << 5;
+ const uint32_t red = ((color >> 1) & 7) << 5;
+ return 0xff000000 | (red << 16) | (green << 8) | blue;
+}
+
+static inline uint32_t GetU32BE(const void *buf)
+{
+ return
+ (static_cast<uint32_t>(reinterpret_cast<const uint8_t*>(buf)[0]) << 24) |
+ (static_cast<uint32_t>(reinterpret_cast<const uint8_t*>(buf)[1]) << 16) |
+ (static_cast<uint32_t>(reinterpret_cast<const uint8_t*>(buf)[2]) << 8) |
+ (static_cast<uint32_t>(reinterpret_cast<const uint8_t*>(buf)[3]) << 0);
+}
+
+static inline uint16_t GetU16BE(const void *buf)
+{
+ return
+ (static_cast<uint16_t>(reinterpret_cast<const uint8_t*>(buf)[0]) << 8) |
+ (static_cast<uint16_t>(reinterpret_cast<const uint8_t*>(buf)[1]) << 0);
+}
+
+void VDP::renderScrollALine(const size_t line_index, const size_t hcell_count)
+{
+ const uint8_t scrolla_reg = _reg[static_cast<size_t>(RegID::kScrollAAddress)];
+ const uint16_t scrolla_addr = SCROLLA_ADDR_GET(scrolla_reg);
+ const size_t cy = line_index / kCellHeightPixels;
+ const size_t y = line_index % kCellHeightPixels;
+ for (size_t cx = 0; cx < hcell_count; cx++) {
+ if (cx * kCellWidthPixels >= kRenderWidth) {
+ break;
+ }
+ const uint16_t cell_id = GetU16BE(
+ _vram + scrolla_addr + (cy * hcell_count + cx) * sizeof(uint16_t));
+ const uint32_t val = GetU32BE(
+ _vram + (cell_id * kCellHeightPixels + y) * (kCellWidthPixels / kPixelsPerByte));
+ for (size_t x = 0; x < kCellWidthPixels; x++) {
+ if (cx * kCellWidthPixels + x >= kRenderWidth) {
+ continue;
+ }
+ const size_t color_index = (val >> ((kCellWidthPixels - 1 - x) * 4)) & 0xf;
+ const uint16_t cram_color = GetU16BE(_cram + color_index * sizeof(uint16_t));
+ const uint32_t color = RenderColorFromCRAM(cram_color);
+ const size_t render_offset =
+ ((cy * kCellHeightPixels + y) * kRenderWidth) + cx * kCellWidthPixels + x;
+ _rendered_buffer[render_offset] = color;
+ }
+ }
+}
+
bool VDP::Scanline()
{
const uint16_t mode_set_2 = _reg[static_cast<size_t>(RegID::kModeSet2)];
@@ -146,20 +236,23 @@ bool VDP::Scanline()
return true;
}
const uint16_t lines_per_screen = _status.pal_mode ? kLinesPerScreenPAL : kLinesPerScreenNTSC;
- // TODO remove the `1 || ` part
- if (1 || _lines_counter + 1 == lines_per_screen) {
- for (size_t i = 0; i < render_width; i++) {
+ if (_lines_counter >= (int)kRenderHeight) {
+ // just render palette
+ for (size_t i = 0; i < kRenderWidth; i++) {
const size_t color_index = (i * 2) % kCRAMSize;
- const uint16_t cram_color = (((uint16_t)_cram[color_index]) << 8) | _cram[color_index + 1];
- const uint8_t blue = ((cram_color >> 9) & 7) << 4;
- const uint8_t green = ((cram_color >> 5) & 7) << 4;
- const uint8_t red = ((cram_color >> 1) & 7) << 4;
- const uint32_t color = 0xff000000 | (red << 16) | (green << 8) | blue;
- _rendered_buffer[render_width * _lines_counter + i] = color;
+ const uint16_t cram_color = GetU16BE(_cram +color_index * sizeof(uint16_t));
+ const uint32_t color = RenderColorFromCRAM(cram_color);
+ _rendered_buffer[kRenderWidth * _lines_counter + i] = color;
}
} else {
- for (size_t i = 0; i < render_width; i++) {
- _rendered_buffer[render_width * _lines_counter + i] = 0xff000000;
+ // Render sprites
+ const uint8_t scrollsize_reg = _reg[static_cast<size_t>(RegID::kScrollSize)];
+ const uint8_t vcell = SCROLLSIZE_VCELL_GET(scrollsize_reg);
+ const uint8_t hcell = SCROLLSIZE_HCELL_GET(scrollsize_reg);
+ // 2 is prohibited value, so we just won't render if it is set
+ if (vcell != 2 && hcell != 2) {
+ const size_t hcell_count = scrollsize_cells_count(hcell);
+ renderScrollALine(_lines_counter, hcell_count);
}
}
_lines_counter++;
@@ -329,11 +422,12 @@ void VDP::writeControl(const uint16_t value)
reg_val);
}
_reg[reg_num] = reg_val;
+ // TODO print warnings of all invalid values or writes that ignore a mask
} 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 = value & 0x3fff;
_address_mode = (value >> 14) & 0x3;
_control_write_second_word = true;
}
diff --git a/vdp.hpp b/vdp.hpp
index 71f259b..c239273 100644
--- a/vdp.hpp
+++ b/vdp.hpp
@@ -10,18 +10,19 @@
class VDP {
public:
- VDP(const uint32_t base_address_a = VDP_START): base_address(base_address_a) {}
+ constexpr 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);
bool Scanline(); // Returns true if display disabled or vblank happened
void Reset();
- const uint32_t* GetRenderedBuffer() const { return _rendered_buffer; }
+ constexpr const uint32_t* GetRenderedBuffer() const { return _rendered_buffer; }
const uint32_t base_address;
- static constexpr size_t render_height = 224;
- static constexpr size_t render_width = 320;
- static constexpr size_t render_buffer_size = 320*224*4;
+ static constexpr size_t kRenderHeight = 224;
+ static constexpr size_t kRenderWidth = 320;
+ static constexpr size_t render_buffer_size = kRenderWidth * kRenderHeight *
+ sizeof(*reinterpret_cast<VDP*>(4)->GetRenderedBuffer());
private:
struct StatusRegister {
@@ -70,6 +71,7 @@ class VDP {
kRegistersCount, ///< Keep it last
};
+ void renderScrollALine(size_t line_index, size_t hcell_count);
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);
@@ -112,5 +114,5 @@ class VDP {
uint8_t _vram[kVRAMSize]{};
uint8_t _cram[kCRAMSize]{};
uint8_t _vsram[kVSRAMSize]{};
- uint32_t _rendered_buffer[kLinesPerScreenNTSC * render_width]{};
+ uint32_t _rendered_buffer[kLinesPerScreenNTSC * kRenderWidth]{};
};