Ú-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:

  Debug medabs.exe.
  • 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á.)

  S CS:23 L 1778 55. 
  • Výslekom tohto hľadania bude výpis niekoľkých adries, v ktorých sa nachádza kód 55H. Teraz treba len upresniť, ktorá adresa sa vzťahuje k začiatku konkrétnej subrutiny. Napríklad ak jedna z najdených adries bola 21E8:0172 potom sa možno presvedčiť o obsahu buniek v tvare inštrukcii mikroprocesora.pomocou príkazu Unassemnle :

  •   u 21E8:172
    • 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:

    1. na začiatku hlavného programu bol deklarovaný tvar volania call:

      DECLARE SUB absolute (BYVAL Num, BYVAL Offs, BYVAL Segm)
    1. 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 :
     
      INT 5H
      retf  

    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:

      debug QB.exe/L.
    • 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:)

      M cs:0 L16 7FC0:0
    • 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



     


    [Návrat]