summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/audio/tiamc1.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/audio/tiamc1.c')
-rw-r--r--src/mame/audio/tiamc1.c314
1 files changed, 314 insertions, 0 deletions
diff --git a/src/mame/audio/tiamc1.c b/src/mame/audio/tiamc1.c
new file mode 100644
index 00000000000..3adca0faa96
--- /dev/null
+++ b/src/mame/audio/tiamc1.c
@@ -0,0 +1,314 @@
+/***************************************************************************
+
+ TIA-MC1 sound hardware
+
+ driver by Eugene Sandulenko
+ special thanks to Shiru for his standalone emulator and documentation
+
+ timer1 timer0
+ |--------| |--------|
+ | 8253 | | 8253 | |---|
+ in0 | | | | | 1 | |---|
+ ----+ g0 o0 +---+ g0 o0 +---+ | | & |
+ in1 | | | | | o---+ | out
+ ----+ g1 o1 +---+ g1 o1 +---+ | | +-----
+ in2 | | | | |---| | |
+ ----+ g2 o2 +---+ g2 o2 +-----------+ |
+ clk1| | | | |---|
+ ----+ clk | +-+ clk |
+ | | | | |
+ |--------| | |--------|
+ clk0 |
+ ---------------+
+
+ in0-in2 comes from port #da
+ clk0 comes from 8224 and equals to 1777777Hz, i.e. processor clock
+ clk1 comes from divider 16000000/4/16/16/2 = 7812Hz
+
+ ********************
+
+ TODO: use machine/pit8253.c
+
+***************************************************************************/
+
+#include "driver.h"
+#include "streams.h"
+#include "sound/custom.h"
+
+#define CLOCK_DIVIDER 16
+#define BUF_LEN 100000
+
+static sound_stream *channel;
+static int timer1_divider;
+
+struct timer8253chan {
+ UINT16 count;
+ UINT16 cnval;
+ UINT8 bcdMode;
+ UINT8 cntMode;
+ UINT8 valMode;
+ UINT8 gate;
+ UINT8 output;
+ UINT8 loadCnt;
+ UINT8 enable;
+};
+
+struct timer8253struct {
+ struct timer8253chan channel[3];
+};
+
+static struct timer8253struct timer0;
+static struct timer8253struct timer1;
+
+
+#define T8253_CHAN0 0
+#define T8253_CHAN1 1
+#define T8253_CHAN2 2
+#define T8253_CWORD 3
+
+
+static void timer8253_reset(struct timer8253struct *t) {
+ memset(t,0,sizeof(struct timer8253struct));
+}
+
+
+static void timer8253_tick(struct timer8253struct *t,int chn) {
+ if (t->channel[chn].enable && t->channel[chn].gate) {
+ switch (t->channel[chn].cntMode) {
+ case 0:
+ t->channel[chn].count--;
+ if (t->channel[chn].count == 0xffff)
+ t->channel[chn].output = 1;
+ break;
+
+ case 3:
+ t->channel[chn].count--;
+
+ if (t->channel[chn].count < (t->channel[chn].cnval >> 1))
+ t->channel[chn].output = 0;
+ else
+ t->channel[chn].output = 1;
+
+ if (t->channel[chn].count == 0xffff)
+ t->channel[chn].count = t->channel[chn].cnval;
+ break;
+
+ case 4:
+ t->channel[chn].count--;
+ if(t->channel[chn].count==0)
+ t->channel[chn].output = 1;
+
+ if(t->channel[chn].count == 0xffff) {
+ t->channel[chn].enable = 0;
+ t->channel[chn].output = 1;
+ }
+ break;
+ }
+ }
+}
+
+
+
+static void timer8253_wr(struct timer8253struct *t, int reg, UINT8 val)
+{
+ int chn;
+
+ switch (reg) {
+ case T8253_CWORD:
+ chn = val >> 6;
+ if (chn < 3) {
+ t->channel[chn].bcdMode = (val & 1) ? 1 : 0;
+ t->channel[chn].cntMode = (val >> 1) & 0x07;
+ t->channel[chn].valMode = (val >> 4) & 0x03;
+
+ switch (t->channel[chn].valMode) {
+ case 1:
+ case 2:
+ t->channel[chn].loadCnt = 1;
+ break;
+
+ case 3:
+ t->channel[chn].loadCnt = 2;
+ break;
+
+ default:
+ mame_printf_debug("unhandled val mode %i\n", t->channel[chn].valMode);
+ }
+
+ switch (t->channel[chn].cntMode) {
+ case 0:
+ t->channel[chn].output = 0;
+ t->channel[chn].enable = 0;
+ break;
+
+ case 3:
+ t->channel[chn].output = 1;
+ break;
+
+ case 4:
+ t->channel[chn].output = 1;
+ t->channel[chn].enable = 0;
+ break;
+
+ default:
+ mame_printf_debug("unhandled cnt mode %i\n", t->channel[chn].cntMode);
+ }
+ }
+ break;
+
+ default:
+ chn = reg;
+
+ switch (t->channel[chn].valMode) {
+ case 1:
+ t->channel[chn].cnval = (t->channel[chn].cnval & 0xff00) | val;
+ break;
+ case 2:
+ t->channel[chn].cnval = (t->channel[chn].cnval & 0x00ff) | (val << 8);
+ break;
+ case 3:
+ t->channel[chn].cnval = (t->channel[chn].cnval >> 8) | (val << 8);
+ break;
+ default:
+ mame_printf_debug("unhandled val mode %i\n", t->channel[chn].valMode);
+ }
+
+ if (t->channel[chn].cntMode==0) {
+ t->channel[chn].enable = 0;
+ }
+
+ t->channel[chn].loadCnt--;
+
+ if (t->channel[chn].loadCnt == 0) {
+ switch (t->channel[chn].valMode) {
+ case 1:
+ case 2:
+ t->channel[chn].loadCnt = 1;
+ break;
+
+ case 3:
+ t->channel[chn].loadCnt = 2;
+ break;
+
+ default:
+ mame_printf_debug("unhandled val mode %i\n", t->channel[chn].valMode);
+ }
+
+ switch (t->channel[chn].cntMode) {
+ case 3:
+ t->channel[chn].count = t->channel[chn].cnval;
+ t->channel[chn].enable = 1;
+ break;
+
+ case 0:
+ case 4:
+ t->channel[chn].count = t->channel[chn].cnval;
+ t->channel[chn].enable = 1;
+ break;
+
+ default:
+ mame_printf_debug("unhandled cnt mode %i\n", t->channel[chn].cntMode);
+ }
+ }
+ }
+}
+
+static void timer8253_set_gate(struct timer8253struct *t, int chn, UINT8 gate)
+{
+ t->channel[chn].gate = gate;
+}
+
+
+
+static char timer8253_get_output(struct timer8253struct *t, int chn)
+{
+ return t->channel[chn].output;
+}
+
+
+WRITE8_HANDLER( tiamc1_timer0_w )
+{
+ timer8253_wr(&timer0, offset, data);
+}
+
+WRITE8_HANDLER( tiamc1_timer1_w )
+{
+ timer8253_wr(&timer1, offset, data);
+}
+
+WRITE8_HANDLER( tiamc1_timer1_gate_w )
+{
+ timer8253_set_gate(&timer1, 0, (data & 1) ? 1 : 0);
+ timer8253_set_gate(&timer1, 1, (data & 2) ? 1 : 0);
+ timer8253_set_gate(&timer1, 2, (data & 4) ? 1 : 0);
+}
+
+
+static void tiamc1_sound_update(void *param, stream_sample_t **inputs, stream_sample_t **outputs, int length)
+{
+ int count, o0, o1, o2, len, or = 0;
+
+ len = length * CLOCK_DIVIDER;
+
+ for (count = 0; count < len; count++) {
+ timer1_divider++;
+ if (timer1_divider == 228) {
+ timer1_divider = 0;
+ timer8253_tick(&timer1, 0);
+ timer8253_tick(&timer1, 1);
+ timer8253_tick(&timer1, 2);
+
+ timer8253_set_gate(&timer0, 0, timer8253_get_output(&timer1, 0));
+ timer8253_set_gate(&timer0, 1, timer8253_get_output(&timer1, 1));
+ timer8253_set_gate(&timer0, 2, timer8253_get_output(&timer1, 2));
+ }
+
+ timer8253_tick(&timer0, 0);
+ timer8253_tick(&timer0, 1);
+ timer8253_tick(&timer0, 2);
+
+ o0 = timer8253_get_output(&timer0, 0) ? 1 : 0;
+ o1 = timer8253_get_output(&timer0, 1) ? 1 : 0;
+ o2 = timer8253_get_output(&timer0, 2) ? 1 : 0;
+
+ or = (or << 1) | (((o0 | o1) ^ 0xff) & o2);
+
+ if ((count + 1) % CLOCK_DIVIDER == 0) {
+ outputs[0][count / CLOCK_DIVIDER] = or ? 0x2828 : 0;
+ or = 0;
+ }
+ }
+}
+
+void *tiamc1_sh_start(int clock, const struct CustomSound_interface *config)
+{
+ int i, j;
+
+ timer8253_reset(&timer0);
+ timer8253_reset(&timer1);
+
+ channel = stream_create(0, 1, clock / CLOCK_DIVIDER, 0, tiamc1_sound_update);
+
+ timer1_divider = 0;
+
+ for (i = 0; i < 2; i++) {
+ struct timer8253struct *t = (i ? &timer1 : &timer0);
+
+ for (j = 0; j < 3; j++) {
+ state_save_register_item("channel", i * 3 + j, t->channel[j].count);
+ state_save_register_item("channel", i * 3 + j, t->channel[j].cnval);
+ state_save_register_item("channel", i * 3 + j, t->channel[j].bcdMode);
+ state_save_register_item("channel", i * 3 + j, t->channel[j].cntMode);
+ state_save_register_item("channel", i * 3 + j, t->channel[j].valMode);
+ state_save_register_item("channel", i * 3 + j, t->channel[j].gate);
+ state_save_register_item("channel", i * 3 + j, t->channel[j].output);
+ state_save_register_item("channel", i * 3 + j, t->channel[j].loadCnt);
+ state_save_register_item("channel", i * 3 + j, t->channel[j].enable);
+ }
+ }
+
+ state_save_register_global(timer1_divider);
+
+ return channel;
+}
+