I have been working on a program to run an AD9833 module by inputting the frequency in kHz and Hz in decimal. It is a work in progress and will end up being driven by a rotary encoder and/or a keypad. I claim no credit for the arithmetic part of the program, this uses a combination of Darrel Taylors' N Bit Math and a modified version of a program by, I think, Henrik Olsson. Haven't had any problems so far controlling an SPI device using SHIFTOUT.

PRECISION CON 5 SYSTEM
INCLUDE "N-bit_Math.pbp" ' Include the math routines.
include "modedefs.bas"
ADCON0=7 'MESSES UP SERIAL COMMS IF OMITTED
ADCON1=7

' Create some variables for the math routines to use.
MathVar1 VAR BYTE[PRECISION] ' Used by N-Bit math routines
MathVar2 VAR BYTE[PRECISION] ' Used by N-Bit math routines
MathVar3 VAR BYTE[PRECISION] ' Used by N-Bit math routines
Compensator VAR WORD

LSB VAR WORD'LEAST SIGNIFICANT BYTE OF TUNING WORD
MSB VAR WORD'MOST SIGNIFICANT BYTE OF TUNING WORD

LSBL VAR BYTE:LSBH VAR BYTE'SPLIT LSB WORD INTO 2 BYTES
MSBL VAR BYTE:MSBH VAR BYTE'SPLIT MSB WORD INTO 2 BYTES

DATAPIN3 var PORTA.0
CLOCKPIN3 var PORTA.1
FSYNC var PORTA.2

kHz VAR WORD
Hz VAR WORD

MAIN:

KHZ=1
HZ=500
gosub CalculateTuningValue

LSB=((MATHVAR1[0]+(MATHVAR1[1]&%00111111)<<8))+$4000'SEPARATE LOW 14 BITS AND SET BIT 15 TO 1
MSB=((MATHVAR1[1]>>6)+(MATHVAR1[2]<<2)+(MATHVAR1[3]<<10))+$4000'SEPARATE HIGH 14 BITS AND SET BIT 15 TO 1

LSBL=LSB&$FF:LSBH=LSB>>8'SEPARATE HIGH AND LOW BYTES
MSBL=MSB&$FF:MSBH=MSB>>8'SEPARATE HIGH AND LOW BYTES

HIGH FSYNC:PAUSE 2
HIGH CLOCKPIN3:PAUSE 2

LOW FSYNC
SHIFTOUT datapin3,clockpin3,5,[$21,$00,LSBH,LSBL,MSBH,MSBL,$C0,$00,$20,$00]
HIGH FSYNC

pause 1000
SEROUT2 PORTB.0,84,[HEX2 LSBH,HEX2 LSBL,HEX2 MSBH,HEX2 MSBL,10,13]
goto MAIN

CalculateTuningValue:
' Multiply the kHz variable by 1000 to get from kHz to Hz
@ MOVE?WP _kHz, _MathVar1 ; Move WORD to P_VAR
@ MOVE?CP 1000, _MathVar2 ; Move 1k to P_VAR
@ MATH_MUL _MathVar1, _MathVar2, _MathVar3 ; Multiply, store result in MathVar3

' Add the Hz variable to the previously calculated result
@ MOVE?WP _Hz, _MathVar2 ; Move WORD to MathVar2
@ MATH_ADD _MathVar3, _MathVar2, _MathVar1 ; Add MathVar3 to MathVar2, result in MathVar1

' Multiply the previous result by 87961.
@ MOVE?CP 87961, _MathVar2 ; Load constant to MathVar2
@ MATH_MUL _MathVar1, _MathVar2, _MathVar3 ; Multimply, result in MathVar3

' Add 4096 to the current result. This is done to perform rounding of the value
' to the closest integer when later dividing by 8192 instead of simply truncating
' the value as would be the case otherwise.
@ MOVE?CP 4096, _MathVar2 ; Load constant to MathVar2
@ MATH_ADD _MathVar3, _MathVar2, _MathVar1 ; Add to MathVar3, result in MathVar1

' Divide the previous result by 8192 to end up with the final tuning value.
@ MOVE?CP 8192, _MathVar2 ; Load MathVar2 with the value 64
@ MATH_DIV _MathVar1, _MathVar2, _MathVar3 ; Divide, result now in MathVar3

' By subtracting a proportion of the value of the kHz variable to the calculated tuning value
' we can get a little closer to the exact value.
Compensator = kHz ** 550 '550/65536
@ MOVE?WP _Compensator, _MathVar2 ; Move value to P_Var
@ MATH_SUB _MathVar3, _MathVar2, _MathVar1 ; Add it to previous result.
return