/*12log2(r) * 4096 freq = 2^((in - 0xe000) / 4096)*/ /*LFO - lowest (0.042 Hz) = 2^20 steps = 1048576 highest (10.72 Hz) = 2^12 steps = 4096*/ #include #include #include "../ibm.h" #include "../io.h" #include "../mem.h" #include "../rom.h" #include "../timer.h" #include "../device.h" #include "sound.h" #include "snd_emu8k.h" enum { ENV_STOPPED = 0, ENV_ATTACK = 1, ENV_DECAY = 2, ENV_SUSTAIN = 3, ENV_RELEASE = 4 }; static int64_t freqtable[65536]; static int attentable[256]; static int envtable[4096]; static int lfotable[4096]; static int32_t filt_w0[256]; /*static float filt_w0[256];*/ #define READ16(addr, var) switch ((addr) & 2) \ { \ case 0: ret = (var) & 0xffff; break; \ case 2: ret = ((var) >> 16) & 0xffff; break; \ } #define WRITE16(addr, var, val) switch ((addr) & 2) \ { \ case 0: var = (var & 0xffff0000) | (val); break; \ case 2: var = (var & 0x0000ffff) | ((val) << 16); break; \ } static __inline int16_t EMU8K_READ(emu8k_t *emu8k, uint16_t addr) { addr &= 0xffffff; if (addr < 0x80000) return emu8k->rom[addr]; if (addr < 0x200000 || addr >= emu8k->ram_end_addr) return 0; if (!emu8k->ram) return 0; return emu8k->ram[addr - 0x200000]; } static __inline int16_t EMU8K_READ_INTERP(emu8k_t *emu8k, uint16_t addr) { int16_t dat1 = EMU8K_READ(emu8k, addr >> 8); int16_t dat2 = EMU8K_READ(emu8k, (addr >> 8) + 1); return ((dat1 * (0xff - (addr & 0xff))) + (dat2 * (addr & 0xff))) >> 8; } static __inline void EMU8K_WRITE(emu8k_t *emu8k, uint16_t addr, uint16_t val) { addr &= 0xffffff; if (emu8k->ram && addr >= 0x200000 && addr < emu8k->ram_end_addr) emu8k->ram[addr - 0x200000] = val; } static int ff = 0; static int voice_count = 0; uint16_t emu8k_inw(uint16_t addr, void *p) { emu8k_t *emu8k = (emu8k_t *)p; uint16_t ret = 0xffff; /* pclog("emu8k_inw %04X reg=%i voice=%i\n", addr, emu8k->cur_reg, emu8k->cur_voice);*/ addr -= 0x220; switch (addr & 0xc02) { case 0x400: case 0x402: /*Data0*/ switch (emu8k->cur_reg) { case 0: READ16(addr, emu8k->voice[emu8k->cur_voice].cpf); return ret; case 1: READ16(addr, emu8k->voice[emu8k->cur_voice].ptrx); return ret; case 2: READ16(addr, emu8k->voice[emu8k->cur_voice].cvcf); return ret; case 3: READ16(addr, emu8k->voice[emu8k->cur_voice].vtft); return ret; case 4: case 5: /*???*/ return 0xffff; case 6: READ16(addr, emu8k->voice[emu8k->cur_voice].psst); return ret; case 7: READ16(addr, emu8k->voice[emu8k->cur_voice].cpf); return ret; } break; case 0x800: /*Data1*/ switch (emu8k->cur_reg) { case 0: { READ16(addr, emu8k->voice[emu8k->cur_voice].ccca); return ret; } case 1: switch (emu8k->cur_voice) { case 20: READ16(addr, emu8k->smalr); return ret; case 21: READ16(addr, emu8k->smarr); return ret; case 22: READ16(addr, emu8k->smalw); return ret; case 23: READ16(addr, emu8k->smarw); return ret; case 26: { uint16_t val = emu8k->smld_buffer; emu8k->smld_buffer = EMU8K_READ(emu8k, emu8k->smalr); /* pclog("emu8k_SMLR in %04X (%04X) %08X\n", val, emu8k->smld_buffer, emu8k->smalr);*/ emu8k->smalr++; return val; } /*The EMU8000 PGM describes the return values of these registers as 'a VLSI error'*/ case 29: /*Configuration Word 1*/ return (emu8k->hwcf1 & 0xfe) | (emu8k->hwcf3 & 0x01); case 30: /*Configuration Word 2*/ return ((emu8k->hwcf2 >> 4) & 0x0e) | (emu8k->hwcf1 & 0x01) | ((emu8k->hwcf3 & 0x02) ? 0x10 : 0) | ((emu8k->hwcf3 & 0x04) ? 0x40 : 0) | ((emu8k->hwcf3 & 0x08) ? 0x20 : 0) | ((emu8k->hwcf3 & 0x10) ? 0x80 : 0); case 31: /*Configuration Word 3*/ return emu8k->hwcf2 & 0x1f; } break; case 2: /*INIT1*/ case 3: /*INIT3*/ return 0xffff; /*Can we read anything useful from here?*/ case 5: return emu8k->voice[emu8k->cur_voice].dcysusv; case 7: return emu8k->voice[emu8k->cur_voice].dcysus; } break; case 0x802: /*Data2*/ switch (emu8k->cur_reg) { case 0: { READ16(addr, emu8k->voice[emu8k->cur_voice].ccca); return ret; } case 1: switch (emu8k->cur_voice) { case 20: READ16(addr, emu8k->smalr | ff); ff ^= 0x80000000; return ret; case 21: READ16(addr, emu8k->smarr | ff); ff ^= 0x80000000; return ret; case 22: READ16(addr, emu8k->smalw); return ret; case 23: READ16(addr, emu8k->smarw); return ret; case 26: { uint16_t val = emu8k->smrd_buffer; emu8k->smrd_buffer = EMU8K_READ(emu8k, emu8k->smarr); /* pclog("emu8k_SMRR in %04X (%04X) %08X\n", val, emu8k->smrd_buffer, emu8k->smarr);*/ emu8k->smarr++; return val; } case 27: /*Sample Counter*/ return emu8k->wc; } break; case 2: /*INIT2*/ case 3: /*INIT4*/ return 0xffff; /*Can we read anything useful from here?*/ case 4: return emu8k->voice[emu8k->cur_voice].atkhldv; } break; case 0xc00: /*Data3*/ switch (emu8k->cur_reg) { case 0: return emu8k->voice[emu8k->cur_voice].ip; case 1: return emu8k->voice[emu8k->cur_voice].ifatn; case 2: return emu8k->voice[emu8k->cur_voice].pefe; case 3: return emu8k->voice[emu8k->cur_voice].fmmod; case 4: return emu8k->voice[emu8k->cur_voice].tremfrq; case 5: return emu8k->voice[emu8k->cur_voice].fm2frq2; case 6: return 0xffff; case 7: /*ID?*/ return 0x1c | ((emu8k->id & 0x0002) ? 0xff02 : 0); } break; case 0xc02: /*Status - I think!*/ voice_count = (voice_count + 1) & 0x1f; /* emu8k->c02_read ^= 0x1000; pclog("Read status %04X\n", 0x803f | (voice_count << 8));*/ return 0x803f | (voice_count << 8); } /* fatal("Bad EMU8K inw from %08X\n", addr);*/ return 0xffff; } void emu8k_outw(uint16_t addr, uint16_t val, void *p) { emu8k_t *emu8k = (emu8k_t *)p; emu8k_update(emu8k); /* pclog("emu8k_outw : addr=%08X reg=%i voice=%i val=%04X\n", addr, emu8k->cur_reg, emu8k->cur_voice, val);*/ addr -= 0x220; switch (addr & 0xc02) { case 0x400: case 0x402: /*Data0*/ switch (emu8k->cur_reg) { case 0: WRITE16(addr, emu8k->voice[emu8k->cur_voice].cpf, val); return; case 1: WRITE16(addr, emu8k->voice[emu8k->cur_voice].ptrx, val); return; case 2: WRITE16(addr, emu8k->voice[emu8k->cur_voice].cvcf, val); return; case 3: WRITE16(addr, emu8k->voice[emu8k->cur_voice].vtft, val); return; case 6: WRITE16(addr, emu8k->voice[emu8k->cur_voice].psst, val); emu8k->voice[emu8k->cur_voice].loop_start = (uint64_t)(emu8k->voice[emu8k->cur_voice].psst & 0xffffff) << 32; if (addr & 2) { emu8k->voice[emu8k->cur_voice].vol_l = val >> 8; emu8k->voice[emu8k->cur_voice].vol_r = 255 - (val >> 8); } /* pclog("emu8k_outl : write PSST %08X l %i r %i\n", emu8k->voice[emu8k->cur_voice].psst, emu8k->voice[emu8k->cur_voice].vol_l, emu8k->voice[emu8k->cur_voice].vol_r);*/ return; case 7: WRITE16(addr, emu8k->voice[emu8k->cur_voice].cpf, val); emu8k->voice[emu8k->cur_voice].loop_end = (uint64_t)(emu8k->voice[emu8k->cur_voice].cpf & 0xffffff) << 32; /* pclog("emu8k_outl : write CPF %08X\n", emu8k->voice[emu8k->cur_voice].cpf);*/ return; } break; case 0x800: /*Data1*/ switch (emu8k->cur_reg) { case 0: WRITE16(addr, emu8k->voice[emu8k->cur_voice].ccca, val); emu8k->voice[emu8k->cur_voice].addr = (uint64_t)(emu8k->voice[emu8k->cur_voice].ccca & 0xffffff) << 32; /* pclog("emu8k_outl : write CCCA %08X\n", emu8k->voice[emu8k->cur_voice].ccca);*/ return; case 1: switch (emu8k->cur_voice) { case 20: WRITE16(addr, emu8k->smalr, val); return; case 21: WRITE16(addr, emu8k->smarr, val); return; case 22: WRITE16(addr, emu8k->smalw, val); return; case 23: WRITE16(addr, emu8k->smarw, val); return; case 26: EMU8K_WRITE(emu8k, emu8k->smalw, val); /* pclog("emu8k_SMLW %04X %08X\n", val, emu8k->smalw);*/ /* if (val = 0xffff && emu8k->smalw == 0x200000) output = 3;*/ emu8k->smalw++; break; case 29: /*Configuration Word 1*/ emu8k->hwcf1 = val; return; case 30: /*Configuration Word 2*/ emu8k->hwcf2 = val; return; case 31: /*Configuration Word 3*/ emu8k->hwcf3 = val; return; } break; case 5: /* pclog("emu8k_outw : write DCYSUSV %04X\n", val);*/ emu8k->voice[emu8k->cur_voice].dcysusv = val; emu8k->voice[emu8k->cur_voice].env_sustain = (((val >> 8) & 0x7f) << 5) << 9; if (val & 0x8000) /*Release*/ { emu8k->voice[emu8k->cur_voice].env_state = ENV_RELEASE; emu8k->voice[emu8k->cur_voice].env_release = val & 0x7f; } else /*Decay*/ emu8k->voice[emu8k->cur_voice].env_decay = val & 0x7f; if (val & 0x80) emu8k->voice[emu8k->cur_voice].env_state = ENV_STOPPED; return; case 7: /* pclog("emu8k_outw : write DCYSUS %04X\n", val);*/ emu8k->voice[emu8k->cur_voice].dcysus = val; emu8k->voice[emu8k->cur_voice].menv_sustain = (((val >> 8) & 0x7f) << 5) << 9; if (val & 0x8000) /*Release*/ { emu8k->voice[emu8k->cur_voice].menv_state = ENV_RELEASE; emu8k->voice[emu8k->cur_voice].menv_release = val & 0x7f; } else /*Decay*/ emu8k->voice[emu8k->cur_voice].menv_decay = val & 0x7f; if (val & 0x80) emu8k->voice[emu8k->cur_voice].menv_state = ENV_STOPPED; return; } break; case 0x802: /*Data2*/ switch (emu8k->cur_reg) { case 0: { float q; WRITE16(addr, emu8k->voice[emu8k->cur_voice].ccca, val); emu8k->voice[emu8k->cur_voice].addr = (uint64_t)(emu8k->voice[emu8k->cur_voice].ccca & 0xffffff) << 32; q = (float)(emu8k->voice[emu8k->cur_voice].ccca >> 28) / 15.0f; q /= 10.0f; /*Horrible and wrong hack*/ emu8k->voice[emu8k->cur_voice].q = (int32_t)((1.0f / (0.707f + q)) * 256.0f); /* pclog("emu8k_outl : write CCCA %08X Q %f invQ %X\n", emu8k->voice[emu8k->cur_voice].ccca, q, emu8k->voice[emu8k->cur_voice].q);*/ } return; case 1: switch (emu8k->cur_voice) { case 20: WRITE16(addr, emu8k->smalr, val); return; case 21: WRITE16(addr, emu8k->smarr, val); return; case 22: WRITE16(addr, emu8k->smalw, val); return; case 23: WRITE16(addr, emu8k->smarw, val); return; case 26: EMU8K_WRITE(emu8k, emu8k->smarw, val); /* pclog("emu8k_SMRW %04X %08X\n", val, emu8k->smarw);*/ emu8k->smarw++; break; } break; case 4: /* pclog("emu8k_outw : write ATKHLDV %04X\n", val);*/ emu8k->voice[emu8k->cur_voice].atkhldv = val; emu8k->voice[emu8k->cur_voice].env_attack = (val & 0x7f) << 6; if (!(val & 0x8000)) /*Trigger attack*/ emu8k->voice[emu8k->cur_voice].env_state = ENV_ATTACK; return; case 6: /* pclog("emu8k_outw : write ATKHLD %04X\n", val);*/ emu8k->voice[emu8k->cur_voice].atkhld = val; emu8k->voice[emu8k->cur_voice].menv_attack = (val & 0x7f) << 6; if (!(val & 0x8000)) /*Trigger attack*/ emu8k->voice[emu8k->cur_voice].menv_state = ENV_ATTACK; return; } break; case 0xc00: /*Data3*/ switch (emu8k->cur_reg) { case 0: emu8k->voice[emu8k->cur_voice].ip = val; emu8k->voice[emu8k->cur_voice].pitch = val; return; case 1: emu8k->voice[emu8k->cur_voice].ifatn = val; emu8k->voice[emu8k->cur_voice].attenuation = attentable[val & 0xff]; emu8k->voice[emu8k->cur_voice].cutoff = (val >> 8); /* pclog("Attenuation now %02X %i\n", val & 0xff, emu8k->voice[emu8k->cur_voice].attenuation);*/ return; case 2: emu8k->voice[emu8k->cur_voice].pefe = val; emu8k->voice[emu8k->cur_voice].fe_height = (int8_t)(val & 0xff); return; case 3: emu8k->voice[emu8k->cur_voice].fmmod = val; emu8k->voice[emu8k->cur_voice].lfo1_fmmod = (val >> 8); return; case 4: emu8k->voice[emu8k->cur_voice].tremfrq = val; emu8k->voice[emu8k->cur_voice].lfo1_trem = (val >> 8); return; case 5: emu8k->voice[emu8k->cur_voice].fm2frq2 = val; emu8k->voice[emu8k->cur_voice].lfo2_fmmod = (val >> 8); return; case 7: /*ID?*/ emu8k->id = val; return; } break; case 0xc02: /*Pointer*/ emu8k->cur_voice = (val & 31); emu8k->cur_reg = ((val >> 5) & 7); return; } } uint8_t emu8k_inb(uint16_t addr, void *p) { if (addr & 1) return emu8k_inw(addr & ~1, p) >> 1; return emu8k_inw(addr, p) & 0xff; } void emu8k_outb(uint16_t addr, uint8_t val, void *p) { if (addr & 1) emu8k_outw(addr & ~1, val << 8, p); else emu8k_outw(addr, val, p); } void emu8k_update(emu8k_t *emu8k) { int32_t *buf; int pos; int c; int new_pos = (sound_pos_global * 44100) / 48000; if (emu8k->pos < new_pos) { buf = &emu8k->buffer[emu8k->pos*2]; for (pos = emu8k->pos; pos < new_pos; pos++) emu8k->buffer[pos*2] = emu8k->buffer[pos*2 + 1] = 0; for (c = 0; c < 32; c++) { buf = &emu8k->buffer[emu8k->pos*2]; for (pos = emu8k->pos; pos < new_pos; pos++) { int32_t voice_l, voice_r; int32_t dat; int lfo1_vibrato, lfo2_vibrato; if (freqtable[emu8k->voice[c].pitch] >> 32) dat = EMU8K_READ(emu8k, emu8k->voice[c].addr >> 32); else dat = EMU8K_READ_INTERP(emu8k, emu8k->voice[c].addr >> 24); dat = (dat * emu8k->voice[c].attenuation) >> 16; dat = (dat * envtable[emu8k->voice[c].env_vol >> 9]) >> 16; if ((emu8k->voice[c].ccca >> 28) || (emu8k->voice[c].cutoff != 0xff)) { int cutoff = emu8k->voice[c].cutoff + ((emu8k->voice[c].menv_vol * emu8k->voice[c].fe_height) >> 20); if (cutoff < 0) cutoff = 0; if (cutoff > 255) cutoff = 255; emu8k->voice[c].vhp = ((-emu8k->voice[c].vbp * emu8k->voice[c].q) >> 8) - emu8k->voice[c].vlp - dat; emu8k->voice[c].vlp += (emu8k->voice[c].vbp * filt_w0[cutoff]) >> 8; emu8k->voice[c].vbp += (emu8k->voice[c].vhp * filt_w0[cutoff]) >> 8; if (emu8k->voice[c].vlp < -32767) dat = -32767; else if (emu8k->voice[c].vlp > 32767) dat = 32767; else dat = (int16_t)emu8k->voice[c].vlp; } voice_l = (dat * emu8k->voice[c].vol_l) >> 7; voice_r = (dat * emu8k->voice[c].vol_r) >> 7; (*buf++) += voice_l * 8192; (*buf++) += voice_r * 8192; switch (emu8k->voice[c].env_state) { case ENV_ATTACK: emu8k->voice[c].env_vol += emu8k->voice[c].env_attack; emu8k->voice[c].vtft |= 0xffff0000; if (emu8k->voice[c].env_vol >= (1 << 21)) { emu8k->voice[c].env_vol = 1 << 21; emu8k->voice[c].env_state = ENV_DECAY; } break; case ENV_DECAY: emu8k->voice[c].env_vol -= emu8k->voice[c].env_decay; emu8k->voice[c].vtft = (emu8k->voice[c].vtft & ~0xffff0000) | ((emu8k->voice[c].env_sustain >> 5) << 16); if (emu8k->voice[c].env_vol <= emu8k->voice[c].env_sustain) { emu8k->voice[c].env_vol = emu8k->voice[c].env_sustain; emu8k->voice[c].env_state = ENV_SUSTAIN; } break; case ENV_RELEASE: emu8k->voice[c].env_vol -= emu8k->voice[c].env_release; emu8k->voice[c].vtft &= ~0xffff0000; if (emu8k->voice[c].env_vol <= 0) { emu8k->voice[c].env_vol = 0; emu8k->voice[c].env_state = ENV_STOPPED; } break; } if (emu8k->voice[c].env_vol >= (1 << 21)) emu8k->voice[c].cvcf &= ~0xffff0000; else emu8k->voice[c].cvcf = (emu8k->voice[c].cvcf & ~0xffff0000) | ((emu8k->voice[c].env_vol >> 5) << 16); switch (emu8k->voice[c].menv_state) { case ENV_ATTACK: emu8k->voice[c].menv_vol += emu8k->voice[c].menv_attack; if (emu8k->voice[c].menv_vol >= (1 << 21)) { emu8k->voice[c].menv_vol = 1 << 21; emu8k->voice[c].menv_state = ENV_DECAY; } break; case ENV_DECAY: emu8k->voice[c].menv_vol -= emu8k->voice[c].menv_decay; if (emu8k->voice[c].menv_vol <= emu8k->voice[c].menv_sustain) { emu8k->voice[c].menv_vol = emu8k->voice[c].menv_sustain; emu8k->voice[c].menv_state = ENV_SUSTAIN; } break; case ENV_RELEASE: emu8k->voice[c].menv_vol -= emu8k->voice[c].menv_release; if (emu8k->voice[c].menv_vol <= 0) { emu8k->voice[c].menv_vol = 0; emu8k->voice[c].menv_state = ENV_STOPPED; } break; } lfo1_vibrato = (lfotable[(emu8k->voice[c].lfo1_count >> 8) & 4095] * emu8k->voice[c].lfo1_fmmod) >> 9; lfo2_vibrato = (lfotable[(emu8k->voice[c].lfo2_count >> 8) & 4095] * emu8k->voice[c].lfo2_fmmod) >> 9; emu8k->voice[c].addr += freqtable[(emu8k->voice[c].pitch + lfo1_vibrato + lfo2_vibrato) & 0xffff]; if (emu8k->voice[c].addr >= emu8k->voice[c].loop_end) emu8k->voice[c].addr -= (emu8k->voice[c].loop_end - emu8k->voice[c].loop_start); emu8k->voice[c].lfo1_count += (emu8k->voice[c].tremfrq & 0xff); emu8k->voice[c].lfo2_count += (emu8k->voice[c].fm2frq2 & 0xff); } } buf = &emu8k->buffer[emu8k->pos*2]; for (pos = emu8k->pos; pos < new_pos; pos++) { buf[0] >>= 15; buf[1] >>= 15; if (buf[0] < -32768) buf[0] = -32768; else if (buf[0] > 32767) buf[0] = 32767; if (buf[1] < -32768) buf[1] = -32768; else if (buf[1] > 32767) buf[1] = 32767; buf += 2; } emu8k->wc += (new_pos - emu8k->pos); emu8k->pos = new_pos; } } void emu8k_init(emu8k_t *emu8k, int onboard_ram) { FILE *f; int c; double out; f = romfopen(L"roms/awe32.raw", L"rb"); if (!f) fatal("ROMS/AWE32.RAW not found\n"); if (onboard_ram) { emu8k->ram = malloc(onboard_ram * 1024); emu8k->ram_end_addr = 0x200000 + ((onboard_ram * 1024) / 2); } emu8k->rom = malloc(1024 * 1024); fread(emu8k->rom, 1024 * 1024, 1, f); fclose(f); /*AWE-DUMP creates ROM images offset by 2 bytes, so if we detect this then correct it*/ if (emu8k->rom[3] == 0x314d && emu8k->rom[4] == 0x474d) { memcpy(&emu8k->rom[0], &emu8k->rom[1], (1024 * 1024) - 2); emu8k->rom[0x7ffff] = 0; } io_sethandler(0x0620, 0x0004, emu8k_inb, emu8k_inw, NULL, emu8k_outb, emu8k_outw, NULL, emu8k); io_sethandler(0x0a20, 0x0004, emu8k_inb, emu8k_inw, NULL, emu8k_outb, emu8k_outw, NULL, emu8k); io_sethandler(0x0e20, 0x0004, emu8k_inb, emu8k_inw, NULL, emu8k_outb, emu8k_outw, NULL, emu8k); /*Create frequency table*/ for (c = 0; c < 0x10000; c++) { freqtable[c] = (uint64_t)(exp2((double)(c - 0xe000) / 4096.0) * 65536.0 * 65536.0); } out = 65536.0; for (c = 0; c < 256; c++) { attentable[c] = (int)out; out /= sqrt(1.09018); /*0.375 dB steps*/ } out = 65536; for (c = 0; c < 4096; c++) { envtable[4095 - c] = (int)out; out /= 1.002709201; /*0.0235 dB Steps*/ } for (c = 0; c < 4096; c++) { int d = (c + 1024) & 4095; if (d >= 2048) lfotable[c] = 4096 - ((2048 - d) * 4); else lfotable[c] = (d * 4) - 4096; } out = 125.0; for (c = 0; c < 256; c++) { /* filt_w0[c] = (int32_t)((2.0 * 3.142 * (out / 44100.0)) * 0.707 * 256.0);*/ /* filt_w0[c] = 2.0 * 3.142 * (out / 44100.0);*/ filt_w0[c] = (int32_t)(2.0 * 3.142 * (out / 44100.0) * 256.0); out *= 1.016378315; } emu8k->hwcf1 = 0x59; emu8k->hwcf2 = 0x20; emu8k->hwcf3 = 0x04; } void emu8k_close(emu8k_t *emu8k) { free(emu8k->rom); free(emu8k->ram); }