ref: d4e734b0d7a580854304b01a922dbb06da7a6a20
dir: /ay38910/ay38910.c/
#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); }