'**************************************************************** '* Name : Fa_Valves.BAS * '* Author : Godfried-Willem RAES * '* Notice : Copyleft (c) 2011 Logosoft Public Domain * '* Date : 25-04-2011 * '* Version : 1.2 * '* Notes : first code for the 18F4620 * '**************************************************************** ' 22.04.2011: start code setup ' all five ports are required for the valve lookups ' 23.04.2011: First version of firmware ready for testing V1.0 ' for the fingered vibrato we will have to introduce a second task ' implemented here: note-on/off, with time out for valve release ' power on/off ctrl ' fingering controllers ' 24.04.2011: range bug removed ' start coding for fingered vibrato, using channel aftertouch ' 25.04.2011: cnthw overflows when reaching 00FF. Can this cause trouble? ' valve release seems not working. ' Portb.5 masked. ' Seems working now, though the lookups can be improved. ' fingered vibrato needs to be tested. ' 27.04.2011: first empirical tests with fingered vibrato. Firmware version 1.2 ' some code optimized for speed Include "18F4620.inc" ' for the Fa-Valves processor (40MHz) ' Mapping defines for midi-events on pin outputs and inputs: ' valves $define Valve1 PORTA.2 $define Valve2 PORTA.1 $define Valve3 PORTA.0 $define Valve4 PORTA.3 $define Valve5 PORTA.4 $define Valve6 PORTA.5 $define Valve7 PORTE.0 $define Valve8 PORTE.1 $define Valve9 PORTE.2 $define Valve10 PORTB.4 $define Valve11 PORTB.3 $define Valve12 PORTB.2 $define Valve13 PORTB.1 $define Valve14 PORTB.0 $define Valve15 PORTD.7 $define Valve16 PORTD.6 $define Valve17 PORTD.5 $define Valve18 PORTD.4 $define Valve19 PORTC.5 $define Valve20 PORTC.4 $define Valve21 PORTD.3 $define Valve22 PORTD.2 $define Valve23 PORTC.0 ' nc $define Valve24 PORTC.1 ' nc $define Valve25 PORTC.2 ' nc $define Valve26 PORTC.3 ' nc $define Valve27 PORTD.0 ' nc $define Valve28 PORTD.1 ' nc 'red LED for debug: $define Debug_Led PORTB.5 ' for testing - red led - watchdog ' $define CntLw TMR0L.Word this does not work, as CntLw now is the pointer ' We have to use the DIM construction Declare All_Digital = True ' makes all analog pins, digital for I/O ' configure the input and output pins: TRISA = %11000000 'bits set to 0 are output, 1 = input - bits 6 and 7 are the clock! TRISB = %11100000 'bits 6 and 7 are for the ICP, bit 5 is the red LED TRISC = %11000000 'RC6 en RC7 zijn USART I/O and must be set to input TRISD = %00000000 'all bits can be used TRISE = %11111000 'low nibble only, bit3 (RE3) is MCLR 'constant definitions: 'initialisations for the midi input parser: Symbol Midichannel = 15 ' Fa_Channel Symbol NoteOff_Status = 128 + Midichannel ' 2 bytes follow Symbol NoteOn_Status = 144 + Midichannel Symbol Keypres_Status = 160 + Midichannel Symbol Control_Status = 176 + Midichannel Symbol ProgChange_Status = 192 + Midichannel ' 1 byte message Symbol Aftertouch_Status = 208 + Midichannel ' 1 byte follows Symbol Pitchbend_Status = 224 + Midichannel ' lsb msb follow 'application specific constants Symbol NrTasks = 2 ' maximum 16 ' one single task serves as a valve release time out ' a second task handles fingered vibrato ' Setup the USART Declare Hserial_Baud = 31250 ' Set baud rate for the USART to MIDI specs. Declare Hserial_TXSTA = 0x24 ' instead of the normal 0x20 - ?? 0x24 ' Declare Hserial_Clear = On ' should clear on errors. Bytes get lost of course... This must be 31250 for MIDI ' Create variables: Dim Cnt As Dword System '32 bit counter Dim CntHw As Cnt.Word1 ' Word System 'used in the timer0 interrupt, where it is incremented Dim CntLw As TMR0L.Word 'this is the trick to read both TMR0L and TMR0H 'it makes Cntlw the low word of cnt, when we use cnt.word0=CntLw ' Dim Sr as TMR0L.7 '512 S/s - this works but these DO NOT WORK!!!: ' Dim Sr as CntLw.Byte1 does not ' Dim Sr As TMR0H.0 'sampling rate bit, 256 S/s ' DIM Sr as CntLw.8 ' As TMR0H.1 would be 128 S/s ' As TMR0H.2 would be 64 S/s ' As TMR0H.3 would be 32 S/s ' As TMR0H.4 would be 16 S/s Dim Bytein As Byte System ' midi byte read from buffer Dim StBit As Bytein.7 ' highest bit of ByteIn Dim i As Byte System ' general purpose counter ' midi variables Dim statusbyte As Byte System Dim noteUit As Byte System ' note off + release value Dim release As Byte System Dim noteAan As Byte System ' note on + release value Dim velo As Byte System Dim notePres As Byte System ' note pressure + pressure value Dim pres As Byte System Dim Ctrl As Byte System ' continuous controller + value Dim value As Byte System Dim prog As Byte System ' program change + program-byte Dim aft As Byte System ' channel aftertouch Dim pblsb As Byte System ' pitch bend lsb Dim pbmsb As Byte System ' pitch bend msb Dim veltim As Dword System ' 32 bit velo Dim newtim As Dword System Dim VelFlags As Word System ' bits 0 - 15 used as flags for active timers Dim VelFlags0 As VelFlags.Byte0 ' alias for bits 0-7 Dim VelFlags1 As VelFlags.Byte1 ' bits 8-15 - not used in this code. Dim CC66 As Byte System ' global on/off switch Dim PowerOn As CC66.0 Dim st As Byte System Dim b1 As Byte System Dim b2 As Byte System Dim Idx As Byte System Dim Playingnote As Byte System Dim CC30 As Byte System ' valve hold down time '----------------------------------------------------------------------------------------- ' Load the USART Interrupt handler and buffer read subroutines into memory Include "Midi_Irq.inc" ' our own version for UART And Timer0 Interrupt Include "Timers.inc" ' required for velo support with timed pulses and periods. 'Include "DwordArrays.inc" ' support for dword arrays. 'Include "ADC.inc" ' Load the ADC macros into the program 'framework for a multitasker: Dim Task_rsi[NrTasks] As Word 'task reschedule interval (period), if 0 the task is not active 'max. value limited to 65535. For longer periods, it will have to 'become dword!!! Dim Velmsb[NrTasks] As Word 'the application for velo-timers, is in fact just a one-shot task Dim VelLsb[NrTasks] As Word 'DeclareDwordArray(TimeVals , NrTasks) 'alternative using the macro's. [not yet used] ' assigning values syntax: DwordArray Timvals,[i], value ' reading values syntax: value = DwordArray TimVals,[i] Dim FingersA[46] As Byte ' 0 to 45 Dim FingersB[46] As Byte Dim FingersC[46] As Byte Dim FingersD[46] As Byte Dim FingersE[46] As Byte ' for the extended range 80-91 we use a section of the same lookup) 'make sure we initialize those pins on start up: 'fault?: there should be no executable statements outside the main program. Low Debug_Led CC66 = 0 CC30 = 127 ' 7 bits 'Task_rsi[0] = CC30 << 16 ' $00020000 ' release time for the valves ' bug: Task_rsi is only 16 bits! Task_rsi[0] = CC30 << 9 '----------------------------------------------------------------------------------------- ' Main program starts here MAIN: 'High Debug_Led DelayMS 10 ' wait for stability 'Low Debug_Led GoSub PowerDown GoSub Valve_Table ' read the fingerings lookup table Init_Usart_Interrupt ' Initiate the USART serial buffer interrupt ' this procedure is in the include file Clear_Serial_Buffer ' Clear the serial buffer and reset its pointers ' in the include as well ' following only required for pulse outputs: ' Note that we can only use timer0 since the other timers ' are used by the PWM subsystem ' Configure Timer0 for: ' Clear TMR0L and TMR0H registers ' Interrupt on Timer0 overflow ' 16-bit operation ' Internal clock source 40MHz ' 1:256 Prescaler : thus 40MHz / 256 = 156.250kHz Opentimer0 (Timer_INT_On & T0_16BIT & T0_SOURCE_INT & T0_PS_1_256) ' Setup the High priorities for the interrupts ' Open the ADC: ' not used on this board. ' Fosc/32 ' Right justified for 10-bit operation ' Tad value of 0 ' Vref+ at Vcc : Vref- at Gnd ' Make AN0 an analogue input ' ' OpenADC(ADC_FOSC_32 & ADC_RIGHT_JUST & ADC_0_TAD, ADC_REF_VDD_VSS, ADC_1ANA) ' for debug: ' set Velflags0.2 ' 'Task_rsi[2] = 65535 ' 32768 '8192 ' Cnt.Word0 = CntLw ' veltim = Cnt + 32768 'Task_rsi[2] 'add the period duration in Task_rsi[2] ' Velmsb[2] = veltim.Word1 ' VelLsb[2] = veltim.Word0 '--------------------------- ' start the main program loop: LOOP: 'PortD.1 = TMR0H.5 ' dit werkt 'PortD.1 = CntLw.12 ' werkt ook 'Valve28 = CntLw.11 ' werkt ook (ca 8Hz) ' Valve28 = CntHw.1 ' werkt ook - de timers lopen dus goed Cnt.Word0 = CntLw ' read counter 'Valve28 = Cnt.17 ' werkt ok 'Cnt.Word1 = CntHw ' read highword also, to complete cnt even when no timers are running ' no longer required, since CntHw is now defined as Cnt.Word1 ' Create an infinite loop Bytein = HRSIn ' Read data from the serial buffer, with no timeout ' Start the midi parser. Midi_Parse: If Bytein > Control_Status Then ' here higher statusses are not implemented. If Bytein > 253 Then '254 = midiclock, 255= reset 'midiclock can interrupt all other msg's... '255 had to be intercepted since thats what we 'get when no new byte flows in (?) GoTo Check_Timers 'throw away... Else Clear statusbyte 'reset the status byte GoTo Check_Timers 'throw away End If EndIf If StBit =1 Then 'should be faster than If Bytein > 127 Then 'status byte received, bit 7 is set Clear statusbyte 'if on another channel, the statusbyte needs a reset Select Bytein 'eqv to Select case ByteIn Case NoteOff_Status statusbyte = Bytein Set noteUit '= 255 'reset value. Cannot be 0 !!! Set release '= 255 '0 is a valid midi note! Case NoteOn_Status statusbyte = Bytein Set noteAan '= 255 Set velo '= 255 ' Case Keypres_Status 'not used for fingered vibrato ' statusbyte = Bytein ' notePres = 255 ' pres = 255 Case Control_Status ' for fingerings statusbyte = Bytein Set Ctrl '= 255 Set value '= 255 ' Case ProgChange_Status ' could be used for different lookup tables ' statusbyte = Bytein ' prog = 255 Case Aftertouch_Status ' for fingered vibrato statusbyte = Bytein Set aft '= 255 ' Case Pitchbend_Status ' statusbyte = Bytein ' pblsb = 255 ' pbmsb = 255 End Select Else 'midi byte is 7 bits Select statusbyte Case 0 'not a message for this channel GoTo Check_Timers 'disregard Case NoteOff_Status If noteUit = 255 Then noteUit = Bytein Else release = Bytein 'message complete, so we can do the action... Select noteUit Case 34 To 91 ' here we should start a timer. ' when the timers runs out, we release the valves. Set VelFlags0.0 'set the timer Clear Playingnote 'no note anymore Clear VelFlags0.1 'no fingered vibrato Cnt.Word0 = CntLw veltim = Cnt + Task_rsi[0] 'add the period duration Velmsb[0] = veltim.Word1 VelLsb[0] = veltim.Word0 End Select Set noteUit '= 255 'reset EndIf GoTo Check_Timers Case NoteOn_Status If noteAan = 255 Then noteAan = Bytein Else velo = Bytein If velo = 0 Then Select noteAan Case 34 To 91 ' equivalent to noteoff ' so we start a timer for valve releases. Set VelFlags0.0 'set the timer Clear VelFlags0.1 Clear Playingnote Cnt.Word0 = CntLw veltim = Cnt + Task_rsi[0] 'add the timeout duration Velmsb[0] = veltim.Word1 VelLsb[0] = veltim.Word0 End Select Else Select noteAan Case 34 To 79 Idx = noteAan - 34 Clear VelFlags0.0 ' start release timer only after note-off PORTA = FingersA[Idx] 'PORTB = FingersB[Idx] ' interferes with Portb.5, red LED !!! ' hence we do this trick: i = FingersB[Idx] i.5 = PORTB.5 ' read the red_led port 'FingersB[Idx].5 = PORTB.5 gaat niet met de syntax 'FingersB.5[Idx] = PortB.5 dit evenmin 'PortB = FingersB[Idx] PORTB = i PORTC = FingersC[Idx] PORTD = FingersD[Idx] PORTE = FingersE[Idx] Playingnote = noteAan Case 80 To 91 Idx = noteAan - 46 Clear VelFlags0.0 PORTA = FingersA[Idx] i = FingersB[Idx] i.5 = PORTB.5 PORTB = i PORTC = FingersC[Idx] PORTD = FingersD[Idx] PORTE = FingersE[Idx] Playingnote = noteAan End Select Set noteAan '= 255 'reset !!! EndIf EndIf GoTo Check_Timers ' Case Keypres_Status 'used for lite flashing on Fa-Hub ' 'in used by PIC2 for fingerings !!! ' 'here we could use it for fingered vibrato ' If notePres = 255 Then ' notePres = Bytein ' Else ' pres = Bytein ' 'GoSub KeyPres ' EndIf ' GoTo Check_Timers Case Control_Status 'this is where the action takes place for controllers If Ctrl = 255 Then Ctrl = Bytein Else value = Bytein GoSub Controller ' fingerings implemented here EndIf GoTo Check_Timers ' Case ProgChange_Status ' could be used to select alternative fingering lookups ' If prog = 255 Then 'single byte message ' prog = Bytein 'weak coding... ' GoSub ProgChange ' EndIf Case Aftertouch_Status If aft = 255 Then aft = Bytein GoSub Aftertouch EndIf ' GoTo Check_Timers End Select EndIf Check_Timers: ' here we check the Task counters and compare them with the 32 bit cnt value ' using the Velflags dword variable: 'If VelFlags0 > 0 Then 'if any bit is set here, there is a timer running If VelFlags0.0 = 1 Then ' on this board we use this timer for release veltim.Word1 = Velmsb[0] veltim.Word0 = VelLsb[0] 'Cnt.Word1 = CntHw 'Cnt.Word0 = CntLw 'read counter If Cnt >= veltim Then GoSub Task0 'valve timeout EndIf If VelFlags0.1 = 1 Then ' this timer is used for fingered vibrato veltim.Word1 = Velmsb[1] veltim.Word0 = VelLsb[1] 'Cnt.Word1 = CntHw 'Cnt.Word0 = CntLw 'read counter If Cnt >= veltim Then GoSub Task1 'vibrato End If 'Else 'If CntHw > 0xFF Then Clear CntHw ' reset if no counter is in use ' Clear CntHw ' harmless and faster than the above statement ' but, is it required?? ' EndIf ' for test only:------------------------------------------- 'If VelFlags0.2 = 1 Then ' for debug tests only ' veltim.Word1 = Velmsb[2] ' veltim.Word0 = VelLsb[2] ' 'Cnt.Word0 = CntLw 'read counter ' If Cnt >= veltim Then GoSub Task2 'End If '----------------------------------------------------------- GoTo LOOP ' end of the main loop 'KeyPres: ' ' not implemented on this board for ' notePres = 255 'Return 'ProgChange: ' prog = 255 'this is not realy required 'Return 'Pitchbend: ' 'only implemented on dsPIC based robots, irrelevant here ' set pblsb '= 255 'Return Aftertouch: 'this is the channel aftertouch, affecting any playing note 'used for fingered vibrato 'the value of aft is used to set the vibrato speed. 'if a note is playing, load task1 Select Playingnote Case 0 Clear VelFlags0.1 ' no note playing, means no vibrato possible Case 34 To 91 Set VelFlags0.1 Cnt.Word0 = CntLw 'read timer 'Cnt.Word1 = CntHw - not required Task_rsi[1] = (~aft & 127) << 9 ' speed controlled by aftertouch value veltim = Cnt + Task_rsi[1] 'add the period duration in Task_rsi[1] Velmsb[1] = veltim.Word1 VelLsb[1] = veltim.Word0 EndSelect Set aft ' = 255 'not mandatory Return Controller: ' here we implement 4 controllers for alternate fingering 4 x 7 = 28 Select Ctrl Case 30 ' valve release time controller CC30 = value If CC30 = 0 Then Clear VelFlags.0 Else Task_rsi[0] = CC30 << 9 ' shift to the high word Set VelFlags.0 End If Case 66 'on/off for the robot If value = 0 Then Clear PowerOn 'CC66.0 GoSub PowerDown Else Set PowerOn 'CC66.0 EndIf Case 100 Valve1 = value.0 Valve2 = value.1 Valve3 = value.2 Valve4 = value.3 Valve5 = value.4 Valve6 = value.5 Valve7 = value.6 Case 101 Valve8 = value.0 Valve9 = value.1 Valve10 = value.2 Valve11 = value.3 Valve12 = value.4 Valve13 = value.5 Valve14 = value.6 Case 102 Valve15 = value.0 Valve16 = value.1 Valve17 = value.2 Valve18 = value.3 Valve19 = value.4 Valve20 = value.5 Valve21 = value.6 Case 103 Valve22 = value.0 Valve23 = value.1 Valve24 = value.2 Valve25 = value.3 Valve26 = value.4 Valve27 = value.5 Valve28 = value.6 Case 123 GoSub PowerDown End Select Set Ctrl '= 255 'mandatory reset Return PowerDown: 'should switch off all valves activated Clear VelFlags0 'stop all running timers Clear CntHw 'Low Debug_Led ' following is no good, as it affects the TRIS settings ' Low Valve1 Clear Valve1 Clear Valve2 ' = 0 Clear Valve3 ' = 0 Clear Valve4 ' = 0 Clear Valve5 ' = 0 Clear Valve6 ' = 0 Clear Valve7 ' = 0 Clear Valve8 ' = 0 Clear Valve9 ' = 0 Clear Valve10 ' = 0 Clear Valve11 ' = 0 Clear Valve12 ' = 0 Clear Valve13 ' = 0 Clear Valve14 ' = 0 Clear Valve15 ' = 0 Clear Valve16 ' = 0 Clear Valve17 ' = 0 Clear Valve18 ' = 0 Clear Valve19 ' = 0 Clear Valve20 ' = 0 Clear Valve21 ' = 0 Clear Valve22 ' = 0 Clear Valve23 ' = 0 Clear Valve24 ' = 0 Clear Valve25 ' = 0 Clear Valve26 ' = 0 Clear Valve27 ' = 0 Clear Valve28 ' = 0 Return Task0: ' not really a task, as it doesn't refresh itself. ' this is the valve hold time-out Clear PORTA '= 0 Clear PORTB '= 0 Clear PORTC '= 0 Clear PORTD '= 0 Clear PORTE '= 0 Clear VelFlags0.0 ' one shot Return Task1: ' this is the task for fingered vibrato ' the task should stop on reception of a note-off If Playingnote = 0 Then Clear VelFlags0.1 ' stop finger vibrato task 'Low Valvexx ' not required. Just leave in the state it was. Else 'reload task1 Set VelFlags0.1 'can just stay set veltim = Cnt + Task_rsi[1] 'add the period duration in Task_rsi[1] Velmsb[1] = veltim.Word1 VelLsb[1] = veltim.Word0 Select Playingnote Case 34, 42, 43, 49, 53, 55 , 65, 77, 89 ',46,58,70,82 Toggle Valve10 ' Toggle Valve20 Case 35,36, 83,84 ',47,59,71,83 Toggle Valve9 ' to be tested. Case 37, 61, 73, 85 ',48,60,72,84 Toggle Valve5 Case 38, 41, 54 '37,49,61,73,85 Toggle Valve6 Case 39, 47, 50, 59, 62, 74, 86' , 38,50,62,74,86 Toggle Valve8 Case 40, 46, 48, 58, 60 ' 39,51,63,75,87 Toggle Valve7 Case 44 , 52, 56 ,64,76,88 Toggle Valve1 Case 45, 51, 57, 63, 75,87 '41,53,65,77,89 Toggle Valve12 Case 66,78,90 Toggle Valve6 Case 67,79,91 Toggle Valve7 Case 68,80 Toggle Valve8 Case 69,81 Toggle Valve9 Case Else Clear VelFlags0.1 EndSelect EndIf Return 'Task2: ' for debugging on testboard only ' 'Set VelFlags0.2 'can just stay set ' Valve28 = ~PortD.1 ' 'Toggle Valve28 ' PORTD.1 ' Valve28 ' PortD.1 ' ' reload timer: ' Cnt.Word0 = CntLw ' veltim = Cnt + 32768 ' Task_rsi[2] 'add the period duration in Task_rsi[2] ' Velmsb[2] = veltim.Word1 ' VelLsb[2] = veltim.Word0 'RETURN Valve_Table: ' index = midinoot - 34 FingersA[0] = %00000000 FingersB[0] = %00000000 ' portB FingersC[0] = %00000000 ' portC FingersD[0] = %00000000 FingersE[0] = %0000 ' portE FingersA[1] = %00000100 ' 35, port A FingersB[1] = %00000000 FingersC[1] = %00000000 FingersD[1] = %00000000 FingersE[1] = %0000 FingersA[2] = %00000110 ' 36 FingersB[2] = %00000000 FingersC[2] = %00000000 FingersD[2] = %00000000 FingersE[2] = %0000 FingersA[3] = %00000111 ' 37 FingersB[3] = %00000000 FingersC[3] = %00000000 FingersD[3] = %00000000 FingersE[3] = %0000 FingersA[4] = %00001110 ' 38 FingersB[4] = %00000000 FingersC[4] = %00000000 FingersD[4] = %00000000 FingersE[4] = %0000 FingersA[5] = %00011110 ' 39 FingersB[5] = %00000000 FingersC[5] = %00000000 FingersD[5] = %00000000 FingersE[5] = %0000 FingersA[6] = %00101110 ' 40 FingersB[6] = %00000000 FingersC[6] = %00000000 FingersD[6] = %00000000 FingersE[6] = %0000 FingersA[7] = %00101110 ' 41 FingersB[7] = %00000000 FingersC[7] = %00000000 FingersD[7] = %00000000 FingersE[7] = %0001 FingersA[8] = %00101110 ' 42 FingersB[8] = %00000000 FingersC[8] = %00000000 FingersD[8] = %00000000 FingersE[8] = %0101 FingersA[9] = %00101110 ' 43 FingersB[9] = %00000000 FingersC[9] = %00000000 FingersD[9] = %00000000 FingersE[9] = %0011 FingersA[10] = %00101110 ' 44 FingersB[10] = %00000100 FingersC[10] = %00000000 FingersD[10] = %00000000 FingersE[10] = %0011 FingersA[11] = %00101110 ' 45 FingersB[11] = %00001000 FingersC[11] = %00000000 FingersD[11] = %00000000 FingersE[11] = %0011 FingersA[12] = %00101110 ' 46 FingersB[12] = %00001001 FingersC[12] = %00000000 FingersD[12] = %00000000 FingersE[12] = %0011 FingersA[13] = %00101110 ' 47 FingersB[13] = %00001010 FingersC[13] = %00000000 FingersD[13] = %00000000 FingersE[13] = %0011 FingersA[14] = %00101110 ' 48 FingersB[14] = %00001010 FingersC[14] = %00000000 FingersD[14] = %10000000 FingersE[14] = %0011 FingersA[15] = %00001110 ' 49 FingersB[15] = %00001010 FingersC[15] = %00000000 FingersD[15] = %10100000 FingersE[15] = %0011 FingersA[16] = %00101110 ' 50 FingersB[16] = %00001010 FingersC[16] = %00000000 FingersD[16] = %11000000 FingersE[16] = %0011 FingersA[17] = %00111110 ' 51 FingersB[17] = %00001010 FingersC[17] = %00000000 FingersD[17] = %10010000 FingersE[17] = %0011 FingersA[18] = %00101110 ' 52 FingersB[18] = %00001010 FingersC[18] = %00000000 FingersD[18] = %11010000 FingersE[18] = %0011 FingersA[19] = %00101110 ' 53 FingersB[19] = %00001010 FingersC[19] = %00100000 FingersD[19] = %11010000 FingersE[19] = %0011 FingersA[20] = %00001110 ' 54 FingersB[20] = %00000000 FingersC[20] = %00100000 FingersD[20] = %00000000 FingersE[20] = %0101 FingersA[21] = %00111110 ' 55 FingersB[21] = %00000000 FingersC[21] = %00100000 FingersD[21] = %00000000 FingersE[21] = %0011 FingersA[22] = %00101110 ' 56 FingersB[22] = %00000100 FingersC[22] = %00100000 FingersD[22] = %00000000 FingersE[22] = %0011 FingersA[23] = %00101110 ' 57 FingersB[23] = %00001000 FingersC[23] = %00010000 FingersD[23] = %00000000 FingersE[23] = %0011 FingersA[24] = %00101110 ' 58 FingersB[24] = %00001001 FingersC[24] = %00010000 FingersD[24] = %00000000 FingersE[24] = %0011 FingersA[25] = %00101110 ' 59 FingersB[25] = %00001010 FingersC[25] = %00000000 FingersD[25] = %00001000 FingersE[25] = %0011 FingersA[26] = %00101110 ' 60 FingersB[26] = %00001010 FingersC[26] = %00000000 FingersD[26] = %10000000 FingersE[26] = %0011 FingersA[27] = %00001110 ' 61 FingersB[27] = %00001010 FingersC[27] = %00000000 FingersD[27] = %10100000 FingersE[27] = %0011 FingersA[28] = %00101110 ' 62 FingersB[28] = %00001010 FingersC[28] = %00000000 FingersD[28] = %11000000 FingersE[28] = %0011 FingersA[29] = %00101110 ' 63 FingersB[29] = %00000000 FingersC[29] = %00000000 FingersD[29] = %01000000 FingersE[29] = %0011 FingersA[30] = %00111110 ' 64 FingersB[30] = %00000000 FingersC[30] = %00000000 FingersD[30] = %00010000 FingersE[30] = %0011 FingersA[31] = %00111110 ' 65 FingersB[31] = %00001000 FingersC[31] = %00000000 FingersD[31] = %00010000 FingersE[31] = %0011 FingersA[32] = %00111110 ' 66 FingersB[32] = %00001000 FingersC[32] = %00100000 FingersD[32] = %00000000 FingersE[32] = %0001 FingersA[33] = %00111110 ' 67 FingersB[33] = %00001010 FingersC[33] = %00100000 FingersD[33] = %00000000 FingersE[33] = %0001 FingersA[34] = %00111110 ' 68 FingersB[34] = %00000010 FingersC[34] = %00100000 FingersD[34] = %10000000 FingersE[34] = %0011 FingersA[35] = %00111110 ' 69 FingersB[35] = %00000010 FingersC[35] = %00010000 FingersD[35] = %10100000 FingersE[35] = %0011 FingersA[36] = %00111110 ' 70 FingersB[36] = %00001000 FingersC[36] = %00010000 FingersD[36] = %00100000 FingersE[36] = %0001 FingersA[37] = %00111110 ' 71 FingersB[37] = %00001001 FingersC[37] = %00000001 FingersD[37] = %01001000 FingersE[37] = %0001 FingersA[38] = %00111110 ' 72 FingersB[38] = %00001001 FingersC[38] = %00000001 FingersD[38] = %01011000 FingersE[38] = %0001 FingersA[39] = %00111110 ' 73 FingersB[39] = %00000111 FingersC[39] = %00000000 FingersD[39] = %00011000 FingersE[39] = %0011 FingersA[40] = %00111110 ' 74 FingersB[40] = %00000111 FingersC[40] = %00100000 FingersD[40] = %10011000 FingersE[40] = %0011 FingersA[41] = %00001110 ' 75 FingersB[41] = %00001010 FingersC[41] = %00100000 ' RC5 should be 1/2 hole... FingersD[41] = %10100000 FingersE[41] = %0011 FingersA[42] = %00001110 ' 76 FingersB[42] = %00000000 FingersC[42] = %00000000 ' FingersD[42] = %00100000 FingersE[42] = %0011 FingersA[43] = %00101110 ' 77 FingersB[43] = %00000101 FingersC[43] = %00100000 ' FingersD[43] = %10100000 FingersE[43] = %0011 FingersA[44] = %00111110 ' 78, we use same as 66 FingersB[44] = %00001000 FingersC[44] = %00100000 FingersD[44] = %00000000 FingersE[44] = %0001 FingersA[45] = %00101110 ' 79 FingersB[45] = %00001010 FingersC[45] = %00010000 ' FingersD[45] = %11010000 FingersE[45] = %0011 Return '[EOF]