- A Tutorial I'm Working On
- 27 Sep 2011 09:15:51 pm
- Last edited by ACagliano on 28 Sep 2011 08:15:35 am; edited 1 time in total
I started working on this tutorial for Z80 after I started learned how to use basic Z80. Now that I'm getting better, I decided to create the tutorial, presenting the learning of Z80 in the way that worked for me. Here is my request to you. Please read this over with a fine tooth comb. Correct anything you want to correct. Add anything you feel needs added. I want this to be as comprehensive and as detailed as possible. Please and thank you.
Intro and Concepts
Code:
Using the language
Code:
Note...as I complete sections, I'll upload them to this post.
Intro and Concepts
Code:
Introduction to Z80 Assembly for the TI Calculator
Welcome to the beginner’s guide to Z80 assembly. Before you read on, know that Z80 is not an easy language. In fact, it is an extremely hard language to learn and annoying to code in. However, the learning curve is drastic. This is because, once you understand the concepts behind assembly and what commands do what, Z80 all of sudden becomes less cryptic.
Most programming guides will start you off with a compiler and some code examples, without really explaining why you need to do what you do. For me, this resulted in confusion. As is typical with human nature, the mind cannot begin to comprehend the “what” until it understands the “why”. For this reason, this tutorial will start with the “why”? Please know that you can email me at acagliano.blast@gmail.com at any time if you have any questions. Also, let it be known that this guide will only deal with Z80 for the Texas Instruments graphing handheld, not computer assembly.
The Merits of Z80
What are the benefits of using Z80 over another programming language? Well, for calculators there are three languages. TI-Basic, Axe, and Z80. TI-Basic is the easiest. It can be programmed directly on your calculator and can do basic math, as well as complex math, create lists, and other cool things. However, it cannot check the battery, set the contrast, or create and delete external variables. Also, TI-Basic is extremely slow. This is because it is an interpreted language, not a compiled one. Interpreted languages are read line-by-line by a processor and are translated before they are executed. I’ll return to this in the next title. Axe is a slightly more powerful language. It is sort of an intermittent language. It has a TI-Basic like syntax, but it compiles on-calc into Z80. This is achieved through the use of the Application “Axe Parser” by Quigibo. A compiler is a program that converts a program into machine code. Axe provides a lot of the power and functionality that pure Z80 does, but it’s command set is incomplete, considering that Axe is a fairly new product.
Z80 is the most powerful language. It can read and write directly to areas of hardware such as LCD RAM, the link port, the keyboard, and others. It is also so much faster than TI-Basic and slightly faster than Axe. Z80 can modify all memory areas, including system variables, free RAM, and Flash. The significance of all of these will be visited later. It can create, delete, and modify memory without much hassle. This makes Z80 very efficient and useful. It can be used for a wide range of programs. However, for a beginner, I do not recommend using it for anything that requires fractions and math with fractions. You’ll see why in a bit.
The Caveats of Z80
Just as much as Z80 has benefits, it also has some caveats (negatives). I cannot say weaknesses, because they really aren’t weaknesses. To start with, Z80 has a memory system in which data can be used in increments of one or two bytes. What significance is this to us? Remember when I said that beginners should avoid using Z80 for anything requiring fractions. Well, that’s why. One (1) byte of memory encompasses integers from 0 to 255 (for negatives, you halve that and get -128 to 127). Two (2) bytes of memory encompasses integers from 0 to 65,536 (for negatives, halve that and get -32, 768 to 32,768). Z80 does not really support anything other than integers, although there are ways to achieve them. That is an advanced topic.
Remember when I said that Z80 can write to memory with little hassle? Well, that also means that it can corrupt your data easily. One mistake in addressing or a mathematical mistake in a routine that edits memory and you can alter data that you didn’t want to. That data is rarely recoverable. If you can get past these risks, then Z80 is the language for you.
TI Calculator Programming versus Traditional Z80 Programming
What sets TI-calculator programming aside from computer assembly? Well, this is simple and it really doesn’t warrant a whole new title, but whatever. It has to do with the system variables and instructions. Ever wonder why some programs are compatible with some devices and not others. Why can’t I run Windows on an iPod, or Max OS X on a Android? It is because the system equates are different and the memory areas differ. Thus, a perfectly good instruction to move a song from one playlist to another on your iPod might move the headphone driver to the trash on your Android. A program has no idea what device it is on or what it “should” be doing. All it’s doing is looking at your memory and doing what it is told to do.
Important Concepts to Understand
As I said, this tutorial will start with the necessary concepts from the beginning, instead of interrupt the programming to do so as you encounter them. This way, you will enter the commands and programming sections of this tutorial already knowing why certain things must happen.
Addressing
One of the most important things about learning Z80 is understanding “addressing”. What does it mean? What significance does it have. In your calculator, or any electronic device for that matter, memory is addressed by bytes. Each byte on your calculator has a two-byte numerical address . The first byte is addressed as 0. The second as 1. And so on, until you hit 65,536.
Now, let’s look at referencing conventions for those addresses. Let us assume we are trying to look at the byte addressed 1000. There are two ways to reference that address.
1000
(1000)
The first one references the address itself. That will always be the two-byte value 1000, regardless of what is held there. This will be further explained in the Pointers section. The second one references the data held at that address. That will be the one-byte value of the data. You may ask why this is so? Well, there is no reason. That is just the convention used by the designers who developed this language. It’s a distinction you just need to remember.
Numerical Systems
Most programmers choose to use other numerical systems instead to represent their numbers. This may be because they wish to compress the data, or they may wish to read it in a way that suits the hardware. Other than decimal, there are two other numerical systems that are commonly used: hexadecimal notation (hex) and binary. To avoid yourself headaches while I elaborate, please remember these rules:
1. A number in decimal and its hex or binary equivalent are the same number. If you say a word in another language, does its meaning change?
2. Regardless of the numerical system used to write the number, its value and its size does not change. If you use a one-byte number, it will always be one byte.
3. You may use numerical systems interchangeably within a program. You simply need to use some sort of notation to indicate so.
Let’s start with hexadecimal. Hexadecimal numbers are denoted by placing a ‘$’ in front of the number OR an ‘h’ after. Do not use both. Also, as a general rule, place an extra ‘0’ in front of the hexadecimal value only if using the ‘h’. That’s because of a compiler error.
Hexadecimal extends its digits to F, not just 9. So, watch the counting below. Note that once you understand this system, digits carry the same way as they do in the decimal number system.
$00, $01, $02, $03, $04, $05, $06, $07, $08, $09, $0A, $0B, $0C, $0D, $0E, $0F, $10, $11, $12, $13.
$13 is the hexadecimal equivalent of 19. Both are the same number, and both are one byte large. Hex is mainly used to represent addresses, as well as for tile mapping.
Now, let’s move on to binary. Binary numbers are denoted by placing a ‘%’ in front of the number or a ‘b’ after. Do not use both. Binary is used mainly for bit-level things, like writing to the LCD (display), link port, and keyboard. This is because those drivers have their memory areas compressed as such. The LCD, for instance, uses only twelve (12) bytes across to hold it’s data. Why? Because a pixel can be only on or off. Why waste a full byte on one pixel when a value of 1 or 0 can suffice and binary can handle that. Remember that eight (8) bits make up a byte, and thus, one byte can hold the values of 8 pixels. Watch me count to 19 in binary.
%00000000, %00000001, %00000010, %00000011, %00000100, %00000101, %00000110, %00000111, %00001000, %00001001, %00001010, %00001011, %00001100, %00001101, %00001110, %00001111, %00010000, %00010001,
%00010010, %00010011
%00010011 is the binary equivalent of 19. Both are the same number and both are one byte large.
Data Types and Usage
Now, let’s talk more about data. You know that your calculator has strings, picture variables, graph databases (GDB’s), lists, and matrixes, right? Well, let me rock your world straight away. Wrong! All your calculator has is a sequence of bytes. It has absolutely no idea what the hell type of data is in that type. It is up to you to tell the calculator, through your program, what is there. Isn’t it great to be in control? Also, let me rock your world some more. In Z80 (and in Axe too), it is perfectly fine to call your picture data Str1 and your string data Pic1 (I don’t see why you would want to, though, since you can name your data anything in Z80). It doesn’t make a difference, as long as you remember what you called it. It’s just a name. When you compile your program, the compiler replaces all instances of that name with the address of the data named such. All hail referencing.
When you write your program, you know the format of the data you are holding there. You also know how it is to be used. So, you will need to write code to handle it in that way. Remember that there are system routines to do some things with data. We’ll deal with that later. But, for ease of use, we will discuss main data types below. Remember, though. Your calculator does not know what data type you are intending. You need to.
Strings
Strings are data types that hold text. Strings can hold any legal ASCII character, including symbols, numbers, and letters, whether upper or lowercase. Strings are generally sized at one larger than their length, where their length is how many characters they are holding. You make them one larger because you will need to mark where the end of the string is. This is accomplished in two ways. Either you can prepend the string with the length of the string (place a number before the string, where that number is the length of the string. Ex: The string “Hello!” would be written as ‘ 6 H e l l o ! ’) or can terminate the string with a zero (Ex: the string “Hello!” would be written as ‘ H e l l o ! 0 ‘). Strings are very widely used in Z80.
Sprites
You would not encounter this data type in TI-Basic, because it does not support sprites. The closest you get are the RecallPic and StorePic commands, which save and recall the entire screen. In Z80, you can place sprites of variable height and width on the screen. A sprite is, simply put, an image. Sprite data is put on the screen by writing the data to the LCD port. More on that later.
Numbers
You may use memory to hold one- or two-byte integers. If you are advanced on doing so, you can use two bytes to hold a one-byte non-integer. In this case, the decimal part can only be accurate in intervals of 255 (1/(2^8)). Note, that the calculator will not do this automatically. You must handle it with your code. You can also create lists (arrays) by storing a succession of numbers into memory.
You can have your data be almost anything you want it to be, as long as you tell your calculator how to use it.
Pointers
Pointers are the “bread-and-butter” of Z80. You can’t really do anything without knowing what they are and how to use them. So, here we go.
Do me a quick favor and scroll back to the section on addressing and look at the two examples of referencing that you saw there. The first example, the one that references the address itself is just one example of a pointer. Why? Because the value 1000 points to the data you want. That’s all a pointer is. It points to data or a section of data. Most routines or commands in Z80 require that pointers be passed to them. More on that in the CPU-Based Math section.
Memory Areas
Another facet of programming is knowing what areas of memory to use for what. Well, this is one of the hardest tasks a programmer can encounter. Because it often changes depending on the purpose. The good thing is that Z80 gives you a great deal of control over what you can use. Below, I will list areas of memory, their uses, their versatility, and how to write to them.
User RAM
This memory area is very dynamic and always changes, and it is very unwise to use it within a program. This is because this area of memory is where the calculator stores other programs and non-system variables that you have installed. Using this area of memory can corrupt the data stored there. The user RAM is great for long term storage, by means of external variables. You are free to create a program or application variable and hold your data there. You are also free to modify data held there, so long as you do not alter anything else.
Free RAM
This area of RAM is great for short term storage. It does not interfere with anything, and will not be used by anything else while you are using it. There are several areas of free RAM that are open for use.
Name Address Size Versatility
APDRAM $9872 768 bytes Auto-power down will corrupt
statRAM $8A3A 531 bytes Using stat graphing will corrupt
textMem $8508 128 bytes --
OpXs $8478 66 bytes --
System RAM
This area of RAM is not good for anything, but sometimes must be modified in order to accomplish things systematically. For instance, let’s say we wanted to put a letter on the screen. In order to do that, you will need to move the cursor. Well, go into the folder in which your choice compiler is and find the compiler’s working folder, where all of its subprograms are. You should see a file named ‘ti83plus.inc’. If you cannot find it, use the one enclosed with this tutorial. Open it. Then, search for the phrase “curRow”. When you find it, you should see “curCol” right below it. Those are the system variables that control the position of the cursor on the home screen. Further down on the list, you should see “penCol” and “penRow”. Those are the variables that control the position of the pen on the graph screen. There are many, many other system variables. You will never need to know their addresses. You will simply need to know what they control.
Archive Memory
This area of memory is not a part of RAM. It is a much more secure area of memory. It is also write-protected, meaning that you cannot edit it. You can, however, move variables there. This is a great place to keep variables that may be needed later, like game saves. There is a command to do this. That will be discussed later.
CPU-Based Math
This is the final concept that we need to discuss, before we can start learning the Z80 command set. In TI-Basic, you could store a number into memory, then just have math done with it. In Z80, you cannot do math with the RAM. You must use the CPU of your calculator. You calculator has several registers that are utilized for functions like math. These registers can hold values and do math on them. You must understand the concepts of pointers and addressing before you can understand how to use the CPU effectively. Your calculator’s CPU has two sets of registers, the 8-bit (1-byte) and the 16-bit (2-bytes). Note how, the two byte registers contain the one byte registers. They will modify each other. You will often times be done with a calculation before this becomes an issue, but if it is, there is a way to save the value of a register. More on that later.
8-Bit Registers
A : called “The accumulator”. Used mainly for math.
B : commonly used as an 8-bit counter. Can also be used for math.
C : used for interfacing with hardware. Can also be used for math.
D : not normally used in its 8-bit form, but if necessary, can be used for math.
E : not normally used in its 8-bit form, but if necessary, can be used for math.
F : holds the flags. Never use.
H : another register not normally used in 8-bit form, but can be used for math.
L : another register not normally used in 8-bit form, but can be used for math.
I : the interrupt vector register. It is used by the calculator in the interrupt 2 mode.
R : the refresh register. Can be used to generate random numbers.
IXH : The higher (first) byte of the IX register.
IXL : The lower (second) byte of the IX register.
IYH : The higher byte of the IY register.
IYL : The lower byte of the IX register.
16-Bit Registers
AF : not used because of the F, which is used to store flags.
BC : is also used as a 16 bit counter. Can also be used for math.
DE : often used as a pointer to some memory location, usually a destination.
HL : the general 16-bit register, used everywhere you use 16 bit registers. Used for both math and for holding a pointer to some memory location, usually for a read.
PC : the program counter. Points to the current address the calculator is executing code from. You can’t change this.
SP : the stack pointer. It holds the current address of the top of the stack.
IX : is called an index register. It's use is similar to HL, but it's use should be limited as it has other purposes, and also runs slower than HL.
IY : is another index register. It holds the location of the system flags and is used when you want to change a certain flag. For now, we won't do anything to it.
Using the language
Code:
Assembly Commands and their Uses
This section will be your first real exposure to the world of assembly. The command set of assembly is very complicated, but mnemonically-based. Please do not be discouraged. You will be asking yourself “How the hell am I going to remember all of these different commands?” Well, you don’t have to. Nothing will stop you from looking back at this document. I’d rather you do that than make a mistake. However, you will find, over time, that if you stick with it, a lot of the commands will just sink in. You’ll remember them and their functions by sheer repetition.
Memory-Based
In assembly, you will find that almost all of your code will be about moving or reading some memory. TI-Basic programmers take for granted how easy it is to store variables or move a variable, then get it later . When you start to work with assembly, you will realize just how tedious it actually is.
One of the first things you need to do in assembly is move memory. For that, you will need the ld instruction. That instruction will be your bread-and-butter. Use that instruction to move a value from one location to another. For instance, the following examples are valid:
ld a, b ;loads b into a
ld a, 8 ;loads 8 (as 1-byte) into a
ld hl, 8 ;loads 8 (as 2-bytes) into hl
ld a, (hl) ;loads the 1-byte value at the address pointed to by hl into a
ld hl, [PTR] ;loads the 2-byte address of [PTR] into hl
Remember that when loading data you cannot load a 1-byte value into a 2-byte register or vice versa. Also, please note that you cannot load the value of one two byte register into another two byte register. Instead, you have to load them by loading their 1-byte register components. For example:
Invalid Valid
ld hl, de ld h, d
ld l, e
The ld instruction is all that you need in order to create a pointer. Let’s have a look at the code below:
ld a, 0
ld (curCol), a
ld a, 0
ld (curRow), b
ld hl, TextMessage
bcall(_PutS)
ret
TextMessage:
.db “Hello!”, 0
.end
End
This code has a lot of elements you will not yet be familiar with. Let’s work with what you do know. The ld instruction. You also know what curCol and curRow are. So, what we first do is load 0 into the register a, then load that into (curCol). Note that you cannot load a value directly into RAM. It must be passed through a register. We repeat the loading for curRow, so that we now have our cursor set to (0,0). But, what is that we are seeing next? A pointer is being created. The pointer is now hl, which holds the address of “TextMessage”. Note that “TextMessage” will have a 2-byte address and that is why we use hl. A pointer of any kind will always require two bytes. The next thing you see is a system routine, known as a base call. It places a string on the screen, at cursor position indicated by the system variables curCol and curRow, with the string to display pointed to by hl. Remember when I said that a lot of system routines require arguments or pointers passed into various locations. The _PutS routine displays until it reaches the end of the string. In this case, the routine reads the 0 at the end to represent the end of the string. The last command you see is ‘ret’ which simply stops the program. It has a more complex purpose, but for now all you need to know is that is what it does here. After that, you have your data and your two END directives. We use two because of a assembler bug that causes the last line of your program to be ignored.
Sometimes, you may wish to perform a ld instruction multiple times, for instance, to copy a block of memory. In this case, there is a variation on the ld instruction, ldir. This instruction loads the value at the address pointed to by hl into the address pointed to by de, then increases both hl and de, then decreases bc. It then continues to repeat until bc is equal to 0. So, let us say you wanted to copy the ten bytes starting at memory location $1120 to memory location $2316. You could use the following code:
ld hl, $1120
ld de, $2316
ld bc, 9
ldir
It is also possible to save the value of a register and get it later. There is something called the “stack” in your calculator, which is a list of values. When you need to retain the value of a register, you can place the value onto the stack. Use the push command for that. Retrieve the value with the pop command. Note, that you need to push a 2-byte register and pop a 2-byte register. Also, if you push hl, you are not obligated to pop hl. That can be used to switch the values of two registers. The ex command can accomplish this as well, but it has very few arguments. Let us suppose that you wanted to swap the values of de and bc, which ex does not support:
push de
push bc
pop de
pop bc
This works because entries are pushed onto the stack in chronological order. So, in the above example, the value of de is pushed before the value of bc. Below, you will see what happens to the stack with the commands above.
value of ‘de’
Previous value
Previous value
Bottom of Stack
push de →
Value of ‘bc’
Value of ‘de’
Previous value
Previous value
Bottom of Stack
push bc →
Value of ‘bc’
Value of ‘de’
Previous value
Previous value
Bottom of Stack
pop de →
**former ‘bc’ is now
in ‘de’
Value of ‘de’
Previous value
Previous value
Bottom of Stack
pop bc →
**former ‘de’ is now
in ‘bc’
This is a very useful technique, as it gives you liberty as to using registers in routines. Oftentimes, if I need to use a register in a routine, I’ll push the register before calling the routine, then pop the value back into the register afterwards. Also, I use this as a great method for collaborating work with others. When several people are doing different parts of a program, sometimes it can be difficult to communicate what data is needed. But, it becomes easier when you provide the data on the stack. For instance, in my Legend of Zelda project, I had another programmer working on artificial intelligence. Instead of having him reload the map data, find the correct screen, then read it for enemies, I pushed a pointer to the map onto the stack. Thus, my helper only need pop the value, saving us both a lot of work.
Controlling Program Flow
How do we control what a program does when? How do we have it do something only if a condition is true. This is one of the most complex facets of assembly, and so we will address it now. Best to get this out of the way. Since it is important to grasp these concepts in a certain order, I will break this section down into several topics. First, we will learn about the number system in assembly and the flags. Then, we will move to conditionals and how to use them. Then, we will discuss several commands that control program flow.
The assembly Number System
Counting in assembly is pretty simple. There are no decimals, only integers. You count from 0 up until…well, how high can you count? Remember that in assembly, you can only hold one- or two- byte numbers. A one-byte number can go from 0 to 255 or from -128 to 127 in signed mode. A two-byte number can go from 0 to 65,536 or from -32, 768 to 32,768 in signed mode. So, what happens if you add 1 to the one-byte number 255? Will the calculator self-destruct? Will it take over the world? No! It simply cycles back to 0. Add 1 to 127 in signed mode and you get -128. Why does this happen?
$FF is the hexadecimal version of the one-byte value 255. If you add 1 to this, the result is $0100. That is a two-byte value. But when you are only supporting one-byte arithmetic, the second byte is dropped and only the $00 is retained. Hence, you cycle back to 0.
The Flags
Whenever we do any sort of instruction that adds or subtracts, the flags are changed. The flags are held in the ‘f’ register. Please do not confuse these flags with the system flags. To preserve the flag values into a routine, push the ‘af’ register, then pop it when you need them. Each bit in the ‘f’ register means something. Below, you will see them all. Please note that bits are numbered from right to left. Thus the left-most bit is bit 7 and the right-most bit is bit 0.
7th bit Signed flag (S). Determines whether the accumulator ended up positive (1), or negative (0). This flag assumes that the accumulator is signed.
6th bit Zero Flag (Z). Determines whether the accumulator is zero (z) or not (nz). This is the flag you will use a lot.
5th bit The 5th bit of the last 8-bit instruction that altered flags. You will probably not use this.
4th bit Half-carry (H). Is set when the least-significant nibble overflows.
3th bit The 3rd bit of the last 8-bit instruction that altered flags.
2nd bit Parity/Overflow (P/V). Either holds the results of parity or overflow. It's value depends on the instruction used. Is used for signed integers.
1st bit Add/Subtract (N). Determines what the last instruction used on the accumulator was. If it was add, the bit is reset (0). If it was subtract, the bit is set (1).
0th bit Carry (C). Determines if there was an overflow (c) or not (nc). Note that it checks for unsigned values. The carry flag is also set if a subtraction results in a negative number. You will also use this flag a lot.
Conditionals
One of the advantages of using TI-Basic over assembly is that you can use an If-Then statement to control your program flow. In assembly, you cannot. There is no command-based support for testing conditionals. Instead, you must do some sort of math, check the correct flag, and then do something based on the value of the flag. In TI-Basic, you can simply do If A=5. In assembly, it is so much more complex. You must realize that if you subtract 5 from the ‘a’ register when it equals 5, the result will be 0 and will be non-zero otherwise. Luckily, assembly does provide a command that compares two values. The cp command compares a value with the ‘a’ register, without changing the value in the ‘a’ register. So, a cp 5 will compare 5 to the value in a and will alter the flags as necessary. The zero will be set (z) if the result was 0 or reset (nz) if the result was non-zero. So, the following code will be necessary to achieve If A=5.
cp 5
jr z, DothisifA=5
We will discuss the command jr later.
Similarly, we could do the same for the carry flag. In games, where you need to check to see if the player’s health drops below zero, the carry flag is perfect. The carry flag will be set if the result of an operation has “carried” to the other end of the cycle. If we are at 255 and add 1, the result will be 0, and the carry flag will be set. An example of the use of the carry flag is the following code:
ld a, 255
add a, 1
jr c, Dothisifcarryset
Controlling Program Actions
We have just learned how to use the flags register to determine if certain conditions about a calculation are true. Now, we will learn how to determine what parts of a program get executed if said condition is true. There are several commands that control what a program does. This first are the jp and jr set. The first one is an absolute jump. The second is a relative jump. Let’s start with the relative jump, jr. This jump moves to a spot in memory relative to the current location. It is a one-byte instruction, and as such it can only jump 128 bytes in either direction. It is also slower that it’s alternative. When you can, use jr to save memory. Jp is a two-byte instruction and it can jump anywhere in memory, even outside the program (though I do not recommend this). The arguments for both commands are only the pointer to the place you want to jump.
There is another command that you can use in a similar manner to the jumps. It is the call. There is one major difference. When you call a routine, the next time the program encounters a ret (return), it will jump back to spot where it encountered the call instruction and continue. Remember earlier, when I said that ret had a more complex purpose than to simply exit a program? Well here it is. Ret and call are opposites. The call command pushes the ‘pc’ (program counter – indicates what location in memory the calculator is executing from) register onto the stack, then loads the address called into ‘pc’. The ret command pops the prior executing location into ‘pc’, causing a jump back to that address. This differs from the jp/jr set, which you cannot ret from. When ret is used in the main program, there are no other executing addresses pushed, so it exits the program.
All four instructions, jr, jp, ret, and call can take flag-based arguments to make them conditional. The following four commands are valid, as will they be with any of the flags.
jp z, Address ;jump to Address if the zero flag is set
jr nz. Address ;jump to Address if the zero flag is reset
call c, Address ;call Address if the carry flag is set
ret nc ;return if the carry flag is reset
Rendering Text and Images
Before we get into the ways to display text and images, I just want to rehash what I’ve said once before. Data, whether it be text or numbers or images, is just data. It is up to the user to handle it properly.
Let’s discuss naming conventions. In a assembly program, you are free to name your code segments or data anything you want. Yes, that’s right! Even code segments. If you write a routine that has to do with moving a variable, you may call it anything you want. Think of the this name in the same way you would think of the Lbl command in TI-Basic, because it acts in the same way, marking a location in the program.
Displaying Text
The “text” variable class includes letters, numbers, and any ASCII symbols. The display of text is not difficult at all. It requires something to display, a cursor or pen position, and a system call. Before we discuss how to achieve the display, let’s visit the system routines that are needed to display text. Note that these routines should be called with the instruction bcall(.
_PutS: Places a zero-terminated string on the screen, using the large font, at the current cursor location. Accepts a pointer to the string to display in ‘hl’. Advances the cursor to the next free coordinate.
_PutC: Places a character on the screen, using the large font, at the current cursor location. Accepts the ASCII (hex) code of the character in ‘a’. Advances the cursor to the next free coordinate.
_PutMap: The same as _PutC, except it does not advance the cursor.
_vPutS: Places a zero-terminated string on the screen, using the small font, at the current pen position. Accepts a pointer to the string to display in ‘hl’.
_vPutMap: Places a character on the screen using the small font, at the current pen position. Accepts the ASCII (hex) code of the character in ‘a’.
Also important to the display of text are the four system variables: curCol, curRow, penCol, and penRow. You should load the text position you desire into these variables and they will move the cursor and the pen, respectively. Note that when displaying a string, the position you indicate will be where the string STARTS being displayed.
Displaying Images
There are several ways to display images, but they all have some things in common. First off, they require data; referred to as a bitmap. A bitmap controls the screen because, when passed though the LCD port, it indicates which pixels are turned on and which are turned off. You will read more about using this in a later section about graphics. For now, all we will discuss is how to achieve an image display...
Note...as I complete sections, I'll upload them to this post.