mirror of
https://github.com/polpo/picogus.git
synced 2026-10-25 21:40:16 +00:00
MPU-401 support - UART working, intelligent not so much
This commit is contained in:
1 parent
53a4f0d780
commit
714964fdb4
8 files changed
+1189
-11
No files matched your search
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"board": {
|
||||
"active_layer": 44,
|
||||
"active_layer": 0,
|
||||
"active_layer_preset": "",
|
||||
"auto_track_width": true,
|
||||
"hidden_nets": [],
|
||||
|
||||
+15
-3
@@ -20,8 +20,9 @@ set(CMAKE_BUILD_TYPE "Release")
|
||||
set(PICO_COPY_TO_RAM 1)
|
||||
#set(SKIP_PICO_MALLOC 1)
|
||||
|
||||
set(PROJECT_TYPE "GUS")
|
||||
# set(PROJECT_TYPE "GUS")
|
||||
# set(PROJECT_TYPE "OPL")
|
||||
set(PROJECT_TYPE "MPU")
|
||||
|
||||
# Initialise the Raspberry Pi Pico SDK
|
||||
pico_sdk_init()
|
||||
@@ -44,7 +45,7 @@ target_compile_definitions(picogus PRIVATE
|
||||
)
|
||||
|
||||
if(PROJECT_TYPE STREQUAL "GUS")
|
||||
target_sources(picogus PRIVATE gusplay.cpp pico_pic.cpp)
|
||||
target_sources(picogus PRIVATE gusplay.cpp pico_pic.c)
|
||||
target_compile_definitions(picogus PRIVATE
|
||||
SOUND_GUS=1
|
||||
PSRAM=1
|
||||
@@ -68,12 +69,23 @@ if(PROJECT_TYPE STREQUAL "OPL")
|
||||
add_subdirectory(opl)
|
||||
endif()
|
||||
|
||||
if(PROJECT_TYPE STREQUAL "MPU")
|
||||
target_compile_definitions(picogus PRIVATE
|
||||
SOUND_MPU=1
|
||||
USE_ALARM=1
|
||||
)
|
||||
target_sources(picogus PRIVATE
|
||||
pico_pic.c
|
||||
mpu401/mpu401.c
|
||||
mpu401/midi.c
|
||||
)
|
||||
endif()
|
||||
|
||||
pico_set_program_name(picogus "picogus")
|
||||
pico_set_program_version(picogus "0.1")
|
||||
|
||||
pico_enable_stdio_uart(picogus 0)
|
||||
pico_enable_stdio_usb(picogus 0)
|
||||
pico_enable_stdio_usb(picogus 1)
|
||||
pico_enable_stdio_semihosting(picogus 0)
|
||||
|
||||
pico_generate_pio_header(picogus ${CMAKE_CURRENT_LIST_DIR}/isa_io.pio)
|
||||
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2012 The DOSBox Team
|
||||
* Copyright (C) 2013-2014 bjt, elianda
|
||||
* Copyright (C) 2015 ab0tj
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef EXPORT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// #define NULL 0 /* SOFTMPU */
|
||||
#define RTCFREQ 4000 /* SOFTMPU: RTC interrupt frequency */
|
||||
|
||||
typedef unsigned char Bit8u;
|
||||
typedef unsigned int Bitu;
|
||||
|
||||
// typedef enum bool {false = 0,true = 1} bool;
|
||||
typedef enum EventID {MPU_EVENT,RESET_DONE,EOI_HANDLER,NUM_EVENTS} EventID;
|
||||
|
||||
/* Interface functions */
|
||||
void MPU401_Init();
|
||||
void MPU401_WriteCommand(Bit8u val);
|
||||
Bit8u MPU401_ReadData(void);
|
||||
void MPU401_WriteData(Bit8u val);
|
||||
Bit8u QueueUsed();
|
||||
void send_midi_byte();
|
||||
void output_to_uart(Bit8u val);
|
||||
void wait_for_uart();
|
||||
Bit8u uart_tx_status();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#define EXPORT_H
|
||||
#endif
|
||||
Executable
+321
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2013 The DOSBox Team
|
||||
* Copyright (C) 2013-2014 bjt, elianda
|
||||
* Copyright (C) 2015 ab0tj
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* --------------------------------------------
|
||||
* HardMPU by ab0tj - Hardware MPU-401 Emulator
|
||||
* --------------------------------------------
|
||||
*
|
||||
* Based on midi.c from SoftMPU by bjt which was
|
||||
* based on original midi.c from DOSBox
|
||||
*
|
||||
*/
|
||||
|
||||
/* SOFTMPU: Moved exported functions & types to header */
|
||||
#include "export.h"
|
||||
|
||||
/* HardMPU includes */
|
||||
/*
|
||||
#include "config.h"
|
||||
#include <util/delay.h>
|
||||
*/
|
||||
#include "hardware/timer.h"
|
||||
|
||||
/* SOFTMPU: Additional defines, typedefs etc. for C */
|
||||
typedef unsigned long Bit32u;
|
||||
typedef int Bits;
|
||||
|
||||
#define SYSEX_SIZE 1024 // sysex buffer for delay calculation
|
||||
|
||||
/* RAWBUF: This is the buffer for outgoing MIDI data. The larger the buffer,
|
||||
the less likely it is to overrun when SysEx delay is enabled and large SysEx
|
||||
transfers are occurring. */
|
||||
#define RAWBUF 14336
|
||||
|
||||
typedef struct ring_buffer {
|
||||
unsigned char buffer[RAWBUF];
|
||||
unsigned int head;
|
||||
unsigned int tail;
|
||||
} ring_buffer;
|
||||
|
||||
static ring_buffer midi_out_buff = { {0}, 0, 0 };
|
||||
|
||||
/* SOFTMPU: Note tracking for RA-50 */
|
||||
#define MAX_TRACKED_CHANNELS 16
|
||||
#define MAX_TRACKED_NOTES 8
|
||||
|
||||
static const char* MIDI_welcome_msg = "\xf0\x41\x10\x16\x12\x20\x00\x00 *** PicoGUS *** \x0a\xf7"; // message to show on MT-32 display
|
||||
|
||||
static Bit8u MIDI_note_off[3] = { 0x80,0x00,0x00 }; /* SOFTMPU */
|
||||
|
||||
static const Bit8u 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
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x30
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x40
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x50
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x60
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x70
|
||||
|
||||
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0x80
|
||||
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0x90
|
||||
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xa0
|
||||
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xb0
|
||||
|
||||
2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, // 0xc0
|
||||
2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, // 0xd0
|
||||
|
||||
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xe0
|
||||
|
||||
0,2,3,2, 0,0,1,0, 1,0,1,1, 1,0,1,0 // 0xf0
|
||||
};
|
||||
|
||||
/* SOFTMPU: Note tracking for RA-50 */
|
||||
typedef struct {
|
||||
Bit8u used;
|
||||
Bit8u next;
|
||||
Bit8u notes[MAX_TRACKED_NOTES];
|
||||
} channel;
|
||||
|
||||
channel tracked_channels[MAX_TRACKED_CHANNELS];
|
||||
|
||||
static struct {
|
||||
Bit8u status;
|
||||
Bit8u cmd_len;
|
||||
Bit8u cmd_pos;
|
||||
Bit8u cmd_buf[8];
|
||||
Bit8u rt_buf[8];
|
||||
struct {
|
||||
Bit8u buf[SYSEX_SIZE];
|
||||
Bitu used;
|
||||
Bit8u usedbufs;
|
||||
//Bitu delay;
|
||||
bool delay;
|
||||
Bit8u status;
|
||||
} sysex;
|
||||
bool fakeallnotesoff;
|
||||
bool available;
|
||||
/*MidiHandler * handler;*/ /* SOFTMPU */
|
||||
} midi;
|
||||
|
||||
/* SOFTMPU: Sysex delay is decremented from PIC_Update */
|
||||
volatile Bitu MIDI_sysex_delaytime;
|
||||
|
||||
static void PlayMsg(Bit8u* msg, Bitu len)
|
||||
{
|
||||
// despite the name of this function, we're just going to buffer this message to send later.
|
||||
for (Bitu i = 0; i < len; i++) {
|
||||
unsigned int next = (unsigned int)(midi_out_buff.head + 1) % RAWBUF;
|
||||
if (next != midi_out_buff.tail) {
|
||||
midi_out_buff.buffer[midi_out_buff.head] = msg[i];
|
||||
midi_out_buff.head = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void send_midi_byte_now(Bit8u byte) {
|
||||
wait_for_uart();
|
||||
output_to_uart(byte);
|
||||
}
|
||||
|
||||
/* SOFTMPU: Fake "All Notes Off" for Roland RA-50 */
|
||||
static void FakeAllNotesOff(Bit8u chan)
|
||||
{
|
||||
Bit8u note;
|
||||
channel* pChan;
|
||||
|
||||
MIDI_note_off[0] &= 0xf0;
|
||||
MIDI_note_off[0] |= chan;
|
||||
|
||||
pChan=&tracked_channels[chan];
|
||||
|
||||
for (note=0;note<pChan->used;note++)
|
||||
{
|
||||
MIDI_note_off[1]=pChan->notes[note];
|
||||
PlayMsg(MIDI_note_off,3);
|
||||
}
|
||||
|
||||
pChan->used=0;
|
||||
pChan->next=0;
|
||||
}
|
||||
|
||||
void MIDI_RawOutByte(Bit8u data) {
|
||||
channel* pChan; /* SOFTMPU */
|
||||
|
||||
/* Test for a realtime MIDI message */
|
||||
if (data>=0xf8) {
|
||||
midi.rt_buf[0]=data;
|
||||
PlayMsg(midi.rt_buf,1);
|
||||
return;
|
||||
}
|
||||
if (data&0x80) {
|
||||
midi.status=data;
|
||||
midi.cmd_pos=0;
|
||||
midi.cmd_len=MIDI_evt_len[data];
|
||||
}
|
||||
if (midi.cmd_len) {
|
||||
midi.cmd_buf[midi.cmd_pos++]=data;
|
||||
if (midi.cmd_pos >= midi.cmd_len) {
|
||||
/*if (CaptureState & CAPTURE_MIDI) {
|
||||
CAPTURE_AddMidi(false, midi.cmd_len, midi.cmd_buf);
|
||||
}*/ /* SOFTMPU */
|
||||
|
||||
if (midi.fakeallnotesoff)
|
||||
{
|
||||
/* SOFTMPU: Test for "Note On" */
|
||||
if ((midi.status&0xf0)==0x90)
|
||||
{
|
||||
if (midi.cmd_buf[2]>0)
|
||||
{
|
||||
pChan=&tracked_channels[midi.status&0x0f];
|
||||
pChan->notes[pChan->next++]=midi.cmd_buf[1];
|
||||
if (pChan->next==MAX_TRACKED_NOTES) pChan->next=0;
|
||||
if (pChan->used<MAX_TRACKED_NOTES) pChan->used++;
|
||||
}
|
||||
|
||||
PlayMsg(midi.cmd_buf,midi.cmd_len);
|
||||
}
|
||||
/* SOFTMPU: Test for "All Notes Off" */
|
||||
else if (((midi.status&0xf0)==0xb0) &&
|
||||
(midi.cmd_buf[1]>=0x7b) &&
|
||||
(midi.cmd_buf[1]<=0x7f))
|
||||
{
|
||||
FakeAllNotesOff(midi.status&0x0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayMsg(midi.cmd_buf,midi.cmd_len);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayMsg(midi.cmd_buf,midi.cmd_len);
|
||||
}
|
||||
midi.cmd_pos=1; //Use Running status
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
midi.rt_buf[0] = data;
|
||||
PlayMsg(midi.rt_buf,1);
|
||||
}
|
||||
}
|
||||
|
||||
void send_midi_byte() {
|
||||
if (midi_out_buff.head == midi_out_buff.tail) return; // nothing to send
|
||||
if (uart_tx_status()) return; // can't send yet
|
||||
//loop_until_bit_is_set(UCSR0A, UDRE0);
|
||||
if (midi.sysex.delay && MIDI_sysex_delaytime) { // still waiting for sysex delay
|
||||
busy_wait_us(320);
|
||||
return;
|
||||
}
|
||||
Bit8u data = midi_out_buff.buffer[midi_out_buff.tail];
|
||||
midi_out_buff.tail = (unsigned int)(midi_out_buff.tail + 1) % RAWBUF; // increment tail, wrap to 0 if we're at the end
|
||||
|
||||
if (midi.sysex.status==0xf0) {
|
||||
if (!(data&0x80)) {
|
||||
if (midi.sysex.used==SYSEX_SIZE) {
|
||||
midi.sysex.used = 0;
|
||||
midi.sysex.usedbufs++;
|
||||
}
|
||||
|
||||
output_to_uart(data);
|
||||
midi.sysex.buf[midi.sysex.used++] = data;
|
||||
return;
|
||||
} else {
|
||||
output_to_uart(0xf7);
|
||||
midi.sysex.buf[midi.sysex.used++] = 0xf7;
|
||||
midi.sysex.status = 0xf7;
|
||||
/*LOG(LOG_ALL,LOG_NORMAL)("Play sysex; address:%02X %02X %02X, length:%4d, delay:%3d", midi.sysex.buf[5], midi.sysex.buf[6], midi.sysex.buf[7], midi.sysex.used, midi.sysex.delay);*/
|
||||
if (midi.sysex.delay) {
|
||||
if (midi.sysex.usedbufs == 0 && midi.sysex.buf[5] == 0x7F) {
|
||||
/*midi.sysex.delay = 290;*/ /* SOFTMPU */ // All Parameters reset
|
||||
MIDI_sysex_delaytime = 290*(RTCFREQ/1000);
|
||||
} else if (midi.sysex.usedbufs == 0 && midi.sysex.buf[5] == 0x10 && midi.sysex.buf[6] == 0x00 && midi.sysex.buf[7] == 0x04) {
|
||||
/*midi.sysex.delay = 145;*/ /* SOFTMPU */ // Viking Child
|
||||
MIDI_sysex_delaytime = 145*(RTCFREQ/1000);
|
||||
} else if (midi.sysex.usedbufs == 0 && midi.sysex.buf[5] == 0x10 && midi.sysex.buf[6] == 0x00 && midi.sysex.buf[7] == 0x01) {
|
||||
/*midi.sysex.delay = 30;*/ /* SOFTMPU */ // Dark Sun 1
|
||||
MIDI_sysex_delaytime = 30*(RTCFREQ/1000);
|
||||
} else MIDI_sysex_delaytime = ((((midi.sysex.usedbufs*SYSEX_SIZE)+midi.sysex.used)/2)+2)*(RTCFREQ/1000); /*(Bitu)(((float)(midi.sysex.used) * 1.25f) * 1000.0f / 3125.0f) + 2;
|
||||
midi.sysex.start = GetTicks();*/ /* SOFTMPU */
|
||||
}
|
||||
return;
|
||||
/*LOG(LOG_ALL,LOG_NORMAL)("Sysex message size %d",midi.sysex.used);*/ /* SOFTMPU */
|
||||
/*if (CaptureState & CAPTURE_MIDI) {
|
||||
CAPTURE_AddMidi( true, midi.sysex.used-1, &midi.sysex.buf[1]);
|
||||
}*/ /* SOFTMPU */
|
||||
}
|
||||
}
|
||||
if (data&0x80) {
|
||||
midi.sysex.status=data;
|
||||
if (midi.sysex.status==0xf0) {
|
||||
output_to_uart(0xf0);
|
||||
midi.sysex.used=1;
|
||||
midi.sysex.buf[0]=0xf0;
|
||||
midi.sysex.usedbufs=0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
output_to_uart(data); // not sysex
|
||||
}
|
||||
|
||||
bool MIDI_Available(void) {
|
||||
return midi.available;
|
||||
}
|
||||
|
||||
/* SOFTMPU: Initialization */
|
||||
void MIDI_Init(bool delaysysex,bool fakeallnotesoff){
|
||||
Bit8u i; /* SOFTMPU */
|
||||
MIDI_sysex_delaytime = 0; /* SOFTMPU */
|
||||
midi.sysex.delay = delaysysex;
|
||||
|
||||
midi.status=0x00;
|
||||
midi.sysex.status=0x00;
|
||||
midi.cmd_pos=0;
|
||||
midi.cmd_len=0;
|
||||
midi.fakeallnotesoff=fakeallnotesoff>0;
|
||||
midi.available=true;
|
||||
|
||||
midi_out_buff.head = midi_out_buff.tail = 0;
|
||||
|
||||
/* SOFTMPU: Display welcome message on MT-32 */
|
||||
for (i=0;i<30;i++)
|
||||
{
|
||||
send_midi_byte_now(MIDI_welcome_msg[i]);
|
||||
}
|
||||
|
||||
/* HardMPU: Turn off any stuck notes */
|
||||
for (i=0xb0;i<0xc0;i++)
|
||||
{
|
||||
send_midi_byte_now(i);
|
||||
send_midi_byte_now(0x7b);
|
||||
send_midi_byte_now(0);
|
||||
}
|
||||
|
||||
/* SOFTMPU: Init note tracking */
|
||||
for (i=0;i<MAX_TRACKED_CHANNELS;i++)
|
||||
{
|
||||
tracked_channels[i].used=0;
|
||||
tracked_channels[i].next=0;
|
||||
}
|
||||
}
|
||||
Executable
+724
@@ -0,0 +1,724 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2012 The DOSBox Team
|
||||
* Copyright (C) 2013-2014 bjt, elianda
|
||||
* Copyright (C) 2015 ab0tj
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* --------------------------------------------
|
||||
* HardMPU by ab0tj - Hardware MPU-401 Emulator
|
||||
* --------------------------------------------
|
||||
*
|
||||
* Based on mpu401.c from SoftMPU by bjt which was
|
||||
* based on original mpu401.c from DOSBox
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hardware/uart.h"
|
||||
|
||||
/* SOFTMPU: Moved exported functions & types to header */
|
||||
#include "export.h"
|
||||
|
||||
/* HardMPU includes */
|
||||
/*
|
||||
#include <avr/sfr_defs.h>
|
||||
#include <avr/io.h>
|
||||
*/
|
||||
|
||||
/* SOFTMPU: Additional defines, typedefs etc. for C */
|
||||
typedef uint16_t Bit16u;
|
||||
typedef int32_t Bits;
|
||||
typedef int8_t Bit8s;
|
||||
|
||||
/*typedef void (*PIC_EventHandler)(Bitu val);*/ /* SOFTMPU */
|
||||
|
||||
#include "../pico_pic.h"
|
||||
/* SOFTMPU: Stubbed functions */
|
||||
#if 0
|
||||
void PIC_Init(void); /* SOFTMPU */
|
||||
void PIC_AddEvent(EventID event,Bitu delay);
|
||||
void PIC_RemoveEvents(EventID event);
|
||||
#endif
|
||||
|
||||
void MIDI_Init(bool delaysysex,bool fakeallnotesoff);
|
||||
void MIDI_RawOutByte(Bit8u data);
|
||||
bool MIDI_Available(void);
|
||||
|
||||
static uint32_t MPU401_Event(Bitu val);
|
||||
static uint32_t MPU401_ResetDone(Bitu val);
|
||||
static uint32_t MPU401_EOIHandler(Bitu val);
|
||||
static void MPU401_Reset(void);
|
||||
static void MPU401_EOIHandlerDispatch(void);
|
||||
|
||||
#define MPU401_VERSION 0x15
|
||||
#define MPU401_REVISION 0x01
|
||||
#define MPU401_QUEUE 32
|
||||
//#define MPU401_TIMECONSTANT 58000 /* SOFTMPU: Was 60000 */
|
||||
#define MPU401_TIMECONSTANT 60000000
|
||||
#define MPU401_RESETBUSY 27*(RTCFREQ/1000) /* SOFTMPU */
|
||||
|
||||
enum MpuMode { M_UART,M_INTELLIGENT };
|
||||
typedef enum MpuMode MpuMode; /* SOFTMPU */
|
||||
enum MpuDataType {T_OVERFLOW,T_MARK,T_MIDI_SYS,T_MIDI_NORM,T_COMMAND};
|
||||
typedef enum MpuDataType MpuDataType; /* SOFTMPU */
|
||||
|
||||
/* Messages sent to MPU-401 from host */
|
||||
#define MSG_EOX 0xf7
|
||||
#define MSG_OVERFLOW 0xf8
|
||||
#define MSG_MARK 0xfc
|
||||
|
||||
/* Messages sent to host from MPU-401 */
|
||||
#define MSG_MPU_OVERFLOW 0xf8
|
||||
#define MSG_MPU_COMMAND_REQ 0xf9
|
||||
#define MSG_MPU_END 0xfc
|
||||
#define MSG_MPU_CLOCK 0xfd
|
||||
#define MSG_MPU_ACK 0xfe
|
||||
|
||||
/* HardMPU: MPU config bits */
|
||||
#define CONFIG_SYSEXDELAY (mpu.config & 0x80)
|
||||
#define CONFIG_FAKEALLNOTESOFF (mpu.config & 0x40)
|
||||
#define CONFIG_VERSIONFIX (mpu.config & 0x20)
|
||||
#define CONFIG_MIDIPORT (mpu.config & 0x10)
|
||||
|
||||
static struct {
|
||||
bool intelligent;
|
||||
Bit8u config;
|
||||
MpuMode mode;
|
||||
Bit8u queue[MPU401_QUEUE];
|
||||
Bit8u queue_pos,queue_used;
|
||||
struct track {
|
||||
Bits counter;
|
||||
Bit8u value[8],sys_val;
|
||||
Bit8u vlength,length;
|
||||
MpuDataType type;
|
||||
} playbuf[8],condbuf;
|
||||
struct {
|
||||
bool conductor,cond_req,cond_set, block_ack;
|
||||
bool playing,reset;
|
||||
bool wsd,wsm,wsd_start;
|
||||
bool run_irq,irq_pending;
|
||||
bool send_now;
|
||||
bool eoi_scheduled;
|
||||
Bit8s data_onoff;
|
||||
Bitu command_byte,cmd_pending;
|
||||
Bit8u tmask,cmask,amask;
|
||||
Bit16u midi_mask;
|
||||
Bit16u req_mask;
|
||||
Bit8u channel,old_chan;
|
||||
} state;
|
||||
struct {
|
||||
Bit8u timebase,old_timebase;
|
||||
Bit8u tempo,old_tempo;
|
||||
Bit8u tempo_rel,old_tempo_rel;
|
||||
Bit8u tempo_grad;
|
||||
Bit8u cth_rate,cth_counter;
|
||||
bool clock_to_host,cth_active;
|
||||
} clock;
|
||||
} mpu;
|
||||
|
||||
Bit8u QueueUsed() {
|
||||
return mpu.queue_used;
|
||||
}
|
||||
|
||||
static void QueueByte(Bit8u data) {
|
||||
if (mpu.state.block_ack) {mpu.state.block_ack=false;return;}
|
||||
if (mpu.queue_used==0 && mpu.intelligent) {
|
||||
mpu.state.irq_pending=true;
|
||||
PIC_ActivateIRQ();
|
||||
}
|
||||
if (mpu.queue_used<MPU401_QUEUE) {
|
||||
Bit8u 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 LOG(LOG_MISC,LOG_NORMAL)("MPU401:Data queue full");*/ /* SOFTMPU */
|
||||
}
|
||||
|
||||
static void ClrQueue(void) {
|
||||
mpu.queue_used=0;
|
||||
mpu.queue_pos=0;
|
||||
}
|
||||
|
||||
void MPU401_WriteCommand(Bit8u val) { /* SOFTMPU */
|
||||
Bit8u i; /* SOFTMPU */
|
||||
if (mpu.state.reset) {
|
||||
if (mpu.state.cmd_pending || (val!=0x3f && val!=0xff)) {
|
||||
mpu.state.cmd_pending=val+1;
|
||||
return;
|
||||
}
|
||||
PIC_RemoveEvents(MPU401_ResetDone);
|
||||
mpu.state.reset=false;
|
||||
}
|
||||
if (val<=0x2f) {
|
||||
switch (val&3) { /* MIDI stop, start, continue */
|
||||
case 1: {MIDI_RawOutByte(0xfc);break;}
|
||||
case 2: {MIDI_RawOutByte(0xfa);break;}
|
||||
case 3: {MIDI_RawOutByte(0xfb);break;}
|
||||
}
|
||||
/*if (val&0x20) LOG(LOG_MISC,LOG_ERROR)("MPU-401:Unhandled Recording Command %x",val);*/ /* SOFTMPU */
|
||||
switch (val&0xc) {
|
||||
case 0x4: /* Stop */
|
||||
PIC_RemoveEvents(MPU401_Event);
|
||||
mpu.state.playing=false;
|
||||
for (i=0xb0;i<0xbf;i++) { /* All notes off */
|
||||
MIDI_RawOutByte(i);
|
||||
MIDI_RawOutByte(0x7b);
|
||||
MIDI_RawOutByte(0);
|
||||
}
|
||||
break;
|
||||
case 0x8: /* Play */
|
||||
/*LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Intelligent mode playback started");*/ /* SOFTMPU */
|
||||
mpu.state.playing=true;
|
||||
PIC_RemoveEvents(MPU_EVENT);
|
||||
puts("start intell");
|
||||
PIC_AddEvent(MPU401_Event,MPU401_TIMECONSTANT/((mpu.clock.tempo*mpu.clock.timebase*mpu.clock.tempo_rel)/0x40), 2);
|
||||
ClrQueue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (val>=0xa0 && val<=0xa7) { /* Request play counter */
|
||||
if (mpu.state.cmask&(1<<(val&7))) QueueByte((Bit8u)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;
|
||||
mpu.state.wsd=true;
|
||||
mpu.state.wsm=false;
|
||||
mpu.state.wsd_start=true;
|
||||
}
|
||||
else
|
||||
switch (val) {
|
||||
case 0xdf: /* Send system message */
|
||||
mpu.state.wsd=false;
|
||||
mpu.state.wsm=true;
|
||||
mpu.state.wsd_start=true;
|
||||
break;
|
||||
case 0x8e: /* Conductor */
|
||||
mpu.state.cond_set=false;
|
||||
break;
|
||||
case 0x8f:
|
||||
mpu.state.cond_set=true;
|
||||
break;
|
||||
case 0x94: /* Clock to host */
|
||||
mpu.clock.clock_to_host=false;
|
||||
break;
|
||||
case 0x95:
|
||||
mpu.clock.clock_to_host=true;
|
||||
break;
|
||||
case 0xc2: /* Internal timebase */
|
||||
mpu.clock.timebase=48/(RTCFREQ/1000); /* SOFTMPU */
|
||||
break;
|
||||
case 0xc3:
|
||||
mpu.clock.timebase=72/(RTCFREQ/1000); /* SOFTMPU */
|
||||
break;
|
||||
case 0xc4:
|
||||
mpu.clock.timebase=96/(RTCFREQ/1000); /* SOFTMPU */
|
||||
break;
|
||||
case 0xc5:
|
||||
mpu.clock.timebase=120/(RTCFREQ/1000); /* SOFTMPU */
|
||||
break;
|
||||
case 0xc6:
|
||||
mpu.clock.timebase=144/(RTCFREQ/1000); /* SOFTMPU */
|
||||
break;
|
||||
case 0xc7:
|
||||
mpu.clock.timebase=168/(RTCFREQ/1000); /* SOFTMPU */
|
||||
break;
|
||||
case 0xc8:
|
||||
mpu.clock.timebase=192/(RTCFREQ/1000); /* SOFTMPU */
|
||||
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(MSG_MPU_ACK);
|
||||
QueueByte(0);
|
||||
return;
|
||||
case 0xac: /* Request version */
|
||||
if (true)
|
||||
{
|
||||
/* SOFTMPU: Fix missing music in Gateway by reversing version and ACK */
|
||||
QueueByte(MPU401_VERSION);
|
||||
QueueByte(MSG_MPU_ACK);
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueByte(MSG_MPU_ACK);
|
||||
QueueByte(MPU401_VERSION);
|
||||
}
|
||||
return;
|
||||
case 0xad: /* Request revision */
|
||||
QueueByte(MSG_MPU_ACK);
|
||||
QueueByte(MPU401_REVISION);
|
||||
return;
|
||||
case 0xae: /* HardMPU: Request config */
|
||||
QueueByte(MSG_MPU_ACK);
|
||||
QueueByte(mpu.config);
|
||||
return;
|
||||
case 0xaf: /* Request tempo */
|
||||
QueueByte(MSG_MPU_ACK);
|
||||
QueueByte(mpu.clock.tempo);
|
||||
return;
|
||||
case 0xb1: /* Reset relative tempo */
|
||||
mpu.clock.tempo_rel=40;
|
||||
break;
|
||||
case 0xb9: /* Clear play map */
|
||||
case 0xb8: /* Clear play counters */
|
||||
for (i=0xb0;i<0xbf;i++) { /* All notes off */
|
||||
MIDI_RawOutByte((Bit8u)i);
|
||||
MIDI_RawOutByte(0x7b);
|
||||
MIDI_RawOutByte(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=true;
|
||||
break;
|
||||
case 0xfe: /* HardMPU: Reset with config byte */
|
||||
mpu.state.command_byte=val;
|
||||
break;
|
||||
case 0xff: /* Reset MPU-401 */
|
||||
/*LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Reset %X",val);*/ /* SOFTMPU */
|
||||
PIC_AddEvent(MPU401_ResetDone, MPU401_RESETBUSY, 0);
|
||||
mpu.state.reset=true;
|
||||
MPU401_Reset();
|
||||
if (mpu.mode==M_UART) return;//do not send ack in UART mode
|
||||
break;
|
||||
case 0x3f: /* UART mode */
|
||||
/*LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Set UART mode %X",val);*/ /* SOFTMPU */
|
||||
mpu.mode=M_UART;
|
||||
break;
|
||||
default:
|
||||
/*LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Unhandled command %X",val);*/
|
||||
break;
|
||||
}
|
||||
QueueByte(MSG_MPU_ACK);
|
||||
}
|
||||
|
||||
Bit8u MPU401_ReadData(void) { /* SOFTMPU */
|
||||
Bit8u ret=MSG_MPU_ACK; // HardMPU: we shouldn't be running this function if the queue is empty.
|
||||
//if (mpu.queue_used) {
|
||||
if (mpu.queue_pos>=MPU401_QUEUE) mpu.queue_pos-=MPU401_QUEUE;
|
||||
ret=mpu.queue[mpu.queue_pos];
|
||||
mpu.queue_pos++;mpu.queue_used--;
|
||||
//}
|
||||
if (!mpu.intelligent) return ret;
|
||||
|
||||
if (mpu.queue_used == 0) PIC_DeActivateIRQ();
|
||||
|
||||
if (ret>=0xf0 && ret<=0xf7) { /* MIDI data request */
|
||||
mpu.state.channel=ret&7;
|
||||
mpu.state.data_onoff=0;
|
||||
mpu.state.cond_req=false;
|
||||
}
|
||||
if (ret==MSG_MPU_COMMAND_REQ) {
|
||||
mpu.state.data_onoff=0;
|
||||
mpu.state.cond_req=true;
|
||||
if (mpu.condbuf.type!=T_OVERFLOW) {
|
||||
mpu.state.block_ack=true;
|
||||
MPU401_WriteCommand(mpu.condbuf.value[0]);
|
||||
if (mpu.state.command_byte) MPU401_WriteData(mpu.condbuf.value[1]);
|
||||
}
|
||||
mpu.condbuf.type=T_OVERFLOW;
|
||||
}
|
||||
if (ret==MSG_MPU_END || ret==MSG_MPU_CLOCK || ret==MSG_MPU_ACK) {
|
||||
mpu.state.data_onoff=-1;
|
||||
MPU401_EOIHandlerDispatch();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MPU401_WriteData(Bit8u val) { /* SOFTMPU */
|
||||
static Bit8u length,cnt,posd; /* SOFTMPU */
|
||||
if (mpu.mode==M_UART) { MIDI_RawOutByte(val); return; }
|
||||
switch (mpu.state.command_byte) { /* 0xe# command data */
|
||||
case 0x00:
|
||||
break;
|
||||
case 0xe0: /* Set tempo */
|
||||
mpu.state.command_byte=0;
|
||||
mpu.clock.tempo=val;
|
||||
return;
|
||||
case 0xe1: /* Set relative tempo */
|
||||
mpu.state.command_byte=0;
|
||||
/*if (val!=0x40) //default value
|
||||
LOG(LOG_MISC,LOG_ERROR)("MPU-401:Relative tempo change not implemented");*/ /* SOFTMPU */
|
||||
return;
|
||||
case 0xe7: /* Set internal clock to host interval */
|
||||
mpu.state.command_byte=0;
|
||||
mpu.clock.cth_rate=val>>2;
|
||||
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;
|
||||
mpu.state.midi_mask|=((Bit16u)val)<<8;
|
||||
return;
|
||||
//case 0xe2: /* Set graduation for relative tempo */
|
||||
//case 0xe4: /* Set metronome */
|
||||
//case 0xe6: /* Set metronome measure length */
|
||||
case 0xfe: /* Config byte */
|
||||
mpu.state.command_byte=0;
|
||||
mpu.config=val;
|
||||
MPU401_Init();
|
||||
QueueByte(MSG_MPU_ACK);
|
||||
return;
|
||||
default:
|
||||
mpu.state.command_byte=0;
|
||||
return;
|
||||
}
|
||||
if (mpu.state.wsd) { /* 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;
|
||||
break;
|
||||
case 0x80:case 0x90:case 0xa0:case 0xb0:case 0xe0:
|
||||
mpu.playbuf[mpu.state.channel].value[0]=val;
|
||||
length=3;
|
||||
break;
|
||||
case 0xf0:
|
||||
/*LOG(LOG_MISC,LOG_ERROR)("MPU-401:Illegal WSD byte");*/ /* SOFTMPU */
|
||||
mpu.state.wsd=0;
|
||||
mpu.state.channel=mpu.state.old_chan;
|
||||
return;
|
||||
default: /* MIDI with running status */
|
||||
cnt++;
|
||||
MIDI_RawOutByte(mpu.playbuf[mpu.state.channel].value[0]);
|
||||
}
|
||||
}
|
||||
if (cnt<length) {MIDI_RawOutByte(val);cnt++;}
|
||||
if (cnt==length) {
|
||||
mpu.state.wsd=0;
|
||||
mpu.state.channel=mpu.state.old_chan;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (mpu.state.wsm) { /* Directly send system message */
|
||||
if (val==MSG_EOX) {MIDI_RawOutByte(MSG_EOX);mpu.state.wsm=0;return;}
|
||||
if (mpu.state.wsd_start) {
|
||||
mpu.state.wsd_start=0;
|
||||
cnt=0;
|
||||
switch (val) {
|
||||
case 0xf2:{ length=3; break;}
|
||||
case 0xf3:{ length=2; break;}
|
||||
case 0xf6:{ length=1; break;}
|
||||
case 0xf0:{ length=0; break;}
|
||||
default:
|
||||
length=0;
|
||||
}
|
||||
}
|
||||
if (!length || cnt<length) {MIDI_RawOutByte(val);cnt++;}
|
||||
if (cnt==length) mpu.state.wsm=0;
|
||||
return;
|
||||
}
|
||||
if (mpu.state.cond_req) { /* Command */
|
||||
switch (mpu.state.data_onoff) {
|
||||
case -1:
|
||||
return;
|
||||
case 0: /* Timing byte */
|
||||
mpu.condbuf.vlength=0;
|
||||
if (val<0xf0) mpu.state.data_onoff++;
|
||||
else {
|
||||
mpu.state.data_onoff=-1;
|
||||
MPU401_EOIHandlerDispatch();
|
||||
return;
|
||||
}
|
||||
if (val==0) mpu.state.send_now=true;
|
||||
else mpu.state.send_now=false;
|
||||
mpu.condbuf.counter=val;
|
||||
break;
|
||||
case 1: /* Command byte #1 */
|
||||
mpu.condbuf.type=T_COMMAND;
|
||||
if (val==0xf8 || val==0xf9) mpu.condbuf.type=T_OVERFLOW;
|
||||
mpu.condbuf.value[mpu.condbuf.vlength]=val;
|
||||
mpu.condbuf.vlength++;
|
||||
if ((val&0xf0)!=0xe0) MPU401_EOIHandlerDispatch();
|
||||
else mpu.state.data_onoff++;
|
||||
break;
|
||||
case 2:/* Command byte #2 */
|
||||
mpu.condbuf.value[mpu.condbuf.vlength]=val;
|
||||
mpu.condbuf.vlength++;
|
||||
MPU401_EOIHandlerDispatch();
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (mpu.state.data_onoff) { /* Data */
|
||||
case -1:
|
||||
return;
|
||||
case 0: /* Timing byte */
|
||||
if (val<0xf0) mpu.state.data_onoff=1;
|
||||
else {
|
||||
mpu.state.data_onoff=-1;
|
||||
MPU401_EOIHandlerDispatch();
|
||||
return;
|
||||
}
|
||||
if (val==0) mpu.state.send_now=true;
|
||||
else mpu.state.send_now=false;
|
||||
mpu.playbuf[mpu.state.channel].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) {
|
||||
case 0xf0: /* System message or mark */
|
||||
if (val>0xf7) {
|
||||
mpu.playbuf[mpu.state.channel].type=T_MARK;
|
||||
mpu.playbuf[mpu.state.channel].sys_val=val;
|
||||
length=1;
|
||||
} else {
|
||||
/*LOG(LOG_MISC,LOG_ERROR)("MPU-401:Illegal message");*/ /* SOFTMPU */
|
||||
mpu.playbuf[mpu.state.channel].type=T_MIDI_SYS;
|
||||
mpu.playbuf[mpu.state.channel].sys_val=val;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!(posd==1 && val>=0xf0)) mpu.playbuf[mpu.state.channel].value[posd-1]=val;
|
||||
if (posd==length) MPU401_EOIHandlerDispatch();
|
||||
}
|
||||
}
|
||||
|
||||
static void MPU401_IntelligentOut(Bit8u chan) {
|
||||
Bit8u val, i; /* SOFTMPU */
|
||||
switch (mpu.playbuf[chan].type) {
|
||||
case T_OVERFLOW:
|
||||
break;
|
||||
case T_MARK:
|
||||
val=mpu.playbuf[chan].sys_val;
|
||||
if (val==0xfc) {
|
||||
MIDI_RawOutByte(val);
|
||||
mpu.state.amask&=~(1<<chan);
|
||||
mpu.state.req_mask&=~(1<<chan);
|
||||
}
|
||||
break;
|
||||
case T_MIDI_NORM:
|
||||
for (i=0;i<mpu.playbuf[chan].vlength;i++)
|
||||
MIDI_RawOutByte(mpu.playbuf[chan].value[i]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateTrack(Bit8u chan) {
|
||||
MPU401_IntelligentOut(chan);
|
||||
if (mpu.state.amask&(1<<chan)) {
|
||||
mpu.playbuf[chan].vlength=0;
|
||||
mpu.playbuf[chan].type=T_OVERFLOW;
|
||||
mpu.playbuf[chan].counter=0xf0;
|
||||
mpu.state.req_mask|=(1<<chan);
|
||||
} else {
|
||||
if (mpu.state.amask==0 && !mpu.state.conductor) mpu.state.req_mask|=(1<<12);
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateConductor(void) {
|
||||
if (mpu.condbuf.value[0]==0xfc) {
|
||||
mpu.condbuf.value[0]=0;
|
||||
mpu.state.conductor=false;
|
||||
mpu.state.req_mask&=~(1<<9);
|
||||
if (mpu.state.amask==0) mpu.state.req_mask|=(1<<12);
|
||||
return;
|
||||
}
|
||||
mpu.condbuf.vlength=0;
|
||||
mpu.condbuf.counter=0xf0;
|
||||
mpu.state.req_mask|=(1<<9);
|
||||
}
|
||||
|
||||
uint32_t MPU401_Event(Bitu val) {
|
||||
/* SOFTMPU */
|
||||
putc('.');
|
||||
Bit8u i;
|
||||
Bitu new_time;
|
||||
if (mpu.mode==M_UART) return 0;
|
||||
if (mpu.state.irq_pending) goto next_event;
|
||||
for (i=0;i<8;i++) { /* Decrease counters */
|
||||
if (mpu.state.amask&(1<<i)) {
|
||||
mpu.playbuf[i].counter--;
|
||||
if (mpu.playbuf[i].counter<=0) UpdateTrack(i);
|
||||
}
|
||||
}
|
||||
if (mpu.state.conductor) {
|
||||
mpu.condbuf.counter--;
|
||||
if (mpu.condbuf.counter<=0) UpdateConductor();
|
||||
}
|
||||
if (mpu.clock.clock_to_host) {
|
||||
mpu.clock.cth_counter++;
|
||||
if (mpu.clock.cth_counter >= mpu.clock.cth_rate) {
|
||||
mpu.clock.cth_counter=0;
|
||||
mpu.state.req_mask|=(1<<13);
|
||||
}
|
||||
}
|
||||
if (!mpu.state.irq_pending && mpu.state.req_mask) MPU401_EOIHandler(0);
|
||||
next_event:
|
||||
PIC_RemoveEvents(MPU_EVENT);
|
||||
if ((new_time=((mpu.clock.tempo*mpu.clock.timebase*mpu.clock.tempo_rel)/0x40))==0) return 0;
|
||||
return (Bitu)MPU401_TIMECONSTANT/new_time;
|
||||
}
|
||||
|
||||
static void MPU401_EOIHandlerDispatch(void) {
|
||||
if (mpu.state.send_now) {
|
||||
mpu.state.eoi_scheduled=true;
|
||||
PIC_AddEvent(MPU401_EOIHandler,1, 1); // Possible a bit longer
|
||||
}
|
||||
else if (!mpu.state.eoi_scheduled) MPU401_EOIHandler(0);
|
||||
}
|
||||
|
||||
//Updates counters and requests new data on "End of Input"
|
||||
uint32_t MPU401_EOIHandler(Bitu val) {
|
||||
Bit8u i=0; /* SOFTMPU */
|
||||
mpu.state.eoi_scheduled=false;
|
||||
if (mpu.state.send_now) {
|
||||
mpu.state.send_now=false;
|
||||
if (mpu.state.cond_req) UpdateConductor();
|
||||
else UpdateTrack(mpu.state.channel);
|
||||
}
|
||||
mpu.state.irq_pending=false;
|
||||
if (!mpu.state.playing || !mpu.state.req_mask) return 0;
|
||||
do {
|
||||
if (mpu.state.req_mask&(1<<i)) {
|
||||
QueueByte(0xf0+i);
|
||||
mpu.state.req_mask&=~(1<<i);
|
||||
break;
|
||||
}
|
||||
} while ((i++)<16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t MPU401_ResetDone(Bitu val) { /* SOFTMPU */
|
||||
mpu.state.reset=false;
|
||||
if (mpu.state.cmd_pending) {
|
||||
MPU401_WriteCommand(mpu.state.cmd_pending-1);
|
||||
mpu.state.cmd_pending=0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void MPU401_Reset(void) {
|
||||
Bit8u i; /* SOFTMPU */
|
||||
|
||||
PIC_DeActivateIRQ();
|
||||
mpu.mode=(mpu.intelligent ? M_INTELLIGENT : M_UART);
|
||||
PIC_RemoveEvents(MPU401_EOIHandler);
|
||||
mpu.state.eoi_scheduled=false;
|
||||
mpu.state.wsd=false;
|
||||
mpu.state.wsm=false;
|
||||
mpu.state.conductor=false;
|
||||
mpu.state.cond_req=false;
|
||||
mpu.state.cond_set=false;
|
||||
mpu.state.playing=false;
|
||||
mpu.state.run_irq=false;
|
||||
mpu.state.irq_pending=false;
|
||||
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=false;
|
||||
mpu.clock.tempo=mpu.clock.old_tempo=100;
|
||||
mpu.clock.timebase=mpu.clock.old_timebase=120/(RTCFREQ/1000);
|
||||
mpu.clock.tempo_rel=mpu.clock.old_tempo_rel=40;
|
||||
mpu.clock.tempo_grad=0;
|
||||
mpu.clock.clock_to_host=false;
|
||||
mpu.clock.cth_rate=60;
|
||||
mpu.clock.cth_counter=0;
|
||||
ClrQueue();
|
||||
mpu.state.req_mask=0;
|
||||
mpu.condbuf.counter=0;
|
||||
mpu.condbuf.type=T_OVERFLOW;
|
||||
for (i=0;i<8;i++) {mpu.playbuf[i].type=T_OVERFLOW;mpu.playbuf[i].counter=0;}
|
||||
}
|
||||
|
||||
/* HardMPU: Initialisation */
|
||||
void MPU401_Init()
|
||||
{
|
||||
/* Initalise PIC code */
|
||||
PIC_Init();
|
||||
|
||||
/* Initialise MIDI handler */
|
||||
MIDI_Init(CONFIG_SYSEXDELAY,CONFIG_FAKEALLNOTESOFF);
|
||||
if (!MIDI_Available()) return;
|
||||
|
||||
mpu.queue_used=0;
|
||||
mpu.queue_pos=0;
|
||||
mpu.mode=M_UART;
|
||||
|
||||
mpu.intelligent=true; /* Default is on */
|
||||
if (!mpu.intelligent) return;
|
||||
|
||||
/* SOFTMPU: Moved IRQ 9 handler init to asm */
|
||||
MPU401_Reset();
|
||||
}
|
||||
|
||||
/* HardMPU: Output a byte to the physical UART */
|
||||
void output_to_uart(Bit8u val)
|
||||
{
|
||||
uart_write_blocking(uart0, &val, 1);
|
||||
}
|
||||
|
||||
/* HardMPU: Wait for UART TX buffer to be empty */
|
||||
void wait_for_uart()
|
||||
{
|
||||
uart_tx_wait_blocking(uart0);
|
||||
}
|
||||
|
||||
/* HardMPU: Check UART TX status, returns 0 for ready */
|
||||
Bit8u uart_tx_status()
|
||||
{
|
||||
return uart_is_writable(uart0) ? 0 : 1;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "pico/time.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <stdio.h>
|
||||
|
||||
// A fixed pool of only 3 events for now. gus-x only has 3 different timer events - two timers and one DMA
|
||||
PIC_TimerEvent timerEvents[3];
|
||||
@@ -64,13 +64,13 @@ void PIC_Init() {
|
||||
irq_set_priority(TIMER_IRQ_2, PICO_HIGHEST_IRQ_PRIORITY);
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
timerEvents[i].alarm_id = 0;
|
||||
timerEvents[i].handler = nullptr;
|
||||
timerEvents[i].handler = 0;
|
||||
}
|
||||
#else
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
timerEvents[i].active = false;
|
||||
timerEvents[i].deadline = UINT32_MAX;
|
||||
timerEvents[i].handler = nullptr;
|
||||
timerEvents[i].handler = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
+10
-2
@@ -13,9 +13,13 @@
|
||||
|
||||
#define IRQ_PIN 21 // TODO don't spread around pin definitions like this
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef uint32_t (* PIC_EventHandler)(Bitu val);
|
||||
|
||||
struct PIC_TimerEvent {
|
||||
typedef struct {
|
||||
PIC_EventHandler handler;
|
||||
Bitu value;
|
||||
#ifdef USE_ALARM
|
||||
@@ -24,7 +28,7 @@ struct PIC_TimerEvent {
|
||||
uint32_t deadline;
|
||||
bool active;
|
||||
#endif
|
||||
};
|
||||
} PIC_TimerEvent;
|
||||
|
||||
extern PIC_TimerEvent timerEvents[3];
|
||||
|
||||
@@ -74,3 +78,7 @@ __force_inline void PIC_HandleEvents() {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+63
-2
@@ -6,8 +6,10 @@
|
||||
// #include "hardware/vreg.h"
|
||||
#include "hardware/regs/vreg_and_chip_reset.h"
|
||||
|
||||
|
||||
#ifndef SOUND_MPU
|
||||
#include "stdio_async_uart.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
#define UART_TX_PIN (0)
|
||||
#define UART_RX_PIN (1)
|
||||
@@ -49,6 +51,10 @@ constexpr uint16_t GUS_PORT_TEST = GUS_PORT >> 4 | 0x10;
|
||||
void play_gus(void);
|
||||
#endif
|
||||
|
||||
#ifdef SOUND_MPU
|
||||
#include "mpu401/export.h"
|
||||
#endif
|
||||
|
||||
constexpr uint LED_PIN = PICO_DEFAULT_LED_PIN;
|
||||
|
||||
static uint iow_sm;
|
||||
@@ -110,6 +116,28 @@ __force_inline void handle_iow(void) {
|
||||
pio_sm_put(pio0, iow_sm, 0x0u);
|
||||
}
|
||||
#endif // SOUND_OPL
|
||||
#ifdef SOUND_MPU
|
||||
switch (port) {
|
||||
case 0x330:
|
||||
pio_sm_put(pio0, iow_sm, 0xffffffffu);
|
||||
value = iow_read & 0xFF;
|
||||
// printf("MPU IOW: port: %x value: %x\n", port, value);
|
||||
MPU401_WriteData(value);
|
||||
// Tell PIO that we are done
|
||||
pio_sm_put(pio0, iow_sm, 0x0u);
|
||||
break;
|
||||
case 0x331:
|
||||
pio_sm_put(pio0, iow_sm, 0xffffffffu);
|
||||
MPU401_WriteCommand(iow_read & 0xFF);
|
||||
// printf("MPU IOW: port: %x value: %x\n", port, value);
|
||||
__dsb();
|
||||
// Tell PIO that we are done
|
||||
pio_sm_put(pio0, iow_sm, 0x0u);
|
||||
break;
|
||||
default:
|
||||
pio_sm_put(pio0, iow_sm, 0x0u);
|
||||
}
|
||||
#endif // SOUND_OPL
|
||||
}
|
||||
|
||||
__force_inline void handle_ior(void) {
|
||||
@@ -138,6 +166,19 @@ __force_inline void handle_ior(void) {
|
||||
pio_sm_put(pio0, ior_sm, 0x0u);
|
||||
}
|
||||
#endif
|
||||
#ifdef SOUND_MPU
|
||||
if (port == 0x331) {
|
||||
// Tell PIO to wait for data
|
||||
pio_sm_put(pio0, ior_sm, 0xffffffffu);
|
||||
value = MPU401_ReadData();
|
||||
// printf("MPU IOR: port: %x value: %x\n", port, value);
|
||||
// OR with 0x00ffff00 is required to set pindirs in the PIO
|
||||
pio_sm_put(pio0, ior_sm, 0x00ffff00u | value);
|
||||
} else {
|
||||
// Reset PIO
|
||||
pio_sm_put(pio0, ior_sm, 0x0u);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_IRQ
|
||||
@@ -175,7 +216,11 @@ int main()
|
||||
// vreg_set_voltage(VREG_VOLTAGE_1_20);
|
||||
|
||||
// stdio_init_all();
|
||||
#ifndef SOUND_MPU
|
||||
stdio_async_uart_init_full(UART_ID, BAUD_RATE, UART_TX_PIN, UART_RX_PIN);
|
||||
#else
|
||||
stdio_init_all();
|
||||
#endif
|
||||
|
||||
puts("PicoGUS booting!");
|
||||
|
||||
@@ -191,10 +236,21 @@ int main()
|
||||
gpio_init(LED_PIN);
|
||||
gpio_set_dir(LED_PIN, GPIO_OUT);
|
||||
|
||||
alarm_pool_init_default();
|
||||
// alarm_pool_init_default();
|
||||
gpio_init(IRQ_PIN);
|
||||
gpio_set_dir(IRQ_PIN, GPIO_OUT);
|
||||
|
||||
#ifdef SOUND_MPU
|
||||
puts("Initing MIDI UART...");
|
||||
uart_init(UART_ID, 31250);
|
||||
uart_set_translate_crlf(UART_ID, false);
|
||||
uart_set_format(UART_ID, 8, 1, UART_PARITY_NONE);
|
||||
gpio_set_drive_strength(UART_TX_PIN, GPIO_DRIVE_STRENGTH_12MA);
|
||||
gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);
|
||||
busy_wait_ms(1000);
|
||||
MPU401_Init();
|
||||
#endif
|
||||
|
||||
#ifdef PSRAM
|
||||
puts("Initing PSRAM...");
|
||||
psram_spi = psram_init();
|
||||
@@ -324,8 +380,10 @@ int main()
|
||||
irq_set_enabled(PIO0_IRQ_1, true);
|
||||
#endif
|
||||
|
||||
#ifdef SOUND_GUS
|
||||
puts("Initing ISA DMA PIO...");
|
||||
dma_config = DMA_init(pio);
|
||||
#endif
|
||||
|
||||
gpio_xor_mask(1u << LED_PIN);
|
||||
|
||||
@@ -347,6 +405,9 @@ int main()
|
||||
#endif
|
||||
#ifndef USE_ALARM
|
||||
PIC_HandleEvents();
|
||||
#endif
|
||||
#ifdef SOUND_MPU
|
||||
send_midi_byte(); // see if we need to send a byte
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user