276 lines
9.4 KiB
Plaintext
276 lines
9.4 KiB
Plaintext
|
====================================================================
|
|||
|
DR 6502 AER 201S Engineering Design 6502 Execution Simulator
|
|||
|
====================================================================
|
|||
|
|
|||
|
Supplementary Notes By: M.J.Malone
|
|||
|
|
|||
|
|
|||
|
6502 Assembly Code Examples
|
|||
|
===========================
|
|||
|
|
|||
|
The remainder of this file will be in a format acceptable to TASM
|
|||
|
for direct assembly. Note there may be errors in the code, it is
|
|||
|
not intended that it be cut up and included in students files. It
|
|||
|
is meant only as an example of addressing modes and instructions.
|
|||
|
|
|||
|
;==============================================================================
|
|||
|
; Coding Examples for the Students of AER201S
|
|||
|
;==============================================================================
|
|||
|
;
|
|||
|
;<=== Note the semicolon is a comment indicator
|
|||
|
;
|
|||
|
.ORG $E000
|
|||
|
SEI ; INITIALIZING THE STACK POINTER
|
|||
|
LDX #$FF
|
|||
|
TXS
|
|||
|
;
|
|||
|
LDX #$00 ; Initial delay to allow spurious Resets to
|
|||
|
LDY #$00 ; to subside. At this point accept that this code
|
|||
|
Delay DEX ; must appear at the beginning of every EPROM
|
|||
|
BNE Delay ; that will ever be used for the 6502 project board.
|
|||
|
DEY
|
|||
|
BNE Delay
|
|||
|
;
|
|||
|
; We are now ready to try some simple examples
|
|||
|
;
|
|||
|
;
|
|||
|
; 1) Add the numbers $65 and $27 and place the answer in the memory
|
|||
|
; location 'ANSWER'.
|
|||
|
;
|
|||
|
; First we must assign the location to the label 'ANSWER'
|
|||
|
;
|
|||
|
ANSWER = $0000
|
|||
|
;
|
|||
|
; OK now we do the addition:
|
|||
|
LDA #$65 ; Load the number (#) $65 into the accumulator.
|
|||
|
CLC ; Clear the carry flag to prevent carries from a
|
|||
|
; previous and unrelated computation.
|
|||
|
ADC #$27 ; Add the number (#) $27, the result goes in the
|
|||
|
; accumulator
|
|||
|
STA ANSWER ; Store the result into the memory location ANSWER
|
|||
|
;
|
|||
|
; Remarks: Note that the # number sign preceded the numbers $65 and $27 to
|
|||
|
; indicate that these were in fact CONSTANTS, NUMBERS. If the # sign were
|
|||
|
; omitted, TASM would interpret LDA $65 to mean 'load the accumulator from
|
|||
|
; the memory location $0065'.
|
|||
|
; Looking back at the assignment of ANSWER, we could have said:
|
|||
|
; ANSWER = $00. This, when used in the statement STA ANSWER would still
|
|||
|
; be interpreted as referring to the memory location $0000 but it would
|
|||
|
; allow the 6502 to use something called 'Zero Page' addressing, a faster
|
|||
|
; method of accessing the memory locations between $0000-$00FF.
|
|||
|
;
|
|||
|
;
|
|||
|
;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
page 2
|
|||
|
|
|||
|
; 2) Subtract the number $27 from the number $65 and store the result in the
|
|||
|
; memory location 'ANS2'.
|
|||
|
;
|
|||
|
ANS2 = $01 ; We will use zero page addressing to refer to ANS2
|
|||
|
;
|
|||
|
LDA #$65 ; Load the number (#) $65 into the accumulator
|
|||
|
SEC ; Clear the C flag (used as the borrow flag in
|
|||
|
; subtraction) to prevent borrows from a previous
|
|||
|
; unrelated computation.
|
|||
|
SBC #$27 ; Subtract the number (#) $27 from the accumulator
|
|||
|
; the result will be in the accumulator.
|
|||
|
STA ANS2 ; Store the result in the memory location 'ANS2'
|
|||
|
;
|
|||
|
; Remarks: Note that the carry or borrow flag has a inverse logic in the case
|
|||
|
; of subtraction: You have to SEC set the carry flag to prevent a borrow.
|
|||
|
;
|
|||
|
;
|
|||
|
;
|
|||
|
; 3) Program a subroutine to turn on specific bits in on VIA 1, Port A which
|
|||
|
; can be used to light LEDs to monitor program performance. We will
|
|||
|
; assume that there are 8 LEDs connected to Port A.
|
|||
|
;
|
|||
|
; We must first initialize port A as an output port
|
|||
|
;
|
|||
|
LDA #%11111111 ; All ones means all bits are outputs
|
|||
|
STA $A003 ; $A003 is the address of VIA1 Port A, data
|
|||
|
; direction register. This register determines
|
|||
|
; whether a bit is input or output.
|
|||
|
LDA #%00000000 ;
|
|||
|
STA $A001 ; $A001 is the address of VIA1 Port A I/O
|
|||
|
; register. It is initialized to all zeros
|
|||
|
; to mean all bits output 0 volts.
|
|||
|
|
|||
|
; Before we proceed with writing the subroutine we need to send the program
|
|||
|
; to a point somewhere after the subroutine we are about to write. We will
|
|||
|
; use a JuMP statement:
|
|||
|
;
|
|||
|
JMP Down_Past ; Down_Past is a program position label we will
|
|||
|
; define later.
|
|||
|
;
|
|||
|
;
|
|||
|
;
|
|||
|
;
|
|||
|
; ============================================================================
|
|||
|
; It is a good idea to put these bars in to make subroutines obvious
|
|||
|
; ============================================================================
|
|||
|
;
|
|||
|
; Ok know we need to have some Specifications for this subroutine:
|
|||
|
;
|
|||
|
; Subroutine Name: Show
|
|||
|
; Input Registers: .A contains a '1' in the bit position that you want to
|
|||
|
; have turned on
|
|||
|
; Output Registers: None
|
|||
|
;
|
|||
|
Show ORA $A001 ; 'Show' against the margin indicates a program
|
|||
|
; position label. ORA $A001 takes the bit pattern
|
|||
|
; in .A and logically OR's it with the pattern of
|
|||
|
; bits in memory location $A001, the VIA 1, Port A
|
|||
|
; I/O register.
|
|||
|
;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
page 3
|
|||
|
|
|||
|
; Say $A001 contains the bits: %abcdefgh (a through h are unknown 0 or 1)
|
|||
|
; and we or it with .A : OR %00001000
|
|||
|
; -------------
|
|||
|
; We will get: %abcd1fgh
|
|||
|
;
|
|||
|
; Because: 0 or 0 = 0, 1 or 0 = 1, 0 or 1 = 1, 1 or 1 = 1
|
|||
|
; the following is true: u or 0 = u and u or 1 = 1
|
|||
|
; where u is an unknown level.
|
|||
|
;
|
|||
|
STA $A001 ; Store the new bit levels back into the I/O Port
|
|||
|
RTS ; Return from Subroutine
|
|||
|
;
|
|||
|
; Remarks: This subroutine can be called by setting specific bits in the .A
|
|||
|
; register and calling the subroutine:
|
|||
|
; LDA #%00000001 ; Set bit 0
|
|||
|
; JSR Show ; Show this on the VIA port
|
|||
|
;
|
|||
|
; Subroutines can be nested IE one subroutine can call others. Care
|
|||
|
; should be exercised when one subroutine calls itself because there are no
|
|||
|
; 'local' variables in assembly language (unless you simulate them) since
|
|||
|
; the memory in the 6502 is global to all programs or subroutines.
|
|||
|
; Subroutines can significantly compact a program and can lend to readability
|
|||
|
; of the code if properly documented.
|
|||
|
;
|
|||
|
;
|
|||
|
;
|
|||
|
Down_Past ; This is the program position label that marks where the
|
|||
|
; JMP Down_Past goes to.
|
|||
|
;
|
|||
|
;
|
|||
|
; 4) Now that we have done an output subroutine, lets do some comparisons and
|
|||
|
; light leds to show the results. Check if ANSWER - ANS2 = 2*#$27.
|
|||
|
;
|
|||
|
RHS = $03 ; Stores the value of the right hand side of the equation
|
|||
|
;
|
|||
|
LDA ANSWER ; Calculate the RHS
|
|||
|
SEC
|
|||
|
SBC ANS2
|
|||
|
STA RHS
|
|||
|
;
|
|||
|
LDA #$27 ; To calculate 2*#$27 you use #$27+#$27
|
|||
|
CLC
|
|||
|
ADC #$27 ; Got the LHS in the accumulator
|
|||
|
;
|
|||
|
CMP RHS
|
|||
|
BNE Not_equal ; Don't Show any LED's
|
|||
|
;
|
|||
|
; The equation is true, light LED connected to bit 0
|
|||
|
LDA #%00000001
|
|||
|
JSR Show
|
|||
|
;
|
|||
|
Not_Equal
|
|||
|
;
|
|||
|
;
|
|||
|
;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
page 4
|
|||
|
|
|||
|
; 5) See if ANSWER and ANS2 have any bits in common; light LED 1 if they do.
|
|||
|
;
|
|||
|
LDA ANSWER
|
|||
|
AND ANS2 ; AND Tests to see if a bit in both arguments is
|
|||
|
; set. If it is the resulting bit is set, otherwise
|
|||
|
; the resulting bit is cleared.
|
|||
|
; If two arguments have no bits in common the result
|
|||
|
; will have no bits set or will be zero. In this case
|
|||
|
; the AND instruction will set the zero flag.
|
|||
|
BEQ No_Bits ; The BEQ branches on result equal for comparisons
|
|||
|
; or result zero for arithmetic and logical
|
|||
|
; OpCodes.
|
|||
|
LDA #%00000010
|
|||
|
JSR Show ; This turns on bit 1 if there were any bits in common
|
|||
|
;
|
|||
|
; There were no bits in common, the AND resulted in a Z=1, zero
|
|||
|
No_Bits
|
|||
|
;
|
|||
|
;
|
|||
|
;
|
|||
|
; 6) See if ANSWER and ANS2 have bit #1 in common; light LED 2 if they do.
|
|||
|
;
|
|||
|
LDA ANSWER
|
|||
|
AND #%00000010 ; This picks off the value of bit 1. You can think
|
|||
|
; of a 1 in a constant of an AND statement like a
|
|||
|
; delta function that zeros picks off one value
|
|||
|
; and zeros the rest of the domain. As a result
|
|||
|
; ANDing with #%11111111 does not change a number.
|
|||
|
AND ANS2 ; We know that the .A has all zeros except possibly
|
|||
|
; bit 1. ANDing with ANS2 will result in zero
|
|||
|
; unless bit 1 was set in both ANSWER and ANS2
|
|||
|
BEQ Bit1_Not_Set ; If the result is Z=1, don't show LEDs
|
|||
|
LDA #%00000100 ; otherwise Show LED 2
|
|||
|
JSR Show
|
|||
|
;
|
|||
|
Bit1_Not_Set
|
|||
|
;
|
|||
|
;
|
|||
|
;
|
|||
|
; 7) Do an endless loop to alternate the pattern of indicators now present
|
|||
|
; on the LEDs with the binary representation of ANSWER.
|
|||
|
;
|
|||
|
Endless LDX $A001 ; Grab the present bit pattern
|
|||
|
LDA ANSWER ; Grab the new bit pattern
|
|||
|
STA $A001 ; Put the new pattern on the Port
|
|||
|
STX ANSWER ; Put the old pattern into ANSWER
|
|||
|
;
|
|||
|
LDX #$00
|
|||
|
LDY #$00
|
|||
|
D1 DEX ; A double indexed wait loop to waste time
|
|||
|
BNE D1
|
|||
|
DEY
|
|||
|
BNE D1
|
|||
|
JMP Endless ; Next time the old bit pattern becomes the new
|
|||
|
; by the way it was stored, rotating through
|
|||
|
; the ANSWER==>.A==>Port==>.X==>ANSWER loop.
|
|||
|
;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
page 5
|
|||
|
|
|||
|
; The last entry in every program must set the reset vector to point
|
|||
|
; to the beginning of the program. If there is an interrupt routine
|
|||
|
; then the interrupt vector must be set as well. In this case there is
|
|||
|
; only a main program.
|
|||
|
;
|
|||
|
.ORG $FFFC ; Set pointer to Reset Vector Location
|
|||
|
.WORD $E000 ; Store $E000 as the reset vector
|
|||
|
.END
|
|||
|
|