-- DCT.mesa
-- D0 printer port CAT test
-- Last Modified LStewart January 29, 1981 5:51 PM
DIRECTORY
BcplOps USING [CleanupReason],
ImageDefs USING [
AddCleanupProcedure, AllReasons, BcdTime, CleanupItem,
RemoveCleanupProcedure],
Inline USING [COPY],
IODefs USING [GetInputStream, ReadChar, ReadID, Rubout],
MiscDefs USING [Zero],
Mopcodes USING [zJRAM, zLIW],
ProcessDefs USING [MsecToTicks, Pause, Yield],
StreamDefs USING [
DiskHandle, FileNameError, NewWordStream, Read, ReadBlock, StreamHandle,
WriteAppend, WriteBlock],
StringDefs USING [InvalidNumber, LowerCase, StringToNumber],
SystemDefs USING [
AllocateHeapNode, AllocateResidentSegment, FreeHeapNode, FreeSegment, Quad],
TimeDefs USING [PackedTime],
WF USING [SWF1, WF0, WF1, WFC, WFCR];
DCT: PROGRAM
IMPORTS
ImageDefs, Inline, IODefs, MiscDefs, ProcessDefs, StreamDefs, StringDefs,
SystemDefs, WF =
BEGIN
-- Data buffer
BufSize: CARDINAL = 16384; -- for each input and output (samples)
BufWords: CARDINAL = BufSize/2;
Buffer: TYPE = RECORD [
outputStartOffset: CARDINAL,
outputEndOffset: CARDINAL,
outputCurrentOffset: CARDINAL,
inputDisplacement: CARDINAL,
buffers: ARRAY [0..0) OF UNSPECIFIED];
buffer: POINTER TO Buffer;
lbuffer: LONG POINTER TO Buffer;
currentSize: CARDINAL;
audioOn, echoOn, tickOn, quitFlag, tock: BOOLEAN ← FALSE;
memory: POINTER;
request: CARDINAL;
echoer, ticker: PROCESS;
fileName: STRING ← [40];
Main: PROC = {
c: CHARACTER;
bcdTime: TimeDefs.PackedTime ← ImageDefs.BcdTime[];
WF.WF1["Dolphin Printer Port Audio Terminal Test of %lt*n", @bcdTime];
DO
ENABLE { StringDefs.InvalidNumber, IODefs.Rubout => {WF.WFCR[]; LOOP; }; };
WF.WF0["%% "];
c ← StringDefs.LowerCase[IODefs.ReadChar[]];
SELECT c FROM
'a => {
arg: LONG UNSPECIFIED;
WF.WF0["Audio is now "];
audioOn ← NOT audioOn;
IF audioOn THEN buffer.outputCurrentOffset ← buffer.outputStartOffset;
arg ← IF audioOn THEN lbuffer ELSE 1;
AudioControl[arg];
WF.WF0[IF audioOn THEN "ON*n" ELSE "OFF*n"];
};
'b => {
s: STRING ← [40];
arg: CARDINAL;
AllOff[];
WF.WF1["Buffer length (max %u words): ", BufWords];
WF.SWF1[s, "%u", currentSize];
IODefs.ReadID[s];
arg ← StringDefs.StringToNumber[s, 10];
currentSize ← MIN[BufWords, arg];
buffer.outputEndOffset ← buffer.outputStartOffset + currentSize - 1;
buffer.outputCurrentOffset ← buffer.outputStartOffset;
WF.WF1[" [used %u]*n", currentSize];
};
'e => {
echoOn ← NOT echoOn;
tickOn ← echoOn;
IF NOT echoOn THEN ProcessDefs.Pause[ProcessDefs.MsecToTicks[500]];
IF tock THEN WF.WF0["*n%% "];
tock ← FALSE;
WF.WF0["Echo is now "];
WF.WF0[IF echoOn THEN "ON*n" ELSE "OFF*n"];
};
'o => {
s: STRING ← [40];
arg: CARDINAL;
AllOff[];
WF.WF1["Offset of input buffer (max %u): ", BufWords];
WF.SWF1[s, "%u", buffer.inputDisplacement];
IODefs.ReadID[s];
arg ← StringDefs.StringToNumber[s, 10];
arg ← MIN[BufWords, arg];
buffer.inputDisplacement ← arg;
WF.WF1[" [used %u]*n", arg];
};
'p => PlayFile[];
'q => {WF.WF0["Quit!*n"]; EXIT; };
'r => RecordFile[];
'z => {
WF.WF0["Zero output buffer*n"];
MiscDefs.Zero[@buffer.buffers[0], currentSize];
};
'? => {
WF.WF0["Audio on/off, "];
WF.WF0["Buffer length, "];
WF.WF0["Echo on/off, "];
WF.WF0["Offset (to input buffer), "];
WF.WF0["Play, "];
WF.WF0["Quit, "];
WF.WF0["Record, "];
WF.WF0["Zero output buffer, "];
WF.WF0["???, "];
WF.WFCR[];
};
ENDCASE => WF.WF0["???*n"];
ENDLOOP;
WF.WF0["Bye*n"];
};
AllOff: PROC = {
IF audioOn OR echoOn THEN {
audioOn ← echoOn ← tickOn ← FALSE;
AudioControl[1];
ProcessDefs.Pause[ProcessDefs.MsecToTicks[200]];
IF tock THEN WF.WF0["*n%% "];
WF.WF0["Audio and echoing are off*n"];
ProcessDefs.Pause[ProcessDefs.MsecToTicks[50]];
tock ← FALSE;
};
};
PlayFile: PROC = {
fh: StreamDefs.DiskHandle;
eof: BOOLEAN ← FALSE;
rLoc, wLoc, count, eofLoc: CARDINAL;
AllOff[];
WF.WF0["Play local file (name): "];
IODefs.ReadID[fileName];
WF.WFCR[];
fh ← StreamDefs.NewWordStream[
name: fileName, access: StreamDefs.Read !
StreamDefs.FileNameError => {WF.WF0[" no such file*n"]; GOTO Quit; }];
currentSize ← BufWords;
buffer.outputEndOffset ← buffer.outputStartOffset + currentSize - 1;
wLoc ← buffer.outputCurrentOffset ← buffer.outputStartOffset;
-- keep input out of the way
buffer.inputDisplacement ← BufWords;
-- prefill buffer
rLoc ← StreamDefs.ReadBlock[
stream: fh, address: @buffer.buffers[0], words: currentSize];
IF rLoc # currentSize THEN {eof ← TRUE; eofLoc ← rLoc; } ELSE rLoc ← 0;
-- turn on audio
audioOn ← TRUE;
AudioControl[lbuffer];
-- keep buffer filled
DO
IF eof THEN EXIT;
ProcessDefs.Yield[];
wLoc ← buffer.outputCurrentOffset - buffer.outputStartOffset;
IF (BufWords + wLoc - rLoc) MOD currentSize > 512 THEN {
IF wLoc < rLoc THEN {
--wraparound
count ← StreamDefs.ReadBlock[
stream: fh, address: @buffer.buffers[rLoc],
words: currentSize - rLoc];
IF count # (currentSize - rLoc) THEN {
eof ← TRUE; rLoc ← rLoc + count; eofLoc ← rLoc; EXIT; }
ELSE rLoc ← 0;
};
-- no wraparound
count ← StreamDefs.ReadBlock[
stream: fh, address: @buffer.buffers[rLoc], words: wLoc - rLoc];
IF count # (wLoc - rLoc) THEN {
eof ← TRUE; rLoc ← rLoc + count; eofLoc ← rLoc; EXIT; }
ELSE rLoc ← wLoc;
};
ENDLOOP;
-- supply a bunch of zeros
DO
ProcessDefs.Yield[];
wLoc ← buffer.outputCurrentOffset - buffer.outputStartOffset;
IF (BufWords + wLoc - eofLoc) MOD currentSize > 1024 THEN EXIT;
ENDLOOP;
IF wLoc < rLoc THEN {
MiscDefs.Zero[@buffer.buffers[rLoc], currentSize - rLoc]; rLoc ← 0; };
MiscDefs.Zero[@buffer.buffers[rLoc], wLoc - rLoc];
-- wait for device to finish file
DO
ProcessDefs.Yield[];
wLoc ← buffer.outputCurrentOffset - buffer.outputStartOffset;
IF (BufWords + wLoc - eofLoc) MOD currentSize < 1024 THEN EXIT;
ENDLOOP;
-- turn off audio and close file
AudioControl[1];
audioOn ← FALSE;
fh.destroy[fh];
EXITS Quit => NULL;
};
RecordFile: PROC = {
fh: StreamDefs.DiskHandle;
keys: StreamDefs.StreamHandle ← IODefs.GetInputStream[];
eof: BOOLEAN ← FALSE;
rLoc, wLoc, count: CARDINAL;
{
-- extra for Quit below
AllOff[];
WF.WF0["Record local file (name): "];
IODefs.ReadID[fileName];
WF.WFCR[];
fh ← StreamDefs.NewWordStream[name: fileName, access: StreamDefs.WriteAppend];
currentSize ← BufWords;
buffer.outputEndOffset ← buffer.outputStartOffset + currentSize - 1;
wLoc ← buffer.outputCurrentOffset ← buffer.outputStartOffset;
-- sidetone
buffer.inputDisplacement ← 1;
WF.WF0["Type any key to start, then another key to stop.*n"];
[] ← IODefs.ReadChar[];
WF.WF0["Start."];
-- turn on audio
audioOn ← TRUE;
AudioControl[lbuffer];
tickOn ← TRUE;
rLoc ← 0;
-- keep buffer emptied
DO
IF NOT keys.endof[keys] THEN EXIT;
ProcessDefs.Yield[];
wLoc ← buffer.outputCurrentOffset - buffer.outputStartOffset;
IF (BufWords + wLoc - rLoc) MOD currentSize > 512 THEN {
IF wLoc < rLoc THEN {
--wraparound
count ← StreamDefs.WriteBlock[
stream: fh, address: @buffer.buffers[rLoc],
words: currentSize - rLoc];
IF count # (currentSize - rLoc) THEN {eof ← TRUE; EXIT; } ELSE rLoc ← 0;
};
-- no wraparound
count ← StreamDefs.WriteBlock[
stream: fh, address: @buffer.buffers[rLoc], words: wLoc - rLoc];
IF count # (wLoc - rLoc) THEN {eof ← TRUE; EXIT; } ELSE rLoc ← wLoc;
};
ENDLOOP;
-- turn off audio
AudioControl[1];
audioOn ← tickOn ← FALSE;
ProcessDefs.Yield[];
IF eof THEN GOTO Quit;
-- finish writing file
wLoc ← buffer.outputCurrentOffset - buffer.outputStartOffset;
IF wLoc < rLoc THEN {
--wraparound
count ← StreamDefs.WriteBlock[
stream: fh, address: @buffer.buffers[rLoc], words: currentSize - rLoc];
IF count # (currentSize - rLoc) THEN GOTO Quit ELSE rLoc ← 0;
};
-- no wraparound
count ← StreamDefs.WriteBlock[
stream: fh, address: @buffer.buffers[rLoc], words: wLoc - rLoc];
IF count # (wLoc - rLoc) THEN GOTO Quit;
-- clean up
ProcessDefs.Yield[];
[] ← IODefs.ReadChar[];
WF.WF0["Done*n"];
fh.destroy[fh];
buffer.inputDisplacement ← currentSize;
EXITS Quit => {WF.WF0["file error!*n"]; fh.destroy[fh]; };
};
};
TickProcess: PROC = {
wLoc, rLoc: CARDINAL ← 0;
DO
IF quitFlag THEN EXIT;
ProcessDefs.Pause[ProcessDefs.MsecToTicks[1000]];
IF NOT tickOn THEN LOOP;
WF.WFC['.];
tock ← TRUE;
ENDLOOP;
};
EchoProcess: PROC = {
wLoc, rLoc: CARDINAL ← 0;
DO
IF quitFlag THEN EXIT;
ProcessDefs.Yield[];
wLoc ← buffer.outputCurrentOffset - buffer.outputStartOffset;
IF NOT echoOn THEN {rLoc ← wLoc; LOOP; };
IF (BufWords + wLoc - rLoc) MOD currentSize > 256 THEN {
IF wLoc < rLoc THEN {
--wraparound
Inline.COPY[
from: @buffer.buffers[rLoc + buffer.inputDisplacement],
nwords: currentSize - rLoc, to: @buffer.buffers[rLoc]];
rLoc ← 0;
};
IF wLoc > rLoc THEN {
-- no wraparound
Inline.COPY[
from: @buffer.buffers[rLoc + buffer.inputDisplacement],
nwords: wLoc - rLoc, to: @buffer.buffers[rLoc]];
rLoc ← wLoc;
};
};
ENDLOOP;
};
-- Calling AudioControl with a LONG POINTER to the (quad-aligned) Buffer will turn on the audio microcode. Calling AudioControl with an odd value will turn it off again.
AudioGo: CARDINAL = 166600B;
oAudioControl: PROC [bp: LONG UNSPECIFIED] = MACHINE CODE
BEGIN Mopcodes.zLIW, AudioGo/256, AudioGo MOD 256; Mopcodes.zJRAM; END;
AudioControl: PROC [bp: LONG UNSPECIFIED, xxx: CARDINAL ← AudioGo] = MACHINE
CODE BEGIN Mopcodes.zJRAM; END;
AudioCleanup: PROC [why: BcplOps.CleanupReason] = {
SELECT why FROM
InLd => {
-- if returning from the debugger
IF wasEnabled THEN {AudioControl[lbuffer]; wasEnabled ← FALSE; };
};
Finish, Abort => {
-- leaving Mesa
AudioControl[1];
};
ENDCASE => {
-- everything else
IF audioOn THEN {AudioControl[1]; wasEnabled ← TRUE; };
};
};
-- Mainline code
wasEnabled: BOOLEAN ← FALSE;
cleanUpItemP: POINTER TO ImageDefs.CleanupItem;
cleanUpItemP ← SystemDefs.AllocateHeapNode[SIZE[ImageDefs.CleanupItem]];
cleanUpItemP↑ ← [NIL, ImageDefs.AllReasons, AudioCleanup];
ImageDefs.AddCleanupProcedure[cleanUpItemP];
request ← SIZE[Buffer] + (BufWords*2) + 4; -- +4 for Quad alignment
memory ← SystemDefs.AllocateResidentSegment[request];
buffer ← LOOPHOLE[SystemDefs.Quad[memory], POINTER];
lbuffer ← buffer; -- extend to LONG POINTER
currentSize ← BufWords;
MiscDefs.Zero[@buffer.buffers[0], currentSize];
buffer.outputStartOffset ← SIZE[Buffer];
buffer.outputEndOffset ← buffer.outputStartOffset + BufWords - 1;
buffer.outputCurrentOffset ← buffer.outputStartOffset + 0;
buffer.inputDisplacement ← BufWords;
echoer ← FORK EchoProcess[];
ticker ← FORK TickProcess[];
Main[];
tickOn ← echoOn ← FALSE;
quitFlag ← TRUE;
JOIN echoer;
JOIN ticker;
AudioControl[1]; -- Definitely turn off the audio stuff.
ImageDefs.RemoveCleanupProcedure[cleanUpItemP];
SystemDefs.FreeHeapNode[cleanUpItemP];
SystemDefs.FreeSegment[memory];
END.