Skip to navigation


Maths (Arithmetic): ADD

[Commodore 64 version]

Name: ADD [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate (A X) = (A P) + (S R) Deep dive: Adding sign-magnitude numbers
Context: See this subroutine in context in the source code References: This subroutine is called as follows: * DIALS (Part 2 of 4) calls ADD * MVS5 calls ADD * PIX1 calls ADD * PLS22 calls ADD * STARS1 calls ADD * STARS2 calls ADD * STARS6 calls ADD * WARP calls ADD

Add two 16-bit sign-magnitude numbers together, calculating: (A X) = (A P) + (S R)
.ADD STA T1 ; Store argument A in T1 AND #%10000000 ; Extract the sign (bit 7) of A and store it in T STA T EOR S ; EOR bit 7 of A with S. If they have different bit 7s BMI MU8 ; (i.e. they have different signs) then bit 7 in the ; EOR result will be 1, which means the EOR result is ; negative. So the AND, EOR and BMI together mean "jump ; to MU8 if A and S have different signs" ; If we reach here, then A and S have the same sign, so ; we can add them and set the sign to get the result LDA R ; Add the least significant bytes together into X: CLC ; ADC P ; X = P + R TAX LDA S ; Add the most significant bytes together into A. We ADC T1 ; stored the original argument A in T1 earlier, so we ; can do this with: ; ; A = A + S + C ; = T1 + S + C ORA T ; If argument A was negative (and therefore S was also ; negative) then make sure result A is negative by ; OR'ing the result with the sign bit from argument A ; (which we stored in T) RTS ; Return from the subroutine .MU8 ; If we reach here, then A and S have different signs, ; so we can subtract their absolute values and set the ; sign to get the result LDA S ; Clear the sign (bit 7) in S and store the result in AND #%01111111 ; U, so U now contains |S| STA U LDA P ; Subtract the least significant bytes into X: SEC ; SBC R ; X = P - R TAX LDA T1 ; Restore the A of the argument (A P) from T1 and AND #%01111111 ; clear the sign (bit 7), so A now contains |A| SBC U ; Set A = |A| - |S| ; At this point we have |A P| - |S R| in (A X), so we ; need to check whether the subtraction above was the ; right way round (i.e. that we subtracted the smaller ; absolute value from the larger absolute value) BCS MU9 ; If |A| >= |S|, our subtraction was the right way ; round, so jump to MU9 to set the sign ; If we get here, then |A| < |S|, so our subtraction ; above was the wrong way round (we actually subtracted ; the larger absolute value from the smaller absolute ; value). So let's subtract the result we have in (A X) ; from zero, so that the subtraction is the right way ; round STA U ; Store A in U TXA ; Set X = 0 - X using two's complement (to negate a EOR #$FF ; number in two's complement, you can invert the bits ADC #1 ; and add one - and we know the C flag is clear as we TAX ; didn't take the BCS branch above, so the ADC will do ; the correct addition) LDA #0 ; Set A = 0 - A, which we can do this time using a SBC U ; subtraction with the C flag clear ORA #%10000000 ; We now set the sign bit of A, so that the EOR on the ; next line will give the result the opposite sign to ; argument A (as T contains the sign bit of argument ; A). This is the same as giving the result the same ; sign as argument S (as A and S have different signs), ; which is what we want, as S has the larger absolute ; value .MU9 EOR T ; If we get here from the BCS above, then |A| >= |S|, ; so we want to give the result the same sign as ; argument A, so if argument A was negative, we flip ; the sign of the result with an EOR (to make it ; negative) RTS ; Return from the subroutine