diff options
Diffstat (limited to 'src/mame/machine/nmk004.c')
-rw-r--r-- | src/mame/machine/nmk004.c | 1076 |
1 files changed, 1076 insertions, 0 deletions
diff --git a/src/mame/machine/nmk004.c b/src/mame/machine/nmk004.c new file mode 100644 index 00000000000..e11c94ed142 --- /dev/null +++ b/src/mame/machine/nmk004.c @@ -0,0 +1,1076 @@ +#include "driver.h" +#include "nmk004.h" +#include "sound/2203intf.h" +#include "sound/okim6295.h" + + + +#define FM_CHANNELS 6 +#define PSG_CHANNELS 3 +#define EFFECTS_CHANNELS 8 + +#define FM_FLAG_NEED_INITIALIZATION (1<<0) +#define FM_FLAG_UNKNOWN2 (1<<1) +#define FM_FLAG_NOTE_IS_PAUSE (1<<2) +#define FM_FLAG_UNKNOWN3 (1<<3) +#define FM_FLAG_MODULATE_NOTE (1<<4) +#define FM_FLAG_MUST_SEND_KEYON (1<<5) +#define FM_FLAG_MUST_SEND_CONFIGURATION (1<<6) +#define FM_FLAG_ACTIVE (1<<7) + +#define PSG_FLAG_ACTIVE (1<<0) +#define PSG_FLAG_NOTE_IS_PAUSE (1<<1) +#define PSG_FLAG_NEED_INITIALIZATION (1<<2) +#define PSG_FLAG_INITIALIZE_VOLUME (1<<3) +#define PSG_FLAG_NOTE_IS_NOISE (1<<5) +#define PSG_FLAG_NOISE_NOT_ENABLED (1<<6) + +#define EFFECTS_FLAG_NEED_INITIALIZATION (1<<0) +#define EFFECTS_FLAG_ACTIVE (1<<7) + +#define NOTE_PAUSE 0x0c + +struct psg_control +{ +/* C220 */ UINT8 flags; +/* C221-C222 */ UINT16 note_timer; +/* C223-C224 */ UINT16 note_length; +/* C225 */ UINT8 volume_timer; +/* C227-C228 */ UINT16 current; // current position in control table +/* C229-C22A */ UINT16 return_address[16]; // return address when control table calls a subtable + int return_address_depth; +/* C22B-C22C */ UINT16 loop_start; // first instruction of loop +/* C22D */ UINT8 loop_times; // number of times to loop +/* C22E */ UINT8 volume_shape; +/* C22F */ UINT8 volume_position; +/* C230 */ UINT8 octave; // base octave +/* C231 */ UINT8 note; // note to play +/* C233 */ UINT8 note_period_hi_bits; +}; + +struct fm_control +{ +UINT8 note; +/* C020 */ UINT8 flags; +/* C021 */ UINT8 slot; // for ym2203 keyon command +/* C022-C039 */ UINT8 voice_params[0x18]; // parameters for the YM2203 to configure sound shape +/* C03A-C03B */ UINT16 f_number; +/* C03C */ UINT8 self_feedback; +/* C03D */ UINT8 note_duration_table_select; +/* C03E-C03F */ UINT16 current; // current position in control table +/* C040-C041 */ UINT16 loop_start; // first instruction of loop +/* C042 */ UINT8 loop_times; // number of times to loop +/* C043-C044 */ UINT16 return_address[16]; // return address when control table calls a subtable + int return_address_depth; +/* C045 */ UINT8 octave; +/* C046-C047 */ UINT16 timer1; +/* C048-C049 */ UINT16 timer2; +/* C04A-C04B */ UINT16 timer1_duration; +/* C04C-C04D */ UINT16 timer2_duration; +/* C04E */ UINT8 modulation_table_number; +/* C04F-C050 */ UINT16 modulation_timer; +/* C051-C052 */ UINT16 modulation_table; +/* C053-C054 */ UINT16 modulation_table_position; +/* C055-C056 */ UINT16 note_period; +/* C057-C05A */ UINT8 voice_volume[4]; // parameters for the YM2203 to configure sound shape +/* C05C */ UINT8 must_update_voice_params; +}; + +struct effects_control +{ +/* C1A0 */ UINT8 flags; +/* C1BE-C1BF */ UINT16 current; // current position in control table +/* C1C0-C1C1 */ UINT16 loop_start; // first instruction of loop +/* C1C2 */ UINT8 loop_times; // number of times to loop +/* C1C3-C1C4 */ UINT16 return_address[16]; // return address when control table calls a subtable + int return_address_depth; +/* C1C6-C1C7 */ UINT16 timer; +/* C1CA-C1CB */ UINT16 timer_duration; +}; + +struct +{ + const UINT8 *rom; // NMK004 data ROM + UINT8 from_main; // command from main CPU + UINT8 to_main; // answer to main CPU + int protection_check; + + /* C001 */ UINT8 last_command; // last command received + /* C016 */ UINT8 oki_playing; // bitmap of active Oki channels + /* C020-C19F */ struct fm_control fm_control[FM_CHANNELS]; + /* C220-C2DF */ struct psg_control psg_control[PSG_CHANNELS]; + /* C1A0-C21F */ struct effects_control effects_control[EFFECTS_CHANNELS]; +} NMK004_state; + + +#define SAMPLE_TABLE_0 0xefe0 +#define SAMPLE_TABLE_1 0xefe2 +#define FM_MODULATION_TABLE 0xefe4 +#define FM_NOTE_TABLE 0xefe6 +#define NOTE_LENGTH_TABLE_1 0xefe8 +#define NOTE_LENGTH_TABLE_2 0xefea +#define NOTE_LENGTH_TABLE_3 0xefec +#define PSG_VOLUME_TABLE 0xefee +#define COMMAND_TABLE 0xeff0 +#define PSG_NOTE_TABLE 0xeff2 + + +static UINT8 read8(int address) +{ + return NMK004_state.rom[address]; +} + +static UINT16 read16(int address) +{ + return NMK004_state.rom[address] + 256 * NMK004_state.rom[address+1]; +} + + + +/***************************** + + OKI6295 + +*****************************/ + +static void oki_play_sample(int sample_no) +{ + UINT16 table_start = (sample_no & 0x80) ? read16(SAMPLE_TABLE_1) : read16(SAMPLE_TABLE_0); + UINT8 byte1 = read8(table_start + 2 * (sample_no & 0x7f) + 0); + UINT8 byte2 = read8(table_start + 2 * (sample_no & 0x7f) + 1); + int chip = (byte1 & 0x80) >> 7; + + if ((byte1 & 0x7f) == 0) + { + // stop all channels + if (chip == 0) + OKIM6295_data_0_w(0, 0x78 ); + else + OKIM6295_data_1_w(0, 0x78 ); + } + else + { + int sample = byte1 & 0x7f; + int ch = byte2 & 0x03; + int force = (byte2 & 0x80) >> 7; + + if (!force && (NMK004_state.oki_playing & (1 << (ch + 4*chip)))) + return; + + NMK004_state.oki_playing |= 1 << (ch + 4*chip); + + // stop channel + if (chip == 0) + OKIM6295_data_0_w(0, (0x08 << ch) ); + else + OKIM6295_data_1_w(0, (0x08 << ch) ); + + if (sample != 0) + { + UINT8 *rom = memory_region(REGION_SOUND1 + chip); + int bank = (byte2 & 0x0c) >> 2; + int vol = (byte2 & 0x70) >> 4; + + if (bank != 3) + memcpy(rom + 0x20000,rom + 0x40000 + bank * 0x20000,0x20000); + + if (chip == 0) + { + OKIM6295_data_0_w(0, 0x80 | sample ); + OKIM6295_data_0_w(0, (0x10 << ch) | vol ); + } + else + { + OKIM6295_data_1_w(0, 0x80 | sample ); + OKIM6295_data_1_w(0, (0x10 << ch) | vol ); + } + } + } +} + +static void oki_update_state(void) +{ + NMK004_state.oki_playing = ((OKIM6295_status_1_r(0) & 0x0f) << 4) | (OKIM6295_status_0_r(0) & 0x0f); +} + + + +/***************************** + + EFFECTS (OKI6295) + +*****************************/ + +static void effects_update(int channel) +{ + struct effects_control *effects = &NMK004_state.effects_control[channel]; + + // advance the timers + if (effects->timer) + effects->timer--; + + + if (effects->flags & EFFECTS_FLAG_NEED_INITIALIZATION) + { + effects->flags = EFFECTS_FLAG_ACTIVE; + effects->timer = 0; + } + + + if (effects->flags & EFFECTS_FLAG_ACTIVE) + { + if (effects->timer == 0) + { + UINT8 token; + + do + { +//logerror("channel %d address %04x token %02x\n",channel,effects->current,read8(effects->current)); + token = read8(effects->current++); + + if (token == 0x0ef || (token & 0xf0) == 0xf0) + { + switch (token) + { + case 0xef: // play sample + oki_play_sample(read8(effects->current++)); + break; + + case 0xf6: // jump + effects->current = read16(effects->current); + break; + + case 0xf7: // begin repeat loop + effects->loop_times = read8(effects->current++); + effects->loop_start = effects->current; + break; + + case 0xf8: // end repeat loop + if (--effects->loop_times > 0) + effects->current = effects->loop_start; + break; + + case 0xf9: // call subtable + effects->return_address[effects->return_address_depth++] = effects->current + 2; + effects->current = read16(effects->current); + break; + + case 0xfa: // return from subtable + effects->current = effects->return_address[--effects->return_address_depth]; + break; + + case 0xfc: // ??? (hachamf command 04) + break; + + case 0xfd: // ??? (hachamf command 04) + break; + + case 0xff: // end + effects->flags = 0; // disable channel + return; + + default: + fatalerror("effects channel %d unsupported token %02x",channel,token); + } + } + } while (token == 0xef || (token & 0xf0) == 0xf0); + + effects->current--; + + if ((read8(effects->current) & 0x80) == 0) + { + if (read8(effects->current++) != 0x0c) + { + // this shouldn't happen on the effects channels (but it happens e.g. hachamf command 04) + + logerror("effects channel %d invalid token %02x\n",channel,read8(effects->current)); + } + } + + // optional note length (otherwise use the same length as the previous one) + if (read8(effects->current) & 0x80) + { + UINT16 table_start = read16(NOTE_LENGTH_TABLE_1); + + effects->timer_duration = read16(table_start + 2 * (read8(effects->current++) & 0x7f)); + } + + effects->timer = effects->timer_duration; + } + } +} + + + +/***************************** + + YM2203 - FM + +*****************************/ + +static void fm_update(int channel) +{ + struct fm_control *fm = &NMK004_state.fm_control[channel]; + + // advance the timers + if (fm->timer1) + fm->timer1--; + + if (fm->timer2) + fm->timer2--; + + if (fm->modulation_timer) + fm->modulation_timer--; + + + if (fm->flags & FM_FLAG_NEED_INITIALIZATION) + { + fm->flags = FM_FLAG_ACTIVE; + fm->timer1 = 0; + } + + + if (fm->flags & FM_FLAG_ACTIVE) + { + if (fm->timer1 == 0) + { + UINT8 token; + + do + { +//logerror("channel %d address %04x token %02x\n",channel,fm->current,read8(fm->current)); + token = read8(fm->current++); + + if (token == 0x0ef || (token & 0xf0) == 0xf0) + { + int i; + + switch (token) + { +// case 0xef: // play sample +// oki_play_sample(read8(fm->current++)); +// break; + + case 0xf0: // slot (for keyon ym2203 command) + fm->flags |= FM_FLAG_MUST_SEND_CONFIGURATION; + fm->slot = read8(fm->current++); + if (channel < 3 || !(NMK004_state.fm_control[channel-3].flags & FM_FLAG_ACTIVE)) + { + YM2203_control_port_0_w(0, 0x28); // keyon/off + YM2203_write_port_0_w(0, channel % 3); + } + break; + + case 0xf1: // sound shape + fm->flags |= FM_FLAG_MUST_SEND_CONFIGURATION; + for (i = 0x00; i < 0x04; i++) + fm->voice_params[i] = read8(fm->current++); + break; + + case 0xf2: // sound shape + fm->flags |= FM_FLAG_MUST_SEND_CONFIGURATION; + for (i = 0; i < 4; i++) + fm->voice_volume[i] = read8(fm->current++); + break; + + case 0xf3: // sound shape + fm->flags |= FM_FLAG_MUST_SEND_CONFIGURATION; + for (i = 0x08; i < 0x18; i++) + fm->voice_params[i] = read8(fm->current++); + break; + + case 0xf4: // set self-feedback + fm->flags |= FM_FLAG_MUST_SEND_CONFIGURATION; + fm->self_feedback = read8(fm->current++); + break; + + case 0xf5: // select note duration table + fm->note_duration_table_select = read8(fm->current++); + break; + + case 0xf6: // jump + fm->current = read16(fm->current); + break; + + case 0xf7: // begin repeat loop + fm->loop_times = read8(fm->current++); + fm->loop_start = fm->current; + break; + + case 0xf8: // end repeat loop + if (--fm->loop_times > 0) + fm->current = fm->loop_start; + break; + + case 0xf9: // call subtable + fm->return_address[fm->return_address_depth++] = fm->current + 2; + fm->current = read16(fm->current); + break; + + case 0xfa: // return from subtable + fm->current = fm->return_address[--fm->return_address_depth]; + break; + + case 0xfb: // set octave + fm->octave = read8(fm->current++); + break; + + case 0xfc: // ??? + fm->flags |= FM_FLAG_UNKNOWN2; + fm->flags |= FM_FLAG_UNKNOWN3; + break; + + case 0xfd: // ??? + fm->flags &= ~FM_FLAG_UNKNOWN2; + break; + + case 0xfe: // set note modulation + fm->modulation_table_number = read8(fm->current++); + if (fm->modulation_table_number == 0) + { + fm->flags &= ~FM_FLAG_MODULATE_NOTE; + } + else + { + UINT16 table_start = read16(FM_MODULATION_TABLE); + + fm->modulation_table = read16(table_start + 2 * (fm->modulation_table_number - 1)); + fm->modulation_timer = read16(fm->modulation_table); + fm->modulation_table_position = fm->modulation_table + 2; + fm->flags |= FM_FLAG_MODULATE_NOTE; + } + break; + + case 0xff: // end + fm->flags = FM_FLAG_MUST_SEND_CONFIGURATION; // disable channel + for (i = 0x04; i < 0x08; i++) + fm->voice_params[i] = 0x7f; + for (i = 0x14; i < 0x18; i++) + fm->voice_params[i] = 0x0f; + return; + + default: + fatalerror("fm channel %d unsupported token %02x",channel,token); + } + } + } while (token == 0xef || (token & 0xf0) == 0xf0); + + fm->current--; + + if ((read8(fm->current) & 0x80) == 0) + { + int note = read8(fm->current++); + +fm->note = note; + if ((note & 0x0f) == NOTE_PAUSE) + fm->flags |= FM_FLAG_NOTE_IS_PAUSE; + else + { + UINT16 table_start = read16(FM_NOTE_TABLE); + UINT16 period = read16(table_start + 2 * (note & 0x0f)); + UINT8 octave = ((fm->octave << 4) + note) & 0xf0; + + fm->flags &= ~FM_FLAG_NOTE_IS_PAUSE; + fm->note_period = period | (octave << 7); + + fm->must_update_voice_params = 1; + } + } + + // optional note length (otherwise use the same length as the previous one) + if (read8(fm->current) & 0x80) + { + UINT16 table_start; + UINT8 duration = read8(fm->current++) & 0x7f; + + table_start = read16(NOTE_LENGTH_TABLE_1); + fm->timer1_duration = read16(table_start + 2 * duration); + + table_start = fm->note_duration_table_select ? read16(NOTE_LENGTH_TABLE_3) : read16(NOTE_LENGTH_TABLE_2); + fm->timer2_duration = read16(table_start + 2 * duration); + } + + fm->timer1 = fm->timer1_duration; + fm->timer2 = fm->timer2_duration; + + + if (!(fm->flags & FM_FLAG_NOTE_IS_PAUSE) && + ((fm->flags & FM_FLAG_UNKNOWN3) || !(fm->flags & FM_FLAG_UNKNOWN2))) + { + fm->flags &= ~FM_FLAG_UNKNOWN3; + fm->flags |= FM_FLAG_MUST_SEND_KEYON; + fm->flags |= FM_FLAG_MUST_SEND_CONFIGURATION; + } + } + } + + + + if ((fm->flags & FM_FLAG_MODULATE_NOTE) && (fm->flags & FM_FLAG_MUST_SEND_KEYON)) + { + fm->modulation_timer = read16(fm->modulation_table); + fm->modulation_table_position = fm->modulation_table + 2; + } + + if (!(fm->flags & FM_FLAG_MODULATE_NOTE) || (fm->flags & FM_FLAG_MUST_SEND_KEYON) || fm->must_update_voice_params) + { + int i; + + fm->must_update_voice_params = 0; + + for (i = 0; i < 4; i++) + fm->voice_params[0x04 + i] = fm->voice_volume[i]; + + fm->f_number = fm->note_period; + } + else + { + if (fm->modulation_timer == 0) + { + int i; + UINT16 a; + + for (i = 0; i < 4; i++) + fm->voice_params[0x04 + i] = fm->voice_volume[i]; + + fm->modulation_table_position++; + a = read8(fm->modulation_table_position++); + if (a & 0x80) // sign extend + a |= 0xff00; + a *= 4; + + fm->f_number = fm->note_period + a; + + fm->modulation_timer = read8(fm->modulation_table_position++); // modulation_timer is UINT16 but this is just 8-bit + + if (read8(fm->modulation_table_position) == 0x80) // end of table - repeat + { + fm->modulation_table_position = fm->modulation_table + 2; + } + else if (read8(fm->modulation_table_position) == 0x88) // end of table - stop + { + fm->flags &= ~FM_FLAG_MODULATE_NOTE; + } + } + } + + +#if 0 +popmessage("%02x %02x %02x %02x %02x %02x", + NMK004_state.fm_control[0].note, + NMK004_state.fm_control[1].note, + NMK004_state.fm_control[2].note, + NMK004_state.fm_control[3].note, + NMK004_state.fm_control[4].note, + NMK004_state.fm_control[5].note); +#endif +#if 0 +popmessage("%02x %02x%02x%02x%02x %02x %02x%02x%02x%02x %02x %02x%02x%02x%02x", + NMK004_state.fm_control[3].note, + NMK004_state.fm_control[3].voice_volume[0], + NMK004_state.fm_control[3].voice_volume[1], + NMK004_state.fm_control[3].voice_volume[2], + NMK004_state.fm_control[3].voice_volume[3], + NMK004_state.fm_control[4].note, + NMK004_state.fm_control[4].voice_volume[0], + NMK004_state.fm_control[4].voice_volume[1], + NMK004_state.fm_control[4].voice_volume[2], + NMK004_state.fm_control[4].voice_volume[3], + NMK004_state.fm_control[5].note, + NMK004_state.fm_control[5].voice_volume[0], + NMK004_state.fm_control[5].voice_volume[1], + NMK004_state.fm_control[5].voice_volume[2], + NMK004_state.fm_control[5].voice_volume[3]); +#endif +} + + +static void fm_voices_update(void) +{ + static int ym2203_registers[0x18] = + { + 0x30,0x38,0x34,0x3C,0x40,0x48,0x44,0x4C,0x50,0x58,0x54,0x5C,0x60,0x68,0x64,0x6C, + 0x70,0x78,0x74,0x7C,0x80,0x88,0x84,0x8C + }; + int channel,i; + + for (channel = 0; channel < 3;channel++) + { + struct fm_control *fm1 = &NMK004_state.fm_control[channel]; + struct fm_control *fm2 = &NMK004_state.fm_control[channel + 3]; + + if (fm1->flags & FM_FLAG_MUST_SEND_CONFIGURATION) + { + fm1->flags &= ~FM_FLAG_MUST_SEND_CONFIGURATION; + + for (i = 0; i < 0x18; i++) + { + YM2203_control_port_0_w(0, ym2203_registers[i] + channel); + YM2203_write_port_0_w(0, fm1->voice_params[i]); + } + } + + if (fm2->flags & FM_FLAG_MUST_SEND_CONFIGURATION) + { + fm2->flags &= ~FM_FLAG_MUST_SEND_CONFIGURATION; + + if (!(fm1->flags & FM_FLAG_ACTIVE)) + { + for (i = 0; i < 0x18; i++) + { + YM2203_control_port_0_w(0, ym2203_registers[i] + channel); + YM2203_write_port_0_w(0, fm2->voice_params[i]); + } + } + } + + + if (fm1->flags & FM_FLAG_ACTIVE) + { + YM2203_control_port_0_w(0, 0xb0 + channel); // self-feedback + YM2203_write_port_0_w(0, fm1->self_feedback); + + YM2203_control_port_0_w(0, 0xa4 + channel); // F-number + YM2203_write_port_0_w(0, fm1->f_number >> 8); + + YM2203_control_port_0_w(0, 0xa0 + channel); // F-number + YM2203_write_port_0_w(0, fm1->f_number & 0xff); + } + else + { + YM2203_control_port_0_w(0, 0xb0 + channel); // self-feedback + YM2203_write_port_0_w(0, fm2->self_feedback); + + YM2203_control_port_0_w(0, 0xa4 + channel); // F-number + YM2203_write_port_0_w(0, fm2->f_number >> 8); + + YM2203_control_port_0_w(0, 0xa0 + channel); // F-number + YM2203_write_port_0_w(0, fm2->f_number & 0xff); + } + + + + if (fm1->flags & FM_FLAG_MUST_SEND_KEYON) + { + fm1->flags &= ~FM_FLAG_MUST_SEND_KEYON; + + YM2203_control_port_0_w(0, 0x28); // keyon/off + YM2203_write_port_0_w(0, fm1->slot | channel); + } + + if (fm2->flags & FM_FLAG_MUST_SEND_KEYON) + { + fm2->flags &= ~FM_FLAG_MUST_SEND_KEYON; + + if (!(fm1->flags & FM_FLAG_ACTIVE)) + { + YM2203_control_port_0_w(0, 0x28); // keyon/off + YM2203_write_port_0_w(0, fm2->slot | channel); + } + } + } +} + + + +/***************************** + + YM2203 - PSG + +*****************************/ + +static void psg_update(int channel) +{ + struct psg_control *psg = &NMK004_state.psg_control[channel]; + + // advance the timers + if (psg->note_timer) + psg->note_timer--; + + if (psg->volume_timer) + psg->volume_timer--; + + if (psg->flags & PSG_FLAG_NEED_INITIALIZATION) + { + psg->flags &= ~PSG_FLAG_NEED_INITIALIZATION; + psg->flags |= PSG_FLAG_ACTIVE; + + if (psg->flags & PSG_FLAG_NOTE_IS_NOISE) + { + int enable; + + psg->flags &= ~PSG_FLAG_NOTE_IS_NOISE; + psg->flags &= ~PSG_FLAG_NOISE_NOT_ENABLED; + + // enable noise, disable tone on this channel + YM2203_control_port_0_w(0, 0x07); + enable = YM2203_read_port_0_r(0); + enable |= (0x01 << channel); // disable tone + enable &= ~(0x08 << channel); // enable noise + YM2203_write_port_0_w(0, enable); + } + + + psg->note_timer = 0; + psg->volume_timer = 0; + psg->octave = 0; + } + + if (psg->flags & PSG_FLAG_ACTIVE) + { + if (psg->note_timer == 0) + { + UINT8 token; + + do + { + token = read8(psg->current++); + + if ((token & 0xf0) == 0xf0) + { + int enable; + + switch (token) + { + case 0xf0: // noise + psg->flags |= PSG_FLAG_NOTE_IS_NOISE; + break; + + case 0xf1: // note + psg->flags &= ~PSG_FLAG_NOTE_IS_NOISE; + psg->flags &= ~PSG_FLAG_NOISE_NOT_ENABLED; + + // enable noise, disable tone on this channel + YM2203_control_port_0_w(0, 0x07); + enable = YM2203_read_port_0_r(0); + enable |= (0x01 << channel); // disable tone + enable &= ~(0x08 << channel); // enable noise + YM2203_write_port_0_w(0, enable); + break; + + case 0xf2: // set volume shape + case 0xf3: + case 0xf4: + case 0xf5: + psg->volume_shape = read8(psg->current++); + break; + + case 0xf6: // jump + psg->current = read16(psg->current); + break; + + case 0xf7: // begin repeat loop + psg->loop_times = read8(psg->current++); + psg->loop_start = psg->current; + break; + + case 0xf8: // end repeat loop + if (--psg->loop_times > 0) + psg->current = psg->loop_start; + break; + + case 0xf9: // call subtable + psg->return_address[psg->return_address_depth++] = psg->current + 2; + psg->current = read16(psg->current); + break; + + case 0xfa: // return from subtable + psg->current = psg->return_address[--psg->return_address_depth]; + break; + + case 0xfb: // set octave + case 0xfc: + case 0xfd: + case 0xfe: + psg->octave = read8(psg->current++); + break; + + case 0xff: // end + psg->flags = 0; // disable channel + psg->volume_shape = 0; + + // mute channel + YM2203_control_port_0_w(0, 8 + channel); + YM2203_write_port_0_w(0, 0); + return; + } + } + } while ((token & 0xf0) == 0xf0); + + // token is the note to play + psg->note = token; + if ((psg->note & 0x0f) > NOTE_PAUSE) + { + fatalerror("PSG channel %d invalid note %02x",channel,psg->note); + } + + // optional note length (otherwise use the same length as the previous one) + if (read8(psg->current) & 0x80) + { + UINT16 table_start = read16(NOTE_LENGTH_TABLE_1); + + psg->note_length = read16(table_start + 2 * (read8(psg->current++) & 0x7f)); + } + + psg->note_timer = psg->note_length; + psg->volume_timer = 0; + + psg->flags |= PSG_FLAG_INITIALIZE_VOLUME; + + if (psg->note == NOTE_PAUSE) + psg->flags |= PSG_FLAG_NOTE_IS_PAUSE; + else + { + psg->flags &= ~PSG_FLAG_NOTE_IS_PAUSE; + + if (!(psg->flags & PSG_FLAG_NOTE_IS_NOISE)) + { + UINT16 table_start = read16(PSG_NOTE_TABLE); + UINT16 period = read16(table_start + 2 * (psg->note & 0x0f)); + UINT8 octave = psg->octave + ((psg->note & 0xf0) >> 4); + + period >>= octave; + + YM2203_control_port_0_w(0, 2 * channel + 1); + YM2203_write_port_0_w(0, (period & 0x0f00) >> 8); + YM2203_control_port_0_w(0, 2 * channel + 0); + YM2203_write_port_0_w(0, (period & 0x00ff)); + + psg->note_period_hi_bits = (period & 0x0f00) >> 8; + } + else + { + if (!(psg->flags & PSG_FLAG_NOISE_NOT_ENABLED)) + { + int enable; + + psg->flags |= PSG_FLAG_NOISE_NOT_ENABLED; + + // disable noise, enable tone on this channel + YM2203_control_port_0_w(0, 0x07); + enable = YM2203_read_port_0_r(0); + enable &= ~(0x01 << channel); // enable tone + enable |= (0x08 << channel); // disable noise + YM2203_write_port_0_w(0, enable); + } + + YM2203_control_port_0_w(0, 0x06); // noise period + YM2203_write_port_0_w(0, psg->note); + psg->note_period_hi_bits = psg->note; + } + } + } + + if (psg->volume_timer == 0) + { + UINT16 table_start = read16(PSG_VOLUME_TABLE); + UINT16 vol_table_start = read16(table_start + 2 * psg->volume_shape); + int volume; + + if (psg->flags & PSG_FLAG_INITIALIZE_VOLUME) + { + psg->flags &= ~PSG_FLAG_INITIALIZE_VOLUME; + psg->volume_position = 0; + } + + volume = read8(vol_table_start + psg->volume_position++); + psg->volume_timer = read8(vol_table_start + psg->volume_position++); + + if (psg->flags & PSG_FLAG_NOTE_IS_PAUSE) + volume = 0; + + // set volume + YM2203_control_port_0_w(0, 8 + channel); + YM2203_write_port_0_w(0, volume & 0x0f); + } + } +} + + + +/***************************** + + Command processing + +*****************************/ + +static void get_command(void) +{ + static UINT8 from_main[] = + { + 0x00,0x22,0x62,0x8c,0xc7,0x00,0x00,0x3f,0x7f,0x89,0xc7,0x00,0x00,0x2b,0x6b + }; + static UINT8 to_main[] = + { + 0x82,0xc7,0x00,0x2c,0x6c,0x00,0x9f,0xc7,0x00,0x29,0x69,0x00,0x8b,0xc7,0x00 + }; + + UINT8 cmd = NMK004_state.from_main; + + if (NMK004_state.protection_check < sizeof(to_main)) + { + // startup handshake + if (cmd == from_main[NMK004_state.protection_check]) + { + logerror("advance handshake to %02x\n",to_main[NMK004_state.protection_check]); + NMK004_state.to_main = to_main[NMK004_state.protection_check++]; + } + } + else + { + // send command back to main CPU to acknowledge reception + NMK004_state.to_main = cmd; + } + + if (NMK004_state.last_command != cmd) + { + UINT16 table_start = read16(COMMAND_TABLE); + UINT16 cmd_table = read16(table_start + 2 * cmd); + + NMK004_state.last_command = cmd; + + if ((cmd_table & 0xff00) == 0) + { + oki_play_sample(cmd_table); + } + else + { + int channel; + + while ((channel = read8(cmd_table++)) != 0xff) + { + table_start = read16(cmd_table); + cmd_table += 2; + + if (channel == 0xef) break; // bioship bug? + +//logerror("%04x: channel %d table %04x\n",cmd_table-3,channel,table_start); + if (channel < FM_CHANNELS) + { + NMK004_state.fm_control[channel].current = table_start; + NMK004_state.fm_control[channel].return_address_depth = 0; + NMK004_state.fm_control[channel].flags |= FM_FLAG_NEED_INITIALIZATION; + } + else + { + channel -= FM_CHANNELS; + if (channel < PSG_CHANNELS) + { + NMK004_state.psg_control[channel].current = table_start; + NMK004_state.psg_control[channel].return_address_depth = 0; + NMK004_state.psg_control[channel].flags |= PSG_FLAG_NEED_INITIALIZATION; + } + else + { + channel -= PSG_CHANNELS; + if (channel >= EFFECTS_CHANNELS) + { + fatalerror("too many effects channels"); + } + NMK004_state.effects_control[channel].current = table_start; + NMK004_state.effects_control[channel].return_address_depth = 0; + NMK004_state.effects_control[channel].flags |= EFFECTS_FLAG_NEED_INITIALIZATION; + } + } + } + } + } +} + + + +static void update_music(void) +{ + int channel; + + for (channel = 0; channel < FM_CHANNELS; channel++) + fm_update(channel); + fm_voices_update(); + + for (channel = 0; channel < PSG_CHANNELS; channel++) + psg_update(channel); + + for (channel = 0; channel < EFFECTS_CHANNELS; channel++) + effects_update(channel); +} + + + +void NMK004_irq(int irq) +{ + int status; + + if (!irq) return; + + status = YM2203_status_port_0_r(0); + + if (status & 1) // timer A expired + { + oki_update_state(); + get_command(); + update_music(); + + // restart timer + YM2203_control_port_0_w(0, 0x27); + YM2203_write_port_0_w(0, 0x15); + } +} + + +static TIMER_CALLBACK( real_nmk004_init ) +{ + static UINT8 ym2203_init[] = + { + 0x07,0x38,0x08,0x00,0x09,0x00,0x0A,0x00,0x24,0xB3,0x25,0x00,0x26,0xF9,0x27,0x15, + 0x28,0x00,0x28,0x01,0x28,0x02,0x40,0x00,0x41,0x00,0x42,0x00,0x44,0x00,0x45,0x00, + 0x46,0x00,0x48,0x00,0x49,0x00,0x4A,0x00,0x4C,0x00,0x4D,0x00,0x4E,0x00,0xFF, + }; + int i; + + memset(&NMK004_state, 0, sizeof(NMK004_state)); + + NMK004_state.rom = memory_region(REGION_CPU2); + + YM2203_control_port_0_w(0, 0x2f); + + i = 0; + while (ym2203_init[i] != 0xff) + { + YM2203_control_port_0_w(0, ym2203_init[i++]); + YM2203_write_port_0_w(0, ym2203_init[i++]); + } + + NMK004_state.oki_playing = 0; + + oki_play_sample(0); + + NMK004_state.protection_check = 0; +} + +void NMK004_init(void) +{ + /* we have to do this via a timer because we get called before the sound reset */ + timer_call_after_resynch(0, real_nmk004_init); +} + + +WRITE16_HANDLER( NMK004_w ) +{ + if (ACCESSING_LSB) + { +logerror("%06x: NMK004_w %02x\n",activecpu_get_pc(),data); + NMK004_state.from_main = data & 0xff; + } +} + +READ16_HANDLER( NMK004_r ) +{ +static int last; + int res = NMK004_state.to_main; + +if (res != last) logerror("%06x: NMK004_r %02x\n",activecpu_get_pc(),res); +last = res; + + return res; +} + |