Ero sivun ”PSR70 software” versioiden välillä

Helsinki Hacklabin wikistä
Siirry navigaatioon Siirry hakuun
 
(4 välissä olevaa versiota samalta käyttäjältä ei näytetä)
Rivi 12: Rivi 12:
 
cross-referenssit ja siitä saa ulos myös HTML:ää, jossa selaimessa katseltuna kaikki call- ja jump-osoitteet ovat linkkejä. Koodia on kätevää selailla tuossa muodossa.
 
cross-referenssit ja siitä saa ulos myös HTML:ää, jossa selaimessa katseltuna kaikki call- ja jump-osoitteet ovat linkkejä. Koodia on kätevää selailla tuossa muodossa.
  
Tämän vaiheen tuotokset (intel-hex ja disassembly) löytyvät projektin Github-repositorystä.
+
Tämän vaiheen tuotokset (intel-hex ja disassembly) löytyvät projektin [https://github.com/JKN0/PSR70-reverse Github]-repositorystä.
  
  
Rivi 94: Rivi 94:
  
  
=== OPQ-kirjoitusten monitorointa ===
+
=== OPQ-kirjoitusten monitorointia ===
  
 
[[Tiedosto:PSR70_dump_analyzer.png|300px|thumb|right|OPQ-kirjoitusten analysointia Pythonilla]]
 
[[Tiedosto:PSR70_dump_analyzer.png|300px|thumb|right|OPQ-kirjoitusten analysointia Pythonilla]]
Rivi 119: Rivi 119:
 
koskettimen painallus tekee alle parikymmentä kirjoitusta piirille. Mutta suuri osa jäi pimentoon.
 
koskettimen painallus tekee alle parikymmentä kirjoitusta piirille. Mutta suuri osa jäi pimentoon.
  
Erilaiset kokeilut ottaa ulos sekvenssejä palasina eivät johtaneet mihinkään kunnolliseen tulokseen. Lisäksi Arduionen SoftwareSerialilla tuntui olevan ongelmia ottaa näitä ryöppyjä vastaan
+
Erilaiset kokeilut ottaa ulos sekvenssejä palasina eivät johtaneet mihinkään kunnolliseen tulokseen. Lisäksi Arduinon SoftwareSerialilla tuntui olevan ongelmia ottaa näitä ryöppyjä vastaan
 
hukkaamatta mitään, mikä vielä pahensi saatujen sekvenssien epäluotettavuuta. Yritin myös hidastaa toimintaa siten, että odotellaan funktiossa, että sarjaportti saa lähetettyä. Se sotki muun
 
hukkaamatta mitään, mikä vielä pahensi saatujen sekvenssien epäluotettavuuta. Yritin myös hidastaa toimintaa siten, että odotellaan funktiossa, että sarjaportti saa lähetettyä. Se sotki muun
 
ohjelman toiminnan, koska siellä ei ole varauduttu, että nopeaksi ajateltu kirjoitusfunktio alkaa kestää millisekuntitolkulla.
 
ohjelman toiminnan, koska siellä ei ole varauduttu, että nopeaksi ajateltu kirjoitusfunktio alkaa kestää millisekuntitolkulla.
Rivi 139: Rivi 139:
 
=== ROM2 tutkimuksia ===
 
=== ROM2 tutkimuksia ===
  
Tässä vaiheessa siirryin hetkeksi tutkimaan ROM2:n mysteerejä. Hardwaren tutkimisesta jäänyt käsitys siis oli, että se on 32 kB ROM, joka näkyy prosessorin osoitevarauudessa 16 kB blokkina,  
+
Tässä vaiheessa siirryin hetkeksi tutkimaan ROM2:n mysteerejä. Hardwaren tutkimisesta jäänyt käsitys siis oli, että se on 32 kB ROM, joka näkyy prosessorin osoiteavaruudessa 16 kB blokkina,  
 
blokki valitaan 8255:n PC4-bitillä.  Nastajärjestyksensä puolesta se olisi varmaankin soveltunut luettavaksi prommerilla, mutta sekin oli juotettu piirilevylle, enkä halunnut ruveta
 
blokki valitaan 8255:n PC4-bitillä.  Nastajärjestyksensä puolesta se olisi varmaankin soveltunut luettavaksi prommerilla, mutta sekin oli juotettu piirilevylle, enkä halunnut ruveta
irrottamaan sitä. Joten päätin tehdä ohjelman, joka lukee ROM2:n ja dumppaa sisällön sarjaportista ulos. Näin saatu heksadumppi löytyy Githubista. Ajoin sen myös disassemblerin läpi  
+
irrottamaan sitä. Joten päätin tehdä ohjelman, joka lukee ROM2:n ja dumppaa sisällön sarjaportista ulos. Näin saatu heksadumppi löytyy [https://github.com/JKN0/PSR70-reverse Githubista]. Ajoin sen myös disassemblerin läpi  
 
siltä varalta, että siellä olisi suoritettavaa koodia. Ja olihan siellä sitäkin.
 
siltä varalta, että siellä olisi suoritettavaa koodia. Ja olihan siellä sitäkin.
  
Ohjlemoija on ollut hiukan vainoharhainen, koska hän on tehnyt keskeytyspalvelurutiinin alkuun muutaman käskyn sekvenssin, joka asettaa ROM2:n ylimmän osoitebitin (8255:n PC4-bitin) nollaksi  
+
Keskeytyspalvelurutiinin alussa on muutaman käskyn sekvenssi, joka asettaa ROM2:n ylimmän osoitebitin (8255:n PC4-bitin) nollaksi siltä varalta, että keskeytyksen tullessa ollaan suorittamassa ylemmässä pankissa olevaa rutiinia. Mekanismi, jolla pankin asetus palautetaan keskeytyspalvelun loputtua jäi epäselväksi, mutta sellainen on pakko olla.
siltä varalta, että se omia aikojaan ponnahtaisi ykköseksi. Koska tämä tehdään keskeytyspalvelussa, joka voi tulla mihin väliin tahansa, se estää ROM2:n ylemmän blokin käytön mitenkään
 
järjellisesti mihinkään. Voidaan siis olla varsin varmoja, että ainoastaan alempi 16 kB on käytössä.
 
  
 
ROM2 sisältää ison joukon erilaisia funktioita, joita EPROM-osuus kutsuu hyppytaulukon kautta. Samoin ROM2:n funktiot kutsuvat EPROM-funktioita hyppytaulukon kautta. Tämä on vanha tekniikka,
 
ROM2 sisältää ison joukon erilaisia funktioita, joita EPROM-osuus kutsuu hyppytaulukon kautta. Samoin ROM2:n funktiot kutsuvat EPROM-funktioita hyppytaulukon kautta. Tämä on vanha tekniikka,
Rivi 154: Rivi 152:
  
 
Suoritettavan koodin lisäksi iso osa ROM2:sta sisältää vakiotaulukoita, tärkeimpänä soitinsoundien määrittelyt. Nämä ovat käytännössä talukoita, joiden avulla alustetaan OPQ-piiri tuottamaan
 
Suoritettavan koodin lisäksi iso osa ROM2:sta sisältää vakiotaulukoita, tärkeimpänä soitinsoundien määrittelyt. Nämä ovat käytännössä talukoita, joiden avulla alustetaan OPQ-piiri tuottamaan
haluttua soundia. Oletan ymmärtäneeni suunnilleen formaatin, miten soundit talletetaan. Tällä perustella laskien sieltä löytyy noin 80 erilaista soundia. Etupaneelista voi valita 32 soundia
+
haluttua soundia. Oletan ymmärtäneeni suunnilleen formaatin, miten soundit talletetaan. Tällä perusteella laskien sieltä löytyy noin 80 erilaista soundia. Etupaneelista voi valita 32 soundia
 
(16 ORCHESTRA + 16 SOLO) ja automaattisäestykset käyttää muutamaa eri soundia, mutta selvästi siellä on kokonaan käyttämättömiäkin. En ole vielä tehnyt ohjelmaa, joka ottaisi noita  
 
(16 ORCHESTRA + 16 SOLO) ja automaattisäestykset käyttää muutamaa eri soundia, mutta selvästi siellä on kokonaan käyttämättömiäkin. En ole vielä tehnyt ohjelmaa, joka ottaisi noita  
 
käyttämättömiä soundeja käyttöön, mutta se täytyy jossain vaiheessa tehdä.
 
käyttämättömiä soundeja käyttöön, mutta se täytyy jossain vaiheessa tehdä.
 
  
 
=== SDCC käyttöön ===
 
=== SDCC käyttöön ===
Rivi 180: Rivi 177:
  
 
Tällä periaatteella olen nyt pommittanut OPQ-piiriä pitkään ja hartaasti, ja lopputuloksena on varsin kattava käsitys siitä, miten sitä ohjataan ja mikä on minkäkin rekisterin vaikutus.
 
Tällä periaatteella olen nyt pommittanut OPQ-piiriä pitkään ja hartaasti, ja lopputuloksena on varsin kattava käsitys siitä, miten sitä ohjataan ja mikä on minkäkin rekisterin vaikutus.
Toistaiseksi tieto on sekalaisissa muistiinpanoissa ja päässä, mutta tarkoitus on kirjoittaa tästä "OPQ programmer's guide"-tyyppinen dokumentti.
+
Tästä syntyi dokumentti nimeltä "Programmer’s Guide to Yamaha OPQ FM Synthesizer", joka löytyy projektin [https://github.com/JKN0/PSR70-reverse/blob/master/Guides/OPQ_ProgGuide.pdf Githubista].
 +
Jos ei tykkää latailla koko pakettia Githubista, pelkkä PDF löytyy myös [https://www.dropbox.com/s/8q1spizgkqvi8ru/OPQ_ProgGuide.pdf?dl=0 täältä].
  
 
Jos tämän jälkeen vielä innostusta riittää, tavoite on tehdä vastaava temppu myös RYP4-piirille, josta siitäkään ei ole netissä juurikaan tietoa tarjolla.
 
Jos tämän jälkeen vielä innostusta riittää, tavoite on tehdä vastaava temppu myös RYP4-piirille, josta siitäkään ei ole netissä juurikaan tietoa tarjolla.
  
 
* [https://wiki.helsinki.hacklab.fi/Yamaha_PSR70_reverse_engineering Projektin pääsivulle]
 
* [https://wiki.helsinki.hacklab.fi/Yamaha_PSR70_reverse_engineering Projektin pääsivulle]

Nykyinen versio 25. heinäkuuta 2020 kello 09.40

EPROMin lukeminen

EPROMin luenta

Ohjelmiston reversaaminen alkoi hardware-operaatiolla nimeltä EPROMin irrotus. Vaikka ohjelma oli EPROMissa, piiri oli ikävästi juotettu paikalleen ilman kantaa. Onneksi piirilevy on yksipuolinen, niin EPROM irtosi täysissä ruumiin ja sielun voimissa kohtalaisen helposti imukolvia käytellen. Juottelin tilalle kannan.

Luin EPROMin vanhalla prommerilla, ja ohjelmoin toiseen EPROMiin, koska vanhan piirin jalkoihin jää väkisinkin hiukan tinaa, joka ei oikein toimi kantaan työnnettynä. Soitin toimi uudella EPROMilla moitteitta, joten luentaoperaatio voitiin todeta onnistuneeksi. Ohjelmointilaitteesta tuloksen saa ulos Intel-hex-formaatissa, ja tästä sen voi sitten ajaa disassemblerin läpi. Käytin tähän yazd-disassembleria, joka on osoittautunut varsin hyväksi softaksi Z80-projekteissa. Se tuottaa monipuoliset cross-referenssit ja siitä saa ulos myös HTML:ää, jossa selaimessa katseltuna kaikki call- ja jump-osoitteet ovat linkkejä. Koodia on kätevää selailla tuossa muodossa.

Tämän vaiheen tuotokset (intel-hex ja disassembly) löytyvät projektin Github-repositorystä.


Ohjelman analysointia

Tavoite ei ollut ymmärtää ohjelmistoa läheskään kokonaan, siellä on paljon epäkiinnostavia osia, kuten automaattisäestysten pyörittäminen, omien säestysten sekvensointi, nauhalle talletukset ym. Mutta joitakin perusjuttuja siitä olisi syytä tunnistaa ja ymmärtää.

Yleiskuvaltaan koodi näyttää siltä, että se olisi ihmisen kirjoittamaa assemblyä, vieläpä monen ihmisen, ei niinkään kääntäjän tuotosta. Epäyhtenäiset tavat, esim. parametrien välitys funktioon tapahtuu välillä pinossa, välillä rekistereissä, eivät oikein sovi kääntäjän tuottamaksi. Samoin siellä on välillä sen tason optimointeja, että 80-luvun kääntäjä tuskin osasi sellaista.

Reset-vektori on aina hyvä lähtökohta lähteä seurailemaan koodia. Pian sen jälkeen tulee yleensä eri I/O-piirien alustukset. Nyt kun muisti- ja I/O-kartat on jo valmiiksi piirretty, alustukset on varsin helppo tunnistaa ja ymmärtää. UART:in ja 8255:n alustukset ovat selkeitä sen kun vertailee datalehteen. Yamahan omat piirit ovat hankalampia, OPQ:ssa on 256 rekisteriä, RYP4:ssä 128, ja niiden kaikkien merkitys on tässä vaiheessa tuntematon.

Toinen hyvä lähtökohta on keskeytysvektori, sen perästä löytyy keskeytyspalvelu. Tässä on helppo tunnistaa tuo hardwaren perusteella arvattu kaava: ensimmäisenä käydään lukemassa jokaiselta keskeytyskykyiseltä piiriltä sopiva statustieto, josta päätellään, oliko tämä piiri se, joka keskeytti, ja kutsutaan sitä piiriä vastaavaa keskeytyspalvelufunktiota. Tätä kautta selvinneitä asioita:

  • OPQ:n rekisterissä 00 on statustavu, jonka lukemalla näkee, onko sillä keskeytyspyyntö päällä. Kirjoittamalla OPQ:n rekisteriin 03 arvo 71H keskeytys kuittaantuu, ja ilmeisesti samalla alustuu uudelleen keskeyttämään taas hetken kuluttua. Funktion toiminnallisuudet näyttävät kovasti reaaliaikakeskeytykseltä. Eli tämä on se reaaliaikakeskeytys, joka näkyi skoopilla, väli on 10 ms, ja se tulee siis OPQ-piiriltä. Ei mitenkään yllättävää, myös Yamahan OPL-piiri (YMF262) sisältää timerin ja keskeytyskyvykkyyden. Saadaan säästettyä erillinen ajastinpiiri.
  • Midi-sarjaliikenne tapahtuu keskeytyspohjaisesti, ja lähetystä ja vastaanottoa varten on rengaspuskurit. Keskeytyspalvelu lähettää lähetyspuskurista ja tallettaa vastaanottopuskuriin. Erikseen on sitten funktiot, jotka kirjoittavat lähetyspuskuriin ja lukevat vastaanottopuskuria. Nekin löytyy puskurien osoitteiden kautta.

Erityisen kiinnostava oli funktio, jota kutsutaan keskeytyspalvelusta:

033F: E1                         L033F: POP     HL
0340: D1                                POP     DE
0341: E3                                EX      (SP),HL
0342: 3A 00 C0                   L0342: LD      A,(0C000h)
0345: 17                                RLA
0346: 38 FA                             JR      C,L0342
0348: 26 C0                             LD      H,0C0h
034A: 73                                LD      (HL),E
                                        ...

Tuossa selvästi kirjoitetaan pinossa jälkimmäisenä ollut parametri ulkoiseen muistiosoitteeseen C0xxH, missä xx = pinossa ensimmäisenä ollut arvo. Aivan ilmeinen OPQ-piirille kirjoitusfunktio, koska muistikartan mukaan OPQ on osoitteissa C000H...C0FFH. Näköjään myös aina pitää odottaa, että OPQ:n rekisterin 00 (osoite C000H) ylin bitti nollaantuu, ennen kuin kirjoitetaan mitään. Jonkinlainen busy-bitti siis.

Funktiota L033F kutsutaan yazd:n cross-referenssin mukaan noin 60 eri kohdasta koodia, ja suoria kirjoituksia C0-alkaviin muistiosoitteisiin ei löydy. Voidaan siis olettaa, että kaikki OPQ-piirille menevät kirjoitukset menevät tämän yhden ja saman funktion kautta. Funktiokutsujen isosta määrästä johtuen alkaa mennä hankalaksi analysoida mitä kaikkea OPQ-piirille kirjoitetaan ja missä järjestyksessä. Yritin tätä vähän tehdä, mutta totesin, että ei taida tulla mitään. Jätin asian hautumaan.


Hello world

Romulointi käynnissä
Sarjaporttisovitin Arduinolla

Seuraava vaihe oli yrittää saada oma ohjelma pyörimään PSR-70:n laitteistossa. Tähän tarvitaan avuksi vanhaa kunnon Romulaattoria.

Niille, jotka eivät tunne Romulaattoria, pieni selostus: se on EPROM-emulaattori, jonka rakensin useampi vuosi sitten. Siinä on liitin, joka työnnetään kohdejärjestelmän EPROMin kantaan, ja se näyttelee EPROMia kohdejärjestelmälle päin. Todellisuudessa se sisältää RAMmia, johon voidaan kirjoittaa haluttu sisältö lähettämällä sille sarjaportista intel-hex-tiedosto. Lisäksi Romulaattorilla voi ohjata kohdejärjestelmän reset-signaalia siten, että se pitää kohdeprosessoria resetissä sen aikaa, kun emuloituun EPROMiin kirjoitetaan uusi sisältö. Kun reset vapautetaan, kohdejärjestelmän prosessori lähtee suorittamaan vastaladattua ohjelmaa iloisesti luullen suorittavansa normaalia EPROMiin talletettua ohjelmaa.

Ensimmäisenä pitää tietysti tehdä Hello world. Soittimen etupaneelissa on kyllä lähemmäs 100 lediä, mutta ne ovat kaikki monimutkaisen ja tuntemattoman custom-piirin ohjaaman sarjaväylän takana, joten niistä mikään ei sovellu perinteiseksi hello world -ledivilkuttimeksi. Mitään helpommin ohjattavia ledejä ei ole. Pitää siis yrittää hyödyntää sarjaporttia.

Laitteen ainut sarjaportti on midi-portti, ja hardwaren analysointivaihe jo sanoi, että baudinopeus on midin mukainen kiinteä nopeus 31,25 kb/s, ja tähän ei voi ohjelmallisesti vaikuttaa. Tämä on yleiskäyttöisenä sarjaporttina hiukan hankalaa, kun mikään pääte-emulaattoriohjelma ei halua toimia tuolla nopeudella. Lisäksi midi out-puolen kytkentä oli muutenkin erikoinen, enkä saanut sitä toimimaan yleensäkään midi out-porttina. Päädyin sitten ottamaan lähetyspuolen suoraan piirilevyltä ulos TTL-tasoisena sarjaporttina, ja kytkin väliin Arduinon, joka pyörittää SoftwareSerialin avulla tehtyä hyvin yksinkertaista "toisesta portista sisään ja toisesta ulos"-nopeusmuunninta, jolla nopeus muunnetaan standardinopeudeksi.

Sen jälkeen kirjoittamaan yksinkertaisinta mahdollista Hello worldia Z80-assemblerilla, ilman mitään hienouksia: viiveet busy-looppeina ja sarjalähetys UARTin statuksia pollaamalla. Kääntäjänä toimii yaza, joka hoitaa sen homman riittävän hyvin. Srecord-apuohjelma tekee sitten yazan tuottamasta binääristä intel-hexiä ladattavaksi Romulaattoriin.

Ohjelma lähtikin toimimaan kohtalaisen helposti, ja tulosti innokkaasti Hello worldia, mutta jotain selittämättömiä outouksia ja epämääräisyyksiä esiintyi. Pähkäilin näiden kanssa pitkään ja ihmettelin, eikö minulla vielä olekaan laitteisto hallussa, tapahtuuko täällä jotain, mitä en ole tajunnut? Lopulta tulin siihen tulokseen, että olen törmännyt samaan mystiikkaan, jonka kanssa Depili oli repinyt hiuksiaan reversatessaan videotiskiä.

Pitkällisten ihmettelyjen ja testailujen jälkeen tulin siihen tulokseen, että tämä on bugi Romulaattorissa. Intel-hexin vastaanotto menee sopivilla ehdoilla pieleen ja ohjelma kirjoittuu virheellisenä emuloituun EPROMiin. Syyksi paikallistui srecordin oletuksena tuottamat 32-tavuiset intel-hex-tietueet, nämä aiheuttavat hankalia ajoitusongelmia Romulaattorin sisäisessä käsittelyssä. Olen käyttänyt Romulaattoria paljonkin, mutta tämä ei ole koskaan tullut esiin, kun sattumoisin kaikki käyttämäni kääntäjät ovat tuottaneet oletuksena 16-tavuista intel-hexiä, joka ei aiheuta ongelmia. Tämän tajuttuani säädin srecordin komentoriviparametrit siten, että se tuottaa 16-tavuisia tietueita, ja ongelmat loppuivat siihen. Pitänee joskus korjata tuo Romulaattorikin; on aina ärsyttävää, kun työkalut ei toimi.

Kun näistä ongelmista oli päästy, tein Hello worldista keskeytyspohjaisen version, joka käyttää viiveisiin OPQ:lta saatavaa 10 ms reaaliaikakeskeytystä ja lähettää sarjadatan UARTin keskeytyksen kautta. Kun sekin toimi, voidaan tyytyväisenä todeta, että keskeytyssysteemikin on ymmärretty oikein.


OPQ-kirjoitusten monitorointia

OPQ-kirjoitusten analysointia Pythonilla

Edelleen päätavoitteena oli saada selville, miten OPQ-piiriä pitäisi ohjata ja miten kukin rekisteri vaikuttaa piirin tuottamaan ääneen. Rekistereitä on 256 ja niillä jokaisella 256 arvoa, joten sokkona arvojen kirjoittelu rekistereihin ei johda mihinkään järjelliseen tulokseen. Alkuperäisen ohjelman tekemät kirjoitukset olisivat hyvä lähtökohta, mutta kuten edellä sanoin, niiden kaivaminen koodista osoittautui varsin työlääksi, enkä sitä kautta päässyt kovinkaan pitkälle.

Vaikutti vahvasti siltä, että kaikki kirjoitukset OPQ-piiriin menevät yhden ja saman funktion kautta. Tämä voisi olla lähtökohta: monitoroidaan, millä parametreilla tätä funktiota kutsutaan, ja siitä saadaan valmiit sekvenssit, mitä piirille pitää kirjoittaa missäkin tilanteessa. Tavaran voisi ottaa ulos sarjaportista. Tavoite olisi säilyttää alkuperäinen ohjelma toimivana monitoroinnin lisäämisestä huolimatta, jolloin soitinta voi käyttää normaalisti ja samalla voi seurata, mitä kaikkia kirjoituksia OPQ-piirille tehdään, kun etupaneelista valitaan toimintoja tai koskettimistoa soitetaan. Tämän toteuttaminen käytännössä vaati kyllä omat kikkailunsa.

Aluksi tein modatun version alkuperäisen EPROMin sisällöstä:

  • Koska haluan sarjaportin omaan käyttööni, normaali sarajaportin lähetyspuskuriin kirjoittava funktio pitää muuttaa dummyksi, joka ei tee mitään. Alkuperäinen ohjelma haluaa lähettää jatkuvasti mm. Active Sensing midi-sanomaa useita kertoja sekunnissa, joten tämä pitää saada pois sotkemasta.
  • OPQ-kirjoitusfunktion alkuun pitää tehdä hyppy lisärutiiniin, joka ottaa talteen funktion kutsuparametrit ja vie ne lähetyspuskuriin, josta alkuperäinen keskeytyspohjainen UART-lähetysrutiini hoitaa ne sitten ulos. Lopuksi pitää vielä suorittaa se alkuperäinen OPQ:lle kirjoituskin, jotta soitin jatkaa toimintaansa.

Tämä osoittautui ajoituksellisesti hankalaksi, koska jokaista rekisterikirjoitusta kohti pitää lähettää minimissään 2 tavua sarjaportista (rekisterin numero ja arvo). Kun baudinopeus on mikä on, kestää yhden kirjoituksen tietojen lähettäminen 640 us. OPQ-piiriä alustettaessa sinne kirjoitetaan lähes kaikki rekisterit, osa useampaan kertaan, jolloin tapahtuu yli 300 kirjoitusta. Ne tulevat niin nopeasti kuin ohjelma pystyy ne tekemään, eli millisekunneissa. Koska sarjaportin puskuri on varautunut vain lähtevien midi-sanomien käsittelyyn, se on varsin pieni (128 tavua), eli täyttyy saman tien.

Yritin ensin tällä, mutta totesin nopeasti, että suurin osa sekvensseistä jää tulostumatta, kun puskuri on täyttynyt ajat sitten. Lyhyitä sekvenssejä tälläkin sai ulos, esim. yksittäisen koskettimen painallus tekee alle parikymmentä kirjoitusta piirille. Mutta suuri osa jäi pimentoon.

Erilaiset kokeilut ottaa ulos sekvenssejä palasina eivät johtaneet mihinkään kunnolliseen tulokseen. Lisäksi Arduinon SoftwareSerialilla tuntui olevan ongelmia ottaa näitä ryöppyjä vastaan hukkaamatta mitään, mikä vielä pahensi saatujen sekvenssien epäluotettavuuta. Yritin myös hidastaa toimintaa siten, että odotellaan funktiossa, että sarjaportti saa lähetettyä. Se sotki muun ohjelman toiminnan, koska siellä ei ole varauduttu, että nopeaksi ajateltu kirjoitusfunktio alkaa kestää millisekuntitolkulla.

Tulin siihen tulokseen, että data pitää mahtua kerralla puskuriin, ja sitä lähetetään sieltä sitten kaikessa rauhassa, siihen tahtiin että SoftwareSerialkin pysyy kyydissä. OPQ:lle kirjoittaminen on kuitenkin purskeittaista, eli kirjoitusryöpyn jälkeen on runsaasti aikaa, jolloin ei tule uutta dataa. Löysin RAMmista yhtenäisen noin 1,5 kB alueen, jossa ei ole irtomuuttujia. Joku iso puskuri siis. Mahdollisesti vaikka omien säestysten talletuspuskuri, joten jos noita ei aio tehdä, ohjelma ei todennäköisesti kirjoita sinne mitään. Otin härskisti tämän omaan käyttööni, sinne isoimmatkin kirjoitusryöpyt mahtuu hyvin. Lisäksi muutin lähetystahtia siten, että vain yhden OPQ-kirjoituksen tiedot trigataan lähtemään reaaliaikakeskeytyksen tullessa. Tällöin saadaan tulostettua 100 OPQ-kirjoitusta sekunnissa, ja puskurin purkaminen kestää muutaman sekunnin, mutta se ei haittaa, kunhan data on luotettavaa eikä siitä puolet ole hukattu.

Kaiken tämän jälkeen alkoivat tulostuvat sekvenssit näyttää varsin luotettavilta. Nyt oli ilo katsella, kun painelee etupaneelista soundinvalintoja ja muita säätöjä, niin sarjaportista valuu pitkä lista OPQ-rekisterikirjoituksia, jotka voi sitten pääteohjelmasta tallettaa tiedostoksi myöhempää analysointia varten.

Koska PSR-70:n ohjelma rakenteestaan johtuen kirjoittelee rekisterit aika sekalaisessa järjestyksessä, sekvenssiä sellaisenaan on hankala analysoida. Tein tätä varten Pythonilla pienen apuohjelman, joka analysoi sekvenssiä ja kerää sieltä yhteenvedon, mihin rekistereihin kirjoiteltiin mitäkin arvoja. Tämä on lukijalle paljon havainnollisempi formaatti, kun pyrkii havaitsemaan patterneja, mitä kirjoitetaan mihinkäkin.


ROM2 tutkimuksia

Tässä vaiheessa siirryin hetkeksi tutkimaan ROM2:n mysteerejä. Hardwaren tutkimisesta jäänyt käsitys siis oli, että se on 32 kB ROM, joka näkyy prosessorin osoiteavaruudessa 16 kB blokkina, blokki valitaan 8255:n PC4-bitillä. Nastajärjestyksensä puolesta se olisi varmaankin soveltunut luettavaksi prommerilla, mutta sekin oli juotettu piirilevylle, enkä halunnut ruveta irrottamaan sitä. Joten päätin tehdä ohjelman, joka lukee ROM2:n ja dumppaa sisällön sarjaportista ulos. Näin saatu heksadumppi löytyy Githubista. Ajoin sen myös disassemblerin läpi siltä varalta, että siellä olisi suoritettavaa koodia. Ja olihan siellä sitäkin.

Keskeytyspalvelurutiinin alussa on muutaman käskyn sekvenssi, joka asettaa ROM2:n ylimmän osoitebitin (8255:n PC4-bitin) nollaksi siltä varalta, että keskeytyksen tullessa ollaan suorittamassa ylemmässä pankissa olevaa rutiinia. Mekanismi, jolla pankin asetus palautetaan keskeytyspalvelun loputtua jäi epäselväksi, mutta sellainen on pakko olla.

ROM2 sisältää ison joukon erilaisia funktioita, joita EPROM-osuus kutsuu hyppytaulukon kautta. Samoin ROM2:n funktiot kutsuvat EPROM-funktioita hyppytaulukon kautta. Tämä on vanha tekniikka, jota olen itsekin käyttänyt kauan sitten, kun kehitettävä ohjelma jakaantuu useaan EPROMiin, etkä halua jokaisen muutoksen jälkeen ohjelmoida koko EPROM-satsia uusiksi. Edelleen kyllä ihmettelen, miksi osa softasta on maski-ROMissa ja osa EPROMissa. Tämä yksilö lienee kaikenkaikkiaan jonkinlainen varhainen versio PSR-70:stä, ehkä myöhemmissä versioissa on EPROMkin korvattu maski-ROMilla.

Suoritettavan koodin lisäksi iso osa ROM2:sta sisältää vakiotaulukoita, tärkeimpänä soitinsoundien määrittelyt. Nämä ovat käytännössä talukoita, joiden avulla alustetaan OPQ-piiri tuottamaan haluttua soundia. Oletan ymmärtäneeni suunnilleen formaatin, miten soundit talletetaan. Tällä perusteella laskien sieltä löytyy noin 80 erilaista soundia. Etupaneelista voi valita 32 soundia (16 ORCHESTRA + 16 SOLO) ja automaattisäestykset käyttää muutamaa eri soundia, mutta selvästi siellä on kokonaan käyttämättömiäkin. En ole vielä tehnyt ohjelmaa, joka ottaisi noita käyttämättömiä soundeja käyttöön, mutta se täytyy jossain vaiheessa tehdä.

SDCC käyttöön

Oli jo tullut aika jättää assembler-maailma taakse ja siirtyä C-kielen ihanuuksiin. Tähän päätin käyttää SDCC-kääntäjää, joka osaa tuottaa koodia usealle eri CPU:lle, mukaanlukien Z80. Olen käyttänyt SDCC-kääntäjää 8051-projekteissa paljonkin, mutta en Z80:n kanssa. Eipä tuo iso siirtymä ollut, sopivat komentorivivalitsimet piti hakea, itse koodissa näkyvät erot ovat pieniä. Porttasin vakioympäristöni tähän hardwareen: kesksytyspohjaiset timerit ja sarjaliikenteen. Sillä jo pääseekin tekemään kaikenlaista testiohjelmaa.

Tässä vaiheessa piti saada myös sarjaportin toinen suunta käyttöön, jotta pääsee keskustelemaan testiohjelmien kanssa sivistyneesti komentorivikäyttöliittymällä. Midi in-portti oli jo aiemmin todettu toimivaksi, joten Arduino-muuntimeen vain sitten pieni yhden transistorin puskurointi, joka lähettää datan midi-portille kelpaavana, ja näin meillä onkin kaksisuuntainen sarjaliikenne käytössä.


Testailua OPQ:n kanssa

Envelopen analysointia

Vihdoin aletaan päästä itse asiaan. Kirjoittelin C:llä testiohjelman, joka lähettää OPQ:lle edellä kaapatun alustussekvenssin, jotta piiri saadaan soimaan halutulla soundilla. Sen jälkeen ohjelma alkaa lähettää piirille tasaiseen tahtiin samoja sekvenssejä, jotka on kaapattu kosketinta painettaessa ja päästettäessä, eli alkaa "painella kosketinta". Sitten siinä on vielä sarjaporttikäyttöliittymä, josta voi samaan aikaan antaa komentoja tyyliin "kirjoita tämä arvo tähän OPQ:n rekisteriin". Sitten vain kuunnellaan, mitä tapahtuikaan soundille tämän muutoksen seurauksena, tarvittaessa katsellaan myös skoopilla.

Tällä periaatteella olen nyt pommittanut OPQ-piiriä pitkään ja hartaasti, ja lopputuloksena on varsin kattava käsitys siitä, miten sitä ohjataan ja mikä on minkäkin rekisterin vaikutus. Tästä syntyi dokumentti nimeltä "Programmer’s Guide to Yamaha OPQ FM Synthesizer", joka löytyy projektin Githubista. Jos ei tykkää latailla koko pakettia Githubista, pelkkä PDF löytyy myös täältä.

Jos tämän jälkeen vielä innostusta riittää, tavoite on tehdä vastaava temppu myös RYP4-piirille, josta siitäkään ei ole netissä juurikaan tietoa tarjolla.