10
20
30
40 :
50
60
70
80
90
100
110
120
130 :
140 OSARGS=&FFDA:OSBYTE=&FFF4:WORDV=&20C
150 :
160 DIM mcode% &280:load%=&FFFF0900
170 ptr=&A8:num=&AA:regA=&EF:ctrl=&F0
180 :
190 FOR P=0 TO 1
200 P%=load%:O%=mcode%
210 [OPT P*3+4
220 .defAddr:EQUB &FCF0 AND 255 :\ Default I/O port
230 .defClk :EQUB 0 :\ Default clock setting 0,&15,&16
240 EQUS "v1.20":EQUS STRING$(1,CHR$0) :\ Spare padding
250 :
260 .RdNum
270 LDX #0
280 .RdNumLp
290 LDA (ptr),Y:CMP #ASC"0":BCC RdNumEnd
300 CMP #ASC"9"+1:BCC P%+4:SBC #7
310 AND #15:STA num
320 TXA:ASL A:ASL A:ASL A:ASL A
330 ORA num:TAX:INY:BNE RdNumLp
340 .RdNumEnd
350 TXA:RTS
360 .SkipSpc
370 LDA (ptr),Y:INY:CMP #ASC" ":BEQ SkipSpc:RTS
380 :
390 .go%
400 LDX defAddr:JSR setPorts :\ Set default I/O port
410 LDA defClk:STA mClock :\ Set default Clock
420 LDA #1:LDX #ptr:LDY #0:JSR OSARGS
430 LDA (ptr),Y:CMP #13:BEQ initON :\ *MIDI -> Do *MIDI ON
440 AND #&DF:CMP #ASC"O":BNE initNum :\ Not *MIDI ON/OFF
450 INY:LDA (ptr),Y:AND #&DF
460 CMP #ASC"N":BEQ initON :\ *MIDI ON
470 CMP #ASC"F":BEQ initOFF:DEY :\ *MIDI OFF
480 .initNum
490 CMP #ASC"&"AND&DF:BEQ initAddr :\ &nnnn - I/O Address
500 JSR RdNum:CMP #&10:BCC P%+4:SBC #7 :\ Get channel as dec or hex
510 JSR midiSetChannel :\ Convert 1..16 to 0..15
520 JSR SkipSpc:CMP #ASC"&":BNE initON
530 .initAddr
540 INY:JSR RdNum:JSR setPorts :\ Set I/O address
550 .initON
560 LDX mClock:BNE initClock :\ Use coded default
570 LDA #&15:STA mClock:JSR midiReset :\ Set to Divide By 16
580 PHP:SEI:LDY #4 :\ Time TxData clearing, X=0
590 .initLp1:LDA #0:JSR midiOutWR :\ Send note=0,vel=0
600 .initLp2:DEX:JSR midiTxRDY:BEQ initLp2
610 DEY:BNE initLp1:PLP:TXA:AND #&40 :\ X should be about &B0
620 BEQ initClock:INC mClock :\ If not, use Divide By 64
630 .initClock
640 JSR midiReset :\ Reset ACIA and set Clock
650 JSR cmpWORDV:BEQ initExit :\ Already redirected
660 LDA WORDV+0:STA oldWORDV+0
670 LDA WORDV+1:STA oldWORDV+1
680 LDA #midiWORD AND 255:STA WORDV+0
690 LDA #midiWORD DIV 256:STA WORDV+1
700 .initExit
710 RTS
720 .initOFF
730 JSR cmpWORDV:BNE initOffExit :\ Not redirected
740 LDA oldWORDV+0:STA WORDV+0
750 LDA oldWORDV+1:STA WORDV+1
760 .initOffExit
770 RTS
780 :
790 .midiReset
800 LDA #3:JSR midiReset2
810 LDA #&15:]:mClock=P%-1:[OPT P*3+4
820 .midiReset2
830 STA &FCF0:]:mCommand=P%-2:[OPT P*3+4:RTS
840 .setPorts
850 STX mCommand:STX mStatus:INX:STX mData:RTS
860 .cmpWORDV
870 LDA WORDV+0:CMP #midiWORD AND 255:BNE cmpDone
880 LDA WORDV+1:CMP #midiWORD DIV 256:.cmpDone
890 .midiCORE :\ Resident code starts here
900 .noNewNote
910 RTS
920 :
930 \ OSWORD intercept
940 \ ----------------
950 .midiWORD
960 CMP #7:BEQ midiSOUND
970 .oldWORD
980 JMP (oldWORDV)
990 .oldSOUND
1000 LDX ctrl+0:LDY ctrl+1
1010 LDA #7:BNE oldWORD
1020 :
1030 .midiSOUND
1040 STX ctrl+0:STY ctrl+1:LDY #0
1050 LDA (ctrl),Y:AND #15:BEQ oldSOUND :\ Channel 0, pass to MOS
1060 TAX:INY:LDA (ctrl),Y:INY :\ X=1..15, up to 15 notes
1070 CMP #&FE:BEQ midiControl :\ SOUND -257 -> MIDI Control
1080 CMP #&20:BCS oldSOUND :\ Channel >= &2000, pass to MOS
1090 CMP #&10:BCS noNewNote :\ Ignore &Hxxx notes
1100 LDA lastnote,X:BMI noOldNote:PHA :\ Nothing being played
1110 LDA #&80:STA lastnote,X:JSR midiCmd :\ Send NoteOff to current channel
1120 PLA:JSR midiOut:JSR midiOutZero :\ Send note and velocity
1130 .noOldNote
1140 :
1150 \ At this point Carry=CC
1160 LDA (ctrl),Y:BEQ noNewNote :\ Volume=0
1170 SBC #0:EOR #255:BPL P%+4:LDA #15 :\ Convert 0..-15 to 0..15
1180 ASL A:ASL A:ASL A:PHA :\ Convert to velocity and save
1190 LDA #&90:JSR midiCmd :\ Send NoteOn to current channel
1200 LDY #4:LDA (ctrl),Y :\ Pitch
1210 LSR A:LSR A:CLC:ADC #35:STA lastnote,X :\ Note number
1220 JSR midiOut:PLA:BPL midiOut :\ Send note and velocity
1230 \ JSR midiOut
1240 \ BIT &262\BMI oldSOUND\RTS
1250 :
1260 \ SOUND -258,Command >&7F,Note,Velocity - send raw output
1270 \ SOUND -257,Command >&7F,Note,Velocity - send to current channel
1280 \ SOUND -257,Data <&80,0,0 - send raw data byte
1290 \ SOUND -257,<0,param1,param2 - midi control
1300 \ -----------------------------------------------------
1310 \ X=Channel low nybble, Y=>Command low byte
1320 .midiControl
1330 CPX #&0F :\ CS=SOUND -257, CC=SOUND -258
1340 LDA (ctrl),Y:TAX :\ Get Command byte
1350 INY:LDA (ctrl),Y:BMI midiSystem :\ SOUND -257/8,<0 - System control
1360 TXA:BPL midiOut :\ SOUND -257/8,<&80 - send raw byte
1370 JSR midiCmdRaw :\ SOUND -257/8,>&7F,note,vol
1380 CMP #&F0:BCS midiCtrlDone:PHA :\ &Fx, one byte only
1390 INY:LDA (ctrl),Y:CMP #&80:PHP :\ Copy b7 of note number to Carry
1400 AND #127:JSR midiOut :\ Send note number
1410 PLP:INY:LDA (ctrl),Y:ASL A :\ Copy previous b7 into b0
1420 INY:ORA (ctrl),Y:AND #127:TAX :\ Merge with velocity
1430 PLA:CMP #&E0:BCS midiCtrl3 :\ &Ex, send third byte
1440 CMP #&C0:BCS midiCtrlDone :\ &Cx/&Dx, have sent two bytes
1450 .midiCtrl3
1460 TXA:BPL midiOut :\ Send velocity
1470 :
1480 \ SOUND -257/8,<0,0,0 - MIDI system control
1490 \ -----------------------------------------
1500 \ A=&80-&FF, X=Command low byte &00-&FF, Y=>Command high byte
1510 .midiSystem
1520 INY:LDA (ctrl),Y:BNE midiSetChannel :\ SOUND -257/8,-1,n,x
1530 \
1540 \ SOUND -257,-1,0,0 - All Notes Off
1550 \ ---------------------------------
1560 DEX :\ Loop SOUND x,1-count,x,x times
1570 .midiAllLp1
1580 TAY :\ Start at note 0
1590 .midiAllOff
1600 LDA #&80:JSR midiCmd:TYA:JSR midiOut :\ Send Note Off for all notes
1610 JSR midiOutZero:INY:BPL midiAllOff
1620 INX:BNE midiAllLp1 :\ Loop twice for SOUND x,-1,x,x
1630 TYA:LDX #15 :\ A=&80 for Note Off
1640 .midiAllLp2
1650 STA lastnote,X:DEX:BNE midiAllLp2 :\ Clear last note buffer
1660 .midiCtrlDone
1670 RTS
1680 :
1690 \ SOUND -257,-1,chan,0 - set MIDI channel
1700 \ ---------------------------------------
1710 .midiSetChannel
1720 BMI midiCtrlEnable :\ SOUND -257/8,nn,<0,0
1730 CLC:SBC #0:AND #15:STA mChannel :\ SOUND -257/8,-1,chn,0 - set chan
1740 INX :\ X=0, enable MOS sound
1750 \
1760 \ SOUND -257,-255,-1,0 - disable MOS sound
1770 \ SOUND -257,-256,-1,0 - enable MOS sound
1780 \ ---------------------------------------------------
1790 .midiCtrlEnable
1800 STX &0262:RTS :\ Set MOS sound and return
1810 :
1820 .midiOutZero
1830 LDA #0
1840 .midiCmdRaw
1850 BCC midiOut :\ CS=SOUND -257, CC=SOUND -258
1860 .midiCmd
1870 PHA:AND #&F0:STA regA:PLA :\ Save command nybble
1880 CLC:ADC #0:]:mChannel=P%-1:[OPT P*3+4:\ Add channel to command byte
1890 AND #&0F:ORA regA :\ Mask channel and add in command
1900 .midiOut:PHA :\ midiCmd always returns CC
1910 .midiOutLp
1920 BIT &FF:BMI midiOutEsc :\ Abort if Escape pending
1930 JSR midiTxRDY:BEQ midiOutLp :\ Loop until TxData empty
1940 .midiOutEsc:PLA
1950 .midiOutWR
1960 STA &FCF1:]:mData=P%-2:[OPT P*3+4
1970 RTS
1980 .midiTxRDY
1990 LDA &FCF0:]:mStatus=P%-2:[OPT P*3+4:AND #2
2000 .lastnote
2010 RTS
2020 EQUS STRING$(15,CHR$&80) :\ Last note sent
2030 \ If run out of space, use &0870-&087F for note buffer
2040 :
2050 .oldWORDV
2060 ]:NEXT:PRINT"0";~2+P%AND&FFFF
2070 IFmidiCORE<&09C0:PRINT"WARNING: Overlaps ENVELOPEs"
2080 IFP%>&FFFF0AFE:PRINT"WARNING: Overlaps soft keys"
2090 :
2100 A$="*SAVE MIDI "+STR$~mcode%+" "+STR$~O%+" "+STR$~(go%OR&FFFF0000)+" "+STR$~load%
2110 PRINTA$;:OSCLIA$:PRINT