shithub: neindaw

ref: fb785323a3088953f80459b04c0c796c527af4ac
dir: neindaw/ay38910/ay38910.c

View raw version
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <libsec.h>
#include <draw.h>
#include <keyboard.h>
#define CHIPS_IMPL
#define CHIPS_ASSERT assert
#include "ay38910.h"

#define MIN(a,b) ((a)<=(b)?(a):(b))
#define MAX(a,b) ((a)>=(b)?(a):(b))

enum {
	Tickhz = 2000000,

	Levelenv = 1<<4,

	Hold = 1<<0,
	Alternate = 1<<1,
	Attack = 1<<2,
	Continue = 1<<3,
};

static void
regw(ay38910_t *ay, int reg, int v)
{
	u64int p;

	/* latch address */
	p = AY38910_BDIR | AY38910_BC1;
	AY38910_SET_DATA(p, reg);
	ay38910_iorq(ay, p);

	/* write to psg */
	p = AY38910_BDIR;
	AY38910_SET_DATA(p, v);
	ay38910_iorq(ay, p);

	/* inactive */
	ay38910_iorq(ay, 0);
}

static int
regr(ay38910_t *ay, int reg)
{
	u64int p;
	int v;

	/* latch address */
	p = AY38910_BDIR | AY38910_BC1;
	AY38910_SET_DATA(p, reg);
	ay38910_iorq(ay, p);

	/* read from psg */
	v = AY38910_GET_DATA(ay38910_iorq(ay, AY38910_BC1));

	/* inactive */
	ay38910_iorq(ay, 0);

	return v;
}

static int
hz2tp(int hz)
{
	return MAX(1, MIN(4095, Tickhz / (MAX(1, hz) * 16)));
}

static int
hz2ep(int hz)
{
	return MAX(1, MIN(65535, Tickhz / (MAX(1, hz) * 256)));
}

static int
ms2ep(int ms)
{
	return MAX(1, MIN(65535, (Tickhz / 1000) * ms / 256));
}

static void
tone(ay38910_t *ay, int chan, int hz)
{
	int tp;

	tp = hz2tp(hz);
	regw(ay, chan*2+0, tp & 0xff); /* fine */
	regw(ay, chan*2+1, (tp>>8) & 0x0f); /* coarse */
}

static void
toneon(ay38910_t *ay, int chan)
{
	regw(ay, AY38910_REG_ENABLE, regr(ay, AY38910_REG_ENABLE) & ~(1<<chan));
}

static void
toneoff(ay38910_t *ay, int chan)
{
	regw(ay, AY38910_REG_ENABLE, regr(ay, AY38910_REG_ENABLE) | 1<<chan);
}

static void
envp(ay38910_t *ay, int p)
{
	regw(ay, AY38910_REG_ENV_PERIOD_FINE, p & 0xff);
	regw(ay, AY38910_REG_ENV_PERIOD_COARSE, p>>8);
}

static void
envsc(ay38910_t *ay, int v)
{
	regw(ay, AY38910_REG_ENV_SHAPE_CYCLE, v & 0xf);
}

static void
amp(ay38910_t *ay, int chan, int level)
{
	regw(ay, AY38910_REG_AMP_A+chan, level);
}

static ay38910_t *
aynew(float mag)
{
	ay38910_desc_t d = {
		.type = AY38910_TYPE_8910,
		.tick_hz = Tickhz,
		.sound_hz = 44100,
		.magnitude = mag,
	};
	ay38910_t *ay;

	ay = malloc(sizeof(*ay));
	ay38910_init(ay, &d);
	regw(ay, AY38910_REG_ENABLE, 0xff); /* disable everything */

	return ay;
}

void
threadmain(int argc, char **argv)
{
	u64int tick;
	ay38910_t *ay[2];
	int i, base, diff[] = {20, 200, 40, 220, 60};
	float f[2], x;
	bool havesample[2];

	USED(argc); USED(argv);

	ay[0] = aynew(1.0);
	amp(ay[0], 0, Levelenv | 4);
	envp(ay[0], ms2ep(200));
	envsc(ay[0], Continue|Alternate);
	toneon(ay[0], 0);

	ay[1] = aynew(1.0);
	amp(ay[1], 0, Levelenv | 4);
	envp(ay[1], ms2ep(500));
	envsc(ay[1], Continue|Attack);
	toneon(ay[1], 0);
	tone(ay[1], 0, 500);

	base = 200;
	for (i = 0, tick = 0;; tick++) {
		/* 100Hz */
		if ((tick % (Tickhz / 100)) == 0) {
			if (i >= nelem(diff))
				i = 0;
			tone(ay[0], 0, base+diff[i++]);
		}

		if (!havesample[0] && ay38910_tick(ay[0])) {
			f[0] = ay[0]->sample;
			havesample[0] = true;
		}
		if (!havesample[1] && ay38910_tick(ay[1])) {
			f[1] = ay[1]->sample;
			havesample[1] = true;
		}
		if (havesample[0] && havesample[1]) {
			havesample[0] = havesample[1] = false;
			x = f[0] + f[1];
			if (write(1, &x, sizeof(x)) < 0) 
				break;
		}
	}

	free(ay[0]);
	free(ay[1]);
	threadexitsall(nil);
}