shithub: sms

ref: 47cf02e0c5a922a7d6d01eb3dffdd1fe56978362
dir: /psg.c/

View raw version
#include <u.h>
#include <libc.h>
#include <thread.h>
#include "../eui.h"
#include "dat.h"
#include "fns.h"

static int fd;
static short sbuf[2 * 2000], *sbufp;

static const u8int noise_table[3] = { 0x10, 0x20, 0x40 };
static const short vol_table[16] = {
	8191, 6507, 5168, 4105, 3261, 2590, 2057,
	1642, 1298, 1031, 819, 650, 516, 410, 326, 0
};

static float cyclespersample;
static float cyc;
static u16int freqreg[4];
static u16int countreg[4];
static u8int attn[4];
static int flipflop[4];
static u16int noise;
static u8int curr_reg;
static u8int curr_type;

void
psgwrite(const u8int data)
{
	int first;

	first = data & 128;
	if(first){
		curr_reg = (data >> 5) & 3;
		curr_type = (data >> 4) & 1;
	}

	if(curr_type){
		attn[curr_reg] = data & 0x0F;
	}else if(first && curr_reg == 3){
		freqreg[3] = data & 7;
		noise = 0x8000;
	}
	else if(first)
		freqreg[curr_reg] = (freqreg[curr_reg] & 0x3F0) | (data & 0x0F);
	else
		countreg[curr_reg] = freqreg[curr_reg] = (freqreg[curr_reg] & 0x0F) | ((data & 0x3F) << 4);
}

static inline u16int
parity(u16int v)
{
	v ^= v >> 8;
	v ^= v >> 4;
	v ^= v >> 2;
	v ^= v >> 1;
	v &= 1;
	return v;
}

static inline s16int
vol(u8int chn)
{
	return (flipflop[chn] ? 1 : -1) * vol_table[attn[chn]];
}

u16int
psgstep(void)
{
	while(cyc > 0){
		for(u8int i = 0; i < 4; i++){
			countreg[i]--;
			if(!countreg[i]){
				if(i < 3){
					countreg[i] = freqreg[i];
					flipflop[i] = !flipflop[i];
				}else{
					u8int nf = freqreg[3] & 3;
					u8int fb = (freqreg[3] >> 2) & 1;
					countreg[3] = nf == 3 ? freqreg[2] : (0x10 << nf);

					noise = (noise >> 1) | ((fb ? parity(noise & 0x9) : noise & 1) << 15);
					flipflop[3] = (noise & 1);
				}
			}
		}

		cyc--;
	}

	cyc += cyclespersample;

	return vol(0) + vol(1) + vol(2) + vol(3);
}

void
audiosample(void)
{
	s16int v;

	if(sbufp == nil)
		return;

	v = (s16int)psgstep();
	if(sbufp < sbuf + nelem(sbuf) - 1){
		*sbufp++ = v;
		*sbufp++ = v;
	}
}

int
audioout(void)
{
	int rc;

	if(sbufp == nil)
		return -1;
	if(sbufp == sbuf)
		return 0;
	rc = warp10 ? (sbufp - sbuf) * 2 : write(fd, sbuf, (sbufp - sbuf) * 2);
	if(rc > 0)
		sbufp -= (rc+1)/2;
	if(sbufp < sbuf)
		sbufp = sbuf;
	return 0;
}

void
psginit(const u16int rate, const int clockspd)
{
	cyclespersample = (double)clockspd / (double)PSGDIV/ (double)rate;
	cyc = cyclespersample;

	fd = open("/dev/audio", OWRITE);
	if(fd < 0)
		return;

	sbufp = sbuf;
}