Ejemplo n.º 1
0
static void do_toneporta(struct module_data *m,
                         struct channel_data *xc, int note)
{
    struct xmp_instrument *instrument = &m->mod.xxi[xc->ins];
    int mapped = instrument->map[xc->key].ins;
    struct xmp_subinstrument *sub = &instrument->sub[mapped];

    if (note >= 1 && note <= 0x80 && (uint32)xc->ins < m->mod.ins) {
        note--;
        xc->porta.target = note_to_period(note + sub->xpo +
                                          instrument->map[xc->key].xpo, xc->finetune,
                                          HAS_QUIRK(QUIRK_LINEAR), xc->per_adj);
    }
    xc->porta.dir = xc->period < xc->porta.target ? 1 : -1;
}
Ejemplo n.º 2
0
/* virt_on (number of tracks) */
int virt_on(struct context_data *ctx, int num)
{
	struct player_data *p = &ctx->p;
	struct module_data *m = &ctx->m;
	int i;

	p->virt.num_tracks = num;
	num = mixer_numvoices(ctx, -1);

	p->virt.virt_channels = p->virt.num_tracks;

	if (HAS_QUIRK(QUIRK_VIRTUAL)) {
		p->virt.virt_channels += num;
	} else if (num > p->virt.virt_channels) {
		num = p->virt.virt_channels;
	}

	p->virt.maxvoc = mixer_numvoices(ctx, num);

	p->virt.voice_array = calloc(p->virt.maxvoc,
				sizeof(struct mixer_voice));
	if (p->virt.voice_array == NULL)
		goto err;

	for (i = 0; i < p->virt.maxvoc; i++) {
		p->virt.voice_array[i].chn = FREE;
		p->virt.voice_array[i].root = FREE;
	}

	p->virt.virt_channel = malloc(p->virt.virt_channels *
				sizeof(struct virt_channel));
	if (p->virt.virt_channel == NULL)
		goto err1;

	for (i = 0; i < p->virt.virt_channels; i++) {
		p->virt.virt_channel[i].map = FREE;
		p->virt.virt_channel[i].count = 0;
	}

	p->virt.virt_used = 0;

	return 0;

      err1:
	free(p->virt.voice_array);
      err:
	return -1;
}
Ejemplo n.º 3
0
static void set_effect_defaults(struct context_data *ctx, int note,
				struct xmp_subinstrument *sub,
				struct channel_data *xc, int is_toneporta)
{
	struct module_data *m = &ctx->m;

	if (sub != NULL && note >= 0) {
		xc->pan.val = sub->pan;
		xc->finetune = sub->fin;
		xc->gvl = sub->gvl;

		if (sub->ifc & 0x80) {
			xc->filter.cutoff = (sub->ifc - 0x80) * 2;
		} else {
			xc->filter.cutoff = 0xff;
		}

		if (sub->ifr & 0x80) {
			xc->filter.resonance = (sub->ifr - 0x80) * 2;
		} else {
			xc->filter.resonance = 0;
		}

		set_lfo_depth(&xc->insvib.lfo, sub->vde);
		set_lfo_rate(&xc->insvib.lfo, sub->vra >> 2);
		set_lfo_waveform(&xc->insvib.lfo, sub->vwf);
		xc->insvib.sweep = sub->vsw;

		set_lfo_phase(&xc->vibrato, 0);
		set_lfo_phase(&xc->tremolo, 0);

		xc->porta.target = note_to_period(note,
				xc->finetune, HAS_QUIRK(QUIRK_LINEAR),
				xc->per_adj);
		if (xc->period < 1 || !is_toneporta) {
			xc->period = xc->porta.target;
		}
	}
Ejemplo n.º 4
0
static void set_position(struct context_data *ctx, int pos, int dir)
{
	struct player_data *p = &ctx->p;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	struct flow_control *f = &p->flow;
	int seq;
	int has_marker;

	/* If dir is 0, we can jump to a different sequence */
	if (dir == 0) {
		seq = libxmp_get_sequence(ctx, pos);
	} else {
		seq = p->sequence;
	}

	if (seq == 0xff) {
		return;
	}

	has_marker = HAS_QUIRK(QUIRK_MARKER);

	if (seq >= 0) {
		int start = m->seq_data[seq].entry_point;

		p->sequence = seq;

		if (pos >= 0) {
			int pat;

			while (has_marker && mod->xxo[pos] == 0xfe) {
				if (dir < 0) {
					if (pos > start) {
						pos--;
					}
				} else {
					pos++;
				}
			}
			pat = mod->xxo[pos];

			if (pat < mod->pat) {
				if (has_marker && pat == 0xff) {
					return;
				}

				if (pos > p->scan[seq].ord) {
					f->end_point = 0;
				} else {
					f->num_rows = mod->xxp[pat]->rows;
					f->end_point = p->scan[seq].num;
					f->jumpline = 0;
				}
			}
		}

		if (pos < mod->len) {
			if (pos == 0) {
				p->pos = -1;
			} else {
				p->pos = pos;
			}
		}
	}
}
Ejemplo n.º 5
0
void process_fx(struct context_data *ctx, struct channel_data *xc, int chn,
                uint8 note, uint8 fxt, uint8 fxp, int fnum)
{
    struct player_data *p = &ctx->p;
    struct module_data *m = &ctx->m;
    struct xmp_module *mod = &m->mod;
    struct flow_control *f = &p->flow;
    int h, l;

    switch (fxt) {
    case FX_ARPEGGIO:
fx_arpeggio:
        if (fxp != 0) {
            xc->arpeggio.val[0] = 0;
            xc->arpeggio.val[1] = MSN(fxp);
            xc->arpeggio.val[2] = LSN(fxp);
            xc->arpeggio.size = 3;
        }
        break;
    case FX_S3M_ARPEGGIO:
        EFFECT_MEMORY(fxp, xc->arpeggio.memory);
        goto fx_arpeggio;
#ifndef LIBXMP_CORE_PLAYER
    case FX_OKT_ARP3:
        if (fxp != 0) {
            xc->arpeggio.val[0] = -MSN(fxp);
            xc->arpeggio.val[1] = 0;
            xc->arpeggio.val[2] = LSN(fxp);
            xc->arpeggio.size = 3;
        }
        break;
    case FX_OKT_ARP4:
        if (fxp != 0) {
            xc->arpeggio.val[0] = 0;
            xc->arpeggio.val[1] = LSN(fxp);
            xc->arpeggio.val[2] = 0;
            xc->arpeggio.val[3] = -MSN(fxp);
            xc->arpeggio.size = 4;
        }
        break;
    case FX_OKT_ARP5:
        if (fxp != 0) {
            xc->arpeggio.val[0] = LSN(fxp);
            xc->arpeggio.val[1] = LSN(fxp);
            xc->arpeggio.val[2] = 0;
            xc->arpeggio.size = 3;
        }
        break;
#endif
    case FX_PORTA_UP:	/* Portamento up */
        EFFECT_MEMORY(fxp, xc->freq.memory);

        if (HAS_QUIRK(QUIRK_FINEFX)
                && (fnum == 0 || !HAS_QUIRK(QUIRK_ITVPOR))) {
            switch (MSN(fxp)) {
            case 0xf:
                fxp &= 0x0f;
                goto fx_f_porta_up;
            case 0xe:
                fxp &= 0x0f;
                fxp |= 0x10;
                goto fx_xf_porta;
            }
        }

        SET(PITCHBEND);

        if (fxp != 0) {
            xc->freq.slide = -fxp;
            if (HAS_QUIRK(QUIRK_UNISLD))
                xc->porta.memory = fxp;
        } else if (xc->freq.slide > 0) {
            xc->freq.slide *= -1;
        }
        break;
    case FX_PORTA_DN:	/* Portamento down */
        EFFECT_MEMORY(fxp, xc->freq.memory);

        if (HAS_QUIRK(QUIRK_FINEFX)
                && (fnum == 0 || !HAS_QUIRK(QUIRK_ITVPOR))) {
            switch (MSN(fxp)) {
            case 0xf:
                fxp &= 0x0f;
                goto fx_f_porta_dn;
            case 0xe:
                fxp &= 0x0f;
                fxp |= 0x20;
                goto fx_xf_porta;
            }
        }

        SET(PITCHBEND);

        if (fxp != 0) {
            xc->freq.slide = fxp;
            if (HAS_QUIRK(QUIRK_UNISLD))
                xc->porta.memory = fxp;
        } else if (xc->freq.slide < 0) {
            xc->freq.slide *= -1;
        }
        break;
    case FX_TONEPORTA:	/* Tone portamento */
        if (HAS_QUIRK(QUIRK_IGSTPOR)) {
            if (note == 0 && xc->porta.dir == 0)
                break;
        }
        if (!IS_VALID_INSTRUMENT(xc->ins))
            break;

        do_toneporta(m, xc, note);

        EFFECT_MEMORY_SETONLY(fxp, xc->porta.memory);

        if (fxp != 0) {
            if (HAS_QUIRK(QUIRK_UNISLD)) /* IT compatible Gxx off */
                xc->freq.memory = fxp;
            xc->porta.slide = fxp;
        }
        SET(TONEPORTA);
        break;

    case FX_VIBRATO:	/* Vibrato */
        EFFECT_MEMORY_SETONLY(fxp, xc->vibrato.memory);

        SET(VIBRATO);
        SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 2, MSN(fxp));
        break;
    case FX_FINE_VIBRATO:	/* Fine vibrato */
        EFFECT_MEMORY_SETONLY(fxp, xc->vibrato.memory);

        SET(VIBRATO);
        SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp), MSN(fxp));
        break;

    case FX_TONE_VSLIDE:	/* Toneporta + vol slide */
        if (!IS_VALID_INSTRUMENT(xc->ins))
            break;
        do_toneporta(m, xc, note);
        SET(TONEPORTA);
        goto fx_volslide;
    case FX_VIBRA_VSLIDE:	/* Vibrato + vol slide */
        SET(VIBRATO);
        goto fx_volslide;

    case FX_TREMOLO:	/* Tremolo */
        EFFECT_MEMORY_SETONLY(fxp, xc->tremolo.memory);
        SET(TREMOLO);
        SET_LFO_NOTZERO(&xc->tremolo.lfo, LSN(fxp), MSN(fxp));
        break;

    case FX_SETPAN:		/* Set pan */
        xc->pan.val = fxp;
        break;
    case FX_OFFSET:		/* Set sample offset */
        SET(OFFSET);
        if (fxp) {
            xc->offset_val = xc->offset = fxp << 8;
        } else {
            xc->offset_val = xc->offset;
        }
        break;
    case FX_VOLSLIDE:	/* Volume slide */
fx_volslide:
        /* S3M file volume slide note:
         * DFy  Fine volume down by y (...) If y is 0, the command will
         *      be treated as a volume slide up with a value of f (15).
         *      If a DFF command is specified, the volume will be slid
         *      up.
         */
        if (HAS_QUIRK(QUIRK_FINEFX)) {
            h = MSN(fxp);
            l = LSN(fxp);
            if (l == 0xf && h != 0) {
                xc->vol.memory = fxp;
                fxp >>= 4;
                goto fx_f_vslide_up;
            } else if (h == 0xf && l != 0) {
                xc->vol.memory = fxp;
                fxp &= 0x0f;
                goto fx_f_vslide_dn;
            }
        }
Ejemplo n.º 6
0
static int
init_keyboard(KBDC kbdc, int *type, int flags)
{
	int codeset;
	int id;
	int c;

	if (!kbdc_lock(kbdc, TRUE)) {
		/* driver error? */
		return EIO;
	}

	/* temporarily block data transmission from the keyboard */
	write_controller_command(kbdc, KBDC_DISABLE_KBD_PORT);

	/* save the current controller command byte */
	empty_both_buffers(kbdc, 200);
	c = get_controller_command_byte(kbdc);
	if (c == -1) {
		/* CONTROLLER ERROR */
		kbdc_lock(kbdc, FALSE);
		printf("atkbd: unable to get the current command byte value.\n");
		return EIO;
	}
	if (bootverbose)
		printf("atkbd: the current kbd controller command byte %04x\n",
		   c);
#if 0
	/* override the keyboard lock switch */
	c |= KBD_OVERRIDE_KBD_LOCK;
#endif

	/* enable the keyboard port, but disable the keyboard intr. */
	if (setup_kbd_port(kbdc, TRUE, FALSE)) {
		/* CONTROLLER ERROR: there is very little we can do... */
		printf("atkbd: unable to set the command byte.\n");
		kbdc_lock(kbdc, FALSE);
		return EIO;
	}

	if (HAS_QUIRK(kbdc, KBDC_QUIRK_RESET_AFTER_PROBE) &&
	    atkbd_reset(kbdc, flags, c)) {
		kbdc_lock(kbdc, FALSE);
		return EIO;
	}

	/* 
	 * Check if we have an XT keyboard before we attempt to reset it. 
	 * The procedure assumes that the keyboard and the controller have 
	 * been set up properly by BIOS and have not been messed up 
	 * during the boot process.
	 */
	codeset = -1;
	if (flags & KB_CONF_ALT_SCANCODESET)
		/* the user says there is a XT keyboard */
		codeset = 1;
#ifdef KBD_DETECT_XT_KEYBOARD
	else if ((c & KBD_TRANSLATION) == 0) {
		/* SET_SCANCODE_SET is not always supported; ignore error */
		if (send_kbd_command_and_data(kbdc, KBDC_SET_SCANCODE_SET, 0)
			== KBD_ACK) 
			codeset = read_kbd_data(kbdc);
	}
	if (bootverbose)
		printf("atkbd: scancode set %d\n", codeset);
#endif /* KBD_DETECT_XT_KEYBOARD */
 
	*type = KB_OTHER;
	id = get_kbd_id(kbdc);
	switch(id) {
	case 0x41ab:	/* 101/102/... Enhanced */
	case 0x83ab:	/* ditto */
	case 0x54ab:	/* SpaceSaver */
	case 0x84ab:	/* ditto */
#if 0
	case 0x90ab:	/* 'G' */
	case 0x91ab:	/* 'P' */
	case 0x92ab:	/* 'A' */
#endif
		*type = KB_101;
		break;
	case -1:	/* AT 84 keyboard doesn't return ID */
		*type = KB_84;
		break;
	default:
		break;
	}
	if (bootverbose)
		printf("atkbd: keyboard ID 0x%x (%d)\n", id, *type);

	if (!HAS_QUIRK(kbdc, KBDC_QUIRK_RESET_AFTER_PROBE) &&
	    atkbd_reset(kbdc, flags, c)) {
		kbdc_lock(kbdc, FALSE);
		return EIO;
	}

	/*
	 * Allow us to set the XT_KEYBD flag so that keyboards
	 * such as those on the IBM ThinkPad laptop computers can be used
	 * with the standard console driver.
	 */
	if (codeset == 1) {
		if (send_kbd_command_and_data(kbdc,
			KBDC_SET_SCANCODE_SET, codeset) == KBD_ACK) {
			/* XT kbd doesn't need scan code translation */
			c &= ~KBD_TRANSLATION;
		} else {
			/*
			 * KEYBOARD ERROR 
			 * The XT kbd isn't usable unless the proper scan
			 * code set is selected. 
			 */
			set_controller_command_byte(kbdc, ALLOW_DISABLE_KBD(kbdc)
			    ? 0xff : KBD_KBD_CONTROL_BITS, c);
			kbdc_lock(kbdc, FALSE);
			printf("atkbd: unable to set the XT keyboard mode.\n");
			return EIO;
		}
	}

#if defined(__sparc64__)
	if (send_kbd_command_and_data(
		kbdc, KBDC_SET_SCANCODE_SET, 2) != KBD_ACK) {
		printf("atkbd: can't set translation.\n");
	}
	c |= KBD_TRANSLATION;
#endif

	/*
	 * Some keyboards require a SETLEDS command to be sent after
	 * the reset command before they will send keystrokes to us
	 */
	if (HAS_QUIRK(kbdc, KBDC_QUIRK_SETLEDS_ON_INIT) &&
	    send_kbd_command_and_data(kbdc, KBDC_SET_LEDS, 0) != KBD_ACK) {
		printf("atkbd: setleds failed\n");
	}
	if (!ALLOW_DISABLE_KBD(kbdc))
	    send_kbd_command(kbdc, KBDC_ENABLE_KBD);

	/* enable the keyboard port and intr. */
	if (!set_controller_command_byte(kbdc, 
		KBD_KBD_CONTROL_BITS | KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK,
		(c & (KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK))
		    | KBD_ENABLE_KBD_PORT | KBD_ENABLE_KBD_INT)) {
		/*
		 * CONTROLLER ERROR 
		 * This is serious; we are left with the disabled
		 * keyboard intr. 
		 */
		set_controller_command_byte(kbdc, ALLOW_DISABLE_KBD(kbdc)
		    ? 0xff : (KBD_KBD_CONTROL_BITS | KBD_TRANSLATION |
			KBD_OVERRIDE_KBD_LOCK), c);
		kbdc_lock(kbdc, FALSE);
		printf("atkbd: unable to enable the keyboard port and intr.\n");
		return EIO;
	}

	kbdc_lock(kbdc, FALSE);
	return 0;
}
Ejemplo n.º 7
0
static int
probe_keyboard(KBDC kbdc, int flags)
{
	/*
	 * Don't try to print anything in this function.  The low-level 
	 * console may not have been initialized yet...
	 */
	int err;
	int c;
	int m;

	if (!kbdc_lock(kbdc, TRUE)) {
		/* driver error? */
		return ENXIO;
	}

	/* temporarily block data transmission from the keyboard */
	write_controller_command(kbdc, KBDC_DISABLE_KBD_PORT);

	/* flush any noise in the buffer */
	empty_both_buffers(kbdc, 100);

	/* save the current keyboard controller command byte */
	m = kbdc_get_device_mask(kbdc) & ~KBD_KBD_CONTROL_BITS;
	c = get_controller_command_byte(kbdc);
	if (c == -1) {
		/* CONTROLLER ERROR */
		kbdc_set_device_mask(kbdc, m);
		kbdc_lock(kbdc, FALSE);
		return ENXIO;
	}

	/* 
	 * The keyboard may have been screwed up by the boot block.
	 * We may just be able to recover from error by testing the controller
	 * and the keyboard port. The controller command byte needs to be
	 * saved before this recovery operation, as some controllers seem 
	 * to set the command byte to particular values.
	 */
	test_controller(kbdc);
	if (!(flags & KB_CONF_NO_PROBE_TEST))
		test_kbd_port(kbdc);

	err = get_kbd_echo(kbdc);

	/*
	 * Even if the keyboard doesn't seem to be present (err != 0),
	 * we shall enable the keyboard port and interrupt so that
	 * the driver will be operable when the keyboard is attached
	 * to the system later.  It is NOT recommended to hot-plug
	 * the AT keyboard, but many people do so...
	 */
	kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS);
	setup_kbd_port(kbdc, TRUE, TRUE);
#if 0
	if (err == 0) {
		kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS);
	} else {
		/* try to restore the command byte as before */
		set_controller_command_byte(kbdc,
		    ALLOW_DISABLE_KBD(kbdc) ? 0xff : KBD_KBD_CONTROL_BITS, c);
		kbdc_set_device_mask(kbdc, m);
	}
#endif

	kbdc_lock(kbdc, FALSE);
	return (HAS_QUIRK(kbdc, KBDC_QUIRK_IGNORE_PROBE_RESULT) ? 0 : err);
}
Ejemplo n.º 8
0
void process_fx(struct context_data *ctx, struct channel_data *xc, int chn,
		struct xmp_event *e, int fnum)
{
	struct player_data *p = &ctx->p;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	struct flow_control *f = &p->flow;
	uint8 note, fxp, fxt;
	int h, l;

	/* key_porta is IT only */
	if (m->read_event_type != READ_EVENT_IT) {
		xc->key_porta = xc->key;
	}

	note = e->note;
	if (fnum == 0) {
		fxt = e->fxt;
		fxp = e->fxp;
	} else {
		fxt = e->f2t;
		fxp = e->f2p;
	}

	switch (fxt) {
	case FX_ARPEGGIO:
	      fx_arpeggio:
		if (fxp != 0) {
			xc->arpeggio.val[0] = 0;
			xc->arpeggio.val[1] = MSN(fxp);
			xc->arpeggio.val[2] = LSN(fxp);
			xc->arpeggio.size = 3;
		}
		break;
	case FX_S3M_ARPEGGIO:
		EFFECT_MEMORY(fxp, xc->arpeggio.memory);
		goto fx_arpeggio;
#ifndef LIBXMP_CORE_PLAYER
	case FX_OKT_ARP3:
		if (fxp != 0) {
			xc->arpeggio.val[0] = -MSN(fxp);
			xc->arpeggio.val[1] = 0;
			xc->arpeggio.val[2] = LSN(fxp);
			xc->arpeggio.size = 3;
		}
		break;
	case FX_OKT_ARP4:
		if (fxp != 0) {
			xc->arpeggio.val[0] = 0;
			xc->arpeggio.val[1] = LSN(fxp);
			xc->arpeggio.val[2] = 0;
			xc->arpeggio.val[3] = -MSN(fxp);
			xc->arpeggio.size = 4;
		}
		break;
	case FX_OKT_ARP5:
		if (fxp != 0) {
			xc->arpeggio.val[0] = LSN(fxp);
			xc->arpeggio.val[1] = LSN(fxp);
			xc->arpeggio.val[2] = 0;
			xc->arpeggio.size = 3;
		}
		break;
#endif
	case FX_PORTA_UP:	/* Portamento up */
		EFFECT_MEMORY(fxp, xc->freq.memory);

		if (HAS_QUIRK(QUIRK_FINEFX)
		    && (fnum == 0 || !HAS_QUIRK(QUIRK_ITVPOR))) {
			switch (MSN(fxp)) {
			case 0xf:
				fxp &= 0x0f;
				goto fx_f_porta_up;
			case 0xe:
				fxp &= 0x0f;
				fxp |= 0x10;
				goto fx_xf_porta;
			}
		}

		SET(PITCHBEND);

		if (fxp != 0) {
			xc->freq.slide = -fxp;
			if (HAS_QUIRK(QUIRK_UNISLD))
				xc->porta.memory = fxp;
		} else if (xc->freq.slide > 0) {
			xc->freq.slide *= -1;
		}
		break;
	case FX_PORTA_DN:	/* Portamento down */
		EFFECT_MEMORY(fxp, xc->freq.memory);

		if (HAS_QUIRK(QUIRK_FINEFX)
		    && (fnum == 0 || !HAS_QUIRK(QUIRK_ITVPOR))) {
			switch (MSN(fxp)) {
			case 0xf:
				fxp &= 0x0f;
				goto fx_f_porta_dn;
			case 0xe:
				fxp &= 0x0f;
				fxp |= 0x20;
				goto fx_xf_porta;
			}
		}

		SET(PITCHBEND);

		if (fxp != 0) {
			xc->freq.slide = fxp;
			if (HAS_QUIRK(QUIRK_UNISLD))
				xc->porta.memory = fxp;
		} else if (xc->freq.slide < 0) {
			xc->freq.slide *= -1;
		}
		break;
	case FX_TONEPORTA:	/* Tone portamento */
		if (HAS_QUIRK(QUIRK_IGSTPOR)) {
			if (note == 0 && xc->porta.dir == 0)
				break;
		}
		if (!IS_VALID_INSTRUMENT(xc->ins))
			break;

		do_toneporta(m, xc, note);

		EFFECT_MEMORY_SETONLY(fxp, xc->porta.memory);

		if (fxp != 0) {
			if (HAS_QUIRK(QUIRK_UNISLD)) /* IT compatible Gxx off */
				xc->freq.memory = fxp;
			xc->porta.slide = fxp;
		}
		SET(TONEPORTA);
		break;

	case FX_VIBRATO:	/* Vibrato */
		EFFECT_MEMORY_SETONLY(fxp, xc->vibrato.memory);

		SET(VIBRATO);
		SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 2, MSN(fxp));
		break;
	case FX_FINE_VIBRATO:	/* Fine vibrato */
		EFFECT_MEMORY_SETONLY(fxp, xc->vibrato.memory);

		SET(VIBRATO);
		SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp), MSN(fxp));
		break;

	case FX_TONE_VSLIDE:	/* Toneporta + vol slide */
		if (!IS_VALID_INSTRUMENT(xc->ins))
			break;
		do_toneporta(m, xc, note);
		SET(TONEPORTA);
		goto fx_volslide;
	case FX_VIBRA_VSLIDE:	/* Vibrato + vol slide */
		SET(VIBRATO);
		goto fx_volslide;

	case FX_TREMOLO:	/* Tremolo */
		EFFECT_MEMORY_SETONLY(fxp, xc->tremolo.memory);
		SET(TREMOLO);
		SET_LFO_NOTZERO(&xc->tremolo.lfo, LSN(fxp), MSN(fxp));
		break;

	case FX_SETPAN:		/* Set pan */
		/* From OpenMPT PanOff.xm:
		 * "Another chapter of weird FT2 bugs: Note-Off + Note Delay
		 *  + Volume Column Panning = Panning effect is ignored."
		 */
		if (!HAS_QUIRK(QUIRK_FT2BUGS)		/* If not FT2 */
		    || fnum == 0			/* or not vol column */
		    || e->note != XMP_KEY_OFF		/* or not keyoff */
		    || e->fxt != FX_EXTENDED		/* or not delay */
		    || MSN(e->fxp) != EX_DELAY) {
			xc->pan.val = fxp;
			xc->pan.surround = 0;
		}
		break;
	case FX_OFFSET:		/* Set sample offset */
		EFFECT_MEMORY(fxp, xc->offset.memory);
		SET(OFFSET);
		if (note) {
			xc->offset.val &= xc->offset.val & ~0xffff;
			xc->offset.val |= fxp << 8;
			xc->offset.val2 = fxp << 8;
		}
		if (e->ins) {
			xc->offset.val2 = fxp << 8;
		}
		break;
	case FX_VOLSLIDE:	/* Volume slide */
	      fx_volslide:
		/* S3M file volume slide note:
		 * DFy  Fine volume down by y (...) If y is 0, the command will
		 *      be treated as a volume slide up with a value of f (15).
		 *      If a DFF command is specified, the volume will be slid
		 *      up.
		 */
		if (HAS_QUIRK(QUIRK_FINEFX)) {
			h = MSN(fxp);
			l = LSN(fxp);
			if (l == 0xf && h != 0) {
				xc->vol.memory = fxp;
				fxp >>= 4;
				goto fx_f_vslide_up;
			} else if (h == 0xf && l != 0) {
				xc->vol.memory = fxp;
				fxp &= 0x0f;
				goto fx_f_vslide_dn;
			}
		}