HolyCityAudio/SpinCAD-Designer

Multiple flags in the Skip instruction will fail

Digital-Larry opened this issue · 0 comments

This could impact people creating their own blocks in the SpinCAD Builder language.

This doesn't happen very often but this time it did.

Working on the Sample and Hold block, I needed to use the instruction:

SKP ZRC | NEG, label

First thing I noticed was that SpinCAD Builder's grammar definition (SpinCAD.xtext) couldn't even accept Or'ed arguments. However, I'd done that before for a different instruction (CHO RDA), so it was pretty easy. By the way, SpinCAD Builder's grammar is essentially a rewrite of the Spin ASM parser with extra features added. And some features not supported (such as inline arithmetic).

Before:

Skip: 'skp' flags = ID ',' label = [Label];

After:

Skip: 'skp' flags = ID ('|' ID)* ',' label = [Label];

Since I'm not really trying to teach Xtext at this moment, I won't explain that, but suffice it to say that my brains were leaking out of my ears for about a month while I figured all that stuff out. Those were the good old days! :?

With that in place, I added the extra flag to what had been simply:

SKP ZRC, label

This approach would skip (thereby sampling the control input) twice per Ramp LFO cycle, as there are two zero crossings. But when I Or'ed in the NEG flag, the simulator output waveform started following the input on every other ramp half-cycle. Turns out the logic in the ElmGen simluator skip instructions are processed as "Or". If one condition OR the other obtains, then the skip takes place. That's not what I wanted. I want ALL conditions to be met and ONLY THEN can we skip.

Here's the original simulate method in the Skip class:

public void simulate(SimulatorState state) {
      boolean skip = false;
      if((flags & ElmProgram.RUN) > 0) {
         if(state.isFirstRun()) {
            skip = false;
         }
         else {
            skip = true;
         }
      }
      else if((flags & ElmProgram.ZRC) > 0) {
         if((state.getACCVal() < 0 && state.getPACCVal() >= 0) ||
               state.getACCVal() >= 0 && state.getPACCVal() < 0) {
            skip = true;
         }
         else {
            skip = false;
         }
      }
      else if((flags & ElmProgram.ZRO) > 0) {
         if(state.getACCVal() == 0) {
            skip = true;
         }
         else {
            skip = false;
         }
      }
      else if((flags & ElmProgram.GEZ) > 0) {
         if(state.getACCVal() > 0) {
            skip = true;
         }
         else {
            skip = false;
         }
      }
      else if((flags & ElmProgram.NEG) > 0) {
         if(state.getACCVal() < 0) {
            skip = true;
         }
         else {
            skip = false;
         }
      }
      if(skip) {
         state.skipInst(nskip);
      }
   }
}

This just goes down the list of flags, and the first one it finds that matches a bit in the supplied flags for the instruction, then that's good enough and away you go. WRONG! BZZZT!

In this particular case (the S/H block), I'm going to do an end-around the multiple flags in SKP instruction simulator bug. As it turns out, if you use the ramp for S/H triggering and trigger on zero crossings, if you "AC couple" the ramp (that is, remove the DC offset) you'll get two equally spaced triggers per LFO cycle. If you adjust the offset up or down a little, you can put some "swing" (asymmetry) into the pulses. So I'm going to take that approach for the time being.