;**********************************
;***                            ***
;***     A Fast 3D LandScape    ***
;***            by TTT          ***
;***                            ***
;***  ttaipale@phoenix.oulu.fi  ***
;**********************************
;Copyright (C) TTT, All rights reserved.
;
;The routine to set ModeX by Draeden/VLA
;
;NOTE: The land data can be anything. This land is a FractInt
;      plasma-cloud converted to ASCII.
;
;*** Land data now generated at runtime, by Tenie Remmel
;    This saves a HUGE amount of space.


        DOSSEG
        .MODEL  small
        .STACK  200h
        .386

;***************
;*** EQUATES ***
;***************
ClipT           equ     0
ClipB           equ     239
ClipL           equ     0
ClipR           equ     319
MidX            equ     160
MidY            equ     160

GridX           equ     1                       ;Size of the grid
GridY           equ     50
GridZ           equ     50
GyAdd           equ     GridY*2*2
GridPoints      equ     GridX*GridY*GridZ
DIST            equ     15                      ;Distance between points
SVAL_2          equ     6                       ;2nd shift value

;*************************
;*** Data for the land ***
;*************************
Land    segment use16 'BSS'
        db      64000 dup(0)
Land    ends

PointData segment       use16
        dd      GridPoints*3 dup(0)
PointData ends

PointData2 segment      use16
        dw      GridPoints*3 dup(0)
PointData2 ends

        .CODE

;******************
;INCLUDES
;******************
INCLUDE modex.inc
        ALIGN   2
INCLUDE scos.dd           ;Sine and cosine tables: value * 16384

        ALIGN   2
YTable  label   word
        INCLUDE ytab80.dw

        ALIGN   2
YTab    label   word
        INCLUDE ytab.dw

Palette         label   byte
                i = 0
                REPT    63
                        db      0,0,i
                        i = i + 1
                ENDM
                i = 0
                REPT    63
                        db      0,i,0
                        i = i + 1
                ENDM
                i = 0
                REPT    63
                        db      i,i,i
                        i = i + 1
                ENDM
                db      768 dup(63)

;******************
;VARIABLES
;******************
                ALIGN   2
CurrentPage     dw      32000
DisplayPage     dw      0
AngleX          dw      0
AngleY          dw      256
AngleZ          dw      200
Depth           dd      800
SinX            dd      0
SinY            dd      0
SinZ            dd      0
CosX            dd      0
CosY            dd      0
CosZ            dd      0
Add1            dd      0
Add2            dd      0
Add3            dd      0
Add4            dd      0
Add5            dd      0
Add6            dd      0
Add7            dd      0
Add8            dd      0
Add9            dd      0
XPos            dw      0
YPos            dw      0
XDir            dw      0
YDir            dw      0
CtrlPts         dd      6*3 dup(0)

Points          label   word
                dw      (DIST*GRIDX)/2       ,(DIST*GRIDY)/2,(DIST*GRIDZ)/2
                dw      ((DIST*GRIDX)/2)-DIST,(DIST*GRIDY)/2,(DIST*GRIDZ)/2

                dw      (DIST*GRIDX)/2,(DIST*GRIDY)/2       ,(DIST*GRIDZ)/2
                dw      (DIST*GRIDX)/2,((DIST*GRIDY)/2)-DIST,(DIST*GRIDZ)/2

                dw      (DIST*GRIDX)/2,(DIST*GRIDY)/2,(DIST*GRIDZ)/2
                dw      (DIST*GRIDX)/2,(DIST*GRIDY)/2,((DIST*GRIDZ)/2)-DIST

;**********************************
;*** ROTATES THE CONTROL POINTS ***
;**********************************
        ALIGN   2
RotControl PROC
        mov     si, [cs:AngleX]
        shl     si, 2
        mov     eax, [cs:Sinetable+si]
        mov     ebx, [cs:Costable+si]
        mov     [cs:SinX], eax
        mov     [cs:CosX], ebx

        mov     si, [cs:AngleY]
        shl     si, 2
        mov     eax, [cs:Sinetable+si]
        mov     ebx, [cs:Costable+si]
        mov     [cs:SinY], eax
        mov     [cs:CosY], ebx

        mov     si, [cs:AngleZ]
        shl     si, 2
        mov     eax, [cs:Sinetable+si]
        mov     ebx, [cs:Costable+si]
        mov     [cs:SinZ], eax
        mov     [cs:CosZ], ebx

        mov     cx, 6
        sub     si, si
        sub     di, di

        ALIGN   2
Rotloop:
        push    cx
        mov     bx, [cs:Points+si]              ;Get X
        mov     cx, [cs:Points+si+2]            ;Get Y
        mov     bp, [cs:Points+si+4]            ;Get Z

        movsx   ebx, bx
        movsx   ecx, cx
        movsx   ebp, bp

        push    si
        push    di

        mov     eax, [cs:CosX]                  ;Around X
        imul    ecx
        mov     edi, eax
        mov     eax, [cs:SinX]
        imul    ebp
        sub     edi, eax
        mov     eax, [cs:SinX]
        imul    ecx
        mov     esi, eax
        mov     eax, [cs:CosX]
        imul    ebp
        add     esi, eax
        sar     esi, 8
        sar     edi, 8
        mov     ecx, edi
        mov     ebp, esi

        mov     eax, [cs:CosZ]                  ;Around Z
        imul    ebx
        mov     edi, eax
        mov     eax, [cs:SinZ]
        imul    ecx
        sub     edi, eax
        mov     eax, [cs:SinZ]
        imul    ebx
        mov     esi, eax
        mov     eax, [cs:CosZ]
        imul    ecx
        add     esi, eax
        sar     edi, 14
        sar     esi, 14
        mov     ebx, edi
        mov     ecx, esi

        mov     eax, [cs:CosY]                  ;around Y
        imul    ebx
        mov     edi, eax
        mov     eax, [cs:SinY]
        imul    ebp
        sub     edi, eax
        mov     eax, [cs:SinY]
        imul    ebx
        mov     esi, eax
        mov     eax, [cs:CosY]
        imul    ebp
        add     esi, eax
        sar     esi, 14
        sar     edi, 14
        mov     ebx, edi
        mov     ebp, esi

        pop     di
        pop     si

        mov     [cs:CtrlPts+di], ebx
        mov     [cs:CtrlPts+di+4], ecx
        mov     [cs:CtrlPts+di+8], ebp

        pop     cx
        add     di, 12
        add     si, 6
        dec     cx
        jnz     Rotloop
        ret
RotControl EndP

;***********************************************************
;*** CALCULATES THE DISTANCES BETWEEN THE CONTROL POINTS ***
;***********************************************************
        ALIGN   2
CalculateDistances PROC
        mov     eax, [cs:CtrlPts]
        mov     ebx, [cs:CtrlPts+4]
        mov     ecx, [cs:CtrlPts+12]
        mov     edx, [cs:CtrlPts+16]
        sub     ecx, eax
        sub     edx, ebx
        mov     [cs:Add1], ecx
        mov     [cs:Add2], edx
        mov     eax, [cs:CtrlPts+8]
        mov     ebx, [cs:CtrlPts+20]
        sub     ebx, eax
        mov     [cs:Add3], ebx

        mov     eax, [cs:CtrlPts+24]
        mov     ebx, [cs:CtrlPts+28]
        mov     ecx, [cs:CtrlPts+36]
        mov     edx, [cs:CtrlPts+40]
        sub     ecx, eax
        sub     edx, ebx
        mov     [cs:Add4], ecx
        mov     [cs:Add5], edx
        mov     eax, [cs:CtrlPts+32]
        mov     ebx, [cs:CtrlPts+44]
        sub     ebx, eax
        mov     [cs:Add6], ebx

        mov     eax, [cs:CtrlPts+48]
        mov     ebx, [cs:CtrlPts+52]
        mov     ecx, [cs:CtrlPts+60]
        mov     edx, [cs:CtrlPts+64]
        sub     ecx, eax
        sub     edx, ebx
        mov     [cs:Add7], ecx
        mov     [cs:Add8], edx
        mov     eax, [cs:CtrlPts+56]
        mov     ebx, [cs:CtrlPts+68]
        sub     ebx, eax
        mov     [cs:Add9], ebx
        ret
CalculateDistances EndP

;***********************************************************
;*** CALCULATES THE GRID FROM THE ROTATED CONTROL POINTS ***
;***********************************************************
        ALIGN   2
MakeGrid PROC
        mov     ax, PointData
        mov     ds, ax
        sub     si, si
        mov     edx, [cs:CtrlPts]
        mov     ebp, [cs:CtrlPts+4]
        mov     edi, [cs:CtrlPts+8]
        mov     cx, GridZ
        mov     ebx, [cs:Add1]

        ALIGN   2
ZLoop:  push    cx
        push    edx
        push    ebp
        push    edi
        mov     cx, GridY

        ALIGN   2
YLoop:  push    cx
        push    edx
        push    ebp
        push    edi
        mov     cx, GridX

        ALIGN   2
XLoop:  mov     eax, edx
        sar     eax, SVAL_2
        mov     ds:[si], eax

        mov     eax, ebp
        sar     eax, SVAL_2
        mov     ds:[si+4], eax

        mov     eax, edi
        sar     eax, SVAL_2
        mov     ds:[si+8], eax

        add     edx, ebx
        add     ebp, [cs:Add2]
        add     edi, [cs:Add3]

        add     si, 12
        dec     cx
        jnz     XLoop

        pop     edi
        pop     ebp
        pop     edx

        add     edx, [cs:Add4]
        add     ebp, [cs:Add5]
        add     edi, [cs:Add6]

        pop     cx
        dec     cx
        jnz     YLoop

        pop     edi
        pop     ebp
        pop     edx

        add     edx, [cs:Add7]
        add     ebp, [cs:Add8]
        add     edi, [cs:Add9]

        pop     cx
        dec     cx
        jnz     ZLoop
        ret
MakeGrid EndP

;**********************************************************
;*** TRANSFORMS THE GRID POINTS INTO SCREEN COORDINATES ***
;**********************************************************
        ALIGN   2
TransformGrid PROC
        mov     ax, PointData
        mov     ds, ax
        mov     ax, PointData2
        mov     es, ax
        mov     cx, GridPoints
        sub     si, si
        sub     di, di

        ALIGN   2
TransformLoop:
        push    cx

        mov     ebx, ds:[si]
        mov     ecx, ds:[si+4]
        mov     ebp, ds:[si+8]
        add     ebp, [cs:Depth]
        cmp     ebp, 20
        jge     NoZTrunc

        mov     ebp, 20
        mov     eax, ebx
        movsx   dx, ah
        sal     eax, 8
        idiv    bp
        add     ax, MidX
        mov     es:[di], ax

        mov     eax, ecx
        movsx   dx, ah
        sal     eax, 8
        idiv    bp
        add     ax, MidY
        mov     es:[di+2], ax

        pop     cx
        add     si, 12
        add     di, 6
        dec     cx
        jnz     TransformLoop
        ret

        ALIGN   2
NoZTrunc:
        mov     eax, ebx
        movsx   dx, ah
        sal     eax, 8
        idiv    bp
        add     ax, MidX
        mov     es:[di], ax

        mov     eax, ecx
        movsx   dx, ah
        sal     eax, 8
        idiv    bp
        add     ax, MidY
        mov     es:[di+2], ax

        pop     cx
        add     si, 12
        add     di, 6
        dec     cx
        jnz     TransformLoop
        ret
TransformGrid EndP

;***********************************
;*** Maps the land over the grid ***
;***********************************
        ALIGN   2
PutLand PROC
        mov     ax, Land
        mov     ds, ax
        mov     ax, PointData2
        mov     es, ax
        sub     si, si

        mov     bx, [cs:YPos]
        shl     bx, 1
        add     si, [cs:YTab+bx]
        add     si, [cs:XPos]
        sub     di, di
        mov     bp, GridZ

        ALIGN   2
PZLoop: mov     cx, GridY
PYLoop: sub     bx, bx
        mov     ax, es:[di+2]
        mov     bl, BYTE PTR ds:[si]
        mov     es:[di+4], bx
        sar     bx, 1
        sub     ax, bx
        mov     es:[di+2], ax

        inc     si
        add     di, 6
        dec     cx
        jnz     PYLoop

        add     si, 320-GridY
        dec     bp
        jnz     PZLoop
        ret
PutLand EndP

;*************************
;*** CLEARS THE SCREEN ***
;*************************
        ALIGN   2
Clear   PROC
        mov     dx, SC_INDEX
        mov     ax, 0f02h
        out     dx, ax

        mov     ax, 0a000h
        mov     es, ax
        mov     di, [cs:CurrentPage]
        mov     cx, 19200/4
        sub     eax, eax
        rep     stosd
        ret
Clear   EndP

;**********************
;*** Draws the land ***
;**********************
        ALIGN   2
DrawLand PROC
        mov     ax, PointData2
        mov     ds, ax
        mov     ax, 0a000h
        mov     es, ax
        sub     si, si
        mov     di, [cs:CurrentPage]
        mov     bp, GridPoints
        mov     dx, SC_INDEX

        ALIGN   2
DrawLoop:
        mov     ax, ds:[si]
        cmp     ax, ClipL
        jl      ClipThis
        cmp     ax, ClipR
        jg      ClipThis
        mov     bx, ds:[si+2]
        cmp     bx, ClipT
        jl      ClipThis
        cmp     bx, ClipB
        jg      ClipThis

        mov     cx, ax                  ;Store X
        sar     ax, 2
        shl     bx, 1
        add     ax, [cs:YTable+bx]
        mov     bx, ax

        and     cl, 011b
        mov     ax, 0100h + 2
        shl     ah, cl
        out     dx, ax

        mov     ax, ds:[si+4]
        mov     es:[di+bx], al
        add     si, 6
        dec     bp
        jnz     DrawLoop
        ret

        ALIGN   2
ClipThis:
        add     si, 6
        dec     bp
        jnz     DrawLoop
        ret
DrawLand EndP

;*************************************
;*** MOVES THE LAND IN Y-DIRECTION ***
;*************************************
        ALIGN   2
MoveLandY PROC
        cmp     [cs:YDir], 1
        jz      ThatWayY
        add     [cs:YPos], 1
        cmp     [cs:YPos], 200-GridZ
        jnz     NoCYDir
        mov     [cs:YDir], 1
NoCYDir:
        ret

        ALIGN   2
ThatWayY:
        sub     [cs:YPos], 1
        cmp     [cs:YPos], 0
        jnz     NoCYDir
        mov     [cs:YDir], 0
        ret
MoveLandY EndP

;*************************************
;*** MOVES THE LAND IN X-DIRECTION ***
;*************************************
        ALIGN   2
MoveLandX PROC
        cmp     [cs:XDir], 1
        jz      ThatWayX
        add     [cs:XPos], 1
        cmp     [cs:XPos], 320-GridY
        jnz     NoCXDir
        mov     [cs:XDir], 1
NoCXDir:
        ret

        ALIGN   2
ThatWayX:
        sub     [cs:XPos], 1
        cmp     [cs:XPos], 0
        jnz     NoCXDir
        mov     [cs:XDir], 0
        ret
MoveLandX EndP

GETPIX      macro  x, y             ;Getpixel macro

            mov bx,x
            mov cx,y
            add bh,cl
            shl cx,6
            add bx,cx
            mov al,es:[bx]
EndM

SETPIX      macro  x, y             ;Setpixel macro

            mov bx,x
            mov cx,y
            add bh,cl
            shl cx,6
            add bx,cx
            mov es:[bx],al
endm

PUSHI       macro a,b,c,d,e,f       ;Multiple push

            ifnb <a>
              push a
              PUSHI b,c,d,e,f
            endif
endm

GenLand     proc

            pusha                   ;Save registers
            push ds
            push es

            push cs                 ;DS = CS
            pop ds

            push 0                  ;ES = 0
            pop es
            mov eax,es:[046Ch]      ;Seed RNG
            mov dword ptr RandNum,eax

            mov ax,seg Land         ;ES = land segment
            mov es,ax

            mov ax,192              ;Set corner pixels to random colors
            call Rand
            inc ax
            mov es:[0],al
            mov ax,192
            call Rand
            inc ax
            mov es:[319],al
            mov ax,192
            call Rand
            inc ax
            mov es:[63360],al
            mov ax,192
            call Rand
            inc ax
            mov es:[63999],al

            PUSHI 199 319 0 0       ;Draw plasma
            call Plasma

            pop es                  ;Restore registers
            pop ds
            popa
            ret                     ;Return

GenLand     endp

;**************************** Plasma -- Recursive plasma procedure

Plasma      proc

            push bp                 ;Set up stack frame
            mov bp,sp

            pusha                   ;Save all registers

            mov ax,[bp+8]           ;x2 - x1 < 2, done
            sub ax,[bp+4]
            cmp ax,2
            jb P_Done

            mov ax,[bp+4]           ;Get values
            mov bx,[bp+6]
            mov cx,[bp+8]
            mov dx,[bp+10]

            mov si,ax               ;x = (x1 + x2) / 2
            add si,cx
            sar si,1
            mov di,bx               ;y = (y1 + y2) / 2
            add di,dx
            sar di,1

            pusha                   ;Save registers

            PUSHI bx si bx cx bx ax ;Adjust top side
            call Adjust
            PUSHI dx si dx cx dx ax ;Adjust bottom side
            call Adjust
            PUSHI di ax dx ax bx ax ;Adjust left side
            call Adjust
            PUSHI di cx dx cx bx cx ;Adjust right side
            call Adjust

            GETPIX si,di            ;Center pixel on, recurse
            test al,al
            jnz P_Recurse

            xor dx,dx               ;Zero DX, AH
            xor ah,ah
            GETPIX [bp+4],[bp+6]    ;DX = sum of corners
            add dx,ax
            GETPIX [bp+8],[bp+6]
            add dx,ax
            GETPIX [bp+4],[bp+10]
            add dx,ax
            GETPIX [bp+8],[bp+10]
            add dx,ax

            shr dx,2                ;DX = average of corners
            mov al,dl
            SETPIX si,di            ;Set center pixel

P_Recurse:  popa                    ;Restore registers
            PUSHI di si bx ax       ;Plasma x1, y1, x, y
            call Plasma
            PUSHI di cx bx si       ;Plasma x, y1, x2, y
            call Plasma
            PUSHI dx cx di si       ;Plasma x, y, x2, y2
            call Plasma
            PUSHI dx si di ax       ;Plasma x1, y, x, y2
            call Plasma

P_Done:     popa                    ;Restore registers
            pop bp                  ;Delete stack frame
            ret 8                   ;Return, pop args

Plasma      endp

;**************************** Adjust -- Random adjust pixel midpoint

Adjust      proc

            push bp                 ;Set up stack frame
            mov bp,sp

            pusha                   ;Save all registers

            GETPIX [bp+12],[bp+14]  ;Check pixel
            test al,al              ;Already on, done
            jnz A_Done

            mov ax,[bp+8]           ;BX = |x2 - x1| + |y2 - y1|
            sub ax,[bp+4]
            mov bx,[bp+10]
            sub bx,[bp+6]
            test ax,ax              ;get absolute values...
            jge $+4
            neg ax
            test bx,bx
            jge $+4
            neg bx
            add bx,ax

            mov ax,bx               ;AX = BX * 2 + 1
            add ax,ax
            inc ax

            mov bx,ax               ;Get random number
            call Rand               ;positive and negative
            sar bx,1
            sub ax,bx
            mov dx,ax               ;in DX

            GETPIX [bp+4],[bp+6]    ;AX = average of ends + DX
            push ax
            GETPIX [bp+8],[bp+10]
            pop bx
            xor ah,ah
            add al,bl
            adc ah,0
            sar ax,1
            add ax,dx

            cmp ax,1                ;Make sure it's in range
            jge $+5
            mov ax,1
            cmp ax,192
            jle $+5
            mov ax,192

            SETPIX [bp+12],[bp+14]  ;Set center pixel

A_Done:     popa                    ;Restore registers
            pop bp                  ;Delete stack frame
            ret 12                  ;Return, pop args

Adjust      endp

;**************************** Rand -- Random number generator

Rand        proc

            push edx
            push ebx
            mov bx,ax
            imul eax,RandNum,015A4E35h
            inc eax
            mov RandNum,eax
            shr eax,15
            xor dx,dx
            div bx
            mov ax,dx
            pop edx
            pop ebx
            ret

RandNum     dd ?

Rand        endp

;********************
;*** MAIN PROGRAM ***
;********************
START:  call GenLand
        @SetModeX x320, y480, 2, 320

        mov     ax, cs
        mov     ds, ax
        mov     si, OFFSET Palette
        mov     ax, 0
        mov     cx, 256
        call    WritePalette

Mainloop:
        call    Clear
        call    RotControl
        call    CalculateDistances
        call    MakeGrid
        call    TransformGrid
        call    PutLand
        call    DrawLand

        mov     bx, [cs:CurrentPage]
        mov     ax, [cs:DisplayPage]
        mov     [cs:CurrentPage], ax
        mov     [cs:DisplayPage], bx
        call    Set_Start_Offset

        call    MoveLandX
;        call    MoveLandY

        add     [cs:AngleX], 4
        cmp     [cs:AngleX], 1023
        jle     NoWrap
        sub     [cs:AngleX], 1023
NoWrap:
;        add     [cs:AngleZ], 4
        cmp     [cs:AngleZ], 1023
        jle     NoWrap2
        sub     [cs:AngleZ], 1023
NoWrap2:
;        add     [cs:AngleY], 4
        cmp     [cs:AngleY], 1023
        jle     NoWrap3
        sub     [cs:AngleY], 1023
NoWrap3:

        call    Wait_FVR1

        mov     ah, 1
        int     16h
        jz      Mainloop
        mov     ah, 0
        int     16h
        cmp     al, 27
        jz      Bye
        jmp     Mainloop

BYE:    mov     ax, 3h
        int     10h
        mov     ax, 4c00h
        int     21h
END START

        end

