
/* Copyright 1990 by Gary R. Olhoeft 
 * This code may be freely copied for personal,
 * non-commercial use. compile using MicroWay's 
 * NDP C-386 compiler:
 *   cc graphics.c -w -v -rt2 -n2 -n3 -lGREX.LIB
 */

#include <stdio.h>
#include <dos.h>
#include <grex.h>

void plot_pixel();
void graphics();  
void spectrum();
void ext_palette();

union REGS reg;    /* required by inline assembly */
int v_mode, v_color, v_rowy, v_colx, page;
unsigned short ati_extreg;
char ega_palette[17] = {0,1,9,11,3,19,2,18,6,54,38,
                        52,4,36,41,13,0};
int modes[] = {0x0d,0x0e,0x10,0x12,0x13,0x54,
               0x62,0x63,0x65};
void main()
{
  int i,x,y,key;
  unsigned char color;
  for (i=0; i<9; i++)
    {
      graphics(modes[i]); /* cycle through ATI VGA 
                            Wonder graphics modes */
      printf("Mode %d %dx%dx%d\n",v_mode,v_colx+1,
               v_rowy+1,v_color+1);
      /* printing shrinks as pixel count increases */
      spectrum();
      /* draw banded color lines on screen */
      for (x = 0; x<=v_colx>>2; x++) 
        {
          for (y = 0; y<=v_rowy; y++)
            {
               color = y % v_color;    
               plot_pixel(x+y,y,color);
            }  
        }
      /* pause loop until keypress; 
          exit with ^C or Esc */
      if ((key=pauseb())==27) {
          set_video_mode(0x02); exit();
          }
      }
  set_video_mode(0x02); /* restore text mode */
  exit();
}

void graphics(mode)
unsigned char mode;
{
   unsigned char buffer[2];
   switch (mode) /* note: not all possible modes shown */
     {
       case 0x0d: /* EGA */
          v_colx = 320;
          v_rowy = 200;
          v_color = 16;
          break;
       case 0x0e: /* EGA */
          v_colx = 640;
          v_rowy = 200;
          v_color = 16;
          break;
       case 0x10: /* EGA high resolution */
          v_colx = 640;
          v_rowy = 350;
          v_color = 16;
          break;
       case 0x12: /* VGA */
          v_colx = 640;
          v_rowy = 480;
          v_color = 16;
          break;
       case 0x13: /* MCGA */
          v_colx = 320;
          v_rowy = 200;
          v_color = 256;
          break;
       case 0x54: /* ATI SVGA */  
          v_colx = 800; 
          v_rowy = 600;
          v_color = 16;
          break;
       case 0x62: /* ATI SVGA */  
          v_colx = 640;
          v_rowy = 480;
          v_color = 256;
          break;
       case 0x63: /* ATI SVGA */  
          v_colx = 800;
          v_rowy = 600;
          v_color = 256;
          break;
       case 0x65: /* ATI SVGA */
          v_colx = 1024;
          v_rowy = 768;
          v_color = 16;
          break;
       default:
          mode = 0x02; /* text mode */
          v_colx = 80;
          v_rowy = 25;
          v_color = 1;
          break;
     }
     v_mode = mode;
     v_colx--; /* set ranges 0 to pixels-1 */
     v_rowy--;
     v_color--;

     reg.b.ah = 0;
     reg.b.al = mode;
     int386(0x10,&reg,&reg); /* call BIOS video 
                         interrupt to set mode */

     blk_mb(buffer, 0x34, 786448, 2);
     ati_extreg = buffer[0]+256*buffer[1];
         /* find ATI extended_reg address 
               (could use peek() instead)*/
     page = 99; /* force paging first plot cycle */
 } 

/* plot pixel of color at (x,y) display units */
void plot_pixel(x,y,color) 
int x,y;
unsigned char color;
{
#define peek(addr, val) asm("     push   es"); \
        asm("     mov   ax, 034h"); asm("     mov   \
        es, ax"); ebx = addr; asm(ebx, "     mov   \
        cl, byte ptr es:[ebx]", cl); val = cl; asm\
        ("     pop   es")
#define poke(addr, val) asm("     push   es"); \
        cl = val; asm("     mov   ax, 034h"); \
        asm("     mov   es, ax"); ebx = addr; \
        asm(ebx, cl, "     mov   byte ptr es:[ebx], \
        cl"); asm("     pop   es")
     /* required by inline assembly */
     reg$eax unsigned short ax; 
     reg$eax unsigned char al;
     reg$ah unsigned char ah;
     reg$ecx unsigned short cx;
     reg$ch unsigned char ch;
     reg$ecx unsigned char cl;
     reg$edx unsigned short dx;
     reg$edx unsigned char dl, val;
     reg$ebx unsigned ebx, addr;
     int i,vcol,yvx;
     if ((y < 0) || (y > v_rowy) || (x < 0) || 
        (x > v_colx)) return; 
           /* clip physical display boundaries */
     y = v_rowy-y; /* put (0,0) at lower left corner of plotter */
     vcol = v_colx+1;
     yvx = y*vcol + x;
     switch (v_mode)
       {
         case 0x65: /* ATI 1024x768x16 */
           ch = (char)(y >> 6);
           if (ch!=page) /* only change page if different */
             {
               dx = ati_extreg; /* location of ATI card external register */
               asm("     cli     ");         /* disable interrupts */
               asm("     mov     al,0b2h");  /* page select */
            asm(dx,"     out     dx,al");    /* ATI extended register */
               asm("     inc     dl");
               asm("     in      al,dx");
               asm("     mov     ah,al");
               asm("     and     ah,0e1h");  /* page mask */
            asm(ch,"     or      ah,ch");    /* ch = memory page desired */
               asm("     mov     al,0b2h");  /* page select */
               asm("     dec     dl");
               asm("     out     dx,ax");
               asm("     sti     ");         /* enable interrupts */
               page = ch;
             }
           addr = 655360 + ((y << 9) % 65536) + (x >> 1);
           peek(addr, val); /* read existing color of pixel pair */
           if (x % 2) val = color | (val & 0xF0); /* change addressed pixel */
           else val = (color << 4) | (val & 0x0F);
           poke(addr, val); /* write pixel pair */
           break;
         case 0x13: /* MCGA 320x200x256 */
           addr = 655360 + yvx;
           poke(addr, color); /* write direct to real video memory */
           break;
         case 0x62: /* ATI 640x480x256 */
         case 0x63: /* ATI 800x600x256 */
           ch = (unsigned char)(yvx >> 15); 
           if (ch!=page)
             {
               dx = ati_extreg; /* location of ATI card external register */
               asm("     cli     ");         /* disable interrupts */
               asm("     mov     al,0b2h");  /* page select */
            asm(dx,"     out     dx,al");    /* ATI extended register */
               asm("     inc     dl");
               asm("     in      al,dx");
               asm("     mov     ah,al");
               asm("     and     ah,0e1h");  /* page mask */
            asm(ch,"     or      ah,ch");    /* ch = memory page desired */
               asm("     mov     al,0b2h");  /* page select */
               asm("     dec     dl");
               asm("     out     dx,ax");
               asm("     sti     ");         /* enable interrupts */
               page = ch;
             }
           addr = 655360 + (yvx % 65536);
           poke(addr, color); /* write direct to real video memory */
           break;
         default:
         /* 0x10 EGA 640x350x16, 0x12 VGA 640x480x16, 0x54 ATI 800x600x16 */
            asm("     push   es");       /* save segment register */
            asm("     mov    ax, 034h"); /* use Phar Lap LDT to */
            asm("     mov    es, ax");   /* access real memory */
            dx = 0x3ce;                  /* EGA graphics register */
            ebx= 655360 + (yvx >> 3);    /* memory position of pixel */
        asm(ebx, "     mov    cl, byte ptr es:[ebx]"); /* load EGA registers */
            ax = color << 8;
    asm(dx, ax, "     out    dx, ax");   /* set color */
            ax = 0x0F01;
    asm(dx, ax, "     out    dx, ax");   /* enable */
            ax = 0x0003; /* 0x00 = replace, 0x10 OR, 0x18 XOR, 0x08 AND */
    asm(dx, ax, "     out    dx, ax");   /* pixel write mode */
            ax = ((0x80 >> (x%8)) << 8 ) + 8;
    asm(dx, ax, "     out    dx, ax");   /* bit mask (8 pixels/byte) */
       asm(ebx, "     mov    byte ptr es:[ebx], 255"); /* write EGA regs */
            asm("     pop    es");       /* restore segment register */
       }
}

void spectrum() /* create color spectrum palette */
{
     int i;
     if (v_mode == 0x65) /* 1024x768x16 mode only */
       {
         ext_palette( 0,  0,  0,  0); /* black */
         ext_palette( 1, 63,  0,  0); /* red */
         ext_palette( 2, 63, 21,  0);
         ext_palette( 3, 63, 42,  0);
         ext_palette( 4, 63, 63,  0); /* yellow */
         ext_palette( 5, 42, 63,  0);
         ext_palette( 6, 21, 63,  0);
         ext_palette( 7,  0, 63,  0); /* green */
         ext_palette( 8,  0, 63, 21);
         ext_palette( 9,  0, 63, 42);
         ext_palette(10,  0, 63, 63); /* cyan */
         ext_palette(11,  0, 42, 63);
         ext_palette(12,  0, 21, 63);
         ext_palette(13,  0,  0, 63); /* blue */
         ext_palette(14, 21,  0, 63);
         ext_palette(15, 42,  0, 63); /* magenta */
       }
     else
       {
         if (v_color == 255) /* all 256 color modes */
           {
             vga_palette(0,0,0,0);
             for (i=1; i<=64; i++) { vga_palette(i,63,i-1,0); }
             for (i=65; i<=128; i++) { vga_palette(i,128-i,63,0); }
             for (i=129; i<=192; i++) { vga_palette(i,0,63,i-129); }
             for (i=193; i<=255; i++) { vga_palette(i,0,255-i,63); }
           }
         else
           { /* 16 color EGA modes except 1024x768 */
             set_palette(ega_palette);
           }
       }
}

void ext_palette(i,r,g,b) /* write to external palette registers */
unsigned char i,r,g,b;
{
#define outp(p,v) dx = p; al = v; asm(dx,al,"     out     dx,al")
     reg$eax unsigned char al,v;
     reg$edx unsigned short dx,p;
     outp(0x3C8, i);
     outp(0x3C9, r);
     outp(0x3C9, g);
     outp(0x3C9, b);
     outp(0x3C8, i<<4);
     outp(0x3C9, r);
     outp(0x3C9, g);
     outp(0x3C9, b);
}


