;************************************************************************
;*                                                                      *
;*    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

;        Rand8                ; random byte in al
;        mov   bl, recadj
;        shr   bl, 1
;        and   al, bl
;        shr   bl, 1          ; div by 2
;        sub   al, bl         ; subtract 1/2 (so low # neg, high pos)
;        cbw
;        add   ax, cx

         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    ; 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    ; 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



