/* ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
   ::                          Roto - Zooming                            ::
   ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
   ::  (c) 1995 DIGITAL CRäATION                                         ::
   ::  Code   : PuzNik                                                   ::
   ::  Grafix : PuzNik                                                   ::
   ::                                                                    ::
   ::  Compil with Compact Model. ( Borland C++ )                        ::
   :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: */

#include <stdio.h>
#include <dos.h>
#include <stdlib.h>
#include <mem.h>
#include <time.h>
#pragma inline

unsigned char far *m;
extern unsigned char far ecr[52000U];   // Contain the mapped texture
#include "_sincos.h"			// Speeds up maths
unsigned int Map;                       // Memory buffer segment
unsigned int x,y;                       // (x,y) pos of the texture in buff.
unsigned dist,cDd;                      // z distance of the texture
int  qui;                               // What texture is shown ??
long int nbimages;                      // Nb of frames displayer
double backup,timing;                   // Timing variables
int i,j,k,l,i1,j1;                      // Loop variables
int ddx,ddy,d2x,d2y;                    // (x,y) and (x1,y1) Positions
char rot;                      		// Rotation angle
					// of the displayed texture
// Switch to 320x200 256 colors mode
#define Graphmode       { asm mov ax,0x13; asm int 0x10; }

// Switch to 80x25 text mode
#define Textmode        { asm mov ax,0x3; asm int 0x10; }

// Allocate 64k of memory for the roto-zoom texture
#define AllocateMem { asm mov ah,0x48;asm mov bx,0x1000;asm int 0x21; \
		      asm jnc noerror; asm mov ax,0; noerror: asm mov Map,ax; \
		      if (Map==0) { printf("\nCould not allocate enough mem.");\
		      printf("\nProgramme ending..."); } }

// Free 64k of memory allocated for the texture
#define GiveBackMem { asm mov ah,0x49; asm mov dx,Map; \
		      asm mov es,dx; asm int 0x21; }

// Draw a bloc onto the texture.
void bloc(int x,int y,int coul)
{
 for(i1=0;i1<32;i1++) {
     for(j1=0;j1<32;j1++) {  m[((y+i1)<<8)+x+j1]=(coul)*255; }
 }
}

// G‚n‚rate che checker texture
void gen_checker()
{
 if(qui!=2) {
    k=0;l=0;
    for(i=0;i<256;i+=32) {
	if(l==1) k=1; else k=0;
	for(j=0;j<256;j+=32) {
	    bloc(j,i,k);k++;if(k>1) k=0;
	}
	l++;if(l>1) l=0;
    }
    qui=2;
 }
}

/// G‚n‚rate the texture from the picture.
void gen_digital()
{
 if(qui!=0) {
	memset((void *)m,0,65535U);qui=0;
	memcpy((void *)m,(void *)&ecr[800u],52000U-800U);
 }
}

/* ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
   :: Updating the rotozoom                                              ::
   :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: */

// Draw 4 pixel of the texture
#define updateline { asm {  add  ax,si;		\
			   mov  bl,ah;          \
			   mov  bh,dh;          \
			   mov  cl,[gs:bx]; 	\
			   add  ax,si;		\
			   add  dx,ddy;         \
			   mov  bl,ah;          \
			   mov  bh,dh;          \
			   mov  ch,[gs:bx];     \
			   shl  ecx,16;         \
			   add  ax,si;          \
			   mov  bl,ah;          \
			   mov  bh,dh;          \
			   mov  cl,[gs:bx];     \
			   add  ax,si;          \
			   add  dx,ddy;         \
			   mov  bl,ah;          \
			   mov  bh,dh;          \
			   mov  ch,[gs:bx];     \
			   rol  ecx,16;         \
			   mov  [fs:di],ecx;  \
		           add  di, 4 } }


// Wait the vertical Blank
#define wait_vbl {   asm mov dx, 0x3DA; \
	     synch1: asm in al, dx; asm test al, 8; asm jnz  synch1; \
	     synch2: asm in al, dx; asm test al, 1; asm jz synch2; }

// Make the rotozoom
void near DoRotate(void)
{
 asm mov  ax,Map              // Take the texture segment
 asm mov  gs,ax
 asm mov  ax,0xa000           // ES look to the screen
 asm mov  fs,ax
 asm cld
 ENCORE:
    asm inc byte ptr [rot]    // Update rot
    y+=27;                    // Move then texture along x,y
    x+=80;
    asm mov bx,cDd
    asm mov ax,dist
    asm add ax,cDd	      //    dist+=cDd;Change the z position
    asm mov dist,ax
    asm cmp ax,2500
    asm jg _change
    asm cmp ax,80
    asm jnl _ok
    _change:
    asm  neg bx		      //    if((dist>2500) || (dist<80)) cDd=-cDd;
    asm mov cDd,bx
    _ok:

 asm {  .486
	xor   bh,bh
	mov   bl,rot
	shl   bx,1                  // tab. index is bx = rot
	xor   ecx,ecx		    // multiply by the distance (ecx)
	movsx eax,word ptr[bx+Sin]
	mov   cx,word ptr dist
	imul  eax,ecx
	shr   eax,10
	mov   ddy,ax                 // ddx=(CosTable[rot]*dist);ddx=ddx>>8;
	shr   bx,1
	mov   dx,bx
	neg   bl
	add   bl,255
	shl   bx,1
	movsx eax,word ptr[bx+Sin]
	imul  eax,ecx
	shr   eax,10
	mov   d2x,ax
	mov   bx,dx
	add   bl,64
	shl   bx,1
	movsx eax,word ptr[bx+Sin]
	imul  eax,ecx
	shr   eax,10
	mov   ddx,ax                 // d2x=(Cos2Table[rot]*dist);d2x=d2x>>8;
	mov   ax,y
	mov   bx,ddy
	shl   ebx,7
	sub   ax,bx
	rol   eax,16
	mov   ax,x
	mov   bx,ddx
	shl   ebx,7
	sub   ax,bx
	push  eax }
	wait_vbl;
asm {   xor   di,di
	mov   si,ddx
	mov   cx,200
	pop   ax
	pop   dx }
 vloop:
 asm {  push  cx
	push  ax
	push  dx
	mov   cx,5 }
 hloop:
 asm    push  cx
 asm    REPT 16
	updateline;
 asm	ENDM
asm {   pop   cx
	dec   cx
	jz    ehloop
	jmp   hloop }
ehloop:
asm {   add si,qui }
	no_update:
 asm {  pop   dx
	pop   ax
	add   ax,d2x
	add   dx,ddx
	pop   cx
	dec   cx
	jz    evloop
	jmp   vloop }
 evloop:
 asm inc dword ptr nbimages                //  nbimages=nbimages++;
 asm mov dx,0x60                               // check keyboard
 asm in  al,dx
 asm cmp al,57                                 // Space ?
 asm je  espace
 asm cmp al,28                                 // Return ?
 asm je  retour
 asm cmp al,1                                  // Esc ?
 asm je  fin
 asm jmp ENCORE
 espace: gen_checker();asm jmp ENCORE
 retour: gen_digital();asm jmp ENCORE
 fin:    asm cli
}

/* ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
   :: Main proc.                                                         ::
   :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: */

void main()
{
 int ent,virg;
 nbimages=0;timing=0;                           // Init timing variables
 AllocateMem;                                   // Init alloc mem
 m=(unsigned char far *)MK_FP(Map,0);           // Pointer to texture
 randomize();
 Graphmode;                                     // Switch to 320x200x256
 outport(0x3C8,0);                              // Init Palette
 for(i=0;i<768;i++) outportb(0x3C9,ecr[33+i]>>2);
 if(rand()%50>25)                               // Little random to choose
 {                                              // between the 2 textures
  qui=2;gen_digital();gen_checker();gen_digital();
 }
 else
 { qui=0;gen_checker(); }
 asm sti
 asm mov  al,01111010b                          // Shut down all
 asm out  21h,Al                                // unnecesary interrupts
 asm cli
 x=32000U; y=0;                                 // (x,y) initial positions
 rot=0;                                         // starting angle of rotation
 dist=2499;                                      // z distance of the texture
 cDd=65520U;                                    // increment to z distance
 backup=peek(0x40,0x6E)*65536L+peek(0x40,0x6C); // Starting time
 asm sti
 DoRotate();                                    // Roto Zoom
 timing=peek(0x40,0x6E)*65536L+peek(0x40,0x6C); // Ending time
 asm sti
 asm mov  al,0
 asm out  21h,Al                                // Restore interrupts
 timing=timing-backup-10L;                      // makes up C calls
 GiveBackMem;                                   // Free memory
 Textmode;                                      // Return to text mode
 ent=( (long int)(nbimages*182L)/(long int)timing);
 virg=ent-((ent/10)*10);
 ent=ent/10;
 printf("\n%d.%d Fps",ent,virg);
 printf("\n\n(c) 1995 D.C.\nCoding by PuzNik\n\n");
}

