Problem z asemblerem
Problem z asemblerem
Witam wszystkich.
Mam taki problem.
Jak chce zaprogramowac granie glosniczka w ukladzie z 8051, to wykorzystuje wczesniej przygotowana procedure obslugi tego dzwieku. Niestety, kiedy podaje do procedury czestotliowsc jako liczbe ( np. graj_dzwiek 64536) to dzwiek gra, natomiast jak zrobie poprzez zmienna to nie gra.
zmienna deklaruje:
czes: dw 64536
i w programie odwoluje sie do zmiennej
graj_dzwiek czes
Czy wie ktos moze jak sobie z tym poradzic?
Wydaje mi sie, ze problem lezy w przekazywaniu zmiennej do DPTR, ale doklanie nie wiem.
Z gory dziekuje za wasza pomoc.
Mam taki problem.
Jak chce zaprogramowac granie glosniczka w ukladzie z 8051, to wykorzystuje wczesniej przygotowana procedure obslugi tego dzwieku. Niestety, kiedy podaje do procedury czestotliowsc jako liczbe ( np. graj_dzwiek 64536) to dzwiek gra, natomiast jak zrobie poprzez zmienna to nie gra.
zmienna deklaruje:
czes: dw 64536
i w programie odwoluje sie do zmiennej
graj_dzwiek czes
Czy wie ktos moze jak sobie z tym poradzic?
Wydaje mi sie, ze problem lezy w przekazywaniu zmiennej do DPTR, ale doklanie nie wiem.
Z gory dziekuje za wasza pomoc.
Kod: Zaznacz cały
; ex_sound1.asm
$MOD51
Start CODE 9000h ; Rozpoczecie wykonywania programu
Pamzew XDATA 0F000h ; Adres poczatku zewnetrznej pamieci danych
Pamwew DATA 30h ; Adres poczatku zewnetrznej pamieci programu
XSEG AT Pamzew
DSEG AT Pamwew
CSEG AT Start
ljmp main_
$nolist
$include(lcd.inc)
$include(i2c.inc)
$include(std.inc)
$list
;Glowna czesc programuKod: Zaznacz cały
main_delay:
mov a,#5
main_1:
delay 0ffh
djnz acc,main_1
ret
main_:
soundondokl 64536 ;tu zamiast tej liczby wpisuje czes; 500Hz
call main_delay
soundon2dokl 65075 ; 1kHz
call main_delay
soundoffdokl
call main_delay
soundon2dokl 65203 ; 1.5kHz
call main_delay
soundon2dokl 65286 ; 2kHz
call main_delay
soundoffdokl
call main_delay
soundon2dokl 65336 ; 2.5kHz
call main_delay
soundon2dokl 65370 ; 3kHz
call main_delay
ret
end
Kod: Zaznacz cały
soundondokl MACRO freqn
push dpl
push dph
mov dptr,#freqn
call soundondokl_
pop dph
pop dpl
ENDM
Kod: Zaznacz cały
czes EQU 64536 Problem tkwi w tym, że ja muszę w programie napisać obsługę częstotliwości na podstawie tablicy, a deklaracja tablicy możliwa jest tylko (chyba?) w taki sposób:
tablica: dw 64536, 64075, 63536,.....
No i właśnie jest tu problem z odczytaniem wartości zmiennej.
Widzę, że kolega Jumbo gruntownie zmienił swój ostatni post, co w moim mniemaniu nie zmieniło to istoty problemu. Poprzednio nie bardzo wiadomo było w czym jest problem, program był bardzo niekompletny i niejasny w swoim działaniu. Jakieś operacje z zapisem do pamięci zewnętrznej kodu operacji LJMP z operandem będącym adresem czegoś o nazwie “soundinit_” rodzą pytania: dlaczego? w jakim celu? itp. Czy chcesz wykonać tą instrukcję? z zewnętrznej pamięci RAM? Chyba, że masz w jakiś sposób przemapowaną zewnętrzną pamięć i jest ona dostępna zarówno jako pamięć danych jak i pamięć kodu programu.
Po wymianie tekstu programu, pojawiła się jednak nadzieja na rozwiązanie problemu. Być może się mylę, bo to są jedynie moje przypuszczenia, których nie można odnieść do tekstu programu.
Sądzę, że problem tkwi w niezrozumieniu istoty zmiennych. Zmienna, jak sama nazwa wskazuje, może się zmieniać, czyli być modyfikowalną przez program, co oznacza, że znajduje się w obszarze pamięci danych. Jeżeli zostanie napisane:
czes: dw 64536
to nie jest utworzenie zmiennej a jedynie zapisanie do pamięci przeznaczonej na kod programu dwóch bajtów o określonej zawartości.
Użycie makroinstrukcji:
soundondokl 64536
oznacza, że zostaną wykonane następujące operacje:
push dpl
push dph
mov dptr,#64536
call soundondokl_
pop dph
pop dpl
czyli między innym do rejestru DPTR zostaje wpisana wartość operandu wywołania makroinstrukcji. Jeżeli zostanie napisane:
czes EQU 64536
...
soundondokl czes
to nie zmieni to swojej istoty działania. Identyfikator czes jest identyfikatorem stałej o wartości 64536, czyli kompilator wygeneruje identyczny kod. Istotny fragment rozwinięcia makroinstrukcji:
mov dptr,#czes
nie różni się od poprzedniego użycia, czes jest stałą o wartości 64536.
Znacząco inaczej zadziała następujący zapis:
czes: dw 64536
...
soundondokl czes
Tu mamy (w istotnym fragmencie makroinstrukcji):
mov dptr,#czes
ale czes jest etykietą (adresem) w pamięci programu, gdzie zapisana jest jakaś stała. W efekcie wykonania instrukcji, rejestr dptr będzie zawierał adres miejsca, gdzie jest zapisana stała a nie samą stałą a sądzę, że autorowi nie o to chodziło.
Jeżeli moje przypuszczenia są słuszne, to lekarstwem może być następujący zapis makroinstrukcji:
soundondokl MACRO freqn
push dpl
push dph
mov dptr,#freqn
mov a,#0
movc a,@a+dptr
push acc
mov a,#1
movc a,@a+dptr
push acc
pop dpl
pop dph
call soundondokl_
pop dph
pop dpl
ENDM
PS.
Na zakończenie,
Jumbo do czego służy istrukcja ret w poniższym fragmencie:
Po wymianie tekstu programu, pojawiła się jednak nadzieja na rozwiązanie problemu. Być może się mylę, bo to są jedynie moje przypuszczenia, których nie można odnieść do tekstu programu.
Sądzę, że problem tkwi w niezrozumieniu istoty zmiennych. Zmienna, jak sama nazwa wskazuje, może się zmieniać, czyli być modyfikowalną przez program, co oznacza, że znajduje się w obszarze pamięci danych. Jeżeli zostanie napisane:
czes: dw 64536
to nie jest utworzenie zmiennej a jedynie zapisanie do pamięci przeznaczonej na kod programu dwóch bajtów o określonej zawartości.
Użycie makroinstrukcji:
soundondokl 64536
oznacza, że zostaną wykonane następujące operacje:
push dpl
push dph
mov dptr,#64536
call soundondokl_
pop dph
pop dpl
czyli między innym do rejestru DPTR zostaje wpisana wartość operandu wywołania makroinstrukcji. Jeżeli zostanie napisane:
czes EQU 64536
...
soundondokl czes
to nie zmieni to swojej istoty działania. Identyfikator czes jest identyfikatorem stałej o wartości 64536, czyli kompilator wygeneruje identyczny kod. Istotny fragment rozwinięcia makroinstrukcji:
mov dptr,#czes
nie różni się od poprzedniego użycia, czes jest stałą o wartości 64536.
Znacząco inaczej zadziała następujący zapis:
czes: dw 64536
...
soundondokl czes
Tu mamy (w istotnym fragmencie makroinstrukcji):
mov dptr,#czes
ale czes jest etykietą (adresem) w pamięci programu, gdzie zapisana jest jakaś stała. W efekcie wykonania instrukcji, rejestr dptr będzie zawierał adres miejsca, gdzie jest zapisana stała a nie samą stałą a sądzę, że autorowi nie o to chodziło.
Jeżeli moje przypuszczenia są słuszne, to lekarstwem może być następujący zapis makroinstrukcji:
soundondokl MACRO freqn
push dpl
push dph
mov dptr,#freqn
mov a,#0
movc a,@a+dptr
push acc
mov a,#1
movc a,@a+dptr
push acc
pop dpl
pop dph
call soundondokl_
pop dph
pop dpl
ENDM
PS.
Na zakończenie,
Kod: Zaznacz cały
main_:
soundondokl 64536 ;tu zamiast tej liczby wpisuje czes; 500Hz
call main_delay
soundon2dokl 65075 ; 1kHz
call main_delay
soundoffdokl
call main_delay
soundon2dokl 65203 ; 1.5kHz
call main_delay
soundon2dokl 65286 ; 2kHz
call main_delay
soundoffdokl
call main_delay
soundon2dokl 65336 ; 2.5kHz
call main_delay
soundon2dokl 65370 ; 3kHz
call main_delay
ret
end
Dzieki za odpowiedz.
Tak, chodzilo o to, aby w DPTR znajdowala sie wartosc, a nie adres do niej.
Czlowiek uczy sie cale zycie... szukalem w roznych miejscach, ale jakos nigdzie nie znalazlem, ze takie odwolanie powoduje, ze zamiast wartosci mamy w rejestrze adres pod ktorym ta wartosc sie znajduje.
Raz jeszcze wielkie dzieki za pomoc. Mam nadzieje, ze juz sobie z tym do konca poradze
[ Dodano: 2007-01-02, 22:43 ]
No wiec tak.
Twoj sposob okazal sie skuteczny i rozwiazal jeden problem. Niestety pojawil sie nastepny.
A brzmi on tak:
w sekcji DSEG .... deklaruje zmienna
f: ds 1 ; jest to zmienna, do ktorej w trakcie trwania programu wpisywac chce dane
no i jak zrobie:
mov f, #5 ; to sie tam zadna wartosc nie pojawia.
Jaki jest tego powod? zle deklaruje ta zmienna?
Tak, chodzilo o to, aby w DPTR znajdowala sie wartosc, a nie adres do niej.
Czlowiek uczy sie cale zycie... szukalem w roznych miejscach, ale jakos nigdzie nie znalazlem, ze takie odwolanie powoduje, ze zamiast wartosci mamy w rejestrze adres pod ktorym ta wartosc sie znajduje.
Raz jeszcze wielkie dzieki za pomoc. Mam nadzieje, ze juz sobie z tym do konca poradze
[ Dodano: 2007-01-02, 22:43 ]
No wiec tak.
Twoj sposob okazal sie skuteczny i rozwiazal jeden problem. Niestety pojawil sie nastepny.
A brzmi on tak:
w sekcji DSEG .... deklaruje zmienna
f: ds 1 ; jest to zmienna, do ktorej w trakcie trwania programu wpisywac chce dane
no i jak zrobie:
mov f, #5 ; to sie tam zadna wartosc nie pojawia.
Jaki jest tego powod? zle deklaruje ta zmienna?
program wyglada następująco:
Kod: Zaznacz cały
$MOD51
Start CODE 9000h ; Rozpoczecie wykonywania programu
Pamzew XDATA 0F000h ; Adres poczatku zewnetrznej pamieci danych
Pamwew DATA 30h ; Adres poczatku zewnetrznej pamieci programu
XSEG AT Pamzew
DSEG AT Pamwew
i: ds 1
tmp1: ds 1
tmp2: ds 1
f: ds 2
tdph: ds 2
tdpl: ds 2
j: ds 2
CSEG AT Start
ljmp main_
$nolist
$include(lcd.inc)
$include(std.inc)
$list
;Glowna czesc programu
czes1: dw 1000
tab_f: dw
tab_t: dw
czes: dw 64536
;liczenie czest. ze wzoru dptr=65536-(10^6/(2f)), gdzie f - czestotliwosc w [Hz]
przeliczanie:
mov dptr, #czes1
mov a,#0
movc a,@a+dptr
push acc
inc i
mov a, #1
movc a, @a+dptr
push acc
pop tdpl
pop tdph
; 2f
mov a, #tdpl
add a, #tdpl
push acc
pop tdpl
mov a, #tdph
addc a, #tdph
push acc
pop tdph
;f1 :1000) * 256
mov a, #tdph
mov b, #100
div ab
mov b, #10
div ab
push acc
pop tdph
mov a, #tdph
mov b, #16
mul ab
push acc
pop acc
mov b, #16
mul ab
push acc
push b
pop dph
pop dpl
;f2 przez 1000
mov a, #tdpl
mov b, #100
div ab
mov b, #10
div ab
push acc
pop tdpl
mov a,#dpl
add a, #tdpl
push acc
pop dpl
mov a, dph
addc a, #0
push acc
pop dph
mov a, #250
mov b, #dpl
div ab
push acc
pop acc
mov b, #4
mul ab
push acc
push b
pop tmp1
pop tmp2
mov tdph, #3
mov tdpl, #232
mov a, #255
subb a, #tdpl
push acc
pop acc
add a, #1
push acc
pop dpl
mov a, #255
subb a, #tdph
push acc
pop dph
ret
main_delay:
mov a,#5
main_1:
delay 0ffh
djnz acc,main_1
ret
main_:
mov i,#0
;call soundondokl_
call przeliczanie
call soundondokl_
call main_delay
soundoffdokl
call main_delay
;soundondokl1 czes
;call main_delay
; soundoffdokl
; call main_delay
soundondokl 64535 ; 500Hz
call main_delay
soundoffdokl
call main_delay
ret
end
W przedstawionym programie nie znalazłem owej nieszczęsnej instrukcji mov f,#5 ani nie zostało wyróżnione żadne miejsce, które sprawia kłopoty. Zacytowana instrukcja oznacza, że do komórki pamięci wewnętrznej reprezentowanej symbolem f (która ma jakiś adres w pamięci, w tym przypadku jest to adres 33 hex) zostanie zapisana liczba o wartości 5. I procesor mając do wykonania taką instrukcję dokładnie tak zrobi, bo nie może inaczej. Jeżeli robi to inaczej, to należy wymienić procesor na inny, bo ten jest zepsuty.
Piszesz:
w sekcji DSEG .... deklaruje zmienna
f: ds 1 ;
jest to zmienna, do której w trakcie trwania programu wpisywać chce dane no i jak zrobię:
mov f, #5 ;
to sie tam żadna wartość nie pojawia.
Pytanie jest takie: skąd wiadomo, że się nie pojawia. Co znaczy, że się nie pojawia? A może zachodzi przypadek, że było przykładowo zero i zapisujesz zero.
Przyjrzałem się więc całości i ... nie nastraja to optymistycznie.
Piszesz:
; 2f
mov a, #tdpl
add a, #tdpl
push acc
pop tdpl
mov a, #tdph
addc a, #tdph
push acc
pop tdph
Zapewne chciałeś pomnożyć zawartość zmiennych tdph i tdpl przez 2 (swoją drogą dlaczego jest w obu przypadkach zapisane DS 2, czyli masz tdph i tdpl jako dwubajtową zmienną a używasz jako jednobajtowe). Jeżeli tak, to niestety realizacja znacząco rozminęła się z zamiarem. Wyszła rzecz następująca:
mov a, #tdpl ; wpisz do a adres zmiennej tdpl
add a, #tdpl ; dodaj do a (adres zmiennej tdpl) adres zmiennej tdpl
push acc ; zachowaj na stosie
pop tdpl ; pobierz do zmiennej zawartość szczytu stosu, czyli zmienna tdpl zawiera liczbę, która jest jej adresem pomnożonym przez 2.
Dalsza część (dotycząca tdph) jest identyczna.
inny fragment:
;f1 :1000) * 256
mov a, #tdph
mov b, #100
div ab
mov b, #10
div ab
push acc
pop tdph
masz zawartość jednobajtowego rejestru, do którego wpisano młodszą część adresu zmiennej finalnie podzieloną przez 1000 (podziel liczbę z zakresu 0..255 przez 1000 -> wynik wyjdzie zero).
Dalej nie przyglądałem się programowi, bo jest to bezcelowe. Cały program, w moim przekonaniu, zawiera dużo błędów wynikających z niewłaściwego rozumienia adresu zmiennej i zawartości zmiennej.
Kiedyś na tym forum napisałem:
(...) kompilator tłumacząc twój program żródłowy na postać binarną analizuje tekst i wyłapuje błędy. Należy pamiętać, że te błędy dotyczą spraw językowych, czyli zapisu. Jak kompilator zgłasza błąd, to znaczy, że zapis w programie zródłowym nie jest zgodny z regułami. (...) Zawsze można formalnie poprawnymi zapisami kazać prockowy robić głupoty. (...) program z bezwzględną dokładnością wykona każde twoje polecenie, nawet te najgłupsze i nawet 'mu powieka nie drgnie'.(...)
i tu chyba zaistniał ten przypadek.
Piszesz:
w sekcji DSEG .... deklaruje zmienna
f: ds 1 ;
jest to zmienna, do której w trakcie trwania programu wpisywać chce dane no i jak zrobię:
mov f, #5 ;
to sie tam żadna wartość nie pojawia.
Pytanie jest takie: skąd wiadomo, że się nie pojawia. Co znaczy, że się nie pojawia? A może zachodzi przypadek, że było przykładowo zero i zapisujesz zero.
Przyjrzałem się więc całości i ... nie nastraja to optymistycznie.
Piszesz:
; 2f
mov a, #tdpl
add a, #tdpl
push acc
pop tdpl
mov a, #tdph
addc a, #tdph
push acc
pop tdph
Zapewne chciałeś pomnożyć zawartość zmiennych tdph i tdpl przez 2 (swoją drogą dlaczego jest w obu przypadkach zapisane DS 2, czyli masz tdph i tdpl jako dwubajtową zmienną a używasz jako jednobajtowe). Jeżeli tak, to niestety realizacja znacząco rozminęła się z zamiarem. Wyszła rzecz następująca:
mov a, #tdpl ; wpisz do a adres zmiennej tdpl
add a, #tdpl ; dodaj do a (adres zmiennej tdpl) adres zmiennej tdpl
push acc ; zachowaj na stosie
pop tdpl ; pobierz do zmiennej zawartość szczytu stosu, czyli zmienna tdpl zawiera liczbę, która jest jej adresem pomnożonym przez 2.
Dalsza część (dotycząca tdph) jest identyczna.
inny fragment:
;f1 :1000) * 256
mov a, #tdph
mov b, #100
div ab
mov b, #10
div ab
push acc
pop tdph
masz zawartość jednobajtowego rejestru, do którego wpisano młodszą część adresu zmiennej finalnie podzieloną przez 1000 (podziel liczbę z zakresu 0..255 przez 1000 -> wynik wyjdzie zero).
Dalej nie przyglądałem się programowi, bo jest to bezcelowe. Cały program, w moim przekonaniu, zawiera dużo błędów wynikających z niewłaściwego rozumienia adresu zmiennej i zawartości zmiennej.
Kiedyś na tym forum napisałem:
(...) kompilator tłumacząc twój program żródłowy na postać binarną analizuje tekst i wyłapuje błędy. Należy pamiętać, że te błędy dotyczą spraw językowych, czyli zapisu. Jak kompilator zgłasza błąd, to znaczy, że zapis w programie zródłowym nie jest zgodny z regułami. (...) Zawsze można formalnie poprawnymi zapisami kazać prockowy robić głupoty. (...) program z bezwzględną dokładnością wykona każde twoje polecenie, nawet te najgłupsze i nawet 'mu powieka nie drgnie'.(...)
i tu chyba zaistniał ten przypadek.
Heh, to jak widze musze jeszcze sie duzo pouczyc.
Mam takie pytanko jeszcze:
Jak dziala polecenie subb?
W opisie pisze, ze jest to odejmowanie z pozyczka.
Czy jezeli mam liczbe 2 bajtowa, i odejme jedno bajtowa liczbe od dolnej czesci, to czy pozyczka zostanie pobrana od gornej czesci tej liczby 2 bajtowej, czy zostanie tylko ustawiony jakis znacznik mowiacy o tym, ze dokonano porzyczki?
Mam takie pytanko jeszcze:
Jak dziala polecenie subb?
W opisie pisze, ze jest to odejmowanie z pozyczka.
Czy jezeli mam liczbe 2 bajtowa, i odejme jedno bajtowa liczbe od dolnej czesci, to czy pozyczka zostanie pobrana od gornej czesci tej liczby 2 bajtowej, czy zostanie tylko ustawiony jakis znacznik mowiacy o tym, ze dokonano porzyczki?
Instrukcja SUBB A,<operand> realizuje następującą operację:
(A) = (A) - <operand> - C
czyli od zawartości akumulatora odejmowana jest liczba wynikająca z <opeand> oraz od wyniku jest odejmowany aktualny stan wskaźnika C (jeżeli C=0, to nie ma to żadnego efektu, jeżeli C=1, to od wyniku odejmowane jest jeszcze 1). Jak widać, w operacji uczesticzy wskażnik C. W wyniku odejmowania jest ustawiany nowy stan wskaźnika C (C=1 jeżeli nastąpiła pożyczka, C=0 w przeciwnym wypadku). Aby odjąć dwie liczby wielobajtowe należy:
* wyzerować wskaźnik C
* odjąć najmłodsze części liczby instrukcją SUBB
* odjąć kolejno starsze części liczby
....
* odjąć najstarsze części liczby.
Więcej na temat arytmetyki wielobajtowej (dodawania, odejmowanie, mnożenia, dzielenia, porównywania) znajdziesz w http://www.btc.pl/?id_prod=6832
Co prawda rozwiązania dotyczą procka AVR, ale jest tam opisana "filozofia", która jest niezależna od procków. Przy okazji znajdziesz tam opisany sposób dzielenia liczb dowolnej wielkości przez 10.
(A) = (A) - <operand> - C
czyli od zawartości akumulatora odejmowana jest liczba wynikająca z <opeand> oraz od wyniku jest odejmowany aktualny stan wskaźnika C (jeżeli C=0, to nie ma to żadnego efektu, jeżeli C=1, to od wyniku odejmowane jest jeszcze 1). Jak widać, w operacji uczesticzy wskażnik C. W wyniku odejmowania jest ustawiany nowy stan wskaźnika C (C=1 jeżeli nastąpiła pożyczka, C=0 w przeciwnym wypadku). Aby odjąć dwie liczby wielobajtowe należy:
* wyzerować wskaźnik C
* odjąć najmłodsze części liczby instrukcją SUBB
* odjąć kolejno starsze części liczby
....
* odjąć najstarsze części liczby.
Więcej na temat arytmetyki wielobajtowej (dodawania, odejmowanie, mnożenia, dzielenia, porównywania) znajdziesz w http://www.btc.pl/?id_prod=6832
Co prawda rozwiązania dotyczą procka AVR, ale jest tam opisana "filozofia", która jest niezależna od procków. Przy okazji znajdziesz tam opisany sposób dzielenia liczb dowolnej wielkości przez 10.
Wielkie dzieki za pomoc.
[ Dodano: 2007-01-06, 15:12 ]
No wiec pojawil mi sie kolejny problem.
Czy da sie jakosc wykryc czy liczba jest ujemna?
Zamienilem dzielenie na odejmowanie, i w niektorych przypadkach pojawia sie liczba ujemna, czy jest jakis prosty sposob na wykrycie, czy liczba jest ujemna?
[ Dodano: 2007-01-06, 15:12 ]
No wiec pojawil mi sie kolejny problem.
Czy da sie jakosc wykryc czy liczba jest ujemna?
Zamienilem dzielenie na odejmowanie, i w niektorych przypadkach pojawia sie liczba ujemna, czy jest jakis prosty sposob na wykrycie, czy liczba jest ujemna?
Jest kilka notacji pozwalających przedstawiać liczby ze znakiem. W przypadku mikroprocesorów (mikrokontrolerów) najczęściej używana jest notacja U2 (notacja z uzupełnieniem do 2). W tej notacji znakiem liczby jest najbardziej znaczący bit w liczbie. Jeżeli liczba jest jednobajtowa, to tym bitem jest b7, w przypadku liczby dwubajtowej, to znak liczby jest przechowtwany jako b15. Jeżeli ten bit jest ustawiony, to liczba jest ujemna. Jeżeli bit nie jest ustawiony, to liczba jest dodatnia lub równa zero. Oczywiste jest, że wtedy na samą liczbę przeznaczonych jest mniej bitów. W przypadku liczb jednobajtowych zakres liczb w kodzie U2 jest -128 ... +127.