#include "compiler.h"
#if defined(SUPPORT_KEYDISP)
#include "pccore.h"
#include "iocore.h"
#include "sound.h"
#include "fmboard.h"
#include "keydisp.h"
typedef struct {
UINT16 posx;
UINT16 pals;
const UINT8 *data;
} KDKEYPOS;
typedef struct {
UINT8 k[KEYDISP_NOTEMAX];
UINT8 r[KEYDISP_NOTEMAX];
UINT remain;
UINT8 flag;
UINT8 padding[3];
} KDCHANNEL;
typedef struct {
UINT8 ch;
UINT8 key;
} KDDELAYE;
typedef struct {
UINT pos;
UINT rem;
UINT8 warm;
UINT8 warmbase;
} KDDELAY;
typedef struct {
UINT16 fnum[4];
UINT8 lastnote[4];
UINT8 flag;
UINT8 extflag;
} KDFMCTRL;
typedef struct {
UINT16 fto[4];
UINT8 lastnote[4];
UINT8 flag;
UINT8 mix;
UINT8 padding[2];
} KDPSGCTRL;
typedef struct {
UINT8 mode;
UINT8 dispflag;
UINT8 framepast;
UINT8 keymax;
UINT8 fmmax;
UINT8 psgmax;
UINT8 fmpos[KEYDISP_FMMAX];
UINT8 psgpos[KEYDISP_PSGMAX];
UINT fmreg[KEYDISP_FMMAX];
KDDELAY delay;
KDCHANNEL ch[KEYDISP_CHMAX];
KDFMCTRL fmctl[KEYDISP_FMMAX];
KDPSGCTRL psgctl[KEYDISP_PSGMAX];
UINT8 pal8[KEYDISP_PALS];
UINT16 pal16[KEYDISP_LEVEL*2];
RGB32 pal32[KEYDISP_LEVEL*2];
KDKEYPOS keypos[128];
KDDELAYE delaye[KEYDISP_DELAYEVENTS];
} KEYDISP;
static KEYDISP keydisp;
#include "keydisp.res"
// ---- event
static void keyon(UINT ch, UINT8 note) {
UINT i;
KDCHANNEL *kdch;
note &= 0x7f;
kdch = keydisp.ch + ch;
for (i=0; i<kdch->remain; i++) {
if (kdch->k[i] == note) { // ヒットした
for (; i<(kdch->remain-1); i++) {
kdch->k[i] = kdch->k[i+1];
kdch->r[i] = kdch->r[i+1];
}
kdch->k[i] = note;
kdch->r[i] = 0x80 | (KEYDISP_LEVEL - 1);
kdch->flag |= 1;
return;
}
}
if (i < KEYDISP_NOTEMAX) {
kdch->k[i] = note;
kdch->r[i] = 0x80 | (KEYDISP_LEVEL - 1);
kdch->flag |= 1;
kdch->remain++;
}
}
static void keyoff(UINT ch, UINT8 note) {
UINT i;
KDCHANNEL *kdch;
note &= 0x7f;
kdch = keydisp.ch + ch;
for (i=0; i<kdch->remain; i++) {
if (kdch->k[i] == note) { // ヒットした
kdch->r[i] = 0x80 | (KEYDISP_LEVEL - 2);
kdch->flag |= 1;
break;
}
}
}
static void chkeyoff(UINT ch) {
UINT i;
KDCHANNEL *kdch;
kdch = keydisp.ch + ch;
for (i=0; i<kdch->remain; i++) {
if ((kdch->r[i] & (~0x80)) >= (KEYDISP_LEVEL - 1)) {
kdch->r[i] = 0x80 | (KEYDISP_LEVEL - 2);
kdch->flag |= 1;
}
}
}
static void keyalloff(void) {
UINT8 i;
for (i=0; i<KEYDISP_CHMAX; i++) {
chkeyoff(i);
}
}
static void keyallreload(void) {
UINT i;
for (i=0; i<KEYDISP_CHMAX; i++) {
keydisp.ch[i].flag = 2;
}
}
static void keyallclear(void) {
ZeroMemory(keydisp.ch, sizeof(keydisp.ch));
keyallreload();
}
// ---- delay event
static void delayreset(void) {
keydisp.delay.warm = keydisp.delay.warmbase;
keydisp.delay.pos = 0;
keydisp.delay.rem = 0;
ZeroMemory(keydisp.delaye, sizeof(keydisp.delaye));
keyalloff();
}
static void delayexecevent(UINT8 framepast) {
KDDELAYE *ebase;
UINT pos;
UINT rem;
KDDELAYE *ev;
ebase = keydisp.delaye;
pos = keydisp.delay.pos;
rem = keydisp.delay.rem;
while((keydisp.delay.warm) && (framepast)) {
keydisp.delay.warm--;
framepast--;
if (rem >= KEYDISP_DELAYEVENTS) {
ev = ebase + pos;
rem--;
if (ev->ch == 0xff) {
keydisp.delay.warm++;
}
else if (ev->key & 0x80) {
keyon(ev->ch, ev->key);
rem--;
}
else {
keyoff(ev->ch, ev->key);
}
pos = (pos + 1) & (KEYDISP_DELAYEVENTS - 1);
}
ebase[(pos + rem) & (KEYDISP_DELAYEVENTS - 1)].ch = 0xff;
rem++;
}
while(framepast) {
framepast--;
while(rem) {
rem--;
ev = ebase + pos;
if (ev->ch == 0xff) {
pos = (pos + 1) & (KEYDISP_DELAYEVENTS - 1);
break;
}
if (ev->key & 0x80) {
keyon(ev->ch, ev->key);
}
else {
keyoff(ev->ch, ev->key);
}
pos = (pos + 1) & (KEYDISP_DELAYEVENTS - 1);
}
ebase[(pos + rem) & (KEYDISP_DELAYEVENTS - 1)].ch = 0xff;
rem++;
}
keydisp.delay.pos = pos;
keydisp.delay.rem = rem;
}
static void delaysetevent(UINT8 ch, UINT8 key) {
KDDELAYE *e;
e = keydisp.delaye;
if (keydisp.delay.rem < KEYDISP_DELAYEVENTS) {
e += (keydisp.delay.pos + keydisp.delay.rem) &
(KEYDISP_DELAYEVENTS - 1);
keydisp.delay.rem++;
e->ch = ch;
e->key = key;
}
else {
e += keydisp.delay.pos;
keydisp.delay.pos += (keydisp.delay.pos + 1) &
(KEYDISP_DELAYEVENTS - 1);
if (e->ch == 0xff) {
keydisp.delay.warm++;
}
else if (e->key & 0x80) {
keyon(e->ch, e->key);
}
else {
keyoff(e->ch, e->key);
}
e->ch = ch;
e->key = key;
}
}
// ---- FM
static UINT8 getfmnote(UINT16 fnum) {
UINT8 ret;
int i;
ret = (fnum >> 11) & 7;
ret *= 12;
ret += 24;
fnum &= 0x7ff;
while(fnum < FNUM_MIN) {
if (!ret) {
return(0);
}
ret -= 12;
fnum <<= 1;
}
while(fnum > FNUM_MAX) {
fnum >>= 1;
ret += 12;
}
for (i=0; fnum >= fnumtbl[i]; i++) {
ret++;
}
if (ret > 127) {
return(127);
}
return(ret);
}
static void fmkeyoff(UINT8 ch, KDFMCTRL *k) {
delaysetevent(keydisp.fmpos[ch], k->lastnote[0]);
}
static void fmkeyon(UINT8 ch, KDFMCTRL *k) {
UINT reg;
fmkeyoff(ch, k);
reg = keydisp.fmreg[ch] + 0xa0;
k->fnum[0] = ((opn.reg[reg + 4] & 0x3f) << 8) + opn.reg[reg];
k->lastnote[0] = getfmnote(k->fnum[0]);
delaysetevent(keydisp.fmpos[ch], (UINT8)(k->lastnote[0] | 0x80));
}
static void fmkeyreset(void) {
ZeroMemory(keydisp.fmctl, sizeof(keydisp.fmctl));
}
void keydisp_fmkeyon(UINT8 ch, UINT8 value) {
KDFMCTRL *k;
if (keydisp.mode != KEYDISP_MODEFM) {
return;
}
if (ch < keydisp.fmmax) {
k = keydisp.fmctl + ch;
value &= 0xf0;
if (k->flag != value) {
if (value) {
fmkeyon(ch, k);
}
else {
fmkeyoff(ch, k);
}
k->flag = value;
}
}
}
static void fmkeysync(void) {
UINT8 ch;
KDFMCTRL *k;
UINT16 reg;
UINT16 fnum;
for (ch=0, k=keydisp.fmctl; ch<keydisp.fmmax; ch++, k++) {
if (k->flag) {
reg = keydisp.fmreg[ch] + 0xa0;
fnum = ((opn.reg[reg + 4] & 0x3f) << 8) + opn.reg[reg];
if (k->fnum[0] != fnum) {
UINT8 n;
k->fnum[0] = fnum;
n = getfmnote(fnum);
if (k->lastnote[0] != n) {
fmkeyoff(ch, k);
}
k->lastnote[0] = n;
delaysetevent(keydisp.fmpos[ch],
(UINT8)(k->lastnote[0] | 0x80));
}
}
}
}
// ---- PSG
static const void *psgtbl[3] = {&psg1, &psg2, &psg3};
static UINT8 getpsgnote(UINT16 tone) {
UINT8 ret;
int i;
ret = 60;
tone &= 0xfff;
while(tone < FTO_MIN) {
tone <<= 1;
ret += 12;
if (ret >= 128) {
return(127);
}
}
while(tone > FTO_MAX) {
if (!ret) {
return(0);
}
tone >>= 1;
ret -= 12;
}
for (i=0; tone < ftotbl[i]; i++) {
ret++;
}
if (ret >= 128) {
return(127);
}
return(ret);
}
static void psgmix(UINT8 ch, PSGGEN psg) {
KDPSGCTRL *k;
k = keydisp.psgctl + ch;
if ((k->mix ^ psg->reg.mixer) & 7) {
UINT8 i, bit, pos;
k->mix = psg->reg.mixer;
pos = keydisp.psgpos[ch];
for (i=0, bit=1; i<3; i++, pos++, bit<<=1) {
if (k->flag & bit) {
k->flag ^= bit;
delaysetevent(pos, k->lastnote[i]);
}
else if ((!(k->mix & bit)) && (psg->reg.vol[i] & 0x1f)) {
k->flag |= bit;
k->fto[i] = LOADINTELWORD(psg->reg.tune[i]) & 0xfff;
k->lastnote[i] = getpsgnote(k->fto[i]);
delaysetevent(pos, (UINT8)(k->lastnote[i] | 0x80));
}
}
}
}
static void psgvol(UINT8 ch, PSGGEN psg, UINT8 i) {
KDPSGCTRL *k;
UINT8 bit;
UINT8 pos;
UINT16 tune;
k = keydisp.psgctl + ch;
bit = (1 << i);
pos = keydisp.psgpos[ch] + i;
if (psg->reg.vol[i] & 0x1f) {
if (!((k->mix | k->flag) & bit)) {
k->flag |= bit;
tune = LOADINTELWORD(psg->reg.tune[i]);
tune &= 0xfff;
k->fto[i] = tune;
k->lastnote[i] = getpsgnote(tune);
delaysetevent(pos, (UINT8)(k->lastnote[i] | 0x80));
}
}
else if (k->flag & bit) {
k->flag ^= bit;
delaysetevent(pos, k->lastnote[i]);
}
}
static void psgkeyreset(void) {
ZeroMemory(keydisp.psgctl, sizeof(keydisp.psgctl));
}
void keydisp_psgmix(void *psg) {
UINT8 c;
if (keydisp.mode != KEYDISP_MODEFM) {
return;
}
for (c=0; c<keydisp.psgmax; c++) {
if (psgtbl[c] == psg) {
psgmix(c, (PSGGEN)psg);
break;
}
}
}
void keydisp_psgvol(void *psg, UINT8 ch) {
UINT8 c;
if (keydisp.mode != KEYDISP_MODEFM) {
return;
}
for (c=0; c<keydisp.psgmax; c++) {
if (psgtbl[c] == psg) {
psgvol(c, (PSGGEN)psg, ch);
break;
}
}
}
static void psgkeysync(void) {
UINT8 ch;
KDPSGCTRL *k;
UINT8 bit;
UINT8 i;
UINT8 pos;
PSGGEN psg;
UINT16 tune;
UINT8 n;
for (ch=0, k=keydisp.psgctl; ch<keydisp.psgmax; ch++, k++) {
psg = (PSGGEN)psgtbl[ch];
pos = keydisp.psgpos[ch];
for (i=0, bit=1; i<3; i++, pos++, bit<<=1) {
if (k->flag & bit) {
tune = LOADINTELWORD(psg->reg.tune[i]);
tune &= 0xfff;
if (k->fto[i] != tune) {
k->fto[i] = tune;
n = getpsgnote(tune);
if (k->lastnote[i] != n) {
delaysetevent(pos, k->lastnote[i]);
k->lastnote[i] = n;
delaysetevent(pos, (UINT8)(n | 0x80));
}
}
}
}
}
}
// ---- BOARD change...
static void setfmhdl(UINT8 items, UINT base) {
while(items--) {
if ((keydisp.keymax < KEYDISP_CHMAX) &&
(keydisp.fmmax < KEYDISP_FMMAX)) {
keydisp.fmpos[keydisp.fmmax] = keydisp.keymax++;
keydisp.fmreg[keydisp.fmmax] = base;
keydisp.fmmax++;
base++;
if ((base & 3) == 3) {
base += 0x100 - 3;
}
}
}
}
static void setpsghdl(UINT8 items) {
while(items--) {
if ((keydisp.keymax <= (KEYDISP_CHMAX - 3)) &&
(keydisp.psgmax < KEYDISP_PSGMAX)) {
keydisp.psgpos[keydisp.psgmax++] = keydisp.keymax;
keydisp.keymax += 3;
}
}
}
void keydisp_setfmboard(UINT b) {
keydisp.keymax = 0;
keydisp.fmmax = 0;
keydisp.psgmax = 0;
if (b & 0x02) {
if (!(b & 0x04)) {
setfmhdl(3, 0);
}
else { // 2枚刺しの時レジスタ移動
setfmhdl(3, 0x200);
}
setpsghdl(1);
}
if (b & 0x04) {
setfmhdl(6, 0);
setpsghdl(1);
}
if (b & 0x08) {
setfmhdl(6, 0);
setpsghdl(1);
}
if (b & 0x20) {
setfmhdl(6, 0);
setpsghdl(1);
}
if (b & 0x40) {
setfmhdl(12, 0);
setpsghdl(1);
}
if (b & 0x80) {
setpsghdl(3);
}
delayreset();
fmkeyreset();
psgkeyreset();
if (keydisp.mode == KEYDISP_MODEFM) {
keydisp.dispflag |= KEYDISP_FLAGSIZING;
}
}
// ---- MIDI
void keydisp_midi(const UINT8 *cmd) {
if (keydisp.mode != KEYDISP_MODEMIDI) {
return;
}
switch(cmd[0] & 0xf0) {
case 0x80:
keyoff(cmd[0] & 0x0f, cmd[1]);
break;
case 0x90:
if (cmd[2] & 0x7f) {
keyon(cmd[0] & 0x0f, cmd[1]);
}
else {
keyoff(cmd[0] & 0x0f, cmd[1]);
}
break;
case 0xb0:
if ((cmd[1] == 0x78) || (cmd[1] == 0x79) || (cmd[1] == 0x7b)) {
chkeyoff(cmd[0] & 0x0f);
}
break;
case 0xfe:
keyalloff();
break;
}
}
// ---- draw
static int getdispkeys(void) {
int keys;
switch(keydisp.mode) {
case KEYDISP_MODEFM:
keys = keydisp.keymax;
break;
case KEYDISP_MODEMIDI:
keys = 16;
break;
default:
keys = 0;
break;
}
if (keys > KEYDISP_CHMAX) {
keys = KEYDISP_CHMAX;
}
return(keys);
}
static void clearrect(CMNVRAM *vram, int x, int y, int cx, int cy) {
CMNPAL col;
switch(vram->bpp) {
#if defined(SUPPORT_8BPP)
case 8:
col.pal8 = keydisp.pal8[KEYDISP_PALBG];
break;
#endif
#if defined(SUPPORT_16BPP)
case 16:
col.pal16 = keydisp.pal16[KEYDISP_LEVEL];
break;
#endif
#if defined(SUPPORT_24BPP)
case 24:
#endif
#if defined(SUPPORT_32BPP)
case 32:
#endif
#if defined(SUPPORT_24BPP) || defined(SUPPORT_32BPP)
col.pal32 = keydisp.pal32[KEYDISP_LEVEL];
break;
#endif
default:
return;
}
cmndraw_fill(vram, x, y, cx, cy, col);
}
static void drawkeybg(CMNVRAM *vram) {
CMNPAL bg;
CMNPAL fg;
int i;
switch(vram->bpp) {
#if defined(SUPPORT_8BPP)
case 8:
bg.pal8 = keydisp.pal8[KEYDISP_PALBG];
fg.pal8 = keydisp.pal8[KEYDISP_PALFG];
break;
#endif
#if defined(SUPPORT_16BPP)
case 16:
bg.pal16 = keydisp.pal16[KEYDISP_LEVEL];
fg.pal16 = keydisp.pal16[0];
break;
#endif
#if defined(SUPPORT_24BPP)
case 24:
bg.pal32 = keydisp.pal32[KEYDISP_LEVEL];
fg.pal32 = keydisp.pal32[0];
break;
#endif
#if defined(SUPPORT_32BPP)
case 32:
bg.pal32 = keydisp.pal32[KEYDISP_LEVEL];
fg.pal32 = keydisp.pal32[0];
break;
#endif
default:
return;
}
for (i=0; i<10; i++) {
cmndraw_setpat(vram, keybrd1, i * KEYDISP_KEYCX, 0, bg, fg);
}
cmndraw_setpat(vram, keybrd2, 10 * KEYDISP_KEYCX, 0, bg, fg);
}
static BOOL draw1key(CMNVRAM *vram, KDCHANNEL *kdch, UINT n) {
KDKEYPOS *pos;
UINT pal;
CMNPAL fg;
pos = keydisp.keypos + (kdch->k[n] & 0x7f);
pal = kdch->r[n] & 0x7f;
switch(vram->bpp) {
#if defined(SUPPORT_8BPP)
case 8:
if (pal != (KEYDISP_LEVEL - 1)) {
fg.pal8 = keydisp.pal8[
(pos->pals)?KEYDISP_PALBG:KEYDISP_PALFG];
cmndraw_setfg(vram, pos->data, pos->posx, 0, fg);
kdch->r[n] = 0;
return(TRUE);
}
fg.pal8 = keydisp.pal8[KEYDISP_PALHIT];
break;
#endif
#if defined(SUPPORT_16BPP)
case 16:
fg.pal16 = keydisp.pal16[pal + pos->pals];
break;
#endif
#if defined(SUPPORT_24BPP)
case 24:
fg.pal32 = keydisp.pal32[pal + pos->pals];
break;
#endif
#if defined(SUPPORT_32BPP)
case 32:
fg.pal32 = keydisp.pal32[pal + pos->pals];
break;
#endif
default:
return(FALSE);
}
cmndraw_setfg(vram, pos->data, pos->posx, 0, fg);
return(FALSE);
}
static BOOL draw1ch(CMNVRAM *vram, UINT8 framepast, KDCHANNEL *kdch) {
BOOL draw;
UINT i;
BOOL coll;
UINT8 nextf;
UINT j;
draw = FALSE;
if (kdch->flag & 2) {
drawkeybg(vram);
draw = TRUE;
}
if (kdch->flag) {
coll = FALSE;
nextf = 0;
for (i=0; i<kdch->remain; i++) {
if ((kdch->r[i] & 0x80) || (kdch->flag & 2)) {
kdch->r[i] &= ~0x80;
if (kdch->r[i] < (KEYDISP_LEVEL - 1)) {
if (kdch->r[i] > framepast) {
kdch->r[i] -= framepast;
kdch->r[i] |= 0x80;
nextf = 1;
}
else {
kdch->r[i] = 0;
coll = TRUE;
}
}
coll |= draw1key(vram, kdch, i);
draw = TRUE;
}
}
if (coll) {
for (i=0; i<kdch->remain; i++) {
if (!kdch->r[i]) {
break;
}
}
for (j=i; i<kdch->remain; i++) {
if (kdch->r[i]) {
kdch->k[j] = kdch->k[i];
kdch->r[j] = kdch->r[i];
j++;
}
}
kdch->remain = j;
}
kdch->flag = nextf;
}
return(draw);
}
// ----
void keydisp_initialize(void) {
int r;
UINT16 x;
int i;
r = 0;
x = 0;
do {
for (i=0; i<12 && r<128; i++, r++) {
keydisp.keypos[r].posx = keyposdef[i].posx + x;
keydisp.keypos[r].pals = keyposdef[i].pals;
keydisp.keypos[r].data = keyposdef[i].data;
}
x += 28;
} while(r < 128);
keyallclear();
}
void keydisp_setpal(CMNPALFN *palfn) {
UINT i;
RGB32 pal32[KEYDISP_PALS];
if (palfn == NULL) {
return;
}
if (palfn->get8) {
for (i=0; i<KEYDISP_PALS; i++) {
keydisp.pal8[i] = (*palfn->get8)(palfn, i);
}
}
if (palfn->get32) {
for (i=0; i<KEYDISP_PALS; i++) {
pal32[i].d = (*palfn->get32)(palfn, i);
cmndraw_makegrad(keydisp.pal32, KEYDISP_LEVEL,
pal32[KEYDISP_PALFG], pal32[KEYDISP_PALHIT]);
cmndraw_makegrad(keydisp.pal32 + KEYDISP_LEVEL, KEYDISP_LEVEL,
pal32[KEYDISP_PALBG], pal32[KEYDISP_PALHIT]);
}
if (palfn->cnv16) {
for (i=0; i<KEYDISP_LEVEL*2; i++) {
keydisp.pal16[i] = (*palfn->cnv16)(palfn, keydisp.pal32[i]);
}
}
}
keydisp.dispflag |= KEYDISP_FLAGREDRAW;
}
void keydisp_setmode(UINT8 mode) {
if (keydisp.mode != mode) {
keydisp.mode = mode;
keydisp.dispflag |= KEYDISP_FLAGREDRAW | KEYDISP_FLAGSIZING;
keyallclear();
if (mode == KEYDISP_MODEFM) {
delayreset();
fmkeyreset();
psgkeyreset();
}
}
else {
keyalloff();
}
}
void keydisp_setdelay(UINT8 frames) {
keydisp.delay.warmbase = frames;
delayreset();
}
UINT8 keydisp_process(UINT8 framepast) {
int keys;
int i;
if (framepast) {
if (keydisp.mode == KEYDISP_MODEFM) {
fmkeysync();
psgkeysync();
delayexecevent(framepast);
}
keydisp.framepast += framepast;
}
keys = getdispkeys();
for (i=0; i<keys; i++) {
if (keydisp.ch[i].flag) {
keydisp.dispflag |= KEYDISP_FLAGDRAW;
break;
}
}
return(keydisp.dispflag);
}
void keydisp_getsize(int *width, int *height) {
if (width) {
*width = KEYDISP_WIDTH;
}
if (height) {
*height = (getdispkeys() * KEYDISP_KEYCY) + 1;
}
keydisp.dispflag &= ~KEYDISP_FLAGSIZING;
}
BOOL keydisp_paint(CMNVRAM *vram, BOOL redraw) {
BOOL draw;
int keys;
int i;
KDCHANNEL *p;
draw = FALSE;
if ((vram == NULL) ||
(vram->width < KEYDISP_WIDTH) || (vram->height < 1)) {
goto kdpnt_exit;
}
if (keydisp.dispflag & KEYDISP_FLAGREDRAW){
redraw = TRUE;
}
if (redraw) {
keyallreload();
clearrect(vram, 0, 0, KEYDISP_WIDTH, 1);
clearrect(vram, 0, 0, 1, vram->height);
draw = TRUE;
}
vram->ptr += vram->xalign + vram->yalign; // ptr (1, 1)
keys = (vram->height - 1) / KEYDISP_KEYCY;
keys = min(keys, getdispkeys());
for (i=0, p=keydisp.ch; i<keys; i++, p++) {
draw |= draw1ch(vram, keydisp.framepast, p);
vram->ptr += KEYDISP_KEYCY * vram->yalign;
}
keydisp.dispflag &= ~(KEYDISP_FLAGDRAW | KEYDISP_FLAGREDRAW);
keydisp.framepast = 0;
kdpnt_exit:
return(draw);
}
#endif
RetroPC.NET-CVS <cvs@retropc.net>