summaryrefslogtreecommitdiffstatshomepage
path: root/src/mess/audio/wswan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mess/audio/wswan.c')
-rw-r--r--src/mess/audio/wswan.c268
1 files changed, 268 insertions, 0 deletions
diff --git a/src/mess/audio/wswan.c b/src/mess/audio/wswan.c
new file mode 100644
index 00000000000..03d7b5ce3a4
--- /dev/null
+++ b/src/mess/audio/wswan.c
@@ -0,0 +1,268 @@
+/**************************************************************************************
+
+ Wonderswan sound emulation
+
+ Wilbert Pol
+
+ Sound emulation is very preliminary and far from complete
+
+**************************************************************************************/
+
+#include "includes/wswan.h"
+
+
+struct CHAN {
+ UINT16 freq; /* frequency */
+ UINT32 period; /* period */
+ UINT32 pos; /* position */
+ UINT8 vol_left; /* volume left */
+ UINT8 vol_right; /* volume right */
+ UINT8 on; /* on/off */
+ INT8 signal; /* signal */
+};
+
+typedef struct SND wswan_sound_state;
+struct SND {
+ sound_stream *channel;
+ struct CHAN audio1; /* Audio channel 1 */
+ struct CHAN audio2; /* Audio channel 2 */
+ struct CHAN audio3; /* Audio channel 3 */
+ struct CHAN audio4; /* Audio channel 4 */
+ INT8 sweep_step; /* Sweep step */
+ UINT32 sweep_time; /* Sweep time */
+ UINT32 sweep_count; /* Sweep counter */
+ UINT8 noise_type; /* Noise generator type */
+ UINT8 noise_reset; /* Noise reset */
+ UINT8 noise_enable; /* Noise enable */
+ UINT16 sample_address; /* Sample address */
+ UINT8 audio2_voice; /* Audio 2 voice */
+ UINT8 audio3_sweep; /* Audio 3 sweep */
+ UINT8 audio4_noise; /* Audio 4 noise */
+ UINT8 mono; /* mono */
+ UINT8 voice_data; /* voice data */
+ UINT8 output_volume; /* output volume */
+ UINT8 external_stereo; /* external stereo */
+ UINT8 external_speaker; /* external speaker */
+ UINT16 noise_shift; /* Noise counter shift register */
+ UINT8 master_volume; /* Master volume */
+};
+
+
+INLINE wswan_sound_state *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == WSWAN);
+ return (wswan_sound_state *)downcast<legacy_device_base *>(device)->token();
+}
+
+static void wswan_ch_set_freq( running_machine &machine, struct CHAN *ch, UINT16 freq )
+{
+ freq &= 0x7ff; // docs say freq is 11bits and a few games (Morita Shougi, World Stadium + others) write 0x800 causing a divide by 0 crash
+ ch->freq = freq;
+ ch->period = machine.sample_rate() / (3072000 / ((2048 - freq) << 5));
+}
+
+WRITE8_DEVICE_HANDLER( wswan_sound_port_w )
+{
+ wswan_sound_state *state = get_safe_token(device);
+
+ state->channel->update();
+
+ switch( offset ) {
+ case 0x80: /* Audio 1 freq (lo) */
+ wswan_ch_set_freq(device->machine(), &state->audio1, (state->audio1.freq & 0xff00) | data);
+ break;
+ case 0x81: /* Audio 1 freq (hi) */
+ wswan_ch_set_freq(device->machine(), &state->audio1, (data << 8 ) | (state->audio1.freq & 0x00ff));
+ break;
+ case 0x82: /* Audio 2 freq (lo) */
+ wswan_ch_set_freq(device->machine(), &state->audio2, (state->audio2.freq & 0xff00) | data);
+ break;
+ case 0x83: /* Audio 2 freq (hi) */
+ wswan_ch_set_freq(device->machine(), &state->audio2, (data << 8 ) | (state->audio2.freq & 0x00ff));
+ break;
+ case 0x84: /* Audio 3 freq (lo) */
+ wswan_ch_set_freq(device->machine(), &state->audio3, (state->audio3.freq & 0xff00) | data);
+ break;
+ case 0x85: /* Audio 3 freq (hi) */
+ wswan_ch_set_freq(device->machine(), &state->audio3, (data << 8) | (state->audio3.freq & 0x00ff));
+ break;
+ case 0x86: /* Audio 4 freq (lo) */
+ wswan_ch_set_freq(device->machine(), &state->audio4, (state->audio4.freq & 0xff00) | data);
+ break;
+ case 0x87: /* Audio 4 freq (hi) */
+ wswan_ch_set_freq(device->machine(), &state->audio4, (data << 8) | (state->audio4.freq & 0x00ff));
+ break;
+ case 0x88: /* Audio 1 volume */
+ state->audio1.vol_left = ( data & 0xF0 ) >> 4;
+ state->audio1.vol_right = data & 0x0F;
+ break;
+ case 0x89: /* Audio 2 volume */
+ state->voice_data = data;
+ state->audio2.vol_left = ( data & 0xF0 ) >> 4;
+ state->audio2.vol_right = data & 0x0F;
+ break;
+ case 0x8A: /* Audio 3 volume */
+ state->audio3.vol_left = ( data & 0xF0 ) >> 4;
+ state->audio3.vol_right = data & 0x0F;
+ break;
+ case 0x8B: /* Audio 4 volume */
+ state->audio4.vol_left = ( data & 0xF0 ) >> 4;
+ state->audio4.vol_right = data & 0x0F;
+ break;
+ case 0x8C: /* Sweep step */
+ state->sweep_step = (INT8)data;
+ break;
+ case 0x8D: /* Sweep time */
+ state->sweep_time = device->machine().sample_rate() / ( 3072000 / ( 8192 * (data + 1) ) );
+ break;
+ case 0x8E: /* Noise control */
+ state->noise_type = data & 0x07;
+ state->noise_reset = ( data & 0x08 ) >> 3;
+ state->noise_enable = ( data & 0x10 ) >> 4;
+ break;
+ case 0x8F: /* Sample location */
+ state->sample_address = data << 6;
+ break;
+ case 0x90: /* Audio control */
+ state->audio1.on = data & 0x01;
+ state->audio2.on = ( data & 0x02 ) >> 1;
+ state->audio3.on = ( data & 0x04 ) >> 2;
+ state->audio4.on = ( data & 0x08 ) >> 3;
+ state->audio2_voice = ( data & 0x20 ) >> 5;
+ state->audio3_sweep = ( data & 0x40 ) >> 6;
+ state->audio4_noise = ( data & 0x80 ) >> 7;
+ break;
+ case 0x91: /* Audio output */
+ state->mono = data & 0x01;
+ state->output_volume = ( data & 0x06 ) >> 1;
+ state->external_stereo = ( data & 0x08 ) >> 3;
+ state->external_speaker = 1;
+ break;
+ case 0x92: /* Noise counter shift register (lo) */
+ state->noise_shift = ( state->noise_shift & 0xFF00 ) | data;
+ break;
+ case 0x93: /* Noise counter shift register (hi) */
+ state->noise_shift = ( data << 8 ) | ( state->noise_shift & 0x00FF );
+ break;
+ case 0x94: /* Master volume */
+ state->master_volume = data;
+ break;
+ }
+}
+
+static STREAM_UPDATE( wswan_sh_update )
+{
+ wswan_sound_state *state = get_safe_token(device);
+ stream_sample_t sample, left, right;
+
+ while( samples-- > 0 )
+ {
+ left = right = 0;
+
+ if ( state->audio1.on ) {
+ sample = state->audio1.signal;
+ state->audio1.pos++;
+ if ( state->audio1.pos >= state->audio1.period / 2 ) {
+ state->audio1.pos = 0;
+ state->audio1.signal = -state->audio1.signal;
+ }
+ left += state->audio1.vol_left * sample;
+ right += state->audio1.vol_right * sample;
+ }
+
+ if ( state->audio2.on ) {
+ if ( state->audio2_voice ) {
+ left += (state->voice_data - 128)*(state->master_volume & 0x0f);
+ right += (state->voice_data - 128)*(state->master_volume & 0x0f);
+ } else {
+ sample = state->audio2.signal;
+ state->audio2.pos++;
+ if ( state->audio2.pos >= state->audio2.period / 2 ) {
+ state->audio2.pos = 0;
+ state->audio2.signal = -state->audio2.signal;
+ }
+ left += state->audio2.vol_left * sample;
+ right += state->audio2.vol_right * sample;
+ }
+ }
+
+ if ( state->audio3.on ) {
+ sample = state->audio3.signal;
+ state->audio3.pos++;
+ if ( state->audio3.pos >= state->audio3.period / 2 ) {
+ state->audio3.pos = 0;
+ state->audio3.signal = -state->audio3.signal;
+ }
+ if ( state->audio3_sweep && state->sweep_time ) {
+ state->sweep_count++;
+ if ( state->sweep_count >= state->sweep_time ) {
+ state->sweep_count = 0;
+ state->audio3.freq += state->sweep_step;
+ state->audio3.period = device->machine().sample_rate() / (3072000 / ((2048 - (state->audio3.freq & 0x7ff)) << 5));
+ }
+ }
+ left += state->audio3.vol_left * sample;
+ right += state->audio3.vol_right * sample;
+ }
+
+ if ( state->audio4.on ) {
+ if ( state->audio4_noise ) {
+ sample = 0;
+ } else {
+ sample = state->audio4.signal;
+ state->audio4.pos++;
+ if ( state->audio4.pos >= state->audio4.period / 2 ) {
+ state->audio4.pos = 0;
+ state->audio4.signal = -state->audio4.signal;
+ }
+ }
+ left += state->audio4.vol_left * sample;
+ right += state->audio4.vol_right * sample;
+ }
+
+ left <<= 5;
+ right <<= 5;
+
+ *(outputs[0]++) = left;
+ *(outputs[1]++) = right;
+ }
+}
+
+static DEVICE_START(wswan_sound)
+{
+ wswan_sound_state *state = get_safe_token(device);
+ state->channel = device->machine().sound().stream_alloc(*device, 0, 2, device->machine().sample_rate(), 0, wswan_sh_update);
+
+ state->audio1.on = 0;
+ state->audio1.signal = 16;
+ state->audio1.pos = 0;
+ state->audio2.on = 0;
+ state->audio2.signal = 16;
+ state->audio2.pos = 0;
+ state->audio3.on = 0;
+ state->audio3.signal = 16;
+ state->audio3.pos = 0;
+ state->audio4.on = 0;
+ state->audio4.signal = 16;
+ state->audio4.pos = 0;
+}
+
+
+DEVICE_GET_INFO( wswan_sound )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(wswan_sound_state); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(wswan_sound); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "WonderSwan Custom"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ }
+}
+
+DEFINE_LEGACY_SOUND_DEVICE(WSWAN, wswan_sound);