oop asm


+-LAB-ASMPL-by-GustawKIT------Programowanie obiektowe w Asemblerze.--------+ ver. 0.1beta

Idea programowania obiektowego znana jest każdemu programiście zainteresowanym tym tematem więc nie ma potrzeby jej przedstawiać. Doskonale znamy pojęcia obiektu (czy też klasy), dziedziczenia, polimorfizmu, enkapsulacji z języków wyższego poziomu. Okazuje się, że w asemblerze także można programować objektowo, a dzięki Turbo Asemblerowi Borlanda jest to równie łatwe jak w językach wyższego poziomu. W innych kompilatorach np. MASM także można w pewien sposób przygotować kod do typu objektowego zapisu. Postaram się Wam przedstawić możliwości takiego programowania w Turbo Asemblerze. Wybrałem tryb IDEAL, ponieważ oferuje on najwięcej ułatwien w tym temacie.

______________________________________________________________________________________
Spis TREŚCI:

1. Pola i Metody & Enkapsulacja.
2. Dziedziczenie (Inheritance).
3. Przykład metod statycznych i dziedziczenia.
4. Polimorfizm i metody wirtualne.
________________________________________________________________________________________

1. Pola i Metody & Enkapsulacja.

Pola są to dane w pewnej strukturze (rekordzie), zgrupowane wspólnie w celu łatwego dostępu i organizacji. Przyklad prostej deklaracji struktury w asemblerze:

STRUC Liczby
Pierwsza dw ?
Druga dw ?
ENDS Liczby

Jest to jedynie deklaracja struktury i aby ją wykorzystać musimy zdefiniować zmienną typu takiej struktury:

Skladniki1 Liczby ? - niezainicjalizowane
Skladniki2 Liczby < > - zainicjalizowane domyślnie
Skaldniki3 Liczby <13,16> - zainicjalizowane wartości

Załóżym teraz, że chcemy tą strukture wykorzystąć np. przez procedure sumująca liczby.

PROC Sumator
mov ax,[Liczby.Pierwsza]
mov bx,[Liczby.Druga]
add ax,bx
ENDP Sumator

I procedura w rejestrze AX zwróci nam wartość sumy liczb.

Turbo Asembler umożliwia definiowanie struktur na podobieństwo obiektów. Powyższą strukture i procedure możemy połączyć tak:

GLOBAL TLiczby_Sumator :PROC

STRUC TLiczby METHOD {
Suma : dword = TLiczby_Sumator}
Pierwsza dw ?
Druga dw ?
ENDS TLiczby

PROC TLiczby_Sumator PASCAL
mov ax, [(TSumator PTR si).Pierwsza]
mov bx, [(TSumator PTR si).Druga]
add ax,bx
ret
ENDP TLiczby_Sumator

Widzimy wię, że w myśl programowania obiektowego w strukture włącząno definicje metody. Metody definiujemy:

Wskaźnik_metody : typ(rozmiar) = Nazwa metody

co znaczy, że w definicji objektu nie ma samej metody a jedynie wskaźnik do tej procedury a dokładniej dyrektywa METHOD w rzeczwistości tworzy odrębną tabele ze wskaźnikami do poszczególnych metod i nie wchodzi ona w sklad struktury. Tak więc jeżeli chcielibyśmy obliczyć rozmiar struktury TLiczby wystarczy zsumowac rozmiar pol, czyli dw+dw = 4bajty. Taki rozmiar możemy sobie także obliczyc instrukcją 'size' (rozmiar = size TLiczby). Natomiast statyczne adresy (z uwagi na statyczny metody).. tworzone są w specjalnej tabeli. Jeżeli zdeklarujemy metode w definicji obiektu musimy pamiętać o jej implementacji w kodzie programu.

Wykorzystać tak zefiniowany object możemy następująco:

Skladniki1 TLiczby <12,15>

mov si, offset Skladniki1
Call SI METHOD TLiczby:Suma

Do SI ladowany jest adres wartości Skladnik1, teraz w DS:SI mamy adres obiektu TLiczby i przez ten adres odowływuje się CALL do metod objektu. W wywołaniu podajemy także nazwe obiektu i wskaźnik do metody.

Natomiast aby z implementacji metody odwoływać sie do pól czy innych metod obiektu używamy nazwy obiektu lub predefiniowanego wskaźnika @object, który własnie wskazuje na aktualny object.

2. Dziedziczenie (Inheritance).

Jedną z głównych zalet OOP jest dziedzicznie. Polega ona na przekazywaniu metod i pól obiektowi, który tworzony jest na podstawie obiektu przodka. Aby np. do powyższego objektu dodać metode np. odejmowania, moglibyćmy dodać tą metode w definicji objektu TLiczby. Mozemy jednak stworzyć nowy obiekt dziedziczący pola i metody z objektu TLiczby:

STRUC TLiczbyNowe TLiczby METHOD {
Iloczyn : dword = TLiczbyNowe_Iloczynn}
ENDS TLiczbyNowe

Widzimy w definicji struktury po nazwie objektu dodano nazwe przodka po którym dziedziczy. W rzczeczywistości nasz objekt TLiczbyNowe zawiera także procedure Suma i pola Pierwsza, Druga. Odpowiednio domyślnie!!! ten objekt wygląda jakby:

STRUC TLiczbyNowe METHOD {
Suma : dword = ......
Iloczyn : dword = ......}
Pierwsza dw ?
Druga dw ?
ENDS TLiczbyNowe

Aby się odwołać do nowego obiektu musimy go utworzyć:

Skladniki2 TLiczbyNowe <12,15>

i wywołac procedure Iloczyn:

mov si, offset Skladniki2
Call SI METHOD TLiczbyNowe:Iloczyn

ale możemy także wywołac odziedziczoną procedure Suma:

Call SI METHOD TLiczbyNowe:Suma

3. Przykład metod statycznych i dziedziczenia.

;-------------------------------------------------------------------
IDEAL

JUMPS
LOCALS @@
MODEL large, PASCAL
STACK 1000h

GLOBAL TLiczby_podaj_pierwszy :PROC
GLOBAL TLiczby_podaj_drugi :PROC
GLOBAL TLiczby_przypisz_pierwszy :PROC
GLOBAL TLiczby_przypisz_drugi :PROC
GLOBAL TLiczby_oblicz_sume :PROC
GLOBAL TSuma_oblicz_sume :PROC
GLOBAL TSuma_Constructor :PROC
GLOBAL TSuma_Destructor :PROC

STRUC TLiczby METHOD {
podaj_pierwszy :dword = TLiczby_podaj_pierwszy
podaj_drugi :dword = TLiczby_podaj_drugi
przypisz_pierwszy :dword = TLiczby_przypisz_pierwszy
przypisz_drugi :dword = TLiczby_przypisz_drugi
}
pierwsza dw ?
druga dw ?
ENDS TLiczby

STRUC TSuma TLiczby METHOD{
init :dword = TSuma_Constructor
oblicz_sume :dword = TSuma_oblicz_sume
done :dword = TSuma_Destructor}
ENDS TSuma

CODESEG

;---------------------------------------------------------------
; TLiczby_podaj_pierwszy TLiczby podaj_pierwszy method
;---------------------------------------------------------------
; Input: ds:si = adres danych
; Output: ax = dana pierwsza w ax
; Registers: ax
;---------------------------------------------------------------

PROC TLiczby_podaj_pierwszy PASCAL
mov ax, [(TLiczby PTR si).pierwsza]
ret
ENDP TLiczby_podaj_pierwszy

;---------------------------------------------------------------
; TLiczby_podaj_drugi TLiczby podaj_drugi method
;---------------------------------------------------------------
; Input: ds:si = adres danych
; Output: ax = dana druga w AX
; Registers: ax
;---------------------------------------------------------------

PROC TLiczby_podaj_drugi PASCAL
mov ax, [(TLiczby PTR si).druga]
ret
ENDP TLiczby_podaj_drugi

;---------------------------------------------------------------
; TLiczby_przypisz_pierwszy TLiczby przypisz_pierwszy method
;---------------------------------------------------------------
; Input: ds:si = adres
; pierwsza (word) parametr
; Output: none
; Registers: ax
;---------------------------------------------------------------
PROC TLiczby_przypisz_pierwszy PASCAL
ARG @@pierwsza:word ; Tworzy przesuniecie stosu do parametru
USES ax
mov ax, [@@pierwsza]
mov [(TLiczby PTR si).pierwsza], ax
ret
ENDP TLiczby_przypisz_pierwszy

;---------------------------------------------------------------
; TLiczby_przypisz_drugi TLiczby przypisz_drugi method
;---------------------------------------------------------------
; Input: ds:si = adres
; druga (word) parametr
; Output:brak
; Registers: ax
;---------------------------------------------------------------

PROC TLiczby_przypisz_drugi PASCAL
ARG @@druga:word
USES ax
mov ax, [@@druga]
mov [(TLiczby PTR si).druga], ax
ret
ENDP TLiczby_przypisz_drugi

;---------------------------------------------------------------
; TSuma_Constructor TSuma Constructor
;---------------------------------------------------------------
; Input: brak
; Output:brak
; Registers: none
;---------------------------------------------------------------

PROC TSuma_Constructor PASCAL
ARG _param1:word,_param2:word
CALL si METHOD TSuma:przypisz_pierwszy, [_param1]
CALL si METHOD TSuma:przypisz_drugi, [_param2]
ret
ENDP TSuma_Constructor

;---------------------------------------------------------------
; TSuma_Destructor TSuma Destructor
;---------------------------------------------------------------
; Input: brak
; Output:brak
; Registers: none
;---------------------------------------------------------------
PROC TSuma_Destructor PASCAL
CALL si METHOD TSuma:przypisz_pierwszy, 0
CALL si METHOD TSuma:przypisz_drugi, 0
ret
ENDP TSuma_Destructor

;---------------------------------------------------------------
; TSuma_oblicz_sume TSuma oblicz_sume methode
;---------------------------------------------------------------
; Input: brak
; Output:ax
; Registers: ax
;---------------------------------------------------------------
PROC TSuma_oblicz_sume PASCAL
mov ax, [(TSuma PTR si).pierwsza]
mov bx, [(TSuma PTR si).druga]
add ax,bx
ret
ENDP TSuma_oblicz_sume

DATASEG
;----- Definicja objektu TLiczby
s1 TSuma { } ; Deklaracja objektu

CODESEG

Start:
mov ax, @data ; Inicjalizacja segmentu danych
mov ds, ax
mov si, offset s1
CALL si METHOD TSuma:init, 03h, 04h
CALL si METHOD TSuma:oblicz_sume
CALL si METHOD TSuma:done

Exit:
mov ah, 04Ch
mov al, 0
int 21h

END Start

;--------------------------------------------------------------------

Komentarz:
W powyższym programie zdeklarowano dwa objekty TLiczby i TSuma. TSuma odpowiada za zsumowanie danych i dziedziczy pola i metody z obiektu TLiczby. Wywołania procedur są oczywiście statyczne.

Na uwage zasługuje sposób wywołania metod obiektow z programu głównego. Jak pisałem wcześniej wskaźniki do poszczególnych metod przechowywane są w specjalnej tabeli. Dla powyższzego programu wygląda ona następująco:

@TABLE_TLICZBY
PODAJ_PIERWSZY Dword Stat SUMA_TEXT:0000
PODAJ_DRUGI Dword Stat SUMA_TEXT:0003
PRZYPISZ_PIERWSZY Dword Stat SUMA_TEXT:0007
PRZYPISZ_DRUGI Dword Stat SUMA_TEXT:0015
@TABLE_TSUMA
PODAJ_PIERWSZY Dword Stat SUMA_TEXT:0000
PODAJ_DRUGI Dword Stat SUMA_TEXT:0003

Metody wywoływane są kodem zapisanym:

mov si, offset s1
CALL si METHOD TSuma:init, 03h, 04h
CALL si METHOD TSuma:oblicz_sume
CALL si METHOD TSuma:done

W rzeczywistości TASM tłumaczy sobie powyższy kod do formy:

mov si, offset s1
PUSH 03H
PUSH 04H
CALL +@TABLE_TSUMA|INIT
CALL +@TABLE_TSUMA|OBLICZ_SUME
CALL +@TABLE_TSUMA|DONE

Ciekawie nie??? :-).. Tasm posiada predefiniowane stałe, jedną z nich jest właśnie @TABLE_, która zawiera tablice metod danego obiektu. Przez nią możemy się odwoływać do statycznych metod. Oba zapisy są poprawne jednak ten pierwszy bardziej przyfomina forme języka wyższego poziomu.

4. Polimorfizm i metody wirtualne.

Polimorfizm (wielopostaciowość) odnosi się do sposobu wywoływania metod identyfikowanych tą samą nazwą w zależność od tego, który obiekt w hierarchii wywołuje metode polimorficzną. Czasami pokrywamy metody w obiektach dziedziczących nowymi procedurami, jednak zawsze możemy chcieć aby program wykonywał daną metodę rożnie w zależnosci od wywołania. Do tego celu można skonnstruować metody virtualne. Do metod statystycznych wszystkie odwołania tworzone są w czasie kompilacji programu czyli wykonywane są w sposób bezpośredni. Natomiast do metod metod virtualnych w czasie kompilacji dla każdego obiektu zawierającego takie metody tworzona jest tablica metod virtualnych (VMT). W tej tablicy przechowywane są adresy wszystkich metod virtualnych danego obiektu. Podczas wykonywania dowolnej procedury, którea zawiera odwołania do metod virtualnych następuje podstawienie w miejscu wywołań metod virtualnych, fizycznych adresów tych metod dla konkretnego obiektu w zależności od wywołania.

Co nam ułatwia w tym kierunku TASM.?
Jezeli zdeklarujemy metody virtualne, w objecie podstawowym musimy wstawic specjalne pole, które bedzie wskaźnikiem do VMT. Tasm posiada macro TBLPTR, które definiuje daną @MPtr_ o odpowiednim rozmiarze. Rozmiar wskaznika (adresu) związany jest z ulokowaniem VMT albo w tym samym albo i w innym segmencie.

STRUC TObject_Pierwszy METHOD {
init :dword = TObject_Pierwszy_Init
wypisz :dword = TObject_Pierwszy_Wypisz
virtual Podaj :dword = TObject_Pierwszy_Podaj}
TBLPTR
ENDS TObject_Pierwszy

Teraz musimy sobie umieścic gdzieś tablice VMT. Sluzy do tego makro TBLINST

SEGMENT VMT_SEG PUBLIC
TBLINST
ENDS VMT_SEG

a następnie ją zainicjować. W jeżykach wyższego poziomu słuzy do tego konstrukotr, który automatycznie inicujuje tą tablice w przypadku jego wywołania i stosowania metod virtualnych. My stwórzmy sobie własny konstruktor zawierający predefiniowane makro TBLINIT :

PROC TObject_Pierwszy_Init PASCAL
TBLINIT @Object
ret
ENDP TObject_Pierwszy_init

I tak wygląda cała ide utworzenia obiektu z metodami virtualnymi i VMT w tasmie. W rzeczywistości tasm tłumaczy sobie ten kod:

STRUC TOBJECT_PIERWSZY
init :dword = TObject_Pierwszy_Init
wypisz :dword = TObject_Pierwszy_Wypisz
virtual Podaj :dword = TObject_Pierwszy_Podaj}
@Mptr_TOBJECT_PIERWSZY DD ?
ENDS TObject_Pierwszy

Widzimy, ze makro TBLPTR zostało zastąpione przez @Mptr_TObject_Pierwszy o rozmiarz double word. Rozmiar taki wynika z tego, ze VMT bedzie umieszczane w osobnym segmencie VMT_SEG i potrzeba nam 32bit adres.

SEGMENT VMT_SEG PUBLIC
@TableAddr_TOBJECT_PIERWSZY @Table_TOBJECT_PIERWSZY {}
ENDS VMT_SEG

Tutaj widzimy, ze TBLINST zastąpione zostało przez @TableAddr_, która przechowuje adres VMT. Tablice VMT należy tworzyć i inicjować zaraz po deklaracji objectu zawierającego metody virutalne aby zachowana została struktura i poprawnośc kody przy kompilacji TASMEM. Mozemy ją inaczej umieszczać ale wtedy musimy odwoływać sie bezpośrednio do danych adresów nie używajać predefiniowanych makr.

Natomiast konstruktor i inicjalizacja VMT wygląda tak:

PROC TObject_Pierwszy_Init PASCAL
MOV [WORD PTR (TOBJECT_PIERWSZY).@MPTR_TOBJECT_PIERWSZY],OFFSET +@TableAddr_TOBJECT_PIERWSZY
MOV [WORD PTR ((TOBJECT_PIERWSZY).@MPTR_TOBJECT_PIERWSZY)+2h],SEG +@TableAddr_TOBJECT_PIERWSZY
RET 00000h
ENDP TObject_Pierwszy_init

Woowww..., skomplikowanie wygląda, ale zasada jest prosta..., do pierwszego dword ładowany jest offset tablicy VMT, natomiast do drugiego dword adres segmentu zawierającego VMT.
Kompilator inicjujac tablice VMT zapełnia ją wskaźnikami do odpowiednich metod w objekcie.

Do metod takiego obiektu odwołujemy się podobnie jak w przypadku metod statycznych, np. metoda statyczna odwołująca się do metody virtualnej tego samego obiektu:

PROC TObject_Pierwszy_Wypisz PASCAL
call (@Object) METHOD TObject_pierwszy:Podaj
ret
ENDP TObject_Pierwszy_Wypisz

ale TASM zupełnie inaczej tłumaczy sobie ten kod (rozpoznają wywołania metod virtualnych). Po tłumaczeniu bedzie to wyglądało tak :

PROC TObject_Pierwszy_Wypisz PASCAL
LES BX,[((TOBJECT_DRUGI)).@MPTR_TOBJECT_PIERWSZY]
CALL [(@TABLE_TOBJECT_PIERWSZY PTR ES:BX).PODAJ]
RET 00000h
ENDP TObject_Pierwszy_Wypisz

Tutaj instrukcja LES ładuje do ES:BX adres tablicy VMT a CALL już wywołuje metode Podaj w zależności od tego z jakiego obiektu została wywołana. Tutaj mamy jeszcze TOBJECT_DRUGI bo to jest ostatni obiekt w hierarchi z którego została wywołana.

To mniej więcej wszystko co istotne. Poniżej załączam gotowy przykład wykorzystujący metody virtualne:

;---------------------------------------------------------------------------
IDEAL
JUMPS
LOCALS @@
MODEL large, PASCAL
STACK 1000h


GLOBAL TObject_Pierwszy_Podaj :PROC
GLOBAL TObject_Pierwszy_Init :PROC
GLOBAL TObject_Drugi_Init :PROC
GLOBAL TObject_Drugi_Podaj :PROC
GLOBAL TObject_Pierwszy_Wypisz :PROC


STRUC TObject_Pierwszy METHOD {
init :dword = TObject_Pierwszy_Init
wypisz :dword = TObject_Pierwszy_Wypisz
virtual Podaj :dword = TObject_Pierwszy_Podaj}
TBLPTR
ENDS TObject_Pierwszy

SEGMENT VMT_SEG PUBLIC
TBLINST
ENDS VMT_SEG

CODESEG

PROC TObject_Pierwszy_Init PASCAL
TBLINIT @Object
ret
ENDP TObject_Pierwszy_init


STRUC TObject_Drugi TObject_Pierwszy METHOD {
init :dword = TObject_Drugi_Init
virtual Podaj :dword = TObject_Drugi_Podaj}
ENDS TObject_Drugi

SEGMENT VMT_SEG PUBLIC
TBLINST
ENDS VMT_SEG

CODESEG

PROC TObject_Drugi_Init PASCAL
TBLINIT @Object
ret
ENDP TObject_Drugi_init



PROC TObject_Pierwszy_Podaj PASCAL

mov dx, offset ds:dana1
mov ah,09h
int 21h
ret
ENDP TObject_Pierwszy_Podaj

PROC TObject_Drugi_Podaj PASCAL

mov dx, offset ds:dana2
mov ah,09h
int 21h
ret
ENDP TObject_Drugi_Podaj

PROC TObject_Pierwszy_Wypisz PASCAL
call (@Object) METHOD TObject_pierwszy:Podaj
ret
ENDP TObject_Pierwszy_Wypisz

DATASEG

obj1 TObject_Pierwszy < >
obj2 TObject_Drugi < >
dana1 db 0dh,0ah,'Pierwy$'
dana2 db 0dh,0ah,'Drugi $'


CODESEG
Start:
mov ax,@Data
mov ds,ax
mov si, offset obj1
call si METHOD TObject_Pierwszy:Init
call si METHOD TObject_Pierwszy:Wypisz
mov si, offset obj2
call si METHOD TObject_Drugi:Init
call si METHOD TObject_Drugi:Wypisz
Exit:
mov ah, 04Ch
mov al, 0
int 21h


END Start
;------------------------------------------------------------------------------

Komentarz:...

Program wywołuje metod Wypisz, jest ona zdefiniowana w objekcie podstawowmy i ona wywoluje metode virtualna Podaj rożnie zdefiniowaną w obiekcie podstawowym i dzidziczącym. W zależnośći który obiekt wywołuje tą samą metode Wypisz ona odpowiednio wywołuje metody Podaj na podstawie VMT.


Ta wersja FAQ jest narazie beta..., to są pierwsze wrażenia z zabawy w OOP z tasmem, wiec zawiera jeszcze wiele błedów i nieścisłoći. Wszelkie uwagi i kometarze lub pytania kierować na gustawkit@pcplus.com.pl

Wyszukiwarka

Podobne podstrony:
language oop
pierw asm
asm z5 psp n
Macros AvrStudio asm ini
A K5B10BCD ASM
VSS1 Flowchart programming mode English ASM 000 079 0 PZ464 T0131 00
a1 asm
asm avr
instrukcja TM cw02 ASM
vgastop asm
kod[waz ze zmiana asm]
Delphi OOP 4programmer
asm jurnal APJ 1
oop
asm lin sys
language oop

więcej podobnych podstron