From 958d3ee96a986b99a1b7e472a2858319d9acd0ec Mon Sep 17 00:00:00 2001 From: Maxim Poliakovski Date: Tue, 9 Nov 2021 13:40:13 +0100 Subject: [PATCH] Factor out common video controller code. --- devices/video/atirage.cpp | 58 +++------------ devices/video/atirage.h | 15 +--- devices/video/displayid.cpp | 82 +++----------------- devices/video/displayid.h | 10 +-- devices/video/videoctrl.cpp | 145 ++++++++++++++++++++++++++++++++++++ devices/video/videoctrl.h | 60 +++++++++++++++ 6 files changed, 227 insertions(+), 143 deletions(-) create mode 100644 devices/video/videoctrl.cpp create mode 100644 devices/video/videoctrl.h diff --git a/devices/video/atirage.cpp b/devices/video/atirage.cpp index 2f53154..441b15b 100644 --- a/devices/video/atirage.cpp +++ b/devices/video/atirage.cpp @@ -86,10 +86,12 @@ static const std::map mach64_reg_names = { }; -ATIRage::ATIRage(uint16_t dev_id, uint32_t mem_amount) : PCIDevice("ati-rage") { +ATIRage::ATIRage(uint16_t dev_id, uint32_t vmem_size_mb) + : PCIDevice("ati-rage"), VideoCtrlBase() +{ uint8_t asic_id; - this->vram_size = mem_amount << 20; + this->vram_size = vmem_size_mb << 20; // convert MBs to bytes /* allocate video RAM */ this->vram_ptr = new uint8_t[this->vram_size]; @@ -119,14 +121,10 @@ ATIRage::ATIRage(uint16_t dev_id, uint32_t mem_amount) : PCIDevice("ati-rage") { /* initialize display identification */ this->disp_id = new DisplayID(); - - //this->surface = new uint8_t[640 * 480]; } ATIRage::~ATIRage() { - //delete (this->surface); - if (this->vram_ptr) { delete this->vram_ptr; } @@ -272,6 +270,11 @@ void ATIRage::write_reg(uint32_t offset, uint32_t value, uint32_t size) { if ((this->block_io_regs[ATI_CRTC_GEN_CNTL+3] & 2) && !(this->block_io_regs[ATI_CRTC_GEN_CNTL] & 0x40)) { + int32_t src_offset = (READ_DWORD_LE_A(&this->block_io_regs[ATI_CRTC_OFF_PITCH]) & 0xFFFF) * 8; + + this->fb_pitch = ((READ_DWORD_LE_A(&this->block_io_regs[ATI_CRTC_OFF_PITCH])) >> 19) & 0x1FF8; + + this->fb_ptr = this->vram_ptr + src_offset; this->update_screen(); } } @@ -579,46 +582,3 @@ void ATIRage::draw_hw_cursor(uint8_t *dst_buf, int dst_pitch) { } } } - -void ATIRage::update_screen() { - uint8_t *src_buf, *dst_buf, *src_row, *dst_row, pix; - int src_pitch, dst_pitch; - - //auto start_time = std::chrono::steady_clock::now(); - - this->disp_id->get_disp_texture((void **)&dst_buf, &dst_pitch); - - uint32_t src_offset = (READ_DWORD_LE_A(&this->block_io_regs[ATI_CRTC_OFF_PITCH]) & 0xFFFF) * 8; - - src_pitch = ((READ_DWORD_LE_A(&this->block_io_regs[ATI_CRTC_OFF_PITCH])) >> 19) & 0x1FF8; - - src_buf = this->vram_ptr + src_offset; - - for (int h = 0; h < this->active_height; h++) { - src_row = &src_buf[h * src_pitch]; - dst_row = &dst_buf[h * dst_pitch]; - - for (int x = 0; x < this->active_width; x++) { - pix = src_row[x]; - dst_row[0] = this->palette[pix][2]; // B - dst_row[1] = this->palette[pix][1]; // G - dst_row[2] = this->palette[pix][0]; // R - dst_row[3] = 255; // A - dst_row += 4; - } - } - - // HW cursor data is stored at the beginning of the video memory - // HACK: use src_offset to recognize cursor data being ready - // Normally, we should check GEN_CUR_ENABLE bit in the GEN_TEST_CNTL register - if (src_offset > 0x400 && READ_DWORD_LE_A(&this->block_io_regs[ATI_CUR_OFFSET])) { - this->draw_hw_cursor(dst_buf + dst_pitch * 20 + 120, dst_pitch); - } - - this->disp_id->update_screen(); - - //auto end_time = std::chrono::steady_clock::now(); - //auto time_elapsed = std::chrono::duration_cast(end_time - start_time); - //LOG_F(INFO, "Display uodate took: %lld ns", time_elapsed.count()); - SDL_Delay(15); -} diff --git a/devices/video/atirage.h b/devices/video/atirage.h index db8d02f..90ac887 100644 --- a/devices/video/atirage.h +++ b/devices/video/atirage.h @@ -24,6 +24,7 @@ along with this program. If not, see . #include #include +#include #include @@ -192,9 +193,9 @@ constexpr auto BE_FB_OFFSET = 0x00800000UL; /* Offset to the big-endian frame b constexpr auto ATI_XTAL = 14318180.0f; // external crystal oscillator frequency -class ATIRage : public PCIDevice { +class ATIRage : public PCIDevice, public VideoCtrlBase { public: - ATIRage(uint16_t dev_id, uint32_t mem_amount); + ATIRage(uint16_t dev_id, uint32_t vmem_size_mb); ~ATIRage(); /* MMIODevice methods */ @@ -226,7 +227,6 @@ protected: void verbose_pixel_format(int crtc_index); void crtc_enable(); void draw_hw_cursor(uint8_t *dst_buf, int dst_pitch); - void update_screen(); private: uint8_t block_io_regs[2048] = {0}; @@ -235,13 +235,6 @@ private: uint8_t plls[64] = {0}; // internal PLL registers - /* CRT controller parameters */ - bool crtc_on = false; - int active_width; // width of the visible display area - int active_height; // height of the visible display area - float pixel_clock; - float refresh_rate; - /* Video RAM variables */ uint32_t vram_size; uint8_t* vram_ptr; @@ -250,8 +243,6 @@ private: DisplayID* disp_id; - uint8_t palette[256][4]; /* internal DAC palette in RGBA format */ int comp_index; /* color component index for DAC palette access */ - uint8_t *surface; }; #endif /* ATI_RAGE_H */ diff --git a/devices/video/displayid.cpp b/devices/video/displayid.cpp index 9614d45..b9a81ee 100644 --- a/devices/video/displayid.cpp +++ b/devices/video/displayid.cpp @@ -19,11 +19,13 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +/** @file DisplayID class implementation. */ + #include #include -#include -DisplayID::DisplayID() { +DisplayID::DisplayID() +{ /* Initialize Apple monitor codes */ this->std_sense_code = 6; this->ext_sense_code = 0x2B; @@ -38,78 +40,11 @@ DisplayID::DisplayID() { /* DDC sense mode is on by default */ this->i2c_on = true; - - LOG_F(INFO, "Create display window..."); - - // Create display window - this->display_wnd = SDL_CreateWindow( - "DingusPPC Display", - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - 640, - 480, - SDL_WINDOW_OPENGL - ); - - if (this->display_wnd == NULL) { - LOG_F(ERROR, "Display: SDL_CreateWindow failed with %s\n", 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\n", 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, - 640, 480 - ); - - if (this->disp_texture == NULL) { - LOG_F(ERROR, "Display: SDL_CreateTexture failed with %s\n", SDL_GetError()); - } } -DisplayID::~DisplayID() { - if (this->disp_texture) { - SDL_DestroyTexture(this->disp_texture); - } - if (this->renderer) { - SDL_DestroyRenderer(this->renderer); - } - - if (this->display_wnd) { - SDL_DestroyWindow(this->display_wnd); - } -} - -void DisplayID::get_disp_texture(void **pix_buf, int *pitch) { - SDL_LockTexture(this->disp_texture, NULL, pix_buf, pitch); -} - -void DisplayID::update_screen() { - SDL_UnlockTexture(this->disp_texture); - //SDL_SetRenderDrawColor(this->renderer, 255, 255, 255, 255); - SDL_RenderClear(this->renderer); - SDL_RenderCopy(this->renderer, this->disp_texture, NULL, NULL); - SDL_RenderPresent(this->renderer); -} - -uint16_t DisplayID::set_result(uint8_t sda, uint8_t scl) { +uint16_t DisplayID::set_result(uint8_t sda, uint8_t scl) +{ this->last_sda = sda; this->last_scl = scl; @@ -129,7 +64,8 @@ uint16_t DisplayID::set_result(uint8_t sda, uint8_t scl) { } -uint16_t DisplayID::read_monitor_sense(uint16_t data, uint16_t dirs) { +uint16_t DisplayID::read_monitor_sense(uint16_t data, uint16_t dirs) +{ uint8_t scl, sda; uint16_t result; @@ -168,7 +104,7 @@ uint16_t DisplayID::read_monitor_sense(uint16_t data, uint16_t dirs) { } -uint16_t DisplayID::update_ddc_i2c(uint8_t sda, uint8_t scl) //(uint16_t data, uint16_t dirs) +uint16_t DisplayID::update_ddc_i2c(uint8_t sda, uint8_t scl) { bool clk_gone_high = false; diff --git a/devices/video/displayid.h b/devices/video/displayid.h index 888ee4f..400d121 100644 --- a/devices/video/displayid.h +++ b/devices/video/displayid.h @@ -33,8 +33,6 @@ along with this program. If not, see . #ifndef DISPLAY_ID_H #define DISPLAY_ID_H -#include - #include /** I2C bus states. */ @@ -52,21 +50,15 @@ enum I2CState : uint8_t { class DisplayID { public: DisplayID(); - ~DisplayID(); + ~DisplayID() = default; uint16_t read_monitor_sense(uint16_t data, uint16_t dirs); - void get_disp_texture(void **pix_buf, int *pitch); - void update_screen(void); protected: uint16_t set_result(uint8_t sda, uint8_t scl); uint16_t update_ddc_i2c(uint8_t sda, uint8_t scl); private: - SDL_Window *display_wnd; - SDL_Renderer *renderer; - SDL_Texture *disp_texture; - bool i2c_on; uint8_t std_sense_code; diff --git a/devices/video/videoctrl.cpp b/devices/video/videoctrl.cpp new file mode 100644 index 0000000..0c78446 --- /dev/null +++ b/devices/video/videoctrl.cpp @@ -0,0 +1,145 @@ +/* +DingusPPC - The Experimental PowerPC Macintosh emulator +Copyright (C) 2018-21 divingkatae and maximum + (theweirdo) spatium + +(Contact divingkatae#1017 or powermax#2286 on Discord for more info) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +/** @file Video Conroller base class implementation. */ + +#include "videoctrl.h" +#include +#include + +#include +#include + +VideoCtrlBase::VideoCtrlBase() +{ + LOG_F(INFO, "Create display window..."); + + // Create display window + this->display_wnd = SDL_CreateWindow( + "DingusPPC Display", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + 640, + 480, + SDL_WINDOW_OPENGL + ); + + if (this->display_wnd == NULL) { + LOG_F(ERROR, "Display: SDL_CreateWindow failed with %s\n", 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\n", 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, + 640, 480 + ); + + if (this->disp_texture == NULL) { + LOG_F(ERROR, "Display: SDL_CreateTexture failed with %s\n", SDL_GetError()); + } +} + +VideoCtrlBase::~VideoCtrlBase() +{ + if (this->disp_texture) { + SDL_DestroyTexture(this->disp_texture); + } + + if (this->renderer) { + SDL_DestroyRenderer(this->renderer); + } + + if (this->display_wnd) { + SDL_DestroyWindow(this->display_wnd); + } +} + +void VideoCtrlBase::update_screen() +{ + uint8_t* dst_buf; + int dst_pitch; + + //auto start_time = std::chrono::steady_clock::now(); + + SDL_LockTexture(this->disp_texture, NULL, (void **)&dst_buf, &dst_pitch); + + // call texture update method (hardcoded for now) + // TODO: convert it to a callback + this->convert_frame_8bpp(dst_buf, dst_pitch); + + SDL_UnlockTexture(this->disp_texture); + SDL_RenderClear(this->renderer); + SDL_RenderCopy(this->renderer, this->disp_texture, NULL, NULL); + SDL_RenderPresent(this->renderer); + + // HW cursor data is stored at the beginning of the video memory + // HACK: use src_offset to recognize cursor data being ready + // Normally, we should check GEN_CUR_ENABLE bit in the GEN_TEST_CNTL register + //if (src_offset > 0x400 && READ_DWORD_LE_A(&this->block_io_regs[ATI_CUR_OFFSET])) { + // this->draw_hw_cursor(dst_buf + dst_pitch * 20 + 120, dst_pitch); + //} + + //auto end_time = std::chrono::steady_clock::now(); + //auto time_elapsed = std::chrono::duration_cast(end_time - start_time); + //LOG_F(INFO, "Display uodate took: %lld ns", time_elapsed.count()); + + SDL_Delay(15); +} + +void VideoCtrlBase::convert_frame_8bpp(uint8_t *dst_buf, int dst_pitch) +{ + uint8_t *src_buf, *src_row, *dst_row, pix; + int src_pitch; + + src_buf = this->fb_ptr; + src_pitch = this->fb_pitch; + + for (int h = 0; h < this->active_height; h++) { + src_row = &src_buf[h * src_pitch]; + dst_row = &dst_buf[h * dst_pitch]; + + for (int x = 0; x < this->active_width; x++) { + pix = src_row[x]; + dst_row[0] = this->palette[pix][2]; // B + dst_row[1] = this->palette[pix][1]; // G + dst_row[2] = this->palette[pix][0]; // R + dst_row[3] = 255; // Alpha + dst_row += 4; + } + } +} diff --git a/devices/video/videoctrl.h b/devices/video/videoctrl.h new file mode 100644 index 0000000..8c8804b --- /dev/null +++ b/devices/video/videoctrl.h @@ -0,0 +1,60 @@ +/* +DingusPPC - The Experimental PowerPC Macintosh emulator +Copyright (C) 2018-21 divingkatae and maximum + (theweirdo) spatium + +(Contact divingkatae#1017 or powermax#2286 on Discord for more info) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +/** @file Video Conroller base class definitions. */ + +#ifndef VIDEO_CTRL_H +#define VIDEO_CTRL_H + +#include + +#include + +class VideoCtrlBase { +public: + VideoCtrlBase(); + ~VideoCtrlBase(); + + void update_screen(void); + + void convert_frame_8bpp(uint8_t *dst_buf, int dst_pitch); + +protected: + /* CRT controller parameters */ + bool crtc_on = false; + int active_width; // width of the visible display area + int active_height; // height of the visible display area + float pixel_clock; + float refresh_rate; + + uint8_t palette[256][4]; /* internal DAC palette in RGBA format */ + + // Framebuffer parameters + uint8_t* fb_ptr; + int fb_pitch; + +private: + SDL_Window *display_wnd; + SDL_Renderer *renderer; + SDL_Texture *disp_texture; +}; + +#endif // VIDEO_CTRL_H