Skip to content

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

alt: "LEDs und Taster an PORTA des VIA", w:66

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

    lda #(LED1 + LED2 + LED3 + LED4)
    sta DDRA

Setzen eines Bits des Output-Register

    lda ORA
    ora #LED1
    sta ORA ; LED1 on

Togglen eines Bits des Output-Register

    lda ORA
    eor #LED2
    sta ORA ; LED2 toogle

Löschen eines Bits des Output-Registers

    lda ORA
    and #~LED1 ; LED1 off
    sta ORA

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.
    alt: "6502 Status Register", w:50,  src: "W65C02 Datenblatt, S. 8"

    → Overflow und Negativ-Bit später (Einführung negativer Zahlen!)

Implementierung einer Verzögerungsschleife

loop:
    lda ORA
    eor #LED1
    sta ORA

    ldx #0xFF
delay1:
    dex
    bne delay1

    jmp loop

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

    ldx #0xFF
delay1:
    ldy #0xFF
delay2:
    dey
    bne delay2
    dex
    bne delay1

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

reset:
    ldx #$ff
    txs

Anwendungbeispiel der Push- und Pull-Anweisung zum Zwischenspeichern einer Variable

loop:
    sta ORB
    inc A

    pha
    lda ORA
    eor #LED1
    sta ORA
    pla

    jmp loop

Adressdekoder für den RAM

alt: "Adressdekoder für den RAM", w:80

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-Bereich
  • sta 0x0200, sta 0x3FFF: Regulärer RAM-Zugriff
  • sta 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