	TITLE	Modules for Modular Sequencer
	NAME	MBA
	.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
;==============================================================
BUFPA   SEGMENT
        if      debug
        db      7fffh dup (?)
        db      7fffh dup (?)
        else
        db      7fffh dup (0)
        db      7fffh dup (0)
        endif
BUFPA   ENDS
;--------------------------------------------------------------
BUFSP   SEGMENT
        if      debug
        db      4096 dup (?)
        else
        db      4096 dup (0)
        endif
BUFSP   ENDS
;--------------------------------------------------------------
BUFFS   SEGMENT
        if      debug
        db      7fffh dup (?)
        db      7fffh dup (?)
        else
        db      7fffh dup (0)
        db      7fffh dup (0)
        endif
BUFFS   ENDS
;--------------------------------------------------------------
BUFTU   SEGMENT
        if      debug
        db      4096 dup (?)
        else
        db      4096 dup (0)
        endif
BUFTU   ENDS
;==============================================================
_DATA   SEGMENT
        ASSUME DS:DGROUP, CS:_TEXT
;--------------------------------------------------------------
        extrn   ticka:byte,tickis:byte,fastflg:byte
        extrn   _header:byte
        extrn   varsav:word,cmdflg:byte,special:word,cmdcnt:byte
        extrn   locsav:word,cmdloc:word,vpage:word,curadr:word
        extrn   midip:byte,valflg:byte,asensf:byte
        extrn   @zero:near,magflg:byte,usrflg:byte,holdv:word
        extrn   colr:byte,lodflg:byte
        extrn   modnum:byte,doesc:word,clrchf:byte,curonf:byte
;--------------------------------------------------------------
        if      debug
        extrn   show0:word,show1:word,show2:word,show3:word
        endif
;--------------------------------------------------------------
        public midisf,midixsf,mpmodf,mpadrf,mpadr
        public mmcount,mmtick,mmreset,mmstart
;--------------------------------------------------------------
midisf  db      0                       ; 0=no sync, 1=send sync
midixsf db      0                       ; 0=not xtrn sync, NZ=is
mpmodf  db      1                       ; master program mode flag
mpadrf  db      0                       ; nz if change in prog address
mpadr   db      0                       ; master program address
mmcount dw      0                       ; master measure count
mmtick  dw      1                       ; ticks left in current measure
mmreset db      1                       ; master measure reset flag
mmstart db      1                       ; master measurd start flag
;--------------------------------------------------------------
        public rseed,rhold,notetbl,notes
        public xcax,xcbx,xccx,xcdx
        public temp0,temp1,temp2,temp3
;--------------------------------------------------------------
rseed   dw      0                       ; random number seed
rhold   db      0                       ; nz = holding          
notes   dw      4186,4435,4699,4978,5274,5588,5920,6272,6645,7040,7459,7902
;
;--------------------------------------------------------------
; notes --> clocks
;
; nv =      0    1   2   3   4   5   6   7
;
notetbl db 192, 96, 48, 24, 12,  6,  3,  2 ; normal
        db 128, 64, 32, 16,  8,  4,  2,  2 ; dotted
        db 255,144, 72, 36, 18,  9,  5,  3 ; tripplett
;--------------------------------------------------------------
;
xcax    dw      0       ; register storage, used by xcall
xcbx    dw      0 
xccx    dw      0
xcdx    dw      0
;
temp0   db      0       ; temp storage, use within module
temp1   db      0
temp2   db      0
temp3   db      0
;--------------------------------------------------------------
; the following are saved/loaded
;--------------------------------------------------------------
        public interv,mvlsav,mvlnum,mute,mutef,mprstf
        public mbeat,mnote,mtempo,mclocks
        public ctrlmap,pcva,pcvb,pcvc,pcvd,pcve,pcvf
;--------------------------------------------------------------
mvlsav  equ     $                       ; start of mod values to save
interv  db      64 dup(0)               ; intervals for modulation
        ;
mprstf  db      0                       ; master program reset flag
mute    dw      -1                      ; channel mute flags
mutef   db      1                       ; 1=mute, 0=solo flag
        ;
mbeat   db      4                       ; master beats/measure
mnote   db      24                      ; master note value
mtempo  db      78H                     ; master tempo
mclocks dw      96                      ; master clocks/measure
        ;
pcva    db      16 dup (0)              ; values sent to controller
pcvb    db      16 dup (0)              ; values sent to controller
pcvc    db      16 dup (0)              ; values sent to controller
pcvd    db      16 dup (0)              ; values sent to controller
pcve    db      16 dup (0)              ; values sent to controller
pcvf    db      16 dup (0)              ; values sent to controller
pcvg    db      16 dup (0)              ; values sent to controller
pcvh    db      16 dup (0)              ; values sent to controller
pcvx    db      16 dup (0)              ; values sent to controller
pcvy    db      16 dup (0)              ; values sent to controller
pcvz    db      16 dup (0)              ; values sent to controller
        ;
ctrlmap db      7,1,2,3,4,5,64,65       ; midi controller map
ctrlmpp db      7,1,2,3,4,5,64,65       ; midi controller map
        ;
mvlnum  equ     $-mvlsav                ; 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.
slewe   equ var22               ; programmer slew buffer
slewf   equ slewe+6             ; etc.
slewg   equ slewf+6             ; etc.
slewh   equ slewg+6             ; etc.
pubuf   equ slewh+6             ; programmer undo buffer
psbuf   equ pubuf+16            ; 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   midatix:byte,misend:byte
        extrn   _cancel:near,workx:near,split:near
        extrn   turnon:near,turnoff:near,noop:near,curon:near,curoff:near
        extrn   mstart:byte,mstop:byte,mcont:byte,midata:byte,miflag:byte
        extrn   sendm:near,alloff:near,tomidi:near,allmidi:near,allclr:near
        extrn   tstmob:near,allclr:near,_mpuinf:byte,_mpuinm:byte
;==============================================================
        include	macros.asm
;==============================================================
; THE MODULES
;==============================================================

;==============================================================
; Global Parameter Module
; (only one copy available)
; Output: Stop flag (is high for 1 cycle prior to HLT)
; Inputs:
; 0: NZ Sets master programmer reset flag
; 1: value --> master programmer address
; 2: NZ sets menu for solo, 0 sets mute
; 3: NZ sets Program-mode flag to Program, Z sets to Play
; 4: Beats Per measure, change recaluclates tick-count "ticka"
; 5: Note value for above, change recaluclates tick-count
; 6: Tempo, change reclalculates tick-count
; 7: Reset RNG.  Any change sets seed to new value
; 8: bit 0 set MIDI output sync, bit 1 sets input sync
; 9: NZ transition causes HLT, if FF then BYE 
; 10 : initialized flag
; 10+: 
; 11 : Copy of ticks remaining
; 11+: Transition (tick) flags
;--------------------------------------------------------------
        public _alpha
_alpha: cmp     byte ptr var10[di],0ABh  ; initialized yet?
        jz      alphaz                   ; yes, go on
        mov     byte ptr var10[di],0ABh  ; no, set init values
        initv   var3,1          ; prog  <-- 1
        initv   var4,4          ; beats <-- 4
        initv   var5,3          ; note  <-- 3
        initv   var6,78h        ; tempo <-- 78h 
alphaz:
;--------------------------------------------------------------
; 0: NZ Sets master programmer reset flag, Z releases
;
        mov     al,mprstf       ; get master reset flag
        and     al,0FEH         ; clear alpha reset bit
        testv   var0,-1         ; check for Z/NZ
        jz      alpha0a         ; branch if Z
        or      al,1            ; ...if NZ
        mov     mmcount,0       ; clear measure count
        mov     cs:seconds,0    ; reset clock
        mov     cs:secondf,2    ; flag to show it
        mov     mmtick,1        ; reset to 1st note in measure
alpha0a:mov     mprstf,al       ; set/reset the flag
;--------------------------------------------------------------
; 1: value --> master programmer address
;
        getv    al,var1         ; get the value
        cmp     al,mpadr        ; any change
        mov     ah,0            ; setup to clear change flag
        jz      alpha1a         ; no, branch
        mov     ah,1            ; yes, set change flag
        mov     mpadr,al        ; set new address
alpha1a:mov     mpadrf,ah       ; flag change/no change
;--------------------------------------------------------------
; 2: NZ sets menu for solo, 0 sets mute
;
        mov     al,1            ; set for mute
        testv   var2,-1         ; get the flag
        jz      alpha2a         ; branch if solo
        mov     al,0            ; else set for solo
alpha2a:mov     mutef,al        ; set it
;--------------------------------------------------------------
; 3: NZ sets Program-mode flag to Program, Z sets to Play
;
        testv   var3,-1         ; check for Z/NZ
        mov     al,0            ; ...if Z
        jz      alpha3a         ; branch if Z
        inc     al              ; ...if NZ
alpha3a:mov     mpmodf,al       ; set/reset the flag
;--------------------------------------------------------------
; 4: Beats Per measure, change recaluclates 
;
        getv    al,var4         ; get beats per measure
        cmp     mbeat,al        ; compare with master beats/measure
        jz      alpha4z         ; boogie if no change
        or      al,al           ; don't allow 0 either
        jz      alpha4z         ; /
        mov     mbeat,al        ; else set new beats/measure
        mov     ah,mnote        ; get clocks per note
        mul     ah              ; ax = clocks/measure        
        mov     mclocks,ax      ; put away
alpha4z:
;--------------------------------------------------------------
; 5: Note value for above, change recaluclates
;
        getv    bl,var5         ; get note value
        cmp     mnote,bl        ; compare with master note value
        jz      alpha5z         ; boogie if no change
        cmp     bl,5            ; 0-5 allowed
        ja      alpha5z         ; do nothing if > 6
        mov     bh,0            ; else xlate to clocks
        add     bx,offset dgroup:notetbl; /
        mov     al,[bx]         ; al = clocks
        mov     mnote,al        ; set new clocks/note
        mov     ah,mbeat        ; get beats/measure
        mul     ah              ; ax = clocks/measure        
        mov     mclocks,ax      ; put away
alpha5z:
;--------------------------------------------------------------
; 6: Tempo, change reclalculates tick-count
;
        getv    dl,var6         ; get beats per measure
        mov     fastflg,0       ; clear fast flag
        cmp     dl,0ffh         ; fast as possible?
        jnz     alpha6b         ; no, branch
        mov     fastflg,1       ; yes, set flag 
alpha6b:cmp     mtempo,dl       ; compare with master tempo
        jz      alpha6z         ; boogie if no change
        mov     mtempo,dl       ; else set new value
        cmp     dl,5            ; must be > 5
        ja      alpha6a         ; branch if ok
        mov     dl,6            ; else make it 6
alpha6a:mov     ax,1456         ; calculate loop ticks
        div     dl              ; /
        mov     ticka,al        ; put it in the timer
alpha6z:
;--------------------------------------------------------------
; 7: Reset RNG.  Any change sets seed to new value
;
        getv    al,var7         ; get rng seed
        or      al,al           ; is zero?
        jz      alpha7z         ; yes, don't set new seed
        ror     al,1            ; 1 --> 80H, etc.
        mov     ah,al           ; 255 possible seeds
        mov     rseed,ax        ; set the new seed
alpha7z:mov     rhold,al        ; set/clear hold flag
;--------------------------------------------------------------
; 8: bit 0 sets output sync, bit 1 sets input sync
;
        test    valflg,-1       ; inputting values now?
        jnz     alpha8d         ; yes, don't do nothin 
        getv    dl,var8         ; get the flag
        mov     ax,0            ; clear sync flags
        test    dl,1            ; output sync?
        jz      alpha8a         ; no, branch
        mov     al,1            ; yes, set out sync flag
alpha8a:test    dl,2            ; input sync?
        jz      alpha8b         ; no, branch
        mov     ah,1            ; yes, set input sync flag
alpha8b:mov     midisf,al       ; set sync out flag
        mov     midixsf,ah      ; set external sync flag
        test    dl,6            ; defeat active sensing?
        mov     al,1            ; ...setup for no defeat
        jz      alpha8c         ; no, branch
        mov     al,0            ; yes, clear active sensing flag
alpha8c:mov     asensf,al       ; set the flag
alpha8d:
;--------------------------------------------------------------
; 9: NZ transition causes HLT.
;    Value of FF causes BYE.
;
        tick    var9,var11+1   ; clock tick
        jc      alpha9c        ; /
        jmp     alpha9z        ; boogie if no tick
alpha9c:
        getv    al,var9        ; check for FF (split)
        cmp     al,0ffh        ; wanna split?
        jz      alpha9e        ; yes, do it
        jmp     alpha9a        ; no, branch to HLT
alpha9e:
        call    alloff          ; process all notes off
        jmp     split           ; become history
alpha9a:                        ; HLT
        mov     bx,6E0H         ; menu text offset
        call    turnon          ; turn on the menu text
        or      mprstf,2        ; set HLT flag
        mov     al,0fch         ; send MIDI STOP command
        call    allmidi         ; /
alpha9z:
;--------------------------------------------------------------
; display number of calculation clocks available
;
alpha11:mov     al,tickis       ; get ticks remaining
        cmp     al,var11[di]    ; same as before?
        jz      alpha11z        ; yes, boogie
        mov     var11[di],al    ; no, save new value
        gettag                  ; es:bx = screen addr of tag
        or      al,al           ; test for bottoming out
        jz      alpha11a        ; branch if Z
        and     byte ptr es:(160*13-1)[bx],0F7H; lowlight if NZ
        jmp     short alpha11b  ; then branch around highlight
alpha11a:or     byte ptr es:(160*13-1)[bx],8; highlight if Z
alpha11b:tohex                  ; convert to hex word
        mov     es:(160*13)[bx],ah; put to the screen
        mov     es:(160*13+2)[bx],al; /
alpha11z:
;--------------------------------------------------------------
; shunt stop flag to output
;
alphax: mov     al,mprstf       ; get the flag
        shr     al,1            ; HLT is bit 2
        putn    al              ; send it out
        nextl                   ; show it

;==============================================================
; absolute time counter
; output: ticks count AND input0
; input: 0: hold, 1: period,  2: AND mask
;
;--------------------------------------------------------------
        public _beta
_beta:  hold    var0            ; hold?
        jz      beta0           ; no, branch
        mov     al,0            ; yes, clear clock
        jmp     short betax     ; exit
        ;
beta0:  mov     ax,cs:ticks     ; get timer value
        getv    cl,var1         ; get period
        shr     ax,cl           ; bump
        getv    ah,var2         ; get AND mask
        and     al,ah           ; AND them
betax:  putn    al              ; send to output
        nextl
;==============================================================
; seconds counter
; output: seconds count AND input0
; input: 0: hold/reset
;        1: nz = minutes
;        2:  offset
;
;--------------------------------------------------------------
        public _secs
_secs:  hold    var0            ; hold?
        jz      secs0           ; no, branch
        mov     ax,cs:seconds   ; yes, get current second count
        mov     var2[di],ax     ; set new time offset
        jmp     short secsz     ; exit
        ;
secs0:  mov     ax,cs:seconds   ; get timer value
        sub     ax,var2[di]     ; subtract offset
        mov     dx,0            ; double word
        getv    ch,var1         ; get clock flag
        test    ch,-1           ; leave free count?
        jz      secsx           ; yes, branch
        mov     bx,60           ; no,/60
        div     bx              ; /
        cmp     ch,1            ; want seconds?
        jnz     secsx           ; no, do minutes
        mov     al,dl           ; yes, mod for secs
secsx:  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
        ;
        cmp     ch,1            ; want seconds?
        jnz     secsz           ; no, branch
        mov     byte ptr es:[bx],':'; yes, print ':' 
secsz:  nextv
;==============================================================
; simple loop counter
; output: loop count AND input0
; input: 0: hold, 1: period,  2: AND mask
;
;--------------------------------------------------------------
        public _lambda
_lambda:hold    var0            ; hold?
        jz      lambda0         ; no, branch
        mov     al,0            ; yes, clear clock
        jmp     short lambdax   ; exit
        ;
lambda0:mov     ax,cs:loops     ; get count
        getv    cl,var1         ; get period
        shr     ax,cl           ; bump
        getv    ah,var2         ; get AND mask
        and     al,ah           ; AND them
lambdax:putn    al              ; send to output
        nextl
;==============================================================
; measure clock
; output: measure start strobe, with delay
; input:
; 0:    Hold
; 1:    delay value
; 2x:   countdown
; 2z:   trip flag
;--------------------------------------------------------------
        public  _mclk
_mclk:  mov     byte ptr outn[di],0; clear output
        hold    var0            ; hold?
        jnz     mclkx           ; yes, exit
        ;
        mov     al,var2+1[di]   ; trip flag on?
        or      al,mmstart      ; or measure start?
        jz      mclkx           ; no, exit
        ;
        test    byte ptr var2+1[di],1; trip flag?
        jnz     mclk0                ; yes, go countdown
        getv    al,var1              ; no, get delay value
        inc     al                   ; 0 --> 1
        mov     var2[di],al          ; set new countdown
        mov     byte ptr var2+1[di],1; set trip flag
        ;
mclk0:  dec     byte ptr var2[di]    ; count down
        jnz     mclkx                ; exit if not end of count
        mov     byte ptr var2+1[di],0; else clear trip flag
        mov     byte ptr outn[di],1  ; set output
        ;
mclkx:  nextl                   ; exit
;==============================================================
; measure clock
; output: clock strobe
; input: 0: reset, 1: count, 2x:countdown timer, 2z: copy of count
;
;--------------------------------------------------------------
        public _mclock
_mclock:hold    var0            ; hold?
        jz      mclock2         ; no, branch
        getv    al,var1         ; yes, reset
        mov     ah,al           ;  /
        mov     var2[di],ax     ; /
        jmp     short mclockz   ; exit with output=0
        ;
mclock2:test    mmstart,1       ; measure start?
        jz      mclockz         ; no, exit
        mov     ah,1            ; yes, set output=1
        ;
        getv    al,var1         ; get current count
        cmp     al,var2+1[di]   ; same as before?
        jz      mclock1         ; yes, branch
        mov     var2+1[di],al   ; no, set new count
        ;
mclock0:mov     al,var2+1[di]   ; reset timer
        mov     var2[di],al     ; / 
        jmp     short mclockx   ; exit
        ;
mclock1:dec     byte ptr var2[di]; tick
        jz      mclock0          ; branch if end of count
mclockz:mov     ah,0             ; else set output=0
        ;
mclockx:mov     outn[di],ah      ; send output
        nextl                    ; exit
;==============================================================
; simple loop counter
; output: clock strobe
; input: 0: reset, 1: count, 2: offset
;        3x:countdown timer, 3z: copy of count
;--------------------------------------------------------------
        public _gamma
_gamma: testv   var1,-1         ; clock = 0
        jz      gammaz          ; yes, just exit
        hold    var0            ; hold?
        jz      gamma0          ; no, branch
        mov     al,byte ptr cs:loops; get count
        and     al,1            ; /
        mov     var3[di],al     ; reset clock counters
        jmp     short gammaz    ; split
        ;
gamma0: getv    ah,var1         ; get current count
        cmp     ah,var3+1[di]   ; same as before?
        jz      gamma1          ; yes, branch
        cmp     ah,1            ; set to 1?
        jnz     gamma3          ; no, branch
        mov     ah,2            ; yes, set to 2
gamma3: mov     al,byte ptr cs:loops; get count
        and     al,1            ; /
        mov     var3[di],ax     ; /
        ;
gamma1: getv    al,var2         ; get offset trigger
        cmp     al,ah           ; out of range?
        jb      gamma4          ; no, branch
        mov     al,0            ; yes, zip it
gamma4: cmp     al,var3[di]     ; same as count?
        jnz     gamma2          ; no, branch
        mov     byte ptr outn[di],1; yes, set output flag
        ;
gamma2: inc     byte ptr var3[di]; tick
        cmp     ah,var3[di]     ; reached count?
        jnz     gammaz          ; no, exit
        mov     byte ptr var3[di],0; yes, reset timer
gammaz: nextt                   ; exit

;==============================================================
; simple loop counter
; output: clock strobe
; input: 0: reset, 1: count
;        2x:countdown timer
;--------------------------------------------------------------
        public _muclk
_muclk: testv   var1,-1         ; clock = 0
        jz      muclkz          ; yes, just exit
        hold    var0            ; hold?
        jnz     muclk0          ; yes, reset & exit
        ;
        dec     byte ptr var2[di]; count down
        jnz     muclkz          ; exit if not 0
        mov     byte ptr outn[di],1; set output
muclk0: getv    al,var1         ; else get count
        mov     var2[di],al     ; put it away
muclkz: nextt                   ; exit
;==============================================================
; Note Clock
; output: 0/Veloc input value
; inputs:
; 0:    Reset & hold
; 1:    Sync pulse
; 2:    Note value 0-7, +10H tripplett, +20H dotted
; 3:    Sustain value 0-255
; 4:    Clock offset value
; 5:    Velocity value
; 6x:   note countdown
; 6+:   nz = wait for sync with measure
; 7x:   note-on clocks
; 7+:   AB = has been initialized
; 8x:   nz = sync countdown flag
; 8+:   offset value changed flag
;--------------------------------------------------------------

        public _clock,_klock
_klock: nop                             ; same routine, different address
_clock:
        cmp     byte ptr var7+1[di],0ABH; initialized yet?
        jz      clock00                 ; yes, branch
        mov     byte ptr var7+1[di],0ABH; no, initialize
        ;
        initv   var1,1                  ; sync
        initv   var2,3                  ; note
        initv   var5,1                  ; velocity
        mov     word ptr var6[di],0     ; flags
        mov     byte ptr var7[di],0     ; flags
        ;
clock00:hold    var0            ; hold?
        jnz     clock01                 ; yes, go do it
        getv    al,var4                 ; change in offset value?
        cmp     al,var8+1[di]           ; /
        jz      clock0                  ; no, branch
        ;
clock01:getv    al,var4                 ; get sync offset
        mov     var8+1[di],al           ; clear change flag
        inc     al                      ; bump sync count so 0 --> 1
        mov     var6+1[di],al           ; set up sync offset count
        mov     byte ptr outn[di],0     ; clear output
        mov     byte ptr var6[di],0     ; clear note count
        jmp     clockx                  ; exit
        ;
clock0: test    byte ptr var6+1[di],-1  ; waiting for sync?
        jz      clock1                  ; no, branch
        getv    al,var1                 ; sync start?
        or      al,byte ptr var8[di]    ; or countdown?
        jnz     clock0a                 ; yes, branch
        jmp     clockx                  ; no, exit
        ;
clock0a:mov     byte ptr var8[di],1     ; set offset flag    
        dec     byte ptr var6+1[di]     ; countdown sync
        jz      clock0b                 ; branch on if 0
        jmp     clockx                  ; else exit  
clock0b:mov     byte ptr var8[di],0     ; clear offset flag
        ;        
clock1: cmp     byte ptr var6[di],0     ; at end of note?
        jz      clock1b                 ; yes, branch
        jmp     clock2                  ; no, countdown
        ;
clock1b:getv    bl,var2                 ; get note value
        mov     dl,bl                   ; save in dl for dot/tripplet
        and     bl,7                    ; mask
        mov     bh,0                    ; else xlate to clocks
        add     bx,offset dgroup:notetbl; /
        test    dl,10h                  ; triplett?
        jz      clock1c                 ; no, branch
        add     bx,8                    ; yes, bump table addr
        jmp     short clock1d           ; branch around dot
clock1c:test    dl,20h                  ; dot?
        jz      clock1d                 ; no, branch
        add     bx,16                   ; yes, bump table addr
clock1d:;
        mov     cl,[bx]                 ; cl = clocks
        getv    dl,var3                 ; dl = sustain value
        ;
        mov     al,1                    ; al = 1 clock
        cmp     dl,0                    ; sustain = min
        jz      clock1e                 ; yse, exit sust.
        ;
        mov     al,cl                   ; al = nv (clocks)
        cmp     dl,0ffh                 ; sustain = max
        jz      clock1e                 ; yes, exit legato
        ;
        mul     dl                      ; ax = nv * sust
        mov     al,ah                   ; al = nv * sust / 256
        or      al,al                   ; /
        jnz     clock1e                 ; go if > 0
        inc     al                      ; else make 1
        ;
clock1e:mov     var6[di],cl             ; set new note value
        sub     cl,al                   ; off = nv - on
        mov     var7[di],cl             ; set new off-time
        jmp     short clock2a           ; send out the note
        ;
clock2: dec     byte ptr var6[di]       ; count down note
        jnz     clock2a                 ; branch if not eon
        jmp     clock1b                 ; if end of note, do a new one
clock2a:mov     al,0                    ; setup for note-off
        mov     ah,var6[di]             ; get note clock countdown
        cmp     ah,var7[di]             ; > note-off time?
        jbe     clock2b                 ; no, send note off
        getv    al,var5                 ; yes, send velocity    
clock2b:putn    al                      ; send to output
clockx: nextl                           ; exit
;==============================================================
; Loop Timer
; output: delayed pulse
; inputs: 0: strobe, 1: delay, 2: hold
;         3x: strobe clock, 4x: delay count, 4z: hold count
;--------------------------------------------------------------
        public  _ltimer
_ltimer:
        test    byte ptr var4[di],-1    ; delay high?
        jz      ltimer1                 ; no, branch
        dec     byte ptr var4[di]       ; yes, count down
        jnz     ltimerz                 ; exit if not finished
ltimer0:mov     byte ptr outn[di],1     ; else set output
        jmp     short ltimerz           ; then exit
        ;
ltimer1:test    byte ptr var4+1[di],-1  ; hold high?
        jz      ltimer2                 ; no, branch
        dec     byte ptr var4+1[di]     ; yes, count down
        jnz     ltimerz                 ; exit if not finished
        mov     byte ptr outn[di],0     ; else clear output
        jmp     short ltimerz           ; then exit
        ;
ltimer2:tick    var0,var3               ; strobe tick?
        jnc     ltimerz                 ; no, just exit
        getv    al,var1                 ; yes, get delay
        getv    ah,var2                 ; ...get hold
        or      ah,mprstf               ; /
        mov     var4[di],ax             ; set them up
        or      al,al                   ; delay = 0?
        jz      ltimer0                 ; yes, go set
        mov     byte ptr outn[di],0     ; else clear output
ltimerz:nextl                           ; exit
;==============================================================
; Clock Timer
; output: delayed pulse
; inputs: 0: clock, 1: strobe, 2: delay, 3: hold
;         4x: strobe clock, 5x: delay count, 5z: hold count
;--------------------------------------------------------------
        public  _ctimer
_ctimer:
        tickb1  var1,var4               ; strobe tick?
        jnc     ctimer2                 ; no, go on
        getv    al,var2                 ; yes, get delay
        getv    ah,var3                 ; ...get hold
        or      ah,mprstf               ; /
        mov     var5[di],ax             ; set them up
        or      al,al                   ; delay = 0?
        jz      ctimer0                 ; yes, go set
        mov     byte ptr outn[di],0     ; clear output
        jmp     short ctimerz           ; exit
        ;
ctimer2:tick    var0,var4               ; clock tick?
        jnc     ctimerz                 ; no, branch
        ;
        test    byte ptr var5[di],-1    ; delay high?
        jz      ctimer1                 ; no, branch
        dec     byte ptr var5[di]       ; yes, count down
        jnz     ctimerz                 ; exit if not finished
ctimer0:mov     byte ptr outn[di],1     ; else set output
        jmp     short ctimerz           ; then exit
        ;
ctimer1:test    byte ptr var5+1[di],-1  ; hold high?
        jz      ctimerz                 ; no, branch
        dec     byte ptr var5+1[di]     ; yes, count down
        jnz     ctimerz                 ; exit if not finished
        mov     byte ptr outn[di],0     ; else clear output
        ;
ctimerz:nextl                           ; exit
;==============================================================
; Abs Timer
; output: delayed pulse
; inputs: 0: strobe, 1: delay, 2: hold, 3: period
;         4: timer value
;         5x: b0=delay flag, b1=hold flag
;         5z: clock
;--------------------------------------------------------------
        public  _atimer
_atimer:
        test    byte ptr var5[di],1     ; delay high?
        jz      atimer1                 ; no, branch
        mov     ax,cs:ticks             ; yes, get timer value
        sub     ax,var4[di]             ; get lapsed time
        getv    dl,var1                 ; get delay time
        mov     dh,0                    ; /
        getv    cl,var3                 ; get period
        shl     dx,cl                   ; bump
        cmp     dx,ax                   ; out of gas?
        jnb     atimerz                 ; no, exit
        mov     ax,cs:ticks             ; get current ticks
        mov     var4[di],ax             ; save it
atimer0:mov     byte ptr outn[di],1     ; set output
        hold    var2                    ; hold?
        jz      atimer3                 ; no, clear flag & exit
        mov     byte ptr var5[di],2     ; yes, set hold flag
        jmp     short atimerz           ; then exit
        ;
atimer1:test    byte ptr var5[di],2     ; hold high?
        jz      atimer2                 ; no, branch
        mov     ax,cs:ticks             ; yes, get timer value
        sub     ax,var4[di]             ; get lapsed time
        getv    dl,var2                 ; get hold time
        mov     dh,0                    ; /
        getv    cl,var3                 ; get period
        shl     dx,cl                   ; bump
        cmp     dx,ax                   ; out of gas?
        jnb     atimerz                 ; no, exit
        mov     byte ptr outn[di],0     ; ...clear output
atimer3:mov     byte ptr var5[di],0     ; yes, clear flags
        jmp     short atimerz           ; then exit
        ;
atimer2:tick    var0,var5+1             ; strobe tick?
        jnc     atimerz                 ; no, just exit
        mov     ax,cs:ticks             ; get current ticks
        mov     var4[di],ax             ; save it
        testv   var1,-1                 ; delay = 0
        jz      atimer0                 ; yes, go set
        mov     byte ptr var5[di],1     ; else flag delay
        mov     byte ptr outn[di],0     ; ...clear output
atimerz:nextl                           ; exit
;==============================================================
; Seconds Timer
; output: delayed pulse
; inputs: 0: strobe, 1: delay, 2: hold, 3: period
;         4: timer value
;         5x: b0=delay flag, b1=hold flag
;         5z: clock
;--------------------------------------------------------------
        public  _stimer
_stimer:
        test    byte ptr var5[di],1     ; delay high?
        jz      stimer1                 ; no, branch
        mov     ax,cs:seconds           ; yes, get timer value
        sub     ax,var4[di]             ; get lapsed time
        getv    dl,var1                 ; get delay time
        mov     dh,0                    ; /
        getv    cl,var3                 ; get period
        shl     dx,cl                   ; bump
        cmp     dx,ax                   ; out of gas?
        jnb     stimerz                 ; no, exit
        mov     ax,cs:seconds           ; get current ticks
        mov     var4[di],ax             ; save it
stimer0:mov     byte ptr outn[di],1     ; set output
        hold    var2                    ; hold?
        jz      stimer3                 ; no, clear flag & exit
        mov     byte ptr var5[di],2     ; yes, set hold flag
        jmp     short stimerz           ; then exit
        ;
stimer1:test    byte ptr var5[di],2     ; hold high?
        jz      stimer2                 ; no, branch
        mov     ax,cs:seconds           ; yes, get timer value
        sub     ax,var4[di]             ; get lapsed time
        getv    dl,var2                 ; get hold time
        mov     dh,0                    ; /
        getv    cl,var3                 ; get period
        shl     dx,cl                   ; bump
        cmp     dx,ax                   ; out of gas?
        jnb     stimerz                 ; no, exit
        mov     byte ptr outn[di],0     ; ...clear output
stimer3:mov     byte ptr var5[di],0     ; yes, clear flags
        jmp     short stimerz           ; then exit
        ;
stimer2:tick    var0,var5+1             ; strobe tick?
        jnc     stimerz                 ; no, just exit
        mov     ax,cs:seconds           ; get current ticks
        mov     var4[di],ax             ; save it
        testv   var1,-1                 ; delay = 0
        jz      stimer0                 ; yes, go set
        mov     byte ptr var5[di],1     ; else flag delay
        mov     byte ptr outn[di],0     ; ...clear output
stimerz:nextl                           ; exit
;==============================================================
; interference clock
; inputs: 0:   hold/reset input
;         1-4: clock inputs
;         5x:
;         5z:  clock ticks
; output: strobe upon any clock input
;--------------------------------------------------------------
        public _iclock
_iclock:
        hold    var0                    ; hold?
        jz      iclock0                 ; no, branch
        jmp     iclockz                 ; else exit
        ;
iclock0:tick    var1,var5+1             ; clock
        jnc     iclock1                 ; branch if no tick
        mov     byte ptr outn[di],1     ; else set output
iclock1:
        tickb1  var2,var5+1             ; clock
        jnc     iclock2                 ; branch if no tick
        mov     byte ptr outn[di],1     ; else set output
iclock2:
        tickb2  var3,var5+1             ; clock
        jnc     iclock3                 ; branch if no tick
        mov     byte ptr outn[di],1     ; else set output
iclock3:
        tickb3  var4,var5+1             ; clock
        jnc     iclockz                 ; branch if no tick
        mov     byte ptr outn[di],1     ; else set output
iclockz:nextt                           ; exit
;==============================================================
; interference clock
; inputs: 0:   clock
;         1:   hold/reset input
;         2-5: count inputs
;         6x:
;         6z:  clock ticks
;         7-8: change detectors 
;         9-10:countdown registers    
; output: strobe upon any clock input
;--------------------------------------------------------------
        public _kclock
_kclock:
        hold    var1                    ; hold?
        jz      kclocka                 ; no, branch
kclockb:mov     ax,101H                 ; yes, clear registers
        mov     var9[di],ax             ;  /
        mov     var10[di],ax            ; /
        jmp     kclockz                 ; exit
        ;
kclocka:tick    var0,var6+1             ; clock
        jc      kclockd                 ; do it if tick
        jmp     kclockz                 ; branch if no tick
        ;
kclockd:getv    al,var2                 ; input change?
        getv    ah,var3                 ;  /
        cmp     ax,var7[di]             ; /
        jz      kclockc                 ; no, branch 
        mov     var7[di],ax             ; yes, reset
        jmp     short kclockb           ; /
kclockc:getv    al,var4                 ; input change?
        getv    ah,var5                 ;  /
        cmp     ax,var8[di]             ; /
        jz      kclock0                 ; no, branch 
        mov     var8[di],ax             ; yes, reset
        jmp     short kclockb           ; /
        ;
kclock0:test    byte ptr var9[di],-1    ; register zero'd?
        jz      kclock1                 ; yes, branch
        dec     byte ptr var9[di]       ; no, count down
        jnz     kclock1                 ; branch if not zero'd
        getv    al,var2                 ; else get count
        mov     byte ptr var9[di],al    ; reset register
        mov     byte ptr outn[di],1     ; set output strobe
        ;
kclock1:test    byte ptr var9+1[di],-1  ; register zero'd?
        jz      kclock2                 ; yes, branch
        dec     byte ptr var9+1[di]     ; no, count down
        jnz     kclock2                 ; branch if not zero'd
        getv    al,var3                 ; else get count
        mov     byte ptr var9+1[di],al  ; reset register
        mov     byte ptr outn[di],1     ; set output strobe
        ;
kclock2:test    byte ptr var10[di],-1   ; register zero'd?
        jz      kclock3                 ; yes, branch
        dec     byte ptr var10[di]      ; no, count down
        jnz     kclock3                 ; branch if not zero'd
        getv    al,var4                 ; else get count
        mov     byte ptr var10[di],al   ; reset register
        mov     byte ptr outn[di],1     ; set output strobe
        ;
kclock3:test    byte ptr var10+1[di],-1 ; register zero'd?
        jz      kclockz                 ; yes, branch
        dec     byte ptr var10+1[di]    ; no, count down
        jnz     kclockz                 ; branch if not zero'd
        getv    al,var5                 ; else get count
        mov     byte ptr var10+1[di],al ; reset register
        mov     byte ptr outn[di],1     ; set output strobe
        ;
kclockz:nextt                           ; exit
;==============================================================
; Index counter, incrementing
; inputs: 0: clock up, 1: hold/reset 2: inc/dec 3x: tick flag
; output: index
;--------------------------------------------------------------
        public _icount
_icount:
        hold    var1            ; hold?
        jz      icount0         ; branch if no hold
        mov     word ptr outn[di],100h; else zip output/index  
        jmp     short icountx   ; exit with video update
icount0:tick    var0,var3       ; clock tick
        jc      icount1         ; branch if there is a tick
        nextx                   ; else exit easy
icount1:testv   var2,-1         ; test inc/dec flag
        jnz     icount2         ; branch if decrement
        inc     word ptr outn[di]; inc index
        jmp     next            ; exit with video update
icount2:dec     word ptr outn[di]; dec index
icountx:nextv                   ; exit
;==============================================================
; Index counter, with mask
; inputs: 0: clock up,   1: hold/reset 2: mask
;         3x: tick flag, 3z: tick count     
; output: index
;--------------------------------------------------------------
        public _ocount
_ocount:
        hold    var1            ; hold?
        jz      ocount0         ; branch if no hold
        mov     byte ptr var3+1[di],0; else zip index  
        jmp     short ocountx   ; exit with video update
ocount0:tick    var0,var3       ; clock tick
        jnc     ocountx         ; branch if no tick
        inc     byte ptr var3+1[di]; inc index
ocountx:getv    al,var2         ; get the mask
        and     al,var3+1[di]   ; and with count
        putn    al              ; send it out
        nextv                   ; exit
;==============================================================
; Index counter, adding
; inputs: 0: clock up, 1: hold/reset 2: increment 3x: tick flag
; output: index
;--------------------------------------------------------------
        public _jcount
_jcount:
        hold    var1            ; hold?
        jz      jcount0         ; branch if no hold
        mov     word ptr outn[di],100H; else zip output/index  
jcountx:jmp     next            ; exit
jcount0:tick    var0,var3       ; clock tick
        jc      jcount1         ; branch if there is a tick
        nextx                   ; else exit easy
jcount1:getv    al,var2         ; get the increment
        add     outn[di],al     ; add to the index
        nextv                   ; exit with video update
;==============================================================
; Index counter, adding with upper limit
; inputs: 0: clock up, 1: hold/reset 2: increment 
;         3: inc/dec   4: top limit  5x: tick
; output: index
;--------------------------------------------------------------
        public _kcount
_kcount:
        hold    var1            ; hold?
        jz      kcount0         ; branch if no hold
        testv   var3,-1         ; counting up or down
        jnz     kcounta         ; branch if counting down
        mov     al,0            ; up, set to bottom limit
        jmp     short kcountb   ; /
kcounta:getv    al,var4         ; down, set to top limit
kcountb:mov     ah,al           ;   /
        mov     outn[di],ax     ; /
        jmp     next            ; exit
kcount0:tick    var0,var5       ; clock tick
        jc      kcount1         ; branch if there is a tick
        nextx                   ; else exit easy
kcount1:mov     dl,0            ; dl = 0 = bottom
        getv    dh,var4         ; dh = the top limit
        jmp     lcountk         ; continue with lcount routine
;==============================================================
; Index counter, adding with upper and lower limit
; inputs: 0: clock up, 1: hold/reset 2: increment 
;         3: inc/dec   4: top limit  5: bottom limit 
;         6x: tick
; output: index                 
;--------------------------------------------------------------
        public _lcount
_lcount:
        hold    var1            ; hold?
        jz      lcount0         ; branch if no hold
        testv   var3,-1         ; counting up or down
        jnz     lcounta         ; branch if counting down
        getv    al,var5         ; up, set to bottom limit
        jmp     short lcountb   ; /
lcounta:getv    al,var4         ; down, set to top limit
lcountb:mov     ah,al           ;   /
        mov     outn[di],ax     ; /
        jmp     next            ; exit
lcount0:tick    var0,var6       ; clock tick
        jc      lcount1         ; branch if there is a tick
        nextx                   ; else exit easy
lcount1:getv    dl,var5         ; dl = the lower limit
        getv    dh,var4         ; dh = the top limit
        cmp     dh,dl           ; top < bottom ?
        jnb     lcountk         ; no, branch
        xchg    dh,dl           ; yes, swap
lcountk:inc     dh              ; bump so top is included
        getv    al,var2         ; al = the increment
        getv    ah,var3         ; ah = inc/dec
        test    ah,-1           ; inc or dec?
        jnz     lcount2         ; branch if dec
        ;                       ; counting up
        add     al,outn+1[di]   ; add inc to index
        cmp     al,dh           ; over the top?
        jna     lcount3         ; no, branch
        sub     al,dh           ; yes, subtract top
        add     al,dl           ; add bottom
lcount3:mov     ah,al           ; set new index
        dec     al              ; show index -1
        mov     outn[di],ax     ; /
        jmp     next            ; display & exit
        ;                       ; counting down
lcount2:mov     cl,outn+1[di]   ; get index
        sub     cl,al           ; subtract inc
        jc      lcount5         ; branch if rollover
        cmp     cl,dl           ; below the limit
        jnb     lcount4         ; no, branch
lcount5:add     cl,dh           ; yes, add top
        sub     cl,dl           ; subtract bottom
lcount4:mov     ch,cl           ; set new index
        mov     outn[di],cx     ; /
        jmp     next            ; display & exit

;==============================================================
; Index counter, adding with upper and lower limit
; inputs: 0: clock up, 1: hold/reset 2: increment 
;         3: inc/dec   4: top limit  5: bottom limit 
;         6x: tick
; output: index                 
;--------------------------------------------------------------
        public _mcount
_mcount:
        hold    var1            ; hold?
        jz      mcount0         ; branch if no hold
        testv   var3,-1         ; counting up or down
        jnz     mcounta         ; branch if counting down
        getv    al,var5         ; up, set to bottom limit
        jmp     short mcountb   ; /
mcounta:getv    al,var4         ; down, set to top limit
mcountb:mov     ah,al           ;  /
        mov     outn[di],ax     ; /
        jmp     next            ; exit
mcount0:tick    var0,var6       ; clock tick
        jc      mcount1         ; branch if there is a tick
        nextx                   ; else exit easy
mcount1:getv    dl,var5         ; dl = the lower limit
        getv    dh,var4         ; dh = the top limit
        cmp     dh,dl           ; top < bottom ?
        jnb     mcountk         ; no, branch
        xchg    dh,dl           ; yes, swap
mcountk:inc     dh              ; bump so top is included
        getv    al,var2         ; al = the increment
        getv    ah,var3         ; ah = inc/dec
        test    ah,-1           ; inc or dec?
        jnz     mcount2         ; branch if dec
        ;                       ; counting up
        add     al,outn+1[di]   ; add inc to index
        cmp     al,dh           ; over the top?
        jna     mcount3         ; no, branch
        mov     al,dh           ; get hi count
        sub     al,dl           ; get hi-lo
        shr     al,1            ; /2
        mov     ah,al           ; hold
        shr     al,1            ; 1/4
        add     al,ah           ; 3/4
        add     al,dl           ; offset from bottom
mcount3:mov     ah,al           ; set new index
        mov     outn[di],ax     ; /
        jmp     next            ; display & exit
        ;                       ; counting down
mcount2:mov     cl,outn+1[di]   ; get index
        sub     cl,al           ; subtract inc
        jc      mcount5         ; branch if rollover
        cmp     cl,dl           ; below the limit
        jnb     mcount4         ; no, branch
mcount5:mov     cl,dh           ; get hi count
        sub     cl,dl           ; get hi-lo
        shr     cl,1            ; 1/2
        shr     cl,1            ; 1/4
        add     cl,dl           ; offset from bottom
mcount4:mov     ch,cl           ; set new index
        mov     outn[di],cx     ; /
        jmp     next            ; display & exit

;==============================================================
; Index counter, incrementing, 16 bit
; inputs: 0: clock up
;         1: hold/reset
;         2: reset count lo
;         3: reset count hi
;         4: reset
;         5: shift output
;         6: up/down
;         7x:tick flag
;         8: index
; output: index
;--------------------------------------------------------------
        public _ncount
_ncount:
        testv   var4,-1                 ; reset?
        jz      ncount0                 ; no, branch
        getv    al,var2                 ; yes, get reset count lo
        getv    ah,var3                 ; ... and reset count hi
        mov     var8[di],ax             ; reset
        ;
ncount0:hold    var1                    ; hold?
        jnz     ncountx                 ; branch if no hold
        ;
        tick    var0,var7               ; clock tick
        jnc     ncountx                 ; branch if no tick
        mov     ax,1                    ; setup for inc
        testv   var6,-1                 ; up or down
        jz      ncount1                 ; branch if up
        mov     ax,-1                   ; else setup for down
ncount1:
        add     var8[di],ax             ; bump index
        ;
ncountx:mov     ax,word ptr var8[di]    ; get the index
        getv    cl,var5                 ; get shift byte
        shr     ax,cl                   ; bump right
        putn    al                      ; send it out
        nextv                           ; exit
;==============================================================
; Sequencer/Switcher
; inputs: 0: stage addr, 1 - 16: stages, xl: old stage
; output: value of stage pointed to by var0
;
        public _wseq
_wseq:  
        getv    al,var0         ; get variable0
        and     al,15           ; 16 stages
        mov     cl,al           ; save it
        mov     ch,var1+32[di]  ; get saved last stage
        and     ch,15           ; just to be safe
        mov     var1+32[di],al  ; store the new stage
        mov     ah,0            ; make new stage into word
        add     ax,ax           ; /
        mov     bx,ax           ; bx = offset to the stage
        mov     bx,var1[bx+di]  ; get the value
        mov     al,[bx]         ; /
        putn    al              ; send to output
;--------------------------------------------------------------
; got the value, now clear old stage led, and set the new
;--------------------------------------------------------------
        cmp     cl,ch           ; have stages changed?
        jz      wseqx           ; no, then just exit
        gettag                  ; es:bx = screen addr of tag
        dec     bx              ; point to leds
        add     ch,4            ; offset to 2nd var loc in screen
        mov     al,160          ; point to stage led
        mul     ch              ; ax = offset to old stage led
        mov     di,ax           ; put in di for indexing
        mov     byte ptr es:[bx+di],yellow; turn the led off
        add     cl,4            ; offset to 2nd var loc in screen
        mov     al,160          ; point to stage led
        mul     cl              ; ax = offset to new stage led
        mov     di,ax           ; put in di for indexing
        mov     byte ptr es:[bx+di],hi+red; turn the led on 
wseqx:
        mov     di,2[si]        ;; set di pointing to variable list
        nextv                   ; show the output
;==============================================================
; Sequencer/Switcher
; inputs: 0: stage addr, 1 - 8: stages, xl: old stage
; output: value of stage pointed to by var0
;
        public _xseq
_xseq:  
        getv    al,var0         ; get variable0
        and     al,7            ; 8  stages
        mov     cl,al           ; save it
        mov     ch,var1+16[di]  ; get saved last stage
        and     ch,7            ; just to be safe
        mov     var1+16[di],al  ; store the new stage
        mov     ah,0            ; make new stage into word
        add     ax,ax           ; /
        mov     bx,ax           ; bx = offset to the stage
        mov     bx,var1[bx+di]  ; get the value
        mov     al,[bx]         ; /
        putn    al              ; send to output
;--------------------------------------------------------------
; got the value, now clear old stage led, and set the new
;--------------------------------------------------------------
        cmp     cl,ch           ; have stages changed?
        jz      xseqx           ; no, then just exit
        gettag                  ; es:bx = screen addr of tag
        dec     bx              ; point to leds
        add     ch,4            ; offset to 2nd var loc in screen
        mov     al,160          ; point to stage led
        mul     ch              ; ax = offset to old stage led
        mov     di,ax           ; put in di for indexing
        mov     byte ptr es:[bx+di],yellow; turn the led off
        add     cl,4            ; offset to 2nd var loc in screen
        mov     al,160          ; point to stage led
        mul     cl              ; ax = offset to new stage led
        mov     di,ax           ; put in di for indexing
        mov     byte ptr es:[bx+di],hi+red; turn the led on 
xseqx:
        mov     di,2[si]        ; set di pointing to variable list
        nextv                   ; show the output
;==============================================================
; Sequencer/Switcher
; inputs: 0: stage addr, 1 - 4: stages, xl: old stage
; output: value of stage pointed to by var0
;
        public _yseq
_yseq:  
        getv    al,var0         ; get variable0
        and     al,3            ;  4 stages
        mov     cl,al           ; save it
        mov     ch,var1+8[di]   ; get saved last stage
        and     ch,3            ; just to be safe
        mov     var1+8[di],al   ; store the new stage
        mov     ah,0            ; make new stage into word
        add     ax,ax           ; /
        mov     bx,ax           ; bx = offset to the stage
        mov     bx,var1[bx+di]  ; get the value
        mov     al,[bx]         ; /
        putn    al              ; send to output
;--------------------------------------------------------------
; got the value, now clear old stage led, and set the new
;--------------------------------------------------------------
        cmp     cl,ch           ; have stages changed?
        jz      yseqx           ; no, then just exit
        gettag                  ; es:bx = screen addr of tag
        dec     bx              ; point to leds
        add     ch,4            ; offset to 2nd var loc in screen
        mov     al,160          ; point to stage led
        mul     ch              ; ax = offset to old stage led
        mov     di,ax           ; put in di for indexing
        mov     byte ptr es:[bx+di],yellow; turn the led off
        add     cl,4            ; offset to 2nd var loc in screen
        mov     al,160          ; point to stage led
        mul     cl              ; ax = offset to new stage led
        mov     di,ax           ; put in di for indexing
        mov     byte ptr es:[bx+di],hi+red; turn the led on 
yseqx:
        mov     di,2[si]        ; set di pointing to variable list
        nextv                   ; show the output
;==============================================================
; Sequencer/Switcher
; inputs: 0: stage addr, 1 - 2: stages, xl: old stage
; output: value of stage pointed to by var0
;
        public _zseq
_zseq:  
        getv    al,var0         ; get variable0
        and     al,1            ;  1 stages
        mov     cl,al           ; save it
        mov     ch,var1+4[di]   ; get saved last stage
        and     ch,1            ; just to be safe
        mov     var1+4[di],al   ; store the new stage
        mov     ah,0            ; make new stage into word
        add     ax,ax           ; /
        mov     bx,ax           ; bx = offset to the stage
        mov     bx,var1[bx+di]  ; get the value
        mov     al,[bx]         ; /
        putn    al              ; send to output
;--------------------------------------------------------------
; got the value, now clear old stage led, and set the new
;--------------------------------------------------------------
        cmp     cl,ch           ; have stages changed?
        jz      zseqx           ; no, then just exit
        gettag                  ; es:bx = screen addr of tag
        dec     bx              ; point to leds
        add     ch,4            ; offset to 2nd var loc in screen
        mov     al,160          ; point to stage led
        mul     ch              ; ax = offset to old stage led
        mov     di,ax           ; put in di for indexing
        mov     byte ptr es:[bx+di],yellow; turn the led off
        add     cl,4            ; offset to 2nd var loc in screen
        mov     al,160          ; point to stage led
        mul     cl              ; ax = offset to new stage led
        mov     di,ax           ; put in di for indexing
        mov     byte ptr es:[bx+di],hi+red; turn the led on 
zseqx:
        mov     di,2[si]        ; set di pointing to variable list
        nextv                   ; show the output
;==============================================================
; Sequencer/Switcher
; output: "a" input if var0 AND var1 = 0, else "b" input
; inputs: 
; 0, 1: test inputs, ANDed to give flag
; 2: input stage "a"
; 3: input stage "b"
;
        public _useq
_useq:  getv    dl,var0         ; get flag vars
        getv    dh,var1         ; /
        mov     ch,yellow       ; set up led colors
        mov     cl,red+hi       ; /
        getv    al,var2         ; get "a" input
        ;
        and     dh,dl           ; test
        jz      useq0           ; branch if "b" input
        xchg    ch,cl           ; else setup for 2nd
        getv    al,var3         ; get "b" input
        ;
useq0:  gettag                  ; get screen address of leds
        add     bx,799          ; set up input leds
        mov     es:[bx],cl      ; do the leds
        mov     es:160[bx],ch   ; /
        ;
        putn    al              ; send to output
        nextv                   ; exit        
;==============================================================
; Sequencer/Switcher
; output: "a" input if var0 AND var1 = 0, else "b" input
; inputs: 
; 0, 1: test inputs, ANDed to give flag
; 2: input stage "a"
; 3: input stage "b"
; 4: offset
;
        public _vseq
_vseq:  getv    dl,var0         ; get flag vars
        getv    dh,var1         ; /
        mov     ch,yellow       ; set up led colors
        mov     cl,red+hi       ; /
        getv    al,var2         ; get "a" input
        ;
        and     dh,dl           ; test
        jz      vseq0           ; branch if "b" input
        xchg    ch,cl           ; else setup for 2nd
        getv    al,var3         ; get "b" input
        ;
vseq0:  gettag                  ; get screen address of leds
        add     bx,799          ; set up input leds
        mov     es:[bx],cl      ; do the leds
        mov     es:160[bx],ch   ; /
        ;
        getv    ah,var4         ; get the offset
        add     al,ah           ; put them together
        putn    al              ; send to output
        nextv                   ; exit        
;==============================================================
; Pass input thru to output on nz test input, zero on low
; inputs: 0: switch 
;         1: value1, 2: value2
; output: value/0
;--------------------------------------------------------------
        public _passt
_passt: getv    al,var1         ; al = input a
        mov     ch,yellow       ; set up led colors
        mov     cl,red+hi       ; /
        testv   var0,-1         ; test a/b switch
        jz      passt1          ; branch if 1st input
        ;
        getv    al,var2         ; al = input b
        xchg    ch,cl           ; else setup for 2nd
        ;
passt1: putn    al              ; put it away
        ;
        gettag                  ; get screen address of leds
        dec     bx              ; /
        mov     bp,bx           ; save it
        add     bx,640          ; bx = screen addr of 1st led
        mov     es:[bx],cl      ; do the leds
        mov     es:160[bx],ch   ; /
        ;
        nextv                   ; show it
;==============================================================
; Pass input thru to output on nz test input, zero on low
; inputs: 0: switch 
;         1: value1, 2: value2, 3: offset
; output: value/0
;--------------------------------------------------------------
        public _passq
_passq: getv    al,var1         ; al = input a
        mov     ch,yellow       ; set up led colors
        mov     cl,red+hi       ; /
        testv   var0,-1         ; test a/b switch
        jz      passq1          ; branch if 1st input
        ;
        getv    al,var2         ; al = input b
        xchg    ch,cl           ; else setup for 2nd
        ;
passq1: getv    ah,var3         ; get offset
        add     al,ah           ; /
        putn    al              ; put it away
        ;
        gettag                  ; get screen address of leds
        dec     bx              ; /
        mov     bp,bx           ; save it
        add     bx,640          ; bx = screen addr of 1st led
        mov     es:[bx],cl      ; do the leds
        mov     es:160[bx],ch   ; /
        ;
        nextv                   ; show it
;==============================================================
; Standard 256 byte sequencer with input mask
; inputs: 0: address 1: write enable, 2: input, 3: input mask 
;         4x: module number, 4z, init flag
; output: byte addressed by address
;--------------------------------------------------------------
        public _pseq
_pseq:  cmp     byte ptr var4+1[di],0abh; initialized?
        jnz     pseq2           ; no, branch
        test    usrflg,1        ; user input
        jnz     pseq1           ; yes, no go
        ;
        mov     ax,seg bufsp    ; setup seg register
        mov     es,ax           ; es = seg addr
        getv    dl,var0         ; get address
        mov     dh,0            ; /
        mov     bh,var4[di]     ; get hi address
        mov     bl,0            ; /
        add     bx,dx           ; set up as index
        mov     dx,bx           ; save
        mov     al,es:[bx]      ; get the byte
        putn    al              ; send it out
        ;
        testv   var1,-1         ; write enabled?
        jz      pseq1           ; no, branch
        getv    al,var2         ; yes, get input
        getv    ah,var3         ; get mask
        and     al,ah           ; mask the input
        not     ah              ; complement
        mov     bx,dx           ; get address again
        and     es:[bx],ah      ; clear old bits
        or      es:[bx],al      ; put in new bits
;       or      _mpab,8         ; set buffer used flag
pseq1:  nextv                   ; exit with video update
        ;
pseq2:  mov     al,modnum               ; get module number
        mov     var4[di],al             ; save it
        mov     byte ptr var4+1[di],0abh; set init flag
        initv   var3,0ffh               ; mask <- FF
        gettag                          ; es:bx = screen addr of tag
        tohex                           ; convert to hex
        mov     es:-2[bx],al            ; show the default number
        nextx                           ; exit
;--------------------------------------------------------------
; magenta input
;
        public  upseq 
upseq:  pop     ax              ; stack bullshit
        mov     cmdflg,10       ; set up special routine
        mov     special,offset upseqx
        mov     di,varsav       ; point to variable list
        mov     al,outn[di]     ; get the output value
        mov     var4[di],al     ; save
        jmp     workx           ; on to the next stage
        ;
upseqx: mov     di,varsav       ; point to variable list
        mov     al,outn[di]     ; get the new channel
        xchg    var4[di],al     ; set it, get orig patch
        mov     outn[di],al     ; restore
        jmp     _cancel         ; exit
;==============================================================
; Standard 256 byte sequencer with offset
; inputs: 0: address, 1: offset, 2x: mod #, 2z: init flag
; output: byte addressed by address
;--------------------------------------------------------------
        public _qseq,_rseq,_sseq,_tseq
_tseq:  nop
_sseq:  nop
_rseq:  nop
_qseq:  cmp     byte ptr var2+1[di],0abh; initialized?
        jnz     qseq1           ; no, branch
        test    usrflg,1        ; user input
        jnz     qseq0           ; yes, no go
        ;
        mov     ax,seg bufsp    ; setup seg register
        mov     es,ax           ; es = seg addr
        getv    dl,var1         ; get offset 
        mov     dh,0            ; (not needed)
        getv    bl,var0         ; get address
        mov     bh,var2[di]     ; get hi address
        add     bl,dl           ; set up as index
        mov     al,es:[bx]      ; get the byte
        putn    al              ; send it out
qseq0:  nextv                   ; exit with video update
        ;
qseq1:  mov     al,modnum               ; get module number
        mov     var2[di],al             ; save it
        mov     byte ptr var2+1[di],0abh; set init flag
        gettag                          ; es:bx = screen addr of tag
        tohex                           ; convert to hex
        mov     es:-2[bx],al            ; show the default number
        nextx                           ; exit
;--------------------------------------------------------------
; magenta input
;
        public  uqseq 
uqseq:  pop     ax              ; stack bullshit
        mov     cmdflg,10       ; set up special routine
        mov     special,offset uqseqx
        mov     di,varsav       ; point to variable list
        mov     al,outn[di]     ; get the output value
        mov     var2[di],al     ; save
        jmp     workx           ; on to the next stage
        ;
uqseqx: mov     di,varsav       ; point to variable list
        mov     al,outn[di]     ; get the new channel
        xchg    var2[di],al     ; set it, get orig patch
        mov     outn[di],al     ; restore
        jmp     _cancel         ; exit
;==============================================================
; 65K byte Sequencer with input mask
; inputs: 0: address lo, 1: address hi,  
;       2: write enable, 3: input, 4: input mask, 5x: init flag
; output: byte addressed by address + offset
;--------------------------------------------------------------
        public _puseq
_puseq: 
        cmp     byte ptr var5[di],0abh; initialized?
        jnz     puseq2          ; no, branch
        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
        mov     bx,dx           ; set up as index
        mov     al,es:[bx]      ; get the byte
        putn    al              ; send it out
        ;
        testv   var2,-1         ; write enabled?
        jz      puseq1          ; no, branch
        getv    al,var3         ; yes, get input
        getv    ah,var4         ; get mask
        and     al,ah           ; mask the input
        not     ah              ; complement
        mov     bx,dx           ; get address again
        and     es:[bx],ah      ; clear old bits
        or      es:[bx],al      ; put in new bits
;       or      _mpab,4         ; set buffer used flag
puseq1: nextv                   ; exit with video update
        ;
puseq2: mov     al,modnum               ; get module number
        mov     byte ptr var5[di],0abh; set init flag
        initv   var4,0ffh               ; mask <- FF
        nextx                           ; exit
;==============================================================
; 65K byte sequencer with offset
; inputs: 0: address lo, 1: address hi, 2: offset
; output: byte addressed by address
;--------------------------------------------------------------

        public _quseq,_ruseq,_suseq,_tuseq
_tuseq: nop
_suseq: nop
_ruseq: nop
_quseq: 
        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
        putn    al              ; send it out
        nextv                   ; exit with video update
;==============================================================
; Delay Sequencer

; inputs: 0: clock, 1: page address, 2: read offset, 3: write enable,
;         4: input, 5: offset,  6x: clock tick  

; inputs: 0: clock
;         1: input
;         2: read offset
;         3: write enable
;         4: offset
;         5x: page address
;         5z: index
;         6x: clock tick
;         6z: init flag  
; output: byte addressed by index (hi byte of output addr) + var2
;         AND 15 + (var1 * 16), AFTER write operation.
; Thus, a read offset of 0 causes the output to track the input,
; an offset of 1 tracks 1 clock behind, etc.
;--------------------------------------------------------------
        public _euseq
_euseq: 
        cmp     byte ptr var6+1[di],0abh; initialized?
        jnz     euseqi          ; no, go do it 
        ;
        tick    var0,var6       ; clock tick
        jnc     euseqx          ; exit if no tick
        ;
        mov     ax,seg buftu    ; setup seg register
        mov     cl,var5[di]     ; ch = buffer page addr low
        mov     ch,0            ; cx = buffer page addr
        add     ax,cx           ; ax = seg addr of 16 byte buffer
        mov     es,ax           ; es = seg addr
        mov     ah,var5+1[di]   ; ah = index offset
        ;
        testv   var3,-1         ; write enabled?
        jz      euseq2          ; no, branch
        getv    al,var1         ; yes, get input
        mov     bl,ah           ; set up offset
        and     bl,15           ; 15 bytes in the buffer
        mov     bh,0            ; /
        mov     es:[bx],al      ; write the byte
        ;
euseq2: getv    al,var2         ; al = read offset
        neg     al              ; read behind
        add     al,ah           ; al = index-offset
        and     al,15           ; 15 bytes in the buffer
        mov     bl,al           ; set up an index pointer
        mov     bh,0            ; /
        mov     al,es:[bx]      ; get the read byte
        getv    dl,var4         ; get offset
        add     al,dl           ; add to output
        putn    al              ; send it out
        ;
        inc     ah              ; bump index
        mov     var5+1[di],ah   ; set the new index
        nextv                   ; exit with video update
euseqx: nextx
;
euseqi: mov     byte ptr var6+1[di],0abh; set init flag
        initv   var3,1                  ; write enable
        mov     al,modnum               ; get module number
        mov     ah,0                    ; zip index
        mov     var5[di],ax             ; save it
        gettag                          ; es:bx = screen addr of tag
        tohex                           ; convert to hex
        mov     es:-2[bx],al            ; show the default number
        nextx                           ; exit
;--------------------------------------------------------------
; magenta input
;
        public  ueuseq 
ueuseq: pop     ax              ; stack bullshit
        mov     cmdflg,10       ; set up special routine
        mov     special,offset ueuseqx
        mov     di,varsav       ; point to variable list
        mov     al,outn[di]     ; get the output value
        mov     var5[di],al     ; save
        jmp     workx           ; on to the next stage
        ;
ueuseqx:mov     di,varsav       ; point to variable list
        mov     al,outn[di]     ; get the new channel
        xchg    var5[di],al     ; set it, get orig patch
        mov     outn[di],al     ; restore
        jmp     _cancel         ; exit
;==============================================================
; set/clear mute flags
; inputs: 0: flags 0-7,  1: flags 8-15,
;         2: shift, 3: mask
;         4x: last var0, 4z: last var1 
;
; output: 16bit mute word, shifted & masked
;--------------------------------------------------------------
        public  _domute
_domute:test    valflg,-1       ; inputting now
        jnz     domutex         ; yes, just get them
        ;
        getv    al,var0         ; get mute flags
        getv    ah,var1         ; /
        not     ax              ; flip polarity
        ;
        mov     dx,ax           ; save it
        xor     ax,var4[di]     ; bits set are changes
        mov     var4[di],dx     ; save the new value
        mov     cx,ax           ; save flag bits
        not     ax              ; flip flag bits
        and     mute,ax         ; strip changed bits
        and     cx,dx           ; get new changed bits
        or      mute,cx         ; put them away
        ;
domutex:mov     dx,mute         ; get the mute word
        getv    cl,var2         ; get the shift
        ror     dx,cl           ; shift right
        not     dl              ; flip bits
        getv    al,var3         ; get mask
        and     al,dl           ; mask the mute flags
        putn    al              ; send it out
        nextv                   ; exit
;==============================================================
; External call via software interrupt
; output: returned value of AL
; inputs:
; 0: clock
; 1: interrupt number
; 2: value loaded into AH
; 3: value loaded into AL
; 4: value loaded into BH
; 5: value loaded into BL
; 6: value loaded into CH
; 7: value loaded into CL
; 8: value loaded into DH
; 9: value loaded into DL
; 10x: clock tick
;--------------------------------------------------------------
        public _xcall
_xcall: tick    var0,var10      ; tick clock
        jnc     xcallx          ; exit if no tick
        ;
        getv    al,var1         ; get the interrupt number
        test    al,-1           ; anything there
        jz      xcallx          ; no, exit
        ;
        mov     byte ptr cs:xcalli+1,al; set up the int number
        getv    ah,var2         ; load registers
        getv    al,var3         ;         /
        getv    ch,var4         ;        /
        getv    cl,var5         ;       /
        mov     bp,cx           ;      /
        getv    ch,var6         ;     /
        getv    cl,var7         ;    /
        getv    dh,var8         ;   /
        getv    dl,var9         ;  /
        mov     bx,bp           ; /
        push    si              ; save needed registers
        push    di              ;  /
        push    ds              ; /
xcalli: int     64              ; go do it
        pop     ds              ; restore registers
        pop     di              ;  /
        pop     si              ; /
        mov     xcax,ax         ; save registers
        mov     xcbx,bx         ;   /
        mov     xccx,cx         ;  /
        mov     xcdx,dx         ; /
        pushf                   ; get flags
        pop     ax              ; /
        putn    al              ; send low flags to output
xcallx: nextv                   ; exit
;==============================================================
; read a register after xcall
; output: xcall register storage
; inputs: 0: strobe, 1: register number, 2x: clock tick
;         0 = al, 1 = ah, 2 = bl, 3 = bh, 
;         4 = cl, 5 = bh, 6 = dl, 7 = dh
;--------------------------------------------------------------
        public _rcall
_rcall: tick    var0,var2       ; clock tick
        jnc     rcallx          ; exit if no clock
        getv    al,var1         ; get the register number
        and     ax,7            ; 8 registers allowed
        mov     bx,offset dgroup:xcax; get table location
        add     bx,ax           ; add offset
        mov     al,[bx]         ; get the register
        putn    al              ; send it out
rcallx: nextv                   ; exit
;==============================================================
; read a byte from the keyboard
; output: ascii keyboard data
; inputs: 0: hold
;--------------------------------------------------------------
        public _xkbd
_xkbd:
        hold    var0            ; hold?
        jnz     xkbdx           ; yes, exit
        and     byte ptr outn[di],7fh; strip old key flag
        mov     ah,6            ; dos direct console i/o
        mov     dl,0ffh         ; input request
        int     21h             ; call dos
        jz      xkbdx           ; exit if no input
        or      al,al           ; function key
        jz      xkbd0           ; yes, don't set flag
        or      al,80H          ; set new key flag
xkbd0:  putn    al              ; send it out
xkbdx:  nextv                   ; exit
;==============================================================
; read a byte from MIDI to s-sequencer
; output: index into s-sequencer
; inputs: 0: reset strobe, 1: enable, 2x: tick
;--------------------------------------------------------------
        public _xmidii
_xmidii:
        tick    var0,var2       ; strobe tick?
        jnc     xmidii0         ; no, branch
        mov     cs:midatix,0    ; yes, clear index
        ;
xmidii0:getv    al,var1         ; get write enable flag
        mov     cs:misend,al    ; set/clear the flag
        ;
        mov     al,cs:midatix   ; get index
        putn    al              ; send it out
        nextv                   ; exit
;==============================================================
; show output buffer status
; inputs: none
; output: flag bits 0,1 set if output buffer not empty
;--------------------------------------------------------------
        public  _tstmob
_tstmob:call    tstmob          ; check it
        putn    al              ; send it out
        nextl                   ; show it
;==============================================================
; show mpu input flags, set mpu input mask
;   0 = no input, 1=mpu#1, 2=mpu#2
; inputs: 0: reset input flag
;         1: mpu input mask
;         2x init flag
; output: mpu input flags
;--------------------------------------------------------------
        public  _mpuins
_mpuins:cmp     byte ptr var2[di],0abh  ; initialized?
        jnz     mpuins0                 ; no, branch
        ;
        getv    al,var1                 ; get mask byte
        mov     cs:_mpuinm,al           ; set mask
        ;
        mov     al,cs:_mpuinf           ; get mpu input flag
        putn    al                      ; send it out
        ;
        testv   var0,-1                 ; want to clear mpu flag?
        jnz     mpuins1                 ; no, branch
        mov     cs:_mpuinf,0            ; clear mpu flag
        ;
mpuins1:nextl                           ; show it
        ;
mpuins0:mov     byte ptr var2[di],0abh  ; no, set init values
        initv   var1,3                  ; default is all ok
        nextx                           ; exit
;;==============================================================
; output a byte to output port
; output: none
; inputs:
; 0: clock
; 1: value to be output
; 2: output port address hi
; 3: output port address lo
; 4x: clock tick
;--------------------------------------------------------------
        public _outpb
_outpb: tick    var0,var4       ; clock tick
        jnc     outpbx          ; exit if no clock
        getv    dh,var2         ; get the address
        getv    dl,var3         ; /
        getv    al,var1         ; get the value
        out     dx,al           ; send it out
outpbx: nextx                   ; exit
;==============================================================
; input a byte from input port
; output: value from port
; inputs:
; 0: clock
; 1: input port address hi
; 2: input port address lo
; 3x: clock tick
;--------------------------------------------------------------
        public _inptb
_inptb: tick    var0,var3       ; clock tick
        jnc     inptbx          ; exit if no clock
        getv    dh,var1         ; get the address
        getv    dl,var2         ; /
        in      al,dx           ; get the value from port
        putn    al              ; send it
inptbx: nextv                   ; exit & show
;==============================================================
; input a byte from memory
; output: value from memory
; inputs:
; 0: clock
; 1: seg addr hi
; 2: input port address hi
; 3: input port address lo
; 4x: clock tick
;--------------------------------------------------------------
        public _peek
_peek:  tick    var0,var4       ; clock tick
        jnc     peekx           ; exit if no clock
        getv    dh,var1         ; get seg hi
        getv    ah,var2         ; get address hi
        getv    bl,var3         ; get address lo
        mov     bh,ah           ; bx = address
        mov     dl,0            ; dx = seg
        mov     es,dx           ; es = seg
        mov     al,es:[bx]      ; get the byte
        putn    al              ; send it
peekx:  nextv                   ; exit & show
;==============================================================
; output a byte to memory
; output: none
; inputs:
; 0: clock
; 1: value to be output
; 2: seg addr hi
; 3: input port address hi
; 4: input port address lo
; 5x: clock tick
;--------------------------------------------------------------
        public _poke
_poke:  tick    var0,var5       ; clock tick
        jnc     pokex           ; exit if no clock
        getv    al,var1         ; get the value
        getv    dh,var2         ; get seg hi
        getv    ah,var3         ; get address hi
        getv    bl,var4         ; get address lo
        mov     bh,ah           ; bx = address
        mov     dl,0            ; dx = seg
        mov     es,dx           ; es = seg
        mov     es:[bx],al      ; put the byte
        putn    al              ; send it
pokex:  nextx                   ; exit
;==============================================================
; System Exclusive Receive
; inputs:
;   0:    Address Low of sequencer "S" where data will go
;   1:    Address High of sequencer "S"
;   2:    Strobe to start receive
;   3:    Shift output value
;   4xz:  Index into seq "s"
;   5x:   strobe tick
; output: Next available seq addr, shifted right by input 3
;--------------------------------------------------------------
        public _sxget
_sxget: tick    var2,var5               ; wanna do it?
        jc      sxget0                  ; yes, branch
        jmp     sxgetx                  ; no, exit to readout
        ;
sxget0: mov     es,4[di]                ; get seg addr
        mov     bx,6[di]                ; get module screen address
        mov     al,'X'                  ; set output byte
        mov     es:320[bx],al           ; put to the screen
        mov     es:322[bx],al           ; /
        mov     ax,80H                  ; to set hi bit
        mov     outn[di],ax             ; trash output
        or      es:321[bx],al           ; put to the screen
        or      es:323[bx],al           ; /
        ;
sxget6: call    allclr                  ; clear midi channel
        ; 
        mov     word ptr var4[di],-2    ; setup error flag
        mov     cs:miflag,0             ; clear input data flag
        ;
        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
        mov     bx,dx                   ; set up as index
        ;
        mov     bp,cs:ticks             ; get timer
        add     bp,11640                ; timeout in 20 sec.
        ;
sxget1: test    cs:miflag,-1            ; anything there?
        jnz     sxget2                  ; yes, branch
        cmp     bp,cs:ticks             ; timeout?
        jnz     sxget1                  ; no, keep looking
        jmp     sxgetx                  ; yes, exit
        ;
sxget2: mov     cs:miflag,0             ; clear input flag
        cmp     cs:midata,0f0h          ; system exclusive?
        jnz     sxget1                  ; no, keep looking
        mov     byte ptr es:[bx],0f0h   ; yes, send the data
        inc     bx                      ; bump index
        jz      sxgetx                  ; exit if index overflow
        ;
sxget3: test    cs:miflag,-1            ; anything there?
        jnz     sxget4                  ; yes, branch
        cmp     bp,cs:ticks             ; timeout?
        jnz     sxget3                  ; no, keep looking
        ;
sxget4: mov     cs:miflag,0             ; clear input flag
        mov     al,cs:midata            ; get the data
        mov     es:[bx],al              ; put it in the seq memory
        cmp     al,0f7h                 ; EOX?
        jz      sxget5                  ; yes, branch
        inc     bx                      ; bump index
        jz      sxgetx                  ; exit if index overflow
        jmp     short sxget3            ; else back to the loop
        ;
sxget5: mov     var4[di],bx             ; set the new index
        mov     _header+7,1             ; set buffer used flag
        ;
sxgetx: mov     ax,var4[di]             ; get the index
        inc     ax                      ; bump one
        getv    cl,var3                 ; get shift byte
        shr     ax,cl                   ; bump right
        putn    al                      ; send it out
        mov     al,7fH                  ; to set lo bit
        mov     es,4[di]                ; get seg addr
        mov     bx,6[di]                ; get module screen address
        and     es:321[bx],al           ; put to the screen
        and     es:323[bx],al           ; /
        nextv                           ; exit
;==============================================================
; System Exclusive Send
; inputs:
;   0:    Address Low of sequencer "S" where data is found
;   1:    Address High of sequencer "S"
;   2:    Strobe to start send
;   3:    Shift output value
;   4xz:  Index into seq "s"
;   5x:   strobe tick
; output: Next available seq addr, shifted right by input 3
;--------------------------------------------------------------
        public _sxput
_sxput: tick    var2,var5               ; wanna do it?
        jc      sxput0                  ; yes, branch
        jmp     sxputx                  ; no, exit to readout
        ;
sxput0: mov     word ptr var4[di],-2    ; setup error flag
        mov     midip,0                 ; set midi port 0
        ;
        mov     es,4[di]                ; get seg addr
        mov     bx,6[di]                ; get module screen address
        mov     al,'X'                  ; set output byte
        mov     es:320[bx],al           ; put to the screen
        mov     es:322[bx],al           ; /
        mov     ax,80H                  ; to set hi bit
        mov     outn[di],ax             ; trash output
        or      es:321[bx],al           ; put to the screen
        or      es:323[bx],al           ; /
        ;
        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
        mov     bp,dx                   ; set up as index
        ;
sxput1: call    allclr                  ; clear midi channels
        ;
sxput2: mov     al,es:[bp]              ; get the data byte
        mov     ah,al                   ; save in ah
        call    allmidi                 ; send it to buffer
        sendmb                          ; send it to MIDI
        ;
        cmp     ah,0f7h                 ; EOX?
        jz      sxput3                  ; yes, branch
        inc     bp                      ; bump index
        jz      sxputx                  ; exit if index overflow
        jmp     short sxput2            ; else back to the loop
        ;
sxput3: mov     var4[di],bp             ; set the new index
        ;
sxputx: mov     ax,var4[di]             ; get the index
        inc     ax                      ; bump one
        getv    cl,var3                 ; get shift byte
        shr     ax,cl                   ; bump right
        putn    al                      ; send it out
        mov     al,7fH                  ; to set lo bit
        mov     es,4[di]                ; get seg addr
        mov     bx,6[di]                ; get module screen address
        and     es:321[bx],al           ; put to the screen
        and     es:323[bx],al           ; /
        nextv                           ; exit
;==============================================================
; MIDI Program change
; inputs: 
;       0:  channel
;       1:  delay  
;       2:  strobe
;       3:  input
;       4x: strobe tick
; output: none
;--------------------------------------------------------------
        public _patcho        
_patcho:
        test    lodflg,-1               ; load happen?
        jnz     patcho1                 ; yes, force change
        test    clrchf,3                ; clear chan flag?
        jz      patcho2                 ; yes, force change
        or      clrchf,2                ; setup to clear
        jmp     patcho1                 ; go do it
        ;
patcho2:tick    var2,var4               ; clock tick
        jnc     patchox                 ; exit if no strobe
        ;
patcho1:call    allclr                  ; clear all midi channels
        getv    al,var0                 ; get channel
        mchan   al                      ; /
        or      al,0c0H                 ; MIDI Program Change
        call    tomidi                  ; send it to midi
        ;
        getv    al,var3                 ; get value input
        mov     outn[di],al             ; send to output
        and     al,127                  ; strip msb
        call    tomidi                  ; send to midi
        ;
        getv    al,var1                 ; get delay value
        or      al,al                   ; split if 0
        jz      patchox                 ; /
        mov     ah,0                    ; make it a word
        add     ax,cs:ticks             ; add current ticks
patcho0:cmp     al,byte ptr cs:ticks    ; reached it yet?
        jnz     patcho0                 ; no, loop
patchox:nextv
;==============================================================
; Programmer output values
; inputs: 0x: channel
; output: sequencer send to the controller
;--------------------------------------------------------------
        public _pcouta
_pcouta:cmp     byte ptr var0+1[di],0abh; initialized?
        jz      pcouta0                 ; yes, branch
        ;
pcouti: mov     al,modnum               ; get module number
        mov     var0[di],al             ; save it

        gettag                          ; es:bx = screen addr of tag
        tohex                           ; convert to hex
        mov     es:-2[bx],al            ; show the default number
        mov     byte ptr var0+1[di],0abh; set init flag
        nextx
        ;
pcouta0:test    usrflg,1                ; user input
        jnz     pcoutax                 ; yes, no go
        mov     bl,var0[di]             ; get channel
        mov     bh,0                    ; bx = channel
        add     bx,offset dgroup:pcva   ; add table to offset
        mov     al,[bx]                 ; get the value
        putn    al                      ; send it out
pcoutax:nextv                           ; exit with display
;--------------------------------------------------------------
        public _pcoutb
_pcoutb:cmp     byte ptr var0+1[di],0abh; initialized?
        jz      pcoutb0                 ; yes, branch
        jmp     pcouti                  ; no, initialize
        ;
pcoutb0:test    usrflg,1                ; user input
        jnz     pcoutbx                 ; yes, no go
        mov     bl,var0[di]             ; get channel
        mov     bh,0                    ; bx = channel
        add     bx,offset dgroup:pcvb   ; add table to offset
        mov     al,[bx]                 ; get the value
        putn    al                      ; send it out
pcoutbx:nextv                           ; exit with display
;--------------------------------------------------------------
        public _pcoutc
_pcoutc:cmp     byte ptr var0+1[di],0abh; initialized?
        jz      pcoutc0                 ; yes, branch
        jmp     pcouti                  ; no, initialize
        ;
pcoutc0:test    usrflg,1                ; user input
        jnz     pcoutcx                 ; yes, no go
        mov     bl,var0[di]             ; get channel
        mov     bh,0                    ; bx = channel
        add     bx,offset dgroup:pcvc   ; add table to offset
        mov     al,[bx]                 ; get the value
        putn    al                      ; send it out
pcoutcx:nextv                           ; exit with display
;--------------------------------------------------------------
        public _pcoutd
_pcoutd:cmp     byte ptr var0+1[di],0abh; initialized?
        jz      pcoutd0                 ; yes, branch
        jmp     pcouti                  ; no, initialize
        ;
pcoutd0:test    usrflg,1                ; user input
        jnz     pcoutdx                 ; yes, no go
        mov     bl,var0[di]             ; get channel
        mov     bh,0                    ; bx = channel
        add     bx,offset dgroup:pcvd   ; add table to offset
        mov     al,[bx]                 ; get the value
        putn    al                      ; send it out
pcoutdx:nextv                           ; exit with display
;--------------------------------------------------------------
        public _pcoute
_pcoute:cmp     byte ptr var0+1[di],0abh; initialized?
        jz      pcoute0                 ; yes, branch
        jmp     pcouti                  ; no, initialize
        ;
pcoute0:test    usrflg,1                ; user input
        jnz     pcoutex                 ; yes, no go
        mov     bl,var0[di]             ; get channel
        mov     bh,0                    ; bx = channel
        add     bx,offset dgroup:pcve   ; add table to offset
        mov     al,[bx]                 ; get the value
        putn    al                      ; send it out
pcoutex:nextv                           ; exit with display
;--------------------------------------------------------------
        public _pcoutf
_pcoutf:cmp     byte ptr var0+1[di],0abh; initialized?
        jz      pcoutf0                 ; yes, branch
        jmp     pcouti                  ; no, initialize
        ;
pcoutf0:test    usrflg,1                ; user input
        jnz     pcoutfx                 ; yes, no go
        mov     bl,var0[di]             ; get channel
        mov     bh,0                    ; bx = channel
        add     bx,offset dgroup:pcvf   ; add table to offset
        mov     al,[bx]                 ; get the value
        putn    al                      ; send it out
pcoutfx:nextv                           ; exit with display
;--------------------------------------------------------------
        public _pcoutg
_pcoutg:cmp     byte ptr var0+1[di],0abh; initialized?
        jz      pcoutg0                 ; yes, branch
        jmp     pcouti                  ; no, initialize
        ;
pcoutg0:test    usrflg,1                ; user input
        jnz     pcoutgx                 ; yes, no go
        mov     bl,var0[di]             ; get channel
        mov     bh,0                    ; bx = channel
        add     bx,offset dgroup:pcvg   ; add table to offset
        mov     al,[bx]                 ; get the value
        putn    al                      ; send it out
pcoutgx:nextv                           ; exit with display
;--------------------------------------------------------------
        public _pcouth
_pcouth:cmp     byte ptr var0+1[di],0abh; initialized?
        jz      pcouth0                 ; yes, branch
        jmp     pcouti                  ; no, initialize
        ;
pcouth0:test    usrflg,1                ; user input
        jnz     pcouthx                 ; yes, no go
        mov     bl,var0[di]             ; get channel
        mov     bh,0                    ; bx = channel
        add     bx,offset dgroup:pcvh   ; add table to offset
        mov     al,[bx]                 ; get the value
        putn    al                      ; send it out
pcouthx:nextv                           ; exit with display
;--------------------------------------------------------------
        public _pcoutx
_pcoutx:cmp     byte ptr var0+1[di],0abh; initialized?
        jz      pcoutx0                 ; yes, branch
        jmp     pcouti                  ; no, initialize
        ;
pcoutx0:test    usrflg,1                ; user input
        jnz     pcoutxx                 ; yes, no go
        mov     bl,var0[di]             ; get channel
        mov     bh,0                    ; bx = channel
        add     bx,offset dgroup:pcvx   ; add table to offset
        mov     al,[bx]                 ; get the value
        putn    al                      ; send it out
pcoutxx:nextv                           ; exit with display
;--------------------------------------------------------------
        public _pcouty
_pcouty:cmp     byte ptr var0+1[di],0abh; initialized?
        jz      pcouty0                 ; yes, branch
        jmp     pcouti                  ; no, initialize
        ;
pcouty0:test    usrflg,1                ; user input
        jnz     pcoutyx                 ; yes, no go
        mov     bl,var0[di]             ; get channel
        mov     bh,0                    ; bx = channel
        add     bx,offset dgroup:pcvy   ; add table to offset
        mov     al,[bx]                 ; get the value
        putn    al                      ; send it out
pcoutyx:nextv                           ; exit with display
;--------------------------------------------------------------
        public _pcoutz
_pcoutz:cmp     byte ptr var0+1[di],0abh; initialized?
        jz      pcoutz0                 ; yes, branch
        jmp     pcouti                  ; no, initialize
        ;
pcoutz0:test    usrflg,1                ; user input
        jnz     pcoutzx                 ; yes, no go
        mov     bl,var0[di]             ; get channel
        mov     bh,0                    ; bx = channel
        add     bx,offset dgroup:pcvz   ; add table to offset
        mov     al,[bx]                 ; get the value
        putn    al                      ; send it out
pcoutzx:nextv                           ; exit with display
;--------------------------------------------------------------
; magenta input
;
        public  upcout
upcout: pop     ax              ; stack bullshit
        mov     cmdflg,10       ; set up special routine
        mov     special,offset upcoutx
        mov     di,varsav       ; point to variable list
        mov     al,outn[di]     ; get the output value
        mov     var0[di],al     ; save
        jmp     workx           ; on to the next stage

upcoutx:mov     di,varsav       ; point to variable list
        mov     al,outn[di]     ; get the new channel
        xchg    var0[di],al     ; set it, get orig patch
        mov     outn[di],al     ; restore
        jmp     _cancel         ; exit
;==============================================================
; add switch
; inputs: 0:   switch bits
;         1-8: switch inputs  
; output: sequencer send to the controller
;--------------------------------------------------------------
        public _addswa
_addswa:getv    dl,var0                 ; get the bits byte
        gettag                          ; es:bx = screen addr of tag
        dec     bx                      ; drop back 1 for led
        add     bx,640                  ; bx = screen addr of 1st stage led
        mov     bp,bx                   ; bp = leds
        add     di,var1                 ; point to first stage
        ;
        mov     ah,1                    ; set bit flag
        mov     al,0                    ; clear the add register
        mov     cx,8                    ; 8 to do
addswa1:
        test    dl,ah                   ; is the bit hi?
        jz      addswa3                 ; no, turn it off
        mov     byte ptr es:[bp],red+hi ; else turn the led on
        mov     bx,[di]                 ; get the value addr
        add     al,[bx]                 ; add in the value
        jmp     short addswa4           ; branch
addswa3:
        mov     byte ptr es:[bp],yellow ; turn the led off
addswa4:
        add     di,2                    ; point to next val
        shl     ah,1                    ; bump bit pointer
        add     bp,160                  ; point to next led
        loop    addswa1                 ; do it again
        ;
        mov     di,2[si]                ; set di pointing to variable list
        putn    al                      ; send it out
addswax:nextv                           ; exit with display
;==============================================================
; Programmer add switch output values
; inputs: 0-3: switch inputs  
;         4: control byte
; output: sequencer send to the controller
;--------------------------------------------------------------
        public _psouta
_psouta:getv    dl,var4                 ; get the value
        mov     cl,4                    ; get shift
        shr     dl,cl                   ; shift down
        and     dl,15                   ; mask
        ;
        gettag                          ; es:bx = screen addr of tag
        dec     bx                      ; drop back 1 for led
        add     bx,480                  ; bx = screen addr of 1st stage led
        mov     bp,bx                   ; bp = leds
        add     di,var0                 ; point to first stage
        ;
        mov     ah,1                    ; set bit flag
        mov     al,0                    ; clear the add register
        mov     cx,4                    ; 4 to do
psouta1:
        test    dl,ah                   ; is the bit hi?
        jz      psouta3                 ; no, turn it off
        mov     byte ptr es:[bp],red+hi ; else turn the led on
        mov     bx,[di]                 ; get the value addr
        add     al,[bx]                 ; add in the value
        jmp     short psouta4           ; branch
psouta3:
        mov     byte ptr es:[bp],yellow ; turn the led off
psouta4:
        add     di,2                    ; point to next val
        shl     ah,1                    ; bump bit pointer
        add     bp,160                  ; point to next led
        loop    psouta1                 ; do it again
        ;
        mov     di,2[si]                ; set di pointing to variable list
        putn    al                      ; send it out
psoutax:nextv                           ; exit with display
;--------------------------------------------------------------
        public _psoutc
_psoutc:getv    dl,var4                 ; get the value
        mov     cl,4                    ; get shift
        shr     dl,cl                   ; shift down
        and     dl,15                   ; mask
        ;
        gettag                          ; es:bx = screen addr of tag
        dec     bx                      ; drop back 1 for led
        add     bx,480                  ; bx = screen addr of 1st stage led
        mov     bp,bx                   ; bp = leds
        add     di,var0                 ; point to first stage
        ;
        mov     ah,1                    ; set bit flag
        mov     al,0                    ; clear the add register
        mov     cx,4                    ; 4 to do
psoutc1:
        test    dl,ah                   ; is the bit hi?
        jz      psoutc3                 ; no, turn it off
        mov     byte ptr es:[bp],red+hi ; else turn the led on
        mov     bx,[di]                 ; get the value addr
        add     al,[bx]                 ; add in the value
        jmp     short psoutc4           ; branch
psoutc3:
        mov     byte ptr es:[bp],yellow ; turn the led off
psoutc4:
        add     di,2                    ; point to next val
        shl     ah,1                    ; bump bit pointer
        add     bp,160                  ; point to next led
        loop    psoutc1                 ; do it again
        ;
        mov     di,2[si]                ; set di pointing to variable list
        putn    al                      ; send it out
psoutcx:nextv                           ; exit with display
;--------------------------------------------------------------
        public _psoutb
_psoutb:getv    dl,var4                 ; get the value
        and     dl,15                   ; mask
        ;
        gettag                          ; es:bx = screen addr of tag
        dec     bx                      ; drop back 1 for led
        add     bx,480                  ; bx = screen addr of 1st stage led
        mov     bp,bx                   ; bp = leds
        add     di,var0                 ; point to first stage
        ;
        mov     ah,1                    ; set bit flag
        mov     al,0                    ; clear the add register
        mov     cx,4                    ; 4 to do
psoutb1:
        test    dl,ah                   ; is the bit hi?
        jz      psoutb3                 ; no, turn it off
        mov     byte ptr es:[bp],red+hi ; else turn the led on
        mov     bx,[di]                 ; get the value addr
        add     al,[bx]                 ; add in the value
        jmp     short psoutb4           ; branch
psoutb3:
        mov     byte ptr es:[bp],yellow ; turn the led off
psoutb4:
        add     di,2                    ; point to next val
        shl     ah,1                    ; bump bit pointer
        add     bp,160                  ; point to next led
        loop    psoutb1                 ; do it again
        ;
        mov     di,2[si]                ; set di pointing to variable list
        putn    al                      ; send it out
psoutbx:nextv                           ; exit with display
;--------------------------------------------------------------
        public _psoutd
_psoutd:getv    dl,var4                 ; get the value
        and     dl,15                   ; mask
        ;
        gettag                          ; es:bx = screen addr of tag
        dec     bx                      ; drop back 1 for led
        add     bx,480                  ; bx = screen addr of 1st stage led
        mov     bp,bx                   ; bp = leds
        add     di,var0                 ; point to first stage
        ;
        mov     ah,1                    ; set bit flag
        mov     al,0                    ; clear the add register
        mov     cx,4                    ; 4 to do
psoutd1:
        test    dl,ah                   ; is the bit hi?
        jz      psoutd3                 ; no, turn it off
        mov     byte ptr es:[bp],red+hi ; else turn the led on
        mov     bx,[di]                 ; get the value addr
        add     al,[bx]                 ; add in the value
        jmp     short psoutd4           ; branch
psoutd3:
        mov     byte ptr es:[bp],yellow ; turn the led off
psoutd4:
        add     di,2                    ; point to next val
        shl     ah,1                    ; bump bit pointer
        add     bp,160                  ; point to next led
        loop    psoutd1                 ; do it again
        ;
        mov     di,2[si]                ; set di pointing to variable list
        putn    al                      ; send it out
psoutdx:nextv                           ; exit with display
;==============================================================
; Programmer add switch output values
; inputs: 0-3: switch inputs  
;         4: control byte 5: shift
; output: sequencer send to the controller
;--------------------------------------------------------------
        public _psoute
_psoute:getv    dl,var4                 ; get the value
        getv    cl,var5                 ; get shift
        shr     dl,cl                   ; shift down
        and     dl,15                   ; mask
        ;
        gettag                          ; es:bx = screen addr of tag
        dec     bx                      ; drop back 1 for led
        add     bx,480                  ; bx = screen addr of 1st stage led
        mov     bp,bx                   ; bp = leds
        add     di,var0                 ; point to first stage
        ;
        mov     ah,1                    ; set bit flag
        mov     al,0                    ; clear the add register
        mov     cx,4                    ; 4 to do
psoute1:
        test    dl,ah                   ; is the bit hi?
        jz      psoute3                 ; no, turn it off
        mov     byte ptr es:[bp],red+hi ; else turn the led on
        mov     bx,[di]                 ; get the value addr
        add     al,[bx]                 ; add in the value
        jmp     short psoute4           ; branch
psoute3:
        mov     byte ptr es:[bp],yellow ; turn the led off
psoute4:
        add     di,2                    ; point to next val
        shl     ah,1                    ; bump bit pointer
        add     bp,160                  ; point to next led
        loop    psoute1                 ; do it again
        ;
        mov     di,2[si]                ; set di pointing to variable list
        putn    al                      ; send it out
psoutex:nextv                           ; exit with display
;--------------------------------------------------------------
        public _psoutf
_psoutf:getv    dl,var4                 ; get the value
        getv    cl,var5                 ; get shift
        shr     dl,cl                   ; shift down
        and     dl,15                   ; mask
        ;
        gettag                          ; es:bx = screen addr of tag
        dec     bx                      ; drop back 1 for led
        add     bx,480                  ; bx = screen addr of 1st stage led
        mov     bp,bx                   ; bp = leds
        add     di,var0                 ; point to first stage
        ;
        mov     ah,1                    ; set bit flag
        mov     al,0                    ; clear the add register
        mov     cx,4                    ; 4 to do
psoutf1:
        test    dl,ah                   ; is the bit hi?
        jz      psoutf3                 ; no, turn it off
        mov     byte ptr es:[bp],red+hi ; else turn the led on
        mov     bx,[di]                 ; get the value addr
        add     al,[bx]                 ; add in the value
        jmp     short psoutf4           ; branch
psoutf3:
        mov     byte ptr es:[bp],yellow ; turn the led off
psoutf4:
        add     di,2                    ; point to next val
        shl     ah,1                    ; bump bit pointer
        add     bp,160                  ; point to next led
        loop    psoutf1                 ; do it again
        ;
        mov     di,2[si]                ; set di pointing to variable list
        putn    al                      ; send it out
psoutfx:nextv                           ; exit with display
;==============================================================
; MIDI sys excl send (8 bytes max)
; inputs: 0: trigger, 1-8 input values to send, 9x: clock tick
; output: none
;--------------------------------------------------------------
        public _sysxo
_sysxo:
        tick    var0,var9       ;
        jnc     sysxox          ; exit if no tick
        mov     al,0F0H         ; send sys ex opcode
        call    allmidi         ; send to midi
        mov     cx,8            ; 8 data bytes
sysxo1: getv    al,var1         ; get byte
        test    al,80H          ; don't send if ms bit is hi
        jnz     sysxo2          ; /
        call    allmidi         ; else dump to midi
sysxo2: add     di,2            ; bump pointer
        loop    sysxo1          ; do them all
        mov     al,0F7H         ; send eox
        call    allmidi         ; /
sysxox: nextx                   ; exit
;==============================================================
; MIDI Control change
; inputs: 0: clock, 1: channel, 2: Control number, 3: Control value
;         4x: clock tick
; output: none
;--------------------------------------------------------------
        public _ctrlo
_ctrlo: 
        tick    var0,var4       ; clock tick
        jnc     ctrlox          ; exit if no tick
        getv    al,var1         ; get channel
        mchan   al              ; strip & set
        or      al,0B0H         ; MIDI Control Change
        call    tomidi          ; send it to midi
        getv    al,var2         ; get control number
        and     al,127          ; /
        call    tomidi          ; send data to midi
        getv    al,var3         ; get control value
        and     al,127          ; /
        call    tomidi          ; send data to midi
ctrlox: nextx
;==============================================================
; MIDI Control mapping
; first group of control modules
;--------------------------------------------------------------

        public _mapo   
_mapo:  test    usrflg,1                ; inputting now
        jnz     mapox                   ; yes, exit
        ;
        mov     bp,si                   ; save si
        mov     si,6[di]                ; set up for show
        mov     es,4[di]                ; /
        mov     bx,offset dgroup:ctrlmap; point to control map
        add     si,320                  ; point to 1st value screen location
        add     di,var0                 ; point to 1st value in module
        mov     cx,8                    ; # of values
        ;
mapo1:  mov     al,[bx]                 ; look at value
        cmp     al,[di]                 ; same as in module?
        jz      mapo1a                  ; yes, branch
        mov     [di],al                 ; no, make same
        tohex   al                      ; display
        mov     es:[si],ah              ;  /
        mov     es:2[si],al             ; /
mapo1a: inc     bx                      ; next map number
        add     di,2                    ; next value location
        add     si,160                  ; next screen location
        loop    mapo1                   ; do another
        ;
        mov     si,bp                   ; restore si
mapox:  nextx                           ; exit
;--------------------------------------------------------------
        public umapo
umapo:  pop     ax              ; stack bullshit
        mov     cmdflg,10       ; set up special routine
        mov     special,offset umapox
        jmp     workx           ; on to the next stage

umapox:
        mov     bx,holdv        ; get offset to the value
        mov     di,varsav       ; get value table for module
        mov     dl,10[bx+di]    ; get current value
        shr     bx,1            ; /2 for byte table
        dec     bx              ; 1->n --> 0->n-1
        mov     di,offset dgroup:ctrlmap; point to control map
        mov     [di+bx],dl      ; set new ctrl code
        jmp     _cancel
;==============================================================
; MIDI Control mapping
; second group of control modules
;--------------------------------------------------------------

        public _mapp   
_mapp:  test    usrflg,1                ; inputting now
        jnz     mappx                   ; yes, exit
        ;
        mov     bp,si                   ; save si
        mov     si,6[di]                ; set up for show
        mov     es,4[di]                ; /
        mov     bx,offset dgroup:ctrlmpp; point to control map
        add     si,320                  ; point to 1st value screen location
        add     di,var0                 ; point to 1st value in module
        mov     cx,8                    ; # of values
        ;
mapp1:  mov     al,[bx]                 ; look at value
        cmp     al,[di]                 ; same as in module?
        jz      mapp1a                  ; yes, branch
        mov     [di],al                 ; no, make same
        tohex   al                      ; display
        mov     es:[si],ah              ;  /
        mov     es:2[si],al             ; /
mapp1a: inc     bx                      ; next map number
        add     di,2                    ; next value location
        add     si,160                  ; next screen location
        loop    mapp1                   ; do another
        ;
        mov     si,bp                   ; restore si
mappx:  nextx                           ; exit
;--------------------------------------------------------------
        public umapp
umapp:  pop     ax              ; stack bullshit
        mov     cmdflg,10       ; set up special routine
        mov     special,offset umappx
        jmp     workx           ; on to the next stage

umappx:
        mov     bx,holdv        ; get offset to the value
        mov     di,varsav       ; get value table for module
        mov     dl,10[bx+di]    ; get current value
        shr     bx,1            ; /2 for byte table
        dec     bx              ; 1->n --> 0->n-1
        mov     di,offset dgroup:ctrlmpp; point to control map
        mov     [di+bx],dl      ; set new ctrl code
        jmp     _cancel
;==============================================================
; MIDI note out.  
; All parameters except Velocity are sent when changed. 
; Velocity changes are ignored except zero transitions.
; When Velocity = 0, a Key-off message is sent with the currently
; saved note; the first NZ Velocity seen will send a Key-on message 
; with the note value at the input, which gets saved, and the given
; velocity level.  While Velocity stays NZ, any change in note values
; will cause an immediate Key-off message with the old note, then a
; Key on message with the new note, and the latest Velocity value for
; that note.
;--------------------------------------------------------------
; inputs: 
;         0: Channel
;         1: Hold
;         2: Hold AND
;         3: Transpose
;         4: Transpose
;         5: Velocity Offset
;         6: Release Value
;         7,  9,11,13: Note value
;         8, 10,12,14: Velocity
;         ---------------------
;         15x,16x,17x,18x: saved note
;         15+,16+,17+,18+: velocity tick flags
;         ---------------------
;         outn+1: b7 = initialized flag
;         ---------------------
;         temp0:  transpose offset
;         temp1:  velocity  offset
;         temp2:  release value
;         temp3:  enable flag
;         ---------------------
;         dh:     channel
;--------------------------------------------------------------
        public _noteo,_noteo1,_noteo2,_noteo3
_noteo3:nop
_noteo2:nop
_noteo1:nop
_noteo: 
        test    byte ptr outn+1[di],80h ; initialized?
        jnz     noteo1                  ; yes, branch
;--------------------------------------------------------------
; initialize
;--------------------------------------------------------------
        mov     cx,8                    ; 4 note+velocity inputs
        mov     bx,0                    ; use for index
noteo0: mov     byte ptr var15[bp+di],0 ; clear
        inc     bx                      ; bump index
        loop    noteo0                  ; loop
        initv   var2,0ffh               ; Hold mask <- FF
        mov     byte ptr outn+1[di],80h ; set init flag
        nextx                           ; ... on to next module 
;--------------------------------------------------------------
; Get Channel Number
;--------------------------------------------------------------
noteo1: getv    dh,var0                 ; get it
        mchan   dh                      ; mask & set
        xchg    outn[di],dh             ; save for next time
        cmp     outn[di],dh             ; same as last?
        jnz     noteo2a                 ; no, mute with old
;--------------------------------------------------------------
; Check for Enable/Mute
;--------------------------------------------------------------
        mov     temp3,0                 ; zip enable flag
        getv    al,var1                 ; get enable
        getv    ah,var2                 ; get enable mask
        and     al,ah                   ; /
        jz      noteo2a                 ; mute if not enabled
        mov     al,mprstf               ; stop set
        or      al,lodflg               ; or load flag
        jz      noteo2x                 ; branch if no mute
noteo2a:mov     temp3,1                 ; else set it
noteo2x:mov     es,4[di]                ; get seg addr
        mov     bx,6[di]                ; get module screen address
        dec     bx                      ; point to led
        test    temp3,1                 ; test for z
        mov     al,red                  ; set up for z
        jnz     noteo2b                 ; branch if z
        mov     al,red+hi               ; else set up for nz
noteo2b:mov     es:[bx],al              ; set the led
;--------------------------------------------------------------
; Get note Transpose value
;--------------------------------------------------------------
        getv    al,var3                 ; get it
        getv    ah,var4                 ; get it
        add     al,ah                   ; add together
        mov     temp0,al                ; put it away
;--------------------------------------------------------------
; Get Velocity offset
;--------------------------------------------------------------
        getv    al,var5                 ; get it
        mov     temp1,al                ; put it away
;--------------------------------------------------------------
; Get Release value   
;--------------------------------------------------------------
        getv    al,var6                 ; get it
        and     al,127                  ; strip msb
        mov     temp2,al                ; put it away
;--------------------------------------------------------------
; Key/velocity "A"
;--------------------------------------------------------------
        push    si                      ; save si
        mov     si,di                   ; use as saved value ptr
        add     si,var15                ; si = onv, si+1 = vflag
        mov     bp,4                    ; do 4 of them
        ;
noteo4: test    temp3,1                 ; mute?
        jnz     noteo4a                 ; yes, branch
        ;
        getv    ah,var8                 ; get velocity
        or      ah,ah                   ; is it on?
        jnz     noteo4b                 ; yes, branch
        ;
noteo4a:test    byte ptr 1[si],1        ; was note on
        jz      noteo4x                 ; no, exit
        ;
        mov     al,[si]                 ; yes, turn old note off
        call    noteoff                 ; /
        mov     byte ptr 1[si],0        ; zip vflag
        jmp     noteo4x                 ; exit
        ; 
noteo4b:test    byte ptr 1[si],1        ; was note on
        jnz     noteo4c                 ; yes, branch
        ;
        getv    al,var7                 ; get the note
        add     al,temp0                ; add offset
        mov     [si],al                 ; save it
        call    noteon                  ; turn it on
        mov     byte ptr 1[si],1        ; flag it
        jmp     noteo4x                 ; exit
        ;
noteo4c:getv    al,var7                 ; get the note
        add     al,temp0                ; add offset
        cmp     al,[si]                 ; has it changed?
        jz      noteo4x                 ; no, exit
        ;
        xchg    al,[si]                 ; yes, swap new w old
        call    noteoff                 ; turn old off
        mov     al,[si]                 ; turn new on
        call    noteon
        ;
noteo4x:
        add     si,2                    ; bump pointers
        add     di,4                    ; /
        dec     bp                      ; dec loop index
        jnz     noteo4                  ; loop
        pop     si                      ; restore si
        nextx
;--------------------------------------------------------------
; Note On 
; call nv in al, v in ah
;--------------------------------------------------------------
Noteon:
        xchg    al,dh                   ; get channel
        or      al,090H                 ; MIDI Key-on
        call    tomidi                  ; send Key-on+channel
        xchg    al,dh                   ; get nv, restore channel
        and     al,127                  ; strip msb
        call    tomidi                  ; send Note value
        mov     al,ah                   ; get velocity
        add     al,temp1                ; add offset
        jns     noteon1                 ; branch if < 128
        mov     al,127                  ; else make 127
noteon1:call    tomidi                  ; send Velocity
        ret                             ; exit
;--------------------------------------------------------------
; Note Off
; call nv in al
;--------------------------------------------------------------
Noteoff:
        xchg    al,dh                   ; get channel
        or      al,080H                 ; MIDI Key-off
        call    tomidi                  ; send Key-off+channel
        xchg    al,dh                   ; get nv, restore channel
        and     al,127                  ; strip msb
        call    tomidi                  ; send Note value
        mov     al,temp2                ; get release value
        call    tomidi                  ; send release value
        ret                             ; exit
;==============================================================
; MIDI channel output controller
;--------------------------------------------------------------
; inputs: 
;         0: channel
;         1: Hold
;         2: Program change
;         3: After Touch
;         4: Pitch Bend
;         ---------------------
;         5: Volume        (mapo 0)
;         6: Mod whl       (mapo 1)
;         7: controller A  (mapo 2)
;         8: controller B  (mapo 3)
;         9: controller C  (mapo 4)
;         10:controller D  (mapo 5)
;         11:controller 1  (mapo 6)
;         12:controller 2  (mapo 7)
;         ---------------------
;         13x: old program change
;         13+: old after touch
;         14x: old pitch bend
;         15x - 18+: old controller values
;         ---------------------
;         outn+1: b7 = initialized flag
;         ---------------------
;         dh:     channel
;--------------------------------------------------------------
        public _chano,_chanl
_chanl: mov     bp,offset dgroup:ctrlmpp; index for ctrlmap
        jmp     chanl0                  ; start w 2nd map
;--------------------------------------------------------------
_chano: mov     bp,offset dgroup:ctrlmap; index for ctrlmap
chanl0: test    byte ptr outn+1[di],80h ; initialized?
        jnz     chano1                  ; yes, branch
;--------------------------------------------------------------
; initialize
;--------------------------------------------------------------
        initv   var1,1                  ; Hold   <- 1
        initv   var4,080h               ; Bend   <- 80H
        initv   var5,07fh               ; Volume <- 7fH
        mov     byte ptr outn+1[di],80h ; set init flag
        ;
chano0: mov     cx,12                   ; 12 save areas
        mov     bx,0                    ; use for index
chano0a:mov     byte ptr var13[bx+di],-1; set to force change
        inc     bx                      ; bump index
        loop    chano0a                 ; loop
        or      clrchf,2                ; setup to clear
chano0x:
        nextx                           ; ... on to next module 
;--------------------------------------------------------------
; Get Channel Number
;--------------------------------------------------------------
chano1: 
        getv    dh,var0                 ; get it
        mchan   dh                      ; mask & set
        test    valflg,-1               ; inputting values?
        jnz     chano1a                 ; yes, branch out
        xchg    outn[di],dh             ; save for next time
        cmp     outn[di],dh             ; same as last?
        jnz     chano0                  ; no, force change
chano1a:
;--------------------------------------------------------------
; Check for Hold
;--------------------------------------------------------------
        getv    al,var1                 ; get hold
        or      al,mprstf               ; or program reset 
        or      al,lodflg               ; or load flag
        or      al,clrchf               ; clear chan flag?
        jnz     chano1b                 ; yes, force change
        mov     al,0                    ; clear led flag
;--------------------------------------------------------------
chano1b:mov     es,4[di]                ; get seg addr
        mov     bx,6[di]                ; get module screen address
        dec     bx                      ; point to led
        test    al,-1                   ; test for z
        jz      chano1c                 ; branch if z
        mov     byte ptr es:[bx],red    ; clr the led
        jmp     chano0                  ; reset stuff
chano1c:mov     byte ptr es:[bx],red+hi ; set the led
;--------------------------------------------------------------
; Patch Change
;--------------------------------------------------------------
        getv    al,var2                 ; get input value
        cmp     al,var13[di]            ; any change
        jz      chano2x                 ; no, branch
        and     al,7fh                  ; mask
        mov     var13[di],al            ; yes, store the new value
        mov     ah,0C0H                 ; MIDI Patch Change
        or      ah,dh                   ; add in channel info
        xchg    al,ah                   ; fix
        call    tomidi                  ; send it to midi
        xchg    al,ah                   ; fix
        call    tomidi                  ; send value to midi
chano2x:
;--------------------------------------------------------------
; Aftertouch
;--------------------------------------------------------------
        getv    al,var3                 ; get input value
        cmp     al,var13+1[di]          ; any change
        jz      chano3x                 ; no, branch
        and     al,127                  ; strip msb
        mov     var13+1[di],al          ; yes, store the new value
        mov     ah,0D0H                 ; MIDI After Touch
        or      ah,dh                   ; add in channel info
        xchg    al,ah                   ; fix
        call    tomidi                  ; send it to midi
        xchg    al,ah                   ; fix
        call    tomidi                  ; send value to midi
chano3x:
;--------------------------------------------------------------
; Pitch Bend
;--------------------------------------------------------------
        getv    al,var4                 ; get input value
        cmp     al,var14[di]            ; any change
        jz      chano5x                 ; no, branch
        mov     var14[di],al            ; yes, store the new value
;       add     al,80h                  ; center for pitch bend
        shr     al,1                    ; strip lsb
        rcr     ch,1                    ; put in ch
        rcr     ch,1                    ; mov into position
        and     ch,64                   ; clean it up
        mov     ah,0E0H                 ; MIDI Pitch Bend
        or      ah,dh                   ; add in channel info
        xchg    ah,al                   ; fix
        call    tomidi                  ; send it to midi
        mov     al,ch                   ; fix
        call    tomidi                  ; send ls value to midi
        mov     al,ah                   ; fix
        call    tomidi                  ; send ms value to midi
chano5x:
;--------------------------------------------------------------
; Controllers mapped by mapo module into array: ctrlmap 0-5
;--------------------------------------------------------------
        push    si                      ; save si
        mov     si,di                   ; use as pointer
        add     si,var15                ; /
        mov     cx,6                    ; 6 to do
chano6:
        getv    al,var5                 ; get input value
        cmp     al,[si]                 ; any change
        jz      chano6a                 ; no, branch
        and     al,127                  ; strip msb
        mov     [si],al                 ; yes, store the new value
        mov     ah,0B0H                 ; MIDI Control Change
        or      ah,dh                   ; add in channel info
        xchg    ah,al                   ; fix
        call    tomidi                  ; send it to midi
        mov     al,ds:[bp]              ; mapped ctrl number
        call    tomidi                  ; send it to midi
        xchg    ah,al                   ; fix
        call    tomidi                  ; send value to midi
chano6a:inc     bp                      ; bump pointers
        inc     si                      ;  /
        add     di,2                    ; /
        loop    chano6                  ; loop
;--------------------------------------------------------------
; Controllers mapped by mapo module into array: ctrlmap 6,7
;--------------------------------------------------------------
        mov     cx,2                    ; 2 to do
chano7:
        getv    al,var5                 ; get input value
        and     al,127                  ; strip msb, test for Z
        jz      chano7b                 ; branch if Z
        mov     al,127                  ; else make 127
chano7b:cmp     al,[si]                 ; any change
        jz      chano7a                 ; no, branch
        mov     [si],al                 ; yes, store the new value
        mov     ah,0B0H                 ; MIDI Control Change
        or      ah,dh                   ; add in channel info
        xchg    ah,al                   ; fix
        call    tomidi                  ; send it to midi
        mov     al,ds:[bp]              ; mapped ctrl number
        call    tomidi                  ; send it to midi
        xchg    ah,al                   ; fix
        call    tomidi                  ; send value to midi
chano7a:inc     bp                      ; bump pointers
        inc     si                      ;  /
        add     di,2                    ; /
        loop    chano7                  ; loop
        ;
        pop     si                      ; restore si
        nextx                           ; exit
;==============================================================
; MIDI event programmer/sequencer
; normal output, all inputs are magenta user inputs.            
; -- all number inputs use addr+1 to hold output flag copy
; inputs:
; 0x:   user input to change address
; 1x:   end address
; 2x:   Undo switch
; 2+:   user input flag
; 3x:   Store switch
; 3+:   initialized flag
; 4x:   Recall switch
; 4+:   module number
; 5x:   Save switch
; 6x:   Measures to next (first of sequencer data)
; 7x:   stage 1
; 8x:   stage 2     
; 9x:   stage 3      
; 10x:  stage A
; 11x:  stage B     
; 12x:  stage C
; 13x:  stage D
; 14x:  stage w slew E
; 15x:  slew value for E
; 16x:  stage w slew F
; 17x:  slew value for F
; 18x:  stage w slew G
; 19x:  slew value for G
; 20x:  stage w slew H
; 21x:  slew value for H
; slewc: slew value buffer C
; slewd: slew value buffer D
; slewe: slew value buffer E
; slewf: slew value buffer F
; pubuf: Undo Buffer
; psbuf: Save Buffer
;--------------------------------------------------------------
        public _progo
_progo: cmp     byte ptr var3+1[di],0abh; initialized?
        jz      progoii                 ; yes, branch
        ;
        mov     cx,98                   ; big mother
        mov     bx,var0                 ; clear variable space
progoi: mov     byte ptr [bx+di],0      ; zip it
        inc     bx                      ; bump address
        loop    progoi                  ; loop
        ;
        mov     al,modnum               ; get module number
        mov     byte ptr var4+1[di],al  ; save it
        mov     byte ptr var3+1[di],0abh; set init flag
        ;
progoii:test    usrflg,1                ; user input now?
        jz      progo0                  ; no, branch
        mov     byte ptr var2+1[di],1   ; yes, set show differences
        nextx                           ; ...easy exit
;--------------------------------------------------------------
; process master reset
;
progo0: mov     al,mpadrf               ; address change?
        or      al,al                   ; /
        jz      progo0a                 ; no, branch
        mov     al,mpadr                ; yes, get master address
        mov     var0[di],al             ; set local address = master
progo0a:
;--------------------------------------------------------------
        mov     bp,si                   ; save si
        mov     es,4[di]                ; get screen seg addr
        mov     si,6[di]                ; get screen ofst addr
;--------------------------------------------------------------
        test    mpmodf,1                ; programming or playing?
        jz      progo1                  ; branch if playing
        jmp     progo2                  ; else jump to programming
;--------------------------------------------------------------
; mode = playing
;--------------------------------------------------------------
;
progo1: test    mmstart,1               ; master measure start?
        jz      progo1b                 ; no, branch
        cmp     byte ptr var6[di],0     ; local measure count = 0?
        jz      progo1a                 ; yes, branch
        dec     byte ptr var6[di]       ; no, count down 1
        jmp     short progo1b           ; ...on to the next
;--------------------------------------------------------------
; address change
;
progo1a:mov     al,var1[di]             ; is end <= address
        cmp     al,byte ptr var0[di]    ; /
        jbe     progo1b                 ; yes, don't bump address
        test    mprstf,-1               ; master reset?
        jnz     progo1b                 ; yes, don't bump address
        inc     byte ptr var0[di]       ; else bump sequencer address
progo1b:mov     al,var0[di]             ; has address changed
        cmp     al,var0+1[di]           ; /
        jz      progo1c                 ; no, branch
        putn    al                      ; yes, send it out
        call    sq_ob                   ; yes copy seq to output buffer
        mov     byte ptr var2+1[di],1   ; set show differences
        showpq  0                       ; display address
progo1c:showpq  6                       ; display measure counter
        jmp     progo3                  ; branch to common stuff
;--------------------------------------------------------------
; mode = programming
;--------------------------------------------------------------
; address change
;
progo2: mov     al,var0[di]             ; has address changed
        cmp     al,var0+1[di]           ; /
        jz      progo2a                 ; no, branch
        putn    al                      ; yes, send it out
        call    ob_ub                   ; output buf to undo buf
        call    sq_ob                   ; sequencer to output buf
        showpq  0                       ; display the new value
        mov     byte ptr var2+1[di],1   ; set show differences
progo2a:
;--------------------------------------------------------------
; measures counter
;
        mov     al,var6[di]             ; measures value changed?
        cmp     al,var6+1[di]           ; /
        jz      progo2e                 ; no, branch
        mov     var6+1[di],al           ; yes, clear flag
        showpv  6                       ; display measure counter
        mov     byte ptr var2+1[di],1   ; set show differences
progo2e:
;--------------------------------------------------------------
; undo button
;
        test    byte ptr var2[di],1     ; undo button pushed?
        jz      progo2b                 ; no, branch
        mov     byte ptr var2[di],0     ; clear the button
        call    ub_ob                   ; swap undo with output
        jmp     short progo2z           ; show & tell
progo2b: 
;--------------------------------------------------------------
; store button
;
        test    byte ptr var3[di],1     ; store button pushed?
        jz      progo2c                 ; no, branch
        mov     byte ptr var3[di],0     ; clear the button
        call    sq_ub                   ; sequencer to undo buf
        call    ob_sq                   ; output buffer to seq
;       or      _mpab,2                 ; set buffer used flag
        jmp     short progo2z           ; show & tell
progo2c:
;--------------------------------------------------------------
; recall button
;                                               
        test    byte ptr var4[di],1     ; recall button pushed?
        jz      progo2d                 ; no, branch
        mov     byte ptr var4[di],0     ; clear the button
        call    ob_ub                   ; output buf to undo buf
        call    sb_ob                   ; save buf to out buf
        jmp     short progo2z           ; show & tell
progo2d:
;--------------------------------------------------------------
; save button
;
        test    byte ptr var5[di],1     ; save button pushed?
        jz      progo2x                 ; no, branch
        mov     byte ptr var5[di],0     ; clear the button
        call    ob_sb                   ; output buf to save buf
;--------------------------------------------------------------
progo2z:mov     byte ptr var2+1[di],1   ; set show differences
progo2x:
;--------------------------------------------------------------
; common code for programming or playing
;--------------------------------------------------------------
; process outbyte
;
progo3:
        mov     al,var7[di]             ; out byte change?
        cmp     al,var7+1[di]           ; /
        jz      progo3a                 ; no, jmp to next
        mov     var7+1[di],al           ; yes, set flag
        mov     bl,var4+1[di]           ; bx = Channel
        mov     bh,0                    ; /
        add     bx,offset dgroup:pcvx   ; point to outval table
        mov     [bx],al                 ; put value to the table
progo3a:showpv  7                       ; update display
;--------------------------------------------------------------
        mov     al,var8[di]             ; out byte change?
        cmp     al,var8+1[di]           ; /
        jz      progo3b                 ; no, jmp to next
        mov     var8+1[di],al           ; yes, set flag
        mov     bl,var4+1[di]           ; bx = Channel
        mov     bh,0                    ; /
        add     bx,offset dgroup:pcvy   ; point to outval table
        mov     [bx],al                 ; put value to the table
progo3b:showpv  8                       ; update display
;--------------------------------------------------------------
        mov     al,var9[di]             ; out byte change?
        cmp     al,var9+1[di]           ; /
        jz      progo3c                 ; no, jmp to next
        mov     var9+1[di],al           ; yes, set flag
        mov     bl,var4+1[di]           ; bx = Channel
        mov     bh,0                    ; /
        add     bx,offset dgroup:pcvz   ; point to outval table
        mov     [bx],al                 ; put value to the table
progo3c:showpv  9                       ; update display
;--------------------------------------------------------------
; send non-slew output values
;
;progo4:
;--------------------------------------------------------------
        mov     al,var10[di]            ; out byte change?
        cmp     al,var10+1[di]          ; /
        jz      progo4a                 ; no, jmp to next
        mov     var10+1[di],al          ; yes, set flag
        mov     bl,var4+1[di]           ; bx = Channel
        mov     bh,0                    ; /
        add     bx,offset dgroup:pcva   ; point to outval table
        mov     [bx],al                 ; put value to the table
progo4a:showpv  10                      ; update display
;--------------------------------------------------------------
        mov     al,var11[di]            ; out byte change?
        cmp     al,var11+1[di]          ; /
        jz      progo4b                 ; no, jmp to next
        mov     var11+1[di],al          ; yes, set flag
        mov     bl,var4+1[di]           ; bx = Channel
        mov     bh,0                    ; /
        add     bx,offset dgroup:pcvb   ; point to outval table
        mov     [bx],al                 ; put value to the table
progo4b:showpv  11                      ; update display
;--------------------------------------------------------------
        mov     al,var12[di]            ; out byte change?
        cmp     al,var12+1[di]          ; /
        jz      progo4c                 ; no, jmp to next
        mov     var12+1[di],al          ; yes, set flag
        mov     bl,var4+1[di]           ; bx = Channel
        mov     bh,0                    ; /
        add     bx,offset dgroup:pcvc   ; point to outval table
        mov     [bx],al                 ; put value to the table
progo4c:showpv  12                      ; update display
;--------------------------------------------------------------
        mov     al,var13[di]            ; out byte change?
        cmp     al,var13+1[di]          ; /
        jz      progo4d                 ; no, jmp to next
        mov     var13+1[di],al          ; yes, set flag
        mov     bl,var4+1[di]           ; bx = Channel
        mov     bh,0                    ; /
        add     bx,offset dgroup:pcvd   ; point to outval table
        mov     [bx],al                 ; put value to the table
progo4d:showpv  13                      ; update display
;--------------------------------------------------------------
; send values with slew
;
        mov     bx,slewe                ; point to slew buffer        
        mov     dx,var14[di]            ; get input & target
        mov     cl,var15[di]            ; get rate
        call    pslew                   ; get new slew value
        cmp     var14[di],dx            ; did it change?
        mov     var14[di],dx            ; (put it away)
        jz      progo4e                 ; no change,split
        ;
        mov     bl,var4+1[di]           ; bx = Channel
        mov     bh,0                    ; /
        add     bx,offset dgroup:pcve   ; point to outval table
        mov     [bx],dh                 ; put value to the table
        ;
progo4e:showpp  14                      ; update display
        showpq  15                      ; display & flag rate
;--------------------------------------------------------------
        mov     bx,slewf                ; point to slew buffer        
        mov     dx,var16[di]            ; get input & target
        mov     cl,var17[di]            ; get rate
        call    pslew                   ; get new slew value
        cmp     var16[di],dx            ; did it change?
        mov     var16[di],dx            ; (put it away)
        jz      progo4f                 ; no change,split
        ;
        mov     bl,var4+1[di]           ; bx = Channel
        mov     bh,0                    ; /
        add     bx,offset dgroup:pcvf   ; point to outval table
        mov     [bx],dh                 ; put value to the table
        ;
progo4f:showpp  16                      ; update display
        showpq  17                      ; display & flag rate
;--------------------------------------------------------------
        mov     bx,slewg                ; point to slew buffer        
        mov     dx,var18[di]            ; get input & target
        mov     cl,var19[di]            ; get rate
        call    pslew                   ; get new slew value
        cmp     var18[di],dx            ; did it change?
        mov     var18[di],dx            ; (put it away)
        jz      progo4g                 ; no change,split
        ;
        mov     bl,var4+1[di]           ; bx = Channel
        mov     bh,0                    ; /
        add     bx,offset dgroup:pcvg   ; point to outval table
        mov     [bx],dh                 ; put value to the table
        ;
progo4g:showpp  18                      ; update display
        showpq  19                      ; display & flag rate
;--------------------------------------------------------------
        mov     bx,slewh                ; point to slew buffer        
        mov     dx,var20[di]            ; get input & target
        mov     cl,var21[di]            ; get rate
        call    pslew                   ; get new slew value
        cmp     var20[di],dx            ; did it change?
        mov     var20[di],dx            ; (put it away)
        jz      progo4h                 ; no change,split
        ;
        mov     bl,var4+1[di]           ; bx = Channel
        mov     bh,0                    ; /
        add     bx,offset dgroup:pcvh   ; point to outval table
        mov     [bx],dh                 ; put value to the table
        ;
progo4h:showpp  20                      ; update display
        showpq  21                      ; display & flag rate
;--------------------------------------------------------------
; module execution ends here
; clean up & display output value
;--------------------------------------------------------------
        test    byte ptr var2+1[di],1   ; show differences?
        jz      progo6a                 ; no, branch
        call    sq?ob                   ; show if ob <> seq
progo6a:mov     si,bp                   ; restore si
        mov     byte ptr var2+1[di],0   ; clear user input flag
        nextv                           ; display output, exit
;--------------------------------------------------------------
; subroutine to update slew value
; call with bx = slew buffer offset (slewc, slewd, etc.)
;           dx = input/output (dh), target (dl)
;           cl = rate
; returns with dx = input/output, target
;
; formula:
;       initialize:
;               input * 256 --> sum; input --> output
;       thereafter until sum/256 = target:
;               sum - input + target --> sum
;               sum/256              --> output
;
; buffer offsets for values:
;       0: sum
;       2: saved input
;       3: saved target
;       4: saved rate
;       5: busy
;--------------------------------------------------------------
pslew:  test    byte ptr 5[bx+di],1     ; busy?
        jnz     pslewa                  ; yes, branch
        cmp     dl,dh                   ; no, see if target=output
        jnz     pslewb                  ; not equal, go slew
        cmp     dh,2[bx+di]             ; else see if input has changed
        jz      pslewx                  ; exit if no change                
        ;
pslewb: mov     3[bx+di],dl             ; save target
        mov     2[bx+di],dh             ; save input
        mov     4[bx+di],cl             ; save rate
        ;
        mov     dh,dl                   ; target --> output
        or      cl,cl                   ; rate = 0?
        jz      pslewx                  ; yes, just exit
;        test    mpmodf,1                ; programming or playing?
;        jnz     pslewx                  ; just exit if programming
        mov     byte ptr 5[bx+di],1     ; else flag busy
;
        mov     al,2[bx+di]             ; ax = input * 128
        mov     ah,0                    ;   /
        mov     cl,7                    ;  /
        shl     ax,cl                   ; /
;        mov     ah,2[bx+di]             ; ax = input * 256
;        mov     al,0                    ; /
        mov     [bx+di],ax              ; set new sum
        mov     dh,2[bx+di]             ; input --> output
        jmp     short pslewx            ; exit
        ;
pslewa: dec     byte ptr 4[bx+di]       ; countdown 
        jnz     pslewx                  ; /
        or      cl,cl                   ; rate set to 0?
        jnz     pslewc                  ; no, go on
        mov     dh,dl                   ; yes, set output=target
        jmp     short pslewz            ; exit finished
        ;
pslewc: mov     4[bx+di],cl             ; save rate count
        mov     ax,[bx+di]              ; ax = sum
        mov     cl,2[bx+di]             ; cx = input
        mov     ch,0                    ; /
        sub     ax,cx                   ; ax = sum - input
        mov     cl,3[bx+di]             ; cx = target
        add     ax,cx                   ; ax = sum - input + target
        mov     [bx+di],ax              ; save new sum
        mov     cl,7                    ; ax = new sum / 128
;
        shr     ax,cl                   ; /
        mov     dh,al                   ; dh = new sum / 128
;        mov     dh,ah                   ; dh = new sum / 256
pslewz: cmp     dh,dl                   ; output = target
        jnz     pslewx                  ; no, exit
        mov     byte ptr 5[bx+di],0     ; yes, flag not busy
        ;
pslewx: ret                             ; exit
;--------------------------------------------------------------
; subroutines to copy sequencer buffers
;
; ob_sb = obuf --> sbuf = output buffer to save buffer
; ob_ub = obuf --> ubuf = output buffer to undo buffer
; ob_sq = obuf --> seq  = output buffer to sequencer
; sb_ob = sbuf --> obuf = save buffer to output buffer
; sq_ob = seq  --> obuf = sequencer to output buffer
; sq_ub = seq  --> ubuf = sequencer to undo buffer
; ub_ob = ubuf <-> obuf = undo buffer swapped with output buffer
; sq?ob = show red if sq <> ob, else show yellow
;--------------------------------------------------------------
; ob_sb = obuf --> sbuf = output buffer to save buffer
;
ob_sb:  mov     bx,di           ; use bx for sbuf addr
        add     bx,psbuf        ; bx = sbuf addr
        jmp     short ob_ub0    ; go do it
;--------------------------------------------------------------
; ob_ub = obuf --> ubuf = output buffer to undo buffer
;
ob_ub:  mov     bx,di           ; use bx for sbuf addr
        add     bx,pubuf        ; bx = ubuf addr
        ;
ob_ub0: mov     si,di           ; get offset to obuf
        add     si,var6         ; /
        ;
        mov     cx,16           ; 16 in a loop
ob_ubl: mov     al,[si]         ; get obuf byte
        mov     [bx],al         ; put it away
        inc     bx              ; bump pointers
        add     si,2            ; /
        loop    ob_ubl          ; loop til done
        ;
        mov     si,6[di]        ; get screen ofst addr
        ret
;--------------------------------------------------------------
; ob_sq = obuf --> seq  = output buffer to sequencer
;
ob_sq:  mov     ah,var4+1[di]   ; offset for channel
        mov     al,0            ; /
        add     ax, seg bufpa   ; base seg of seq buffer
        mov     es,ax           ; es = seg addr of seq buf
        mov     al,var0[di]     ; get seq step
        mov     bl,16           ; bump up
        mul     bl              ; ax = seq addr
        mov     bx,ax           ; es:bx = seq addr
        ;
        mov     si,di           ; get offset to obuf
        add     si,var6         ; /
        ;
        mov     cx,16           ; 16 in a loop
ob_sql: mov     al,[si]         ; get buffer byte
        mov     es:[bx],al      ; put to sequencer
        inc     bx              ; bump pointers
        add     si,2            ; /
        loop    ob_sql          ; loop til done
        ;
        mov     es,4[di]        ; restore screen seg addr
        mov     si,6[di]        ; get screen ofst addr
        ret
;--------------------------------------------------------------
; sb_ob = sbuf --> obuf = save buffer to output buffer
;
sb_ob:  mov     bx,di           ; use bx for sbuf addr
        add     bx,psbuf        ; bx = sbuf addr

        mov     si,di           ; get offset to obuf
        add     si,var6         ; /
        ;
        mov     cx,16           ; 16 in a loop
sb_obl: mov     al,[bx]         ; get sbuf byte
        mov     [si],al         ; put to obuf
        inc     bx              ; bump pointers
        add     si,2            ; /
        loop    sb_obl          ; loop til done
        ;
        mov     si,6[di]        ; get screen ofst addr
        ret
;--------------------------------------------------------------
; sq_ob = seq  --> obuf = sequencer to output buffer
; 
sq_ob:  mov     ah,var4+1[di]   ; offset for channel
        mov     al,0            ; /
        add     ax,seg bufpa    ; base seg of seq buffer
        mov     es,ax           ; es = seg addr of seq buf
        mov     al,var0[di]     ; get seq step
        mov     bl,16           ; bump up
        mul     bl              ; ax = seq addr
        mov     bx,ax           ; es:bx = seq addr
        ;
        mov     si,di           ; get offset to obuf
        add     si,var6         ; /
        ;
        mov     cx,16           ; 16 in a loop
sq_obl: mov     al,es:[bx]      ; get the byte
        mov     [si],al         ; put it away
        inc     bx              ; bump pointers
        add     si,2            ; /
        loop    sq_obl          ; loop til done
        ;
        mov     es,4[di]        ; restore screen seg addr
        mov     si,6[di]        ; get screen ofst addr
        ret
;--------------------------------------------------------------
; sq_ub = seq  --> ubuf = sequencer to undo buffer
;
sq_ub:  mov     ah,var4+1[di]   ; offset for channel
        mov     al,0            ; /
        add     ax, seg bufpa   ; base seg of seq buffer
        mov     es,ax           ; es = seg addr of seq buf
        mov     al,var0[di]     ; get seq step
        mov     bl,16           ; bump up
        mul     bl              ; ax = seq addr
        mov     bx,ax           ; es:bx = seq addr
        ;
        mov     si,di           ; use si for sbuf addr
        add     si,pubuf        ; si = ubuf addr
        ;
        mov     cx,16           ; 16 bytes to move
sq_ubl: mov     al,es:[bx]      ; get seq byte
        mov     [si],al         ; put it in ubuf
        loop    sq_ubl          ; loop til done
        ;
        mov     es,4[di]        ; restore screen seg addr
        mov     si,6[di]        ; get screen ofst addr
        ret
;--------------------------------------------------------------
; ub_ob = ubuf <-> obuf = undo buffer swapped with output buffer
;
ub_ob:  mov     bx,di           ; use bx for sbuf addr
        add     bx,pubuf        ; bx = ubuf addr
        ;
        mov     si,di           ; get offset to obuf
        add     si,var6         ; /
        ;
        mov     cx,16           ; 16 in a loop
ub_obl: mov     al,[si]         ; get obuf byte
        xchg    [bx],al         ; put it away
        mov     [si],al         ; swap
        inc     bx              ; bump pointers
        add     si,2            ; /
        loop    ub_obl          ; loop til done
        ;
        mov     si,6[di]        ; get screen ofst addr
        ret

;--------------------------------------------------------------
; sq?ob show red for each of the 16 values when sq <> ob, 
; else show yellow
; 
sq?ob:  mov     ah,var4+1[di]   ; offset for channel
        mov     al,0            ; /
        add     ax, seg bufpa   ; base seg of seq buffer
        mov     es,ax           ; es = seg addr of seq buf
        mov     cx,ax           ; cx = copy of above
        mov     al,var0[di]     ; get seq step
        mov     bl,16           ; bump up
        mul     bl              ; ax = seq addr
        mov     bx,ax           ; es:bx = seq addr
        ;
        mov     si,di           ; get offset to obuf
        add     si,var6         ; ds:si = offset to obuf
        ;
        push    bp              ; save current bp
        mov     bp,ds:4[di]     ; bp = screen seg
        mov     dx,6[di]        ; dx = screen address
        add     dx,160*6+480    ; dx = offset to var6
        dec     dx              ; back up to point to label
        xchg    dx,di           ; di = offset to var6
        ;                       ; dx = old di (save)
        mov     ah,16           ; 16 in a loop
sq?obl: mov     al,es:[bx]      ; get the byte at var
        cmp     [si],al         ; same
        mov     al,yellow       ; pretend it is
        jz      sq?ob2          ; yes, branch
        mov     al,red
sq?ob2: mov     es,bp           ; get screen seg
        mov     es:[di],al      ; set color
        mov     es,cx           ; seq seg
        inc     bx              ; bump pointers
        add     si,2            ;  /
        add     di,160          ; /
        dec     ah              ; loop til done
        jnz     sq?obl          ; /
        ;
        pop     bp              ; restore bp
        mov     di,dx           ; restor variable indes
        mov     es,4[di]        ; restore screen seg addr
        mov     si,6[di]        ; get screen ofst addr
        ret
;--------------------------------------------------------------
; process magenta inputs
;
        public  uprogo
uprogo: pop     ax                      ; stack bullshit
        mov     di,varsav               ; get value table for module
        cmp     magflg,0                ; no-value?
        jnz     uprogb                  ; no, branch
        mov     bx,holdv                ; get offset to the value
        xor     byte ptr 10[bx+di],1    ; flip value of input
uprogr: jmp     _cancel                 ; exit
        ;
uprogb: mov     cmdflg,10               ; set up special routine
        mov     special,offset uprogr   ; data exit
        jmp     workx                   ; on to the next stage
;==============================================================
; beep a note on the pc speaker
; inputs: 0: gate, 1: pitch, 2x: note flags
; output: gate
;--------------------------------------------------------------
        public _bopo
_bopo:  
        test    mprstf,-1       ; stopped?
        jz      bopogo          ; no, go on
        call    toneoff         ; yes, turn off the speaker
        jmp     bopox           ; exit

bopogo: getv    al,var0         ; get the gate
        mov     dl,var2[di]     ; dl = on/off flags
        or      al,al           ; gate on?
        jz      bopo1           ; no, branch
        and     dl,offlg xor -1 ; yes, clear off flag
        test    dl,onflg        ; is on flag set?
        jz      bopo2           ; no, branch
bopo3:
        mov     di,2[si]        ;; set di pointing to variable list
        nextx                   ; yes, easy exit
bopo2:  or      dl,onflg        ; no, set on flag
        mov     var2[di],dl     ; put it away
;--------------------------------------------------------------
; note on
        getv    al,var1         ; get the pitch
        call    pitch           ; play it
        mov     cx,dx           ;     /
        call    toneset         ;   /
        call    toneon          ; /
        mov     ah,-1           ; load True
        putn    ah              ; flag to the output
        jmp     short bopox
;--------------------------------------------------------------
bopo1:  and     dl,onflg xor -1 ; clock is off, clear on flag
        test    dl,offlg        ; off flag set?
        jnz     bopo3           ; yes, easy exit
        or      dl,offlg        ; no, set the off flag
        mov     var2[di],dl     ; put it away
        call    toneoff         ; turn off the note
        mov     ah,0            ; load False
        putn    ah              ; flag to the output
bopox:
        mov     di,2[si]        ;; set di pointing to variable list
        nextx                   ; exit
;--------------------------------------------------------------
; play tones on the pc speaker
;
;--------------------------------------------------------------
; initialize timer chip
; Just called once
;
toneinit:
;        mov     al,2*40h+3*10h+3*2
;        out     43h,al
        ret
;--------------------------------------------------------------
; convert from frequency to period
; input: frequency in CX
; output: period in CX
;
freq:   push    dx
        push    ax
        ;
        mov     dx,12H
        mov     ax,34deh
        div     cx
        mov     cx,ax
        ;
        pop     ax
        pop     dx
        ret
;--------------------------------------------------------------
; convert from a pitch to a number
; input: pitch number in al
; output: value for TONESET in DX
;--------------------------------------------------------------
pitch:  push    cx
        push    bx
        push    ax
        ;
        mov     ah,0
        mov     cl,12
        div     cl
        ;
        mov     dl,al
        mov     al,ah
        cbw
        sal     ax,1
        mov     bx,ax
        mov     cx,notes[bx]
        call    freq
        ;
        xchg    cx,dx
        neg     cl
        add     cl,8
        sal     dx,cl
        ;
        pop     ax
        pop     bx
        pop     cx
        ret
;--------------------------------------------------------------
; select a tone
; input: tone in CX
; output: none
;
toneset:push    ax
        ;
        mov     al,cl
        out     42h,al
        jmp     short $+2
        mov     al,ch
        out     42h,al
        ;
        pop     ax
        ret
;--------------------------------------------------------------
; turn tone on
;
toneon: push    ax
        in      al,61H
        or      al,3
        out     61h,al
        pop     ax
        ret
;--------------------------------------------------------------
; turn tone off
;
toneoff:push    ax
        in      al,61h
        and     al,0fch
        out     61h,al
        pop     ax
        ret
;==============================================================
; pushbutton routine
; output is turned on/off every other push
; input is set only by user routine
;--------------------------------------------------------------
        public _pusha
_pusha:
        nextl                   ; exit
;--------------------------------------------------------------
        public  upusha
upusha: mov     di,varsav       ; point to variable list
        xor     byte ptr 8[di],1; flip the output
        pop     ax              ; keep stack right
        jmp     _cancel         ; return clean
;==============================================================
; pushbutton routine
; output is a single strobe, lasting from start of loop to priority.
; input is set only by user routine
;--------------------------------------------------------------
        public _pushb
_pushb: 
        nextt
;--------------------------------------------------------------
        public  upushb
upushb: mov     di,varsav       ; point to variable list
        mov     byte ptr 8[di],1; set hi
        pop     ax              ; keep stack right
        jmp     _cancel         ; return clean
;==============================================================
; pushbutton routine
; output is incremented/decremented every push, depending on
; cursor location on magenta pad. 
; input is set only by user routine
;--------------------------------------------------------------
        public _pushc
_pushc: nextv                           ; update screen
;--------------------------------------------------------------
        public  upushc
upushc: pop     ax                      ; stack bullshit
        cmp     magflg,1                ; inc/dec?
        jnz     upushc1                 ; no, branch
        ;
        mov     di,varsav               ; get value table for module
        mov     ax,curadr               ; up or down button?
        cmp     ax,cmdloc               ; if same, it's up
        jz      upushc2                 ; branch if same
        dec     byte ptr outn[di]       ; dec output value
        jmp     _cancel                 ; exit
upushc2:inc     byte ptr outn[di]       ; inc output value
        jmp     _cancel                 ; exit

upushc1:mov     cmdflg,10               ; set up special routine
        mov     special,offset upushc3  ; data exit
        jmp     workx                   ; exit

upushc3:mov     di,varsav               ; get value table
        mov     al,byte ptr var0[di]    ; get new value
        mov     byte ptr outn[di],al    ; send it out
        jmp     _cancel                 ; exit
;==============================================================
; Exit routine for modules that use msb of output byte.
; It displays output value, then executes the next module on the list.
; assumes di has been set to variable table
;
next:   mov     es,4[di]        ; get seg addr
        mov     bx,6[di]        ; get module screen address
        mov     al,8[di]        ; get output value
        tohex                   ; convert to hex word
        mov     es:320[bx],ah   ; put to the screen
        mov     es:322[bx],al   ; /
        nextx                   ; easy exit
;--------------------------------------------------------------
; Just execute the next module on the list.
;
        public _dummy
_dummy: nextx                   ; easy exit

;--------------------------------------------------------------
; this code converts in-line macros to jumps at the end of modules
;
        public  _nextv,_nextx,_nextl,_nextt
_nextv: @nextv
_nextx: @nextx
_nextl: @nextl
_nextt: @nextt
;==============================================================
; user input routines for magenta inputs
;
;--------------------------------------------------------------
        public ugatel
ugatel: pop     ax
        jmp     _cancel
;==============================================================
_TEXT   ENDS
        END

