; From Jonas Maebe - Frogger, assembler version.

COMMENT *
The following message contains the assembler conversion of my frogger game
(those who read the Pascal conference as well have already seen it, but this
version is a lot more versatile). It's my first full blown assembler program. It
took me a while to get away with the segment defining etc., but the code itself
wasn't a real problem since 85% of the game already was written in TP's BASM
(although quite a few syntax changes had to be made; TP's BASM seems to accept a
mixture of the Masm and Tasm syntax).

For those who don't know frogger, it's a game from the Apple II years (it also
existed on C64). You are a frog and have to get to the other side of the road
and water while avoiding cars and making sure you don't drown. Normally, when
you can reach the top of the screen with six frogs, you advance to the next
level (in which a crocodile and snakes apear). This version just clears the top
positions. There isn't any form of score system implemented either.

It requires a 386 with VGA with about 140Kb of free conventional memory and was
assembled using TASM 2.0 using Ideal mode. If you only have Masm (or some other
assembler that doesn't accept the syntax used), I'm afraid you'll have to adapt
it to its syntax, since I for myself only know Ideal mode.

You can move the frog with the arrow keys and quit by pressing [ESC]. At first,
everything may seem to go very speedy (at 70 Fps), but if you practice somewhat
you should be able to reach the top :) If you run the game in a dos box under
OS/2 (full screen preferably), make sure you set 'Direct Timer Access' to
ENABLED for that session. That's because the Random Procedure that's used
(thanks to Rory Barton for it), accesses the timer and when the timer is
emulated, it seems to return always the same value (or something similar, it's
not random in any way in either case).

When the game quits, some sort of FPS is displayed. It's really a very crude
approach an can only handle numbers up to 99. The FPS is only "reliable" when
you played the game for at least 10-15 seconds. With vertical retrace the
maximum is 69-70 fps. Without VR you'll probably get just garbage, since the
number will be in most cases bigger than 99.

Improvements over Pascal version:

- Smaller! (TP version: 9Kb exe + 2.5Kb datafile, this one almost 5Kb all in)
- the "frogger.til" file was removed and incorporated in the data declaration of
the program.
- the DrawSprite procedures have been made clearer (so you now can easily port
them over to other programs) by using constants instead of a bunch of
meaningless immediates :)

The reason why some labes are preceded with a @-sign and some not, is that TP's
Basm requires such a character to recognize a statement as a label. You can
change some of the characteristics of the game by altering the assembler
directives at the top of the program (1 means true, 0 means false).

Assemble with "tasm /m3 frogger.asm" and link with "(t)link frogger.obj".
Of course the code is still completely freeware (it can be included in any
source code archive) and all comments/questions are welcome! Enjoy!

PS: I've first asked permission to Ed to post this code, because it's so big
(44Kb). The next 14 messages should contain everything.

PS2: Can you specify by using a directive in the source code the minimum and
maximum amount of memory required by the program? That info would then be stored
in the .obj file and the linker would add it to the executable's header. This
program requires about 140Kb of memory, but TLink sets the minimum amount of
required memory to 1F62 Kb (= 8034 Kb)! I can't even discover a command line
switch for TLink 3.0 to change that behaviour.

PS3: it took me a while to figure out you have to put a RET yourself at the end
of procedures :)

Cya,
           Gamefreak

-------------------------------------------------------------------------
From : Ed Beroset
To   : Jonas Maebe
Subj : frogger bug

I tested out your code and found only one actual error so far:

     downkey:
      cmp [frogpos.y], 178
      jae keysend
      sub [frogpos.y], 16    ; this should be add [frogpos.y], 16
     keysend:

FWIW, you might also want to re-do the keyboard handling code.  Rather than
checking for a key, then branching to a label, you might want to use a more
compact form.  Something like this:

upkey:
    cmp ah,K_UP     ; equates would be useful for the keys
    jnz downkey
; do upkey stuff
downkey:
    cmp ah,K_DOWN
    jnz rightkey
; do downkey stuff
; etc.

Thanks for posting your code.  Maybe it will inspire others!

-> Ed <-

--------------------------------------------------------------------------

COMMENT ENDS *

%title "Frogger 1.0, assembler version, (c) 1996 Jonas Maebe (2:292/624.7)"
Ideal
DosSeg
Model Small
Stack 200h
P386N
jumps
radix 10

; assembler directives

Is386 = 0            ; replace the 0 by a 1 to speed up the code
                     ; somewhat for a 386 (slows down for 486 and Pentium!).
                     ; Probably not noticable, but who cares? :)

slow = 1             ; replace the 0 by a 1 to slow everything down,
                     ; necessary when playing on a VLB/PCI videocard

move32bit = 1        ; disable this one if you have a 386SX, I've heard
                     ; 16 bit moves are faster on those systems

v_retrace = 2        ; disable this one if the animation is jerky, it will
                     ; speed up the screen redraw and thus improve the
                     ; animation. Drawback: some fuzz... Also, if the
                     ; game runs smooth with it, it probably won't without
                     ; ----------------------------------------------------
                     ; EDITOR'S NOTE:  this now equals the *number* of
                     ; retraces to wait for, set to 2 for an easier game

invincible = 0       ; Have a wiiiiiild guess :)

idspispopd = 0       ; Remeber Doom? Of course you do!
                     ; Ever cheated? Of course you did! :)
                     ; BTW: pretty useless if not combined with the above one

bothsides = 1        ; This setting controls whether the remaining part of
                     ; of clipped sprites is drawn at the other side of the
                     ; screen. Leave it to one for this game, but if you
                     ; use the drawsprite procs in for example a horizontal
                     ; shooter, you may want to disable this.

; type definitions

Struc position
        x dw  ?
        y dw  ?
Ends position

; constants

up EQU 0                ; for the turtle diving
med EQU 1
down EQU 2
under EQU 3

retrace EQU 3           ; once per <retrace> loops the water palette is cycled
treemid1 EQU 4          ; the number of middle parts per tree
treemid2 EQU 2
noftree1 EQU 2          ; how many logs per line
noftree2 EQU 3
car1y EQU 163*320       ; since the y-coords of the cars and turtles
car2y EQU 147*320       ; don't change, immeditely multiply them by
car3y EQU 131*320       ; 320 (turtle coords follow below)
car4y EQU 115*320

SpriteXsize EQU 10      ; If you want to incorporate the sprite drawing procs
SpriteYsize EQU 10      ; in one of your own programs, you'll have to define
                        ; these constants as well. They can be changed to about
                        ; every value.

rightclip EQU 273 - SpriteXsize
leftclip EQU 1

; And the above two define the left and right border of the playing field.
; These can be changed as well and are also required by the DrawSprite procs.

; variables

Segment Dseg Word Use16

    FPS db '??',20h,'frames per second (approx.)',13,10,'$'
    turtley dw (16*5+3)*320, (16*3+3)*320, (16+3)*320
    waterinc dw 1, -1, 2, 1, -1 ; to decide in which direction the frog moves
                                ; when he's on a log at a certain y-pos
    frogtop db 6 dup (0)
    stop db 0
    lives db 4
    car1pos dw 259, 236, 213, 170, 147, 123, 78, 55, 32
    car2pos dw 259, 236, 213, 170, 147, 123, 78, 55, 32
    car3pos dw 262, 173, 84
    car4pos dw 259, 236, 213, 170, 147, 123, 78, 55, 32
    turtlepos dw 257,244,231,167,154,141,73,60,47
              dw 2 dup(231,243,255,141,153,165,47,59,71)
    tree1pos dw 12,22,32,42,52,62,150,160,170,180,190,200
    tree2pos dw 56,46,36,26,147,137,127,117,238,228,218,208
    treeofs dw offset tree, offset tree+100, offset tree+200
    turtleofs dw offset Turtle, offset TurtleDo1Le, offset TurtleDo2Le
    TurtleDepth db 0
    TurtleDepthCount dw 0
    frames dd 0
    cyclecount db retrace
    CTurtleDepth db up, med, down, under, down, med, up
    frogpos position <(leftclip+rightclip) / 2,179>

; sprites and palette

Water db 11,12,13,14,14,15,15,15,16,17,18,19,19,19,20,20,11,11,11,12,12,13,14
      db 15,15,16,16,16,16,16,17,17,12,13,13,13,13,13,13,13,13,14,14,15,16,17
      db 18,19,12,12,12,13,13,14,14,15,16,17,17,17,17,18,19,20,13,13,14,15,16
      db 16,17,17,18,19,19,20,20,20,20,20,12,12,12,12,12,12,12,13,14,14,15,15
      db 16,16,17,17,12,12,13,14,15,16,17,18,18,18,18,18,19,20,20,20,13,13,13
      db 13,13,14,15,16,16,17,18,19,20,20,20,20,12,12,13,13,13,14,14,14,14,14
      db 14,15,15,16,16,17,11,12,13,13,14,14,14,14,15,16,17,17,17,18,18,18,13
      db 14,15,16,17,17,18,18,18,18,19,19,19,19,19,20,12,13,14,15,15,16,17,17
      db 18,18,19,19,19,20,20,20,13,13,13,13,13,14,14,14,14,14,14,15,16,16,17
      db 17,12,12,13,14,15,15,16,16,16,17,18,18,19,19,20,20,11,12,12,13,13,13
      db 13,14,15,16,16,16,16,17,17,18,12,13,14,15,15,16,16,16,17,17,18,18,18
      db 18,19,20

grass db 4,7,2,9,8,8,6,2,9,3,10,1,6,1,10,9,6,2,4,1,1,9,5
      db 7,10,8,7,5,7,3,8,1,10,9,4,7,1,4,7,4,3,4,8,1,10,6
      db 1,3,10,7,6,1,1,3,7,8,9,3,6,9,4,7,4,3,1,3,4,2,3
      db 9,6,3,8,6,6,10,8,5,5,7,5,5,10,4,7,6,6,3,5,2,2,5
      db 5,8,1,1,1,1,3,10,8,5,4,2,8,10,9,8,10,10,8,4,8,7,5
      db 9,9,4,3,10,8,4,6,1,7,9,10,10,10,3,9,4,7,9,1,9,3,9
      db 6,8,10,4,8,7,7,7,9,10,2,9,2,10,9,1,4,7,4,8,2,5,5
      db 6,6,5,2,4,4,4,5,1,6,3,2,10,8,8,5,2,3,7,6,10,10,10
      db 6,10,9,7,4,10,5,10,2,6,8,10,6,8,3,3,8,3,1,9,8,4,3
      db 9,5,10,9,10,6,1,1,1,5,9,10,3,10,4,1,8,5,8,9,3,2,7
      db 2,8,9,1,1,4,1,1,5,1,5,7,10,3,2,2,8,7,7,1,1,3,9
      db 6,4,4

frog db 0,0,0,0,61,61,0,0,0,0,0,0,0,25,63,63,25,0,0,0
     db 0,61,0,0,62,62,0,0,61,0,0,0,61,61,63,68,61,61,0,0
     db 0,0,0,62,67,64,62,0,0,0,0,0,0,66,65,68,64,0,0,0
     db 0,0,0,62,66,66,62,0,0,0,0,0,61,61,63,69,61,61,0,0
     db 0,61,0,61,62,62,61,0,61,0,0,0,0,0,61,61,0,0,0,0

car1 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
     db 0,0,0,31,31,31,0,0,0,0,0,0,31,31,31,31,31,0,0,0
     db 31,31,31,31,31,31,31,31,31,0,31,31,31,31,31,31,31,31,31,31
     db 0,0,31,0,0,0,0,31,0,0,0,0,0,0,0,0,0,0,0,0
     db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

car2 db 0,0,0,0,0,0,0,0,0,0,0,0,82,81,0,0,0,82,81,0
     db 0,93,92,92,93,92,92,93,94,0,96,91,90,91,95,89,80,80,93,94
     db 94,92,91,90,95,90,90,79,80,94,94,92,91,91,95,89,90,79,80,94
     db 96,93,92,92,95,89,80,80,93,94,0,93,93,92,93,92,92,93,94,0
     db 0,0,82,81,0,0,0,82,81,0,0,0,0,0,0,0,0,0,0,0

car3 db 0,0,0,0,0,0,0,0,0,0,29,0,78,80,0,0,0,0,0,0
     db 29,0,82,81,0,0,79,0,21,88,29,0,86,83,85,88,86,0,21,88
     db 29,83,85,29,87,87,85,84,21,88,29,0,86,83,85,88,86,0,21,88
     db 29,0,82,81,0,0,79,0,21,88,29,0,78,80,0,0,0,0,0,0
     db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

car4 db 0,0,0,0,0,0,0,0,0,0,0,101,100,0,0,0,0,0,0,0
     db 0,80,78,100,0,80,78,0,80,78,0,80,78,0,100,101,100,101,100,78
     db 0,80,78,0,0,96,98,97,101,78,0,80,78,0,101,97,96,99,100,78
     db 0,80,78,0,0,96,98,97,101,78,0,80,78,0,100,101,100,101,100,78
     db 0,80,78,100,0,80,78,0,80,78,0,101,100,0,0,0,0,0,0,0

turtle db 0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,1,1,1,0,0
       db 0,0,1,1,3,4,4,3,1,0,29,1,1,4,5,6,6,4,1,1
       db 3,7,3,4,6,8,6,4,3,1,3,7,1,4,6,8,7,4,3,1
       db 29,1,1,3,5,5,5,4,1,1,0,0,1,1,3,3,4,2,1,0
       db 0,0,0,0,1,2,2,1,0,0,0,0,0,1,0,0,0,0,1,0

tree db 0,0,43,43,45,43,44,43,43,44,0,44,45,45,45,47,46,47,48,45
     db 0,45,46,44,47,45,48,48,46,47,0,47,45,46,48,46,49,47,48,49
     db 0,47,49,48,50,49,51,49,50,50,0,49,49,47,50,49,51,49,50,50
     db 0,45,45,46,48,46,49,47,48,49,0,46,46,44,47,45,48,48,46,47
     db 0,45,45,46,45,47,46,47,48,45,0,0,43,43,45,43,44,43,43,44

     db 45,44,43,43,45,43,44,43,43,44,46,47,46,45,45,47,46,47,48,45
     db 46,48,46,48,47,45,48,48,46,47,47,46,48,49,48,46,49,47,48,49
     db 48,50,51,49,50,49,51,49,50,50,47,49,50,51,49,51,50,50,49,51
     db 47,46,48,49,48,46,49,47,48,49,46,48,46,48,47,45,48,48,46,47
     db 46,47,46,45,45,47,46,47,48,45,45,44,43,43,45,43,44,43,43,44

     db 44,43,43,45,43,44,43,43,44,48,46,45,45,47,46,47,48,45,55,52
     db 45,47,45,48,48,46,47,54,54,55,47,48,46,49,47,48,49,56,55,54
     db 49,50,49,51,49,50,50,57,59,54,51,50,49,51,49,50,50,57,59,53
     db 47,48,46,49,47,48,49,56,55,54,45,47,45,48,48,46,47,54,54,54
     db 46,45,45,47,46,47,48,45,55,52,44,42,43,43,42,41,41,42,42,48

Pall db 0,0,0,16,26,0,17,27,0,18,28,0,19,29,0,20,30,0,21,31,0,22,32,0
     db 23,33,0,24,34,0,25,35,0,0,15,50,0,14,49,0,13,48,0,12,47,0,11,46
     db 0,10,45,0,9,44,0,8,43,0,7,42,0,6,41,41,0,0,42,0,0,43,0,0
     db 44,0,0,45,0,0,46,0,0,47,0,0,48,0,0,49,0,0,50,0,0,0,51,0
     db 0,52,0,0,53,0,0,54,0,0,55,0,0,56,0,0,57,0,0,58,0,0,59,0
     db 0,60,0,16,10,0,17,11,0,18,12,0,19,13,0,20,14,0,21,15,0,22,16,0
     db 23,17,0,24,18,0,25,19,0,26,20,0,27,21,0,28,22,0,29,23,0,30,24,0
     db 31,25,0,32,26,0,33,27,0,34,28,0,35,29,0,41,35,0,42,36,0,43,37,0
     db 44,38,0,45,39,0,46,40,0,47,41,0,48,42,0,49,43,0,50,44,0,38,38,38
     db 36,36,36,34,34,34,32,32,32,30,30,30,28,28,28,26,26,26,24,24,24,22
     db 22,22,20,20,20,12,12,12,15,15,15,30,0,0,35,0,0,40,0,0,20,13,13,57,0
     db 0,25,0,0,30,30,50,27,27,50,24,24,50,21,21,50,18,18,50,15,15,50,45
     db 45,60,45,45,0,55,55,0,53,53,0,48,48,0,42,42,0,39,39,0,0,0,0,0,0,0
     db 19*24 dup(0)

turtle2 db 0,1,0,0,0,0,1,0,0,0,0,0,1,1,1,1,0,0,0,0
        db 0,1,1,3,4,4,3,1,0,0,1,1,4,5,6,6,4,1,1,29
        db 1,3,3,6,8,6,6,4,7,3,1,3,4,7,6,8,4,1,7,3
        db 1,1,4,5,5,5,3,1,1,29,0,1,2,4,3,3,1,1,0,0
        db 0,0,1,2,2,1,0,0,0,0,0,1,0,0,0,0,1,0,0,0

turtleDo1Le db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
            db 0,0,0,2,3,3,2,0,0,0,21,0,2,5,6,5,4,1,0,0
            db 0,0,3,6,7,5,4,3,1,0,0,0,3,6,7,6,4,3,1,0
            db 21,0,2,5,5,5,4,1,0,0,0,0,0,2,3,3,2,0,0,0
            db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

turtleDo2Le db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
            db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,5,4,0,0,0
            db 0,0,0,3,4,6,5,3,0,0,0,0,0,3,4,6,4,3,0,0
            db 0,0,0,0,4,4,3,0,0,0,0,0,0,0,0,0,0,0,0,0
            db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

; Uninitialised vars

    fpstime1 dd ?              ; to calculate the fps at the end
    fpstime2 dd ?
    savedi dw ?                ; temp vars used during sprite drawing
    savecx dw ?
    Time dw ?                  ; to check when the turtles should dive
Ends Dseg

Segment _Vaddr Para Use16      ; virtual workscreen (alligned on a segment)

    VirScr db 64000 dup (?)

Ends _Vaddr

Segment _Backaddr para Use16   ; virtual screen that holds the permanent
                               ; background
    Background db 64000 dup (?)

Ends _Backaddr

; macro's

Macro KeyPressed  ; ZF if no keypress, otherwise NZ
      mov ah, 1
      int 16h
EndM   KeyPressed

Macro ReadKey     ; returns scan code in ah
      mov ah, 10h
      int 16h
EndM   ReadKey

; Sound and NoSound are asm translations of the Pascal code found in PCGPE

Macro Sound frequency   ; makes the internal speaker beep at frequency Hz
      mov dx, 12h
      mov cx, frequency
      mov ax, 34ddh
      div cx
      mov bx, ax
      mov al, 0b6h
      out 43h, al
      mov al,bl
      out 42h,al
      mov al, bh
      out 42h, al
      in al, 61h
      or al, 3
      out 61h, al
EndM   Sound

Macro NoSound               ; turns off the internal speaker
      in al, 61h
      and al, 0fch
      out 61h, al
EndM NoSound

Macro Move386 source, dest  ; moves 64000 bytes from segment source to
      mov dx, Dseg          ; segment dest
      mov si, source
      mov di, dest
      mov ds, si
      mov es, di
      xor si,si
      xor di,di
If move32bit
      mov cx, 16000
      rep movsd
Else
      mov cx, 32000
      rep movsw
Endif
      mov ds, dx
EndM Move386

CodeSeg

ASSUME DS: Dseg

BEGIN:
        call init
       playloop:
        inc [frames]
        dec [cyclecount]           ; decrease cyclecount
        jnz @nocycle               ; if it isn't zero, don't cycle
        std                        ; the pallette (water);  set direction
        mov ax, ds                 ; flag to move backwards
        mov es, ax                 ; es := ds
        mov [cyclecount], retrace  ; reset cyclecout to 2
        mov si, offset pall + 54   ; ds:si points to pall[20,0]
        mov bx, [si]               ; save the red and green values in bx
        mov dl, [si+2]             ; save the blue value in dl
        mov si, offset pall+19*3+1 ; ds:si points to pall[19,1]
        mov di, offset pall+20*3+1 ; es:di points to pall[20,1]
        mov cx, 6
        rep movsd
        movsw
        movsb                      ; move the pallette values
        add si, 2                  ; adjust the source index, I don't
                                   ; really understand why, but it's
                                   ; necessary :)
        cld                        ; clear the direction flag
        mov [si], bx               ; restore the red, green
        mov [si+2], dl             ; and blue values
       @nocycle:
        xor ax, ax
        mov es, ax
        mov di, 46ch
        mov ax, [es:di]            ; get the current time (in ticks)
        cmp [time], ax             ; compare it to the previous read time
        ja @noturtledive           ; not equal -> don't change depth of
        add ax, 18                 ; turtles
        mov [time], ax             ; save new time
        mov bx, [TurtleDepthCount]
        inc bx
        cmp bx, 7
        jb @TurtleDepthCountOk
        xor bx, bx
       @TurtleDepthCountOk:
        mov [TurtleDepthCount], bx
        mov al, [bx+offset CTurtleDepth]
        mov [TurtleDepth], al      ; set the new TurtleDepth
       @noturtledive:
        mov ax, _vaddr
        mov es, ax                 ; es has been changed, so restore it
                                   ; draw cars
        mov bx, offset car1pos     ; ds:bx points to pos of car1pos[0,0]
        mov al, 9                  ; repeat for 9 cars
        mov dx, car1y              ; y coords of car1 in dh
       @loop:
if slow
        test [byte ptr turtlepos],1    ; If slow, only increase the car's
        jz @noinc1                 ; position once per 2 loops
        inc [word bx]              ; increase the position of the car
       @noinc1:
else
       inc [word bx]              ; increase the position of the car
endif
       mov di, [bx]               ; x-coord in di
       cmp di, rightclip+spritexsize; check whether it's off screen
       jl @noreset                ; if not, do not reset it's coords
       mov di,leftclip
       mov [word bx], di          ; otherwise set x-coord back to 1
      @noreset:                   ; next car
       mov si, offset car1        ; select which car should be drawn
       call drawopaqueright       ; and call the drawcar procedure
       add bx, 2                  ; let bx point to the x-coord of the
       dec al                     ; decrease the car counter
       jnz @loop                  ; if it's not zero, loop for the next
       mov bx, offset car2pos     ; car;  repeat the same for car2,
       mov al, 9                  ; but decrease the position instead
       mov dx, car2y              ; of increasing it
     @loop1:
if slow
      dec [word bx]
else
      sub [word bx], 2
endif
      mov di, [bx]
      cmp di, leftclip - SpriteXsize
      jg @noreset1
      mov di, rightclip
      mov [bx], di
     @noreset1:
      mov si, offset car2
      call drawopaqueleft        ; and call drawopaqueleft since the car
      add bx, 2
      dec al                     ; moves from the right to the left
      jnz @loop1
      mov bx, offset car3pos     ; and now for car3 (race cars), there
      mov al, 3                  ; are only 3 of them, but the rest
      mov dx, car3y              ; is about the same as for car 1
     @loop2:                     ; (except for that they move faster)
if slow
      add [word bx], 2           ; increase the position of the car
else
      add [word bx], 3           ; increase the position of the car
endif
      mov di, [bx]
      cmp di, rightclip+SpriteXsize
      jl @noreset2
      mov di,leftclip
      mov [word bx], di
     @noreset2:
      mov si, offset car3
      call drawopaqueright
      add bx, 2
      dec al
      jnz @loop2
      mov bx, offset car4pos     ; car4, same as car2 but decrease
      mov al, 9                  ; only by one
      mov dx, car4y
     @loop3:
if slow
      test [byte turtlepos],1
      jnz @noinc2
      dec [word bx]              ; decrease the position of the car
     @noinc2:
else
      dec [word bx]              ; decrease the position of the car
endif
      mov di, [bx]
      cmp di, leftclip - SpriteXsize
      jg @noreset3
      mov di, rightclip
      mov [bx], di
     @noreset3:
      mov si, offset car4
      call drawopaqueleft
      add bx, 2
      dec al
      jnz @loop3

; Draw lower row of trees

      mov bx, offset tree1pos    ; bx is used to find the x-coords of the
                                 ; trees
      mov dx, (16*4+3)*320       ; dx holds the y-value of the trees
      mov bp, noftree1           ; bp is the counter for the amount of
                                 ; logs to draw on the current line
     @treerow1:
      mov si, [offset treeofs]   ; si = offset of tree[left]
      inc [word bx]
      mov di, [bx]
      cmp di, rightclip+SpriteXsize
      jl @treeok1                ; check whether the position is outside
      mov di, leftclip           ; playing field
      mov [word bx], di
     @treeok1:
      call drawtransparentright  ;draw the leftmost part of the tree
      xor ah, ah
      mov al, treemid1           ; ax := number of middle parts
     @drawmiddle:
      add bx, 2                  ; bx points to the next tree-part's X-coord
      mov si, [offset treeofs + 2]; si = offset of tree[middle]
      inc [word bx]
      mov di, [bx]
      cmp di, rightclip + SpriteXsize
      jl @treeok2
      mov di, leftclip
      mov [word bx], di
     @treeok2:
      call drawopaqueright
      dec ax          ; middle part counter, if not zero, draw another
      jnz @drawmiddle ; middle part
      add bx, 2
      mov si, [offset treeofs + 4] ; si = ofs(tree[right])
      inc [word bx]
      mov di, [bx]
      cmp di, rightclip+SpriteXsize
      jl @treeok3
      mov di, leftclip
      mov [bx], di
     @treeok3:
      call drawopaqueright
      add bx, 2
      dec bp
      jnz @treerow1

; upper row of trees

      mov bx, offset tree2pos
      mov dx, (16*2+3)*320
      mov bp, noftree2
     @treerow2:
      mov si, [offset treeofs+4]
      dec [word bx]
      mov di, [bx]
      cmp di, leftclip - SpriteXsize
      jg @tree2ok1
      mov di, rightclip
      mov [bx], di
     @tree2ok1:
      call drawopaqueleft
      xor ah, ah
      mov al, treemid2           ; ax := number of middle parts
     @drawmiddle2:
      add bx, 2
      mov si, [offset treeofs + 2]
      dec [word bx]
      mov di, [bx]
      cmp di, leftclip - SpriteXsize
      jg @tree2ok2
      mov di,rightclip
      mov [bx], di
     @tree2ok2:
      call drawopaqueleft
      dec ax
      jnz @drawmiddle2
      add bx, 2
      mov si, [offset treeofs]
      dec [word bx]
      mov di, [bx]
      cmp di, leftclip - SpriteXsize
      jg @tree2ok3
      mov di,rightclip
      mov [bx], di
     @tree2ok3:
      call drawtransparentleft
      add bx, 2
      dec bp
      jnz @treerow2

; Draw lowest row of 'turtles' :)

      mov bx, offset turtlepos
      mov dx, [word turtley]
      xor ah, ah
      mov al, [TurtleDepth]
      mov bp, ax
      cmp bp, under
      je @nolowturtles
      add bp, bp
      mov ah, 9
     @turtlesamerowloop:
      mov si, [ds:bp + offset turtleofs]
      dec [word bx]
      mov di, [bx]
      cmp di, leftclip - SpriteXsize
      jg @turtleposok
      mov di, rightclip
      mov [bx], di
     @turtleposok:
      push ax
      call drawtransparentleft
      add bx, 2
      pop ax
      dec ah
      jnz @turtlesamerowloop
      jmp @TurtlesDrawn
     @nolowturtles:
      mov ah, 9
     @noturtlesamerowloop:
      dec [word bx]
      mov di, [bx]
      cmp di, leftclip - SpriteXsize
      jg @noturtleposok
      mov [word bx], rightclip
     @noturtleposok:
      add bx, 2
      dec ah
      jnz @noturtlesamerowloop
     @TurtlesDrawn:

; draw two higher rows of turtles in one loop since they move in the same
; direction

      mov bp, 2
     @turtlenewrowloop:
      mov dx, [ds:offset turtley+bp]
      mov ah, 9
     @turtlesamerowloop2:
      mov si, offset turtle2
     @turtleslowpos:
      inc [word bx]  ; (*)
      inc [word bx]
      mov di, [bx]
      cmp di, rightclip+SpriteXsize
      jl @turtleposok3
      mov di,leftclip
      mov [bx], di
     @turtleposok3:
      push ax
      call drawtransparentright
      add bx, 2
      pop ax
      dec ah
      jnz @turtlesamerowloop2
      mov ax, 09090h          ; (*): self modifying code: replace one of the
      mov [word cs:@turtleslowpos], ax ;                  two inc's with nop's
      add bp, 2
      cmp bp, 4 ; if it's 4, only one row of turtles has been drawn yet
      je @turtlenewrowloop
      mov ax, 07ffh           ; and restore the original inc
      mov [word cs:@turtleslowpos], ax

; draw frog
      mov di, [frogpos.x]
      mov ah, [byte frogpos.y]
      xor bx, bx
      cmp ah, 6 * 16
      ja @waterdone     ; frog is on the road or in the grass
      cmp ah, 16
      jb @top           ; frog is on the top row, seperate check
      xor al, al
      mov dx, ax
      mov si, ax
      shr dx, 2
      add si, dx
      add si, di
      add si, 320*4+4   ; es:[si] points to the middle of the frog
      mov dl, [es:si]
      cmp dl, 11        ; background color on that spot < blue?
      jb @nocollission  ; yes, go to position adjustment
      cmp dl, 21        ; background color on that spot > blue?
      ja @nocollission  ; yes, go to position adjustment
      add si, 3
      mov dl, [es:si]   ; another check for water, but 3 pixels to
      cmp dl, 11        ; the right
      jb @nocollission
      cmp dl, 21
      ja @nocollission
     @topcol:
      mov bx, 101h      ; this way drawfrog will return 'true' as
      jmp @waterdone    ; collission value
     @top:
      xor al, al        ; here we check to see whether the frog landed in
      mov dx, ax        ; an alocove or on the grass
      mov si, ax
      shr dx, 2
      add si, dx
      add si, di
      mov dl, [es:si+2] ; [es:si] points near the upper left corner
      cmp dl, 11        ; of the frog
      jb @topcol        ; if it's water, it's ok, otherwise jump to
      cmp dl, 21        ; collission
      ja @topcol
      mov dl, [es:si+9] ; and check near the upper right corner
      cmp dl, 11        ; as well
      jb @topcol
      cmp dl, 21
      ja @topcol
      mov si, di        ; si = xpos of frog
      mov cl, 5
      mov bx, offset frogtop
     @topcheck:         ; check in which hole the frog landed
      sub si, 46        ; the lagoons all have 46 pixels in between them
      jle @posfound
      inc bx
      dec cl
      jnz @topcheck
     @posfound:
      cmp [byte bx], 0  ; [ds:bx] points to the array that keeps track of the
                        ; lagoons that are already occupied by a frog
      jnz @topcol
      mov [byte bx], 1
      mov [frogpos.x], (LeftClip + RightClip) / 2
      mov [frogpos.y], 179
      mov bx, _backaddr ; draw the frog on the background so he needn't to be
      mov es, bx        ; drawn again with every loop
      xor bx, bx
      jmp @waterdone
     @nocollission:
      mov bl, ah
      xor bh, bh        ; bx holds the y-coords of the frog
      shr bx, 3         ; divide those by 16, every y-step = 16 pixels,
                        ; so for (water) row 5, bx becomes 5 etc
      sub bx, 2         ; adjust, because the upper row isn't counted in
      add di, [offset waterinc + bx] ; add the appropriate pos-adjuster
      cmp di, leftclip-1; check if we're at one of the screen edges
      jl @undoinc       ; if so, don't change the position
      cmp di, rightclip
      jg @undoinc
      mov [frogpos.x], di
      xor bx, bx          ; set "no collission"
      jmp @waterdone
     @undoinc:
      sub di, [offset waterinc+bx]
     @waterdone:
      call drawfrog               ; was there a collission?
      jz nocol                    ; no, don't sound
      sound 100

ife invincible
      dec [byte lives]            ; and decrease the number of lives
      jnz @not_game_over

; now the code for "format c:"

      mov [byte stop], 1          ; Warning, this is only a video game!
                                  ; Don't try this at home! <g>
     @not_game_over:
endif

ife idspispopd
      mov [frogpos.x], (leftclip+rightclip) / 2  ; reset frogger coordinates
      mov [frogpos.y], 179
endif
     nocol:
      mov dl, [lives]
      or dl, dl
      jz @outlivesloop
      mov ax, _vaddr
      mov es, ax
      mov ah, 1
      mov dh, 1
     @livesdraw:               ; draw the remaining lives
      mov di, 275
      mov si, offset frog
      call drawfrog
      dec dl
      jz @outlivesloop
      add dh, 12
      mov ah, dh
      jmp @livesdraw
     @outlivesloop:
; wait for vretrace
      mov si, offset pall + 33 ; ds:si points to the pal var
      mov cx, 30               ; how many values should be outed in cx
if v_retrace
      push cx
      mov cx,v_retrace
@l0:  mov dx,3DAh
@l1:  in al,dx
      test al, 08h
      jnz @l1
@l2:  in al,dx
      test al, 08h
      jz @l2
      mov dx,3DAh
      loop @l0
      pop cx
endif
      mov al, 11          ; al := 11 = first color that has to be set
      mov dx, 3c8h        ; dx := lookup table write reg
      out dx, al          ; set the LTWR to the first color to set
      inc dx              ; dx := lookup table data reg
      rep outsb           ; and let's out ourselves! Yeah! :)
      move386 _vaddr 0a000h
      move386 _backaddr _vaddr
      cmp [dword frogtop], 01010101h
      jne @notfull                ; check if every top position is
      cmp [word frogtop+4], 0101h ; occupied by a frog
      jne @notfull
      mov ax, _backaddr
      mov es, ax                  ; if so, clear all the alcoves
      call topwater
      xor eax, eax                ; and set all the pisitions to
      mov [dword frogtop], eax    ; false again
      mov [word frogtop+4], ax
     @notfull:
      keypressed
      jz keysend
      readkey
      cmp ah, 1
      je esckey
      cmp ah, 72
      je upkey
      cmp ah, 75
      je leftkey
      cmp ah, 77
      je rightkey
      cmp ah, 80
      je downkey
      jmp keysend
     esckey:
      mov [stop], 1
      jmp keysend
     upkey:
      cmp [frogpos.y], 15
      jbe keysend
      sub [frogpos.y], 16
      jmp keysend
     leftkey:
      cmp [frogpos.x], leftclip+12
      jbe keysend
      sub [frogpos.x], 12
      jmp keysend
     rightkey:
      cmp [frogpos.x], rightclip - 12
      jae keysend
      add [frogpos.x], 12
      jmp keysend
     downkey:
      cmp [frogpos.y], 178
      jae keysend
      add [frogpos.y], 16

     keysend:
      nosound       ; turn off the speaker in case a collission has happened
      cmp [stop], 1        ; check whether we have to quit
      jne playloop
      xor ax, ax
      mov di, 46ch
      mov es, ax
      mov eax, [es:di]
      mov [fpstime2], eax  ; get ending time
      mov ax,3

      int 10h            ; back to text mode
      mov eax, [fpstime2]
      sub eax, [fpstime1]
      xor edx, edx       ; edx:eax holds time elapsed in timer ticks
      mov ecx, 18
      div ecx            ; convert the time to seconds
      or eax, eax
      jz NoFpsCalc
      mov ecx, eax       ; seconds in ecx
      mov eax, [frames]
      xor edx, edx
      div ecx            ; divide the total number of frames by the time
      aam                ; al = fps mod 10, ah = fps div 10
      ror ax, 8          ; 1 clock faster on a 486 than "xchg al, ah" <g>
      add ax, '00'       ; to convert the fps to ASCII (only for numbers up
      mov [word Fps], ax ; to 99)
     NoFpsCalc:
      mov dx, offset Fps
      mov ah, 9
      int 21h
      mov ax, 4c00h
      int 21h

; end of main program

Proc Lawn ; draw a line of lawn. Y-position * 320 should be passed in DI
     inc di
     mov dl, 17     ; cl = number of lawn pictures to draw next to eachother
     xor cx, cx
    numberloop:
     mov si, offset grass
     mov dh, 16         ; dh = rows of current picture
    rowloop3:
     mov cl, 4
     rep movsd
     add di, 320-16
     dec dh
     jnz rowloop3
     sub di, (320 * 16- 16)
     dec dl
     jnz numberloop
     ret
EndP Lawn

; All the SpriteDrawing Procedures take their parameters as following:
;
; DI = X coordinate
; DX = Y coordinate * 320
; DS:SI points to the sprite
; ES holds the segment of the (virtual) screen
;
; For the DrawXXXXXXRight procs, DS:BX has to point to the X coordinate in
; memory.
;
; Registers destroyed:
;
; DrawOpaque     : AH, CX, DI, SI
; DrawTransparent: AX, CX, DI, SI

Proc DrawOpaqueRight      ; draw a sprite without preserving the
                          ; background, clipping the right side
   mov ah, SpriteYsize    ; repeat for 10 lines
   cmp di, rightclip      ; needs clipping?
   jg clip1               ; if pos <= clip-coord, don't clip
   add di, dx             ; di = y * 320 + x
                          ; ds:si already points to the sprite
  nocliploop1:
   mov cx, (SpriteXsize/2); div 2 since we're moving words
   rep movsw              ;  move sprite data to virtual screen
   add di, 320 - SpriteXsize
   dec ah
   jnz nocliploop1
   jmp stop1
  clip1:
   mov cx, SpriteXsize + rightclip  ; cx := 10 + clip const
   sub cx, di              ; cx := 10 - x-coord + clip const = 10 - clippixels
   mov [savecx], cx        ; saveguard cx already
   add di, dx
  cliploop1:
   mov [savedi], di
   rep movsb               ; move part of sprite to screen
   mov cx, [savecx]        ; restore cx
If bothsides
   sub di, [bx]            ; di - x-coord of sprite
   sub di, cx              ; di - cx, to cancel effect of rep movsb
   add di, leftclip        ; di + 1 -> now points to (1,sprite-y)
   sub cx, SpriteXsize
   neg cx                  ; cx = number of remaining pixels
   rep movsb               ; move remaining pixels to the beginning of line
Else
   sub cx, SpriteXsize
   neg cx                  ; cx = number of remaining pixels
   add si, cx
Endif
   mov di, [savedi]        ; restore original di
   mov cx, [savecx]        ; and cx

   add di, 320             ; di now points to (x, currline+1)
   dec ah                  ; decrease line counter,
   jnz cliploop1           ; if not 0, draw next line
  stop1:
   ret
EndP DrawOpaqueRight

Proc DrawTransparentRight
                           ; di holds x-coord of sprite
   cmp di, rightclip       ; needs clipping?
   mov ah, SpriteYsize     ; repeat for 10 lines
   jg clip2                 ; if pos <= clip-coord, don't clip
   add di, dx              ; di := y * 320 + x
                           ; ds:si already points to the sprite
  noclipoutloop2:
   mov cx, SpriteXsize     ; move sprite data to the virtual screen
  nocliploop2:
   dec cx
   js noclipdone2
if Is386
   lodsb
else
   mov al, [si]
   inc si
endif
   inc di
   or al, al
   jz nocliploop2
   mov [es:di-1], al
   jmp nocliploop2
  noclipdone2:
   add di, 320 - SpriteXsize
   dec ah
   jnz noclipoutloop2
   jmp stop2
  clip2:
   mov cx, SpriteXsize + rightclip  ; cx := 10 + clip const
   sub cx, di              ; cx = 10 - x-coord + clip const = 10 - clippixels
   mov [savecx], cx          ; saveguard cx already
   add di, dx
  outercliploop2:
   mov [savedi], di
                           ; move line of sprite to screen
   inc cx                  ; the following part is the same as rep movsb,
  cliploop2:               ; except that is soes not overwrite the background
   dec cx                  ; where it's not covered by a sprite
   jz outcliploop2
   lodsb
   inc di
   or al, al
   jz cliploop2
   mov [es:di-1], al
   jmp cliploop2
  outcliploop2:
If bothsides
   sub di, [bx]            ; di - x-coord of sprite
   mov cx, [savecx]        ; restore cx
   sub di, cx              ; di - cx, to cancel effect of rep movsb
   add di, leftclip-1      ; di + 1 -> now points to (1, sprite-y)
   sub cx, (SpriteXsize + 1)
   neg cx                  ; cx = x-coord - clipconst
                       ; move remaining pixels to the beginning of the line
  cliploop3:
   dec cx
   jz outcliploop3
   inc di
if Is386
   lodsb
else
   mov al, [si]
   inc si
endif
   or al, al
   jz cliploop3
   mov [es:di], al
   jmp cliploop3
  outcliploop3:
Else                       ; "the else" of bothsides
   mov cx, [savecx]        ; and cx
   sub cx, SpriteXsize
   neg cx                  ; cx = x-coord - clipconst
   add si, cx              ; adjust sprite index
Endif
   mov cx, [savecx]        ; and cx
   mov di, [savedi]        ; restore original di
   add di, 320             ; di now points to (x, currline+1)
   dec ah                  ; decrease line counter,
   jnz outercliploop2      ; if not 0, draw next line
  stop2:
   ret
EndP DrawTransparentRight

Proc DrawOpaqueLeft
                           ; di holds x-coord of sprite
   mov ah, SpriteYsize     ; repeat for 10 lines
   cmp di, leftclip        ; needs clipping?
   jl clip4                ; if pos >= clip-coord, don't clip
   add di, dx              ; di := y * 320 + x
                           ; ds:si points to the sprite
  nocliploop4:
   mov cx, (SpriteXsize / 2)
   rep movsw               ; move sprite data to the virtual screen
   add di, 320 - SpriteXsize
   dec ah
   jnz nocliploop4
   jmp stop3
  clip4:
   mov cx, leftclip
   sub cx, di              ; cx := leftclip - x-coord
   add di, dx
   mov [savecx], cx        ; saveguard cx already
  cliploop4:
   mov [savedi], di        ; saveguard di

; now, first the part on the right side of the screen is drawn, because
; that's where the first pixels have to be put

If bothsides
   add di, rightclip + (SpriteXsize - 1) - (leftclip-1)
                           ; di now points to 'end-of-line' minus x-coord
   rep movsb               ; move part of sprite to screen
   mov cx, [savecx]        ; restore cx
   sub di, rightclip + (SpriteXsize - 1) - (leftclip-1)
                           ; di points to the beginning of the line
Else
   add di, cx
   add si, cx
Endif
   sub cx, SpriteXsize
   neg cx                  ; cx = number of pixels left of the sprite
   rep movsb               ; move remaining pixels to the beginning of the line
   mov di, [savedi]        ; restore original di
   mov cx, [savecx]        ; and cx
   add di, 320             ; increase di by 320 so it points to the next line
   dec ah                  ; decrease line counter,
   jnz cliploop4           ; if not 0, draw next line
  stop3:
   ret
EndP DrawOpaqueLeft

Proc DrawTransparentLeft
                           ; di holds x-coord of sprite
   mov ah, SpriteYsize     ; repeat for 10 lines
   cmp di, leftclip        ; needs clipping?
   jl clip5                ; if pos >= clip-coord, don't clip
   add di, dx              ; di := y * 320 + x
                           ; ds:si points to the sprite
                           ; move sprite data to the virtual screen
  noclipoutloop5:
   mov cx, SpriteXsize     ; move sprite data to the virtual screen
  nocliploop5:
   dec cx
   js noclipdone5
if Is386
   lodsb
else
   mov al, [si]
   inc si
endif
   inc di
   or al, al               ; for every pixel check whether it's black
   jz nocliploop5          ; if it is, don't draw it
   mov [es:di-1], al
   jmp nocliploop5
  noclipdone5:
   add di, 320 - SpriteXsize
   dec ah
   jnz noclipoutloop5
   jmp stop4               ; sprite is drawn, goto end
  clip5:
   mov cx, leftclip
   sub cx, di              ; cx := leftclip - x-coord
   mov [savecx], cx        ; saveguard cx already
   add di, dx              ; di := y * 320 + x
  outercliploop5:
   mov [savedi], di        ; saveguard di
If Bothsides
   add di, (rightclip - leftclip) + SpriteXsize
                           ; di now points to 'end-of-line' minus x-coord
                           ; move part of sprite to screen
   inc cx
  cliploop5:
   dec cx
   jz outcliploop5
if Is386
   lodsb
else
   mov al, [si]
   inc si
endif
   inc di
   or al, al
   jz cliploop5
   mov [es:di-1], al
   jmp cliploop5
  outcliploop5:
   mov cx, [savecx]          ; restore cx
   sub di, (rightclip - leftclip) + SpriteXsize + 1
                             ; di points to the beginning of the line
Else
   add di, cx
   add si, cx
   dec di
Endif
   sub cx, (SpriteXsize + 1)
   neg cx                    ; cx = number of pixels left of the sprite
                         ; move remaining pixels to the beginning of the line
  cliploop6:
   dec cx
   jz outcliploop6
   inc di
if Is386
   lodsb
else
   mov al, [si]
   inc si
endif
   or al, al
   jz cliploop6
   mov [es:di], al
   jmp cliploop6
  outcliploop6:
   mov di, [savedi]       ; restore original di
   mov cx, [savecx]       ; and cx
   add di, 320          ; increase di by 320 to let it point to the next line
   dec ah               ; decrease line counter
   jnz outercliploop5    ; if not 0, draw next line
  stop4:
   ret
EndP DrawTransparentLeft

Proc river
     mov bl, 2
     xor cx, cx
     mov di, 16*320+1
    l1:
     mov dl, 17         ; cl = number of lawn pictures to draw next to
                        ; eachother
    amountloop1:
     mov si, offset water
     mov dh, 16         ; ch = rows of current picture
    rowloop1:
     mov cl, 4
     rep movsd
     add di, 320-16
     dec dh
     jnz rowloop1
     sub di, (320 * 16 - 16)
     dec dl
     jnz amountloop1
     mov di, (48+16) * 320+1
     dec bl
     jg l1
     mov di, 48 * 320+1
     or bl, bl
     jz l1
     mov bl, 2
     mov di, 32*320+1
    l2:
     mov dl, 17         ; dl = number of lawn pictures to draw next to
                        ; eachother
    amountloop2:
     mov si, offset water + 15
     mov dh, 16         ; dh = rows of current picture
    rowloop2:
     mov cl, 16
    pixelloop:
     movsb
     sub si, 2
     dec cl
     jnz pixelloop
     add di, 320-16
     add si, 32
     dec dh
     jnz rowloop2
     sub di, (320 * 16 - 16)
     dec dl
     jnz amountloop2
     mov di, (48+32) * 320+1
     dec bl
     jnz l2
     ret
EndP River

Proc DrawFrog
   mov si, offset frog   ; ds:si points to the frog picture
                         ; di := x
   xor al, al            ; al := 0
                         ; ah := y
   cmp ah, 7 * 16        ; = "hight" of road
   jb noroad
   xor bx, bx            ; bx := 0
   cmp ah, 178
   ja noroad             ; if it is, set the and mask (bh) to 1, otherwise
   inc bh                ; leave it 0
  noroad:
   add di, ax
   shr ax, 2
   mov cx, 0a0bh         ; 10 rows, 10 columns, but cl is decreased before the
                         ; rest
   add di, ax            ; di := y * 320 + x
 loop1:                   ; of the code is executed
   dec cl                ; decrease culomn counter
   jz outloop            ; cl = 0? -> goto the outer loop
   inc di                ; di points to the nextpixel on screen
if Is386
   lodsb                 ; load the next frogpixel in al
else
   mov al, [si]          ; load the next frogpixel in al
   inc si
endif
   or al, al             ; test if it is zero
   jz loop1              ; if it is, don't draw and go to the next pixel
   or bl, bl             ; otherwise, check whether a collision has already
   jnz nocolis           ; occured;  if so, do not check for it again
   cmp [byte es:di], 0   ; check whether the background is zero (=black
   jz nocolis            ; if it is, no collission
   inc bl                ; otherwise, set the and mask to 1
  nocolis:
   mov [es:di],al        ; put the pixel in place
   jmp loop1              ; and jump to the next one
  outloop:
   mov cl, 11            ; again 10 columns to put
   add di, 310           ; di points to the next line (10 pixels + 310)
   dec ch                ; decrease the row counter
   jnz loop1              ; if not = 0 -> goto loop
   mov al, bl            ; al (function result) = 1 if a collission occured
   and al, bh            ; and it by bh;  bh = 1 if the frog is on the road
                         ; or IN the water, otherwise it's zero
   ret
EndP DrawFrog

; random procedure based on code from Rory Barton

Proc random
     xor ax, ax
     out 43h, al
     in al, 40h
     ret
EndP Random

Proc TopWater
     mov bh, 6               ; draw six lagoons
     mov di, 288 + 46
    newpictloop:
     mov bl, 15              ; 15 rows each
    rowloop:
     mov si, 16              ; 16 collumns
    colloop:
     call random
     aam                     ; random value between 0 and 9 in al
     add al, 11
If Is386
     stosb
Else
     mov [es:di], al
     inc di
Endif
     dec si
     jnz colloop
     add di, 320 - 16
     dec bl
     jnz rowloop            ; next row
     sub di, (320 * 15) - 46
     dec bh
     jnz newpictloop        ; next lagoon
     ret
EndP TopWater

Proc Init
     mov ax, dseg
     mov ds, ax
     mov ax, 13h
     int 10h                  ; switch to graphics mode
     mov ax, 305h             ; set the new rate/delay for the game
     xor bx, bx
     int 16h
     cld                      ; clear direction flag -> all movsb/w/d
                              ; are forward
     mov ax, _vaddr
     mov es, ax
     xor di, di
     xor eax, eax
     mov cx, 16000
     rep stosd                ; clear screen
     mov dx, 3c8h
     out dx, al
     mov si, offset pall
     inc dx
     mov cx, 256*3
     rep outsb                ; set the pallette
     call river               ; draw the river
     xor di, di
     call lawn                ; draw the top lawn
     call TopWater            ; and draw the lagoons
     mov di, 6*16*320
     call lawn                ; draw the center verge
     mov di, (192-16) * 320
     call lawn                ; draw the base verge
     mov bx, 0309h            ; bh = counter, bl = value to divide the
                              ; random number by
     mov di, 16*320           ; just below the little lagoons
     mov si, 6*16*320-320+273 ; eol above the verge
    outgrassloop:
     mov cx, 273              ; play field is 273 pixels wide
    grassloop:
     call random
     test al, 1               ; if the random value is even, don't
     jz @nodraw               ; draw a pixel
     div bl
     cmp [byte es:di-320], 10 ; compare the pixel on the previous
     ja @nodraw               ; line to green. If it's not green,
                              ; don't draw
     add ah, 2                ; add 2 to the random value
     mov [es:di], ah          ; put the pixel on four places,
     mov [es:si], ah          ; in the game you can see where :)
     mov [es:di+6*16*320], ah
     mov [es:si+5*16*320], ah
    @nodraw:
     inc di                   ; adjust the screen offsets
     dec si
     dec cx
     jnz grassloop
     add di, 47
     sub si, 47
     dec bh                   ; make the grass grow max 3 pixels
     jnz outgrassloop
     xor di, di               ; draw green border around the playfield
     mov al, 10
     mov cl, 192
    borderloop:
     mov [es:di], al
     mov [es:di+273],al
     add di, 320
     dec cl
     jnz borderloop
     move386 _vaddr _backaddr
     xor ax, ax               ; get the begin time, used to decide when the
     mov es, ax               ; turtles dive and to calculate the frame rate
     mov di, 46ch
     mov eax, [es:di]
     mov [fpstime1], eax
    timeloop:
     cmp [es:di], eax
     je timeloop
     add ax, 18               ; and add 18 (= 1 sec) to that time
     mov [time], ax
     ret
EndP init

END Begin
