<font size=-1>
http://a330.g.akamai.net/7/330/2540/20020215161858/www.edn.com/contents/images/di2849l1.txt
di2849l1.txt
;********************************************************************
; 
; LISTING 1 - 16c54 NUMERICALLY CONTROLLED OSCILLATOR-EMULATION ROUTINE
;
; "Microcontroller emulates numerically controlled oscillator," EDN, Feb 21, 2002, pg 92
;
;********************************************************************


STRT:
   clr  a          ;MOVLW    0
                   ;TRIS     PORTB        ;Set port B to output
   mov  PHASL,a    ;MOVWF    PHASL        ;Initialize phase
   mov  PHASM,a    ;MOVWF    PHASM
   mov  PHASH,a    ;MOVWF    PHASH
                   ;MOVWF    ZER          ;Constant = 256
                   ;MOVLW    64
   mov  MAX,#-64    ;MOVWF    MAX          ;Constant = 64

; Set up default frequency, here 1000 Hz

;the actual frequency of course depends on loop length 
;(presence/absence of modulation) and crystal - needs
;to be recalculated
                   ;MOVLW    64
   mov  FREQL,#64  ;MOVWF    FREQL
                   ;MOVLW    150
   mov  FREQM,#150 ;MOVWF    FREQM
                   ;MOVLW    1
   mov  FREQH,#1   ;MOVWF    FREQH

;in '51, no need for the "quirk exploit"

                   ;GOTO     DONE
; Set up fixed return address in stack
           ;RETX    CALL     RETY
                   ;GOTO     LOOP         ;Start generating output
           ;RETY    RETLW    0            ;Dummy to fix return address
           ;DONE    CALL     RETX         ;Set up fixed return address

; Look-up return reenters at this point with sine sample in W
LOOP:
   jnb  TEMP,INV,LOOPX1     ;BTFSC    TEMP,INV     ;Test if inversion needed
; Replace previous instruction by BTFSC PHASH,INV
; if BPSK modulation not needed.
   cpl  a          ;SUBWF    ZER,0        ;Invert output
   inc  a
LOOPX1:
   mov  p1,a       ;MOVWF    PORTB        ;Output sample

; Check if user wants to load a new frequency
   jnb  P3,DAT,NEW ;BTFSC    PORTA,DAT    ;Test for change flag
                   ;GOTO     NEW          ;Get new user frequency

; Three-byte addition of control to phase accumulator
   mov  a,PHASL    ;MOVF     FREQL,0      ;Get frequency low byte
   add  a,FREQL    ;ADDWF    PHASL,1      ;Increment phase low byte
   mov  PHASL,a    ;MOVF     STATUS,0
                   ;ANDLW    1            ;W = carry
                   ;ADDWF    PHASM,1      ;Add carry
                   ;BTFSC    STATUS,CY    ;Skip if no carry
                   ;INCF     PHASH,1      ;Propagate carry
   mov  a,PHASM    ;MOVF     FREQM,0      ;Get frequency mid byte
   addc a,FREQM    ;ADDWF    PHASM,1      ;Increment phase mid byte
   mov  PHASM,a    ;BTFSC    STATUS,CY    ;Skip if no carry
   mov  a,PHASH    ;INCF     PHASH,1      ;Propagate carry
   addc a,FREQH    ;MOVF     FREQH,0      ;Get frequency high byte
   mov  PHASH,a    ;ADDWF    PHASH,1      ;Increment phase high byte

   mov   a,P3      ;SWAPF    PORTA,MOD    ;Modulation input to bit 7
   swap  a
   xrl   a,PHASH   ;XORWF    PHASH,0      ;XOR inversion bit
   mov   TEMP,a    ;MOVWF    TEMP         ;Save inversion bit
; Remove above (three) four instructions if modulation not needed.

   mov   a,PHASH   ;MOVF     PHASH,0
   anl   a,#63     ;ANDLW    63           ;Get table index
   jnb   a,REV,LOOPX2  ;BTFSC    PHASH,REV    ;Test if reversal needed
; Replace above instruction by BTFSC TEMP,REV for QPSK
   add   a,MAX     ;SUBWF    MAX,0        ;Reverse index direction
;'51: MAX is assumed to be set as negative modulation depth
LOOPX2:
   add   a,#TABLE-LOOPX3         ;ADDWF    PC,1         ;Jump to table
   movc  a,@a+pc
LOOPX3:
   jmp   LOOP
   
TABLE:
; Partial sine look-up table
; 65 entries = sines of first quadrant
;
;        RETLW        128
;        RETLW        131
;        RETLW        134
;        ;
;        ;
;        RETLW        255
   db    128,131,134,...,255</font>