4A

show how assembly language statements, the assembler, and the computer are ..... If you plan to v/rite programs to enhance the power of Basic, you will not ...
10MB taille 5 téléchargements 392 vues
TI Home Computer ASSEMBLY LANGUAGE PRIMER

\j^•

Each byte in memory has its own name tag, called its ad dress. As a member of a "computerized society", you should not be surprised that the address is a number. (Addresses are discusssed in greater depth in the chapter of this book that ex plains the memory of the computer.) For now, the important point is that each address refers to a unique byte in memory.

Sometimes memory is considered to consist of words rather than bytes. On the TI, a word is simply two bytes. However, the two bytes must occupy exactly'adjacent locations, and the first byte must have an even address. For example, the bytes at loca tions

0000 and

0001

are

one

word.

It is important to remember that word addresses must be

even. If you forget and use an odd address (or compute an odd address), the computer will not indicate an error condition but will simply use the next lower even address. used 0001 for a word address,

For example,

it would be treated as 0000.

if you If

you inadvertently make this mistake, it can be very difficult to track down. One of the skills of an assembly language programmer is therefore a constant alertness for odd addresses when only even will work properly.

\&y

Suppose the computer retrieves a byte out of its memory: just what does one look like? To understand them in better de tail, it is necessary to move down to the next level of speci ficity, bits. Although I said earlier that you could think of

bytes as characters, you also need to learn to think of t^ytes as groups of eight bits. A bit is a "Binary digiT." Each bit can be either 0 or 1. A byte is therefore eight 0's, l's, or a

Bits,

Nybbles,

combination

of

Bytes,

Words,

and Hex

both.

If you haven't tried using the Basic subroutines CHAR and HCHAR, try them now. This will help you understand just what bits and bytes are. Start by looking at this matrix, which defines a pattern. 10000000 01000000 00100000 00010000 00001000 00000100 00000010 00000001

As you know, you can redefine characters on the TI to have any shape you want them to have. Each character is eight bits

across and eight bits down. (When talking about the screen dis play, the terra pixels is essentially synonymous with bits.) The pattern of 0's and l's above can be made to represent a shape which is a line sloping to the left. Each 0 represents back ground color, and each 1 represents black. (These colors can be changed by calling COLOR.) This is how you specify the pattern above

with

a

call

to

the

CHAR

subroutine:

CALL CHAR(100,"8040201008040201")

To understand how the string "8040201008040201" represents the matrix of 0's and l's, we need to appreciate how much more compact the string is than the matrix, and just how important this is. The matrix has 64 0's and l's, while the string only has 16 characters. This compactness is important because it shows a big difference between the way people and computers work with symbols. Computers work well because they are as simple as

possible. Computers are also very fast, so they make up for doing simple things by doing many of them in rapid succession. We humans, on the other hand, are very slow by comparison. We make up for being slow by doing a lot at once. Let me make the point clearly.

Look at these three charac-

er strings briefly, one at a time, and turn away from the book and try to repeat each in turn.

1) 2) 3)

0111110110001100 7D8C 32140

If you are like everyone else, the second string was easier

\

to remember than the first, and the last was easier than the se-

**^

cond. In fact, the first string was much harder to remember, if You may be surprised to realin fact you remembered it at all. 8

Bits,

Nybbles,

Bytes,

Words,

and Hex

\^y ize that all are numbers and have the same value. The first num ber is in the binary number system, the second is in the hexa decimal number system, and the third is in the decimal number

system (which is what we humans normally use). The first is what computers "prefer", while we humans do much better with the se cond

and

third.

The first is difficult to remember because it is so long, even though the symbols are familiar and simple. The second is easier because it is shorter, but is uses a strange mixture of characters and numbers so it is not as easy as the third. The word "hexadecimal" comes from the Greek word for six

teen.

The list below shows you the correspondence between the

decimal, hexadecimal (or "hex" for short) symbols, and binary values.

and l's.

There are exactly 16 possible combinations of four 0's

They are listed here in numeric order, representing the

values 0 through 15.

Note that decimal and hexadecimal are the

same for 0 through 9 - hexadecimal must represent the values 10

through 15 with a single character, so the characters A through F are used as if they were digits. CORRESPONDENCE BETWEEN THREE NUMBER SYSTEMS Decimal 0 1 2 3

4 5 6 7 8 9 10 11 12

13 14 15

Binary 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111

Hexadecimal 0 1 2

3 4 5 6 7 8 9 A

B

C D E F

Look at the first two versions of the number again, as shown below. This time, the binary value has been split into groups of four digits, with the hex digit just below it. Also, the decimal equivalent for each group is shown. (Caution: Grouping these di gits together does not give the same as the decimal equivalent for the entire number.) You should be able to see hov/ each binary

|

group exactly matches the corresponding hexadecimal group.

Bits, Nybbles, Bytes, Words, and Hex

Clearly, hex is a nice shorthand for cumbersome binary strings. The advantage of hex over decimal can be seen here - in all cases, hex only requires one digit, whereas in some cases decimal requires two digits. 1) binary 2) hex 3) decimal

0111

1101

7

D

7

13

1000 8 8

Let's go back to the preceding matrix.

1100 C 12

As displayed below, the

matrix has been modified so that it consists of two sets of four

columns.

To the right of the matrix of 0's and l's are two col

umns of numbers, with the values 0, 1, 2, 4, and 8. The second two columns are simply hex, while the 0's and l's are binary.

Although you can actually see the pattern better when represented in binary, the hex method of coding is preferable because it has only one fourth as many digits. 1000 0000 0100 0000 0010 0000 0001

80

40 20 10 08 04 02 01

0000

0000 1000 0000 0100

0000 0010 0000 0001

'vsgjgF

Because hex is more convenient for humans to use,

the pat

tern is defined in the subroutine call as character 100 by 8040201008040201

rather than by 100000000100000000100000000000010000000001000000001000000001

To actually see what this character looks like, learn to visualize bits (or pixels),

and thus

try this simple Basic

program.

100 CALL CHAR(100,"8040201008040201") 110

CALL

CLEAR

120 CALL HCHAR(10,10,100)

You have now seen that eight bits can be represented by two

hex digits. Eight bits are also a byte, so sometimes each of the two hex digits in a byte is called a nybble. A nybble is thus four

bits. \^§F

10

Bits,

Nybbles , Bytes,

Words,

and Hex

%£,*-

As stated above, computers do well with simple things. Let's briefly look at the binary number system to see why virtually all computers use it rather than the decimal number

system which we humans prefer. Here are the complete addition and multiplication tables for binary. ADDITION 0 +

0

0 0

+

1

1

+

0

1

10

MULTIPLICATION 0 x

0

x

0

0 1

1 x

1

0

0

x

0

1 1

Computers are built to use binary arithmetic because it is

so simple. data

In fact, being binary also simplifies the memory and

transmission.

It is not really necessary for you to be fluent in binary or hexadecimal arithmetic.

You do need to understand them at a

fundamental level and be able to occasionally do an addition on hex numbers. What is more important is that you learn to think of bit strings and their equivalent hex strings, since not only numbers

but also characters and the machine's

instructions are

bit strings.

Having just said that fluency in hex arithmetic is not ne cessary, let us look at a few examples. HEXADECIMAL

0005 0005 000A 7E48

+ 0004 + 0005 + 0009 + 006E

= = = =

0009 000A 0013 7EB6

5

+

5 10 32328

+ + +

DECIMAL 4 = 9 5 = 10 9 = 19 110 =32438

You need to understand another important fact about binary and hex, and that is how most computers handle negative numbers. We have been taught to write a number the same way, whether pos

itive or negative, except that the minus sign "-" precedes a ne gative value.

However, nearly all computers represent negative

values by using the two's complement of the positive value. To get the 2's complement of a value in binary, start by changing each 0 to a 1, and change each 1 to a 0. (Making these changes to each of the bits is called complementing the bits.

The re

sult is called the one's complement.) Then add 1 to the re sult, forming the two's complement. In changing each bit, you 11

Bits,

Nybbles,

Bytes,

Words,

and Hex

must remember to change all the leading 0's as well. Thus, be cause the TI is a 16 bit machine, you need to complement each of the 16 bits, then add 1 to the rightmost bit. This example shows how to complement the value 1: BINARY 0000000000000001

Before

1111111111111110 After complementing each bit 1111111111111111 After adding 1 HEXADECIMAL 0001

Before

FFFE FFFF

After complementing each bit After adding 1

This shows that -1 is 1111111111111111 in binary and FFFF in hex. Compare that to a very large, positive number, the value 32,767. It is 0111111111111111 in binary and 7FFF in hex. It only differs from -1 in that the left-most bit is a 0 instead of a

1.

The good thing about using two's complement notation for negative numbers is that they can be added with exactly the same hardware logic as a positive number, since negative numbers are so large that the addition may cause overflow, thereby resulting in a positive value again. For example, let's add 5 and -1. In hex,

this is 0005 + FFFF.

It is shown here with the carries

above and in parentheses.

(1)

(1)

(1)

(1)

(1)

0

0

0

5

F

F

F

F

0

0

0

4

Adding the digits on the right side (5 + F) yields decimal 20, or hex 14. Carry the 1, and add 0 and F from the second to the

right digits.

That yields decimal 16, or hex 10.

again, and again. The final result is 10004. the left is lost (because it is the 17th bit), 0004,

which is indeed the sum of 5 and -1.

12

So carry the 1

But since the 1 on the result is

Bits,

Nybbles,

Bytes,

Words,

Here are several values,

and Hex

shown in hex and decimal.

These

are values you are apt to see frequently, so you should learn to recognize them. HEXADECIMAL 0001 000F 0010 00FF 0100 1000 FFFF FFFE FF00

DECIMAL 1 15 16 255 256 4096 -1 -2 -256

Remember that if you work out the bits for a value such as FFFF, which would be 65,535, they may also be negative numbers in two's complement form.

After all this detailed discussion, a brief summary is in order. A bit (short for "binary digit") is a 0 or a 1. A group of four bits can be represented by a single hex digit, called a nybble. Two nybbles, or two hex digits, make eight bits, and thus two hex digits represent a byte. Eight bits therefore also make a byte. Two bytes make 16 bits, and if the address of the first is even, 16 bits make a word. This is shown

here.

TERM Bit Nybble Byte Word

DEFINITION Binary digit 4 bits 8 bits=2 nybbles 16 bits=4 nybbles=2 bytes

Finally,

the computer does not care what is stored in a lo

cation.

RANGE OF VALUES (in hex) 0 and 1 0 through F 00 through FF 0000 through FFFF

It could be a character, a numeric value,

or an instruc

tion. To the memory of the computer, a byte is just eight bits regardless of what it may mean to you and regardless of how your program is supposed to use it. In fact, you frequently cannot tell what is stored in a byte if that is all the information you have. For instance, hex 7041 could be the characters "pA", a subtract instruction, the value 28737, part of the definition of a character, or many other things. Using the hexadecimal number system is merely a convenient way to write all these different things. Appendix C contains a Basic program you can use to convert values

from

decimal

to hexadecimal and

13

back.

\^0r

The

CPU

and Other

Processors

All computers have one (or more) central processing unit, called the CPU.

chip.

For microcomputers,

the entire CPU is on one

In the TI Home Computer, it is the TMS9900 chip, which

has been available since the 70's.

When it was introduced,

it

was a departure from the norm because it performs arithmetic on 16 bit integers (more on this later in the book) rather than on 8 bit integers. Today there are many 16 bit machines, including the IBM PC and the TI PC, and many somewhat similar machines. However, these other 16 bit machines use a different processor and are not packaged as inexpensive home computers.

When you learn assembly language for the 99/4A, much of what you learn is the instruction set for the 9900 processor. How ever, there is much more to the TI Home Computer than the CPU. There are also the visual display processor (called the VDP), a processor for the cassette port, the sound generator, and the speech synthesizer (not included in the console itself). If you plan to v/rite programs to enhance the power of Basic, you will not need to learn as much about these other processors. However, if you want to write an arcade game program, you v/ill not only have to learn how to program the CPU but also how to

control these other processors. (I say "control" because you can tell them what to do but you don't really program them to the extent that you program the CPU.) Note that when you write in Ba sic, you do have some limited degree of control over these func tions without having to understand them in much detail. This book will emphasize programming the CPU since that skill is needed no matter what you do with assembly language. Once you have grasped the concepts of programming in assembly language, you should be able to advance into these other areas

more readily by relying on the TI Editor/Assembler manual.

14

The Memory of the TI-99/4A

A very important part of any computer is the high speed memory that is accessed by the CPU. The memory is used to hold

both program steps (i.e., instructions) and data.

Computer mem

ories can be given a new program step or data element to store as easily as the old ones can be retrieved: the ease with which a

program can be changed and stored, when compared to the diffi

culty of changing something which is "hard-wired" or mechanical, is a major reason why computers have been a revolutionary new step in our post-industrial age.

A microcomputer's memory consists of bytes. This was dis cussed briefly in the chapter on nybbles, bytes, and so forth. You are also familiar with bytes indirectly at least from Basic each character in a string is a byte. The size of the memory is measured in K's.

The letter "K"

is used for the value 1024, which is 2 raised to the tenth power. This is a convenient value because it is nearly equal to 1000. The console RAM in the TI is said to be 16K, which is approx imately 16,000. The exact value is 16 times 1024, or 16,384 bytes. Note that this terminology actually mixes two number bases - base 10 and base 2 - but does so in a way that lets us easily understand approximately how much memory is involved because we are familiar with the decimal value 1000.

Memory is accessed by addresses. Each byte has a unique address, which is a number 0 or greater. Each computer is limited in the number of addresses it can handle. For instance, the TI-99/4A uses 16 bits for memory addressing. With 16 bits one can count from 0 to 64K minus 1, or 65,535. This means that the address space for the TI is 64K, because it can refer directly to that many bytes.

Because addresses are represented as binary values, the maximum memory size is nearly always an even multiple of two. Thus, various computers for home use may have 2K, 4K, 8K, 16K, 32K, or 64K.

A computer need not actually have all the memory in its

address space. Because of this, you can buy memory expansion "cards" or "modules" for some computers, including the 32K card for the TI.

Sometimes part of the address space is reserved for

special uses, and indeed it is on the TI, as we shall see later.

It is also possible to have memory which is not within the

computer's address space.

Most printers have their own memory,

enabling them to accept data much faster than they can actually print it. This is called a "buffer". While this memory does not belong to the computer itself, it shows how additional memory can be used by the computer system without its being part of the 15

The Memory of the TI-99/4A

CPU's memory space. We will see that the 16K in the TI is in fact very similar to the memory in the printer. Microcomputers have made extensive use of ROM - read only memory. This differs from traditional memory in that the CPU cannot store anything in it. The nice thing about ROM is that when you turn on the Home Computer, it can immediately come to life v/ith its built-in Basic interpreter; without ROM, you would have to

load Basic from an external memory,

such as cassette or

disk.

What is put in the ROM at the time of manufacturing can never be changed. The CPU does not know if some of its memory is ROM,

so in fact the essential character of the computer has not

been altered by the use of ROM. The TI-99/4A has 26K of ROM, quite a lot by current standards. Command modules (plugged in to the slot on the top right of the machine) also contain ROM; in the case of Extended Basic, there is an additional 36K of ROM.

TI.

One type of ROM, called GROM, was invented and patented by GROM can only be used in certain ways, v/hich will be de

scribed

below.

Memory into which new data can be stored needs a name to distinguish it from ROM. In the old days, computers only had one

kind of memory, called "core memory". term used,

You may still hear this

but due to different memory technologies now in use,

the usual name today is RAM, which simply means "random access memory". This is really a very poor term to use, since other forms of memory, including ROM and disk storage, are also random access.

All

that

random access means

is

that

the

data

can

be

retrieved from the memory in any order by specifying the address. The alternative form of retrieval would be sequential. A tape memory is a good example of sequential storage.

Remember that the address space of the TI is 64K. In fact, it has only 256 bytes of RAM for use by the CPU. These 256 bytes represent less than one half of one percent (.5%) of the potential memory space. The 256 bytes of RAM is called the "PAD" and is the only memory in the console which can be al tered by the CPU.

Of the memory in the console v/hich is in the address space, nearly all of it is ROM. Because the PAD is so small, you cannot write assembly language programs without using either the Mini Memory Module or the 32K memory expansion card - there simply is no place to put the program to run it.

Perhaps you are now wondering about the 16K that you know is in the console.

This is indeed RAM,

but unlike the 256 bytes,

it

really does not belong to the CPU. In this sense, it is like the memory in a printer. That 16K belongs to the VDP, the visual 16

The Memory of the TI-99/4A

display processor.

This memory holds the screen image and color

table for Basic. When Extended Basic is in use, it also holds the data describing any sprites you have. Because Basic and

Extended Basic use screen display modes which do not use very much of the 16K RAM, most of it is unused by the VDP to display the screen. Therefore, the bulk of it is actually used by the CPU as a storage device for the Basic or Extended Basic program itself.

Because the 16K is not in the CPU's address space, assembly language programs cannot be stored there and run. Furthermore, the CPU can only access the memory sequentially, although it can reposition within the memory quite rapidly. You can think of the 16K VDP memory as a very, very fast cassette drive for the CPU. Every time the CPU needs to access something in the VDP RAM, it must reposition, then read sequentially. Although this is much faster than a tape,

it is also much slower than if the CPU could

directly access this memory within its address space. This is also probably the major reason why Basic on the TI is slower than on some other home computers, especially when one would expect it to be faster because it has a 16 bit processor as opposed to an 8 bit processor.

By now you have probably realized that the CPU has access to several different types of memory. The console and the modules contain ROM which the CPU can directly access and from which it can execute instructions.

There is also GROM in both the con

sole and modules, which the CPU accesses sequentially; this means that the CPU cannot execute instructions in GROM.

execute instructions in the VDP RAM; sequentially.

Nor can it

it can only read and write

To summarize, there are several types of memory: ROM, GROM, The CPU can directly access any location in RAM, and VDP RAM. ROM or RAM, and this memory exists within the address space of the CPU. GROM and VDP RAM are only accessed sequentially and are not within the address space of the CPU.

Refer now to the table below. the CPU in simplified form. 1st

8K

2nd 8K 3rd 8K 4th 8K 5th 8K last 24K

Console

It shows the address space of

ROM

Reserved for expansion memory RAM Reserved for peripherals Reserved for modules (ROM or RAM) Special uses, including 256 byte PAD RAM Reserved for expansion memory RAM

Notice that 32K, exactly half the 64K address space, is reserved for expansion memory RAM. Of the remaining 32K, 8K is held for modules, 8K is held for peripherals, and 8K has special uses.

This leaves only 8K for ROM in the console. 17

One of the

The Memory of the TI-99/4A

advantages of GROM is the fact that it adds storage without oc cupying the precious address space. Also, notice that the 16K VDP RAM is not a part of the address space of the CPU.

Perhaps a look at a picture will help clarify what has been

said so far.

The figure on the next page schematically illus

trates the two processors and the several types of memories dis cussed so far. The command module shown is'the Mini Memory Mod ule; note that the typical module has no RAM, only ROM.

18

The Memory of the TI-99/4A

MEMORY STRUCTURE FOR THE TI-99/4A

CONSOLE RAM

256 bytes = .25K (in address space)

(1) CONSOLE ROM

8K

MINI MEMORY MODULE

ROM

(in address space)

4K RAM 4K ROM

CPU

(central 18K

GROM

(not in address space) Total

(both in address space)

process

ing unit)

(2)

(3) 6K GROM

(not in address space)

26K holds

Basic interpreter.

(4)

16K VDP RAM

(Screen image and

data

for

CPU, including Basic programs)

(5)

VDP

(visual display processor)

(1) The CPU can read (retrieve) or write (store) directly from or to the 256 byte RAM.

(2) The CPU can read the 8K ROM directly and can read the 18K GROM sequentially. (3) In the module, the CPU can read and write the 4K RAM, can read the 4K ROM, and can read sequentially the 6K GROM.

(4) The CPU can read or write sequentially the 16K VDP RAM. (5) The VDP accesses its 16K directly. 19

\^g^ The CPU and Registers In order to understand the instructions for the CPU,

helps to know a few things about the CPU itself. learned with a comparison to Basic.

it

That can be

In the TI, there are three special purpose registers contained within the CPU which the programmer needs to know about. TI calls them hardware registers. These are discussed below.

1) 2) 3)

The PC register. The status register. The workspace pointer.

A typical statement in Basic is "J=J+K".

This means to add

the values of J and K together and to then put the sum back into J. The programmer only knows J and K by their names. He need not be much concerned about their values, since Basic supports a large range (10 to the 64th power) and great precision (13 to 14 digits).

In assembly language, this would be "A @K,@J".

This also

means to add the values of J and K and then to put the sum into J. Unlike Basic, the programmer must be sure to reserve memory locations for both J and K (we will see how to do this later). He must also be concerned about the range of values both J and K may have in his program, since the CPU only performs integer arithmetic. Since the TI is a 16 bit machine, values may range

from -32,768 to +32,767.

Integers, by definition, have no

fractional part.

After a statement in Basic has been interpreted, the next statement in sequence is interpreted (unless the statement was a GOTO, IF, or FOR...NEXT loop). The interpreter has to keep track

of which statement it has just interpreted and be able to deter mine which is next. In the same way, when executing an assembly language program (that has been assembled into machine language), the CPU executes statements one at a time, in sequence, unless there is a special instruction to alter the order.

The CPU also

has to keep track of which statement it has just executed: it does this with the Program Counter register, or PC. The CPU uses the PC register to keep track of the current location of the program. The PC register is simply a 16 bit counter,

able to count from 0 to 65,535.

CPU's address space.

Thus,

This is the size of

the

the value in the PC is the address of

the next instruction to be executed.

(Note:

it is not the

value of the instruction last executed.) Many instructions are two bytes long (some are four or six); the PC is therefore auto

matically incremented by two (or four or six) after executing these instructions. In this way it always points to the next 20

The CPU and Registers

instruction.

If the instruction is a jump (the equivalent to a Basic

GOTO), the instruction does not actually make anything "jump" instead, it simply puts a new value into the PC register. Whether the PC is automatically incremented or is loaded

with a new value, when the CPU is ready for another instruction, it uses the value in the PC register to get two bytes from ROM or RAM, starting at the location pointed to by the PC.

Those two bytes form the next instruction. Once loaded into the CPU, into a special instruction decoding register, the CPU proceeds to exe cute

that

next

instruction.

The PC is one of several registers in the CPU.

Some regis

ters are 16 bit counters. Other registers hold values which are not necessarily addresses, an example being the instruction de coding register mentioned above.

The status register (ST) is a means by which the program (software) can communicate with the CPU (hardware) about miscell aneous conditions that may arise.

Perhaps you have used a vari

able in a Basic program which was set as the result of a logical test. For example, if you have a variable in your program called VALUE,

you might test it like this: TOOMUCH =

VALUE >

10000.

Having done this, your program can easily test for the condition TOOMUCH any number of times.

The variable TOOMUCH has the value

FALSE or the value TRUE. This type of variable is a flag. You can think of the status register as a set of such flags, each bit in the register being a flag. The exact meaning of the various bits in the status register will be discussed later.

The two registers just discussed are necessary but hardly sufficient for a computer. Other registers are needed for gen eral purpose tasks, such as arithmetic operations and compar isons. The TI has a set of 16 general purpose registers. These registers are different than the PC and ST registers because they are actually memory locations.

The workspace pointer register (WP) is a special purpose register which points into memory to the 9900fs general purpose registers. TI calls these software registers. The WP must point to a 32 byte region of RAM which the programmer has desig nated to be the 16 general purpose registers. It is the respons ibility of the programmer to make sure the WP contains the N|j^/

address of these 32 bytes. (However, if you call an assembly language routine from Basic, the WP has been already set up to point to a 32 byte space and you need not worry about it.)

21

The CPU and

Registers

These 32 bytes are taken two at a time to be 16 registers, numbered 0 to 15. Each has 16 bits. These registers are used to hold any 16 bit pattern the programmer chooses, including add resses (pointers), integer values (just as the variables J and K), characters, or instructions. In many cases they are used to perform arithmetic operations, instead of using variables. For instance, this is another addition instruction: "A R6,R9". It says to add the values of registers 6 and 9, then to put the sum into register 9. This figure will help show what the PC and WP registers do. CPU MEMORY (RAM)

7200 7202 WP

REGISTER

7204

R0

7206

Rl

7208

R2

720A

R3

720C

R4 etc .

CPU MEMORY (ROM OR RAM)

7D10 7D12

7D14 PC

REGISTER

7D16 next

7D18

7D1A 7D1C

22

to

be

instruction

executed

The CPU and

Registers

%&? The WP and PC registers are represented by boxes on the left of this diagram, while memory is represented on the right. Since the registers point to even addresses, memory is shown as two columns - each column is a byte, and the two bytes together make a word.

The addresses shown next to the memory are for the left

column.

All

numbers are

hexadecimal.

The WP register indicates that the address of the software registers starts at 7204. That is, register 0 is locations 7204 and 7205, register 1 is locations 7206 and 7207, and so forth. The PC shows that the next locations 7D18 and 7D19.

instruction

to

be

executed

is at

Note that the PC can point into ROM, but since the WP points to registers which are used for computations, it would not be useful for the WP to point into ROM.

sole.

Very often the WP points into the PAD RAM area in the con If there is no additional memory on the computer (Mini

Memory Module or Memory Expansion), the WP has to point into the PAD (locations >8300 through >83FF). Because the PAD is faster than other memory, it often makes the most sense to use the PAD for the register space.

23

The First Program

In this chapter you will enter and run the simplest possible program. The purpose will be to learn how to go through the mechanics of getting a program assembled and loaded as well as to

begin to learn some detail of the computer's instruction set. The simple program does nothing except return to the Basic calling program. It is therefore similar in concept to the Basic statement "RETURN".

Frequently what we program in assembly lan

guage is a subroutine, either called from Basic or from another assembly language program.

Therefore, the return statement is

used quite often.

The essence of a return statement in any language is simply to return control to the point from which it was transferred to the subroutine. In Basic, the interpreter performs this function

by storing the return on a stack when the subroutine is called with

GOSUB.

Since this instruction is used so often, the TI Editor/Ass embler allows you to abbreviate it as simply "RT", as though it were a special instruction. However, you can still use "B *R11" and have exactly the same result. There are two terms that are used for assembly language but

are not needed for Basic programming. First, source code re fers to a program as you write it, whether with the TI or Dow Editor/Assembler. The source program has labels and names. Se cond, object code refers to the program after assembly. It no longer has labels and names. With the TI Editor/Assembler, you key in the source using the editor: you must then store it in a disk file. This file in turn is read by the assembler, which generates the object file, also on disk. The object file is loaded into memory by CALL LOAD

from

Basic

or

Extended

Basic.

24

The First Program

Here is a diagram to show this sequence Source entered with editor and stored in file on disk

Assembler

reads

source

file

and creates object file on disk.

Object file is loaded into memory and executed.

With the Dow Editor/Assembler, you key in the source using the EDIT command.

Using the SAVE command,

you can store it on

tape if you wish. The LOAD command generates the object code and puts it directly into the memory. This is how it is done. Source

entered

with

EDIT.

(May be stored on cassette with SAVE.)

LOAD command stores object

directly into module's RAM

Program is executed.

Let us now follow the steps for entering,

loading, and run

ning this program with the TI Editor/Assembler. (The instruc tions will then be repeated for the DOW Editor/Assembler.) Stepl) Insert the Editor/Assembler Command Module and the diskette labelled "Editor/Assembler Part A." (Actually, you should make a copy of TI's diskette and use the copy. If you delete the DEBUG files from your copy, there will be room on the same diskette to store some of your own programs and you will not

have to switch diskettes repeatedly.)

Step 2) Press any key, then press 2 to select the Editor/Ass embler .

Step 3) Press 1 to select the editor.

Step 4) Press 2 to edit.

(Wait while the editor loads from

disk.) %fcs/

25

The First Program

Step 5) Enter the program,

It should be displayed on the

screen

like this. DEF TEMP TEMP RT END

Th ese

th e

instructions give the actual key strokes needed

P rogram.

Note that

First

line

..

FCTN 8 (to

TAB

means

FCTN

to

enter

7.



insert

a

line)

to

leave

TAB

type DEF TAB

type TEMP ENTER Second line

.

.

.

Type TEMP TAB

type RT ENTER Third

line

..

.

TAB

type END ENTER

Press

FCTN

9

twicei

the

editor.

Step 6) Press 3 to save the program. Reply Y to the prompt about variable 80 format. For a filename, you could specify

"DSK1.TEMP".

Switch diskettes if there is not enough space on

the diskette which has the editor program. Press ENTER and wait while it writes the program to disk. Then press FCTN 9.

Step 7) Press 2 to assemble.

If necessary, change back to the

"Editor/Assembler Part A" diskette. about loading the assembler.

Then reply Y to the prompt

Wait while the assembler is loaded

from disk. If necessary, replace the diskette containing your source file. When asked for the name of the source file, reply with the same file name given in step 6 above (DSK1.TEMP). For If the name of the object file, you could enter "DSK1.TEMPOBJ". you have a printer, you would give its name in response to the prompt for the list file name, otherwise just press ENTER.

(Examples: "PIO", "RS232", "TP", "RS232.BA=9600".) For options, enter RLS if you have a printer and want a listing to be printed, otherwise just enter R. Now wait while it is assembled. You should get a message that there were no errors. Press ENTER.

Step 8) Press FCTN 9 to return to the master title screen.

Step 9) Press any key, then 1 for Basic. Enter and run this program with the Editor/Assembler module still inserted. 100

CALL

INIT 26

The First Program

110 CALL L0AD("DSK1.TEMP0BJ") 120 CALL LINK("TEMP") The call to INIT prepares the computer to accept your program. For example, it copies some utility programs from ROM in the cartridge into the Memory Expansion RAM. The call to LOAD ac

tually reads the object file from disk and loads it into Memory Expansion. The call to LINK transfers control from the Basic program to the assembly language program. The file name specified in statement 110 must be the same as

the name given for the object file during the assembly phase in step 7.

The name passed to LINK in statement 120 must be the

same as the name appearing on the DEF statement in the program in step 5. When you run the program, there will be a pause as the object file is read from disk and loaded into the expansion RAM by CALL LOAD, but the actual execution will only require micro seconds .

Step 10) To prove that your program is actually being loaded, change the file name in statement 110 and you will get the mess

age 1/0 ERROR 02 IN 110.

To prove that is actually being called,

change TEMP to XXXX in 120 and you will get the message PROGRAM NOT FOUND

IN

120.

If you were to list this program during the assembly in step 7, it would look like this:

99/4 ASSEMBLER VERSION 1.2 0001 0002 0000 045B 0003

TEMP

DEF RT END

PAGE

0001

PAGE Rll R15 R5 R9

0002 000B 000F 0005 0009

TEMP

99/4 ASSEMBLER VERSION 1 .2 0000 R0 R12 oooc 0002 R2 0006 R6 0000 D TEMP 0000 ERRORS

Rl R13 R3 R7

0001

R10

000A

000D 0003 0007

R14 R4

000E 0004 0008

R8

On the first page, the numbers 0001, 0002, and 0003 in the The 0000 on

column on the left are merely statement numbers.

%to^

line 0002 is the relative address at which the statement will be loaded. Notice that the DEF and END statements have no add

ress: that is because they generate no code to be loaded. The 045B is the code generated by the RT statement. It means B *R11

(branch indirect of register 11).

The term "relative address" 27

The First Program

means

that

the

address

shown

is

not

the actual

address

at

which

the instruction or data will be loaded, but represents a relative amount to be added to the actual address when the program is pre pared for loading into memory. On the second page all the symbols and their values are listed. RO through R15 were automatically defined by the R in the option specification in step 7. The only label explicitly defined was TEMP. The D by it means that it also occurs in a DEF statement.

Here is what the screen should look like after entering the

same program using the DOW Editor/Assembler.

(The value 7FE8 in

the fourth line may not appear when you do this. The value dis played will be simply whatever happens to be at locations 701E and 701F when the program is entered.) DOW EDITOR/ASSEMBLER (C) JOHN DOW 1982 >MINI 701E >701E >7FE8 ?>7FF8 >701E >7FF8 ?. >EDIT E->E

LOC LBL:OPCD OPERAND(S) 000 TEXT /TEMP / 006

DATA

>7118

DATA

>7118

008

006 E->.

>L0AD 7FF8 ADDR = >7FF8 0K?Y •







NEXT = >8000 >NEW >EDIT E->E

LOC LBL:0PCD OPERAND(S) 000 002 000 E->.

B

*R11

B

*R11

>L0AD 7118 ADDR = >7118. 0K?Y •

NEXT = >711A >LINK TEMP

This is a step by step explanation of what you do to enter, assemble, load, and run the program.

28

The First Program

Step 1) Put the Mini Memory Module into the slot on the com

puter. (Remember the admonition to turn off the computer while doing this so that you do not inadvertently destroy any data or program you may have already stored in the battery powered RAM in the module.)

Step 2) Press any key,

then press 1 for Basic.

Step 3) If you want to clear out anything stored so far in the Mini Memory Module, type "CALL INIT" and press ENTER. Step 4) Load the Dow Editor/assembler and run it.

Step 5) Next you have to make an entry in the REF/DEF table in the Mini Memory Module.

This is a table of names and their

associated addresses. It forms the link between the calling pro gram, which uses the name, and the assembly language routine, which is loaded at an address determined by you. Since you also determine the name,

you must enter both name and address into the

table. Look at the diagram at the end of the chapter to see how LFAM (last free address in the module) points to the table, and

the table points to the program's entry point. in the table, N^,"

To make the entry

follow the instructions in Section 7 of the Dow

Editor/Assembler manual, except change 7500 to 7118.

This is

what you would enter:

Type Type Type Type Type

MINI 701E, press ENTER. >7FF8, press ENTER. a period, press ENTER. EDIT, press ENTER. E, press ENTER.

Space 4 times, then type TEXT, space, type /TEMP / (with two spaces after TEMP), press ENTER. Space 4 times, then type DATA, space, >7118, press ENTER.

Press ENTER again. Type a period, press ENTER. Type LOAD 7FF8, press ENTER. Finally type Y, press ENTER. You will see four dots displayed as the four words are loaded

into the Mini Memory Module's RAM. It will then say that the next location (that is, the one just after the last location loaded) is >8000.

^fej^,-'

Step 6) Key in the program.

First,

the

entered

TEXT

and

DATA

statements

Type NEW, press ENTER. Type EDIT, press ENTER. Type E, press ENTER.

29

you will have to clear out above.

The First Program \^0r

You are now ready to key in your program as shown below. It only has one statement. The spaces are necessary for the instruction

(the "B") and the operand (the "*R11") to be in the correct col umns. After you press return, the editor checks the statement for correct syntax. It then prompts for another statement.

Space 4 times, type B, space 4 times, type *R11, then press ENTER.

Press ENTER (to leave the editor's enter mode). Type a period (.), press ENTER (to leave the editor). Step 7) Load the program into the Mini Memory Module. Type LOAD 7118 and press return. Notice that the value you enter here absolutely must be the same as the value you loaded into the table a few minutes ago. Furthermore, it must be the entry point of the program. (The entry point is the first instruction to be executed. Sometimes you might want other instructions or data to come first in a program, but in our

simple example, the entry point is the one and only instruction.) The loader will ask you to confirm the value you entered before it proceeds with the loading: type Y and press ENTER Type LINK TEMP and Step 8) You can now ca 11 the program, press ENTER. Since th e program doesn ft do anything, the Dow Editor/Ass- embler wil 1 just ask you for another command right away. However, if the re was a proble m with one of the steps For instance, if the above, you may get an error message, REF/DEF table was not loaded properly or the name entered with the LINK command (TEMP in this case) is not in the table, you In that will get a message sue h as "PROGRAM N OT FOUND IN 2810". case, you have to run the Dow Editor/ Assembler again, which will Another wipe out your assembly language sourc e program. control of the computer and possibility is that yo u may even lose have to turn if off an d back on again to regain control!

Step 9) You can prove that you can call the program from any

Basic program.

Tell the Dow Editor/Assembler STOP, then enter

the Basic command NEW to prepare to enter your own Basic program This is all you need:

100 CALL LINK("TEMP") When you RUN this program, it will terminate immediately.

To prove that it actually called the program, change TEMP to XXXX and call LINK again: you will get the message "PROGRAM NOT FOUND IN

100".

You can type MINI 7118 to see the instruction that was ass

embled into the Mini Memory Module's RAM. It will display the value >045B, which is the machine language version of B *R11.

30

The First Program Nissan

If you have a printer and tell the Dow Editor/Assembler to list this program, it will display the prompt "OLD:" and "OK?".

This shows you that there is no "old" label: you may enter a la bel by pressing ENTER and then entering the label in response to

the prompt "NEW:".

For instance, a label might be as simple as

TEMP PROGRAM TO TEST SUBROUTINE LINKAGE. You may of course enter a version number or date, as well as a tape name or number. With the above label, this program would list just like this: TEMP PROGRAM TO TEST SUBROUTINE LINKAGE 000

B

*R11

Here is a diagram for the Mini Memory Module. described on the next page.)

(It is

DIAGRAM SHOWING LFAM TABLE, AND PROGRAM ENTRY POINT IN THE MINI MEMORY MODULE'S RAM 701C

(LFAM

701E

7FF8

points

to 7FF8)

7020

\l^^

7116

(The program 7118 begins here. )

B*R11

711A

7FF6

(The name of

7FF8

program

is and

_"TE"

the

"TEMP

7FFA

"

the

7FFC

entry point

is 7118.)

7FFE

7118

8000

31


A800, RO Rl,1000

RO LP1

RT

LOAD RO FROM LOCATION SET Rl=1000 R1=R1-1 LOOP IF R1>0 R0=R0-1 LOOP IF R0>0 RETURN

A

END

This Basic program will load and run the TI version.

written to pass any value of N to the subroutine.

It is

However, it

does not check the value, so make sure you only enter a positive integer value between 1 and 32,767. 100

CALL

INIT

110 CALL L0AD("DSK1.TEMP0BJ") 120

INPUT N

130 BYTEl=INT(N/256) 140 BYTE2=N-BYTE1

*

256

150 CALL LOAD(-22528,BYTEl,BYTE2) 160 CALL LINK("TEMP")

Note that LOAD in statement 110 loads the object program from disk into memory, while the LOAD in statement 150 stores specific values (BYTE1 and BYTE2) into memory at a specified location. Start timing after entering the value of N. Statements 130 and 140 break the value apart into two bytes, and statement 150 stores the two bytes into locations A800 and A801.

35

^j

L°°PinS Here is the Dow Editor/Assembler version of the program. is loaded at 7118,

the first free location in the module,

It

and it

gets the value of N from location 7200. MOV LP1-.LI LP2:DEC JGT DEC JGT B

To a Basic similar However,

@>7200;R0 Rl;1000

LOAD RO FROM LOCATION SET RU1000 RUR1-1 LOOP IF R1>0 R0=R0-1 LOOP IF R0>0 RETURN

Rl LP 2

RO LP1 *R11

A800

pass the value 1000 to "N" to the program, you could use program to prompt for N and store it into memory, very to the Basic program shown above for the TI version. it is easier to follow the steps below. The advantage

is that you can leave the Dow Editor/Assembler running while you test your assembly language program.

tering the LINK command.

Start the timing after en

(The "hhhh" below merely represents the

value in location 7200, whatever it may be.) >MINI 7200 >7200 >hhhh 71000 >7200 >03E8 ?. >LINK TEMP

At this point,

you should have been able to enter, assemble,

and run this program using one of the editor/assemblers.

Let us

now examine the program one statement at a time.

The purpose of explaining these statements here is only to give you a better feel for assembly language. (Each statement will be described again later in the book in the chapter which discusses the appropriate instruction types.) Do not be discour aged if you do not understand everything at this time.

sion.

DEF - This appears only in the TI Editor/Assembler ver Its purpose is to define an entry in the REF/DEF table

so the CALL LINK in the Basic program will be able to transfer con-

trol

to

it.

This

statement was

described

earlier

in

this

book and will be discussed again in the chapter on directives. MOV - The value of N was stored in memory location A800 or

7200, depending on which editor/assembler you used. This in struction moves it from that location into register 0. Thus, if you entered the value 500 for N with the MINI command or with the Basic program, it would now be placed into register 0. LI - This is a special form of move. Load immediate moves a value from right there in the program (actually a part of the instruction itself) into a register. Whereas MOV is roughly equivalent to the Basic statement LET X=Y, LI is similar to LET 36

N^^r'

Looping

X=9.

1000.

In this program, the LI statement sets register 1 equal to

Register 1 is the counter *for the inner loop.

DEC - Now begins the inner loop. This instruction means to decrement register 1. Since it was just set to 1000, it is now 999. Each time through the loop, register 1 will be decremented by 1. It thus fulfills its role as the loop counter Unlike the typical loop counter in Basic, it counts down toward 0, not up from 1. JGT - This statement completes the loop. It means to jump if greater than to the statement with the label LP2.

At this point, a review of the parts of a loop in Basic is needed for comparison to the assembly language version. A loop has a variable which is the counter. That variable is: 1) given an initial value; 2) incremented (or decremented, as with STEP

-1); 3) possibly used within the loop for other purposes (such as an index into an array); and 4) tested to determine when to leave the loop. In the assembly language loop,

these same parts exist.

How

ever, you have to explicitly put them there. Usually, a register serves as the loop counter instead of a memory location (the

counter part to a variable in Basic). In the inner loop, the counter is register 1. It is: 1) initialized with LI to be 1000; 2) decremented by DEC; 3) not used within the loop, although it could be; 4) tested by the JGT to determine when to leave the loop. To complete the e xplanation, it is importan t to cover how The answer i s that on the TI, JGT can tell when to s top looping, as well as on many com puters, it i s standard to provide automatic Such tests are possible for or simple tests agains t the value 0. registers, memory loca tions, and t he result of v arious operations (such as adding or sub tracting, in crementing or decrementing, or even just moving). Fo r example, t he DEC instruc tion on the TI not only decrements th e register, it also tests the new value against 0. The result of the comp arison is left in certain bits in the status register (one of the TI's hardware registers described earlier). The se bits are tested by inst ructions such as JGT. Thus, the JGT re ally means " if the most re cently performed operation which did a comparison r esulted in a v alue greater than

0, then jump". This automatic comparison to 0 is why loops in assembly lan

guage count down to 0. For example, instead of counting down by l's with DEC, it would certainly be possible to count up by l's with INC (which is the increment-by-1 instruction). However, in that case you would have to insert an extra statement in the pro37

Looping Nsj^f

gram to compare the count to the final value. This comparison instruction would be placed before the jump, at the end of the loop. Including this instruction would make the program a little larger and a little slower, so the natural tendency of assembly language programmers is to take advantage of the automatic capa bilities of the computer and decrement to 0 when possible. Because the decrement comes before the test,

the value in

register 1 during the last iteration through the loop will be 1. After leaving the loop, the register will contain 0. If you want the last value to be used in the loop to be 0, use the JOC in struction instead of the JGT instruction. (Both JGT and JOC are described again in the chapter on jumps.)

38

Arithmetic Operations

Computers, as the English language name implies, can com For many applications, the computations are actually quite trivial mathematically. For instance, computers frequently count things, or look in lists, or find the middle or average. These operations only require simple arithmetic operations, frequently with integer values.

pute.

Programs written in Basic on the TI are capable of handling large numbers with great precision. The language also provides advanced arithmetic functions, such as the trig function SIN.

However, most programs don't use any of these features.

In

stead, the integer arithmetic capabilities of the computer it self, the 9900 processor, are very adequate. These operations include addition, subtraction, multiplication, and division.

All of these operations are "binary", as opposed to "unary". That means that they operate on two values. On the TI, the oper ands (the values operated on) can be registers or memory loca

tions. Here are examples of the binary (that is, two operand) arithmetic operations, addition and subtraction. Examples of unary operations will come later in this chapter. A

R1,R12

A S

@C0UNT,R5 R3,@ABC

add the contents of register 1 to the contents of register 12 add COUNT to register 5 subtract register 3 from ABC

For all these intructions, the first operand is called the source and the second

is the

destination.

In performing an addition, the sum of the two values re places the second value. The destination becomes the sum of the source and destination. Thus, read "A @X,@Y" as "add X to Y"; this is similar to

"Y=Y+X" in Basic.

For subtraction,

the difference between the two values

replaces the second value. The destination becomes the differ ence between the destination and source. Read "S @X,@Y" as "subtract X from Y"; in Basic, "Y=Y-X". Here are examples of multiplication and division. MPY R5,R0 MPY @SCALE,R9

DIV R1,R2

multiply register 5 times register 0 (product in registers 0 and 1) multiply SCALE times register 9 (product in registers 9 and 10) divide register 1 into registers 2 and 3 (quotient in register 2, remainder in 3)

39

Arithmetic Operations

Since multiplication and division are somewhat different in Basic and assembly language, actual Basic statements cannot be used to show how they work. Therefore, in the discussion below you will see statements that look something like Basic but are not. However, the differences are simple and do not have to be explained formally. The examples are provided merely as some thing roughly familiar in order to facilitate understanding. The format of the multiplication instruction differs from the

addition

and

subtraction

instructions

in

that

the

destination

of a multiplication must be a register, whereas for the other two that is just one of the options. The operation itself dif fers in that the result of a multiplication may actually be a two-word value. That is, the result occupies two registers, not just one.

This size doubling during multiplication is true of multi plication in any number system. For instance, multiply two 3-digit numbers and the product may have as many as six digits. As an example, 999 times 999 is 998,001.

The instruction "MPY @X,R1" multiplies X times register 1, putting the result into registers 1 and 2 combined. The two

16-bit values result in a 32-bit value. Read it as "multiply X times register 1, putting the result into registers 1 and 2." In pretend Basic, this might be "Rlc2=X*Rl". (I am using "Rlc2" to mean registers 1 and 2 combined.) If the multiplier and multiplicand will always be small enough, you will know that the result of a multiplication oper ation cannot in fact be a two word value.

In those cases,

the

first register will always be 0 and you can write the program to just use the result left in the second register. Here are the listings for two programs which will let you experiment with the multiplication instruction. This should help you understand how it works. The first program is in Basic and the second is an assembly language subroutine. The Basic program prompts for two values, then calls the assembly language routine to multiply them together. Store the Basic program in the file

"DSK1.TESTB" and the assembly language program in the file "DSK1. TESTA". The object program should be stored in "DSK1.TESTAOBJ". (Instructions will follow for using the the Dow Editor/Assem bler.) Here is the Basic program.

100 REM TEST ARITHMETIC INSTRUCTIONS (DSK1.TESTB) 101

CALL

INIT

102 CALL L0AD("DSK1.BSCSUP","DSK1.TESTAOBJ") 110 120

L0C=-22528 FOR 1=0 TO

2

STEP

2

40

Arithmetic Operations

130

INPUT

N

140 IF N>-1 THEN 150 N=N + 65536

160

160 BYTEl=INT(N/256) 170 BYTE2=N-256

*

BYTEl

180 CALL L0AD(L0C+I,BYTEl,BYTE2) 190 NEXT

I

200 CALL LINK("An) 210 FOR

1=0 TO 6

STEP

2

220 CALL PEEK(L0C+I,BYTEl,BYTE2) 230

N=BYTE1

*

256

+

BYTE2

240 IF BYTEK128 THEN

260

250 N=N-65536

260 PRINT "("; 270 B=BYTE1 280 GOSUB 360 290 B=BYTE2 300 GOSUB 360

310 PRINT ") ";N 320

NEXT

I

330 CALL PEEK(L0C+4,BYTEl,BYTE2,BYTE3,BYTE4) 340 PRINT ((BYTEl * 256 + BYTE2) * 256 + BYTE3) * 256 + BYTE4 350 GOTO 120 360 REM BINARY TO HEX CONVERSION

370 HD1=INT(B/16) 380

HD2=B-16

*

HD1

390 S$="0123456789ABCDEF" 400 PRINT SEG$(S$,HD1 + 1,1);SEG$(S$,HD2 + 1,1); 410 RETURN

This is the assembly language routine.

TITL 'TEST MULTIPLY INSTRUCTION1 (DSK1.TESTA) A

DEF

A

MOV MPY MOV MOV

@>A800,R0 @>A802,R0 R0,@>A804 R1,@>A806

B END

*R11

LOAD RO FROM LOCATION A800 MULTIPLY VALUE IN A802 TIMES RO PUT LEFT HALF OF PRODUCT IN A804 PUT RIGHT HALF IN A806

If you try these programs and enter 3 and then 7 in response to the prompts, you will see the following output.

sij^/

(0003) 3 (0007) 7 (0000) 0 (0015) 21 21

The Basic program stores your two values in the words starting at 41

Arithmetic Operations

locations >A800 and >A802.

The assembly language program multi

plies those two values and puts the result into the two words starting at locations >A804 and >A806. These four words are displayed by the Basic program as 3, 7, 0, and 21. (The values

in parentheses are the same values in hexadecimal notation.) The

value 21, shown on the last line, is the result of accumulating all four bytes of the product into one value by successively multiplying by 256 and adding. As discussed above, the product occupies two words. For a small positive product such as 21, the first word is 0. Now try a negative value to see what happens. (0003) 3 (FFF9) -7 (0002) 2 (FFEB) -21 196587

As a result of taking the product of 3 and -7, the first word of the product is always 1 less than the positive value, or 3 - 1 = 2 in this case. The second word contains the expected -21. The two words together yield 196,587, which is useless. Therefore, just as you can multiply small positive values and ignore the first word of the product, so also you can multiply small nega tive values and ignore the first word of the product. However, if large values must be multiplied, be sure both are positive before doing so.

Try one more example. This is what happens when the two values to be multiplied are large enough to create an actual twoword product. The two values are 10,000 and 10,000 and the pro duct is

100,000,000.

In this case,

neither the first word of the

product nor the second is meaningful by itself. (2710) 10000 (2710) 10000 (05F5) 1525 (E100) -7936 100000000

Division also requires the second operation to be a regis ter. Just as with multiplication, it is actually two registers combined. A two word value is divided by a single word value to get a single word result. That is, a 32-bit value is divided by a 16-bit value to get a 16-bit result. There is also a remainder, which is also a single register (16-bits). Although multiplying two 3-digit numbers cannot yield a product with more than six digits, dividing a 6-digit dividend by

a 3-digit divisor may yield a result of more than three digits. This is an overflow condition that is detected by the computer. 42

Siigagr

Arithmetic Operations

For example, divide 300 into 354,137 and the result is more than

three digits.

In many cases, this poses no problem for your pro

gramming because you should know in advance if such a situation will arise with your data. For instance, if you divide 537 into 354,137 you get 659 with remainder 254. Division in Basic may result in a number with a fraction.

For instance, in Basic, 354,137 divided by 537 is 659.47299. The division instruction in assembly language cannot yield a frac tional number because all numbers are integers. Therefore, if there is anything left over it must be expressed as a remainder instead of a fraction. This is how you can compute a remainder manually.

354,137 divided by 537 take the integer result multiply 537 * 659 subtract 354,137 - 353,883

659.47299 659 353,883 254

Even though you want to divide a single word value such as

10,000 by another single word (but smaller value), you still need a 2-register dividend. To do this, you must put the dividend N^,

into the second of two registers and make sure the first is 0. You can clear a register with the CLR instruction; thus, to zero

register 1, use "CLR Rl".

Read "DIV @X,R9" as "divide X into registers 9 and 10, with the result in register 9 and the remainder in register 10." In pretend Basic, you might write "R9=INT(R9cl0/X) and R10=remainder(R9clO/X)". Try this assembly language program to become familiar with the

division

instruction.

TITL 'TEST DIVIDE INSTRUCTION' (DSK1.TESTA) DEF MOV CLR DIV MOV MOV

B

A

@>A802,R1 R0

@>A800,R0 R0,@>A804 R1,@>A806

LOAD Rl FROM LOCATION A802 SET R0 TO 0

DIVIDE CONTENTS OF A800 INTO R0,R1 STORE QUOTIENT IN A804 STORE REMAINDER

IN A806

*R11

END

If you divide 5 into 100, this will be displayed

N^s/

(0005) (0064) (0014) (0000)

5 100

20 0

1310720

43

Arithmetic Operations

This shows that 5 into 100 gives 20, remainder 0. (The value on the last line is a meaningless combination of the quotient and remainder and should be ignored.) Now divide 5 into 103 to see what happens when there is a remainder.

(0005) (0067) (0014) (0003)

5 103 20 3

1310723

Try some negative values with the DIV instruction. You will undoubtedly conclude that it is better to try to stay with posi tive values. This should not be a problem too often. If it is, you will have to test for negative values, then use ABS to make it positive, and use NEG to make it negative later. Remember that both the multiply and divide instructions operate on unsigned numbers. These are numbers that are treat ed as positive values, even though the left-most bit, the sign bit, may be a 1. Try the test programs above to understand what happens when negative values are used.

Let us summarize the binary operations. to

remember

both

the addition and

subtraction

It should be easy instructions.

Mul

tiplication is similar to addition and subtraction, except that the second operand must be a register and the result is two words. Division is the most complicated. Like multiplication, the second operand is a register combination, but it starts out as two registers together and ends up as two separate registers. The most difficult thing to remember is which is which; perhaps it will help to remember that they are in alphabetical order the first is the Quotient, and the second is the Remainder. An other scheme for remembering is to think that the quotient is used more often than the remainder, so it is put into the first register. There

are

several

other

arithmetic

instructions.

In

addi

tion to addition and subtraction on words, there are also byte addition and byte subtraction instructions, AB and SB re spectively. These are exactly the same as A and S (for words), except that they add bytes. (Remember that if a register is an operand, only the left half is used.)

44

Arithmetic Operations

There is a special addition instruction, called add immed iate.

The term "immediate" means that the value to

be added is

right there in the instruction. Unfortunately, the order of the operands is reversed from the normal addition instruction so that the sum replaces the first operand, not the second. The instruc tion

looks

like

AI AI

this.

R5,980 R0,-100

add 980 to register 5 add -100 to register 0

There is no "subtract immediate" instruction.

However, you

can add the complement of the value you want to subtract.

example, to subtract 100 you should add -100.

For

See the example

above.

There is no immediate addition or subtraction for bytes, and no immediate multiply or divide.

^p/

There are four instructions which do a special form of arithmetic. (All are instances of unary operations, while all operations discussed so far were binary.) Two of these in structions add or subtract the value "1", and two add or subtract the value "2". These are called increment and decrement, and are very useful when writing loops, as discussed in the last chapter. Here are examples of these special addition and sub traction

instructions.

DEC DECT INC INCT

R3 Subtract R9 Subtract @SW . Add 1 to R0 Add 2 to

1 from register 3. 2 from register 9. SW. register 0.

The reason there are instructions which add

or

subtract

two

is that frequently a program operates on a list of word values rather than on byte values, and since the memory is addressed by bytes, each word value is two bytes from the last. Although these instructions are often used to increment or decrement loop counters, they can be used whenever you simply want

to add

or

subtract one or two.

Another

use

can

be

for

setting register or memory locations which you are using as switches. For instance, you can use CLR R5 to set register 5 to 0, and then you can use INC R5 to set it to 1. Finally, there are two remaining arithmetic instructions, absolute value and negation, both of which only can be used on words (not bytes).

The first, ABS, sets the register or location to a positive value but leaves it alone if already positive. instance, if location DIFF contained the value -193, ABS

@DIFF would set the value to +193. 45

then

For

Arithmetic Operations

The second, MEG, sets the value in a location or register to its complement. Thus, if the value is negative, it is set positive, and if positive, it is set negative. For example, if register 3 contained +5, NEG R3 would set it to -5, whereas if it contained -5 to begin with, NEG R3 would set it to +5.

The test programs shown above are for the TI Editor/Assem bler. The value -22528 given to LOC in the Basic program state ment 110 is equal to hex A800. Below is what the assembly lan

guage program looks like when listed from the Dow Editor/Assem bler and the Mini Memory Module. Notice that >7200 is used instead of >A800, that >7202 is used instead of >A802, and so forth. Load the program at >7118. It is also necessary to put

the label A and the address >7118 into the REF/DEF table. (Go back to the chapter "The First Program" to see how to do this.) In the Basic program, set LOC in statement 110 to 29184 (which is equal to >7200) and delete statements 101 and 102 from the Basic program.

TEST MULTIPLY INSTRUCTION MOV @>7202;R0 000 004 @>7200;R0 MPY R0;@>7204 MOV 008 MOV Rl;@>7206 00C 010 B *R11

You may wish to modify these programs to try other instruc tions, such as addition and subtraction on words or bytes. This

is a useful exercise to get a good feel for 2's complement nota tion .

46

\lt^-^-

Addressing Modes

Nearly every instruction refers to some data in a register or in memory. The act of referring to the data is called ad dressing. The way in which it does it is called the address ing mode. A diagram at the end of this chapter shows most of the modes in simple schematic form.

So far in this manual,

you have seen data accessed both in

registers and in memory locations. These are just two of a number of ways of addressing data. Although I have described instructions as only allowing reference to registers or memory, there are several other modes also available for most of those

instructions.

The examples below will show how this is done.

The first addressing mode to be discussed in this chapter is register addressing,

N|ia^

which means that the value to be used is

in the specified register. A register is simply a word in memory that can be addressed easily by a number in the range 0 to 15. (Remember that TI calls these software registers.) On some computers, registers are not memory locations; instead, they are hardware registers. Because they are implemented in hard ware rather than in memory, they can be much faster than memory. Of course memory could be made as fast as a hardware register, but this would make the computer very expensive. However,

even though a register on the TI is not as fast as

on machines with hardware registers, the use of registers can represent a speed gain over the use of memory addressing (to be

discussed below).

This is because the register is specified by 4

bits within the instruction itself, whereas a memory reference requires that the instruction include an additional word to contain the 16 bit address, which in turn points to the value in memory. Because the processor has to load both the instruction and the address word from memory, the instruction will take longer to execute. The extra word also makes the program larger. Note that the 256 bytes of RAM within the console is faster than the memory in the Mini Memory Module or the 32K memory ex pansion. Typically the registers are located in this 256 byte memory whereas variables you define in your programs are much more apt to be in the slower memory. This means that even the software registers on the TI are faster than the usual memory addressing.

47

Addressing Modes \^0r

Here are some examples of register addressing. Take absolute value of register 0. Add register 1 to register 9.

RO

ABS A MOV

R1,R9 R9,R3

AB

R6,R1

Move register 9 to register 3. Add the left byte of register 6 to the left byte of register 1.

Direct addressing, or symbolic memory addressing, or

just memory addressing, means that the address is part of the instruction (as an additional word). The address points to the memory location whose contents are to be used by the instruction. This address is an absolute address.

ABS

@>7200

ABS

@XYZ

Here are two examples.

Take the absolute value of locations 7200 and 7201 (hexadecimal). Take the absolute value of locations XYZ and

XYZ+1.

In the first example, the contents of the word at locations >7200 and >7201 is used. The address could be specified in hexa decimal or decimal, although hexadecimal is more usual.

In the second example, locations XYZ and XYZ+1 are used. Since "XYZ" is a symbol, by the definition of a symbol it repre sents something other than itself. You cannot tell by looking at the instruction just what memory location is to be used. There must be some other place in the program where XYZ is defined. One way to define a symbol such as XYZ is to use it as a label for some data. XYZ

DATA

Example:

906

(The DATA directive will be discussed in a the chapter on directives. )

Another way to define a symbol is with the EQD (equate or like this:

equivalence) directive, XYZ

EQU

>A000

This would mean that wherever the symbol XYZ has been used in the program, you could just as well have used >A000.

48

Addressing Modes Vjj^

With the Dow Editor/Assembler, the symbol must be EQU'd to a decimal or hexadecimal constant. Remember that it is best to put the EQU's at the end of the program.

However, with the TI Editor/Assembler, the symbol can be EQU'd to other symbols or even to the result of certain comput ations. Here is a more complicated example: XYZ

EQU

BUFSTR+100

This sets XYZ to 100 (decimal) greater than the value of BUFSTR. This type of computation is useful when there must be a specific relationship between two symbols in memory. A symbol can also be given a value with the REF directive,

but this only works with the TI Editor/Assembler loader and not with the Dow Editor/Assembler or with Extended Basic. Here is an example of defining the symbol VMBW for use with the three tech

niques of running an assembly language program. (VMBW is the name of a utility routine that is used to display on the screen.)

^j^

1)

REF

VMBW

TI Editor/Assembler loader.

2) VMBW

EQU

>2024

Extended Basic loader

3) MBW:

EQU

>6028

Dow Editor/Assembler

In case 1, the program is loaded with the TI Editor/Assembler mo dule in place. The programmer does not need to know the exact location of the utility routine. In the second case, the program

is assembled using the TI Editor/Assembler, but it is loaded with the Extended Basic module in place, the

correct

address.

so the programmer must supply

These addresses are

listed

in

section

24.4.8 of the TI Editor/Assembler manual. The third case is essentially the same as the second, although the module in place

is the Mini Memory Module and the location is different. These locations are listed in the Mini Memory manual, starting on page 35.

Another form of memory addressing uses both memory and register. Indexed memory mode uses a memory address, as described above, but also includes a register. The processor adds the memory address specified in the instruction to the contents of the register specified in the instruction; the resulting value, called an effective address, points to the data in memory. Here is an example.

A N^e/

@LIST(R1),R5

This means to use register 1 as an index into a list named LIST, adding the value of the appropriate item to the value in regis

ter 5.

This is very similar to the Basic statement "R5=R5+ 49

Addressing Modes

LIST(Rl)".

That is,

indexing in assembly language is like

subscripting in Basic (or mathematics). To help clarify what indexing is, assume that the symbol

LIST is equivalent to >A000 and that register 1 contains 20 decimal, or >14 hexadecimal. The instruction above would refer to the word value at locations >A014 and >A015. (Remember that word values must start at even locations.) If register 1 were to be incremented by 2, the instruction would refer to the word value at locations >A016 and >A017. Thus, if there is a list of

word values pointed to by LIST, them all into register 5.

this single statement can add

Indexing is used frequently in loops. If you are refering to word values, the loop counter should be incremented or decre mented by 2's, not by l's. That is, use INCT and DECT, not INC or DEC.

Also,

if the index value is derived somehow - perhaps

the value of the key pressed by a user is to be used to look up a value in a list - the value must be doubled before being used as an index. (You can easily double a value by adding it to itself, as "A R1,R1".) A small note of caution regarding indexing is necessary.

You cannot use register 0 as an index register. The reason for this is very simple: the direct memory address mode and the in dexed memory mode are stored in the same fashion internally, with the direct mode appearing to index by register 0. The computer acts as though register 0 always contains the value 0. Another mode of addressing is indirect addressing. There are two forms. Both use registers and are indicated by an aster isk before the register. Here is an example of the first form. A

*R4,R5

Add indirect register 4 to register 5.

Register 4 does not contain the value to be added, but contains the address of the value in memory which is to be added. ter 4 is in effect a pointer.

Regis

The closest Basic comes to a pointer is the PEEK subroutine.

If you use the statement "CALL PEEK(L0C,A)" the contents of the location pointed to by LOC will be moved into the variable A. Pretend that Basic has a function WORDPEEK(LOC) which returns the

value of the word pointed to by LOC.

Then "R5=R5+W0RDPEEK(R4)"

would be the same as the assembly language instruction above. The second form of indirect addressing, register indirect auto-increment, is very similar. This is what it looks like.

A

*R4+,R5

Add indirect register 4 (incremented) to register 5.

50

Addressing Modes

The use of the "+" means that the contents of the register, pointer value,

executed.

the

is to be incremented after the instruction is

V/hether it is incremented by 1 or by 2 depends on

whether the instruction refers to a byte value or a word value.

(Byte instructions are identifiable by their names: eg, AB is add byte.) The

feature is very useful in loops, si milar

auto-increment

to the way that indexin g is. There are differences, howeve r. For instance, you may n ot know when you write the program w here the list will be locate d in memory so you cannot have a lab el for it

such as

LIST

in

the

example above.

In that case, you ca n

make

a register point to the beginning of the list, and the comp uter will automatically move the pointer along the list as you g o through the loop. Sine e it will increment by 2's if you ar e processing word values, you can use a loop index which incr ements only by l's without hav ing to double it to use it as an ind ex value.

An

additional

b enefit

of this mode is the same adva ntage

that register addressin g has over memory addressing - namel y that it is not necessary to include the address in the instructi on, which makes the program a little smaller and faster.

Program counter relative addressing on the TI is only used for jump instructions. Here is a typical jump instruction. JMP

Jump to statement with label TOP.

TOP

On some other computers,

thi s mode can be used for instructions

such as

is

add

or

move.

It

a special form of memory addressing,

The nice thing about it is t hat the address is contained within the instruction itself (unli ke a normal memory address but like a register address). The bad thing is that it can only refer to memory locations within the vicinity of the instruction itself. The way it works is somewhat similar to indexing, in which a register value is added to a m emory address. With PC relative addressing, the memory address that is used is the address of the instruction itself,

and inst ead of the value of a register,

the

value of the displacement (s tored within the instruction) is added. In effect, a jump sa ys "jump 20 locations down in the program," or "jump 180 locat ions back up in the program."-There is nothing in the instructio n which specifies an absolute address, which is why it is ca lied "relative" addressing.

The reason this type of addressing is nice is that an in struction can transfer control to another location without having to use a second word to point to the new location. The displace ment value is the byte in the right half of the instruction it

self. This means that it has 8 bits, so it can have 256 possible values. These are treated as -128 through +127. Because in structions are all an even number of bytes in length, the value stored in the instruction is doubled before being used. Also, the

value

is added

to

the PC after 51

the instruction has

been

Addressing Modes

executed, so if you wanted to jump to the next instruction you

would end up with a displacement of 0 because the PC has already been incremented by the processor to point to the next instruc tion .

Although the instruction contains a numeric displacement value, when you look at an assembly language program you see a label instead. In the example above, the statement with the label TOP could be either before or after the jump, provided it falls within the -128 and +127 word

limit.

The assembler com

putes the difference between the instruction location and the labeled statement and puts the correct value into the instruc tion .

With the TI Editor/Assembler, you have another option with PC relative addressing which should in fact be avoided. If used at all, the jump should be to a location not very far away be cause, as you will see, you have to count how far to jump and it is easy to count incorrectly and thereby cause an error. Fur thermore, if you change your program so that an instruction is added or deleted,

the count will be wrong and the jump will go to

the wrong location.

This type of bug can be very difficult to

locate.

Now that you have been cautioned not to use this form, this

is how you do it. You explicitly refer to the relative nature of the addressing. The symbol $ is used to mean the current loca tion, so "JMP $+10" would mean to jump down five words, or 10 locations. The value you specify should be an even value, since the assembler will divide it by two because when stored in the instruction, it refers to a word count, not a byte count.

If you want to, with the TI Editor/Assembler you can specify an absolute location with a jump instruction.

In that case,

the

assembler has to convert it into a relative address anyway. The same restrictions about how far it is possible to jump apply in this

case.

Immediate addressing is a mode which was described in the context" of the add immediate instruction in the chapter on arithmetic. In this mode, the value to be used is present as

part of the instruction. All instructions with immediate ad dressing on the TI indicate that fact by their name, and all are two word instructions. LI, and LIMI.

The immediate instructions are:

AI,

CI,

\gg0r

52

Addressing Modes

Remember that for AI, CI, and LI, the first operand must be a register and the second an immediate value. Look at these four

examples.

The error messages from the TI Editor/Assembler and

the Dow Editor/Assembler are shown. INSTRUCTION TI E/A MESSAGE

DOW E/A MESSAGE @

1) CI

@A,29

SYNTAX

2) CI

A,29

INVALID REGISTER

ERROR

3) CI

R2,4

no

no

4) CI

R2,R6

no error

ERROR

error

ERROR

error

ERR:

LBL

R6

AT

loc

(shown during LOAD) Notice that in one case the TI Editor/Assembler will not detect

an error that the Dow Editor/Assembler will detect. This happens because the Dow Editor/Assembler knows that the symbols RO through R15 are always reserved for registers.

On the other

hand, the TI Editor/Assembler simply maps these symbols into the values 0 through 15; mean a register even the particular case, if the corresponding

therefore, if you intend such a symbol to though register usage is inappropriate in it will be assembled with no error message value is acceptable.

.Finally, the TI communicates with the outside word (such as printers, phone modems, and disk drives) through the CRD (Com munications Register Unit). Instructions which refer to the CRU include values which are CRU bit addresses. discussed

further

here.

53

These will

not

be

Addressing Modes

v^#

Here is a simple diagram which shows most of the addressinj modes.

DIAGRAM OF

ADDRESSING MODES

CPU

WP

REGISTER

MEMORY

34

7200

12

7202

00 00

7204

R0

7206

Rl

7208

12 I34

R2

720A

72102

R3

720C

84I1A

R4 etc,

CPU MEMORY (ROM OR RAM)



JMP

comes

here



MOV R2,@X

7D10

C8

02

7D12

72

00

7D14

04

D3

CLR *R3

7D16

02

04

LI

7D18

84

1A

7D1A

10

FC

R4,>841A

JMP $-6

7D1C —



The MOV instruction shows the register mode with "R2" and the memory mode with "@X". The contents of register 2 are moved to location X. Register 2 is at location 7208 because the WP register contains 7204. X is defined to be location 7200 (per haps with "X EQU >7200"). Notice that 7200 is stored as the second word

of

the

instruction.

The

instruction moves

the

from locations 7208 and 7209 to locations 7200 and 7201. 54

value

(In the

Addressing Modes

example, the value is "1234".) The CLR instruction shows indirect register addressing. Register 3 is at location 720A, where the value 7202 has al ready been stored. Therefore, the instruction clears location

7202 and 7203.

(The diagram shows the 0's resulting from the CLR

instruction. ) The LI

instruction shows the immediate mode.

The

value

to

be moved into register 4 (at location 720C) is stored as the se cond word of the instruction. Compare the second word here to the second word of the MOV - with memory addressing, a word is appended to the instruction to hold an address, while with immed iate addressing, a word is appended to hold a value.

The JMP shows PC relative addressing. The amount to jump is stored within the right half of the instruction. To compute the value, first subtract 2 from the amount specified in the assembly language statement. Thus, subtract 2 from -6 to get -8. (This is done because the PC register will have been incremented by two bytes before the arithmetic on its value is performed by the

CPU.) Then divide the result by 2; -8 / 2 = -4. If negative, convert this to two's complement; -4 = FFFC. Finally, take the right half of the result, FC.

Remember that an instruction such as JMP $-6 can be poor programming, since it is often better to use labels. The Dow Editor/Assembler does not even support this form. With either assembler, if a JMP with a label is used, it generates PC rela tive addressing anyway.

55

\&0

Jump and Branch Instructions These instructions are all used to alter the flow of a pro gram. There are several classes of these instructions: uncon ditional, conditional, subroutine calls, execute a remote instruction, and extended operation.

A major difference between Basic and assembly language is that the statement numbers in a Basic program are used by GOTO statements, but statement numbers in assembly language programs are only to number the lines for reference purposes. Jumps and branches in assembly language typically use statement labels. (There are other methods, but labels are preferable.) Look back in the chapter on looping to see examples of statement labels: LP1

and

LP2.

Unconditional jumps or form

the

same

function

as

branches in assembly language per

GOTO

statements

in

Basic.

There

are

two assembly language instructions: JMP and B. The differ ence between the two is that the B (for branch) can go to a loca

tion anyplace in memory, but the JMP (for jump) only uses the PC relative mode of addressing, meaning that it can only jump a relatively short distance to a statement with a label. (See the chapter on addressing modes - there are alternative forms, but they should be avoided.) Here is an example. JMP

TOP

The JMP instruction is appropriate for most of the times you want to transfer control unconditionally, even though a branch in struction can go longer distances. The reason to prefer the jump is that a well written program consists of modules or segments within which you have gathered logic pertaining to a particular process. As discussed below, control is passed to such modules with either of the BL or BLWP instructions, so the JMP is only needed

within

The

B

modules.

instruction

is

far

more

flexible

than

the

JMP

but

in

turn is overkill if you only want to move forward or backward in the program a small amount. If you were to use B to go to a label,

it would look like this. B

@TOP

This would do exactly the same as the jump shown above, requires two words of memory instead of just one.

but it

The real usefulness of B over JMP is in the flexibility of addressing that is possible with B. For instance, you can branch to a location which is held in a register. This is how to return from a subroutine called with BL. You branch indirect of regis-

56

\gj0r

Jump and Branch Instructions

ter 11, in which the return address was automatically stored by the

BL. B

*R11

However, B can also be used in a way similar to the ON GOTO of Basic. Suppose you want to branch to one of three labels in your program, depending on an integer value. You would have to

double the value for use as a subscript into a list of label addresses

The following example assumes that the value to be used for the branch is passed from the program DSK1.TESTB into location

>A800. The program returns one of the values >0000, >1111, or >2222 in location >A802 to prove that it branched correctly. Notice that it is necessary to move the address from the vector into a register in order to branch indirect, since there is no way to branch indirect of a memory location. TITL 'IMPLEMENT ON GOTO' (DSK1.TESTA)

A

VEC

LI

L2 L3 END

DEF

A

MOV

@>A800,R1

A

R1,R1

MOV

@VEC(R1),R1

B *R1 DATA LI DATA L2 DATA L3

LI

R1,>0000

JMP

END

LI

Rl,>llll

JMP

END

LI MOV

Rl,>2222 R1,@>A802

B

*R11

Move value into register 1. Double it for word addressing. Move branch address into register Branch indirect of register 1. List of branch addresses.

Value

was

0.

Return

0000.

Value

was

1.

Return

1111.

Value

was

2.

Return

2222.

END

The conditional jumps are used for the equival ent of the IF in Basic. Unfortunately you cannot simply say a s you can in Extended Basic "IF X>Y THEN Z=3".

pier "IF X>Y THEN 300".

N^j/

You cannot even say the sim-

Instead, in assembly language it is ne-

cessary to separate the operation which performs th e comparison of X and Y from the operation which alters the flow in the program based on this comparison. The comparison itse lf can be done either with a special compare instruction, or it ca n depend on numerous other instructions which automatically com pare a value against 0. In either case, the result of the compa rison sets some bits in the Status Register. There are then a number of jumps which you can use to complete the "IF" constr uct because they jump conditionally. For instance, if you want to know if

"X>Y", you would compare X and Y, then JGT, like on page. 57

the

next

Jump and Branch Instructions

J

C

@X,@Y

Compare X and Y.

JGT

SI

Go

if

X>Y.

In this example, SI is the label to which control will transfer if X is greater than Y.

In addition to JGT (jump greater than), there is also JLT (jump less than), JEQ (jump equal), and JNE (jump not equal). With combinations of these you can perform all the arithmetic comparisons you need.

Let us examine again the example above: IF X>Y THEN Z=3. It is actually inappropriate to jump if X is greater than Y, since

if that condition is true we want to do something, but if* it is false we don't want to do it. So really, the jump should take place if X is less than or equal to Y. Unfortunately, there is However, it can be done with two no such jump instruction. jumps, as follows.

SI

c JLT

@X,@Y

Compare X and Y

SI

JEQ

SI

MOV

@V3,@Z

Go if XA000 and register 3 contains 20 decimal,

which is 14 in hex.

Then the two locations to

and A015. Suppose that word contains hex F35E. that in binary to see what the inverse would be.

be A014

Let's look at

Original value hex

binary Inverted hex

binary

^^x

F

3

5

E

1111 0011 0101 1110 value

0

C

A

1

0000 1100 1010 0001

See how each individual bit is changed. All you need to do to figure out the hexadecimal equivalent value for the entire word is to be able to picture what happens to each digit. Notice that for each digit, the original and the resulting values always add 67

Logical Instructions

to 15 (in hex, F). Thus, F+0=F, 3+C=F, 5+A=F, and E+1=F. You can therefore invert a hex digit by subtracting it from 15. All three of the instructions discussed above operate on

only one value. The remaining all operate on two values. Just as with arithmetic instructions, there are both unary and bi nary operations. The following instructions are all binary. In each case,

each bit in the result is determined by some

how combining the corresponding bit in the two operands. For example, below are two 16-bit values that were chosen at random. Four of the 16 pairs have been identified with letters below the bits. A 0 or 1 will be placed where each letter is, depending on the values of the two bits immediately above it. The values of any bits to the left or right do not affect this determination. 1001010010100101

a

b

c

d

Notice that the four letters represent the four patterns that are

possible: 00, 01, 10, and 11. Below is a table which has column A discussion of headings corresponding to these four letters. all the various ways that two bits can be combined will make these specific logical instructions more understandable. The table below shows all possible combinations, with three named in the right margin. First

Second

bit

bit

Resulting bit Combination

1

Combination

2

Combination

3

Combination

4

Combination

5

Combination

6

Combination

7

Combination Combination Combination

8

0

0

1

1

0

1

0

1

a

b

c

d

0 0 0 0 0

0 0 0 0 1 1 1 1

0

0 1

0 0 0

0 1 1

1

0

0

0

1

1

0

1

1 0

Combination

9 10 11

0 0 0

0 0 1

Combination

12

0

1

Combination

13 14 15 16

1 1

0 0

0

1

1

0

1

1

1

Combination Combination

Combination

AND

0

EXCLUSIVE OR INCLUSIVE OR

1

0 1 1

Three of these have been identified because the resulting

bit depends on the two operand bits in an interesting and useful fashion.

68

Logical Instructions

The result labelled AND has a 1 only if both of the oper and bits have a 1. Incidentally, this is like multiplication of

two single bit values, since the result is 1 only if both factors are 1: 0*0=0, 0*1=0,

1*0=0,

but 1*1=1.

This is also similar to

the way we use the word "and" in normal conversation. use a

knife and a The result

Example: I

fork to eat steak.

labelled

INCLUSIVE OR has a

1 if

either

or

both of the operand bits is a 1. There is no exact analog to this in arithmetic, although you would get the same result if you added the two bits and then added the carry bit, if any, back into the sum.

That is,

0+0=0,

0+1=1,

1+0=1, and 1+1=10=1.

Often

the word "or" when used in plain speech has the same meaning. For example: I could swim or ride a bicycle to get exercise

(either or both is possible).

Sometimes people use "and/or" for

this.

The result labelled EXCLUSIVE OR has a 1 if only one of

the operand bits is a 1, but if both are l's the result is 0. This same result can be obtained by adding the two bits and discard

ing any carry bit. Thus, 0+0=0, 0+1=1, 1+0=1, but 1+1=10=0. Sometimes when we use the word "or" it has this sense, as in: you have to pull on your left pant leg first or your right pant leg first (but you can't do both at the same time). The other results are not seen as often either as instruc

tions on computers or for that matter in computer languages. Some are omitted because they do not depend at all on the operand bits: see the combinations 1 and 16. Others are not symmetrical, so it matters which operand has the 1 or 0: thus, combinations 3 and 5 are identical, except for the order of the operands. The description above should help you understand what hap pens with each bit. If you remember that the exact same opera

tion is applied to each of the bits in the word (or byte if it is a byte instruction), you should understand the instruction. In cidentally, in the chapter on comparisons, the instructions COC and CZC also worked in a bit-wise fashion.

They differ from

logical instructions in that they do not generate a result that is

stored.

69

Logical Instructions

Here are examples of bit-wise, or logical operations, ap

plied to entire words.

Each is shown in binary and hex notation

Example of AND First word Second word

1001

1011 1010 1010

0100 0101 0101

1011

954B

1010

CD5A

1011

DD5B

0100 1011 0101 1010 1000 0001 0001

1101 1000 0101

Result

954B

0100 0101 0100

0101

1100

CD5A

854A

Example of INCLUSIVE OR First word Second word Result

1001 0101 1100 1101 1101

1101

Example of EXCLUSIVE OR 954B

First- word Second word

1001 0101 1100 1101

CD5A

Result

0101

5811

The various logical operations have now been discussed. Here is the description of the instructions that are used for these operations.

To AND, you can use "and immediate", ANDI. This has the SZC, set zeros corresponding, is another instruction which performs a modified "and." It is

same format as "add immediate".

combination 5 instead of combination 2,

so it

does the same as

performing an inversion on the first operand and then anding the two.

It has the same format as the addition instruction,

A.

This is an example of using these two instructions. ANDI

R12,>F00F

SZC

@M,R2

And register 12 by hex F00F. Set register 2 to the result of SZCing M and register 2.

Here is an example of how the result is computed for SZC Example of SZC First

word

After inversion Second word Result

1001

0101

0100

1011

0110

1010

1011

0100

954B 6AB4

1100 0100

1101 0101 1000 0001

1010 0000

CD5A 4810

(based on first word after inversion) There is also a SZCB instruction, which operates on bytes. Re member that if the operand is a register, only the left byte is used.

To do an INCLUSIVE OR, there is ORI, "or immediate", and SOC, "set ones corresponding." The ORI has the same format as

ANDI

and

SOC

has

the

tually performs an "or",

same

format

as

SZC.

Note

that

SOC

ac

unlike SZC which performs a modified 70

Logical Instructions \!l^/

"and".

There is also SOCB, which performs an "or" on bytes: it

has

same

the

tion.

format

as

SZCB.

To do an EXCLUSIVE OR, there is only the XOR instruc The result is stored in the second operand which must be a

register.

Here is an example.

@ABC,R3

XOR

Exclusive or ABC to register 3.

You can modify the little program used to test arithmetic

instructions to become familiar with these various operations. Here

it

is with

the SOC

instruction.

TITL 'TEST SOC INSTRUCTION' (DSK1.TESTA) A

DEF

A

MOV SOC MOV

@>A802,R0 @>A800,R0 R0,@>A804

B

*R11

LOAD RO FROM LOCATION A802 SET ONES, LOCATION A800 TO RO

END

This is what happens when the Basic program calls this with the values 1 and 2, and then -256 and 10.

(0001) (0002) (0003)

1 2 3

(FF00) -256 (000A) 10 (FF0A) -246

You should of course try many other examples on your own until you develop a good feel for how the instruction works. will also help make you familiar with hexadecimal notation.

It

Try the assembly language program with SZC and XOR also. ding

of logical operations, it is perspective. Usually these instr uctio ns a re of value in manip ulating data within bytes or words . Fo r in stanc e, you may want to create the bit pattern to make a par ticu lar s hape t o be disp layed on the screen. Another examp le wo uld be pi otting wit h a d ot-matrix printer. It is helpful t o thi nk o f the "and" ope ratio n as masking: you can use it to "knoc k out or "str ip awa y" s orae o f the bits in a word or byte, On th e oth er h and, the "o r" is use d to meld two values together Befor e

neces

into

C

such

(to

sary

to

en

the

di scus sion

p ut th em int o

th e

pro per

a gain . Fr equent ly a d" operation will use a mask as he xade cima.l FF00 (to knock out the right half) or 00FF k nock out the 1 eft ha If), And frequently an "or" will have one

a only hav ing " 1" bit s in the left half (such as hex 0C00) and a nothe r va lue o nly ha ving it i n bits in the right half (such as value

71

Logical Instructions \^r

hex 0068).

Incidentally, these instructions set the status register so that you can follow with a jump instruction to test the result against 0.

Finally, if you use Extended Basic, you may not be aware that you can perform AND, OR, XOR, and NOT (inversion) in Extend ed Basic. The following program listing demonstrates the use of AND. (See statement 140.) 100 REM TEST LOGICAL OPERATIONS IN EXTENDED BASIC 110 FOR

1=1

TO

2

120 INPUT N(I) 130

NEXT

I

140 N(3)=N(1)AND N(2) 150 FOR

1=1

TO 3

160 T=N(I):: IF T65535 THEN N(I)=N(I)-65536 200 PRINT N(I); 210 B=BYTE1 220 G0SUB 280 230 B=BYTE2

240 GOSUB

280

250 PRINT ") ";N(I) 260 NEXT I 270 GOTO 110 280 REM BINARY TO HEX CONVERSION

290 HEXDIG1=INT(B/16) 300 HEXDIG2=B-16*HEXDIG1

310 PRINT SEG$("0123456789ABCDEF",HEXDIG1+1,1); SEG$("0123456789ABCDEF",HEXDIG2+1 ,1); 320 RETURN

\^0r

72

Shift

Instructions

After all the experience you have just gained with logical operations, it should now be quite easy to pick up an understand ing of the shift instructions. These are in fact basically logical instructions, in that they are also used to manipulate bits

within

words.

In order to understand these instructions, you must think of word values as strings of bits. There are two fundamental types of shifts - shift left, or shift right.

Here is an example of shifting a string of 16 bits to the left. Each line of the example represents shifting another bit. It is shifted a total of 5 times. The vertical lines represent the word boundaries. not exist anymore.

Original value After shifting After shifting After shifting After shifting After shifting

That is,

values outside the boundaries do

once 0 twice 01 three times Oil four times 0110 five times 01101

0110101010011101 1101010100111010 1010101001110100 0101010011101000 1010100111010000 0101001110100000

There is a difference between shifting to the right and shifting to the left, as in the example above. In the shift to the left,

0's were filled on the right as the bits already there moved across to the left and vacated their positions.

In the shift to

the right, it is possible to fill with 0's on the left. How ever, there is also the option of filling with either 0's or l's, depending on what was initially present in the left-most bit.

(Incidentally, the bit on the left is the sign-bit and is numbered 0, and the bit on the far right is numbered 15.) Here is what happens with a right shift that extends the sign-bit. Original value After shifting After shifting After shifting After shifting After shifting

1010101010011101 1101010101001110 1 1110101010100111 01 1111010101010011 101 1111101010101001 1101 1111110101010100 11101

once twice three times four times five times

The other possibility for filling on the left side is to pick up the bit that was dropped off the right side. It would also be possible to fill on the right with the bit that was dro pped off the left side. Either is called a circular shift. A computer designer could make a circular shift go in either direc tion. However, both are not really necessary, in that a circular shift to the right by 1 bit is the same as a circular shift to

73

Shift

the

Instructions

left

15

bits,

Here is an example of circular shifting to the

right.

Original value After shifting After shifting After shifting After shifting After shifting

1010101010010101 1101010101001010 0110101010100101 1011010101010010 0101101010101001 1010110101010100

once twice three times four times five times

Having looked at all these examples, it is now appropriate to list the instructions actually available on the TI. SRC

Shift right circular

SRA

Shift right arithmetic (fills on left with

SRL

Shift right logical (fills on left with 0's)

SLA

Shift left arithmetic (fills on right with 0's)

sign bit)

For all of these, the first operand is a register that contains the value to be shifted. You must also specify how many bits to

shift with the second operand, and you use the same method for all of them. You can either specify the shift count in the in struction itself, or you can specify 0 in the instruction, which means to use the value in register 0 to specify how many to shift.

Here are two examples. Notice that the first shifts the contents of the register by 3, which is specified in the instruc tion, and so it is very similar to immediate addressing. SRA

R6,3

Shift right arithmetic register 6 by 3

bits.

In the second, the number of bits to shift is not in the instruction itself but is specified in register 0. SRL

R3,0

Shift right logical register 3 by the value in register 0.

Here is an assembly language example of the SRC instruction You should try it as well as the others. TITL 'TEST SRC INSTRUCTION* (DSK1.TESTA)

A

DEF

A

MOV MOV SRC MOV

@>A802,R1 @>A800,R0 R1,0 R1,@>A804

B

*R11

LOAD Rl FROM LOCATION A802 LOAD R0 FROM LOCATION A800 SHIFT Rl BY CONTENTS OF R0 STORE RESULT IN A804

END

74

Shift

Instructions

Here are several sample results of calling this program,

using the same small Basic program (DSKl.TESTB) used in previous chapters. (0001) (0002) (4000)

1 2 16384

(FF00) -256 (0004) 4 (0FF0) 4080 (0003) 3 (0001) 1 (8001) -32767 In the first example,

the value 1 is shifted out of the

right position (bit 15) into the left-most bit and then again into the second bit, making hex 4000. In the second example, 8 bits can be seen to shift right 4 bits (which is one hex digit), thus changing FF00 to 0FF0. In the third example, two l's are shifted right just 1 bit, leaving one on the right side and moving one to the left side - this yields hex 8001. Here is a more elaborate example, combining a shift and a loop structure. The purpose of this program is to find the highest power of 2 in a given number. Due to the way it is written, you cannot give it a number greater than 16,384. To use

the Basic test program (DSKl.TESTB) to call it, you may wish to change statement 120 to be "1=0", delete statements 190 and 340, and change "6" to "2" in statement 210. TITL A

A

MOV

@>A800,R0 Rl, 1

C

R1,R0

JEQ

OK DEC

R1=R0. R1>R0.

Rl, 1 L

MULTIPLY Rl BY GO BACK TO TRY

JGT SLA JMP DEC OK

2'

LOAD R0 WITH VALUE SET Rl = 1 IS R1=R0 OR R1>R0?

LI L

'FIND HIGHEST POWER OF

DEF

FOUND LARGEST VALUE. TOO LARGE. QUIT

SRA MOV

Rl,l

TOO BIG,

R1,@>A802

RETURN

B

*R11

QUIT.

2 AGAIN.

SO DIVIDE BY 2.

RESULT

IN

A802

END

%^y

The program puts the value you specify into register 0. The value 1 is put into register 1. Then the 1 is shifted repeatedly to the left by 1 until it is equal to or greater than the value you entered. If it is greater than your value, it is shifted back to the right once.

75

Shift

Instructions

Here are the results of entering 1 and 19 (0001) (0001)

1 1

(0013) (0010)

19 16

76

Directives

Commands that you give to an assembler are called "direc tives". None of them generate any instructions for the computer to execute. Instead, they are used to control the listing, to make the program load at a particular location, to reserve space in memory for data, to link to other programs, and so forth. Some directives are common to both TI's Editor/Assembler and the Dow Editor/Assembler, and some are not. All are discussed here, and the differences between the two assemblers are describ ed .

When using the TI Editor/Assembler, you must put the END directive at the end of each program.

This is not necessary with the Dow Editor/Assembler, since the editor and assembler func tions are combined in one program, which knows where the end of your program is. There is nothing complicated about this directive: it just marks the end.

If you are using the TI Editor/Assembler, you have access to a very powerful feature known as "linking". By means of the REF and DEF directives, the loader is able to link together the various parts of your program and link your program to

library routines (that is, utilities). What this means in practical terms is that your program can refer to something by its name instead of by its location in memory. (By comparison, using the Extended Basic loader or the Dow Editor/Assembler, you must know the location and enter it into the program with an EQU statement.) If you want a program to be callable by another program, you use the DEF statement to list any labels which are to be known

"externally".

(Naturally any label in your program can be used

from within the program itself: DEF makes a label available outside your program.) If your program refers to something out side itself, it must list that label on a REF statement. Here are examples.

MYSUB

DEF

MYSUB

REF

SUB1.SUB2

...

(The program called MYSUB starts here)



*



BL •



@SUB1

(This is a call to SUB1)

@SUB2

(This is a call to SUB2)



BL END

This represents the fragments of a program which can be called by the name MYSUB and which in turn calls SUB1 and SUB2.

77

Directives

\$g0r The

DEF

program, REF

statement

statement

program,

declares

that

MYSUB

can

be

used

outside

presumably by some other program that you write. declares

that

SUBl

and

SUB2

are

defined

in

this

The another

perhaps a subroutine you wrote earlier.

Use DEF to define the entry point for a program so that it can be called by name to be run. For instance, the name you specify with DEF is the name you refer to in the CALL LINK in Basic

or

Extended

Basic.

EQU is the directive used instead of REF when loading as sembly language programs with Extended Basic or when using the Dow Editor/Assembler. It has other uses as well because what it actually does is assign a value to a symbol in the program. Whenever that symbol is used in the program, it is as if the value itself had been used. For instance, here is an example that was taken from the Tombstone City game included as an

example with the TI Editor/Assembler. It illustrates the use of EQU to give a meaningful name (SHIPRT) to a hexadecimal value (>68).

SHIPRT EQU

>6800

J

LI R4,SHIPRT MOVB R4,@SHIP This assembles into exactly the same machine language instructions

as

if

these

two

statements

had

been

used.

LI R4,>6800 MOVB R4,@SHIP

In both cases, the byte value >68 is moved into location SHIP. In this instance, >68 is apparently a character pattern instead of an address. As another example, you may want to use a symbol to indicate a constant which is used in several places in your

program. You could use the symbol NSTARS to mean the number of sprites that look like stars zooming around on the screen. The symbol would of course only be defined once, like this: NSTARS EQU 20. Then, if it becomes necessary to change the value, you need only change the value in the EQU statement. Do not confuse the assembly language statement "A with the Basic statement "A = 5".

EQU

5"

Using the EQU statement to

give a symbol a value is a concept entirely alien to Basic. Whether in Basic or assembly language, a symbol such as "A" has a value which is the address at which the contents of the variable

is stored. The difference is that in Basic you have no need to know what the address is, and so Basic programmers do not even need to know that "A" is a symbol with a value.

An interpreted language such as Basic maintains a symbol 78

Directives

%&'

table.

The symbol "A" would be an entry in the table, and stored

in the table would be its value,

the address.

Each time the var

iable "A" is used, the interpreter finds the symbol "A" in the table,

and then uses the address stored with it

to retrieve or

store the value of the variable (5 in the example in the preced ing paragraph).

In assembly language you may also not know the actual ad

dress, since a symbol can be assigned a value by usage as a label (and in other ways to be discussed below). However, if you desire, you can use a directive such as EQU to force a symbol to have

a

known

value.

There are three ways of giving a symbol a value: use it as a

label, use REF (if using the TI Editor/Assembler loader), or use EQU. When using EQU, usually asymbol will be assigned an address represented as a hexadecimal constant.

It could be the

address of a utility routine or it could be the address of your own routine. For instance, if using the Dow Editor/Assembler there is a limit to program size, so different parts have to be assembled and loaded individually: you should then use EQU's to enable each part to refer to the other parts. Here is a simple \^p-

example of an EQU, although the examples that follow later in this book will be much more meaningful.

STRREF EQU STRREF EQU

>2014 >604C

(For Extended Basic) (For Mini Memory Module)

Having included the EQU for STRREF in your program, you can now branch to STRREF with BLWP. (STRREF is a utility that copies a character string from a Basic program into your assembly lan guage subroutine.) Next in order of usefulness after the directives

described

above is probably the DATA directive. This allows you to put data into your program. Usually you will have a label on it as well. It is not the same as the DATA statement in Basic, since in Basic you must use a READ statement to use any value in a DATA statement, while in assembly language you address the value directly by the label.

An example of using the DATA directive is if you want to divide something by 100. There is no "divide immediate" instruc tion which would allow you to specify the value 100 right in the instruction. Instead, you can put the value 100 in a word in memory, and then refer to that location when doing the division, like

this . . .

DIV •

V100



@V100,R5

Divide 100 into register 5

100

The value



DATA

79

100.

Directives

\&0r

(Notice that the label used here suggests the value itself:

"V100" for "value 100".) The value you assign in a data statement can be a decimal or hexadecimal constant.

embler,

With TI's Editor/Ass

but not with the Dow Editor/Assembler, it can also be a

string.

The BYTE directive is very similar to DATA, except that it loads individual bytes, not whole words. This directive is use ful for byte oriented instructions. Remember to watch out for creating odd addresses in your program by defining an odd number of bytes. Many programmers always insert an EVEN directive after one

or

more

BYTE

directives.

After using BYTE with an odd number of bytes, the location

counter for the TI Editor/Assembler may have an odd value.

How

ever, if you follow it with an instruction or DATA directive, the location will automatically be incremented to the next even value, since values addressed by word oriented instructions and instructions

necessary,

themselves

must

be

loaded

at

even

addreses.

If

you can use the EVEN directive to force the location

to be even.

(The Dow Editor/Assembler automatically prevents the

odd address problem from happening with the BYTE directive. It differs from the TI Editor/Assembler also in that decimal values must be positive.)

With either the TI or Dow Editor/Assembler, you can define a string of characters without having to use BYTE to define each character individually. TEXT is very similar to DATA and BYTE in that it stores the values you specify and allows you to have a label

for

them.

A

difference

is

that

it

is restricted

acters that you can enter with the editor.

to char

This is what it looks

like:

SAYBYE TEXT

'Bye now, see you later'

(The Dow Editor/Assembler allows you to use any break character to define the string in the TEXT statement. In the example above, the quotation mark was used for the break character. You could, for instance, also use the slash (/) if you wish.)

The Dow Editor/Assembler also differs by having an addi tional directive, BTXT. This stands for "Basic TEXT" and is used when you want to define a string to be displayed on the screen by a subroutine called from a Basic program. The reason for this extra directive is rather obscure, having to do with the way Basic uses VDP memory as efficiently as possible. What it amounts

to is a

bias of

hexadecimal

60 added

to each character

before being stored in the screen image in VDP memory. This bias amount is automatically added by BTXT so that characters are dis played correctly. If you are writing a program to be run inde pendently of Basic, be sure to just use TEXT. Here is a sample assembly language program to display data on the screen. (The 80

Directives

program is very simple. All it does is call a utility program to copy data to the screen. This will be discussed later in the Primer.) TEST TEXT AND BTXT DIRECTIVES CLR RO LOAD RO WITH VDP ADDRESS 000 002 LI R1;TXT LOAD Rl WITH CPU ADDRESS R2;5 006 LI LOAD R2 WITH CHARACTER COUNT BLWP @MBW 00A SEND DATA FROM CPU TO VDP *R11 B 00E 010 TXT:TEXT /HELLO/ TEXT TO BE SENT (WRITTEN ON

SCREEN)

016 MBW:EQU

>6028

Load the assembly language program into the Mini Memory Module, then run it by selecting the Mini Memory option and then sel

ecting "RUN". Type the same name for the program that you loaded it into the REF/DEF table. Watch closely - the word HELLO will flash rapidly in the upper corner of the screen. Now enter this Basic program and run it. 100

CALL

CLEAR

110 CALL LINK("TEMP")

l^r-

120 GOTO This

time

the

120

screen

is

blank

because

the TEXT

directive

did

not

bias the characters as the BTXT directive would. Now go back to the assembly language program and change TEXT to BTXT at loca tion 010. Again run the Basic program. Now it should say HELLO in the corner

of

the

screen.

If you want to define an area of memory to be used as a list of values or as a string, the best way is to assign a label to it

and reserve space with the BSS directive. starting with symbol".

This means "block

This statement will reserve 100 bytes, which is 50 words, and give it the label BUF. BUF

BSS

100

Buffer space.

BSS is somewhat similar to DIM in Basic.

some very significant differences. in

Basic

looks

However,

there are

This is what a DIM statement

like.

DIM N(25),S$(15) First, lla^

BSS allocates memory exactly where it occurs in your

program, whereas you the programmer have no idea where Basic has obtained space for your variables. Second, BSS allocates exactly as many bytes as you specify, whereas Basic allocates eight bytes 81

Directives

v^r

for each numeric value and as many as are necessary for each

string in your array.

Thus, eight bytes are allocated for each

of N(l), N(2), N(3), and so forth.

And a variable number of

bytes is allocated to each of S$(l), S$(2), S$(3), and so forth. Remember that in assembly language, the address of the first location assigned to the label (BUF in the BSS example above) is equated to the symbol. Therefore, if you are computing an index into the space, don't forget that the first word is indexed by 0. Also, remember that you must increment the index value by 2 for each

new word

value.

The TI Editor/Assembler also has the directive BES,

is "blocked ended by symbol".

which

This is useful if for some reason

you want to use negative index values, since they would be sub tracted from the label at the end of the space.

When you load a program with the TI Editor/Assembler or Extended Basic, you should not be concerned about its location in memory. The loader worries about that. If your program assigns

all the memory space it needs with BSS (or perhaps BES), you should have no problem. However, it is possible to control where your program is loaded by using the AORG directive. This allows you to specify an absolute address, and that part of your program which follows the directive will start loading at the specified location. This should only be used when absolutely necessary and with extreme caution, since you could cause the program to load on top of something else. With the Dow Editor/Assembler there is no

AORG directive

because every time you load you use the LOAD command and spec ify the absolute address where the program is to be loaded. The LOAD command is thus very much like the AORG directive. Another

difference is that if using the Dow Editor/Assembler, you may wish to assign blocks of space using EQU rather than BSS. The reason for this is that there is only a certain amount of memory available at one time for the editor, and using it for a block of memory could result in more fragmentation of your program than would be nice.

However, if you keep track of how your are using memory with a memory map (in your notebook!), you can use EQU in the program to make a label refer to the appropriate place in memory.

82

J

Calling From Basic Programs

There are four routines which can be used to pass data back and forth between a Basic program and an assembly language pro

gram. These routines are usually preferable to the technique frequently used so far in this book, which is to have the Basic program call LOAD or PEEK to pass values through specific memory locations.

The four routines are: STRREF and NUMREF (to pass strings and numbers from Basic to assembly language), and STRASG and NUMASG (to pass strings and numbers from assembly language to

Basic). They are discussed in the TI Editor/Assembler manual, pages 284 through 287, and in the Mini Memory Module manual, pages 52 through 54. If you are using Basic with the TI Editor/Assembler Module, just use the REF directive to refer to these routines and be sure to

load

the

file

DSK1.BSCSUP.

For

use

with

either

Extended

Basic

or the Mini Memory Module, use the following addresses with the EQU directive.

Mini Memory

Extended

NUMASG NUMREF STRASG STRREF

Basic

Module

>2008 >200C >2010

>6040 >6044 >6048 >604C

>2014

For all four routines, registers 0 and 1 must be set with

appropriate values before the routine is called. If the argument being passed is not an array element, register 0 should be clear ed (set to 0). Register 1 should be set to indicate which argu ment in the CALL LINK is to be passed. In determining what num ber to use, do not count the name of the subroutine itself; thus,

in CALL LINK("NTOS",A,A$), A is 1 and A$ is 2. For the two numeric routines,

the value is passed to the

assembly language program using FAC, the floating point accumul ator. It has 8 bytes, starting at location >834A. To refer to it, use FAC EQU >834A. For the two string routines, register 2 must contain the address of the string in the assembly language program. The

first byte of the string indicates the length of the string. STRREF, the first byte should be set to indicate the longest

string that can be accepted from the Basic program.

For

(If the

string actually passed is too long, you will get an error message indicating string truncation.) For STRASG, the first byte should indicate the exact string length to be sent to the Basic program.

83

Calling From Basic Programs

J

This chapter includes the listing for a program which uses all four calls. The routine STON (for String TO Number) accepts a string as the first argument and just passes it back as a num ber in the second argument. (See statement 340 in the Basic pro gram below.) The routine NTOS (for Number TO String) accepts a number as the first argument and passes it back as a string to the second argument. (See statement 270 below.) Since strings and numbers are the two different types of data provided by Basic, these routines perform the type trans fer function. That is, you can treat a number as a string and vice versa. (This is not the same as VAL and STR$, which convert a number to a string, and vice versa.) Here is the Basic program which performs the calls. The program has two numeric variables, A and B. Corresponding to each is a string, A$ and B$. The program allows you to enter a number or a string into A or A$, and it then makes the other agree. Each number is stored in 8 bytes, and these can be copied into a string of length 8. For example, if you enter the numeric value 100 into A, the program sets A$ to an 8 byte string having the same contents byte by byte as A. The program has an option to enable you to copy A into B and A$ into B$. It displays both A and B as numbers, then displays the ASCII equivalents for the 8 bytes of A$ and B$. Finally, it performs comparisons between A and B and between A$ and B$ and displays the results of the comparisons. 100 110

REM PROGRAM TO TEST NTOS CALL INIT

AND STON

120 CALL L0AD("DSK1.ST0N0BJ") 130

B=0

140 CALL LINK("NT0S",B,B$) 150

PRINT

:

:

:

160 PRINT "ENTER 1 TO MOVE A TO B" 170 PRINT "ENTER 2 TO ENTER A NUMBER" 180 PRINT "ENTER 3 TO ENTER A STRING" 190

INPUT CHOICE

200 IF CH0ICE3 THEN

150 150

220 ON CHOICE GOTO 230,260,290 230

B=A

240 B$=A$ 250 GOTO

150

260 INPUT "ENTER NUMBER:":A

270 CALL LINK("NT0S",A,A$) 280 GOTO

350

290 A$="" 300 FOR

C=l

TO

8

310 INPUT "ENTER BYTE "&STR$(C)&":":BYTE 320 A$=A$&CHR$(BYTE) 330

NEXT

C 84

v^ggr

Calling From Basic Programs

340 350 360 370 380 390

400 410 420 430 440 450 460 470 480 490 500 510 520 530 540 550 560 570 580 590 600

CALL LINK("ST0N",A$,A) PRINT : :"A=";A N$=A$ GOSUB

540

PRINT "B=";B N$=B$ GOSUB 540 IF A>=B THEN

430

PRINT "AB"

IF A$>=B$ THEN 490 PRINT "A$B$" GOTO 150 REM DISPLAY BYTES FOR C=l TO 8

IN

STR

PRINT ASC(SEG$(N$,C,1)); NEXT C PRINT RETURN END

The assembly language program (Extended Basic version) is listed on the next page. The numbers in the left margin are mentioned in the accompanying text. The routine STON must first prepare the space S (at 20 on the listing) to accept the 8 bytes of the string. The first byte has to have an 8, meaning that at most 8 bytes can be received. At 1, the MOVB @DB8,@S puts the value 8 into the first byte of S.

(DB8 means "decimal, byte, value 8"; this is just a convenient means of naming the value. The value is defined at 21 on the listing.) At 2, register 0 is cleared (because the value to be received is not an element of an array). At 3, register 1 is set to 1 to get the first argument. At 4, register 2 is loaded with the address of S. Finally, at 5, BLWP is used to call STRREF to move the string.

85

Calling From Basic Programs

TITL

'STON AND NTOS'

* * *

CALL LINK("STON",S$,N) CALL LINK("NTOS",N,S$) To

be

called

from

Extended

Basic.

*

FAC NUMASG NUMREF STRASG STRREF

EQU EQU EQU EQU EQU

5

DEF MOVB CLR LI LI BLWP

6

LI

7

LI

8 9 10

MOVB

1

STON

2

3 4

MOVB MOVB

>834A >2008 >200C >2010 >2014

STON,NTOS

@DB8,@S RO

Rl,l R2,S @STRREF R0,S+1 R1,FAC *R0+,*R1+ *R0+,*R1+ *R0+,*R1+ *R0+,*R1+ *R0+,*R1+ *R0+,*R1+ *R0+,*R1+

Move 8 to S (max number of chars) Call STRREF to move string in first argument into S.

Now copy string from S to FAC.

11

MOVB

12

MOVB

13 14

MOVB

15 16

MOVB CLR

17

LI

Call NUMASG to pass FAC back to second argument.

18 19

RT

All done. Return to Basic program.

MOVB

*R0+,*R1+ RO

Rl,2 BLWP @NUMASG

NTOS

CLR

RO

Call

LI

Rl,l

argument into FAC.

BLWP

@NUMREF

MOVB

@DB8,@S

LI

RO,FAC R1,S+1 *R0+,*R1+ *R0+,*R1+ *R0+,*R1+ *R0+,*R1+

LI

MOVB

MOVB MOVB MOVB MOVB MOVB MOVB MOVB CLR

*R0+ *R0+ *R0+ *R0+ RO

NUMREF

to

move

number

in first

Move 8 into S (string length). Now copy string from FAC to S.

*R1+ *R1 + *R1 + *R1 +

LI

Call STRASG to pass string to second argument.

RT

All done.

Rl,2 LI R2,S BLWP @STRASG

86

Return to Basic program

Calling From Basic Programs NSfe/

*

20 S

BSS

9

*

Space large enough for length byte and 8

21 DB8

BYTE 8

characters.

Max string length for NAME.

END

Now that the 8 bytes have been accepted into S, they have to be,copied into FAC to be sent back via the second argument. To copy the 8 bytes, the MOVB *R0+,*R1+ instruction is used 8 times, at 8 through 15. Each time it is executed, it moves one byte and increments both registers 0 and 1 to point to the next byte in sequence to be moved. Before the first use, however, it is ne cessary to make register 0 point to the first byte of the string in S (this is done at 6) and to make register 1 point to the first byte where the string is to be moved in FAC (this is done at 7) .

Because the first byte of S contains the length, the first data byte is at S+l. Notice, at 6, the statement LI R0,S+1, which puts the address of the second byte in S into register 0.

If you are using the Dow Editor/Assembler, you cannot use S+l.

\^S

In that case, you have to load register 0 with the address of S (use LI R0,S) and then increment the address with INC RO. Passing the number back to Basic once it has been moved into FAC is done with NUMASG. At 16, register 0 should be 0 because it is not being passed an array element. At 17, register 1 is loaded with 2 to pass the data to the second argument of the CALL LINK. After calling NUMASG at 18, the program returns to the

calling Basic program with RT at 19. The routine NTOS is nearly identical to STON, discussed

here

so it is not

in detail.

These programs can now be used not only to learn how to pass data but also to learn how numbers are actually stored in the TI.

This is described in the TI Editor/Assembler manual on page 279 and in the Mini Memory Module manual on page 27.

Here is how to interpret values that are positive, within the range 0 to 9999.

If first byte is 64, Value = second byte.

If first byte is 65, Value = second byte times 100 plus third byte.

87

Calling From Basic Programs

\^F

Here are some examples of values for the resulting string. A

String 1

2

3

0 1

0 64

0 1

?

4 ?

6

-1

191 65 65 66

255 1 99 3

100

9999 2767

0 0

5

6

?

?

0

0

0

0 0

0 0 67

99 27

A and the 8 bytes in

7 ?

8 ?

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

In the first example, A is 0. In that case, only the first two bytes have any meaning - both are 0. (The question marks mean that it does not matter what is in the last 6 bytes.) In the

next example, A has the value 1. The first byte, 64, is the ex ponent, which is always biased by 64. Therefore, an exponent value of 64 means 100 raised to the 0 power. The next example, A = -1, has an exponent of 191 and a first byte of 255. Taken

together, these values form the two's complement of 64 and 1. (In hex, 64 and 1 is >4001, while 191 and 255 is >BFFF.) When the value of A is 100 or greater, the exponent is no longer 64 but

increases to 65 or higher.

In all cases, subtract 64 from the

J

exponent to find the correct power. For instance, 65 means multiply by 100, 66 means multiply by 10000, and so forth. Now use the option to assign a string to A.

If you assign

the random string 231, 23, 49, 0, 128, 23, 3, 67, to A, it is displayed as -G3.49013E-80, which is nonsense,. (The E-80 is the exponent, but the -G3 is garbage.) If you set A and B to the two values below, the number A is greater than the number B, but the string A$ is less than the string B$, certainly a strange re sult .

A B

= =

127 127

127 127

127 127

127 127

127 127

127 127

127 127

128 127

Further experimentation will show that numeric comparisons do not work well if you load numbers with strange values. Also, string comparisons treat each byte as a signed value. Therefore, any decimal value between 128 (hex >80) and 255 is less than 127

(hex >7F) because 128 turns on the sign bit for the byte.

If you

want to test two strings to see which is less, and if you have loaded the strings with values in the range 0 to 255 (instead of just 0 to 127), you must compare individual bytes with SEG$ ra ther than comparing the strings as a whole. The Basic program shown above assumes Extended Basic is

ing used.

be

For Basic with the TI Editor/Assembler, include DSK1.

BSCUP in the CALL LOAD on

statement 120.

For the Dow Editor/

Assembler, delete statements 110 and 120, and follow previous

examples in this book to convert the assembly language routine. 88

\^0/

A Case History

In this chapter, the actual case history of the development of an assembly language routine will be shown. It will mean that there will be several listings, as the routine starts out simple

and develops until it does all that we want.

This should give

you a better idea of the types of problems that can be encounter ed when programming in assembly language. The goal will be to write an assembly language program which will be useful. A way to do that is to enhance what can be done with Basic, and one way to do that is to do something faster. Look at this small program. 100 REM TEST MULTIPLE CONCATENATION

130 READ A$,B$,C$,D$,G$,M$,W$,X$,Y$,Z$ 140 DATA A,B,C,DEF,GHIJKL,MNOPQRSTUV,W,X,Y,Z 150 160

INPUT N FOR 1=1

TO

N

170 MC$=A$&B$&C$&D$&G$&M$&W$&X$&Y$&Z$ 180

NEXT

I

190 PRINT MC$ 200

END

When you run it, it will prompt for N. It then goes through the loop N times, allowing you to time how long it takes to concat enate all the strings to assign the result to MC$. When it is done, it prints MC$ so you see that it worked properly. Given the value of 100, I found it to take 22 seconds. This seems ra ther slow. Just for comparison, if statement 170 is changed to this...

170 MC$="ABCDEFGHIJKLMNOPQRSTUVWXYZ"

it only takes 1 second to iterate 100 times. Clearly doing many concatenations (the "&" operation in Basic) might be something that could be improved with assembly language. Here is another version of the program above. It is written with the assumption that there exists a routine named "MC" (for "multiple concat enation") that is being called from Basic using the TI Editor/ Assembler. (Modifications for use with the Dow Editor/Assembler and Extended Basic will be shown later.)

89

_A Case History

100 ] REM 110 1 CALL 120 ( CALL 130 ] READ

140 150 160 170 180 190 200

TEST MULTIPLE CONCATENATION INIT

L0AD("DSK1.BSCSUP","DSK1.MCEA0BJ") A$,B$,C$,D$,G$,M$,W$,X$,Y$,Z$

] DATA A,B,C,DEF,GHIJKL,MNOPQRSTUV,W,X,Y,Z : INPUT N ] FOR 1=1 TO N ( CALL LINK("MC",MC$,A$,B$,C$,D$,G$,M$,W$,X$,Y$,Z$) 1 NEXT I ] PRINT MC$ 1 END

At this point we have defined a problem and have written an example program showing what we would like to do. Now all that

is left is to write the actual assembly language routine to do the multiple concatenation! We can only hope that it will result in an improvement in speed.

Let's start by trying to write MC so that if called with a string as one argument it will return it as another. test

Here is the

program. 100 REM

TEST MULTIPLE CONCATENATION

110 CALL

INIT

120 CALL L0AD("DSK1.BSCSUP","DSK1.MCEA0BJ") 130 CALL LINK("MC",MC$,"TEST") 140 PRINT MC$ 150

END

We want this to copy the string "TEST" into MC$.

For the

program to work, it is necessary for it to be able to get data from the Basic program and to return data to the Basic program. One reason I have chosen this particular program to demonstrate parameter passing between Basic and assembly language is that strings are very simple. They consist of one or more bytes, usually designating characters. Unlike numbers, strings are thus essentially the same in Basic as in assembly language. The

utilities

STRREF and

STRASG can

be

used

from assem

bly language to get strings from Basic and to return strings to Basic, respectively. It is not necessary to use them, but they make programs much easier to read and do not add enough to the overhead of the program to be a consideration. In both cases, register 0 must be set to 0 before calling the utility to indicate that the string to be passed is not an element in an array. Register 1 must be set to indicate which parameter is to be used - this is a number from 1 to 15. And register 2 is loaded with the address of the buffer which con tains the string in the assembly language program.

90

\g§jjj/

A_ Case History Here is the initial program.

It is the file "DSK1.MCEA".

TITL !MC - Multiple Concatenate' MC

DEF

MC

REF

STRREF,STRASG

CLR LI LI BLWP DEC BLWP RT

RO Rl,2 R2,BUF @STRREF Rl @STRASG

Set register 0 to 0. Get the second argument. This is where it goes. Get the string. Return the string in 1st argument. Send the string back now. Now return to the Basic program.

END

To assemble, send the object to "DSK1.MCEAOBJ". can go to your printer or to "DSK1.MCEAL".

The listing

The assembly results in one error which can be identified in

several ways. First, there is the message "UNDEFINED SYMBOL 0006". The "0006" refers to line 6 in the program, which has the symbol "BUF". Second, after the program listing there is the heading "THE FOLLOWING SYMBOLS ARE UNDEFINED:" and below that "BUF" is listed. Finally, because the "S" option was used, the symbols are listed by the assembler, and "BUF" is flagged with "U" for

"undefined".

From this, we ought to realize that BUF has to be defined.

BUF is supposed to be an area of memory: 1) into which the string is copied from the second argument, and 2) from where it is to be copied to the first argument. Memory can be assigned by using the BSS directive.

Below is the program with BUF defined.

TITL 'MC - Multiple Concatenate' DEF MC STRREF, REF ST] CLR R0 CLR R0 Rl,2 LI R2,BUF LI BLWP @STRREF Rl DEC BLWP @STRASG

MC

BUF

RT BSS END

255

Set register 0 to 0. Get the second argument. This is where it goes. Get the string. Return the string in 1st argument. Send the string back now. Now return to the Basic program. Define buffer space.

This time it assembles with no errors.

what happens.

Now,

let's run it to see

(No guarantees!!!)

Running the Basic program results in the message "UNKNOWN %^y'

ERROR CODE IN 150". A lot of help that is! Unfortunately, that is all too often what happens with assembly language. Remember that one of the strong points of a language like Basic is the 91

A^ Case History degree of support an interpreted language is able to give to the programmer.

Instead of trying to figure out what that message means, I

realized that I forgot to prepare BUF properly before calling STRREF.

BUF is to receive a string, and it is necessary to indi

cate the length of the longest allowable string that can be ac cepted. This is explained in this book in the chapter on calling

from Basic programs, in the TI Editor/Assembler manual, page 287, and in the Mini Memory manual,

page 54.

Because the maximum length was not placed in the first byte, the program has to be changed again in two ways. One change is to make BUF one byte longer in order to hold the maximum length in the first byte. A second change is to insert some code to move the maximum length into the byte. Note that it would usually not be good to load the proper value with a DATA or BYTE directive, since the byte is changed to the actual length by STRREF. This means that on subsequent calls to MC, the byte may no longer allow as long a string as we would like. Here is the modified program.

TITL 'MC - Multiple Concatenate' DEF

MC

REF STRREF,STRASG Allow 255 byte string. SETO @BUF

MC

CLR

RO

LI Rl,2 LI R2,BUF BLWP @STRREF DEC

Rl

BLWP @STRASG RT

BUF

BSS END

256

Set register 0 to 0. Get the second argument. This is where it goes. Get the string.

Return the string in 1st argument Send the string back now. Now return to the Basic program. Define buffer space.

Notice that SETO was used to put 255 into the first byte of BUF.

It does this

because

255 decimal

1111 1111, or all l's in binary. sets all

the

bits to

l's.

is FF in hex,

which is

And SETO does just that, it

Now of course it also

sets all

the

bits of the second byte of BUF to l's as well, but it probably won't matter what is in the second byte, since STRREF is going to copy data on top of the second byte anyway. It would of course also have been possible to use MOVB to move a byte containing 255 into BUF,

MAX

like this. MOVB

@MAX,@BUF

BYTE

255

The program with the above changes assembles with no errors What happens when it is called? IT WORKS! The string MC$ is set 92

_A Case History to "TEST" and is printed.

Of course,

it is a rather simple pro

gram, but nevertheless it is rewarding when it does finally work. Now it is time to make it more complicated and have it do

more than one string. Rather than trying to have it concatenate them, as the next step it would be nice to correctly determine

how many arguments there are, and then perhaps just copy the last one. To prove that just the last one is being copied, modify the Basic program to look like this. 100 REM 110

TEST MULTIPLE CONCATENATION

CALL INIT

120 CALL L0AD("DSK1.BSCSUP","DSK1.MCEA0BJ") 130 CALL LINK("MC",MC$,"FIRST","SECOND","THIRD") 140 PRINT MC$ 150

END

Incidentally, if this Basic program calls the current ver

sion of MC, MC$ will be set to FIRST. should be set

After MC is changed, MC$

to THIRD.

In order to have the assembly language program pick up just the last argument, it is necessary to know how many there are.

The description of LINK (both in the TI Editor/Assembler manual and in the Mini Memory Module manual) states that location >8312 contains the number of arguments. This fact can be verified from Basic by inserting these two statements into the Basic program above. The value 4 will be printed for NARGS. (Note that hex 8312 is decimal -31982.)

131 CALL PEEK(-31982,NARGS) 132 PRINT NARGS

Now to modify MC to use this same fact. version

This is the changed

of MC.

TITL 'MC - Multiple Concatenate' DEF

MC

MC

REF STRREF,STRASG SETO @BUF Allow 255 byte string. CLR R0 Set register 0 to 0.

CLR Rl MOVB @>8312,R1 SWPB Rl

BUF

LI

R2,BUF

BLWP DEC BLWP RT BSS

@STRREF Rl @STRASG

256

Prepare to move no. args into Rl. (Value now in left of register) (Value now in right of register) This is where it goes. Get the string. Return the string in 1st argument. Send the string back now. Now return to the Basic program. Define buffer space.

END

93

_A Case History Nai#

Well, this version didn't work. When the Basic program called it, the message "BAD ARGUMENT IN 130" appeared. Sever al appr opriat e

chan ge have

minutes of scouring the listing and rereading the parts of the TI manuals found nothing wrong with the

H owever, problems are not always where changes made. In this case, the DEC Rl looks a little my-

jus t made.

just been

Using it in the first place was a bad idea. The nohat regist er 1 had been set to 2, because STRREF was to se cond argum ent, and at this point in the program it

ster ious.

tion

get

was

the shou Id be

ment to

c

t

set to 1,

Sin ce 1 is 2 hange the 2 in t

because STRASG is to return the first argudecremented by 1, using DEC seemed a nice way he register into a 1.

If the DEC Rl is changed to LI Rl,l, the program works and A$ is set to THIRD. Another step toward the final version has been taken. Incidentally, don't miss the moral of this little story. Doing clever things can do more harm than good, and the temptation when writing assembly language to do clever things can be very strong. (Probably that SETO to put 255 into the first byte of BUF will come back to haunt me some day because it also changes the following byte.)

Now it is time to go for the final version of the program. It will be necessary to make two changes to the program. First, the number of arguments is to be used in a loop, instead of with the single call to STRREF. Second, the strings cannot be simply moved in and out of the same buffer, but each string must be first brought into an "input" buffer and then stuck onto the back Here's a end of whatever is already in the "output" buffer. visual presentation. The characters ABCD have already been copied into the output buffer.

Input string

"EFG"

Input buffer

Output buffer (before)

Output buffer (after)

ABCDEFG

Below is the modified program. I made two mistakes, which I am not going to show with separate listings. The first I real94

\^|F

_A Case History >^p/

ized before I assembled it: I forgot to change BUF to IN and to define OUT.

The second I did not realize until I assembled it

and got an error message, letting me know that I did not define the label END before using it in line 12.

TITL 'MC - Multiple Concatenate1 DEF

MC

TOP

MC

REF

STRREF,STRASG

CLR MOVB SWPB LI LI INC INC C JGT

R4

Prepare to move no. of args into R4

@>8312,R4

(Value now in left of register) (Value now in right of register) Register 3 will incr. across args.

R4

R3,l R7,0UT R7 R3

R3,R4 END

SETO @IN CLR RO MOV R3,R1 LI BLWP LI INC CLR MOVB

%^

JEQ MOV

END

IN OUT

R2,IN @STRREF R6,IN R6 R5

@IN,R5

SWPB

TOP R5

MOVB

*R6+,*R7+

DEC JGT JMP LI LI S DEC SWPB MOVB BLWP RT BSS BSS END

R5 MOV TOP

Rl,l R2,0UT R2,R7

Register 7 is pointer to next output byte position for moves. Loop across arguments. Test for last argument. Go if just did last argument. Allow 255 byte string on input. Set register 0 to 0.

Set register 1 to say which arg. This is where it goes. Get the string. Register 6 is pointer to next input byte position for move. Setup register 5 to be counter for move of string from IN to OUT. If zero length string, skip now. Put length in right of word. Move a byte from IN to OUT. Count

it.

Go back for the next Input string. Return the string in 1st argument. Compute total length in OUT. Subtract address of OUT

from

R7

address of next byte, then

R7

subtract 1 Put result

R7,@0UT @STRASG 256 256

more. in OUT.

Send the string back now. Now return to the Basic program. Define input buffer space. Define output buffer space.

This program returns FIRSTSECONDTHIRD. In other words, it works. Since a zero length string is a special case, I changed SECOND to a null string and tried it again. That returned FIRSTTHIRD, which is correct. Aside from the fact that the program lacks tests to make sure that the total length of the concatenated strings is not greater than 255 characters, it is now a

useable

routine.

95

A^ Case History

The big question now is how much speed will it earn for the Basic program which uses it. The figures are interestin;

I tried to find out why there was not a better increase in

speed and modified the Basic program to call MC with only one string.

170 CALL LINK("MC",MC$,"A")

This took 8 seconds to do 100 times, which should be compared to just 1 second for the equivalent simple Basic version. Obviously there is quite a bit of overhead associated with each CALL LINK approximately .08 seconds each time. Times

in

Seconds

for

100 Iterations

Time To Do

Time To Do

Time To Do

Single String

All 10 Strings

Last String

Using & for Concatenation

1

22

N/A

Link

8

19

17

for MC

Subtracting the 8 seconds (for the calls to LINK) from the 19 second total for

tual concatenation.

100 iterations leaves 11 seconds for the ac

Because I wanted to try to determine how

much of this 11 seconds was due to Basic preparing to call the subroutine and how much was in the subroutine itself, I tempor arily modified the subroutine by placing LI R4,2 after SWPB R4

(near the front of the program).

This makes the program only

return the first string, no matter how many are passed in the CALL LINK.

When I ran the program so it would only return the first string,

it took only 17 seconds instead of

19.

That means

that .02 seconds is required for the assembly language routine on each call with all 10 strings being passed to it but only one being returned.

Therefore, of the 11 second difference between

the 17 seconds required to pass 10 strings and the 8 seconds required to pass only one string, 2 seconds is used in the rou tine and the remaining 9 must be used by Basic in preparing the argument list for the CALL LINK. This is approximately .01 se cond per argument.

96

A^ Case History \te^ Perhaps a real increase in speed over Basic cannot be achieved in a case like this unless the number of arguments is

kept to a minimum.

The easiest way to keep the number of arguments to a minimum yet still refer to a number of strings is to use a string array. modified to

This is the Basic program after being

use such an array.

Notice the call to LINK in

statement 210; the S$() is the correct way to pass a string array to

a

subroutine.

100 REM TEST MULTIPLE CONCATENATION

110 DIM S$(ll) 120 CALL INIT

130 CALL L0AD("DSK1.BSCSUP","DSK1.MCEA0BJ") 140 FOR 1=1

TO

10

150 READ S$(I) 160 NEXT

I

170 DATA A,B,C,DEF,GHIJKL,MNOPQRSTUV,W,X,Y,Z 180 S$(11)=CHR$(0) 190 INPUT N 200 FOR 1=1

TO N

210 CALL LINK("MC",MC$,S$()) 220 NEXT

I

230 PRINT MC$ 240 END

Notice that S$ is dimensioned more than large enough to take all the strings to be copied into it. This is so that a special value can be used to mark the end of the data.

-That value,

a

null character, is assigned to S$(ll) in statement 180. This is an easy method of flagging the end of the data. (Another way of course would be to pass to the subroutine the value 10, or how ever many values are actually in the list. However, rather than

get into the issue of how to pass a number to the routine, it's easier for now to mark the end with a special string.).

Below is the listing of the assembly language routine after being modified to accept the string array. The listing actually went through two intermediate versions, which are not shown. Statement 13 was originally CI @IN,>0100. That did not work because the first operand for CI must be a register, and I had tried a memory reference. (This same restrictions applies to AI also.) After changing it to C @IN,@NUL and inserting a definition of NUL below in the program, it assembled. The value for NUL is >0100 because the length is 01 and the value is 00.

Trying to run it produced the message BAD ARGUMENT IN 210. Once again, the problem turned out to be someplace other than where a change had been made. Down at END is a sequence that passes the resulting string back to Basic. That code originally did not assign a value to register 0 because it still had the correct value (that is, 0) in the original program left over from the calls to STRREF. However, in the revised program, register 0 97

_A Case History Nj^r

gets values other than 0. Therefore, when a nonzero value was in register 0 and STRASG was called, it caused the error. The solution is simply to clear register 0 before calling STRASG. After this change is made, the program works.

TITL 'MC - Multiple Concatenate' DEF

MC

REF

STRREF,STRASG

MC

CLR LI INC

R3 R7,0UT R7

TOP

INC R3 SETO @IN

Loop across arguments. Allow 255 byte string on input.

MOV LI

Set register 0 to index of string. Set register 1 to 2 second arg.

MOV

END

NULL IN OUT

R3,R0 Rl,2

Register 3 will incr. across array Register 7 is pointer to next output byte position for moves.

LI R2,IN BLWP @STRREF

This is where it goes. Get the string.

C

@IN,@NULL

Quit if string was single null chr

JEQ

END

LI INC CLR MOVB JEQ SWPB

R6,IN R6 R5 @IN,R5 TOP R5

Register 6 is pointer to next input byte position for move. Setup register 5 to be counter for move of string from IN to OUT. If zero length string, skip now. Put length in right of word.

MOVB *R6+,*R7+

Move a byte from IN to OUT.

DEC JGT

R5 MOV

Count

JMP CLR LI LI S DEC

TOP RO Rl,l R2,0UT R2,R7 R7

Go back for the next input string. Return the string in the first argument. Compute total length in OUT. Subtract address of OUT from address of next byte, then

it.

SWPB R7

subract

MOVB BLWP RT DATA BSS BSS

Put result in OUT. Send the string back now. Now return to the Basic program. CHR$(0) Define input buffer space. Define output buffer space.

R7,@0UT @STRASG

>0100 256 256

1 more.

END

This program works and is twice as fast as the original Basic version. Instead of requiring 22 seconds for 100 iter ations, it only takes 11 seconds. This is close to what could be calculated from the above experiments: 8 seconds to CALL LINK, 2 seconds for the 2 arguments, and 2 seconds in the subroutine itself.

To make this work from Extended Basic, remove "DSK1.BSCSUP" from the call to LOAD in the Basic program. In the assembly lan98

A^ Case History guage program, replace the REF statement with the appropriate EQU statements:

>2010 for STRASG and >2014 for STRREF.

To make this work with the Dow Editor/Assembler, remove the call LOAD from the Basic program. In the assembly language pro gram, remove the TITL, DEF, REF, BSS, and END statements. Put

EQU statements at the end for STRREF (>604C), STRASG (>6048), and IN and OUT. Make an entry in the REF/DEF table for MC. Choose locations in CPU RAM for IN and OUT so that they are within the limits of the 4K RAM but beyond the program itself. They cannot

be assigned locations with BSS because of the size limitation on

each program segment with the Dow Editor/Assembler.

For example,

you might load the program at >7118 and put IN at >7200 and OUT at >7300. Finally, shorten all labels to three or fewer char acters, change commas to semicolons, and replace RT with B* Rll.

N^^'

99

Sorting

data.

One of the useful things a computer can do is rearrange One method for doing this is to put values into order.

This is called sorting.

To sort requires making many comparisons of values in a list. If the list is long, there are many many comparisons. Since Basic is slow, sorting in Basic can be painfully slow,

but Assembly language really shines when used for this type of task.

The programs in this chapter use a type of sort called a Shell sort. Although the programs are rather small, they are difficult to understand unless you work on it for a while. Here

is an example of how the algorithm works when sorting 22 random numbers.

1

31

2

3

4

5

6

7

8

9

10 11

12 13

14 15

16

41 59 26 53 23 23 54 34 93 28 49 39 28 39 11

17

18 19 20 21

22

39 83 29 84 32 01

The first step is to think of the list as 11 pairs, identi fied with the letter A-K,

A B C D

1 31

2

3

4

5

6

7

8

like this. 9

10

11

12 49

41

13

14

15

16

17

18

19 20 21

39

59

28 26

39

53

E F

11 23

39

23

G

83 54

H

29

34

I

84

93

J K

32 28

01

Now, make sure each pair is in order. This is the result. pairs B, C, E, H, J, and K had to be switched.)

A B

C D E

F G H I J K

1 31

2

22

3

4

5

6

7

8

9

10 11

12

(The

13 14 15 16 17 18 19 20 21

22

49

39

41 28

59

26

39 11

53 23

39 23

83

29

54 34

84 32

93

01

'\&0 28

100

Sorting

In the above operation, each pair is separated by 11 items, which is half the number of items to be sorted.

In the next

step, merge these pairs into five lists of four or five numbers, using an interval of 5 (which is 11 divided by 2). The lists are A-E.

A

1 31

2

3

4

5

6 23

9

10 11 01

29

28

C D

8

23

39

B

7

34

26

32

11

E

12 13 14 15 16 17 18 19 20 21 22 53 93 49 39 28 41 83 59 54 39 84

Next, sort these five lists. Do the first five pairs first. Start at the left, in row A, and compare items 1 and 6 (31 and 23). They are out of order, so switch them. Then go to row B

and compare items 2 and 7 (39 and 23). They are also out of or der, so switch them too. There is nothing to do for the pairs in the next three rows (3 and 8 in C, 4 and 9 in D, and 5 and 10 in E), since they are all in order. At this point, it looks like this.

A

1 23

2

3

4

5

6 31

9

10 11 01

12 13 14 15

16 53

49 41

29

28

C D E

8

39

23

B

7

34

26

59

32

11

39

17

18 19 20 21 22 93 39 28 83 54 84

Now go back to row A to compare the pair at 6 and 11 (31 and 01); the two values are out of order.

After making this switch,

move

to the left on line A and compare items 1 and 6 (23 and 01). Since these also are out of order, switch them. The first three items on line A are now 01, 23, and 31. Continue like this for

all the remaining numbers. This is the rule: whenever a pair is switched, continue to move across the same line to the left, switching again and again until a pair is in order. At that

point, go back to where you started switching and continue moVing to the right. 1 A B

C D E

2

3

4

It should look like this when done. 5

01

6 23

7

8

9 10 11

28

23

12 13 14 15 16 17 18 19 20 21 22

31

53

39 29

28

41 34

26 11

59

39

101

49 83

54

32

93

39

84

Sorting

The next step is to repeat the above procedure, stepping across by 2 (which is half of 5). These are the lists A and B before sorting. 1 2 3 4 A 01 28 B 23 26

5 11

6

7 28

23

8

9 10 11 34 31 29 32

12 13 14 15 16 17 18 19 20 21 22 41 39 39 59 93 39 54 53 83 84 49

Compare 01 and 28; they are in order, so do not switch. Compare 23 and 26; they also are in order, so do not switch. Compare 28 and 11; they are out of order, so switch, then compare 01 and 11 - they are in order, until

1 2 A 01 B 23

done.

3 11

4 23

It

5 28

so do not switch.

should

6 26

7 28

look

8 29

9 31

like

Continue in this manner

this.

10 11 12 13 14 15 16 17 18 19 20 21 22 34 39 39 41 59 93 32 39 49 53 54 83 84

These lists are again merged, and the process repeated with the interval of 1 (which is half of 2). The merged list is this. 1 2 01 23

3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 11 23 28 26 28 29 31 32 34 39 39 49 39 53 41 54 59 83 93 84

Compare 01 and 23; in order, do not switch. Compare 23 and 11; out of order, switch, then compare 01 and 11. Compare 23 (in

position 3, just moved from position 2) to 23 (in position 4); in order, do not switch. Compare 23 and 28, and so forth. the final list, now completely sorted.

This is

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 01 11 23 23 26 28 28 29 31 32 34 39 39 39 41 49 53 54 59 83 84 93

That is how the sort works, although why it works is beyond the scope of this book.

102

Sorting

Here is a Basic program which does this type of sort. Try The program it with small values of N just to see how it works.

prompts for N, then generates N random values. It tells you to press ENTER before starting the sort so you can time it. Stop timing when you see DONE. It will then print the sorted values. I timed it with N = 1000, and it took 598 seconds. 100 REM

SHELL SORT IN BASIC

110 DIM A(1000) 120 INPUT N 130 FOR 1=1

TO

N

140 A(I)=RND 150

NEXT

I

160 INPUT "PRESS ENTER TO START":T$ 170 REM START SORT 180 INTRVL=N

190 REM

(TOP)

200 INTRVLiINT(INTRVL/2) 210 IF INTKVL=0 THEN 380 220 ST0PVAL=N-INTRVL 230 P=l

240 REM

(NXT)

250 LPTR=P

%^f-

260 REM

(BCK)

270 RPTR=LPTR+INTRVL

280 290 300 310

IF A(LPTR)0 THEN 260

340 REM

(OK)

350 P=P+1

360 IF P>ST0PVAL THEN 190 370 GOTO 240

380 REM

(END)

390 REM

SORT COMPLETE

400 INPUT "D0NE":T$ 410 FOR

1=1

TO N

420 PRINT A(I) 430 NEXT I

The names in parentheses, such as (BCK) in statement 260, are the same as the labels which appear in the assembly language version below. Also, the logic of the sort is identical, so you can

compare this Basic program with the assembly language code. There are two small differences: the variables P and STOPVAL are identified as P0S and STOP in the assembly language routine.

%^s

103

Sorting

Here now is a Basic program to call such a sort, using the

Mini Memory Module and the Dow Editor/Assembler.

(The version of

the sort using Extended Basic will come later because it is

slightly more complicated.) This takes 78 seconds to sort 1000 values, which is almost 8 times faster than the Basic version listed above. When you press ENTER after it prints DONE, it will

not display the entire list but just display some values from middle of the list as well as the first and 100

REM TEST

110 120

DIM A(1000)

130 140 150 160 170 180 190 200

INPUT

CALL

210

FOR

last

values.

SORT SUBROUTINE

CLEAR N

1=1

TO

N

A(I)=RND NEXT

I

INPUT "PRESS ENTER TO START":T$ CALL LINK("S0RT",A(),N) $ INPUT "D0NE":T$ FOR I=N/2-10 TO N/2+10 PRINT A(I)

220

NEXT

230

PRINT A(1),A(N)

240 GOTO

I

130

This is the Dow Editor/Assembler version of the sort. The numbers in the left margin are mentioned in the explanation in the 1

2

3 4

text 000 002 006 00A 00E 012

that

follows.

CLR

RO

Rl;2 BLWP @REF

GET N;

2ND ARGUMENT

LI

FAC = N CONVERT N

CI

TO INTEGER @FAC;R2 R2;>C000 SUBTRACT EXPONENT = >4000 R2;>0100 IF RESULT < 0100;

MOV AI

THEN

R2

IS

ALREADY

=

N.

5 6

016

JLT

LI

018

AI

7

01C 020

MPY

R2;>FF00 R2 STILL TOO BIG. SUBTRACT 0100. @HUN;R2 MULTIPLY VALUE SO FAR BY 100.

CLR

R2

022

MOVB

@FA2;R2

8

9 10 11 12

026 SWPB A 028 02A LI :M0V 13 02E T0P:SRL 14 030 JEQ MOV 15 032 036 S 16 038 LI 17 03C NXT:M0V 18 03E BCK:M0V

19

040 042

A MOV

R2

NOW

GET SECOND BYTE.

ADD PRODUCT

FOR

FINAL

VALUE.

R3;R2

R2;@N R2;l

INTRVL=INTRVL/2

END

@N;R3 R2;R3 R8;l R8;R6 R2;R7 R6;R7 R6;R0

STOP=N-INTRVL P0S = 1 LPTR=P0S RPTR=LPTR+INTRVL

COMPARE A(LPTR) TO A(RPTR). 104

Sorting

^^K 044 048 20 04C 052 058 05E

21 22

23

24

25

26

LI

Rl;l

BLWP MOV

@REF

MOV MOV MOV

064 MOV BLWP 066 06A LI 06E LI 072 LI 076 L3 :C 078 JL 07A JNE 07C DEC 07E JGT 080 JMP 082 SWI :MOV 084 BLWP 088 MOV 08E MOV094 MOV 09A MOV 0A0 MOV 0A2 BLWP 0A6 S 0A8 JGT OAA OK :INC OAC C OAE JGT JMP OBO 0B2 END :B 0B4 HUN :DATA 0B6 N :BSS 0B8 TMP :BSS OBA TM2 :BSS OBC TM4 :BSS OBE TM6 :BSS OCO FAC :EQU 0C2 FA2 :EQU 0C4 FA4 :EQU 0C6 FA6 :EQU 0C8 REF :EQU OCA ASG :EQU

@FAC;@TMP @FA2;@TM2 @FA4;@TM4 @FA6;@TM6 R7;R0 @REF

R4;TMP R5;FAC R9;4 INITIALIZE TO LOOP *R4+;*R5+ COMPARE WORDS OK SWI R9 L3 OK

R6;R0

OUT OF ORDER.

SWITCH

@ASG

@TMP;@FAC @TM2;@FA2 @TM4;@FA4 @TM6;@FA6 R7;R0 @ASG

R2;R6

LPTR=LPTR-INTRVL

BCK R8

IF LPTR>0;

GO BACK

IN

MOVE TO

ORDER.

RIG

R8;R3 TOP NXT *R11 100 2 2 2 2

IF POS>STOP GO TO TOP. GO TO

NEXT.

TMP (8 BYTES)

2

>834A >834C >834E >8350 >6044 >6040

NUMREF NUMASG

105

Sorting v^p'

The registers are used as follows. RO Rl

At

for for

NUMREF NUMREF

and and

NUMASG NUMASG

R2

INTRVL: N/2, then half that, and half that, etc.

R3

STOP

R4 R5

pointer to A(LPTR) for comparison. pointer to A(RPTR) for comparison.

R6

LPTR

value

for

each

iteration

R7

RPTR

R8

POS

R9

miscellaneous loop counter

=

N

-

R2

1, call NUMREF to move the value of N into FAC.

Then,

at

through 12, convert it to an integer. Go back to the discussion in the chapter on calling from Basic to see what numbers look like internally. Remember that the only legitimate values of N for this subroutine would be between 0 and 1000 or so (and cer

tainly never a five digit value).

These values all require at

most 2 or 3 bytes when stored in Basic, a fact which makes it easy to convert them to integers for use in an assembly language program.

Move the first two bytes into register 2.

the exponent and must be either 64 or 65.

The first byte is

At 3, subtract 64

(which is >4000 in hex). The register should then hold >00xx or >01xx. So, at 4, compare to see which it is. If >00xx, the value has been converted, so go to LI (at 5). Otherwise, it is >01xx, so subtract off the >0100 at 6, then (at 7) multiply the

second byte (now left in the right half of the register) by 100. The product is now in register 3. At 8 through 10, move the third byte of FAC (which is the second byte of the value) into register 2 and flip to the right side. At 11, add the product (of the first byte and 100) into register 2. At 12,

move the converted value of N from register 2 into N.

Now, at 13, is the top of the sort loop. It has the label TOP. At this point, the list is essentially split into multiple lists, based on a new interval. In the examples of sorting above in this chapter, the. various sublists were shown on different lines for clarity, but of course in memory this is not actually done. The SRL R2;l computes the new value for the interval. The first value is N/2.

At 14, jump if equal to 0 to END. In other words, if the interval just computed by shifting is 0, the sort is done. Otherwise, at 15, set register 3 equal to the stopping value for the position as you scan across the list, checking pairs for proper order; STOP = N - INTRVL. For example, in the example in this chapter, on the first iteration the interval is 11, so the 106

Sorting

last possible value for the left member of a pair is 22 - 11 = 11. For the second iteration, the last possible value is 22 - 5 = 17. = 20. =

For the next iteration, the last possible value is 22 - 2 And obviously, on the last iteration, the value is 22 - 1

21.

Now, at 16, start the scan at the left by setting register 8 (POS) to 1.

2

At 17 the label is NXT. Here prepare to compare the items at LPTR and RPTR. LPTR is the pointer to the left value of the

pair, and RPTR is the value on the right.

LPTR, register 6, is

initially set to POS.

At 18, compute RPTR = LPTR + INTRVL. That is, register 7 is the sum of register 6 and 2. To actually compare the values at LPTR and RPTR, at 19 move LPTR into register 0, set register 1, and call NUMREF. Because LPTR is in register 0, this moves A(LPTR) into FAC. At 20, move the eight byte value from FAC into TMP. This is done with four MOV instructions. Because the Dow Editor/Assem bler does not allow addresses such as FAC+2, it is necessary to

k^

define symbols such as FA2 with EQU directives. for TMP, TM2, eight bytes.

and so forth.

The same is done

Four MOV instructions will move the

At 21, move RPTR (register 7) into register 0 and call NUMREF again to get A(RPTR) in FAC. Now, at 22, both values are available. Make register 4 point to TMP and register 5 point to FAC. Initialize register 9 to be 4, then loop to compare four words. The top of the loop is at

*

the label L3 and the bottom is at the statement JGT L3.

If

the first value is less than the second, the numbers are in order and the JL will cause a transfer to OK. (Because this is JL ra ther than JLT, the sort is a logical rather than a numeric

sort. That is negative values will not sort before positive val ues.) If the values are not equal, the first must be greater than the second, so transfer to SWI to switch them. Otherwise, decre ment register 9 and jump back to L3 if register 9 is still great er than 0. Finally, if the two numbers were the same for all four words,

go to OK.

At 23, switch the two values. This is done by calling NUMASG to store them back into A. Reverse the order of register 6

and

7

to switch the

values.

At 24, because the values were out of order and had to be switched, it is necessary to move to the left in the list. Therefore, subtract INTRVL (register 2) from LPTR (register 6). If this results in a value still within the list (i.e., greater 107

Sorting

than 0), go to BCK to compare again. At 25, move POS one step to the right. If POS is now greater than STOP, the iteration has been completed, so go to TOP.

Otherwise,

go to NXT go compare pairs some more.

At 26, the program has finished, so return to Basic. Next comes the definition of HUN, which is the value 100; this is used

for multiplication during conversion of N. Then come TMP, TM2, TM4, and TM6 (eight bytes to hold a value during comparison). After that are the EQU statements for FAC and so forth. Notice that the conversion logic does not test for N = 0, Also, you may and the program will do bad things in this case. be able to make this routine faster by not using NUMASG to store both members of the pair after switching, since one is to be used immediately in the next comparison.

From the sort programs shown above in both Basic bly language, you can see the sort algorithm itself. another version which uses exactly the same algorithm relies on the use of the 32K memory expansion. It is

and assem Here is but which shown for use with Extended Basic, since it depends on the numerical array being stored in the 32K memory.

The reason that the memory expansion is required is that the program can directly access the array in that memory, without having to send data back and forth between VDP RAM and CPU RAM. Even though this transfer can be done conveniently with the util ity routines NUMREF and NUMASG, it is rather slow. Because of this change in the logic, the program can sort 1000 values in just 5 seconds. This is 15 times faster than the previous ver sion, and 120 times faster than the Basic version. (It is capab le of sorting 3000 values in just 20 seconds. Here is the Basic program to call the modified assembly language sort routine. 100

REM TEST XBS0RT

SUBROUTINE

110 DIM A(3000) 120 CALL

CLEAR

121 CALL INIT :: CALL L0AD("DSK1.XBS0RT0BJ") 130 140

INPUT N FOR 1=1

TO

N

150 A(I)=RND 160

NEXT

170 180 190 200 210

INPUT "PRESS ENTER TO START":T$ CALL LINK("XBS0RT",A(),N) INPUT "D0NE":T$ FOR I=N/2-10 TO N/2+10 PRINT A(I)

I

220

NEXT

I 108

'\^0r

Sorting

230 PRINT A(I),A(N) 240

GOTO

130

By comparison to the Basic program which calls the Mini Memory sort routine, note the addition of statement 121 and the change of

SORT

to

XBSORT

in

statement

180.

Here is the sort routine modified to use the 32K memory expansion. The numbers in the left margin are used in the text below.

TITL

'XBSORT'

CALL LINK("XBS0RT",A(),N)

* *

*

*

REGISTER USAGE: R0 TEMP FOR SUBSCRIPTS ETC N/2 ETC R2 INTRVL N-INTRVL R3 STOP R4 POINTER TO A(LPTR) R5 POINTER TO A(RPTR) R6 LPTR R7 RPTR R8 POS R9 MISCELLANEOUS LOOP COUNTER RIO BASE ADDRESS FOR A ()

FAC NUMREF VMBR

EQU EQU EQU

DEF XBSORT LI

A LI LI

>834A >200C >202C XBSORT

R0,16 @>8310,R0 Rl,STACK R2,8

GET R1=BASE ADDRESS FOR A() C(8310)+16 POINTS TO STACK ENTRY IN

VDP

MEM.

BLWP @VMBR

MOV LI

@STACK+4,R0 R2,4

LI R1,DATA BLWP @VMBR MOV @DATA+2,R10

Nl^/

BYTES

4&5

POINT TO

ARRAY

IN

VDP.

VDP HAS 2 BYTES ARRAY SIZE, FOLLOWED

BY

ADDR

OF DATA

RIO NOW POINTS TO A(0) GET N, 2ND ARGUMENT

IN

EXP

MEM

IN EXP MEM.

CLR

RO

LI BLWP MOV AI CI

Rl,2 @NUMREF

JLT

LI

THEN

AI MPY

R2,>FF00 @V100,R2

R2 STILL TOO BIG. SUBTRACT 0100. MULTIPLY VALUE SO FAR BY 100.

CLR

R2

NOW

@FAC,R2 R2,>C000 R2,>0100

FAC=N. CONVERT

N

TO

INTEGER

SUBTRACT EXPONENT =

>4000.

IF RESULT < 0100, R2

GET

.109

IS

ALREADY

SECOND

=

BYTE.

N.

Sorting

MOVB

@FAC+2,R2

SWPB

R2

A

R3,R2 R2,@N R2,l

LI

MOV

TOP

SRL

DONE

@N,R3 R2,R3

STOP=N-INTRVL

R8,l R8,R6 R2,R7 R6,R7

P0S=1 LPTR=POS RPTR=LPTR+INTRVL

R6,R4 R4,3 R10,R4

COMPARE A(LPTR) TO A(RPTR).

LI MOV

MOV

SLA A MOV SLA A

LI

10

CB JL JNE

DEC JGT JMP SWITCH MOV SLA A

MOV

11

12

L4

13

OKAY

SLA A LI MOVB MOVB MOVB DEC JGT S JGT INC C JGT JMP

DONE

B

V100

DATA

N

BSS BSS BSS END

STACK DATA

INTRVL=INTRVL/2

JEQ

A MOV

L3

VALUE

MOV

S NEXT GOBACK

ADD PRODUCT FOR FINAL

R7,R5 R5,3 R10,R5 R9,8

*R4+,*R5+

INITIALIZE TO LOOP. COMPARE BYTES

OKAY SWITCH

R9 L3 OKAY

R6,R4 R4,3 R10,R4 R7,R5 R5,3 R10,R5 R9,8

*R4,R0 *R5,*R4+ R0,*R5+

A(RPTR)

> A(LPTR)

SWITCH THE

VALUES.

INITIALIZE TO LOOP. SWITCH BYTES

R9 L4

R2,R6

LPTR=LPTR-INTRVL

GOBACK

IF LPTR>0 GO TO GOBACK

R8

A(LPTR) < A(RPTR). SET P0S=P0S+1.

R8,R3 IF POS>STOP GO TO TOP

TOP NEXT

GO TO

*R11 100 2 8 4

8 BYTE STACK VALUE FOR A() A() DIM & PTR TO EXP MEMORY.

NEXT

This assembly language program differs from the Dow Edit

or/Assembler version above in the standard ways discussed already 110

Sorting

in this book.

For instance,

there are a number of comment lines

at the beginning of this version.

Also, the EQU value NUMREF is

different.

However, the significant difference begins at 1. Extended Basic stores numerical data, such as the array A, in the 32K mem ory expansion. The first thing this program does is find out where it is stored in this memory. Page 278 of the TI Editor/ Assembler manual, while describing LINK, says that location >8310 in CPU RAM contains the value stack pointer. (See also Appendix A of this book.) Unfortunately, that statement is rather too cryptic. However, I determined by experimentation and partially disassembling the ROM for NUMREF that in fact the contents 'of that location plus 16 does indeed point to an 8-byte pointer for the first argument to LINK. Therefore,

at 1, the contents of locations >8310 and >8311

are added to the value 16 in register 0. used in a call to VMBR, at 2.

The resulting sum is

VMBR is a utility routine which

passes data from VDP memory to CPU memory; it is described on

page 249 of the TI Editor/Assembler manual and on page 36 of the Mini Memory manual. To use it, register 0 must contain the address in VDP memory from which data is to be read. Register 1 contains the address where it is to be sent (STACK in this case).

And register 2 contains the number of bytes to transfer (8 in this case). After ca lling VMBR, STACK S tack val ues are described

holds the 8-byte stac k

value for Editor/Asse mbler manual

in the TI 280 a nd on pages 27 and 28 of the Mini Memor y manual, For a numeric array, bytes 4 and 5 are the address i n VDP memory The manual neglects to p oint out that where the arr ay is stored. if Ex tended B asic is in use, the array is not actual ly in VDP memor y. Howe ver, VDP memory at that location does c ontain size So, at 3, inf or mation f ollowed by a pointer into CPU memory, the poin ter (to VDP memory) into register 0 and then call move A.

on

pa ge

At 4, the thi rd and fourth again to read 4 bytes into DATA. bytes thus ob tained are the address of A in CPU memo ry; this addre ss is mo ved into register 10 for later use.

VMBR

At 5, the program obtains N and converts it to an integer, just as in the other version.

At 6, the programs differ. The other version had to pull A(LPTR) and A(RPTR) from VDP memory into FAC, using NUMREF. This version compares the two values while they are actually in the array, and exchanges them in place. To do this, it needs to add the value of LPTR to the address in register 10 to come up with x^^

the actual address in CPU memory of A(LPTR). At 6, register 6 (LPTR) is moved into register 4, then shifted left 3 bits. This shift has the effect of multiplying it by 8. This needs to be done because there are 8 bytes per numeric value in the array. Ill

Sorting

Next, register 10 is added to register 4, thus completing the computation of the actual address of A(LPTR). A similar comput ation is performed for A(RPTR)

At 7, the same type of computation is carried out for RPTR, using registers 7 and 5. Now, at 8, the two 8-byte quantities need to be compared. Register 9 is loaded with the value 8 to compare the 8 bytes. Notice that the other assembly language program was able to com pare 4 words, while this one needs to compare 8 bytes. The other one could compare words because the values were moved into CPU

memory at addresses known to be even. However, Extended Basic does not necessarily store numeric data at even addresses, so byte instructions must be used.

Starting at 9, the comparison logic is the same as the other version.

However, at 10 there is again a difference. As before, re gister 4 is computed to point to A(LPTR), and at 11, register 5 is made to point to A(RPTR). These two 8-byte values in A now need to be exchanged. At 12, there is a loop which exchanges them byte by byte, using register 0 as temporary storage. The MOVB *R4,R0 moves one byte from A(LPTR) to register 0; the MOVB *R5,*R4+ moves the corresponding byte from A(RPTR) into A(LPTR) and increments register 4 to point to the next byte of A(LPTR); and MOVB R0,*R5+ moves the original byte from temporary storage in register 0 into A(RPTR) while simultaneously incre menting the memory pointer for A(RPTR).

The rest of the logic is the same, starting at 13.

vj^Sf

112

Preparing To Sort Names

In the last chapter is a powerful sort routine. It sorts numbers because that is more efficient that sorting strings, but often you want to sort strings instead. For instance, you might want to write a program of your own to create an alphabetical listing of names. This chapter describes an assembly language routine that will pack up to nine letters into one Basic number. You can use it to alphabetize on the first nine characters of the name. In addition to the nine letters, it also packs a reference number in the Basic number so that you can retrieve the original data after sorting.

From the chapter on calling from Basic programs, you should realize that you can in fact store any set of eight bytes you want in a number in Basic. It is possible to compress three letters into two bytes. If the first six bytes are used for letters, that enables the program to pack nine letters, with two bytes left over. The two remaining bytes can be used to hold a pointer to the original data. This is how to use such a routine for alphabetizing.

Sup

pose you have a RELATIVE file with 1,000 names to be sorted. N||^'

Write a program to read through the file. As each record is read, the name is pulled out of the record with SEG$ and is placed into NAME$. The routine PACKNM is then called to compress both the name and the record number,

REC,

into PACKED.

Then,

PACKED is stored in an array at a position corresponding to the record number.

After all records have been read and the names

packed, a sort routine cycles down the sorted list, using the compressed record number to retrieve the name from the file for printing.

Here is a simple Extended Basic program which demonstrates the essential steps of this kind of sorting. Since it is just a

simple program to demonstrate the technique, it does not read the data from a disk file but accepts it from the keyboard, and it

keeps the data in a string array to be printed after sorting. 100 REM GENERAL PURPOSE SORT PROGRAM

110 DIM CVEC(100),NAMES$(100) 120 CALL CLEAR 130 CALL INIT

140 CALL L0AD("DSK1.PACKNM0BJ","DSK1.XBS0RT0BJ") 150 PRINT "ENTER NAMES."

160 PRINT "(ENTER 'END' WHEN DONE)" 170 LINPUT NAME$ 180 IF NAME$="END" THEN 240 190 N=N+1

200 NAMES$(N)=NAME$ 210 CALL LINK("PACKNM",NAME$,N,PACKED) 220 CVEC(N)=PACKED 113

Preparing To Sort Names

230 GOTO 170 240 REM ALL DATA

HAS

BEEN READ.

NOW SORT.

250 CALL LINK("XBSORT",CVEC(),N) 260 FOR

1=1

TO

N

270 PACKED=CVEC(I)

280 CALL LINK("UNPKNM",PACKED,J) 290 PRINT NAMES$(J) 300

NEXT

310

END

I

Statement 210 passes the name in NAME$ and the 'record num ber'

in N to the routine, and the compressed name and number are

returned in PACKED.

packed values.

Statement 250 sorts CVEC,

which has the N

Statement 280 gets the record number out of

PACKED so it can be used to print the strings in order.

The listing of the assembly language routines starts on the next page. They make up a fairly large program, which should not be written all at once. The sequence I went through to write them was something like this.

First, I simply passed the string and the record number to I converted the record number to an integer and returned it in the first two bytes of PACKED. I checked this carefully

PACKNM.

before continuing.

Next, I enhanced the routine to move the record number to

the last two bytes of PACKED. "A", "b" to "B", and so forth.

I also put in logic to map "a" to Characters other than a-z and A-Z

were ignored, although a comma was recognized to mean the end of the name. Characters still accepted were moved into TEMP (a holding area within the program). Finally, three words from TEMP were copied into the first three words (six bytes) of PACKED. After that worked, I added the code to pack three characters

into one word.

This packing is done by mapping "A" to 1, "B" to

2, and so forth. Since "Z" maps to 26, legitimate values range from 1 to 26. They are packed by multiplying them by powers of 27 and then adding. (The actual algorithm alternates between multplying and adding.) This transforms three letters into a

one-word integer value. the value is

1 *

27 *

For example, if the letters are "AAA",

27 +

1 *

27 +

1 =

757.

If the letters are

"ZZZ", the number is 26 * 27 * 27 + 26 * 27 + 26 = 19,862. Since the largest possible value for a one-word integer is 32,767, this technique will not cause any overflow during multiplication. Finally, I changed it again to map the comma to 0 and to This causes the program

continue with the name past the comma. to sort on as much of

both the

last name and

first name as will

fit within the nine character limit. Instead of stopping when it hits a comma, it now stops either at the end of the string NAME$ or after packing nine characters. 114

J

Preparing To Sort Names

The numbers along the left margin of the listing are men tioned in the text which follows the listing. SUBROUTINE

PACKNM

CALL LINK("PACKNM",NAME$,N,PACKED) CALL LINK("UNPKNM",PACKED,N) CALL LINK("UNPKST",PACKED$,N)

*

* #

#

PACKNM * * #

*

a string with up to 36 characters in NAME$ number from 0 to 9999 in N and packs them into PACKED. Up to 9 characters of the string are packed into the first 6 bytes of PACKED, followed by the value N in the last two bytes. Only the characters A-Z and a-z and comma are packed, and

takes and a

A and a are treated alike,

#

sorts

before

B and b,

etc.

The comma

A.

*

*

UNPKNM returns to Basic the value N that was packed into the last two bytes of PACKED.

*

*

UNPKST

*

%*^

is

the

is

a string.

same

as

UNPKNM except that the input

*

jjc j{e 5je sj: # ^ * # * s}: # # 5jc # sjc # sje sje j}: * # sjt s}c * s}: 5jc jjc # # # sje sjc jjc # j}s # sfc s}: * * # # * # jjc # * # * jjt sfc sje 5jc jje # # # * sje # *

FAC STRREF NUMREF NUMASG COMMA

EQU EQU EQU EQU EQU

>834A >2014 >200C >2008

>2C00

=44=","

PACKNM,UNPKNM,UNPKST DEF PACKNM MOVB @DB36,@NAME Get NAME$ in NAME. CLR

R0

LI LI BLWP LI BLWP MOV JEQ AI CI

Rl,l R2,NAME @STRREF Rl,2 @NUMREF @FAC,R0 NOSCAL R0,>C000 R0,>0100

JLT

NOSCAL

AI MPY

R0,>FF00 @V100,R0

CLR

RO

Get

N

in FAC.

Convert floating-point to integer. Done immediately if = 0. Subtract exponent >4000. Test for power of 100. Jump if power of 100 is 0. Subtract

>0100.

Scale by 100. Now get second byte.

MOVB @FAC+2,R0 SWPB 10 11

A NOSCAL MOV

RO

R1,R0 R0,@FAC+6

Add product for final value. Put integer value in FAC+6. 115

Preparing To Sort Names

12

LI CLR

RO.NAME R3

RO points to NAME Set R3 to length of name.

MOVB *R0,R3

13 LP

INC

RO

SWPB

R3

A

R0,R3

R3 points just past end of name

LI CLR

Rl,16 @TEMP(R1)

Clear TEMP.

DECT JOC

Rl LP

CLR 14 NXTBYT C JEQ 15 CLR

Rl R0,R3 NOMORE R2

16

MOVB *R0+,R2

17

CI

R2,COMMA

JEQ

ANOTHR

18

RO points to first char of name

Rl = @ words in TEMP so far. Quit if no more name. Get next char from NAME.

Change comma into 0.

SWPB R2

AI CI

19 SKIP

20

R2,-64 R2,27

Map A to 1, B to 2, etc. Test for too large.

JLT

SKIP

AI

R2,-32

Scale a to A, b to B, etc.

MOV JLT

R2,R2 NXTBYT

Too small - ignore.

JEQ

NXTBYT

Too small - ignore.

CI JGT MOV

R2,26 NXTBYT Too large - ignore. R2,@TEMP(R1)

21 ANOTHR INCT Rl CI

Count another value in TEMP.

Rl,18

JLT 22 NOMORE CLR

NXTBYT R3

Keep on if not yet 9. R3 is index into FAC=0,2,4.

23 LP1

MOV

R3,R2

R2 is index into TEMP=0,6,12.

A A

R3,R2 R3,R2

MOV MPY

@TEMP(R2),R0 Combine each 3 words in TEMP @V27,R0 into each 1 word of FAC.

24

25

26 27

MOV

R1,R0

A MPY A MOV

@TEMP+2(R2),R0 @V27,R0 @TEMP+4(R2),R1 R1,@FAC(R3)

INCT R3

28

CI

R3,6

JLT

LP1

CLR

RO

Test for end of loop. All done.

Return PACKED.

LI Rl,3 BLWP @NUMASG RT *

29 UNPKST CLR LI

RO Rl,l

Get PACKED$.

116

Preparing To Sort Names

LI MOVB BLWP MOVB MOVB MOV JMP

COMMON

32

33

JMP AI MOV

34 LARGE

35

@STRREF

@TEMP+7,@TEMP @TEMP+8,@TEMP+1 @TEMP,R1

COMMON RO CLR Rl,l LI BLWP @NUMREF MOV @FAC+6,R1 RO CLR R2,FAC LI R0,*R2+ MOV R0,*R2+ MOV R0,*R2+ MOV R0,*R2 MOV DIV @V100,R0 MOV RO,RO LARGE JNE Rl,>4000 AI MOV R1,@FAC

30 UNPKNM

31

R2,TEMP @DB8,@TEMP

SWPB MOV CLR

RETN

LI BLWP

Get

PACKED.

Rl=integer at end of FAC. Set FAC to O's.

Divide integer by 100. See if quotient is 0. Jump if >99. Value

is less than

Value

is

100.

RETN

R0,>4100 RO,@FAC

>99.

Rl

Rl,@FAC+2 RO Rl

Return value in STRING$ 2

@NUMASG

RT

36

NAME

BSS

Buffer

37

TEMP

BSS

38

DB2 DB8 DB36 V100 V27

Temp workspace. 9 chars (words). String length returned.

38 18 BYTE 2 BYTE 8 BYTE 36 DATA 100 DATA 27 END

for

NAME.

Max string length for NAME. Value 100 for scaling integers. Value 27 for packing into FAC.

At 1, t he string is obtained from the first argument in the In this version of the program, up to 36 charac-

call

to LINK

This allows for a long name. Also, if the he first part of the record, it is not necessary to use SEG$ to separate it from the rest of the record, since the routine will stop after packing nine characters even if the rest of the recor d contains data which could be mistaken for part of ters

are

name

is

the

ace eptable.

in

name.

er. ment.

At 2, The

t

B

y not having to call SEG$, the program will run fast-

t he record number is obtained from the second argur outines

on calling f rom

for doing this were described in the chapter

Basic.

117

Preparing To Sort Names

At 3, the program moves the first word of the number into

register 0 to begin converting it to an integer. There is no problem with a word move instruction here (rather than two byte move instructions) because FAC and registers are both known to have

even

addresses.

Since the value 0 is represented in Basic by O's in the first two bytes of FAC, at 4 a JEQ following the MOV is all that is needed to test for 0; if it is 0, go to NOSCAL ("no scaling

needed") to use 0 as the converted value.

Register 0 already

contains 0; at NOSCAL, it is moved from register 0 into the last two bytes of FAC.

If the value is not zero, there is a bias of 64 (hex >40) in the exponent, and this has to be subtracted. Therefore, at 5, >C000 is added to register 0. (The value >C000 is the two's complement of >4000.) Given the range of numbers that can be converted (0 'through 9999), the exponent must be 64 or 65. Therefore, after subtracting, it must be either 0 or 1. That is, the first word of FAC must now be either >00xx or >01xx. At 6, go to NOSCAL Xi the left byte is less than 1 (that is, if it is

0). In this case, because the value is between 1 and 99, having subtracted off the exponent, the first two bytes contain >00xx, where xx is the number itself,

so it can now be stored in FAC.

At 7, the number was larger than 99, so the values in two bytes have to be added together. Remove the exponent from the

word (it is the left byte and is equal to 1) by subtracting >0100. Then multiply by 100 at 8, which leaves the product in registers 0 and 1. At 9, clear register 0, move the third byte into the left half of the register with MOVB, then switch it to the right half with SWPB. At 10, add the product of the first byte and 100 to the third byte now in register 0, thus completing the

conversion.

At 11, move the converted integer from register 0 into the last two bytes of FAC, in preparation for being returned to the Basic program.

At 12, compute the address of the first byte past the end of the input string. Make register 0 point to the first byte, then move the byte (since it contains the length of the string) into the right half of register 3, add 1 to the address in register 0, and then add the address to the length in register 3. Now, at 13, loop to clear all 18 bytes of TEMP. TEMP has nine words, to hold nine characters temporarily (one character per word to make arithmetic easy). TEMP is defined at 37. Also, initialize register 1 to keep track of the number of characters that

have

been moved

into TEMP.

At 14 is the top of the main loop. 118

It is identified by the

Preparing To Sort Names

label NXTBYT. Remember that register 3 was computed to be the address of the byte just past the end of the string. Register 0 was left holding the address of the first byte in t»he string. At 16, register 0 is incremented each time through the loop. Thus,

the comparison at 14 is executed each time through the loop. It will be equal when register 0 has been incremented enough to equal register 3. (If the input string is empty, the test will succeed on the first attempt.) At 15 and 16, move the next character into the left half of

register 2, making sure the right half is O's. At 17, test the character to see if it is a comma. Note the use of the symbol COMMA, which is not an address but the value

>4400, which is the character ",". Jump to ANOTHR if it is a comma. (Because TEMP was filled with O's initially, this means that each comma is represented by 0 in temp.) At 18, move the character into the right half of the word,

then subtract 64 from it to map "A" into 1, "B" into 2, and so forth. Compare the result to 27; if less than 27, it is okay. Otherwise, case

subtract another 32 in case the character was a lower

letter.

At 19, move register 2 to itself - this is a means of com paring it to 0. If the register is less than 0 or equal to 0, ignore it by jumping to NXTBYT. At 20, see if the value is not greater than 26. Jump to NXTBYT if it is greater than 26. If not, move the word (now a value between 1 and 26) into TEMP (indexed by register 1). The bottom of the loop is at 21. Increment register 1 to count another value, moved into TEMP (or a comma). Check to see

if register 1 is now 18; if not yet 18, continue to process the input string by going back to NXTBYT. The compressing of the nine characters into three words be gins at 22. Register 3 will loop across the three words of FAC, which get the results.

The top of the compression loop is at 23. Move register 3 into register 2, then add it to register 2 twice more. This has the effect of setting register 2 to three times the value in re gister 3. (It is actually easier to add several times than it would be to multiply.)

>l^/

At 24, move a word value from TEMP (indexed by register 2) into register 0. Multiply by 27; V27 is the value 27, defined at 38. Move the product from register 1 back to register 0. At 25, add the next value from TEMP into register 0. Multiply the sum by 27, and add the third value from TEMP into register 1 as well. 119

Preparing To Sort Names

At 26, move the combined value into FAC,

indexed by register 3.

The bottom of the compression loop is at 27.

Increment re

gister 3 to move along FAC, and test to see if it is six yet. still less than six,

If

go back to LP1 to continue compressing.

At 28, call NUMASG to return the value in FAC to the third argument in the call to LINK. Then return.

The UNPKST entry point is at 29. It is used if the packed data is passed as a string, rather than as a number. It calls STRREF to get an eight character string. Then it moves the two bytes from the string into register 1. The UNPKNM entry point is at 30. It uses NUMREF to get the number, then moves the integer value at the end of it into register 1. Whether UNPKST or UNPKNM was called, at 31 the integer has

been moved into register 1.

It needs to be converted into the eight character form Basic uses. First, set all eight bytes of FAC to O's. This is done by clearing register 0, setting regis ter 2 to point to FAC, and then executing four MOV's of register 0 (which contains 0) indirect of register 2. Each move incre ments register 2, thus moving along FAC automatically.

At 32, register 0 and 1 (combined) are divided by 100. The quotient is tested for 0; if not 0, the value is larger than 100, so go to LARGE.

At 33, the value is less than 100, so add in the exponent of 64 (hex >4000), put the result in FAC, and go to RETN to return. At 34, because the value was larger than 100, add the expo nent of 65 (hex >4100) to the quotient and put the sum into the first two bytes of FAC. Then move the remainder from the right half of register 1 into the third byte of FAC.

Finally, at 35, FAC is passed back to Basic as the second argument.

At 36, various symbols are defined and memory space is allocated. NAME, for instance, is given 38 bytes. It needs 36 bytes to hold a string that long; it needs an extra byte to hold the length byte at the front; and the 38th byte is to make it even length.

The Extended Basic program can be converted for use with the TI Editor/Assembler and Basic by including DSK1.BSCSUP in state ment 140, changing XBSORT to SORT in 140 and 250, and changing LINPUT to INPUT in 170. For use with the Dow Editor/Assembler, also

delete

statements

130 and

140. 120

Interrupts, Screen, and Keyboard

The use of interrupts greatly enhances the utility of com puters. Without interrupts, a CPU (central processing unit) could in practice only handle one task.

To handle more than one

task, it would be necessary to write into each program frequent points at which the computer would check to see if something else needed to be done. To make this point clearer, try the following Basic program. (If you do not have Extended Basic, omit the CALL SPRITE; the CALL SOUND alone will provide the demonstration.) 100 REM DEMONSTRATE 110 CALL CLEAR

INTERRUPTS

120 CALL SPRITE(#1,97,2,20,20,10,10):: CALL S0UND(1000,500,0)

The program will clear the screen, generate a tone, then put a lower case "a" in the upper left corner and move it towards the lower right corner. The "a" is of course a sprite. Now change the program as shown below to call the small assembly language routine, also shown below.

100 REM DEMONSTRATE INTERRUPTS

110 CALL INIT :: CALL L0AD("DSK1.HANGUP0BJ") 120 CALL CLEAR

130 CALL SPRITE(#1,97,2,20,20,10,10):: CALL S0UND(1000,500,0) 140 CALL LINK("HANGUP") DEF HANGUP JMP END

HANGUP HANGUP

Calling this version of the program demonstrates dramatical ly the importance of interrupts. When you run it, the screen

will clear, the tone will start, and the "a" will appear, just as before.

However,

the sound will never end and the "a" will not

move. Furthermore, the computer will be locked up. The only way to regain control is to turn it off. Pressing CLEAR and even pressing QUIT has no effect. There are two reasons why the little assembly language rou tine can tie up the computer.

The first is that the Basic inter

preter turns off interrupts before calling the assembly language routine. The second is that the assembly language routine does not return control to Basic. Taken together, these facts mean that the computer will no longer process interrupts. Without in terrupts it cannot move the sprite, stop the sound, or check the keyboard to see if you are pressing CLEAR or QUIT.

121

Interrupts,

Screen,

and Keyboard

You can further prove the importance of interrupts by substituting this assembly language program. DEF

HANGUP

LIMI LIMI JMP

HANGUP 2 0 HANGUP

END

In this routine,

the LIMI 2 instruction enables inter

rupts, and the LIMI 0 immediately disables interrupts.

(Programmers also talk about "unmasking" and "masking" inter rupts.) If an interrupt is disabled or masked, it cannot be "ser viced" by the CPU. serviced. be

However, as soon as it is unmasked, it is

In other words, an interrupt will wait indefinitely to

serviced.

A clock in the computer causes the interrupts 60 times per second. Unless the interrupts are masked, the CPU does certain bookkeeping tasks each time. These tasks include tending to

sprites and sound generation and checking to see if you are pressing QUIT.

To understand the practical significance of interrupts, even on a home computer which only has to pay attention to one person

at a time, imagine writing a complicated and fairly time consum ing program so that it would check all the time to see if the user is holding down a key to stop the program. How much simpler it is to have a clock get the computer's attention on a regular basis, without our having to think about it. appens when there is an interrupt that is not masked (or when an interrupt that has already occured is ultiControl jumps out of your program to a special mately unmasked), Starting location, determine d by the designers of the computer. This

at

that

is

what

location

h

is

a

routine

which

determines

what

caused

the

This routine is called an interrupt and respo nds to it properly. For instance, as stated above, if the interrupt handler. interrupt was the c lock, the routine checks the keyboard to see On a larger computer, if you are holding down the QUIT key. there are many thin gs that can cause interrupts, including printers and termin als. You need to know about interrupts only if you need to have them enabled for sprite movement, for sound, or to allow the user to break out.of the program. If you do enable them, you have to The best technique is to have the be careful how you do it. LIMI 0 come immediately after the LIMI 2 at a place in the pro gram that is executed fairly often. That is, unmask them fre quently, but do not leave them unmasked all the time.

122

Interrupts, Screen,

and Keyboard

The reason you do not want to leave them unmasked all the time is that in that case you have no idea when an interrupt will occur. Due to the manner in which VDP and GROM are accessed, a program should not be interrupted during the transfer of data between either of these memories and CPU memory. If the inter rupt processor also uses these other memories, the hardware will

"forget" your location in the memory and transfer data to or from the wrong place. This can have disastrous consequences for your program.

There is no harm in executing the LIMI 2 and LIMI 0 instruc

tions if there is no interrupt pending. The TI Editor/Assembler manual suggests that a good time to allow interrupts is at the same point in your program where you are checking the keyboard to

see if the user is pressing a key.

(This would be done fairly

frequently if you were writing some kind of game or simulation program.) Screen manipulation is quite different in assembly lan guage than in Basic. For example, there is no simple way to clear the screen, as Basic does it with just one statement - CALL CLEAR.

There

is no PRINT statement which causes

the

screen to

scroll automatically. Furthermore, in a Basic program you put data on the screen using subroutines such as HCHAR, VCHAR, and (in Extended Basic) DISPLAY; in all of these, the location on the screen is defined by a row and column coordinate. However, in assembly language you must compute the coordinate yourself from the row and column. (Incidentally, this step is necessary even in Basic on some other computers.) In the normal screen mode used by Basic, called graphics mode, there are 32 columns and 24 rows. This means there are 768

positions. Each of the positions is defined by a byte in VDP memory. For instance, the upper left corner of the screen is defined by location 0 in VDP memory. Locations 0 through 31 de fine the top row of the screen. Suppose you want to put a char acter at the 5th position of the 20th row. To compute that loca tion in VDP memory, you have to take into account the 19 rows that were skipped. That is 19 times 32 positions, or 608. There are also four positions to be skipped in the 20th row to get to the 5th position. This makes the exact count 612. Thus, row 20 column 5 is position 612. In order to display a character at a given position, you need to move a byte value into that location in VDP memory. The value of that byte should be the equivalent ASCII value for the character you want to display. Suppose for instance that you

want to display an "X", which is represented by the value 88. Move 88 into location 612 of VDP memory to put an X in row 20

%^^

column

5.

123

Interrupts, Screen, and Keyboard

If your routine is called from a Basic program, the value moved into VDP memory must have 96 added to it so the proper character is displayed. For instance, put 184 into VDP memory to

display an X.

(The BTXT directive in the Dow Editor/Assembler

does this for your automatically.) data from th e keyboard is also very different in bly lang uage than th e standard INPUT statement in Basic, All a ssem bly lang uage has is KSCAN, which is essentially idenRead xng

assem

tical

to

CALL

KEY

Notic e

that

KEY

in

Basic

and

KSCAN

in

assem-

bly 1 angu age can be calle d when a k ey is not being pressed, so Also, even when data is they do n ot a lway s return any data, This means that if you retur ned, it is o nly one key at a t ime. series o f characte rs as input, you must write a want to a ccep t the p rogr am t o ac cept the m one at a time and combine them within Fur thermore , nothing is displayed on the screen the p rogr am. when the keyb oard is read , so your program has to do this itself.

To make all these concepts clear and to set the stage for the assembly language program which comes later in this chapter, here is an Extended Basic program which does essentially the same things. It clears the screen, then prompts for and accepts a number, checking to make sure that each character typed is a

digit (beeping if an invalid character is pressed). 100

CALL

CLEAR

110 DISPLAY AT(13,14):"DATA:" 120 ACCEPT BEEP AT(13,19)VALIDATE(DIGIT):T 130

PRINT T

Here is a Basic program which does the same thing, but with out the aid of commands such as CALL CLEAR,

DISPLAY, and ACCEPT.

This second Basic program is very similar to the way you have to do these things in assembly language. 100 110 120 130

140 150

FOR R =1 TO 24 FOR C =1 TO 32 CALL HCHAR(R,C,32) NEXT C NEXT R CALL HCHAR(13,14,68)

160 CALL 170 CALL 180 CALL

HCHAR(13,15,65) HCHAR(13,16,84) HCHAR(13,17,65) HCHAR(13,18,58)

190 CALL 200 C=19 210 N=0 220 CALL S0UND(100,1500,0) 230 CALL KEY(0,K,S) 240 IF S< 1 THEN 230 250 IF K = 13 THEN 330 124

Interrupts,

Screen,

260 270 280 290

and Keyboard

K=K-48 IF K9 THEN 350 N=N * 10 + K

300 CALL HCHAR(13,C,K+48) 310 320 330 340

C=C+1 GOTO 230 PRINT N STOP

350 CALL SOUND(200,200,0) 360 GOTO

230

Statements 100 through 140 clear the screen, one character

at a time.

Statements 150 through 190 put "DATA:" on the screen,

one character at a time.

(In fact,

this is somewhat different

than assembly language, in which you can put more than one char acter on the screen,

somewhat similar to DISPLAY in Extended

Basic.) Statement 200 sets C to point to the screen position for input, and statement 210 sets N to 0 (to be used to accumulate

the number as it is keyed in).

Statement 220 generates the tone

to prompt the user to begin data entry. The entry loop begins at 230, where KEY is called. If no key is being held down, state ment 240 loops right back to 230. If the key pressed was the

"ENTER" key, K is 13; statement 250 transfers to 330 to print the value of N and then stop. If not ENTER, subtract 48 from K; this maps the character "0" to the value 0, maps "1" to 1, and so forth.

After doing this, if K is less than 0 as tested in state

ment 270, go to 350 because it was not a digit. Similarly, at 280, check to see if K is now greater than 9. If K is a digit from 0 to 9, multiply N by 10 and add K; this accumulates the number from the multiple key presses. At statement 300, dis play the character at position C on the screen, then add 1 to C to move to the right, and go back to wait for another key to be

pressed.

At 350, generate a tone to signify an improper key

press, then go back to the top of the loop at 230.

On the next page is an assembly language program to do the same thing.

The Basic program above was written with almost

identical logic to facilitate comparison of Basic and assembly language doing the same things.

However, one difference is that

this program does not print the number once the ENTER key is pressed.

Instead, the value is stored in location >7200.

The assembly language program is shown using the syntax of

the Dow Editor/Assembler because it was take directly from Sec tion 10 of the Dow Editor/Assembler manual.

\l^/

125

Interrupts, Screen, and Keyboard

1 2

3

4

5 6 7 8 9

DEMO PROGRAM - INPUT AND DISPLAY INTEGER VALUE (LOAD AT 7500) Rl;766 CLEAR SCREEN 000 LI R2;>8080 (BLANKS) 004 LI R2;@BUF(R1) 008 TOP:MOV 00C DECT Rl TOP 00E JOC RO WRITE 010 CLR 012 016 01A 01E 022 026 02A 02E 032 034 038 03C 03E LP 042

LI LI BLWP LI LI LI BLWP LI CLR MOVB BLWP DATA :LIMI LIMI

R1;BUF R2;768 @MBW R0;392 R1;PR0 R2;5 @MBW R0;397 R2

BLANKS TO SCREEN.

WRITE PROMPT TO SCREEN.

INPUT POS. R2=NUMBER.

R2;@M0D MODE 0. @GPL >34

ACCEPT TONE.

2 0

ALLOW INTERRUPTS BRIEFLY,

@KEY

CALL KSCAN.

10 046 04A 04E 052 11 054 056

BLWP MOVB COC JNE CLR MOVB

12

05A 05E

MOVB SWPB

13

060

CI

R1;>D

ENTER?

064

JEQ

END

GO

Rl;-48

NO.

ERR

FOR DIGIT.

14 066 06A 06C 070 15 072 076 078 16 07A 07E 17 080 18 084 086 19 088 08C 20 08E 092 094 096 09C 09E

AI JLT CI JGT MPY MOV A AI SWPB BLWP INC JMP END:MOV B ERR:BLWP DATA JMP PRO:BTXT MSK:DATA V10:DATA

@STA;R1 CHECK STATUS @MSK;R1 FOR NEW KEY. LP Rl

NOT YET. YES.

R1;@STA CLR STATUS. @INP;R1 LOOK AT IT. Rl

IF DONE.

CHECK

Rl;9 ERR

@V10;R2 R3;R2 R1;R2 Rl;>90

OK. COMPUTE NUMBER IN R2. NOW WRITE

Rl

DIGIT TO

@SBW

SCREEN.

RO LP

GO

FOR

NEXT.

R2;@>7200 STORE R2. *R11

BACK TO BASIC

@GPL >36

ERROR. BAD TONE.

LP

\^0

/DATA:/ >2000

MASK.

10

VALUE TEN. 126

Interrupts,

Screen,

and Keyboard

0A0 0A2 0A4 0A6

MOD:EQU BUF:EQU KEY:EQU STA:EQU

>8374 >7200 >6020 >837C

0A8 OAA OAC OAE

INP:EQU GPL:EQU SBW:EQU MBW:EQU

>8375 >6018 >6024 >6028

MODE. BUFFER. KSCAN. STATUS. KEY PRESSED GPLLNK. VSBW. VMBW.

At 1, the first nine instructi ons b lank o ut

s cree n by Basi c is running, all characters must be bia sed b y >60, so a bl ank is >80 instead of >20. This is discussed and d emonst rate d du ring the discussion of the BTXT directive in the chapte r on dir ecti ves. ) The 768 blanks are first loaded int o CPU RAM, star ting at BUF;

filling VDP locations 0 through 767

with

blank s.

t he

(If

this is done with a loop at 2. At 3, th ey are wri tten int o VDP RAM by calling VMBR. This is relat ively easy to u se. Fir st, load register 0 with the address in VDP where data is to b e put; this corresponds to the screen posi tion, and i s 0 to b lank out the entire screen. Second, load re giste r 1 wi th t he ddre ss in CPU memory of the data to be obtain ed; t his va lue is he a ddress of BUF in this instance. Last, loa d reg ister 2 wi th he n umber of bytes to be sent (768 to blank t he en tire s cree n) At 4, locations 01E to 02A write "DATA:" to the screen at

position 392. As when blanking out the screen, register 0 holds the address in VDP memory where the data is to be sent; 392 for row 13 column 8. Register 1 holds the address in CPU memory of the data to be sent; in this case, the label PRO (for prompt) points to the string "DATA:". Finally, register 2 is loaded with the value 5, which is the number of characters to be sent to VDP memory with VMBW.

At 5, RO is set to point to the input position, which is immediately to the right of the prompt message on the screen.

At 6, register 2 is cleared to accumulate the number to be entered.

At 7, the location identified by the symbol MOD is set to 0 by moving a byte from register 2. This location corresponds to It determines the mode when the first argument in CALL KEY. reading from the keyboard. Although there is a CLR instruction,

N^/

were to be used, CLR @MOD, it would clear two bytes. Therefore, it is necessary to have a byte value 0 someplace to be moved into MOD. Since register 2 has. just been cleared (for an entirely different reason), it is handy to use it: MOVB R2;@M0D. When doing this, watch out so that the two instructions do not become separated. If they do, the wrong value may end up in register 2, causing unpredictable behavior on the part of KSCAN.

127

Interrupts,

Screen, and Keyboard

At 8, the prompt tone is started. GPLLNK routine.

It can do many things.

This is done with the You indicate which to do

by the value in the next word, >34 in this case. This routine is described in the TI Editor/Assembler manual, starting at page 251, and in the Mini Memory manual, starting at page 38. Remem ber the discussion about interrupts; they should not be enabled when GPLLNK is called, but they have to enabled at intervals in order for the sound to function properly. At 9 is the top of the input loop. First, interrupts are allowed briefly. This is necessary so a tone can be generated.

At 10, KSCAN is called. bit 2 can be tested.

The status byte is moved to Rl so

(The COC instruction uses MSK,

which con

tains a mask value that identifies the single bit to be tested.) If the bit is set, a key has been pressed. If not set, loop back to

LP.

At 11, the status byte is reset to 0. As discussed above, there is no clear byte instruction. Therefore, register 1 is cleared

and

then MOVB

is

used.

At 12, the byte holding the key that was pressed is moved from INP to register 1 with MOVB. The move puts the byte value into the left half of the register. Register 1 had just been cleared (for another reason); this means that after the SWPB Rl,

the character is in the right of Rl and the left is 0's.

Now, at

13, it is an easy matter to check to see if the ENTER key, >D, was the key that was pressed; go to END if so. At 14,

value

subtract 48 from the code for the key that was

pressed, and if the result is less than 0, go to ERR. Then compare the register to 9, and go to ERR if it is greater. At

15,

the character has been tested to make sure it is a

digit, so it is now time to add it into the total for the number being accumulated in register 2. Do this by multiplying the num ber so far by 10. This leaves the product in registers 2 and 3. However, the product is assumed to be small enough to fit into just one register, which is register 3. Therefore, register 3 is moved into register 2. The digit just keyed in can now be added into register 2 from register 1. To show the digit on the screen so the user knows that it has been accepted, at 16 bias the digit by two values. First, if the program is called from Basic, the digit must be biased by >60 so it can be displayed. Second, because register 1 now contains the value (instead of the code for the character), the digit must be biased by >30 = 48 (because it was subtracted out above). In other words, add >90 to it. Then swap it to the left byte.

At 17, put the character on the screen using VSBW (single 128

\^0

Interrupts,

Screen,

and Keyboard

Sto^ byte write). For this routine, register 0 is supposed to hold the VDP address where the single byte is to be sent. Register 1 holds the byte in the left side.

At 18, increment the screen position pointer and loop for another digit.

At 19, terminate with B *R11. (If called from Basic, this will return properly.) The accumulated value is first stored at >7200.

It is not safe to return to Basic if the STATUS byte is not first cleared.

In this case,

it was cleared after the last

KSCAN.

At 20, the bad response tone is generated using GPLLNK in a manner very similar to the generation of the prompt tone. Finally, data and equates end the program. As discussed above, the BTXT directive is used if the program is called from Basic.

%fc^

129

APPENDIX A:

CPU MEMORY MAP

This appendix lists in numerical order many of the addresses of interest in the CPU memory. The data was derived from the TI Editor/Assembler manual and no claim is made for its accuracy or completeness. Locations 0000-1FFF

The first 8K (0000-1FFF) is console ROM.

General page references are: 398, 399, 400, and 401. LOCATIONS LABEL

DESCRIPTION

EDITOR/ASSEMBLER MANUAL PAGE REFERENCES 0000,1

Return to color bar screen.

Pages: 442 000E,F

SCAN

Keyboard scan routine

Pages: 247,415 0010,1

Return to calling program. Pages: 442

Nl^/

A-l

APPENDIX

A:

CPU MEMORY MAP

Locations

2000-3FFF

The next 8K (2000-3FFF) is low memory expansion. General page references are 398, 399, and 400. LOCATIONS

LABEL

DESCRIPTION

EDITOR/ASSEMBLER MANUAL PAGE REFERENCES

E/A loader and assembly language programs,

2000-????

XB utilities and assembly language programs. Pages: 305,410 ID code >A55A for E/A loader Pages: 411

2000,1

2000,1

XML

vector

for

XB

Pages: 412 2002-????

XML vectors for E/A Pages: 411

2002-????

Utility data area for XB Pages: 412

2006,7

ID code

Pages:

>AA55

for

XB

412

Utility BLWP vectors, routines, language programs, DEF table

2008-3FFF

assembly

Pages: 412 2008,B

NUMASG

in

Pages:

416

XB

Argument identifiers used by GPLLNK in Basic Pages: 278

200A-2019

200C,F

NUMREF

in

XB

Pages: 416 2022-???? UTLTAB Utility table entry address Pages: 247,263,308,411

2024,5

FSTHI

First free address in high memory Pages: 265,305

2026,7

LSTHI

Last free address in high memory Pages: 265

2028,9

FSTLOW First free address in low memory Pages: 265,276,305

A-2

APPENDIX

A:

CPU MEMORY MAP

202A,B

LSTLOW Last free address in low memory Pages: 265,276,307

202C,D

CHKSAV Check sum

Pages:

265

202E,F

FLGPTR Pointer to the flag in the PAB. Pages: 265

2030,1

SVGPRT GPL return address.

Pages:

265

2032,3

SAVCRU CRU address in the peripheral. Pages: 265

2034,5

SAVENT Entry address of the DSR or subprogram. Pages:

2036,7

SAVLEN Device or subprogram name length. Pages:

2038,9

265

265

SAVPAB Ptr to the device or subprogram name in PAB Pages: 265

203A,B

SAVVER Version number of the DSR.

Pages:

265

2094-20C3 UTILWS Utility workspace registers Pages: 246 20BA-20DB USRWSP User workspace registers Pages: 246,440,441

2100-????

Utility BLWP vectors for E/A Pages: 411

2128-????

Utility routines for E/A Pages: 411

2676-3F7F

Expansion area for relocatable programs. Pages: 305

2700-????

Relocatable assembly language programs, E/A Pages: 411

3F38-3FFF

REF/DEF table Pages: 246,308,411

A-3

APPENDIX

A:

CPU MEMORY MAP

Locations 4000-7FFF

The next 8K (4000-5FFF) is reserved for ROM in peripherals

General page references are 399, 400, and 401. The next 8K (6000-7FFF) is reserved for ROM in modules.

General page references are 399, 400, and 401.

A-4

APPENDIX A:

CPU MEMORY MAP

\l^'

Locations 8000-9FFF

The next 8K (8000-9FFF) is console RAM and memory mapped addresses. General page reference: 399. LOCATIONS LABEL

DESCRIPTION

EDITOR/ASSEMBLER MANUAL PAGE REFERENCES 8300-83FF PAD

Scratch pad (THIS IS THE ONLY RAM) Pages: 247,308,415

8300-8340

Destroyed by GPLLNK 003B Pages: 253

8300-8317

Not used by Basic if assembly language routine has no parameters.

Pages: 404 Used by Basic

8318-8349

Pages: 404 830C,D

No. of types to be allocated by GPLLNK 0038 Pages: 253

8310,1

Value stack pointer used by LINK in Basic. Pages: 278

8312

No. arguments in LINK in Basic. Pages: 278

831A,B

Pointer

to

first

free

address

in

VDP

RAM

Pages: 253 831C,D

Pointer to allocated string space Pages: 253

831C,D

Pointer

Pages:

FAC

8354

^JSBfc*^

ERR

Floating point accumulator

Pages: 834A-836D

for

Convenient area for speech read routine. Pages: 349,350

8328-????

834A-8351

to PAB

287

252,285,286,290,415

Used by DSR's. Pages: 300,404 Error code returned by mathematical routines

Pages: 254,259,261

A-5

APPENDIX

8354.5

A:

CPU MEMORY MAP

????

No. of chars in cass name for GPLLNK 003D Pages: 253

8356,7

Ptr to char past cass name for GPLLNK 003D Pages: 253

8356,7

Ptr to name len in PAB for DSRLNK or LOADER Pages: 262,263

8356,9

Used by GPLLNK 0038 Pages: 253

835C-???? ARG

Arguments for GPL routines Pages: 252

836D

836E,F

Set

VSPTR

to

>08

Pages:

253

for GPLLNK

0038

Ptr into VDP RAM for Value Stack ROM math Pages: 252,259,260,404

8370,1

Highest available VDP RAM address Pages: 252,404

8372

Least significant byte of value stack ptr. Pages: 404

8373

Least significant byte of subr stack ptr. Pages: 404

8374

Selects keyboard device

Pages:

250,404

8375.6

Used by some mathematical routines Pages: 255,256

8375

ASCII value of key pressed Pages: 251,405

8376

Joystick Y-position Pages: 250,405

8377

Joystick X-position Pages: 250,405

8378

Random number generator Pages: 405

8379

VDP interrupt timer Pages: 405

A-6

APPENDIX

837A

837B

A:

CPU MEMORY MAP

No. sprites in motion. Pages: 340,347,405

VDP status byte Pages:

837C

837D

405

STATUS STATUS byte Pages: 250,298,299,311,405,410,441 VDP character buffer

Pages: 405 837E

VDP current row

Pages: 405 837F

VDP current column

Pages: 405 8380-839F

Default subroutine stack

Pages: 405

8386,7

Highest loc available for assfy Ian with XB Pages:

8388,9

410,412

Used by Basic Pages: 405

838A-83BF

83A0-83BF

Subroutine and data stack areas for Basic Pages: 405 Default data stack

Pages: 405 83C0,1

83C0-83DF

Random number seed Pages: 405

Interpreter workspace Pages: 405

83C2

Flag byte to control interrupt routine. Pages:

83C4,5

406

Address of user defined interrupt routine. Pages: 406

83CA

83CC,D

Console keyboard debounce. Pages: 406

Sound list pointer in VDP RAM. 312,321,322,323,405

Pages:

A-7

APPENDIX A:

CPU MEMORY MAP

Set to

83CE

>01

to start sound generator

Pages: 312,321,322,323,406 83D0,1

Should

be

Pages:

253,406

set

0

before

GPLLNK

003D

Should be copy of VDP Register 1

83D4

Pages: 248,326,406 Screen

83D6

time

out

counter

Pages: 406 Return

83D8,9

address

for

scan

routine.

Pages: 406 83DA

Player number for scan Pages: 406

83DA

Used by DSR if not interrupt driven Pages: 300

83E0,F

GPLWS

GPL workspace

Pages: 247,308,406,415 \sg|r

Set

83F0

LSB

for

sound

Pages: 312,321,322,323 8400

SOUND

Sound chip register

Pages: 308,317,415 8800

VDPRD

VDP

RAM

read

data

Pages: 247,267,308,402,415 8802

VDPSTA

VDP

RAM

Pages: 8C00

VDPWD

VDP

RAM

Pages: 8C02

VDPWA

VDP

RAM

Pages:

status

247,269,308,402,415 write

data

247,268,308,402,415 write

address

247,402,415

9000

SPCHRD

Speech read data register Pages: 308,351,415

9400

SPCHWT

Speech write data register Pages: 308,351,415

9800

GRMRD

GR0M/GRAM read data

Pages:

247,271,308,415

A-8

APPENDIX

A:

CPU MEMORY

MAP

9802

GRMRA

GROM/GRAM read address Pages: 247,270,308,415

9C00

GRMWD

GROM/GRAM write data Pages: 247,271,308,415

9C02

GRMWA

GROM/GRAM write address Pages: 247,270,308,415

A-9

APPENDIX

A:

CPU MEMORY

MAP

Locations

AOOO-FFFF

The last 24K (A000 - FFFF) is high memory expansion.

(Extended Basic stores numeric data and the program here.) General page references:399, 410. LOCATIONS LABEL

DESCRIPTION

EDITOR/ASSEMBLER MANUAL PAGE REFERENCES A000-FFD7

Relocatable assembly language programs, E/A. Pages: 305,410,411

A000-FFE0

Free space (see >8386), numeric values, number table, XB program. Pages: 412

FF08-FFFF

Used by X0P1 Pages: 400

line

J A-10

APPENDIX B:

VISUAL DISPLAY PROCESSOR MEMORY MAP

This appendix lists in numerical order many of the addresses of interest in the CPU memory. The data was derived from the TI

Editor/Assembler manual and no claim is made for its accuracy or completeness. In particular, there are many ways VDP memory can be used, so the table below is only representative. Visual Display Processor Memory Map

LOCATIONS

DESCRIPTION/REFERENCES

0000-02FF

Screen image table for E/A Pages: 330,343,344,403

0300-037F

Sprite attribute list for E/A Pages:339,347,403

0380-039F

0400-077F

Color table for E/A Pages:330,342,347,403

Sprite descriptor table (characters 80-EF) Pages:339,347,403

0780-07FF

Sprite motion table (4 bytes each sprite) Pages:340,347,403

0800-OFFF

Pattern Descriptor Table for E/A Pages:329,403

1000-37D6

PAB's, buffers, free space for E/A Pages:403

1800

Usual screen image table for bit map mode Pages:334

37D7-3FFF

Blocks

reserved

for

Pages:403

v^^

B-l

diskette

DSR

>^^z APPENDIX C:

DECIMAL TO HEXADECIMAL CONVERSION PROGRAM

This appendix lists a Basic program which can be used to convert from decimal to hexadecimal notation, or from hexadecimal to decimal

notation.

100 REM (HEXDEC AND DECHEX CONVERSION) 110 INPUT A$ 120 IF SEG$(A$,1,1)=">" THEN 250 130 REM

DECHEX -

DECIMAL TO HEX

140 HEX$="" 150 N=VAL(A$) 160 IF N>=0 THEN 180 170 N=65536+N

180 T=INT(N/16) 190 D=N-16*T

200 HEX$=SEG$("0123456789ABCDEF",D+1,1)&HEX$ 210 N=T

220 IF N>0 THEN

230 PRINT "

180

>";HEX$

240 GOTO

110

250 REM

HEXDEC - HEX TO DECIMAL CONVERSION

260 N=0

270 FOR 1 = 2 TO LEN(SA$) 280 D$=SEG$(A$,I,1)

290 D=P0S("0123456789ABCDEF",D$,1)-1 300 N=N *

16 +

D

310 NEXT I

320 PRINT " ";N,N-65536 330 GOTO

110

Enter hexadecimal values with the ">"; example: >FFFF. The value is displayed in decimal. If negative, it is displayed also in the equivalent value assuming the sign bit is the value 32,768. Example: >FFFF is both -1 and 65,535.

Enter positive or negative decimal values without the ">"; example: 5230. The result is displayed in hex with the ">".

C-l

\^