minivmac4ios/Mini vMac/mnvm_core/ASCEMDEV.c

415 lines
8.7 KiB
C
Raw Normal View History

/*
ASCEMDEV.c
Copyright (C) 2008 Paul C. Pratt
You can redistribute this file and/or modify it under the terms
of version 2 of the GNU General Public License as published by
the Free Software Foundation. You should have received a copy
of the license along with this file; see the file COPYING.
This file is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
license for more details.
*/
/*
Apple Sound Chip EMulated DEVice
*/
#ifndef AllFiles
#include "SYSDEPNS.h"
#include "ENDIANAC.h"
#include "MYOSGLUE.h"
#include "EMCONFIG.h"
#include "GLOBGLUE.h"
#include "VIAEMDEV.h"
#endif
#include "ASCEMDEV.h"
LOCALVAR ui3r SoundReg801 = 0;
LOCALVAR ui3r SoundReg802 = 0;
LOCALVAR ui3r SoundReg803 = 0;
LOCALVAR ui3r SoundReg804 = 0;
LOCALVAR ui3r SoundReg805 = 0;
LOCALVAR ui3r SoundReg_Volume = 0; /* 0x806 */
LOCALVAR ui3r SoundReg807 = 0;
LOCALVAR ui4r ASC_InputIndex = 0;
LOCALVAR ui3b ASC_SampBuff[0x800];
struct ASC_ChanR {
ui3b freq[0x800];
ui5r phase;
};
typedef struct ASC_ChanR ASC_ChanR;
LOCALVAR ASC_ChanR ASC_ChanA[4];
#define ASC_dolog (dbglog_HAVE && 0)
GLOBALFUNC ui5b ASC_Access(ui5b Data, blnr WriteMem, CPTR addr)
{
if (addr < 0x800) {
if (WriteMem) {
if ((1 == SoundReg801) && (2 == SoundReg802)) {
if ((2 * 370) == (ASC_InputIndex & 0x3FF)) {
SoundReg804 |= 0x08;
} else {
ui4r j = (addr & 0x400) | (ASC_InputIndex & 0x3FF);
++ASC_InputIndex;
ASC_SampBuff[j] = Data;
#if ASC_dolog && 0
dbglog_AddrAccess("ASC_Access SampBuff.wrap",
Data, WriteMem, j);
#endif
#if 1
if ((2 * 370) == (ASC_InputIndex & 0x3FF)) {
SoundReg804 |= 0x08;
}
#endif
}
#if ASC_dolog && 0
dbglog_writeCStr("ASC_InputIndex =");
dbglog_writeNum(ASC_InputIndex);
dbglog_writeReturn();
#endif
} else {
ASC_SampBuff[addr] = Data;
}
} else {
Data = ASC_SampBuff[addr];
}
#if ASC_dolog && 1
#if 1
if (((addr & 0x1FF) >= 0x04)
&& ((addr & 0x1FF) < (0x200 - 0x04)))
{
/* don't report them all */
} else
#endif
{
dbglog_AddrAccess("ASC_Access SampBuff",
Data, WriteMem, addr);
}
#endif
} else if (addr < 0x810) {
switch (addr) {
case 0x800: /* CONTROL */
if (WriteMem) {
} else {
Data = 0;
}
break;
case 0x801: /* ENABLE */
if (WriteMem) {
SoundReg801 = Data;
} else {
Data = SoundReg801;
}
break;
case 0x802: /* MODE */
if (WriteMem) {
SoundReg802 = Data;
} else {
Data = SoundReg802;
}
break;
case 0x803:
if (WriteMem) {
SoundReg803 = Data;
} else {
Data = SoundReg803;
}
break;
case 0x804:
if (WriteMem) {
SoundReg804 = Data;
} else {
Data = SoundReg804;
SoundReg804 = 0;
}
break;
case 0x805:
if (WriteMem) {
SoundReg805 = Data;
} else {
Data = SoundReg805;
}
break;
case 0x806: /* VOLUME */
if (WriteMem) {
SoundReg_Volume = Data >> 5;
if (0 != (Data & 0x1F)) {
ReportAbnormal("ASC - unexpected volume value");
}
} else {
Data = SoundReg_Volume << 5;
ReportAbnormal("ASC - reading volume register");
}
break;
case 0x807: /* CHAN */
if (WriteMem) {
SoundReg807 = Data;
} else {
Data = SoundReg807;
}
break;
default:
if (WriteMem) {
} else {
Data = 0;
}
break;
}
#if ASC_dolog && 1
if (addr != 0x804) {
dbglog_AddrAccess("ASC_Access Control",
Data, WriteMem, addr);
}
#endif
} else if (addr < 0x830) {
ui3r b = addr & 3;
ui3r chan = ((addr - 0x810) >> 3) & 3;
if (0 != (addr & 4)) {
if (WriteMem) {
ASC_ChanA[chan].freq[b] = Data;
} else {
Data = ASC_ChanA[chan].freq[b];
}
#if ASC_dolog && 1
dbglog_AddrAccess("ASC_Access Control",
Data, WriteMem, addr);
#endif
#if ASC_dolog && 0
dbglog_writeCStr("freq b=");
dbglog_writeNum(WriteMem);
dbglog_writeCStr(", chan=");
dbglog_writeNum(chan);
dbglog_writeReturn();
#endif
} else {
#if ASC_dolog && 1
dbglog_AddrAccess("ASC_Access Control *** unknown reg",
Data, WriteMem, addr);
#endif
}
} else if (addr < 0x838) {
#if ASC_dolog && 1
dbglog_AddrAccess("ASC_Access Control *** unknown reg",
Data, WriteMem, addr);
#endif
} else {
#if ASC_dolog && 1
dbglog_AddrAccess("ASC_Access Control ? *** unknown reg",
Data, WriteMem, addr);
#endif
ReportAbnormal("unknown ASC reg");
}
return Data;
}
/*
approximate volume levels of vMac, so:
x * vol_mult[SoundVolume] >> 16
+ vol_offset[SoundVolume]
= {approx} (x - kCenterSound) / (8 - SoundVolume) + kCenterSound;
*/
LOCALVAR const ui4b vol_mult[] = {
8192, 9362, 10922, 13107, 16384, 21845, 32768
};
LOCALVAR const trSoundSamp vol_offset[] = {
#if 3 == kLn2SoundSampSz
112, 110, 107, 103, 96, 86, 64, 0
#elif 4 == kLn2SoundSampSz
28672, 28087, 27307, 26215, 24576, 21846, 16384, 0
#else
#error "unsupported kLn2SoundSampSz"
#endif
};
LOCALVAR const ui4b SubTick_offset[kNumSubTicks] = {
0, 23, 46, 69, 92, 115, 138, 161,
185, 208, 231, 254, 277, 300, 323, 346
};
LOCALVAR const ui3r SubTick_n[kNumSubTicks] = {
23, 23, 23, 23, 23, 23, 23, 24,
23, 23, 23, 23, 23, 23, 23, 24
};
#if MySoundEnabled
LOCALVAR ui5b SoundPhase = 0;
#endif
#ifdef ASC_interrupt_PulseNtfy
IMPORTPROC ASC_interrupt_PulseNtfy(void);
#endif
#if MySoundEnabled
GLOBALPROC MacSound_SubTick(int SubTick)
{
ui4r actL;
tpSoundSamp p;
ui4r i;
ui4r j = 0;
ui4r n = SubTick_n[SubTick];
ui3b SoundVolume = SoundReg_Volume;
label_retry:
p = MySound_BeginWrite(n, &actL);
if (actL > 0) {
if (1 == SoundReg801) {
ui5b StartOffset = SubTick_offset[SubTick] + j;
ui3p addr = ASC_SampBuff + (2 * StartOffset);
if (2 == SoundReg802) {
addr += /* 0x400 */ 1;
}
for (i = 0; i < actL; i++) {
/* Copy sound data, high byte of each word */
#if ASC_dolog && 1
dbglog_StartLine();
dbglog_writeCStr("out sound ");
dbglog_writeCStr("[");
dbglog_writeHex(StartOffset + i);
dbglog_writeCStr("]");
dbglog_writeCStr(" = ");
dbglog_writeHex(*addr);
dbglog_writeReturn();
#endif
*p++ = *addr
#if 4 == kLn2SoundSampSz
<< 8
#endif
;
/* Move the address on */
*addr = 0x80;
addr += 2;
}
} else if (2 == SoundReg801) {
ui4r v;
ui4r i0;
ui4r i1;
ui4r i2;
ui4r i3;
ui5r freq0 = do_get_mem_long(ASC_ChanA[0].freq);
ui5r freq1 = do_get_mem_long(ASC_ChanA[1].freq);
ui5r freq2 = do_get_mem_long(ASC_ChanA[2].freq);
ui5r freq3 = do_get_mem_long(ASC_ChanA[3].freq);
#if ASC_dolog && 0
dbglog_writeCStr("freq0=");
dbglog_writeNum(freq0);
dbglog_writeCStr(", freq1=");
dbglog_writeNum(freq1);
dbglog_writeCStr(", freq2=");
dbglog_writeNum(freq2);
dbglog_writeCStr(", freq3=");
dbglog_writeNum(freq3);
dbglog_writeReturn();
#endif
for (i = 0; i < actL; i++) {
ASC_ChanA[0].phase += freq0;
ASC_ChanA[1].phase += freq1;
ASC_ChanA[2].phase += freq2;
ASC_ChanA[3].phase += freq3;
#if 1
i0 = ((ASC_ChanA[0].phase + 0x4000) >> 15) & 0x1FF;
i1 = ((ASC_ChanA[1].phase + 0x4000) >> 15) & 0x1FF;
i2 = ((ASC_ChanA[2].phase + 0x4000) >> 15) & 0x1FF;
i3 = ((ASC_ChanA[3].phase + 0x4000) >> 15) & 0x1FF;
#else
i0 = ((ASC_ChanA[0].phase + 0x8000) >> 16) & 0x1FF;
i1 = ((ASC_ChanA[1].phase + 0x8000) >> 16) & 0x1FF;
i2 = ((ASC_ChanA[2].phase + 0x8000) >> 16) & 0x1FF;
i3 = ((ASC_ChanA[3].phase + 0x8000) >> 16) & 0x1FF;
#endif
v = ASC_SampBuff[i0]
+ ASC_SampBuff[0x0200 + i1]
+ ASC_SampBuff[0x0400 + i2]
+ ASC_SampBuff[0x0600 + i3];
#if ASC_dolog && 1
dbglog_StartLine();
dbglog_writeCStr("i0=");
dbglog_writeNum(i0);
dbglog_writeCStr(", i1=");
dbglog_writeNum(i1);
dbglog_writeCStr(", i2=");
dbglog_writeNum(i2);
dbglog_writeCStr(", i3=");
dbglog_writeNum(i3);
dbglog_writeCStr(", output sound v=");
dbglog_writeNum(v);
dbglog_writeReturn();
#endif
*p++ = (v >> 2);
++SoundPhase;
SoundPhase &= 0x1FF;
}
} else {
for (i = 0; i < actL; i++) {
*p++ = kCenterSound;
}
}
if (SoundVolume < 7) {
/*
Usually have volume at 7, so this
is just for completeness.
*/
ui5b mult = (ui5b)vol_mult[SoundVolume];
trSoundSamp offset = vol_offset[SoundVolume];
p -= actL;
for (i = 0; i < actL; i++) {
*p = (trSoundSamp)((ui5b)(*p) * mult >> 16) + offset;
++p;
}
}
MySound_EndWrite(actL);
n -= actL;
j += actL;
if (n > 0) {
goto label_retry;
}
}
}
#endif
GLOBALPROC ASC_Update(void)
{
if (1 == SoundReg801) {
ASC_InputIndex = 0;
SoundReg804 |= 0x04;
#ifdef ASC_interrupt_PulseNtfy
ASC_interrupt_PulseNtfy();
#endif
#if ASC_dolog && 1
dbglog_StartLine();
dbglog_writeCStr("called ASC_interrupt_PulseNtfy");
dbglog_writeReturn();
#endif
}
}