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