diff options
author | Oxore <oxore@protonmail.com> | 2022-10-19 02:28:03 +0300 |
---|---|---|
committer | Oxore <oxore@protonmail.com> | 2022-10-19 02:28:03 +0300 |
commit | eddd0a826b1720c26b0ac5df0269db3982bc8f35 (patch) | |
tree | 94548f986eb190c734315db252e57a79c118031f | |
parent | ae9a7aef2f0422f6872e6ae27e4e4e2084d8ce8f (diff) |
Begin implementing VDP rendering
-rw-r--r-- | CMakeLists.txt | 10 | ||||
-rw-r--r-- | emulator.cpp | 17 | ||||
-rw-r--r-- | graphics.cpp | 83 | ||||
-rw-r--r-- | graphics.hpp | 26 | ||||
-rw-r--r-- | vdp.cpp | 6 | ||||
-rw-r--r-- | vdp.hpp | 6 |
6 files changed, 143 insertions, 5 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 5edf23b..994b703 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ set(emulator_sources bus.cpp emulator.cpp gdbremote_parser.cpp + graphics.cpp m68k_debugging.cpp vdp.cpp ) @@ -28,6 +29,7 @@ set(musashi_m68k_sources musashi-m68k/softfloat/softfloat.c ${CMAKE_CURRENT_BINARY_DIR}/m68kops.c ) + add_executable(m68kmake musashi-m68k/m68kmake.c) target_include_directories(m68kmake PRIVATE musashi-m68k) add_custom_command( @@ -45,12 +47,20 @@ target_include_directories(musashi_m68k PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ) +# TODO make SDL2 optional for headless mode +find_package(SDL2 REQUIRED) + add_executable(emulator ${emulator_sources}) target_link_libraries(emulator musashi_m68k) +# TODO make SDL2 optional for headless mode +target_include_directories(emulator PRIVATE ${SDL2_INCLUDE_DIRS}) +# TODO make SDL2 optional for headless mode +target_link_libraries(emulator ${SDL2_LIBRARIES}) target_compile_definitions(emulator PRIVATE DEBUG_TRACE_INSTRUCTIONS=0 DEBUG_TRACE_GDB_REMOTE=0 DEBUG_TRACE_VDP_ACCESS=1 + HAS_GRAPHICS=1 ) ## Target for GDB Remote Debugging protocol implementation testing diff --git a/emulator.cpp b/emulator.cpp index 3ec756d..70927bd 100644 --- a/emulator.cpp +++ b/emulator.cpp @@ -2,6 +2,7 @@ */ #include "bus.hpp" +#include "graphics.hpp" #include "vdp.hpp" #include "m68k_debugging.hpp" #include "gdbremote_parser.hpp" @@ -433,7 +434,7 @@ void ParseAndReact( } } -int emulator(M68KDebuggingControl& m68k_debug) +int emulator(M68KDebuggingControl& m68k_debug, Graphics& graphics) { const int port = 3333; const int socket_fd = setup_socket(port); @@ -483,8 +484,9 @@ int emulator(M68KDebuggingControl& m68k_debug) } if (m68k_debug.IsRunning()) { do { - m68k_execute(100000); + m68k_execute(1000000); } while(!g_vdp.Scanline()); + graphics.Render(g_vdp); } if (m68k_debug.HasBreakpoint()) { m68k_debug.SetRunning(false); @@ -501,7 +503,7 @@ int emulator(M68KDebuggingControl& m68k_debug) clock_nanosleep(CLOCK_MONOTONIC, 0, &kOneMillisecond, nullptr); } close(conn_fd); - g_no_ack_mode = false; + g_no_ack_mode = false; // TODO move to GDB::ExchangeContext } close(socket_fd); return 0; @@ -525,12 +527,17 @@ int main(int argc, char* argv[]) exit_error("Error reading %s", argv[1]); printf("Read into ROM %zu bytes\n", fread_ret); + Graphics graphics{}; + if (!graphics.IsOk()) { + return EXIT_FAILURE; + } + m68k_init(); m68k_set_cpu_type(M68K_CPU_TYPE_68000); m68k_pulse_reset(); m68k_execute(1); // Skip reset cycles - emulator(g_m68k_debug); + emulator(g_m68k_debug, graphics); - return 0; + return EXIT_SUCCESS; } diff --git a/graphics.cpp b/graphics.cpp new file mode 100644 index 0000000..2455630 --- /dev/null +++ b/graphics.cpp @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: Unlicense + */ + +#include "graphics.hpp" +#include "vdp.hpp" + +#include <cstdlib> + +Graphics::Graphics() +{ +#if HAS_GRAPHICS == 1 + SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); + + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); + return; + } + _window = SDL_CreateWindow("Gut (SEGA MD/G emulator)", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + VDP().render_width, VDP().render_height, + SDL_WINDOW_RESIZABLE); + if (!_window) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create window: %s\n", SDL_GetError()); + SDL_Quit(); + return; + } + _renderer = SDL_CreateRenderer(_window, -1, 0); + if (!_renderer) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create renderer: %s\n", SDL_GetError()); + SDL_Quit(); + return; + } + _render_texture = SDL_CreateTexture( + _renderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, + VDP().render_width, + VDP().render_height); + if (!_render_texture) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create texture: %s\n", SDL_GetError()); + SDL_Quit(); + return; + } +#endif + _initialized_ok = true; +} + +Graphics::~Graphics() +{ +#if HAS_GRAPHICS == 1 + SDL_DestroyTexture(_render_texture); + SDL_DestroyRenderer(_renderer); + SDL_DestroyWindow(_window); + SDL_Quit(); +#endif +} + +void Graphics::Render(const VDP& vdp) +{ + const uint8_t* buffer = vdp.GetRenderedBuffer(); +#if HAS_GRAPHICS == 1 + void* pixels; + int pitch; + if (SDL_LockTexture(_render_texture, NULL, &pixels, &pitch) < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't lock texture: %s\n", SDL_GetError()); + abort(); + } + for (int row = 0; (size_t)row < VDP().render_height; row++) { + uint32_t *dst = (uint32_t*)((uint8_t*)pixels + row * pitch); + for (int col = 0; (size_t)col < VDP().render_width; col++, buffer += 4) { + const uint32_t color = ((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); + *dst++ = color; + } + } + SDL_UnlockTexture(_render_texture); + SDL_RenderClear(_renderer); + SDL_RenderCopy(_renderer, _render_texture, NULL, NULL); + SDL_RenderPresent(_renderer); +#else + (void) buffer; +#endif +} diff --git a/graphics.hpp b/graphics.hpp new file mode 100644 index 0000000..a88cd61 --- /dev/null +++ b/graphics.hpp @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: Unlicense + */ + +#pragma once + +class VDP; + +#if defined(HAS_GRAPHICS) && HAS_GRAPHICS == 1 +# include "SDL.h" +#endif + +class Graphics { +public: + Graphics(); + ~Graphics(); + bool IsOk() const { return _initialized_ok; } + void Render(const VDP&); + +private: + bool _initialized_ok{}; +#if defined(HAS_GRAPHICS) && HAS_GRAPHICS == 1 + SDL_Renderer* _renderer{}; + SDL_Window* _window{}; + SDL_Texture* _render_texture{}; +#endif +}; @@ -141,6 +141,12 @@ bool VDP::Scanline() if (!MODESET2_DISP_GET(mode_set_2)) { return true; } + for (size_t i = 0; i < render_width; i++) { + _rendered_buffer[render_width * _lines_counter * 4 + i + 0] = 0xff; // Alpha + _rendered_buffer[render_width * _lines_counter * 4 + i + 1] = 0xff; // Red + _rendered_buffer[render_width * _lines_counter * 4 + i + 2] = 0x7f; // Green + _rendered_buffer[render_width * _lines_counter * 4 + i + 3] = 0x7f; // Blue + } _lines_counter++; const uint16_t lines_per_screen = _status.pal_mode ? kLinesPerScreenPAL : kLinesPerScreenNTSC; if (_lines_counter >= lines_per_screen) { @@ -15,9 +15,14 @@ class VDP { void Write(uint32_t offset, enum bitness, uint32_t value); bool Scanline(); // Returns true if display disabled or vblank happened void Reset(); + const uint8_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; + private: struct StatusRegister { bool fifo_not_empty{}; @@ -107,4 +112,5 @@ class VDP { uint8_t _vram[kVRAMSize]{}; uint8_t _cram[kCRAMSize]{}; uint8_t _vsram[kVSRAMSize]{}; + uint8_t _rendered_buffer[render_buffer_size]{}; }; |