; Microcode for running Alto II CAT
; ~~ extra rule: only one list at a time may be issuing Tones!
; Interface to Audio Task
; Client Specifies an acb list using AudioList or MAudioList (see below), supplying a list
; of acbs and an index.
;index is 0 or 1, allowing two parallel activities
; Client can augment a list using AudioLink or MAudioLink (see below);
;If oldACB has completed (# samples word is 0), this returns false
; ACB format:
;0idxLink-> next ACB (MUST BE ON EVEN WORD BOUNDARY)
;1idxLength# of samples in this block (client initializes)
;2idxCmdCommand
;3idxDataAddress of last word of data block (1st word of waveform block for Tone)
;(4idxF1Frequency 1 for tone)
;(5idxFAcc1Accumulator for same -- used by task only)
;(6idxF2Frequency 2)
;(7idxFAcc2Accumulator for same -- used by task only)
;10idxSamples# samples left in this block (client inits if desired, task reinits and decrements)
; Commands:
;1AdcInput samples to data block, one 16-bit (12 significant) sample per word
;2DacOutput samples from data block, one 16-bit sample per word
;3Tone Output @idxLength samples of the sum of two tones, whose frequencies and
;sample waveforms are given (more below)
;4AdcPackedInput data block, two 8-bit samples per word -- @idxLength must be even
;5DacPackedOutput data block, two 8-bit samples per word -- @idxLength even
;6AdcU255Input data block, two 8-bit samples per word
;in u-255 law coding -- @idxLength must be even
;7DacU255Output data block, two 8-bit samples per word
;in u-255 law coding -- @idxLength even
;
!1,2, DoAudio1, NoActivity1;
!1,2, DoAudio2, AudioIdle2;
!1,2, Dispatch, Advance;
!1,2, AudioIdle, ReturnFrom1;
!1,2, SaveSil2,SaveSil1;
!1,2, SetSpecific2, SetSpecific1;
!1,2, SetDispatch, Empty;
!7,10, IglCmd, Adc, Dac, Tone, AdcPacked, DacPacked, AdcU255, DacU255;
AudioStart:L←0;No activity
ACB1←L;
ACB2←L,TASK;
WData ← L;
T ← 177000;Clear ready bit in hardware
MAR ← 16 + T;
L ← Strobe;
MD ← LastL;
MAR ← 16 + T;
L ← 0, TASK;
MD ← LastL,:MRT;
AudioIdle:TASK, :Idle;
AudioIdle2:TASK, :Idle;
Idle:NOP, :ATDone;
AudioReal:T←ACB1, BUS=0;
NOP,:DoAudio1;!1,2,DoAudio1, NoActivity1;
ReturnFrom1:
T←ACB2, BUS=0, :NoActivity;
NoActivity1:
T←ACB2, BUS=0;
NoActivity:NOP,:DoAudio2;!1,2,DoAudio2, AudioIdle2;
; T = ACB, L=index
DoAudio1:MAR←idxSamples+T;
L←0, :DoAudio;
DoAudio2:MAR←idxSamples+T;
L←0+1;
DoAudio:Index←L, L←T;
ACB←L;
L←MD-1;
Redispatch:MAR←idxCmd+T;
Samples←L, SH<0;
L←MD, :Dispatch;!1,2,Dispatch, Advance; Read Cmd
Dispatch:T←MD; Read Data
Temp←L,L←T, TASK;
Data←L;** (**)
;Seemed to be no reason not to do this right away.
T←ACB;
MAR←idxSamples+T;
NOP, TASK;
MD←Samples;
SINK←Temp, BUS;
L←T←Samples, :IglCmd; [IglCmd, Adc, Dac, Tone, ... ] ; Samples usually needed
; This routine simplified by moving Samples update into Dispatch above.
RoutDone:SINK←Index, BUS=0, TASK;
NOP, :AudioIdle;!1,2,AudioIdle, ReturnFrom1; **
Advance:
; Code to write to cursor for testing
;T←2;
;MAR ← CLOCKLOC+T;
;NOP;
;MD←Sil1,TASK;
;MD←Sil2;
T←idxF1;storage for silence detection result
MAR←ACB+T;Save old silence register and clear it
SINK←Index,BUS=0;
L←0,:SaveSil2;!1,2,SaveSil2,SaveSil1;
SaveSil2:MD←Sil2,TASK;
Sil2←L,:AdvCom;
SaveSil1:MD←Sil1,TASK;
Sil1←L,:AdvCom;
AdvCom:MAR←ACB;
NOP;
L←MD, TASK;
ACB←L; **
SetData:L←T←ACB;
MAR←idxLength+T;
SINK←Index, BUS=0;
SH=0, :SetSpecific2;!1,2,SetSpecific2, SetSpecific1;
SetSpecific1:ACB1←L, :SetDispatch;!1,2,SetDispatch, Empty;
SetSpecific2:ACB2←L, :SetDispatch;!1,2,SetDispatch, Empty;
SetDispatch:L←MD-1, :Redispatch;
Empty:SINK←Index, BUS=0, TASK;
NOP, :AudioIdle;!1,2,AudioIdle, ReturnFrom1; **
!1,2,DacPlus,DacMinus;
!1,2,DacCom2,DacCom1;
!1,2, WriteNow, WriteCycled;
!1,2, JustWrite, ReadCycleWrite;
!1,2,eWriteNow,eWriteCycled;
; Dac must convert the word from memory to u-255 and leave it in WData
Dac:MAR←Data-T;
NOP;
L←T←MD, :utEncode;
; Call utEncode to convert the 12 bit value left justified in L and T
; into a u255 byte left justified in Temp=utVal. return to utStore.
utStore:L←Temp,TASK;
WData←L,:RoutDone;
; Adc must accept convert the word in RData to linear and store it in memory
Adc:L ← RData, TASK;
Temp ← L LCY 8;
L ← Temp,:utDecode;
; Routine utDecode logically goes here. It is called with the u255 byte
; right justified in L. Available registers are
; Temp (R), MTEMP (R), NCValue (S), ACB (S)
; Temp, ACB, and NCValue are used.
; utDecode should return to utShipit with the 12 bit value left justified
; in utVal=Temp.
utShipit:T←Samples;
MAR←Data-T;
NOP;
TASK;
MD←Temp;
L←T←Temp;
; Expects data in both L and T
SDProc:L←177400 AND T,SH<0;
DacCom:Temp ← L LCY 8,:DacPlus;!1,2,DacPlus,DacMinus;
DacMinus:L←0-T;
T←LASTL;
L←177400 AND T,TASK,:DacCom;
DacPlus:SINK←Index,BUS=0;
T←Temp,:DacCom2;!1,2,DacCom2,DacCom1;
DacCom1:L←Sil1+T,TASK;
Sil1←L,:RoutDone;
; Test code, writes to cursor
;T←4;
;MAR←CLOCKLOC+T;
;NOP;
;NOP,TASK;
;MD←Temp,:RoutDone;
DacCom2:L←Sil2+T,TASK;
Sil2←L,:RoutDone;
; Test code, writes to cursor
;T←5;
;MAR←CLOCKLOC+T;
;NOP;
;NOP,TASK;
;MD←Temp,:RoutDone;
DacU255:Temp←L RSH 1;Word index for next sample
T←Temp;Start Store/Fetch cycle
MAR←Data-T;
T←ONE;Odd or Even to L
L←LASTL AND T;
T←LeftMask, SH=0;
L←MD, :WriteNow;!1,2,WriteNow, WriteCycled;
; Write Odd sample
WriteCycled:
Temp←L LCY 8;
L←Temp AND T,TASK, :ShipIt;
WriteNow:L←LASTL AND T,TASK;
ShipIt:WData←L, :RoutDone;
AdcU255:Temp←L RSH 1;Word index for next sample
T←ONE;Odd or Even to L
L←LASTL AND T;
T←Temp;Start Store/Fetch cycle
MAR←L←Data-T, SH=0;
NOP, :JustWrite; !1,2,JustWrite, ReadCycleWrite;
JustWrite:MD←L←T←RData;MD = S1,,0
NOP,TASK;
Temp←L, :AdcPackedCom;
ReadCycleWrite:
T←RData;S2,,0
Temp←L;Save data pointer
L←T, T←MD;L = S2,,0; T=S1,,0
MTEMP←L LCY 8;MTEMP = 0,,S2
MAR←Temp;
Temp←L;Save S2,,0
L←MTEMP+T, TASK;L = S1,,S2
MD←LASTL;**
; The branch to SDProc must load L and T, for SH<0 next cycle
AdcPackedCom:
L←T←Temp,:SDProc;
; DacPacked can get the byte left justified in L and
; call utEncode
DacPacked:Temp←L RSH 1;Word index for next sample
T←Temp;Start Store/Fetch cycle
MAR←Data-T;
T←ONE;Odd or Even to L
L←LASTL AND T;
T←LeftMask, SH=0;
L←MD, :eWriteNow;!1,2,eWriteNow,eWriteCycled;
; Write Odd sample
eWriteCycled:
Temp←L LCY 8;
L←T←Temp.T, :utEncode;
eWriteNow:L←T←LASTL.T, :utEncode;
; AdcPacked will need another copy of utDecode, and then call AdcU255
; The additional copy of utDecode is called uttDecode and is in utt255.mu
AdcPacked:L←RData,TASK;
Temp←L LCY 8;
L←Temp,:uttDecode;
uttShipit:T←LeftMask;
L←LASTL AND T,TASK;
RData←L;
L←T←Samples,:AdcU255;
IglCmd:NOP, :RoutDone;
; There can be but one of these at a time. F1, F2, FAcc1-2 are Frequencies;
; Data is address of 32 word waveform at correct amplitude, filled last to first
; F1, F2, FAcc1-2 are expressed as fixed-point fractions with the point between bits 2 and 3.
; F1-2 are expressed as fractions of the sample rate. The assignment
;FAccx = FAccx + Fx yields the phase of the next sample in FAccx.
; The high-order 5 bits of this fraction (bits 3-7) are used to index a waveform sample table.
; Open code both fetches
Tone:T←ACB;
MAR←idxF1+T;
NOP;
T←MD;
L←MD+T;
T←ACB;
MAR←idxFAcc1+T;
Temp←L LCY 8;
TASK;
MD←LASTL;**
T←Temp;
T←37.T;
MAR←Data+T;
T←ACB;
L←MD;
NCValue←L;
MAR←idxF2+T;
NOP;
T←MD;
L←MD+T;
T←ACB;
MAR←idxFAcc2+T;
Temp←L LCY 8;
TASK;
MD←LASTL;**
T←Temp;
T←37.T;
MAR←Data+T;
T←NCValue;
L←MD+T,:utEncode;
; This code runs as part of MRT
!1,2,Ready,NotYet;
AudioTest:T ← 177000;
MAR ← 30 + T;
T ← OREMask;two cycles here! (compute portout?)
L ← T ← MD AND T;
L ← DataMask AND T, SH=0;
RData ← L,:Ready;!1,2,Ready,NotYet;
; At this point, the first incoming nibble is left justified in RData
NotYet:TASK;
NOP,:ATDone;back to MRT
; On entry at Ready, the outgoing byte is expected left justified in WData
;(with possible garbage in right half)
; On exit, the incoming byte is returned left justified in RData
Ready:; write data
T ← 177000;
MAR ← 16 + T;
T ← 177400;
L ← WData AND T;
MD ← LastL, TASK;
WData ← L;
; Strobe it in
T ← 177000;
MAR ← 16 + T;
L ← WData + 1;we know 1 is Strobe
MD ← LastL;
; Now read next nibble
MAR ← 30 + T;#177030
T ← DataMask;
L ← MD AND T,TASK;
Temp ← L;
; The second incoming nibble is left justified in Temp.
; Turn off strobe
T ← 177000;
MAR ← 16 + T;
L ← Temp;Start shifting Temp
Temp ← L RSH 1;
MD ← WData;
L ← Temp, TASK;
Temp ← L RSH 1;
L ← Temp, TASK;
Temp ← L RSH 1;
L ← Temp, TASK;
Temp ← L RSH 1;
T ← Temp;
L ← RData OR T, TASK;
RData ← L, :AudioReal;
;; March 17, 1980 12:22 PM, L. Stewart, Modified from AuburnMc-u5-r4.mu for CAT.