Problem z asemblerem

To forum jest dla wszystkich pasjonatów wiecznie młodych mikrokontrolerów '51. Wymiana doświadczeń i pomoc dla początkujących w pisaniu programów zarówno w C, Asemblerze jak i BASCOM. Zapraszam znawców tematu, aby pomogli wszystkim początkującym!
ODPOWIEDZ
jumbo
-
Posty: 6
Rejestracja: 30 gru 2006, 17:10
Lokalizacja: Gliwice
Kontakt:

Problem z asemblerem

Post autor: jumbo » 30 gru 2006, 17:17

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.

ZbeeGin

Post autor: ZbeeGin » 30 gru 2006, 18:25

A jakiś bezpośredni fragment kodu? Bo na razie to nic nie widać.

jumbo
-
Posty: 6
Rejestracja: 30 gru 2006, 17:10
Lokalizacja: Gliwice
Kontakt:

Post autor: jumbo » 01 sty 2007, 13:05

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 programu
czes: dw 64536 ;a tu ta zmienna deklaruje

Kod: 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
no i w miejscu dokladnie soundondokl zamiast liczby podaje zmienna, a procedura ta wyglada nastepujaco:

Kod: Zaznacz cały

soundondokl MACRO freqn
	push dpl
	push dph
	mov dptr,#freqn
	call soundondokl_
	pop dph
	pop dpl
ENDM
Zauważyłem również, że jak zadeklaruje zmienną czes w następujący sposób:

Kod: Zaznacz cały

 czes EQU 64536 
to także działa poprawnie.

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.

Awatar użytkownika
gaweł
Użytkownik
Posty: 651
Rejestracja: 05 kwie 2004, 8:38
Lokalizacja: Białystok
Kontakt:

Post autor: gaweł » 01 sty 2007, 21:47

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, :arrow: Jumbo do czego służy istrukcja ret w poniższym fragmencie:

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 

jumbo
-
Posty: 6
Rejestracja: 30 gru 2006, 17:10
Lokalizacja: Gliwice
Kontakt:

Post autor: jumbo » 01 sty 2007, 22:27

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?

Awatar użytkownika
gaweł
Użytkownik
Posty: 651
Rejestracja: 05 kwie 2004, 8:38
Lokalizacja: Białystok
Kontakt:

Post autor: gaweł » 03 sty 2007, 1:11

jumbo pisze: Jaki jest tego powod? zle deklaruje ta zmienna?
Pokaż to w formie programu.

jumbo
-
Posty: 6
Rejestracja: 30 gru 2006, 17:10
Lokalizacja: Gliwice
Kontakt:

Post autor: jumbo » 03 sty 2007, 6:09

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

Awatar użytkownika
gaweł
Użytkownik
Posty: 651
Rejestracja: 05 kwie 2004, 8:38
Lokalizacja: Białystok
Kontakt:

Post autor: gaweł » 04 sty 2007, 0:24

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.

jumbo
-
Posty: 6
Rejestracja: 30 gru 2006, 17:10
Lokalizacja: Gliwice
Kontakt:

Post autor: jumbo » 05 sty 2007, 10:01

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?

Awatar użytkownika
gaweł
Użytkownik
Posty: 651
Rejestracja: 05 kwie 2004, 8:38
Lokalizacja: Białystok
Kontakt:

Post autor: gaweł » 05 sty 2007, 10:21

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.

jumbo
-
Posty: 6
Rejestracja: 30 gru 2006, 17:10
Lokalizacja: Gliwice
Kontakt:

Post autor: jumbo » 05 sty 2007, 11:29

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?

Awatar użytkownika
gaweł
Użytkownik
Posty: 651
Rejestracja: 05 kwie 2004, 8:38
Lokalizacja: Białystok
Kontakt:

Post autor: gaweł » 07 sty 2007, 23:03

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.

ODPOWIEDZ