C-64: Ohjelmointi

BASIC – Beginners’ All-purpose Symbolic Instruction Code

Janne Heinikangas ja Markus Salmijärvi

Johdatus BASIC-kieleen.

BASIC – Beginners’ All-purpose Symbolic Instruction Code kehitettiin vuonna 1964 Dartmouthin yliopistossa professoreiden John Kemenyn ja Thomas Kurtzin toimesta. Professoreiden tarkoituksena oli luoda pieni ja helppokäyttöinen kieli, jolla voitaisiin opettaa opiskelijoille ohjelmoinnin alkeet koska kääntäjien avulla tapahtuva perinteinen työskentely kun oli hidasta ja työlästä. Niinpä tarvittiin kieli joka olisi välittömästi käyttövalmis ilman kääntämistä ja joka ei ollut liian hankala opittavaksi.

BASIC kehitettiin alkujaan isoja tietokoneita varten. Silloin ei vielä osattu aavistella kotimikrojen tulevaa kulta-aikaa. Ensimmäisen kotimikroja varten valmistettu BASIC-tulkin työstivät Paul Allen ja Bill Gates vuonna 1974. Heidän työstämäänsä tulkkia myytiin muutamassa vuodessa kymmeniätuhansia kappaleita legendaarisen Altair-koneen mukana. Myöhemmin menestyksen huumassa herrat Allen ja Gates perustivat Microsoft-nimisen yrityksen joka onkin sitten jo tyystin oma tarinansa.

Commodore osti koneissaan käytetyn BASIC-rungon juuri Microsoftilta, mutta muutti sitä jonkin verran omiin tarpeisiinsa sopivaksi.

Microsoft jatkoi oman BASICinsa kehittelyä ja sen ominaisuudet ohittivat nopeasti Commodorelle myydyn version. Microsot nimesi tuotteensa MBasic:ksi ja versio 4.0 oli ensimmäinen levyasemaa tukeva BASIC-versio.

Vuonna 1981 julkaistiin versio 5.0 joka tuli käyttöön 16-bittisissä koneissa. Ainoa puute BASICssa oli sen kykenemättömyys hyödyntää enempää muistia kuin 64 kiloa. Versio 5 sai nimekseen BASICA eli Advanced Basic. BASICAn laajennetut versiot on nimetty GW-BASICksi ja myöhemmin QBASICksi. Myöhemmin BASICsta julkaistiin myös aidolla kääntäjällä julkaistu Quick Basic – versio.

8-bittisten valtakaudella BASICn asema oli vankkumaton harrastajan ohjelmointikielenä. Harmittava piirre tosin oli, että kukin konevalmistaja julkaisi omanlaisensa BASIC-version koneessaan joten harvoin eri koneiden BASIC-koodi oli yhteensopivaa keskenään. Varsinaisesti koskaan ei ole olut yhtenäistä BASIC-standardia vaikka yrityksiä sen eteen on ollutkin mm. MSX-Basic oli yksi sellainen yritys BASIC:n yhdenmukaistamiseksi. MSX-Basic on Microsoftin käsialaa ja lukuisat japanilaiset kotikoneiden valmistajat mm. Sony, Yamaha, Sanyo etc. lähtivät mukaan MSX-standardiin omilla MSX-yhteensopivilla koneillaan.

Basicin käyttökelpoisuutta on arvosteltiin kovin sanoin mm. rakenteellisuuden puuttumisen vuoksi. BASIC-ohjelmista tapaa tulla kovin sekavia ja rönsyileviä ja niissä olevia virheitä on melkoisen työlästä jäljittää. Vaikka alkuun BASIC suunniteltiin juuri helpottamaan ohjelmointiin tutustumista on toisaalta kynnys siirtyä järeämpiin rakenteisiin ohjelmointikieliin korkea. Sanottiin BASICsta mitä tahansa puolusti se mielestäni hyvin paikkaansa mm 8-bittisten kotikoneiden ohjelmointikielenä.

BASIC-kielen perusteet

Kuten jo mainittiin on BASIC tulkattava kieli tästä johtuen aloitettaessa ohjelmointi BASIC-tulkin avulla on se ensin ladattava koneen muistiin. Kotimikroissa ja tässä tapauksessa käytettäessä Commodore 64 konetta on tulkki jo sijoitettuna valmiiksi koneen ROMissa ja se on käytössä C-64 koneessa välittömästi kun laitteeseen kytketään virta

Tutustuessamme BASIC-kieleen käytämme esimerkkilaitteena Commodore 64-konetta. Esimerkit voi sulavasti testata myös C-64 emulaattorilla vaikka VICElla Tutustutaan aluksi aivan perinteisiin komentoihin joilla hallitaan omien ohjelmien tutkimista ja ajoa

”Ohjauskomennot”
Basic-tulkkia ”ohjaillaan” muutamalla peruskomennolla l. käyttökomennoilla

  • LIST listaa näytölle muistissa olevan ohjelman koodiin
  • RUN ajaa muistissa olevan ohjelman
  • NEW tyhjentää muistissa olevan ohjelman tehden tilaa uudelle ohjelmalle
  • LLIST listaa keskusmuistissa olevan ohjelman kirjoittimelle
  • LOAD esim: LOAD ”ohjelma” lataa ohjelma nimisen ohjelman muistiin
  • SAVE esim: SAVE ”ohjelma” tallentaa ”ohjelma”-nimisen ohjelman massamuistiin esm: kasetille
  • RENUM numeroi BASIC-ohjelman rivit uudelleen kymmenen välein

Peruskäskyt

Tässä kerrottavat käskyt pohjautuvat pääsääntöisesti C-64:n BASICiin, mutta suurin osa käskyistä ovat hyvin yleismaailmallisia jotka soveltuvat useisiin muihinkin BASIC-versioihin.

PRINT

Kielen tärkein käsky liittyy tulostamiseen näytölle. Ohjelmasta ei olisi paljoakaan hyötyä jos mitään näkyvää ei näytön kautta välittyisi käyttäjälle. Käskyn nimi BASICssa on PRINTMicrosoftin ja Commodore 64:n tulkit sallivat käskyn lyhentämisen kysymysmerkillä. Tällaista ei välttämättä sallita muiden koneiden BASIC-versioissa. Käsky toimii sekä suoraan, että ohjelmassa.

Esimerkki suoraan käytöstä

Näppäile C-64:llä tai C-64 emulaattorilla:
PRINT 1+3

Oletettavasti saat vastaukseksi 4. Eli suoraan PRINT-komentoa käytettäessä voidaan tehdä matemaattisia laskutoimituksia Käytettävät merkit ovat + – * (=kertomerkki) ja / (jakomerkki)

Lakutoimituksista saadaan huomattavasti monipuolisempia yhdistelemällä erilaisia operaattoreita.

PRINT 56 * (5+105)/(34-5)

Laskutoimitusten lisäksi tulostuskomentoa voidaan käyttää suoraan tulostamaan selväkielistä tekstiä sulkemalla haluttu teksti lainausmerkkien sisään:

PRINT ”HEI MAAILMA!”

Esimerkki käytöstä ohjelmassa

PRINT-käskyn käyttäminen ohjelmassa on helpoimmillaan enemmän kuin yksinkertaista. Jokainen Commodore 64:llä joskus on varmasti toteuttanut seuraavan simppelin esimerkin PRINT-käskyn käytöstä ohjelmassa.
10 PRINT ”HEI MAAILMA!”
20 GOTO10

Yläpuolella näet hyvin yksinkertaisen BASIC-ohjelman. Itsessään ohjelma ei vielä tee mitään mutta naputtelemalla komennon RUN ja painamalla return alkaakin jo tapahtumaan ihmeitä 🙂

Ohjelma tulostaa tauotta HEI MAAILMA! – sanaa. Selvitetäänpä lyhyesti ensimmäistä ohjelmaamme. BASIC-kieliset ohjelmat alkavat aina rivinumerolla. On lähinnä makuasia minkälaista rivinumerointia käyttää, mutta itse suosin systeemiä 10, 20, 30, 40 jne eli ensimmäinen rivi on 10 seuraava 20 jne.

Komento PRINT jo käsiteltiin, mutta mitä tarkoittaa rivillä 20 oleva GOTO10? Itseasiassa käsky selittää itse itsensä eli kyseessä on hyppykäsky riville 10 eli käsky selvennettynä on ”mene riville 10”.

 

Muuttujat

Ohjelmoinnin keskeisimpiä käsitteitä on muuttujat eli variable. Se on koneen keskusmuistissa oleva nimeltä mainittu kohta johon voidaan tietoa tallentaa. Koska käytössä on useita tuhansia, jopa kymmeniätuhansia muistipaikkoja on käyttäjän nimettävä ne jotta ne eivät menisi sekaisin. Nimen käyttö on huomattavasti helpompaa kuin muistipaikan numeron ja sen sijainnin muistaminen muistiavaruudessa. Kotimikroissa BASIC suhtautuu muuttujanimiin siten, että vain nimen kaksi ensimmäistä kirjainta vaikuttavat. Esimerkin valossa: Sanat NUMERO JA KIRJAIN viittaavat eri paikkoihin mutta vastaavasti sanat NUMERO ja NUMBER samaan paikkaan eli siis kaksi ensimmäistä kirjainta nimessä vaikuttavat.

Käytämme nyt seuraavassa esimerkissä yhden merkin mittaista muuttujaa, koska ne ovat nopeampia kirjoittaa. Kun muuttujalle halutaan BASIC-kielessä antaa arvo tarvitaan uusi komento. Se on LET, ja se on peräisin ensimmäisestä BASIC-tulkeista. Koska Commodore 64 koneessa ei tarvitse kirjoittaa tuota LET sanaa käytämme jatkossa

 

LET A=15

tilalla yksinkertaisempaa esitystapaa

A=15

jolloin muuttujalle A sijoitetaan arvo 15. Luku 15 siirtyy koneen muistiin paikkaan johon nimi A osoittaa. Musitipaikan sijainti koneen keskusmuistissa ei näy käyttäjälle. Hän saa muistin sisällön käyttöönsä vain muuttujanimen A avulla.
Kun muuttujalle on annettu arvo voidaan sitä käyttää kuten mitä tahansa lukua.

Artikkeli on ollut jo vuosia jäässä, mutta vihdoin sivustomme innokkaan käyttäjän myötävaikutuksella se saa jatkoa…tästä eteenpäin tekstistä vastaa Markus Salmijärvi (”makedin”).

Muuttujiin voidaan tallentaa myös merkkijonoja, siis myös kirjaimia ja merkkejä. Tällaisilla merkkijonomuuttujilla ei tietenkään voida eikä olisi mielekästä laskea kuten numeromuuttujilla. Niihin voidaan kuitenkin tallentaa esimerkiksi uuden piste-ennätyksen haltijan nimi, mitä numeromuuttujin ei voida tehdä. Merkkijonomuuttujan nimen perään tulee dollarimerkki ja muuttujan arvo annetaan lainausmerkeissä. Esimerkiksi A$ = ”MERKKIJONO”. Samassa ohjelmassa voi olla muuttujat A$ ja A. Tällöin ne nähdään eri muuttujina. Sama pätee vaikka muuttujan nimen pituus olisi kaksi merkkiä.
Taulukot

Taulukko ovat muuttujien erikoistyyppi, joka, kuten nimikin vihjaa, on muuttujista koostuva taulukko. Taulukko määritellään komennolla DIM. DIM:in syntaksi on DIM taulukonnimi(taulukonkoko[, taulukon toisen ulottuvuuden koko…]) Hetkinen! Mikä ihmeen taulukon toinen ulottuvuus? BASICin taulukko voidaan ajatella paperille piirrettynä vastineenaan. Se voi olla yksiulotteinen, jolloin se vastaa paperille merkittyä merkkijonoa, tai se voi olla kaksiulotteinen, jolloin se vastaa varsinaista taulukkoa. Kaksiulotteisen taulukon käyttölogiikka on sama kuin koordinaatiston; tietyn arvon löytämiseksi tarvitsee tietää sen sijainti molemmissa ulottuvuuksissa. Parhaiten kaksiulotteista taulukkoa voi havainnollistaa avaamalla taulukkolaskentaohjelman, ja miettimällä sen toimintalogiikkaa (alkiolla [solulla] on sijainti kahdessa ulottuvuudessa.)

 

Itse asiassa taulukolla voi olla useampiakin ulottuvuuksia. Kolmiulotteinen taulukko on vielä helppo hahmottaa, mutta sitä pidemmälle mentäessä ei kannata yrittää ajatella miltä taulukko näyttäisi tosimaailmassa – riittää kun ymmärtää, että alkion arvon määrittämiseksi tulee tietää sen sijainti kaikissa ulottuvuuksissa.

 

Eräs hämmentävä piirre taulukoissa on, että ne on indeksoitu nollasta alkaen. Niinpä määriteltäessä taulukko komennolla DIM A(9) saadaankin itse asiassa kymmenalkioinen taulukko. Nyt taulukossa on siis alkiot A(0)…A(9) joita kutakin voi käyttää kuten tavallisia muuttujia.
Käyttäjäsyötteet

Useimmat mielekkäät ohjelmat, joita haluaisimme kirjoittaa C64:n kaltaiselle tietokoneelle ottavat jossakin suorituksensa vaiheessa käyttäjältä jotakin syötteitä. Muutoinhan se suoritettaisiin joka kerta tarkalleen samalla tavalla. Meillä on käytössämme muutamia tapoja selvittää mitä käyttäjä tahtoo ohjelmalle kertoa. Otamme näistä käsittelyyn komennot GET jaINPUT.

 

GET ottaa yhden näppäinsyötteen ja tallentaa sen arvon määrättyyn muuttujaan. Komennon syntaksi on GET muuttujannimi. Jos siis esimerkiksi kirjoitamme GET N ja käyttäjä painaa näppäimistössä ykköstä, tallentuu muuttuja N:n arvoksi numero 1. Myös GETiä käytettäessä on mahdollista hyödyntää merkkijonomuuttujia. GET N$ hyväksyy minkä tahansa merkin syötteekseen, GET N antaa virheilmoituksen saadessaan muun merkin kuin numeron.

 

Ohjelma ei GET:in kohdatessaan pysähdy odottamaan että käyttäjä antaa syötteen vaan posottaa suoraan eteen päin. Niinpä ohjelma 10 GET A ei tekisi ajettaessa oikeastaan yhtään mitään. Voimme kuitenkin testata komentoa jo aikaisemmin käyttämäämme metodia hyödyntäen. Ohjelma

10 GET A$
20 PRINT A$
30 GOTO 10

näyttää ruudulla kulloinkin painetun näppäimen merkin. Huomaat, että aina näppäinsyötteen tulostettuaan ohjelma alkaa täyttää ruutua tyhjällä. Muuttuja siis tyhjenee heti kun sen arvo on luettu. Näin ohjelma ei luule, että käyttäjä painelee jatkuvasti samaa näppäintä.

 

INPUT toimii hiukan eri tavalla. Se ottaa käyttäjältä kokonaisen merkkijonon (tai moninumeroisen luvun) ja ohjelma pysähtyy vastaanottamaan käyttäjän syötettä kunnes tämä painaa RETURN. Samalla INPUT-komennolla voidaan vastaanottaa monta muuttujanarvoa. Koska ohjelma pysähtyy odottamaan käyttäjän syötettä, on käyttäjälle myös syytä kertoa mitä hänen tulisi tehdä. Komennon syntaksi on INPUT [selite;] muuttujannimi[, muuttujanimi2 …]. Hakasulkein merkityt kohdat ovat vapaaehtoisia. Näin lyhin mahdollinen määrittely on INPUT muuttuja, missä tapauksessa ruudulle ilmestyy vain kysymysmerkki ja vilkkuva kursori. Toisaalta voitaisiin myös kirjoittaa INPUT ”ANNA NIMESI JA IKASI”; A$, B. Nyt ruudulle ilmestyisi teksti ”ANNA NIMESI JA IKASI?” ja sen perään vilkkuva kursori. Käyttäjä syöttäisi sitten nimensä, painaisi RETURN ja syöttäisi ikänsä. Tiedot tallentuisivat muuttujiin A$ ja B, kumpikin omaansa. INPUTilla hankittu muuttujan arvo ei nollaannu kun se luetaan. Niinpä voisimme kirjoittaa esimerkiksi seuraavanlaisen ohjelman:
10 INPUT ”KERRO NIMESI”; A$
20 PRINT ”TERVE, ”A$
30 PRINT ”OLIHAN NIMESI ”A$”?”

Silmukat

Monet ohjelmat eivät vain suorita muutamaa komentoa ja sammu sitten, vaan jäävät pyörimään kunnes jokin tappaa ne (esimerkiksi käyttäjä kyllästyessään katsomaan samasta sanasta täyttyvää ruutua.) Tällöin ohjelma suorittaa jotakin osaa koodistaan, niin kutsuttua silmukkaa, joko ikuisesti tai kunnes jokin ehto täyttyy (RUN/STOP-näppäimen painaminen ei ole ehto, vaan se sammuttaa ohjelman kylmästi riippumatta siitä, halusiko ohjelmoija niin käyvän.) Olemme oppineet jo erään käskyn, jolla silmukka voidaan toteuttaa: GOTO. GOTO kaikessa yksinkertaisuudessaan saa ohjelman hyppäämään määritetylle koodiriville, mistä sen suoritus jatkuu. Jos rivi, jolle hypätään on ennen riviä, jolla GOTO sijaitsee, on seurauksena ikuinen silmukka (paitsi jos GOTO on ehdollinen, mitä käsittelemme myöhemmin.) GOTO on näppärä tapa toteuttaa silmukka, mutta aina se ei ole paras tapa. Kuvitellaanpa, että haluamme tehdä ohjelman, joka pyytää käyttäjältä muutamia lukuja, laskee niiden keskiarvon ja tulostaa sen sitten.Tämä voitaisiin toki toteuttaa kokonaan ilman silmukoitakin, mutta mikäli pyydettäviä lukuja olisi paljon, vaikkapa sata tai tuhat, tulisi koodin kirjoittamisesta melko epäkäytännöllistä. Apuun rientävät komennot FOR, TO, NEXT ja STEP.

 

FOR, TO, NEXT, STEP toimivat yhdessä luoden silmukan tietyin lähtöarvoin ja suorittaen sitä kunnes tietty tavoite on saavutettu. Syntaksi on seuraava: FOR muuttujannimi = lähtöarvo TO tavoitearvo [STEP askel]. Sitten seuraa silmukassa suoritettava koodi ja lopuksi NEXT muuttujannimi. Eli mitä? Jos esimerkiksi kirjoitettaisiin ohjelma:
10 FOR 1 = A TO 10 STEP 2
20 PRINT A
30 NEXT A

tulostuisi sen ajettaessa ruudulle kaikki parittomat luvut väliltä 1 – 10. Tämä siksi, että aluksi A:n arvoksi määritetään 1 FOR-komennon avulla. Saman tien A:n tavoitearvoksi asetetaan 10 TO-komennolla. STEP kertoo paljonko A:ta kasvatetaan NEXT-komennon yhteydessä. Jos se jätettäisiin pois, olettaisi tulkki arvoksi 1 ja ohjelma tulostaisi kaikki luvut väliltä 1-10. Rivillä 20 tulostetaan muuttujan arvo tuttuun tapaan. Rivillä 30 sijaitsevalla NEXT-komennolla on kaksi tehtävää: se kasvattaa A:n arvoa niin paljon kuin askellukseksi on STEP-komennolla säädetty ja palauttaa ohjelman kulun riville 10. Aiemmin mainittu lukujen keskiarvon määrittävä ohjelma voitaisiin toteuttaa seuraavasti:
10 INPUT ”MONTAKO LUKUA OTETAAN”;LKM
20 DIM ARVOT(LKM – 1)
30 FOR A = 0 TO LKM – 1
40 INPUT ”ANNA LUKU”;ARVOT(A)
50 NEXT A
60 FOR A = 0 TO LKM -1
70 B = B + ARVOT(A)
80 NEXT A
90 B = B/LKM
100 PRINT B

Ohjelma on tehtävään nähden tarpeettoman monimutkainen, näin siksi, että se esittelee monia tähän mennessä käsiteltyjä asioita. Miten kirjoittaisit saman tehtävän hoitavan ohjelman yksinkertaisemmin?
Ohjelman kulun ohjaaminen

Olemme käsitelleet muutamia komentoja ja niiden käyttötapoja, mutta vieläkin olemme joutuneet tyytymään ohjelmiin, jotka vain virtaavat alusta loppuun (tai jäävät junnaamaan paikoilleen.) Kuitenkin tiedämme, että on mahdollista tehdä ohjelmia, jotka reagoivat käyttäjän tekosiin muutenkin kuin näyttämällä ne tälle takaisin tai laskien niillä. Tarvitsemme siis jonkin keinon määritellä mitä tehdään, jos jotakin tapahtuu, mitä taas jos tapahtuu jotakin muuta. Tähän tarpeeseen on komennot IF ja THEN, jos ja sitten. Näiden komentojen syntaksi on IF ehdot THEN seuraus (tätä perusmuotoa on mahdollista toisinaan lyhennellä, mutta se ei ole nyt oleellista.) Ehtona käytetään yleensä tarkistuslausetta ja seuraus voi olla vaikkapa sijoituslause tai GOTO. Eli voitaisiin kirjoittaa vaikkapa IF A = 1 THEN GOTO 40. Huomaa, että toisin kuin joissakin kielissä, myös muuttujan arvon tarkastuksessa käytetään yksinkertaista yhtäsuuruusmerkkiä kaksinkertaisen sijasta. Esimerkkikomennon merkitys lienee melko selvä: jos A:n arvo on 1, hypätään riville 40. Muuten suoritus jatkuu seuraavalta riviltä. Minkäänlaista ”mutta jos” komentoa meillä ei ole käytössä, joten jos A:n arvon ollessa 0 halutaankin hypätä riville 70, täytyy temppu toteuttaa toisella IF-THEN rakenteella joko ennen tai jälkeen toisen tarkastuksen.