X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=hw%2Fcirrus_vga.c;h=85bf4a21ff174448ad7ee47ce561471479c6c31a;hb=cd346349b45ef056f138a184f660b8c34c3213cc;hp=2707f93865c219fb204fd4fed73133936fb7e739;hpb=46e50e9d58aa0fd6ab8f5cadceb8b55ee7e1d806;p=qemu diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c index 2707f93..85bf4a2 100644 --- a/hw/cirrus_vga.c +++ b/hw/cirrus_vga.c @@ -1,9 +1,9 @@ /* * QEMU Cirrus CLGD 54xx VGA Emulator. - * + * * Copyright (c) 2004 Fabrice Bellard * Copyright (c) 2004 Makoto Suzuki (suzu) - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -31,7 +31,7 @@ /* * TODO: - * - add support for WRITEMASK (GR2F) + * - destination write mask support not complete (bits 5..7) * - optimize linear mappings * - optimize bitblt functions */ @@ -259,9 +259,6 @@ typedef struct CirrusVGAState { uint8_t *cirrus_srcptr; uint8_t *cirrus_srcptr_end; uint32_t cirrus_srccounter; - uint8_t *cirrus_dstptr; - uint8_t *cirrus_dstptr_end; - uint32_t cirrus_dstcounter; /* hwcursor display state */ int last_hw_cursor_size; int last_hw_cursor_x; @@ -269,6 +266,7 @@ typedef struct CirrusVGAState { int last_hw_cursor_y_start; int last_hw_cursor_y_end; int real_vram_size; /* XXX: suppress that */ + CPUWriteMemoryFunc **cirrus_linear_write; } CirrusVGAState; typedef struct PCICirrusVGAState { @@ -277,7 +275,7 @@ typedef struct PCICirrusVGAState { } PCICirrusVGAState; static uint8_t rop_to_index[256]; - + /*************************************** * * prototypes. @@ -285,7 +283,8 @@ static uint8_t rop_to_index[256]; ***************************************/ -static void cirrus_bitblt_reset(CirrusVGAState * s); +static void cirrus_bitblt_reset(CirrusVGAState *s); +static void cirrus_update_memory_access(CirrusVGAState *s); /*************************************** * @@ -403,7 +402,54 @@ static const cirrus_bitblt_rop_t cirrus_bkwd_rop[16] = { cirrus_bitblt_rop_bkwd_notsrc_or_dst, cirrus_bitblt_rop_bkwd_notsrc_and_notdst, }; - + +#define TRANSP_ROP(name) {\ + name ## _8,\ + name ## _16,\ + } +#define TRANSP_NOP(func) {\ + func,\ + func,\ + } + +static const cirrus_bitblt_rop_t cirrus_fwd_transp_rop[16][2] = { + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_0), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_and_dst), + TRANSP_NOP(cirrus_bitblt_rop_nop), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_and_notdst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notdst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_1), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_and_dst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_xor_dst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_or_dst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_or_notdst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_notxor_dst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_or_notdst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_or_dst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_and_notdst), +}; + +static const cirrus_bitblt_rop_t cirrus_bkwd_transp_rop[16][2] = { + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_0), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_and_dst), + TRANSP_NOP(cirrus_bitblt_rop_nop), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_and_notdst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notdst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_1), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_and_dst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_xor_dst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_or_dst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_or_notdst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_notxor_dst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_or_notdst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_or_dst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_and_notdst), +}; + #define ROP2(name) {\ name ## _8,\ name ## _16,\ @@ -456,25 +502,6 @@ static const cirrus_bitblt_rop_t cirrus_colorexpand_transp[16][4] = { ROP2(cirrus_colorexpand_transp_notsrc_and_notdst), }; -static const cirrus_bitblt_rop_t cirrus_colorexpand_transp_inv[16][4] = { - ROP2(cirrus_colorexpand_transp_inv_0), - ROP2(cirrus_colorexpand_transp_inv_src_and_dst), - ROP_NOP2(cirrus_bitblt_rop_nop), - ROP2(cirrus_colorexpand_transp_inv_src_and_notdst), - ROP2(cirrus_colorexpand_transp_inv_notdst), - ROP2(cirrus_colorexpand_transp_inv_src), - ROP2(cirrus_colorexpand_transp_inv_1), - ROP2(cirrus_colorexpand_transp_inv_notsrc_and_dst), - ROP2(cirrus_colorexpand_transp_inv_src_xor_dst), - ROP2(cirrus_colorexpand_transp_inv_src_or_dst), - ROP2(cirrus_colorexpand_transp_inv_notsrc_or_notdst), - ROP2(cirrus_colorexpand_transp_inv_src_notxor_dst), - ROP2(cirrus_colorexpand_transp_inv_src_or_notdst), - ROP2(cirrus_colorexpand_transp_inv_notsrc), - ROP2(cirrus_colorexpand_transp_inv_notsrc_or_dst), - ROP2(cirrus_colorexpand_transp_inv_notsrc_and_notdst), -}; - static const cirrus_bitblt_rop_t cirrus_colorexpand[16][4] = { ROP2(cirrus_colorexpand_0), ROP2(cirrus_colorexpand_src_and_dst), @@ -494,6 +521,44 @@ static const cirrus_bitblt_rop_t cirrus_colorexpand[16][4] = { ROP2(cirrus_colorexpand_notsrc_and_notdst), }; +static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern_transp[16][4] = { + ROP2(cirrus_colorexpand_pattern_transp_0), + ROP2(cirrus_colorexpand_pattern_transp_src_and_dst), + ROP_NOP2(cirrus_bitblt_rop_nop), + ROP2(cirrus_colorexpand_pattern_transp_src_and_notdst), + ROP2(cirrus_colorexpand_pattern_transp_notdst), + ROP2(cirrus_colorexpand_pattern_transp_src), + ROP2(cirrus_colorexpand_pattern_transp_1), + ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_dst), + ROP2(cirrus_colorexpand_pattern_transp_src_xor_dst), + ROP2(cirrus_colorexpand_pattern_transp_src_or_dst), + ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_notdst), + ROP2(cirrus_colorexpand_pattern_transp_src_notxor_dst), + ROP2(cirrus_colorexpand_pattern_transp_src_or_notdst), + ROP2(cirrus_colorexpand_pattern_transp_notsrc), + ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_dst), + ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_notdst), +}; + +static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern[16][4] = { + ROP2(cirrus_colorexpand_pattern_0), + ROP2(cirrus_colorexpand_pattern_src_and_dst), + ROP_NOP2(cirrus_bitblt_rop_nop), + ROP2(cirrus_colorexpand_pattern_src_and_notdst), + ROP2(cirrus_colorexpand_pattern_notdst), + ROP2(cirrus_colorexpand_pattern_src), + ROP2(cirrus_colorexpand_pattern_1), + ROP2(cirrus_colorexpand_pattern_notsrc_and_dst), + ROP2(cirrus_colorexpand_pattern_src_xor_dst), + ROP2(cirrus_colorexpand_pattern_src_or_dst), + ROP2(cirrus_colorexpand_pattern_notsrc_or_notdst), + ROP2(cirrus_colorexpand_pattern_src_notxor_dst), + ROP2(cirrus_colorexpand_pattern_src_or_notdst), + ROP2(cirrus_colorexpand_pattern_notsrc), + ROP2(cirrus_colorexpand_pattern_notsrc_or_dst), + ROP2(cirrus_colorexpand_pattern_notsrc_and_notdst), +}; + static const cirrus_fill_t cirrus_fill[16][4] = { ROP2(cirrus_fill_0), ROP2(cirrus_fill_src_and_dst), @@ -525,7 +590,7 @@ static inline void cirrus_bitblt_fgcol(CirrusVGAState *s) s->cirrus_blt_fgcol = le16_to_cpu(color); break; case 3: - s->cirrus_blt_fgcol = s->cirrus_shadow_gr1 | + s->cirrus_blt_fgcol = s->cirrus_shadow_gr1 | (s->gr[0x11] << 8) | (s->gr[0x13] << 16); break; default: @@ -549,7 +614,7 @@ static inline void cirrus_bitblt_bgcol(CirrusVGAState *s) s->cirrus_blt_bgcol = le16_to_cpu(color); break; case 3: - s->cirrus_blt_bgcol = s->cirrus_shadow_gr0 | + s->cirrus_blt_bgcol = s->cirrus_shadow_gr0 | (s->gr[0x10] << 8) | (s->gr[0x12] << 16); break; default: @@ -584,30 +649,11 @@ static void cirrus_invalidate_region(CirrusVGAState * s, int off_begin, static int cirrus_bitblt_common_patterncopy(CirrusVGAState * s, const uint8_t * src) { - uint8_t work_colorexp[256]; uint8_t *dst; - int patternbytes = s->cirrus_blt_pixelwidth * 8; - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { - cirrus_bitblt_rop_t rop_func; - cirrus_bitblt_fgcol(s); - cirrus_bitblt_bgcol(s); - rop_func = cirrus_colorexpand[CIRRUS_ROP_SRC_INDEX][s->cirrus_blt_pixelwidth - 1]; - rop_func(s, work_colorexp, src, patternbytes, 1, patternbytes, 8); - src = work_colorexp; - s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_COLOREXPAND; - } - if (s->cirrus_blt_mode & ~CIRRUS_BLTMODE_PATTERNCOPY) { -#ifdef DEBUG_BITBLT - printf("cirrus: blt mode %02x (pattercopy) - unimplemented\n", - s->cirrus_blt_mode); -#endif - return 0; - } - dst = s->vram_ptr + s->cirrus_blt_dstaddr; (*s->cirrus_rop) (s, dst, src, - s->cirrus_blt_dstpitch, 0, + s->cirrus_blt_dstpitch, 0, s->cirrus_blt_width, s->cirrus_blt_height); cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, s->cirrus_blt_dstpitch, s->cirrus_blt_width, @@ -622,7 +668,7 @@ static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop) cirrus_fill_t rop_func; rop_func = cirrus_fill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; - rop_func(s, s->vram_ptr + s->cirrus_blt_dstaddr, + rop_func(s, s->vram_ptr + s->cirrus_blt_dstaddr, s->cirrus_blt_dstpitch, s->cirrus_blt_width, s->cirrus_blt_height); cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, @@ -641,19 +687,94 @@ static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop) static int cirrus_bitblt_videotovideo_patterncopy(CirrusVGAState * s) { return cirrus_bitblt_common_patterncopy(s, - s->vram_ptr + + s->vram_ptr + (s->cirrus_blt_srcaddr & ~7)); } -static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s) +static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h) { + int sx, sy; + int dx, dy; + int width, height; + int depth; + int notify = 0; + + depth = s->get_bpp((VGAState *)s) / 8; + s->get_resolution((VGAState *)s, &width, &height); + + /* extra x, y */ + sx = (src % (width * depth)) / depth; + sy = (src / (width * depth)); + dx = (dst % (width *depth)) / depth; + dy = (dst / (width * depth)); + + /* normalize width */ + w /= depth; + + /* if we're doing a backward copy, we have to adjust + our x/y to be the upper left corner (instead of the lower + right corner) */ + if (s->cirrus_blt_dstpitch < 0) { + sx -= (s->cirrus_blt_width / depth) - 1; + dx -= (s->cirrus_blt_width / depth) - 1; + sy -= s->cirrus_blt_height - 1; + dy -= s->cirrus_blt_height - 1; + } + + /* are we in the visible portion of memory? */ + if (sx >= 0 && sy >= 0 && dx >= 0 && dy >= 0 && + (sx + w) <= width && (sy + h) <= height && + (dx + w) <= width && (dy + h) <= height) { + notify = 1; + } + + /* make to sure only copy if it's a plain copy ROP */ + if (*s->cirrus_rop != cirrus_bitblt_rop_fwd_src && + *s->cirrus_rop != cirrus_bitblt_rop_bkwd_src) + notify = 0; + + /* we have to flush all pending changes so that the copy + is generated at the appropriate moment in time */ + if (notify) + vga_hw_update(); + (*s->cirrus_rop) (s, s->vram_ptr + s->cirrus_blt_dstaddr, s->vram_ptr + s->cirrus_blt_srcaddr, s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch, s->cirrus_blt_width, s->cirrus_blt_height); - cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, - s->cirrus_blt_dstpitch, s->cirrus_blt_width, - s->cirrus_blt_height); + + if (notify) + s->ds->dpy_copy(s->ds, + sx, sy, dx, dy, + s->cirrus_blt_width / depth, + s->cirrus_blt_height); + + /* we don't have to notify the display that this portion has + changed since dpy_copy implies this */ + + if (!notify) + cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, + s->cirrus_blt_dstpitch, s->cirrus_blt_width, + s->cirrus_blt_height); +} + +static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s) +{ + if (s->ds->dpy_copy) { + cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->start_addr, + s->cirrus_blt_srcaddr - s->start_addr, + s->cirrus_blt_width, s->cirrus_blt_height); + } else { + (*s->cirrus_rop) (s, s->vram_ptr + s->cirrus_blt_dstaddr, + s->vram_ptr + s->cirrus_blt_srcaddr, + s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch, + s->cirrus_blt_width, s->cirrus_blt_height); + + cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, + s->cirrus_blt_dstpitch, s->cirrus_blt_width, + s->cirrus_blt_height); + } + return 1; } @@ -667,7 +788,7 @@ static void cirrus_bitblt_cputovideo_next(CirrusVGAState * s) { int copy_count; uint8_t *end_ptr; - + if (s->cirrus_srccounter > 0) { if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) { cirrus_bitblt_common_patterncopy(s, s->cirrus_bltbuf); @@ -711,9 +832,7 @@ static void cirrus_bitblt_reset(CirrusVGAState * s) s->cirrus_srcptr = &s->cirrus_bltbuf[0]; s->cirrus_srcptr_end = &s->cirrus_bltbuf[0]; s->cirrus_srccounter = 0; - s->cirrus_dstptr = &s->cirrus_bltbuf[0]; - s->cirrus_dstptr_end = &s->cirrus_bltbuf[0]; - s->cirrus_dstcounter = 0; + cirrus_update_memory_access(s); } static int cirrus_bitblt_cputovideo(CirrusVGAState * s) @@ -728,23 +847,26 @@ static int cirrus_bitblt_cputovideo(CirrusVGAState * s) if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { s->cirrus_blt_srcpitch = 8; } else { + /* XXX: check for 24 bpp */ s->cirrus_blt_srcpitch = 8 * 8 * s->cirrus_blt_pixelwidth; } s->cirrus_srccounter = s->cirrus_blt_srcpitch; } else { if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { w = s->cirrus_blt_width / s->cirrus_blt_pixelwidth; - if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_DWORDGRANULARITY) + if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_DWORDGRANULARITY) s->cirrus_blt_srcpitch = ((w + 31) >> 5); else s->cirrus_blt_srcpitch = ((w + 7) >> 3); } else { - s->cirrus_blt_srcpitch = s->cirrus_blt_width; + /* always align input size to 32 bits */ + s->cirrus_blt_srcpitch = (s->cirrus_blt_width + 3) & ~3; } s->cirrus_srccounter = s->cirrus_blt_srcpitch * s->cirrus_blt_height; } s->cirrus_srcptr = s->cirrus_bltbuf; s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch; + cirrus_update_memory_access(s); return 1; } @@ -790,8 +912,8 @@ static void cirrus_bitblt_start(CirrusVGAState * s) blt_rop = s->gr[0x32]; #ifdef DEBUG_BITBLT - printf("rop=0x%02x mode=0x%02x modeext=0x%02x w=%d h=%d dpitch=%d spicth=%d daddr=0x%08x saddr=0x%08x writemask=0x%02x\n", - blt_rop, + printf("rop=0x%02x mode=0x%02x modeext=0x%02x w=%d h=%d dpitch=%d spitch=%d daddr=0x%08x saddr=0x%08x writemask=0x%02x\n", + blt_rop, s->cirrus_blt_mode, s->cirrus_blt_modeext, s->cirrus_blt_width, @@ -800,7 +922,7 @@ static void cirrus_bitblt_start(CirrusVGAState * s) s->cirrus_blt_srcpitch, s->cirrus_blt_dstaddr, s->cirrus_blt_srcaddr, - s->sr[0x2f]); + s->gr[0x2f]); #endif switch (s->cirrus_blt_mode & CIRRUS_BLTMODE_PIXELWIDTHMASK) { @@ -835,43 +957,68 @@ static void cirrus_bitblt_start(CirrusVGAState * s) } if ((s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_SOLIDFILL) && - (s->cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSDEST | + (s->cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSDEST | CIRRUS_BLTMODE_TRANSPARENTCOMP | - CIRRUS_BLTMODE_PATTERNCOPY | - CIRRUS_BLTMODE_COLOREXPAND)) == + CIRRUS_BLTMODE_PATTERNCOPY | + CIRRUS_BLTMODE_COLOREXPAND)) == (CIRRUS_BLTMODE_PATTERNCOPY | CIRRUS_BLTMODE_COLOREXPAND)) { cirrus_bitblt_fgcol(s); cirrus_bitblt_solidfill(s, blt_rop); } else { - if ((s->cirrus_blt_mode & (CIRRUS_BLTMODE_COLOREXPAND | - CIRRUS_BLTMODE_PATTERNCOPY)) == + if ((s->cirrus_blt_mode & (CIRRUS_BLTMODE_COLOREXPAND | + CIRRUS_BLTMODE_PATTERNCOPY)) == CIRRUS_BLTMODE_COLOREXPAND) { if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) { - if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) { + if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) cirrus_bitblt_bgcol(s); - s->cirrus_rop = cirrus_colorexpand_transp_inv[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; - } else { + else cirrus_bitblt_fgcol(s); - s->cirrus_rop = cirrus_colorexpand_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; - } + s->cirrus_rop = cirrus_colorexpand_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; } else { cirrus_bitblt_fgcol(s); cirrus_bitblt_bgcol(s); s->cirrus_rop = cirrus_colorexpand[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; } } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) { - s->cirrus_rop = cirrus_patternfill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; - } else { - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) { - s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch; - s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch; - s->cirrus_rop = cirrus_bkwd_rop[rop_to_index[blt_rop]]; + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) { + if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) + cirrus_bitblt_bgcol(s); + else + cirrus_bitblt_fgcol(s); + s->cirrus_rop = cirrus_colorexpand_pattern_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } else { + cirrus_bitblt_fgcol(s); + cirrus_bitblt_bgcol(s); + s->cirrus_rop = cirrus_colorexpand_pattern[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } } else { - s->cirrus_rop = cirrus_fwd_rop[rop_to_index[blt_rop]]; + s->cirrus_rop = cirrus_patternfill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; } - } - + } else { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) { + if (s->cirrus_blt_pixelwidth > 2) { + printf("src transparent without colorexpand must be 8bpp or 16bpp\n"); + goto bitblt_ignore; + } + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) { + s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch; + s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch; + s->cirrus_rop = cirrus_bkwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } else { + s->cirrus_rop = cirrus_fwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } + } else { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) { + s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch; + s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch; + s->cirrus_rop = cirrus_bkwd_rop[rop_to_index[blt_rop]]; + } else { + s->cirrus_rop = cirrus_fwd_rop[rop_to_index[blt_rop]]; + } + } + } // setup bitblt engine. if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSSRC) { if (!cirrus_bitblt_cputovideo(s)) @@ -912,13 +1059,13 @@ static void cirrus_write_bitblt(CirrusVGAState * s, unsigned reg_value) * ***************************************/ -static void cirrus_get_offsets(VGAState *s1, - uint32_t *pline_offset, - uint32_t *pstart_addr) +static void cirrus_get_offsets(VGAState *s1, + uint32_t *pline_offset, + uint32_t *pstart_addr, + uint32_t *pline_compare) { CirrusVGAState * s = (CirrusVGAState *)s1; - uint32_t start_addr; - uint32_t line_offset; + uint32_t start_addr, line_offset, line_compare; line_offset = s->cr[0x13] | ((s->cr[0x1b] & 0x10) << 4); @@ -931,6 +1078,11 @@ static void cirrus_get_offsets(VGAState *s1, | ((s->cr[0x1b] & 0x0c) << 15) | ((s->cr[0x1d] & 0x80) << 12); *pstart_addr = start_addr; + + line_compare = s->cr[0x18] | + ((s->cr[0x07] & 0x10) << 4) | + ((s->cr[0x09] & 0x40) << 3); + *pline_compare = line_compare; } static uint32_t cirrus_get_bpp16_depth(CirrusVGAState * s) @@ -996,10 +1148,10 @@ static int cirrus_get_bpp(VGAState *s1) static void cirrus_get_resolution(VGAState *s, int *pwidth, int *pheight) { int width, height; - + width = (s->cr[0x01] + 1) * 8; - height = s->cr[0x12] | - ((s->cr[0x07] & 0x02) << 7) | + height = s->cr[0x12] | + ((s->cr[0x07] & 0x02) << 7) | ((s->cr[0x07] & 0x40) << 3); height = (height + 1); /* interlace support */ @@ -1030,10 +1182,10 @@ static void cirrus_update_bank_ptr(CirrusVGAState * s, unsigned bank_index) else offset <<= 12; - if (s->vram_size <= offset) + if (s->real_vram_size <= offset) limit = 0; else - limit = s->vram_size - offset; + limit = s->real_vram_size - offset; if (((s->gr[0x0b] & 0x01) == 0) && (bank_index != 0)) { if (limit > 0x8000) { @@ -1186,7 +1338,6 @@ cirrus_hook_write_sr(CirrusVGAState * s, unsigned reg_index, int reg_value) case 0x14: // Scratch Register 2 case 0x15: // Scratch Register 3 case 0x16: // Performance Tuning Register - case 0x17: // Configuration Readback and Extended Control case 0x18: // Signature Generator Control case 0x19: // Signature Generator Result case 0x1a: // Signature Generator Result @@ -1201,6 +1352,10 @@ cirrus_hook_write_sr(CirrusVGAState * s, unsigned reg_index, int reg_value) reg_index, reg_value); #endif break; + case 0x17: // Configuration Readback and Extended Control + s->sr[reg_index] = (s->sr[reg_index] & 0x38) | (reg_value & 0xc7); + cirrus_update_memory_access(s); + break; default: #ifdef DEBUG_CIRRUS printf("cirrus: outport sr_index %02x, sr_value %02x\n", reg_index, @@ -1335,13 +1490,19 @@ cirrus_hook_write_gr(CirrusVGAState * s, unsigned reg_index, int reg_value) return CIRRUS_HOOK_NOT_HANDLED; case 0x05: // Standard VGA, Cirrus extended mode s->gr[reg_index] = reg_value & 0x7f; + cirrus_update_memory_access(s); break; case 0x09: // bank offset #0 case 0x0A: // bank offset #1 + s->gr[reg_index] = reg_value; + cirrus_update_bank_ptr(s, 0); + cirrus_update_bank_ptr(s, 1); + break; case 0x0B: s->gr[reg_index] = reg_value; cirrus_update_bank_ptr(s, 0); cirrus_update_bank_ptr(s, 1); + cirrus_update_memory_access(s); break; case 0x10: // BGCOLOR 0x0000ff00 case 0x11: // FGCOLOR 0x0000ff00 @@ -1492,6 +1653,7 @@ cirrus_hook_write_cr(CirrusVGAState * s, unsigned reg_index, int reg_value) case 0x1a: // Miscellaneous Control case 0x1b: // Extended Display Control case 0x1c: // Sync Adjust and Genlock + case 0x1d: // Overlay Extended Control s->cr[reg_index] = reg_value; #ifdef DEBUG_CIRRUS printf("cirrus: handled outport cr_index %02x, cr_value %02x\n", @@ -1503,7 +1665,6 @@ cirrus_hook_write_cr(CirrusVGAState * s, unsigned reg_index, int reg_value) case 0x26: // Attribute Controller Index Readback (R) case 0x27: // Part ID (R) break; - case 0x1d: // Overlay Extended Control case 0x25: // Part Status default: #ifdef DEBUG_CIRRUS @@ -1759,11 +1920,12 @@ static void cirrus_mem_writeb_mode4and5_8bpp(CirrusVGAState * s, dst = s->vram_ptr + offset; for (x = 0; x < 8; x++) { if (val & 0x80) { - *dst++ = s->cirrus_shadow_gr1; + *dst = s->cirrus_shadow_gr1; } else if (mode == 5) { - *dst++ = s->cirrus_shadow_gr0; + *dst = s->cirrus_shadow_gr0; } val <<= 1; + dst++; } cpu_physical_memory_set_dirty(s->vram_offset + offset); cpu_physical_memory_set_dirty(s->vram_offset + offset + 7); @@ -1781,13 +1943,14 @@ static void cirrus_mem_writeb_mode4and5_16bpp(CirrusVGAState * s, dst = s->vram_ptr + offset; for (x = 0; x < 8; x++) { if (val & 0x80) { - *dst++ = s->cirrus_shadow_gr1; - *dst++ = s->gr[0x11]; + *dst = s->cirrus_shadow_gr1; + *(dst + 1) = s->gr[0x11]; } else if (mode == 5) { - *dst++ = s->cirrus_shadow_gr0; - *dst++ = s->gr[0x10]; + *dst = s->cirrus_shadow_gr0; + *(dst + 1) = s->gr[0x10]; } val <<= 1; + dst += 2; } cpu_physical_memory_set_dirty(s->vram_offset + offset); cpu_physical_memory_set_dirty(s->vram_offset + offset + 15); @@ -1873,7 +2036,7 @@ static uint32_t cirrus_vga_mem_readl(void *opaque, target_phys_addr_t addr) return v; } -static void cirrus_vga_mem_writeb(void *opaque, target_phys_addr_t addr, +static void cirrus_vga_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t mem_value) { CirrusVGAState *s = opaque; @@ -1984,7 +2147,7 @@ static CPUWriteMemoryFunc *cirrus_vga_mem_write[3] = { static inline void invalidate_cursor1(CirrusVGAState *s) { if (s->last_hw_cursor_size) { - vga_invalidate_scanlines((VGAState *)s, + vga_invalidate_scanlines((VGAState *)s, s->last_hw_cursor_y + s->last_hw_cursor_y_start, s->last_hw_cursor_y + s->last_hw_cursor_y_end); } @@ -2060,7 +2223,7 @@ static void cirrus_cursor_invalidate(VGAState *s1) s->last_hw_cursor_y != s->hw_cursor_y) { invalidate_cursor1(s); - + s->last_hw_cursor_size = size; s->last_hw_cursor_x = s->hw_cursor_x; s->last_hw_cursor_y = s->hw_cursor_y; @@ -2077,8 +2240,8 @@ static void cirrus_cursor_draw_line(VGAState *s1, uint8_t *d1, int scr_y) unsigned int color0, color1; const uint8_t *palette, *src; uint32_t content; - - if (!(s->sr[0x12] & CIRRUS_CURSOR_SHOW)) + + if (!(s->sr[0x12] & CIRRUS_CURSOR_SHOW)) return; /* fast test to see if the cursor intersects with the scan line */ if (s->sr[0x12] & CIRRUS_CURSOR_LARGE) { @@ -2089,7 +2252,7 @@ static void cirrus_cursor_draw_line(VGAState *s1, uint8_t *d1, int scr_y) if (scr_y < s->hw_cursor_y || scr_y >= (s->hw_cursor_y + h)) return; - + src = s->vram_ptr + s->real_vram_size - 16 * 1024; if (s->sr[0x12] & CIRRUS_CURSOR_LARGE) { src += (s->sr[0x13] & 0x3c) * 256; @@ -2119,11 +2282,11 @@ static void cirrus_cursor_draw_line(VGAState *s1, uint8_t *d1, int scr_y) x2 = s->last_scr_width; w = x2 - x1; palette = s->cirrus_hidden_palette; - color0 = s->rgb_to_pixel(c6_to_8(palette[0x0 * 3]), - c6_to_8(palette[0x0 * 3 + 1]), + color0 = s->rgb_to_pixel(c6_to_8(palette[0x0 * 3]), + c6_to_8(palette[0x0 * 3 + 1]), c6_to_8(palette[0x0 * 3 + 2])); - color1 = s->rgb_to_pixel(c6_to_8(palette[0xf * 3]), - c6_to_8(palette[0xf * 3 + 1]), + color1 = s->rgb_to_pixel(c6_to_8(palette[0xf * 3]), + c6_to_8(palette[0xf * 3 + 1]), c6_to_8(palette[0xf * 3 + 2])); bpp = ((s->ds->depth + 7) >> 3); d1 += x1 * bpp; @@ -2158,7 +2321,7 @@ static uint32_t cirrus_linear_readb(void *opaque, target_phys_addr_t addr) addr &= s->cirrus_addr_mask; - if (((s->sr[0x17] & 0x44) == 0x44) && + if (((s->sr[0x17] & 0x44) == 0x44) && ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) { /* memory-mapped I/O */ ret = cirrus_mmio_blt_read(s, addr & 0xff); @@ -2216,8 +2379,8 @@ static void cirrus_linear_writeb(void *opaque, target_phys_addr_t addr, unsigned mode; addr &= s->cirrus_addr_mask; - - if (((s->sr[0x17] & 0x44) == 0x44) && + + if (((s->sr[0x17] & 0x44) == 0x44) && ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) { /* memory-mapped I/O */ cirrus_mmio_blt_write(s, addr & 0xff, val); @@ -2291,6 +2454,36 @@ static CPUWriteMemoryFunc *cirrus_linear_write[3] = { cirrus_linear_writel, }; +static void cirrus_linear_mem_writeb(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + CirrusVGAState *s = (CirrusVGAState *) opaque; + + addr &= s->cirrus_addr_mask; + *(s->vram_ptr + addr) = val; + cpu_physical_memory_set_dirty(s->vram_offset + addr); +} + +static void cirrus_linear_mem_writew(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + CirrusVGAState *s = (CirrusVGAState *) opaque; + + addr &= s->cirrus_addr_mask; + cpu_to_le16w((uint16_t *)(s->vram_ptr + addr), val); + cpu_physical_memory_set_dirty(s->vram_offset + addr); +} + +static void cirrus_linear_mem_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + CirrusVGAState *s = (CirrusVGAState *) opaque; + + addr &= s->cirrus_addr_mask; + cpu_to_le32w((uint32_t *)(s->vram_ptr + addr), val); + cpu_physical_memory_set_dirty(s->vram_offset + addr); +} + /*************************************** * * system to screen memory access @@ -2392,6 +2585,37 @@ static CPUWriteMemoryFunc *cirrus_linear_bitblt_write[3] = { cirrus_linear_bitblt_writel, }; +/* Compute the memory access functions */ +static void cirrus_update_memory_access(CirrusVGAState *s) +{ + unsigned mode; + + if ((s->sr[0x17] & 0x44) == 0x44) { + goto generic_io; + } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) { + goto generic_io; + } else { + if ((s->gr[0x0B] & 0x14) == 0x14) { + goto generic_io; + } else if (s->gr[0x0B] & 0x02) { + goto generic_io; + } + + mode = s->gr[0x05] & 0x7; + if (mode < 4 || mode > 5 || ((s->gr[0x0B] & 0x4) == 0)) { + s->cirrus_linear_write[0] = cirrus_linear_mem_writeb; + s->cirrus_linear_write[1] = cirrus_linear_mem_writew; + s->cirrus_linear_write[2] = cirrus_linear_mem_writel; + } else { + generic_io: + s->cirrus_linear_write[0] = cirrus_linear_writeb; + s->cirrus_linear_write[1] = cirrus_linear_writew; + s->cirrus_linear_write[2] = cirrus_linear_writel; + } + } +} + + /* I/O ports */ static uint32_t vga_ioport_read(void *opaque, uint32_t addr) @@ -2440,7 +2664,11 @@ static uint32_t vga_ioport_read(void *opaque, uint32_t addr) case 0x3c7: val = s->dac_state; break; - case 0x3c9: + case 0x3c8: + val = s->dac_write_index; + s->cirrus_hidden_dac_lockindex = 0; + break; + case 0x3c9: if (cirrus_hook_read_palette(s, &val)) break; val = s->palette[s->dac_read_index * 3 + s->dac_sub_index]; @@ -2604,7 +2832,7 @@ static void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) printf("vga: write CR%x = 0x%02x\n", s->cr_index, val); #endif /* handle CR0-7 protection */ - if ((s->cr[11] & 0x80) && s->cr_index <= 7) { + if ((s->cr[0x11] & 0x80) && s->cr_index <= 7) { /* can always write bit 4 of CR7 */ if (s->cr_index == 7) s->cr[7] = (s->cr[7] & ~0x10) | (val & 0x10); @@ -2616,7 +2844,7 @@ static void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) case 0x09: case 0x0c: case 0x0d: - case 0x12: /* veritcal display end */ + case 0x12: /* vertical display end */ s->cr[s->cr_index] = val; break; @@ -2736,6 +2964,105 @@ static CPUWriteMemoryFunc *cirrus_mmio_write[3] = { cirrus_mmio_writel, }; +/* load/save state */ + +static void cirrus_vga_save(QEMUFile *f, void *opaque) +{ + CirrusVGAState *s = opaque; + + if (s->pci_dev) + pci_device_save(s->pci_dev, f); + + qemu_put_be32s(f, &s->latch); + qemu_put_8s(f, &s->sr_index); + qemu_put_buffer(f, s->sr, 256); + qemu_put_8s(f, &s->gr_index); + qemu_put_8s(f, &s->cirrus_shadow_gr0); + qemu_put_8s(f, &s->cirrus_shadow_gr1); + qemu_put_buffer(f, s->gr + 2, 254); + qemu_put_8s(f, &s->ar_index); + qemu_put_buffer(f, s->ar, 21); + qemu_put_be32s(f, &s->ar_flip_flop); + qemu_put_8s(f, &s->cr_index); + qemu_put_buffer(f, s->cr, 256); + qemu_put_8s(f, &s->msr); + qemu_put_8s(f, &s->fcr); + qemu_put_8s(f, &s->st00); + qemu_put_8s(f, &s->st01); + + qemu_put_8s(f, &s->dac_state); + qemu_put_8s(f, &s->dac_sub_index); + qemu_put_8s(f, &s->dac_read_index); + qemu_put_8s(f, &s->dac_write_index); + qemu_put_buffer(f, s->dac_cache, 3); + qemu_put_buffer(f, s->palette, 768); + + qemu_put_be32s(f, &s->bank_offset); + + qemu_put_8s(f, &s->cirrus_hidden_dac_lockindex); + qemu_put_8s(f, &s->cirrus_hidden_dac_data); + + qemu_put_be32s(f, &s->hw_cursor_x); + qemu_put_be32s(f, &s->hw_cursor_y); + /* XXX: we do not save the bitblt state - we assume we do not save + the state when the blitter is active */ +} + +static int cirrus_vga_load(QEMUFile *f, void *opaque, int version_id) +{ + CirrusVGAState *s = opaque; + int ret; + + if (version_id > 2) + return -EINVAL; + + if (s->pci_dev && version_id >= 2) { + ret = pci_device_load(s->pci_dev, f); + if (ret < 0) + return ret; + } + + qemu_get_be32s(f, &s->latch); + qemu_get_8s(f, &s->sr_index); + qemu_get_buffer(f, s->sr, 256); + qemu_get_8s(f, &s->gr_index); + qemu_get_8s(f, &s->cirrus_shadow_gr0); + qemu_get_8s(f, &s->cirrus_shadow_gr1); + s->gr[0x00] = s->cirrus_shadow_gr0 & 0x0f; + s->gr[0x01] = s->cirrus_shadow_gr1 & 0x0f; + qemu_get_buffer(f, s->gr + 2, 254); + qemu_get_8s(f, &s->ar_index); + qemu_get_buffer(f, s->ar, 21); + qemu_get_be32s(f, &s->ar_flip_flop); + qemu_get_8s(f, &s->cr_index); + qemu_get_buffer(f, s->cr, 256); + qemu_get_8s(f, &s->msr); + qemu_get_8s(f, &s->fcr); + qemu_get_8s(f, &s->st00); + qemu_get_8s(f, &s->st01); + + qemu_get_8s(f, &s->dac_state); + qemu_get_8s(f, &s->dac_sub_index); + qemu_get_8s(f, &s->dac_read_index); + qemu_get_8s(f, &s->dac_write_index); + qemu_get_buffer(f, s->dac_cache, 3); + qemu_get_buffer(f, s->palette, 768); + + qemu_get_be32s(f, &s->bank_offset); + + qemu_get_8s(f, &s->cirrus_hidden_dac_lockindex); + qemu_get_8s(f, &s->cirrus_hidden_dac_data); + + qemu_get_be32s(f, &s->hw_cursor_x); + qemu_get_be32s(f, &s->hw_cursor_y); + + /* force refresh */ + s->graphic_mode = -1; + cirrus_update_bank_ptr(s, 0); + cirrus_update_bank_ptr(s, 1); + return 0; +} + /*************************************** * * initialize @@ -2783,15 +3110,16 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci) register_ioport_read(0x3ba, 1, 1, vga_ioport_read, s); register_ioport_read(0x3da, 1, 1, vga_ioport_read, s); - vga_io_memory = cpu_register_io_memory(0, cirrus_vga_mem_read, + vga_io_memory = cpu_register_io_memory(0, cirrus_vga_mem_read, cirrus_vga_mem_write, s); - cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000, + cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000, vga_io_memory); s->sr[0x06] = 0x0f; - s->sr[0x1F] = 0x22; // MemClock if (device_id == CIRRUS_ID_CLGD5446) { /* 4MB 64 bit memory config, always PCI */ + s->sr[0x1F] = 0x2d; // MemClock + s->gr[0x18] = 0x0f; // fastest memory configuration #if 1 s->sr[0x0f] = 0x98; s->sr[0x17] = 0x20; @@ -2804,8 +3132,9 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci) s->real_vram_size = 2048 * 1024; #endif } else { + s->sr[0x1F] = 0x22; // MemClock s->sr[0x0F] = CIRRUS_MEMSIZE_2M; - if (is_pci) + if (is_pci) s->sr[0x17] = CIRRUS_BUSTYPE_PCI; else s->sr[0x17] = CIRRUS_BUSTYPE_ISA; @@ -2825,6 +3154,8 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci) s->cirrus_linear_io_addr = cpu_register_io_memory(0, cirrus_linear_read, cirrus_linear_write, s); + s->cirrus_linear_write = cpu_get_io_memory_write(s->cirrus_linear_io_addr); + /* I/O handler for LFB */ s->cirrus_linear_bitblt_io_addr = cpu_register_io_memory(0, cirrus_linear_bitblt_read, cirrus_linear_bitblt_write, @@ -2843,6 +3174,8 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci) s->get_resolution = cirrus_get_resolution; s->cursor_invalidate = cirrus_cursor_invalidate; s->cursor_draw_line = cirrus_cursor_draw_line; + + register_savevm("cirrus_vga", 0, 2, cirrus_vga_save, cirrus_vga_load, s); } /*************************************** @@ -2851,14 +3184,14 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci) * ***************************************/ -void isa_cirrus_vga_init(DisplayState *ds, uint8_t *vga_ram_base, +void isa_cirrus_vga_init(DisplayState *ds, uint8_t *vga_ram_base, unsigned long vga_ram_offset, int vga_ram_size) { CirrusVGAState *s; s = qemu_mallocz(sizeof(CirrusVGAState)); - - vga_common_init((VGAState *)s, + + vga_common_init((VGAState *)s, ds, vga_ram_base, vga_ram_offset, vga_ram_size); cirrus_init_common(s, CIRRUS_ID_CLGD5430, 0); /* XXX ISA-LFB support */ @@ -2891,19 +3224,19 @@ static void cirrus_pci_mmio_map(PCIDevice *d, int region_num, s->cirrus_mmio_io_addr); } -void pci_cirrus_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base, +void pci_cirrus_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base, unsigned long vga_ram_offset, int vga_ram_size) { PCICirrusVGAState *d; uint8_t *pci_conf; CirrusVGAState *s; int device_id; - + device_id = CIRRUS_ID_CLGD5446; /* setup PCI configuration registers */ - d = (PCICirrusVGAState *)pci_register_device(bus, "Cirrus VGA", - sizeof(PCICirrusVGAState), + d = (PCICirrusVGAState *)pci_register_device(bus, "Cirrus VGA", + sizeof(PCICirrusVGAState), -1, NULL, NULL); pci_conf = d->dev.config; pci_conf[0x00] = (uint8_t) (PCI_VENDOR_CIRRUS & 0xff); @@ -2917,10 +3250,14 @@ void pci_cirrus_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base, /* setup VGA */ s = &d->cirrus_vga; - vga_common_init((VGAState *)s, + vga_common_init((VGAState *)s, ds, vga_ram_base, vga_ram_offset, vga_ram_size); cirrus_init_common(s, device_id, 1); + graphic_console_init(s->ds, s->update, s->invalidate, s->screen_dump, s); + + s->pci_dev = (PCIDevice *)d; + /* setup memory space */ /* memory #0 LFB */ /* memory #1 memory-mapped I/O */