|
|
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.");
1.1.1.7 root 66: ConfigureParams.Midi.bEnableMidi = false;
1.1.1.6 root 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.");
1.1.1.7 root 80: ConfigureParams.Midi.bEnableMidi = false;
1.1.1.6 root 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:
1.1.1.8 ! root 98: CycInt_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: {
1.1.1.8 ! root 113: CycInt_AddRelativeInterrupt(2050, INT_CPU_CYCLE, INTERRUPT_MIDI);
1.1.1.6 root 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 */
1.1.1.8 ! root 223: CycInt_AcknowledgeInterrupt();
1.1.1.6 root 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:
1.1.1.8 ! root 272: CycInt_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.