;               AMSAT/TAPR DSP BPSK demod for PIII satellites
;               Sample rate should be set to 8000 samples/sec.
;                       code by Bob McGwier N4HY
;
    org 0
    b go
;
;      Okay Jose' set up equates for data memory etc.
;
       org 10H  ;  code should begin at memory address 16 cause David
          ;  does something strange with locations 0-15.  Does
          ;  he in fact document this?

sine:   equ 0    ;  will store sine values from tone
one:    equ 1      ;  guess what goes here duhhhhh!
freq:   equ 2    ;  PLL frequency stored here
phase:  equ 3    ;  PLL phase stored here
maskl:  equ 4    ;  mask for fine or low order bits of phase
mask:   equ 5    ;  mask for doing modulo 16384 arithmetic with phase
sinx:   equ 6    ;  used to store coarse sine value (high order bits)
cosx:   equ 7    ;  as above for cosine
mone:   equ 8    ;   minus one stored here
wkph:   equ 9    ;  different phases (PLL, remodulator tone etc) are stored hr
                ;  for frequency synthesis
masko:  equ 10   ;  mask for converting on board PCM to DAC format
mps:    equ 11   ;  multiplier for quadrant determination for sine
mpc:    equ 12   ;  multiplier for quadrant determination for cosine
siny:   equ 13   ;  fine correction value for use in SIN(X+Y) = sinx*cosy+
cosy:   equ 14   ;          cosx*siny
cosine: equ 15   ;  cosine is also needed for Q arm in Costas loop
coph:   equ 16   ;  working number holder
modem:  equ 17   ;  Used to choose between complex tone for arms and real tone
                ;  in the modulator as explained below
bitpc:  equ 20   ;  freqo is assigned one of the values above for remod

xn0:    equ 21   ;  place  for storing all the values for the "I" or data arm
xn1:    equ 22
xn2:    equ 23
xn3:    equ 24
xn4:    equ 25
xn5:    equ 26
xn6:    equ 27
xn7:    equ 28
xn8:    equ 29
xn9:    equ 30
xn10:   equ 31
xn11:   equ 32
xn12:   equ 33
xn13:   equ 34
xn14:   equ 35
xn15:   equ 36
xn16:   equ 37
xn17:   equ 38
xn18:   equ 39
xn19:   equ 40
xn20:   equ 41
xn21:   equ 42
xn22:   equ 43
yn0:    equ 44  ; storage for values in "Q" or phase error arm.
yn1:    equ 45
yn2:    equ 46
yn3:    equ 47
yn4:    equ 48
yn5:    equ 49
yn6:    equ 50
yn7:    equ 51
yn8:    equ 52
yn9:    equ 53
yn10:   equ 54
yn11:   equ 55
yn12:   equ 56
yn13:   equ 57
yn14:   equ 58
yn15:   equ 59
yn16:   equ 60
yn17:   equ 61
yn18:   equ 62
yn19:   equ 63
yn20:   equ 64
yn21:   equ 65
yn22:   equ 66
c0:     equ 67  ; Phase error
maskf:  equ 68  ; frequency mask so that frequency doesn't try and leave
               ; proper range
H9:     equ 69
H10:    equ 70  ;  arm filter value too large to use mpyk
H11:    equ 71  ;  arm filter value too large to use mpyk
sweep:  equ 74  ;  contains sweep value  +/- one frequency unit per sample
highfq: equ 75  ;  high end of sweep range
lowfq:  equ 76  ;  low end of sweep range
clkfr:  equ 78
clkph:  equ 79
ch4:    equ 80
ch5:    equ 81
ch6:    equ 82
bd0:    equ 83
bd1:    equ 84
bd2:    equ 85
bd3:    equ 86
bd4:    equ 87
bd5:    equ 88
bd6:    equ 89
bd7:    equ 90
bd8:    equ 91
bd9:    equ 92
bd10:   equ 93
bd11:   equ 94
bd12:   equ 95
cd0:    equ 96
cd1:    equ 97
cd2:    equ 98
cd3:    equ 99
cd4:    equ 100
cd5:    equ 101
cd6:    equ 102
cd7:    equ 103
cd8:    equ 104
cd9:    equ 105
cd10:   equ 106
cd11:   equ 107
cd12:   equ 108
clkav:  equ 109
bit:    equ 110
clock:  equ 111
clocke: equ 112
bito:   equ 115
bit0:   equ 18
bit1:   equ 19
bita0:  equ 113
bita1:  equ 114
clkit:  equ 116
bitm0:  equ 117
bitm1:  equ 118
tbit:   equ 119
tclk:   equ 120
tfrq:   equ 121
tph:    equ 122
tcnt:   equ 123
carof:  equ 124
inty:   equ 125
eom:    equ 126
sintbl: dw      0  ; coarse sine table in steps of PI/64 radians to PI/2
      dw    804
      dw   1607
      dw   2410
      dw   3211
      dw   4011
      dw   4807
      dw   5601
      dw   6392
      dw   7179
      dw   7961
      dw   8739
      dw   9511
      dw  10278
      dw  11038
      dw  11792
      dw  12539
      dw  13278
      dw  14009
      dw  14732
      dw  15446
      dw  16150
      dw  16845
      dw  17530
      dw  18204
      dw  18867
      dw  19519
      dw  20159
      dw  20787
      dw  21402
      dw  22004
      dw  22594
      dw  23169
      dw  23731
      dw  24278
      dw  24811
      dw  25329
      dw  25831
      dw  26318
      dw  26789
      dw  27244
      dw  27683
      dw  28105
      dw  28510
      dw  28897
      dw  29268
      dw  29621
      dw  29955
      dw  30272
      dw  30571
      dw  30851
      dw  31113
      dw  31356
      dw  31580
      dw  31785
      dw  31970
      dw  32137
      dw  32284
      dw  32412
      dw  32520
      dw  32609
      dw  32678
      dw  32727
      dw  32757
      dw  32767  ; PI/2
fines: dw      0  ; fine sine table  in steps of PI/(64*64) radians to PI/64
      dw     12
      dw     25
      dw     37
      dw     50
      dw     62
      dw     75
      dw     87
      dw    100
      dw    113
      dw    125
      dw    138
      dw    150
      dw    163
      dw    175
      dw    188
      dw    201
      dw    213
      dw    226
      dw    238
      dw    251
      dw    263
      dw    276
      dw    289
      dw    301
      dw    314
      dw    326
      dw    339
      dw    351
      dw    364
      dw    376
      dw    389
      dw    402
      dw    414
      dw    427
      dw    439
      dw    452
      dw    464
      dw    477
      dw    490
      dw    502
      dw    515
      dw    527
      dw    540
      dw    552
      dw    565
      dw    578
      dw    590
      dw    603
      dw    615
      dw    628
      dw    640
      dw    653
      dw    665
      dw    678
      dw    691
      dw    703
      dw    716
      dw    728
      dw    741
      dw    753
      dw    766
      dw    779
      dw    791
finec: dw   32767  ;ditto to above for fine sine
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32766
      dw   32765
      dw   32765
      dw   32765
      dw   32765
      dw   32765
      dw   32765
      dw   32765
      dw   32765
      dw   32764
      dw   32764
      dw   32764
      dw   32764
      dw   32764
      dw   32764
      dw   32764
      dw   32763
      dw   32763
      dw   32763
      dw   32763
      dw   32763
      dw   32762
      dw   32762
      dw   32762
      dw   32762
      dw   32762
      dw   32761
      dw   32761
      dw   32761
      dw   32761
      dw   32760
      dw   32760
      dw   32760
      dw   32760
      dw   32759
      dw   32759
      dw   32759
      dw   32759
      dw   32758
      dw   32758
      dw   32758
      dw   32758
      dw   32757
      dw   32757
armfilt: dw  5054   ; H9
        dw  7679   ; H10
        dw  8706   ; H11
        dw  4769   ; CH4
        dw  6557   ; CH5
        dw  7249   ; CH6
go:  ldpk 0         ; make sure that we are pointing to 0 data page
    larp ar0
    lark ar0,1
    lark ar1,19
    lack one   ; make and store one
    sacl one

    lack armfilt ; store address of the too large coefficients
    tblr H9
    add one
    tblr H10
    add one
    tblr H11
    add one
    tblr CH4
    add one
    tblr ch5
    add one
    tblr ch6
    lac one,11  ; make and store DAC converter mask
    sacl masko
    lac one,13   ; make and store frequency mask
    sub one
    sacl maskf
    lt one
    mpyk 3072  ; 1500Hz
    pac
    sacl freq
    lt one
    mpyk 4095  ; make and store high end of sweep range
    pac
    sacl highfq
    lt one
    mpyk 2400 ; make and store low end of sweep range
    pac
    sacl lowfq
    zac
    sub one
    sacl mone  ; make and store minus one
    lac one,14
    sub one
    sacl mask  ; make and store mask for modulo 16384 phase arithmetic
    lac one,6
    sub one
    sacl maskl ; make and store fine part of address mask;
    zac
    sacl inty
    sacl phase
    sacl clkph
    sacl tph
    sacl clkph
    lac one
    sacl sweep
    lt one
    mpyk 1638  ; make and store clock rate (1638 = 800 Hz)
    pac
    sacl clkfr
    lt one
    mpyk 3072
    pac
    sacl tfrq

;  Okay the BS is over lets get to work !

wait: bioz fire    ; is it time for a new sample
     b wait       ; nope go wait in the corner
fire: in xn0,pa3   ; get a new sample here

    lac xn0,4
    sub one,15   ; Change ADC format to two's complement
    sacl xn0

;    modulator/status/misc output

    lac bito,12
    addh masko
    sach yn0
    out yn0,pa4   ; send bito to DAC
    out bitpc,pa7 ;  output control word to PC
       lac inty   ; load the PC interrupt control word
       bz noi
       zac        ; yes
       sacl inty
       out 6,6   ; tell the PC that we are ready

;    end output

;    Determine transmit bit timing

noi: larp 1   ; transmit timing auxiliary register
    banz noty ;  is time to tick the clock for the PC?

    lark ar1,19  ; yes,reset output bit counter
    zac
    sacl tcnt    ; reset Manchester (split phase) counter
    lac one,2    ; get ready to tick the clock in the PC control word
    sacl c0
    lac bitpc    ; load the control word
    xor c0       ; tick the <TX> clock
    sacl bitpc   ; store the new PC control word
    lac inty
    or one
    sacl inty
    in  c0,7     ; input the PC side control word
    lac one,4
    and c0       ; pick off the eye or modulator control bit
    sacl eom
    lac one,3    ; pick off the transmit bit
    and c0
    bgz tpo      ; is it zero ?
    lac mone     ; yes set bit multiplier to minus one
    sacl tbit
    b noty
tpo: lac one      ; No it is a one so set the multiplier to one
    sacl tbit

noty:


;    Manchester the bit

    lac tcnt    ; Find out if we are half way through the bit
    sub one,3
    sub one,2   ; if tcnt is ten, it is time to split the bit
    bnz dnch    ; if we are not half way, don't change it
    lt mone     ;  We are halfway so change the sign of the bit multiplier
    mpy tbit
    pac
    sacl tbit
dnch: lac tcnt
    add one     ;  Increment ther Manchester (split phase) counter
    sacl tcnt

;    Begin modulator
    lac eom
    bnz eye
    lac mone
    sacl modem ; real tone
    lack one  ;
    sacl mps  ;   Restore sine and cosine quadrant multipliers
    sacl mpc  ;
    lac tph
    add tfrq ; increment the modulator phase, tph, by the modulator frq, tfrq
    and mask
    sacl tph
    call tones  ; get the sinusoid
    lt sine
    mpy tbit   ; multiply by the bit
    pac
    sacl bito  ; store for the DAC  to output to the transmitter


;    Begin demodulator
eye: lack one
    sacl modem  ; store control that makes complex tone
    lack one  ;
    sacl mps  ;   Restore sine and cosine quadrant multipliers
    sacl mpc  ;
    lac phase
    call tones ; go make complex tone

    lt xn0  ; load the current sample
    mpy cosine; multiply by cosine
    pac
    sach yn0,1 ; store it Q channel current sample
    mpy sine ; multiply sample by sine
    pac
    sach xn0,1 ; now store it in current sample place for I channel

;     Low pass for each sample cutoff is at 800 Hz. Carrier is 20-25 dB down

;     First lowpass I arm product to get the lower frequency mixer products

    zac
;
    lt xn22
    mpyk -117
;
    ltd xn21
    mpyk 675
;
    ltd xn20
    mpyk 647
;
    ltd xn19
    mpyk 325
;
    ltd xn18
    mpyk -453
;
    ltd xn17
    mpyk -1303
;
    ltd xn16
    mpyk -1513
;
    ltd xn15
    mpyk -459
;
    ltd xn14
    mpyk 1949
;
    ltd xn13
    mpy H9
;
    ltd xn12
    mpy H10
;
    ltd xn11
    mpy H11
;
    ltd xn10
    mpy H10
;
    ltd xn9
    mpy H9
;
    ltd xn8
    mpyk 1949
;
    ltd xn7
    mpyk -459
;
    ltd xn6
    mpyk -1513
;
    ltd xn5
    mpyk -1303
;
    ltd xn4
    mpyk -453
;
    ltd xn3
    mpyk 325
;
    ltd xn2
    mpyk 647
;
    ltd xn1
    mpyk 675
;
    ltd xn0
    mpyk -117
;
    apac
;
    sach xn0,1

;  Now do the Q arm to get the lower frequency mixer products.

    zac
;
    lt yn22
    mpyk -117
;
    ltd yn21
    mpyk 675
;
    ltd yn20
    mpyk 647
;
    ltd yn19
    mpyk 325
;
    ltd yn18
    mpyk -453
;
    ltd yn17
    mpyk -1303
;
    ltd yn16
    mpyk -1513
;
    ltd yn15
    mpyk -459
;
    ltd yn14
    mpyk 1949
;
    ltd yn13
    mpy H9
;
    ltd yn12
    mpy H10
;
    ltd yn11
    mpy H11
;
    ltd yn10
    mpy H10
;
    ltd yn9
    mpy H9
;
    ltd yn8
    mpyk 1949
;
    ltd yn7
    mpyk -459
;
    ltd yn6
    mpyk -1513
;
    ltd yn5
    mpyk -1303
;
    ltd yn4
    mpyk -453
;
    ltd yn3
    mpyk 325
;
    ltd yn2
    mpyk 647
;
    ltd yn1
    mpyk 675
;
    ltd yn0
    mpyk -117
;
    apac
;
    sach yn0,1

;  Okay we now have filtered products with data bauding pseudo base banded
;  Let's close the loop and lock on

    lac xn0;  Hard limit data line for phase error detector
    sacl bd0;  Input data into delay line so that demanchester can occur at
            ;  Zero crossing.
    bgez pdps  ; is data baud a plus one ?
    lt yn0     ; get ready for a multiply
    mpyk -1    ; multiply phase error arm by -1
    pac
    sacl yn0
pdps: lt carof
    mpyk 7     ; leaky integrate the phase error
    pac
    sacl cosy
    lac carof,15
    add cosy,12  ; take 7/8 of the old value and add it to the gain times
    add yn0,10   ; the current phase error observation. (LPF)
    sach carof
;;      lac carof,3  ;  uncomment these two lines to send the
;;      sacl bito  ;  VCO error voltage out the D/A
    lac yn0,10  ; add a small amount of the current phase error to
    addh carof  ; the integrator output.
    sach c0     ; this is the phase correction
    lac c0      ;  load correction
delph: add phase   ; add the old phase
    add freq    ; add the phase increment (frequency)
    and mask    ; modulo 2*pi
    sacl phase  ; store it for next sample complex tone generation step
       lac eom
       bz mod
       lac xn0     ; uncomment these two lines to send eye patterns out
       sacl bito   ; the D/A port
;       lac sine,11 ; uncomment these two to hear recovered carrier.
;       sach bito


;    Assuming lock, let's recover clock using a PLL on a nonlinearity
;    generating a clock tone

mod: lt xn0
    mpy xn0    ; square the current data arm value
    pac
    apac
    apac       ; add it to the accumulator three times to raise the number
               ; of significant bits in the high word
    sach cd0,1 ; shift left 4 and store the high word
    lt clkav
    mpyk 3    ; we will LPF with an exponential decay filter to estimate
    pac        ;  the average amplitude of the clock signal.  Multiply old
    sacl cosy
    lac cd0,13 ; load one sixteenth of the current clock signal value
    abs
    add clkav,15  ; value first by fifteen and then divide by 16.
    add cosy,13
    sach clkav
    lac cd0,2     ;load it shifting one up to increase significance
    sub clkav,2
    sacl cd0      ; subtract the average value, that is remove the DC.
;       sacl bito
    lac mone
    sacl modem ; real tone
    lack one  ;
    sacl mps  ;   Restore sine and cosine quadrant multipliers
    sacl mpc  ;
    lac one,12    ;  we are going to use the tone generator and we need
    sub clkph     ; cosine so take pi/2 - phase and then find the sine of
    add one,14    ; that after doing mod 2*pi
    and mask
    call tones    ; get the cosine  (which will be the sine since we did
;       lac sine,11
;       sach bito  ; un comment these two lines to see the clock tone
    lt sine       ; complimentary angle
    mpy cd0       ; multiply by the current clock tone value
    pac
    sach cd0,1

;    filter this product to get the clock signal phase error
;    sine(clock phase)*cosine(clock phase estimate) yields the error signal
;    LP Filter to get the lower frequency mixer products
;    Filter with cutoff at 800 Hz, doesn't need to be a great filter, just
;    the best you can do in 13 taps.  25+ dB rejection of the stuff at
;    1600.

    lt cd12
    mpyk -930
    pac

    ltd cd11
    mpyk -293
    apac
    dmov bd11

    ltd cd10
    mpyk 732
    apac
    dmov bd10

    ltd cd9
    mpyk 2571
    apac
    dmov bd9

    ltd cd8
    mpy ch4
    apac
    dmov bd8


    ltd cd7
    mpy ch5
    apac
    dmov bd7

    ltd cd6
    mpy ch6
    apac
    dmov bd6

    ltd cd5
    mpy ch5
    apac
    dmov bd5

    ltd cd4
    mpy ch4
    apac
    dmov bd4

    ltd cd3
    mpyk 2571
    apac
    dmov bd3

    ltd cd2
    mpyk 732
    apac
    dmov bd2

    ltd cd1
    mpyk -293
    apac
    dmov bd1

    ltd cd0
    mpyk -930
    apac
    dmov bd0

    sach clocke

    lt clkit
    mpyk 15   ; SLOW leak integrator,  31/32 old phase error plus
    pac
    sacl cosy
;     lac clkit,15
    lac cosy,12
;     add cosy,11
;     lac clkit,11
    add clocke,11  ;   1/32 of the new phase error estimate
    sach clkit
    lac clocke,10  ;   add 1/32 of the current phase error observation to
    add clkit,15     ;   the integrated estimate
    sach clocke
    lac clocke     ;   load phase correction

dlcph: add clkph   ; add the old phase
    add clkfr    ; add the phase increment (frequency)
    sacl clkph
    sub 1,14     ; if the phase is greater than two pi, we have hit a
    blz wait     ; baud boundary, if not, go back to wait and start over

;    We have crossed a baud boundary since the phase is >= 2^14.

    sacl clkph  ; store it for next sample complex tone generation step
;
;       We have had to comprimise.  The most we could fit, due to constraints
;       of 128 data memory data locations in page 0, is just over half the total
;       bit into our delay line.  HOWEVER, this is "the important half"
;       It is the center and contains the "peaks" around the guaranteed
;       zero crossing in the center of the bit.  Convolve with the bit shape
;       and get 0 or 1.
;
    ; de Manchester the bits in the delay line
    lt bd12
    mpyk 1024
    pac
    lt bd11
    mpyk 4095
    apac
    lt bd10
    mpyk 4095
    apac
    lt bd8
    mpyk 1024
    apac
    lt bd7
    mpyk 128
    apac
    lt bd5
    mpyk -128
    apac
    lt bd4
    mpyk -1024
    apac
    lt bd2
    mpyk -4095
    apac
    lt bd1
    mpyk -4095
    apac
    lt bd0
    mpyk -1024
    apac
    sach bit,4   ; Store the de-Manchestered bit
;    There is an ambiguity in the clock since the clock tone recovery
;    above runs at 800 Hz.  We must decide which is the REAL bit center
;    and then output that bit determined above.  This is a statistical
;    process.  We are guaranteed a bit transition in center of each
;    bit.   If we are on the boundary, the occasional change from a zero
;    to a one and vice versa causes NO transition.  The process above
;    Will yield a small positive number for abs(bit) when there is no
;    transition
    lac bit
    larp 0      ; set auxiliary register pointer to the receive clock control.
                ; <RX> clock control register
    banz b1     ; is it bit time 0 or bit time 1
    bgz psb0    ; it is bit time zero.  Is the bit positive?
    lac mone,10    ; No  load -1
    sacl bit0   ; and store it in bit 0
    b rar0
psb0: lac one,10
    sacl bit0
rar0: lark ar0,1  ; reset the counter  for the next two 2pi crossings.
    lac bit      ; load bit for the bit time 0, take absolute value
    abs
    sacl bita0   ; store it for later
    b wait
;    The bit time was 1 and not zero.
b1:  bgz psb1   ; is bit positive?
    lac mone,10   ; no so load -1
    sacl bit1  ; store it in bit one
    b rar1
;    yes it is positive
psb1: lac one,10   ; load one
    sacl bit1  ; store it in bit one
rar1: lac bit   ; load bit for the bit time 1, take absolute value
    abs
    sacl bita1 ; store it for later
    lack 13    ;  load the accumulator with 1101  binary
    and bitpc  ;  use it mask the PC control word to destroy old bit.
    xor one    ; tick the output clock
    sacl bitpc ; store it
    lac inty
    or one     ; setup to interrupt PC with new control word
    sacl inty
    lac bita1  ;  load the bit1 size
    sub bita0  ;  sub the bit0 size
    add bitm0  ;  integrate
    sacl bitm0 ; store it
    sub 1,12  ; let 4096 be the rails
    bgz th   ; are we greater than 4096?
    lac bitm0 ; load it again
    add 1,12   ; add 4096
    blz tl    ; still less than zero?  < -4096?
    b dbt
;   Yes we are greater than 4096, so make us 4096
th:  lac 1,12
    sacl bitm0
    b dbt
;   Yes we are less than -4096, so make us -4096
tl:  zac
    sub one,12   ; load -4096
    sacl bitm0  ; store it
dbt: lt bitm1
    mpyk 7   ; average with 31/32 times old + 1/32 * new
    pac
    sacl cosy
    lac bitm1,15
    lac cosy,12
    add bitm0,11
    sach bitm1
    lac bitm1
;       sacl bito  ; uncomment to see which clock phase was chosen for
                  ; the true center of bit
    blz bi0  ; is the averaged size of bit0 bigger?
; NO
    lac bit1 ; load bit 1
    b outb   ; jump to modify PC control word
bi0: lac bit0
outb:
;       sacl bito     ; uncomment to send bits out the D/A port
    blz wait ; if the bit is negative, bitpc is already setup
    lac one,1 ; if not we need to add a 1 in the 2's spot, i.e. add 2.
    or bitpc
    sacl bitpc ; store it in the PC control word
outp: b wait         ; go output and then do it again

;     complex tone generator if modem>0 if modem<0 sine wave generator

tones: sacl wkph  ;  store a working copy
    lac wkph,4
    subh one
    blz getem  ;  is it in a quadrant bigger than first?
    subh one
    bgez thfr; is it in a quadrant greater than two?
    lac 1,13  ;  nope so load pi
    sub wkph  ;  subtract phase so that it maps back into 1st quad
    sacl wkph ; store
    lac mone  ; load -1
    sacl mpc  ; store it in the cosine multiplier
    b getem   ; go read tables
thfr: lac mone  ; multiplier for bottom half
    sacl mps  ; store
    lac wkph  ;
    sub one,13 ; map angle back to  upper half and go do it again
    b tones
getem: lac wkph,10 ; take 1st quadrant phase equiv for sine and pick
                  ; off coarse part of phase address
    sach coph     ; store it
    lack sintbl   ;  load sine table offset into accumulator
    add coph      ; add coarse address off set
    tblr sinx     ; read the coarse sine value
    lac one,6     ;  load pi/2
    sub coph      ; subtract the coarse phase to get cosines offset
    sacl coph
    lack sintbl
    add coph      ; do same as above for coarse cosine
    tblr cosx
    lac wkph      ; load working phase
    and maskl     ;  mask off fine addres
    sacl coph     ; store it
    lack fines    ; locate fine table offset
    add coph      ; add for offset into fine table
    tblr siny     ;  read table
    lack finec
    add coph
    tblr cosy
    zac
    lt sinx       ; load sinx
    mpy cosy      ;  multiply by cosy
    lta siny      ; load t reg with fine sin and accumulate previous prod.
    mpy cosx      ; multiply by coarse cosx to use sin(X+Y)
    apac          ; add the result to coarse sine
    sach sine     ; store it.
    lac modem
    blz mult
    lac 1,12      ; load full address pi/2
    sub wkph      ; subtract the working phase to get cosine
    sacl wkph
    lac wkph,10   ;  from here to the later MARK it is identical to above
    sach coph
    lack sintbl
    add coph
    tblr sinx
    lac one,6
    sub coph
    sacl coph
    lack sintbl
    add coph
    tblr cosx
    lac wkph
    and maskl
    sacl coph
    lack fines
    add coph
    tblr siny
    lack finec
    add coph
    tblr cosy
    zac
    lt cosy
    mpy sinx
    lta siny
    mpy cosx
    apac  ; MARK
    sach cosine ; store it in the cosine
mult: lt mps;  now we need to do a few multiplies by sign changes due to
    mpy sine ; to quadrant part of phase address
    pac;     multiply sine by sine sign (:-)  and
    sacl sine ;  store the result
    lac modem
    bgz cosm
    ret
cosm: mpy cosine;  now multiply cosine by the same
    pac
    sacl cosine ; store it
    lt mpc;  load cosine differentiator from sine sign (:-)
    mpy cosine;  multiply
    pac
    sacl cosine ; store
    ret
    end
;  Ebbly Ebbly Ebbly thats all folks