Jump to content
  • entries
  • comments
  • views

Finally, I'm Playing MIDI Notes using MAC/65 Assembler Code



Do you remember me mentioning that the first attempt to write a machine language program to read MIDI data delivered to the Atari joystick port was a complete failure? The Arduino hardware has remained the same. The joystick trigger and cassette motor control(CMC) pin on the SIO port are still being used to control data flow. But this time I redefined the project specs to simplify the ML program and tested the ML data transfer routine as a USR call.

The USR routine was written to replace the BASIC code that checked the joystick trigger to see if data is ready to be read, gets the data, and tells the Arduino to get the next byte by setting the CMC. (the subroutine starting at line 100 in many of the earlier programs) The data byte is stored in a page Zero location to be PEEKed after control is returned to BASIC.

Once working as a USR call the PLA was removed and the M65 file was edited and saved for later inclusion in the Project file as a subroutine. #INCLUDEd READMIDI.M65 routine is as follows:


01 ;USR FUNCTION TO GET MIDI DATA FROM02 ;ARDUINO BOARD WITH DATA FLOW CNTRL03 ;DATA RETURNED IN LOCATION $CB04 ;05 ;Kevin Packard 4/201906 ;0100 ;STRIG0 = $0284 -ARDUINO DSR0110 ;PORTA = $D300  -JOYSTICK INPUT0120 ;PACTL = $D302  -SIGNIAL ARDUINO0130 ;0140 READMIDI0150 MU1 LDA $0284   ;STRIG00160     CMP #00170     BEQ MU1     ;WAIT FOR DATA0180     LDA $D300   ;PORTA-READ DATA0190     STA $CB     ;STORE BYTE PEEK(203)0200     LDA #52     ;DATA READ0210     STA $D302   ;PACTL-CMC ON0220 MU2 LDA $0284   ;STRIG00230     CMP #10240     BEQ MU2     ;LOOP TILL RESET0250     LDA #60     ;TURNS OFF BYTE0260     STA $D302   ;PACTL - CMC OFF0270     RTS         ;RETURN 

Playing a MIDI note on the monophonic-Atari in its simplest form consists of receiving the MIDI note number for the tone to play or to turn the tone off . If you assume the volume value to be the same for every note then the volume data doesn't have to be sent. Since a MIDI note can have a value of 0 - 127, passing a value of 128 can be used to signal note off.

The Arduino had to be reprogramed to minimize the data being sent to the Atari. The Arduino will first look for MIDI data for channel one. If its a NOTEON or NOTEOFF command it reads the following MIDI note number and then the volume byte. It then makes the decision to send a note number to start or change the pitch OR turn off the note being played. The logic makes sense considering a NOTEOFF command can be transmitted for a note that isn't being played or a NOTEON can set the volume to ZERO.

/*Mono synth - channel 1 * Sends: *        MIDI Note number to play *                 or *        Bit 7 ON(128) to turn off note *        *  Kevin Packard May 2019     */// Atari PORTA(54016) mapped to pins on arduinoint porta0 = 4;int porta1 = 5;int porta2 = 6;int porta3 = 7;int porta4 = 8;int porta5 = 9;int porta6 = 10;int porta7 = 11;int DSR = 3; //data set ready triggerint DTR = 13;//Atari ready cmc byte zero = 0;byte midiData = 0;byte midiCommand = 0;byte midiNoteNum = 0;byte lastMidiNoteNum = 0;byte volume = 0;byte volOff = 128;  //Function to Send a byte to the Atarivoid sendByte(byte byteToSend){   setPorta(byteToSend);  digitalWrite(DSR,LOW); //data ready-Trigger 0  while(digitalRead(DTR) == LOW){} //Wait for Atari to get byte cmc goes low  digitalWrite(DSR,HIGH); //data not ready-trigger 1  while(digitalRead(DTR) == HIGH){} //wait for Atari to signal ok to get next byte} void setPorta(byte byteToMap){  // Sets digital pins to transfer data to Atari joystick ports(PORTA)  // When digital port high, joystick pin shorted to ground or logic 0  if (byteToMap & B00000001){digitalWrite(porta0,LOW);}    else {digitalWrite(porta0,HIGH);}  if (byteToMap & B00000010){digitalWrite(porta1,LOW);}    else {digitalWrite(porta1,HIGH);}  if (byteToMap & B00000100){digitalWrite(porta2,LOW);}    else {digitalWrite(porta2,HIGH);}  if (byteToMap & B00001000){digitalWrite(porta3,LOW);}    else {digitalWrite(porta3,HIGH);}  if (byteToMap & B00010000){digitalWrite(porta4,LOW);}    else {digitalWrite(porta4,HIGH);}  if (byteToMap & B00100000){digitalWrite(porta5,LOW);}    else {digitalWrite(porta5,HIGH);}  if (byteToMap & B01000000){digitalWrite(porta6,LOW);}    else {digitalWrite(porta6,HIGH);}  if (byteToMap & B10000000){digitalWrite(porta7,LOW);}    else {digitalWrite(porta7,HIGH);}} void setup() {  pinMode(porta0,OUTPUT);  pinMode(porta1,OUTPUT);  pinMode(porta2,OUTPUT);  pinMode(porta3,OUTPUT);  pinMode(porta4,OUTPUT);  pinMode(porta5,OUTPUT);  pinMode(porta6,OUTPUT);  pinMode(porta7,OUTPUT);  pinMode(DSR,OUTPUT);  pinMode(DTR,INPUT);  Serial.begin(31250);  setPorta(zero);//  digitalWrite(DSR,HIGH);} void loop() {   //read data until its a Midi command  //command bytes will have bit 7 true   while (midiCommand != 128 && midiCommand != 144){   //midi command + Channel    while(Serial.available()<1){}//wait for data    midiCommand = Serial.read(); //read MIDI stream  }   // get data required by command.      while(Serial.available()<1){}    midiNoteNum =  Serial.read();    while(Serial.available()<1){}    volume = Serial.read();     if(midiNoteNum == lastMidiNoteNum && ((midiCommand == 128) ||  (midiCommand == 144 && volume == 0))){     sendByte(volOff);     lastMidiNoteNum = 0;    }     if(midiCommand == 144 && volume > 0 && midiNoteNum != lastMidiNoteNum){      sendByte(midiNoteNum);      lastMidiNoteNum = midiNoteNum;     }       midiCommand = 0;}     // End of Listing

The Atari now simply has to wait for the Arduino to set the trigger as new data is ready to be received. It then will turn off the sound, turn on the note, or change the pitch. The response time seems to be much faster then the compiled BASIC program. The Arduino"s data buffer didn't get overwritten when I ran my fingers up and down the keyboard or mashed down a bunch of keys over and over. I did note a bit of a delay as the buffer emptied.


0100 ;MIDI SYNTH MONO VOICE0110 ;ARDUINO 8BIT INPUT/CMC CONTROL0120 ;   Monosyn_1-Byte_Data.ino0130 ;K-PACK 20190140 ;0150 STRIG0 = $02840160 AUDCTL = $D208  ;SET TO 120 - 16BIT FREQUENCEY DEFINITIONS0170 AUDF1 = $D200   ;LOWBIT- VOICE 10180 AUDF2 = $D202   ;HIBIT - VOICE 10190 AUDC2 = $D203   ;VEL-DIS-VOICE 10200 AUDF3 = $D204   ;LOWBIT- VOICE 20210 AUDF4 = $D206   ;HIBIT - VOICE 20220 AUDC4 = $D207   ;VEL-DIS-VOICE20230 SKCTL = $D20F0240 PORTA = $D300   ;JOYSTICKS VALUE0250 PACTL = $D302   ;CMC 60-OFF 52-ON0260 CMCON = 520270 CMCOFF = 600280 MIDIBYTE = $CB0290 ;0300     *=  $40000310     .INCLUDE #D:FREQTABL.M650320     .INCLUDE #D:READMIDI.M650330 ;0340 ;0350 ;0360 START0370     LDA #0      ;RESET AUDIO0380     STA AUDCTL0390     LDA #30400     STA SKCTL0410     LDA #1200420     STA AUDCTL  ;SET 16 BIT SOUND0430     LDA #CMCOFF0440     STA PACTL   ;SET CASS MOTOR0442     LDA #00444     STA $022F   ;kill SCREEN0450 ;MAIN LOOP0460 JP1 JSR READMIDI0470     LDA MIDIBYTE0490     CMP #128    ;NOTE OFF?0500     BNE JP20502     LDX #00505     STX AUDC20520     JMP JP1     ;GET NEXT DATA0530 JP2 LDX MIDIBYTE ;OFFSET FREQ TABLE0540     LDA FREQLO,X ;SET FREQ0550     STA AUDF10560     LDA FREQHI,X0570     STA AUDF20590     LDX #173    ;DIS=10 VOL=130600     STX AUDC2   ;TURN SET VOL0610     JMP JP1     ;NEXT COMMAND BYTE0620 ;0630     *=  $02E20640     .WORD START0650     .END

I'm hoping this program will be a good start for some interesting routines to modulate the sounds.

A sound recording didn't seem to be necessary and the Arduino interface has been explained in previous blog entries. The atr file contains the MAC/65 files and BASIC files used to create and test the USR input routine.



Recommended Comments

There are no comments to display.

Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Create New...