---
# System prepended metadata

title: Środowisko uruchomieniowe dla efektów

---

# Ś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 */
}
```