static void RENAME(postProcess)(const uint8_t src[], int srcStride, uint8_t dst[], int dstStride, int width, int height,

                                const QP_STORE_T QPs[], int QPStride, int isColor, PPContext *c2)

{

    DECLARE_ALIGNED(8, PPContext, c)= *c2; //copy to stack for faster access

    int x,y;

#ifdef TEMPLATE_PP_TIME_MODE

    const int mode= TEMPLATE_PP_TIME_MODE;

#else

    const int mode= isColor ? c.ppMode.chromMode : c.ppMode.lumMode;

#endif

    int black=0, white=255; // blackest black and whitest white in the picture

    int QPCorrecture= 256*256;

    int copyAhead;

#if TEMPLATE_PP_MMX

    int i;

#endif

    const int qpHShift= isColor ? 4-c.hChromaSubSample : 4;

    const int qpVShift= isColor ? 4-c.vChromaSubSample : 4;

    //FIXME remove

    uint64_t * const yHistogram= c.yHistogram;

    uint8_t * const tempSrc= srcStride > 0 ? c.tempSrc : c.tempSrc - 23*srcStride;

    uint8_t * const tempDst= (dstStride > 0 ? c.tempDst : c.tempDst - 23*dstStride) + 32;

    //const int mbWidth= isColor ? (width+7)>>3 : (width+15)>>4;

    if (mode & VISUALIZE){

        if(!(mode & (V_A_DEBLOCK | H_A_DEBLOCK)) || TEMPLATE_PP_MMX) {

            av_log(c2, AV_LOG_WARNING, "Visualization is currently only supported with the accurate deblock filter without SIMD\n");

        }

    }

#if TEMPLATE_PP_MMX

    for(i=0; i<57; i++){

        int offset= ((i*c.ppMode.baseDcDiff)>>8) + 1;

        int threshold= offset*2 + 1;

        c.mmxDcOffset[i]= 0x7F - offset;

        c.mmxDcThreshold[i]= 0x7F - threshold;

        c.mmxDcOffset[i]*= 0x0101010101010101LL;

        c.mmxDcThreshold[i]*= 0x0101010101010101LL;

    }

#endif

    if(mode & CUBIC_IPOL_DEINT_FILTER) copyAhead=16;

    else if(   (mode & LINEAR_BLEND_DEINT_FILTER)

            || (mode & FFMPEG_DEINT_FILTER)

            || (mode & LOWPASS5_DEINT_FILTER)) copyAhead=14;

    else if(   (mode & V_DEBLOCK)

            || (mode & LINEAR_IPOL_DEINT_FILTER)

            || (mode & MEDIAN_DEINT_FILTER)

            || (mode & V_A_DEBLOCK)) copyAhead=13;

    else if(mode & V_X1_FILTER) copyAhead=11;

//    else if(mode & V_RK1_FILTER) copyAhead=10;

    else if(mode & DERING) copyAhead=9;

    else copyAhead=8;

    copyAhead-= 8;

    if(!isColor){

        uint64_t sum= 0;

        int i;

        uint64_t maxClipped;

        uint64_t clipped;

        double scale;

        c.frameNum++;

        // first frame is fscked so we ignore it

        if(c.frameNum == 1) yHistogram[0]= width*(uint64_t)height/64*15/256;

        for(i=0; i<256; i++){

            sum+= yHistogram[i];

        }

        /* We always get a completely black picture first. */

        maxClipped= (uint64_t)(sum * c.ppMode.maxClippedThreshold);

        clipped= sum;

        for(black=255; black>0; black--){

            if(clipped < maxClipped) break;

            clipped-= yHistogram[black];

        }

        clipped= sum;

        for(white=0; white<256; white++){

            if(clipped < maxClipped) break;

            clipped-= yHistogram[white];

        }

        scale= (double)(c.ppMode.maxAllowedY - c.ppMode.minAllowedY) / (double)(white-black);

#if TEMPLATE_PP_MMXEXT

        c.packedYScale= (uint16_t)(scale*256.0 + 0.5);

        c.packedYOffset= (((black*c.packedYScale)>>8) - c.ppMode.minAllowedY) & 0xFFFF;

#else

        c.packedYScale= (uint16_t)(scale*1024.0 + 0.5);

        c.packedYOffset= (black - c.ppMode.minAllowedY) & 0xFFFF;

#endif

        c.packedYOffset|= c.packedYOffset<<32;

        c.packedYOffset|= c.packedYOffset<<16;

        c.packedYScale|= c.packedYScale<<32;

        c.packedYScale|= c.packedYScale<<16;

        if(mode & LEVEL_FIX)        QPCorrecture= (int)(scale*256*256 + 0.5);

        else                        QPCorrecture= 256*256;

    }else{

        c.packedYScale= 0x0100010001000100LL;

        c.packedYOffset= 0;

        QPCorrecture= 256*256;

    }

    /* copy & deinterlace first row of blocks */

    y=-BLOCK_SIZE;

    {

        const uint8_t *srcBlock= &(src[y*srcStride]);

        uint8_t *dstBlock= tempDst + dstStride;

        // From this point on it is guaranteed that we can read and write 16 lines downward

        // finish 1 block before the next otherwise we might have a problem

        // with the L1 Cache of the P4 ... or only a few blocks at a time or something

        for(x=0; x<width; x+=BLOCK_SIZE){

#if TEMPLATE_PP_MMXEXT && HAVE_6REGS

/*

            prefetchnta(srcBlock + (((x>>2)&6) + 5)*srcStride + 32);

            prefetchnta(srcBlock + (((x>>2)&6) + 6)*srcStride + 32);

            prefetcht0(dstBlock + (((x>>2)&6) + 5)*dstStride + 32);

            prefetcht0(dstBlock + (((x>>2)&6) + 6)*dstStride + 32);

*/

            __asm__(

                "mov %4, %%"REG_a"              \n\t"

                "shr $2, %%"REG_a"              \n\t"

                "and $6, %%"REG_a"              \n\t"

                "add %5, %%"REG_a"              \n\t"

                "mov %%"REG_a", %%"REG_d"       \n\t"

                "imul %1, %%"REG_a"             \n\t"

                "imul %3, %%"REG_d"             \n\t"

                "prefetchnta 32(%%"REG_a", %0)  \n\t"

                "prefetcht0 32(%%"REG_d", %2)   \n\t"

                "add %1, %%"REG_a"              \n\t"

                "add %3, %%"REG_d"              \n\t"

                "prefetchnta 32(%%"REG_a", %0)  \n\t"

                "prefetcht0 32(%%"REG_d", %2)   \n\t"

                :: "r" (srcBlock), "r" ((x86_reg)srcStride), "r" (dstBlock), "r" ((x86_reg)dstStride),

                "g" ((x86_reg)x), "g" ((x86_reg)copyAhead)

                : "%"REG_a, "%"REG_d

            );

#elif TEMPLATE_PP_3DNOW

//FIXME check if this is faster on an 3dnow chip or if it is faster without the prefetch or ...

/*          prefetch(srcBlock + (((x>>3)&3) + 5)*srcStride + 32);

            prefetch(srcBlock + (((x>>3)&3) + 9)*srcStride + 32);

            prefetchw(dstBlock + (((x>>3)&3) + 5)*dstStride + 32);

            prefetchw(dstBlock + (((x>>3)&3) + 9)*dstStride + 32);

*/

#endif

            RENAME(blockCopy)(dstBlock + dstStride*8, dstStride,

                              srcBlock + srcStride*8, srcStride, mode & LEVEL_FIX, &c.packedYOffset);

            RENAME(duplicate)(dstBlock + dstStride*8, dstStride);

            if(mode & LINEAR_IPOL_DEINT_FILTER)

                RENAME(deInterlaceInterpolateLinear)(dstBlock, dstStride);

            else if(mode & LINEAR_BLEND_DEINT_FILTER)

                RENAME(deInterlaceBlendLinear)(dstBlock, dstStride, c.deintTemp + x);

            else if(mode & MEDIAN_DEINT_FILTER)

                RENAME(deInterlaceMedian)(dstBlock, dstStride);

            else if(mode & CUBIC_IPOL_DEINT_FILTER)

                RENAME(deInterlaceInterpolateCubic)(dstBlock, dstStride);

            else if(mode & FFMPEG_DEINT_FILTER)

                RENAME(deInterlaceFF)(dstBlock, dstStride, c.deintTemp + x);

            else if(mode & LOWPASS5_DEINT_FILTER)

                RENAME(deInterlaceL5)(dstBlock, dstStride, c.deintTemp + x, c.deintTemp + width + x);

/*          else if(mode & CUBIC_BLEND_DEINT_FILTER)

                RENAME(deInterlaceBlendCubic)(dstBlock, dstStride);

*/

            dstBlock+=8;

            srcBlock+=8;

        }

        if(width==FFABS(dstStride))

            linecpy(dst, tempDst + 9*dstStride, copyAhead, dstStride);

        else{

            int i;

            for(i=0; i<copyAhead; i++){

                memcpy(dst + i*dstStride, tempDst + (9+i)*dstStride, width);

            }

        }

    }

    for(y=0; y<height; y+=BLOCK_SIZE){

        //1% speedup if these are here instead of the inner loop

        const uint8_t *srcBlock= &(src[y*srcStride]);

        uint8_t *dstBlock= &(dst[y*dstStride]);

#if TEMPLATE_PP_MMX

        uint8_t *tempBlock1= c.tempBlocks;

        uint8_t *tempBlock2= c.tempBlocks + 8;

#endif

        const int8_t *QPptr= &QPs[(y>>qpVShift)*QPStride];

        int8_t *nonBQPptr= &c.nonBQPTable[(y>>qpVShift)*FFABS(QPStride)];

        int QP=0;

        /* can we mess with a 8x16 block from srcBlock/dstBlock downwards and 1 line upwards

           if not than use a temporary buffer */

        if(y+15 >= height){

            int i;

            /* copy from line (copyAhead) to (copyAhead+7) of src, these will be copied with

               blockcopy to dst later */

            linecpy(tempSrc + srcStride*copyAhead, srcBlock + srcStride*copyAhead,

                    FFMAX(height-y-copyAhead, 0), srcStride);

            /* duplicate last line of src to fill the void up to line (copyAhead+7) */

            for(i=FFMAX(height-y, 8); i<copyAhead+8; i++)

                    memcpy(tempSrc + srcStride*i, src + srcStride*(height-1), FFABS(srcStride));

            /* copy up to (copyAhead+1) lines of dst (line -1 to (copyAhead-1))*/

            linecpy(tempDst, dstBlock - dstStride, FFMIN(height-y+1, copyAhead+1), dstStride);

            /* duplicate last line of dst to fill the void up to line (copyAhead) */

            for(i=height-y+1; i<=copyAhead; i++)

                    memcpy(tempDst + dstStride*i, dst + dstStride*(height-1), FFABS(dstStride));

            dstBlock= tempDst + dstStride;

            srcBlock= tempSrc;

        }

        // From this point on it is guaranteed that we can read and write 16 lines downward

        // finish 1 block before the next otherwise we might have a problem

        // with the L1 Cache of the P4 ... or only a few blocks at a time or something

        for(x=0; x<width; x+=BLOCK_SIZE){

            const int stride= dstStride;

#if TEMPLATE_PP_MMX

            uint8_t *tmpXchg;

#endif

            if(isColor){

                QP= QPptr[x>>qpHShift];

                c.nonBQP= nonBQPptr[x>>qpHShift];

            }else{

                QP= QPptr[x>>4];

                QP= (QP* QPCorrecture + 256*128)>>16;

                c.nonBQP= nonBQPptr[x>>4];

                c.nonBQP= (c.nonBQP* QPCorrecture + 256*128)>>16;

                yHistogram[ srcBlock[srcStride*12 + 4] ]++;

            }

            c.QP= QP;

#if TEMPLATE_PP_MMX

            __asm__ volatile(

                "movd %1, %%mm7         \n\t"

                "packuswb %%mm7, %%mm7  \n\t" // 0, 0, 0, QP, 0, 0, 0, QP

                "packuswb %%mm7, %%mm7  \n\t" // 0,QP, 0, QP, 0,QP, 0, QP

                "packuswb %%mm7, %%mm7  \n\t" // QP,..., QP

                "movq %%mm7, %0         \n\t"

                : "=m" (c.pQPb)

                : "r" (QP)

            );

#endif

#if TEMPLATE_PP_MMXEXT && HAVE_6REGS

/*

            prefetchnta(srcBlock + (((x>>2)&6) + 5)*srcStride + 32);

            prefetchnta(srcBlock + (((x>>2)&6) + 6)*srcStride + 32);

            prefetcht0(dstBlock + (((x>>2)&6) + 5)*dstStride + 32);

            prefetcht0(dstBlock + (((x>>2)&6) + 6)*dstStride + 32);

*/

            __asm__(

                "mov %4, %%"REG_a"              \n\t"

                "shr $2, %%"REG_a"              \n\t"

                "and $6, %%"REG_a"              \n\t"

                "add %5, %%"REG_a"              \n\t"

                "mov %%"REG_a", %%"REG_d"       \n\t"

                "imul %1, %%"REG_a"             \n\t"

                "imul %3, %%"REG_d"             \n\t"

                "prefetchnta 32(%%"REG_a", %0)  \n\t"

                "prefetcht0 32(%%"REG_d", %2)   \n\t"

                "add %1, %%"REG_a"              \n\t"

                "add %3, %%"REG_d"              \n\t"

                "prefetchnta 32(%%"REG_a", %0)  \n\t"

                "prefetcht0 32(%%"REG_d", %2)   \n\t"

                :: "r" (srcBlock), "r" ((x86_reg)srcStride), "r" (dstBlock), "r" ((x86_reg)dstStride),

                "g" ((x86_reg)x), "g" ((x86_reg)copyAhead)

                : "%"REG_a, "%"REG_d

            );

#elif TEMPLATE_PP_3DNOW

//FIXME check if this is faster on an 3dnow chip or if it is faster without the prefetch or ...

/*          prefetch(srcBlock + (((x>>3)&3) + 5)*srcStride + 32);

            prefetch(srcBlock + (((x>>3)&3) + 9)*srcStride + 32);

            prefetchw(dstBlock + (((x>>3)&3) + 5)*dstStride + 32);

            prefetchw(dstBlock + (((x>>3)&3) + 9)*dstStride + 32);

*/

#endif

            RENAME(blockCopy)(dstBlock + dstStride*copyAhead, dstStride,

                              srcBlock + srcStride*copyAhead, srcStride, mode & LEVEL_FIX, &c.packedYOffset);

            if(mode & LINEAR_IPOL_DEINT_FILTER)

                RENAME(deInterlaceInterpolateLinear)(dstBlock, dstStride);

            else if(mode & LINEAR_BLEND_DEINT_FILTER)

                RENAME(deInterlaceBlendLinear)(dstBlock, dstStride, c.deintTemp + x);

            else if(mode & MEDIAN_DEINT_FILTER)

                RENAME(deInterlaceMedian)(dstBlock, dstStride);

            else if(mode & CUBIC_IPOL_DEINT_FILTER)

                RENAME(deInterlaceInterpolateCubic)(dstBlock, dstStride);

            else if(mode & FFMPEG_DEINT_FILTER)

                RENAME(deInterlaceFF)(dstBlock, dstStride, c.deintTemp + x);

            else if(mode & LOWPASS5_DEINT_FILTER)

                RENAME(deInterlaceL5)(dstBlock, dstStride, c.deintTemp + x, c.deintTemp + width + x);

/*          else if(mode & CUBIC_BLEND_DEINT_FILTER)

                RENAME(deInterlaceBlendCubic)(dstBlock, dstStride);

*/

            /* only deblock if we have 2 blocks */

            if(y + 8 < height){

                if(mode & V_X1_FILTER)

                    RENAME(vertX1Filter)(dstBlock, stride, &c);

                else if(mode & V_DEBLOCK){

                    const int t= RENAME(vertClassify)(dstBlock, stride, &c);

                    if(t==1)

                        RENAME(doVertLowPass)(dstBlock, stride, &c);

                    else if(t==2)

                        RENAME(doVertDefFilter)(dstBlock, stride, &c);

                }else if(mode & V_A_DEBLOCK){

                    RENAME(do_a_deblock)(dstBlock, stride, 1, &c, mode);

                }

            }

#if TEMPLATE_PP_MMX

            RENAME(transpose1)(tempBlock1, tempBlock2, dstBlock, dstStride);

#endif

            /* check if we have a previous block to deblock it with dstBlock */

            if(x - 8 >= 0){

#if TEMPLATE_PP_MMX

                if(mode & H_X1_FILTER)

                        RENAME(vertX1Filter)(tempBlock1, 16, &c);

                else if(mode & H_DEBLOCK){

//START_TIMER

                    const int t= RENAME(vertClassify)(tempBlock1, 16, &c);

//STOP_TIMER("dc & minmax")

                    if(t==1)

                        RENAME(doVertLowPass)(tempBlock1, 16, &c);

                    else if(t==2)

                        RENAME(doVertDefFilter)(tempBlock1, 16, &c);

                }else if(mode & H_A_DEBLOCK){

                        RENAME(do_a_deblock)(tempBlock1, 16, 1, &c, mode);

                }

                RENAME(transpose2)(dstBlock-4, dstStride, tempBlock1 + 4*16);

#else

                if(mode & H_X1_FILTER)

                    horizX1Filter(dstBlock-4, stride, QP);

                else if(mode & H_DEBLOCK){

#if TEMPLATE_PP_ALTIVEC

                    DECLARE_ALIGNED(16, unsigned char, tempBlock)[272];

                    int t;

                    transpose_16x8_char_toPackedAlign_altivec(tempBlock, dstBlock - (4 + 1), stride);

                    t = vertClassify_altivec(tempBlock-48, 16, &c);

                    if(t==1) {

                        doVertLowPass_altivec(tempBlock-48, 16, &c);

                        transpose_8x16_char_fromPackedAlign_altivec(dstBlock - (4 + 1), tempBlock, stride);

                    }

                    else if(t==2) {

                        doVertDefFilter_altivec(tempBlock-48, 16, &c);

                        transpose_8x16_char_fromPackedAlign_altivec(dstBlock - (4 + 1), tempBlock, stride);

                    }

#else

                    const int t= RENAME(horizClassify)(dstBlock-4, stride, &c);

                    if(t==1)

                        RENAME(doHorizLowPass)(dstBlock-4, stride, &c);

                    else if(t==2)

                        RENAME(doHorizDefFilter)(dstBlock-4, stride, &c);

#endif

                }else if(mode & H_A_DEBLOCK){

                    RENAME(do_a_deblock)(dstBlock-8, 1, stride, &c, mode);

                }

#endif //TEMPLATE_PP_MMX

                if(mode & DERING){

                //FIXME filter first line

                    if(y>0) RENAME(dering)(dstBlock - stride - 8, stride, &c);

                }

                if(mode & TEMP_NOISE_FILTER)

                {

                    RENAME(tempNoiseReducer)(dstBlock-8, stride,

                            c.tempBlurred[isColor] + y*dstStride + x,

                            c.tempBlurredPast[isColor] + (y>>3)*256 + (x>>3) + 256,

                            c.ppMode.maxTmpNoise);

                }

            }

            dstBlock+=8;

            srcBlock+=8;

#if TEMPLATE_PP_MMX

            tmpXchg= tempBlock1;

            tempBlock1= tempBlock2;

            tempBlock2 = tmpXchg;

#endif

        }

        if(mode & DERING){

            if(y > 0) RENAME(dering)(dstBlock - dstStride - 8, dstStride, &c);

        }

        if((mode & TEMP_NOISE_FILTER)){

            RENAME(tempNoiseReducer)(dstBlock-8, dstStride,

                    c.tempBlurred[isColor] + y*dstStride + x,

                    c.tempBlurredPast[isColor] + (y>>3)*256 + (x>>3) + 256,

                    c.ppMode.maxTmpNoise);

        }

        /* did we use a tmp buffer for the last lines*/

        if(y+15 >= height){

            uint8_t *dstBlock= &(dst[y*dstStride]);

            if(width==FFABS(dstStride))

                linecpy(dstBlock, tempDst + dstStride, height-y, dstStride);

            else{

                int i;

                for(i=0; i<height-y; i++){

                    memcpy(dstBlock + i*dstStride, tempDst + (i+1)*dstStride, width);

                }

            }

        }

/*

        for(x=0; x<width; x+=32){

            volatile int i;

            i+=   dstBlock[x + 7*dstStride] + dstBlock[x + 8*dstStride]

                + dstBlock[x + 9*dstStride] + dstBlock[x +10*dstStride]

                + dstBlock[x +11*dstStride] + dstBlock[x +12*dstStride];

                + dstBlock[x +13*dstStride]

                + dstBlock[x +14*dstStride] + dstBlock[x +15*dstStride];

        }*/

    }

#if   TEMPLATE_PP_3DNOW

    __asm__ volatile("femms");

#elif TEMPLATE_PP_MMX

    __asm__ volatile("emms");

#endif

#ifdef DEBUG_BRIGHTNESS

    if(!isColor){

        int max=1;

        int i;

        for(i=0; i<256; i++)

            if(yHistogram[i] > max) max=yHistogram[i];

        for(i=1; i<256; i++){

            int x;

            int start=yHistogram[i-1]/(max/256+1);

            int end=yHistogram[i]/(max/256+1);

            int inc= end > start ? 1 : -1;

            for(x=start; x!=end+inc; x+=inc)

                dst[ i*dstStride + x]+=128;

        }

        for(i=0; i<100; i+=2){

            dst[ (white)*dstStride + i]+=128;

            dst[ (black)*dstStride + i]+=128;

        }

    }

#endif

    *c2= c; //copy local context back

}


static char *choose_pix_fmts(OutputStream *ost)

{

     if (ost->keep_pix_fmt) {

        if (ost->filter)

            avfilter_graph_set_auto_convert(ost->filter->graph->graph,

                                            AVFILTER_AUTO_CONVERT_NONE);

        if (ost->st->codec->pix_fmt == PIX_FMT_NONE)

            return NULL;

        return av_strdup(av_get_pix_fmt_name(ost->st->codec->pix_fmt));

    }

    if (ost->st->codec->pix_fmt != PIX_FMT_NONE) {

        return av_strdup(av_get_pix_fmt_name(choose_pixel_fmt(ost->st, ost->enc, ost->st->codec->pix_fmt)));

    } else if (ost->enc->pix_fmts) {

        const enum PixelFormat *p;

        AVIOContext *s = NULL;

        uint8_t *ret;

        int len;

        if (avio_open_dyn_buf(&s) < 0)

            exit_program(1);

        p = ost->enc->pix_fmts;

        if (ost->st->codec->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL) {

            if (ost->st->codec->codec_id == CODEC_ID_MJPEG) {

                p = (const enum PixelFormat[]) { PIX_FMT_YUVJ420P, PIX_FMT_YUVJ422P, PIX_FMT_YUV420P, PIX_FMT_YUV422P, PIX_FMT_NONE };

            } else if (ost->st->codec->codec_id == CODEC_ID_LJPEG) {

                p = (const enum PixelFormat[]) { PIX_FMT_YUVJ420P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ444P, PIX_FMT_YUV420P,

                                                    PIX_FMT_YUV422P, PIX_FMT_YUV444P, PIX_FMT_BGRA, PIX_FMT_NONE };

            }

        }

        for (; *p != PIX_FMT_NONE; p++) {

            const char *name = av_get_pix_fmt_name(*p);

            avio_printf(s, "%s:", name);

        }

        len = avio_close_dyn_buf(s, &ret);

        ret[len - 1] = 0;

        return ret;

    } else

        return NULL;

}


static int xiph_handle_packet(AVFormatContext *ctx, PayloadContext *data,

                              AVStream *st, AVPacket *pkt, uint32_t *timestamp,

                              const uint8_t *buf, int len, uint16_t seq,

                              int flags)

{

    int ident, fragmented, tdt, num_pkts, pkt_len;

    if (!buf) {

        if (!data->split_buf || data->split_pos + 2 > data->split_buf_len ||

            data->split_pkts <= 0) {

            av_log(ctx, AV_LOG_ERROR, "No more data to return\n");

            return AVERROR_INVALIDDATA;

        }

        pkt_len = AV_RB16(data->split_buf + data->split_pos);

        data->split_pos += 2;

        if (data->split_pos + pkt_len > data->split_buf_len) {

            av_log(ctx, AV_LOG_ERROR, "Not enough data to return\n");

            return AVERROR_INVALIDDATA;

        }

        if (av_new_packet(pkt, pkt_len)) {

            av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");

            return AVERROR(ENOMEM);

        }

        pkt->stream_index = st->index;

        memcpy(pkt->data, data->split_buf + data->split_pos, pkt_len);

        data->split_pos += pkt_len;

        data->split_pkts--;

        return data->split_pkts > 0;

    }

    if (len < 6 || len > INT_MAX/2) {

        av_log(ctx, AV_LOG_ERROR, "Invalid %d byte packet\n", len);

        return AVERROR_INVALIDDATA;

    }

    // read xiph rtp headers

    ident       = AV_RB24(buf);

    fragmented  = buf[3] >> 6;

    tdt         = (buf[3] >> 4) & 3;

    num_pkts    = buf[3] & 0xf;

    pkt_len     = AV_RB16(buf + 4);

    if (pkt_len > len - 6) {

        av_log(ctx, AV_LOG_ERROR,

               "Invalid packet length %d in %d byte packet\n", pkt_len,

               len);

        return AVERROR_INVALIDDATA;

    }

    if (ident != data->ident) {

        av_log(ctx, AV_LOG_ERROR,

               "Unimplemented Xiph SDP configuration change detected\n");

        return AVERROR_PATCHWELCOME;

    }

    if (tdt) {

        av_log(ctx, AV_LOG_ERROR,

               "Unimplemented RTP Xiph packet settings (%d,%d,%d)\n",

               fragmented, tdt, num_pkts);

        return AVERROR_PATCHWELCOME;

    }

    buf += 6; // move past header bits

    len -= 6;

    if (fragmented == 0) {

        if (av_new_packet(pkt, pkt_len)) {

            av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");

            return AVERROR(ENOMEM);

        }

        pkt->stream_index = st->index;

        memcpy(pkt->data, buf, pkt_len);

        buf += pkt_len;

        len -= pkt_len;

        num_pkts--;

        if (num_pkts > 0) {

            if (len > data->split_buf_size || !data->split_buf) {

                av_freep(&data->split_buf);

                data->split_buf_size = 2 * len;

                data->split_buf = av_malloc(data->split_buf_size);

                if (!data->split_buf) {

                    av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");

                    av_free_packet(pkt);

                    return AVERROR(ENOMEM);

                }

            }

            memcpy(data->split_buf, buf, len);

            data->split_buf_len = len;

            data->split_pos = 0;

            data->split_pkts = num_pkts;

            return 1;

        }

        return 0;

    } else if (fragmented == 1) {

        // start of xiph data fragment

        int res;

        // end packet has been lost somewhere, so drop buffered data

        ffio_free_dyn_buf(&data->fragment);

        if((res = avio_open_dyn_buf(&data->fragment)) < 0)

            return res;

        avio_write(data->fragment, buf, pkt_len);

        data->timestamp = *timestamp;

    } else {

        av_assert1(fragmented < 4);

        if (data->timestamp != *timestamp) {

            // skip if fragmented timestamp is incorrect;

            // a start packet has been lost somewhere

            ffio_free_dyn_buf(&data->fragment);

            av_log(ctx, AV_LOG_ERROR, "RTP timestamps don't match!\n");

            return AVERROR_INVALIDDATA;

        }

        if (!data->fragment) {

            av_log(ctx, AV_LOG_WARNING,

                   "Received packet without a start fragment; dropping.\n");

            return AVERROR(EAGAIN);

        }

        // copy data to fragment buffer

        avio_write(data->fragment, buf, pkt_len);

        if (fragmented == 3) {

            // end of xiph data packet

            int ret = ff_rtp_finalize_packet(pkt, &data->fragment, st->index);

            if (ret < 0) {

                av_log(ctx, AV_LOG_ERROR,

                       "Error occurred when getting fragment buffer.");

                return ret;

            }

            return 0;

        }

    }

   return AVERROR(EAGAIN);

}


static int check_image_pointers(uint8_t *data[4], enum AVPixelFormat pix_fmt,

                                const int linesizes[4])

{

    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);

    int i;

    for (i = 0; i < 4; i++) {

        int plane = desc->comp[i].plane;

        if (!data[plane] || !linesizes[plane])

            return 0;

    }

    return 1;

}


static void vc1_mc_4mv_luma(VC1Context *v, int n, int dir)

{

    MpegEncContext *s = &v->s;

    DSPContext *dsp = &v->s.dsp;

    uint8_t *srcY;

    int dxy, mx, my, src_x, src_y;

    int off;

    int fieldmv = (v->fcm == ILACE_FRAME) ? v->blk_mv_type[s->block_index[n]] : 0;

    int v_edge_pos = s->v_edge_pos >> v->field_mode;

    if (!v->field_mode && !v->s.last_picture.f.data[0])

        return;

    mx = s->mv[dir][n][0];

    my = s->mv[dir][n][1];

    if (!dir) {

        if (v->field_mode) {

            if ((v->cur_field_type != v->ref_field_type[dir]) && v->cur_field_type)

                srcY = s->current_picture.f.data[0];

            else

                srcY = s->last_picture.f.data[0];

        } else

            srcY = s->last_picture.f.data[0];

    } else

        srcY = s->next_picture.f.data[0];

    if (v->field_mode) {

        if (v->cur_field_type != v->ref_field_type[dir])

            my = my - 2 + 4 * v->cur_field_type;

    }

    if (s->pict_type == AV_PICTURE_TYPE_P && n == 3 && v->field_mode) {

        int same_count = 0, opp_count = 0, k;

        int chosen_mv[2][4][2], f;

        int tx, ty;

        for (k = 0; k < 4; k++) {

            f = v->mv_f[0][s->block_index[k] + v->blocks_off];

            chosen_mv[f][f ? opp_count : same_count][0] = s->mv[0][k][0];

            chosen_mv[f][f ? opp_count : same_count][1] = s->mv[0][k][1];

            opp_count  += f;

            same_count += 1 - f;

        }

        f = opp_count > same_count;

        switch (f ? opp_count : same_count) {

        case 4:

            tx = median4(chosen_mv[f][0][0], chosen_mv[f][1][0],

                         chosen_mv[f][2][0], chosen_mv[f][3][0]);

            ty = median4(chosen_mv[f][0][1], chosen_mv[f][1][1],

                         chosen_mv[f][2][1], chosen_mv[f][3][1]);

            break;

        case 3:

            tx = mid_pred(chosen_mv[f][0][0], chosen_mv[f][1][0], chosen_mv[f][2][0]);

            ty = mid_pred(chosen_mv[f][0][1], chosen_mv[f][1][1], chosen_mv[f][2][1]);

            break;

        case 2:

            tx = (chosen_mv[f][0][0] + chosen_mv[f][1][0]) / 2;

            ty = (chosen_mv[f][0][1] + chosen_mv[f][1][1]) / 2;

            break;

        }

        s->current_picture.f.motion_val[1][s->block_index[0] + v->blocks_off][0] = tx;

        s->current_picture.f.motion_val[1][s->block_index[0] + v->blocks_off][1] = ty;

        for (k = 0; k < 4; k++)

            v->mv_f[1][s->block_index[k] + v->blocks_off] = f;

    }

    if (v->fcm == ILACE_FRAME) {  // not sure if needed for other types of picture

        int qx, qy;

        int width  = s->avctx->coded_width;

        int height = s->avctx->coded_height >> 1;

        qx = (s->mb_x * 16) + (mx >> 2);

        qy = (s->mb_y *  8) + (my >> 3);

        if (qx < -17)

            mx -= 4 * (qx + 17);

        else if (qx > width)

            mx -= 4 * (qx - width);

        if (qy < -18)

            my -= 8 * (qy + 18);

        else if (qy > height + 1)

            my -= 8 * (qy - height - 1);

    }

    if ((v->fcm == ILACE_FRAME) && fieldmv)

        off = ((n > 1) ? s->linesize : 0) + (n & 1) * 8;

    else

        off = s->linesize * 4 * (n & 2) + (n & 1) * 8;

    if (v->field_mode && v->cur_field_type)

        off += s->current_picture_ptr->f.linesize[0];

    src_x = s->mb_x * 16 + (n & 1) * 8 + (mx >> 2);

    if (!fieldmv)

        src_y = s->mb_y * 16 + (n & 2) * 4 + (my >> 2);

    else

        src_y = s->mb_y * 16 + ((n > 1) ? 1 : 0) + (my >> 2);

    if (v->profile != PROFILE_ADVANCED) {

        src_x = av_clip(src_x, -16, s->mb_width  * 16);

        src_y = av_clip(src_y, -16, s->mb_height * 16);

    } else {

        src_x = av_clip(src_x, -17, s->avctx->coded_width);

        if (v->fcm == ILACE_FRAME) {

            if (src_y & 1)

                src_y = av_clip(src_y, -17, s->avctx->coded_height + 1);

            else

                src_y = av_clip(src_y, -18, s->avctx->coded_height);

        } else {

            src_y = av_clip(src_y, -18, s->avctx->coded_height + 1);

        }

    }

    srcY += src_y * s->linesize + src_x;

    if (v->field_mode && v->ref_field_type[dir])

        srcY += s->current_picture_ptr->f.linesize[0];

    if (fieldmv && !(src_y & 1))

        v_edge_pos--;

    if (fieldmv && (src_y & 1) && src_y < 4)

        src_y--;

    if (v->rangeredfrm || (v->mv_mode == MV_PMODE_INTENSITY_COMP)

        || s->h_edge_pos < 13 || v_edge_pos < 23

        || (unsigned)(src_x - s->mspel) > s->h_edge_pos - (mx & 3) - 8 - s->mspel * 2

        || (unsigned)(src_y - (s->mspel << fieldmv)) > v_edge_pos - (my & 3) - ((8 + s->mspel * 2) << fieldmv)) {

        srcY -= s->mspel * (1 + (s->linesize << fieldmv));

        /* check emulate edge stride and offset */

        s->dsp.emulated_edge_mc(s->edge_emu_buffer, srcY, s->linesize,

                                9 + s->mspel * 2, (9 + s->mspel * 2) << fieldmv,

                                src_x - s->mspel, src_y - (s->mspel << fieldmv),

                                s->h_edge_pos, v_edge_pos);

        srcY = s->edge_emu_buffer;

        /* if we deal with range reduction we need to scale source blocks */

        if (v->rangeredfrm) {

            int i, j;

            uint8_t *src;

            src = srcY;

            for (j = 0; j < 9 + s->mspel * 2; j++) {

                for (i = 0; i < 9 + s->mspel * 2; i++)

                    src[i] = ((src[i] - 128) >> 1) + 128;

                src += s->linesize << fieldmv;

            }

        }

        /* if we deal with intensity compensation we need to scale source blocks */

        if (v->mv_mode == MV_PMODE_INTENSITY_COMP) {

            int i, j;

            uint8_t *src;

            src = srcY;

            for (j = 0; j < 9 + s->mspel * 2; j++) {

                for (i = 0; i < 9 + s->mspel * 2; i++)

                    src[i] = v->luty[src[i]];

                src += s->linesize << fieldmv;

            }

        }

        srcY += s->mspel * (1 + (s->linesize << fieldmv));

    }

    if (s->mspel) {

        dxy = ((my & 3) << 2) | (mx & 3);

        v->vc1dsp.put_vc1_mspel_pixels_tab[dxy](s->dest[0] + off, srcY, s->linesize << fieldmv, v->rnd);

    } else { // hpel mc - always used for luma

        dxy = (my & 2) | ((mx & 2) >> 1);

        if (!v->rnd)

            dsp->put_pixels_tab[1][dxy](s->dest[0] + off, srcY, s->linesize, 8);

        else

            dsp->put_no_rnd_pixels_tab[1][dxy](s->dest[0] + off, srcY, s->linesize, 8);

    }

}


int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,

                                unsigned int bytes, QEMUIOVector *qiov,

                                BdrvRequestFlags flags)

{

    int ret;

    trace_blk_co_pwritev(blk, blk_bs(blk), offset, bytes, flags);

    ret = blk_check_byte_request(blk, offset, bytes);

    if (ret < 0) {

        return ret;

    }

    /* throttling disk I/O */

    if (blk->public.throttle_state) {

        throttle_group_co_io_limits_intercept(blk, bytes, true);

    }

    if (!blk->enable_write_cache) {

        flags |= BDRV_REQ_FUA;

    }

    return bdrv_co_pwritev(blk_bs(blk), offset, bytes, qiov, flags);

}


void monitor_protocol_event(MonitorEvent event, QObject *data)

{

    QDict *qmp;

    const char *event_name;

    Monitor *mon;

    assert(event < QEVENT_MAX);

    switch (event) {

        case QEVENT_DEBUG:

            event_name = "DEBUG";

            break;

        case QEVENT_SHUTDOWN:

            event_name = "SHUTDOWN";

            break;

        case QEVENT_RESET:

            event_name = "RESET";

            break;

        case QEVENT_POWERDOWN:

            event_name = "POWERDOWN";

            break;

        case QEVENT_STOP:

            event_name = "STOP";

            break;

        case QEVENT_VNC_CONNECTED:

            event_name = "VNC_CONNECTED";

            break;

        case QEVENT_VNC_INITIALIZED:

            event_name = "VNC_INITIALIZED";

            break;

        case QEVENT_VNC_DISCONNECTED:

            event_name = "VNC_DISCONNECTED";

            break;

        default:

            abort();

            break;

    }

    qmp = qdict_new();

    timestamp_put(qmp);

    qdict_put(qmp, "event", qstring_from_str(event_name));

    if (data) {

        qobject_incref(data);

        qdict_put_obj(qmp, "data", data);

    }

    QLIST_FOREACH(mon, &mon_list, entry) {

        if (!monitor_ctrl_mode(mon))

            return;

        monitor_json_emitter(mon, QOBJECT(qmp));

    }

    QDECREF(qmp);

}


static int ppce500_prep_device_tree(MachineState *machine,

                                    PPCE500Params *params,

                                    hwaddr addr,

                                    hwaddr initrd_base,

                                    hwaddr initrd_size)

{

    DeviceTreeParams *p = g_new(DeviceTreeParams, 1);

    p->machine = machine;

    p->params = *params;

    p->addr = addr;

    p->initrd_base = initrd_base;

    p->initrd_size = initrd_size;

    qemu_register_reset(ppce500_reset_device_tree, p);

    /* Issue the device tree loader once, so that we get the size of the blob */

    return ppce500_load_device_tree(machine, params, addr, initrd_base,

                                    initrd_size, true);

}


int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)

{

    BlockDriver *drv = bs->drv;

    if (drv && drv->bdrv_ioctl)

        return drv->bdrv_ioctl(bs, req, buf);

    return -ENOTSUP;

}


void tb_invalidate_phys_addr(target_phys_addr_t addr)

{

    ram_addr_t ram_addr;

    MemoryRegionSection *section;

    section = phys_page_find(addr >> TARGET_PAGE_BITS);

    if (!(memory_region_is_ram(section->mr)

          || (section->mr->rom_device && section->mr->readable))) {

        return;

    }

    ram_addr = (memory_region_get_ram_addr(section->mr) & TARGET_PAGE_MASK)

        + memory_region_section_addr(section, addr);

    tb_invalidate_phys_page_range(ram_addr, ram_addr + 1, 0);

}


static void scsi_generic_realize(SCSIDevice *s, Error **errp)

{

    int rc;

    int sg_version;

    struct sg_scsi_id scsiid;

    if (!s->conf.bs) {

        error_setg(errp, "drive property not set");

        return;

    }

    if (bdrv_get_on_error(s->conf.bs, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {

        error_setg(errp, "Device doesn't support drive option werror");

        return;

    }

    if (bdrv_get_on_error(s->conf.bs, 1) != BLOCKDEV_ON_ERROR_REPORT) {

        error_setg(errp, "Device doesn't support drive option rerror");

        return;

    }

    /* check we are using a driver managing SG_IO (version 3 and after */

    rc = bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version);

    if (rc < 0) {

        error_setg(errp, "cannot get SG_IO version number: %s.  "

                         "Is this a SCSI device?",

                         strerror(-rc));

        return;

    }

    if (sg_version < 30000) {

        error_setg(errp, "scsi generic interface too old");

        return;

    }

    /* get LUN of the /dev/sg? */

    if (bdrv_ioctl(s->conf.bs, SG_GET_SCSI_ID, &scsiid)) {

        error_setg(errp, "SG_GET_SCSI_ID ioctl failed");

        return;

    }

    /* define device state */

    s->type = scsiid.scsi_type;

    DPRINTF("device type %d\n", s->type);

    switch (s->type) {

    case TYPE_TAPE:

        s->blocksize = get_stream_blocksize(s->conf.bs);

        if (s->blocksize == -1) {

            s->blocksize = 0;

        }

        break;

        /* Make a guess for block devices, we'll fix it when the guest sends.

         * READ CAPACITY.  If they don't, they likely would assume these sizes

         * anyway. (TODO: they could also send MODE SENSE).

         */

    case TYPE_ROM:

    case TYPE_WORM:

        s->blocksize = 2048;

        break;

    default:

        s->blocksize = 512;

        break;

    }

    DPRINTF("block size %d\n", s->blocksize);

}


static void pool_release_buffer(void *opaque, uint8_t *data)

{

    BufferPoolEntry *buf = opaque;

    AVBufferPool *pool = buf->pool;

    if(CONFIG_MEMORY_POISONING)

        memset(buf->data, 0x2a, pool->size);

    add_to_pool(buf);

    if (!avpriv_atomic_int_add_and_fetch(&pool->refcount, -1))

        buffer_pool_free(pool);

}


static void dec_load(DisasContext *dc)

{

    TCGv t, *addr;

    unsigned int size;

    size = 1 << (dc->opcode & 3);

    LOG_DIS("l %x %d\n", dc->opcode, size);

    t_sync_flags(dc);

    addr = compute_ldst_addr(dc, &t);

    /* If we get a fault on a dslot, the jmpstate better be in sync.  */

    sync_jmpstate(dc);

    /* Verify alignment if needed.  */

    if ((dc->env->pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) {

        gen_helper_memalign(*addr, tcg_const_tl(dc->rd),

                            tcg_const_tl(0), tcg_const_tl(size));

    }

    if (dc->rd) {

        gen_load(dc, cpu_R[dc->rd], *addr, size);

    } else {

        gen_load(dc, env_imm, *addr, size);

    }

    if (addr == &t)

        tcg_temp_free(t);

}


void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled)

{

    ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);

    qemu_irq sci_irq;

    sci_irq = qemu_allocate_irq(ich9_set_sci, lpc, 0);

    ich9_pm_init(lpc_pci, &lpc->pm, smm_enabled, sci_irq);

    ich9_lpc_reset(&lpc->d.qdev);

}


uint64_t helper_fctiw (uint64_t arg)

{

    CPU_DoubleU farg;

    farg.ll = arg;

    if (unlikely(float64_is_signaling_nan(farg.d))) {

        /* sNaN conversion */

        farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI);

    } else if (unlikely(float64_is_nan(farg.d) || float64_is_infinity(farg.d))) {

        /* qNan / infinity conversion */

        farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI);

    } else {

        farg.ll = float64_to_int32(farg.d, &env->fp_status);

#if USE_PRECISE_EMULATION

        /* XXX: higher bits are not supposed to be significant.

         *     to make tests easier, return the same as a real PowerPC 750

         */

        farg.ll |= 0xFFF80000ULL << 32;

#endif

    }

    return farg.ll;

}


static int ppc_hash64_pte_prot(PowerPCCPU *cpu,

                               ppc_slb_t *slb, ppc_hash_pte64_t pte)

{

    CPUPPCState *env = &cpu->env;

    unsigned pp, key;

    /* Some pp bit combinations have undefined behaviour, so default

     * to no access in those cases */

    int prot = 0;

    key = !!(msr_pr ? (slb->vsid & SLB_VSID_KP)

             : (slb->vsid & SLB_VSID_KS));

    pp = (pte.pte1 & HPTE64_R_PP) | ((pte.pte1 & HPTE64_R_PP0) >> 61);

    if (key == 0) {

        switch (pp) {

        case 0x0:

        case 0x1:

        case 0x2:

            prot = PAGE_READ | PAGE_WRITE;

            break;

        case 0x3:

        case 0x6:

            prot = PAGE_READ;

            break;

        }

    } else {

        switch (pp) {

        case 0x0:

        case 0x6:

            prot = 0;

            break;

        case 0x1:

        case 0x3:

            prot = PAGE_READ;

            break;

        case 0x2:

            prot = PAGE_READ | PAGE_WRITE;

            break;

        }

    }

    /* No execute if either noexec or guarded bits set */

    if (!(pte.pte1 & HPTE64_R_N) || (pte.pte1 & HPTE64_R_G)

        || (slb->vsid & SLB_VSID_N)) {

        prot |= PAGE_EXEC;

    }

    return prot;

}


static void exynos4210_mct_write(void *opaque, target_phys_addr_t offset,

        uint64_t value, unsigned size)

{

    Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;

    int index;  /* index in buffer which represents register set */

    int shift;

    int lt_i;

    uint64_t new_frc;

    uint32_t i;

    uint32_t old_val;

#ifdef DEBUG_MCT

    static uint32_t icntb_max[2] = {0};

    static uint32_t icntb_min[2] = {UINT32_MAX, UINT32_MAX};

    static uint32_t tcntb_max[2] = {0};

    static uint32_t tcntb_min[2] = {UINT32_MAX, UINT32_MAX};

#endif

    new_frc = s->g_timer.reg.cnt;

    switch (offset) {

    case MCT_CFG:

        s->reg_mct_cfg = value;

        exynos4210_mct_update_freq(s);

        break;

    case G_CNT_L:

    case G_CNT_U:

        if (offset == G_CNT_L) {

            DPRINTF("global timer write to reg.cntl %llx\n", value);

            new_frc = (s->g_timer.reg.cnt & (uint64_t)UINT32_MAX << 32) + value;

            s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_L;

        }

        if (offset == G_CNT_U) {

            DPRINTF("global timer write to reg.cntu %llx\n", value);

            new_frc = (s->g_timer.reg.cnt & UINT32_MAX) +

                    ((uint64_t)value << 32);

            s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_U;

        }

        s->g_timer.reg.cnt = new_frc;

        exynos4210_gfrc_restart(s);

        break;

    case G_CNT_WSTAT:

        s->g_timer.reg.cnt_wstat &= ~(value);

        break;

    case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3):

    case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3):

    index = GET_G_COMP_IDX(offset);

    shift = 8 * (offset & 0x4);

    s->g_timer.reg.comp[index] =

            (s->g_timer.reg.comp[index] &

            (((uint64_t)UINT32_MAX << 32) >> shift)) +

            (value << shift);

    DPRINTF("comparator %d write 0x%llx val << %d\n", index, value, shift);

    if (offset&0x4) {

        s->g_timer.reg.wstat |= G_WSTAT_COMP_U(index);

    } else {

        s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index);

    }

    exynos4210_gfrc_restart(s);

    break;

    case G_TCON:

        old_val = s->g_timer.reg.tcon;

        s->g_timer.reg.tcon = value;

        s->g_timer.reg.wstat |= G_WSTAT_TCON_WRITE;

        DPRINTF("global timer write to reg.g_tcon %llx\n", value);

        /* Start FRC if transition from disabled to enabled */

        if ((value & G_TCON_TIMER_ENABLE) > (old_val &

                G_TCON_TIMER_ENABLE)) {

            exynos4210_gfrc_start(&s->g_timer);

        }

        if ((value & G_TCON_TIMER_ENABLE) < (old_val &

                G_TCON_TIMER_ENABLE)) {

            exynos4210_gfrc_stop(&s->g_timer);

        }

        /* Start CMP if transition from disabled to enabled */

        for (i = 0; i < MCT_GT_CMP_NUM; i++) {

            if ((value & G_TCON_COMP_ENABLE(i)) != (old_val &

                    G_TCON_COMP_ENABLE(i))) {

                exynos4210_gfrc_restart(s);

            }

        }

        break;

    case G_INT_CSTAT:

        s->g_timer.reg.int_cstat &= ~(value);

        for (i = 0; i < MCT_GT_CMP_NUM; i++) {

            if (value & G_INT_CSTAT_COMP(i)) {

                exynos4210_gcomp_lower_irq(&s->g_timer, i);

            }

        }

        break;

    case G_INT_ENB:

        /* Raise IRQ if transition from disabled to enabled and CSTAT pending */

        for (i = 0; i < MCT_GT_CMP_NUM; i++) {

            if ((value & G_INT_ENABLE(i)) > (s->g_timer.reg.tcon &

                    G_INT_ENABLE(i))) {

                if (s->g_timer.reg.int_cstat & G_INT_CSTAT_COMP(i)) {

                    exynos4210_gcomp_raise_irq(&s->g_timer, i);

                }

            }

            if ((value & G_INT_ENABLE(i)) < (s->g_timer.reg.tcon &

                    G_INT_ENABLE(i))) {

                exynos4210_gcomp_lower_irq(&s->g_timer, i);

            }

        }

        DPRINTF("global timer INT enable %llx\n", value);

        s->g_timer.reg.int_enb = value;

        break;

    case G_WSTAT:

        s->g_timer.reg.wstat &= ~(value);

        break;

    case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR:

    case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR:

        index = GET_G_COMP_ADD_INCR_IDX(offset);

        s->g_timer.reg.comp_add_incr[index] = value;

        s->g_timer.reg.wstat |= G_WSTAT_COMP_ADDINCR(index);

        break;

        /* Local timers */

    case L0_TCON: case L1_TCON:

        lt_i = GET_L_TIMER_IDX(offset);

        old_val = s->l_timer[lt_i].reg.tcon;

        s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCON_WRITE;

        s->l_timer[lt_i].reg.tcon = value;

        /* Stop local CNT */

        if ((value & L_TCON_TICK_START) <

                (old_val & L_TCON_TICK_START)) {

            DPRINTF("local timer[%d] stop cnt\n", lt_i);

            exynos4210_ltick_cnt_stop(&s->l_timer[lt_i].tick_timer);

        }

        /* Stop local INT */

        if ((value & L_TCON_INT_START) <

                (old_val & L_TCON_INT_START)) {

            DPRINTF("local timer[%d] stop int\n", lt_i);

            exynos4210_ltick_int_stop(&s->l_timer[lt_i].tick_timer);

        }

        /* Start local CNT */

        if ((value & L_TCON_TICK_START) >

        (old_val & L_TCON_TICK_START)) {

            DPRINTF("local timer[%d] start cnt\n", lt_i);

            exynos4210_ltick_cnt_start(&s->l_timer[lt_i].tick_timer);

        }

        /* Start local INT */

        if ((value & L_TCON_INT_START) >

        (old_val & L_TCON_INT_START)) {

            DPRINTF("local timer[%d] start int\n", lt_i);

            exynos4210_ltick_int_start(&s->l_timer[lt_i].tick_timer);

        }

        /* Start or Stop local FRC if TCON changed */

        if ((value & L_TCON_FRC_START) >

        (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) {

            DPRINTF("local timer[%d] start frc\n", lt_i);

            exynos4210_lfrc_start(&s->l_timer[lt_i]);

        }

        if ((value & L_TCON_FRC_START) <

                (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) {

            DPRINTF("local timer[%d] stop frc\n", lt_i);

            exynos4210_lfrc_stop(&s->l_timer[lt_i]);

        }

        break;

    case L0_TCNTB: case L1_TCNTB:

        lt_i = GET_L_TIMER_IDX(offset);

        index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);

        /*

         * TCNTB is updated to internal register only after CNT expired.

         * Due to this we should reload timer to nearest moment when CNT is

         * expired and then in event handler update tcntb to new TCNTB value.

         */

        exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, value,

                s->l_timer[lt_i].tick_timer.icntb);

        s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCNTB_WRITE;

        s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] = value;

#ifdef DEBUG_MCT

        if (tcntb_min[lt_i] > value) {

            tcntb_min[lt_i] = value;

        }

        if (tcntb_max[lt_i] < value) {

            tcntb_max[lt_i] = value;

        }

        DPRINTF("local timer[%d] TCNTB write %llx; max=%x, min=%x\n",

                lt_i, value, tcntb_max[lt_i], tcntb_min[lt_i]);

#endif

        break;

    case L0_ICNTB: case L1_ICNTB:

        lt_i = GET_L_TIMER_IDX(offset);

        index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);

        s->l_timer[lt_i].reg.wstat |= L_WSTAT_ICNTB_WRITE;

        s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = value &

                ~L_ICNTB_MANUAL_UPDATE;

        /*

         * We need to avoid too small values for TCNTB*ICNTB. If not, IRQ event

         * could raise too fast disallowing QEMU to execute target code.

         */

        if (s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] *

            s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] < MCT_LT_CNT_LOW_LIMIT) {

            if (!s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]) {

                s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] =

                        MCT_LT_CNT_LOW_LIMIT;

            } else {

                s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] =

                        MCT_LT_CNT_LOW_LIMIT /

                        s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB];

            }

        }

        if (value & L_ICNTB_MANUAL_UPDATE) {

            exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer,

                    s->l_timer[lt_i].tick_timer.tcntb,

                    s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB]);

        }

#ifdef DEBUG_MCT

        if (icntb_min[lt_i] > value) {

            icntb_min[lt_i] = value;

        }

        if (icntb_max[lt_i] < value) {

            icntb_max[lt_i] = value;

        }

DPRINTF("local timer[%d] ICNTB write %llx; max=%x, min=%x\n\n",

        lt_i, value, icntb_max[lt_i], icntb_min[lt_i]);

#endif

break;

    case L0_FRCNTB: case L1_FRCNTB:

        lt_i = GET_L_TIMER_IDX(offset);

        index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);

        DPRINTF("local timer[%d] FRCNTB write %llx\n", lt_i, value);

        s->l_timer[lt_i].reg.wstat |= L_WSTAT_FRCCNTB_WRITE;

        s->l_timer[lt_i].reg.cnt[L_REG_CNT_FRCCNTB] = value;

        break;

    case L0_TCNTO: case L1_TCNTO:

    case L0_ICNTO: case L1_ICNTO:

    case L0_FRCNTO: case L1_FRCNTO:

        fprintf(stderr, "\n[exynos4210.mct: write to RO register "

                TARGET_FMT_plx "]\n\n", offset);

        break;

    case L0_INT_CSTAT: case L1_INT_CSTAT:

        lt_i = GET_L_TIMER_IDX(offset);

        DPRINTF("local timer[%d] CSTAT write %llx\n", lt_i, value);

        s->l_timer[lt_i].reg.int_cstat &= ~value;

        if (!s->l_timer[lt_i].reg.int_cstat) {

            qemu_irq_lower(s->l_timer[lt_i].irq);

        }

        break;

    case L0_INT_ENB: case L1_INT_ENB:

        lt_i = GET_L_TIMER_IDX(offset);

        old_val = s->l_timer[lt_i].reg.int_enb;

        /* Raise Local timer IRQ if cstat is pending */

        if ((value & L_INT_INTENB_ICNTEIE) > (old_val & L_INT_INTENB_ICNTEIE)) {

            if (s->l_timer[lt_i].reg.int_cstat & L_INT_CSTAT_INTCNT) {

                qemu_irq_raise(s->l_timer[lt_i].irq);

            }

        }

        s->l_timer[lt_i].reg.int_enb = value;

        break;

    case L0_WSTAT: case L1_WSTAT:

        lt_i = GET_L_TIMER_IDX(offset);

        s->l_timer[lt_i].reg.wstat &= ~value;

        break;

    default:

        hw_error("exynos4210.mct: bad write offset "

                TARGET_FMT_plx "\n", offset);

        break;

    }

}


static int no_init_in (HWVoiceIn *hw, audsettings_t *as)

{

    audio_pcm_init_info (&hw->info, as);

    hw->samples = 1024;

    return 0;

}


uint32_t HELPER(stfle)(CPUS390XState *env, uint64_t addr)

{

    uint64_t words[MAX_STFL_WORDS];

    unsigned count_m1 = env->regs[0] & 0xff;

    unsigned max_m1 = do_stfle(env, words);

    unsigned i;

    for (i = 0; i <= count_m1; ++i) {

        cpu_stq_data(env, addr + 8 * i, words[i]);

    }

    env->regs[0] = deposit64(env->regs[0], 0, 8, max_m1);

    return (count_m1 >= max_m1 ? 0 : 3);

}


static void pxa2xx_fir_write(void *opaque, hwaddr addr,

                             uint64_t value64, unsigned size)

{

    PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;

    uint32_t value = value64;

    uint8_t ch;

    switch (addr) {

    case ICCR0:

        s->control[0] = value;

        if (!(value & (1 << 4)))			/* RXE */

            s->rx_len = s->rx_start = 0;

        if (!(value & (1 << 3))) {                      /* TXE */

            /* Nop */

        }

        s->enable = value & 1;				/* ITR */

        if (!s->enable)

            s->status[0] = 0;

        pxa2xx_fir_update(s);

        break;

    case ICCR1:

        s->control[1] = value;

        break;

    case ICCR2:

        s->control[2] = value & 0x3f;

        pxa2xx_fir_update(s);

        break;

    case ICDR:

        if (s->control[2] & (1 << 2)) { /* TXP */

            ch = value;

        } else {

            ch = ~value;

        }

        if (s->enable && (s->control[0] & (1 << 3))) { /* TXE */

            /* XXX this blocks entire thread. Rewrite to use

             * qemu_chr_fe_write and background I/O callbacks */

            qemu_chr_fe_write_all(&s->chr, &ch, 1);

        }

        break;

    case ICSR0:

        s->status[0] &= ~(value & 0x66);

        pxa2xx_fir_update(s);

        break;

    case ICFOR:

        break;

    default:

        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);

    }

}


static void disas_thumb_insn(CPUARMState *env, DisasContext *s)

{

    uint32_t val, insn, op, rm, rn, rd, shift, cond;

    int32_t offset;

    int i;

    TCGv_i32 tmp;

    TCGv_i32 tmp2;

    TCGv_i32 addr;

    if (s->condexec_mask) {

        cond = s->condexec_cond;

        if (cond != 0x0e) {     /* Skip conditional when condition is AL. */

          s->condlabel = gen_new_label();

          gen_test_cc(cond ^ 1, s->condlabel);

          s->condjmp = 1;

        }

    }

    insn = arm_lduw_code(env, s->pc, s->bswap_code);

    s->pc += 2;

    switch (insn >> 12) {

    case 0: case 1:

        rd = insn & 7;

        op = (insn >> 11) & 3;

        if (op == 3) {

            /* add/subtract */

            rn = (insn >> 3) & 7;

            tmp = load_reg(s, rn);

            if (insn & (1 << 10)) {

                /* immediate */

                tmp2 = tcg_temp_new_i32();

                tcg_gen_movi_i32(tmp2, (insn >> 6) & 7);

            } else {

                /* reg */

                rm = (insn >> 6) & 7;

                tmp2 = load_reg(s, rm);

            }

            if (insn & (1 << 9)) {

                if (s->condexec_mask)

                    tcg_gen_sub_i32(tmp, tmp, tmp2);

                else

                    gen_sub_CC(tmp, tmp, tmp2);

            } else {

                if (s->condexec_mask)

                    tcg_gen_add_i32(tmp, tmp, tmp2);

                else

                    gen_add_CC(tmp, tmp, tmp2);

            }

            tcg_temp_free_i32(tmp2);

            store_reg(s, rd, tmp);

        } else {

            /* shift immediate */

            rm = (insn >> 3) & 7;

            shift = (insn >> 6) & 0x1f;

            tmp = load_reg(s, rm);

            gen_arm_shift_im(tmp, op, shift, s->condexec_mask == 0);

            if (!s->condexec_mask)

                gen_logic_CC(tmp);

            store_reg(s, rd, tmp);

        }

        break;

    case 2: case 3:

        /* arithmetic large immediate */

        op = (insn >> 11) & 3;

        rd = (insn >> 8) & 0x7;

        if (op == 0) { /* mov */

            tmp = tcg_temp_new_i32();

            tcg_gen_movi_i32(tmp, insn & 0xff);

            if (!s->condexec_mask)

                gen_logic_CC(tmp);

            store_reg(s, rd, tmp);

        } else {

            tmp = load_reg(s, rd);

            tmp2 = tcg_temp_new_i32();

            tcg_gen_movi_i32(tmp2, insn & 0xff);

            switch (op) {

            case 1: /* cmp */

                gen_sub_CC(tmp, tmp, tmp2);

                tcg_temp_free_i32(tmp);

                tcg_temp_free_i32(tmp2);

                break;

            case 2: /* add */

                if (s->condexec_mask)

                    tcg_gen_add_i32(tmp, tmp, tmp2);

                else

                    gen_add_CC(tmp, tmp, tmp2);

                tcg_temp_free_i32(tmp2);

                store_reg(s, rd, tmp);

                break;

            case 3: /* sub */

                if (s->condexec_mask)

                    tcg_gen_sub_i32(tmp, tmp, tmp2);

                else

                    gen_sub_CC(tmp, tmp, tmp2);

                tcg_temp_free_i32(tmp2);

                store_reg(s, rd, tmp);

                break;

            }

        }

        break;

    case 4:

        if (insn & (1 << 11)) {

            rd = (insn >> 8) & 7;

            /* load pc-relative.  Bit 1 of PC is ignored.  */

            val = s->pc + 2 + ((insn & 0xff) * 4);

            val &= ~(uint32_t)2;

            addr = tcg_temp_new_i32();

            tcg_gen_movi_i32(addr, val);

            tmp = tcg_temp_new_i32();

            gen_aa32_ld32u(tmp, addr, IS_USER(s));

            tcg_temp_free_i32(addr);

            store_reg(s, rd, tmp);

            break;

        }

        if (insn & (1 << 10)) {

            /* data processing extended or blx */

            rd = (insn & 7) | ((insn >> 4) & 8);

            rm = (insn >> 3) & 0xf;

            op = (insn >> 8) & 3;

            switch (op) {

            case 0: /* add */

                tmp = load_reg(s, rd);

                tmp2 = load_reg(s, rm);

                tcg_gen_add_i32(tmp, tmp, tmp2);

                tcg_temp_free_i32(tmp2);

                store_reg(s, rd, tmp);

                break;

            case 1: /* cmp */

                tmp = load_reg(s, rd);

                tmp2 = load_reg(s, rm);

                gen_sub_CC(tmp, tmp, tmp2);

                tcg_temp_free_i32(tmp2);

                tcg_temp_free_i32(tmp);

                break;

            case 2: /* mov/cpy */

                tmp = load_reg(s, rm);

                store_reg(s, rd, tmp);

                break;

            case 3:/* branch [and link] exchange thumb register */

                tmp = load_reg(s, rm);

                if (insn & (1 << 7)) {

                    ARCH(5);

                    val = (uint32_t)s->pc | 1;

                    tmp2 = tcg_temp_new_i32();

                    tcg_gen_movi_i32(tmp2, val);

                    store_reg(s, 14, tmp2);

                }

                /* already thumb, no need to check */

                gen_bx(s, tmp);

                break;

            }

            break;

        }

        /* data processing register */

        rd = insn & 7;

        rm = (insn >> 3) & 7;

        op = (insn >> 6) & 0xf;

        if (op == 2 || op == 3 || op == 4 || op == 7) {

            /* the shift/rotate ops want the operands backwards */

            val = rm;

            rm = rd;

            rd = val;

            val = 1;

        } else {

            val = 0;

        }

        if (op == 9) { /* neg */

            tmp = tcg_temp_new_i32();

            tcg_gen_movi_i32(tmp, 0);

        } else if (op != 0xf) { /* mvn doesn't read its first operand */

            tmp = load_reg(s, rd);

        } else {

            TCGV_UNUSED_I32(tmp);

        }

        tmp2 = load_reg(s, rm);

        switch (op) {

        case 0x0: /* and */

            tcg_gen_and_i32(tmp, tmp, tmp2);

            if (!s->condexec_mask)

                gen_logic_CC(tmp);

            break;

        case 0x1: /* eor */

            tcg_gen_xor_i32(tmp, tmp, tmp2);

            if (!s->condexec_mask)

                gen_logic_CC(tmp);

            break;

        case 0x2: /* lsl */

            if (s->condexec_mask) {

                gen_shl(tmp2, tmp2, tmp);

            } else {

                gen_helper_shl_cc(tmp2, cpu_env, tmp2, tmp);

                gen_logic_CC(tmp2);

            }

            break;

        case 0x3: /* lsr */

            if (s->condexec_mask) {

                gen_shr(tmp2, tmp2, tmp);

            } else {

                gen_helper_shr_cc(tmp2, cpu_env, tmp2, tmp);

                gen_logic_CC(tmp2);

            }

            break;

        case 0x4: /* asr */

            if (s->condexec_mask) {

                gen_sar(tmp2, tmp2, tmp);

            } else {

                gen_helper_sar_cc(tmp2, cpu_env, tmp2, tmp);

                gen_logic_CC(tmp2);

            }

            break;

        case 0x5: /* adc */

            if (s->condexec_mask) {

                gen_adc(tmp, tmp2);

            } else {

                gen_adc_CC(tmp, tmp, tmp2);

            }

            break;

        case 0x6: /* sbc */

            if (s->condexec_mask) {

                gen_sub_carry(tmp, tmp, tmp2);

            } else {

                gen_sbc_CC(tmp, tmp, tmp2);

            }

            break;

        case 0x7: /* ror */

            if (s->condexec_mask) {

                tcg_gen_andi_i32(tmp, tmp, 0x1f);

                tcg_gen_rotr_i32(tmp2, tmp2, tmp);

            } else {

                gen_helper_ror_cc(tmp2, cpu_env, tmp2, tmp);

                gen_logic_CC(tmp2);

            }

            break;

        case 0x8: /* tst */

            tcg_gen_and_i32(tmp, tmp, tmp2);

            gen_logic_CC(tmp);

            rd = 16;

            break;

        case 0x9: /* neg */

            if (s->condexec_mask)

                tcg_gen_neg_i32(tmp, tmp2);

            else

                gen_sub_CC(tmp, tmp, tmp2);

            break;

        case 0xa: /* cmp */

            gen_sub_CC(tmp, tmp, tmp2);

            rd = 16;

            break;

        case 0xb: /* cmn */

            gen_add_CC(tmp, tmp, tmp2);

            rd = 16;

            break;

        case 0xc: /* orr */

            tcg_gen_or_i32(tmp, tmp, tmp2);

            if (!s->condexec_mask)

                gen_logic_CC(tmp);

            break;

        case 0xd: /* mul */

            tcg_gen_mul_i32(tmp, tmp, tmp2);

            if (!s->condexec_mask)

                gen_logic_CC(tmp);

            break;

        case 0xe: /* bic */

            tcg_gen_andc_i32(tmp, tmp, tmp2);

            if (!s->condexec_mask)

                gen_logic_CC(tmp);

            break;

        case 0xf: /* mvn */

            tcg_gen_not_i32(tmp2, tmp2);

            if (!s->condexec_mask)

                gen_logic_CC(tmp2);

            val = 1;

            rm = rd;

            break;

        }

        if (rd != 16) {

            if (val) {

                store_reg(s, rm, tmp2);

                if (op != 0xf)

                    tcg_temp_free_i32(tmp);

            } else {

                store_reg(s, rd, tmp);

                tcg_temp_free_i32(tmp2);

            }

        } else {

            tcg_temp_free_i32(tmp);

            tcg_temp_free_i32(tmp2);

        }

        break;

    case 5:

        /* load/store register offset.  */

        rd = insn & 7;

        rn = (insn >> 3) & 7;

        rm = (insn >> 6) & 7;

        op = (insn >> 9) & 7;

        addr = load_reg(s, rn);

        tmp = load_reg(s, rm);

        tcg_gen_add_i32(addr, addr, tmp);

        tcg_temp_free_i32(tmp);

        if (op < 3) { /* store */

            tmp = load_reg(s, rd);

        } else {

            tmp = tcg_temp_new_i32();

        }

        switch (op) {

        case 0: /* str */

            gen_aa32_st32(tmp, addr, IS_USER(s));

            break;

        case 1: /* strh */

            gen_aa32_st16(tmp, addr, IS_USER(s));

            break;

        case 2: /* strb */

            gen_aa32_st8(tmp, addr, IS_USER(s));

            break;

        case 3: /* ldrsb */

            gen_aa32_ld8s(tmp, addr, IS_USER(s));

            break;

        case 4: /* ldr */

            gen_aa32_ld32u(tmp, addr, IS_USER(s));

            break;

        case 5: /* ldrh */

            gen_aa32_ld16u(tmp, addr, IS_USER(s));

            break;

        case 6: /* ldrb */

            gen_aa32_ld8u(tmp, addr, IS_USER(s));

            break;

        case 7: /* ldrsh */

            gen_aa32_ld16s(tmp, addr, IS_USER(s));

            break;

        }

        if (op >= 3) { /* load */

            store_reg(s, rd, tmp);

        } else {

            tcg_temp_free_i32(tmp);

        }

        tcg_temp_free_i32(addr);

        break;

    case 6:

        /* load/store word immediate offset */

        rd = insn & 7;

        rn = (insn >> 3) & 7;

        addr = load_reg(s, rn);

        val = (insn >> 4) & 0x7c;

        tcg_gen_addi_i32(addr, addr, val);

        if (insn & (1 << 11)) {

            /* load */

            tmp = tcg_temp_new_i32();

            gen_aa32_ld32u(tmp, addr, IS_USER(s));

            store_reg(s, rd, tmp);

        } else {

            /* store */

            tmp = load_reg(s, rd);

            gen_aa32_st32(tmp, addr, IS_USER(s));

            tcg_temp_free_i32(tmp);

        }

        tcg_temp_free_i32(addr);

        break;

    case 7:

        /* load/store byte immediate offset */

        rd = insn & 7;

        rn = (insn >> 3) & 7;

        addr = load_reg(s, rn);

        val = (insn >> 6) & 0x1f;

        tcg_gen_addi_i32(addr, addr, val);

        if (insn & (1 << 11)) {

            /* load */

            tmp = tcg_temp_new_i32();

            gen_aa32_ld8u(tmp, addr, IS_USER(s));

            store_reg(s, rd, tmp);

        } else {

            /* store */

            tmp = load_reg(s, rd);

            gen_aa32_st8(tmp, addr, IS_USER(s));

            tcg_temp_free_i32(tmp);

        }

        tcg_temp_free_i32(addr);

        break;

    case 8:

        /* load/store halfword immediate offset */

        rd = insn & 7;

        rn = (insn >> 3) & 7;

        addr = load_reg(s, rn);

        val = (insn >> 5) & 0x3e;

        tcg_gen_addi_i32(addr, addr, val);

        if (insn & (1 << 11)) {

            /* load */

            tmp = tcg_temp_new_i32();

            gen_aa32_ld16u(tmp, addr, IS_USER(s));

            store_reg(s, rd, tmp);

        } else {

            /* store */

            tmp = load_reg(s, rd);

            gen_aa32_st16(tmp, addr, IS_USER(s));

            tcg_temp_free_i32(tmp);

        }

        tcg_temp_free_i32(addr);

        break;

    case 9:

        /* load/store from stack */

        rd = (insn >> 8) & 7;

        addr = load_reg(s, 13);

        val = (insn & 0xff) * 4;

        tcg_gen_addi_i32(addr, addr, val);

        if (insn & (1 << 11)) {

            /* load */

            tmp = tcg_temp_new_i32();

            gen_aa32_ld32u(tmp, addr, IS_USER(s));

            store_reg(s, rd, tmp);

        } else {

            /* store */

            tmp = load_reg(s, rd);

            gen_aa32_st32(tmp, addr, IS_USER(s));

            tcg_temp_free_i32(tmp);

        }

        tcg_temp_free_i32(addr);

        break;

    case 10:

        /* add to high reg */

        rd = (insn >> 8) & 7;

        if (insn & (1 << 11)) {

            /* SP */

            tmp = load_reg(s, 13);

        } else {

            /* PC. bit 1 is ignored.  */

            tmp = tcg_temp_new_i32();

            tcg_gen_movi_i32(tmp, (s->pc + 2) & ~(uint32_t)2);

        }

        val = (insn & 0xff) * 4;

        tcg_gen_addi_i32(tmp, tmp, val);

        store_reg(s, rd, tmp);

        break;

    case 11:

        /* misc */

        op = (insn >> 8) & 0xf;

        switch (op) {

        case 0:

            /* adjust stack pointer */

            tmp = load_reg(s, 13);

            val = (insn & 0x7f) * 4;

            if (insn & (1 << 7))

                val = -(int32_t)val;

            tcg_gen_addi_i32(tmp, tmp, val);

            store_reg(s, 13, tmp);

            break;

        case 2: /* sign/zero extend.  */

            ARCH(6);

            rd = insn & 7;

            rm = (insn >> 3) & 7;

            tmp = load_reg(s, rm);

            switch ((insn >> 6) & 3) {

            case 0: gen_sxth(tmp); break;

            case 1: gen_sxtb(tmp); break;

            case 2: gen_uxth(tmp); break;

            case 3: gen_uxtb(tmp); break;

            }

            store_reg(s, rd, tmp);

            break;

        case 4: case 5: case 0xc: case 0xd:

            /* push/pop */

            addr = load_reg(s, 13);

            if (insn & (1 << 8))

                offset = 4;

            else

                offset = 0;

            for (i = 0; i < 8; i++) {

                if (insn & (1 << i))

                    offset += 4;

            }

            if ((insn & (1 << 11)) == 0) {

                tcg_gen_addi_i32(addr, addr, -offset);

            }

            for (i = 0; i < 8; i++) {

                if (insn & (1 << i)) {

                    if (insn & (1 << 11)) {

                        /* pop */

                        tmp = tcg_temp_new_i32();

                        gen_aa32_ld32u(tmp, addr, IS_USER(s));

                        store_reg(s, i, tmp);

                    } else {

                        /* push */

                        tmp = load_reg(s, i);

                        gen_aa32_st32(tmp, addr, IS_USER(s));

                        tcg_temp_free_i32(tmp);

                    }

                    /* advance to the next address.  */

                    tcg_gen_addi_i32(addr, addr, 4);

                }

            }

            TCGV_UNUSED_I32(tmp);

            if (insn & (1 << 8)) {

                if (insn & (1 << 11)) {

                    /* pop pc */

                    tmp = tcg_temp_new_i32();

                    gen_aa32_ld32u(tmp, addr, IS_USER(s));

                    /* don't set the pc until the rest of the instruction

                       has completed */

                } else {

                    /* push lr */

                    tmp = load_reg(s, 14);

                    gen_aa32_st32(tmp, addr, IS_USER(s));

                    tcg_temp_free_i32(tmp);

                }

                tcg_gen_addi_i32(addr, addr, 4);

            }

            if ((insn & (1 << 11)) == 0) {

                tcg_gen_addi_i32(addr, addr, -offset);

            }

            /* write back the new stack pointer */

            store_reg(s, 13, addr);

            /* set the new PC value */

            if ((insn & 0x0900) == 0x0900) {

                store_reg_from_load(env, s, 15, tmp);

            }

            break;

        case 1: case 3: case 9: case 11: /* czb */

            rm = insn & 7;

            tmp = load_reg(s, rm);

            s->condlabel = gen_new_label();

            s->condjmp = 1;

            if (insn & (1 << 11))

                tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, s->condlabel);

            else

                tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, s->condlabel);

            tcg_temp_free_i32(tmp);

            offset = ((insn & 0xf8) >> 2) | (insn & 0x200) >> 3;

            val = (uint32_t)s->pc + 2;

            val += offset;

            gen_jmp(s, val);

            break;

        case 15: /* IT, nop-hint.  */

            if ((insn & 0xf) == 0) {

                gen_nop_hint(s, (insn >> 4) & 0xf);

                break;

            }

            /* If Then.  */

            s->condexec_cond = (insn >> 4) & 0xe;

            s->condexec_mask = insn & 0x1f;

            /* No actual code generated for this insn, just setup state.  */

            break;

        case 0xe: /* bkpt */

            ARCH(5);

            gen_exception_insn(s, 2, EXCP_BKPT);

            break;

        case 0xa: /* rev */

            ARCH(6);

            rn = (insn >> 3) & 0x7;

            rd = insn & 0x7;

            tmp = load_reg(s, rn);

            switch ((insn >> 6) & 3) {

            case 0: tcg_gen_bswap32_i32(tmp, tmp); break;

            case 1: gen_rev16(tmp); break;

            case 3: gen_revsh(tmp); break;

            default: goto illegal_op;

            }

            store_reg(s, rd, tmp);

            break;

        case 6:

            switch ((insn >> 5) & 7) {

            case 2:

                /* setend */

                ARCH(6);

                if (((insn >> 3) & 1) != s->bswap_code) {

                    /* Dynamic endianness switching not implemented. */

                    qemu_log_mask(LOG_UNIMP, "arm: unimplemented setend\n");

                    goto illegal_op;

                }

                break;

            case 3:

                /* cps */

                ARCH(6);

                if (IS_USER(s)) {

                    break;

                }

                if (IS_M(env)) {

                    tmp = tcg_const_i32((insn & (1 << 4)) != 0);

                    /* FAULTMASK */

                    if (insn & 1) {

                        addr = tcg_const_i32(19);

                        gen_helper_v7m_msr(cpu_env, addr, tmp);

                        tcg_temp_free_i32(addr);

                    }

                    /* PRIMASK */

                    if (insn & 2) {

                        addr = tcg_const_i32(16);

                        gen_helper_v7m_msr(cpu_env, addr, tmp);

                        tcg_temp_free_i32(addr);

                    }

                    tcg_temp_free_i32(tmp);

                    gen_lookup_tb(s);

                } else {

                    if (insn & (1 << 4)) {

                        shift = CPSR_A | CPSR_I | CPSR_F;

                    } else {

                        shift = 0;

                    }

                    gen_set_psr_im(s, ((insn & 7) << 6), 0, shift);

                }

                break;

            default:

                goto undef;

            }

            break;

        default:

            goto undef;

        }

        break;

    case 12:

    {

        /* load/store multiple */

        TCGv_i32 loaded_var;

        TCGV_UNUSED_I32(loaded_var);

        rn = (insn >> 8) & 0x7;

        addr = load_reg(s, rn);

        for (i = 0; i < 8; i++) {

            if (insn & (1 << i)) {

                if (insn & (1 << 11)) {

                    /* load */

                    tmp = tcg_temp_new_i32();

                    gen_aa32_ld32u(tmp, addr, IS_USER(s));

                    if (i == rn) {

                        loaded_var = tmp;

                    } else {

                        store_reg(s, i, tmp);

                    }

                } else {

                    /* store */

                    tmp = load_reg(s, i);

                    gen_aa32_st32(tmp, addr, IS_USER(s));

                    tcg_temp_free_i32(tmp);

                }

                /* advance to the next address */

                tcg_gen_addi_i32(addr, addr, 4);

            }

        }

        if ((insn & (1 << rn)) == 0) {

            /* base reg not in list: base register writeback */

            store_reg(s, rn, addr);

        } else {

            /* base reg in list: if load, complete it now */

            if (insn & (1 << 11)) {

                store_reg(s, rn, loaded_var);

            }

            tcg_temp_free_i32(addr);

        }

        break;

    }

    case 13:

        /* conditional branch or swi */

        cond = (insn >> 8) & 0xf;

        if (cond == 0xe)

            goto undef;

        if (cond == 0xf) {

            /* swi */

            gen_set_pc_im(s, s->pc);

            s->is_jmp = DISAS_SWI;

            break;

        }

        /* generate a conditional jump to next instruction */

        s->condlabel = gen_new_label();

        gen_test_cc(cond ^ 1, s->condlabel);

        s->condjmp = 1;

        /* jump to the offset */

        val = (uint32_t)s->pc + 2;

        offset = ((int32_t)insn << 24) >> 24;

        val += offset << 1;

        gen_jmp(s, val);

        break;

    case 14:

        if (insn & (1 << 11)) {

            if (disas_thumb2_insn(env, s, insn))

              goto undef32;

            break;

        }

        /* unconditional branch */

        val = (uint32_t)s->pc;

        offset = ((int32_t)insn << 21) >> 21;

        val += (offset << 1) + 2;

        gen_jmp(s, val);

        break;

    case 15:

        if (disas_thumb2_insn(env, s, insn))

            goto undef32;

        break;

    }

    return;

undef32:

    gen_exception_insn(s, 4, EXCP_UDEF);

    return;

illegal_op:

undef:

    gen_exception_insn(s, 2, EXCP_UDEF);

}