**Appendix D**

A Library of

Subroutines

A Library of

Subroutines

Here is a collection of techniques you'll need to use in many of your ML programs. Those techniques which are not inherently easy to understand are followed by an explanation.

**Increment and Decrement Double Byte Numbers**

You'll often want to raise or lower a number by 1. To increment a number, you add 1 to it: Incrementing 5 results in 6. Decrement lowers a number by 1. Single-byte numbers are easy; you just use INC or DEC. But you'll often want to increment two-byte numbers which hold addresses, game scores, pointers, or some other number which requires two bytes. Two bytes, ganged together and seen as a single number, can hold values from 0 ($0000) up to 65535 ($FFFF). Here's how to raise a two-byte number by 1, to increment it:

(Let's assume that the number you want to increment or decrement is located in addresses $0605 and $0606, and the ML program segment performing the action is located at $5000.)

**5000 INCREMENT INC $0605; raise the low byte**

5003 BNE GOFORTH; if not zero, leave high byte alone

5005 INC $0606; raise high byte

5008 GOFORTH ... continue with program

5003 BNE GOFORTH; if not zero, leave high byte alone

5005 INC $0606; raise high byte

5008 GOFORTH ... continue with program

The trick in this routine is the BNE. If the low byte isn't raised to zero (from 255), we don't need to add a "carry" to the high byte, so we jump over it. However, if the low byte does turn into a zero, the high byte must then be raised. This is similar to the way an ordinary decimal increment creates a carry when you add 1 to 9 (or 99 or 999). The lower number turns to zero, and the next column over is raised by one.

To double decrement, you need an extra step. The reason it's more complicated is that the 6502 chip has no way to test if you've crossed over to $FF, down from $00. BNE and BEQ will test if something is zero, but nothing tests for $FF. (The N flag is turned on when you go from $00 to $FF, and BPL or BMI could test it.) The problem with it, though, is that the N flag isn't limited to sensing $FF. It is sensitive to any number higher than 127 decimal ($7F).

So, here's the way to handle double-deckers:

**5000 LDA $0605; load in the low byte (affecting the zero flag)**

5003 BNE FIXLOWBYTE; if it's not zero, lower it, skipping high byte

5005 DEC $0606, zero in low byte forces this.

5008 FIXLOWBYTE DEC $0605, always dec the low byte.

5003 BNE FIXLOWBYTE; if it's not zero, lower it, skipping high byte

5005 DEC $0606, zero in low byte forces this.

5008 FIXLOWBYTE DEC $0605, always dec the low byte.

Here we always lower the low byte, but lower the high byte only when the low byte is found to be zero. If you think about it, that's the way any subtraction would work.

**Comparison**

Comparing a single-byte against another single-byte is easily achieved with CMP. Double-byte comparison can be handled this way:

(Assume that the numbers you want to compare are located in addresses $0605,0606 and $0700,0701. The ML program segment performing the comparison is located at $5000.)

**5000 SEC**

5001 LDA $0605; low byte of first number

5004 SBC $0700; low byte of second number

5007 STA $0800; temporary holding place for this result

500A LDA $0606; high byte of first number

500D SBC $0701; high byte of second number, leave result in A

5010 ORA $0800; results in zero if A and $0800 were both zero.

5001 LDA $0605; low byte of first number

5004 SBC $0700; low byte of second number

5007 STA $0800; temporary holding place for this result

500A LDA $0606; high byte of first number

500D SBC $0701; high byte of second number, leave result in A

5010 ORA $0800; results in zero if A and $0800 were both zero.

The flags in the Status Register are left in various states after this routine-you can test them with the B instructions and branch according to the results. The ORA sets the Z (zero) flag if the results of the first subtraction (left in $0800) and the second subtraction (in A, the Accumulator) were both zero. This would only happen if the two numbers tested were identical, and BEQ would test for this (Branch if EQual).

If the first number is lower than the second, the carry flag would have been cleared, so BCC (Branch if Carry Clear) will test for that possibility. If the first number is higher than the second, BCS (Branch if Carry Set) will be true. You can therefore branch with BEQ for =, BCC for <, and BCS for >. Just keep in mind which number you are considering the first and which the second in this test.

**Double-Byte Addition**

CLC ADC and EC SBC will add and subtract one-byte numbers. To add two-byte numbers, use:

(Assume that the numbers you want to add are located in addresses $0605,0606 and $0700,0701. The ML program segment performing the addition is located at $5000.)

**5000 CLC; always do this before any addition**

5001 LDA $0605

5004 ADC $0700

5007 STA $0605; the result will be left in $0605,0606

500A LDA $0606

500D ADC $0701

5010 STA $0606

5001 LDA $0605

5004 ADC $0700

5007 STA $0605; the result will be left in $0605,0606

500A LDA $0606

500D ADC $0701

5010 STA $0606

It's not necessary to put the result on top of the number in $0605,0606-you can put it anywhere. But you'll often be adding a particular value to another and not needing the original any longer-adding ten points to a score for every blasted alien is an example. If this were the case, following the logic of the routine above, you would have a 10 in $0701, 0702:

**0701 0A; the 10 points you get for hitting an alien**

0702 00

0702 00

You'd want that 10 to remain undisturbed throughout the game. The score, however, keeps changing during the game and, held in $0605,0606, it can be covered over, replaced with each addition.

**Double-Byte Subtraction**

This is quite similar to double-byte addition. Since subtracting one number from another is also a comparison of those two numbers, you could combine subtraction with the double-byte comparison routine above (using ORA). In any event, this is the way to subtract double-byte numbers. Be sure to keep straight which number is being subtracted from the other. We'll call the number being subtracted the second number.

(Assume that the number you want to subtract [the "second number"] is located in addresses $0700,0701, and the number it is being subtracted from [the "first number"] is held in $0605,0606. The result will be left in $0605,0606. The ML program segment performing the subtraction is located at $5000.)

**5000 SEC; always do this before any subtraction**

5001 LDA $0605; low byte of first number

5004 SBC $0700; low byte of second number

5007 STA $0605; the result will be left in $0605,0606

500A LDA $0606; high byte of first number

500D SBC $0701; high byte of second number

5010 STA $0606; high byte of final result

5001 LDA $0605; low byte of first number

5004 SBC $0700; low byte of second number

5007 STA $0605; the result will be left in $0605,0606

500A LDA $0606; high byte of first number

500D SBC $0701; high byte of second number

5010 STA $0606; high byte of final result

**Multi-Byte Addition and Subtraction**

Using the methods for adding and subtracting illustrated above, you can manipulate larger numbers than can be held within two bytes (65535 is the largest possible two-byte integer). Here's how to subtract one four-byte-long number from another. The locations and conditions are the same as for the two-byte subtraction example above, except the "first number" (the minuend) is held in the four-byte chain, $0605,0606,0607,0608, and the "second number" (the subtrahend, the number being subtracted from the first number) is in $0700,0701,0702,0703.

Also observe that the most significant byte is held in $0703 and $0608. We'll use the Y Register for Indirect Y addressing, use four bytes in zero page as pointers to the two numbers, and use the X Register as a counter to make sure that all four bytes are dealt with. This means that X must be loaded with the length of the chains we're subtracting-in this case, 4.

**5000 LDX #4; length of the byte chains**

5002 LDY #0, set Y

5004 SEC; always before subtraction

5005 LOOP LDA (FIRST),Y

5007 SBC (SECOND),Y

5009 STA (FIRST),Y; the answer will be left in $0605-0608.

500B INY; raise index to chains

500C DEX; lower counter

5010 BNE LOOP; haven't yet done all four bytes

5002 LDY #0, set Y

5004 SEC; always before subtraction

5005 LOOP LDA (FIRST),Y

5007 SBC (SECOND),Y

5009 STA (FIRST),Y; the answer will be left in $0605-0608.

500B INY; raise index to chains

500C DEX; lower counter

5010 BNE LOOP; haven't yet done all four bytes

Before this will work, the pointers in zero page must have been set up to allow the Indirect Y addressing. This is one way to do it:

**2000 FIRST = $FB; define zero page pointers at $FB and $FD**

2000 SECOND = $FD

2000 SETUP LDA #5; set up pointer to $0605

2002 STA FIRST

2004 LDA #6

2006 STA FIRST+1

2008 LDA #0; set up pointer to $0700

200A STA SECOND

200C LDA #7

200E STA SECOND+1

2000 SECOND = $FD

2000 SETUP LDA #5; set up pointer to $0605

2002 STA FIRST

2004 LDA #6

2006 STA FIRST+1

2008 LDA #0; set up pointer to $0700

200A STA SECOND

200C LDA #7

200E STA SECOND+1

**Multiplication**

**× 2**

ASL (no argument used, "Accumulator addressing mode") will multiply the number in the Accumulator by 2.

**× 3**

(To multiply by 3, use a temporary variable byte we'll call TEMP.)

**5000 STA TEMP; put the number into the variable**

5003 ASL; multiply it by 2

5004 ADC TEMP; (X * 2 + X = X * 3) the answer is in A.

5003 ASL; multiply it by 2

5004 ADC TEMP; (X * 2 + X = X * 3) the answer is in A.

**× 4**

(To multiply by 4, just ASL twice.)

**5000 ASL; * 2**

5001 ASL; * 2 again

5001 ASL; * 2 again

**× 4 (two byte)**

(To multiply a two-byte integer by 4, use a two-byte variable we'll call TEMP and TEMP+1.)

**5000 ASL TEMP; multiply the low byte by 2**

5003 ROL TEMP+1; moving any carry into the high byte

5006 ASL TEMP, multiply the low byte by 2 again

5009 ROL TEMP+1; again acknowledge any carry.

5003 ROL TEMP+1; moving any carry into the high byte

5006 ASL TEMP, multiply the low byte by 2 again

5009 ROL TEMP+1; again acknowledge any carry.

**× 10**

(To multiply a two-byte integer by 10, use an additional twobyte variable we'll call STORE.)

**5000; first put the number into STORE for safekeeping**

5000 LDA TEMP:STA STORE:LDA TEMP+1:STA STORE+1

500C; then multiply it by 4

500C ASL TEMP; multiply the low byte by 2

500F ROL TEMP+1; moving any carry into the high byte

5012 ASL TEMP; multiply the low byte by 2 again

5015 ROL TEMP+1; again acknowledge any carry.

5018; then add the original, resulting in X * 5

5018 LDA STORE

501B ADC TEMP

501E STA TEMP

5021 LDA STORE+1

501D ADC TEMP+1

5024 STA TEMP+1

5027; then just multiply by 2 since (5 * 2 = 10)

5027 ASL TEMP

502A ROL TEMP+1

5000 LDA TEMP:STA STORE:LDA TEMP+1:STA STORE+1

500C; then multiply it by 4

500C ASL TEMP; multiply the low byte by 2

500F ROL TEMP+1; moving any carry into the high byte

5012 ASL TEMP; multiply the low byte by 2 again

5015 ROL TEMP+1; again acknowledge any carry.

5018; then add the original, resulting in X * 5

5018 LDA STORE

501B ADC TEMP

501E STA TEMP

5021 LDA STORE+1

501D ADC TEMP+1

5024 STA TEMP+1

5027; then just multiply by 2 since (5 * 2 = 10)

5027 ASL TEMP

502A ROL TEMP+1

**× ?**

(To multiply a two-byte integer by other odd values, just use a similar combination of addition and multiplication which results in the correct amount of multiplication.)

**× 100**

(To multiply a two-byte integer by 100, just go through the above subroutine twice.)

**× 256**

(To multiply a one-byte integer by 256, just transform it into a two-byte integer.)

**5000 LDA TEMP**

5003 STA TEMP+1

5006 LDA #0

5008 STA TEMP

5003 STA TEMP+1

5006 LDA #0

5008 STA TEMP

**Division**

**÷ 2**

LSR (no argument used, "Accumulator addressing mode") will divide the number in the Accumulator by 2.

**÷ 4**

(To divide by 4, just LSR twice.)

**5000 LSR; / 2**

5001 LSR; / 2 again

5001 LSR; / 2 again

**÷ 4 (two byte)**

(To divide a two-byte integer, called TEMP, by 2)

**5000 LSR TEMP+1; shift high byte right**

5001 ROR TEMP; pulling any carry into the low byte

5001 ROR TEMP; pulling any carry into the low byte

Return to Table of Contents | Previous Chapter | Next Chapter