/******************************************************************************
 * visr.c - Chapter 7 SBUFI sample code                                       *
 *                                                                            *
 * mach64 functions to handle vertical line interrupts                        *
 *                                                                            *
 * Copyright (c) 1994-1998 ATI Technologies Inc.  All rights reserved.        *
 ******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <i86.h>
#include "..\util\atim64.h"
#include "..\util\defines.h"
#include "..\util\main.h"

extern void int_off();
#pragma aux int_off = "cli";

extern void int_on();
#pragma aux int_on = "sti";

void (__interrupt __far *prev_int)(void);
short irq_num;
short irq_index;
short irq_mask1;
short irq_mask2;
short count;
short iwidth;
short height;
short srcx;
short srcy;
short savex;
short savey;
short ydraw;
short step;
short xpos;
short image;


/******************************************************************************
 * __interrupt VlineISR                                                       *
 *  Function: Interrupt routine that is chained to int 2, 3, 5, or 0xA        *
 *    Inputs: NONE                                                            *
 *   Outputs: NONE                                                            *
 ******************************************************************************/

void __interrupt VlineISR (void)
{
    short temp;

    int_off ();

    // Check if interrupt trigger belongs to the VLINE interrupt.

    if (ior16 (CRTC_INT_CNTL) & 0x18)
    {
        count++;

        // ACK cascade 8259 - required for IRQ 2, 10.
        if ((irq_num >= 0x0A) || (irq_num == 0x02))
        {
            outp (0xA0, 0x20);
        } // if

        // ACK cascade 8259 - required for 2, 3, 5.

        if (irq_num != 0x0A)
        {
            outp (0x20, 0x20);
        } // if

        // ACK CRTC_VLINE.

        temp = ior16 (CRTC_INT_CNTL);
        temp = (temp | 0x10);
        iow16 (CRTC_INT_CNTL, temp);

        // x position is greater than 0, so restore frame.

        if (xpos != 0)
        {
            blit (savex, savey, (xpos-step), ydraw, iwidth, height);
        } // if

        // Save frame.
        blit (xpos, ydraw, savex, savey, iwidth, height);

        // Update frame.
        blit (srcx, srcy, xpos, ydraw, iwidth, height);
    } // if

    // Call old interrupt vector that this routine is chained to.

    _chain_intr (prev_int);
    int_on ();

    return;

} // VlineISR


/******************************************************************************
 * get_count                                                                  *
 *  Function: returns the value of count                                      *
 *    Inputs: NONE                                                            *
 *   Outputs: count - flag to indicate that an interrupt has occured          *
 ******************************************************************************/

short get_count (void)
{
    return (count);
} // get_count


/******************************************************************************
 * set_blit_info                                                              *
 *  Function: stores the blit information into local variables                *
 *    Inputs: wdth - width of blit image                                      *
 *            hght - height of blit image                                     *
 *            x0 - top left x coordinate of blit source                       *
 *            y0 - top left y coordinate of blit source                       *
 *            x1 - top left x coordinate of save position                     *
 *            y1 - top left y coordinate of save position                     *
 *            ydraw - vertical position of destination of blit                *
 *            step - blit incrementat step                                    *
 *   Outputs: NONE                                                            *
 ******************************************************************************/

void set_blit_info (short wdth, short hght, short x0, short y0,
                    short x1, short y1, short yd, short stp)
{
    iwidth = wdth;
    height = hght;
    srcx = x0;
    srcy = y0;
    savex = x1;
    savey = y1;
    ydraw = yd;
    step = stp;

    return;

} // set_blit_info


/******************************************************************************
 * set_x_pos                                                                  *
 *  Function: stores position of the destination blit (x coordinate only)     *
 *    Inputs: pos - x coordinate of destination of blit                       *
 *   Outputs: NONE                                                            *
 ******************************************************************************/

void set_x_pos (short pos)
{
    xpos = pos;

    return;

} // set_x_pos


/******************************************************************************
 * set_image                                                                  *
 *  Function: stores the source image for the ISR                             *
 *    Inputs: image - image number (frame)                                    *
 *            imgx - x coordinate of image                                    *
 *            imgy - y coordinate of image                                    *
 *   Outputs: NONE                                                            *
 ******************************************************************************/

void set_image (short img, short imgx, short imgy)
{
    image = img;
    srcx = imgx;
    srcy = imgy;

    return;

} // set_image


/******************************************************************************
 * get_irq_index                                                              *
 *  Function: converts the irq number to the indexing address                 *
 *    Inputs: irqnum - interrupt number (2, 3, 5, of 0xA                      *
 *   Outputs: irqindex - address of interrupt vector                          *
 *     Notes: exits program if interrupt is invalid.                          *
 ******************************************************************************/

short get_irq_index (short irqnum)
{
    if (irqnum == 2) return 0x0A;
    if (irqnum == 3) return 0x0B;
    if (irqnum == 5) return 0x0D;
    if (irqnum == 0xA) return 0x72;
    if (irqnum == 0xB) return 0x73;
    if (irqnum == 0xC) return 0x74;
    if (irqnum == 0xD) return 0x75;
    if (irqnum == 0xF) return 0x77;

    // Invalid irq if it gets here.
    finish ();
    printf ("invalid IRQ number");
    exit (1);

    return (0);

} // get_irq_index


/******************************************************************************
 * init_vline_isr                                                             *
 *  Function: Initiates the interrupt routine, by chaining it.                *
 *    Inputs: irqnum - interrupt number to chain interrupt function to.       *
 *   Outputs: NONE                                                            *
 ******************************************************************************/

void init_vline_isr (short irqnum)
{
    short temp;
    short i;
    short al;

    // Valid irq_num: 2, 3, 5, 0xA.

    irq_num = irqnum;
    temp = irqnum;
    irq_index = get_irq_index (irq_num);
    irq_mask1 = inp (0x21);
    irq_mask2 = inp (0xA1);
    if (irq_num > 0x08)
    {
        temp -= 0x08;
    } // if
    al = 1;

    // Shift left.

    for (i = 0; i < temp; i++)
    {
        al = al << 1;
    } // for
    if (irq_num < 0x08)
    {
        // Enable interrupt on master 8259 for irq 2, 3, 5.
        int_off ();
        outp (0x21, ((0xFF - al) & irq_mask1));
        int_on ();
    }
    else
    {
        // irq_cascade - enable interrupt on cascade 8259 for irq 10.
        // Note: irq 2 also needs to be enabled.
        int_off ();

        // Enable cascade int 2.
        outp (0x21, (0xFB & irq_mask1));

        // Enable cacade int 10.
        outp (0xA1, ((0xFF - al) & irq_mask2));
        int_on ();
    } // if
    count = 0;
    prev_int = _dos_getvect (irq_index);
    _dos_setvect (irq_index, VlineISR);

    return;

} // init_vline_isr


/******************************************************************************
 * cancel_vline_isr                                                           *
 *  Function: cancels interrupt routine and restores of interrupt vector.     *
 *    Inputs: NONE                                                            *
 *   Outputs: NONE                                                            *
 ******************************************************************************/

void cancel_vline_isr (void)
{
    // Restore old vector interrupt.

    _dos_setvect (irq_index, prev_int);
    int_off ();

    // Restore masks.

    outpw (0x21, irq_mask1);
    outpw (0xA1, irq_mask2);
    int_on ();

    return;

} // cancel_vline_isr
