Forum: Mikrocontroller und Digitale Elektronik 32 bit arithmetik mit 8051


von Mario S. (wandiii)


Lesenswert?

hallo

ich habe ein problem, wie kann ich mit den 8051 in 32 bit rechnen.

das problem besteht darin, dass ich eine 32 bit dualzahl, an den 4
ports anliegen hab, diese in eine dezimalzahl umrechnen will und dann
seriell ausgeben will. das zweite stellt nicht so das problem dar,
jedoch das erste.

ich hatte mir das ganze bisher so gedacht:

zuerst kontrollier ich das höchste bit auf 0 oder 1, um zu wissen ob
das ne pos. oder negative dualzahl ist. dann würde ich port 0 auslesen
und bit bit für kontrollieren ob da ne 0 oder 1 drinsteht und jeweils
z.b. 2^0, 2^1 etc. dann immer dazuaddieren. das ganze geht ja auch bei
port0 (8bit) ganz gut, aber wie gehts nun weiter :/

es bringt mir ersten ja nix mit port 1 das gleiche zu machen, da ich ja
irgendwann über meine 8 bit bzw. 16 bit drüber hinaus bin, spätestens
bei port 3. andererseits muss ich das ergebnis ja auch irgenwie
abspeichern können und dann so drauf zugreifen, dass ich das die dann
ziffernweise (also für jede zahl 4 bit) am seriellen port ausgeben
kann.

von Peter D. (peda)


Lesenswert?


von Mario S. (wandii)


Lesenswert?

so ich meld mich nochmal, ich komm leider mit dem angegebenen code für
die 32 bit arithmetik überhaupt nicht klar.

wie man 32 bit addiert ist mit mittlerweile klar geworden (immer 1 byte
mit dem gleichwertigen anderen, bei übertrag beim nächsten byte halt
noch mit dazu), aber ehrlich gesagt hab ich net mal die ahnung ob ich
arithmetik überhaupt brauch.

wie gesagt ich soll eine 32 bit dualzahl (die im 2er komplement
vorliegt) in eine dezimalzahl (+vorzeichen) unwandeln und am ser.port
ausgeben. das prob was sich mir nun stellt ist, ich muss ja an die
einzelnen dezimalziffern drankommen, da nutzt mir ja rechnen eigentlich
nix. gute wäre es zum beispiel, wenn ich die dezimalzahl zuerst abcheck
ob bit 32 0 oder 1 ist, wenns 0 ist alles ok, wenns 1 ist bild ich das
komplement und addier noch ne 1 dazu. das bekomm ich selbst noch hin.

nun wäre es aber geil wenn ich z.b. die dezimalzahl direkt in bcd
wandeln könnte, weil da wären ja genau 4 bit eine dezimalziffer und ich
bräuchte immer nur 4 bits nehmen, das in dezimal umwandeln und dann
entweder nochmal pro bcd-tetrade in einer speicherstelle
zwischenspeichern und wenn alles fertig ist halt über die schnittstelle
rausschicken.

nur wie kann ich ne dualzahl in bcd umwandeln? jedenfalls so ne 32 bit
 dualzahl? mit 8 bit wäre das ganze ja leicht und 16 bit, könnte ich
bestimmt auch noch irgendwie im acc und b register unterbringen aber 32
bit geht halt nicht.

ich hab auch schon probiert mit rotation, aber das bringt mich auch
nicht wirklich weiter.

hoffe einer kann mir bissi auf die sprünge helfen, wichtige wäre mir,
dass mir einer die mathematik oder das grundlegende zeigt, wie das
geht, wenn ichs verstanden hab kann ich auch den assembler code selbst
entwerfen, aber ich hab halt net mal ne ahnung wie ich das anstellen
soll.

PS: PW vergessen und nicht an meinem rechner und pw zuschicken lassen
ging net, also hab ich mich flux mal neu angemeldet :)

von Peter D. (peda)


Lesenswert?

Du hast Dir meinen Link nicht angesehen oder ist mein Englisch zu
schlecht ?

Schau Dir mal die Funktion ADIVR10 an.
Die teilt durch 10 und liefert den Rest im ACC.
Du mußt nur noch '0' (ASCII) addieren und dann ab in die Serielle.
Zusätzlich zeigt F0, ob das Ergebnis Null ist und man die Wandlung
abbrechen kann oder weiter teilen muß.


Welchen 8051 benutzt Du ?
Standardmäßig hat der doch nur 4 Ports.
Es ist ungewöhnlich, 32Bit parallel einzulesen. Meistens kann man die 4
Byte hintereinander einlesen und spart damit massig Potpins.


Peter

von Mario S. (wandii)


Lesenswert?

welcher 8051 kann ich net sagen, das ganze wird reine theorie :)

wird wohl nur mit nem terminalprogramm getestet, wir warn auch noch gar
net an nem echten µcontroller dran, bzw. bisher nur bissi fpga und so
zeug programmiert.

also deinem tipp konnte ich entnehmen, dass die dualzahl durch 10
geteilt wird und der rest im acc steht, das ist auch logisch. das mit
'0' ist mir scho mal untergekommen :)

nur ich versteh immer noch net, wie der 8bit µcontroller die 32 bit
zahl durch 10 teilt, der kann doch eigentlich nur mit 8 bit bzw. 16 bit
bei multi/div umgehen. den code hab ich mir schon durchgelesen.

das #precision soll z.b. die anzahl der bits meiner darstellen (also
muss ich dort #32 schreiben, das # gibt ja an, dass das ne konstante
sein soll oder?). und wieso beginnen die sprungmarken mit einem ?. hat
das einen bestimmten grund? und so ganz versteh ich auch net was das
programm eigentlich macht, also zuerst wird #precision ins register 2
übertragen, dann wird der accu gelöscht, nun gehts schon los, was ist
aptr? wahrscheinlich ne symbolische adresse oder? also adpr wird ins
register 0 übertragen, nun wird register 0 um 1 decrementiert, dann
wird durch indirekte adressiernng (adresse steht in register 0) der
accuinahlt mit diesem speicherplatz getauscht und dann wie 4
niederwertigsten bits nochmal, also im endeffekt nur die 4
höherwertigen bits, swap a heißt doch glaube dass die 4 unteren bits
des accus mit den 4 oberen bits getauscht/rotiert werden oder?...

also die befehle kenn ich soweit alle nur, ich versteh die logik
dahinter nocht so 100 %, also was der code eigentlich macht. ich werd
mich diese nacht nochmal drüber machen den code zu verstehen :)

aber wie gesagt, hab bisher kaum programmiererfahrungen mit assembler.

danke schon mal für die hilfe, ich bewunder echt die leute, die da
durchblicken :)

von Peter D. (peda)


Lesenswert?

Der 8051 ist ein 8-Bitter, d.h. 32 Bit sind 4 Byte, also precision = 4.

Das ? habe ich genommen für Sprungmarken, die funktionslokal sind.

APTR ist der Beginn eines Speicherbereiches, der fürs Rechnen benutzt
wird (je Operand 4 Byte für 32 Bit).


Peter

von Mario S. (wandiii)


Lesenswert?

hätte noch paar kleine fragen :)

in aptr steht auf deutsch also immer die zahl mit der ich rechnen will
und zwar hab ich das so verstanden immer nur 8 bit, da du in deinem
beispielpogramm ja

atpr: ds 1

schreibst und das ja bedeutet es wird 1 byte speicherplatz reserviert

im astack stehen die adressen der speicherstellen wo ich meine werten
von den ports hingelegt hab, bzw. da muss ich die werte der ports
hintransferieren.

astack ds 4 * precision (ich muss precision=4 wählen da ja 4 byte)
reserviert für den astack nun 4 byte speicher.

jetzt kommt schon die erste frage: wie kann ich dem astack jetzt
adressen vorgeben also wo der seine werte her holen soll, weil bisher
beginnt der ja an adresse 30h (jedenfalls im beispielprogramm, da steht
ja dseg at 30h), bedeutet das von 30h bis 38h liegt nun der
speicherbereich vom atpr und von 38h bis 58h liegt dann mein astack?

von Mario S. (wandiii)


Lesenswert?

hmmm leider geht das ganze net zu editieren oben :)

mir ist aufgefallen bei astack definierst du: astack ds 4*precision,
wähle ich also precision 4, reserviert der 16 byte für astack(4*4=16),
wieso das?

von Mario S. (wandiii)


Lesenswert?

hmm das mit den adressen kann auch net stimmmen

hab nen kurzes programm geschrieben und das haut net hin

precision  equ 4
  dseg  at 30h
aptr:    ds 1
        cseg at 0h
MOV 30h,P0
MOV R0,APTR
MOV P1,@R0

normal sollte der doch Port0 auf Adresse 30h tansferieren, dann nimmt
der das was in APTR steht (sollte doch 30h sein oder, wenn ich das pben
mit dseg at 30h initialisier?) und tranferiert das ins R0, von dort wird
 indirekt adressiert und zwar adresse 30h (was dort als inhalt drin
steht) sollte in den Port1

aber irgendwie schiebt der mir 0h in Port1, also steht da auch 0h drin,
wieso steht in adresse 30h net das was ich port0 vorgeb?

von Mario S. (wandiii)


Lesenswert?

ps: mit cseg haben wir gearbeitet aber dseg ist mir leider noch nie
untergekommen, vielleicht versteh ich da was falsch, aber dseg steht
doch für datensegment oder? ab dieser adresse legt der seine daten ab,
also z.b. APTR?

von Mario S. (wandiii)


Lesenswert?

hmm dsa hat sich erledigt, geht doch, bin grad leicht durcheinander
gekommen, in aptr stimmt scho alles, muss ja 0 raus kommen, steht ja
gar nix drin, sorry wenn ich soviel hier im forum schreib, aber blick
echt kaum durch und muss mich mühsam einarbeiten in die materie

von Peter D. (peda)


Lesenswert?

ist schon ewig her, seit ich das entwickelt habe.

Also APTR ist nur der Merker für den Zeiger und ASTACK ist dann der
eigentliche Speicherbereich.

Wie tief ASTACK ist, hängt davon ab, wie viele Variablen man
gleichzeitig ablegen will.

Will man z.B. 2 Werte addieren, muß da auch Platz für 2 Werte sein,
also 2 * 4 = 8 Byte.


Peter

von Mario S. (wandiii)


Lesenswert?

so nochmal nachgefragt :) um wirklich sicher zu gehen: ich poste auch
mal den code ^^

;**********************  Division / 10 for decimal output
****************
;Attention: Input must be positive !

;Output: X = X / 10, ACC = X % 10
;   F0 = (F0 and 0) if X != 0;

adivr10:
  mov  r2, #precision
  clr  a
  mov  r0, aptr
?ad101:  dec  r0
  xch  a, @r0
  xchd  a, @r0
  swap  a
  mov  b, #10
  div  ab      ;H-Digit /10
  swap  a
  xch  a, @r0
  swap  a
  add  a, b
  swap  a
  mov  b, #10
  div  ab      ;L-Digit /10
  xchd  a, @r0
  mov  a, @r0
  jz  ?ad102
  clr  f0
?ad102:  mov  a, b
  djnz  r2, ?ad101
  ret

also ich versuch jetzt mal in meinen worten zu erklären, wie das ganze
funktionieren soll

in precision wird in r2 kopiert, d.h. dort steht dann die anzahl meiner
bytes drin, die meine zahl die ich von dual in dezimal umwandeln will
hat. in zusammenhang mit der letzten zeile djnz r2,.... bedeutet das
ganze ist ne schleife und bei 32 bit (4byte) dualzahlen, wird das ganze
von programm von marke ?ad101 bis zu dem djnz... genau 4 mal
durchlaufen, also immer für 8 bit nehm ich mal an, ist ja auch logisch
irgenwie

clr a ist wohl klar

mov r0, aptr

aptr ist sozusagen ne art stack, also wie der normale stack nur halt
extra programmiert, in diesem stehen eigentlich adressen drin, wo die
daten (dualzahl) hinterlegt ist und die zeile heißt, in r0 wird die
adresse der ersten 8 bits meiner dualzahl von aptr transferiert und
dann wird r0 mit dec r0 um eins dekrementiert (wieso das gemacht wird
verstehe ich noch nicht).

nun wird mit a,@r0 der speicherinhalt in den akku geladen und zwar der
unter der adresse im register r0 steht

nun werden die 4 untersten bites wieder zurückgetauscht

mit swap a werden nun die 4 untersten mit den 4 obersten bits im accu
getauscht


im hilfsregister b wird nun dezimal 10 geladen

und div ab teilt nun a mit b

wieder swap a

dann wird nocmal der inhalt der von r0 indirekt addressierten
speicherzelle mit a getauscht

und nochmal swap a

nun wird a und b addiert

wieder swap a

und a nun wieder durch 10 geteilt

a wird nun wieder mit der speicherstelle (R0 indirekt adressiert)
getauscht, bzw. nur die 4 unteren bits und dann wird wieder alles
untereinander getauscht durch mov a,@R0

ist der akku 0 wird auf ?ad102 gesprungen und der rest der letzten
division (B) in den akku geladen


bitte korrigieren falls ich da was falsch verstanden hab, ich versuch
nämlich grad mal auf dieser grundlagen durchzusteigen, was dieser code
 eigentlich macht bzw. wie er es macht.

was ich auch noch nicht verstanden haben (dafür ist wohl apdec:
(increment arithemtic pointer) da, wie er dann die nächsten 8 bit von
der dualzahl holt.

kann ja nur so gemacht werden, dass aptr decrementiert wird und somit
die nächsten 8 bit meiner dualzahl adressiert oder?

also atpr adressiert mir immer indirekt die speicherstelle von astack,
wo gerade ein ergebnis drin steht?

da der astack bei 4 byte 16 byte groß ist, kann es ja nur sein, dass
auch zwischenergebnisse in diesem drin stehen.

aptr adressiert mir doch aber immer nur 1 byte vom astack und
decrementiert wird der astack immer gleich um die precision also um 4
byte, wie komm ich also an die 3 anderne byte ran?

von Mario S. (wandiii)


Lesenswert?

ja das hab ich mitlerweile auch verstanden :) mit zeiger meinst du
bestimmt zeiger auf den speicherbereich von astack

nur ich komm mit der ganzen adressierungsgeschichte noch nicht so klar

was ich jetzt gemacht hätte ist, dass ich meine dualzahlen direkt im
speicherbereich vom astack (kann man ja ausrechnen wenn man ein cseg
vorgibt) abgespeichert hätte. dann müsste ich nur noch gucken, dass
aptr mir immer darauf zeigt, was ich gerade brauch. und genau das ist
mir noch unklar wie ich das hinbekomme und zwar, weil mir noch nicht
klar ist, wie die eigentliche division durchgeführt wird, also wieso
die mathematik dahinter ist, wenn ich das verstanden hätte, könnte ich
mich ja auch um atpr kümmern, damit der das richtig macht, aber ich
versteh es immer noch net :/.

bisher haben wir immer nur lauflichter programmiert, einfache 8 bit
addition ,sinuston am port generieren. das schwierigste bisher war mal
nen kleines programm mit interrupts, aber das brauch ich hier ja gar
nicht :)


"ist schon ewig her, seit ich das entwickelt habe.

Also APTR ist nur der Merker für den Zeiger und ASTACK ist dann der
eigentliche Speicherbereich.

Wie tief ASTACK ist, hängt davon ab, wie viele Variablen man
gleichzeitig ablegen will.

Will man z.B. 2 Werte addieren, muß da auch Platz für 2 Werte sein,
also 2 * 4 = 8 Byte.


Peter"

von Mario S. (wandiii)


Lesenswert?

noch ne kleine frage, wo wird eigentlich das ergebnis hingespeichert?

ne 32 bit zahl kann doch max. rund 4 mrd. dezimal werden bzw. im
zweierkomplement  rund 2 mrd. d.h. ich muss mit 10 dezimalziffern
rechnen, die ich ja auch irgendwo speichern muss?

wie gesagt ich blick den code noch nicht so ganz durch und auch net wo
mein ergebnis eigentlich hingelegt wird, eigentlich bleibt ja nur der
astack übrig, aber wie wird das gemacht?

von Mario S. (wandiii)


Lesenswert?

hmmm ich geh hier wohl vielen auf den keks weil sich keiner mehr zu wort
 meldet

ich hab das ganze nochmal durchprobiert und bin zu folgenden gekommen:

nehmen wir an ich hab ne 16 bit dualzahl 01000100 00101000 (17448)

die stehen an speicherstelle 32h und 31h

nun nehm ich mir zuerst 32h vor

der code macht nun nix anders als die 8 bit durch 10 dezimal zu teilen
und damit den rest zu berechnen

der rest wird gespeichert im akku

nun wird mit den anderen 8 bit das gleiche gemacht und wieder durch 10
geteilt und der rest ermittelt.

der rest von der ersten teilaktion und der rest von der zweiten
teilaktion sind nun addiert (falls beides kleiner gleich 10, ansonsten
muss ich wohl nochmal 10 abziehen) meine niederwertigste
dezimalziffer.

in adresse 32 h und 33 h steht jeweil der ganze teil der durch/10
division, mit dem ich nun das ganze spiel wieder mach, so dass ich
dezimalziffer 2, 3,... bekomm

da 16 bis max. 5dezimalziffern entspricht muss ich das also 5 mal
wiederholen also ingesamt 10 mal die division durch 10 und
restermittlung?

nun stellt sich mir nur die frage: wie bekomm ich das jeweilige
restergebnis aus dem akku raus? der läuft an in ner schleife, muss ich
das irgendwo in dem code noch einfügen, also in der schleife oder wie
kann ich den akku mit dem divisionsrest sichern?

von Mario S. (wandiii)


Lesenswert?

und noch ne frage: wie kann ich das mit dem programmcode am
geschicktesten machen, dass der mir bei 32 bit immer z.b. zuerst den
teil der in z.b. in 34h, dann in 33h, dann in 32h, dann 31h gespeichert
ist berechnet, dazu muss ich doch aptr immer dekrementieren oder aber
das fehlt im dem code total, wie das gemacht wird versteh ich noch net
so ganz

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

"nun nehm ich mir zuerst 32h vor"

Du kannst nicht einzelne Bytes getrennt behandeln, da kommt nur Mist
raus.

Die Routine ADIVR10 arbeitet immer über die komplette 32Bit-Zahl.


Anbei mal ein Testprogramm, welches hex 499602D2 nach dezimal wandelt.

Die 32Bit Operanden werden in ASTACK abgelegt (niederwertiges Byte
zuerst) und APTR zeigt dann auf den nächsten freien Speicher hinter dem
letzten Operanden.

Die Ziffern in A kannst Du entweder direkt auf ne Anzeige ausgeben oder
woanders abspeichern, z.B. mittels R1 als Zeiger.


Peter

von Mario S. (wandiii)


Lesenswert?

ich hab nun 5 stunden rumgebastelt und das programm ist fast fertig :)

hab vieles nun manuell aufgebaut, bzw. für 32 bit umgebastelt :)

hab sogar vorher die prüfung des msb auf 0 oder 1 hinbekommen und dann
die 2er komplementbildung :)^^

nun fehlt nur noch die schnittstelle, da les ich mich grad etwas ein
wie das geht, aber danke scho mal für die hilfe, wenn ich mit der
schnittstelle net zurecht komm, meld ich mich nochmal

von Peter D. (peda)


Lesenswert?

"und dann die 2er komplementbildung"

Hoffentlich hast Du nicht vergessen, auch den Übertrag abzuziehen
(siehe  Funktion AABS).


Peter

von Mario S. (wandiii)


Lesenswert?

ne das passt scho, nu hab ich nur nen anders prob^^

mit der seriellen schnittstelle, ich hab ja wie gesagt kein µcontroller
hier, das ganze soll rein theoretisch gemacht werden und wird wohl
später überprüft. aber genau um die überprüfung gehts

wie kann ich das mit der seriellen schnittstelle testen?
hab gelesen das ginge mit nem terminal programm, aber wie muss ich die
software einstellen oder muss ich da was spezielles programmieren?

benutze keil µvision 2

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.