PIC Mapping for midi to LPT decoder for musical robots using Intel 8254 Timer chips:
************************************************************************************
last update: 30.01.2005
Workout for <Vibi>
Default midi channel (set with DIP switch)= 10  ( = binary 1010)
Note that <Vibi> has a slightly different demultiplex board than Tubi. The circuit details are published on the <Vibi> page on the Logos website.

data-port: RB0-RB7  Port B van de PIC, alle bits geflipt: RB7 ==> D0	   pic pin 28
							  RB6 ==> D1	   pic pin 27
							  RB5 ==> D2	   pic pin 26
						          RB4 ==> D3	   pic pin 25
							  RB3 ==> D4	   pic pin 24
							  RB2 ==> D5 	   pic pin 23
							  RB1 ==> D6	   pic pin 22
							  RB0 ==> D7	   pic pin 21
adres-bus using port A, bits 0-5
Port A van de PIC:   RA0 = bit 0 van Reg	    	   		   pic pin 2
	             RA1 = bit 1 van Reg		   		   pic pin 3
		     RA2 = bit 2 van Reg			           pic pin 4
                     RA3 = nc			                           pic pin 5
                     RA4 = channel select dil switch bit 0                 pic pin 6  bit0
	             RA5 = channel select DIL switch bit 1                 pic pin 7  bit1

Port C van de PIC:   RC0 = Timer 1 output. Test LED op PIC board (zoals op het <Puff> board)
			   LED via 1k2 naar GND.			   pic pin 11
                     RC1 = nc   					   pic pin 12
                     RC2 = nc						   pic pin 13
                     RC3 = strobe bit (high at rest)   			   pic pin 14
                     RC4 = channel select DIL switch bit2	           pic pin 15 bit 2
		     RC5 = channel select DIL switch bit3                  pic pin 16 bit 3 
		     RC6 = serial output MIDI for debug			   pic pin 17
	             RC7 = serial input for MIDI data			   pic pin 18
			   (zoals op alle boards)


 GROUND:								   pic pin 19
									   pic pin 8
 POSITIVE SUPPLY:    5V dc						   pic pin 20

Note1: we connect RA4, RA5, RC4, RC5 to a DIL switch for setting the midi-channel (0-F) (read only on startup or after PIC reset).

Note2: it is essential that all data on the ports be stable before the strobe goes low. At rest, RA3 should always be high. Strobe duration must be 1 microsecond.

Software implementation:

' constants:
strobe = RC3, at rest should always be high.
dport = RB7-RB0 (8 bits)
aport = RA0-RA2 ( 3bits) (RA3 can be kept always low, such that we can always send a nibble)
reg(0) => RA0 = 1, RA1 = 0, RA2 = 1	(dec. 5)
reg(1) => RA0 = 0, RA1 = 0, RA2 = 1	(dec. 4)
reg(2) => RA0 = 1, RA1 = 1, RA2 = 1	(dec. 7)
reg(3) => RA0 = 0, RA1 = 1, RA2 = 1	(dec. 6)
reg(4) => RA0 = 1, RA1 = 0, RA2 = 0	(dec. 1)
reg(5) => RA0 = 0, RA1 = 0, RA2 = 0	(dec. 0)
reg(6) => RA0 = 1, RA1 = 1, RA2 = 0	(dec. 3)
reg(7) => RA0 = 0, RA1 = 1, RA2 = 0	(dec. 2)

global variables: 
midichannel as byte
dampmode    as byte
sustain     as byte

' procedure prototypes:
Vibi_Initialize
  local n as byte
        ' should be called on startup
        CALL Tubi_Stop              ' make sure power is OFF
	for n = 60 to 107           ' 96 is the note limit, but we have more timers on board.
            CALL Vibi_Beat n, 20    ' zie proc. - this will be inaudible since we have no power yet
            CALL Vibi_Damp n, 20
        next n
        CALL Vibi_Read_Port         ' only read after a reset of the PIC
        CALL Vibi_Start             ' autostart
end

Vibi_Read_Port
	IF RA4 = 1 then bit set (midichannel, 0) else bit reset (midichannel, 0)
	IF RA5 = 1 then bit set (midichannel, 1) else bit reset (midichannel, 1)
        IF RC4 = 1 then bit set (midichannel, 2) else bit reset (midichannel, 2)
        IF RC5 = 1 then bit set (midichannel, 3) else bit reset (midichannel, 3)
end

Vibi_Start
     ' sends pincode to board: (switch on the high power supply)
     ' this proc. should be called on reception of midi controller 66 with param = %true
      out dport, 251	' pincode number
      out aport, reg(3)
      strobe
      out aport, reg(2)
      out dport, 251    ' required for start up of the basic stamps (BS2)
      strobe              (RC3 high to low and back to high for 1 microsecond)
end

Vibi_Stop
     ' sends inverted pincode to board:(high power supply switched off
     ' not implemented on Vibi hardware yet
      out dport, 5	' inverted pincode
      out aport, reg(2)   ' port nr.3 - ext1port
      strobe
      out aport, reg(3) 
      strobe              '(RC3 high to low and back to high for 1 microsecond)
end



Vibi_Beat (note as byte, velo as byte)
                     ' this is the 8 bit msb only code for the timer chips
                     ' notes 60-96 = normal pitches
   local cw        as dword
   local wvelo     as dword


      if velo = 0 then exit function
      if note < 60 then exit function
      if note > 107 then exit function
      note = note - 60               
      wvelo = 3584 + ((velo * velo) /2)    ' kwadratisch 3584-10648
      
      velo = HIBYT(wvelo)              

      cw = n MOD 3          ' control word , here 0,1,2
      shift left cw, 6      ' put bits in the right place for the timer chip: D7 en D6 = select timer
      bit set cw,5          ' set bit 5, so be use msb only. (setting bit 4 would be lsb only)  	

                            ' velo:
                            ' in 256 microsecond increments
                            ' thus the range is 0.256ms to 32 ms for 1-127 velo.

      out aport, reg(1)     ' 2 bitlines velodatabus
      out dport, 3          ' set both lines high, write a command to the 82C54
      strobe
      out aport, reg(0)     ' databus 8 bit 
      out dport, cw         ' write program byte to the bus
      strobe
      out aport, reg(5) ' cause strobe on the 74154's
      out dport, note \ 3   ' chip number
      strobe
      out aport, reg(7)       '  
      strobe
      			    ' now send the required data to the timer:
      out aport, reg(1)
      out dport, note MOD 3   ' 0=timer0 1=timer1 2=timer2 3=control
      strobe
      
      out dport, note \ 3    ' prepare chip selector
      out aport, reg(5)
      strobe	
			     ' send 1 byte of timer data
      out aport, reg(0)
      out dport, velo
      strobe	

      out aport, reg(7)  ' generate a strobe
      strobe                  ' now the counter should be counting...
end

Vibi_Damp (note as byte, velo as byte)
                     ' should be called on reception of note off + release.
                     ' notes 60-96 = normal pitches
                     ' here there is less of a benefit in using 16 bit.
   local cw        as dword
   local wvelo     as dword


      if velo = 0 then exit function  ' no damping
      if note < 60 then exit function
      if note > 96 then exit function
      note = note - 12                ' harware mapped op notes 48 - 84
      wvelo = velo * velo   ' kwadratisch
      if wvelo < 3600 then wvelo = 3600   ' opgelet , andere scaling dan vibi_beat!!!
      velo = HIBYT(wvelo)              

      cw = n MOD 3          ' control word , here 0,1,2
      shift left cw, 6      ' put bits in the right place for the timer chip: D7 en D6 = select timer
      bit set cw,5          ' set bit 5, so be use msb only. (setting bit 4 would be lsb only)  	

                            ' velo:
                            ' in 256 microsecond increments
                            ' thus the range is 0.256ms to 32 ms for 1-127 velo.

      out aport, reg(1)     ' 2 bitlines velodatabus
      out dport, 3          ' set both lines high, write a command to the 82C54
      strobe
      out aport, reg(0)     ' databus 8 bit 
      out dport, cw         ' write program byte to the bus
      strobe
      out aport, reg(5)     ' cause strobe on the 74154's
      out dport, note \ 3   ' chip number
      strobe
      out aport, reg(7)       '  
      strobe
      			    ' now send the required data to the timer:
      out aport, reg(1)
      out dport, note MOD 3   ' 0=timer0 1=timer1 2=timer2 3=control
      strobe
      
      out dport, note \ 3    ' prepare chip selector
      out aport, reg(5)
      strobe	
			     ' send 1 byte of timer data
      out aport, reg(0)
      out dport, velo
      strobe	

      out aport, reg(7)  ' generate a strobe
      strobe                  ' now the counter should be counting...
end function

Vibi_Motor (motornumber, speed)
      if speed > 100 then speed = 100
      select case motornumber
             case 0
		out dport, speed
                out aport, reg(3)  ' port 4 - ext.2 port
                strobe
             case 1
		out dport, speed
                out  aport, reg(2)
                strobe
             case 2
		out dport speed
                out aport, reg(3)
                strobe
		out aport, reg(2)
                strobe
      end select
end function



Vibi_Beat_16bitcoding (note, velo)
      local wvelo as dword
      local cw as dword
                      ' this did not work on the wintel platform...
	IF note < 60 then exit function
	if note > 107 then exit function
        if isfalse velo then exit function

	note = note - 60
        'wvelo = 3584 + ((velo * velo) /2)    ' kwadratisch 3600-11648
        wvelo = 3584 + (velo * 100)          ' lineair in 100microsec. stapjes 3584 tot 16284
        cw = note MOD 3
        shift left cw, 6
        bit set cw, 4
        bit set cw, 5
        out aport, reg(1)
        out dport, 3   ' command
        strobe
	out aport, reg(0)
        out dport, cw
        strobe
	out aport, reg(5)
	out dport, note \ 3  ' chip number - integer divide
        strobe
	out aport, reg(7)
        strobe

        out aport, reg(1)
	out dport, note MOD 3
	strobe
	out aport, reg(5)
	out dport, note \ 3
	strobe
        			' now send lsb:
        out aport, reg(0)
	out dport, LOBYT(wvelo)
        strobe
        out aport, reg(7)
	strobe
        			' then msb:
        out aport, reg(0)
	out dport, HIBYT(wvelo)
        strobe
        out aport, reg(7)
	strobe   

        ' lsb-msb send, timer should be counting...
  
end function

FUNCTION Vibi_Midi_Input ()
        static init   
	static dampval
	static dampmode   ' 0 = use the damp value as set with the controller
			  ' 1 = use note-off + release for damping control
	static sustain    ' 1 = no damping
  if isfalse init then
    'Initialisation of default values:
        sustain = %False
	dampmode = %False
	damptim  = 64
	dampval  = 80
        init = %True
  end if

        if statusbyte = controller then
		select case controller
                       case 20
                            Vibi_Motor, 0, value
                       case 21
			    Vibi_Motor, 1, value
		       case 22
		            Vibi_Motor, 2, value
		       case 23
                            dampval = value
                       case 24
			    if value then dampmode = %True else dampmode = %False
		       case 64			' sustain pedal
			    if value the sustain = %true else sustain = %False		
		       case 66
			    CALL Vibi_On	' quite impossible with vibi hardware...		
		       case 123
			    sustain = %False
			    dampmode = %False
			    dampval = 80
			    Call Vibi_All_Off
                       case 127
			    ' reset PIC, reread receive channel        
                end select
        end if
        if statusbyte = note-on then
           select case note
	      case > 59
		select case velo
			case %False
				if isfalse sustain then
					if dampval then
						if dampmode then
							CALL Vibi_Damp noot, dampval
						else
							' niks - note-off release should be used.
						end if
					end if 
				else
					' no damping at all !!!
				end if
			case else
				CALL Vibi_Beat noot, velo
		end select
              case 12 to 48   ' play with the dampers as beaters!
                        note = note + 48
			CALL Vibi_Damp noot, velo
	   end select
        end if 
        if statusbyte = note-off then
		select case value
			case %False
				' no damping
			case else
				select case dampmode
					case %False
						CALL Vibi_Damp, noot, value
					case %True
					     IF dampval then
						CALL Vibi_Damp, noot, dampval  ' improper coding by user
				             end if
				end select
		end select
        end if
end function


Midi implementation:
<Vibi> should listen to Note On + Velo commands for notes 60-96.
<Vibi> should listen to Note Off + Velo commands for notes 60-96 for the dampers.
<Vibi> listens to following controllers:
	20		' motor 0
	21              ' motor 1
	22              ' both motors
	23              ' set damping force 
	24              ' set damping mode
        64              ' sustain  - sets sustain mode
	66              ' power ON
	123             ' all notes off / resets controllers
	127             ' pic reset

Vibi should listen to the channel set with the dip switches (0-15). 
The normal channel-setting for Vibi is 10.
The pincode command can be implemented as a binary midi controller >= 66 (on/off).
The user should send this controller in order to enable Vibi.