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