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