Inhoud leereenheid 3 De instructiesetarchitectuur Introductie Leerkern 1 89 90 Draaiboek van de computer 90 Locatie van de operanden: geheugen of registers 93 Classificatie van instructies 96 2.1 Categorieën instructies 96 2.2 Opbouw van de instructies: het instructieformaat 98 2.3 Assembleertalen 101 2.4 Het effect van een instructie 102 Adresseringsmethoden 107 1.1 2 3 Samenvatting Zelftoets 111 112 Terugkoppeling 1 2 88 113 Uitwerking van de opgaven 113 Uitwerking van de zelftoets 115 Leereenheid 3 De instructiesetarchitectuur INTRODUCTIE Niveau 2 en 3 in leereenheid 2. In de vorige leereenheid is de fysieke opbouw van een computer in hoofdlijnen uiteengezet: de hardware. Om in een gebruikersapplicatie op een efficiënte manier de hardware te kunnen aansturen is echter een bruikbare verzameling machine-instructies nodig, alsmede een tussenlaag van systeemsoftware. Software is een essentieel onderdeel van een computer als compleet systeem dat een bepaalde functie voor een gebruiker uit kan voeren. Vanuit het oogpunt van de computer als samenwerkend geheel van hardware en software moet de relatie tussen deze twee worden gedefinieerd en vastgelegd. Reeds in de vorige leereenheid heeft deze relatie een naam gekregen: de instructiesetarchitectuur. Door de definitie van deze interface tussen hardware en software is het ook mogelijk om programma’s voor computers te ontwikkelen, als men geen uitvoerige kennis van de implementatie van de hardware heeft. In deze leereenheid staan we stil bij het begrip instructiesetarchitectuur. Een doordacht ontwerp van de instructieset is van groot belang. Immers, eenmaal vastgelegd in de hardware zijn wijzigingen in de instructieset bijna niet meer mogelijk. En, instructiesets kunnen lang leven! De basisinstructieset van bijvoorbeeld de bekende Intel-processoren leeft al meer dan dertig jaar. Fundamentele ontwerpkeuzes dienen zich aan bij het vastleggen van de instructiecyclus, dus de rij deelacties die afgewerkt wordt bij uitvoering van elke instructie. De instructies die in een instructiesetarchitectuur zijn vastgelegd, kunnen worden ingedeeld in een aantal categorieën, elk met zijn eigen functie en bijbehorende vorm. Voor alle instructies moet worden aangegeven hoe ze in bitpatronen, nullen en enen, in de computer gecodeerd worden, het zogenaamde instructieformaat. Voor het schrijven en begrijpen van programma’s in assembleertaal is kennis van de corresponderende symbolische notatie nodig. De functie of betekenis van een instructie kan op een aantal manieren worden aangegeven. Natuurlijke taal als medium om dit te doen is vaak te dubbelzinnig. In deze leereenheid geven we aan hoe formele notaties gebruikt worden bij het vastleggen van de betekenis van instructies. Dat een instructie moet aangeven wat de gewenste operatie is en waarmee deze moet worden uitgevoerd, is reeds in de vorige leereenheid aangegeven. De gegevens waarmee de operatie wordt uitgevoerd, de operanden, moeten door de instructie worden aangegeven, zodat de microprocessor deze operanden tot zijn beschikking kan krijgen. OUN 89 De werking van computersystemen Het aangeven van de plaats van de operanden wordt het adresseren van de operanden genoemd. Er bestaan diverse manieren om te adresseren, elk met zijn specifieke voor- en nadelen, en dus zijn eigen toepassingsgebied. LEERDOELEN Na het bestuderen van deze leereenheid wordt verwacht dat u – het standpunt kunt verdedigen dat de computer als een instructieverwerker beschouwd kan worden – het belang van de instructiecyclus kent – instructiesets kunt classificeren op basis van de locatie van de operanden (in registers of geheugen) – de verschillende soorten instructies en hun functie kunt noemen – enkele gebruikelijke instructieformaten kunt beschrijven – weet wat met de termen ‘mnemonische instructie’ en ‘assembleertaal’ bedoeld wordt – de formele definitie van instructies met Hoare-triples kunt interpreteren – de belangrijkste technieken om operanden te adresseren kunt beschrijven. Studeeraanwijzingen In deze leereenheid worden veel voorbeelden van instructies, instructieformaten en assembleertaalnotaties gegeven. Deze voorbeelden zijn in principe ontleend aan de doordachte en goed opgebouwde instructieset van de MIPS-processor, die in bepaalde werkstations en embedded systemen wordt toegepast. Op grond van didactische overwegingen zijn de instructies hier echter enigszins aangepast. Er wordt dan ook niet gepretendeerd dat de gepresenteerde voorbeelden bruikbaar zijn in de praktijk en het heeft dus ook geen zin de instructies uit het hoofd te leren. In leereenheid 16 komen we terug op de instructieset van de MIPS. De studielast van deze leereenheid is 5 uur. LEERKERN 1 Gegevensverwerker Draaiboek van de computer In de vorige leereenheid is de computer neergezet als een apparaat dat de invoer die het krijgt aangeboden, omzet in uitvoer. Op deze wijze voorziet de computer in een behoefte van de gebruikers. De computer werd getypeerd als een gegevensverwerker. Vanuit het oogpunt van de gebruiker is dat de functie die een computer vervult. Ontwerpers van hardware en systeemsoftware zien de computer echter vanuit een ander perspectief. Zij moeten namelijk de functionaliteit voor de gebruiker realiseren. Het beeld van een computer als ‘black box’, zoals de gebruiker dat heeft, is dan niet meer voldoende. Deze verschillen in perspectief zien we bijvoorbeeld ook bij de auto. Voor de gebruiker is een auto een vervoermiddel om van A naar B te komen. Voor de ontwerper is de auto een mechanisme dat in staat is benzine om te zetten in een voortgaande beweging. Hij zal ernaar streven die omzetting zo efficiënt mogelijk te laten verlopen. 90 OUN Leereenheid 3 De instructiesetarchitectuur Enerzijds geven de ontwerpers van systeemsoftware en hardware een invulling aan de ‘black box’ die de gebruiker ziet. Anderzijds moet de ontwerper van gebruikersapplicaties de hardware inzetten om functionaliteit voor de gebruiker te realiseren. Hij maakt daarbij echter niet direct gebruik van de hardware, maar van functies in de systeemsoftware, de interface tussen hardware en applicatiesoftware. De applicatieprogrammeur staat dan ook dichter bij het gebruikersperspectief van de computer dan bij het perspectief van de ontwerper van systeemsoftware en hardware. Instructieverwerker Zoals reeds in de vorige leereenheid is vermeld, is de verzameling beschikbare instructies, dus de instructiesetarchitectuur, bepalend voor de manier waarop systeemsoftware de hardware kan gebruiken. Voor de ontwerper van systeemsoftware is dat de essentie van de computer. Hij zal moeten bepalen welke instructies hij in welke volgorde gaat gebruiken. Hij ziet de computer dan ook meer als instructieverwerker dan als gegevensverwerker. Ook de ontwerper van hardware ziet de computer eerder als instructieverwerker. Het is zijn taak ervoor te zorgen dat de hardware in staat is de instructies te verwerken. De instructies en hun definitie dienen daarbij als contract tussen de beide ontwerpers. Von Neumanncyclus of instructiecyclus Het beeld van de computer als instructieverwerker komt duidelijk tot uitdrukking in de Von Neumanncyclus of instructiecyclus. In figuur 3.1 is de cyclus weergegeven die van toepassing was op de klassieke processor, nog voordat er sprake was van met IC-technologie gebouwde microprocessoren. In essentie volgen echter zelfs de nieuwste microprocessoren nog steeds deze cyclus. Deze cyclus geeft de activiteiten weer die de microprocessor achtereenvolgens moet uitvoeren om een instructie te verwerken. Als een cyclus is afgerond, begint de microprocessor weer van voor af aan. De activiteiten zijn: – instructie ophalen – instructie decoderen – instructie uitvoeren. FIGUUR 3.1 De Von Neumanncyclus of instructiecyclus van de klassieke processor OUN 91 De werking van computersystemen Ophalen van de instructie De microprocessor begint het uitvoeren van instructies met het ophalen van de instructie vanuit het interne geheugen. Via een busoperatie wordt het interne geheugen benaderd op de locatie die wordt aangegeven door de program counter. De instructie wordt in het instructieregister van de microprocessor gezet. Ook wordt de program counter aangepast, zodat deze alvast naar de volgende instructie wijst. Hiermee is de eerste fase van de instructiecyclus afgerond. De control unit heeft nu de instructie beschikbaar om de volgende fase uit te voeren: het decoderen van de instructie. Elke instructie van een instructieset heeft een afgesproken indeling: het instructieformaat. In de control unit is kennis over de gebruikte indeling vastgelegd in elektronische schakelingen, waarmee de control unit de instructie kan decoderen. Uit de gedecodeerde instructie kunnen de te nemen acties bepaald worden. Hierna zal het datapad, onder aansturing van de control unit, de instructie uitvoeren. Als de instructie is uitgevoerd, zal de volgende iteratie van de cyclus beginnen met het ophalen van de volgende instructie op de locatie die wordt aangegeven door de inmiddels gewijzigde program counter. Deze cyclus gaat in een werkende computer eindeloos door. Decoderen van de instructie Uitvoeren van de instructie Leestekst Wie vond wat uit? Het toeschrijven van een uitvinding aan alleen één persoon is meestal een hachelijke onderneming. Thomas Edison geldt als de uitvinder van de gloeilamp, maar andere uitvinders werkten aan hetzelfde idee en feitelijk was Edison net iets eerder met de patentaanvraag. Evenzo worden de Wright-broers gezien als de uitvinders van de vliegmachine, maar ze concurreerden met en profiteerden van het werk van vele tijdgenoten, die op hun beurt min of meer leunden op Leonardi da Vinci die al in de vijftiende eeuw speelde met gedachten over ‘vliegende machines’. Zelfs Leonardo’s ontwerpen zijn waarschijnlijk gebaseerd op ideeën van anderen. In voorgaande gevallen heeft de erkende uitvinder natuurlijk legitieme rechten op de titel ‘uitvinder van’. In andere gevallen heeft de historie deze titel ten onrechte verleend; een voorbeeld hiervan is het ‘stored program concept’. John von Neumann is zonder twijfel een briljante wiskundige die veel op zijn naam mag schrijven. Maar de eer als uitvinder van het ‘stored program concept’ komt eigenlijk toe aan een groep van onderzoekers onder leiding van J. P. Eckert van de Moore School of Electrical Engineering van de universiteit van Pennsylvania. John von Neumann was eerder degene die in een interne notitie naar dit concept verwees, waarna de overlevering hem bombardeerde tot de uitvinder van dit concept. Op grond hiervan treffen we in de litteratuur steeds meer de term ‘instructiecyclus’ aan in plaats van ‘Von Neumanncyclus’. [Bron: http://www.library.upenn.edu/exhibits/rbm/mauchly/jwm9.html en Brookshear J. G., Computer Science An Overview, 10e druk Pearson International EditionISBN-13: 978-0-321-54428-5 © 2009] Wat: soort actie 92 In de vorige leereenheid is reeds aangegeven dat een instructie altijd twee zaken moet aangeven, de operatie en de operanden. Elke instructie moet aangeven wat het datapad moet doen, met andere woorden wat voor soort actie er uitgevoerd moet worden. Deze informatie, de aanduiding van het soort actie, is dan ook altijd opgenomen in het OUN Leereenheid 3 De instructiesetarchitectuur instructieformaat. Na het ophalen van de instructie uit het geheugen is deze informatie in de microprocessor beschikbaar. Door het decoderen weet de microprocessor (de control unit) vervolgens wat er moet gebeuren. 1.1 Waarmee: de operanden LOCATIE VAN DE OPERANDEN: GEHEUGEN OF REGISTERS De meeste instructies geven bovendien aan waarmee de actie dient te worden uitgevoerd: de operanden. Soms is het nuttig de behandeling van operanden expliciet te maken. De klassieke instructiecyclus in figuur 3.1 suggereert namelijk dat ‘uitvoeren instructie’ een simpele interne operatie in de processor is. Dat hoeft echter niet zo te zijn, dat hangt af van de locatie van de operanden. Operanden kunnen namelijk in verschillende componenten van de computer zijn opgeslagen: in het interne geheugen van de computer of in de registers van de microprocessor. Ook voor de bestemming van het resultaat van een instructie geldt dat dit een geheugenlocatie of een register kan zijn. Bij het ontwerp van een computer wordt vaak een duidelijke afspraak gemaakt over de locatie van operanden en resultaten. Op grond van het onderscheid in locatie kunnen bestaande instructiesetarchitecturen in twee groepen worden ingedeeld, elk met zijn eigen voor- en nadelen, namelijk – geheugen-geheugen – register-register Instructiesetarchitecturen Leestekst In feite geldt deze onderverdeling alleen voor een (belangrijke) subgroep van instructiesetarchitecturen: de registerarchitecturen. Alternatieven als stackgeoriënteerde en accumulatorarchitecturen laten we in deze cursus echter buiten beschouwing. Geheugengeheugen CISC Register-register RISC Register-geheugen Spreek uit: ‘sisk’ In paragraaf 2.2 en in deel 4 komen we nog terug op CISC versus RISC. Spreek uit ‘risk’ Beschrijving geheugengeheugenarchitectuur Bij geheugen-geheugenarchitecturen kunnen instructies voor zowel de operanden als voor het resultaat een geheugenlocatie gebruiken. Bijvoorbeeld in één instructie de operanden uit het geheugen laden, een bewerking op deze operanden uitvoeren en vervolgens het resultaat weer in het geheugen opslaan. Een dergelijke ingewikkelde instructie is typerend voor de zogenaamde Complex Instruction Set Computers (CISC). We zullen later in deze leereenheid hiervan een voorbeeld zien. Bij register-registerarchitecturen worden zowel voor de operanden als voor het resultaat altijd registers van de processor gebruikt. Deze opzet maakt een veel eenvoudiger implementatie van de instructieset mogelijk, en dus een minder gecompliceerde en vaak snellere processor. Deze instructies worden vaak toegepast in zogenaamde Reduced Instruction Set Computers (RISC). Ten slotte zijn registergeheugenarchitecturen een tussenvorm van de beide eerdergenoemde architecturen. In de klassieke instructiecyclus van figuur 3.1 zijn het ophalen van de operanden en het wegschrijven van het resultaat niet zichtbaar. Die acties worden opgevat als onderdeel van de uitvoeringsfase. Nemen we, voor een geheugen-geheugenarchitectuur, deze acties wel expliciet op, dan ontstaat een verfijning van de cyclus zoals weergegeven in figuur 3.2. OUN 93 De werking van computersystemen ophalen instructie (uit intern geheugen) (naar intern geheugen) schrijven resultaat decoderen instructie (uit intern geheugen) uitvoeren instructie FIGUUR 3.2 Ophalen van de operanden ophalen operanden Verfijning van de instructiecyclus voor een geheugengeheugenarchitectuur In die figuur is te zien dat na het decoderen van de instructie, als bekend is waar de operanden zich bevinden, die operanden worden opgehaald. De control unit initieert daartoe de juiste acties in het datapad. Bij de geheugen-geheugenarchitectuur bevinden de operanden zich meestal in het interne geheugen, dus moeten ze via een busoperatie worden opgehaald en in de operandregisters A en B gezet. Na het uitvoeren van de instructie wordt het resultaat naar de juiste locatie geschreven. Ook hierbij is vaak weer een busoperatie nodig, ditmaal om data naar het geheugen te schrijven. Kortom, instructies en operanden komen uit het interne geheugen, resultaten van een instructie worden naar het interne geheugen weggeschreven. Schrijven van het resultaat Binaire instructies In feite is de cyclus van figuur 3.2 nog steeds een simplificatie. We halen in één actie meer dan één operand op. Dit vereist een verdere verfijning van figuur 3.2. In leereenheid 2 zagen we dat een microprocessor over twee operandregisters beschikt; het kan dus nodig zijn om twee operanden op te halen. Bijvoorbeeld bij het optellen van twee getallen (add) is inderdaad sprake van twee operanden. Dergelijke instructies noemen we binair. In de meeste instructiesets komen ook instructies voor met slechts één operand, bijvoorbeeld het omkeren van het teken van een getal (neg). Deze instructies noemen we unair. Unaire instructies OPGAVE 3.1 Het onderscheid tussen unaire en binaire operaties wordt niet alleen in instructiesets gemaakt. Waar elders, binnen of buiten de informatica, maakt men dit onderscheid ook? Ten slotte is het denkbaar dat er instructies zijn waarbij helemaal geen operanden opgehaald hoeven te worden, bijvoorbeeld als er alleen iets binnen de processor, intern dus, met de registers, hoeft te gebeuren, zoals het getal 0 in een register laden. Er is in dat geval ook geen resultaat om weg te schrijven. De verschillende mogelijkheden geven aanleiding tot een verdere verfijning van de instructiecyclus in figuur 3.3. Afhankelijk van het aantal operanden en het al dan niet moeten wegschrijven van een resultaat kunnen verschillende paden in het schema gevolgd worden. Eigenlijk is er dus sprake van meer dan één soort instructiecyclus. 94 OUN Leereenheid 3 FIGUUR 3.3 De instructiesetarchitectuur Compleet draaiboek voor de processor bij een geheugen-geheugenarchitectuur Figuur 3.3 is op te vatten als een soort draaiboek voor de processor, in dit geval voor een geheugen-geheugenarchitectuur. Voor een registerregisterarchitectuur zal het draaiboek er weer heel anders uitzien. Zo zijn in die architectuur de operanden voor ALU-berekeningen reeds in de registers van de microprocessor aanwezig. Ze hoeven dus niet uit het geheugen te worden opgehaald. Ook wordt het resultaat in dat geval naar een register in de processor geschreven. Voor deze berekeningen zou de instructiecyclus in figuur 3.1, waarbij ‘uitvoeren instructie’ als interne operatie in de processor wordt opgevat, dus al volstaan. Waarom is de instructiecyclus in figuur 3.1 toch niet afdoende om alle instructies voor een register-registerarchitectuur te beschrijven? Beschrijving register-registerarchitectuur Load-instructie Store-instructie Load-storearchitectuur Als alle instructies van een register-registerarchitectuur de cyclus van figuur 3.1 zouden volgen, is er geen communicatie met de rest van de computer meer: alles speelt zich af in de registers van de microprocessor. Er moeten bijvoorbeeld instructies zijn voor het ophalen van waarden uit het geheugen en het schrijven van waarden in het geheugen. De eerste soort noemen we load-instructies. Daarbij wordt uitsluitend een waarde gelezen van het geheugen naar een register. De tweede soort, waarbij alleen een waarde van een register in het geheugen wordt geschreven, noemen we store-instructies. Vandaar dat register-registerarchitecturen ook wel load-store-architecturen genoemd worden. Het essentiële verschil met de geheugen-geheugenarchitectuur is dus dat communicatie met het geheugen en het uitvoeren van een operatie op de gegevens nooit in één en dezelfde instructie gebeurt, maar altijd apart. In figuur 3.4 is schematisch het draaiboek voor de processor bij een load-storearchitectuur weergegeven. Net als bij de register-registerarchitectuur zijn de eerste twee stappen in de cyclus steeds hetzelfde: instructie ophalen en decoderen. Pas na het decoderen is duidelijk om wat voor soort instructie het gaat, en welke tak van de cyclus verder gevolgd zal worden. OUN 95 De werking van computersystemen FIGUUR 3.4 Het draaiboek van de processor bij een loadstorearchitectuur (register-register) OPGAVE 3.2 Welk gevolg heeft een load-storearchitectuur op de eerder genoemde (leereenheid 2) Von Neumann bottleneck ten opzichte van een geheugen-geheugenarchitectuur? 2 Classificatie van instructies 2.1 CATEGORIEËN INSTRUCTIES Een instructiesetarchitectuur bevat een groot aantal instructies die elk te onderscheiden zijn van andere instructies door het effect dat zij teweegbrengen. Om enige ordening in de veelheid van instructies aan te brengen worden de volgende categorieën onderscheiden: – ALU-operaties – transportinstructies – spronginstructies – testinstructies. ALU-operaties De genoemde instructies zijn hier alleen voorbeelden. Aritmetische instructies Voor representaties van getallen en andere waarden zie blok 2. Logische operaties en schuifoperaties 96 Ook logische en schuifoperaties komen in blok 2 uitgebreid aan bod. ALU-operaties zijn die instructies die een berekening uitvoeren met de operanden. De berekening wordt uitgevoerd door de ALU. Onder de ALU-operaties vallen bijvoorbeeld optellen (add), aftrekken (sub), vermenigvuldigen (mult) en delen (div). Deze operaties worden ook wel aangeduid met aritmetische instructies. Bij aritmetische instructies worden berekeningen (optellen, aftrekken, …) uitgevoerd met getallen. Een computer wordt echter niet alleen gebruikt voor de verwerking van getallen. Een waarde of een rij waarden in het geheugen van een computer kan ook een naam of adres voorstellen, een foto, of een geluidsfragment. Ook deze waarden worden, net als getallen, in de computer gerepresenteerd door bitpatronen. Om ook op deze gegevens nuttige operaties uit te kunnen voeren bevat elke instructieset logische operaties en schuifoperaties, welke eveneens tot de categorie ALU-operaties worden gerekend. OUN Leereenheid 3 De instructiesetarchitectuur Logische instructies voeren bijvoorbeeld bewerkingen op individuele bits van operanden uit. Het op 0 of 1 zetten van individuele bits van een operand en het verschuiven van bits binnen een woord is hiermee mogelijk. Transportinstructies De transportinstructies verzorgen het transport van gegevens in de computer. Een instructie die gegevens uitwisselt tussen geheugen en registers van de processor, is bijvoorbeeld een transportinstructie, zoals de instructies store-word (sw) en load-word (lw). Daarnaast zijn er transportinstructies die gegevens tussen I/O-controllers en registers van de microprocessor mogelijk maken, zoals de in (0x34) en out (0x35) instructies waarbij 0x34 en 0x35 de namen van de I/O-registers zijn. Ook zijn er instructies in deze categorie die het transport van gegevens tussen de registers van de processor onderling mogelijk maken. Bijvoorbeeld de move-low-instructie, die de rechterhelft van een productregister in een algemeen register plaatst. OPGAVE 3.3 In leereenheid 2 is onderscheid gemaakt tussen memory-mapped I/O en I/O-mapped I/O. Stel dat een computersysteem gebruikmaakt van memory-mapped I/O. Wat betekent dit voor de categorie transportinstructies? Spronginstructies Denk aan de ifthen-else-, for- en while-opdrachten uit hogere programmeertalen. Voorwaardelijke spronginstructie Engels: branch Spronginstructies maken het voor de programmeur mogelijk om het verloop van het programma te beïnvloeden. Zoals reeds in de vorige leereenheid is opgemerkt, bestaat een programma uit een rij instructies die in het geheugen is opgeslagen (figuur 3.5). Elke instructie heeft in dat geheugen een adres. Als de instructiecyclus normaal wordt gevolgd, dan zal een programma ergens beginnen en de instructies vanaf dat beginpunt één voor één, in de gegeven volgorde, afwerken. Dit is echter onvoldoende om zinvolle programma’s te kunnen schrijven. Sommige stukken programma moeten alleen onder een bepaalde voorwaarde worden uitgevoerd en andere juist weer niet. Ook moet het mogelijk zijn om bepaalde instructies meerdere malen uit te voeren. Er is dan sprake van een programmalus. De instructies die de beide genoemde constructies mogelijk maken, zijn de spronginstructies. Met het uitvoeren van een spronginstructie wordt onder zekere voorwaarden de program counter (PC) van de processor expliciet gewijzigd. Het nieuwe adres in de PC is dan niet langer de locatie van de volgende instructie in de rij, maar de locatie van de in de spronginstructie aangegeven instructie in het programma. Bij het ophalen van de volgende instructie wordt dus niet verdergegaan met de instructie na de reeds afgeronde instructie, maar met de instructie op het sprongadres. In het voorbeeld van figuur 3.5 wordt na de instructie ‘jump 132’ instructie 9 op adres 132 uitgevoerd, in plaats van instructie 4 op adres 127. Spronginstructies zijn er in twee soorten, voorwaardelijke en onvoorwaardelijke sprongen (conditioneel en niet-conditioneel). Bij voorwaardelijke spronginstructies volgt alleen een sprong naar de aangegeven locatie als aan een bepaalde voorwaarde is voldaan. OUN 97 De werking van computersystemen Onvoorwaardelijke spronginstructie Engels: jump Zo niet, dan wordt gewoon verdergegaan met de volgende instructie, alsof er geen spronginstructie was geweest. Vaak zijn er bijvoorbeeld instructies waarbij gesprongen wordt als een operand gelijk of ongelijk aan nul is, of groter of juist kleiner dan nul. Bij een onvoorwaardelijke spronginstructie daarentegen wordt altijd naar de aangegeven locatie gesprongen (bijvoorbeeld jump 132 in figuur 3.5). FIGUUR 3.5 Testinstructie Voorbeeld van een sprong, het gevolg van de instructie in geheugenplaats 126 ‘jump 132’ Met testinstructies kan de waarde in een register of het geheugen worden vergeleken met een andere waarde. Het resultaat van een dergelijke instructie is uit te drukken als waar of niet waar, weergegeven door respectievelijk 1 of 0. Een voorbeeld is de test of een getal groter dan of gelijk is aan een ander getal. Het resultaat van zo’n test zou weer gebruikt kunnen worden in een voorwaardelijke spronginstructie. OPGAVE 3.4 Hoe verhouden testinstructies zich tot voorwaardelijke spronginstructies? 2.2 Instructieformaat Velden 98 OPBOUW VAN DE INSTRUCTIES: HET INSTRUCTIEFORMAAT Instructies worden in de computer gerepresenteerd door bitrijen die de processor de informatie moeten verschaffen over de operatie en de operanden. De instructies in een instructieset hebben steeds een vaste, voorgeschreven indeling, het instructieformaat. Het instructieformaat geeft een indeling van de instructies in velden met elk een bepaalde lengte in bits. De instructiesetarchitectuur definieert deze velden, met de mogelijke waarden en hun betekenis. OUN Leereenheid 3 De instructiesetarchitectuur Instructiesets verschillen in de praktijk in hoge mate van elkaar. Het verschil komt niet alleen tot uitdrukking in de verscheidenheid van de instructies, maar ook in de gebruikte formaten. Instructiesets die door de huidige microprocessoren worden ondersteund, zijn niet in één keer ontstaan, maar evolueerden door de jaren heen, waarbij de complexiteit vaak groeide. Er bestaat dan ook niet één algemeen geldend formaat voor instructies. Zelfs binnen één instructieset worden nog verschillende formaten gebruikt. Figuur 3.6 geeft een idee hoe een instructieformaat er uit zou kunnen zien, met een indeling in velden en hun betekenis. soort opdracht ALU-op. sprong test ... algemene registers opcode rs1 rs2 rd * func 6 bits 5 bits 5 bits 5 bits 5 bits 6 bits operaties + – * / ... * = in dit specifieke geval niet gebruikt FIGUUR 3.6 rs: register source (Engels voor ‘bron’) rd: register destination (Engels voor ‘bestemming’) Een voorbeeld van een instructieformaat De instructies met de indeling van figuur 3.6 zijn 32 bits lang. Het (register-register)formaat geeft de instructies weer die registers gebruiken voor zowel de beide operanden als voor het resultaat. De 32 bits van deze instructies zijn opgedeeld in de volgende velden: – opcode: de operatiecode, waarmee wordt aangegeven wat voor soort instructie het is (in dit geval de ALU-operatie ‘aftrekken’, te zien aan de inhoud van het functieveld ‘-‘) – rs1: het nummer van het register waar de eerste operand te vinden is (een verwijzing dus, bijvoorbeeld volgens de pijl in figuur 3.6) – rs2: het nummer van het register waar de tweede operand te vinden is (ook een verwijzing) – rd: het nummer van het register waar het resultaat naartoe moet (weer een verwijzing) – *: een veld dat bij dit formaat niet gebruikt wordt – func: de functie, ofwel operatie, die door de ALU toegepast moet worden (in dit geval aftrekken). Andere instructies in dezelfde instructieset kunnen een ander formaat hebben, dus een andere indeling in velden. Verder worden in dit voorbeeld, ontleend aan de MIPS-processor, 5 bits niet gebruikt. Dit komt doordat bij de MIPS-processor alle instructies een vaste lengte van 32 bits hebben, waardoor het decoderen wordt vereenvoudigd. In andere instructiesets kunnen de instructieformaten echter verschillende lengtes hebben, afhankelijk van de complexiteit en de hoeveelheid informatie, juist om zuiniger met het geheugen om te gaan. Een voorbeeld, ontleend aan de Intel-processoren, is te zien in figuur 3.7. OUN 99 De werking van computersystemen U hoeft deze instructies niet te begrijpen. Het gaat er alleen om te laten zien dat er instructieformaten zijn met verschillende lengte. FIGUUR 3.7 Variabele lengte van instructieformaten (Intelprocessoren, vereenvoudigd) Instructiesets die veel formaten bevatten, waarbij die formaten ook nog een verschillende lengte hebben, maken ontwerp en implementatie van de microprocessor ingewikkeld. De vele formaten komen vaak voort uit de wens om voor veel specifieke situaties speciale instructies aan te bieden. Bij het opzetten van een instructieset dient de afweging tussen een uitgebreide of een eenvoudige instructieset de nodige aandacht te krijgen. Een uitgebreide instructieset betekent een toename van de complexiteit van de microprocessor, met name de control unit. In de control unit moeten namelijk de instructies worden gedecodeerd. Een eenvoudige instructieset zal ervoor zorgen dat dit onderdeel van de processor een eenvoudiger structuur en werking kan hebben. RISC versus CISC 100 In deel 3 komen we hierop terug. De keuze voor één van beide alternatieven heeft geleid tot een onderscheid in Reduced Instruction Set Computers (RISC) met een eenvoudige instructieset, en de Complex Instruction Set Computers (CISC) met een uitgebreide instructieset. In principe zouden programmeurs bij het programmeren in assembleertaal complexe instructies in CISC-systemen uit kunnen buiten om snellere programma’s te maken. Bij programma’s in assembleertaal, die ontstaan door compilatie van programma’s in een hogere programmeertaal, blijkt echter dat de exotische instructies die deel uitmaken van een CISC-instructieset, nauwelijks in de machinecode terug te vinden zijn. De snelheidswinst die met de krachtige, exotische instructies geboekt wordt, valt dan in het niet bij het snelheidsverlies dat optreedt doordat de processor complexer en dus trager is. Aangezien de meeste programma’s tegenwoordig in hogere programmeertalen worden geschreven, zijn de instructiesetarchitecturen die na 1982 nieuw zijn geïntroduceerd, bijna allemaal te typeren als RISC, omdat ze voor dergelijke programma’s aanzienlijk sneller uitpakken. OUN Leereenheid 3 2.3 Assembleertaal Mnemonic Regelgeoriënteerd Commentaar Assembler Label De instructiesetarchitectuur ASSEMBLEERTALEN De representatie van instructies met nullen en enen is voor de mens niet goed leesbaar. Deze machinerepresentatie wordt daarom vervangen door een symbolische notatie. In deze symbolische taal, de assembleertaal, worden de operatiecodes vervangen door, indien mogelijk zinvolle, voor mensen begrijpelijke afkortingen. De afkortingen worden mnemonics genoemd. Een instructie in een assembleertaal is een mnemonic, gevolgd door symbolen voor de operanden. Assembleertalen zijn altijd regelgeoriënteerd. Dit betekent dat elke instructie op een aparte regel staat. De namen van operanden worden vaak gescheiden door komma’s. Een voorbeeld van een programma-fragment in assembleertaal is: add add a, b, c a, a, d add a, a, e ; ; ; ; ; de som van b en c wordt in a geplaatst de som van a en d wordt in a geplaatst a is nu gelijk aan de som van b, c en d de som van a en e wordt in a geplaatst a is nu gelijk aan de som van b, c, d en e De teksten achter de puntkomma’s zijn commentaar. Dat kan in assembleertaal altijd naar believen toegevoegd worden, mits voorafgegaan door een speciaal symbool, bijvoorbeeld als documentatie voor andere programmeurs. Commentaar verandert de betekenis van de instructie niet. Voor alle computers zijn speciale programma’s beschikbaar, zogenaamde assemblers, die programma’s in assembleertaal automatisch kunnen omzetten in de corresponderende bitrijen, de door de computer uitvoerbare instructies dus. Tijdens het vertalen zal de assembler regel voor regel lezen. Als een regel een correcte assembleerinstructie bevat, dan zal hij de daarmee corresponderende machine-instructie genereren. Aangezien de assembler de lengte van elke instructie kent, is hij bovendien in staat om, gegeven het beginadres van een programma, elke instructie een adres te geven. De programmeur hoeft zich daar niet mee bezig te houden. Een probleem hierbij zijn spronginstructies, waarmee naar een bepaalde locatie in het programma wordt gesprongen, aangeduid met een sprongadres. Als programmeurs deze adressen zouden moeten berekenen, zouden daarbij veel fouten worden gemaakt. Ter vermijding van deze fouten ondersteunt elke assembleertaal labels. Labels zijn symbolische namen, of verwijzingen, voor programmalocaties. De assembler zal deze namen vervangen door daadwerkelijke adressen. Het volgende fragment illustreert het gebruik van labels in een assembleertaal. lus: add add sub jnz a, a, e, e, b, c a, e e, 1 lus ; ; ; ; ; a wordt som van b en c a wordt som van a en e e wordt met 1 verminderd als e ongelijk 0 dan sprong naar locatie lus In het fragment hierboven is één label gebruikt: lus. De declaratie van het label, in de linkerkolom, wordt afgesloten met een dubbele punt (:). OUN 101 De werking van computersystemen Zie leereenheid 17. Bij het gebruik van dit label, middelste kolom, vervalt deze dubbele punt. Het label lus geeft de locatie aan van de instructie waar het programma moet worden voortgezet. Na het uitvoeren van de eerste twee add-instructies wordt de waarde van e met 1 verlaagd door de aftrekinstructie sub. Daarna zal de voorwaardelijke spronginstructie jnz (‘jump [if] non zero’) ervoor zorgen dat er naar de locatie lus terug wordt gesprongen als de waarde van e ongelijk is aan 0. Als de waarde van e echter wel gelijk aan 0 is, dan zal het programma doorgaan met de instructie na jnz. Bij het vertalen van dit fragment berekent de assembler het adres van de machine-instructie die met ‘add a, a, e’ correspondeert, en zet dit adres, gecodeerd als bitrij, in het veld van de instructie dat bij ‘jnz e, lus’ hoort. Wanneer de assembler op het moment van assembleren het adres van het label niet kent omdat het label bijvoorbeeld pas later wordt gedeclareerd, wordt dit label even in een apart bestand onthouden. Wanneer de assembler het gehele programma heeft doorlopen, moet het adres van het label bekend zijn en kan het door de assembler worden ingevuld. Programma’s die geschreven zijn in een assembleertaal kunnen dus enerzijds beter door mensen geschreven en gelezen worden, maar anderzijds door assemblers eenvoudig omgezet worden in voor de computer leesbare instructies. 2.4 Let op! HET EFFECT VAN EEN INSTRUCTIE De instructies in de volgende paragrafen 2.4 en 3 lijken erg op de MIPS-instructies van deel 4. Die gelijkenis is echter soms bedrieglijk, omdat sommige instructies in de MIPSinstructieset niet bestaan of van die set afwijken qua naamgeving en/of qua formaat. U moet bij het tentamen de MIPS-instructies kennen en kunnen toepassen. De instructies van de paragrafen 2.4 en 3 hoeft u niet te kennen. Bij tentamenvragen over de paragrafen 2.4 en 3 wordt de betekenis van een eventuele instructie in de vraag aangegeven. Hier gaat het alleen om een eerste kennismaking met de structuur en het gedrag van assemblerinstructies. Alleen het noemen van de instructies en het aangeven van de formaten die door de diverse instructies worden gebruikt, is voor programmeurs niet voldoende om aan de slag te kunnen met een instructieset. Een instructiesetarchitectuur moet ook aangeven wat het effect van elke instructie is als deze wordt uitgevoerd door de hardware. Toestand van de computer 102 Engels: state Een manier om dit te doen is door een computer op te vatten als een instructieverwerker die op ieder moment een bepaalde toestand heeft. Die toestand wordt mede bepaald door de waarden van gegevens in de registers en het geheugen. Voor het beschrijven van het effect van instructies is het dan voldoende om de toestand van de computer vóór en na de instructie weer te geven. Hiermee is het effect eenduidig gedefinieerd. Deze beschrijving van de toestand van de computer is echter bijzonder uitgebreid en daardoor ingewikkeld. Het is ondoenlijk om het complete geheugen, en alle registers in de processor en in de I/O-controllers te moeten beschrijven voor en na het uitvoeren van elke instructie. Daarom wordt bij het beschrijven van het effect van een instructie alleen de wijziging in de toestand aangegeven. Dat betekent dat we alleen díe registers en geheugenplaatsen zullen vermelden in de toestand vóór en na een OUN Leereenheid 3 De instructiesetarchitectuur instructie, die dus voorkomen in die instructie en waarvan de inhoud wordt gebruikt en mogelijk gewijzigd. Hieronder is te zien hoe de toestand van de computer wordt gewijzigd door twee opeenvolgende instructies. {Toestand A} Instructie 1 {Toestand B} Instructie 2 {Toestand C} ... In figuur 3.8 is een vereenvoudigd model te zien van een computer met de belangrijkste elementen die de toestand bepalen. De aspecten van inen uitvoer zijn hierin niet opgenomen. FIGUUR 3.8 Eenvoudig model van dát deel van de computer dat de toestand bepaalt Om de toestand van het geheugen vast te leggen wordt dat opgevat als een tabel, met het adres als index. De grootte van de tabel is gelijk aan het adresbereik. In het model van figuur 3.8 is dit 232, ofwel 4 294 967 296 (4GB geheugen). De mogelijke adressen lopen dan van 0 tot en met 4 294 967 295. De tabel heeft een vaste naam, bijvoorbeeld Memory. De geheugenlocatie op het adres 128 kan dus worden aangegeven met Memory[128] Zie leereenheid 2. Natuurlijke taal Naast de geheugenlocaties maken ook de registers deel uit van de toestand van de computer. De registers worden aangeduid met hun naam. In figuur 3.8 zijn R1, R2, R3 en R4 de algemene registers. Voor de speciale registers worden de namen IR, PC , OAR en SP gebruikt. Met een dergelijk model van de computer kunnen we de effecten van instructies beschrijven. Eén mogelijkheid om de wijzigingen van de toestand te beschrijven is het gebruik van natuurlijke taal. Voor veel problemen is dit voldoende. Natuurlijke taal heeft echter een nadeel: vaak is de interpretatie niet eenduidig. Het is in een natuurlijke taal een hele kunst om volledig en exact weer te geven wat er wordt bedoeld. Als daartoe toch een poging wordt gedaan, dan levert dit vaak lange en ingewikkelde teksten op. De taal die in wetsartikelen en contracten tussen partijen wordt gebruikt, is hiervan een duidelijk voorbeeld. OUN 103 De werking van computersystemen Formele notatie Hoare-triples Tony Hoare is één van de grondleggers van de principes van gestructureerd programmeren. Preconditie Postconditie Om iets toch exact en volledig weer te geven worden daarom formele notaties gebruikt. De notatie die wij zullen toepassen, maakt gebruik van zogenaamde Hoare-triples, afkomstig uit de predikatenlogica. Een predikaat is een uitdrukking die als waar of niet waar kan worden geëvalueerd. Het waar of niet waar zijn van een predikaat hangt af van de waarden van de variabelen die in de uitdrukking voorkomen. Een Hoare-triple bestaat uit een instructie tussen twee predikaten. Het eerste predikaat, de preconditie, geeft de toestand vóór het uitvoeren van de instructie aan, het laatste predikaat, de postconditie, beschrijft de toestand ná het uitvoeren van de instructie. De betekenis van het Hoare-triple {preconditie} INSTRUCTIE {postconditie} is nu als volgt: Als de toestand vóór de instructie correct beschreven wordt door de preconditie, dan zal de instructie de toestand zo veranderen als in de postconditie beschreven wordt. Overeenkomstig de afspraak hierboven, zullen we daarbij in de predikaten alleen die registers en geheugenplaatsen vermelden, welke voor de desbetreffende instructie van belang zijn. In de postconditie worden alleen de gewijzigde registers en geheugenplaatsen vermeld. Voorbeeld: add De definitie van de assembleerinstructie add door middel van een Hoare-triple zou er als volgt uit kunnen zien. a ∧ b is (alleen dan) waar als a en b beide waar zijn. {rs1 = X ∧ rs2 = Y} add rd, rs1, rs2 {rd = X + Y} De preconditie betekent dat register rs1 de waarde X bevat, en rs2 de waarde Y. Binnen het Hoare-triple zijn X en Y constanten, dat wil zeggen: voor X en Y mogen we elke waarde invullen, als we voor en na de instructie maar dezelfde waarde gebruiken. In de postconditie wordt uitgedrukt wat de waarde van het register rd dient te zijn na uitvoering van de instructie. In dit geval moet dat X + Y zijn, dus de som van de waarden die rs1 en rs2 vóór de instructie hadden. Uit de afspraak over de presentatie van een Hoare-trippel volgt dat in bovenstaand voorbeeld in de postconditie alleen rd vermeld wordt. De registers rs1 en rs2 zijn hierin niet opgenomen, hetgeen betekent dat de inhoud van deze registers niet verandert. OPGAVE 3.5 De preconditie lijkt in bovenstaand voorbeeld niets toe te voegen aan de definitie van het effect van de instructie. We zouden namelijk kunnen schrijven: add rd, rs1, rs2 {rd = rs1 + rs2} Waarom zou dit ongewenst zijn? Voorbeelden: lw en sw 104 Om gegevens te transporteren van het geheugen naar de registers en omgekeerd zijn load- en store-instructies beschikbaar. Het effect van de load-operatie lw, gedefinieerd door een Hoare-triple, is: OUN Leereenheid 3 De instructiesetarchitectuur {Memory[adres] = X} lw rd, adres {rd = X} Het doelregister rd krijgt dus de waarde die vóór het uitvoeren van de instructie op de geheugenplaats met nummer adres stond. De waarde in het geheugen wordt daarbij niet gewijzigd. De onderstaande storeoperatie sw schrijft de inhoud van register rs in geheugenplaats adres. {rs = X} sw rs, adres {Memory[adres] = X} In de precondities van de Hoare-triples voor add en lw wordt register rd niet vermeld; in de preconditie van sw ontbreekt de geheugenplaats adres. Wat concludeert u daaruit? Als in de postconditie een variabele niet vermeld wordt, verandert de instructie die variabele kennelijk niet (de dan-frase uit de interpretatie van een Hoare-triple). Als echter in de preconditie een variabele niet voorkomt, wordt er geen voorwaarde (de ‘als’ uit de interpretatie van een Hoare-triple) opgelegd aan die variabele voor het correct functioneren van de instructie. De waarde van die variabele vóór de instructie doet er dus kennelijk niet toe. Voorbeeld: add met geheugenlocaties In sommige computers zijn instructies beschikbaar waarmee waarden op geheugenplaatsen bij elkaar kunnen worden opgeteld. De onderstaande instructie is hiervan een voorbeeld, waarbij het resultaat weer in het geheugen wordt geschreven (reg1, reg2 en regd zijn registernamen). {Memory[reg1] = X ∧ Memory[reg2] = Y} add (regd), (reg1), (reg2) {Memory[regd] = X + Y} Merk op dat deze instructie een geheugen-geheugenarchitectuur veronderstelt. Met de haakjes om de operanden in de instructie wordt aangegeven dat de registers niet zelf doel respectievelijk bron zijn, maar dat de waarden in de registers de adressen zijn van de bedoelde geheugenplaatsen. Voorbeelden: jmp en jez Om het effect van een spronginstructie te beschrijven moeten we aangeven wat de waarde van de program counter (PC) wordt na het uitvoeren van de spronginstructie. De onderstaande spronginstructie (jmp) is een onvoorwaardelijke sprong. {PC = X} jmp adres {PC = adres} De uitvoering van de sprong is kennelijk niet afhankelijk van de waarde in een register of geheugenplaats. Dat is anders bij de voorwaardelijke spronginstructie jez (‘jump on equal zero’). Als de waarde van het register rs gelijk is aan 0, dan wordt een sprong uitgevoerd naar een programmalocatie aangeduid met adres. Zo niet, dan wordt gewoon verdergegaan met de volgende instructie van het programma. OUN 105 De werking van computersystemen a ∨ b is waar (alleen dan) als a of b waar is of beide {rs = X ∧ PC = Y} jez rs, adres {(X = 0 ∧ PC = adres) ∨ (X ≠ 0 ∧ PC = Y + 1)} De postconditie bevat een keuze. Aangezien X = 0 en X ≠ 0 elkaar uitsluiten, kan slechts één van de twee delen van het predikaat waar zijn. Hiermee wordt het conditionele karakter aangegeven. Als X = 0 waar is, dan dient na het uitvoeren van de instructie de program counter gelijk te zijn aan adres (een sprong). Als X ≠ 0 waar is, dan dient de program counter slechts met 1 verhoogd te zijn (doorgaan met volgende instructie). Voorbeeld: slt Het onderstaande voorbeeld geeft een testinstructie (slt: Set on Less Than) weer. Register rd krijgt de waarde 1 als register rs1 kleiner is dan rs2, en anders de waarde 0. Ook in dit voorbeeld is het predikaat na de instructie opgebouwd uit twee delen die elkaar uitsluiten. {rs1 = X ∧ rs2 = Y} SLT rd, rs1, rs2 {(X < Y ∧ rd = 1) ∨ (X ≥ Y ∧ rd = 0)} OPGAVE 3.6 Tabel 3.1 is niet volledig ingevuld. Het is de bedoeling dat van de gegeven instructies zowel een informele (in natuurlijke taal) als een formele definitie (met een Hoare-triple) gegeven wordt. Vul de opengelaten vakken van de tabel in. TABEL 3.1 106 Enkele instructies en hun effect assembleerinstructie formele notatie effect informele notatie effect add rd, rs1, rs2 {rs1 = X ∧ rs2 = Y} add rd, rs1, rs2 {rd = X + Y} Het doelregister rd krijgt de waarde van de som van de bronregisters rs1 en rs2. sub rd, rs1, rs2 ... Hetdoelregister rd krijgt de waarde van het verschil van de bronregsiters rs1 en rs2. jmp imm {PC = X} jmp imm {PC = imm} ... sne rd, rs1, imm ... Register rd wordt gelijk aan 1 als rs1 ongelijk is aan imm, anders wordt rd gelijk aan 0. mov rd, rs1 ... Register rd wordt gelijk aan rs1. add (124), rs1, rs2 ... Geheugenlocatie met adres 124 wordt gelijk aan de som van de inhouden van registers rs1 en rs2. sgt rd, rs1, rs2 {rs1 = X ∧ rs2 sgt rd, rs1, {(X > Y ∧ rd = (X ≤ Y ∧ rd = OUN = Y} rs2 1) ∨ 0)} ... Leereenheid 3 De instructiesetarchitectuur Voor de logische relaties AND en OR worden, afhankelijk van de discipline, verschillende notaties gebruikt. In de logica gebruikt men de notitie als hierboven om een AND- (∧) of OR- relatie (∨) te noteren; in de elektrotechniek gebruikt men hiervoor de punt ‘•’ respectievelijk de plus ‘+’. In software zijn ‘AND’ en ‘OR’ de notaties voor deze logische operaties. We gebruiken in de cursus alle drie de notaties omdat we ervan uitgaan dat u met alle drie de disciplines in aanraking komt en dan ook de daar gebruikte notaties moet kunnen interpreteren. 3 Adresseringsmethoden Zie bijvoorbeeld figuur 3.6 Adresseringsmethoden Een processor verwerkt instructies. Bij deze instructies hoort te worden aangegeven ‘wat’ er gedaan moet worden en ‘waarmee’ dit gedaan moet worden. Het ‘wat’ staat aangegeven in de operatiecode (opcode) van het instructieformaat, en eventueel in het veld func. Het aangeven van het ‘waarmee’, de operanden, is iets ingewikkelder. In het voorgaande is reeds aangegeven dat de operanden van een instructie zich in verschillende componenten van een computer kunnen bevinden. Afgezien van de verscheidenheid in plaats van de operanden is er ook een grote verscheidenheid aan mogelijkheden om die plaatsen vast te leggen in een instructie. We noemen dit de adresseringsmethoden. De methoden die hier worden behandeld zijn: impliciete, onmiddellijke, absolute, register, register-indirecte, PC-relatieve adressering en register offset. Instructies hebben meestal meer dan één operand. Vaak kunnen deze operanden op verschillende manieren geadresseerd worden. Impliciete adressering Bij impliciete adressering wordt de impliciet geadresseerde operand niet expliciet vermeld in de instructie; deze volgt dan uit de operatiecode. Een voorbeeld is de instructie inc, met als definitie: inc van Engels: increment (verhogen) {rs = X} inc rs {rs = X + 1} Deze instructie verhoogt de waarde van register rs met 1. De waarde waarmee moet worden opgehoogd (1), is in dit geval niet in de instructie zichtbaar, maar volgt uit de operatiecode inc. De operand 1 is hiermee impliciet geadresseerd. Onmiddellijke adressering Engels: immediate (onmiddellijk) Bij onmiddellijke adressering is de operand die onmiddellijk geadresseerd wordt, zelf als getalswaarde in de instructie opgenomen. Met het ophalen van de instructie heeft de processor de operand dus meteen beschikbaar. Deze adressering wordt bijvoorbeeld gebruikt bij de instructie addi, met als definitie: {rs = X} addi rd, rs, val {rd = X + val} OUN 107 De werking van computersystemen Voor ‘val’ (van value) kan een concreet geheel getal (binnen zekere grenzen) worden ingevuld, bijvoorbeeld 183. De variabelen rs en rd verwijzen weer naar de inhoud van registers, de waarde van val daarentegen staat in de instructie zelf: deze operand is onmiddellijk geadresseerd. In figuur 3.9 is aangegeven hoe een onmiddellijke operand in een instructie kan zijn opgenomen. FIGUUR 3.9 Instructieformaat met een onmiddellijk geadresseerde operand (hier 183) De naam van deze adresseringsmethode is ontleend aan het feit dat de waarde val onmiddellijk beschikbaar is zodra de instructie is opgehaald en niet nog eens keer uit een register of geheugenplaats moet worden gehaald. Absolute adressering Engels: direct addressing Bij absolute adressering wordt de geheugenlocatie in het werkgeheugen van een operand in de instructie aangegeven. Bij deze methode zal de instructie altijd dezelfde geheugenplaats benaderen. De inhoud van deze geheugenplaats kan veranderen, de locatie niet! Een voorbeeld hiervan is de instructie waar een waarde in geheugenplaats 15342 in register rd wordt gekopieerd: {Memory[15342] = X} Lw rd, (15342) {rd = X} In deze instructie betekenen de haakjes rondom 15342 dat het getal een adres voorstelt en geen onmiddellijke waarde, al is dit ook duidelijk uit de opcode (lw). Directe adressering werkt dus alleen goed wanneer de locatie van de operand op het moment van compileren bekend is. De omgekeerde weg is ook mogelijk: wegschrijven naar een ‘hard-geprogrammmeerde’ geheugenplaats. Beide instructies bergen het gevaar in zich dat naar verboden locaties in het geheugen wordt gewezen, bijvoorbeeld het gebied waar het besturingsysteem staat opgeslagen. Vooral schrijven in dit gebied door een applicatie kan tot desastreuze gevolgen leiden. Moderne besturingsystemen voorkomen echter deze problematiek door, uit het oogpunt van de taak van geheugenbeheer , bepaalde gedeeltes van het geheugen af te schermen door aan adresruimtes privileges toe te kennen. Een applicatieprogramma mag dan alleen in toegewezen geheugenruimtes schrijven. Lezen van geheugenplaatsen buiten de toegewezen ruimte mag echter meestal wel. Gezien vanuit het besturingssysteem heet deze handelswijze de protected mode. De echte implementatie van deze handelswijze valt echter buiten het bestek van deze cursus en is onderwerp van een cursus Besturingssystemen. Registeradressering 108 Registeradressering is conceptueel hetzelfde als directe adressering, alleen wordt nu een register aangegeven als de plek voor de operand in plaats van een werkgeheugenplaats. Doordat de meeste processoren nu vanwege de korte toegangstijden en korte adressen van de registers met een OUN Leereenheid 3 De instructiesetarchitectuur load-store architectuur werken, is deze methode veruit de meest gangbare adresseringmethode geworden. In feite hebben we deze manier van adresseren al in praktisch alle voorbeelden van paragraaf 2.4 gezien. In tegenstelling tot een onmiddellijk geadresseerde operand staat nu niet de waarde zelf in de instructie, maar het nummer van het register waar de operand te vinden is. Bovengenoemde instructie addi kan eveneens als voorbeeld dienen voor registeradressering: operand rs is op die manier geadresseerd (net als rd overigens, alleen gaat het dan om het register waar het resultaat naartoe moet). In figuur 3.10 is schematisch aangegeven hoe een veld in het instructieformaat naar een register kan verwijzen. FIGUUR 3.10 Registeradressering: het tweede veld verwijst naar het register R2 De plek (naam) van het register is bij het compileren bekend zodat de verwijzing hiernaar geen probleem is. Register-indirecte adressering Als een operand met behulp van register-indirecte adressering wordt aangeduid, dan is de operand in het geheugen te vinden. Het adres van de operand is op zijn beurt in een register van de processor te vinden. Let wel: het opzoeken van de waarde vereist nu twee stappen: eerst naar een register, en vervolgens naar het aldaar opgegeven adres in het geheugen. Vandaar de toevoeging ‘indirect’. Een voorbeeld is te zien in de onderstaande instructie. {Memory[rs]= X} lw rd, (rs) {rd = X} Deze instructie haalt een waarde uit het geheugen en plaatst deze in register rd. Het adres van deze geheugenlocatie staat in register rs. De tweede operand, de opgehaalde waarde, is daarmee register-indirect geadresseerd, hetgeen in de instructie aangegeven is door de haakjes. Deze situatie is schematisch weergegeven in figuur 3.11. FIGUUR 3.11 Register-indirecte adressering OUN 109 De werking van computersystemen Bij sommige architecturen kan het adres van de geheugenlocatie in plaats van in een register, ook zelf in het geheugen staan. We spreken dan van geheugen-indirecte adressering. Bij programmeurs staat register-indirecte adressering bekend als het gebruik van een pointer. PC-relatieve adressering Bij PC-relatieve adressering is slechts een gedeelte van een adres als operand in de instructie opgenomen. Het volledige adres wordt berekend uit de inhoud van de programcounter PC en de waarde die is opgenomen in de instructie zelf. Deze wijze van adresseren wordt vaak gebruikt bij spronginstructies. De instructie jr van ‘jump relative’, is geen MIPS-instructie! {PC = X} jr val {PC = X + val} laat een voorbeeld van PC-relatieve adressering zien. Als de instructie zelf op adres 1000 staat, dan zal de program counter de waarde 1000 + val krijgen. Het programma zal dan verder gaan met de instructie op dat adres. Deze manier van adresseren heet relatief, omdat niet naar een vast adres in het geheugen wordt gesprongen, maar relatief ten opzichte van het startpunt van de sprong. In figuur 3.12 is getracht in een schematisch voorbeeld relatieve adressering uit te leggen. jr * * 142 6 bits 5 bits 5 bits 16 bits + instructie y instructie x 1143 1142 instructie w 1141 1140 1000 PC instructies in het geheugen * = ongebruikt FIGUUR 3.12 Leestekst PC-relatieve adressering De optelling die bij relatieve adressering nodig is om de nieuwe waarde van de program counter te bepalen, is feitelijk ingewikkelder dan in figuur 3.12 wordt gesuggereerd. In paragraaf 1 is al opgemerkt dat na het ophalen van de instructie de program counter direct wordt aangepast zodat hij al naar de volgende instructie wijst. In het voorbeeld is de waarde van PC dus niet langer 1000, maar 1001 als de optelling plaatsvindt. Daar wordt echter voor gecorrigeerd, zodat de nieuwe waarde toch 1142 is. We gaan er verder van uit dat de preconditie de situatie vóór het ophalen van de instructie beschrijft. Het effect van de relatieve sprong klopt dan precies met de specificatie. Overigens kunnen de nodige berekeningen in principe door de ALU uitgevoerd worden, maar in veel processoren gebeurt dat in een aparte schakeling, de adresunit. PC-relatieve adressering is één van de vormen van relatieve adressering. Naast de program counter als basis voor de adresberekening, kan ook een ander register als basis voor deze berekening dienen. De adreslocatie van de operand is in dit geval de som van de inhoud van dit register en een 110 OUN Leereenheid 3 Zie o.a. leereenheid 16 De instructiesetarchitectuur waarde in de instructie. In dit geval noemen we deze relatieve adresseringsmethode base of register-offset adressering. Het voordeel van relatieve adressering is dat de instructie met kleine getallen, 5 bits voor het register en 16 bits voor de waarde, de gewenste geheugenlocatie kan aanwijzen, waardoor de instructielengte tot 32 bits (bij MIPS) beperkt kan blijven. Een voorbeeld van een MIPS-instructie op dit punt is: {Memory[rs + val]= X} lw rd, val(rs) {rd = X} De waarde ‘val’ is dan de offset ten opzichte van de adreswaarde in register rs. De eerder genoemde laadinstructie ‘lw’ wordt dan bij de MIPS: lw rd, 0(rs) Met enige goede wil zouden PC-relatieve en registeroffset-adressering varianten van register-indirecte adressering genoemd kunnen worden waarbij de bedoelde registers dan de programcounter of algemene registers zijn. Let op! De adresseringsmethoden die in deze paragraaf zijn genoemd, vormen de belangrijkste en meest gebruikte manieren om operanden te adresseren. Behalve het aangeven van de operanden, kunnen ook de locaties voor de resultaten met deze manieren worden aangegeven. In realistische computersystemen kunnen nog op grond van de gevolgde architectuur bijzondere adresseringsmethoden voorkomen. Bij het bestuderen van een specifieke instructieset dient aan het onderwerp adressering dan ook altijd extra aandacht te worden besteed. OPGAVE 3.7 Benoem de adresseringsmethoden die gebruikt zijn in de voorbeeldinstructies in paragraaf 2.4. SAMENVATTING Paragraaf 1 We zijn in deze leereenheid ingegaan op de interface tussen de hardware van de computer en de software: de instructiesetarchitectuur. Vanuit het perspectief van de gebruiker kan de computer beschouwd worden als een gegevensverwerker. In deze leereenheid staat echter het standpunt van de ontwerper van zowel hardware als van systeemsoftware centraal: de computer als instructieverwerker. Voor de verwerking van de instructies implementeert de hardware de zogenaamde instructiecyclus, die stap voor stap aangeeft wat de processor moet doen. Door het expliciet maken van het ophalen van operanden ontstaan verfijningen van de basisinstructiecyclus. Paragraaf 2 De instructies van een instructiesetarchitectuur kunnen worden opgedeeld in een aantal categorieën: – ALU-operaties, waaronder aritmetische instructies (berekeningen zoals optellen en aftrekken) en logische en schuifinstructies (bewerkingen op individuele bits van gegevens) – transportinstructies: transporteren van gegevens tussen geheugen en registers en tussen registers onderling OUN 111 De werking van computersystemen – spronginstructies: springen naar bepaalde locaties in een programma – testinstructies: het testen van de waarden van registers of geheugenlocaties. Instructies hebben een voorgeschreven formaat, zodat de processor in staat is om deze te decoderen en de bijbehorende acties te starten. In een instructieformaat zijn instructies ingedeeld in velden, met elk een bepaalde afmeting en betekenis. De instructiesetarchitectuur definieert deze formaten. Afhankelijk van de instructiesetarchitectuur zijn alle instructieformaten even lang of kunnen instructies in lengte verschillen. Paragraaf 3 Om de programma’s ook schrijfbaarder en leesbaarder te maken voor de mens worden assembleertalen gebruikt in plaats van de representatie in bitpatronen. Assembleertalen zijn regelgeoriënteerde talen en ze laten het gebruik van commentaar en labels toe. Assembleertaalprogramma’s kunnen automatisch vertaald worden door een speciaal programma, de assembler. Voor het vastleggen van het effect van een instructie worden vaak formele notaties gebruikt. Deze notaties drukken het effect van een instructie uit als een wijziging van de toestand van de computer. De toestand van een computer is vastgelegd in de computerorganisatie als de verzameling registers (zowel van de processor als van de I/O-controllers) en geheugenlocaties. In de beschrijving van de instructies geven we echter alleen de elementen weer die gebruikt of gewijzigd worden. Om de locatie van de operanden van een instructie te bepalen, definieert de instructiesetarchitectuur een aantal adresseringsmethoden. De belangrijkste daarvan zijn impliciet, onmiddellijk, absoluut, register, register-indirect en PC-relatief. ZELFTOETS 112 1 Noem de stappen van de instructiecyclus en geef in het kort aan wat de betekenis van deze stappen is. 2 Wat is het verschil tussen aritmetische instructies en logische en schuifinstructies? 3 In welke drie groepen kan de klassieke registerarchitectuur worden onderverdeeld, wat de instructiesetarchitectuur betreft? Geef uitleg over de principes van deze groepen. 4 Welke elementen zijn altijd in assembleertalen terug te vinden? 5 Welke onderdelen van een computer bepalen de toestand van het computersysteem vanuit het oogpunt van de instructiesetarchitectuur? Zijn al deze onderdelen voor elke instructie van belang? 6 Hoe vindt de processor een operand in het geval van: – registeradressering – register-indirecte adressering en – impliciete adressering? OUN Leereenheid 3 De instructiesetarchitectuur TERUGKOPPELING 1 3.1 Uitwerking van de opgaven Het meest sprekende voorbeeld zijn hogere programmeertalen, zoals Pascal en Java. Een voorbeeld van een binaire operatie is het optellen van twee getallen in Java, in de toekenningsopdracht: A = B + C; De binaire operatie + wordt uitgevoerd met de waarden van B en C. Het resultaat wordt in A opgeslagen. Een unaire operatie is bijvoorbeeld het minteken in Java om de tegengestelde waarde te bepalen, zoals in de toekenningsopdracht D = -E; De unaire operatie - wordt uitgevoerd op de waarde van E. Het resultaat wordt in D opgeslagen. Behalve in hogere progammeertalen komen binaire en unaire operaties bijvoorbeeld ook in de wiskunde en in de logica voor. 3.2 Bij een geheugen-geheugenarchitectuur moet voor het ophalen van iedere operand en het wegschrijven van ieder resultaat van een instructie het interne geheugen via de bus worden benaderd. Daarnaast moeten ook de instructies uit het geheugen worden gehaald. Bij een register-registerarchitectuur worden de operanden eerst in de registers geladen (load), vervolgens bewerkt waarbij tussenresultaten ook in registers worden opgeslagen en ten slotte worden de eindresultaten weer naar het geheugen weggeschreven. In vergelijking met de geheugen-geheugenarchitectuur is het bij de register-registerarchitectuur veel minder druk op de bus omdat het iedere keer ophalen en wegschrijven van operanden achterwege blijft. Bij een registerregisterarchitectuur is dus de kans dat de Von Neumann bottleneck optreedt kleiner, waardoor de microprocessor sneller kan werken. 3.3 De categorie transportinstructies bestaat uit instructies die het transport verzorgen tussen – registers van de processor en het geheugen – registers van de processor en de I/O-controller en – registers van de processor onderling. Bij memory-mapped I/O zijn er geen aparte instructies nodig voor het benaderen van de I/O-controllers. Naar verwachting zullen die instructies (in en out) dus ontbreken. 3.4 Een voorwaardelijke spronginstructie voert eerst een test uit op de operand en voert een sprong uit als het resultaat ‘waar’ is (1). Voorwaardelijke spronginstructies bevatten dus impliciet een testinstructie. Of andersom: een testinstructie is een voorwaardelijke spronginstructie waarbij de sprong ontbreekt. Het resultaat van de test kan dan kennelijk ook voor een ander doel gebruikt worden dan een sprong. OUN 113 De werking van computersystemen 3.5 In de eerste plaats is de voorgestelde notatie onvolledig. Omdat in het (enige) predikaat drie registers voorkomen, is niet duidelijk welk register door de instructie gewijzigd wordt. Het zou best zo kunnen zijn dat register rs1 zodanig gewijzigd wordt dat rd gelijk wordt aan de som van rs1 en rs2 (dus rs1 wordt gelijk aan rd – rs2). Dat is natuurlijk niet de bedoeling. Een ander manco wordt duidelijk als in het predikaat na de add één van beide registers rs1 of rs2 hetzelfde register is als rd, bijvoorbeeld: add R1, R1, R4 {R1 = R1 + R4} De postconditie zegt nu dat na uitvoering van de instructie R1 gelijk moet zijn aan de som van R1 en R4. In de meeste programmeertalen is deze notitie de gewoonste zaak ter wereld. Alleen is in deze talen de interpretatie van deze notatie anders: R1 wordt de som van R1 en R2. De wiskundige interpretatie van deze notatie is onzin, tenzij R4 toevallig gelijk is aan 0. Het probleem wordt veroorzaakt door het feit dat in één predikaat zowel de ‘nieuwe’ als de ‘oude’ waarde van R1 gebruikt wordt. Dit interpretatieverschil is ongewenst in een formele specificatie omdat we juist daar exacte betekenissen willen beschrijven. Juist daarom zijn constanten als X en Y ingevoerd. 3.6 Hieronder staat de volledig ingevulde tabel. TABEL 3.2 114 Enkele instructies en hun effect assembleerinstructie formele notatie effect informele notatie effect add rd, rs1, rs2 {rs1 = X ∧ rs2 = Y} add rd, rs1, rs2 {rd = X + Y} Het doelregister rd krijgt de waarde van de som van de bronregisters rs1 en rs2. sub rd, rs1, rs2 {rs1 = X ∧ rs2 = Y} sub rd, rs1, rs2 {rd = X – Y} Het doelregister rd wordt gelijk aan rs1 min rs2. jmp imm {PC = X} jmp imm {PC = imm} Er wordt een sprong uitgevoerd naar de geheugenlocatie met adres imm. sne rd, rs1, imm {rs1 = X ∧ imm sne rd, rs1, {(X = Y ∧ rd = (X ≠ Y ∧ rd = mov rd, rs1 {rs1 = X} mov rd, rs1 {rd = X} add (124), rs1, rs2 Geheugenlocatie met {rs1 = X ∧ rs2 = Y} add (124), rs1, rs2 adres 124 wordt gelijk {Memory[124] = X + Y} aan de som van de inhouden van registers rs1 en rs2. sgt rd, rs1, rs2 {rs1 = X ∧ rs2 sgt rd, rs1, {(X > Y ∧ rd = (X ≤ Y ∧ rd = OUN = Y} imm 0) ∨ 1)} Register rd wordt gelijk aan 1 als rs1 ongelijk is aan imm, anders wordt rd gelijk aan 0. Register rd wordt gelijk aan rs1. = Y} rs2 1) ∨ 0)} Register rd wordt gelijk aan 1 als rs1 groter dan rs2, anders wordt rd gelijk aan 0. Leereenheid 3 3.7 De instructiesetarchitectuur We laten de voorbeeldinstructies één voor één de revue passeren. add rd, rs1, rs2 Alle operanden hebben registeradressering. lw sw rd, adres rs, adres Het resultaat heeft registeradressering. De waarde adres staat kennelijk in de instructie. Deze waarde wordt echter niet als onmiddellijke waarde gebruikt, maar als adres van een geheugenplaats. Deze manier van adresseren noemden we in paragraaf 3 directe adressering. add (regd), (reg1), (reg2) De registernamen reg1, reg2 en regd staan tussen haakjes en bevatten volgens de definitie in het Hoare-triple inderdaad geheugenadressen, dus het gaat hier om register-indirecte adressering. jmp adres De waarde adres staat in de instructie zelf en wordt ook daadwerkelijk gebruikt als sprongadres. Deze operand is dus onmiddellijk geadresseerd. jez rs, adres Bij de eerste operand is registeradressering gebruikt, bij de tweede onmiddellijke adressering. slt rd, rs1, rs2 Alledrie de operanden maken gebruik van registeradressering. 2 1 Uitwerking van de zelftoets De stappen van de instructiecyclus zijn: – ophalen instructie: het ophalen van de instructie uit het geheugen op het adres, aangegeven door de program counter, gevolgd door het ophogen van deze program counter – decoderen instructie: het door de control unit decoderen van de instructie en het op basis daarvan bepalen wat het datapad moet doen – uitvoeren instructie: het onder aansturing van de control unit uitvoeren van de instructie door het datapad. OUN 115 De werking van computersystemen 2 De operanden van aritmetische instructies zijn waarden die getallen voorstellen. Bij logische en schuifinstructies hoeft dat niet het geval te zijn. Hier stellen de operanden vaak andere gegevens voor, bijvoorbeeld leesbare tekst. Het optellen van de letter ‘A’ bij de letter ‘H’ is een betekenisloze operatie, maar een logische operatie die een letter in de corresponderende hoofdletter omzet, is heel nuttig. Overigens zullen we in leereenheid 6 zien dat schuifoperaties ook bij getallen zinvol kunnen zijn. NB: door de wijze van representeren van karakters kunnen sommige hogere programmeertalen (zoals C) wel degelijk letters bij elkaar optellen. Het is verleidelijk deze operaties dan te gebruiken voor listige programmeerconstructies. 3 Groepen instructiesets zijn: – register-register: de operanden van de operaties zijn overwegend in de interne registers van de processor te vinden en het resultaat wordt daar ook heen geschreven – geheugen-geheugen; de instructies kunnen ook operanden direct in het geheugen adresseren; de adresseringsmethoden voor deze groep zijn vaak erg complex – register-geheugen: een tussenvorm van beide voorgaande groepen. Behalve deze indeling kan er ook nog een onderscheid worden gemaakt in instructiesets op basis van instructieformaten met een vaste lengte en instructieformaten met een variabele lengte. 4 In assembleertalen zullen altijd mnemonics, commentaar en labels kunnen worden gebruikt. 5 Vanuit het oogpunt van de instructiesetarchitectuur wordt de toestand van een computersysteem bepaald door die elementen die door het uitvoeren van een instructie veranderen. Dit zijn het interne geheugen, de interne registers van de processor en de registers van de I/O-controllers. In het algemeen zijn het die delen van de computer die gegevens en instructies op kunnen slaan. Bij het beschrijven van het effect van een instructie zijn echter alleen die onderdelen van belang die door de instructie eventueel kunnen worden gewijzigd. De overige onderdelen blijven dan buiten beschouwing. 6 Bij registeradressering staat de operand in een register van de processor. In de instructie is het nummer van het desbetreffende register opgenomen (figuur 3.10). Bij register-indirecte adressering is het adres van de operand in een register van de microprocessor te vinden. Door de inhoud van dit register op de adresbus te specificeren, wordt de geheugenlocatie met de operand toegankelijk (figuur 3.11). Bij impliciete adressering wordt de operand die impliciet wordt geadresseerd, niet vermeld in het instructieformaat. De operand is in dat geval impliciet in de instructie opgenomen. Doordat de control unit kennis heeft van de instructie(formaten), weet de processor wat de impliciet geadresseerde operand is. 116 OUN