forum speccy.pl
ZX Spectrum => PROGRAMOWANIE => Wątek zaczęty przez: tdu w 2015.04.12, 21:55:31
-
Mam parę drobnych programów do napisania i znudziło mi się pisanie tego na piechotę.
Wybór padł na zachwalane pasmo. Udało się pobrać wersje 052.
W notatniku napisałem parę prostych rozkazów dla testów, zapisałem jako .asm.
Uruchomiłem pasmo x.asm x.bin, i o dziwo wygenerował mi plik x.bin.
Tylko co dalej można z tym zrobić ?
Żaden emulator tego nie akceptuje.
Nie udało mi się uruchomić z parametrem --tap aby wygenerował odpowiedni plik.
Liczę na pomoc mistrzów assemblera w tak trywialnej sprawie.
Jak powinien być prawidłowo zbudowany plik do asemblacji ?
Jak uzyskać plik TAP ?
-
Rozwiązaniem problemu jest:
pasmo --tapbas x.asm x.tap
jeżeli na końcu źródła będzie linijka:
end adres_startowy
wtedy pasmo wygeneruje loader basicowy wraz z RANDOMIZE USR adres_startowy.
Przykład:
org 32768
out (254),a
ret
end 32768
To samo, tylko z labelem:
org 32768
start:
out (254),a
ret
end start
-
Dzięki za szybką odpowiedź, jutro potestuje.
Zdaje mi się że w pliku zródłowym musi być na początku np. org 32768
i coś na końcu ?
A swoją drogą przydałaby się jakaś krótka instrukcja po polsku dla początkujących.
-
Dodałem przykład z 'end' w moim poprzednim poście.
Pasmo dla początkujących - będzie w jednym z artykułów, które mam nadzieję do wakacji się ukażą.
-
Faktycznie, w przykładach to jest.
-
Zrobiłem kilka prób z takim wywołaniem:
pasmo -tapbas p1.asm p1.tap
kod wynikowy, w czesci code ma długość 2 bajty i nie odzwierciedla tego co wpisałem do pliku p1.asm,
deasemblacje zrobiłem w programie SCADM.
Twoje przykłady, również dają ten sam efekt.
-
Dodaj opcję -d, a będziesz widział listing wygenerowanego kodu wraz z adresami pamięci.
przykład:
$ pasmo -d --tapbas tdu.asm tdu.tap
ORG 8000
8000:D3FE OUT (FE), A
8002:C9 RET
8003: END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 8002
przy czym od $8000 do $8002 to są 3 bajty ($8000, ...1, ...2)
-
Do pliku .asm wpisałem Twój pierwszy przykład.
Po uruchomieniu pasmo -d --tapbas p1.asm p1.tap
jest komunikat
ORG FFFF
Emiting TAP basic loader
Emiting TAP fromm FFF to 0000
i czesc code w TAP ma 2 bajty (2 zera)
-
Hm.. ORG $FFFF ?
-
W pliku asm jest to:
org 32768
out (254),a
ret
end 32768
komunikaty pasma, na obrazku
-
A takie coś działa:
org 50000
ld a,255
ld (16384),a
ret
end 50000
-
Taki sam efekt uzyskałem:
$ pasmo -d --tapbas p1.asm p1.tap
ORG 8000
Emiting TAP basic loader
Emiting TAP from FFFF to 0000
kiedy plik p1.asm zawiera jedynie dyrektywe ORG:
$ cat p1.asm
org 32768
-
A po co przed pasmo jest znak $
u mnie wtedy wywala błąd.
-
'$' to 'znak zachęty'. Zrobiłem copy&paste z konsoli tekstowej.
-
Parę różnych przykładów wklepałem i cały czas mam znowu
asemblację od FFFF do 0000, czyli 2 bajty
np. tutaj:
org 50000
ld hl,22528
ld a,0
loop1 ld (hl),a
inc hl ;zwieksz
inc a ;zwieksz
cp 32 ;porownuje akumulator z liczba 32
jp nz,loop1 ;jesli flaga zero skasowana wykonaj skok do loop1
ret
end 50000
Gdzie jest błąd ?
-
Coś źle robisz... Ściągnąłeś niewłaściwą/uszkodzoną wersję pasmo... i jeszcze kilka innych przyczyn.
Skopiowałem twój kod i assembluje się bez najmniejszych problemów. Potem żeby nie było, że mam jakąś extra wersję (mam 0.5.3 kompilowane ze źródeł i odpalane pod Linuksem) ściągnąłem binarną wersję 0.5.2 dla windows (http://pasmo.speccy.org/#down a konkretny link: http://pasmo.speccy.org/bin/pasmo-0.5.2.zip), rozpakowałem i odpalam używając Wine - też assembluje bez problemów. Na koniec odpaliłem jeszcze Windows XP w virtualboksie i tam też assembluje bez żadnych problemów.
Coś musi być po twojej stronie.
-
Strzelam w ślepo ale może jest jakiś problem z tą wersją Pasmo i twoją wersją Windows / komputera
Częsty problem przy różnych programach to maszyna 32-bitowa vs 64-bitowa.
Na Pasmo się nie znam, gdybyś chciał spróbować alternatywy - Sjasm to daj znać :)
-
Pasmo używam pod XP, ale wieczorem spróbuje pod win7.
Pobrałem wersję 052, wersji 053 nie udało się pobrać.
Ważna informacja że u was się kompiluje, będe walczył dalej.
Trochę mnie ten assembler zniechęcił, zamiast uczyć się algorytmów
to męcze się z nim i tracę czas.
@rafalm - chętnie spróbuję na Sjasm, szukałem w internecie ale nie znalazłem
-
Pasmo sciągniete z http://pasmo.speccy.org/bin/pasmo-0.5.4.beta2.zip
Zrobiłem copy&paste kodu, który został umieszczony.
Testowane pod W7, 64bit, pasmo jest skomplilowane jako 32bit:
pasmo -d --tapbas p2.asm p2.tap
ORG C350
C350:210058 LD HL, 5800
C353:3E00 LD A, 00
C355: label loop1
C355:77 LD (HL), A
C356:23 INC HL
C357:3C INC A
C358:FE20 CP 20
C35A:C255C3 JP NZ, C355
C35D:C9 RET
C35E: END C350
Emiting TAP basic loader
Emiting TAP from C350 to C35D
Sprawdzenie zawartości .tap
dirtap.exe p2.tap
ZX Spectrum TAP format analyzer 1.04a (c) 1996 Busy soft
Linux port and safe endian correction (c) 2002 Tritol
File: p2.tap (length 135 bytes)
Offset Len Flg Type Name Len Add Bas
------- ------- --- ---- ---------- ------ ----- -----
0 17 0 prog loader 71 10 71
21 71 255 body
96 17 0 code p2.tap 14 50000 32768
117 14 255 body
------- ------- --- ---- ---------- ------ ----- -----
135 127 End of p2.tap 85 Items:4
-
Pobrałem nową wersję 054 uruchomiłem na tym samym przykładzie
i mam to samo czyli, błąd.
A pracuje na innym komputerze niż rano.
Wieczorem sprawdzę na win7.
-
pasmo 0.5.4.2 pod linuksem - kompiluje się bez problemu.
Natomiast sugerowałbym w dyrektywie END wykorzystać etykietę, a nie adres bezpośredni.
Może to pomoże...
org 50000
start:
ld hl,22528
ld a,0
loop1 ld (hl),a
inc hl ;zwieksz
inc a ;zwieksz
cp 32 ;porownuje akumulator z liczba 32
jp nz,loop1 ;jesli flaga zero skasowana wykonaj skok do loop1
ret
end start
-
A moze zamiast ekranow lepiej zalaczyc plik asm ?
-
@Gryzor - ten plik jest jawny, zobacz post Steeva
No myślę Panowie że sprawa się wyjaśniła.
Używałem notatnika windowsowego i w pewnym momencie zainstalowałem notepad2
bo koloruje składnie i numeruje wiersze i chyba w tym momencie zaczęły się problemy.
Przeszedłem na edytor który normalnie używam do programowania PHP.
Trzeba zrobić ctrl+c, i ctrl+v do nowego pliku, zapisać i zaczęło działać.
Zapisuje jako program w C, daje rozszerzenie asm i jest ok.
Dziękuje wszystkim za zainteresowanie problemem i za pomoc.
Jeszcze muszę sprawdzić czy rzeczywiście w poprzednim notatniku jest dobrze,
czy to tylko notepad2 tak miesza, a może wystarczy go gdzieś przekonfigurować.
No i jeszcze w hexedytorze sprawdze co on tam do pliku dołącza.
-
Chyba powinieneś w Notepadzie zaznaczyć zapisywanie w czystym tekście bez windowsowych wodotrysków.
Pozdrawiam
jpomiar
-
Po analizie w hexedytorze okazało się że pasmo wymaga aby każdy koniec wiersza miał CR+LF ew tylko LF,
jeśli będzie to tylko CR to będzie problem taki jak u mnie.
Większość edytorów ma takie opcje, wystarczy odpowiednio ustawić.
W załączniku dwa pliki jeden daje się kompilować, drugi nie.
-
Fajnie że udało się prawidłowo skonfigurować edytor.
-
Fajnie działa. Jak na razie naciąłem się na jedną rzecz.
Nie można w etykietach używać słów: LOW i HIGH.
Muszą być do czegoś zastrzeżone.
-
oczywiście, że są zastrzeżone - to są operatory zwracające odpowiednio dolną i górną połówkę dwubajtowej wartości. Sugeruję zacząć od przeczytanie stosownej dokumentacji: http://pasmo.speccy.org/pasmodoc.html ;)
-
Wlasnie dlatego chodzilo mi o zalaczeniu oryginalnego pliku.
A jako ciekawostka, widzialem kiedys jak "przetestowany" skrypt rozwalil kawal sporego systemu, bo nie uwzglednil lokalizacji OSa. Niestety w informatyce nic nie jest oczywiste, nawet pojecie "to samo" ;-)
-
Potrzebuję procedurę opóźniającą ok 100ms.
Jako podprogram, do wklejenia w pasmo.
Może mi ktoś na szybko coś wymyśli, bardzo proszę !
-
LD BC,500
Loop:
DEC BC
LD A,B
OR C
JP NZ,Loop
Poeksperymentuj i dobierz sobie BC odpowiednie do twoich potrzeb
-
Dzięki, działa.
Bez tego opóźnienia za szybko działa i efekty pracy uciekają.
Miałem bardzo podobnie tylko zamiast DEC dałem INC i nie chciało działać.
-
Taka wprawka, udało się zatrzymać pasy na boderze.
org 40000
petla ld a,2
out (254),a
call time
ld a,6
out (254),a
call time
jp petla
ret
time
LD BC,1435
Loop
DEC BC
LD A,B
OR C
JP NZ,Loop
ret
end 40000
-
A co to ma być? Bo 100ms do 'testowej pętli' na czuja ma się niestety nijak.
-
To tylko zabawa z borderem żeby przetestować opóźniacz.
Na razie się uczę i to wszystko takie proste programiki dla nauki.
A w tym opóźniaczu da się uzyskać 1ms i 100, a pewnie i więcej da radę.
Mam jeszcze kilka innych ale nie będę was zanudzał takimi prymitywnymi rzeczami.
-
Border jest "rysowany" co 20 ms, więc po co Ci takie duże opóźnienie? :D
-
Temat opóźnienia czasowego jest w sumie dość ciekawy temat, a i w miarę łatwy do wyliczenia.
Inną sprawą jest zabawa borderem i multicolorem. Tu raczej nie operujemy czasem wyrażonym w (mikro/mili)sekundach, lecz w taktach procesora, a przynajmniej w ten sposób podawane są wszelkie opóźnienia i czasy trwania/przebiegów rastra.
-
Jako parametr opóźnienia do rejestrów BC musiałem wpisać 1435.
Dało to stojący border.
A procedura jest uniwersalna, można ją do wszystkiego zastosować.
-
100 ms (rozmiem że milisekund) czyli 0,1 sekundy to na pewno nie jest.
Spectrum wyświetla 50 ramek na sekundę a ty zmieniasz jeszcze w każdej ramce kilka razy kolor bordera więc powiedzmy robisz 50*5=250 zmian na sekundę. Czyli opóźnienie byłoby około 1000/250=4 milisekundy.
Ale jak słusznie powiedziano nikt w takich sytuacjach nie posługuje się milisekundami tylko czymś co u nas nazywa się taktami a po angielsku T-states.
Oczywiście można nie mieć pojęcia o taktach a i tak uzyskać metodą eksperymentowania efekt, co właśnie udowodniłeś :)
Osobiście odradzałbym ci teraz wchodzenie w multicolory, to nie są najlepsze zagadnienia na początek nauki. Zaraz zderzysz się z liczeniem taktów dla każdej instrukcji, przerwaniami, różnymi modelami Spectrum itp.
-
To była tylko wprawka, w zadne bardziej zaawansowane sprawy na razie nie wejdę.
Na razie utknąłem w takim miejscu:
ld c,10
petla
dec c
ld a,c
cp a
jp nz,petla
ret
petla ma sie wykonac 10 razy i z niej wyjsc
według mnie to po 10 dekrementacji gdy c osiągnie 0 powinna wyjsc
a nie wychodzi.
gdzie jest błąd?
takie cos stworzylem:
-
Trochę przedobrzyłeś :)
Rozkaz dekrementacji rejestru 8-bitowego sam zmienia stan znacznika Z, więc jeśli do niczego innego jak tylko do sprawdzenia stanu rejestru C nie jest potrzebna zawartość akumulatora, to fragment
ld a,c
cp a
jest zupełnie niepotrzebny.
Znacznik Z będzie ustawiony, gdy C będzie równe 0 od razu po DEC C.
A błąd tkwi w tym, że CP A zawsze daje Z=1, bo A zawsze jest równe A.
-
Niestety pewne rzeczy w asemblerze Z80 są nieintuicjne.
Taka np instrukcja DEC BC nie zmienia w ogóle flagi zero.
Stąd ta cała dziwaczna konstrukcja:
DEC BC
LD A,B
OR C ; ustawia zero tylko gdy B=0 i C=0 czyli gdy BC=0
JP NZ,nnnn
Natomiast instrukcje na jednym rejestrze ustawiają flagę zero więc wystarczy
DEC C ;ustawia flagę zero na tak gdy C=0
JP NZ,nnnn
Poszukaj sobie w sieci listy instrukcji Z80 i zobacz jaki mają wpływ na flagi. Na początek wystarczą ci flagi zera i przeniesienia - to ich się używa w ponad 90% przypadków.
A CP N działa tak: od rejestru A odejmuje "na niby" rejestr N, patrzy jaki jest wynik i odpowiednio ustawia flagi. Samego wyniku odejmowania nigdzie nie zapisuje. Robiłeś CP A czyli "odejmij A od A" czego wynik jest zawsze zerem.
-
Nie wiem, czy Tygrys już naszą Wiki upublicznił, ale aż szkoda, żeby się marnowało.
http://speccy.pl/wiki/index.php?title=Lista_rozkaz%C3%B3w_-_alfabetycznie (http://speccy.pl/wiki/index.php?title=Lista_rozkaz%C3%B3w_-_alfabetycznie)
-
Dziękuje za wyczerpującą odpowiedź. Będę zaraz testował.
Jedna rzecz rzuciła mi się w oczy, w komentarzu Rafała
LD A,B
OR C ; ustawia zero tylko gdy B=0 i C=0 czyli gdy BC=0
funkcja logiczna OR to jest po polsku "lub",
a "i" to jest AND
Na chłopski rozum to powinno być:
LD A,B
AND C
więć albo w programie błąd, albo w komentarzu.
No ale program przecież działa ...
-
Jest dobrze.
Suma logiczna OR daje w wyniku 0, jeśli oba argumenty są zerem i 1 jeśli dowolny z argumentów jest jedynką.
Iloczyn logiczny AND daje w wyniku 1, jeśli oba argumenty są jedynkami i 0 jeśli dowolny z argumentów jest zerem.
Ty chcesz wiedzieć, czy WSZYSTKIE sprawdzane bity są ZERAMI, więc potrzebujesz wynik z ich SUMY.
-
Rozumiem.
Pętla zadziałała gdy ustawiłem akumulator
ld a,0
dec c
cp c
jp nz,start
-
Spróbuj tak:
ld c,10
loop: dec c
jp nz,loop
Akumulator i CP C są zbędne w tym przypadku.
-
Faktycznie, też działa.
A nie angażuje akumulatora.
-
Nie przejmuj się. Znam kilku zawodowych programistów z młodszego pokolenia, którzy też nie rozumieją dlaczego :)
Mało kto próbuje dzisiaj zrozumieć zasady działania procesora.
W przypadku krótkiego skoku w zakresie -128 do +127 bajtów możesz użyć instrukcji JR zamiast JP.
Różnią się ilością bajtów i czasem wykonania. Czasami potrzeba zaoszczędzić miejsca (wtedy JR, jeśli krótki skok), a czasami czasu (JP wykona się szybciej, bo cel skoku jest podany wprost i nie trzeba go wyliczać).
-
Różnią się ilością bajtów i czasem wykonania. Czasami potrzeba zaoszczędzić miejsca (wtedy JR, jeśli krótki skok), a czasami czasu (JP wykona się szybciej, bo cel skoku jest podany wprost i nie trzeba go wyliczać).
Istotne jest też to, że każde JP (za wyjątkiem (HL)) wykonuje się zawsze tak samo długo - 10 taktów, a JR zależnie od warunku 12 (jeśli spełniony) albo 7 (jeśli niespełniony) - jak potrzebujesz stabilnych czasowo algorytmów to czasem warto stracić jeden bajt na rozkazie ;)
-
Należy zauważyć, że JR i JP nie są w pełni redundantne. Różnice, oprócz czasów trwania, występują w warunkach wykonania skoku.
-
Akumulator przechowuje liczby do 65535
wykonałem operacje
add a,c ;czyli 16384+15
i teraz jak to do pamięci zapisać, bo
ld (a), b ;takiej operacji nie ma
a chciałbym pod wyliczony adres wpisać zawartość rejestru b
-
Od kiedy akumulator jest 16-bitowy? :D
-
no to dałem plamę :-[
-
Za 16-bitowy "akumulator" pracuje w Z80 para rejestrów HL.
-
zrobilem teraz tak
ld hl,16384
ld c,15
ld b,0
add hl,bc
ld b,128
ld (hl),b
i chyba działa
-
Znalazłem świetną książkę ABC asemblera Rolanda Wacławka
Leżała w kącie przez lata bo na okładce jest zdjęcie C64
przez przypadek zajrzałem, a tam mnóstwo przykładów z Z80 (wyłącznie)
-
Wersja skrócona jest taka:
ld hl,16384
ld bc,15
add hl,bc
ld (hl),128
-
ld c,15
ld b,0
Krócej będzie:
ld bc,15
-
jeśli wartość, którą chcesz dodać jest 8-bitowa i nie chciałbyś niszczyć zawartości bc (bo np. używasz jej jeszcze do czegoś innego), to zamiast:
ld bc,nnnn
add hl,bc
możesz użyć:
ld a,l
add a,nn ; teraz a = l + twoja_wartość
ld l,a ; zapisujesz zmiany w l
adc a,h ; jeśli suma przekroczyła 255, to zapalona została flaga carry - dodajesz wartość flagi do h (1 lub 0) [a=l+h+carry]
sub l ; aby a=nowe_h, musimy odjąć l (bo obecnie jest a=l+nowe_h)
ld h,a
zabiera 1 bajt więcej i jest ciut wolniejsze, ale jeśli potrzebujesz bc lub de do czegoś innego to bardzo się ten kod przydaje...
Inną możliwością jest zachowanie bc na stosie i przywrócenie poprzedniej wartości po dodawaniu, ale to jest bardziej kosztowne...
Poniżej odejmowanie 8-bitowej wartości od 16-bitowej wartości w hl:
ld a,l
sub nn
ld l,a
sbc a,l
add a,h
ld h,a
-
Ciekawe muszę to przemyśleć.
Właśnie po dodawaniu robi mi się błąd.
Chyba na debuggerze muszę spróbować zrobić test.
W spectaculatorze chyba jest niezły, a może są inne sposoby
na krokową pracę i obserwowanie rejestrów ?
-
Nadszedł wreszcie czas żeby pokazać mój pierwszy program napisany w asemblerze Pasmo.
Dla starych wyjadaczy pewnie będzie to prościutki programik, ale mi zajął tydzień czasu.
Musiałem rozwiązać wiele problemów a teraz już widzę że drugi raz zupełnie inaczej bym go napisał.
Najgorszy jest pierwszy krok, trzeba przestawić się na inny tok myślenia niż w Basicu.
W miarę posuwania się pracy idzie coraz łatwiej.
Tak naprawdę to liznąłem zaledwie wierzchołek góry.
W każdym razie Pasmo to wdzięczny asembler, pokazuje miejsce popełnienia błędu i ku mojemu
zdziwieniu można pracować w układzie dziesiętnym.
(Chociaż przy adresowaniu ekranu, hex też się przydał)
-
Bravo! Naprawdę jak na pierwszy program to jest coś.
I teraz mam do Ciebie WIELKĄ prośbę.
Ponieważ to Twój pierwszy program to na pewno wszedł Ci mocno za skórę i dobrze go pamiętasz.
Czy mógłbyś to rozpisać na składowe - w sensie na mnemoniki z komentarzem ?
Fajnie by było jakbyś napisał coś o problemach które się pojawiły po drodze i jak to omijałeś.
Byłby to wspaniały sposób na pozwolenie innym (początkującym) by odrobinę zbliżyli się do Twego poziomu :D
Oczywiście to ostatnie zdanie jest troszkę "liżydupne" ale dla mnie jesteś MAESTRO!
-
Byłby to chyba przykład jak nie należy programować.
Generalnie jest główna pętla z której wywoływane są podprogramy.
W głównej pętli testowany jest stan klawiszy i w zależności od nich uruchamiane są
różne podprogramy.
Pokazujący pozycje kropki.
Obliczający pozycje o jeden w lewo, prawo, góra,dół.
Ze względu na specyficzną organizację ekranu nie jest to proste.
Opóźnienie czasowe, żeby kropka za szybko nie latała.
Kasowanie kropki na starej pozycji.
A najwięcej czasu straciłem na podprogramie do kasowania kropki w starej pozycji.
Nie rozwiązałem do końca wypisywania tekstu.
Za diabła nie działa mi dyrektywa DEFB z której mają być pobierane teksty.
Napisy zrobiłem trochę na piechotę, bardzo nieelegancko.
A na samym starcie podprogram do kasowania ekranu, zamalowania na żółto, ustawienia kropki na środku.
Tak sobie myśle że można by z tego zrobić grę, przesuwanie kropki ze startu do mety, omijanie przeszkód i pomiar czasu.
Byłyby liczone kolizje i czas.
-
Za diabła nie działa mi dyrektywa DEFB z której mają być pobierane teksty.
W GENS służyła do tego dyrektywa DEFS.
-
Po przeczytaniu dokumentacji gensa i paru próbach
zadziałało mi coś takiego
tylko nie wiem czemu zamiast spacji wyświetla mi znak funta
ld hl,tekst
sko3
push af
call 16
pop af
inc hl
ld a,(hl)
cp 0
jp nz,sko3
ret
tekst DEFB ' ','P','r',"o","g","r","a","m",0
-
Spacji? Jakiej spacji?
OK, już zauważyłem :) W akumulatorze masz na początku przypadkową zawartość, rozkaz LD A, (HL) powinien być przed callem.
Nie wiem natomiast, czemu używasz CALL 16 zamiast RST 16 oraz CP 0 zamiast OR A, jak również JP zamiast JR :)
-
Rzeczywiście przegapiłem ustawienie akumulatora, już działa.
CALL 16 bo gdzieś wyczytałem że tak trzeba, nie wiem co to za różnica.
A reszta zarzutów to kwestia mojego niedouczenia i potrzeba dalszej optymalizacji.
-
Wspaniale! A jak już dojdziecie do wszystkiego to będzie można liczyć na jakieś "linia-po-linii" wytłumaczenie łopatologiczne
działania całego programu? Wiem, że to nie jest kwestia kwadransa ale to by naprawdę dużo wniosło do tematu nauki
asemblera przez wielu naszych użytkowników.
-
CALL 16 bo gdzieś wyczytałem że tak trzeba, nie wiem co to za różnica.
Serio? To chyba jednak korzystasz ze złych źródeł. Załączam stronę z "Przewodnika po ZX Spectrum".
-
Tę książkę też mam.
Call 16 jest w ABC asemblera z 1988, Rolanda Wacławka,
no sam tego nie wymyśliłem.
On daje tam wtrącenia o CPM i procesorze 8080,
może to chodziło o pracę ZX po CPM.
Nie mam w tej chwili jej pod ręką żeby to sprawdzić.
Działa w każdym razie w obu opcjach, a jaka jest różnica
to muszę dopiero się wczytać.
-
@tdu: jak na pierwszy raz to jest bardzo dobrze.
To co mogę zaproponować z optymalizacji programu to zamiana 'CP 0' na 'OR A'.
W tym przypadku robi dokładnie to samo, tj, jest 1 bajt krótsze oraz szybsze - 4 takty zamiast 7.
@ikci: uczysz się assemblera?
-
Tę książkę też mam.
Call 16 jest w ABC asemblera z 1988, Rolanda Wacławka,
no sam tego nie wymyśliłem.
Być może chodziło o czytelność kodu, ale generalnie CALL16 nie ma sensu. RST 16 zajmuje jeden bajt zamiast trzech i wykonuje się szybciej.
Procesor 8080 też miał instrukcje restartów, tylko inaczej zapisywane w assemblerze (RST 0..7). Ale kody instrukcji były identyczne.
-
@ikci: uczysz się assemblera?
Kilka razy już o tym pisałem na naszym forum, że się uczę.
Każdy kawałek wiedzy jaki tutaj uzyskam jest dla mnie cenny i dlatego naciskam na
kolegę tdu aby to jakoś rozpisał dokładnie z wytłumaczeniem działania ponieważ
będzie to stanowiło niebywale przydatny podręcznik. Jeśli coś jest pisane na ciepło
z uwzględnieniem emocji jakie towarzyszyły przy powstawaniu pierwszego programu
to jest to najlepsza szkoła.
-
Ikci, a tak zapytam - jak się uczysz?
Odpaliłeś komplilator? Napisałeś SWÓJ pierwszy,drugi, trzeci i czwarty program? Jeśli nie to dlaczego?
Od czytania forum się nie nauczysz, tak jak nie nauczysz się tańczyć oglądając "Taniec z gwiazdami" czy grać w piłkę oglądając mecze. Można jedynie zostać teoretykiem - gawędziarzem.
Programować można się jedynie nauczyć poprzez... no właśnie, programowanie :)
-
Programowanie czyli czynność umysłowa, w odróżnieniu od czynności fizycznych wymienionych przez Ciebie, wymaga
odrobiny teorii, a najlepszym źródłem tej teorii jest właśnie analiza programu dokonana przez jego twórcę.
-
Zabierasz się jak pies do jeża.
Ale jeśli tak chcesz, uczyć się na sucho, ze słabego kodu i jeszcze przy tym jakieś cudze emocje analizować to cóż, rób po swojemu ;)
-
Dziękuję za radę.
-
Kolejny problem,
w akumulatorze jest jakaś liczba
jak ją dziesiętnie wyświetlić na ekranie
kombinuje od godziny, ale to chyba strata czasu
są na to pewnie jakieś gotowce
-
Kolejny problem,
w akumulatorze jest jakaś liczba
jak ją dziesiętnie wyświetlić na ekranie
kombinuje od godziny, ale to chyba strata czasu
są na to pewnie jakieś gotowce
Z użyciem procedur ROMU czy bez?
Jest w ROMIE jakiś gotowiec, mogę spróbować odszukać w plikach
Jeśli robisz samemu, to jest to już trochę trudniejszy temat.
Generalnie robisz tak (zapis w pseudokodzie)
cyfra1=0,cyfra2=0,cyfra3=0
while (A>100)
{
cyfra1=cyfra1+1
A=A-100
}
while (A>10)
{
cyfra2=cyfra2+1
A=A-10
}
cyfra3=A
Dochodzi kwestia czy pisać liczby w formacie 005 gdzie z przodu są zera czy pomijać te zera
-
Pisze na ZX więc z ROM można korzystać
a zera, są mile widziane ale niekoniecznie
W języku wysokiego poziomu to nie problem,
ale jak to przetłumaczyć na kod ?
W końcu każda gra zawiera liczniki, życia czasu itp.
więc jest to już jakoś rozwiązane.
-
Korzystając z ROMu, najkrócej można napisać procedurę piszącą bez zer.
LD C, A
LD B, $00
CALL $1A1B
RET
Oczywiście wysyła do bieżącego otwartego kanału.
Żeby pisała z zerami, trzeba by ją przepisać, zamieniając LD E, $FF na LD E,$30 (kod znaku 0). To jest procedura pisząca Basicowy numer linii, więc po zmianie na drukowanie zer, będzie pisać liczby czterocyfrowe np. 0003.
I przy okazji: zamiast DEFB "P", "r", .... można napisać DEFM "Program" (przynajmniej w GENSie tak jest).
-
Wiem ze mozna uzyc defm ale chciałem uzyskać efekt kolejno pokazujących się liter.
Co zresztą jest widoczne.
A rozwiazanie problemu licznika chyba znalazłem, przyśniło mi się w nocy.
Każdej cyfrze, liczby licznika przypisujemy osobny bajt pamięci.
Robimy dwa podprgramy, jeden incrementuje poszczególne bajty, do 10.
Drugi wyświetla ich stan przez RST 16.
Implementacja nie powinna być trudna, dzisiaj ja zrobię.
-
w akumulatorze jest jakaś liczba
jak ją dziesiętnie wyświetlić na ekranie
W https://chuntey.wordpress.com/2013/09/08/how-to-write-zx-spectrum-games-chapter-10/ (polecam w sumie wszystkie części, bo fajnie się to czyta) jest taki kod (zwróć uwagę, że wyświetla zera na początku), na wyświetlenie liczby w hl:
; Show number passed in hl, right-justified.
shwnum ld a,48 (or 32) ; leading zeroes (or spaces).
ld de,10000 ; ten thousands column.
call shwdg ; show digit.
ld de,1000 ; thousands column.
call shwdg ; show digit.
ld de,100 ; hundreds column.
call shwdg ; show digit.
ld de,10 ; tens column.
call shwdg ; show digit.
or 16 ; last digit is always shown.
ld de,1 ; units column.
shwdg and 48 ; clear carry, clear digit.
shwdg1 sbc hl,de ; subtract from column.
jr c,shwdg0 ; nothing to show.
or 16 ; something to show, make it a digit.
inc a ; increment digit.
jr shwdg1 ; repeat until column is zero.
shwdg0 add hl,de ; restore total.
push af
rst 16 ; show character.
pop af
ret
Osobiście, do zrobienia procedury wyświetlającej punkty (ten sam problem), korzystałem z artykułu na cpcwiki: http://www.cpcwiki.eu/index.php/Programming:Display_and_update_Scores - części z BCD, bo z tego co tu wypisują są 3 metody: powyższa (podana wcześniej rzez RafalM), taka używająca BCD i zapis każdej cyfry w osobnym bajcie...
-
Postanowiłem doprowadzić do końca moją procedurę z nocnych koszmarów
wynik działania w załączniku
Więc mamy główną pętle:
ld a,0
ld hl,50005 ;jednosci
ld (hl),a
ld a,0
ld hl,50006 ;dziesiatki
ld (hl),a
start
call wyswietl
call zwieksz
call time
jp start
I procedury:
procedura wyswietl - wyświetla aktualny stan licznika
procedura zwieksz zwiększa jego stan o 1
procedura time - spowalnia działanie programu
WYSWIETL
ld hl,50006 ;dziesiatki
ld a,(hl)
add a,48
rst 16
ld hl,50005 ;jednosci
ld a,(hl)
add a,48
rst 16
ZWIEKSZ
ld a,(50005)
cp 9
jp z,zw1
inc a
ld (50005),a
ret
zw1
ld a,0
ld (50005),a
ld a,(50006)
inc a
cp 10
jp z,stop
ld (50006),a
no i procedura END zmienia border na żółty żeby pokazać że licznik doszedł do końca
PS dalej nie potrafię wyświetlić więcej niż jednego bajta na raz DEFB, DEFM też nie działa.
No i problemem jest ustawienie miejsca wyświetlania, osiągam to przez znaki sterujące, cofanie kursora, enter, spacja.
Przez zmienną systemową 23566/7 nie działa.
-
No to po kolei... Drukowanie pojedynczych znaków przy użyciu procedur z ROMu jest stosunkowo prostą sprawą - można tam używać znaków kontrolnych w tym również kodu 22 - AT, po którym dwa kolejne bajty to odpowiednik BASICowego PRINT AT w,k;
Czyli:
ld a,22
rst 16
ld a,10
rst 16
ld a,12
rst 16
ld a,48
rst 16
To odpowiednik
PRINT AT 10,12;"0";
Używając procedur z ROMu dobrze jest pamiętać, że niekoniecznie na początku programu musi być otwarty kanał 2 odpowiadający za pisanie w głównej części ekranu i dobrze jest go najpierw otworzyć:
ld a,2
call $1601
Użycie DB/DM do przechowywania tekstów wymaga albo znajomości długości tekstu albo postawienia znacznika na końcu.
Jedna z prostych metod jest taka:
ld hl,tekst_do_wydrukowania
call drukuj
...
drukuj
ld a,(hl)
or a
ret z
push hl
rst 16
pop hl
inc hl
jr drukuj
tekst_do_wydrukowania:
db 22,10,10
dm "Tekst"
db 0
Można też użyć procedury PO-MSG ($0C0A) z ROMu, ale wymaga ona trochę zachodu na starcie, bo jest zasadniczo przeznaczona do czego innego niż proste drukowanie tekstu:
ld de,tekst
xor a
call $0C0A
tekst: db 128
db "Tekst do wydrukowani"
db "a"+128
Skomplikowanie wynika stąd, że kod jest przeznaczony do drukowania tekstów z tabeli - komunikatów o błędach, tokenów BASICa itp. DE na wejściu zawiera początek tabeli w której pierwszy znak to 128 - jest przez procedurę po-msg przeskakiwany. Dalej następują kolejne teksty w każdym z nich ostatni znak ma ustawiony najstarszy bit (+128). A zawiera indeks do tekstu, który chcemy wydrukować - 0 (XOR a) znaczy pierwszy tekst. Jeśli mamy więcej tekstów, to można je wszystkie wrzucić w jednym ciągu i drukować wołając po-msg z różnymi A dla tego samego DE.
-
Nie mam teraz czasu wszystkiego przeanalizować ale aż się prosi
zamiast
ld a,0
ld hl,50005 ;jednosci
ld (hl),a
ld a,0
ld hl,50006 ;dziesiatki
ld (hl),a
użyć:
xor a
ld hl,50005
ld (hl),a
inc hl
ld (hl),a
W tej chwili możesz to uznać za mniej czytelne, ale jest to krótsze, szybsze i powszechnie stosowane :)
-
A jeśli chodzi o drukowanie "normalnych" liczb dwubajtowych, to można to zrobić np. tak:
ld hl,liczba
ld a,0
call print_16
...
print_16
ld (was_digit),a
ld de,10000
call print_one
ld de,1000
call print_one
ld de,100
call print_one
ld de,10
call print_one
ld a,l
print_digit
add a,"0"
push de
push hl
rst 16
pop hl
pop de
ret
print_one
xor a
print_one1
sbc hl,de
jr c,print_one2
inc a
jr print_one1
print_one2
add hl,de
or a
jr nz,print_one3
ex af,af'
ld a,(was_digit)
or a
ret z
ld a,1
ld (was_digit),a
ex af,af'
print_one3
ex af,af'
ld a,1
ld (was_digit),a
ex af,af'
jr print_digit
was_digit:
db 0
Procedura print_16 jako parametry przyjmuje liczbę do wydrukowania w HL i status drukowania zer wiodących w A. Jeśli A zawiera 0 to procedura wydrukuje liczbę bez zer na początku, jeśli coś innego, to z zerami. Jeśli zamiast zer mają być spacje to trzeba zmienić w print_digit add a,"0" na add a," ".
Procedura działa "na rympał" i oczywiście psuje HL a do tego A i DE. Działanie jest proste - cztery razy wołana jest procedura print_one, która "dzieli" HL przez DE dając wynik w A a następnie zależnie od znacznika "was_digit" drukuje bądź nie (jeśli was_digit zawiera 0 i wynikiem jest 0) kolejne cyfry używając print_digit. Na koniec zawartość L (czyli ostatnia cyfra po podzieleniu przez 10) przepisywana jest do A i drukowana niezależenie od stanu was_digit - jeśli w HL było 0 to ma się ono wydrukować.
Całość jest niezbyt szybka, ma nierówny czas wykonania ale za to jest stosunkowo łatwa (mam nadzieję) do zrozumienia ;)
edit: Tak się zapędziłem, że nie zwróciłem uwagi, że @ssr86 cytował w zasadzie identyczne rozwiązanie drukowania liczb ;)
-
Też długo używałem takiego sposobu żeby zmienić pozycję, ale nie podobał mi się.
ld a,22
rst 16
ld a,10
rst 16
ld a,12
rst 16
ld a,48
rst 16
To odpowiednik
PRINT AT 10,12;"0";
Przez modyfikację zmiennych systemowych nie udawało się nic osiągnąć, więc w końcu przeanalizowałem problem. Tak można ustawić tą samą pozycję co wyżej, krócej i (przynajmniej dla mnie) elegancko:
LD BC,$0E15 ;B=24-y, C=33-x
CALL $0DD9
-
@Phonex Oczywiście, że robienie tego ciągiem LD A,n, RST 16 jest brzydkie i bez sensu ;) Jak już robię takie rzeczy to raczej wstawiam kody sterujące na początek właściwego tekstu i wołam PO-MSG albo inną stosowną procedurę drukującą tekst. Jest spójniej i krócej niż osobne ustawianie pozycji :)
-
Przez modyfikację zmiennych systemowych nie udawało się nic osiągnąć, więc w końcu przeanalizowałem problem. Tak można ustawić tą samą pozycję co wyżej, krócej i (przynajmniej dla mnie) elegancko:
LD BC,$0E15 ;B=24-y, C=33-x
CALL $0DD9
Zacząłem przeglądać swoje stare pliki ASM i znalazłem, że można prościej:
LD BC,$0E15 ;B=y, C=x
CALL $200A
-
Ładnie. Ale moja procedura jest szybsza! :P
@Phonex Oczywiście, że robienie tego ciągiem LD A,n, RST 16 jest brzydkie i bez sensu ;) Jak już robię takie rzeczy to raczej wstawiam kody sterujące na początek właściwego tekstu i wołam PO-MSG albo inną stosowną procedurę drukującą tekst. Jest spójniej i krócej niż osobne ustawianie pozycji :)
Zgadza się, ale nie można tak gdy drukuje się zmienne.
-
A właściwie kto powiedział, że nie da się ustawić pozycji przez modyfikację zmiennych? Da się tylko jest to wysoce upiardliwe, bo trzeba modyfikować równocześnie "DF CC" (23684) i "SPOSN" (23688) - ta pierwsza zawiera adres w pamięci ekranu aktualnej pozycji kursora, ta druga to 33-kolumna, 24-wiersz.
Takie coś...
ld a,33-10
ld (23688),a
ld a,24-10
ld (23689),a
ld hl,16384+2048+32*2+10
ld (23684),hl
ld a,48
rst 16
...wyświetli "0" na pozycji 10,10 ;)
-
Oczywiście że da się. ;)
Napisałem za krótko. Miałem na myśli: "Przez modyfikację zmiennych systemowych pozycji (SPOSN) nie udawało się nic osiągnąć..."
CALL $0DD9 robi właśnie to co Twoja procedura - dla otwartego kanału 2 (ekran), ustawia zmienne SPOSN oraz DF CC.
-
A tak całkiem na koniec przypomniałem sobie, że przecież w ROMie jest też procedura do zrzucania na ekran dowolnej liczby ze stosu kalkulatora. I oczywiście procedury do wrzucania na ten stos liczb z rejestrów. A więc drukowanie liczb można też rozwiązać tak:
ld a,200
call $2d28 ; stack-a
call $2de3 ; print-fp
...
ld bc,12345
call $2d2b ; stack-bc
call $2de3 ; print-fp
Oczywiście print-fp wydrukuje też liczby zmiennoprzecinkowe o ile będziemy w stanie przygotować stosowną formę i wrzucić na stos kalkulatora albo będzie wynikiem jakichś obliczeń wykonanych tymże kalkulatorem...
ld bc,12345
call $2d2b ; stack-bc
ld a,7
call $2d28 ; stack-a
rst $28 ; uruchamiamy kalkulator - na stosie mamy 7 i 12345
db $05 ; dzielenie - zdejmuje ze stosu dwie wartości i dzieli
; jedną przez drugą pozostawiając wynik
; w efekcie na stosie zostaje 12345/7
db $38 ; koniec obliczeń
call $2de3 ; print-fp - drukuje ostatnią liczbę ze stosu kalkulatora, czyli
; wynik obliczeń
Takie zabawy dają sporo możliwości (można sobie policzyć z ASMa w zasadzie wszystko to, co się da policzyć z BASICa), ale mają jedna zasadniczą wadę - kalkulator nie jest szybki i bardziej rozbudowane obliczenia trwają zdecydowanie za długo.
Ale jak ktoś potrzebuje, to się da ;)
-
Wersja z licznikiem i napisami
napisy robione tak:
wstep
ld hl,wst
call tekst
wst DEFM 22,10,1,'Wedrujacy punkt',0
tekst
ld a,(hl)
sko3
push af
rst 16
pop af
inc hl
ld a,(hl)
cp 0
jp nz,sko3
ret
-
Wersja z licznikiem i napisami
Prawie dobrze ;)
tekst
ld a,(hl)
sko3
push af
rst 16
pop af
inc hl
ld a,(hl)
cp 0
jp nz,sko3
ret
Działa a to do czego się przyczepię to drobiazgi, z których z czasem "wyrośniesz" ;)
Po pierwsze... w dwóch miejscach pobierasz bajt spod (hl) - niepotrzebnie. Po drugie - przed RST 16 zrzucasz na stos AF, które i tak później nie będzie ci potrzebne. Po trzecie - nadal używasz CP 0, które jest dłuższe niż OR A i zajmuje więcej miejsca.
"Ładniej" zrobiona procedura mogłaby wyglądać tak:
tekst
ld a,(hl) ; pobieramy kolejny bajt
or a ; zero?
ret z ; jeśli tak, kończymy procedurę
rst 16 ; drukujemy znak
inc hl ; zwiększamy wskaźnik
jr tekst ; skaczemy na początek pętli
Ale poza tym poruszasz się we właściwym kierunku :)
-
Dziękuje za poprawki.
Znalazłem w końcu błąd z którym walczyłem ze dwie godziny
takie coś daje kolorowe kwadraty na ekranie:
info2
ld hl,inf2
call tekst
inf2 DEFB 22,19,1,'Sterowanie klawiszami: Q A O P',0
ret
musi być tak:
info2
ld hl,inf2
call tekst
ret
inf2 DEFB 22,19,1,'Sterowanie klawiszami: Q A O P',0
-
No słuchaj, w oryginalnej wersji chciałeś treść swojego napisu wykonać jako kod. To nie mogło się udać ;)
Po instrukcji CALL tekst następną rzeczą w pamięci jest treść twojego tekstu. Program nie wie że to tekst, potraktuje to jako kod.
W asemblerze program i dane są właściwie tym samym - sekwencją bajtów. Twój napis został zamieniony na jakieś bajty a im zawsze odpowiadają jakieś instrukcje asemblera. W przypadku takiego tekstu będą to jednak zupełnie bezsensowne, losowe instrukcje które na 90% zawieszą program lub spowodują reset.
-
No chyba tak nie do końca:
Po instrukcji CALL tekst następną rzeczą w pamięci jest treść twojego tekstu. Program nie wie że to tekst, potraktuje to jako kod.
miejsce w pamięci gdzie znajduje się tekst jest wskazany w rejestrze HL
i gdzie by on nie był to program powinien go sobie znaleźć,
a to że okazało się że nie jest tak całkiem wszystko jedno,
to raczej ograniczenie programu asemblującego.
a następną wykonywaną instrukcją po CALL jest instrukcja
w podprogramie "tekst"
Tak mi się przynajmniej wydaje
-
Tak mi się przynajmniej wydaje
Masz rację - wydaje ci się ;)
Owszem - po CALL wykonywana jest procedura, którą wywołujesz, ale jak w niej trafisz na RET, to program wraca do kolejnego bajtu za instrukcją CALL czyli w twoim wypadku tak jak pisał Rafał usiłuje wykonać twój tekst jako kod i idzie w kartofle ;)
edit: Z ciekawości sprawdziłem co ci się tam tak na prawdę wykonywało...
org $0000
ld d,$13 ; 1613 ; db $16, $13
ld bc,$7453 ; 015374 ; db $01, $53, $74
ld h,l ; 65 ; db $65
ld (hl),d ; 72 ; db $72
ld l,a ; 6F ; db $6F
ld (hl),a ; 77 ; db $77
ld h,c ; 61 ; db $61
ld l,(hl) ; 6E ; db $6E
ld l,c ; 69 ; db $69
ld h,l ; 65 ; db $65
jr nz,$007A ; 206B ; db $20, $6B
ld l,h ; 6C ; db $6C
ld h,c ; 61 ; db $61
ld (hl),a ; 77 ; db $77
ld l,c ; 69 ; db $69
ld (hl),e ; 73 ; db $73
ld a,d ; 7A ; db $7A
ld h,c ; 61 ; db $61
ld l,l ; 6D ; db $6D
ld l,c ; 69 ; db $69
ld a,($5120) ; 3A2051 ; db $3A, $20, $51
jr nz,$005E ; 2041 ; db $20, $41
jr nz,$006E ; 204F ; db $20, $4F
jr nz,$0071 ; 2050 ; db $20, $50
nop ; 00 ; db $00
ORG $0000 jest po to, żeby cokolwiek dało się wywnioskować - kod nie robi nic istotnego, co najwyżej przeskakuje gdzieś dalej i zależnie od tego, co miałeś za tekstem i w jaki dokładnie adres się wstrzeli może się zachowywać w dowolnie nieprzewidywalny sposób ;)
-
No zdecydowanie nie rozumiesz :)
Włącz sobie w emulatorze debugger i zobacz instrukcja po instrukcji co wykonuje twój program. Zobaczysz że pomieszałeś kod z danymi. To że wstawisz do HL adres tego tekstu nie ma na tym etapie żadnego znaczenia.
-
A żeby ci jeszcze bardziej namieszać, to pokażę ci jeden z bardziej zaawansowanych kawałków, który pozwala na mieszanie kodu z danymi...
W głównej części programu można drukować teksty w taki sposób:
...
call print_text
db 22,18,0,"Napis w tresci",0
...
Wielokropki to oczywiście dowolny kod przed i po wydrukowaniu tekstu. A sama procedura drukowania wygląda tak:
print_text
pop hl
print_text2
ld a,(hl)
inc hl
or a
jr nz,print_text1
push hl
ret
print_text1
rst 16
jr print_text2
Procedury dostarczające w taki sposób statycznych parametrów są dość częstym rozwiązaniem w ROMie (RST 8, RST 28 - kalkulator itp).
Cały mechanizm polega na tym, że po CALL na stos odkładany jest adres powrotu, czyli adres następnego bajtu po trzybajtowej instrukcji. W procedurze podnosimy ten adres ze stosu i używamy go jako wskaźnika do tekstu. Jedyna istotna różnica to sposób powrotu - po odczytaniu ostatniego bajtu wskaźnik ustawia się na pierwszy bajt po tekście (a właściwie po znaczniku końca) a następnie jest odkładany na stos a RET działa dokładnie odwrotnie do CALL - podnosi adres ze stosu i skacze pod ten adres wracając do kolejnej instrukcji po tekście.
Powrót zamiast PUSH/RET można w tym wypadku wykonać też przez JP (HL), ale nie zawsze się da, bo nie zawsze wskaźnikiem do danych będzie HL.
Ot taki sobie - czasem przydatny - myk ;)
-
Świetny przykład, choć pewnie istnieją jeszcze bardziej zagmatwane kody, z pracą na stosie, JP zamiast CALL i innymi cudami.
tdu: czy to co piszesz ma jakąś zaplanowaną formę?
-
Ta analiza programu przemówiła do mnie, zwracam honor starym wyjadaczom asemblera.
Na moją obronę mam to że sam błąd znalazłem, nie wiedziałem tylko dlaczego.
A te dywagacje nad udziwnieniami to nie na mój poziom.
@tygrys - to tylko taka improwizacja, nie wiem co z tego wyjdzie, może się zniechęcę i zajmę się czymś innym.
Chciałem złapać tylko podstawy pracy z asemblerem ale mnie wciągnęło. Sam się dziwię że nie zrobiłem
tego kroku 25 lat temu. Ale wtedy jakoś Basic mi wystarczał.
-
To ja podziele się jeszcze jednym usprawnienem.
Jeżeli mamy coś takiego:
ZZZ:
....
CALL xxxxxx
RET
to śmiało można to zamienić na
JP xxxxxx
A dlaczego tak? Bo CALL odkłada na stos adres, który wskazuje na RET. RET to taki odpowiednik POP PC (taka instrukcja nie istnieje, w razie wątpliwości). Możemy zatem skorzystać z RET w procedurze xxxxxxx. Takie rozwiązanie daje nam 1 bajt i 17 taktów oszczędności.
-
To ja podziele się jeszcze jednym usprawnienem.
A to ci powiem Tygrysie że jak tak nie robię.
Masz absolutną rację co do oszczędności w bajtach i taktach ale razi to jakoś moje prywatne poczucie piękna ;D Procedura powinna się kończyć przez RET i już ;D
-
Ja też.
To znaczy robię tak, ale tylko jak oszczędzam miejsce (lub takty). ;)
-
Odnośnie dyskusji na temat błędu wskutek złego umieszczenia deklaracji danych.
Uważam że asembler powinien z automatu wyrzucać je na koniec programu tak
żeby nie powodowało to błędu.
Programista pisze deklaracje DEFB gdzie mu wygodnie, a asembler powinien to
wstawić tam gdzie trzeba.
Do mojego programu dorzuciłem dźwięk przy kolizji,
zastosowałem taką procedurę:
sound1 LD bc,200
pet1
ld a,29
out (254),a
call time4
ld a,6
out (254),a
DEC bc
LD a,b
OR c
JP NZ,pet1
ret
time4 robi dodatkowe opóźnienie
a wartości 6 i 29 dobrałem tak żeby uzyskać
określone kolory
efekt w załączniku.
Zdominowałem ostatnio forum swoimi wpisami,
ale wyjeżdżam na długi weekend i macie mnie z głowy
do przyszłego tygodnia.
Nie ma to jak rowery.
-
"DEFB" to nie jest deklaracja danych.
To po prostu sposób na wpisanie liczb. Są tam gdzie programista je umieści. Mogą być wszystkim, również np. częścią samomodyfikującego się programu.
Nie ma w asemblerze rozdzielenia na dane i program. Wszystko ustala się samemu.
-
Uważam że asembler powinien z automatu wyrzucać je na koniec programu tak
żeby nie powodowało to błędu.
Programista pisze deklaracje DEFB gdzie mu wygodnie, a asembler powinien to
wstawić tam gdzie trzeba.
Masz naleciałości z języków wyższego poziomu.
Jak w Basicu czy w C napiszesz int x=1 to zwykle cię dalej nie obchodzi gdzie ten x jest przechowywany bo dba o to kompilator i o późniejszy dostęp do tej zmiennej także.
W asemblerze tak nie ma. Sam zawsze decydujesz gdzie co umieścisz i uwierz mi że tak jest dobrze. Są bardzo często konkretne powody dla których chcesz by dana wartość była przechowywana w komórce 49152 a nie komórce 50000.
Realne programy na Zx Specrum mają przemieszane wartości - kod, dane, kod,dane,kod,dane. Ale się nie wywalają bo programista dba oto by każdy blok kody zakończyć przez RET albo instrukcję skoku i bajty danych które są potem nigdy nie zostaną osiągnięte przez wskaźnik aktualnie wykonywanej instrukcji.
-
Na dowód że dalej z pasmo walczę, kolejna wersja
mojego programiku.
Teraz muszę rozpracować kolizje z przeszkodami
i konieczna jest zmiana sposobu kasowania kropki
po przemieszczeniu, a to już wymaga operacji na bitach.
-
Teraz muszę rozpracować kolizje z przeszkodami
i konieczna jest zmiana sposobu kasowania kropki
po przemieszczeniu, a to już wymaga operacji na bitach.
Nie śledzę co dokładnie robisz w assemblerze, ale jak chcesz "zmazać" uprzednio zapalony piksel, to po prostu przechowuj adres ekranu + bajt danych spod tego adresu (który był przed postawieniem punktu). Oszczędzisz od groma czasu procesora ;)
-
W tym kierunku w zasadzie zmierzam.
Do tej pory kasowałem cały bajt.
Przy zbliżaniu kropki do krawędzi ekranu to się sprawdzało,
przy dowolnych przeszkodach, jest gorzej.
A teraz muszę opanować operację na bitach.
Aż się prosi żeby jeden piksel obrazu to był jeden bajt, o ile życie byłoby prostsze.
-
Aż się prosi żeby jeden piksel obrazu to był jeden bajt, o ile życie byłoby prostsze.
I o ile "kolorowsze" :D
-
Jest takie rozwiązanie, ale rozdzielczość spada do 32x24 "piksele" ;)
-
Alternatywnie - emulator Speccy256, choć nie do końca tam o to chodzi ;)
-
Aż się prosi żeby jeden piksel obrazu to był jeden bajt, o ile życie byłoby prostsze.
Rozumiem że to taki żart :)
Bo w takiej sytuacji pamięć ekranu przy tej samej rozdzielczości zajmowałaby 48kB a narysowanie czegokolwiek trwałoby 8 razy dłużej.
Oczywiście ludzie już kilka lat po Spectum stworzyli maszyny zdolne to pociągnąć ale Spectruma taki ciężar by całkowicie przygniótł.
-
Bo w takiej sytuacji pamięć ekranu przy tej samej rozdzielczości zajmowałaby 48kB
Zgoda!
a narysowanie czegokolwiek trwałoby 8 razy dłużej.
Niekoniecznie. Po co wymyślono bitplany? :)
-
a narysowanie czegokolwiek trwałoby 8 razy dłużej.
Niekoniecznie. Po co wymyślono bitplany? :)
Tylko bitplane'y powodują, że wracamy do organizacji w której jeden pixel nie jest jednym bajtem ;)
-
.. a do obsługi bitplanów trzeba coś więcej niż procka. Do Amigi, w którym jest organizacja bitplanowa, dobudowano blitter, którzy operacje na grafice znacznie przyspiesza. ale dość offtopów ;)
Wracając do pytania o usuwanie pixeli. Jak zwykle możliwości jest wiele.
Najszybsza to zapamiętanie adresu pamięci oraz bajtu reprezentującego pixel. To co trzeba zrobić to operacje AND między komórka pamięci a odwróconymi bitami dla pixala (poczytaj o instrukcji CPL). Można od razu przechowywać wartość dla AND.
-
Znowu ugrzęzłem, tym razem na operacjach logicznych.
Wypróbowałem, podpowiedź Tygrysa, ale to nie to.
Bajty w komórkach pamięci, po operacji logicznej, mają mieć
te ustawione bity które również są ustawione w każdej z komórek.
Po analizie wyszło mi że realizować to powinna funkcja OR.
Ale nie robi tego. Kod testowy poniżej
org 40000
ld a,32
ld (50000),a
ld a,4
ld (50006),a
;-------------
ld a,(50000)
ld b,(50006)
or b
ld (50008),a
;----monitor
ld a,(50000)
ld(16896),a
ld a,(50006)
ld(17408),a
ld a,(50008)
ld(17920),a
ld hl,22528
ld a,120
ld(hl),a
ret
end 40000
W kodzie istotna jest tylko relacja między rejestrem a i b,
reszta to otoczka i minimonitor stanu rejestrów.
-
Nic z tego nie rozumiem :)
To znaczy kod rozumiem - bierzesz wartości z dwóch komórek robisz na nich OR i wynik zapisujesz do trzeciej komórki. Po czym przerzucasz te 3 bajty na ekran. I co w tym jest nie tak?
I drobna rada trochę z innego obszaru nikt w prawdziwych programach tak nie pisze że coś zapisujemy do komórki 50000 a coś do 50002.
Jest coś takiego jak etykiety. Możesz napisać:
LD A,(LiczbaZyc)
DEC A
LD (LiczbaZyc),A
RET
LiczbaZyc .BYTE 3
i wtedy nawet nie wiesz dokładnie pod jakim adresem jest przechowywana ta liczba żyć w grze ale nie musisz tego wiedzieć bo program po prostu działa.
Ewentualnie na końcu swojego kodu możesz sobie zrobić blok danych pod określonym adresem
ORG 50000
PixelX .BYTE 128
PixelY .BYTE 96
....
To bardzo ułatwia życie. Jak będziesz miał więcej parametrów przechowywanych pod różnymi adresami to nie będziesz w stanie zapamiętać co dokładnie jest pod 50008 a co pod 46573. A po nazwach będziesz od razu wiedział.
-
Tak na pierwszy rzut oka, to nie możesz zrobić LD B,(adres). Tak można zrobić tylko z A.
Jak chcesz do B załadować dane z pamięci, to musisz to robić poprzez HL lub IX/IY:
LD B,(HL)
LD B,(IX+10)
LD B,(IY-20)
A to, o czym pisałem o kasowaniu punktu na ekranie, gdzie masz zapamiętany adres pamięci oraz pixel, powinno wyglądać mniej więcej tak (piszę z pamięci, bez testów):
LD IX,adres_tablicy_danych
LD L,(IX+0) ; zaladowanie mlodszego bajtu adresu
LD H,(IX+1) ; zaladowanie starszego bajtu adresu
LD A,(IX+2) ; zaladowanie punktu np %00100000
CPL ; stworzenie maski, wynik: %11011111
LD B,A ; przekazanie maski do rejestru pomocniczego B
LD A,(HL) ; załadowanie danej z komórki pamięci
AND B ; usunięcie zbędnego punktu, pozostałe punkty pozostają
LD (HL),A ; zapisanie wyniku do pamięci
-
Dzęki Tygrys, w tym był właśnie był problem.
Przyzwyczaiłem się że pasmo daje komunikat gdy jest zła składnia,
tym razem tego nie było.
zrobiłem:
ld a,(50000)
ld b,a
ld a,(50006)
or b
ld (50008),a
i zadziałało, mogę posuwać się dalej.
kod ciekawy, oczywiście przeanalizuje.
@RafałM - dziękuje za uwagi, zapewne jak trochę dojrzeje programistycznie,
zacznę się stosować do takich reguł.
-
Przyzwyczaiłem się że pasmo daje komunikat gdy jest zła składnia, tym razem tego nie było.
Z ciekawości przyjrzałem się co tak na prawdę się u ciebie stało... Okazuje się, że z jakiegoś nie do końca zrozumiałego powodu pasmo w wypadku niektórych konstrukcji (np. użycie niedostępnych rozkazów w trybie assemblacji 8080) - w tym w wypadku adresowania pośredniego - zamiast przerwać assemblację z błędem wyrzuca tylko ostrzeżenie i assembluje jakieś głupoty. W wypadku twojego kodu ostrzeżenie wygląda tak:
WARNING: looks like a non existent instruction on line 9 of file tdu.asm
Twoja instrukcja...
ld b,(50006)
...generuje zaś ciąg $06 $56, który odpowiada rozkazowi
ld b,$56
Wygląda tak, jakby parser wyrzucał ostrzeżenie a następnie pomijał nawiasy i zera i generował bzdurny kod.
Zajrzałem w kod, ale nie bardzo mam czas i zacięcie, żeby się wgryzać głębiej w C i to, co udało mi się uzyskać to wyrzucanie błędu w miejscu napotkania niepoprawnej konstrukcji i... błąd gdzieś "wyżej" w kodzie, który mówi, żeby zgłosić błąd do autora ;)
Opisałem problem i wysłałem zgłoszenie mailem - może w następnej wersji już tego błędu nie będzie :)
-
No właśnie kod był długi i to strzeżenie tylko mignęło, a na końcu nic nie było.
Czyli wygenerował jakieś bzdury.
Chociaż nie wiem czemu w tym krótkim kodzie testowym nie zauważyłem ostrzeżenia.
W końcu pisze tam jak wół "non existent instruction on line 5"
ps.dalej walczę z kodem, przesuwanie i odtwarzanie lewo prawo już działa, ale jak doszło
góra dół, to się trochę zaplątałem z adresowaniem. Musze to sobie rozrysować i wtedy do tego dojdę.
-
No i już dostałem odpowiedź... Takie a nie inne działanie pasmo jest jak najbardziej prawidłowe - chodzi o to, że pasmo pozwala na używanie wyrażeń arytmetycznych włączając w to używanie nawiasów. Stąd też użycie ld b,(50006) jest traktowane po prostu jako wyrażenie arytmetyczne, które jest obliczane i używane jako argument. A, że program wie, że nawiasy mogą powodować dwuznaczność stąd ostrzeżenie. A to, że w wynikowym kodzie dostajemy ld b,$56 to nie kwestia parsera tylko tego, że 50006=$c356 i w związku z ośmiobitową instrukcją program bierze po prostu młodszy bajt wyniku.
Żeby uniknąć takich sytuacji pasmo ma dodatkowy tryb pracy włączany parametrem -B (--bracket) - po dodaniu takiego parametru wszystkie "okrągłe" nawiasy są traktowane jako część wyrażeń a adresowanie pośrednie oznacza się używając nawiasów kwadratowych. W naszym wypadku problematyczny rozkaz wyglądałby wtedy tak:
ld b,[50006]
Próba zaś assemblowania takiego kodu daje taki wynik:
pasmo -B --tapbas tdu.asm tdu.tap
ERROR on line 9 of file tdu.asm
ERROR: Expected ']' but 'C356' found
I nie zostawia tym samym wątpliwości co zrobiliśmy nie tak :)
edit : Żeby było śmiesznie właśnie znalazłem kawałek dokumentacji pasmo pod nagłówkeim Bugs:
Pasmo emits a warning when using a expression that looks line a non existent z80 instruction, such as 'ld b, (nn)', but the simplified way used to detect that also warns in cases like 'ld b,(i1+i2)*(i3+i4)'.
A way to avoid the warning in that case is to prefix the expression with parenthesis with '+' or '0 +'.
Using the bracket only mode the problem does not exist, in that case the parenthesis are always taken as expresssions (and the programmer is supposed to know that), thus the warning is not emitted.
-
No i wszystko jasne. Odpowiedź błyskawiczna.
A teraz z innej beczki, jak zatrzymać pracę programu
w określonym miejscu żeby odczytać rejestry.
Używam Spectaculatora, tylko nie wiem jak dojść
w debuggerze do interesującego mnie miejsca.
Chyba najlepiej by było w programie ustawić coś
żeby się zatrzymał, jakiś stop czy halt,
nie mogę tego znaleźć.
-
To zależy od sytuacji, w której potrzebujesz danych o rejestrach. Ja najczęściej używam konstrukcji:
DI
HALT
Skutecznie zatrzymuje kod w wybranym miejscu, a w debuggerze można zobaczyć wartości rejestrów. Niektóre też potrafią ponownie włączyć przerwania i kontynuować program, do kolejnego wystąpienia DI:HALT
-
Dzięki działa.
Żeby odczytać akumuator musiałem wcześniej go do c przepisać.
Debugger pokazuje af, nie potrafiłem z tego a wydzielić.
Ale, ale, teraz wpadłem na pomysł, jak się przełączy na hex
to starszy bajt to będzie a, młodszy to flagi.
-
DI HALT umieściłem w pętli programu.
Jak przeskoczyć przez niego do następnego wystąpienia ?
-
Program zatrzymał się na HALT. Trzeba go przeskoczyć, włączyć przerwania (to pewnie jakiś checkbox w debuggerze lub rejestrach kontrolnych) i odpalić program dalej. Tyle, że to w takim przypadku jest niewygodne. Dla pętli lepiej użyć breakpointów.
Nie używam Spectaculatora bo jego debugger nie spełnia moich oczekiwań, aczkolwiek wiem że breakpointy są.
-
DI HALT umieściłem w pętli programu.
Jak przeskoczyć przez niego do następnego wystąpienia ?
Nijak ;)
DI/HALT zatrzymuje procesor i jedyna metoda na przerwanie tego stanu to reset albo wywołanie NMI. W emulatorze to, co jest ci potrzebne to nie DI/HALT tylko ustawianie pułapek. Zwykle jest do tego w sekcji debuggera stosowny przycisk - odnajdujesz w disassemblerze punkt, w którym chcesz zbadać stan procesora/programu, klikasz przycisk ustawiania pułapki i puszczasz program dalej. Jak dojdzie do pułapki to emulator się zatrzyma i wskoczy do debuggera. Jak się dokładnie ustawia pułapki zależy od używanego emulatora - przycisk na pasku, kliknięcie w lewy margines okna debuggera przy zadanej instrukcji, wprowadzenie stosownego rozkazu w linii poleceń debuggera. Musisz sobie znaleźć w emulatorze, którego używasz.
A jak już znajdziesz to poszukaj też przycisków do wykonywania krokowego, przeskakiwania wywołań, powrotu z wywołań itp. Większość emulatorów z debuggerami ma takie opcje. A do tego też na przykład pułapki warunkowe - "przerwij wykonanie programu w tym miejscu pod warunkiem, że rejestr zawiera konkretną wartość, pamięć zawiera konkretne informacje itp."
A w emulatorze przeskoczenie za HALT można zrealizować zmieniając zawartość rejestru PC ;)
-
Tygrys piszesz o breakpointach, Matofesi o pułapkach,
czy to to samo ?
Jeśli debugger Spectaculatora, nie jest dobry,
to jaki emulator (debugger) polecacie ?
-
Pułapki i breakpointy są tym samym.
Ja używam ZX Spin.
-
Żaden emulator nie ma takiego debuggera, który zaspokoiłby wszystkie potrzeby każdego programisty ;)
Musisz wypróbować kilka różnych i sprawdzić, który ci najbardziej pasuje. Ja osobiście używam Fuse ale głównie dlatego, że to w tej chwili jedyny sensowny emulator działający na Linuksie - tam debugger nie jest klikany tylko ma commandline'a i wpisuje się polecenia z klawiatury.
-
Spina tez mam
-
Kolejna wersja moich zmagań z Pasmem.
Teraz kropka nie zaburza planszy nad którą się przesuwa.
Kolizje wykrywa na razie tylko z ramką zewnętrzną.
Musiałem mocno program przebudować żeby to wyszło,
powoli ale idę do przodu.
-
Mamy rozkazy LD HL,(nn) i LD HL,nn
czy istnieją analogiczne dla BC i DE ?
są rozbieżności w dokumentacji.
-
sa.
ld rr,(adr) gdzie rr moze byc jednym z pary rejestrow bc,de,hl,sp, ix, iy
ld rr, adr gdzie rr moze byc jednym z pary rejestrow bc,de,hl,sp, ix, iy
-
@tdu: kiedyś udostępniłem tabele instrukcji, z których sam często korzystam. Jest tam wszystko lub prawie wszystko.
http://speccy.pl/forum/index.php/topic,731.0.msg8605.html#msg8605
@Dr Piotr: prośba o nie cytowanie postu, jeżeli odpisujesz zaraz pod nim.
-
@ Tygrys
W tych tabelach te instrukcje są, natomiast w innych publikacjach nie ma.
np. tutaj http://edu.i-lo.tarnow.pl/inf/retro/004_z80_inst/index.php
i w moich książkach również nie ma.
Akurat miałem problem z działaniem mojego programu,
i podejrzenie padło właśnie na ld bc,(nn) ale do końca
tego nie sprawdziłem.
-
hm.. nawet w w/w stronie jest informacja, której szukasz: http://edu.i-lo.tarnow.pl/inf/retro/004_z80_inst/0020.php#LD_dd,%28nn%29
-
Faktycznie jest.
Widać nie skojarzyłem że to dd to inne rejestry,
szukalem ld bc i na dd nie zwróciłem uwagi.
-
Tutaj też wszystko jest ;)
http://speccy.pl/wiki/index.php?title=Lista_rozkaz%C3%B3w_-_tabela (http://speccy.pl/wiki/index.php?title=Lista_rozkaz%C3%B3w_-_tabela)
http://speccy.pl/wiki/index.php?title=Rozkaz_LD (http://speccy.pl/wiki/index.php?title=Rozkaz_LD)
-
No tak, zapomniałem że jest jeszcze inne - bardziej podstawowe źródło danych o asmie ;-)
@tdu: Zmagasz się z nauką asemblera, a kompilator pasmo jest jedynie narzędziem kompilującym program. Coś jak młotek i kowadło.
-
W zasadzie sam sobie opowiedziałem na to pytanie,
wymieniłem wszystkie ld bc,(nn) na ld hl,(nn)
i program dalej nie działa więc nie w tym problem :(
Walczę dalej.
-
Mam wrażenie, że nadal nie poznaliśmy problemu...
-
Z tym problemem muszę się sam uporać.
Wytłumaczenie działania zajęłoby za dużo czasu,
to jest 500 linijek kodu (w asmie), a i tak usłyszę
że tak się nie robi.
Pasmo kompiluje, błędów formalnych nie ma
to tylko moje błędy w rozumowaniu.
A na własnych błędach można najszybciej się nauczyć.
-
Bo może rzeczywiście tak się nie robi.
Jeśli stworzyłeś kod który przestajesz ogarniać to może czas go przerobić tak by robił to samo ale był prostszy i bardziej uporządkowany.
Fachowo się to nazywa refaktoryzacja kodu ;)
-
Chyba już błąd znalazłem, czeka mnie teraz kolejna refaktoryzacja :P
-
No i gotowe, tym razem to dłużej trochę trwało, ale w końcu się udało.
Teraz jeszcze zmiana plansz, wykorzystanie większej części ekranu.
może jakieś lepsze dźwięki ... ta zabawa nie ma końca.
Dla kropki to jest wyzwanie, ale gdyby to było coś więcej niż kropka,
nie mam pomysłu, musi być na to jakaś chytra sztuczka.
Procesor nie dałby sobie rady z animacją zbioru kropek.
Trzeba by to jakoś hurtowo zrobić.
-
Procesor nie dałby sobie rady z animacją zbioru kropek.
Jak mądrze zrobić to dałby ;)
Ale rozumiem że chodzi ci o sprajty. Więc masz rację,sprajtów się nie składa z pojedynczych kropek.
Jak coś kopiujesz na ekran to tak naprawdę zawsze nie jedną kropkę tylko 8 kropek (zapalonych lub zgaszonych) czyli bajt.
Spectrum nie ma sprzętowych sprajtów. Jak chcesz na ekranie wyświetlić jakiegoś ludka to musisz do pamięci ekranu przekopiować wszystkie bajty składające się na tego ludka. Innej opcji nie ma.
-
No tak, właśnie to przerabiałem, żeby wyświetlić poruszającą się
kropkę trzeba odpowiednie bity zapalać i gasić.
Chociaż pierwsza wersja mojego programu właśnie na całych
bajtach operowała, wpisywałem $ff.
Oszczędności wymusiły taką organizację ekranu.
Wszystko jeden wielki kompromis rozdzielczości (jakości) do ceny.
-
czy takie polecenia są sobie równoważne:
1. ld hl, $4020
2. ld hl, 16416
3. ld h, $64 ld l, $20
4 ld h, $64 ld l, 32
bo miałem przypadek żę wersja 4 działa a 1 i 3 nie.
(pasmo błędu w żadnej wersji nie wyrzuca)
-
1 i 2 są tożsame.
3 i 4 również.
To że masz gdzieś jakiś błąd, może wynikać stąd, że gdzieś mogłeś pomieszać zapis liczb w dziesiętnym i szesnastkowym.
-
A 1i2 z 3i4 chyba też.
W takim razie muszę jeszcze w kodzie pogrzebać.
-
1 i 2 nie są tożsame z 3 i 4. W tych drugich do H ładujesz $64 czyli 100 zamiast $40 czyli 64.
-
To żle napisałem w 1, powinno być $6420 czyli dziesiętnie to samo co w 2, 16416
Już mi się oczywiste rzeczy mylą, muszę trochę od asemblera odpocząć.
Dema nie będzie :'(
-
To żle napisałem w 1, powinno być $6420 czyli dziesiętnie to samo co w 2, 16416
To nie jest to samo. Cały czas ci się myli 64 i $64 czyli 64 i 100.
Napisałem kiedyś dla siebie prosty programik okienkowy do przeliczania z szestnastkowego na dziesiętny i vice versa.
Wrzucę go tutaj wieczorem, może się komuś przyda.
-
Znowu jakiś dziwny efekt wyskakuje mi po asemblacji.
Najprostszy program, tylko zeruje akumulator
org 40000
xor a
di
halt
ret
end 40000
pasmo generuje kod prawidłowo
natomiast debugger emulatora, zaczyna prace nie od 40000
a od 39999
wiec na poczatku nie ma juz xor a, a za to jest ld a,175
więc dłuższy program pójdzie w krzaki.
Na Spectaculatorze i Spinie jest to samo.
-
Debugger w ZX Spin czasami głupieje, bo chce być inteligentny ;-) Jeżeli masz jakąś wartość w 39999 i jest nim opcode dla LD A,xx (dec: 160, hex $A0), to kolejną bajt traktuje jako argument dla tegoż (XOR A ma opcode 175).
Program zadziała prawidłowo, bo on na adresie 40000 będzie mieć XOR A....
Nie wiem jak zachowują się inne debugery.
-
Zauważyłem że instrukcja NOP po org 40000 porawia sytuację.
A te bajty na dole to chyba stos z basica.
Powiniem być niżej ustawiony.
-
Debugger w ZX Spin czasami głupieje, bo chce być inteligentny ;-)
Bo jest uparty, a nie inteligentny. >:(
Przy przewijaniu w górę pamięci robi dobrze, zwiększając adres początku deasemblacji o tyle bajtów ile zajmowała instrukcja w pierwszej linii (bo inaczej się nie da!). Ale przy przewijaniu w dół pamięci powinien zmniejszać adres początku deasemblacji o 1, a nie o długość najdłuższej sensownej instrukcji (jak zdaje się robi).
MAD monitor na ZX-a robi to dobrze. Efekt uboczny jest taki, że przy skrolowaniu w dół pamięci w pierwszej linii ciągle się kotłuje, bo instrukcje się zmieniają. ;) Ale przynajmniej jest jedno ustawienie pokazujące poprawny program, a w Spinie nie ma.
Ta cecha jest wkurzająca, bo jeśli (jak w przytoczonym programie) ustawi się pułapkę na 40000, to nie będzie jej widać! Często w takim przypadku wpisuję w to miejsce NOP debuggerem.
Ale może nie było innego rozwiązania, skoro wpisywany adres początkowy deasemblacji nie jest na górze strony, tylko w połowie?
PS. LD A,xx to 62 ($3E). To jest, zdaje się, znacznik końca stosu BASICowego GO SUB.
-
W Spectaculatorze jest to samo.
Rozwiązuje sprawę profilaktyczne wpisanie NOP zaraz po org w asm'ie
nie przeszkadza a robi się porządek.
Straciłem przez dobre 2-3 godziny, nie wiedziałem co się dzieje
zacząłem już wirusów szukać ;)
-
Najprostszy program, tylko zeruje akumulator
org 40000
xor a
di
halt
ret
end 40000
Niech mnie ktoś wyprowadzi z błędu, bom oszalał... (albo pamięć zawodzi).
Czy następujące po sobie instrukcje DI a potem HALT nie zablokują komputera (będzie czekał w nieskończoność)???
-
E tam, w nieskończoność :)
Na reset albo NMI.
-
Czy następujące po sobie instrukcje DI a potem HALT nie zablokują komputera (będzie czekał w nieskończoność)???
Też tak myślałem... do czasu jak to nie zadziałało na Pentagonie i intro (chernobyl'86) zamiast stać w miejscu, ruszyło dalej co skończyło się resetem :/
-
Też tak myślałem... do czasu jak to nie zadziałało na Pentagonie i intro (chernobyl'86) zamiast stać w miejscu, ruszyło dalej co skończyło się resetem :/
Jak widać, niektóre Pentagony korzystają z NMI :)