[comp.sys.ibm.pc] 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

awylie@pyr1.cs.ucl.ac.uk (01/18/88)

This posting is appalling. Your routines contain some horrendous bugs.
It will not even compile under Microsoft-C. Initialization of automatic
arrays!!! What broken compiler accpets this??? Even if you did somehow
compile it, several of the routines could never work -
  set_text has incorrect loop index : try to go from graphics mode
           back to text and KABLAM REBOOT TIME
  get_screen and save_screen do an illegal trick to convert the screen
           address to a long pointer ("weird" is your comment) but my
           compiler complains that the pointer is converted to short
           again when the memcpy (or memmove, I forget) routine is called
           to do the dirty work.
            These two routines also do not bother checking that fopen has
          been successful.

There are other horrors in there which I will not mention. Stylistically
the whole thing is a mess too, eg capital letters in non-preprocessor
variables.

Anyone who would like to get a working version of this mess, with also a
routine to display text whilst in graphics mode, send me an e-mail.
------------------------------------------------------------
Andrew Wylie, University of London Computer Centre,
20 Guilford Street, London WC1N 1DZ, England.

JANET:      andrew@uk.ac.ulcc.ncdlab
UUCP:              ..!mcvax!ukc!cs.ucl.ac.uk!awylie
ARPA:              awylie@cs.ucl.ac.uk
BITNET:     andrew%uk.ac.ulcc.ncdlab@ac.uk

If replying via satnet please give a UUCP path from mcvax to
your site.

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

In article <39500005@pyr1.cs.ucl.ac.uk> awylie@pyr1.cs.ucl.ac.uk writes:
>
>This posting is appalling. Your routines contain some horrendous bugs.
>It will not even compile under Microsoft-C. Initialization of automatic

It was probably my fault not mentioning that it was written for the
Turbo C compiler version 1.0.  I assumed everyone knew from previous
postings that it was from a magazine article, and that only thing I did
was put it on my floppy.

>There are other horrors in there which I will not mention. Stylistically
>the whole thing is a mess too, eg capital letters in non-preprocessor
>variables.
 
You may want to mention this to Larry Fogg yourself, by writing to

MICRO CORNUCOPIA
P.O. BOX 223
BEND, OREGON 97709

>
>Anyone who would like to get a working version of this mess, with also a
>routine to display text whilst in graphics mode, send me an e-mail.

I think the Article was pretty good beginning point for people 
to learn about the innards of the Hercules card. I knew as I typed the 
program that I would modify it extensively, if I was going to write a program
using those routines. However, since I had no idea before how the
Hercules board works, I was pretty happy with it as is.

Also, I'd like to clarify that I NEVER type in the actual
article on my PC. I apologize if I didn't make myself clear.

Send further flames to > /dev/null  or pipe it to MicroCornucopia Inc.

John Sin

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

broehl@watdcsu.waterloo.edu (Bernie Roehl) (01/26/88)

I found the posting quoted below so obnoxious I felt I should say something
about it:

In article <39500005@pyr1.cs.ucl.ac.uk> awylie@pyr1.cs.ucl.ac.uk writes:
>This posting is appalling. Your routines contain some horrendous bugs.

Not having seen the posting awylie@pyr1 is referring to, I can't argue
about whether it has bugs or not.
However, anything posted to the net is posted "as-is".  I'm sure that
the posting in question is neither the first nor the last to have some
bugs in it.

>It will not even compile under Microsoft-C. Initialization of automatic
>arrays!!! What broken compiler accpets this???

A great many C compilers that I have used support this feature.  The fact
that Microsoft lacks it is unfortunate, but is certainly not the fault of
the original poster.

>There are other horrors in there which I will not mention.

What "horrors" are these, pray tell?  And why *not* mention them, since the
person who originally posted the source would doubtless like to know about
them?  (and more importantly, has a *right* to know about them; see below)

>Stylistically the whole thing is a mess too, eg capital letters in
>non-preprocessor variables.

To each their own coding style.

>Anyone who would like to get a working version of this mess, with also a
>routine to display text whilst in graphics mode, send me an e-mail.

This is far and away the most irresponsible thing I've seen anyone do on
the net in many, many years.  Take source posted by someone else, publicly
attack them because you don't like the way they code, and
then redistribute their program so you can take credit for "fixing" it.

Yuck.

>------------------------------------------------------------
>Andrew Wylie, University of London Computer Centre,
>20 Guilford Street, London WC1N 1DZ, England.

Is this sort of behaviour *sanctioned* by the U of London Computing Centre?


-- 
	Bernie Roehl, University of Waterloo Electrical Engineering Dept
	Mail: broehl@watdcsu.waterloo{.edu,.csnet,.cdn}
	BangPath: {allegra,decvax,utzoo,clyde}!watmath!watdcsu!broehl
	Voice:  (519) 745-4419 [home]  (519) 885-1211 x 2607 [work]