6502 - Assembler-Programmierungasm
Überblick
- Assembler = Menschen-lesbare Sprache, die auf den Opcode des Prozessors ausgelegt ist.
- Ein Assembler-Befehl steht dabei für genau einen Opcode-Befehl des Prozessors.
- Es gibt zusätzliche Anweisung, Makros und Sprungmarken.
- Nach den Assembler wird ein Linker ausgeführt, der alle Sprungmarken durch Adressen ersetzt.
- Das Endergebnis ist das Abbild des EEPROM-Speichers.
- Für das Demoboard wird der Assembler VASM von Volker Barthelmann im Oldstyle Syntax verwendet.
- Dokumentation: http://sun.hasenbraten.de/vasm/release/vasm_6.html#Oldstyle-Syntax-Module
Blinken-Programm in Assembler
Blinken-Programm in Assembler ohne zusätzliche Sprungmarken und Makros
.org 0x8000
lda #0xFF
sta 0x7F02 ; DDRB = 0xFF
lda #0xA5
sta 0x7F00 ; ORB = 0xA5
lda #0x5A
sta 0x7F00 ; ORB = 0x5A
jmp 0x8005
.org 0xFFFC
.word 0x8000
.org
-Anweisung (Origin) zum Setzen der Adresse der folgenden Anweisungen.word
-Anweisung zum Schreiben eines 16 bit-Wertes an diese Stelle.- Alle Anweisungen müssen eingerückt werden!
Blinken-Programm in Assembler
DDRB = 0x7F02
ORB = 0x7F00
.org 0x8000
reset:
lda #0xFF
sta DDRB ; DDRB = 0xFF
loop:
lda #0xA5
sta ORB ; ORB = 0xA5
lda #0x5A
sta ORB ; ORB = 0x5A
jmp loop
.org 0xFFFC
.word reset
- Definieren von Makros mit Zuweisungsoperator.
- Marken werden nicht eingerückt und mit folgenden Doppelpunkt geschrieben.
- Marken enthalten die zugehörigen Adresswerte und können frei verwendet werden.
Bit-Manipulation
PA0 | PA1 | PA2 | PA3 | PA4 | PA5 | PA6 | PA7 |
---|---|---|---|---|---|---|---|
BTN1 | D1 | BTN2 | D2 | BTN3 | D3 | BTN4 | D4 |
- Nur einzelne Bits von DDRA und ORA dürfen verändert werden!
Konstanten-Definitionen
ORB = 0x7F00
ORA = 0x7F01
DDRB = 0x7F02
DDRA = 0x7F03
LED1 = 0x02
LED2 = 0x08
LED3 = 0x20
LED4 = 0x80
Schreiben des Data-Direction-Register
Setzen eines Bits des Output-Register
Togglen eines Bits des Output-Register
Löschen eines Bits des Output-Registers
Programmieren von Schleifen
- Für Schleifen werden Sprungbefehle verwendet.
- Unbedingte Sprünge werden mit der
JMP
-Anweisung realisiert. (z. B. Unendlich-Schleifen) - Schleifen mit einer Bedingung werden mit bedingten Sprüngen realisiert:
BCC
: Branch on carry Clear (C=0)
BCS
: Branch on carry Set (C=1)
BEQ
: Branch if equal (Z=1)
BNE
: Branch if not equal (Z=0)
BMI
: Branch if result minus (N=1)
BPL
: Branch if result plus (N=0)
BVC
: Branch on overflow clear (V=0)
BVS
: Branch on overflow set (V=1) -
Entsprechend einer vorangegangen Operation werden die Flaggen im Statusregister gesetzt.
→ Overflow und Negativ-Bit später (Einführung negativer Zahlen!)
Implementierung einer Verzögerungsschleife
Resultierende Bus-Aktivität
> AB = 0x800D R DB = 0xA2 ldx #$ff
> AB = 0x800E R DB = 0xFF
> AB = 0x800F R DB = 0xCA dex
> AB = 0x8010 R DB = 0xD0
> AB = 0x8010 R DB = 0xD0 bne -$03
> AB = 0x8011 R DB = 0xFD
> AB = 0x8012 R DB = 0x4C
> AB = 0x800F R DB = 0xCA dex
> AB = 0x8010 R DB = 0xD0
> AB = 0x8010 R DB = 0xD0 bne -$03
> AB = 0x8011 R DB = 0xFD
> AB = 0x8012 R DB = 0x4C
> AB = 0x800F R DB = 0xCA dex
Verschachtelte Schleife mit Register X und Y
Stapeloperationen
PUSH und PULL
- Stapel (engl. stack)
- Mögliche Stapelzugriffe:
- Ein Blatt von oben auf den Stapel legen. (PUSH)
- Ein Blatt vom Stapel nehmen und lesen. (PULL)
- Der Stapel des 6502 befindet sich immer im Adressbereich 0x100-0x1FF
- Der Stapel wird von hohen Adressen zu niedrigeren Adressen gefüllt.
- Der Stapelzeiger zeigt die erste nicht zum Stapel gehörende Speicherzelle.
- PUSH: Wert des Stapelzeigers wird verringert.
X→Mem[S]; S-1→S
- PULL: Wert des Stapelzeigers wird erhöht.
S+1→S; Mem[S]→X
- Anweisungen:
PHA
,PHX
,PHY
: Push der Register A, X, Y
PHP
: Push des Status-Registers
PLA
,PLX
,PLY
: Pull der Register A, X, Y
PLP
: Pull des Status-Registers
Zurücksetzen des Stapelzeiger zum Systemstart
Anwendungbeispiel der Push- und Pull-Anweisung zum Zwischenspeichern einer Variable
Adressdekoder für den RAM
- Erklärungen zum RAM-Timing: https://youtu.be/i_wrxBdXTgM
- Zulässiger RAM-Bereich: 0x0000-0x3FFF
Schreiben in unterschiedlichen RAM-Segmenten
org 0x8000
reset:
lda #0xFF
sta 0x00
sta 0x0100
sta 0x0200
sta 0x3FFF
sta 0x4000
lda 0x4000
loop:
jmp loop
Resultierende Bus-Aktivität
> AB = 0xFFFC R DB = 0x00
> AB = 0xFFFD R DB = 0x80
> AB = 0x8000 R DB = 0xA9 lda #$ff
> AB = 0x8001 R DB = 0xFF
> AB = 0x8002 R DB = 0x85 sta $00
> AB = 0x8003 R DB = 0x00
> AB = 0x0000 w DB = 0xFF
> AB = 0x8004 R DB = 0x8D sta $0100
> AB = 0x8005 R DB = 0x00
> AB = 0x8006 R DB = 0x01
> AB = 0x0100 w DB = 0xFF
> AB = 0x8007 R DB = 0x8D sta $0200
> AB = 0x8008 R DB = 0x00
> AB = 0x8009 R DB = 0x02
> AB = 0x0200 w DB = 0xFF
> AB = 0x800A R DB = 0x8D sta $3fff
> AB = 0x800B R DB = 0xFF
> AB = 0x800C R DB = 0x3F
> AB = 0x3FFF w DB = 0xFF
> AB = 0x800D R DB = 0x8D sta $4000
> AB = 0x800E R DB = 0x00
> AB = 0x800F R DB = 0x40
> AB = 0x4000 w DB = 0xFF
> AB = 0x8010 R DB = 0xAD lda $4000
> AB = 0x8011 R DB = 0x00
> AB = 0x8012 R DB = 0x40
> AB = 0x4000 R DB = 0x00
> AB = 0x8013 R DB = 0x4C jmp $8013
> AB = 0x8014 R DB = 0x13
> AB = 0x8015 R DB = 0x80
sta 0x00
: Zeropage-Zugriff (Einsparung eines Taktzyklus)sta 0x0100
: Schreiben in den Stapel-Bereichsta 0x0200
,sta 0x3FFF
: Regulärer RAM-Zugriffsta 0x4000
: Außerhalb des RAM-Bereiches, folgender Lesebefehl liefert 0x00
Subroutinen
- Eine Prozedur ist im Assembler eine Folge von Maschinenbefehlen, die einmalig definiert wird und an beliebigen Stellen im Programm aufgerufen werden kann.
- Da der Aufruf an verschiedenen Stellen erfolgen kann, können keine einfachen Sprungbefehle verwendet werden, um die Prozedur aufzurufen.
- Es müssten unterschiedliche Fälle betrachtet werden, um anschließend zum richtigen Programmteil zurückzuspringen.
- Lösung: Speichern der Rückkehradresse im Stapel.
- JSR (Jump to Subroutine) → PUSH PCH, PUSH PCL+2
- RTS (Return from Subroutine) → PULL PCL, PULL PCH
Subroutine Verzögerung
reset:
ldx #$ff
txs
lda #(LED1 + LED2 + LED3 + LED4)
sta DDRA
loop:
lda ORA
ora #LED1
sta ORA ; LED1 on
jsr delay
lda ORA
eor #LED2
sta ORA ; LED2 toogle
jsr delay
lda ORA
and #~LED1 ; LED1 off
sta ORA
jsr delay
lda ORA
eor #LED2
sta ORA ; LED2 toogle
jsr delay
jmp loop
delay:
nop
rts
Resultierende Bus-Aktivität
> AB = 0x8010 R DB = 0x20 jsr $8037
> AB = 0x8011 R DB = 0x37
> AB = 0x01FF R DB = 0x80
> AB = 0x01FF w DB = 0x80
> AB = 0x01FE w DB = 0x12
> AB = 0x8012 R DB = 0x80
> AB = 0x8037 R DB = 0xEA nop
> AB = 0x8038 R DB = 0x60
> AB = 0x8038 R DB = 0x60 rts
> AB = 0x8039 R DB = 0x00
> AB = 0x01FD R DB = 0x27
> AB = 0x01FE R DB = 0x12
> AB = 0x01FF R DB = 0x80
> AB = 0x8012 R DB = 0x80