This lesson will handle about some more instructions that we haven't talked about before: Block instructions
. This are instructions that combine a bunch of other instructions into one handy operation. This results in byte savings and faster execution.
LDI and LDIR |
LDD and LDDR |
CPI and CPIR |
CPD and CPDR |
More block instructions
LDI and LDIR
The LDI instruction is equivalent to the following structure:
But instead of 7 bytes (that's the ammount of bytes the previous piece of code takes in), LDI only takes 2 bytes and the execution speed of LDI is also a lot faster.
The LDI instruction affects the Parity/Overflow flag only. If the result of BC-1=0, then the P/V flag will be reset to zero, otherwise P/V will be set to 1.
LDIR is the same as LDI, but here the LDI instruction is repeated until BC=0. At the end of instruction, the P/V flag will be 0.
So what can LDIR be used for? Well, most of the time it is used to copy a series of bytes. Take for example that you want to copy the contents of the GRAPH_MEM to the APD_BUF. Then you'll use the piece of code below.
Notice that we set BC to 768. This is the amount of bytes that we need to copy. The LDI instruction will be executed 768 times.
Another routine where the use of LDIR is very handy is the following:
This routine will clear the GRAPH_MEM. It does this by first writing a zero to the first byte of GRAPH_MEM and then copying this byte one byte further every time. Think about it.
Please note that BC needs to be set to 767 and NOT to 768 here!! When you would use 768, the last byte would be copied outside of the GRAPH_MEM. Many programmers make this mistake. Luckily the byte just behind GRAPH_MEM is not that important. If it was, using 768 as your BC could cause a crash on your calculator.
LDD and LDDR
The LDD instruction is very similar to the LDI instruction. The replacement code for LDD could be:
As you see, instead of increasing both DE and HL registers, they are decreased.
The settings of the flags are the same as with the LDI instruction. After LDD, the P/V flag will be 0 if BC-1=0 and P/V will be 1 otherwise.
As you can guess, LDDR will do the same as LDIR, but backwards. So if you want to clear the GRAPH_MEM with LDDR, you'll need to use the following code:
Now you could ask yourself why you need LDDR is you already can use the LDIR instruction.
Well, suppose you want to copy the data from $8000-$9000 to $8500-$9500. When you would do this with LDIR, the first $500 bytes will be OK, but the last $500 bytes will be wrong. You'll be copying $8000-$8500 to both $8500-$9000 and $9000-$9500.
When you use LDDR instead, you won't have this problem, because $8500-$9000 is already copied to $9000-$9500 before you overwrite it with $8000-$8500.
CPI and CPIR
The CPI instruction is equivalent to the following series of instructions:
So this means that the value of A is compared to the value at the memory location with the address stored in HL. After comparing, HL is increased and BC is decreased.
Accordingly to the LDI and LDD instructions, the P/V flag will be 0 if the resulot of BC-1 is 0 and 1 otherwise. The Z flag will also be affected: if A=(HL), the Z flag will be set to 1 and if A is not equal to (HL), the Z flag will be reset to zero.
Just like with LDI and LDD, you will not often use this instruction. Instead, you will want to use the CPIR instruction once in a while.
The CPIR instruction is almost the same as the CPI one. Thye only difference is that CPIR will keep looping until BC=0 or A=(HL). The Z and P/V flags are set in the same way as with CPI.
An example of CPIR: suppose HL points to the middle of a zero-terminated string and you want to find the address of the string that comes immediately after that string. Here's a way to do it (supposing the string is less than $10 bytes long):
XOR A ; = LD A,0
.db "This is a "
.db "This is another string",0
This will look for the next zero byte. If the zero byte was found, HL will point to the byte immediately after that zero byte (and NOT to the zero byte itsself) and thus to the next string.
Another string: suppose you have a table of data with length stored in DE and beginning at GRAPH_MEM. Suppose you have received A from the input and you want to check if A is in the table.
POP BC ; BC = DE = length of table
CPD and CPDR
The CPD instruction is equivalent to the following series of instructions:
This means that the value of A is compared to the value at the memory location with the address stored in HL. After comparing, both HL and BC are is decreased.
Accordingly to the CPI instruction, the P/V flag will be 0 if the resulot of BC-1 is 0 and 1 otherwise. The Z flag is also affected: if A=(HL), the Z flag will be set to 1 and if A is not equal to (HL), the Z flag will be reset to zero.
CPDR is a repeating CPD instruction that will continue until A=(HL) or BC=0. The flags are set in the same way as the CPD instruction.
An example: Suppose HL points to the middle of a string (which is not the first one in a series of strings) and you want HL to point to the beginning of that string. What will you do?
XOR A ; = LD A,0
INC HL ; point to zero byte
INC HL ; point to 1st non zero byte
.db "This is a string",0
.db "This is"
.db "another string",0
In this example, we supposed that there are not more than 10 bytes between HL and the beginning of the string and that there is a zero-terminated string immediately in front of the string we need.
Please note also that we still need to do INC HL twice after the CPDR instruction. Why? When we found the zero byte, we decreased HL once more, so increasing it again will make it point to the zero byte. Once more will make HL point to the frist byte of our string.
More block instructions
Yes, there are some more block instructions, but they are almost never used when doing TI-82 Z-80 Assembly. They can be useful for fast graphics, though.
We give a listing anyway to be complete.
OUTI, OTIR: used for sending output from the memory to ports and moving downwards in memory (increasing HL).
OUTD, OTDR: used for sending output from the memory to ports and moving upwards in memory (decreasing HL).
INI, INIR: used to get input from a port to the memory (increasing HL every time)
IND, INDR: used to get input from a port to the memory (decreasing HL every time)
Previous lesson |