diff --git a/devices/ioctrl/amic.cpp b/devices/ioctrl/amic.cpp index 0e7c7dd..8ee25a7 100644 --- a/devices/ioctrl/amic.cpp +++ b/devices/ioctrl/amic.cpp @@ -32,6 +32,8 @@ along with this program. If not, see . #include #include #include +#include +#include #include #include @@ -63,6 +65,7 @@ AMIC::AMIC() // initialize on-board video this->disp_id = std::unique_ptr (new DisplayID()); + this->def_vid = std::unique_ptr (new PdmOnboardVideo()); } bool AMIC::supports_type(HWCompType type) { @@ -113,6 +116,8 @@ uint32_t AMIC::read(uint32_t reg_start, uint32_t offset, int size) } switch(offset) { + case AMICReg::Video_Mode: + return this->def_vid->get_video_mode(); case AMICReg::Monitor_Id: return this->mon_id; case AMICReg::Diag_Reg: @@ -200,8 +205,20 @@ void AMIC::write(uint32_t reg_start, uint32_t offset, uint32_t value, int size) case AMICReg::VIA2_IER: LOG_F(INFO, "AMIC VIA2 Interrupt Enable Register updated, val=%x", value); break; + case AMICReg::Ariel_Clut_Index: + this->def_vid->set_clut_index(value); + break; + case AMICReg::Ariel_Clut_Color: + this->def_vid->set_clut_color(value); + break; + case AMICReg::Ariel_Config: + this->def_vid->vdac_config(value); + break; case AMICReg::Video_Mode: - LOG_F(INFO, "AMIC Video Mode Register set to %x", value); + this->def_vid->set_video_mode(value); + break; + case AMICReg::Pixel_Depth: + this->def_vid->set_pixel_depth(value); break; case AMICReg::Monitor_Id: { // extract and convert pin directions (0 - input, 1 - output) diff --git a/devices/ioctrl/amic.h b/devices/ioctrl/amic.h index 711929e..b4cf1d2 100644 --- a/devices/ioctrl/amic.h +++ b/devices/ioctrl/amic.h @@ -31,6 +31,7 @@ along with this program. If not, see . #include #include #include +#include #include #include @@ -98,9 +99,14 @@ enum AMICReg : uint32_t { VIA2_Slot_IER = 0x26012, VIA2_IER = 0x26013, + // Video DAC (Ariel II) control registers + Ariel_Clut_Index = 0x24000, + Ariel_Clut_Color = 0x24001, + Ariel_Config = 0x24002, + // Video control registers Video_Mode = 0x28000, - Color_Ctrl = 0x28001, + Pixel_Depth = 0x28001, Monitor_Id = 0x28002, Int_Ctrl = 0x2A000, @@ -158,6 +164,7 @@ private: // on-board video std::unique_ptr disp_id; + std::unique_ptr def_vid; uint8_t mon_id; }; diff --git a/devices/memctrl/hmc.cpp b/devices/memctrl/hmc.cpp index 606f4ba..6cbcd3c 100644 --- a/devices/memctrl/hmc.cpp +++ b/devices/memctrl/hmc.cpp @@ -33,7 +33,7 @@ HMC::HMC() : MemCtrlBase() /* add memory mapped I/O region for the HMC control register */ add_mmio_region(0x50F40000, 0x10000, this); - this->config_reg = 0ULL; + this->ctrl_reg = 0ULL; this->bit_pos = 0; } @@ -48,7 +48,7 @@ bool HMC::supports_type(HWCompType type) { uint32_t HMC::read(uint32_t reg_start, uint32_t offset, int size) { if (!offset) - return !!(this->config_reg & (1ULL << this->bit_pos++)); + return !!(this->ctrl_reg & (1ULL << this->bit_pos++)); else return 0; /* FIXME: what should be returned for invalid offsets? */ } @@ -60,8 +60,8 @@ void HMC::write(uint32_t reg_start, uint32_t offset, uint32_t value, int size) switch(offset) { case 0: bit = 1ULL << this->bit_pos++; - this->config_reg = (value & 1) ? this->config_reg | bit : - this->config_reg & ~bit; + this->ctrl_reg = (value & 1) ? this->ctrl_reg | bit : + this->ctrl_reg & ~bit; break; case 8: /* writing to HMCBase + 8 resets internal bit position */ this->bit_pos = 0; diff --git a/devices/memctrl/hmc.h b/devices/memctrl/hmc.h index b4596ff..46b9ce4 100644 --- a/devices/memctrl/hmc.h +++ b/devices/memctrl/hmc.h @@ -37,6 +37,10 @@ along with this program. If not, see . #include +/** Bit in the HMC control register telling us where the frame bufffer + for the on-board video is located. */ +#define HMC_VBASE_BIT 33 + class HMC : public MemCtrlBase, public MMIODevice { public: HMC(); @@ -48,9 +52,13 @@ public: uint32_t read(uint32_t reg_start, uint32_t offset, int size); void write(uint32_t reg_start, uint32_t offset, uint32_t value, int size); + uint64_t get_control_reg(void) { + return this->ctrl_reg; + }; + private: int bit_pos; - uint64_t config_reg; + uint64_t ctrl_reg; }; #endif // HMC_H diff --git a/devices/video/pdmonboard.cpp b/devices/video/pdmonboard.cpp new file mode 100644 index 0000000..d546f86 --- /dev/null +++ b/devices/video/pdmonboard.cpp @@ -0,0 +1,230 @@ +/* +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 PDM on-board video emulation. */ + +#include +#include +#include +#include +#include +#include + +#include + +PdmOnboardVideo::PdmOnboardVideo() + : VideoCtrlBase() +{ + this->video_mode = PDM_VMODE_OFF; + this->blanking = 0x80; + this->crtc_on = false; + this->vdac_mode = 8; + + // get pointer to the Highspeed Memory controller + this->hmc_obj = dynamic_cast(gMachineObj->get_comp_by_name("HMC")); +} + +void PdmOnboardVideo::set_video_mode(uint8_t new_mode) +{ + switch(new_mode & 0x7F) { + case PdmVideoMode::Portrait: + case PdmVideoMode::Rgb12in: + case PdmVideoMode::Rgb13in: + case PdmVideoMode::Rgb16in: + case PdmVideoMode::VGA: + if (this->video_mode != (new_mode & 0x1F)) { + 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(); + } + } + 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; + } +} + +void PdmOnboardVideo::set_pixel_depth(uint8_t depth) +{ + static uint8_t pix_depths[8] = {1, 2, 4, 8, 16, 0xFF, 0xFF, 0xFF}; + + this->pixel_depth = pix_depths[depth]; + if (this->pixel_depth == 0xFF) { + ABORT_F("PDM-Video: invalid pixel depth code %d specified!", depth); + } +} + +void PdmOnboardVideo::vdac_config(uint8_t mode) +{ + if (mode != 8) { + LOG_F(WARNING, "Unknown Ariel Config code 0x%X", mode); + } + this->vdac_mode = 8; // 1 bpp, master mode, no overlay +} + +void PdmOnboardVideo::set_clut_index(uint8_t index) +{ + this->clut_index = index; + this->comp_index = 0; +} + +void PdmOnboardVideo::set_clut_color(uint8_t color) +{ + this->clut_color[this->comp_index++] = color; + if (this->comp_index >= 3) { + LOG_F(INFO, "PDM-Video: received color for index %d", this->clut_index); + // TODO: combine separate components into a single ARGB value + this->palette[this->clut_index][0] = this->clut_color[0]; + this->palette[this->clut_index][1] = this->clut_color[1]; + this->palette[this->clut_index][2] = this->clut_color[2]; + this->palette[this->clut_index][3] = 255; + this->clut_index++; // assume the HW works like that + this->comp_index = 0; // assume the HW works like that + } +} + +void PdmOnboardVideo::enable_video_internal() +{ + int new_width, new_height; + + // ensure all video parameters contain safe values + switch(this->video_mode) { + case PdmVideoMode::Portrait: + case PdmVideoMode::Rgb16in: + case PdmVideoMode::VGA: + if (this->pixel_depth > 8) { + ABORT_F("PDM-Video: no 16bpp support in mode %d!", this->video_mode); + } + default: + break; + } + + // set video mode parameters + switch(this->video_mode) { + case PdmVideoMode::Portrait: + new_width = 640; + new_height = 870; + this->pixel_clock = 57283200; + break; + case PdmVideoMode::Rgb12in: + new_width = 512; + new_height = 384; + this->pixel_clock = 15667200; + break; + case PdmVideoMode::Rgb13in: + new_width = 640; + new_height = 480; + this->pixel_clock = 31334400; + break; + case PdmVideoMode::Rgb16in: + new_width = 832; + new_height = 624; + this->pixel_clock = 57283200; + break; + case PdmVideoMode::VGA: + new_width = 640; + new_height = 480; + this->pixel_clock = 25175000; + break; + default: + ABORT_F("PDM-Video: invalid video mode %d", this->video_mode); + } + + if (new_width != this->active_width || new_height != this->active_height) { + LOG_F(WARNING, "Display window resizing not implemented yet!"); + } + + // figure out where our framebuffer is located + uint64_t hmc_control = this->hmc_obj->get_control_reg(); + uint32_t fb_base_phys = ((hmc_control >> HMC_VBASE_BIT) & 1) ? 0 : 0x100000; + LOG_F(INFO, "PDM-Video: framebuffer phys base addr = 0x%X", fb_base_phys); + + // set framebuffer address and pitch + this->fb_ptr = mmu_get_dma_mem(fb_base_phys, PDM_FB_SIZE_MAX); + this->active_width = new_width; + this->active_height = new_height; + + switch(this->pixel_depth) { + case 1: + this->convert_fb_cb = [this](uint8_t *dst_buf, int dst_pitch) { + this->convert_frame_1bpp(dst_buf, dst_pitch); + }; + this->fb_pitch = new_width >> 3; // one byte contains 8 pixels + break; + default: + ABORT_F("PDM-Video: pixel depth %d not implemented yet!", this->pixel_depth); + } + + this->update_screen(); + + LOG_F(INFO, "PDM-Video: video enabled"); + this->crtc_on = true; +} + +void PdmOnboardVideo::disable_video_internal() +{ + this->crtc_on = false; + LOG_F(INFO, "PDM-Video: video disabled"); +} + +/** Ariel II has a weird 1bpp mode where a white pixel is mapped to + CLUT entry #127 (%01111111) and a black pixel to #255 (%11111111). + It requres a non-standard conversion routine implementend below. + */ +void PdmOnboardVideo::convert_frame_1bpp(uint8_t *dst_buf, int dst_pitch) +{ + uint8_t *src_buf, *src_row, pix; + uint32_t *dst_row; + int src_pitch, width; + uint32_t pixels[2]; + + // prepare cached ARGB values for white & black pixels + pixels[0] = (0xFF << 24) | (this->palette[127][0] << 16) | + (this->palette[127][1] << 8) | this->palette[127][2]; + pixels[1] = (0xFF << 24) | (this->palette[255][0] << 16) | + (this->palette[255][1] << 8) | this->palette[255][2]; + + src_buf = this->fb_ptr; + src_pitch = this->fb_pitch; + width = this->active_width >> 3; + + for (int h = 0; h < this->active_height; h++) { + src_row = &src_buf[h * src_pitch]; + dst_row = (uint32_t *)(&dst_buf[h * dst_pitch]); + + for (int x = 0; x < width; x++) { + pix = src_row[x]; + + for (int i = 0; i < 8; i++, pix <<= 1, dst_row++) { + *dst_row = pixels[(pix >> 7) & 1]; + } + } + } +} diff --git a/devices/video/pdmonboard.h b/devices/video/pdmonboard.h new file mode 100644 index 0000000..4665465 --- /dev/null +++ b/devices/video/pdmonboard.h @@ -0,0 +1,78 @@ +/* +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 Definitions for the PDM on-board video. */ + +#ifndef PDM_ONBOARD_H +#define PDM_ONBOARD_H + +#include +#include + +#include + +#define PDM_VMODE_OFF 0x1F + +/** Max. size of our framebuffer in bytes (RGB 13-inch, 16 bpp) */ +#define PDM_FB_SIZE_MAX (640 * 480 * 2) + +/** Fixed video modes supported by the PDM on-board video. */ +enum PdmVideoMode : uint8_t { + Portrait = 1, + Rgb12in = 2, + Rgb13in = 6, + Rgb16in = 9, + VGA = 0xB +}; + +class PdmOnboardVideo : public VideoCtrlBase { +public: + PdmOnboardVideo(); + ~PdmOnboardVideo() = default; + + uint8_t get_video_mode() { + return ((this->video_mode & 0x1F) | this->blanking); + }; + + void set_video_mode(uint8_t new_mode); + void set_pixel_depth(uint8_t depth); + void vdac_config(uint8_t config); + void set_clut_index(uint8_t index); + void set_clut_color(uint8_t color); + +protected: + void enable_video_internal(); + void disable_video_internal(); + void convert_frame_1bpp(uint8_t *dst_buf, int dst_pitch); + +private: + uint8_t video_mode; + uint8_t pixel_depth; + uint8_t blanking; + uint8_t vdac_mode; + uint8_t clut_index; + uint8_t comp_index; + uint8_t clut_color[3]; + + HMC* hmc_obj; +}; + +#endif // PDM_ONBOARD_H