Typy zmiennych / stałych
Typ danych zmiennej / typ stałej
(1) Podaj typy danych BYTE, WORD, EWORD, DWORD i inne.
(2). Wszystkie typy danych są liczbami bez znaku.
(3). BYTE jest 8-BITOWYM TYPEM DANYCH, WORD jest 16-BITOWY,
EWORD jest 24-BITOWY, a DWORD jest 32-BITOWY.
(4). INT i BYTE to takie same 8-bitowe typy danych bez znaku, co zapewnia zgodność z przyszłymi normami.
Case: BYTE BB1, BB2;
WORD WW1, WW2;
EWORD BB3;
DWORD DW1, DW2;
mov A, BB1;
A = BB2; // A = information
(5). Definicja typu stałego, na przykład:
CONST_VAL EQU 12;
Value => 34h;
mov A, CONST_VAL;
mov A, value;
A = 55h; // A = constant
W wyrażeniu z wartością r będącą stałą, jeśli wartość liczbowa przekracza dopuszczalny zakres wartości l, wynik będzie różny w różnych wersjach.
- FIX_VAL => 0x1234;
BYTE bb1 = FIX_VAL; // bb1 = 0x34 : Automatycznie wybierz bajt niższy.
BYTE bb2 = FIX_VAL >> 8; // bb2 = 0x12
label: ....
WORD Point = label;
...
Point$0 = label + 2; // Point$0 = Automatically pick the low byte of label + 2
2. PA = 0b_1_0000_1001; // PA = 0x109 = 0x09
// The customer mistakenly wrote 0x89 as 0x109 and needs to remind the error.
3. WORD ww = -4; // ww = 0xFFFFFFFC = 0xFFFC;
Przed wersją 0.82 automatycznie pobierany był niższy bajt, jak w przypadku 1. Mimo że program jest wygodniejszy w pisaniu, może istnieć ryzyko wystąpienia przypadku 2.
Jednak od wersji 0.83, aby uniemożliwić użytkownikom kompilację z powodu nieprawidłowych wartości, ale poświęcających więcej czasu na debugowanie, zdecydowano wyłączyć tę funkcję.
Wręcz przeciwnie, zapis przypadku 1 należy zmienić na zapis przypadku 4.
(4). BYTE bb1 = FIX_VAL & 0xFF;
Point$0 = (label + 2) & 0xFF;
Jeśli chcesz skorzystać z funkcjonalności Przypadku 1, po prostu wpisz poniższe polecenie (zwykle umieszczone w pliku .PRE) i nie wykonuj już sprawdzania zakresu wartości.
#pragma No_Chk Syntax.Mov_Over
Przypadek 3 to nawyk wielu osób podczas pisania programu. Mimo że wartość z prawej strony przekracza wartość z lewej, ale liczba przekroczeń wynosi 0xFF, IDE nadal może zaakceptować ten zapis.
【Typ danych BIT】
(1). gramatyka: BIT name [: variable . bit_num ]
Przypadek:
BIT BB1_2 : BB1.2;
BIT WW1_12 : WW1.12
BIT Flag; // Defined by the system
Wyjaśnienie:
For BYTE variables,bit_num the range is 0 ~ 7,
For WORD variables,bit_num the range is 0 ~ 15,
For EWORD variables,bit_num the range is 0 ~ 23,
For DWORD variables,bit_num the range is 0 ~ 31,
Zmienna, która nie określa adresu (jak Flaga w przykładzie powyżej), a adres jest ustalany przez system.
(2). Do deklaracji BIT można również użyć wszystkich poniższych formatów:
a) BIT name : variable . bit_num
b) name BIT variable . bit_num
c) name EQU variable . bit_num
(3). W języku asemblera bit SET0/SET1 można wyrazić w języku C jako bit = 0/1, jak pokazano w poniższym przykładzie.
WORD ww;
BIT Bit_1;
Bit_1 = 1; // Equivalent to SET1 Bit_1
Bit_1 = 0; // Equivalent to SET0 Bit_1
ww.15 = 1; // Equivalent to SET1 ww.15,Equivalent to SET1 ww$1.7
【Użyj $, aby uzyskać dostęp do danych BYTE】
W przypadku danych typu WORD dostęp tylko do jednego bajtu jest niepraktyczny. Oto przykład:
WORD WW;
WW = (WW & 0xFF00) | ((WW + 1) & 0xFF);
// Add 1 to WW's Low Byte, and WW's High Byte remains unchanged.
Mimo że chcesz po prostu dodać 1 do młodszego bajtu, większość kompilatorów musi napisać długą listę skryptów.
Dlatego Mini-C oferuje następującą nową składnię:
WW $ 0 = WW + 1;
// low byte of WW = low byte of (WW+1)
// high byte of WW unchanged
Analogicznie, $1 is Bit[15~8], $2 is Bit[23~16], and $3 is Bit[31~24].
WW $ 0 = WW $ 1;
// WW Low Byte <= WW High Byte.
DWORD DD;
A = DD $ 0 ^ DD $ 1 ^ DD $ 2 ^ DD $ 3;
// The four bytes of DD are used as XOR operations.
Nawiasem mówiąc, chociaż w powyższym przypadku,
WW = (WW & 0xFF00) | ((WW + 1) & 0xFF);
Chciałem tylko dodać 1 do najniższego bajtu WW. Jednak wiele kompilatorów kompiluje bezużyteczny kod programu, sugerując ludziom, że programy napisane w C są nieefektywne. W rzeczywistości optymalizacja kompilatora nie jest dobrze wykonana.
Wskaźniki:
W języku asemblera IDXM, LDTABL, LDTABH mogą odpowiadać składni Mini-C:
Sprawdź składnię pamięci RAM, dostępną
* punkt reprezentujący IDXM: Sprawdź bajt pamięci RAM,
Sprawdź składnię ROM-u, dostępną
* punkt $ L reprezentujący LDTABL: Sprawdź najniższy bajt pamięci ROM,
* punkt $ H reprezentuje LDTABH: Sprawdź wyższy bajt pamięci ROM,
* punkt $ W reprezentuje Sprawdź słowo ROM.
Należy pamiętać, że nie każdy układ scalony obsługuje instrukcje tablicowe. Więcej informacji można znaleźć w arkuszu danych.
W składni Mini-C wszystkie zmienne WORD mogą być używane jako Point.
Przykłady użycia są następujące:
Label: DC 1234h, 5678h;
...
void func (void)
{
WORD point, data;
BYTE buffer[10];
point = buffer; // point to the address of the buffer
// mov A, la@buffer[0]
// mov lb@point, A
// mov A, ha@buffer[0]
// mov hb@point, A
* point = * point ^ 0x55; // Use IDXM to change the RAM // idxm A, point
// xor A, 0x55
// idxm point, A
point = Label; // point to the address of the Label
// mov A, la@Label
// mov lb@point, A
// mov A, ha@Label
// mov hb@point, A
A = * point $ L; // Use LDTABL to check the ROM
// ldtabl point
data$1 = * point $ H; // Use LDTABH to check the ROM
// ldtabh point
// mov hb@data, A
data = * point $ W; // Use LDTABL / H to check the ROM
// ldtabl point
// mov lb@data, A
// ldtabh point
// mov hb@data, A
}
Ze względu na ograniczenia sprzętowe, Wskaźnik musi znajdować się na parzystej granicy, więc zmienne WORD korzystające z funkcji Point są umieszczane na parzystej granicy. IDE domyślnie używa adresu pamięci WORD, o ile jest to możliwe w parzystej granicy.
Podczas używania Wskaźnik ++/--, Wskaźnik doda lub odejmie tylko jeden. Jeśli wskazywana zmienna to RAM WORD, EWORD, DWORD, należy samodzielnie użyć Point += / -= 2/3/4.
Tablice
Tablica zaczyna się od 0, a jej długość nie może wynosić 0 i nie może przekraczać zadeklarowanej wartości maksymalnej.
#define BUF_SIZE 0x10
BYTE Buffer [BUF_SIZE];
Buffer[0x0] = 0; // The first one.
Buffer[0xF] = 0xF; // The last one.
Buffer[0x10]= 0x10; // Error, out of range
Użyj &, aby odczytać adres zmiennej
Jeśli chcemy użyć tablicy z powyższego przykładu jako kolejki, możemy zastosować następującą metodę.
WORD Point;
Point = Buffer;
// …
while (1)
{
// … calculate ACC
*Point = A;
Point ++; // Point plus one each time
if (Point == & Buffer[BUF_SIZE])// until (Point == Buffer),
{ // bottom of buffer
Point = Buffer; // set Point to the initial value
} // of Buffer
}
&, jako referencja zmiennej
Jeżeli chcemy mieć różne nazwy zmiennych pod tym samym adresem zmiennej, możemy zastosować następującą metodę.
WORD Point, Data[2];
BYTE & Var1 = Point$0; // Var1 is the same address as the Low Byte of Point.
BYTE & Var2 = Point$1; // Var2 is the same address as the High Byte of Point.
BYTE & Var3 = Data[0]$1; // Var3 is the same address as the High Byte of Data [0].
WORD & Var4 = Data[1]; // Var4 is the same address as Data [1].
Funkcje
W funkcjach projektu Mini-C bieżąca wartość zwracana funkcji jest ustalona na void.
Funkcja może nie mieć parametru void lub parametrów BYTE, WORD, EWORD, DWORD i innych typów i może mieć wartości przychodzące, wychodzące, incoming i outgoed (podobnie jak w przypadku referencji). Części przychodzącej można najpierw nadać wartość domyślną stałej.
Case :
void Func_1 (void) // Functions without parameters and return values
{ .... }
void Func_2 (BYTE bb) // Pass in BYTE parameters
{ .... }
void Func_3 (BYTE bb, WORD ww) // Pass in BYTE, WORD two parameters
{ .... }
void Func_4 (BYTE in, WORD ^ out) // Incoming BYTE parameters, outgoing WORD parameters
{ .... }
void Func_5 (WORD & ref) // Incoming and outgoing WORD parameters
{ .... }
extern void Func_1 (void); // Declaring a function prototype
extern void Func_2 (BYTE bb = 0x55); // If the incoming parameter is not set, the default value is 0x55
Zmienna globalna/lokalna/statyczna.
Zmienna globalna: Zdefiniuj zmienną poza funkcją.
Zmienna lokalna: Zdefiniuj zmienną w obrębie funkcji.
Zmienna statyczna: Pola widoczne to zmienne ograniczone do funkcji lub pliku.
BYTE Global; // Global variable
STATIC BYTE Static_Var1; // Static global variable
void Func (void)
{
BYTE local_var; // Local variable
STATIC BYTE st_Var2; // Static local variable
...
}
BYTE Init_Val1 = 1; // Global variable + boot initial value
STATIC BYTE Init_Var2 = 2; // Static global variable + boot initial value
void Func2 (void)
{
BYTE local_var = 0; // Local variable + program execution is not set until this time
STATIC BYTE st_Var_0 = 0; // Static local variable + boot initial value
...
}
Różne funkcje mogą używać tej samej nazwy zmiennej lokalnej. Przed wersją 0.82 zmienne globalne i lokalne również mogły mieć tę samą nazwę. Jednak po wersji 0.83, aby zapobiec nadużyciom wynikającym z tej samej nazwy, poświęcono więcej czasu na debugowanie, dlatego zdecydowano się wyłączyć tę funkcję.
Jeśli chcesz, aby zmienne globalne i lokalne miały tę samą nazwę, po prostu wpisz poniższe polecenie (zwykle w pliku .PRE), a środowisko IDE nie będzie już sprawdzać, czy zmienne mają tę samą nazwę.
#pragma No_Chk Syntax.Same_Name
Po wersji 0.85, zgodnie ze specyfikacją języka C, do projektu Mini-C dodano funkcję ustawiania wartości początkowej boot. Po ADJUST_IC ustawiana jest wartość początkowa zmiennej.
Dla tych, którzy korzystają z niego po raz pierwszy, prosimy pamiętać, że ustawienie statycznych zmiennych lokalnych różni się od ustawienia zmiennych lokalnych. Statyczne zmienne lokalne są ustawiane, gdy wartość zmiennej wynosi ADJUST_IC, a zmienne lokalne są ustawiane, gdy program wykonuje się do tej pozycji. Ustawiana jest tylko wartość zmiennej.
Jeśli jest zbyt wiele zmiennych, które muszą początkowo mieć wartość 0, zaleca się wyczyszczenie całej pamięci RAM do 0 w ADJUST_IC, co może skrócić wiele skryptów.
Proszę zapoznać się ze specjalnymi opcjami ADJUST_IC i Clear RAM.
Adresowanie pamięci RAM
Różne nazwy zmiennych pod tym samym adresem zmiennej, który jest niestandardowym adresem zmiennej.
.RAMADR 0x00 // Put data from RAM 0
BYTE Array[0x10]; // Cały zakres 0x0~ Adr 0xF jest zajęty.
.RAMADR 0x00 // Put data from RAM 0
WORD Word_0, Word_2; // Cały zakres 0x0~ Adr 0x3 jest zajęty
.RAMADR SYSTEM // Only available in the "Mini-C" project
// Powrót do systemu w celu automatycznego uporządkowania adresu zmiennej.
BYTE Mem_0; // Ponieważ adres 0x0~ 0xF jest już zajęty
// System może wyszukiwać dostępne adresy od 0x10 w górę
Adresowanie pamięci ROM
Możesz użyć wartości .ROMADR, aby określić adres bezwzględny kodu programu. W następujący sposób:
.ROMADR 0x3F0; // Absolutely addressing to 0x3F0
...
.ROMADR $ + 2; // program address = (current + 2)
...
Wartość .ALIGN można również wykorzystać do określenia granic wyrównania kodu programu, w następujący sposób:
.ALIGN 0x10; // Align to the boundaries of 0/0x10/0x20/0x30/..
...
.ALIGN 0x100; // Align to the boundaries of 0/0x100/0x200/..
...
.ALIGN BOTTOM; // Put the following program code at the bottom of the program
...
Zwykle jednak nie będziesz korzystał z powyższych instrukcji.
ROM zdefiniowany przez DC
Dane stałe można zdefiniować wyłącznie w układach scalonych obsługujących instrukcje wyszukiwania w tabeli (LDTABL/LDTABH).
Dane stałe można zadeklarować w następujący sposób:
Labe1: DC 1234h, 5678h, 0xABCD, 0xFFFF, -1;
W przypadku niektórych układów scalonych, które nie mają 16-bitowej architektury zestawu instrukcji, zdefiniowanie 0xABCD już wygeneruje błąd. Jednak liczby 0xFFFF i -1, które ogólnie oznaczają, że przestrzeń ROM jest zarezerwowana, a nie wypalona, pozwolą na zaakceptowanie zapisu w tym szczególnym przypadku.
Jeśli chcesz zadeklarować stałą jako ciąg znaków, możesz użyć poleceń DC lub DB, jak pokazano w poniższym przykładzie:
Label2: DC "ABC", 55h, 0AAh; // 0x4241, 0x0043, 0x0055, 0x00AA
Label3: DB "ABC", 55h, 0AAh; // 0x4241, 0x5543, 0x00AA
Można również użyć {}, aby umieścić kilka wierszy stałych danych, jak pokazano w poniższym przykładzie:
Label4: DC
{
"ABC", // 0x4241, 0x0043
55h, 0AAh, // 0x0055, 0x00AA
1234h, 0x5678, // 0x1234, 0x5678
((2 * 3) << 8) + 4 // 0x0604
}
Polecenie DC jest wyrażone w jednostkach WORD, a część każdego parametru danych mniejsza niż WORD jest wypełniana jako 0.
Polecenie DB opiera się na BYTE. Najpierw wpisz bajt młodszy, a następnie starszy. Po uzupełnieniu wszystkich informacji, część, która nie spełnia warunku WORD, jest zastępowana wartością 0.
Jeśli chodzi o reprezentację ciągu znaków, obsługuje on następujący format ogólnego języka C:
Label5: DC "AB\0CD\377"
// \ nnn is base 8 notation,\ 0 is 0, and\ 377 is 255.
// So the data in ROM will be 0x4241, 0x4300, 0xFF44.
Label6: DC "AB\tCD\n" // 0x4241, 0x4309, 0x0A44。
Poniżej przedstawiono przykład wyszukiwania w tabeli:
WORD Point;
Point = Label1; // Point looks up the table from Label1
while (1)
{
A = *Point$L;
if (A == 0) break; // The query ends when data is 0.
// if (ZF) break; // Error: Because the LDTAB instruction does not affect ZF.
Point++;
}
Poniżej przedstawiono przykład tabeli przeglądowej:
label: DC lab1, lab2;
lab1: DB lab2 - lab1, "123456\0";
lab2: DB lab3 - lab2, "ABCDEFG\0";
lab3:
Istnieją również pewne specjalne zastosowania, które służą do odczytu wyższego adresu ($^ 1) i niższego adresu ($^ 0) etykiety, w następujący sposób:
DB lab1$^1, lab1$^0, lab2$^0;
ROM zdefiniowany przez CONST
Dane stałe można zdefiniować wyłącznie w układach scalonych obsługujących instrukcje wyszukiwania w tabeli (LDTABL/LDTABH).
W projekcie „Mini-C” stałe dane pamięci ROM można zadeklarować w następujący sposób:
CONST WORD array_name [] = { 0x1234, 0x5678, 0xABCD };
Jeśli chodzi o rzeczywistą lokalizację przechowywania pamięci ROM, reguły definicji są następujące:
1) Jeżeli zadeklarowano jako FAR CONST WORD xxx[] = ...
Dane stałe są umieszczane stopniowo w dół, tak aby przestrzeń pamięci ROM była wykorzystywana najefektywniej.
2) Jeśli zadeklarowano jako NEAR CONST WORD xxx[] = ...
Stała długość danych musi być mniejsza niż 256, a kompilator automatycznie przesunie ją tak, aby dane nie przekroczyły granicy 256.
Zaletą jest to, że punkty wskazujące na obszar danych muszą mieć taki sam wysoki adres, program może dokończyć wyznaczanie punktu, pod warunkiem, że niski adres zostanie zmieniony.
Wadą jest to, że niektóre transgraniczne pamięci ROM nie mogą być w pełni wykorzystane, więc gdy długość danych jest większa, więcej miejsca w pamięci ROM może zostać zmarnowane w celu umieszczenia danych w tym samym bloku.
3) Jeśli nie określono wartości NEAR/FAR, domyślnie traktowana jest wartość FAR.
Użytkownicy mogą użyć polecenia .ROMADR, aby samodzielnie określić lokalizację przechowywania CONST WORD [].
4) Aby odczytać dane stałe o długości mniejszej niż 256, możesz skorzystać z następujących metod:
Przesunięcie BAJTÓW = 3;
WORD All_Data = array_name [shift];
BYTE Low_Data = array_name [shift]$L;
BYTE High_Data = array_name [A] $H;
Jeżeli zmienna shift/A znajduje się poza zakresem tablicy, akcja odczytu pamięci ROM nie zostanie wykonana.
Atrybuty array_name NEAR/FAR nie muszą być definiowane, kompilator automatycznie zajmie się ustaleniem granic.
【Adresowanie STACK】
W projekcie typu „ASM” programista musi zdefiniować głębokość stosu, co ilustruje poniższy program:
.ROMADR 0
PRZEJDŹ DO FPPA0
PRZEJDŹ DO FPPA1
...
.RAMADR 0 // Adres musi być mniejszy niż 0x100
Stos SŁÓW0 [3]; // 3 SŁOWA
Stos SŁÓW1 [2]; // 2 SŁOWA
...
FPPA0:
.ADJUST_IC ....;
MOV A, LA @ Stack0[0]; // Określ Stack0 dla FPPA0
MOV SP, A; // Bardziej tradycyjne pismo
...
FPPA1:
SP = Stack0; // Określ Stack1 dla FPPA1
... // Prostszy sposób pisania
W projekcie „Mini-C” możesz pozostawić obsługę stosu systemowi, nie martwiąc się o to.
Przypadek 1:
unieważnić FPPA0 (pusty)
{ // SP Automatyczna konfiguracja
...
// Błąd: SP = XX; // Personalizacja użytkownika jest niedozwolona.
}
Jeśli jednak chcesz dostosować głębokość stosu, możesz również odwołać się do następującego przykładu:
Przypadek 2:
void FPPA0 (void) : Stos = 0x10
{ // Wymuś konfigurację stosu WORD 0x10
...
}
Przypadek 3:
.RAMADR 0x8 // Użytkownik ręcznie adresuje
WORD Stack0 [0x10]; // Należy upewnić się, że adres stosu jest mniejszy niż 0x100
.RAMADR SYSTEM // Adresowanie systemu automatycznej odpowiedzi
...
void FPPA2 (void) : Stos = ?
{ // Poproś system, aby nie konfigurował stosu
SP = Stack0; // Stos ustawiony przez użytkownika
...
}
Przejdź do strony głównej Wróć do kategorii MINI-C
