Archiv verlassen und diese Seite im Standarddesign anzeigen : Quersumme bestimmen
zipperface
16.05.2005, 15:20
Ich möchte gerne ein Programm schreiben das die Quersumme einer Zahlenfolge berechnet. Die Zahlenfolge soll lauten 1234,1235 und 1236. Habe schon mal einen Ansatz versucht, weiss nicht ob das der richtige Weg oder Müll ist.
Mein Quellcode ist:
; Programmbereich
Start:
;Begruessungstext
MOV DX,Offset GRUSS
MOV AH,09H
Int 21h
;Berechnung der Quersumme
;hier erste Quersumme bestimmen
mov ax,0 ;Loeschen des Register
mov al,1 ;Wert 1 in Register AL laden
add al,2 ;zu dem Register AL den Wert 2 addieren
add al,3 ;zu dem Register AL den Wert 3 addieren
add al,4 ;zu dem Register AL den Wert 4 addieren
mov QUER1,al ;zur Variablen QUER1 den Wert aus Register AL laden
;hier zweite Quersumme bestimmen
mov ax,0 ;Loeschen des Registers
mov al,1 ;Wert 1 in Register AL laden
add al,2 ;zu dem Register AL den Wert 2 addieren
add al,3 ;zu dem Register AL den Wert 3 addieren
add al,5 ;zu dem Register AL den Wert 5 addieren
mov QUER2,al ;zur Variablen QUER2 den Wert aus Register AL laden
;hier dritte Quersumme bestimmen
mov ax,0 ;Loeschen des Register
mov al,1 ;Wert 1 in Register AL laden
add al,2 ;zu dem Register AL den Wert 2 addieren
add al,3 ;zu dem Register AL den Wert 3 addieren
add al,6 ;zu dem Register AL den Wert 6 addieren
mov QUER3,al ;zur Variablen QUER3 den Wert aus Register AL laden
mov ax,0 ;Loeschen des Register
mov al,QUER1 ;Variable QUER1 in Register AL laden
add al,QUER2 ;zur Variablen QUER1 die Variable QUER2 addieren
add al,QZER3 ;zum Register AL die Variable QUER3 addieren
mov QUER,al ;Inhalt des Register AL in die Variable QUER laden
; Ausgabe des Ergebnisses
mov ax,0 ;Loeschen des Registers AX
mov dx,0 ;Loeschen des Registers DX
mov dx, offset Meldung ;Ausgabe des Textes "Meldung"
mov ah,09h
int 21h
add QUER,30h ;Vorbereiten fr ASCII Ausgabe
mov dx,QUER ;Ausgabe der Quersumme
mov ah,02h
Int 21h
; Programmende
mov al,0 ;Fehlercode 0
mov ah,4Ch ;Funktion: Programm verlassen
int 21h ;Schnittstelle zu DOS
;*************************************************************************
;Datenbereich
ANFANG DW 1234,1235,1236
ANZAHL DB ?
QUER DB ?
QUER1 DB ?
QUER2 DB ?
QUER3 DB ?
GRUSS DB "Das folgende Programm berechnet die Quersumme einer 16-Bit-Zahlenfolge",Cr,Lf,"$"
Meldung DB "Die errechnete Quersumme lautet:",Cr,Lf,"$"
_TEXT ends ;Codesegment schlieáen
end Start ;Ende des Quellprogramms
Habe Probleme irgendwie alle Variablen und Konstanten usw. zu definieren.
Hoffe Ihr könnt mir helfen?
Hier noch die Original Aufgabenstellung:
"Erstelle ein Programm, welches aus einer Folge von 16-Bit-Zahlen diejenige bestimmt, die die kleinste Quersumme besitzt. Die Folge beginnt an der Adresse ANFANG. Die Zahl der Elemente ist in der Speicherzelle ANZAHL abgelegt. Das Element mit der kleinsten Quersumme soll in die Seicherstelle QUER gebracht werden.":mauer:
butterkeks
16.05.2005, 18:47
mhh...
so, wie du das lösen willst, ist das sicher nicht im Sinne der Aufgabe, denn du arbeitest nicht mit den Daten in ANFANG, sondern mit konstanten Werten in deinem Programm (du könntest auch gleich sagen "mov QUER, (1+2+3+4)", dann wäre es prinzipiell das selbe).
Ich würde das wohl in etwa so lösen:
Zuerst setzt du si auf ANFANG und cx auf die Zahl der Elemente (z.B. mit "mov cx, word ptr ANZAHL").
ANZAHL musst du natürlich vorher auf "3" setzen, nicht auf "?".
als nächstes lädst du deinen ersten Wert von "si" (also ANFANG) nach ax, indem du z.B. einen "lodsw" ausführst.
den Wert in AX teilst du durch 10.
Ich nehme mal an, du wählst ein byte register als Parameter für div (das reicht bei einem Divisor von 10 aus, wenn du mit Zahlen bis einschließlich 2559 arbeitest; Ansonsten brauchst du 16 bit)...
In diesem Fall liegt der Rest in ah; diesen addierst du zu einem (vorher auf 0 initialisierten) register.
Wenn al 0 ist, dann bist du fertig; Ansonsten setzt du ah auf 0 und teilst wieder durch 10, addierst den Rest etc.
Nun sollte im register, das du zum addieren verwendet hast, die Quersumme der ersten Zahl stehen.
Vergleiche den Wert mit [QUER]; Ist er kleiner? Dann setze den Wert von [QUER] auf die Summe, die du eben berechnet hast.
Du musst [QUER] natürlich entsprechend initialisieren, z.B. auf 255, wenn du mit DB arbeitest (sollte ausreichen).
Wenn cx nicht null ist, dann dekrementiere es und springe wieder zum lodsw (das lässt sich z.B. mit der "loop" instruktion lösen).
Wie du die Zahl ausgibst, ist eine komplett andere Geschichte und wurde hier im Board bereits behandelt, soweit ich weiß...
zipperface
17.05.2005, 12:05
mhh...
so, wie du das lösen willst, ist das sicher nicht im Sinne der Aufgabe, denn du arbeitest nicht mit den Daten in ANFANG, sondern mit konstanten Werten in deinem Programm (du könntest auch gleich sagen "mov QUER, (1+2+3+4)", dann wäre es prinzipiell das selbe).
Ich würde das wohl in etwa so lösen:
Zuerst setzt du si auf ANFANG und cx auf die Zahl der Elemente (z.B. mit "mov cx, word ptr ANZAHL").
ANZAHL musst du natürlich vorher auf "3" setzen, nicht auf "?".
als nächstes lädst du deinen ersten Wert von "si" (also ANFANG) nach ax, indem du z.B. einen "lodsw" ausführst.
den Wert in AX teilst du durch 10.
Ich nehme mal an, du wählst ein byte register als Parameter für div (das reicht bei einem Divisor von 10 aus, wenn du mit Zahlen bis einschließlich 2559 arbeitest; Ansonsten brauchst du 16 bit)...
In diesem Fall liegt der Rest in ah; diesen addierst du zu einem (vorher auf 0 initialisierten) register.
Wenn al 0 ist, dann bist du fertig; Ansonsten setzt du ah auf 0 und teilst wieder durch 10, addierst den Rest etc.
Nun sollte im register, das du zum addieren verwendet hast, die Quersumme der ersten Zahl stehen.
Vergleiche den Wert mit [QUER]; Ist er kleiner? Dann setze den Wert von [QUER] auf die Summe, die du eben berechnet hast.
Du musst [QUER] natürlich entsprechend initialisieren, z.B. auf 255, wenn du mit DB arbeitest (sollte ausreichen).
Wenn cx nicht null ist, dann dekrementiere es und springe wieder zum lodsw (das lässt sich z.B. mit der "loop" instruktion lösen).
Wie du die Zahl ausgibst, ist eine komplett andere Geschichte und wurde hier im Board bereits behandelt, soweit ich weiß...
Danke für deine Tipps, werde es mal nach deinen Vorschlägen versuchen umzusetzten. Du schreibst von einem lodsw, kannst du mir erklären was das ist?
Gruss Zipperface
butterkeks
17.05.2005, 17:08
LODSW liest ein word von DS:SI nach AX und erhöht SI um 2 (um ein word halt).
Daneben gibt es noch LODSB für bytes und LODSD für dwords.
Man hätte das auch mit einem "mov ax, [si]" und anschließendem "add si, 2" lösen können, aber mit dem lodsw ist es kompakter
zipperface
19.05.2005, 20:45
Hallo,
habe mir folgendes überlegt:
zuerst würde ich die Quersumme des Lowbytes (od. Word?) berechnen und in Variable speichern, dann die Quersumme des Highbyte berechnen, beides addieren und wieder in Variable speichern. Jetzt definiere ich eine Schleife und gehe wie oben beschrieben für die nächste Zahl vor. Vergleiche nun beide Variablen miteinander, wo bei die kleinste Quersumme gesucht ist, wenn ja springe zur nächsten Schleife. Habe hierbei Probleme die Schleifenbedingung zu bestimmen, habe auch keinen Schimmer wie alle Variablen usw. im Datenbereich bestimmen muss. Sollte ich hier mit Bytes oder Wörter arbeiten?
Schleife:
mov al,byte ptr ANFANG
div 10
add al,ah
mov VAR,al
mov al,byte ptr ANFANG+1
div 10
add al,ah
mov QUER,al
Schleife2:
mov al,byte ptr ANFANG+2
div 10
add al,ah
mov VAR,al
mov al,byte ptr ANFANG+3
div 10
add al,ah
mov QUER1,al
cmp QUER,QUER1
jne Schleife
Schleife3:
mov al,word ptr ANFANG+4
div 10
add al,ah
mov VAR,al
mov al,word ptr ANFANG+5
div 10
add al,ah
mov QUER2,al
cmp QUER,QUER2
jne Schleife2
;Datenbereich
ANFANG DW 1234,1235,1236
ANZAHL DW ?
QUER DW ?
QUER1 DW ?
QUER2 DW ?
VAR DW ?
Langsam aber sicher verliere ich die Nerven!!!!:mauer:
butterkeks
19.05.2005, 23:11
habe auch keinen Schimmer wie alle Variablen usw. im Datenbereich bestimmen muss. Sollte ich hier mit Bytes oder Wörter arbeiten?
ANZAHL und QUER würde ich ein Byte groß machen, weil man sicherlich nicht mehr brauchen wird (ist aber natürlich Sache des Szenarios).
Dein Code an sich hat wieder das Problem, dass mit konstanetn Werten gearbeitet wird (die Anzahl der Divisonen hängt von der Zahl der Ziffern ab, ist also nciht immer gleich) und einiges habe ich nicht so recht verstanden...
Hab mal eine kleine Routine gebaut und erläutert (ließe sich sicherlich ncoh verbessern).
Das Beispiel sollte sich mit dem TASM als COM File bauen lassen:
.model tiny
.code
org 100h
start:
mov si, offset ANFANG ; si zeigt nun auf ANFANG (erster Datensatz)
mov di, offset QUER ; di zeigt nun auf QUER
; (dort landet die Zahl)
mov cx, word ptr ANZAHL ; ANZAHL (anzahl der Datensätze) nach cx laden
mov dl, 10 ; dl ist nachher der operand für div
; eine Zahl abarbeiten
nextone:
lodsw ; zuerst wird sie nach ax geladen
mov bl, 0 ; in bl liegt die zwischensumme, deshalb
; wird das register zuerst geleert
; eine Ziffer addieren
digit:
div dl ; ax durch dl (also 10) teilen
add bl, ah ; der Rest der Division (eine Ziffer) liegt in ah.
; dieser wird zu bl addiert
mov ah, 0 ; ah für die nächste Division zurücksetzen
cmp ax, 0 ; ist ax = 0? Dann sind wir fertig...
jnz digit ; ...ansonsten addieren wir die nächste Ziffer
cmp bl, [di] ; bl (momentane Quersumme) mit der
; bisher kleinsten, [di], vergleichen
ja skip ; ist bl gößer? Dann lassen wir [di] so stehen...
mov [di], bl ; ... ansonsten ist bl der neue kleinste Wert
skip:
loop nextone ; wenn noch nicht alle Zahlen bearbeitet wurden,
; ist die nächste dran
; hier bist du gefragt!
; die kleinste Quersumme steht nun in QUER und du kannst
; sie mit den dir (mehr oder weniger) bekannten Methoden
; ausgeben.
; wenn du fertig bist, wird das Prog hier beendet:
mov ah, 4Ch
int 21h
ANFANG: DW 1234, 1235, 1236 ; die Daten
QUER: DB 255 ; hier landet das Ergebnis.
; ich habe das auf 255 initialisiert, damit der
; erste Vergleich von bl < [di] funktioniert
; (setz es probeweise mal auf 0 und du siehst,
; dass es nicht mehr funktioniert)
ANZAHL: DW 3 ; die Anzahl der Daten
end start
denke immer daran, dass deine Daten auch von einem User eingegeben sein oder aus einer Datei stammen könnten; In diesen Fällen wäre es schwierig (= unmöglich), mit allzu vielen konstaten Werten zu arbeiten.
Ansonsten kannst du es auch von Hand machen und das Programm besteht nur aus einem einzigen "mov" befehl und das ist sicher nicht der sinn...
Das sollte mit Zahlen bis einschließlich 2559 funktionieren; Für mehr müsste man es geringfügig abändern.
Beim "ja" Beehl handelt es sich übrigens um die unsigned-Variante von "jg", also "jump if greater than"
Bei Fragen einfach fragen
@zipperface:
GANZ WICHTIGER HINWEIS:
Eine Zahl in Low- und Highbyte zu teilen und dann getrennt
deren Quersummen zu ermitteln funktioniert NICHT! Das ist
ne mathematische Tatsache und nicht zu ändern. Weil
Quersummen ja an ZAHLENSYSTEME bzw ZIFFERNSYSTEME gebunden
sind (und in diesem Fall das 10er-System).
Teilung in Low-/Highbyte teilt es aber an der 256er-Stelle
und die ist leider teilerfremd zu 10.
Beispiel: Die Zahl 1040 hat eine Quersumme von 5.
(1+0+4+0 = 5).
Geteilt in zwei Bytes ist 1040 dann 4 (Highbyte) und 16
(Lowbyte). Quersumme von 4 ist 4. Quersumme von 16 ist 7.
Beide zusammen sind 11. Was natürlich dann nicht hinhaut,
weil Quersumme von 1040 ja = 5 ist.
Merke: Nur weil eine Zahl A größer ist als eine Zahl B, ist
nicht automatisch auch die Quersumme von A größer als die
Quersumme von B.
Und nochwas:
Ob Du mit Bytes, Words oder DWords arbeitest, liegt daran,
welche Zahlenbereiche Du benutzen willst:
In einem Byte kannst Du Zahlen von 0 bis 255 speichern.
In einem Word kannst Du Zahlen von 0 bis 65535 speichern.
In einem DWord kannst Du Zahlen von 0 bis 4294967295 speichern.
Hinweis zu negativen Zahlen:
Wenn du Vorzeichen benutzen willst (was Du in dem Fall
hier natürlich NICHT willst) "halbieren" sich die
Bereiche, weil ja eine Hälfte der Zahlen dann immer die
negative ist. (also für Byte z.B. -128 bis +127. Es gibt
immer eine negative Zahl mehr als es positive gibt. Das
ist ne Definitionsfrage. Weil es zu Null einfach kein
"minus Null" gibt und man sich irgendwie (kommt auf die
Programmiersprache an) "geeinigt" hat, die Zahl als negativ
zu definieren.)
Eigentlich ist dies unwichtig, wollt's nur erwähnt haben.
Im vorliegenden Fall ist das so gemeint: Man kann in
Assembler verschiedene Arten von Divisionen ausführen,
nämlich BYTE- WORD- und DWORD (ok, QWORD....)
Das bezieht sich dabei jedoch auf die Größe des
Ergebnisses, d.h. die Dividend-Größe ist jeweils "doppelt
so groß" (also doppelt soviele Bytes).
Was butterkeks meint, ist, daß, wenn das Ergebnis in ein
BYTE paßt (und bei Teilung durch 10 ist das für alle
Zahlen bis 2559 der Fall, weil das ja die letzte Zahl ist,
die 255 ergibt - also in ein "Byte paßt") daß man dann
DIESES Programm benutzen kann. Für Zahlen größer als 2559
wäre das Ergebnis bei der ersten Teilung größer als 255 -
in dem Fall müßte man eine WORD-Division benutzen. Was
unter anderem dazu führt, daß man DL nicht mehr als
"Divisor" benutzen könnte, weil alle Divisionen außer Byte
zusätzlich zum AX auch das DX-Register mitbenutzen.
Das ist damit gemeint. - Will sagen: Byte-Division einer
Zahl >2559 durch 10 erzeugt einen INT0 (Division by Zero)
Fehler, weil "Result doesn't fit into Target Register"
denselben Fehler-INT erzeugt wie "Division by Zero". Und
wer will das schon? (Zumal das Ergebnis in dem Fall ja
unbrauchbar wäre.)
Und jetzt noch zur Quersumme selbst. AUF JEDEN FALL reicht
für die Quersumme ein Byte aus. IMMER!
Selbst eine DWORD-Zahl hat max. 10 Dezimalziffern. Und für
den unwahrscheinlichen Fall, daß alle diese Ziffern = 9
wären (was sie nie sein können), wäre die Quersumme 90 -
also paßt in ein Byte. (Müßte dann was bei 180 sein.)
Die Zahlen mit den größtmöglichen Quersummen sind:
DWORD: 3999999999 (Quersumme =84)
WORD: 59999 (Quersumme =41)
BYTE: 199 (Quersumme =19)
Oder, butterkeks' Vorschlag
Für Zahlen 0 bis 2559 wäre die Zahl mit der größtmöglichen
Quersumme 1999 (Quersumme 28).
Na? Muster erkannt?
Also, meiner Meinung nach - selbst für den
unwahrscheinlichen Fall, daß Du mit QWORDs (64Bit-Zahlen)
rechnest... Die müßten meiner Ansicht nach 20 (oder höchstens
21) Ziffern haben - und selbst deren größte Quersumme paßt
noch in ein Byte. (Müßte dann was bei 180 sein.)
Anmerkung:
Manchmal hilft es, vorher ein wenig die Theorie dahinter
durchzuspinnen. Hab mal irgendwann einen "Primfaktoren-
Zerleger" gebaut und hatte dann so überlegt: Hm. Brauch
ich ja ein Array - wie groß mach ich das - also in
wieviele Primfaktoren kann man eine Zahl einer bestimmten
Größe maximal zerlegen? ... Bis ich auf die Lösung kam - die
übrigens so verblüffend simpel ist, daß selbst ein Idiot
sie implementieren kann...
zipperface
20.05.2005, 12:43
ANZAHL und QUER würde ich ein Byte groß machen, weil man sicherlich nicht mehr brauchen wird (ist aber natürlich Sache des Szenarios).
Dein Code an sich hat wieder das Problem, dass mit konstanetn Werten gearbeitet wird (die Anzahl der Divisonen hängt von der Zahl der Ziffern ab, ist also nciht immer gleich) und einiges habe ich nicht so recht verstanden...
Hab mal eine kleine Routine gebaut und erläutert (ließe sich sicherlich ncoh verbessern).
Das Beispiel sollte sich mit dem TASM als COM File bauen lassen:
.model tiny
.code
org 100h
start:
mov si, offset ANFANG ; si zeigt nun auf ANFANG (erster Datensatz)
mov di, offset QUER ; di zeigt nun auf QUER
; (dort landet die Zahl)
mov cx, word ptr ANZAHL ; ANZAHL (anzahl der Datensätze) nach cx laden
mov dl, 10 ; dl ist nachher der operand für div
; eine Zahl abarbeiten
nextone:
lodsw ; zuerst wird sie nach ax geladen
mov bl, 0 ; in bl liegt die zwischensumme, deshalb
; wird das register zuerst geleert
; eine Ziffer addieren
digit:
div dl ; ax durch dl (also 10) teilen
add bl, ah ; der Rest der Division (eine Ziffer) liegt in ah.
; dieser wird zu bl addiert
mov ah, 0 ; ah für die nächste Division zurücksetzen
cmp ax, 0 ; ist ax = 0? Dann sind wir fertig...
jnz digit ; ...ansonsten addieren wir die nächste Ziffer
cmp bl, [di] ; bl (momentane Quersumme) mit der
; bisher kleinsten, [di], vergleichen
ja skip ; ist bl gößer? Dann lassen wir [di] so stehen...
mov [di], bl ; ... ansonsten ist bl der neue kleinste Wert
skip:
loop nextone ; wenn noch nicht alle Zahlen bearbeitet wurden,
; ist die nächste dran
; hier bist du gefragt!
; die kleinste Quersumme steht nun in QUER und du kannst
; sie mit den dir (mehr oder weniger) bekannten Methoden
; ausgeben.
; wenn du fertig bist, wird das Prog hier beendet:
mov ah, 4Ch
int 21h
ANFANG: DW 1234, 1235, 1236 ; die Daten
QUER: DB 255 ; hier landet das Ergebnis.
; ich habe das auf 255 initialisiert, damit der
; erste Vergleich von bl < [di] funktioniert
; (setz es probeweise mal auf 0 und du siehst,
; dass es nicht mehr funktioniert)
ANZAHL: DW 3 ; die Anzahl der Daten
end start
denke immer daran, dass deine Daten auch von einem User eingegeben sein oder aus einer Datei stammen könnten; In diesen Fällen wäre es schwierig (= unmöglich), mit allzu vielen konstaten Werten zu arbeiten.
Ansonsten kannst du es auch von Hand machen und das Programm besteht nur aus einem einzigen "mov" befehl und das ist sicher nicht der sinn...
Das sollte mit Zahlen bis einschließlich 2559 funktionieren; Für mehr müsste man es geringfügig abändern.
Beim "ja" Beehl handelt es sich übrigens um die unsigned-Variante von "jg", also "jump if greater than"
Bei Fragen einfach fragen
Hallo butterkeks,
habe das mal durchlaufen lassen und scheint zu funktionieren. meine ausgabe für das ergebnis funktioniert noch nicht richtig. das ergebnis ist doch eigentlich eine hex-zahl und muss für die darstellung diese in ein ascii-zeichen umwandeln. oder? habe mir gedacht das es so aussehen könnte :
mov dx, offset Meldung ;Ausgabe des Textes "Meldung"
mov ah,09h
int 21h
add QUER,30h ;Vorbereiten für ASCII Ausgabe
mov di,QUER ;Ausgabe der Quersumme
mov ah,02h
int 21h
bei der ausgabe bekomme ich irgend ein zeichen, aber nicht die zahl. habe ich da eine denkfehler?
gruss zipperface
zipperface
20.05.2005, 12:51
@zipperface:
GANZ WICHTIGER HINWEIS:
Eine Zahl in Low- und Highbyte zu teilen und dann getrennt
deren Quersummen zu ermitteln funktioniert NICHT! Das ist
ne mathematische Tatsache und nicht zu ändern. Weil
Quersummen ja an ZAHLENSYSTEME bzw ZIFFERNSYSTEME gebunden
sind (und in diesem Fall das 10er-System).
Teilung in Low-/Highbyte teilt es aber an der 256er-Stelle
und die ist leider teilerfremd zu 10.
Beispiel: Die Zahl 1040 hat eine Quersumme von 5.
(1+0+4+0 = 5).
Geteilt in zwei Bytes ist 1040 dann 4 (Highbyte) und 16
(Lowbyte). Quersumme von 4 ist 4. Quersumme von 16 ist 7.
Beide zusammen sind 11. Was natürlich dann nicht hinhaut,
weil Quersumme von 1040 ja = 5 ist.
Merke: Nur weil eine Zahl A größer ist als eine Zahl B, ist
nicht automatisch auch die Quersumme von A größer als die
Quersumme von B.
Und nochwas:
Ob Du mit Bytes, Words oder DWords arbeitest, liegt daran,
welche Zahlenbereiche Du benutzen willst:
In einem Byte kannst Du Zahlen von 0 bis 255 speichern.
In einem Word kannst Du Zahlen von 0 bis 65535 speichern.
In einem DWord kannst Du Zahlen von 0 bis 4294967295 speichern.
Hinweis zu negativen Zahlen:
Wenn du Vorzeichen benutzen willst (was Du in dem Fall
hier natürlich NICHT willst) "halbieren" sich die
Bereiche, weil ja eine Hälfte der Zahlen dann immer die
negative ist. (also für Byte z.B. -128 bis +127. Es gibt
immer eine negative Zahl mehr als es positive gibt. Das
ist ne Definitionsfrage. Weil es zu Null einfach kein
"minus Null" gibt und man sich irgendwie (kommt auf die
Programmiersprache an) "geeinigt" hat, die Zahl als negativ
zu definieren.)
Eigentlich ist dies unwichtig, wollt's nur erwähnt haben.
Im vorliegenden Fall ist das so gemeint: Man kann in
Assembler verschiedene Arten von Divisionen ausführen,
nämlich BYTE- WORD- und DWORD (ok, QWORD....)
Das bezieht sich dabei jedoch auf die Größe des
Ergebnisses, d.h. die Dividend-Größe ist jeweils "doppelt
so groß" (also doppelt soviele Bytes).
Was butterkeks meint, ist, daß, wenn das Ergebnis in ein
BYTE paßt (und bei Teilung durch 10 ist das für alle
Zahlen bis 2559 der Fall, weil das ja die letzte Zahl ist,
die 255 ergibt - also in ein "Byte paßt") daß man dann
DIESES Programm benutzen kann. Für Zahlen größer als 2559
wäre das Ergebnis bei der ersten Teilung größer als 255 -
in dem Fall müßte man eine WORD-Division benutzen. Was
unter anderem dazu führt, daß man DL nicht mehr als
"Divisor" benutzen könnte, weil alle Divisionen außer Byte
zusätzlich zum AX auch das DX-Register mitbenutzen.
Das ist damit gemeint. - Will sagen: Byte-Division einer
Zahl >2559 durch 10 erzeugt einen INT0 (Division by Zero)
Fehler, weil "Result doesn't fit into Target Register"
denselben Fehler-INT erzeugt wie "Division by Zero". Und
wer will das schon? (Zumal das Ergebnis in dem Fall ja
unbrauchbar wäre.)
Und jetzt noch zur Quersumme selbst. AUF JEDEN FALL reicht
für die Quersumme ein Byte aus. IMMER!
Selbst eine DWORD-Zahl hat max. 10 Dezimalziffern. Und für
den unwahrscheinlichen Fall, daß alle diese Ziffern = 9
wären (was sie nie sein können), wäre die Quersumme 90 -
also paßt in ein Byte. (Müßte dann was bei 180 sein.)
Die Zahlen mit den größtmöglichen Quersummen sind:
DWORD: 3999999999 (Quersumme =84)
WORD: 59999 (Quersumme =41)
BYTE: 199 (Quersumme =19)
Oder, butterkeks' Vorschlag
Für Zahlen 0 bis 2559 wäre die Zahl mit der größtmöglichen
Quersumme 1999 (Quersumme 28).
Na? Muster erkannt?
Also, meiner Meinung nach - selbst für den
unwahrscheinlichen Fall, daß Du mit QWORDs (64Bit-Zahlen)
rechnest... Die müßten meiner Ansicht nach 20 (oder höchstens
21) Ziffern haben - und selbst deren größte Quersumme paßt
noch in ein Byte. (Müßte dann was bei 180 sein.)
Anmerkung:
Manchmal hilft es, vorher ein wenig die Theorie dahinter
durchzuspinnen. Hab mal irgendwann einen "Primfaktoren-
Zerleger" gebaut und hatte dann so überlegt: Hm. Brauch
ich ja ein Array - wie groß mach ich das - also in
wieviele Primfaktoren kann man eine Zahl einer bestimmten
Größe maximal zerlegen? ... Bis ich auf die Lösung kam - die
übrigens so verblüffend simpel ist, daß selbst ein Idiot
sie implementieren kann...
Hallo,
muss leider zugeben, dass ich nur verstanden habe, das es für das ergebnis genügt es als byte zu deklarieren. eigentlich wollte ich schon mit 16-Bit-Zahlen arbeiten, die zahlen für die berechnung sollten bis 65535 gross sein. das mit den low/high-byte dachte ich mir, dass in low zb. 34 und im high 12 stehen muss, wenn ich sage die zahl ist 1234. muss auch eingestehen, ich bin auch eine niete was das programmieren betriftt:p
@zipperface:
also erstmal (staune schon, daß es noch keiner gesagt hat) :
Gewöhn Dir mal bitte ab, jedesmal den gesamten Beitrag von jemandem zu
quoten, wenn Du ihm antwortest. Der Name reicht völlig - die Beiträge
stehen sowieso alle untereinander (und dann hat man ja alles doppelt) -
Es liest sich einfach bescheiden und machts bissel unübersichtlich. Wenn
Du quoten willst (damit man weiß, auf welche Passage Du Dich beziehst), dann
halt nur den Teil, der defür wichtig ist.
( @cb-team: Sorry für die Hilfsmoderation.)
Das mit dem Byte gilt nur für die Quersumme der Zahlen. Die Zahlen selbst
sollten (und müssen) 16-Bit sein, wenn sie größer als 255 sind (weil Du sie
ja dann nichtmehr in 8 Bit speichern/darstellen kannst).
Low- und High-Byte von 1234 sind nicht 12 und 34! Die 12 erhält man
bei Teilung der Zahl durch 100 - die 34 ist der Teilungsrest. In DEM Fall
gehts. - Aber da man sowieso dauernd durch 10 teilt und die Ziffern ohnehin
einzeln braucht, würde das nur den Code komplizierter machen, ohne
wesentlichen Vorteil. (OK, außer vielleicht den, daß man dann Zahlen bis
25599 benutzen könnte.)
Allerdings rate ich eigentlich davon ab, für jede Erhöhung der
Stellenzahl extra nochmal Programmcode zu generieren. - Will sagen: Wenn man
dann noch ne eine oder zwei 9en anhängen will, teilt man wieder irgendwo
durch 100 oder 1000 - das wird total unordentlich (und auch wohl nicht besser
oder schneller). Da ist es besser, wenn man weiß, daß man so große Zahlen hat,
daß man gleich von Anfang an mit größeren Bereichen arbeitet.
Zu Low-/Highbyte nochmal:
Die Zahlen werden im Computer dann nach 8-Bit geteilt. D.h. Die Zahl liegt
in Binär-schreibweise (Einsen und Nullen) vor und man teilt sie nach 8
"Binärziffern":
1234 ist z.B. Binär: 0 0 0 0 0 1 0 0 1 1 0 1 0 0 1 0 (wenn mans als 16Bit
schreibt).
Das high-Byte sind die "ersten acht", das lowbyte die "letzten acht" Bits
davon.
Also High-Byte = 0 0 0 0 0 1 0 0 (dezimal halt 4)
und Low-Byte = 1 1 0 1 0 0 1 0 (dezimal halt 210)
Zahlen werden in zwei Bytes gespeichert, wenn ein Byte nicht mehr ausreicht.
In einem Byte kann man 256 verschiedene Zahlen speichern, nämlich 0 bis 255.
Wenn man größere Zahlen speichern will, muß man ein Byte dazunehmen - und
das ist das 256-fache "wert".
Wenn Du z.B. eine Zahl im normalen Zehnersystem darstellen willst und hast
nur eine Ziffer, kannst Du damit die Zahlen 0 bis 9 darstellen. Wenn Du eine
größere Zahl darstellen willst, brauchst Du eine weitere Ziffer. Diese ist
dann zehnmal soviel "wert". Wenn Du z.B. 14 schreibst, dann ist das zwar eine
1 und eine 4 - aber die 1 bedeutet ja nicht 1, sondern 10, weil sie an der
zweiten Stelle von rechts steht.
Dein "Denkfehler" bei der Ausgabe ist der:
Erstens: Das Zeichen für die Funktion 02h von INT 21h muß nicht in DI,
sondern in DL übergeben werden. Wichtig.
Zweitens: Die Funktion gibt nur ein einzelnes Zeichen aus - also z.B. eine
Ziffer - Quersummen können aber auch mehrstellig sein.
Drittens: Die Variante mit add DL,30h funzt daher auch nur, wenn die
Quersumme einstellig (also 0 bis 9) ist, denn dadurch erzeugst Du den
ASCII-Code der Ziffern 0 bis 9 (also 30h bis 39h - bzw 48 bis 57 dezimal.
NACH den Ziffern folgen wieder "Zeichen". D.h. selbst wenn Du DL statt DI
benutzt, würde es, wenn die Quersumme z.B. 13 wäre, nicht 13, sondern das
Zeichen = sein. (48+13 halt).
Da Du jedoch nicht DL, sondern DI benutzt hast, liest die
Ausgaberoutine halt das ein, was zufällig grade in DL
steht. Und weil wir ja in dem Beispiel DL als "Konstante
zum Dividieren" (also 10) benutzt haben, wird das
ASCII-Zeichen 10 ausgegeben. Wenn man Pech hat, geht er
also einfach eine Zeile tiefer. Oder er gibt halt das
ASCII-Zeichen 10 aus. Im 437er Zeichensatz wäre das
beispielsweise ein Kästchen mit nem ausgestanzten Kreis.
Oder mit anderen Worten: "irgendso'n Zeichen"...
Noch ne Anmerkung:
Hm... - Ohne Dich nerven zu wollen: Aber wenn man Assembler lernen will, ist
es von großen Vorteil, wenn man erstmal die ganze "Zahlentheorie" lernt -
also auch, wie der Computer Zahlen speichert und so. Sonst macht man sich's
nur unnötig schwer.
butterkeks
21.05.2005, 16:30
die zahlen für die berechnung sollten bis 65535 gross sein.
In diesem Fall musst du einen 16 bit operanden bei der Division verwenden.
Der Divisor kann, wie von Xpyder bereits angemerkt, in diesem Fall nicht mehr in DL stehen, da der Rest in DX landet; musst dir also anders behelfen.
zipperface
23.05.2005, 16:18
Vielen Dank für Euere Tips und Anmerkungen. Habe soweit mein Programm geschrieben mit Ein-u. Ausgabe, habe dies auch mal mit TASM assembliert und habe keine Fehlermeldungen bekommen. Leider habe ich bei der Ausgabe Mist gebaut und es kommt immer nur ein Zeichen heraus (ist aber vorerst egal). Mein Anliegen wäre ein anderes, und zwar ob jemand mal durch mein Listing schauen könnte und mir bestätigen kann, ob so alles in Ordnung ist. Bei der Eingabe habe ich mir gedacht, jedesmal wenn eine Zahl eingegeben wird, soll überprüft werden ob sie zwischen 0 und 9 liegt, soll aus 5 Ziffern bestehen und das es sich auch tatsächlich um eine Zahl handelt.
Start:
;********************************************************************************
;Begruessungstext
;********************************************************************************
mov dx,offset GRUSS
mov ah,09h
int 21h
;*********************************************************************************
;Eingabe der Zahlen
;*********************************************************************************
;---------------------------------------------------------------------------------
;Eingabe der 1. Zahl
;---------------------------------------------------------------------------------
START1:
lea bx,EINGABE1 ;Adresse von EINGABE nach BX
mov al,6 ;Anzahl der max. Zeichen nach AL
mov [bx],al ;Max. Zeichen in 1.Byte von EINGABE
lea dx,EINGABE1 ;Adresse von EINGABE nach DX
mov ah,0AH ;Funktion String-Einlesen
INT 21h
inc bx ;BX inkrementieren
mov ch,[bx] ;Anzahl der eingelesenen Zeichen nach CH
mov LAENGE1,ch
cmp CH,0 ;CH=0 ? Erlaubte Zeichenanzahl erreicht?
je F1 ;Wenn ja gehe zu FERTIG
L1:
inc bx ;BX inkrementieren
mov al,[bx] ;Inhalt von BX nach AL
cmp al,"0" ;Vergleich AL mit '0'
jb F1 ;Wenn AL < '0' gehe zu ENDE
cmp al,"9" ;Vergleich AL mit '9'
ja F1 ;Wenn AL > '9' gehe zu L8
sub al,30h ;AL=AL-30H
dec ch
jne L1
jmp FERTIG1 ;Gehe zu L10
F1:
lea dx,FEHLER ;Adresse von FEHLER nach DX
mov ah,9 ;Funktion: Text ausgeben
int 21h
jmp START1
FERTIG1:
mov ah,02h
mov dl,lf
int 21h
;----------------------------------------------------------------------
;Eingabe der 2. Zahl
;----------------------------------------------------------------------
START2:
lea bx,EINGABE2 ;Adresse von EINGABE nach BX
mov al,6 ;Anzahl der max. Zeichen nach AL
mov [bx],al ;Max. Zeichen in 1.Byte von EINGABE
lea dx,EINGABE2 ;Adresse von EINGABE nach DX
mov ah,0Ah ;Funktion String-Einlesen
int 21h
inc bx ;BX inkrementieren
mov ch,[bx] ;Anzahl der eingelesenen Zeichen nach CH
mov LAENGE2,ch
cmp ch,0 ;CH=0 ?
je F1 ;Wenn ja gehe zu FERTIG
L2:
inc bx ;BX inkrementieren
mov al,[bx] ;Inhalt von BX nach AL
cmp al,"0" ;Vergleich AL mit '0'
jb F2 ;Wenn AL < '0' gehe zu ENDE
cmp al,"9" ;Vergleich AL mit '9'
ja F2 ;Wenn AL > '9' gehe zu L8
sub al,30h ;AL=AL-30H
dec ch
jne L2
jmp FERTIG2 ;Gehe zu L10
F2:
lea dx,FEHLER ;Adresse von FEHLER nach DX
mov ah,9 ;Funktion: Text ausgeben
int 21h
jmp START2
FERTIG2:
mov ah,02h
mov dl,lf
int 21h
;-------------------------------------------------------------------------
;Eingabe der 3. Zahl
;-------------------------------------------------------------------------
START3:
lea bx,EINGABE3 ;Adresse von EINGABE nach BX
mov al,6 ;Anzahl der max. Zeichen nach AL
mov [bx],al ;Max. Zeichen in 1.Byte von EINGABE
lea dx,EINGABE3 ;Adresse von EINGABE nach DX
mov ah,0Ah ;Funktion String-Einlesen
int 21h
inc bx ;BX inkrementieren
mov ch,[bx] ;Anzahl der eingelesenen Zeichen nach CH
mov LAENGE3,ch
cmp ch,0 ;CH=0 ? Erlaubte Zeichenanzahl erreicht?
je F3 ;Wenn ja gehe zu FERTIG
L3:
inc bx ;BX inkrementieren
mov al,[bx] ;Inhalt von BX nach AL
cmp al,"0" ;Vergleich AL mit '0'
jb F3 ;Wenn AL < '0' gehe zu ENDE
cmp al,"9" ;Vergleich AL mit '9'
ja F3 ;Wenn AL > '9' gehe zu L8
sub al,30h ;AL=AL-30H
dec ch
jne L3
jmp FERTIG3 ;Gehe zu L10
F3:
lea dx,FEHLER ;Adresse von FEHLER nach DX
mov ah,9 ;Funktion: Text ausgeben
int 21h
jmp START3
FERTIG3:
mov ah,02h
mov dl,lf
int 21h
;*********************************************************************************
;Berechnung der Quersumme
;*********************************************************************************
mov si, offset ANFANG ;si zeigt nun auf ANFANG (erster Datensatz)
mov di, offset QUER ;di zeigt nun auf QUER (dort landet die Zahl)
mov cx, word ptr ANZAHL ;ANZAHL (anzahl der Datensaetze) nach cx laden
mov dl, 10 ;dl ist nachher der operand fuer die Division
;eine Zahl abarbeiten
nextone:
lodsw ;zuerst wird sie nach ax geladen
mov bl, 0 ;in bl liegt die zwischensumme, deshalb
;wird das register zuerst geleert
; eine Ziffer addieren
digit:
div dl ;ax durch dl (also 10) teilen
add bl, ah ;der Rest der Division (eine Ziffer) liegt in ah.
;dieser wird zu bl addiert
mov ah, 0 ;ah fuer die naechste Division zuruecksetzen
cmp ax, 0 ;ist ax = 0? Dann sind wir fertig...
jnz digit ;...ansonsten addieren wir die nächste Ziffer
cmp bl, [di] ;bl (momentane Quersumme) mit der
;bisher kleinsten, [di], vergleichen
ja skip ;ist bl groesser? Dann lassen wir [di] so stehen...
mov [di], bl ;... ansonsten ist bl der neue kleinste Wert
skip:
loop nextone ;wenn noch nicht alle Zahlen bearbeitet wurden,
;ist die naechste dran
;*****************************************************************************************
;Ausgabe der Quersumme
;*****************************************************************************************
mov ax,0 ;Loeschen des Registers AX
mov dx,0 ;Loeschen des Registers DX
mov dx, offset Meldung ;Ausgabe des Textes "Meldung"
mov ah,09h
int 21h
add QUER,30h ;Vorbereiten fuer ASCII Ausgabe
mov bl,QUER ;Ausgabe der Quersumme
mov ah,02h
int 21h
;*****************************************************************************************
; Programmende
;*****************************************************************************************
mov al,0 ;Fehlercode 0
mov ah,4Ch ;Funktion: Programm verlassen
int 21h ;Schnittstelle zu DOS
;*****************************************************************************************
;Datenbereich
;*****************************************************************************************
ANFANG DW ?,?,?,?
ANZAHL DW 3
EINGABE DB 7 dup(?)
EINGABE2 DB 7 dup(?)
EINGABE3 DB 7 dup(?)
LAENGE DB ?
LAENGE2 DB ?
LAENGE3 DB ?
QUER DB 255
GRUSS DB "Das folgende Programm berechnet die Quersumme einer 16-Bit-Zahlenfolge",Cr,Lf,"$"
Meldung DB "Die errechnete Quersumme lautet:",Cr,Lf,"$"
_TEXT ends ;Codesegment schlieáen
end Start ;Ende des Quellprogramms
Wie gesagt, beim assemblieren hatte ich keine Fehlermeldung, und sollte so auch funktionieren.
Für weitere Tips, oder auch Rüge, währe ich sehr dankbar.
vBulletin® v3.8.6, Copyright ©2000-2012, Jelsoft Enterprises Ltd.