[comp.graphics] A Hercules Primer

myoung@ingr.UUCP (Mark Young) (01/02/88)

Good News (for me it was good news) !

for a while now, I've watched several requests get battered back and forth
about information on the hercules graphics card.  I kept waiting for some 
kind soul to fill me in on the why and wheres of programming the darn thing.

well, this last week I mentioned it to a friend who steered me to a copy of 
"Micro Cornucopia", issue #39, Jan-Feb 1988.  It has an article in it that 
seemed to cover most of my questions.  

sooo, if you were looking for the info too, look here.  it may answer some of
your questions too!

						...myoung

---
ingr!myoung!myoung@uunet.uu.net       | mark allan young          | where
{uunet,ihnp4}!ingr!myoung!myoung      | intergraph corp, cr1105   | do I
WARNING: all postings from this node  | one madison industrial pk | put the
  are monitored, so I guess I have to | huntsville, al  35807     | usual
    be responsible (hi dave...;-)     | (205) 772-6094            | disclaimer

jsin@CS.UCLA.EDU (01/05/88)

In article <1866@ingr.UUCP> myoung@ingr.UUCP (Mark Young) writes:
>
>well, this last week I mentioned it to a friend who steered me to a copy of 
>"Micro Cornucopia", issue #39, Jan-Feb 1988.  It has an article in it that 
>seemed to cover most of my questions.  
>

I've spent some time typing this into my computer. It seems to work
pretty well with my Herc-compatible clone card. 
If anyone wants a copy, send me e-mail.    


John (Jonghoon) Sin
UCLA SEASnet Facilities   InterNet:   jsin@seas.ucla.edu
2567 Boelter Hall         UUCP:   ...!{ihnp4,ucbvax,{hao!cepu}}!ucla-cs!jsin
Los Angeles, CA. 90025    VoiceNet:   (213) 206-6864

jsin@CS.UCLA.EDU (01/06/88)

Hi Folks!

I've received enough request for this, so I'm posting it on the
net. I also included the Mandelbrot function which appeared in
the same issue of MC. 

I thought of posting this on one of the 'sources' newsgroup, but
decided against it.  Sorry if that causes problem for you.

Anyway, Here it is, enjoy and have fun!

John Sin

------------------------------------------------------------------
/*
 * Graphics Routine for the Hercules Graphics Card
 *
 * Following routines were obtained from the article _A Hercules Primer_,
 * by Larry Fogg. (Micro Cornucopia, Issue No. 39, Jan/Feb 1988,  pp 24-29.)
 *
 * Entered and Modified by John Sin, January 1988.
 *
 *
 *   ---> 6845 Registers <---
 *	
 *   Register		Function	      Settings  in Hex
 *					      (Text)  (Graphics)
 *					
 *  	0	Total Horizontal characters	61	35
 *	1	Displayed horizontal characters 50	2d
 *	2	Horizontal sync position	52	2e
 *	3	Sync width			0f	07
 *	4	Total Vertical rows		19	5b
 *	5	Vertical total adjust		06	02
 *	6	Vertical rows displayed		19	57
 *	7	Vertical sync position		19	57
 *	8	Interlace mode and skew		02	02
 *	9	Maximum scan line address	0d	03
 *	10	Cursor's starting scan line	0b	00
 *	11 	Cursor's ending scan line	0c	00
 *
 *
 *	---> Hercules Card Ports <---
 *
 *      Register 		Function
 *
 *	 3b8		Mode Control
 *	 3ba		Status
 *	 3bf		Configuration
 *	 3b4		CRTC Index Register
 *	 3b5		CRTC Data Register
 *
 */

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

#define boolean char
#define yes     1
#define no      0
#define index   0x3b4		/* 6845 ports */
#define data    0x3b5
#define mode    0x3b8		/* Herc ports */
#define status  0x3ba
#define config  0x3bf

/* char attributes for direct memory screen writes */

const char normal        = 0x07;
const char reverse       = 0x70;
const char under_line    = 0x01;
const char normal_blink  = 0x87;
const char reverse_blink = 0xf0;
const char under_blink   = 0x81;

const int page0       = 0xb000;              /* base address of video page */
const int page1       = 0xb800;
const int text_base   = 0xb000;              /* base address of text pages */
const int max_col     = 719;
const int max_row     = 347;
const int screen_size = 0x800;                           /* bytes per page */

int page_drawn, page_viewed;	      /* current drawing and viewing pages */


void init()                     /* set initial values for global variables */
{
   page_drawn = page0;
   page_viewed = page1;
}

boolean herc_there()                   /* test for presence of Herc card */
{
   int i;

   geninterrupt (0x11);
   if ((_AX & 0x30) == 0x30)	       /* check for mono card presence */
      for (i=0; i<0x800; i++)          /* is it Herc Card? */
         if (inportb(status) & 0x80)   /* watch bit 7 */
            return (yes);
   return (no);
}

void screen_off()
{
   outportb(mode, 2);
}

void set_page_viewed (int page)
{
   if (page == page0)
      outportb (mode, 0x0a);
   else
      outportb (mode, 0x8a);
   page_viewed = page;
}

void set_page_drawn (int page)
{
   page_drawn = page;
}

void clear_gr_scr (int page)
{
   int i;

   if (page_viewed == page)
      screen_off();		/* turn off screen if page is displayed */
   for (i=0; i<0x3fff; i++)
      poke (page, 2*i, 0);      /* reset all bits in page */
   if (page_viewed == page)     /* turn screen on again */
      set_page_viewed (page);
}

void clear_txt_scr ()
{
   int i;

   for (i=0; i<=0x3fff; i++)    /* fill text page with nuls & attribute 7*/
      poke (text_base, 2*i, 0x0700);
}

void scr_write (int X, int Y, char str[81], char attrib)
{		                           /* direct memory write of text */	
   int i, offset;

   i = 0;
   offset = 2 * (80 * Y + X);  /* find memory offset of first char in str */
   while (str[i] != '\0') {                 /* poke each char/attrib pair */
      pokeb (text_base, offset, str[i]);
      pokeb (text_base, ++offset, attrib);
      i++;
      offset++;
   }
}

void set_graphics ()
{
   char i;
   char params[12] = {0x35, 0x2d, 0x2e, 0x07, 0x5b, 0x02,
                      0x57, 0x57, 0x02, 0x03, 0x00, 0x00};
   outportb (config, 3);                     /* allows both graphics pages */
   screen_off ();
   for (i=0; i<12; i++) {           /* program 6845 registers for graphics */
      outportb (index, i);
      outportb (data, params[i]);
   }
   clear_gr_scr (page0);                       /* clear and turn on screen */
   clear_gr_scr (page1);
}

void set_text()
{
   char i;
   char params[12] = {0x61, 0x50, 0x52, 0x0f, 0x19, 0x06,
                      0x19, 0x19, 0x02, 0x0d, 0x0b, 0x0c};
   outportb (config,0);                         /* lock out graphics mode */
   outportb (mode, 0);                  /* set text mode and blank screen */
   for (i=1; i<12; i++) {                        /* program 6845 for text */
      outportb (index, i);
      outportb (data, params[i]);
   }
   clear_txt_scr();
   outportb (mode, 0x28);              /* enable blink and turn on screen */
}

void save_screen (char fname[13], int page)
{                                                        /* write to disk */
   FILE *f;
   char far *screen;

   screen = page*0x10000;                          /* weird, but it works */
   f = fopen (fname, "w");
   fwrite (screen, screen_size, 1, f);
   fclose (f);
}

void get_screen (char fname[13], int page)
{                                                       /* read from disk */
   FILE *f;
   char far *screen;

   screen = page*0x10000;
   f = fopen (fname, "r");
   screen_off();
   fread (screen, screen_size, 1, f);
   set_page_viewed (page);                     /* turn the screen back on */
   fclose (f);
}

void draw_point (int col, int row, boolean fill)
{
   int byte_ofs;      /* offset within page for byte containing the point */
   char mask;                            /* locates point within the byte */
   mask = 1 << (7 - (col % 8));
   byte_ofs = 0x2000 * (row % 4) + 90 * (row/4) + (col/8);
   if (fill)                                            /* draw the point */
      pokeb (page_drawn, byte_ofs, peekb (page_drawn, byte_ofs) | mask);
   else                                                /* erase the point */
      pokeb (page_drawn, byte_ofs, peekb (page_drawn, byte_ofs) & ~mask);
}

void draw_line (int X1, int Y1, int X2, int Y2, boolean on)
{                             /* use Bresenham's algorithm to draw a line */
   boolean pos_slope;
   int dX, dY,                                       /* vector components */
       row, col,
       final,                                  /* final row or col number */
       G,                           /* used to test for new row or column */
       inc1,             /* G increment when row or column doesn't change */
       inc2;                /* G increment when row or column does change */

   dX = X2 - X1;   dY = Y2 - Y1;                 /* find vector component */
   pos_slope = (dX > 0);                            /* is slope positive? */
   if (dY < 0) pos_slope = !pos_slope;
   if (abs(dX) > abs(dY)) {                          /* shallow line case */
      if (dX > 0) {              /* determine start point and last column */
         col = X1; row = Y1; final = X2;
      } else {
         col = X1; row = Y2; final = X1;
      }
      inc1 = 2*abs(dY);             /* determine increments and initial G */
      G = inc1 - abs(dX);
      inc2 = 2 * (abs(dY) - abs(dX));
      if (pos_slope)
         while (col<=final) {     /* step thru cols. checking for new row */
            draw_point (col, row, on);
            col++;
            if (G >= 0) {                     /* it's time to change rows */
               row++;  G+= inc2;      /* positive slope, so inc thru rows */
            } else                                /* stay at the same row */
               G += inc1;
         } /* while */
      else
         while (col<=final) {        /* step thru cols, check for new row */
            draw_point (col, row, on);
            col++;
            if (G > 0) {                       /* time to change the rows */
               row--;  G+= inc2;         /* negative slope, dec thru rows */
            } else
               G += inc1;                         /* stay at the same row */
         } /* while */
   } /* if |dX| > |dY| */  else {
      if (dY > 0) {                 /* steep line case, angle > 45 degree */
         col = X1; row = Y1; final = Y2; /* find start point and last row */
      } else {
         col = X2; row = Y2; final = Y1;
      }
      inc1 = 2 * abs(dX);           /* determine increments and initial G */
      G = inc1 - abs(dY);
      inc2 = 2 * (abs(dX) - abs(dY));
      if (pos_slope)
         while (row <= final) {  /* step thru rows - check for new column */
            draw_point (col, row, on);
            row++;
            if (G >= 0) {                  /* it's time to change columns */
               col++;  G+= inc2;      /* pos. slope, increment thru cols. */
            } else
               G += inc1;                      /* stay at the same column */
         } /* while */
     else
         while (row <= final) {/* step thru rows, checking for new column */
            draw_point (col, row, on);
            row++;
            if (G > 0) {                   /* it's time to change columns */
               col--;  G+= inc2;  /* neg slope, so decrement thru columns */
            } else
               G += inc1;                      /* stay at the same column */
         } /* while */
   } /* if |dY| > |dX| */
}


/**************************************************************************/

/*  Subroutine MANDEL() from the same issue  */

#define sqr(x) (x*x)
#define MAX_ITERATIONS 100
#define MAX_SIZE       4

const int max_colors = 2;

void mandel (float Pmax, float Pmin, float Qmax, float Qmin)
{
  int color, row, col;
  float P, Q, modulus, deltaP, deltaQ, Xcur, Xlast, Ycur, Ylast;

  deltaP = (Pmax - Pmin) / (max_col - 1);
  deltaQ = (Qmax - Qmin) / (max_row - 1);
  for (col = 0; col <= max_col; col++)
     for (row = 0; row <= max_row; row++) {
        P = Pmin + col * deltaP;
        Q = Qmin + row * deltaQ;
        Xlast = Ylast = modulus = 0.0;
        color = 0;
        while ( (modulus < MAX_SIZE) && (color < MAX_ITERATIONS) ) {
           Xcur = sqr (Xlast) - sqr (Ylast) + P;
           Ycur = 2 * Xlast * Ylast + Q;
           color++;
           Xlast = Xcur;
           Ylast = Ycur;
           modulus = sqr (Xcur) + sqr (Ycur);
        }  /* while */
        draw_point (col, row, (color % max_colors));
     }  /* for */
}  /* mandel */

/****************************************************************************/
John (Jonghoon) Sin
UCLA SEASnet Facilities   InterNet:   jsin@seas.ucla.edu
2567 Boelter Hall         UUCP:   ...!{ihnp4,ucbvax,{hao!cepu}}!ucla-cs!jsin
Los Angeles, CA. 90025    VoiceNet:   (213) 206-6864