Das letzte Experiment mit dem Lautsprecher hat dazu geführt, dass man für jeden Ton eine eigene Routine schreiben mussten, obwohl sich diese Routinen nur durch den Wert des übergebenen Parameters unterscheiden. Im folgenden werden wir verschiedene Möglichkeiten kennenlernen, wie man solche Daten im PIC ablegen kann und durch geschicktes Adressieren ansprechen kann
1. Tabellen im Programmspeicher.
Eigentlich können Daten aus dem Programmspeicher nicht so ausgelesen werden, dass sie von einem Programm selber verwertet werden können. Allerdings gibt es im PIC-Assembler eine Rückkehr aus einer Prozeduraufruf, dem einen Wert im Work-Register mit an das Hauptprogramm übergeben kann: retlw wert.
Nach Rücksprung in die aufrufende Prozedur steht der übergebene Wert im Workregister. Das kann man sich zunutze machen, um Tabellen auch im Programmspeicher abzulegen: Das Hauptprogramm ruft die Prozedur tabelle mit dem offset des Wertes auf, den man auslesen möchte.
main
movwf offset
call tabelle
incf offset,1
goto main
tabelle
addwf pc,1 ; addiere den Inhalt von w
zum Befehlszähler
retlw 5a
retlw 50
retlw 48 ;
Sprungziel für offset = 3, Rückkehr mit w =48
retlw 43
retlw 40
; usw
; usw
Die Manipulation des Befehlszählers hat keinen Einfluss auf den Stack, auf den die Rücksprungsadresse beim Prozeduraufruf gelegt worden war.
Aufgaben:
1. Lege die Frequenzdaten für die c-Dur-Tonleiter in einer Tabelle ab. Hole die Daten nacheinander mit Hilfe der obigen Methode ins Work-Register, so dass der entsprechende Ton dann gespielt werden kann. Sind alle acht Töne gespielt, so soll die Tonleiter wiederholt werden.
2. Auch die Dauer des Tones soll aus einer Tabelle ausgelesen und entsprechend ausgewertet werden.
2. Daten in Registern - Indirektes Adressieren
Viele Assembler kennen Befehle für das sogenannte indirekte Adressieren. So bedeutet z.B. der 8086-Assembler-Befehl mov dl,basis[bx] folgendes. Lade den Inhalt der Adresse Basis+[bx] ([bx] : Inhalt des Registers bx als offset) ins Register dl. Das ist eine elegante Möglichkeit, um Tabellen aus- bzw. einzulesen. Eine nicht ganz so elegante Art der indirekten Adressierung gibt es auch im PIC.
So kann man Konstanten in Registern ablegen, wenn man diese nicht für wichtigere Zwecke benötigt. Fängt eine Tabelle z.B. im Register Tabelle equ 18 an, dann ist eine Adressierung in der Form movwf tabelle+3 möglich. Anweisungen wie movwf tabelle+offset (wobei offset selbst ein Register ist) sind im Assembler des PIC nicht vorgesehen. Allerdings hat der PIC zwei Register, die eine indirekte Adressierung möglich machen: fsr_data equ 0 und fsr_adr equ 4. Führt man eine Schreib- oder Leseoperation in dem Register fsr_data durch, so wird diese in dem Register wirksam, dessen Adresse in fsr_adr steht. Das folgende Programm zeigt, wie man in den Registern eine Tabelle indirekt schreiben und anschließend wieder auslesen kann.
; *********************************************************************indirekt.asm
; Beispielprogramm für indirekten Zugriff auf Datenregister
list P=16C84
fsr_data equ 00
fsr_adr equ 04
portb equ 06
offset equ 0c
;*** Beispiel für indirekten schreibenden Zugriff auf die Register
writedata_ind
movlw tabelle ; Anfangsadresse der Tabelle nach
movwf fsr_adr ; fsr_adr (FSR:FileStackRegister) laden
movlw 04
; Zähler laden
movwf offset
loopw
movf offset,0 ; Jede Operation auf fsr_data wird in der
movwf fsr_data ; Adresse ausgeführt, die in fsr_adr enthalten ist
incf fsr_adr,1 ; Tabellenadresse erhöhen
decfsz counter
goto loopw
return
;*** Hier werden Daten direkt in die Register geschrieben
writedata_dir
movlw 081
movwf tabelle
movlw 042
movwf tabelle+1
movlw 024
movwf tabelle+2
movlw 018
movwf tabelle+3
return
;*** Daten werden durch indirekten Zugriff ausgelesen
readdata
addlw tabelle-1 ; tabelle-1 um offset
erhöhen
movwf fsr_adr ; die folgenden Leseopereation wird in dem
movf fsr_data,0 ; Register ausgeführt, dass in fsr_adr steht
return
verz
; die altbekannte verz-Routine
return
END
Aufgabe:
1. Benutze die Register 10 bis 19 für die Speicherung der Anzeigebytes für die 7-Segment-Anzeige. Dabei sollen diese Register beim Start des Programms nacheinander mit den entsprechenden Daten geladen werden. Im Hauptprogramm sollen ein Zähler vom 9 auf 0 heruntergezählt werden und dabei als offset für die indirekte Adressierung bzgl. der Basisadresse tabelle=100 verwendet werden. Die Werte sollen dann am PortB mit der 7-Segment-Anzeige ausgegeben werden.