;--------------------------------------------
; code to fade VGA in and out. Author unknown.

;+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
; extern "C" void fadepalout(int start, int count)
;
; This function fades the vga palette from the values in palette
; to 0 using a 64 pass incremental RGB fade. Similar in execution to
; _fadepalin above.
;
; note: start and count are in the range 0..255, and denote the first
; palette register to process, and the number of subsequent registers
; to process. The palette argument points to an array of 768 bytes of
; DAC palette data. Only the low six bits of each byte are significant.
; Palette registers outside the specified range are not affected.

_fadepalout     PROC
        	ARG start: WORD, count: WORD

	push    bp
       	mov     bp, sp
        push    es
        push    ds
       	push    di
        push    si
       	push    dx
        push    cx
       	push    bx
        jmp     o_go
       	opal    db 768 dup (20)  ; temporary palette area
o_go:
       	push    cs               ; get code segment into es
       	pop     es
       	mov     dx,offset opal   ; es:dx points to start of opal
       	push    dx               ; save offset of opal
       	xor     bx,bx
       	mov     cx,100h
       	mov     ax,1017h         ; bios read dac registers function
       	int     10h              ; read the palette registers into opal
       	pop     di               ; offset of opal, was in dx!
       	mov     ax,start         ; get offset of first palette byte to
       	mov     bx,3             ; be processed
       	mul     bx
       	add     di,ax            ; adjust offset into opal
       	mov     ax,count         ; find the number of bytes to be processed
       	mov     bx,3
       	mul     bx               ; leave it in ax
       	mov     cx,64            ; 64 passes through fade loop
o_fade_loop:
        push    cx               ; save the fade loop counter
        push    di               ; save offset of first byte processed in
        mov     bl,cl            ; we'll use the pass number as a threshold
        mov     cx,ax            ; load number of bytes to process into cx
o_pal_cmp_loop:
        cmp     bl,es:[di]       ; start decrementing when palette value
        jnz     o_no_dec         ; is equal loop count (it will stay equal
       	dec     BYTE PTR es:[di] ; to loop count for the rest of this pass)
o_no_dec:
        inc     di
        loop    o_pal_cmp_loop   ; do the next byte

        mov     bx,sp            ; need the stack pointer for a moment
        mov     di,ss:[bx]       ; restore offset into pal without popping
        mov     cx,count         ; number of triplets to process
        push    ax               ; need to use ax for port i/o

    	mov 	dx,03DAh         ; CRT controller input status 1 register
o_vbi_1:
	in 	al,dx	    	 ; watch vertical blanking bit
	test 	al,08h           ; wait for it to clear to make sure
	jnz o_vbi_1              ; we're not in a blanking interval
o_vbi_2:
	in 	al,dx            ; now wait for the start of the
	test 	al,08h           ; next blanking interval
	jz o_vbi_2

       	mov     ah,BYTE PTR start    	; get 1st register to process into ah
       	mov     dx,03c8h             	; DAC palette index register
o_pal_load_loop:
        mov     al, ah               	; get next palette number to write
        out     dx, al               	; write the register number to the dac
        inc     dx                   	; address dac data register
        mov     al, BYTE PTR [es:di] 	; get first byte of triplet
        out     dx, al               	; write it to the dac data register
        inc     di                   	; point to second byte
        mov     al, BYTE PTR [es:di] 	; get second byte of triplet
        out     dx, al               	; write it to the dac data register
        inc     di                   	; point to third byte
        mov     al, BYTE PTR [es:di] 	; get third byte of triplet
        out     dx, al               	; write it to the dac data register
        inc     di                   	; point to first byte of next triplet
        dec     dx                   	; address the dac index register
        inc     ah                   	; point to next palette register
        loop    o_pal_load_loop      	; process next triplet

        pop     ax                ; restore ax
        pop     di                ; restore the offset into pal
        pop     cx                ; restore the fade loop counter
        loop    o_fade_loop       ; do the next pass through the fade loop

        pop     bx
        pop     cx
        pop     dx
        pop     si
        pop     di
        pop     ds
        pop     es
        pop     bp
        ret
_fadepalout     ENDP
;+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+


;+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
; extern "C" void fadepalin(int start, int count, const byte* palette)
;
; This function fades the vga palette from 0 to the values in palette
; using a 64 pass incremental RGB fade.
;
; note: start and count are in the range 0..255, and denote the first
; palette register to process, and the number of subsequent registers
; to process. The palette argument points to an array of 768 bytes of
; DAC palette data. Only the low six bits of each byte are significant.
; Palette registers outside the specified range are not affected.

_fadepalin	PROC
	        ARG start: WORD, count: WORD, palette: DWORD

        push    bp
        mov     bp, sp
        push    es                ; we use just about everything
        push    ds
        push    di
        push    si
        push    dx
        push    cx
        push    bx
        jmp     go

        pal     db 768 dup (0)    ; working palette area

go:
        ; setup for and make bios call to load the current dac palette
        ; into the table at pal

        push    cs             ; get code segment into es
        pop     es
        mov     dx, offset pal ; es:dx points to start of pal
        push    dx             ; save offset of pal
        xor     bx,bx          ; need to get the current palette into
        mov     cx,100h        ; pal using bios int 10h, 10h, 17h
        mov     ax,1017h       ; set up and generate the interrupt
        int     10h            ; read the palette registers into pal
        pop     di             ; offset of pal, was in dx!

        ; get the palette pointer, then calculate the offset to the
        ; first byte processed, based on start, and the number of
        ; bytes to process based on count. Use the offset to adjust
        ; the string pointers in si and di, and leave the byte
        ; count in ax

	lds     si,palette       ; get address of target palette
	mov     ax,start         ; get offset of first palette byte to
        mov     bx,3             ; be processed
	mul     bx
        add     si,ax            ; adjust di and si point first byte in the
	add     di,ax            ; target and temporary palettes
        mov     ax,count         ; find the number of bytes to be processed
	mov     bx,3
        mul     bx               ; leave it in ax

        ; clear the bytes in the triplets which will be operated on by
        ; the fade. All other registers are unaffected

        push    di        ; save the starting offset into pal
        push    ax        ; save the number of bytes to process
        mov     cx,ax     ; set up a loop counter
       	xor     ax,ax     ; clear ax
        rep     stosb     ; fill relevant range of pal with 0's
       	pop     ax        ; restore the number of bytes to process
        pop     di        ; restore the starting offset into pal
       	mov     cx,64     ; 64 passes through fade loop

        ; the fade loop will execute 64 times. On each pass the inner
        ; loop adjusts the working palette, then waits for a blanking
        ; interval, and loads the working palette into the DAC

fade_loop:
       	push    cx        ; save the fade loop counter
       	push    di        ; save offset of first byte processed in
       	push    si        ; temp and target palettes
       	mov     bl,cl     ; outer loop count into bl
       	mov     cx,ax     ; load number of bytes to process into cx

        ; inner loop makes one pass through the palette for each pass
        ; through the outer loop. Each byte is incremented if it's
        ; target value is one greater than the outer loop count. Using
        ; this logic ensures that all bytes arrive at their target values
        ; on the same pass through the outer loop

pal_cmp_loop:
       	cmp     bl,ds:[si]    ; start incrementing when palette value
       	jns     no_add        ; is one greater than loop count
       	inc     BYTE PTR es:[di]
no_add:
       	inc     si            ; point to the next byte in both palettes
       	inc     di
       	loop    pal_cmp_loop  ; do the next byte

        ; setup for palette load. As much as possible was moved above the
        ; blanking interval wait, in order to maximize the amount of the
        ; blanking interval remaining in which to do the palette loading

        mov     bx, sp
        mov     di, ss:[bx+02]      ; restore offset into pal without
       	mov     cx, count           ; popping number of triplets to
        push    ax                  ; process need to use ax for port i/o

        ; monitor bit 1 of CRT controller's input status 1 register to
        ; sense a vertical blanking interval. Wait for any current vbi
        ; to end, then wait for the next full one to begin.

    	mov 	dx,03DAh            ; CRT controller input status 1 register
vbi_1:
	in 	al, dx              ; watch vertical blanking bit
	test 	al,08h              ; wait for it to clear to make
	jnz 	vbi_1               ; sure we're not in a blanking
					    ; interval
vbi_2:
	in 	al,dx               ; now wait for the start of the
	test 	al,08h              ; next blanking interval
	jz 	vbi_2

        ; load the relevant triplets from pal into the VGA DAC palette

       	mov     ah,BYTE PTR start  ; get first register to process into ah
       	mov     dx,03c8h           ; DAC palette index register
pal_load_loop:
        mov     al,ah               ; get next palette number to write
        out     dx,al               ; write the reg. number to the dac
        inc     dx                  ; address dac data register
        mov     al,BYTE PTR [es:di] ; get first byte of triplet
        out     dx,al               ; write it to the dac data reg.
        inc     di                  ; point to second byte
        mov     al,BYTE PTR [es:di] ; get second byte of triplet
        out     dx,al               ; write it to the dac data reg.
        inc     di                  ; point to third byte
        mov     al,BYTE PTR [es:di] ; get third byte of triplet
        out     dx,al               ; write it to the dac data reg.
        inc     di                  ; point to 1st byte of next triplet
        dec     dx                  ; address the dac index register
        inc     ah                  ; point to next palette register
        loop    pal_load_loop       ; process next triplet

        ; clean-up for the next pass through the fade loop

        pop     ax                ; restore ax
        pop     si                ; restore the offset into palette
        pop     di                ; restore the offset into pal
        pop     cx                ; restore the fade loop counter
        loop    fade_loop         ; do the next pass through the
        			  ; fade loop

        pop     bx
        pop     cx
        pop     dx
        pop     si
        pop     di
        pop     ds
        pop     es
        pop     bp
        ret
_fadepalin      ENDP


