;************************************************************************
;*                                                                      *
;*    TPLAS.ASM - Generate a Plasma fractal of 320x200 dimensions       *
;*       and display on screen.                                         *
;*                                                                      *
;*    This is a working prototype of what will become TomsPlas.  At     *
;*       that point, the fractal will be twice the size of the screen   *
;*       (640x480), allowing greater movement in the window, and the    *
;*       window will be expanded to much more of the screen (perhaps    *
;*       all?).  Controls will be added to vary the speed (1-9 the      *
;*       number of frames to skip to speed it up, +/- a delay factor.)  *
;*       The code itself will be optimized to eliminate repetitive      *
;*       code, such as a push right before a pop, from the macros.      *
;*       Also, the movement and palette generation routines should      *
;*       be moved into the function itself rather than in the data      *
;*       file.  This would shrink the data file to exactly 65,536       *
;*       bytes, all of which would be numbers for the random series.    *
;*                                                                      *
;*          All code and original algorithms expressed herein,          *
;*                Copyright (C) 1994 by Tom Dibble                      *
;************************************************************************


.MODEL TINY    ; always must be TINY model for .COM ...

LOCALS

	;<<Macros>>

Screen_MaxX    equ   320
Screen_MaxY    equ   240


; PrintNum(n)
;		Print a decimal number to the screen using DBuf and INT 21h:09h
;		Prints number up to 1000
PrintNum	MACRO	n
	LOCAL @@PosNum
			push 	ax
			push	bx
			push	cx
			push	dx
			push	di

			xor	bx,bx
			mov	di,offset DBuf

			mov	ax,n
			cmp	ax,0
			jg		@@PosNum
			neg	ax
			mov	[di+0], BYTE PTR '-'
			inc	bx

		@@PosNum:
			xor	dx,dx
			mov	cx,1000
			div	cx
			add	ax,48				; convert whole # to ASCII
			mov	[di+bx],al
			inc	bx
			mov	ax,dx				; put remainder in ax

			xor	dx,dx
			mov	cx,100
			div	cx
			add	ax,48				; convert whole # to ASCII
			mov	[di+bx],al
			inc	bx
			mov	ax,dx				; put remainder in ax

			xor	dx,dx
			mov	cx,10
			div	cx
			add	ax,48				; convert whole # to ASCII
			mov	[di+bx],al
			inc	bx
			mov	ax,dx				; put remainder in ax
			add	ax,48				; convert whole # to ASCII
			mov	[di+bx],al
			inc	bx
			mov	ax,dx				; put remainder in ax
			inc	bx
			mov	[di+bx], BYTE PTR 10		; CR
			inc	bx
			mov	[di+bx], BYTE PTR 13		; LF
			inc	bx
			mov	[di+bx], BYTE PTR '$'		; terminate string

			mov   ah,09h
			mov   dx,offset DBuf
			int   21h
			pop	di
			pop	dx
			pop	bx
			pop	cx
			pop	ax
			endm


;  GetPoint(x,y)
;     Get the point in DS:[plasma] and place it in AL
;        x and y can not be any of the following:  AX, BX, CX
GetPoint MACRO    x,y
			push  bx       ; So we have some regs to play with
			push  cx       ;
			push  ds

         mov   bx, y
         mov   cx, bx      ; multiply by (320) [256+64 == <<8 + <<6]
			xchg  bh, bl      ;
         shl   cx, 6       ;
         add   bx, cx      ;
         add   bx, x
         mov   ds, [Plasma]   ; DS -->  (0,0) in Plasma mem block
         mov   al, [bx]    ; Get the damned byte

			pop   ds
         pop   cx
         pop   bx
			endm


;  SetPoint(x,y)
;     Set the point in DS:[plasma] to value in AL
SetPoint MACRO    x,y
         push  bx       ; So we have some regs to play with
         push  cx       ;
         push  ds

			mov   bx, y
         mov   cx, bx      ; multiply by (320) [256+64 == <<8 + <<6]
         xchg  bh, bl      ;  (mov low into high, same as shl bx, 8)
			shl   cx, 6       ;
			add   bx, cx      ;
         add   bx, x
			mov   ds, [Plasma]   ; DS -->  (0,0) in Plasma mem block
         mov   [bx], al    ; Set the damned byte
         pop   ds          ; DS -->  Data
         push  ds
         mov   ds, [Video] ; DS -->  Video mem block
         mov   [bx], al    ; Put this bitch on screen!

			pop   ds
			pop   cx
			pop   bx
			endm


;  SubDivide x1, y1, x2, y2
;  Subdivide a Plasma rectangle defined by given coordinates and recurse.
SubDivide   MACRO    x1,y1,x2,y2
			mov   ax,y2
			push  ax
			mov   ax,x2
			push  ax
			mov   ax,y1
			push  ax
			mov   ax,x1
			push  ax
			call SubDiv
			endm

;  Adjust x1, y1, x2, y2, xm, ym
;  Adjust point (x,ym) to average of (x,y1) and (x,y2) plus a random val.
;
Adjust   MACRO x1, y1, x2, y2, xm, ym
			mov   ax, ym
			push  ax
			mov   ax, xm
			push  ax
			mov   ax, y2
			push  ax
			mov   ax, x2
			push  ax
			mov   ax, y1
			push  ax
			mov   ax, x1
			push  ax

			call  Adj

			endm


;  Rand8
;  Get the next 8-bit random value from Rand_Series.  Put it into AL.
Rand8    MACRO
         push  ds
			mov   bx, Rand_Ptr
         mov   ds, [Rand_Series]
			mov   al, [bx]
         pop   ds
         inc   Rand_Ptr
         endm

;  Rand16
;  Get the next 16-bit random value from Rand_Series.  Put it into AX.
Rand16   MACRO
			push  ds
         mov   bx, Rand_Ptr
         mov   ds, [Rand_Series]
			mov   ax, [bx]
         pop   ds
         inc   Rand_Ptr
			inc   Rand_Ptr
         endm


;  GetTRand
;  Get System-Time 'seconds' and '1/100 seconds' into DH and DL, respectively
;  'hour' and 'minute' into CH and CL, respectively
GetTRand MACRO
         push  ax
         mov   ah, 2Ch     ;  DOS Interrupt Fn:  'Get System Time'
			int   21h
         pop   ax
         endm


; malloc:  allocate memory to program in 16-byte chunks
;  'amount' is the number of 'paragraphs' to allocate
;  (a paragraph being 16 bytes).
;  CF set on error, AX is 09h, BX is size of largest block
;  The segment address is left in AX if successful
malloc   MACRO    amount
         mov     ah,48h
         mov     bx,amount
			int     21h
         endm

; mfree:  frees malloc'd memory.
mfree    MACRO    where
			mov     ah,49h
			mov     es,where
         int     21h
         endm


; keyp:  check for key press
;  CF set if key is present.
keyp     MACRO
         mov     ah,0bh
         int     21h
         or      al,al
         endm

; kwait:  wait for key-press
kwait    MACRO
         push     ax
         mov      ah, 08h
			int      21h
         pop      ax
         endm

; exit:  exit with error code
exit     MACRO    return_code
         mov     al,return_code
         mov     ah,4ch
         int     21h
			endm

;  prints:  print a string to text screen
prints   MACRO    str
			mov     ah,09h
			mov     dx,offset str
			int     21h
			endm


; fopen:  open a file for read (0) or write (1) or both (2)
;  CF set on error, AX holds error code.
;  file handle left in AX if successful.
fopen   macro   file,attrib
        mov     ah,3dh
		  mov     al,attrib
        mov     dx,offset file
		  int     21h
        endm


; fread:  read information from file opened by fopen.
;  Data read into (AX:00h).
;  CF set on error.
;  AX return (number of bytes read) is nuked.
fread   macro   handle,bytes
		  push    ax
        push    ds
        push    ax
		  mov     ah,3fh
		  mov     bx,[handle]
        mov     cx,bytes
        xor     dx,dx      ;  Segment is (00h)
		  pop     ds         ;  this used to be AX!
        int     21h
        pop     ds
        pop     ax
        endm




;**************************** << CODE SEGMENT >> ****************************;

.CODE

org 100h
start:  jmp begin

	;  Variables
			;  Strings
		CopyRight   db    "Tom's Plasma (C) 1994 by Tom Dibble",10,13,"$"
		TPVersion	db		"Revision 1.1, March 19, 1994",10,13,"$"
		NoMemory    db    "Out of Base Memory!",10,13,"$"
		NoFile      db    10,13,\
								"ERROR:  Couldn't open TomsPlas.DAT!",10,13,\
								"   Change to the directory containing",10,13,\
								"   TomsPlas.DAT and restart the program!",10,13,"$"
		DebugPoint  db    "Debug Point!",10,13,"$"
		DataFile    db    "TOMSPLASM.DAT",0
		DBuf			db    80 dup (?)

			;  General Variables
		OrigVMode	db		03h	; Mode the video card was in when we got here!
		recadj      db    11111111B   ; last 8 bits set (255)
		count       dw    0		; loop counter while swimmin' and cyclin'
		DATHandle   dw    ?		; Handle to Data File
		Rand_Ptr    dw    ?     ; Pointer to next random value in [Rand_Series]
		WinHide		db		0		; Is the swim-window hidden?

			;  Segment Addresses
		Plasma      dw    0     ; Segment beginning for Plasma Bitmap
		Video       dw    0     ; Segment beginning for Video Memory
		Color       dw    0     ; Segment for Color-Palette Data
		Movement    dw    0     ; Movement data
		Rand_Series dw    0     ; 64k of random values

ASSUME   CS:@code,DS:@code,ES:@code

.386           ; select the processor

begin:      ; from here on in is executable stuffs ...

			mov     sp,offset stacktop       ; set new stack

			mov     bx,last_inst-start+100h  ; (size of prog. + 100h header)
			shr     bx,4            ; shrink memory usage to program size
			inc     bx              ; in pages (16 bytes)
			mov     ah,4ah
			int     21h            	; ES segment is resized.  ES is set to @code

			prints  CopyRight       ; display header message
			prints  TPVersion       ; display header message, part 2


		; InitProgram sets video mode, allocates memory, seeds 'Random'
			CALL  InitProgram

		; GenPlasma does all the work
			mov	[recadj], 11111111B	; last eight bits default ('Rough')
			CALL		ClearPlasma
	NewPlasma:
			CALL		ShowPlasma
			CALL		ClearPlasma
			CALL  	GenPlasma

		; Cycle the colors and swim the image
	ContinueCycle:
			cmp	[WinHide],0
			jne	NoWindow
			CALL  CycleSwim
			jmp	KeyDecipher
		NoWindow:
			CALL	ShowPlasma
			CALL	CycleNoSwim

	KeyDecipher:
		; Wait for a key to be pressed (if none has already)
			mov   ah, 08h
			int   21h
		; Process that key!
			cmp	al, 13		; Return --> New Plasma!
			je		NewPlasma
			cmp	al, ' '		; Space --> Hide Window!
			je		ToggleWindow
			cmp	al, '1'		; New Plasma, recadj of 255
			je		RoughPlasma
			cmp	al, '2'		; New Plasma, recadj of 127
			je		MedPlasma
			cmp	al, '3'		; New Plasma, recadj of 63
			je		SmoothPlasma
			cmp	al,27			; <Esc> --> Get outta here!
			je    LeaveProg

			jmp	ContinueCycle

	ToggleWindow:
			not	[WinHide]
			jmp	ContinueCycle	; any key puts the window back!

	RoughPlasma:
			mov	recadj, 11111111B
			jmp	NewPlasma

	MedPlasma:
			mov	recadj, 01111111B
			jmp	NewPlasma

	SmoothPlasma:
			mov	recadj, 00111111B
			jmp	NewPlasma

	LeaveProg:

		; Clean out memory allocated, return to right video mode
			CALL  CleanUp

		;Now exit to DOS
			mov   ah,4Ch
			int   21h
			ret

;************************************************************************
;*                                                                      *
;*    InitProgram                                                       *
;*                                                                      *
;*             Allocate memory to Plasma, clear it out                  *
;*             Open Data File                                           *
;*             Allocate memory to Movement, read                        *
;*             Allocate memory to Color, read                           *
;*             Allocate memory to Rand_Series, read                     *
;*             Get Rand_Ptr from system time function                   *
;*             Set Video Mode to 13                                     *
;*                                                                      *
;************************************************************************

InitProgram PROC NEAR
			malloc   4000+1         ; allocate memory for plasma buffer + 1 para.
			jc       nomem          ; break if not enough memory
			mov      [Plasma],ax    ; store address

			malloc   2500+1            ; allocate memory for movement buffer + 1
			jc       nomem             ; as above...
			mov      [Movement],ax
			malloc   192+1            ; allocate memory for color buffer + 1
			jc       nomem
			mov      [Color],ax
			malloc   1000h             ; allocate memory for random buffer + 1
			jc       nomem
			mov      [Rand_Series],ax
			jmp      allocok           ; skip the following

allocok: fopen    DataFile,0        ; open TomsPlas.DAT read only
			jnc      loadok            ;
			prints   NoFile            ; You schmuck!  It's not here!
			mfree    [Plasma]
			mfree    [Movement]
			mfree    [Color]
			mfree    [Rand_Series]
			exit     254               ; so quit
loadok:  mov      [DATHandle],ax    ; store handle

			mov      ax,[Movement]     ; read movement data
			fread    DATHandle,40000
			mov      ax,[Color]        ; color data
			fread    DATHandle,3072
			mov      ax,[Rand_Series]  ; and random series
			fread    DATHandle,0ffffh

			GetTRand                   ; get 'time' values into CX and DX
			add      cx, dx            ; accumulate values
			mov      Rand_Ptr, cx      ; put Rand_Ptr as a random place in series

			mov		ax,0F00h          ; get the video mode now
			int		10h               ;
			mov		[OrigVMode], al
			mov      ax,0013h          ; set video mode 13
			int      10h
			mov      [Video],0a000h    ; Start segment for Video Mem

			mov      si, 0
			mov      cx, 3*255         ; Set Color Palette!
			push     ds                ;
			mov      ds, [Color]
			cld
			mov      dx, 03C8h         ;
			mov      al, 1             ; start with color '1'
			out      dx, al            ;
			inc      dx
			rep      outsb
			pop      ds

			ret

	nomem:
			prints   NoMemory
         exit     -1

InitProgram ENDP

ClearPlasma	PROC	NEAR
			push		eax					;
			push		cx                ;
			push		es                ;	Clear out the memory devoted to
			push		di                ;		the Plasma buffer so that
												;		we can create a new Plasma!
			mov		ax, 0             ;
			shl		eax, 16           ;
			mov		ax,0              ;
			mov		cx, [Plasma]      ;
			mov		es, cx            ;
			mov		di, 0             ;
			mov		cx, 64000/4       ;
			rep		stosd					; put eax into es:di
												;
			pop		di                ;
			pop		es                ;
			pop		cx                ;
			pop		eax               ;
			ret
ClearPlasma	ENDP


ShowPlasma	PROC	NEAR
			push		ax
			push		cx
			push		es
			push		ds
			push		si
			push		di

			mov		si,0
			mov		di,0
			mov		ax,[Video]
			mov		es,ax
			mov		ds,[Plasma]
			mov		cx, 320*200/4
			rep		movsd

			pop		di
			pop		si
			pop		ds
			pop		es
			pop		cx
			pop		ax
			ret
ShowPlasma	ENDP


CleanUp  PROC  NEAR
			push     ax

			mfree    [Plasma]
			mfree    [Movement]
			mfree    [Color]
			mfree    [Rand_Series]

			mov		ah,00h
			mov      al,[OrigVMode]	  		; set video mode back to what it was
			int      10h

			prints   CopyRight				; Tell 'em this again!

			pop      ax
			ret

CleanUp  ENDP


GenPlasma   PROC NEAR

			Rand8
			SetPoint 0,0
			Rand8
			SetPoint 319, 199
			Rand8
			SetPoint 0, 199
			Rand8
			SetPoint 319, 0

			SubDivide 0,0,319,199
			ret
GenPlasma   ENDP

;************************************************************************
;*                                                                      *
;*    SubDiv      x1, y1, x2, y2                                        *
;*                                                                      *
;*             Corners have already been set to proper values.          *
;*             Check each side if it has been set yet, and if not,      *
;*                   set it to the average of associated corners plus   *
;*                   a random value based on Code:recadj.               *
;*             Set middle to average of four sides.                     *
;*             Recurse for each of four sectors in lt,rt,rb,lb order.   *
;*             Free up our memory and get the hell outta here!          *
;*                                                                      *
;*       18 bytes on stack per recursion, times 7 recursions.  Stack 	*
;*						size must be large enough to handle this load!        *
;*                                                                      *
;************************************************************************


SubDiv   PROC
	ARG x1:word, y1:word, x2:word, y2:word
	LOCAL	x:word, y:word=LocalStk
			push 	bp
			mov  	bp,sp
			sub	sp,LocalStk		; Set-up local stack
			push  cx
			push  dx

						;  First, check for recursion halts.
						;     < if(xhalt && yhalt) return >

			mov   ax,[x1]
			add   ax,[x2]
			shr   ax,1        ;  divide by two for average of x1,x2
			mov	[x],ax      ; now 'x' is on stack!

						;  Find the 'y' value now!
			mov   cx,[y1]
			add   cx,[y2]
			shr   cx,1        ;  divide by two for average of y1,y2
			mov	[y],cx      ; now 'y' is on stack!

			cmp   ax,[x1]
			jg xchecks        ;  if x1 != x, keep on going
						;  x does not check.
						;  Check for 'y' value recursion halt.
			cmp   cx,[y1]
			jg ychecks        ;  y checks.  Jump out.

				pop   dx          ;  Cleanup:  return DX, to it's place
				pop   cx          ;  	and same with CX, SP and BP ...
				mov   sp,bp       ;
				pop   bp          ;
				ret   8

		xchecks:
		ychecks:

						;  Okay!  We have checked for recursion halt
						;  and have calculated x and y.
						;  Next, check each of the four sides for empty
						;  and call 'Adjust' if so.  Meanwhile, keep
						;  a running total of all the four sides so we
						;  can do a quick average of them in the next
						;  step.

			GetPoint [x],[y1]
			cmp   al,0
			jne topgood
			Adjust [x1],[y1],[x2],[y1],[x],[y1]
		topgood:
			mov   ah,0
			push  ax

			GetPoint [x2],[y]
			cmp   al,0
			jne rightgood
			Adjust [x2],[y1],[x2],[y2],[x2],[y]
		rightgood:
			mov   ah,0
			pop   bx
			add   ax,bx
			push  ax

			GetPoint [x],[y2]
			cmp   al,0
			jne bottomgood
			Adjust [x1],[y2],[x2],[y2],[x],[y2]
		bottomgood:
			mov   ah,0
			pop   bx
			add   ax,bx
			push  ax

			GetPoint [x1],[y]
			cmp   al,0
			jne leftgood
			Adjust [x1],[y1],[x1],[y2],[x1],[y]
		leftgood:
			mov   ah,0
			pop   cx
			add   cx,ax
			;  No push of AX here!

						;  Okay.  All four side pixels have been computed
						;  and plotted on the screen and in memory.  AX
						;  holds the sum of all four pixels.  Divide by four
						;  and set x,y to the resulting value.
			shr   cx,2

			mov	al,cl
			SetPoint    [x],[y]

						;  Finally.  All the real work is done.  Now, decrease
						;  the recurse-level adjustment and recurse down
						;  to the next level for each of the four quads

			mov   al, recadj
			push  ax
			shr   al, 1
			mov   recadj, al

			SubDivide [x1], [y1], [x],  [y]
			SubDivide [x],  [y1], [x2], [y]
			SubDivide [x],  [y],  [x2], [y2]
			SubDivide [x1], [y],  [x],  [y2]

			pop   ax
			mov   recadj, al  ; return it to what it was before we got here.

			pop   dx          ;  Cleanup:  return DX, to it's place
			pop   cx          ;  	and same with CX, SP and BP ...
			mov   sp,bp       ;
			pop   bp          ;
			ret   8
SubDiv   ENDP


;************************************************************************
;*                                                                      *
;*    Adj         x1, y1, x2, y2, x, y                                  *
;*                                                                      *
;*          Sets the point at (x,y) to be the average of points (x1,y1) *
;*                and (x2,y2), plus a random factor based on recval.    *
;*                                                                      *
;*          Get points (1) and (2) into AH, AL.                         *
;*          Average the values into AL.                                 *
;*          CBW AL, move into BX.                                       *
;*          Get a random 16-bit number into AX                          *
;*          Multiply AX and recval.                                     *
;*          Shift DX to the right 2 bits (same as shifting 32-bit 18)   *
;*          Move DX into AX                                             *
;*          Add BX into AX                                              *
;*          if AX < 1, AX = 1.                                          *
;*          if AH != 0, AL = 255                                        *
;*          Set point (x,y) to value in AL.                             *
;*                                                                      *
;************************************************************************

Adj      PROC  NEAR
	ARG x1:word, y1:word, x2:word, y2:word, xm:word, ym:word
			push  bp
			mov   bp, sp
			push  bx
			push  cx
			push  dx

			mov	ah,0

			GetPoint [x1], [y1]     ;  Sets 'AL'
			mov   cl, al            ;  Put it into 'CL'

			GetPoint [x2], [y2]     ;  Sets 'AL'
			add   cx, ax            ;  Add this guy to the first.

			shr   cx, 1             ;  Divide by two (to make average)

			Rand8                ; random byte in al
			mov   bl, recadj
			and   al, bl    		; restrict the rand #
			shr   bl, 1          ; div by 2
			sub   al, bl         ; subtract 1/2 (so low # neg, high pos)
			cbw
			add	ax, cx			; cx is pos.  ax may be + or -
										; If the sum exceeded 255, ah will
										; 		have a positive number in it.
										; If the sum was less than zero, ax
										; 		will be negative.

			cmp	ax,0
			jg		AdjLowCheck
			mov	ax,1				; ax < 0, force it to lower bound!
			jmp	AdjSetPoint

		AdjLowCheck:
			cmp	ax,256
			jl		AdjSetPoint
			mov	ax,255			; ax > 255, force it to upper bound!

		AdjSetPoint:
			SetPoint [xm], [ym];  Set (x,y) to 'AL'

			pop   dx
			pop   cx
			pop   bx
			pop   bp

			ret   12                ; 6*2 parameters

Adj      ENDP




;************************************************************************
;*                                                                      *
;*    CycleSwim                                                         *
;*                                                                      *
;*          Cycle the color palette and swim a middle window of plasma. *
;*                                                                      *
;*          'Swimming' is the process of adding two sections of the     *
;*                plasma fractal together, then moving the pointers     *
;*                to each of those sections in a loop so as to create   *
;*                a composite, changing fractal.                        *
;*                                                                      *
;************************************************************************


CycleSwim	PROC  NEAR

			mov      ax, [Video]    ; Put the video segment address in es
			mov      es, ax         ;   for stosd loop below.

		mainloop:
						; We'll do all this setup stuff *BEFORE* we wait for VRT.
						; This sets things up for :setpl: loop below ...

						; Rotate the palette!
			push     ds
			push     es
			cld
			mov      es, [Color]
			mov      ds, [Color]
			mov      bx, 0

			mov      ax, [bx]			; r,g of color[0]
			mov      dl, [bx+2]		; b of color[0]

			mov      cx, 256*3      ; loop 256*4*3/4 times
			mov      si, 3
			mov      di, 0
			rep      movsd          ; move bytes from ds:si to es:di

			mov      bx, 256*4*3-3 	; copy saved rgb to end of current pal.
			mov      [bx],ax
			mov      [bx+2],dl

			pop      es
			pop      ds

			mov      si,0
			mov      cx,255*3       ; loop 255 times (for 255 DAC's)
			push     ds
			mov      ds,[Color]     ; address segment
			cld                     ; ensure SI is incremented

		VSync:
			mov      dx,03dah       ; VGA input status register 1
			in       al,dx          ; load value
			test     al,08          ; vertical retrace??
			je       VSync	        	; if not, try again...

						;  Load all the info from the colour segment
						;  into the DAC's ...

						; NOW do the final prep for color loading
			mov      dx,3c8h        ; DAC index register
			mov      al,1           ; start with color 1
			out      dx,al          ; and load
			inc      dx             ; DAC read/write register
			rep 		outsb

			pop      ds             ; and restore DS back to code segment

			mov      di,[count]     ; source = count * 4
			shl      di,2           ;
			push     ds
			mov      ds,[Movement]  ; get segment address of movement data
			mov      si,[di]        ; load point 1, start of reading x-data
			mov      bx,[di+2]      ; load point 2, start of added x-data
			pop      ds             ; and restore DS back to code segment
			push     ds
			mov      ds,[Plasma]    ; get segment of start of plasma
			mov      di,320*50+90   ; put window in the center of the screen.

			mov      ch,100         ; y loop = 100 pixels
	pl1:  mov      cl,40          ; x loop = 40 * 4 = 160 pixels
	pl2:  lodsd                   ; get 4 source pixels from ds:si, inc si
			add      eax,[si+bx]    ; add 4 source pixels from ds:(si+bx)
			stosd                   ; and store them on screen to es:di, inc di
			dec      cl             ; dec  x-counter
			jnz      pl2            ; and loop..

			sub      si,160         ; reset source to beg. of :pl1:
			mov      dx,ds          ; add 20 to DS :
			add      dx,20          ; move 20*16 = 320 bytes down in source mem
			mov      ds,dx
			add      di,160         ; add 160 to vid ptr (to beg of next line)
			dec      ch             ; dec y-counter
			jnz      pl1            ; and loop..

			pop      ds                ; and restore DS back to code segment
			inc      word ptr [count]  ; increase counter
			cmp      word ptr [count],10000  ; reset it at end of cycle
			jne      noreset
			mov      word ptr [count],0

		noreset:
			keyp                       ; keypressed??
			jnz      closedown         ; if yes then quit..
			jmp      mainloop

		closedown:
			ret

CycleSwim	ENDP


;************************************************************************
;*                                                                      *
;*    CycleNoSwim                                                       *
;*                                                                      *
;*          Just cycle the color palette. 										*
;*                                                                      *
;************************************************************************


CycleNoSwim	PROC  NEAR

			mov      ax, [Video]    ; Put the video segment address in es
			mov      es, ax         ;   for stosd loop below.

		CNSmainloop:
						; We'll do all this setup stuff *BEFORE* we wait for VRT.
						; This sets things up for :setpl: loop below ...

						; Rotate the palette!
			push     ds
			push     es
			cld
			mov      es, [Color]
			mov      ds, [Color]
			mov      bx, 0

			mov      ax, [bx]			; r,g of color[0]
			mov      dl, [bx+2]		; b of color[0]

			mov      cx, 256*3      ; loop 256*4*3/4 times
			mov      si, 3
			mov      di, 0
			rep      movsd          ; move bytes from ds:si to es:di

			mov      bx, 256*4*3-3 	; copy saved rgb to end of current pal.
			mov      [bx],ax
			mov      [bx+2],dl

			pop      es
			pop      ds
			push     ds

			mov      si,0
			mov      cx,255*3       ; loop 255 times (for 255 DAC's)
			mov      ds,[Color]     ; address segment
			cld                     ; ensure SI is incremented

		CNSVSync:
			mov      dx,03dah       ; VGA input status register 1
			in       al,dx          ; load value
			test     al,08          ; vertical retrace??
			je       CNSVSync	      ; if not, try again...

						;  Load all the info from the colour segment
						;  into the DAC's ...

						; NOW do the final prep for color loading
			mov      dx,3c8h        ; DAC index register
			mov      al,1           ; start with color 1
			out      dx,al          ; and load
			inc      dx             ; DAC read/write register
			rep 		outsb

			pop      ds             ; and restore DS back to code segment

			keyp                  	; keypressed??
			jnz      leaveCNS       ; if yes then quit..
			jmp      CNSmainloop

		leaveCNS:
			ret

CycleNoSwim	ENDP


mystack    db      8192 dup (?)  ; my stack:  room for lots o' recursion
stacktop   equ     $             ; top of my stack.

last_inst:                       ; the end of the program!

END     start


