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.
|
|||
|
|