unit x3dunit2;

{ mode-x 3D unit - xhlin-procedure by Sean Palmer }
{ Optimized by Luis Mezquita Raya                 }

{$g+}

interface

const vidseg:word=$a000;
      divd:word=128;
      dist:word=200;
      minx:word=0;
      maxx:word=319;
      border:boolean=false;

var   ctab:array[byte] of integer;
      stab:array[byte] of integer;
      address:word;
      triangles:boolean;

Procedure setborder(col:byte);
Procedure setpal(c,r,g,b:byte);
Procedure retrace;
Procedure setmodex;
Procedure setaddress(ad:word);
Procedure cls;
Procedure polygon(x1,y1,x2,y2,x3,y3,x4,y4:integer; c:byte);
Function  cosinus(i:byte):integer;
Function  sinus(i:byte):integer;

implementation

var   xpos:array[0..199,0..1] of integer;

Procedure setborder(col:byte); assembler;
asm
        xor ch,ch
        mov cl,border
        jcxz @out
        mov dx,3dah
        in al,dx
        mov dx,3c0h
        mov al,11h+32
        out dx,al
        mov al,col
        out dx,al
@out:
end;

Procedure setpal(c,r,g,b:byte); assembler;
asm
        mov dx,3c8h
        mov al,[c]
        out dx,al
        inc dx
        mov al,[r]
        out dx,al
        mov al,[g]
        out dx,al
        mov al,[b]
        out dx,al
end;

Procedure retrace; assembler;
asm
        mov dx,3dah;
@vert1: in al,dx
        test al,8
        jz @vert1
@vert2: in al,dx
        test al,8
        jnz @vert2
end;

Procedure setmodex; assembler;
asm
        mov ax,13h
        int 10h
        mov dx,3c4h
        mov ax,0604h
        out dx,ax
        mov ax,0f02h
        out dx,ax
        mov cx,320*200
        mov es,vidseg
        xor ax,ax
        mov di,ax
        rep stosw
        mov dx,3d4h
        mov ax,0014h
        out dx,ax
        mov ax,0e317h
        out dx,ax
end;

Procedure setaddress(ad:word); assembler;
asm
        mov dx,3d4h
        mov al,0ch
        mov ah,[byte(ad)+1]
        out dx,ax
        mov al,0dh
        mov ah,[byte(ad)]
        out dx,ax
end;

Procedure cls; assembler;
asm
        mov es,vidseg
        mov di,address
        mov cx,8000
        mov dx,3c4h
        mov ax,0f02h
        out dx,ax
        xor ax,ax
        rep stosw
end;

{$f-}

Procedure polygon(x1,y1,x2,y2,x3,y3,x4,y4:integer; c:byte); assembler;
var mny,mxy,y,m,mult,divi,top,s,
    stb,px1,py1,px2,py2:integer;
    dir:byte;
asm                                     { Procedure Polygon }
        mov ax,y1                       { Determine lowest & highest points }
        mov cx,ax
        mov bx,y2

        cmp ax,bx                       { if mny>y2 ==> mny:=y2 }
        jl @p2
        mov ax,bx

@p2:    cmp cx,bx                       { if mxy<y2 ==> mxy:=y2 }
        jg @p3
        mov cx,bx

@p3:    mov bx,y3
        cmp ax,bx                       { if mny>y3 ==> mny:=y3 }
        jl @p3M
        mov ax,bx

@p3M:   cmp cx,bx                       { if mxy<y3 ==> mxy:=y3 }
        jg @p4
        mov cx,bx

@p4:    mov bx,y4
        cmp ax,bx                       { if mny>y4 ==> mny:=y4 }
        jl @p4M
        mov ax,bx

@p4M:   cmp cx,bx                       { if mxy<y4 ==> mxy:=y4 }
        jg @vert
        mov cx,bx

@vert:  cmp ax,0                        { Vertical range checking }
        jge @minin                      { if mny<0 ==> mny:=0 }
        xor ax,ax
@minin: cmp cx,200                      { if mxy>199 ==> mxy:=199 }
        jl @maxin
        mov cx,199
@maxin: cmp cx,0                        { if mxy<0 ==> Exit }
        jl @pexit
        cmp ax,199                      { if mny>199 ==> Exit }
        jg @pexit

        mov mny,ax                      { ax=mny=lowest point }
        mov mxy,cx                      { cx=mxy=highest point }

        push x1                         { RangeChk(x1,y1,x2,y2) }
        push y1
        push x2
        push y2
        call @Range

        push x2                         { RangeChk(x2,y2,x3,y3) }
        push y2
        push x3
        push y3
        call @Range

        push x3                         { RangeChk(x3,y3,x4,y4) }
        push y3
        cmp Triangles,0
        jz @Poly4
        push x1
        push y1
        jmp @Last

@Poly4: push x4
        push y4
        call @Range

        push x4                         { RangeChk(x4,y4,x1,y1) }
        push y4
        push x1
        push y1
@Last:  call @Range

        mov ax,mny                      { Show a poly }
        mov di,ax                       { y:=mny }
        shl di,2
        lea bx,xpos
        add di,bx                       { di points to xpos[y,0] }
@Show:  mov y,ax                        { repeat ... }
        mov cx,[di]
        mov dx,[di+2]
        mov px1,cx
        mov px2,dx
        push ax
        push di
        call @xhlin                     { xhlin(px1,px2,y,c) }
        pop di
        pop ax
        add di,4                        { Next xpos }
        inc ax                          { inc(y) }
        cmp ax,mxy                      { ... until y>mxy; }
        jle @Show
        jmp @pexit

{ RangeChk }

@Range: pop di                          { Get return IP }
        pop py2                         { Get params }
        pop px2
        pop py1
        pop px1
        push di                         { Save return IP }

        mov ax,py1                      { dir:=byte(y1<y2) }
        cmp ax,py2
        mov ax,1
        jl @Rdwn
        dec al
@Rdwn:  mov dir,al

        shl al,1
        push ax
        shl al,2
        sub ax,4
        mov stb,ax                      { stb:=8*dir-4 }
        pop ax
        dec ax                          { s:=2*dir-1 }
        mov s,ax                        { Check directions (-1= down, 1=up) }

        test AH,10000000b               { Calculate constants }
        mov dx,0
        jz @Rposi
        dec dx
@Rposi: mov bx,px2
        sub bx,px1
        imul bx
        mov mult,ax                     { mult:=s*(x2-x1) }
        mov ax,py2
        mov bx,py1
        mov cx,ax
        sub ax,bx
        mov divi,ax                     { divi:=y2-y1 }

        cmp bx,cx                       { ¨y1=y2? }

        pushf                           { Calculate pointer to xpos[y,dir] }
        mov y,bx                        { y:=y1 }
        mov di,bx
        shl di,2
        lea bx,xpos
        add di,bx
        mov cl,dir
        mov ch,0
        shl cl,1
        add di,cx                       { di points to xpos[y,dir] }
        popf

        je @Requ                        { if y1=y2 ==> @Requ }

        mov m,0                         { m:=0 }
        mov ax,py2
        add ax,s
        mov top,ax                      { top:=y2+s }

@RLoop: mov ax,y                        { repeat ... }
        cmp ax,mny                      { if y<mny ==> @RNext }
        jl @RNext
        cmp ax,mxy                      { if y>mxy ==> @RNext }
        jg @RNext

        mov ax,m                        { Calculate int(m/divi)+x1 }
        test AH,10000000b
        mov dx,0
        jz @RLpos
        dec dx
@RLpos: mov bx,divi
        idiv bx
        add ax,px1
        call @HR                        { HorRangeChk(m div divi+x1) }

@RNext: mov ax,mult
        add m,ax                        { inc(m,mult) }
        add di,stb                      { Next xpos }
        mov ax,y                        { inc(y,s) }
        add ax,s
        mov y,ax
        cmp ax,top
        jne @RLoop                      { ... until y=top }
        jmp @Rexit

@Requ:  mov ax,y
        cmp ax,mny                      { if y<mny ==> Exit }
        jl @Rexit
        cmp ax,mxy                      { if y>mxy ==> Exit }
        jg @Rexit
        mov ax,px1
        call @HR                        { HorRangeChk(px1) }
@Rexit: jmp @exit

{ HorRangeChk }

@HR:    mov bx,minx                     { bx:=minx }
        cmp ax,bx
        jl @HRsav
        mov bx,maxx                     { bx:=maxx }
        cmp ax,bx
        jg @HRsav
        mov bx,ax
@HRsav: mov [di],bx                     { xpos[y,dir]:=bx }
        jmp @exit
{ xhlin }

@xhlin: mov es,vidseg
        cld
        mov ax,80
        mul y
        mov di,ax                       { base of scan line }
        add di,address

        mov bx,px1                      { px1 = x begin coord }
        mov dx,px2                      { px2 = x end coord }
        cmp bx,dx
        jb @skip
        xchg bx,dx                      { switch coords if px1>px2 }

@skip:  mov cl,bl
        shr bx,2
        mov ch,dl
        shr dx,2
        and cx,$0303
        sub dx,bx                       { width in Bytes }
        add di,bx                       { offset into video buffer }
        mov ax,$ff02
        shl ah,cl
        and ah,1111b                    { left edge mask }
        mov cl,ch
        mov bh,$f1
        rol bh,cl
        and bh,1111b                    { right edge mask }
        mov cx,dx
        or cx,cx
        jnz @left
        and ah,bh                       { combine left & right bitmasks }

@left:  mov dx,$03c4
        out dx,ax
        inc dx
        mov al,c
        stosb
        jcxz @exit
        dec cx
        jcxz @right
        mov al,1111b
        out dx,al                       { skipped if cx=0,1 }
        mov al,c
        repz stosb                      { fill middle Bytes }

@right: mov al,bh
        out dx,al                       { skipped if cx=0 }
        mov al,c
        stosb

@exit:  pop ax
        push cs
        push ax
        ret
@pexit:
end;

{$f+}

Function cosinus(i:byte):integer;
begin
 cosinus:=ctab[i];
end;

Function sinus(i:byte):integer;
begin
 sinus:=stab[i];
end;

Procedure Initialize;
var i:byte;
begin
 triangles:=False;
 for i:=0 to 255 do ctab[i]:=round(-cos(i*pi/128)*divd);
 for i:=0 to 255 do stab[i]:=round(sin(i*pi/128)*divd);
end;

begin
 Initialize;
end.
