ticalc.org
Basics Archives Community Services Programming
Hardware Help About Search Your Account
   Home :: Programming :: Columns and Tutorials :: Usgard Programming: Lesson 1
Usgard Programming: Lesson 1

by Terry Peng

INTRODUCTION

Welcome to the Usgard Tutorial! So you're interested in becoming an Usgard programmer, eh? Well, before you get started make sure you have the zip file Usgard v1.1 for programmers. You should also have a graphlink/homemade link and link program, and should be fairly familiar with the shells and program strings from a user's point of view.

If you need help with these, goto the Usgard Homepage

You also need a PC, NOT a MAC. There is no TASM equivalent for MAC. Since I use a lot of ASCII Drawings, view these files with a fixed width editor like NotePad or DOS Editor. You WILL need an understanding of basic algebra, and number bases. I'll do a short review of number bases incase you've forgotten.

Number Bases

There are three number bases that you will use, binary (base 2), decimal (base 10), and hexadecimal (base 16). (Bin,Dec,Hex for short). Decimal, of course, is the number system we all use in our everyday lives.

In binary, however, there are only two digits, 1 and 0. We represent a binary number by putting a % before it. Basically for binary numbers you simply multiply by 2 raised to the power of the binary digit (bit) number. It is easier to understand by looking at a diagram:

lets look at this binary number: %11100011
1       1      1      0      0      0      1      1
*2^7    *2^6   *2^5   *2^4   *2^3   *2^2   *2^1   *2^0 =
128 +   64 +   32 +    0 +    0 +    0 +    2 +   1    = 227

%11100011 = 227 in decimal

The same thing can be done with hex numbers, although there are more hex digits, so it is a little more complicated, but still easy:

Dec	Hex	Bin
0	0	0000
1	1	0001
2	2	0010
3	3	0011
4	4	0100
5	5	0101
6	6	0110
7	7	0111
8	8	1000
9	9	1001
10	A	1010
11	B	1011
12	C	1100
13	D	1101
14	E	1110
15	F	1111

Hex numbers are represented with a $ before them. The number base for hex is 16.

$FC00=

F         C          0         0
15*16^3 + 12*16^2 +  0*16^1 +  0*16^0  =
61440	+    3072 +  0      +  0       = 64512 dec
$FC00 = 64512 dec

Luckily you have a calculator to make these calculations for you!

Memory and variables

One of the big differences between assembly and BASIC or C is that in assembly, there are no variables! How is this possible? Well, instead of variables we have REGISTERS. Registers are high-speed memory locations in the processor itself. There are 7 main registers; A,B,C,D,E,H,L. It is better not to think of the registers as variables though. You will see why soon.

The A register is special. It is sometimes called the accumulator. Often you can use the A register when others can't be used. Also most math operations store the results in the A register.

A,B,C,D,E,F,H, and L are 8-bit registers. What does this mean? It means that these registers cannot hold more than 8 bits of data, so it can never be more than 255 dec. One important thing to remember that one byte is two hex digits.

What about decimals (floating point numbers)? Well, you can't store decimals without a special routine. But that's okay, because you usually won't have to.

When accessed as BC,DE,and HL, the registers are called register pairs. Then they can hold 16 bits of data, or up to 65535. AF is technically a register pair although you cannot access it as a register pair in most cases.

TASM and program structure

TASM is your assembler. There is a neat batch file which does all your work for you, called c.bat in the main Usgard dir. Run it to compile your programs. Now about TASM...

Instructions

What is an instruction? It is command, sometimes with parameters, that TASM changes into a byte or bytes, and is interpreted by the processer to do a certain thing. Something like

	ld	a,b

You'll learn what that means later. But just remember to seperate it from the beginning of the line by space(s) or a tab. Seperate the parameters from the instruction by space(s) or a tab also. And seperate your parameters with commas (a space after the comma is optional)

Aliases

Note that just because we can't have variables does NOT mean we can't have constants! Except we call them ALIASES. You can define an alias by saying

video_mem = $fc00

or whatever. Now when you make a reference to video_mem the compiler replaces all occurences of video_mem with the number $fc00! Note that you should put all aliases at the beginning of your program.

	ld	a,b 	;these words are ignored

To store larger amounts of data, and to store it more permanantly we use the RAM. The memory is $10000 bytes large. That's 64k. $8000 is for the ROM. (although the ROM is actually $20000 (128k)... but only $8000 of it can be accessed at one time) $8000 is for the RAM, although you can't use all of it.

Usgard.h

At the beginning of each program, you type:

include	"usgard.h"

This simply searches the current directory for Usgard.h and inserts that file at the beginning of the program, so that you won't have to type it all. What is in Usgard.h? Just a bunch of aliases to make your life easier.

You can't just access any of the RAM that you want. The calculator's built in operating system (the ROM, also called the TI-OS) uses most of the RAM for variables and other data, which include program strings.

Luckily certain areas have been used in many games and are known to be safe. The main area is TEXT_MEM (168 bytes). Usgard clears TEXT_MEM to zeros before running a program. There are other safe areas, but they are not cleared by Usgard. These areas are GRAPH_MEM ($400 bytes), TEXT_MEM2 (168 bytes), DELC (100 bytes), and TEMP_STORAGE (128 bytes).

To reference a memory location, use parentheses around the value of the address. Addresses are always four hex digits (sixteen bits or two bytes). Notice that two bytes are commonly called a WORD. ALWAYS use hex when referencing addresses.

($fc00)

would mean the memory at the address $fc00.

Note that parenthesis are used ONLY WHEN ACCESSING THE DATA AT THE ADDRESS. If you just wanted the address, you wouldn't use parenthesis.

Labels

The program, as interpreted by TASM, starts at $0000. You can put a label in your program like this-

Label:

There is no space before the Label name, and there is a colon right after it. Basically the Label takes up no space, and TASM counts the bytes before it in the program, and it adds it to the starting point (zero). Then that value is the address of the label.

Unfortunately, the TI-85 was not designed for assembly. The TI-OS is always moving variables- so you never know where exactly in RAM the program will be. Usgard detects where it is when you try to run it. How does that help? Well, to tell Usgard when you are accessing a label, add a & before it. Usgard will add the program's address to the label's value- thus giving you the absolute address of the label in memory Note that you don't change an actual label-they are still written like this-

Label:

But when you ACCESS the label (as an address), type it like this-

	(&Label) or &Label

Using an ampersand to tell Usgard to add the program address is known as relocation. This may not make much sense- Relocation is actually supposed to be the shell moving the entire program to a specified address, but Usgard adds the programs address each time at run time. Still, I will use the term 'relocate' when I refer to putting an & before a reference to a label.

Comments

Now comments.... comments are basically just little notes you write to your self to remind you about something, or to tell the person reading the source what you are trying to do. Simply put a semicolon ; and the rest of the line is ignored by TASM, the assembler.

	ld	a,b	;these words are ignored

Data Storage

To store actual DATA in the program, use .db and .dw- .db=defined byte, .dw=defined word. So lets say you typed

.db $ff	;no space at start of line

TASM, instead of converting instructions to bytes, simply puts the data byte $ff directly into the executable file! You can store more than one byte per line by seperating them with commas.

.db $ff,$fe,$fc,$f9

although if you put too many on a line you will get an error. .dw is the same thing except it does two bytes (a word). You will see why this is important later.

ASCII Data Storage

ASCII is the code system for characters. How do you store ASCII characters and strings? Like this-

.db "Example of a string"
.db 'c'

Strings are enclosed by double quotes, characters by single quotes. Basically the characters are converted into bytes (based on an ASCII table), and stored. Later, by using routines in assembly, the ASCII values can be displayed as characters.

THE BASIC INSTRUCTIONS

Another big difference between assembly and other languages is that you can't assign values with a =, or use the +,-,*,/ signs, or use ==,>,< to test. You can't use if-then, for, or while statements either. Instead, everything is done with INSTRUCTIONS. Here are some common instructions:

The LD instruction (LOAD)

Instead of using the = sign to assign values, we use the instruction LD. The LD instruction loads values into registers. You can load direct values, or you can load other registers. Here are some vaild uses for the load instruction:

	ld	a,b
	ld	d,$ff
	ld	hl,$001f

When using register pairs, the most signifigant byte is stored in the the register whose letter comes first in the name of the register pair. So if I stored $001f into hl, then $00 would be stored to register h and $1f would be stored into register l.

	ld	a,($fc00)
	ld	($8641),a

Another way to access memory is by loading a register pair with the address, and then put parenthesis around the register pair.

	ld	hl,$fc00
	ld	e,(hl)

	ld	bc,$8641
	ld	(bc),a
	ld	bc,($8641)
	ld	($8641),bc

All words (two bytes) are stored with the least signifigant byte first. So in the last example register C would be stored in ($8641), then register B would be stored in ($8642).

note that you can't load direct values into memory, like this

	ld	($fc00),%11111111	;not allowed

you also can't load register pairs from other register pairs

	ld	bc,hl			;not allowed

here are some more traps... while these are allowed:

        ld      a,(reg pair)    ;allowed
	ld	reg,(hl)	;allowed

this is not:

	ld 	reg,(reg pair) 	;not allowed

Note that (hl) may be used in place of any of the registers, except not always in place of the accumulator. It MAY NOT be used in place of register pairs.

Loading negative numbers

So how do you load negative numbers? Well, when writing the source code it is the same. Simply put

	ld	b,-1

But how do you store a negative in 8-bits? After all, you can only truly store positive numbers - well, what happens is it is stored like this: for -x, you do a 2^x+1 and store the result as a postive number (the result of that equation is always positive). But how can you work that out in your head? It is not worth it. Instead I suggest you do a 256-x. So -1 would actually be stored as 255. You can still type it in as -1, though, TASM accepts that. Why does this work? We will see later.

	ld	hl,-1

the same thing works for register pairs, except that they are stored as 65536-x.

INC AND DEC (INCREMENT and DECREMENT)

INC increases a register or register pair by one.

	inc	a
	inc	de

What happens if you INC a register when it is $ff (255 dec)? Well, it goes back to zero! Think of the register as wrapping around to zero. Register pairs wrap around after $ffff (65535 dec).

DEC decreases a register or register pair by one.

	dec	b
	dec	hl

What happens if you DEC a register when it is 0? Well, you guessed it, it goes back to around, to $ff! When register pairs are dec'ed when they are at zero, they go to $ffff.

ADD (ADDITION)

ADD adds a number or register to the accumulator, then stores the result in the accumulator.

	add	a,b
	add	a,8
	add	a,(hl)

So what happens if you add 1 to the acc (accumulator) when it is $ff? It goes to zero, just like when you inc it at $ff. What happens if you add 2 to it when it is at $ff? It goes to 1! So how do you figure out the result of an add if it involves a carry? Basically just subtract 256 from the answer. OK, now remember how I said that negative numbers are stored as positves? Remember the example of negative 1. It would be stored as 256-1, or 255. Now what happens when you add 255 to say, 100? add the numbers together to get 355:

	 100
	+255
	====
	 355

then subtract 256 from the result:

	 355
	-256
	====
	 099

the answer is 99, or 100 + negative 1! Pretty nifty, huh?

You can also add to hl, except that it cannot add to direct values- only register pairs. It stores the result in hl.

	add	hl,de

In this case, you will want to subtract 65535 from the answer if it carries.

SUB (SUBTRACTION)

SUB is like ADD, but a little more restricted. You can't sub with 16 bit registers, you will how to do that learn that in another lesson.

	sub	b
	sub	8

If the result of a sub is a negative number, what happens? Well, add 256 to it and that is the result! (it is like loading negative numbers, I hope you did not skip that section!)

Jumps

Jumps are similar to gotos in basic or C.

JP (ABSOLUTE JUMP)

        jp      &Lbl    ;absolute jump to Lbl
	....		;code here is skipped
	....		;code here is skipped
	....		;code here is skipped
Lbl:

This jumps to Lbl. REMEMBER to put an ampersand before the Lbl, and do NOT use parenthesis. You are simply telling the processor to use the address, not to use the data at the addressed memory.

JR (RELATIVE JUMP)

	jr	Lbl	;relative jump to Lbl
	....		;code here is skipped
	....		;code here is skipped
	....		;code here is skipped
Lbl:

This jumps to Lbl. DO NOT put an ampersand before the Lbl, and do NOT use parenthesis. Why not relocate? Because this is a special case- TASM actually changes the parameter to a byte that tells the processor to jump forward or back a certain amount. In this case, no matter where in memory the program is, it will always work without Usgard adding the Program's Address. In fact, your calculator will probably crash if you do relocate it.

The difference

JR is smaller, and faster, but it has a certain range. If you ever get a TASM error like this:

program.asm line 0006: Range of relative branch exceeded

simply goto the line it gives an error for, and change the JR to JP. DON'T FORGET TO RELOCATE!

Call and Return

Using call is like calling a subroutine or function (duh). Anyway, here's how to do it:

	call	&Lbl	;goes to Lbl
	.......		;main program
Lbl:
	.......		;routine
	ret		;goes back to main program

The syntax is the same as JP, except it is CALL. The program jumps to Lbl, and then when it reaches a RET(return) instruction, it returns to where the CALL originally occured. Note that Usgard runs programs by CALLing their address; so you can return to Usgard by inserting a RET instruction in your code.

	.......		;main program
	ret		;goes back to shell

Flags and conditionals

So if you're not allowed to use IF statements, how do you do conditionals? There is a special register for conditionals, it is called the flag register. There are actually many flags, but only two are important. These are the zero flag and carry flag. How are the flags changed? Well, basically the results of a math operation affects the flag. Lets say after doing a SUB, the acc is zero.

	ld	a,10	;a=10
	ld	b,10	;b=10
	sub	b	;a=a-b... a=10-10... a=0

After this command the ZERO FLAG is SET. So what good is that? Well, you can make conditions for your jumps, calls, and returns to occur. You simply put a flag name as a first parameter and put the label as the second parameter. Possible first parameters (conditions) are z,nz,c,nc.

	jr	z,Lbl
	jp	z,&Lbl
	call	z,&Lbl
	ret	z

So what does that do? Well, if the ZERO FLAG is set (as discussed above), the instruction happens. (jump,call,ret) Otherwise, nothing happens. You can change the first parameter (condition) to change what situation the jump, call, or return is executed.

Z	=	Zero flag set
NZ	=	Zero flag not set
C	=	Carry flag set
NC	=	Carry flag reset

So you already know what the zero flag does. But what about the Carry Flag? Well, anytime a 'wraparound' of the affected register happens, the carry flag is set. Otherwise it is reset. SOMETIMES OPERATIONS ON REGISTER PAIRS WILL NOT HAVE THE SAME AFFECT. One big example is inc or dec on a reg pair.

Note that LD is not a math operation; it NEVER changes ANY FLAG.

Your first program

#include "usgard.h"

.org 0
.db "My first program",0

	call	CLEARLCD
	ld	hl,0
	ld	(CURSOR_ROW),hl
	ld	hl,&Text
	call	D_ZT_STR
Wait_4_key:
	call	GET_KEY
	or	a
	ret	nz
	jr	Wait_4_key
Text:
.db "Hello World!",0

.end

You may not understand this program, but it will be discussed in detail in the next lesson.

Cut and paste this program into a new text file, and name it hello.asm. Make sure you leave a few empty lines after the last line. (.end)

Change to the Usgard main dir, and type this in at the DOS prompt:

c hello

Then send it using whatever link program you have. It should have a description of "My first program". When you enter the program it should display "Hello World" then wait for a key and return.

Congratulations, you have compiled your first program!

  Copyright © 1996-2012, the ticalc.org project. All rights reserved. | Contact Us | Disclaimer