mirror of
https://github.com/daniel5151/ANESE.git
synced 2025-04-02 10:32:00 -04:00
1281 lines
50 KiB
HTML
Vendored
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
|
|
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. </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 & Rotates</h3>
|
|
<p>The shift and rotate instructions allow the bits within 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 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 | 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 | 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 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>
|
|
) 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: | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | C=1
|
|
+---+---+---+---+---+---+---+---+</PRE>
|
|
<P>Or a 'Rotate Right' (<A href="reference.html#ROR">ROR</A>) 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: | 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 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. To make this
|
|
algorithm work for all two complement coded values we need
|
|
to ensure that value of this bit is copied back into itself to
|
|
keep the value the same sign. 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 & 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 => $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
|
|
=> $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 and adds it to an
|
|
intermediate 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 is the conversion of hexadecimal
|
|
digits to printable ASCII characters. The usual way to perform this
|
|
conversion is to add $30 to the digit ($00 - $0F) to make an intermediate
|
|
result which is then examined to see if it 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 (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 decimal mode the processor does basically
|
|
the same correction after an addition and with the 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 & 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 => $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 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> </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 <ctype.h>. 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 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 ; "
|
|
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|_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 ; <
|
|
DB _PRN|_PCT ; =
|
|
DB _PRN|_PCT ; >
|
|
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. </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 </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 </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 & 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 & 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 & 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 </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%"> </TD>
|
|
<TD WIDTH="78%"> </TD>
|
|
</TR>
|
|
<TR>
|
|
<TD WIDTH="22%"> </TD>
|
|
<TD WIDTH="78%"> </TD>
|
|
</TR>
|
|
<TR>
|
|
<TD WIDTH="22%"> </TD>
|
|
<TD WIDTH="78%"> </TD>
|
|
</TR>
|
|
<TR>
|
|
<TD WIDTH="22%"> </TD>
|
|
<TD WIDTH="78%"> </TD>
|
|
</TR>
|
|
<TR>
|
|
<TD WIDTH="22%"> </TD>
|
|
<TD WIDTH="78%"> </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"> <A href="addressing.html"><< 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 >></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>
|