Files
mazegame/engine/src/scc.c
T

393 lines
10 KiB
C

// ____________________________
// ██▀▀█▀▀██▀▀▀▀▀▀▀█▀▀█ │ ▄▄▄ ▄▄ ▄▄
// ██ ▀ █▄ ▀██▄ ▀ ▄█ ▄▀▀ █ │ ▀█▄ ██ ▀ ██ ▀
// █ █ █ ▀▀ ▄█ █ █ ▀▄█ █▄ │ ▄▄█▀ ▀█▄▀ ▀█▄▀
// ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀────────┘
// by Guillaume 'Aoineko' Blanchard under CC BY-SA license
//─────────────────────────────────────────────────────────────────────────────
// PSG handler module
//
// References:
// - Konami SCC Sound Chip
// http://bifi.msxnet.org/msxnet/tech/scc.html
//─────────────────────────────────────────────────────────────────────────────
#include "scc.h"
#include "system.h"
#include "bios.h"
//=============================================================================
// DEFINES
//=============================================================================
//=============================================================================
// READ-ONLY DATA
//=============================================================================
#if (SCC_USE_EXTA)
const u16 g_SCC_WaveformAddr[5] = { SCC_ADDR_WAVE_1, SCC_ADDR_WAVE_2, SCC_ADDR_WAVE_3, SCC_ADDR_WAVE_4, SCC_ADDR_WAVE_5 };
#endif
#if (SCC_USE_RESUME)
u8 g_SCC_MixerBackup;
#endif
//=============================================================================
// MEMORY DATA
//=============================================================================
#if ((SCC_SLOT_MODE == SCC_SLOT_USER) || (SCC_SLOT_MODE == SCC_SLOT_AUTO))
u8 g_SCC_SlotId = SLOT_NOTFOUND;
#endif
//=============================================================================
// FUNCTIONS
//=============================================================================
#if (SCC_SLOT_MODE == SCC_SLOT_AUTO)
//-----------------------------------------------------------------------------
//
bool SCC_CheckSlotID(u8 slotId)
{
// Check ROM
u8 val = BIOS_InterSlotRead(slotId, 0x8000);
BIOS_InterSlotWrite(slotId, 0x8000, ~val);
if (BIOS_InterSlotRead(slotId, 0x8000) != val) // Is RAM?
{
BIOS_InterSlotWrite(slotId, 0x8000, val); // backup RAM value
return FALSE;
}
// Select SCC
BIOS_InterSlotWrite(slotId, 0x9000, 0x3F);
// Check SCC RAM
val = BIOS_InterSlotRead(slotId, 0x9800);
BIOS_InterSlotWrite(slotId, 0x9800, ~val);
if (BIOS_InterSlotRead(slotId, 0x9800) != val) // Is SCC RAM?
{
BIOS_InterSlotWrite(slotId, 0x9800, val); // backup SCC RAM value
return TRUE;
}
return FALSE;
}
//-----------------------------------------------------------------------------
//
u8 SCC_AutoDetect()
{
return Sys_CheckSlot(SCC_CheckSlotID);
}
//-----------------------------------------------------------------------------
// Auto-detecting the SCC slot id
/*u8 SCC_AutoDetect() __NAKED
{
__asm
SCC_Detect:
in a,(#0xA8) // read prim. slotregister
rra
rra
rra
rra
and #0b00000011 // A = prim.slot page 2
ld b, #0
ld c, a
ld hl, #M_EXPTBL
add hl, bc
bit #7, (hl) // page 2-slot expanded ?
jr z, SCC_Test
ld hl, #M_SLTTBL
add hl, bc
ld a, (hl) // A = sec.sel.reg. of page 2-slot
rra
rra
and #0b00001100 // bit 1/2 = sec.slot page 2
or c
set #7, a // compose sec.slot-code
SCC_Test:
push af // save page 2-slot on the stack
ld a, (M_EXPTBL) // 1st slot to test
SCC_TestSlot:
push af // save test-slot on the stack
ld h, #0x80
call #R_ENASLT // switch slot-to-test in 8000-bfffh
ld hl, #0x9000
ld b, (hl) // save contents of address 9000h
ld (hl), #0x3F // activate SCC (if present)
xor a
ld (0xBFFE), a // SCC+ patch for bluemsx
ld h, #0x9C // address of SCC-register mirrors
ld de, #0x9800 // 9800h = address of SCC-registers
SCC_TestReg:
ld a, (de)
ld c, a // save contents of address 98xxh
ld a, (hl) // read byte from address 9cxxh
cpl // and invert it
ld (de), a // write inverted byte to 98xxh
cp (hl) // same value on 9cxxh ?
ld a, c
ld (de), a // restore value on 98xxh
jr nz, SCC_NextSlot // unequal -> no SCC -> continue search
inc hl
inc de // next test-addresses
bit #7, l // 128 addresses (registers) tested ?
jr z, SCC_TestReg // no -> repeat mirror-test
ld a, b
ld (#0x9000), a // restore value on 9000h
pop bc // retrieve slotcode (=SCC-slot) from stack
jr SCC_Done // SCC found, restore page 2-slot & return
SCC_NextSlot:
ld a, b
ld (#0x9000), a // restore value on 9000h
pop bc // retrieve slotcode from stack
bit #7, b // test-slot = sec.slot ?
jr z, SCC_NextPrim
ld a, b
add a, #4 // increase sec.slotnumber
bit #4, a // sec.slot = 4 ?
jr z, SCC_TestSlot
SCC_NextPrim:
ld a, b
and #0b00000011
cp #3 // prim.slot = 3 ?
jr z, SCC_NoSCC
inc a // increase prim.slotnumber
ld d, #0
ld e, a
ld hl, #M_EXPTBL
add hl, de
or (hl) // combine slot-expansion with slotcode
jr SCC_TestSlot
SCC_NoSCC:
ld b, #0xFF // code for no SCC
SCC_Done:
pop af // retrieve page 2-slot from stack
push bc
ld h, #0x80
call #R_ENASLT // restore original page 2-slot
pop bc
ei
ld a, b // return value
ret
__endasm;
}*/
//-----------------------------------------------------------------------------
//
/*u8 SCC_AutoDetect() __NAKED
{
__asm
call SCCDETECT
ret nc // SCC found (result in A)
ld a, #SLOT_NOTFOUND
ret
SCCDETECT:
//----------------------------------------------------
// SCCDETECT (Made by : NYYRIKKI)
// Input: (None)
// Output:
// Success:
// CF = NC
// A = SlotID
// B = Number of untested slots
// (SCC SLOT on #8000-#BFFF)
//
// Fail:
// CF = C
// B = 0
// (Random slot on #8000-#BFFF)
// Changes: All registers
//----------------------------------------------------
ld b, #16
SCCDETECT_NEXT:
//-----------------------------------------------------
// Call this address if you need to detect another SCC
// Input: B = Value returned by SCCDETECT (must be >0)
//-----------------------------------------------------
ld h, #0xFC
SLOTLOOP:
dec b
ld a, b
and #3
add a, #0xC1
ld l, a
ld a, (hl)
and #128
or b
cp #12
call nc, TESTSLOT
ret nc
inc b
djnz SLOTLOOP
ret
TESTSLOT:
xor #12
ld c, a
push bc
push hl
ld h, #0x80
call #R_ENASLT
ld a, (#0x9000)
ld d, a
ld a, #0x3F
ld (#0x9000), a
ld hl, #0x9800
ld e, (hl)
xor a
ld (hl), a
or (hl)
jr nz, NOSCC
dec a
ld (hl), a
ld a, (hl)
inc a
jr nz, NOSCC
ld a, (#0x9000)
xor #0x3F
jr nz, EXIT
NOSCC:
ld (hl), e
ld a, d
ld (#0x9000), a
scf
EXIT:
pop hl
pop bc
ld a, c
ret
__endasm;
}*/
#endif // (SCC_SLOT_MODE == SCC_SLOT_AUTO)
//-----------------------------------------------------------------------------
// Initialize SCC module
bool SCC_Initialize()
{
#if (SCC_SLOT_MODE == SCC_SLOT_AUTO)
g_SCC_SlotId = SCC_AutoDetect();
if (g_SCC_SlotId == SLOT_NOTFOUND)
return FALSE;
#elif (SCC_SLOT_MODE == SCC_SLOT_USER)
g_SCC_SlotId = SLOT_2; // Non-expanded secondary slot cartridge
#endif
SCC_Select();
return TRUE;
}
//-----------------------------------------------------------------------------
// Select sound chip on SCC cartridge
void SCC_Select()
{
#if (SCC_SLOT_MODE == SCC_SLOT_DIRECT)
SET_BANK_SEGMENT(2, 0x3F);
#else
BIOS_InterSlotWrite(SCC_SLOT, 0x9000, 0x3F);
#endif
}
//-----------------------------------------------------------------------------
// Set the value of a given register
// Note: Only wavetable area can be read
void SCC_SetRegister(u8 reg, u8 value)
{
#if (SCC_SLOT_MODE == SCC_SLOT_DIRECT)
Poke(0x9800 + reg, value);
#else
BIOS_InterSlotWrite(SCC_SLOT, 0x9800 + reg, value);
#endif
#if (SCC_USE_RESUME)
if (reg == SCC_REG_MIXER)
g_SCC_MixerBackup = value;
#endif
}
//-----------------------------------------------------------------------------
// Get the value of a given register
u8 SCC_GetRegister(u8 reg)
{
#if (SCC_SLOT_MODE == SCC_SLOT_DIRECT)
return Peek(0x9800 + reg);
#else
return BIOS_InterSlotRead(SCC_SLOT, 0x9800 + reg);
#endif
}
//-----------------------------------------------------------------------------
// Mute the SCC sound chip
void SCC_Mute()
{
#if (SCC_SLOT_MODE == SCC_SLOT_DIRECT)
Poke(SCC_ADDR_MIXER, 0);
#else
BIOS_InterSlotWrite(SCC_SLOT, SCC_ADDR_MIXER, 0x00);
#endif
}
#if (SCC_USE_RESUME)
//-----------------------------------------------------------------------------
// Resume SCC sound
void SCC_Resume()
{
#if (SCC_SLOT_MODE == SCC_SLOT_DIRECT)
Poke(SCC_ADDR_MIXER, g_SCC_MixerBackup);
#else
BIOS_InterSlotWrite(SCC_SLOT, SCC_ADDR_MIXER, g_SCC_MixerBackup);
#endif
}
#endif
//-----------------------------------------------------------------------------
// EXTRA FEATURES
//-----------------------------------------------------------------------------
#if (SCC_USE_EXTA)
//-----------------------------------------------------------------------------
// Load a full waveform
void SCC_LoadWaveform(u8 channel, const u8* data)
{
u16 addr = g_SCC_WaveformAddr[channel];
#if (SCC_SLOT_MODE == SCC_SLOT_DIRECT)
Mem_Copy(data, addr, 32);
#else
loopx(32)
BIOS_InterSlotWrite(SCC_SLOT, addr++, *data++);
#endif
}
//-----------------------------------------------------------------------------
// Set channel frquency
void SCC_SetFrequency(u8 channel, u16 freq)
{
u16 addr = SCC_ADDR_FREQ_1 + (channel * 2);
#if (SCC_SLOT_MODE == SCC_SLOT_DIRECT)
Mem_Copy(&freq, addr, 2);
#else
BIOS_InterSlotWrite(SCC_SLOT, addr++, (u8)freq);
BIOS_InterSlotWrite(SCC_SLOT, addr, freq >> 8);
#endif
}
#endif // (SCC_USE_EXTA)