;Date:              Sat, 16 Dec 1995 17:18:24 +0000
;From:     Raju Agaskar  [raju@eagle.ecs.csus.edu]
;Subject:  LZ: Graphic/Sprite Rountine -- Based on Dines Justensen's ideas

;This message comes from the List-ZShell mailing list:

;If anyone's interested in somewhat "compiled" graphics (probably not
;as good as Stephane's) i wrote the following code using an idea Dines
;Justensen had, to first flag the calc which color you want (either
;"black" [on] or "white" [off]) w/ the MSB  and then use the following three
;bits to tell how many pixels of that color to draw (you wouldn't want
;to draw a big box w/ this code, but small stuff, I think) ...
;Anyways, i thought that was a great idea, and wrote this
;(unoptimized, since I'm new to asm) routine:

	                   ;-----------------
		;DrawMap:
		;assumes bc= top left start
		;de = offset of bitmap string
		;hl = byte to begin drawing
		;keep in mind, each byte = eight bits
		;code:
		;example, 10110010
		;equals 3 black pixels (MSB is 1, 2^1 and 2^0 are on)
		;equals 2 white pixels (MSB is 0, 2^1 is on)
		; (thanks to Dines for the above ideas)
		; 0000b in the low nybble is a linereturn,
		;00000000b is a stop of map
		;if you want a blank line, then write 10000000b
		;no black pixels, and a line return
		;AFTER EACH LINE RETURN IT GOES BACK TO THE
		;ORIGINAL X POSITION, WITH Y INCREASED!
		;LAST REVISION = DEC 16th, 1995
		;------------------
DrawMap:
	add 	hl,de 	;beginning now in hl
	ld	de,$0000
	ld	e,b
DrawM:
	ld	a,(hl)	;put the byte in a
	or	a	;check if it's 00000000b,
	ret	z	;if it is, stop!
TestTop:	;test top nybble
	and	$F0
	srl	a
	srl	a
	srl	a
	srl	a		;get top nybble to lower rung
	CALL_(ColorTest)
TestBottom:
	ld	a,(hl)	;put byte in a
	and	$0F	;get low nybble
	or	a
	JUMP_Z(LineReturn)
	CALL_(ColorTest)
NextByte:
	inc	hl	;next byte
	JUMP_(DrawM)	;start again

ColorTest:
	bit	3,a	;test for color, 0=white, 1=black
	push	af
	CALL_Z(White)
	pop	af
	CALL_NZ(Black)	;we have to test twice, cause we're calling
	ret

White:
	CALL_(NybbleTest)
	CALL_(LineOff)
	ret

Black:
	CALL_(NybbleTest)
	CALL_(LineOn)
	ret

NybbleTest:
	ld	d,$00
	bit	2,a	;check the 2^2 bit (4 bit)
	CALL_NZ(Add4)	;add 4 if it is on
	bit	1,a	;check the 2^1 bit
	CALL_NZ(Add2)	;add 2 if it is on
	bit	0,a	;check the 2^0 bit
	CALL_NZ(Add1)	;add 1 if it is on
	ret

Add4:
	inc	d
	inc	d	;add 2 (total = 4)
Add2:
	inc	d	;add 1 (total = 2)
Add1:
	inc	d	;add 1 (total = 1)
	ret

LineReturn:
	dec	c 	;decreases y
	ld	b,e
	inc	hl
	JUMP_(DrawM)

LineOn:
	CALL_(CheckPoint)
	or	a
	CALL_Z(PointOn)	;if the point is off, need to turn it on
	inc	b	;else, inc b and repeat
	dec	d	;decrease the counter
	ret	z	;return if it's zero
	JUMP_(LineOn)

LineOff:
	CALL_(CheckPoint)
	or	a
	CALL_NZ(PointOff)	;if the point is NOT off, we need to do it
	inc	b
	dec	d
	ret	z	
	JUMP_(LineOff)

;This is how you might use it to draw an object:



#INCLUDE "TI-85.H"
	.org	0
	.db "Map Test v1.0f",0

	ROM_CALL(CLEARLCD)
	ld	a,4
	OUT	(5),a
	ld	a,60
	ld	b,a
	ld	a,30
	ld	c,a
	ld	de,TestMap
	ld	hl,(PROGRAM_ADDR)
	CALL_(DrawMap)
	ld	a,0
	ld	b,a
	ld	a,40
	ld	c,a
	ld	de,TestMap2
	ld	hl,(PROGRAM_ADDR)
	CALL_(DrawMap)
Pause:
	call GET_KEY
	or	a
	jr	z,Pause
	ret

		;-----------------
		;DrawMap:
		;assumes bc= top left start
		;de = offset of bitmap string
		;hl = byte to begin drawing
		;keep in mind, each byte = eight bits
		;code:
		;example, 10110010
		;equals 3 black pixels (MSB is 1, 2^1 and 2^0 are on)
		;equals 2 white pixels (MSB is 0, 2^1 is on)
		; (thanks to Dines for the above ideas)
		; 0000b in the low nybble is a linereturn,
		;00000000b is a stop of map
		;if you want a blank line, then write 10000000b
		;no black pixels, and a line return
		;AFTER EACH LINE RETURN IT GOES BACK TO THE
		;ORIGINAL X POSITION, WITH Y INCREASED!
		;------------------
DrawMap:
	add 	hl,de 	;beginning now in hl
	ld	de,$0000
	ld	e,b
DrawM:
	ld	a,(hl)	;put the byte in a
	or	a	;check if it's 00000000b,
	ret	z	;if it is, stop!
TestTop:	;test top nybble
	and	$F0
	srl	a
	srl	a
	srl	a
	srl	a		;get top nybble to lower rung
	CALL_(ColorTest)
TestBottom:
	ld	a,(hl)	;put byte in a
	and	$0F	;get low nybble
	or	a
	JUMP_Z(LineReturn)
	CALL_(ColorTest)
NextByte:
	inc	hl	;next byte
	JUMP_(DrawM)	;start again

ColorTest:
	bit	3,a	;test for color, 0=white, 1=black
	push	af
	CALL_Z(White)
	pop	af
	CALL_NZ(Black)	;we have to test twice, cause we're calling
	ret

White:
	CALL_(NybbleTest)
	CALL_(LineOff)
	ret

Black:
	CALL_(NybbleTest)
	CALL_(LineOn)
	ret

NybbleTest:
	ld	d,$00
	bit	2,a	;check the 2^2 bit (4 bit)
	CALL_NZ(Add4)	;add 4 if it is on
	bit	1,a	;check the 2^1 bit
	CALL_NZ(Add2)	;add 2 if it is on
	bit	0,a	;check the 2^0 bit
	CALL_NZ(Add1)	;add 1 if it is on
	ret

Add4:
	inc	d
	inc	d	;add 2 (total = 4)
Add2:
	inc	d	;add 1 (total = 2)
Add1:
	inc	d	;add 1 (total = 1)
	ret

LineReturn:
	dec	c 	;decreases y
	ld	b,e
	inc	hl
	JUMP_(DrawM)

LineOn:
	CALL_(CheckPoint)
	or	a
	CALL_Z(PointOn)	;if the point is off, need to turn it on
	inc	b	;else, inc b and repeat
	dec	d	;decrease the counter
	ret	z	;return if it's zero
	JUMP_(LineOn)

LineOff:
	CALL_(CheckPoint)
	or	a
	CALL_NZ(PointOff)	;if the point is NOT off, we need to do it
	inc	b
	dec	d
	ret	z	
	JUMP_(LineOff)

CheckPoint:	;a=0 pt off, a>0, point on
			;credit to magnus... 
   push bc                  ;Store away, so it can be used again
   push de
   push hl
   ROM_CALL(FIND_PIXEL)     ;Get the pixel offset
   ld   de,$FC00
   add  hl,de               ;Point into graphics memory
   and  (hl)                ;ACC is ACC AND the memory
   pop  hl
   pop  de
   pop  bc                  ;Restore
   ret


	;--------------------
	;PointOn, turns on a point, b=x c=y, saves all regs.
	;--------------------
PointOn:
	push	bc	;save it for later
	push	de
	push	hl
	ROM_CALL(FIND_PIXEL)	;returns HL offset of pix
				;address, a= bit to change
	ld	de,$FC00	;FC00 is the beginning of
				;the video buffer
	add	hl,de	;add the offset to the buffer addr,
			;hl points to the location!
	or	(hl)	;ors the byte to change with whats
			;at (hl) the byte at present.
			;if it's the same, the same, diff, diff.
			;to turn it on, we need to load that
			;value in thar!
	ld	(hl),a	;there we go!
	pop	hl
	pop	de
	pop	bc	;returns bc to its original contents
	ret

	;-----------------------
	;point off, b=x c=y, pushes and pops all regs
	;-----------------------

PointOff:
	push	bc	;save for later
	push	de
	push	hl
	ROM_CALL(FIND_PIXEL)	;gives hl=offset, a=change
	ld	de,$FC00	;get video buffer
	add	hl,de		;hl now points to pixel
	xor	255		;only bit that is different,
				;becomes a one
	and	(hl)		;becomes a zero if diff, one
				;if other is one
	ld	(hl),a		;load it in
	pop	hl
	pop	de
	pop	bc
	ret

TestMap:
	.db $29,$19,$10		;this draws a heart
	.db $1D,$10
	.db $F0
	.db $1D,$10
	.db $2B,$20
	.db $39,$30
	.db $00


TestMap2:
	.db $FF,$77,$FF,$F0	;dotted line
	.db $FF,$77,$FF,$F0
	.db $2F,$2F,$2F,$2F
	.db $00

.end

; Any advice for optimization would be *greatly* apprieciated, because
;I want to implement this in a program (finally a program!!)

;thanks.

;raju
