het VIJGEBLAD 22. Uitgave van de HCC Forth Interesse Groep. 4e kwartaal 1987

Forth Orgel
Deel II: Software

door: Han de Bruijn

De computer is een COMX-35 [ google search ] voorzien van een FORTH compiler. Het orgel is een Eminent 300 [ 310 bijna ].
Het idee van een interface tussen deze apparaten werd, wat de hardware betreft, succesvol gerealiseerd. De laatste aflevering van deze tweeledige serie gaat over software.

CFORTH konventies

Om een interface vanuit FORTH te kunnen aansturen is zowiezo kennis nodig van machinecode of assembler. In ons geval dient men met name te weten hoe de COMX FORTH interpreter ("CFORTH", Ken Tracton 1983) gebruik maakt van de processor registers. De 1802 heeft zestien van zulke registers; ze worden aangeduid als R0,R1,...,RF. Voor ons van belang zijn:
R2    = FORTH return stack pointer
R7,R8 = vrij te gebruiken (lokaal)
R9    = FORTH data stack pointer
RC    = programma teller van NEXT
RE    = vrij te gebruiken (globaal)
Van de overige registers zijn er sommige gereserveerd voor interrupt en DMA (R0,R1); andere zijn pointers naar BASIC CALL en RETURN procedures (R4,R5,R6); weer andere hebben een vaste funktie in het FORTH operating system (R3,RA,RB,RD,RF). Wij zullen verder niet moeilijk doen, en alleen vrij gebruik maken van de registers R7,R8 en RE. Ook de return stack zal ons incidenteel (via R2) van nut zijn. De pointer in R9 hebben we nodig om parameters op te vissen van de data-stack.
De syntax van machinecode definities in FORTH is als volgt:
                 /  instruktie  C,  instruktie  C, ...  \
HEX CREATE naam |                                        | SMUDGE
                 \  instruktie+instruktie  ,  .....     /
Een bijzondere plaats wordt ingenomen door de 1802 instruktie DC (hex): maak RC tot programma teller. Dit betekent dat NEXT wordt aangeroepen, waarmee de executie van het machinecode-woord beëindigd wordt.
Het voert natuurlijk te ver om hier de instruktieset van de CDP1802 te gaan behandelen. Daarvoor moeten wij u verwijzen naar een boek: Tom Swan, "Programmer's guide to the 1802" (1981). Informeer verder bij de 1802 gebruikersgroep. Een en ander werd uitgebreider behandeld in COMX NEWS, de nummers 22 en 23.
In COMX NEWS 24 bespraken wij een simpele toepassing van het bovenstaande, gekombineerd met hardware voor beginners. Het komt erop neer dat we een LED aansluiten op de "flip-flop Q lijn". De volgende FORTH funkties maken van deze LED een knipperlicht:
HEX CREATE AAN 7BDC , SMUDGE
   CREATE UIT 7ADC , SMUDGE DECIMAL
1000 VARIABLE TELLER  : WACHT TELLER @ 0 DO LOOP ;
: KEER_KNIPPEREN 0 DO AAN WACHT UIT WACHT LOOP ;
100 KEER KNIPPEREN    (cr)

Homofone musicode

Een FORTH koderingswijze voor monofone muziek werd door mij gepubliceerd in het Vijgeblad nr. 15 (1985). Eerlijkheidshalve dien ik te wijzen op een soortgelijk, en eerder, artikel van Rieks Joosten: "Muziek op de Apple" in nr. 14 (1984). En ik was verrast door de demonstratie op de FIG stand van de HCC dagen, waarbij driestemmig werd gemusiceerd door drie PC's. (Zie "3 Muzikale Clowns" in het Vijgeblad nr. 20 van dit jaar.)
Is iedereen me weer te vlug af geweest ? Niet serieus; het moge duidelijk zijn dat beide wegen naar het doel volkomen verschillen.
In het geval dat 3 PC's samen spelen zal feitelijk toch sprake zijn van drie monofone muziekstukken, zij het voorzien van een mechanisme voor de synchronisatie (dirigent). Dit is in wezen het polyfone idee: horizontale schrijfwijze, per afzonderlijke partij. De mogelijkheid die ik zelf heb uitgebuit is daaraan tegengesteld: een vertikale schrijfwijze namelijk, niet per partij, maar per afzonderlijke samenklank. Homofone musicode.
Een muzikaal moment wordt volgens de homofone beschouwingswijze als volgt omschreven. Men bouwt een akkoord op, bijvoorbeeld C E G , en brengt het vervolgens tot klinken. Dit laatste zal door ons worden aangeduid met een liggende streep _ (underscore). Daarna wordt onmiddelijk een nieuwe samenklank opgebouwd en tot klinken gebracht, bijvoorbeeld C F A _ . Essentieel is de uitbreiding met "tot klinken brengen". Om die reden is homofone musicode niet echt opwaarts kompatibel met het monofone koncept. Dit neemt niet weg dat veel FORTH woorden van de "platte" éénstemmige notatie hun oorspronkelijke betekenis kunnen behouden. Wij geven nu een opsomming van het door ons gebruikte repertoire.
C C# D D# E F F# G G# A A# B = 12 tonen van de chromatische toonladder
LC LC# LD LD# LE LF LF# LG LG# LA LA# LB = idem, één oktaaf lager
HC HC# HD HD# HE HF HF# HG HG# HA HA# HB = idem, één oktaaf hoger
_ = met vastgestelde tijdsduur tot klinken brengen van een akkoord
\ = vaststellen van de tijdsduur voor _ , volgens: aantal_tellen ->
LO = verlagen met één oktaaf
HI = verhogen met één oktaaf
VOET = bevat een kodering van de registratie ( 1 = aan , 0 = uit ):
      eerste bit = hoog , tweede bit = midden , derde bit = laag
We zullen zodadelijk zien dat elke samenklank op een volgend muzikaal moment automatisch wordt uitgezet. Deze default kan echter overschreven worden door:
^ = doorklinken van het akkoord (Klavarskribo "doorklinkpunt")
Tenslotte enkele algemene voorzieningen, die strikt genomen niet tot het Musicode applikatie repertoire behoren:
START = zet de computerbesturing van het orgel aan
STOP = zet de computerbesturing van het orgel uit (default)
STIL = forceer stilte, wanneer de zaak uit de hand loopt

Verzamelingenleer

Is it still possible for mathematicians to contribute to the theory of music?

Bij eenstemmige muziek kan er maar één noot tegelijk klinken. Dit betekent dat een vorige toon automatisch door de volgende wordt uitgezet. Deze vanzelfsprekendheid gaat niet langer op voor meerstemmige muziek. Een voorbeeld moge de situatie verduidelijken. Beschouw de volgende frase:
C E G _ C F A _ . Neem voor het gemak aan dat alleen de laagste register voetmaat in werking is: 4 VOET ! We kunnen dan de kontaktnummers berekenen welke bij de samenklank C E G behoren. Dat zijn in ons geval 13, 17 en 20 .
Om dit akkoord tot klinken te brengen zouden moeten worden overgezonden de kodes 2 x kontakt - 1 = 25,33,39 . Het eerstvolgende muzikale moment wordt gevuld door de samenklank C F A . Dit zou overeenkomen met output van: 25,35,43 . Echter, het getal 25 korrespondeert met de C , die ook al aanwezig is in C E G . Let op de verbindingsboog in het notenschrift. Er is geen enkele reden om een toon die al klinkt nogmaals aan te zetten. Resteert uitvoer van de kontakten 18 en 22. Maar behalve de samenklank C F A horen we nog steeds de tonen E G , want die hebben we vergeten uit te zetten. Dit wordt verholpen door 2 x (17,20) - 2 = 32 en 38 op poort 2 uit te voeren. Samenvattend zien we dat de akkoordwisseling C B G _ C F A _ gepaard gaat met oversturen van de volgende kodes: 35,43 en 32,38 . Het is belangrijk dit resultaat te generaliseren, alvorens ook maar een letter te programmeren. Als volgt.
Het opbouwen van een akkoord is gelijkwaardig met het verzamelen van kontaktnummers. De aldus gekonstrueerde verzameling noemen wij AAN .
Wanneer een akkoord eenmaal tot klinken is gebracht, dan worden dezelfde kontaktnummers overgebracht naar een andere verzameling, genaamd UIT .
Op dit punt aangeland is het zinnig een begrip uit de verzamelingenleer naar voren te halen. Het verschil van twee verzamelingen X en Y , hetgeen we noteren als X \ Y , is gedefinieerd als de verzameling van alle elementen van X die niet behoren tot Y .
Akkoordwisseling vindt nu in zijn algemeenheid plaats als volgt.
Bepaal het VERSCHIL van AAN en UIT. Stuur de elementen van AAN \ UIT over de hoogste lijnen van de data-bus om bijbehorende orgelkontakten aan te zetten: laagste bit op 1.
Bepaal het VERSCHIL van UIT en AAN. Stuur de elementen van UIT \ AAN over de hoogste lijnen van de data-bus om bijbehorende orgelkontakten uit te zetten: laagste bit op 0.
De termen in hoofdletters kan men wel ergens in de listing van de FORTH screens terugvinden.

Akkoordbouw

Wie met een computer homofone muziek wil bedrijven, zal blijkbaar op een adekwate manier aan verzamelingenleer moeten doen. Onmiddelijk wordt dan de vraag gesteld hoe men verzamelingen in de machine representeert. Volgens ons zijn er niet meer dan twee essentieel verschillende manieren om dat te doen.
1. Een verzameling is een groot computervoord, waarbij elk bit aanduidt of het ermee korresponderende element wel (1) of niet (0) in de kollektie aanwezig is.
2. Een verzameling is een geordende rij van nummers, waarbij elk nummer niet meer dan één keer in de rij kan voorkomen.
Toevallig hebben wij gekozen voor de laatste representatie: verzamelingen als gesorteerde integer arrays. Hetgeen allerminst betekent dat een weergave als bit-map ondoenlijk of ondoelmatig zou zijn.
In het homofone muzikale gebeuren kan men nu drie fasen onderscheiden.
1. Het, onhoorbaar, toevoegen van afzonderlijke tonen aan een akkoord.
2. Akkoordwisseling, waarbij de ene samenklank, hoorbaar, wordt vervangen door een andere.
3. TELLEN: de tijd waarover een bepaald akkoord blijft klinken.
De derde fase is simpel een lege loopstruktuur, enkel bedoeld om tijdsduur af te meten. De tweede fase noopte tot invoering van de verzamelingenleer.
Fase 1, de akkoordbouw, wordt in Musicode notatie gekarakteriseerd door opeenvolgingen van notennamen, bijvoorbeeld C E G of C F A . Elk van die notennamen wordt op zijn beurt uitgedrukt in een eigen stackparameter en een uniforme funktie voor akkoordbouw, die wij TOON zullen noemen.
Voorbeelden zijn : C 13 TOON ; : E 17 TOON ; : G 20 TOON ; .
De funktie van TOON is: het berekenen en opslaan van kontaktnumers.
Kontaktnummers worden berekend uit de stackparameter, in kombinatie met geldende waarden voor oktaafhoogte en registratie. De formule luidt:
kontaktnummers = 12 x oktaafhoogte + stackparameter + ( 84 , 42 , 0 )
De oktaafhoogte bevindt zich in de variabele OKT. Ze wordt geregeld door de applikatiefunkties LO en HI. Afhankelijk van de registratie-kodering in VOET worden de resulterende kontaktnummers, drie in getal, wel of niet aan de samenklank toegevoegd. Het akkoord draagt een bekende naam: AAN . Zoals gezegd is het een gesorteerd integer array. Nieuwe nummers worden ge"merged" met de reeds AANwezige kontakten. De daarbij gebruikte methode is "straight insertion". Wil men akkoordbouw in de praktijk soepel kunnen toepassen, dan is het niet altijd te voorkomen dat identieke kontakten in AAN komen. Deze moeten na afloop van het proces worden verwijderd, voordat akkoordwisseling plaatsvindt. Het VERSCHIL algorithme zal namelijk alleen werken op echte verzamelingen ("sets") AAN en UIT, en niet op "bags" (: Smalltalk terminologie).

FORTH compiler

Ook voor homofone muzicode hebben we FORTH uitgekozen als de voertaal. Geen enkele andere taal laat zich zo gemakkelijk optrekken naar het niveau dat we nodig hebben. Er is een verschil met de compiler voor eenstemmige muziek. In dat geval was de hogere taal voldoende snel, en hoefden we niet in 1802 machinekode te duiken. Wanneer het echter aankomt op homofone verzamelingenleer, dan blijkt standaard FORTH meer dan een faktor 100 te traag te wezen. Gelukkig is de slakkegang van een meegeleverde TIL nooit een probleem. Met CREATE en "DC" dalen we gemakkelijk af naar het hart van de machine. Momenteel zijn praktisch alle funkties van de Musicode compiler geschreven op de huid van de 1802.
In screen #1 (zie Listing) treft men enkele woorden aan die het compileer proces moeten vergemakkelijken. Dat zijn _REG, >REG, LABEL! en GOTO, . De eerste twee dienen om enkele veel voorkomende machinecode sekwenties in één keer vast te leggen. De laatste twee vereenvoudigen het omgaan met (absolute !) adressen bij sprong-instrukties. Waar mogelijk wordt gewerkt met korte sprongen (short branch); waar nodig wordt een lange sprong (long branch) gekompileerd.
Een precieze behandeling van de overige screens zou bij de lezer ruime kennis van de 1802 instruktieset veronderstellen. Dat is, zoals men zal begrijpen, niet de bedoeling. Voorzien worden een paar artikeltjes in COMX NEWS. In "Woordenlijst" worden alle funkties op een rijtje gezet.

Woordenlijst

AAN             -> addr      gesorteerde rij van aan te zetten kontakten
UIT             -> addr      gesorteerde rij van uit te zetten kontakten
De orgelkontakten zijn genummerd van 1 t/m 126; nullen betekent leeg.
_REG,       a n ->           kompileer-woord: breng adres a in register n
>REG,         n ->           kompileer-woord: breng TOS naar register n
LABEL           -> addr      array voor het opslaan van sprong-adressen
LABEL!      a n ->           label n toekennen aan sprong-adres a
LABEL@        n -> a         bepaal sprong-adres a behorende bij label n
GOTO,       m n ->           kompileren van short/long branch
                             met opcode m naar het adres met label n
VERSCHIL    a b ->           bepaal het verschil van de verzamelingen a en b
                             en stuur de elementen van a \ b naar het orgel:
                             kode = 2 x kontakt - byte met label 4
HUP             ->           kopieer AAN naar UIT en maak AAN leeg
EXTRA         n ->           zet tos (1 of 2) in het adres met label 4
                             en wijzig daarmee lokaal een byte in VERSCHIL
KNIJP           ->           verwijder de identieke elementen in AAN
LEEG            ->           en maak waar nodig schoon; AAN wordt een "set"
VOET            -> addr      variabele waarin een kodering van de registratie
OKT             -> addr      variabele die de oktaafhoogte bevat
TOON          n ->           berekent kontaktnummers uit tos, OKT en VOET
                             en slaat deze gesorteerd op in AAN
&               ->           funktie voor akkoordwisseling
Gevolgd door alle tonen van de chromatische toonladder, uitgebreid met een oktaaf hoger en een oktaaf lager: zie "Homofone Musicode".
Ook voor de overige funkties, op applikatie niveau, wordt verwezen naar desbetreffende paragraaf. Resteren een aantal woorden voor tijd en maat:
TEL            -> addr     variablele die de tel-eenheid bevat, voor:
DUUR           -> addr     bevat de momentane tijdsduur van een akkoord
TELLEN                     lege loopstruktuur om de tijd te doden
Zij nog vermeld dat "doorklinken" ^ wordt bewerkstelligd door UIT terug te kopiëren naar AAN.
Behalve de backslash \ (één parameter) hebben de applikatiefunkties geen effekt op de data stack.

Nawoord (1997/2021)

Ik heb er nog over gedacht om de FORTH screens van COMX hier neer te zetten in het originele (platte tekst) formaat. Maar dit heeft denkelijk geen enkele zin. Ik zou om te beginnen de OCR uitvoer handmatig moeten korrigeren, met alle mogelijke kans op fouten. Het is daarna niet (meer) mogelijk om het programma nog ooit daadwerkelijk te 'debuggen' of zelfs maar te draaien: de COMX is ergens veilig opgeborgen in een museum, het Eminent orgel is al geruime tijd het huis uit en de interface lag in gedeelten op zolder achter een schot. Maar de ideeën achter het bovenstaande zijn nog steeds levend. Men kan ze met name terugvinden in mijn (Delphi) Pascal programma's voor MIDI.