Vrije Universiteit Brussel
Faculteit Ingenieurswetenschappen

Inleiding tot programmeren

Inleiding

De Von Neumann computer

Een algemene voorstelling van de architectuur van een computer wordt wel eens een ‘Von Neumann’ architectuur genoemd, geïllustreerd in figuur 1.


Figuur 1: De Von Neumann computer

De computer beschikt over input interfaces zodat de gebruiker nieuwe data aan de computer kan meegeven en hij beschikt over output interfaces om de gewenste resultaten terug naar de gebruiker over te brengen. Verder beschikt hij over een datageheugen dat wordt gebruikt om tijdelijk gegevens in op te slaan. Deze gegevens kunnen komen van de input interface of kunnen het resultaat zijn van eerdere berekeningen. Vanuit dit datageheugen kunnen er tevens gegevens via de output interface naar de gebruiker worden overgebracht. De rekeneenheid (ALU – Arithmetic and Logic Unit) is het onderdeel dat instaat voor het uitvoeren van allerlei bewerkingen op de gegevens die van het datageheugen afkomstig zijn. Deze beweringen zijn voornamelijk wiskundige bewerkingen op twee getallen (optellen, vermenigvuldigen, etc.) of vergelijkingen tussen twee getallen (gelijkheid, groter dan, etc.). Het programmageheugen is de plaats in de computer waar de lijst met de gewenste bewerkingen (m.a.w., het programma) word opgeslagen voordat de bewerkingen worden uitgevoerd. Deze informatie wordt via de program interface in het programmageheugen geladen. Ten slotte is er ook nog een control-unit nodig die de instructies uit het programmageheugen één voor één inleest en decodeert, waarna de nodige signalen worden uitgestuurd naar de andere onderdelen van de computer om de bewerking effectief te kunnen uitvoeren (vb. transfer van data van datageheugen naar ALU, etc.).

Als we een moderne PC vergelijken met deze Von Neumann architectuur vinden we vele overeenkomsten. Input interfaces zijn tegenwoordig de computermuis en het toetsenbord, maar ook apparaten zoals een scanner of een microfoon. De monitor en een printer zijn dan weer typische moderne output interfaces. Naar de ALU wordt tegenwoordig met de term ‘processor’ verwezen. Een PC bezit ook het zogenaamde RAM-geheugen. Dit wordt door het operating system (vb. Windows) onderverdeeld in een stuk waar data in wordt opgeslagen (het datageheugen) en een stuk waar programma-instructies in worden opgeslagen (het programmageheugen). In een PC zijn alle componenten met elkaar verbonden via het moederbord, wat de communicatie tussen alle onderdelen voor zijn rekening neemt (control unit). Uiteraard is de opbouw van een moderne PC veel ingewikkelder dan de basis Von Neumann architectuur. Zo zijn er bijvoorbeeld nog vele extra geheugens ingevoerd (zoals het cache van de processor) en extra ALU’s (zoals de GPU op een grafische kaart). Toch is het duidelijk dat de algemene lijn van de Von Neumann architectuur nog steeds aanwezig is, hoewel deze reeds eind jaren ’40 van de vorige eeuw werd gedefinieerd.

Programmeren

Een programmeerbare machine is per definitie een machine die je een bepaalde sequentie van berekeningen kunt laten doen, waarbij deze sequentie niet vooraf was gedefinieerd bij de fabricatie van de machine. Een programmeerbare computer kan men dus willekeurige sequenties van bewerkingen laten uitoefenen. Hiervoor dient er wel aan de computer te worden ingegeven wat deze bewerkingen zijn. Zoals werd vermeld in paragraaf 1, zal deze sequentie van bewerkingen in de computer worden opgeslagen in het programmageheugen. Deze bewerkingen worden beschreven aan de hand van binaire machinecode. Uiteraard is het zeer onpraktisch voor een gebruiker om op deze manier de gewenste sequentie van bewerkingen in te geven. Daarom zal men de machinecode uitschrijven in een meer leesbare vorm, de zogenaamde assembler-code. Dit is in feite niets meer dan een lijst van instructies die de ALU moet uitvoeren. De beschrijving van een ALU bewerking bestaat standaard uit 5 delen: de gevraagde bewerking (optellen, vergelijken, etc.), de twee adressen uit het datageheugen waar de input data staat, een adres in het datageheugen waar het resultaat wordt weggeschreven en het adres in het programmageheugen waar de volgende uit te voeren opdracht te vinden is:

OPC
OP1
OP2
RES
NEXT

In tegenstelling tot de binaire machinecode, worden in assembler-code de gevraagde bewerkingen door middel van een bepaalde syntax uitgeschreven (vb. ‘ADD’ voor een optelling) en in plaats van de gegevens in het datageheugen aan te spreken met hun fysieke adres kan de programmeur gebruik maken van een meer leesbare naam. Zulke ‘namen’ worden variabelen genoemd, vermits hun waarde kan worden aangepast door input van de gebruiker of door bewerkingen van de ALU. Een programma dat geschreven is in assembler-code dient eerst te worden omgezet in machinecode voordat de computer het programma kan uitvoeren. Een applicatie die deze omzetting kan doen noemen we een assembler. In figuur 2 is een stukje code voorgesteld in assembler-code en in bijhorende machinecode.


Figuur 2: Een programma in machinecode (binair omgezet in beter leesbare getallen) en in assembler-code

Het spreekt vanzelf dat het uitschrijven van complexe programma’s aan de hand van assembler-code een zeer tijdrovende en foutgevoelige opdracht is. Dit heeft geleid tot de ontwikkeling van de zogenaamde hogere programmeertalen, zoals Fortran, Cobol, Modula, Pascal, C, C++, Java, etc. (zie figuur 3). Deze talen laten een programmeur toe om een gewenste functionaliteit te definiëren zonder specifiek elke aparte bewerking van de ALU te moeten uitschrijven. Elke hogere programmeertaal heeft zijn eigen syntax, maar een groot aantal commando’s/structuren vinden we in elke taal terug zoals functies/procedures, if-statements, for/while lussen, etc. Code geschreven in een hogere programmeertaal zal uiteraard moeten worden omgezet in machinecode voordat het programma kan worden uitgevoerd. Een applicatie die dit kan verwezenlijken noemen we een compiler. Merk op dat het omzetten van hogere programmeercode in machinecode veel moeilijker is dan het omzetten van assembler-code in machinecode, vermits de compiler zelf zal moeten uitmaken welke sequentie van ALU bewerkingen exact nodig is om de gewenste commando’s uit te voeren. Een voorbeeld van code in een hogere taal geschreven is gegeven in figuur 4.




Figuur 3: Evolutie van programmeertalen



Figuur 4: Code in een hogere programmeertaal


Programmeren in praktijk

Bij wijze van illustratie kan je eens kijken naar de file Tetris_assembler.txt (in folder ‘Tetris_Assembler’). Dit is de code van een klassieke Tetris, geschreven in assembler-code. Je kunt het programma eens proberen te runnen om te zien wat de code juist doet. Zoals je kunt merken is zowel het lezen als het schrijven van een programma in assembler-code een zeer lastige taak. Daarom wordt er bijna niet meer rechtstreeks in assembler-code geprogrammeerd en maakt men gebruik van hogere talen. In de folder ‘Tetris_C’ vind je bestand Tetris_C.txt. Dit is een (gedeeltelijke) implementatie van het Tetris spel, geschreven in de programmeertaal C. Zoals je kunt merken is deze code veel overzichtelijker en kan je duidelijk de instructies lezen. Je kunt desgewenst de gecompileerde code ook eens uitvoeren.

Om het werk van een programmeur eenvoudiger te maken zijn er applicaties ontwikkeld die het programmeren in één of meerdere hoge programmeertalen vergemakkelijkt. Deze zogenaamde ‘editors’ zullen vaak de programmeercode automatisch van de nodige lay-out en kleurcodes voorzien zodat de code beter leesbaar is voor de programmeur. Vaak kunnen deze editors ook worden gebruikt voor het omzetten van de code in machine-instructies (m.a.w, het compileren) en ook voor het ‘verpakken’ van deze machinecode in een uitvoerbaar bestand (‘executable file’).

Voor deze cursus zullen we gebruik maken van de Modula 2 programmeertaal. De editor die we zullen gebruiken heet ‘XDS’. Tekstbestanden die Modula 2 code bevatten worden opgeslagen op de computer met de extensie ‘.mod’.

Open eerst het bestand ‘HelloWorld.mod’ (in folder ‘Voorbeelden’) in de XDS editor. Bekijk de code en probeer ze eens te compileren, een executable file te maken en deze uit te voeren. Dit kan allemaal met één enkele klik op de knop ‘Run’ in XDS (knop met het blauwe mannetje). Let er wel op dat je het ‘.mod’ bestand ergens op je computer hebt opgeslagen in een pad dat geen spaties bevat. Bekijk nu de code eens een beetje grondiger. Een Modula 2 code is steeds opgebouwd met een vaste structuur. Zo kan je in dit heel simpel voorbeeld duidelijk het blok tussen de woorden ‘BEGIN’ en ‘END HelloWorld’ opmerken. In dit blok zullen steeds van boven naar onder de bewerkingen worden geschreven die de computer moet uitvoeren. In dit voorbeeld staan er maar twee commando’s beschreven: het ‘WrStr’ commando dat wordt gebruikt om een bepaalde tekst op het scherm af te printen en het ‘WrLn’ commando dat wordt gebruikt om in het outputvenster een regel naar onder te gaan.

Open nu bestand ‘Optelling1.mod’. Hier kan je zien hoe je variabelen kunt gebruiken om bepaalde waarden in op te slaan.

Als je ‘Optelling2.mod’ opent kan je zien hoe je de optelling uit het vorige voorbeeld kunt afhankelijk maken van de input van de gebruiker.

Een zeer belangrijk aspect bij programmeren is het gebruik van controle-statements. Open het bestand ‘Control1.mod’. Hier kan je zien hoe het verloop van een programma kan gestuurd worden door gebruik te maken van een ‘IF’ structuur. Naargelang de waarde die wordt gegeven aan ‘var1’ zal het programma een andere weg inslaan en dus in dit geval een andere tekst op het scherp printen.

Open nu het bestand ‘Control2.mod’. In dit voorbeeld kan je zien hoe een bepaalde opdracht meerdere keren kan worden uitgevoerd. Merk hierbij op dat het aantal herhalingen afhankelijk kan gemaakt worden van de waarde van een bepaalde variabele. In dit voorbeeld zal de tekst een aantal keer achter elkaar worden afgeprint. Het exacte aantal keer is afhankelijk van de waarde van ‘var1’, die kan worden ingegeven door de gebruiker. Bij elke herhaling zal de waarde van variabele ‘i’ met 1 verhogen want dat staat zo geschreven in de definitie van de FOR-lus. Verder kan je in dit voorbeeld ook zien hoe het resultaat van een bewerking op een bepaalde variabele opnieuw kan worden opgeslagen in die variabele zelf. Zo zal bij elke herhaling van de code die binnen de FOR-lus staat, de waarde van ‘var2’ verdubbelen. Dit kan je controleren door het programma eens te compileren en uit te voeren.

Vele programmeertalen geven de programmeur de mogelijkheid om grafische commando’s te gebruiken. Dit zijn opdrachten die ervoor zorgen dat er iets wordt ‘getekend’ op het scherm. In het bestand ‘Grafisch2D.mod’ (in folder ‘Voorbeelden_grafisch’) is een voorbeeld gegeven van enkele eenvoudige grafische functies. Als je de code eens uitvoert kan je zien wat het resultaat van deze functies is. Meestal komt het erop neer dat je in je programma eerst de coördinaten definieert of berekent waar je de figuur wilt tekenen en dat je deze coördinaten dan meegeeft aan de procedure die deze figuur effectief tekent.

Om een wereld in 3D te tekenen zijn er verschillende mogelijkheden. Algemeen komt het erop neer dat je het voorwerp eerst definieert in 3D coördinaten en dat je daarna in 3D een vlak tekent tussen elke set van aanliggende coördinaten. Op die manier kan je eender welke vorm in 3D tekenen, waarbij het 3D-belichtingseffect wordt verkregen door de kleur van al deze kleine vlakjes aan te passen naargelang hun positie. Voor deze cursus zullen wij een eenvoudige 2D naar 3D omzetting gebruiken, zoals zal worden uitgelegd in het volgende deel van de cursus. Open nu het bestand ‘Grafisch3D.mod’. Dit is gelijkaardig met de vorige demo, maar hier worden er figuren in 3D getekend. Merk echter op dat ‘onder de motorkap’ alles wordt getekend aan de hand van de 2D tekenopdrachten, maar door de juiste omzetting tussen de 3D en 2D coördinaten en het gebruik van verschillende kleuren voor de vlakken wordt er een 3D effect bereikt.

Als laatste illustratie open je eens ‘GrafischAnimatie1.mod’. Dit is een voorbeeld van hoe je een animatie kan programmeren. Feitelijk komt dit er op neer dat je een bepaald object op een bepaalde plaats op het scherm tekent, dat je dit object hier voor een bepaalde tijd laat staan en dat je nadien het object wist en het opnieuw tekent maar dan een beetje verplaatst. Als je de demo uitvoert kan je dit duidelijk zien. Uiteraard kan je dit geen mooie animatie noemen, vermits je al de afzonderlijke stapjes kunt waarnemen. Open nu ‘GrafischAnimatie2.mod’. Dit is net hetzelfde programma, maar nu is de tijd tussen elke stap korter en ook de verplaatsing van de rechthoek tussen elke stap is kleiner. Als je dit programma uitvoert zie je een veel vloeiendere animatie verschijnen.