Masz rację coberr, najpierw wersja podstawowa a później eksperymenty.
Sterowanie pamięci dynamicznych typu 4464 planuję zrealizować tak:
Można pozbyć się problemu z sygnałem CAS i zastosować pamięci statyczne.
Tutaj koledzy z wysp proponują takie rozwiązanie:
Przypadkiem i z miłym zaskoczeniem natrafiłem na temat Mikrokomputer COBRA 1.
Przed 30 laty też zbudowałem go i obecnie znajduje się w Muzeum Historii Komputerów i Informatyzacji w Katowicach.
Po uruchomieniu podstawowej wersji dorobiłem emulator ZX SPECTRUM
(mapowane na przemian z COBRĄ : ROM SPECTRUM, pamięć statyczna wyświetlacza SPECTRUM, generacja dźwięku )
Zastosowałem oczywiście oryginalne adresowanie ZX SPECTRUM oraz kolor z wyjściem RGB (monitorowe)
Poprzez porty I/O COBRY podłączyłem 3-kanałowy syntezator dźwięku na AY-3-8910, stosowany kiedyś w Amstradzie CPC. Generacja dźwięku nie zajmowała czasu procesora, gdyż wystarczyło do rejestrów wejściowych tego scalaka wpisywać ustawienia, a AY sam generował dźwięk z odpowiednim kształtem, obwiednią i czasem trwania i to niezależnie w 3 kanałach.
Pod klawiaturę podłączyłem gniazdko joysticka (4styki kierunku + ENTER)
Miałem też rozpoczęte inne dodatki jak: TIMER, dodatkową kartę wyświetlacza w oparciu o scalak karty HERCULES z PC, ale po kupnie PC tego już nie dokończyłem.
Czytając cały ten temat nie zauważyłem uwagi na temat błędu układu wyświetlania oryginalnej COBRY. Chodzi o kolizję odczytu z zapisem pamięci statycznej. Objawiało się to dużą ilością poziomych, białych kresek o szerokości 8 pikseli podczas szybkiego przewijania zawartości ekranu.
Nie jest to błąd, tylko uproszczenie układu przez konstruktorów.
W swojej COBRZE wykorzystując niewykorzystane bramki znajdujące się na płycie oraz dokładając UCY7474 blokowałem zapis do pamięci ekranu podczas impulsów odczytu. Zapis był możliwy tylko podczas impulsu wygaszania H i V.
Początkowo odblokowałem tylko czas impulsu ramki, ale to wyraźnie spowalniało komputer, przy szybkich zmianach zawartości ekranu (około 40%).
Po umożliwieniu zapisu do pamięci w trakcie impulsów wygaszania poziomego spowolnienie stało się niezauważalne, a zakłóceń nie było.
Niestety nie mam już żadnych notatek dotyczących moich modyfikacji, ale analiza układu umożliwia prostą realizację tej przeróbki. Zapis trwa bardzo krótko w porównianiu z czasem impulsów V i H.
Miałem też około 30 gier i programów samodzielnie napisanych w kodzie maszynowym Z-80 i kilka w BASICU. Korespondując (nie było wówczas internetu) wymieniałem się kasetami z innymi użytkownikami, więc może gdzieś się jeszcze zachowały (Gwiezdne wojny, Jumping Jack, Wyścig samochodowy, Glizda, Labirynt, Horoskop, Tenis, Hokej, Klocki - tyle pamiętam)
Do ponownej budowy tego samego brak mi już zapału, ale chętnie pobawię się emulatorem programowym COBRY na PC.
Życzę powodzenia i gratuluję zapału obecnym konstruktorom.
PS. Chciałbym uzyskać odpowiedź na kilka pytań dotyczących emulatora COBRY:
-Czy wszystkie kody maszynowe Z80 użyte we własnym programie napisanym w kodzie HEX jednakowo działają zarówno w emulatorze jak i oryginalnej konstrukcji COBRY ? Czy czasy wykonywania pętli też są zachowane ?
-Jaką procedurą sprawdzać wciśnięcie klawisza? (nie chodzi o BASIC tylko własny program w kodzie maszynowym).
Czy należałoby użyć dwóch przełączanych procedur (jedną dla emulatora a inną dla harwarowej wersji COBRY ?
Kiedyś miałem rozpracowane wszystkie podprogramy EPROMU MONITOR i opisane adresy uruchamiania np. procedury przeglądania klawiatury i rozkaz RET powodował powrót do mojego programu z numerem wciśniętego klawisza w akumulatorze. Obecnie nie mam już tamtych notatek i zaczynam od zera.
Może ktoś zna adres startowy tej procedury ?
Napisałem program w Basicu Cobry1.
Jest to gra logiczna "Juxto", której animację zaprezentowałem wcześniej.
Gra posiada 9 plansz z puzlami.
Widok gry i wszystkich 9 plansz pokazuje filmik poniżej.
Wzbogaci ona nieco dość ubogą bibliotekę programów do Cobry1.
Niestety gra wykorzystuje dodatkowe znaki semigraficzne.
Wsad do epromu udostępniłem jakiś czas temu.
Więc kto chce skorzystać z tego programu będzie musiał przeprogramować generator znaków.
Myślę, że warto, bo gra prezentuje się dobrze.
Jeżeli jeszcze coś napiszę w Basicu, to z pewnością z wykorzystaniem tych dodatkowych znaków.
Gra do pobrania w załączniku.
:arrow:zdzis_ek
Sprawdziłem tą grę na swoim emulatorze i działa poprawnie, sama gra jest dosyć trudna.
Odnalazłem ten post, w którym udostępniłeś wsad do generatora znaków. Rzeczywiście, zgodnie z tym, co tam pisałeś, zagospodarowałeś kody od 195 do 255.
Jednakże, pojawiły się polskie litery i niektóre znaki z zakresu od 0 to 31 też sa inne. Czy to Ty zmodyfikowałeś, czy taki wsad zastałeś? Ja zrobiłem generator w emulatorze według istniejącej dokumentacji.
@wieswas Potwierdzam istnienie kolizji w układzie video (moje PCB to replika), było dla mnie dziwnie, że nikt o tym nie wspomniał ale po analizie schematu zrozumiałem, że tak ma być.
Schemat modyfikacji układu sterowania pamięcią statyczną wyświetlacza wysyłałem już prawie 30 lat temu użytkownikom COBRY, z którymi prowadziłem korespondencję, a także wysłałem redakcji czasopisma AudioVideo.
Cykl artykułów na temat COBRY się jednak zakończył i nie wiem czy ktoś to zastosował.
Obecnie nie mam już notatek z tego okresu. Na oryginalnej płycie COBRY są niewykorzystane bramki. Wykorzystałem je do tej modyfikacji łącząc kynarem.
Przy impulsach H miałem problem z opóźnieniem wprowadzanym przez bramki.
Zastosowałem więc "trik", że impuls H sterował więc blokadą następnego impulsu H poprzez celowe opóźnienie, a nie własnego.
Analiza układu wykazała, że powinienem zablokować chwilę przed zakończeniem impulsu H, aby układ zdążył zareagować i nie wystąpiła kolizja. Odstęp między impulsami H jest znany i stały. Można więc go opóźnić o czas krótszy od taktu jednej linii. Bez tego sposobu sporadycznie pojawiały się nadal pojedyńcze zakłócenia. Po tym "triku" zero kolizji.
Przy okazji chciałbym wspomnieć o jeszcze jednej modyfikacji:
Oryginalny układ dźwięku uruchamiany instrukcją mikroprocesora Z-80 OUT(1B),A
co w kodzie hexadecymalnym reprezentują dwa bajty: D3, 1B uruchamia BEEP o stałej częstotliwości generowanej przez multiwibrator na bramkach NAND 7400 (bramka 115c i 115d) oraz stałym czasie trwania wymuszonym przez układ 74121 (nr 106 na schemacie). W swojej COBRZE wykorzystałem inny bit portu wyjściowego podłączony do scalaka 7474 pracującego jako dzielnik przez 2.
Instrukcja OUT(PORT),A podawana w pętli czasowej przerzucała stany wyjściowe 7474 , które były zsumowane na tranzystorze sterującym głośnikiem.
Umożliwiało to generowanie dowolnego dźwięku 1-bitowego o dowolnej częstotliwości i dowolnym czasie trwania. Można więc było płynnie zmieniać wysokości dźwięku dokładnie tak jak robił to ZX SPECTRUM. Moje programy używały więc według potrzeby i BEEP i ten dodatkowy sposób generowania dźwięku. Aby nie spowalniać wydajności Z-80 w pętlach generujących dźwięk nie wstawiałem bezproduktywnego liczenia taktów, tylko konkretne procedury np. graficzne o znanej ilości taktów adekwatnej do wymaganego okresu częstotliwości akustycznej.
:arrow:zdzis_ek
Sprawdziłem tą grę na swoim emulatorze i działa poprawnie, sama gra jest dosyć trudna.
Odnalazłem ten post, w którym udostępniłeś wsad do generatora znaków. Rzeczywiście, zgodnie z tym, co tam pisałeś, zagospodarowałeś kody od 195 do 255.
Jednakże, pojawiły się polskie litery i niektóre znaki z zakresu od 0 to 31 też sa inne. Czy to Ty zmodyfikowałeś, czy taki wsad zastałeś? Ja zrobiłem generator w emulatorze według istniejącej dokumentacji.
zdzis_ek wrote:
@andrzejlisek, ja zakupiony w tamtym czasie eprom miałem już z polskimi znakami.
To co było podane w AV, to jakaś pierwsza wersja generatora znaków.
Oto załącznik z oficjalnej książki na temat BASIC'a i samej Cobry 1 wydanej przez WNT w 1986 r.
Załączam także spakowane skany w lepszej rozdzielczości (300 dpi).
Witam
Miałem kiedyś te książkę, jak i wszystkie numery Audio-Video z opisem Cobry ale niestety książka gdzieś zaginęła. Czy możesz zeskanować tę książkę i wrzucić gdzieś na jakiś serwer np: typu dropbox?
Pozdrawiam
Witam
Miałem kiedyś te książkę, jak i wszystkie numery Audio-Video z opisem Cobry ale niestety książka gdzieś zaginęła. Czy możesz zeskanować tę książkę i wrzucić gdzieś na jakiś serwer np: typu dropbox?
Pozdrawiam
Cześć,
W wolej chwili postaram się to zrobić. Porządkuję właśnie bibliotekę na temat Cobry 1 i może jeszcze się coś znajdzie czego tutaj nie ma.
Jeśli chodzi o generator znaków, to ten udostępniony przez Zdzis_ek wydaje się być bardziej użyteczny, tym bardziej, że oprócz dodatkowych od 192 do 255, taki był sprzedawany. Mam na myśli to, że są polskie litery i że jest pełny komplet semigrafiki 4x4 taki sam, jak w ZX Spectrum. Oczywiście, te dodatkowe znaki również się przydadzą.
Jeśli chodzi o generator z dokumentacji, to kilka postów wyżej pisałem o możliwych błędach w pojedynczych bajtach, w sumie zmieniłem wartość 5 bajtów i wszystkie znaki wyglądają lepiej.
Czy prawdziwy komputer Cobra ma zdolność wyświetlania raz czarnych liter na białym tle, a raz białych na czarnym tle na jednym obrazie? Tylko wtedy teoretycznie można było korzystać z semigrafiki 4x4, bo w zestawie znaków jest pierwsze 8, a drugie 8 dorobiłoby się przez użycie negatywu w miejscach, w których są te znaki.
W oryginalnej COBRZE pozytyw/negatyw ustawiało się za pomocą zwory przełączającej wyjście UCY74165 (układ nr. 109)
W swoim komputerze wyprowadziłem przełącznik na zewnątrz obudowy.
Nic nie stoi na przeszkodzie, aby te dwa wyjścia przełączać dodatkowymi bramkami, których aktywność byłaby zależna od przerzutnika (np. 1/2 7474) ustawianego portem wyjściowym instrukcją programową OUT(port).
Oczywiście przełączanie pozytyw/negatyw dotyczy całego ekranu, a nie pojedynczych znaków.
Miałbym prośbę do twórców emulatora. Czy mógłby ktoś dołożyć obsługę dźwięku kompatybilnego z ZX SPECTUM. To wzbogaciłoby ogromnie COBRĘ o muzykę podczas gier. Kompatybilność umożliwiłaby wykorzystanie ogromnej ilości efektów dźwiękowych i muzyki zczytanych bezpośrednio z kodów maszynowych gier ZX SPECTRUM. Dla posiadaczy oryginalnej COBRY wystarczyłoby kynarem wyprowadzić sygnał z określonego portu i sterować nim przerzutnik w układzie podziału przez 2, którego wyjście przez opornik sterowałoby bezpośrednio tranzystor głośnika.
Akurat wczoraj doimplementowałem do emulatora uproszczoną obsługę dwóch drukarek opisanych w AudioVideo. Uproszczenie polega na tym, że przy odczycie drukarki zawsze jest zwracany stan odpowiadający bezczynności lub oczekiwaniu na następny znak. Pomimo tego druk (a właściwie wyprowadzenie tekstu do pola tekstowego) działa poprawnie. Był jeszcze opis obsługi dalekopisu, ale uznałem, że jego implementacja nie ma sensu, bo po pierwsze jest to dużo bardziej skomplikowane, po drugie, jest to dokładnie to samo, tylko, że inny sposób sterowania drukiem jednego znaku od strony komputera. Głównym celem implementacji drukarki była możliwość wyprowadzenia kodu programu Basic wczytanego z nagrania audio. Wtedy odpada próba przetłumaczenia nagrana na ciąg znaków lub wydobywanie tekstu ze zrzutu pamięci. Dorobiłem też wprowadzanie znaków (keystrokes) z pola tekstowego, które w praktyce jest bardziej potrzebne niż wprowadzanie znaków z pliku.
Skoro wieswas pisze o implementacji dźwięku takiego samego, jak w ZX Spectrum, to jak najbardziej, jest to możliwe, ale przede wszystkim potrzebuję informacji, jak jest ten dźwięk generowany od strony programu, tzn, jaki rozkaz powoduje wygenerowanie dźwięku i jak ustala się jego brzmienie (o ile jest to możliwe). Mając program i informację, co powinienem usłyszeć, będę w stanie zaimplementować obsługę dźwięku. Ja sam nigdy nie programowałem dźwięku w ZX Spectrum, ani nie mam info, jak się go programuje. Poszukam w Google, myślę, że znalazłbym szczegółowe info. Czy to jest tak, że okres czasowy dźwięku (który jest odwrotnie proporcjonalny do częstotliwości) trwa pewien stały odcinek czasu pomnożony przez wartość wystawioną na określony adres? W ten sposób działa pozytywka do CA80.
Emulator udostępnię w ciągu kilku dni, możliwe, że już z dźwiękiem ZX Spectrum.
W ZX SPECTRUM wysyła się na port OUT(FE),A zawartość akumulatora naprzemian 04 i 00. Oznacza to, że jedynie bit 4 liczby wysłanej na ten port jest połączony z głośniczkiem tworząc falę prostokątną o częstotliwości zależnej od częstotliwości wywoływania instrukcji OUT(FE),A
W hardwarowej wersji Cobry jest to banalnie proste, ale w emulatorze na PC może być nieco trudniej, gdyż emulator musi po napotkaniu instrukcji w realizowanym kodzie maszynowym ciągu liczb D3,FE czyli OUT(FE),A
- przeczytać zawartość akumulatora
- sprawdzić czy bit 4 akumulatora jest 1 - wówczas wysłać na głośnik stan H i pamiętać go, aż do chwili gdy w kodzie maszynowym programu (gry) znów pojawi się ciąg D3,FE. Jeżeli znów wystąpi 1 (tylko wówczas, gdy programista omyłkowo zrobił błąd w programie) to nic nie zmieniaj.
Jeżeli po wykryciu D3,FE w akumulatorze 4 bit jest zerem, to wyślij na głośnik stan L. Starałem się opisać w miarę precyzyjnie algorytm działania emulatora po napotkaniu instrukcji OUT(FE),A (4bit=1 lub 4bit=0)
Nie mam pojęcia jak w PC wysłać stan H i L na głośniczek PC lub kartę dźwiękową.
Dodatkowo to musi się odbywać w krótkim czasie , gdyż między tymi instrukcjami należy realizować setki innych.
W ZX SPECTRUM inne bity wysłane na ten port realizują inne funkcje.
W COBRZE nie są używane. Może więc uprościć i nie sprawdzać tego bitu ?
Taka uproszczona wersja przesyłałaby na głośnik lub kartę dźwiękową naprzemian stan H i L po każdym napotkaniu w programie instrukcji mikroprocesora Z-80 : OUT(FE),A z obojętnie jaką zawartością akumulatora.
W moim emulatorze są dwie procedury, jedna uruchamiana przy każdym rozkazie OUT, druga przy każdym rozkazie IN. Ta procedura już określa zachowanie w zależności od adresu i wartości podanej w rozkazie. Biblioteka Qt już zapewnia obsługę dźwięku karty dźwiękowej, jednakże dźwięk musi być buforowany, co objawia się krótkim opóźnieniem w stosunku do zdarzeń generujących dźwięk.
Na przykład, okres dźwięku 500Hz trwa 0,002 sekundy. Czy dobrze rozumiem, że jeżeli wykonam rozkaz OUT (FE), A przy A=04, a po czasie 0,001 sekundy wykonam rozkaz OUT (FE), A przy A=00, a po kolejnym 0,001 znowu będzie rozkaz OUT (FE), A przy A=04, to usłyszę dźwięk 500Hz z wypełnieniem 50%?
A jak wyślę rozkaz OUT (FE), A przy A=04, po czasie 0,0005 sekundy wyśle OUT (FE), A przy A=00, a po czasie 0,0015 wyślę ponownie OUT (FE), A przy A=04 i tak w kółko, to usłyszę również dźwięk o częstotliwości podstawowej 500Hz, ale o bardziej ostrzejszym brzmieniu, bo jest to przebieg o wypełnieniu 25%. Czy dobrze rozumiem?
Druga część wypowiedzi jest jasna. Gdy wytworzymy przebieg prostokątny o wypełnieniu 50%, to dominuje częstotliwość podstawowa (to 500Hz). Dodatkowo występują też harmoniczne, ale dużo słabsze.
Gdy przebieg jest niesymetryczny, to obok częstotliwości podstawowej otrzymamy wiele harmonicznych oraz dodatkowych częstotliwości, które nie spełniają warunku harmonicznych - nie są całkowitymi wielokrotnościami tonu podstawowego.
Daje to efekt bardziej ostrego czy charczącego dźwięku. W grach może to być efekt zamierzony. Jakie dodatkowe częstotliwości uzyskamy zmieniając współczynnik wypełnienia najlepiej obejrzeć w programie AUDACITY nagrywając próbki a następnie oglądając obraz transformacji Fouriera, który pokazuje ten sam przebieg ale nie na poziomej skali czasu, ale na skali częstotliwości. Można tam odczytać które piki są największe i jaki mają częstotliwości.
Natomiast pierwszej części wypowiedzi nie zrozumiałem dokładnie.
Co robi w emulatorze funkcja OUT(nr. portu),A ?
Czy jest przypisana do konkretnego numeru portu ?
Czy dla każdego numeru portu robi coś innego ?
Przecież w Cobrze OUT(1B) powinien uruchomić standardową procedurę BEEP, a OUT(FF) standardową procedurę tworzenia zapisu na taśmie magnetofonu, jeszcze inny numer wystawia stany linii klawiatury.
I jeszcze jedno pytanie: Skoro po instrukcji OUT(nr.portu),A emulator analizuje też zawartość wysłanego z akumulatora bajtu to jak to wykorzystuje ?
Można sobie wyobrazić, że skoro analizuje zawartość akumulatora to można robić dźwięk nie 1-bitowy, a 8-bitowy. Zamiast stanu H i L mamy stany od 0 do 256.
A to już jest całkiem inna jakość dźwięku, choć nieprzydatna dla COBRY:
1-zabiera zbyt dużo zasobów procesora Z-80 aby opisać 8 bitami dźwięk !!!
2-hardware dla oryginalnej COBRY bardzo by się rozbudował. Byłby to już prosty przetwornik cyfrowo-analogowy.
Może lepiej podłączyć układ AY-3-8910 (lub 8912), popularny w licznych komputerkach z okresu świetności Z80 (oraz w grach arcade, gdzie często było ich kilka)? Można by użyć adresów takich jak w Spectrum-2 albo w Amstradach by zachować kompatybilność.
Natomiast pierwszej części wypowiedzi nie zrozumiałem dokładnie.
Co robi w emulatorze funkcja OUT(nr. portu),A ?
Czy jest przypisana do konkretnego numeru portu ?
Czy dla każdego numeru portu robi coś innego ?
Przecież w Cobrze OUT(1B) powinien uruchomić standardową procedurę BEEP, a OUT(FF) standardową procedurę tworzenia zapisu na taśmie magnetofonu, jeszcze inny numer wystawia stany linii klawiatury.
I jeszcze jedno pytanie: Skoro po instrukcji OUT(nr.portu),A emulator analizuje też zawartość wysłanego z akumulatora bajtu to jak to wykorzystuje ?
Można sobie wyobrazić, że skoro analizuje zawartość akumulatora to można robić dźwięk nie 1-bitowy, a 8-bitowy. Zamiast stanu H i L mamy stany od 0 do 256.
A to już jest całkiem inna jakość dźwięku, choć nieprzydatna dla COBRY:
1-zabiera zbyt dużo zasobów procesora Z-80 aby opisać 8 bitami dźwięk !!!
2-hardware dla oryginalnej COBRY bardzo by się rozbudował. Byłby to już prosty przetwornik cyfrowo-analogowy.
W uproszczeniu, emulator pracuje tak, że każdy rejestr jest zmienną liczbową i emulator pracuje w pętli, w której przy każdym przebiegu jest odczytywanych tyle kolejnych bajtów, ile ma dany rozkaz (bajty odczytywane po jednym), a potem, w zależności od rozkazu, jest wykonywana pewna czynność. Jeżeli jest rozkaz OUT (xx), A, to jest wywołanie procedury DoOut, do której jest przekazywany jako argument adres z rozkazu i wskaźnik na zmienną reprezentująca rejestr A. W ramach samej procedury są ify, które uzależniają czynności od adresu, w którym są wyzerowane bity niemające znaczenia (wynika to z konstrukcji układu adresowego Cobry), a następnie jest wykonywana określona czynność, która może wykorzystywać przekazaną wartość.
Dzięki za precyzyjne objaśnienie tej kwestii.
Rozumiem, że obecny emulator zawiera rozpoznanie każdej instrukcji Z-80, ale dla instrukcji OUT(PORT),A brak mu podprogramu, który wykona jakąś czynność (np. przesłania jakiś danych do karty dźwiękowej), więc powraca do dalszej analizy kodu programu jedynie po obiegu pętli zawierającej warunki IF i standardowo zapamiętując stan akumulatora.
Sądzę, że ze względu na kompatybilność z ZX SPECTRUM należy wybrać port FE, ale nie sprawdzać 4 bitu akumulatora albo jeśli emulator standardowo analizuje każdy bit akumulatora dla tej instrukcji, to przypisać ten sam podprogram dla wszystkich warunków IF.
Czyli taki podprogram przy każdym wywołaniu wykonywałby negację dotychczasowego stanu 0 lub 1 i wysyłał na głośnik.
Dla użytkowników hardwarowej wersji COBRY dorobienie sterowania głośnikiem z portu FE jest bardzo proste, ale analizowanie poszczególnych bitów zawartości akumulatora nie byłoby już takie łatwe.
A przecież to emulator ma być dostosowany do COBRY, a nie COBRA do emulatora.
Cały sens istnienia emulatora jest w tym, by tworzone w nim programy działały tak samo w komputerze.
Robię pierwsze próby implementacji dźwięku zgodnego z ZX Spectrum i słyszane dźwięki zgadzają sie z ponizszymi rozważaniami.
Rozkaz NOP (kod operachi 00) zajmuje 4 takty zegara. Procesor ma szybkość 3250000Hz, więc po podzieleniu przez 4 jest 812500 rozkazów NOP na sekundę.
Napisałem taki program testowy:
Start:
LD A, #4
OUT FEh, A
ORG 1000h
LD A, #0
OUT FEh, A
ORG 2000h
JP Start
Ten program kompiluje się nastepująco:
Po etykiecie Start: 3E,04,D3,FE - stan wysoki na głośniku
Po ORG 1000h: 3E,00,D3,FE - stan niski na głośniku
Po ORG 2000h: C3,00,00 - skok do początku
Nie zwarzając na pojedyncze cykle maszynwe i szczegóły rozkazów, można przyjąć, że czas przejścia od adresu 0000 do adresu 1000, czyli 4096 wykonań rozkazu NOP trwa 4096/812500=0,005 sekundy
W powyższym przykładzie, przejście od 0000 do 2000, czyli 8192 rozkazów NOP w przybliżeniu, to 0,01 sekundy, czyli powinienem usłyszeć dźwięk o czestotliwości ok 812500/4096=99Hz
Aby wprowadzić taki program, wystarczy po uruchomieniu Cobry wydatrzy polecenia.
Zerowanie pierwszych 32kB pamięci:
Z0000,7FFF,00
Wprowadzenie rozkazu zapisu 04 do A i wysłania A na FE pod adres 0000:
M0000,3E,04,D3,FE
Wprowadzenie rozkazu zapisu 00 do A i wysłania A na FE pod adres 1000:
M1000,3E,00,D3,FE
Wprowadzenie rozkazu skoku pod adres 2000
M2000,C3,00,00
W przypadku poniższych poleceń będzie dźwięk o częstotliwości 99Hz z wypełnieniem 50%
Z0000,7FFF,00
M0000,3E,04,D3,FE
M1000,3E,00,D3,FE
M2000,C3,00,00
W przypadku poniższych poleceń będzie dźwięk o częstotliwości 812500/2048=397Hz z wypełnieniem 50%
Z0000,7FFF,00
M0000,3E,04,D3,FE
M0400,3E,00,D3,FE
M0800,C3,00,00
W przypadku poniższych poleceń będzie dźwięk o tej samej częstotliwości, ale z wypełnieniem 25%, czyli ostrzejsze brzmienie
Z0000,7FFF,00
M0000,3E,04,D3,FE
M0200,3E,00,D3,FE
M0800,C3,00,00
Czy dobrze wszystko napisałem? Czy tak właśnie wygląda generowanie dxwięku w ZX Spectrum?
wieswas wrote:
Rozumiem, że obecny emulator zawiera rozpoznanie każdej instrukcji Z-80, ale dla instrukcji OUT(PORT),A brak mu podprogramu, który wykona jakąś czynność (np. przesłania jakiś danych do karty dźwiękowej), więc powraca do dalszej analizy kodu programu jedynie po obiegu pętli zawierającej warunki IF i standardowo zapamiętując stan akumulatora.
Zgadza się. Dla każdej obsługiwanej instrukcji Z80 sa wykonane czynności i odliczone cykle maszynowe. Jeżeli jest grupa podobnych rozkazów, to część wspólna jest zawarta w jednej procedurze, która jest uruchamiana z głównej pętli podczas wykonywania danego rozkazu. Dla OUT i IN też jest osobna procedura. Dla przykładu, każdy rozkaz OUT wywołuje taki program (wyciąg z kodu źródłowego emulatora):
Code: c
Log in, to see the code
Jak widać, mozna pod ten rozkaz doimplementować to, co się chce. Dla rozkazów IN jest podobny podprogram.
Dodano po 1 [godziny] 29 [minuty]:
wieswas wrote:
W oryginalnej COBRZE pozytyw/negatyw ustawiało się za pomocą zwory przełączającej wyjście UCY74165 (układ nr. 109)
W swoim komputerze wyprowadziłem przełącznik na zewnątrz obudowy.
Czy w wersji Cobry bez semigrafiki, znaki od 128 do 255 są powtórzonymi znakami od 0 do 127? Czy te dalsze znaki są równoważne, czyli zarówno znak 65, jak i 192 wyświetla białą literę A w tych samych kolorach, czy te dalsze znaki miały wtedy odwrócone kolory?
Na ZX SPECTRUM tak powinno to działać.
Na Cobrze z wyprowadzonym przewodem z portu FE sterującym przerzutnikiem dzielącym przez 2, z wyjścia którego przez opornik jest sterowana baza tranzystora T3 (głośnikowego) także powinno to działać, chociaż tam nie będzie sprawdzany bit 4 akumulatora.
Tam wywołanie OUT(FE),A ustawi na wyjściu H, następny OUT(FE),A stan L , kolejny znów stan H, następny stan L itd.
Stan akumulatora nie miałby żadnego znaczenia.
Może i w emulatorze zrezygnować z badania 4 bitu ?
W ZX SPECTRUM ten sam port służy do różnych funkcji , więc musi być analizowane ustawienie bitów. W COBRZE ten port byłby używany tylko do dźwięku, więc nie trzeba sprawdzać bitów akumulatora.
Korzystając z Twojej pomocy mam jeszcze pytania:
1.Jaki lub jakie numery portów wejściowych są używane do obsługi klawiatury ?
2.Gdzie w programie MONITOR COBRY znajduje się procedura czytania klawiatury ?
3.Czy do modyfikacji emulatora, aby odsługiwał dźwięk wystarczy dokonać przeróbek w kodzie maszynowym, czy będzie wymagana całkowita jego wymiana ?
Na ZX SPECTRUM tak powinno to działać.
Na Cobrze z wyprowadzonym przewodem z portu FE sterującym przerzutnikiem dzielącym przez 2, z wyjścia którego przez opornik jest sterowana baza tranzystora T3 (głośnikowego) także powinno to działać, chociaż tam nie będzie sprawdzany bit 4 akumulatora.
Tam wywołanie OUT(FE),A ustawi na wyjściu H, następny OUT(FE),A stan L , kolejny znów stan H, następny stan L itd.
Stan akumulatora nie miałby żadnego znaczenia.
Może i w emulatorze zrezygnować z badania 4 bitu ?
W ZX SPECTRUM ten sam port służy do różnych funkcji , więc musi być analizowane ustawienie bitów. W COBRZE ten port byłby używany tylko do dźwięku, więc nie trzeba sprawdzać bitów akumulatora.
Rozumiem. W takim razie to będzie działać tak, że wartość wysyłana na OUT nie jest istotna, każde wywołanie OUT na adres FE zmienia stan sygnału na głośniku i żeby uzyskać dźwięk o określonej częstotliwości, należy wywoływać ten rozkaz z odpowiednią częstotliwością.
wieswas wrote:
1.Jaki lub jakie numery portów wejściowych są używane do obsługi klawiatury ?
Klawiatura znajduje się pod adresem mniej znaczący bajt 9C (na tym bajcie należy wykonać iloczyn bitowy z wartością 9C w celu odrzucenia niepodłączonych bitów), ale bardziej znaczący bajt to jeden z następujących: FE, FD, FB, F7, EF, DF, BF, 7F. Klawiatura ma 8 sektorów po 5 klawiszy każdy i w zwracanej wartości 5 najmniej znaczących bitów to stan tych 5 klawiszy.
wieswas wrote:
2.Gdzie w programie MONITOR COBRY znajduje się procedura czytania klawiatury ?
Jakiś czas temu analizowałem ten fragment, jak jeszcze miałem problem z implementacją klawiatury. On znajduje się pod adresami od C5FD do C6BB, jeden fragment przeanalizowałem, później nie próbowałem analizować. Zamieszczam notatkę z tej analizy:
PROCEDURA WYWOŁANA PO SPRAWDZENIU WSZYSTKICH SEKCJI KLAWISZY
JEJ ZADANIEM PRAWDOPODOBNIE JEST WYŁĄCZNIE PRZEKSZTAŁCENIE WARTOŚCI HL W INNĄ WARTOŚĆ HL
-- Stos: 48924
c60e 44 LD B,H
c60f 4D LD C,L
### Do BC jest przepisany HL
c610 1600 LD D,00H Zerowanie rejestru D
c612 CB28 SRA B Przesuniecie w prawo, najmlodszy bit trafia do C, najstarszy pozostaje bez zmian
c614 9F SBC A,A Jeżeli C==1 to A=01, Z=0, jeżeli C==0, to A=00, Z=1
c615 F626 OR 26H Maskowanie rejestru A z 00100110
c617 2E05 LD L,05H
c619 95 SUB L Od A odejmuje się 05
c61a 85 ADD A,L Do A dodaje się 05
c61b 37 SCF Ustawienie flagi C
c61c CB19 RR C Obrot bitowy rejestru C w prawo, flaga C do najstarszego, najmlodszy do flagi C
c61e 38FA JR C,0C61AH
c620 0C INC C
c621 48 LD C,B
c622 2D DEC L
c623 2E01 LD L,01H
c625 20F3 JR NZ,0C61AH
c627 212DC6 LD HL,0C62DH
c62a 5F LD E,A
c62b 19 ADD HL,DE
c62c 37 SCF Ustawienie flagi C
c62d C9 RET Powrot do C60C
-- Stos: 48924
PROCEDURA SPRAWDZAJACA, CZY NACISNIETO KLAWISZ
D:01234
FE9C ^ZXCV
FD9C ASDFG
FB9C QWERT
F79C 12345
D:43210
EF9C 67890
DF9C YUIOP
BF9C HJKL-
7F9C BNM,_
Dane: D3|D4|D5|__|__|D2|D1|D0
-- Stos: 48926
c5e2 21FFFF LD HL,0FFFFH
c5e5 01FFFE LD BC,0FEFFH
c5e8 ED78 IN A,(C) Odczyt adresu FEFF
### w A jest wartość stanu klawiszy pod adresem FEFF - klawisze ^ZXCV
c5ea F601 OR 01H 00000001
c5ec F6E0 OR 0E0H 11100000 < Do tego miejsca przeskakuje z C5FD po sprawdzeniu FD9C, FB9C...7F9C, po FE9C nie ma przeskoku
### w A jest wartość stanu klawiszy, ale tylko D1,D2 przy wejsciu do pętli lub D0,D1,D2 przy kolejnym obrocie
### Chodzi o to, żeby otczytać D0,D1,D2, ale nie uwzględnić SHIFT przy wejściu do pętli, ale przy ostatnim obrocie Shift będzie uwzględniony
c5ee 57 LD D,A
c5ef 2F CPL Odwrocenie wszystkich bitow rejestru A
c5f0 FE01 CP 01H Porownanie z 01, jeżeli A==0, to flaga C jest podnoszona, w przeciwnym wypadku opuszczona
c5f2 9F SBC A,A Jeżeli C==1 to A=01, Z=0, jeżeli C==0, to A=00, Z=1
c5f3 B0 OR B W kazdym obrocie B przyjmuje kolejno: FE,
c5f4 A5 AND L W kazdym obrocie L przyjmuje kolejno: FF,
c5f5 6F LD L,A
c5f6 7C LD A,H
c5f7 A2 AND D
c5f8 67 LD H,A
c5f9 CB00 RLC B Przesuniecie w lewo, najstarszy bit wchodzi do C, do najmlodszego wchodzi 0
c5fb ED78 IN A,(C) Odczyt adresow klawiatury, w kolejnych przebiegach petli: FD9C, FB9C, F79C, EF9C, DF9C, BF9C, 7F9C, FE9C
c5fd 38ED JR C,0C5ECH Przeskok do C5EC, jezeli flaga C podniesiona, nastepuje w petli po kolejnych adresach klawiatury, nie nastepuje po przetworzeniu ostatniego
### Tu wchodzi po ostatnim przebiegu, po drugim odczycie FEFF
c5ff CB1F RR A Obrot bitowy rejestru A w prawo, flaga C do najstarszego, najmlodszy do flagi C
### Teraz w fladze C jest stan klawisza Shift, 0 to naciśnięty, 1 to nienaciśnięty
c601 CB14 RL H Obrot bitowy rejestru H w lewo, flaga C do najmlodszego, najstarszy do flagi C
### W najmłodszym bicie rejestru H jest stan klawisza Shift
c603 3EFF LD A,0FFH Zapis liczby FF do rejestru A
c605 BD CP L Rejestr L teoretycznie zawiera informację o stanie klawiatury, w praktyce nigdy nie zawiera FF
c606 2001 JR NZ,0C609H Przeskok do C609 jezeli L!=FF - warunkiem przeskoku jest liczba inna niż FF w rejestrze L
c608 C9 RET Powrót do programu głównego
### Wydaje się, że wartość HL zależy od naciśniętego klawisza, w praktyce jest ten sam bez względu na klawisz tak, jakby nie było żadnego nacisniętego
c609 CD0EC6 CALL 0C60EH Wywolanie C60E - jako dane wykorzystuje rejestry HL
c60c 7E LD A,(HL) Ladowanie do A komórki pamięci o adresie zapisanym w HL
c60d C9 RET Powrót do programu głównego
-- Stos: 48926
FRAGMENT PROGRAMU GLOWNEGO, W KTORYM PROGRAM ZATRZYMUJE SIE OCZEKUJAC NA KLAWISZ
-- Stos: 48928
c6bb CDE2C5 CALL 0C5E2H Wywolanie C5E2
c6be FEFF CP 0FFH Porownanie rejestru A z FF
### Jeżeli wywołano C60E, to nie wychodzi się z pętli, w przeciwnym razie się wychodzi
c6c0 20F9 JR NZ,0C6BBH Przeskok do C6BB jezeli A nie jest zerowy (flaga Z opuszczona) - ZAWSZE NASTEPUJE !!!
-- Stos: 48928
Rozkaz CP:
A < val ==> S=1, Z=0
A = val ==> S=0, Z=1
A > val ==> S=0, Z=0
Wnioski:
Program dochodzi do adresi C6BB, z którego następuje wywołanie programu C5E2, który sprawdza, czy jest naciśnięty
jakiś klawisz, jeżeli żaden klawisz nie jest nacisnięty, to w rejestrze A jest FF i program C5E2 wywołuje się ponownie.
Potem następuje sprawdzenie adresów sekcji, przy czym na początku jest adres FEFF z pominięciem (maskowaniem Shift),
w ostatnim przebiegu jest ponownie odczytywany adres FEFF, ale już bez maskowania klawisza Shift.
wieswas wrote:
3.Czy do modyfikacji emulatora, aby odsługiwał dźwięk wystarczy dokonać przeróbek w kodzie maszynowym, czy będzie wymagana całkowita jego wymiana ?
Czy pytasz się o zmiany w emulatorze, czy w programie monitora uruchomionym wewnątrz emulatora? Jeżeli to pierwsze, to po prostu robię zmiany w kodzie źródłowym i kompiluję. Jeżeli to drugie, to zależy, co chciałbyś otrzymać i jak chciałbyś używać. Jeżeli to są niewielkie zmiany, to lepiej przerobić istniejący kod maszynowy, podobnie, jak jakiś czas temu wcisnąłem obsługę wielkich i małych liter w programie monitora.
@wieswas, czy pamiętasz z jakich elementów jest zbudowany moduł który odpowiada za emulacje zx spectrum na cobrze (czy zachował się jakiś schemat, cokolwiek co mogłoby przybliżyć w jaki sposób tego dokonałeś).
Zgodnie z obietnicą, wysyłam nową wersję emulatora. Doimplementowałem następujące funkcje:
1. Generator dźwięku zgodny z ZX Spectrum
2. Możliwość odwrócenia kolorów
3. Wczytywanie naciśnięć klawiszy z pola tekstowego, nie tylko z pliku
4. Symulacja drukowania
5. Możliwość ustalenia początkowej zawartości pamięci RAM
Do: andrzejlisek
Dziękuję, za jak zwykle precyzyjne wyjaśnienie procedury obsługi klawiatury na PW.
Jestem pełen podziwu nad szybkością tworzenia implementacji nowych procedur w emulatorze.
Procedura MONITORA COBRY wywołana przez CALL C5E2 sprawdza stan klawiatury i wraca z kodem wciśniętego znaku w akumulatorze.
We własnych grach wystarczy zwykle obsługa 5 klawiszy (A-lewo,S-prawo,W-góra,Z-dół i Enter-strzał) więc mocno to upraszcza pisanie gier.
Do: jackfinch
Niestety nie mam żadnych notatek, gdyż nie sądziłem, że jeszcze kiedykolwiek wrócę do tematu Cobra.
Ale jak widać na fotografiach zamieszczonych przez Muzeum Komputeryzacji i Informatyki w Katowicach na ich stronie, nie ma tam żadnych specjalistycznych układów scalonych.
Przeróbka polegała na maskowaniu pierwszych 16kB dynamicznej pamięci RAM przez EPROM z zawartością oryginalnego ROMu ZX SPECTRUM oraz części pamięci dynamicznej Cobry pamięcią statyczną wyświetlacza ZX-spectrum.
Wszystkie pamięci są na stałe podłączone do magistrali adresowej i danych COBRY, a tylko wspólnym przełącznikiem włączałem aktywację/dezaktywację grup pamięci COBRY/ZX SPECTRUM. Do obsługi odczytu pamięci video wykorzystywałem te same sygnały co w Cobrze. Procesor też był wspólny.
Ale to było ponad ćwierć wieku temu i bez notatek trudno o szczegóły.
Mam też pytanie: jak zapisać zawartość fragmentu pamięci z wklepanym kodem programu w emulatorze do pliku na PC ? Po wciśnięciu klawisza F3 (zapisz zawartość) otwiera się okno do wpisania nazwy pliku *.HEX oraz katalogu docelowego, ale nie wiem gdzie mu podać zakres adresów, które chciałbym zapisać, aby następnego dnia znów wczytać i kontynuować wklepywanie kodu.
Mam też pytanie: jak zapisać zawartość fragmentu pamięci z wklepanym kodem programu w emulatorze do pliku na PC ? Po wciśnięciu klawisza F3 (zapisz zawartość) otwiera się okno do wpisania nazwy pliku *.HEX oraz katalogu docelowego, ale nie wiem gdzie mu podać zakres adresów, które chciałbym zapisać, aby następnego dnia znów wczytać i kontynuować wklepywanie kodu.
Jak zatwierdzisz nazwę pliku, to pojawią się dwa okna dialogowe z prośbą o wpisanie początkowego i końcowego adresu. Przy wczytywaniu analogicznie pojawia się okienko z prośba o podanie adresu początkowego.
Ja testowałem wszystkie gry na moim emulatorze i działają. Tylko gra "Nalot" jest w nietypowym pliku WAV. Wystarczy wczytać do programu dźwiękowego i zapisać jako standardowy WAV, ale ja to już uczyniłem i na którejś z poprzednich stron umieściłem przerobione pliki z tą grą.
Ponieważ Cobra ma obecnie wiele rozszerzeń w stosunku do oryginału, chciałbym zapytać, jaki zakres pamięci RAM najlepiej stosować podczas tworzenia programów i gier w kodzie maszynowym, aby nie kolidowały z obszarem przeznaczonym na zmienne i programy BASICA , a równocześnie wszyscy mieli dostęp do tego zakresu ?
I drugie pytanie, czy dałoby się ujednolicić dodatkowe znaki semigrafiki ?
Taka normalizacja ułatwiłaby wzajemną wymianę.
Ponieważ Cobra ma obecnie wiele rozszerzeń w stosunku do oryginału, chciałbym zapytać, jaki zakres pamięci RAM najlepiej stosować podczas tworzenia programów i gier w kodzie maszynowym, aby nie kolidowały z obszarem przeznaczonym na zmienne i programy BASICA , a równocześnie wszyscy mieli dostęp do tego zakresu ?
Czy chodzi Ci o zarezerwowanie pewnej przestrzeni adresowej na te dodatkowe rozszerzenia? Ja myślę, że, ta zarezerwowana pamięć to byłaby od adresu 4000 do BFFF (16kb), a jak mniej, np. 4 kB, to od B000 do BFFF. Basic TRS za każdym razem pyta się o wielkość pamięci, wydaje mi się, że wpisywanie za każdym razem 32 spowoduje, że Basic nie będzie zajmować więcej niż od 0000 do 7FFF, ale nie jestem tego pewien.
Jeżeli chcesz tworzyć program w ASM lub w C, nawet ze wstawkami ASM, to po co Ci jednoczesna praca tego programu i Basica? W tym przypadku masz do dyspozycji te 32kB lub 36kB na program i do tego obszaru kompilator SDCC skompiluje i wczytasz do Cobry.
wieswas wrote:
I drugie pytanie, czy dałoby się ujednolicić dodatkowe znaki semigrafiki ?
Moim zdaniem najlepiej przyjąć za standard czcionkę utworzoną przez zdzis_ek, którą też załączam jako domyślną do emulatora w poprzednim poście. W stosunku do tej z Audio-Video, zawiera różne ikony, elementy ramek, a także pełne 16 kombinacji podziału znaku na 4 kwadraty tak, jak w ZX Spectrum.