Uporaba NASM kode s QuickBASICom

To je vodič in zbirka nasvetov za uporabo zbirniške (assembly language) kode v QuickBASIC programih z uporabo knjižnice (library). Osredotočil se bom na NASM, toda programerske tehnike delujejo z vsakim zbirnikom (assemblerjem). Pozor! To ni vodič za učenje zbirniškega jezika. Če imate kakšna vprašanja, me vprašajte.

Glavni vir za ta vodič je bila objava uporabnika po imenu "Artelius".

Splošna predloga

Ustvarite datoteko z besedilom, dajte ji končnico ASM, in jo odprite. Vanjo skopirajte naslednjo predlogo:

GLOBAL MojaProcedura

GROUP DGROUP

SECTION CODE

MojaProcedura:
	; vstavite kodo tukaj

Analizirajmo jo. Na začetku se s smernico GLOBAL določijo procedure (oznake, oz. labeli), ki bodo dostopne QuickBASIC programom. Vsaka procedura mora biti v svoji vrstici s svojo smernico GLOBAL. Vrstica GROUP DGROUP povezovalniku (linkerju) pove, da bo koda dostopala do spremenljivk v privzetem segmentu, ki ga QuickBASIC imenuje DGROUP (toda še vedno lahko dostopate do podatkov v drugih segmentih; glejte spodaj). SECTION CODE označuje začetek dejanske kode. Potem lahko začnete pisati procedure.

Procedure

Vse procedure (tako SUB procedure kot funkcije - FUNCTION) morajo slediti naslednji obliki (takoj za oznako):

	push bp
	mov bp, sp

	; sem vstavite telo procedure

	pop bp
	retf x

Vsi ukazi razen RETF pravzaprav niso potrebni, toda če želite dostopati do parametrov, ki vam jih posreduje QuickBASIC (oz. jih spreminjati, če so podani kot kazalci), jih boste potrebovali. Zamenjajte x za RETF s številom parametrov, ki so vam posredovani, množeno z dva (ker so 16-bitni). Za več informacij glede funkcij in parametrov glejte Napredne tehnike spodaj.

Prevajanje kode in izdelava knjižnice

Vaša koda je sedaj pripravljena na prevajanje in povezovanje. Ustvarite batch file z naslednjo vsebino:

@DEL %1.LIB
N:\NASM -o %1.OBJ -f obj %1.ASM
T:\LINK /Q %1.OBJ, %1.QLB,,T:\BQLB45.LIB;
T:\LIB %1.LIB +%1.OBJ

Zamenjajte N:\ in T:\ z lokacijama, kjer sta NASM in QuickBASIC, ali pa uporabite ukaz SUBST (DOS/Windows) ali MOUNT (DOSBox), da ustvarite navidezne pogone, ki kažejo nanju. Zdaj, ko ste ustvarili to datoteko, lahko vsakič, ko želite narediti NASM knjižnico, vtipkate ASMLINK imeKnjižnice in dobili boste knjižnico, nared za uporabo.

Deklaracije procedur za QuickBASIC

Preden lahko QuickBASIC uporablja katerokoli izmed procedur v vaši knjižnici, morajo biti te deklarirane. Priporočam vam, da si ustvarite datoteko BI, ki bo vsebovala te deklaracije. Te uporabljajo enako sintakso, kot če bi deklarirali BASICovske procedure (z ukazom DECLARE), toda nekatere parametre boste želeli posredovati po vrednosti (BYVAL), če jih ne boste spreminjali. Ko ste napisali in vključili ($INCLUDE) svojo datoteko BI, lahko svoje procedure začnete uporabljati.

Napredne tehnike

To je zbirka nasvetov, ki razložijo reči, ki že niso bile razložene zgoraj.

Registri

V splošnem lahko uporabljate vse registre.

Če je vaša procedura funkcija, se AX in DX uporabljata za vračanje vrednosti (glej spodaj).

QuickBASIC pričakuje, da bodo SI, DI, SP, BP, DS (se mi zdi), SS, in CS (razumljivo) na koncu procedure vsebovali enake vrednosti kot pred vstopom vanjo. Te registre lahko shranite na sklad (stack; PUSH) in jih nato prikličete (POP), toda včasih boste morali ohraniti SP, ker bo uničen. Lahko uporabite MOV ES, SP namesto PUSH in MOV SP, ES namesto POP (pravzaprav bo deloval vsak 16-bitni register, toda jaz uporabljam ES najmanj od vseh). Mislim, da QuickBASIC ne pričakuje, da bo ES ohranjen; ta tehnika je doslej zame delovala.

Začetne vrednosti registrov niso določene.

Vračanje vrednosti (funkcije)

Da lahko vaša procedura vrne vrednost, jo mora datoteka BI deklarirati kot funkcijo (FUNCTION). Postopek vračanja pa je odvisen od podatkovnega tipa:

Sledi primer funkcije, ki vrne INTEGER:

GLOBAL VrniPrivzetiPogon

GROUP DGROUP

SECTION CODE

VrniPrivzetiPogon:
	; Primer vračanja trenutnega privzetega pogona. Deklaracija:
	; DECLARE FUNCTION VrniPrivzetiPogon% ()

	mov ah, 19h	; DOS funkcija
	int 21h
	mov ah, 0	; Zavrzi višji bajt
	add al, 65	; Prevori v ASCII
	retf		; Vrni se v QB (rezultat je v AX)

Dostopanje do parametrov (na splošno)

QuickBASIC parametre posreduje na skladu. Dobite jih lahko s premiki (displacements), relativnimi na BP. Zadnji parameter ima najmanjši premik, ki je vedno enak 6 (besedi pod njo sta CS in IP pred vstopom v vašo proceduro). Na primer: če posredujete a%, b%, in c%, bo imel a% premik +6, b% +8, in c% +10. Premiki so vedno sodi, toda velikost parametrov je odvisna od njihovih tipov in načina posredovanja (po vrednosti ali po kazalcu). Lahko tudi mešate parametre, ki so posredovani po vrednosti in kazalcu; ni treba, da so vsi enaki. Ko se vaša procedura zaključi, mora imeti RETF ustrezen operand (glej Procedure zgoraj).

Dostopanje do parametrov po vrednosti

Posredovanje po vrednosti je, kar se branja tiče, najlažje. To velja še posebej za vrednosti tipa INTEGER. To storite tako, da vstavite BYVAL pred njihovim imenom v ukazu DECLARE v vaši datoteki BI. Tako posredovanih parametrov ne morete spreminjati, lahko pa jih berete neposredno, če jih premaknete v registre. Po vrednosti lahko posredujete le tipe INTEGER, LONG, SINGLE, in DOUBLE. Sledi primer branja spremenljivke tipa INTEGER po vrednosti:

GLOBAL IzpisiZnak

GROUP DGROUP

SECTION CODE

IzpisiZnak:
	; Primer branja in izpisovanja znaka, posredovanega po vrednosti. Deklaracija:
	; DECLARE SUB IzpisiZnak (BYVAL Koda%)

	; Postavi sklad
	push bp
	mov bp, sp

	mov ah, 2	; DOS funkcija (izpis znaka)
	mov dx, [bp+6]	; Znak, ki naj bo izpisan (višji bajt se ignorira)

	int 21h		; Izpiši ga

	; Nazaj v QB
	pop bp
	retf 2

Dostopanje do parametrov po kazalcu

Če mu ne ukažemo drugače (z BYVAL), QuickBASIC ne posreduje dejanskih vrednosti parametrov, temveč le kazalce nanje. Tako jih lahko tudi spreminjate. To ima tudi lep stranski učinek; ker dobite njihove odmike, vsak od njih zasede dva bajta na skladu. Branje vrednosti pa zahteva malo več dela: najprej morate premakniti odmik s sklada v register, šele potem lahko ta register uporabite kot kazalec. Sledi primer dostopanja do spremenljivke tipa INTEGER po kazalcu:

GLOBAL Povecaj

GROUP DGROUP

SECTION CODE

Povecaj:
	; Primer povečevanja INTEGER spremenljivke po kazalcu. Deklaracija:
	; DECLARE SUB Povecaj (Kaj%)

	; Postavi sklad
	push bp
	mov bp, sp

	mov bx, [bp+6]	; Preberi kazalec
	inc word [bx]	; Povečaj spremenljivko

	; Nazaj v QB
	pop bp
	retf 2

Dostopanje do "oddaljenih" parametrov (zunaj DGROUP)

Da dobite oddaljen parameter, najprej po vrednosti posredujte njegov segment (dobite ga s funkcijo VARSEG), nato pa ga posredujte s kazalcem kot ponavadi. Obstaja še en način, in sicer z uporabo SEG (namesto BYVAL; tako vam ni treba ročno posredovati segmenta), toda tega še nisem preizkusil.

Dostopanje do nizov

Opomba: sledeče informacije veljajo za QuickBASIC 4.5 in morda še nekatere druge verzije, toda tega nisem preizkusil. Ne veljajo za QuickBASIC 7.1 (najbrž tudi ne za Visual Basic za DOS), ker ta shranjuje nize na drugačen način.

Branje nizov (spreminjanja ne bom obravnaval; glej Klicanje notranjih procedur QB spodaj), ne glede, ali so dinamični ali fiksne velikosti (fixed-length; ti se začasno pretvorijo v dinamične nize), poteka na sledeč način: QuickBASIC vam posreduje odmik opisa niza (string descriptor). To je 4-bajtna struktura, ki vsebuje (v tem vrstnem redu) dolžino niza in kazalec (odmik) na njegovo vsebino. Oba sta 16 bitov široka. Lahko pa tudi ročno posredujete dolžino niza (dobite jo s funkcijo LEN) in odmik (dobite ga s funkcijo SADD). Zdaj, ko to veste, lahko uporabite indeksni register, da pokažete na niz in CX (ali katerikoli drug register) kot števec, in se v zanki premikate čez niz. Sledi primer branja "bližnjega" niza:

GLOBAL IzpisiNiz

GROUP DGROUP

SECTION CODE

IzpisiNiz:
	; Primer izpisovanja QuickBASIC niza. Deklaracija:
	; DECLARE SUB IzpisiNiz (Kaj$)

	; Postavi sklad
	push bp
	mov bp, sp
	push si		; SI moramo ohraniti

	mov si, [bp+6]	; Preberi kazalec na opis niza
	mov cx, [si]	; Preberi dolžino niza
	mov bx, [si+2]	; Preberi kazalec na vsebino niza
	mov ah, 2	; DOS funkcija (izpis znaka)

.naslednji:
	mov dl, [bx]	; Preberi znak
	int 21h		; Izpiši ga
	inc bx		; Pojdi na naslednjega
	dec cx		; Zmanjšaj števec
	jnz .naslednji	; Če še ni konec niza, ponovi

	; Nazaj v QB
	pop si
	pop bp
	retf 2

Dostopanje do spremenljivk uporabniških tipov

Uporabniški tipi (user-defined types) so le seznam navadnih spremenljivk, ki so shranjene ena za drugo. Izjema so nizi, ki so vedno fiksni in ne uporabljajo opisov; so le surovi bajti, toda brez kakršnegakoli načina, da bi dobili njihovo dolžino (vedeti jo morate vnaprej). Takšne strukture so vedno "oddaljene"; glejte Dostopanje do "oddaljenih" parametrov (zunaj DGROUP) zgoraj.

Dostopanje do polj

Polja (arrays) so očitno vedno shranjena "daleč" in najprej s stolpcem (column-major; toda mislim, da se prevajalnik da prisiliti, da najprej shrani vrstice). So le surovi seznami podatkov. Polja dinamičnih nizov so seznami opisov nizov in polja fiksnih nizov so le en fiksen niz za drugim brez podatka o dolžini (vedeti jo morate vnaprej). Da dobite začetek polja, uporabite funkcijo VARPTR s prvim elementom v polju.

Klicanje notranjih procedur QB

Iz svojih procedur lahko tudi kličete procedure, ki jih QuickBASIC uporablja za svoje ukaze in funkcije. Tega še nisem preizkusil. Da jih uporabite, jih deklarirajte s smernico EXTERN med vrsticama GROUP DGROUP in SECTION CODE. Na primer: če želite deklarirati B$SCAT, ki jo QuickBASIC uporablja za združevanje nizov (concatenation), vstavite EXTERN B$SCAT. Če želite raziskati, kako te procedure delujejo in kakšne parametre zahtevajo, vam priporočam, da prevedete nekaj programov s parametrom /A (na ukazni vrstici; prevajalnik se pokliče z ukazom BC), da dobite zbirniško kodo za ta program.


Prvič objavljeno .
Zadnjič posodobljeno .

Kazalo

Kontakt