'CZPLUS for the Amiga by Jim McConkey
' loosely adapted from the C64 version by Tim Dowty
'  published in Electronic Musician Feb. & Aug. 1987

CLS : LOCATE 1,1

DEFLNG a-Z
RANDOMIZE TIMER
DEF FNrnd16=INT(RND*16)
Casio%=&H44 : MMF.SYSEX=&H400

'Assumes a subdirectory defined by Path$ off the current directory
' for the storage of patch files
Path$="CZfiles"

'Assumes exec.bmap and midi.bmap in the current directory
LIBRARY "exec.library"
LIBRARY "midi.library"

Declarations:
 DECLARE FUNCTION AllocMem() LIBRARY
 memf.public = 1
 memf.clear = 65536&

 DECLARE FUNCTION CreateMDest() LIBRARY
 DECLARE FUNCTION CreateMSource() LIBRARY
 DECLARE FUNCTION GetMidiMsg() LIBRARY
 DECLARE FUNCTION MidiMsgLength() LIBRARY
 DECLARE FUNCTION MRouteDest() LIBRARY
 DECLARE FUNCTION MRouteSource() LIBRARY
 DestName$="MidiOut"+CHR$(0)
 SourceName$="MidiIn"+CHR$(0)

'sysex voice request and header:
 RequestSize=10 : HeaderSize=8
 DIM SHARED Rqst(RequestSize-1),Header(HeaderSize-1)
 DATA &hF0,&h44,0,0,&h70,&h10,0,&h70,&h31,&hF7
 DATA &hF0,&h44,0,0,&h70,&h20,0,&hF7

AllocateBuffers:
 VoiceSize=264 : MainSize=16*VoiceSize
 RandomSize=VoiceSize
 main=AllocMem(MainSize,memf.public+memf.clear)
 IF main=0 THEN CloseDown
 Random=AllocMem(RandomSize,memf.public+memf.clear)
 IF Random=0 THEN CloseDown
 Request=AllocMem(RequestSize,memf.public+memf.clear)
 IF Request=0 THEN CloseDown

SetupMIDIRoute:
 RouteInfoSize=14
 RouteInfo=AllocMem(RouteInfoSize,memf.public+memf.clear)
 IF RouteInfo=0 THEN CloseDown
' POKEW RouteInfo  ,MMF.SYSEX  'Allow only Casio Sysex messages
' POKE  RouteInfo+8,1          'on Channel 1
' POKE  RouteInfo+9,Casio%
 POKEW RouteInfo  ,MMF.SYSEX   'Allow only Sysex messages
 POKE  RouteInfo+6,1	       'for just one manufacturer
 POKE  RouteInfo+7,Casio%      'which is in this case Casio

CZPLUS:

 PRINT"CZPLUS for the Amiga"
 PRINT" by Jim McConkey after C64 original by Tim Dowty"
 GOSUB Init
 Dest=CreateMDest(0&,0&)
 IF Dest=0 THEN PRINT"Can't create Dest": GOTO CloseDown
 Source=CreateMSource(0&,0&)
 IF Source=0 THEN PRINT"Can't create Source": GOTO CloseDown

 Out=MRouteSource(Source,SADD(DestName$),RouteInfo)
 IF Out=0 THEN PRINT"Can't route MIDI output": GOTO CloseDown
 In=MRouteDest(SADD(SourceName$),Dest,RouteInfo)
 IF In=0 THEN PRINT"Can't route MIDI input" : GOTO CloseDown

 MENU 1,0,1,"CZPLUS"
 MENU 1,1,1,"Quit        "

 MENU 2,0,1,"MIDI"
 MENU 2,1,1,"Random Patch     "
 MENU 2,2,1,"Get Presets      "
 MENU 2,3,1,"Get Internals    "
 MENU 2,4,1,"Get Cartridge    "
 MENU 2,5,1,"Send to Internals"

 MENU 3,0,1,"FILES"
 MENU 3,1,1,"Load File"
 MENU 3,2,1,"Save File"
 MENU 3,3,1,"Directory"

 MENU 4,0,0,""
 ON MENU GOSUB MenuHandler
 MENU ON

 Quit%=0
 WHILE NOT Quit%
  SLEEP
 WEND
 CLS : MENU RESET

CloseDown:
 IF Dest<>0 THEN CALL DeleteMDest(Dest)
 IF Source<>0 THEN CALL DeleteMSource(Source)
 IF In<>0 THEN CALL DeleteMRoute(In)
 IF Out<>0 THEN CALL DeleteMRoute(Out)
 IF RouteInfo<>0 THEN CALL FreeMem(RouteInfo,RouteInfoSize)
 IF main<>0 THEN CALL FreeMem(main,MainSize)
 IF Request<>0 THEN CALL FreeMem(Request,RequestSize)
 IF Random<>0 THEN CALL FreeMem(Random,RandomSize)
 LIBRARY CLOSE
END

MenuHandler:
 MenuID%=MENU(0) : MenuItem%=MENU(1)
 IF MenuID%=1 THEN
  ON MenuItem% GOSUB Quit
 ELSEIF MenuID%=2 THEN
  ON MenuItem% GOSUB RandomPatch,GetPre,GetInt,GetCart,Send
 ELSEIF MenuID%=3 THEN
  ON MenuItem% GOSUB LoadFile,SaveFile,Directory
 END IF
RETURN

RandomPatch:
 LOCATE 5,1 : PRINT"Generating Patch ...       "
 addr=Random : POKE addr+6,&H60
 FOR i=7 TO 262
  POKE addr+i,PEEK(main+i+VoiceSize*FNrnd16)
 NEXT
 LOCATE 5,1 : PRINT "Sending Patch ...         "
 CALL PutMidiMsg(Source,addr) : RcvdPtr=0
 FOR i=200 TO 0 STEP -1
  RcvdPtr=GetMidiMsg(Dest)
  IF RcvdPtr<>0 THEN i=-1
 NEXT
 IF RcvdPtr<>0 THEN CALL FreeMidiMsg(RcvdPtr)
 LOCATE 5,1 : PRINT SPACE$(30)
RETURN

Quit:
 Quit%=-1
RETURN

GetPre:
 LOCATE 6,1 : PRINT SPACE$(30)
 FOR voice=&H0 TO &HF
  CALL GetVoice(voice,oops)
  IF oops THEN voice=&HFF
 NEXT
 IF voice<>&H100 THEN LOCATE 5,1 : PRINT "Presets received!     "
RETURN

GetInt:
 LOCATE 6,1 : PRINT SPACE$(30)
 FOR voice=&H20 TO &H2F
  CALL GetVoice(voice,oops)
  IF oops THEN voice=&HFF
 NEXT
 IF voice<>&H100 THEN LOCATE 5,1 : PRINT "Internals received!     "
RETURN

GetCart:
 LOCATE 6,1 : PRINT SPACE$(30)
 FOR voice=&H40 TO &H4F
  CALL GetVoice(voice,oops)
  IF oops THEN voice=&HFF
 NEXT
 IF voice<>&H100 THEN LOCATE 5,1 : PRINT "Cartridge received!     "
RETURN

Send:
 addr=main
 FOR voice=32 TO 47
  LOCATE 5,1 : PRINT "Sending voice"voice+1"     "
  POKE addr+6,voice
  CALL PutMidiMsg(Source,addr)
  FOR i=200 TO 0 STEP -1
   RcvdPtr=GetMidiMsg(Dest)
   IF RcvdPtr<>0 THEN i=-1
  NEXT
  IF RcvdPtr<>0 THEN CALL FreeMidiMsg(RcvdPtr)
  addr=addr+VoiceSize
 NEXT voice
 LOCATE 5,1 : PRINT "Finished sending bank.  "
RETURN

LoadFile:
 ON ERROR GOTO LoadErrMsg
 LOCATE 5,1 : INPUT "File to get patches from: ",FileName$
 LOCATE 5,1 : PRINT "Reading patches from "FileName$" ..."SPACE$(15)
 OPEN Path$+"/"+FileName$ FOR INPUT AS 1
 FOR i=0 TO MainSize-1
  POKE main+i,ASC(INPUT$(1,1))
 NEXT
 LOCATE 5,1 : PRINT SPACE$(50)
EndLoad:
 CLOSE #1
RETURN

SaveFile:
 ON ERROR GOTO SaveErrMsg
 LOCATE 5,1 : INPUT "File to save patches to: ",FileName$
 LOCATE 5,1 : PRINT "Saving patches to "FileName$" ..."SPACE$(15)
 OPEN Path$+"/"+FileName$ FOR OUTPUT AS 1
 FOR i=main TO main+MainSize-1
  PRINT#1,CHR$(PEEK(i));
 NEXT
 LOCATE 5,1 : PRINT SPACE$(50)
EndSave:
 CLOSE #1
RETURN

LoadErrMsg:
 LOCATE 5,1
 IF ERR=53 THEN
  PRINT "File not found" SPACE$(30)
 ELSEIF ERR=70 THEN
  PRINT "Disk is write protected" SPACE$(20)
 ELSE
  PRINT "Load error" SPACE$(30)
 END IF
RESUME EndLoad

SaveErrMsg:
 LOCATE 5,1
 IF ERR=70 THEN
  PRINT "Disk is write protected"
 ELSE
  PRINT "Write error"
 END IF
RESUME EndSave

Directory:
 CLS : FILES Path$ : MENU OFF
 PRINT : PRINT"Touch any key to continue"
 WHILE INKEY$="" : WEND : MENU ON
 CLS : PRINT"CZPLUS for the Amiga"
 PRINT" by Jim McConkey after C64 original by Tim Dowty"
RETURN

Init:
'Set up voice request message
 FOR i=0 TO RequestSize-1
  READ Rqst(i) : POKE Request+i,Rqst(i)
 NEXT
'Put sysex headers in buffers
 FOR i=0 TO HeaderSize-1: READ Header(i) : NEXT
 FOR voice=0 TO 15
  addr=main+voice*VoiceSize
  FOR i=0 TO 7
   POKE addr+i,Header(i)
  NEXT i
  i=VoiceSize-1 : POKE addr+i,Header(7)
 NEXT voice
  addr=Random
  FOR i=0 TO 7
   POKE addr+i,Header(i)
  NEXT i
  i=VoiceSize-1 : POKE addr+i,Header(7)
RETURN

SUB GetVoice(voice,oops) STATIC
 SHARED main,Request,VoiceSize,Source,Dest
 LOCATE 5,1 : PRINT "Getting voice"voice+1"       "
 POKE Request+6,voice : CALL PutMidiMsg(Source,Request)
 FOR i=200 TO 0 STEP -1
  RcvdPtr=GetMidiMsg(Dest)
  IF RcvdPtr<>0 THEN i=-1
 NEXT
 IF RcvdPtr=0 THEN
  LOCATE 5,1 : PRINT "CZ is not responding!     " : oops=-1
 ELSE
  addr=main+VoiceSize*(voice AND 15)+7
  RcvdPtr=RcvdPtr+6
  FOR i=0 TO 255
   POKE addr+i,PEEK(RcvdPtr+i)
  NEXT
  FreeMidiMsg(RcvdPtr-6) : oops=0
 END IF
END SUB




