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.