Tm tiedosto on koottu CyberDyne Magazine:ssa ilmestyneen
'Assy Ohjelmointikurssi' -sarjan viidennest osasta. Sen
levittminen elektronisessa muodossa muuttamattomana on
sallittua ja jokainen saa omaa kyttn varten printata
sen kokonaisuudessaan tai osia siit. Sarjan toimitti
alunperin Antti Niskanen. Tmn tiedoston on koonnut
Antti Niskanen.

=====Kurssin viides osa====================================================
=====Ilmestyi numerossa 05/95==============================================


                                    
               ۲    ۲   ۲  ۲   ۲
              ۱ޱ  ޱ ޱ ޱ ޱ ޱ   ޱ
             ް  ް  ް   ް   ߰
             ޲  ߲   ߲      ޲
             ޱޱ ޱ ޱ ޱ ޱ     ޱ
             ް  ް  ۰   ۰      ۰
                                   
                O H J E L M O I N T I K U R S S I   O S A  5

        Jopas  on pitk aika siit,  kun edellinen  osa  ohjelmointikurssia
    ilmestyi. Syyn lienee se, ett lehte ei ole ilmestynyt vhn aikaan.
    Siihen on syyn tietenkin... h, kyllhn te jo tiedtte.
        Kvin  lpi  edellisi kurssin osia ja huomasin,  ett olen  tullut
    luvanneeksi opettavani paljon sellaista,  mit en sitten ole kuitenkaan
    opettanut,   ja  kertovani  paljon sellaista,  mik on  jostain  syyst
    jnytkin kertomatta.  Korjaankin asian nyt tss,  Assykurssin viiden-
    ness  ja viimeisess osassa.  Selitn lopultakin,  mik ihme on  pino,
    julkaisen  lopultakin  listan  hydyllisimmist  systeemikeskeytyksist
    (eli  niist,   jotka laukaistaan INT-komennolla),  ja  ptn  kurssin
    kertomalla  hieman  todellisista  assembler-kntjist  (Kuten  olette
    varmaan huomanneet, DEBUG:lla ohjelmointi on hieman hankalaa).

        Kurssin  kolmannessa  osassa  lupasin,   ett  selitn  seuraavassa
    osassa,   mik pino on.  Neljnness osassa en kuitenkaan pinoa  tullut
    edes maininneeksi. On siis korkea aika ryhty selittmn.
        Pino on vain osa muistia. Aivan kuten tavalliseen muistiinkin, mys
    pinoon voidaan tallentaa tietoa silytyst varten.  Pino soveltuu erin-
    omaisesti  tiedon vliaikaiseen silyttmiseen.  Sit kytetn  komen-
    noilla PUSH ja POP. Asia selvinnee parhaiten esimerkill:

        MOV AX,1234
        PUSH AX
        MOV AX,5678
        POP AX

    Osaatko  arvata,  mik arvo on nyt AX:ss?  Oikea vastaus on...   1234!
    Ensimminen  MOV-komento  tallentaa  AX:n tmn arvon.   Toinen  MOV-
    komento  muuttaa  arvon 5678:ksi,  mutta sit ennen arvo  tallennettiin
    pinoon  (PUSH-komento  'tynt'  AX:n sislln pinon plle)   ja  sen
    jlkeen  sislt  palautettiin  takaisin  pinosta  AX:n  (vanha  arvo
    otetaan pinon plt ja sijoitetaan AX:n) POP-komennolla.

    Otetaan heti pern toinen esimerkki:

        MOV AX,1234
        MOV CX,5678
        PUSH AX
        PUSH CX
        POP AX
        POP CX

    Mik arvo on nyt AX:ss,  ja mik CX:ss?  AX:ss on ylltten 5678  ja
    CX:ss 1234!  Miksi?  PUSH tynt pinon plle arvon.  Huomaa,   pinon
    *plle*! PUSH AX -komennon jlkeen pinon pll on arvo 1234 (joka oli
    AX:ss).  PUSH CX tynt pinon plle uuden arvon 5678.  1234  on  nyt
    pinossa  vasta toisena.  Pinon pll on arvo 5678  (joka oli  CX:ss).
    POP-komento palauttaa arvon pinon plt rekisteriin.  Huomaa  jlleen,
    pinon *plt*! Pinon pll on arvo 5678!  Se palautetaan AX:n  (POP
    AX).   Samalla se poistetaan pinosta.  Nyt pllimmisen on arvo 1234.
    POP CX palauttaa sen CX:n. Pinon toiminta on siis LIFO, eli 'Last In,
    First  Out',  tarkoittaen:  'Mik viimeiseksi menee sisn,   se  tulee
    ensimmiseksi ulos'.

       Voit verrata pinoa tavalliseen paperipinoon. Viimeiseksi paperipinon
    plle  asetettu paperi otetaan ensimmisen pinon plt pois.   Pieni
    ero  niss pinoissa kuitenkin on:  paperipinosta voit  tonkia  paperin
    mys paperipinon puolivlist ja ottaa sen ulos,  ja voit mys  laittaa
    paperin jonnekin paperipinon keskelle. Pinosta et kuitenkaan voi tonkia
    mitn sen keskelt, etk mitn voi laittaa sen keskelle. Kaikki, mit
    pinoon  menee,  menee sen plle.  Kaikki,  mit pinosta  lhtee  pois,
    lhtee sen plt.
        Mainitsin,  ett pino on vain pala tavallista muistia. Miten muisti
    voi  toimia  nin  oudolla tavalla?  Muistiinhan voi  kirjoittaa  minne
    hyvns  ja sit voi lukea mist hyvns.  Miten pino voi toimia  nin?
    Tietokoneen prosessorissa on kaksi rekisteri,  SS ja SP (Stack Segment
    ja Stack Pointer - olen maininnut ne aikaisemmissa kurssin osissa).  Ne
    osoittavat pinon huippua.  Kun annat kskyn PUSH CX,  CX:n arvo tallen-
    netaan muistiin osoitteeseen SS:SP,  ja SP-rekisteri pivitetn niin,
    ett  se osoittaa muistin seuraavaan tyhjn kohtaan.  Kun annat kskyn
    POP  DX,   pino-osoitinta SP pivitetn taaksepin  yksi  askel,   eli
    osoittamaan viimeist pinoon tallennettua arvoa,  ja siell oleva  arvo
    luetaan rekisteriin DX.

        Yksinkertaista   ohjelmointia  harrastaessasi  sinun  ei   tarvitse
    huolehtia SS- ja SP-rekistereist lainkaan.  Itse asiassa voit unohtaa,
    ett sellaiset rekisterit on olemassa,  ja voit mys unohtaa, ett pino
    on  osa muistia.  Sinun on vain muistettava,  ett kaikki,  mit pinoon
    kirjoitat, sinun on mys sielt luettava - muuten pino voi kasvaa liian
    suureksi  ja  vallata vaikkapa ohjelmallesi varatun  muistin,   jolloin
    ohjelma voi kaatua.  Et myskn saa lukea pinosta enemp,  kuin  olet
    sinne  kirjoittanut - seuraukset voivat muuten olla yht kohtalokkaita.
    Jos jostain syyst haluat muuttaa SS-  tai SP-rekistereit,   kannattaa
    ensin  hankkia jokin todellinen assembler-ohjelmoinnista kertova  kirja
    ja  lukea sielt,  mit ongelmia siihen liittyy.  Pino on hyvin  ktev
    laite, mutta vrin kytettyn se helposti kaataa koko jrjestelmn.
        Pinoa  ksittelevi kskyj ei todellakaan ole enemp kuin  kaksi:
    PUSH ja POP. Niiden muodot ovat todella helpot:

        PUSH <lhde>    Tallentaa lhteen pinoon, tuhoamatta itse lhdett
        POP <kohde>     Palauttaa pinosta arvon kohteeseen

    Lhde ja kohde saavat olla mit hyvns 16-bittisi rekistereit, kuten
    AX, BX, SI tai DS.  8-bittisi rekistereit kuten AH tai BL et voi tal-
    lentaa pinoon - koko rekisteri AX tai BX on tallennettava.

        Siin  kaikki,   mit pinosta tarvitsee tiet,  jotta  sit  voisi
    kytt  hydyksi.  En valitettavasti voi antaa esimerkki  ohjelmasta,
    jossa  pinosta olisi jotain hyty,  sill kaikkein  yksinkertaisimpiin
    ohjelmiin,   joita  olemme  tehneet,  riitt hyvin  prosessorin  nelj
    datarekisteri AX,  BX, CX ja DX. Pino on hydyllinen vain,  jos haluat
    esim.  kirjoittaa jotain kuvaruudulle kytten DOS-kekseytyst,  kadot-
    tamatta AX:n arvoa.  Tllin on nimittin kirjoitettava AH:hon luku 09,
    jolloin siell aikaisemmin ollut arvo katoaa. Jos taas ennen sit annat
    komennon  PUSH AX ja koko operaation jlkeen komennon POP AX,  on  AX:n
    alkuperinen arvo yh tallella.

        Tm kurssin osa alkoi hyvin teoreettisesti. Asiaan ei tule nytkn
    mitn  parannusta.  Vuorossa on rekisterit SI ja DI - mihin niit  voi
    kytt?   Ne  ovat  aivan tavallisia 16-bittisi  rekistereit.   Voit
    vliaikaisesti silytt niiss tietoa, jos haluat. Niill on kuitenkin
    prosessorin  toiminnan kannalta omat merkityksens,  nimittin  muistin
    osoittamisessa.
        Kurssin  kolmannessa  osassa kerroin muistin  osoittamisesta.   Nyt
    kannattaa  kerrata,   sill  en aio toistaa sit  tss.   Muistia  voi
    osoittaa muutenkin kuin suoralla osoitteenmuodostuksella esim.  [1234].
    Voit jossain tilanteessa haluta kirjoittaa muistiin niin,  ett osoite,
    jonne  aiot kirjoittaa,  on jossain rekisteriss.  Thn  tarkoitukseen
    kelpaa nelj rekisteri: BX, BP, SI ja DI. Jos BX:ss on vaikkapa 4567,
    komento

        MOV BYTE PTR [BX],CH

    kirjoittaa osoitteeseen DS:4567  yhden tavun,  joka on CH:ssa.  Tm on
    niinkutsuttu epsuora osoitteenmuodostus. Rekisteri, jossa muistiosoite
    on,  sijoitetaan hakasulkujen vliin. Ei sen kummempaa.  Ja todellakin,
    vain edellmainitut nelj rekisteri kelpaavat tarkoitukseen.

        On  mys olemassa kolmas tapa osoittaa muistia,  nimittin epsuora
    osoitteenmuodostus vakion siirrolla. Esimerkiksi

        MOV AX,WORD PTR [BP+0019]

    lukee muistista AX:n 16-bittisen luvun eli sanan,  jonka muistiosoite
    on DS:BP+19.  Yksinkertaista yhteenlaskua. Luku, joka listn rekiste-
    riin,  on 16-bittinen,  ja rekisterin voi toimia jlleen yksi neljst
    rekisterist: BX, BP, SI tai DI.

        Tietysti  on  olemassa neljskin tapa osoittaa  muistia:   epsuora
    osoitteenmuodostus indeksill. Sellainen on esimerkiksi

        CMP AH,BYTE PTR [BX+SI]

    joka vertaa AH:ssa olevaa tavua tavuun, jonka muistiosoite on DS:BX+SI.
    Siin kytetn kahta rekisteri ilmoittamaan siirrososoitetta.  Ensim-
    misen  rekisterin  on oltava BX tai BP,  toisen on oltava SI  tai  DI.
    Rekisterit vain lasketaan yhteen. Koska SI:t ja DI:t kytetn indek-
    soidussa osoitteenmuodostuksessa, niit kutsutaan indeksirekistereiksi.

        Jnnitys tiivistyy... Nyt on vuorossa viides (voi ei) osoitteenmuo-
    dostus:   epsuora osoitteenmuodostus kannalla,  indeksill  ja  vakion
    siirrolla! Esimerkki:

        MOV WORD PTR [BP+DI+0104],DX

    Siin siirrososoite on, ylltten, BP+DI+0104. Siirrososoitetta ilmoit-
    tamassa  on siis kaksi rekisteri,  joista ensimmisen on oltava BX tai
    BP  (joita kutsutaan kantarekistereiksi)  ja toisen on oltava  indeksi-
    rekisteri (SI tai DI),  sek 16-bittisell luvulla.  Nm kaikki laske-
    taan yksinkertaisesti yhteen. Tm on monimutkaisin osoitteenmuodostus,
    jonka  8086-prosessori  tuntee.   486-prosessorissa on  olemassa  viel
    monimutkaisempi osoittenmuodostus,  mutta sit en aio opettaa.   Osoit-
    teenmuodostukset on nyt vihdoin ksitelty loppuun.

        Indeksirekistereit  SI  ja DI kytetn mys niinsanotuissa  jono-
    operaatioissa,   joilla  voi  ktevsti siirt  suuria  mri  tietoa
    muistin  osasta  toiseen,   lhett  suuria  mri  tietoa  muistista
    ulkoisille laitteille, lukea suuria mri tietoa ulkoisilta laitteilta
    muistiin, tai vertailla suuria mri tietoa muistissa keskenn.  Nm
    operaatiot ovat kuitenkin ylikurssia.  En aio opettaa niit.  Jtn sen
    oppikirjojen  huoleksi.   Mikli  aiot  tosissasi  ryhty  ohjelmoimaan
    assembleria, tarvitset joka tapauksessa kunnon oppikirjan.  Itse kytn
    kirjaa  '486   ohjelmointi',  jonka on kirjoittanut  Gunvald  Hedemalm,
    kustantaja  on Pagina.  Kirja on melko selke,  ja voin suositella sit
    sek  oppikirjaksi ett hakuteokseksi,  vaikka siin onkin yksi todella
    paha vika: se on suomenkielinen.  Muutaman virheen olen siit lytnyt,
    mutta ne eivt ole kovin vakavia.

    Tss on sitten lyhyt lista hydyllisist keskeytyksist:
   Ŀ
                              I N T   1 0 h                               
                                                             
    AH:  Parametrit:           Toiminta:                                
   Ĵ
    00h  AL=videotila          Asettaa nytn annettuun videotilaan.    
                               Tavallisia tiloja ovat:                  
                               0:  teksti 40x25                         
                               3:  teksti 80x25                         
                               19: grafiikka 320x200, 256 vri         
   Ĵ
    02h  BH=sivunumero         Asettaa kursorin annetulle sivulle       
         DH=rivi               annettuun kohtaan                        
         DL=sarake                                                      
   Ĵ
    05h  AL=sivunumero         Asettaa nytettvn sivun. Tavallisimmat 
                               sivunumerot ovat tekstitilassa 0-3.      
   Ĵ
    08h  BH=sivunumero         Lukee kursorin kohdalla olevan merkin    
                               AL:n ja vrin AH:hon.                  
  

    Ŀ
     AH:  Parametrit:           Toiminta:                               
    Ĵ
     09h  AL=merkki             Kirjoittaa annetulle sivulle, kursorin  
          BH=sivunumero         kohdalle, annettua merkki annetulla    
          BL=vri               vrill CX:n ilmoittaman mrn.        
          CX=lukumr                                                  
    Ĵ
     0Ch  AL=vri               Grafiikkatilassa tulostaa ruudulle      
          CX=sarake             annettuun kohtaan annetunvrisen        
          DX=rivi               pisteen.                                
    

   Ŀ
                              I N T   2 1 h                               
                                                             
    AH:  Parametrit:           Toiminta:                                
   Ĵ
    01h  ei ole                Lukee AL:n nppimenpainalluksen ja    
                               nytt merkin ruuudulla.                
   Ĵ
    02h  DL=merkki             Tulostaa ruudulle yhden merkin           
   Ĵ
    05h  DL=merkki             Tulostaa printterille yhden merkin       
   Ĵ
    08h  ei ole                Lukee AL:n nppimenpainalluksen       
                               nyttmtt merkki ruudulla.            
   Ĵ
    09h  DS:DX=alkuosoite      Tulostaa ruudulle merkkijonon. DS:DX     
                               on oltava jonon alun muistiosoite. Jonon 
                               on ptyttv dollarimerkkiin ($). Jono  
                               ei voi sislt dollarimerkkej.         
   

    Ŀ
     AH:  Parametrit:           Toiminta:                                
    Ĵ
     0Ah  DS:DX=osoite          Lukee nppimistlt ENTER:iin pttyvn 
                                merkkijonon. Jono sijoitetaan muistiin   
                                alkaen annetusta osoitteesta. Annetussa  
                                osoitteessa ennen kutsua oleva           
                                ensimminen tavu ilmoittaa jonon         
                                maksimipituuden.                         
    Ĵ
     0Eh  DL=asemanumero        Valitsee oletuslevyaseman. Palauttaa     
          0=A:, 1=B:, jne.      AL:n asemien lukumrn.               
    Ĵ
     19h  ei ole                Palauttaa senhetkisen oletuslevyaseman   
                                numeron AL:n                           
    Ĵ
     2Ah  ei ole                Palauttaa pivmrn: AL=viikonpiv,   
                                CX=vuosi, DH=kuukausi, DL=piv.         
    Ĵ
     2Bh  CX=vuosi, DL=piv    Asettaa pivmrn                      
          DH=kuukausi                                                    
   

    Ŀ
     AH:  Parametrit:           Toiminta:                               
    Ĵ
     2Ch  ei ole                Palauttaa kellonajan: CH=tunnit,        
                                CL=minuutit, DH=sekunnit, DL=1/100 sek. 
    Ĵ
     2Dh  ks 2Ch palautus yll  Asettaa kellonajan                      
    Ĵ
     30h  AL=00                 Palauttaa DOS versionumeron: AL=numero  
                                ennen desimaalipilkkua, AH=jlkeen      
    Ĵ
     4Ch  AL=Errorlevel         Lopettaa ohjelman, palauttaa annetun    
                                arvon Errorlevelin.                    
    

        Koko  kurssin  ajan olemme tehneet kaikki ohjelmamme DOS:in  mukana
    tulevalla  DEBUG-ohjelmalla,  johon sisltyy alkeellinen kntj  sek
    muutama viel alkeellisempi tykalu.  Niin alkeellisella ohjelmalla  ei
    kuitenkaan pysty tosissaan tekemn minknlaisia ohjelmia. Sit varten
    on  olemassa assembler-kntjt eli assemblerit.  Tunnetuimmat (suosi-
    tuimmat?)   lienevt  Microsoftin  MASM ja  Borlandin  TASM.   Jokainen
    kntj on hieman erilainen,  joten en milln voi kertoa,  tarkalleen
    kuinka kntjsi toimii.  Kntj tarvitsee nimittin ohjelman alussa
    hieman tietoa ohjelmasta,  kuinka se tulisi knt, mit numerosystee-
    mi kytt, tuleeko ohjelmasta EXE vai COM, jos ohjelma on EXE, montako
    data-  ja koodisegmentti siihen tulee,  jne jne jne.  Tm voi  alussa
    olla  hieman  hmv,  mutta itse olen voittanut  ongelman  tekemll
    kaksi sapluunaohjelmaa, jonka ensin kopioin sille nimelle,  mill uutta
    ohjelmaani  teen,  ja sitten kytn niit runkona ohjelmaa  tehdessni.
    Julkaisenkin omat sapluunani tmn kurssin osan lopussa,  mikli niist
    jollekulle sattuisi olemaan jotain hyty.

       Assembler-kntjn (oikean sellaisen) vahvin etu DEBUG:iin verrat-
    tuna on se, ettei itse tarvitse antaa muistiosotteita. Kurssin toisessa
    osassa teimme ohjelman nimelt KVAIE.COM, joka odottaa nppimistlt K
    tai  E painallusta,  ja palauttaa tuloksen Errorlevelin  siten,   ett
    Kyll/Ei kysymyksi voi kytt .BAT-tiedostojen ohjaamiseen.   Muista-
    nette,  kuinka monimutkaista oli saada hyppykskyt kohdalleen  DEBUG:ia
    kyttmll.   Oikealla  assembler-kntjll  se  on  hyvin  helppoa.
    Seuraavalla sivulla nytn,  kuinka se tapahtuu.  On mys hyvin helppoa
    silytt tietoa muistissa esim.  ruudulle tulostamista varten - nytn
    mys siit esimerkin.

    Tss on KVAIE.COM assembler-kntjlle.  Puolipisteell voi  aloittaa
    kommmentin.  Kommentit helpottavat ohjelman kirjoittamista ja tutkimis-
    ta jlkikteen.


    uusiksi:  MOV AH,08h           ;Haetaan merkki nppimistlt
              INT 021h
              CMP AL,04Bh          ;ISO K
              JZ joopajoo
              CMP AL,06Bh          ;pieni k
              JZ joopajoo
              CMP AL,045h          ;ISO E
              JZ ehei
              CMP AL,065h          ;pieni e
              JZ ehei
              JMP uusiksi          ;jokin muu - haetaan uusi merkki
    joopajoo: MOV AX,04C01h        ;Vastaus oli K - Errorlevel = 1
              INT 021h             ;Paluu DOS:iin
    ehei:     MOV AX,04C00h        ;Vastaus oli K - Errorlevel = 0
              INT 021h             ;Paluu DOS:iin

        Tm ohjelma kirjoittaa kuvaruudulle tervehdyksen kytten  hyvksi
    DOS-keskeytyksen tarjoamaa palvelua:


              MOV AH,09h
              MOV DX,OFFSET terve
              INT 021h
              MOV AH,04Ch
              INT 021h
       terve: DB 'No pevi, mits kuuluupi?',0Dh,0Ah,'$'


        Kuten huomaat,  assemblerilla muistin osoittaminen on hyvin  yksin-
    kertaista.   Tarvitaan vain rivin alkuun jokin nimi,  joka ei saa olla
    mikn  assembler-komento eik pelkk numero.  Tt nimit voidaan sen
    jlkeen kytt osoitteena hyppykskyiss tai OFFSET-etuliitteen kanssa
    voidaan  osoittaa  muistia,   kuten tss  esimerkiss.   Assemblerilla
    ohjelmoitaessa  on mys kerrottava,  mit numerojrjestelm (desimaali
    vai heksadesimaali) kytetn - siksi jokaisen numeron perss on h.

    Seuraavalla sivulla on oma sapluunani COM-ohjelmien tekemiseen.


    ; Sapluuna .COM-ohjelmien tekoon TASM:lla
    .radix 16                      ;numerojrjestelm heksadesimaali
    main    segment para 'code'    ;sekalaista roinaa
            org 0100               ;
            assume cs:main         ;
    hiphei:                        ;

    ;***Ohjelma ja siihen kuuluva data tulee thn!!!***;

    main    ends                   ;ilmoittaa kntjlle ohjelman loppuvan
    end hiphei                     ;


    Sijoitetaan  edellinen  ohjelmaesimerkki  (tervehdintohjelma)    thn
    sapluunaan ja annetaan nimeksi TERVE.ASM, niin sen voi knt TASM:lla
    nin:
            C:\> TASM TERVE
            C:\> TLINK /t TERVE

    /t kertoo linkittjlle, ett on tehtv .COM-ohjelma.

        Seuraavalla sivulla on sapluuna, jota kytn .EXE:jen tekoon. Siin
    data ja ohjelma tulee erikseen. Ohjelman alussa on mriteltv,  miss
    data on.  Datan yhteydess kytettviss nimiiss ei kytet  kaksois-
    pistett.  Sapluunassa on valmiina yksinkertainen ohjelma,  joka,  taas
    vaihteeksi,  tervehtii.  Tll kertaa englanniksi - vaihteluhan virkis-
    t!


        Ja niin pttyykin Assy ohjelmointikurssi. Yhteens viisi osaa tuli
    kurssin   pituudeksi.    Kunhan  ehdin,   teen  viimeisestkin   osasta
    tekstiedoston,   yhdistn kaikki osat yhdeksi,  ja laitan koko  paketin
    ainakin CyberDyne BBS:n. Kiitokset mielenkiinnosta.

                                                            -Antti Niskanen



    title   sapluuna                    ;tosi paljon roinaa
    locals                              ;
    .model  small                       ;
    .stack  1024                        ;
    .radix 16                           ;
    .data                               ;
    ;***Data tulee thn!!!***;
    message db      'Hi there!',0Dh,0Ah,'$'     ;tss on ohjelman data

    .code
    main    proc
    ;***Ohjelmakoodi tulee thn!!!***;
    mov     ax,@data                    ;nm rivit mrittvt, miss
    mov     ds,ax                       ;data on.
    mov     dx,offset message
    mov     ah,09h
    int     021h
    mov     ah,04Ch
    int     021h

    main    endp                        ;ilmoitetaan, ett ohjelma loppuu
   end     main                        ;


