diff --git a/opmplay/lxmplay/main.cpp b/opmplay/lxmplay/main.cpp index d45257f..57b68b9 100644 --- a/opmplay/lxmplay/main.cpp +++ b/opmplay/lxmplay/main.cpp @@ -509,6 +509,24 @@ int main(int argc, char* argv[]) yy++; if ((ch % 3) == 2) { + tprintf(0, yy, "AY%d: [%02X %02X] [%02X %02X] [%02X %02X] %02X %02X [%02X %02X %02X] [%02X %02X] <- %02X", + cc, + opn_reg_view[cc][0], + opn_reg_view[cc][1], + opn_reg_view[cc][2], + opn_reg_view[cc][3], + opn_reg_view[cc][4], + opn_reg_view[cc][5], + opn_reg_view[cc][6], + opn_reg_view[cc][7], + opn_reg_view[cc][8], + opn_reg_view[cc][9], + opn_reg_view[cc][10], + opn_reg_view[cc][11], + opn_reg_view[cc][12], + opn_reg_view[cc][13] + ); + yy++; tprintf(0, yy, "EC%d: [%02X %02X %02X %02X %02X %02X %02X %02X]", cc, opn_reg_view[cc][0xAD], @@ -521,6 +539,7 @@ int main(int argc, char* argv[]) opn_reg_view[cc][0xA2] ); yy++; + } } } diff --git a/opmplay/lxmplay/opmplay.cpp b/opmplay/lxmplay/opmplay.cpp index a414ebe..359cc01 100644 --- a/opmplay/lxmplay/opmplay.cpp +++ b/opmplay/lxmplay/opmplay.cpp @@ -206,6 +206,81 @@ extern "C" { bool doNextOp; } +// AY channel stream +const uint8_t* opmplay_parse_ay_channel_stream(opmplay_context_t* ctx, opmplay_channel_context_t* chctx, int ch, const uint8_t* data) { + if ((*(data) & 0x80) == OPM_AYTONE_REGS) { + // volume and period low + int mask = *data; + data++; + opn_write_reg(chip_index, 8 + ch, (mask & OPM_AYTONE_CMD00_VOLUME_MASK)); + if (mask & OPM_AYTONE_CMD00_PERIOD_LOW) opn_write_reg(chip_index, 0 + (ch<<1), *data++); + if (mask & OPM_AYTONE_CMD00_EOF) endOfFrame = true; + doNextOp = true; + goto done; + } + if ((*(data) & 0xC0) == OPM_AYTONE_PERIOD) { + // period high/low + int mask = *data; + data++; + opn_write_reg(chip_index, 1 + (ch<<1), (mask & OPM_AYTONE_CMD80_PERIOD_HIGH)); + if (mask & OPM_AYTONE_CMD80_PERIOD_LOW) opn_write_reg(chip_index, 0 + (ch << 1), *data++); + if (mask & OPM_AYTONE_CMD80_EOF) endOfFrame = true; + doNextOp = true; + goto done; + } + if ((*(data) & 0xF8) == OPM_AYTONE_MASK) { + const static uint8_t mask_lookup[4] = {(0<<3)|(0<<0), (0<<3)|(1<<0), (1<<3)|(0<<0), (1<<3)|(1<<0)}; + + // mask + int mask = *data; + data++; + ctx->ssg_r7[chip_index] &= ~(((1 << 3) | (1 << 0)) << ch); + ctx->ssg_r7[chip_index] |= (mask_lookup[mask & 3] << ch); + if (mask & OPM_AYTONE_MASK_EOF) endOfFrame = true; + doNextOp = true; + goto done; + } +done: + return data; +} + +// AY noise/envelope stream +const uint8_t* opmplay_parse_ay_envnoise_stream(opmplay_context_t* ctx, opmplay_channel_context_t* chctx, int ch, const uint8_t* data) { + if ((*(data) & 0x80) == OPM_AYENV_REGS) { + // noise and period low + int mask = *data; + data++; + opn_write_reg(chip_index, 6, (mask & OPM_AYENV_CMD00_NOISE_MASK)); + if (mask & OPM_AYENV_CMD00_PERIOD_LOW) opn_write_reg(chip_index, 11, *data++); + if (mask & OPM_AYENV_CMD00_EOF) endOfFrame = true; + doNextOp = true; + goto done; + } + if ((*(data) & 0xC0) == OPM_AYENV_ENVTYPE) { + // envelope type (and retrig) + int mask = *data; + data++; + opn_write_reg(chip_index, 13, (mask & OPM_AYENV_CMD80_ENV_TYPE)); + if (mask & OPM_AYENV_CMD80_PERIOD_LOW) opn_write_reg(chip_index, 11, *data++); + if (mask & OPM_AYENV_CMD80_EOF) endOfFrame = true; + doNextOp = true; + goto done; + } + if ((*(data) & 0xF8) == OPM_AYENV_PERIOD_FULL) { + // mask + int mask = *data; + data++; + if (mask & OPM_AYENV_CMDF0_PERIOD_LOW) opn_write_reg(chip_index, 11, *data++); + if (mask & OPM_AYENV_CMDF0_PERIOD_HIGH) opn_write_reg(chip_index, 12, *data++); + if (mask & OPM_AYENV_CMDF0_EOF) endOfFrame = true; + doNextOp = true; + goto done; + } +done: + return data; +} + + // FM control stream const uint8_t* opmplay_parse_fm_control_stream(opmplay_context_t* ctx, opmplay_channel_context_t* chctx, int ch, const uint8_t* data) { if ((*(data) & 0xE0) == OPM_CTRL_TIMER_CSM) { @@ -225,34 +300,25 @@ const uint8_t* opmplay_parse_fm_control_stream(opmplay_context_t* ctx, opmplay_c // OP4 freq is handled by FM channel 3 stream int mask = *data; data++; - if (mask & OPM_CTRL_EXTCH3_OP1_HIGH) opn_write_reg(chip_index, 0xAD, *data++); - if (mask & OPM_CTRL_EXTCH3_OP1_LOW) opn_write_reg(chip_index, 0xA9, *data++); - if (mask & OPM_CTRL_EXTCH3_OP2_HIGH) opn_write_reg(chip_index, 0xAC, *data++); - if (mask & OPM_CTRL_EXTCH3_OP2_LOW) opn_write_reg(chip_index, 0xA8, *data++); - if (mask & OPM_CTRL_EXTCH3_OP3_HIGH) opn_write_reg(chip_index, 0xAE, *data++); - if (mask & OPM_CTRL_EXTCH3_OP3_LOW) opn_write_reg(chip_index, 0xAA, *data++); + if (mask & OPM_CTRL_EXTCH3_OP1_HIGH) ctx->extch3_block[chip_index][0] = *data++; + if (mask & OPM_CTRL_EXTCH3_OP1_LOW) { + opn_write_reg(chip_index, 0xAD, ctx->extch3_block[chip_index][0]); + opn_write_reg(chip_index, 0xA9, *data++); + } + if (mask & OPM_CTRL_EXTCH3_OP2_HIGH) ctx->extch3_block[chip_index][1] = *data++; + if (mask & OPM_CTRL_EXTCH3_OP2_LOW) { + opn_write_reg(chip_index, 0xAC, ctx->extch3_block[chip_index][1]); + opn_write_reg(chip_index, 0xA8, *data++); + } + if (mask & OPM_CTRL_EXTCH3_OP3_HIGH) ctx->extch3_block[chip_index][2] = *data++; + if (mask & OPM_CTRL_EXTCH3_OP3_LOW) { + opn_write_reg(chip_index, 0xAE, ctx->extch3_block[chip_index][2]); + opn_write_reg(chip_index, 0xAA, *data++); + } if (mask & OPM_CTRL_EXTCH3_EOF) endOfFrame = true; doNextOp = true; goto done; } - if ((*data & 0xF0) == OPM_STREAM_BACKREF) { - // back reference, nested call :) - int distance = ((*(data + 0) & 0x0F) << 8) | (*(data + 1)); - int frames_to_play = *(data + 2); - chctx->stream.ptr = data + 3; - opmplay_push_stack(chctx); - data -= distance; - chctx->stream.samples_to_play = frames_to_play; // hack? - doNextOp = true; - goto done; - } - if ((*data) == OPM_STREAM_END) { - // rewind to start - chctx->stream.reload = -1; - endOfFrame = true; - doNextOp = true; - goto done; - } done: return data; } @@ -287,8 +353,11 @@ const uint8_t* opmplay_parse_fm_channel_stream(opmplay_context_t* ctx, opmplay_c // frequency/feedpack int mask = *data; data++; - if (mask & OPM_FM_CMD80_REGA4) opn_write_reg(chip_index, 0xA4 + ch, *data++); - if (mask & OPM_FM_CMD80_REGA0) opn_write_reg(chip_index, 0xA0 + ch, *data++); + if (mask & OPM_FM_CMD80_REGA4) chctx->block = *data++; + if (mask & OPM_FM_CMD80_REGA0) { + opn_write_reg(chip_index, 0xA4 + ch, chctx->block); + opn_write_reg(chip_index, 0xA0 + ch, *data++); + } if (mask & OPM_FM_CMD80_REGB0) opn_write_reg(chip_index, 0xB0 + ch, *data++); if (mask & OPM_FM_CMD80_REGB4) opn_write_reg(chip_index, 0xB4 + ch, *data++); if (mask & OPM_FM_CMD80_EOF) endOfFrame = true; @@ -303,25 +372,6 @@ const uint8_t* opmplay_parse_fm_channel_stream(opmplay_context_t* ctx, opmplay_c doNextOp = true; goto done; } - - if ((*data & 0xF0) == OPM_STREAM_BACKREF) { - // back reference, nested call :) - int distance = ((*(data + 0) & 0x0F) << 8) | (*(data + 1)); - int frames_to_play = *(data + 2); - chctx->stream.ptr = data + 3; - opmplay_push_stack(chctx); - data -= distance; - chctx->stream.samples_to_play = frames_to_play; // hack? - doNextOp = true; - goto done; - } - if ((*data) == OPM_STREAM_END) { - // rewind to start - chctx->stream.reload = -1; - endOfFrame = true; - doNextOp = true; - goto done; - } done: return data; } @@ -343,6 +393,17 @@ const int opmplay_parse_stream(opmplay_context_t* ctx, opmplay_channel_context_t continue; } + if ((*data & 0xF0) == OPM_STREAM_BACKREF) { + // back reference, nested call :) + int distance = ((*(data + 0) & 0x0F) << 8) | (*(data + 1)); + int frames_to_play = *(data + 2); + chctx->stream.ptr = data + 3; + opmplay_push_stack(chctx); + data -= distance; + chctx->stream.samples_to_play = frames_to_play; // hack? + continue; + } + // check for common stuff switch (*data) { // just an NOP, break @@ -356,6 +417,11 @@ const int opmplay_parse_stream(opmplay_context_t* ctx, opmplay_channel_context_t chctx->stream.loop = data; data++; break; + case OPM_STREAM_END: + // rewind to start + chctx->stream.reload = -1; + endOfFrame = true; + break; // set new frame rate case OPM_STREAM_SET_FRAME_RATE: ctx->header.frame_rate = *(uint16_t*)(data + 1); data += 3; @@ -404,12 +470,25 @@ int opmplay_tick(opmplay_context_t* ctx) { opmplay_parse_stream(ctx, ctx->channels + 1, 0, opmplay_parse_fm_channel_stream); opmplay_parse_stream(ctx, ctx->channels + 2, 1, opmplay_parse_fm_channel_stream); opmplay_parse_stream(ctx, ctx->channels + 3, 2, opmplay_parse_fm_channel_stream); + + opmplay_parse_stream(ctx, ctx->channels + 4, 0, opmplay_parse_ay_channel_stream); + opmplay_parse_stream(ctx, ctx->channels + 5, 1, opmplay_parse_ay_channel_stream); + opmplay_parse_stream(ctx, ctx->channels + 6, 2, opmplay_parse_ay_channel_stream); + opmplay_parse_stream(ctx, ctx->channels + 7, 0, opmplay_parse_ay_envnoise_stream); + opn_write_reg(chip_index, 7, ctx->ssg_r7[chip_index]); + chip_index = 1; opmplay_parse_stream(ctx, ctx->channels + 8, 0, opmplay_parse_fm_control_stream); opmplay_parse_stream(ctx, ctx->channels + 9, 0, opmplay_parse_fm_channel_stream); opmplay_parse_stream(ctx, ctx->channels + 10, 1, opmplay_parse_fm_channel_stream); opmplay_parse_stream(ctx, ctx->channels + 11, 2, opmplay_parse_fm_channel_stream); + opmplay_parse_stream(ctx, ctx->channels + 12, 0, opmplay_parse_ay_channel_stream); + opmplay_parse_stream(ctx, ctx->channels + 13, 1, opmplay_parse_ay_channel_stream); + opmplay_parse_stream(ctx, ctx->channels + 14, 2, opmplay_parse_ay_channel_stream); + opmplay_parse_stream(ctx, ctx->channels + 15, 0, opmplay_parse_ay_envnoise_stream); + opn_write_reg(chip_index, 7, ctx->ssg_r7[chip_index]); + // advance frame counter ctx->pos.frame++; diff --git a/opmplay/lxmplay/opmplay.h b/opmplay/lxmplay/opmplay.h index 9a87f9f..b1a3593 100644 --- a/opmplay/lxmplay/opmplay.h +++ b/opmplay/lxmplay/opmplay.h @@ -88,6 +88,9 @@ struct opmplay_channel_context_t { const uint8_t* ptr; const uint8_t* loop; // if active } stream; + + // private stuff + uint8_t block; // cached block register }; struct opmplay_context_t { @@ -103,6 +106,10 @@ struct opmplay_context_t { uint16_t frame_looped; uint32_t samples; } pos; + + // private stuff + uint8_t ssg_r7[2]; // cached SSG mask register + uint8_t extch3_block[2][3]; // cached extch3 block }; diff --git a/vgm2opl_next/vgm2opl_next/opmfile.h b/vgm2opl_next/vgm2opl_next/opmfile.h index d1e95ac..ebb2daf 100644 --- a/vgm2opl_next/vgm2opl_next/opmfile.h +++ b/vgm2opl_next/vgm2opl_next/opmfile.h @@ -150,7 +150,7 @@ enum { OPM_AYTONE_CMD00_PERIOD_LOW = (1 << 5), OPM_AYTONE_CMD00_EOF = (1 << 6), - OPM_AYTONE_CMD80_PERIOD_HIGH = (0xF << 0), + OPM_AYTONE_CMD80_PERIOD_HIGH_MASK = (0xF << 0), OPM_AYTONE_CMD80_PERIOD_LOW = (1 << 4), OPM_AYTONE_CMD80_EOF = (1 << 5), diff --git a/vgm2opl_next/vgm2opl_next/vgm2opl_next.cpp b/vgm2opl_next/vgm2opl_next/vgm2opl_next.cpp index 4f38115..5a10756 100644 --- a/vgm2opl_next/vgm2opl_next/vgm2opl_next.cpp +++ b/vgm2opl_next/vgm2opl_next/vgm2opl_next.cpp @@ -208,7 +208,7 @@ int opm_requantize(opm_convert_context_t* ctx) { oplchans[channel].data.push_back((data >> ch) & ((1 << 0)|(1 << 3))); } break; - case 5: case 11: case 12: case 13: + case 6: case 11: case 12: case 13: // noise and envelope stuff channel = chip_chidx + ctx->ssg_chidx + 3; oplchans[channel].frame = currentFrame; @@ -1055,7 +1055,7 @@ int opm_serialize_ssg_tone_stream(opm_channel_t* chctx) { OPM_AYTONE_PERIOD | ((ef == 0) && (i == s.records.size() - 1) ? OPM_AYTONE_CMD80_EOF : 0) | (ef_cur & OPM_REC_AYTONE_PERIOD_LOW ? OPM_AYTONE_CMD80_PERIOD_LOW : 0) | - (e.aytone.period_hi & OPM_AYTONE_CMD80_PERIOD_HIGH) + (e.aytone.period_hi & OPM_AYTONE_CMD80_PERIOD_HIGH_MASK) ); if (ef_cur & OPM_REC_AYTONE_PERIOD_LOW) s.rawdata.push_back(e.aytone.period_low); } @@ -1076,11 +1076,11 @@ int opm_serialize_ssg_tone_stream(opm_channel_t* chctx) { if (ef & OPM_REC_AYTONE_MASK) { // write mask at last - ef & ~OPM_REC_AYTONE_MASK; + ef &= ~OPM_REC_AYTONE_MASK; // write registers s.rawdata.push_back( - OPM_AYTONE_REGS | - ((ef == 0) && (i == s.records.size() - 1) ? OPM_AYTONE_CMD00_EOF : 0) | + OPM_AYTONE_MASK | + ((ef == 0) && (i == s.records.size() - 1) ? OPM_AYTONE_MASK_EOF : 0) | (e.aytone.mask & 1 ? OPM_AYTONE_MASK_TONE : 0) | (e.aytone.mask & 8 ? OPM_AYTONE_MASK_NOISE : 0) );