add: ASMCALL SAVEDS static void PUHSoftInt( REG( d1, UWORD           active_ints ), REG( a0, struct Custom*  custom ), REG( a1, struct PUHData* pd ), REG( a5, void*           me ), REG( a6, struct ExecBase* SysBase ) ) { pd->m_CausePending = FALSE;       #if 1 asm( " movem.l   d0-d1/a0-a1/a5-a6,-(sp) /* Make sure we don't kill the C code */  move      #0x2400,sr        /* Fake a level 4 interrupt */  move.l    %1,a0 move.l    %0,a6 move.w    (%2),d1 and.w     (%3),d1  test_channel_1: btst      #8,d1             /* Audio channel 1? */ beq.s     test_channel_3 movem.l   180(a6),a1/a5     /* Get IntVects[8] handler data. */ pea       int_exit          /* Use special ExitIntr() below. */ jmp       (a5)  test_channel_3: btst      #10,d1            /* Audio channel 3? */ beq.s     test_channel_0 movem.l   204(a6),a1/a5     /* Get IntVects[10] handler data. */ pea       int_exit          /* Use special ExitIntr() below. */ jmp       (a5)  test_channel_0: btst      #7,d1             /* Audio channel 0? */ beq.s     test_channel_2 movem.l   168(a6),a1/a5     /* Get IntVects[7] handler data. */ pea       int_exit          /* Use special ExitIntr() below. */ jmp       (a5)  test_channel_2: btst      #9,d1             /* Audio channel 2? */ beq.s     done movem.l   192(a6),a1/a5     /* Get IntVects[9] handler data. */ pea       int_exit          /* Use special ExitIntr() below. */ jmp       (a5)  * This routine allows a single invocation of the level 4 interrupt * handler to service all level 4 interrupts which are pending or * become pending while one is serviced. int_exit: move.l    %1,a0             /* Point at custom chip register area. */ move.l    %0,a6             /* Get ExecBase */ move.w    #0x0780,d1        /* Mask for all level 4 interrupt bits. */ and.w     (%2),d1 and.w     (%3),d1           /* Find enabled and pending level 4 ints. */ bne       test_channel_1  done: movem.l   (sp)+,d0-d1/a0-a1/a5-a6 " :  : "a" (SysBase), "d" (pd->m_Intercepted), "a" (&pd->m_INTENA), "a" (&pd->m_INTREQ) : "cc", "memory", "d0", "d1", "a0", "a1", "a5", "a6"  ); #endif   }   static void PUHSoftIntWrapper( UWORD           active_ints, struct Custom*  custom, struct PUHData* pd, struct ExecBase* SysBase ) { asm(" movem.l   d0-d1/a0-a1/a5-a6,-(sp) move.w    %0,d1 move.l    %1,a0 move.l    %2,a1 lea       _PUHSoftInt,a5 move.l    %3,a6 jsr       (a5) movem.l   (sp)+,d0-d1/a0-a1/a5-a6 " :  : "r" (active_ints), "r" (custom), "r" (pd), "r" (SysBase) : "d1", "a0", "a1", "a5", "a6" ); }   ASMCALL SAVEDS static void PUHSoundFunc( REG( a0, struct Hook*            hook ), REG( a2, struct AHIAudioCtrl*    actrl ), REG( a1, struct AHISoundMessage* msg ) ) { APTR            stack = NULL; struct PUHData* pd    = (struct PUHData*) hook->h_Data;  pd->m_INTREQ |= ( 1 << ( INTB_AUD0 + msg->ahism_Channel ) );  if( ( pd->m_INTENA & INTF_INTEN ) && ( pd->m_INTENA & pd->m_INTREQ & INTF_AUDIO ) ) {   stack = SuperState();  PUHSoftIntWrapper( 0, pd->m_Custom, pd, SysBase );  if( stack != NULL ) { UserState( stack ); } } }
context: PUHWrite( UWORD            reg,           UWORD            value,          BOOL*            handled,          struct PUHData*  pd,          struct ExecBase* SysBase ){  UWORD* address = (UWORD*) ( (ULONG) pd->m_Custom + reg );  switch( reg )  {    case DMACON:    {      UWORD old_dmacon;      UWORD new_dmacon;      UWORD xor_dmacon;      if( pd->m_DMACON & DMAF_MASTER )      {        old_dmacon = pd->m_DMACON;      }      else      {        old_dmacon = 0;      }      if( value & DMAF_SETCLR )      {        pd->m_DMACON |= ( value & ~DMAF_SETCLR );      }      else      {        pd->m_DMACON &= ~( value & ~DMAF_SETCLR );      }      if( pd->m_DMACON & DMAF_MASTER )      {        new_dmacon = pd->m_DMACON;      }      else      {        new_dmacon = 0;      }      xor_dmacon = old_dmacon ^ new_dmacon;      KPrintF( "DMACON: %04lx => %04lx->%04lx\n", value, old_dmacon, new_dmacon );      if( xor_dmacon & DMAF_AUD0 )      {        if( new_dmacon & DMAF_AUD0 )        {          pd->m_SoundOn[ 0 ] = TRUE;          AHI_SetSound( 0, 0,                        pd->m_SoundLocation[ 0 ],                        pd->m_SoundLength[ 0 ],                        pd->m_AudioCtrl,                        AHISF_IMM );                  }        else        {          pd->m_SoundOn[ 0 ] = FALSE;          AHI_SetSound( 0, AHI_NOSOUND, 0, 0, pd->m_AudioCtrl, AHISF_IMM );        }      }      if( xor_dmacon & DMAF_AUD1 )      {        if( new_dmacon & DMAF_AUD1 )        {          pd->m_SoundOn[ 1 ] = TRUE;          AHI_SetSound( 1, 0,                        pd->m_SoundLocation[ 1 ],                        pd->m_SoundLength[ 1 ],                        pd->m_AudioCtrl,                        AHISF_IMM );                  }        else        {          pd->m_SoundOn[ 1 ] = FALSE;          AHI_SetSound( 1, AHI_NOSOUND, 0, 0, pd->m_AudioCtrl, AHISF_IMM );        }      }      if( xor_dmacon & DMAF_AUD2 )      {        if( new_dmacon & DMAF_AUD2 )        {          pd->m_SoundOn[ 2 ] = TRUE;          AHI_SetSound( 2, 0,                        pd->m_SoundLocation[ 2 ],                        pd->m_SoundLength[ 2 ],                        pd->m_AudioCtrl,                        AHISF_IMM );                  }        else        {          pd->m_SoundOn[ 2 ] = FALSE;          AHI_SetSound( 2, AHI_NOSOUND, 0, 0, pd->m_AudioCtrl, AHISF_IMM );        }      }      if( xor_dmacon & DMAF_AUD3 )      {        if( new_dmacon & DMAF_AUD3 )        {          pd->m_SoundOn[ 3 ] = TRUE;          AHI_SetSound( 3, 0,                        pd->m_SoundLocation[ 3 ],                        pd->m_SoundLength[ 3 ],                        pd->m_AudioCtrl,                        AHISF_IMM );                  }        else        {          pd->m_SoundOn[ 3 ] = FALSE;          AHI_SetSound( 3, AHI_NOSOUND, 0, 0, pd->m_AudioCtrl, AHISF_IMM );        }      }//      WriteWord( address, value & ~DMAF_AUDIO );      WriteWord( address, value );      *handled = TRUE;      break;    }          case INTENA:      if( value & INTF_SETCLR )      {        pd->m_INTENA |= ( value & ~INTF_SETCLR );      }      else      {        pd->m_INTENA &= ~( value & ~INTF_SETCLR );      }      WriteWord( address, value & ~INTF_AUDIO );      if( ( pd->m_INTENA & INTF_INTEN ) &&          ( pd->m_INTENA & pd->m_INTREQ & INTF_AUDIO ) &&          ! pd->m_CausePending )      {        pd->m_CausePending = TRUE;        KPrintF( "Cause\n" );        Cause( &pd->m_SoftInt );      }      *handled = FALSE;      break;    case INTREQ:      if( value & INTF_SETCLR )      {        pd->m_INTREQ |= ( value & ~INTF_SETCLR );      }      else      {        pd->m_INTREQ &= ~( value & ~INTF_SETCLR );      }      KPrintF( "INTREQ: %04lx\n", value );      WriteWord( address, value & ~INTF_AUDIO );      if( ( pd->m_INTENA & INTF_INTEN ) &&          ( pd->m_INTENA & pd->m_INTREQ & INTF_AUDIO ) &&          ! pd->m_CausePending )      {        pd->m_CausePending = TRUE;        KPrintF( "Cause\n" );        Cause( &pd->m_SoftInt );      }      *handled = FALSE;      break;    case ADKCON:      WriteWord( address, value );      *handled = FALSE;      break;    case AUD0LCH:    case AUD1LCH:    case AUD2LCH:    case AUD3LCH:      pd->m_SoundLocation[ ( reg - AUD0LCH ) >> 4 ] &= 0x0000ffff;      pd->m_SoundLocation[ ( reg - AUD0LCH ) >> 4 ] |= value << 16;      WriteWord( address, value );      *handled = TRUE;      break;    case AUD0LCL:    case AUD1LCL:    case AUD2LCL:    case AUD3LCL:    {      int channel = ( reg - AUD0LCL ) >> 4;      pd->m_SoundLocation[ channel ] &= 0xffff0000;      pd->m_SoundLocation[ channel ] |= value;            if( pd->m_SoundOn[ channel ] )      {        // Queue it        AHI_SetSound( channel, 0,                      pd->m_SoundLocation[ channel ],                      pd->m_SoundLength[ channel ],                      pd->m_AudioCtrl,                      AHISF_NONE );      }      WriteWord( address, value );      *handled = TRUE;      break;    }    case AUD0LEN:    case AUD1LEN:    case AUD2LEN:    case AUD3LEN:    {      int channel = ( reg - AUD0LEN ) >> 4;            pd->m_SoundLength[ channel ] = value * 2;            if( pd->m_SoundOn[ channel ] )      {        // Queue it        AHI_SetSound( channel, 0,                      pd->m_SoundLocation[ channel ],                      pd->m_SoundLength[ channel ],                      pd->m_AudioCtrl,                      AHISF_NONE );      }      WriteWord( address, value );      *handled = TRUE;      break;    }    case AUD0PER:    case AUD1PER:    case AUD2PER:    case AUD3PER:    {      int channel = ( reg - AUD0PER ) >> 4;            AHI_SetFreq( channel,                   PALFREQ / value,                    pd->m_AudioCtrl,                   AHISF_IMM );      WriteWord( address, value );      *handled = TRUE;      break;    }    case AUD0VOL:      AHI_SetVol( 0,                  value << 10,                  0x10000,                  pd->m_AudioCtrl,                  AHISF_IMM );      WriteWord( address, value );      *handled = TRUE;      break;    case AUD1VOL:      AHI_SetVol( 1,                  value << 10,                  0x0,                  pd->m_AudioCtrl,                  AHISF_IMM );      WriteWord( address, value );      *handled = TRUE;      break;    case AUD2VOL:      AHI_SetVol( 2,                  value << 10,                  0x0,                  pd->m_AudioCtrl,                  AHISF_IMM );      WriteWord( address, value );      *handled = TRUE;      break;    case AUD3VOL:      AHI_SetVol( 3,                  value << 10,                  0x10000,                  pd->m_AudioCtrl,                  AHISF_IMM );      WriteWord( address, value );      *handled = TRUE;      break;    case AUD0DAT:      KPrintF( "AUD0DAT=%04lx; DMACON: %04lx; INTENA: %04lx\n",      value, pd->m_DMACON, pd->m_INTENA );              if( ( pd->m_DMACON & DMAF_AUD0 ) == 0 )      {        pd->m_INTREQ |= INTF_AUD0;                if( ( pd->m_INTENA & INTF_INTEN ) &&            ( pd->m_INTENA & INTF_AUD0 ) &&            ! pd->m_CausePending )        {          pd->m_CausePending = TRUE;        KPrintF( "Cause\n" );          Cause( &pd->m_SoftInt );        }      }      *handled = TRUE;      break;    case AUD1DAT:      if( ( pd->m_DMACON & DMAF_AUD1 ) == 0 )      {        pd->m_INTREQ |= INTF_AUD1;                if( ( pd->m_INTENA & INTF_INTEN ) &&            ( pd->m_INTENA & INTF_AUD1 ) &&            ! pd->m_CausePending )        {          pd->m_CausePending = TRUE;        KPrintF( "Cause\n" );          Cause( &pd->m_SoftInt );        }      }      *handled = TRUE;      break;    case AUD2DAT:      if( ( pd->m_DMACON & DMAF_AUD2 ) == 0 )      {        pd->m_INTREQ |= INTF_AUD2;                if( ( pd->m_INTENA & INTF_INTEN ) &&            ( pd->m_INTENA & INTF_AUD2 ) &&            ! pd->m_CausePending )        {          pd->m_CausePending = TRUE;        KPrintF( "Cause\n" );          Cause( &pd->m_SoftInt );        }      }      *handled = TRUE;      break;    case AUD3DAT:      if( ( pd->m_DMACON & DMAF_AUD3 ) == 0 )      {        pd->m_INTREQ |= INTF_AUD3;                if( ( pd->m_INTENA & INTF_INTEN ) &&            ( pd->m_INTENA & INTF_AUD3 ) &&            ! pd->m_CausePending )        {          pd->m_CausePending = TRUE;        KPrintF( "Cause\n" );          Cause( &pd->m_SoftInt );        }      }      *handled = TRUE;      break;    default:      // Just carry out the intercepted write operation      WriteWord( address, value );      *handled = FALSE;      break;  }}