diff --git a/devices/video/atimach64defs.h b/devices/video/atimach64defs.h index c5cabf32f1..f272dbe59b 100644 --- a/devices/video/atimach64defs.h +++ b/devices/video/atimach64defs.h @@ -41,6 +41,19 @@ enum { ATI_PIX_FMT_ARGB8888 = 6, }; +/* Draw engine data-path values. */ +enum { + ATI_DP_COLOR_SRC_BKGD_CLR = 0, + ATI_DP_COLOR_SRC_FRGD_CLR = 1, + ATI_DP_COLOR_SRC_HOST = 2, + ATI_DP_COLOR_SRC_BLIT = 3, + ATI_DP_MONO_SRC_ALWAYS_1 = 0, + ATI_DP_MIX_SRC = 7, + ATI_SRC_TRAJ_UNBOUNDED = 0, + ATI_SRC_TRAJ_PATTERN = 1, + ATI_SRC_TRAJ_ROTATED = 3, +}; + /** Mach64 register offsets. */ enum { ATI_CRTC_H_TOTAL_DISP = 0x000, // 0x0000 @@ -485,22 +498,30 @@ enum { ATI_SRC_PITCH = 22, ATI_SRC_PITCH_size = 10, ATI_SRC_X = 0x061, // 0x0184 - ATI_SRC_X_pos = 0, ATI_SRC_X_size = 13, + ATI_SRC_X_pos = 0, ATI_SRC_X_size = 14, ATI_SRC_Y = 0x062, // 0x0188 ATI_SRC_Y_pos = 0, ATI_SRC_Y_size = 15, ATI_SRC_Y_X = 0x063, // 0x018C ATI_SRC_WIDTH1 = 0x064, // 0x0190 + ATI_SRC_WIDTH1_pos = 0, ATI_SRC_WIDTH1_size = 14, ATI_SRC_HEIGHT1 = 0x065, // 0x0194 + ATI_SRC_HEIGHT1_pos = 0, ATI_SRC_HEIGHT1_size = 15, ATI_SRC_HEIGHT1_WIDTH1 = 0x066, // 0x0198 ATI_SRC_X_START = 0x067, // 0x019C + ATI_SRC_X_START_pos = 0, ATI_SRC_X_START_size = 14, ATI_SRC_Y_START = 0x068, // 0x01A0 + ATI_SRC_Y_START_pos = 0, ATI_SRC_Y_START_size = 15, ATI_SRC_Y_X_START = 0x069, // 0x01A4 ATI_SRC_WIDTH2 = 0x06A, // 0x01A8 + ATI_SRC_WIDTH2_pos = 0, ATI_SRC_WIDTH2_size = 14, ATI_SRC_HEIGHT2 = 0x06B, // 0x01AC + ATI_SRC_HEIGHT2_pos = 0, ATI_SRC_HEIGHT2_size = 15, ATI_SRC_HEIGHT2_WIDTH2 = 0x06C, // 0x01B0 ATI_SRC_CNTL = 0x06D, // 0x01B4 + ATI_SRC_COLOR_REG_WRITE_EN = 13, + ATI_SRC_BLOCK_WRITE_EN = 14, ATI_SCALE_OFF = 0x070, // 0x01C0 ATI_SCALE_WIDTH = 0x077, // 0x01DC ATI_SCALE_HEIGHT = 0x078, // 0x01E0 @@ -509,6 +530,8 @@ enum { ATI_SCALE_Y_INC = 0x07D, // 0x01F4 ATI_SCALE_VACC = 0x07E, // 0x01F8 ATI_SCALE_3D_CNTL = 0x07F, // 0x01FC + ATI_HOST_DATA0 = 0x080, // 0x0200 + ATI_HOST_DATA15 = 0x08F, // 0x023C ATI_HOST_CNTL = 0x090, // 0x0240 ATI_BM_HOSTDATA = 0x091, // 0x0244 ATI_BM_ADDR = 0x092, // 0x0248 @@ -520,10 +543,10 @@ enum { ATI_PAT_CNTL = 0x0A2, // 0x0288 ATI_SC_LEFT = 0x0A8, // 0x02A0 - ATI_SC_LEFT_pos = 0, ATI_SC_LEFT_size = 13, + ATI_SC_LEFT_pos = 0, ATI_SC_LEFT_size = 14, ATI_SC_RIGHT = 0x0A9, // 0x02A4 - ATI_SC_RIGHT_pos = 0, ATI_SC_RIGHT_size = 13, + ATI_SC_RIGHT_pos = 0, ATI_SC_RIGHT_size = 14, ATI_SC_LEFT_RIGHT = 0x0AA, // 0x02A8 @@ -599,14 +622,16 @@ enum { ATI_SRC_PATT_EN = 16, // VT/GT ATI_SRC_PATT_ROT_EN = 17, // VT/GT ATI_SRC_LINEAR_EN = 18, // VT/GT - ATI_SRC_LINE_X_DIR = 19, // VT/GT - ATI_SRC_TRACK_DST = 20, // VT/GT - ATI_PAT_MONO_EN = 23, // GT - ATI_PAT_CLR_4x2_EN = 24, // VT/GT - ATI_PAT_CLR_8x1_EN = 25, // VT/GT - ATI_HOST_BYTE_ALIGN = 26, // VT/GT - ATI_HOST_BIG_ENDIAN_EN = 28, // VT/GT - ATI_SRC_BYTE_ALIGN = 29, // VT/GT + ATI_SRC_BYTE_ALIGN = 19, // VT/GT + ATI_SRC_LINE_X_DIR = 20, // VT/GT + ATI_SRC_8x8x8_BRUSH = 21, // GT + ATI_FAST_FILL_EN = 22, // GT + ATI_SRC_TRACK_DST = 23, // VT/GT + ATI_PAT_MONO_EN = 24, // GT + ATI_PAT_CLR_4x2_EN = 25, // VT/GT + ATI_PAT_CLR_8x1_EN = 26, // VT/GT + ATI_HOST_BYTE_ALIGN = 28, // VT/GT + ATI_HOST_BIG_ENDIAN_EN = 29, // VT/GT ATI_GUI_STAT = 0x0CE, // 0x0338 ATI_GUI_ACTIVE = 0, diff --git a/devices/video/atirage.cpp b/devices/video/atirage.cpp index e718f5332b..e56a1b28ae 100644 --- a/devices/video/atirage.cpp +++ b/devices/video/atirage.cpp @@ -37,6 +37,28 @@ static const int mach64_post_div[8] = { 3, 5, 6, 12 // alternate post dividers }; +static int32_t mach64_extract_signed(uint32_t value, int pos, int width) { + value = extract_bits(value, pos, width); + return static_cast(value << (32 - width)) >> (32 - width); +} + +static bool mach64_clip_axis(int coord, int increment, uint32_t length, + int scissor_min, int scissor_max, + int& skip, uint32_t& clipped_length) { + int first = increment > 0 ? scissor_min - coord : coord - scissor_max; + int last = increment > 0 ? scissor_max - coord : coord - scissor_min; + + first = std::max(first, 0); + last = std::min(last, int(length) - 1); + if (first > last) { + return false; + } + + skip = first; + clipped_length = last - first + 1; + return true; +} + /* Human readable Mach64 HW register names for easier debugging. */ static const std::map mach64_reg_names = { #define one_reg_name(x) {ATI_ ## x, #x} @@ -266,6 +288,13 @@ uint32_t ATIRage::read_reg(uint32_t reg_offset, uint32_t size) { case ATI_GUI_STAT: result = uint64_t(this->cmd_fifo_size << 16); // HACK: pretend empty FIFO break; + case ATI_GUI_TRAJ_CNTL: + result = 0; + insert_bits(result, this->regs[ATI_DST_CNTL], 0, 16); + insert_bits(result, this->regs[ATI_SRC_CNTL], 16, 8); + insert_bits(result, this->regs[ATI_PAT_CNTL], 24, 3); + insert_bits(result, this->regs[ATI_HOST_CNTL], 28, 2); + break; case ATI_DP_BKGD_CLR: case ATI_DP_FRGD_CLR: uint32_t pix_fmt = extract_bits( @@ -320,6 +349,11 @@ void ATIRage::write_reg(uint32_t reg_offset, uint32_t value, uint32_t size) { uint32_t old_value = this->regs[reg_num]; uint32_t new_value; + if (reg_num >= ATI_HOST_DATA0 && reg_num <= ATI_HOST_DATA15) { + this->write_host_data(value, size); + return; + } + if (offset || size != 4) { // slow path if ((offset + size) > 4) { ABORT_F("%s: unaligned DWORD writes not implemented", this->name.c_str()); @@ -578,12 +612,43 @@ void ATIRage::write_reg(uint32_t reg_offset, uint32_t value, uint32_t size) { break; case ATI_SRC_HEIGHT1_WIDTH1: new_value = value; - this->regs[ATI_SRC_HEIGHT1] = extract_bits(value, 0, 16); - this->regs[ATI_SRC_WIDTH1] = extract_bits(value, 16, 16); + this->regs[ATI_SRC_HEIGHT1] = extract_bits(value, 0, ATI_SRC_HEIGHT1_size); + this->regs[ATI_SRC_WIDTH1] = extract_bits(value, 16, ATI_SRC_WIDTH1_size); + break; + case ATI_SRC_Y_X_START: + new_value = value; + this->regs[ATI_SRC_Y_START] = extract_bits(value, 0, ATI_SRC_Y_START_size); + this->regs[ATI_SRC_X_START] = extract_bits(value, 16, ATI_SRC_X_START_size); + break; + case ATI_SRC_HEIGHT2_WIDTH2: + new_value = value; + this->regs[ATI_SRC_HEIGHT2] = extract_bits(value, 0, ATI_SRC_HEIGHT2_size); + this->regs[ATI_SRC_WIDTH2] = extract_bits(value, 16, ATI_SRC_WIDTH2_size); + break; + case ATI_SC_LEFT_RIGHT: + new_value = value; + this->regs[ATI_SC_LEFT] = extract_bits(value, 0, ATI_SC_LEFT_size); + this->regs[ATI_SC_RIGHT] = extract_bits(value, 16, ATI_SC_RIGHT_size); + break; + case ATI_SC_TOP_BOTTOM: + new_value = value; + this->regs[ATI_SC_TOP] = extract_bits(value, 0, ATI_SC_TOP_size); + this->regs[ATI_SC_BOTTOM] = extract_bits(value, 16, ATI_SC_BOTTOM_size); + break; + case ATI_GUI_TRAJ_CNTL: + new_value = value; + insert_bits(this->regs[ATI_DST_CNTL], value, 0, 16); + insert_bits(this->regs[ATI_SRC_CNTL], value >> 16, 0, 8); + insert_bits(this->regs[ATI_PAT_CNTL], value >> 24, 0, 3); + insert_bits(this->regs[ATI_HOST_CNTL], value >> 28, 0, 2); break; case ATI_DST_WIDTH: case ATI_DST_HEIGHT_WIDTH: case ATI_DST_X_WIDTH: + case ATI_DST_WIDTH_HEIGHT: + this->begin_drawing(reg_num, value); + new_value = this->regs[reg_num]; + break; case ATI_DST_BRES_LNTH: this->begin_drawing(reg_num, value); break; @@ -997,10 +1062,46 @@ int ATIRage::device_postinit() // =================================== Draw Engine ===================================== void ATIRage::begin_drawing(uint32_t initiator, uint32_t value) { switch(initiator) { - case ATI_DST_HEIGHT_WIDTH: + case ATI_DST_WIDTH: { + uint32_t width = extract_bits(value, ATI_DST_WIDTH_pos, ATI_DST_WIDTH_size); + this->regs[ATI_DST_WIDTH] = width; + if (!bit_set(value, DST_WIDTH_FILL_DIS)) { + this->draw_rect( + width, + extract_bits( + this->regs[ATI_DST_HEIGHT], ATI_DST_HEIGHT_pos, ATI_DST_HEIGHT_size)); + } + break; + } + case ATI_DST_HEIGHT_WIDTH: { + uint32_t width = extract_bits(value, 16, ATI_DST_WIDTH_size); + uint32_t height = extract_bits(value, 0, ATI_DST_HEIGHT_size); this->regs[ATI_DST_HEIGHT_WIDTH] = value; - this->draw_rect(extract_bits(value, 16, 14), extract_bits(value, 0, 15)); + this->regs[ATI_DST_WIDTH] = width; + this->regs[ATI_DST_HEIGHT] = height; + this->draw_rect(width, height); + break; + } + case ATI_DST_X_WIDTH: { + uint32_t width = extract_bits(value, 16, ATI_DST_WIDTH_size); + this->regs[ATI_DST_X_WIDTH] = value; + this->regs[ATI_DST_X] = extract_bits(value, 0, ATI_DST_X_size); + this->regs[ATI_DST_WIDTH] = width; + this->draw_rect( + width, + extract_bits( + this->regs[ATI_DST_HEIGHT], ATI_DST_HEIGHT_pos, ATI_DST_HEIGHT_size)); break; + } + case ATI_DST_WIDTH_HEIGHT: { + uint32_t width = extract_bits(value, 0, ATI_DST_WIDTH_size); + uint32_t height = extract_bits(value, 16, ATI_DST_HEIGHT_size); + this->regs[ATI_DST_WIDTH_HEIGHT] = value; + this->regs[ATI_DST_WIDTH] = width; + this->regs[ATI_DST_HEIGHT] = height; + this->draw_rect(width, height); + break; + } default: LOG_F(WARNING, "%s: unimplemented engine operation, initiator=0x%X", this->name.c_str(), initiator); @@ -1008,14 +1109,34 @@ void ATIRage::begin_drawing(uint32_t initiator, uint32_t value) { } void ATIRage::draw_rect(uint32_t width, uint32_t height) { - uint8_t frgd_src = extract_bits(this->regs[ATI_DP_SRC], ATI_DP_FRGD_SRC, ATI_DP_FRGD_SRC_size); - uint8_t bkgd_src = extract_bits(this->regs[ATI_DP_SRC], ATI_DP_BKGD_SRC, ATI_DP_BKGD_SRC_size); - uint8_t mono_src = extract_bits(this->regs[ATI_DP_SRC], ATI_DP_MONO_SRC, ATI_DP_MONO_SRC_size); + uint8_t frgd_src = extract_bits( + this->regs[ATI_DP_SRC], ATI_DP_FRGD_SRC, ATI_DP_FRGD_SRC_size); + uint8_t mono_src = extract_bits( + this->regs[ATI_DP_SRC], ATI_DP_MONO_SRC, ATI_DP_MONO_SRC_size); + uint32_t src_cntl = this->regs[ATI_SRC_CNTL]; + + this->host_data_active = false; + + // Color register blits update SGRAM state rather than VRAM. + if (bit_set(src_cntl, ATI_SRC_COLOR_REG_WRITE_EN) && + !bit_set(src_cntl, ATI_SRC_BLOCK_WRITE_EN)) { + this->finish_rect(width, height); + return; + } - if (frgd_src == 1 && !mono_src) { // rectangle fill with foreground color - this->fill_rect(width, height); - } else if (frgd_src == 3 && !mono_src) { // rectangle copy from the blit source + if ((frgd_src == ATI_DP_COLOR_SRC_BKGD_CLR || frgd_src == ATI_DP_COLOR_SRC_FRGD_CLR) && + mono_src == ATI_DP_MONO_SRC_ALWAYS_1) { + uint32_t color = frgd_src == ATI_DP_COLOR_SRC_BKGD_CLR ? this->regs[ATI_DP_BKGD_CLR] + : this->regs[ATI_DP_FRGD_CLR]; + this->fill_rect(width, height, color); + this->finish_rect(width, height); + } else if (frgd_src == ATI_DP_COLOR_SRC_HOST && + mono_src == ATI_DP_MONO_SRC_ALWAYS_1) { + this->start_host_rect(width, height); + } else if (frgd_src == ATI_DP_COLOR_SRC_BLIT && + mono_src == ATI_DP_MONO_SRC_ALWAYS_1) { this->blit_rect(width, height); + this->finish_rect(width, height); } else { LOG_F(WARNING, "%s: unimplemented rectangle draw op, DP_SRC=0x%08X, DP_MIX=0x%08X, " "DP_PIX_WIDTH=0x%08X, SRC_CNTL=0x%08X", this->name.c_str(), @@ -1024,14 +1145,29 @@ void ATIRage::draw_rect(uint32_t width, uint32_t height) { } } -void ATIRage::fill_rect(uint32_t dst_width, uint32_t dst_height) { - uint8_t frgd_mix = extract_bits(this->regs[ATI_DP_MIX], ATI_DP_FRGD_MIX, ATI_DP_FRGD_MIX_size); - uint8_t bkgd_mix = extract_bits(this->regs[ATI_DP_MIX], ATI_DP_BKGD_MIX, ATI_DP_BKGD_MIX_size); +void ATIRage::finish_rect(uint32_t width, uint32_t height) { + int dst_x = mach64_extract_signed(this->regs[ATI_DST_X], ATI_DST_X_pos, ATI_DST_X_size); + int dst_y = mach64_extract_signed(this->regs[ATI_DST_Y], ATI_DST_Y_pos, ATI_DST_Y_size); + int x_delta = int(width); + int y_delta = int(height); - // check for non-trivial operations - if (frgd_mix != 7 || bkgd_mix != 3) { - LOG_F(WARNING, "%s: unimplemented rectangle fill op, DP_FRGD_MIX=0x%X, DP_BKGD_MIX=0x%X", - this->name.c_str(), frgd_mix, bkgd_mix); + if (bit_set(this->regs[ATI_DST_CNTL], ATI_DST_X_TILE)) { + dst_x += bit_set(this->regs[ATI_DST_CNTL], ATI_DST_X_DIR) ? x_delta : -x_delta; + insert_bits(this->regs[ATI_DST_X], dst_x, ATI_DST_X_pos, ATI_DST_X_size); + } + if (bit_set(this->regs[ATI_DST_CNTL], ATI_DST_Y_TILE)) { + dst_y += bit_set(this->regs[ATI_DST_CNTL], ATI_DST_Y_DIR) ? y_delta : -y_delta; + insert_bits(this->regs[ATI_DST_Y], dst_y, ATI_DST_Y_pos, ATI_DST_Y_size); + } +} + +void ATIRage::fill_rect(uint32_t dst_width, uint32_t dst_height, uint32_t color) { + uint8_t frgd_mix = extract_bits(this->regs[ATI_DP_MIX], ATI_DP_FRGD_MIX, + ATI_DP_FRGD_MIX_size); + + if (frgd_mix != ATI_DP_MIX_SRC) { + LOG_F(WARNING, "%s: unimplemented rectangle fill op, DP_FRGD_MIX=0x%X", + this->name.c_str(), frgd_mix); return; } @@ -1077,16 +1213,31 @@ void ATIRage::fill_rect(uint32_t dst_width, uint32_t dst_height) { // grab trajectory params int dst_offs = extract_bits(this->regs[ATI_DST_OFF_PITCH], ATI_DST_OFFSET, ATI_DST_OFFSET_size); int dst_pitch = extract_bits(this->regs[ATI_DST_OFF_PITCH], ATI_DST_PITCH, ATI_DST_PITCH_size); - int dst_x = extract_bits(this->regs[ATI_DST_X], ATI_DST_X_pos, ATI_DST_X_size); - int dst_y = extract_bits(this->regs[ATI_DST_Y], ATI_DST_Y_pos, ATI_DST_Y_size); + int dst_x = mach64_extract_signed(this->regs[ATI_DST_X], ATI_DST_X_pos, ATI_DST_X_size); + int dst_y = mach64_extract_signed(this->regs[ATI_DST_Y], ATI_DST_Y_pos, ATI_DST_Y_size); dst_offs *= 8; dst_pitch *= 8 * bytes_per_pixel; - int x_inc = (this->regs[ATI_DST_CNTL] & 1) ? 1 : -1; - int y_inc = (this->regs[ATI_DST_CNTL] & 2) ? 1 : -1; + int x_inc = bit_set(this->regs[ATI_DST_CNTL], ATI_DST_X_DIR) ? 1 : -1; + int y_inc = bit_set(this->regs[ATI_DST_CNTL], ATI_DST_Y_DIR) ? 1 : -1; + + int sc_left = mach64_extract_signed(this->regs[ATI_SC_LEFT], ATI_SC_LEFT_pos, ATI_SC_LEFT_size); + int sc_right = mach64_extract_signed( + this->regs[ATI_SC_RIGHT], ATI_SC_RIGHT_pos, ATI_SC_RIGHT_size); + int sc_top = mach64_extract_signed(this->regs[ATI_SC_TOP], ATI_SC_TOP_pos, ATI_SC_TOP_size); + int sc_bottom = mach64_extract_signed( + this->regs[ATI_SC_BOTTOM], ATI_SC_BOTTOM_pos, ATI_SC_BOTTOM_size); + + int x_skip, y_skip; + if (!mach64_clip_axis(dst_x, x_inc, dst_width, sc_left, sc_right, x_skip, dst_width) || + !mach64_clip_axis(dst_y, y_inc, dst_height, sc_top, sc_bottom, y_skip, dst_height)) { + return; + } + dst_x += x_skip * x_inc; + dst_y += y_skip * y_inc; - uint32_t pix = this->regs[ATI_DP_FRGD_CLR] & this->regs[ATI_DP_WRITE_MSK]; + uint32_t pix = color & this->regs[ATI_DP_WRITE_MSK]; switch (dst_pix_fmt) { case ATI_PIX_FMT_8BPP: @@ -1140,6 +1291,74 @@ void ATIRage::fill_rect(uint32_t dst_width, uint32_t dst_height) { this->draw_fb = true; } +void ATIRage::start_host_rect(uint32_t dst_width, uint32_t dst_height) { + uint8_t frgd_mix = extract_bits(this->regs[ATI_DP_MIX], ATI_DP_FRGD_MIX, + ATI_DP_FRGD_MIX_size); + uint8_t host_pix_fmt = extract_bits(this->regs[ATI_DP_PIX_WIDTH], ATI_DP_HOST_PIX_WIDTH, + ATI_DP_HOST_PIX_WIDTH_size); + uint8_t dst_pix_fmt = extract_bits(this->regs[ATI_DP_PIX_WIDTH], ATI_DP_DST_PIX_WIDTH, + ATI_DP_DST_PIX_WIDTH_size); + + if (frgd_mix != ATI_DP_MIX_SRC || host_pix_fmt != ATI_PIX_FMT_8BPP || + dst_pix_fmt != ATI_PIX_FMT_8BPP || this->regs[ATI_CLR_CMP_CNTL]) { + LOG_F(WARNING, "%s: unsupported color host rectangle, DP_MIX=0x%08X, " + "DP_PIX_WIDTH=0x%08X, CLR_CMP_CNTL=0x%08X", this->name.c_str(), + this->regs[ATI_DP_MIX], this->regs[ATI_DP_PIX_WIDTH], + this->regs[ATI_CLR_CMP_CNTL]); + return; + } + + this->host_dst_width = dst_width; + this->host_dst_height = dst_height; + this->host_dst_col = 0; + this->host_dst_row = 0; + this->host_data_active = true; +} + +void ATIRage::write_host_data(uint32_t value, uint32_t size) { + if (!this->host_data_active) { + return; + } + + int dst_offs = extract_bits( + this->regs[ATI_DST_OFF_PITCH], ATI_DST_OFFSET, ATI_DST_OFFSET_size) * + 8; + int dst_pitch = + extract_bits(this->regs[ATI_DST_OFF_PITCH], ATI_DST_PITCH, ATI_DST_PITCH_size) * 8; + int dst_x = mach64_extract_signed(this->regs[ATI_DST_X], ATI_DST_X_pos, ATI_DST_X_size); + int dst_y = mach64_extract_signed(this->regs[ATI_DST_Y], ATI_DST_Y_pos, ATI_DST_Y_size); + int sc_left = mach64_extract_signed(this->regs[ATI_SC_LEFT], ATI_SC_LEFT_pos, ATI_SC_LEFT_size); + int sc_right = mach64_extract_signed( + this->regs[ATI_SC_RIGHT], ATI_SC_RIGHT_pos, ATI_SC_RIGHT_size); + int sc_top = mach64_extract_signed(this->regs[ATI_SC_TOP], ATI_SC_TOP_pos, ATI_SC_TOP_size); + int sc_bottom = mach64_extract_signed( + this->regs[ATI_SC_BOTTOM], ATI_SC_BOTTOM_pos, ATI_SC_BOTTOM_size); + int x_inc = bit_set(this->regs[ATI_DST_CNTL], ATI_DST_X_DIR) ? 1 : -1; + int y_inc = bit_set(this->regs[ATI_DST_CNTL], ATI_DST_Y_DIR) ? 1 : -1; + uint8_t write_mask = this->regs[ATI_DP_WRITE_MSK]; + + for (uint32_t byte = 0; byte < size && this->host_data_active; byte++) { + int shift = x_inc > 0 ? int(byte) * 8 : int(size - byte - 1) * 8; + uint8_t pixel = value >> shift; + int x = dst_x + int(this->host_dst_col) * x_inc; + int y = dst_y + int(this->host_dst_row) * y_inc; + + if (x >= sc_left && x <= sc_right && y >= sc_top && y <= sc_bottom) { + uint8_t* dst = &this->vram_ptr[dst_offs + y * dst_pitch + x]; + *dst = (*dst & ~write_mask) | (pixel & write_mask); + this->draw_fb = true; + } + + if (++this->host_dst_col >= this->host_dst_width) { + this->host_dst_col = 0; + if (++this->host_dst_row >= this->host_dst_height) { + this->host_data_active = false; + this->finish_rect(this->host_dst_width, this->host_dst_height); + } + } + } +} + void ATIRage::blit_rect(uint32_t dst_width, uint32_t dst_height) { uint8_t frgd_mix = extract_bits(this->regs[ATI_DP_MIX], ATI_DP_FRGD_MIX, ATI_DP_FRGD_MIX_size); @@ -1168,7 +1387,11 @@ void ATIRage::blit_rect(uint32_t dst_width, uint32_t dst_height) { uint32_t pixel_mask = bytes_per_pixel == 4 ? 0xFFFFFFFFU : ((1U << (bytes_per_pixel * 8)) - 1); - if (frgd_mix != 7 || this->regs[ATI_SRC_CNTL] || this->regs[ATI_CLR_CMP_CNTL] || + uint32_t src_cntl = this->regs[ATI_SRC_CNTL]; + bool supported_src_trajectory = src_cntl == ATI_SRC_TRAJ_UNBOUNDED || + src_cntl == ATI_SRC_TRAJ_PATTERN || src_cntl == ATI_SRC_TRAJ_ROTATED; + + if (frgd_mix != ATI_DP_MIX_SRC || !supported_src_trajectory || this->regs[ATI_CLR_CMP_CNTL] || src_pix_fmt != dst_pix_fmt || !bytes_per_pixel || (this->regs[ATI_DP_WRITE_MSK] & pixel_mask) != pixel_mask) { LOG_F(WARNING, "%s: unsupported rectangle blit, DP_SRC=0x%08X, DP_MIX=0x%08X, " @@ -1179,27 +1402,92 @@ void ATIRage::blit_rect(uint32_t dst_width, uint32_t dst_height) { return; } - int src_offs = extract_bits(this->regs[ATI_SRC_OFF_PITCH], ATI_SRC_OFFSET, - ATI_SRC_OFFSET_size) * 8; - int src_pitch = extract_bits(this->regs[ATI_SRC_OFF_PITCH], ATI_SRC_PITCH, - ATI_SRC_PITCH_size) * 8 * bytes_per_pixel; - int src_x = extract_bits(this->regs[ATI_SRC_X], ATI_SRC_X_pos, ATI_SRC_X_size); - int src_y = extract_bits(this->regs[ATI_SRC_Y], ATI_SRC_Y_pos, ATI_SRC_Y_size); - - int dst_offs = extract_bits(this->regs[ATI_DST_OFF_PITCH], ATI_DST_OFFSET, - ATI_DST_OFFSET_size) * 8; - int dst_pitch = extract_bits(this->regs[ATI_DST_OFF_PITCH], ATI_DST_PITCH, - ATI_DST_PITCH_size) * 8 * bytes_per_pixel; - int dst_x = extract_bits(this->regs[ATI_DST_X], ATI_DST_X_pos, ATI_DST_X_size); - int dst_y = extract_bits(this->regs[ATI_DST_Y], ATI_DST_Y_pos, ATI_DST_Y_size); + int src_offs = extract_bits( + this->regs[ATI_SRC_OFF_PITCH], ATI_SRC_OFFSET, ATI_SRC_OFFSET_size) * + 8; + int src_pitch = extract_bits( + this->regs[ATI_SRC_OFF_PITCH], ATI_SRC_PITCH, ATI_SRC_PITCH_size) * + 8 * bytes_per_pixel; + int src_x = mach64_extract_signed(this->regs[ATI_SRC_X], ATI_SRC_X_pos, ATI_SRC_X_size); + int src_y = mach64_extract_signed(this->regs[ATI_SRC_Y], ATI_SRC_Y_pos, ATI_SRC_Y_size); + int src_x_start = mach64_extract_signed( + this->regs[ATI_SRC_X_START], ATI_SRC_X_START_pos, ATI_SRC_X_START_size); + int src_y_start = mach64_extract_signed( + this->regs[ATI_SRC_Y_START], ATI_SRC_Y_START_pos, ATI_SRC_Y_START_size); + uint32_t src_width1 = extract_bits( + this->regs[ATI_SRC_WIDTH1], ATI_SRC_WIDTH1_pos, ATI_SRC_WIDTH1_size); + uint32_t src_height1 = extract_bits( + this->regs[ATI_SRC_HEIGHT1], ATI_SRC_HEIGHT1_pos, ATI_SRC_HEIGHT1_size); + uint32_t src_width2 = extract_bits( + this->regs[ATI_SRC_WIDTH2], ATI_SRC_WIDTH2_pos, ATI_SRC_WIDTH2_size); + uint32_t src_height2 = extract_bits( + this->regs[ATI_SRC_HEIGHT2], ATI_SRC_HEIGHT2_pos, ATI_SRC_HEIGHT2_size); + + if ((src_cntl == ATI_SRC_TRAJ_PATTERN && (!src_width1 || !src_height1)) || + (src_cntl == ATI_SRC_TRAJ_ROTATED && + (!src_width1 || !src_height1 || !src_width2 || !src_height2))) { + LOG_F(WARNING, "%s: invalid rectangle blit source trajectory, SRC_CNTL=0x%08X, " + "SRC_HEIGHT1_WIDTH1=0x%08X, SRC_HEIGHT2_WIDTH2=0x%08X", this->name.c_str(), + src_cntl, this->regs[ATI_SRC_HEIGHT1_WIDTH1], + this->regs[ATI_SRC_HEIGHT2_WIDTH2]); + return; + } - int x_inc = (this->regs[ATI_DST_CNTL] & 1) ? 1 : -1; - int y_inc = (this->regs[ATI_DST_CNTL] & 2) ? 1 : -1; + int dst_offs = extract_bits( + this->regs[ATI_DST_OFF_PITCH], ATI_DST_OFFSET, ATI_DST_OFFSET_size) * + 8; + int dst_pitch = extract_bits( + this->regs[ATI_DST_OFF_PITCH], ATI_DST_PITCH, ATI_DST_PITCH_size) * + 8 * bytes_per_pixel; + int dst_x = mach64_extract_signed(this->regs[ATI_DST_X], ATI_DST_X_pos, ATI_DST_X_size); + int dst_y = mach64_extract_signed(this->regs[ATI_DST_Y], ATI_DST_Y_pos, ATI_DST_Y_size); + + int x_inc = bit_set(this->regs[ATI_DST_CNTL], ATI_DST_X_DIR) ? 1 : -1; + int y_inc = bit_set(this->regs[ATI_DST_CNTL], ATI_DST_Y_DIR) ? 1 : -1; + + int sc_left = mach64_extract_signed(this->regs[ATI_SC_LEFT], ATI_SC_LEFT_pos, ATI_SC_LEFT_size); + int sc_right = mach64_extract_signed( + this->regs[ATI_SC_RIGHT], ATI_SC_RIGHT_pos, ATI_SC_RIGHT_size); + int sc_top = mach64_extract_signed(this->regs[ATI_SC_TOP], ATI_SC_TOP_pos, ATI_SC_TOP_size); + int sc_bottom = mach64_extract_signed( + this->regs[ATI_SC_BOTTOM], ATI_SC_BOTTOM_pos, ATI_SC_BOTTOM_size); + + int x_skip, y_skip; + if (!mach64_clip_axis(dst_x, x_inc, dst_width, sc_left, sc_right, x_skip, dst_width) || + !mach64_clip_axis(dst_y, y_inc, dst_height, sc_top, sc_bottom, y_skip, dst_height)) { + return; + } + dst_x += x_skip * x_inc; + dst_y += y_skip * y_inc; for (uint32_t y = 0; y < dst_height; y++) { - uint8_t* src_row = &this->vram_ptr[src_offs + (src_y + int(y) * y_inc) * src_pitch]; - uint8_t* dst_row = &this->vram_ptr[dst_offs + (dst_y + int(y) * y_inc) * dst_pitch]; - for (int sx = src_x, dx = dst_x, width = dst_width; width-- > 0; sx += x_inc, dx += x_inc) { + uint32_t src_row_index = y_skip + y; + int sy; + if (src_cntl == ATI_SRC_TRAJ_PATTERN) { + sy = src_y + int(src_row_index % src_height1) * y_inc; + } else if (src_cntl == ATI_SRC_TRAJ_ROTATED && src_row_index >= src_height1) { + sy = src_y_start + int((src_row_index - src_height1) % src_height2) * y_inc; + } else { + sy = src_y + int(src_row_index) * y_inc; + } + + uint8_t* src_row = &this->vram_ptr[src_offs + sy * src_pitch]; + int dy = dst_y + int(y) * y_inc; + uint8_t* dst_row = &this->vram_ptr[dst_offs + dy * dst_pitch]; + for (uint32_t x = 0; x < dst_width; x++) { + uint32_t src_col_index = x_skip + x; + int sx; + if (src_cntl == ATI_SRC_TRAJ_PATTERN) { + sx = src_x + int(src_col_index % src_width1) * x_inc; + } else if (src_cntl == ATI_SRC_TRAJ_ROTATED && src_row_index > 0) { + sx = src_x_start + int(src_col_index % src_width2) * x_inc; + } else if (src_cntl == ATI_SRC_TRAJ_ROTATED && src_col_index >= src_width1) { + sx = src_x_start + int((src_col_index - src_width1) % src_width2) * x_inc; + } else { + sx = src_x + int(src_col_index) * x_inc; + } + + int dx = dst_x + int(x) * x_inc; uint8_t* src_pixel = &src_row[sx * bytes_per_pixel]; uint8_t* dst_pixel = &dst_row[dx * bytes_per_pixel]; for (int byte = 0; byte < bytes_per_pixel; byte++) { diff --git a/devices/video/atirage.h b/devices/video/atirage.h index 4ce37e507f..2b636197e5 100644 --- a/devices/video/atirage.h +++ b/devices/video/atirage.h @@ -92,14 +92,23 @@ class ATIRage : public PCIDevice, public VideoCtrlBase { void begin_drawing(uint32_t initiator, uint32_t value); void draw_rect(uint32_t width, uint32_t height); - void fill_rect(uint32_t dst_width, uint32_t dst_height); + void finish_rect(uint32_t width, uint32_t height); + void fill_rect(uint32_t dst_width, uint32_t dst_height, uint32_t color); void blit_rect(uint32_t dst_width, uint32_t dst_height); + void start_host_rect(uint32_t dst_width, uint32_t dst_height); + void write_host_data(uint32_t value, uint32_t size); uint32_t regs[512] = {}; // internal registers uint8_t plls[64] = {}; // internal PLL registers uint8_t cmd_fifo_size = 0; + bool host_data_active = false; + uint32_t host_dst_width = 0; + uint32_t host_dst_height = 0; + uint32_t host_dst_col = 0; + uint32_t host_dst_row = 0; + // Video RAM variables std::unique_ptr vram_ptr; uint32_t vram_size;