-- September 8, 1980 5:47 PM
DIRECTORY
FSPDefs: FROM "FSPDefs" USING[ Deallocator, FreeNode, MakeNewZone, MakeNode, NodeSize, ZonePointer],
Mopcodes: FROM "mopcodes" USING[zMISC],
ImageDefs: FROM "imagedefs" USING[StopMesa],
IODefs: FROM "IODefs"
USING[GetInputStream, ReadChar, SP, WriteChar, WriteLine, WriteString],
StreamDefs: FROM "StreamDefs"
USING[NewByteStream, Read, StreamHandle],
StringDefs: FROM "StringDefs" USING[AppendLongNumber],
SystemDefs: FROM "SystemDefs" USING[AllocateHeapNode];
--
InlineDefs: FROM "InlineDefs"
--
USING[BITAND, BITOR, BITSHIFT, HighHalf, LowHalf],
--
TimeDefs: FROM "TimeDefs"
--
USING[AppendDayTime, CurrentDayTime, UnpackedTime, UnpackDT],

music
: PROGRAM IMPORTS FSPDefs, ImageDefs, IODefs, StreamDefs, StringDefs, SystemDefs =
BEGIN OPEN Mopcodes;

WriteVersion: PROCEDURE=BEGIN IODefs.WriteLine["
Music of June 22, 1980 1:12 PM "]; END;

myZone: FSPDefs.ZonePointer;
myP: POINTER;
allocs: ARRAY[0..100] OF POINTER;

StartMusic: PROCEDURE=
MACHINE CODE BEGIN Mopcodes.zMISC, 244B END;
StopMusic: PROCEDURE=
MACHINE CODE BEGIN Mopcodes.zMISC, 245B END;

tMusicControl: TYPE= MACHINE DEPENDENT RECORD[
pInput: plMusicBlock,
pOutput: plMusicBlock,
status: tMusicStatus,
intsIn: WORD,
intsOut: WORD,
pRegs: LONG POINTER TO tMicroRegs,
seal: WORD,
time: LONG CARDINAL,
pInputHead: plMusicBlock,
pOutputHead: plMusicBlock
];
tChannelX: TYPE= [0..12];
tMicroRegs: TYPE=MACHINE DEPENDENT RECORD[
in: ARRAY tChannelX OF tMusicVal,
out: ARRAY tChannelX OF tMusicVal
];
tMusicVal: TYPE=MACHINE DEPENDENT RECORD[
ch:[0..17B],
events:CARDINAL[0..7777B]
];
sealVal: CARDINAL= 75145B;
plMusicBlock: TYPE = LONG POINTER TO tMusicBlock;
tMusicBlock: TYPE= MACHINE DEPENDENT RECORD[
pNext: plMusicBlock,
maxEntries: tMaxMusicX,
nEntries: tMaxMusicX,
status: tMusicStatus,
data: ARRAY[0..0) OF tMusicEvent
];
tMaxMusicX: TYPE= CARDINAL;
tMusicStatus: TYPE= MACHINE DEPENDENT RECORD[
f1:[0..37B],
iDn: [0..1],
iDry: [0..1],
iOff: [0..1],
f2:[0..37B],
oDn: [0..1],
oDry: [0..1],
oOff: [0..1]
];
nullStatus: tMusicStatus= tMusicStatus[0,0,0,0,0,0,0,0];
tMusicEvent: TYPE= MACHINE DEPENDENT RECORD[
ch: [0..17B],
events: CARDINAL[0..7777B],
time: CARDINAL
];
ppMusicControl: POINTER TO POINTER TO tMusicControl=LOOPHOLE[415B];
mc: tMusicControl← tMusicControl[
pInput: NIL,
pOutput: NIL,
status: nullStatus,
intsIn: 0,
intsOut: 0,
pRegs: NIL,
seal: 0,
time: 0,
pInputHead: NIL,
pOutputHead: NIL
];


-- THESE ARE ROUTINES THAT PARSE COM.CM

readComCM: PROCEDURE[str: STRING]=
BEGIN OPEN StreamDefs;
comcm: StreamHandle ← NewByteStream["com.cm", Read];
i: CARDINAL←0;

UNTIL comcm.endof[comcm] OR (i>=256) DO
str[i] ← comcm.get[comcm];
i←i+1;
ENDLOOP;
str.length ← IF i=0 THEN 0 ELSE i-1;
IODefs.WriteLine["All event counts are divided by 256"];
END;

skipToken: PROCEDURE[str: STRING, x: CARDINAL] RETURNS[CARDINAL]=
BEGIN
IF (str[x]=IODefs.SP) OR (str[x]=’/) THEN
-- skip to next token
BEGIN
FOR x← 0, x+1 UNTIL ((str[x] # IODefs.SP) AND (str[x]#’/)) OR (x>=str.length) DO ENDLOOP;
RETURN[x]
END;

UNTIL (x>=str.length) OR (str[x]=IODefs.SP) OR (str[x]=’/) DO
x←x+1; ENDLOOP;
-- skip current token
UNTIL (x>=str.length) OR (str[x]#IODefs.SP AND (str[x]#’/)) DO
x←x+1; ENDLOOP;
-- move to next token
RETURN[x];
END;

printToken: PROCEDURE[str: STRING, i: CARDINAL] =
BEGIN
j: CARDINAL;
IODefs.WriteString[" we see this token:"];
FOR j←i, j+1 UNTIL (j>=str.length OR str[j]=IODefs.SP OR (str[j]=’/)) DO
IODefs.WriteChar[str[j]];
ENDLOOP;
IODefs.WriteChar[15C];
END;

startingSwitch: PROCEDURE[ch: CHARACTER] RETURNS[BOOLEAN]=
BEGIN
IF ch=’I OR ch=’i THEN RETURN[TRUE];
RETURN[FALSE];
END;
DebugPause: SIGNAL = CODE;

-- run is the controlling procedure for this program. It reads com.cm to determine what action to take, whether to init the event counters and return to the executive or to read them and print their value. Very early in the execution run we read the event counters to minimize the effect of measure’s exectuion upon event counters results.

run: PROCEDURE=
BEGIN
str: STRING←[256];
i,j: CARDINAL;

WriteVersion[];
i←0;
readComCM[str];
i←skipToken[str,i];
-- skip mesa.run junk. i point to "measure.bcd..."

i←skipToken[str,i];
-- skip measure.bcd. i point to our parameter
IF str[i-1]=’/ THEN
BEGIN i←skipToken[str,i]; END;
j←skipToken[str,i];
-- j point to the modifier /"I"

SELECT str[i] FROM
’e,’E=> musicEchoOn[];
’s,’S=> musicStartLoop[];
’p,’P=> musicPlayback[];
’t, ’T=> musicTest[];
’q,’Q=> musicQuiet[];
ENDCASE => IODefs.WriteLine[ " not yet implemented"];

UNTIL str[i] = IODefs.SP OR (i>=str.length) DO i←i+1;ENDLOOP;
IODefs.WriteLine[" "];
ImageDefs.StopMesa[];
END;

musicEchoOn: PROCEDURE=
BEGIN
IODefs.WriteString["Music Echo ON"];
createMusicControl[];
resetMC[];
mc.pInput← NIL;
mc.pOutput← NIL;
doStartMusic[@mc];
IODefs.WriteLine["Type any character to exit"];
[]← IODefs.ReadChar[];
doStopMusic[];
END;
musicQuiet: PROCEDURE=
BEGIN
keys: StreamDefs.StreamHandle← IODefs.GetInputStream[];
p: plMusicBlock;
i: CARDINAL;

IODefs.WriteLine["Music Quiet ON"];
createMusicControl[];
resetMC[];
mc.pInput← NIL;
FOR p← mc.pOutputHead, p.pNext UNTIL p=NIL DO
p.nEntries← 15;
FOR i IN [0..p.nEntries] DO p.data[i]← tMusicEvent[17B,0,0]; ENDLOOP;
ENDLOOP;
mc.pOutput← mc.pOutputHead;
doStartMusic[@mc];
IODefs.WriteLine["Type any character to exit"];
WHILE TRUE DO
IF mc.pOutput=NIL THEN EXIT;
IF ~keys.endof[keys] THEN EXIT;
ENDLOOP;
doStopMusic[];
IODefs.WriteLine["Silence Test done. Type any char to exit to exec."];
[]← IODefs.ReadChar[];
END;
musicStartLoop: PROCEDURE=
BEGIN
i: CARDINAL;
limit: CARDINAL← 5;
createMusicControl[];
resetMC[];
mc.pInput← NIL;
mc.pOutput← NIL;
WHILE TRUE DO
doStartMusic[@mc];
FOR i IN [0..limit] DO i←i; ENDLOOP;
ENDLOOP;
END;
touchHwre: BOOLEAN← TRUE;
doStartMusic: PROCEDURE[p: POINTER TO tMusicControl]=
BEGIN
IF ~ touchHwre THEN RETURN;
p.seal← sealVal;
ppMusicControl↑← p;
StartMusic[];
END;
doStopMusic: PROCEDURE=
BEGIN
IF ~ touchHwre THEN RETURN;
StopMusic[];
ppMusicControl.seal← 0;
ppMusicControl↑← NIL;
END;
musicPlayback: PROCEDURE=
BEGIN
char: CHARACTER;
lastnEntries: CARDINAL← 0;
createMusicControl[];
IODefs.WriteLine["WARNING: YOU MUST BOOT To end this test"];
IODefs.WriteLine["Begin PLAYBACK test"];
IODefs.WriteLine["Type any character, then play a few keys!"];
char← IODefs.ReadChar[];
resetMC[];
mc.pInput← mc.pInputHead;
mc.pOutput← mc.pOutputHead;

doStartMusic[@mc];

WHILE (lastnEntries← mc.pInput.nEntries) <900 DO NULL; ENDLOOP;
doStopMusic[];

WHILE TRUE DO

mc.pOutput← mc.pInputHead;
mc.pInput← NIL;

mc.pOutput.status← nullStatus;
doStartMusic[@mc];
IODefs.WriteLine["PLAYING BACK"];
WHILE TRUE DO
SELECT mc.pOutput FROM
NIL=> EXIT;
ENDCASE=>NULL;
ENDLOOP;

doStopMusic[];
ENDLOOP;
END;

musicTest:
PROCEDURE=
BEGIN
char: CHARACTER;
lastnEntries: CARDINAL← 0;
createMusicControl[];
IODefs.WriteLine["Begin music test"];
WHILE TRUE DO
resetMC[];
mc.pInput← mc.pInputHead;
mc.pOutput← NIL;

IODefs.WriteLine["Type any character, then play a few keys!"];
char← IODefs.ReadChar[];

doStartMusic[@mc];

DO
SELECT mc.pInput FROM
NIL=> EXIT;
ENDCASE=> NULL;
ENDLOOP;
doStopMusic[];

mc.pOutput← mc.pInputHead;
mc.pInput← NIL;

mc.pOutput.status← nullStatus;
doStartMusic[@mc];
DO
SELECT mc.pOutput FROM
NIL=> EXIT;
ENDCASE=> NULL;
ENDLOOP;
doStopMusic[];
IODefs.WriteLine["2Type any char to begin again"];
char←000C;
char←IODefs.ReadChar[];

ENDLOOP;
END;
cannedOut: BOOLEAN← FALSE;
resetMC: PROCEDURE=
BEGIN
i: CARDINAL;
zeroChain: PROCEDURE[ptr: plMusicBlock]=
BEGIN
p2: plMusicBlock;
FOR p2← ptr, p2.pNext UNTIL p2=NIL DO
p2.nEntries←0;
p2.status← nullStatus;
FOR i IN [0..p2.maxEntries) DO p2.data[i]←tMusicEvent[0,0,0]; ENDLOOP;
ENDLOOP;
END;

zeroChain[mc.pInputHead];
zeroChain[mc.pOutputHead];

FOR i IN tChannelX DO mc.pRegs.in[i]← tMusicVal[0,0]; ENDLOOP;
FOR i IN tChannelX DO mc.pRegs.out[i]← tMusicVal[0,0]; ENDLOOP;
mc.pRegs.out[LAST[tChannelX]]← tMusicVal[10B, 0];
END;
nEntries: CARDINAL← 100;
createMusicControl: PROCEDURE=
BEGIN
paranoidOffset: CARDINAL← 3;
n,allocSize: CARDINAL;
p: plMusicBlock;

n← paranoidOffset+nEntries;
allocSize← paranoidOffset+SIZE[tMusicBlock]+(n*SIZE[tMusicEvent]);

THROUGH [1..10] DO
p← myAlloc[allocSize];
p.pNext← mc.pInputHead;
p.maxEntries← nEntries;
mc.pInputHead← p;
p← myAlloc[allocSize];
p.pNext← mc.pOutputHead;
p.maxEntries← nEntries;
mc.pOutputHead← p;
ENDLOOP;
mc.pRegs← myAlloc[SIZE[tMicroRegs]+paranoidOffset];

mc.pOutputHead.maxEntries← mc.pInputHead.maxEntries←nEntries;
END;

WriteLNumber: PROCEDURE[l: LONG CARDINAL] =
BEGIN
ctr, x, digits: CARDINAL;
str: STRING←[20];
str2: STRING←[20];
str.length←0;
StringDefs.AppendLongNumber[str, l, 10];
digits←ctr←0;
FOR x DECREASING IN [0..str.length) DO
str2[ctr]←str[x];
ctr←ctr+1;
digits←digits+1;
IF (digits MOD 3)=0 THEN BEGIN str2[ctr]←’,; ctr←ctr+1; END;
ENDLOOP;
str2.length←ctr;
ctr←0;
IF str2[str2.length-1] = ’, THEN str2.length←str2.length-1;
FOR x DECREASING IN [0..str2.length) DO
str[ctr]←str2[x];
ctr←ctr+1;
ENDLOOP;
str.length←str2.length;
IODefs.WriteString[str];

END;

myFree: PROCEDURE[p: POINTER]=
BEGIN
found: BOOLEAN← FALSE;
i, size: CARDINAL;

FOR i IN [0..100] DO
IF allocs[i]=p THEN
BEGIN
found← TRUE;
allocs[i]← NIL;
EXIT;
END;
ENDLOOP;
IF ~found THEN ERROR;
size← FSPDefs.NodeSize[p];
FSPDefs.FreeNode[myZone, p];
p← p-1;
FOR i IN [0..size] DO (p+i)↑← p-myP+i; ENDLOOP;
END;

myAlloc: PROCEDURE[n: CARDINAL] RETURNS[p: POINTER]=
BEGIN
found: BOOLEAN← FALSE;
i: CARDINAL;

p← FSPDefs.MakeNode[myP, n];
FOR i IN [0..100] DO
IF allocs[i]=NIL THEN BEGIN
allocs[i]← p;
found← TRUE;
EXIT;
END;
ENDLOOP;
IF ~ found THEN ERROR;
END;

BEGIN OPEN SystemDefs, FSPDefs;
i: CARDINAL;
p: POINTER;

FOR i IN [0..100] DO allocs[i]← NIL; ENDLOOP;

myP← AllocateHeapNode[7000];
p← myP;
FOR i IN [0..7000) DO
(p+i)↑←i;
ENDLOOP;

myZone← MakeNewZone[myP, 7000, myFree];
[]← myAlloc[100];
END;
run[];
END.