home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_200 / 282_01 / hod.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-01-11  |  22.9 KB  |  868 lines

  1. /*
  2. TITLE:        HODGEPODGE;
  3. DATE:        8/30/88;
  4. DESCRIPTION:    "Makes waves.";
  5. VERSION:    1.01;
  6. KEYWORDS:    HODGEPODGE, Waves;
  7. FILENAME:    HOD.C;
  8. WARNINGS:    "Requires Hercules monochrome graphics. Hard disk recommended.";
  9. SEE-ALSO:    HOD.DOC;
  10. SYSTEM:        MS-DOS;
  11. COMPILERS:    Aztec;
  12. AUTHORS:    Dan Schechter;
  13. REFERENCES:
  14.     AUTHORS:    A.K. Dewdney;
  15.     TITLE:        "Computer Recreations";
  16.     CITATION:    "Scientific American, 259, pp 104-107 (August 1988)."
  17. ENDREF;
  18.  */
  19.  
  20. #define VERSION "Version 1.01 8/30/88"
  21.  
  22. /* Notes to programmers:
  23.  
  24.     This program compiles under Aztec C in large-code/large-data
  25.     model. Some functions are used that may not exist or may have 
  26.     other names in the libraries of other compilers.
  27.     
  28.     scr_getc() causes the program to halt and wait for a key to be
  29.     pressed. It then returns that key. Functions keys and arrow keys
  30.     which DOS returns as a null followed by a character are returned
  31.     as a single character with the high bit set. But that feature
  32.     is not used in this program. It differs from getchar() in that
  33.     it does not wait for the ENTER key to be pressed. Some compilers
  34.     call this function getch().
  35.     
  36.     abstoptr() takes an absolute address in the form of a long
  37.     and returns a long pointer in segment:offset form, which can
  38.     then be used to directly access memory.
  39.     
  40.     time() returns the DOS time and date packed into a long. In
  41.     this program it is used only to seed the random number generator
  42.     and to provide a little extra mixing "exercise" for it. 
  43.     
  44.     clock() returns the number of hundredths of a second since the
  45.     beginning of the present day. In this program it is used only
  46.     to provide setmode() with a one-second pause, needed when
  47.     re-programming the HGA. The return type is a long.
  48.     
  49.     If the argument to either time() or clock() is non-zero, the return
  50.     value will also be placed in the long pointed at by the argument.
  51.     
  52.     bdos() performs INT 21H. AH is set to its first parameter, DX is
  53.     set to its second parameter, and CX is set to its third parameter.
  54.     It returns the value in AL. In this program it is used as an
  55.     inkey() function. inkey() polls the keyboard and returns 0 if
  56.     no key has been pressed, otherwise it returns the value of the
  57.     key that was pressed. It differs from scr_getc() in that inkey()
  58.     does not cause program operation to stop if no key has been pressed.
  59.     
  60.     access() determines the accessibility of a disk file. In this
  61.     program it is only used to check the existance of a file, and as
  62.     called by this program it returns 0 if the named file does not
  63.     exist, and non-zero otherwise.
  64.  
  65.         The first 7 #defines are user-changeable. 
  66.         HEIGHT should not be greater than 80 and WIDTH
  67.         should not be greater than 180, to fit in the
  68.         screen area. Making them smaller will result in
  69.         faster operation. Set MAXFRAME smaller if you have
  70.         limited disk space. If HEIGHT==80 and WIDTH==180
  71.         and MAXFRAME==200 then a full length movie will
  72.         occupy 1440000 bytes of disk space. A good archiving
  73.         program can squeeze that by perhaps half.
  74.         
  75.         K1, K2, and G are the defaults for k1, k2, and g
  76.         respectively. They can be changed at compile time
  77.         or set at run time on the command line. They 
  78.         determine the characteristics of the hodgepodge.
  79.         Likewise for RANDOM, a Kittensoft innovation.
  80.     
  81.     If you compile this program with TABLETEST defined, it will
  82.     display the halftone graphics characters as defined in table.
  83.     It will then wait for you to press any key and then exit.
  84.     Use this feature to view the characters if you want to change
  85.     them.
  86.  
  87.         Exit codes:
  88.             0. Normal termination.
  89.             1. File load failure.
  90.             2. Unrecognized command line option.
  91.             3. Invalid command line parameter value.
  92.  */
  93. #include <string.h>
  94. #include <fcntl.h>
  95.  
  96. #define HEIGHT 80    /* Height of display in 4 X 6 pixel characters. */
  97. #define WIDTH 120    /* Width of display in 4 X 6 pixel characters. */
  98. #define MAXFRAME 200    /* Maximum number of frames in a movie to prevent 
  99.             over-filling your disk. */
  100. #define K1 2        /* Default. May be changed at run time. */
  101. #define K2 3        /* Default. May be changed at run time. */
  102. #define G 25        /* Default. May be changed at run time. */
  103. #define RANDOM 0    /* Default. May be changed at run time. */
  104.  
  105. #define STATENUM 128    /* The number of states. Since the state number
  106.             is divided by 8 to arrive at a display number
  107.             and there are 16 display characters, and since
  108.             disk storage of movies uses one nibble per cell
  109.             STATENUM should not be changed without revising
  110.             the entire display and movie storage schemes. */
  111.  
  112. #define TEXT 0        /* Used for Hercules display. */
  113. #define GRAPHICS 1        /* Used for Hercules display. */
  114. #define INDEXPORT 0x3B4        /* Used for Hercules display. */
  115. #define CONTROLPORT 0x3b8    /* Used for Hercules display. */
  116. #define DATAPORT 0x3b5        /* Used for Hercules display. */
  117. #define CONFIGPORT 0x3bf    /* Used for Hercules display. */
  118. #define G_DELAY 100        /* Used for Hercules display. */
  119. #define FONTADDR 0xffa6e    /* ROM font address. */
  120. #define SCREEN 0xb0000        /* Hercules screen 0 address. */
  121. #define SCREEN1 0xb8000        /* Hercules screen 1 address. */
  122.  
  123. #define HEALTHY 1
  124. #define INFECTED 0
  125. #define ILL -1
  126.  
  127. long time(long *);
  128. void *abstoptr();
  129. int main(int n,char **arg);
  130. void table_init(void);
  131. void screen_put4c(int c1,int c2,int c3, int c4, int v,int h);
  132. int work(unsigned char o[HEIGHT][WIDTH]);
  133. void screenwrite(unsigned char old[HEIGHT][WIDTH],int count);
  134. void sleep(int n);
  135. int rando(void);
  136. int inkey(void);
  137. int infected_neighbors(unsigned char a[HEIGHT][WIDTH],int v,int h);
  138. int ill_neighbors(unsigned char a[HEIGHT][WIDTH],int v,int h);
  139. int n_sum(unsigned char a[HEIGHT][WIDTH],int v,int h);
  140. int health(int a);
  141. void setmode(int q);
  142. int clearscreen(void);
  143. int g_putch(int c,int y);
  144. char *itoa(int n);
  145. int blank(void);
  146. void newname(char *s,int q);
  147. int playback(char *s);
  148. void sw_scr(void);
  149. void g_printf(int v,char *ctrl,...);
  150. int _kdput(char *p,int v);
  151. void erl(void);
  152. int load(unsigned char old[HEIGHT][WIDTH], unsigned char datbuf[HEIGHT*WIDTH], int *count, char *s);
  153. void prompt(void);
  154.  
  155. int gdata[12] = {         /* Constants for programming Hercules card. */
  156.     0x35, 0x2d, 0x2e, 0x7, 0x5b, 
  157.     0x2, 0x57, 0x57, 0x2, 0x3, 0x0, 0x0 
  158. };
  159. int tdata[12] = { 
  160.     0x61, 0x50, 0x52, 0xf, 0x19, 
  161.     0x6, 0x19, 0x19, 0x2, 0xd, 0xb, 0xc 
  162. };
  163. unsigned char table[16][4] = {
  164. /*0*/    { 0,0,0,0 },        /* This table defines the characters        */
  165. /*1*/    { 0,8,0,0 },        /* that will represent the states. Actual   */
  166. /*3*/    { 16,2,8,0 },        /* state numbers are divided by 8,        */
  167. /*4*/    { 8,1,16,2 },        /* requiring 16 display characters.        */
  168. /*6*/    { 40,2,5,16 },        /* Integers in this table must not be        */
  169. /*7*/    { 40,18,5,16 },        /* greater than 63. table_init() will        */
  170. /*9*/    { 36,18,10,37 },    /* shift all these numbers to create tables */
  171. /*10*/    { 42,20,42,17 },    /* 1, 2, 3, and 4 so that                     */
  172. /*12*/    { 42,21,42,21 },    /* characters may be quickly OR'd together  */
  173. /*13*/    { 42,46,42,42 },    /* and written to the display in full       */
  174. /*15*/    { 43,42,43,43 },    /* bytes. You may alter this table        */
  175. /*16*/    { 43,53,45,21 },    /* to change the appearance of the display  */
  176. /*18*/    { 47,53,43,61 },    /* without otherwise affecting program        */
  177. /*20*/    { 47,61,47,61 },    /* operation.                    */
  178. /*22*/    { 63,61,47,63 },
  179. /*24*/    { 63,63,63,63 }
  180. };
  181.  
  182. #define CAT "\n\040\040\040\040\057\134\137\057\134\n\040\040\040\050\376\040\322\040\376\051\n\360\360\360\360\040\304\312\304\040\360\360\360\360\n\040\040\040\040\040\042\042\042"
  183.  
  184. char *HELP = "\
  185. USAGE: HOD [A<k1>] [B<k2>] [G<g>] [M<n>] [I<nn>] [P[<fn>]] [R[<fn>]]\n\
  186. Parameters may be in any order. All are integers.\n\
  187. Defaults: k1=2, k2=3, g=25.\n\n\
  188. <k1> is infected cells divisor.\n\
  189. <k2> is ill cells divisor.\n\
  190. <g> is infection rate.\n\
  191. <nn> is an alternate initializing factor. (1 to number of cells.)\n\
  192. <n> is a randomizing factor. (1 to number of cells.)\n\n\
  193. R tells the program you want to load a file.\n\
  194. P tells the program you want to play back a movie.\n\
  195. <fn> is a file name to load or play back.\n\n\
  196. During program operation:\n\
  197. # (crosshatch) Toggles movie recording.\n\
  198. $ to set movie stop frame.\n\
  199. ^W Writes the current data to a file.\n\
  200. ^D Writes the data to a file and then exits.\n\
  201. ^R Reads data from a file.\n\
  202. ^B Toggles screen blanking.\n\
  203. ^S Pause.\n\
  204. ^C Exits from the program.";
  205.  
  206. char *PRESS_ANY_KEY = "\
  207.                                                PRESS ANY KEY...";
  208.  
  209. unsigned char table1[16][4],table2[16][4],table3[16][4],table4[16][4],table5[16][4];
  210. int k1=K1, k2=K2, g=G, random=RANDOM;
  211. int g_th,movie,stopframe;
  212. unsigned char *font, *screen, *screen1, *st;
  213.  
  214. int main(n,arg)
  215. int n;
  216. char **arg;
  217. {
  218.     int i,j,count=0,quit=0;
  219.     unsigned char old[HEIGHT][WIDTH],dat;
  220.     unsigned char datbuf[HEIGHT*WIDTH];
  221.     int fd,mfd,datcount,frame;
  222.     char fn[250];
  223.     
  224.     table_init();
  225. #ifdef TABLETEST
  226.     for (i=0;i<WIDTH;i++) for (j=0;j<HEIGHT;j++) old[j][i]=0;
  227.     for (j=0;j<HEIGHT;j++) old[j][0]=STATENUM-1;
  228.     for (j=0;j<16;j++){
  229.         old[j*4][2]=j*8;
  230.         for(i=10;i<20;i++) 
  231.             old[j*4][i]=old[j*4+1][i]=old[j*4+2][i]=j*8;
  232.     }
  233. #else
  234.     for (i=0;i<WIDTH;i++) for (j=0;j<HEIGHT;j++) 
  235.         old[j][i]=rando()%STATENUM;
  236. #endif
  237.     st=screen=abstoptr(SCREEN);
  238.     screen1=abstoptr(SCREEN1);
  239.     font=abstoptr(FONTADDR);
  240.     while (n>1) {
  241.         n--;
  242.         switch(tlr(arg[n][0])){
  243.             case 'a':
  244.                 k1= atoi(arg[n]+1);
  245.                 break;
  246.             case 'b':
  247.                 k2= atoi(arg[n]+1);
  248.                 break;
  249.             case 'g':
  250.                 g= atoi(arg[n]+1);
  251.                 break;
  252.             case 'm':
  253.                 random= atoi(arg[n]+1);
  254.                 break;
  255.             case 'r':
  256.                 if (load(old,datbuf,&count,arg[n]+1)==-1){
  257.                     puts("Load failed.");
  258.                     exitt(1);
  259.                 }
  260.                 break;
  261.             case 'i':
  262.                 for (i=0;i<WIDTH;i++) for (j=0;j<HEIGHT;j++) 
  263.                     old[j][i]=0;
  264.                 i= atoi(arg[n]+1);
  265.                 while(i--){
  266.                     j= rando()%HEIGHT;
  267.                     i= rando()%WIDTH;
  268.                     old[j][i]=rando()%STATENUM;
  269.                 }
  270.                 break;
  271.             case 'p':
  272.                 exitt (playback(arg[n]+1));
  273.             default:
  274.                 puts(HELP);
  275.                 exit(2);
  276.         }
  277.     }
  278.     if ((k1<=0)||(k2<=0)||(g<0)||(random<0)) {
  279.         puts(HELP);
  280.         exit(3);
  281.     }
  282.     setmode(GRAPHICS);
  283.     prompt();
  284.     for(screenwrite(old,count);;){
  285.         switch (work(old)){
  286.             case '?':
  287.                   setmode(TEXT);
  288.                   puts(HELP);
  289.                   puts(PRESS_ANY_KEY);
  290.                   scr_getc();
  291.                   setmode(GRAPHICS);
  292.                   prompt();
  293.                   continue;
  294.             case '#':
  295.                   movie ^= 1;
  296.                   if (!movie) close(mfd);
  297.                   else {
  298.                       frame=0;
  299.                       newname(fn,'m');
  300.                     if ((mfd=open(fn,O_WRONLY|O_TRUNC))==-1)
  301.                           movie=0;
  302.                       else {
  303.                         write(mfd,&count,sizeof(int));
  304.                         write(mfd,&k1,sizeof(int));
  305.                         write(mfd,&k2,sizeof(int));
  306.                         write(mfd,&g ,sizeof(int));
  307.                         write(mfd,&random,sizeof(int));
  308.                     }
  309.                   }
  310.                   continue;  
  311.             case '$':
  312.                   setmode(TEXT);
  313.                   puts("Enter movie stop frame:");
  314.                   gets(fn);
  315.                   i= atoi(fn);
  316.                   if (i>frame) stopframe=i;
  317.                   setmode(GRAPHICS);
  318.                   prompt();
  319.                   continue;
  320.     /* ^R */    case 18:
  321.                   setmode(TEXT);
  322.                   if (load(old,datbuf,&count,"")==-1){
  323.                       puts("Load failed. Press any key.");
  324.                       if (scr_getc()==3) exitt(1);
  325.                   }
  326.                   setmode(GRAPHICS);
  327.                   prompt();
  328.                   continue; 
  329.     /* ^D */    case 4:
  330.                   quit=2;
  331.     /* ^W */    case 23:
  332.                   newname(fn,0);
  333.                   if ((fd=open(fn,O_WRONLY | O_TRUNC))!=-1){
  334.                       for(i=datcount=0;i<WIDTH;i++)
  335.                           for(j=0;j<HEIGHT;j++)
  336.                               datbuf[datcount++]=old[j][i];
  337.                       write(fd,&count,sizeof(int));
  338.                       write(fd,&k1,sizeof(int));
  339.                       write(fd,&k2,sizeof(int));
  340.                       write(fd,&g,sizeof(int));
  341.                       write(fd,&random,sizeof(int));
  342.                       write(fd,datbuf,HEIGHT*WIDTH);
  343.                       close(fd);
  344.                       g_printf(340,"  Data saved. ");
  345.                   } 
  346.                   else {
  347.                       g_printf(340,"  File failure. Data not saved. ");
  348.                       quit=0;
  349.                   }
  350.                   if (quit) break;
  351.                   continue;
  352.     /* ^C */    case 3:
  353.                   quit=1;
  354.                   break;
  355.             case 0:
  356.                   break;
  357.             default:
  358.                   continue;
  359.         }
  360.         if (quit) break;
  361.         screenwrite(old,++count);
  362.         if (movie) {
  363.             for(i=datcount=0;i<WIDTH;i+=4) for(j=0;j<HEIGHT;j++) {
  364.     datbuf[datcount++]=((old[j][i]>>3)<<4) | (old[j][i+1]>>3); 
  365.     datbuf[datcount++]=((old[j][i+2]>>3)<<4) | (old[j][i+3]>>3); 
  366.             }
  367.             if ((write(mfd,datbuf,WIDTH*HEIGHT/2))!=WIDTH*HEIGHT/2) {
  368.                 close(mfd);
  369.                 movie=g_th=0;
  370.                 g_printf(328,"Movie file write error or disk full. ");
  371.             }
  372.             else {
  373.                 g_printf(340,"Movie frame %d ",frame++);
  374.                 if ((frame==MAXFRAME)||(frame==stopframe)){
  375.                     close(mfd);
  376.                     movie=0;
  377.                 }
  378.             }
  379.         } 
  380.     }
  381.     setmode(TEXT);
  382.     if (quit==2) puts("Data saved.\n");
  383.     exitt(0);
  384. }
  385. void table_init()
  386. {
  387.     int i,j;
  388.     
  389.     for (i=0;i<16;i++) for(j=0;j<4;j++) {
  390.         table1[i][j]= table[i][j]<<2;
  391.         table2[i][j]= table[i][j]>>4;
  392.         table3[i][j]= table[i][j]<<4;
  393.         table4[i][j]= table[i][j]>>2;
  394.         table5[i][j]= table[i][j]<<6;
  395.     }
  396. }
  397.  
  398. int work(o)
  399. unsigned char o[HEIGHT][WIDTH];
  400. {
  401.     int i,j,k;
  402.     unsigned char n[HEIGHT][WIDTH];
  403.     
  404.     for (i=0;i<WIDTH;i++){
  405.         if (!(i&7)) {
  406.             g_putch('.',340);
  407.             switch (k=inkey()) {
  408.     /* ^S */        case 19:
  409.                     g_printf(340,"  Paused... ");
  410.                     scr_getc();
  411.                     break;
  412.     /* ^B */        case 2:
  413.                     blank();
  414.                     break;
  415.                 case 0:
  416.                     break;
  417.                 default:
  418.                     return k;
  419.             }
  420.         }
  421.         for (j=0;j<HEIGHT;j++)
  422.             switch (health(o[j][i])){
  423.                 case HEALTHY:
  424.                     k= infected_neighbors(o,j,i)/k1 + ill_neighbors(o,j,i)/k2;
  425.                     n[j][i]=k;
  426.                     break;
  427.                 case INFECTED:
  428.                     k= (infected_neighbors(o,j,i)+ill_neighbors(o,j,i));
  429.                     if (k) k=n_sum(o,j,i)/k +g;
  430.                     else k=10000;
  431.                     n[j][i]= (k>=STATENUM)? STATENUM-1 : k;
  432.                     break;
  433.                 case ILL:
  434.                     n[j][i]=0;
  435.                     break;
  436.             }
  437.     }
  438.     if (random){
  439.         for(k= (int)(time((long *)0)&7L)   ;k;k--)
  440.             rando();    /* Exercise the random number generator. */
  441.         for(k=random;k;k--){
  442.             j= rando()%HEIGHT;
  443.             i= rando()%WIDTH;
  444.             n[j][i]=rando()%STATENUM;
  445.         }
  446.     }
  447.     memcpy(o,n,HEIGHT*WIDTH);
  448.     return 0;
  449. }
  450. void screenwrite(old,count)
  451. unsigned char old[HEIGHT][WIDTH];
  452. int count;
  453. {
  454.     int i,j;
  455.     
  456.     for (i=0;i<WIDTH;i+=4) for (j=0;j<HEIGHT;j++) 
  457.         screen_put4c(old[j][i]>>3,old[j][i+1]>>3,old[j][i+2]>>3,old[j][i+3]>>3,j*4,i/4);
  458. #ifdef TABLETEST
  459.     scr_getc();
  460.     setmode(TEXT);
  461.     exit(0);
  462. #endif
  463.     g_printf(340,"\rTime=%d ",count);
  464. }
  465. void sleep(n)
  466. int n;
  467. {
  468.     long clock(),a;
  469.     
  470.     a=clock();
  471.     while( clock()-a< n)
  472.         ;
  473. }
  474. int rando()        /* This is a quick-and-dirty but acceptable
  475.             pseudo-random generator for non-critical use. 
  476.             Be wary of using it where truely random
  477.             numbers are needed. */
  478. {
  479.     static long zi;
  480.     static int mark=1;
  481.     
  482.     if (mark) {
  483.         mark=0;
  484.         zi = time((long *)0);
  485.         zi = zi & 16383;
  486.     }
  487.     zi*=23;
  488.     zi+=17;
  489.     zi%=32749;
  490.     return((int)zi);
  491. }
  492. int inkey()
  493. {
  494.     
  495.     return(bdos(0x600,0xff,0));
  496. }
  497. int infected_neighbors(a,v,h)
  498. int v,h;
  499. unsigned char a[HEIGHT][WIDTH];
  500. {
  501.     int i,j,n=0;
  502.     
  503.     for(i=h-1;i<=h+1;i++){
  504.         if ((i<0)||(i>=WIDTH)) continue;
  505.         for (j=v-1;j<=v+1;j++){
  506.             if ((i==h)&&(j==v)) continue;
  507.             if ((j<0)||(j>=HEIGHT)) continue;
  508.             if ((a[j][i]>0)&&(a[j][i]!=STATENUM-1)) n++;
  509.         }
  510.     }
  511.     return n;
  512. }
  513. int ill_neighbors(a,v,h)
  514. int v,h;
  515. unsigned char a[HEIGHT][WIDTH];
  516. {
  517.     int i,j,n=0;
  518.     
  519.     for(i=h-1;i<=h+1;i++){
  520.         if ((i<0)||(i>=WIDTH)) continue;
  521.         for (j=v-1;j<=v+1;j++){
  522.             if ((i==h)&&(j==v)) continue;
  523.             if ((j<0)||(j>=HEIGHT)) continue;
  524.             if (a[j][i]==STATENUM-1) n++;
  525.         }
  526.     }
  527.     return n;
  528. }
  529.  
  530. int n_sum(a,v,h)
  531. int v,h;
  532. unsigned char a[HEIGHT][WIDTH];
  533. {
  534.     int i,j,n=0;
  535.     
  536.     for(i=h-1;i<=h+1;i++){
  537.         if ((i<0)||(i>=WIDTH)) continue;
  538.         for (j=v-1;j<=v+1;j++){
  539.             if ((j<0)||(j>=HEIGHT)) continue;
  540.             n += a[j][i];
  541.         }
  542.     }
  543.     return n;
  544. }
  545. int health(a)
  546. int a;
  547. {
  548.     if (a==0) return HEALTHY;
  549.     if (a==STATENUM-1) return ILL;
  550.     return INFECTED;
  551. }
  552. char *itoa(n)
  553. int n;
  554. {
  555.     static char s[25];
  556.     int i=0;
  557.     
  558.     s[i++]= n/10000+48;
  559.     s[i++]= (n/1000)%10+48;
  560.     s[i++]= (n/100)%10+48;
  561.     s[i++]= (n/10)%10+48;
  562.     s[i++]= n%10+48;
  563.     s[i]=0;
  564.     return s;
  565. }
  566. void newname(s,q)
  567. char *s;
  568. int q;
  569. {
  570.     int fnum,t;
  571.     
  572.     t=g_th;
  573.     g_printf(17,"\r                         ");
  574.     g_printf(33,"\r                         ");
  575.     g_printf(25,"\r   Save to what drive?   ");
  576.     g_th-=2;
  577.     if (q=='m') strcpy(s,"X:\\HOD..MAT");
  578.     else strcpy(s,"X:\\HOD..DAT");
  579.     s[0]=g_putch(scr_getc(),25);
  580.     for (fnum='A';fnum<='Z';fnum++){
  581.         s[6]=fnum;
  582.         if (access(s,0)) break;
  583.     }
  584.     g_th=t;
  585. }
  586. int playback(s)
  587. char *s;
  588. {
  589.     int i,j,fd,count,frame=0,bufcount;
  590.     unsigned char mbuf[HEIGHT*WIDTH/2];
  591.     char fn[250];
  592.     
  593.     if (!s[0]) {
  594.         puts("Playback file name?");
  595.         gets(fn);
  596.     }
  597.     else strcpy(fn,s);
  598.     
  599.     if (!strchr(fn,'.')) strcat(fn,".MAT");
  600.     if ((fd=open(fn,O_RDONLY))==-1) return 2;
  601.     
  602.     setmode(GRAPHICS);
  603.     read(fd,&count,sizeof(int));
  604.     read(fd,&k1,sizeof(int));
  605.     read(fd,&k2,sizeof(int));
  606.     read(fd,&g ,sizeof(int));
  607.     read(fd,&random,sizeof(int));
  608.     sw_scr();
  609.     prompt();
  610.     sw_scr();
  611.     prompt();
  612.     while (read(fd,mbuf,HEIGHT*WIDTH/2)==HEIGHT*WIDTH/2){
  613.         sw_scr();
  614.         switch (inkey()){
  615.     /* ^C */    case 3:
  616.                 setmode(TEXT);
  617.                 return 0;
  618.     /* ^S */    case 19:
  619.                 scr_getc();
  620.                 break;
  621.             case '?':
  622.                 setmode(TEXT);
  623.                 puts(HELP);
  624.                 puts(PRESS_ANY_KEY);
  625.                 scr_getc();
  626.                 setmode(GRAPHICS);
  627.                 break;
  628.             case 0:    
  629.                 break;
  630.         }
  631.         for (i=bufcount=0;i<WIDTH/4;i++) 
  632.             for (j=0;j<HEIGHT;j++,bufcount+=2) 
  633.     screen_put4c(mbuf[bufcount]>>4,mbuf[bufcount]&15,mbuf[bufcount+1]>>4,mbuf[bufcount+1]&15,j*4,i);
  634.         g_th=0;
  635.         g_printf(340,"Time=%d    Movie frame=%d         ",count++,frame++);
  636.     }
  637.     setmode(TEXT);
  638.     return 0;
  639. }
  640.  
  641. /* The following extremely abridged version of printf() lets me use
  642. the %d conversion without all the overhead of the real thing. */
  643.  
  644. void g_printf(v,ctrl,parml)
  645. char *ctrl;
  646. int v;
  647. unsigned parml;
  648. {
  649.     char *p;
  650.     
  651.     p= (char *)(&parml);
  652.     while (*ctrl) {
  653.         if ((*ctrl)=='%') {
  654.             ctrl+=2;
  655.             _kdput(p,v);
  656.             p += sizeof(int);
  657.             continue;
  658.         }
  659.         if (*ctrl=='\r'){
  660.             g_th=0;
  661.             erl(); 
  662.             ctrl++;
  663.             continue;
  664.         }
  665.         g_putch( *(ctrl++),v);
  666.     }
  667. }
  668. _kdput(p,v)
  669. char *p;
  670. int v;
  671. {
  672.     int t,n,mask;
  673.     
  674.     n= *((int *)p);
  675.     if (n<0){
  676.         g_putch('-',v);
  677.         n = -n;
  678.     }
  679.     for(mask = 10000; mask!=1; mask/=10)
  680.         if (mask<=n) break;
  681.     for(;mask;mask/=10){
  682.         t = n/mask;
  683.         n -= t*mask;
  684.         g_putch(t+48,v);
  685.     }
  686. }
  687. int load(old,datbuf,count,s)
  688. unsigned char old[HEIGHT][WIDTH], datbuf[HEIGHT*WIDTH];
  689. int *count;
  690. char *s;
  691. {
  692.     int i,j,datcount,fd;
  693.     char fn[250];
  694.     
  695.     if (!s[0]) {
  696.         puts("Load file name?");
  697.         gets(fn);
  698.     }
  699.     else strcpy(fn,s);
  700.     if (!strchr(fn,'.')) strcat(fn,".DAT");
  701.     if ((fn[0])&&((fd=open(fn,O_RDONLY))==-1)) return -1;
  702.     else {
  703.         read(fd,count,sizeof(int));
  704.         read(fd,&k1,sizeof(int));
  705.         read(fd,&k2,sizeof(int));
  706.         read(fd,&g,sizeof(int));
  707.         read(fd,&random,sizeof(int));
  708.         read(fd,datbuf,HEIGHT*WIDTH);
  709.         close(fd);
  710.         for(i=datcount=0;i<WIDTH;i++)
  711.             for(j=0;j<HEIGHT;j++)
  712.                 old[j][i]=datbuf[datcount++];
  713.     }
  714. }
  715. exitt(e)
  716. int e;
  717. {
  718.     puts("\
  719. The Hodgepodge machine\n\
  720. from Scientific American August 1988, p. 104, by A.K. Dewdney.\n\n\
  721. This version by Dan Schechter.\n\
  722. Public domain software from Kittensoft. " VERSION CAT);
  723.  
  724.     exit(e);
  725. }
  726. int tlr(c)
  727. int c;
  728. {
  729.     if (c>='A') c |= 32;
  730.     return c;
  731. }
  732. void prompt()
  733. {
  734.     g_printf(328,"\rPress ^C to exit. Press ? for help.   k1=%d k2=%d g=%d",k1,k2,g); 
  735.     if (stopframe) g_printf(328," s=%d",stopframe);
  736.     if (random) g_printf(328," r=%d",random);
  737. }
  738.  
  739. /* All the rest of the functions are Hercules-dependent. */
  740.  
  741. /* The following function erases the bottom line by writing directly to
  742. screen memory. It affects only page 0. It is not elegant, but it is
  743. faster than using g_printf() with a long line of spaces. */
  744.  
  745. void erl()
  746. {
  747.     int i;
  748.     static unsigned off[8] = {
  749.         0x1de2,0x3de2,0x5de2,0x7de2,0x1e3c,0x3e3c,0x5e3c,0x7e3c };
  750.     
  751.     for(i=0;i<8;i++) memset(screen+off[i],0,90);
  752. }
  753.  
  754. /* The following function simultaneously switches the screen that is
  755. on display and the screen that is being written to by all the
  756. direct-access screen functions. It is used only by the playback() function
  757. to give an effect more like animation by assuring that the screen being
  758. written to is not the one being viewed. */
  759.  
  760. void sw_scr()
  761. {
  762.     static int scr_num;
  763.     
  764.     scr_num^=128;
  765.     outportb(CONTROLPORT, 10 | scr_num);
  766.     if (scr_num) screen=st;
  767.     else screen=screen1;
  768. }
  769.  
  770. /* The following function toggles the screen display on and off. I like
  771. to allow the user to blank the screen so as to avoid phosphor burn. */
  772.  
  773. blank()
  774. {
  775.     static blank=0;
  776.     
  777.     blank ^= 1;
  778.     if (blank) outportb(CONTROLPORT,2);
  779.     else outportb(CONTROLPORT,10);
  780. }
  781.  
  782. /* The following function sets the HGA to graphics or text mode, as needed. */
  783.  
  784. void setmode(q)
  785. int q;
  786. {
  787.     int i;
  788.     
  789.     switch (q) {
  790.         case GRAPHICS:
  791.                 outportb(CONFIGPORT,3);
  792.                 outportb(CONTROLPORT,2);
  793.                 for(i=0;i<12;i++){
  794.                     outportb(INDEXPORT,i);
  795.                     outportb(DATAPORT,gdata[i]);
  796.                 }
  797.                 sleep(G_DELAY);
  798.                 clearscreen();  
  799.                 outportb(CONTROLPORT,10);
  800.                 break;
  801.         case TEXT:
  802.                 outportb(CONTROLPORT,0);
  803.                 for(i=0;i<12;i++){
  804.                     outportb(INDEXPORT,i);
  805.                     outportb(DATAPORT,tdata[i]);
  806.                 }
  807.                 sleep(G_DELAY);
  808.                 scr_clear();
  809.                 outportb(CONTROLPORT,40);
  810.                 outportb(CONFIGPORT,0);
  811.                 break;
  812.     }
  813. }
  814.  
  815. /* The following function clears the two Hercules graphics pages. */
  816.  
  817. clearscreen()
  818. {
  819.     memset(screen,0,(unsigned)32768);
  820.     memset(screen1,0,(unsigned)32768);
  821. }
  822.  
  823. /* The following function writes a character to the graphics screen.
  824. It uses the IBM-ROM character set. The horizontal character position
  825. is set by the global variable g_th. y is the vertical scan line of the
  826. top of the character. c is the character to be written. */
  827.  
  828. int g_putch(c,y)
  829. int c,y;
  830. {
  831.     int i;
  832.     
  833.     for (i=0;i<8;i++,y++)
  834.         screen[( 0x2000 * (y&3) + 90*(y>>2) + g_th)] = font[c*8+i];
  835.     g_th++;
  836.     return(c);
  837.     
  838. }
  839.  
  840. /* The following function writes 4 half-tone graphics characters to screen
  841. memory. The actual character set is defined in the array named table at
  842. the beginning of the program. table1, table2, table3, table4, and table5
  843. are created in table_init() by appropriate bit-shifting. Since the characters
  844. are 4 pixels high and 6 pixels wide, to achieve a square aspect ratio
  845. on the screen, the only efficient way to get them to the screen, as far as I
  846. know, is to OR four of them into 3 bytes and write the 3 bytes to memory.
  847. the table arrays are used to gain speed. The constant 0x1ffe in the for()
  848. loop moves the offset to the next scan line. The HGA mixes scan lines in
  849. memory so that line 0 is immediately followed by line 4, which is followed 
  850. by line 8, etc., and the beginning of line 1 is 0x2000 bytes above the
  851. beginning of line 0, and line 2 is 0x2000 bytes above line 1, etc. I think
  852. this is because the HGA actually directs the cathode ray to the scan lines
  853. in the order 0, 4, 8, 12, ... to the bottom of the screen, then lines
  854. 1, 5, 9, 13, ... then 2, 6, 10, 14, ... etc. */
  855.  
  856. void screen_put4c(c1,c2,c3,c4,v,h)
  857. int c1,c2,c3,c4,v,h;
  858. {
  859.     int i,off;
  860.     
  861.     off= 0x2000 * (v&3) + 90*(v>>2) + h*3;
  862.     for(i=0;i<4;i++,off += 0x1ffe){
  863.         screen[off++]= table1[c1][i] | table2[c2][i];
  864.         screen[off++]= table3[c2][i] | table4[c3][i];
  865.         screen[off]= table5[c3][i] | table[c4][i];
  866.     }
  867. }
  868.