z80:Data Manipulation and Basic Math
If you're ever going to get anything do in assembly, you'll probably find yourself having to manipulate data and do basic math on your calculator. However, doing math in assembly is a lot harder than doing math on the home screen or in a TI-BASIC program. For the most part you'll do math on the byte level. This leaves a lot to be desired, but for now let's start at the most basic part of doing math in assembly.
Data manipulation is the process of changing data to fit the needs of the programmer. There are a few elementary ways of changing data, and also advanced ways to manipulate data. For now, we'll focus on manipulating on the byte level and higher.
One way to add is to just increase a number by one. The instruction to do this is "inc." It stands for increase and this is it's syntax:
Inc A Inc HL Inc (HL)
Inc will add 1 to a register, register pair, or (HL). So if A=9 then after this instruction, A=10.
Increasing a number wont get you very far if you want to do some serious adding. So the next instruction is "add." It's syntax is as follows:
Add A, $06 Add A, B Add A,(HL)
What this does is adds either a register, a constant, or what HL is pointing to, to A and in the end, the final answer is in A.
Add HL, DE
This adds BC or DE to HL.
Ld B, 0 Ld C, A Add HL, BC
This adds A to HL.
Add A, E Ld E, A Adc A, D Sub E Ld D, A
This is a better version of the previous code. It can add A to HL, BC, or DE, it doesn't require that you destroy the data in BC or DE, and it takes 2 clock cycles less. The only disadvantage is that it takes up 1 more byte.
just like increasing, decreasing only subtracts 1. It works just like Inc too.
Dec A Dec HL Dec (HL)
Dec subtracts 1 from registers, register pairs, and (HL). So if A=10, then after this instruction, A=9.
The instruction is called sub, but it works differently. It only takes one input because it always subtracts from the accumulator.
Sub B Sub (HL)
The first code subtracts B from A. The second code subtracts the value pointed to by HL from A.
- It is also worth noting that adding the negation of a number to another number is the equivalent of subtraction. Say, for instance that you wish to subtract 7 from 12.
ld a,12 ld b,-7 add a,b ;result, 5, is in a
This is where actually coding gets sketchy. Multiplication really depends on the goals you want to achieve. Here are a few general ways to multiply 8 bit and 16 bit registers.
The simplest way to multiply. It works all the time (mostly because it's the definition of multiplication).
LD A, 6 ;multiply 6*6 LD B,6 Multiply: ADD A,6 DJNZ Multiply
Extremely simple, and for the most part a rather small size. However, what it lacks in complexity it more than makes up in its slowness. There are special ways you can get around it. One is if you know what one of the numbers you are multiplying by is going to be (like 8*X, or 4*X) you can write a custom routine that will be faster, and many times even smaller.
;multiply by 4 ADD A,A ;A+A=2A ADD A,A ;'2A'+'2A'='4A' ;multiply by 6 ADD A,A LD B,A ADD A,A ADD A,B
An equally simple way to multiply is to shift the bits of a register. How does this work?
Let's take the number 4. In binary, it is %00000100. If we shift it left, it will become %00001000. Hey, isn't that the number 8? So, shifting an operator left allows you to multiply a number by 2. Well, that's quite lame if all we can do is multiply a number by powers of 2! Or is it? Remember, the z80 processor (in fact, all modern computers) are binary based. And also, the shift operators are extremely fast! They can make the difference in crucial code that needs to go as fast as possible.
The shift left operators are SLA and SLL. There is quite a difference between the two, such as SLA will put the 7th bit into the carry, while SLL will destroy it. Also, SLL will put a 1 into the 0th bit, as opposed to a 0 from SLA.
Division is a bit trickier. Again, it all depends on what you want to get done. Also, remember that there are no decimals in registers, so your division will either be rounded off or you can create code that will give you a remainder. Here are a few examples of code that will divide a register by a number.
If you can shift an operator left, you can also shift it right. Note that there are two shift operators: SRA and SRL. They both shift to the right, but the difference is that SRA preserves the 7th bit, which holds the sign (1=negative, 0=positive). SRL will shift all 8 bits, so should be used if your number is to be unsigned.
Other Division Methods
Here is a routine that will give you an idea on dividing registers.
;=================================== ; Divide ;=================================== ; ;Inputs: ; A=divisor ; C=dividend ; ;Outputs: ; B=A/C ; A=A%C (the remainder) ; Divide: Ld B,0 DivLoop: sub A,C jr c,DivEnd inc B jr DivLoop DivEnd: add a,c ret