textfiles/programming/more65.txt

446 lines
20 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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