|
|
1.1 root 1: /* 1.1.1.2 root 2: Hatari - midi.c 1.1 root 3: 1.1.1.9 ! root 4: This file is distributed under the GNU General Public License, version 2 ! 5: or at your option any later version. Read the file gpl.txt for details. 1.1.1.2 root 6: 7: MIDI communication. 8: Note that this code is far from being perfect. However, it is already 9: enough to let some ST programs (e.g. the game Pirates!) use the host's midi 10: system. 11: 12: TODO: 13: - Most bits in the ACIA's status + control registers are currently ignored. 14: - Check when we have to clear the ACIA_SR_INTERRUPT_REQUEST bit in the 15: ACIA status register (it is currently done when reading or writing to 16: the data register, but probably it should rather be done when reading the 17: status register?). 1.1 root 18: */ 1.1.1.6 root 19: const char Midi_fileid[] = "Hatari midi.c : " __DATE__ " " __TIME__; 1.1.1.2 root 20: 21: #include <SDL_types.h> 1.1 root 22: 23: #include "main.h" 1.1.1.2 root 24: #include "configuration.h" 1.1.1.3 root 25: #include "ioMem.h" 1.1.1.4 root 26: #include "m68000.h" 1.1.1.2 root 27: #include "mfp.h" 28: #include "midi.h" 1.1.1.5 root 29: #include "file.h" 1.1.1.9 ! root 30: #include "acia.h" 1.1.1.2 root 31: 32: 33: #define ACIA_SR_INTERRUPT_REQUEST 0x80 1.1.1.6 root 34: #define ACIA_SR_TX_EMPTY 0x02 35: #define ACIA_SR_RX_FULL 0x01 1.1.1.2 root 36: 37: #define MIDI_DEBUG 0 38: #if MIDI_DEBUG 39: #define Dprintf(a) printf a 40: #else 41: #define Dprintf(a) 42: #endif 43: 44: 1.1.1.6 root 45: static FILE *pMidiFhIn = NULL; /* File handle used for Midi input */ 46: static FILE *pMidiFhOut = NULL; /* File handle used for Midi output */ 1.1.1.2 root 47: static Uint8 MidiControlRegister; 48: static Uint8 MidiStatusRegister; 1.1.1.6 root 49: static Uint8 nRxDataByte; 1.1.1.2 root 50: 51: 1.1.1.5 root 52: /** 53: * Initialization: Open MIDI device. 54: */ 1.1.1.2 root 55: void Midi_Init(void) 56: { 1.1.1.5 root 57: if (!ConfigureParams.Midi.bEnableMidi) 58: return; 1.1.1.2 root 59: 1.1.1.6 root 60: if (ConfigureParams.Midi.sMidiOutFileName[0]) 1.1.1.5 root 61: { 1.1.1.6 root 62: /* Open MIDI output file */ 63: pMidiFhOut = File_Open(ConfigureParams.Midi.sMidiOutFileName, "wb"); 64: if (!pMidiFhOut) 65: { 66: Log_AlertDlg(LOG_ERROR, "MIDI output file open failed. MIDI support disabled."); 1.1.1.7 root 67: ConfigureParams.Midi.bEnableMidi = false; 1.1.1.6 root 68: return; 69: } 70: setvbuf(pMidiFhOut, NULL, _IONBF, 0); /* No output buffering! */ 71: Dprintf(("Opened file '%s' for MIDI output.\n", 72: ConfigureParams.Midi.sMidiOutFileName)); 73: } 74: if (ConfigureParams.Midi.sMidiInFileName[0]) 75: { 76: /* Try to open MIDI input file */ 77: pMidiFhIn = File_Open(ConfigureParams.Midi.sMidiInFileName, "rb"); 78: if (!pMidiFhIn) 79: { 80: Log_AlertDlg(LOG_ERROR, "MIDI input file open failed. MIDI support disabled."); 1.1.1.7 root 81: ConfigureParams.Midi.bEnableMidi = false; 1.1.1.6 root 82: return; 83: } 84: setvbuf(pMidiFhIn, NULL, _IONBF, 0); /* No input buffering! */ 85: Dprintf(("Opened file '%s' for MIDI input.\n", 86: ConfigureParams.Midi.sMidiInFileName)); 1.1.1.2 root 87: } 88: } 89: 90: 1.1.1.5 root 91: /** 92: * Close MIDI device. 93: */ 1.1.1.2 root 94: void Midi_UnInit(void) 95: { 1.1.1.6 root 96: pMidiFhIn = File_Close(pMidiFhIn); 97: pMidiFhOut = File_Close(pMidiFhOut); 98: 1.1.1.8 root 99: CycInt_RemovePendingInterrupt(INTERRUPT_MIDI); 1.1.1.2 root 100: } 101: 102: 1.1.1.5 root 103: /** 1.1.1.6 root 104: * Reset MIDI emulation. 1.1.1.5 root 105: */ 1.1.1.6 root 106: void Midi_Reset(void) 1.1.1.2 root 107: { 1.1.1.6 root 108: MidiControlRegister = 0; 109: MidiStatusRegister = ACIA_SR_TX_EMPTY; 110: nRxDataByte = 1; 1.1.1.2 root 111: 1.1.1.6 root 112: if (ConfigureParams.Midi.bEnableMidi) 1.1.1.8 root 113: CycInt_AddRelativeInterrupt(2050, INT_CPU_CYCLE, INTERRUPT_MIDI); 1.1.1.9 ! root 114: else ! 115: CycInt_RemovePendingInterrupt (INTERRUPT_MIDI); 1.1.1.2 root 116: } 117: 118: 1.1.1.5 root 119: /** 1.1.1.6 root 120: * Read MIDI status register ($FFFC04). 1.1.1.5 root 121: */ 1.1.1.6 root 122: void Midi_Control_ReadByte(void) 1.1.1.2 root 123: { 1.1.1.6 root 124: Dprintf(("Midi_ReadControl : $%x.\n", MidiStatusRegister)); 1.1.1.2 root 125: 1.1.1.9 ! root 126: ACIA_AddWaitCycles (); /* Additional cycles when accessing the ACIA */ 1.1.1.4 root 127: 1.1.1.6 root 128: IoMem[0xfffc04] = MidiStatusRegister; 1.1.1.2 root 129: } 130: 1.1 root 131: 1.1.1.5 root 132: /** 133: * Write to MIDI control register ($FFFC04). 134: */ 1.1.1.3 root 135: void Midi_Control_WriteByte(void) 1.1.1.2 root 136: { 1.1.1.9 ! root 137: ACIA_AddWaitCycles (); /* Additional cycles when accessing the ACIA */ 1.1.1.4 root 138: 1.1.1.3 root 139: MidiControlRegister = IoMem[0xfffc04]; 1.1.1.2 root 140: 1.1.1.3 root 141: Dprintf(("Midi_WriteControl($%x)\n", MidiControlRegister)); 1.1.1.2 root 142: 143: /* Do we need to generate a transfer interrupt? */ 144: if ((MidiControlRegister & 0xA0) == 0xA0) 145: { 146: Dprintf(("WriteControl: Transfer interrupt!\n")); 147: 148: /* Acknowledge in MFP circuit, pass bit,enable,pending */ 1.1.1.9 ! root 149: MFP_InputOnChannel ( MFP_INT_ACIA , 0 ); 1.1.1.2 root 150: 151: MidiStatusRegister |= ACIA_SR_INTERRUPT_REQUEST; 152: } 153: } 154: 155: 1.1.1.6 root 156: /** 157: * Read MIDI data register ($FFFC06). 158: */ 159: void Midi_Data_ReadByte(void) 160: { 161: Dprintf(("Midi_ReadData : $%x.\n", 1)); 162: 1.1.1.9 ! root 163: ACIA_AddWaitCycles (); /* Additional cycles when accessing the ACIA */ 1.1.1.6 root 164: 165: MidiStatusRegister &= ~(ACIA_SR_INTERRUPT_REQUEST|ACIA_SR_RX_FULL); 166: 167: /* GPIP I4 - General Purpose Pin Keyboard/MIDI interrupt, 168: * becomes high(1) again after data has been read. */ 169: MFP_GPIP |= 0x10; 170: 171: IoMem[0xfffc06] = nRxDataByte; 172: } 173: 174: 1.1.1.5 root 175: /** 176: * Write to MIDI data register ($FFFC06). 177: */ 1.1.1.3 root 178: void Midi_Data_WriteByte(void) 1.1.1.2 root 179: { 1.1.1.6 root 180: Uint8 nTxDataByte; 1.1.1.4 root 181: 1.1.1.9 ! root 182: ACIA_AddWaitCycles (); /* Additional cycles when accessing the ACIA */ 1.1.1.4 root 183: 1.1.1.6 root 184: nTxDataByte = IoMem[0xfffc06]; 1.1.1.3 root 185: 1.1.1.6 root 186: Dprintf(("Midi_WriteData($%x)\n", nTxDataByte)); 1.1.1.2 root 187: 188: MidiStatusRegister &= ~ACIA_SR_INTERRUPT_REQUEST; 189: 190: if (!ConfigureParams.Midi.bEnableMidi) 191: return; 192: 1.1.1.6 root 193: if (pMidiFhOut) 1.1.1.2 root 194: { 195: int ret; 1.1.1.6 root 196: 1.1.1.2 root 197: /* Write the character to the output file: */ 1.1.1.6 root 198: ret = fputc(nTxDataByte, pMidiFhOut); 199: 1.1.1.2 root 200: /* If there was an error then stop the midi emulation */ 201: if (ret == EOF) 202: { 203: Midi_UnInit(); 204: return; 205: } 206: } 207: 1.1.1.6 root 208: MidiStatusRegister &= ~ACIA_SR_TX_EMPTY; 209: } 210: 211: 212: /** 213: * Read and write MIDI interface data regularly 214: */ 215: void Midi_InterruptHandler_Update(void) 216: { 217: int nInChar; 218: 219: /* Remove this interrupt from list and re-order */ 1.1.1.8 root 220: CycInt_AcknowledgeInterrupt(); 1.1.1.6 root 221: 222: /* Flush outgoing data */ 223: if (!(MidiStatusRegister & ACIA_SR_TX_EMPTY)) 1.1.1.2 root 224: { 1.1.1.6 root 225: /* Do we need to generate a transfer interrupt? */ 226: if ((MidiControlRegister & 0xA0) == 0xA0) 227: { 228: Dprintf(("WriteData: Transfer interrupt!\n")); 229: /* Acknowledge in MFP circuit, pass bit,enable,pending */ 1.1.1.9 ! root 230: MFP_InputOnChannel ( MFP_INT_ACIA , 0 ); 1.1.1.6 root 231: MidiStatusRegister |= ACIA_SR_INTERRUPT_REQUEST; 232: } 1.1.1.2 root 233: 1.1.1.6 root 234: // if (pMidiFhOut) 235: // fflush(pMidiFhOut); 1.1.1.2 root 236: 1.1.1.6 root 237: MidiStatusRegister |= ACIA_SR_TX_EMPTY; 1.1.1.2 root 238: } 1.1.1.6 root 239: 240: /* Read the bytes in, if we have any */ 241: if (pMidiFhIn && File_InputAvailable(pMidiFhIn)) 242: { 243: nInChar = fgetc(pMidiFhIn); 244: if (nInChar != EOF) 245: { 246: Dprintf(("Midi: Read character $%x\n", nInChar)); 247: /* Copy into our internal queue */ 248: nRxDataByte = nInChar; 249: /* Do we need to generate a receive interrupt? */ 250: if ((MidiControlRegister & 0x80) == 0x80) 251: { 252: Dprintf(("WriteData: Receive interrupt!\n")); 253: /* Acknowledge in MFP circuit */ 1.1.1.9 ! root 254: MFP_InputOnChannel ( MFP_INT_ACIA , 0 ); 1.1.1.6 root 255: MidiStatusRegister |= ACIA_SR_INTERRUPT_REQUEST; 256: } 257: MidiStatusRegister |= ACIA_SR_RX_FULL; 258: /* GPIP I4 - General Purpose Pin Keyboard/MIDI interrupt: 259: * It will remain low(0) until data is read from $fffc06. */ 260: MFP_GPIP &= ~0x10; 261: } 262: else 263: { 264: Dprintf(("Midi: error during read!\n")); 265: clearerr(pMidiFhIn); 266: } 267: } 268: 1.1.1.8 root 269: CycInt_AddRelativeInterrupt(2050, INT_CPU_CYCLE, INTERRUPT_MIDI); 1.1.1.2 root 270: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.