MCS-51
Opis rozszerzeń języka C w kompilatorze SDCC

Alokacja zmiennej pod określonym adresem

Podczas deklaracji zmiennej możliwe jest określenie adresu absolutnego, pod jakim będzie alokowana ta zmienna. Do tego celu wykorzystuje się słowo kluczowe at. Dodatkowo przy takiej deklaracji zmiennej konieczne jest jawne określenie typu pamięci, w jakiej ma być alokowana deklarowana zmienna.

Dla przykładu, poniższa deklaracja

xdata at 0xF800 unsigned char licznik8b;

spowoduje alokację zmiennej o nazwie licznik8b w zewnętrznej pamięci danych pod adresem 0xF800. Natomiast następująca deklaracja

xdata at 0xF800 unsigned int licznik16b;

spowoduje alokację zmiennej o nazwie licznik16b w zewnętrznej pamięci danych pod adresem 0xF800 (młodszy bajt) i 0xF801 (starszy bajt).

Przy tego typu deklaracjach kompilator nie oznacza danych komórek pamięci jako zarezerwowanych. Dlatego w takim wypadku to programista powinien sprawdzić, czy tego typu deklaracja nie wystąpi pod adresem, pod którym kompilator zaalokował zmienną zadeklarowaną bez określenia adresu bezwzględnego. Sprawdzenia tego można dokonać zaglądając do plików z rozszerzeniami *.lst, *.rst i *.map.

Alokacja zmiennej pod określonym adresem możliwa jest w każdym typie pamięci, również w obszarze adresowanycm bitowo, np.

bit at 0x03 znacznik;

W powyższym przkładzie zmienna bitowa znacznik zostanie alokowana pod adresem 0x03 w przestrzeni pamięci adresowanej bitowo.

Oczywiście alokacja wszystkich zmiennych w ten sposób nie ma sensu, gdyż stwarza więcej problemów niż wygód. Przedstawiony sposób alokacji zmiennych pod określonym adresem jest jednak bardzo pomocny np. przy pisaniu kodu realizującego obsługę urządzeń peryferyjnych w stosunku do mikrokontrolera, niezależnie od sposobu ich podłączenia (ang. hardware portable code).

Rozpatrzmy następujący przykład zamieszczony w dokumentacji kompilatora SDCC. Załóżmy, że pewien układ do komunikacji z mikrokontrolerem wymaga trzech linii. W zależności od projektanta systemu mikroprocesorowego układ ten może zostać podłączony pod różne linie portów mikrokontrolera. Zatem w pisanym programie linie te mogą zostać zadeklarowane w następujący sposób

extern volatile bit MOSI; /* master out, slave in */
extern volatile bit MISO; /* master in, slave out */
extern volatile bit MCLK; /* master clock */

Natomiast sama funkcja obsługująca taki 3-przewodowy interfejs szeregowej transmisji danych może mieć następującą postać

unsigned char spi_io(unsigned char out_byte)
{
  unsigned char i=8;

  do
  {
    MOSI = out_byte & 0x80;
    out_byte <<= 1;
    MCLK = 1;
    if(MISO)
      out_byte += 1;
    MCLK = 0;
  }
  while(--i);

  return out_byte;
}

Zakładając, że powyższa funkcja została skompilowana do postaci bilbioteki i dołączona do nowo tworzonego programu, jej wywołanie wymaga tylko definicji linii, pod które został rzeczywiście podłączony fizycznie dany układ, tj. na przykład

bit at 0x80 MOSI; /* I/O port 0, bit 0 */
bit at 0x81 MISO; /* I/O port 0, bit 1 */
bit at 0x82 MCLK; /* I/O port 0, bit 2 */

W innym przypadku mogą być to inne linie i wtedy deklaracje odpowiednich bitów będą inne, np.

bit at 0x83 MOSI; /* I/O port 0, bit 3 */
bit at 0x91 MISO; /* I/O port 1, bit 1 */
bit at 0x92 MCLK; /* I/O port 1, bit 2 */

Może się również zdarzyć, że w systemie mikroprocesorowym występuja dwa takie same urządzenia. Jeśli obsługę każdego z nich umieścimy w osobnych modułach, które będa niezależnie kompilowane a dopiero później linkowane, to w każdym z nich możemy zadeklarować inne adresy linii, natomiast do obsługi tych urządzeń użyć tej samej funkcji bibliotecznej. W ten sposób ten sam kod obsługi danego urządzenia bez żadnych zmian może być stosowany w różnych przypadkach.

Przedstawiony sposób deklaracji adresów linii jest bardzo podobny do deklaracji poszczególnych bitów rejestrów specjalnych za pomocą słowa kluczowego sbit (patrz Rejestry specjalne), jednak jego użycie umożliwiłoby tylko jedną deklarację linii o tej samej nazwie w całym projekcie, co uniemożliwiłoby implementację obsługi więcej niż jednego urządzenia przy wykorzystaniu tej samej funkcji.

Więcej na temat dostępu do układów peryferyjnych w systemach mikroprocesorowych znajduje się w punkcie Adresowanie układów we/wy.