2017-05-30 03:38:38 +02:00
/*
2022-02-22 20:28:56 -05:00
* 86 Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus .
2017-05-30 03:38:38 +02:00
*
2022-02-22 20:28:56 -05:00
* This file is part of the 86 Box distribution .
2017-05-30 03:38:38 +02:00
*
2022-02-22 20:28:56 -05:00
* Roland MPU - 401 emulation .
2017-05-30 03:38:38 +02:00
*
2020-03-25 00:46:02 +02:00
*
2017-05-30 03:38:38 +02:00
*
2023-08-14 21:51:47 +02:00
* Authors : DOSBox Team ,
2022-02-22 20:28:56 -05:00
* Miran Grca , < mgrca8 @ gmail . com >
* TheCollector1995 , < mariogplayer @ gmail . com >
2017-10-17 01:59:09 -04:00
*
2022-02-22 20:28:56 -05:00
* Copyright 2008 - 2020 DOSBox Team .
* Copyright 2016 - 2020 Miran Grca .
2023-08-14 21:51:47 +02:00
* Copyright 2016 - 2020 TheCollector1995 .
2017-05-30 03:38:38 +02:00
*/
2023-08-18 16:39:26 +02:00
# include <inttypes.h>
2018-05-21 19:04:05 +02:00
# include <stdarg.h>
2017-09-25 04:31:20 -04:00
# include <stdint.h>
2018-05-21 19:04:05 +02:00
# include <stdio.h>
2017-09-25 04:31:20 -04:00
# include <stdlib.h>
2022-02-22 20:28:56 -05:00
# include <string.h>
2017-09-25 04:31:20 -04:00
# include <wchar.h>
2018-05-21 19:04:05 +02:00
# define HAVE_STDARG_H
2022-02-22 20:28:56 -05:00
2020-03-29 14:24:42 +02:00
# include <86box/86box.h>
# include <86box/device.h>
# include <86box/io.h>
# include <86box/machine.h>
# include <86box/mca.h>
2022-02-22 20:28:56 -05:00
# include <86box/midi.h>
2020-03-29 14:24:42 +02:00
# include <86box/pic.h>
2022-02-22 20:28:56 -05:00
# include <86box/plat.h>
2020-03-29 14:24:42 +02:00
# include <86box/timer.h>
# include <86box/snd_mpu401.h>
2022-02-22 20:28:56 -05:00
# include <86box/sound.h>
2023-06-09 23:46:54 -04:00
# include <86box/plat_unused.h>
2020-01-19 05:45:05 +01:00
2022-02-22 20:28:56 -05:00
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 } ;
2017-05-10 22:52:11 +02:00
2017-11-23 21:45:10 -05:00
enum {
STATUS_OUTPUT_NOT_READY = 0x40 ,
STATUS_INPUT_NOT_READY = 0x80
2017-05-09 21:45:20 +02:00
} ;
2017-06-03 20:32:58 +02:00
int mpu401_standalone_enable = 0 ;
2017-11-23 21:45:10 -05:00
static void MPU401_WriteCommand ( mpu_t * mpu , uint8_t val ) ;
2020-01-01 20:20:16 +01:00
static void MPU401_IntelligentOut ( mpu_t * mpu , uint8_t track ) ;
static void MPU401_EOIHandler ( void * priv ) ;
2023-06-09 23:46:54 -04:00
static void MPU401_EOIHandlerDispatch ( void * priv ) ;
2020-01-01 20:20:16 +01:00
static void MPU401_NotesOff ( mpu_t * mpu , int i ) ;
2017-11-23 21:45:10 -05:00
2018-05-21 19:04:05 +02:00
# ifdef ENABLE_MPU401_LOG
int mpu401_do_log = ENABLE_MPU401_LOG ;
2017-05-10 22:52:11 +02:00
static void
2018-05-21 19:04:05 +02:00
mpu401_log ( const char * fmt , . . . )
2017-05-10 22:52:11 +02:00
{
va_list ap ;
if ( mpu401_do_log ) {
2022-02-22 20:28:56 -05:00
va_start ( ap , fmt ) ;
pclog_ex ( fmt , ap ) ;
va_end ( ap ) ;
2017-05-10 22:52:11 +02:00
}
}
2018-10-19 00:39:32 +02:00
# else
2022-02-22 20:28:56 -05:00
# define mpu401_log(fmt, ...)
2018-10-19 00:39:32 +02:00
# endif
2017-05-10 22:52:11 +02:00
2020-01-01 20:20:16 +01:00
static void
MPU401_ReCalcClock ( mpu_t * mpu )
{
2023-05-29 01:30:51 -04:00
int32_t mintempo = 16 ;
int32_t maxtempo = 240 ;
2020-01-04 03:32:20 +01:00
int32_t freq ;
2020-01-23 06:40:20 +01:00
if ( mpu - > clock . timebase < 72 ) {
2022-02-22 20:28:56 -05:00
maxtempo = 240 ;
mintempo = 32 ;
2020-01-23 06:40:20 +01:00
} else if ( mpu - > clock . timebase < 120 ) {
2022-02-22 20:28:56 -05:00
maxtempo = 240 ;
mintempo = 16 ;
2020-01-23 06:40:20 +01:00
} else if ( mpu - > clock . timebase < 168 ) {
2022-02-22 20:28:56 -05:00
maxtempo = 208 ;
mintempo = 8 ;
2020-01-23 06:40:20 +01:00
} else {
2022-02-22 20:28:56 -05:00
maxtempo = 179 ;
mintempo = 8 ;
2020-01-23 06:40:20 +01:00
}
2020-01-04 03:32:20 +01:00
2022-02-22 20:28:56 -05:00
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 ) ) ;
2020-01-04 03:32:20 +01:00
if ( mpu - > state . sync_in ) {
2022-02-22 20:28:56 -05:00
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 ;
2022-02-20 02:26:27 -05:00
}
2020-01-01 20:20:16 +01:00
}
2023-08-18 16:39:26 +02:00
static void
MPU401_ReStartClock ( mpu_t * mpu )
{
if ( mpu - > clock . active ) {
timer_disable ( & mpu - > mpu401_event_callback ) ;
timer_set_delay_u64 ( & mpu - > mpu401_event_callback , ( MPU401_TIMECONSTANT / mpu - > clock . freq ) * 1000 * TIMER_USEC ) ;
}
}
2020-01-01 20:20:16 +01:00
static void
MPU401_StartClock ( mpu_t * mpu )
{
2023-08-18 16:39:26 +02:00
mpu401_log ( " MPU401_StartClock(): %i, %i, %i, %i \n " , mpu - > clock . active , mpu - > state . clock_to_host ,
mpu - > state . playing , ( mpu - > state . rec = = M_RECON ) ) ;
2020-01-04 03:32:20 +01:00
if ( mpu - > clock . active )
2022-02-22 20:28:56 -05:00
return ;
2023-08-18 16:39:26 +02:00
if ( mpu - > state . clock_to_host | | mpu - > state . playing | | ( mpu - > state . rec = = M_RECON ) )
2022-02-22 20:28:56 -05:00
return ;
2020-01-04 03:32:20 +01:00
mpu - > clock . active = 1 ;
timer_set_delay_u64 ( & mpu - > mpu401_event_callback , ( MPU401_TIMECONSTANT / mpu - > clock . freq ) * 1000 * TIMER_USEC ) ;
2020-01-01 20:20:16 +01:00
}
static void
MPU401_StopClock ( mpu_t * mpu )
{
2023-08-18 16:39:26 +02:00
if ( ! mpu - > state . clock_to_host & & ! mpu - > state . playing & & ( mpu - > state . rec = = M_RECOFF ) )
2022-02-22 20:28:56 -05:00
return ;
2020-01-04 03:32:20 +01:00
mpu - > clock . active = 0 ;
timer_disable ( & mpu - > mpu401_event_callback ) ;
2020-01-01 20:20:16 +01:00
}
static void
MPU401_RunClock ( mpu_t * mpu )
{
2020-01-04 03:32:20 +01:00
if ( ! mpu - > clock . active ) {
2022-02-22 20:28:56 -05:00
timer_disable ( & mpu - > mpu401_event_callback ) ;
return ;
2020-01-04 03:32:20 +01:00
}
2023-08-18 16:39:26 +02:00
timer_advance_u64 ( & mpu - > mpu401_event_callback , ( MPU401_TIMECONSTANT / mpu - > clock . freq ) * 1000 * TIMER_USEC ) ;
2023-08-21 20:24:51 -04:00
#if 0
mpu401_log ( " Next event after % " PRIu64 " us (time constant: %i) \n " , ( uint64_t ) ( ( MPU401_TIMECONSTANT / mpu - > clock . freq ) * 1000 * TIMER_USEC ) , ( int ) MPU401_TIMECONSTANT ) ;
# endif
2020-01-01 20:20:16 +01:00
}
2017-11-23 21:45:10 -05:00
static void
2023-06-09 23:46:54 -04:00
MPU401_QueueByteEx ( mpu_t * mpu , uint8_t data , UNUSED ( int irq ) )
2017-05-09 21:45:20 +02:00
{
2017-11-23 21:45:10 -05:00
if ( mpu - > state . block_ack ) {
2022-02-22 20:28:56 -05:00
mpu - > state . block_ack = 0 ;
return ;
2017-11-23 21:45:10 -05:00
}
2020-01-04 03:32:20 +01:00
2020-04-12 00:18:55 +02:00
if ( mpu - > queue_used = = 0 ) {
2022-02-22 20:28:56 -05:00
if ( mpu - > ext_irq_update )
mpu - > ext_irq_update ( mpu - > priv , 1 ) ;
else {
mpu - > state . irq_pending = 1 ;
picint ( 1 < < mpu - > irq ) ;
}
2017-11-23 21:45:10 -05:00
}
2020-01-04 03:32:20 +01:00
2017-11-23 21:45:10 -05:00
if ( mpu - > queue_used < MPU401_QUEUE ) {
2022-02-22 20:28:56 -05:00
int pos = mpu - > queue_used + mpu - > queue_pos ;
2017-11-23 21:45:10 -05:00
2022-02-22 20:28:56 -05:00
if ( mpu - > queue_pos > = MPU401_QUEUE )
mpu - > queue_pos - = MPU401_QUEUE ;
if ( pos > = MPU401_QUEUE )
pos - = MPU401_QUEUE ;
2017-11-23 21:45:10 -05:00
2022-02-22 20:28:56 -05:00
mpu - > queue_used + + ;
mpu - > queue [ pos ] = data ;
2020-01-01 20:20:16 +01:00
}
2017-05-09 21:45:20 +02:00
}
2020-01-23 06:40:20 +01:00
static void
2022-02-20 02:26:27 -05:00
MPU401_QueueByte ( mpu_t * mpu , uint8_t data )
2020-01-23 06:40:20 +01:00
{
MPU401_QueueByteEx ( mpu , data , 1 ) ;
}
2020-04-12 00:18:55 +02:00
static int
MPU401_IRQPending ( mpu_t * mpu )
{
int irq_pending ;
if ( mpu - > ext_irq_pending )
2022-02-22 20:28:56 -05:00
irq_pending = mpu - > ext_irq_pending ( mpu - > priv ) ;
2020-04-12 00:18:55 +02:00
else
2022-02-22 20:28:56 -05:00
irq_pending = mpu - > state . irq_pending ;
2020-04-12 00:18:55 +02:00
return irq_pending ;
}
2020-01-01 20:20:16 +01:00
static void
2023-06-09 23:46:54 -04:00
MPU401_RecQueueBuffer ( mpu_t * mpu , uint8_t * buf , uint32_t len , UNUSED ( int block ) )
2020-01-01 20:20:16 +01:00
{
2020-01-04 03:32:20 +01:00
uint32_t cnt = 0 ;
2022-02-22 20:28:56 -05:00
int pos ;
2020-01-02 18:19:22 +01:00
2020-01-04 03:32:20 +01:00
while ( cnt < len ) {
2022-02-22 20:28:56 -05:00
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 + + ;
}
2020-01-04 03:32:20 +01:00
}
if ( mpu - > queue_used = = 0 ) {
2022-02-22 20:28:56 -05:00
if ( mpu - > state . rec_copy | | MPU401_IRQPending ( mpu ) ) {
if ( MPU401_IRQPending ( mpu ) ) {
if ( mpu - > ext_irq_update )
mpu - > ext_irq_update ( mpu - > priv , 0 ) ;
else {
mpu - > state . irq_pending = 0 ;
picintc ( 1 < < mpu - > irq ) ;
}
}
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 + + ;
2020-01-04 03:32:20 +01:00
}
2020-01-01 20:20:16 +01:00
}
2017-11-23 21:45:10 -05:00
static void
2022-02-20 02:26:27 -05:00
MPU401_ClrQueue ( mpu_t * mpu )
2017-05-09 21:45:20 +02:00
{
2022-02-22 20:28:56 -05:00
mpu - > queue_used = 0 ;
mpu - > queue_pos = 0 ;
mpu - > rec_queue_used = 0 ;
mpu - > rec_queue_pos = 0 ;
2020-01-04 03:32:20 +01:00
mpu - > state . sysex_in_finished = 1 ;
2020-04-12 00:18:55 +02:00
if ( mpu - > ext_irq_update )
2022-02-22 20:28:56 -05:00
mpu - > ext_irq_update ( mpu - > priv , 0 ) ;
2020-04-12 00:18:55 +02:00
else {
2022-02-22 20:28:56 -05:00
mpu - > state . irq_pending = 0 ;
picintc ( 1 < < mpu - > irq ) ;
2020-04-12 00:18:55 +02:00
}
2017-05-09 21:45:20 +02:00
}
2017-11-23 21:45:10 -05:00
static void
2022-02-20 02:26:27 -05:00
MPU401_Reset ( mpu_t * mpu )
2017-05-09 21:45:20 +02:00
{
2017-11-23 21:45:10 -05:00
uint8_t i ;
2020-02-29 19:12:23 +01:00
# ifdef DOSBOX_CODE
2018-09-15 20:17:13 +02:00
if ( mpu - > mode = = M_INTELLIGENT ) {
2022-02-22 20:28:56 -05:00
if ( mpu - > ext_irq_update )
mpu - > ext_irq_update ( mpu - > priv , 0 ) ;
else {
mpu - > state . irq_pending = 0 ;
picintc ( 1 < < mpu - > irq ) ;
}
2018-09-15 20:17:13 +02:00
}
2020-02-29 19:12:23 +01:00
# else
2020-04-12 00:18:55 +02:00
if ( mpu - > ext_irq_update )
2022-02-22 20:28:56 -05:00
mpu - > ext_irq_update ( mpu - > priv , 0 ) ;
2020-04-12 00:18:55 +02:00
else {
2022-02-22 20:28:56 -05:00
mpu - > state . irq_pending = 0 ;
picintc ( 1 < < mpu - > irq ) ;
2020-04-12 00:18:55 +02:00
}
2020-02-29 19:12:23 +01:00
# endif
2022-02-22 20:28:56 -05:00
mpu - > mode = M_INTELLIGENT ;
mpu - > midi_thru = 0 ;
mpu - > state . rec = M_RECOFF ;
2017-11-23 21:45:10 -05:00
mpu - > state . eoi_scheduled = 0 ;
2022-02-22 20:28:56 -05:00
mpu - > state . wsd = 0 ;
mpu - > state . wsm = 0 ;
mpu - > state . conductor = 0 ;
mpu - > state . cond_req = 0 ;
mpu - > state . cond_set = 0 ;
mpu - > state . playing = 0 ;
mpu - > state . run_irq = 0 ;
mpu - > state . cmask = 0xff ;
2017-11-23 21:45:10 -05:00
mpu - > state . amask = mpu - > state . tmask = 0 ;
2022-02-22 20:28:56 -05:00
mpu - > state . midi_mask = 0xffff ;
mpu - > state . command_byte = 0 ;
mpu - > state . block_ack = 0 ;
2017-11-23 21:45:10 -05:00
mpu - > clock . tempo = mpu - > clock . old_tempo = 100 ;
mpu - > clock . timebase = mpu - > clock . old_timebase = 120 ;
2020-01-01 20:20:16 +01:00
mpu - > clock . tempo_rel = mpu - > clock . old_tempo_rel = 0x40 ;
2022-02-22 20:28:56 -05:00
mpu - > clock . freq_mod = 1.0 ;
mpu - > clock . tempo_grad = 0 ;
2020-01-04 03:32:20 +01:00
MPU401_StopClock ( mpu ) ;
MPU401_ReCalcClock ( mpu ) ;
for ( i = 0 ; i < 4 ; i + + )
2022-02-22 20:28:56 -05:00
mpu - > clock . cth_rate [ i ] = 60 ;
2020-01-04 03:32:20 +01:00
2022-02-22 20:28:56 -05:00
mpu - > clock . cth_counter = 0 ;
mpu - > clock . midimetro = 12 ;
mpu - > clock . metromeas = 8 ;
2020-01-04 03:32:20 +01:00
mpu - > filter . rec_measure_end = 1 ;
2022-02-22 20:28:56 -05:00
mpu - > filter . rt_out = 1 ;
mpu - > filter . rt_affection = 1 ;
2020-01-04 03:32:20 +01:00
mpu - > filter . allnotesoff_out = 1 ;
2022-02-22 20:28:56 -05:00
mpu - > filter . all_thru = 1 ;
mpu - > filter . midi_thru = 1 ;
2020-01-04 03:32:20 +01:00
mpu - > filter . commonmsgs_thru = 1 ;
/* Reset channel reference and input tables. */
for ( i = 0 ; i < 4 ; i + + ) {
2022-02-22 20:28:56 -05:00
mpu - > chanref [ i ] . on = 1 ;
mpu - > chanref [ i ] . chan = i ;
mpu - > ch_toref [ i ] = i ;
2020-01-04 03:32:20 +01:00
}
for ( i = 0 ; i < 16 ; i + + ) {
2022-02-22 20:28:56 -05:00
mpu - > inputref [ i ] . on = 1 ;
mpu - > inputref [ i ] . chan = i ;
if ( i > 3 )
mpu - > ch_toref [ i ] = 4 ; /* Dummy reftable. */
2020-01-04 03:32:20 +01:00
}
2017-11-23 21:45:10 -05:00
2020-01-01 20:20:16 +01:00
MPU401_ClrQueue ( mpu ) ;
mpu - > state . data_onoff = - 1 ;
2017-11-23 21:45:10 -05:00
2022-02-22 20:28:56 -05:00
mpu - > state . req_mask = 0 ;
mpu - > condbuf . counter = 0 ;
mpu - > condbuf . type = T_OVERFLOW ;
2017-11-23 21:45:10 -05:00
2020-01-04 03:32:20 +01:00
for ( i = 0 ; i < 8 ; i + + ) {
2022-02-22 20:28:56 -05:00
mpu - > playbuf [ i ] . type = T_OVERFLOW ;
mpu - > playbuf [ i ] . counter = 0 ;
2017-11-23 21:45:10 -05:00
}
2020-01-04 03:32:20 +01:00
/* Clear MIDI buffers, terminate notes. */
midi_clear_buffer ( ) ;
for ( i = 0xb0 ; i < = 0xbf ; i + + ) {
2022-02-22 20:28:56 -05:00
midi_raw_out_byte ( i ) ;
midi_raw_out_byte ( 0x7b ) ;
midi_raw_out_byte ( 0 ) ;
2020-01-04 03:32:20 +01:00
}
2017-05-09 21:45:20 +02:00
}
2017-11-23 21:45:10 -05:00
static void
2022-02-20 02:26:27 -05:00
MPU401_ResetDone ( void * priv )
2017-05-09 21:45:20 +02:00
{
2022-02-22 20:28:56 -05:00
mpu_t * mpu = ( mpu_t * ) priv ;
2017-05-10 20:39:46 +02:00
2018-05-21 19:04:05 +02:00
mpu401_log ( " MPU-401 reset callback \n " ) ;
2017-05-10 20:39:46 +02:00
Added the IBM 5161 ISA expansion for PC and XT;
Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port;
Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX);
Finished the 586MC1;
Added 8087 emulation;
Moved Cyrix 6x86'es to the Dev branch;
Sanitized/cleaned up memregs.c/h and intel.c/h;
Split the chipsets from machines and sanitized Port 92 emulation;
Added support for the 15bpp mode to the Compaq ATI 28800;
Moved the MR 386DX and 486 machines to the Dev branch;
Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00;
Ported the new timer code from PCem;
Cleaned up the CPU table of unused stuff and better optimized its structure;
Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch;
Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem;
Added the AHA-1540A and the BusTek BT-542B;
Moved the Sumo SCSI-AT to the Dev branch;
Minor IDE, FDC, and floppy drive code clean-ups;
Made NCR 5380/53C400-based cards' BIOS address configurable;
Got rid of the legacy romset variable;
Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit;
Added the Amstead PPC512 per PCem patch by John Elliott;
Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages);
Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing;
Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem;
Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit;
Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement;
Amstrad MegaPC does now works correctly with non-internal graphics card;
The SLiRP code no longer casts a packed struct type to a non-packed struct type;
The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present;
The S3 Virge on BeOS is no longer broken (was broken by build #1591);
OS/2 2.0 build 6.167 now sees key presses again;
Xi8088 now work on CGA again;
86F images converted from either the old or new variants of the HxC MFM format now work correctly;
Hardware interrupts with a vector of 0xFF are now handled correctly;
OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct;
Fixed VNC keyboard input bugs;
Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver;
Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly;
Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4;
Compaq Portable now works with all graphics cards;
Fixed various MDSI Genius bugs;
Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly;
Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355;
OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400.
Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391.
Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389.
Fixed a minor IDE timing bug, fixes #388.
Fixed Toshiba T1000 RAM issues, fixes #379.
Fixed EGA/(S)VGA overscan border handling, fixes #378;
Got rid of the now long useless IDE channel 2 auto-removal, fixes #370;
Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366;
Ported the Unicode CD image file name fix from VARCem, fixes #365;
Fixed high density floppy disks on the Xi8088, fixes #359;
Fixed some bugs in the Hercules emulation, fixes #346, fixes #358;
Fixed the SCSI hard disk mode sense pages, fixes #356;
Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349;
Fixed bugs in the serial mouse emulation, fixes #344;
Compiled 86Box binaries now include all the required .DLL's, fixes #341;
Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
timer_disable ( & mpu - > mpu401_reset_callback ) ;
2017-05-10 20:39:46 +02:00
2018-09-11 22:41:14 +02:00
mpu - > state . reset = 0 ;
2022-02-20 02:26:27 -05:00
2017-11-23 21:45:10 -05:00
if ( mpu - > state . cmd_pending ) {
2022-02-22 20:28:56 -05:00
MPU401_WriteCommand ( mpu , mpu - > state . cmd_pending - 1 ) ;
mpu - > state . cmd_pending = 0 ;
2017-11-23 21:45:10 -05:00
}
2017-05-09 21:45:20 +02:00
}
2017-11-23 21:45:10 -05:00
static void
MPU401_WriteCommand ( mpu_t * mpu , uint8_t val )
2022-02-20 02:26:27 -05:00
{
2023-05-29 01:30:51 -04:00
uint8_t i ;
uint8_t j ;
uint8_t was_uart ;
uint8_t recmsg [ 3 ] ;
2017-06-04 16:19:11 +02:00
2018-09-11 22:41:14 +02:00
if ( mpu - > state . reset )
2022-02-22 20:28:56 -05:00
mpu - > state . cmd_pending = val + 1 ;
2018-09-11 22:41:14 +02:00
2020-01-04 03:32:20 +01:00
/* The only command recognized in UART mode is 0xFF: Reset and return to Intelligent mode. */
if ( ( val ! = 0xff ) & & ( mpu - > mode = = M_UART ) )
2022-02-22 20:28:56 -05:00
return ;
2020-01-04 03:32:20 +01:00
/* In Intelligent mode, UART-only variants of the MPU-401 only support commands 0x3F and 0xFF. */
2020-02-29 19:12:23 +01:00
if ( ! mpu - > intelligent & & ( val ! = 0x3f ) & & ( val ! = 0xff ) )
2022-02-22 20:28:56 -05:00
return ;
2017-11-23 21:45:10 -05:00
2020-01-04 03:32:20 +01:00
/* Hack: Enable midi through after the first mpu401 command is written. */
mpu - > midi_thru = 1 ;
2020-01-01 20:20:16 +01:00
if ( val < = 0x2f ) { /* Sequencer state */
2022-02-22 20:28:56 -05:00
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 ;
2023-06-09 23:46:54 -04:00
default :
break ;
2022-02-22 20:28:56 -05:00
}
switch ( val & 0xc ) { /* Playing */
case 0x4 : /* Stop */
MPU401_StopClock ( mpu ) ;
2023-08-18 16:39:26 +02:00
mpu - > state . playing = 0 ;
2022-02-22 20:28:56 -05:00
for ( i = 0 ; i < 16 ; i + + )
MPU401_NotesOff ( mpu , i ) ;
mpu - > filter . prchg_mask = 0 ;
break ;
case 0x8 : /* Start */
MPU401_StartClock ( mpu ) ;
2023-08-18 16:39:26 +02:00
mpu - > state . playing = 1 ;
MPU401_ClrQueue ( mpu ) ;
2022-02-22 20:28:56 -05:00
break ;
2023-06-09 23:46:54 -04:00
default :
break ;
2022-02-22 20:28:56 -05:00
}
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 ;
MPU401_StartClock ( mpu ) ;
2023-08-18 16:39:26 +02:00
mpu - > state . rec = M_RECON ;
2022-02-22 20:28:56 -05:00
if ( mpu - > filter . prchg_mask )
send_prchg = 1 ;
break ;
case 0x10 : /* Stop */
MPU401_StopClock ( mpu ) ;
2023-08-18 16:39:26 +02:00
mpu - > state . rec = M_RECOFF ;
2022-02-22 20:28:56 -05:00
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 ;
2023-06-09 23:46:54 -04:00
default :
break ;
2022-02-22 20:28:56 -05:00
}
}
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 ) ;
}
}
}
} 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 ;
2020-01-04 03:32:20 +01:00
} else if ( ( val < 0x80 ) & & ( val > = 0x40 ) ) { /* Set reference table channel */
2022-02-22 20:28:56 -05:00
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 0x30 : /* Configuration 0x30 - 0x39 */
mpu - > filter . allnotesoff_out = 0 ;
break ;
case 0x32 :
mpu - > filter . rt_out = 0 ;
break ;
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 ; j < 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 */
MPU401_StopClock ( mpu ) ;
2023-08-18 16:39:26 +02:00
mpu - > state . clock_to_host = 0 ;
2022-02-22 20:28:56 -05:00
break ;
case 0x95 :
MPU401_StartClock ( mpu ) ;
2023-08-18 16:39:26 +02:00
mpu - > state . clock_to_host = 1 ;
2022-02-22 20:28:56 -05:00
break ;
case 0x96 :
case 0x97 : /* Sysex input allow */
mpu - > filter . sysex_in = ! ! ( val & 1 ) ;
if ( val & 1 )
mpu - > filter . sysex_thru = 0 ;
break ;
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 ;
/* Commands 0xa# returning data */
case 0xab : /* Request and clear recording counter */
MPU401_QueueByte ( mpu , MSG_MPU_ACK ) ;
MPU401_QueueByte ( mpu , 0 ) ;
return ;
case 0xac : /* Request version */
MPU401_QueueByte ( mpu , MSG_MPU_ACK ) ;
MPU401_QueueByte ( mpu , MPU401_VERSION ) ;
return ;
case 0xad : /* Request revision */
MPU401_QueueByte ( mpu , MSG_MPU_ACK ) ;
MPU401_QueueByte ( mpu , MPU401_REVISION ) ;
return ;
case 0xaf : /* Request 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 = 0x40 ;
break ;
case 0xb8 : /* Clear play counters */
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 ;
mpu - > state . amask = mpu - > state . tmask ;
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 ) ;
2023-08-18 16:39:26 +02:00
MPU401_ReStartClock ( mpu ) ;
2022-02-22 20:28:56 -05:00
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 ) ;
timer_set_delay_u64 ( & mpu - > mpu401_reset_callback , MPU401_RESETBUSY * 33LL * TIMER_USEC ) ;
mpu - > state . reset = 1 ;
was_uart = ( mpu - > mode = = M_UART ) ;
MPU401_Reset ( mpu ) ;
if ( was_uart )
return ;
break ;
2023-06-09 23:46:54 -04:00
default :
#if 0
mpu401_log ( " MPU-401:Unhandled command %X " , val ) ;
# endif
break ;
2022-02-22 20:28:56 -05:00
}
2017-11-23 21:45:10 -05:00
2020-01-01 20:20:16 +01:00
MPU401_QueueByte ( mpu , MSG_MPU_ACK ) ;
2017-05-09 21:45:20 +02:00
}
2017-11-23 21:45:10 -05:00
static void
2022-02-20 02:26:27 -05:00
MPU401_WriteData ( mpu_t * mpu , uint8_t val )
2017-05-09 21:45:20 +02:00
{
2023-05-29 01:30:51 -04:00
static int length ;
static int cnt ;
2018-09-11 22:41:14 +02:00
2020-02-29 19:12:23 +01:00
# ifdef DOSBOX_CODE
2018-09-11 22:41:14 +02:00
if ( mpu - > mode = = M_UART ) {
2022-02-22 20:28:56 -05:00
midi_raw_out_byte ( val ) ;
return ;
2018-09-11 22:41:14 +02:00
}
if ( ! mpu - > intelligent ) {
2022-02-22 20:28:56 -05:00
mpu - > state . command_byte = 0 ;
return ;
2018-09-11 22:41:14 +02:00
}
2020-02-29 19:12:23 +01:00
# else
if ( ! mpu - > intelligent | | ( mpu - > mode = = M_UART ) ) {
2022-02-22 20:28:56 -05:00
midi_raw_out_byte ( val ) ;
return ;
2020-02-29 19:12:23 +01:00
}
# endif
2022-02-20 02:26:27 -05:00
2022-02-22 20:28:56 -05:00
switch ( mpu - > state . command_byte ) { /* 0xe# command data */
case 0x00 :
break ;
case 0xe0 : /* Set tempo */
mpu - > state . command_byte = 0 ;
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 ) ;
2023-08-18 16:39:26 +02:00
MPU401_ReStartClock ( mpu ) ;
2022-02-22 20:28:56 -05:00
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 ) ;
2023-08-18 16:39:26 +02:00
MPU401_ReStartClock ( mpu ) ;
2022-02-22 20:28:56 -05:00
return ;
case 0xe2 : /* Set gradation for relative tempo */
mpu - > clock . tempo_grad = val ;
MPU401_ReCalcClock ( mpu ) ;
2023-08-18 16:39:26 +02:00
MPU401_ReStartClock ( mpu ) ;
2022-02-22 20:28:56 -05:00
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 ;
if ( ! val )
val = 64 ;
2023-05-29 01:30:51 -04:00
for ( uint8_t i = 0 ; i < 4 ; i + + )
2022-02-22 20:28:56 -05:00
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 ;
mpu - > state . midi_mask | = ( ( uint16_t ) val ) < < 8 ;
return ;
default :
mpu - > state . command_byte = 0 ;
return ;
2017-11-23 21:45:10 -05:00
}
2020-01-01 20:20:16 +01:00
if ( mpu - > state . wsd & & ! mpu - > state . track_req & & ! mpu - > state . cond_req ) {
2022-02-22 20:28:56 -05:00
/* Directly send MIDI message */
if ( mpu - > state . wsd_start ) {
mpu - > state . wsd_start = 0 ;
cnt = 0 ;
switch ( val & 0xf0 ) {
case 0xc0 :
case 0xd0 :
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 :
2023-06-09 23:46:54 -04:00
#if 0
mpu401_log ( " MPU-401:Illegal WSD byte \n " ) ;
# endif
2022-02-22 20:28:56 -05:00
mpu - > state . wsd = 0 ;
mpu - > state . track = mpu - > state . old_track ;
return ;
default : /* MIDI with running status */
cnt + + ;
length = mpu - > playbuf [ mpu - > state . track ] . length ;
mpu - > playbuf [ mpu - > state . track ] . type = T_MIDI_NORM ;
}
}
if ( cnt < length ) {
mpu - > playbuf [ mpu - > state . track ] . value [ cnt ] = val ;
cnt + + ;
}
if ( cnt = = length ) {
MPU401_IntelligentOut ( mpu , mpu - > state . track ) ;
mpu - > state . wsd = 0 ;
mpu - > state . track = mpu - > state . old_track ;
}
return ;
2017-11-23 21:45:10 -05:00
}
2022-02-22 20:28:56 -05:00
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 ;
switch ( val ) {
case 0xf2 :
length = 3 ;
break ;
case 0xf3 :
length = 2 ;
break ;
case 0xf6 :
length = 1 ;
break ;
case 0xf0 :
length = 0 ;
break ;
default :
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_raw_out_byte ( val ) ;
cnt + + ;
}
if ( cnt = = length )
mpu - > state . wsm = 0 ;
return ;
2017-11-23 21:45:10 -05:00
}
if ( mpu - > state . cond_req ) {
2022-02-22 20:28:56 -05:00
/* Command */
switch ( mpu - > state . data_onoff ) {
case - 1 :
return ;
case 0 : /* Timing byte */
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 ) ;
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 ) | | ( val = = 0xfc ) )
mpu - > condbuf . type = T_OVERFLOW ;
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 . length ] = val ;
mpu - > condbuf . length + + ;
MPU401_EOIHandler ( mpu ) ;
mpu - > state . data_onoff = - 1 ;
mpu - > state . cond_req = 0 ;
break ;
2023-06-09 23:46:54 -04:00
default :
break ;
2022-02-22 20:28:56 -05:00
}
return ;
2017-11-23 21:45:10 -05:00
}
switch ( mpu - > state . data_onoff ) {
2022-02-22 20:28:56 -05:00
/* Data */
case - 1 :
break ;
case 0 : /* Timing byte */
if ( val < 0xf0 )
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 . track ] . counter = val ;
break ;
case 1 : /* MIDI */
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 . track ] . type = T_MARK ;
if ( val = = 0xf9 )
mpu - > clock . measure_counter = 0 ;
} else {
2023-06-09 23:46:54 -04:00
#if 0
mpu401_log ( " MPU-401:Illegal message " ) ;
# endif
2022-02-22 20:28:56 -05:00
mpu - > playbuf [ mpu - > state . track ] . type = T_OVERFLOW ;
}
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 ;
}
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 ;
2023-06-09 23:46:54 -04:00
default :
break ;
2017-11-23 21:45:10 -05:00
}
2022-02-20 02:26:27 -05:00
2022-02-22 20:28:56 -05:00
return ;
2017-05-09 21:45:20 +02:00
}
2017-11-23 21:45:10 -05:00
static void
2022-02-20 02:26:27 -05:00
MPU401_IntelligentOut ( mpu_t * mpu , uint8_t track )
2017-05-09 21:45:20 +02:00
{
2023-05-29 01:30:51 -04:00
uint8_t chan ;
uint8_t chrefnum ;
uint8_t key ;
uint8_t msg ;
int send ;
int retrigger ;
2017-11-23 21:45:10 -05:00
2020-01-01 20:20:16 +01:00
switch ( mpu - > playbuf [ track ] . type ) {
2022-02-22 20:28:56 -05:00
case T_OVERFLOW :
break ;
case T_MARK :
if ( mpu - > playbuf [ track ] . sys_val = = 0xfc ) {
midi_raw_out_rt_byte ( mpu - > playbuf [ track ] . sys_val ) ;
mpu - > state . amask & = ~ ( 1 < < track ) ;
}
break ;
case T_MIDI_NORM :
chan = mpu - > playbuf [ track ] . value [ 0 ] & 0xf ;
key = mpu - > playbuf [ track ] . value [ 1 ] & 0x7f ;
chrefnum = mpu - > ch_toref [ chan ] ;
send = 1 ;
retrigger = 0 ;
switch ( msg = mpu - > playbuf [ track ] . value [ 0 ] & 0xf0 ) {
case 0x80 : /* note off */
if ( mpu - > inputref [ chan ] . on & & ( mpu - > inputref [ chan ] . M_GETKEY ) )
send = 0 ;
if ( mpu - > chanref [ chrefnum ] . on & & ( ! ( mpu - > chanref [ chrefnum ] . M_GETKEY ) ) )
send = 0 ;
mpu - > chanref [ chrefnum ] . M_DELKEY ;
break ;
case 0x90 : /* note on */
if ( mpu - > inputref [ chan ] . on & & ( mpu - > inputref [ chan ] . M_GETKEY ) )
retrigger = 1 ;
if ( mpu - > chanref [ chrefnum ] . on & & ( ! ( mpu - > chanref [ chrefnum ] . M_GETKEY ) ) )
retrigger = 1 ;
mpu - > chanref [ chrefnum ] . M_SETKEY ;
break ;
case 0xb0 :
if ( mpu - > playbuf [ track ] . value [ 1 ] = = 123 ) { /* All notes off */
MPU401_NotesOff ( mpu , mpu - > playbuf [ track ] . value [ 0 ] & 0xf ) ;
return ;
}
break ;
2023-06-09 23:46:54 -04:00
default :
break ;
2022-02-22 20:28:56 -05:00
}
if ( retrigger ) {
midi_raw_out_byte ( 0x80 | chan ) ;
midi_raw_out_byte ( key ) ;
midi_raw_out_byte ( 0 ) ;
}
if ( send ) {
2023-05-29 01:30:51 -04:00
for ( uint8_t i = 0 ; i < mpu - > playbuf [ track ] . length ; i + + )
2022-02-22 20:28:56 -05:00
midi_raw_out_byte ( mpu - > playbuf [ track ] . value [ i ] ) ;
}
break ;
default :
break ;
2017-11-23 21:45:10 -05:00
}
}
static void
2022-02-20 02:26:27 -05:00
UpdateTrack ( mpu_t * mpu , uint8_t track )
2017-11-23 21:45:10 -05:00
{
2020-01-01 20:20:16 +01:00
MPU401_IntelligentOut ( mpu , track ) ;
2017-11-23 21:45:10 -05:00
2022-02-22 20:28:56 -05:00
if ( mpu - > state . amask & ( 1 < < track ) ) {
mpu - > playbuf [ track ] . type = T_OVERFLOW ;
mpu - > playbuf [ track ] . counter = 0xf0 ;
mpu - > state . req_mask | = ( 1 < < track ) ;
2017-11-23 21:45:10 -05:00
} else {
2022-02-22 20:28:56 -05:00
if ( ( mpu - > state . amask = = 0 ) & & ! mpu - > state . conductor )
mpu - > state . req_mask | = ( 1 < < 12 ) ;
2017-11-23 21:45:10 -05:00
}
2017-05-09 21:45:20 +02:00
}
2018-09-11 22:41:14 +02:00
/* Updates counters and requests new data on "End of Input" */
2017-11-23 21:45:10 -05:00
static void
MPU401_EOIHandler ( void * priv )
2017-05-09 21:45:20 +02:00
{
2022-02-22 20:28:56 -05:00
mpu_t * mpu = ( mpu_t * ) priv ;
2017-11-23 21:45:10 -05:00
uint8_t i ;
2017-05-10 20:39:46 +02:00
2018-05-21 19:04:05 +02:00
mpu401_log ( " MPU-401 end of input callback \n " ) ;
2022-02-20 02:26:27 -05:00
Added the IBM 5161 ISA expansion for PC and XT;
Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port;
Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX);
Finished the 586MC1;
Added 8087 emulation;
Moved Cyrix 6x86'es to the Dev branch;
Sanitized/cleaned up memregs.c/h and intel.c/h;
Split the chipsets from machines and sanitized Port 92 emulation;
Added support for the 15bpp mode to the Compaq ATI 28800;
Moved the MR 386DX and 486 machines to the Dev branch;
Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00;
Ported the new timer code from PCem;
Cleaned up the CPU table of unused stuff and better optimized its structure;
Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch;
Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem;
Added the AHA-1540A and the BusTek BT-542B;
Moved the Sumo SCSI-AT to the Dev branch;
Minor IDE, FDC, and floppy drive code clean-ups;
Made NCR 5380/53C400-based cards' BIOS address configurable;
Got rid of the legacy romset variable;
Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit;
Added the Amstead PPC512 per PCem patch by John Elliott;
Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages);
Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing;
Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem;
Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit;
Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement;
Amstrad MegaPC does now works correctly with non-internal graphics card;
The SLiRP code no longer casts a packed struct type to a non-packed struct type;
The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present;
The S3 Virge on BeOS is no longer broken (was broken by build #1591);
OS/2 2.0 build 6.167 now sees key presses again;
Xi8088 now work on CGA again;
86F images converted from either the old or new variants of the HxC MFM format now work correctly;
Hardware interrupts with a vector of 0xFF are now handled correctly;
OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct;
Fixed VNC keyboard input bugs;
Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver;
Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly;
Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4;
Compaq Portable now works with all graphics cards;
Fixed various MDSI Genius bugs;
Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly;
Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355;
OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400.
Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391.
Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389.
Fixed a minor IDE timing bug, fixes #388.
Fixed Toshiba T1000 RAM issues, fixes #379.
Fixed EGA/(S)VGA overscan border handling, fixes #378;
Got rid of the now long useless IDE channel 2 auto-removal, fixes #370;
Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366;
Ported the Unicode CD image file name fix from VARCem, fixes #365;
Fixed high density floppy disks on the Xi8088, fixes #359;
Fixed some bugs in the Hercules emulation, fixes #346, fixes #358;
Fixed the SCSI hard disk mode sense pages, fixes #356;
Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349;
Fixed bugs in the serial mouse emulation, fixes #344;
Compiled 86Box binaries now include all the required .DLL's, fixes #341;
Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
timer_disable ( & mpu - > mpu401_eoi_callback ) ;
2018-09-11 22:41:14 +02:00
mpu - > state . eoi_scheduled = 0 ;
2017-11-23 21:45:10 -05:00
if ( mpu - > state . send_now ) {
2022-02-22 20:28:56 -05:00
mpu - > state . send_now = 0 ;
if ( mpu - > state . cond_req ) {
mpu - > condbuf . counter = 0xf0 ;
mpu - > state . req_mask | = ( 1 < < 9 ) ;
} else
UpdateTrack ( mpu , mpu - > state . track ) ;
2017-11-23 21:45:10 -05:00
}
2022-02-20 02:26:27 -05:00
if ( mpu - > state . rec_copy | | ! mpu - > state . sysex_in_finished )
2022-02-22 20:28:56 -05:00
return ;
2020-01-01 20:20:16 +01:00
2023-08-18 16:39:26 +02:00
if ( ! mpu - > state . req_mask | | ! mpu - > clock . active )
return ;
2020-04-12 00:18:55 +02:00
if ( mpu - > ext_irq_update )
2022-02-22 20:28:56 -05:00
mpu - > ext_irq_update ( mpu - > priv , 0 ) ;
2020-04-12 00:18:55 +02:00
else {
2022-02-22 20:28:56 -05:00
mpu - > state . irq_pending = 0 ;
2020-04-12 00:18:55 +02:00
}
2017-11-23 21:45:10 -05:00
2018-09-11 22:41:14 +02:00
i = 0 ;
2017-11-23 21:45:10 -05:00
do {
2022-02-22 20:28:56 -05:00
if ( mpu - > state . req_mask & ( 1 < < i ) ) {
MPU401_QueueByte ( mpu , 0xf0 + i ) ;
mpu - > state . req_mask & = ~ ( 1 < < i ) ;
break ;
}
2018-09-11 22:41:14 +02:00
} while ( ( i + + ) < 16 ) ;
2017-05-09 21:45:20 +02:00
}
2017-11-23 21:45:10 -05:00
static void
2022-02-20 02:26:27 -05:00
MPU401_EOIHandlerDispatch ( void * priv )
2017-05-09 21:45:20 +02:00
{
2022-02-22 20:28:56 -05:00
mpu_t * mpu = ( mpu_t * ) priv ;
2017-11-23 21:45:10 -05:00
2018-09-04 13:26:07 +02:00
mpu401_log ( " EOI handler dispatch \n " ) ;
2017-11-23 21:45:10 -05:00
if ( mpu - > state . send_now ) {
2022-02-22 20:28:56 -05:00
mpu - > state . eoi_scheduled = 1 ;
timer_advance_u64 ( & mpu - > mpu401_eoi_callback , 60LL * TIMER_USEC ) ; /* Possibly a bit longer */
2022-02-20 02:26:27 -05:00
} else if ( ! mpu - > state . eoi_scheduled )
2022-02-22 20:28:56 -05:00
MPU401_EOIHandler ( mpu ) ;
2017-05-10 20:39:46 +02:00
}
2017-11-23 21:45:10 -05:00
static void
2023-06-09 23:46:54 -04:00
imf_write ( UNUSED ( uint16_t addr ) , UNUSED ( uint8_t val ) , UNUSED ( void * priv ) )
2017-05-10 20:39:46 +02:00
{
2018-05-21 19:04:05 +02:00
mpu401_log ( " IMF:Wr %4X,%X \n " , addr , val ) ;
2017-05-09 21:45:20 +02:00
}
2020-01-04 03:32:20 +01:00
void
MPU401_ReadRaiseIRQ ( mpu_t * mpu )
{
/* Clear IRQ. */
2020-04-12 00:18:55 +02:00
if ( mpu - > ext_irq_update )
2022-02-22 20:28:56 -05:00
mpu - > ext_irq_update ( mpu - > priv , 0 ) ;
2020-04-12 00:18:55 +02:00
else {
2022-02-22 20:28:56 -05:00
mpu - > state . irq_pending = 0 ;
picintc ( 1 < < mpu - > irq ) ;
2020-04-12 00:18:55 +02:00
}
2020-01-04 03:32:20 +01:00
2020-04-12 00:18:55 +02:00
if ( mpu - > queue_used ) {
2022-02-22 20:28:56 -05:00
/* Bytes remaining in queue, raise IRQ again. */
if ( mpu - > ext_irq_update )
mpu - > ext_irq_update ( mpu - > priv , 1 ) ;
else {
mpu - > state . irq_pending = 1 ;
picint ( 1 < < mpu - > irq ) ;
}
2020-01-04 03:32:20 +01:00
}
}
2017-11-23 21:45:10 -05:00
uint8_t
MPU401_ReadData ( mpu_t * mpu )
2017-05-10 18:29:16 +02:00
{
2017-11-23 21:45:10 -05:00
uint8_t ret ;
2022-02-20 02:26:27 -05:00
2017-11-23 21:45:10 -05:00
ret = MSG_MPU_ACK ;
2020-01-02 18:19:22 +01:00
2017-11-23 21:45:10 -05:00
if ( mpu - > queue_used ) {
2022-02-22 20:28:56 -05:00
if ( mpu - > queue_pos > = MPU401_QUEUE )
mpu - > queue_pos - = MPU401_QUEUE ;
ret = mpu - > queue [ mpu - > queue_pos ] ;
mpu - > queue_pos + + ;
mpu - > queue_used - - ;
2017-11-23 21:45:10 -05:00
}
2017-05-10 18:29:16 +02:00
2018-09-11 22:41:14 +02:00
/* Shouldn't this check mpu->mode? */
2020-02-29 19:12:23 +01:00
# ifdef DOSBOX_CODE
2018-09-15 20:17:13 +02:00
if ( mpu - > mode = = M_UART ) {
2022-02-22 20:28:56 -05:00
MPU401_ReadRaiseIRQ ( mpu ) ;
return ret ;
2018-09-15 20:17:13 +02:00
}
2020-02-29 19:12:23 +01:00
# else
if ( ! mpu - > intelligent | | ( mpu - > mode = = M_UART ) ) {
2022-02-22 20:28:56 -05:00
MPU401_ReadRaiseIRQ ( mpu ) ;
return ret ;
2020-02-29 19:12:23 +01:00
}
# endif
2017-05-10 18:29:16 +02:00
2020-01-04 03:32:20 +01:00
if ( mpu - > state . rec_copy & & ! mpu - > rec_queue_used ) {
2022-02-22 20:28:56 -05:00
mpu - > state . rec_copy = 0 ;
MPU401_EOIHandler ( mpu ) ;
return ret ;
2020-01-04 03:32:20 +01:00
}
2020-01-01 20:20:16 +01:00
2020-01-04 03:32:20 +01:00
/* Copy from recording buffer. */
if ( ! mpu - > queue_used & & mpu - > rec_queue_used ) {
2022-02-22 20:28:56 -05:00
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_pos + + ;
mpu - > rec_queue_used - - ;
2018-09-15 20:17:13 +02:00
}
2017-11-23 21:45:10 -05:00
2020-01-04 03:32:20 +01:00
MPU401_ReadRaiseIRQ ( mpu ) ;
2018-09-11 22:41:14 +02:00
if ( ( ret > = 0xf0 ) & & ( ret < = 0xf7 ) ) {
2022-02-22 20:28:56 -05:00
/* MIDI data request */
mpu - > state . track = ret & 7 ;
mpu - > state . data_onoff = 0 ;
mpu - > state . cond_req = 0 ;
mpu - > state . track_req = 1 ;
2017-11-23 21:45:10 -05:00
}
2018-09-11 22:41:14 +02:00
if ( ret = = MSG_MPU_COMMAND_REQ ) {
2022-02-22 20:28:56 -05:00
mpu - > state . data_onoff = 0 ;
mpu - > state . cond_req = 1 ;
if ( mpu - > condbuf . type ! = T_OVERFLOW ) {
mpu - > state . block_ack = 1 ;
MPU401_WriteCommand ( mpu , mpu - > condbuf . value [ 0 ] ) ;
if ( mpu - > state . command_byte )
MPU401_WriteData ( mpu , mpu - > condbuf . value [ 1 ] ) ;
mpu - > condbuf . type = T_OVERFLOW ;
}
2017-11-23 21:45:10 -05:00
}
2020-01-04 03:32:20 +01:00
if ( ( ret = = MSG_MPU_END ) | | ( ret = = MSG_MPU_CLOCK ) | | ( ret = = MSG_MPU_ACK ) | | ( ret = = MSG_MPU_OVERFLOW ) )
2022-02-22 20:28:56 -05:00
MPU401_EOIHandlerDispatch ( mpu ) ;
2017-11-23 21:45:10 -05:00
2020-01-04 03:32:20 +01:00
return ret ;
2017-05-10 18:29:16 +02:00
}
2022-02-25 19:22:45 -03:00
void
2017-11-23 21:45:10 -05:00
mpu401_write ( uint16_t addr , uint8_t val , void * priv )
2017-05-09 21:45:20 +02:00
{
2022-02-22 20:28:56 -05:00
mpu_t * mpu = ( mpu_t * ) priv ;
2022-02-20 02:26:27 -05:00
2018-05-21 19:04:05 +02:00
/* mpu401_log("MPU401 Write Port %04X, val %x\n", addr, val); */
2017-11-23 21:45:10 -05:00
switch ( addr & 1 ) {
2022-02-22 20:28:56 -05:00
case 0 : /*Data*/
MPU401_WriteData ( mpu , val ) ;
mpu401_log ( " Write Data (0x330) %X \n " , val ) ;
break ;
case 1 : /*Command*/
MPU401_WriteCommand ( mpu , val ) ;
mpu401_log ( " Write Command (0x331) %x \n " , val ) ;
break ;
2023-06-09 23:46:54 -04:00
default :
break ;
2017-11-23 21:45:10 -05:00
}
2017-05-09 21:45:20 +02:00
}
2022-02-25 19:22:45 -03:00
uint8_t
2017-11-23 21:45:10 -05:00
mpu401_read ( uint16_t addr , void * priv )
2017-05-09 21:45:20 +02:00
{
2022-02-22 20:28:56 -05:00
mpu_t * mpu = ( mpu_t * ) priv ;
2017-11-23 21:45:10 -05:00
uint8_t ret = 0 ;
2022-02-20 02:26:27 -05:00
switch ( addr & 1 ) {
2022-02-22 20:28:56 -05:00
case 0 : /* Read Data */
ret = MPU401_ReadData ( mpu ) ;
mpu401_log ( " Read Data (0x330) %X \n " , ret ) ;
break ;
case 1 : /* Read Status */
if ( mpu - > state . cmd_pending )
ret = STATUS_OUTPUT_NOT_READY ;
if ( ! mpu - > queue_used )
ret = STATUS_INPUT_NOT_READY ;
ret | = 0x3f ;
mpu401_log ( " Read Status (0x331) %x \n " , ret ) ;
break ;
2023-06-09 23:46:54 -04:00
default :
break ;
2017-11-23 21:45:10 -05:00
}
2018-05-21 19:04:05 +02:00
/* mpu401_log("MPU401 Read Port %04X, ret %x\n", addr, ret); */
2023-05-29 01:30:51 -04:00
return ret ;
2017-05-09 21:45:20 +02:00
}
2017-11-23 21:45:10 -05:00
static void
2022-02-20 02:26:27 -05:00
MPU401_Event ( void * priv )
2017-05-09 21:45:20 +02:00
{
2022-02-22 20:28:56 -05:00
mpu_t * mpu = ( mpu_t * ) priv ;
int max_meascnt ;
2017-05-10 20:39:46 +02:00
2018-05-21 19:04:05 +02:00
mpu401_log ( " MPU-401 event callback \n " ) ;
2017-11-23 21:45:10 -05:00
2020-02-29 19:12:23 +01:00
# ifdef DOSBOX_CODE
2018-09-11 22:41:14 +02:00
if ( mpu - > mode = = M_UART ) {
2022-02-22 20:28:56 -05:00
timer_disable ( & mpu - > mpu401_event_callback ) ;
return ;
2017-11-23 21:45:10 -05:00
}
2020-02-29 19:12:23 +01:00
# else
if ( ! mpu - > intelligent | | ( mpu - > mode = = M_UART ) ) {
2022-02-22 20:28:56 -05:00
timer_disable ( & mpu - > mpu401_event_callback ) ;
return ;
2020-02-29 19:12:23 +01:00
}
# endif
2017-11-23 21:45:10 -05:00
2020-04-12 00:18:55 +02:00
if ( MPU401_IRQPending ( mpu ) )
2022-02-22 20:28:56 -05:00
goto next_event ;
2020-04-12 00:18:55 +02:00
2020-01-04 03:32:20 +01:00
if ( mpu - > state . playing ) {
2023-05-29 01:30:51 -04:00
for ( uint8_t i = 0 ; i < 8 ; i + + ) {
2022-02-22 20:28:56 -05:00
/* Decrease counters. */
if ( mpu - > state . amask & ( 1 < < i ) ) {
mpu - > playbuf [ i ] . counter - - ;
if ( mpu - > playbuf [ i ] . counter < = 0 )
UpdateTrack ( mpu , i ) ;
}
}
if ( mpu - > state . conductor ) {
mpu - > condbuf . counter - - ;
if ( mpu - > condbuf . counter < = 0 ) {
mpu - > condbuf . counter = 0xf0 ;
mpu - > state . req_mask | = ( 1 < < 9 ) ;
}
}
2017-11-23 21:45:10 -05:00
}
2020-01-01 20:20:16 +01:00
if ( mpu - > state . clock_to_host ) {
2022-02-22 20:28:56 -05:00
mpu - > clock . cth_counter + + ;
if ( mpu - > clock . cth_counter > = mpu - > clock . cth_rate [ mpu - > clock . cth_mode ] ) {
mpu - > clock . cth_counter = 0 ;
mpu - > clock . cth_mode + + ;
mpu - > clock . cth_mode % = 4 ;
mpu - > state . req_mask | = ( 1 < < 13 ) ;
}
2017-11-23 21:45:10 -05:00
}
2020-01-04 03:32:20 +01:00
if ( mpu - > state . rec = = M_RECON ) {
2022-02-22 20:28:56 -05:00
/* Recording. */
mpu - > clock . rec_counter + + ;
if ( mpu - > clock . rec_counter > = 240 ) {
mpu - > clock . rec_counter = 0 ;
mpu - > state . req_mask | = ( 1 < < 8 ) ;
}
2020-01-04 03:32:20 +01:00
}
2020-01-01 20:20:16 +01:00
2020-01-04 03:32:20 +01:00
if ( mpu - > state . playing | | ( mpu - > state . rec = = M_RECON ) ) {
2022-02-22 20:28:56 -05:00
max_meascnt = ( mpu - > clock . timebase * mpu - > clock . midimetro * mpu - > clock . metromeas ) / 24 ;
if ( max_meascnt ! = 0 ) {
/* Measure end. */
if ( + + mpu - > clock . measure_counter > = max_meascnt ) {
if ( mpu - > filter . rt_out )
midi_raw_out_rt_byte ( 0xf8 ) ;
mpu - > clock . measure_counter = 0 ;
if ( mpu - > filter . rec_measure_end & & ( mpu - > state . rec = = M_RECON ) )
mpu - > state . req_mask | = ( 1 < < 12 ) ;
}
}
2020-01-04 03:32:20 +01:00
}
2020-04-12 00:18:55 +02:00
2023-08-18 16:39:26 +02:00
if ( ! MPU401_IRQPending ( mpu ) & & mpu - > state . req_mask )
2022-02-22 20:28:56 -05:00
MPU401_EOIHandler ( mpu ) ;
2017-11-23 21:45:10 -05:00
2017-05-09 21:45:20 +02:00
next_event :
2020-01-04 03:32:20 +01:00
MPU401_RunClock ( mpu ) ;
if ( mpu - > state . sync_in )
2022-02-22 20:28:56 -05:00
mpu - > clock . ticks_in + + ;
2020-01-01 20:20:16 +01:00
}
2022-02-20 02:26:27 -05:00
static void
MPU401_NotesOff ( mpu_t * mpu , int i )
2020-01-01 20:20:16 +01:00
{
2022-02-22 20:28:56 -05:00
if ( mpu - > filter . allnotesoff_out & & ! ( mpu - > inputref [ i ] . on & & ( mpu - > inputref [ i ] . key [ 0 ] | mpu - > inputref [ i ] . key [ 1 ] | mpu - > inputref [ i ] . key [ 2 ] | mpu - > inputref [ i ] . key [ 3 ] ) ) ) {
2023-05-29 01:30:51 -04:00
for ( uint8_t j = 0 ; j < 4 ; j + + )
2022-02-22 20:28:56 -05:00
mpu - > chanref [ mpu - > ch_toref [ i ] ] . key [ j ] = 0 ;
midi_raw_out_byte ( 0xb0 | i ) ;
midi_raw_out_byte ( 123 ) ;
midi_raw_out_byte ( 0 ) ;
2020-01-04 03:32:20 +01:00
} else if ( mpu - > chanref [ mpu - > ch_toref [ i ] ] . on ) {
2023-05-29 01:30:51 -04:00
for ( uint8_t key = 0 ; key < 128 ; key + + ) {
2022-02-22 20:28:56 -05:00
if ( ( mpu - > chanref [ mpu - > ch_toref [ i ] ] . M_GETKEY ) & & ! ( mpu - > inputref [ i ] . on & & ( mpu - > inputref [ i ] . M_GETKEY ) ) ) {
midi_raw_out_byte ( 0x80 | i ) ;
midi_raw_out_byte ( key ) ;
midi_raw_out_byte ( 0 ) ;
}
mpu - > chanref [ mpu - > ch_toref [ i ] ] . M_DELKEY ;
}
2020-01-04 03:32:20 +01:00
}
2020-01-01 20:20:16 +01:00
}
/*Input handler for SysEx */
2020-01-19 05:45:05 +01:00
int
2023-08-21 20:24:51 -04:00
MPU401_InputSysex ( void * priv , uint8_t * buffer , uint32_t len , int abort )
2020-01-01 20:20:16 +01:00
{
2023-08-21 20:24:51 -04:00
mpu_t * mpu = ( mpu_t * ) priv ;
2022-02-22 20:28:56 -05:00
int i ;
2020-01-04 03:32:20 +01:00
uint8_t val_ff = 0xff ;
mpu401_log ( " MPU401 Input Sysex \n " ) ;
2022-01-07 18:09:16 +02:00
# ifdef DOSBOX_CODE
if ( mpu - > mode = = M_UART ) {
# else
2022-01-07 23:36:25 +02:00
if ( ! mpu - > intelligent | | mpu - > mode = = M_UART ) {
2022-01-07 18:09:16 +02:00
# endif
2022-02-22 20:28:56 -05:00
/* UART mode input. */
for ( i = 0 ; i < len ; i + + )
MPU401_QueueByte ( mpu , buffer [ i ] ) ;
return 0 ;
2022-01-07 18:09:16 +02:00
}
2020-01-04 03:32:20 +01:00
if ( mpu - > filter . sysex_in ) {
2022-02-22 20:28:56 -05:00
if ( abort ) {
mpu - > state . sysex_in_finished = 1 ;
mpu - > rec_queue_used = 0 ; /*reset also the input queue*/
return 0 ;
}
if ( mpu - > state . sysex_in_finished ) {
if ( mpu - > rec_queue_used > = MPU401_INPUT_QUEUE )
return len ;
MPU401_RecQueueBuffer ( mpu , & val_ff , 1 , 1 ) ;
mpu - > state . sysex_in_finished = 0 ;
mpu - > clock . rec_counter = 0 ;
}
if ( mpu - > rec_queue_used > = MPU401_INPUT_QUEUE )
return len ;
int available = MPU401_INPUT_QUEUE - mpu - > rec_queue_used ;
if ( available > = len ) {
MPU401_RecQueueBuffer ( mpu , buffer , len , 1 ) ;
return 0 ;
} else {
MPU401_RecQueueBuffer ( mpu , buffer , available , 1 ) ;
if ( mpu - > state . sysex_in_finished )
return 0 ;
return ( len - available ) ;
}
2020-01-04 03:32:20 +01:00
} else if ( mpu - > filter . sysex_thru & & mpu - > midi_thru ) {
2022-02-22 20:28:56 -05:00
midi_raw_out_byte ( 0xf0 ) ;
for ( i = 0 ; i < len ; i + + )
midi_raw_out_byte ( * ( buffer + i ) ) ;
2020-01-04 03:32:20 +01:00
}
return 0 ;
2020-01-01 20:20:16 +01:00
}
/*Input handler for MIDI*/
2020-01-19 05:45:05 +01:00
void
2023-08-21 20:24:51 -04:00
MPU401_InputMsg ( void * priv , uint8_t * msg , uint32_t len )
2020-01-01 20:20:16 +01:00
{
2023-08-21 20:24:51 -04:00
mpu_t * mpu = ( mpu_t * ) priv ;
2023-05-29 01:30:51 -04:00
int i ;
int tick ;
2020-01-04 03:32:20 +01:00
static uint8_t old_msg = 0 ;
2022-02-22 20:28:56 -05:00
uint8_t key ;
2023-05-29 01:30:51 -04:00
uint8_t recdata [ 2 ] ;
uint8_t recmsg [ 4 ] ;
int send = 1 ;
int send_thru = 0 ;
int retrigger_thru = 0 ;
int chan ;
int chrefnum ;
2020-01-04 03:32:20 +01:00
/* Abort if sysex transfer is in progress. */
if ( ! mpu - > state . sysex_in_finished ) {
2022-02-22 20:28:56 -05:00
mpu401_log ( " SYSEX in progress \n " ) ;
return ;
2020-01-04 03:32:20 +01:00
}
2020-01-01 20:20:16 +01:00
2020-01-04 03:32:20 +01:00
mpu401_log ( " MPU401 Input Msg \n " ) ;
2020-01-02 18:19:22 +01:00
2020-02-29 19:12:23 +01:00
# ifdef DOSBOX_CODE
2020-01-04 03:32:20 +01:00
if ( mpu - > mode = = M_INTELLIGENT ) {
2020-02-29 19:12:23 +01:00
# else
if ( mpu - > intelligent & & ( mpu - > mode = = M_INTELLIGENT ) ) {
# endif
2022-02-22 20:28:56 -05:00
if ( msg [ 0 ] < 0x80 ) {
/* Expand running status */
msg [ 2 ] = msg [ 1 ] ;
msg [ 1 ] = msg [ 0 ] ;
msg [ 0 ] = old_msg ;
}
old_msg = msg [ 0 ] ;
chan = msg [ 0 ] & 0xf ;
chrefnum = mpu - > ch_toref [ chan ] ;
key = msg [ 1 ] & 0x7f ;
if ( msg [ 0 ] < 0xf0 ) {
/* If non-system msg. */
if ( ! ( mpu - > state . midi_mask & ( 1 < < chan ) ) & & mpu - > filter . all_thru )
send_thru = 1 ;
else if ( mpu - > filter . midi_thru )
send_thru = 1 ;
switch ( msg [ 0 ] & 0xf0 ) {
case 0x80 : /* Note off. */
if ( send_thru ) {
if ( mpu - > chanref [ chrefnum ] . on & & ( mpu - > chanref [ chrefnum ] . M_GETKEY ) )
send_thru = 0 ;
if ( ! mpu - > filter . midi_thru )
break ;
if ( ! ( mpu - > inputref [ chan ] . M_GETKEY ) )
send_thru = 0 ;
mpu - > inputref [ chan ] . M_DELKEY ;
}
break ;
case 0x90 : /* Note on. */
if ( send_thru ) {
if ( mpu - > chanref [ chrefnum ] . on & & ( mpu - > chanref [ chrefnum ] . M_GETKEY ) )
retrigger_thru = 1 ;
if ( ! mpu - > filter . midi_thru )
break ;
if ( mpu - > inputref [ chan ] . M_GETKEY )
retrigger_thru = 1 ;
mpu - > inputref [ chan ] . M_SETKEY ;
}
break ;
case 0xb0 :
if ( msg [ 1 ] > = 120 ) {
send_thru = 0 ;
if ( msg [ 1 ] = = 123 ) {
/* All notes off. */
for ( key = 0 ; key < 128 ; key + + ) {
if ( ! ( mpu - > chanref [ chrefnum ] . on & & ( mpu - > chanref [ chrefnum ] . M_GETKEY ) ) ) {
if ( mpu - > inputref [ chan ] . on & & mpu - > inputref [ chan ] . M_GETKEY ) {
midi_raw_out_byte ( 0x80 | chan ) ;
midi_raw_out_byte ( key ) ;
midi_raw_out_byte ( 0 ) ;
}
mpu - > inputref [ chan ] . M_DELKEY ;
}
}
}
}
2023-06-09 23:46:54 -04:00
break ;
default :
break ;
2022-02-22 20:28:56 -05:00
}
}
if ( ( msg [ 0 ] > = 0xf0 ) | | ( mpu - > state . midi_mask & ( 1 < < chan ) ) ) {
switch ( msg [ 0 ] & 0xf0 ) {
case 0xa0 : /* Aftertouch. */
if ( ! mpu - > filter . bender_in )
send = 0 ;
break ;
case 0xb0 : /* Control change. */
if ( ! mpu - > filter . bender_in & & ( msg [ 1 ] < 64 ) )
send = 0 ;
if ( msg [ 1 ] > = 120 ) {
if ( mpu - > filter . modemsgs_in )
send = 1 ;
}
break ;
case 0xc0 : /* Program change. */
if ( ( mpu - > state . rec ! = M_RECON ) & & ! mpu - > filter . data_in_stop ) {
mpu - > filter . prchg_buf [ chan ] = msg [ 1 ] ;
mpu - > filter . prchg_mask | = 1 < < chan ;
}
break ;
case 0xd0 : /* Ch pressure. */
case 0xe0 : /* Pitch wheel. */
if ( ! mpu - > filter . bender_in )
send = 0 ;
break ;
case 0xf0 : /* System message. */
if ( msg [ 0 ] = = 0xf8 ) {
send = 0 ;
if ( mpu - > clock . active & & mpu - > state . sync_in ) {
send = 0 ; /* Don't pass to host in this mode? */
tick = mpu - > clock . timebase / 24 ;
if ( mpu - > clock . ticks_in ! = tick ) {
if ( ! mpu - > clock . ticks_in | | ( mpu - > clock . ticks_in > ( tick * 2 ) ) )
mpu - > clock . freq_mod * = 2.0 ;
else {
if ( ABS ( mpu - > clock . ticks_in - tick ) = = 1 )
mpu - > clock . freq_mod / = mpu - > clock . ticks_in / ( float ) ( tick * 2 ) ;
else
mpu - > clock . freq_mod / = mpu - > clock . ticks_in / ( float ) ( tick ) ;
}
MPU401_ReCalcClock ( mpu ) ;
2023-08-18 16:39:26 +02:00
MPU401_ReStartClock ( mpu ) ;
2022-02-22 20:28:56 -05:00
}
mpu - > clock . ticks_in = 0 ;
}
} else if ( msg [ 0 ] > 0xf8 ) { /* Realtime. */
if ( ! ( mpu - > filter . rt_in & & ( msg [ 0 ] < = 0xfc ) & & ( msg [ 0 ] > = 0xfa ) ) ) {
recdata [ 0 ] = 0xff ;
recdata [ 1 ] = msg [ 0 ] ;
MPU401_RecQueueBuffer ( mpu , recdata , 2 , 1 ) ;
send = 0 ;
}
} else { /* Common or system. */
send = 0 ;
if ( ( msg [ 0 ] = = 0xf2 ) | | ( msg [ 0 ] = = 0xf3 ) | | ( msg [ 0 ] = = 0xf6 ) ) {
if ( mpu - > filter . commonmsgs_in )
send = 1 ;
if ( mpu - > filter . commonmsgs_thru )
for ( i = 0 ; i < len ; i + + )
midi_raw_out_byte ( msg [ i ] ) ;
}
}
if ( send ) {
recmsg [ 0 ] = 0xff ;
recmsg [ 1 ] = msg [ 0 ] ;
recmsg [ 2 ] = msg [ 1 ] ;
recmsg [ 3 ] = msg [ 2 ] ;
MPU401_RecQueueBuffer ( mpu , recmsg , len + 1 , 1 ) ;
}
if ( mpu - > filter . rt_affection ) {
switch ( msg [ 0 ] ) {
case 0xf2 :
case 0xf3 :
mpu - > state . block_ack = 1 ;
MPU401_WriteCommand ( mpu , 0xb8 ) ; /* Clear play counters. */
break ;
case 0xfa :
mpu - > state . block_ack = 1 ;
MPU401_WriteCommand ( mpu , 0xa ) ; /* Start, play. */
if ( mpu - > filter . rt_out )
midi_raw_out_rt_byte ( msg [ 0 ] ) ;
break ;
case 0xfb :
mpu - > state . block_ack = 1 ;
MPU401_WriteCommand ( mpu , 0xb ) ; /* Continue, play. */
if ( mpu - > filter . rt_out )
midi_raw_out_rt_byte ( msg [ 0 ] ) ;
break ;
case 0xfc :
mpu - > state . block_ack = 1 ;
MPU401_WriteCommand ( mpu , 0xd ) ; /* Stop: Play, rec, midi */
if ( mpu - > filter . rt_out )
midi_raw_out_rt_byte ( msg [ 0 ] ) ;
break ;
2023-06-09 23:46:54 -04:00
default :
break ;
2022-02-22 20:28:56 -05:00
}
return ;
}
2023-06-09 23:46:54 -04:00
break ;
default :
break ;
2022-02-22 20:28:56 -05:00
}
}
if ( send_thru & & mpu - > midi_thru ) {
if ( retrigger_thru ) {
midi_raw_out_byte ( 0x80 | ( msg [ 0 ] & 0xf ) ) ;
midi_raw_out_byte ( msg [ 1 ] ) ;
midi_raw_out_byte ( msg [ 2 ] ) ;
}
for ( i = 0 ; i < len ; i + + )
midi_raw_out_byte ( msg [ i ] ) ;
}
if ( send ) {
if ( mpu - > state . rec = = M_RECON ) {
recmsg [ 0 ] = mpu - > clock . rec_counter ;
recmsg [ 1 ] = msg [ 0 ] ;
recmsg [ 2 ] = msg [ 1 ] ;
recmsg [ 3 ] = msg [ 2 ] ;
MPU401_RecQueueBuffer ( mpu , recmsg , len + 1 , 1 ) ;
mpu - > clock . rec_counter = 0 ;
} else if ( mpu - > filter . data_in_stop ) {
if ( mpu - > filter . timing_in_stop ) {
recmsg [ 0 ] = 0 ;
recmsg [ 1 ] = msg [ 0 ] ;
recmsg [ 2 ] = msg [ 1 ] ;
recmsg [ 3 ] = msg [ 2 ] ;
MPU401_RecQueueBuffer ( mpu , recmsg , len + 1 , 1 ) ;
} else {
recmsg [ 0 ] = msg [ 0 ] ;
recmsg [ 1 ] = msg [ 1 ] ;
recmsg [ 2 ] = msg [ 2 ] ;
recmsg [ 3 ] = 0 ;
MPU401_RecQueueBuffer ( mpu , recmsg , len , 1 ) ;
}
}
}
return ;
2020-01-04 03:32:20 +01:00
}
/* UART mode input. */
2020-01-23 06:40:20 +01:00
for ( i = 0 ; i < len ; i + + )
2022-02-22 20:28:56 -05:00
MPU401_QueueByte ( mpu , msg [ i ] ) ;
2017-05-09 21:45:20 +02:00
}
2020-03-25 00:49:25 +01:00
void
mpu401_setirq ( mpu_t * mpu , int irq )
{
mpu - > irq = irq ;
}
2017-11-23 21:45:10 -05:00
2020-02-29 19:12:23 +01:00
void
mpu401_change_addr ( mpu_t * mpu , uint16_t addr )
{
if ( mpu = = NULL )
2022-02-22 20:28:56 -05:00
return ;
2020-02-29 19:12:23 +01:00
if ( mpu - > addr )
2022-02-22 20:28:56 -05:00
io_removehandler ( mpu - > addr , 2 ,
mpu401_read , NULL , NULL , mpu401_write , NULL , NULL , mpu ) ;
2020-02-29 19:12:23 +01:00
mpu - > addr = addr ;
if ( mpu - > addr )
2022-02-22 20:28:56 -05:00
io_sethandler ( mpu - > addr , 2 ,
mpu401_read , NULL , NULL , mpu401_write , NULL , NULL , mpu ) ;
2020-02-29 19:12:23 +01:00
}
2017-11-23 21:45:10 -05:00
void
2020-01-19 05:45:05 +01:00
mpu401_init ( mpu_t * mpu , uint16_t addr , int irq , int mode , int receive_input )
2017-05-09 21:45:20 +02:00
{
2022-02-22 20:28:56 -05:00
mpu - > status = STATUS_INPUT_NOT_READY ;
mpu - > irq = irq ;
2017-11-23 21:45:10 -05:00
mpu - > queue_used = 0 ;
2022-02-22 20:28:56 -05:00
mpu - > queue_pos = 0 ;
mpu - > mode = M_UART ;
mpu - > addr = addr ;
2017-05-10 14:52:38 +02:00
2018-09-11 22:41:14 +02:00
/* Expalantion:
2022-02-22 20:28:56 -05:00
MPU - 401 starting in intelligent mode = Full MPU - 401 intelligent mode capability ;
MPU - 401 starting in UART mode = Reduced MPU - 401 intelligent mode capability seen on the Sound Blaster 16 / AWE32 ,
only supporting commands 3F ( set UART mode ) and FF ( reset ) . */
2017-11-23 21:45:10 -05:00
mpu - > intelligent = ( mode = = M_INTELLIGENT ) ? 1 : 0 ;
2018-05-21 19:04:05 +02:00
mpu401_log ( " Starting as %s (mode is %s) \n " , mpu - > intelligent ? " INTELLIGENT " : " UART " , ( mode = = M_INTELLIGENT ) ? " INTELLIGENT " : " UART " ) ;
2017-05-10 14:52:38 +02:00
2020-02-29 19:12:23 +01:00
if ( mpu - > addr )
2022-02-22 20:28:56 -05:00
io_sethandler ( mpu - > addr , 2 ,
mpu401_read , NULL , NULL , mpu401_write , NULL , NULL , mpu ) ;
2017-11-23 21:45:10 -05:00
io_sethandler ( 0x2A20 , 16 ,
2022-02-22 20:28:56 -05:00
NULL , NULL , NULL , imf_write , NULL , NULL , mpu ) ;
2020-01-19 05:45:05 +01:00
timer_add ( & mpu - > mpu401_event_callback , MPU401_Event , mpu , 0 ) ;
timer_add ( & mpu - > mpu401_eoi_callback , MPU401_EOIHandler , mpu , 0 ) ;
timer_add ( & mpu - > mpu401_reset_callback , MPU401_ResetDone , mpu , 0 ) ;
2017-11-23 21:45:10 -05:00
MPU401_Reset ( mpu ) ;
2020-01-19 05:45:05 +01:00
if ( receive_input )
2022-02-22 20:28:56 -05:00
midi_in_handler ( 1 , MPU401_InputMsg , MPU401_InputSysex , mpu ) ;
2017-05-09 21:45:20 +02:00
}
2017-06-03 20:32:58 +02:00
2017-11-23 21:45:10 -05:00
void
mpu401_device_add ( void )
2017-06-03 20:32:58 +02:00
{
2020-01-04 03:32:20 +01:00
if ( ! mpu401_standalone_enable )
2022-02-22 20:28:56 -05:00
return ;
2017-06-03 20:32:58 +02:00
2021-12-13 01:23:06 +01:00
if ( machine_has_bus ( machine , MACHINE_BUS_MCA ) )
2022-02-22 20:28:56 -05:00
device_add ( & mpu401_mca_device ) ;
2018-09-11 22:41:14 +02:00
else
2022-02-22 20:28:56 -05:00
device_add ( & mpu401_device ) ;
2018-09-03 13:55:09 +02:00
}
2018-09-11 22:41:14 +02:00
static uint8_t
2023-06-09 23:46:54 -04:00
mpu401_mca_read ( int port , void * priv )
2018-09-03 13:55:09 +02:00
{
2023-07-20 18:58:26 -04:00
const mpu_t * mpu = ( mpu_t * ) priv ;
2018-09-11 22:41:14 +02:00
return mpu - > pos_regs [ port & 7 ] ;
2018-09-03 13:55:09 +02:00
}
2018-09-11 22:41:14 +02:00
static void
2023-06-09 23:46:54 -04:00
mpu401_mca_write ( int port , uint8_t val , void * priv )
2018-09-03 13:55:09 +02:00
{
2023-06-09 23:46:54 -04:00
mpu_t * mpu = ( mpu_t * ) priv ;
2018-09-11 22:41:14 +02:00
uint16_t addr ;
if ( port < 0x102 )
2022-02-22 20:28:56 -05:00
return ;
2018-09-11 22:41:14 +02:00
addr = ( mpu - > pos_regs [ 2 ] & 2 ) ? 0x0330 : 0x1330 ;
port & = 7 ;
mpu - > pos_regs [ port ] = val ;
if ( port = = 2 ) {
2022-02-22 20:28:56 -05:00
io_removehandler ( addr , 2 ,
mpu401_read , NULL , NULL , mpu401_write , NULL , NULL , mpu ) ;
2018-09-11 22:41:14 +02:00
2022-02-22 20:28:56 -05:00
addr = ( mpu - > pos_regs [ 2 ] & 2 ) ? 0x1330 : 0x0330 ;
2018-09-11 22:41:14 +02:00
2022-02-22 20:28:56 -05:00
io_sethandler ( addr , 2 ,
mpu401_read , NULL , NULL , mpu401_write , NULL , NULL , mpu ) ;
2018-09-11 22:41:14 +02:00
}
2017-06-03 20:32:58 +02:00
}
Added the IBM 5161 ISA expansion for PC and XT;
Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port;
Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX);
Finished the 586MC1;
Added 8087 emulation;
Moved Cyrix 6x86'es to the Dev branch;
Sanitized/cleaned up memregs.c/h and intel.c/h;
Split the chipsets from machines and sanitized Port 92 emulation;
Added support for the 15bpp mode to the Compaq ATI 28800;
Moved the MR 386DX and 486 machines to the Dev branch;
Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00;
Ported the new timer code from PCem;
Cleaned up the CPU table of unused stuff and better optimized its structure;
Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch;
Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem;
Added the AHA-1540A and the BusTek BT-542B;
Moved the Sumo SCSI-AT to the Dev branch;
Minor IDE, FDC, and floppy drive code clean-ups;
Made NCR 5380/53C400-based cards' BIOS address configurable;
Got rid of the legacy romset variable;
Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit;
Added the Amstead PPC512 per PCem patch by John Elliott;
Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages);
Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing;
Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem;
Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit;
Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement;
Amstrad MegaPC does now works correctly with non-internal graphics card;
The SLiRP code no longer casts a packed struct type to a non-packed struct type;
The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present;
The S3 Virge on BeOS is no longer broken (was broken by build #1591);
OS/2 2.0 build 6.167 now sees key presses again;
Xi8088 now work on CGA again;
86F images converted from either the old or new variants of the HxC MFM format now work correctly;
Hardware interrupts with a vector of 0xFF are now handled correctly;
OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct;
Fixed VNC keyboard input bugs;
Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver;
Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly;
Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4;
Compaq Portable now works with all graphics cards;
Fixed various MDSI Genius bugs;
Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly;
Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355;
OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400.
Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391.
Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389.
Fixed a minor IDE timing bug, fixes #388.
Fixed Toshiba T1000 RAM issues, fixes #379.
Fixed EGA/(S)VGA overscan border handling, fixes #378;
Got rid of the now long useless IDE channel 2 auto-removal, fixes #370;
Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366;
Ported the Unicode CD image file name fix from VARCem, fixes #365;
Fixed high density floppy disks on the Xi8088, fixes #359;
Fixed some bugs in the Hercules emulation, fixes #346, fixes #358;
Fixed the SCSI hard disk mode sense pages, fixes #356;
Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349;
Fixed bugs in the serial mouse emulation, fixes #344;
Compiled 86Box binaries now include all the required .DLL's, fixes #341;
Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
static uint8_t
2023-06-09 23:46:54 -04:00
mpu401_mca_feedb ( UNUSED ( void * priv ) )
Added the IBM 5161 ISA expansion for PC and XT;
Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port;
Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX);
Finished the 586MC1;
Added 8087 emulation;
Moved Cyrix 6x86'es to the Dev branch;
Sanitized/cleaned up memregs.c/h and intel.c/h;
Split the chipsets from machines and sanitized Port 92 emulation;
Added support for the 15bpp mode to the Compaq ATI 28800;
Moved the MR 386DX and 486 machines to the Dev branch;
Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00;
Ported the new timer code from PCem;
Cleaned up the CPU table of unused stuff and better optimized its structure;
Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch;
Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem;
Added the AHA-1540A and the BusTek BT-542B;
Moved the Sumo SCSI-AT to the Dev branch;
Minor IDE, FDC, and floppy drive code clean-ups;
Made NCR 5380/53C400-based cards' BIOS address configurable;
Got rid of the legacy romset variable;
Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit;
Added the Amstead PPC512 per PCem patch by John Elliott;
Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages);
Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing;
Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem;
Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit;
Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement;
Amstrad MegaPC does now works correctly with non-internal graphics card;
The SLiRP code no longer casts a packed struct type to a non-packed struct type;
The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present;
The S3 Virge on BeOS is no longer broken (was broken by build #1591);
OS/2 2.0 build 6.167 now sees key presses again;
Xi8088 now work on CGA again;
86F images converted from either the old or new variants of the HxC MFM format now work correctly;
Hardware interrupts with a vector of 0xFF are now handled correctly;
OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct;
Fixed VNC keyboard input bugs;
Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver;
Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly;
Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4;
Compaq Portable now works with all graphics cards;
Fixed various MDSI Genius bugs;
Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly;
Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355;
OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400.
Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391.
Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389.
Fixed a minor IDE timing bug, fixes #388.
Fixed Toshiba T1000 RAM issues, fixes #379.
Fixed EGA/(S)VGA overscan border handling, fixes #378;
Got rid of the now long useless IDE channel 2 auto-removal, fixes #370;
Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366;
Ported the Unicode CD image file name fix from VARCem, fixes #365;
Fixed high density floppy disks on the Xi8088, fixes #359;
Fixed some bugs in the Hercules emulation, fixes #346, fixes #358;
Fixed the SCSI hard disk mode sense pages, fixes #356;
Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349;
Fixed bugs in the serial mouse emulation, fixes #344;
Compiled 86Box binaries now include all the required .DLL's, fixes #341;
Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
{
return 1 ;
}
2020-04-12 00:18:55 +02:00
void
mpu401_irq_attach ( mpu_t * mpu , void ( * ext_irq_update ) ( void * priv , int set ) , int ( * ext_irq_pending ) ( void * priv ) , void * priv )
{
2022-02-22 20:28:56 -05:00
mpu - > ext_irq_update = ext_irq_update ;
2020-04-12 00:18:55 +02:00
mpu - > ext_irq_pending = ext_irq_pending ;
2022-02-22 20:28:56 -05:00
mpu - > priv = priv ;
2020-04-12 00:18:55 +02:00
}
2017-11-23 21:45:10 -05:00
static void *
2018-03-19 01:02:04 +01:00
mpu401_standalone_init ( const device_t * info )
2017-06-03 20:32:58 +02:00
{
2022-02-22 20:28:56 -05:00
mpu_t * mpu ;
int irq ;
2018-09-11 22:41:14 +02:00
uint16_t base ;
2017-06-03 20:32:58 +02:00
2017-11-23 21:45:10 -05:00
mpu = malloc ( sizeof ( mpu_t ) ) ;
memset ( mpu , 0 , sizeof ( mpu_t ) ) ;
2020-01-19 05:45:05 +01:00
2018-05-21 19:04:05 +02:00
mpu401_log ( " mpu_init \n " ) ;
2017-06-03 20:32:58 +02:00
2018-09-11 22:41:14 +02:00
if ( info - > flags & DEVICE_MCA ) {
2022-02-22 20:28:56 -05:00
mca_add ( mpu401_mca_read , mpu401_mca_write , mpu401_mca_feedb , NULL , mpu ) ;
mpu - > pos_regs [ 0 ] = 0x0F ;
mpu - > pos_regs [ 1 ] = 0x6C ;
base = 0 ; /* Tell mpu401_init() that this is the MCA variant. */
2022-07-30 00:20:22 +06:00
/* According to @6c0f.adf, the IRQ is supposed to be fixed to 2.
This is only true for earlier models . Later ones have selectable IRQ . */
2022-09-18 17:16:40 -04:00
irq = device_get_config_int ( " irq " ) ;
2018-09-11 22:41:14 +02:00
} else {
2022-02-22 20:28:56 -05:00
base = device_get_config_hex16 ( " base " ) ;
irq = device_get_config_int ( " irq " ) ;
2018-09-11 22:41:14 +02:00
}
2020-01-19 05:45:05 +01:00
mpu401_init ( mpu , base , irq , M_INTELLIGENT , device_get_config_int ( " receive_input " ) ) ;
2022-02-20 02:26:27 -05:00
2023-05-29 01:30:51 -04:00
return mpu ;
2017-06-03 20:32:58 +02:00
}
2017-11-23 21:45:10 -05:00
static void
mpu401_standalone_close ( void * priv )
2017-06-03 20:32:58 +02:00
{
2022-02-22 20:28:56 -05:00
mpu_t * mpu = ( mpu_t * ) priv ;
2017-06-03 20:32:58 +02:00
2017-11-23 21:45:10 -05:00
free ( mpu ) ;
2017-06-03 20:32:58 +02:00
}
2022-02-22 20:28:56 -05:00
static const device_config_t mpu401_standalone_config [ ] = {
2022-09-18 17:16:40 -04:00
// clang-format off
2022-02-22 20:28:56 -05:00
{
2022-04-08 21:41:33 -04:00
. name = " base " ,
. description = " MPU-401 Address " ,
. type = CONFIG_HEX16 ,
. default_string = " " ,
. default_int = 0x330 ,
. file_filter = " " ,
. spinner = { 0 } ,
. selection = {
{
. description = " 0x220 " ,
. value = 0x220
} ,
{
. description = " 0x230 " ,
. value = 0x230
} ,
{
. description = " 0x240 " ,
. value = 0x240
} ,
{
. description = " 0x250 " ,
. value = 0x250
} ,
{
. description = " 0x300 " ,
. value = 0x300
} ,
{
. description = " 0x320 " ,
. value = 0x320
} ,
{
. description = " 0x330 " ,
. value = 0x330
} ,
2022-08-03 13:38:44 +06:00
{
. description = " 0x332 " ,
. value = 0x332
} ,
{
. description = " 0x334 " ,
. value = 0x334
} ,
{
. description = " 0x336 " ,
. value = 0x336
} ,
2022-04-08 21:41:33 -04:00
{
. description = " 0x340 " ,
. value = 0x340
} ,
{
. description = " 0x350 " ,
. value = 0x350
} ,
{ . description = " " }
2020-01-19 05:45:05 +01:00
}
2022-02-22 20:28:56 -05:00
} ,
{
2022-04-08 21:41:33 -04:00
. name = " irq " ,
. description = " MPU-401 IRQ " ,
. type = CONFIG_SELECTION ,
. default_string = " " ,
. default_int = 2 ,
. file_filter = " " ,
. spinner = { 0 } ,
. selection = {
{
. description = " IRQ 2 " ,
. value = 2
} ,
{
. description = " IRQ 3 " ,
. value = 3
} ,
{
. description = " IRQ 4 " ,
. value = 4
} ,
{
. description = " IRQ 5 " ,
. value = 5
} ,
{
. description = " IRQ 6 " ,
. value = 6
} ,
{
. description = " IRQ 7 " ,
. value = 7
} ,
{ . description = " " }
2017-06-03 20:32:58 +02:00
}
2022-02-22 20:28:56 -05:00
} ,
{
. name = " receive_input " ,
. description = " Receive input " ,
. type = CONFIG_BINARY ,
. default_int = 1
} ,
2022-04-08 21:41:33 -04:00
{ . name = " " , . description = " " , . type = CONFIG_END }
2022-09-18 17:16:40 -04:00
// clang-format on
2017-06-03 20:32:58 +02:00
} ;
2022-02-22 20:28:56 -05:00
static const device_config_t mpu401_standalone_mca_config [ ] = {
2022-09-18 17:16:40 -04:00
// clang-format off
2022-07-30 00:20:22 +06:00
{
. name = " irq " ,
. description = " MPU-401 IRQ " ,
. type = CONFIG_SELECTION ,
. default_string = " " ,
. default_int = 9 ,
. file_filter = " " ,
. spinner = { 0 } ,
. selection = {
{
. description = " IRQ 3 " ,
. value = 3
} ,
{
. description = " IRQ 4 " ,
. value = 4
} ,
{
. description = " IRQ 5 " ,
. value = 5
} ,
{
. description = " IRQ 6 " ,
. value = 6
} ,
{
. description = " IRQ 7 " ,
. value = 7
} ,
{
. description = " IRQ 9 " ,
. value = 9
} ,
{ . description = " " }
}
} ,
2022-02-22 20:28:56 -05:00
{
. name = " receive_input " ,
. description = " Receive input " ,
. type = CONFIG_BINARY ,
. default_int = 1
} ,
2022-04-08 21:41:33 -04:00
{ . name = " " , . description = " " , . type = CONFIG_END }
2022-11-19 08:49:04 -05:00
// clang-format on
2022-02-22 20:28:56 -05:00
} ;
2017-11-23 21:45:10 -05:00
2018-03-19 01:02:04 +01:00
const device_t mpu401_device = {
2022-09-18 17:16:40 -04:00
. name = " Roland MPU-IPC-T " ,
2022-03-13 10:03:39 -04:00
. internal_name = " mpu401 " ,
2022-09-18 17:16:40 -04:00
. flags = DEVICE_ISA ,
. local = 0 ,
. init = mpu401_standalone_init ,
. close = mpu401_standalone_close ,
. reset = NULL ,
2022-03-13 10:03:39 -04:00
{ . available = NULL } ,
. speed_changed = NULL ,
2022-09-18 17:16:40 -04:00
. force_redraw = NULL ,
. config = mpu401_standalone_config
2017-06-03 20:32:58 +02:00
} ;
2018-09-03 13:55:09 +02:00
const device_t mpu401_mca_device = {
2022-09-18 17:16:40 -04:00
. name = " Roland MPU-IMC " ,
2022-03-13 10:03:39 -04:00
. internal_name = " mpu401_mca " ,
2022-09-18 17:16:40 -04:00
. flags = DEVICE_MCA ,
. local = 0 ,
. init = mpu401_standalone_init ,
. close = mpu401_standalone_close ,
. reset = NULL ,
2022-03-13 10:03:39 -04:00
{ . available = NULL } ,
. speed_changed = NULL ,
2022-09-18 17:16:40 -04:00
. force_redraw = NULL ,
. config = mpu401_standalone_mca_config
2018-09-03 13:55:09 +02:00
} ;