ref: 19935ea466f4bec5b7b5abad85b8b68dd3da45e9
dir: /psg.c/
#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;
}