use bitflags::bitflags;
use std::array::from_fn;
bitflags! {
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(transparent))]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, Debug)]
pub struct Config: u32 {
const YM2612 = 0x01;
const READ_MODE = 0x02;
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
enum EgState {
#[default]
Attack = 0,
Decay = 1,
Sustain = 2,
Release = 3,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
pub struct State {
config: Config,
cycles: u32,
channel: u32,
mol: i16,
mor: i16,
write_data: u16,
write_a: u8,
write_d: u8,
write_a_en: bool,
write_d_en: bool,
write_busy: bool,
write_busy_cnt: u8,
write_fm_address: bool,
write_fm_data: bool,
write_fm_mode_a: u16,
address: u16,
data: u8,
pin_test_in: bool,
busy: bool,
lfo_en: u8,
lfo_freq: u8,
lfo_pm: u8,
lfo_am: u8,
lfo_cnt: u8,
lfo_inc: bool,
lfo_quotient: u8,
pg_fnum: u16,
pg_block: u8,
pg_kcode: u8,
pg_inc: [u32; 24],
pg_phase: [u32; 24],
pg_reset: [bool; 24],
pg_read: u32,
eg_cycle: u8,
eg_cycle_stop: bool,
eg_shift: u8,
eg_shift_lock: u8,
eg_timer_low_lock: u8,
eg_timer: u16,
eg_timer_inc: u8,
eg_quotient: u16,
eg_custom_timer: bool,
eg_rate: u8,
eg_ksv: u8,
eg_inc: u8,
eg_ratemax: bool,
eg_sl: [u8; 2],
eg_lfo_am: u8,
eg_tl: [u8; 2],
eg_state: [EgState; 24],
eg_level: [u16; 24],
eg_out: [u16; 24],
eg_kon: [bool; 24],
eg_kon_csm: [bool; 24],
eg_kon_latch: [bool; 24],
eg_ssg_enable: [bool; 24],
eg_ssg_pgrst_latch: [bool; 24],
eg_ssg_repeat_latch: [bool; 24],
eg_ssg_hold_up_latch: [bool; 24],
eg_ssg_dir: [bool; 24],
eg_ssg_inv: [bool; 24],
eg_read: [u32; 2],
eg_read_inc: bool,
fm_op1: [[i16; 2]; 6],
fm_op2: [i16; 6],
fm_out: [i16; 24],
fm_mod: [u16; 24],
ch_acc: [i16; 6],
ch_out: [i16; 6],
ch_lock: i16,
ch_lock_l: bool,
ch_lock_r: bool,
ch_read: i16,
timer_a_cnt: u16,
timer_a_reg: u16,
timer_a_load_lock: bool,
timer_a_load: bool,
timer_a_enable: bool,
timer_a_reset: bool,
timer_a_load_latch: bool,
timer_a_overflow_flag: bool,
timer_a_overflow: u8,
timer_b_cnt: u16,
timer_b_subcnt: u8,
timer_b_reg: u16,
timer_b_load_lock: bool,
timer_b_load: bool,
timer_b_enable: bool,
timer_b_reset: bool,
timer_b_load_latch: bool,
timer_b_overflow_flag: bool,
timer_b_overflow: u8,
mode_test_21: [bool; 8],
mode_test_2c: [bool; 8],
mode_ch3: u8,
mode_kon_channel: u8,
mode_kon_operator: [bool; 4],
mode_kon: [bool; 24],
mode_csm: bool,
mode_kon_csm: bool,
dacen: bool,
dacdata: u16,
ks: [u8; 24],
ar: [u8; 24],
sr: [u8; 24],
dt: [u8; 24],
multi: [u8; 24],
sl: [u8; 24],
rr: [u8; 24],
dr: [u8; 24],
am: [u8; 24],
tl: [u8; 24],
ssg_eg: [u8; 24],
fnum: [u16; 6],
block: [u8; 6],
kcode: [u8; 6],
fnum_3ch: [u16; 6],
block_3ch: [u8; 6],
kcode_3ch: [u8; 6],
reg_a4: u8,
reg_ac: u8,
connect: [u8; 6],
fb: [u8; 6],
pan_l: [bool; 6],
pan_r: [bool; 6],
ams: [u8; 6],
pms: [u8; 6],
status: u8,
status_time: u32,
}
const LOGS_IN_ROM: [u16; 256] = [
0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5,
0x398, 0x37e, 0x365, 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, 0x2bd, 0x2af,
0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218,
0x20f, 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, 0x1c5, 0x1be, 0x1b7, 0x1b0,
0x1a9, 0x1a2, 0x19b, 0x195, 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, 0x160,
0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121,
0x11c, 0x118, 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, 0x0f4, 0x0f0, 0x0ec,
0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1,
0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c,
0x099, 0x097, 0x094, 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, 0x07f, 0x07d,
0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062,
0x060, 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, 0x050, 0x04e, 0x04d, 0x04b,
0x04a, 0x048, 0x046, 0x045, 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, 0x038,
0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028,
0x027, 0x026, 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, 0x01d, 0x01c, 0x01b,
0x01a, 0x019, 0x018, 0x017, 0x017, 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011,
0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, 0x00b, 0x00a, 0x00a, 0x009, 0x009,
0x008, 0x008, 0x007, 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004,
0x003, 0x003, 0x003, 0x002, 0x002, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001,
0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
];
const EXPROM: [u16; 256] = [
0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, 0x016, 0x019, 0x01c, 0x01f, 0x022,
0x025, 0x028, 0x02a, 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, 0x045, 0x048,
0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a, 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f,
0x072, 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b, 0x08e, 0x091, 0x094, 0x098,
0x09b, 0x09e, 0x0a1, 0x0a4, 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be, 0x0c2,
0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed,
0x0f1, 0x0f4, 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, 0x114, 0x117, 0x11b,
0x11e, 0x122, 0x125, 0x129, 0x12c, 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149,
0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167, 0x16b, 0x16f, 0x172, 0x176, 0x17a,
0x17e, 0x181, 0x185, 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4, 0x1a8, 0x1ac,
0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0,
0x1e4, 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, 0x209, 0x20e, 0x212, 0x216,
0x21a, 0x21e, 0x223, 0x227, 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, 0x24e,
0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d, 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288,
0x28c, 0x291, 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5, 0x2ba, 0x2bf, 0x2c4,
0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302,
0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, 0x32e, 0x333, 0x338, 0x33d, 0x342,
0x347, 0x34c, 0x351, 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, 0x37f, 0x384,
0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4, 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9,
0x3cf, 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa,
];
const FN_NOTE: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3];
const EG_STEPHIGH: [[u8; 4]; 4] = [[0, 0, 0, 0], [1, 0, 0, 0], [1, 0, 1, 0], [1, 1, 1, 0]];
const EG_AM_SHIFT: [u8; 4] = [7, 3, 1, 0];
const PG_DETUNE: [u32; 8] = [16, 17, 19, 20, 22, 24, 27, 29];
const PG_LFO_SH1: [[u8; 8]; 8] = [
[7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 1, 1],
[7, 7, 7, 7, 1, 1, 1, 1],
[7, 7, 7, 1, 1, 1, 1, 0],
[7, 7, 1, 1, 0, 0, 0, 0],
[7, 7, 1, 1, 0, 0, 0, 0],
[7, 7, 1, 1, 0, 0, 0, 0],
];
const PG_LFO_SH2: [[u8; 8]; 8] = [
[7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 2, 2, 2, 2],
[7, 7, 7, 2, 2, 2, 7, 7],
[7, 7, 2, 2, 7, 7, 2, 2],
[7, 7, 2, 7, 7, 7, 2, 7],
[7, 7, 7, 2, 7, 7, 2, 1],
[7, 7, 7, 2, 7, 7, 2, 1],
[7, 7, 7, 2, 7, 7, 2, 1],
];
const OP_OFFSET: [u16; 12] = [
0x000, 0x001, 0x002, 0x100, 0x101, 0x102, 0x004, 0x005, 0x006, 0x104, 0x105, 0x106, ];
const CH_OFFSET: [u16; 6] = [0x000, 0x001, 0x002, 0x100, 0x101, 0x102];
const LFO_CYCLES: [u8; 8] = [108, 77, 71, 67, 62, 44, 8, 5];
const FM_ALGORITHM: [[[u8; 8]; 6]; 4] = [
[
[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 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, 1],
],
[
[0, 1, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[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, 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],
[1, 0, 0, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 1],
],
[
[0, 0, 1, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0],
[1, 1, 0, 1, 1, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1],
],
];
#[inline(always)]
fn test_bit(data: u8, bit: usize) -> bool {
data & (1 << bit) != 0
}
#[inline(always)]
fn to_u16(hi: u8, lo: u8) -> u16 {
((hi as u16) << 8) | lo as u16
}
#[inline(always)]
fn sign_extend_u16(v: u16, pos: u8) -> i16 {
let shift = 15 - pos;
((v << shift) as i16) >> shift
}
impl State {
fn do_io(&mut self) {
self.write_a_en = (self.write_a & 0x03) == 0x01;
self.write_d_en = (self.write_d & 0x03) == 0x01;
self.write_a <<= 1;
self.write_d <<= 1;
self.busy = self.write_busy;
self.write_busy_cnt += self.write_busy as u8;
self.write_busy = (self.write_busy && ((self.write_busy_cnt >> 5) == 0)) || self.write_d_en;
self.write_busy_cnt &= 0x1f;
}
fn do_reg_write(&mut self) {
let mut slot = (self.cycles % 12) as usize;
if self.write_fm_data {
if OP_OFFSET[slot] == self.address & 0x107 {
if self.address & 0x08 != 0 {
slot += 12;
}
match self.address & 0xf0 {
0x30 => {
let multi = self.data & 0x0f;
self.multi[slot] = if multi == 0 { 1 } else { multi << 1 };
self.dt[slot] = (self.data >> 4) & 0x07;
}
0x40 => {
self.tl[slot] = self.data & 0x7f;
}
0x50 => {
self.ar[slot] = self.data & 0x1f;
self.ks[slot] = (self.data >> 6) & 0x03;
}
0x60 => {
self.dr[slot] = self.data & 0x1f;
self.am[slot] = (self.data >> 7) & 0x01;
}
0x70 => {
self.sr[slot] = self.data & 0x1f;
}
0x80 => {
self.rr[slot] = self.data & 0x0f;
let sl0 = (self.data >> 4) & 0x0f;
self.sl[slot] = sl0 | ((sl0 + 1) & 0x10);
}
0x90 => {
self.ssg_eg[slot] = self.data & 0x0f;
}
_ => {}
}
}
}
let channel = self.channel as usize;
if CH_OFFSET[channel] == self.address & 0x103 {
match self.address & 0xfc {
0xa0 => {
self.fnum[channel] = to_u16(self.reg_a4 & 0x07, self.data);
self.block[channel] = (self.reg_a4 >> 3) & 0x07;
self.kcode[channel] =
(self.block[channel] << 2) | FN_NOTE[self.fnum[channel] as usize >> 7];
}
0xa4 => {
self.reg_a4 = self.data;
}
0xa8 => {
self.fnum_3ch[channel] = to_u16(self.reg_ac & 0x07, self.data);
self.block_3ch[channel] = (self.reg_ac >> 3) & 0x07;
self.kcode_3ch[channel] = (self.block_3ch[channel] << 2)
| FN_NOTE[self.fnum_3ch[channel] as usize >> 7];
}
0xac => {
self.reg_ac = self.data;
}
0xb0 => {
self.connect[channel] = self.data & 0x07;
self.fb[channel] = (self.data >> 3) & 0x07;
}
0xb4 => {
self.pms[channel] = self.data & 0x07;
self.ams[channel] = (self.data >> 4) & 0x03;
self.pan_l[channel] = test_bit(self.data, 7);
self.pan_r[channel] = test_bit(self.data, 6);
}
_ => {}
}
}
if self.write_a_en || self.write_d_en {
if self.write_a_en {
self.write_fm_data = false;
}
if self.write_fm_address && self.write_d_en {
self.write_fm_data = true;
}
if self.write_a_en {
self.write_fm_address = if self.write_data & 0xf0 != 0x00 {
self.address = self.write_data;
true
} else {
false
}
}
if self.write_d_en && self.write_data & 0x100 == 0 {
let wb = (self.write_data & 0xff) as u8;
match self.write_fm_mode_a {
0x21 => {
self.mode_test_21 = from_fn(|i| test_bit(wb, i));
}
0x22 => {
self.lfo_en = if test_bit(wb, 3) { 0x7f } else { 0 };
self.lfo_freq = wb & 0x07;
}
0x24 => {
self.timer_a_reg = (self.timer_a_reg & 0x03) | ((wb as u16) << 2);
}
0x25 => {
self.timer_a_reg = (self.timer_a_reg & 0x3fc) | ((wb as u16) & 0x03);
}
0x26 => {
self.timer_b_reg = wb as u16;
}
0x27 => {
self.mode_ch3 = (wb & 0xc0) >> 6;
self.mode_csm = self.mode_ch3 == 2;
self.timer_a_load = test_bit(wb, 0);
self.timer_a_enable = test_bit(wb, 2);
self.timer_a_reset = test_bit(wb, 4);
self.timer_b_load = test_bit(wb, 1);
self.timer_b_enable = test_bit(wb, 3);
self.timer_b_reset = test_bit(wb, 5);
}
0x28 => {
self.mode_kon_operator = from_fn(|i| test_bit(wb, 4 + i));
self.mode_kon_channel = if wb & 0x03 == 0x03 {
0xff
} else {
(wb & 0x03) + ((wb >> 2) & 1) * 3
}
}
0x2a => {
self.dacdata = (self.dacdata & 0x01) | (((wb ^ 0x80) as u16) << 1);
}
0x2b => {
self.dacen = test_bit(wb, 7);
}
0x2c => {
self.mode_test_2c = from_fn(|i| test_bit(wb, i));
self.dacdata = (self.dacdata & 0x1fe) | self.mode_test_2c[3] as u16;
self.eg_custom_timer = !self.mode_test_2c[7] && self.mode_test_2c[6];
}
_ => {}
}
}
if self.write_a_en {
self.write_fm_mode_a = self.write_data & 0x1ff;
}
}
if self.write_fm_data {
self.data = (self.write_data & 0xff) as u8;
}
}
fn phase_calc_increment(&mut self) {
let chan = self.channel as usize;
let slot = self.cycles as usize;
let mut fnum = self.pg_fnum as u32;
let fnum_h = fnum >> 4;
let pms = self.pms[chan] as usize;
let lfo = self.lfo_pm;
let lfo_l = (if lfo & 0x08 != 0 { !lfo } else { lfo } & 0x0f) as usize;
let mut fm = (fnum_h >> PG_LFO_SH1[pms][lfo_l]) + (fnum_h >> PG_LFO_SH2[pms][lfo_l]);
if pms > 5 {
fm <<= pms - 5;
}
fm >>= 2;
fnum <<= 1;
let fnum = (if lfo & 0x10 != 0 {
fnum - fm
} else {
fnum + fm
}) & 0xfff;
let dt = self.dt[slot];
let dt_l = dt & 0x03;
let detune = if dt_l != 0 {
let kcode = self.pg_kcode.min(0x1c);
let block = kcode >> 2;
let note = kcode & 0x03;
let sum = block + 9 + (((dt_l == 3) as u8) | (dt_l & 0x02));
let sum_h = sum >> 1;
let sum_l = sum & 0x01;
PG_DETUNE[((sum_l << 2) | note) as usize] >> (9 - sum_h)
} else {
0
};
let basefreq = (fnum << self.pg_block) >> 2;
let basefreq = if dt & 0x04 != 0 {
basefreq - detune
} else {
basefreq + detune
} & 0x1ffff;
self.pg_inc[slot] = ((basefreq * self.multi[slot] as u32) >> 1) & 0xfffff;
}
fn phase_generate(&mut self) {
let slot = ((self.cycles + 20) % 24) as usize;
if self.pg_reset[slot] {
self.pg_inc[slot] = 0;
}
let slot = ((self.cycles + 19) % 24) as usize;
if self.pg_reset[slot] || self.mode_test_21[3] {
self.pg_phase[slot] = 0;
}
self.pg_phase[slot] = (self.pg_phase[slot] + self.pg_inc[slot]) & 0xfffff;
}
fn envelope_ssg_eg(&mut self) {
let slot = self.cycles as usize;
self.eg_ssg_pgrst_latch[slot] = false;
self.eg_ssg_repeat_latch[slot] = false;
self.eg_ssg_hold_up_latch[slot] = false;
let mut direction = false;
if self.ssg_eg[slot] & 0x08 != 0 {
direction = self.eg_ssg_dir[slot];
if self.eg_level[slot] & 0x200 != 0 {
if self.ssg_eg[slot] & 0x03 == 0x00 {
self.eg_ssg_pgrst_latch[slot] = true;
}
if self.ssg_eg[slot] & 0x01 == 0x00 {
self.eg_ssg_repeat_latch[slot] = true;
}
direction = match self.ssg_eg[slot] & 0x03 {
0x02 => !direction,
0x03 => true,
_ => direction,
}
}
if self.eg_kon_latch[slot] && matches!(self.ssg_eg[slot] & 0x07, 0x05 | 0x03) {
self.eg_ssg_hold_up_latch[slot] = true;
}
direction &= self.eg_kon[slot];
}
self.eg_ssg_dir[slot] = direction;
self.eg_ssg_enable[slot] = test_bit(self.ssg_eg[slot], 3);
self.eg_ssg_inv[slot] = (self.eg_ssg_dir[slot]
^ (test_bit(self.ssg_eg[slot], 2) & test_bit(self.ssg_eg[slot], 3)))
& self.eg_kon[slot];
}
fn envelope_adsr(&mut self) {
let slot = ((self.cycles + 22) % 24) as usize;
let nkon = self.eg_kon_latch[slot];
let okon = self.eg_kon[slot];
self.eg_read[0] = self.eg_read_inc as u32;
self.eg_read_inc = self.eg_inc > 0;
self.pg_reset[slot] = (nkon && !okon) || self.eg_ssg_pgrst_latch[slot];
let kon_event = (nkon && !okon) || (okon && self.eg_ssg_repeat_latch[slot]);
let koff_event = okon && !nkon;
let level = self.eg_level[slot] as i16;
let ssg_level = if self.eg_ssg_inv[slot] {
(512 - level) & 0x3ff
} else {
level
};
let level = if koff_event { ssg_level } else { level };
let eg_off = if self.eg_ssg_enable[slot] {
(level >> 9) as u8
} else {
((level & 0x3f0) == 0x3f0) as u8
};
let mut nextlevel = level;
let mut nextstate = self.eg_state[slot];
let mut inc = 0;
if kon_event {
nextstate = EgState::Attack;
if self.eg_ratemax {
nextlevel = 0;
} else if self.eg_state[slot] == EgState::Attack
&& level != 0
&& self.eg_inc != 0
&& nkon
{
inc = (!level << self.eg_inc) >> 5;
}
} else {
match self.eg_state[slot] {
EgState::Attack => {
if level == 0 {
nextstate = EgState::Decay;
} else if self.eg_inc != 0 && !self.eg_ratemax && nkon {
inc = (!level << self.eg_inc) >> 5;
}
}
EgState::Decay => {
if (level >> 4) == (self.eg_sl[1] << 1) as i16 {
nextstate = EgState::Sustain;
} else if eg_off == 0 && self.eg_inc != 0 {
inc = 1 << (self.eg_inc - 1);
if self.eg_ssg_enable[slot] {
inc <<= 2;
}
}
}
EgState::Sustain | EgState::Release => {
if eg_off == 0 && self.eg_inc != 0 {
inc = 1 << (self.eg_inc - 1);
if self.eg_ssg_enable[slot] {
inc <<= 2;
}
}
}
}
if !nkon {
nextstate = EgState::Release;
}
}
if self.eg_kon_csm[slot] {
nextlevel |= (self.eg_tl[1] << 3) as i16;
}
if !kon_event
&& !self.eg_ssg_hold_up_latch[slot]
&& self.eg_state[slot] != EgState::Attack
&& eg_off != 0
{
nextstate = EgState::Release;
nextlevel = 0x3ff;
}
nextlevel += inc;
self.eg_kon[slot] = self.eg_kon_latch[slot];
self.eg_level[slot] = nextlevel as u16 & 0x3ff;
self.eg_state[slot] = nextstate;
}
fn envelope_prepare(&mut self) {
let slot = self.cycles as usize;
let rate = ((self.eg_rate << 1) + self.eg_ksv).min(0x3f);
let sum = ((rate >> 2) + self.eg_shift_lock) & 0x0f;
self.eg_inc = if self.eg_rate != 0 && self.eg_quotient == 2 {
if rate < 48 {
match sum {
12 => 1,
13 => (rate >> 1) & 0x01,
14 => rate & 0x01,
_ => 0,
}
} else {
(EG_STEPHIGH[(rate & 0x03) as usize][self.eg_timer_low_lock as usize] + (rate >> 2)
- 11)
.min(4)
}
} else {
0
};
self.eg_ratemax = (rate >> 1) == 0x1f;
let rate_sel = if (self.eg_kon[slot] && self.eg_ssg_repeat_latch[slot])
|| (!self.eg_kon[slot] && self.eg_kon_latch[slot])
{
EgState::Attack
} else {
self.eg_state[slot]
};
self.eg_rate = match rate_sel {
EgState::Attack => self.ar[slot],
EgState::Decay => self.dr[slot],
EgState::Sustain => self.sr[slot],
EgState::Release => (self.rr[slot] << 1) | 0x01,
};
self.eg_ksv = self.pg_kcode >> (self.ks[slot] ^ 0x03);
self.eg_lfo_am = if self.am[slot] != 0 {
self.lfo_am >> EG_AM_SHIFT[self.ams[self.channel as usize] as usize]
} else {
0
};
self.eg_tl[1] = self.eg_tl[0];
self.eg_tl[0] = self.tl[slot];
self.eg_sl[1] = self.eg_sl[0];
self.eg_sl[0] = self.sl[slot];
}
fn envelope_generate(&mut self) {
let slot = ((self.cycles + 23) % 24) as usize;
let mut level = (if self.eg_ssg_inv[slot] {
512 - self.eg_level[slot]
} else if self.mode_test_21[5] {
0
} else {
self.eg_level[slot]
}) & 0x3ff;
level += self.eg_lfo_am as u16;
if !(self.mode_csm && self.channel == 2 + 1) {
level += (self.eg_tl[0] as u16) << 3
}
self.eg_out[slot] = level.min(0x3ff);
}
fn update_lfo(&mut self) {
let lfo_cycles = LFO_CYCLES[self.lfo_freq as usize];
if self.lfo_quotient & lfo_cycles == lfo_cycles {
self.lfo_quotient = 0;
self.lfo_cnt += 1;
} else {
self.lfo_quotient += self.lfo_inc as u8;
}
self.lfo_cnt &= self.lfo_en;
}
fn fm_prepare(&mut self) {
let slot = ((self.cycles + 6) % 24) as usize;
let channel = self.channel as usize;
let op = slot / 6;
let connect = self.connect[channel] as usize;
let prevslot = ((self.cycles + 18) % 24) as usize;
let mut mod1 = 0;
let mut mod2 = 0;
if FM_ALGORITHM[op][0][connect] != 0 {
mod2 |= self.fm_op1[channel][0];
}
if FM_ALGORITHM[op][1][connect] != 0 {
mod1 |= self.fm_op1[channel][1];
}
if FM_ALGORITHM[op][2][connect] != 0 {
mod1 |= self.fm_op2[channel];
}
if FM_ALGORITHM[op][3][connect] != 0 {
mod2 |= self.fm_out[prevslot];
}
if FM_ALGORITHM[op][4][connect] != 0 {
mod1 |= self.fm_out[prevslot];
}
let mods = mod1 + mod2;
self.fm_mod[slot] = if op == 0 {
if self.fb[channel] != 0 {
mods >> (10 - self.fb[channel])
} else {
0
}
} else {
mods >> 1
} as u16;
let slot = ((self.cycles + 18) % 24) as usize;
if slot / 6 == 0 {
self.fm_op1[channel][1] = self.fm_op1[channel][0];
self.fm_op1[channel][0] = self.fm_out[slot];
}
if slot / 6 == 2 {
self.fm_op2[channel] = self.fm_out[slot];
}
}
fn ch_generate(&mut self) {
let slot = ((self.cycles + 18) % 24) as usize;
let channel = self.channel as usize;
let op = slot / 6;
let test_dac = self.mode_test_2c[5];
let acc = if op == 0 && !test_dac {
0
} else {
self.ch_acc[channel]
};
let add = if FM_ALGORITHM[op][5][self.connect[channel] as usize] != 0 && !test_dac {
self.fm_out[slot] >> 5
} else {
test_dac as i16
};
let sum = acc + add;
let sum = sum.clamp(-256, 255);
if op == 0 || test_dac {
self.ch_out[channel] = self.ch_acc[channel];
}
self.ch_acc[channel] = sum;
}
fn ch_output(&mut self) {
let cycles = self.cycles;
let slot = cycles as usize;
let test_dac = self.mode_test_2c[5];
let channel = (if slot < 12 {
self.channel + 1
} else {
self.channel
}) as usize;
self.ch_read = self.ch_lock;
if cycles & 3 == 0 {
if !test_dac {
self.ch_lock = self.ch_out[channel];
}
self.ch_lock_l = self.pan_l[channel];
self.ch_lock_r = self.pan_r[channel];
}
let out = if ((cycles >> 2) == 1 && self.dacen) || test_dac {
sign_extend_u16(self.dacdata, 8)
} else {
self.ch_lock
};
(self.mol, self.mor) = if self.config.contains(Config::YM2612) {
let out_en = ((cycles & 3) == 3) || test_dac;
let (sign, out) = if out >= 0 {
((out >> 8) + 1, out + 1)
} else {
(out >> 8, out)
};
(
(if self.ch_lock_l && out_en { out } else { sign }) * 3,
(if self.ch_lock_r && out_en { out } else { sign }) * 3,
)
} else {
let out_en = ((cycles & 3) != 0) || test_dac;
(
(if self.ch_lock_l && out_en { out } else { 0 }),
(if self.ch_lock_r && out_en { out } else { 0 }),
)
}
}
fn fm_generate(&mut self) {
let slot = ((self.cycles + 19) % 24) as usize;
let phase = (self.fm_mod[slot] + (((self.pg_phase[slot] >> 10) & 0x3ff) as u16)) as usize;
let quarter = if phase & 0x100 != 0 {
(phase ^ 0xff) & 0xff
} else {
phase & 0xff
};
let level = (LOGS_IN_ROM[quarter] + (self.eg_out[slot] << 2)).min(0x1fff);
let output = ((EXPROM[((level & 0xff) ^ 0xff) as usize] | 0x400) << 2) >> (level >> 8);
let testinv = (self.mode_test_21[4] as u16) << 13;
let output = if phase & 0x200 != 0 {
(!output ^ testinv) + 1
} else {
output ^ testinv
};
self.fm_out[slot] = sign_extend_u16(output, 13);
}
fn do_timer_a(&mut self) {
let mut load = self.timer_a_overflow != 0;
if self.cycles == 2 {
load |= !self.timer_a_load_lock && self.timer_a_load;
self.timer_a_load_lock = self.timer_a_load;
self.mode_kon_csm = if self.mode_csm { load } else { false };
}
let mut time = if self.timer_a_load_latch {
self.timer_a_reg
} else {
self.timer_a_cnt
};
self.timer_a_load_latch = load;
if (self.cycles == 1 && self.timer_a_load_lock) || self.mode_test_21[2] {
time += 1;
}
if self.timer_a_reset {
self.timer_a_reset = false;
self.timer_a_overflow_flag = false;
} else {
self.timer_a_overflow_flag |= (self.timer_a_overflow != 0) & self.timer_a_enable;
}
self.timer_a_overflow = (time >> 10) as u8;
self.timer_a_cnt = time & 0x3ff;
}
fn do_timer_b(&mut self) {
let mut load = self.timer_b_overflow != 0;
if self.cycles == 2 {
load |= !self.timer_b_load_lock && self.timer_b_load;
self.timer_b_load_lock = self.timer_b_load;
}
let mut time = if self.timer_b_load_latch {
self.timer_b_reg
} else {
self.timer_b_cnt
};
self.timer_b_load_latch = load;
if self.cycles == 1 {
self.timer_b_subcnt += 1;
}
if (self.timer_b_subcnt == 0x10 && self.timer_b_load_lock) || self.mode_test_21[2] {
time += 1;
}
self.timer_b_subcnt &= 0x0f;
if self.timer_b_reset {
self.timer_b_reset = false;
self.timer_b_overflow_flag = false;
} else {
self.timer_b_overflow_flag |= (self.timer_b_overflow != 0) & self.timer_b_enable;
}
self.timer_b_overflow = (time >> 8) as u8;
self.timer_b_cnt = time & 0xff;
}
fn key_on(&mut self) {
let slot = self.cycles as usize;
(self.eg_kon_latch[slot], self.eg_kon_csm[slot]) = if self.channel == 2 && self.mode_kon_csm
{
(true, true)
} else {
(self.mode_kon[slot], false)
};
if self.cycles == self.mode_kon_channel as u32 {
let chan = self.channel as usize;
self.mode_kon[chan] = self.mode_kon_operator[0];
self.mode_kon[chan + 12] = self.mode_kon_operator[1];
self.mode_kon[chan + 6] = self.mode_kon_operator[2];
self.mode_kon[chan + 18] = self.mode_kon_operator[3];
}
}
pub fn new(config: Config) -> Self {
let mut state = Self {
config,
..Default::default()
};
state.eg_out.fill(0x3ff);
state.eg_level.fill(0x3ff);
state.eg_state.fill(EgState::Release);
state.multi.fill(1);
state.pan_l.fill(true);
state.pan_r.fill(true);
state
}
pub fn reset(&mut self) {
*self = Self::new(self.config);
}
pub fn clock(&mut self) -> (i16, i16) {
let slot = self.cycles as usize;
self.lfo_inc = self.mode_test_21[1];
self.pg_read >>= 1;
self.eg_read[1] >>= 1;
self.eg_cycle += 1;
if self.cycles == 1 && self.eg_quotient == 2 {
self.eg_shift_lock = if self.eg_cycle_stop {
0
} else {
self.eg_shift + 1
};
self.eg_timer_low_lock = (self.eg_timer & 0x03) as u8;
}
match self.cycles {
0 => {
self.lfo_pm = self.lfo_cnt >> 2;
self.lfo_am = (if self.lfo_cnt & 0x40 != 0 {
self.lfo_cnt & 0x3f
} else {
self.lfo_cnt ^ 0x3f
}) << 1;
}
1 => {
self.eg_quotient = (self.eg_quotient + 1) % 3;
self.eg_cycle = 0;
self.eg_cycle_stop = true;
self.eg_shift = 0;
self.eg_timer_inc |= (self.eg_quotient >> 1) as u8;
self.eg_timer += self.eg_timer_inc as u16;
self.eg_timer_inc = (self.eg_timer >> 12) as u8;
self.eg_timer &= 0x0fff;
}
2 => {
self.pg_read = self.pg_phase[21] & 0x3ff;
self.eg_read[1] = self.eg_out[0] as u32;
}
13 => {
self.eg_cycle = 0;
self.eg_cycle_stop = true;
self.eg_shift = 0;
self.eg_timer += self.eg_timer_inc as u16;
self.eg_timer_inc = (self.eg_timer >> 12) as u8;
self.eg_timer &= 0x0fff;
}
23 => {
self.lfo_inc = true;
}
_ => {}
}
self.eg_timer &= !((self.mode_test_21[5] as u16) << self.eg_cycle);
if ((self.eg_timer >> self.eg_cycle != 0) || (self.pin_test_in && self.eg_custom_timer))
&& self.eg_cycle_stop
{
self.eg_shift = self.eg_cycle;
self.eg_cycle_stop = false;
}
self.do_io();
self.do_timer_a();
self.do_timer_b();
self.key_on();
self.ch_output();
self.ch_generate();
self.fm_prepare();
self.fm_generate();
self.phase_generate();
self.phase_calc_increment();
self.envelope_adsr();
self.envelope_generate();
self.envelope_ssg_eg();
self.envelope_prepare();
(self.pg_fnum, self.pg_block, self.pg_kcode) = if self.mode_ch3 != 0 {
match slot {
1 => (self.fnum_3ch[1], self.block_3ch[1], self.kcode_3ch[1]), 7 => (self.fnum_3ch[0], self.block_3ch[0], self.kcode_3ch[0]), 13 => (self.fnum_3ch[2], self.block_3ch[2], self.kcode_3ch[2]), _ => {
let chan = ((self.channel + 1) % 6) as usize;
(self.fnum[chan], self.block[chan], self.kcode[chan])
}
}
} else {
let chan = ((self.channel + 1) % 6) as usize;
(self.fnum[chan], self.block[chan], self.kcode[chan])
};
self.update_lfo();
self.do_reg_write();
self.cycles = (self.cycles + 1) % 24;
self.channel = self.cycles % 6;
self.status_time = self.status_time.saturating_sub(1);
(self.mol, self.mor)
}
pub fn write(&mut self, port: u16, data: u8) {
let port = port & 3;
self.write_data = ((port << 7) & 0x100) | data as u16;
if port & 1 != 0 {
self.write_d |= 1;
} else {
self.write_a |= 1;
}
}
pub fn chip_type(&self) -> Config {
self.config
}
pub fn set_test_pin(&mut self, value: bool) {
self.pin_test_in = value;
}
pub fn read_test_pin(&self) -> bool {
if !self.mode_test_2c[7] {
false
} else {
self.cycles == 23
}
}
pub fn read_irq_pin(&self) -> bool {
self.timer_a_overflow_flag || self.timer_b_overflow_flag
}
pub fn read(&mut self, port: u32) -> u8 {
if port & 3 == 0 || self.config.contains(Config::READ_MODE) {
self.status = if self.mode_test_21[6] {
let slot = (self.cycles + 18) % 24;
let mut test_data = (((self.pg_read & 0x01) << 15)
| (self.eg_read[self.mode_test_21[0] as usize] & 0x01) << 14)
as u16;
test_data |= (if self.mode_test_2c[4] {
self.ch_read & 0x1ff
} else {
self.fm_out[slot as usize] & 0x3fff
}) as u16;
(if self.mode_test_21[7] {
test_data
} else {
test_data >> 8
}) as u8
} else {
(self.busy as u8) << 7
| ((self.timer_b_overflow_flag as u8) << 1)
| (self.timer_a_overflow_flag as u8)
};
self.status_time = if self.config.contains(Config::YM2612) {
300000
} else {
40_000_000
}
}
if self.status_time != 0 {
self.status
} else {
0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_do_io() {
let mut s = State::new(Config::default());
assert_eq!(s.write_a_en, false);
assert_eq!(s.write_d_en, false);
s.write_a = 1;
s.write_d = 1;
s.do_io();
assert_eq!(s.write_a_en, true);
assert_eq!(s.write_d_en, true);
assert_eq!(s.write_a, 2);
assert_eq!(s.write_d, 2);
s.write_busy = true;
s.do_io();
assert_eq!(s.write_a_en, false);
assert_eq!(s.write_d_en, false);
assert_eq!(s.write_a, 4);
assert_eq!(s.write_d, 4);
assert_eq!(s.busy, true);
assert_eq!(s.write_busy_cnt, 1);
assert_eq!(s.write_busy, true);
}
}