Back    Contents   Next


Basic Example

Below is a basic example of an as10k1 assembly program with lots of comments. For the lazy, this example should get you going without needed to do to much reading. the comments explain the code, not the function of the program.
;;; An example for the lazy.
;;; this line is a comment
; this too

;;; To avoid confusion, it should be pointed out that the dsp programs are also
;;; referred to as "patches" or "effects".

;; first we need to give our program a name, this will be used by the loader to
;; identify it.  Commands such a these are called a "directive", they don't
;; map to an emu10k1 instruction, but instead are used by the assembler and/or loader:

        name "fir_example" ; note, everything after a semicolon is a comment.

;; we include the "emu_constants.asm" file, it contains constants and macros.
;; Like most other languages, an include basically results in another file 
;; being "pasted" at the point of inclusion.

        include "emu_constants.asm"

;; The above shows that unlike C, semi colons are not nessecary to terminate a line.


;; Next we'll define some variables. "coef" is an array of read-only constants, the "con" 
;; (short for constants) is another directive. These constants will be stored in the 
;; emu's memory, the read-only-ness allows for reuse of the memory values by other loaded
;; programs. This increases memory usage efficiency. The array will be called "coef", "coef" is
;; known as a label, labels are always placed at the beginning of the line.

coef con 0.038684406  0.058115275  0.113007075  0.194116501  0.287525429  0.377072924  0.447195555  0.485671998  0.485783252 0.447503000  0.377505237  0.287987288  0.194517783  0.113292922  0.058289230  0.038818213

;; note: lines can be up to 256 characters long.

;; the next line defines an assembly time constant, this is the assembly
;; equivalent of C's #define statement.
;; in assembly, this is called an "equate".

n equ 15        ; filter order

;;; The loader currently only handle mono dsp programs. The programs
;;; are loaded onto "lines", and should always have one input and one output.
;;; input-output this pair is defined using the "io" directive:

in      io

;; "in" is a our io line, if we read from it we'll get the input value,
;; if we write to it, we'll put the output value. To make the code a bit more readable,
;; we'll create an equate of "in" and call it "out".

out equ in

;; The program needs user control, for this we define special memory locations
;; as "control" regiters. The name of the control seen by the users will be the label placed at
;; the beginning of the line. the format is:
;; 
;;label  control  initial-value, min-value, max-value

bass    control 0,0,#1

;; The next line declares an array of static (or shorthand sta) memory locations with initial
;; values:

delay   sta 0,0,0,0,0 ,0,0,0,0,0 ,0,0,0,0,0 ,0

;; temporary locations are declared using dyn (short for dynamic). 
;; dynamic registers are reused by other dsp programs to increase
;; register usage.

tmp  dyn

;;ok, now that we've declared everything we need, it's time to tackle the actual code:


;; the 16 instructions instructions always 4 operands
;; the first operand is the destination register, the last three
;; are source registers.

        macints  delay,in,C_0,C_0

;; the next instruction is a dummy instruction used to clear the accumulator
;; the C_0 was declared in "emu_constants.asm", it's a hardware register containing the 
;; value 0.

        macs  C_0,C_0,C_0,C_0

;; An assembly time for statement is available, the format:
;; for variable = start : finnish  

        for i = n : 1

;; operands can be calculated at assembly time as is shown below:

                macmv   delay+i,delay+i-1,delay+i,coef+i

        endfor ;; end of for loop


        macs tmp,ACCUM,delay,coef
        macs1 out,in,tmp,bass

;; an "end" directive tells the assmbler that we have reached the end of the program, 
;; anything after is ingnored

        end




Filtering

Fir and IIR filters are a staple of dsp programming. In this section I'll show how the emu10k1 can be used to accomplish such filtering.

FIR

FIR filters are the simplest filter to implement. Saturation is easy to avoid and a variety of frequency and phase responses can be created. The FIR filter's weakness is that they generally require more coefficients and more multiplications to have the same frequency response as an IIR filter.

If you've never worked with FIR filters, I recommend the following reading:
DSPGuru's FIR FAQ

Figure 1 below shows a FIR filter with N=3, the z^-1 elements are unit delays, the series of them tied together is called a delay-line. The "a0", "a1", etc. are multipications, when two lines join, it is a summation.


FIG 1, FIR filter

An fir filter has the following z-domain transfer function

 H(z) = a0 + a1*Z^-1 + a2*Z^-2 + ... 

Implementation

To implement a FIR filter we can either use tram, or the macmv instruction. If we use the macmv instruction an fir filter will have to following form (this example has n=4):

;;5th-order FIR filter


	include "emu_contanst.asm"

coef	con 	0.52478 , 0.12367 , 0.12365, 0.14734, 0.76346	;;our "a" coefficients
dly	sta 	0,0,0,0,0 				;;we need "n" static storage spaces	

in	IO
out 	equ in		
	
	macs  	C_0,C_0,C_0,C_0                    	;; clear the accumulator
	macmv   dly+5 , dly+4,   dly+5, coef +5  	;; we start by macing the last element, 
						   	;; and move the previous delay into the current
							;; The Result doesn't get written to "dly+5"till 
							;; after the mac is preformed.

	macmv   dly+4 ,  dly+3 , dly+4 , coef+4
	macmv	dly+3 ,  dly+2 , dly+3 ,coef+3
	macmv	dly+2 ,  dly+1 , dly+2 ,coef+2
	macmv	dly+1 ,  dly , dly+1 ,coef+1
	macmv   dly, in , dly, coef			;; on the last one we move the input into the delay-line
	
	move	out,ACCUM				;; Moves the accumulated value to the output	

On normal processor, the above may be implemented with a "for" loop. Since the emu10k1 does not have a loop instruction (in order to maintain real-timeness), each delay element must take 1 instruction. To code faster, we can use the assembly-time-for statement in as10k1, in which case the above example becomes:

;;5th-order FIR filter using assembly-time for statement

	include "emu_contanst.asm"

coef	con 	0.52478 , 0.12367 , 0.12365, 0.14734, 0.76346	;;our "a" coefficients
dly	sta 	0,0,0,0,0 				;;we need "n" static storage spaces	

in	IO
out 	equ in		
	
	macs  	C_0,C_0,C_0,C_0                    	;; clear the accumulator

	for i=5:1
		macmv   dly+i , dly+i-1,   dly+i, coef +i 	
	endfor

	macmv   dly, in , dly, coef			;; We move the input into the delay-line
	
	move	out,ACCUM				;; Moves the accumulated value to the output	

Alternatively one can use tram for the delay-line. I would only recommend to do such a thing in the event that you have alot of coefficients equal to 0. An example of a tram-based FIR filter is shown below:

;;6-th order FIR filter using tram

;;In this example, the 2 middle coefficients are 0, thus no need to calculate them.

	include "emu_contanst.asm"

coef	con 	0.12454,  0.52478 , 0.12367 , -0.12367, -0.52478 ;our "a" coefficients

dly	delay  7

write	twrite dly,0
rd1	tread  dly,1
rd2	tread  dly,2
;;the 2 middle taps are gone
;;
rd5	tread  dly,5
rd6	tread  dly,6

in	IO
out 	equ in		
tmp	dyn
	
	move  write,in
	
	macs	tmp,C_0,in,coef

	for i=0:3
		macw   tmp , tmp, rd1 +i, coef +i+1 	;;we use macw since it's ok if 
							;;intermediate results wraparound
	endfor
	
	move	out,tmp				;; Moves the accumulated value to the output	

Things to remember:

Octave has some prebuilt functions which will spit out filter coefficients based on arguments (filter order, cut-off frequency, etc) you give it. It's a good time saver.

IIR Filters

IIR filters are much more complicated to implement then FIR filter. This is primarily due to the fact that the emu10k1 is a fixed-point beast, and saturation and round-off are it's natural predators :-). None the less, the advantages of IIR filters usually warrant going to great length to implement. And well, nothing beats the personal satisfaction of have pull of such an impressive feat. The figure below shows two common methods of implementing a 2nd-order IIR filter.

     
Fig 2, IIR System

The IIR system's transfer function is given by:

        Y(z)       b0 + b1*z^-1 + b2*z^-2 + ...
 H(z) = ----  =   ------------------------------
	X(z)       1  + a1*z^-1 + a2*z^-2 + ...

Direct Form II The point which is of most concern in the system is W(z), the transfer function from the input to W(z) is given by:

        W(z)                   1               
        ----  =   ------------------------------
	X(z)       1  + a1*z^-1 + a2*z^-2 + ...

It is important to check that this point is bounded within [-1,1]. The simplest way is to calculate it's impulse response, take it's absolute value, then summ it up. Or:

 	---
	\_  imp(  W(z)  )
	/	  ---- 
	---       X(z)

[august 11, 2001] oops, I forgot to finish this part :-(


Back    Contents   Next