Appendix 9

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