/* This program is sort of a lava-lamp for your computer. It requires VGA equipment or anything else that is compatible with mode 13h of the VGA (320x200x256 mode). To run the program, execute the command ooze The first time this is run, it will generate a random, amorphous image on the screen and will store this image into the file ooze.ooz. The image on the screen will then begin to "ooze" all over the screen. To change the direction of oozing, press a key (like the space bar). To stop the program and return to DOS, press the Escape key. The next time ooze is run, it will read the file ooze.ooz and proceede directly to ooze the image stored there. Ooze.ooz is mearly the default filename. Any other filename may be specified on the command line. If, for instance, the command ooze image1.ooz is issued, the file image1.ooz will be read from disk (if it is present) and the image from this file will be oozed. If image1.ooze is not found, it will be created in the same manner as ooze.ooz was created above. But the image created will be different for every new ooze image file created. You could build a whole library of differnet ooze files! ---------------------- For Programmers Only ------------------------- This program was translated to Turbo C from Turbo Pascal. The original TP code was written and copyrighted (1988) by Bret Bulvey [71330,3567] and can be found in various places as PLASMA.ARC (or .ZIP depending on where you get it). The translation to TC was done by me (Jeff Clough [71330,2227]) and includes improvements and enhancements over the TP code. These include the elimination of flicker, the use of secondary as well as primary colors in the color palette, the use of 252 colors instead of only the 191 colors used in PLASMA, the ability to reverse the direction in which the colors move on the screen while the program is executing, and being able to specify the name of the image file to use on the command line. Also, as a side-effect of the process by which flicker is avoided, OOZE should run at the same speed on all VGA equipment regardless of the speed of the computer. The only unique part of the original code presented here (aside from a few variable and type names) is the algorithm for generating the amorphous images. The algorithm has been modified (simplified, actually) so that it includes no floating point. The original algorithm employed the use of Manhattan distances to determine how far apart two pixels were. The code presented here employes its own square root function (using integer bisection) and uses this to take advantage of a little trick Pathagorus demonstrated a while back. It is hoped that this new method for computing distances will produce more rounded edges in the generated images. Also, there was provision in the original code for adjusting the "roughness of the image" at compile time. This has been removed (since it involved a floating point operation). To eliminate the flicker, it was necessary to implement parts of the program in assembly language. The code that follows is, therefore, a hybrid of Turbo C and Assembly. Turbo C 2.0 and TASM were used to compile/assemble this program. To recompile, do tcc -B -mc ooze This will cause TC to "compile" to assembly source code and then invoke TASM on that code. The -mc switch may optionally be changed to -ml or -mh for the large or huge memory models, but may not be changed to -mt, -ms, or -mm because the tiny, small, and medium memory models all use near data segments. The far data segments used by the other memory models are necessary because this program uses the fread and fwrite functions to read from and write to the video memory directly. If the identifier "dotest" is defined during compilation, a test pattern will be generated instead of the more usual amorphous images when the program is run. This test code was used during debugging to ensure that all colors were being represented on the screen. If you change any part of this source code, you may want to use this test pattern to verify that your changes meet your expectations of them. To tell the compiler to generate the test code, include the -Ddotest command line switch when you compile as follows: tcc -B -mc -Ddotest ooze When ooze.exe is run, the test pattern will be generated and stored into whatever file is passed to it on the command line or to ooze.ooz by default. Don't forget to recompile without the -Ddotest switch so that the program will once again generate amorphous images. */ #if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__) #error COMPILE WITH LARGE DATA MODEL. #endif #include #include #include #include #include #define VERSION "1.1" typedef struct {unsigned char r,g,b; } colortype; colortype p[256]; int direction=0; /* controls the direction of palette rotation */ void adjust(int x1,int y1,int x,int y,int x2,int y2); int getpixel(int x,int y); void putpixel(int x,int y,int c); void rotatepalette(colortype p[]); void setvgapalette(void *buffer,int first,int length); void subdivide(int x1,int y1,int x2,int y2); unsigned sqrt(long n); void test(void); void usage(void); int main(int argc,char *argv[]) {FILE *file; char filename[129]; union REGS reg; unsigned char r,g,b; int i, /* holds the number of the current palette register */ key, /* holds the scancode of the last key pressed */ newooze; /* TRUE if a new .OOZ file is to be created */ newooze=0; if (argc>2) {usage(); return 1; } if (argc>1) {if (stricmp(argv[1],"?")==0 || stricmp(argv[1],"-H")==0 || stricmp(argv[1],"/H")==0) {usage(); return 1; } strcpy(filename,argv[1]); } else strcpy(filename,"ooze.ooz"); file=fopen(filename,"rb"); if (!file) {file=fopen(filename,"w+b"); newooze=1; } if (!file) {printf("%s cannot be found or created.\n\n",filename); usage(); return 2; } /* initialize palette register array */ r=53; g=11; b=53; for(i=1;i<=42;i++) /* from magenta to red */ {p[i].r=r; p[i].g=g; p[i].b=b--; } for(;i<=84;i++) /* from red to yellow */ {p[i].r=r; p[i].g=++g; p[i].b=b; } for(;i<=126;i++) /* from yellow to green */ {p[i].r=r--; p[i].g=g; p[i].b=b; } for(;i<=168;i++) /* from green to cyan */ {p[i].r=r; p[i].g=g; p[i].b=++b; } for(;i<=210;i++) /* from cyan to blue */ {p[i].r=r; p[i].g=g--; p[i].b=b; } for(;i<=252;i++) /* from blue to magenta */ {p[i].r=++r; p[i].g=g; p[i].b=b; } /* put VGA into mode 0x13 (320x200x256) */ reg.x.ax=0x13; int86(0x10,®,®); setvgapalette(p,0,128); setvgapalette(&p[128],128,128); if (newooze) { #if defined(dotest) test(); #else srand(peek(0x40,0x6c)); putpixel(0,0,random(252)+1); putpixel(319,0,random(252)+1); putpixel(319,199,random(252)+1); putpixel(0,199,random(252)+1); subdivide(0,0,319,199); #endif fwrite(MK_FP(0xa000,0),1,0xfa00,file); } else fread(MK_FP(0xa000,0),1,0xfa00,file); fclose(file); /* rotate the palette until the [Esc] key is pressed */ do {rotatepalette(p); if (bioskey(1)) {key=bioskey(0)>>8; direction=1-direction; } } while(key!=1); /* put VGA into mode 3 (color text mode (80x25)) */ reg.x.ax=3; int86(0x10,®,®); return 0; } /* end of int main(argc,argv[]) */ void adjust(int x1,int y1,int x,int y,int x2,int y2) {int c,d; long horz,vert; if (getpixel(x,y)) return; horz=(long)(x2-x1); vert=(long)(y2-y1); d=sqrt(horz*horz+vert*vert); if (random(2)) c=((getpixel(x1,y1)+getpixel(x2,y2))/2-random(d)) % 252; else c=((getpixel(x1,y1)+getpixel(x2,y2))/2+random(d)) % 252; if (c<0) c=-c; else if (!c) c=1; putpixel(x,y,abs(c)); } /* end of adjust(x1,y1,x,y,x2,y2) */ int getpixel(int x,int y) {return(peekb(0xa000,320*y+x) & 0xff); } /* end of int getpixel(x,y) */ void putpixel(int x,int y,int c) {pokeb(0xa000,320*y+x,c); } /* end of putpixel(x,y,c) */ void rotatepalette(colortype p[]) {colortype temp; if (direction) {memmove(&temp,p+252,sizeof(colortype)); memmove(p+2,p+1,251*sizeof(colortype)); memmove(p+1,&temp,sizeof(colortype)); } else {memmove(&temp,p+1,sizeof(colortype)); memmove(p+1,p+2,251*sizeof(colortype)); memmove(p+252,&temp,sizeof(colortype)); } setvgapalette(p,0,128); setvgapalette(p+128,128,128); } /* end of rotatepalette(p) */ void setvgapalette(void *buffer,int first,int length) {asm cli /* Disable interrupts. */ asm mov dx,3dah setvgapalette1: /* Wait for vertical retrace to end. */ asm in al,dx asm test al,8 asm jnz setvgapalette1 setvgapalette2: /* Wait for vertical retrace to start. */ asm in al,dx asm test al,8 asm jz setvgapalette2 /* Set the VGA palette registers. */ asm push ds _DS=FP_SEG(buffer); _SI=FP_OFF(buffer); asm mov dx,3c8h /* port address of DAC address register. */ asm mov ax,first /* this is the number of first DAC register to update. */ asm out dx,al /* start with this DAC. */ asm inc dx /* 3c9h is the port address where the RGB info is written. */ asm mov cx,length /* this is the number of DAC registers to update. */ setdacloop: asm mov al,[si] asm out dx,al asm inc si asm mov al,[si] asm out dx,al asm inc si asm mov al,[si] asm out dx,al asm inc si asm loop setdacloop asm pop ds asm sti /* Enable interrupts. */ } /* end of setvgapalette(buffer,first,length) */ void subdivide(int x1,int y1,int x2,int y2) {int c,x,y; if (bioskey(1)) return; if (x2-x1<2 && y2-y1<2) return; x=(x1+x2)/2; y=(y1+y2)/2; adjust(x1,y1,x,y1,x2,y1); adjust(x2,y1,x2,y,x2,y2); adjust(x1,y2,x,y2,x2,y2); adjust(x1,y1,x1,y,x1,y2); if (getpixel(x,y)==0) {c=(getpixel(x1,y1)+getpixel(x2,y1)+getpixel(x2,y2)+getpixel(x1,y2))/4; if (c<1) c=1; else if (c>252) c=252; putpixel(x,y,c); } subdivide(x1,y1,x,y); subdivide(x,y1,x2,y); subdivide(x,y,x2,y2); subdivide(x1,y,x,y2); } /* end of subdivide(x1,y1,x2,y2) */ /* Use bisection to find the root of y=x*x-n. Return the value of x. */ unsigned sqrt(long n) {long xl,x,xh,y; if (n<0) n=-n; xl=0L; /* (xl,xh) is the range of y=x*x-n */ xh=46340L; while(xl