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