;-----------------------------------------------------------------------
; MODULE XPBITMAP
; Compile with Tasm.
; C callable.
;
; MODE X Source Addapted from Themie Gouthas's
;   XLIB - Mode X graphics library
;-----------------------------------------------------------------------


include xlib.inc
include xpbitmap.inc
LOCALS
	.code

;----------------------------------------------------------------------
; PutObj- mask write a planar bitmap from system ram to video ram
; all zero source bitmap bytes indicate destination byte to be left unchanged
;
;  PutObj(X,Y,ScrnOffs,char far * Bitmap)
;----------------------------------------------------------------------
_PutObj proc
    ARG     X:word,Y:word,ScrnOffs:word,Bitmap:dword

    LOCAL   BMWidth:byte,BMHeight:byte,NextLineIncr:word=LocalStk

    push    bp
    mov     bp,sp
    sub     sp,LocalStk                 ; CREATE SPACE FOR LOCAL VARIABLES
    push    si
    push    di
    push    ds
	cld
    mov     ax,SCREEN_SEG
    mov     es,ax
    mov     ax,[Y]                    ; CALCULATE DEST SCREEN ROW
    mov     bx,[_ScrnLogicalByteWidth];  BY MULT. DEST Y COORD BY SCREEN
    mul     bx                        ;  WIDTH THEN ADDING SCREEN OFFSET
    mov     di,[ScrnOffs]             ;  STORE RESULT IN DI
    add     di,ax
    mov     cx,[X]                    ;   LOAD X COORD INTO CX AND MAKE A
    mov     dx,cx                     ; COPY IN DX
    shr     dx,2                      ;   FIND STARTING BYTE IN DES
    add     di,dx                     ; ADD TO DI GIVING SCREEN
                                      ; FIRST PIXEL'S BYTE
    lds     si,[Bitmap]               ; DS:SI -> BITMAP DATA
    lodsw                             ;   AL = B.M. WIDTH (BYTES) A
                                      ; HEIGHT
    mov     [BMHeight],ah             ;   SAVE SOURCE BITMAP DIMENS
    mov     [BMWidth],al              ;
    xor     ah,ah                     ;   NEXTLINEINCR = BYTES TO TO
    sub     bx,ax                     ; OF BITMAPS NEXT ROW ON S
    mov     [NextLineIncr],bx         ;
    and     cx,0003h                  ; MASK X COORD GIVING PLANE
                                      ; BITMAP PIXEL
    mov     ah,11h                    ; INIT. MASK
    shl     ah,cl                     ; SHIFT FOR STARTING PIXEL
    mov     dx,SC_INDEX               ; PREPARE VGA FOR CPU WRITES
    mov     al,MAP_MASK
    out     dx,al
    inc     dx
    mov     bh,4                      ; SET PLANE COUNTER (BH) TO
@@PlaneLoop:
    push    di                        ; SAVE BITMAP'S START DEST.
    mov     bl,[BMHeight]             ; RESET ROW COUNTER (BL)
    mov     al,ah
    out     dx,al                     ; SET VGA WRITE PLANE
@@RowLoop:
    mov     cl,[BMWidth]              ; RESET COLUMN COUNTER CL
@@ColLoop:
    lodsb                             ; GET NEXT SOURCE BITMAP BY
    or      al,al                     ; IF NOT ZERO THEN WRITE TO
    jz      @@NoPixel                 ; OTHERWISE SKIP TO NEXT BY
    mov     es:[di],al
@@NoPixel:
    inc     di
    loop    @@ColLoop                 ; LOOP IF MORE COLUMNS LEFT
    add     di,[NextLineIncr]         ; MOVE TO NEXT ROW
    dec     bl                        ; DECREMENT ROW COUNTER
    jnz     @@RowLoop                 ; JUMP IF MORE ROWS LEFT
    pop     di                        ; RESTORE BITMAPS START DES
    rol     ah,1                      ; SHIFT MASK FOR NEXT PLANE
    adc     di,0                      ; IF WRAPPED INCREMENT DEST
    dec     bh                        ; DECREMENT PLANE COUNTER
    jnz     @@PlaneLoop               ; JUMP IF MORE PLANES LEFT

    pop     ds                        ; RESTORE DATA SEGMENT
    pop     di                        ; RESTORE REGISTERS
    pop     si
    mov     sp,bp                     ; DEALLOC LOCAL VARIABLES
    pop     bp
    ret
_PutObj  endp

;----------------------------------------------------------------------
; WriteObj - Write a planar bitmap from system ram to video ram
;
; Source Bitmap structure:
;
;  Width:byte, Height:byte, Bitmap data (plane 0)...Bitmap data (plane 1)..,
;  Bitmap data (plane 2)..,Bitmap data (plane 3)..
;
;----------------------------------------------------------------------

_WriteObj proc
	ARG X:word,Y:word,ScrnOffs:word,Bitmap:dword
	LOCAL Plane:byte,BMHeight:byte,LineInc:word=LocalStk
	push  bp
    mov   bp,sp
	sub   sp,LocalStk                 ; Create space for local variables
	push  si
    push  di
	push  ds
	cld
    mov   ax,SCREEN_SEG
    mov   es,ax
    mov   ax,[Y]                      ; Calculate dest screen row
	mov   bx,[_ScrnLogicalByteWidth]  ;  by mult. dest Y coord by Screen
	mul   bx                          ;  width then adding screen offset
    mov   di,[ScrnOffs]               ;  store result in DI
    add   di,ax
    mov   cx,[X]                      ; Load X coord into CX and make a
	mov   dx,cx                       ;  copy in DX
	shr   dx,2                        ; Find starting byte in dest row
	add   di,dx                       ;  add to DI giving screen offset of
                                      ;  first pixel's byte
    lds   si,[Bitmap]                 ; DS:SI -> Bitmap data
    lodsw                             ; Al = B.M. width (bytes) AH = B.M.
                                      ;  height
UnFlipped:
    mov   [BMHeight],ah               ; Save source bitmap dimensions
    xor   ah,ah                       ; LineInc = bytes to the begin.
	sub   bx,ax                       ;  of bitmaps next row on screen
	mov   [LineInc],bx
	mov   bh,al
                                    ; Self Modifying, Shame, shame shame..
    and   cx,0003h                    ; mask X coord giving plane of 1st
                                          ; bitmap pixel(zero CH coincidentally)
	mov   ah,11h                      ; Init. mask for VGA plane selection
    shl   ah,cl                       ; Shift for starting pixel plane
    mov   dx,SC_INDEX                 ; Prepare VGA for cpu to video writes
    mov   al,MAP_MASK
    out   dx,al
    inc   dx
	mov   [Plane],4                   ; Set plane counter to 4
@@PlaneLoop:
    push  di
	mov   bl,[BMHeight]
    mov   al,ah
    out   dx,al
@@RowLoop:
	mov   cl,bh
	shr   cl,1
	rep   movsw                       ; Copy a complete row for curr plane
	adc   cl,0
	rep   movsb
	add   di,[LineInc]                ; Move to next row
    dec   bl                          ; decrement row counter
    jnz   @@RowLoop                   ; Jump if more rows left
    pop   di                          ; Restore bitmaps start dest byte
    rol   ah,1                        ; Shift mask for next plane
	adc   di,0                        ; If wrapped increment dest address
	dec   [Plane]                     ; Decrement plane counter
    jnz   @@PlaneLoop                 ; Jump if more planes left

    pop   ds                          ; restore data segment
    pop   di                          ; restore registers
    pop   si
    mov   sp,bp                       ; dealloc local variables
    pop   bp
    ret
_WriteObj  endp

;----------------------------------------------------------------------
; EraseObj
;----------------------------------------------------------------------

_EraseObj proc
    ARG   X:word,Y:word,Width:byte,Height:byte,ScrnOffs:word
    LOCAL Plane:byte,LineInc:word=LocalStk
	push  bp
    mov   bp,sp
	sub   sp,LocalStk                 ; Create space for local variables
	push  si
    push  di
	push  ds
	cld
    mov   ax,SCREEN_SEG
    mov   es,ax
    mov   ax,[Y]                      ; Calculate dest screen row
	mov   bx,[_ScrnLogicalByteWidth]  ;  by mult. dest Y coord by Screen
	mul   bx                          ;  width then adding screen offset
    mov   di,[ScrnOffs]               ;  store result in DI
    add   di,ax
    mov   cx,[X]                      ; Load X coord into CX and make a
	mov   dx,cx                       ;  copy in DX
	shr   dx,2                        ; Find starting byte in dest row
	add   di,dx                       ;  add to DI giving screen offset of
                                      ;  first pixel's byte
    mov   al, [Width]
    xor   ah,ah                       ; LineInc = bytes to the begin.
	sub   bx,ax                       ;  of bitmaps next row on screen
	mov   [LineInc],bx
	mov   bh,al
                                    ; Self Modifying, Shame, shame shame..
    and   cx,0003h                    ; mask X coord giving plane of 1st
                                          ; bitmap pixel(zero CH coincidentally)
	mov   ah,11h                      ; Init. mask for VGA plane selection
    shl   ah,cl                       ; Shift for starting pixel plane
    mov   dx,SC_INDEX                 ; Prepare VGA for cpu to video writes
    mov   al,MAP_MASK
    out   dx,al
    inc   dx
	mov   [Plane],4                   ; Set plane counter to 4
@@PlaneLoop:
    push  di
    mov   bl,[Height]
    mov   al,ah
    out   dx,al
@@RowLoop:
	mov   cl,bh
	shr   cl,1
    push ax
    mov   ax,0
    rep   stosw                       ; Copy a complete row for curr plane
    pop ax
	add   di,[LineInc]                ; Move to next row
    dec   bl                          ; decrement row counter
    jnz   @@RowLoop                   ; Jump if more rows left
    pop   di                          ; Restore bitmaps start dest byte
    rol   ah,1                        ; Shift mask for next plane
	adc   di,0                        ; If wrapped increment dest address
	dec   [Plane]                     ; Decrement plane counter
    jnz   @@PlaneLoop                 ; Jump if more planes left

    pop   ds                          ; restore data segment
    pop   di                          ; restore registers
    pop   si
    mov   sp,bp                       ; dealloc local variables
    pop   bp
    ret
_EraseObj  endp

	end
