Ich bitte euch um Hilfe

Ernst

Mitglied
4 Okt 2009
50
5
8
nähe Regensburg
Sprachen
Grüßt euch

Seit dem Tod von meiner Frau kurz vor den letzen Weihnachten habe ich endlich wieder Mut gefunden etwas zu basteln wobei ich aber Hilfe bräuchte. Es geht darum eine Zahl zwischen 0 bis 450 mit dem Atmega 8 durch 16 zu teilen. Ich habe zwar im Net schon etliches gefunden aber keine Anleitung in Assembler die auch Nachkommastellen ausgibt. Vielleicht kann mir jemand helfen.

Gruß Ernst
 

LotadaC

Sehr aktives Mitglied
22 Jan 2009
3.090
57
48
Hennigsdorf
Sprachen
BascomAVR, Assembler
Durch sechzehn zu teilen ist für 'nen AVR 'nen Klacks. Einfach viermal nach rechts schieben. Sollen dabei auftretende Nachkommastellen berücksichtigt werden, müssen diese in ein entsprechendes Nachkommabyte gerollt werden. Deine 450 erforden bereits zwei Bytes, sonst könnte man mit "SwapMovDoppelandi" tricksen.
Aber entweder verstehst Du binäre Nachkommastellen nicht (ganz), oder Du willst das noch irgendwie … wandeln? … ausgeben?

Wenn nötig, erklär ich später mehr...
 

LotadaC

Sehr aktives Mitglied
22 Jan 2009
3.090
57
48
Hennigsdorf
Sprachen
BascomAVR, Assembler
Also erstmal grundsätzliches:
Die AVR arbeiten binär, wenn das Komma nach rechts geschoben wird (bzw die Zahl nach Links) wird verdoppelt, wenn es nach links geschoben wird (bzw die Zahl nach rechts) wird halbiert. Basis Zwei.
Im Dezimalsystem (Basis Zehn) verzehnfachst/zehntelst Du genauso.
Wenn Du (analog zu Deinem binären Problem) dezimal die ... Sagen wir 1287651 durch 10000 teilen solltest, würdest Du einfach das Komma vier Stellen nach links schieben (bzw die Zahl selbst viermal eine Stelle nach Rechts).
Ergäbe 128,7651.
Was bedeutet das mit den Nachkommastellen nun? Die "7" sind sieben zehntel (also 7*10-1), die "6" sechs hundertstel (also 6*10-2) usw.
Binär ist es genau dasselbe, nur eben auf Basis Zwei.
Um durch sechzehn zu teilen, schiebst Du die Zahl viermal nach rechts. Als Beispiel einfach mal die 450dez.
Wäre binär 111000010
Komma um vier Stellen verschoben: 11100,0010
11100binär entspricht 28dezimal.
und 0,001binär ist "null halbe" plus "null viertel" plus "ein achtel"
also 11100,0010binär ist 28 und einachtel ... 28,128 dezimal.

Um ein Byte durch sechzehn zu teilen, müßte also viermal geschoben werden. Das dabei jeweils ins Carry fallende letzte Bit müßte je in ein Nachkommabyte gerollt werden (ignorieren wir mal das neunte Bit):
11000010 -LSR-> 01100001 Carry=0, ROR->0
01100001 -LSR-> 00110000 Carry=1, ROR->10
00110000 -LSR-> 00011000 Carry=0, ROR->010
00011000 -LSR-> 00001100 Carry=0, ROR->0010
Erfordert acht Instruktionen. Viermal je ein Shift und ein Rotate. Das neunte Bit bräuchte ein weiters Byte, also je ein weiteres Rotate - zusammen 12 Instruktionen.
Ok, soweit die stupide Rechnung - jetzt zu den Tricks.
Nach den vier Shifts/Rotates befinden sich die vier vorher linken Bits rechts im selben Byte, und die vier vorher rechten links in einem anderen Byte.
Der AVR kann die Nibbles eines Bytes mit einer Instruktion tauschen - SWAP
Aus 11000010 wird 00101100.
Das wird in das Nachkommabyte kopiert (MOV)
Also 00101100 00101100.
Jetzt werden im Vorkommabyte die ersten vier, und im Nachkommabyte die letzten vier Bits wegmaskiert. Je mit ANDI.
Ergibt 0000110 00100000.
Fertig nach vier (statt acht) Instruktionen.

Ok, und mit neun Bits (bis max 12 Bits)?


CodeBox Assembler
;verwendet werden drei Register HByte, Lbyte und Nachkommabyte
SWAP Lbyte ;Nibbles tauschen
MOV Nachkommabyte, Lbyte ;Kopieren
ANDI Nachkommabyte, 0xF0 ;unteres Nibble im Nachkommabyte löschen
ANDI Lbyte, 0x0F ;dito oberes Nibble im Lbyte
SWAP Hbyte ;unteres Nibble nach oben tauschen
ADD Lbyte, Hbyte ;und auf Lbyte addieren

Fertig nach sechs (statt zwölf) Instruktionen.

Was meintest Du jetzt mit "ausgeben"?
 

Ernst

Mitglied
4 Okt 2009
50
5
8
nähe Regensburg
Sprachen
Grüßt euch
Einen herzlichen Dank für die Hilfe es klappt so wie ich wollte.
Mit Ausgeben meinte ich das die Zahlen auf einen 2 X 16 Display angezeigt werden.
Gruß Ernst
 

LotadaC

Sehr aktives Mitglied
22 Jan 2009
3.090
57
48
Hennigsdorf
Sprachen
BascomAVR, Assembler
das die Zahlen auf einen 2 X 16 Display angezeigt werden.
Nach der Code-Box aus #3 hast Du ein Byte mit dem Vorkomma-Anteil und eins mit dem Nachkomma-Anteil. Binär.
Du kannst die natürlich auch als Byte darstellen - vermutlich willst Du sie aber dezimal darstellen. Also mußt Du sie einerseits stellenweise in eine Dezimalzahl überführen, und dann in die entsprechende ASCII, die Du an das alphanumerische (?) Display senden kannst. zwischen den beiden Bytes schummelst Du das Komma.

Zum Vorkomma-Byte:
Nach der Division durch sechzehn kannst Du höchstens 28,125 haben, also 28 im Vorkomma-Byte.
Theoretisch müßtest Du durch zehn teilen, um die Zehner zu erhalten (28 / 10 = 2). Ist die Divisionsroutine effizient, liefert sie Dir nebenbei den Rest, mit dem Du weitere Dezimalstellen gewinnen kannst. Da es hier nur noch die Einer wären (8), wärst Du fertig. Ist die Divisionsroutine weniger effizient, könntest Du den Rest mittels Modulo-Division berechnen (28 Mod 10 = 8).

In der Praxis würde man in einer Schleife sooft zehn subtrahieren, bis der Rest kleiner Zehn wird. "Sooft" wären dann die Zehner, Der Rest die Einer.
Damit hättest Du den Vorkommaanteil in seine Dezimalstellen zerhackt - Binary Coded Digits.
Und wie machst Du da ASCIIs draus?
Indem Du einfach 'ne ASCII-Null (0x30) draufaddierst. Oder noch besser: "Sooft" vor dem Zählen mit 0x30 initialisierst, und dann eben jedesmal, wenn Du vom Dividenten zehn abziehen kannst "sooft" inkrementierst.

Du hast also "Zehner" und "Einer" gesendet, anschließend noch das Komma (0x2C).

Nun der Nachkommaanteil:
Theoretisch könntest Du hier ebenso versuchen "zehntel", "hundertstel" usw abzuziehen - allerdings wirst Du feststellen, daß diese sich mit einem Byte nicht darstellen lassen.

Aber Du kannst das Nachkommabyte einfach mit Zehn multiplizieren. Zehn entspricht &B00001010 - also nur zwei Einsen drin. Du mußt also das mit zehn zu multiplizierende Byte einmal nach links schieben (16bit), in zwei Register kopieren, dann noch zweimal linksschieben, und draufaddieren. Das höherwertige Register enthält danach das Zehnfache, also die "Zehntel" als Dezimalstelle, das andere den Rest. Wenn Du mit dem das ganze wiederholst, erhälst Du die hundertstel usw. Addierst Du auf das höhere Register 0x30, steht da wieder der entsprechende ASCII, den Du an Dein Display senden kannst.

Bei 'nem Mega-Controller kannst Du natürlich auch den Multiplikationsbefehl nutzen.

Dank für die Hilfe es klappt so wie ich wollte.
Wie sieht Deine Lösung aus?

P.S.: Bei Deinem konkreten Fall (max 450, wobei das Low-Nibble den Nachkomma-Anteil stellt (also 1/16 entspricht)) kann man das Bit-Geschubse natürlich noch deutlich … optimieren, denk ich.
Bin noch dran...
 
Zuletzt bearbeitet:

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)