Listing for DFM Realtime.asm
nolist
include 'DSPInit.asm'
;-----------------------------------------------------------------------------------
;Memory Map :
;- See how the C compiler organizes the data and variables
; and we will hand code the asm file generated
;
;- We will use the load a sine table into locations Y:256 through Y:511 of the
; extrenal Y data RAM and a exponential table into locations X:256 through X:511 of the
; external X data RAM of the DSP56000
; we need the set Data Rom Enable, DE = 0, in the Operating Mode Register (OMR)
;-----------------------------------------------------------------------------------
ProgramStart EQU $0040
;General variables used in the algorithm
Pi EQU 3.1415926536
TwoPi EQU 2.0*Pi
Factor EQU 0.017453292
PointOne EQU 0.1
PointZOne EQU 0.01
PointZZOne EQU 0.001
PointZZZOne EQU 0.0001
PointFive EQU 0.5
One EQU 1.0
Two EQU 2.0
;Memory locations for general use
FLAGS EQU $0001
DELAY EQU $0002
CYCLE_A EQU $0003
CYCLE_B EQU $0004
SINE_TABLE_START EQU $0005
SINE_TABLE_END EQU $0006
SINE_TABLE_SIZE EQU $0007
REPEAT_A EQU $0008
REPEAT_COUNTER EQU $0009
TEMP_A EQU $000A
TEMP_B EQU $000B
TEMP_C EQU $000C
TEMP_D EQU $000D
TEMP_E EQU $000E
TEMP_F EQU $000F
;Amplitude envelope parameters for DFM operator
AMP EQU $0010
AMP_COUNTER EQU $0011
AMP_STATUS EQU $0012
AMP_CURRENT EQU $0013
AMP_RATE12 EQU $0014
AMP_DURATION12 EQU $0015
AMP_RATE23 EQU $0016
AMP_DURATION23 EQU $0017
AMP_RATE34 EQU $0018
AMP_DURATION34 EQU $0019
AMP_RATE45 EQU $001A
AMP_DURATION45 EQU $001B
AMP_RATE56 EQU $001C
AMP_DURATION56 EQU $001D
;Parameters for Carrier
CARRIER_FREQ EQU $0020
CARRIER_PHASE EQU $0021
;Parameters and modulation index envelope for modulator (generator) 1
MODULATOR1_FREQ EQU $0030
MODULATOR1_INDEX EQU $0031
MODULATOR1_PHASE EQU $0032
INDEX1_COUNTER EQU $0033
INDEX1_STATUS EQU $0034
INDEX1_CURRENT EQU $0035
INDEX1_RATE12 EQU $0036
INDEX1_DURATION12 EQU $0037
INDEX1_RATE23 EQU $0038
INDEX1_DURATION23 EQU $0039
INDEX1_RATE34 EQU $003A
INDEX1_DURATION34 EQU $003B
INDEX1_RATE45 EQU $003C
INDEX1_DURATION45 EQU $003D
INDEX1_RATE56 EQU $003E
INDEX1_DURATION56 EQU $003F
;Parameters and modulation index envelope for modulator (generator) 2
MODULATOR2_FREQ EQU $0040
MODULATOR2_INDEX EQU $0041
MODULATOR2_PHASE EQU $0042
INDEX2_COUNTER EQU $0043
INDEX2_STATUS EQU $0044
INDEX2_CURRENT EQU $0045
INDEX2_RATE12 EQU $0046
INDEX2_DURATION12 EQU $0047
INDEX2_RATE23 EQU $0048
INDEX2_DURATION23 EQU $0049
INDEX2_RATE34 EQU $004A
INDEX2_DURATION34 EQU $004B
INDEX2_RATE45 EQU $004C
INDEX2_DURATION45 EQU $004D
INDEX2_RATE56 EQU $004E
INDEX2_DURATION56 EQU $004F
;-----------------------------------------------------------------------------------
;Host Command Jump Table
;This is where we assign the interrupt vectors to the labels (addresses)
;of the various subroutines
;-----------------------------------------------------------------------------------
ORG P:Reset
JMP ProgramStart
ORG P:SSITxInt
JSR TxInterrupt
ORG P:SSITxEx
JSR SSIException
ORG P:HostCmdInt0
JSR hcOutputWave
ORG P:HostCmdInt13
JSR RxDataInterrupt
;-----------------------------------------------------------------------------------
;Main Program
;-----------------------------------------------------------------------------------
ORG P:ProgramStart
MOVEP #$0000,X:M_BCR ;no wait states
BCLR #M_HF3,X:M_HCR ;HF3 = 0 ie handshake to Mac indicating now not in play mode
;This is where we usually set up various constants in memory, initialize pointers, etc...
;that stay the same between running of the algorithm. REMEMBER that if you request a card
;that has your algorithm already loaded into it, code that lives here WILL NOT BE RUN again
;because the driver will just say heres your card and it's already loaded so don't put
;stuff here that changes after or while you run your algorithm. Put stuff like that in a
;host command called initialize or at the start of your generic do it host command. See
;the filter module in DSPWorkshop for an example.
;NOTE: The following lines set up the various on chip and on card hardware. Generally you should
;just cut and paste from here and/or DSPWorkshop to get things the way you want them. The order
;is important so don't change it unless you know what your doing. See the white binder for
;more info on all the card controll logic and chapter 11 in the red book for the SSI etc...
;The I/O-interrupts on the 56k are quite complicated so mess with these initial 'magic' bit settings
;at your own risk and only after consulting the white binder and the red book.
MOVE #>$001377,X0 ;standard STEREO_ON_CARD_PLAYBACK (see white book)
MOVEM X0,P:CTL_LATCH ;set I/O control latch (see white book for details)
MOVEP #$2400,X:M_IPR ;set ssi ipl = 1, host port ipl = 0
MOVEP #$4006,X:M_CRA ;prescale = 1,const = 6,wl = 16 bits ie 44.1kHz
;sets SSI's on chip baud rate - see chap 11 in red book
BTST #4,X:M_SR ;read SSI SR to clear TUE bit so we don't get intrrupted??
MOVEP #0,X:M_TX ;send out zero sample, in conjunction with last line clrs TUE
MOVEP #$531E,X:M_CRB ;RIE = disable,RE = disable, TIE = enable,TE = enable
;SCKD = 0(ext clk),OF1 =1(stereo),OF0 = 0(ext clk)
MOVEP #$0005,X:M_PCDDR ;always!! set to this value so PAL's on card don't
;get conflicting pin directions on expansion port
MOVEP #$0001,X:M_PCD ;Oncard clock source,enable expansion port
;transmit it's a good idea to always enable the expansion port
;transmit so that someone using external DAC,I/O boxes etc...
;can get hold of the data your program generates
;The following three lines must be in this order and next to each other.
MOVEP #$01F8,X:M_PCC ;set port c to SSI mode, disable SCI
BSET #M_HCIE,X:M_HCR ;enable host command data interrupts
ANDI #$FC,MR ;enable interrupts ie set interrupt level to 0
;remember, reseting and loading code into the card
;starts you our at level 3 so you can set up the I/O
;stuff.
ORI #$FF,OMR ;set to mode 0 and set DE = 0 to disable use of ROM table
; ANDI #$0C,OMR ;
ANDI #$08,OMR ;
WtHost WAIT ;wait for mac/host to say start
JMP WtHost
;-----------------------------------------------------------------------------------
;
;-----------------------------------------------------------------------------------
hcOutputWave
BSET #M_HF3,X:M_HCR ;set HF3 to 1 to let host know code is running
BCLR #M_OF0,X:M_CRB ;OF0 = 0 external clock, OF0 = 1 internal clock
BCLR #M_OF1,X:M_CRB
BCLR #M_SCD0,X:M_CRB ;SCD0,SCD1,SCD2 must be set to 1
BSET #M_SCD1,X:M_CRB ;
BSET #M_SCD2,X:M_CRB ;
BCLR #M_SCKD,X:M_CRB ;SCKD = 0 external clock, SCKD = 1 internal clock
BSET #M_FSL,X:M_CRB
BSET #M_SYN,X:M_CRB
BCLR #M_GCK,X:M_CRB
BCLR #M_MOD,X:M_CRB
BSET #M_STE,X:M_CRB
BCLR #M_SRE,X:M_CRB
; BCLR #M_STIE,X:M_CRB
BCLR #M_SRIE,X:M_CRB
;Start of algorithm
CLR A
MOVE A0,Y:OUTPUT ;clear the output register
MOVE X:SINE_TABLE_START,A0
MOVE A0,X:CARRIER_PHASE
MOVE X:SINE_TABLE_START,A0
MOVE A0,X:MODULATOR1_PHASE
MOVE X:SINE_TABLE_START,A0
MOVE A0,X:MODULATOR2_PHASE
MOVE X:AMP,A0
MOVE A0,X:AMP_CURRENT
MOVE X:MODULATOR1_INDEX,A0
MOVE A0,X:INDEX1_CURRENT
MOVE X:MODULATOR2_INDEX,A0
MOVE A0,X:INDEX2_CURRENT
BSET #M_STIE,X:M_CRB ;switch DAC on
CLR A
MOVE A0,X:REPEAT_COUNTER
.REPEAT
CLR A #1,B0
MOVE X:REPEAT_COUNTER,A0
ADD B,A
MOVE A0,X:REPEAT_COUNTER
DO X:CYCLE_A,_EndCycle_A
DO X:CYCLE_B,_EndCycle_B
;The following is needed to ensure reference within the size of the sine table
.IF X:CARRIER_PHASE <GT> X:SINE_TABLE_END
CLR A
CLR B
MOVE X:CARRIER_PHASE,A0
MOVE X:SINE_TABLE_SIZE,B0
SUB B,A
MOVE A0,X:CARRIER_PHASE
NOP
.ELSE
NOP ;This NOP part is to ensure calculation time stays the same
NOP
NOP
NOP
NOP
NOP
NOP
.ENDI
;The following is needed to ensure reference within the size of the sine table
.IF X:MODULATOR1_PHASE <GT> X:SINE_TABLE_END
CLR A
CLR B
MOVE X:MODULATOR1_PHASE,A0
MOVE X:SINE_TABLE_SIZE,B0
SUB B,A
MOVE A0,X:MODULATOR1_PHASE
NOP
.ELSE
NOP ;This NOP part is to ensure calculation time stays the same
NOP
NOP
NOP
NOP
NOP
NOP
.ENDI
;The following is needed to ensure reference within the size of the sine table
.IF X:MODULATOR2_PHASE <GT> X:SINE_TABLE_END
CLR A
CLR B
MOVE X:MODULATOR2_PHASE,A0
MOVE X:SINE_TABLE_SIZE,B0
SUB B,A
MOVE A0,X:MODULATOR2_PHASE
NOP
.ELSE
NOP ;This NOP part is to ensure calculation time stays the same
NOP
NOP
NOP
NOP
NOP
NOP
.ENDI
;DFM Operator
;Modulator 1
MOVE X:MODULATOR1_FREQ,N2 ;N2 = modulator 1 frequency = Wm1
MOVE X:MODULATOR1_PHASE,R2 ;R2 = modulator 1 phase
MOVE X:INDEX1_CURRENT,Y0 ;Y0 = modulation index 1 = I1
MOVE Y:(R2)+N2,X0 ;X0 = Y:(R2) = sin(Wm1*t), then R2 = R2 + N2
MPY X0,Y0,A ;A = I1 * sin(Wm1*t)
MOVE A1,X:TEMP_A ;TEMP_A = I1 * sin(Wm1*t)
MOVE R2,X:MODULATOR1_PHASE ;keep modulator phase 1 up to date
;Modulator 2
MOVE X:MODULATOR2_FREQ,N2 ;N2 = modulator 2 frequency = Wm2
MOVE X:MODULATOR2_PHASE,R2 ;R2 = modulator 2 phase
MOVE X:INDEX2_CURRENT,Y0 ;Y0 = modulation index 2 = I2
MOVE Y:(R2)+N2,X0 ;X0 = Y:(R2) = sin(Wm2*t), then R2 = R2 + N2
MPY X0,Y0,A ;A = I2 * sin(Wm2*t)
MOVE A1,X:TEMP_B ;TEMP_B = I2 * sin(Wm2*t)
MOVE R2,X:MODULATOR2_PHASE ;keep modulator phase 2 up to date
;Carrier
CLR A ;clear A
CLR B ;clear B
MOVE X:TEMP_A,A0 ;A0 = I1 * sin(Wm1*t)
MOVE X:TEMP_B,B0 ;B0 = I2 * sin(Wm2*t)
ADD B,A ;A = I1 * sin(Wm1*t) + I2 * sin(Wm2*t)
MOVE A0,N0 ;N0 = I1 * sin(Wm1*t) + I2 * sin(Wm2*t)
MOVE X:CARRIER_PHASE,R1 ;R1 = carrier phase
MOVE R1,R0 ;R0 = carrier phase
MOVE X:CARRIER_FREQ,N1 ;N1 = carrier frequency = Wc
LUA (R0)+N0,R0 ;R0 = carrier phase + I1 * sin(Wm1*t) + I2 * sin(Wm2*t)
LUA (R1)+N1,R1 ;R1 = carrier phase + Wc
;The following is needed to ensure reference within the size of the sine table
.IF R0 <GT> X:SINE_TABLE_END
CLR A
CLR B
MOVE R0,A0
MOVE X:SINE_TABLE_SIZE,B0
SUB B,A
MOVE A0,R0
NOP
.ELSE
NOP ;This NOP part is to ensure calculation time stays the same
NOP
NOP
NOP
NOP
NOP
NOP
.ENDI
MOVE X:AMP_CURRENT,Y0 ;Y0 = amplitude = Amp
MOVE Y:(R0),X0 ;X0 = sin(Wc*t + I1 * sin(Wm1*t) + I2 * sin(Wm2*t))
MPY X0,Y0,A ;A = Amp * sin(Wc*t + I1 * sin(Wm1*t) + I2 * sin(Wm2*t))
MOVE A1,Y:OUTPUT ;OUTPUT = Amp * sin(Wc*t + I1 * sin(Wm1*t) + I2 sin(Wm2*t))
MOVE R1,X:CARRIER_PHASE ;keep carrier phase up to date
DO X:DELAY,_EndDelay
NOP
_EndDelay
NOP ;needed because addresses between "end of do loops" cannot be less than 2
_EndCycle_B
NOP ;needed because addresses between "end of do loops" cannot be less than 2
_EndCycle_A
NOP
;The following instructions simulates
;the envelopes of the amplitude and modulation indices
;Envelope for amplitude
MOVE X:AMP_STATUS,R0
.IF R0 <EQ> #1
MOVE X:AMP_COUNTER,R1
.IF R1 <LE> X:AMP_DURATION12
MOVE X:AMP_CURRENT,A0
MOVE X:AMP_RATE12,B0
ADD B,A
MOVE A0,X:AMP_CURRENT
.ELSE
CLR A #2,B0
MOVE A0,X:AMP_COUNTER
MOVE B0,X:AMP_STATUS
.ENDI
.ENDI
.IF R0 <EQ> #2
MOVE X:AMP_COUNTER,R1
.IF R1 <LE> X:AMP_DURATION23
MOVE X:AMP_CURRENT,A0
MOVE X:AMP_RATE23,B0
ADD B,A
MOVE A0,X:AMP_CURRENT
.ELSE
CLR A #3,B0
MOVE A0,X:AMP_COUNTER
MOVE B0,X:AMP_STATUS
.ENDI
.ENDI
.IF R0 <EQ> #3
MOVE X:AMP_COUNTER,R1
.IF R1 <LE> X:AMP_DURATION34
MOVE X:AMP_CURRENT,A0
MOVE X:AMP_RATE34,B0
ADD B,A
MOVE A0,X:AMP_CURRENT
.ELSE
CLR A #4,B0
MOVE A0,X:AMP_COUNTER
MOVE B0,X:AMP_STATUS
.ENDI
.ENDI
.IF R0 <EQ> #4
MOVE X:AMP_COUNTER,R1
.IF R1 <LE> X:AMP_DURATION45
MOVE X:AMP_CURRENT,A0
MOVE X:AMP_RATE45,B0
ADD B,A
MOVE A0,X:AMP_CURRENT
.ELSE
CLR A #5,B0
MOVE A0,X:AMP_COUNTER
MOVE B0,X:AMP_STATUS
.ENDI
.ENDI
.IF R0 <EQ> #5
MOVE X:AMP_COUNTER,R1
.IF R1 <LE> X:AMP_DURATION56
MOVE X:AMP_CURRENT,A0
MOVE X:AMP_RATE56,B0
ADD B,A
MOVE A0,X:AMP_CURRENT
.ELSE
CLR A #6,B0
MOVE A0,X:AMP_COUNTER
MOVE B0,X:AMP_STATUS
.ENDI
.ENDI
;Envelope for modulator1 index
MOVE X:INDEX1_STATUS,R0
.IF R0 <EQ> #1
MOVE X:INDEX1_COUNTER,R1
.IF R1 <LE> X:INDEX1_DURATION12
MOVE X:INDEX1_CURRENT,A0
MOVE X:INDEX1_RATE12,B0
ADD B,A
MOVE A0,X:INDEX1_CURRENT
.ELSE
CLR A #2,B0
MOVE A0,X:INDEX1_COUNTER
MOVE B0,X:INDEX1_STATUS
.ENDI
.ENDI
.IF R0 <EQ> #2
MOVE X:INDEX1_COUNTER,R1
.IF R1 <LE> X:INDEX1_DURATION23
MOVE X:INDEX1_CURRENT,A0
MOVE X:INDEX1_RATE23,B0
ADD B,A
MOVE A0,X:INDEX1_CURRENT
.ELSE
CLR A #3,B0
MOVE A0,X:INDEX1_COUNTER
MOVE B0,X:INDEX1_STATUS
.ENDI
.ENDI
.IF R0 <EQ> #3
MOVE X:INDEX1_COUNTER,R1
.IF R1 <LE> X:INDEX1_DURATION34
MOVE X:INDEX1_CURRENT,A0
MOVE X:INDEX1_RATE34,B0
ADD B,A
MOVE A0,X:INDEX1_CURRENT
.ELSE
CLR A #4,B0
MOVE A0,X:INDEX1_COUNTER
MOVE B0,X:INDEX1_STATUS
.ENDI
.ENDI
.IF R0 <EQ> #4
MOVE X:INDEX1_COUNTER,R1
.IF R1 <LE> X:INDEX1_DURATION45
MOVE X:INDEX1_CURRENT,A0
MOVE X:INDEX1_RATE45,B0
ADD B,A
MOVE A0,X:INDEX1_CURRENT
.ELSE
CLR A #5,B0
MOVE A0,X:INDEX1_COUNTER
MOVE B0,X:INDEX1_STATUS
.ENDI
.ENDI
.IF R0 <EQ> #5
MOVE X:INDEX1_COUNTER,R1
.IF R1 <LE> X:INDEX1_DURATION56
MOVE X:INDEX1_CURRENT,A0
MOVE X:INDEX1_RATE56,B0
ADD B,A
MOVE A0,X:INDEX1_CURRENT
.ELSE
CLR A #6,B0
MOVE A0,X:INDEX1_COUNTER
MOVE B0,X:INDEX1_STATUS
.ENDI
.ENDI
;Envelope for modulator2 index
MOVE X:INDEX2_STATUS,R0
.IF R0 <EQ> #1
MOVE X:INDEX2_COUNTER,R1
.IF R1 <LE> X:INDEX2_DURATION12
MOVE X:INDEX2_CURRENT,A0
MOVE X:INDEX2_RATE12,B0
ADD B,A
MOVE A0,X:INDEX2_CURRENT
.ELSE
CLR A #2,B0
MOVE A0,X:INDEX2_COUNTER
MOVE B0,X:INDEX2_STATUS
.ENDI
.ENDI
.IF R0 <EQ> #2
MOVE X:INDEX2_COUNTER,R1
.IF R1 <LE> X:INDEX2_DURATION23
MOVE X:INDEX2_CURRENT,A0
MOVE X:INDEX2_RATE23,B0
ADD B,A
MOVE A0,X:INDEX2_CURRENT
.ELSE
CLR A #3,B0
MOVE A0,X:INDEX2_COUNTER
MOVE B0,X:INDEX2_STATUS
.ENDI
.ENDI
.IF R0 <EQ> #3
MOVE X:INDEX2_COUNTER,R1
.IF R1 <LE> X:INDEX2_DURATION34
MOVE X:INDEX2_CURRENT,A0
MOVE X:INDEX2_RATE34,B0
ADD B,A
MOVE A0,X:INDEX2_CURRENT
.ELSE
CLR A #4,B0
MOVE A0,X:INDEX2_COUNTER
MOVE B0,X:INDEX2_STATUS
.ENDI
.ENDI
.IF R0 <EQ> #4
MOVE X:INDEX2_COUNTER,R1
.IF R1 <LE> X:INDEX2_DURATION45
MOVE X:INDEX2_CURRENT,A0
MOVE X:INDEX2_RATE45,B0
ADD B,A
MOVE A0,X:INDEX2_CURRENT
.ELSE
CLR A #5,B0
MOVE A0,X:INDEX2_COUNTER
MOVE B0,X:INDEX2_STATUS
.ENDI
.ENDI
.IF R0 <EQ> #5
MOVE X:INDEX2_COUNTER,R1
.IF R1 <LE> X:INDEX2_DURATION56
MOVE X:INDEX2_CURRENT,A0
MOVE X:INDEX2_RATE56,B0
ADD B,A
MOVE A0,X:INDEX2_CURRENT
.ELSE
CLR A #6,B0
MOVE A0,X:INDEX2_COUNTER
MOVE B0,X:INDEX2_STATUS
.ENDI
.ENDI
NOP
; MOVE X:INDEX1_CURRENT,A0
; MOVE X:INDEX1_INC,B0
; ADD B,A
; MOVE A0,X:INDEX1_CURRENT
;_EndRepeat_A
.UNTIL X:REPEAT_COUNTER <GE> X:REPEAT_A
nolist
BCLR #M_STIE,X:M_CRB ;switch DAC off
BCLR #M_HF3,X:M_HCR ;clear HF3 to let host know end of code running
RTI
;-------------------------------------------------------------------------------------
;SSI transmit data
;-------------------------------------------------------------------------------------
TxInterrupt
JSET #M_HF3,X:M_HCR,Playing ;see if we think we are playing
MOVEP #0,X:M_TX ;play a zero
RTI
Playing
MOVEP Y:OUTPUT,X:M_TX ;send the sample to SSI port (DAC)
JCLR #1,X:M_PCD,Done ;if we just played a right sample
Done RTI
;-------------------------------------------------------------------------------------
;Handle a SSI transmit interrupt with underrun exception - tell Mac via HREQ interrupts
;-------------------------------------------------------------------------------------
SSIException
BTST #4,X:M_SR ;read SSI SR to clear TUE bit
MOVEP Y:OUTPUT,X:M_TX ;transmit data to DAC (also needed to clear TUE)
RTI
;-------------------------------------------------------------------------------------
;Interrupt to receive data from host to specific locations of X or Y memory
;-------------------------------------------------------------------------------------
RxDataInterrupt
LoopRxa JCLR #M_HRDF,X:M_HSR,LoopRxa ;wait till MAC has sent the address over
MOVEP X:M_HRX,X0 ;read the xySelect into X0
LoopRxb JCLR #M_HRDF,X:M_HSR,LoopRxb ;wait till MAC has sent the address over
MOVEP X:M_HRX,R0 ;read the xyAddress into R0
LoopRxc JCLR #M_HRDF,X:M_HSR,LoopRxc ;wait till MAC has sent the value over
MOVEP X:M_HRX,Y0 ;read the xyValue into Y0
CLR B
ADD X0,B
JNE RxY
RxX MOVE Y0,X:(R0) ;put value into specified location in X memory
RTI
RxY MOVE Y0,Y:(R0) ;put value into specified location in Y memory
RTI
END