@ -47,6 +47,7 @@ static int ati_bpp_from_datatype(const ATIVGAState *s)
typedef struct {
int bpp ;
uint32_t rop3 ;
bool host_data_active ;
bool left_to_right ;
bool top_to_bottom ;
uint32_t frgd_clr ;
@ -85,6 +86,7 @@ static void setup_2d_blt_ctx(const ATIVGAState *s, ATI2DCtx *ctx)
{
ctx - > bpp = ati_bpp_from_datatype ( s ) ;
ctx - > rop3 = s - > regs . dp_mix & GMC_ROP3_MASK ;
ctx - > host_data_active = s - > host_data . active ;
ctx - > left_to_right = s - > regs . dp_cntl & DST_X_LEFT_TO_RIGHT ;
ctx - > top_to_bottom = s - > regs . dp_cntl & DST_Y_TOP_TO_BOTTOM ;
ctx - > frgd_clr = s - > regs . dp_brush_frgd_clr ;
@ -178,9 +180,10 @@ static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman)
qemu_log_mask ( LOG_GUEST_ERROR , " Zero source pitch \n " ) ;
return false ;
}
if ( vis_src . x > 0x3fff | | vis_src . y > 0x3fff | |
if ( ! ctx - > host_data_active & &
( vis_src . x > 0x3fff | | vis_src . y > 0x3fff | |
ctx - > src_bits > = ctx - > vram_end | | ctx - > src_bits + vis_src . x +
( vis_src . y + vis_dst . height ) * ctx - > src_stride > = ctx - > vram_end ) {
( vis_src . y + vis_dst . height ) * ctx - > src_stride > = ctx - > vram_end ) ) {
qemu_log_mask ( LOG_UNIMP , " blt outside vram not implemented \n " ) ;
return false ;
}
@ -300,8 +303,133 @@ static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman)
void ati_2d_blt ( ATIVGAState * s )
{
ATI2DCtx ctx ;
uint32_t src_source = s - > regs . dp_mix & DP_SRC_SOURCE ;
/* Finish any active HOST_DATA blits before starting a new blit */
ati_host_data_finish ( s ) ;
if ( src_source = = DP_SRC_HOST | | src_source = = DP_SRC_HOST_BYTEALIGN ) {
/* Begin a HOST_DATA blit */
s - > host_data . active = true ;
s - > host_data . next = 0 ;
s - > host_data . col = 0 ;
s - > host_data . row = 0 ;
return ;
}
setup_2d_blt_ctx ( s , & ctx ) ;
if ( ati_2d_do_blt ( & ctx , s - > use_pixman ) ) {
ati_set_dirty ( & s - > vga , & ctx ) ;
}
}
bool ati_host_data_flush ( ATIVGAState * s )
{
ATI2DCtx ctx , chunk ;
uint32_t fg = s - > regs . dp_src_frgd_clr ;
uint32_t bg = s - > regs . dp_src_bkgd_clr ;
unsigned bypp , pix_count , row , col , idx ;
uint8_t pix_buf [ ATI_HOST_DATA_ACC_BITS * sizeof ( uint32_t ) ] ;
uint32_t byte_pix_order = s - > regs . dp_datatype & DP_BYTE_PIX_ORDER ;
uint32_t src_source = s - > regs . dp_mix & DP_SRC_SOURCE ;
uint32_t src_datatype = s - > regs . dp_datatype & DP_SRC_DATATYPE ;
if ( ! s - > host_data . active ) {
return false ;
}
if ( src_source ! = DP_SRC_HOST ) {
qemu_log_mask ( LOG_GUEST_ERROR ,
" host_data_blt: unsupported src_source %x \n " , src_source ) ;
return false ;
}
if ( src_datatype ! = SRC_MONO_FRGD_BKGD & & src_datatype ! = SRC_MONO_FRGD & &
src_datatype ! = SRC_COLOR ) {
qemu_log_mask ( LOG_GUEST_ERROR ,
" host_data_blt: undefined src_datatype %x \n " ,
src_datatype ) ;
return false ;
}
setup_2d_blt_ctx ( s , & ctx ) ;
if ( ! ctx . left_to_right | | ! ctx . top_to_bottom ) {
qemu_log_mask ( LOG_UNIMP ,
" host_data_blt: unsupported blit direction %c%c \n " ,
ctx . left_to_right ? ' > ' : ' < ' ,
ctx . top_to_bottom ? ' v ' : ' ^ ' ) ;
return false ;
}
bypp = ctx . bpp / 8 ;
if ( src_datatype = = SRC_COLOR ) {
pix_count = ATI_HOST_DATA_ACC_BITS / ctx . bpp ;
memcpy ( pix_buf , & s - > host_data . acc [ 0 ] , sizeof ( s - > host_data . acc ) ) ;
} else {
pix_count = ATI_HOST_DATA_ACC_BITS ;
/* Expand monochrome bits to color pixels */
idx = 0 ;
for ( int word = 0 ; word < 4 ; word + + ) {
for ( int byte = 0 ; byte < 4 ; byte + + ) {
uint8_t byte_val = s - > host_data . acc [ word ] > > ( byte * 8 ) ;
for ( int i = 0 ; i < 8 ; i + + ) {
bool is_fg = byte_val & BIT ( byte_pix_order ? i : 7 - i ) ;
uint32_t color = is_fg ? fg : bg ;
stn_he_p ( & pix_buf [ idx ] , bypp , color ) ;
idx + = bypp ;
}
}
}
}
/* Copy and then modify blit ctx for use in a chunked blit */
chunk = ctx ;
chunk . src_bits = pix_buf ;
chunk . src . y = 0 ;
chunk . src_stride = ATI_HOST_DATA_ACC_BITS * bypp ;
/* Blit one scanline chunk at a time */
row = s - > host_data . row ;
col = s - > host_data . col ;
idx = 0 ;
DPRINTF ( " blt %dpx @ row: %d, col: %d \n " , pix_count , row , col ) ;
while ( idx < pix_count & & row < ctx . dst . height ) {
unsigned pix_in_scanline = MIN ( pix_count - idx ,
ctx . dst . width - col ) ;
chunk . src . x = idx ;
/* Build a rect for this scanline chunk */
chunk . dst . x = ctx . dst . x + col ;
chunk . dst . y = ctx . dst . y + row ;
chunk . dst . width = pix_in_scanline ;
chunk . dst . height = 1 ;
DPRINTF ( " blt %dpx span @ row: %d, col: %d to dst (%d,%d) \n " ,
pix_in_scanline , row , col , chunk . dst . x , chunk . dst . y ) ;
if ( ati_2d_do_blt ( & chunk , s - > use_pixman ) ) {
ati_set_dirty ( & s - > vga , & chunk ) ;
}
idx + = pix_in_scanline ;
col + = pix_in_scanline ;
if ( col > = ctx . dst . width ) {
col = 0 ;
row + = 1 ;
}
}
/* Track state of the overall blit for use by the next flush */
s - > host_data . next = 0 ;
s - > host_data . row = row ;
s - > host_data . col = col ;
if ( s - > host_data . row > = ctx . dst . height ) {
s - > host_data . active = false ;
}
return s - > host_data . active ;
}
void ati_host_data_finish ( ATIVGAState * s )
{
if ( ati_host_data_flush ( s ) ) {
qemu_log_mask ( LOG_GUEST_ERROR ,
" HOST_DATA blit ended before all data was written \n " ) ;
}
s - > host_data . active = false ;
}