#include <assert.h>
#include "ibm.h"
#include "device.h"
#include "mem.h"
#include "thread.h"
#include "video.h"
#include "vid_svga.h"
#include "vid_voodoo.h"
#include "vid_voodoo_common.h"
#include "vid_voodoo_display.h"
#include "vid_voodoo_regs.h"
#include "vid_voodoo_render.h"

void voodoo_update_ncc(voodoo_t *voodoo, int tmu) {
        int tbl;

        for (tbl = 0; tbl < 2; tbl++) {
                int col;

                for (col = 0; col < 256; col++) {
                        int y = (col >> 4), i = (col >> 2) & 3, q = col & 3;
                        int i_r, i_g, i_b;
                        int q_r, q_g, q_b;

                        y = (voodoo->nccTable[tmu][tbl].y[y >> 2] >> ((y & 3) * 8)) & 0xff;

                        i_r = (voodoo->nccTable[tmu][tbl].i[i] >> 18) & 0x1ff;
                        if (i_r & 0x100)
                                i_r |= 0xfffffe00;
                        i_g = (voodoo->nccTable[tmu][tbl].i[i] >> 9) & 0x1ff;
                        if (i_g & 0x100)
                                i_g |= 0xfffffe00;
                        i_b = voodoo->nccTable[tmu][tbl].i[i] & 0x1ff;
                        if (i_b & 0x100)
                                i_b |= 0xfffffe00;

                        q_r = (voodoo->nccTable[tmu][tbl].q[q] >> 18) & 0x1ff;
                        if (q_r & 0x100)
                                q_r |= 0xfffffe00;
                        q_g = (voodoo->nccTable[tmu][tbl].q[q] >> 9) & 0x1ff;
                        if (q_g & 0x100)
                                q_g |= 0xfffffe00;
                        q_b = voodoo->nccTable[tmu][tbl].q[q] & 0x1ff;
                        if (q_b & 0x100)
                                q_b |= 0xfffffe00;

                        voodoo->ncc_lookup[tmu][tbl][col].rgba.r = CLAMP(y + i_r + q_r);
                        voodoo->ncc_lookup[tmu][tbl][col].rgba.g = CLAMP(y + i_g + q_g);
                        voodoo->ncc_lookup[tmu][tbl][col].rgba.b = CLAMP(y + i_b + q_b);
                        voodoo->ncc_lookup[tmu][tbl][col].rgba.a = 0xff;
                }
        }
}

void voodoo_pixelclock_update(voodoo_t *voodoo) {
        int m = (voodoo->dac_pll_regs[0] & 0x7f) + 2;
        int n1 = ((voodoo->dac_pll_regs[0] >> 8) & 0x1f) + 2;
        int n2 = ((voodoo->dac_pll_regs[0] >> 13) & 0x07);
        float t = (14318184.0 * ((float)m / (float)n1)) / (float)(1 << n2);
        double clock_const;
        int line_length;

        if ((voodoo->dac_data[6] & 0xf0) == 0x20 || (voodoo->dac_data[6] & 0xf0) == 0x60 || (voodoo->dac_data[6] & 0xf0) == 0x70)
                t /= 2.0f;

        line_length = (voodoo->hSync & 0xff) + ((voodoo->hSync >> 16) & 0x3ff);

        //        pclog("Pixel clock %f MHz hsync %08x line_length %d\n", t, voodoo->hSync, line_length);

        voodoo->pixel_clock = t;

        clock_const = cpuclock / t;
        voodoo->line_time = (uint64_t)((double)line_length * clock_const * (double)(1ull << 32));
}

static void voodoo_calc_clutData(voodoo_t *voodoo) {
        int c;

        for (c = 0; c < 256; c++) {
                voodoo->clutData256[c].r =
                        (voodoo->clutData[c >> 3].r * (8 - (c & 7)) + voodoo->clutData[(c >> 3) + 1].r * (c & 7)) >> 3;
                voodoo->clutData256[c].g =
                        (voodoo->clutData[c >> 3].g * (8 - (c & 7)) + voodoo->clutData[(c >> 3) + 1].g * (c & 7)) >> 3;
                voodoo->clutData256[c].b =
                        (voodoo->clutData[c >> 3].b * (8 - (c & 7)) + voodoo->clutData[(c >> 3) + 1].b * (c & 7)) >> 3;
        }

        for (c = 0; c < 65536; c++) {
                int r = (c >> 8) & 0xf8;
                int g = (c >> 3) & 0xfc;
                int b = (c << 3) & 0xf8;
                //                r |= (r >> 5);
                //                g |= (g >> 6);
                //                b |= (b >> 5);

                voodoo->video_16to32[c] =
                        (voodoo->clutData256[r].r << 16) | (voodoo->clutData256[g].g << 8) | voodoo->clutData256[b].b;
        }
}

#define FILTDIV 256

static int FILTCAP, FILTCAPG, FILTCAPB = 0; /* color filter threshold values */

void voodoo_generate_filter_v1(voodoo_t *voodoo) {
        int g, h;
        float difference, diffg, diffb;
        float thiscol, thiscolg, thiscolb, lined;
        float fcr, fcg, fcb;

        fcr = FILTCAP * 5;
        fcg = FILTCAPG * 6;
        fcb = FILTCAPB * 5;

        for (g = 0; g < FILTDIV; g++) // pixel 1
        {
                for (h = 0; h < FILTDIV; h++) // pixel 2
                {
                        difference = (float)(h - g);
                        diffg = difference;
                        diffb = difference;

                        thiscol = thiscolg = thiscolb = g;

                        if (difference > FILTCAP)
                                difference = FILTCAP;
                        if (difference < -FILTCAP)
                                difference = -FILTCAP;

                        if (diffg > FILTCAPG)
                                diffg = FILTCAPG;
                        if (diffg < -FILTCAPG)
                                diffg = -FILTCAPG;

                        if (diffb > FILTCAPB)
                                diffb = FILTCAPB;
                        if (diffb < -FILTCAPB)
                                diffb = -FILTCAPB;

                        // hack - to make it not bleed onto black
                        // if (g == 0){
                        // difference = diffg = diffb = 0;
                        //}

                        if ((difference < fcr) || (-difference > -fcr))
                                thiscol = g + (difference / 2);
                        if ((diffg < fcg) || (-diffg > -fcg))
                                thiscolg = g + (diffg / 2); /* need these divides so we can actually undither! */
                        if ((diffb < fcb) || (-diffb > -fcb))
                                thiscolb = g + (diffb / 2);

                        if (thiscol < 0)
                                thiscol = 0;
                        if (thiscol > FILTDIV - 1)
                                thiscol = FILTDIV - 1;

                        if (thiscolg < 0)
                                thiscolg = 0;
                        if (thiscolg > FILTDIV - 1)
                                thiscolg = FILTDIV - 1;

                        if (thiscolb < 0)
                                thiscolb = 0;
                        if (thiscolb > FILTDIV - 1)
                                thiscolb = FILTDIV - 1;

                        voodoo->thefilter[g][h] = thiscol;
                        voodoo->thefilterg[g][h] = thiscolg;
                        voodoo->thefilterb[g][h] = thiscolb;
                }

                lined = g + 4;
                if (lined > 255)
                        lined = 255;
                voodoo->purpleline[g][0] = lined;
                voodoo->purpleline[g][2] = lined;

                lined = g + 0;
                if (lined > 255)
                        lined = 255;
                voodoo->purpleline[g][1] = lined;
        }
}

void voodoo_generate_filter_v2(voodoo_t *voodoo) {
        int g, h;
        float difference;
        float thiscol, thiscolg, thiscolb;
        float clr, clg, clb = 0;
        float fcr, fcg, fcb = 0;

        // pre-clamping

        fcr = FILTCAP;
        fcg = FILTCAPG;
        fcb = FILTCAPB;

        if (fcr > 32)
                fcr = 32;
        if (fcg > 32)
                fcg = 32;
        if (fcb > 32)
                fcb = 32;

        for (g = 0; g < 256; g++) // pixel 1 - our target pixel we want to bleed into
        {
                for (h = 0; h < 256; h++) // pixel 2 - our main pixel
                {
                        float avg;
                        float avgdiff;

                        difference = (float)(g - h);
                        avg = (float)((g + g + g + g + h) / 5);
                        avgdiff = avg - (float)((g + h + h + h + h) / 5);
                        if (avgdiff < 0)
                                avgdiff *= -1;
                        if (difference < 0)
                                difference *= -1;

                        thiscol = thiscolg = thiscolb = g;

                        // try lighten
                        if (h > g) {
                                clr = clg = clb = avgdiff;

                                if (clr > fcr)
                                        clr = fcr;
                                if (clg > fcg)
                                        clg = fcg;
                                if (clb > fcb)
                                        clb = fcb;

                                thiscol = g + clr;
                                thiscolg = g + clg;
                                thiscolb = g + clb;

                                if (thiscol > g + FILTCAP)
                                        thiscol = g + FILTCAP;
                                if (thiscolg > g + FILTCAPG)
                                        thiscolg = g + FILTCAPG;
                                if (thiscolb > g + FILTCAPB)
                                        thiscolb = g + FILTCAPB;

                                if (thiscol > g + avgdiff)
                                        thiscol = g + avgdiff;
                                if (thiscolg > g + avgdiff)
                                        thiscolg = g + avgdiff;
                                if (thiscolb > g + avgdiff)
                                        thiscolb = g + avgdiff;
                        }

                        if (difference > FILTCAP)
                                thiscol = g;
                        if (difference > FILTCAPG)
                                thiscolg = g;
                        if (difference > FILTCAPB)
                                thiscolb = g;

                        // clamp
                        if (thiscol < 0)
                                thiscol = 0;
                        if (thiscolg < 0)
                                thiscolg = 0;
                        if (thiscolb < 0)
                                thiscolb = 0;

                        if (thiscol > 255)
                                thiscol = 255;
                        if (thiscolg > 255)
                                thiscolg = 255;
                        if (thiscolb > 255)
                                thiscolb = 255;

                        // add to the table
                        voodoo->thefilter[g][h] = (thiscol);
                        voodoo->thefilterg[g][h] = (thiscolg);
                        voodoo->thefilterb[g][h] = (thiscolb);

                        // debug the ones that don't give us much of a difference
                        // if (difference < FILTCAP)
                        // pclog("Voodoofilter: %ix%i - %f difference, %f average difference, R=%f, G=%f, B=%f\n", g, h,
                        // difference, avgdiff, thiscol, thiscolg, thiscolb);
                }
        }
}

void voodoo_threshold_check(voodoo_t *voodoo) {
        int r, g, b;

        if (!voodoo->scrfilterEnabled)
                return; /* considered disabled; don't check and generate */

        /* Check for changes, to generate anew table */
        if (voodoo->scrfilterThreshold != voodoo->scrfilterThresholdOld) {
                r = (voodoo->scrfilterThreshold >> 16) & 0xFF;
                g = (voodoo->scrfilterThreshold >> 8) & 0xFF;
                b = voodoo->scrfilterThreshold & 0xFF;

                FILTCAP = r;
                FILTCAPG = g;
                FILTCAPB = b;

                pclog("Voodoo Filter Threshold Check: %06x - RED %i GREEN %i BLUE %i\n", voodoo->scrfilterThreshold, r, g, b);

                voodoo->scrfilterThresholdOld = voodoo->scrfilterThreshold;

                if (voodoo->type == VOODOO_2)
                        voodoo_generate_filter_v2(voodoo);
                else
                        voodoo_generate_filter_v1(voodoo);

                if (voodoo->type >= VOODOO_BANSHEE)
                        voodoo_generate_vb_filters(voodoo, FILTCAP, FILTCAPG);
        }
}

static void voodoo_filterline_v1(voodoo_t *voodoo, uint8_t *fil, int column, uint16_t *src, int line) {
        int x;

        // Scratchpad for avoiding feedback streaks
        uint8_t fil3[4096 * 3];

        assert(voodoo->h_disp <= 4096);

        /* 16 to 32-bit */
        for (x = 0; x < column; x++) {
                fil[x * 3] = ((src[x] & 31) << 3);
                fil[x * 3 + 1] = (((src[x] >> 5) & 63) << 2);
                fil[x * 3 + 2] = (((src[x] >> 11) & 31) << 3);

                // Copy to our scratchpads
                fil3[x * 3 + 0] = fil[x * 3 + 0];
                fil3[x * 3 + 1] = fil[x * 3 + 1];
                fil3[x * 3 + 2] = fil[x * 3 + 2];
        }

        /* lines */

        if (line & 1) {
                for (x = 0; x < column; x++) {
                        fil[x * 3] = voodoo->purpleline[fil[x * 3]][0];
                        fil[x * 3 + 1] = voodoo->purpleline[fil[x * 3 + 1]][1];
                        fil[x * 3 + 2] = voodoo->purpleline[fil[x * 3 + 2]][2];
                }
        }

        /* filtering time */

        for (x = 1; x < column; x++) {
                fil3[(x)*3] = voodoo->thefilterb[fil[x * 3]][fil[(x - 1) * 3]];
                fil3[(x)*3 + 1] = voodoo->thefilterg[fil[x * 3 + 1]][fil[(x - 1) * 3 + 1]];
                fil3[(x)*3 + 2] = voodoo->thefilter[fil[x * 3 + 2]][fil[(x - 1) * 3 + 2]];
        }

        for (x = 1; x < column; x++) {
                fil[(x)*3] = voodoo->thefilterb[fil3[x * 3]][fil3[(x - 1) * 3]];
                fil[(x)*3 + 1] = voodoo->thefilterg[fil3[x * 3 + 1]][fil3[(x - 1) * 3 + 1]];
                fil[(x)*3 + 2] = voodoo->thefilter[fil3[x * 3 + 2]][fil3[(x - 1) * 3 + 2]];
        }

        for (x = 1; x < column; x++) {
                fil3[(x)*3] = voodoo->thefilterb[fil[x * 3]][fil[(x - 1) * 3]];
                fil3[(x)*3 + 1] = voodoo->thefilterg[fil[x * 3 + 1]][fil[(x - 1) * 3 + 1]];
                fil3[(x)*3 + 2] = voodoo->thefilter[fil[x * 3 + 2]][fil[(x - 1) * 3 + 2]];
        }

        for (x = 0; x < column - 1; x++) {
                fil[(x)*3] = voodoo->thefilterb[fil3[x * 3]][fil3[(x + 1) * 3]];
                fil[(x)*3 + 1] = voodoo->thefilterg[fil3[x * 3 + 1]][fil3[(x + 1) * 3 + 1]];
                fil[(x)*3 + 2] = voodoo->thefilter[fil3[x * 3 + 2]][fil3[(x + 1) * 3 + 2]];
        }
}

static void voodoo_filterline_v2(voodoo_t *voodoo, uint8_t *fil, int column, uint16_t *src, int line) {
        int x;

        // Scratchpad for blending filter
        uint8_t fil3[4096 * 3];

        assert(voodoo->h_disp <= 4096);

        /* 16 to 32-bit */
        for (x = 0; x < column; x++) {
                // Blank scratchpads
                fil3[x * 3 + 0] = fil[x * 3 + 0] = ((src[x] & 31) << 3);
                fil3[x * 3 + 1] = fil[x * 3 + 1] = (((src[x] >> 5) & 63) << 2);
                fil3[x * 3 + 2] = fil[x * 3 + 2] = (((src[x] >> 11) & 31) << 3);
        }

        /* filtering time */

        for (x = 1; x < column - 3; x++) {
                fil3[(x + 3) * 3] = voodoo->thefilterb[((src[x + 3] & 31) << 3)][((src[x] & 31) << 3)];
                fil3[(x + 3) * 3 + 1] = voodoo->thefilterg[(((src[x + 3] >> 5) & 63) << 2)][(((src[x] >> 5) & 63) << 2)];
                fil3[(x + 3) * 3 + 2] = voodoo->thefilter[(((src[x + 3] >> 11) & 31) << 3)][(((src[x] >> 11) & 31) << 3)];

                fil[(x + 2) * 3] = voodoo->thefilterb[fil3[(x + 2) * 3]][((src[x] & 31) << 3)];
                fil[(x + 2) * 3 + 1] = voodoo->thefilterg[fil3[(x + 2) * 3 + 1]][(((src[x] >> 5) & 63) << 2)];
                fil[(x + 2) * 3 + 2] = voodoo->thefilter[fil3[(x + 2) * 3 + 2]][(((src[x] >> 11) & 31) << 3)];

                fil3[(x + 1) * 3] = voodoo->thefilterb[fil[(x + 1) * 3]][((src[x] & 31) << 3)];
                fil3[(x + 1) * 3 + 1] = voodoo->thefilterg[fil[(x + 1) * 3 + 1]][(((src[x] >> 5) & 63) << 2)];
                fil3[(x + 1) * 3 + 2] = voodoo->thefilter[fil[(x + 1) * 3 + 2]][(((src[x] >> 11) & 31) << 3)];

                fil[(x - 1) * 3] = voodoo->thefilterb[fil3[(x - 1) * 3]][((src[x] & 31) << 3)];
                fil[(x - 1) * 3 + 1] = voodoo->thefilterg[fil3[(x - 1) * 3 + 1]][(((src[x] >> 5) & 63) << 2)];
                fil[(x - 1) * 3 + 2] = voodoo->thefilter[fil3[(x - 1) * 3 + 2]][(((src[x] >> 11) & 31) << 3)];
        }

        // unroll for edge cases

        fil3[(column - 3) * 3] = voodoo->thefilterb[((src[column - 3] & 31) << 3)][((src[column] & 31) << 3)];
        fil3[(column - 3) * 3 + 1] = voodoo->thefilterg[(((src[column - 3] >> 5) & 63) << 2)][(((src[column] >> 5) & 63) << 2)];
        fil3[(column - 3) * 3 + 2] = voodoo->thefilter[(((src[column - 3] >> 11) & 31) << 3)][(((src[column] >> 11) & 31) << 3)];

        fil3[(column - 2) * 3] = voodoo->thefilterb[((src[column - 2] & 31) << 3)][((src[column] & 31) << 3)];
        fil3[(column - 2) * 3 + 1] = voodoo->thefilterg[(((src[column - 2] >> 5) & 63) << 2)][(((src[column] >> 5) & 63) << 2)];
        fil3[(column - 2) * 3 + 2] = voodoo->thefilter[(((src[column - 2] >> 11) & 31) << 3)][(((src[column] >> 11) & 31) << 3)];

        fil3[(column - 1) * 3] = voodoo->thefilterb[((src[column - 1] & 31) << 3)][((src[column] & 31) << 3)];
        fil3[(column - 1) * 3 + 1] = voodoo->thefilterg[(((src[column - 1] >> 5) & 63) << 2)][(((src[column] >> 5) & 63) << 2)];
        fil3[(column - 1) * 3 + 2] = voodoo->thefilter[(((src[column - 1] >> 11) & 31) << 3)][(((src[column] >> 11) & 31) << 3)];

        fil[(column - 2) * 3] = voodoo->thefilterb[fil3[(column - 2) * 3]][((src[column] & 31) << 3)];
        fil[(column - 2) * 3 + 1] = voodoo->thefilterg[fil3[(column - 2) * 3 + 1]][(((src[column] >> 5) & 63) << 2)];
        fil[(column - 2) * 3 + 2] = voodoo->thefilter[fil3[(column - 2) * 3 + 2]][(((src[column] >> 11) & 31) << 3)];

        fil[(column - 1) * 3] = voodoo->thefilterb[fil3[(column - 1) * 3]][((src[column] & 31) << 3)];
        fil[(column - 1) * 3 + 1] = voodoo->thefilterg[fil3[(column - 1) * 3 + 1]][(((src[column] >> 5) & 63) << 2)];
        fil[(column - 1) * 3 + 2] = voodoo->thefilter[fil3[(column - 1) * 3 + 2]][(((src[column] >> 11) & 31) << 3)];

        fil3[(column - 1) * 3] = voodoo->thefilterb[fil[(column - 1) * 3]][((src[column] & 31) << 3)];
        fil3[(column - 1) * 3 + 1] = voodoo->thefilterg[fil[(column - 1) * 3 + 1]][(((src[column] >> 5) & 63) << 2)];
        fil3[(column - 1) * 3 + 2] = voodoo->thefilter[fil[(column - 1) * 3 + 2]][(((src[column] >> 11) & 31) << 3)];
}

void voodoo_callback(void *p) {
        voodoo_t *voodoo = (voodoo_t *)p;

        if (voodoo->fbiInit0 & FBIINIT0_VGA_PASS) {
                if (voodoo->line < voodoo->v_disp) {
                        voodoo_t *draw_voodoo;
                        int draw_line;

                        if (SLI_ENABLED) {
                                if (voodoo == voodoo->set->voodoos[1])
                                        goto skip_draw;

                                if (((voodoo->initEnable & INITENABLE_SLI_MASTER_SLAVE) ? 1 : 0) == (voodoo->line & 1))
                                        draw_voodoo = voodoo;
                                else
                                        draw_voodoo = voodoo->set->voodoos[1];
                                draw_line = voodoo->line >> 1;
                        } else {
                                if (!(voodoo->fbiInit0 & 1))
                                        goto skip_draw;
                                draw_voodoo = voodoo;
                                draw_line = voodoo->line;
                        }

                        if (draw_voodoo->dirty_line[draw_line]) {
                                uint32_t *p = &((uint32_t *)buffer32->line[voodoo->line])[32];
                                uint16_t *src = (uint16_t *)&draw_voodoo
                                                        ->fb_mem[draw_voodoo->front_offset + draw_line * draw_voodoo->row_width];
                                int x;

                                draw_voodoo->dirty_line[draw_line] = 0;

                                if (voodoo->line < voodoo->dirty_line_low) {
                                        voodoo->dirty_line_low = voodoo->line;
                                        video_wait_for_buffer();
                                }
                                if (voodoo->line > voodoo->dirty_line_high)
                                        voodoo->dirty_line_high = voodoo->line;

                                if (voodoo->scrfilter && voodoo->scrfilterEnabled) {
                                        uint8_t fil[4096 * 3]; /* interleaved 24-bit RGB */

                                        assert(voodoo->h_disp <= 4096);

                                        if (voodoo->type == VOODOO_2)
                                                voodoo_filterline_v2(voodoo, fil, voodoo->h_disp, src, voodoo->line);
                                        else
                                                voodoo_filterline_v1(voodoo, fil, voodoo->h_disp, src, voodoo->line);

                                        for (x = 0; x < voodoo->h_disp; x++) {
                                                p[x] = (voodoo->clutData256[fil[x * 3]].b << 0 |
                                                        voodoo->clutData256[fil[x * 3 + 1]].g << 8 |
                                                        voodoo->clutData256[fil[x * 3 + 2]].r << 16);
                                        }
                                } else {
                                        for (x = 0; x < voodoo->h_disp; x++) {
                                                p[x] = draw_voodoo->video_16to32[src[x]];
                                        }
                                }
                        }
                }
        }
skip_draw:
        if (voodoo->line == voodoo->v_disp) {
                //                pclog("retrace %i %i %08x %i\n", voodoo->retrace_count, voodoo->swap_interval,
                //                voodoo->swap_offset, voodoo->swap_pending);
                voodoo->retrace_count++;
                if (SLI_ENABLED && (voodoo->fbiInit2 & FBIINIT2_SWAP_ALGORITHM_MASK) == FBIINIT2_SWAP_ALGORITHM_SLI_SYNC) {
                        if (voodoo == voodoo->set->voodoos[0]) {
                                voodoo_t *voodoo_1 = voodoo->set->voodoos[1];

                                thread_lock_mutex(voodoo->swap_mutex);
                                /*Only swap if both Voodoos are waiting for buffer swap*/
                                if (voodoo->swap_pending && (voodoo->retrace_count > voodoo->swap_interval) &&
                                    voodoo_1->swap_pending && (voodoo_1->retrace_count > voodoo_1->swap_interval)) {
                                        memset(voodoo->dirty_line, 1, 1024);
                                        voodoo->retrace_count = 0;
                                        voodoo->front_offset = voodoo->swap_offset;
                                        if (voodoo->swap_count > 0)
                                                voodoo->swap_count--;
                                        voodoo->swap_pending = 0;

                                        memset(voodoo_1->dirty_line, 1, 1024);
                                        voodoo_1->retrace_count = 0;
                                        voodoo_1->front_offset = voodoo_1->swap_offset;
                                        if (voodoo_1->swap_count > 0)
                                                voodoo_1->swap_count--;
                                        voodoo_1->swap_pending = 0;
                                        thread_unlock_mutex(voodoo->swap_mutex);

                                        thread_set_event(voodoo->wake_fifo_thread);
                                        thread_set_event(voodoo_1->wake_fifo_thread);

                                        voodoo->frame_count++;
                                        voodoo_1->frame_count++;
                                } else
                                        thread_unlock_mutex(voodoo->swap_mutex);
                        }
                } else {
                        thread_lock_mutex(voodoo->swap_mutex);
                        if (voodoo->swap_pending && (voodoo->retrace_count > voodoo->swap_interval)) {
                                voodoo->front_offset = voodoo->swap_offset;
                                if (voodoo->swap_count > 0)
                                        voodoo->swap_count--;
                                voodoo->swap_pending = 0;
                                thread_unlock_mutex(voodoo->swap_mutex);

                                memset(voodoo->dirty_line, 1, 1024);
                                voodoo->retrace_count = 0;
                                thread_set_event(voodoo->wake_fifo_thread);
                                voodoo->frame_count++;
                        } else
                                thread_unlock_mutex(voodoo->swap_mutex);
                }
                voodoo->v_retrace = 1;
        }
        voodoo->line++;

        if (voodoo->fbiInit0 & FBIINIT0_VGA_PASS) {
                if (voodoo->line == voodoo->v_disp) {
                        if (voodoo->dirty_line_high > voodoo->dirty_line_low)
                                svga_doblit(0, voodoo->v_disp, voodoo->h_disp, voodoo->v_disp - 1, voodoo->svga);
                        if (voodoo->clutData_dirty) {
                                voodoo->clutData_dirty = 0;
                                voodoo_calc_clutData(voodoo);
                        }
                        voodoo->dirty_line_high = -1;
                        voodoo->dirty_line_low = 2000;
                }
        }

        if (voodoo->line >= voodoo->v_total) {
                voodoo->line = 0;
                voodoo->v_retrace = 0;
        }
        if (voodoo->line_time)
                timer_advance_u64(&voodoo->timer, voodoo->line_time);
        else
                timer_advance_u64(&voodoo->timer, TIMER_USEC * 32);
}
