A rust port of vgm2mid by Paul Jensen and Valley Bell
use midly::num::u7;
use crate::config::Config;
use crate::midi_shim::{
	MIDIShim, MIDI_DATA_ENTRY_MSB, MIDI_MODULATOR_WHEEL, MIDI_NRPN_LSB, MIDI_NRPN_MSB,
	MIDI_PAN, MIDI_VOLUME, NRPN_DRUM_PAN, NRPN_DRUM_PITCH_COARSE,
};
use crate::strict;
use crate::utils::FactoredState;
use crate::vgm2mid::CHN_DAC;
use crate::ym3812::YM3812;
use anyhow::{anyhow, Result};
use midly::num::u4;

#[allow(dead_code)]
const YM278_REG_LSI_TEST: u8 = 0x00;
#[allow(dead_code)]
const YM278_REG_MEMORY_ACCESS_MODE: u8 = 0x02;
#[allow(dead_code)]
const YM278_REG_MEMORY_TYPE: u8 = 0x02;
#[allow(dead_code)]
const YM278_REG_WAVE_TABLE_HEADER: u8 = 0x02;

struct Opl4Tone {
	instrument: u8,
	pitch: u16,
	#[allow(dead_code)]
	key_scl: u8,
}

const XG_MODE: bool = false;

static OPL4_TONES: Vec<Opl4Tone> = Vec::new();

fn note_ymf278(tone: u16, fnum: u32, oct: i16) -> f64 {
	if oct == -8 && fnum == 0 {
		return 0xFF.into();
	}

	// Note2FNum from Linux// OPL4 Driver
	//pitch = (note - 60) * voice->sound->key_scaling / 100 + 60;
	//pitch = pitch << 7;	-- 1 Halftone = 128 pitch --
	//pitch += voice->sound->pitch_offset;
	//octave = pitch / 0x600 - 8;
	//fnum = pitch2fnum(pitch % 0x600);

	let mut pitch = (1.0 + fnum as f64 / 1024.0).log2();
	let octave = oct + 8;
	//if OPL4Tones(Tone).Pitch = 0x1000 { Stop
	pitch -= OPL4_TONES[tone as usize].pitch as f64 / 0x600 as f64;
	// Formula from YMF278B Manual:			 1024 + F_NUMBER
	// F(c) = 1200 x (Octave - 1) + 1200 x log2 ---------------
	// 1 octave = 1200c							   1024

	//if OPL4Tones(Tone).KeyScl > 0 & OPL4Tones(Tone).KeyScl != 100 {
	//	Note = (Note - 60) * 100 / OPL4Tones(Tone).KeyScl + 60
	//}

	(octave as f64 * 12.0) + pitch * 12.0
}

fn channel_select(snum: u8, tone_ins: u8) -> (u4, bool) {
	let mut select_channel: u4;
	let drum_mode: bool;

	//FIXME: more nonsense constants
	if XG_MODE {
		select_channel = (snum & 0xF).into();
		drum_mode = false;
	} else if tone_ins & 0x80 == 0 {
 			select_channel = (snum & 0xF).into();
 			if select_channel == CHN_DAC {
 				select_channel = 0xF.into();
 			}
 			//if SNum <= 0x4 { SelChn = 0x8
 			drum_mode = false;
 		} else {
 			select_channel = CHN_DAC;
 			drum_mode = true;
 		}
	(select_channel, drum_mode)
}

pub(crate) struct YMF278State {
	tone_wave: [u16; 0x18],
	tone_ins: [u8; 0x18],
	oct: [i16; 0x18],
	fnum_o: [u32; 0x18],
	fnum_n: [u32; 0x18],
	note_o: [f64; 0x18],
	note_n: [f64; 0x18],
	note_on_o: [u8; 0x18],
	note_on_n: [u8; 0x18],
	slot_volume: [u8; 0x18],
	slot_pan: [u8; 0x18],
	damper_on_o: [u8; 0x18],
	damper_on_n: [u8; 0x18],
	vib: [u8; 0x18],
	drum_pan: [[u8; 0x80]; 0x10],
	drum_pitch: [[u8; 0x80]; 0x10],
	drum_nrpn_m: [u7; 0x10],
	drum_nrpn_l: [u8; 0x10],
	opl4_tones: Vec<Opl4Tone>,
	factored: FactoredState,
}

pub(crate) struct YMF278<'config> {
	pub state: YMF278State,
	config: &'config Config,
}

impl<'config> YMF278<'config> {
	pub(crate) fn new<'c: 'config>(
		config: &'c Config,
		opt_state: Option<YMF278State>,
	) -> YMF278 {
		YMF278 {
			state: opt_state.unwrap_or_default(),
			config,
		}
	}
	pub(crate) fn command_handle(
		&mut self,
		port: u8,
		register: u8,
		data: u8,
		ym3812: &mut YM3812,
		fsam_3812: f64,
		midi: &mut MIDIShim,
	) -> Result<()> {
		if port < 0x2 {
			ym3812.command_handle(
				(port as u16) << 8 | register as u16,
				data,
				fsam_3812,
				midi,
			)?;
			if port == 0x1 && register == 0x5 {
				self.state = Default::default();
			}
			return Ok(());
		}

		if (0x08..=0xF7).contains(&register) {
			let slot_number = (register - 0x8) % 24;
			let (channel, drum_mode) = channel_select(
				slot_number,
				self.state.tone_ins[slot_number as usize],
			);

			//FIXME: this is dumb, make this more sensible.
			match (register - 0x8) / 24 {
				0x0 => {
					wave_table_number(
						&mut self.state,
						slot_number,
						data,
						channel.into(),
						midi,
					)?;
				},
				0x1 => {
					f_number(&mut self.state, slot_number, data);
				},
				0x2 => {
					octave(
						&mut self.state,
						slot_number,
						data,
						channel.into(),
						midi,
					)?;
				},
				0x3 => {
					total_level(
						&mut self.state,
						slot_number,
						data,
						drum_mode,
						channel.into(),
						midi,
					);
				},
				0x4 => {
					key_on(
						data,
						&mut self.state,
						slot_number,
						drum_mode,
						channel.into(),
						midi,
					)?;
				},
				0x5 => {
					lfo(
						&mut self.state,
						slot_number,
						data,
						drum_mode,
						channel.into(),
						midi,
					);
				},
				0x6 => {
					ar(
						&mut self.state,
						slot_number,
						data,
						channel.into(),
						midi,
					)?;
				},
				0x7 => (), // Reg 0xB0 - 0xC7
				//slot.DL = dl_tab(Int(Data / 0x10))
				//slot.D2R = Data & 0xF
				0x8 => (), // Reg 0xC8 - 0xDF
				//slot.RC = Int(Data / 0x10)
				//slot.RR = Data & 0xF
				0x9 => (), // Reg 0xE0 - 0xF7
				//slot.AM = Data & 0x7
				_ => strict!(),
			}
		} else {
			// All non-slot registers
			match register {
				0x0 => (), // TEST
				0x1 => (),
				0x2 => (),
				//wavetblhdr = (Data >> 2) & 0x7
				//memmode = Data & 1
				0x3 => (),
				//memadr = (memadr & 0xFFFF&) | (Data * 0x10000)
				0x4 => (),
				//memadr = (memadr & 0xFF00FF) | (Data * 0x100&)
				0x5 => (),
				//memadr = (memadr & 0xFFFF00) | Data
				0x6 => (), // memory data
				//Call writeMem(memadr, Data)
				//memadr = (memadr + 1) & 0xFFFFFF
				0xF8 => (),
				//fm_l = Data & 0x7
				//fm_r = (Data >> 3) & 0x7
				0xF9 => (),
				//pcm_l = Data & 0x7
				//pcm_r = (Data >> 3) & 0x7
				_ => (),
			}
		}

		//regs [Register] = Data

		Ok(())
	}

	pub(crate) fn load_opl4_instrument_set(&mut self) -> Result<()> {
		const INSSET_SIG: &[u8] = b"INSSET";
		const COUNT_PTR: usize = INSSET_SIG.len(); //This should resolve to the subsequent byte.

		//NOTE: Consider adding this as a static resource?
		//There is at most 1 KiB of data. Even on an embedded device it would be reasonable to
		//just statically allocate this.
		let bytes = std::fs::read("yrw801.ins").map_err(|err| anyhow!(err))?;

		if bytes.len() <= INSSET_SIG.len() {
			strict!("OPL4 Instrument file is too short!");
		}

		if &bytes[0..INSSET_SIG.len()] != INSSET_SIG {
			strict!("Invalid OPL4 instrument set");
		}

		let tone_count = bytes[COUNT_PTR];

		for tone in 0..tone_count {
			let instrument = bytes[COUNT_PTR + 1 + (tone * 4) as usize];
			let pitch = u16::from_le_bytes([
				bytes[COUNT_PTR + 1 + (1 + tone * 4) as usize],
				bytes[COUNT_PTR + 1 + (2 + tone * 4) as usize],
			]);
			let key_scl = bytes[COUNT_PTR + 1 + (3 + tone * 4) as usize];
			self.state.opl4_tones.push(Opl4Tone {
				instrument,
				pitch,
				key_scl,
			});
		}
		Ok(())
	}
}

impl Default for YMF278State {
	fn default() -> Self {
		YMF278State {
			tone_wave: [0; 0x18],
			tone_ins: [0; 0x18],
			oct: [0; 0x18],
			fnum_o: [0; 0x18],
			fnum_n: [0; 0x18],
			note_o: [0xFF.into(); 0x18],
			note_n: [0xFF.into(); 0x18],
			note_on_o: [0; 0x18],
			note_on_n: [0; 0x18],
			slot_volume: [0; 0x18],
			slot_pan: [0; 0x18],
			damper_on_o: [0; 0x18],
			damper_on_n: [0; 0x18],
			vib: [0; 0x18],
			drum_pan: [[0xFF; 0x80]; 0x10],
			drum_pitch: [[0xFF; 0x80]; 0x10],
			drum_nrpn_m: [0xFF.into(); 0x10],
			drum_nrpn_l: [0xFF; 0x10],
			opl4_tones: Vec::new(),
			factored: Default::default(),
		}
	}
}

fn ar(
	state: &mut YMF278State,
	slot_number: u8,
	data: u8,
	channel: u8,
	midi: &mut MIDIShim,
) -> Result<()> {
	// Reg 0x98 - 0xAF
	//slot.AR = Int(Data / 0x10)
	//slot.D1R = Data & 0xF
	if state.tone_ins[slot_number as usize] == 0x77 {
		// Reverse Cymbal
		let temp_byte = data / 0x10;
		if temp_byte >= 0xE {
			// -> Crash Cymbal
			if state.note_on_o[slot_number as usize] != 0 {
				midi.do_note_on(
					state.note_o[slot_number as usize],
					0xFF.into(),
					channel.into(),
					&mut state.factored.midi_note[slot_number as usize],
					&mut state.factored.midi_wheel[slot_number as usize],
					Some(0xFF),
					Some(temp_byte.into()),
				)?;
				state.note_on_o[slot_number as usize] = 0;
			}

			state.tone_wave[slot_number as usize] = 0x180;
			state.tone_ins[slot_number as usize] = OPL4_TONES
				[state.tone_wave[slot_number as usize] as usize]
				.instrument;
			let (channel, _drum_mode) =
				channel_select(slot_number, state.tone_ins[slot_number as usize]);
			let channel_ptr = channel.as_int() as usize;
			if state.tone_ins[slot_number as usize] & 0x80 != 0 {
				let temp_byte = state.tone_ins[slot_number as usize] & 0x7F;
				let old_val = state.drum_pitch[channel_ptr][temp_byte as usize];
				state.drum_pitch[channel_ptr][temp_byte as usize] =
					state.note_n[slot_number as usize] as u8; //FIXME: This is impossible to understand;
				if state.drum_pitch[channel_ptr][temp_byte as usize] != old_val {
					if state.drum_nrpn_m[channel_ptr] != NRPN_DRUM_PITCH_COARSE
						|| state.drum_nrpn_l[channel_ptr] != temp_byte
					{
						state.drum_nrpn_m[channel_ptr] =
							NRPN_DRUM_PITCH_COARSE;
						state.drum_nrpn_l[channel_ptr] = temp_byte;
						midi.controller_write(
							channel,
							MIDI_NRPN_MSB,
							state.drum_nrpn_m[channel_ptr],
						);
						midi.controller_write(
							channel,
							MIDI_NRPN_LSB,
							state.drum_nrpn_l[channel_ptr].into(),
						);
					}
					midi.controller_write(
						channel,
						MIDI_DATA_ENTRY_MSB,
						state.drum_pitch[channel_ptr][temp_byte as usize]
							.into(),
					);
				}

				state.note_n[slot_number as usize] =
					(state.tone_ins[slot_number as usize] & 0x7F).into();
			}
		}
	}
	Ok(())
}

fn lfo(
	state: &mut YMF278State,
	slot_number: u8,
	data: u8,
	drum_mode: bool,
	channel: u8,
	midi: &mut MIDIShim,
) {
	// Reg 0x80 - 0x97
	let old_val = state.vib[slot_number as usize];
	state.vib[slot_number as usize] = (data & 0x7) * 0x10;
	if !drum_mode && state.vib[slot_number as usize] != old_val {
		midi.controller_write(
			channel.into(),
			MIDI_MODULATOR_WHEEL,
			state.vib[slot_number as usize].into(),
		);
	}
	//slot.set_lfo(Int(Data / 0x8) & 0x7)
}

fn key_on(
	data: u8,
	state: &mut YMF278State,
	slot_number: u8,
	drum_mode: bool,
	channel: u8,
	midi: &mut MIDIShim,
) -> Result<()> {
	// Reg 0x68 - 0x7F
	if data & 0x80 != 0 {
		let temp_byte = data & 0xF;
		state.slot_pan[slot_number as usize] = match temp_byte.cmp(&0x08) {
			std::cmp::Ordering::Less => 0x40 - temp_byte / 0x7 * 0x40,
			std::cmp::Ordering::Equal => 0x40,
			std::cmp::Ordering::Greater => {
				let temp = 0x40 + (0x10 - temp_byte) / 0x7 * 0x40;
				if temp == 0x80 {
					0x7F
				} else {
					temp
				}
			},
		};

		if !drum_mode {
			let old_val = state.factored.midi_pan[channel as usize];
			state.factored.midi_pan[channel as usize] =
				state.slot_pan[slot_number as usize].into();
			if state.factored.midi_pan[channel as usize] != old_val {
				midi.controller_write(
					channel.into(),
					MIDI_PAN,
					state.factored.midi_pan[channel as usize],
				);
			}
		} else {
			let temp_byte = state.tone_ins[slot_number as usize] & 0x7F;
			let old_val = state.drum_pan[channel as usize][temp_byte as usize];
			state.drum_pan[channel as usize][temp_byte as usize] =
				state.slot_pan[slot_number as usize];
			if state.drum_pan[channel as usize][temp_byte as usize] != old_val {
				if state.drum_nrpn_m[channel as usize] != NRPN_DRUM_PAN
					|| state.drum_nrpn_l[channel as usize] != temp_byte
				{
					state.drum_nrpn_m[channel as usize] = NRPN_DRUM_PAN;
					state.drum_nrpn_l[channel as usize] = temp_byte;
					midi.controller_write(
						channel.into(),
						MIDI_NRPN_MSB,
						state.drum_nrpn_m[channel as usize],
					);
					midi.controller_write(
						channel.into(),
						MIDI_NRPN_LSB,
						state.drum_nrpn_l[channel as usize].into(),
					);
				}
				midi.controller_write(
					channel.into(),
					MIDI_DATA_ENTRY_MSB,
					state.drum_pan[channel as usize][temp_byte as usize].into(),
				);
			}
		}
	}

	state.damper_on_n[slot_number as usize] = (data & 0x40) / 0x40;
	state.note_on_n[slot_number as usize] = (data & 0x80) / 0x80;

	if state.damper_on_n[slot_number as usize] != state.damper_on_o[slot_number as usize] {
		// Damping increases Decay rate
		//midi.event_write(MIDI_CONTROLLER_CHANGE, CH, MIDI_SOFT, DamperOn_N[SNum]) & 0x7F != 0;
		state.damper_on_o[slot_number as usize] = state.damper_on_n[slot_number as usize]
	}
	if state.note_on_n[slot_number as usize] != state.note_on_o[slot_number as usize] {
		let temp_byte = if !drum_mode {
			0x7F
		} else {
			state.slot_volume[slot_number as usize]
		};
		if state.note_on_n[slot_number as usize] != 0 {
			midi.do_note_on(
				state.note_o[slot_number as usize],
				state.note_n[slot_number as usize],
				channel.into(),
				&mut state.factored.midi_note[slot_number as usize],
				&mut state.factored.midi_wheel[slot_number as usize],
				Some(0xFF),
				Some(temp_byte.into()),
			)?;
		} else {
			midi.do_note_on(
				state.note_o[slot_number as usize],
				0xFF.into(),
				channel.into(),
				&mut state.factored.midi_note[slot_number as usize],
				&mut state.factored.midi_wheel[slot_number as usize],
				Some(0xFF),
				Some(temp_byte.into()),
			)?;
		}
		state.note_on_o[slot_number as usize] = state.note_on_n[slot_number as usize];
	}
	Ok(())
}

fn total_level(
	state: &mut YMF278State,
	slot_number: u8,
	data: u8,
	drum_mode: bool,
	channel: u8,
	midi: &mut MIDIShim,
) {
	// Reg 0x50 - 0x67
	let old_val = state.slot_volume[slot_number as usize];
	state.slot_volume[slot_number as usize] = 0x7F - data / 0x2;
	if state.slot_volume[slot_number as usize] == 0x0
		&& state.note_on_n[slot_number as usize] == 0
	{
		state.slot_volume[slot_number as usize] = old_val;
	}
	//LD = Data & 0x1

	//if LD {
	//	// directly change volume
	//} else {
	//	// interpolate volume
	//}

	if !drum_mode {
		let old_val = state.factored.midi_volume[channel as usize];
		state.factored.midi_volume[channel as usize] =
			state.slot_volume[slot_number as usize].into();

		if state.factored.midi_volume[channel as usize] != old_val {
			midi.controller_write(
				channel.into(),
				MIDI_VOLUME,
				state.factored.midi_volume[channel as usize],
			);
		}
	}
}

fn octave(
	state: &mut YMF278State,
	slot_number: u8,
	data: u8,
	channel: u8,
	midi: &mut MIDIShim,
) -> Result<()> {
	// Reg 0x38 - 0x4F
	//slot.FN = (slot.FN & 0x07F) | ((Data & 0x07) << 7)
	state.factored.fnum_msb[slot_number as usize] = data & 0x7;
	//slot.PRVB = ((data & 0x08) >> 3)
	//slot.OCT =  ((data & 0xF0) >> 4)
	state.oct[slot_number as usize] = ((data & 0xF0) / 0x10).into();
	//int oct = slot.OCT;
	if state.oct[slot_number as usize] & 0x8 != 0 {
		state.oct[slot_number as usize] |= -8;
	}

	state.fnum_o[slot_number as usize] = state.fnum_n[slot_number as usize];
	state.fnum_n[slot_number as usize] = ((state.factored.fnum_msb[slot_number as usize]
		* 0x80) | state.factored.fnum_lsb
		[slot_number as usize])
		.into();

	state.note_o[slot_number as usize] = state.note_n[slot_number as usize];

	state.note_n[slot_number as usize] = note_ymf278(
		state.tone_wave[slot_number as usize],
		state.fnum_n[slot_number as usize],
		state.oct[slot_number as usize],
	);
	if state.tone_ins[slot_number as usize] & 0x80 != 0 {
		let temp_byte = state.tone_ins[slot_number as usize] & 0x7F;
		let old_val = state.drum_pitch[channel as usize][temp_byte as usize];
		state.drum_pitch[channel as usize][temp_byte as usize] =
			state.note_n[slot_number as usize] as u8; //FIXME wtf is this shit.
		if state.drum_pitch[channel as usize][temp_byte as usize] != old_val {
			if state.drum_nrpn_m[channel as usize] != NRPN_DRUM_PITCH_COARSE
				|| state.drum_nrpn_l[channel as usize] != temp_byte
			{
				state.drum_nrpn_m[channel as usize] = NRPN_DRUM_PITCH_COARSE;
				state.drum_nrpn_l[channel as usize] = temp_byte;
				midi.controller_write(
					channel.into(),
					MIDI_NRPN_MSB,
					state.drum_nrpn_m[channel as usize],
				);
				midi.controller_write(
					channel.into(),
					MIDI_NRPN_LSB,
					state.drum_nrpn_l[channel as usize].into(),
				);
			}
			midi.controller_write(
				channel.into(),
				MIDI_DATA_ENTRY_MSB,
				state.drum_pitch[channel as usize][temp_byte as usize].into(),
			);
		}

		state.note_n[slot_number as usize] =
			(state.tone_ins[slot_number as usize] & 0x7F).into();
	}

	if state.note_on_n[slot_number as usize] != 0 {
		midi.do_note_on(
			state.note_o[slot_number as usize],
			state.note_n[slot_number as usize],
			channel.into(),
			&mut state.factored.midi_note[slot_number as usize],
			&mut state.factored.midi_wheel[slot_number as usize],
			None,
			None,
		)?;
	}
	Ok(())
}

fn f_number(state: &mut YMF278State, slot_number: u8, data: u8) {
	// Reg 0x20 - 0x37
	//slot.wave = (slot.wave & 0xFF) | ((data & 0x1) << 8);
	state.tone_wave[slot_number as usize] =
		(state.tone_wave[slot_number as usize] & 0xFF) | ((data as u16 & 0x1) * 0x100);
	//slot.FN = (slot.FN & 0x380) | (data >> 1);
	state.factored.fnum_lsb[slot_number as usize] = data / 0x2;
	// usually, FNum MSB is sent after FNum LSB
}

fn wave_table_number(
	state: &mut YMF278State,
	slot_number: u8,
	data: u8,
	channel: u8,
	midi: &mut MIDIShim,
) -> Result<()> {
	// Reg 0x8 - 0x1F
	//loadTime = time + LOAD_DELAY;
	//
	//slot.wave = (slot.wave & 0x100) | data;
	state.tone_wave[slot_number as usize] =
		(state.tone_wave[slot_number as usize] & 0x100) | data as u16;
	state.tone_ins[slot_number as usize] =
		OPL4_TONES[state.tone_wave[slot_number as usize] as usize].instrument;

	let old_val = channel;
	let (channel, _drum_mode) =
		channel_select(slot_number, state.tone_ins[slot_number as usize]);
	let channel_ptr = channel.as_int() as usize;
	if channel != old_val & state.note_on_o[slot_number as usize] {
		midi.do_note_on(
			state.note_o[slot_number as usize],
			0xFF.into(),
			channel,
			&mut state.factored.midi_note[slot_number as usize],
			&mut state.factored.midi_wheel[slot_number as usize],
			Some(0xFF),
			Some(0x00.into()), //FIXME: This was temp_byte, though it was never assigned...
		)?;
		state.note_on_o[slot_number as usize] = 0
	}

	let old_instrument = state.factored.midi_instrument[channel_ptr];
	state.factored.midi_instrument[channel_ptr] =
		if state.tone_ins[slot_number as usize] & 0x80 != 0 {
			0x80.into()
		} else {
			(state.tone_ins[slot_number as usize] & 0x7F).into()
		};

	if state.factored.midi_instrument[channel_ptr] != old_instrument {
		if XG_MODE {
			if (state.factored.midi_instrument[channel_ptr].as_int() & 0x80)
				!= (old_instrument.as_int() & 0x80)
			{
				let temp_byte = if state.factored.midi_instrument[channel_ptr]
					.as_int() & 0x80 != 0
				{
					0x7F
				} else {
					0x0
				};
				midi.controller_write(channel, 0x00.into(), temp_byte.into());
			}
			if (state.factored.midi_instrument[channel_ptr].as_int() & 0x80)
				& (old_instrument.as_int() & 0x80)
				== 0
			{
				midi.program_change_write(
					channel,
					state.factored.midi_instrument[channel_ptr],
				);
			}
		} else if state.factored.midi_instrument[channel_ptr].as_int() & 0x80 == 0 {
  				midi.program_change_write(
  					channel,
  					state.factored.midi_instrument[channel_ptr],
  				);
  			}
		//if CH < 8 & MIDIIns[channel_ptr] = 0x77 { Stop
	}
	//int base = (slot.wave < 384 | !wavetblhdr) ?
	//		   (slot.wave * 12) :
	//		   (wavetblhdr * 0x80000 + ((slot.wave - 384) * 12));
	//byte buf[12];
	//for (int i = 0; i < 12; ++i) {
	//	buf[i] = readMem(base + i);
	//}
	//slot.bits = (buf[0] & 0xC0) >> 6;
	//slot.set_lfo((buf[7] >> 3) & 7);
	//slot.vib  = buf[7] & 7;
	//slot.AR   = buf[8] >> 4;
	//slot.D1R  = buf[8] & 0xF;
	//slot.DL   = dl_tab[buf[9] >> 4];
	//slot.D2R  = buf[9] & 0xF;
	//slot.RC   = buf[10] >> 4;
	//slot.RR   = buf[10] & 0xF;
	//slot.AM   = buf[11] & 7;
	//slot.startaddr = buf[2] | (buf[1] << 8) |
	//				 ((buf[0] & 0x3F) << 16);
	//slot.loopaddr = buf[4] + (buf[3] << 8);
	//slot.endaddr  = (((buf[6] + (buf[5] << 8)) ^ 0xFFFF) + 1);
	//if ((regs[Register + 4] & 0x080)) {
	//	 keyOnHelper(slot);
	//}
	Ok(())
}