{\rtf1\ansi\ansicpg1252\uc1 \deff0\deflang1033\deflangfe1053{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f16\froman\fcharset238\fprq2 Times New Roman CE;}{\f17\froman\fcharset204\fprq2 Times New Roman Cyr;}{\f19\froman\fcharset161\fprq2 Times New Roman Greek;}{\f20\froman\fcharset162\fprq2 Times New Roman Tur;}{\f21\froman\fcharset186\fprq2 Times New Roman Baltic;}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\stylesheet{\nowidctlpar\widctlpar\adjustright \fs20\lang1053\cgrid \snext0 Normal;}{\*\cs10 \additive Default Paragraph Font;}}{\info{\title MPX61: A MIDI-IN Project for Matrix Keyboards}{\author MegaSize, of the ugly Birger!}{\operator ME96}{\creatim\yr1997\mo6\dy3\hr22\min3}{\revtim\yr1998\mo10\dy1\hr22\min28}{\printim\yr1997\mo6\dy3\hr22\min30}{\version3}{\edmins7}{\nofpages7}{\nofwords1720}{\nofchars9809}{\*\company Umeå Universitet}{\nofcharsws12046}{\vern73}}\paperw11906\paperh16838\margl1417\margr1417\margt1417\margb1417 \deftab1304\widowctrl\ftnbj\aenddoc\hyphhotz425\lytprtmet\formshade\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot \fet0\sectd \linex0\headery709\footery709\colsx709\endnhere\sectdefaultcl {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang{\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}\pard\plain \nowidctlpar\widctlpar\adjustright \fs20\lang1053\cgrid {MPX61: A MIDI-IN Project for Matrix Keyboards \par }\pard \nowidctlpar\widctlpar\adjustright { \par Note: this circuit and assembly-language program are from a project I did in 1985. It is far from elegant, but it works--even today! I have since completely \par redesigned both the hardware and software, but as the redesign was done specifically for a Korg PolySix (adding patch change as well), the new design is not \par nearly as easy to adapt to other matrix keyboards. The material here is meant simply as a guideline for those interested in rolling their own; it is up to them to figure \par out how the wiring and note<>matrix table for their particular synth is configured (These changes also required in the software.) \par \par The new design still uses a Z8 microcomputer, but also makes use of some sexy analog switches from Siliconix. Common-point switches with built-in latches and a \par serial interface are *much* easier to lay out on a circuit baord than eight 8-bit latches and sixteen 4066s. \par \par Anyway, a quick circuit rundown: \par \par The Z8603 is a Zilog Z8 NMOS CPU with a ROM socket attached. The CPU runs the program listed below to read in MIDI data, match the note on/off and \par sustain pedal data for a selected MIDI channel, then play the notes by storing them to a bank of latches (eight 8-bit latches = 64 bits), and using the state of each bit \par in the latches to drive the SPST analog switch sections in sixteen 4066 chips. Since a standard 5-octave keyboard has 61 keys, three of the latch bits and their \par corresponding switches and not used. The analog switches are simply wired (each with a matrix diode) in parallel with the switch matrix of the actual keyboard. \par \par There are a few nice things about the circuit. Since it operates completely outside the knowledge of the synth's on-board key-assigner, the MIDI-IN circuit and the \par key-assigner do not have to keep track of one another. If the MIDI-IN circuit is holding down more keys than the synth has assignables voices, the synth will react \par just as if the performer was holding down the actual keys on the keyboard. In addition, the circuit can be installed and removed from most synths without making \par any tricky mods to the existing hardware (the Polysix has a 16-pin connector for the keyboard; that is the only connection to the synth). Note that the keyboard can \par be played at the same time MIDI-IN data is playing notes; the two switch matrices do not interfere with each other. \par \par The nicest feature, however, is the MIDI pedal sustain, which will work on multi-voice synths even if the synth itself has no pedal sustain provision. Examine the \par code listing to see how it works. \par }{{\pict\wmetafile8\picw24836\pich16933\picwgoal14080\pichgoal9600 \picscalex64\picscaley64 \bin339104  P–"–  àÀ  àÀ ÿÿÿ"–C ÌàÀàÀ(Àà(ÿÿÿ}}{ \par Code Listing \par \par \par ; \par ; MPX-61 Universal MIDI card, software version 0.3 \par ; Copyright (c) 1985, 1986 by CSR/DMI (Christopher S. Rider) \par }\pard \nowidctlpar\widctlpar\adjustright { ; \par }\pard \nowidctlpar\widctlpar\adjustright { ; This version supports standard AGO keyboards, \par ; transpositon is by octave, C1 ~ C5, common-anode \par ; switching matrix. \par ; \par ; Note: This was assembled using an ancient CP/M cross assembler which \par ; didn't like the indexed forms \{LD INDEX(Rx),Ry\} although the \par ; actual opcodes are fine. They were DB'ed in as needed. \par ; \par ; Note 2: This code is ten years old. I would do some things differently \par ; today. This works just fine. Don't complain. :) \par ; \par ; CSR 11/29/95 \par ; \par RDA: EQU %00001000 ;Receiver Data Available \par TBE: EQU %00010000 ;Transmitter Buffer Empty \par RDA_: EQU %11110111 \par TBE_: EQU %11101111 \par \par CHANMSK: EQU %00001111 ;MIDI channel is lower nibble \par MIDIMSK: EQU %11110000 ;MIDI command is upper nibble \par \par DEF CHAN R:$10 ;Register 16 holds DIP switch value \par DEF MIDI_R R:$11 ;Receive data register \par DEF MIDI_T R:$12 ;Transmit data register \par DEF SUSFLG R:$13 ;Sustain pedal flag \par DEF IRQ R:$FA \par DEF SIO R:$F0 \par \par KEY_ON: EQU %10010000 \par KEY_OFF: EQU %10000000 \par CONTROL: EQU %10110000 ;Control parameter change \par SUSTAIN: EQU 64 ;Control parm=64 for sustain pedal \par \par K_TABLE: EQU $30-1 ;Registers 30h to 37h are the key table \par S_TABLE: EQU $38-1 ;Registers 38h to 3Fh are sustained keys \par ; \par ; \par ; \par ORG $000C \par \par INIT: \par SRP #$F0 ;Select control group \par \par LD R4,#2 ;Set timer for 31,250 baud \par }\pard \nowidctlpar\widctlpar\adjustright { LD R5,#%00000101 ;/ (R4=T0, R5=PRE0) \par }\pard \nowidctlpar\widctlpar\adjustright { \par LD R6,#%00000000 ;Set I/O ports, #2 as output (P2M) \par LD R7,#%01000001 ;#3 as SIO, active pull-ups (P3M) \par LD R8,#%01000101 ;#0 as input, #1 as output, int. stack \par \par CLR R11 ;Use polling for SIO operations \par LD R15,#127 ;Top of internal RAM \par LD R1,#%00000011 ;Start the UART \par \par SRP #0 ;Select "main" working set \par ; \par ; Clear the latches of random data \par ; \par CLR R2 ;Clear the 74LS374s \par LD R1,#%11111111 ;/ \par CLR R1 \par ; \par ; Clear out the key and sustain tables \par ; \par LD R4,#16 ;Eight passes in two tables \par CLR R5 \par \par KT_CLR: DB $D7,K_TABLE,$45 \par ; LD K_TABLE(R4),R5 ;Clear a byte \par DJNZ R4,KT_CLR \par ; \par ; Enable serial communications \par ; \par EI ;Enable polling flags \par \par CALL FLUSH ;Clear out UART \par \par LD CHAN,R0 ;Get the MIDI channel we work with \par AND CHAN,#CHANMSK \par CLR SUSFLG ;Clear sustain flag \par \par MAIN: LD R7,#%11111111 ;Assume a key on / pedal down \par CALL MIDI_IN ;Get MIDI byte... \par LD R5,MIDI_R ;/ \par AND R5,#CHANMSK ;Check for the right channel \par CP R5,CHAN ; / \par JR NZ,MAIN ;/ \par \par LD R5,MIDI_R \par }\pard \nowidctlpar\widctlpar\adjustright { AND R5,#MIDIMSK ;Now check the command bits \par }\pard \nowidctlpar\widctlpar\adjustright { \par CP R5,#KEY_ON \par JR Z,K_IS_ON \par \par CP R5,#KEY_OFF \par JR Z,K_IS_OFF \par \par CP R5,#CONTROL \par JR NZ,MAIN \par \par CTL_MSG: \par CALL MIDI_IN \par CP MIDI_R,#SUSTAIN ;Check for sustain \par JR NZ,MAIN \par \par CALL MIDI_IN \par OR MIDI_R,MIDI_R ;Pedal up or down? \par JR NZ,SUST \par ; \par ; Pedal released, clear sustain table \par ; \par CLR SUSFLG \par CLR R9 \par LD R10,#%10000000 \par LD R8,#8 \par SUSLP: DB $D7,S_TABLE,$89 \par ; LD S_TABLE(R8),R9 ;Clear table entry \par ; \par ; Don't kill keys that are active in key table, silence all others \par ; \par DB $C7,K_TABLE,$B8 \par ; LD R11,K_TABLE(R8) \par LD R2,R11 ;Yeah!! \par LD R1,R10 \par CLR R1 \par RR R10 ;Next bank \par DJNZ R8,SUSLP \par JR MAIN \par ; \par ; Pedal down \par ; \par SUST: LD SUSFLG,#$FF \par JR MAIN \par ; \par }\pard \nowidctlpar\widctlpar\adjustright { ; Key release \par }\pard \nowidctlpar\widctlpar\adjustright { ; \par K_IS_OFF: \par CLR R7 ;If key off, remember it \par ; \par ; Key down \par ; \par K_IS_ON: \par CALL MIDI_IN ;Get key number data \par LD R6,MIDI_R ;/ \par CALL MIDI_IN ;Get key velocity data \par \par OR MIDI_R,MIDI_R ;If velocity=0, key off \par JR NZ,DO_0 \par CLR R7 \par \par DO_0: SUB R6,#36 ;C1 key is group 0, bit 0 \par JR ULT,MAIN ;Below a C1? Forget it. \par \par CP R6,#60 ;C6 key is group 8, bit 4 \par JR UGT,MAIN \par ; \par ; Valid key number, create two values (R8 and R9) that represent where \par ; from the LSB in a byte the desired group and key bits are located. \par ; \par LD R9,R6 ;Prepare to point at key flags \par AND R9,#%00000111 ;(binary bit/key value) \par LD R8,R6 \par RR R8 \par RR R8 \par RR R8 \par AND R8,#%00000111 ;(binary group value) \par ; \par ; Adjust for offset to make DJNZ instruction loop the correct number of \par ; times, also clear the result registers, R10 and R11 \par ; \par INC R8 \par INC R9 \par CLR R10 \par CLR R11 \par LD R12,R8 \par LD R13,R9 \par ; \par ; Group bit determination: shift a "1" from the carry through the byte "n" \par ; times, where "n" = R12; the "1" ends up in the position corresponding \par ; to which latch is to receive the new key bit. \par ; \par SCF \par G_BIT: RLC R10 ;All these shifts and masks...bleah \par DJNZ R12,G_BIT \par ; \par ; Same thing for the key bit as above. \par ; \par SCF \par K_BIT: RLC R11 \par DJNZ R13,K_BIT \par ; \par ; Save the status of the bit in the key table, also put it in the sustain \par ; table if sustain is flagged. \par ; \par DB $C7,S_TABLE,$F8 \par ; LD R15,S_TABLE(R8) ;Get the bits of the group \par OR SUSFLG,SUSFLG ;Sustain? \par JR Z,SET_KEY \par \par OR R15,R11 ;Accumulate bits if sustained \par DB $D7,S_TABLE,$8F \par ; LD S_TABLE(R8),R15 \par \par SET_KEY: \par DB $C7,K_TABLE,$E8 \par ; LD R14,K_TABLE(R8) ;Get the bits of the group \par OR R7,R7 ;Look at key on/off \par JR Z,K_OFF \par \par OR R14,R11 ;Set the requested bit \par \par PLAY: DB $D7,K_TABLE,$8E \par ; LD K_TABLE(R8),R14 ;Save it in the key table \par ; \par ; Make sure that sustained notes get played. \par ; \par OR SUSFLG,SUSFLG \par JR Z,NOSUS \par \par OR R14,R15 \par \par NOSUS: LD R2,R14 ;(R2=port 2) \par LD R1,R10 ;Clock it into the selected group latch \par CLR R1 \par JP MAIN ;Always loop for more \par \par K_OFF: COM R11 ;Turn the bit off \par AND R14,R11 ;Reset the bit in the key table \par JR PLAY \par ; \par ; \par ; Utility routines \par ; \par ; \par MIDI_IN: \par TM IRQ,#RDA ;Check for RDA \par JR Z,MIDI_IN \par \par LD MIDI_R,SIO ;Get byte \par \par RTRN: AND IRQ,#RDA_ ;Reset the RDA bit \par RET \par \par MIDI_OUT: \par TM IRQ,#TBE ;Check for TBE \par JR Z,MIDI_OUT \par \par LD SIO,MIDI_T ;Send byte \par AND IRQ,#TBE_ ;Reset TBE bit \par RET \par \par FLUSH: \par TM IRQ,#RDA \par JR Z,RTRN \par \par LD R4,SIO \par AND IRQ,#RDA_ \par JR FLUSH \par \par ; \par ; The End \par ; \par \par \par \par MPX61 MIDI-IN / Christopher S. Rider / syzygy@mcs.com \par }}