#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: cc.c,v 1.28 2021/08/12 20:13:54 andvar Exp $");
#include <sys/types.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <amiga/amiga/custom.h>
#include <amiga/amiga/cc.h>
#include "audio.h"
vaddr_t CUSTOMADDR, CUSTOMbase;
#if defined (__GNUC__)
#define INLINE inline
#else
#define INLINE
#endif
void
custom_chips_init(void)
{
cc_init_chipmem();
cc_init_vbl();
cc_init_audio();
cc_init_blitter();
cc_init_copper();
}
LIST_HEAD(vbllist, vbl_node) vbl_list;
void
turn_vbl_function_off(struct vbl_node *n)
{
if (n->flags & VBLNF_OFF)
return;
n->flags |= VBLNF_TURNOFF;
while ((n->flags & VBLNF_OFF) == 0)
;
}
void
turn_vbl_function_on(struct vbl_node *n)
{
n->flags &= (short) ~(VBLNF_OFF);
}
void
add_vbl_function(struct vbl_node *add, short priority, void *data)
{
int s;
struct vbl_node *n, *prev;
s = spl3();
prev = NULL;
LIST_FOREACH(n, &vbl_list, link) {
if (add->priority > n->priority) {
if (prev == NULL) {
LIST_INSERT_HEAD(&vbl_list, add, link);
} else {
LIST_INSERT_AFTER(prev, add, link);
}
add = NULL;
break;
}
prev = n;
}
if (add != NULL) {
if (prev == NULL) {
LIST_INSERT_HEAD(&vbl_list, add, link);
} else {
LIST_INSERT_AFTER(prev, add, link);
}
}
splx(s);
}
void
remove_vbl_function(struct vbl_node *n)
{
int s;
s = spl3();
LIST_REMOVE(n, link);
splx(s);
}
void
vbl_handler(void)
{
struct vbl_node *n;
LIST_FOREACH(n, &vbl_list, link) {
if (n->flags & VBLNF_TURNOFF) {
n->flags |= VBLNF_OFF;
n->flags &= ~(VBLNF_TURNOFF);
} else {
if (n != NULL)
n->function(n->data);
}
}
custom.intreq = INTF_VERTB;
}
void
cc_init_vbl(void)
{
LIST_INIT(&vbl_list);
custom.intena = INTF_SETCLR | INTF_VERTB;
}
void
cc_init_blitter(void)
{
}
int
is_blitter_busy(void)
{
u_short bb;
bb = (custom.dmaconr & DMAF_BLTDONE);
if ((custom.dmaconr & DMAF_BLTDONE) || bb)
return (1);
return (0);
}
void
wait_blit(void)
{
while (is_blitter_busy())
;
}
void
blitter_handler(void)
{
custom.intreq = INTF_BLIT;
}
void
do_blit(u_short size)
{
custom.bltsize = size;
}
void
set_blitter_control(u_short con0, u_short con1)
{
custom.bltcon0 = con0;
custom.bltcon1 = con1;
}
void
set_blitter_mods(u_short a, u_short b, u_short c, u_short d)
{
custom.bltamod = a;
custom.bltbmod = b;
custom.bltcmod = c;
custom.bltdmod = d;
}
void
set_blitter_masks(u_short fm, u_short lm)
{
custom.bltafwm = fm;
custom.bltalwm = lm;
}
void
set_blitter_data(u_short da, u_short db, u_short dc)
{
custom.bltadat = da;
custom.bltbdat = db;
custom.bltcdat = dc;
}
void
set_blitter_pointers(void *a, void *b, void *c, void *d)
{
custom.bltapt = a;
custom.bltbpt = b;
custom.bltcpt = c;
custom.bltdpt = d;
}
void
wait_tof(void)
{
while ((custom.vposr & 0x0007) == 0)
;
while (custom.vposr & 0x0007)
;
if (custom.vposr & 0x8000)
return;
while ((custom.vposr & 0x8000) == 0)
;
}
cop_t *
find_copper_inst(cop_t *l, u_short inst)
{
cop_t *r = NULL;
while ((l->cp.data & 0xff01ff01) != 0xff01ff00) {
if (l->cp.inst.opcode == inst) {
r = l;
break;
}
l++;
}
return (r);
}
void
install_copper_list(cop_t *l)
{
wait_tof();
wait_tof();
custom.cop1lc = l;
}
void
cc_init_copper(void)
{
}
void
copper_handler(void)
{
custom.intreq = INTF_COPER;
}
struct audio_channel channel[4];
struct vbl_node audio_vbl_node;
void
cc_init_audio(void)
{
int i;
custom.intena = INTF_AUD0|INTF_AUD1|INTF_AUD2|INTF_AUD3;
for (i = 0; i < 4; i++) {
channel[i].play_count = 0;
channel[i].isaudio = 0;
channel[i].handler = NULL;
}
}
void
audio_handler(void)
{
u_short audio_dma, flag, ir;
int i;
audio_dma = custom.dmaconr;
audio_dma &= (DMAF_AUD0|DMAF_AUD1|DMAF_AUD2|DMAF_AUD3);
custom.intena = (audio_dma << INTB_AUD0) & AUCC_ALLINTF;
if (!audio_dma) {
custom.intreq = AUCC_ALLINTF;
goto out;
}
for (i = 0; i < AUCC_MAXINT; i++) {
flag = (1 << i);
ir = custom.intreqr;
if ((ir & (flag << INTB_AUD0)) == 0)
continue;
#if NAUDIO>0
custom.intreq=(flag<<INTB_AUD0);
if (channel[i].isaudio==1)
if (channel[i].handler)
(*channel[i].handler)(i);
#endif
if (channel[i].play_count)
channel[i].play_count--;
else {
custom.dmacon = flag;
custom.intena = (flag << INTB_AUD0);
if (channel[i].isaudio==-1)
channel[i].isaudio=0;
}
custom.intreq = (flag << INTB_AUD0);
}
out:
audio_dma = custom.dmaconr;
audio_dma &= (DMAF_AUD0|DMAF_AUD1|DMAF_AUD2|DMAF_AUD3);
custom.intena = INTF_SETCLR | (audio_dma << INTB_AUD0);
}
void
play_sample(u_short len, u_short *data, u_short period, u_short volume, u_short channels, u_long count)
{
u_short dmabits, ch;
register int i;
dmabits = channels & 0xf;
for (i=0;i<4;i++) {
if ((1<<i) & dmabits) {
if (channel[i].isaudio)
return;
else
channel[i].isaudio=-1;
}
}
custom.dmacon = dmabits;
for (ch = 0; ch < 4; ch++) {
if ((dmabits & (ch << ch)) == 0)
continue;
custom.aud[ch].len = len;
custom.aud[ch].lc = data;
custom.aud[ch].per = period;
custom.aud[ch].vol = volume;
channel[ch].play_count = count;
}
custom.intena = INTF_SETCLR | (dmabits << INTB_AUD0);
custom.dmacon = DMAF_SETCLR | DMAF_MASTER |dmabits;
}
static TAILQ_HEAD(chiplist, mem_node) chip_list;
static TAILQ_HEAD(freelist, mem_node) free_list;
static u_long chip_total;
static u_long chip_size;
void
cc_init_chipmem(void)
{
int s = splhigh ();
struct mem_node *mem;
chip_size = chipmem_end - (chipmem_start + PAGE_SIZE);
chip_total = chip_size - sizeof(*mem);
mem = (struct mem_node *)chipmem_steal(chip_size);
mem->size = chip_total;
TAILQ_INIT(&chip_list);
TAILQ_INIT(&free_list);
TAILQ_INSERT_HEAD(&chip_list, mem, link);
TAILQ_INSERT_HEAD(&free_list, mem, free_link);
splx(s);
}
void *
alloc_chipmem(u_long size)
{
int s;
struct mem_node *mn, *new;
if (size == 0)
return NULL;
s = splhigh();
if (size & ~(CM_BLOCKMASK))
size = (size & CM_BLOCKMASK) + CM_BLOCKSIZE;
TAILQ_FOREACH(mn, &free_list, free_link)
if (size <= mn->size)
break;
if (mn == NULL) {
splx(s);
return NULL;
}
if ((mn->size - size) <= sizeof (*mn)) {
TAILQ_REMOVE(&free_list, mn, free_link);
mn->type = MNODE_USED;
size = mn->size;
chip_total -= mn->size;
splx(s);
return ((void *)&mn[1]);
}
new = mn;
new->size -= size + sizeof(struct mem_node);
mn = (struct mem_node *)(MNODES_MEM(new) + new->size);
mn->size = size;
TAILQ_INSERT_AFTER(&chip_list, new, mn, link);
mn->type = MNODE_USED;
chip_total -= size + sizeof(struct mem_node);
splx(s);
return ((void *)&mn[1]);
}
void
free_chipmem(void *mem)
{
struct mem_node *mn, *next, *prev;
int s;
if (mem == NULL)
return;
s = splhigh();
mn = (struct mem_node *)mem - 1;
next = TAILQ_NEXT(mn, link);
prev = TAILQ_PREV(mn, chiplist, link);
if (next->type == MNODE_FREE) {
TAILQ_INSERT_BEFORE(next, mn, free_link);
mn->type = MNODE_FREE;
TAILQ_REMOVE(&chip_list, next, link);
TAILQ_REMOVE(&free_list, next, free_link);
chip_total += mn->size + sizeof(struct mem_node);
mn->size += next->size + sizeof(struct mem_node);
}
if (prev->type == MNODE_FREE) {
if (mn->type != MNODE_FREE)
chip_total += mn->size + sizeof(struct mem_node);
else {
TAILQ_REMOVE(&free_list, mn, free_link);
mn->type = MNODE_USED;
chip_total += sizeof(struct mem_node);
}
TAILQ_REMOVE(&chip_list, mn, link);
prev->size += mn->size + sizeof(struct mem_node);
} else if (mn->type != MNODE_FREE) {
while (next != NULL && prev != NULL) {
if (next->type == MNODE_FREE) {
TAILQ_INSERT_BEFORE(next, mn, free_link);
mn->type = MNODE_FREE;
break;
}
if (prev->type == MNODE_FREE) {
TAILQ_INSERT_AFTER(&free_list, prev, mn,
free_link);
mn->type = MNODE_FREE;
break;
}
prev = TAILQ_PREV(prev, chiplist, link);
next = TAILQ_NEXT(next, link);
}
if (mn->type != MNODE_FREE) {
if (next == NULL) {
TAILQ_INSERT_TAIL(&free_list,mn,free_link);
} else {
TAILQ_INSERT_HEAD(&free_list,mn,free_link);
}
mn->type = MNODE_FREE;
}
chip_total += mn->size;
}
splx(s);
}
u_long
sizeof_chipmem(void *mem)
{
struct mem_node *mn;
if (mem == NULL)
return (0);
mn = mem;
mn--;
return (mn->size);
}
u_long
avail_chipmem(int largest)
{
struct mem_node *mn;
u_long val;
int s;
val = 0;
if (largest == 0)
val = chip_total;
else {
s = splhigh();
TAILQ_FOREACH(mn, &free_list, free_link) {
if (mn->size > val)
val = mn->size;
}
splx(s);
}
return (val);
}