Archiv verlassen und diese Seite im Standarddesign anzeigen : Registerwert als Zahl ausgeben
Hallo Leute,
ich beginne gerade etwas mit ASM rumzuprobieren und habe da auch gleich die erste Frage:
Wenn ich in einem Register, sagen wir das Register ax, einen Zahl, sagen wir 254, stehen habe, wie bekomme ich die als Zahl 254 auf dem Bildschirm angezeigt? Ich möchte keinerlei DOS- bzw. Windowsfunktionen verwenden.
Übrigens ich benutze nasmw
Du musst die '2' '5' und '4' an eine Stelle deiner Wahl (je 2 byte, 1 für Farbe) in den Bildspeicher (ab B800:0000) schreiben (DOS).
Danke für deine Antwort, Scavi.
Das war mir schon klar, was mir fehlt ist das Zerlegen der Zahl. Was vieleicht bei meiner Frage nicht ganz klar ausgedrückt ist, die Zahl kann sich ändern.
Mein möglicher Weg wäre:
Teiler = 100
Zahl durch Teiler teilen
Ganzzahliges Ergebnis mit dem ASCII-Code für die 0 addieren
ausgeben
Ganzahligen Ergebnis * Teiler - Zahl = Restzahl
Teiler = Teiler / 10
wieder bei 1. anfangen solange Restzahl nicht gleich 0
So würde ich das machen. Aber ich dachte es gibt einen einfacheren Weg.
MfG
Oh Sorry bei 5. muss es
Zahl - (Ganzahligen Ergebnis * Teiler) = Restzahl
heissen.
Sorry
kuechi
Wie wäre es mit MODULO 10? ;)
Teste ich ggf. heute abend mal
Danke
kuechi
Das Gute an Assembler ist (und kenne keine Hochsprache,
die das nutzt - leider!), daß Ergebnis und "Rest" beide
gleichzeitig mit einer Operation ermittelt werden:
mov CX,10 ;10 (als "Teiler")
mov AX,zahl ;die darzustellende Zahl
div CX ;durch CX (also in dem Fall 10) teilen
Danach liegt in AX der Wert AX/10, in DX der Teilungsrest
(also 0-9). D.h. man braucht nur
or DL,48
rechnen (0-9 sind halt ASCII 48-57) und kann die Ziffer
darstellen. (Bitte beachten: Auf manchen Assemblern muß man
nicht:
div CX
sondern:
div AX,CX
schreiben! Warum man es manchmal weglassen kann, liegt
daran, daß Multiplikationen/Divisionen NUR mit AX und DX
funktionieren.
Das gute an der Methode ist, daß in AX nun gleich der
neue Wert (also /10) steht, man braucht nur wieder
zurückzuspringen zu "div CX" und kann die nächste Ziffer
ermitteln.
Was Du sicher schon gemerkt hast: Die Ziffern werden hierbei
"von hinten" ermittelt.
Man ist übrigens "fertig", wenn AX=0 wird.
Das ist die 16-Bit-Variante!
Man kann das ganze auch mit AL machen, dann muß der
Divisor ebenfalls 8 Bit sein (dann also nur durch CL
teilen!).
Daran, daß man
div CL
schreibt, erkennt der Assembler automatisch, daß es 8-Bit
sein soll (weil CL ein 8-Bit-Register ist).
Das Ergebnis steht dann in AL, der "Rest" in AH.
Das wäre dann die 8-Bit-Variante.
Die 32-Bit-Variante funktioniert wie die 16-Bit-Variante,
nur daß statt der Register AX, CX und DX die Register
EAX,ECX und EDX (also die entpsrechenden 32-Bit-Register)
benutzt werden.
Der Divisor muß nicht CX sein! Es ist jedes Register,
jeder feste Wert und auch Speicherstellen erlaubt. Man
sollte nur sicherstellen, daß der Assembler richtig
erkennt, welche Operation man nutzen will.
Schreibt man
div 10
weiß der Assembler nicht, ob die 10 als Byte (8Bit), Word
(16Bit) oder DWord (32Bit) zu interpretieren ist. Man kann
dies dann "festlegen" - allerdings unterscheiden sich die
Schreibweisen für so ein "Operator Size Override" zwischen
den Assemblerprogrammen. (Interpretiert er sie als 8Bit,
nimmt er automatisch AL/AH - was bedeutet, daß der
Dividend (also die Zahl, die man teilen will) nicht größer
als 255 werden kann.
Davon abgesehen: Vorher die 10 in ein Register laden und
dann immer durch das Register zu teilen, wird schneller
ausgeführt. (Wenn man die Wahl hat, mit Registern, festen
Werten oder Speicherstellen zu arbeiten, sollte man sich
in dieser Reihenfolge entscheiden. Also: Am besten
Register. Geht das nicht, fester Wert. Geht das auch
nicht, Speicherstelle.)
(Ich für meinen Teil arbeite oft mit "festen Werten", die
veränderlich sind. Schließlich steht das Programm ja auch
im Speicher - d.h. der "feste Wert" steht in einer
Speicherstelle und ist somit veränderlich. Im Gegensatz zu
8-Bit-CPUs muß man hier jedoch einiges beachten - deswegen
ist die Methode für Anfänger nicht ganz so zu empfehlen,
da hierbei Fehler auftreten können, die sich manchem nicht
sofort erschließen. - Stichwort "Prefetch Queue")
Aber für genanntes Problem ist die Speicherstelle sowieso
die beste Wahl.
Achja, speichern passiert dann z.B. so:
man lädt z.B. nach DI den Offset der Speicherstelle, an
die die Einer-Ziffer soll und für jede neue Ziffer
verringert man diesen um 2. Gibt noch andere Methoden,
findeste sicher alleine raus.
Achja, nochwas:
Es gibt auch noch den Befehl idiv. Dieser teilt auch,
erhält aber das "Vorzeichen". Für eine Zahlenausgabe ist
dies jedoch eher nicht zu gebrauchen. Wenn man also sowas
wie "negative Zahlen" unbedingt haben muß, sollte man:
Vorher das oberste Bit der Zahl prüfen (am besten: or
AX,AX - weil dann das "negative flag" gesetzt wird und es
schneller ist als test AX,$8000).
Wenn "negativ" nicht gesetzt ist, dann normal
weitermachen. Wenn gesetzt, dann mit neg AX die Zahl
"umkehren" und es sich irgendwo merken, damit man am
Schluß das Minuszeichen setzen kann.
Letzte Anmerkung: Es gibt auch Methoden, wie man mit
Hilfe des "Dezimal-Modus" der CPU sowas simulieren kann,
um "billig" zu der Teilung zu kommen. Dafür wird der
Befehl AAA mißbraucht. (Hat nix mit Freimaurern zu tun...
- sondern bedeutet "Adjust After Addition")
Allerdings ist die Anwendungsmöglichkeit hier ziemlich
begrenzt - man kann nicht alle Zahlen darstellen, sondern
ist auf 2 Ziffern beschränkt - also eine Zahl von 0 bis
99, die in AL steht.
xor AH,AH ; AH auf 0 setzen, ist notwendig für AAA
add AH ; Null addieren (praktischerweise in AH)
aaa ; Der mysteriöse Zauberbefehl...
Danach steht in AH die Zehnerstelle und in AL die
Einerstelle. Wie gesagt - da ziemlich beschränkt nutzbar,
würd ich das nicht so machen - kann aber auch sein, daß
Dir das reicht (d.h. Deine Zahlen nicht größer als 99
werden).
Das mit dem Addieren dient dazu, das "auxiliary Flag" zu
löschen (das ist so'n Halb-überlauf an der 4-Bit-Stelle).
Denn schließlich heißt der Befehl "Adjust after addition"
- dient also eigentlich dazu, das Ergebnis zu fixen, nachdem
man zwei "BCD" Zahlen ("Ziffern" 0-9, kein A-F) addiert
hat. (Gibt btw auch noch den AAM-Befehl ("Adjust after
Multiplication")... Der Möglichkeiten gibts viele...)
(Achja, bevor die Frage kommt: xor AH,AH - so kann man
Register auf 0 setzen. Es ist schneller als mov AH,0.
Klingt komisch - is aber so.)
Hoffe, das war jetzt nicht zuviel auf einmal.
Danach liegt in AX der Wert AX/10, in DX der Teilungsrest
(also 0-9). D.h. man braucht nur
or DL,48
rechnen (0-9 sind halt ASCII 48-57) und kann die Ziffer
darstellen.
Ich hätte den ADD Befehl genommen, wenn ich zu 48 eine Zahl hinzu addieren will. Da sich der Wert nur zwischen 0 und 9 bewegt, sind die Ergebnisse natürlich die selben, es sieht im ersten Moment nur etwas komisch aus. Hat die Oder-Verknüpfung denn einen Vorteil gegen der Addition (z.B. weniger Taktzyklen)?
Ja, ist so'ne Angewohnheit.
Dacht mir schon, daß einer fragt.
Weil die Ziffern halt gerade so günstig im ASCII-Code liegen,
daß man OR benutzen kann, benutze ich halt OR. (Bzw: Bin
überzeugt, daß die Entwickler des ASCII auch genau solche
Dinge im Sinn hatten - sieht man ja auch unter anderem an den
Codes für Buchstaben und einigen anderen Dingen.)
Ist so'ne Angewohnheit: OR ist ein "weniger komplizierter"
Befehl als ADD (weil keine "Übertrag" Bits usw) und wenn
ich die Wahl hab, nehm ich instinktiv den "einfacheren"
Befehl von beiden, weil es auf manchen CPUs IN DER TAT
Taktzyklen spart.
Um also Deine Frage zu beantworten, ob es Vorteile hat:
1.) Ja, unter Umständen hat es das.
2.) Selbst wenn nicht, kanns auf keinen Fall schaden.
3.) Wenn man sich so "Reflexe" angewöhnt, wird man schneller
mit Assembler klarkommen. Denn, daß einer Assembler codet,
heißt ja, daß ihm Hochsprachen wohl zu langsam sind (womit
er recht hätte) und dementsprechend kanns ja nicht schaden,
"schlechte Angewohnheiten" von Hochsprachen gleich zu
Anfang abzulegen.
Ein Beispiel, was ich meine:
Wenn ich z.B. eine ganze Zahl mit 2 multiplizieren will,
habe ich eigentlich 4 Möglichkeiten (angenommen, die Zahl
läge in der Variablen a):
(Code in einer "allgemeinen-Phantasie-Hochsprache", um hier
keine spezifische zu nennen)
1.) a=a*2 (mit 2 multiplizieren)
2.) a=a+a (zu sich selbst addieren)
3.) inc(a,a) (zu sich selbst addieren - "optimiert")
4.) a=a shl 1 (um ein Bit nach links schieben)
Und wenn man die Wahl hat (und in Assembler hat man die
glücklicherweise - im Gegensatz zu manchen Hochsprachen!)
sollte man sich erst für 4.), wenn das nicht geht, für 3.)
usw... - und erst zuletzt für 1.) entscheiden.
Manche Hochsprachen sind leider so systemfern, daß sie
einem diese Wahl nicht mehr lassen. (Und in manchen
Hochsprachen muß man Bitverschiebungen und Bitmasken
dermaßen umständlich implementieren, wenn man sie braucht,
daß der daraus EIGENTLICH entstehende Nutzen/Vorteil
natürlich hinfällig wird.)
Ich bin halt so der Meinung: Klar, man MUß heutzutage nicht
mehr wissen, wie ein Computer im Prinzip funktioniert, um
programmieren zu können. Aber schaden kann's auch nicht.
Tschuldigung - ist vielleicht wieder ein wenig lang geworden.
vBulletin® v3.8.6, Copyright ©2000-2012, Jelsoft Enterprises Ltd.