Good job, Carl.

That's one of the nice things about SBasic. While we (as product developers) have to play by rules that meet generalized requirements, the tools provided by SBasic allow you develop solutions for yourself that work for your specific requirements. Thanks for sharing this with others who may want the same behavior.
You might consider posting this to the Programming Examples board and/or submitting it to Inside Sesame as a Tip.
Also, may I suggest redoing it slightly as something like shown below. It's untested as I am not at a computer with Sesame, so you probably want to check it out yourself. The changes are:
1. The return type of the Function is declared, so it does not accidentally get forced to an Integer.
2. A separate variable is used to return, rather than taking over an argument. The results of returning a passed in argument can be unpredicatable.
//########## Begin Custom Rounding Function ##########
Function @cuRnd(Value as double, Places as int) as Double
var Neg as int //Used to handle Negatives properly
var vRet as Double
if Value < 0
Neg = -1
else
Neg = 1
vRet = @Abs(Value)
if @Instr(vRet, ".") > 0 //Protects whole numbers when rounding to zero places
{
if @Mid(vRet, @Instr(vRet, ".") + Places + 1, 1) >= 5
vRet = vRet + (1 / @Exp(10, Places))
vRet = @Del(vRet, @Instr(vRet, ".") + Places + 1, 10)
}
vRet = vRet * Neg
Return vRet
End Function
//########## End Custom Rounding Function ##########
BTW, this code repeatedly treats a number as a String without explicilty converting it first, which means that SBasic is converting it back and forth several times as you go, so you may want to keep an eye on this for anomalies caused by the repeated type conversions.
Just my $0.019999 cents.