Dictaat Computersystemen in1705



Vergelijkbare documenten
Proeftentamen in1211 Computersystemen I (NB de onderstreepte opgaven zijn geschikt voor de tussentoets)

Proeftentamen in1211 Computersystemen I (Opm: de onderstreepte opgaven zijn geschikt voor de tussentoets)

Dictaat Computerorganisatie in 1005

4,7. Praktische-opdracht door een scholier 1959 woorden 1 juni keer beoordeeld

Talstelsels en getalnotaties (oplmodel)

THEORIE TALSTELSELS. 1 x 10 0 = 1 (een getal tot de macht 0 = 1) 8 x 10 1 = 80 2 x 10 2 = x 10 3 = Opgeteld: 9281d(ecimaal)

3. Structuren in de taal

Tentamen Computerorganisatie 28 augustus 1998, uur. N.B.: Dit tentamen bestaat uit 30 opgaven Totaal aantal bladzijden: 11

Binair Binair = tweewaardig Beperkt aantal mogelijke waarden (discreet aantal in amplitude) Wij zijn gewoon aan decimaal (tiendelig)

17 Operaties op bits Bitoperatoren en bitexpressies

Bijlage D. Binair rekenen

Bij dit hoofdstukken horen geen opgaven.

Variabelen en statements in ActionScript

Talstelsels, getalnotaties en Ascii code

scc = b) CD AB

1 Rekenen in eindige precisie

Assembly en Assemblers. Processoren 5 januari 2015

start -> id (k (f c s) (g s c)) -> k (f c s) (g s c) -> f c s -> s c

Inleiding Digitale Techniek

Bouwstenen voor PSE. Datatypes en Datastructuren

Een korte samenvatting van enkele FORTRAN opdrachten

Pascal uitgediept Data structuren

Interne voorstelling. types en conversies. Binaire en andere talstelsels. Voorstelling van gegevens: bits en bytes

Hexadecimale en binaire getallen

slides12.pdf December 14,

Praktisch bestaan er enkele eenvoudige methoden om een decimaal getal om te zetten naar een binair getal. We bespreken hier de twee technieken.

De Arduino-microcontroller in de motorvoertuigentechniek (2)

Zelftest Inleiding Programmeren

Algoritme noteren? Algoritmen voor de computer worden vastgelegd met behulp van een programmeertaal.

Niet-numerieke data-types

OPDRACHT Opdracht 2.1 Beschrijf in eigen woorden wat het bovenstaande PSD doet.

Tentamen Computersystemen

Studentnummer:... Opleiding:...

Getalformaten, timers en tellers

6,2. Paragraaf 2.1. Paragraaf 2.2. Samenvatting door een scholier 1375 woorden 10 december keer beoordeeld. Informatica Informatica actief

Fout detecterende en verbeterende codes

QR-code op aanvoerbrief 2.xx.0: Specificaties

Antwoorden zijn afgedrukt!!!!!!!

Samenvatting Computer Architectuur

Hoe werkt een rekenmachine?

HOOFDSTUK 3. Imperatief programmeren. 3.1 Stapsgewijs programmeren. 3.2 If Then Else. Module 4 Programmeren

Als een PSD selecties bevat, deelt de lijn van het programma zich op met de verschillende antwoorden op het vraagstuk.

Constanten. Variabelen. Expressies. Variabelen. Constanten. Voorbeeld : varid.py. een symbolische naam voor een object.

VAN HET PROGRAMMEREN. Inleiding

Les A-03 Binaire en hexadecimale getallen

Linux Assembly Uitwerkingen van de vragen en opdrachten

Inleiding Digitale Techniek

Algoritme noteren? Algoritmen voor de computer worden vastgelegd met behulp van een programmeertaal.

Programmeren met Arduino-software

Uitleg van de Hough transformatie

Computerarchitectuur en netwerken. Memory management Assembler programmering

{ auteur, toelichting }

Tentamen Computerorganisatie in aug. 1999, uur. N.B.: Dit tentamen bestaat uit 30 opgaven Totaal aantal bladzijden: 9

Zelftest Programmeren in PL/I

Tentamen Programmeren in C (EE1400)

6.2 VBA Syntax. Inleiding Visual Basic

Activiteit 1. Tel de punten Binaire Getallen. Samenvatting. Kerndoelen. Vaardigheden. Leeftijd. Materiaal

Computertechniek vorige examens

Deel 1: Arduino kennismaking. Wat is een microcontroller, structuur van een programma, syntax,

Een eenvoudig algoritme om permutaties te genereren

Inleiding Digitale Techniek

Module 4 Hoofdstuk 1. Programmeertalen

+ = Talstelsels. Maar wat is dan: -

Onafhankelijke verzamelingen en Gewogen Oplossingen, door Donald E. Knuth, The Art of Computer Programming, Volume 4, Combinatorial Algorithms

Lab Webdesign: Javascript 3 maart 2008

Programmeren A. Genetisch Programma voor het Partitie Probleem. begeleiding:

Tweede college algoritmiek. 12 februari Grafen en bomen

2 Algemene opbouw van een computersysteem

Rekenen met computergetallen

Talstelsels. Het is belangrijk om de volgende twee zaken uit elkaar te houden:

6.3 VBA Syntax Instructie. Wij gaan de Visual Basic Editor opnieuw openen, om de instructie die wij zojuist getypt hebben, nader te bekijken.

Week 2 : Hoofdstukken 2 en 6; extra stof: inleiding pointers

De AT90CAN microprocessor van ATMEL in de motorvoertuigentechniek (2)

College Notatie, Recursie, Lijsten

recursie Hoofdstuk 5 Studeeraanwijzingen De studielast van deze leereenheid bedraagt circa 6 uur. Terminologie

Zelftest Programmeren in COBOL - deel I

In Vlaanderen bestaat er nog geen leerlijn programmeren! Hierdoor baseren wij ons op de leerlijn die men in Nederland toepast voor basisscholen.

In de tweede regel plaatsen we in het gereserveerde stukje geheugen een getal.

Geheugen en Adressering. Binding. Binding (2) Logische en Fysieke adresruimten. relocatie register. Dynamic loading

UNIVERSITEIT ANTWERPEN FACULTEIT WETENSCHAPPEN DEPARTEMENT WISKUNDE-INFORMATICA OBERON CODE CONVENTIONS

BEGINNER JAVA Inhoudsopgave

Memoriseren: Een getal is deelbaar door 10 als het laatste cijfer een 0 is. Of: Een getal is deelbaar door 10 als het eindigt op 0.

scc =!F3.!F2 b) CD AB

FAT32 disk structuur 2007 stam.blogs.com

FLIPIT 5. (a i,j + a j,i )d i d j = d j + 0 = e d. i<j

PSD. Reeksen van logische procedures om problemen op te lossen in een eindig aantal stappen.

Binaire getallen? Werkboek. Doeblad

COMMUNICATIE- EN COMPUTERVAARDIGHEDEN IN DE CHEMIE

Positiestelsels, rekenen en streepjescodes

Tentamen Programmeren in C (EE1400)

Hoofdstuk 6: Digitale signalen

Bijlage Inlezen nieuwe tarieven per verzekeraar

Indirecte adressering

Een typisch programma in C en C++ bestaat uit een aantal onderdelen:

VAN HET PROGRAMMEREN. Inleiding. Het spiraalmodel. De programmeertaal. vervolgens de berekening van het totale bedrag, incl. BTW:

Voorbeeld casus mondeling college-examen

slides6.pdf 16 nov

Geheugenbeheer. ICT Infrastructuren 2 december 2013

Vakgroep CW KAHO Sint-Lieven

Antwoorden vragen en opgaven Basismodule

Transcriptie:

Dictaat Computersystemen in1705 Tiende editie Technische Universiteit Delft Faculteit EWI Afdeling Softwaretechnologie, Basiseenheid PDS Januari 2007

i Ten Geleide Het studieonderdeel Computersystemen (vakcode in1705) heeft tot doel het verschaffen van kennis en begrip van de architectuur en organisatie van computersystemen, zowel wat betreft de apparatuur als de programmatuur. Het vak bestaat uit twee delen in1705-i (het vak zelf) en een practicum in1705-ii. Het vak wordt gedoceerd aan de hand van het voorliggende collegedictaat en het boek [Hamacher02] V.C. Hamacher, Z.G. Vranesic, en S.G. Zaky, Computer Organization, vijfde editie, 2002, McGraw-Hill. Het uitgangspunt bij het college is het boek [Hamacher02]. Dit collegedictaat bevat de volgende aanvullingen en verduidelijkingen op het boek: Hoofdstuk 1 bevat een uitgebreidere behandeling van datarepresentaties dan in H6 van [Hamacher02] en sluit aan bij hetgeen in de colleges wordt behandeld. Hoofdstuk 2 t/m 3 zijn uitgebreidere behandelingen van H2, paragraaf 2.6 van [Hamacher02]. Hoofdstuk 4 geeft een inleiding op de principes van vertalers voor computertalen (compilers en interpreters). Dit staat niet in [Hamacher02]. Hoofstuk 5 bevat aanvullend materiaal op H12 van [Hamacher02]. H. Sips Delft, januari 2007.

ii

Inhoudsopgave 1 Arithmetic (and Data Representation) 3 1.1 Getal representaties.................................. 4 1.1.1 De Binaire Representatie........................... 4 1.1.2 De r-tallige Representatie.......................... 5 1.1.3 Conversies.................................. 5 1.1.4 Criteria voor de beoordeling van getalrepresentaties............. 7 1.1.5 De Sign Magnitude Representatie...................... 7 1.1.6 De Two s-complement Representatie.................... 7 1.1.7 De One s-complement Representatie.................... 8 1.1.8 De Excess-2 n 1 Representatie........................ 8 1.1.9 Teken-extensie en -reductie......................... 8 1.1.10 Binary Coded Decimal............................ 9 1.13 Beslisvariabelen.................................... 9 1.14 Karakters....................................... 10 1.15 Pointers........................................ 12 1.16 Arrays......................................... 12 1.17 Records........................................ 15 1.18 Gelinkte Lijsten.................................... 16 2 Assembler talen 17 2.1 Assembler Commando s............................... 17 2.2 Het vertalen en executeren van assembler programma s............... 18 2.2.1 Literals.................................... 19 2.2.2 Conditional Assembly............................ 19 2.2.3 Functies van Assemblers........................... 20 2.2.4 Datastructuren in Assemblers........................ 21 2.2.5 Two-pass Assemblers............................. 22 2.2.6 One-pass Assemblers............................. 23 2.2.7 One-pass Load-and-go Assembler...................... 23 2.2.8 One-pass Module Assembler......................... 24 2.3 Macro s........................................ 25 2.3.1 Macro-definitie, -Aanroep en -Expansie................... 25 iii

INHOUDSOPGAVE 1 2.3.2 Macro s met Parameters........................... 26 2.3.3 Conditionele Macro-expansie........................ 28 2.3.4 Geneste Macro-aanroepen.......................... 29 2.3.5 De Implementatie van een Macro-faciliteit................. 29 2.3.6 Geneste Macro-definities........................... 30 3 Linking and Loading 31 3.1 Introductie....................................... 31 3.2 Compile, Load and Go................................ 32 3.3 De Absolute Loader.................................. 32 3.4 De Relocating Loader................................. 34 3.5 Linken, Libraries en de Linking Loader....................... 35 3.6 De Linkage Editor................................... 38 3.7 Relocatie via Virtuele Adressering.......................... 38 3.8 Het Tijdstip van Binding............................... 39 3.9 De Dynamische Linker................................ 40 4 Taalniveaus 41 4.1 Programmatransformaties............................... 41 4.1.1 Soorten Vertalers............................... 41 4.1.2 Soorten Interpretatoren............................ 42 4.1.3 Vergelijking van Interpreteren en Compileren................ 44 4.1.4 Just-in-time compilers............................ 45 4.1.5 Simuleren en Emuleren............................ 46 4.1.6 Compatibiliteit................................ 46 4.2 Taalniveaus...................................... 47 4.3 De Structuur van Compilers............................. 50 4.3.1 Lexicografische Analyse........................... 51 4.3.2 Syntactische Analyse............................. 53 4.3.3 Tussencode-generatie............................. 54 4.3.4 Code-optimalisatie.............................. 55 4.3.5 Code-generatie................................ 57 4.3.6 Het Beheer van Tabellen en Foutafhandeling................ 58 5 Large Computer Systems 61 5.1 Interconnection Networks............................... 61 5.1.3 Multistage Networks............................. 62 5.1.4 Hypercube Networks............................. 62 5.1.5 Mesh Networks................................ 63 5.1.7 Ring Networks................................ 64 A Overzicht Tentamenstof 67

2 INHOUDSOPGAVE

Hoofdstuk 1 Arithmetic (and Data Representation) In dit hoofstuk 1 houden we ons bezig met de vraag hoe gegevens opgeslagen worden in computers, oftewel met gegevensrepresentatie. Een getal wordt in het algemeen gerepresenteerd middels: Een alfabet Σ; Een rij (string) X bestaande uit elementen van Σ; Een afbeeldingsfunctie F van X naar een getal. Meestal hebben de rijen X een vaste lengte van n symbolen, X Σ n, hier genoteerd als X n. Er zijn vele keuzen mogelijk voor het alfabet Σ. Voorbeelden zijn: Σ roman = {I, V, M,...} Σ tientallig = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} Ook zijn er vele mogelijkheden voor de afbeeldingsfunctie F. In de computer beperken we ons tot het alfabet B = {0, 1}. De signatuur van de afbeeldingsfunctie F voor een string X n is dan F : B n V, Het domein V kan bestaan uit de verzameling van natuurlijke (N of Z) of reële getallen (R). Voor sommige datatypen in een bepaalde representatie bieden computers ondersteuning, d.w.z. ze zijn in zekere zin intrinsiek. Dit geldt in veel gevallen b.v. voor gehele getallen in de two scomplement representatie (zie hieronder) waarvoor een optel-operatie beschikbaar is die die representatie kent. In veel gevallen bestaat er bij het programmeren (zowel in assemblertaal als in hogere programmeertalen) naast z.g. enkelvoudige typen, die in hogere programmeertalen veelal voorgedefinieerd zijn, behoefte aan samengestelde variabelen, en dus aan samengestelde datatypen, die (eventueel in een aantal niveaus) opgebouwd zijn uit de enkelvoudige typen. We zullen van de volgende enkelvoudige datatypen nagaan op welke manier ze gerepresenteerd kunnen worden: 1 Zie ook H6 van [Hamacher02] 3

4 HOOFDSTUK 1. ARITHMETIC (AND DATA REPRESENTATION) 1. Gehele getallen (Engels: integers); 2. Reële getallen (Engels: reals), in computer-terminologie vaak aangeduid als drijvendekomma getallen (Engels: floating-point numbers); 3. Beslisvariabelen (Booleans); 4. Characters, tekens, o.a. de voor de mens leesbare tekens (letters, cijfers, leestekens, enz.). 5. Pointers, adressen van datastructuren in het hoofdgeheugen. Daarnaast behandelen we de volgende samengestelde datatypen: 1. Arrays, d.w.z. matrices van in principe willekeurige dimensie met elementen van een enkelvoudig of samengesteld datatype (b.v. integers, characters); 2. Records, d.w.z. datatypen die verschillende andere datatypen in zich verenigen; 3. Gelinkte lijsten, een combinatie van records en pointers. 1.1 Getal representaties Gehele getallen worden genoteerd op een wijze die samenhangt met het grondtal. De gebruikte tekens (cijfers) duiden op een zekere waarde, waarvan het gewicht wordt bepaald door de plaats in het getal. De natuurlijke wijze om in een computer met gehele getallen om te gaan is uiteraard het binaire (tweetallige) talstelsel. 1.1.1 De Binaire Representatie De binaire representatie van niet-negatieve gehele getallen (unsigned integers) m.b.v. een string X n = (x n 1... x 0 ) van n bits is F b (X n ) = x n 1 2 n 1 + x n 2 2 n 2... x 0 2 0 of F b (X n ) = n 1 i=0 Het waarde bereik van deze representatie is [0, 2 n 1]. x i 2 i.

1.1. GETAL REPRESENTATIES 5 1.1.2 De r-tallige Representatie De r-tallige representatie van niet-negatieve gehele getallen m.b.v. n (g-tallige) cijfers is (S is hier de verzameling van alle symbolen (0, 1,..., r 1)): F r (X n ) = x n 1 r n 1 + x n 2 r n 2... x 0 r 0 of F r (X n ) = Het waarde bereik van deze representatie is [0, r n 1]. Het getal r wordt wel de radix of het grondtal genoemd. De meest gebruikte waarden voor r zijn 2 (het binaire stelsel), 8 (het oktale stelsel met cijfers 0,1,2,3,4,5,6,7), 10 (het decimale stelsel), en 16 (het zestientallige of hexadecimale stelsel met cijfers 0,1,...,9,A,B,C,D,E,F). Zie Tabel 1.1. n 1 i=0 x i r i. binair oktaal decimaal hexadecimaal 0 0 0 0 1 1 1 1 10 2 2 2 11 3 3 3 100 4 4 4 101 5 5 5 110 6 6 6 111 7 7 7 1000 10 8 8 1001 11 9 9 1010 12 10 A 1011 13 11 B 1100 14 12 C 1101 15 13 D 1110 16 14 E 1111 17 15 F Tabel 1.1: De binaire, oktale, decimale, en hexadecimale representatie. 1.1.3 Conversies De conversie tussen de binaire en oktale, en tussen de binaire en hexadecimale representaties van niet-negatieve gehele getallen is eenvoudig, omdat een aantal cijfers in de binaire representatie

6 HOOFDSTUK 1. ARITHMETIC (AND DATA REPRESENTATION) overeenkomt met één cijfer in de oktale en hexadecimale representatie (omdat 8 en 16 machten zijn van 2). Door groepen van 3 resp. 4 bits samen te nemen (en eventueel nullen voor het getal te plaatsen om het aantal bits een veelvoud van 3 of 4 te laten zijn), komt de conversie van binair naar oktaal resp. hexadecimaal tot stand. Andersom bestaat de conversie uit het uitschrijven van de oktale of hexadecimale cijfers in bits. De conversie tussen decimaal en binair (of oktaal of hexadecimaal) verloopt wat moeilijker. Van binair naar decimaal kan geconverteerd worden door de machten van 2 uit te rekenen en op te tellen. De conversie van decimaal naar binair kan geschieden door de grootste macht van 2 kleiner dan of gelijk aan het getal te bepalen, daarvoor een 1 in de binaire representatie op te schrijven, die macht van 2 van het getal af te trekken, en dit proces voort te zetten tot het getal 0 is. Hierbij wordt dus de binaire representatie van links naar rechts opgebouwd. Een tweede methode (zie ook [H], app. E.2) bepaalt de binaire representatie van rechts naar links: deel het getal door 2, schrijf de rest op (0 of 1, dit is het rechtse bit), en doe vervolgens hetzelfde met het quotiënt. Ga hiermee door totdat het quotiënt gelijk aan 0 is. Voorbeelden. Van binair naar oktaal (en andersom): 10 100 101 001 = 010 100 101 001 = 2451 Van binair naar hexadecimaal (en andersom): 101 1010 1001 = 0101 1010 1001 = 5A9 Van binair naar decimaal: Van decimaal naar binair (eerste methode): 10110101001 = 2 10 + 2 8 + 2 7 + 2 5 + 2 3 + 2 0 = 1449. 1449 = 2 10 + 425 = 2 10 + 2 8 + 169 =... = 10110101001. Van decimaal naar binair (tweede methode, de laatste kolom geeft, van boven naar onder gelezen, het binair genoteerde getal gelezen van rechts naar links): 1449 = 2 724 + 1 724 = 2 362 + 0... =... 11 = 2 5 + 1 5 = 2 2 + 1 2 = 2 1 + 0 1 = 0 1 + 1

1.1. GETAL REPRESENTATIES 7 1.1.4 Criteria voor de beoordeling van getalrepresentaties Alvorens de verschillende getalrepresentaties te behandelen worden een aantal criteria gegeven waar de getalrepresentatie aan getoets kan worden. Die criteria hebben meestal verband met de efficiency van de representatie in hardware. De criteria zijn: Het getallenbereik van de representatie; De representatie van het getal nul; De efficiency van de implementatie van Tekeninversie Optelling Extensie van een n bits naar m bits representatie 1.1.5 De Sign Magnitude Representatie De sign/magnitude representatie van gehele getallen m.b.v. n bits is F sm = sign(x n 1 )(x n 2 2 n 2... x 0 2 0 ) waarbij sign(0) = + en sign(1) =. Het waarde bereik van deze representatie is [ (2 n 1 1), 2 n 1 1]. Een voordeel van deze representatie is dat het waardenbereik symmetrisch om het getal 0 is. Een nadeel is dat het getal 0 twee representanten heeft, nl. 00..00 en tt 10..00. 1.1.6 De Two s-complement Representatie De two s-complement representatie van gehele getallen m.b.v. n bits is F 2c = x n 1 2 n 1 + (x n 2 2 n 2... x 0 2 0 ) Het waarde bereik van deze representatie is [ 2 n 1, 2 n 1 1]. Een nadeel van deze representatie is dat het waardenbereik niet symmetrisch om het getal 0 is. Een voordeel is dat het getal 0 slechts één representant heeft. In computers is de two s-complement representatie de meest gangbare.

8 HOOFDSTUK 1. ARITHMETIC (AND DATA REPRESENTATION) 1.1.7 De One s-complement Representatie De one s-complement representatie van gehele getallen m.b.v. n bits is F 1c = x n 1 (2 n 1 1) + (x n 2 2 n 2... x 0 2 0 ) Het waarde bereik van deze representatie is [ (2 n 1 1), 2 n 1 1]. Bij deze representatie geldt hetzelfde voor- en nadeel als bij de sign/magnitude representatie. 1.1.8 De Excess-2 n 1 Representatie De excess-2 n 1 representatie van gehele getallen m.b.v. n bits is F ex = x n 1 2 n 1 + x n 2 2 n 2... x 0 2 0 2 n 1 Het waarde bereik van deze representatie is [ 2 n 1, 2 n 1 1]. Bij deze representatie geldt hetzelfde voor- en nadeel als bij de two s complement-representatie. De excess-representatie en de two s-complement representatie verschillen slechts in het linker (teken-)bit. 1.1.9 Teken-extensie en -reductie Het kan voorkomen dat een getal m, weergegeven in de two s-complement representatie met n bits, moet worden gerepresenteerd in de two s-complement representatie met k bits, met k n (b.v. als een getal in een byte in een woord moet worden geplaatst, of andersom). Veronderstel eerst dat k > n. In dat geval zal m zeker gerepresenteerd kunnen worden in k bits. Als m 0, dan X m = 0... 0X n en als m < 0, X m = 1... 1X n (Hier heeft 1...10...0 k n enen en n nullen). Dit betekent dat de k-bits two s-complement representatie wordt gevonden uit de n-bits two s-complement representatie door de k n extra bits gelijk te nemen aan het oorspronkelijke linker bit. Dit wordt teken-extensie (Engels: sign extension) genoemd, omdat het linker bit het teken van het getal aangeeft. Als k < n, dan treedt teken-reductie of -compressie op. Indien de linker n k +1 bits van X n gelijk zijn treedt bij representatie met k bits geen overflow op en is m te representeren in k bits. Indien die bits niet alle gelijk zijn treedt bij representatie met k bits wel overflow op en is m niet te representeren in k bits. In het eerste geval kunnen de linker n k bits van X n weggelaten worden om X m te verkrijgen.

1.13. BESLISVARIABELEN 9 Voorbeelden. De decimale getallen 4, 9, 6, 20 worden in de 6-bits two s-complement representatie gerepresenteerd door: 000100 001001 111010 101100 Volgens bovenstaande kan men aan deze bitpatronen zien dat 4 en 6 wel, en 9 en 20 niet in 4 bits zijn te representeren. 1.1.10 Binary Coded Decimal Sommige computers hebben de mogelijkheid om rechtstreeks te rekenen met decimale getallen, die dan per cijfer binair gecodeerd worden opgeslagen in het geheugen. De code die daarvoor gebruikt wordt heet de BCD-code voor Binary Coded Decimal (zie ook [H], Table E.1). Aangezien er 10 tientallige cijfers zijn is er voor één cijfer 4 bits nodig, die binair de combinaties 0000 tot 1001 mogen aannemen. De overige zes mogelijkheden (1010 t/m 1111) zijn ongeldig. Per byte worden twee decimale cijfers opgeslagen. Deze wijze van opbergen van getallen vergt uiteraard meer bits dan de gewone binaire representatie, maar heeft als voordeel dat de omzetting naar b.v. af te drukken getallen veel eenvoudiger is. Voorbeeld. r(3095) = 0011 0000 1001 0101 Het is gebruikelijk om als er ook negatieve getallen gebruikt worden, de combinaties 1010 en 1011 (twee niet-geldige combinaties voor cijfers) voor het plus-teken resp. het min-teken te gebruiken. 1.13 Beslisvariabelen Een veel gebruikte soort variabelen in computerprogramma s wordt gevormd door de beslis-variabelen of booleans, waarin condities worden opgeslagen aan de hand waarvan een programma deze of gene wending zal nemen. In hogere programmeertalen treft men deze vaak aan in statements als of if condition then action1 else action2 while condition do action

10 HOOFDSTUK 1. ARITHMETIC (AND DATA REPRESENTATION) waarin condition een boolean uitdrukking of variabele is die als waarden true of false kan hebben. Het is duidelijk dat een enkel bit voldoende is om een boolean variabele op te slaan (bijvoorbeeld met true=1 en false=0). Omdat in vrijwel geen enkele computer de bits apart adresseerbaar zijn, wordt een boolean toch veelal in een heel byte of woord opgeslagen. Er zijn dan vele mogelijkheden voor het coderen van true en false; de keuze wordt bepaald door de mogelijkheden die de instructieset biedt. Een mogelijkheid is om false te representeren door een byte met louter nullen, en true door iedere andere waarde (hier is dus de representatie r geen functie). 1.14 Karakters Voor in- en uitvoer worden door de mens leesbare tekens gebruikt, die intern in de computer op zekere wijze gecodeerd zijn. Het aantal bits dat hiervoor nodig is wordt bepaald door het aantal verschillende tekens. Een bepaalde afspraak voor het coderen van een verzameling characters wordt een character code genoemd (zie ook [H], app. E.1). Hoewel er soms wel enige regelmatigheid is in de codering, moet zo n character code in het algemeen worden weergegeven door een tabel met de vertaling tussen characters en codes. Twee gangbare character codes zijn: 1. De 7-bits American Standard Code for Information Interchange (ASCII) code van de International Standards Organization (ISO). Hierbij wordt een karakter in een byte opgeslagen. 2. De 8-bits Extended Binary Coded Decimal Interchange Code (EBCDIC), die z n naam ontleent aan het feit dat hij de BCD-code voor decimale cijfers in zekere zin omvat (de code van de decimale cijfers 0 t/m 9 is F0,...,F9 (hexadecimaal)). Beide codes bevatten grote en kleine letters, cijfers, bijzondere symbolen, leestekens, en een assortiment speciale codes ten behoeve van datatransmissie en in- en uitvoer met randapparatuur (zo is er b.v. een code voor newline,ëen karakter dat aangeeft dat een terminal of printer op een nieuwe regel verder moet gaan). De meeste computers kennen geen afzonderlijke instructies voor bewerkingen op characters: ze worden derhalve behandeld als (kleine) getallen. Het is dus noodzakelijk om rekening te houden met de interne, getalsmatige voorstelling van characters. In character codes zitten soms regelmatigheden, d.w.z. dat bij elkaar horende c.q. elkaar logisch opvolgende tekens met elkaar (numeriek) opeenvolgende codes worden weergegeven. In de ASCII-code zitten als regelmatigheden b.v. dat de decimale cijfers, de kleine letters, en de hoofdletters ieder op zich als opeenvolgende binaire getallen gecodeerd zijn. EBCDIC is wat meer historisch bepaald, en hier is voor het oog wat meer onregelmatigheid. Zo bevat de codering van kleine letters en hoofdletters diverse gaten (zie [H], Tabellen E.2,3). Conversies In een computersysteem vinden constant allerlei conversies van gegevens plaats. Om het gebruik van gegevens-representatie en de daarbij behorende conversies duidelijk te maken volgen we nu

1.14. KARAKTERS 11 de weg door een computer van een door de gebruiker ingetypt getal waarop een bewerking wordt uitgevoerd: De gebruiker typt een getal in, b.v. 123 (decimaal). De computer ontvangt de ASCII-codes van de drie karakters 1,2, en 3: ASCII-code van 1: 00110001 ASCII-code van 2: 00110010 ASCII-code van 3: 00110011 Het programma zet deze drie codes om naar de binaire waarde van het bedoelde getal: 1*100: 01100100 2*10: 00010100 3*1: 00000011 -------- + 123: 01111011 Op het getal vindt een bewerking plaats, er wordt b.v. 50 vanaf getrokken: 123: 01111011 50: 00110010 -------- - 73: 01001001 Het getal 73 moet naar de terminal uitgevoerd worden, dus berekent het programma de cijfers in de decimale representatie van het resultaat: 0 (*100): 00000000 7 (*10): 00000111 3 (*1): 00000011 De computer stuurt de volgende ASCII-codes naar de terminal, waardoor 073 op de terminal verschijnt: ASCII-code van 0: 00110000 ASCII-code van 7: 00110111 ASCII_code van 3: 00110011

12 HOOFDSTUK 1. ARITHMETIC (AND DATA REPRESENTATION) 1.15 Pointers Een pointer is het adres in het hoofdgeheugen van een variabele. In machine-taal is het gebruik van het adres van een variabele de aangewezen manier om z n waarde te bekijken of te veranderen. Ook in hogere programmeertalen kunnen pointers nuttig zijn, vooral voor dynamische datastructuren, d.w.z. variabelen die niet al ten tijde van het schrijven van een programma volledig gedeclareerd kunnen worden, maar ten tijde van de executie worden opgebouwd (en afgebroken). De representatie is in principe simpel: het waardenbereik wordt gevormd door de gehele getallen vanaf 0 t/m een of andere maximale waarde N, en de gewone binaire codering kan worden gebruikt. 1.16 Arrays Het eerste voorbeeld van samengestelde datatypen dat we behandelen is het array, waarvoor ook in veel assemblertalen ondersteuning bestaat via index-adressering of autoincrement/ autodecrementadressering. Een één-dimensionaal array (of vector) bestaat uit een eindige rij elementen van hetzelfde type, b.v. integers, characters, of op zich weer een samengesteld type. Bij arrays van characters spreekt men meestal van (text)strings, waarbij één character één element van het array inneemt. Soms wordt er een speciaal karakter (veelal het NULL-character met als representatie een byte bestaande uit 8 nullen) als laatste element aan zo n array toegevoegd om het einde van de string aan te geven. Als A de naam (identifier) van het array is, dan worden de elementen aangeduid met A[I], waarbij I (de index) een geheel getal binnen een bepaald (eindig) interval is (er bestaat in sommige talen de mogelijkheid om een ander datatype dan integers als index te gebruiken, maar we veronderstellen hier voor het gemak integers). Zo zal bij opslag van de string "This is a text string" in array A gelden dat A[0] = T, A[4] =, A[13] = t, en A[21] = \0, waarbij \0 een notatie is voor het NULL-character. De meest voor de hand liggende manier om een een-dimensionaal array in een computergeheugen op te slaan is door de opeenvolgende elementen aaneensluitend te plaatsen in geheugenlocaties (op opeenvolgende adressen, als ieder element slechts één geheugenplaats inneemt, en anders op adressen die net zoveel verschillen als een element van het array aan geheugenplaatsen inneemt). In feite kunnen we een geheugen opvatten als een één-dimensionaal array mem[0..n-1] van N adresseerbare eenheden (b.v. bytes), en op deze manier wordt het array A een deel van het geheugenarray. De opslag van een één-dimensionaal array A[K,L] vanaf adres B kan dan op de volgende manier weergegeven worden, waarbij we ervan uitgaan dat een element slechts één geheugenlocatie beslaat: for I:=K to L DO mem[b+(i-k)] := A[I] De opslag van een een-dimensionaal array wordt weergegeven in Figuur 1.1.

1.16. ARRAYS 13 Adres: 2404 2406 A[1] A[2] 2402 + 2(N 1) 2402 + 2N A[N-1] A[N] Figuur 1.1: Opslag van een een-dimensionaal array (het datatype van de elementen van A beslaat twee locaties). Meer-dimensionale arrays (matrices) komen eveneens voor in hogere programmeertalen. De declaratie in Modula-2 van een array van dimensie n met als component-type datatype ziet eruit als var A: array[l1..h1,l2..h2,...,ln..hn] of datatype; waarbij een component wordt aangeduid met A[I1,...,In]. Omdat een computergeheugen slechts een-dimensionaal adresseerbaar is, moet een meer-dimensionaal array in stukken worden opgesplitst, die na elkaar worden opgeslagen. Op welke manier dat gebeurt is afhankelijk van de (compiler van) de hogere programmeertaal die wordt gebruikt. Er bestaan twee veel-gebruikte manieren om dit te doen, die gemeenschappelijk hebben dat een array van dimensie n gezien wordt als een een-dimensionaal array van elementen die ieder op zich een array van dimensie n-1 zijn. Deze laatste worden dan direct achter elkaar in het geheugen opgeslagen. We zullen de twee representaties eerst nagaan aan de hand van twee-dimensionale arrays, en daarna het algemene geval behandelen. In een twee-dimensionaal array A[K..L,P..Q] vatten we zoals gebruikelijk de eerste index op als de rij-index, en de tweede index als de kolom-index. De twee representaties zijn nu: 1. De row-major ordening (voor twee-dimensionale arrays) houdt in dat men vanaf het beginadres eerst de eerste rij (de elementen A[K,P] t/m A[K,Q]) opslaat, vervolgens de tweede rij, en zo doorgaande t/m de laatste rij (de elementen A[L,P] t/m A[L,Q]). In code kan men dat als volgt weergeven, ervan uitgaande dat een array-element één geheugenlocatie beslaat (zie ook Figuur 1.2): for I:=K to L do for J:=P to Q do mem[b+(i-k)(q-p+1)+(j-p)] := A[I,J]

14 HOOFDSTUK 1. ARITHMETIC (AND DATA REPRESENTATION) Adres: B A[1,1] B+2 A[1,2] A[1,L-1] A[1,L] A[2,1] A[K-1,L] A[K,1] A[K,2] A[K,L-1] A[K,L] Dope vector: verwijzing: B aantal dim.: 2 dim. 1 multiplier: 2*L dim. 1 low bound: 1 dim. 1 high bound: K dim. 2 multiplier: 2 dim. 2 low bound: 1 dim. 2 high bound: L Figuur 1.2: Row-major ordening van een twee-dimensionaal array[1..k,1..l] A. 2. De definitie van column-major ordening (voor twee-dimensionale arrays) is analoog aan de row-major ordening, met rijen en kolommen verwisseld. In code (zie ook Figuur 1.3): for J:=P to Q do for I:=K to L do mem[b+(i-k)+(j-p)(l-k+1)] := A[I,J] In het algemeen (d.w.z. voor willekeurige dimensie) kan men de representaties als volgt omschrijven: 1. De row-major ordening van het n-dimensionale array A[L1..H1,...,Ln..Hn] verkrijgt men door achtereenvolgens de H1-L1+1 (n-1)-dimensionale matrices A[I,L2..H2,...,Ln..Hn], I=L1,...,H1, volgens de row-major ordening op te slaan. Row-major ordening houdt in dat als men het hoofdgeheugen doorloopt van laag naar hoog adres, de laatste index het hardst loopt, dan de één-na-laatste, etc., terwijl de eerste index het langzaamst loopt. 2. De column-major ordening van het n-dimensionale array A[L1..H1,...,Ln..Hn] verkrijgt men door achtereenvolgens de Hn-Ln+1 (n-1)-dimensionale matrices A[L1..H1,...,Ln-1..Hn-1,I], I=Ln,...,Hn, volgens de column-major ordening op te slaan.

1.17. RECORDS 15 Adres: B A[1,1] B+2 A[2,1] A[K-1,1] A[K,1] A[1,2] A[K,L-1] A[1,L] A[2,L] A[K-1,L] A[K,L] Dope vector: verwijzing: B aantal dim.: 2 dim. 1 multiplier: 2 dim. 1 low bound: 1 dim. 1 high bound: K dim. 2 multiplier: 2*K dim. 2 low bound: 1 dim. 2 high bound: L Figuur 1.3: Column-major ordening van een twee-dimensionaal array[1..k,1..l] A. Om de opslagstructuur van een array bij te houden wordt tevens een beschrijving (descriptor, voor een array ook wel dope vector genoemd) opgeslagen met informatie die het mogelijk maakt na te gaan of een bepaalde verzameling waarden voor de indices toegestaan is, en snelle bepaling van de geheugenplaats van het bijbehorende array-element vergemakkelijkt. Deze dope vector bevat: 1. De verwijzing, d.w.z. het beginadres in het geheugen waar het array is opgeslagen; 2. Het aantal dimensies van het array; 3. Per dimensie de minimale en maximale waarde van de index (zodat nagegaan kan worden of de gebruikte index binnen het toegelaten waardenbereik valt); 4. Per dimensie de z.g. multiplier, d.w.z. voor dimensie k de waarde die bij het adres van array-element A[I1,..,Ik,..,In] moet worden opgeteld om het adres van arrayelement A[I1,..,Ik+1,..,In] te vinden. 1.17 Records Een andere manier om gegevens gestructureerd op te slaan is m.b.v. records. Een record bestaat uit een aantal velden van mogelijk verschillende typen, b.v. integers, reals, characters, etc. Elk veld

16 HOOFDSTUK 1. ARITHMETIC (AND DATA REPRESENTATION) kan afzonderlijk aangeduid worden via de naam van het veld. In het geheugen heeft elk veld een vaste plaats ten opzichte van het begin van het record. Een record-declaratie in Modula-2 ziet er zo uit: RECORD veld1: type1; veld2: type2;... veldm: typem; END; 1.18 Gelinkte Lijsten Indien in een record een veld is opgenomen dat als type het adres naar zo n record heeft, kan men in een programma een gelinkte lijst opbouwen. Er is een pointer nodig naar het eerste element van de lijst, en ieder element verwijst naar een volgend element van de lijst, behalve het laatste. De waarde van de pointer daarin moet een speciale waarde (symbolisch aangeduid met NULL) hebben, om het einde van de lijst aan te geven. Dit soort structuren is vooral handig als ten tijde van het compileren van een programma nog niet bekend is hoeveel records nodig zijn.

Hoofdstuk 2 Assembler talen 2.1 Assembler Commando s Een assemblerprogramma bestaat uit een rij assembler-statements. Een assembler-statement is een instructie of een pseudo-instructie (ook wel directief, of, in [Hamacher02], assembler command genoemd 1 ). Een pseudo-instructie is een aanwijzing voor de vertaling van het programma naar machinecode, en wordt dus niet uitgevoerd tijdens de executie van het programma. Een voorbeeld is een aanwijzing om voor een bepaalde variabele één of meer geheugenlocaties te reserveren. Instructies en pseudo-instructies bestaan uit de volgende vier delen: 1. Het labeldeel, dat een label bevat of leeg is; indien het een label bevat, dan wordt dat label geassocieerd met de geheugenlocatie van datgene wat erachter staat (een instructie of een variabele). Een label wordt voor een instructie gezet om er naar toe te kunnen springen vanuit andere instructies. Voor pseudo-instructies wordt een label gezet om gereserveerde variabelen met een symbolische naam (het label) te kunnen adresseren. 2. Het operatiedeel van een instructie bevat een symbolische operatiecode, b.v. ADD. Bij een pseudo-instructie staat er een code voor de aanwijzing. 3. Het operanden-deel van een instructie bevat nul, één, of meer aanduidingen voor de operanden van de instructie. Het aantal operanden wordt bepaald door de structuur (Engels: format) van de betreffende instructie. Een, twee of drie operanden zijn gebruikelijk. Bij een pseudo-instructie hangt de inhoud van dit deel van het type pseudo-instructie af. 4. Commentaar wordt opgenomen om een toelichting op de instructie, de pseudo-instructie of het programma in het algemeen te geven. Hoewel assemblerprogramma s veel duidelijker zijn dan machinecodeprogramma s, zijn ze in vergelijking met programma s geschreven in hogere programmeertalen nog steeds moeilijk te begrijpen, doordat assembler-instructies veel meer aansluiten bij de machine dan bij de op te lossen problemen. Vandaar dat het 1 Zie paragraaf 2.6 van [Hamacher02] 17

18 HOOFDSTUK 2. ASSEMBLER TALEN verstandig is zinvol commentaar bij programma s op te nemen, om op die manier de leesbaarheid te vergroten en het aanbrengen van veranderingen te vergemakkelijken. 2.2 Het vertalen en executeren van assembler programma s Aangezien een computer machine-instructies en geen assembler-instructies uitvoert, zal er, wanneer we in assemblercode programmeren, een omzetting van assemblercode naar machinecode plaats moeten vinden. Deze omzetting kan in principe door de programmeur zelf gedaan worden, maar is ook automatisch te doen, d.w.z. met een programma. Een assembler is een programma dat programma s geschreven in assemblercode omzet in programma s in machinecode. Aangezien elke assembler-instructie overeenkomt met een machine-instructie, is deze omzetting niet ingewikkeld, hoewel er wel verbanden bestaan tussen de instructies onderling, zoals in het geval van een sprong naar een andere instructie. Omzetting van programma s geschreven in een hogere programmeertaal naar programma s in machinecode, wat gedaan wordt door programma s die men compilers noemt, is een veel ingewikkelder proces, doordat hogere programmeertalen onafhankelijk van specifieke machinetypen gedefinieerd zijn, terwijl assemblertalen juist aansluiten bij een bepaald machinetype. Zowel bij assemblers als bij compilers wordt het programma dat omgezet wordt het bronprogramma (Engels: source program) en het resultaat van de omzetting het objectprogramma (Engels: object program) genoemd. Ook wordt wel gesproken over brontaal (Engels: source language) en doeltaal (Engels: target language) voor de respectievelijke talen waarin de programma s zijn en worden uitgedrukt. Het uitvoeren van een programma geschreven in assemblercode of een hogere programmeertaal geschiedt in twee stappen: 1. Het omzetten van het bronprogramma m.b.v. een assembler of vertaler in machinecode; 2. Het uitvoeren van het aldus gecreëerde objectprogramma. Een assembler zet een programma geschreven in assemblertaal om in machinecode. De beschrijving van de assemblertaal ligt vast in het z.g. assembler reference manual. Hierin staat, in tegenstelling tot het definiërend rapport van een hogere programmeertaal, meestal geen duidelijke syntax-beschrijving in BNF-notatie. Syntax en semantiek worden in het reference manual meestal in natuurlijke taal beschreven. De voornaamste taken van een assembler zijn het controleren of er een correct assemblerprogramma wordt aangeboden en het genereren van de bijbehorende machinecode. Daarnaast kunnen allerlei faciliteiten in de assemblertaal zijn opgenomen die het de assemblerprogrammeur gemakkelijker maken, zoals mogelijkheden voor het gebruik van literals, van macro s, etc. De verwerking van deze faciliteiten behoort ook tot de taken van een assembler. Ook kunnen d.m.v. pseudo-instructies aanwijzingen worden gegeven over de plaats van en de soort van te genereren machinecode, over de layout van de listing, en ook over het feit onder welke omstandigheden delen van een assemblerprogramma moeten worden vertaald, de z.g. conditional assembly. Alvorens nader in te gaan op de structuur van assemblers, zullen we eerst nog een paar begrippen die hiervoor genoemd zijn nader beschrijven.

2.2. HET VERTALEN EN EXECUTEREN VAN ASSEMBLER PROGRAMMA S 19 2.2.1 Literals Literals zijn constanten waarvoor de assembler automatisch geheugenruimte reserveert in het geval de computer op het conventionele-machine niveau niet over de mogelijkheid van onmiddelijke adressering beschikt terwijl dat op het assemblerniveau wel door de programmeur kan worden gespecificeerd. De programmeur behoeft deze naam niet te declareren. Tegenwoordig wordt aanbevolen om elke constante expliciet een duidelijke naam te geven. Dit maakt het programma beter leesbaar en beter onderhoudbaar. Voorbeeld ADDI R3,0,1 is gelijk aan (d.w.z. er wordt dezelfde machinecode gegenereerd als voor): EEN:.equ 1. ADDI R3,0,EEN 2.2.2 Conditional Assembly Door middel van parameters is het mogelijk om te genereren machine-code aan te passen aan verschillende situaties. Zo kan men b.v. een algemeen programma voor verschillende typen CPUs geschreven hebben met speciale delen code die alleen voor een bepaald type nodig of geschikt is. Een andere situatie waarin dit handig is, is wanneer delen van het assemblerprogramma in bepaalde situaties niet nodig zijn. In het te assembleren programma moeten dan pseudo-instructies worden opgenomen die aangeven wanneer een deel van de code wel/niet geassembleerd moet worden. Afhankelijk van de (b.v. op de commando-regel) meegegeven parameterwaarde vindt al dan niet assembly plaats. De pseudo-instructies kunnen er uit zien als:.ifdef a.ifnotdef a.ifpos a.ifneg a.ifzer a #assembleren als a gedefinieerd is #assembleren als a niet gedefinieerd is #assembleren als a positief is #assembleren als a negatief is #assembleren als a nul is en.endif #einde van de conditionele code

20 HOOFDSTUK 2. ASSEMBLER TALEN Voorbeeld.IFDEF a... #te assembleren als a gedefinieerd is.endif....ifzer a... #te assembleren als a gelijk aan 0 is.endif 2.2.3 Functies van Assemblers Een te assembleren programma-module bevat drie soorten grootheden, te weten 1. Absolute, zoals operatiecodes, numerieke en stringconstanten, vaste adressen. De waarden van deze grootheden zijn onafhankelijk van de geheugenlocaties waar het programma uiteindelijk terecht zal komen. 2. Relatieve, zoals adressen van instructies en adressen van data die relatief zijn ten opzichte van het begin van een module. 3. Extern gedefinieerde, d.w.z. grootheden die in een module wel gebruikt maar niet gedefinieerd worden (zie 3). De functies van een assembler zijn: 1. Lexicografische en syntactische analyse (altijd). Onder lexicografische analyse wordt verstaan het karakter voor karakter inlezen van een programma en het groeperen van bij elkaar horende karakters (zoals de karakters van een label, een opcode); labels, symbolische adressen, operatiecodes, en constanten moeten worden herkend. Daarna moet de syntax van een assemblerinstructie worden gecontroleerd. 2. Vervangen van symbolische namen en operatiecodes (altijd). Met behulp van de aanwezige operatiecode-tabel en de reeds opgebouwde naamlijst (zie hieronder) worden symbolische namen en operatiecodes omgezet in numerieke codes. 3. Vertalen van constanten naar machine-representatie (altijd). De assemblerprogrammeur kan numerieke constanten meestal op een aantal manieren weergeven: decimaal, oktaal, hexadecimaal, floating point, fixed point. Deze representaties moeten worden geconverteerd naar de machine-representatie. Ook stringconstanten moeten worden omgezet in een machinerepresentatie. 4. Verwerken van literals (alleen nodig bij sommige doelmachines). Er wordt een lijst van literals bijgehouden (de literal pool), eventueel zodanig dat elke literal slechts eenmaal voorkomt. Deze literal pool wordt aan het einde van het programma toegevoegd.

2.2. HET VERTALEN EN EXECUTEREN VAN ASSEMBLER PROGRAMMA S 21 5. Foutencontrole en -afhandeling (altijd). Een assembler moet in een enkele vertaalslag zoveel mogelijk fouten detecteren en afhandelen. Een assembler die reeds na de eerste fout stopt is ontoelaatbaar. Als een fout wordt gevonden, moet deze geregistreerd worden, zodat in de (eventuele) listing kan worden aangegeven waar de fout optrad en welke fout het was. Veel gemaakte fouten zijn: ongedefinieerde namen, dubbel gedefinieerde namen, niet-bestaande operatiecodes, onjuist aantal operanden. 6. Verzorgen van een listing (naar wens van de gebruiker). De programmeur moet, indien hij dit wenst, een afdruk van zijn programma met daarnaast de code die gegenereerd is kunnen krijgen. Tevens zal vaak een cross-reference table, d.w.z. een tabel met voor iedere door de programmeur geïntroduceerde naam een lijst met de locaties waar die naam is gebruikt, gewenst zijn om het debuggen te vereenvoudigen. 7. Verwerken van pseudo-instructies (altijd). Behalve instructies die omgezet moeten worden in code, bevat een programma meestal ook pseudo-instructies die verwerkt moeten worden. Voorbeelden hiervan zijn: conditionele assembleer-aanwijzingen, aanwijzingen voor het reserveren van geheugen, einde-programma aanwijzing, etc. 2.2.4 Datastructuren in Assemblers In een assembler worden de volgende drie datastructuren gebruikt: 1. De naamlijst (Engels: symbol table); 2. De opcode-tabel; 3. De Instruction Location Counter (ILC). We zullen deze datastructuren bespreken aan de hand van het volgende stukje PowerPC assemblerprogramma: label opcode operanden lengte instructie ILC (in bytes) (oktaal) ADDI R4,0,0 4 144 ADDI R3,0,4 4 150 L1: ADDI R4,R4,1 4 154 ADDI R4,R4,A 4 160 ADDIC. R3,R3,-1 4 164 BNE L1 4 170 LHZ R6,2(R4) 4 174 De assembler bouwt gedurende het doorlezen van een programma een naamlijst op met voor iedere door de programmeur gebruikte naam (in het voorbeeld A en L1) een entry, die tenminste die naam en de waarde bevat. Voor een variabele (zoals A) is dat het adres van de variabele, voor een label (zoals L1) het adres van de instructie waar het label voor staat.

22 HOOFDSTUK 2. ASSEMBLER TALEN Om een assembler-instructie te kunnen vertalen in de bijbehorende machine-instructie heeft de assembler de z.g. opcode-tabel nodig. Die bevat van iedere assembler-mnemonic (minstens) één entry met informatie over de bijbehorende machine-instructie, zoals de (binaire) opcode, het aantal en de typen van de operanden, en de lengte. In het geval die zaken niet alleen van de mnemonic afhangen maar b.v. ook van de typen operanden kunnen er meerdere entries per mnemonic zijn. Als een assembler-programma wordt vertaald, wordt veelal verondersteld dat het resulterende machine-programma voor executie op een vast adres, b.v. 100 (oktaal) zal worden geladen. Na het analyseren van de eerste instructie weet de assembler dan op welke plaats de tweede instructie zal komen. Zo verder gaande weet de assembler voor het analyseren van iedere instructie op welke plaats die komt te staan. Deze waarde wordt de Instruction Location Counter genoemd. Bij het analyseren van de instructie ADDI R4,R4,1 vindt de assembler als waarde van L1 154, hetgeen in de symbol table wordt ingevuld. 2.2.5 Two-pass Assemblers Om symbolische namen zoals labels door hun waarde te vervangen moet een assembler die waarde wel weten. Indien een assembler een assemblerprogramma sequentieel doorleest, zal, als hij een instructie tegenkomt waarin een label gebruikt wordt (b.v. in een spronginstructie) dat pas later in het programma voorkomt, niet bekend zijn welke waarde moet worden gebruikt. Dit wordt het forward referencing problem genoemd. De meest voor de hand liggende oplossing, die in veel assemblers is gekozen, is om een assemblerprogramma twee keer door te lezen. Zo n assembler bestaat dan uit twee doorgangen (Engels: passes), en wordt een two-pass assembler genoemd. In pass 1 van een two-pass assembler worden in het algemeen uitgevoerd: 1. Scanning van de invoertekst, d.w.z. er vindt een lexicografische en syntactische analyse plaats; 2. Bepalen van instructielengte en -format; 3. Opbouwen van de naamlijst; 4. Vastleggen van fouten voor een eventuele listing; 5. Opbouwen van een literal pool, zodat dubbele literals geëlimineerd kunnen worden. In pass 2 worden uitgevoerd: 1. Het definitief invullen van de waarden van labels en de adressen van operanden en literals; 2. Het genereren van de objectcode; 3. Het verzorgen van een listing. De uitvoer van pass 2 bestaat uit de objectcode en de extra informatie die nodig is voor de linker en de loader om losse modulen aan elkaar te koppelen en het programma op een willekeurige

2.2. HET VERTALEN EN EXECUTEREN VAN ASSEMBLER PROGRAMMA S 23 plaats in het hoofdgeheugen te kunnen laden (relocatie tabel, externe-referentie tabel en entrypoint tabel, zie 3). Bij het assembleren spelen naast de al eerder genoemde datastructuren de files voor pass 1, pass 2, de brontekst, eventuele tussencode, de doeltekst, en de listing een rol. De grootste verschillen in organisatie van two-pass assemblers worden bepaald door de keuze van de datastructuren. 2.2.6 One-pass Assemblers Het grote probleem dat bij assemblers met één pass moet worden opgelost, is het forward referencing problem. Door het opleggen van restricties, het gebruik van ingewikkeldere datastructuren in de assembler, en/of het overlaten van een deel van het werk aan loaders (zie 3), is het mogelijk verschillende typen one-pass assemblers te construeren, zoals de one-pass load-and-go assembler en de one-pass module assembler. 2.2.7 One-pass Load-and-go Assembler De eenvoudigste assembler is de load-and-go assembler. Deze produceert een machinecodeprogramma in het hoofdgeheugen, klaar om uitgevoerd te worden. Het uitvoeren geschiedt direct na het assembleren zonder tussenkomst van de gebruiker: de assembler springt naar het begin van het geassembleerde programma als ware het een subroutine. Het resulterende programma neemt geheugenlocaties in beslag waarvan de adressen vast liggen ten tijde van het assembleren en die later niet meer gewijzigd kunnen worden. Het geassembleerde programma kan bibliotheekroutines aanroepen, vooropgesteld dat deze routines andere locaties bezetten dan die welke door het programma in beslag worden genomen. Load-and-go-assemblers zijn niet geschikt voor modulaire programma-ontwikkeling, waarbij men in staat moet zijn parallel programmacomponenten te ontwerpen, te coderen en te testen. Bovendien moeten programma s die vaker gebruikt worden elke keer opnieuw worden geassembleerd. Bij deze wijze van assembleren gelden de volgende restricties: 1. Het programma draait in een vast gebied van het geheugen. 2. Het programma kan niet samengevoegd worden met andere, apart vertaalde modulen. 3. Er moet voldoende hoofdgeheugen zijn om zowel de assembler als de te genereren machinecode op te slaan. Load-and-go assemblers zijn bijzonder geschikt voor programma s die klein zijn en frequent gewijzigd worden. Het probleem van de voorwaartse referenties kan worden opgelost door in de objectcode per symbolische naam een keten van verwijzingen op te nemen naar de locaties waar de uiteindelijke waarde, zodra die bekend geworden is, moet worden ingevuld. In de naamlijst staan de naam en een pointer naar de plaats waar de laatst verwerkte instructie staat waarin die naam voorkwam. In de voorlopige vertaling van iedere instructie waarin de naam voorkomt staat het adres van de vorige (voorlopige) machine-instructie behorend bij een assembler-instructie waarin de naam ook voorkomt. In de eerste instructie waarin de naam voorkomt, staat een önmogelijkadres (b.v. 0000, als dat adres niet gebruikt wordt).

24 HOOFDSTUK 2. ASSEMBLER TALEN Voorbeeld Hieronder staat een klein programma voor een eenvoudige machine. locatie assemblerinstructie geassembleerd programma opcode/adres 100 LDA END 04 0000 102 TST RES 05 0000 104 LDA END 04 0100 114 JNZ NFOUND 06 0000 120 LDA RES 10 0102....... 200 RES:.. 210 NFOUND:.. 250 END:.. Het adres in de eerste instructie waarin een naam voorkomt, is 0000. Dit is het geval in de instructies op locaties 100, 102, en 114. In iedere volgende instructie is het adres gelijk aan het adres van de voorgaande instructie waarin dezelfde naam voorkwam. Dit is het geval in de instructies op locaties 104 en 120. Voordat waarden van de labels bekend zijn bevat de naamlijst voor iedere naam het adres van de laatst gelezen instructie waar die naam is aangetroffen. In het voorbeeld ziet de naamlijst er op het moment dat de assembler de instructie op locatie 120 heeft verwerkt er dus uit als: RES 0120 NFOUND 0114 END 0104 Bij verwerking van de pseudo-instructie op locatie 200 wordt in de entry van RES in de naamlijst 200 als waarde opgenomen, nadat in de instructies op locaties 120 en 102 (in die volgorde) de juiste waarde (200) is ingevuld. 2.2.8 One-pass Module Assembler Een one-pass module assembler produceert objectmodulen die nog gelinkt en geladen moeten worden. Er wordt vertrouwd op de pass die de loader nog moet maken, d.w.z. de loader voert min of meer de tweede pass van de assembler uit. De restrictie die wordt opgelegd is dat de voorwaartse referenties alleen mogen voorkomen in spronginstructies. Dus datagebieden moeten voorafgaan aan de instructies die naar deze gebieden verwijzen; ook mogen geen literals worden gebruikt. Elke ongedefinieerde naam in een spronginstructie komt in de z.g. branch-ahead table (BAT). Indien verscheidene spronginstructies naar dezelfde naam verwijzen, kan ofwel iedere keer een aparte entry gecreëerd worden die de naam en het adres van de instructie bevat, ofwel er kan voor iedere naam die in een voorwaartse referentie gebruikt wordt weer een soortgelijke keten opgebouwd worden als bij onbekende namen bij een one-pass load-and-go assembler. Zodra een labelnaam

2.3. MACRO S 25 voorkomt, wordt de BAT langsgelopen, en voor elke keer dat de naam van het label voorkomt (of voor elk element in de bij de naam behorende keten), wordt een directief voor de loader gegenereerd om te zorgen dat het bijbehorende adresveld wordt aangepast als het programma wordt geladen. Vervolgens wordt het betreffende naam-adres paar uit de tabel (of het element uit de keten) verwijderd. Referenties naar de labelnaam die daarna nog volgen kunnen uiteraard direct vertaald worden in het juiste adres. Een andere mogelijkheid is dat er indirect wordt gesprongen via de branch-ahead table. Op de plaats van de voorwaartse referentie komt een indirecte sprong via een BAT-entry te staan. In de betreffende BAT-entry wordt het definitieve adres van het label gezet. De tabel moet dan wel met het programma worden meegeladen. 2.3 Macro s Bij het schrijven van assemblerprogramma s komt het regelmatig voor dat (vrijwel) identieke stukken code opgeschreven worden. Om dit schrijfwerk te verminderen en de overzichtelijkheid van assemblerprogramma s te vergroten, bestaat er in sommige assemblers een z.g. macro-faciliteit. Dit houdt in dat aan een rij assembler-statements een naam gegeven kan worden, die op verschillende plaatsen in het assemblerprogramma kan worden gebruikt om aan te geven dat op die plaatsen eigenlijk die rij assembler-statements moet staan. In zekere zin lijken macro s dus op subroutines, maar er zijn essentiële verschillen waarop we hieronder zullen ingaan. Een assembler die een macro-faciliteit heeft wordt wel een macro-assembler genoemd. 2.3.1 Macro-definitie, -Aanroep en -Expansie Om een macro te kunnen gebruiken moet deze gedefinieerd worden, hetgeen gebeurt m.b.v. pseudoinstructies die het begin en het einde van de rij assembler-statements (ook wel genoemd de body van de macro) van de macro aangeven, en moet er een naam aan de macro gegeven worden. Veronderstel dat de macro-body er als volgt uitziet: ADDI R3,0,A@l ADDIS R3,R3,A@h ADDI R4,0,B@l ADDIS R4,R4,B@h LWZ R6,0(R3) LWZ R5,0(R4) STW R5,0(R3) STW R6,0(R4) dan kan de macro-definitie er als volgt uitzien:.mbegin SWAP ADDI R3,0,A@l ADDIS R3,R3,A@h ADDI R4,0,B@l

26 HOOFDSTUK 2. ASSEMBLER TALEN.MEND ADDIS R4,R4,B@h LWZ R6,0(R3) LWZ R5,0(R4) STW R5,0(R3) STW R6,0(R4) Hierin is.mbegin de pseudo-instructie die aangeeft dat er een macro-definitie staat, waarbij de naam van de macro opgegeven dient te worden (hier SWAP), en.mend de pseudo-instructie die het einde van de macro-body aangeeft. Indien nu op een bepaalde plaats de rij assembler-staments moet komen te staan, kan dit worden aangegeven door een macro-aanroep op te nemen, d.w.z. door simpelweg SWAP als assembler-statement op te nemen. Bij het assembleren van het programma zal de assembler deze macro-aanroep vervangen door de body van de macro, een proces dat macro-expansie genoemd wordt. Logisch gezien is macro-expansie de eerste stap van de assembler, ook al wordt het meestal uitgevoerd als onderdeel van pass 1. Deze stap vervangt a.h.w. het aangeboden assemblerprogramma met macro s door een programma zonder macro s, waarin alle macro-aanroepen zijn vervangen door de bijbehorende bodies. Macro-expansie is louter string-manipulatie: de ene string (de macro-aanroep) wordt vervangen door de andere (de macro-body). Na macro-expansie (en zekere na volledige assemblage) is aan een programma niet meer te zien of er macro s zijn gebruikt (i.t.t. de situatie bij gebruik van subroutines). Subroutines en macro s lijken op elkaar. Beide worden gebruikt om een rij instructies een naam te geven en die naam te gebruiken i.p.v. aldoor de rij instructies te moeten opschrijven. Er zijn ook essentiële verschillen, die in Tabel 2.1 worden opgesomd. Met overhead wordt bedoeld of het mechanisme extra tijd kost. Bij macro s is dit niet zo, bij subroutines wel: parameters moeten worden doorgegeven, de aanroep- en terugkeer-instructies moeten worden uitgevoerd, en registers moeten worden gered en hersteld. Het is enigszins een kwestie van smaak wanneer een macro of een subroutine moet worden gebruikt, maar als een rij instructies logisch samenhangt en een herkenbaar onderdeel moet zijn van een programma is het aan te bevelen een subroutine te gebruiken. 2.3.2 Macro s met Parameters Indien slechts identieke rijen assembler-statements als macro-body mogen fungeren, zou de macrofaciliteit niet erg flexibel zijn. Indien b.v. naast bovengenoemde ook het rijtje assembler-statements ADDI R3,0,C@l ADDIS R3,R3,C@h ADDI R4,0,D@l ADDIS R4,R4,D@h LWZ R6,0(R3)