;==============================================================
; include file, with MACROS for Modules
;--------------------------------------------------------------
; generate a random number in CX
; 
random  macro
        local   randz
        mov     cx,rseed        ;; get next raw seed
        test    rhold,-1        ;; test for hold
        jnz     randz           ;; do rand if not hold
        add     cx,9248H        ;;     /
        ror     cx,1            ;;    /
        ror     cx,1            ;;   /
        ror     cx,1            ;;  /
randz:  mov     rseed,cx        ;; /
        endm
;--------------------------------------------------------------
; generate a random number in DX
; 
randdx  macro
        local   randz
        mov     dx,rseed        ;; get next raw seed
        test    rhold,-1        ;; test for hold
        jnz     randz           ;; do rand if not hold
        add     dx,9248H        ;;     /
        ror     dx,1            ;;    /
        ror     dx,1            ;;   /
        ror     dx,1            ;;  /
randz:  mov     rseed,dx        ;; /
        endm
;--------------------------------------------------------------
; move value in first register to range specified by 2nd register
; locks up if 2nd register (max) = 0
;
range   macro   reg,max 
        local   rang0,rangx             ;; labels
rang0:  cmp     reg,max                 ;; within range?
        jb      rangx                   ;; yes, exit
        sub     reg,max                 ;; no, foldover
        jmp     short rang0             ;; check again
rangx:  ;;
        endm
;--------------------------------------------------------------
; normalize value in al to range 0 - 11 (octave)
; trashes dl, al; results in ah
;
normal  macro   reg
        local   normx
        mov     ah,0                    ;; load for 0
        or      al,al                   ;; zero not allowed
        jz      normx                   ;; don't mod if 0
        mov     dl,12                   ;; for octave
        div     dl                      ;; mod 12
normx:  ;;
        endm
;--------------------------------------------------------------
; sort byte values in al, ah, dl, dh, in acending order
;
sort4   macro
        local   sorta,sortb,sortc,sortd,sorte,sortf
        cmp     al,ah           ;; sort al,ah,dl,dh
        jbe     sorta           ;; \
        xchg    al,ah           ;;  \
sorta:  cmp     al,dl           ;;   \
        jbe     sortb           ;;    \
        xchg    al,dl           ;;     \
sortb:  cmp     al,dh           ;;      \  
        jbe     sortc           ;;       \ 
        xchg    al,dh           ;;        \
sortc:  cmp     ah,dl           ;;         >
        jbe     sortd           ;;        /
        xchg    ah,dl           ;;       /
sortd:  cmp     ah,dh           ;;      /
        jbe     sorte           ;;     /
        xchg    ah,dh           ;;    /
sorte:  cmp     dl,dh           ;;   /
        jbe     sortf           ;;  /
        xchg    dl,dh           ;; /
sortf:  ;;
        endm
;--------------------------------------------------------------
; initialize a variable to a number
;
initv   macro   vnum,inum
        mov     vnum[di],offset word ptr dgroup:@zero+(inum*2)
        endm
;--------------------------------------------------------------
; fetch a variable to a register
;
getv    macro   reg,vnum
        mov     bx,vnum[di]     ;; point to var addr
        mov     reg,[bx]        ;; move the value to the register
        endm
;--------------------------------------------------------------
; test variable against a byte
;
testv   macro   vnum,tnum
        mov     bx,vnum[di]     ;; point to var addr
        test    byte ptr [bx],tnum 
        endm
;--------------------------------------------------------------
; test for hold by nz in variable, or by master hold flag
; returns nz if hold
;
hold    macro   vnum
        mov     bx,vnum[di]     ;; point to var addr
        mov     bl,[bx]         ;; get it
        or      bl,mprstf       ;; check master reset
        endm
;--------------------------------------------------------------
; store a register into the output byte
;
putn    macro   reg
        mov     outn[di],reg
        endm
;--------------------------------------------------------------
; clock tick routine, exits cy set if tick, else cy clr
; uses bit 0 of hi byte of output as flag, other bits undistrubed
; call with 1st value the clock input, the 2nd value local storage
; identical to "TICK"
;-------------------------------------------------------------------
tick    macro   varx,varz
        local   tick1,tick2,tick3;; labels 
        mov     bx,varx[di]     ;; get clock variable
        test    byte ptr [bx],-1;; clock on or off
        jz      tick1          ;; branch if it was off
        test    byte ptr varz[di],1;; new one?
        jnz     tick2          ;; no, exit clock off
        or      byte ptr varz[di],1;; yes, set Last
        stc                    ;; clock on
        jmp     short tick3    ;; branch
tick1:  and     byte ptr varz[di],0FEH;; clk off, clear Last
tick2:  clc                     ;; clock off 
tick3   =       $
        endm
;--------------------------------------------------------------
; clock tick routine, exits cy set if tick, else cy clr
; uses bit 1 of hi byte of output as flag, other bits undistrubed
; call with 1st value the clock input, the 2nd value local storage
; identical to "TICK", except bit 1 of 2nd variable is flag
;-------------------------------------------------------------------
tickb1  macro   varx,varz
        local   tick1,tick2,tick3;; labels 
        mov     bx,varx[di]     ;; get clock variable
        test    byte ptr [bx],-1;; clock on or off
        jz      tick1          ;; branch if it was off
        test    byte ptr varz[di],2;; new one?
        jnz     tick2          ;; no, exit clock off
        or      byte ptr varz[di],2;; yes, set Last
        stc                    ;; clock on
        jmp     short tick3    ;; branch
tick1:  and     byte ptr varz[di],0FDH;; clk off, clear Last
tick2:  clc                     ;; clock off 
tick3   =       $
        endm
;--------------------------------------------------------------
; clock tick routine, exits cy set if tick, else cy clr
; uses bit 2 of hi byte of output as flag, other bits undistrubed
; call with 1st value the clock input, the 2nd value local storage
; identical to "TICK", except bit 2 of 2nd variable is flag
;-------------------------------------------------------------------
tickb2  macro   varx,varz
        local   tick1,tick2,tick3;; labels 
        mov     bx,varx[di]     ;; get clock variable
        test    byte ptr [bx],-1;; clock on or off
        jz      tick1          ;; branch if it was off
        test    byte ptr varz[di],4;; new one?
        jnz     tick2          ;; no, exit clock off
        or      byte ptr varz[di],4;; yes, set Last
        stc                    ;; clock on
        jmp     short tick3    ;; branch
tick1:  and     byte ptr varz[di],0FBH;; clk off, clear Last
tick2:  clc                     ;; clock off 
tick3   =       $
        endm
;--------------------------------------------------------------
; clock tick routine, exits cy set if tick, else cy clr
; uses bit 3 of hi byte of output as flag, other bits undistrubed
; call with 1st value the clock input, the 2nd value local storage
; identical to "TICK", except bit 3 of 2nd variable is flag
;-------------------------------------------------------------------
tickb3  macro   varx,varz
        local   tick1,tick2,tick3;; labels 
        mov     bx,varx[di]     ;; get clock variable
        test    byte ptr [bx],-1;; clock on or off
        jz      tick1          ;; branch if it was off
        test    byte ptr varz[di],8;; new one?
        jnz     tick2          ;; no, exit clock off
        or      byte ptr varz[di],8;; yes, set Last
        stc                    ;; clock on
        jmp     short tick3    ;; branch
tick1:  and     byte ptr varz[di],0F7H;; clk off, clear Last
tick2:  clc                     ;; clock off 
tick3   =       $
        endm
;--------------------------------------------------------------
; clock tick routine, exits cy set if tick, else cy clr
; uses bit 3 of hi byte of output as flag, other bits undistrubed
; call with 1st value the clock input, the 2nd value local storage
; identical to "TICK", except bit 3 of 2nd variable is flag
;-------------------------------------------------------------------
tickb4  macro   varx,varz
        local   tick1,tick2,tick3;; labels 
        mov     bx,varx[di]     ;; get clock variable
        test    byte ptr [bx],-1;; clock on or off
        jz      tick1          ;; branch if it was off
        test    byte ptr varz[di],10h;; new one?
        jnz     tick2          ;; no, exit clock off
        or      byte ptr varz[di],10h;; yes, set Last
        stc                    ;; clock on
        jmp     short tick3    ;; branch
tick1:  and     byte ptr varz[di],0EFH;; clk off, clear Last
tick2:  clc                     ;; clock off 
tick3   =       $
        endm
;--------------------------------------------------------------
; set es:bx to screen tag address
;
gettag  macro
        mov     es,4[di]        ;; get seg addr
        mov     bx,6[di]        ;; get module screen address
        endm
;--------------------------------------------------------------
; convert binary in al to hex in ax
;
tohex   macro
        rol     al,1
        rol     al,1
        rol     al,1
        rol     al,1
        mov     ah,al
        and     al,0FH
        daa
        add     al,0F0H
        adc     al,040H
        ;
        xchg    al,ah
        rol     al,1
        rol     al,1
        rol     al,1
        rol     al,1
        and     al,00FH
        daa
        add     al,0F0H
        adc     al,040H
        endm
;--------------------------------------------------------------
; get next byte from pass buffer and send to midi
; does nothing if midi output port is busy  
; or if the buffer is empty
; 
sendmb  macro
        call    sendm
        endm
;--------------------------------------------------------------
; set channel flag, strip ms channel information
;
mchan   macro   reg
        mov     midip,reg
        and     reg,0FH
        endm
;--------------------------------------------------------------
; The normal exit routine for all modules.
; if the hi byte of the output variable is different from the 
; low byte, the low byte is displayed on the screen, and put
; into the hi byte.  Then the MIDI output routine is invoked,
; The module execution pointer (SI) is bumped, the variable
; list pointer (DI) for the next module is set up, and control
; is passed to next module on the execution list.
;
nextv   macro
        jmp     _nextv          ;; jump, rather copy the code
        endm
;--------------------------------------------------------------
@nextv  macro
        local   next1           ;; label
        mov     al,outn[di]     ;; get low byte of output
        cmp     al,outn+1[di]   ;; same as before ?
        jz      next1           ;; yes, branch
        mov     outn+1[di],al   ;; no, set up "done"
        mov     es,4[di]        ;; get seg addr
        mov     bx,6[di]        ;; get module screen address
        tohex                   ;; convert to hex word
        mov     es:320[bx],ah   ;; put to the screen
        mov     es:322[bx],al   ;; /
next1:  sendmb                  ;; send next midi buffer byte
        add     si,4            ;; point to next module exe addr
        mov     di,2[si]        ;; set di pointing to variable list
        jmp     [si]            ;; go for it
        endm
;--------------------------------------------------------------
; The "easy exit" routine for modules.
; No screen update. The MIDI output routine is invoked,
; The module execution pointer (SI) is bumped, the variable
; list pointer (DI) for the next module is set up, and control
; is passed to next module on the execution list.
;
nextx   macro
        jmp     _nextx          ;; jump, rather copy the code
        endm
;--------------------------------------------------------------
@nextx  macro
        sendmb                  ;; send next midi buffer byte
        add     si,4            ;; point to next module exe addr
        mov     di,2[si]        ;; set di pointing to variable list
        jmp     [si]            ;; go for it
        endm
;--------------------------------------------------------------
; This is the same as NEXTV, but it also sets the red led
; If the hi byte of the output variable is different from the 
; low byte, the low byte is displayed on the screen, and put
; into the hi byte. Next, the low byte is tested; if NZ, the
; LED is turned on, else it is turned on.
; Then the MIDI output routine is invoked.
; The module execution pointer (SI) is bumped, the variable
; list pointer (DI) for the next module is set up, and control
; is passed to next module on the execution list.
;
nextl   macro
        jmp     _nextl          ;; jump, rather copy the code
        endm
;--------------------------------------------------------------
@nextl  macro
        local   next1,next2     ;; labels
        mov     al,outn[di]     ;; get low byte of output
        cmp     al,outn+1[di]   ;; same as before ?
        jz      next1           ;; yes, branch
        mov     outn+1[di],al   ;; no, set up "done"
        mov     es,4[di]        ;; get seg addr
        mov     bx,6[di]        ;; get module screen address
        mov     dl,al           ;; save in dl
        tohex                   ;; convert to hex word
        mov     es:320[bx],ah   ;; put to the screen
        mov     es:322[bx],al   ;; /
        dec     bx              ;; point to led
        or      dl,dl           ;; test for z
        mov     al,red          ;; set up for z
        jz      next2           ;; branch if z
        mov     al,red+hi       ;; else set up for nz
next2:  mov     es:[bx],al      ;; set the led
next1:  sendmb                  ;; send next midi buffer byte
        add     si,4            ;; point to next module exe addr
        mov     di,2[si]        ;; set di pointing to variable list
        jmp     [si]            ;; go for it
        endm
;--------------------------------------------------------------
; This NEXT is used when the output is to be a one-shot, only
; set hi for one cycle.
; If the output is low when the routine is called, it does nothing.
; If the output is hi, it displays & sends to MIDI, upon the next
; cycle, the output will be set to low.
;
nextt   macro
        jmp     _nextt          ;; jump, rather copy the code
        endm
;--------------------------------------------------------------
@nextt  macro
        local   next1,next2,next3 ;; labels
        mov     ax,outn[di]     ;; get hi & low byte of output
        or      al,ah           ;; anything there 
        jz      next1           ;; no, easy exit
        ;;
        test    ah,-1           ;; 2nd time around?
        mov     ah,al           ;; (set up)
        jz      next3           ;; no, branch
        mov     ax,0            ;; yes, zip it
        ;;
next3:  mov     outn[di],ax     ;; /
        mov     es,4[di]        ;; get seg addr
        mov     bx,6[di]        ;; get module screen address
        mov     dl,al           ;; save in dl
        tohex                   ;; convert to hex word
        mov     es:320[bx],ah   ;; put to the screen
        mov     es:322[bx],al   ;; /
        dec     bx              ;; point to led
        or      dl,dl           ;; test for z
        mov     al,red          ;; set up for z
        jz      next2           ;; branch if z
        mov     al,red+hi       ;; else set up for nz
next2:  mov     es:[bx],al      ;; set the led
next1:  sendmb                  ;; send next midi buffer byte
        add     si,4            ;; point to next module exe addr
        mov     di,2[si]        ;; set di pointing to variable list
        jmp     [si]            ;; go for it
        endm
;--------------------------------------------------------------
; macro to display a magenta input value
; displays ls byte at value location
; assumes si = 6[di], es = 4[di]
;
showpv  macro   value
        mov     al,((value*2)+var0)[di]    ;; get the binary value
        tohex                              ;; convert to hex in ax
        mov     es:((value*160)+480)[si],ah;; put it to the screen
        mov     es:((value*160)+482)[si],al;; put it to the screen
        endm
;--------------------------------------------------------------
; macro to display a magenta input value
; displays ms byte at value location + 1
; assumes si = 6[di], es = 4[di]
;
showpp  macro   value
        mov     al,((value*2)+var0+1)[di]  ;; get the binary value
        tohex                              ;; convert to hex in ax
        mov     es:((value*160)+480)[si],ah;; put it to the screen
        mov     es:((value*160)+482)[si],al;; put it to the screen
        endm
;--------------------------------------------------------------
; macro to display a magenta input value, only if value <> value+1
; displays ls byte at value location, sets value = value+1
; assumes si = 6[di], es = 4[di]
;
showpq  macro   value
        local   boogie                     ;; label for exit
        mov     al,((value*2)+var0)[di]    ;; get the binary value
        cmp     al,((value*2)+var0+1)[di]  ;; changed?
        jz      boogie                     ;; no, split
        mov     ((value*2)+var0+1)[di],al  ;; set value+1 <-- value
        tohex                              ;; convert to hex in ax
        mov     es:((value*160)+480)[si],ah;; put it to the screen
        mov     es:((value*160)+482)[si],al;; put it to the screen
boogie  = $
        endm
