TTL-Gatter, Atmega 8, MIPS Assembler

Benutzeravatar
davidvajda.de
Site Admin
Beiträge: 1512
Registriert: Di Jul 18, 2023 8:36 pm
Wohnort: D-72072, Tübingen
Kontaktdaten:

Re: TTL-Gatter, Atmega 8, MIPS Assembler

Beitrag von davidvajda.de »

Das erste, was wir wissen müssen, ist, dass sich
  1. Atmel
  2. MIPS
auf dem ersten Blick, im Gegensatz zu
  1. Atmel
  2. MIPS
  3. Intel


sehr ähnlich sehen, das ist nur bedingt so.

Wir müssen zwei Generelle Parameter unterscheiden
  1. Die Addressierungsart
  2. Das Addressformat
Die Addressierungsart wirkt auf uns wie etwas naturgegbenes, das ist bedingt so, allerdings ist auch die Addressierungsart abhängig von der Instruction Set Architecture der CPU. Ich zähle hiermit beides auf

MIPS
  1. Die Addressierungsart
    1. Registeraddressierung
    2. Direktwertaddressierung
    3. Unmittelbare Addressierung
    4. Indirekte Addressierung
    5. Indirekte Addressierung mit Autoinkrement/Dekrement
    6. Indirekte Addressierung mit Verschiebung
    7. Indizierte Addressierung
    8. Indizerte Addressierung mit Verschiebung
  2. Das Addressformat
    1. Dreiaddressformat
    2. Zweiaddressformat
    3. Einaddressformat
    4. Nulladdressformat
Ein wenig einfacher ist der Standard von Intel beim 8086er:
  1. Die Addressierungsart
    1. Registeraddressierung
    2. Direktwertaddressierung (Immidiate)
    3. Direkte Addressierung
    4. Indirekte Addressierung
    5. Indizierte Addressierung
  2. Das Addressformat
    1. Zweiaddressformat
    2. Einaddressformat
    3. Nulladdressformat
Tatsächlich ist das Addressformat kein Syntaktischer Zucker. Schauen wir uns übliche Befehle an.

Üblicherweise heissen, die Allzweckregister bei einem Prozessor

Code: Alles auswählen

r0 bis r31
Der Mips führt daneben die Bezeichnungen

Code: Alles auswählen

$t0-$t9, $s0-$s7, $a0-$1, $v0-$vx, $gp, ...
ein

Intel hat den Standard eingeführt

Code: Alles auswählen

ax
bx
dx
cx
sp
bp
si
di
es
cs
ds
ss
Tatsächlich lautet der Befehl auf einer gewöhnlichen Assemblermaschine oft, wie folgt, betrachtet man MIPS

Code: Alles auswählen

add $t0, $t0, $t1
oder

Code: Alles auswählen

add $t2, $t1, $t0
Auf einer anderen Maschine, wie dem Atmega8. Das muss jetzt ein wenig üben

Code: Alles auswählen

add r0, r1
Das ist sehr wohl kein Syntaktischer Zucker, darauf müssen wir achten

Code: Alles auswählen

add r0, r1
bedeutet aus einer gewissen Sicht, etwas ganz anderes, als

Code: Alles auswählen

add r0, r0, r1
bedeutet

Nicht ganz - aus der Sicht des Programmierers - also aus sicht der Instruction Set Architecture ist es verzeihbar. Allerdings ist ein Prozessor realisiert und implementiert in der Mikroarchitektur

Die Mikroarchitektur legt die Schaltung in der Realität fest

Ein 3-Address-Befehl, ein R-Typ befehl beim MIPS sieht aus wie

Code: Alles auswählen

add $t0, $t1, $t2
Hier haben wir zwei Quelloperanden und einen Zieloperanden. Das macht sich allerdings nicht nur im Assembler Code bemerkbar, das macht sich auch im Binär Codierten Befehl bemerkbar.

Ein R-Typ Befehl beim MIPS verfügt über die Felder

Code: Alles auswählen

rt, rs, rd
s und t, das sind wohl beides einfach s weil t der näcshte Buchstabe nach s ist. Und d steht für Destination während s für Source Steht

Doch diese Felder sind Felder im Binärcodierten Befehl. Und nein ein Binärcodierte Befehl wir nicht ähnlich einer Suchfunktion

Code: Alles auswählen

sed, grep
in Linux oder dem Datamining analysiert. Tatsächlich gibt es im MIPS im Steuerwerk aber auch an den Registern, Datenbusse und so seltsam es erscheint - also 5 Bit Datenbusse, für einen insgesamt 32 Bit Datenbus

Der Befehl passt in das Format der Schaltung

Das ist der Unterschied

Nun möchte ich zum nächsten Thema übergehen. Die Architektur

Die überhaupt nicht irrelevant ist. Wir unterscheiden
  1. Die Lade- und Speicherarchitekur
  2. Die Register Speicherarchitektur
  3. Die Speicher Speicher Architektur
  4. Die Stack und Kellerarchitektur
  5. Die Akkumulatorarchitektur.
Kehren wir zu den Addressierungsarten zurück

Tatsächlich benutzt Intel so etwas wie Syntaktischen Zucker, wobei das auch etwas genauer ein wenig anders sein könnte, nämlich den
Befehl

Dieser Befehl tut mehrere dinge gleichzeitig, was nicht immer üblich ist

Sehen wir das mal so
  1. Wir haben Register
  2. Wir haben Speicher
Nun, wie können denn die Addressen von ihrer Art im Computer aussehen?

Wir können einen Befehl

von Register zur Register anwenden
von Speicher zu Register
von Register zu Speicher
von Speicher zu Speicher

Ersteres macht keinen Sinn. Einen Computer zu entwickeln, der keinen Arbeitsspeicher hat, ist ein anderes Modell. aber genau das bedienen wir normalerweise. Nämlich eine Lade und Speicherarchitektur

Der ADD-Befehl wird sinngemäss eigentlich nicht direkt auf den Arbeitsspeicher angewendet, sondern auf die Register

Code: Alles auswählen

add $r0, $r1, $r2
Das sind alles Register. Doch woher kommen die Daten aus dem Arbeitsspeicher

Das erledigen die Befehle

Code: Alles auswählen

LOAD 
STORE 
Das sind neben MOVE, die wichtigsten Datenbewegungsbefehle. In dem wir eine Lade und Speicherarchitektur haben, haben wir beides. Eine Register Register architektur, die trotzdem den RAM verwendet, eben indem die Daten mit LOAD and STORE von Registern und Arbeitsspeicher zusammenarbeiten

eine Register-Speicherarchitektur erwartet, dass der eine Opreand im Speicher ist, der andere in einem Register

Eine Speicher Speicherarchitektur, die höchst selten ist, benutzt den Arbeitsspeicher

Und eine Akkumulaturarchitektur tut das, was passieren würde, würden wir typischerweise eine Schaltung mit TTL 74xx aufbauen. Wir würden einen Addierer nehmen, oder gar eine ALU - daneben würden wir zwei Register nehmen

In unserem Operations und Steuerwerk Schaltplan, heissen diese A und B oder R0 und R1.

Dabei ist A oder R0, der Akkumulator. Wir würden eine reihe Berechnen. In R1 schreiben wir den Inkrement wert

Zum Beispiel 2

Code: Alles auswählen

2, 4, 6, 8, 10, 12, ... 
würde nun durch wiederholte Anwendung gegeben durch das Steuwerk in den Akkumulator A bzw. R0 geschrieben, der einen Initialwert haben kann, so dass die Folge (nicht reihe) Das können wir so oder so sehen - denn wir könnten diese folge auch als Reihe ausdrücken

Code: Alles auswählen

5, 7, 9, 11
und so weiter. Gut. Das ist wäre klassischer Weise eine Akkumulatorarchitektur

Bleibt die Stack und Kellerarchitektur

Nun wir haben die Befehle

Code: Alles auswählen

PUSH und POP
Mit PUSH a legen wir einen Wert a auf den Stack, mit POP a nehmen wir einen Wert vom Stack und speichern ihn in a. Dabei müssen wir bedenken, dass bei PUSH der Stackpointer üblicherweise +2 erhöht wird - oder wenn wir wie bei Intel denken, wo der Stack am Ende los geht -2 und bei POP werden wir den Wert entsprechen erniedrigen, erhöhen

Doch wie kann man so addieren. Das ist ganz einfach

Code: Alles auswählen

PUSH r0
PUSH r1
ADD 

So funktioniert die Stackarchitektur. Doch wo ist unser POP? Das ist ganz einfach. Natürlich wird der Hersteller einen Befehl POP anbieten, um den Stack künstlich zu leeren. Doch es geht so. Man legt den ersten Quelloperanden auf dem Stack ab, dann den Zweiten und ADD addiert die zwei Zahlen indem sie sie vom Stack nimmt und legt das Ergebnis wieder auf dem Stack

Das gibt es bei 8087 Intel, bei ATAM 862, Atmel, einer 4 Bit Architektur und der Java Virtual Machine

Gut - damit wäre fast alles geklärt. Nämlich folgendes, was ist

Code: Alles auswählen

LOAD and STORE
Nun halten wir fest, bei Intel gibt es einen Befehl
Den könnte man auch als MOVE bezeichnen

LOAD kann man als ld bezeichnen
und STORE als st

Gut - MOVE ist bei den meisten Architekturen dafür vorgesehen

Code: Alles auswählen

move $r0, $r1
Je nachdem ob der quelloperand oder der Zieloperand an erster Stelle steht, wird der Wert aus dem einen Register in das andere geschrieben. Das ist eigentlich ein MOVE Befehl, nämlich ein Befehl, der Daten zwischen den Verschiedenen Allzweckregistern in der CPU transferiert. Normalerweise sind es 32, $r0 bis $r1

Gut - daneben müssen wir bedenken gibt es den Arbeitsspeicher, den RAM

Dieser nimmt eine Sonderrolle ein. Will ich etwas im Arbeitsspeicher speichern, dann handelt es sich dabei wie der Name sagt um einen

Code: Alles auswählen

STORE, store, st
Befehl. Der STORE Befehl speichert den Wert eines Registers an einer Stelle im Arbeitsspeicher. Der andere ist der LOAD Befehl

Ich lade einen Wert aus dem Arbeitsspeicher in einem Register

Also von Register zu Arbeitsspeicher: Store
Und von Arbeitsspeicher zu Register: Load

Damit wären wir fast fertig. würden nicht die Addressierungarten bestehen. Intel verwendet für alles den MOV Befehl.

Demnach können wir schreiben

Code: Alles auswählen

MOV AX, BX                      ; Register-Register
MOV AX, 0xffff                   ; Registser Direktwert
MOV SI, OFFSET LABEL  ; Feste Addresse im Arbeitsspeicher
MOV [BX], AX                    ; Addrese steht in BX
MOV DL, [BX+SI]               ; Indirekte
Die Reihenfolge:
  1. Registeraddressierung
  2. Immidiatewert - Direktwert
  3. Direkte Addressierung
  4. Indirekte Addressierung
  5. Indiziert
Indiziert kommt dadurch zustande, dass wir einen Verschiebungswert zu der Address addieren, den wir wie bei der indirkten Bilden

Nun hier übernimmt, bei Intel, der MOV Befehl alles.

Anders bei MIPS

Code: Alles auswählen

move
lb, lbu
lh, lhu
lw
ld
la
li
sb
sh
sw
sd
Das erste, worauf man hier wert legt ist die Breite. Addressieren wir eine Addresse im Arbeitsspeicher

Code: Alles auswählen

0x1234
Dann ist das eine Addresse, wir haben allerdings einen 32 Bit Datenbus - Und wir können ihn Byteweise addressieren

Das heisst, wir können ein Byte, ein Halbwort ein Wort oder Doppelwort bilden. Je nachdem wie viel in unserer Register $t0 bzw. allgemein $r0 passt. Nämlich oft 32 Bit oder 64, aber aber auch 16 oder 8 ist nicht unüblich

gut, anders beim Atmega8. hier haben wir

Code: Alles auswählen

ld, ldd, lds
Die Addressierungsart wird hier im Befehl angegeben. Ich bin noch etwas unsicher, das muss ich gleich üben

Ich kenne die Befehle, aber so weit ich weiss, ist ldd indirekt, ld, direkt und lds - daneben gibt es vielleicht noch ldi für Immidiate Werte. Das können wir gleich üben und auch wieder auswendig lernen. Natürlich gibt es

Code: Alles auswählen

ld, ldd, lds, ldi
sd, sdd, sds, sdi
move
Gut, jetzt noch eine letzte Frage - die Ports am Atmega8. Der typische Atmel Prozessor hat die Ports

Code: Alles auswählen

PORTA, PORTB, PORTC, PORTD, PORTE
Was ist das nun. Nun ein 8086er hat auch Ports. allerdings ist das eine eigene CPU. Und ein Atmega8 ist ein Mikrocontroller

Sowohl der RAM, als auch die EA benutzen beim 8086er, denselben Addressbus, aber auch denselben Datenbus, vor allem

Das heisst, wird ausserhalb des 8086er in den RAM geschrieben, oder davon gelesen, wird der äussere (!) nicht einfach innere (!) Datenbus verwendet. Der befindet sich an den Pins. Damit schreibt er in den RAM oder liesst davon. Aber er benutzt das auch für die Hardware - wenn zum Beispiel Parallel in die Floppy Disk geschrieben wird. Gelöst wird das durch bausteine, auf dem IBM Mainboard. Und gewissen Pins an dem Prozessor. Es gibt einen INTR und ein INTA und ein INT - Pin - daneben gibt es den PC 8259 A, den Programmable Interrupt Controller.

Durch diesen und weitere Bausteine auf dem IBM Mainboard - zum Beispiel IBM 5126 beim 8088er und den Pins am 8088er bzw. 8086er wird festgelegt, wird der RAM beschrieben oder die Hardware.

Nur geht das alles auf dem externen Datenbus nach draussen

Das ist beim Atmega8 etwas anders. Natürlich lässt sich an PORTB ein RAM anschliessen. Trotzdem - ist ein RAM auf dem Chip des Mikrocontrollers. Die Ports

Code: Alles auswählen

PORTA, PORTB, ...
sind tatsächlich auch PINS am IC. Allerdings führen sie normalerweise zu einem E/A-Modul

Wir dürfen das nicht verwechseln. Die Ports sind die EA. Vom Computer aus gesehen, während das LCD, was wir anschliessen, die LED's oder die Tastatur, das sind Geräte. Bestimmte Geräte. Intel löst das Problem dadurch dass an bestimmte Addressen geschrieben wird, die bei Intel Ports heissen, die eigentlich so gesehen für den RAM vorgeshen wären

Die Geräte sind die LED's. Und letzten Endes schicke ich über PORTB eine Zahl in Form einer Binärcodierten Zahl drauf. Natürlich steuere ich LCD anders an. Aber letzten Endes schicke ich Werte drauf

Gut, das ist das eine. Und was sollen nun die Angaben bei den Befehlen, im AVR Atmega8 Assembler Code

Code: Alles auswählen

         ldi r16, 0xFF       
         out DDRD, r16     

         ldi r16, 0x00 
         out PORTD, r16      


Das ist ganz einfach. Um den Port an zu sprechen, benutzen wir den Operanden in der Schreibweise

Code: Alles auswählen

PORTD, PORTB, PORTC usw
Gut, der Witz ist, dass die LED's umgekehrt leuchten. Wenn wir

0xff drauf schicken, gehen alle aus. Wenn wir 0x00 drauf schicken, gehen sie alle an. Was wir wissen müssen, ist, dass es neben den übrigen Befehlen

Code: Alles auswählen

ld, lds, ldd
sd, sds, sdd
ldi
move
Auch noch die Befehle

Code: Alles auswählen

in 
und 
out 
gibt. Das gibt es bei Intel auch. Die heissen, IN und OUT und heissen im Allgemeinen so. Das heisst, wir steuern die Hardware ausserhalb an

Wir schreiben in einen Port mit:

Code: Alles auswählen

         out PORTD, r16      
Jetzt ist die Frage, was ist das

Code: Alles auswählen

         out DDRD, r16     
Nun neben DDRD gibt es DDRB, DDRA, DDRC und so weiter

Es gibt also diese Zwei Dinge

Code: Alles auswählen

PORTD und DDRD
PORTD klingt logisch. Brauche ich aber auch die kryptisch anmutende Bezeichung DDRD, ja, die ist ganz wichtig. Man würde wissen, sie ist genauso wichtig und steht in nichts nach. Denn ähnlich wie es bei VHDL

Code: Alles auswählen

in 
out
inout 
gibt, für die Pins ausserhalb einer Komponente. Kann ein Port bei dem Atmega, Gleichzeitig nur zum Lesen oder Schreiben sein. Wohlgemerkt, es gibt serielle Hardware, wo beide Kommunizieren. Jedes Bit kann einzeln festgelegt werden. Für jeden Port. Deswegen

Code: Alles auswählen

         ldi r16, 0xFF       
         out DDRD, r16     
Hier lege ich jeweils für jedes Bit jeweils für jeden Port Fest, ob es zum Lesen oder Schreiben dient. Weil ein PORT auch 8 Bit ist, schreibe ich ein Byte rein

Code: Alles auswählen

0b00011100
legt etwas für die Bits fest, 11 steht für schreiben am jeweiligen Bits, des Ports

Gut, damit können wir anfangen
Antworten