	TITLE	Modules for Modular Sequencer
	NAME	MBB
	.SALL
;==============================================================
; MusicBox Modular Sequencer, Version 2
; modules code
;--------------------------------------------------------------
; author: John Dunn
; date:   03/07/86
; update: 03/20/88
;--------------------------------------------------------------
; COPYRIGHT (C) 1986 John Dunn, All Rights Reserved 
; Entered into the Public Domain, March 20, 1988
;
; Use and copying of this software and preparation of derivative works
; based upon this software are permitted.  Any distribution of this
; software or derivative works must comply with all applicable United
; States export control laws.
; 
; This software is made available AS IS, and the author makes no warranty 
; about the software, its performance, or its conformity to any specification.
; 
; Any person obtaining a copy of this software is requested to send their
; name and address address to:
;
;       John Dunn, Senior Research Fellow
;       Time Arts Inc.
;       3436 Mendocino Ave.
;       Santa Rosa, CA 95401
;
;==============================================================
        include	order.asm
;--------------------------------------------------------------
        include equates.asm
;--------------------------------------------------------------
;_DATA   SEGMENT
;        ASSUME DS:DGROUP, CS:_TEXT
;--------------------------------------------------------------
        extrn   _nextv:near,_nextx:near,_nextl:near,_nextt:near
        extrn   _doit:near
;--------------------------------------------------------------
        extrn   ticka:byte,tickis:byte
        extrn   _header:byte
        extrn   varsav:word,cmdflg:byte,special:word,cmdcnt:byte
        extrn   locsav:word,cmdloc:word,vpage:word,curadr:word
        extrn   valflg:byte
        extrn   @zero:near,magflg:byte,usrflg:byte,holdv:word
        extrn   colr:byte
;--------------------------------------------------------------
        extrn   midisf:byte     ; 0=no sync, 1=send sync
        extrn   midixsf:byte    ; 0=not xtrn sync, NZ=is
        extrn   midiok:byte     ; 0=off/not init, 2=off&init, 3=on
        extrn   mprstf:byte     ; master program reset flag
        extrn   mpmodf:byte     ; master program mode flag
        extrn   mpadrf:byte     ; nz if change in prog address
        extrn   mpadr:byte      ; master program address
        extrn   mmcount:word    ; master measure count
        extrn   mmtick:word     ; ticks left in current measure
        extrn   mmreset:byte    ; master measure reset flag
        extrn   mmstart:byte    ; master measurd start flag
;--------------------------------------------------------------
        extrn   rseed:word      ; random number seed
        extrn   rhold:byte      ; rng hold flag
        extrn   notetbl:byte    ; note -> clocks xlate
        extrn   notes:word
        extrn   interv:byte     ; intervals for modulation
        ;
        extrn   xcax:word       ; register storage, used by xcall
        extrn   xcbx:word
        extrn   xccx:word
        extrn   xcdx:word
        ;
        extrn   temp0:byte      ; temp storage, use within module
        extrn   temp1:byte
        extrn   temp2:byte
        extrn   temp3:byte
;--------------------------------------------------------------
; the following are saved/loaded
;--------------------------------------------------------------
        extrn   mvlsav:abs      ; start of mod values to save
        extrn   mute:word       ; channel mute flags
        extrn   mutef:byte      ; 1=mute, 0=solo flag
        ;
        extrn   mbeat:byte      ; master beats/measure
        extrn   mnote:byte      ; master note value
        extrn   mtempo:byte     ; master tempo
        extrn   mclocks:word    ; master clocks/measure
        ;
        extrn   ctrlmap:byte    ; midi controller map
        extrn   pcva:byte       ; values sent to controller
        extrn   pcvb:byte       ; values sent to controller
        extrn   pcvc:byte       ; values sent to controller
        extrn   pcvd:byte       ; values sent to controller
        extrn   pcve:byte       ; values sent to controller
        extrn   pcvf:byte       ; values sent to controller
        ;
        extrn   mvlnum:abs      ; number of mod values to save
;--------------------------------------------------------------
;_DATA   ENDS
;==============================================================
; Module Execution Code
; All inputs are word pointers to output word values.
; All outputs are binary words, with only the ls byte output.
;==============================================================
; EQUATES for modules
;
vseg    equ 4                   ; offset to video seg addr
vaddr   equ 6                   ; offset to video page addr
outn    equ 8                   ; offset to output
numvar  equ 10                  ; number of input variables
var0    equ 12                  ; offset to variable 0
var1    equ var0+2              ; offset to variable 1
var2    equ var1+2              ; etc.
var3    equ var2+2              ; etc.
var4    equ var3+2              ; etc.
var5    equ var4+2              ; etc.
var6    equ var5+2              ; etc.
var7    equ var6+2              ; etc.
var8    equ var7+2              ; etc.
var9    equ var8+2              ; etc.
var10   equ var9+2              ; etc.
var11   equ var10+2             ; etc.
var12   equ var11+2             ; etc.
var13   equ var12+2             ; etc.
var14   equ var13+2             ; etc.
var15   equ var14+2             ; etc.
var16   equ var15+2             ; etc.
var17   equ var16+2             ; etc.
var18   equ var17+2             ; etc.
var19   equ var18+2             ; etc.
var20   equ var19+2             ; etc.
var21   equ var20+2             ; etc.
var22   equ var21+2             ; etc.
slewc   equ var22               ; programmer slew buffer
slewd   equ slewc+6             ; etc.
slewe   equ slewd+6             ; etc.
slewf   equ slewe+6             ; etc.
pubuf   equ slewf+6             ; programmer undo buffer
psbuf   equ pubuf+13            ; programmer save buffer
        ;
onflg   equ 1                   ; bit mask for note-on
offlg   equ 2                   ; bit mask for note-off
sentf   equ 4                   ; bit mask for note-sent
;--------------------------------------------------------------
_TEXT   SEGMENT
	ASSUME  CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: NOTHING
;--------------------------------------------------------------
        extrn   ticks:word,loops:word,seconds:word,secondf:byte
        extrn   todec:near,tonote:near,startm:near
        extrn   _cancel:near,workx:near,split:near
        extrn   turnon:near,turnoff:near
        extrn   mstart:byte,mstop:byte,mcont:byte
        extrn   allmidi:near
;==============================================================

;==============================================================
; THE MODULES
;==============================================================
        include	macros.asm
;==============================================================
; generate melody & chord changes from input base & intervals
;--------------------------------------------------------------
; inputs:
;    0:   chord array location (16 possible arrays of 4 values)
;    1:   base input value
;    2:   1st interval above base
;    3:   2nd interval above base
;    4:   3rd interval above base
;    5:   nz = normalize to octave
;    6:   offset to base
;    7:   nz = sort
;    8:   foldover range
;    9:   doit strobe
;    10x: clock ticks
; output: root # (always 0 unless sort)
;--------------------------------------------------------------
        public _cgen 
_cgen:
        tick    var9,var10              ; clock strobe
        jc      cgen0                   ; yes, go do it
        jmp     cgenz                   ; exit if no tick
        ;
cgen0:  getv    al,var0                 ; get array location
        and     ax,15                   ; mask
        add     ax,ax                   ; *4
        add     ax,ax                   ; /
        add     ax,offset dgroup:interv ; add in int table addr
        mov     bp,ax                   ; save in bp
        ;       
        getv    al,var1                 ; get input
        testv   var5,-1                 ; want to normalize?
        jz      cgen1                   ; no, branch
        normal                          ; yes, normalize to octave
        mov     al,ah                   ; put back in al
        ;
cgen1:  getv    ah,var6                 ; get offset
        add     al,ah                   ; add to root
        ;
        getv    ah,var2                 ; add intervals to root
        add     ah,al                   ;       /
        getv    dl,var3                 ;      /
        add     dl,al                   ;     /
        getv    dh,var4                 ;    /
        add     dh,al                   ;   /
        ;
        mov     byte ptr outn[di],0     ; root is 0
        testv   var7,-1                 ; want to sort
        jnz     cgen4                   ; yes, do it
        jmp     cgenx                   ; no, exit
        ;
cgen4:  push    ax                      ; save root
        getv    al,var8                 ; get max octave range
        and     al,15                   ; limit
        mov     ch,12                   ;   /
        mul     ch                      ;  /
        add     ch,al                   ; ch = range
        pop     ax                      ; restore root
        ;
        range   al,ch                   ; put in range
        range   ah,ch                   ; put in range
        range   dl,ch                   ; put in range 
        range   dh,ch                   ; put in range 
        ;
        mov     cl,al                   ; save root in cl
        sort4                           ; sort
        mov     ch,0                    ; look for root
        cmp     al,cl                   ;         /
        jz      cgen3                   ;        /
        inc     ch                      ;       /
        cmp     ah,cl                   ;      /
        jz      cgen3                   ;     /
        inc     ch                      ;    /
        cmp     dl,cl                   ;   /
        jz      cgen3                   ;  /
        inc     ch                      ; /
cgen3:  mov     byte ptr outn[di],ch    ; send root # to output
        ;
cgenx:  mov     ds:[bp],ax              ; set chord values
        mov     ds:2[bp],dx             ; /
cgenz:  nextv                           ; exit
;==============================================================
; set new chords table data from 4 inputs
;--------------------------------------------------------------
; inputs:
;    0:   chord array location (16 possible arrays of 4 values)
;    1:   root input
;    2:   1st interval input
;    3:   2nd interval input
;    4:   3rd interval input
;    5:   nz = normalize to octave
;    6:   offset to base
;    7:   nz = sort
;    8:   foldover range
;    9:   doit strobe
;    10x: clock ticks
; output: root # (always 0 unless sort)
;--------------------------------------------------------------
        public _cmak 
_cmak:
        tick    var9,var10              ; clock strobe
        jc      cmak0                   ; yes, go do it
        jmp     cmakz                   ; exit if no tick
        ;
cmak0:  getv    al,var0                 ; get array location
        and     ax,15                   ; mask
        add     ax,ax                   ; *4
        add     ax,ax                   ; /
        add     ax,offset dgroup:interv ; add in int table addr
        mov     bp,ax                   ; save in bp
        ;       
        getv    cl,var6                 ; get offset
        getv    al,var1                 ; get inputs
        add     al,cl                   ; add offset
        getv    ah,var2                 ;      /
        add     ah,cl                   ;     /
        getv    dl,var3                 ;    /
        add     dl,cl                   ;   /
        getv    dh,var4                 ;  /
        add     dh,cl                   ; /
        ;
        testv   var5,-1                 ; want to normalize?
        jz      cmak1                   ; no, branch
        mov     cx,ax                   ; yes, save ax
        mov     bx,dx                   ; save dx
        normal                          ; normalize
        mov     cl,ah                   ;          /
        mov     al,ch                   ;         /
        normal                          ;        /
        mov     ch,ah                   ;       /
        mov     al,bl                   ;      /
        normal                          ;     /
        mov     bl,ah                   ;    /
        mov     al,bh                   ;   /
        normal                          ;  /
        mov     bh,ah                   ; /
        mov     ax,cx                   ; restore to ax,dx
        mov     dx,bx                   ; /
        ;
cmak1:  mov     byte ptr outn[di],0     ; root is 0
        testv   var7,-1                 ; want to sort
        jnz     cmak4                   ; yes, do it
        jmp     cmakx                   ; no, exit
        ;
cmak4:  push    ax                      ; save root
        getv    al,var8                 ; get max octave range
        and     al,15                   ; limit
        mov     ch,12                   ;   /
        mul     ch                      ;  /
        add     ch,al                   ; ch = range
        pop     ax                      ; restore root
        ;
        range   al,ch                   ; put in range
        range   ah,ch                   ; put in range
        range   dl,ch                   ; put in range 
        range   dh,ch                   ; put in range 
        ;
        mov     cl,al                   ; save root in cl
        sort4                           ; sort
        mov     ch,0                    ; look for root
        cmp     al,cl                   ;         /
        jz      cmak3                   ;        /
        inc     ch                      ;       /
        cmp     ah,cl                   ;      /
        jz      cmak3                   ;     /
        inc     ch                      ;    /
        cmp     dl,cl                   ;   /
        jz      cmak3                   ;  /
        inc     ch                      ; /
cmak3:  mov     byte ptr outn[di],ch    ; send root # to output
        ;
cmakx:  mov     ds:[bp],ax              ; set chord values
        mov     ds:2[bp],dx             ; /
cmakz:  nextv                           ; exit
;==============================================================
; select interval from modulated interval table
; inputs:
;    0:   clock
;    1:   location
;    2:   interval step
;    3:   nz = sort
;    4:   offset
;    5x:  clock tick
; output: note interval
;--------------------------------------------------------------
        public _cplay,_cplai
_cplai: nop                             ; 2 sets of modules
_cplay: tick    var0,var5               ; clock
        jnc     cplayz                  ; branch if no tick
        getv    cl,var1                 ; get interval address
        and     cx,15                   ; mask
        add     cx,cx                   ; *4
        add     cx,cx                   ; /
        add     cx,offset dgroup:interv ; cx = interval address
        ;
        testv   var3,-1                 ; wanna sort?   
        jz      cplay1                  ; no, branch
        ;
        mov     bx,cx                   ; pick up intervals
        mov     ax,[bx]                 ;  /
        mov     dx,2[bx]                ; /
        sort4                           ; sort them
        mov     word ptr temp0,ax       ; put in temp array
        mov     word ptr temp2,dx       ; /
        mov     cx,offset dgroup:temp0  ; cx = sorted interval addr
        ;
cplay1: getv    bl,var2                 ; get interval number
        and     bx,3                    ; mask
        add     bx,cx                   ; bx = table index
        mov     al,[bx]                 ; get the interval
        ;
        getv    ah,var4                 ; get offset
        add     al,ah                   ; add offset to interval
        putn    al                      ; send it out
cplayz: nextv                           ; exit
;==============================================================
; store value to interval table
; inputs:
;    0:   chord array location (16 possible arrays of 4 values)
;    1:   location in array
;    2:   base input value
;    3:   nz = normalize to octave
;    4:   offset to base
;    5:   doit strobe
;    6x:  clock tick 
;
; output: previous value at address
;--------------------------------------------------------------
        public _cstor
_cstor: tick    var5,var6               ; strobe?
        jnc     cstorz                  ; no, exit
        ;
        getv    al,var2                 ; get input
        testv   var3,-1                 ; want to normalize?
        jz      cstor1                  ; no, branch
        normal                          ; yes, normalize to octave
        mov     al,ah                   ; put back in al
        ;
cstor1: getv    ah,var4                 ; get offset
        add     al,ah                   ; add to input
        ;
        getv    cl,var0                 ; get interval address
        and     cx,15                   ; mask
        add     cx,cx                   ; *4
        add     cx,cx                   ; /
        add     cx,offset dgroup:interv ; cx = interval address
        ;
        getv    bl,var1                 ; get interval number
        and     bx,3                    ; mask
        add     bx,cx                   ; bx = table index
        ;
        xchg    al,[bx]                 ; swap old with new
        putn    al                      ; send old to output
cstorz: nextv                           ; exit
;==============================================================
; generate bass note line, dithered input a,b
; inputs:
;    0:   clock
;    1:   nz = hold
;    2:   strobe center to output
;    3:   center
;    4:   max distance from center
;    5:   nz = use -, z = use +
;    6:   input "a"
;    7:   input "b"
;    8x:  
;    8z:  clock tick, init flag
; output: new bass note
;--------------------------------------------------------------
        public _bassd 
_bassd:
        tickb1  var2,var8+1             ; strobe clock?
        jnc     bassd4                  ; branch if no strobe
        and     byte ptr var8+1[di],0fbh; yes, flag/auto initialize
bassd4: tick    var0,var8+1             ; wait for clock tick
        jnc     bassdz                  ; exit if not clock
        hold    var1                    ; hold?
        jnz     bassdz                  ; exit if hold
        ;
        test    byte ptr var8+1[di],4   ; was flag set?
        jnz     bassd1                  ; no, branch
        or      byte ptr var8+1[di],4   ; yes, clear flag
bassd0: getv    al,var3                 ; get the input
        putn    al                      ; send it to output
        jmp     short bassdz            ; exit
        ;
bassd1: getv    al,var6                 ; load for "a"
        testv   var5,-1                 ; test for +/-
        jz      bassd2                  ; branch if +
        getv    al,var7                 ; load for "b"
        neg     al                      ; make "b" -
bassd2: add     outn[di],al             ; add the offset
        ;
        getv    al,var3                 ; get center
        getv    ah,var4                 ; get max
        mov     dl,al                   ; test for upper limit
        add     dl,ah                   ;  /
        cmp     dl,outn[di]             ; /
        jb      bassd0                  ; reset if above limit
        sub     al,ah                   ; test for lower limit
        cmp     outn[di],al             ; /
        jb      bassd0                  ; reset if below limit
bassdz: nextv                           ; exit
;==============================================================
; Bit Formatter
; inputs: 1 - 8: bit set inputs, zero clrs, nz sets
; output: byte formatted from the 8 inputs
;--------------------------------------------------------------
        public _gout
_gout:  
        gettag                  ; es:bx = screen addr of tag
        dec     bx              ; point to leds
        add     bx,480          ; point to the first bit led
        mov     ax,bx           ; ax = screen address pointer
        mov     bp,var0         ; bp = addr of first variable
        mov     dx,100H         ; dl=0, dh=1
        mov     cx,8            ; gonna do 8 bits
gout1:  mov     bx,ds:[bp+di]   ; get the variable address
        cmp     byte ptr [bx],0 ; test for zero/not zero
        mov     bx,ax           ; get screen address
        jnz     gout2           ; branch if not zero
        mov     byte ptr es:[bx],yellow; turn off the led
        jmp     short gout3     ; branch to finish up
gout2:  mov     byte ptr es:[bx],hi+red; turn on the led
        or      dl,dh           ; set the bit
gout3:  shl     dh,1            ; bump bit flag
        add     ax,160          ; bump screen addr 1 line
        add     bp,2            ; bump variable pointer
        loop    gout1           ; loop til done
        putn    dl              ; set the new byte value
        nextv                   ; exit w screen update
;==============================================================
; Display bits in a byte as gate lites
; inputs: 0: byte 
; output: none
;--------------------------------------------------------------
        public _gatel
_gatel: 
        getv    al,var0         ; get the byte
        gettag                  ; es:bx = screen addr of tag
        dec     bx              ; point to leds
        add     bx,480          ; point to the first bit led
        mov     cx,8            ; 8 bits to do
gatel1: shr     al,1            ; bit --> cy
        jc      gatel2          ; branch if the bit is hi
        and     byte ptr es:[bx],7; turn the led off
        jmp     short gatel3    ; branch
gatel2: or      byte ptr es:[bx],8; turn the led on
gatel3: add     bx,160          ; go down 1 line
        loop    gatel1          ; do all 8 bits
        nextx                   ; exit quiet

;==============================================================
; Display values as an indicator bar
; linear display
; inputs: 0: left row, 1: right 2: scale
;         3x: left save, 3z: right save
; output: none
;--------------------------------------------------------------
        public _imeter
_imeter:
        getv    al,var0         ; al = left value
        getv    dl,var1         ; dl = right value
        getv    dh,var2         ; dh = scale
        or      dh,dh           ; scale = 0?
        jnz     imeter0         ; no, branch
        inc     dh              ; yes, start with 1
imeter0:mov     es,4[di]        ; get seg addr
        mov     bx,6[di]        ; get module screen address
        add     bx,160*5        ; point to top row of leds
        ;
        inc     bx              ; point to left leds
        cmp     al,var3[di]     ; has left changed
        jz      imeter4         ; no, branch
        mov     var3[di],al     ; yes, save new value
        push    dx              ; save right value & scale
        or      al,al           ; al = 0 ?
        jz      imeter7         ; yes, don't divide
        mov     ah,0            ; ax = left value
        div     dh              ; al = ax/dl
imeter7:call    imeter1         ; show the new value
        pop     dx              ; restore right value & scale
imeter4:add     bx,2            ; point to right column
        cmp     dl,var3+1[di]   ; did middle change
        jz      imeter6         ; no, branch
        mov     var3+1[di],dl   ; yes, save the new value
        mov     al,dl           ; al = right value
        or      al,al           ; al = 0 ?
        jz      imeter8         ; yes, don't divide
        mov     ah,0            ; ax = left value
        div     dh              ; al = ax/dl
imeter8:call    imeter1         ; show the new value
imeter6:nextx                   ; exit quiet

;--------------------------------------------------------------
; convert linear 0-8 to log bit position
; then jump to emeter1 to display
; call with al = value 
;
imeter1:test    al,0f8h         ; if > 7, max out
        jz      imeter2         ;   /
        mov     dl,-1           ;  /
        jmp     short imeter3   ; /
imeter2:mov     cl,al           ; else convert
        xor     al,al           ;    /
        stc                     ;   /
        rcl     al,cl           ;  /
        mov     dl,al           ; /
imeter3:jmp     emeter1         ; display

;==============================================================
; Display values as an indicator bar
; inputs: 0: left row, 1: right 2: scale
;         3x: left save, 3z: right save
; output: none
;--------------------------------------------------------------
        public _emeter
_emeter:
        getv    al,var0         ; al = left value
        getv    dl,var1         ; dl = right value
        getv    dh,var2         ; dh = multiplier
        or      dh,dh           ; mult = 0?
        jnz     emeter0         ; no, branch
        inc     dh              ; yes, start with 1
emeter0:mov     es,4[di]        ; get seg addr
        mov     bx,6[di]        ; get module screen address
        add     bx,160*5        ; point to top row of leds
        ;
        inc     bx              ; point to left leds
        cmp     al,var3[di]     ; has left changed
        jz      emeter4         ; no, branch
        mov     var3[di],al     ; yes, save new value
        push    dx              ; save right value & mult
        mul     dh              ; ax = bumped right value
        or      ah,ah           ; > 255 ?
        jz      emeter5         ; no, branch
        mov     al,255          ; yes, make it 255
emeter5:mov     dl,al           ; set up for display
        call    emeter1         ; show the new value
        pop     dx              ; restore right value & scale
emeter4:add     bx,2            ; point to right column
        mov     al,dl           ; al = right value
        cmp     al,var3+1[di]   ; did right change
        jz      emeter6         ; no, branch
        mov     var3+1[di],al   ; yes, save the new value
        mul     dh              ; ax = bumped right value
        or      ah,ah           ; > 255 ?
        jz      emeter7         ; no, branch
        mov     al,255          ; yes, make it 255
emeter7:mov     dl,al           ; set up for display
        call    emeter1         ; show the new value
emeter6:nextx                   ; exit quiet

;--------------------------------------------------------------
; subroutine to show a column of leds
; call with BX = address of top of column (preserved), DL = value
; display is log, sets top led high and all following if
; bit 7 is set, else sets top led low, then top-1 if bit 6, etc.
;
emeter1:push    bx              ; preserve bx
        mov     dh,0            ; use dh as flag byte
        mov     cx,8            ; 8 leds in the column
emeterl:shl     dx,1            ; bump hi bit of dl into dh
        test    dh,-1           ; on or off?
        jz      emeter2         ; branch if off
        or      byte ptr es:[bx],8    ; make bright
        jmp     short emeter3         ; duck
emeter2:and     byte ptr es:[bx],0f7h ; or make dark  
emeter3:add     bx,160          ; next line
        loop    emeterl         ; loop til done 
        pop     bx              ; restore bx
        ret                     ; exit
;==============================================================
; Set output to approximately the same as input, within limits
; inputs: 0: input 1: upper limit, 2: lower limit, 
;         3x: last input
; output: scaled random number
;--------------------------------------------------------------
        public _apprx
_apprx: 
        getv    dl,var0         ; get the value
        test    rhold,-1        ; branch if alpha seed
        jnz     appr1           ; /
        cmp     dl,var3[di]     ; same as last?
        jnz     appr1           ; no, go to work
        nextx                   ; yes, easy exit
        ;
appr1:  mov     var3[di],dl     ; set new value
        random
        getv    al,var1         ; get upper offset
        inc     al              ; fudge
        add     al,dl           ; add in value
        mov     ah,0            ; msb = 0
        getv    bl,var2         ; get lower offset
        sub     dl,bl           ; subtract from value
        mov     bl,dl           ; keep it in bl
        mov     bh,0            ; msb = 0
        sub     ax,bx           ; ax = upper - lower
        mul     cx              ; dx = appr * (upper-lower)/10000H
        add     dx,bx           ; dx = above + lower 
        putn    dl              ; send it out
        nextv                   ; exit with video update
;==============================================================
; Generate random numbers
; inputs: 0: clock 1: hold 2: seed value, 3: range, 4: offset, 
;         5: seed, 6x: saved value,   6z: clock-tick
; output: random number
;--------------------------------------------------------------
        public _rand
_rand:
        mov     ah,var6[di]     ; get last value
        ;
        getv    al,var1         ; local hold?
        or      al,rhold        ; global rand hold?
        jz      rand0           ; no, branch
rand2:  getv    al,var2         ; yes, reset seed
        mov     ah,al           ;  /
        mov     var5[di],ax     ; /
        jmp     rand1a          ; go out
        ;
rand0:  test    mprstf,-1       ; master hold?
        jnz     rand1           ; yes, branch
        ;
        tick    var0,var6+1     ; clock tick
        jnc     rand1           ; branch if no tick
        ;
        mov     cx,var5[di]     ;; get next raw seed
        add     cx,9248H        ;;     /
        ror     cx,1            ;;    /
        ror     cx,1            ;;   /
        ror     cx,1            ;;  /
        mov     var5[di],cx     ;; /
        ;
        getv    al,var3         ; get range
        mul     cl              ; ah = rand * range /256
rand1a: mov     var6[di],ah     ; put it away
rand1:  getv    al,var4         ; get offset
        add     al,ah           ; put them together
        putn    al              ; send it out
        nextv                   ; exit
;==============================================================
; Non-repeating random pattern
; inputs: 0: clock 1: hold  2: seed value, 3: inc/dec, 4: range
;         5x: saved value, 5z: clock-tick, 6: address pointer
; output: random number
;--------------------------------------------------------------
        public _rrand
_rrand:
        getv    cl,var4         ; get range
        cmp     cl,1            ; check for 0,1
        jna     rrand0          ; split if 0
        mov     dx,1            ; for inc/dec
        testv   var3,-1         ; dec if nz
        jz      rrand03         ;  /
        mov     dx,-1           ; /
        ;
rrand03:
        getv    al,var1         ; local hold?
        or      al,rhold        ; global rand hold?
        jz      rrand01         ; no, branch
rrand2: getv    al,var2         ; yes, get seed
        putn    al              ; send it out
        mov     ah,64           ;   /
        mul     ah              ;  /
        mov     var6[di],ax     ; /
        jmp     rrandx          ; split
        ;
rrand01:test    mprstf,-1       ; master hold?
        jnz     rrand0          ; yes, branch
        ;
        tick    var0,var5+1     ; clock tick
        jc      rrand1          ; branch if tick
rrand0: nextx                   ; else just split
        ;
rrand1: add     var6[di],dx     ; bump pointer
rrandz: mov     bx,var6[di]     ; get it
        and     bx,3fffh        ; keep in code range
        add     bx,offset _doit ; start of code
        mov     ax,cs:[bx]      ; get the byte
        add     al,ah           ; fill it out
        mul     cl              ; ah = rand * range /256
        cmp     ah,var5[di]     ; same as last?
        jz      rrand1          ; yes, do it again
        mov     var5[di],ah     ; no, save it
        putn    ah              ; send it out
rrandx: nextv                   ; exit
;==============================================================
; Random strobe generator
; output: 50% strobe
; input: 0: strobe, 1x: clock tick
;--------------------------------------------------------------
        public _randp
_randp:
        mov     cl,0            ; setup for hold
        test    mprstf,-1       ; check alpha hold
        jnz     randpx          ; yes, exit
        tick    var0,var1       ; clock input
        jnc     randpz          ; exit if no clock
        random                  ; get random value
        and     cl,1            ; isolate lsb
randpx: putn    cl              ; send it our
randpz: nextl                   ; show it
;==============================================================
; Random strobe generator
; output: clock strobe
; input: 0: reset, 1: count, 2: offset 3: dither
;        4x:countdown timer, 4z: copy of count
;--------------------------------------------------------------
        public _nrand
_nrand: testv   var1,-1         ; clock = 0
        jz      nrandz          ; yes, just exit
        hold    var0            ; hold?
        jz      nrand0          ; no, branch
        mov     al,byte ptr cs:loops; get count
        and     al,1            ; /
        mov     var4[di],al     ; reset clock counters
        jmp     short nrandz    ; split
        ;
nrand0: getv    ah,var1         ; get current count
        cmp     ah,var4+1[di]   ; same as before?
        jz      nrand1          ; yes, branch
        cmp     ah,1            ; set to 1?
        jnz     nrand3          ; no, branch
        mov     ah,2            ; yes, set to 2
nrand3: mov     al,byte ptr cs:loops; get count
        and     al,1            ; /
        mov     var4[di],ax     ; /
        ;
nrand1: getv    al,var2         ; get offset trigger
        cmp     al,ah           ; out of range?
        jb      nrand4          ; no, branch
        mov     al,0            ; yes, zip it
nrand4: cmp     al,var4[di]     ; same as count?
        jnz     nrand2          ; no, branch
        random                  ; yes, get random value
        getv    ch,var3         ; get ddithr value
        sub     ch,cl           ; above threshold?
        jna     nrand2          ; no, branch
        mov     byte ptr outn[di],1; else output flag
        ;
nrand2: inc     byte ptr var4[di]; tick
        cmp     ah,var4[di]     ; reached count?
        jnz     nrandz          ; no, exit
        mov     byte ptr var4[di],0; yes, reset timer
nrandz: nextt                   ; exit
;==============================================================
; Random slope output
; inputs 0: count,    1: range,  
;        2x: target,  2+:
;        3x: current, 3+: +/-
;        4x: clock,   4+: old clock    
;
; ouput  last +/- 1
;--------------------------------------------------------------
        public _variz,_variy
_variy: nop
_variz:
        getv    dh,var1                 ; get range input
        getv    al,var0                 ; get clock
        ;
        test    dh,80H                  ; test msb of range for reset
        jnz     varizz                  ; go reset if high
        ;
        test    mprstf,-1               ; check for master reset
        jnz     varizz                  ; reset if high
        ;
        test    rhold,-1                ; check for random reset
        jnz     varizz                  ; reset if high
        ;
        cmp     al,var4+1[di]           ; clock same as before
        jz      variz0                  ; yes, go to work
        jmp     varizk                  ; no, fix clock
;--------------------------------------------------------------
varizz: and     dh,7fh                  ; strip msb
        putn    dh                      ; send it out
        mov     var2[di],dh             ; set as target
        mov     byte ptr var3+1[di],1   ; set to count up
        shr     dh,1                    ; current is range/2
        shr     dh,1                    ; current is range/2
        mov     var3[di],dh             ;  /
        ;
varizk: mov     var4+1[di],al           ; save clock
        mov     byte ptr var4[di],1     ; new one next clock
        nextv                           ; split
;--------------------------------------------------------------
varizzx:nextx                           ; split
variz0: dec     byte ptr var4[di]       ; bump clock
        jnz     varizzx                 ; exit if not 0
        getv    al,var0                 ; else get it
        mov     var4+1[di],al           ; save clock
        inc     al                      ; bump
        mov     var4[di],al             ; put away
;--------------------------------------------------------------
        mov     dl,var3[di]             ; get current value
        add     dl,var3+1[di]           ; add bump
        mov     var3[di],dl             ; put it away
        cmp     dl,var2[di]             ; reached target?
        jnz     variz2                  ; no, branch
        ;
variz1: random                          ; get new rand
        mov     al,dh                   ; get range
        test    ch,0E0H                 ; 3/4 of the time
        jz      variz1a                 ; ... use it straight
        rcr     cl,1                    ; ... else cut 1/2
        jnc     variz1a                 ; ... and maybe more
        shr     cl,1                    ; ... cut again
variz1a:mul     cl                      ; ah = rand * range /256
        ;
        mov     byte ptr var3+1[di],0ffh; set up for inc
        cmp     ah,var2[di]             ; is it < target?
        jb      variz3                  ; yes,is <, exit
        mov     byte ptr var3+1[di],1   ; no, set up for dec
        jnz     variz3                  ; exit if <>
        inc     ah                      ; else bump
variz3:
        mov     var2[di],ah             ; save new target
variz2:
        shr     dh,1                    ; calc offset
        shr     dh,1                    ; calc offset
        add     dl,dh                   ; add in count
        jns     variz2a                 ; branch if <= 7fh
        mov     dl,7fh                  ; else make 7fh
variz2a:putn    dl                      ; send it out
        nextv                           ; exit
;==============================================================
; Random slope output
; inputs 0: clock 1: hold, 2: range 3: offset
;        4x: +/-  4+: clock tick, 5x: target, 5+: current
; ouput  last +/- 1
;--------------------------------------------------------------
        public _variv
_variv:
        test    rhold,-1                ; check for random reset
        jnz     variv00                 ; reset if high
        ;
        hold    var1                    ; hold?
        jz      variv0                  ; no, branch
variv00:mov     ah,var5+1[di]           ; yes, get current
        getv    al,var3                 ; get offset
        add     al,ah                   ; put them together
        putn    al                      ; send it   
        ;
        inc     ah                      ; bump current
        mov     var5[di],ah             ; set up target
        mov     byte ptr var4[di],1     ; set up counter
        nextv                           ; exit
;--------------------------------------------------------------
variv0:
        tick    var0,var4+1             ; clock tick
        jc      variv1                  ; branch if tick
        nextx                           ; else just exit
;--------------------------------------------------------------
variv1: mov     dl,var5+1[di]           ; get current value
        add     dl,var4[di]             ; add bump
        mov     var5+1[di],dl           ; put it away
        cmp     dl,var5[di]             ; reached target?
        jnz     variv2                  ; no, branch
        ;
        random                          ; else get new rand
        getv    al,var2                 ; get range
        mul     cl                      ; ah = rand * range /256
        ;
        mov     byte ptr var4[di],-1    ; set up for inc
        cmp     ah,var5[di]             ; is it < target?
        jb      variv3                  ; yes,is <, exit
        mov     byte ptr var4[di],1     ; no, set up for dec
        jnz     variv3                  ; exit if <>
        inc     ah                      ; else bump
variv3:
        mov     var5[di],ah             ; save new target
variv2:
        getv    al,var3                 ; get offset
        add     al,dl                   ; add current
        putn    al                      ; send it out
        nextv                           ; exit
;==============================================================
; Random slope output
; inputs 0:  clock 
;        1:  hold
;        2:  addamt
;        3:  range 
;        4:  offset
;        5x: target
;        6x: count
;        6+: b7=sign, b0=clock tick
;
; output: count+offset
;--------------------------------------------------------------
        public _varix
_varix:
        test    rhold,-1                ; check for random reset
        jnz     varix00                 ; reset if high
        ;
        hold    var1                    ; hold?
        jz      varix0                  ; no, branch
varix00:mov     byte ptr var6[di],0     ; yes count = 0
        mov     byte ptr var5[di],0     ; target = 1
        and     byte ptr var6+1[di],7FH ; set sign bit
varixz: jmp     varixx                  ; exit w count+offset
;--------------------------------------------------------------
varix0:
        tick    var0,var6+1             ; clock tick
        jc      varix1                  ; branch if tick
        nextx                           ; else just exit
;--------------------------------------------------------------
varix1: getv    al,var2                 ; get add ammt
        getv    ah,var3                 ; is add amb > range
        cmp     al,ah                   ; add amt - range
        jnb     varixz                  ; yes, just exit
        ;       
        mov     ah,0                    ; ax = add amt
        mov     dl,var6[di]             ; get count
        mov     dh,0                    ; dx = count
        ;
        test    byte ptr var6+1[di],80H ; sign = count down?
        jnz     varix1a                 ; yes, branch
        ;
        add     dx,ax                   ; dx = count + add amt
        mov     al,var5[di]             ; get target
        mov     ah,0                    ; ax = target
        cmp     ax,dx                   ; target - new count
        jnb     varix4                  ; send if target > nc
        jmp     varix2                  ; else get a new one
;--------------------------------------------------------------
varix1a:sub     dx,ax                   ; dx = count - add amt
        jnc     varix1b                 ; make 0 if underflow
        mov     dx,0                    ; /
varix1b:mov     al,var5[di]             ; get target
        mov     ah,0                    ; ax = target
        cmp     ax,dx                   ; target - new count
        jna     varix4                  ; send if target < nc
;       jmp     varix2                  ; else get a new one
;--------------------------------------------------------------
varix2: random                          ; else get new rand
        getv    al,var3                 ; get range
        or      al,al                   ; exit if range=0
        jz      varixx                  ; /
        mul     cl                      ; ah = rand * range /256
        or      ah,ah                   ; don't want 0
        jz      varix2                  ; /  
        xchg    var5[di],ah             ; save new target
        mov     var6[di],ah             ; set count to old target
        cmp     ah,var5[di]             ; old - new
        ja      varix2a                 ; old >  new, go count down
        ;
        and     byte ptr var6+1[di],7FH ; clear sign
        getv    al,var2                 ; get add ammt
        mov     ah,0                    ; ax = add amt
        mov     dl,var6[di]             ; get count
        mov     dh,0                    ; dx = count
        add     dx,ax                   ; dx = count + add amt
        jmp     varix4                  ; go with dl = new count
        ;
varix2a:or      byte ptr var6+1[di],80H ; set sign
        getv    al,var2                 ; get add ammt
        mov     ah,0                    ; ax = add amt
        mov     dl,var6[di]             ; get count
        mov     dh,0                    ; dx = count
        sub     dx,ax                   ; dx = count - add amt
        jnc     varix4                  ; make 0 if underflow
        mov     dl,0                    ; /
;--------------------------------------------------------------
varix4: mov     var6[di],dl             ; save new count
varixx: getv    al,var4                 ; get offset
        add     al,var6[di]             ; add count
        putn    al                      ; send it out
        nextv                           ; exit
;==============================================================
; Generate a random tone row of 0-11
; inputs: 0:  address
;         1:  strobe
;         2x: tick
;         3-9:tone row table
; output: addressed value of tone row (0-11)
;--------------------------------------------------------------
        public _trand
_trand: 
        test    rhold,-1        ; check for random reset
        jnz     trand1          ; branch if high
        ;
        tick    var1,var2       ; clock tick
        jnc     trand1          ; branch if no tick
        ;
        mov     cx,12           ; 12 to do
        mov     bp,0            ; set up base
trand3: mov     bx,0            ; set up index
        test    rhold,-1        ; holding?
        jz      trand7          ; no, get rand
        mov     dx,bp           ; yes, do 0-1
        jmp     short trand2    ; get on with it
        ;
trand7: randdx                  ; get rng
        and     dl,15           ; 0-15 allowed
        cmp     dl,12           ; really 0-11
        jb      trand2          ; branch if ok
        shr     dl,1            ; else /2
        ;
trand2: cmp     bx,bp           ; at the target?
        jnz     trand4          ; no, branch
        mov     var3[bx+di],dl  ; yes, put the byte away
trand6: inc     bp              ; bump index
        loop    trand3          ; do all 12
        jmp     short trand1    ; exit when done
        ;
trand4: cmp     var3[bx+di],dl  ; already got?
        jz      trand3          ; yes, try again
        inc     bx              ; no, look at next
        jmp     short trand2    ; /
        ;
trand1: getv    bl,var0         ; get address
        mov     bh,0            ; make into word
        cmp     bl,12           ; keep within range
        jb      trand0          ; branch if ok
        ;
        mov     ax,bx           ; else mod 12
        mov     dl,12           ;   /
        div     dl              ;  /
        mov     bl,ah           ; /
        ;
trand0: mov     al,var3[bx+di]  ; pick up the byte
        putn    al              ; send it out
        nextv                   ; exit
;==============================================================
; Generate raw binary odds
; inputs: 0: clock  1x: clock-tick
; output: 0-7; 0 @ 1:2, 1 @ 1:4, ... 7 @ 1:256
;--------------------------------------------------------------
        public _oddz 
_oddz:  
        tick    var0,var1       ; clock tick
        jc      oddz1           ; branch if there is a tick
        nextx                   ; else exit easy
oddz1:  random
        mov     al,0            ; output is 0-7
        rol     cl,1            ; test msb
        jc      oddzx           ; branch if set
        inc     al              ; else bump value
        rol     cl,1            ; test msb
        jc      oddzx           ; branch if set
        inc     al              ; else bump value
        rol     cl,1            ; test msb
        jc      oddzx           ; branch if set
        inc     al              ; else bump value
        rol     cl,1            ; test msb
        jc      oddzx           ; branch if set
        inc     al              ; else bump value
        rol     cl,1            ; test msb
        jc      oddzx           ; branch if set
        inc     al              ; else bump value
        rol     cl,1            ; test msb
        jc      oddzx           ; branch if set
        inc     al              ; else bump value
        rol     cl,1            ; test msb
        jc      oddzx           ; branch if set
        inc     al              ; else bump value
        rol     cl,1            ; test msb
        jc      oddzx           ; branch if set
        xor     al,al           ; else clr value
oddzx:  putn    al              ; send it out
        nextv                   ; exit
;==============================================================
; dither between 4 inputs
; inputs: 0: clock, 1-4: inputs, 5: offset, 6x: clock
; output: one of the 4 inputs
;--------------------------------------------------------------
        public _odds
_odds:  
        test    rhold,-1        ; branch if alpha seed
        jnz     odds1           ; /
        tick    var0,var6       ; clock tick
        jc      odds1           ; branch if there is a tick
oddsz:  nextx                   ; else exit easy
odds1:  random
        test    cl,80H          ; 1:2 is input1
        jz      odds2           ;     /
        getv    al,var1         ;   /
        jmp     short oddsx     ; /
odds2:  test    cl,40H          ; 1:4 is input2
        jz      odds3           ;     /
        getv    al,var2         ;   /
        jmp     short oddsx     ; /
odds3:  test    cl,20H          ; 1:8 is input3
        jz      odds4           ;     /
        getv    al,var3         ;   /
        jmp     short oddsx     ; /
odds4:  test    cl,10H          ; 1:16 is input4
        jz      oddsz           ; else no change
        getv    al,var4         ; /
        ;
oddsx:  getv    ah,var5         ; get the offset
        add     al,ah           ; add to input
        putn    al              ; send it out
        nextv                   ; exit with video update
;==============================================================
; dither between 2 inputs
; inputs: 0: clock, 1: dither, 2,3 inputs, 4: offset, 5x: clock
; output: one of the 2 inputs
;--------------------------------------------------------------
        public _dither
_dither:
        test    rhold,-1        ; branch if alpha seed
        jnz     dither1         ; /
        tick    var0,var5       ; clock tick
        jc      dither1         ; branch if there is a tick
ditherz:nextx                   ; else exit easy
dither1:random
        ;
dither2:getv    ch,var1         ; get dither value
        sub     ch,cl           ; above threshold?
        jna     dither3         ; no, branch
        getv    al,var3         ; yes, get "b" input
        jmp     short ditherx   ; split
dither3:getv    al,var2         ; no, get "a" input
ditherx:getv    ah,var4         ; get the offset
        add     al,ah           ; add to input
        putn    al              ; send it out
        nextv                   ; exit with video update
;==============================================================
; dither between and input and 0
; inputs: 0: clock, 1: dither, 2: input, 3: offset, 4x: clock
; output: the input or 0
;--------------------------------------------------------------
        public _dithzr
_dithzr:
        test    rhold,-1        ; branch if alpha seed
        jnz     dithzr1         ; /
        tick    var0,var4       ; clock tick
        jc      dithzr1         ; branch if there is a tick
dithzrz:nextx                   ; else exit easy
dithzr1:random
        ;
dithzr2:getv    ch,var1         ; get dithzr value
        sub     ch,cl           ; above threshold?
        jna     dithzr3         ; yes, branch
        getv    al,var2         ; no, get the input
        jmp     short dithzrx   ; split
dithzr3:mov     al,0            ; no, make it 0
dithzrx:getv    ah,var3         ; get the offset
        add     al,ah           ; add to input
        putn    al              ; send it out
        nextv                   ; exit with video update
;==============================================================
; dither between 0 and 1
; inputs: 0: clock, 1: dither, 2x: clock
; output: 0/1
;--------------------------------------------------------------
        public _ddithr
_ddithr:
        test    rhold,-1        ; branch if alpha seed
        jnz     ddithr1         ; /
        tick    var0,var2       ; clock tick
        jc      ddithr1         ; branch if there is a tick
ddithrz:nextx                   ; else exit easy
ddithr1:random
ddithr2:getv    ch,var1         ; get ddithr value
        sub     ch,cl           ; above threshold?
        mov     al,1            ; set flag
        ja      ddithrx         ; yes, branch branch
        dec     al              ; no, clear flag
ddithrx:putn    al              ; send it out
        nextl                   ; exit with video update
;==============================================================
; calc given ratio based on clock input
; inputs: 0:   master clock
;         1-4: clock inputs
;         5x:  clock ticks
;         5z:  storage for output until tick
; output: 0-F, based on input pattern
;--------------------------------------------------------------
        public _ratio
_ratio:
        mov     al,var5+1[di]   ; get current output
        tick    var1,var5       ; clock tick
        jnc     ratio1          ; branch if no tick
        xor     al,1            ; else flip the bit
ratio1: tickb1  var2,var5       ; clock tick
        jnc     ratio2          ; branch if no tick
        xor     al,2            ; else flip the bit
ratio2: tickb2  var3,var5       ; clock tick
        jnc     ratio3          ; branch if no tick
        xor     al,4            ; else flip the bit
ratio3: tickb3  var4,var5       ; clock tick
        jnc     ratio4          ; branch if no tick
        xor     al,8            ; else flip the bit
ratio4: mov     var5+1[di],al   ; put it away
        tickb4  var0,var5       ; master clock tick?
        jnc     ratioz          ; no, exit
        putn    al              ; yes, send out new tick
ratioz: nextv                   ; exit with video update
;==============================================================
; calc given ratio based on dither input
; inputs: 0:   clock
;         1-4: dither levels
;         5x:  clock tick 
; output: 0-F, based on input pattern
;--------------------------------------------------------------
        public _ratiz
_ratiz:
        test    rhold,-1        ; branch if alpha seed
        jnz     ratiz0          ; /
        tick    var0,var5       ; tick?
        jc      ratiz0          ; yes, branch
        jmp     ratizz          ; no, just exit
ratiz0: mov     al,outn[di]     ; get current output
        random
        getv    ch,var1         ; get ddithr value
        or      ch,ch           ; anything there
        jnz     ratiza          ; yes, branch
        and     al,0feh         ; no, clear the bit
ratiza: sub     ch,cl           ; above threshold?
        jna     ratiz1          ; no, branch
        xor     al,1            ; yes, set flag
ratiz1: random
        getv    ch,var2         ; get ddithr value
        or      ch,ch           ; anything there
        jnz     ratizb          ; yes, branch
        and     al,0fdh         ; no, clear the bit
ratizb: sub     ch,cl           ; above threshold?
        jna     ratiz2          ; no, branch
        xor     al,2            ; yes, set flag
ratiz2: random
        getv    ch,var3         ; get ddithr value
        or      ch,ch           ; anything there
        jnz     ratizc          ; yes, branch
        and     al,0fbh         ; no, clear the bit
ratizc: sub     ch,cl           ; above threshold?
        jna     ratiz3          ; no, branch
        xor     al,4            ; yes, set flag
ratiz3: random
        getv    ch,var4         ; get ddithr value
        or      ch,ch           ; anything there
        jnz     ratizd          ; yes, branch
        and     al,0f7h         ; no, clear the bit
ratizd: sub     ch,cl           ; above threshold?
        jna     ratiz4          ; no, branch
        xor     al,8            ; yes, set flag
ratiz4: putn    al              ; send it out
ratizz: nextv                   ; exit with video update
;==============================================================
; select intervals
; inputs:
;    0:   sort step
;    1-4: inputs
;
; output: value at sorted step
;--------------------------------------------------------------
        public _sort
_sort:  getv    al,var1                 ; get the inputs
        getv    ah,var2                 ;   /
        getv    dl,var3                 ;  /
        getv    dh,var4                 ; /
        sort4                           ; sort them
        mov     word ptr temp0,ax       ; put in temp array
        mov     word ptr temp2,dx       ; /
        getv    bl,var0                 ; get interval number
        and     bx,3                    ; mask
        add     bx,offset dgroup:temp0  ; bx = table index
        mov     al,[bx]                 ; get the value
        putn    al                      ; send it out
        nextv                           ; exit
;==============================================================
; delta test
; inputs:
;    0:   nz = hold
;    1:   delta
;    2:   nz = normalize within octave
;    3:   input
;    4x:  last input
; output: strobe when delta is detected
;--------------------------------------------------------------
        public _deltat
_deltat:
        hold    var0            ; hold?
        jnz     deltatz         ; exit if hold
        testv   var1,-1         ; delta 0?
        jz      deltatz         ; yes, just exit
        ;
        getv    al,var3         ; get pitch input for delta
        mov     ah,al           ; setup
        testv   var2,-1         ; want to normalize
        jz      deltat1         ; no, branch
        normal                  ; normalize
deltat1:getv    al,var1         ; get delta
        xchg    ah,var4[di]     ; swap input with last one
        sub     ah,var4[di]     ; delta match?
        cmp     ah,al           ; /
        jz      deltat2         ; yes, branch
        neg     al              ; else swap sign
        cmp     ah,al           ; try again
        jz      deltat2         ; branch if = delta
        jmp     deltatz         ; no, exit
        ;
deltat2:mov     byte ptr outn[di],1; set flag
deltatz:nextt                   ; strobe
;==============================================================
; * 12, + offset
; inputs:
;    0:   value
;    1:   offset
;
; output: value * 12 + offset
;--------------------------------------------------------------
        public _mul12
_mul12:
        getv    al,var0         ; get value
        mov     dl,12           ; for octave
        mul     dl              ; * 12
        getv    ah,var1         ; get offset
        add     al,ah           ; add it in
        putn    al              ; send it out
        nextv                   ; exit
;==============================================================
; / 12
; input:  0: value
; output: value / 12
;--------------------------------------------------------------
        public _div12
_div12:
        getv    al,var0         ; get value
        or      al,al           ; zero not allowed
        jz      div12x          ; split if zero
        mov     dl,12           ; for octave
        mov     ah,0            ; make into word
        div     dl              ; / 12
div12x: putn    al              ; send it out
        nextv                   ; exit
;==============================================================
; mod 12
; input:  0: value
;         1: offset
; output: value mod 12
;--------------------------------------------------------------
        public _mod12
_mod12:
        getv    al,var0         ; get value
        mov     ah,0            ; load for 0
        or      al,al           ; zero not allowed
        jz      mod12x          ; split if zero
        mov     dl,12           ; for octave
        div     dl              ; / 12
mod12x: getv    al,var1         ; get offset
        add     al,ah           ; add in mod value
        putn    al              ; send it out
        nextv                   ; exit
;==============================================================
; fold to octave range
; input:  0: value
;         1: octave range
;         2: offset
; output: value mod 12
;--------------------------------------------------------------
        public _fold
_fold: 
        getv    al,var1         ; get max octave range
        and     al,15           ; limit
        mov     ch,12           ; octave
        mul     ch              ; /
        add     ch,al           ; ch = max
        ;
        getv    ah,var0         ; get input
        range   ah,ch           ; put in range
        getv    al,var2         ; get offset
        add     al,ah           ; add in mod value
        putn    al              ; send it out
        nextv                   ; exit
;==============================================================
; Union
; inputs: 0: trigger, 1: sync, 2x: clocks, 2z: trig flag
; output: pulse of one loop cycle duration
;--------------------------------------------------------------
        public _union
_union: 
        tick    var0,var2               ; clock trig input
        jnc     union0                  ; branch if no tick
        mov     byte ptr var2+1[di],1   ; else set holding byte
union0: tickb1  var1,var2               ; clock sync input
        jnc     unionz                  ; branch if no tick
        mov     al,0                    ; else clear trigger, if any
        xchg    al,var2+1[di]           ; get trigger, if any
        putn    al                      ; send it out
unionz: nextt                           ; strobe

;==============================================================
; Flip-flop
; inputs: 0: set, 1: reset, 2x: set-tick, 2z: reset-tick 
; output: pulse of one loop cycle duration
;--------------------------------------------------------------
        public _fflop
_fflop: 
        tick    var0,var2       ; clock tick
        jnc     fflop1          ; branch if no set input
        mov     byte ptr outn[di],1; set the pulse
fflop1: tick    var1,var2+1     ; clock tick
        jnc     fflop2          ; branch if no reset input
        mov     byte ptr outn[di],0; reset the pulse
fflop2: nextl                   ; exit with led update
;==============================================================
; shift left
; inputs: 0: value, 1: shift ammount, 2: mask
; output: shifted value
;--------------------------------------------------------------
        public _lshift
_lshift:
        getv    cl,var1         ; get the exponent
        and     cl,07H          ; 0-7 allowed
        getv    al,var0         ; get the value
        rol     al,cl           ; shift
        getv    ah,var2         ; get mask
        and     al,ah           ; mask output
        putn    al              ; send it out
        nextv                   ; exit with video update
;==============================================================
; shift right
; inputs: 0: value, 1: shift ammount, 2: mask
; output: shifted value
;--------------------------------------------------------------
        public _rshift
_rshift:
        getv    cl,var1         ; get the exponent
        and     cl,07H          ; 0-7 allowed
        getv    al,var0         ; get the value
        ror     al,cl           ; shift
        getv    ah,var2         ; get mask
        and     al,ah           ; mask output
        putn    al              ; send it out
        nextv                   ; exit with video update
;==============================================================
; Low Pass Filter
; inputs: 0: clock, 1: value, 2: slope, 3: offset
;        4x: index, 4z: clock tick, 5-12: storage
; output: average of last 2, 4, 8, or 16 inputs (slope 0-3)
;--------------------------------------------------------------
        public _filter
_filter:
        tick    var0,var4+1     ; clock tick
        jc      filte1          ; branch if there is a tick
        nextx                   ; else easy exit
        ;
filte1: getv    al,var1         ; get the input value
        getv    cl,var2         ; get the slope
        and     cl,03H          ; 0-3 allowed
        mov     dl,2            ; shift
        shl     dl,cl           ; dl = 2, 4, 8, or 16
        mov     dh,dl           ; copy
        dec     dh              ; dh = 1, 3, 7, or 15
        mov     bl,var4[di]     ; get index
        inc     bl              ; bump it
        and     bl,dh           ; keep in bounds
        mov     var4[di],bl     ; put it away
        mov     bh,0            ; bx = index offset
        mov     var5[bx+di],al  ; put value into save list
        mov     dh,cl           ; dh = slope value (for later)
        mov     cl,dl           ; get slope count
        mov     ch,0            ; cx = 2, 4, 8, or 16
        mov     bx,var5         ; bx = start of save table
        mov     bp,0            ; bp will get sum of saved inputs
        mov     ah,0            ; ax will get each byte saved
filte2: mov     al,[bx+di]      ; get the saved byte
        add     bp,ax           ; sum of saved bytes
        inc     bx              ;   /
        loop    filte2          ; /
        mov     ax,bp           ; ax = sum of saved byte values
        mov     cl,dh           ; dh = slope value
        inc     cl              ; bump
        shr     ax,cl           ; ax = average of saved inputs
        getv    ah,var3         ; get offset
        add     al,ah           ; add to averaged value
        putn    al              ; send it out
        nextv                   ; exit with video update
;==============================================================
; Power of 2
; inputs: 0: value
; output: 2 ^^ value
;--------------------------------------------------------------
        public _twopwr
_twopwr:
        getv    cl,var0         ; get the exponent
        and     cl,07H          ; 0-7 allowed
        mov     al,1            ; shift
        shl     al,cl           ; /
        putn    al              ; send it out
        nextv                   ; exit with video update
;==============================================================
; Test for flagged bits
; inputs: 0: input, 1: value
; output: 1 if any masked bits are hi, else 0
;--------------------------------------------------------------
        public _bitsp
_bitsp: getv    al,var0         ; get input
        mov     dl,0            ; flag = false
        test    al,-1           ; any bits hi?
        jz      bitsp1          ; no, branch
        getv    dl,var1         ; yes, get the value
bitsp1: putn    dl              ; send the flag
        nextl                   ; exit with led update
;==============================================================
; Test for flagged bits
; inputs: 0: input, 1: bitmask, 2: value
; output: 1 if any masked bits are hi, else 0
;--------------------------------------------------------------
        public _bitsq
_bitsq: getv    al,var0         ; get input
        getv    ah,var1         ; get mask byte
        mov     dl,0            ; flag = false
        test    al,ah           ; any bits hi?
        jz      bitsq1          ; no, branch
        getv    dl,var2         ; yes, get the value
bitsq1: putn    dl              ; send the flag
        nextl                   ; exit with led update
;==============================================================
; Test for minus ( bit 7 high)
; inputs: 0: input
; output: 1 if bit 7 hi, else 0
;--------------------------------------------------------------
        public _minusp
_minusp:
        getv    al,var0         ; get input
        mov     dl,0            ; flag = false
        test    al,80H          ; is negative?
        jz      minusp1         ; no, branch
        inc     dl              ; yes, flag = true
minusp1:putn    dl              ; send the flag
        nextl                   ; exit with led update
;==============================================================
; Test for non-zero
; inputs: 0: input
; output: 0 if input is 0, else 1
;--------------------------------------------------------------
        public _truep
_truep: 
        getv    al,var0         ; get input
        mov     dl,1            ; flag = false
        or      al,al           ; is zero?
        jnz     truep1          ; no, branch
        dec     dl              ; yes, flag = true
truep1: putn    dl              ; send the flag
        nextl                   ; exit with led update
;==============================================================
; Test for zero 
; inputs: 0: input
; output: 1 if input is 0, else 0
;--------------------------------------------------------------
        public _zerop
_zerop: 
        getv    al,var0         ; get input
        mov     dl,0            ; flag = false
        or      al,al           ; is zero?
        jnz     zerop1          ; no, branch
        inc     dl              ; yes, flag = true
zerop1: putn    dl              ; send the flag
        nextl                   ; exit with led update
;==============================================================
; Test for within range
; inputs: 0: input, 1: hi bounds, 2: low bounds
; output: 1 if within range, else 0
;--------------------------------------------------------------
        public _rangep
_rangep:
        getv    al,var0         ; get input 
        mov     dl,0            ; flag = false
        getv    ah,var1         ; get hi range
        cmp     al,ah           ; input > hi range?
        ja      range1          ; yes, exit False
        getv    ah,var2         ; get lo range
        cmp     al,ah           ; input < lo range?
        jb      range1          ; yes, exit False
        inc     dl              ; no, flag = True
range1: putn    dl              ; send the flag
        nextl                   ; exit with led update
;==============================================================
; Test for a > b
; inputs: 0: input a, 1: input b
; output: 1 if a > b, else 0
;--------------------------------------------------------------
        public _great
_great: 
        getv    al,var0         ; get input a
        getv    ah,var1         ; get inmpu b
        mov     dl,0            ; flag = false
        cmp     al,ah           ; is a > b ?
        jna     great1          ; no, branch
        inc     dl              ; yes, flag = true
great1: putn    dl              ; send the flag
        nextl                   ; exit with led update
;==============================================================
; Test for a < b
; inputs: 0: input a, 1: input b
; output: 1 if a < b, else 0
;--------------------------------------------------------------
        public _lessp
_lessp: 
        getv    al,var0         ; get input a
        getv    ah,var1         ; get inmpu b
        mov     dl,0            ; flag = false
        cmp     al,ah           ; is a < b ?
        jnb     lessp1          ; no, branch
        inc     dl              ; yes, flag = true
lessp1: putn    dl              ; send the flag
        nextl                   ; exit with led update
;==============================================================
; Test for change
; inputs: 0: input, 1x: last input value
; output: 1 if change else 0
;--------------------------------------------------------------
        public _change
_change:
        mov     ah,0            ; clear flag
        getv    al,var0         ; get input 1
        cmp     al,var1[di]     ; same as last?
        jz      chang1          ; yes, send "no change"
        inc     ah              ; else send change
chang1: putn    ah              ; send the flag
        mov     var1[di],al     ; save value for next time
        nextl                   ; exit with led update
;==============================================================
; Test for equal
; inputs: 0: input, 1: input
; output: 1 if equal, else 0
;--------------------------------------------------------------
        public _equal
_equal: 
        getv    al,var0         ; get input 1
        getv    ah,var1         ; get inmpu 2
        mov     dl,0            ; flag = false
        cmp     al,ah           ; are they equal
        jnz     equal1          ; no, branch
        inc     dl              ; yes, flag = true
equal1: putn    dl              ; send the flag
        nextl                   ; exit with led update
;==============================================================
; Test for not equal
; inputs: 0: input, 1: input
; output: 1 if not equal, else 0
;--------------------------------------------------------------
        public _nequal
_nequal: 
        getv    al,var0         ; get input 1
        getv    ah,var1         ; get inmpu 2
        mov     dl,0            ; flag = false
        cmp     al,ah           ; are they equal
        jz      nequal1         ; yes, branch
        inc     dl              ; no, flag = true
nequal1:putn    dl              ; send the flag
        nextl                   ; exit with led update
;==============================================================
; output: delayed input
; input: 0: value
;        1: delay clocks
;        2: offset
;        3: index
;        4-131: value array
;--------------------------------------------------------------
        public _delay
_delay: getv    al,var0         ; get input value
        getv    dl,var1         ; get number of clocks delay
        getv    dh,var2         ; get offset
        mov     bl,var3[di]     ; get current count
        dec     byte ptr var3[di]; bump it
        mov     bh,0            ; byte --> word
        mov     var4[bx+di],al  ; set input byte
        add     bl,dl           ; add delay
        mov     al,var4[bx+di]  ; get the output byte
        add     al,dh           ; add offset
        putn    al              ; send the new output
        nextv
;==============================================================
; output: the last changed input
; input: 0-7: values
;        8x - 9+: saved values 
;--------------------------------------------------------------
        public _lastv
_lastv: mov     bp,si           ; save si
        mov     dx,di           ; save di
        mov     si,di           ; set si to saved values
        add     si,var8         ; /
        add     di,var0         ; set di to input values
        mov     cx,8            ; 8 to do
lastvl: mov     bx,[di]         ; point to var addr
        mov     al,[bx]         ; get it
        cmp     al,[si]         ; same?
        jnz     lastvx          ; yes, exit
        add     di,2            ; point to next
        inc     si              ; /
        loop    lastvl          ; do all 8
        mov     di,dx           ; restore registers
        mov     si,bp           ; /
        nextx                   ; easy exit if no change
        ;       
lastvx: mov     [si],al         ; set the flag
        mov     di,dx           ; restore registers
        mov     si,bp           ; /
        putn    al              ; send the new output
        nextv
;==============================================================
; Quantize value input to nearest match input
; inputs: 0: value input, 1: extract value, 2-9: match inputs
; output: nearest match input
;--------------------------------------------------------------
        public _quant
_quant:
        getv    dl,var0         ; dl = value input
        ;
        getv    cl,var1         ; get extract root
        or      cl,cl           ; if zero, ignore
        jz      quant0          ; /
        ;       
        mov     al,dl           ; get the value
        or      al,al           ; zero not allowed
        jz      quant0a         ; split if zero
        mov     ah,0            ; make into word
        div     cl              ; divide by extract value
        mov     dl,ah           ; mod result is compare value
        mul     cl              ; restore to base
quant0a:mov     cl,al           ; cl is base
        ;
quant0: mov     dh,-1           ; dh = initialized test
        ;
        getv    al,var2         ; get match input
        mov     ah,dl           ; get value input
        sub     ah,al           ; get difference
        cmp     ah,dh           ; difference < test
        jnb     quant1a         ; no, branch
        mov     dh,ah           ; yes, difference is new test
        putn    al              ; match input --> output
quant1a:neg     ah              ; test abs difference 
        cmp     ah,dh           ; abs difference < test
        jnb     quant2          ; no, branch
        mov     dh,ah           ; yes, difference is new test
        putn    al              ; match input --> output
        ;
quant2: getv    al,var3         ; get match input
        mov     ah,dl           ; get value input
        sub     ah,al           ; get difference
        cmp     ah,dh           ; difference < test
        jnb     quant2a         ; no, branch
        mov     dh,ah           ; yes, difference is new test
        putn    al              ; match input --> output
quant2a:neg     ah              ; test abs difference 
        cmp     ah,dh           ; abs difference < test
        jnb     quant3          ; no, branch
        mov     dh,ah           ; yes, difference is new test
        putn    al              ; match input --> output
        ;
quant3: getv    al,var4         ; get match input
        mov     ah,dl           ; get value input
        sub     ah,al           ; get difference
        cmp     ah,dh           ; difference < test
        jnb     quant3a         ; no, branch
        mov     dh,ah           ; yes, difference is new test
        putn    al              ; match input --> output
quant3a:neg     ah              ; test abs difference 
        cmp     ah,dh           ; abs difference < test
        jnb     quant4          ; no, branch
        mov     dh,ah           ; yes, difference is new test
        putn    al              ; match input --> output
        ;
quant4: getv    al,var5         ; get match input
        mov     ah,dl           ; get value input
        sub     ah,al           ; get difference
        cmp     ah,dh           ; difference < test
        jnb     quant4a         ; no, branch
        mov     dh,ah           ; yes, difference is new test
        putn    al              ; match input --> output
quant4a:neg     ah              ; test abs difference 
        cmp     ah,dh           ; abs difference < test
        jnb     quant5          ; no, branch
        mov     dh,ah           ; yes, difference is new test
        putn    al              ; match input --> output
        ;
quant5: getv    al,var6         ; get match input
        mov     ah,dl           ; get value input
        sub     ah,al           ; get difference
        cmp     ah,dh           ; difference < test
        jnb     quant5a         ; no, branch
        mov     dh,ah           ; yes, difference is new test
        putn    al              ; match input --> output
quant5a:neg     ah              ; test abs difference 
        cmp     ah,dh           ; abs difference < test
        jnb     quant6          ; no, branch
        mov     dh,ah           ; yes, difference is new test
        putn    al              ; match input --> output
        ;
quant6: getv    al,var7         ; get match input
        mov     ah,dl           ; get value input
        sub     ah,al           ; get difference
        cmp     ah,dh           ; difference < test
        jnb     quant6a         ; no, branch
        mov     dh,ah           ; yes, difference is new test
        putn    al              ; match input --> output
quant6a:neg     ah              ; test abs difference 
        cmp     ah,dh           ; abs difference < test
        jnb     quant7          ; no, branch
        mov     dh,ah           ; yes, difference is new test
        putn    al              ; match input --> output
        ;
quant7: getv    al,var8         ; get match input
        mov     ah,dl           ; get value input
        sub     ah,al           ; get difference
        cmp     ah,dh           ; difference < test
        jnb     quant7a         ; no, branch
        mov     dh,ah           ; yes, difference is new test
        putn    al              ; match input --> output
quant7a:neg     ah              ; test abs difference 
        cmp     ah,dh           ; abs difference < test
        jnb     quant8          ; no, branch
        mov     dh,ah           ; yes, difference is new test
        putn    al              ; match input --> output
        ;
quant8: getv    al,var9         ; get match input
        mov     ah,dl           ; get value input
        sub     ah,al           ; get difference
        cmp     ah,dh           ; difference < test
        jnb     quant8a         ; no, branch
        mov     dh,ah           ; yes, difference is new test
        putn    al              ; match input --> output
quant8a:neg     ah              ; test abs difference 
        cmp     ah,dh           ; abs difference < test
        jnb     quant9          ; no, branch
        mov     dh,ah           ; yes, difference is new test
        putn    al              ; match input --> output
        ;
quant9: add     outn[di],cl     ; add base
        nextv                   ; exit with video update
;==============================================================
; Keep the highest input
; inputs: 0: hold/reset 1: input 
; output: highest input since reset
;--------------------------------------------------------------
        public _higher
_higher:
        hold    var0            ; hold?
        jz      higher0         ; branch if no hold
        getv    al,var1         ; else get the input
        putn    al              ; send it to the output
        nextv                   ; exit
higher0:getv    al,var1         ; get the value
        cmp     al,outn[di]     ; higher than already there?
        jna     higher1         ; no, branch
        putn    al              ; yes, send the new value
higher1:nextv                   ; exit with video update
;==============================================================
; Keep the lowest input
; inputs: 0: hold/reset 1: input 
; output: lowest input since reset
;--------------------------------------------------------------
        public _lower
_lower: 
        hold    var0            ; hold?
        jz      lower0          ; branch if no hold
        getv    al,var1         ; else get the input
        putn    al              ; send it to the output
        nextv                   ; exit
lower0: getv    al,var1         ; get the value
        cmp     al,outn[di]     ; lower than already there?
        jnb     lower1          ; no, branch
        putn    al              ; yes, send the new value
lower1: nextv                   ; exit with video update
;==============================================================
; Keep input between limits
; inputs: 0: input, 1: upper bounds, 2: lower bounds 
; output: input between bounds
;--------------------------------------------------------------
        public _limit
_limit: 
        getv    al,var0         ; get the value
        getv    ah,var1         ; get upper limit
        cmp     al,ah           ; less than upper limit?
        jna     limit1          ; yes, branch
        mov     al,ah           ; else set it to upper limit
limit1: getv    ah,var2         ; get lower limit
        cmp     al,ah           ; greater than lower limit?
        jnb     limit2          ; yes, branch
        mov     al,ah           ; else set it to lower limit
limit2: putn    al              ; send the value
        nextv                   ; exit w video
;==============================================================
; Sample & Hold
; inputs: 0: follow 1: clock, 2: read 3: offset 4x: clock tick
; output: scaled sample
;--------------------------------------------------------------
        public _sandh
_sandh: 
        testv   var0,-1         ; follow on?
        jnz     sandh1          ; yes, branch
        tick    var1,var4       ; clock tick
        jc      sandh1          ; branch if there is a tick
        nextx                   ; else exit easy
sandh1: getv    al,var2         ; get sample
        getv    ah,var3         ; get offset
        add     al,ah           ; put it together
        putn    al              ; send it
        nextv                   ; exit with video update
;==============================================================
; Midi sync control interface for Stop, Start, Continue, etc.
; output: bit flags
;       bit 0: stop 
;       bit 1: start
;       bit 2: continue
; input:
;       0: NZ = send Stop to Midi
;       1: NZ = send Start to MIDI
;       2: NZ = send Continue to MIDI
;       3: NZ = send Reset to MIDI
;       4: NZ = send Tune req to midi
;       5: NZ bits = clear output bits
;       6x: last stop input
;       6+: last start input
;       7x: last continue input
;       7+: initialized flag
;       8x: tick for stop, start, continue
;       8+: tick for reset, tune, clear
;--------------------------------------------------------------
        public _stopo
_stopo: cmp     byte ptr var7+1[di],0abh; initialized?
        jz      stopo0                  ; yes, branch
        mov     word ptr var7[di],0ab00h; no, initialize
        mov     word ptr var8[di],0     ;    /
        mov     word ptr var6[di],0     ;  /
        jmp     stopox                  ; exit from init
        ;
stopo0: mov     al,cs:mstop             ; change in ext stop?
        cmp     al,var6[di]             ; /
        jz      stopo0a                 ; no, branch
        or      byte ptr outn[di],1     ; yes, set the flag
        mov     var6[di],al             ; /
        ;
stopo0a:mov     al,cs:mstart            ; change in ext start?
        cmp     al,var6+1[di]           ; /
        jz      stopo0b                 ; no, branch
        or      byte ptr outn[di],2     ; yes, set the flag
        mov     var6+1[di],al           ; /
        ;
stopo0b:mov     al,cs:mcont             ; change in ext cont?
        cmp     al,var7[di]             ; /
        jz      stopo1                  ; no, branch
        or      byte ptr outn[di],4     ; yes, set the flag
        mov     var7[di],al             ; /
;--------------------------------------------------------------
stopo1: tick    var0,var8               ; stop cmd input?
        jnc     stopo1a                 ; no, branch
        mov     al,0fch                 ; send Stop cmd to midi
        call    allmidi
        ;
stopo1a:tickb1  var1,var8               ; start cmd input?
        jnc     stopo1b                 ; no, branch
        mov     al,0fah                 ; send Start cmd to midi
        call    allmidi
        ;
stopo1b:tickb2  var2,var8               ; Cont cmd input?
        jnc     stopo2                  ; no, branch
        mov     al,0fbh                 ; send Cont cmd to midi
        call    allmidi
;--------------------------------------------------------------
stopo2: tick    var3,var8+1             ; Reset cmd input?
        jnc     stopo2a                 ; no, branch
        mov     al,0ffh                 ; send Reset cmd to midi
        call    allmidi
        ;
stopo2a:tickb1  var4,var8+1             ; Tune Request input?
        jnc     stopo3                  ; no, branch
        mov     al,0f6h                 ; send Tune Request to midi
        call    allmidi
;--------------------------------------------------------------
stopo3: tickb2  var5,var8+1             ; clear output?
        jnc     stopox                  ; no, branch
        mov     byte ptr outn[di],0     ; clear the output
stopox: nextl                           ; show it
;==============================================================
; Sum of 5 inputs
; inputs: 0-4:  inputs, 5: scale 6: offset
; output: scaled and offset input
;--------------------------------------------------------------
        public _sumit
_sumit: 
        xor     cx,cx           ; clr cx
        mov     ah,ch           ; clr ah
        getv    al,var0         ; get input 
        add     cx,ax           ; add to total
        getv    al,var1         ; get input 
        add     cx,ax           ; add to total
        getv    al,var2         ; get input 
        add     cx,ax           ; add to total
        getv    al,var3         ; get input 
        add     cx,ax           ; add to total
        getv    al,var4         ; get input 
        add     cx,ax           ; add to total
        ;
        getv    al,var5         ; get scale value
        xchg    al,ah           ; al = 0, ah = scale
        mul     cx              ; dx = scaled value
        getv    al,var6         ; get offset
        add     al,dl           ; put it together
        putn    al              ; send it
        nextv                   ; exit with video update
;==============================================================
; Mix 3 Scaled inputs
; inputs: 0,2,4: input, 1,3,5: scale 6: offset
; output: scaled and offset input
;--------------------------------------------------------------
        public _mixit
_mixit: 
        getv    al,var0         ; get input 
        getv    cl,var1         ; get scale value
        mul     cl              ; ah = scaled value
        mov     ch,ah           ; for sum
        getv    al,var2         ; get input 
        getv    cl,var3         ; get scale value
        mul     cl              ; ah = scaled value
        add     ch,ah           ; sum
        getv    al,var4         ; get input 
        getv    cl,var5         ; get scale value
        mul     cl              ; ah = scaled value
        add     ch,ah           ; sum
        ;
        getv    al,var6         ; get offset
        add     al,ch           ; put it together
        putn    al              ; send it
        nextv                   ; exit with video update
;==============================================================
; Scale and offset input
; inputs: 0: input, 1: scale 2: offset
; output: scaled and offset input
;--------------------------------------------------------------
        public _scale
_scale: 
        getv    al,var0         ; get input 
        getv    cl,var1         ; get scale value
        mul     cl              ; ah = scaled value
        getv    al,var2         ; get offset
        add     al,ah           ; put it together
        putn    al              ; send it
        nextv                   ; exit with video update
;==============================================================
; Max of two inputs
; inputs: 0: input, 1: input
; output: max
;--------------------------------------------------------------
        public _maxf
_maxf:  
        getv    al,var0         ; get input 
        getv    ah,var1         ; get input
        cmp     ah,al           ; select larger
        jb      maxf1           ;   /
        mov     al,ah           ; /
maxf1:  putn    al              ; send it
        nextv                   ; exit with video update
;==============================================================
; Min of two inputs
; inputs: 0: input, 1: input
; output: min
;--------------------------------------------------------------
        public _minf
_minf:  
        getv    al,var0         ; get input 
        getv    ah,var1         ; get input
        cmp     al,ah           ; select larger
        jb      minf1           ;   /
        mov     al,ah           ; /
minf1:  putn    al              ; send it
        nextv                   ; exit with video update
;==============================================================
; Rate of change (delta)
; inputs: 0: input, 1x: last input
; output: delta from last changed input
;
        public _delta
_delta: 
        getv    al,var0         ; get input 
        cmp     al,byte ptr var1[di]; any change
        jnz     delta1          ; yes, branch
        nextx                   ; no, easy exit
        ;
delta1: mov     ah,byte ptr var1[di]; get old value
        sub     ah,al           ; calc delta
        jnb     delta2          ; keep it positive
        neg     ah              ; /
delta2: mov     byte ptr var1[di],al; save new input
        putn    ah              ; send delta
        nextv                   ; exit with video update
;==============================================================
; Add three inputs
; inputs: 0: input, 1: input, 2: input
; output: sum of the three inputs
;
        public _plus 
_plus:  
        getv    al,var0         ; get input 
        getv    ah,var1         ; get input
        add     al,ah           ; put it together
        getv    ah,var2         ; get input
        add     al,ah           ; put it together
        putn    al              ; send it
        nextv                   ; exit with video update
;==============================================================
; Add two inputs, subtract the third
; inputs: 0: input, 1: input, 2: input
; output: sum of first two inputs minus the third
;
        public _plusm,_plusn
_plusn: nop
_plusm: getv    al,var0         ; get input 
        getv    ah,var1         ; get input
        add     al,ah           ; put it together
        getv    ah,var2         ; get input
        sub     al,ah           ; put it together
        putn    al              ; send it
        nextv                   ; exit with video update
;==============================================================
; multiply input by 2nd input, divide by 3rd if nz
; inputs: 0: input, 1: mult, 2: divid
; output: product of the 3 inputs
;
        public _times
_times: 
        getv    al,var0         ; get input 
        getv    cl,var1         ; get input
        mul     cl              ; put it together
        getv    cl,var2         ; get input
        or      cl,cl           ; zero?
        jz      times1          ; yes, do not divide
        mov     dx,0            ; give it headroom
        mov     ch,0
        div     cx              ; divide dx:ax / cx
times1: putn    al              ; send it
        nextv                   ; exit with video update
;==============================================================
; divide first input by 2nd, return remainder
; inputs: 0: input, 1: divide
; output: product of the 3 inputs
;
        public _modulo
_modulo:
        getv    al,var0         ; get input 
        mov     ah,0            ; setup for 0
        getv    cl,var1         ; get input
        or      cl,cl           ; zero?
        jz      modulx          ; yes, do not divide
        div     cl              ; divide ax / cl
modulx: putn    ah              ; send remainder
        nextv                   ; exit with video update
;==============================================================
; AND gate
; inputs: 0,1,2
; output: result of input0 AND input1 XOR input2
;--------------------------------------------------------------
        public _andl
_andl:  
        getv    al,var0         ; al = 1st variable
        getv    ah,var1         ; ah = 2nd variable
        and     al,ah           ; AND them
        getv    ah,var2         ; ah = 3rd variable
        xor     al,ah           ; xor them
        putn    al              ; send to output
        nextv         
;==============================================================
; AND gate
; inputs: 0: &input 1: &input 2: +input  
; output: result of input 0 AND input 1 PLUS input 2
;--------------------------------------------------------------
        public _mask
_mask:  
        getv    al,var0         ; al = 1st variable
        getv    ah,var1         ; ah = 2nd variable
        and     al,ah           ; var0 AND var1
        getv    ah,var2         ; ah = 3rd variable
        add     al,ah           ; add it to the AND stuff
        putn    al              ; send to output
        nextv
;==============================================================
; AND gate
; inputs: 0: &input 1: &input, 2: shift
; output: shifted result of input 0 AND input 1
;--------------------------------------------------------------
        public _maskl
_maskl: getv    al,var0         ; al = 1st variable
        getv    ah,var1         ; ah = 2nd variable
        and     al,ah           ; var0 AND var1
        getv    cl,var2         ; get shift
        rol     al,cl           ; shift
        putn    al              ; send to output
        nextv
;--------------------------------------------------------------
        public _maskr
_maskr: getv    al,var0         ; al = 1st variable
        getv    ah,var1         ; ah = 2nd variable
        and     al,ah           ; var0 AND var1
        getv    cl,var2         ; get shift
        ror     al,cl           ; shift
        putn    al              ; send to output
        nextv
;==============================================================
; NOT logic
; inputs: 0: value
;         1: mask 
; output: NOT input0 AND input 1 
;--------------------------------------------------------------
        public _notl
_notl:  
        getv    al,var0         ; al = 1st variable
        not     al              ; complement
        getv    ah,var1         ; get mask
        and     al,ah           ; AND them
        putn    al              ; send to output
        nextv         
;==============================================================
; Slope detector
; inputs: 0
; output: nz if positive slope, z if neg, no change if 0
;
;--------------------------------------------------------------
        public _slope
_slope: 
        getv    ah,var0         ; al = 1st variable
        cmp     ah,var1[di]     ; same as last?
        jz      slopex          ; yes, just exit
        mov     var1[di],ah     ; no, save it
        mov     al,1            ; setup for pos
        ja      slope1          ; branch if pos
        mov     al,0            ; else flag neg
slope1: putn    al              ; send to output
slopex: nextl         

;==============================================================
; negate
; inputs: 0: input
; output: NEG of input
;--------------------------------------------------------------
        public _negate
_negate:
        getv    al,var0         ; al = 1st variable
        neg     al              ; negate it
        putn    al              ; send to output
        nextv
;==============================================================
; OR gate
; inputs: 0,1,2
; output: result of input0 OR input1 OR input2
;--------------------------------------------------------------
        public _orl
_orl:   
        getv    al,var0         ; var0 OR var1...
        getv    ah,var1         ;   /
        or      al,ah           ; /
        getv    ah,var2         ; ...OR var2
        or      al,ah           ; /
        putn    al              ; send out
        nextv
;==============================================================
; shunt input to output
; inputs: 0: value
; output: input 0 unchanged
;--------------------------------------------------------------
        public _value
_value: 
        getv    al,var0         ; get variable 1
        putn    al              ; send it
        nextv
;==============================================================
; display input value in ascii
; inputs: 0-2: values to be displayed
; outputs: none
;--------------------------------------------------------------
     public _ascii
_ascii: 
        getv    al,var0         ; get the input value
        getv    ah,var1         ;  /
        getv    cl,var2         ; /
        mov     es,4[di]        ; get seg addr
        mov     bx,6[di]        ; get module screen address
        mov     es:318[bx],al   ; put to the screen
        mov     es:320[bx],ah   ;  /
        mov     es:322[bx],cl   ; /
        nextx                   ; exit
;==============================================================
; display input value in ascii from seq "s"
; inputs: 0: lo addr of value, 1: hi addr,  2: offset to addr
; outputs: none
;--------------------------------------------------------------
     public _ascis
_ascis: 
        mov     ax,seg bufsp    ; setup seg register
        mov     es,ax           ; es = seg addr
        getv    dl,var0         ; get address
        getv    dh,var1         ; get hi address
        and     dh,15           ; 16 max
        getv    bl,var2         ; get offset
        add     dl,bl           ; put them together
        mov     bx,dx           ; /
        mov     al,es:[bx]      ; get the byte
        inc     bl              ; bump addr low
        mov     ah,es:[bx]      ; and the next
        inc     bl              ; bump addr low
        mov     cl,es:[bx]      ; and the next
        ;
        mov     es,4[di]        ; get seg addr
        mov     bx,6[di]        ; get module screen address
        mov     es:318[bx],al   ; put to the screen
        mov     es:320[bx],ah   ;  /
        mov     es:322[bx],cl   ; /
        nextx                   ; exit
;==============================================================
; display input value in ascii from seq "S"
; inputs: 0: lo addr of value, 1: hi addr, 2: offset
; outputs: none
;--------------------------------------------------------------
     public _asciz
_asciz: 
        mov     ax,seg buffs    ; setup seg register
        mov     es,ax           ; es = seg addr
        getv    dl,var0         ; get address lo
        getv    dh,var1         ; get address hi
        getv    bl,var2         ; get offset
        mov     bh,0            ; /
        add     bx,dx           ; set up as index
        mov     al,es:[bx]      ; get the byte
        mov     ah,es:1[bx]     ; and the next
        mov     cl,es:2[bx]     ; and the next
        ;
        mov     es,4[di]        ; get seg addr
        mov     bx,6[di]        ; get module screen address
        mov     es:318[bx],al   ; put to the screen
        mov     es:320[bx],ah   ;  /
        mov     es:322[bx],cl   ; /
        nextx                   ; exit
;==============================================================
; display input value in decimal
; inputs:  x: (display location)
;          0: value to be displayed
; outputs: copy of value
;--------------------------------------------------------------
     public _decin
_decin: 
        getv    al,var0         ; get the input value
        putn    al              ; send to output
        mov     colr,yellow     ; set color to yellow
        mov     es,4[di]        ; get seg addr
        mov     bx,6[di]        ; get module screen address
        add     bx,478          ; point to readout
        call    todec           ; do it
        mov     colr,green      ; fix it back to green
        nextv                   ; exit with update
;==============================================================
; display input value in decimal
; inputs:  0: decimal display 
; outputs: none (display location)
;--------------------------------------------------------------
     public _decim
_decim: 
        mov     colr,yellow     ; set color to yellow
        getv    al,var0         ; get the input value
        mov     es,4[di]        ; get seg addr
        mov     bx,6[di]        ; get module screen address
        add     bx,318          ; point to readout
        call    todec           ; do it
        mov     colr,green      ; fix it back to green
        nextx                   ; exit, no update
;==============================================================
; slew over time
; formula:
;       initialize:
;               input * rate --> sum; input --> output
;       thereafter until sum/rate = target:
;               sum - input + target --> sum
;               sum/rate             --> output
;
; output: slewed input, with led hi while slewing
; inputs:
; 0:  input
; 1:  target
; 2:  rate
; 3x: sum
; 4x: saved input
; 4+: saved target
; 5x: saved rate
; 5+: flag nz = busy
;--------------------------------------------------------------
        public _slew
_slew:  test    byte ptr var5+1[di],1   ; busy?
        jnz     slewa                   ; yes, branch
        getv    dl,var1                 ; no, see if target=output
        cmp     dl,outn[di]             ; /
        jnz     slewb                   ; yes, go slew
        getv    al,var0                 ; no, see if input has changed
        cmp     al,var4[di]             ; /
        jz      slewx                   ; yes, exit no change                
        ;
slewb:  mov     var4+1[di],dl           ; save target
        getv    al,var0                 ; get input
        mov     var4[di],al             ; save input
        getv    cl,var2                 ; get rate
        mov     var5[di],cl             ; save rate
        ;
        putn    dl                      ; target --> output
        or      cl,cl                   ; rate = 0?
        jz      slewx                   ; yes, just exit
        putn    al                      ; no, start with input --> output
        mov     byte ptr var5+1[di],1   ; flag busy
        mul     cl                      ; ax = input * rate
        mov     var3[di],ax             ; set new sum
        jmp     short slewx             ; exit
        ;
slewa:  test    byte ptr cs:loops,1     ; every other clock
        jnz     slewx                   ; branch if not even
        mov     ax,var3[di]             ; ax = sum
        mov     dl,var4[di]             ; dx = input
        mov     dh,0                    ; /
        sub     ax,dx                   ; ax = sum - input
        mov     dl,var4+1[di]           ; dx = target
        add     ax,dx                   ; ax = sum - input + target
        mov     var3[di],ax             ; save new sum
        mov     cl,var5[di]             ; cl = rate
        div     cl                      ; al = new sum / rate
        putn    al                      ; send to output
        cmp     al,dl                   ; output = target
        jnz     slewx                   ; no, exit
        mov     byte ptr var5+1[di],0   ; yes, flag not busy
        ;
slewx:  nextv                           ; exit
        
;==============================================================
; slew over time
; formula:
;       initialize:
;               input * 256 --> sum; input --> output
;       thereafter until sum/256 = target:
;               sum - input + target --> sum
;               sum/256              --> output
;
; output: slewed input, with led hi while slewing
; inputs:
; 0:  input
; 1:  target
; 2:  rate
; 3x: sum
; 4x: saved input
; 4+: saved target
; 5x: rate countdown
; 5+: flag nz = busy
;--------------------------------------------------------------
        public _xslew
_xslew: test    byte ptr var5+1[di],1   ; busy?
        jnz     xslewa                  ; yes, branch
        getv    dl,var1                 ; no, see if target=output
        cmp     dl,outn[di]             ; /
        jnz     xslewb                  ; yes, go slew
        getv    al,var0                 ; no, see if input has changed
        cmp     al,var4[di]             ; /
        jz      xslewx                  ; yes, exit no change                
        ;
xslewb: mov     var4+1[di],dl           ; save target
        getv    cl,var2                 ; get rate
        mov     var5[di],cl             ; save rate
        getv    al,var0                 ; get input
        mov     var4[di],al             ; save input
        ;
        putn    dl                      ; target --> output
        or      cl,cl                   ; rate = 0?
        jz      xslewx                  ; yes, just exit
        putn    al                      ; no, start with input --> output
        mov     byte ptr var5+1[di],1   ; flag busy
        mov     ah,al                   ; ax = input * 256
        mov     al,0                    ; /
        mov     var3[di],ax             ; set new sum
        jmp     short xslewx            ; exit
        ;
xslewa: dec     byte ptr var5[di]       ; countdown 
        jnz     xslewx                  ; /
        getv    al,var2                 ; get rate
        or      al,al                   ; somebody make it 0?
        jnz     xslewc                  ; no, go on
        getv    al,var1                 ; yes, set output to target
        mov     dl,al                   ; ...short cut
        jmp     short xslewz            ; exit finished
        ;
xslewc: mov     var5[di],al             ; save rate count
        mov     ax,var3[di]             ; ax = sum
        mov     dl,var4[di]             ; dx = input
        mov     dh,0                    ; /
        sub     ax,dx                   ; ax = sum - input
        mov     dl,var4+1[di]           ; dx = target
        add     ax,dx                   ; ax = sum - input + target
        mov     var3[di],ax             ; save new sum
        mov     al,ah                   ; al = new sum / 256
xslewz: putn    al                      ; send to output
        cmp     al,dl                   ; output = target
        jnz     xslewx                  ; no, exit
        mov     byte ptr var5+1[di],0   ; yes, flag not busy
        ;
xslewx: nextv                           ; exit
        
;==============================================================
; display input value in scale of CM
; inputs: 0: value to be displayed
; outputs: none
;--------------------------------------------------------------
     public _noter
_noter: 
        mov     colr,yellow     ; set value color
        getv    al,var0         ; get the input value
        mov     es,4[di]        ; get seg addr
        mov     bx,6[di]        ; get module screen address
        add     bx,318          ; point to readout
        call    tonote          ; do it
        mov     colr,green      ; restore to green again
        nextx
;==============================================================
_TEXT   ENDS
        END

