From bc79410a77486065ca034913d9ae94fec49ae032 Mon Sep 17 00:00:00 2001 From: sfja Date: Mon, 12 Jan 2026 22:12:39 +0100 Subject: [PATCH] add screen --- Makefile | 3 + src/main.cpp | 5 ++ src/screen.cpp | 160 +++++++++++++++++++++++++++++++++++++++++++++++++ src/screen.hpp | 61 +++++++++++++++++++ 4 files changed, 229 insertions(+) create mode 100644 src/screen.cpp create mode 100644 src/screen.hpp diff --git a/Makefile b/Makefile index e58e90e..23d99cc 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,9 @@ MAKEFLAGS += -j16 CXXFLAGS := -std=c++23 -Wall -Wextra -pedantic-errors -fsanitize=address,undefined LDFLAGS := +CXXFLAGS += $(shell pkgconf sdl2 --cflags) +CXXFLAGS += $(shell pkgconf sdl2 --libs) + build_dir = build obj_dir = $(build_dir)/obj diff --git a/src/main.cpp b/src/main.cpp index 8a0f2b2..db6b339 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,6 @@ #include "builder.hpp" +#include "screen.hpp" +#include #include #include #include @@ -9,6 +11,9 @@ using R = Reg; int main() { + auto screen = Screen::create(4, 8 * 40, 8 * 24); + screen.value()->set_char(0, 'A'); + constexpr R r0 = R::R0; constexpr R r1 = R::R1; constexpr R r2 = R::R2; diff --git a/src/screen.cpp b/src/screen.cpp new file mode 100644 index 0000000..aea1634 --- /dev/null +++ b/src/screen.cpp @@ -0,0 +1,160 @@ +#include "screen.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace vc5; + +namespace { + +bool sdl_is_initialized = false; + +struct Char { + explicit constexpr Char(char ch, uint64_t data) + : ch(ch) + , data(data) + { + } + + auto to_u64() const -> uint64_t + { + return data; + } + + char ch; + uint64_t data; +}; + +constexpr auto charset = std::array { + Char(' ', 0x0000000000000000), + Char('A', 0x66667E66667E1800), + Char('B', 0x7C7E667C667E7800), + Char('C', 0x3C7E6060607E3C00), + Char('D', 0x7C7E6666667E7C00), + Char('E', 0x7E7E607E607E7E00), + Char('F', 0x60607878607E7E00), + Char('G', 0x7E7E666E607E7E00), + Char('H', 0x6666667E7E666600), + Char('I', 0x7E7E1818187E7E00), + Char('J', 0x3C7E6606067E7E00), + Char('K', 0x666E7C7C6E666600), + Char('L', 0x7E7E606060606000), + Char('M', 0xC3C3C3C3DBFFE700), + Char('N', 0x6666666E7E766600), + Char('O', 0x3C66666666663C00), + Char('P', 0x60607C7E667C7C00), + Char('Q', 0x3F7E6E66667E3C00), + Char('R', 0x666C7C7E667E7C00), + Char('S', 0x3C66063C60663C00), + Char('T', 0x18181818187E7E00), + Char('U', 0x3C7E666666666600), + Char('V', 0x183C7E6666666600), + Char('W', 0xE7FFDBDBC3C3C300), + Char('X', 0x6666663C3C666600), + Char('Y', 0x181818183C666600), + Char('Z', 0x7E7E30180C7E7E00), +}; + +} + +auto Screen::create(int ch_size, int ch_width, int ch_height) + -> std::expected, std::string> +{ + auto profile = ScreenProfile(ch_size, ch_width, ch_height); + auto [_0, _1, _2, px_width, px_height] = profile; + + if (!sdl_is_initialized) { + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + return std::unexpected(SDL_GetError()); + } + } + + SDL_Window* window; + SDL_Renderer* renderer; + + if (SDL_CreateWindowAndRenderer(px_width, px_height, 0, &window, &renderer) + != 0) { + return std::unexpected(SDL_GetError()); + } + + SDL_Texture* texture = SDL_CreateTexture(renderer, + SDL_PIXELFORMAT_RGBA32, + SDL_TEXTUREACCESS_STREAMING, + px_width, + px_height); + + if (texture == nullptr) { + return std::unexpected(SDL_GetError()); + } + + SDL_RenderPresent(renderer); + + return std::make_unique(profile, window, renderer, texture); +} + +Screen::~Screen() +{ + auto lock = std::lock_guard(mx); + + SDL_DestroyTexture(m_texture); + SDL_DestroyRenderer(m_renderer); + SDL_DestroyWindow(m_window); + + if (sdl_is_initialized) { + SDL_Quit(); + } +} + +auto Screen::set_char(uint16_t offset, uint8_t value) + -> std::expected +{ + auto lock = std::lock_guard(mx); + + auto it = std::find_if(charset.begin(), charset.end(), [&](Char ch) { + return ch.ch == value; + }); + Char my_char = *it; + + SDL_Color* buffer; + int pitch; + int res = SDL_LockTexture(m_texture, NULL, (void**)&buffer, &pitch); + if (res != 0) { + return std::unexpected(SDL_GetError()); + } + + auto [ch_size, ch_width, ch_height, px_width, px_height] = m_profile; + + for (int ch_y = 0; ch_y < ch_height; ++ch_y) { + for (int ch_x = 0; ch_x < ch_width; ++ch_x) { + bool ch + = my_char.to_u64() >> (ch_y * ch_width + (ch_width - ch_x - 1)) + & 1; + + for (int px_y = 0; px_y < ch_size; ++px_y) { + for (int px_x = 0; px_x < ch_size; ++px_x) { + + int x = (offset % ch_width * ch_size + ch_x) * px_width + + px_x; + int y = (offset / ch_height * ch_size + ch_y) * px_height + + px_y; + + buffer[y * px_width + x] = ch + ? SDL_Color { 0xff, 0xff, 0xff, 0xff } + : SDL_Color { 0x00, 0x00, 0x00, 0xff }; + } + } + } + } + + SDL_UnlockTexture(m_texture); + SDL_RenderCopy(m_renderer, m_texture, NULL, NULL); + + SDL_RenderPresent(m_renderer); + + return {}; +} diff --git a/src/screen.hpp b/src/screen.hpp new file mode 100644 index 0000000..a7bdf40 --- /dev/null +++ b/src/screen.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include +#include +#include + +struct SDL_Window; +struct SDL_Renderer; +struct SDL_Texture; + +namespace vc5 { + +struct ScreenProfile { + explicit ScreenProfile(int ch_size, int ch_width, int ch_height) + : ch_size(ch_size) + , ch_width(ch_width) + , ch_height(ch_height) + , px_width(ch_size * ch_width) + , px_height(ch_size * ch_height) + { + } + + int ch_size; + int ch_width; + int ch_height; + int px_width; + int px_height; +}; + +class Screen { +public: + static auto create(int ch_size, int ch_width, int ch_height) + -> std::expected, std::string>; + + explicit Screen(ScreenProfile profile, + SDL_Window* window, + SDL_Renderer* renderer, + SDL_Texture* texture) + : m_profile(profile) + , m_window(window) + , m_renderer(renderer) + , m_texture(texture) + { + } + ~Screen(); + + auto set_char(uint16_t offset, uint8_t value) + -> std::expected; + +private: + std::mutex mx; + + ScreenProfile m_profile; + SDL_Window* m_window; + SDL_Renderer* m_renderer; + SDL_Texture* m_texture; +}; + +}