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