Frage deutsch
~~~~~~~~~~~~~~~
Was ist ein Assembler und wie verwende ich ihn in QB?
 

Question English
~~~~~~~~~~~~~~~
What is an assembler and assembly language? How to use it in QB?
 

Antwort 1
~~~~~~~~~
[ von Thomas Antoni und BufferUnderrun, 11.1999 - 24.2.2006 ]
 
*** Was ist ein Assembler?
Als 'Assembler' werden zwei Dinge bezeichnet:
Eine Programmiersprache
Ein Übersetzungsprogramm
 
1. Ein -> Assembler ist eine niedrige (also systemnahe) Programmiersprache, bei der jeder Befehl in einen direkt von der CPU ausführbareren -> Maschinenbefehl umgesetzt wird. Für jeden Maschinenbefehl wird in der Assemblkersprache eine Abkürzung oder mnemonischer (von Menschen lesbarer und merkbarer) Code verwendet. Für die Speicheradressen und Sprungadressen kann man ebenfalls beliebige mnemotechnische Namen definieren. Der Anwender kommt also nicht mit dem Binären Maschinencode in Berührung, kann aber trotzdem hardwarenah und effektiv programmieren.
 
Statt der bei einer höheren Programmiersprache - z.B. QBasic - erlaubten Anweisung
anna = egon + otto
 
muß man in einer Assemblesprache so etwas kompliziertes hinschreiben wie
MOV A, otto 'Inhalt der Speicherzelle OTTO ins A-Register transferieren
ADD A, egon 'Inhalt der Seicherzelle egon zum Register A hinzuaddieren
MOV anna, A 'Register A in die Speicherzelle anna transferieren
 
Zur Umwandlung der Assembler-Sprache in die ->
Maschinensprache dient ein Assembler. Die Assemblersprache hängt naturgemäß vom eingesetzten Prozessor ab und kann nur Code für diesen erzeugen. Die Vorteile beim Einsatz einer Assembler-Sprache sind u.a. eine höhere Geschwindigkeit und die Möglichkeit, direkt auf die Hardware des Systems zugreifen zu können.
 
2. Außerdem bezeichnet man mit Assembler ein Übersetzungsprogramm, das in einer Assembler-Sprache geschriebene Programme (diese bestehend aus ausgeschriebenen Befehlsnamen und Bezeichnern und sind daher vom Programmierer leicht zu verstehen) in ein ausführbares "("binäres") Maschinenspracheprogramm umwandelt.
 
*** Lohnt es sich, Assembler zu lernen?
Tu mir den Gefallen und fange nicht an, Assembler 86 zu lernen. Dies ist bei der "Barockarchitektur" der xx86-Prozessoren eine Strafe für einen, der Vater und Mutter erschlagen hat. 95% aller Programmierer, die Assembler-Programme in Ihre QuickBasic-Programme einbauen, verwenden fertige Bibliotheken oder basteln vorhandene Assembler-Programme für ihre Anwendungen zurecht.
 
*** Wo finde ich Support zur Assemblerprogrammierung im Internet?
Sehr gute Assemblerbibliotheken für QuickBasic kannst Du z.B. auf den folgenden Webseiten downloaden :
Future Software www.qb45.com
www.qbasic.de in der Rubrik "QBasic -> Download -> Libraries"
 
Wer einen Einblick in die Assemblerprogrammierung erhalten will, dem sei das gute deutsche Tutorial "The Real Adok's Way to Assembler". Es steht auf http://www.qbasic.de in der Rubrik "QBasic -> Tutorials " zum Download bereit.
 
Viel englisches Material zur Assemblerprogrammierung findest Du auf
http://www.programmersheaven.com/ .
 
*** Wo kann ich einen kostenlosen Assembler herunterladen?
Es gibt eine Reihe von sehr guten Freeware-Assemblern, etwa
NASM - Ein guter Freeware-Assembler ist . Du kannst ihn auf http://sourceforge.net/projects/nasm undherunterladen oder such mal mit der Suchmaschine Google danach!
A86 - A86 ist ein sehr leistungsfähiger kleiner Freeware-Assembler von Eric Isaacson. Download auf www.qbasic.de unter "Download -> Tools" und auf Erics Seite http://eji.com/a86 .
MASM - MASM ist der offizielle Microsoft ASseMbler, der für Profis kostenlos auf der Microsoft-Webseite zur Verfügung steht, jedoch mit einer sehr hakeligen Installation. Bequemer geht es über www.easystreet.com/~jkirwan/pctools.htm .
Assembler ASM 2.11 von A.Olejko (Download einer Testversion auf http://assembler86.de und www.olejko.de
WASM - Wolfware Assembler (http://wasm.port5.com/ und www.uv.tietgen.dk/staff/mlha/Download/DOS/ )
 
Auf der folgenden Webseite findest Du diverse kostenlose Assembler sowie diverse Tools und Tutorials zur Assemblerprogrammierung zum herunterladen:
http://personal5.iddeo.es/ret007ow/tools.html
www.robsite.de/programme.php?prog=assembler
 
 
*** Wo kann ich einen kostenlosen Disassembler herunterladen?
Ein -> Disassemnler erzeugt aus den 0/1-Kombinationen des -> Maschinencodes wieder lesbaren Assemblercode. DOS und Windows haben bereits einen kleinen kostenlosen Disassembler eingebaut; den Du mit dem Kommando "DEBUG" aufrufen kannst. Siehe dazu den untenstehenden Abschnitt "Assemblieren mit dem DOS-Kommando DEBUG".
 
Einen Freeware-Disassembler gibt es z.B. auf
www.geocities.com/~sangcho/disasm.html .
 
Der bei Profi-Programmierern und -Hackern mit Sicherheit beliebteste Disassembler ist IDA ("Intelligent Disassembler"; siehe
www.datarescue.com/idabase ). Der kostet aber leider ca. 300 EUR.
 
 
*** Welche Tools gibt es für die Assemblerprogrammierung?
Um Assembler-Code in BASIC einzubinden ist das Programm ASM2BAS eine große Hilfe. Es schreibt eine BAS-Datei, mit dem ASM-Code fertig in die QBasic-Interrupt-Calls integriert wird. Download z.B. auf www.qbasic.de unter "Download -> Tools"
 
Um BASIC- in Assembler-Quellspracheprogramme umzuwandeln, kann man
BASM verwenden. BASM ist nicht 100%ig kompatibel zu QBasic. Aber man muß nur wenige Modifikationen an seinem Programm vornehmen. Download z.B. auf www.qbasic.de unter "Download -> Tools" und http://shu.emuunlim.com/files.html .
 
 
*** Was für einen Assemblercode setzt QuickBasic für mein Programm ab?
Kaum jemand weiß, dass QuickBasic den Assemblercode des beim Compileren erzeugten Objektprogramms in eine lesbare Datei ausgeben kann. Verwende dazu den Befehlszeilen-Compiler BC mit der Option /a wie folgt:
BC meinprog.bas /a
 
Gib in dem dann folgenden Dialog an der Stelle "Quell-Listing [NUL.LST]:" den Namen der Text-Datei an, in der das Assemblerlisting hinterlegt werden soll, z.B. "meinprog.asm". In diesem Listing ist dann fein säuberlich für jeden QB-Befehl die dafür abgesetzte Assembler-Befehlssequenz angegeben.
 
 
*** Die "Assembler Story" von BufferUnderrun
BufferUnderrun kann sehr kurzweilig über Assembler erzählen. Das hört sich dann so an:
... ähh, da muss ich wohl weiter ausholen...
 
Also am Anfang war das Chaos, und die Programmierer mussten Computer direkt im Maschinencode programmieren, d.h. den HEX-Salat, den Du siehst, wenn du eine EXE- oder COM-Datei in einen HEX-Editor lädst (die Computer hatten damals auch nur 16 Tasten: 0123456789ABCDEF).
 
Und so begab es sich, dass es eine bessere Lösung zu finden galt, mit der man auch richtige Programme schreiben kann, nämlich ...
>> ASSEMBLER >>
 
"To assemble" heisst soviel wie "zusammenfügen". Assembler ist also eine Programmiersprache eine Stufe über der Maschinensprache. Sie ist (im Gegensatz zu höheren Sprachen) direkt an den verwendeten Prozessor angepasst. Es kann daher durchaus vorkommen, dass ein in Assembler geschriebenes Prog auf dem Rechner deines Freundes gar nicht oder nicht richtig läuft.
 
In Assembler gibt es keine Befehle wie PRINT o. ä. sondern nur ganz primitive Befehle (wie tausche Wert von Variable 1 gegen den von Variable 2; oder "Benutze Variable mit Interrupt soundso; oder wenn Variable 1 = Variable 2 springe 10 Zeilen weiter ......).
Beispiele:
je ax, bx = Jump if Equal (Springe, wenn Register ax = Register bx)
jne ax, bx = Jump if Not Equal (Springe, wenn Register ax ungleich Register bx)
...
 
Um in Assembler programmieren zu können muss man einiges über das Innenleben eines Rechners wissen, da man Speicheradressierungen und solche Dinge eben selber machen muss und nicht alles - wie bei BASIC - vorgekaut bekommt.
 
Der Vorteil eines Assembler-Progs ist seine enorm geringe Grösse und die extrem hohe Geschwindigkeit, weshalb früher fast nur so programmiert wurde (Speicherplatz war extrem teuer und die Rechner schneckenlahm).

Du darfst Dir Assembler natürlich nicht wie QBasic vorstellen. Es gibt keinen Interpreter oder so. Der im Texteditor geschriebene .ASM File wird mit einem Assembler (Turbo-Assembler etc.) in einen OBJ-File kompiliert und dann mit einem Linker in eine COM oder EXE verwandelt. Du kannst auch einfach DEBUG (gib einfach mal DEBUG in die Eingabeaufforderung ein ...) benutzen. Wenn du dort dein Assembler-Programm eintippst bekommst du den Maschinencode ausgespuckt.
 
Heute benutzt man Assembler normalerweise nur, um besonders hardwarenah zu programmieren (also u.a. Treiber - für Drucker z.B. oder auch Maus, Grafikkarte, Soundkarte....). Denn Speicherplatz ist im Base-Memory-Bereich (die unteren 640K wo unter DOS alle Treiber hingeladen werden) noch immer rar, und ausserdem lässt sich mit einer höheren Programmiersprache nicht so hardwarenah programmieren.
 
Aus obigen Gründen ist Assembler natürlich auch DIE Sprache, wenn es ums Schreiben von Viren geht :)
 
Im Internet gibts tausende von Tutorials über ASM. Zwei besonders gute findest Du oben; also suche und lerne!
HaveFun - BufferUnderrun
 
*** Wo erhalte ich mehr Informationen zur Assemblerprogrammierung im Internet?
Da gibt es einige gute Seiten, z.B.
http://www.geocities.com/assembler86/ - gute deutsche Assemblerseite von Niels Danylak , besonders für Einsteiger geeignet.
 
 
*** Assemblieren mit dem DOS-Kommando DEBUG
DOS und Windows haben einen kleinen kostenlosen Assembler bereits eingebaut. Du kannst ihn mit dem Befehl DEBUG am DOS-Prompt bzw. der Eingabeaufforderung aufrufen.
 

Erläuterung des DEBUG-Befehls
Den folgenden Hilfe-Text zu DEBUG erhältst Du, wenn Du in Windows 95 am DOS-Prompt
DEBUG /? eingibst:
DEBUG startet das Programm DEBUG, ein Programm zum Testen und zur
Fehlersuche für ausführbare Dateien.
DEBUG [[Laufwerk:][Pfad]Dateiname [Testparameter]]
- [Laufwerk:][Pfad]Dateiname Zu testende Datei.
- Testparameter Befehlszeilenparameter, die die
zu testende Datei fordert.
Assemblieren.....A [Adresse]
Vergleichen......C Bereich Adresse
Anzeigen ........D [Bereich]
Eingeben.........E Adresse [Liste]
Füllen...........F Bereich Liste
Starten .........G [=Adresse] [Adressen]
Hex rechnen......H Wert1 Wert2
Einlesen.........I E/A-Anschluß
Laden............L [Adresse] [Laufwerk] [ErsterSektor] [Anzahl]
Verschieben......M Bereich Adresse
Benennen.........N [Pfadname] [Argumentenliste]
Ausgeben.........O E/A-Anschluß Byte
Ausführen........P [=Adresse] [Anzahl]
Beenden..........Q
Registeranz......R [Register]
Suchen...........S Bereichsliste
Verfolgen........T [=Adresse] [Wert]
Deassemblieren...U [Bereich]
Schreiben........W [Adresse] [Laufwerk] [ErsterSektor] [Anzahl]
Expansionsspeicher reservieren .....XA [Seitenzahl]
Expansionsspeicher freigeben........XD [Zugriffsnummer]
Expansionsspeicher zuordnen.........XM [LSeite] [PSeite] [Zugriffsnummer]
Expansionsspeicherstatus anzeigen...XS
 

Kleines Anwendungsbeispiel für DEBUG
Dieses kleine Beipiel zeigt, wie Du den Assembler-Befehl
MOV ah, 4c mit DEBUG assemblieren, also in Maschinensprache umwandeln kannst. Wie Du am Beispiel siehst, ergibt dieser Assemblerbefehl die beiden -> Maschinensprache-Bytes B44C im Hexadezimalcode (als -> Hexadezimalzahlen ). Die Anwendereingaben sind rot dargestellt.
 
[Win95!]D:\WIN95>debug ;DEBUG starten
-
a ;Assembler aufrufen (Hilfe mit ? anforderbar)
2EA6:0100
mov ah, 4c ;Asemblerbefehl eingeben
-
u 2EA6:0100 ;Befehl disassemblieren
2EA6:0100 B44C MOV AH,4C
...
-
q ;DEBUG beenden
[Win95!]D:\WIN95>
 
 
 

Antwort 2
~~~~~~~~~
[ von Tobias Doerffel ("todo"; mailtodo*web.de), per Mail, 29.4.02 ]
.
Ein Assembler ist ein Programm, das die sog. Mnemonics, in das Binärformat "übersetzt". Übersetzen kann man es eigentlich nicht nennen, da er lediglich für jedes Mnemomic sein Binärpendant einsetzt. Ein Mnemomic ist eine Bezeichnung für einen Prozessorbefehl. Ein solcher Befehl ist normalerweise in Nullen und Einsen codiert. Da heute kein Mensch mehr so programmieren möchte (irgendwelche Nullen und Einsen eingeben), hat man für die binären Bitmuster, die einen Befehl darstellen, kurze Namen/Bezeichnungen eingeführt.
 
Der Assembler ersetzt also die Mnemomics durch Bitmuster. Das Programmieren mit Assembler stellt somit die unterste Ebene der Programmierung (des Prozessors) dar. Jedes Hochsprachen-Programm wird am Ende in viele solche Assemblerbefehle übersetzt. Die Assemblerprogrammierung hat den Vorteil, dass man da den Prozessor/PC wirklich direkt programmiert und es wird kein Byte mehr erzeugt, als man will (was man bei einer Hochsprache nicht beeinflussen kann). Und hier zu optimieren macht oft viel Sinn. Man kann Prozessor-Register statt Variablen verwenden, man kann richtigen 32 Bit-Code erzeugen uvm. Assemblerprogrammierung hat aber auch den entscheidenden Nachteil, dass sie wirklich auf unterster Ebene arbeitet. Das heißt, von komfortablen Befehlen wie PRINT
 
*** Was ist der Unterschied zwischen einem "normalen" und einem 32-Bit-Assembler?
Ein 32-Bit-Assembler kann auch 32-Bit Code erzeugen und unterstützt den Protected Mode. 32-Bit-Code meint, alle Operationen können mit 32 Bit durchgeführt werden. Desweiteren sind (fast) alle Prozessorregister 32 Bit (z.B. EAx) Desweiteren gibt es auch ein erweiteres Instruktions-Set, d.h. es gibt neue Befehle. Alle Prozessoren ab dem 386 unterstützen 32 Bit-Code.
 
*** Wozu braucht man eigentlich einen Assembler, welche Dinge kann man nur in Assembler sinnvoll programmieren? Wie tut man dies? Wo bekommt man darüber mehr Informationen? (BIOS-Aufrufe, Interrupts, Treiber)
Assembler ist vorallem sinnvoll, wenn man sehr systemnah programmieren und/oder extrem schnelle Routinen schreiben will. Mit unter ist eine Assembler-Routine 100 mal schneller als das Pendant in QB. Es gibt jedoch keine Programme, die man nicht auch mit anderen Hochsprachen schreiben kann. Bei TSR-Programmierung empfehle ich trotzdem wärmstens, auf Assembler zurück zugreifen. Das Ergebnis ist ein TSR-Programm, das sehr wenig Speicher benötigt (und sehr schnell ist)!
Wenn ich etwas Größeres in Assembler programmiere, dann schreibe ich das Programm meist erst einmal mit QB oder C/C++. Danach übersetzte ich Zeile für Zeile nach Assembler. Denn spätenstens bei dem folgenden Beispiel würde ich sagen, geht es ohne Zwischenstufe nicht!
for (k = (-Height + 1) << 6; k < (Height << 6); i += 320, k += 64)
pokeb (pWorkPage, i, peekb(pTexturePage, l + ((64 - k / j) << 7)));
 
(Das ist ein Teil meiner Raycast-Engine (Zeichnen)) Das, was dann in Assembler herausgekommen ist sieht wie folgt aus:
 
StartRender:
Mov Cx,Height
Mov Ax,Cx
Neg Ax
Inc Ax
ShL Ax,6
Mov k,Ax
ShL Cx,6
Push pTexturePage
Pop Es
Push pWorkPage
Pop Fs
StartFor:
Xor Dx,Dx
Test Ax,65535
JNS NoNeg
Or Dx,65535
NoNeg:
Push Ax
IDiv j
Mov Bx,64
Sub Bx,Ax
ShL Bx,7
Add Bx,l
Mov Si,Bx
Mov Al,Es:[Si]
Mov Fs:[Di],Al
Pop Ax
Add Di,320
Add Ax,64
Cmp Ax,Cx
JL StartFor
Ret
 
Und das schafft niemand, ohne vorher einen Entwurf zu schreiben! Also, ein wichtiger Schritt ist erst einmal ein Entwurf in einer Hochsprache, wenn es nicht etwas ganz einfaches ist. Und dieser Entwurf muss natürlich auch funktionieren, sonst bringt das alles nichts.
 
Der zweite Schritt ist dann das Zeile für Zeile des Hochsprachen-Codes in Assembler-Anweisungen übersetzen oder, wenn man keinen Entwurf geschrieben hat, den Code so schreiben.
Der dritte Schritt ist das ausgiebige Testen, des geschriebenen. Wenn dann alles klappt, kann es gleich zu Schritt 5 gehen, ansonsten muss man erst noch Schritt 4 durchlaufen, welcher nämlich im Debugging/Fehlersuche besteht. Das ist bei Assembler ist ganz so einfach, aber es ist natürlich möglich. Als erstes sollte man die Stellen, wo man selbst denkt, dass da irgendetwas falsch ist checken. Wenn alles nichts hilft, muss man halt wieder zu Schritt 2 gehen, evtl. sogar Schritt 1.
 
Wenn man dann bis hier her doch alles geschafft hat, ist der letzte Schritt, wie auch bei Hochsprachen, das Optimieren. Viel optimieren lässt sich vor allem, wenn man statt Variablen, die Register des Prozessors benutzt, da diese um ein Vielfaches schneller sind. Selbst bei den Registern ist es oft nicht von Unbelangen, welche Register man verwendet. Am besten ist es immer, so viel wie möglich das AX/EAX-Register zu verwenden, da dieses bei vielen Operationen am schnellsten ist. Es sind aber auch so Kleinigkeiten, wie statt Add xx,1 Inc xx zu verwenden, wobei xx hier für ein Register oder eine Speicherstelle (Variable) steht. Genauso beim Gegenstück. Statt Sub xx,1 sollte man Dec xx verwenden. Aber auch Mov xx,0 ist jedem guten Assemblerprogrammierer ein Dorn im Auge. Das lässt sich viel schneller und eleganter lösen, und zwar mit Xor xx,xx, wobei es sich hier bei xx nur um ein Prozessorregister handeln kann/darf.
 
Es gibt noch eine Menge weiterer solcher Optimierungen, auf die jetzt aber nicht weiter eingegangen werden soll. Ich habe ein paar Texte, über das Optimieren, welche ich auch gern mailen würde, wenn Interesse besteht. (einfach eine Mail schicken an
todosoft*gmx.de). Was Informationen zur Assemblerprogrammierung angeht, kann ich die Seite www.datasource.de wärmstens empfehlen. Unter "Programmierung" findet sich alles, was das Programmierherz höher schlagen lässt!
 
*** Wo kann man gute Assembler und Assemblerprogramme downloaden?
Bald auf meiner zukünftigen Website, die vorraussichtlich unter www.todosystems.de erreichbar sein wird (Sommer 2002).
 
*** Wo kann man einen Disassembler downloaden?
Assembler, Disassembler, Debugger usw. gibt es unter www.programmersheaven.com.
 
*** Lohnt es sich, Assembler zu lernen?
Wenn man systemnah programmieren will/programmiert auf alle Fälle. Wenn man Programme mit einer hohen Performance schreiben möchte ebenfalls.
 
Und wenn man mit QB arbeitet, ist es auch empfehlenswert, da QB nicht die schnellste Sprache mit dem besten Code ist. Bei meinen C/C++ Programmen ist immer Assembler dabei, oft zwar "nur" Inline-Assembler, aber Assembler ist Assembler. Assembler gehört meiner Meinung einfach dazu. Ich habe z.B. schon einen Euro-Treiber für DOS geschrieben, der weniger als 400 Byte Speicher benötigt (ich könnte den Speicherverbrauch auch noch auf 100-150 Byte senken, wenn ich den PSP mit Programmcode überschreiben würde)! Vollständig zu lernen braucht man es trotzdem nicht. Zwei Drittel der Befehle reicht vollkommen aus. Die wichtigsten, die man braucht, habe ich hier einmal zusammengefasst:
Add/Sub/Mul/IMul/Div/IDiv
Call
ClD
Cmp
Inc/Dec
In/Out
Int
Jmp/JE/JnE/JZ/JnZ/JA/JAE/JL/JLE
Mov
Neg
And/Or/Xor/Not
Pop/PopA/PopF/Push/PushA/PushF
Rep MovSB/MovSW/MovSD
Ret/IRet/RetF
ShL/ShR
Test
 
Mit diesem Instruktions-Set kann man schon ganz ordentliche Programme/Programmteile schreiben! Andere braucht man eher selten.
 
*** Welche empfehlenswerte Tutorials gibt es im Internet über die Assemblerprogrammierung?
z.B. folgende Adressen könnten interessant sein:
www.avr-asm-tutorial.net/avr_de/index.html
www.sundancerinc.de/archiv/coding/pc/lang/asm/wirthi/
www.pronix.de/misc/assembler.htm
http://217.68.164.152/qmystic/help-tutorial-asm-1.html
"The Real Adok's Way to Assembler" , downloadbar auf http://www.qbasic.de in der Rubrik "Tutorials"
 
 
*** Welche empfehlenswerte Bücher gibt es über die Assemblerprogrammierung?
 

Sven Letzel/Rene Meyer: MASM - der Makroassembler von Microsoft
Dieses Buch kann ich für alle empfehlen, die keine trockene Fachliteratur lesen wollen. Dieses Buch ist in einer sehr lockeren Art geschrieben und vermittelt trotzdem wichtige Grundlagen und viel Fachwissen. Es wird jedes Thema der Assemblerprogrammierung besprochen, von der TSR-Programmierung, über die Soundkarte/den PC-Speaker bis hin zur Coprozessorprogrammierung.
 

Trutz Eyke Podschun: Das Assembler-Buch - Grundlagen und Hochsprachenoptimierung
In diesem 1000 Seiten umfassenden Buch (bitte nicht erschrecken!) gibt es alles aber auch wirlich alles, was man über Assembler wissen muss. Es wird detailliert auf die Coprozessorprogrammierung eingegangen. Genauso ausführlich wird der Protected Mode abgehandelt. Der Schwerpunkt des Buches liegt jedoch in der Verwendung von Assembler in Verbindung mit Hochsprachen und der Optimierung dieser durch Assembler. Also genau das Richtige! Es werden die Unterschiede zwischen C/C++ und PASCAL deutlich gemacht und mit Beispielen wird das ganze untermauert. Die Hälfte des Buches besteht aus der Referenz in der man wirklich alles findet. Über jeden Assemblerbefehl gibt es eine ganze Seite (Funktion, Flags, Verwendung, Anzahl der Takte, die die unterschiedlichen Prozessoren benötigen, Hexcodes, Operanden...) Ein richtiges Kompendium also.
 
 

Antwort 3
~~~~~~~~~
[ von Frank Steinberg ("Steini";
f-steinberg*nexgo.de ) im QB-Forum 20.10.01 ]
.
Die entscheidende Anweisung zum Einbau von Assemlerprogrämmchen in QBasic ist CALL ABSOLUTE. Damit sagt man BASIC praktisch: "Führe den Maschinencode aus, der an folgender Speicherstelle im Hauptspeicher beginnt".
 
Wo kommt der Maschinencode her? Der Code besteht ja aus einer Folge von Bytes. Die müssen in eine BASIC-Variable. Dazu kann man ein Array oder einen String nehmen. Es gibt viele Methoden, die Maschinencode-Bytes in das Array / den String zu bekommen. Oft wird der Maschinencode in DATA-Zeilen abgelegt und anschließend in leeres Array gePOKEt. Man kann aber auch einfach einen String erzeugen und die Bytes z.B. mit CHR$(xxx) zuweisen. Der Code kann auch in einer Datei liegen, die gelesen und in einem Array/String abgelegt wird.
 
Anschließend wird das Speichersegment mit VARSEG ermittelt, in dem sich die Variable befindet und mit DEF SEG gesetzt. Dann wird der Offset der Speicheradresse mit VARPTR bzw. SADD (bei normalen Strings) bestimmt. Zum Schluss ruft man den Maschinencode unter Angabe des Offsets mit CALL ABSOLUTE auf.
 
Auf meiner Homepage gibt es zwei Tools, die das Ganze automatisieren: "Absolute ASM" von Petter Holmberg (sollte man sich schon wegen des enthaltenen Tutorials runterladen) benutzt DEBUG um Maschinencode zu erzeugen. Zum Zweiten "ABSOLUTE.BAS" von mir selber. Dort wird ASM-Code erzeugt/editiert, dann assembliert, und (nach Abfrage) ausgeführt. Außerdem wird eine BASIC-Quelltextdatei mit einem Maschinencode-String erzeugt, der in eigene Progs kopiert werden kann. Alle erforderlichen Programme und Beipiele sind im ZIP-Archiv enthalten.
 
Download:
http://home.nexgo.de/steini63/pbeisp.htm
 
 

Antwort 4
~~~~~~~~~
[ von Soeren Dressler im QB-Forum 25.4.02 ]
.
Es gibt viele Möglichkeiten, Assembler in QBasic einzubauen.
 
Entweder du schreibst den ASM-Code mit der DEBUG.EXE, die es in Maschinencode (HEX) umwandelt. Laßt es dann in QBasic mit CALL ABSOLUTE aufrufen. Oder du benutzt TASM oder MASM um deinen ASM-Code in OBJ-Dateien umzuwandeln, die du dann mit LINK.EXE (von QBasic 4.5) in eine QuickLibrary verwandelst.
 
Bei der ersten Möglichkeit ist die Handhabung von DEBUG und CALL ABSOLUTE nicht besonders einfach.
 
Bei der zweiten brauchst du TASM oder MASM. Aber es funktioniert. Programme wie DirectQB oder FutureLib wurden damit geschrieben. Ich hab selbst auch einiges damit geschrieben.
 
 

Antwort 5
~~~~~~~~~
[ von Matthias Becker (alias "Helium" alias "Beckah";
M.Beckah*gmx.de ), 18.10.2002 - 19.4.2002 ]
.
Es gibt zwei Wege, Assembler in QB zu verwenden. Eine sehr umständliche Art ist Call Absolute. Du schreibst alle Opcodes deines Assemblerprogramms in ein Datenfeld und rufst CALL ABSOLUTE mit dessen Adresse auf.
Von Dieser methode rate ich aber ab, da das Herausfinden der Opcodes kompliziert ist. Sinnvoller ist es, eine Bibliothek zu verwenden. Dazu schreibst Du die Funktionen ganz normal in eine .asm-Datei und assemblierst diese zu einer .obj Datei. Daraus machst Du dann eine Bibliothek, indem Du die QuickBASIC-Dienstprogramme Lib und Link wie weiter unten beschrieben verwendest. Diese Bibliothek kannst Du dann beliebig in QB verwenden.
Wichtig ist, das die .asm-Datei mit folgenden Zeilen beginnt:
.model medium, basic
.386
.code
 

.
model medium, basic: Das Model medium wird hier verwendet, da QB es auch verwendet und basic, da die Funktionen von QB aus zugänglich sein sollen.
 
.386: Ok, hier kannst Du auch 286, 486 oder 586 verwenden, vorausgesetzt, Du kennst sich mit den Befehlssätzen aus und verwendest diese auch.
 

.
code: Wie Du als ASM-Programmierer weißt, fängt ab hier <dein Programm an. (Du kannst natürlich auch ein .stack und ein .data vor .code einfügen.)
 
Die Parameter übergibt Basic mit Hilfe des ->
Stacks, wie folgt (von oben nach unten):
QB Rücksprung-Offset
QB Rücksprung-Segement
Letzter Parameter
Vorletzter Parameter
...
erster Parameter
 
Die Funktion SUB MalePixel (byVal x%,byVal y%,byVal farbe%) hätte also folgenden Stack-Aufbau:
0: QB Rüchsprung-Offset
2: QB Rücksprung-Segement
4: farbe
6: y
8: x
 
Als nächstes solltest Du wissen, dass Du alle Funktionen, die von Basic aufgerufen werden sollen als public definieren musst:
public Funktionsname , Funktionsname, ...nach dem .code Befehl.
 
Am Ende ihrer Funktion springst Du mit ret n wieder zurück zum Basic-Programm, wobei n die Anzahl der Bytes ist, die Du auf den Stack geschrieben hast. In Basic musst Du die Funktionen noch deklarieren: Declare Sub Name (byVal ...). Ich empfehle, alle Werte als reine Werte (mit byVal) statt deren Adresse (was in Basic der normalfall ist) zu übergeben, also mit ->
CALL by Value statt -> Call by Reference .
 
Wenn das Programm fertig ist und die .asm-Datei gespeichert ist, must Du es nur noch assemblieren. Dabei entsteht eine Datei mit der Endung .obj. Diese verwendest Du, um die Bibliothek zu erstellen:
 
Dafür benötigst Du den bei QB mitgelieferten Library-Manager (LIB.EXE) und den Linker (LINK.EXE).
 
LIB:
 
Nach dem Aufruf von lib wirst Du nach dem Bibliotheksnamen gefragt. Gib hier einen beliebigen Namen ein. Als nächstes musst Du mit y (nicht j) bestätigen, dass sie erstellt werden soll. (Wenn Du nicht gefragt wirst und nicht einer bereits bestehenden .lib eine weitere .obj hinzufügen willst, solltet Du mit Strg+C oder Strg+Pause hier abbrechen und einen anderen noch nicht verwendeten Namen verwenden). Dann wirst Du nach den Operationen gefragt. Zur Verfügung stehen + und -. Mit + fügst Du eine Datei hinzu. Die Anweisung lautet +objdatei.obj. (Wenn keine Endung angegeben wird automatisch ein .obj angehängt). Wenn mehr als eine .obj-Datei eingebunden werden soll oder eine zusätzliche bereits bestehende .lib-Datei, kannst Du das mit einem & am Ende der Anweisung erreichen. Wenn Du nach einer List-Datei gefragt wirst, drücke einfach Enter. Nun hast Du eine .lib-Datei erstellt. QB verlangt aber zum Ablaufenlassen im Interpreter eine .QBL-Datei und verwendet die lib nur beim Compilieren.
 
Die .qlb erstellst Du mit dem Linker. Nach einem Aufruf von link wirst Du nach dem Objekt gefragt. Hier gibst Du aber /QU ein, gefolgt von einem Leerzeichen und dem Namen der eben erstellten .lib-Datei (mit Endung!!!, da sonst .obj angenommen wird, was in unserem Fall falsch ist.). Bei der Frage nach der ausfürbaren Datei bestätigst Du einfach mit Enter. (Dann wird derselbe Name wie bei der .lib verwendet). Das map ist ebenfalls nicht von Interesse für uns, also Enter. Bei der gefragten Bibliothek musst Du BQLB45.lib angeben (gegebenenfalls mit komplettem Pfad), wenn Du QB4.5 verwendest. Bei QB7.1 heißt die Datei QBX.QLB.
 
Zum Schluss kannst Du QuickBasic mit
QB /L qlbname starten.
 
Alle Funktionsdeklarationen solltest Du am besten in einer Datei mit der Endung .BI speichern und mit
REM $INCLUDE: 'name.bi' einbinden, damit diese nicht in jedem Programm, das die Assemblerfunktionen verwenden soll, neu deklariert werden müssen.
 
Mehr über Bibliotheken erfährst Du i nder FAQ-Kategorie
"Bibliotheken, CHAIN- und MAK-Module" .
 
 

Antwort 6
~~~~~~~~~
[ von marzec im QB-Forum 25.4.02 ]
.
Du hast zwei Möglichkeiten:
 

1) call absolut. Dieser Befehl erlaubt es dir im Speicher in Hexcodes vorliegende Routinen aufzurufen. Dazu schreibst du zuerst dein Programm mit hilfe von DEBUG (ein altes dos-util mit dem man
Assemblerprogramme umständlich schreiben kann) liest die so erhaltenen Hexcodes für der einzelnen Befehle in einen Array in deinem Programm ein und führst dann call absolut aus (übergib die Segment:Offset Adresse des Arrays an die Funktion). Das ist die wohl schlechteste Lösung aber es ist möglich.
 

2) MASM, NASM, TASM: Mit diesen Programmen kannst du bequem deine im Editor geschriebenen asm-routinen compilieren. Hast du sie zu einer Objekt-datei compiliert wandelst du diese mit den Befehlen Lib und link in eine QLB-Datei um und startest dann qb mit dieser Bibliothek. Innerhalb des Programms mußt du nur mehr die Routine deklarieren (DECLARE SUB etc) und kannst sie dann wie eine normale QB-Funktion handhaben. Hier ein Beispiel zum setzten eines Pixel im Mode
13h:
 
 
*** Assembler-Beispielprogramm (ASMPSET.ASM)
 
;Speicher & Parameterübergabemodell festlegen
.model medium,basic
; Stackgröße festlegen
.stack 100h
;codesegment einleiten
.code
;Name der funktion ist öffentlich (wie SHARED)
public asmpset
;Deklaration der Routine mit Parametern
;Word = INTEGER
asmpset proc x:word, y:word, col:word
mov ax, a000h
mov es, ax
mov di, y
mov bx, di
shl di, 6
shl bx, 8
add di, bx
add di, x
mov cx, col
mov es:[di], cl
ret
asmpset endp
END
 
Das ist das Assembler-Quellspracheprogamm. Jetzt kompilieren wir es, und machen daraus eine
QLB-Datei:
masm asmpset.asm;
lib asmpset +C:\qb45\qb.lib;
link /q asmpset.lib, asmpset.qlb, NULL, C:\qb45\bqlb45.lib;
 
Ich glaub das passt nicht ganz so muß erst noch nachschauen. Egal, jetzt haben
wir die Datei ASMPSET.QLB. Starten wir QBASIC
qb /lasmpset
 
Jetzt noch die Deklaration am Anfang des QB-progs:
DECLARE SUB asmpset(BYVAL x%, BYVAL y%, BYVAL col%)
 
und fertig. Die Routine lässt sich ganz einfach aufrufen z.B.
asmpset 160, 100, 4
 
Ich hoffe das war hilfreich. Bei weiteren Fragen schreib ein Ma.il
 
 

Antwort 7
~~~~~~~~~
[ von Gono
basicartstudios*yahoo.com im QB-Forum 20.10.01 ]
.
Mit der DATA-Anweisung kann man die ASM-Kommandos im ->
Maschiensprache -Code hinterlegen und dann mit DO an die entsprechenden Stellen im Speicher poken. Das ist aber ziemlich umständlich, da normalerweise ja die Befehle nicht in HEX sonst als ASM-Code (JMP, MOV, etc.) angegeben werden. Aber möglich ist das...
 
So komfortabel und praktisch wie in Pascal oder C geht es leider nicht.
 
 

Antwort 8
~~~~~~~~~
[ von ensugo im QB-Forum , 19.10.01 ]
im Notfall gehts auch mit DEBUG..
 
 
...Kleines Beispiel:
-------------------------------------------------
debug
a 100; Assemblieren ab Offset 100h
push cs
pop ds
mov dx,10d ; Adresse noch nicht bekannt, aber später können wir die
; Richtige eingeben.
mov ah,9 ; String
int 21 ; ausgeben.
mov ah,4c ; Programm
int 21 ; beeden
:010D db "Hallo!$" ; diese Adresse muss in der 3. Zeile
; angegeben werden(mov dx,)
:0114 <Enter>
a 102 mov dx,10d ; Richtige Adresse vom String.
:0105 <Enter>
h 114 100 ; Länge ausrechnen
Ausgabe: "0214 0014"
14h ist die Länge des Programmes in Bytes
und die wollen wir speichern:
rcx
14
n hallo.com ; hier geben wir dem Programm einen Namen
w ; Speichern
q ; Verlassen
-------------------------------------
 
Fertig. Nun können wir mit "hallo" das Programm starten.
 
Ich habe mir ein QB-Programm gebastelt, das so ein Programm Byte für Byte ausliest und als DATA-Zeilen an eine BAS-Datei anhängt (FOR APPEND geöffnet).
 
 

Antwort 9
~~~~~~~~~
[ vom QBasic.de Forum, 28.6.01 ]
.
Auf dem QBasic-Forum von
www.QBasic.de wurde beispielsweise einmal die folgende Frage gestellt:
 
Ich möchte in einer Integer Variable die Bit nach links shiften. Die mache ich einfach, indem ch die Variable mit 2 multipliziere. Das funktioniert so lange gut, wie das höchstwertige Bit icht gesetzt wird, also so lange das Ergebnis unter 32768 (2 ^ 15) bleibt, danach kommt es u einem Overflow. Ich kann das natürlich über eine IF Abfrage vorher abprüfen, da ich aber uch um mehr als ein Bit shiften will, wird das ziemlich aufwendig un dvo allem viel zu angsam. Ich suche also nach einer schnellen Lösung, am besten in Assembler da die Geschwindigkeit sehr wichtig ist !
 
Die Antwort von Alex darauf war:
 
Ich würde es ungefähr so machen:
 
'-------- Anfang ----------
DEFINT A-Z
DECLARE SUB SHL (Zahl%, Bits%)
DIM SHARED AsmSHL AS STRING * 30
'-------- Machinencode laden----------
' Dann befindet sich der Code an der Adresse Varseg(ASmSHL):Varptr(AsmSHL)
' und kann beliebig oft mit >CALL ABSOLUTE( Var1%,Var2% oder Wert,StartOffset%)<
' aufgerufen werden (SUB)
DEF SEG = VARSEG(AsmSHL)
FOR i = 0 TO 29
READ Opc$
POKE VARPTR(AsmSHL) + i, VAL("&H" + Opc$)
NEXT i
DEF SEG
Zahl = -3
SHL Zahl, 2 ' Statt "2" kann auch eine Variable stehen"
PRINT Zahl
END
' Maschinencode (30 Bytes)
DATA 8B,DC,8B,77,4,8B,C,8B,77,6,8B,4,8B,D8,81,E3,0,80,D3,E0
DATA 25,FF,7F,B,C3,89,4,CA,4,0
SUB SHL (Zahl, Bits)
DEF SEG = VARSEG(AsmSHL)
CALL absolute(Zahl, Bits, VARPTR(AsmSHL))
DEF SEG
END SUB
'-------- Ende ----------
 
Für shl (in diesem Fall "shl ax,cl") steht nur "D3E0" oder 2 Byte, alles andere ist nur die Vorbereitung usw. Und zwar werden die Parameter über den Stack übergeben. Dann noch die Sache mit dem Vorzeichen (Bit 15). Also der Assembler-Quellcode sieht so aus (bin kein Profi):
 
mov bx,sp
mov si,[bx+4] ; Zeiger auf die ZWEITE Variable nach SI
mov cx,[si] ; 2. Variable in CX holen
mov si,[bx+6] ; Zeiger auf die ERSTE Variable nach SI
mov ax,[si] ; 1. Variable in AX holen
mov bx,ax ; 1. Variable nach BX kopieren
and bx,8000h ; Vorzeichen (Bit 15) isolieren, steht in BX
shl ax,cl ; hier wird geschoben! In CL steht um wieviele Bits(Opcode D3E0)
and ax,7FFFh ; 15. Bit loeschen (Vorzeichen)
or ax,bx ; und altes Vorzeichen wiederherstellen
mov [si],ax ; Ergebnis in 1. Variable schreiben
retf 4 ; Ruecksprung-Unterprogramm verlassen
 
Wenn du alles assembliert hast, kannst Du den DOS-Befehl DEBUG verwenden, um den Maschinencode für diese Assembler-Befehlssequenz herauszubekommen.
 
Gehe dazu wie folgt vor:
DEBUG starten, danach "a" für Assemblieren eingeben. Es erscheint eine Adresse, z.B. 222A:0100 (XXXX:0100) und du kannst die Befehle eingeben und mit Enter bestätigen. Wenn du fertig bist, Enter ohne Eingabe drücken. Mit "u 100" (oder "u Adresse") disassemblieren. Für mehr Optionen "?" eingeben. So einfach ist das.
 
DEBUG ist natürlich nur eine Notlösung für den Fall, dass du keinen Assembler (TASM, MASM usw.) besitzt.
 
 

Answer 10
~~~~~~~~~
What is assembly language?
Assembly language is a low level language which will usually get you more performance from the machine, because it 'talks' directly with the hardware. ASM is great, but it can crash your machine if not used correctly. Be careful.
 
 

Answer 11
~~~~~~~~~
[ Tutorial by Aaron Severn (
mkv*internetwis.com ), Dec. 22, 1997 ]
 

A s s e m b l y


i n


Q B a s i c


 
 
By Aaron Severn
December 22, 1997
 
 
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Table of Contents
0 Disclaimer
1 Introduction
2 Using Debug to Generate Machine Code
3 Using Machine Code in QBasic
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 
 
0 Disclaimer
------------
I assume no responsibility for any harm that comes from using the material
contained in this document to you, your computer, or anything relating to
your existence. No warranty is provided or implied with this information.
This document is provided as is.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 
 
1 Introduction
--------------
It's always nice to throw in a little assembly in your QBasic programs, if
only to speed things up. Also, some things like BIOS calls are impossible
in QBasic without assembly (but not in QuickBasic, there's another way).
Now I'm not going to start teaching you assembly, if that's what you want
look into one of the hundreds of other tutorials available on that subject,
this is about taking assembly and making it usable to your QBasic programs.
I'm sure there are alot of people out there who know how to program in
assembly well enough but don't have a clue as to how you get all those hex
numbers that CALL ABSOLUTE uses. That's where this tutorial will help you.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 
 
2 Using Debug to Generate Machine Code
--------------------------------------
In your DOS directory (or your WINDOWS/COMMAND directory for Windows 95
users) you will find a nice little program called DEBUG.EXE. This is
probably the most user unfriendly piece of software ever created. If you've
tried running it you'll notice that you get the following prompt and nothing
else:
-
So what use is that? Well try typing in the letter 'a' and hitting enter.
Now you've got just as ugly a screen staring back at you that looks
something like this:
-a
199D:0100
 
But at least we're getting somewhere. You see, 'a' (which stands for
assemble, I think) turns DEBUG into a crude assembler. You can now start
typing in assembly code. As an example, let's try something simple like
turning the mouse on. Here's the assembly code:
push ax
xor ax,ax
int 33
mov ax,1
int 33
pop ax
retf
 
Enter this code in DEBUG, when you're done enter a blank line, you've just
written a simple assembly program. When you enter the blank line at the end
it should take you back to the dash prompt. Now enter a 'u' (for unassemble)
at the prompt. A whole bunch of weird lines pour out, but wait, there's
that program we just wrote, and beside it, machine code. There should be
four columns on your screen, the first one lists the memory address of each
bit of code, the second one lists the machine code, the third lists the
assembly command, and the fourth lists the arguments for that command. The
column we want is the second one, copy down those numbers that came out,
you should get this.
50
31C0
CD33
B80100
CD33
58
CB
 
All those numbers are in hex, so in QBasic you'll have to add the &H
prefix.
 
So now we've got our machine code and we just have to get out of DEBUG.
Type 'q' at the prompt and we'll move on to implementing this in QBasic.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 
3 Using Machine Code in QBasic
------------------------------
Chances are you already know that CALL ABSOLUTE is the command we need to
use here. This is how you do it. First take the numbers that you copied
down that are the machine code for our little mouse program and put them
together in a string. You want to take the ASCII character associated with
each number, that way we'll have a string of bytes in memory accessible by
CALL ABSOLUTE. Your QBasic code should look something like this:
DIM ASM AS STRING
ASM = ASM + CHR$(&H50)
ASM = ASM + CHR$(&H31) + CHR$(&HC0)
ASM = ASM + CHR$(&HCD) + CHR$(&H33)
ASM = ASM + CHR$(&HB8) + CHR$(&H1) + CHR$(&H0)
ASM = ASM + CHR$(&HCD) + CHR$(&H33)
ASM = ASM + CHR$(&H58)
ASM = ASM + CHR$(&HCB)
 
Now all you have to do is run it. First set the default segment to the
segment address of ASM, then use CALL ABSOLUTE with the offset address of
ASM as the parameter. Add the following two lines to the bottom of the
above code and then run it.
DEF SEG = VARSEG(ASM)
CALL ABSOLUTE(SADD(ASM)
 
So that's the basics of using ASM in QBasic. But before I'm finished let
me write a little about passing variables to your assembly procedures. For
example, if you wrote a faster PSET routine done in assembly you'd want to
pass the (x, y) coordinates and the colour of the pixel. To do that you
have to understand where the variables are. When you put additional
parameters in a CALL ABSOLUTE statement QBasic puts them on the stack, so
all you need is to set up a pointer to the stack, which is done with the
following assembly code.
 
push bp
mov bp, sp
.
. [your code here]
.
pop bp
 
Now bp points to the first byte on the stack. Take the following sample
code. Let's assume that ASM contains code which puts a pixel on the screen.
DEF SEG = VARSEG(ASM)
CALL ABSOLUTE (x, y, colour, SADD(ASM))
 
In this example the stack would look something like this:
0A x
08 y
06 colour
04 QBasic return offset
02 QBasic return segment
00 bp (pushed on to the stack by the ASM code)
 
So in order to get at x you would just use [bp + 0A]. Remember that
everytime you push a byte on to the stack those numbers will be adjusted.
For example if you had pushed AX on to the stack then x would be located at
[bp + 0C] now. If you push on BX, x is now at [bp + 0E]. You get the
picture. So good luck!
 
 

Answer 12
~~~~~~~~~
[ Addition to Aaron Severn's ASM tutorial by Vic, downloaded from
www.qbasicnews.com , 21.11.03)
 
-----------------------------------------------------------------------------
Vic's QBasic Programming Tutorial
Basic Tutorial XIII
USING BASIC ASM IN YOUR BASIC PROGRAM!!!
This actually is more complicated then ASM itself!!!
-----------------------------------------------------------------------------
Before I start On this tutorial, I want to admit something...
This tutorial was done before By a genius called Aaron Severn back in 1997...
That tutorial has everything you could want, but I think that it leaves
some things out... It also leeves out some details. At the time I learned
this, I did not know that that tutorial existed, (I wish I did!!), I picked
it up from some peoples source code, so I don't consider this plagerism...
 
---
Ok, where to start...
First, why use ASM at all...
1. Its fast...
2. It can do many many things that Qbasic can't do on its own...
3. well, I guess #2 covers everything doesn't it??
 
 
---
Ok, What must you do first?
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!NOTE: This tutorial expects that you read the last one on ASM... !!
!! This tutorial does not teach asm... !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
Ok, lets start of by using the mouse ASM program we used in the last
tutorial...
remember?
 
mov ax,1
int 33
RETF
 
Ok, you might not remember the RETF, but we need it to shut down the program
withought crashing... just remember to put it at the end of your program...
now what?
 
well, Qbasic would not understand this program in this format, so, we have
to make it more Qbasic friendly...
How? (I ask more questions than you probly do!)
by converting it from ASM to MACHINE Code...
 
 
*** CONVERTING ASM TO MACHINE CODE
There are a few ways to convert ASM to Machine code...
the ways that I use are...
1. Memorizing
2. Using Debug's Desembler or Unasembler or whatever its called...
 
 

#1. Memorizing... 'If your no interested in the word memorizing skip this
 
The MOV command would be the machine code equivalent of "B8"
so, the first command "mov ax,1" would look like this in Machine code
"B80100"
 
The INT command would be the machine code equivalent of "CD"
so, the second command "int 33" would look like this...
"CD33"
 
Finally, the RETF command (which is equal to "CB" would look like this
"CB"
 
So, all together, the program would look like this...
B80100
CD33
CB
 
You would have to memorize each command... I don't recomend this!!!
This is the way I originally did it until I found Aaron Severn's tutor...
 

#2. Using Debug!! (easy... LISTEN!!!!!)
ok, this is going to sound a little familiar... I hope!
goto a dos prompt (command.com)
and type DEBUG
 
type
a
to go to assembler mode
Now, type in the program ps. don't write the {enter}s
mov ax,1 {enter}
int 33 {enter}
RETF {enter}
{enter}
 
that should bring you back to the
-
prompt (if not, hit enter again)
Now, type
u
and you should see this on your screen...
197A:0100 B80100 MOV AX,0001
197A:0103 CD33 INT 33
197A:0105 CB RETF
197A:0106 A7 CMPSW
197A:0107 D802 FADD DWORD PTR [BP+SI]
197A:0109 3C2A CMP AL,2A
197A:010B 7505 JNZ 0112
197A:010D 800EA7D802 OR BYTE PTR [D8A7],02
197A:0112 3A0694D2 CMP AL,[D294]
197A:0116 75C9 JNZ 00E1
197A:0118 4E DEC SI
197A:0119 32C0 XOR AL,AL
197A:011B 8634 XCHG DH,[SI]
197A:011D 006919 ADD [BX+DI+19],CH
 
'or something like this...
!!!!
whats all that crap for???
 
well, My best guess, would be that thats left over from another program...
The only part that you need to pay attention to is the stuff that contains
your program code...
EX.
197A:0100 B80100 MOV AX,0001
197A:0103 CD33 INT 33
197A:0105 CB RETF
 
Look over to the right of that ^ ^
Thats our complete program...
 
look over to the left... past the 197A:0100...
you see B80100...
 
That is those commands (to the right...) in machine code...
It did it for you!!! GREAT!!!
Do you get it???
 
 
*** Using MACHINE CODE in QBasic...
Each two digit number (in HEX.. (i'll explain later!)) is a command, or a
number... So, qbasic needs the program seperated into 2 digit #'s...
so the above program...
B80100
CD33
CB
 
needs to look like this...
B8 01 00 CD 33 CB
 
but, even now After it is machine code and seperated, QB will not understand
it... you have to tell QB that they are HEX and not Decimal numbers and then
put it all into an array... Then your finished...
Here we go!!
 
 
*** How to tell QB the numbers Are HEX...
Since this is a basic ASM in QB tutor I will cover the easy most
understandable explaination... In a more advanced tutorial, I plan to
cover a different way... for now just use this...
 
Lets take the first 2 digit HEX command B8...
in order for qb to understand it as hex we need to present it like this...
 
CHR$(&HB8)
 
Lets disect this...
CHR$( &H B8 )
^ ^ ^ ^
changes to Makes it The command Closing
character. HEX...
... ...
 
Ok... Lets look at the next one 01...
CHR$(&H01)
 
Disect again...
CHR$( &H 01 )
^ ^ ^ ^
changes to Makes it The command Closing
character. HEX... ...
...
 
do you get it yet? I hope so...
 
 
*** Putting the hex into an array...
This is simple...
do you remember how to add words together in qb?
like this
' First QB example in a long time (copy into QB...)
'
ONE$ = "School "
TWO$ = "Sucks!"
'
THREE$ = ONE$ + TWO$
'
Print THREE$
'
END
'
'---- end example...
 
In this example, the words are connected...
 
How do we do this with HEX?
The same way!
 
The mouse program would look like this...
this is the original (not ready for QB)
B8 01 00
CD 33
CB
 
This is what it would look like going into an array...
ASM$ = ASM$ + CHR$(&HB8) + CHR$(&H01) + CHR$(&H00)
ASM$ = ASM$ + CHR$(&HCD) + CHR$(&H33)
ASM$ = ASM$ + CHR$(&HCB)
 
can you see how its done???
 
 
*** OK Qbasic Can see the ASM program... Now what?
All we have to do now, is tell QB to execute the above code as an ASM file...
So, the program is stored as one array called ASM$...
That is what we have to tell QB to Run...
What command do we use to run it?
I am sure you have seen this before... It is called CALL ABSOLUTE...
 
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!NOTE: if you are using QB 4.0 or above, you have to start qbasic in a dos !
! Box (command.com) whith the line c:\qbdir\qb /l If you don't !
! then QB will not recognize the Call absolute command... !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
This is how you would call the call absolute...
Memorize this...
DEF SEG = VARSEG(ASM$)
CALL ABSOLUTE (SADD(ASM$))
 
Thats IT!! Your finished!!!
Here is the total program... (copy and paste this into a qbasic dos window)
 
'---------- QB ASM MOUSE EXAMPLE
'
SCREEN 13
ASM$ = ASM$ + CHR$(&HB8) + CHR$(&H01) + CHR$(&H00)
ASM$ = ASM$ + CHR$(&HCD) + CHR$(&H33)
ASM$ = ASM$ + CHR$(&HCB)
'
DEF SEG = VARSEG(ASM$)
CALL ABSOLUTE (SADD(ASM$))
'
'------- END EXAMPLE
 
Wow, that was a lot of explaining to do for such a short example...
 
This method is to do something that does not require input or output into and
out from QBASIC...
 
In the next ASM tutorial I will show you how to interact with the ASM that is
running. Examples of this is how to get the mouse position into qbasic, and
how to set the mouse position... This gets really ASM advanced...
 
 
If you want to know when this or any other tutorial is out make sure to sign
up on the list (look at the bottom of this file...)
 
 
-----------------------------------------------------------------------------
Thats it for this tutorial, If I didn't get into enough detail in the
explanations then just look at the source code and try to figure it out
on your own. All else fails E-Mail Me... I want to make these tutorials
as easy to understand as posible!!!
My current E-Mail address is
RADIOHANDS*AOL.com
If you are using this tutorial on your page, please leave the tutorial
exactly as it is... please don't change anything, unless its spelling
errors... Theres alot of them! I don't like using the backspace key...
The original website that these were on is
http://members.aol.com/radiohands/index.html
Thank you
Vic Luce
Finished
January 21
2000
 
If you want to be notified when a new tutorial is out..
Send An E-mail to RADIOHANDS*AOL.com
with the subject saying VQBLIST and then your E-mail address(check website)
 
 
 

Answer 13
~~~~~~~~~
[ by Milo Sedlacek and Drew Vogel (
mkv*internetwis.com ) ]
 

Assembly Language Tutorial v0.3

For Intel's 80286 Processor

by: Milo Sedlacek and Drew Vogel
 
Dear Reader,
The purpose of this tutorial is to familiarize you with
the 8086 processor registers, accessing modes and
architecture so you'll eventually be able to interface
QuickBasic 4.5 with Assembly Language.
 
Knowledge of Hexidecimal and Binary numbers is assumed.
 
Now let's go over the parts of the computer...
there's:
1. main memory
2. the processor
3. peripherals attached on the bus
 
Basically, the processor performs operations on memory
and reads and writes to the peripherals.
 
So let's take a look at what registers are in the 8086
 
General Purpose Registers:
AX
BX
CX
DX
 
OK first let's talk about what these do, and what they
are. First of all, they are 16 bits wide, and they
are split into 2 parts, the low and high byte. Actually,
you could pretend that there are this many general
purpose registers:
 
AX AH AL
BX BH BL
CX CH CL
DX DH DL
 
Now, AH is the high byte of AX, so if I set AL to 0 and
AH to 1 then AX will be 256
AX = AH * 256 + AL
 
That's the way the _X and hi / low registers are related.
 
So basically you can access a general purpose register
in its 16-bit entirety or in its 8 bit high and low
parts. Note that if you put a value in _L or _H it affects _X.
 
Basically, these registers just hold temporary values and
are used for calculations, loop counters and so on.
 
Let's move on to other registers now!
;-------------------------------------------------
;other regs will be here when we reg them wrote up
;-------------------------------------------------
 
OK, now it's time to learn some assembly instructions
In oder to understand instructions you need to know what an operand is.
Here's the shorest and easiest to understand definition:
 
operand(s): values after the instruction
 
First I'll teach you MOV (doesn't have to be in caps, I just do
that to seperate instructions from other stuff) since it's probably
the easiest to understand and definately the most commonly used
instruction. The syntax for MOV is:
MOV detsination, source
 
MOV just moves the value of 'source' to 'destination', so 'MOV AL, 0005h'
would place 0005h into AL. Both operands can be registers, 'destination'
can be a literal expression and either (but not both) operands can be a
variable, or memory, but we'll get into variables and memory later.
 
Well, Since MOV is pretty useless by itself you probably want to learn
some more instructions, so we'll move on to INC and DEC. Herte's the syntax:
INC value
DEC value
 
INC simply adds 1 to 'value' and DEC subtracts 1 from 'value' where value is
either a register, variable, or memory. Ok, here's a little quiz.
MOV AX, 0010h
INC AX
DEC BX
INC AX
 
What be the value of AX? If you said 0012h then your right. Good thing you
didn't fall for my little trick of sticking 'DEC BX' in there which has nothing
to do with the value of AX.
 
What's that you say? You don't want to have 2 zillion INC's and DEC's in your
code? Well me either, and obviously Intel didn't like that idea either since
they made us ADD and SUB. Syntax:
ADD destination, source
SUB destination, source
 
Ok, ADD just adds the value of 'source' to 'destination' and stores the result
in 'destination'. SUB subtracts the value of 'source' from 'destination' and
stores the result in 'destination'. ADD and SUB have the same operand rules as
MOV which you should remember (if you don't read it over and over until you do). ]
Your really gonna hate me now, time for another quiz:
MOV DX, 0012h
MOV BX, 0006h
SUB DX, AX
DEC DX
INC BS
ADD DX, BX
 
If you think the value of DX is 0012h then your right. Damn your good. Right
now you probably saying, whoppdy do da, this is really gonna help me. Well
I know this is going kinda slow, but you need to know all these little things
in order to do anything useful.
 
Alrighty, here's how to multiply!
 
there are 2 ways you can multiply:
 
8 bit * 8 bit to yield a 16 bit result, or 16 bit * 16 bit to yield a 32 bit
result.
 
Multiplying 8 bit by 8 bit.
 
To set up for a MUL instruction, one of the numbers to be multiplied must
be in the AL register and the other number can be in any other 8 bit general
purpose register.
 
Example:
MOV AL,10
MOV DL,20
MUL DL
 
And the result will be in AX.
 
Multiplying 16 bit by 16 bit.
 
To set up for a 16 bit multiply instruction, one of the numbers must be in
the AX register and the other number can be in any other 16 bit register:
 
Example:
MOV AX,320
MOV CX,10
MUL CX
 
And the result will be in AX:DX where AX is the low word of the result and
DX is the high word.
 
And now onto dividing!
 
When dividing, you either divide a 16 bit number by an 8 bit number or a
32 bit number by a 16 bit number.
 
Dividing a 16 bit number by an 8 bit number:
 
The number to divide is to be in AX. The number to divide by (the
denominator) can be in any 8 bit register. The qotient is returned in AL
and the remainder in AH.
 
Example:
MOV AX,100
MOV CL,2
DIV CL
 
In a 32 bit by 16 bit divide, place the number to be divided (the numerator
in the AX:DX pair where AX is the low word). Divide by any other 16 bit
register.
 
Example:
MOV AX,0
MOV DX,1 ;the ax:dx 32 bit pair now contains the value 65536
DIV CX
 
The quotient is returned in AX and the remainder in DX.
 
Ok, heres some juicy stuff! These next two instructions are the reason I
NEVER use MUL and DIV. SHL and SHR (our two new 'wonder' instructions) are
probably the best and hardest to understand instructions, put on your think
cap for these. This is where knowledge of binary and hexidecimal numbers
comes in handy (you might want to go out and get a TI-34 or better).
Syntax:
SHL destination, value
SHR destination, value
 
SHL shifts each bit in 'destination' left 2^'value' places (or digit) and
SHR shift each bit in 'destination' right 2^'value' place. In other words,
the breakdown of it is, they multiply and divide by powers of 2. Here, look
at a few examples:
MOV AX, 0010b ;'SHL destination, 1' multiplies destination by it's base
SHR AX, 1 ;so now AX holds 0001b (b = binary)
 
MOV CX, 2 ;ok, this example is like '2*(2^4)'
SHL CX, 4 ;I know it's hard, but it'll make sense eventually.
 
Next comes IN and OUT. These are very useful, if you have a port reference that
is. Yes, you guessed right boys and girls, IN and OUT recieve data from a port
and write data to a port respectively. Syntax:
OUT destination, source
IN destination, source
 
The thing I never understood about these (and a few other instructions) is that
even if you provide a 'destination' and a 'source' they still use the port in
DX and the value in AL. Maybe Milo can tell all of us why. Since you do need to
know the port indexes to use these, I suppose I should provide you with at least
a small list huh? Well I'll see what I can do.
 
Ok, It's been a while since we've worked on this, this being for many reasons,
and we're going to take off into the juicy stuff pretty rapidly so I'd suggest
you make sure you understand the above info before continueing and unless you
just started reading this tutorial then I'd also suggest at least skimming over
it again!
 
Before we begin I would suggest you go learn the internals of screen 13h and how
it works. There will eventually be an explanation in the same zip as this tutorial
but since this tutorial is aimed at teaching assembly programming, we'll stick to
the language for now.
 
Since we are teaching you howto use assembly with quickbasic, we should probably
teach you how to call it from quickbasic. There are many ways to do this, but
since you are just starting out we'll make it easy on you. We'll make a library
full of all of our assembly routines from throughout the tutorial.
 
We will be using TASM for our assembling. If you don't have TASM, MASM will probably
work for most of our routines. I would suggest going out and getting TASM though.
Here is a sample program to access screen 13h and then exit.
.MODEL SMALL ;you can read up on MODELs in the TASM help file
.STACK 256 ;allocate space on the stack for PUSHs and POPs
.CODE
PUBLIC SET_SCREEN_13 ;let quickbasic call it
SET_SCREEN_13 PROC ;start a procedure
MOV AX, 0013h ;move 13h into ax
INT 0010h ;call the video bios interrupt (read tutor on INTs)
RETF ;return from call
ENDP ;end the procedure
END ;tells the assembler to stop assembling
 
You can write this in any text editor. Save it as 'tutor.asm' if you choose not to,
you may have some problems down the line. Ok, you need to assemble this into a
.obj (object) file by doing:
TASM tutor.asm
 
Then (assuming the .obj file is already in your quickbasic directory) you need to
link it like this:
LINK /q tutor.obj
 
You will be confronted by a few questions, here is an example:
Run File [MODE-X.QLB]: [Just Hit Return]
List File [NUL.MAP]: [Just Hit Return]
Libraries [.LIB]: BQLB.LIB
 
Now to load your library just do:
QB /l tutor.qlb
 
To call your new procedure just do:
CALL SET_SCREEN_13
 
Ok, If it doesn't work then it's at your fault because we are double checking
the sample code and directions for errors. Since this code is, more or less,
impossible to see working, we'll quit write up another procedure.
 
Here is why you should know how screen 13h works. If you've ever done a POKE
routine for screen 13h in place of a PSET then you know ay least just enough
to get by. To keep you interested though, without having to know screen 13h just
yet, we'll make a simple routine to color in the pixel in the top left corner.
I'm going to just make a PROC...ENDP in this on since you can stick this code
into the library we've already started. Here's the code:
SETP PROC
MOV AX, 0A000h ;screen segment
MOV ES, AX ;you can't affect Segment Registers with imediate values
MOV DI, 0 ;clear DI
MOV AL, 2 ;color 2
MOV ES:[DI], AL ;write color in AL to the screen
RETF ;return from call
ENDP
 
Whoaa, what the hell is that ES:[DI] thing? Well, unless you know some assembly
already, you're probably thinking something of this sort. Well my friend, this is
how you point to memory. And we'll explain this in a few days. Please go learn how
memory is set up (ie: segments and offsets). We will explain this eventually but
once again, this tutorial is aimed at learning assembly, not much else.
 
Ok, now that you've read up on memory and segments and offsets and all that other
good stuff I suggested, we can move on. When you have something like ES:[DI], ES
hold the segment and DI holds the offset to write to. Take the PSET routine above.
ES held the segment of the screen (0A000h). And DI held 0 since we wanted to write
to the very first pixel on the screen.
 
Now that you understand how pointing to memory works, we can make a totally functional
PSET routine, with paramaters for X, Y, and COLOR. This next step will be a big one.
so maybe we should take it in two steps. How about this, you email me telling me
which of the following you want to learn first, ok?
 
1) Calculating Offsets & Complex/Fast Math
2) Parameters

 

Answer 14
~~~~~~~~~~~~
[ Unknown Author ]
 
Using assembler language from within QBasic
 
> Hi all,
> I am looking for a QBasic routine (not QuickBasic, please) that will
> let you use Assembly language code in QBasic programs.
> I have seen these all over the place. I even had a few, but I either
> misplaced or deleted them.
 
Well, there is no routine as such to convert ASM code to the assembly language hex code QBASIC and QB4.x requires for CALL ABSOLUTE. Mark K Kim'sassembly language in BASIC tutorial at the following FTP site,
ftp://users.aol.com/markkkim/asm_tutorial/ gives good step by step instructions for using DEBUG to convert ASM code to hex code. This won't work on MASM or TASM code though (but you should be able to convert this to DEBUG acceptable code with only minor difficulties if you know what you are doing).
 
You can semi automate the system Mark mentions though. Here's a modification of one of his examples, the code to turn the mouse on.
 
Create a file called mouseon.dbg (or cut and paste the example below)
with the following DEBUG code (best read Marks examples first so this makes
sense
;==========Mouseon.dbg, cut here
a
mov ax,0000 ;Copy 0000h to AX
int 33h ;Interrupt 33h
cmp ax,0000 ;Compare, is AX=0000h
jz 010f ;If equal, Jump to last statement
mov ax,0001 ;Else move 0001h to AX
int 33 ;Interrupt 33h
retf ;Return
u 100 10f
q
;=============cut here.
now type
DEBUG < mouseon.dbg > mouseon.asm
 
This will send the contents of the debug session to a file called mouseon.asm which looks like this.
;========mouseon.asm
-a
1289:0100 mov ax,0000 ;Copy 0000h to AX
1289:0103 int 33h ;Interrupt 33h
1289:0105 cmp ax,0000 ;Compare, is AX=0000h
1289:0108 jz 010f ;If equal, Jump to last statement
1289:010A mov ax,0002 ;Else move 0001h to AX
1289:010D int 33 ;Interrupt 33h
1289:010F retf ;Return
1289:0110
-u 100 10f
1289:0100 B80000 MOV AX,0000
1289:0103 CD33 INT 33
1289:0105 3D0000 CMP AX,0000
1289:0108 7405 JZ 010F
1289:010A B80100 MOV AX,0001
1289:010D CD33 INT 33
1289:010F CB RETF
-q
;===========end
 
you want the lines between -u and -q, now you have to select the hex codes and split them up (eg, ignore the 1289:0103, you just want CD33, which has to be split up into CD and 33 and then converted into a string format CALL ABSOULTE can use. So CD33 becomes HR$(&HCD)+CHR$(&H33) (&H means hex)
Thus you get
'============================================================
asm$ = ""
asm$ = asm$ + CHR$(&HB8) + CHR$(&H0) + CHR$(&H0) 'MOV AX,0000
asm$ = asm$ + CHR$(&HCD) + CHR$(&H33) 'INT 33 Call the interupt
asm$ = asm$ + CHR$(&H3D) + CHR$(&H0) + CHR$(&H0) 'CMP AX,0000 If AX is 0 then no mouse
asm$ = asm$ + CHR$(&H74) + CHR$(&H5) 'JZ 010F so jump to the end
asm$ = asm$ + CHR$(&HB8) + CHR$(&H1) + CHR$(&H0) 'MOV AX,0001 otherwise load AX with 1
asm$ = asm$ + CHR$(&HCD) + CHR$(&H33) 'INT 33 and do the interrupt
asm$ = asm$ + CHR$(&HCB) 'RETF Return to the system

DEF SEG = VARSEG(asm$) 'calculate segment
offset% = SADD(asm$) 'calculate offset
CALL ABSOLUTE(offset%) 'execute
'============================================================
 
It should be easy to write a parser that will take the mouseon.asm file and convert it to BASIC code, I might even do it when I get some time.
 
 

Answer 15
~~~~~~~~~~
[ by lkt153 (
www.lkt153.cjb.net - ICQ: 27901426) ]
 
*** 1. The Assembler Basics
Assembly as the meaning of the word suggests is not what Quick Basic can execute. Assembly is what we want for Quick Basic to execute. Quickbasic can execute machine language only.
If you have an assembly routine, you have to have it translated. Dos' DEBUG can do the job.
Suppose we have this tiny routine:
MOV AX,0013
INT 10
RETF
(Assembly routines always terminate with RETF)
 
We start DEBUG, type a0, press enter and type all the instructions. Press enter on a blank line to stop. Now type u0> and you will see like a dozen lines of which these are on top:
 
1E2C:0000 B81300 MOV AX,0013
1E2C:0003 CD10 INT 10
1E2C:0005 CB RETF
 
The machine language located is in the 2nd colum and you must extract them and keep them for later.
B8 13 00 CD 10 CB
 
To implement them in Quickbasic, read the implementing section in this category.
 
*** 2. Implementing ASM in QuickBASIC (a simple way)
There are several ways to implement asm into Quick Basic, of which most can easily cause crashes or a corrupt string space.
 
The way I'm gonna show now is the most simple one (if implemented right) and it requires some formatting of the machine code.
 
If you have this routine:
MOV CX,FFFF
;0003
PUSH CX
MOV CX,1000
;0007
LOOP 0007
POP CX
LOOP 0003
RETF
 
coded this way
B9FFFF
51
B90010
E2FE
59
E2F7
CB
 
then you can translate it and build this program to execute it. You read all the bytes as numerical format, convert them to character, add to a string and use the string as instruction space. To do that, you need to know the absolute location of the string. Get the segment by using VARSEG(). To get the beginning of the string, you normally use VARPTR(). You must first note that the beginning of a variable length string is in fact a length identifier. Only 2 bytes after the beginning is the first character in the string. To get the right beginning we use SADD() (string address) instead. This function returns the actual beginning of the string without the length identifier.
 
(tested)
DATA &hB9,&hFF,&hFF,&h51,&hB9,&h00,&h10,&hE2,&hFE,&h59,&hE2,&hF7,&hCB
asm$ = ""
FOR a = 1 TO 13
READ h%
asm$ = asm$ + CHR$(h%)
NEXT a
DEF SEG = VARSEG(asm$)
CALL absolute(SADD(asm$))
 
Now, you can rethink the situation and shorten the data line by adding some code. You just store the hex codes in one data string. You can let the program read the string and get every hex code out of it and have it translated to characters. This way you don't have to count how many machine codes you got.
 
(tested)
DATA B9FFFF51B90010E2FE59E2F7CB
asm$ = ""
READ h$
FOR a = 1 TO LEN(h$) STEP 2
asm$ = asm$ + CHR$(VAL("&h" + MID$(h$, a, 2)))
NEXT a
DEF SEG = VARSEG(asm$)
CALL absolute(SADD(asm$))
 
*** 3. Using numeric variables
Assembly routines (or should I say, machine language?) can read or write Quick Basic variables. First of all, you need to know the 2 types of variables:
 

Value variables are variables where the value is passed to the routine using BYVAL. It can only be read. The routine may overwrite the value, but it is lost once it exits.
Pointer variables are variables that are identical to QuickBASIC variables. The pointer is passed to the routine. Writing a variable by writing a value at the given location in the current segment is analogous to writing a variable in memory using VARPTR() and POKE in Quick Basic.
 
*** 4. Using far addresses
Well... QB, basically uses three segments: The code segment, the data segment and an extra segment for arrays.
 
If you're passing an array, you could always pass it as a far address, 'cause: cs, will be pointing to code seg, ds will be pointing to data seg.
 
Anyway, you don't know the actual segment for the arrays, so you'd have to pass the variable as a far address, like seg:[off], so you know where to get it's value(s) from.
 
Okay, in order to pass far addresses you'd use the SEG keyword in the parameters declaration.
like:
CALL ABSOLUTE (SEG Array(Index), SADD$(Routine$))
'--- (CALL ABSOLUTE is explained in the above messages)
 
So, that way, what'll be put on stack will be, not only the array's offset, but the array's segment as well. let's suppose you use the above command, so your stack would be like:
 
sp + 08: Array's Segment
sp + 06: Array's Offset
sp + 04: Return Segment
sp + 02: Return Offset
sp: bp
 
So, you can now easily address the array, just getting it's address, which will be: [sp + 08]:[sp + 06].
 
Remember to ALWAYS preserve ds and bp... sp also, but that one cannot be changed anyway (unless you're using esp, but leave that alone for a while... hehe)...

Variables are stored at the top of the stack before the routine is called. The stack pointer SP is a register which holds the pointer to the top of the stack. To get variables, you must address those locations. SP can not address something so you have to copy the value to another register which can. BP, SI or DI would be a logical choice since all other registers are general-purpose. I recomment you use BP since SI and DI are just too usefull for working with strings, arrays or video memory. Before you start using BP you must backup the value because Quick Basic uses it. Quick Basic also uses DS, SS and SP.
 
Before you can use variables, you have to know where they are located in the stack. The first 2 bytes if the stack will be the backuped BP value. The next 4 bytes are the return address to where execution resumes after exiting. From the 6th byte and on are 2-byte pointers to all the value and pointer variables passed through.
 
Now you can make a simple template for assembly supporting variables:
PUSH BP (backup old value)
MOV SP,BP (copy stack pointer)
...
...your code here
...
POP BP (restore old value)
RETF
 
To use Value variables, you calculate the address (BP+6 for the first variable) and do this:
MOV AX,[BP+6]
 
to get the first WORD (or the entire variable, if Integer) into AX.
 
To use Pointer variables, you need to get the address of the variable. You are forced to use a general purpose or reserved register to do this. I would recomment using SI, DI or BX. Watch out for DI because it is by default linked with the ES segment (which by default holds the same as the DS segment), which you can use for your own purposes.
 
MOV SI,[BP+8] (2nd variable in stack)
MOV [SI],AX (Store the contents of AX into the variable)
 
Now, the variable is changed in Quick Basic too.
 
When calling a routine from Quick Basic, the variables passed through are in reversed order. If you have one variable, you write:
CALL absolute(var%, SADD(asm$))
 
Variable var% is accesssed by [BP+6].
 
When you add another variable, you put it before var%. This way you dont have to rewrite your code:
CALL absolute(BYVAL color%, var%, SADD(asm$))
 
Variable var% is still accesssed by [BP+6], and variable color% is accessed by [BP+8], and so on.
In the example above, var% is a pointer variable, which can return a value to Quick Basic, and color% is a value variable because it uses BYVAL.

*** 4. Handling strings
Strings, are inside QB's data segment (unless they're far strings, but I won't be explaining that).
 
Every time you pass a string by reference (QB's standard), what will be passed is the string's descriptor's offset, inside ds.
 
So, let's say you pass this:
CALL ABSOLUTE (String$, SADD$(Routine$))
 
Your stack should look like (in a near call):
 
sp + 06: String's descriptor's address.
sp + 04: Return Segment
sp + 02: Return Offset
sp: bp
 
Now, you must be asking yourself: "what in the world is a string descriptor?!". Well that's a chunk of information, in this format:
 
TYPE StringDescriptor
Lenght AS WORD
Offset AS WORD
END TYPE
 
Of course, this isn't actually, writtable in QB, nor would you have a reason to do so, but it's a general format, for all of us BASIC programmers to grasp.
 
But, enough of the non-sense. Here's a real routine:
 
push bp
mov bp, sp
mov si, [bp + 06] ; get string's descriptor's address
mov cx, ss:[si] ; get lenght to cx
mov dx, ss:[si + 02] ;get offset to dx
pop bp
retf
 
note that there's a ss call to determine the seg ment we're using, why? 'cause si's default is ds.
That way, ds:[dx] would be the string's offset.
Of course the above routine has no actual use, but it can be addapted...
 
 

Answer 16
~~~~~~~~~~~~~
[ by -/\lipha (
aliphax*hotmail.com - www.geocities.com/aliphax ) ]
 
*** Question
How do I write/access ASM code in QBasic using CALL ABSOLUTE?
 
*** Answer


 

Writing a routine for CALL ABSOLUTE..
 
CALL ABSOLUTE executes a machine-code string in memory, so you set the segment of the string with DEF SEG, then the rightmost parameter to the CALL ABSOLUTE routine is the offset at which the string begins.
 
Any previous parameters are like parameters you would pass to a regular sub (if you were making an assembly library or something). Parameters are pushed left to right, so your stack would look like this upon entering the assembly routine:
.
..
sp + 4 = rightmost parameter passed to routine (not the offset of the assembly routine
sp + 2 = return segment
sp = return offset
 
Parameters are passed to the routine by reference, by default, so the offsets of the variables are placed on the stack (DS contains the segment). You can pass variables by value by using BYVAL. Um.. here's a pointless routine.. it performs ShiftLeft on an integer:
 
DECLARE SUB addAsm (asmStr$, nextCmd$)
'
DIM number AS INTEGER
DIM count AS INTEGER
'
CLS
'
asm$ = ""
addAsm asm$, "55" 'push bp
addAsm asm$, "89E5" 'mov bp, sp
addAsm asm$, "8B5E08" 'mov bx, [bp+08]
addAsm asm$, "8B07" 'mov ax, [bx]
addAsm asm$, "8A4E06" 'mov cl, [bp+06]
addAsm asm$, "D3E0" 'shl ax, cl
addAsm asm$, "8907" 'mov [bx], ax
addAsm asm$, "5D" 'pop bp
addAsm asm$, "CA0400" 'retf 4
'
INPUT "Enter in a number: ", number
INPUT "Shift left how much?(1-15): ", count
DEF SEG = VARSEG(asm$) 'shouldn't be necessary, since data segment is default
CALL ABSOLUTE(number, BYVAL count, SADD(asm$))
DEF SEG
PRINT "Result:"; number
END
'
SUB addAsm (asmStr$, nextCmd$)
FOR i = 1 TO LEN(nextCmd$) STEP 2
asmStr$ = asmStr$ + CHR$(VAL("&H" + MID$(nextCmd$, i, 2)))
NEXT i
END SUB
 
BTW, remember to pay attention to the type of parameters you are passing (usually would just be integer, perhaps long integer). That would be a common thing to overlook.
 
*** Comment on this item
uhh... why not just use string concatenation from the beginning...
...instead of adding an unnecessary sub... If you're too lazy to type a few extra "&H"s, then perhaps you shouldn't be using call absolute (unless you want to use decimal... *shudder*).


[ The QBasic-MonsterFAQ --- Start Page: www.antonis.de/faq ]