Hi Leute!
Ich will ein Programm schreiben, dass mir 5 Zahlen entgegennimmt und
danach in einem Doppelwort abspeichert. Für das entgegennemen benutze
ich ein Makro. Dieses Makro schreibt mir den ASCII-Code der Zahl in AL.
In der Variable "Zahl" die als Doppelwort deklariert ist, sollen dann
die 5 entgegengenommenen Zahlen abgespeichert sein. Mein Problem ist
nun, dass ich nicht weiß wie ich den Inhalt von AL in die als Doppelwort
deklararierte Variable "Zahl" hineinschreibe. Könnt ihr mir helfen?
Ich hab's jetzt schon mal zusammengebracht, mit dem Befehl MOV BYTE
PTR[Zahl],BL den Inhalt von BL in Zahl zu kopieren. Das Problem ist aber
jetzt, dass er mir in der Variable "Zahl" den vorher reinkopierten wert
überschreibt. Was mach ich falsch?
Aussehen tut das jetzt so:
%noctls ; Listing-Steueranweisungen werden nicht im Listing
augegeben
%noincl ; Incude-Dateien werden nicht im Listing ausgegeben
INCLUDE MACROS.ASM ; Datei Macros.asm einbinden
ASSUME CS:CODE, DS:DATA ; Segmentnamen bekannt geben
; „CODE“ wird Codesegment, „DATA“ wird
Datensegment
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++
DATA SEGMENT ; Beginn des Segmentes „DATA“
Zahl DD 0h
DATA ENDS ; Ende des Segmentes „DATA“
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++
;-----------------------------------------------------------------------
--------------------------------------------------------
CODE SEGMENT ; Beginn des Segmentes „CODE“
Anfang:
MOV AX, DATA ; Datensegmentadresse ins ;Register AX
MOV DS, AX ; Inhalt von Register AX ins Register DS
MOV AX,0h ; AX auf 0 setzen
MOV BX,0h ; BX auf 0 setzen
MOV CX,5h ; Abbruchbedingung von LOOP in CX schreiben
MOV DX,0h ; DX auf 0 setzen
Marke1:
Read_KBD_and_Echo ; Makro zum Einlesen einer Zahl
aufrufen
MOV BL,AL ; Momentanen Inhalt von AL in BL
speichern
MOV BYTE PTR [Zahl + 2]
LOOP Marke1
Terminate_Program ; Makro Terminate_Program aufrufen
CODE ENDS ; Ende des Segmentes „CODE“
;-----------------------------------------------------------------------
--------------------------------------------------------
END Anfang ; Ende des Programms
; Programm wird bei Anfang
gestartet
Angenommen das ist x86 Assembler.
> MOV BYTE PTR [Zahl + 2]
Wo ist hier der zweite Operand
> LOOP Marke1
Den Befehl LOOP kenn ich nicht ist das auch ein Macro?
Wo ist die Bedingung für die 5 Durchläufe?
gugge da
http://www.i8086.de/asm/8086-88-asm-stosb.html
Achja Doppelwort hat nur 4 Byte wie das mit 5 Zeichen Passt ist mir ein
Rätsel.
Formuliere noch mal klarer was du willst (sollst), beachte Zahl ist
nicht Ziffer.
> Den Befehl LOOP kenn ich nicht ist das auch ein Macro?
Nein, das ist ein normaler Assembler-Befehl. Er dekrementiert CX und
springt zur angegebenen Sprungmarke, falls es danach nicht 0 ist.
Skua schrieb:> Achja Doppelwort hat nur 4 Byte wie das mit 5 Zeichen Passt ist mir ein> Rätsel.
Es ist zu vermuten, daß der TO fünf Ziffern einlesen will, und diese als
fünfstellige Dezimalzahl interpretieren will.
Damit ist es mit einer einfachen Schiebeoperation natürlich gar nicht
mehr getan, da muss gerechnet werden.
Der Algorithmus sieht so aus:
- Ziel = 0 (32-Bit-Wert)
- Zeichen einlesen
- Zeichen in Ziffer umwandeln (0x30 -> 0, 0x39 -> 9)
- Ziel += Ziffer (erste Stelle)
- Zeichen einlesen
- Zeichen in Ziffer umwandeln (0x30 -> 0, 0x39 -> 9)
- Ziel *= 10
- Ziel += Ziffer (zweite Stelle)
- Zeichen einlesen
- Zeichen in Ziffer umwandeln (0x30 -> 0, 0x39 -> 9)
- Ziel *= 10
- Ziel += Ziffer (dritte Stelle)
- Zeichen einlesen
- Zeichen in Ziffer umwandeln (0x30 -> 0, 0x39 -> 9)
- Ziel *= 10
- Ziel += Ziffer (vierte Stelle)
- Zeichen einlesen
- Zeichen in Ziffer umwandeln (0x30 -> 0, 0x39 -> 9)
- Ziel *= 10
- Ziel += Ziffer (fünfte Stelle)
Erweiterung:
Wird beim nach dem Einlesen der Zeichen festgestellt, daß es sich nicht
um eine Ziffer handelt, kann die Prozedur abgebrochen werden, "Ziel"
enthält dann den bisher eingegebenen Wert, was die Eingabe auch von ein-
bis vierstelligen Zahlen ermöglicht.
Im Gegensatz zu den hier oft programmierten µCs kann der x86
multiplizieren, daher sollte eine Umsetzung des obigen Algorithmus kein
riesiges Problem darstellen.
Rufus t. Firefly schrieb:> Im Gegensatz zu den hier oft programmierten µCs kann der x86> multiplizieren
Also viele AVRs haben aber auch einen Multiplizierer an Board, den man
auch per Assembler ansprechen kann auf vielfältige Weise ;)
> Den Befehl LOOP kenn ich nicht ist das auch ein Macro?
Soweit ich mich errinnern kann, ist LOOP <Marke> so etwas:
dec cx
jrnz marke
also ideal für (16Bit) Zählschleifen.
mfG ingo
Hey Leute!
Danke für eure Hilfe!
Der Algorithmus von rufus hat mir sehr weitergeholfen. Wenn ich nun mit
diesem READ_KBD_and_ECHO eine Zeichen einlese, dann steht dieses Zeichen
laut Makrobeschreibung in AL. Zusätzlich dazu steht in AH eine "1".
Keine Ahnung wo diese "1" herkommt. Nun rechne ich das Zeichen in eine
Zahl um und speichere es wieder in AL. Wie aber bekomme ich diese Zahl
nun in das ganze AX? Einfach mit MOV geht's ja nicht weil MOV Befehle
der Art AX,AL nicht erlaubt... Wie bekomme ich die Zahl dennoch von AL
in AX?
Könnt ihr mir helfen?
So, also dank eurer Hilfe sieht das ganze jetzt so aus:
Anfang:
MOV AX, DATA ; Datensegmentadresse ins ;Register AX
MOV DS, AX ; Inhalt von Register AX ins Register DS
MOV AX,0h ; AX auf 0 setzen
MOV BX,0h ; BX auf 0 setzen
MOV CX,0h ; CX auf 0 setzen
MOV DX,0h ; DX auf 0 setzen
Read_KBD_and_Echo ; 1. Zeichen einlesen
SUB AL,30h ; 1. Zeichen in 1. Zahl umrechnen
XOR AH,AH ; Funktionsaufruf löschen
MOV BX,10000 ; 10000 in BX speichern
MUL BX ; Inhalt von AX mit BX multiplizieren
MOV CX,AX ; AX in DX zwischenspeichern
Read_KBD_and_Echo ; 2. Zeichen einlesen
SUB AL,30h ; 2. Zeichen in 2. Zahl umrechnen
XOR AH,AH ; Funktionsaufruf löschen
MOV BX,1000 ; 1000 in BX speichern
MUL BX ; Inhalt von AX mit BX multiplizieren
ADD CX,AX ; CX mit AX addieren und in DX speichern
Read_KBD_and_Echo ; 3. Zeichen einlesen
SUB AL,30h ; 3. Zeichen in 3. Zahl umrechnen
XOR AH,AH ; Funktionsaufruf löschen
MOV BX,100 ; 100 in BX speichern
MUL BX ; Inhalt von AX mit BX multiplizieren
ADD CX,AX ; CX mit AX addieren und in DX speichern
Read_KBD_and_Echo ; 4. Zeichen einlesen
SUB AL,30h ; 4. Zeichen in 4. Zahl umrechnen
XOR AH,AH ; Funktionsaufruf löschen
MOV BX,10 ; 10 in BX speichern
MUL BX ; Inhalt von AX mit BX multiplizieren
ADD CX,AX ; CX mit AX addieren und in DX speichern
Read_KBD_and_Echo ; 5. Zeichen einlesen
SUB AL,30h ; 5. Zeichen in 5. Zahl umrechnen
XOR AH,AH ; Funktionsaufruf löschen
ADD CX,AX ; CX mit AX addieren und in DX speichern
Nun hab ich aber doch noch einige Fragen. Nämlich: Mit 5 Zahlen die man
eingeben soll ist die größte Zahl 99999. Diese Zahl entspricht 1869Fh.
Wenn ich nun mein Programm mit dieser "Zahl" 99999 füttere, dann steht
in AX 869Fh. Das ist klar, weil ich mich momentan noch nicht um den
Übertrag kümmere. Wie aber mach ich das am besten? Irgendwie muss ich
wahrscheinlich noch die Stelle um irgendein Codeschnipsel ergänzen an
der addiert bzw. multipliziert wird, denn nur an diesen beiden Stellen
kann überhaupt ein Übertrag entstehen. Wie aber fang ich das da ab?
Könnt ihr mir nochmal helfen?
Noch eine Frage. Ich soll nun die berechnete Zahl auf dem Bildschirm
mittig ausgeben. Ich hab jetzt schon einige Zeit damit verbracht Antwort
in meinem Assemblerbuch zu finden, aber über die Makros Display_Char und
Display_String kann ich leider keine weiteren Informationen finden. Vor
allem stell ich es mir schwierig vor, da ja die Zahl als Hex im Register
vorliegt und ich soll ja bestimmt nicht die Hex-Zahl sondern die
Dezimalzahl ausgeben... Wie gesagt, ich weiß nicht wie das gehen soll...
Vielleicht helft ihr mir einfach nochmal! Dankeschön!
Anstatt PUSH POP kannst du das ganze auch im Datensegment speichern.
An ADC geht kaum ein weg vorbei.
Umständlich:
ADD BX,AX
JNC Labelx ; Kein Überlauf
ADD CX,1 ; bei Überlauf 1 Addieren (INC CX ginge auch)
Labelx:
Erst mal die Frage: Wieso willst Du kein XOR verwenden um Register zu
nullen? Das ist die einfachste und schnellste Möglichkeit... da Dein
Sourcecode kommentiert ist spielt das also keine Rolle.
Dann eine andere Frage: Was ist mit SI und DI? Die kann man doch auch
bentutzen... oder hab ich da was falsch in Erinnerung?
Und zu guter letzt: Register die man benutzt sollte man sichern,
initialisieren (z. B. mit 0x00 bzw 0x0000) und zum Schluss wieder
herstellen (lernst Du später bei PUSH and POP ;)), nur um die Frage zu
beantworten wieso DH 1 war: Weil es ein anderer Programmteil (Dein
eigener oder ein anderer) so hinterlassen hat und möglicherweise so auch
wieder benötigt (z. B. im Falle nach dme Aufruf von Unterroutinen). Da
ich, bis jetzt, nur WinASM genutzt hatte (16 Bit-Coding hab ich recht
früh fallen gelassen) weiß ich dass manche Register frei zur Verfügung
stehen, manch andere nicht. Das ist so zumindest unter
Windows-API-Programmierung. EAX, ESI und EDI sind z. B. solche freien,
deren Werte sind undefiniert nach einem API-Call. Alle anderen muss man
sichern und wieder herstellen. Daher mein letzter Tipp, da ich den Rest
des Codes nicht kenne schadet ein Hinweis sicher nicht. Man sollte sich
das gleich angewöhnen um spätere, unerwartete Fehler zu vermeiden.
Last but not least: Du willst Zahlen einlesen? Dann stell auch sicher
dass die Eingabe eine Zahl war, man kann nicht einfach einen Wert
abziehen und hoffen dass draus eine Zahl wird! So was kann in einer
bösen Sicherheitslücke enden und man sollte sich von Anfang an
angewöhnen Eingabefehler abzufangen, also erst prüfen ob es eine Zahl
war, falls nicht muss eine Fehlermeldung ausgegeben werden und die
Eingabe beginnt von vorne. Besser wäre natürlich wenn der Benutzer gar
keine Zahlen erst eingeben kann... ob das bei "Read_KBD_and_Echo" weiß
ich aber nicht, falls das der Fall ist ignoriere meinen Einwand. ;)
So, das wars von mir... interessant dass ich noch was davon behalten
habe ^^
Tobias Mehrl schrieb:> Der Algorithmus von rufus hat mir sehr weitergeholfen.
Und warum hast Du es dann doch anders gemacht? Würdest Du jeweils das
Ergebnis mit 10 multiplizieren, hättest Du die Chance, auch Zahlen mit
weniger als 5 Stellen einlesen zu können. So aber ist die erste Ziffer
immer der Zehntausenderwert.
@Tobias
die Kommentare deiner Assemblerbefehle könntest du noch optimieren.
einfache Regel:
Kommentiere nicht WAS du machst, sondern WARUM du es machst. Schreibe
einen Kommentar hinein, den du auch noch in 5 Jahren direkt verstehst,
und wo du nicht grübeln mußt "warum hatte ich denn jetzt eigentlich
genau diesen Befehl hier verwendet"?
--> Das Ziel sollte sein: "Eine Zeile Kommentar ist wertvoller als eine
Zeile Code"
Beispiele:
1.
MOV AX,0h ; AX auf 0 setzen
MOV BX,10000 ; 10000 in BX speichern
Kommetare wie die zuvor genannten sind ziemlich "unnütz". DAS die
Befehle genau das machen was du daneben geschrieben hast, steht im
Datenblatt des Prozessors/Assemblerbefehls. Was die Intention ist, in
deinem Algorithmus genau diesen Wert zu nutzen, steht jedoch nicht im
Datenblatt
2.
XOR AH,AH ; Funktionsaufruf löschen
DASS damit der AH auf den Wert "0" gesetzt wird ist mit ein bischen
Datenblattstudium zu identifizieren (anstelle des "tricky" XOR hätte ich
hier ein MOV Statement empfohlen). Aber wieso wird dadurch ein
"Funktionsaufruf gelöscht"?
3.
SUB AL,30h ; 1. Zeichen in 1. Zahl umrechnen
uhhh, Magie greift um sich. Eine Zahl wird umgerechnet. WIESO?
Kommentar-Vorschlag z.B.
ASCII "0" = 0x30 ... ASCII "9" = 0x39.
ACSII-Offset subtrahieren, um echte Binärzahl zu erhalten.
>Vor allem stell ich es mir schwierig vor, da ja die Zahl als Hex im >Register>vorliegt und ich soll ja bestimmt nicht die Hex-Zahl sondern die>Dezimalzahl ausgeben... Wie gesagt, ich weiß nicht wie das gehen soll...
Eine reine Hex-Zahl kannst du in Decimal umwandeln wenn du fortlaufend
durch 10 teilst. Die Reste pushst du in einer Schleife auf den Stack und
liest sie dann, in einer zweiten schleife, wieder runter und gibst sie
aus. (Aber Vorsicht, die X86-Division ist ein Befehl den du sehr gut
verstehen musst.) Vor der Ausgabe musst natürlich noch auf Ascii (mit
Addition) konvertieren.
Du bist bestimmt Student im Info-Kurs (das ist eine der typischen
Aufgaben mit Fallstricken) und ich schätze mal selbst wenn du dich
reinkniest, wirst du Tage brauchen bist du die ganze Aufgabe gelöst
hast. Viel Erfolg!
@ asm Guru:
Also ich rechne ein Zahl von Hex in Dezimal so um:
3: 3 · 1 = 3
7: 7 · 16 = 112
1: 1 · 256 = 256
B: 11 · 4096 = 45056
——————
45427
Wie du das meinst mit fortlaufen durch 10 teilen verstehe ich nicht. Von
einer Hex-Zahl zurück auf eine Dezimalzahl komme ich doch nur mit dem
oben gezeigten Schema... Das mit dem Stack mag dann einleuchten,
funktioniert aber auch so lange nicht wie ich nicht die "Reste" meiner
eingegebenen fünfstelligen Zahl habe!
Tobias Mehrl schrieb:> Wie du das meinst mit fortlaufen durch 10 teilen verstehe ich nicht.
wie zerlegst du die Zahl 4598 in die einzelnen Ziffern?
4598 % 10 macht 8
4598 / 10 macht 459
459 % 10 macht 9
459 / 10 macht 45
45 % 10 macht 5
45 / 10 macht 4
4 % 10 macht 4
4 / 10 macht 0 -> fertig
Lies die Ziffern in den Modulo (%) Zeilen von unten nach oben.
Da steht 4 5 9 8
und die ursprüngliche Zahl war 4598
> Von> einer Hex-Zahl zurück auf eine Dezimalzahl komme ich doch nur mit dem> oben gezeigten Schema...
Du willst das Horner Schema anwenden.
Das besagt:
anstatt zu rechnen
(4 * 1000) + (5 * 100) + (9 * 10) + (8*1)
kannst du auch rechnen
(((((4) * 10) + 5) * 10) + 9) * 10 + 8
anstelle von unterschiedlichen Multiplikanden 1000, 100, 10
multiplizierst du einfach immer nur mit 10
Die Zerlegung ist dann einfach nur die Umkehrung davon
Wegstaben Verbuchsler schrieb:> Kommentiere nicht WAS du machst, sondern WARUM du es machst.
Völlig richtig
> --> Das Ziel sollte sein: "Eine Zeile Kommentar ist wertvoller als eine> Zeile Code"
Zugegeben: In Assembler ist das verdammt schwer bis unmöglich. Aber das
Ziel ist eigentlich, dass der Code sein eigener Kommentar ist. D.h. ein
wirklich guter Code braucht nicht unbedingt einen Kommentar, weil alles
wichtige schon im Code steht.
Wie gesagt: In Assembler geht das nicht, aber in einer Hochsprache
schon. zb durch die Wahl von Variablennamen und/oder Funktionsnamen kann
ein Code ganz schön sprechend werden. Aber das hindert ja niemanden,
auch in Assembler um zb die jeweils günstigste Zahlendarstellung zu
kämpfen und sich so dem großen Ziel wenigstens ein bischen anzunähern.
In diesem Sinne ....
> SUB AL,30h ; 1. Zeichen in 1. Zahl umrechnen>> uhhh, Magie greift um sich. Eine Zahl wird umgerechnet. WIESO?> Kommentar-Vorschlag z.B.>> ASCII "0" = 0x30 ... ASCII "9" = 0x39.> ACSII-Offset subtrahieren, um echte Binärzahl zu erhalten.
... ist es hier die beste Lösung, auf einen Kommentar alle '0' abziehen
zu verzichten, sondern gleich zu schreiben
SUB AL, '0' ; aus dem Zeichen eine Zahl machen
das der ASCII Code für '0' die 0x30 ist, braucht niemand zu
interessieren.