-- 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.