;// FAKEMODE.ASM  Assembler Routines Library for FakeMode
;// (c) 1993,94 by Yaka/Xography
;// V1.1; arranged 12.5.94

.model large

PUBLIC _F_settimer              ;//Timer routines.
PUBLIC _F_gettimer

PUBLIC _F_isvga                 ;//FakeMode routines.
PUBLIC _F_initgraph
PUBLIC _F_closegraph

PUBLIC _F_videooff              ;//Display routines.
PUBLIC _F_videoon
PUBLIC _F_fadein
PUBLIC _F_fadeout
PUBLIC _F_fadeinfast
PUBLIC _F_fadeoutfast
PUBLIC _F_setlumi

PUBLIC _F_putbigpixel           ;//Pixel routines.
PUBLIC _F_putsmallpixel


.code

;//---------------============ INTERNAL ROUTINES =============--------------

palette db 768 dup (?)

initpalette PROC
  push di
  push cs
  pop es
  mov di, offset palette
  cld
  mov dx,3c8h
  xor ax,ax
  xor bx,bx
  out dx,al
  inc dx                ;//ah=red, bh=green, bl=blue
  mov cx,15
initpal_outer:
  push cx
  mov cx,16
  initpal_inner:
      mov al,ah
      out dx,al
      stosb
      mov al,bh
      out dx,al
      stosb
      mov al,bl
      out dx,al
      stosb

      add ah,4
    loop initpal_inner
    mov ah,0
    add bl,4
    cmp bl,4
    jne goon
      add bl,4
  goon:
    pop cx
  loop initpal_outer

  mov cx,16
  xor ax,ax
  xor bx,bx
initpal_second:
    mov al,ah
    out dx,al
    stosb
    mov al,bh
    out dx,al
    stosb
    mov al,bl
    out dx,al
    stosb
    add bh,4
  loop initpal_second
  pop di
  ret
initpalette ENDP

setluminance PROC
;//Internal Routine to set one luminance value when fading in/out.
;//requires luminance value (0..255) in AL
;//Sets original VGA palette (256 colors)
;//Attention! Changes values of all general purpose registers (ax,bx,cx,dx)!
;//This routine is used in routines H_setlumi, H_fadein, H_fadeout.
  mov bl,al  ;//luminance in bl
  push ds
  push cs
  pop ds
  push si
  mov si, offset palette
  push di
  cld
  mov dx,3c8h
  mov di,3dah
  mov al,0
  out dx,al
  inc dx
  mov cx,256
lumiloop:
    lodsb
    mul bl
    mov al,ah
    out dx,al
    lodsb
    mul bl
    mov al,ah
    out dx,al
    lodsb
    mul bl
    mov al,ah

    push ax
    xchg dx,di
   lumjmp1:                     ;//wait for end of retrace
     in al,dx
     and al,00000001b
    jnz lumjmp1
   lumjmp2:                     ;//wait for retrace
     in al,dx
     and al,00000001b
    jz lumjmp2
    xchg dx,di
    pop ax

    out dx,al
  loop lumiloop
  pop di
  pop si
  pop ds
  ret
setluminance ENDP

;//------ TIMER

systimer dw 0

currentfloptime dw ?

currentpage dw 0
currentsystimer dw 0
alterint08 dw ?,?

switchpageint PROC
  push ax
  push bx
  push dx

  inc word ptr systimer ;//set system timer

  mov bx,currentpage
  add bx,32768
  mov currentpage,bx
  mov dx,3d4h
  mov al,0ch            ;//set Start Adresse High (0Ch)
  mov ah,bh
  out dx,ax

  mov dx,3dah           ;//Wait for Retrace
swretjmp:
  in al,dx
  and al,00001000b
  jz swretjmp

  mov al,34h            ;//start Monoflop new
  out 43h,al
  mov ax,currentfloptime
  out 40h,al
  mov al,ah
  out 40h,al

  mov bx,currentsystimer        ;//Test for Systemtimer call at 18.2 Hz
  add ax,bx
  mov currentsystimer,ax
  cmp ax,bx
  ja nosysroutine               ;//No --> continue
  pop dx
  pop bx
  pop ax
  jmp dword ptr alterint08      ;//call Systemtimer routine
nosysroutine:
  mov al,20h            ;//acknowledge interrupt controller
  out 20h,al

  pop dx
  pop bx
  pop ax
  iret
switchpageint ENDP

counter dw 0

synchroint PROC
  push ax
  mov ax,counter
  inc ax
  mov counter,ax
  mov al,20h
  out 20h,al
  pop ax
  iret
synchroint ENDP

inittimer PROC
  push di
  mov ax,1234h
  mov currentfloptime,ax

  mov ax,3508h          ;//save old Interrupt 08
  int 21h
  mov alterint08,bx
  mov alterint08+2,es

  xor ax,ax             ;//redirect Interrupt 08 to synchronisation routine
  mov es,ax
  mov di,08h*4
  cli
  cld
  mov ax,offset synchroint
  stosw
  mov ax,cs
  stosw
  sti

  ;//------ measure Synchronisation

  mov dx,3dah           ;//Wait for End of Retrace
s1endretjmp:
  in al,dx
  and al,00001000b
  jnz s1endretjmp
s1retjmp:                       ;//Wait for Retrace
  in al,dx
  and al,00001000b
  jz s1retjmp

synchroback:

  mov al,36h
  out 43h,al
  mov ax,currentfloptime
  out 40h,al
  mov al,ah
  out 40h,al

  mov ax,0
  mov counter, ax

  mov dx,3dah           ;//Wait for End of Retrace
s2endretjmp:
  in al,dx
  and al,00001000b
  jnz s2endretjmp
s2retjmp:               ;//Wait for Retrace
  in al,dx
  and al,00001000b
  jz s2retjmp

  mov ax,counter
  cmp ax,0
  je fertig
  mov ax,currentfloptime
  add ax,250
  mov currentfloptime,ax
  jmp synchroback

fertig:
  mov al,34h            ;//set Systemtimer correctly (Monoflop)
  out 43h,al
  mov ax, currentfloptime
  sub ax,800
  mov currentfloptime,ax
  out 40h,al
  mov al,ah
  out 40h,al

  xor ax,ax             ;//redirect Interrupt 08 to Screenswitch routine
  mov es,ax
  mov di,08h*4
  cli
  cld
  mov ax,offset switchpageint
  stosw
  mov ax,cs
  stosw
  sti
  pop di
  ret
inittimer ENDP

closetimer PROC
  push ds
  push di
  push si
  cli
  mov al,36h            ;//Systemtimer back to normal speed
  out 43h,al
  xor al,al
  out 40h,al
  out 40h,al
  push cs               ;//restore interrupt vector back to normal
  pop ds
  mov si,offset alterint08
  xor ax,ax
  mov es,ax
  mov di,08h*4
  cld
  movsw
  movsw
  sti
  pop si
  pop di
  pop ds
  ret
closetimer ENDP

;//---------------============ USER ROUTINES =============--------------

_F_settimer PROC
;//Sets timer variable to defined value. (timer variable is incremented
;// every vertical retrace)
arg value:word
  push bp
  mov bp,sp
  mov ax,value
  mov systimer,ax
  pop bp
  ret
_F_settimer ENDP

_F_gettimer PROC
;//Reads current value of timer variable.
  mov ax,systimer
  ret
_F_gettimer ENDP


_F_isvga PROC  ;//AX=0 = No VGA, AX=255 = is VGA, AX = 254 = unknown
;//Checks if VGA card is installed.
  mov ax,1a00h
  int 10h
  cmp al,1ah
  jne isnovga
  cmp bl,07h
  jb isnovga
  xor ax,ax
  cmp bl,08
  ja isunknown
  mov al,0ffh
  ret
 isunknown:
  mov al,254d
  ret
 isnovga:
  xor ax,ax
  ret
_F_isvga ENDP

oldvideomode db 3

_F_initgraph PROC
;//Initializes Fakemode, including palette and timer setup.
  push di
  mov ax,0f00h
  int 10h
  mov oldvideomode,al

  mov ax,0013h
  int 10h

  mov dx,3ceh
  mov al,5
  out dx,al
  inc dx
  in al,dx
  and al,11101111b
  out dx,al
  dec dx

  mov al,6
  out dx,al
  inc dx
  in al,dx
  and al,11111101b
  out dx,al

  mov dx,3c4h
  mov al,4
  out dx,al
  inc dx
  in al,dx
  and al,11110111b
  or al,4
  out dx,al

  mov ax,0a000h
  mov es,ax
  xor di,di
  mov ax,di
  mov cx,8000h
  rep stosw

  mov dx,3d4h
  mov al,9
  out dx,al
  inc dx
  in al,dx
  and al,01110000b
  out dx,al
  dec dx

  mov al,14h
  out dx,al
  inc dx
  in al,dx
  and al,10111111b
  out dx,al
  dec dx

  mov al,17h    ;//select Word-Mode (normally: Bytemode)
  out dx,al
  inc dx
  in al,dx
  and al,10111111b ;//normally: or al,01000000b
  out dx,al

  call initpalette

  call inittimer
  pop di
  ret
_F_initgraph ENDP

_F_closegraph PROC
;//Closes FakeMode and returns to previous video mode.
  call closetimer
  xor ax,ax
  mov al,oldvideomode
  int 10h
  ret
_F_closegraph ENDP


_F_videooff PROC
;//Switches Screen off.
  mov dx,3c4h
  mov al,1
  out dx,al
  inc dx
  in al,dx
  or al,00100000b
  out dx,al
  ret
_F_videooff ENDP

_F_videoon PROC
;//Switches screen on.
  mov dx,3c4h
  mov al,1
  out dx,al
  inc dx
  in al,dx
  and al,11011111b
  out dx,al
  ret
_F_videoon ENDP

_F_fadein PROC
;//Fades screen in slowly. (Attention! Works only when Screen is switched on.
;// Use H_setlumi(0) to darken screen before fadein (instead of H_videooff)!)
  mov ax,0
fadeinloop:
    push ax
    call setluminance
    pop ax
    inc ax
    cmp ax,256
  jb fadeinloop
  ret
_F_fadein ENDP

_F_fadeout PROC
;//Fades out screen slowly.
  mov ax,256
fadeoutloop:
    dec ax
    push ax
    call setluminance
    pop ax
    cmp ax,0
  ja fadeoutloop
  ret
_F_fadeout ENDP

_F_fadeinfast PROC
;//Fades in screen with double speed
  mov ax,0
fadeinfastloop:
    push ax
    call setluminance
    pop ax
    add ax,2
    cmp ax,256
  jb fadeinfastloop
  mov al,255
  call setluminance
  ret
_F_fadeinfast ENDP

_F_fadeoutfast PROC
;//Fades out screen with double speed
  mov ax,256
fadeoutfastloop:
    sub ax,2
    push ax
    call setluminance
    pop ax
    cmp ax,0
  ja fadeoutfastloop
  ret
_F_fadeoutfast ENDP

_F_setlumi PROC
;//Sets screen luminance directly.
arg lumi:byte
  push bp
  mov bp,sp
  mov al,lumi
  call setluminance
  pop bp
  ret
_F_setlumi ENDP


_F_putbigpixel PROC
;//Puts one pixel with 320x200 resolution (Actually there are 2 pixels at
;// 320x400 resolution)
  ARG x:word, y:byte, red:byte, green:byte, blue:byte
  push bp
  mov bp,sp
  push di
  mov bx,x
  mov cx,bx
  and cl,00000011b      ;//calculate Bitplane...
  mov dx,3c4h
  mov ax,0102h
  shl ah,cl
  out dx,ax             ;//...and set it
  mov ax,0a000h         ;//set dest segment
  mov es,ax
  mov ax,320            ;//set dest offset
  mov dl,y
  mov dh,0
  mul dx
  shr bx,1
  and bl,11111110b
  add bx,ax             ;//bx contains basic offset
  mov di,bx
  mov al,blue           ;//calculate red-blue
  mov ah,16
  mul ah
  cmp ax,0
  je bpixgoon
    sub ax,16
bpixgoon:
  add al,red
  mov ah,green
  add ah,240
  and cl,00000001b
  jz typetwo
    mov es:[di+160],ax
    xchg ah,al
    mov es:[di],ax
    jmp tend
typetwo:
    mov es:[di],ax
    xchg ah,al
    mov es:[di+160],ax
    jmp tend
tend:
  pop di
  pop bp
  ret
_F_putbigpixel ENDP

_F_putsmallpixel PROC
;//Puts one pixel with 320x400 resolution.
  ARG x:word, y:word, red:byte, green:byte, blue:byte
  push bp
  mov bp,sp
  push di
  mov bx,x
  mov cx,bx
  and cl,00000011b      ;//calculate Bitplane...
  mov dx,3c4h
  mov ax,0102h
  shl ah,cl
  out dx,ax             ;//...and set it
  mov ax,0a000h         ;//set destination segment
  mov es,ax
  mov ax,160            ;//set destination offset
  mov dx,y
  mul dx
  shr bx,1
  and bl,11111110b
  add bx,ax             ;//bx contains basic offset
  mov di,bx
  mov al,blue           ;//calculate red-blue
  mov ah,16
  mul ah
  cmp ax,0
  je spixgoon
    sub ax,16           ;//This is where i adapt blue values.
spixgoon:
  add al,red
  add cx,y
  and cl,00000001b
  jz stypetwo           ;//Here i decide whether to put red/blue on page 1
    mov ah,al           ;//and green on page 2 OR to put green on page 1
    mov al,green        ;//and red/blue on page 2.
    add al,240
    mov es:[di],ax
    jmp send
stypetwo:
    mov ah,green
    add ah,240
    mov es:[di],ax
send:
  pop di
  pop bp
  ret
_F_putsmallpixel ENDP

END
