control: implement HW cursor rendering.

This commit is contained in:
Maxim Poliakovski 2024-01-19 23:47:03 +01:00
parent a68afbf79a
commit 5f06be6226
2 changed files with 90 additions and 46 deletions

View File

@ -394,7 +394,7 @@ void ControlVideo::enable_display()
this->active_height = new_height;
// set framebuffer parameters
this->fb_ptr = &this->vram_ptr[this->fb_base];
this->fb_ptr = &this->vram_ptr[this->fb_base];
this->fb_pitch = this->row_words;
// get pixel depth from RaDACal
@ -463,48 +463,70 @@ void ControlVideo::disable_display()
LOG_F(INFO, "Control: display disabled");
}
void ControlVideo::measure_hw_cursor() {
uint8_t *src_fw_ptr = &this->vram_ptr[this->fb_base];
uint8_t *src_bw_ptr = &this->vram_ptr[this->fb_base +
this->fb_pitch * (this->active_height - 1)];
int cur_pos_y_start = -1;
int cur_pos_y_end = -1;
// forward scanning to find the first line of the cursor
for (int h = 0; h < this->active_height; h++, src_fw_ptr += this->fb_pitch) {
if (((uint64_t *)src_fw_ptr)[0] | ((uint64_t *)src_fw_ptr)[1]) {
cur_pos_y_start = h;
break;
}
}
if (cur_pos_y_start < 0)
return; // bail out because no cursor data start was found
// backward scanning to find the last line of the cursor
for (int h = this->active_height - 1; h >= 0; h--, src_bw_ptr -= this->fb_pitch) {
if (((uint64_t *)src_bw_ptr)[0] | ((uint64_t *)src_bw_ptr)[1]) {
cur_pos_y_end = h;
break;
}
}
if (cur_pos_y_end < 0)
return; // bail out because no cursor data end was found
this->rad_cursor_height = cur_pos_y_end - cur_pos_y_start + 1;
this->rad_cur_ypos = cur_pos_y_start;
}
void ControlVideo::draw_hw_cursor(uint8_t *dst_buf, int dst_pitch) {
uint8_t *src_row = &this->vram_ptr[this->fb_base];
uint8_t *dst_row = dst_buf;
int cur_height = this->active_height;
dst_pitch -= 32 * 4;
int src_pitch = this->fb_pitch - 16;
uint32_t color[16];
for (int c = 0; c < 16; c++) {
color[c] = (this->cursor_data[c*3] << 16) | (this->cursor_data[c*3 + 1] << 8) | (this->cursor_data[c*3 + 2]);
}
this->measure_hw_cursor();
for (int h = 0; h < cur_height; h++) {
for (int x = 0; x < 2; x++) {
uint64_t px16 = READ_QWORD_BE_A(src_row);
for (int p = 0; p < 16; p++) {
int c = px16 >> 60;
switch (c) {
case 0:
// transparent
break;
case 1:
// 1's complement
WRITE_DWORD_LE_A(dst_row, READ_DWORD_LE_A(dst_row) ^ 0xffffff);
break;
case 8:
WRITE_DWORD_LE_A(dst_row, color[0]);
break;
case 9:
WRITE_DWORD_LE_A(dst_row, color[1]);
break;
default:
WRITE_DWORD_LE_A(dst_row, (c << 16) | (c << 8) | c);
break;
}
px16 <<= 4;
dst_row += 4;
if (this->rad_cur_rgn_pos >= this->active_width)
return;
src_row += this->fb_pitch * this->rad_cur_ypos;
dst_row += this->rad_cur_ypos * dst_pitch + this->rad_cur_rgn_pos * sizeof(uint32_t);
dst_pitch -= 32 * sizeof(uint32_t);
for (int h = 0; h < this->rad_cursor_height; h++) {
for (int x = 0; x < 2; x++) { // two sets of 16 pixels
uint64_t pix_data = READ_QWORD_BE_A(src_row + x * 8);
if (!pix_data) { // skip processing of 16 transparent pixels
dst_row += 16 * sizeof(uint32_t);
break;
}
for (int p = 0; p < 16; p++) {
uint8_t pix = pix_data >> 60; // each pixel is 4 bits wide
if (pix & 8) { // check control bit: 0 - transparent, 1 - opaque
WRITE_DWORD_LE_A(dst_row, this->cursor_clut[pix & 7]);
}
pix_data <<= 4;
dst_row += sizeof(uint32_t);
}
src_row += 8;
}
src_row += this->fb_pitch;
dst_row += dst_pitch;
src_row += src_pitch;
}
}
@ -544,26 +566,44 @@ void ControlVideo::iodev_write(uint32_t address, uint16_t value)
this->rad_addr = value;
this->comp_index = 0;
break;
case RadacalRegs::CURSOR_DATA:
this->cursor_data[(this->rad_addr++) % 24] = value;
case RadacalRegs::CURSOR_CLUT:
this->clut_color[this->comp_index++] = value;
if (this->comp_index >= 3) {
this->cursor_clut[this->rad_addr & 7] = (this->clut_color[0] << 16) |
(this->clut_color[1] << 8) | this->clut_color[2];
this->rad_addr++; // auto-increment CLUT address
this->comp_index = 0;
}
break;
case RadacalRegs::MULTI:
switch (this->rad_addr) {
case RadacalRegs::CURSOR_POS_HI:
this->rad_cur_pos = (value << 8) | (this->rad_cur_pos & 0x00ff);
this->rad_cur_rgn_pos = (value << 8) | this->rad_cur_pos_lo;
break;
case RadacalRegs::CURSOR_POS_LO:
this->rad_cur_pos = (this->rad_cur_pos & 0xff00) | (value & 0x00ff);
this->rad_cur_pos_lo = value;
break;
case RadacalRegs::MISC_CTRL:
if (bit_changed(this->rad_cr, value, 1)) {
if (value & 2) {
//LOG_F(WARNING, "RaDACal: HW cursor enabled!");
this->measure_hw_cursor();
this->cursor_ovl_cb = [this](uint8_t *dst_buf, int dst_pitch) {
this->draw_hw_cursor(dst_buf, dst_pitch);
};
} else {
//LOG_F(WARNING, "RaDACal: HW cursor disabled!");
this->cursor_ovl_cb = nullptr;
}
}
this->rad_cr = value;
break;
case RadacalRegs::DBL_BUF_CTRL:
this->rad_dbl_buf_cr = value;
break;
case RadacalRegs::TEST_CTRL:
this->tst_cr = value;
if (this->tst_cr & 1)
this->rad_tst_cr = value;
if (value & 1)
LOG_F(WARNING, "RaDACal: DAC test enabled!");
break;
default:

View File

@ -94,7 +94,7 @@ namespace RadacalRegs {
enum RadacalRegs : uint8_t {
ADDRESS = 0, // address register
CURSOR_DATA = 1, // cursor data
CURSOR_CLUT = 1, // cursor palette data
MULTI = 2, // multipurpose section
CLUT_DATA = 3, // color palette data
@ -134,7 +134,8 @@ protected:
uint16_t iodev_read(uint32_t address);
void iodev_write(uint32_t address, uint16_t value);
// HW cursor support
// HW cursor support (in RaDACal)
void measure_hw_cursor();
void draw_hw_cursor(uint8_t *dst_buf, int dst_pitch);
private:
@ -166,11 +167,14 @@ private:
uint8_t rad_addr = 0;
uint8_t rad_cr = 0;
uint8_t rad_dbl_buf_cr = 0;
uint8_t tst_cr = 0;
uint16_t rad_cur_pos = 0;
uint8_t rad_tst_cr = 0;
uint16_t rad_cur_rgn_pos = 0; // horizontal position of the cursor region
uint8_t rad_cur_pos_lo = 0; // lower byte of the horizontal cursor position
uint16_t rad_cur_ypos = 0;
uint16_t rad_cursor_height = 0;
uint8_t comp_index;
uint8_t clut_color[3];
uint8_t cursor_data[24] = { 0 };
uint32_t cursor_clut[8] = {};
};
#endif // CONTROL_VIDEO_H