diff --git a/src/config.c b/src/config.c
index 583605583..2b9026989 100644
--- a/src/config.c
+++ b/src/config.c
@@ -649,6 +649,12 @@ load_sound(void)
else
midi_device_current = 0;
+ p = config_get_string(cat, "midi_in_device", NULL);
+ if (p != NULL)
+ midi_input_device_current = midi_in_device_get_from_internal_name(p);
+ else
+ midi_input_device_current = 0;
+
mpu401_standalone_enable = !!config_get_int(cat, "mpu401_standalone", 0);
SSI2001 = !!config_get_int(cat, "ssi2001", 0);
@@ -1531,6 +1537,11 @@ save_sound(void)
else
config_set_string(cat, "midi_device", midi_device_get_internal_name(midi_device_current));
+ if (!strcmp(midi_in_device_get_internal_name(midi_input_device_current), "none"))
+ config_delete_var(cat, "midi_in_device");
+ else
+ config_set_string(cat, "midi_in_device", midi_in_device_get_internal_name(midi_input_device_current));
+
if (mpu401_standalone_enable == 0)
config_delete_var(cat, "mpu401_standalone");
else
diff --git a/src/device.h b/src/device.h
index e59952205..dbf9976a2 100644
--- a/src/device.h
+++ b/src/device.h
@@ -50,6 +50,7 @@
#define CONFIG_HEX16 7
#define CONFIG_HEX20 8
#define CONFIG_MAC 9
+#define CONFIG_MIDI_IN 10
enum {
diff --git a/src/plat_midi.h b/src/plat_midi.h
index 5070de68a..933e49ee6 100644
--- a/src/plat_midi.h
+++ b/src/plat_midi.h
@@ -7,3 +7,9 @@ extern int plat_midi_write(uint8_t val);
extern int plat_midi_get_num_devs(void);
extern void plat_midi_get_dev_name(int num, char *s);
+
+extern void plat_midi_input_init(void);
+extern void plat_midi_input_close(void);
+
+extern int plat_midi_in_get_num_devs(void);
+extern void plat_midi_in_get_dev_name(int num, char *s);
diff --git a/src/sound/midi.c b/src/sound/midi.c
index 1f5f0ed47..98c5100a0 100644
--- a/src/sound/midi.c
+++ b/src/sound/midi.c
@@ -8,7 +8,7 @@
*
* MIDI device core module.
*
- * Version: @(#)midi.c 1.0.1 2018/10/10
+ * Version: @(#)midi.c 1.0.2 2018/12/31
*
* Authors: Sarah Walker,
* Miran Grca,
@@ -37,28 +37,23 @@
#ifdef USE_MUNT
# include "midi_mt32.h"
#endif
+#include "midi_input.h"
-#define SYSEX_SIZE 1024
-#define RAWBUF 1024
-
int midi_device_current = 0;
static int midi_device_last = 0;
+int midi_input_device_current = 0;
+static int midi_input_device_last = 0;
+midi_t *midi = NULL, *midi_in = NULL;
-typedef struct
-{
- uint8_t midi_rt_buf[1024], midi_cmd_buf[1024],
- midi_status, midi_sysex_data[1026];
- int midi_cmd_pos, midi_cmd_len;
- unsigned int midi_sysex_start, midi_sysex_delay,
- midi_pos;
- midi_device_t* m_device;
-} midi_t;
+void (*input_msg)(void *p, uint8_t *msg);
+int (*input_sysex)(void *p, uint8_t *buffer, uint32_t len, int abort);
+void *midi_in_p;
-static midi_t *midi = NULL;
+uint8_t MIDI_InSysexBuf[SYSEX_SIZE];
-static uint8_t MIDI_evt_len[256] = {
+uint8_t MIDI_evt_len[256] = {
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* 0x00 */
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* 0x10 */
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* 0x20 */
@@ -85,7 +80,7 @@ typedef struct
{
const char *name, *internal_name;
const device_t *device;
-} MIDI_DEVICE;
+} MIDI_DEVICE, MIDI_IN_DEVICE;
static const MIDI_DEVICE devices[] =
{
@@ -101,6 +96,12 @@ static const MIDI_DEVICE devices[] =
{"", "", NULL}
};
+static const MIDI_IN_DEVICE midi_in_devices[] =
+{
+ {"None", "none", NULL},
+ {MIDI_INPUT_NAME, MIDI_INPUT_INTERNAL_NAME, &midi_input_device},
+ {"", "", NULL}
+};
int
midi_device_available(int card)
@@ -172,16 +173,25 @@ midi_init(midi_device_t* device)
midi = (midi_t *) malloc(sizeof(midi_t));
memset(midi, 0, sizeof(midi_t));
- midi->m_device = device;
+ midi->m_out_device = device;
+}
+
+void
+midi_in_init(midi_device_t* device, midi_t **mididev)
+{
+ *mididev = (midi_t *)malloc(sizeof(midi_t));
+ memset(*mididev, 0, sizeof(midi_t));
+
+ (*mididev)->m_in_device = device;
}
void
midi_close(void)
{
- if (midi && midi->m_device) {
- free(midi->m_device);
- midi->m_device = NULL;
+ if (midi && midi->m_out_device) {
+ free(midi->m_out_device);
+ midi->m_out_device = NULL;
}
if (midi) {
@@ -190,41 +200,137 @@ midi_close(void)
}
}
+void
+midi_in_close(void)
+{
+ if (midi_in && midi_in->m_in_device) {
+ free(midi_in->m_in_device);
+ midi_in->m_in_device = NULL;
+ }
+
+ if (midi_in) {
+ free(midi_in);
+ midi_in = NULL;
+ }
+}
+
void
midi_poll(void)
{
- if (midi && midi->m_device && midi->m_device->poll)
- midi->m_device->poll();
+ if (midi && midi->m_out_device && midi->m_out_device->poll)
+ midi->m_out_device->poll();
}
void
play_msg(uint8_t *msg)
{
- if (midi->m_device->play_msg)
- midi->m_device->play_msg(msg);
+ if (midi->m_out_device->play_msg)
+ midi->m_out_device->play_msg(msg);
}
void
play_sysex(uint8_t *sysex, unsigned int len)
{
- if (midi->m_device->play_sysex)
- midi->m_device->play_sysex(sysex, len);
+ if (midi->m_out_device->play_sysex)
+ midi->m_out_device->play_sysex(sysex, len);
}
+int
+midi_in_device_available(int card)
+{
+ if (midi_in_devices[card].device)
+ return device_available(midi_in_devices[card].device);
+
+ return 1;
+}
+
+char *
+midi_in_device_getname(int card)
+{
+ return (char *) midi_in_devices[card].name;
+}
+
+const device_t *
+midi_in_device_getdevice(int card)
+{
+ return midi_in_devices[card].device;
+}
+
+int
+midi_in_device_has_config(int card)
+{
+ if (!midi_in_devices[card].device)
+ return 0;
+ return midi_in_devices[card].device->config ? 1 : 0;
+}
+
+
+char *
+midi_in_device_get_internal_name(int card)
+{
+ return (char *) midi_in_devices[card].internal_name;
+}
+
+
+int
+midi_in_device_get_from_internal_name(char *s)
+{
+ int c = 0;
+
+ while (strlen(midi_in_devices[c].internal_name)) {
+ if (!strcmp(midi_in_devices[c].internal_name, s))
+ return c;
+ c++;
+ }
+
+ return 0;
+}
+
void
-midi_write(uint8_t val)
+midi_in_device_init()
+{
+ if (midi_in_devices[midi_input_device_current].device)
+ device_add(midi_in_devices[midi_input_device_current].device);
+ midi_input_device_last = midi_input_device_current;
+}
+
+void
+midi_raw_out_rt_byte(uint8_t val)
+{
+ if (!midi_in->midi_realtime)
+ return;
+
+ if ((!midi_in->midi_clockout && (val == 0xf8)))
+ return;
+
+ midi_in->midi_cmd_r = val << 24;
+ pclog("Play RT Byte msg\n");
+ play_msg((uint8_t *)&midi_in->midi_cmd_r);
+}
+
+void
+midi_raw_out_thru_rt_byte(uint8_t val)
+{
+ if (midi_in->thruchan)
+ midi_raw_out_rt_byte(val);
+}
+
+void
+midi_raw_out_byte(uint8_t val)
{
uint32_t passed_ticks;
- if (!midi || !midi->m_device)
+ if (!midi || !midi->m_out_device) {
return;
+ }
- if (midi->m_device->write && midi->m_device->write(val))
+ if ((midi->m_out_device->write && midi->m_out_device->write(val))) {
return;
+ }
if (midi->midi_sysex_start) {
passed_ticks = plat_get_ticks() - midi->midi_sysex_start;
@@ -289,3 +395,12 @@ midi_write(uint8_t val)
}
}
}
+
+void
+midi_clear_buffer(void)
+{
+ midi->midi_pos = 0;
+ midi->midi_status = 0x00;
+ midi->midi_cmd_pos = 0;
+ midi->midi_cmd_len = 0;
+}
\ No newline at end of file
diff --git a/src/sound/midi.h b/src/sound/midi.h
index 6268a0e0c..8e516c2a3 100644
--- a/src/sound/midi.h
+++ b/src/sound/midi.h
@@ -2,18 +2,35 @@
# define EMU_SOUND_MIDI_H
-extern int midi_device_current;
+#define SYSEX_SIZE 8192
+extern uint8_t MIDI_InSysexBuf[SYSEX_SIZE];
+extern uint8_t MIDI_evt_len[256];
+
+extern int midi_device_current;
+extern int midi_input_device_current;
+
+extern void (*input_msg)(void *p, uint8_t *msg);
+extern int (*input_sysex)(void *p, uint8_t *buffer, uint32_t len, int abort);
+extern void *midi_in_p;
int midi_device_available(int card);
+int midi_in_device_available(int card);
char *midi_device_getname(int card);
+char *midi_in_device_getname(int card);
#ifdef EMU_DEVICE_H
const device_t *midi_device_getdevice(int card);
+const device_t *midi_in_device_getdevice(int card);
#endif
int midi_device_has_config(int card);
+int midi_in_device_has_config(int card);
char *midi_device_get_internal_name(int card);
+char *midi_in_device_get_internal_name(int card);
int midi_device_get_from_internal_name(char *s);
+int midi_in_device_get_from_internal_name(char *s);
void midi_device_init();
+void midi_in_device_init();
+
typedef struct midi_device_t
{
@@ -23,9 +40,27 @@ typedef struct midi_device_t
int (*write)(uint8_t val);
} midi_device_t;
+typedef struct midi_t
+{
+ uint8_t midi_rt_buf[8], midi_cmd_buf[8],
+ midi_status, midi_sysex_data[SYSEX_SIZE];
+ int midi_cmd_pos, midi_cmd_len, midi_cmd_r,
+ midi_realtime, thruchan, midi_clockout;
+ unsigned int midi_sysex_start, midi_sysex_delay,
+ midi_pos;
+ midi_device_t *m_out_device, *m_in_device;
+} midi_t;
+
+extern midi_t *midi, *midi_in;
+
void midi_init(midi_device_t* device);
+void midi_in_init(midi_device_t* device, midi_t **mididev);
void midi_close();
-void midi_write(uint8_t val);
+void midi_in_close(void);
+void midi_raw_out_rt_byte(uint8_t val);
+void midi_raw_out_thru_rt_byte(uint8_t val);
+void midi_raw_out_byte(uint8_t val);
+void midi_clear_buffer(void);
void midi_poll();
#if 0
@@ -41,5 +76,7 @@ void midi_poll();
#define SYSTEM_MIDI_INTERNAL_NAME "system_midi"
#endif
+#define MIDI_INPUT_NAME "MIDI Input Device"
+#define MIDI_INPUT_INTERNAL_NAME "midi_in"
#endif /*EMU_SOUND_MIDI_H*/
diff --git a/src/sound/midi_input.h b/src/sound/midi_input.h
new file mode 100644
index 000000000..163d6fa91
--- /dev/null
+++ b/src/sound/midi_input.h
@@ -0,0 +1 @@
+extern const device_t midi_input_device;
diff --git a/src/sound/midi_system.c b/src/sound/midi_system.c
index d9a15c6a9..6ee56b01a 100644
--- a/src/sound/midi_system.c
+++ b/src/sound/midi_system.c
@@ -9,6 +9,7 @@
#include "../plat_midi.h"
#include "midi.h"
#include "midi_system.h"
+#include "midi_input.h"
void* system_midi_init(const device_t *info)
@@ -27,6 +28,22 @@ void* system_midi_init(const device_t *info)
return dev;
}
+void* midi_input_init(const device_t *info)
+{
+ midi_device_t* dev = malloc(sizeof(midi_device_t));
+ memset(dev, 0, sizeof(midi_device_t));
+
+ plat_midi_input_init();
+
+ midi_in_init(dev, &midi_in);
+
+ midi_in->midi_realtime = device_get_config_int("realtime");
+ midi_in->thruchan = device_get_config_int("thruchan");
+ midi_in->midi_clockout = device_get_config_int("clockout");
+
+ return dev;
+}
+
void system_midi_close(void* p)
{
plat_midi_close();
@@ -34,11 +51,23 @@ void system_midi_close(void* p)
midi_close();
}
+void midi_input_close(void* p)
+{
+ plat_midi_input_close();
+
+ midi_close();
+}
+
int system_midi_available(void)
{
return plat_midi_get_num_devs();
}
+int midi_input_available(void)
+{
+ return plat_midi_in_get_num_devs();
+}
+
static const device_config_t system_midi_config[] =
{
{
@@ -52,6 +81,37 @@ static const device_config_t system_midi_config[] =
}
};
+static const device_config_t midi_input_config[] =
+{
+ {
+ .name = "midi_input",
+ .description = "MIDI in device",
+ .type = CONFIG_MIDI_IN,
+ .default_int = 0
+ },
+ {
+ .name = "realtime",
+ .description = "MIDI Real time",
+ .type = CONFIG_BINARY,
+ .default_int = 0
+ },
+ {
+ .name = "thruchan",
+ .description = "MIDI Thru",
+ .type = CONFIG_BINARY,
+ .default_int = 1
+ },
+ {
+ .name = "clockout",
+ .description = "MIDI Clockout",
+ .type = CONFIG_BINARY,
+ .default_int = 1
+ },
+ {
+ .type = -1
+ }
+};
+
const device_t system_midi_device =
{
SYSTEM_MIDI_NAME,
@@ -64,3 +124,17 @@ const device_t system_midi_device =
NULL,
system_midi_config
};
+
+
+const device_t midi_input_device =
+{
+ MIDI_INPUT_NAME,
+ 0, 0,
+ midi_input_init,
+ midi_input_close,
+ NULL,
+ midi_input_available,
+ NULL,
+ NULL,
+ midi_input_config
+};
\ No newline at end of file
diff --git a/src/sound/snd_audiopci.c b/src/sound/snd_audiopci.c
index c6bbefc6b..f24a383d3 100644
--- a/src/sound/snd_audiopci.c
+++ b/src/sound/snd_audiopci.c
@@ -13,6 +13,8 @@
#include "../pci.h"
#include "../timer.h"
#include "sound.h"
+#include "midi.h"
+#include "snd_mpu401.h"
#include "snd_audiopci.h"
@@ -23,6 +25,8 @@
static float low_fir_es1371_coef[ES1371_NCoef];
typedef struct {
+ mpu_t mpu;
+
uint8_t pci_command, pci_serr;
uint32_t base_addr;
@@ -116,14 +120,21 @@ typedef struct {
#define INT_DAC1_EN (1<<6)
#define INT_DAC2_EN (1<<5)
+#define INT_UART_EN (1<<3)
#define SI_P2_INTR_EN (1<<9)
#define SI_P1_INTR_EN (1<<8)
#define INT_STATUS_INTR (1<<31)
+#define INT_STATUS_UART (1<<3)
#define INT_STATUS_DAC1 (1<<2)
#define INT_STATUS_DAC2 (1<<1)
+#define UART_CTRL_TXINTEN (1<<5)
+
+#define UART_STATUS_TXINT (1<<2)
+#define UART_STATUS_TXRDY (1<<1)
+
#define FORMAT_MONO_8 0
#define FORMAT_STEREO_8 1
#define FORMAT_MONO_16 2
@@ -164,8 +175,13 @@ static void es1371_update_irqs(es1371_t *es1371)
if ((es1371->int_status & INT_STATUS_DAC1) && (es1371->si_cr & SI_P1_INTR_EN))
irq = 1;
- if ((es1371->int_status & INT_STATUS_DAC2) && (es1371->si_cr & SI_P2_INTR_EN))
+ if ((es1371->int_status & INT_STATUS_DAC2) && (es1371->si_cr & SI_P2_INTR_EN)) {
irq = 1;
+ }
+ /*MIDI input is unsupported for now*/
+ if ((es1371->int_status & INT_STATUS_UART) && (es1371->uart_status & UART_STATUS_TXINT)) {
+ irq = 1;
+ }
if (irq)
es1371->int_status |= INT_STATUS_INTR;
@@ -219,10 +235,10 @@ static uint8_t es1371_inb(uint16_t port, void *p)
case 0x07:
ret = (es1371->int_status >> 24) & 0xff;
break;
-
-
+
case 0x09:
- ret = es1371->uart_status;
+ ret = es1371->uart_status & 0xc7;
+ audiopci_log("ES1371 UART Status = %02x\n", es1371->uart_status);
break;
case 0x0c:
@@ -253,7 +269,7 @@ static uint8_t es1371_inb(uint16_t port, void *p)
audiopci_log("Bad es1371_inb: port=%04x\n", port);
}
-// audiopci_log("es1371_inb: port=%04x ret=%02x\n", port, ret);
+ audiopci_log("es1371_inb: port=%04x ret=%02x\n", port, ret);
// output = 3;
return ret;
}
@@ -340,31 +356,51 @@ static uint32_t es1371_inl(uint16_t port, void *p)
ret |= CODEC_READY;
break;
- case 0x34:
- switch (es1371->mem_page)
- {
+ case 0x30:
+ switch (es1371->mem_page) {
+ case 0xe: case 0xf:
+ audiopci_log("ES1371 0x30 read UART FIFO: val = %02x\n", ret & 0xff);
+ break;
+ }
+ break;
+ case 0x34:
+ switch (es1371->mem_page) {
case 0xc:
ret = es1371->dac[0].size | (es1371->dac[0].count << 16);
break;
case 0xd:
-
ret = es1371->adc.size | (es1371->adc.count << 16);
break;
+ case 0xe: case 0xf:
+ audiopci_log("ES1371 0x34 read UART FIFO: val = %02x\n", ret & 0xff);
+ break;
+
default:
audiopci_log("Bad es1371_inl: mem_page=%x port=%04x\n", es1371->mem_page, port);
}
break;
+ case 0x38:
+ switch (es1371->mem_page) {
+ case 0xe: case 0xf:
+ audiopci_log("ES1371 0x38 read UART FIFO: val = %02x\n", ret & 0xff);
+ break;
+ }
+ break;
+
case 0x3c:
- switch (es1371->mem_page)
- {
+ switch (es1371->mem_page) {
case 0xc:
ret = es1371->dac[1].size | (es1371->dac[1].count << 16);
break;
-
+
+ case 0xe: case 0xf:
+ audiopci_log("ES1371 0x3c read UART FIFO: val = %02x\n", ret & 0xff);
+ break;
+
default:
audiopci_log("Bad es1371_inl: mem_page=%x port=%04x\n", es1371->mem_page, port);
}
@@ -374,7 +410,7 @@ static uint32_t es1371_inl(uint16_t port, void *p)
audiopci_log("Bad es1371_inl: port=%04x\n", port);
}
-// audiopci_log("es1371_inl: port=%04x ret=%08x %08x\n", port, ret, cpu_state.pc);
+ audiopci_log("es1371_inl: port=%04x ret=%08x\n", port, ret);
return ret;
}
@@ -382,7 +418,7 @@ static void es1371_outb(uint16_t port, uint8_t val, void *p)
{
es1371_t *es1371 = (es1371_t *)p;
-// audiopci_log("es1371_outb: port=%04x val=%02x %04x:%08x\n", port, val, cs, cpu_state.pc);
+ audiopci_log("es1371_outb: port=%04x val=%02x\n", port, val);
switch (port & 0x3f)
{
case 0x00:
@@ -412,8 +448,13 @@ static void es1371_outb(uint16_t port, uint8_t val, void *p)
es1371->int_ctrl = (es1371->int_ctrl & 0x00ffffff) | (val << 24);
break;
+ case 0x08:
+ midi_raw_out_byte(val);
+ break;
+
case 0x09:
- es1371->uart_ctrl = val;
+ es1371->uart_ctrl = val & 0xe3;
+ audiopci_log("ES1371 UART Cntrl = %02x\n", es1371->uart_ctrl);
break;
case 0x0c:
@@ -481,7 +522,7 @@ static void es1371_outl(uint16_t port, uint32_t val, void *p)
{
es1371_t *es1371 = (es1371_t *)p;
-// audiopci_log("es1371_outl: port=%04x val=%08x %04x:%08x\n", port, val, CS, cpu_state.pc);
+ audiopci_log("es1371_outl: port=%04x val=%08x\n", port, val);
switch (port & 0x3f)
{
case 0x04:
@@ -593,6 +634,10 @@ static void es1371_outl(uint16_t port, uint32_t val, void *p)
// audiopci_log("DAC1 addr %08x\n", val);
break;
+ case 0xe: case 0xf:
+ audiopci_log("ES1371 0x30 write UART FIFO: val = %02x\n", val & 0xff);
+ break;
+
default:
audiopci_log("Bad es1371_outl: mem_page=%x port=%04x val=%08x\n", es1371->mem_page, port, val);
}
@@ -615,6 +660,10 @@ static void es1371_outl(uint16_t port, uint32_t val, void *p)
es1371->adc.count = val >> 16;
break;
+ case 0xe: case 0xf:
+ audiopci_log("ES1371 0x34 write UART FIFO: val = %02x\n", val & 0xff);
+ break;
+
default:
audiopci_log("Bad es1371_outl: mem_page=%x port=%04x val=%08x\n", es1371->mem_page, port, val);
}
@@ -633,6 +682,10 @@ static void es1371_outl(uint16_t port, uint32_t val, void *p)
case 0xd:
break;
+
+ case 0xe: case 0xf:
+ audiopci_log("ES1371 0x38 write UART FIFO: val = %02x\n", val & 0xff);
+ break;
default:
audiopci_log("Bad es1371_outl: mem_page=%x port=%04x val=%08x\n", es1371->mem_page, port, val);
@@ -649,6 +702,10 @@ static void es1371_outl(uint16_t port, uint32_t val, void *p)
case 0xc:
es1371->dac[1].size = val & 0xffff;
es1371->dac[1].count = val >> 16;
+ break;
+
+ case 0xe: case 0xf:
+ audiopci_log("ES1371 0x3c write UART FIFO: val = %02x\n", val & 0xff);
break;
default:
@@ -1107,10 +1164,33 @@ static void es1371_poll(void *p)
timer_advance_u64(&es1371->dac[1].timer, es1371->dac[1].latch);
- es1371_update(es1371);
-
- if (es1371->int_ctrl & INT_DAC1_EN)
- {
+ es1371_update(es1371);
+
+ if (es1371->int_ctrl & INT_UART_EN) {
+ audiopci_log("UART INT Enabled\n");
+ if (es1371->uart_ctrl & 0x80) { /*We currently don't implement MIDI Input.*/
+ /*But if anything sets MIDI Input and Output together we'd have to take account
+ of the MIDI Output case, and disable IRQ's and RX bits when MIDI Input is enabled as well but not in the MIDI Output portion*/
+ if (es1371->uart_ctrl & UART_CTRL_TXINTEN)
+ es1371->int_status |= INT_STATUS_UART;
+ else
+ es1371->int_status &= ~INT_STATUS_UART;
+ } else if (!(es1371->uart_ctrl & 0x80) && ((es1371->uart_ctrl & UART_CTRL_TXINTEN))) { /*Or enable the UART IRQ and the respective TX bits only when the MIDI Output is enabled*/
+ es1371->int_status |= INT_STATUS_UART;
+ }
+
+ if (es1371->uart_ctrl & 0x80) {
+ if (es1371->uart_ctrl & UART_CTRL_TXINTEN)
+ es1371->uart_status |= (UART_STATUS_TXINT | UART_STATUS_TXRDY);
+ else
+ es1371->uart_status &= ~(UART_STATUS_TXINT | UART_STATUS_TXRDY);
+ } else
+ es1371->uart_status |= (UART_STATUS_TXINT | UART_STATUS_TXRDY);
+
+ es1371_update_irqs(es1371);
+ }
+
+ if (es1371->int_ctrl & INT_DAC1_EN) {
int frac = es1371->dac[0].ac & 0x7fff;
int idx = es1371->dac[0].ac >> 15;
int samp1_l = es1371->dac[0].filtered_l[idx];
diff --git a/src/sound/snd_gus.c b/src/sound/snd_gus.c
index 46bd3408d..1f8fcfd3b 100644
--- a/src/sound/snd_gus.c
+++ b/src/sound/snd_gus.c
@@ -13,8 +13,39 @@
#include "../timer.h"
#include "../device.h"
#include "sound.h"
+#include "midi.h"
#include "snd_gus.h"
+enum
+{
+ MIDI_INT_RECEIVE = 0x01,
+ MIDI_INT_TRANSMIT = 0x02,
+ MIDI_INT_MASTER = 0x80
+};
+
+enum
+{
+ MIDI_CTRL_TRANSMIT_MASK = 0x60,
+ MIDI_CTRL_TRANSMIT = 0x20,
+ MIDI_CTRL_RECEIVE = 0x80
+};
+
+enum
+{
+ GUS_INT_MIDI_TRANSMIT = 0x01,
+ GUS_INT_MIDI_RECEIVE = 0x02
+};
+
+enum
+{
+ GUS_TIMER_CTRL_AUTO = 0x01
+};
+
+enum
+{
+ GUS_CLASSIC = 0,
+ GUS_MAX = 1,
+};
typedef struct gus_t
{
@@ -51,12 +82,14 @@ typedef struct gus_t
uint64_t samp_latch;
uint8_t *ram;
+ uint32_t gus_end_ram;
int irqnext;
pc_timer_t timer_1, timer_2;
int irq, dma, irq_midi;
+ uint16_t base;
int latch_enable;
uint8_t sb_2xa, sb_2xc, sb_2xe;
@@ -68,9 +101,9 @@ typedef struct gus_t
uint8_t ad_status, ad_data;
uint8_t ad_timer_ctrl;
- uint8_t midi_ctrl, midi_status;
- uint8_t midi_data;
- int midi_loopback;
+ uint8_t midi_ctrl, midi_status, midi_queue[64], midi_data;
+ int midi_r, midi_w;
+ int uart_in, uart_out, sysex;
uint8_t gp1, gp2;
uint16_t gp1_addr, gp2_addr;
@@ -78,8 +111,8 @@ typedef struct gus_t
uint8_t usrr;
} gus_t;
-static int gus_irqs[8] = {-1, 2, 5, 3, 7, 11, 12, 15};
-static int gus_irqs_midi[8] = {-1, 2, 5, 3, 7, 11, 12, 15};
+static int gus_gf1_irqs[8] = {0, 2, 5, 3, 7, 11, 12, 15};
+static int gus_midi_irqs[8] = {0, 2, 5, 3, 7, 11, 12, 15};
static int gus_dmas[8] = {-1, 1, 3, 5, 6, 7, -1, -1};
int gusfreqs[]=
@@ -102,48 +135,26 @@ void pollgusirqs(gus_t *gus)
gus->irqstatus2=0x60|c;
if (gus->rampirqs[c]) gus->irqstatus2 |= 0x80;
gus->irqstatus|=0x20;
- if (gus->irq != -1)
- picint(1 << gus->irq);
+ if (gus->irq != 0)
+ picint(1 << gus->irq);
return;
}
if (gus->rampirqs[c])
{
gus->irqstatus2=0xA0|c;
gus->irqstatus|=0x40;
- if (gus->irq != -1)
- picint(1 << gus->irq);
+ if (gus->irq != 0)
+ picint(1 << gus->irq);
return;
}
}
gus->irqstatus2=0xE0;
- if (!gus->irqstatus && gus->irq != -1) picintc(1 << gus->irq);
+ if (!gus->irqstatus) {
+ if (gus->irq != 0)
+ picintc(1 << gus->irq);
+ }
}
-enum
-{
- MIDI_INT_RECEIVE = 0x01,
- MIDI_INT_TRANSMIT = 0x02,
- MIDI_INT_MASTER = 0x80
-};
-
-enum
-{
- MIDI_CTRL_TRANSMIT_MASK = 0x60,
- MIDI_CTRL_TRANSMIT = 0x20,
- MIDI_CTRL_RECEIVE = 0x80
-};
-
-enum
-{
- GUS_INT_MIDI_TRANSMIT = 0x01,
- GUS_INT_MIDI_RECEIVE = 0x02
-};
-
-enum
-{
- GUS_TIMER_CTRL_AUTO = 0x01
-};
-
void gus_midi_update_int_status(gus_t *gus)
{
gus->midi_status &= ~MIDI_INT_MASTER;
@@ -157,57 +168,63 @@ void gus_midi_update_int_status(gus_t *gus)
if ((gus->midi_ctrl & MIDI_CTRL_RECEIVE) && (gus->midi_status & MIDI_INT_RECEIVE))
{
- gus->midi_status |= MIDI_INT_MASTER;
- gus->irqstatus |= GUS_INT_MIDI_RECEIVE;
+ gus->midi_status |= MIDI_INT_MASTER;
+ gus->irqstatus |= GUS_INT_MIDI_RECEIVE;
}
else
gus->irqstatus &= ~GUS_INT_MIDI_RECEIVE;
- if ((gus->midi_status & MIDI_INT_MASTER) && (gus->irq_midi != -1))
+ if ((gus->midi_status & MIDI_INT_MASTER) && (gus->irq_midi != 0))
{
picint(1 << gus->irq_midi);
}
}
-
+
void writegus(uint16_t addr, uint8_t val, void *p)
{
gus_t *gus = (gus_t *)p;
int c, d;
int old;
- if (gus->latch_enable && addr != 0x24b)
- gus->latch_enable = 0;
- switch (addr)
+ uint16_t port;
+
+ if ((addr == 0x388) || (addr == 0x389))
+ port = addr;
+ else
+ port = addr & 0xf0f;
+
+ switch (port)
{
- case 0x340: /*MIDI control*/
+ case 0x300: /*MIDI control*/
old = gus->midi_ctrl;
gus->midi_ctrl = val;
-
- if ((val & 3) == 3)
- gus->midi_status = 0;
- else if ((old & 3) == 3)
- {
- gus->midi_status |= MIDI_INT_TRANSMIT;
- }
+ gus->uart_out = 1;
+
+ if ((val & 3) == 3) { /*Master reset*/
+ gus->uart_in = 0;
+ gus->midi_status = 0;
+ gus->midi_r = 0;
+ gus->midi_w = 0;
+ } else if ((old & 3) == 3) {
+ gus->midi_status |= MIDI_INT_TRANSMIT;
+ } else if (gus->midi_ctrl & MIDI_CTRL_RECEIVE) {
+ gus->uart_in = 1;
+ }
gus_midi_update_int_status(gus);
break;
-
- case 0x341: /*MIDI data*/
- if (gus->midi_loopback)
- {
- gus->midi_status |= MIDI_INT_RECEIVE;
- gus->midi_data = val;
+ case 0x301: /*MIDI data*/
+ gus->midi_data = val;
+ if (gus->uart_out) {
+ midi_raw_out_byte(val);
}
- else
- gus->midi_status |= MIDI_INT_TRANSMIT;
+ if (gus->latch_enable & 0x20) {
+ gus->midi_status |= MIDI_INT_RECEIVE;
+ } else
+ gus->midi_status |= MIDI_INT_TRANSMIT;
break;
-
- case 0x342: /*Voice select*/
- gus->voice=val&31;
- break;
- case 0x343: /*Global select*/
+ case 0x303: /*Global select*/
gus->global=val;
break;
- case 0x344: /*Global low*/
+ case 0x304: /*Global low*/
switch (gus->global)
{
case 0: /*Voice control*/
@@ -231,11 +248,11 @@ void writegus(uint16_t addr, uint8_t val, void *p)
gus->end[gus->voice]=(gus->end[gus->voice]&0x1FFFFF00)|val;
break;
- case 0x6: /*Ramp frequency*/
+ case 6: /*Ramp frequency*/
gus->rfreq[gus->voice] = (int)( (double)((val & 63)*512)/(double)(1 << (3*(val >> 6))));
break;
- case 0x9: /*Current volume*/
+ case 9: /*Current volume*/
gus->curvol[gus->voice] = gus->rcur[gus->voice] = (gus->rcur[gus->voice] & ~(0xff << 6)) | (val << 6);
break;
@@ -259,14 +276,10 @@ gus->curx[gus->voice]=(gus->curx[gus->voice]&0xF807F00)|((val<<7)<<8);
break;
}
break;
- case 0x345: /*Global high*/
+ case 0x305: /*Global high*/
switch (gus->global)
{
case 0: /*Voice control*/
- if (!(val&1) && gus->ctrl[gus->voice]&1)
- {
- }
-
gus->ctrl[gus->voice] = val & 0x7f;
old = gus->waveirqs[gus->voice];
@@ -294,16 +307,16 @@ gus->curx[gus->voice]=(gus->curx[gus->voice]&0xF807F00)|((val<<7)<<8);
gus->end[gus->voice]=(gus->end[gus->voice]&0x1FFF00FF)|(val<<8);
break;
- case 0x6: /*Ramp frequency*/
+ case 6: /*Ramp frequency*/
gus->rfreq[gus->voice] = (int)( (double)((val & 63) * (1 << 10))/(double)(1 << (3 * (val >> 6))));
break;
- case 0x7: /*Ramp start*/
+ case 7: /*Ramp start*/
gus->rstart[gus->voice] = val << 14;
break;
- case 0x8: /*Ramp end*/
+ case 8: /*Ramp end*/
gus->rend[gus->voice] = val << 14;
break;
- case 0x9: /*Current volume*/
+ case 9: /*Current volume*/
gus->curvol[gus->voice] = gus->rcur[gus->voice] = (gus->rcur[gus->voice] & ~(0xff << 14)) | (val << 14);
break;
@@ -449,11 +462,12 @@ gus->curx[gus->voice]=(gus->curx[gus->voice]&0xFFF8000)|((val&0x7F)<<8);
break;
}
break;
- case 0x347: /*DRAM access*/
- gus->ram[gus->addr]=val;
+ case 0x307: /*DRAM access*/
gus->addr&=0xFFFFF;
+ if (gus->addr < gus->gus_end_ram)
+ gus->ram[gus->addr]=val;
break;
- case 0x248: case 0x388:
+ case 0x208: case 0x388:
gus->adcommand = val;
break;
@@ -466,7 +480,7 @@ gus->curx[gus->voice]=(gus->curx[gus->voice]&0xFFF8000)|((val&0x7F)<<8);
{
if (gus->sb_nmi)
nmi = 1;
- else if (gus->irq != -1)
+ else
picint(1 << gus->irq);
}
}
@@ -493,34 +507,76 @@ gus->curx[gus->voice]=(gus->curx[gus->voice]&0xFFF8000)|((val&0x7F)<<8);
}
break;
- case 0x240:
- gus->midi_loopback = val & 0x20;
- gus->latch_enable = (val & 0x40) ? 2 : 1;
+ case 0x200:
+ gus->latch_enable = val;
break;
- case 0x24b:
+ case 0x20b:
switch (gus->reg_ctrl & 0x07)
{
case 0:
- if (gus->latch_enable == 1)
- gus->dma = gus_dmas[val & 7];
- if (gus->latch_enable == 2)
- {
- gus->irq = gus_irqs[val & 7];
-
- if (val & 0x40)
- {
- if (gus->irq == -1)
- gus->irq = gus->irq_midi = gus_irqs[(val >> 3) & 7];
- else
- gus->irq_midi = gus->irq;
- }
- else
- gus->irq_midi = gus_irqs_midi[(val >> 3) & 7];
-
- gus->sb_nmi = val & 0x80;
- }
- gus->latch_enable = 0;
+ if (gus->latch_enable & 0x40) {
+ // GUS SDK: IRQ Control Register
+ // Channel 1 GF1 IRQ selector (bits 2-0)
+ // 0=reserved, do not use
+ // 1=IRQ2
+ // 2=IRQ5
+ // 3=IRQ3
+ // 4=IRQ7
+ // 5=IRQ11
+ // 6=IRQ12
+ // 7=IRQ15
+ // Channel 2 MIDI IRQ selector (bits 5-3)
+ // 0=no interrupt
+ // 1=IRQ2
+ // 2=IRQ5
+ // 3=IRQ3
+ // 4=IRQ7
+ // 5=IRQ11
+ // 6=IRQ12
+ // 7=IRQ15
+ // Combine both IRQs using channel 1 (bit 6)
+ // Reserved (bit 7)
+ //
+ // "If both channels are sharing an IRQ, channel 2's IRQ must be set to 0 and turn on bit 6. A
+ // bus conflict will occur if both latches are programmed with the same IRQ #."
+ if ((val & 7) != 0)
+ gus->irq = gus_gf1_irqs[val & 7];
+
+ if (val & 0x40) // "Combine both IRQs"
+ gus->irq_midi = gus->irq;
+ else
+ gus->irq_midi = gus_midi_irqs[(val >> 3) & 7];
+
+ gus->sb_nmi = val & 0x80;
+ } else {
+ // GUS SDK: DMA Control Register
+ // Channel 1 (bits 2-0)
+ // 0=NO DMA
+ // 1=DMA1
+ // 2=DMA3
+ // 3=DMA5
+ // 4=DMA6
+ // 5=DMA7
+ // 6=?
+ // 7=?
+ // Channel 2 (bits 5-3)
+ // 0=NO DMA
+ // 1=DMA1
+ // 2=DMA3
+ // 3=DMA5
+ // 4=DMA6
+ // 5=DMA7
+ // 6=?
+ // 7=?
+ // Combine both DMA channels using channel 1 (bit 6)
+ // Reserved (bit 7)
+ //
+ // "If both channels are sharing an DMA, channel 2's DMA must be set to 0 and turn on bit 6. A
+ // bus conflict will occur if both latches are programmed with the same DMA #."
+ if (gus_dmas[val & 7] != -1)
+ gus->dma = gus_dmas[val & 7];
+ }
break;
case 1:
gus->gp1 = val;
@@ -542,67 +598,97 @@ gus->curx[gus->voice]=(gus->curx[gus->voice]&0xFFF8000)|((val&0x7F)<<8);
}
break;
- case 0x246:
- gus->ad_status |= 0x08;
- if (gus->sb_ctrl & 0x20)
- {
+ case 0x206:
+ if (gus->sb_ctrl & 0x20) {
+ gus->ad_status |= 0x08;
if (gus->sb_nmi)
- nmi = 1;
- else if (gus->irq != -1)
- picint(1 << gus->irq);
+ nmi = 1;
+ else if (gus->irq != 0)
+ picint(1 << gus->irq);
}
break;
- case 0x24a:
+ case 0x20a:
gus->sb_2xa = val;
break;
- case 0x24c:
+ case 0x20c:
gus->ad_status |= 0x10;
if (gus->sb_ctrl & 0x20)
{
if (gus->sb_nmi)
nmi = 1;
- else if (gus->irq != -1)
+ else if (gus->irq != 0)
picint(1 << gus->irq);
}
- case 0x24d:
+ case 0x20d:
gus->sb_2xc = val;
break;
- case 0x24e:
+ case 0x20e:
gus->sb_2xe = val;
break;
- case 0x24f:
+ case 0x20f:
gus->reg_ctrl = val;
break;
}
}
+
uint8_t readgus(uint16_t addr, void *p)
{
gus_t *gus = (gus_t *)p;
uint8_t val = 0xff;
- switch (addr)
+ uint16_t port;
+
+ if ((addr == 0x388) || (addr == 0x389))
+ port = addr;
+ else
+ port = addr & 0xf0f;
+
+ switch (port)
{
- case 0x340: /*MIDI status*/
- val = gus->midi_status;
+ case 0x300: /*MIDI status*/
+ val = gus->midi_status;
break;
- case 0x341: /*MIDI data*/
- val = gus->midi_data;
- gus->midi_status &= ~MIDI_INT_RECEIVE;
- gus_midi_update_int_status(gus);
+ case 0x301: /*MIDI data*/
+ val = 0;
+ if (gus->uart_in) {
+ if ((gus->midi_data == 0xaa) && (gus->midi_ctrl & MIDI_CTRL_RECEIVE)) /*Handle master reset*/
+ val = gus->midi_data;
+ else {
+ val = gus->midi_queue[gus->midi_r];
+ if (gus->midi_r != gus->midi_w) {
+ gus->midi_r++;
+ gus->midi_r &= 63;
+ }
+ }
+ gus->midi_status &= ~MIDI_INT_RECEIVE;
+ gus_midi_update_int_status(gus);
+ }
break;
- case 0x240: return 0;
- case 0x246: /*IRQ status*/
+ case 0x200:
+ val = 0xff;
+ break;
+
+ case 0x206: /*IRQ status*/
val = gus->irqstatus & ~0x10;
if (gus->ad_status & 0x19)
val |= 0x10;
- return val;
+ break;
- case 0x24F: return 0;
- case 0x342: return gus->voice;
- case 0x343: return gus->global;
- case 0x344: /*Global low*/
+ case 0x20F:
+ val = 0;
+ break;
+
+ case 0x302:
+ val = gus->voice;
+ break;
+
+ case 0x303:
+ val = gus->global;
+ break;
+
+ case 0x304: /*Global low*/
switch (gus->global)
{
case 0x82: /*Start addr high*/
@@ -632,7 +718,7 @@ uint8_t readgus(uint16_t addr, void *p)
break;
}
break;
- case 0x345: /*Global high*/
+ case 0x305: /*Global high*/
switch (gus->global)
{
case 0x80: /*Voice control*/
@@ -681,16 +767,20 @@ uint8_t readgus(uint16_t addr, void *p)
break;
}
break;
- case 0x346: return 0xff;
- case 0x347: /*DRAM access*/
+ case 0x306: case 0x706: /*Revision level*/
+ val = 0xff;
+ break;
+ case 0x307: /*DRAM access*/
val=gus->ram[gus->addr];
gus->addr&=0xFFFFF;
- return val;
- case 0x349: return 0;
- case 0x746: /*Revision level*/
- return 0xff; /*Pre 3.7 - no mixer*/
+ if (gus->addr < gus->gus_end_ram)
+ val = gus->ram[gus->addr];
+ else
+ val = 0;
+ break;
+ case 0x309: return 0;
- case 0x24b:
+ case 0x20b:
switch (gus->reg_ctrl & 0x07)
{
case 1:
@@ -708,15 +798,15 @@ uint8_t readgus(uint16_t addr, void *p)
}
break;
- case 0x24c:
+ case 0x20c:
val = gus->sb_2xc;
if (gus->reg_ctrl & 0x20)
gus->sb_2xc &= 0x80;
break;
- case 0x24e:
+ case 0x20e:
return gus->sb_2xe;
- case 0x248: case 0x388:
+ case 0x208: case 0x388:
if (gus->tctrl & GUS_TIMER_CTRL_AUTO)
val = gus->sb_2xa;
else
@@ -727,14 +817,14 @@ uint8_t readgus(uint16_t addr, void *p)
}
break;
- case 0x249:
+ case 0x209:
gus->ad_status &= ~0x01;
nmi = 0;
case 0x389:
val = gus->ad_data;
break;
- case 0x24A:
+ case 0x20A:
val = gus->adcommand;
break;
@@ -756,9 +846,9 @@ void gus_poll_timer_1(void *p)
gus->ad_status |= 0x40;
if (gus->tctrl&4)
{
- if (gus->irq != -1)
- picint(1 << gus->irq);
- gus->ad_status |= 0x04;
+ if (gus->irq != 0)
+ picint(1 << gus->irq);
+ gus->ad_status |= 0x04;
gus->irqstatus |= 0x04;
}
}
@@ -767,10 +857,11 @@ void gus_poll_timer_1(void *p)
{
gus->irqnext=0;
gus->irqstatus|=0x80;
- if (gus->irq != -1)
- picint(1 << gus->irq);
+ if (gus->irq != 0)
+ picint(1 << gus->irq);
}
- gus_midi_update_int_status(gus);
+
+ gus_midi_update_int_status(gus);
}
void gus_poll_timer_2(void *p)
@@ -787,8 +878,8 @@ void gus_poll_timer_2(void *p)
gus->ad_status |= 0x20;
if (gus->tctrl&8)
{
- if (gus->irq != -1)
- picint(1 << gus->irq);
+ if (gus->irq != 0)
+ picint(1 << gus->irq);
gus->ad_status |= 0x02;
gus->irqstatus |= 0x08;
}
@@ -798,8 +889,8 @@ void gus_poll_timer_2(void *p)
{
gus->irqnext=0;
gus->irqstatus|=0x80;
- if (gus->irq != -1)
- picint(1 << gus->irq);
+ if (gus->irq != 0)
+ picint(1 << gus->irq);
}
}
@@ -998,16 +1089,57 @@ static void gus_get_buffer(int32_t *buffer, int len, void *p)
gus->pos = 0;
}
+static void gus_input_msg(void *p, uint8_t *msg)
+{
+ gus_t *gus = (gus_t *)p;
+ uint8_t i;
+
+ if (gus->sysex)
+ return;
+
+ if (gus->uart_in) {
+ gus->midi_status |= MIDI_INT_RECEIVE;
+
+ for (i=0;imidi_queue[gus->midi_w++] = msg[i];
+ gus->midi_w &= 63;
+ }
+
+ gus_midi_update_int_status(gus);
+ }
+}
+
+static int gus_input_sysex(void *p, uint8_t *buffer, uint32_t len, int abort)
+{
+ gus_t *gus = (gus_t *)p;
+ uint32_t i;
+
+ if (abort) {
+ gus->sysex = 0;
+ return 0;
+ }
+ gus->sysex = 1;
+ for (i=0;imidi_r == gus->midi_w)
+ return (len-i);
+ gus->midi_queue[gus->midi_w++] = buffer[i];
+ gus->midi_w &= 63;
+ }
+ gus->sysex = 0;
+ return 0;
+}
void *gus_init(const device_t *info)
{
int c;
double out = 1.0;
+ uint8_t gus_ram = device_get_config_int("gus_ram");
gus_t *gus = malloc(sizeof(gus_t));
memset(gus, 0, sizeof(gus_t));
- gus->ram = malloc(1 << 20);
- memset(gus->ram, 0, 1 << 20);
+ gus->gus_end_ram = 1 << (18 + gus_ram);
+ gus->ram = (uint8_t *)malloc(gus->gus_end_ram);
+ memset(gus->ram, 0x00, (gus->gus_end_ram));
for (c=0;c<32;c++)
{
@@ -1026,10 +1158,14 @@ void *gus_init(const device_t *info)
gus->samp_latch = (uint64_t)(TIMER_USEC * (1000000.0 / 44100.0));
gus->t1l = gus->t2l = 0xff;
+
+ gus->uart_out = 1;
+
+ gus->base = device_get_config_hex16("base");
- io_sethandler(0x0240, 0x0010, readgus, NULL, NULL, writegus, NULL, NULL, gus);
- io_sethandler(0x0340, 0x0010, readgus, NULL, NULL, writegus, NULL, NULL, gus);
- io_sethandler(0x0746, 0x0001, readgus, NULL, NULL, writegus, NULL, NULL, gus);
+ io_sethandler(gus->base, 0x0010, readgus, NULL, NULL, writegus, NULL, NULL, gus);
+ io_sethandler(0x0100+gus->base, 0x0010, readgus, NULL, NULL, writegus, NULL, NULL, gus);
+ io_sethandler(0x0506+gus->base, 0x0001, readgus, NULL, NULL, writegus, NULL, NULL, gus);
io_sethandler(0x0388, 0x0002, readgus, NULL, NULL, writegus, NULL, NULL, gus);
timer_add(&gus->samp_timer, gus_poll_wave, gus, 1);
timer_add(&gus->timer_1, gus_poll_timer_1, gus, 1);
@@ -1037,6 +1173,10 @@ void *gus_init(const device_t *info)
sound_add_handler(gus_get_buffer, gus);
+ input_msg = gus_input_msg;
+ input_sysex = gus_input_sysex;
+ midi_in_p = gus;
+
return gus;
}
@@ -1058,11 +1198,76 @@ void gus_speed_changed(void *p)
gus->samp_latch = (uint64_t)(TIMER_USEC * (1000000.0 / gusfreqs[gus->voices - 14]));
}
+static const device_config_t gus_config[] = {
+ {
+ "type", "GUS type", CONFIG_SELECTION, "", 0,
+ {
+ {
+ "Classic", GUS_CLASSIC
+ },
+#if 0
+ {
+ "MAX", GUS_MAX
+ },
+#endif
+ {
+ NULL
+ }
+ },
+ },
+ {
+ "base", "Address", CONFIG_HEX16, "", 0x220,
+ {
+ {
+ "210H", 0x210
+ },
+ {
+ "220H", 0x220
+ },
+ {
+ "230H", 0x230
+ },
+ {
+ "240H", 0x240
+ },
+ {
+ "250H", 0x250
+ },
+ {
+ "260H", 0x260
+ },
+ },
+ },
+ {
+ "gus_ram", "Onboard RAM", CONFIG_SELECTION, "", 0,
+ {
+ {
+ "256 KB", 0
+ },
+ {
+ "512 KB", 1
+ },
+ {
+ "1 MB", 2
+ },
+ {
+ NULL
+ }
+ }
+ },
+ {
+ "", "", -1
+ }
+};
+
const device_t gus_device =
{
"Gravis UltraSound",
- 0, 0,
- gus_init, gus_close, NULL, NULL,
- gus_speed_changed, NULL,
- NULL
+ DEVICE_ISA,
+ 0,
+ gus_init, gus_close, NULL,
+ NULL,
+ gus_speed_changed,
+ NULL,
+ gus_config
};
diff --git a/src/sound/snd_mpu401.c b/src/sound/snd_mpu401.c
index 15105268f..1ffc067f1 100644
--- a/src/sound/snd_mpu401.c
+++ b/src/sound/snd_mpu401.c
@@ -29,6 +29,7 @@
#define HAVE_STDARG_H
#include "../86box.h"
#include "../device.h"
+#include "../plat.h"
#include "../io.h"
#include "../machine/machine.h"
#include "../mca.h"
@@ -38,6 +39,8 @@
#include "snd_mpu401.h"
#include "midi.h"
+static uint32_t MPUClockBase[8] = {48,72,96,120,144,168,192};
+static uint8_t cth_data[16] = {0,0,0,0,1,0,0,0,1,0,1,0,1,1,1,0};
enum {
STATUS_OUTPUT_NOT_READY = 0x40,
@@ -48,7 +51,10 @@ enum {
int mpu401_standalone_enable = 0;
static void MPU401_WriteCommand(mpu_t *mpu, uint8_t val);
+static void MPU401_IntelligentOut(mpu_t *mpu, uint8_t track);
+static void MPU401_EOIHandler(void *priv);
static void MPU401_EOIHandlerDispatch(void *p);
+static void MPU401_NotesOff(mpu_t *mpu, int i);
#ifdef ENABLE_MPU401_LOG
@@ -72,38 +78,141 @@ mpu401_log(const char *fmt, ...)
static void
-QueueByte(mpu_t *mpu, uint8_t data)
+MPU401_ReCalcClock(mpu_t *mpu)
+{
+ int32_t maxtempo = 240, mintempo = 16;
+ int32_t freq;
+
+ if (mpu->clock.timebase >= 168)
+ maxtempo = 179;
+ if (mpu->clock.timebase == 144)
+ maxtempo = 208;
+ if (mpu->clock.timebase >= 120)
+ maxtempo = 8;
+
+ mpu->clock.freq = ((uint32_t)(mpu->clock.tempo * 2 * mpu->clock.tempo_rel)) >> 6;
+ mpu->clock.freq = mpu->clock.timebase * (mpu->clock.freq < (mintempo * 2) ? mintempo :
+ ((mpu->clock.freq / 2) < maxtempo ? (mpu->clock.freq / 2) : maxtempo));
+
+ if (mpu->state.sync_in) {
+ freq = (int32_t)((float)(mpu->clock.freq) * mpu->clock.freq_mod);
+ if ((freq > (mpu->clock.timebase * mintempo)) && (freq < (mpu->clock.timebase * maxtempo)))
+ mpu->clock.freq = freq;
+ }
+}
+
+
+static void
+MPU401_StartClock(mpu_t *mpu)
+{
+ if (mpu->clock.active)
+ return;
+ if (!(mpu->state.clock_to_host || mpu->state.playing || (mpu->state.rec == M_RECON)))
+ return;
+
+ mpu->clock.active = 1;
+ timer_set_delay_u64(&mpu->mpu401_event_callback, (MPU401_TIMECONSTANT / mpu->clock.freq) * 1000 * TIMER_USEC);
+}
+
+
+static void
+MPU401_StopClock(mpu_t *mpu)
+{
+ if (mpu->state.clock_to_host || mpu->state.playing || (mpu->state.rec == M_RECON))
+ return;
+ mpu->clock.active = 0;
+ timer_disable(&mpu->mpu401_event_callback);
+}
+
+
+static void
+MPU401_RunClock(mpu_t *mpu)
+{
+ if (!mpu->clock.active) {
+ timer_disable(&mpu->mpu401_event_callback);
+ return;
+ }
+ timer_set_delay_u64(&mpu->mpu401_event_callback, (MPU401_TIMECONSTANT / mpu->clock.freq) * 1000 * TIMER_USEC);
+ mpu401_log("Next event after %i us (time constant: %i)\n", (uint64_t) ((MPU401_TIMECONSTANT/mpu->clock.freq) * 1000 * TIMER_USEC), (int) MPU401_TIMECONSTANT);
+}
+
+
+static void
+MPU401_QueueByte(mpu_t *mpu, uint8_t data)
{
if (mpu->state.block_ack) {
mpu->state.block_ack = 0;
return;
}
-
- if ((mpu->queue_used == 0) && (mpu->mode == M_INTELLIGENT)) {
+
+ if (mpu->queue_used == 0) {
mpu->state.irq_pending = 1;
picint(1 << mpu->irq);
}
+
if (mpu->queue_used < MPU401_QUEUE) {
int pos = mpu->queue_used+mpu->queue_pos;
if (mpu->queue_pos >= MPU401_QUEUE)
mpu->queue_pos -= MPU401_QUEUE;
-
if (pos>=MPU401_QUEUE)
pos-=MPU401_QUEUE;
mpu->queue_used++;
mpu->queue[pos] = data;
- } else
- mpu401_log("MPU401:Data queue full\n");
+ }
}
static void
-ClrQueue(mpu_t *mpu)
+MPU401_RecQueueBuffer(mpu_t *mpu, uint8_t *buf, uint32_t len, int block)
{
- mpu->queue_used=0;
- mpu->queue_pos=0;
+ uint32_t cnt = 0;
+ int pos;
+
+ while (cnt < len) {
+ if (mpu->rec_queue_used < MPU401_INPUT_QUEUE) {
+ pos = mpu->rec_queue_used + mpu->rec_queue_pos;
+ if (pos >= MPU401_INPUT_QUEUE)
+ pos -= MPU401_INPUT_QUEUE;
+ mpu->rec_queue[pos] = buf[cnt];
+ mpu->rec_queue_used++;
+ if ((!mpu->state.sysex_in_finished) && (buf[cnt] == MSG_EOX)) {
+ /* finish sysex */
+ mpu->state.sysex_in_finished = 1;
+ break;
+ }
+ cnt++;
+ }
+ }
+
+ if (mpu->queue_used == 0) {
+ if (mpu->state.rec_copy || mpu->state.irq_pending) {
+ if (mpu->state.irq_pending) {
+ picintc(1 << mpu->irq);
+ mpu->state.irq_pending = 0;
+ }
+ return;
+ }
+ mpu->state.rec_copy = 1;
+ if (mpu->rec_queue_pos >= MPU401_INPUT_QUEUE)
+ mpu->rec_queue_pos -= MPU401_INPUT_QUEUE;
+ MPU401_QueueByte(mpu, mpu->rec_queue[mpu->rec_queue_pos]);
+ mpu->rec_queue_used--;
+ mpu->rec_queue_pos++;
+ }
+}
+
+
+static void
+MPU401_ClrQueue(mpu_t *mpu)
+{
+ mpu->queue_used = 0;
+ mpu->queue_pos = 0;
+ mpu->rec_queue_used = 0;
+ mpu->rec_queue_pos = 0;
+ mpu->state.sysex_in_finished = 1;
+ mpu->state.irq_pending = 0;
}
@@ -116,8 +225,10 @@ MPU401_Reset(mpu_t *mpu)
picintc(1 << mpu->irq);
mpu->state.irq_pending = 0;
}
-
+
mpu->mode = M_INTELLIGENT;
+ mpu->midi_thru = 0;
+ mpu->state.rec = M_RECOFF;
mpu->state.eoi_scheduled = 0;
mpu->state.wsd = 0;
mpu->state.wsm = 0;
@@ -129,27 +240,64 @@ MPU401_Reset(mpu_t *mpu)
mpu->state.cmask = 0xff;
mpu->state.amask = mpu->state.tmask = 0;
mpu->state.midi_mask = 0xffff;
- mpu->state.data_onoff = 0;
mpu->state.command_byte = 0;
mpu->state.block_ack = 0;
mpu->clock.tempo = mpu->clock.old_tempo = 100;
mpu->clock.timebase = mpu->clock.old_timebase = 120;
- mpu->clock.tempo_rel = mpu->clock.old_tempo_rel = 40;
+ mpu->clock.tempo_rel = mpu->clock.old_tempo_rel = 0x40;
+ mpu->clock.freq_mod = 1.0;
mpu->clock.tempo_grad = 0;
- mpu->clock.clock_to_host = 0;
- mpu->clock.cth_rate = 60;
- mpu->clock.cth_counter = 0;
+ MPU401_StopClock(mpu);
+ MPU401_ReCalcClock(mpu);
- ClrQueue(mpu);
+ for (i = 0; i < 4; i++)
+ mpu->clock.cth_rate[i] = 60;
+
+ mpu->clock.cth_counter = 0;
+ mpu->clock.midimetro = 12;
+ mpu->clock.metromeas = 8;
+ mpu->filter.rec_measure_end = 1;
+ mpu->filter.rt_out = 1;
+ mpu->filter.rt_affection = 1;
+ mpu->filter.allnotesoff_out = 1;
+ mpu->filter.all_thru = 1;
+ mpu->filter.midi_thru = 1;
+ mpu->filter.commonmsgs_thru = 1;
+
+ /* Reset channel reference and input tables. */
+ for (i = 0; i < 4; i++) {
+ mpu->chanref[i].on = 1;
+ mpu->chanref[i].chan = i;
+ mpu->ch_toref[i] = i;
+ }
+
+ for (i = 0; i < 16; i++) {
+ mpu->inputref[i].on = 1;
+ mpu->inputref[i].chan = i;
+ if (i > 3)
+ mpu->ch_toref[i] = 4; /* Dummy reftable. */
+ }
+
+ MPU401_ClrQueue(mpu);
+ mpu->state.data_onoff = -1;
mpu->state.req_mask = 0;
- mpu->condbuf.counter = 0;
+ mpu->condbuf.counter = 0;
mpu->condbuf.type = T_OVERFLOW;
- for (i=0;i<8;i++) {
+ for (i = 0; i < 8; i++) {
mpu->playbuf[i].type = T_OVERFLOW;
mpu->playbuf[i].counter = 0;
}
+
+ /* Clear MIDI buffers, terminate notes. */
+ midi_clear_buffer();
+
+ for (i = 0xb0; i <= 0xbf; i++) {
+ midi_raw_out_byte(i);
+ midi_raw_out_byte(0x7b);
+ midi_raw_out_byte(0);
+ }
}
@@ -163,6 +311,7 @@ MPU401_ResetDone(void *priv)
timer_disable(&mpu->mpu401_reset_callback);
mpu->state.reset = 0;
+
if (mpu->state.cmd_pending) {
MPU401_WriteCommand(mpu, mpu->state.cmd_pending - 1);
mpu->state.cmd_pending = 0;
@@ -173,162 +322,286 @@ MPU401_ResetDone(void *priv)
static void
MPU401_WriteCommand(mpu_t *mpu, uint8_t val)
{
- uint8_t i, was_uart;
+ uint8_t i, j, was_uart, recmsg[3];
if (mpu->state.reset)
mpu->state.cmd_pending = val + 1;
+ /* The only command recognized in UART mode is 0xFF: Reset and return to Intelligent mode. */
+ if ((val != 0xff) && (mpu->mode == M_UART))
+ return;
+
+ /* In Intelligent mode, UART-only variants of the MPU-401 only support commands 0x3F and 0xFF. */
if ((val != 0x3f) && (val != 0xff) && !mpu->intelligent)
return;
- if (val <= 0x2f) {
- switch (val&3) { /* MIDI stop, start, continue */
- case 1:
- midi_write(0xfc);
- break;
+ /* Hack: Enable midi through after the first mpu401 command is written. */
+ mpu->midi_thru = 1;
- case 2:
- midi_write(0xfa);
- break;
-
- case 3:
- midi_write(0xfb);
- break;
+ if (val <= 0x2f) { /* Sequencer state */
+ int send_prchg = 0;
+ if ((val & 0xf) < 0xc) {
+ switch (val & 3) { /* MIDI realtime messages */
+ case 1:
+ mpu->state.last_rtcmd = 0xfc;
+ if (mpu->filter.rt_out)
+ midi_raw_out_rt_byte(0xfc);
+ mpu->clock.meas_old = mpu->clock.measure_counter;
+ mpu->clock.cth_old = mpu->clock.cth_counter;
+ break;
+ case 2:
+ mpu->state.last_rtcmd = 0xfa;
+ if (mpu->filter.rt_out)
+ midi_raw_out_rt_byte(0xfb);
+ mpu->clock.measure_counter = mpu->clock.meas_old = 0;
+ mpu->clock.cth_counter = mpu->clock.cth_old = 0;
+ break;
+ case 3:
+ mpu->state.last_rtcmd = 0xfc;
+ if (mpu->filter.rt_out)
+ midi_raw_out_rt_byte(0xfa);
+ mpu->clock.measure_counter = mpu->clock.meas_old;
+ mpu->clock.cth_counter = mpu->clock.cth_old;
+ break;
+ }
+ switch (val & 0xc) { /* Playing */
+ case 0x4: /* Stop */
+ mpu->state.playing = 0;
+ MPU401_StopClock(mpu);
+ for (i = 0; i < 16; i++)
+ MPU401_NotesOff(mpu, i);
+ mpu->filter.prchg_mask = 0;
+ break;
+ case 0x8: /* Start */
+ mpu->state.playing = 1;
+ MPU401_StartClock(mpu);
+ break;
+ }
+ switch (val & 0x30) { /* Recording */
+ case 0: /* check if it waited for MIDI RT command */
+ if (((val & 3) < 2) || !mpu->filter.rt_affection || (mpu->state.rec != M_RECSTB))
+ break;
+ mpu->state.rec = M_RECON;
+ MPU401_StartClock(mpu);
+ if (mpu->filter.prchg_mask)
+ send_prchg = 1;
+ break;
+ case 0x10: /* Stop */
+ mpu->state.rec = M_RECOFF;
+ MPU401_StopClock(mpu);
+ MPU401_QueueByte(mpu, MSG_MPU_ACK);
+ MPU401_QueueByte(mpu, mpu->clock.rec_counter);
+ MPU401_QueueByte(mpu, MSG_MPU_END);
+ mpu->filter.prchg_mask = 0;
+ mpu->clock.rec_counter = 0;
+ return;
+ case 0x20: /* Start */
+ if (!(mpu->state.rec == M_RECON)) {
+ mpu->clock.rec_counter = 0;
+ mpu->state.rec = M_RECSTB;
+ }
+ if ((mpu->state.last_rtcmd == 0xfa) || (mpu->state.last_rtcmd == 0xfb)) {
+ mpu->clock.rec_counter = 0;
+ mpu->state.rec = M_RECON;
+ if (mpu->filter.prchg_mask)
+ send_prchg = 1;
+ MPU401_StartClock(mpu);
+ }
+ break;
+ }
}
-
- switch (val & 0xc) {
- case 0x4: /* Stop */
- mpu->state.playing = 0;
- timer_disable(&mpu->mpu401_event_callback);
-
- for (i = 0xb0; i < 0xbf; i++) {
- /* All notes off */
- midi_write(i);
- midi_write(0x7b);
- midi_write(0);
+ MPU401_QueueByte(mpu, MSG_MPU_ACK);
+ /* record counter hack: needed by Prism, but sent only on cmd 0x20/0x26 (or breaks Ballade) */
+ uint8_t rec_cnt = mpu->clock.rec_counter;
+ if (((val == 0x20) || (val == 0x26)) && (mpu->state.rec == M_RECON))
+ MPU401_RecQueueBuffer(mpu, &rec_cnt, 1, 0);
+
+ if (send_prchg) {
+ for (i = 0; i < 16; i++) {
+ if (mpu->filter.prchg_mask & (1 << i)) {
+ recmsg[0] = mpu->clock.rec_counter;
+ recmsg[1] = 0xc0 | i;
+ recmsg[2] = mpu->filter.prchg_buf[i];
+ MPU401_RecQueueBuffer(mpu, recmsg, 3, 0);
+ mpu->filter.prchg_mask &= ~(1 << i);
}
- break;
-
- case 0x8: /* Play */
- mpu->state.playing = 1;
- timer_set_delay_u64(&mpu->mpu401_event_callback, (MPU401_TIMECONSTANT / (mpu->clock.tempo*mpu->clock.timebase)) * 1000 * TIMER_USEC);
- ClrQueue(mpu);
- break;
+ }
}
- } else if ((val >= 0xa0) && (val <= 0xa7)) { /* Request play counter */
- if (mpu->state.cmask & (1 << (val&7)))
- QueueByte(mpu, mpu->playbuf[val&7].counter);
- } else if ((val >= 0xd0) && (val <= 0xd7)) { /* Send data */
- mpu->state.old_chan = mpu->state.channel;
- mpu->state.channel= val & 7;
+ } else if ((val >= 0xa0) && (val <= 0xa7)) /* Request play counter */
+ MPU401_QueueByte(mpu, mpu->playbuf[val & 7].counter);
+ else if ((val >= 0xd0) && (val <= 0xd7)) { /* Send data */
+ mpu->state.old_track = mpu->state.track;
+ mpu->state.track= val & 7;
mpu->state.wsd = 1;
mpu->state.wsm = 0;
mpu->state.wsd_start = 1;
+ } else if ((val < 0x80) && (val >= 0x40)) { /* Set reference table channel */
+ mpu->chanref[(val >> 4) - 4].on = 1;
+ mpu->chanref[(val >> 4) - 4].chan = val & 0x0f;
+ mpu->chanref[(val >> 4) - 4].trmask = 0;
+ for (i = 0; i < 4; i++)
+ mpu->chanref[(val >> 4) - 4].key[i] = 0;
+ for (i = 0; i < 16; i++) {
+ if (mpu->ch_toref[i] == ((val >> 4) - 4))
+ mpu->ch_toref[i] = 4;
+ }
+ mpu->ch_toref[val & 0x0f] = (val >> 4) - 4;
} else switch (val) {
- case 0xdf: /* Send system message */
- mpu->state.wsd = 0;
- mpu->state.wsm = 1;
- mpu->state.wsd_start = 1;
+ case 0x30: /* Configuration 0x30 - 0x39 */
+ mpu->filter.allnotesoff_out = 0;
break;
-
- case 0x8e: /* Conductor */
- mpu->state.cond_set = 0;
+ case 0x32:
+ mpu->filter.rt_out = 0;
break;
-
- case 0x8f:
- mpu->state.cond_set = 1;
+ case 0x33:
+ mpu->filter.all_thru = 0;
+ mpu->filter.commonmsgs_thru = 0;
+ mpu->filter.midi_thru = 0;
+ for (i = 0; i < 16; i++) {
+ mpu->inputref[i].on = 0;
+ for (j = 0; i < 4; j++)
+ mpu->inputref[i].key[j] = 0;
+ }
+ break;
+ case 0x34:
+ mpu->filter.timing_in_stop = 1;
+ break;
+ case 0x35:
+ mpu->filter.modemsgs_in = 1;
+ break;
+ case 0x37:
+ mpu->filter.sysex_thru = 1;
+ break;
+ case 0x38:
+ mpu->filter.commonmsgs_in = 1;
+ break;
+ case 0x39:
+ mpu->filter.rt_in = 1;
+ break;
+ case 0x3f: /* UART mode */
+ mpu401_log("MPU-401: Set UART mode %X\n",val);
+ MPU401_QueueByte(mpu, MSG_MPU_ACK);
+ mpu->mode = M_UART;
+ return;
+ case 0x80: /* Internal clock */
+ if (mpu->clock.active && mpu->state.sync_in) {
+ timer_set_delay_u64(&mpu->mpu401_event_callback, (MPU401_TIMECONSTANT / mpu->clock.freq) * 1000 * TIMER_USEC);
+ mpu->clock.freq_mod = 1.0;
+ }
+ mpu->state.sync_in = 0;
+ break;
+ case 0x81: /* Sync to tape signal */
+ case 0x82: /* Sync to MIDI */
+ mpu->clock.ticks_in = 0;
+ mpu->state.sync_in = 1;
+ break;
+ case 0x86: case 0x87: /* Bender */
+ mpu->filter.bender_in = !!(val & 1);
+ break;
+ case 0x88: case 0x89: /* MIDI through */
+ mpu->filter.midi_thru = !!(val & 1);
+ for (i = 0; i < 16; i++) {
+ mpu->inputref[i].on = mpu->filter.midi_thru;
+ if (!(val & 1)) {
+ for (j = 0; j < 4; j++)
+ mpu->inputref[i].key[j] = 0;
+ }
+ }
+ break;
+ case 0x8a: case 0x8b: /* Data in stop */
+ mpu->filter.data_in_stop = !!(val & 1);
+ break;
+ case 0x8c: case 0x8d: /* Send measure end */
+ mpu->filter.rec_measure_end = !!(val & 1);
+ break;
+ case 0x8e: case 0x8f: /* Conductor */
+ mpu->state.cond_set = !!(val & 1);
+ break;
+ case 0x90: case 0x91: /* Realtime affection */
+ mpu->filter.rt_affection = !!(val & 1);
break;
-
case 0x94: /* Clock to host */
- mpu->clock.clock_to_host = 0;
+ mpu->state.clock_to_host = 0;
+ MPU401_StopClock(mpu);
break;
-
case 0x95:
- mpu->clock.clock_to_host = 1;
+ mpu->state.clock_to_host = 1;
+ MPU401_StartClock(mpu);
break;
-
- case 0xc2: /* Internal timebase */
- mpu->clock.timebase = 48;
+ case 0x96: case 0x97: /* Sysex input allow */
+ mpu->filter.sysex_in = !!(val & 1);
+ if (val & 1)
+ mpu->filter.sysex_thru = 0;
break;
-
- case 0xc3:
- mpu->clock.timebase = 72;
+ case 0x98: case 0x99: case 0x9a: case 0x9b: /* Reference tables on/off */
+ case 0x9c: case 0x9d: case 0x9e: case 0x9f:
+ mpu->chanref[(val - 0x98) / 2].on = !!(val & 1);
break;
-
- case 0xc4:
- mpu->clock.timebase = 96;
- break;
-
- case 0xc5:
- mpu->clock.timebase = 120;
- break;
-
- case 0xc6:
- mpu->clock.timebase = 144;
- break;
-
- case 0xc7:
- mpu->clock.timebase = 168;
- break;
- case 0xc8:
- mpu->clock.timebase = 192;
- break;
-
- /* Commands with data byte */
- case 0xe0: case 0xe1: case 0xe2: case 0xe4: case 0xe6:
- case 0xe7: case 0xec: case 0xed: case 0xee: case 0xef:
- mpu->state.command_byte = val;
- break;
-
/* Commands 0xa# returning data */
case 0xab: /* Request and clear recording counter */
- QueueByte(mpu, MSG_MPU_ACK);
- QueueByte(mpu, 0);
+ MPU401_QueueByte(mpu, MSG_MPU_ACK);
+ MPU401_QueueByte(mpu, 0);
return;
-
case 0xac: /* Request version */
- QueueByte(mpu, MSG_MPU_ACK);
- QueueByte(mpu, MPU401_VERSION);
+ MPU401_QueueByte(mpu, MSG_MPU_ACK);
+ MPU401_QueueByte(mpu, MPU401_VERSION);
return;
-
case 0xad: /* Request revision */
- QueueByte(mpu, MSG_MPU_ACK);
- QueueByte(mpu, MPU401_REVISION);
+ MPU401_QueueByte(mpu, MSG_MPU_ACK);
+ MPU401_QueueByte(mpu, MPU401_REVISION);
return;
-
case 0xaf: /* Request tempo */
- QueueByte(mpu, MSG_MPU_ACK);
- QueueByte(mpu, mpu->clock.tempo);
+ MPU401_QueueByte(mpu, MSG_MPU_ACK);
+ MPU401_QueueByte(mpu, mpu->clock.tempo);
return;
-
case 0xb1: /* Reset relative tempo */
mpu->clock.old_tempo_rel = mpu->clock.tempo_rel;
- mpu->clock.tempo_rel = 40;
+ mpu->clock.tempo_rel = 0x40;
break;
-
- case 0xb9: /* Clear play map */
case 0xb8: /* Clear play counters */
- for (i = 0xb0; i < 0xbf; i++) {
- /* All notes off */
- midi_write(i);
- midi_write(0x7b);
- midi_write(0);
- }
+ mpu->state.last_rtcmd = 0;
for (i = 0; i < 8; i++) {
mpu->playbuf[i].counter = 0;
mpu->playbuf[i].type = T_OVERFLOW;
}
mpu->condbuf.counter = 0;
mpu->condbuf.type = T_OVERFLOW;
- if (!(mpu->state.conductor=mpu->state.cond_set))
- mpu->state.cond_req = 0;
mpu->state.amask = mpu->state.tmask;
- mpu->state.req_mask = 0;
- mpu->state.irq_pending = 1;
+ mpu->state.conductor = mpu->state.cond_set;
+ mpu->clock.cth_counter = mpu->clock.cth_old = 0;
+ mpu->clock.measure_counter = mpu->clock.meas_old = 0;
+ break;
+ case 0xb9: /* Clear play map */
+ for (i = 0; i < 16; i++)
+ MPU401_NotesOff(mpu, i);
+ for (i = 0; i < 8; i++) {
+ mpu->playbuf[i].counter = 0;
+ mpu->playbuf[i].type = T_OVERFLOW;
+ }
+ mpu->state.last_rtcmd = 0;
+ mpu->clock.cth_counter = mpu->clock.cth_old = 0;
+ mpu->clock.measure_counter = mpu->clock.meas_old = 0;
+ break;
+ case 0xba: /* Clear record counter */
+ mpu->clock.rec_counter = 0;
+ break;
+ case 0xc2: case 0xc3: case 0xc4: /* Internal timebase */
+ case 0xc5: case 0xc6: case 0xc7: case 0xc8:
+ mpu->clock.timebase = MPUClockBase[val-0xc2];
+ MPU401_ReCalcClock(mpu);
+ break;
+ case 0xdf: /* Send system message */
+ mpu->state.wsd = 0;
+ mpu->state.wsm = 1;
+ mpu->state.wsd_start = 1;
+ break;
+ /* Commands with data byte */
+ case 0xe0: case 0xe1: case 0xe2: case 0xe4: case 0xe6:
+ case 0xe7: case 0xec: case 0xed: case 0xee: case 0xef:
+ mpu->state.command_byte = val;
break;
-
case 0xff: /* Reset MPU-401 */
- mpu401_log("MPU-401:Reset %X\n",val);
+ mpu401_log("MPU-401: Reset %X\n", val);
timer_set_delay_u64(&mpu->mpu401_reset_callback, MPU401_RESETBUSY * 33LL * TIMER_USEC);
mpu->state.reset = 1;
was_uart = (mpu->mode == M_UART);
@@ -337,27 +610,22 @@ MPU401_WriteCommand(mpu_t *mpu, uint8_t val)
return; /* do not send ack in UART mode */
break;
- case 0x3f: /* UART mode */
- mpu401_log("MPU-401:Set UART mode %X\n",val);
- QueueByte(mpu, MSG_MPU_ACK);
- mpu->mode = M_UART;
- return;
-
/* default:
mpu401_log("MPU-401:Unhandled command %X",val); */
}
- QueueByte(mpu, MSG_MPU_ACK);
+ MPU401_QueueByte(mpu, MSG_MPU_ACK);
}
static void
MPU401_WriteData(mpu_t *mpu, uint8_t val)
{
- static int length, cnt, posd;
+ static int length, cnt;
+ uint8_t i;
if (mpu->mode == M_UART) {
- midi_write(val);
+ midi_raw_out_byte(val);
return;
}
@@ -369,39 +637,55 @@ MPU401_WriteData(mpu_t *mpu, uint8_t val)
switch (mpu->state.command_byte) { /* 0xe# command data */
case 0x00:
break;
-
case 0xe0: /* Set tempo */
mpu->state.command_byte = 0;
- mpu->clock.tempo = val;
+ if (mpu->clock.tempo < 8)
+ mpu->clock.tempo = 8;
+ else if (mpu->clock.tempo > 250)
+ mpu->clock.tempo = 250;
+ else
+ mpu->clock.tempo = val;
+ MPU401_ReCalcClock(mpu);
return;
-
case 0xe1: /* Set relative tempo */
mpu->state.command_byte = 0;
mpu->clock.old_tempo_rel = mpu->clock.tempo_rel;
mpu->clock.tempo_rel = val;
+ MPU401_ReCalcClock(mpu);
return;
-
+ case 0xe2: /* Set gradation for relative tempo */
+ mpu->clock.tempo_grad = val;
+ MPU401_ReCalcClock(mpu);
+ return;
+ case 0xe4: /* Set MIDI clocks for metronome ticks */
+ mpu->state.command_byte = 0;
+ mpu->clock.midimetro = val;
+ return;
+ case 0xe6: /* Set metronome ticks per measure */
+ mpu->state.command_byte = 0;
+ mpu->clock.metromeas = val;
+ return;
case 0xe7: /* Set internal clock to host interval */
mpu->state.command_byte = 0;
- mpu->clock.cth_rate = val >> 2;
+ if (!val)
+ val = 64;
+ for (i = 0; i < 4; i++)
+ mpu->clock.cth_rate[i] = (val >> 2) + cth_data[(val & 3) * 4 + i];
+ mpu->clock.cth_mode = 0;
return;
-
case 0xec: /* Set active track mask */
mpu->state.command_byte = 0;
mpu->state.tmask = val;
return;
-
case 0xed: /* Set play counter mask */
mpu->state.command_byte = 0;
mpu->state.cmask = val;
return;
-
case 0xee: /* Set 1-8 MIDI channel mask */
mpu->state.command_byte = 0;
mpu->state.midi_mask &= 0xff00;
mpu->state.midi_mask |= val;
return;
-
case 0xef: /* Set 9-16 MIDI channel mask */
mpu->state.command_byte = 0;
mpu->state.midi_mask &= 0x00ff;
@@ -413,53 +697,49 @@ MPU401_WriteData(mpu_t *mpu, uint8_t val)
return;
}
- if (mpu->state.wsd) {
+ if (mpu->state.wsd && !mpu->state.track_req && !mpu->state.cond_req) {
/* Directly send MIDI message */
if (mpu->state.wsd_start) {
mpu->state.wsd_start = 0;
cnt = 0;
switch (val & 0xf0) {
case 0xc0: case 0xd0:
- mpu->playbuf[mpu->state.channel].value[0] = val;
- length = 2;
+ length = mpu->playbuf[mpu->state.track].length = 2;
+ mpu->playbuf[mpu->state.track].type = T_MIDI_NORM;
break;
-
case 0x80: case 0x90: case 0xa0: case 0xb0:case 0xe0:
- mpu->playbuf[mpu->state.channel].value[0] = val;
- length = 3;
+ length = mpu->playbuf[mpu->state.track].length = 3;
+ mpu->playbuf[mpu->state.track].type = T_MIDI_NORM;
break;
case 0xf0:
/* mpu401_log("MPU-401:Illegal WSD byte\n"); */
mpu->state.wsd = 0;
- mpu->state.channel = mpu->state.old_chan;
+ mpu->state.track = mpu->state.old_track;
return;
default: /* MIDI with running status */
cnt++;
- midi_write(mpu->playbuf[mpu->state.channel].value[0]);
+ length = mpu->playbuf[mpu->state.track].length;
+ mpu->playbuf[mpu->state.track].type = T_MIDI_NORM;
}
}
if (cnt < length) {
- midi_write(val);
+ mpu->playbuf[mpu->state.track].value[cnt] = val;
cnt++;
}
if (cnt == length) {
+ MPU401_IntelligentOut(mpu, mpu->state.track);
mpu->state.wsd = 0;
- mpu->state.channel = mpu->state.old_chan;
+ mpu->state.track = mpu->state.old_track;
}
return;
}
- if (mpu->state.wsm) { /* Directly send system message */
- if (val == MSG_EOX) {
- midi_write(MSG_EOX);
- mpu->state.wsm = 0;
- return;
- }
+ if (mpu->state.wsm && !mpu->state.track_req && !mpu->state.cond_req) { /* Send system message */
if (mpu->state.wsd_start) {
mpu->state.wsd_start = 0;
cnt = 0;
@@ -481,12 +761,17 @@ MPU401_WriteData(mpu_t *mpu, uint8_t val)
break;
default:
- length = 0;
+ mpu->state.wsm = 0;
+ return;
}
+ } else if (val & 0x80) {
+ midi_raw_out_byte(MSG_EOX);
+ mpu->state.wsm = 0;
+ return;
}
if (!length || (cnt < length)) {
- midi_write(val);
+ midi_raw_out_byte(val);
cnt++;
}
@@ -501,37 +786,39 @@ MPU401_WriteData(mpu_t *mpu, uint8_t val)
switch (mpu->state.data_onoff) {
case -1:
return;
-
case 0: /* Timing byte */
- mpu->condbuf.vlength = 0;
+ mpu->condbuf.length = 0;
if (val < 0xf0)
mpu->state.data_onoff++;
else {
+ mpu->state.cond_req = 0;
mpu->state.data_onoff = -1;
MPU401_EOIHandlerDispatch(mpu);
- return;
+ break;
}
-
mpu->state.send_now = !val ? 1 : 0;
mpu->condbuf.counter = val;
break;
-
case 1: /* Command byte #1 */
mpu->condbuf.type = T_COMMAND;
- if ((val == 0xf8) || (val == 0xf9))
+ if ((val == 0xf8) || (val == 0xf9) || (val == 0xfc))
mpu->condbuf.type = T_OVERFLOW;
- mpu->condbuf.value[mpu->condbuf.vlength] = val;
- mpu->condbuf.vlength++;
- if ((val & 0xf0) != 0xe0)
- MPU401_EOIHandlerDispatch(mpu);
- else
+ mpu->condbuf.value[mpu->condbuf.length] = val;
+ mpu->condbuf.length++;
+ if ((val & 0xf0) != 0xe0) { /*no cmd data byte*/
+ MPU401_EOIHandler(mpu);
+ mpu->state.data_onoff = -1;
+ mpu->state.cond_req = 0;
+ } else
mpu->state.data_onoff++;
break;
case 2:/* Command byte #2 */
- mpu->condbuf.value[mpu->condbuf.vlength]=val;
- mpu->condbuf.vlength++;
- MPU401_EOIHandlerDispatch(mpu);
+ mpu->condbuf.value[mpu->condbuf.length]=val;
+ mpu->condbuf.length++;
+ MPU401_EOIHandler(mpu);
+ mpu->state.data_onoff = -1;
+ mpu->state.cond_req = 0;
break;
}
return;
@@ -540,86 +827,126 @@ MPU401_WriteData(mpu_t *mpu, uint8_t val)
switch (mpu->state.data_onoff) {
/* Data */
case -1:
- return;
-
+ break;
case 0: /* Timing byte */
if (val < 0xf0)
- mpu->state.data_onoff = 1;
+ mpu->state.data_onoff++;
else {
mpu->state.data_onoff = -1;
MPU401_EOIHandlerDispatch(mpu);
+ mpu->state.track_req = 0;
return;
}
mpu->state.send_now = !val ? 1 : 0;
- mpu->playbuf[mpu->state.channel].counter = val;
+ mpu->playbuf[mpu->state.track].counter = val;
break;
-
case 1: /* MIDI */
- mpu->playbuf[mpu->state.channel].vlength++;
- posd=mpu->playbuf[mpu->state.channel].vlength;
- if (posd == 1) switch (val&0xf0) {
+ cnt = 0;
+ mpu->state.data_onoff++;
+ switch (val & 0xf0) {
+ case 0xc0: case 0xd0: /* MIDI Message */
+ length = mpu->playbuf[mpu->state.track].length = 2;
+ mpu->playbuf[mpu->state.track].type = T_MIDI_NORM;
+ break;
+ case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0:
+ length = mpu->playbuf[mpu->state.track].length = 3;
+ mpu->playbuf[mpu->state.track].type = T_MIDI_NORM;
+ break;
case 0xf0: /* System message or mark */
+ mpu->playbuf[mpu->state.track].sys_val = val;
if (val > 0xf7) {
- mpu->playbuf[mpu->state.channel].type = T_MARK;
- mpu->playbuf[mpu->state.channel].sys_val = val;
+ mpu->playbuf[mpu->state.track].type = T_MARK;
+ if (val == 0xf9)
+ mpu->clock.measure_counter = 0;
} else {
/* mpu401_log("MPU-401:Illegal message"); */
- mpu->playbuf[mpu->state.channel].type = T_MIDI_SYS;
- mpu->playbuf[mpu->state.channel].sys_val = val;
+ mpu->playbuf[mpu->state.track].type = T_OVERFLOW;
}
- length = 1;
- break;
-
- case 0xc0: case 0xd0: /* MIDI Message */
- mpu->playbuf[mpu->state.channel].type = T_MIDI_NORM;
- length = mpu->playbuf[mpu->state.channel].length = 2;
- break;
-
- case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0:
- mpu->playbuf[mpu->state.channel].type = T_MIDI_NORM;
- length = mpu->playbuf[mpu->state.channel].length = 3;
- break;
-
- default: /* MIDI data with running status */
- posd++;
- mpu->playbuf[mpu->state.channel].vlength++;
- mpu->playbuf[mpu->state.channel].type = T_MIDI_NORM;
- length = mpu->playbuf[mpu->state.channel].length;
+ mpu->state.data_onoff = -1;
+ MPU401_EOIHandler(mpu);
+ mpu->state.track_req = 0;
+ return;
+ default: /* MIDI with running status */
+ cnt++;
+ length = mpu->playbuf[mpu->state.track].length;
+ mpu->playbuf[mpu->state.track].type = T_MIDI_NORM;
break;
}
-
- if (!((posd == 1) && (val >= 0xf0)))
- mpu->playbuf[mpu->state.channel].value[posd-1] = val;
- if (posd == length)
- MPU401_EOIHandlerDispatch(mpu);
+ break;
+ case 2:
+ if (cnt < length) {
+ mpu->playbuf[mpu->state.track].value[cnt] = val;
+ cnt++;
+ }
+ if (cnt == length) {
+ mpu->state.data_onoff = -1;
+ mpu->state.track_req = 0;
+ MPU401_EOIHandler(mpu);
+ }
+ break;
}
+
+ return;
}
static void
-MPU401_IntelligentOut(mpu_t *mpu, uint8_t chan)
+MPU401_IntelligentOut(mpu_t *mpu, uint8_t track)
{
- uint8_t val;
+ uint8_t chan, chrefnum, key, msg;
+ int send, retrigger;
uint8_t i;
- switch (mpu->playbuf[chan].type) {
+ switch (mpu->playbuf[track].type) {
case T_OVERFLOW:
break;
case T_MARK:
- val=mpu->playbuf[chan].sys_val;
- if (val==0xfc) {
- midi_write(val);
- mpu->state.amask &= ~(1<state.req_mask &= ~(1<playbuf[track].sys_val == 0xfc) {
+ midi_raw_out_rt_byte(mpu->playbuf[track].sys_val);
+ mpu->state.amask&=~(1<