 (***************************************************************************
 * Name: MIDI.MOD                                                           *
 * Created: 01/06/89   Updated: 00/00/89   Author: Daniel Brookshier        *
 * Description: MIDI Read/Write utilities.                                  *
 ****************************************************************************)
IMPLEMENTATION MODULE MIDI;

FROM CPrintTerminal IMPORT printf;
FROM FormatString  IMPORT Format,FormatArg;
FROM SYSTEM IMPORT BYTE,ADR,ADDRESS;
FROM IODevices IMPORT CmdNonStd, IOStdReq, OpenDevice,DoIO,WaitIO,BeginIO,
                      SendIO,CmdRead,CmdWrite,CloseDevice;
FROM IODevicesUtil IMPORT CreateExtIO,DeleteExtIO;
FROM PortsUtil IMPORT CreatePort, DeletePort;
FROM Ports IMPORT MsgPortPtr;
FROM Lists IMPORT Remove;
FROM InOut IMPORT ReadInt;
FROM SerialDevice IMPORT
 (*CONST*)
 SerialName (*= "serial.device"*),

  SDCmdQuery    (* = CmdNonStd + 0;*),
  SDCmdBreak    (* = CmdNonStd + 1;*),
  SDCmdSetParams(* = CmdNonStd + 2;*),

(*TYPE*)
  (* array of termination char's *)
  IOTArrayPtr (*= POINTER TO IOTArray;*),
  IOTArray (*= RECORD
               TermArray0 : LONGCARD;
               TermArray1 : LONGCARD;
             END;*),

(*CONST*)
  (* IOStdRequest.ioFlags := IOFlagsSet{ } *)
  IOSerBufRead   (* = 7;  from read buffer bit *),
  IOSerQueued    (* = 6;  rqst-queued bit *),
  IOSerAbort     (* = 5; rqst-aborted bit *),
  IOSerActive    (* = 4;  rqst-qued-or-current bit *),

(*TYPE*)
  SerFlags(* = (SerParityOn, (* parity-enabled bit *)
              SerParityOdd,  (* parity feature enabled bit *)
              Ser7Wire,      (* RS232 7-wire protocol *)
              SerQueuedBrk,  (* queue this Break ioRqst *)
              SerRadBoogie,  (* high-speed mode active bit used for MIDI*)
              SerShared,     (* non-exclusive access bit *)
              SerEOFMode,    (* EOF mode enabled bit *)
              SerXDisabled); (* xOn-xOff feature disabled bit *)*),

  SerFlagsSet (*= SET OF SerFlags;*),

  SerExtFlags (*= (SExtMark, (*  if mark=space, use mark bit *)
                 SExtMSPOn, (* mark-space parity bit *)
                 SEF2, SEF3, SEF4, SEF5, SEF6, SEF7,
                 SEF8, SEF9, SEF10, SEF11, SEF12, SEF13,
                 SEF14, SEF15, SEF16, SEF17, SEF18, SEF19,
                 SEF20, SEF21, SEF22, SEF23, SEF24, SEF25,
                 SEF26, SEF27, SEF28, SEF29, SEF30, SEF31);*),
  SerExtFlagsSet (*= SET OF SerExtFlags;*),

  SerStatus (*= (IOSTOverRun,    (* status work RBF overrun bit *)
               IOSTWroteBreak, (* break was latest output bit *)
               IOSTReadBreak,  (* break was latest input bit *)
               IOSTXOffWrite,  (* transmit currently xOFF'ed bit *)
               IOSTXOffRead,   (* receive currently xOFF'ed bit *)
               IOST5, IOST6, IOST7, IOST8, IOST9, IOST10,
               IOST11, IOST12, IOST13, IOST14, IOST15);*),
  SerStatusSet (*= SET OF SerStatus;*),

(***********************************************************************)
(* CAUTION !!  IF YOU ACCESS the serial.device, you MUST (!!!!) use an *)
(*  IOExtSer-sized structure or you may overlay innocent memory !!     *)
(***********************************************************************)

  IOExtSerPtr (*= POINTER TO IOExtSer;*),
  IOExtSer (*= RECORD
               IOSer       : IOStdReq;
               ioCtlChar   : LONGCARD;   (* control char(order=xON,xOFF,INQ,ACK)*)
               ioRBufLen   : LONGCARD;   (* length in bytes of serial read buf *)
               ioExtFlags  : SerExtFlagsSet; (* additional flags (see above) *)
               ioBaud      : LONGCARD;  (* baud rate requesterd (true baud) *)
               ioBrkTime   : LONGCARD;  (* duration of break signal in microsec*)
               ioTermArray : IOTArray; (* termination character array *)
               ioReadLen   : BYTE;     (* bits per read char (# of bits) *)
               ioWriteLen  : BYTE;     (* bits per write char (# of bits) *)
               ioStopBits  : BYTE;     (* stopbits for read (# of bits) *)
               ioSerFlags  : SerFlagsSet; (* SerFlags bit defs above *)
               ioStatus    : SerStatusSet;
             END;*),

(* status of serial port, as follows:
 *                  BIT  ACTIVE  FUNCTION
 *                   0    low    busy
 *                   1    low    paper out
 *                   2    low    select
 *                   3    low    Data Set Ready
 *                   4    low    Clear To Send
 *                   5    low    Carrier Detect
 *                   6    low    Ready To Send
 *                   7    low    Data Terminal Ready
 *                   8    high   read overrun
 *                   9    high   break sent
 *                  10    high   break received
 *                  11    high   transmit x-OFFed       
 *                  12    high   receive x-OFFed       
 *               13-15           reserved
 *)

(*CONST*)
  SerErrDevBusy       (*=  1;*),
  SerErrBaudMismatch  (*=  2;*),
  SerErrInvBaud       (*=  3;*),
  SerErrBufErr        (*=  4;*),
  SerErrInvParam      (*=  5;*),
  SerErrLineErr       (*=  6;*),
  SerErrNotOpen       (*=  7;*),
  SerErrPortReset     (*=  8;*),
  SerErrParityErr     (*=  9;*),
  SerErrInitErr       (*= 10;*),
  SerErrTimerErr      (*= 11;*),
  SerErrBufOverflow   (*= 12;*),
  SerErrNoDSR         (*= 13;*),
  SerErrNoCTS         (*= 14;*),
  SerErrDetectedBreak (*= 15*);
(*================================================================*)
VAR
  Farg: ARRAY [0..3] OF FormatArg;
(*
  Port :  MsgPortPtr; 
  IORser :  IOExtSerPtr;
  X,Y,Z : INTEGER;
  buffer : ARRAY [0..4096] OF BYTE;
*)
(*================================================================*)
(* AbortMIDI:                                                     *)
(* This procedure closes the serial device and deletes the port   *)
(* and external I/O port.                                         *)
(*    IORser = external I/O port created by StartMIDI.            *)
(*    Port   = serial message port created by StartMIDI.          *)
(*================================================================*)
PROCEDURE AbortMIDI(VAR IORser :  IOExtSerPtr;VAR Port :  MsgPortPtr);
BEGIN
  CloseDevice(IORser);
  DeleteExtIO(IORser);
  DeletePort(Port^);
END AbortMIDI;
(*================================================================*)
(* ReadSerial:                                                    *)
(* This procedure does a DoIO read from the serial device.        *)
(*    io = external I/O port created by StartMIDI.                *)
(*    data = address of the area of memory to write to.           *)
(*    length = number of 8 bit bytes to read.                     *) 
(*================================================================*)
PROCEDURE ReadSerial(VAR io:IOExtSerPtr;data:ADDRESS;length:LONGCARD):INTEGER;
VAR
  err : INTEGER;
BEGIN
  (*-----------------------------------------------*)
  (* Setup External I/O with parameters for write. *)
  (*-----------------------------------------------*)
  WITH io^.IOSer DO
    ioData := data;
    ioLength := length;
    ioCommand := CmdRead;
  END;
  (*----------------------------------------------------*)
  (* Do DoIO. Note that you are stuck in this procedure *)
  (* until the entire buffer is filled.                 *)
  (*----------------------------------------------------*)
  err := DoIO(io);
  IF (err <> 0) THEN
    Farg[0].W := err;
    printf("Error in WriteSerial: %d\n",Farg);
  END;
END ReadSerial;
(*================================================================*)
(* ReadSerialByte :                                               *)
(* This procedure is a slightly quicker version of ReadSerial.    *)
(* It only attempts to read one byte because we must process it   *)
(* as soon as possible.  NOTE no error control!                   *)
(*    io = external I/O port created by Start MIDI.               *)
(*    ByteStorage = byte to store the serial data to.             *)
(*================================================================*)
PROCEDURE ReadSerialByte(VAR io:IOExtSerPtr;VAR ByteStorage:BYTE);
VAR
  err : INTEGER;
BEGIN
  (*----------------------------------------------*)
  (* Setup External I/O with parameters for a one *)
  (* char read.                                   *)
  (*----------------------------------------------*)
  WITH io^.IOSer DO
    ioData := ADR(ByteStorage);
    ioLength := 1;
    ioCommand := CmdRead;
  END;
  (*----------------------------------------------------*)
  (* Do DoIO. Note that you are stuck in this procedure *)
  (* until one byte is received.                        *)
  (*----------------------------------------------------*)
  err := DoIO(io);
END ReadSerialByte;
(*================================================================*)
(* WriteSerial:                                                   *)
(* This procedure does a waitIO write to the serial device.       *)
(*    io = external I/O port created by StartMIDI                 *)
(*    data = address of the area of memory to send.               *)
(*    length = number of 8 bit bytes to send, starting from data. *) 
(*================================================================*)
PROCEDURE WriteSerial(VAR io:IOExtSerPtr;data:ADDRESS;length:LONGCARD):INTEGER;
VAR
  err : INTEGER;
BEGIN 
  (*-----------------------------------------------*)
  (* Setup External I/O with parameters for write. *)
  (*-----------------------------------------------*)
 WITH io^.IOSer DO
    ioData := data;
    ioLength := length;
    ioCommand := CmdWrite;
  END;
  (*----------------------------------------------*)
  (* Do a WaitIO.  This will send all the buffer  *)
  (* to the MIDI devices.                         *)
  (*----------------------------------------------*) 
  err := DoIO(io);
  IF (err <> 0) THEN
    Farg[0].W := err;
    printf("Error in WriteSerial: %d\n",Farg);
  END;
END WriteSerial;
(*================================================================*)
(* WriteSerialByte :                                              *)
(* This procedure is a slightly quicker version of ReadSerial.    *)
(* It only attempts to write one byte because we must get rid of  *)
(* it as soon as possible to continue processing.                 *)
(* NOTE no error control!                                         *)
(*    io = external I/O port created by Start MIDI.               *)
(*    ByteStorage = byte to send to MIDI devices                  *)
(*================================================================*)
PROCEDURE WriteSerialByte(VAR io:IOExtSerPtr;ByteStorage:BYTE);
VAR
  err : INTEGER;
BEGIN 
  (*-----------------------------------------------*)
  (* Setup External I/O with parameters for write. *)
  (*-----------------------------------------------*)
 WITH io^.IOSer DO
    ioData := ADR(ByteStorage);
    ioLength := 1;
    ioCommand := CmdWrite;
  END;
  (*------------------------------------------*)
  (* Do a DoIO.  This will send the one byte  *)
  (* to the MIDI devices.                     *)
  (*------------------------------------------*) 
  err := DoIO(io);
END WriteSerialByte;
(*================================================================*)
(* SendWaitWriteSerial:                                           *)
(* This procedure does a waitIO write to the serial device.       *)
(*    io = external I/O port created by Start MIDI.               *)
(*    data = address of the area of memory to send.               *)
(*    length = number of 8 bit bytes to send, starting from data. *) 
(*================================================================*)
PROCEDURE SendWaitWriteSerial(VAR io:IOExtSerPtr;data:ADDRESS;length:LONGCARD)
          :INTEGER;
VAR
  err : INTEGER;
BEGIN
  (*-----------------------------------------------*)
  (* Setup External I/O with parameters for write. *)
  (*-----------------------------------------------*)
  WITH io^.IOSer DO
    ioData := data;
    ioLength := length;
    ioCommand := CmdWrite;
  END;
  (*-----------------------------------------------*)
  (* Do a WaitIO.                                  *)
  (*-----------------------------------------------*)
  err := WaitIO(io);
  IF (err <> 0) THEN
    Farg[0].W := err;
    printf("Error in WriteSerial: %d\n",Farg);
  END;
END SendWaitWriteSerial;
(*================================================================*)
(* StartMIDI:                                                     *)
(* This procedure sets up the serial device to allow reads and    *)
(* writes at 31250 bytes per second, eight bits per word with one *)
(* stop bit and no parity. Note that SerRadBoogie flag is set to  *)
(* allow high transfer speeds need by MIDI devices.               *)
(*    IORser = external I/O port to be created.                   *)
(*    Port   = serial message port to be created.                 *)
(*================================================================*)
PROCEDURE StartMIDI(VAR IORser :  IOExtSerPtr;VAR Port :  MsgPortPtr):INTEGER;
BEGIN
  (*-------------------------------------------*)
  (* Create a serial device port.              *)
  (*-------------------------------------------*)
  Port := CreatePort(ADR(SerialName),0);
  IF (Port = NIL) THEN  
    printf('Port Create Failed\n',Farg);
    AbortMIDI(IORser,Port);
    RETURN(-1);
  END;
  (*---------------------------------------------*)
  (* Create an external I/O port. This allows    *)
  (* us to send parameters to the serial device. *)
  (*---------------------------------------------*)
  IORser := CreateExtIO(Port^,SIZE(IOExtSer));
  IF (IORser = NIL) THEN  
    printf('ExtIO Create Failed\n',Farg);
    AbortMIDI(IORser,Port);
    RETURN(-1);
  END;
 (*----
   (* Note use of SerRadBoogie flag! *)
    IORser^.ioSerFlags  := SerFlagsSet{SerRadBoogie,SerXDisabled};
---------------------------------------*)
  (*----------------------------------------------------*)
  (* Open the serial device with the external I/O port. *) 
  (*----------------------------------------------------*)
  IF (OpenDevice(ADR(SerialName),0,IORser,0D) <>0D) THEN  
    printf('Open of serial device Failed\n',Farg);
    AbortMIDI(IORser,Port);
    RETURN(-1);
  END;
  (*------------------------------------------------------------*)
  (* The following fragment sets up all the parameters need by  *)
  (* the serial device to talk to MIDI devices.                 *)
  (*------------------------------------------------------------*)
  WITH IORser^ DO
    IOSer.ioCommand := SDCmdSetParams;(* IOStdReq*)
  (*ioCtlChar   := ;LONGCARD;control char(order=xON,xOFF,INQ,ACK)*)
    ioRBufLen   := 512;(*LONGCARD;length in bytes of serial read buf *)
  (*ioExtFlags  := ;SerExtFlagsSet; additional flags (see above) *)
    ioBaud      := 31250;(*LONGCARD;baud rate requested (true baud) *)
    ioBrkTime   := 750000D;(*LONGCARD;duration of break signal in microsec*)
    ioTermArray.TermArray0 := 1359217411D;(*IOTArray;termination character array *)
    ioTermArray.TermArray1 := 50529027D;(*IOTArray;termination character array *)
    ioReadLen   := BYTE(8);(*BYTE;bits per read char (# of bits) *)
    ioWriteLen  := BYTE(8);(*BYTE;bits per write char (# of bits) *)
    ioStopBits  := BYTE(1);(*BYTE;stopbits for read (# of bits) *)
    ioSerFlags  := SerFlagsSet{SerRadBoogie,SerXDisabled};(*SerFlagsSet;SerFlags bit defs above *)
  (*ioStatus    := ;SerStatusSet;*)
  END;
  IF ( DoIO(IORser) <> 0D) THEN  
    printf('DoIO  to set up serial parameters Failed!\n',Farg);
    AbortMIDI(IORser,Port);
    RETURN(-1);
  END;
(*----------  
  FOR X := 1 TO 50 DO
    printf(' .\n',Farg);
    ReadInt(Z);
    buffer[0]:= CHAR(Z);
    WriteSerial(IORser,ADR(buffer),1D);
    IF Z =248 THEN
      FOR Y := 1 TO 30 DO
       WriteSerial(IORser,ADR(buffer),1D);
    END;
  END;
-----------*) 
  RETURN(0);
  (*------------------------------------------------------------*)
  (* The Serial port is now configured. Use AbortMIDI to kill.  *)
  (*------------------------------------------------------------*)
END StartMIDI; 
END MIDI.


