Sample Program


Assembled Program: bounce.89z
Source Code: bounce.s

     When programming in assembly language, the hardest part can be getting started. We have written a sample program in TI-89 assembly, using TIGCC. This program simply makes a small ball bounce around the screen, taking 179 lines of code to do so. In this program, we demonstrate how to push and pop data to and from the stack, how to draw to the LCD screen, and how to call functions already in the TI-89's operating system, among other things.

     To run this program, you may download the file from here, or you can copy the following code and paste it into an open TIGCC GNU Assembly Source File. The ball is easier to see on Virtual TI than it is on a real TI-89.

| Assembly Source File

|-----------------------------------------------------------------------------------------
| External Symbol Exports
|
   .xdef     _ti89                    | create TI-89 program file

|-----------------------------------------------------------------------------------------
| Variables
|
   .equ      AMS_jumptable,0xC8

   .equ      kbhit,4*0x52             | Check for button press, returns keycode
   .equ      GKeyFlush,4*0x180        | "Forgets" any keys already pressed
   .equ      ClrScr,4*0x19e           | Erases contents of the screen
   .equ      memcpy,4*0x26a

   .equ      LCD_MEM, 0x4c00
   .equ      AMAXX, 154               | The maximum X for the ball anchor (true max 159)
   .equ      AMAXY, 95                | The maximum Y for the ball anchor (true max 99)
   .equ      MASK, 0x6FF6             | Ball pattern mask

|-----------------------------------------------------------------------------------------
| ASM_loader - prepares the program to be run
|      saves and restores the LCD screen and executes the _main method
| Code originally from Techno-Plaza's tutorial routines:
| http://www.technoplaza.net/assembly/

ASM_loader:
   movem.l   %d3-%d7/%a2-%a6,-(%sp)   | save registers

   lea       (%sp,-3840),%sp          | grab some memory on the stack
   pea       3840                     | save 3,840 bytes
   pea       0x4C00                   | address of LCD
   pea       (%sp,8)                  | where to save the screen memory to
   move.l    0xC8.w,%a0               | load the AMS jumptable into a0
   move.l    (%a0,memcpy),%a2         | load the memcpy function
   jsr       (%a2)                    | execute the memcpy function to save the LCD

   jbsr      _main                    | start the program

   pea       3840                     | restore 3,840 bytes
   pea       (%sp,16)                 | address of screen memory saved on the stack
   pea       0x4C00                   | address of LCD
   move.l    0xC8.w,%a0               | load AMS jumptable into a0
   move.l    (%a0,memcpy),%a2         | load memcpy function
   jsr       (%a2)                    | execute memcpy function to restore the LCD
   lea       (%sp,3864),%sp           | restore the stack pointer

   movem.l   (%sp)+,%d3-%d7/%a2-%a6   | restore registers
   rts                                | leave the program

|-----------------------------------------------------------------------------------------
| _main - Program starts at this label
|
| Basic Program Flow:
| • Clear screen and set variables
| • Slow-down loop
| • Check for keyboard press
| • Switch directions if ball hits a wall
| • Erase ball
| • Move the ball based on new directions
| • Draw ball
| • Return to "Slow-down loop"

_main:
   movem.l   %d3-%d7/%a3-%a5,-(%sp)   | save used registers

   | Clear the screen
   move.l    #LCD_MEM,%a3             | Find the start of the LCD screen
   link.w    %a6,#0                   | preserve the stack pointer location
   move.l    AMS_jumptable,%a5        | load the jumptable
   move.l    ClrScr(%a5),%a4          | load the ClrScr function
   jbsr      (%a4)                    | execute the ClrScr function
   move.l    kbhit(%a5),%a4           | load the kbhit function

   move.w    #0, %d3                  | Set initial x value
   move.w    #0, %d4                  | Set initial y value
   move.w    #1, %d5                  | Set initial dx value
   move.w    #1, %d6                  | Set initial dy value
   move.w    #MASK, %d7               | Store ball mask

loop:
   move.l    #0x00001FFF,%d0          | Increase this to slow down program, decrease
                                      | to speed up
timewastingloop:
   sub.l     #1,%d0                   | Loop does nothing but slow the program down
   jne       timewastingloop

   move.l    kbhit(%a5),%a4           | load the kbhit function
   jbsr      (%a4)                    | execute the kbhit function
   tst.w     %d0                      | Check if key was pressed
   jne       endp                     | End if user pressed any key

   tst       %d5                      | Check dx
   jgt       posx

   | X is moving in a negative direction (to the left)
   cmp       #-1,%d3                  | Has X hit left wall?
   jne       testy                    | If it doesn't hit a wall, continue

   | X is moving to the left and has hit the left wall
   move.w    #1, %d5                  | Switch direction
   jra       testy

posx:
   | X is moving in a positive direction (to the right)
   cmp       #AMAXX, %d3              | Has X hit right wall?
   jle       testy                    | Continue if it doesn't hit a wall

   | X is moving right and has hit the right wall
   move.w    #0, %d5                  | Switch direction

testy:
   tst       %d6                      | Check dy
   jgt       posy

   | Y is moving in a negative direction (up)
   tst       %d4                      | Has Y hit top?
   jne       moveimg                  | If it doesn't hit a wall, continue

   | Y is moving up and has hit the top wall
   move.w    #1, %d6                  | Switch direction
   jra       moveimg

posy:
   | Y is moving in a positive direction (down)
   cmp       #AMAXY, %d4              | Has Y hit bottom?
   jle       moveimg                  | Continue if it doesn't hit a wall

   | Y is moving down and has hit the bottom wall
   move.w    #0, %d6                  | Switch direction

moveimg:
   movem.l   %d3-%d6,-(%sp)           | Save registers
   move.w    #0x8000,%d5              | Masker
   move.w    #0,%d6                   | Loop counter

clearimg:
   add.w     #1,%d6                   | Increment loop counter
   and       %d5,%d7                  | Check mask
   jeq       clearimgshortloop        | Do not change if not masked

   cmp.w     #5,%d6                   | Separate X and Y components
   jlt       noswap
   sub.w     #4,%d6                   | Subtract 4 X units
   swap      %d6
   add.w     #1,%d6                   | Add 1 Y unit
   swap      %d6

noswap:
   add.w     %d6,%d3                  | Add X offset
   swap      %d6
   add.w     %d6,%d4                  | Add Y offset
   swap      %d6
   clr.l     %d0                      | clear for temp calculations
   clr.l     %d1

   | Convert X and Y to one dimension
   | Y bytes = Y rows * 240 pixels/row / 8 pixels/byte
   move.w    %d4,%d0
   mulu.w    #30,%d0

   | X bytes = X columns / 8 columns/row + Remainder
   move.w    %d3,%d1
   divu.w    #8,%d1

   | Save remainder
   move.l    %d1,%d2                  | Copy (remainder + quotient)
   clr.w     %d2                      | Clear quotient
   swap      %d2                      | Move remainder to lower word

   add.w     %d1,%d0                  | %d0 = Pixel byte offset (%d1 now useless)
   moveq.l   #7,%d1                   | Bits are numbered backwards
   sub.w     %d2,%d1                  | Invert remainder (%d2)

   | %a3 + %d0 now contains the absolute location of the selected bit.

   bclr.b    %d1,(%d0,%a3)            | Clear bit

   sub.w     %d6,%d3                  | Reset anchor point
   swap      %d6
   sub.w     %d6,%d4
   swap      %d6

clearimgshortloop:
   move.w    #MASK, %d7               | Replace ball mask
   lsr       #1,%d5                   | Shift %d5 to next position
   jcs       changeanchorx            | If the set bit shifted into the carry, exit
   jra       clearimg                 | Loop

changeanchorx:
   movem.l   (%sp)+,%d3-%d6           | restore used registers

   tst       %d5                      | Check dx
   jgt       moveimgright             | Move right if positive
   | Negative dx
   sub.w     #1,%d3                   | Move drawing anchor left
   jra       changeanchory

moveimgright:
   add.w     #1,%d3                   | Move drawing anchor right

changeanchory:
   tst       %d6                      | Check dy
   jgt       moveimgdown              | Move down if positive
   | Negative dy
   sub.w     #1,%d4                   | Move anchor up
   jra       drawimage

moveimgdown:
   add.w     #1,%d4                   | Move anchor down

drawimage:
   movem.l   %d3-%d6,-(%sp)           | Save registers
   move.w    #0x8000,%d5              | Masker
   move.w    #0,%d6                   | Loop counter

drawimgloop:
   add.w     #1,%d6                   | Increment loop counter
   and       %d5,%d7                  | Check mask
   jeq       drawimgshortloop         | Do not change if not masked

   cmp.w     #5,%d6                   | Separate X and Y components
   jlt       noswaptwo
   sub.w     #4,%d6                   | Subtract 4 X units
   swap      %d6
   add.w     #1,%d6                   | Add 1 Y unit
   swap      %d6

noswaptwo:
   add.w     %d6,%d3                  | Add X offset
   swap      %d6
   add.w     %d6,%d4                  | Add Y offset
   swap      %d6
   clr.l     %d0                      | clear for temp calculations
   clr.l     %d1

   | Convert X and Y to one dimension
   | Y bytes = Y rows * 240 pixels/row / 8 pixels/byte
   move.w    %d4,%d0
   mulu.w    #30,%d0

   | X bytes = X columns / 8 columns/row + Remainder
   move.w    %d3,%d1
   divu.w    #8,%d1

   | Save remainder
   move.l    %d1,%d2                  | Copy (remainder + quotient)
   clr.w     %d2                      | Clear quotient
   swap      %d2                      | Move remainder to lower word

   add.w     %d1,%d0                  | %d0 = Pixel byte offset (%d1 now useless)
   moveq.l   #7,%d1                   | Bits are numbered backwards
   sub.w     %d2,%d1                  | Invert remainder (%d2)

   | %a3 + %d0 now contains the absolute location of the selected bit.

   bset.b    %d1,(%d0,%a3)            | Clear bit

   sub.w     %d6,%d3                  | Reset anchor point
   swap      %d6
   sub.w     %d6,%d4
   swap      %d6

drawimgshortloop:
   move.w    #MASK, %d7               | Replace ball mask
   lsr       #1,%d5                   | Shift %d5 to next position
   jcs       resetloop                | If the set bit shifted into the carry, exit
   jra       drawimgloop              | Loop

resetloop:
   movem.l   (%sp)+,%d3-%d6           | Restore used registers
   jra       loop                     | Loop

endp:
   move.l    GKeyFlush(%a5),%a4       | load the GKeyFlush function
   jbsr      (%a4)                    | execute the GKeyFlush function
   unlk      %a6                      | restore the stack pointer
   movem.l   (%sp)+,%d3-%d7/%a3-%a5   | restore used registers
   rts                                | exit our program


|-----------------------------------------------------------------------------------------
| Common Symbol Definitions
|

   .comm     _nostub,2                | create a nostub (non-kernel) program