;-----------------------------------------------------------------
; Adpcm.mu -- Microcode source for Alto running
;16 Kilobit Adaptive Predictive PCM.
; Last modified by L. Stewart August 28, 1979 9:22 PM
;-----------------------------------------------------------------
; Microcode subroutines are defined and called from Mesa programs
; as shown in the following example:
; routineAddr: CARDINAL = 400B; -- Ram address of microcode --
; CalluRoutine: PROCEDURE[x, y: REAL] RETURNS [z: REAL] =
; MACHINE CODE BEGIN
; Mopcodes.zLIW, routineAddr/256, routineAddr MOD 256;
; Mopcodes.zJRAM;
; END;
; [] ← CalluRoutine[a, b]; -- the call --
; All these routines assume they are called with a clean stack.
; Hence, an invocation such as "[] ← CalluRoutine[a, b]" must be
; written as a complete statement, not as an embedded expression.
; If the routine returns a value, it must be called in a statement
; of the form "simpleVariable ← Routine[args]".
; This permits the Ram subroutine to access fixed S-registers for
; arguments and return values. It must still adjust the stack
; pointer appropriately, however.
; Ram entry vector, for access via Mesa JRAM instruction.
; Note that only Ram locations 400-777 and 1400-1777 are reachable from Rom1.
%1,1777,560,EnCode;8 bit to 2 bit
%1,1777,561,DeCode;2 bit to 8 bit
$LastL$R40;M register
$SubRet$R41;mask
$APTemp$R5;count
$QLLev$R7;taskhole
$QHLev$R50;unused3
$Brkpt$R36;temp2
$Sign$R57;XTPreg
$IBufp$R60;stk0
$OBufp$R61;stk1
$Count$R62;stk2
$CBPtr$R63;stk3
$BTabs$R42;unused1
$QTabs$R43;unused2
$PTab$R44;alpha
$Pred$R35;temp
$Nowp$R55;ATPreg
$Hist$R64;stk4
$PVal$R1;mx
$Bits$R2;saveret
$Val$R3;newfield
$QMode$R56;OTPreg
;---------------------------------------------------------------
; ADPCM
;---------------------------------------------------------------
!1,2,InitRet0,InitRet1;Subroutine returns
!1,2,ComRet0,ComRet1;
;---------------------------------------------------------------
; Initialization
;
;IBufp:POINTER, (stk0)
;OBufp:POINTER, (stk1)
;Count:CARDINAL, (stk2)
;
; Expects pointer to control block in stk3
;
; CB: TYPE = RECORD
;[
;BTabs:POINTER,
;QTabs:POINTER,
;PTab:POINTER,
;Pred:INTEGER,
;QMode:CARDINAL
;Nowp:CARDINAL,
;Hist:INTEGER,
;PVal:CARDINAL,
;]
;---------------------------------------------------------------
InitRegs:SubRet←L;
MAR←L←CBPtr;
APTemp←L;
L←MD,TASK;
BTabs←L;
MAR←L←APTemp+1;
APTemp←L;
L←MD,TASK;
QTabs←L;
MAR←L←APTemp+1;
APTemp←L;
L←MD,TASK;
PTab←L;
MAR←L←APTemp+1;
APTemp←L;
L←MD,TASK;
Pred←L;
MAR←L←APTemp+1;
APTemp←L;
L←MD,TASK;
QMode←L;
MAR←L←APTemp+1;
APTemp←L;
L←MD,TASK;
Nowp←L;
MAR←L←APTemp+1;
APTemp←L;
L←MD,TASK;
Hist←L;
MAR←L←APTemp+1;
APTemp←L;
L←MD,TASK;
PVal←L;
; Fetch mode register contents
T←QMode;
MAR←BTabs+T;
L←QTabs+T;
APTemp←L;
L←MD,TASK;
Brkpt←L;
MAR←APTemp;
T←377;
L←T←MD AND T;(T←MD, L←MD AND 377)
QHLev←L;
L←177400 AND T,TASK;
QLLev←L LCY 8;
SINK←SubRet,BUS,TASK;
NOP,:InitRet0;
;---------------------------------------------------------------
; Save Algorithm state and return
;
; Stuff to save:
; Pred, QMode, Nowp, Hist, PVal
;---------------------------------------------------------------
SaveRet:T←CBPtr;
MAR←L←3+T;
APTemp←L,TASK;
MD←Pred;
MAR←L←APTemp+1;
APTemp←L,TASK;
MD←QMode;
MAR←L←APTemp+1;
APTemp←L,TASK;
MD←Nowp;
MAR←L←APTemp+1;
APTemp←L,TASK;
MD←Hist;
MAR←L←APTemp+1;
APTemp←L,TASK;
MD←PVal;
; returns control to emulator in Rom1
L←3,TASK;most general return (Return&TASK)
stkp←L;
SWMODE;Switch to Rom1
L←T←0,:romnextA;Mesa emulator entry point
;---------------------------------------------------------------
; Encode
;---------------------------------------------------------------
!1,2,EnMore,EnDone;
!1,2,EnLeft,EnRight;
EnCode:L←0,TASK,:InitRegs;
InitRet0:NOP;
EnLoop:MAR←IBufp;
L←Count-1,BUS=0;
Count←L,:EnMore;!1,2,EnMore,EnDone;
EnMore:SINK←Nowp,BUSODD;even or odd byte?
L←MD,:EnLeft;!1,2,EnLeft,EnRight;
EnLeft:T←377;
L←LastL AND NOT T,TASK;even is left byte
Val←L LCY 8,:EnSExt;
EnRight:T←377;
L←LastL AND T,TASK;
Val ← L;
L←IBufp+1,TASK;
IBufp←L,:EnSExt;
; Convert 8 bit two’s complement to sign magnitude
!1,2,EnPos,EnNeg;
EnSExt:T←Val;
L←177-T;SH<0 if Val>=200B
L←177400 OR T,SH<0;Val extended with 1’s
T←Pred,:EnPos;!1,2,EnPos,EnNeg;
EnNeg:Val←L;Use the extended value, if Val was IN[200..377]
EnPos:L←Val-T;Compute error
; at this point, error is two’s complement, we need SM
!1,2,EnPos2,EnNeg2;
Val←L,SH<0;save error
T←Val,:EnPos2;!1,2,EnPos2,EnNeg2;
EnPos2:L←0,TASK,:EnSgn;
EnNeg2:L←0-T;Store 0-Val in Val (now positive)
Val←L,L←0-1,TASK,:EnSgn;Sign ← -1
EnSgn:Sign←L,:Quant;Quantize Error
;---------------------------------------------------------------
; Quant Algorithm
;
; Expects current mode set up in registers,
; input value in Val,,Sign as sign magnitude.
; Returns output in Bits.
; code: 3 -QHLev, 2 -QLLev, 0 QLLev, 1 QHLev
;---------------------------------------------------------------
!1,2,Qlo,Qhi;
!1,2,QNeg,QPos;
Quant:T←Val;
L←Brkpt-T;
NOP,SH<0;
SINK←Sign,BUS=0,:Qlo;!1,2,Qlo,Qhi;
Qlo:L←T←0,:QNeg;!1,2,QNeg,QPos;
Qhi:L←T←ONE,:QNeg;!1,2,QNeg,QPos;
QNeg:L←2 OR T;
QPos:Bits←L;
; At this point, the Quantizer codeword is in Bits...
; We call the common code
L←0,TASK,:AdpcmCom;
;Now we must pack Bits into running sum...
ComRet0:L←PVal,TASK;left shift PVal 2 bits
PVal←L LSH 1;
L←PVal,TASK;
PVal←L LSH 1;
T←Bits;OR in Bits
L←PVal OR T;
!1,2,EnELoop,EnStore;
SINK←Nowp,BUS=0,TASK;
PVal←L,:EnELoop;!1,2,EnELoop,EnStore;
EnStore:L←OBufp+1;
MAR←OBufp;
OBufp←L;
L←0;no good reason to clear PVal
MD←PVal,TASK;
PVal←L,:EnELoop;
!1,2,EnInt,EnNoInt;
EnELoop:SINK←NWW,BUS=0;
TASK,:EnInt;!1,2,EnInt,EnNoInt;
EnNoInt:NOP,:EnLoop;
EnInt:NOP,:SaveRet;
EnDone:NOP,:SaveRet;
;---------------------------------------------------------------
; DeCode
;---------------------------------------------------------------
!1,2,DeMore,DeDone;
!1,2,DeWMore,DeWFetch;
DeCode:L←ONE,TASK,:InitRegs;initialize R & S registers
InitRet1:NOP,:DeLoop;
DeLoop:L←Count-1,BUS=0,TASK;
Count←L,:DeMore;!1,2,DeMore,DeDone
; If the (Modulo 8) counter Nowp is 0, it is time to fetch a new word.
DeMore:SINK←Nowp,BUS=0;
DeLoop1:L←T←PVal,:DeWMore;!1,2,DeWMore,DeWFetch;
DeWFetch:MAR←OBufp;Fetch a new word (when Nowp=0)
L←OBufp+1;
OBufp←L;
L←MD,TASK;
PVal←L,:DeLoop1;
DeWMore:PVal←L LSH 1,L←0;double shift to get left 2 bits of PVal
Bits←L MLSH 1;
L←T←PVal;
PVal←L LSH 1;
L←Bits,TASK;
Bits←L MLSH 1;assert: Bits is IN[0..3]
; at this point, we can call the common code
L←ONE,TASK,:AdpcmCom;
; On return from the Common code, the decompressed sample is in Val
; as Excess 200B, but we need 8 bit two’s complement to satisfy the audio board.
; Conversion algorithm is subtract 200B and mask.
ComRet1:T←177577;(-201B)
T←Val+T+1;T←Val-200B = Val+(-201B)+1
L←377 AND T,TASK;
Val←L;
; DeCode differs from EnCode in that NewQMode has already been called by
; this (access to uncompressed data) part of the loop. Thus here, an ODD
; value of Nowp means left half and an EVEN value means right side and
; increment IBufp
!1,2,DeRight,DeLeft;
SINK←Nowp,BUSODD;
MAR←IBufp,:DeRight;!1,2,DeRight,DeLeft;
DeLeft:L←Val;Store Val to left byte, 0 to right
Val←L LCY 8;
MD←Val,:DeELoop;
DeRight:T←Val;
L←MD OR T,TASK;Read old (left) half, and build complete word
Val←L;
MAR←IBufp;
L←IBufp+1;Increment IBufp
IBufp←L,TASK;
MD←Val,:DeELoop;
!1,2,DeInt,DeNoInt;
DeELoop:SINK←NWW,BUS=0;
TASK,:DeInt;!1,2,DeInt,DeNoInt;
DeInt:NOP,:SaveRet;
DeNoInt:NOP,:DeLoop;
DeDone:NOP,:SaveRet;
;---------------------------------------------------------------
; Common code for
; 1) Converting codeword to Quantizer output level
; 2) Updating current quantizer mode
; 3) Updating Predictor value
; Expects current Quantizer level in Bits, current time in Nowp
; Expects current modes values in QLLev, QHLev, Brkpt.
;---------------------------------------------------------------
;---------------------------------------------------------------
; DeQuant Algorithm
;
; Returns output in Val as 16 bit two’s complement.
; Bits had better only have a number IN [0..3] on entry...
;---------------------------------------------------------------
!3,4,QL0,QL1,QL2,QL3;
AdpcmCom:SubRet←L;Save return pointer
L←Bits,BUS;
T←QLLev,:QL0;!3,4,QL0,QL1,QL2,QL3;
QL0:L←QLLev,TASK,:DQCom;
QL1:L←QHLev,TASK,:DQCom;
QL3:T←QHLev,:QL2;
QL2:L←0-T,TASK,:DQCom;
DQCom:Val←L,:NewQMode;(Val←DeQuant(bits))
;---------------------------------------------------------------
; New QMode algorithm
; And common code for updating Predictor value
; Expects current Quantizer level in Bits, current time in Nowp
;---------------------------------------------------------------
; Determine mode switching
; Hist: 0 Bits: 2 => Decrement mode
; Hist: 2 Bits: 0 => Decrement mode
; Hist: 1 Bits: 1 => Increment mode
; Hist: 3 Bits: 3 => Increment mode
!1,2,He,Ho;
!1,2,HeBe,HeBo;
!1,2,HoBe,HoBo;
NewQMode:T←Nowp+1;
L←7 AND T,TASK;
Nowp←L;
T←Hist,BUSODD;
L←Bits-T,BUSODD,:He;!1,2,He,Ho;
He:L←Bits,TASK,SH=0,:HeBe;!1,2,HeBe,HeBo;
Ho:L←Bits,TASK,SH=0,:HoBe;!1,2,HoBe,HoBo;
!1,1,MSame;squash branch
!1,2,MDown,MDNil;
!1,2,MUNil,MUp;
HeBe:Hist←L,:MDown;!1,2,MDown,MDNil;
HeBo:Hist←L,:MSame;
HoBe:Hist←L,:MSame;
HoBo:Hist←L,:MUNil;!1,2,MUNil,MUp;
MDNil:T←Pred,:MDone1;
MUNil:T←Pred,:MDone1;
MSame:T←Pred,:MDone1;
!1,2,MDCom,MLLimit;
MDown:L←T←QMode-1,BUS=0;
NOP,:MDCom;!1,2,MDCom,MLLimit;
MDCom:QMode←L,:MFetch;now fetch new
MLLimit:T←Pred,:MDone1;
!1,2,MUCom,MULimit;
MUp:T←QMode;
L←14-T;swing - if T>=13 (dec)
L←T←QMode+1,SH<0;
NOP,:MUCom;!1,2,MUCom,MULimit;
MUCom:QMode←L,:MFetch;now fetch new
MULimit:T←Pred,:MDone1;
MFetch:MAR←BTabs+T;T has new QMode
L←QTabs+T;
APTemp←L;
L←MD,TASK;
Brkpt←L;
MAR←APTemp;
T←377;
L←T←MD AND T;(T←MD, L←MD AND 377)
QHLev←L;
L←177400 AND T,TASK;
QLLev←L LCY 8,:MDone;
; Now we have to do the prediction
MDone:T←Pred;
MDone1:L←Val+T,TASK;L has Pred+DeQuant(Bits)
Val←L;
; Val has a 16 bit TC value, we need to limit it to the range [-128..127]
; and convert to Excess 200B. We do this in one operation, by adding 200B
; and limiting to [0..377B].
!1,2,PrNoUF,PrUF;
!1,2,PrOv,PrOk;
T←Val;
L←T←177+T+1;
; If result of this add is still <0, Val was <-128 to start with...
; Now we check to see whether any bits outside [0..377B] range are on.
L←177400 AND T,SH<0;
L←T,SH=0,:PrNoUF;(L←Val+200B) !1,2,PrNoUF,PrUF;
PrNoUF:Val←L,:PrOv;!1,2,PrOv,PrOk;
!1,1,PrOv1;squash branch
PrUF:L←0,:PrOv1;
PrOv:L←377,:PrOv1;
PrOv1:Val←L;
; At this point, the excess 200B value is in both L and Val.
PrOk:APTemp←L RSH 1;
T←APTemp;compute PTab word address
L←PTab+T,TASK;
APTemp←L;
!1,2,PrEven,PrOdd;
MAR←APTemp;Start PTab access
SINK←Val,BUSODD;
T←377,:PrEven;!1,2,PrEven,PrOdd;
PrEven:L←MD AND NOT T,TASK;Even is left byte
Pred←L LCY 8,:PrSave;
PrOdd:L←MD AND T,TASK;Odd is right byte
Pred←L,:PrSave;
PrSave:T←200;
L←Pred-T;convert excess 200B to TC
SINK←SubRet,BUS,TASK;
Pred←L,:ComRet0;
; At exit, APTemp holds correct PTab word address
; and Val has correct Excess 200 version of data (output for DeCode)