Kevyttä Assembler-ohjelmointia

ITKA203 Käyttöjärjestelmät -kurssin Demo 6 keväällä 2018 ja 2019 ja 2020 ja 2021 ja 2022. Tehdään itse pieni aliohjelma konekielellä.

Paavo Nieminen, paavo.j.nieminen@jyu.fi

Aikataulutuksessa voit ottaa huomioon sen, että suoraan tähän demoon liittyviä kysymyksiä ei tule tenttiin. Tentin lähipäivät voi rauhoittaa kertaamiseen ja tenttivalmistautumiseen. Kurssin suorittamiseksi tämä on pakollinen, joten kannattaa tehdä kuitenkin rivakasti, että saa arvosanan ensimmäisen tentin perusteella pien opetusperiodin päätyttyä. Älä jätä roikkumaan vuosiksi, kun on näin pitkälle jo tultu!

Demo-ohjeisiin on kertynyt asteittain tarkentuvia vinkkejä, ja keväällä 2020 nauhoitettiin myös "kädestä pitäen" -ohjevideot. Eli huoli pois: ohjeita tämän demon tekemiseen on riittämiin! Voit kokeilla, mihin pisteeseen asti saat omatoimisesti, ja sitten käydä katsomaan seuraavan tarkkuustason opastuksia.

Kaikista viimeiseen ohjevideoon pitää pyytää tarvittaessa erikseen salainen linkki/polkuavain opettajalta - se ei ole julkinen, koska se tavallaan spoilaa koko demon pelkäksi mallin perässä tekemiseksi... Minimivaatimuksena sekin riittää. Arvomaailman kasvattamiseksi toivomme kuitenkin ahkeraa omatoimista yrittämistä, ja tässä on mahdollisuus arvioida oman osaamisen nykyistä tilaa: Kuinka paljon täytyy tukeutua ohjeisiin, ja kuinka paljon onnistuu omatoimisen tutkimisen kautta.

Tässä tehtäväohjeessa ei vielä ole yhtään juonipaljastusta. Otsikon "Juonipaljastuksia" alla on klikattava linkki, joten vahingossa ei kukaan joudu varsinaisia paljastuksia näkemään :).

Sisällys

Tämän harjoituksen tavoitteet

Päätavoite:

  • Tietämyksesi tietokoneen toiminnasta ja konekielestä syvenee.
  • Saavutat työelämässä ja jatkokursseilla odotetun perustaitotason konekielen ymmärtämisessä. Esimerkiksi ohjelmointikielten toteutuksen ja suunnitteluperiaatteiden ymmärtäminen tulee mahdolliseksi, kun olet tehnyt hieman konekieltä käsipelillä. Samoin ohjelmistojen tietoturvallisuuden ("tekninen kyberturvallisuus") ymmärtäminen on sitä helpompaa mitä enemmän konekielenkin toiminta on ehtinyt hahmottua. Sama pätee muihinkin laiteläheiseen ohjelmointiin liittyviin kursseihin ja työtehtäviin tulevaisuudessa, mukaanlukien tietoliikenne, teollisuuden mikrokontrolleriohjelmointi ja IoT.

Edelleen käytetään nykyaikaisia työkaluja, joita kurssilla on tähän asti käytetty. Tehdään AMD64 -prosessorin (ts. "x86-64") konekieltä GNU:n AT&T-assemblersyntaksilla, jota on tähänkin asti nähty debuggerin tulosteissa. Ohjelma käännetään ja linkitetään komentoriviltä GNU:n työkaluilla Jyväskylän yliopiston suorakäyttökoneella (jalava tai halava), jossa sitä myös kokeillaan. Jotta työskentely olisi näppärää, screen-ohjelman, shellin ja tekstieditorin käyttö on hyvä olla hanskassa. Koko ajan voi opetella niitäkin lisää.

Johdanto

Valmistele hakemisto tätä demoa varten ja hae pieni koodipaketti seuraavin komennoin:

wget https://gitlab.jyu.fi/itka203-kurssimateriaali/itka203-kurssimateriaali-avoin/-/raw/master/2015/demot/mallikoodia/d06/d06_paketti.zip
unzip d06_paketti.zip

Tutki paketista avautunutta runkoa aiemmin opituilla keinoilla (mm. komennot ls, less, tekstieditori). Mukana on Makefile, joka automatisoi vaiheita. Seuraava komento kääntää ohjelman mallirungon:

make

Paketissa on mukana konekielisen aliohjelman tynkä useimmiten.s, johon sinun tulee tehdä rajapinnan ja dokumentaation mukainen toiminnallisuus. Muista kääntää ja kokeilla aina pienten muutosten jälkeen, ennen kuin koodi menee hallitsemattomaan tilaan liian monen muutoksen kautta!

Mitä tarvitsee toteuttaa

Aliohjelmallesi useimmiten() tulee ensimmäisenä parametrina tiedosto-osoitin ja toisena parametrina kokonaisluku, jonka C-kirjaston kutsu fgetc() palauttaa luettavan tiedoston loputtua. Aliohjelmasi tulee lukea C-alustakirjaston fgetc() -kutsua käyttäen kaikki standardisyöttövirrasta tulevat tavut ja palauttaa tieto, mikä tavu syötteessä esiintyi useiten.

Yksinkertainen tehtävä, onko? Jälkikäteen ajatellen tulee olemaan, mutta tehtävä on kuitenkin monivaiheinen, eikä ratkaisu tule ilman järjestelmällistä suunnittelua.

Huomioita:

Strategia ongelman ratkaisemiseksi yleisellä tasolla:

Tehtävä siis jotakuinkin "vaativaa Ohjelmointi 1 -tasoa". Nyt vaan tehdään se konekielellä.

Rajoituksia ja vapauksia:

Vinkkejä

Tehtävässä on yhdistettävä useita tiedonmurusia luentomonisteen alkupuolelta (noin sivut 1-84). Aiemmissa demoissa hankitut taidot komentorivistä, debuggerista ym. auttavat, ja niitä on tarkoitus harjoitella samalla lisää.

Perusvinkit:

Joitain tekemistä helpottavia yksityiskohtia:

Nyansseja

Tässä tapauksessa ohjelma toimisi (täysin säkällä) vaikka käyttäisit aivan kaikkeen kurssilla tähän asti nähtyjä 64-bittisiä kokonaislukuja. Jos tekee mieli olla vähemmän tuurin varassa, voit huomioida seuravaa:

Kuten sanottu, tässä tapauksessa ja tällä kurssilla on lupa luottaa "tuuriinkin".

Lisävinkkejä

Tämä ei vielä ole juonipaljastus, vaikka vähän sellaista lähestyykin. Kerrataan pari edellä mainittua perusvinkkiä:

Huomattiin jo kevään 2018 aikana, että tehtävässä voi olla hankala keksiä, mistä lähdetään liikkeelle. Tässä on yksi suositeltu toimintakaava tähän tilanteeseen. Käytännössä tämä on vain yleinen ongelmanratkaisun kaanon sovellettuna tähän demoon:

  1. Varmista, että pystyt kajoamaan koodiin työkaluillasi. Tee kokeeksi sellainen muutos, että aliohjelmasi palauttaa nollan sijasta vaikkapa luvun 123. Käännä ja suorita ohjelma varmistuen, että se tulostaa vastauksen olevan merkki 123, joka ASCIIn mukaisesti tulostettuna olisi avaava aaltosulku '{'.

  2. Hae tilanteesta positiivinen fiilis: Olet tehnyt konekieliseen koodiin hallitun muutoksen. Kaikki on näpeissäsi. Koodi toimii, ja ymmärrät sen tämänhetkisen toiminnan täysin. Mielellään ei tule missään vaiheessa vastaan syytä poiketa tästä tilanteesta.

  3. Havaitse, että ainakin pinokehyksen luominen ja vapauttaminen täytyy joka tapauksessa tapahtua. Samalla tavoin kuin demo 5:ssä nähtiin. Koodaa siis tarvittavat rivit aliohjelman alkuun ja loppuun. Käännä ja aja, kommentoi. Varmemmaksi vakuudeksi askella debuggerissa ja katso, että kehys syntyy ja tuhoutuu siten kuin demo 5:ssä nähtiin valmiiksi tehdyn esimerkin kanssa.

  4. Tee ratkaisu oman pinokehyksen sisällöstä: Mihin kohtaan tulee taulukko, mihin kohtaan muu tarvittava. Suosituksena voisi olla, että varaat 8 tavua tilaa molemmille parametreille ja 256:lle kokonaisluvulle laskentaa varten. Muokkaa aliohjelman alkua vastaavasti ja dokumentoi valittu rakenne, eli missä kohtaa kehystä pidät mitäkin. Käännä ja aja. Askella debuggerissa, jos et muuten usko, että varaus on OK.

  5. Laita koodi, joka tallentaa parametrit valitsemiisi paikkoihin. Käännä. Debuggaa tarvittaessa nähdäksesi, että parametrit menee varmasti talteen.

  6. Havaitse, että aliohjelman alku ja loppu on kunnossa. Toiminnallisuus tulee seuraavaksi niiden väliin. Laita ennakoiva kommentti, mitä osioita luultavasti tulee.

  7. Tee koodi, joka nollaa taulukon. Käännä. Varmistu debuggerilla (ks. vinkki yllä), että taulukossa on varmasti 256 kappaletta nollaa.

  8. Tee koodi, joka kutsuu fgetc():tä, kunnes se palauttaa EOF. Älä vielä edes mieti muuta toiminnallisuutta. Pieni askel kerrallaan! Käännä, aja, debuggaa. Totea, että ohjelma jää odottamaan syötettä ja että silmukka pysähtyy tiedoston päättymisen kohdalla. Helposti voi tulla aluksi kaatuminen tai ikuinen silmukka. Syy on mahdollista selvittää, kun nyt työn alla on vain muutama käsky. Varmista, että fgetc():n parametrit ja paluuarvo menevät oikein. Muista, että fgetc()-kutsu saa sotkea suuren osan rekistereistä, mm. RAX, RCX, RDX, RDI, RSI... Olet jo varautunut tämän hoitamiseen edellisissä askeleissa. Jumppaa näitä muutamaa riviä, kunnes ohjelma näyttää varmuudella lukevan syötteestä kaikki merkit ja lopettavan lukemisen EOFiin.

  9. Hae onnistumisen fiilis siitä, että olet homman puolivälissä ja ymmärrät koodisi täysin.

  10. Lisää koodi, joka summaa merkin saapuessa ykkösen taulukkoon oikeaan kohtaan. Testaa debuggerissa helpolla syötteellä. Esim. aseta breakpoint lukusilmukkasi perään. Käynnistä. Näppäile "abbccc", rivinvaihto ja Ctrl-D. Oletettu tilanne pysähdyspisteessä on, että taulukossa on peräkkäiset luvut 1, 2 ja 3, koska merkit 'a', 'b', 'c' ovat ASCIIssa peräkkäin. Lisäksi pitäisi olla ykkönen rivinvaihtomerkin kohdalla (indeksi 10).

  11. Vasta sitten, kun lukusilmukka on testattu, on toiveita saada seuraavat vaiheet maaliin. Varmistu siis vielä, että edellinen kohta on OK.

  12. Lisää koodi, joka etsii taulukon suurimman alkion. Tämä on tulossa lukemisen jälkeen, joten enää ei tulla kutsumaan fgetc():tä tai muutakaan ulkopuolista aliohjelmaa, joten voit käyttää apumuuttujina rekistereitä RAX, RCX, RDX, RDI, RSI,... Esimerkiksi:

    • Tee valinta, mitä apumuuttujia tarvitset, ja dokumentoi kommenttiin.
    • Sitten silmukkarakenne eli taulukon läpikäynti toistaiseksi ilman toiminnallisuutta; varmista, ettei tullut syntaksivirheitä tai ikuista silmukkaa. Älä yritä liikaa kerralla.
    • Lopulta silmukan sisään logiikka, jolla löytyy isoimman alkion indeksi.
    • Jumppaa, kunnes toimii.
  13. Varmista, että aliohjelmasi sijoittaa paluuarvon ABIn mukaisesti RAX-rekisteriin ennen paluuta kutsuvaan ohjelmaan. Viimeistään tässä vaiheessa luovut esimerkkirungossa olleesta käskystä, joka sijoitti paluuarvoksi "vain jotakin".

  14. Testaa muutamalla erilaisella syötteellä.

  15. Iloitse - olet nyt astunut assembler-koodaajien kerhoon! Muista myös palauttaa demo ja näyttää teoriaosaaminen tentissä, niin saat kurssista opintopisteet...

Juonipaljastuksia

Jos ei nämä vinkit vielä auttaneet, voit hätäpainikkeena klikata seuraavaa linkkiä, jossa on vielä superlisävinkkejä: http://users.jyu.fi/~nieminen/kj21/demovedokset/spoilereita_d06.html

Pakollinen palautustehtävä

Demo palautetaan samalla tavalla kuin aiemmatkin.

Tästä demosta palautetaan tasan yksi tiedosto nimeltään "useimmiten.s" jossa on yllä selitetyn tehtävänannon mukainen, toimiva assembler-aliohjelma.