Add Namco 163 Support
This commit is contained in:
parent
bdc29455cc
commit
5fdce33b11
16 changed files with 1225 additions and 6 deletions
137
src/engine/platform/sound/n163/n163.cpp
Normal file
137
src/engine/platform/sound/n163/n163.cpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://github.com/cam900/vgsound_emu/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Namco 163 Sound emulation core
|
||||
|
||||
This chip is one of NES mapper with sound expansion, This one is by Namco.
|
||||
|
||||
It has 1 to 8 wavetable channels, All channel registers and waveforms are stored to internal RAM.
|
||||
4 bit Waveforms are freely allocatable, and its length is variables; its can be stores many short waveforms or few long waveforms in RAM.
|
||||
|
||||
But waveforms are needs to squash, reallocate to avoid conflict with channel register area, each channel register size is 8 bytes per channels.
|
||||
|
||||
Sound output is time division multiplexed, it's can be captured only single channels output at once. in reason, More activated channels are less sound quality.
|
||||
|
||||
Sound register layout
|
||||
|
||||
Address Bit Description
|
||||
7654 3210
|
||||
|
||||
78-7f Channel 0
|
||||
78 xxxx xxxx Channel 0 Pitch input bit 0-7
|
||||
79 xxxx xxxx Channel 0 Accumulator bit 0-7*
|
||||
7a xxxx xxxx Channel 0 Pitch input bit 8-15
|
||||
7b xxxx xxxx Channel 0 Accumulator bit 8-15*
|
||||
7c xxxx xx-- Channel 0 Waveform length, 256 - (x * 4)
|
||||
---- --xx Channel 0 Pitch input bit 16-17
|
||||
7d xxxx xxxx Channel 0 Accumulator bit 16-23*
|
||||
7e xxxx xxxx Channel 0 Waveform base offset
|
||||
xxxx xxx- RAM byte (0 to 127)
|
||||
---- ---x RAM nibble
|
||||
---- ---0 Low nibble
|
||||
---- ---1 High nibble
|
||||
7f ---- xxxx Channel 0 Volume
|
||||
|
||||
7f Number of active channels
|
||||
7f -xxx ---- Number of active channels
|
||||
-000 ---- Channel 0 activated
|
||||
-001 ---- Channel 1 activated
|
||||
-010 ---- Channel 2 activated
|
||||
...
|
||||
-110 ---- Channel 6 activated
|
||||
-111 ---- Channel 7 activated
|
||||
|
||||
70-77 Channel 1 (Optional if activated)
|
||||
68-6f Channel 2 (Optional if activated)
|
||||
...
|
||||
48-4f Channel 6 (Optional if activated)
|
||||
40-47 Channel 7 (Optional if activated)
|
||||
|
||||
Rest of RAM area are for 4 bit Waveform and/or scratchpad.
|
||||
Each waveform byte has 2 nibbles packed, fetches LSB first, MSB next.
|
||||
---- xxxx 4 bit waveform, LSB
|
||||
xxxx ---- Same as above, MSB
|
||||
|
||||
Waveform address: Waveform base offset + Bit 16 to 23 of Accumulator, 1 LSB of result is nibble select, 7 MSB of result is Byte address in RAM.
|
||||
|
||||
Frequency formula:
|
||||
Frequency: Pitch input * ((Input clock * 15 * Number of activated voices) / 65536)
|
||||
*/
|
||||
|
||||
#include "n163.hpp"
|
||||
|
||||
void n163_core::tick()
|
||||
{
|
||||
m_out = 0;
|
||||
// 0xe000-0xe7ff Disable sound bits (bit 6, bit 0 to 5 are CPU ROM Bank 0x8000-0x9fff select.)
|
||||
if (m_disable)
|
||||
return;
|
||||
|
||||
// tick per each clock
|
||||
const u32 freq = m_ram[m_voice_cycle + 0] | (u32(m_ram[m_voice_cycle + 2]) << 8) | (bitfield<u32>(m_ram[m_voice_cycle + 4], 0, 2) << 16); // 18 bit frequency
|
||||
u32 accum = m_ram[m_voice_cycle + 1] | (u32(m_ram[m_voice_cycle + 3]) << 8) | ( u32(m_ram[m_voice_cycle + 5]) << 16); // 24 bit accumulator
|
||||
const u16 length = 256 - (m_ram[m_voice_cycle + 4] & 0xfc);
|
||||
const u8 addr = m_ram[m_voice_cycle + 6] + bitfield(accum, 16, 8);
|
||||
const s16 wave = (bitfield(m_ram[bitfield(addr, 1, 7)], bitfield(addr, 0) << 2, 4) - 8);
|
||||
const s16 volume = bitfield(m_ram[m_voice_cycle + 7], 0, 4);
|
||||
|
||||
// accumulate address
|
||||
accum = bitfield(accum + freq, 0, 24);
|
||||
if (bitfield(accum, 16, 8) >= length)
|
||||
accum = bitfield(accum, 0, 18);
|
||||
|
||||
// writeback to register
|
||||
m_ram[m_voice_cycle + 1] = bitfield(accum, 0, 8);
|
||||
m_ram[m_voice_cycle + 3] = bitfield(accum, 8, 8);
|
||||
m_ram[m_voice_cycle + 5] = bitfield(accum, 16, 8);
|
||||
|
||||
// update voice cycle
|
||||
m_voice_cycle -= 0x8;
|
||||
if (m_voice_cycle < (0x78 - (bitfield(m_ram[0x7f], 4, 3) << 3)))
|
||||
m_voice_cycle = 0x78;
|
||||
|
||||
// output 4 bit waveform and volume, multiplexed
|
||||
m_out = wave * volume;
|
||||
}
|
||||
|
||||
void n163_core::reset()
|
||||
{
|
||||
// reset this chip
|
||||
m_disable = false;
|
||||
std::fill(std::begin(m_ram), std::end(m_ram), 0);
|
||||
m_voice_cycle = 0x78;
|
||||
m_addr_latch.reset();
|
||||
m_out = 0;
|
||||
}
|
||||
|
||||
// accessor
|
||||
void n163_core::addr_w(u8 data)
|
||||
{
|
||||
// 0xf800-0xffff Sound address, increment
|
||||
m_addr_latch.addr = bitfield(data, 0, 7);
|
||||
m_addr_latch.incr = bitfield(data, 7);
|
||||
}
|
||||
|
||||
void n163_core::data_w(u8 data, bool cpu_access)
|
||||
{
|
||||
// 0x4800-0x4fff Sound data write
|
||||
m_ram[m_addr_latch.addr] = data;
|
||||
|
||||
// address latch increment
|
||||
if (cpu_access && m_addr_latch.incr)
|
||||
m_addr_latch.addr = bitfield(m_addr_latch.addr + 1, 0, 7);
|
||||
}
|
||||
|
||||
u8 n163_core::data_r(bool cpu_access)
|
||||
{
|
||||
// 0x4800-0x4fff Sound data read
|
||||
const u8 ret = m_ram[m_addr_latch.addr];
|
||||
|
||||
// address latch increment
|
||||
if (cpu_access && m_addr_latch.incr)
|
||||
m_addr_latch.addr = bitfield(m_addr_latch.addr + 1, 0, 7);
|
||||
|
||||
return ret;
|
||||
}
|
||||
78
src/engine/platform/sound/n163/n163.hpp
Normal file
78
src/engine/platform/sound/n163/n163.hpp
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://github.com/cam900/vgsound_emu/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Namco 163 Sound emulation core
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifndef _VGSOUND_EMU_N163_HPP
|
||||
#define _VGSOUND_EMU_N163_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace n163
|
||||
{
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef signed short s16;
|
||||
|
||||
// get bitfield, bitfield(input, position, len)
|
||||
template<typename T> T bitfield(T in, u8 pos, u8 len = 1)
|
||||
{
|
||||
return (in >> pos) & (len ? (T(1 << len) - 1) : 1);
|
||||
}
|
||||
};
|
||||
|
||||
using namespace n163;
|
||||
class n163_core
|
||||
{
|
||||
public:
|
||||
// accessors, getters, setters
|
||||
void addr_w(u8 data);
|
||||
void data_w(u8 data, bool cpu_access = false);
|
||||
u8 data_r(bool cpu_access = false);
|
||||
|
||||
void set_disable(bool disable) { m_disable = disable; }
|
||||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
|
||||
// sound output pin
|
||||
s16 out() { return m_out; }
|
||||
|
||||
// register pool
|
||||
u8 reg(u8 addr) { return m_ram[addr & 0x7f]; }
|
||||
|
||||
private:
|
||||
// Address latch
|
||||
struct addr_latch_t
|
||||
{
|
||||
addr_latch_t()
|
||||
: addr(0)
|
||||
, incr(0)
|
||||
{ };
|
||||
|
||||
void reset()
|
||||
{
|
||||
addr = 0;
|
||||
incr = 0;
|
||||
}
|
||||
|
||||
u8 addr : 7;
|
||||
u8 incr : 1;
|
||||
};
|
||||
|
||||
bool m_disable = false;
|
||||
u8 m_ram[0x80] = {0}; // internal 128 byte RAM
|
||||
u8 m_voice_cycle = 0x78; // Voice cycle for processing
|
||||
addr_latch_t m_addr_latch; // address latch
|
||||
s16 m_out = 0; // output
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue