# Środowisko uruchomieniowe dla efektów ## Jak odizolować efekty od biblioteki? Biblioteka ma być częścią loadera dostępną dla wszystkich efektów. Ma udostępniać: * szereg funkcji, które muszą być częścią loadera lub są na tyle często używane, że nie opłaca się ich trzymać w każdym efekcie, * możliwość komunikacji między efektami na wypadek, gdyby efekty chciały przekazywać sobie stan. ## Przygotowanie efektu Po załadowaniu efektu do pamięci _loader_ powinien jakoś dostać wskaźnik na jego `struct Effect`. Wydaje się, że najłatwiej to osiągnąć przygotowując odpowiednie `crt0.S` dla efektu. Innymi słowy zakładamy, że pierwszy segment pliku wykonywalnego efektu to `.text` (kod). `crt0.o` jest zawsze wrzucany na początek tej sekcji co widać w [effect.mk](https://github.com/cahirwpz/demoscene/blob/master/build/effect.mk#L38). Tam można wkleić taki kawałek kodu: ``` move.l #_Effect,d0 rts ``` Co powoduje, że _loader_ po wywołaniu tego kodu uzyska dostęp do funkcji efektu zdefiniowanych w `struct Effect`, na przykład w taki sposób: ```c EFFECT(anim_polygons, Load, UnLoad, Init, Kill, Render); ``` ## Wywołania funkcji _loadera_ IMHO nie ma się co siłować. Biblioteki _AmigaOS_ pokazują jak to łatwo osiągnąć na `m68k`. Przykład poniżej: ```c= extern struct ExecBase *const SysBase; #define EXEC_BASE_NAME SysBase #define LP2(offs, rt, name, t1, v1, r1, t2, v2, r2, bt, bn) \ ({ \ t1 _##name##_v1 = (v1); \ t2 _##name##_v2 = (v2); \ rt _##name##_re2 = \ ({ \ register int _d1 __asm("d1"); \ register int _a0 __asm("a0"); \ register int _a1 __asm("a1"); \ register rt _##name##_re __asm("d0"); \ register void *const _##name##_bn __asm("a6") = (bn); \ register t1 _n1 __asm(#r1) = _##name##_v1; \ register t2 _n2 __asm(#r2) = _##name##_v2; \ __asm volatile ("jsr a6@(-"#offs":W)" \ : "=r" (_##name##_re), "=r" (_d1), "=r" (_a0), "=r" (_a1) \ : "r" (_##name##_bn), "rf"(_n1), "rf"(_n2) \ : "cc", "memory"); \ _##name##_re; \ }); \ _##name##_re2; \ }) #define AllocMem(___byteSize, ___requirements) \ LP2(0xc6, APTR, AllocMem, \ ULONG, ___byteSize, d0, \ ULONG, ___requirements, d1, \ , EXEC_BASE_NAME) ``` Nie ma sensu bawić się z przeniesionym wektorem przerwań – dzięki rejestrowi VBR począwszy od 68010. Wpisy w wektorze od 48 wzwyż są zarezerwowane dla użytkownika, więc można tam upchnąć dowolne dane. W związku z tym `SysBase` można zakodować jako skoki bezpośrednie w wektorze przerwań (po 6 bajtów na każdy). Wtedy `jsr a6@(offset)` zamieni się na `jsr offset.w`. Zamiast `bsr foobar` (18 cykli), będzie para `jsr offset.w; jmp foobar.l` (18 + 12 cykli). Czyli pośrednie skoki wprowadzą trochę opóźnienia, ale nie jest źle. ## Interfejs loadera ```c= struct API { /* memory.h */ void (*MemCheck)(int verbose); u_int (*MemAvail)(u_int attributes); void *(*MemAlloc)(u_int size, u_int attributes); void *(*MemResize)(void *ptr, u_int size); void (*MemFree)(void *ptr); /* interrupt.h */ void SetIntVector(u_short irq, IntHandlerT code, void *data); void AddIntServer(u_short irq, IntServerT *is); void RemIntServer(u_short irq, IntServerT *is); /* file.h */ int FileRead(FileT *f, void *buf, u_int nbyte); int FileWrite(FileT *f, const void *buf, u_int nbyte); int FileSeek(FileT *f, int offset, int whence); void FileClose(FileT *f); void FilePutChar(FileT *f, char c); int FileGetChar(FileT *f); void FilePrintf(FileT *f, const char *fmt, ...); FileT *SerialOpen(u_int baud, u_int flags); /* timer.h */ CIATimerT *AcquireTimer(u_int num); void ReleaseTimer(CIATimerT *timer); void SetupTimer(CIATimerT *timer, CIATimeoutT timeout, u_short delay, u_short flags); void WaitTimerGeneric(CIATimerT *timer, u_short ticks, bool spin); /* debug.h */ void Log(const char *format, ...) __attribute__ ((format (printf, 1, 2))); __noreturn void Panic(const char *format, ...) __attribute__ ((format (printf, 1, 2))); /* keyboard.h */ void (*KeyboardInit)(void); void (*KeyboardKill)(void); /* mouse.h */ void (*MouseInit)(Area2D *area); void (*MouseKill)(void); /* event.h */ void (*PushEventISR)(EventT *event); void (*PushEvent)(EventT *event); bool (*PopEvent)(EventT *event); /* effect.h */ void (*TaskWaitVBlank)(void); void (*_ProfilerStart)(ProfileT *prof); void (*_ProfilerStop)(ProfileT *prof); }; ``` ## Środki synchronizacji Te które mam chyba nie dadzą rady. ### Semafory ```c= typedef struct Waiter { TAILQ_ENTRY() node; TaskT *task; bool up; } WaiterT; typedef struct Semaphore { u_int count; TAILQ_ENTRY(Waiter) waiters; } SemaphoreT; void SemUp(SemaphoreT *sem) { /* wyłączyć przerwania */ if (TAILQ_EMPTY(&sem->waitList)) { sem->count++; } else { SemWaiterT *waiter = TAILQ_FIRST(&sem->waitList); TAILQ_REMOVE(&waiter->list); waiter->up = true; WakeupTask(waiter->task); } /* włączyć przerwania */ } void SemDown(SemaphoreT *sem) { /* wyłączyć przerwania */ if (sem->count > 0) { sem->count--; } else { SemWaiterT waiter; TAILQ_INSERT_TAIL(&sem->waiters, &waiter); waiter.task = current; waiter.up = false; while (!waiter.up) { TaskSuspend(); } } /* włączyć przerwania */ } ```