Assembler und Computergrafik - Aktuelles - Goethe

Werbung
Praktikum
Grundlagen von Hardwaresystemen
Wintersemester 2013/2014
Versuch 5: Assembler und Computergrafik
Eingebettete Systeme
15. Dezember 2013
Fachbereich 12: Informatik und Mathematik
Institut für Informatik
Professur für Eingebettete Systeme
Prof. Dr. Uwe Brinkschulte
uunter Mitarbeit von
Daniel Lohn
Michael Bauer
Benjamin Betting
Goethe-Universität
Frankfurt am Main
Inhaltsverzeichnis
1 Einleitung
2
2 Grundlagen
2.1 Pseudo-Zufallszahlen . . . . . . . . . . . . . . . . . . .
2.2 Assemblersprache . . . . . . . . . . . . . . . . . . . . .
2.2.1 Eigenschaften einer Assemblersprache . . . . .
2.2.2 Register . . . . . . . . . . . . . . . . . . . . . .
2.2.3 Sprungbefehle . . . . . . . . . . . . . . . . . . .
2.2.4 Sprungadressen . . . . . . . . . . . . . . . . . .
2.2.5 Instruktionsformate . . . . . . . . . . . . . . .
2.2.6 Datenspeicher . . . . . . . . . . . . . . . . . . .
2.2.7 Grundlegende Befehle und Konstrukte . . . . .
2.3 Grafikprogrammierung . . . . . . . . . . . . . . . . . .
2.3.1 Sprites - bewegliche Grafiken . . . . . . . . . .
2.3.2 Funktionsweise eines Sprites . . . . . . . . . . .
2.3.3 Sprite-Implementierung im Praktikumssystem .
2.4 Entwicklungsumgebung und Compiler . . . . . . . . .
2.4.1 Menüleiste . . . . . . . . . . . . . . . . . . . .
2.4.2 Toolbar . . . . . . . . . . . . . . . . . . . . . .
2.4.3 Eingabefeld, Zwischencode und Ausgabe . . . .
2.4.4 Interaktive Boardgrafik . . . . . . . . . . . . .
2.4.5 VGA-Memory Output . . . . . . . . . . . . . .
2.4.6 Interaktive Anzeige interner Werte . . . . . . .
2.4.7 Putty-Konsole . . . . . . . . . . . . . . . . . .
2.4.8 Status-Konsole . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
3
4
5
6
6
7
8
11
11
13
15
15
16
17
17
19
21
21
21
22
22
22
3 Anmerkungen und Tipps
23
4 Vorbereitungsaufgaben
25
5 Praktikumsaufgaben
28
1
Kapitel 1
Einleitung
In diesem Abschlussversuch des Praktikums wird die Programmierung eines Prozessors mittels
Assembler vermittelt. Verwendet wird eine an der Professur für Eingebettete Systeme entwickelte 16-Bit RISC-CPU, deren Design Anleihen bei verschiedenen RISC-Architekturen aufweist und in VHDL implementiert wurde. Hauptziel dieses Versuches ist es die Assemblerprogrammierung der CPU mit verschiedenen Anwendungen aus dem I/O- und Grafikbereich. Neben den wesentlichen Assemblergrundlagen werden unter anderem auch 2D-Grafiktechniken
betrachtet. Im Grundlagenteil werden die Konzepte einer Assemblersprache sowie deren Funktionsweise erläutert. Des Weiteren wird die Umsetzung von 2D-Grafikprogrammierung mittels
Assembler vermittelt. Anschließend werden die folgenden Aufgaben bearbeitet:
ˆ Umsetzung eines Pseudo-Zufallszahlengenerators in Assembler
ˆ Erstellen und Zeichnen von 2D-Grafikobjekten mittels Assembler
ˆ Bewegung von Grafikobjekten mittels externer I/O-Steuereingaben durch den Benutzer
ˆ Umsetzung eines rudimentären Spiel-Automaten in Assembler
2
Kapitel 2
Grundlagen
2.1
Pseudo-Zufallszahlen
Zufallszahlen haben ein weites Anwendungsfeld in der Informatik. Sie werden für Simulationen und Datenverschlüsselung (Kryptographie), aber auch für Computerspiele gebraucht,
um in die Reaktion des Computers, bzw. des Programms einen nicht vorhersagbaren Aspekt
einzubauen. In typischen, industriellen Microcontroller-Anwendungen, wie z. B. einer Heizungssteuerung wird man Zufallszahlen hingegen eher nicht benötigen. Ein einfaches und
unmittelbar einleuchtendes Beispiel für ein Mikrocontroller-Programm, das Zufallszahlen verwendet, ist eine Würfelsimulation. Auch im Audio-Bereich werden Zufallszahlen genutzt, um
akustisches Rauschen zu erzeugen, das Bestandteil vieler komplexer Geräusche ist.
Eine Folge echter Zufallszahlen zeichnet sich dadurch aus, dass sie auch bei perfekter
Kenntnis des erzeugenden Vorgangs nicht vorhersagbar und nicht reproduzierbar (identisch
wiederholbar) ist. Echte Zufallszahlen lassen sich daher nur mit einem gewissen HardwareAufwand erzeugen, indem man zum Beispiel thermisches Rauschen nutzt, das sich in Halbleiterbauelementen als elektrisches Rauschen äußert, also in winzigen Spannungsschwankungen,
die zur Auswertung hoch verstärkt werden müssen.
Vielfach braucht man jedoch überhaupt keinen Hardwareaufwand zu treiben, denn in den
meisten Anwendungen dürften so genannte Pseudo-Zufallszahlen völlig ausreichen und sind
evtl. sogar vorteilhaft. Ein Generator für Pseudo-Zufallszahlen erzeugt eine Folge von Zahlen,
die auf den ersten Blick ohne Zusammenhang, eben zufällig, zu sein scheinen. Tatsächlich
steckt aber ein mathematisches Prinzip hinter der Erzeugung und unter gleichen Voraussetzungen entsteht immer wieder dieselbe, identische Abfolge. Diese Reproduzierbarkeit ist
von Vorteil, wenn etwa eine Simulation mit identischen zufälligen Einflussgrößen wiederholt
werden soll.
Es gibt mehrere Methoden, um Pseudo-Zufallszahlen zu erzeugen. In Computern, die
über leistungsfähige mathematische Co-Prozessoren verfügen, lassen sich problemlos mathematisch aufwendige Algorithmen programmieren, die beispielsweise Divisionen durch sehr
große Primzahlen oder ähnliches durchführen. Bei der Realisierung mit einem viel niedriger
getakteten Mikrocontroller ohne allzu große Rechenfähigkeit und vielleicht knappem Programmspeicherplatz greift man besser auf andere Methoden zurück. Bewährt sind so genannte linear rückgekoppelte Schieberegister, kurz LFSR (für linear feedback shift register). Die
mathematische Beschreibung der Funktion, die ein LFSR ausdrückt, findet sich bei Wikipedia. Die technische Umsetzung ist mit Hilfe eines Schieberegisters und einem oder mehrerer
3
KAPITEL 2. GRUNDLAGEN
4
Abbildung 2.1: Beispiel eines LFSR mit 8-Bit
Exklusiv-Oder-Gatter (EOR, XOR) direkt in Hardware sehr effizient möglich. Aber auch ein
Microcontroller-Programm benötigt nur einige Zeilen (Assembler-) Code zur Realisierung in
Software.
Das Prinzip des LFSR besteht darin, den Ausgang eines Schieberegisters auf seinen Eingang rückzukoppeln und mindestens eine XOR-Operation durchzuführen. Die nebenstehende
Abbildung stellt eine besonders einfache Realisierung mit einem 8-Bit-Schieberegister und
nur einem XOR-Gatter dar. Bei geeigneter Wahl einer Kombination aus Registerlänge n
und den Einkopplungsstellen durchläuft das LFSR beim Shiften (bitweises Weiterschieben)
aus einem beliebigen Anfangszustand ungleich Null sämtliche 2n − 1 möglichen Zustände,
bis der Startzustand wieder erreicht ist. Durch Abgreifen eines Bits, z. B. Bit 0, nach jedem Shift-Schritt oder Verwendung größerer Registeranteile nach mehreren Schritten lassen
sich Pseudo-Zufallszahlen gewinnen. Komplexere LFSR enthalten mehrere XOR-Gatter, die
zwischen einzelnen Registerzellen angeordnet sind und das umlaufende“ Bit mehrfach mit
”
dem internen Bit-Strom verknüpfen. Auf diese Weise findet eine stärkere Veränderung des
Registerinhalts pro Shift-Schritt statt.
Eine Zwischenstufe zwischen den echten und Pseudo-Zufallszahlen stellen Zahlenwerte dar,
die auf besondere Weise gewonnen werden und als scheinbar echte Zufallszahlen bezeichnet
werden können. Eine solche Zahl ist beispielsweise die exakte Uhrzeit zu einem bestimmten Zeitpunkt oder die möglichst genau gemessene Zeitdauer eines Tastendrucks. Wird eine
scheinbar echte Zufallszahl zur Initialisierung eines LFSR verwendet, so kann man durch das
Shiften eine Folge scheinbar echter Zufallszahlen generieren. Dies soll hier aber nicht weiter
verfolgt werden.
2.2
Assemblersprache
Eine Assemblersprache ist eine spezielle Programmiersprache, welche die Maschinensprache einer spezifischen Prozessorarchitektur in einer für den Menschen lesbaren Form repräsentiert. Daraus folgt, dass jede Computerarchitektur ihre eigene Assemblersprache hat.
Ein zugehöriger Assembler ist genaugenommen ein Compiler, der den Code eines Assem”
blerprogramms“ in Maschinensprache, d. h. Binärwörter übersetzt.
Programmbefehle in Maschinensprache sind einfache Bitmuster, die aus den Opcodes und
den zugehörigen Daten gebildet werden. Zur symbolischen Darstellung von Maschinenbefehlen
werden Abkürzungen (so genannte mnemonische Symbole) benutzt, die sich ein Programmierer leichter einprägen kann als eine Funktionsbeschreibung oder gar den binären Opcode eines
Befehls.
Das Assemblerprogramm ist also nur eine für Menschen etwas komfortablere Darstellung
des Maschinenprogramms:
Anstatt
KAPITEL 2. GRUNDLAGEN
5
0x4022
in der hexadezimalen Darstellung schreiben zu müssen, kann der Programmierer die Assembleranweisung
ADD R1, R2
verwenden, die für den Prozessor, welcher im Praktikum benutzt wird, genau dasselbe bedeutet.
2.2.1
Eigenschaften einer Assemblersprache
Die Fähigkeiten einer Assemblersprache hängen direkt von der Zielarchitektur ab. Besitzt
die Ziel-CPU nicht den entsprechenden Befehl, so kann auch der Assemblerprogrammierer
keinen solchen Befehl ohne weiteres benutzen. Die ganzen Konstrukte höherer Programmiersprachen, die dem Programmierer erlauben seine Algorithmen in verständliche Programme
zu übertragen, fehlen:
ˆ keine komfortablen Schleifen (for, while, repeat-until)
ˆ keine strukturierten Datentypen
ˆ keine Unterprogramme mit typisierter Parameterübergabe
ˆ keine automatische Speicherverwaltun
Beispiel:
summe = a + b + c + d;
Der Ausdruck für die obige Summe kann für die Praktikums-CPU nicht in einem einzigen
Maschinensprachebefehl kodiert werden und muss daher in mehreren Anweisungen aufgeteilt
werden. Die CPU kann immer nur zwei Register addieren und das Ergebnis in einem der
Register speichern.
Das folgende C-Programm entspricht daher eher dem Assemblerprogramm:
summe = a + b;
summe = summe + c;
summe = summe + d;
und würde in der Assemblersprache der Praktikums-CPU wiefolgt lauten:
LD R1, [a]
LD R2, [b]
LD R3, [c]
LD R4, [d]
ADD R1, R2
ADD R1, R3
ADD R1, R4
Im Allgemeinen passiert hier folgendes:
KAPITEL 2. GRUNDLAGEN
6
ˆ 4 Variablen werden aus dem Speicher in 4 Register mit Hilfe des LD-Befehls (Load)
geschrieben
ˆ mit Hilfe des ADD-Befehls werden die Inhalte zweier Register addiert und das Ergebnis
in das erste der beiden Register geschrieben.
ˆ zum Schluss enthält R1 das Ergebnis von a + b + c + d
Alle obenstehenden Befehle werden später ausführlicher erklärt.
2.2.2
Register
In dem bisher genannten Beispiel wurden anstelle der Variablennamen des C-Programms stets
die Namen von Registern verwendet.
Ein Register ist ein Hardware-Element innerhalb des Prozessors, das der schnellen Datenspeicherung dient. In Prozessoren eines Computers sind Register direkt eingebaut, um zum
Zwischenspeichern von Speicheradressen und Rechenoperanden benutzt zu werden.
Die Registersätze (die Gesamtheit aller Register eines Prozessors) von Prozessoren unterscheiden sich in der Art, der Anzahl und der Größe der zur Verfügung stehenden Register.
Bei unserer CPU stehen 32 x 16-Bit Register (R0-R31) zur Verfügung.
Das Besondere an den Registern der Praktikums-CPU (im Gegensatz zu anderen Speichern, wie RAM oder Festplatte) ist, dass sie
ˆ direkt in Befehlen verwendet werden können
ˆ Operationen mit ihrem Inhalt mit nur einem Befehlswort ausgeführt werden können
ˆ direkt an das Rechenwerk angeschlossen sind
ˆ sowohl Quelle von Daten, als auch Ziel des Ergebnisses der Operation sein können.
2.2.3
Sprungbefehle
Mit Hilfe von Sprungbefehlen können Sie den Ablauf eines Programmes an einer anderen
Stelle im Programmspeicher fortführen. Grundsätzlich lassen sich Sprungbefehle nach zwei
Kriterien unterscheiden: Unter welchen Umständen wird der Sprung durchgeführt und auf
welche Art wird das Sprungziel bestimmt.
ˆ unbedingter Sprung - das GOTO in Assembler
Mit diesem Befehl wird die Ausführung des Programms an der durch die Sprunginstruktion bestimmten Stelle fortgesetzt.
ˆ bedingter Sprung - das IF-THEN in Assembler
Mit diesem Befehl wird die Ausführung des Programms an der durch die Sprunginstruktion bestimmten Stelle fortgesetzt, wenn die Bedingung wahr ist. Andernfalls wird die
auf den Sprungbefehl folgende Instruktion ausgeführt.
ˆ relativer Sprung
Die Berechnung des Sprungziels erfolg relativ zur aktuellen Position im Programm. So
kann beispielsweise am Ende einer Schleife zum Anfang zurückgesprungen werden ohne
KAPITEL 2. GRUNDLAGEN
7
die absolute Adresse der ersten Schleifeninstruktion kennen zu müssen. Die Sprungdistanz wird häufig als Immediate“ direkt im Sprungbefehl kodiert. Sinnvoll sind derar”
tige relative Sprünge, da in Programmen häufig über kurze Distanzen gesprungen wird
und so der Code übersichtlicher bleiben kann.
ˆ absoluter Sprung
Das Sprungziel ist ein absoluter Wert. Der Programmzähler wird durch die Sprunginstruktion auf diesen Wert gesetzt.
In der Regel werten alle bedingten Sprungbefehle das Statusregister des Prozessors aus.
Im Statusregister speichert der Prozessor wichtige Informationen über seine generelle Betriebsart (zum Beispiel ob Interrupts zugelassen sind oder nicht) und Informationen über die
Ergebnisse der letzen durchgeführten Operation der ALU. Bestimmte Befehle, zum Beispiel
ADD können beim Überlauf des Rechenergebnisses dort das entsprechende Carry-Flag setzten oder, falls das Ergebnis der Operation Null war das Zero-Flag setzten.
Bevor Sprungbefehle bestimmte Informationen aus dem Statusregister und den darin befindlichen Statusflags entnehmen können, müssen die Statusflags durch einen vorausgehenden Befehl erst richtig gesetzt werden. Bei unserer Praktikums-CPU benötigen wir immer
den CP-Befehl (CP = Compare = Vergleich zweier Registerwerte) vor der Ausführung eines
Sprungbefehls. Dieser vergleicht die angegebenen Operanden miteinander und teilt das Ergebnis dem Statusregister mit. Bedingte Sprungbefehle reagieren auf Flags, beeinflussen sie
jedoch nicht. Dies ist sehr wichtig, wenn man verschiedene Flags nacheinander durch Sprungbefehle nutzen möchte.
2.2.4
Sprungadressen
Sprungadressen sind im eigentlichen Sinne Parameter der Sprungbefehle(Abschnitt 2.2.3). Sie
stellen die Weite oder auch Distanz eines Sprungs dar, also die Anzahl an Instruktionen, die
ausgelassen werden sollen, bis der nächste abzuarbeitende Befehl folgt. Da es zwei Arten von
Sprungrichtungen geben kann (vor- oder rückwärts), würde dies im mathematischen Sinn
über das Vorzeichen entschieden werden. Allerdings wird dies nur sehr selten verwendet, da
die meisten Programmzähler die gesamte Breite der möglichen Adressen ausschöpfen und
somit intern nur vorzeichenlose (absolute) Werte verwenden. So auch der Programmzähler
der Praktikums-CPU. Wenn also gesprungen wird, dann nur vorwärts, wobei ein Rücksprung
durch einen Vorwärtssprung mit Zählerüberlauf realisiert ist.
In den meisten Assemblersprachen können Sprungadressen in den folgenden zwei Arten
dargestellt werden.
ˆ als direkte Adresse in Form eines konkreten Zahlenwertes
ˆ als indirekte Adresse durch Sprungmarken
Ein konkretes Beispiel beider Varianten mit äquivalentem Ergebnis ist in Listening 2.3.5 zu
sehen. Die Erklärung zu den verwendeten Assemblerbefehlen ist in der RISC INSSET.PDF
Datei zu finden. Listening 2.3.5
# Die f o l g e n d e n b e i d e n Assemblercode A b s c h n i t t e
# b e i n h a l t e n d i e s e l b e Sprunganweisung mit
# ä q u i v a l e n t e r A d r e s s e in d i r e k t e r und i n d i r e k t e r Form
KAPITEL 2. GRUNDLAGEN
8
# S p r u n g b e f e h l mit d i r e k t e r A d r e s s e
ADD R2 , R10
CP R2 , R3
IMM 0 x000
BRE 0 x3
MUL R2 , R5
XOR R2 , R7
ST R2 , R11
LD R2 , R12
# S p r u n g b e f e h l mit i n d i r e k t e r A d r e s s e durch Sprungmarke JMP MARK
ADD R2 , R10
CP R2 , R3
BRE JMP MARK
MUL R2 , R5
XOR R2 , R7
ST R2 , R11
JMP MARK:
LD R2 , R12
Direkte Adressen stellen die ursprüngliche Form dar, die auch direkt von der Hardware interpretiert werden kann. Aufgrund der Tatsache, dass aber in der Regel jedem Assemblerbefehl
ein Maschinenbefehl im Verhältnis 1:1 zugrundeliegt, ist die Umsetzung von Sprungmarken
hardwaretechnisch mit reinem Assemblerkonzept nur schwer möglich, da Marken im Grunde
Synonyme für Zahlenwerte sind. Die Abbildung dieser Synonyme“ auf konkrete Zahlenwerte
”
müsste somit zur Laufzeit erfolgen, wodurch alleine für die Verwendung des Konzepts von
Sprungmarken zusätzliche Hardware notwendig wäre. Die meisten Assemblersprachen nutzen deshalb den Einsatz von mitgelieferten Compilern um dieses Problem zu umgehen. Diese
ermöglichen es, eine virtuelle Erweiterung des ursprünglichen Befehlsspektrums der Hardware (hier CPU) zu gewährleisten. Für Sprungmarken würde dies konkret bedeuten, dass
die Abbildung der Marke auf den jeweiligen Zahlenwert durch den Compiler erfolgt, der das
Assemblerprogramm in den ursprünglichen Befehlssatz der Hardware überführt. So werden
zum Beispiel oft auch Engpässe bei der Verwaltung von Opcodes umgangen, indem z.B. einem
virtuellen Assemblerbefehl ein oder mehrere Befehle der Hardware zugrundeliegen. Innerhalb
des Praktikums ist die Verwendung von Sprungadressen in beiden Varianten möglich, da Sie
in späteren Versuchen Ihre entwickelten Programme mittels eines bereitgestellten Compilers
in die Maschinensprache der Praktikums-CPU überführen.
2.2.5
Instruktionsformate
Rechner besitzen eine feste Anzahl von Maschinenbefehlen, die sich in mehrere verschiedenen Befehlsformatgruppen einteilen lassen. Diese Befehlsformatgruppen werden beim Entwurf
eines Prozessors definiert. Die Programmierung eines Rechners erfolgt letztlich (nach dem
Übersetzen des Hochsprachenprogramms) durch Maschinenbefehlsfolgen, die im Speicher ab-
KAPITEL 2. GRUNDLAGEN
9
gelegt sind und nach einem festen Algorithmus abgearbeitet werden.
Vor Festlegung der Prozessorstruktur muss erst das Verhalten des Prozessors in Form des
Maschinenbefehlssatzes definiert werden. Dabei sind festzulegen:
ˆ Befehlsformate
ˆ Befehlsvorrat (Maschinenbefehlsmenge)
ˆ Wirkung der Befehl
Ein Maschinenbefehl wird als binäres Wort (Befehlsformat) repräsentiert, wobei allerdings die Wortlänge nicht konstant sein muss. Das Befehlsformat legt fest, wie die Stellen
eines Befehlswortes zu interpretieren sind, wobei oft jeweils mehrere Stellen zu Feldern zusammengefasst werden. Das Befehlsformat definiert also die Einteilung von Befehlswörtern
in Felder und legt deren Bedeutung fest. Im Allgemeinen reicht ein einziges Format für alle
Befehle des Maschinenbefehlssatzes nicht aus. Daher werden mehrere Formate definiert.
Der Befehlsvorrat definiert die Menge aller syntaktisch korrekten Befehle. Der Maschinenbefehlsvorrat und die Interpretation der Befehle beschreiben die Funktion eines Prozessors. Aus diesen Informationen lassen sich Hinweise auf die interne Struktur des Prozessors
ableiten.
Für unsere CPU wurden folgende Architektur-Entscheidungen getroffen: Alle Maschinenbefehle haben eine identische Länge von 16-Bit und bestehen aus einem 6-Bit-Feld für den
Operationscode (siehe Anmerkungen und Tipps) sowie einem 10-Bit Adressfeld. Diese letzten
10 Bit werden in bis zu zwei Felder aufgeteilt. Das Adressfeld dient zur Adressierung eines
Datums im Hauptspeicher, der Register oder eines Sprungziels. Die einzelnen Befehlsformat
werden jetzt genauer betrachtet:
ˆ Befehlsformat 1
– Aufteilung der Bits
IN[15:14] => Opcode A (Befehlsklasse)
IN[13:10] => Opcode B (eigentlicher Befehl/Instruktion der Klasse)
IN[9:5]
=> Operand A (Register)
IN[4:0]
=> Operand B (Register)
– Opcode
Der Opcode kennzeichnet die auszuführende Operation. Unterschieden wird zwischen Opcode A und B, wobei A die Befehlsklasse (Arithmetisch, Sprung oder
Datentransfer) und B die Instruktion der Klasse darstellt. Jeder Befehl hat einen
eigenen Opcode, etwa die Addition, Subtraktion, das Kopieren von Registern oder
Laden und Speichern von Registern aus dem Arbeitsspeicher.
Jedem Opcode wird ein kurzes Wort, ein so genanntes Mnemonic (AssemblerbefehlBezeichner), zugeordnet. Auf die meisten Opcodes müssen Adressen, Konstanten
oder ähnliches folgen, die dann zusammen mit dem Opcode einen Maschinenbefehl
bilden.
– Beispiel
Befehl:
Ergebnis:
ADD R1, R2
R1 = R1 + R2
KAPITEL 2. GRUNDLAGEN
10
Befehlswort: 0x4022
Opcode A: 0x1 (arithmetische Instruktion)
Opcode B: 0x0 (Addition zweier Register)
Im Befehlswort kennzeichnet 0x die hexadezimale Darstellung. Des Weiteren sind
neben der hexadezimalen auch die dezimale (0d) und die binäre (0b) Darstellung
von Zahlen möglich.
ˆ Befehlsformat 2
– Aufteilung der Bits
IN[15:14] => Opcode A (Befehlsklasse)
IN[13:10] => Opcode B (eigentlicher Befehl/Instruktion der Klasse)
IN[9:5]
=> Operand A (Register)
IN[3:0]
=> Immediate[3:0] (konstanter 4-Bit Wert [0:15])
IN[4]
=> nicht definiert
– Beispiel
Befehl:
IMM 0d0
ADDI R1, 0d14
Ergebnis:
R1 ← R1 + 14
Befehlsworte: 0xF000
0x442E
ˆ Befehlsformat 3
– Aufteilung der Bits
IN[15:12] => Opcode (Immediate, IMM)
IN[11:0] => Immediate[15:4] (konstanter 12-Bit Wert [0:4095])
– Opcode
0xF => IMM
// RIM M ← immediate
Der IMM-Befehl ist nur sinnvoll, wenn er direkt vor einem Befehl steht, der ebenfalls ein Immediate enthält wie z.B. ADDI. In jedem Fall wird der Immediate Wert
auf 16-Bit erweitert. Die höherwertigen 12-Bits liefern die IMM-Instruktion, die
restlichen vier die Folgeinstruktion. Die höherwertigen 12-Bits der IMM-Instruktion
bleiben auch nach der Instruktion im IMM-Register erhalten. Deshalb ist darauf
zu achten, dass dieser Wert je nach Bedarf explizit auf Wert 0 (default) rückgesetzt
wird.
– Beispiel
Befehle:
IMM 0d123
ADDI R1, 0d14
Ergebnis:
R1 ← R1 + (123 ∗ 24 + 14)
Befehlsworte: 0xF07B
0x442E
KAPITEL 2. GRUNDLAGEN
2.2.6
11
Datenspeicher
Meistens reichen die Register nicht aus, um ein Problem zu lösen. In diesem Fall muss auf den
Hauptspeicher des Computers zugegriffen werden, der wesentlich mehr Information speichern
kann. Für den Assemblerpogrammierer sieht der Hauptspeicher wie ein riesiges Array von
Registern aus, deren Einträge je nach Wunsch eine Länge von 8, 16 oder 32 Bit aufweisen.
Die kleinste adressierbare Einheit ist also ein Byte (= 8 Bit). Daher wird auch die Größe
des Speichers in Bytes gemessen. Um auf einen bestimmten Eintrag des Arrays ”Hauptspeicherßugreifen zu können, muss der Programmierer den Index, d. h. die Adresse des Eintrages
kennen. Das erste Byte des Hauptspeichers bekommt dabei die Adresse 0x0000, das zweite
die Adresse 1 usw. In einem Assemblerprogramm können Variablen angelegt werden, indem
einer Speicheradresse ein Label zugeordnet und dabei Speicherplatz in der gewünschten Größe
reserviert wird.
2.2.7
Grundlegende Befehle und Konstrukte
Für die Bearbeitung der Aufgaben dieses Versuches werden Sie die zur Verfügung gestellte
Praktikums-CPU und die in das System eingebundene VGA-Grafikeinheit programmieren.
Für den Fall, dass Ihre Kenntnisse in Assemblerprogrammierung nicht ausreichend sein sollten, werden im Folgenden die wesentlichen Programmierkonstrukte betrachtet, bzw. rekapituliert.
Kontrollpfade: IF-ELSE-Statements
IF-ELSE-Kontrollpfade werden auch in Assembler dazu benötigt, um die verschiedenen Befehlssequenzen gemäß der Programmlogik deterministisch auszuführen. Obwohl dieses Konstrukt bei Hochsprachen direkt als eigenständige Direktive eingebettet ist, wird in Assembler
dazu eine Kombination aus mehreren Befehlen benötigt. Zunächst muss eine Abfrage formuliert werden, dem ein Vergleich mit einer entsprechenden Bedingung voraus geht. Anschließend
wird eine Sprungdirektive festgelegt, an welcher Stelle das Programm entsprechend dem Vergleichsergebniss weitergeführt werden soll. Das folgende Beispiel zeigt den Asemblercode einer
allgemeinen IF-ELSE-Abfrage.
Listing 2.1: Assemblercode-Beispiel für IF-ELSE Statement
# Abfrage : IF (R5 == R6) { R5 ++} ELSE { R5=0}
CP R5 , R6
# S p r i n g e zum ELSE−Zweig s o f e r n Bedingung n i c h t e r f ü l l t
BRNE ELSE CLAUSE
INC R5
JMP END IF
# ELSE−Zweig
ELSE CLAUSE :
CLR R5
END IF :
Shiften und Rotieren von Bits
BIT-SHIFT-Befehle verschieben alle Bit in einem Register um eine Position nach links oder
KAPITEL 2. GRUNDLAGEN
12
rechts. Beim Shiften wird eine Null eingefügt und das Bit, das rausgeschoben wird geht
verloren. Beim Rotieren wird das Bit, das rausgeschoben wird, wieder eingefügt.
Listing 2.2: Shiften und Rotieren von Bits in Assembler
# Eine P o s i t i o n nach l i n k s SHIFTEN
# R0 [ 1 5 : 1 ] = R0 [ 1 4 : 0 ] , R0 [ 0 ] = 0
LSL R0
# Eine P o s i t i o n nach r e c h t s SHIFTEN
# R1 [ 1 4 : 0 ] = R1 [ 1 5 : 1 ] , R1 [ 1 5 ] = 0
LSR R1
# Eine P o s i t i o n nach l i n k s ROTIEREN
# R2 [ 1 5 : 1 ] = R2 [ 1 4 : 0 ] , R2 [ 0 ] = R2 [ 1 5 ]
ROL R2
# Eine P o s i t i o n nach r e c h t s ROTIEREN
# R3 [ 1 4 : 0 ] = R3 [ 1 5 : 1 ] , R3 [ 1 5 ] = R3 [ 0 ]
ROR R3
Schleifen
Schleifen werden in der Regel dazu genutzt, um eine Sequenz von Befehlen mehrfach auszuführen. In Assembler werden Schleifen mit bedingten Sprunganweisungen realisiert. Am
Anfang der Schleife benötigen wir hierzu ein Sprungziel, welches den Kopf der Schleife darstellt. Danach folgt die Befehlssequenz, die durch die Schleife mehrfach wiederholt werden
soll. Das Ende der Schleife wird durch den Schleifenbefehl festgelegt.
Listing 2.3: Assemblercode-Beispiel für eine Schleife in Assembler
#
S c h l e i f e n k o n s t r u k t : f o r { i =0; i < 5 ; i++} {R5 = R5 * 2}
IMM 0 x000
LDI R2 , 0 x2
# D e f i n i t i o n d e s A b b r u c h k r i t e r i u m s 5 in R2
LDI R1 , 0 x5
# S c h l e i f e n z ä h l e r
i in R0 a u f Null s e t z e n
CLR R0
# Schleifenrumpf
LOOP:
MUL R5 , R2
# S c h l e i f e n z ä h l e r erhöhen
INC R0
# Check d e r Abbruchbedingung i < 5
CP R0 , R1
# Rücksprung s o l a n g e i < 5
BRL LOOP
KAPITEL 2. GRUNDLAGEN
2.3
13
Grafikprogrammierung
Bei der Grafikprogrammierung ist vor allem die Geschwindigkeit sehr wichtig. Im Allgemeinen
sind Grafikoperationen sehr rechenaufwendig und benötigen daher viel Prozessorzeit. Um
diese Geschwindigkeit zu erreichen, wurden die entsprechenden Programmroutinen früher oft
in Assembler implementiert. Noch heute kann man Unterprogramme mit Assemblerbefehlen
im Quellcode vieler höherer Programmiersprachen sehen.
Die Grafikkarte eines PCs kennt verschiedene Bildschirmmodi, in denen die Anzahl der
Pixel und Farben unterschiedlich sind. In unserem Fall wird der Bildschirm in der StandardVGA-Auflösung von 640 x 480 geometrischen (physikalischen) Bildpunkten angesteuert. Aufgrund der Adressierungskomplexität der Pixelmenge, sieht die interne Darstellung der PraktikumsCPU ein Down-Sampling um den Faktor 4 vor. Somit ist es möglich eine geringere Auflösung
mit 160 x 120 logischen Bildpunkten zu betreiben, wobei 1 logischer Pixel einem Quadrat aus
4x4 geometrischen Pixeln entspricht.
Der Bild- bzw. Grafikspeicher ist im SRAM des FPGA-Boards abgelegt. Beginnend ab
Adresse 0x0000 gibt es zwei Arten der Pixel-Adressierung.
ˆ Flush Adressierung, d.h. jede Adresse adressiert immer alle Pixel zugleich, wodurch
jeder Pixel den gleichen Farbwert zugewiesen bekommt (löschen des Bildspeichers mit
einer einheitlichen Farbe). Den Pixeln können somit nicht unterschiedliche Farbwerte
zugewiesen werden. Des Weiteren muss berücksichtigt werden, dass die Zeitspanne die
für einen vollständigen Flush benötigt wird (ca. 3,1 Millisekunden), deutlich länger ist,
als die Ausführung einer einzigen Assemblerinstruktion durch die CPU (ca. 167 Nanosekunden). Während dieser Phase ist es nicht möglich den Bildspeicher zu adressieren,
noch die Art der Pixel-Adressierung zu ändern.
ˆ Indirekte Adressierung über ein 2D kart. Koordinatensystem, d.h. jede Adresse adressiert genau einen Pixel und unterteilt sich in Lower-Byte (Adresse[7:0]) für die YKoordinate und Upper-Byte (Adresse[15:8]) für die X-Koordinate. Jedem Pixel kann
somit ein individueller Farbwert zugewiesen werden.
Die Art der Adressierung wird über einen Konfigurationswert des Video-Memory-Controllers
(VMC) im Hauptspeicher (RAM) der CPU an der Adresse 0x0007 festgelegt, wobei der Wert
0x0002 für Flush und 0x0001 für die Indirekte Adressierung steht. Da die Grafikeinheit
lediglich 8 Farben darstellen kann, stehen für jeden Pixel 3-Bit für die Farbkodierung (RGB)
zur Verfügung, d.h. bei einem Schreibzugriff auf den Bildspeicher werden nur die 3 niederstwertigen Bits gespeichert und die restlichen Bits des Datums ignoriert. Bei einem Lesezugriff
auf den Bildspeicher werden die nicht vorhandenen Bits des gelesenen Wortes mit Nullen
aufgefüllt.
Das Koordinatensystem hat seinen Ursprung in der linken oberen Ecke des Bildschirms.
Dort befindet sich der Punkt (0, 0) mit der Adresse 0x0000. Wird der Bytewert an dieser
Position verändert, so ändert sich automatisch die Farbe des Pixels auf dem Bildschirm. Um
nun jeden Punkt einzeln zu erreichen (indirekte Adressierung), kann man die Adresse auch
so formulieren:
Basisadresse ist 0x0000, die Verschiebung zum aktuellen Pixel wird durch die Angabe der
X-([0:159]) und Y-Koordinate ([0:119]) intern automatisch durch den VMC berechnet. Um
die jeweiligen Koordinaten an die richtige Bit-Position der Adresse zu setzen, können Sie BIT-
KAPITEL 2. GRUNDLAGEN
14
SHIFTING verwenden. Das folgende Assemblerprogramm realisiert das Setzen des Pixels (23,
67) mit der Farbe Blau, unter Verwendung von Bit-Shifting kombiniert mit Schleifen:
Listing 2.4: Setzen eines blauen Pixels auf dem Monitor
# a k t i v i e r e Flush Modus
IMM 0 x000
LDI R0 , 0 x2
LDI R1 , 0 x7
ST R0 , R1
LDI R0 , 0 x7
CLRV R3
# W a r t e s c h l e i f e b i s Flush b e e n d e t i s t ( 3 . 1 M i l l i s e k u n d e n )
CLR R0
LDI R1 , 0 x4883
AWAIT FLUSH :
INC R0
CP R0 , R1
BRNE AWAIT FLUSH
# a k t i v i e r e n d e r i n d i r e k t e n A d r e s s i e r u n g von P i x e l n
IMM 0 x000
LDI R0 , 0 x1
LDI R1 , 0 x7
ST R0 , R1
# s e t z e n d e r X−K o o r d i n a t e in R1=0d23=0x0017
IMM 0 x001
LDI R1 , 0 x7
# s e t z e n d e r Y−K o o r d i n a t e in R2=0d67=0x0043
IMM 0 x004
LDI R2 , 0 x3
# S c h l e i f e um X−K o o r d i n a t e d e s P i x e l s an P o s i t i o n [ 1 5 : 8 ]
# d e r A d r e s s e zu s h i f t e n
SHIFT X COORD :
# X−K o o r d i n a t e um e i n B i t nach l i n k s s c h i e b e n
LSL R1
# Z ä h l e r i n k r e m e n t i e r e n
INC R3
# V e r g l e i c h Abruchsbedingung
IMM 0 x000
LDI R4 , 0 x8
CP R3 , R4
BRNE SHIFT X COORD
KAPITEL 2. GRUNDLAGEN
15
# Verkn üpfen d e r f e r t i g e n Z i e l a d r e s s e aus b e i d e n Koordinaten
# X [ 1 5 : 8 ] und Y [ 7 : 0 ] in R1
OR R1 , R2
# s e t z e n d e s P i x e l s ( 2 3 , 6 7 ) mit d e r Farbe Blau
IMM 0 x000
# s e t z e n d e r Farbe Blau in R3
LDI R3 , 0 x1
STV R3 , R1
2.3.1
Sprites - bewegliche Grafiken
Ein Sprite ist ein zweidimensionales Grafikobjekt. Bei einem Sprite handelt es sich um einen
kleinen rechteckigen Speicherbereich, der als Bildschirmausschnitt den Bildschirminhalt partiell verdeckt oder sich mit ihm mischt. Ein Sprite wird von der Grafikhardware über das
Hintergrundbild, bzw. den restlichen Inhalt der Bildschirmanzeige eingeblendet.
Die Positionierung wird dabei komplett von der Grafikhardware erledigt. Die aktuelle
Position des Sprites wird in einem Registersatz gehalten, so dass eine Änderung der Registereinträge zu einer Bewegung des Sprites führt. Als Beispiel kann ein Mauszeiger betrachtet
werden, der heutzutage von den meisten Grafikkarten als Hardware-Sprite zur Verfügung
gestellt wird. In vergangenen Zeiten waren Sprites vor allem in Videospielen und Homecomputern verbreitet. Der C64 beispielsweise verdankt einen Großteil seiner Grafikfähigkeiten der
Unterstützung von Sprites.
2.3.2
Funktionsweise eines Sprites
Das Sprite wird von der Grafikhardware an der gewünschten Position im Bild eingefügt.
Weil dadurch das restliche Bild im Grafikspeicher nicht beeinflusst wird, muss dieses nicht
immer wieder neu dorthin kopiert werden. Durch diese Entlastung des Hauptprozessors sind
Sprites sehr schnell und gleichzeitig einfach zu programmieren, erfordern allerdings zusätzliche
Hardwareressourcen.
Die Daten für die Sprite-Grafik werden dabei entweder direkt in Registern der Grafikhardware vorgehalten oder in speziellen RAM-Bereichen, auf die diese Hardware einen genügend
schnellen Zugriff hat.
Zur Bewegung eines Sprites reicht es aus, lediglich dessen i und j Koordinate zu ändern.
Die aktuelle Position (i, j) des Sprites wird in einem Register gehalten, so dass eine Änderung
des Registereintrags (i+1, j+1) zu einer Bewegung des Sprites führt. In den drei Abbildungen
2.2, 2.3 und 2.4 sehen Sie, wie die Bewegung realisiert wird.
Bestimmung der Koordinaten bei der Bewegung des Sprites über den Bildschirm
Die komplizierte Berechnung der Adresse im Grafikspeicher und das Umkopieren des Inhalts
entfällt, was ebenfalls den Hauptprozessor entlastet. Der Grafikprozessor fügt selbständig an
der vorgegebenen Koordinate das Sprite beim Aufbau des nächsten Bildes im Vordergrund
ein. Auch animierte Sprites sind möglich.
KAPITEL 2. GRUNDLAGEN
16
Abbildung 2.2: Graphische Darstellung eines Sprites auf der Position (i, j)
Abbildung 2.3: Graphische Darstellung eines Sprites auf der Position (i, j+1)
Abbildung 2.4: Graphische Darstellung eines Sprites auf der Position (i+1, j+1)
2.3.3
Sprite-Implementierung im Praktikumssystem
Das VGA-Modul des Praktikumsprozessorsystems unterstützt genau ein Sprite mit 8x8 Pixeln. Jedes Pixel des Sprites kann entweder weiß oder durchsichtig sein, abhängig vom zugehörigen Bitwert. Ist das entsprechende Bit gesetzt, so ist das Pixel des Sprites weiß, ansonsten durchsichtig. Jede der acht Zeilen des Sprites wird in einem 8-Bit-Register gespeichert,
die an den Adressen 0xFFF0 - 0xFFF7 liegen. Die Position des Sprites wird in zwei weiteren Registern an den Adressen 0xFFF8 und 0xFFF9 gespeichert. Die Positionsregister sind
jeweils 8 Bit breit.
Beschreibung der Hardware-Register
KAPITEL 2. GRUNDLAGEN
17
ˆ Adresse 0xFFF0, 8-Bit Register - oberste Zeile des Sprites
ˆ Adresse 0xFFF1, 8-Bit Register - zweite Zeile des Sprites
ˆ Adresse 0xFFF2, 8-Bit Register - dritte Zeile des Sprites
ˆ Adresse 0xFFF3, 8-Bit Register - ...
ˆ Adresse 0xFFF4, 8-Bit Register - ...
ˆ Adresse 0xFFF5, 8-Bit Register - ...
ˆ Adresse 0xFFF6, 8-Bit Register - ...
ˆ Adresse 0xFFF7, 8-Bit Register - unterste Zeile des Sprites
ˆ Adresse 0xFFF8, 8-Bit Register - horizontale Position des Sprites
ˆ Adresse 0xFFF9, 8-Bit Register - vertikale Position des Sprites
2.4
Entwicklungsumgebung und Compiler
Das TINY RISC STUDIO v1.0 ist eine Entwicklunsgumgebung, welche Ihnen das Implementieren, Kompilieren, Debuggen und Emulieren von Assemblerprogrammen der PraktikumsCPU ermöglicht. Die fertige Ausgabe des Compilers wird direkt durch Pfadangabe in das
Instruktionsregister (ROM) der CPU geschrieben. Der Aufbau und die Nutzung der GUI
(Abbildung 2.5) ist einfach gehalten und besteht im wesentlichen aus 8 Komponenten, deren
Nutzung im Folgenden erläutert wird.
1. Menüleiste
2. Toolbar
3. Eingabefeld, Zwischencode und Ausgabe
4. Interaktive Boardgrafik
5. VGA-Memory Output
6. Interaktive Anzeige interner Werte
7. Putty-Konsole
8. Status Konsole
2.4.1
Menüleiste
Die Menüleiste, in Abbildung 2.5 als 1. gekennzeichnet, bietet Standardoptionen zum Compilieren, zum Laden und zum Speichern des Quellcodes. Die Menüunterpunkte sind folgend
aufgelistet und beschrieben.
hen (beispielsweise wenn der VGA-Memory geflusht werden soll, obwohl der Modus nicht
auf flush gesetzt wurde).
Zur Orientierung des Benutzers befindet sich bei pausierter Emulation ein roter Pfeil im Codefenster, welcher die nächste auszuführende Codezeile angibt, wodurch der interne Stand
des Emulators erkennbar ist.
KAPITEL
GRUNDLAGEN
6.2.2. Komponenten
18
Abbildung 6.1.: Emulator mit Einteilung der Komponenten
Abbildung 2.5: GUI des TINY RISC STUDIO v1.0
6.2.1. Menüleiste
File
Die Menüleiste, in Abbildung 6.1 als 1. gekennzeichnet, bietet Standardoptionen zum Compilieren,
zum
Laden
zum Speichern
des Quellcodes.
Die MenüunterpunkteCtrl
sind+folgend
ˆ New:
Per
Klick
aufund
diesen
Button oder
mit der Tastenkombination
N, wird ein
aufgelistet und beschrieben.
neues Dokument angelegt. Als Folge wird das komplette Eingabefeld gelöscht und der
Titel zu Unnamed Document“ geändert. Bevor ein neues Dokument angelegt wird, wird
”
der Benutzer gefragt, ob das aktuelle Dokument gespeichert werden soll. Bei Bestätigung
wird das Speichern-Fenster zur Auswahl37des Dateipfades und -namen geöffnet.
ˆ Open: Per Klick auf diesen Button oder mit der Tastenkombination Ctrl + O, wird
ein neues Dokument geöffnet. Bevor ein Dokument geöffnet wird, erscheint jedoch die
Meldung, ob das aktuelle Dokument gespeichert werden soll. Anschließend kann die zu
ladende Datei ausgewählt und ihr Inhalt im Eingabefeld angezeigt werden. Pfad und
Dateiname erscheinen weiterhin am Anfang des Eingabefeldes. Zu beachten ist, dass
nur txt-Dateien korrekt geladen werden können.
ˆ Save: Per Klick auf diesen Button oder mit der Tastenkombination Ctrl + S, wird das
aktuelle Dokument gespeichert. Speicherpfad und Dateiname entsprechen dabei denen,
die am oberen Ende des Eingabefeldes angezeigt werden. Sollte jedoch kein Speicherpfad
angegeben worden sein, d.h. ein Unnamed Document“ vorliegen, so wird stattdessen
”
Save as“ ausgeführt.
”
ˆ Save as: Per Klick auf diesen Button oder mit der Tastenkombination Ctrl + Shift + S,
wird das aktuelle Dokument an der ausgewählten Stelle unter dem ausgewähltem Namen
abgespeichert. Zu beachten ist, dass eine Dateiendung nicht automatisch angehängt
wird, d.h. soll eine txt-Datei erstellt werden, muss der Dateiname mit .txt“ enden.
”
KAPITEL 2. GRUNDLAGEN
19
Options
ˆ Compiler output directory: An dieser Stelle lassen sich Pfad und Dateinamen des
Compiler-Outputs bestimmen. Über den Compile-Button der Toolbar kann anschliessend die angegebene Datei erzeugt werden. Zu beachten ist, dass das Anhängen der
Dateiendung nicht automatisch erfolgt, d.h. zur Erstellung einer mem-Datei muss der
Dateiname mit .mem“ enden.
”
ˆ Compiler output format: Hier kann angegeben werden, ob das Compilat binär oder
hexadezimal ausgegeben werden soll. Als Default-Wert ist hexadezimal eingestellt.
ˆ Commentary Symbol: Hier kann das zu verwendende Kommentarzeichen bestimmt
werden. Zur Auswahl stehen Raute und Doppelslash. Der Default-Wert ist auf die Raute
eingestellt. Das Symbol ist jeder Zeit änderbar, der Compiler wird jedoch ausschließlich
das aktuell als Kommentarsymbol ausgewählte Zeichen erkennen. Desweiteren richtet
sich die Funktion des Popup-Menüs zum Setzen und Entfernen von Kommentarzeichen
nach der aktuell getroffenen Auswahl.
2.4.2
Toolbar
Die in Abbildung 2.5 als 2. gekennzeichnete Toolbar ermöglicht die schnelle und einfache
Steuerung des Emulators und Compilers.
ˆ Compile: Über den Compile-Button wird der eingegebene Programmcode compiliert
und am Compiler output directory“ angegebenen Pfad und Namen gespeichert. Soll”
ten unter Compiler output directory“ keine Angaben vorliegen, wird der Benutzer
”
weiterhin aufgefordert diese Einstellungen zu treffen, bevor anschließend das Compilat
erzeugt wird. Sollten im Quellcode Syntaxfehler vorhanden sein, wird stattdessen eine
entsprechende Fehlermeldung mit Beschreibung auf der Konsole ausgegeben.
ˆ Max/Min: Die Anzeige des VGA-Memory Output ist ein wichtiger Bestandteil bei der
Arbeit mit grafischen Aspekten. Dennoch kann die Anzeige für eine genaue Überprüfung
der einzelnen Pixel zu klein sein. Für einen solchen Fall ist die Anzeige über diesen
Knopf vergrößerbar. Zu diesem Zweck werden die interaktive Boardgrafik, die interaktive Anzeige der internen Werte und die Putty-Konsole ausgeblendet und die Anzeige des
VGA-Memory Outputs entsprechend vergrößert. Bei erneuter Benutzung dieses Buttons werden die Veränderungen der Anzeige rückgängig gemacht und der alte Zustand
wiederhergestellt.
Emulation
ˆ New: Vor dem Start einer Emulation muss diese zunächst initialisiert werden. Zu diesem Zweck wird bei Verwendung dieses Buttons der Assemblercode intern compiliert,
wodurch das Zwischencode- und Ausgabefenster aktualisiert, jedoch kein Compilat als
externe Datei erzeugt wird. Desweiteren werden alle Werte der interaktiven Anzeige der
internen Werte auf ihren default-Zustand zurück gesetzt. Sollten im Code noch Fehler
existieren oder kein Code vorhanden sein, so schlägt die Initialisierung fehl und eine
entsprechende Fehlermeldung wird auf der Konsole ausgegeben. Da die Emulation auf
KAPITEL 2. GRUNDLAGEN
20
dem Stand der Initialisierung arbeitet, werden Veränderungen im Code für die laufende Emulation nicht berücksichtigt. Soll neuer Code oder aktualisierter Code emuliert
werden, muss über diesen Button erneut eine Initialisierung vorgenommen werden.
ˆ Next Stop: Über diesen Button ist exakt die nächste Codezeile ausführbar. Welche dies
aktuell ist, gibt der rote Pfeil beim Eingabefeld an. Sollte kein roter Pfeil zu sehen sein,
beendet sich die Emulation im Initialisierungszustand, d.h. es wird bei der ersten Codezeile begonnen, da der Programmzähler noch Null ist. Der Next Step“-Button kann
”
beliebig oft nacheinander ausgeführt werden. Er bietet sich besonders gut zum Debuggen an, da bei dieser Variante der Emulation in der Konsole zusätzliche Warnhinweise
angezeigt werden. Diese Warnhinweise machen auf ein Verhalten aufmerksam, welches
zwar keinen Programmfehler hervorruft, jedoch wahrscheinlich nicht beabsichtigt war,
beispielsweise bei Werten außerhalb des Wertebereichs.
ˆ Start: Durch diesen Button wird eine Emulation gestartet, bei welcher der vollständige
Code ausgeführt wird. Zu diesem Zweck wird stets bei der ersten Codezeile mit DefaultWerten gestartet. Die Emulation wird gestoppt, sobald ein Breakpoint erreicht wird
oder der Benutzer den Stop“-Button betätigt. Sollte der Code nicht initialisiert sein
”
oder verändert worden sein, muss die Emulation zunächst über den New“-Button neu
”
initialisiert werden, bevor die Start“-Funktion erneut genutzt werden kann.
”
ˆ Resume: Dieser Button dient dem Fortsetzen einer Emulation. Die Emulation startet
an der zuletzt ausgeführten Codezeile oder bei der ersten Codezeile, sollte der Initialisierungszustand vorliegen und läuft bis zum Erreichen eines Breakpoints oder dem Stop“
”
durch den Benutzer. Die Resume“-Funktion läuft dabei nicht auf Default-Werten, son”
dern nutzt die zuletzt aktuellen Werte. Als Folge dessen lässt sich eine Emulation über
diese Funktion fortsetzen und alle vorher getätigten Änderungen berücksichtigen. Zur
effektiven Benutzung wird hierzu ein eventueller Breakpoint in der Codezeile, die als
erstes ausgeführt wird, ignoriert. Sollte der Code nicht initialisiert sein oder verändert
worden sein, muss die Emulation zunächst über den New“-Button neu initialisiert wer”
den, bevor die Resume“-Funktion genutzt werden kann.
”
ˆ Stop: Dieser Button bricht eine laufende Emulation ab. Sollte keine Emulation laufen,
dann bleibt die Aktion funktionslos und es wird eine Meldung auf der Konsole ausgegeben. Beim Beenden der Emulation wird im Eingabefenster ein roter Pfeil auf Höhe der
Codezeile gesetzt, die als nächstes auszuführen gewesen wäre.
ˆ Running: Hinter diesem Schriftzug ist ein roter oder grüner Punkt zu finden, welcher
angibt, ob die Emulation läuft oder gestoppt ist. Ein grüner Punkt bedeutet dabei,
dass die Emulation arbeitet und ein roter Punkt, dass sie nicht arbeitet. Zu beachten
ist die Verwendung von Breakpoints sowie dem Next Step“-Button. Da letzterer nur
”
eine Codezeile ausführt und bei Breakpoints in der Regel nur eine begrenzte Anzahl
an Codezeilen ausgeführt werden, werden diese in der Praxis so schnell verarbeitet
werden, dass ein Umschalten von Rot-Grün-Rot nicht zu erkennen ist, sondern der
Punkt konstant auf Rot bleibt.
KAPITEL 2. GRUNDLAGEN
2.4.3
21
Eingabefeld, Zwischencode und Ausgabe
Der in Abbildung 2.5 als 3. gekennzeichnete Bereich beinhaltet das Eingabefeld, den Zwischencode und die Ausgabe. Über den Reiter am oberen Ende kann jeweils auf den entsprechenden
Bereich zugegriffen werden, wobei das Eingabefeld als Default dient.
ˆ Eingabefeld: Das Eingabefeld dient als Hauptarbeitsbereich. In diesem wird der Quellcode geladen, erstellt und bearbeitet. Am oberen Bereich des Eingabefelds wird weiterhin der Dateipfad und -name zum aktuell geöffneten Dokument angegeben, sofern
vorhanden. Im großen, weißen Bereich kann der Code beliebig editiert werden. Eine
Hilfe bietet dafür das Popup-Menü. Eine Bedienhilfe des Eingabefeldes stellt dabei die
Navigation am linken Rand dar. Sie zeigt nicht nur dynamisch die Nummerierung der
Codezeilen an, sondern bietet auch Interaktionsmöglichkeiten. Ein Klick auf die Navigation der entsprechenden Codezeile setzt einen Breakpoint, ein weiterer Klick entfernt
diesen wieder. Auf diese Weise können beliebig viele Breakpoints gesetzt werden. Die
Navigationszeile beinhaltet weiterhin Raum für die Anzeige des Programmzeigers, also
den aktuellen Stand der Emulation. Sollte eine Emulation ausgeführt worden sein, wird
ein roter Pfeil die entsprechende Zeile markieren.
ˆ Zwischencode: Das Zwischencodefenster ist nicht editierbar. Nach Nutzung des Com”
pile“-Buttons oder des New“-Buttons wird der Zwischencode erzeugt und angezeigt.
”
Der Zwischencode entspricht dem Quellcode, abgesehen davon, dass jegliche Kommentare und leere Bereiche entfernt wurden und IMM-Befehle eingefügt wurden. Zum Debuggen ist ein Blick in den Zwischencode hilfreich, da durch das automatische Einfügen
der IMM-Befehle vor bestimmten Operationen Programmierfehler entstehen können,
wenn der Benutzer von einem anderen IMM-Wert ausgeht, als dies real der Fall ist.
Weiterhin basiert die Emulation auf dem Zwischencode.
ˆ Ausgabe: Das Ausgabefenster ist nicht editierbar und der Inhalt wird nach Nutzung
des ”Compile“-Buttons oder des ”New“-Buttons erzeugt. Das Ausgabefenster zeigt an,
wie die exportierte ”.mem“-Datei aussieht oder aussehen würde.
2.4.4
Interaktive Boardgrafik
Die interaktive Boardgrafik, in Abbildung 2.5 als 4. gekennzeichnet, ermöglicht die Nutzung
der Peripherie des Boards, als wäre dieses real vorhanden. Die LEDs und 7-Segment-Anzeige
werden automatisch verändert und eingeschaltet, sobald intern der entsprechende Wert gesetzt
ist. Auf diese Weise ist das Ergebnis der Emulation direkt sichtbar. Weiterhin sind sowohl
Buttons (unten links) als auch Switches (unten rechts) anklickbar und ermöglichen so eine
Beeinflussung der Emulation. Im Code abgefragte Memory-Werte der Buttons und Switches
beziehen sich direkt auf den Status der Buttons und Switches dieser Boardgrafik. Ihr Status
ist weiterhin auch in der interaktiven Anzeige der internen Werte einsehbar.
2.4.5
VGA-Memory Output
Über diese Anzeige, in Abbildung 2.5 als 5. gekennzeichnet, kann das Ausgabebild des VGAMemory betrachtet werden. Die Anzeige zeigt alle physikalischen Pixel des VGA-Memory mit
den dazugehörigen Farbwerten sowie den Sprite an, sofern dieser definiert wurde. Während
in Realität die Pixel des VGA-Memory ohne vorherige Definition einen beliebigen Farbwert
KAPITEL 2. GRUNDLAGEN
22
annehmen ( Pixelchaos“), ist dieses Verhalten durch das bunte Default-Bild des Miniaturbild”
schirms dargestellt. Über die interaktive Anzeige der internen Werte können Pixel zusätzlich
sowohl gelesen, als auch gesetzt werden. Die Anzeige wird automatisch aktualisiert und auf
dem Stand der internen Verarbeitung gehalten.
2.4.6
Interaktive Anzeige interner Werte
Diese Anzeige, in Abbildung 2.5 als 6. gekennzeichnet, beinhaltet verschiedene Tabellen, die
über die dazugehörigen Reiter wechselbar sind und alle relevanten Werte des internen Boardzustandes besitzen. Das Aussehen der einzelnen Tabellen unterscheidet sich je nach Darstellungsgebiet leicht voneinander, um dadurch zusätzliche Informationen geben zu können. Alle
Tabellenbesitzen eine dezimale, hexadezimale und binäre Darstellung des ausgewählten Wertes. Da diese drei Darstellungen in einer Tabellenzeile jeweils den selben Wert abbilden, sind
diese folglich äquivalent. Bis auf wenige Ausnahmen (Switches, Buttons, SREG und RXDREG) lassen sich alle Werte editieren (Hinweis: Nur die Werte selbst, nicht die Namen oder
sonstigen Informationen), jedoch ausschließlich bei pausierter Emulation. Bei Änderung eines
Wertes werden alle dazugehörigen Äquivalenzen aktualisiert und der neue Wert intern verarbeitet. Beispielsweise lassen sich über diese Anzeige LEDs setzen oder der VGA-Memory
flushen. Durch Modifikation der Werte bei pausierter Emulation eignet sich diese Anzeige
neben dem Lesen vor allem zur Manipulation der Emulation. Alle Tabellen sind bezüglich
der Benutzereingaben sicher und erlauben nur eine Eingabe im entsprechenden Format (z.B.
Binärzahl in die Tabellenspalte für binäre Werte). In der Status-Konsole wird jedoch immer
eine Mitteilung erscheinen, wenn ein Wert erfolgreich gesetzt wurde oder wenn ein Fehler aufgetreten ist. Während alle Tabelleneinträge automatisch beim Pausieren oder Initialisieren
einer Emulation aktualisiert werden, sind die VGA-Memory Werte aus Gründen der Performanz nur manuell über den Button am Anfang der Tabelle aktualisierbar.
2.4.7
Putty-Konsole
Die unter Abbildung 2.5 als 7. gekennzeichnete Putty-Konsole visualisiert die Kommunikation
zwischen dem Board und dem PC. Da diese Komponente für die diesigen Praktikumsaufgaben
irrelevant ist, wird diese hier nicht weiter betrachtet.
2.4.8
Status-Konsole
Die Status-Konsole, unter Abbildung 2.5 als 8. gekennzeichnet, ist das wichtigste Werkzeug
zum Umgang mit Problemen und Fehlermeldungen im Code sowie mit dem Emulator allgemein. Zu fast allen Aktionen werden in dieser Statusmitteilungen ausgegeben, welche entweder
eine erfolgreiche Aktion bestätigen oder den Fehler der Nichtausführung näher beschreiben.
Allgemeine Fehlermeldungen werden dabei durch drei rote Ausrufezeichen in der Navigationszeile der Konsole gekennzeichnet, während spezielle Codefehler mit einer roten Zahl in
der Navigationszeile versehen werden. Diese rote Zahl ist mit der Codezeile des Eingabefeldes gleichzusetzen, in welcher der Fehler aufgetreten ist. Wird eine neue Emulation über den
Button New“ initialisiert, wird der Inhalt der Konsole gelöscht.
”
Kapitel 3
Anmerkungen und Tipps
ˆ In einer Aufgabe werden Sie ein Assemblerprogramm schreiben, um das Sprite auf
dem Bildschirm ausgeben zu können. Um den Inhalt des Sprites zu definieren, sollten
Sie den Bitvektor für jede Zeile hexadezimal kodieren und die Zahl als Immediate in
ein Register laden. Der Registerinhalt kann dann an die entsprechende Speicherstelle
geschrieben werden. Für einen Vektor sieht geht dies wie folgt:
# Hexadezimalekodierung des e r s t e n Vektors
LDI R2 , 0xFFF0
# R2 e n t h ä l t A d r e s s e d e r e r s t e n S p r i t e z e i l e ,
# R3 d i e Bit−Kodierung d e r S p r i t e −Z e i l e
ST
R3 , R2
ˆ Bei der Aufgabenbearbeitung gehen Sie davon aus, dass das Sprite nur um ein Pixel pro
Richtungseingabe verschoben wird.
ˆ Bei einem vollständigen FLUSH des Bildspeichers dauert es ca 3,1 Millisekunden bis
der Bildspeicher wieder adressiert werden kann. Innerhalb dieser Zeitspanne ist die CPU
jedoch in der Lage 18.563 aufeinanderfolgende Instruktionen auszuführen. Somit sollte nach jeder FLUSH-ANWEISUNG (CLRV) ein entsprechender Warteschleifenzyklus
eingelegt werden, bevor die nächste Instruktion des eigentlichen Programms ausgeführt
werden soll. Ansonsten besteht die Gefahr der inkorrekten Ausführung von Bilsdpeicheroperationen innerhalb des Programms.
ˆ Programme, die selbst keine Endlosschleife enthalten, sollten am Programmende mit
einer solchen abgeschlossen werden, damit das Programm nur genau einmal ausgeführt
wird.
# Sprungmarke
MARKE:
# Sprung zum S c h l e i f e n a n f a n g
JMP MARKE
ˆ In der Assemblersprache unserer Praktikums-CPU muss vor jedem bedingtem Sprungbefehl der Compare Befehl ausgeführt werden.
23
KAPITEL 3. ANMERKUNGEN UND TIPPS
24
ˆ Kommentare werden bei der Praktikums-CPU mit dem Symbol # eingeleitet.
ˆ Das FPGA-Board bietet neben dem FPGA-Chip zusätzliche IO-Peripherie (Schalter,LEDS,
Buttons, etc.), auf welche die Praktikums-CPU wie folgt Zugriff hat: Im Datenspeicher
der CPU ist für jede IO-Komponente eine Adresse reserviert, in der deren aktueller
Wert/Zustand abgelegt ist. Die entsprechenden Adressen sind in der nachfolgenden
Memory-Map des Datenspeichers aufgelistet.
Memory-Map des Datenspeichers:
Adresse (HEX)
0x0000
0x0001
0x0002
0x0003
0x0007
0x0004 - 0x07FF
0x0800 - 0xFFFF
Belegung
7-Segment Anzeige
LEDS
SWITCHES
BUTTONS
VGA-ADDRESS-MODE[1 = indirekt,2 = flush]
USER DATA
nicht Verfügbar
Die genaue Bitbelegung der Adressen ist wie folgt:
7-Segment Anzeige [10:0]
AN3 AN2 AN1 AN0
LEDS [7:0]
LD7 LD6
LD5
Switches [7:0]
SW7 SW6 SW5
Buttons [3:0]
BT3 BT2 BT1
LD4
SW4
BT0
g
f
LD3
SW3
e
d
LD2
c
b
LD1
SW2
a
LD0
SW1
SW0
Kapitel 4
Vorbereitungsaufgaben
Mit den folgenden Aufgaben werden Sie die Assembler-Programme vorbereiten, die Sie im
Praktikum fertig implementieren und testen werden. Es ist nicht notwendig, allerdings hilfreich, den Assembler-Code schon komplett zu erstellen. Definieren Sie für alle zu schreibenden
Programme die interne Struktur (Pesudo-Code) und den Ablaufplan. Da einige der Programme im späteren Verlauf mit anderen kombiniert werden, sollten Sie darauf achten möglichst
sauber“ zu Arbeiten, was die Verwendung von Registern betrifft. Dokumentieren Sie deshalb
”
genau welche Register wo und wie verwendet werden, um spätere Inkosistenzen zur Laufzeit
zu vermeiden.
Aufgabe 1. Verinnerlichen Sie die grundlegenden Konzepte der Assemblerprogrammierung, so dass
Sie in der Lage sind, komplexere Programme mit Schleifen, Abfragen und arithmetischen
Ausdrücken zu implementieren. Hierzu sollten Ihnen die in Abschnitt 2.2.7 vorgestellten
Konstrukte genügen. Des Weiteren sollten Sie sich mit dem Befehlssatz der PraktikumsCPU vetraut machen. Lesen Sie hierzu die zur Verfügung gestellte Befehlstabelle (Befehlstabelle.pdf) und versuchen Sie dabei die Funktion und Anwendung der einzelnen
Befehle zu verstehen. Insbesondere die der Sprung-, Daten- und Transferbefehle.
Aufgabe 2. Stellen Sie den Pseudo-Assembler-Code für den in Abschnitt 2.1 vorgestellten PseudoZufallszahlengenerator auf. Verwenden Sie hierfür den vorgestellten Ansatz mittels eines
linear rückgekoppelten Schieberegisters. Verwenden Sie dabei die volle 16 Bit Registerlänge als Schieberegister um damit Zufallszahlen mit einer möglichst langen Periode
zu generieren. Für die Einkopplungsstellen sollten zwei XOR-Operationen an jeweils
unterschiedlichen Bitpositionen genügen. Des Weiteren soll dem Generator eine Parameterübergabe möglich sein, so dass die obere Grenze des Zahlenintervalls dynamisch
zur Laufzeit übergeben werden kann. Die Berechnung einer Pseudo-Zufallszahl dauert
somit solange an, bis eine gewünschte Zahl in dem entsprechenden Intervall berechnet
wurde. Die Rückgabe der Zufallszahl erfolgt ebenfalls in einem von Ihnen frei wählbaren
Register.
Aufgabe 3. Überlegen Sie sich den Ablaufplan eines Assemblerprogrammes, welches ein Quadrat
mit der Größe 10 x 10 Pixel in einer beliebigen Farbe an einer beliebigen Stelle des
Bildschirms zeichnet. Der Code sollte dabei so modular gehalten werden, dass dieser
problemlos später in einem anderen Programm wieder verwendet werden kann. Verwenden Sie daher ein Register, welches als Übergabeparameter für die Ursprungskoordinate
des Quadrats dient. Der Ursprung des Quadrats soll seine oberen linke Ecke sein.
25
KAPITEL 4. VORBEREITUNGSAUFGABEN
26
Aufgabe 4. Bereiten Sie ein Assemblerprogramm vor, das ein Sprite Ihrer Wahl an einer beliebigen Stelle des Bildschirms ausgibt. Überlegen Sie sich zunächst wie es aussehen soll.
Ein Sprite besteht aus 8 Vektoren, die jeweils 8 Bit breit sind. Ihnen steht somit ein
8x8 Feld zur Verfügung. Füllen Sie das Feld mit Einsen und Nullen, entsprechend dem
gewünschten Aussehen. Wenn im Vektor eine 0 vorkommt, dann ist diese Stelle transparent, sonst weiß. Um das Sprite letztendlich zeichnen zu können, müssen Sie es wie
in Abschnitt 3 gezeigt, zeilenweise als Konstante in Register laden und an die entsprechenden Spritespeicherstellen speichern.
Aufgabe 5. Überlegen Sie sich eine Fusion der beiden Assemblerprogramme aus Aufgabe 2. und 3.
Das resultierende Programm sollte in der Lage sein, ein Quadrat mit 10 x 10 Pixeln an
einer durch den Pseudo-Zufallsgenerator bestimmten X,Y-Koordinate des Bildschirms
zu zeichnen. Berücksichtigen Sie als zulässigen Wertebereich der Ursprunkskoordinate
des Quadrats die obere linke Ecke mit X = [0, 150] und Y = [0, 110].
Aufgabe 6. Wie könnte ein Assemblerprogramm aussehen, mit dem man das Sprite mit Hilfe der
Tastereingaben der Nexys-Platine am Bildschirm in vier Richtungen verschieben kann,
so dass es auf eine zufällig positionierte Hitbox (Quadrat 10 x 10 Pixeln) hinzu bewegt werden kann. Ist das Sprite vollständig innerhalb der Hitbox-Hülle positioniert,
so wird die Box gelöscht, an einer zufälligen Stelle des Bildschirms neu gezeichnet und
das Spiel“ beginnt von vorne. Der jeweils entsprechende Taster soll dabei das Sprite
”
nach links, rechts, oben oder unten bewegen. Sie brauchen nicht zu berücksichtigen,
dass mehr als einer der Taster gleichzeitig gedrückt wird. Als Kollisionserkennung, bzw.
Hitbox-Überdeckung durch den Sprite sollte der folgende Koordinatenabgleich nach jeder eigehenden Bewegung ausreichen:
(X, Y )Sprite == (X + 1, Y + 1)Hitbox
Für die spätere Programmumsetzung soll der folgende Automat mit insgesamt drei
Zuständen dienen.
Abbildung 4.1: Hit The Box“ Automaten Programmablauf
”
KAPITEL 4. VORBEREITUNGSAUFGABEN
27
Startzustand 00: Initialisierung
Da nach dem Einschalten der CPU zunächst einmal alle internen Werte und Zustände
mit Null initialisert sind, müssen zunächst einmal die verschiedenen Konfigurationen,
wie Adressmodi, Spritedeklarationen und andere Default-Initialiserungen durchgeführt
werden. Hierzu gehört das Laden und Speichern des Sprites sowie die Definition der
Positionskoordinaten beider Objekte. Damit die Hitbox und das Sprite erstmalig von
der VGA-Hardware korrekt gezeichnet werden können, müssen die Koordinaten beider
Objekte mit der obigen Bedingung gemäß einer vollständigen Überdeckung initialisiert
werden, d.h. (X, Y )Sprite = (X + 1, Y + 1)Hitbox . Anschließend wird ohne jedwede Eingabe in den Folgezustand 01 gewechselt. Nach dem Verlassen ist ein erneuter Wechsel
in den Startzustand nicht mehr möglich.
Zustand 01: Kollisionserkennung
In diesem Zustand erfolgt die Kollisions-/Überdeckungserkennung. Wird eine Kollision
mittels Koordinatenabgleich erkannt, so wird der aktuelle Bildschirm geflusht (gelöscht),
die Koordinate der Hitbox mittels Pseudo-Zufallszahlengenerator neu berechnet und
anschließend an einer zufälligen Position neu gezeichnet. Da es sich bei der Hitbox
nicht um ein Sprite-Objekt handelt, ist ein vollständiges neuzeichnen der Hitbox in
diesem Zustand notwendig. Nach dem Beenden dieser Routine oder im Falle einer
Nichtüberdeckung wird in den Folgezustand 10 gewechselt.
Zustand 10: Eingabekontrolle
Im Zustand 10 erfolgt die Überwachung der Eingabeperipherie. Hierzu werden die Taster mittels Polling“ permanent auf Knopfdruck überprüft. Sofern das Drücken eines
”
Tasters erkannt wurde, wird die Spritekoordinate in der entsprechenden Richtung manipuliert. Da es sich um ein Sprite handelt, welches mit dem aktuellen Inhalt des Bildspeichers lediglich überblendet wird, entfällt an dieser Stelle das Neuzeichnen. Anschließend
wird in den Zustand 01 gewechselt. Wird bei keinem der Taster ein Knopfdruck erkannt,
verbleibt der Automat in diesem Zustand (10).
Kapitel 5
Praktikumsaufgaben
Für die folgenden Aufgaben entpacken Sie die ZIP-Datei für Versuch 5 von der Praktikumswebsite und erstellen ein neues Projekt mit allen darin enthaltenen VHDL-Dateien sowie mit
den Dateien irom.bmm“ und irom.mem“. Verwenden Sie als Editor, Compiler und Debugger
”
”
das zur Verfügung gestellte TINY RISC STUDIO v1.0“.
”
Aufgabe 1. Implementieren Sie den Pseudo-Zufallszahlengenerator aus Vorbereitungsaufgabe 2. Verwenden Sie für Rück- und Übergabeparameter entsprechende Register der CPU. Anschließend simulieren Sie Ihr Programm zunächst mit dem Debugger um logische Fehler
in der Programmstruktur zu erkennen.
Aufgabe 2. Implementieren Sie Ihr Assemblerprogramm zum Zeichnen eines Quadrats mit 10 x 10
Pixeln aus Vorbereitungsaufgabe 3. Simulieren Sie es zunächst mit Hilfe des Debuggers
und testen Sie es abschließend auf der FPGA-Platine.
Aufgabe 3. Stellen Sie Ihr Assemblerprogramm zum Zeichnen eines Sprites aus Vorbereitungsaufgabe 4 fertig und testen Sie es anschließend auf der FPGA-Platine.
Aufgabe 4.
Hit The Box“: Implementieren Sie den in Vorbereitungsaufgabe 6 vorgestellten Auto”
maten vollständig in Assembler. Nutzen Sie dazu Ihre bereits vorhandenen Programme
aus den Aufgaben 1, 2 und 3 und binden Sie diese in den jeweiligen entsprechenden
Zuständen ein. Um Ihnen den Einstieg zu erleichtern, haben wir eine Datei mit dem
Namen HitTheBox Aufgabe 4.txt“ vorbereitet, in der Sie den Code des Automaten
”
vervollständigen müssen. Diese beinhaltet bereits die Struktur des Automaten (Sprungmarken der Zustände und deren Reihenfolge im Programm) sowie den Code für den
Zustand 10 (Eingabeüberprüfung der Taster). Die zu editierenden Stellen sind im Code
mit TODO“ und einem zugehörigen Hinweis gekennzeichnet. Nachdem Sie den Auto”
maten vollständig implementiert haben, simulieren Sie ihn zunächst mit dem Debugger.
Abschließend testen Sie ihn auf der FPGA-Platine.
28
Herunterladen