Handling host events in video controllers.

This commit is contained in:
Maxim Poliakovski 2023-04-01 17:38:11 +02:00
parent f1c898b17e
commit 3e545bdef9
6 changed files with 140 additions and 77 deletions

View File

@ -1,6 +1,6 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-22 divingkatae and maximum
Copyright (C) 2018-23 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
@ -40,7 +40,7 @@ AtiMach64Gx::AtiMach64Gx()
{
supports_types(HWCompType::MMIO_DEV | HWCompType::PCI_DEV);
/* set up PCI configuration space header */
// set up PCI configuration space header
this->vendor_id = PCI_VENDOR_ATI;
this->device_id = ATI_MACH64_GX_DEV_ID;
this->class_rev = (0x030000 << 8) | 3;
@ -53,7 +53,8 @@ AtiMach64Gx::AtiMach64Gx()
// declare expansion ROM containing FCode and Mac OS drivers
if (this->attach_exp_rom_image(std::string("113-32900-004_Apple_MACH64.bin"))) {
LOG_F(WARNING, "AtiMach64Gx: could not load ROM - this device won't work properly!");
ABORT_F("%s: could not load ROM - this device won't work properly!",
this->name.c_str());
}
// initialize display identification
@ -180,6 +181,15 @@ void AtiMach64Gx::write_reg(uint32_t offset, uint32_t value, uint32_t size)
switch (offset & ~3) {
case ATI_CRTC_GEN_CNTL:
if (value & 0x40) {
this->mm_regs[ATI_CRTC_GEN_CNTL] |= (1 << 6);
this->blank_on = true;
this->blank_display();
} else {
this->mm_regs[ATI_CRTC_GEN_CNTL] &= ~(1 << 6);
this->blank_on = false;
}
crtc_en = (this->mm_regs[ATI_CRTC_GEN_CNTL+3] >> 1) & 1;
if (crtc_en != this->crtc_enable) {
if (!crtc_en) {
@ -246,7 +256,7 @@ void AtiMach64Gx::enable_crtc_internal()
LOG_F(WARNING, "AtiMach64Gx: display window resizing not implemented yet!");
}
/* calculate display refresh rate */
// calculate display refresh rate
int hori_total = (this->mm_regs[ATI_CRTC_H_TOTAL_DISP] + 1) * 8;
int vert_total = (READ_WORD_LE_A(&this->mm_regs[ATI_CRTC_V_TOTAL_DISP]) & 0x7FFUL) + 1;
@ -270,16 +280,18 @@ void AtiMach64Gx::enable_crtc_internal()
ABORT_F("AtiMach64Gx: unsupported pixel depth %d", this->pixel_depth);
}
if (this->refresh_task_id) {
TimerManager::get_instance()->cancel_timer(this->refresh_task_id);
}
uint64_t refresh_interval = static_cast<uint64_t>(1.0f / this->refresh_rate * NS_PER_SEC + 0.5);
TimerManager::get_instance()->add_cyclic_timer(
this->refresh_task_id = TimerManager::get_instance()->add_cyclic_timer(
refresh_interval,
[this]() {
this->update_screen();
}
);
this->update_screen();
this->crtc_on = true;
this->crtc_enable = 1;

View File

@ -534,15 +534,17 @@ void ATIRage::crtc_enable() {
LOG_F(INFO, "Pixel (dot) clock: %f MHz", this->pixel_clock * 1e-6);
LOG_F(INFO, "Refresh rate: %f Hz", this->refresh_rate);
if (this->refresh_task_id) {
TimerManager::get_instance()->cancel_timer(this->refresh_task_id);
}
uint64_t refresh_interval = static_cast<uint64_t>(1.0f / this->refresh_rate * NS_PER_SEC + 0.5);
TimerManager::get_instance()->add_cyclic_timer(
this->refresh_task_id = TimerManager::get_instance()->add_cyclic_timer(
refresh_interval,
[this]() {
this->update_screen();
}
);
this->update_screen();
} else {
LOG_F(WARNING, "ATI Rage: VLCK source != VPLL!");
}

View File

@ -293,16 +293,20 @@ void ControlVideo::enable_display()
/ (new_height + vert_blank);
LOG_F(INFO, "Control: refresh rate set to %f Hz", this->refresh_rate);
if (this->refresh_task_id) {
TimerManager::get_instance()->cancel_timer(this->refresh_task_id);
}
// set up periodic timer for display updates
uint64_t refresh_interval = static_cast<uint64_t>(1.0f / refresh_rate * NS_PER_SEC + 0.5);
TimerManager::get_instance()->add_cyclic_timer(
this->refresh_task_id = TimerManager::get_instance()->add_cyclic_timer(
refresh_interval,
[this]() {
this->update_screen();
}
);
this->update_screen();
this->blank_on = false;
LOG_F(INFO, "Control: display enabled");
this->crtc_on = true;

View File

@ -46,6 +46,15 @@ PdmOnboardVideo::PdmOnboardVideo()
void PdmOnboardVideo::set_video_mode(uint8_t new_mode)
{
if (this->blanking != (new_mode & 0x80)) {
if (new_mode & 0x80) {
this->disable_video_internal();
} else {
this->blank_on = false;
this->blanking = 0;
}
}
switch(new_mode & 0x7F) {
case PdmVideoMode::Portrait:
case PdmVideoMode::Rgb12in:
@ -56,20 +65,14 @@ void PdmOnboardVideo::set_video_mode(uint8_t new_mode)
this->video_mode = new_mode & 0x1F;
LOG_F(INFO, "PDM-Video: video mode set to 0x%X", this->video_mode);
}
if (this->blanking != (new_mode & 0x80)) {
this->blanking = new_mode & 0x80;
if (this->blanking) {
this->disable_video_internal();
} else {
this->enable_video_internal();
}
if (!this->blanking) {
this->enable_video_internal();
}
break;
default:
LOG_F(9, "PDM-Video: video disabled, new mode = 0x%X", new_mode & 0x1F);
this->video_mode = new_mode & 0x1F;
this->blanking = 0x80;
this->crtc_on = false;
this->disable_video_internal();
}
}
@ -207,27 +210,32 @@ void PdmOnboardVideo::enable_video_internal()
this->set_depth_internal(new_width);
if (this->refresh_task_id) {
TimerManager::get_instance()->cancel_timer(this->refresh_task_id);
}
// set up video refresh timer
double refresh_rate_hz = (double)(this->pixel_clock) / (new_width + hori_blank) / (new_height + vert_blank);
LOG_F(INFO, "PDM-Video: refresh rate set to %f Hz", refresh_rate_hz);
uint64_t refresh_interval = static_cast<uint64_t>(1.0f / refresh_rate_hz * NS_PER_SEC + 0.5);
TimerManager::get_instance()->add_cyclic_timer(
this->refresh_task_id = TimerManager::get_instance()->add_cyclic_timer(
refresh_interval,
[this]() {
this->update_screen();
}
);
this->update_screen();
LOG_F(INFO, "PDM-Video: video enabled");
LOG_F(9, "PDM-Video: video enabled");
this->crtc_on = true;
}
void PdmOnboardVideo::disable_video_internal()
{
this->blank_on = true;
this->blank_display();
this->blanking = 0x80;
this->crtc_on = false;
LOG_F(INFO, "PDM-Video: video disabled");
LOG_F(9, "PDM-Video: video disabled");
}
/** Ariel II has a weird 1bpp mode where a white pixel is mapped to

View File

@ -1,6 +1,6 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-21 divingkatae and maximum
Copyright (C) 2018-23 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
@ -21,8 +21,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
/** @file Video Conroller base class implementation. */
#include "videoctrl.h"
#include <core/hostevents.h>
#include <devices/video/videoctrl.h>
#include <loguru.hpp>
#include <memaccess.h>
#include <SDL.h>
#include <chrono>
@ -30,51 +32,15 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
VideoCtrlBase::VideoCtrlBase(int width, int height)
{
LOG_F(INFO, "Create display window...");
EventManager::get_instance()->register_handler(EventType::EVENT_WINDOW,
[this](const BaseEvent& event) {
const WindowEvent& wnd_event = static_cast<const WindowEvent&>(event);
if (wnd_event.sub_type == SDL_WINDOWEVENT_SIZE_CHANGED &&
wnd_event.window_id == this->disp_wnd_id)
this->resizing = false;
});
this->active_width = width;
this->active_height = height;
// Create display window
this->display_wnd = SDL_CreateWindow(
"DingusPPC Display",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
this->active_width,
this->active_height,
SDL_WINDOW_OPENGL
);
if (this->display_wnd == NULL) {
LOG_F(ERROR, "Display: SDL_CreateWindow failed with %s", SDL_GetError());
}
this->renderer = SDL_CreateRenderer(this->display_wnd, -1, SDL_RENDERER_ACCELERATED);
if (this->renderer == NULL) {
LOG_F(ERROR, "Display: SDL_CreateRenderer failed with %s", SDL_GetError());
}
SDL_SetRenderDrawColor(this->renderer, 0, 0, 0, 255);
SDL_RenderClear(this->renderer);
SDL_RenderPresent(this->renderer);
// Stupidly poll for 10 events.
// Otherwise no window will be shown on mac OS!
SDL_Event e;
for (int i = 0; i < 10; i++) {
SDL_PollEvent(&e);
}
this->disp_texture = SDL_CreateTexture(
this->renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
width, height
);
if (this->disp_texture == NULL) {
LOG_F(ERROR, "Display: SDL_CreateTexture failed with %s", SDL_GetError());
}
this->create_display_window(width, height);
}
VideoCtrlBase::~VideoCtrlBase()
@ -92,6 +58,61 @@ VideoCtrlBase::~VideoCtrlBase()
}
}
void VideoCtrlBase::create_display_window(int width, int height)
{
if (!this->display_wnd) { // create display window
this->display_wnd = SDL_CreateWindow(
"DingusPPC Display",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
width, height,
SDL_WINDOW_OPENGL
);
this->disp_wnd_id = SDL_GetWindowID(this->display_wnd);
if (this->display_wnd == NULL) {
ABORT_F("Display: SDL_CreateWindow failed with %s", SDL_GetError());
}
this->renderer = SDL_CreateRenderer(this->display_wnd, -1, SDL_RENDERER_ACCELERATED);
if (this->renderer == NULL) {
ABORT_F("Display: SDL_CreateRenderer failed with %s", SDL_GetError());
}
this->blank_on = true; // TODO: should be true!
this->blank_display();
} else { // resize display window
SDL_SetWindowSize(this->display_wnd, width, height);
this->resizing = true;
}
this->active_width = width;
this->active_height = height;
if (this->disp_texture) {
SDL_DestroyTexture(this->disp_texture);
}
this->disp_texture = SDL_CreateTexture(
this->renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
width, height
);
if (this->disp_texture == NULL) {
ABORT_F("Display: SDL_CreateTexture failed with %s", SDL_GetError());
}
}
void VideoCtrlBase::blank_display() {
SDL_SetRenderDrawColor(this->renderer, 0, 0, 0, 255);
SDL_RenderClear(this->renderer);
SDL_RenderPresent(this->renderer);
}
void VideoCtrlBase::update_screen()
{
uint8_t* dst_buf;
@ -99,6 +120,14 @@ void VideoCtrlBase::update_screen()
//auto start_time = std::chrono::steady_clock::now();
if (this->resizing)
return;
if (this->blank_on) {
this->blank_display();
return;
}
SDL_LockTexture(this->disp_texture, NULL, (void **)&dst_buf, &dst_pitch);
// texture update callback to get ARGB data from guest framebuffer

View File

@ -1,6 +1,6 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-21 divingkatae and maximum
Copyright (C) 2018-23 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
@ -34,6 +34,8 @@ public:
VideoCtrlBase(int width = 640, int height = 480);
~VideoCtrlBase();
void create_display_window(int width, int height);
void blank_display();
void update_screen(void);
void get_palette_colors(uint8_t index, uint8_t& r, uint8_t& g, uint8_t& b,
@ -45,10 +47,14 @@ public:
virtual void convert_frame_8bpp(uint8_t *dst_buf, int dst_pitch);
protected:
/* CRT controller parameters */
// CRT controller parameters
bool crtc_on = false;
bool blank_on = true;
bool resizing = false;
int active_width; // width of the visible display area
int active_height; // height of the visible display area
int hori_total;
int vert_total;
int pixel_depth;
float pixel_clock;
float refresh_rate;
@ -58,13 +64,15 @@ protected:
// Framebuffer parameters
uint8_t* fb_ptr;
int fb_pitch;
uint32_t refresh_task_id = 0;
std::function<void(uint8_t *dst_buf, int dst_pitch)> convert_fb_cb;
private:
SDL_Window *display_wnd;
SDL_Renderer *renderer;
SDL_Texture *disp_texture;
uint32_t disp_wnd_id = 0;
SDL_Window* display_wnd = 0;
SDL_Renderer* renderer = 0;
SDL_Texture* disp_texture = 0;
};
#endif // VIDEO_CTRL_H