Compare commits
No commits in common. "d53c543ff4bb535429466dd2267a1861d64a3535" and "bc79410a77486065ca034913d9ae94fec49ae032" have entirely different histories.
d53c543ff4
...
bc79410a77
@ -1,198 +0,0 @@
|
|||||||
#include "io_device.hpp"
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
#include <SDL2/SDL_error.h>
|
|
||||||
#include <SDL2/SDL_render.h>
|
|
||||||
#include <SDL2/SDL_video.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <mutex>
|
|
||||||
#include <print>
|
|
||||||
|
|
||||||
using namespace vc5;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
bool sdl_is_initialized = false;
|
|
||||||
|
|
||||||
constexpr uint64_t char_data(uint8_t ch)
|
|
||||||
{
|
|
||||||
switch (ch) {
|
|
||||||
// clang-format off
|
|
||||||
case ' ': return 0x0000000000000000;
|
|
||||||
|
|
||||||
case '0': return 0x003C666E7666663C;
|
|
||||||
case '1': return 0x001838181818183C;
|
|
||||||
case '2': return 0x003C66060C18307E;
|
|
||||||
case '3': return 0x003C66060C06663C;
|
|
||||||
case '4': return 0x000C1C3C6C7E0C0C;
|
|
||||||
|
|
||||||
case 'A': return 0x00187E66667E6666;
|
|
||||||
case 'B': return 0x00787E667C667E7C;
|
|
||||||
case 'C': return 0x003C7E6060607E3C;
|
|
||||||
case 'D': return 0x007C7E6666667E7C;
|
|
||||||
case 'E': return 0x007E7E607E607E7E;
|
|
||||||
case 'F': return 0x007E7E6078786060;
|
|
||||||
case 'G': return 0x007E7E606E667E7E;
|
|
||||||
case 'H': return 0x0066667E7E666666;
|
|
||||||
case 'I': return 0x007E7E1818187E7E;
|
|
||||||
case 'J': return 0x007E7E0606667E3C;
|
|
||||||
case 'K': return 0x0066666E7C7C6E66;
|
|
||||||
case 'L': return 0x0060606060607E7E;
|
|
||||||
case 'M': return 0x00E7FFDBC3C3C3C3;
|
|
||||||
case 'N': return 0x0066767E6E666666;
|
|
||||||
case 'O': return 0x003C66666666663C;
|
|
||||||
case 'P': return 0x007C7C667E7C6060;
|
|
||||||
case 'Q': return 0x003C7E66666E7E3F;
|
|
||||||
case 'R': return 0x007C7E667E7C6C66;
|
|
||||||
case 'S': return 0x003C66603C06663C;
|
|
||||||
case 'T': return 0x007E7E1818181818;
|
|
||||||
case 'U': return 0x0066666666667E3C;
|
|
||||||
case 'V': return 0x00666666667E3C18;
|
|
||||||
case 'W': return 0x00C3C3C3DBDBFFE7;
|
|
||||||
case 'X': return 0x0066663C3C666666;
|
|
||||||
case 'Y': return 0x0066663C18181818;
|
|
||||||
case 'Z': return 0x007E7E0C18307E7E;
|
|
||||||
default: return 0x55AA55AA55AA55AA;
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
auto IoDevice::create()
|
|
||||||
-> std::expected<std::unique_ptr<IoDevice>, std::string>
|
|
||||||
{
|
|
||||||
auto profile = ScreenProfile();
|
|
||||||
auto width = profile.width_raw_px();
|
|
||||||
auto height = profile.height_raw_px();
|
|
||||||
|
|
||||||
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(width, height, 0, &window, &renderer)
|
|
||||||
!= 0) {
|
|
||||||
return std::unexpected(SDL_GetError());
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Texture* texture = SDL_CreateTexture(renderer,
|
|
||||||
SDL_PIXELFORMAT_RGBA32,
|
|
||||||
SDL_TEXTUREACCESS_STREAMING,
|
|
||||||
width,
|
|
||||||
height);
|
|
||||||
|
|
||||||
if (texture == nullptr) {
|
|
||||||
return std::unexpected(SDL_GetError());
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_RenderPresent(renderer);
|
|
||||||
|
|
||||||
return std::make_unique<IoDevice>(profile, window, renderer, texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
IoDevice::~IoDevice()
|
|
||||||
{
|
|
||||||
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 IoDevice::set_char(uint16_t offset, uint8_t value)
|
|
||||||
-> std::expected<void, std::string>
|
|
||||||
{
|
|
||||||
auto lock = std::lock_guard(mx);
|
|
||||||
|
|
||||||
SDL_Color* buffer;
|
|
||||||
int pitch;
|
|
||||||
int res = SDL_LockTexture(m_texture, NULL, (void**)&buffer, &pitch);
|
|
||||||
if (res != 0) {
|
|
||||||
return std::unexpected(SDL_GetError());
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto white = SDL_Color { 0xff, 0xff, 0xff, 0xff };
|
|
||||||
constexpr auto black = SDL_Color { 0x00, 0x00, 0x00, 0xff };
|
|
||||||
|
|
||||||
int ch_size = m_profile.ch_size_px();
|
|
||||||
int px_size = m_profile.px_size_raw_px();
|
|
||||||
int raw_width = m_profile.width_raw_px();
|
|
||||||
int width_ch = m_profile.width_ch();
|
|
||||||
|
|
||||||
auto set_pixel = [&](int x, int y, bool ch) {
|
|
||||||
int raw_base_x = x * px_size;
|
|
||||||
int raw_base_y = y * px_size;
|
|
||||||
|
|
||||||
for (int raw_px_y = 0; raw_px_y < px_size; ++raw_px_y) {
|
|
||||||
for (int raw_px_x = 0; raw_px_x < px_size; ++raw_px_x) {
|
|
||||||
|
|
||||||
int x = raw_base_x + raw_px_x;
|
|
||||||
int y = raw_base_y + raw_px_y;
|
|
||||||
|
|
||||||
buffer[y * raw_width + x] = ch ? white : black;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
uint64_t char_data = ::char_data(value);
|
|
||||||
|
|
||||||
int base_x = offset % width_ch * ch_size;
|
|
||||||
int base_y = offset / width_ch * ch_size;
|
|
||||||
|
|
||||||
for (int px_y = 0; px_y < ch_size; ++px_y) {
|
|
||||||
uint8_t row = char_data >> (7 - px_y) * ch_size & 0xff;
|
|
||||||
|
|
||||||
for (int px_x = 0; px_x < ch_size; ++px_x) {
|
|
||||||
bool ch = row >> (7 - px_x) & 1;
|
|
||||||
|
|
||||||
set_pixel(base_x + px_x, base_y + px_y, ch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_UnlockTexture(m_texture);
|
|
||||||
SDL_RenderCopy(m_renderer, m_texture, NULL, NULL);
|
|
||||||
|
|
||||||
SDL_RenderPresent(m_renderer);
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto IoDevice::poll_event()
|
|
||||||
-> std::unique_ptr<IoEvent>
|
|
||||||
{
|
|
||||||
std::lock_guard lock(mx);
|
|
||||||
|
|
||||||
SDL_Event event;
|
|
||||||
if (SDL_PollEvent(&event)) {
|
|
||||||
switch (event.type) {
|
|
||||||
case SDL_QUIT:
|
|
||||||
return std::make_unique<IoEvent>(IoEvent::Type::Quit, IoEvent::Quit{});
|
|
||||||
case SDL_KEYDOWN:
|
|
||||||
if (event.key.keysym.sym == SDLK_ESCAPE) {
|
|
||||||
return std::make_unique<IoEvent>(IoEvent::Type::Quit, IoEvent::Quit{});
|
|
||||||
}
|
|
||||||
return std::make_unique<IoEvent>(
|
|
||||||
IoEvent::Type::KeyPress,
|
|
||||||
IoEvent::KeyPress { static_cast<uint16_t>(event.key.keysym.scancode) });
|
|
||||||
case SDL_KEYUP:
|
|
||||||
return std::make_unique<IoEvent>(
|
|
||||||
IoEvent::Type::KeyRelease,
|
|
||||||
IoEvent::KeyRelease { static_cast<uint16_t>(event.key.keysym.scancode) });
|
|
||||||
// default:
|
|
||||||
// std::println(stderr, "warning: unhandled event '{}'", event.type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <expected>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
struct SDL_Window;
|
|
||||||
struct SDL_Renderer;
|
|
||||||
struct SDL_Texture;
|
|
||||||
|
|
||||||
namespace vc5 {
|
|
||||||
|
|
||||||
namespace events {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class IoEvent {
|
|
||||||
public:
|
|
||||||
enum class Type {
|
|
||||||
Quit,
|
|
||||||
KeyPress,
|
|
||||||
KeyRelease,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Quit{};
|
|
||||||
|
|
||||||
struct KeyPress{ uint16_t key; };
|
|
||||||
|
|
||||||
struct KeyRelease{ uint16_t key; };
|
|
||||||
|
|
||||||
using Data = std::variant<Quit, KeyPress, KeyRelease>;
|
|
||||||
|
|
||||||
explicit IoEvent(Type type, Data data)
|
|
||||||
: m_type(type)
|
|
||||||
, m_data(std::move(data))
|
|
||||||
{}
|
|
||||||
|
|
||||||
Type type() const { return m_type; }
|
|
||||||
auto as_quit() -> Quit& { return std::get<Quit>(m_data); }
|
|
||||||
auto as_key_press() -> KeyPress& { return std::get<KeyPress>(m_data); }
|
|
||||||
auto as_key_release() -> KeyRelease& { return std::get<KeyRelease>(m_data); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Type m_type;
|
|
||||||
Data m_data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ScreenProfile {
|
|
||||||
int m_px_size_raw_px = 4;
|
|
||||||
int m_ch_size_px = 8;
|
|
||||||
int m_width_ch = 40;
|
|
||||||
int m_height_ch = 24;
|
|
||||||
|
|
||||||
constexpr int px_size_raw_px() const { return m_px_size_raw_px; }
|
|
||||||
constexpr int ch_size_px() const { return m_ch_size_px; }
|
|
||||||
|
|
||||||
constexpr int width_ch() const { return m_width_ch; }
|
|
||||||
constexpr int height_ch() const { return m_height_ch; }
|
|
||||||
|
|
||||||
constexpr int width_raw_px() const { return px_size_raw_px() * ch_size_px() * width_ch(); }
|
|
||||||
constexpr int height_raw_px() const { return px_size_raw_px() * ch_size_px() * height_ch(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
class IoDevice {
|
|
||||||
public:
|
|
||||||
static auto create()
|
|
||||||
-> std::expected<std::unique_ptr<IoDevice>, std::string>;
|
|
||||||
|
|
||||||
explicit IoDevice(ScreenProfile profile,
|
|
||||||
SDL_Window* window,
|
|
||||||
SDL_Renderer* renderer,
|
|
||||||
SDL_Texture* texture)
|
|
||||||
: m_profile(profile)
|
|
||||||
, m_window(window)
|
|
||||||
, m_renderer(renderer)
|
|
||||||
, m_texture(texture)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
~IoDevice();
|
|
||||||
|
|
||||||
auto set_char(uint16_t offset, uint8_t value)
|
|
||||||
-> std::expected<void, std::string>;
|
|
||||||
|
|
||||||
auto poll_event() -> std::unique_ptr<IoEvent>;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::mutex mx;
|
|
||||||
|
|
||||||
ScreenProfile m_profile;
|
|
||||||
SDL_Window* m_window;
|
|
||||||
SDL_Renderer* m_renderer;
|
|
||||||
SDL_Texture* m_texture;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
35
src/main.cpp
35
src/main.cpp
@ -1,32 +1,18 @@
|
|||||||
#include "builder.hpp"
|
#include "builder.hpp"
|
||||||
#include "io_device.hpp"
|
#include "screen.hpp"
|
||||||
#include <SDL2/SDL_main.h>
|
#include <SDL2/SDL_main.h>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <cstdlib>
|
|
||||||
#include <thread>
|
|
||||||
#include <chrono>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
|
|
||||||
using namespace vc5;
|
using namespace vc5;
|
||||||
using R = Reg;
|
using R = Reg;
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
auto device = IoDevice::create().value();
|
auto screen = Screen::create(4, 8 * 40, 8 * 24);
|
||||||
|
screen.value()->set_char(0, 'A');
|
||||||
auto message = std::string("A-0123456789");
|
|
||||||
uint16_t offset = 0;
|
|
||||||
for (auto ch : message) {
|
|
||||||
device->set_char(offset, static_cast<uint8_t>(ch)).value();
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr R r0 = R::R0;
|
constexpr R r0 = R::R0;
|
||||||
constexpr R r1 = R::R1;
|
constexpr R r1 = R::R1;
|
||||||
@ -82,19 +68,4 @@ int main()
|
|||||||
for (uint16_t i = 0; i < 32; i += 2) {
|
for (uint16_t i = 0; i < 32; i += 2) {
|
||||||
std::println("{: 4x}\t{}", i, vm.word(i));
|
std::println("{: 4x}\t{}", i, vm.word(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
auto event = device->poll_event();
|
|
||||||
|
|
||||||
if (event && event->type() == IoEvent::Type::Quit) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" const char* __asan_default_options(void)
|
|
||||||
{
|
|
||||||
return "detect_leaks=0";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
160
src/screen.cpp
Normal file
160
src/screen.cpp
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
#include "screen.hpp"
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_error.h>
|
||||||
|
#include <SDL2/SDL_render.h>
|
||||||
|
#include <SDL2/SDL_video.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
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::unique_ptr<Screen>, 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<Screen>(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<void, std::string>
|
||||||
|
{
|
||||||
|
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 {};
|
||||||
|
}
|
||||||
61
src/screen.hpp
Normal file
61
src/screen.hpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <expected>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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::unique_ptr<Screen>, 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<void, std::string>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex mx;
|
||||||
|
|
||||||
|
ScreenProfile m_profile;
|
||||||
|
SDL_Window* m_window;
|
||||||
|
SDL_Renderer* m_renderer;
|
||||||
|
SDL_Texture* m_texture;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
28
src/vm.cpp
28
src/vm.cpp
@ -47,7 +47,7 @@ int VM::run_instruction()
|
|||||||
ip,
|
ip,
|
||||||
ins.operator uint16_t(),
|
ins.operator uint16_t(),
|
||||||
op_str(op),
|
op_str(op),
|
||||||
m_regs[reg_value(Reg::R0)]);
|
m_regs[reg_id(Reg::R0)]);
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case Op::Nop:
|
case Op::Nop:
|
||||||
@ -80,7 +80,7 @@ int VM::run_instruction()
|
|||||||
|
|
||||||
if ((addr & 0b1) != 0) {
|
if ((addr & 0b1) != 0) {
|
||||||
std::println(stderr, "error: invalid address alignment");
|
std::println(stderr, "error: invalid address alignment");
|
||||||
*m_rfl |= 1 << flag_value(Flag::Err);
|
*m_rfl |= 1 << flag_id(Flag::Err);
|
||||||
goto halt_execution;
|
goto halt_execution;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ int VM::run_instruction()
|
|||||||
|
|
||||||
if ((addr & 0b1) != 0) {
|
if ((addr & 0b1) != 0) {
|
||||||
std::println(stderr, "error: invalid address alignment");
|
std::println(stderr, "error: invalid address alignment");
|
||||||
*m_rfl |= 1 << flag_value(Flag::Err);
|
*m_rfl |= 1 << flag_id(Flag::Err);
|
||||||
goto halt_execution;
|
goto halt_execution;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,19 +127,19 @@ int VM::run_instruction()
|
|||||||
auto op2 = ins.op2_or_imm();
|
auto op2 = ins.op2_or_imm();
|
||||||
|
|
||||||
if (op1 == op2) {
|
if (op1 == op2) {
|
||||||
*m_rfl |= 1u << flag_value(Flag::Eq);
|
*m_rfl |= 1u << flag_id(Flag::Eq);
|
||||||
} else {
|
} else {
|
||||||
*m_rfl &= (uint16_t)~(1u << flag_value(Flag::Eq));
|
*m_rfl &= (uint16_t)~(1u << flag_id(Flag::Eq));
|
||||||
}
|
}
|
||||||
if (op1 < op2) {
|
if (op1 < op2) {
|
||||||
*m_rfl |= 1u << flag_value(Flag::Be);
|
*m_rfl |= 1u << flag_id(Flag::Be);
|
||||||
} else {
|
} else {
|
||||||
*m_rfl &= (uint16_t)~(1u << flag_value(Flag::Be));
|
*m_rfl &= (uint16_t)~(1u << flag_id(Flag::Be));
|
||||||
}
|
}
|
||||||
if ((int16_t)op1 < (int16_t)op2) {
|
if ((int16_t)op1 < (int16_t)op2) {
|
||||||
*m_rfl |= 1u << flag_value(Flag::Lt);
|
*m_rfl |= 1u << flag_id(Flag::Lt);
|
||||||
} else {
|
} else {
|
||||||
*m_rfl &= (uint16_t)~(1u << flag_value(Flag::Lt));
|
*m_rfl &= (uint16_t)~(1u << flag_id(Flag::Lt));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -202,15 +202,6 @@ halt_execution:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t VM::eat()
|
|
||||||
{
|
|
||||||
uint16_t ins = 0;
|
|
||||||
ins |= static_cast<uint16_t>(m_mem[*m_rip]) << 8;
|
|
||||||
ins |= m_mem[*m_rip + 1];
|
|
||||||
*m_rip += 2;
|
|
||||||
return ins;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* vc5::op_str(Op op)
|
const char* vc5::op_str(Op op)
|
||||||
{
|
{
|
||||||
switch (op) {
|
switch (op) {
|
||||||
@ -257,4 +248,3 @@ const char* vc5::op_str(Op op)
|
|||||||
}
|
}
|
||||||
return ">.<";
|
return ">.<";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
37
src/vm.hpp
37
src/vm.hpp
@ -47,11 +47,6 @@ enum class Reg : uint16_t {
|
|||||||
Rip = 9,
|
Rip = 9,
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr uint16_t reg_value(Reg reg)
|
|
||||||
{
|
|
||||||
return std::to_underlying(reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class Flag : uint16_t {
|
enum class Flag : uint16_t {
|
||||||
Zero = 0,
|
Zero = 0,
|
||||||
Eq = 1,
|
Eq = 1,
|
||||||
@ -60,10 +55,7 @@ enum class Flag : uint16_t {
|
|||||||
Err = 4,
|
Err = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr uint16_t flag_value(Flag flag)
|
struct Ins;
|
||||||
{
|
|
||||||
return std::to_underlying(flag);
|
|
||||||
}
|
|
||||||
|
|
||||||
class VM {
|
class VM {
|
||||||
public:
|
public:
|
||||||
@ -76,7 +68,7 @@ public:
|
|||||||
|
|
||||||
uint16_t reg(Reg reg) const
|
uint16_t reg(Reg reg) const
|
||||||
{
|
{
|
||||||
return m_regs[reg_value(reg)];
|
return m_regs[reg_id(reg)];
|
||||||
};
|
};
|
||||||
|
|
||||||
uint16_t word(uint16_t addr) const
|
uint16_t word(uint16_t addr) const
|
||||||
@ -86,7 +78,6 @@ public:
|
|||||||
value |= m_mem[addr + 1];
|
value |= m_mem[addr + 1];
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t byte(uint16_t addr) const
|
uint8_t byte(uint16_t addr) const
|
||||||
{
|
{
|
||||||
return m_mem[addr];
|
return m_mem[addr];
|
||||||
@ -95,14 +86,32 @@ public:
|
|||||||
private:
|
private:
|
||||||
friend struct Ins;
|
friend struct Ins;
|
||||||
|
|
||||||
|
static constexpr uint16_t reg_id(Reg reg)
|
||||||
|
{
|
||||||
|
return std::to_underlying(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr uint16_t flag_id(Flag flag)
|
||||||
|
{
|
||||||
|
return std::to_underlying(flag);
|
||||||
|
}
|
||||||
|
|
||||||
int run_instruction();
|
int run_instruction();
|
||||||
uint16_t eat();
|
|
||||||
|
inline uint16_t eat()
|
||||||
|
{
|
||||||
|
uint16_t ins = 0;
|
||||||
|
ins |= static_cast<uint16_t>(m_mem[*m_rip]) << 8;
|
||||||
|
ins |= m_mem[*m_rip + 1];
|
||||||
|
*m_rip += 2;
|
||||||
|
return ins;
|
||||||
|
}
|
||||||
|
|
||||||
std::array<uint8_t, 65536> m_mem = {};
|
std::array<uint8_t, 65536> m_mem = {};
|
||||||
std::array<uint16_t, 10> m_regs = {};
|
std::array<uint16_t, 10> m_regs = {};
|
||||||
|
|
||||||
uint16_t* m_rfl = &m_regs[reg_value(Reg::Rfl)];
|
uint16_t* m_rfl = &m_regs[reg_id(Reg::Rfl)];
|
||||||
uint16_t* m_rip = &m_regs[reg_value(Reg::Rip)];
|
uint16_t* m_rip = &m_regs[reg_id(Reg::Rip)];
|
||||||
|
|
||||||
bool m_halted = false;
|
bool m_halted = false;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user