Subversion Repositories shark

Rev

Blame | Last modification | View Log | RSS feed

/* VGAlib version 1.2 - (c) 1993 Tommy Frandsen                    */
/*                                                                 */
/* This library is free software; you can redistribute it and/or   */
/* modify it without any restrictions. This library is distributed */
/* in the hope that it will be useful, but without any warranty.   */

/* Multi-chipset support Copyright 1993 Harm Hanemaayer */
/* partially copyrighted (C) 1993 by Hartmut Schirmer */

/* 21 January 1995 - added vga_readscanline(), added support for   */
/* non 8-pixel aligned scanlines in 16 color mode. billr@rastergr.com */
#include <stdio.h>
#include "vga.h"
#include "libvga.h"
#include "driver.h"

/* used to decompose color value into bits (for fast scanline drawing) */
union bits {
    struct {
        unsigned char bit3;
        unsigned char bit2;
        unsigned char bit1;
        unsigned char bit0;
    } b;
    unsigned int i;
};

/* color decompositions */
static union bits color16[16] =
{
    {
        {0, 0, 0, 0}},
    {
        {0, 0, 0, 1}},
    {
        {0, 0, 1, 0}},
    {
        {0, 0, 1, 1}},
    {
        {0, 1, 0, 0}},
    {
        {0, 1, 0, 1}},
    {
        {0, 1, 1, 0}},
    {
        {0, 1, 1, 1}},
    {
        {1, 0, 0, 0}},
    {
        {1, 0, 0, 1}},
    {
        {1, 0, 1, 0}},
    {
        {1, 0, 1, 1}},
    {
        {1, 1, 0, 0}},
    {
        {1, 1, 0, 1}},
    {
        {1, 1, 1, 0}},
    {
        {1, 1, 1, 1}}};

/* mask for end points in plane buffer mode */
static unsigned char mask[8] =
{
    0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
/* display plane buffers (for fast scanline drawing) */
/* 256 bytes -> max 2048 pixel per line (2/16 colors) */
static unsigned char plane0[256];
static unsigned char plane1[256];
static unsigned char plane2[256];
static unsigned char plane3[256];

static inline void shifted_memcpy(void *dest_in, void *source_in, int len)
{
    int *dest = dest_in;
    int *source = source_in;

    len >>= 2;

    while (len--)
        *dest++ = (*source++ << 8);
}

/* RGB_swapped_memcopy returns the amount of bytes unhandled */
static inline int RGB_swapped_memcpy(char *dest, char *source, int len)
{
    int rest, tmp;

    tmp = len / 3;
    rest = len - 3 * tmp;
    len = tmp;

    while (len--) {
        *dest++ = source[2];
        *dest++ = source[1];
        *dest++ = source[0];
        source += 3;
    }

    return rest;
}

int vga_drawscanline(int line, unsigned char *colors)
{
    if ((CI.colors == 2) || (CI.colors > 256))
        return vga_drawscansegment(colors, 0, line, CI.xbytes);
    else
        return vga_drawscansegment(colors, 0, line, CI.xdim);
}

#ifdef LIBC_MEMCPY
#define MEMCPY memcpy
#else
void MEMCPY(unsigned char *dst, unsigned char *src, size_t n) {
    unsigned char *e;
    e=src+n;
    while(src<e) *(dst++)=*(src++);
}
#endif

int vga_drawscansegment(unsigned char *colors, int x, int y, int length)
{
    /* both length and x must divide with 8 */
    /*  no longer true (at least for 16 & 256 colors) */

    if (MODEX)
        goto modeX;
    switch (CI.colors) {
    case 16:
        {
            int i, j, k, first, last, page, l1, l2;
            int offset, eoffs, soffs, ioffs;
            union bits bytes;
            unsigned char *address;

            k = 0;
            soffs = ioffs = (x & 0x7);  /* starting offset into first byte */
            eoffs = (x + length) & 0x7;         /* ending offset into last byte */
            for (i = 0; i < length;) {
                bytes.i = 0;
                first = i;
                last = i + 8 - ioffs;
                if (last > length)
                    last = length;
                for (j = first; j < last; j++, i++)
                    bytes.i = (bytes.i << 1) | color16[colors[j]].i;
                plane0[k] = bytes.b.bit0;
                plane1[k] = bytes.b.bit1;
                plane2[k] = bytes.b.bit2;
                plane3[k++] = bytes.b.bit3;
                ioffs = 0;
            }
            if (eoffs) {
                /* fixup last byte */
                k--;
                bytes.i <<= (8 - eoffs);
                plane0[k] = bytes.b.bit0;
                plane1[k] = bytes.b.bit1;
                plane2[k] = bytes.b.bit2;
                plane3[k++] = bytes.b.bit3;
            }
            offset = (y * CI.xdim + x) / 8;
            vga_setpage((page = offset >> 16));
            l1 = 0x10000 - (offset &= 0xffff);
            /* k currently contains number of bytes to write */
            if (l1 > k)
                l1 = k;
            l2 = k - l1;
            /* make k the index of the last byte to write */
            k--;

            address = GM + offset;

            /* disable Set/Reset Register */
            __svgalib_outgra(0x01,0x00);

            /* write to all bits */
            __svgalib_outgra(0x08,0xff);

            /* select write map mask register */
            __svgalib_outseq(0x02,0x01);

            /* select read map mask register */
            __svgalib_outgra(0x04,0x00);
            if (soffs)
                plane0[0] |= *address & ~mask[soffs];
            if (eoffs && l2 == 0)
                plane0[k] |= *(address + l1 - 1) & mask[eoffs];
            MEMCPY(address, plane0, l1);

            /* write plane 1 */
            __svgalib_outseq(0x02,0x02);
            /* read plane 1 */
            __svgalib_outgra(0x04,0x01);
            if (soffs)
                plane1[0] |= *address & ~mask[soffs];
            if (eoffs && l2 == 0)
                plane1[k] |= *(address + l1 - 1) & mask[eoffs];
            MEMCPY(address, plane1, l1);

            /* write plane 2 */
            __svgalib_outseq(0x02,0x04);
            /* read plane 2 */
            __svgalib_outgra(0x04,0x02);
            if (soffs)
                plane2[0] |= *address & ~mask[soffs];
            if (eoffs && l2 == 0)
                plane2[k] |= *(address + l1 - 1) & mask[eoffs];
            MEMCPY(address, plane2, l1);

            /* write plane 3 */
            __svgalib_outseq(0x02,0x08);
            /* read plane 3 */
            __svgalib_outgra(0x04,0x03);
            if (soffs)
                plane3[0] |= *address & ~mask[soffs];
            if (eoffs && l2 == 0)
                plane3[k] |= *(address + l1 - 1) & mask[eoffs];
            MEMCPY(address, plane3, l1);

            if (l2 > 0) {
                vga_setpage(page + 1);

                /* write plane 0 */
                __svgalib_outseq(0x02,0x01);
                if (eoffs) {
                    /* read plane 0 */
                    __svgalib_outgra(0x04,0x00);
                    plane0[k] |= *(GM + l2 - 1) & mask[eoffs];
                }
                MEMCPY(GM, &plane0[l1], l2);

                /* write plane 1 */
                __svgalib_outseq(0x02,0x02);
                if (eoffs) {
                    /* read plane 1 */
                    __svgalib_outgra(0x04,0x01);
                    plane1[k] |= *(GM + l2 - 1) & mask[eoffs];
                }
                MEMCPY(GM, &plane1[l1], l2);

                /* write plane 2 */
                __svgalib_outseq(0x02,0x04);
                if (eoffs) {
                    /* read plane 2 */
                    __svgalib_outgra(0x04,0x02);
                    plane2[k] |= *(GM + l2 - 1) & mask[eoffs];
                }
                MEMCPY(GM, &plane2[l1], l2);

                /* write plane 3 */
                __svgalib_outseq(0x02,0x08);
                if (eoffs) {
                    /* read plane 3 */
                    __svgalib_outgra(0x04,0x03);
                    plane3[k] |= *(GM + l2 - 1) & mask[eoffs];
                }
                MEMCPY(GM, &plane3[l1], l2);
            }
            /* restore map mask register */
            __svgalib_outseq(0x02,0x0f);

            /* enable Set/Reset Register */
            __svgalib_outgra(0x01,0x0f);
        }
        break;
    case 2:
        {
            /* disable Set/Reset Register */
            __svgalib_outgra(0x01,0x00);

            /* write to all bits */
            __svgalib_outgra(0x08,0xff);

            /* write to all planes */
            __svgalib_outseq(0x02,0x0f);

            MEMCPY(GM + (y * CI.xdim + x) / 8, colors, length);

            /* restore map mask register */
            __svgalib_outseq(0x02,0x0f);

            /* enable Set/Reset Register */
            __svgalib_outgra(0x01,0x0f);
        }
        break;
    case 256:
        {
            switch (CM) {
            case G320x200x256:  /* linear addressing - easy and fast */
                MEMCPY(GM + (y * CI.xdim + x), colors, length);
                return 0;
            case G320x240x256:
            case G320x400x256:
            case G360x480x256:
            case G400x300x256X:
              modeX:
                {
                    int first, offset, pixel, plane;

                    for (plane = 0; plane < 4; plane++) {
                        /* select plane */
                        __svgalib_outseq(0x02,1 << plane );

                        pixel = ((4 - (x & 3) + plane) & 3);
                        first = (y * CI.xdim + x) / 4;
                        if((x & 3) + pixel > 3)
                            first++;
                        for (offset = first; pixel < length; offset++) {
                            *(GM+offset) = colors[pixel];
                            pixel += 4;
                        }
                    }
                }
                return 0;
            }
            {
                unsigned long offset;
                int segment, free;

              SegmentedCopy:
                offset = y * CI.xbytes + x;
                if (__svgalib_modeinfo_linearset & IS_LINEAR ) {
                    MEMCPY(LINEAR_POINTER+offset, colors, length);
                } else {
                    segment = offset >> 16;
                    free = ((segment + 1) << 16) - offset;
                    offset &= 0xFFFF;
   
                    if (free < length) {
                        vga_setpage(segment);
                        MEMCPY(GM + offset, colors, free);
                        vga_setpage(segment + 1);
                        MEMCPY(GM, colors + free, length - free);
                    } else {
                        vga_setpage(segment);
                        MEMCPY(GM + offset, colors, length);
                    }
                }
            }
        }
        break;
    case 32768:
    case 65536:
        x *= 2;
        goto SegmentedCopy;
    case 1 << 24:
        if (__svgalib_cur_info.bytesperpixel == 4) {
            x <<= 2;
            if (MODEFLAGS & RGB_MISORDERED) {
                unsigned long offset;
                int segment, free;

                offset = y * CI.xbytes + x;
                if (__svgalib_modeinfo_linearset & IS_LINEAR ) {
                    shifted_memcpy(LINEAR_POINTER+offset, colors, length);
                } else {
                    segment = offset >> 16;
                    free = ((segment + 1) << 16) - offset;
                    offset &= 0xFFFF;
   
                    if (free < length) {
                        vga_setpage(segment);
                        shifted_memcpy(GM + offset, colors, free);
                        vga_setpage(segment + 1);
                        shifted_memcpy(GM, colors + free, length - free);
                    } else {
                        vga_setpage(segment);
                        shifted_memcpy(GM + offset, colors, length);
                    }
                }
            } else {
                goto SegmentedCopy;
            }
            break;
        }
        x *= 3;
        if (MODEFLAGS & RGB_MISORDERED) {
            unsigned long offset;
            int segment, free;

            offset = y * CI.xbytes + x;
            if (__svgalib_modeinfo_linearset & IS_LINEAR ) {
                RGB_swapped_memcpy(LINEAR_POINTER+offset, colors, length);
            } else {
                segment = offset >> 16;
                free = ((segment + 1) << 16) - offset;
                offset &= 0xFFFF;
   
                if (free < length) {
                    int i;
   
                    vga_setpage(segment);
                    i = RGB_swapped_memcpy(GM + offset, colors, free);
                    colors += (free - i);
   
                    switch (i) {
                    case 2:
                        *(GM+0xfffe) = colors[2];
                        *(GM+0xffff) = colors[1];
                        break;
                    case 1:
                        *(GM+0xffff) = colors[2];
                        break;
                    }
   
                    vga_setpage(segment + 1);
   
                    switch (i) {
                    case 1:
                        *(GM+1) = colors[0];
                        *(GM)   = colors[1];
                        i = 3 - i;
                        free += i;
                        colors += 3;
                        break;
                    case 2:
                        *(GM) = colors[0];
                        i = 3 - i;
                        free += i;
                        colors += 3;
                        break;
                    }
   
                    RGB_swapped_memcpy(GM + i, colors, length - free);
                } else {
                    vga_setpage(segment);
                    RGB_swapped_memcpy(GM + offset, colors, length);
                }
            }
        } else {
            goto SegmentedCopy;
        }
    }

    return 0;
}

int vga_getscansegment(unsigned char *colors, int x, int y, int length)
{

    if (MODEX)
        goto modeX2;
    switch (CI.colors) {
    case 16:
        {
            int i, k, page, l1, l2;
            int offset, eoffs, soffs, nbytes, bit;
            unsigned char *address;
            unsigned char color;
            k = 0;
            soffs = (x & 0x7);  /* starting offset into first byte */
            eoffs = (x + length) & 0x7;         /* ending offset into last byte */
            offset = (y * CI.xdim + x) / 8;
            vga_setpage((page = offset >> 16));
            l1 = 0x10000 - (offset &= 0xffff);
            if (soffs)
                nbytes = (length - (8 - soffs)) / 8 + 1;
            else
                nbytes = length / 8;
            if (eoffs)
                nbytes++;
            if (l1 > nbytes)
                l1 = nbytes;
            l2 = nbytes - l1;
            address = GM + offset;
            /* disable Set/Reset Register */
            __svgalib_outgra(0x01,0x00);
            /* read plane 0 */
            __svgalib_outgra(0x04,0x00);
            memcpy(plane0, address, l1);
            /* read plane 1 */
            __svgalib_outgra(0x04,0x01);
            memcpy(plane1, address, l1);
            /* read plane 2 */
            __svgalib_outgra(0x04,0x02);
            memcpy(plane2, address, l1);
            /* read plane 3 */
            __svgalib_outgra(0x04,0x03);
            memcpy(plane3, address, l1);
            if (l2 > 0) {
                vga_setpage(page + 1);
                /* read plane 0 */
              __svgalib_outgra(0x04,0x00);
                memcpy(&plane0[l1], GM, l2);
                /* read plane 1 */
              __svgalib_outgra(0x04,0x01);
                memcpy(&plane1[l1], GM, l2);
                /* read plane 2 */
              __svgalib_outgra(0x04,0x02);
                memcpy(&plane2[l1], GM, l2);
                /* read plane 3 */
              __svgalib_outgra(0x04,0x03);
                memcpy(&plane3[l1], GM, l2);
            }
            /* enable Set/Reset Register */
            __svgalib_outgra(0x01,0x0f);
            k = 0;
            for (i = 0; i < length;) {
                for (bit = 7 - soffs; bit >= 0 && i < length; bit--, i++) {
                    color = (plane0[k] & (1 << bit) ? 1 : 0);
                    color |= (plane1[k] & (1 << bit) ? 1 : 0) << 1;
                    color |= (plane2[k] & (1 << bit) ? 1 : 0) << 2;
                    color |= (plane3[k] & (1 << bit) ? 1 : 0) << 3;
                    colors[i] = color;
                }
                k++;
                soffs = 0;
            }
        }
        break;
    case 2:
        {
            /* disable Set/Reset Register */
            __svgalib_outgra(0x01,0x00);
            /* read from plane 0 */
            __svgalib_outseq(0x04,0x00);
            memcpy(colors, GM + (y * CI.xdim + x) / 8, length);
            /* enable Set/Reset Register */
            __svgalib_outgra(0x01,0x0f);
        }
        break;
    case 256:
        {
            switch (CM) {
            case G320x200x256:  /* linear addressing - easy and fast */
                memcpy(colors, GM + y * CI.xdim + x, length);
                return 0;
            case G320x240x256:
            case G320x400x256:
            case G360x480x256:
            case G400x300x256X:
              modeX2:
                {
                    int first, offset, pixel, plane;
                    for (plane = 0; plane < 4; plane++) {
                        /* select plane */
                        __svgalib_outgra(0x04, plane);
                        pixel = ((4 - (x & 3) + plane) & 3);
                        first = (y * CI.xdim + x) / 4;
                        if((x & 3) + pixel > 3)
                            first++;
                        for (offset = first; pixel < length; offset++) {
                            colors[pixel] = gr_readb(offset);
                            pixel += 4;
                        }
                    }
                }
                return 0;
            }
            {
                unsigned long offset;
                int segment, free;
              SegmentedCopy2:
                offset = y * CI.xbytes + x;
                if (__svgalib_modeinfo_linearset & IS_LINEAR ) {
                    memcpy(colors, LINEAR_POINTER+offset, length);
                } else {
                    segment = offset >> 16;
                    free = ((segment + 1) << 16) - offset;
                    offset &= 0xFFFF;
                    if (free < length) {
                        vga_setpage(segment);
                        memcpy(colors, GM + offset, free);
                        vga_setpage(segment + 1);
                        memcpy(colors + free, GM, length - free);
                    } else {
                        vga_setpage(segment);
                        memcpy(colors, GM + offset, length);
                    }
                }
            }
        }
        break;
    case 32768:
    case 65536:
        x *= 2;
        goto SegmentedCopy2;
    case 1 << 24:
        if (__svgalib_cur_info.bytesperpixel == 4) {
                x<<=2;
        } else {
                x *= 3;
        }
        goto SegmentedCopy2;
    }
    return 0;
}