Ú-9 |
Styk
mikropočítača s prostredím |
µ |
Rozhranie
prostredia assembler - Quick Basic |
|
q
|
Niektoré inštrukcie
QB pre styk s vonkajšim prostredím |
|
q
|
Vzorový tvar
assemblerovskej subrutiny volanej z prostredia QB |
|
q
|
Prostredie
jazyka Quick Basic |
|
|
µ
|
Príklad použitia
kompilačného variantu jazyka QB |
|
|
µ
|
Príklad použitia
interpretačnej verzie jazyka QB |
|
|
|
Ü
|
Kód subrutiny je ako DATA
súčasťou QB programu |
|
|
|
Ü
|
Kód subrutiny je ako znakový
reťazec súčasťou QB programu |
|
|
|
Ü
|
Programový kód subrutiny
je uložený mimo oblasť pamäte, používanej QB programom |
|
|
|
Ü
|
Programový kód subrutiny
je uložený rezidentne |

µ |
Rozhranie
prostredia assembler - Quick Basic |
Pri programoch spojených s ovládaním experimentálneho
zariadenia často sa tvorí program s použitím viacerých programovacích jazykov
(Multiple Langue Programming). Najčastejšie ide o prepojenie jazyka
vyššej úrovne a assemblera, pretože v prostredí jazyka vyššej úrovne
sa prijemnejšie programuje a vďaka assembleru možno realizovať rýchle a
flexibilné rutiny pre obsluhu kritických situácii.
Rozhranie subrutiny (Subroutine
Interface) tvorí vrstvu kódu v jazyku assemblera, ktorá umožňuje komunikovať
programu v jazyku vyššej úrovne s subrutinou v assembleri. Toto rozhranie
subrutiny pozostáva z dvoch častí:
-
riadiaceho rozhrania (control interface),
ktoré prenesie riadenie na volaný program a po vykonaní subrutiny vráti
riadenie späť hlavnému programu v jazyku vyššej úrovne.
-
rozhrania dát (data interface) , ktoré
umožní hlavnému programu a subrutine zdieľat dáta.
Hlavný program vo vyššom programovacom jazyku
nachádza cestu k assemblerovkej subrutine pomocou volania:
-
near CALL - keď subrutina je umiestnená
v rámci jedného 64 kB segmentu, čo býva v modeli programu small a compact
( ktoré možno použiť napríklad v jazyku C a C++).
-
far CALL - keď subrutina je lokalizovaná
mimo aktuálneho kodového CS segmentu, napríklad v modeli programu medium
(tento spôsob používa jazyk Basic) a v modeli large (používaný napríklad
v jazyku C a C++ ).
Na konci oboch typov volanej assemblerovskej subrutiny
sa musí nachádzať inštrukcia RET (far alebo near). Subrutina tiež musí
poznať akú má podporu zo strany volacieho programu, napríklad že code segment
je CS, že pre dáta je určený segment DS, či adresa SS:SP, určujúca adresu
v zásobníku je spoločná s volacím programom alebo či subrutina potrebuje
zabezpečiť si svoj zásobník s väčším priestorom a pod. Ak sa prenáša medzi
volajúcim programom a subrutinou informácia (parametre) musí subrutina
poznať, na základe deklarovania v programe, koľko paramatrov existuje,
akeho typu dát sú a kde sa nachádzajú.

Parametre z volajúceho programu
do subrutiny a opačne sa obvykle prenášajú cez zásobníka
to priamo ako hodnota parametra alebo nepriamo ako adresa parametra. Aká
metóda sa používa to závisí od programovacieho jazyka. Napríklad v jazyku
C a C++ sa prenáša hodnota parametra, v jazyku Basic adresa
parametra. Pri prenose riadenia na subrutinu a späť musí byť zabezpečená
neporušenosť informácie, napríklad počas predefinovávania segmentových
registrov musí byť zablokované prerušenie. Na začiatku subrutiny musia
byť uschované a po skončení subrutiny znova obnovené obsahy používaných
registrov a tiež aj obnova hlavných dvoch segmentových registrov BP a DS,
pomocou ktorých sa pristupuje k dátam.

Ako príklad jazyka vyššej úrovne dobre môže poslúžiť
Basic (Beginnera All - purpose Symbolic Instruction Code), ako jazyk jednoduchý
a vhodný pre demonštrovanie rozhrania prostredia assembler - prostredie
jazyka vyššej programovacej pre rôzne programy na ovládanie experimentálneho
zariadenia.

1. |
Niektoré
inštrukcie Quick Basic pre styk s vonkajšim prostredím |
Jazyk Quick Basic (skrátene QB, resp. QBasic)
obsahuje niekoľko príkazov, pomocou ktorých možno nesprostredkovane zabezpečiť
vstup a výstup dát z paralelného a sériového rozhrania, ovládať timer ako
aj možnosť volať subrutinu napísanú v strojovom kóde.
Niektoré
inštrukcie QB na ovládane portov
a nastavenie segmentu:offsetu. |
|
INP (adr_reg) |
- vstup z portu, napríklad
INP 91 alebo INP &H61 |
|
OUT adr_reg,data |
- výstup údaja cez port, |
|
|
napríklad OUT &H41,&HB6 |
|
CLEAR, ,
stack |
- vynuluje všetky programové
premené a nastaví zásobnik (napríklad
clear,,200 na 200 bajtov) |
|
POKEadresa,
byte |
- uloži bajt na adresu
v pamäti |
|
PEEKadresa |
- zistí obsah (bajt )
na adrese |
|
DEF SEG[=adresa] |
- nastaví segmentový
register |
|
VARPTR(variablename) |
- zistí offset
adresy premenej - variblename |
|
VARSEG(variablename) |
- zistí segment adresy
premenej -variblename |
|
CALL
ABSOLUTE ([argumentlist,] integervariable) |
|
|
- volanie rutiny s uvedenými
parametrami, ktorá sa začína na relatívnom posuve - offset (s hodnotou
integervariable) od začiatku segmentu. |

u
Príklady použitia príkazov Quick Basic
Out_ex.bas
Príklad použitia inštrukcie OUT na ovládania
timera s cieľom generovať tóny na reproduktor.
|
'*** Out_ex.bas
- OUT statement programming example |
|
|
|
' Play a scale using speaker
and timer |
|
CONST WHOLE=5000!, QRTR=WHOLE/4. |
|
CONST C=523.0, D=587.33,
E=659.26, F=698.46, G=783.99, A=880.00 |
|
CONST B=987.77, C1=1046.50 |
|
CALL Sounds(C,QRTR) :
CALL Sounds(D,QRTR) |
|
CALL Sounds(E,QRTR) :
CALL Sounds(F,QRTR) |
|
CALL Sounds(G,QRTR) :
CALL Sounds(A,QRTR) |
|
CALL Sounds(B,QRTR) :
CALL Sounds(C1,WHOLE) |
|
|
|
SUB
Sounds (Freq!,Length!) STATIC |
|
'Ports 66, 67, and 97
control timer and speaker |
|
' |
|
'Divide clock frequency
by sound frequency |
|
'to get number of "clicks"
clock must produce |
|
Clicks%=CINT(1193280!/Freq!) |
|
LoByte%=Clicks% AND &H00FF |
|
HiByte%=Clicks%\256 |
|
'Tell timer that data
is coming |
|
OUT 67,182 |
|
'Send count to timer |
|
OUT 66,LoByte% |
|
OUT 66,HiByte% |
|
'Turn speaker on by setting
bits 0 and 1 of PPI chip. |
|
SpkrOn%=INP(97) OR &H03 |
|
OUT 97,SpkrOn% |
|
'Leave speaker on |
|
FOR I!=1 TO Length! : NEXT I! |
|
'Turn speaker off. |
|
SpkrOff%=INP(97) AND &HFC |
|
OUT 97,SpkrOff% |
|
END SUB |

DefSg_ex.bas
Príklad použitia inštrukcie POKE, PEEK a DEF SEG
na modifikovanie stavovej informácie (v uvedenom príklade informácie o
stave prepinača Caps Lock) v tabuľke BIOS parametrov (v pamäti na adrese
400:17).
|
' *** DEFSG_EX.BAS
*** |
|
' |
|
DECLARE SUB CapsOn () |
|
DECLARE SUB CapsOff () |
|
DECLARE SUB PrntMsg (R%,C%,M$) |
|
|
|
CLS |
|
CapsOn |
|
PrntMsg 24,1,"<Caps
Lock On>" |
|
LOCATE 12,10 |
|
LINE INPUT "Enter a string
(all characters are caps): ",S$ |
|
CapsOff |
|
PrntMsg 24,1," " |
|
PrntMsg 25,1,"Press any
key to continue..." |
|
DO WHILE INKEY$="" : LOOP |
|
CLS |
|
END |
|
|
|
SUB
CapsOn STATIC |
|
' Turn Caps Lock on |
|
' Set segment to low memory |
|
DEF SEG = 0 |
|
' Set Caps Lock on (turn
on bit 6 of &H0417) |
|
POKE &H0417,PEEK(&H0417) OR &H40 |
|
' Restore segment |
|
DEF SEG |
|
END SUB |
|
|
|
SUB
CapsOff STATIC |
|
' Turn Caps Lock off |
|
DEF SEG=0 |
|
' Set Caps Lock off (turn
off bit 6 of &H0417) |
|
POKE &H0417,PEEK(&H0417) AND &HBF |
|
DEF SEG |
|
END SUB |
|
|
|
SUB
PrntMsg (Row%, Col%, Message$) STATIC |
|
' Print message at Row%,
Col% without changing cursor |
|
' Save cursor position |
|
CurRow%=CSRLIN : CurCol%=POS(0) |
|
LOCATE Row%,Col% : PRINT Message$; |
|
' Restore cursor |
|
LOCATE CurRow%,CurCol% |
|
END SUB |

2. |
Vzorový
tvar assemblerovskej subrutiny volanej z prostredia Quick Basic |
Rozhranie pre assemblerovskú subrutinu volanú
z prostredia jazyka Basic (resp. Quick Basic) implicitne
používa pamäťový modul assemblera - medium memory small,
to znamená že pripušťa:
-
použitie viacerých segmentových registrov pre kód
programu a
-
spoločný segment register pre dáta.
Pri volaní subrutiny používa sekvenciu far
call - ret, takže procedúra volanej subrutiny musí byť deklarovaná
ako far PROC.
QBasic predpokladá, že volaná assemblerovská subrutina
v priebehu svojho vykonania môže zničiť obsah registrov AX, BX, CX, DX,
SI, DI a BP a preto nie je treba ich obsah rekonštruovat po návrate do
Basicu. Ovšem obsah segmentových registrov a zásobníka musí byť obnovený.
Subrutina musí poznať počet, typ a dĺžku parametrov,
ktoré sa presúvaju do subrutiny z Basicu. V prípade, že sa bude spájať
assemblerovská procedúra s programom Basic pomocou linkera mala by byť
táto procedúra
-
na začiatku assemblerovej procedúry deklarovaná ako
PUBLIC ;
-
na začiatku Basic časti programu deklarovaná pomocou:
|
DECLARE SUB meno_SBR (parm_list). |

u
Používanie zásobníka
Ako prvý krok v procedúre subrutiny je uschovanie
aktuálneho obsahu zásobníka SP pomocou jeho presunu do registra BP pomocou
inštrukcie - mov BP,SP.
Ak sa prenašaju napríklad 3 parametre (v tvare
2 bajtových dát) do volanej subrutiny [napríklad v GW Basic pomocou call
sbr (a, b, c), resp. v prípade Quick Basicu call absolute ( a, b, c, offset)],
tak najhlbšie v zásobníku t.j na najvyššej adrese zásobníka bude prvý parameter
a , nižšie bude b a nad nimi paramater c. Návratová adresa segment:offset
sa pri odskoku z hlavného programu ukladá ako posledná. Na adrese BP+4
bude automaticky uložený segment návratovej adresy a na adrese BP+2 zase
offset návratovej adresy (pretože procedúra je volaná pomocou far call):
demo |
adresa zásobníka: |
adresa = BP+10 |
=> prvý parameter - a |
|
|
adresa = BP+8 |
=> druhý parameter - b |
|
|
adresa = BP+6. |
=> tretí parameter - c |
|
|
adresa = BP+4 |
=> segment adresa návratu |
|
|
Adresa = BP+2 |
=> offset adresa návratu. |
V rámci volanej subrutiny sa potom môžu ďalej
do zásobnika ukladať napríklad pôvodná poloha zásobníka OLD_Stack atď.,
max. však 16 bajtov. Ak by bolo treba uschovať viac údajov treba predefinovat
polohu zásobníka inde a vytvotiť pre subrutinu vlasný zásobník.
Na konci subrutiny musí byť obnovený stav ukazovateľa
zásobníka SP pomocou zodpovedajúceho početu inštrukcii POP alebo RET
n, (kde n je 2*(počet parametrov v zásobniku), t.j. parametrov vyskytujúcich
sa v príkaze inštrukcie Call). Výstupná hodnota parametra zo subrutiny
sa po návrate do hlavného programu uloži do premenej s príslušnym menom,
napríklad do c, a, b a pod.
Ak je argumentom v príkaze Call reťazec,
potom ma tento parameter vo vezii QBasic 4 bajtovú dĺžku v tvare tzv. String
descriptora. Prvé 2 bajty reťazcového deskriptora tvorí 16 bit údaj dlžky
reťazca (1 bajt je LSB, 2 bajt je MSB údaja dĺžky), a ďalšie 2 bajty obsahujú
informáciu o adrese začiatku reťazca (3 bajt je LSB a 4 bajt je MSB adresy).
(Na rozdiel od GW BASIC, kde je string desctiptor 3 bajtový s dĺžkou reťazca
max FFH, tj 1 bajt). Dĺžka reťazca sa nesmie v priebehu programu menit.
Može sa však modifikovat, napríklad reťazec A$="BASIC"+"" može byť zamenenený
rovnakým počtom iných znakov.

Napríklad:
ak premenne A, C sú typu integer , B$ je reťazec.
a v Basic časti programu sa vyskytuje volanie assemblerovského programu
:
|
|
CALL SBR (A,B$,C) |
;=> 3 parametre |
mohla by mať časť assemblerovskej procedúry spojená
s prenosom parametrov A,C a B$ do subrutiny nasledovný tvar :
title Vzorová Sbr.com
pre Basic program - CALL SBR (A,B$,C) |
|
.MODEL |
small |
|
|
.CODE |
|
|
sbr |
proc |
far |
;Toto je vzorova sbr |
|
push |
bp |
;Uschova BP |
|
mov |
bp,sp |
|
|
mov |
bx,[bp]+8 |
;adr. Parametra B$ |
|
mov |
cx,[bx] |
; 2 bajt dlzka stringu
do cx |
|
mov |
dx,2[bx] |
; =>mov dx,[bx+2], 1 znak
z B$ do dx |
|
mov |
si,[bp]+10 |
;adr. parametra a |
|
mov |
ax,[si] |
;parameter a do ax |
|
..... |
|
;tu by nasledovalo telo rutiny
|
|
mov |
di,6[bp] |
;mov di,[bp+6] adr. parametra
c |
|
mov |
[di],ax |
;=> výstup (ax) ako parametra
c |
|
Pop |
bp |
;obnova BP na záver rutiny |
|
Ret |
6 |
; ret n , kde n= 2*3 param=6 |
sbr |
Endp |
|
|
|
End |
sbr |
|

3. |
Prostredie
jazyka Quick Basic |
Basic (resp. jeho verzie GW Basic alebo Quick
Basic) umožňuje vytvoriť spustiteľný program rôznym spôsobom. Buď prostredie
Basic funguje ako:
-
jazyk interpretačný
(vhodný najmä na odlaďovanie programov, keď každý príkaz sa pred vykonaním
prekladá z basicovského tvaru na momentálne vykonateľý príkaz);
-
jazyk kompilačný (
vhodný pre odladené programy v interpretačnom
prostredí . Takýto program
sa prekompiluje do objektívneho kódu za pomoci kompilátora BC.exe a knižnice
BCCOM45.lib, resp. BRUN45.lib, podobne ako assemblerovský program a potom
sa spojí dokopy napríklad z assemblerovskou časťou pomocou link.exe)
1) |
Príklad
použitia kompilačného variantu jazyka QBasic |
MedAbs.bas - príklad
spojenia programov z prostredia assembler a QBasic.
Ako príklad kompilovaného programu v jazyku Qbasicmôže
slúžiť jednoduchý výpis čísel (od -10 do 10) a ich absolutnych hodnôt,
v ktorom sa prevod čísla na absolutnu hodnotu vykoná pomocou assemblerovskej
procedúry Medabs.asm. Pre vytvorenie spustiteľného programu:
-
hlavný QBasic program Medabs.bas, po preložení do
objektívneho kódu pomocou kompilátora BC.exe sa prevedie na tvar madabs.obj
a
-
potom zlinkuje spolu so skompilovanou časťou assemblerovej
procedúry medabs.obj na spustiteľný program medabs.exe.
|
------------------------------------------------------------------ |
|
QuickBasic
časť Medabs.bas - listing |
|
DEFINT A-Z
‘ => impicitne všetky znaky sú premené typu integer |
|
DECLARE
SUB MEDABS (A%) ’deklarovanie assembler. subr |
|
FOR X = -10
TO 10 |
|
|
Y = X |
|
|
CALL MEDABS(Y) ‘
volanie subrutiny |
|
|
PRINT "ABS ("; X; ")=";
Y |
|
NEXT |
|
END |
|
----------------------------------------------------------------------- |
MEDABS |
_TEXT
SEGMENT byte public 'code' |
ASSUME |
cs:MEDABS_TEXT |
|
PUBLIC
MEDABS
; kvôli linkeru |
MEDABS |
PROC
far
;far CALL |
|
push bp |
|
mov bp, sp |
|
mov bx, [bp+6] |
|
mov ax, [bx] |
|
cwd
;*/poznámka dolu. |
|
xor ax, dx |
|
sub ax, dx |
|
mov [bx],
ax |
|
pop bp |
|
ret 2 |
MEDABS |
ENDP |
MEDABS_TEXT |
ENDS |
|
END |
*/ Poznámka:
inštrukcia cwd rozšíri znamienko slova z AX do
registra DX =>
-
ak je obsah (AX) < 8000H tak doplní 0 do DX, lebo
údaj sa chápe ako kladné číslo so znamiemkovým bitom S=0,
-
ak je obsah AX >7FFFH tak je údaj chápaný ako záporné
číslo s S=1 a obsah DX sa dopní jednotkovými bitmi.
u
Poznámky k spôsobu odlaďovania skompilovaného programu medabs.exe
Pri oskúšavaní zlinkovaného programu často treba
overiť činosť assemblerovkej subrutiny v rámci celkového programu. Možno
to vykonať pomocou debuggera napríklad na základe nasledovného postupu:
-
Spustiť zlinkovaný program medabs.exe cez debugger
napríklad pomocou príkazu:
-
Nájsť v kóde programu inštrukciu pop BP, s kódom
55H ktorá stojí na začiatku assemblerovskej subrutiny pomocou príkazu Search
z debuggera. Za predpokladu, že obsah registrov nastavený na začiatku zlinkovaného
programu CS:IP = 21E8:23, tak hľadací príkaz bude mať tvar: (Číslo 1778
= (CX) reprezentuje celkový počet bajtov zlinkovaného programu, v ktorom
sa hľadá.)
-
Po najdení správneho začiatku subrutiny treba ešte
prepísať kód 55H na kód CCH, ktorý reprezentuje BreakPoint, kde sa program
zastaví, napríklad príkazom:
|
e 21FF:0 |
vypíše 55 |
a treba ho zameniť CC. |
-
Takto upravený program sa spustí v debuggeri príkazom
- g a v dôsledku prevedených úprav sa zastaví na práve vytvorenom Break
Pointe - CCH. Od tejto adresy po spätnom obnovení obsahu bunky z CCH na
55H ho možno ďalej krokovať po inštrukciach alebo ho nechať bežať ďalej
príkazom - g.

2) |
Príklad
použitia interpretačnej verzie jazyka QBasic |
Pri používaní QBasic ako interpretačného jazyka
existuje viacero možností ako uložiť kód assemblerovskej procedúry do
pamäti, resp. ako ju pripojiť k k hlavnému QBasic programu. V najjednoduchšom
prípade pri krátkych assemblerovských procedúrach môže tvoriť kód procedúry
časť QBasic programu buď v tvare reťazca v alebo tvare dát, ktoré sa uložia
na želanú adresu (pomocou inštrukcie POKE ptr, data) do pamäti vyhradenej
pre QBasic program. Táto oblasť pamäte musí byť na začiatku programu vyhradená
pomocou príkazu DIM alebo príkazu CLEAR. Začiatok uloženého programového
kódu sa zistí pomocou príkazu Varptr (offs) a Varseg (segm). Pred volaním
assemblerovskej subrutiny treba predefinovať segment register (pomocou
príkazu Def seg), resp. po návrate obnoviť pôvodný segment QBasic programu.

Ü |
Programový
kód subrutiny je ako DATA súčasťou QB programu |
Príkladom uloženia kódu assemblerovskej procedúry
do pamäti v tvare dát môžu slúžiť nasledujúce programy, v ktorých je pre
data na začiatku programu vyhradená oblasť do ktorej sa potom uloží kód
programu, či už v tvare:
-
dekadickom (Call_abs2.bas) alebo
-
hexadecimalnom (Call_abs1.bas).
Príklady ilustrujú použitie inštrukcie VARPTR, VARSEG
a CALL ABSOLUTE. Vlastná assemblerovská procedúra pozostáva zo strojového
kódu, ktorý sa pomocou POKE uloží do zadeklarovanej oblasti pamäte a neskôr
sa vyvolá na základe absolútnej adresy pamäte, kde bol uložený. Z hľadiska
volania subrutiny je dôležité, aby mu predchádzalo predefinovanie segmentu
CS, v ktorom je assemblerovský kód uložený a po návrate zase obnovenie
segmentu QBasic programu.

V niektorých QB programoch je použité zjednodušené
volanie call (bez špecifikácie segmentu v príkaze), v tvare akom ho používa
verzia QBasicu, ktorá je súčasťou DOS. Niektoré verzie QBasicu vyžadujú
ale, aby:
-
na začiatku hlavného programu bol deklarovaný tvar
volania call:
|
DECLARE SUB absolute (BYVAL
Num, BYVAL Offs, BYVAL Segm) |
-
bol v programe dodržaný syntax príkazu volania Call
subrutiny v tvare:
|
CALL absolute
(BYVAL Num, BYVAL Offs) |
|
|
Num je paramater prenášaný
z/do procedúry |
|
|
Offs je relatívne posunutie
(offset) voči začiatku segmentu. |
|
|
Segment subrutiny je totožný
s segmentom QBasic programu. |
|
|
Klauzula BYVAL deklaruje
skutočnú kópiu hodnoty parametrov do subrutiny, nie adresu paramatrov. |
Čo v danom príklade predstavuje
tvar: |
|
CALL absolute (X%, VARPTR(AsmProg(1)),
VARSEG(AsmProg(1))). |
Resp. |
CALL absolute(X%, P, VARSEG(AsmProg(1))) |

Call_abs1.bas -
kód subrutiny v tvare hexadecimálnych dát
Táto demonštrácia slúži ako príklad použitia volania
assemblerovskej subrutiny, ktorej kód je súčasťou hlavného QBasic programu
v tvare dát v hexadecimálnom kóde.
Úlohou demonštračného podprogramu je na základe
výsledku testu vykonaného pomocou INT 11H zistiť či je v počítači zapojený
matematický koprocesor. (O tom, či je alebo nie je kooprocesor prítomný
rozhoduje bit 2 stavového slova, zisteného ako výsledok testu pomocou INT
11H). Z hľadiska uloženia do pamäte kód assemblerovského programu je reprezentovaný
v uvedenom príklade v tvare dát v hexadecimálnom tvare od náveštia AsmBytes,
ktoré sa uložia do pamäte, pomocou inštrukcie POKE od adresy, vyšpecifikovanej
pomocou príkazov VARPTR a VARSEG. (V príkaze volania subrutiny CALL absolute(X%,
P, VARSEG(AsmProg(1))) je deklarovaný len 1 parameter X%. Pri vstupe do
subrutiny sa nepoužíva žiadny parameter, ale pri návrate do QBasicu treba
preniesť hodnotu parametra - X% s informáciou o hardware počítača).
Callabs1.bas - listing |
CONST nASMBYTES = 14 :
’dlžka assembler. kódu |
DEFINT A-Z |
DIM AsmProg(1 TO (nASMBYTES
/ 2)) |
AsmBytes: |
|
DATA &HCC |
: 'PUSH BP |
Save base pointer. |
|
DATA &H8B,
&HEC |
: 'MOV BP,SP |
Get our own. |
|
DATA &HCD,
&H11 |
: 'INT 11H |
Make the BIOS call |
|
DATA &H8B,
&H5E, &H06 |
: 'MOV BX,[BP+6] |
Get argument address |
|
DATA &H89,
&H07 |
:'MOV [BX],AX |
Save list in argument |
|
DATA &H5D |
: 'POP BP |
Restore base pointer |
|
DATA &HCA,
&H02, &H00 |
: 'RET 2 |
Pop argt + far return. |
'P je začiatočná
adresa oblasti s assemblerovským programom. |
P = VARPTR(AsmProg(1)) |
DEF SEG=
VARSEG(AsmProg(1))
' Zmena segmentu. |
RESTORE AsmBytes |
FOR I = 0 TO nASMBYTES
- 1
‘Uloženie do pamäti |
|
READ J |
|
PRINT J
; ‘kvôli demonštrácii |
|
POKE
(P + I), J
'uloží stroják do oblasti pamäte |
NEXT I |
CALL ABSOLUTE (X%,
VARPTR(AsmProg(1))) |
DEF SEG
'Obnova QBasic segmentu. |
' X% má bitovy obrazec
v ktorom treba zamaskovat všetko okrem bit 2 |
CoProcessor = X% AND &H2 |
IF CoProcessor = 2 THEN
' Print výsledny oznam |
PRINT "Math coprocessor
present." |
ELSE |
PRINT "No math coprocessor." |
END IF |
END |

Call_abs2.bas -
kód subrutiny pozostáva z dát v desiatkovom tvare
Príklad ilustruje uloženie kódu subrutiny do QBasic
oblasti pamäte ako DATA v BCD tvare do priestoru vyhradenom
pre premenú a%(). Assemblerovská subrutinu, ktorá vykoná Prtscr obrazovky
a pozostáva len z 2 inštrukcii :
V príkaze volania call netreba preniesť žiadny
parameter a preto je v príkaze Call len adresa offsetu VarPtr(a%(0)).
Callabs2.bas - listing |
DIM a%(2) |
DEF SEG = VARSEG(a%(0)
' Zmena segmentu. |
'RESTORE Data |
FOR i% = 0 TO 2 |
|
READ d% |
|
POKE (VARPTR(a%(0)+i%),
d%
' uloži strojak do oblasti pamäte |
NEXT i% |
DATA 205,5,203 '=>INT
5 retf |
CALL ABSOLUTE(VARPTR(a%(0))
'Program neočakáva parameter |
DEF SEG ' Restore the
segment |

Ü |
Programový
kód subrutiny je ako znakový reťazec súčasťou QB programu |
Bfig108.bas - kód
subrutiny Bfig108.exe v tvare znakového reťazca
Program Bfig108.bas demonštruje rolovanie obrazovky
pomocou kódu subrutiny Bfig108.asm. Kód assemblerovského podprogramu je
uložený ako znakový reťazec v hlavnom QBasic programe. Výpis
kódu subrutiny Bfig108.exe zistený pomocou Debug je:
|
1185:0000 |
55 8B EC 8B 76 06 8B 0C-0A
C9 B7 70 B8 01 06 75 |
|
1185:0010 |
0C B9 00 02 BA 10 10 CD-10
5D CA 02 00 B9 14 05 . |
|
1185:0020 |
BA 24 12 EB F2 |
Tento kód ako reťazec (P$ +CHR$(&H55)+...)
je súčasťou hlavného QBasic programu. Začiatočná adresa subrutiny sa zistí
pomocou Varptr (P$) a Varseg (P$). Za pozornost stoji použitie SP a BP
v Basic prikaze volania Call absolute (L, entry%), keď do zásobnika sa
uloží najprv adresa parametra L=0 alebo R=1 a až potom návratová adresa
IP a CS.
|
Bfig108.bas - listing |
|
DEFINT A-Z |
|
CLEAR , , 200 'REZERVOVANIE
PAMATI PRE PROGRAM =>P$=segm,0-200 |
|
P$ = CHR$(&H55) +
CHR$(&H8B) + CHR$(&HEC) + CHR$(&H8B) |
|
P$ = P$ + CHR$(&H76)
+ CHR$(&H6) + CHR$(&H8B) + CHR$(&HC) |
|
P$ = P$ + CHR$(&HA)
+ CHR$(&HC9) + CHR$(&HB7) + CHR$(&H70) |
|
P$ = P$ + CHR$(&HB8)
+ CHR$(&H1) + CHR$(&H6) + CHR$(&H75) |
|
P$ = P$ + CHR$(&HC)
+ CHR$(&HB9) + CHR$(&H0) + CHR$(&H2) |
|
P$ = P$ + CHR$(&HBA)
+ CHR$(&H10) + CHR$(&H10) + CHR$(&HCD) |
|
P$ = P$ + CHR$(&H10)
+ CHR$(&H5D) + CHR$(&HCA) + CHR$(&H2) |
|
P$ = P$ + CHR$(&H0)
+ CHR$(&HB9) + CHR$(&H14) + CHR$(&H5) |
|
P$ = P$ + CHR$(&HBA)
+ CHR$(&H24) + CHR$(&H12) + CHR$(&HEB) + CHR$(&HF2) |
|
|
|
Entry! = (PEEK(VARPTR(P$) + 2) + PEEK(VARPTR(P$)
+ 3) * 256) |
|
IF entry! > 32768 THEN
entry% = entry! - 65536! ELSE entry% = entry! |
|
A$ = "ABCDEFGHIJKlmnopr" |
|
L = 0: R = 1 |
|
LOCATE 1, 1: PRINT "Window
scrolling example!" |
|
FOR i = 0 TO 1000'pocet
zopakovani zobrazenia => dlzka procedury |
|
LOCATE 17, 1: PRINT A$ |
|
CALL absolute(L,
entry%) |
|
LOCATE 19, 21: PRINT A$ |
|
CALL absolute(R,
entry%) |
|
A$ = RIGHT$(A$, 16) +
LEFT$(A$, 1)'posuv znakov v retazci |
|
NEXT I |
|
STOP |

Bfig108.asm -
procedúra uložená v tvare znakového reťazca v programe Bfig108.bas
Dôležitá súčasť tvoriaca assemblerovskú subrutinu,
ktorá potom v hlavnom QB programe vykonáva rolovanie obrazovky. Pomocou
INT10/AH=6 roluje okno na obrazovke zadefinované obsahom registrov CH,
CL (riadok, stlpec vľavo hore), a obsahom registrov DH, DL (riadok, stlpec
vpravo dolu) o AL=1, t.j. o 1 šírku znaku => 8 liniek. Ak bude zvolený
atribut rolovaného znaku BH=70h, tak plocha okna sa bude posúvať
v inverznom zafarbení.
TITLE
|
oooooooooooo Scroll Routine
for BASIC oooooooooooo |
CODE
|
segment |
Assume
|
cs:code |
SCROLL
|
Proc far |
|
Push bp
;Pri debug sem ulozit CCH |
|
Mov bp, sp |
|
Mov si, [bp+6]
;Adresa parametra |
|
Mov cx, [si]
;Uložit parameter do CX |
|
Or cl, cl |
|
Mov bh, 70H
;Atribut invert - nazorne |
|
Mov ax, 601h
;INT10H/AH=6,1 širka znaku |
|
Jnz window1
;Určiť ktoré okno |
|
;------------------------Okno
0 --------------------------- |
|
Mov cx, 200h
;Okno 0 (2,0) - (10,10) |
|
Mov dx, 1010h |
DO_SCROLL: |
;CH,CL riadok,stlpec zač vľavo |
|
Int 10h
;DH,DL riadok,stlpec vpravo dol |
|
Pop bp
;BH videoatribut |
|
Ret 2
;AL počet rolujúcich liniek |
|
;------------------------Okno
1 ------------------------------- |
WINDOW1: |
|
|
Mov cx, 514h
;Okno 1 (5,14)-(12,24) |
|
Mov dx, 1224h |
|
Jmp do_scroll |
SCROLL
|
Endp |
CODE
|
Ends |
|
End |

Poznámky k spôsobu odlaďovania
programu Bfig108.bas
Pri odlaďovaní programu Bfig108.bas može vzniknuť
potreba overiť si činosť assemblerovkej subrutiny v rámci celkového programu.
Možno to vykonať pomocou debuggera podobným postupom ako bol uvedený v
príklade medabs.exe, napríklad na základe nasledovného postupu:
-
Spustiť QBasic cez Debug, napríklad pomocou príkazu:
-
Pred spustením programu v reťazci P$ kód &H55,
reprezentujúci inštrukciu pop BP, s kódom 55H ktorá stojí na začiatku subrutiny
nahradiť kódom &HCC, reprezentujúcu int3 => BreakPoint.
-
Hlavný program sa po spustení zastaví na int3 a možno
ho ďalej odladovať. Najskôr však treba obnoviť namiesto kódu CCH naspät
kód 55H.

Ü |
Programový
kód subrutiny je uložený mimo oblasť pamäte, používanej QB programom |
Cls7C.bas -
program používajúci subrutinu cls7C.com v tvare cls7C.mod
Príklad, v ktorom kód assemblerovskej procedúry
cls7C.com, demonštrujúcej vymazania obrazovky (ekvivalent CLS ), je uložený
pomocou debugera mimo predpokladanú oblasť používania Qbasic prostredia
(čo bolo veľmi jednoducho realizovateľné v jazyku GW Basic, nakoľko adresovanie
programu požívalo len jeden 64 kB segment. Pre kratšie programy s menším
počtom údajov je použiteľné aj v jazyku QBasic.) Po naslednom zapísaní
programového kódu pomocou inštrukcie BSAVE na disk sa môže podľa potreby
pomocou BLOAD nahrať do prostredia Qbasic. V prípade potreby po zadefinovaní
segmentu a offsetu ho možno zavolať pomocou inštrukcie (offset adresy začiatku
je definovaný v príkaze call hodnotou adresa):
|
Call absolute (zoznam_parmetrov,
adresa). |

Cls7C.bas - listing
hlavného programu
slúži na demonštráciu použitia program cls7C.com,
zapísaného ako súbor ako cls7C.mod a neskôr uloženého do pamäti od adresy
blízko ku koncu segmentu, napr. 7FC0:0H.
|
CLEAR , ,
100 |
'rezerva zásobníka pre
program |
|
BLOAD"c:\qbasic\hrdw\cls7C.mod" |
'uloženie subrutiny |
|
DEF SEG =
&H7FC0 |
' segment subrutiny CLS7C |
|
clh = 0 |
'clh vo funkcii ofsetu
BYVAL Offs |
|
CALL absolute(clh) |
|
|
DEF SEG |
'obnova pôvodneho segmentu
QB |
|
FOR i = 0
TO 16 |
|
|
|
PRINT PEEK(&H0 + I) |
'kvôli kontrole výpis
kódov |
|
NEXT i |
'inštrukcii v decimal.
tvare |
|
CALL absolute(clh) |
|
|
DEF SEG |
'obnova pôvodneho segmentu
QB |
|
INPUT "hodiny";
H |
|
|
INPUT "nasobok";
R |
|
|
W = H * R |
|
|
PRINT "vaha=";
W |
|
|
STOP |
|

cls7C.com - listing assemblerovskej
procedúry
Výpis assemblerovskej subrutiny cls7C.com na vymazanie
obrazovky, ziskaný pomocou debuggera, ktorý bude slúžiť ako subrutina QB
programu:
|
1164:0100 |
55 |
PUSH |
BP |
|
1164:0101 |
8BEC |
MOV |
BP,SP |
|
1164:0103 |
E80200 |
CALL |
0108 |
|
1164:0106 |
5D |
POP |
BP |
|
1164:0107 |
CB |
RETF |
|
|
1164:0108 |
B80006 |
MOV |
AX,0600 |
|
1164:010B |
B707 |
MOV |
BH,07 |
|
1164:010D |
B90000 |
MOV |
CX,0000 |
|
1164:0110 |
BA4F18 |
MOV |
DX,184F |
|
1164:0113 |
CD10 |
INT |
10 |
|
1164:0115 |
C3 |
RET |
|
Modifikovaný tvar assemblerovskej
procedúry cls7C.mod
Kód assemblerovského podprogramu cls7C.com može
byť pomocou príkazov Basic zapísaný do súboru clh7f.mod (v
uvedenom prípade ako c:\qbasic\hrdw\cls7C.mod) pre potreby ďalšieho použitia
v QBasic programe. (Takýto spôsob používal GW Basic, v ktorom vlastný program
nepresahoval dlžku jedného 64kB segmentu a bolo jednoduché špecifikovať
oblasť pamäte, používanú pre GW Basic.) Postup je teda nasledovný:
-
Najskôr treba odladený program cls7C.com pomocou
debuggera presunúť do oblasti pamäte, mimo oblasť pamäte rezervovanú pre
hlavný program v jazyku Basic, napr. od adresy 7FC0:0. (Pomocou príkazu
debuggera m - move sa presunie kód o dĺžke 16H bajtov príkazom:)
-
Po presune vykoná uloženie obsahu pamäte od adresy
7FCOH o dĺžke 16H bajtov do súboru cls7C.mod nasledujúci krátky QB program.
(Využíva QBasic príkaz: BSAVE filename, offset, dlžka).
|
CLEAR , , 200 |
|
DEF SEG=&H7FC0 |
|
BSAVE"CLS7C.mod",0,&H16 |
Pomocou debugera sa možno presvedčiť, že pôvodný
kód programu sa rozšíril po zápise o niekoľko bajtov záhlavia a záveru
s informáciou o adrese a počte dát v súbore.
00000 |
FD
C0 7F 00 00 16 00 55 8B EC E8 02 00 5D CB B8 |
00010 |
00 06 B7 07 B9 00 00 BA
4F 18 CD 10 C3 FD C0 7F |
00020 |
00
00 16 00 1A |
-
Po tomto uloženi cls7C.mod na disk ho už možno použiť
v QBasic programe Cls7C.bas tak, že sa najprv uloži pomocou príkazu BLOAD
z disku na vyhradené miesto QB pamäte.
Ü |
Programový
kód subrutiny je uložený rezidentne |
Problem s uložením kódu subrutiny tak, aby nemohla
byť prepísaná QB programom (tak ako sa niekedy mohlo stať v predošlom prípade)
odstraňuje rezidentné (na stálo mimo oblasť ku ktorej má prístup hlavný
basicovský program) uloženie kódu assemblerovskej procedúry do pamäti.
Po predefinovaní jednej z adries preušenia, uloženého v tabuľke adries
vektorových prerušení , možno zavolať assemblerovskú procedúru buď pomocou
príslušného prerušenia alebo na základe adresy z tabuľky vektorových prerušení.
Gwd.asm -
obslužná procedúra prerušenia INT 78H
Kód assemblerovského podprogramu gwd.asm je rezidentne
uloženy v pamäti a volaný na základe známej adresy prerušenia (na adrese
78H*4=1E0H).
Gwd.asm - pozostáva z dvoch častí:
-
inicializačnej časti start a
-
procedúry usrprg na vykonanie súčtu a+b =
c , ktorá je rezidentne uložená v pamäti. Adresa procedúry usrprg tvorí
obsah vektrovej adresy 0:1E0H pre prerušenie INT 78H.
|
GW BASIC User's Guide
priklad D1 |
subttl
|
Sbr.com pre Basic program |
.MODEL
|
small |
.CODE
|
|
org
|
100h |
start: |
jmp start1 |
usrprg |
proc far
;Toto je prg na a+b=c |
|
push bp |
|
mov bp, sp |
|
mov si, [bp]+8
;adr. parametra b |
|
mov ax, [si]
;(ax)=hodnota b |
|
mov si, [bp]+10
;adr. parametra a |
|
add ax, [si]
;(ax)=a+b |
|
mov di, 6 [bp]
;adr. parametra c |
|
mov [di], ax
;iny zapis [bp]+6 |
|
pop bp |
|
ret 6 |
Usrprg
|
endp |
|
;------- prg na resistentné
uloženie do pamäti ----- |
start1: |
mov ax, 0
;Nastav DS=0 |
|
mov ds, ax |
|
mov bx, 1e0h
;Pointer int vektora INT 78H |
|
cmp word ptr[bx], 0 |
|
jne quit
;prgram už beži tak exit |
|
cmp word ptr+2[bx], 0 |
|
jne quit |
|
mov ax,offset usrprg
;uloz IP |
|
mov [bx],ax |
|
mov ax,cs
;uloz CS |
|
mov [bx+2], ax |
|
push cs |
|
pop ds
;nový inicial DS |
|
mov dx,offset veryend |
|
int 27h
;stay resistent |
quit: |
int 20h
;navrat do prostredia QB |
veryend: |
|
Ends
|
|
end
|
start |

Gwd.bas -
hlavný program v prostredí QBasic
Príklad, v ktorom je assemblerovská procedúra,
demonštrujúca súčet 2 operandov najprv rezidentne uložená do pamäti tak,
že štart procedúry je uložený na adrese 0:1E0H. Táto adresa (4 bajty) slúži
ako vektorová adresa prerušenia INT 78H a teda možno pomocou nej zistiť
začiatok kódu podprogramu. Z prostredia QBasic možno teda kedykoľvek získať
adresu segmentu a offsetu programového kódu, ktorý sa má vykonať ako podprogram
na vykonanie súčtu 2 celých čisel.
Listing Basic programu ilustruje použitie rezidentne
uloženej asmblerovskej procedúry Usrprg, uloženej v pamäti od adresy, ktorej
offset je obsahom slova na adrese 0:1E0 a segment je obsiahnuý v slove
na adrese 0:1E2H.
‘ zistenie adresy z tabuľky
vektorových prerušení - INT |
DEF SEG = 0 |
cs = PEEK(&H1E2)
+ PEEK(&H1E3) * 256 |
PRINT "LSB="; PEEK(&H1E2); "MSB=";
PEEK(&H1E3) |
offset = PEEK(&H1E0)
+ PEEK(&H1E1) * 256 |
twosum% = offset |
DEF SEG
'obnova seg z QBasic programu |
PRINT "CS="; cs; "Offset="; offset |
INPUT "Zadaj 1 (max 255) číslo:",
c1% |
INPUT "Zadaj 2 (max 255) číslo:",
c2% |
c3% = 0'vysledok |
DEF SEG = cs
'segment pre Usrprg twosum% |
PRINT PEEK(&H103)
' vypis 1 instrukcie CCh=204 |
CALL absolute(c1%, c2%, c3%,
twosum%) |
PRINT "Súčet ="; c3% |
END |

|