C-programmering del 4 Ulf Assarsson Läromoment: extern, sta.c, Överkurs: const, inline, Gameloop, grafikloop, fraktalt berg Kopplat.ll: Lab 5 - spelprogrammering
Kombinera C och assembler Anropa assembler-ru.ner från C Anropa C-ru.ner från assembler Kodgenerering sker från olika filer, så ihopkopplingen sker i länkningen..o filer Parameteröverföring och returvärden Se Rogers föreläsning MOP-Cortex_M4_assemblerprogrammering_3 2
GCC genererar assembler från C Från C.ll assembler Från assembler.ll maskinkod Symbolerna i assemblern är samma som i C för ARMs gcc. Annars vanligt med eu understreck: myadd() _myadd: i C i assembler T ex för au undvika reserverade ord i assemblerspråket Nu: import / extern Synlighet assembler / C 3
Bonus Import / Export IMPORT i assembler säger au symbolen kommer från en annan fil. IMPORT myfunc... BL myfunc Symbol från annan fil ( t ex.c-fil) Börja exekvera funk.onen myfunc: EXPORT myfunc BX LR Exportera symbolen så den kan importeras av annan fil ( t ex.c-fil eller assemblerfil) I den här kursen använder vi dock asm () för au inline:a assembler direkt i en c-fil. 4
Extern extern i C säger au symbolen kommer från en annan fil. // main.c #include <stdio.h> extern int g_var; void myfunc(); void main() myfunc(); g_var definerad senare t ex i annan.c-fil extern ej nödvändig för funk.oner, prototypen säger: finns vid länkning. // myfunc.c int g_var; void myfunc() printf("hej"); ; Vanligast är dock au ta hjälp av.h-fil 5
Extern Poäng: Om ni kodar eu större program för lab5 så vill ni nog inte ha alla globala objekt/ variabler deklarerade i main-filen. Då måste ni använda extern. extern i C säger au symbolen kommer från en annan fil. // main.c #include "fractalmountain.h POBJECT objects[] = &player, &fmountain; unsigned int nobjects = 2; void main() // fractalmountain.h #ifndef FRACTALMOUNTAIN_H #define FRACTALMOUNTAIN_H #include "object.h" #include "types.h" void somefunc1(); void somefunc2(); extern OBJECT fmountain; #endif //FRACTALMOUNTAIN_H // fractalmountain.c #include fractalmountain.h" OBJECT fmountain = 0, // geometri - ingen 0,0, // riktningsvektor 0,0, // initial startposition drawmountain, // draw method 0, // clear_object unused movemountain, // move method set_object_speed // set-speed method ; extern = declare without defining Utan extern skulle vi skapa en ny variabel. 6
Synlighet av symboler Alla symboler synliga per-default i C för länkaren. Inga symboler synliga per-default i assembler: Gör symboler (i.o filen) synliga med EXPORT Gör symboler från andra filer synliga med IMPORT 7
sta.c static i C kan göra två saker 1. Ta bort synlighet (för andra filer) av symboler. 2. Allokera minne för lokala variabler som om de vore globala variabler (men forfarande med lokal synlighet) istället för på stacken. 8
Sta.c variant 1 static int var; Variabeln var kan ej ses från andra filer, oavseu om man använder extern eller IMPORT. 9
Sta.c variant 2 #include <stdio.h> void testfkt() int var1 = 0; static int var2 = 0; var1++; var2++; printf("var1: %i, var2: %i \n", var1, var2); int main() testfkt(); testfkt(); testfkt(); Utskrij: var1: 1, var2: 1 var1: 1, var2: 2 var1: 1, var2: 3 var2 ini.aliseras.ll noll endast första gången vi anropar funk.onen, men behåller sedan siu värde mellan anrop. var2 är global variabel som endast har lokal synlighet inom siu scope dvs här för testfkt(). 10
Sta.c variant 2 vanligt exempel #include <stdio.h> void testfkt() static int bfirsttime = 1; if(bfirsttime) // T ex allokera minne på heapen // eller förberäkna något. bfirsttime = 0; Gör huvudberäkningarna. int main() testfkt(); T ex: rita gubbe, skriv ut resultat i eu fönster, gör avancerad AI-simulering, skicka data över nätverk. 11
Bonus Några fler qualifiers const inline sta.c extern vola.le (restrict) const double pi = 3.14159265359; // variabeln kan inte ändras x = 90 * pi / 180; // kan förberäknas vid kompileringen // pekaren får nu inte ändra värde till annan adress volatile unsigned char * const inport = (unsigned char*) 0x40021010; static inline int square(int x) return x * x; int main() int a = square(5); 12
Inline och h-filer Bonus Foo kan inte inline:as såhär (< gcc 2010) // main.c #include <stdio.h> #include "foo.h" int main() printf("x is %i", foo(0)); return 0; // foo.h inline int foo(int x); // foo.c #include <stdlib.h> inline int foo(int x) if( x == 0 ) int x = 4; return x; return x; c-fil Inkluderar header-fil header-fil Innehåller funk.onsprototyper c-fil För main.c så syns inte defini.onen av foo() utan bara deklara.onen. Exemplet leder i själva verket oja.ll kompileringsfel. 13
Inline och h-filer Bonus Nu kan foo inline:as // main.c #include <stdio.h> #include "foo.h" int main() printf("x is %i", foo(0)); return 0; // foo.h static inline int foo(int x) if( x == 0 ) int x = 4; return x; return x; c-fil Inkluderar header-fil header-fil För main.c syns nu hela defini.onen av foo() och kan inline:as J. 14
Inline och h-filer Bonus // vecmath.h #ifndef VECMATH_H #define VECMATH_H typedef struct union float v[2]; struct float x,y; ; ; Vec2f; typedef struct Vec2f min; Vec2f max; Box2f; // box inline innebär au man ber kompilatorn au stoppa in koden direkt i anropande funk.on istället för au göra eu funk.onsanrop. Då slipper man overhead för branching och ak.veringspost. För au kunna göra deua får ej inline-funk.onen ligga i en annan.c-fil (ejersom den aldrig syns vid kompileringen utan först vid länkningen. Vissa kompilatorer klarar dock inline även vid länkningssteget. sta.c inline Box2f Add(Box2f * box, Vec2f v) Box2f res; res.min.x = box->min.x + v.x; res.min.y = box->min.y + v.y; res.max.x = box->max.x + v.x; res.max.y = box->max.y + v.y; return res; #endif //VECMATH_H // Adds vector v to min and max of box Vissa kompilatorer kräver sta.c framför inline, ty om inline misslyckas så kan funk.onen Add() bli globalt definierad (dvs får skapad kod som är globalt synlig vid länknigen) för varje.cfil som inkluderar vecmath.h, om det inte stod sta.c. Sta.c betyder au Add() bara får.c-fillokal synlighet. Utan sta.c genereras länkfelet mul.ple defini.on om vecmath.h inkluderas av flera.c-filer. 15
Typer läses läuast från höger -> vänster #define find and replace på textnivå. Typedef funkar inte på samma säu (men oja ingen prak.sk skillnad) typedef int postnr; typedef int gatunummer; postnr x = 41501; Gatunummer y = 3; // Observera au x och y nu har samma typ så man kan skriva x = y; Angående typer: Läs från höger ;ll vänster: (const finns sedan C89/90) unsigned char * const inport = (unsigned char*) 0x400; // inport är en constant pekare.ll en unsigned char unsigned char const * inport2 = (unsigned char*) 0x400; // inport är en pekare.ll en constant unsigned char const unsigned char * inport2 = (unsigned char*) 0x400; // inport är en pekare.ll en constant unsigned char char unsigned const * inport2 = (unsigned char*) 0x400; // inport är en pekare.ll en constant unsigned char char unsigned const * const inport3 = (unsigned char*) 0x400; // inport är en constant pekare.ll en constant unsigned // char MEN (som exempel på au typedef ej motsvarar find and replace) typedef unsigned char* port8ptr; const port8ptr p = (port8ptr)0x400; const unsigned char* p2 = (port8ptr)0x400; p2=0; // OK för p2 är pekare.ll const unsigned char p = 0; // ej OK för p är const-pekare.ll unsigned char port8ptr const p3 är samma som const port8ptr p3 Bonus 16
EU enkelt spel 17
Game loop En typisk game loop. int main(int argc, char **argv) appinit(); // initialize GPIO_E graphic_initalize(); // initialize the lcd-display graphic_clearscreen(); clearbuffers(); // clear front/backbuffer bool gameover = false; while(!gameover ) clearbuffer(0); collisiondetection(); // sets flags on the objects updateplayer(); // user input updateobjects(); // moves and/or kills objects based on the flags drawobjects(); // draws the objects (based on the flags) swapbuffers(); // draw backbuffer and swap back/frontbuffer. delay_milli(40); // ~25 frames/sec 18
Game loop POBJECT objects[] = &player, osv ; unsigned int nobjects = 1; void collisiondetection() ; // insert your own code void updateobjects() for(int i=0; i<nobjects; i++) objects[i]->move(objects[i]); void drawobjects() for(int i=0; i<nobjects; i++) objects[i]->draw(objects[i]); sta.c OBJECT player = &ball_g, // geometri för en boll 0,0, // ini.ala riktningskoordinater 1,1, // ini.al startposi.on draw_object, clear_object, move_object, set_object_speed ; // funk.onspekare void updateplayer() switch( tstchar() ) // tstchar() checkar input via USART-porten case '6': player.set_speed( &player, 2, 0); break; case '4': player.set_speed( &player, -2, 0); break; case '8': player.set_speed( &player, 0, -2); break; case '2': player.set_speed( &player, 0, 2); break; 19
Game loop En realis.sk grafikloop. int main(int argc, char **argv) appinit(); graphic_initalize(); graphic_clearscreen(); clearbuffers(); bool gameover = false; unsigned char framebuffer0[1024], framebuffer1[1024]; unsigned char *frontbuffer = framebuffer0; unsigned char *backbuffer = framebuffer1; void clearbuffer(unsigned char val) for (int i=0; i<1024; i++) backbuffer[i] = val; void clearbuffers() for (int i=0; i<1024; i++) backbuffer[i] = frontbuffer[i] = 0; while(!gameover ) clearbuffer(0); collisiondetection(); updateplayer(); updateobjects(); drawobjects(); swapbuffers(); delay_milli(40); void swapbuffers() graphic_drawscreen(); unsigned char* tmp = frontbuffer; // swap front/backbuffers frontbuffer = backbuffer; backbuffer = tmp; 20
Game loop En realis.sk grafikloop. void graphic_drawscreen(void) unsigned int k = 0; bool bupdateaddr = true; for(uint8 c=0; c<2; c++) // loop over both controllers (the two displays) uint8 controller = (c == 0)? B_CS1 : B_CS2; for(uint8 j = 0; j < 8; j++) // loop over pages graphic_writecommand(lcd_set_page j, controller ); graphic_writecommand(lcd_set_add 0, controller); for(uint8 i = 0; i <= 63; i++, k++) // loop over addresses // update display only where it is different from last frame if( backbuffer[k]!= frontbuffer[k] ) if(bupdateaddr ) graphic_writecommand(lcd_set_add i, controller); graphic_writedata(backbuffer[k], controller); bupdateaddr = false; // Display hardware auto-increments the address per write else bupdateaddr = true; // No write -> we need to update the x-address next write 21
Game loop Vi får då även uppdatera pixel(). void pixel( int x, int y, int set ) if (!set) return; if( (x > 127 ) (x < 0) (y > 63) (y < 0) ) return; unsigned char mask = 1 << (y%8); int index = 0; if(x>=64) x -= 64; index = 512; index += x + (y/8)*64; backbuffer[index] = mask; switch( y%8 ) case 0: mask = 1; break; case 1: mask = 2; break; case 2: mask = 4; break; case 3: mask = 8; break; case 4: mask = 0x10; break; case 5: mask = 0x20; break; case 6: mask = 0x40; break; case 7: mask = 0x80; break; 22
Fractal mountains POBJECT objects[] = &player, &fmountain; unsigned int nobjects = 2; sta.c OBJECT fmountain = 0, // geometri behöver vi ej 0,0, // ini.ala riktningskoordinater 0,0, // ini.al startposi.on drawmountain, // funk.onspekare 0, // unused move_object, // dummy set_object_speed // dummy ; sta.c OBJECT player = &ball_g, // geometri för en boll 0,0, // ini.ala riktningskoordinater 1,1, // ini.al startposi.on draw_object, // funk.onspekare clear_object, move_object, set_object_speed ; 23
Fractal mountains static unsigned char y_values[128]; void drawmountain() static bool bfirsttime = true; if(bfirsttime) bfirsttime = false; for(uint8 x=0; x<128; x++) y_values[x] = fractal(); else // shift mountain to the left for(uint8 x=0; x<127; x++) y_values[x] = y_values[x+1]; y_values[127] = fractal(); // anropa pixel för alla x=[0,127] for(uint8 x=0; x<127; x++) pixel(x, y_values[x], 1); 24
Fractal mountains #define RAND_MAX 32767 static unsigned int next = 1; static unsigned int rand(void) next = next * 1103515245 + 12345; return (unsigned int)(next/65536) % 32768; static void srand(unsigned int seed) next = seed; 25
Fractal mountains static unsigned char fractal() // f = noise(t)*a + noise(0.5t)*2a + noise(0.25t)*4a... static int noise[] = 0,0,0; // room for 3 octaves static int t = 0; static int f = RAND_MAX / 2; const int half_max = RAND_MAX/2; noise[0] = (rand() > half_max)? 1 : -1; // update each time if( (t%2) == 1) noise[1] = (rand() > half_max)? 1 : -1; // update every 2 nd time if( (t%4) == 3) noise[2] = (rand() > half_max)? 1 : -1; // update every 4 th time // sum octaves int val = 0; for(int i=0; i<3; i++) val += noise[i]; f += val; // update our static non-bounced fractal function t = (t+1) % 4; val = f % 64; // return a bounced value between 32 + [0-32] val = (val > 31)? 63-val : val; return val + 32; 26
För au ni ska kunna läsa kod C luriga uuryck (överkurs) for (expr/dekl; expr; expr) statement; if( expr ) Godtyckliga expressions. Examples of statements: if, while, do/while, for, switch, expr Deklara.on av lokala variabler: int a, b; Examples of expressions: x = y + 3; x++; x = y = 0; // Both x and y are initialized to 0 proc( arg1, arg2 );// Function call y = z = f(x) + 3; // A function-call expression expr, expr; // list of expressions. Conditional value // of the expression is the result of the // last expression. 27
För au ni ska kunna läsa kod C luriga uuryck (överkurs) Godtyckligt expression/dekl. Dock typiskt en vanlig deklara.on och ini.ering av loopvariabeln. for(expr/dekl; expr; expr) statement; for (int a=1, b=0; b<5, a++, f(a), a<3; b++, a += (b>a)? 1 : -1) a++, b++; Godtyckligt expression. OBS vilkorets värde endast lika med sista uurycket a<3 (så b<5 har här ingen effekt). if(a=0, b++, a = (b == c) ) Gör inte såhär ejersom det blir svårläst kod! 28
För au ni ska kunna läsa kod C luriga uuryck (överkurs) EU.ll knäppt exempel void f(int b, int c) printf("%d, %d", b, c); void main() int a = 0; f( (++a, ++a, ++a), (++a, ++a)); Expr för 1:a parametern. Vad skrivs ut? Svar: 3, 5 Kommaseparerade expressions evalueras vä -> hö. Så även för inputparametrar hos de flesta C-kompilatorer (men är ospecificerat). Värdet för en lista av expr är värdet av sista uurycket. Expr för 2:a parametern. Värde = värdet hos sista uurycken (++a). Gör inte såhär ejersom det blir svårläst kod! 29
EU spel Exempel på lite större projekt Output i konsolen Denna version kör enbart på Windows + CodeLite men hyfsat läu au porta. Utan externa APIs Objektorienterad s.l Objekt: Anima.on, Object, Enemy, Player, Text h-filerna visar strukturen hup://www.cse.chalmers.se/~uffe/mop/game.zip 30