; putbits()    - by Mike Salisbury - August 25, 1989
;
; call from C as: putbits(x,y,width,height,bufferptr,fgcolor)
;			x : x position
;			y : y position
;		    width : character width in bits
;		   height : character height in bits
;		bufferptr : pointer to byte buffer holding character
;		  fgcolor : foreground color of character (bg stays same)
;
; putbits will draw a laserjet formatted character to an ega screen.
; x does not have to be byte aligned, but I have included an optimized
; routine for drawing byte aligned characters.  I have only tested
; this on an EGA, but I believe it should work in VGA mode as well.
; This is a Turbo Assembler module.  I really only save the registers
; I have to (CS,SS,DS,BP,SI,DI).  
;

.model small,C
.code

PUBLIC putbits

putbits PROC
ARG x:WORD,y:WORD,w:WORD,h:WORD,buf:PTR WORD,color:BYTE
LOCAL endinc:WORD,count:WORD,color_shift:WORD,startmask:BYTE,endmask:BYTE
LOCAL shorter:BYTE
USES si,di

	mov	ax,0a000h
	mov	es,ax		; Set up EGA video segment 
	mov	dx,w
	dec	dx
	mov	cl,3
	shr	dx,cl
	inc	dx
	mov	count,dx		; Save byte size width (rounded up from pixels)
	mov	ax,80
	sub	ax,dx		; ax = end of line increment 
	mov	endinc,ax
	mov	ax,y
	mov	dx,80
	mul	dx
	mov 	dx,x
	mov 	cl,3
	shr	dx,cl		; Save byte size x offset 
	add	ax,dx
	mov	di,ax		; di = video offset 

	mov	cx,h
	jcxz	pbab1
	mov	si,buf		; si = character data pointer 
	mov 	dx,03ceh		; dx = video port 
	mov 	ax,0a05h		; set Read mode 1, write mode 2 
	out	dx,ax
	mov	ax,0007h		; Set color don't care for all planes
	out	dx,ax
	mov	ax,x
	and	ax,7			; check if byte aligned
	jnz	shifted

	mov	bl,color
	mov	al,8			; al = Bit Mask register
pb1:
	push	cx
	mov	cx,count
pb2:
	mov	ah,[si]		; Byte Aligned
	out	dx,ax
	and	es:[di],bl	; Draw foreground
	inc	si
	inc	di
	loop	pb2

	pop	cx
	add	di,endinc
	loop	pb1
	jmp	pbexit

pbab1:
	jmp	pbabort		; stepping stone to exit for jcxz above

shifted:
	mov	ah,00
	mov	shorter,ah	; initialize shorter flag
	mov	cx,w			; si,di, and dx already set upon entry
	dec	cx
	and	cx,7
	xor	cl,7
	mov	ah,0ffh
	shl	ah,cl		; ah = original end mask
	mov	cl,al
	xor	cl,7
	inc	cl			; cl = bits to shift
	mov	ch,color
	mov	color_shift,cx	; save color and shift
	mov	al,ah
	cbw				; ax = original end mask
	mov	bx,00ffh		; bx = original start mask
	shl	ax,cl
	shl	bx,cl		; shift both start and end masks
	mov	startmask,bh
	dec	endinc
	or	al,al
	jnz	pb3
	dec	count		; decrement total byte count
	inc	endinc
	mov	al,01
	mov	shorter,al	; shifted bytes fit in same space flag
	mov	al,ah
pb3:
	mov	endmask,al	; both start and end mask are now set
	mov	bx,count
	or	bx,bx
	jnz	pb4
	and	al,startmask
	mov	startmask,al	; if only one byte, combine masks
pb4:
	mov	cx,h
pbrept:
	push	cx
	mov	cx,color_shift
	mov	bx,count
	lodsw			; AL = first byte, AH = second byte
	dec	si			; point back to second
	rol	ax,cl		; AH = cond+fi, AL = rst+sec
	and	ah,startmask
pb5:
	mov	al,8			; bit mask register
	out	dx,ax
	and	es:[di],ch	; write bits
	inc	di
	cmp	bx,1
	jle	pb6
	dec	si
	dec	bx
	lodsw
	rol	ax,cl
	mov	ah,al
	jmp	short pb5		; go do next byte
pb6:
	or	bx,bx
	jz	pb7			; if only one byte to start with, do next
	dec	si
	lodsw
	rol	ax,cl
	mov	bl,shorter
	or	bl,bl
	jnz	pb8			; if shorter flag not set, decrement buffer ptr
	dec	si
pb8:
	mov	ah,al	
	and	ah,endmask
	mov	al,8
	out	dx,ax		; write ending byte
	and	es:[di],ch
	inc	di
pb7:
	add	di,endinc
	pop	cx
	loop	pbrept		; repeat for all lines

pbexit:
	mov	ax,0005h		; Reset read/write mode
	out 	dx,ax
	mov	ax,0ff08h		; Reset Bit mask
	out	dx,ax
	mov	ax,00f07h		; Reset color don't care for all planes
	out	dx,ax
pbabort:
	ret

putbits ENDP

END
