Graphics programming on the TI-89
By anonymous
This guide teaches readers how to program sprites on a TI-89

In order to have an effective game, you need to know how to do graphics programming. On a TI-89, these images are reffered to as sprites. In order to display a sprite on the screen, we need the following three things:

1. The Screen
2. The sprite
3. The routine to put the sprite on the screen

I am assuming that you already have the screen, unless you have torn it out, so let's start with the sprite.

A bitmap image on a computer has three key parts in order to work. The three parts are the dimensions of the image, the color palette, and each individual pixel. A sprite has the same basic components except there is no color scheme. In order to do grayscale programming, multiple sprites are flashed up on the screen quickly. Look at the following representation of a plus sign:

Plus_Sign
dc.w		1,8
dc.b		%00011000,%00000000
dc.b		%00011000,%00000000
dc.b		%00011000,%00000000
dc.b		%11111111,%00000000
dc.b		%11111111,%00000000
dc.b		%00011000,%00000000
dc.b		%00011000,%00000000
dc.b		%00011000,%00000000

Let me briefly explain the various parts of this.

Plus_Sign

It is always important to label your data. It is also best to make your programs a little clearer by having a : after a label for a routine and no : after a label for data. This makes your program a little easier to understand. It is also very useful to make recognizable labels in your program. Do not worry about the length of your labels or how many labels you have in a program, because a label takes up NO room in a compiled program.

dc.w		1,8

This tells the dimensions of your image. The first number is the amount of words per row and the second number is the amount of rows. Make sure you use dc.w and not .b or .l. Those of you who are stumped at why I use words per row instead of bytes per row will figure out later

dc.b		%00011000,%00000000
dc.b		%00011000,%00000000
dc.b		%00011000,%00000000
dc.b		%11111111,%00000000
dc.b		%11111111,%00000000
dc.b		%00011000,%00000000
dc.b		%00011000,%00000000
dc.b		%00011000,%00000000

This is the actual image part. On the TI-89 screen, 1 pixel is represented by 1 bit of data, so with changing 1 byte, you change a total of 8 pixels in a row. So in our program, 1 bit will be used to represent 1 pixel to turn on or off, so we write it in binary with a 1 representing a pixel turned on and a 0 representing a pixel not turned on. We write it out in binary (the %) and a byte at a time (dc.b) so that it is a little easier to see the picture and so that we can tell the breakup of the image into each byte. Some of you may be wondering why I have a whole column blank. If I did not have a column blank to make it fit the dimensions, the image would appear to the routine to put it on the screen like this:

Plus_Sign
dc.w		1,8			;make sure it is dc.w
dc.b		%00011000,%00011000	;make sure it is dc.b
dc.b		%00011000,%11111111
dc.b		%11111111,%00011000
dc.b		%00011000,%00011000
dc.b		%00101011,%11010001	;anything after this would vary 
dc.b		%11010101,%00110101
dc.b		%00110100,%10100011
dc.b		%01100101,%11011001

Now that we have the picture, we can move onto the next part of the program, the routine. Your routine needs to have several things inputted into the routine. These are the things needed:

1. The location of the screen to place the image
2.The x and y coordinates of where to place the image
3. The location of the image
4. The size and dimensions of the image

To start out, let's deal with this one step at a time. You can put the location of the screen in one variable and you can choose to combine the x and y coordinates right off the bat into another variable or not, and once you have the location of the image you can get the size and dimensions.

The reason you can combine the x and y coordinates is because what you are really getting is an offset from the location of the screen to start putting the image down at. Also, the memory the calculator uses before displaying the picture on the LCD is not a whole bunch of lines in memory, but basically one long line of memory, with after each line it just wraps around to the next line. To combine x and y you do the following if d0 was x, d1 was y, and then d0 was your output variable.

muls.w		#240,d1
add.l		d1,d0

This is the equivalent of d0=240*y+x. There are 240 pixels in a row (The TI-89 only displays 160 of them, but the amount of memory set aside for the TI-89 screen is identical to the TI-92 and the TI-92+ which has 240 pixels per row), so we multiply the row number by 240, and then we add the column number and we have the starting pixel offset. Let's write out the program that we have so far:

	include	tios.h
	xdef		_ti89
	xdef		_main
	xdef		_comment
_main:
	move.l		#$4c00,a0		;store the address of the screen into a0
	move.l		#Plus_Sign,a1	;store the address of the image into a1
	move.l		#5,d0			;store the column number into d0
	move.l		#2,d1			;store the row number into d1
	muls.w		#240,d1
	add.l		d1,d0			;combine x and y into d0
								; The previous 4 lines could also be written out in the following way:
								;	move.l		#240*2+5,d0
	jsr		drawsprite			;we will make the routine later
	rts
drawsprite:						;It does nothing right now
	rts
_comment
	dc.b		"Sprite Routine",0
Plus_Sign		;here's our picture
	dc.w		1,8
	dc.b		%00011000,%00000000
	dc.b		%00011000,%00000000
	dc.b		%00011000,%00000000
	dc.b		%11111111,%00000000
	dc.b		%11111111,%00000000
	dc.b		%00011000,%00000000
	dc.b		%00011000,%00000000
	dc.b		%00011000,%00000000
	end		;required at the end of every program

If you tried compiling the program, and then you tried running it, nothing should have happened. Now that you have the basic part of your program written, we need to concentrate on the routine to display your sprite. Right now you have the following information:

a0=The location of the screen,

a1=The locaton of your sprite, and

d0=The offset in pixels from the starting location of the screen.

Let's start by determining the following:the dimensions of the picture, the locaton to start putting the picture to the screen, and how we are going to go about making the sprite-displaying routine.

Since the TI-89 has 8 pixels per byte, advancing 1 byte will put the image 8 pixels to the right. Therefore, we need to move the picture with one of the rotate commands. In order to do rotating we will also need to overlap between each byte. If we don't, we will have blank spots. Look at the following situations:

	dc.b		%00001111,%11110000

Each byte rotated to the left two with wrap-around and written with MOVE

	dc.b		%00111100,%11000011

Each byte rotated to the left two without wrap-around and written with MOVE

	dc.b		%00111100,%11000000

Storing each byte into a word, rotating each word two to the left with or without wrap-around, and then written with OR

	dc.b		%00000000,%00111111,%11000000

Each word:

	dc.b		%00000000,%00111100
	dc.b		%00000011,%11000000

From the analysis, you need to store the data into something twice its size and then rotating it, and then writing it to the screen with OR. But if you advance one byte at a time, you will be trying to write a word to and odd location, causing an address error. An address error occurs when you are reading or writing a word or longword to an odd data location. In order to not cause an address error we need to be able to jump 2 bytes at a time, and we also need to be able to rotate the picture up to 15 pixels to the right. If you're confused, this isn't the worst of it, but let's stop with what we have and work with what we've got. Add it to right after drawsprite and before rts.

	move.b		d0,d1
	and.l		#$F,d1
	and.l		#$FFFFFFF0,d0
	lsr		#3,d0

Here are the various parts. d1 now contains a number between 0 and 15, which is the number of pixels to rotate the image to the right, and d0 now contains the offset in bytes from the start of the screen that the image needs to be drawn. Now we add the screen to d0 to get the starting position of the image in bytes. Add the following after the last place you added text:

	add.1		a0,d0

In order to display our sprite, we need to use dbra twice. Once for the vertical routine, and once for the horizontal routine. The amount of times to use each routine is already stored at the front of the sprite, but we also need to subtract 1 from each because dbra does the routine one more time than the value in the variable. Add the following after the last place you added text:

	move.w		(a1)+,d2
	move.w		(a1)+,d3
	sub.l		#1,d2
	sub.l		#1,d3

Now you have your data initialized correctly for the dbra routine. Here are the parts in the routine:

Vert:

Horiz:

draw the picture

dbra Horiz

dbra Vert

First we make a label for the vertical and horizontal routine and put in the dbra commands. Add the following lines where you left off:

drawspritevert:
drawspritehoriz:
	dbra.w		d2,drawspritevert
	dbra.w		d3,drawspritehoriz

Since we will need to repeat the horiz routine, we need to copy d2 to another variable and then use the other variable in the place of d2. So add between the labels drawspritevert: and drawspritehoriz: the following:

	move.l		d2,d4

Then change

	dbra.w		d2,drawspritevert

to

	dbra.w		d4,drawspritehoriz

Now in order to jump a whole line between each time we do the horizontal routine, we need to add the following after move.l d2,d4:

	move.l		d0,a2
	add.l		#30,d0

We have now succesfully gone a step further and have completed the vertical section. Now on th the next part. We first need to clear out the variable where we are going to work with the shifting and all that other junk, and then we need to copy the variable onto the screen. Remember back to the part about rotating the data and needing to jump a word at a time? Here is where it really comes in handy. We copy a word from the sprite into d5, advance the sprite to the next word and then we use ror to rotate it to the right the amount of times specified by d1. But wait! It's already to the right! What we need to do is swap the upper and lower words of d5 and then rotate it to the right the amount of times specified in d1. Let's stick what we've got so far after drawspritehoriz:

	clr.l		d5
	move.w		(a1)+,d5
	swap		d5
	ror.l		d1,d5

Now all we've got left is to put the image on the screen. Here you have several options:

1. Stick the sprite on using OR logic, where black on black, black on white, and white on black make black, but white on white makes white.
Stick the sprite on using XOR logic, where black on black and white on white make white, and black on white or white on black make black
Erase the sprite by inversing d5 and then using AND logic which causes black on the sprite to become white on the screen, else it retains its color.

If you choose to use the first one, insert the following routine where you left off:

	or.l		d5,(a2)
	lea		2(a2),a2

If you choose the second, insert this instead:

	eor.l		d5,(a2)
	lea		2(a2),a2

If you choose the last one, insert this instead:

	not		d5
	and.l		d5,(a2)
	lea		2(a2),a2

You can also use the same routine to do crash detection by inserting the following instead of the above and the two dbra commands.

	and.l		(a2),d5
	lea		2(a2),a2
	cmp.l		#0,d5
	dbne		d4,drawspritehoriz
	dbne		d3,drawspritevert

There you have it! The drawsprite routine should look like this if you chose the first method of putting it on the screen:

drawsprite:
	move.b		d0,d1
	and.l		#$F,d1
	and.l		#$FFFFFFF0,d0
	lsr			#3,d0
	add.l		a0,d0
	move.l		(a1)+,d2
	move.l		(a1)+,d3
	sub.l		#1,d2
	sub.l		#1,d3

drawspritevert:
	move.l		d2,d4
	move.l		d0,a2
	add.l		#30,d0

drawspritehoriz:
	clr.w		d5
	move.w		(a1)+,d5
	swap		d5
	ror.l		d1,d5
	or.l		d5,(a2)
	lea			2(a2),a2
	dbra.w		d4,drawspritevert
	dbra.w		d3,drawspritehoriz