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