by Terry Peng
- CP (Compare)
- Conditionals Table
- Loops
- Crashes
- DJNZ (Decrease and jump if not zero)
- Rotations
- RL,RLA (Rotate left)
- RLC,RLCA (Rotate left branch carry)
- RR,RRA (Rotate right branch carry)
- RRC,RRCA (Rotate right branch carry)
- Shifts
- SRL (Shift right logical)
- SRA (Shift right arithmatic)
- SLA (Shift left arithmatic)
- 16-bit rotate and shift
- Single Bit Instructions
- BIT (test bit)
- SET (set bit)
- RES (reset bit)
- Logical operators
- AND (Logical AND)
- OR (Logical OR)
- XOR (Logical Exclusive OR)
- General Purpose Instructions
- CPL (Complement)
- NEG (Negate)
- CCF (Complement Carry Flag)
- SCF (Set Carry Flag)
- Calls outside of your program
- Some shell history
- Comments on your first program
CP (Compare)
Well, I left off the last lesson talking about using math operations to
affect the flags, and thereby make conditional jumps or calls. Well, the
problem with this is, you change the accumulator, which you won't always
want to do. So there is a command specifically used with flags, CP (Compare).
CP is exactly the same as SUB except that CP doesn't affect the acc, only the
flags.
Now we can easily do all kinds of conditionals without having to change back
the accumulator.
Instructions |
Result |
cp b \ jr z,lbl |
jump to lbl if a = b |
cp b \ jr nz,lbl |
jump to lbl if a != b |
cp b \ jr c,lbl |
jump to lbl if a=a |
cp b+1 \ jr c,lbl |
jump to lbl if a<=b, ie b>a |
cp b+1 \ jr nc,lbl |
jump to lbl if a>b, ie b<=a |
cp b \ jr nc,lbl |
jump to lbl if a<=b, ie b
|
Loops
One of the most powerful things in programming is a loop. Loops can be good,
if you know how to use them. Note that we would never want to have a loop
that goes on forever, because then the program could never exit! You can't
simply press [on] and quit like in ti-basic.
Crashes
This brings up the subject of crashes: there are a few main causes of crashes
most of which I won't go into detail about just yet, but one of them is an
infinite loop. When this happens, don't fret; take out ONE AAA battery
and press [on] ten times.
Back to Loops
So how would you do a loop? Lets say you wanted to go through the loop 6
times. You should load a variable with 6, decrease it 6 times, and then jump
back to the beginning if the z flag isn't set, like so:
ld a,6 ;number of times to loop
Loop:
-code-
-code-
-code-
-code-
dec a ;decrease a once
jr nz,Loop ;if it isn't 0, go to loop
But there is an even easier way to do this! A special instruction djnz,
decreases b, and then does a relative jump if b is not zero. It always
works on register b.
ld b,6 ;number of times to loop
Loop:
-code-
-code-
-code-
-code-
djnz Loop ;decrease a once
;if it isn't 0, go to loop
So why would we even want to do it the first way (not DJNZ?) well, that is a good way to
do a FOR loop. You can choose whether you want the register to go up or down,
and by how much. You can end it with a CP so that the loop doesn't end at
zero!
ld a,3 ;start number
Loop:
-code-
-code-
-code-
-code-
add a,4 ;the change you want each loop
cp 23 ;the value that ends the loop
I believe this is equivalent to For(a,3,23,4) in ti-basic and
for( a = 3; a != 23; a = a + 4 ) in C/C++. Note that whenever you do a loop,
be careful not to change the register inside the loop!
Rotations
Require a parameter (register) BIT (CF= carry flag)
=========================================================
RL CF <- b7 b6 b5 b4 b3 b2 b1 b0 <- CF
RR CF -> b7 b6 b5 b4 b3 b2 b1 b0 -> CF
RLC CF <- b7 b6 b5 b4 b3 b2 b1 b0 <- b7
RRC b0 -> b7 b6 b5 b4 b3 b2 b1 b0 -> CF
RL = Rotate Left RLC = Rotate Left Branch Carry
RR = Rotate Right RRC = Rotate Right Branch Carry
Do not require a parameter
=========================================================
RLA-Rotate Left on the Acc
RRA-Rotate Right on the Acc
RLCA-Rotate Left Branch Carry on the Acc
RRCA-Rotate Right Branch Carry on the Acc
The chart is self explanatory. Each bit is basically rotated to the next
bit.
Shifts (all require a parameter register)
=========================================================
SRL zero -> b7 b6 b5 b4 b3 b2 b1 b0 -> CF
SRA b7 -> b7 b6 b5 b4 b3 b2 b1 b0 -> CF
SLA CF <- b7 b6 b5 b4 b3 b2 b1 b0 <- zero
SRL = Shift Right Logical
SRA = Shift Right Arithmetic
SLA = Shift Left Arithmetic
SRL
Shifts the bits right Logically. B7 is replaced by a zero. Don't use this
for math. Use for graphics and general stuff.
SRA
Shifts the bits right Arithmatically. Use this to divide by two. B7 is saved
so that the register will keep its negativity.
SLA
Shifts the bits left. Use to multiply by two, or for work with graphics.
Bit instructions
Bit has two parameters: the bit, and the register.
bit 7,a
would test bit 7 of register a. BIT works on all registers (not pairs).
BIT copies the bit in question to the zero flag. So if the bit=1 then
the zero flag=1. WATCH OUT! if the bit=0 the zero flag is RESET! don't think
that because it=0, the zero flag is set.
SET has two parameters: the bit, and the register.
set 7,a
would set bit 7 of register a. SET works on all registers (not pairs).
RES has two parameters: the bit, and the register.
bit 7,a
would reset bit 7 of register a. RES works on all registers (not pairs).
16 bit rotate and shift
The shift and rotate instructions are only for 8 bit registers. So how do
you shift/rotate a 16 bit register? Well, you have to put instructions
together.
to SRL BC:
srl b
rr c
to SRA BC:
sra b
rr c
to SLA BC:
sla c
rl b
to RR BC:
rr b
rr c
to RL BC:
rl c
rl b
to RRC BC:
srl b
rr c
jr nc,label1
set 7,b
label1:
to RLC BC:
sla c
rl b
jr nc,label2
set 0,c
label2:
If you study the bits, you will see that those combinations work.
Logical operators - AND
and b
This instruction takes the bits of the accumulator, and the bits of the
parameter, and it sees which bits are common to BOTH. The accumulator is
loaded with a new byte, with only the common bits set. Note that all
logical operators have the same possible parameters, and will change the
flags.
a = %01010101
b = %11110000
=============
new a = %01010000
You can also use it on straight data:
and 5
a = %01010101
5 = %00000101
=============
new a = %00000101
Remember that anytime the register b,c,d, or e can be used, so can (hl)
a = %01010101
(hl) = %10101010
=============
new a = %00000000
Uses
What use is AND? Well, there are many uses, but a common one is called a
MASK. Let's say that, you only want the three low bits of the acc. You
can AND %111 (same as %00000111), and only the bottom three bits will be
preserved! This would be called MASKING out the top five bits.
a = %qrstuwxyz
%111 = %000000111
==============
new a = %000000xyz
Logical operators - OR
OR is simialar to AND. OR takes the bits that are present in either register,
and it puts all those bits in the acc.
a = %01010101
b = %11110000
=============
new a = %11110101
a = %01010101
5 = %00000101
=============
new a = %01010101
a = %01010101
(hl) = %10101010
=============
new a = %11111111
Uses
Most uses of OR are in graphics, which you will learn about later.
Logical operators - XOR (exclusive or)
XOR is simialar to AND. XOR takes the bits that are present in either
register, but not both and it puts all those bits in the acc. So in other
words, the two bits being compare must a a 0 and a 1 for the new bit to
be set. An example illustrates best:
a = %01010101
b = %11110000
=============
new a = %10100101
a = %01010101
5 = %00000101
=============
new a = %01010000
a = %01010101
(hl) = %10101010
=============
new a = %11111111
Uses
One interesting use of XOR is crude encryption! Did you notice that if you
XOR the acc with a value, then XOR it again with the same value, the acc
goes back to normal? Because if the bit in the value is 0, then the acc's
bit won't be changed both times. But if the bit in the value is one, then
if the acc bit is 1, then 1 XOR 1 = 0 XOR 1 = 1
if the acc bit is 0, then 0 XOR 1 = 1 XOR 1 = 0
a = %01010101
Arbitrary value = %11100111
=============================
new a = %10110010
Arbitrary value = %11100111
=============================
new a = %01010101 matches the original value!
General Purpose Instructions
CPL (complement)
This reverses all the bits in the acc.
cpl
before: a=%11010101
after: a=%00101010
NEG (negate)
This negates the acc.
neg
before: a= 20
after: a= -20,duh
SCF (set carry flag)
This sets the carry flag. One good use for this is during a routine, to
specify an error, you could set the carry flag and return.
call &routine
jp c,&error_handler
CCF (complement carry flag)
I can only think of one good use for this (there must be more though).
Let's say that, during a routine, you have made a comparison. If the carry
flag isn't set, then you know there has been an error (in this specific
routine).
Well, if you put ret nc, then it would return, but with the carry flag reset,
signifying no error! So you can put
ccf
ret c
Note that you shouldn't use
scf
ccf
to reset the carry flag, use
or a
instead. It has no effect on the acc, but it resets the carry flag.
Calls outside your program
Now, you may be thinking, "OK, I know lots of instructions, and I know how to
use conditionals, form loops, etc. But how do I do stuff like graphics, and
other things (even simple things like clearing the screen) ?!" Well, now is
the time to do some research on your own. There are several documents about
certain calls named ROM CALLS. These are, calls to the ROM (duh). Since the
routines are not contained within your program, they do not take up any extra
space. There are also some special Usgard routines. These are routines stored
within Usgard, the shell.
Look in the C:\Usgard\docs\prgm directory. Carefully read over the
following docs:
zsfnlib.txt- A description of all the original zshell ROM CALLS
usgardfn.txt- A description of all Usgard routines and new ROM CALLS
You make a ROM CALL by typing
call ROM_ROUTINE
...don't use &.
One excellent source of information is ZShell40.hlp- This is a windows help
file. Just run it from Explorer/File Manager, or even make a shortcut.
You can find all sorts of good information at www.ticalc.org, if you search
the archives.
Some shell history
Well, as you probably know, the first ASM shell ever released for a TI calc
was Zshell. Zshell was great for its time, but lets face it, it is completely
obsolete with all the new shells (especially Usgard).
Something you might want to know about Zshell is that it had a different
way of doing ROM CALLS, CALLs, and JPs, and that is:
ROM_CALL(routine)
CALL_(label)
JUMP_(label)
Usgard also has extra features like libraries, interrupts, TSRs, more ROM
CALLS, routines inside Usgard, RELOCATION!, TI-OS variable access, user
selected interface, and even more that I probably can't remember right now.
You'll learn about all this good stuff later.
Zshell was much less effecient and powerful than Usgard, but it was an
amazing accomplishment for its time. Zshell 4.0, btw, was made by:
(in no particular order)
- Magnus Hagander
- Rob Taylor
- Dan Eble
Usgard 1.1 (the latest at the time of this writing) was(is) made by
- Andreas Ess
- Jimmy Mardell
- Sam Davies
- Austin Butler
- Mel Tsai
Comments on your first program
Finally, that sample program, explained!
#include "usgard.h"
;include all of the useful aliases
.org 0 ;put this at the beginning of program
.db "My first program",0 ;a description string, zero-terminated
call CLEARLCD ;this is a ROM CALL, that, you
;guessed it, clears the screen
ld hl,0 ;loads hl with 0
ld (CURSOR_ROW),hl ;loads 0 into the CURSOR_ROW and
ld hl,&Text ;hl now is "pointing at" the text string
call D_ZT_STR ;ROM CALL, display zero terminated string
Wait_4_key: ;a label
call GET_KEY ;load the code of last keypress into the acc
cp 0 ;is it 0? (0 means no key pressed)
ret nz ;if not, then return to usgard
jr Wait_4_key ;if so, then goto the Wait_4_Key label
Text: ;another label
.db "Hello World!",0 ;the string
.end ;end of program
;be sure to leave several blank lines after
;.end
|