top of page

Programant l'OPL4 (Part FM)


Què és el Moonsound?

El Moonsound és el primer cartutx que es va crear que utilitzava el xip OPL4 (YMF278B) per al MSX. Va ser creat per en Henrik Gilvad (també va crear el Graphics 9000) i distribuït per Sunrise l'any 1995.


Moonsound en la meva història

En aquella època podies aconseguir aquestes ampliacions de Hardware a través de Hnostar, el Moonsound costava 35,000 pessetes i el Gfx9000 40,000 pessetes. Com que no tinc una oïda gaire fina, vaig decidir comprar el Gfx9000 i experimentar amb les imatges que és el que sempre m'ha fascinat. Vaig estar trastejant amb ell i mirant quins programes hi havia a l'escena MSX que el fessin servir. Malauradament els programes que sortien utilitzaven el Moonsound en comptes del Gfx9000. Després el meu contacte amb el MSX es va anar diluint: ja no comprava cap fanzine (després que Hnostar i SDMesxes pleguessin) i només seguia les notícies del msx.org. Fins que em vaig decidir a recuperar l'ordinador de casa als meus pares a finals del 2022. En aquest període de reenganxament he intentat aconseguir un OPL4 en qualsevol de les versions que hi havia (Moonsound, Wozblaster, Shockwave, DalSolRi, Monster sound Fm Blaster i MSX-Blaster) sense èxit. Fins que el Cristiano (the retrohacker) va contactar amb mi i em va dir que estava fent una tirada de Wozblaster. Per fi!!!. Després de tant de temps perseguint-lo el vaig aconseguir.


Les meves capacitats musicals segueixen igual que fa vint anys, pràcticament nul·les. Ara bé, gràcies al roboplay faig servir el Moonsound de juke box. Tinc un fitxer m3u amb totes les cançons que he anat recopilant i faig que cada 3 minuts canviï de cançó. He creat 10 còpies del fitxer amb les línies en un altre ordre (gràcies a la comanda sort -R del linux) i així sembla que l'escolta sigui aleatòria.


Buscant informació per escriure aquest article, he descobert que el roboplay també reprodueix VGM i he descarregat tot de fitxers de VGMrips pels xips d'OPL1 (YM3526), OPL2 (YM3812), OPL3 (YMF262) i com no, l'OPL4 (YMF278). Si els descomprimeixes (el roboplay no reprodueix VGZ, només VGM, però només cal descoprimir-los amb un gunzip i obtens el VGM) ja poden ser reproduïts directament pel roboplay. Però també he descobert el vgm-conv que em permet passar els fitxers VGM de l'OPLL a l'OPL2.


I ara, a part de fer-lo servir de jukebox, anem a estudiar-lo i aprendre millor les seves capacitats.


Especificacions del Moonsound

El xip OPL4 conté la mateixa part FM que l'OPL3 però hi afegeix tota una part de síntesi d'ona, semblant a l'ADPCM del MSX Audio (el xip Y8950 que és un OPL1 afegint aquest canal de síntesi d'ona).


A diferència de l'OPLL, que tenia instruments prefabricats, els OPL1-4 permeten configurar un instrument propi per cada canal. Anem a resumir breument les capacitats i evolucions dels xips OPL:

  • OPL1: 9 canals FM ó 6 FM + 5 percussions. Nom xip YM3526 (el Y8950 és el MSX-Audio que hi afegeix un canal ADPCM. El MSX-Music, l'OPLL, és una simplificació de l'OPL1).

  • OPL2: Igual que l'anterior, però permet 4 formes d'ona diferent.

  • OPL3: Afegeix 4 formes d'ona noves i a part configura els canals FM amb operacions de 4 operands. Es pot tenir un total de 18 canals. Les operacions dels xips anteriors a l'OPL3 són de 2 operands.

  • OPL4: La part FM és la mateixa que l'OPL3, però afegeix una part de samples, basada en PCM. Tecnologia semblant a l'ADPCM del MSX-Audio.


Part FM

Per entendre bé els registres de la part FM, s'ha d'explicar el concepte de slot que s'utilitza a la documentació de Yamaha. Un slot és un circuit que produeix una ona sinusoidal. Aquests slots són els operands que hem comentat abans que poden ser agrupats en grups de 2 o en grups de 4. L'OPL4 disposa de 36 slots, que si fem la combinació mínima de 2 operands, tenim els 18 canals FM que es publiciten a les especificacions. No tots els slots es poden configurar en grups de 4, només 24 slots, quedant 12 per agrupar-se en 2 operands.


Per configurar els slots, hi ha dos bancs de registres, el banc 0 i el banc 1, que són gairebé idèntics, varien els 8 primers registres que són de configuració i el banc0 té el registre 0xBD que és pels ritmes. El banc 0 és per programar els 18 primers slots i el banc1 els 18 següents. Un esquema dels registres és:

A part de l'explicació de la documentació oficial de Yamaha, també hi ha una molt bona explicació en aquesta guia de programació de l'OPL3.


Hi ha dos tipus de registres, els que afecten els slots que van en blocs de 0x16 registres i que van del 0x20 al 0x95, aquests registres serveixen per donar la forma d'ona al slot; i els que afecten els canals que van en blocs de 9 registres, del 0xA0 al 0xB8, que serveixen per donar la nota i l'octava i algun efecte sonor. Tot això per cada banc de registres. Però 0x16 registres són 22 slots, i només hi ha 18 slots per banc, com és això? Doncs perquè els registres de slots 0x?6, 0x?7. 0x?E i 0x?F no s'utilitzen, no modifiquen cap slot. Aquí teniu una gràfica que servirà per consulta ràpida de l'adreça corresponent a cada slot. L'indicador set pren el valor 0 quan es tracta dels registres del primer banc i 1 quan es tracta del segon banc.


Però com s'agrupen els slots per fer canals? Tal i com he comentat abans, hi ha diferents combinacions, no és lliure, no puc combinar els slots 3, 4, 7 i 11, per exemple, sinó que només es poden agrupar d'una determinada manera. En aquesta documentació hi ha una taula per cada combinació, però jo ho he resumit tot en una:

Aquí tenim els 18 canals que permet l'OPL4 amb dues operacions. Cada columna són les operacions de 2 slots, així, el canal 3 està format pels slots 6 i 9. Aquests slots es poden combinar amb operacions de 4, que són els que tenen el mateix color i en l'ordre d'operands de més petit a més gran slot, per exemple, el canal de 4-op número 0 està fet pels slots 0, 3, 6 i 9, desapareixent així el canal 3 a l'hora de modificar els blocs de 9 registres, com ara el F-number i l'octava. Si vull configurar també el canal 2 com a 4-op, desapareix el canal 5 i els slots que forment el canal 2 són 2, 5, 8 i 11. Si configurem per tenir les percussions del FM, perdem els canals 6, 7 i 8 i passen a formar el Bass Drum (12 i 15), Hi-Hat (13), Tom-Tom (14), Snare Drum (16) i Cymbal (17).


En aquest últim esquema he utilitzat una numeració decimal per indicar l'slot. Si volem modificar l'slot 7, hem de tenir en compte que el seu offset per modificar un valor dels registres de configuració de slots. Per exemple el Key Scale Level, no hauré de modificar el registre 0x47, sinó que serà el 0x49 ja que no hi és el 0x?6 i el 0x?7.


Val la pena perdre 3 canals FM per fer percussions? En el cas del Moonsound, molts dels compositors feien servir tota la part FM i després utilitzaven la part d'ones per fer la percussió, ja que així podien posar el seu so samplejat, ja que es comentava que el so que venia per Yamaha era molt pobre.


Per definir un instrument, hi ha molts paràmetres i moltes combinacions que afecten el timbre de so generat (més semblant a un xilòfon, a un piano, a una campana, etc.) com puc saber la variació d'un d'aquests paràmetres com afectarà al so generat?


Avui en dia hi ha el Furnace, un tracker opensource inspirat en el Deflemask (que té una versió antiga que és gratuita). A diferència del Deflemask, el Furnace té molts més xips pels quals es pot crear música. A la documentació del furnace de com generar instruments, indiquen aquest videotutorial explicatiu de com fer-ho en el deflemask (la interfície és molt semblant a la del furnace). Hi ha més videotutorials del Deflemask que del Furnace, però degut a la seva semblança, també et poden ajudar a fer funcionar el Furnace. Si a part d'aquesta explicació pràctica estàs interessat en la part teòrica de le generació FM, pots consultar aquest document explicatiu.


I per MSX natiu què tenim? Quan va sortir el Moonsound, es va adaptar el Moonblaster per poder tenir un tracker que funcionés amb el nou xip. Es van treure 2 versions el Moonblaster FM i el Moonblaster Wave que servien per fer funcionar la part FM i la part Wave, respectivament, del Moonsound. El MBFM no configurava els 3 canals de percussió sinó que tenia 6 canals Wave per utilitzar com a percussió. He intentat esbrinar la part de generació d'instruments del MBFM però no l'he sapiguda trobar.


Mirem doncs d'utilitzar la generació d'instruments del Furnace per generar nous instruments (o carregar els que ja vénen en l'aplicació) i poder-los aprofitar amb l'MSX. Els instruments del Furnace es guarden en arxius amb fui mentre que els de Deflemask tenen l'extensió dmp. Ens centrarem en el primer ja que és de codi lliure i també permet carregar els instruments del dmp.


Hi ha dos tipus de formats d'instruments en el Furnace, l'antic que té les seves especifcacions aquí i el nou que podem trobar les seves especificacions aquí. Els instruments que he carregat dins de la instal·lació del Furnace segueixen el format antic, però només hem de carregar-lo en el Furnace i gravar-lo perquè agafi el nou format. Una de les avantatges del nou format és que ocupa menys lloc.


Càrrega fitxers .fui

Quina aplicació farem per estudiar el Moonsound? Doncs he pensat que podem fer una aplicació que ens carrega l'instrument que s'ha dissenyat amb el Furnace i toca un parell de notes. Pel mateix preu, també mostra els paràmetres extrets del fitxer. Anem doncs primer a veure com és l'estructura d'aquest fitxer.


fui format

L'estructura del fitxer fui versió moderna, es pot trobar al github del furnace. La documentació està en format text i no és gaire agradable visualment. Per això he fet aquest resum:

Els 6 primers bytes és la capçalera que comença amb la paraula FINS per identificar el tipus de fitxers. Després és possible que hi hagi el nom de l'instrument, que això està format pel codi NA, seguit de 2 bytes que indiquen la longitud de la cadena del nom i que estan codificats en little endian, és a dir, que si llegim els valors de 0x0700 la longitud correcte és de 7 bytes en lloc de 1792. L'últim byte de la cadena és el 0x0. Després ja podem trobar la comanda de FM i la longitud del bloc de codificació. Tots els bytes següents tenen diferents paràmetres per configurar xips FM, però no tots són usats per l'OPL4, he marcat en color blau els que sí que utilitzem. El primer byte de configuració, just després de la longitud de bloc de la part FM, indica sí la configuració és per 2 operands o per 4 operands. Si és per 2 operands, primer va l'slot 0 del canal i després el següent slot i si és de 4 operands, el primer és per l'slot 0 del més baix del canal, després per l'slot 0 de l'altre canal que es fusiona per fer les operacions de 4 operands i després els slot 1 dels respectius canals.


Al costat de l'esquema he posat l'adreça a on trobem aquesta configuració en el fitxer que hem fet servir d'exemple i que trobareu al gitlab de MoltSXalats. Veureu que a partir de la posició 0x10 es van repetint per cadascun dels operands i que el fitxer no té la comanda NA a on s'indica el nom.


Segons aquesta estructura anem a fer el codi per trobar la part FM de l'instrument guardat en format fui i configurar el Moonsound:

Comencem amb els includes i tot seguit tenim la definició de MS_WAIT com a macro que és un tros de codi que va llegint un registre del Moonsound fins que ja canvia de valor. A diferència de l'OPLL que no ens indicava quan podíem tornar a escriure i per això teníem dues funcions d'escriptura, una pel Z80 i una per l'R800, aquí no ens farà falta, ja que l'espera és independent del processador, només depèn de l'OPL4.


Per escriure als registres, utilitzarem la funció del sdcc de __sfr __at que fa un out a la direcció que indiquem, en el nostre cas 0xC4, 0xC5, 0xC6 i 0xC7. Els dos primers són pel primer banc de registres FM i els dos darrers pel segon banc de registres. A l'article de l'OPLL, utilitzàvem una funció que contenia ensemblador, el codi en ensemblador feia els OUT i instruccions per esperar. Aquí com que no ens cal mesurar l'espera en tenim prou amb els sfr.


A les línies 18-23 comprovem que hi hagi un Moonsound insertat llegint si hi ha informació al registre del Moonsound, si retorna tot 0s és que no està connectat.


Les línies 26-31 defineix la funció ms_fm1_write que escriu al registre reg del primer bloc el valor data que li passem, primer li indiquem al Moonsound quin registre volem modificar, esperem que ens indiqui que ja està disponible per una següent comanda amb la funció macro MS_WAIT i després li indiquem la dada que ha de guardar. La funció ms_fm2_write fa el mateix però pel segon bloc de registres.


A partir d'aquí comencen les funcions que hem utilitzat en diferents articles de llegir dades del disquet: FT_SetName, FT_errorHandler i FT_openFile. Hem modificat una mica la funció FT_errorHandler perquè pugui esciure els missatges d'error que no hi ha FINS al fitxer que es vol llegir i que no s'ha trobat cap Moonsound.

Finalment tenim la funció WAIT que espera els cicles que indiquem i que utilitzarem després de cada nota per poder-la sentir durant un cert temps. També podríem haver utilitzat el WaitForKey() de la llibreria Fusion-C i esperar que l'usuari apreti una tecla quan vulgui.


Després de la funció WAIT definim el buffer per anar guardant els bytes que llegirem, com a màxim llegirem 5 a la vegada i li he dit al sdcc que la guardi a l'adreça 0x8000, d'aquesta manera és més fàcil de debugar, però en una versió final no faria falta i tindríem tot el bloc de variables seguit.


I comencem el main. A la línia 111 detectem si hi ha Moonsound, en cas que no llancem error i acabem. A la 115 obrim el fitxer i a la 119 inicialitzem el Moonsound dient-li que utilitzarem els registres de l'OPL3 (FM) i els de l'OPL4 (wave).


Després definim el canal que utilitzarem, en aquest cas hem escollit el canal 2 que juntament amb el 5 formen un canal de 4 operands (columnes vermelles de la taula canals/slots/operands, també hagués pogut escollir el 0 i el 3, o l'1 i el 4, o els equivalents del banc 1). Tot seguit definim un array que contindrà la distribució dels slots que utilitzarem.


I comencem a llegir el fitxer. El primer que fem és mirar si el fitxer és de tipus FINS (línia 126), si no ho és acabem senyalant l'error. I llegim els següents 4 bytes que no tenen utilitat en aquesta configuració. Després anem llegint bytes i detectem si la comanda és NA, que conté el nom de l'instrument, o si és FM. Si és NA llegim els bytes del nom i no en fem res. Si és FM, acabem el bucle de llegir bytes i comencem la part de codi per configurar tot el FM.


Llegim els 2 bytes que formen el tamany del bloc FM i els guardem en la variable blockLength per poder comprovar que hàgim llegit tots els bytes que controlarem amb numBytesLLegitsBlockFM.


Tot seguit ve el byte de configuració que ens indica si és un instrument 2-op o 4-op (dos operands o quatre). Si és de dos utilitzarem el canal 1 amb els slots 1 i 4. I si és de 4 operands utilitzarem els canals 2 i 5. Fixeu-vos a la línia 176 que l'offset pel canal 8 és 0x0A tal i com indica la taula slots/offsets, i la del 11 és 0x0D.


El següent byte conté la informació del paràmetre alg i fb, que guardem en diferents variables per poder-les processar tot seguit. Ja que la variable alg guarda la informació de l'algoritme, del tipus de connexió dels 4 operands per generar el so FM, i el fb és un paràmetre de configuració del canal i no de slot com els altres.


Segons el número d'operands tindrem 4 opcions possibles o només la connexió senzilla. Tot i que el fb només afecta el primer canal, mirant el que feia el furnace he vist que configurava aquest paràmetre en els dos canals.


Una altra característica del byte 0xC? és que indica per quin altaveu ha de sonar, l'esquerra, el dret o tots dos. És una propietat que se m'havia passat per alt i que el vgm2txt que utilitzava per debugar tampoc transcrivia a text. Després de moltes proves diferents i d'anar comprovant valors vaig descobrir que no activava els bits 4 i 5 dels registres 0xC? corresponent al canal. És per això que sempre poso un 3 als bits superior d'aquest registre.


Després de posar el bit de 0xC0 de l'algoritme, llegim dos bytes més que no s'utilitzen en l'OPL4.


I finalment la configuració dels slots dels registres 0x2?, 0x4? i 0x6?. Com que he determinat el canal que utilitzava, tota la configuració és pel bloc 0, si volgués configurar el bloc1, utilitzaria la comanda ms_fm2_write. Aquests registres estan barrejats en el fitxer fui, per tant llegeixo els 5 bytes a on es troben i extrec cada paràmetre (línies 234-247) per després configurar els registres corresponents (línies 249-251). El byte SL i RR està en el mateix format que el registre i per tant el configuro directament. Llegim un altre byte que no utilitzarem i finalment queda escollir la forma d'ona. Les últimes línies del bucle presenten per pantalla els valors llegits.


A la línia 271 defineixo que no utilitzaré drums i ja només ens queda definir la nota que volem tocar. A l'igual que l'OPLL té el F-number per indicar la nota i el bloc per indicar l'octava. Jo he utilitzat el F-number 582 que es correspon a la nota la, però quan estava debugant, he vist que el furnace també utilitzava un altre valor per la nota la. Això també passava amb el Moonblaster que tenia valors diferents de F-Number que la documentació.


El valor F-Number és un enter i es reparteix en dos registres diferents tal com mostren les línies 277 i 278. A més a més, a la línia 278 també configurem l'octava i activem el canal. Amb el que la nota ja està sonant, esperem uns segons i passem a tocar l'altra nota.


Per tocar la següent nota, primer s'ha d'apagar la que està sonant i configurar la següent, com es feia a l'OPLL. Que és el que fem a les línies 285 i 287. Esperem que soni la nota en la nova octava, l'apaguem i acabem l'script.


Conclusions

En aquest article hem vist com és la part FM del Moonsound (OPL4), hem presentat els diferents conceptes de slots i canals que utilitza la documentació i hem carregat un instrument en el format del Furnace (fui) per tocar dues notes.


 Com exercici per practicar amb el codi, es pot acabar de controlar els bytes llegits amb la variable numBytesLLegitsBlockFM i comprovar que coincideixen amb els blockLength.


L'Avelino Herrera té un parell d'articles a les revistes call MSX 3 i call MSX 4 a on parla del Moonsound. El primer és sobre la part Wave que encara no hem tractat i que veurem més endavant i el segon és sobre la part FM. Aquest segon explica també els registres i en comptes de carregar un fitxer fui per configurar-los utilitzar els sbi. Un altre exercici que podeu fer és passar el codi en C de l'article que trobareu a la seva web al format de la llibreria Fusion-C. Jo ja ho he fet, ja que vaig estar investigant perquè no em sonava. El resultat el podeu trobar al gitlab de MoltSXalats.


Els que no pogueu esperar a l'article de MoltSXalats sobre la part wave, comentar-vos que a part de l'article de l'Avelino, també hi ha aquest del Moai-Tech número 5 en paper (no confondre amb el nou format digital) que és més semblant al que tractarem aquí.


També m'agradaria explicar-vos, que per debugar he utilitzat l'eina vgm2txt dels vgmtools. El furnace permet exportar a VGM, que és un format que va indicant els registres que es van modificant en una cançó i per tant pots veure tots els passos que fa perquè soni. A part, l'openMSX també té una comanda per gravar en VGM. Només cal que obrim la consola amb F10 i escriguem vgm_rec start Moonsound per començar a gravar i que després posem vgm_rec stop per finalitzar. La consola ja ens indicarà a on ha guardat el fitxer. No us oblideu de cridar l'openMSX amb l'extensió Moonsound (-ext moonsound)! El format VGM és en binari, per això he utilitzat el vgm2txt, que malauradament no tenia la transcripció dels bits estereo del registre 0xC?.


Si voleu provar algun altre instrument de la llibreria del Furnace, només cal que canvieu el nom del fitxer a la línia 115 (i que copieu aquest fitxer al directori que utilitzeu com a dsk en l'openMSX). O si no voleu recompilar el programa podeu copiar el fitxer al directori canviant-li el nom per op4bell.fui.


Apa doncs, que disfruteu del MSX!


Clica aquí per a descarregar la imatge .dsk d'un exemple funcionant.



Opmerkingen


bottom of page