446 lines
20 KiB
Plaintext
446 lines
20 KiB
Plaintext
====================================================================
|
||
DR 6502 AER 201S Engineering Design 6502 Execution Simulator
|
||
====================================================================
|
||
|
||
Supplementary Notes By: M.J.Malone
|
||
|
||
|
||
More 6502 Assembly Code
|
||
=======================
|
||
|
||
There were several instructions not introduced in the first
|
||
introductory file 'START65.DOC'. The remaining 6502 instructions
|
||
will be discussed here.
|
||
|
||
|
||
Operator Instructions
|
||
---------------------
|
||
|
||
These are instructions of only one argument and include
|
||
incrementing, decrementing and bit shifts.
|
||
|
||
INC arg Increases the value of 'arg' by one
|
||
DEC arg Decreases the value of 'arg' by one
|
||
INX Increment .X
|
||
INY Increment .Y
|
||
DEX Decrement .X
|
||
DEY Decrement .Y
|
||
ROR arg Rotates the argument and C flag bits one to the right
|
||
ROL arg Rotates the argument and C flag bits one to the left
|
||
LSR arg Logical Shift Right: shifts bits one to the right
|
||
ASL arg Arithmetic Shift Left: shifts bits one to the left
|
||
|
||
The increment and decrement instructions are very useful when
|
||
dealing with pointers and vectors. Since the .X and .Y are index
|
||
registers used to index within memory spaces, the INX, INY, DEX and
|
||
DEY instructions are used to move on the the next or previous memory
|
||
entry. The .X and .Y are also used as quick counters and here again
|
||
the increment and decrement are very useful.
|
||
The bit rotating and shifting instructions are used both in
|
||
arithmetic operations and bit pattern manipulation for encoding and
|
||
decoding. An LSR can be thought of as dividing by 2 and the ASL as
|
||
multiplying by 2. Along with the ADC and SBC, these instructions
|
||
are key to multiplication and division.
|
||
|
||
|
||
Flag Setting and Clearing
|
||
-------------------------
|
||
|
||
The programmer is able to directly influence the flags through
|
||
the following statements:
|
||
|
||
CLC Clears the Carry Flag
|
||
SEC Sets the Carry Flag
|
||
CLD Clears 'Decimal' Mode
|
||
SED Sets 'Decimal' Mode
|
||
CLI Clears the Interrupt Disable Flag
|
||
SEI Sets Interrupt Disable Flag
|
||
BRK Break Instruction
|
||
CLV Clears the Overflow Flag
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
page 2
|
||
|
||
Carry Flag
|
||
The carry flag is set and cleared by arithme tic and shift
|
||
statements and is influenced by comparisons. In general the flag is
|
||
used as follows: In ROL, ROR, ASL and LSR statements the carry flag
|
||
is used as a 9th bit in the accumulator either above the most
|
||
significant bit (MSB) or below the least significant bit (LSB). In
|
||
SBC and ADC statements it is used as a 9th bit above the MSB to act
|
||
as a carry or a borrow. Since comparisons use a subtraction, the
|
||
carry flag reflects a similar state for CMP, CPX, and CPY as for the
|
||
statement SBC. The carry flag is NOT used as a borrow-from in CMP,
|
||
CPX or CPY. For example using CMP to compare #$05 and #$05 results
|
||
in a result of zero regardless of the state of the carry flag before
|
||
the comparison. After the comparison however, if a borrow was not
|
||
required to do the comparison the carry flag will be set indicating
|
||
no borrow has occurred. The above comparison will result in the
|
||
carry flag being set. Increment instructions DO NOT influence the
|
||
carry flag, nor do AND, ORA or EOR.
|
||
|
||
Negative and Zero Flags
|
||
Neither the negative or zero flag can be explicitly set or
|
||
cleared in a SEx or CLx instruction. All instructions that move
|
||
data to a location inside the processor or compare data within in
|
||
any way except pulling from the stack influence the negative and
|
||
zero flags. That means all instructions except JMP, JSR, RTS, Sets,
|
||
Clears, Pushes, Pulls, STA, STX, STY and Branches alter the negative
|
||
and zero flags. If the data being transferred is zero or if a
|
||
calculation results in the number zero, regardless of the state of
|
||
the carry flag, the zero flag will be set (Z=1). The negative flag
|
||
will have the same logic state as the MSB of the data moved or the
|
||
result of the calculation. As a result, the negative and zero flags
|
||
will never both be set at the same time because a zero has all bits
|
||
including the MSB clear. The zero flag and testing for zero: BNE
|
||
(Z=0?) and BEQ (Z=1?) are quite straight forward.
|
||
The negative flag has some peculiar properties. First note
|
||
that there is no such thing as a negative number in the 6502. If
|
||
required to judge if a number is negative, the 6502 will assume all
|
||
numbers between #$FF and #$80 are negative correspond to the
|
||
numbers -#$01 to -#$80 respectively. The numbers between #$00 and
|
||
#$7F are assumed positive corresponding to +#$00 and +#$7F
|
||
respectively. The comparison and subtraction instructions often
|
||
cause students confusion in relation to the negative flag. Often a
|
||
following will be required:
|
||
|
||
;
|
||
; If arg1<arg2 then ****
|
||
;
|
||
lda arg1
|
||
cmp arg2
|
||
bmi ****
|
||
;
|
||
|
||
On the surface this seems to be ok but remember the limits on a
|
||
negative number for the 6502. If arg1=#$02 and arg2=#$05 then the
|
||
result is #$FD which falls into the range #$FF to #$80 and hence is
|
||
considered negative and the N flag is set. Looking at this from the
|
||
point of view of the 6502, we move the logic value of the MSB of
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
page 3
|
||
|
||
#$FD to the N flag and we get N=1. 2-5<0 and N=1 so there is no
|
||
problem. The problem comes when arg1=#$02 and arg2=#$85 (meant to
|
||
be the positive number +#$85) since the result is #$7D. We know
|
||
that #$02-#$85<0 but the MSB of #$7D is 0 so as a result N=0 and the
|
||
above code example would malfunction. The important thing to
|
||
remember is that the negative flag does not actually tell you if a
|
||
number is negative but instead whether the highest bit is set or
|
||
not.
|
||
|
||
Decimal Mode Flag
|
||
When set, the 6502 works in a 'decimal' mode when performing
|
||
the ADC or SBC instructions. This mode uses the binary coded
|
||
decimal (BCD) storage format. The BCD representation of the decimal
|
||
number 25 would be #$25 or #%0010 0101. In BCD mode the 6502 will
|
||
use only 100 of the possible $100 (256) states for the value in the
|
||
accumulator. The 6502 automatically does all adjustments to cause
|
||
results to be in BCD mode and all arguments must be valid BCD coded
|
||
numbers. For example #$55+#$27 in BCD mode = #$82. There is
|
||
absolutely nothing magic about decimal mode and almost as little of
|
||
interest. One common bug in an IRQ interrupt program is forgetting
|
||
to clear the decimal flag before doing any calculations. If the
|
||
main program uses decimal mode sometimes the IRQ may occur when the
|
||
decimal mode is in effect. This can cause very unusual problems if
|
||
the programmer is unaware of the problem.
|
||
|
||
Interrupt Masking Flag
|
||
If your IRQ (interrupt request) pin is connected to a source of
|
||
interrupt pulses then the processor will be interrupted and will
|
||
begin executing the interrupt program. The interrupt program is a
|
||
separate program that the programmer must write if interrupts are
|
||
used. The IRQ is a maskable interrupt meaning that the software can
|
||
instruct the processor to ignore it. The method of doing this
|
||
involves setting the interrupt disable bit in the processor status
|
||
register. The instruction SEI prevents interrupts from occurring
|
||
and CLI allows them.
|
||
The 6502 has a second interrupt request pin, the NMI which
|
||
stands for non-maskable interrupt request. SEI and CLI have no
|
||
effect on this interrupt: it will always be obeyed. On the 65C02
|
||
the RST (reset) pin behaves as a particular type of interrupt
|
||
similar to the NMI.
|
||
|
||
Break Flag
|
||
There is one instruction in the 6502's set that is very
|
||
confusing to many students; it is the BRK break instruction. The
|
||
break does not actually stop the execution of the processor like a
|
||
HALT type instruction might. The 6502 has no instruction that will
|
||
HALT execution. There are many undefined operation codes on the
|
||
NMOS 6502 that do unpredictable things and some that crash the
|
||
processor. These 'crash codes' could be considered HALT statements
|
||
since after them nothing happens. The break instruction simulates a
|
||
hardware IRQ with the one difference that it sets the B=1 break flag
|
||
in the processor status register. Since there are no statements
|
||
that directly test the break flag this will be explained later in
|
||
the stack operations section. The programmer must be aware that if
|
||
they use both BRK instructions in there code and IRQ interrupts, the
|
||
IRQ program should test the break flag to discover how the routine
|
||
was initiated.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
page 4
|
||
|
||
Overflow Flag
|
||
The overflow flag is used for the ADC and SBC instructions
|
||
only. It represents an arithmetic overflow. Note the overflow flag
|
||
can also be set by a pulse on the SO (set overflow) pin of the 6502
|
||
and if used cleverly can be a very fast input pin in a polling
|
||
loop.
|
||
|
||
Flag Summary
|
||
It is the interrelation of instructions and the system flags
|
||
that makes efficient code. In the following loop it is the
|
||
interrelation between the DEx instruction and the Z zero flag that
|
||
allows branching without a comparison statement, saving at least 3
|
||
clock cycles per iteration resulting in a execution time 38% faster.
|
||
|
||
;
|
||
ldx #$00
|
||
ldy #$00
|
||
Delay ???????
|
||
inx
|
||
bne Delay
|
||
inx
|
||
bne Delay
|
||
;
|
||
|
||
This routine is often used for software delays where clock cycle
|
||
counts are important but even more important is its use in counting
|
||
loops for measurements. If the ?'s were replaced by some polling
|
||
instructions testing say the overflow flag set by some external
|
||
source it could be a very powerful measurement tool. The faster the
|
||
counting executes, the more accurate the measurement of time
|
||
periods. Mastery of the flags is an important ingredient in the
|
||
efficient use of 6502 assembly code.
|
||
|
||
|
||
Stack Operations
|
||
----------------
|
||
|
||
The stack is a reserved area in memory from $0100 to $01FF
|
||
which is referred to as page 1 of the 6502 memory. The .SP stack
|
||
pointer register is used to index within this space. The stack
|
||
pointer starts at #$FF which points to $01FF. The stack pointer
|
||
points to the next free stack location. The stack pointer
|
||
decrements as the stack fills and increments as the stack empties.
|
||
There is nothing (in hardware) to prevent the stack from being
|
||
pushed below #$00 to #$FF again or being pulled from #$FF around
|
||
to #$00. When a Push instruction is executed, the pushed data is
|
||
put in the free stack space that the pointer is currently pointing
|
||
to and the stack pointer is decremented by one. In a pull operation
|
||
the stack pointer is incremented and the data is read from the
|
||
location that is being pointed to.
|
||
|
||
PHA Pushes the .A into the stack
|
||
PLA Pulls .A out of the stack
|
||
PHP Pushes the status register (.R) into the stack
|
||
PLP Pulls the .R out of the stack
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
page 5
|
||
|
||
PHX *Pushes the .X register into the stack
|
||
PLX *Pulls the .X register out of the stack
|
||
PHY *Pushes the .Y register into the stack
|
||
PLY *Pulls the .Y register out of the stack
|
||
|
||
*65C02 only
|
||
|
||
For those unfamiliar with the use of stacks, it is recommended
|
||
that computer theory text be consulted if a full description is
|
||
desired. In practice in 6502 assembly language the stack is used by
|
||
the hardware in the JSR-RTS and IRQ/NMI/BRK/RST-RTI instructions.
|
||
The user has direct access to the data in the stack through the PHx
|
||
and PLx instructions. The most important concept is balancing the
|
||
number of pushes on any program path with the number of pulls in the
|
||
path. This is required so that the stack will not continue filling
|
||
endlessly, overflowing or empty past the point that useful data was
|
||
first pushed in. It is also necessary to balance the number of
|
||
pushes and pulls on a particular program path within all
|
||
subroutines. This is required since the stack is used for the
|
||
return addresses for the subroutine call. If any user data is
|
||
pushed on top of the return address then it must be pulled off
|
||
before the RTS statement so that the program/subroutine nesting
|
||
structure, recorded in the return addresses in the stack, is
|
||
preserved.
|
||
IRQ/NMI and BRK routines require the stack as well, pushing in
|
||
not only the return address but the processor status register (.R).
|
||
Though it is not required, I cannot imagine an IRQ routine where the
|
||
user does not first push the .A, .X and .Y registers into the stack
|
||
as well to preserve the processor context. The reason for this is
|
||
simple. The IRQ could come at any time, perhaps in the middle of a
|
||
calculation where all of the register values are critical. If the
|
||
IRQ must preserve the values of all registers so that upon return
|
||
the main program continues as if the IRQ had not occurred. In this
|
||
way it is possible to make the execution of IRQ programs transparent
|
||
to the main program.
|
||
The stack can also be used with the combination of PHP \ PLA
|
||
to transfer data from the processor status register to the
|
||
accumulator. This is very useful to test flags that cannot be
|
||
directly tested in a branch instruction. The break or decimal flags
|
||
are examples of such flags.
|
||
|
||
|
||
Miscellaneous
|
||
-------------
|
||
|
||
BRK Break Instruction
|
||
BIT arg Bit Test
|
||
*TRB arg Test and Reset Bits
|
||
*TSB arg Test and Set Bits
|
||
RTI Return from Interrupt
|
||
NOP No Operation
|
||
|
||
*65C02 only
|
||
|
||
The BRK break instruction, as said before under the flags and
|
||
stack discussion, does not actually halt the execution of the
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
page 6
|
||
|
||
processor. The following instruction will effectively halt the
|
||
execution of the processor:
|
||
|
||
;
|
||
Halt JMP Halt
|
||
;
|
||
|
||
This is of course nothing more than an endless loop. The break
|
||
instruction actually simulates an IRQ in software with the one
|
||
difference that it also sets the B break flag in the processor
|
||
status register. When a BRK instruction is reached, a $00 as an
|
||
operation code, the processor pushes the program counter and the
|
||
processor status register onto the stack. The processor then loads
|
||
the $FFFE-FFFF IRQ vector position into the program counter and
|
||
begins executing instructions.
|
||
The BIT bit test instruction is somewhat strange. It first
|
||
fetches the argument and copies its 7th (MS) bit into the negative
|
||
flag and its 6th bit into the overflow flag. It then performs a
|
||
logical AND with the accumulator discarding the result but setting
|
||
the zero flag accordingly.
|
||
The TRB test and reset bits instruction is similar to the BIT
|
||
instruction in that bits 7 and 6 of the argument are copied into the
|
||
N and V flags. A logical AND with the complement of the accumulator
|
||
resets the bits of the argument that are set to a one in the
|
||
accumulator. The Z flag is set according to the result and the
|
||
result is stored back into the memory location referred to in the
|
||
argument.
|
||
The TSB test and set bits instruction is similar to the TRB
|
||
instruction in that bits 7 and 6 of the argument are copied into the
|
||
N and V flags. A logical OR with the accumulator sets the bits of
|
||
the argument that are set to a one in the accumulator. The Z flag
|
||
is set according to the result and the result is stored back into
|
||
the memory location referred to in the argument.
|
||
The TSB and TRB instructions were added to the instruction set
|
||
to allow multiple processor systems to share resources through a
|
||
series of semaphores.
|
||
The RTI return from interrupt instruction returns the processor
|
||
from an IRQ, NMI or BRK interrupt. On the 65C02, since the RST line
|
||
is treated by the processor as just another interrupt, it is
|
||
possible to RTI to the program that was running previous to the
|
||
signal on the reset line. RTI retrieves processor status register
|
||
and the program counter from the stack and returns to executing the
|
||
original program.
|
||
The NOP no operation instruction is just that, no operation.
|
||
If the processor encounters this instruction it does nothing for two
|
||
machine cycles. The NOP instruction does not change the processor
|
||
status register.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
page 7
|
||
|
||
65C02 Expansions
|
||
----------------
|
||
STZ arg Store #$00 to the memory location 'arg'
|
||
BRA offset Always do a relative branch by 'offset' bytes
|
||
PHX Push .X into the stack
|
||
PHY Push .Y into the stack
|
||
PLX Pull .X from the stack
|
||
PLY Pull .Y from the stack
|
||
TRB arg Test and Reset Bits
|
||
TSB arg Test and Set Bits
|
||
|
||
All of these instructions have been explained in the previous
|
||
sections explaining the 6502 instruction set.
|
||
|
||
|
||
Rockwell 65C02 Expansions
|
||
-------------------------
|
||
|
||
BBRx arg,offset
|
||
BBSx arg,offset
|
||
RMBx arg
|
||
SMBx arg
|
||
|
||
These instructions are present only on the Rockwell version of
|
||
the 65C02. Though some students have found this version of the
|
||
processor, there is no guarantee that the next batch of processors
|
||
ordered will be of the Rockwell variety. As a result it is not
|
||
recommended that students base their software structures on the
|
||
presence of these instructions since last minute problems may result
|
||
in a change of processor.
|
||
Rockwell added four types of instructions to their processor to
|
||
make the use of bit fields easier. For each instruction, the
|
||
numbers 0-7 must be substituted for the 'x' to give the actual
|
||
operation codes. There are therefore eight RMBx instructions: RMB0,
|
||
RMB1, RMB2, RMB3, RMB4, RMB5, RMB6, and RMB7. Note that these four
|
||
instruction types are the only 6502 instructions that do not conform
|
||
to the standard of 3 character mnemonic codes.
|
||
The BBRx and BBSx are branch statements. These statements test
|
||
a particular bit (bit 'x') of the argument which must be on the zero
|
||
page and branches if it is set (BBSx) or branches if it is reset
|
||
(BBRx). The branch is a relative branch by 'offset' bytes in the
|
||
same way as regular branches.
|
||
The RMBx reset memory bit and SMBx set memory bit instructions
|
||
are used to reset or set individual memory bits anywhere on the zero
|
||
page of memory. The argument is the memory location and the 'x'
|
||
specifies the bit number to be manipulated.
|
||
|