mirror of
https://github.com/Gehstock/Mist_FPGA.git
synced 2026-02-09 09:41:09 +00:00
768 lines
22 KiB
C
768 lines
22 KiB
C
// license:GPL-2.0+
|
|
// copyright-holders:Jarek Burczynski
|
|
/*
|
|
**
|
|
** File: ymf262.c - software implementation of YMF262
|
|
** FM sound generator type OPL3
|
|
**
|
|
** Copyright Jarek Burczynski
|
|
**
|
|
** Version 0.2
|
|
**
|
|
|
|
Revision History:
|
|
|
|
03-03-2003: initial release
|
|
- thanks to Olivier Galibert and Chris Hardy for YMF262 and YAC512 chips
|
|
- thanks to Stiletto for the datasheets
|
|
|
|
Features as listed in 4MF262A6 data sheet:
|
|
1. Registers are compatible with YM3812 (OPL2) FM sound source.
|
|
2. Up to six sounds can be used as four-operator melody sounds for variety.
|
|
3. 18 simultaneous melody sounds, or 15 melody sounds with 5 rhythm sounds (with two operators).
|
|
4. 6 four-operator melody sounds and 6 two-operator melody sounds, or 6 four-operator melody
|
|
sounds, 3 two-operator melody sounds and 5 rhythm sounds (with four operators).
|
|
5. 8 selectable waveforms.
|
|
6. 4-channel sound output.
|
|
7. YMF262 compabile DAC (YAC512) is available.
|
|
8. LFO for vibrato and tremolo effedts.
|
|
9. 2 programable timers.
|
|
10. Shorter register access time compared with YM3812.
|
|
11. 5V single supply silicon gate CMOS process.
|
|
12. 24 Pin SOP Package (YMF262-M), 48 Pin SQFP Package (YMF262-S).
|
|
|
|
|
|
differences between OPL2 and OPL3 not documented in Yamaha datahasheets:
|
|
- sinus table is a little different: the negative part is off by one...
|
|
|
|
- in order to enable selection of four different waveforms on OPL2
|
|
one must set bit 5 in register 0x01(test).
|
|
on OPL3 this bit is ignored and 4-waveform select works *always*.
|
|
(Don't confuse this with OPL3's 8-waveform select.)
|
|
|
|
- Envelope Generator: all 15 x rates take zero time on OPL3
|
|
(on OPL2 15 0 and 15 1 rates take some time while 15 2 and 15 3 rates
|
|
take zero time)
|
|
|
|
- channel calculations: output of operator 1 is in perfect sync with
|
|
output of operator 2 on OPL3; on OPL and OPL2 output of operator 1
|
|
is always delayed by one sample compared to output of operator 2
|
|
|
|
|
|
differences between OPL2 and OPL3 shown in datasheets:
|
|
- YMF262 does not support CSM mode
|
|
|
|
|
|
*/
|
|
|
|
//#include "stdafx.h"
|
|
#include <string.h>
|
|
#include <sys.h>
|
|
#include "ymf262.h"
|
|
|
|
#define PITCH_COEF 1154
|
|
//#define PITCH_COEF 1024
|
|
|
|
#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
|
|
#define EG_SH 16 /* 16.16 fixed point (EG timing) */
|
|
#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
|
|
|
|
#define FREQ_MASK ((1<<FREQ_SH)-1)
|
|
|
|
OPL3 chip = {0xcc, 0xcd};
|
|
|
|
/* mapping of register number (offset) to slot number used by the emulator */
|
|
static const int8_t slot_array[32] = { 0, 2, 4, 1, 3, 5,-1,-1, 6, 8,10, 7, 9,11,-1,-1, 12,14,16,13,15,17,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1 };
|
|
static const uint8_t ksl_tab[8 * 16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,12,16,20,24,28,32,0,0,0,0,0,12,20,28,32,40,44,48,52,56,60,64,0,0,0,20,32,44,52,60,64,72,76,80,84,88,92,96,0,0,32,52,64,76,84,92,96,104,108,112,116,120,124,128,0,32,64,84,96,108,116,124,128,136,140,144,148,152,156,160,0,64,96,116,128,140,148,156,160,168,172,176,180,184,188,192,0,96,128,148,160,172,180,188,192,200,204,208,212,216,220,224 };
|
|
static const uint8_t ksl_shift[4] = { 31, 1, 2, 0 };
|
|
static const uint8_t sl_tab[16] = { 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 248 };
|
|
|
|
static const uint8_t eg_rate_select[16 + 64 + 16] = { 112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24,32,40,48,56,64,72,80,88,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96 };
|
|
static const uint8_t eg_rate_shift[16 + 64 + 16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,12,11,11,11,11,10,10,10,10,9,9,9,9,8,8,8,8,7,7,7,7,6,6,6,6,5,5,5,5,4,4,4,4,3,3,3,3,2,2,2,2,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
|
|
static const uint8_t mul_tab[16]= { 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 }; /* multiple table */
|
|
|
|
|
|
static void FM_KEYON(OPL3_SLOT *SLOT, uint8_t key_set)
|
|
{
|
|
if( !SLOT->key )
|
|
{
|
|
SLOT->Cnt = 0; /* restart Phase Generator */
|
|
SLOT->state = EG_ATT; /* phase -> Attack */
|
|
}
|
|
SLOT->key |= key_set;
|
|
}
|
|
|
|
static void FM_KEYOFF(OPL3_SLOT *SLOT, uint8_t key_clr)
|
|
{
|
|
if( SLOT->key )
|
|
{
|
|
SLOT->key &= key_clr;
|
|
if( !SLOT->key )
|
|
if (SLOT->state>EG_REL) SLOT->state = EG_REL; /* phase -> Release */
|
|
}
|
|
}
|
|
|
|
/* update phase increment counter of operator (also update the EG rates if necessary) */
|
|
static void CALC_FCSLOT(OPL3_CH *CH, OPL3_SLOT *SLOT)
|
|
{
|
|
uint8_t ksr;
|
|
|
|
/* (frequency) phase increment counter */
|
|
uint8_t shift = 7 - (CH->block_fnum >> 10);
|
|
SLOT->Incr = ((CH->block_fnum & 0x03ff) * (uint32_t)SLOT->mul << 2) >> shift;
|
|
ksr = CH->kcode >> SLOT->KSR;
|
|
|
|
if( SLOT->ksr != ksr )
|
|
{
|
|
SLOT->ksr = ksr;
|
|
ksr += SLOT->ar;
|
|
/* calculate envelope generator rates */
|
|
if (ksr < 16+60)
|
|
{
|
|
SLOT->eg_sh_ar = eg_rate_shift [ksr];
|
|
SLOT->eg_sel_ar = eg_rate_select[ksr];
|
|
}
|
|
else
|
|
{
|
|
SLOT->eg_sh_ar = 0;
|
|
SLOT->eg_sel_ar = 13*RATE_STEPS;
|
|
}
|
|
ksr = SLOT->dr + SLOT->ksr;
|
|
SLOT->eg_sh_dr = eg_rate_shift [ksr];
|
|
SLOT->eg_sel_dr = eg_rate_select[ksr];
|
|
ksr = SLOT->rr + SLOT->ksr;
|
|
SLOT->eg_sh_rr = eg_rate_shift [ksr];
|
|
SLOT->eg_sel_rr = eg_rate_select[ksr];
|
|
}
|
|
}
|
|
|
|
/* set multi,am,vib,EG-TYP,KSR,mul */
|
|
static void set_mul(uint8_t slot, uint8_t v)
|
|
{
|
|
OPL3_CH *CH = &chip.P_CH[slot >> 1];
|
|
OPL3_SLOT *SLOT = (slot & 1) ? &CH->SLOT1 : &CH->SLOT0;
|
|
|
|
SLOT->mul = PITCH_COEF*mul_tab[v & 0x0f];
|
|
SLOT->KSR = (v & 0x10) ? 0 : 2;
|
|
SLOT->eg_type = !!(v & 0x20);
|
|
SLOT->vib = !!(v & 0x40);
|
|
SLOT->AMmask_TLL = ((uint16_t)(v & 0x80) << 8) + (SLOT->AMmask_TLL & 0x7fff);
|
|
|
|
if (chip.OPL3_mode)
|
|
{
|
|
int8_t chan_no = slot >> 1;
|
|
|
|
switch(chan_no)
|
|
{
|
|
case 0: case 1: case 2:
|
|
case 9: case 10: case 11:
|
|
if (CH->extended) CALC_FCSLOT(CH,SLOT); /* normal */
|
|
else CALC_FCSLOT(CH,SLOT); /* normal */
|
|
break;
|
|
case 3: case 4: case 5:
|
|
case 12: case 13: case 14:
|
|
if ((CH-3)->extended) CALC_FCSLOT(CH-3,SLOT); /* update this SLOT using frequency data for 1st channel of a pair */
|
|
else CALC_FCSLOT(CH,SLOT); /* normal */
|
|
break;
|
|
default:
|
|
CALC_FCSLOT(CH,SLOT); /* normal */
|
|
break;
|
|
}
|
|
}
|
|
else CALC_FCSLOT(CH,SLOT); /* in OPL2 mode */
|
|
}
|
|
|
|
/* set ksl & tl */
|
|
static void set_ksl_tl(uint8_t slot, uint8_t v)
|
|
{
|
|
OPL3_CH *CH = &chip.P_CH[slot >> 1];
|
|
OPL3_SLOT *SLOT = (slot & 1) ? &CH->SLOT1 : &CH->SLOT0;
|
|
|
|
SLOT->ksl = ksl_shift[v >> 6];
|
|
SLOT->TL = (v & 0x3f) << (ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */
|
|
SLOT->AMmask_TLL = SLOT->TL + (CH->ksl_base >> SLOT->ksl) + (SLOT->AMmask_TLL & 0x8000);
|
|
|
|
if (chip.OPL3_mode)
|
|
{
|
|
uint8_t chan_no = slot >> 2;
|
|
|
|
switch(chan_no)
|
|
{
|
|
case 3: case 4: case 5:
|
|
case 12: case 13: case 14:
|
|
if ((CH-3)->extended) SLOT->AMmask_TLL = SLOT->TL + ((CH-3)->ksl_base>>SLOT->ksl) + (SLOT->AMmask_TLL & 0x8000); /* update this SLOT using frequency data for 1st channel of a pair */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* set attack rate & decay rate */
|
|
static void set_ar_dr(uint8_t slot, uint8_t v)
|
|
{
|
|
OPL3_CH *CH = &chip.P_CH[slot >> 1];
|
|
OPL3_SLOT *SLOT = (slot & 1) ? &CH->SLOT1 : &CH->SLOT0;
|
|
uint8_t ksr;
|
|
SLOT->ar = (v & 0xf0) ? 16 + ((v & 0xf0) >> 2) : 0;
|
|
|
|
ksr = SLOT->ar + SLOT->ksr;
|
|
if (ksr < 16+60) /* verified on real YMF262 - all 15 x rates take "zero" time */
|
|
{
|
|
SLOT->eg_sh_ar = eg_rate_shift [ksr];
|
|
SLOT->eg_sel_ar = eg_rate_select[ksr];
|
|
}
|
|
else
|
|
{
|
|
SLOT->eg_sh_ar = 0;
|
|
SLOT->eg_sel_ar = 13*RATE_STEPS;
|
|
}
|
|
|
|
SLOT->dr = (v&0x0f) ? 16 + ((v&0x0f)<<2) : 0;
|
|
ksr = SLOT->dr + SLOT->ksr;
|
|
SLOT->eg_sh_dr = eg_rate_shift [ksr];
|
|
SLOT->eg_sel_dr = eg_rate_select[ksr];
|
|
}
|
|
|
|
/* set sustain level & release rate */
|
|
static void set_sl_rr(uint8_t slot, uint8_t v)
|
|
{
|
|
OPL3_CH *CH = &chip.P_CH[slot >> 1];
|
|
OPL3_SLOT *SLOT = (slot & 1) ? &CH->SLOT1 : &CH->SLOT0;
|
|
uint8_t ksr = SLOT->ksr;
|
|
SLOT->sl = (uint16_t)sl_tab[ v>>4 ] << 1;
|
|
SLOT->rr = (v&0x0f) ? 16 + ((v&0x0f)<<2) : 0;
|
|
ksr += SLOT->rr;
|
|
SLOT->eg_sh_rr = eg_rate_shift [ksr];
|
|
SLOT->eg_sel_rr = eg_rate_select[ksr];
|
|
}
|
|
|
|
void fn_a0(uint16_t r, uint8_t v, uint8_t ch_offset)
|
|
{
|
|
OPL3_CH *CH;
|
|
uint16_t block_fnum;
|
|
|
|
if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */
|
|
{
|
|
if (ch_offset != 0) return; /* 0xbd register is present in set #1 only */
|
|
|
|
chip.lfo_am_depth = !!(v & 0x80);
|
|
chip.lfo_pm_depth_range = (v & 0x40) ? 8 : 0;
|
|
chip.rhythm = !!(v & 0x20);
|
|
|
|
if (chip.rhythm)
|
|
{
|
|
/* BD key on/off */
|
|
if (v & 0x10)
|
|
{
|
|
FM_KEYON(&chip.P_CH[6].SLOT0, 2);
|
|
FM_KEYON(&chip.P_CH[6].SLOT1, 2);
|
|
}
|
|
else
|
|
{
|
|
FM_KEYOFF(&chip.P_CH[6].SLOT0, ~2);
|
|
FM_KEYOFF(&chip.P_CH[6].SLOT1, ~2);
|
|
}
|
|
/* HH key on/off */
|
|
if (v & 0x01) FM_KEYON(&chip.P_CH[7].SLOT0, 2);
|
|
else FM_KEYOFF(&chip.P_CH[7].SLOT0, ~2);
|
|
/* SD key on/off */
|
|
if (v & 0x08) FM_KEYON(&chip.P_CH[7].SLOT1, 2);
|
|
else FM_KEYOFF(&chip.P_CH[7].SLOT1, ~2);
|
|
/* TOM key on/off */
|
|
if (v & 0x04) FM_KEYON(&chip.P_CH[8].SLOT0, 2);
|
|
else FM_KEYOFF(&chip.P_CH[8].SLOT0, ~2);
|
|
/* TOP-CY key on/off */
|
|
if (v & 0x02) FM_KEYON(&chip.P_CH[8].SLOT1, 2);
|
|
else FM_KEYOFF(&chip.P_CH[8].SLOT1, ~2);
|
|
}
|
|
else
|
|
{
|
|
/* BD key off */
|
|
FM_KEYOFF(&chip.P_CH[6].SLOT0, ~2);
|
|
FM_KEYOFF(&chip.P_CH[6].SLOT1, ~2);
|
|
/* HH key off */
|
|
FM_KEYOFF(&chip.P_CH[7].SLOT0, ~2);
|
|
/* SD key off */
|
|
FM_KEYOFF(&chip.P_CH[7].SLOT1, ~2);
|
|
/* TOM key off */
|
|
FM_KEYOFF(&chip.P_CH[8].SLOT0, ~2);
|
|
/* TOP-CY off */
|
|
FM_KEYOFF(&chip.P_CH[8].SLOT1, ~2);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* keyon,block,fnum */
|
|
if ((r & 0x0f) > 8) return;
|
|
CH = &chip.P_CH[(r & 0x0f) + ch_offset];
|
|
|
|
if (!(r & 0x10)) block_fnum = (CH->block_fnum & 0x1f00) | v; /* a0-a8 */
|
|
else
|
|
{ /* b0-b8 */
|
|
block_fnum = ((v & 0x1f) << 8) | (CH->block_fnum & 0xff);
|
|
|
|
if (chip.OPL3_mode)
|
|
{
|
|
uint8_t chan_no = (r & 0x0f) + ch_offset;
|
|
switch (chan_no)
|
|
{
|
|
case 0: case 1: case 2:
|
|
case 9: case 10: case 11:
|
|
if (CH->extended)
|
|
{
|
|
if (v & 0x20)
|
|
{
|
|
FM_KEYON(&CH->SLOT0, 1);
|
|
FM_KEYON(&CH->SLOT1, 1);
|
|
FM_KEYON(&(CH + 3)->SLOT0, 1);
|
|
FM_KEYON(&(CH + 3)->SLOT1, 1);
|
|
}
|
|
else
|
|
{
|
|
FM_KEYOFF(&CH->SLOT0, ~1);
|
|
FM_KEYOFF(&CH->SLOT1, ~1);
|
|
FM_KEYOFF(&(CH + 3)->SLOT0, ~1);
|
|
FM_KEYOFF(&(CH + 3)->SLOT1, ~1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (v & 0x20)
|
|
{
|
|
FM_KEYON(&CH->SLOT0, 1);
|
|
FM_KEYON(&CH->SLOT1, 1);
|
|
}
|
|
else
|
|
{
|
|
FM_KEYOFF(&CH->SLOT0, ~1);
|
|
FM_KEYOFF(&CH->SLOT1, ~1);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 3: case 4: case 5:
|
|
case 12: case 13: case 14:
|
|
if ((CH - 3)->extended)
|
|
{
|
|
//if this is 2nd channel forming up 4-op channel just do nothing
|
|
}
|
|
else
|
|
{
|
|
if (v & 0x20)
|
|
{
|
|
FM_KEYON(&CH->SLOT0, 1);
|
|
FM_KEYON(&CH->SLOT1, 1);
|
|
}
|
|
else
|
|
{
|
|
FM_KEYOFF(&CH->SLOT0, ~1);
|
|
FM_KEYOFF(&CH->SLOT1, ~1);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (v & 0x20)
|
|
{
|
|
FM_KEYON(&CH->SLOT0, 1);
|
|
FM_KEYON(&CH->SLOT1, 1);
|
|
}
|
|
else
|
|
{
|
|
FM_KEYOFF(&CH->SLOT0, ~1);
|
|
FM_KEYOFF(&CH->SLOT1, ~1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (v & 0x20)
|
|
{
|
|
FM_KEYON(&CH->SLOT0, 1);
|
|
FM_KEYON(&CH->SLOT1, 1);
|
|
}
|
|
else
|
|
{
|
|
FM_KEYOFF(&CH->SLOT0, ~1);
|
|
FM_KEYOFF(&CH->SLOT1, ~1);
|
|
}
|
|
}
|
|
}
|
|
/* update */
|
|
if (CH->block_fnum != block_fnum)
|
|
{
|
|
CH->block_fnum = block_fnum;
|
|
CH->ksl_base = ksl_tab[block_fnum >> 6];
|
|
|
|
/* BLK 2,1,0 bits -> bits 3,2,1 of kcode */
|
|
CH->kcode = (CH->block_fnum & 0x1c00) >> 9;
|
|
|
|
/* the info below is actually opposite to what is stated in the Manuals (verifed on real YMF262) */
|
|
/* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */
|
|
/* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */
|
|
if (chip.nts & 0x40) CH->kcode |= (CH->block_fnum >> 8) & 1; /* notesel == 1 */
|
|
else CH->kcode |= (CH->block_fnum >> 9) & 1; /* notesel == 0 */
|
|
|
|
if (chip.OPL3_mode)
|
|
{
|
|
uint8_t chan_no = (r & 0x0f) + ch_offset;
|
|
switch (chan_no)
|
|
{
|
|
case 0: case 1: case 2:
|
|
case 9: case 10: case 11:
|
|
if (CH->extended)
|
|
{
|
|
/* refresh Total Level in FOUR SLOTs of this channel and channel+3 using data from THIS channel */
|
|
(CH + 3)->SLOT0.AMmask_TLL = (CH + 3)->SLOT0.TL + (CH->ksl_base >> (CH + 3)->SLOT0.ksl) + ((CH + 3)->SLOT0.AMmask_TLL & 0x8000);
|
|
(CH + 3)->SLOT1.AMmask_TLL = (CH + 3)->SLOT1.TL + (CH->ksl_base >> (CH + 3)->SLOT1.ksl) + ((CH + 3)->SLOT1.AMmask_TLL & 0x8000);
|
|
|
|
/* refresh frequency counter in FOUR SLOTs of this channel and channel+3 using data from THIS channel */
|
|
CALC_FCSLOT(CH, &(CH + 3)->SLOT0);
|
|
CALC_FCSLOT(CH, &(CH + 3)->SLOT1);
|
|
}
|
|
break;
|
|
|
|
case 3: case 4: case 5:
|
|
case 12: case 13: case 14:
|
|
if ((CH - 3)->extended) return; //if this is 2nd channel forming up 4-op channel just do nothing
|
|
break;
|
|
}
|
|
}
|
|
/* in OPL2 mode */
|
|
/* refresh Total Level in both SLOTs of this channel */
|
|
CH->SLOT0.AMmask_TLL = CH->SLOT0.TL + (CH->ksl_base >> CH->SLOT0.ksl) + (CH->SLOT0.AMmask_TLL & 0x8000);
|
|
CH->SLOT1.AMmask_TLL = CH->SLOT1.TL + (CH->ksl_base >> CH->SLOT1.ksl) + (CH->SLOT1.AMmask_TLL & 0x8000);
|
|
|
|
/* refresh frequency counter in both SLOTs of this channel */
|
|
CALC_FCSLOT(CH, &CH->SLOT0);
|
|
CALC_FCSLOT(CH, &CH->SLOT1);
|
|
}
|
|
}
|
|
|
|
uint16_t packptr16(void *ptr)
|
|
{
|
|
uint16_t v;
|
|
uint16_t i = (uint8_t*)ptr - (uint8_t*)&chip;
|
|
uint8_t j;
|
|
for (j = 0; i >= 96; i -= 96, j++);
|
|
v = (uint16_t)j << 7;
|
|
j = i & 0xff;
|
|
if (j >= 48) j -= 48, v |= 64;
|
|
return v | j;
|
|
}
|
|
|
|
/* write a value v to register r on OPL chip */
|
|
static void OPL3WriteReg(uint16_t r, uint8_t v)
|
|
{
|
|
OPL3_CH *CH;
|
|
uint8_t ch_offset = 0, base;
|
|
int8_t slot;
|
|
uint32_t mask;
|
|
|
|
if(r & 0x100)
|
|
{
|
|
switch(r)
|
|
{
|
|
case 0x101: return; /* test register */
|
|
|
|
case 0x104: /* 6 channels enable */
|
|
{
|
|
CH = &chip.P_CH[0]; /* channel 0 */
|
|
CH->extended = v & 1;
|
|
CH++; /* channel 1 */
|
|
CH->extended = (v>>=1) & 1;
|
|
CH++; /* channel 2 */
|
|
CH->extended = (v>>=1) & 1;
|
|
CH = &chip.P_CH[9]; /* channel 9 */
|
|
CH->extended = (v>>=1) & 1;
|
|
CH++; /* channel 10 */
|
|
CH->extended = (v>>=1) & 1;
|
|
CH++; /* channel 11 */
|
|
CH->extended = (v>>1) & 1;
|
|
}
|
|
return;
|
|
|
|
case 0x105: /* OPL3 extensions enable register */
|
|
chip.OPL3_mode = v & 0x01; /* OPL3 mode when bit0=1 otherwise it is OPL2 mode */
|
|
return;
|
|
}
|
|
ch_offset = 9; /* register page #2 starts from channel 9 (counting from 0) */
|
|
}
|
|
|
|
r &= 0xff; /* adjust bus to 8 bits */
|
|
|
|
switch(r & 0xe0)
|
|
{
|
|
case 0x00: /* 00-1f:control */
|
|
switch(r & 0x1f)
|
|
{
|
|
case 0x01: /* test register */
|
|
break;
|
|
case 0x02: /* Timer 1 */
|
|
break;
|
|
case 0x03: /* Timer 2 */
|
|
break;
|
|
case 0x04: /* IRQ clear / mask and Timer enable */
|
|
break;
|
|
case 0x08: /* x,NTS,x,x, x,x,x,x */
|
|
chip.nts = v;
|
|
break;
|
|
}
|
|
break;
|
|
case 0x20: /* am ON, vib ON, ksr, eg_type, mul */
|
|
slot = slot_array[r & 0x1f];
|
|
if(slot < 0) return;
|
|
set_mul(slot + ch_offset*2, v);
|
|
break;
|
|
case 0x40:
|
|
slot = slot_array[r & 0x1f];
|
|
if(slot < 0) return;
|
|
set_ksl_tl(slot + ch_offset*2, v);
|
|
break;
|
|
case 0x60:
|
|
slot = slot_array[r&0x1f];
|
|
if(slot < 0) return;
|
|
set_ar_dr(slot + ch_offset*2, v);
|
|
break;
|
|
case 0x80:
|
|
slot = slot_array[r&0x1f];
|
|
if(slot < 0) return;
|
|
set_sl_rr(slot + ch_offset*2, v);
|
|
break;
|
|
case 0xa0:
|
|
fn_a0(r, v, ch_offset);
|
|
break;
|
|
|
|
case 0xc0:
|
|
/* CH.D, CH.C, CH.B, CH.A, FB(3bits), C */
|
|
if( (r & 0xf) > 8) return;
|
|
CH = &chip.P_CH[(r & 0xf) + ch_offset];
|
|
base = (r & 0xf) + ch_offset;
|
|
mask = 1l << base;
|
|
|
|
if (chip.OPL3_mode) /* OPL3 mode */
|
|
{
|
|
chip.panA = (chip.panA & ~mask) | ((uint32_t)((v & 0x10) >> 4) << base);
|
|
chip.panB = (chip.panB & ~mask) | ((uint32_t)((v & 0x20) >> 5) << base);
|
|
// chip.pan[ base ] = (v & 0x10) != 0; /* ch.A */
|
|
// chip.pan[ base +1 ] = (v & 0x20) != 0; /* ch.B */
|
|
// chip.pan[ base +2 ] = (v & 0x40) != 0; /* ch.C */
|
|
// chip.pan[ base +3 ] = (v & 0x80) != 0; /* ch.D */
|
|
}
|
|
else /* OPL2 mode - always enabled */
|
|
{
|
|
chip.panA |= mask;
|
|
chip.panB |= mask;
|
|
// chip.pan[ base ] = 1; /* ch.A */
|
|
// chip.pan[ base +1 ] = 1; /* ch.B */
|
|
// chip.pan[ base +2 ] = 1; /* ch.C */
|
|
// chip.pan[ base +3 ] = 1; /* ch.D */
|
|
}
|
|
CH->SLOT0.FB = (v & 0xe) ? 9 - ((v & 0xe) >> 1) : 0;
|
|
CH->SLOT0.CON = v & 1;
|
|
|
|
if( chip.OPL3_mode )
|
|
{
|
|
uint8_t chan_no = (r & 0x0f) + ch_offset;
|
|
|
|
switch(chan_no)
|
|
{
|
|
case 0: case 1: case 2:
|
|
case 9: case 10: case 11:
|
|
if (CH->extended)
|
|
{
|
|
uint8_t conn = (CH->SLOT0.CON << 1) | (CH+3)->SLOT0.CON;
|
|
switch(conn)
|
|
{
|
|
case 0:
|
|
/* 1 -> 2 -> 3 -> 4 - out */
|
|
CH->SLOT0.connect = packptr16(&chip.phase_modulation);
|
|
CH->SLOT1.connect = packptr16(&chip.phase_modulation2);
|
|
(CH+3)->SLOT0.connect = packptr16(&chip.phase_modulation);
|
|
(CH + 3)->SLOT1.connect = packptr16(&chip.P_CH[chan_no + 3].chanout);// chanout[chan_no + 3];
|
|
break;
|
|
case 1:
|
|
/* 1 -> 2 -\
|
|
3 -> 4 -+- out */
|
|
CH->SLOT0.connect = packptr16(&chip.phase_modulation);
|
|
CH->SLOT1.connect = packptr16(&chip.P_CH[chan_no].chanout); // chanout[chan_no];
|
|
(CH+3)->SLOT0.connect = packptr16(&chip.phase_modulation);
|
|
(CH+3)->SLOT1.connect = packptr16(&chip.P_CH[chan_no + 3].chanout);
|
|
break;
|
|
case 2:
|
|
/* 1 -----------\
|
|
2 -> 3 -> 4 -+- out */
|
|
CH->SLOT0.connect = packptr16(&chip.P_CH[chan_no].chanout);
|
|
CH->SLOT1.connect = packptr16(&chip.phase_modulation2);
|
|
(CH+3)->SLOT0.connect = packptr16(&chip.phase_modulation);
|
|
(CH+3)->SLOT1.connect = packptr16(&chip.P_CH[chan_no + 3].chanout);
|
|
break;
|
|
case 3:
|
|
/* 1 ------\
|
|
2 -> 3 -+- out
|
|
4 ------/ */
|
|
CH->SLOT0.connect = packptr16(&chip.P_CH[chan_no].chanout);
|
|
CH->SLOT1.connect = packptr16(&chip.phase_modulation2);
|
|
(CH+3)->SLOT0.connect = packptr16(&chip.P_CH[chan_no + 3].chanout);
|
|
(CH+3)->SLOT1.connect = packptr16(&chip.P_CH[chan_no + 3].chanout);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* 2 operators mode */
|
|
CH->SLOT0.connect = packptr16(CH->SLOT0.CON ? &chip.P_CH[(r & 0xf) + ch_offset].chanout : &chip.phase_modulation);
|
|
CH->SLOT1.connect = packptr16(&chip.P_CH[(r & 0xf) + ch_offset].chanout);
|
|
}
|
|
break;
|
|
|
|
case 3: case 4: case 5:
|
|
case 12: case 13: case 14:
|
|
if ((CH-3)->extended)
|
|
{
|
|
uint8_t conn = ((CH-3)->SLOT0.CON << 1) | CH->SLOT0.CON;
|
|
switch(conn)
|
|
{
|
|
case 0:
|
|
/* 1 -> 2 -> 3 -> 4 - out */
|
|
(CH-3)->SLOT0.connect = packptr16(&chip.phase_modulation);
|
|
(CH-3)->SLOT1.connect = packptr16(&chip.phase_modulation2);
|
|
CH->SLOT0.connect = packptr16(&chip.phase_modulation);
|
|
CH->SLOT1.connect = packptr16(&chip.P_CH[ chan_no ].chanout);
|
|
break;
|
|
case 1:
|
|
/* 1 -> 2 -\
|
|
3 -> 4 -+- out */
|
|
(CH-3)->SLOT0.connect = packptr16(&chip.phase_modulation);
|
|
(CH-3)->SLOT1.connect = packptr16(&chip.P_CH[ chan_no - 3 ].chanout);
|
|
CH->SLOT0.connect = packptr16(&chip.phase_modulation);
|
|
CH->SLOT1.connect = packptr16(&chip.P_CH[ chan_no ].chanout);
|
|
break;
|
|
case 2:
|
|
/* 1 -----------\
|
|
2 -> 3 -> 4 -+- out */
|
|
(CH-3)->SLOT0.connect = packptr16(&chip.P_CH[ chan_no - 3 ].chanout);
|
|
(CH-3)->SLOT1.connect = packptr16(&chip.phase_modulation2);
|
|
CH->SLOT0.connect = packptr16(&chip.phase_modulation);
|
|
CH->SLOT1.connect = packptr16(&chip.P_CH[ chan_no ].chanout);
|
|
break;
|
|
case 3:
|
|
/* 1 ------\
|
|
2 -> 3 -+- out
|
|
4 ------/ */
|
|
(CH-3)->SLOT0.connect = packptr16(&chip.P_CH[ chan_no - 3 ].chanout);
|
|
(CH-3)->SLOT1.connect = packptr16(&chip.phase_modulation2);
|
|
CH->SLOT0.connect = packptr16(&chip.P_CH[ chan_no ].chanout);
|
|
CH->SLOT1.connect = packptr16(&chip.P_CH[ chan_no ].chanout);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* 2 operators mode */
|
|
CH->SLOT0.connect = packptr16(CH->SLOT0.CON ? &chip.P_CH[(r & 0xf) + ch_offset].chanout : &chip.phase_modulation);
|
|
CH->SLOT1.connect = packptr16(&chip.P_CH[(r & 0xf)+ch_offset].chanout);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* 2 operators mode */
|
|
CH->SLOT0.connect = packptr16(CH->SLOT0.CON ? &chip.P_CH[(r & 0xf) + ch_offset].chanout : &chip.phase_modulation);
|
|
CH->SLOT1.connect = packptr16(&chip.P_CH[(r & 0xf) + ch_offset].chanout);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* OPL2 mode - always 2 operators mode */
|
|
CH->SLOT0.connect = packptr16(CH->SLOT0.CON ? &chip.P_CH[(r&0xf)+ch_offset].chanout : &chip.phase_modulation);
|
|
CH->SLOT1.connect = packptr16(&chip.P_CH[(r&0xf)+ch_offset].chanout);
|
|
}
|
|
break;
|
|
|
|
case 0xe0: /* waveform select */
|
|
slot = slot_array[r&0x1f];
|
|
if(slot < 0) return;
|
|
|
|
slot += ch_offset*2;
|
|
CH = &chip.P_CH[slot >> 1];
|
|
|
|
/* store 3-bit value written regardless of current OPL2 or OPL3 mode... (verified on real YMF262) */
|
|
v &= 7;
|
|
if(slot & 1) CH->SLOT1.waveform_number = v;
|
|
else CH->SLOT0.waveform_number = v;
|
|
|
|
/* ... but select only waveforms 0-3 in OPL2 mode */
|
|
if( !chip.OPL3_mode ) v &= 3; /* we're in OPL2 mode */
|
|
if(slot & 1) CH->SLOT1.wavetable = v;
|
|
else CH->SLOT0.wavetable = v;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void OPL3ResetChip()
|
|
{
|
|
uint16_t c;
|
|
|
|
// eg_cnt = 0;
|
|
// noise_rng = 1; /* noise shift register */
|
|
memset(&chip, 0, sizeof(chip));
|
|
for(c = 0xff ; c >= 0x20 ; c-- ) OPL3WriteReg(c, 0);
|
|
for(c = 0x1ff ; c >= 0x120 ; c-- ) OPL3WriteReg(c, 0);
|
|
|
|
/* reset operator parameters */
|
|
for( c = 0 ; c < 9*2 ; c++ )
|
|
{
|
|
OPL3_CH *CH = &chip.P_CH[c];
|
|
CH->SLOT0.state = EG_OFF;
|
|
CH->SLOT0.volume = MAX_ATT_INDEX;
|
|
CH->SLOT1.state = EG_OFF;
|
|
CH->SLOT1.volume = MAX_ATT_INDEX;
|
|
}
|
|
|
|
c = (uint16_t)&chip;
|
|
outp(0, (uint8_t)c);
|
|
outp(1, (uint8_t)(c>>8));
|
|
}
|
|
|
|
/* YMF262 I/O interface */
|
|
void OPL3Write(uint8_t a, uint8_t v)
|
|
{
|
|
switch(a&3)
|
|
{
|
|
case 0: /* address port 0 (register set #1) */
|
|
chip.address = v;
|
|
break;
|
|
|
|
case 1: /* data port - ignore A1 */
|
|
case 3: /* data port - ignore A1 */
|
|
// if(chip->UpdateHandler) chip->UpdateHandler(chip->UpdateParam,0);
|
|
OPL3WriteReg(chip.address, v);
|
|
break;
|
|
|
|
case 2: /* address port 1 (register set #2) */
|
|
if( chip.OPL3_mode ) chip.address = v | 0x100; /* OPL3 mode */
|
|
else
|
|
{
|
|
if( v==5 ) chip.address = v | 0x100; /* in OPL2 mode the only accessible in set #2 is register 0x05 */
|
|
else chip.address = v; /* verified range: 0x01, 0x04, 0x20-0xef(set #2 becomes set #1 in opl2 mode) */
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define READY 1
|
|
#define QEMPTY 2
|
|
|
|
int main()
|
|
{
|
|
uint8_t c;
|
|
OPL3ResetChip();
|
|
|
|
while(1)
|
|
{
|
|
c = inp(0);
|
|
if((c & 3) == READY) OPL3Write((c >> 2) & 3, inp(1));
|
|
}
|
|
}
|