ANESE/research/obelisk.me.uk/6502/algorithms.html
2017-11-12 12:48:01 -08:00

1281 lines
50 KiB
HTML
Vendored

<HTML>
<HEAD>
<TITLE>6502 Algorithms</TITLE>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<LINK REL="StyleSheet" HREF="../obelisk.css" TYPE="text/css" media="screen,print">
</HEAD>
<BODY>
<script type="text/javascript"><!--
google_ad_client = "pub-0826595092783671";
/* 6502 Header */
google_ad_slot = "9208748029";
google_ad_width = 728;
google_ad_height = 90;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<HR>
<H2><STRONG>Coding Algorithms</STRONG></H2>
<P>As you can see from the preceding descriptions the instruction set of the 6502
is quite basic, having only simple 8 bit operations. Complex operations such as
16 or 32 bit arithmetic and memory transfers have to be performed by executing
a sequence of simpler operations. This sections describes how to build these
algorithms and is based on code taken from my macro library (available from the <A href="downloads.html#source">
download section</A>).</P>
<P>If you find any bugs in the code, have routines to donate to the library, or can
suggest improvements then please mail me.</P>
<H3>Standard Conventions</H3>
<P>The 6502 processor expects addresses to be stored in 'little endian' order, with
the least significant byte first and the most significant byte second. If the
value stored was just a number (e.g. game score, etc.) then we could write code
to store and manipulate it in 'big endian' order if we wished, however the
algorithms presented here always use 'little endian' order so that they may be
applied either to simple numeric values or addresses without
modification.</P>
<dl>
<dd><p><i>The terms 'big endian' and 'little endian' come from Gulliver's
Travels. The people of Lilliput and Blefuscu have been fighting a war
over which end of an boiled egg one should crack to eat it. In computer terms it refers to whether the most or least significant&nbsp;
portion of a binary number is stored in the lower memory address.</i></p>
</dd>
</dl>
<P>To be safe the algorithms usually start by setting processor flags and registers
to safe initial values. If you need to squeeze a few extra bytes or cycles out
of the routine you might be able to remove some of these initializations
depending on the preceding instructions.</P>
<H3>Simple Memory Operations</H3>
<P>Probably the most fundamental memory operation is clearing an area of memory to
an initial value, such as zero. As the 6502 cannot directly move values to
memory clearing even a small region of memory requires the use of a register.
Any of A, X or Y could be used to hold the initial value, but in practice A is
normally used because it can be quickly saved and restored (with PHA and PLA)
leaving X and Y free for application use.</P>
<PRE>; Clearing 16 bits of memory
_CLR16 LDA #0 ;Load constant zero into A
STA MEM+0 ;Then clear the least significant byte
STA MEM+1 ;... followed by the most significant
; Clearing 32 bits of memory
_CLR32 LDA #0 ;Load constant zero into A
STA MEM+0 ;Clear from the least significant byte
STA MEM+1 ;... up
STA MEM+2 ;... to
STA MEM+3 ;... the most significant</PRE>
<P>Moving a small quantity of data requires a register to act as a temporary
container during the transfer. Again any of A, X, or Y may be used, but as
before using A as the temporary register is often the most practical.</P>
<PRE>; Moving 16 bits of memory
_XFR16 LDA SRC+0 ;Move the least significant byte
STA DST+0
LDA SRC+1 ;Then the most significant
STA DST+1
; Moving 32 bits of memory
_XFR32 LDA SRC+0 ;Move from least significant byte
STA DST+0
LDA SRC+1 ;... up
STA DST+1
LDA SRC+2 ;... to
STA DST+2
LDA SRC+3 ;... the most significant
STA DST+3</PRE>
<P>Provided the source and destination areas do not overlap then the order in which
the bytes are moved is irrelevant, but it usually pays to be consistent in your
approach to make mistakes easier to spot.</P>
<P>All of the preceding examples can be extended to apply to larger memory areas
but will generate increasingly larger code as the number of bytes involved
grows. Algorithms that iterate using a counter and use index addressing to
access memory will result in smaller code but will be slightly slower to
execute.</P>
<P>This trade off between speed and size is a common issue in assembly language
programming and there are times when one approach is clearly better than the
other (e.g. when trying to squeeze code into a fixed size ROM - SIZE, or
manipulate data during a video blanking period - SPEED).</P>
<PRE>; Clear 32 bits of memory iteratively
_CLR32C LDX #3
LDA #0
_LOOP STA MEM,X
DEX
BPL _LOOP
; Move 32 bits of memory iteratively
_XFR32C LDX #3
_LOOP LDA SRC,X
STA DST,X
DEX
BPL _LOOP</PRE>
<P>Another basic operation is setting a 16 bit word to an initial constant value.
The easiest way to do this is to load the low and high portions into A one at a
time and store them.</P>
<PRE>; Setting a 16 bit constant
_SET16I LDA #LO NUM ;Set the least significant byte of the constant
STA MEM+0
LDA #HI NUM ;... then the most significant byte
STA MEM+1</PRE>
<h3>Logical Operations</h3>
<p>The simplest forms of operation on binary values are the logical AND, logical OR
and exclusive OR illustrated by the following truth tables.&nbsp;</p>
<table width="100%" border="0">
<tbody>
<tr>
<td width="20%"><b><i>Logical AND (AND)</i></b></td>
<td width="10%"></td>
<td width="20%"><b><i>Logical OR (ORA)</i></b></td>
<td width="10%"></td>
<td width="20%"><b><i>Exclusive OR (EOR)</i></b></td>
</tr>
<tr>
<td width="20%">
<table width="100%" border="0">
<tbody>
<tr>
<td width="33%"></td>
<td width="33%" bgColor="#000000"><font color="#ffffff">0</font></td>
<td width="34%" bgColor="#000000"><font color="#ffffff">1</font></td>
</tr>
<tr>
<td width="33%" bgColor="#000000"><font color="#ffffff">0</font></td>
<td width="33%" bgColor="#c0c0c0">0</td>
<td width="34%" bgColor="#c0c0c0">0</td>
</tr>
<tr>
<td width="33%" bgColor="#000000"><font color="#ffffff">1</font></td>
<td width="33%" bgColor="#c0c0c0">0</td>
<td width="34%" bgColor="#c0c0c0">1</td>
</tr>
</tbody>
</table>
</td>
<td width="10%"></td>
<td width="20%">
<table width="100%" border="0">
<tbody>
<tr>
<td width="33%"></td>
<td width="33%" bgColor="#000000"><font color="#ffffff">0</font></td>
<td width="34%" bgColor="#000000"><font color="#ffffff">1</font></td>
</tr>
<tr>
<td width="33%" bgColor="#000000"><font color="#ffffff">0</font></td>
<td width="33%" bgColor="#c0c0c0">0</td>
<td width="34%" bgColor="#c0c0c0">1</td>
</tr>
<tr>
<td width="33%" bgColor="#000000"><font color="#ffffff">1</font></td>
<td width="33%" bgColor="#c0c0c0">1</td>
<td width="34%" bgColor="#c0c0c0">1</td>
</tr>
</tbody>
</table>
</td>
<td width="10%"></td>
<td width="20%">
<table width="100%" border="0">
<tbody>
<tr>
<td width="33%"></td>
<td width="33%" bgColor="#000000"><font color="#ffffff">0</font></td>
<td width="34%" bgColor="#000000"><font color="#ffffff">1</font></td>
</tr>
<tr>
<td width="33%" bgColor="#000000"><font color="#ffffff">0</font></td>
<td width="33%" bgColor="#c0c0c0">0</td>
<td width="34%" bgColor="#c0c0c0">1</td>
</tr>
<tr>
<td width="33%" bgColor="#000000"><font color="#ffffff">1</font></td>
<td width="33%" bgColor="#c0c0c0">1</td>
<td width="34%" bgColor="#c0c0c0">0</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p>These results can be summarized in English as:</p>
<ul>
<li>
The result of a logical AND is true (1) if and only if both inputs are true,
otherwise it is false (0).
<li>
The result of a logical OR is true (1) if either of the inputs its true,
otherwise it is false (0).
<li>
The result of an exclusive OR is true (1) if and only if one input is true and
the other is false, otherwise it is false (0).</li>
</ul>
<p>The tables show result of applying these operations on two one-bit values but as
the 6502 comprises of eight bit registers and memory each instruction will
operate on two eight bit values simultaneously as shown below.</p>
<table border="0" width="100%">
<tr>
<td width="16%"></td>
<td width="17%"><b><i>Logical AND (AND)</i></b></td>
<td width="5%"></td>
<td width="17%"><b><i>Logical OR (ORA)</i></b></td>
<td width="5%"></td>
<td width="17%"><b><i>Exclusive OR (EOR)</i></b></td>
</tr>
<tr>
<td width="16%"><b><i>Value 1</i></b></td>
<td width="17%">
<table border="0" width="100%">
<tr>
<td width="12%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">1</font></td>
</tr>
</table>
</td>
<td width="5%"></td>
<td width="17%">
<table border="0" width="100%">
<tr>
<td width="12%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">1</font></td>
</tr>
</table>
</td>
<td width="5%"></td>
<td width="17%">
<table border="0" width="100%">
<tr>
<td width="12%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">1</font></td>
</tr>
</table>
</td>
</tr>
<tr>
<td width="16%"><b><i>Value 2</i></b></td>
<td width="17%">
<table border="0" width="100%">
<tr>
<td width="12%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">1</font></td>
</tr>
</table>
</td>
<td width="5%"></td>
<td width="17%">
<table border="0" width="100%">
<tr>
<td width="12%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">1</font></td>
</tr>
</table>
</td>
<td width="5%"></td>
<td width="17%">
<table border="0" width="100%">
<tr>
<td width="12%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="12%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">1</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">0</font></td>
<td width="13%" bgcolor="#000000"><font color="#ffffff">1</font></td>
</tr>
</table>
</td>
</tr>
<tr>
<td width="16%"><b><i>Result</i></b></td>
<td width="17%">
<table border="0" width="100%">
<tr>
<td width="12%" bgcolor="#c0c0c0">0</td>
<td width="12%" bgcolor="#c0c0c0">0</td>
<td width="12%" bgcolor="#c0c0c0">0</td>
<td width="12%" bgcolor="#c0c0c0">1</td>
<td width="13%" bgcolor="#c0c0c0">0</td>
<td width="13%" bgcolor="#c0c0c0">0</td>
<td width="13%" bgcolor="#c0c0c0">0</td>
<td width="13%" bgcolor="#c0c0c0">1</td>
</tr>
</table>
</td>
<td width="5%"></td>
<td width="17%">
<table border="0" width="100%">
<tr>
<td width="12%" bgcolor="#c0c0c0">0</td>
<td width="12%" bgcolor="#c0c0c0">1</td>
<td width="12%" bgcolor="#c0c0c0">1</td>
<td width="12%" bgcolor="#c0c0c0">1</td>
<td width="13%" bgcolor="#c0c0c0">0</td>
<td width="13%" bgcolor="#c0c0c0">1</td>
<td width="13%" bgcolor="#c0c0c0">1</td>
<td width="13%" bgcolor="#c0c0c0">1</td>
</tr>
</table>
</td>
<td width="5%"></td>
<td width="17%">
<table border="0" width="100%">
<tr>
<td width="12%" bgcolor="#c0c0c0">0</td>
<td width="12%" bgcolor="#c0c0c0">1</td>
<td width="12%" bgcolor="#c0c0c0">1</td>
<td width="12%" bgcolor="#c0c0c0">0</td>
<td width="13%" bgcolor="#c0c0c0">0</td>
<td width="13%" bgcolor="#c0c0c0">1</td>
<td width="13%" bgcolor="#c0c0c0">1</td>
<td width="13%" bgcolor="#c0c0c0">0</td>
</tr>
</table>
</td>
</tr>
</table>
<p>It is important to understand the properties and practical applications of each
of these operations as they are extensively used in other algorithms.</p>
<ul>
<li>
Logical AND operates as a filter and is often used to select a subset of bits
from a value (e.g. the status flags from a peripheral control chip).
<li>
Logical OR allows bits to be inserted into an existing value (e.g. to set
control flags in a peripheral control chip).
<li>
Exclusive OR allows selected bits to be set or inverted.</li>
</ul>
<p>In the 6502 these operations are implemented by the <A href="reference.html#AND">AND</A>,
<A href="reference.html#ORA">ORA</A> and <A href="reference.html#EOR">EOR</A> instructions.
One of the values to be operated on will be the current contents of the
accumulator, the other is in memory either as an immediate value or at a
specified location. The result of the operation is placed in the accumulator
and the zero and negative flags are set accordingly.</p>
<pre>; Example logical operations
AND #$0F ;Filter out all but the least 4 bits
ORA BITS,X ;Insert some bits from a table
EOR (DATA),Y ;EOR against some data</pre>
<p>A very common use of the EOR instruction is to calculate the 'complement' (or
logical NOT) of a value. This involves inverting every bit in the value and is
most easily calculated by exclusively ORing against an all ones value.</p>
<pre>; Calculate the complement
EOR #$FF</pre>
<p>The macro library contains reference code for 16 and 32 bit AND, ORA, EOR and
NOT operations although there is very little use for them outside of
interpreters.</p>
<h3>Shifts &amp; Rotates</h3>
<p>The shift and rotate instructions allow the bits within&nbsp;either the
accumulator or a memory location to be moved by one place either up (left) or
down (right). When the bits are moved a new value will be needed to fill the
vacant position created at one end of the value, and similarly the bit
displaced at the opposite end will need to be caught and stored.</p>
<P>Both shifts and rotates catch the displaced bit in the carry flag but they
differ in how they fill the vacant position; shifts will always fill the vacant
bit with a zero whilst a rotate will fill it with the value of the carry flag
as it was at the start of the instruction.</P>
<P>For example the following diagram shows the result of applying an 'Arithmetic
Shift&nbsp;Left' (<A href="reference.html#ASL">ASL</A>) to the value $4D to
give $9A.</P>
<PRE> +---+---+---+---+---+---+---+---+
Initial: | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
+---+---+---+---+---+---+---+---+
| | | | | | | |
/ / / / / / / /
/ / / / / / / / 0
/ / / / / / / / /
/ | | | | | | | |
/ v v v v v v v v
+---+---+---+---+---+---+---+---+
Result: C=0&nbsp; |&nbsp;1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 |
+---+---+---+---+---+---+---+---+</PRE>
<P>Whist the following shows the result of applying a 'Rotate Left' (<A href="reference.html#ROL">ROL</A>)
to the same value, but assuming that the carry contained the value one.</P>
<PRE> +---+---+---+---+---+---+---+---+
Initial: | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | C=1
+---+---+---+---+---+---+---+---+
| | | | | | | | /
/ / / / / / / / /
/ / / / / / / / /
/ / / / / / / / /
/ | | | | | | | |
/ v v v v v v v v
+---+---+---+---+---+---+---+---+
Result: C=0&nbsp; |&nbsp;1 | 0 | 0 | 1 | 1 | 0 | 1 | 1 |
+---+---+---+---+---+---+---+---+</PRE>
<P>Shifting the bits within a value (and introducing a zero as the least
significant bit) has the effect of multiplying its value by two. In order to
apply this multiplication to a value larger than a single byte we use ASL
to&nbsp;shift the first byte and then ROL all the subsequent bytes as
necessary using the carry flag to temporarily hold the displaced bits as they
are moved from one byte to the next.</P>
<PRE>; Shift a 16bit value by one place left (e.g. multiply by two)
_ASL16 ASL MEM+0 ;Shift the LSB
ROL MEM+1 ;Rotate the MSB</PRE>
<P>The behavior of the right shift as rotates follows the same pattern. For
example we can apply a 'Logical Shift Right' (<A href="reference.html#LSR">LSR</A>
)&nbsp;to the value $4D to give $26.<PRE> +---+---+---+---+---+---+---+---+
Initial: | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
+---+---+---+---+---+---+---+---+
| | | | | | | |
\ \ \ \ \ \ \ \
0 \ \ \ \ \ \ \ \
\ \ \ \ \ \ \ \ \
| | | | | | | | \
v v v v v v v v \
+---+---+---+---+---+---+---+---+
Result: &nbsp; |&nbsp;0 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | C=1
+---+---+---+---+---+---+---+---+</PRE>
<P>Or a 'Rotate Right' (<A href="reference.html#ROR">ROR</A>)&nbsp;of the same
value, but assuming that the carry contained the value one to give $A6.</P>
<PRE> +---+---+---+---+---+---+---+---+
Initial: C=1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
+---+---+---+---+---+---+---+---+
\ | | | | | | | |
\ \ \ \ \ \ \ \ \
\ \ \ \ \ \ \ \ \
\ \ \ \ \ \ \ \ \
| | | | | | | | \
v v v v v v v v \
+---+---+---+---+---+---+---+---+
Result: &nbsp; |&nbsp;1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | C=1
+---+---+---+---+---+---+---+---+</PRE>
<P>Not surprisingly if left shifts multiply a value by two then right shifts do an
unsigned division by two. Again if we are applying the division to&nbsp;a
multi-byte value we will typically use LSR on the first byte (the MSB this
time) and ROR on all subsequent bytes.</P>
<pre>; Shift a 16 bit value by one place right (e.g. divide by two)
_LSR16 LSR MEM+1 ;Shift the MSB
ROR MEM+0 ;Rotate the LSB</pre>
<p>There are a number of applications for shifts and rotates, not least the coding
of generic multiply and divide algorithms which are discussed later.</p>
<P>As was pointed out earlier right shifting a value two divide it by two only
works on unsigned values. This is because the LSR is will always place a zero
in the most significant bit of the MSB.&nbsp;To make this
algorithm&nbsp;work&nbsp;for all two complement coded values we need
to&nbsp;ensure that&nbsp;value of this bit is copied&nbsp;back into itself to
keep the value the same sign.&nbsp;We can use another shift to achieve
this.</P>
<PRE>; Divide a signed 16 bit value by two
_DIV2 LDA MEM+1 ;Load the MSB
ASL A ;Copy the sign bit into C
ROR MEM+1 ;And back into the MSB
ROR MEM+0 ;Rotate the LSB as normal</PRE>
<H3>Addition &amp; Subtraction</H3>
<P>The 6502 processor provides 8 bit addition and subtraction instructions and a
carry/borrow flag that is used to propagate the carry bit between operations.</P>
<P>To implement a 16 bit addition the programmer must code two pairs of additions;
one for the least significant bytes and one for the most significant bytes. The
carry flag must be cleared before the first addition to ensure that an
additional increment isn't performed.</P>
<PRE>; 16 bit Binary Addition
CLC ;Ensure carry is clear
LDA VLA+0 ;Add the two least significant bytes
ADC VLB+0
STA RES+0 ;... and store the result
LDA VLA+1 ;Add the two most significant bytes
ADC VLB+1 ;... and any propagated carry bit
STA RES+1 ;... and store the result</PRE>
<P>Subtraction follows the same pattern but the carry must be set before the first
pair of bytes are subtracted to get the correct result.</P>
<PRE>; 16 bit Binary Subtraction
SEC ;Ensure carry is set
LDA VLA+0 ;Subtract the two least significant bytes
SBC VLB+0
STA RES+0 ;... and store the result
LDA VLA+1 ;Subtract the two most significant bytes
SBC VLB+1 ;... and any propagated borrow bit
STA RES+1 ;... and store the result</PRE>
<P>Both the addition and subtraction algorithm can be extended to 32 bits by
repeating the LDA/ADC/STA or LDA/SBC/STA pattern for two further bytes worth of
data.</P>
<H3>Negation</H3>
<P>The traditional approach to negating a twos complement number is to reverse all
the bits (by EORing with $FF) and add one as shown below.</P>
<PRE>; 8 bit Binary Negation
CLC ;Ensure carry is clear
EOR #$FF ;Invert all the bits
ADC #1 ;... and add one</PRE>
<P>This technique works well with a
single byte already held in the accumulator but not with bigger numbers. With
these it is easier just to subtract them from zero.</P>
<PRE>; 16 bit Binary Negation
SEC ;Ensure carry is set
LDA #0 ;Load constant zero
SBC SRC+0 ;... subtract the least significant byte
STA DST+0 ;... and store the result
LDA #0 ;Load constant zero again
SBC SRC+1 ;... subtract the most significant byte
STA DST+1 ;... and store the result</PRE>
<H3>Decimal Arithmetic</H3>
<P>The behavior of the ADC and SBC instructions can be modified by setting or
clearing the decimal mode flag in the processor status register. Normally
decimal mode is disabled and ADC/SBC perform simple binary arithmetic (e.g. $99
+ $01 =&gt; $9A Carry = 0), but if the flag is set with a SED instruction the
processor will perform binary coded decimal arithmetic instead (e.g. $99 + $01
=&gt; $00 Carry = 1).</P>
<P>To make the 16 bit addition/subtraction code work in decimal mode simply include
an SED at the start and a CLD at the end (to restore the processor to normal).</P>
<PRE>; 16 bit Binary Code Decimal Addition
SED ;Set decimal mode flag
CLC ;Ensure carry is clear
LDA VLA+0 ;Add the two least significant bytes
ADC VLB+0
STA RES+0 ;... and store the result
LDA VLA+1 ;Add the two most significant bytes
ADC VLB+1 ;... and any propagated carry bit
STA RES+1 ;... and store the result
CLD ;Clear decimal mode</PRE>
<P>Binary coded values are more easily converted to displayable digits and are
useful for holding numbers such as high scores.</P>
<PRE>; Print the BCD value in A as two ASCII digits
PHA ;Save the BCD value
LSR A ;Shift the four most significant bits
LSR A ;... into the four least significant
LSR A
LSR A
ORA #'0' ;Make an ASCII digit
JSR PRINT ;... and print it
PLA ;Recover the BCD value
AND #$0F ;Mask out all but the bottom 4 bits
ORA #'0' ;Make an ASCII digit
JSR PRINT ;... and print it</PRE>
<P>Another use for BCD is in the conversion of binary values to decimal ones.
Some algorithms perform this conversion by counting the number of times that
10000's, 1000's, 100's, 10's and 1's can be subtracted from the binary value before
it underflows, but I normally use a simple fixed loop that shifts the bits out
of the binary value one at a time&nbsp;and adds it&nbsp;to an
intermediate&nbsp;result that is being doubled (in BCD) on each iteration.</P><PRE>; Convert an 16 bit binary value into a 24bit BCD value
BIN2BCD LDA #0 ;Clear the result area
STA RES+0
STA RES+1
STA RES+2
LDX #16 ;Setup the bit counter
SED ;Enter decimal mode
_LOOP ASL VAL+0 ;Shift a bit out of the binary
ROL VAL+1 ;... value
LDA RES+0 ;And add it into the result, doubling
ADC RES+0 ;... it at the same time
STA RES+0
LDA RES+1
ADC RES+1
STA RES+1
LDA RES+2
ADC RES+2
STA RES+2
DEX ;More bits to process?
BNE _LOOP
CLD ;Leave decimal mode</PRE>
<P>One final odd use of decimal arithmetic&nbsp;is the conversion of hexadecimal
digits to printable ASCII characters. The usual&nbsp;way to perform&nbsp;this
conversion is to add $30 to the digit ($00 - $0F)&nbsp;to make an intermediate
result which is then&nbsp;examined to see if it&nbsp;is greater than or equal to
$3A. If it is then an additional $06 is added to make the result fall in the
range $41 - $46&nbsp;(e.g. 'A' - 'F').</P><PRE>; Convert a hex digit ($00-$0F) to ASCII ('0'-'9' or 'A'-'F')
HEX2ASC ORA #$30 ;Form the basic character code
CMP #$3A ;Does the result need adjustment?
BCC .+4
ADC #$05 ;Add 6 (5 and the carry) if needed</PRE>
<P>It turns out that in&nbsp;decimal mode the processor&nbsp;does&nbsp;basically
the same correction after an addition&nbsp;and with the&nbsp;right arguments we
can convert the digit to its ASCII character without performing any comparisons
as shown in the following code.</P><PRE>; Convert a hex digit ($00-$0F) to ASCII ('0'-'9' or 'A'-'F')
HEX2ASC SED ;Enter BCD mode
CLC ;Ensure the carry is clear
ADC #$90 ;Produce $90-$99 (C=0) or $00-$05 (C=1)
ADC #$40 ;Produce $30-$39 or $41-$46
CLD ;Leave BCD mode</PRE>
<H3>Increments &amp; Decrements</H3>
<P>Assembly programs frequently use memory based counters that occasionally need
incrementing or decrementing by one. One way to achieve this would be to load
the LSB and MSB in turn and add or subtract one with the <A href="reference.html#ADC">
ADC</A>/<A href="reference.html#SBC">SBC</A> instructions, but the 6502 has
a more efficient way to do this using <A href="reference.html#INC">INC</A> and <A href="reference.html#DEC">
DEC</A>.</P>
<P>Incrementing is straight forward, we just increment the least significant byte
until the result becomes zero. This indicates that the calculation has wrapped
round (e.g. $FF + $01 =&gt; $00) and an increment to the most significant byte
is needed.</P>
<PRE>; Increment a 16 bit value by one
_INC16 INC MEM+0 ;Increment the LSB
BNE _DONE ;If the result was not zero we're done
INC MEM+1 ;Increment the MSB if LSB wrapped round
_DONE EQU *</PRE>
<P>Decrementing is a little trickier because we need to know when the least
significant byte is about to underflow from $00 to $FF. The answer is to test
it first by loading it into the accumulator to set the processor flags.</P>
<PRE>; Decrement a 16 bit value by one
_DEC16 LDA MEM+0 ;Test if the LSB is zero
BNE _SKIP ;If it isn't we can skip the next instruction
DEC MEM+1 ;Decrement the MSB when the LSB will underflow
_SKIP DEC MEM+0 ;Decrement the LSB</PRE>
<H3>Complex Memory Transfers</H3>
<p>Moving data from one place to another is a common operation. If the
amount of data to moved is 256 bytes or less and the source and target
locations of the data are fixed then a simple loop around an indexed LDA
followed by an indexed STA is the most efficient. Note that whilst both
the X and Y registers can be used in indexed addressing modes&nbsp; an asymmetry
in the 6502's instruction means that X is the better register to use if
one or both of the memory areas resides on zero page.</p>
<pre>; Move 256 bytes or less in a forward direction
LDX #0 ;Start with the first byte
_LOOP LDA SRC,X ;Move it
STA DST,X
INX ;Then bump the index ...
CPX #LEN ;... until we reach the limit
BNE _LOOP</pre>
<p>The corresponding code moving the last byte first is as follows:</p>
<pre>; Move 256 bytes or less in a reverse direction
LDX #LEN ;Start with the last byte
_LOOP DEX ;Bump the index
LDA SRC,X ;Move a byte
STA DST,X
CPX #0 ;... until all bytes have moved
BNE _LOOP</pre>
<p>If the amount is even smaller (128 bytes or less) then we can
eliminate the comparison against the limit and use the settings of the
flags after a DEX to determine if the loop has finished.</p>
<pre>; Move 128 bytes or less in a reverse direction
LDX #LEN-1 ;Start with the last byte
_LOOP LDA SRC,X ;Move it
STA DST,X
DEX ;Then bump the index ...
BPL _LOOP ;... until all bytes have moved</pre>
<p>To create a completely generic memory transfer we must change to
using indirect indexed addressing to access memory and use all the
registers. The following code shows a forward transferring algorithm
which first moves complete pages of 256 bytes followed by any remaining
fragments of smaller size.</p>
<pre>_MOVFWD LDY #0 ;Initialise the index
LDX LEN+1 ;Load the page count
BEQ _FRAG ;... Do we only have a fragment?
_PAGE LDA (SRC),Y ;Move a byte in a page transfer
STA (DST),Y
INY ;And repeat for the rest of the
BNE _PAGE ;... page
INC SRC+1 ;Then bump the src and dst addresses
INC DST+1 ;... by a page
DEX ;And repeat while there are more
BNE _PAGE ;... pages to move
_FRAG CPY LEN+0 ;Then while the index has not reached
BEQ _DONE ;... the limit
LDA (SRC),Y ;Move a fragment byte
STA (DST),Y
INY ;Bump the index and repeat
BNE _FRAG\?
_DONE EQU * ;All done</pre>
<p>&nbsp;</p>
<H3>Character Classification</H3>
<p>The standard C library provides a set of functions for classifying
(e.g. is letter, is digit, is ASCII, is upper case, etc.) and modifying
(e.g. to upper case and to lower case) characters defined in a header
called &lt;ctype.h&gt;. This section describes how a similar set of
functions can be coded in 6502 assembler. There are two techniques that
can be applied to solve this problem, namely, comparisons or look up
tables.</p>
<dl>
<dd><i>Note: These functions will be restricted to just the normal
ASCII character range $00-$7F.</i></dd>
</dl>
<p>The look up table required to implement character
classification&nbsp; needs a byte per character. Bits within the look up
table indicate how the character is to be classified (e.g. control
character, printable character, white space, decimal digit, hexadecimal
digit, punctuation, upper case latter or lower case letter). To test a
character for a specific classification you load its description byte
from the table and test for the presence of certain bits (e.g. with
AND).</p>
<pre>; Constants describing the role of each classification bit
_CTL EQU $80
_PRN EQU $40
_WSP EQU $20
_PCT EQU $10
_UPR EQU $08
_LWR EQU $04
_DGT EQU $02
_HEX EQU $01
; Test if the character in A is a control character
ISCNTRL TAX
LDA #_CTL
BNE TEST
; Test if the character in A is printable
ISPRINT TAX
LDA #_PRN
BNE TEST
; Test if the character in A is punctation
ISPUNCT TAX
LDA #_PCT
BNE TEST
; Test if the character in A is upper case
ISUPPER TAX
LDA #_UPR
BNE TEST
; Test if the character in A is lower case
ISLOWER TAX
LDA #_LWR
BNE TEST
; Test if the character in A is a letter
ISALPHA TAX
LDA #_UPR|_LWR
BNE TEST
; Test if the character in A is a decimal digit
ISDIGIT TAX
LDA #_DGT
BNE TEST
; Test if the character in A is a hexadecimal digit
ISXDIGIT TAX
LDA #_HEX
BNE TEST
; Test if the character in A is letter or a digit
ISALNUM TAX
LDA #_DGT|_UPR|_LWR
; Tests for the required bits in the look up table value
TEST AND CTYPE,X
BEQ FAIL
; Set the carry flag if any target bits were found
PASS TXA
SEC
RTS
; Test if the character in A is in the ASCII range $00-$7F
ISASCII TAX
BPL PASS
; Clear the carry flag if no target bits were found
FAIL TXA
CLC
RTS
; If A contains a lower case letter convert it to upper case
TOUPPER JSR ISLOWER
BCC *+4
AND #$DF
RTS
; If A contains an upper case letter convert it to lower case
TOLOWER JSR ISUPPER
BCC *+4
ORA #$20
RTS
; The lookup table of character descriptions
CTYPE DB _CTL ; NUL
DB _CTL ; SOH
DB _CTL ; STX
DB _CTL ; ETX
DB _CTL ; EOT
DB _CTL ; ENQ
DB _CTL ; ACK
DB _CTL ; BEL
DB _CTL ; BS
DB _CTL|_WSP ; TAB
DB _CTL|_WSP ; LF
DB _CTL|_WSP ; VT
DB _CTL|_WSP ; FF
DB _CTL|_WSP ; CR
DB _CTL ; SO
DB _CTL ; SI
DB _CTL ; DLE
DB _CTL ; DC1
DB _CTL ; DC2
DB _CTL ; DC3
DB _CTL ; DC4
DB _CTL ; NAK
DB _CTL ; SYN
DB _CTL ; ETB
DB _CTL ; CAN
DB _CTL ; EM
DB _CTL ; SUB
DB _CTL ; ESC
DB _CTL ; FS
DB _CTL ; GS
DB _CTL ; RS
DB _CTL ; US
DB _PRN|_WSP ; SPACE
DB _PRN|_PCT ; !
DB _PRN|_PCT ; &quot;
DB _PRN|_PCT ; #
DB _PRN|_PCT ; $
DB _PRN|_PCT ; %
DB _PRN|_PCT ; &amp;
DB _PRN|_PCT ; '
DB _PRN|_PCT ; (
DB _PRN|_PCT ; )
DB _PRN|_PCT ; *
DB _PRN|_PCT ; +
DB _PRN|_PCT ; ,
DB _PRN|_PCT ; -
DB _PRN|_PCT ; .
DB _PRN|_PCT ; /
DB _PRN|_DGT|_HEX ; 0
DB _PRN|_DGT|_HEX ; 1
DB _PRN|_DGT|_HEX ; 2
DB _PRN|_DGT|_HEX ; 3
DB _PRN|_DGT|_HEX ; 4
DB _PRN|_DGT|_HEX ; 5
DB _PRN|_DGT|_HEX ; 6
DB _PRN|_DGT|_HEX ; 7
DB _PRN|_DGT|_HEX ; 8
DB _PRN|_DGT|_HEX ; 9
DB _PRN|_PCT ; :
DB _PRN|_PCT ; ;
DB _PRN|_PCT ; &lt;
DB _PRN|_PCT ; =
DB _PRN|_PCT ; &gt;
DB _PRN|_PCT ; ?
DB _PRN|_PCT ; @
DB _PRN|_UPR|_HEX ; A
DB _PRN|_UPR|_HEX ; B
DB _PRN|_UPR|_HEX ; C
DB _PRN|_UPR|_HEX ; D
DB _PRN|_UPR|_HEX ; E
DB _PRN|_UPR|_HEX ; F
DB _PRN|_UPR ; G
DB _PRN|_UPR ; H
DB _PRN|_UPR ; I
DB _PRN|_UPR ; J
DB _PRN|_UPR ; K
DB _PRN|_UPR ; L
DB _PRN|_UPR ; M
DB _PRN|_UPR ; N
DB _PRN|_UPR ; O
DB _PRN|_UPR ; P
DB _PRN|_UPR ; Q
DB _PRN|_UPR ; R
DB _PRN|_UPR ; S
DB _PRN|_UPR ; T
DB _PRN|_UPR ; U
DB _PRN|_UPR ; V
DB _PRN|_UPR ; W
DB _PRN|_UPR ; X
DB _PRN|_UPR ; Y
DB _PRN|_UPR ; Z
DB _PRN|_PCT ; [
DB _PRN|_PCT ; \
DB _PRN|_PCT ; ]
DB _PRN|_PCT ; ^
DB _PRN|_PCT ; _
DB _PRN|_PCT ; `
DB _PRN|_LWR|_HEX ; a
DB _PRN|_LWR|_HEX ; b
DB _PRN|_LWR|_HEX ; c
DB _PRN|_LWR|_HEX ; d
DB _PRN|_LWR|_HEX ; e
DB _PRN|_LWR|_HEX ; f
DB _PRN|_LWR ; g
DB _PRN|_LWR ; h
DB _PRN|_LWR ; i
DB _PRN|_LWR ; j
DB _PRN|_LWR ; k
DB _PRN|_LWR ; l
DB _PRN|_LWR ; m
DB _PRN|_LWR ; n
DB _PRN|_LWR ; o
DB _PRN|_LWR ; p
DB _PRN|_LWR ; q
DB _PRN|_LWR ; r
DB _PRN|_LWR ; s
DB _PRN|_LWR ; t
DB _PRN|_LWR ; u
DB _PRN|_LWR ; v
DB _PRN|_LWR ; w
DB _PRN|_LWR ; x
DB _PRN|_LWR ; y
DB _PRN|_LWR ; z
DB _PRN|_PCT ; {
DB _PRN|_PCT ; |
DB _PRN|_PCT ; }
DB _PRN|_PCT ; ~
DB _CTL ; DEL</pre>
<p>If we use comparisons then each function will consist of a number of
comparison stages to determine if a provided character has an
appropriate value. In most cases these functions are quite small but one
or two of them may involve many stages (e.g. is punctuation). The
execution time will vary according to the number of the tests a
character is subjected to.&nbsp;</p>
<pre>ISUPPER CMP #'A'
BCC FAIL
CMP #'Z'+1
BCS FAIL
; Drop thru here on success
ISLOWER CMP #'a'
BCC FAIL
CMP #'z'+1
BCS FAIL
; Drop thru here on success
ISALPHA CMP #'A'
BCC FAIL
CMP #'Z'+1
BCC PASS
CMP #'a'
BCC FAIL
CMP #'z'+1
BCS FAIL
PASS EQU *
; Drop thru here on success</pre>
<p>Which solution is best? As in so many cases it depends on your
program. If you only need one or two tests and memory size is an issue
then the comparison approach will generate less code but may be slightly
slower (for the complex tests), otherwise the look up table is simple
and fast.</p>
<H3>Some notes on my macro library</H3>
<P>As I said in the introduction to this section all of the algorithms presented
here are taken from my macro library. Coding simple algorithms like these as
macros has several advantages over subroutine libraries on the 6502 processor,
namely:</P>
<UL>
<LI>
The assembler adjusts them automatically to zero page or absolute addressing
depending on the parameters.
<LI>
They can be used either inline (for speed) or expanded into subroutines (to
save space) as needed.
<LI>
The same macro can be used several times but customized in each case to suit
its use at that time.
<LI>
The macros can optimize the code they generate under some circumstances (e.g.
_XFR16 detects when the source and target addresses are the same and does
nothing).</LI>
</UL>
<P>Another feature of the macros is that they will generate code for the 65SC02
processor using the additional instructions on that processor if the assembler
defines the correct symbol. (This processor was used in the BBC Microcomputers
6502 second processor that's why I decided to support it).</P>
<P>The routines in the currently library are:</P>
<P><TABLE WIDTH="100%" BORDER="1" CELLSPACING="0" CELLPADDING="0">
<TR>
<TD WIDTH="22%"><B>Macro Name</B></TD>
<TD WIDTH="78%"><B>Description</B></TD>
</TR>
<TR>
<TD WIDTH="22%">_CLR16</TD>
<TD WIDTH="78%">Clears 16 bits of memory to zero</TD>
</TR>
<TR>
<TD WIDTH="22%">_CLR32</TD>
<TD WIDTH="78%">Clears 32 bits of memory to zero</TD>
</TR>
<TR>
<TD WIDTH="22%">_CLR32C</TD>
<TD WIDTH="78%">Clears 32 bits of memory to zero iteratively</TD>
</TR>
<TR>
<TD WIDTH="22%">_XFR16</TD>
<TD WIDTH="78%">Moves 16 bits of memory</TD>
</TR>
<TR>
<TD WIDTH="22%">_XFR32</TD>
<TD WIDTH="78%">Moves 32 bits of memory</TD>
</TR>
<TR>
<TD WIDTH="22%">_XFR32C</TD>
<TD WIDTH="78%">Moves 32 bits of memory iteratively</TD>
</TR>
<TR>
<TD WIDTH="22%">_SET16I</TD>
<TD WIDTH="78%">Load a 16 bit constant into memory</TD>
</TR>
<TR>
<TD WIDTH="22%">_NOT16</TD>
<TD WIDTH="78%">Compute the NOT of a 16 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_NOT32</TD>
<TD WIDTH="78%">Compute the NOT of a 32 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_NOT32C</TD>
<TD WIDTH="78%">Compute the NOT of a 32 bit value iteratively</TD>
</TR>
<TR>
<TD WIDTH="22%">_ORA16</TD>
<TD WIDTH="78%">Compute the OR of two 16 bit values</TD>
</TR>
<TR>
<TD WIDTH="22%">_ORA32</TD>
<TD WIDTH="78%">Compute the OR of two 32 bit values&nbsp;</TD>
</TR>
<TR>
<TD WIDTH="22%">_ORA32C</TD>
<TD WIDTH="78%">Compute the OR of two 32 bit values iteratively</TD>
</TR>
<TR>
<TD WIDTH="22%">_AND16</TD>
<TD WIDTH="78%">Compute the AND of two 16 bit values</TD>
</TR>
<TR>
<TD WIDTH="22%">_AND32</TD>
<TD WIDTH="78%">Compute the AND of two 32 bit values</TD>
</TR>
<TR>
<TD WIDTH="22%">_AND32C</TD>
<TD WIDTH="78%">Compute the AND of two 32 bit values iteratively</TD>
</TR>
<TR>
<TD WIDTH="22%">_EOR16</TD>
<TD WIDTH="78%">Compute the EOR of two 16 bit values</TD>
</TR>
<TR>
<TD WIDTH="22%">_EOR32</TD>
<TD WIDTH="78%">Compute the EOR of two 32 bit values</TD>
</TR>
<TR>
<TD WIDTH="22%">_EOR32C</TD>
<TD WIDTH="78%">Compute the EOR of two 32 bit values iteratively</TD>
</TR>
<TR>
<TD WIDTH="22%">_ASL16</TD>
<TD WIDTH="78%">Compute the arithmetic left shift of a 16 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_ASL32</TD>
<TD WIDTH="78%">Compute the arithmetic left shift of a 32 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_ROL16</TD>
<TD WIDTH="78%">Compute the left rotation of a 16 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_ROL32</TD>
<TD WIDTH="78%">Compute the left rotation of a 32 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_LSR16</TD>
<TD WIDTH="78%">Compute the logical right shift of a 16 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_LSR32</TD>
<TD WIDTH="78%">Compute the logical right shift of a 32 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_ROR16</TD>
<TD WIDTH="78%">Compute the right rotation of a 16 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_ROR32</TD>
<TD WIDTH="78%">Compute the right rotation of a 32 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_INC16</TD>
<TD WIDTH="78%">Increment a 16 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_INC32</TD>
<TD WIDTH="78%">Increment a 32 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_DEC16</TD>
<TD WIDTH="78%">Decrement a 16 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_DEC32</TD>
<TD WIDTH="78%">Decrement a 32 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_ADD16</TD>
<TD WIDTH="78%">Add two 16 bit values</TD>
</TR>
<TR>
<TD WIDTH="22%">_ADD32</TD>
<TD WIDTH="78%">Add two 32 bit values</TD>
</TR>
<TR>
<TD WIDTH="22%" HEIGHT="22">_SUB16&nbsp;</TD>
<TD WIDTH="78%" HEIGHT="22">Subtract two 16 bit values</TD>
</TR>
<TR>
<TD WIDTH="22%" HEIGHT="22">_SUB32</TD>
<TD WIDTH="78%" HEIGHT="22">Subtract two 32 bit values</TD>
</TR>
<TR>
<TD WIDTH="22%">_NEG16</TD>
<TD WIDTH="78%">Negate a 16 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_NEG32</TD>
<TD WIDTH="78%">Negate a 32 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_ABS16</TD>
<TD WIDTH="78%">Compute the absolute value of a 16 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_ABS32</TD>
<TD WIDTH="78%">Compute the absolute value of a 32 bit value</TD>
</TR>
<TR>
<TD WIDTH="22%">_MUL16</TD>
<TD WIDTH="78%">Calculate the 16 bit product of two 16 bit values</TD>
</TR>
<TR>
<TD WIDTH="22%">_MUL16X</TD>
<TD WIDTH="78%">Calculate the 32 bit product of two 16 bit values</TD>
</TR>
<TR>
<TD WIDTH="22%">_MUL32</TD>
<TD WIDTH="78%">Calculate the 32 bit product of two 32 bit values</TD>
</TR>
<TR>
<TD WIDTH="22%">_MUL16I</TD>
<TD WIDTH="78%">Generate the code for a 16 bit constant multiply</TD>
</TR>
<TR>
<TD WIDTH="22%">_DIV16</TD>
<TD WIDTH="78%">Calculate the 16 bit quotient &amp; remainder of a 16
bit value and 16 bit dividend</TD>
</TR>
<TR>
<TD WIDTH="22%">_DIV16X</TD>
<TD WIDTH="78%">Calculate the 16 bit quotient &amp; remainder of a 32
bit value and 16 bit dividend</TD>
</TR>
<TR>
<TD WIDTH="22%">_DIV32</TD>
<TD WIDTH="78%">Calculate the 32 bit quotient &amp; remainder of a 32
bit value and 32 bit dividend</TD>
</TR>
<TR>
<TD WIDTH="22%">_CMP16</TD>
<TD WIDTH="78%">Compare two 16 bit values</TD>
</TR>
<TR>
<TD WIDTH="22%">_CMP32</TD>
<TD WIDTH="78%">Compare two 32 bit values</TD>
</TR>
<TR>
<TD WIDTH="22%">_MEMFWD&nbsp;</TD>
<TD WIDTH="78%">Move a block for memory a forward direction</TD>
</TR>
<TR>
<TD WIDTH="22%">_MEMREV</TD>
<TD WIDTH="78%">Not Implemented</TD>
</TR>
<TR>
<TD WIDTH="22%">_MEMCPY</TD>
<TD WIDTH="78%">Not Implemented</TD>
</TR>
<TR>
<TD WIDTH="22%">_STRLEN</TD>
<TD WIDTH="78%">Compute the length of a 'C' style string</TD>
</TR>
<TR>
<TD WIDTH="22%">_STRCPY</TD>
<TD WIDTH="78%">Copy a 'C' style string</TD>
</TR>
<TR>
<TD WIDTH="22%">_STRCMP</TD>
<TD WIDTH="78%">Compare two 'C' style strings</TD>
</TR>
<TR>
<TD WIDTH="22%">_STRNCMP</TD>
<TD WIDTH="78%">Not implemented</TD>
</TR>
<TR>
<TD WIDTH="22%">&nbsp;</TD>
<TD WIDTH="78%">&nbsp;</TD>
</TR>
<TR>
<TD WIDTH="22%">&nbsp;</TD>
<TD WIDTH="78%">&nbsp;</TD>
</TR>
<TR>
<TD WIDTH="22%">&nbsp;</TD>
<TD WIDTH="78%">&nbsp;</TD>
</TR>
<TR>
<TD WIDTH="22%">&nbsp;</TD>
<TD WIDTH="78%">&nbsp;</TD>
</TR>
<TR>
<TD WIDTH="22%">&nbsp;</TD>
<TD WIDTH="78%">&nbsp;</TD>
</TR>
</TABLE>
<P>Examine the code for more details on the macro parameters and usage.</P>
<P><TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0" WIDTH="100%" HEIGHT="30">
<TR>
<TD WIDTH="25%" BGCOLOR="#aaaaff">&nbsp;<A href="addressing.html">&lt;&lt; Back</A></TD>
<TD WIDTH="25%" BGCOLOR="#aaaaff">
<P><CENTER><A href="../index.html" target=_parent>Home</A></CENTER>
</TD>
<TD WIDTH="25%" BGCOLOR="#aaaaff">
<P><CENTER><A href="index.html">Contents</A></CENTER>
</TD>
<TD ALIGN="right" WIDTH="25%" BGCOLOR="#aaaaff"><A href="reference.html">Next &gt;&gt;</A></TD>
</TR>
</TABLE>
<P>
<HR>
<script type="text/javascript"><!--
google_ad_client = "pub-0826595092783671";
/* 6502 Footer */
google_ad_slot = "9966603696";
google_ad_width = 728;
google_ad_height = 90;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<HR>
<script type="text/javascript"><!--
google_ad_client = "pub-0826595092783671";
/* 6502 Links */
google_ad_slot = "4173075094";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("UA-9026746-2");
pageTracker._trackPageview();
} catch(err) {}</script>
<HR ALIGN="left">
This page was last updated on 19th March 2004
</BODY>
</HTML>