Back when I was working on hardware modules under the brand Microbe Modular, I created a module based on ByteBeat: Sounds generated from equations. The hardware that I was developing for was the Arduino Due.
My first tinkerings with VCV Rack development was to recreate that module. However, I immediately ran into issues. Where the Arduino seemed ambivalent to dividing by zero, or dividing into zero, or dealing with large numbers that rolled over in memory, the compiler used for VCV Rack was not so forgiving. Given the same equations that ran OK on the Arduino, VCV Rack crashed. I spent months exploring and fine tuning the equations for the original hardware module and Iād love to port those to VCV Rack.
Does anyone have any recommendations? Iāve thought about using Lua or some other scripting language, but I worry about the overhead (CPU usage) in that sort of thing. But Iām open minded to any suggestions.
As far as I know divisiĆ³n by zero is not a thing you can do in math so is undefined behavior on c.
When invoking undefined behavior the compiler can do whatever. It may remove the division altogether. Or make the result of the divion 0, which is wrong. It depends on the compiler.
If you want to use the same equations you may have to reverse engeneer what the Arduino compiler does.
While many compilers do that, Section 5.6 of the c+Ā±11 standard (and Andrew uses c++11 flags, although I donāt think this has changed in 14 or 17) says
The binary / operator yields the quotient, and the binary % operator yields the remainder from the division of the first expression by the second. If the second operand of / or % is zero the behavior is undefined.
Which means, alas, a divide by zero could be āanythingā.
If you want divide by or mod by zero to be defined to have a consistent behavior you have to catch them with if before hand alas. Or do something else clever. Looking at the code seems a few of the equations are potentially impacted by this risk.
Thinking a bit more on this: The ARM will give some result for a divide by zero. I donāt know what it is but it would be some representation of nan or inf. You are just consuming that bit wise.
So the way you could make this code portable is to do something like, on your original architecture, with your original compiler, write a program that does (basically)
Uint32_t a, b;
A = 1;
B = 0;
Printf( ā%xā, a/b )
And see the hex code. It will be something. Maybe it is 0xFFBC. Maybe it is something else. Try with a bunch of values and make sure it is consistent.
Then in your formulae where you have something like p1 / t & ( p2 >> 3 ) or what not you have that p1 / t there. If t is 0 you want to replace it with your hex. Otherwise you want to do the divide. But you know that p1 / t will give some value, even if it is undefined. So you could replace p1 / t with something like this
Uint32_t tnonzero = t ? 0xFFFF : 0
Uint32_t armnan = 0xFFBC; // the value you observe in the test
( ~tnonzero & armnan ) | ( tnonzero & ( p1 / t ) )
You get the idea. Basically put your guard inline and do only one if and a known value.
The mods will be a bit trickier. Iām still thinking about a way to do t / t % 10 without a divide. Doesnāt seem like it should need one does it? But this bit wise math is always tricky to be intuitive about.
Also I havenāt run any code and donāt have an arm and am typing this from my ipad and it is free advice, so value this comment accordingly. Good luck!
The integer division instruction div on x86 throws a ādivide errorā CPU exception, and Linux/Mac/Windows catches this and kills the process. So yes, the behavior is defined on x86. The only way around this is to use a function
template <typename T>
T divSafe(T x, T y) {
if (y == 0)
return 0;
return x / y;
}
Good news! By using functions, as Andrew suggested, I have reproduced my first bytebeat from the original module. The reset is going to be cake. I should have a fully functional bytebeat player based on my old Equation Composer done in a matter of days. Woo hoo!