MADS udostępnia możliwość deklaracji dwóch typów danych: strukturalne .STRUCT i wyliczeniowe .ENUM.

STRUKTURY

Jeśli programowaliście w C, to pewnie spotkaliście się już ze strukturami. Ogólnie w MADS struktura definiuje tablicę wirtualną, jednowymiarową o polach różnego typu .BYTE .WORD .LONG .DWORD i ich wielokrotności. Wirtualna ponieważ istnieje ona jak na razie tylko w pamięci assemblera.

Pola takiej struktury zawierają informację o ofsecie do początku struktury.

Deklaracja .STRUCT

Struktur dotyczą n/w dyrektywy:

name .STRUCT
     .STRUCT name
     .ENDS [.SEND] [.END]
  • name .STRUCT

Deklaracja struktury name przy użyciu dyrektywy .STRUCT. Nazwa struktury jest wymagana i konieczna, jej brak wygeneruje błąd. Do nazw struktur nie można używać nazw mnemoników i pseudo rozkazów. Jeśli nazwa jest zarezerwowana wystąpi błąd z komunikatem Reserved word.


Przykład deklaracji struktury:

.STRUCT name

  x .word      ; lda #name.x = 0
  y .word      ; lda #name.y = 2
  z .long      ; lda #name.z = 4
  v .dword     ; lda #name.v = 7

  q :3 .byte   ; lda #name.q = 11

.ENDS          ; lda #name   = 14 (length)

Pola struktury definiujemy przez podanie nazwy i typu pola .BYTE .WORD .LONG .DWORD. Nazwa pola może być poprzedzona białymi spacjami. W obszarze ograniczonym dyrektywami .STRUCT i .ENDS nie ma możliwości używania mnemoników CPU, jeśli je użyjemy lub użyjemy innych niedozwolonych znaków wystąpi błąd z komunikatem Improper syntax lub Illegal instruction.

Podsumowując, etykieta name zawiera informację o całkowitej długości struktury (w bajtach). Pozostałe etykiety opisujące pola zawierają informację o ofsecie do początku struktury. Deklaracji struktur nie można zagnieżdżać, można zagnieżdżać wcześniej zadeklarowane struktury (kolejność wystąpienia w programie nie ma znaczenia), np.:

.STRUCT temp

x .word
y .word
v .byte
z .word

.ENDS


.STRUCT test

tmp  temp

.ENDS

 lda #temp.v
 lda #test.tmp.x
 lda #test.tmp.z

Do czego może przydać się struktura?

Przypuśćmy, że mamy jakąś tablicę z polami różnego typu, możemy odczytywać pola takiej tablicy przy pomocy z góry określonych wartości offsetów. Jednak gdy dodamy dodatkowe pole do tablicy, czy też zmodyfikujemy ją w inny sposób, będziemy zmuszeni poprawiać kod programu który posługiwał się z góry określonymi wartościami offsetów. Gdy zdefiniujemy taką tablicę przy pomocy struktury będziemy mogli odczytywać jej pola posługując się offsetami zapisanymi w deklaracji struktury, czyli zawsze odczytamy właściwe pole niezależnie od zmian jakie zaszły w tablicy.

Inny przykład zastosowania struktur został opisany w rozdziale Symbole zewnętrzne, przykład zastosowania symboli external i struktur .STRUCT.

Definicja (odwołania)

Definiowanie danych strukturalnych polega na przypisaniu nowej etykiecie konkretnej struktury z użyciem pseudo rozkazu DTA lub bez tego pseudo rozkazu. Wynikiem takiego przypisania jest zarezerwowana pamięć, nie jest to już twór wirtualny.

label DTA struct_name [count] (data1,data2,data3...) (data1,data2,data3...) ...

label struct_name

COUNT określa liczbę z przedziału 0..COUNT, która definiuje maksymalną wartość indeksu tablicy jednowymiarowej, a przez to także liczbę odłożonych w pamięci danych typu strukturalnego. Przykład deklaracji struktury i definicji danych strukturalnych:

;----------------------;
; deklaracja struktury ;
;----------------------;
.STRUCT temp

x .word
y .word
v .byte
z .word

.ENDS

;---------------------;
; definiowanie danych ;
;---------------------;

data dta temp [12] (1,20,200,32000) (19,2,122,42700)

data2 dta temp [0]

data3 temp          // krótszy odpowiednik DATA2

Po nazwie struktury w nawiasie kwadratowym musi znajdować się wartość z przedziału 0..2147483647, która definiuje maksymalną wartość indeksu tablicy jednowymiarowej, a jednocześnie liczbę odłożonych w pamięci danych typu strukturalnego.

Po nawiasie kwadratowym może wystąpić opcjonalnie lista wartości początkowych (ograniczona nawiasami okrągłymi), jeśli nie wystąpi wówczas domyślnymi wartościami pól struktury są zera. Z kolei jeśli lista wartości początkowych jest mniejsza od liczby pól zadeklarowanych, wówczas pozostałe pola inicjowane są ostatnimi wartościami jakie zostały podane, np.

data dta temp [12] (1,20,200,32000)

Taka deklaracja spowoduje, że wszystkie pola zostaną zaincjowane wartościami 1,20,200,32000, a nie tylko pierwsze pole data[0].

Jeśli lista wartości początkowych będzie większa lub mniejsza od liczby pól struktury, wówczas wystąpi błąd z komunikatem Constant expression violates subrange bounds.

Aby odwołać się do pól tak nowo powstałych danych należy podać ich nazwę, koniecznie indeks w nawiasie kwadratowym i nazwę pola po kropce, np.:

lda data[4].y
ldx #data[0].v

Brak nawiasu kwadratowego z indeksem label[index] zakończy się komunikatem błędu Undeclared label.

WYLICZENIA

Wyliczeń dotyczą n/w dyrektywy:

name .ENUM
     .ENDE [.EEND] [.END]

Example:

.enum portb
 rom_off = $fe
 rom_on = $ff
.ende

.enum test
 a             ; a=0
 b             ; b=1
 c = 5         ; c=5
 d             ; d=6
.ende

Deklaracja wyliczenia odbywa się przy użyciu dyrektyw .ENUM i .ENDE. Nazwa wyliczenia jest wymagana i konieczna, jej brak wygeneruje błąd. Do nazw wyliczeń nie można używać nazw mnemoników i pseudo rozkazów. Jeśli nazwa jest zarezerwowana wystąpi błąd z komunikatem Reserved word.

Wartości kolejnych etykiet są zwiększane automatycznie o 1 zaczynając od domyślnej wartości 0, wartości etykiet można definiować samodzielnie albo pozostawić to automatowi.

Do etykiet wyliczeniowych odwołujemy się przy pomocy składni:

enum_name (field)

lub bezpośrednio podobnie jak w przypadku odwołań do bloków .LOCAL .PROC czyli po nazwie typu oddzielone znakiem kropki występują kolejne pola, np.:

lda #portb(rom_off)

dta portb.rom_on, portb.rom_off

Wyliczeń możemy użyć do deklaracji pól struktury .STRUCT, do alokacji zmiennych dyrektywą .VAR, np.:

bank portb           // alokacja zmiennej BANK o rozmiarze 1 bajtu
.var bank portb      // alokacja zmiennej BANK o rozmiarze 1 bajtu

.struct test
 a portb
 b portb
.ends

Rozmiar zmiennej typu wyliczeniowego zależny jest od maksymalnej wartości jakie przyjmują kolejne etykiety wyliczenia, np.:

.enum EState
    DONE, DIRECTORY_SEARCH=$ff, INIT_LOADING, LOADING
.ende

Dla w/w przykładu alokacja zmiennej typu EState będzie miała rozmiar dwóch bajtów WORD.

Rozmiar możemy sprawdzić przy pomocy dyrektywy .LEN .SIZEOF, wynikiem będą wartości 1..4 (1 BYTE, 2 WORD, 3 LONG, 4 DWORD) np.:

.print .len EState