680 likes | 892 Views
Datorteknik övning 4. Maskinnära programmering med C inför hemlab 1. Datorteknik övning 4. CE F1. CE F2. CE Ö1. CE F3. CE Ö2. Assemblerprogram. CE F4. CE Ö3. lab nios2time. C. CE F5. CE Ö4. hemlab C. In- och utmatning. CE F6. CE Ö5. CE Ö6. lab nios2io. Avbrott och "trap".
E N D
Datorteknik övning 4 Maskinnära programmering med C inför hemlab 1 William Sandqvist william@kth.se
Datorteknik övning 4 CE F1 CE F2 CE Ö1 CE F3 CE Ö2 Assemblerprogram CE F4 CE Ö3 lab nios2time C CE F5 CE Ö4 hemlab C In- och utmatning CE F6 CE Ö5 CE Ö6 lab nios2io Avbrott och "trap" CE F7 CE Ö7 lab nios2int Cacheminnen CE F8 CE Ö8 hemlab cache Trådar, synkronisering CE F9 CE Ö9 hemlab trådar CE F10 CE Ö10 tentamen William Sandqvist william@kth.se
Formaterad utskrift printf() %d utskrift av heltalsvariabel %x utskrift av heltalsvariabel men hexadecimalt %f utskrift av flyttalsvariabel \n nyradstecken \t tabulatortecken För att använda printf() #include <stdio.h> William Sandqvist william@kth.se
Hur många Byte är en int? det får man veta genom att köra ett testprogram på sin dator: #include <stdio.h> int main(void) { printf("typ\t\tbytes\n"); printf("signed char \t%d\n", sizeof(char)); printf("unsigned char \t%d\n", sizeof(unsignedchar)); printf("signed int \t%d\n", sizeof(int)); printf("unsigned int \t%d\n", sizeof(unsignedint)); printf("long \t\t%d\n", sizeof(long int)); printf("float \t\t%d\n", sizeof(float)); printf("double \t\t%d\n", sizeof(double)); system("PAUSE"); return 0; } William Sandqvist william@kth.se
4.1 Endianess • Förklara skillnaden mellan big-endian och little-endian. • Vilket använder Nios-II? William Sandqvist william@kth.se
Endianess Datorminnen är Byteorienterade medan de flesta datatyperna består av flera Bytes. Antingen lagras den mest signifikanta Byten först (Big-endian) eller så lagras den minst signifikanta Byten först (Little-endian). Macintosh (Motorola), Sparc (Sun) … PC (Intel), VAX (Dec) … Bi-endian: Itanium (Intel)PA-RISC (HP) … William Sandqvist william@kth.se
J Swift endianess Termen ”endianess” kommer från Jonathan Swift’s Gullivers resor. De två kungadömena Lilleput och Blefuscu är i krig med varandra över en dispyt om vilken ända av frukostägget man ska knäcka! Lika välgrundade skäl ligger ofta bakom datateknikens olika ”standards” i vår tid … Internets överföringsstandard är big-endian som därför också kallas för Network order. (char*)&a adressen till a ändras från en ”int”-adress till en ”char”-adress. /* Endianess test */ #include <stdio.h> int main(void) { int a = 1; if( *( (char*)&a ) == 1) printf("Little Endian\n");else printf("Big Endian\n"); system("PAUSE"); return0; } William Sandqvist william@kth.se
Nios II ? Nios II är Little endian ( men eftersom det är en softprocessor så kan endianessen lätt konfigureras om – dvs. den är bi-endian ). William Sandqvist william@kth.se
Vi skall skicka 16 miljoner kronor över internet: 00000001 00000000 00000000 00000000 = 16.777.216 kr men om endianessen missförstås: 00000000 00000000 00000000 00000001 = 1 kr När har endianess betydelse? Endianessen har betydelse när två datorer kommunicerar med varandra, vid olika endianess måste en av datorerna kasta om Byte-ordningen ( vem är frivillig? ). Otur! William Sandqvist william@kth.se
4.3 Adressrymd • Rita en figur som illustrerar hur minnesrymden disponeras i Nios-II. Figuren skall visa områden för programkod (.text), oinitialiserade data (.bss), initialiserade data (.data), samt heap och stack. • Förklara begreppen oinitialiserade data, initialiserade data, heap och stack. William Sandqvist william@kth.se
C:s minnesmodell William Sandqvist william@kth.se
C:s minnemodell – var hamnar variabler? #include <stdio.h> int a = 17; int main(void) { int b = 18; int * c; c = malloc( sizeof( int ) ); *c = 19; printf("a = %d at address %x\n", a, &a); printf("b = %d at address %x\n", b, &b); printf("c = %d at address %x\n", *c, c); free(c); system("PAUSE"); return0; } Global variabel = Data segmentet Automatisk variabel = Stack Dynamisk variabel = Heap Återställ dynamiskt minne! William Sandqvist william@kth.se
Data-segmentet ”Globala” variabler, dvs variabler som deklarerats utanför programmets funktioner hamnar i datasegmentet. Initialiserade variabler förses med sina startvärden. Oinitialiserade variabler placeras för sig (.bss), de kan då enkelt åtminstone 0-ställas. I datasegmentet kan också konstanter och strängkonstanter lagras. Datasegmentet är statiskt, variablerna upptar plats under hela programkörningen både när de används och när de inte används. Nackdelen med de globala variablerna är att de kan nås från alla funktioner. De kan vara både parametrar och returvärden på en och samma gång för alla funktioner som använder dem – på ett oöverskådligt sätt – som gjort för problem! William Sandqvist william@kth.se
main()aktivitetspost Stacken När en funktion anropas så skapas en aktivitetspost på stacken. Den består av utrymme för funktionens parametrar och returvärde, samt de variabler som deklareras inuti funktionen ( = lokala variabler ). Nios-II är en registerrik processor och då ligger en del parametrar (upp till 4 st) och returvärdet i register. När en funktion avslutas så tas aktivitetsposten bort från stacken. Funktionen main() är speciell genom att den avslutas först när hela programmet kört klart. Dess aktivitetspost ligger kvar under programkörningen. William Sandqvist william@kth.se
funk2() Automatiska variabler main() anropar funktionen funk1(). En ny aktivitetspost skapas nu på stacken för den funktionens variabler. När funktionen kört klart returnerar den till main() och aktivitetposten tas bort. Nu anropas en annan funktion funk2(). Den funktionens aktivitetspost kan nu utnyttja samma minne på stacken. main()aktivitetspost Funktionernas variabler på stacken kallas för automatiskavariabler. Minnesutrymmet på stacken återanvänds av funktionerna och utnyttjas därmed effektivt. William Sandqvist william@kth.se
array[] funk3() array[] En funktion som genererar data Antag att funktionen funk2() genererar data och lagrar dessai en lokal array[] på stacken. Funktionen returnerar arrayens adress till main(), med hjälp av adressen kan main()nå ochanvända datat. Datat på stacken lever farligt! Om en ny funktion, funk3(), körs så skrivs datat över. Det behövs någon annan plats för datat. – Var skulle det kunna vara? William Sandqvist william@kth.se
array[] free() funk2() funk3() Heapen Funktionen funk2() behöver lagra sina data någon annanstans. Den kan anropa C:s funktion malloc() för att få utrymme på heapen. funk2() returnerar nu arrayens adress till main(). Datat finns fortfarande kvar om en ny funktion funk3() ska köra. Minnesutrymmet frigörs när man anropar funktionen free(). Om C:s minneshanterare är bra skriven utnyttjas minnesutrymmet på heapen effektivt. William Sandqvist william@kth.se
18 18 18 19 pekare * adress & och avreferering * Kort om C-språkets pekare (repris) int b; Deklaration av heltalsvariabeln b. Plats reserveras. b = 18; Definition av variabeln b. Nu innehåller den talet 18. &b Adressoperatorn. Adressen till variabeln b. int * c; Deklaration av int-pekarvariabeln c. c = &b; Nu pekar c på b. *c Avrefereringsoperatorn. Det som c pekar på. *c = 19; Talet 19 lagras på den plats som c pekar ut. Nu innehåller b = 19. ( ) ( ) ( ) William Sandqvist william@kth.se
4.4 Värden, pekare, referenser, heltalsvariabler Skriv programrader i C som visar hur * och & används i nedanståendeexempel. Översätt varje exempel för hand till Nios-II-assembler. a) deklarera två globala heltalsvariabler a och b.b) initialisera b till värdet 27 i samband med deklarationen.c) deklarera två globala pekarvariabler ptr1 och ptr2 som kan peka på a och b.d) tilldela a värdet 4711 och b värdet 47.e) tilldela pekarvariabeln ptr1 ett värde så att den pekar på heltalsvariabeln a.f) tilldela heltalsvariabeln b samma värde som som a, med hjälp av ptr1.g) tilldela pekarvariabeln ptr2 ett värde så att den pekar på samma variabel som ptr1. William Sandqvist william@kth.se
minne a) b) c) a) deklarera två globala heltalsvariabler a och b. b) initialisera b till värdet 27 i samband med deklarationen. c) deklarera två globala pekarvariabler ptr1 och ptr2. .data.align 2int a, b = 27; a: word 0 b: word 27int * ptr1, * ptr2; ptr1: word 0 ptr2: word 0 .data.align 2int a, b; a: word 0 b: word 0 .data.align 2int a, b = 27; a: word 0 b: word 27 William Sandqvist william@kth.se
a = 4711;b = 47; d) tilldela a värdet 4711 och b värdet 47. .text.align 2movia r8,a # R8 = &amovi r9,4711 # R9 = 4711 stw r9,0(r8) # global a = 4711movia r8,b # R8 = &bmovi r9,47 # R9 = 47stw r9,0(r8) # global b = 47 William Sandqvist william@kth.se
ptr1 = &a; e) tilldela pekarvariabeln ptr1 ett värde så att den pekar på heltalsvariabeln a. movia r8,ptr1 # R8 = &ptr1movia r9,a # R9 = &astw r9,0(r8) # global ptr1 = &a William Sandqvist william@kth.se
b = *ptr1; f) tilldela heltalsvariabeln b samma värde som som a, med hjälp av ptr1. movia r8,ptr1 # R8 = &ptr1movia r9,b # R9 = &bldw r10,0(r8) # R10 = ptr1 ; &aldw r11,0(r10) # R11 = a ; 4711 stw r11,0(r9) # global b = *ptr1 ; 4711 William Sandqvist william@kth.se
ptr2 = ptr1; g) tilldela pekarvariabeln ptr2 ett värde så att den pekar på samma variabel som ptr1. movia r8,ptr1 # R8 = &ptr1ldw r9,0(r8) # R9 = ptr1 movia r8,ptr2 # R8 = &ptr2stw r9,0(r8) # global ptr2 = ptr1 William Sandqvist william@kth.se
4.5Värden, pekare, referenser, char-variabler Skriv programrader i C som visar hur * och & används i nedanståendeexempel. Översätt varje exempel för hand till Nios-II-assembler. a) deklarera två globala variabler av typen char. c och d.b) initialisera d till värdet 65 i samband med deklarationen.c) deklarera två globala pekarvariabler ptr3 och ptr4 som kan peka på c och d.d) tilldela c värdet ’M’ och d värdet 97.e) tilldela pekarvariabeln ptr3 ett värde så att den pekar på variabeln c.f) tilldela variabeln d samma värde som c, med hjälp av ptr3.g) tilldela pekarvariabeln ptr4 ett värde så att den pekar på samma variabel som ptr3.Vilka skillnader i Nios-II-assemblerkoden blir det jämfört med motsvarande operationer med heltalsvariabler? William Sandqvist william@kth.se
a) b) c) a) deklarera två globala variabler av typen char. c och d. b) initialisera d till värdet 65 i samband med deklarationen. c) deklarera två globala pekarvariabler ptr3 och ptr4. .data.align 2char c, d = 65; c: byte 0 d: byte 65char * ptr3, * ptr4; ptr3: word 0 ptr4: word 0 .data.align 2int a, b; a: word 0 b: word 0 .data.align 2int a, b = 27; a: word 0 b: word 27 William Sandqvist william@kth.se
d) tilldela c värdet ’M’ och d värdet 97. c = ’M’;d = 97; .text.align 2movia r8,c # R8 = &cmovi r9,’M’ # R9 = ’M’ stb r9,0(r8) # global c = ’M’movia r8,d # R8 = &dmovi r9,97 # R9 = 97stb r9,0(r8) # global d = 97 William Sandqvist william@kth.se
e) tilldela pekarvariabeln ptr3 ett värde så att den pekar på variabeln c. ptr3 = &c; movia r8,ptr3 # R8 = &ptr3movia r9,c # R9 = &cstw r9,0(r8) # global ptr3 = &c Ingen större skillnad! William Sandqvist william@kth.se
f) tilldela variabeln d samma värde som som c, med hjälp av ptr3. d = *ptr3; movia r8,ptr3 # R8 = &ptr3movia r9,d # R9 = &dldw r10,0(r8) # R10 = ptr3 ; &cldb r11,0(r10) # R11 = c ; ’M’ stb r11,0(r9) # global d = *ptr3 ; ’M’ William Sandqvist william@kth.se
g) tilldela pekarvariabeln ptr4 ett värde så att den pekar på samma variabel som ptr3. ptr4 = ptr3; movia r8,ptr3 # R8 = &ptr3ldw r9,0(r8) # R9 = ptr3 movia r8,ptr4 # R8 = &ptr4stw r9,0(r8) # global ptr4 = ptr3 Ingen större skillnad! William Sandqvist william@kth.se
Hur stora är pekare? Det får man veta genom att köra ett testprogram på sin dator: #include <stdio.h> int main(void) { printf("typ\tbytes\n"); printf("char pekare \t%d\n", sizeof(char *)); printf("int pekare \t%d\n", sizeof(int *)); printf("float pekare \t%d\n", sizeof(float *)); printf("double pekare \t%d\n", sizeof(double *)); printf("void pekare \t%d\n", sizeof(void *)); system("PAUSE"); return0; } William Sandqvist william@kth.se
Vad är en void-pekare? char, int, float, double – pekare tar alla samma plats i minnet. Det som skiljer är hur stort adressteg som tas om man tex. ökar pekaren med 1. Den pekar då ut nästa av sin sort i minnet. En void-pekare är en reserverad minnesplats för en godtycklig pekarsort. Innan man använder pekaren måste den ”castas” (omvandlas) till önskad sort. Genom att använda void-pekare kan man skriva generella funktioner. Tex. kan funktionen Quicksort sortera ”vad som helst” genom att den är skriven för voidpekare. Typecast sker genom att man skriver den önskade data-typens namn i en parentes framför variabeln. float a; (int) a blir tillfälligt int (dvs bara heltals-delen räknas)! William Sandqvist william@kth.se
4.6 Funktionen memcpy() C:s biblioteksrutin memcpy() har följande deklaration: void * memcpy(void * dest, constvoid * src, size_t len); Ett funktionsanrop kan se ut så här: int a[4711][4711], b[4711][4711]; . . . (void *) memcpy( &b[0][0], &a[0][0], sizeof( a) ); a) Förklara vad en pointer till void är för något. b) Förklara de tre parametrarna till memcpy(). c) Förklara returvärdet från memcpy(). d) Förklara operatorn sizeof(). e) Förklara texten (void) vid anropet av memcpy(). f) Översätt funktionen memcpy() till Nios-II-assembler. William Sandqvist william@kth.se
a:s totala storlek beräknas av sizeof() operatorn vid kompileringen. Datatypen size_t är den datatyp som sizeof() svarar med. Eftersom memcpy() skall returnera en voidpekare så ”castar” vi returvärdet till en sådan. (Man kan strunta i returvärdet). b:s och a:s startadresser. Deklaration: void * memcpy(void * dest, constvoid * src, size_t len); Ett funktionsanrop kan se ut så här: int a[4711][4711], int b[4711][4711]; . . . (void *) memcpy( &b[0][0], &a[0][0], sizeof( a) ); memcpy() brukar returnera det ursprungliga dest –värdet (?) William Sandqvist william@kth.se
memcpy() assemblerprogram memcopy: mov r8, r4 # src-pekare mov r9, r5 # dest-pekare mov r10, r6 # counter loop: ble r10, r0, out # kolla om klart fast: ldb r12, 0(r8) # läs en byte addi r8, r8, 1 # öka scr-pekare stb r12, 0(r9) # skriv en byte addi r9, r9, 1 # öka dest-pekare subi r10, r10, 1 # minska counter br loop # alt: bgt r10, r0, fast out: ret # vi struntar i returvärde ... memcpy() kopierar byte för byte. Om man vet att arrayerna är word-aligned så kan man kopiera word för word – det går fyra gånger snabbare! William Sandqvist william@kth.se
Typecast av pekare Vi kan kopiera 16 bokstäver som 4 int – detta går snabbare ( om datorn har 32 bitars buss ). #include <stdio.h>int main(void){ char text[16]="Hello my world!"; char array[16]; char * ptr1 = text; char * ptr2 = array; int i; for( i=0;i<4;i++) { *( ((int*)ptr2)++ ) = *( ((int*)ptr1)++ ); } printf("%s\n",array); system("PAUSE"); return 0;} Pekarna typecastas till int*, och då ökar steget från en Byte till 4 Byte! William Sandqvist william@kth.se
4.7 Matriser • Visa med en tydlig figur hur de 15 elementen i nedanstående matriser lagras i minnet vid körning av C-program. • int a[3][5]; • b) int b[5][3]; William Sandqvist william@kth.se
35 matris matematik-index a[0][0]a[0][1]a[0][2]a[0][3]a[0][4]a[1][0]a[1][1]a[1][2]a[1][3]a[1][4]a[2][0]a[2][1]a[2][2]a[2][3]a[2][4] 35 matris C-index I minnet radvis. William Sandqvist william@kth.se
53 matris matematik-index b[0][0]b[0][1]b[0][2]b[1][0]b[1][1]b[1][2]b[2][0]b[2][1]b[2][2]b[3][0]b[3][1]b[3][2]b[4][0]b[4][1]b[4][2] 53 matris C-index I minnet radvis. William Sandqvist william@kth.se
4.8 C-version av puttime() void puttime( int * timeloc ); void puttime( int * timeloc ){ register int time = *timeloc; putchar(’\n’); putchar(’\r’); putchar( hexasc((time & 0xf000)>>12) ); putchar( hexasc((time & 0xf00)>>8) ); putchar(’:’); putchar( hexasc((time & 0xf0)>>4) ); putchar( hexasc(time & 0xf) ); } William Sandqvist william@kth.se
4.9 Struct Skriv en C-kod som definierar lagring av en struct (datapost) tiger med de ingående delarna: namn, 13 tecken. Tex. ”tiger” (se nalle Puh). vikt, avrundat till hela kilogram. Tex 245 (som int). fart, snabaste i km/tim (som en float). langd, längd överallt, angiven i meter (som en float). William Sandqvist william@kth.se
struct tiger struct tiger{char namn[13];int vikt;float fart;float langd;} ; (13+3) + 4 + 4 + 4 = 28 int och float är word-aligned. sizeof( struct tiger); William Sandqvist william@kth.se
4.9 Struct Skriv en C-kod som definierar lagring av en struct (datapost) elephant med de ingående delarna: namn, 13 tecken. Tex. ”Dumbo” (se Disney). vikt, avrundat till hela kilogram. Tex 1243 (som int). oron, öronyta uppdelat på vänster och höger öra angivet i kvadratmeter (som två float) William Sandqvist william@kth.se
struct elephant struct oron{float left;float right;} ; struct elephant{char namn[13];int vikt;struct oron oronarea;} ; sizeof( struct elephant); (13+3) + 4 + ( 4 + 4 ) = 28int och float är word-aligned. William Sandqvist william@kth.se
Vektor circus[] Skriv C-kod som deklarerar en vektor, circus[], med plats för fyra elefanter. struct elephant circus[4]; William Sandqvist william@kth.se
4.10 Dynamisk minnesallokering Beskriv med ord, eller rita en figur, som illustrerar hur minne kan allokeras, avallokeras och återanvändas vid följande C-operationer: struct elephant * dumbo = malloc( sizeof( struct elephant)); //1 struct tiger * tiger1 = malloc( sizeof( struct tiger)); //2 free( dumbo); //3 struct tiger * tiger2 = malloc( sizeof( struct tiger)); //4 struct tiger * tiger3 = malloc( sizeof( struct tiger)); //5 free( tiger1); //6 William Sandqvist william@kth.se
dumbo tiger3 tiger2 tiger1 struct elephant * dumbo = malloc( sizeof( struct elephant)); //1 struct tiger * tiger1 = malloc( sizeof( struct tiger)); //2 free( dumbo); //3 struct tiger * tiger2 = malloc( sizeof( struct tiger)); //4 struct tiger * tiger3 = malloc( sizeof( struct tiger)); //5 free( tiger1); //6 Det är inte säkert att din malloc() gör precis så här! William Sandqvist william@kth.se
C:s biblioteksfunktioner C:s biblioteksfunktioner är generellt skrivna och använder därför oftast void-pekare som parametrar och för returvärden. Som exempel tar vi funktionen quicksort som storleks-sorterar element i en array för att visa hur man använder C:s biblioteksfunktioner. Sir C.A.R Hoare Turing award William Sandqvist william@kth.se
Sortera element i en array qsort() i stdlib.h Wikipedia Quicksort William Sandqvist william@kth.se