diff options
| author | Marcus Huderle <huderlem@gmail.com> | 2017-11-03 18:00:47 -0700 | 
|---|---|---|
| committer | Marcus Huderle <huderlem@gmail.com> | 2017-11-03 18:14:15 -0700 | 
| commit | 415fb6738308a9bfa2217419d3861784dcd8e5d9 (patch) | |
| tree | 8793240342d37104610f34756f6fee9db706ee8f /src/libs | |
| parent | ae56f7859fe17915d3fb33713dfef2fe31fe2a1d (diff) | |
| parent | 1698e882b4760bcfe8cf91073cf7e46541ae6392 (diff) | |
Merge remote-tracking branch 'upstream/master' into pokenav
Diffstat (limited to 'src/libs')
| -rw-r--r-- | src/libs/agb_flash.c | 295 | ||||
| -rw-r--r-- | src/libs/agb_flash_1m.c | 86 | ||||
| -rw-r--r-- | src/libs/agb_flash_le.c | 31 | ||||
| -rw-r--r-- | src/libs/agb_flash_mx.c | 197 | ||||
| -rw-r--r-- | src/libs/libc.c | 143 | ||||
| -rw-r--r-- | src/libs/m4a_2.c | 912 | ||||
| -rw-r--r-- | src/libs/m4a_4.c | 545 | ||||
| -rw-r--r-- | src/libs/m4a_tables.c | 307 | ||||
| -rw-r--r-- | src/libs/siirtc.c | 432 | 
9 files changed, 2948 insertions, 0 deletions
| diff --git a/src/libs/agb_flash.c b/src/libs/agb_flash.c new file mode 100644 index 000000000..340d469a7 --- /dev/null +++ b/src/libs/agb_flash.c @@ -0,0 +1,295 @@ +#include "gba/gba.h" +#include "gba/flash_internal.h" + +static u8 sTimerNum; +static u16 sTimerCount; +static vu16 *sTimerReg; +static u16 sSavedIme; + +u8 gFlashTimeoutFlag; +u8 (*PollFlashStatus)(u8 *); +u16 (*WaitForFlashWrite)(u8 phase, u8 *addr, u8 lastData); +u16 (*ProgramFlashSector)(u16 sectorNum, u8 *src); +const struct FlashType *gFlash; +u16 (*ProgramFlashByte)(u16 sectorNum, u32 offset, u8 data); +u16 gFlashNumRemainingBytes; +u16 (*EraseFlashChip)(); +u16 (*EraseFlashSector)(u16 sectorNum); +const u16 *gFlashMaxTime; + +void SetReadFlash1(u16 *dest); + +void SwitchFlashBank(u8 bankNum) +{ +    FLASH_WRITE(0x5555, 0xAA); +    FLASH_WRITE(0x2AAA, 0x55); +    FLASH_WRITE(0x5555, 0xB0); +    FLASH_WRITE(0x0000, bankNum); +} + +#define DELAY()                  \ +do {                             \ +    vu16 i;                      \ +    for (i = 20000; i != 0; i--) \ +        ;                        \ +} while (0) + +u16 ReadFlashId(void) +{ +    u16 flashId; +    u16 readFlash1Buffer[0x20]; +    u8 (*readFlash1)(u8 *); + +    SetReadFlash1(readFlash1Buffer); +    readFlash1 = (u8 (*)(u8 *))((s32)readFlash1Buffer + 1); + +    // Enter ID mode. +    FLASH_WRITE(0x5555, 0xAA); +    FLASH_WRITE(0x2AAA, 0x55); +    FLASH_WRITE(0x5555, 0x90); +    DELAY(); + +    flashId = readFlash1(FLASH_BASE + 1) << 8; +    flashId |= readFlash1(FLASH_BASE); + +    // Leave ID mode. +    FLASH_WRITE(0x5555, 0xAA); +    FLASH_WRITE(0x2AAA, 0x55); +    FLASH_WRITE(0x5555, 0xF0); +    FLASH_WRITE(0x5555, 0xF0); +    DELAY(); + +    return flashId; +} + +void FlashTimerIntr(void) +{ +    if (sTimerCount != 0 && --sTimerCount == 0) +        gFlashTimeoutFlag = 1; +} + +u16 SetFlashTimerIntr(u8 timerNum, void (**intrFunc)(void)) +{ +    if (timerNum >= 4) +        return 1; + +    sTimerNum = timerNum; +    sTimerReg = ®_TMCNT(sTimerNum); +    *intrFunc = FlashTimerIntr; +    return 0; +} + +void StartFlashTimer(u8 phase) +{ +    const u16 *maxTime = &gFlashMaxTime[phase * 3]; +    sSavedIme = REG_IME; +    REG_IME = 0; +    sTimerReg[1] = 0; +    REG_IE |= (INTR_FLAG_TIMER0 << sTimerNum); +    gFlashTimeoutFlag = 0; +    sTimerCount = *maxTime++; +    *sTimerReg++ = *maxTime++; +    *sTimerReg-- = *maxTime++; +    REG_IF = (INTR_FLAG_TIMER0 << sTimerNum); +    REG_IME = 1; +} + +void StopFlashTimer(void) +{ +    REG_IME = 0; +    *sTimerReg++ = 0; +    *sTimerReg-- = 0; +    REG_IE &= ~(INTR_FLAG_TIMER0 << sTimerNum); +    REG_IME = sSavedIme; +} + +u8 ReadFlash1(u8 *addr) +{ +    return *addr; +} + +void SetReadFlash1(u16 *dest) +{ +    u16 *src; +    u16 i; + +    PollFlashStatus = (u8 (*)(u8 *))((s32)dest + 1); + +    src = (u16 *)ReadFlash1; +    src = (u16 *)((s32)src ^ 1); + +    i = ((s32)SetReadFlash1 - (s32)ReadFlash1) >> 1; + +    while (i != 0) +    { +        *dest++ = *src++; +        i--; +    } +} + +void ReadFlash_Core(u8 *src, u8 *dest, u32 size) +{ +    while (size-- != 0) +    { +        *dest++ = *src++; +    } +} + +void ReadFlash(u16 sectorNum, u32 offset, u8 *dest, u32 size) +{ +    u8 *src; +    u16 i; +    u16 readFlash_Core_Buffer[0x40]; +    u16 *funcSrc; +    u16 *funcDest; +    void (*readFlash_Core)(u8 *, u8 *, u32); + +    REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; + +    if (gFlash->romSize == FLASH_ROM_SIZE_1M) +    { +        SwitchFlashBank(sectorNum / SECTORS_PER_BANK); +        sectorNum %= SECTORS_PER_BANK; +    } + +    funcSrc = (u16 *)ReadFlash_Core; +    funcSrc = (u16 *)((s32)funcSrc ^ 1); +    funcDest = readFlash_Core_Buffer; + +    i = ((s32)ReadFlash - (s32)ReadFlash_Core) >> 1; + +    while (i != 0) +    { +        *funcDest++ = *funcSrc++; +        i--; +    } + +    readFlash_Core = (void (*)(u8 *, u8 *, u32))((s32)readFlash_Core_Buffer + 1); + +    src = FLASH_BASE + (sectorNum << gFlash->sector.shift) + offset; + +    readFlash_Core(src, dest, size); +} + +u32 VerifyFlashSector_Core(u8 *src, u8 *tgt, u32 size) +{ +    while (size-- != 0) +    { +        if (*tgt++ != *src++) +            return (u32)(tgt - 1); +    } + +    return 0; +} + +u32 VerifyFlashSector(u16 sectorNum, u8 *src) +{ +    u16 i; +    u16 verifyFlashSector_Core_Buffer[0x80]; +    u16 *funcSrc; +    u16 *funcDest; +    u8 *tgt; +    u16 size; +    u32 (*verifyFlashSector_Core)(u8 *, u8 *, u32); + +    REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; + +    if (gFlash->romSize == FLASH_ROM_SIZE_1M) +    { +        SwitchFlashBank(sectorNum / SECTORS_PER_BANK); +        sectorNum %= SECTORS_PER_BANK; +    } + +    funcSrc = (u16 *)VerifyFlashSector_Core; +    funcSrc = (u16 *)((s32)funcSrc ^ 1); +    funcDest = verifyFlashSector_Core_Buffer; + +    i = ((s32)VerifyFlashSector - (s32)VerifyFlashSector_Core) >> 1; + +    while (i != 0) +    { +        *funcDest++ = *funcSrc++; +        i--; +    } + +    verifyFlashSector_Core = (u32 (*)(u8 *, u8 *, u32))((s32)verifyFlashSector_Core_Buffer + 1); + +    tgt = FLASH_BASE + (sectorNum << gFlash->sector.shift); +    size = gFlash->sector.size; + +    return verifyFlashSector_Core(src, tgt, size); // return 0 if verified. +} + +u32 VerifyFlashSectorNBytes(u16 sectorNum, u8 *src, u32 n) +{ +    u16 i; +    u16 verifyFlashSector_Core_Buffer[0x80]; +    u16 *funcSrc; +    u16 *funcDest; +    u8 *tgt; +    u32 (*verifyFlashSector_Core)(u8 *, u8 *, u32); + +    if (gFlash->romSize == FLASH_ROM_SIZE_1M) +    { +        SwitchFlashBank(sectorNum / SECTORS_PER_BANK); +        sectorNum %= SECTORS_PER_BANK; +    } + +    REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; + +    funcSrc = (u16 *)VerifyFlashSector_Core; +    funcSrc = (u16 *)((s32)funcSrc ^ 1); +    funcDest = verifyFlashSector_Core_Buffer; + +    i = ((s32)VerifyFlashSector - (s32)VerifyFlashSector_Core) >> 1; + +    while (i != 0) +    { +        *funcDest++ = *funcSrc++; +        i--; +    } + +    verifyFlashSector_Core = (u32 (*)(u8 *, u8 *, u32))((s32)verifyFlashSector_Core_Buffer + 1); + +    tgt = FLASH_BASE + (sectorNum << gFlash->sector.shift); + +    return verifyFlashSector_Core(src, tgt, n); +} + +u32 ProgramFlashSectorAndVerify(u16 sectorNum, u8 *src) // 3rd is unused +{ +    u8 i; +    u32 result; + +    for (i = 0; i < 3; i++) // 3 attempts +    { +        result = ProgramFlashSector(sectorNum, src); +        if (result != 0) +            continue; + +        result = VerifyFlashSector(sectorNum, src); +        if (result == 0) +            break; +    } + +    return result; // return 0 if verified and programmed. +} + +u32 ProgramFlashSectorAndVerifyNBytes(u16 sectorNum, u8 *src, u32 n) +{ +    u8 i; +    u32 result; + +    for (i = 0; i < 3; i++) +    { +        result = ProgramFlashSector(sectorNum, src); +        if (result != 0) +            continue; + +        result = VerifyFlashSectorNBytes(sectorNum, src, n); +        if (result == 0) +            break; +    } + +    return result; +} diff --git a/src/libs/agb_flash_1m.c b/src/libs/agb_flash_1m.c new file mode 100644 index 000000000..e249fab9a --- /dev/null +++ b/src/libs/agb_flash_1m.c @@ -0,0 +1,86 @@ +#include "gba/gba.h" +#include "gba/flash_internal.h" + +static const char AgbLibFlashVersion[] = "FLASH1M_V103"; + +const struct FlashSetupInfo * const sSetupInfos[] = +{ +    &MX29L010, +    &LE26FV10N1TS, +    &DefaultFlash +}; + +u16 IdentifyFlash(void) +{ +    u16 result; +    u16 flashId; +    const struct FlashSetupInfo * const *setupInfo; + +    REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; + +    flashId = ReadFlashId(); + +    setupInfo = sSetupInfos; +    result = 1; + +    for (;;) +    { +        if ((*setupInfo)->type.ids.separate.makerId == 0) +            break; + +        if (flashId == (*setupInfo)->type.ids.joined) +        { +            result = 0; +            break; +        } + +        setupInfo++; +    } + +    ProgramFlashByte = (*setupInfo)->programFlashByte; +    ProgramFlashSector = (*setupInfo)->programFlashSector; +    EraseFlashChip = (*setupInfo)->eraseFlashChip; +    EraseFlashSector = (*setupInfo)->eraseFlashSector; +    WaitForFlashWrite = (*setupInfo)->WaitForFlashWrite; +    gFlashMaxTime = (*setupInfo)->maxTime; +    gFlash = &(*setupInfo)->type; + +    return result; +} + +u16 WaitForFlashWrite_Common(u8 phase, u8 *addr, u8 lastData) +{ +    u16 result = 0; +    u8 status; + +    StartFlashTimer(phase); + +    while ((status = PollFlashStatus(addr)) != lastData) +    { +        if (status & 0x20) +        { +            // The write operation exceeded the flash chip's time limit. + +            if (PollFlashStatus(addr) == lastData) +                break; + +            FLASH_WRITE(0x5555, 0xF0); +            result = phase | 0xA000u; +            break; +        } + +        if (gFlashTimeoutFlag) +        { +            if (PollFlashStatus(addr) == lastData) +                break; + +            FLASH_WRITE(0x5555, 0xF0); +            result = phase | 0xC000u; +            break; +        } +    } + +    StopFlashTimer(); + +    return result; +} diff --git a/src/libs/agb_flash_le.c b/src/libs/agb_flash_le.c new file mode 100644 index 000000000..39d956e27 --- /dev/null +++ b/src/libs/agb_flash_le.c @@ -0,0 +1,31 @@ +#include "gba/gba.h" +#include "gba/flash_internal.h" + +const u16 leMaxTime[] = +{ +      10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, +      10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, +    2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, +    2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, +}; + +const struct FlashSetupInfo LE26FV10N1TS = +{ +    ProgramFlashByte_MX, +    ProgramFlashSector_MX, +    EraseFlashChip_MX, +    EraseFlashSector_MX, +    WaitForFlashWrite_Common, +    leMaxTime, +    { +        131072, // ROM size +        { +            4096, // sector size +              12, // bit shift to multiply by sector size (4096 == 1 << 12) +              32, // number of sectors +               0  // appears to be unused +        }, +        { 3, 1 }, // wait state setup data +        { { 0x62, 0x13 } } // ID +    } +}; diff --git a/src/libs/agb_flash_mx.c b/src/libs/agb_flash_mx.c new file mode 100644 index 000000000..67348901f --- /dev/null +++ b/src/libs/agb_flash_mx.c @@ -0,0 +1,197 @@ +#include "gba/gba.h" +#include "gba/flash_internal.h" + +const u16 mxMaxTime[] = +{ +      10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, +      10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, +    2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, +    2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, +}; + +const struct FlashSetupInfo MX29L010 = +{ +    ProgramFlashByte_MX, +    ProgramFlashSector_MX, +    EraseFlashChip_MX, +    EraseFlashSector_MX, +    WaitForFlashWrite_Common, +    mxMaxTime, +    { +        131072, // ROM size +        { +            4096, // sector size +              12, // bit shift to multiply by sector size (4096 == 1 << 12) +              32, // number of sectors +               0  // appears to be unused +        }, +        { 3, 1 }, // wait state setup data +#if defined(GERMAN) && defined(SAPPHIRE) +        { { 0xBF, 0xD4 } } // ID +#else +        { { 0xC2, 0x09 } } // ID +#endif +    } +}; + +const struct FlashSetupInfo DefaultFlash = +{ +    ProgramFlashByte_MX, +    ProgramFlashSector_MX, +    EraseFlashChip_MX, +    EraseFlashSector_MX, +    WaitForFlashWrite_Common, +    mxMaxTime, +    { +        131072, // ROM size +        { +            4096, // sector size +              12, // bit shift to multiply by sector size (4096 == 1 << 12) +              32, // number of sectors +               0  // appears to be unused +        }, +        { 3, 1 }, // wait state setup data +        { { 0x00, 0x00 } } // ID of 0 +    } +}; + +u16 EraseFlashChip_MX(void) +{ +    u16 result; +    u16 readFlash1Buffer[0x20]; + +    REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | gFlash->wait[0]; + +    FLASH_WRITE(0x5555, 0xAA); +    FLASH_WRITE(0x2AAA, 0x55); +    FLASH_WRITE(0x5555, 0x80); +    FLASH_WRITE(0x5555, 0xAA); +    FLASH_WRITE(0x2AAA, 0x55); +    FLASH_WRITE(0x5555, 0x10); + +    SetReadFlash1(readFlash1Buffer); + +    result = WaitForFlashWrite(3, FLASH_BASE, 0xFF); + +    REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; + +    return result; +} + +u16 EraseFlashSector_MX(u16 sectorNum) +{ +    u16 numTries; +    u16 result; +    u8 *addr; +    u16 readFlash1Buffer[0x20]; + +    if (sectorNum >= gFlash->sector.count) +        return 0x80FF; + +    SwitchFlashBank(sectorNum / SECTORS_PER_BANK); +    sectorNum %= SECTORS_PER_BANK; + +    numTries = 0; + +try_erase: +    REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | gFlash->wait[0]; + +    addr = FLASH_BASE + (sectorNum << gFlash->sector.shift); + +    FLASH_WRITE(0x5555, 0xAA); +    FLASH_WRITE(0x2AAA, 0x55); +    FLASH_WRITE(0x5555, 0x80); +    FLASH_WRITE(0x5555, 0xAA); +    FLASH_WRITE(0x2AAA, 0x55); +    *addr = 0x30; + +    SetReadFlash1(readFlash1Buffer); + +    result = WaitForFlashWrite(2, addr, 0xFF); + +    if (!(result & 0xA000) || numTries > 3) +        goto done; + +    numTries++; + +    goto try_erase; + +done: +    REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; + +    return result; +} + +u16 ProgramFlashByte_MX(u16 sectorNum, u32 offset, u8 data) +{ +    u8 *addr; +    u16 readFlash1Buffer[0x20]; + +    if (offset >= gFlash->sector.size) +        return 0x8000; + +    SwitchFlashBank(sectorNum / SECTORS_PER_BANK); +    sectorNum %= SECTORS_PER_BANK; + +    addr = FLASH_BASE + (sectorNum << gFlash->sector.shift) + offset; + +    SetReadFlash1(readFlash1Buffer); + +    REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | gFlash->wait[0]; + +    FLASH_WRITE(0x5555, 0xAA); +    FLASH_WRITE(0x2AAA, 0x55); +    FLASH_WRITE(0x5555, 0xA0); +    *addr = data; + +    return WaitForFlashWrite(1, addr, data); +} + +static u16 ProgramByte(u8 *src, u8 *dest) +{ +    FLASH_WRITE(0x5555, 0xAA); +    FLASH_WRITE(0x2AAA, 0x55); +    FLASH_WRITE(0x5555, 0xA0); +    *dest = *src; + +    return WaitForFlashWrite(1, dest, *src); +} + +u16 ProgramFlashSector_MX(u16 sectorNum, u8 *src) +{ +    u16 result; +    u8 *dest; +    u16 readFlash1Buffer[0x20]; + +    if (sectorNum >= gFlash->sector.count) +        return 0x80FF; + +    result = EraseFlashSector_MX(sectorNum); + +    if (result != 0) +        return result; + +    SwitchFlashBank(sectorNum / SECTORS_PER_BANK); +    sectorNum %= SECTORS_PER_BANK; + +    SetReadFlash1(readFlash1Buffer); + +    REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | gFlash->wait[0]; + +    gFlashNumRemainingBytes = gFlash->sector.size; +    dest = FLASH_BASE + (sectorNum << gFlash->sector.shift); + +    while (gFlashNumRemainingBytes > 0) +    { +        result = ProgramByte(src, dest); + +        if (result != 0) +            break; + +        gFlashNumRemainingBytes--; +        src++; +        dest++; +    } + +    return result; +} diff --git a/src/libs/libc.c b/src/libs/libc.c new file mode 100644 index 000000000..920673e3e --- /dev/null +++ b/src/libs/libc.c @@ -0,0 +1,143 @@ +#include "global.h" +#include <stddef.h> + +#define LBLOCKSIZE (sizeof(long)) + +// Nonzero if (long)X contains a NULL byte. +#define CONTAINSNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080) + +// Nonzero if X is not aligned on a "long" boundary. +#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1)) + +void *memcpy(void *dst0, const void *src0, size_t len0) +{ +    char *dst = dst0; +    const char *src = src0; +    long *aligned_dst; +    const long *aligned_src; +    unsigned int len = len0; + +    // If the size is small, or either src or dst is unaligned, +    // then go to the byte copy loop. This should be rare. +    if (len >= 16 && !(UNALIGNED(src) | UNALIGNED(dst))) +    { +        aligned_dst = (long *)dst; +        aligned_src = (long *)src; + +        // Copy 4X long words at a time if possible. +        while (len >= 16) +        { +            *aligned_dst++ = *aligned_src++; +            *aligned_dst++ = *aligned_src++; +            *aligned_dst++ = *aligned_src++; +            *aligned_dst++ = *aligned_src++; +            len -= 16; +        } + +        // Copy one long word at a time if possible +        while (len >= 4) +        { +            *aligned_dst++ = *aligned_src++; +            len -= 4; +        } + +        dst = (char *)aligned_dst; +        src = (char *)aligned_src; +    } + +    // Pick up any remaining bytes with a byte copier. +    while (len--) +        *dst++ = *src++; + +    return dst0; +} + +void *memset(void *m, int c, size_t n) +{ +    char *s = (char *)m; +    int count, i; +    unsigned long buffer; +    unsigned long *aligned_addr; +    unsigned char *unaligned_addr; + +    // If the size is small or m is unaligned, +    // then go to the byte copy loop. This should be rare. +    if (n >= LBLOCKSIZE && !UNALIGNED(m)) +    { +        // We know that n is large and m is word-aligned. +        aligned_addr = (unsigned long *)m; + +        // Store C into each char sized location in buffer so that +        // we can set large blocks quickly. +        c &= 0xFF; +        if (LBLOCKSIZE == 4) +        { +            buffer = (c << 8) | c; +            buffer |= (buffer << 16); +        } +        else +        { +            buffer = 0; +            for (i = 0; i < LBLOCKSIZE; i++) +                buffer = (buffer << 8) | c; +        } + +        while (n >= LBLOCKSIZE * 4) +        { +            *aligned_addr++ = buffer; +            *aligned_addr++ = buffer; +            *aligned_addr++ = buffer; +            *aligned_addr++ = buffer; +            n -= LBLOCKSIZE * 4; +        } +        while (n >= LBLOCKSIZE) +        { +            *aligned_addr++ = buffer; +            n -= LBLOCKSIZE; +        } + +        s = (char *)aligned_addr; +    } + +    // Pick up the remainder with a bytewise loop. +    while (n--) +        *s++ = (char)c; + +    return m; +} + +int strcmp(const char *s1, const char *s2) +{ +    unsigned long *a1; +    unsigned long *a2; + +    // If s1 or s2 are unaligned, then skip this and compare bytes. +    if (!(UNALIGNED(s1) | UNALIGNED(s2))) +    { +        // Compare them a word at a time. +        a1 = (unsigned long *)s1; +        a2 = (unsigned long *)s2; +        while (*a1 == *a2) +        { +            // If *a1 == *a2, and we find a null in *a1, +            // then the strings must be equal, so return zero. +            if (CONTAINSNULL(*a1)) +                return 0; + +            a1++; +            a2++; +        } + +        s1 = (char *)a1; +        s2 = (char *)a2; +    } + +    // Check the remaining few bytes. +    while (*s1 != '\0' && *s1 == *s2) +    { +        s1++; +        s2++; +    } + +    return (*(unsigned char *) s1) - (*(unsigned char *) s2); +} diff --git a/src/libs/m4a_2.c b/src/libs/m4a_2.c new file mode 100644 index 000000000..2d3c65848 --- /dev/null +++ b/src/libs/m4a_2.c @@ -0,0 +1,912 @@ +#include "gba/m4a_internal.h" + +#define BSS_CODE __attribute__((section(".bss.code"))) + +BSS_CODE ALIGNED(4) char SoundMainRAM_Buffer[0x800] = {0}; + +struct SoundInfo gSoundInfo; +struct PokemonCrySong gPokemonCrySongs[MAX_POKEMON_CRIES]; +struct MusicPlayerInfo gPokemonCryMusicPlayers[MAX_POKEMON_CRIES]; +void *gMPlayJumpTable[36]; +struct CgbChannel gCgbChans[4]; +struct MusicPlayerTrack gPokemonCryTracks[MAX_POKEMON_CRIES * 2]; +struct PokemonCrySong gPokemonCrySong; +struct MusicPlayerInfo gMPlay_BGM; +struct MusicPlayerInfo gMPlay_SE1; +struct MusicPlayerInfo gMPlay_SE2; +struct MusicPlayerInfo gMPlay_SE3; +u8 gMPlayMemAccArea[0x10]; + +u32 MidiKeyToFreq(struct WaveData *wav, u8 key, u8 fineAdjust) +{ +    u32 val1; +    u32 val2; +    u32 fineAdjustShifted = fineAdjust << 24; + +    if (key > 178) +    { +        key = 178; +        fineAdjustShifted = 255 << 24; +    } + +    val1 = gScaleTable[key]; +    val1 = gFreqTable[val1 & 0xF] >> (val1 >> 4); + +    val2 = gScaleTable[key + 1]; +    val2 = gFreqTable[val2 & 0xF] >> (val2 >> 4); + +    return umul3232H32(wav->freq, val1 + umul3232H32(val2 - val1, fineAdjustShifted)); +} + +void UnusedDummyFunc() +{ +} + +void MPlayContinue(struct MusicPlayerInfo *mplayInfo) +{ +    if (mplayInfo->ident == ID_NUMBER) +    { +        mplayInfo->ident++; +        mplayInfo->status &= ~MUSICPLAYER_STATUS_PAUSE; +        mplayInfo->ident = ID_NUMBER; +    } +} + +void MPlayFadeOut(struct MusicPlayerInfo *mplayInfo, u16 speed) +{ +    if (mplayInfo->ident == ID_NUMBER) +    { +        mplayInfo->ident++; +        mplayInfo->fadeOC = speed; +        mplayInfo->fadeOI = speed; +        mplayInfo->fadeOV = (64 << FADE_VOL_SHIFT); +        mplayInfo->ident = ID_NUMBER; +    } +} + +void m4aSoundInit(void) +{ +    s32 i; + +    CpuCopy32((void *)((s32)SoundMainRAM & ~1), SoundMainRAM_Buffer, sizeof(SoundMainRAM_Buffer)); + +    SoundInit(&gSoundInfo); +    MPlayExtender(gCgbChans); +    m4aSoundMode(SOUND_MODE_DA_BIT_8 +               | SOUND_MODE_FREQ_13379 +               | (12 << SOUND_MODE_MASVOL_SHIFT) +               | (5 << SOUND_MODE_MAXCHN_SHIFT)); + +    for (i = 0; i < NUM_MUSIC_PLAYERS; i++) +    { +        struct MusicPlayerInfo *mplayInfo = gMPlayTable[i].info; +        MPlayOpen(mplayInfo, gMPlayTable[i].track, gMPlayTable[i].unk_8); +        mplayInfo->unk_B = gMPlayTable[i].unk_A; +        mplayInfo->memAccArea = gMPlayMemAccArea; +    } + +    memcpy(&gPokemonCrySong, &gPokemonCrySongTemplate, sizeof(struct PokemonCrySong)); + +    for (i = 0; i < MAX_POKEMON_CRIES; i++) +    { +        struct MusicPlayerInfo *mplayInfo = &gPokemonCryMusicPlayers[i]; +        struct MusicPlayerTrack *track = &gPokemonCryTracks[i * 2]; +        MPlayOpen(mplayInfo, track, 2); +        track->chan = 0; +    } +} + +void m4aSoundMain(void) +{ +    SoundMain(); +} + +void m4aSongNumStart(u16 n) +{ +    const struct MusicPlayer *mplayTable = gMPlayTable; +    const struct Song *songTable = gSongTable; +    const struct Song *song = &songTable[n]; +    const struct MusicPlayer *mplay = &mplayTable[song->ms]; + +    MPlayStart(mplay->info, song->header); +} + +void m4aSongNumStartOrChange(u16 n) +{ +    const struct MusicPlayer *mplayTable = gMPlayTable; +    const struct Song *songTable = gSongTable; +    const struct Song *song = &songTable[n]; +    const struct MusicPlayer *mplay = &mplayTable[song->ms]; + +    if (mplay->info->songHeader != song->header) +    { +        MPlayStart(mplay->info, song->header); +    } +    else +    { +        if ((mplay->info->status & MUSICPLAYER_STATUS_TRACK) == 0 +         || (mplay->info->status & MUSICPLAYER_STATUS_PAUSE)) +        { +            MPlayStart(mplay->info, song->header); +        } +    } +} + +void m4aSongNumStartOrContinue(u16 n) +{ +    const struct MusicPlayer *mplayTable = gMPlayTable; +    const struct Song *songTable = gSongTable; +    const struct Song *song = &songTable[n]; +    const struct MusicPlayer *mplay = &mplayTable[song->ms]; + +    if (mplay->info->songHeader != song->header) +        MPlayStart(mplay->info, song->header); +    else if ((mplay->info->status & MUSICPLAYER_STATUS_TRACK) == 0) +        MPlayStart(mplay->info, song->header); +    else if (mplay->info->status & MUSICPLAYER_STATUS_PAUSE) +        MPlayContinue(mplay->info); +} + +void m4aSongNumStop(u16 n) +{ +    const struct MusicPlayer *mplayTable = gMPlayTable; +    const struct Song *songTable = gSongTable; +    const struct Song *song = &songTable[n]; +    const struct MusicPlayer *mplay = &mplayTable[song->ms]; + +    if (mplay->info->songHeader == song->header) +        m4aMPlayStop(mplay->info); +} + +void m4aSongNumContinue(u16 n) +{ +    const struct MusicPlayer *mplayTable = gMPlayTable; +    const struct Song *songTable = gSongTable; +    const struct Song *song = &songTable[n]; +    const struct MusicPlayer *mplay = &mplayTable[song->ms]; + +    if (mplay->info->songHeader == song->header) +        MPlayContinue(mplay->info); +} + +void m4aMPlayAllStop(void) +{ +    s32 i; + +    for (i = 0; i < NUM_MUSIC_PLAYERS; i++) +        m4aMPlayStop(gMPlayTable[i].info); + +    for (i = 0; i < MAX_POKEMON_CRIES; i++) +        m4aMPlayStop(&gPokemonCryMusicPlayers[i]); +} + +void m4aMPlayContinue(struct MusicPlayerInfo *mplayInfo) +{ +    MPlayContinue(mplayInfo); +} + +void m4aMPlayAllContinue(void) +{ +    s32 i; + +    for (i = 0; i < NUM_MUSIC_PLAYERS; i++) +        MPlayContinue(gMPlayTable[i].info); + +    for (i = 0; i < MAX_POKEMON_CRIES; i++) +        MPlayContinue(&gPokemonCryMusicPlayers[i]); +} + +void m4aMPlayFadeOut(struct MusicPlayerInfo *mplayInfo, u16 speed) +{ +    MPlayFadeOut(mplayInfo, speed); +} + +void m4aMPlayFadeOutTemporarily(struct MusicPlayerInfo *mplayInfo, u16 speed) +{ +    if (mplayInfo->ident == ID_NUMBER) +    { +        mplayInfo->ident++; +        mplayInfo->fadeOC = speed; +        mplayInfo->fadeOI = speed; +        mplayInfo->fadeOV = (64 << FADE_VOL_SHIFT) | TEMPORARY_FADE; +        mplayInfo->ident = ID_NUMBER; +    } +} + +void m4aMPlayFadeIn(struct MusicPlayerInfo *mplayInfo, u16 speed) +{ +    if (mplayInfo->ident == ID_NUMBER) +    { +        mplayInfo->ident++; +        mplayInfo->fadeOC = speed; +        mplayInfo->fadeOI = speed; +        mplayInfo->fadeOV = (0 << FADE_VOL_SHIFT) | FADE_IN; +        mplayInfo->status &= ~MUSICPLAYER_STATUS_PAUSE; +        mplayInfo->ident = ID_NUMBER; +    } +} + +void m4aMPlayImmInit(struct MusicPlayerInfo *mplayInfo) +{ +    s32 trackCount = mplayInfo->trackCount; +    struct MusicPlayerTrack *track = mplayInfo->tracks; + +    while (trackCount > 0) +    { +        if (track->flags & MPT_FLG_EXIST) +        { +            if (track->flags & MPT_FLG_START) +            { +                Clear64byte(track); +                track->flags = MPT_FLG_EXIST; +                track->bendRange = 2; +                track->volX = 64; +                track->lfoSpeed = 22; +                track->tone.type = 1; +            } +        } + +        trackCount--; +        track++; +    } +} + +void MPlayExtender(struct CgbChannel *cgbChans) +{ +    struct SoundInfo *soundInfo; +    u32 ident; + +    REG_SOUNDCNT_X = SOUND_MASTER_ENABLE +                   | SOUND_4_ON +                   | SOUND_3_ON +                   | SOUND_2_ON +                   | SOUND_1_ON; +    REG_SOUNDCNT_L = 0; // set master volume to zero +    REG_NR12 = 0x8; +    REG_NR22 = 0x8; +    REG_NR42 = 0x8; +    REG_NR14 = 0x80; +    REG_NR24 = 0x80; +    REG_NR44 = 0x80; +    REG_NR30 = 0; +    REG_NR50 = 0x77; + +    soundInfo = SOUND_INFO_PTR; + +    ident = soundInfo->ident; + +    if (ident != ID_NUMBER) +        return; + +    soundInfo->ident++; + +    gMPlayJumpTable[8] = ply_memacc; +    gMPlayJumpTable[17] = ply_lfos; +    gMPlayJumpTable[19] = ply_mod; +    gMPlayJumpTable[28] = ply_xcmd; +    gMPlayJumpTable[29] = ply_endtie; +    gMPlayJumpTable[30] = SampleFreqSet; +    gMPlayJumpTable[31] = TrackStop; +    gMPlayJumpTable[32] = FadeOutBody; +    gMPlayJumpTable[33] = TrkVolPitSet; + +    soundInfo->cgbChans = (struct CgbChannel *)cgbChans; +    soundInfo->CgbSound = CgbSound; +    soundInfo->CgbOscOff = CgbOscOff; +    soundInfo->MidiKeyToCgbFreq = MidiKeyToCgbFreq; +    soundInfo->maxLines = MAX_LINES; + +    CpuFill32(0, cgbChans, sizeof(struct CgbChannel) * 4); + +    cgbChans[0].ty = 1; +    cgbChans[0].panMask = 0x11; +    cgbChans[1].ty = 2; +    cgbChans[1].panMask = 0x22; +    cgbChans[2].ty = 3; +    cgbChans[2].panMask = 0x44; +    cgbChans[3].ty = 4; +    cgbChans[3].panMask = 0x88; + +    soundInfo->ident = ident; +} + +void MusicPlayerJumpTableCopy(void) +{ +    asm("swi 0x2A"); +} + +void ClearChain(void *x) +{ +    void (*func)(void *) = *(&gMPlayJumpTable[34]); +    func(x); +} + +void Clear64byte(void *x) +{ +    void (*func)(void *) = *(&gMPlayJumpTable[35]); +    func(x); +} + +void SoundInit(struct SoundInfo *soundInfo) +{ +    soundInfo->ident = 0; + +    if (REG_DMA1CNT & (DMA_REPEAT << 16)) +        REG_DMA1CNT = ((DMA_ENABLE | DMA_START_NOW | DMA_32BIT | DMA_SRC_INC | DMA_DEST_FIXED) << 16) | 4; + +    if (REG_DMA2CNT & (DMA_REPEAT << 16)) +        REG_DMA2CNT = ((DMA_ENABLE | DMA_START_NOW | DMA_32BIT | DMA_SRC_INC | DMA_DEST_FIXED) << 16) | 4; + +    REG_DMA1CNT_H = DMA_32BIT; +    REG_DMA2CNT_H = DMA_32BIT; +    REG_SOUNDCNT_X = SOUND_MASTER_ENABLE +                   | SOUND_4_ON +                   | SOUND_3_ON +                   | SOUND_2_ON +                   | SOUND_1_ON; +    REG_SOUNDCNT_H = SOUND_B_FIFO_RESET | SOUND_B_TIMER_0 | SOUND_B_LEFT_OUTPUT +                   | SOUND_A_FIFO_RESET | SOUND_A_TIMER_0 | SOUND_A_RIGHT_OUTPUT +                   | SOUND_ALL_MIX_FULL; +    REG_SOUNDBIAS_H = (REG_SOUNDBIAS_H & 0x3F) | 0x40; + +    REG_DMA1SAD = (s32)soundInfo->pcmBuffer; +    REG_DMA1DAD = (s32)®_FIFO_A; +    REG_DMA2SAD = (s32)soundInfo->pcmBuffer + PCM_DMA_BUF_SIZE; +    REG_DMA2DAD = (s32)®_FIFO_B; + +    SOUND_INFO_PTR = soundInfo; +    CpuFill32(0, soundInfo, sizeof(struct SoundInfo)); + +    soundInfo->maxChans = 8; +    soundInfo->masterVolume = 15; +    soundInfo->plynote = (u32)ply_note; +    soundInfo->CgbSound = DummyFunc; +    soundInfo->CgbOscOff = (void (*)(u8))DummyFunc; +    soundInfo->MidiKeyToCgbFreq = (u32 (*)(u8, u8, u8))DummyFunc; +    soundInfo->ExtVolPit = (u32)DummyFunc; + +    MPlayJumpTableCopy(gMPlayJumpTable); + +    soundInfo->MPlayJumpTable = (u32)gMPlayJumpTable; + +    SampleFreqSet(SOUND_MODE_FREQ_13379); + +    soundInfo->ident = ID_NUMBER; +} + +void SampleFreqSet(u32 freq) +{ +    struct SoundInfo *soundInfo = SOUND_INFO_PTR; + +    freq = (freq & 0xF0000) >> 16; +    soundInfo->freq = freq; +    soundInfo->pcmSamplesPerVBlank = gPcmSamplesPerVBlankTable[freq - 1]; +    soundInfo->pcmDmaPeriod = PCM_DMA_BUF_SIZE / soundInfo->pcmSamplesPerVBlank; + +    // LCD refresh rate 59.7275Hz +    soundInfo->pcmFreq = (597275 * soundInfo->pcmSamplesPerVBlank + 5000) / 10000; + +    // CPU frequency 16.78Mhz +    soundInfo->divFreq = (16777216 / soundInfo->pcmFreq + 1) >> 1; + +    // Turn off timer 0. +    REG_TM0CNT_H = 0; + +    // cycles per LCD fresh 280896 +    REG_TM0CNT_L = -(280896 / soundInfo->pcmSamplesPerVBlank); + +    m4aSoundVSyncOn(); + +    while (*(vu8 *)REG_ADDR_VCOUNT == 159) +        ; + +    while (*(vu8 *)REG_ADDR_VCOUNT != 159) +        ; + +    REG_TM0CNT_H = TIMER_ENABLE | TIMER_1CLK; +} + +void m4aSoundMode(u32 mode) +{ +    struct SoundInfo *soundInfo = SOUND_INFO_PTR; +    u32 temp; + +    if (soundInfo->ident != ID_NUMBER) +        return; + +    soundInfo->ident++; + +    temp = mode & (SOUND_MODE_REVERB_SET | SOUND_MODE_REVERB_VAL); + +    if (temp) +        soundInfo->reverb = temp & SOUND_MODE_REVERB_VAL; + +    temp = mode & SOUND_MODE_MAXCHN; + +    if (temp) +    { +        struct SoundChannel *chan; + +        soundInfo->maxChans = temp >> SOUND_MODE_MAXCHN_SHIFT; + +        temp = MAX_DIRECTSOUND_CHANNELS; +        chan = &soundInfo->chans[0]; + +        while (temp != 0) +        { +            chan->status = 0; +            temp--; +            chan++; +        } +    } + +    temp = mode & SOUND_MODE_MASVOL; + +    if (temp) +        soundInfo->masterVolume = temp >> SOUND_MODE_MASVOL_SHIFT; + +    temp = mode & SOUND_MODE_DA_BIT; + +    if (temp) +    { +        temp = (temp & 0x300000) >> 14; +        REG_SOUNDBIAS_H = (REG_SOUNDBIAS_H & 0x3F) | temp; +    } + +    temp = mode & SOUND_MODE_FREQ; + +    if (temp) +    { +        m4aSoundVSyncOff(); +        SampleFreqSet(temp); +    } + +    soundInfo->ident = ID_NUMBER; +} + +void SoundClear(void) +{ +    struct SoundInfo *soundInfo = SOUND_INFO_PTR; +    s32 i; +    void *chan; + +    if (soundInfo->ident != ID_NUMBER) +        return; + +    soundInfo->ident++; + +    i = MAX_DIRECTSOUND_CHANNELS; +    chan = &soundInfo->chans[0]; + +    while (i > 0) +    { +        ((struct SoundChannel *)chan)->status = 0; +        i--; +        chan = (void *)((s32)chan + sizeof(struct SoundChannel)); +    } + +    chan = soundInfo->cgbChans; + +    if (chan) +    { +        i = 1; + +        while (i <= 4) +        { +            soundInfo->CgbOscOff(i); +            ((struct CgbChannel *)chan)->sf = 0; +            i++; +            chan = (void *)((s32)chan + sizeof(struct CgbChannel)); +        } +    } + +    soundInfo->ident = ID_NUMBER; +} + +void m4aSoundVSyncOff(void) +{ +    struct SoundInfo *soundInfo = SOUND_INFO_PTR; + +    if (soundInfo->ident >= ID_NUMBER && soundInfo->ident <= ID_NUMBER + 1) +    { +        soundInfo->ident += 10; + +        if (REG_DMA1CNT & (DMA_REPEAT << 16)) +            REG_DMA1CNT = ((DMA_ENABLE | DMA_START_NOW | DMA_32BIT | DMA_SRC_INC | DMA_DEST_FIXED) << 16) | 4; + +        if (REG_DMA2CNT & (DMA_REPEAT << 16)) +            REG_DMA2CNT = ((DMA_ENABLE | DMA_START_NOW | DMA_32BIT | DMA_SRC_INC | DMA_DEST_FIXED) << 16) | 4; + +        REG_DMA1CNT_H = DMA_32BIT; +        REG_DMA2CNT_H = DMA_32BIT; + +        CpuFill32(0, soundInfo->pcmBuffer, sizeof(soundInfo->pcmBuffer)); +    } +} + +void m4aSoundVSyncOn(void) +{ +    struct SoundInfo *soundInfo = SOUND_INFO_PTR; +    u32 ident = soundInfo->ident; + +    if (ident == ID_NUMBER) +        return; + +    REG_DMA1CNT_H = DMA_ENABLE | DMA_START_SPECIAL | DMA_32BIT | DMA_REPEAT; +    REG_DMA2CNT_H = DMA_ENABLE | DMA_START_SPECIAL | DMA_32BIT | DMA_REPEAT; + +    soundInfo->pcmDmaCounter = 0; +    soundInfo->ident = ident - 10; +} + +void MPlayOpen(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *tracks, u8 trackCount) +{ +    struct SoundInfo *soundInfo; + +    if (trackCount == 0) +        return; + +    if (trackCount > MAX_MUSICPLAYER_TRACKS) +        trackCount = MAX_MUSICPLAYER_TRACKS; + +    soundInfo = SOUND_INFO_PTR; + +    if (soundInfo->ident != ID_NUMBER) +        return; + +    soundInfo->ident++; + +    Clear64byte(mplayInfo); + +    mplayInfo->tracks = tracks; +    mplayInfo->trackCount = trackCount; +    mplayInfo->status = MUSICPLAYER_STATUS_PAUSE; + +    while (trackCount != 0) +    { +        tracks->flags = 0; +        trackCount--; +        tracks++; +    } + +    if (soundInfo->func != 0) +    { +        mplayInfo->func = soundInfo->func; +        mplayInfo->intp = soundInfo->intp; +        soundInfo->func = 0; +    } + +    soundInfo->intp = (u32)mplayInfo; +    soundInfo->func = (u32)MPlayMain; +    soundInfo->ident = ID_NUMBER; +    mplayInfo->ident = ID_NUMBER; +} + +void MPlayStart(struct MusicPlayerInfo *mplayInfo, struct SongHeader *songHeader) +{ +    s32 i; +    u8 unk_B; +    struct MusicPlayerTrack *track; + +    if (mplayInfo->ident != ID_NUMBER) +        return; + +    unk_B = mplayInfo->unk_B; + +    if (!unk_B +        || ((!mplayInfo->songHeader || !(mplayInfo->tracks[0].flags & MPT_FLG_START)) +            && ((mplayInfo->status & MUSICPLAYER_STATUS_TRACK) == 0 +                || (mplayInfo->status & MUSICPLAYER_STATUS_PAUSE))) +        || (mplayInfo->priority <= songHeader->priority)) +    { +        mplayInfo->ident++; +        mplayInfo->status = 0; +        mplayInfo->songHeader = songHeader; +        mplayInfo->tone = songHeader->tone; +        mplayInfo->priority = songHeader->priority; +        mplayInfo->clock = 0; +        mplayInfo->tempoD = 150; +        mplayInfo->tempoI = 150; +        mplayInfo->tempoU = 0x100; +        mplayInfo->tempoC = 0; +        mplayInfo->fadeOI = 0; + +        i = 0; +        track = mplayInfo->tracks; + +        while (i < songHeader->trackCount && i < mplayInfo->trackCount) +        { +            TrackStop(mplayInfo, track); +            track->flags = MPT_FLG_EXIST | MPT_FLG_START; +            track->chan = 0; +            track->cmdPtr = songHeader->part[i]; +            i++; +            track++; +        } + +        while (i < mplayInfo->trackCount) +        { +            TrackStop(mplayInfo, track); +            track->flags = 0; +            i++; +            track++; +        } + +        if (songHeader->reverb & 0x80) +            m4aSoundMode(songHeader->reverb); + +        mplayInfo->ident = ID_NUMBER; +    } +} + +void m4aMPlayStop(struct MusicPlayerInfo *mplayInfo) +{ +    s32 i; +    struct MusicPlayerTrack *track; + +    if (mplayInfo->ident != ID_NUMBER) +        return; + +    mplayInfo->ident++; +    mplayInfo->status |= MUSICPLAYER_STATUS_PAUSE; + +    i = mplayInfo->trackCount; +    track = mplayInfo->tracks; + +    while (i > 0) +    { +        TrackStop(mplayInfo, track); +        i--; +        track++; +    } + +    mplayInfo->ident = ID_NUMBER; +} + +void FadeOutBody(struct MusicPlayerInfo *mplayInfo) +{ +    s32 i; +    struct MusicPlayerTrack *track; +    u16 fadeOI = mplayInfo->fadeOI; +    register u32 temp asm("r3"); +    register u16 mask asm("r2"); + +    if (fadeOI == 0) +        return; + +    mplayInfo->fadeOC--; + +    temp = 0xFFFF; +    mask = temp; + +    if (mplayInfo->fadeOC != 0) +        return; + +    mplayInfo->fadeOC = fadeOI; + +    if (mplayInfo->fadeOV & FADE_IN) +    { +        mplayInfo->fadeOV += (4 << FADE_VOL_SHIFT); + +        if ((u16)(mplayInfo->fadeOV & mask) >= (64 << FADE_VOL_SHIFT)) +        { +            mplayInfo->fadeOV = (64 << FADE_VOL_SHIFT); +            mplayInfo->fadeOI = 0; +        } +    } +    else +    { +        mplayInfo->fadeOV -= (4 << FADE_VOL_SHIFT); + +        if ((s16)(mplayInfo->fadeOV & mask) <= 0) +        { +            i = mplayInfo->trackCount; +            track = mplayInfo->tracks; + +            while (i > 0) +            { +                register u32 fadeOV asm("r7"); +                u32 val; + +                TrackStop(mplayInfo, track); + +                val = TEMPORARY_FADE; +                fadeOV = mplayInfo->fadeOV; +                val &= fadeOV; + +                if (!val) +                    track->flags = 0; + +                i--; +                track++; +            } + +            if (mplayInfo->fadeOV & TEMPORARY_FADE) +                mplayInfo->status |= MUSICPLAYER_STATUS_PAUSE; +            else +                mplayInfo->status = MUSICPLAYER_STATUS_PAUSE; + +            mplayInfo->fadeOI = 0; +            return; +        } +    } + +    i = mplayInfo->trackCount; +    track = mplayInfo->tracks; + +    while (i > 0) +    { +        if (track->flags & MPT_FLG_EXIST) +        { +            track->volX = (mplayInfo->fadeOV >> FADE_VOL_SHIFT); +            track->flags |= MPT_FLG_VOLCHG; +        } + +        i--; +        track++; +    } +} + +void TrkVolPitSet(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track) +{ +    if (track->flags & MPT_FLG_VOLSET) +    { +        s32 x; +        s32 y; + +        x = (u32)(track->vol * track->volX) >> 5; + +        if (track->modT == 1) +            x = (u32)(x * (track->modM + 128)) >> 7; + +        y = 2 * track->pan + track->panX; + +        if (track->modT == 2) +            y += track->modM; + +        if (y < -128) +            y = -128; +        else if (y > 127) +            y = 127; + +        track->volMR = (u32)((y + 128) * x) >> 8; +        track->volML = (u32)((127 - y) * x) >> 8; +    } + +    if (track->flags & MPT_FLG_PITSET) +    { +        s32 bend = track->bend * track->bendRange; +        register s32 x asm("r1") = track->tune; +        x += bend; +        x *= 4; +        x += (track->keyShift << 8); +        x += (track->keyShiftX << 8); +        x += track->pitX; + +        if (track->modT == 0) +            x += 16 * track->modM; + +        track->keyM = x >> 8; +        track->pitM = x; +    } + +    track->flags &= ~(MPT_FLG_PITSET | MPT_FLG_VOLSET); +} + +u32 MidiKeyToCgbFreq(u8 chanNum, u8 key, u8 fineAdjust) +{ +    if (chanNum == 4) +    { +        if (key <= 20) +        { +            key = 0; +        } +        else +        { +            key -= 21; +            if (key > 59) +                key = 59; +        } + +        return gNoiseTable[key]; +    } +    else +    { +        s32 val1; +        s32 val2; + +        if (key <= 35) +        { +            fineAdjust = 0; +            key = 0; +        } +        else +        { +            key -= 36; +            if (key > 130) +            { +                key = 130; +                fineAdjust = 255; +            } +        } + +        val1 = gCgbScaleTable[key]; +        val1 = gCgbFreqTable[val1 & 0xF] >> (val1 >> 4); + +        val2 = gCgbScaleTable[key + 1]; +        val2 = gCgbFreqTable[val2 & 0xF] >> (val2 >> 4); + +        return val1 + ((fineAdjust * (val2 - val1)) >> 8) + 2048; +    } +} + +void CgbOscOff(u8 chanNum) +{ +    switch (chanNum) +    { +    case 1: +        REG_NR12 = 8; +        REG_NR14 = 0x80; +        break; +    case 2: +        REG_NR22 = 8; +        REG_NR24 = 0x80; +        break; +    case 3: +        REG_NR30 = 0; +        break; +    default: +        REG_NR42 = 8; +        REG_NR44 = 0x80; +    } +} + +static inline int CgbPan(struct CgbChannel *chan) +{ +    u32 rightVolume = chan->rightVolume; +    u32 leftVolume = chan->leftVolume; + +    if ((rightVolume = (u8)rightVolume) >= (leftVolume = (u8)leftVolume)) +    { +        if (rightVolume / 2 >= leftVolume) +        { +            chan->pan = 0x0F; +            return 1; +        } +    } +    else +    { +        if (leftVolume / 2 >= rightVolume) +        { +            chan->pan = 0xF0; +            return 1; +        } +    } + +    return 0; +} + +void CgbModVol(struct CgbChannel *chan) +{ +    struct SoundInfo *soundInfo = SOUND_INFO_PTR; + +    if ((soundInfo->mode & 1) || !CgbPan(chan)) +    { +        chan->pan = 0xFF; +        chan->eg = (u32)(chan->rightVolume + chan->leftVolume) >> 4; +    } +    else +    { +        // Force chan->rightVolume and chan->leftVolume to be read from memory again, +        // even though there is no reason to do so. +        // The command line option "-fno-gcse" achieves the same result as this. +        asm("" : : : "memory"); + +        chan->eg = (u32)(chan->rightVolume + chan->leftVolume) >> 4; +        if (chan->eg > 15) +            chan->eg = 15; +    } + +    chan->sg = (chan->eg * chan->su + 15) >> 4; +    chan->pan &= chan->panMask; +} diff --git a/src/libs/m4a_4.c b/src/libs/m4a_4.c new file mode 100644 index 000000000..2e1d140b4 --- /dev/null +++ b/src/libs/m4a_4.c @@ -0,0 +1,545 @@ +#include "gba/m4a_internal.h" + +void m4aMPlayTempoControl(struct MusicPlayerInfo *mplayInfo, u16 tempo) +{ +    if (mplayInfo->ident == ID_NUMBER) +    { +        mplayInfo->ident++; +        mplayInfo->tempoU = tempo; +        mplayInfo->tempoI = (mplayInfo->tempoD * mplayInfo->tempoU) >> 8; +        mplayInfo->ident = ID_NUMBER; +    } +} + +void m4aMPlayVolumeControl(struct MusicPlayerInfo *mplayInfo, u16 trackBits, u16 volume) +{ +    s32 i; +    u32 bit; +    struct MusicPlayerTrack *track; + +    if (mplayInfo->ident != ID_NUMBER) +        return; + +    mplayInfo->ident++; + +    i = mplayInfo->trackCount; +    track = mplayInfo->tracks; +    bit = 1; + +    while (i > 0) +    { +        if (trackBits & bit) +        { +            if (track->flags & MPT_FLG_EXIST) +            { +                track->volX = volume / 4; +                track->flags |= MPT_FLG_VOLCHG; +            } +        } + +        i--; +        track++; +        bit <<= 1; +    } + +    mplayInfo->ident = ID_NUMBER; +} + +void m4aMPlayPitchControl(struct MusicPlayerInfo *mplayInfo, u16 trackBits, s16 pitch) +{ +    s32 i; +    u32 bit; +    struct MusicPlayerTrack *track; + +    if (mplayInfo->ident != ID_NUMBER) +        return; + +    mplayInfo->ident++; + +    i = mplayInfo->trackCount; +    track = mplayInfo->tracks; +    bit = 1; + +    while (i > 0) +    { +        if (trackBits & bit) +        { +            if (track->flags & MPT_FLG_EXIST) +            { +                track->keyShiftX = (s16)pitch >> 8; +                track->pitX = pitch; +                track->flags |= MPT_FLG_PITCHG; +            } +        } + +        i--; +        track++; +        bit <<= 1; +    } + +    mplayInfo->ident = ID_NUMBER; +} + +void m4aMPlayPanpotControl(struct MusicPlayerInfo *mplayInfo, u16 trackBits, s8 pan) +{ +    s32 i; +    u32 bit; +    struct MusicPlayerTrack *track; + +    if (mplayInfo->ident != ID_NUMBER) +        return; + +    mplayInfo->ident++; + +    i = mplayInfo->trackCount; +    track = mplayInfo->tracks; +    bit = 1; + +    while (i > 0) +    { +        if (trackBits & bit) +        { +            if (track->flags & MPT_FLG_EXIST) +            { +                track->panX = pan; +                track->flags |= MPT_FLG_VOLCHG; +            } +        } + +        i--; +        track++; +        bit <<= 1; +    } + +    mplayInfo->ident = ID_NUMBER; +} + +void ClearModM(struct MusicPlayerTrack *track) +{ +    track->lfoSpeedC = 0; +    track->modM = 0; + +    if (track->modT == 0) +        track->flags |= MPT_FLG_PITCHG; +    else +        track->flags |= MPT_FLG_VOLCHG; +} + +void m4aMPlayModDepthSet(struct MusicPlayerInfo *mplayInfo, u16 trackBits, u8 modDepth) +{ +    s32 i; +    u32 bit; +    struct MusicPlayerTrack *track; + +    if (mplayInfo->ident != ID_NUMBER) +        return; + +    mplayInfo->ident++; + +    i = mplayInfo->trackCount; +    track = mplayInfo->tracks; +    bit = 1; + +    while (i > 0) +    { +        if (trackBits & bit) +        { +            if (track->flags & MPT_FLG_EXIST) +            { +                track->mod = modDepth; + +                if (!track->mod) +                    ClearModM(track); +            } +        } + +        i--; +        track++; +        bit <<= 1; +    } + +    mplayInfo->ident = ID_NUMBER; +} + +void m4aMPlayLFOSpeedSet(struct MusicPlayerInfo *mplayInfo, u16 trackBits, u8 lfoSpeed) +{ +    s32 i; +    u32 bit; +    struct MusicPlayerTrack *track; + +    if (mplayInfo->ident != ID_NUMBER) +        return; + +    mplayInfo->ident++; + +    i = mplayInfo->trackCount; +    track = mplayInfo->tracks; +    bit = 1; + +    while (i > 0) +    { +        if (trackBits & bit) +        { +            if (track->flags & MPT_FLG_EXIST) +            { +                track->lfoSpeed = lfoSpeed; + +                if (!track->lfoSpeed) +                    ClearModM(track); +            } +        } + +        i--; +        track++; +        bit <<= 1; +    } + +    mplayInfo->ident = ID_NUMBER; +} + +#define MEMACC_COND_JUMP(cond) \ +if (cond)                      \ +    goto cond_true;            \ +else                           \ +    goto cond_false;           \ + +void ply_memacc(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track) +{ +    u32 op; +    u8 *addr; +    u8 data; + +    op = *track->cmdPtr; +    track->cmdPtr++; + +    addr = mplayInfo->memAccArea + *track->cmdPtr; +    track->cmdPtr++; + +    data = *track->cmdPtr; +    track->cmdPtr++; + +    switch (op) +    { +    case 0: +        *addr = data; +        return; +    case 1: +        *addr += data; +        return; +    case 2: +        *addr -= data; +        return; +    case 3: +        *addr = mplayInfo->memAccArea[data]; +        return; +    case 4: +        *addr += mplayInfo->memAccArea[data]; +        return; +    case 5: +        *addr -= mplayInfo->memAccArea[data]; +        return; +    case 6: +        MEMACC_COND_JUMP(*addr == data) +        return; +    case 7: +        MEMACC_COND_JUMP(*addr != data) +        return; +    case 8: +        MEMACC_COND_JUMP(*addr > data) +        return; +    case 9: +        MEMACC_COND_JUMP(*addr >= data) +        return; +    case 10: +        MEMACC_COND_JUMP(*addr <= data) +        return; +    case 11: +        MEMACC_COND_JUMP(*addr < data) +        return; +    case 12: +        MEMACC_COND_JUMP(*addr == mplayInfo->memAccArea[data]) +        return; +    case 13: +        MEMACC_COND_JUMP(*addr != mplayInfo->memAccArea[data]) +        return; +    case 14: +        MEMACC_COND_JUMP(*addr > mplayInfo->memAccArea[data]) +        return; +    case 15: +        MEMACC_COND_JUMP(*addr >= mplayInfo->memAccArea[data]) +        return; +    case 16: +        MEMACC_COND_JUMP(*addr <= mplayInfo->memAccArea[data]) +        return; +    case 17: +        MEMACC_COND_JUMP(*addr < mplayInfo->memAccArea[data]) +        return; +    default: +        return; +    } + +cond_true: +    { +        void (*func)(struct MusicPlayerInfo *, struct MusicPlayerTrack *) = *(&gMPlayJumpTable[1]); +        func(mplayInfo, track); +        return; +    } + +cond_false: +    track->cmdPtr += 4; +} + +void ply_xcmd(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track) +{ +    u32 n = *track->cmdPtr; +    track->cmdPtr++; + +    gXcmdTable[n](mplayInfo, track); +} + +void ply_xxx(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track) +{ +    void (*func)(struct MusicPlayerInfo *, struct MusicPlayerTrack *) = *(&gMPlayJumpTable[0]); +    func(mplayInfo, track); +} + +#define READ_XCMD_BYTE(var, n)       \ +{                                    \ +    u32 byte = track->cmdPtr[(n)]; \ +    byte <<= n * 8;                  \ +    (var) &= ~(0xFF << (n * 8));     \ +    (var) |= byte;                   \ +} + +void ply_xwave(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track) +{ +    u32 wav; + +    READ_XCMD_BYTE(wav, 0) // UB: uninitialized variable +    READ_XCMD_BYTE(wav, 1) +    READ_XCMD_BYTE(wav, 2) +    READ_XCMD_BYTE(wav, 3) + +    track->tone.wav = (struct WaveData *)wav; +    track->cmdPtr += 4; +} + +void ply_xtype(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track) +{ +    track->tone.type = *track->cmdPtr; +    track->cmdPtr++; +} + +void ply_xatta(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track) +{ +    track->tone.attack = *track->cmdPtr; +    track->cmdPtr++; +} + +void ply_xdeca(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track) +{ +    track->tone.decay = *track->cmdPtr; +    track->cmdPtr++; +} + +void ply_xsust(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track) +{ +    track->tone.sustain = *track->cmdPtr; +    track->cmdPtr++; +} + +void ply_xrele(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track) +{ +    track->tone.release = *track->cmdPtr; +    track->cmdPtr++; +} + +void ply_xiecv(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track) +{ +    track->echoVolume = *track->cmdPtr; +    track->cmdPtr++; +} + +void ply_xiecl(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track) +{ +    track->echoLength = *track->cmdPtr; +    track->cmdPtr++; +} + +void ply_xleng(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track) +{ +    track->tone.length = *track->cmdPtr; +    track->cmdPtr++; +} + +void ply_xswee(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track) +{ +    track->tone.pan_sweep = *track->cmdPtr; +    track->cmdPtr++; +} + +void ply_xcmd_0C(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track) +{ +    u32 unk; + +    READ_XCMD_BYTE(unk, 0) // UB: uninitialized variable +    READ_XCMD_BYTE(unk, 1) + +    if (track->unk_3A < (u16)unk) +    { +        track->unk_3A++; +        track->cmdPtr -= 2; +        track->wait = 1; +    } +    else +    { +        track->unk_3A = 0; +        track->cmdPtr += 2; +    } +} + +void ply_xcmd_0D(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track) +{ +    u32 unk; + +    READ_XCMD_BYTE(unk, 0) // UB: uninitialized variable +    READ_XCMD_BYTE(unk, 1) +    READ_XCMD_BYTE(unk, 2) +    READ_XCMD_BYTE(unk, 3) + +    track->unk_3C = unk; +    track->cmdPtr += 4; +} + +void DummyFunc(void) +{ +} + +struct MusicPlayerInfo *SetPokemonCryTone(struct ToneData *tone) +{ +    u32 maxClock = 0; +    s32 maxClockIndex = 0; +    s32 i; +    struct MusicPlayerInfo *mplayInfo; + +    for (i = 0; i < MAX_POKEMON_CRIES; i++) +    { +        struct MusicPlayerTrack *track = &gPokemonCryTracks[i * 2]; + +        if (!track->flags && (!track->chan || track->chan->track != track)) +            goto start_song; + +        if (maxClock < gPokemonCryMusicPlayers[i].clock) +        { +            maxClock = gPokemonCryMusicPlayers[i].clock; +            maxClockIndex = i; +        } +    } + +    i = maxClockIndex; + +start_song: +    mplayInfo = &gPokemonCryMusicPlayers[i]; +    mplayInfo->ident++; + +#define CRY ((s32)&gPokemonCrySongs + i * sizeof(struct PokemonCrySong)) +#define CRY_OFS(field) offsetof(struct PokemonCrySong, field) + +    memcpy((void *)CRY, &gPokemonCrySong, sizeof(struct PokemonCrySong)); + +    *(u32 *)(CRY + CRY_OFS(tone)) = (u32)tone; +    *(u32 *)(CRY + CRY_OFS(part)) = CRY + CRY_OFS(part0); +    *(u32 *)(CRY + CRY_OFS(part) + 4) = CRY + CRY_OFS(part1); +    *(u32 *)(CRY + CRY_OFS(gotoTarget)) = CRY + CRY_OFS(cont); + +#undef CRY_OFS +#undef CRY + +    mplayInfo->ident = ID_NUMBER; + +    MPlayStart(mplayInfo, (struct SongHeader *)(&gPokemonCrySongs[i])); + +    return mplayInfo; +} + +void SetPokemonCryVolume(u8 val) +{ +    gPokemonCrySong.volumeValue = val & 0x7F; +} + +void SetPokemonCryPanpot(s8 val) +{ +    gPokemonCrySong.panValue = (val + C_V) & 0x7F; +} + +void SetPokemonCryPitch(s16 val) +{ +    s16 b = val + 0x80; +    u8 a = gPokemonCrySong.tuneValue2 - gPokemonCrySong.tuneValue; +    gPokemonCrySong.tieKeyValue = (b >> 8) & 0x7F; +    gPokemonCrySong.tuneValue = (b >> 1) & 0x7F; +    gPokemonCrySong.tuneValue2 = (a + ((b >> 1) & 0x7F)) & 0x7F; +} + +void SetPokemonCryLength(u16 val) +{ +    gPokemonCrySong.unkCmd0CParam = val; +} + +void SetPokemonCryRelease(u8 val) +{ +    gPokemonCrySong.releaseValue = val; +} + +void SetPokemonCryProgress(u32 val) +{ +    gPokemonCrySong.unkCmd0DParam = val; +} + +int IsPokemonCryPlaying(struct MusicPlayerInfo *mplayInfo) +{ +    struct MusicPlayerTrack *track = mplayInfo->tracks; + +    if (track->chan && track->chan->track == track) +        return 1; +    else +        return 0; +} + +void SetPokemonCryChorus(s8 val) +{ +    if (val) +    { +        gPokemonCrySong.trackCount = 2; +        gPokemonCrySong.tuneValue2 = (val + gPokemonCrySong.tuneValue) & 0x7F; +    } +    else +    { +        gPokemonCrySong.trackCount = 1; +    } +} + +void SetPokemonCryStereo(u32 val) +{ +    struct SoundInfo *soundInfo = SOUND_INFO_PTR; + +    if (val) +    { +        REG_SOUNDCNT_H = SOUND_B_TIMER_0 | SOUND_B_LEFT_OUTPUT +                       | SOUND_A_TIMER_0 | SOUND_A_RIGHT_OUTPUT +                       | SOUND_ALL_MIX_FULL; +        soundInfo->mode &= ~1; +    } +    else +    { +        REG_SOUNDCNT_H = SOUND_B_TIMER_0 | SOUND_B_LEFT_OUTPUT | SOUND_B_RIGHT_OUTPUT +                       | SOUND_A_TIMER_0 | SOUND_A_LEFT_OUTPUT | SOUND_A_RIGHT_OUTPUT +                       | SOUND_B_MIX_HALF | SOUND_A_MIX_HALF | SOUND_CGB_MIX_FULL; +        soundInfo->mode |= 1; +    } +} + +void SetPokemonCryPriority(u8 val) +{ +    gPokemonCrySong.priority = val; +} diff --git a/src/libs/m4a_tables.c b/src/libs/m4a_tables.c new file mode 100644 index 000000000..91f00a31d --- /dev/null +++ b/src/libs/m4a_tables.c @@ -0,0 +1,307 @@ +#include "gba/m4a_internal.h" + +// Some of these functions have different signatures, so we need to make this +// an array of void pointers or a struct. It's simpler to just make it an array +// for now. +void * const gMPlayJumpTableTemplate[] = +{ +    ply_fine, +    ply_goto, +    ply_patt, +    ply_pend, +    ply_rept, +    ply_fine, +    ply_fine, +    ply_fine, +    ply_fine, +    ply_prio, +    ply_tempo, +    ply_keysh, +    ply_voice, +    ply_vol, +    ply_pan, +    ply_bend, +    ply_bendr, +    ply_lfos, +    ply_lfodl, +    ply_mod, +    ply_modt, +    ply_fine, +    ply_fine, +    ply_tune, +    ply_fine, +    ply_fine, +    ply_fine, +    ply_port, +    ply_fine, +    ply_endtie, +    SampleFreqSet, +    TrackStop, +    FadeOutBody, +    TrkVolPitSet, +    RealClearChain, +    SoundMainBTM, +}; + +// This is a table of deltas between sample values in compressed PCM data. +const s8 gDeltaEncodingTable[] = +{ +      0, +      1, +      4, +      9, +     16, +     25, +     36, +     49, +    -64, +    -49, +    -36, +    -25, +    -16, +     -9, +     -4, +     -1, +}; + +const u8 gScaleTable[] = +{ +    0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, +    0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, +    0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, +    0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, +    0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, +    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, +    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, +    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, +    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, +    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, +    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, +    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, +    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, +    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, +    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, +}; + +const u32 gFreqTable[] = +{ +    2147483648u, +    2275179671u, +    2410468894u, +    2553802834u, +    2705659852u, +    2866546760u, +    3037000500u, +    3217589947u, +    3408917802u, +    3611622603u, +    3826380858u, +    4053909305u, +}; + +const u16 gPcmSamplesPerVBlankTable[] = +{ +    96, +    132, +    176, +    224, +    264, +    304, +    352, +    448, +    528, +    608, +    672, +    704, +}; + +const u8 gCgbScaleTable[] = +{ +    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, +    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, +    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, +    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, +    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, +    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, +    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, +    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, +    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, +    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, +    0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, +}; + +const s16 gCgbFreqTable[] = +{ +    -2004, +    -1891, +    -1785, +    -1685, +    -1591, +    -1501, +    -1417, +    -1337, +    -1262, +    -1192, +    -1125, +    -1062, +}; + +const u8 gNoiseTable[] = +{ +    0xD7, 0xD6, 0xD5, 0xD4, +    0xC7, 0xC6, 0xC5, 0xC4, +    0xB7, 0xB6, 0xB5, 0xB4, +    0xA7, 0xA6, 0xA5, 0xA4, +    0x97, 0x96, 0x95, 0x94, +    0x87, 0x86, 0x85, 0x84, +    0x77, 0x76, 0x75, 0x74, +    0x67, 0x66, 0x65, 0x64, +    0x57, 0x56, 0x55, 0x54, +    0x47, 0x46, 0x45, 0x44, +    0x37, 0x36, 0x35, 0x34, +    0x27, 0x26, 0x25, 0x24, +    0x17, 0x16, 0x15, 0x14, +    0x07, 0x06, 0x05, 0x04, +    0x03, 0x02, 0x01, 0x00, +}; + +const u8 gCgb3Vol[] = +{ +    0x00, 0x00, +    0x60, 0x60, 0x60, 0x60, +    0x40, 0x40, 0x40, 0x40, +    0x80, 0x80, 0x80, 0x80, +    0x20, 0x20, +}; + +const u8 gClockTable[] = +{ +    0x00, +    0x01, +    0x02, +    0x03, +    0x04, +    0x05, +    0x06, +    0x07, +    0x08, +    0x09, +    0x0A, +    0x0B, +    0x0C, +    0x0D, +    0x0E, +    0x0F, +    0x10, +    0x11, +    0x12, +    0x13, +    0x14, +    0x15, +    0x16, +    0x17, +    0x18, +    0x1C, +    0x1E, +    0x20, +    0x24, +    0x28, +    0x2A, +    0x2C, +    0x30, +    0x34, +    0x36, +    0x38, +    0x3C, +    0x40, +    0x42, +    0x44, +    0x48, +    0x4C, +    0x4E, +    0x50, +    0x54, +    0x58, +    0x5A, +    0x5C, +    0x60, +}; + +#define FINE   0xb1 +#define GOTO   0xb2 +#define PATT   0xb3 +#define PEND   0xb4 +#define REPT   0xb5 +#define MEMACC 0xb9 +#define PRIO   0xba +#define TEMPO  0xbb +#define KEYSH  0xbc +#define VOICE  0xbd +#define VOL    0xbe +#define PAN    0xbf +#define BEND   0xc0 +#define BENDR  0xc1 +#define LFOS   0xc2 +#define LFODL  0xc3 +#define MOD    0xc4 +#define MODT   0xc5 +#define TUNE   0xc8 + +#define XCMD   0xcd +#define xRELE  0x07 +#define xIECV  0x08 +#define xIECL  0x09 + +#define EOT    0xce +#define TIE    0xcf + +const struct PokemonCrySong gPokemonCrySongTemplate = +{ +    1, // track count +    0, // block count +    255, // priority +    0, // reverb +    (struct ToneData *)&voicegroup_pokemon_cry, +    NULL, +    NULL, +    0, +    TUNE, // part 0 +    C_V, // TUNE value +    GOTO, +    0, // GOTO target address +    TUNE, // part 1 +    C_V + 16, // TUNE value +    {VOICE, 0}, // part 0 jumps here with GOTO +    VOL, +    127, // volume +    {XCMD, 0x0D}, +    0, // unk value +    {XCMD, xRELE}, +    0, // release +    PAN, +    C_V, // PAN value +    TIE, +    60, // TIE key (default is Cn3) +    127, // TIE velocity +    {XCMD, 0x0C}, +    60, // unk value +    {EOT, FINE} // end +}; + +const XcmdFunc gXcmdTable[] = +{ +    ply_xxx, +    ply_xwave, +    ply_xtype, +    ply_xxx, +    ply_xatta, +    ply_xdeca, +    ply_xsust, +    ply_xrele, +    ply_xiecv, +    ply_xiecl, +    ply_xleng, +    ply_xswee, +    ply_xcmd_0C, +    ply_xcmd_0D, +}; diff --git a/src/libs/siirtc.c b/src/libs/siirtc.c new file mode 100644 index 000000000..965a068f1 --- /dev/null +++ b/src/libs/siirtc.c @@ -0,0 +1,432 @@ +// Ruby/Sapphire/Emerald cartridges contain a Seiko Instruments Inc. (SII) +// S-3511A real-time clock (RTC). This library ("SIIRTC_V001") is for +// communicating with the RTC. + +#include "gba/gba.h" +#include "siirtc.h" + +#define STATUS_INTFE  0x02 // frequency interrupt enable +#define STATUS_INTME  0x08 // per-minute interrupt enable +#define STATUS_INTAE  0x20 // alarm interrupt enable +#define STATUS_24HOUR 0x40 // 0: 12-hour mode, 1: 24-hour mode +#define STATUS_POWER  0x80 // power on or power failure occurred + +#define TEST_MODE 0x80 // flag in the "second" byte + +#define ALARM_AM 0x00 +#define ALARM_PM 0x80 + +#define OFFSET_YEAR         offsetof(struct SiiRtcInfo, year) +#define OFFSET_MONTH        offsetof(struct SiiRtcInfo, month) +#define OFFSET_DAY          offsetof(struct SiiRtcInfo, day) +#define OFFSET_DAY_OF_WEEK  offsetof(struct SiiRtcInfo, dayOfWeek) +#define OFFSET_HOUR         offsetof(struct SiiRtcInfo, hour) +#define OFFSET_MINUTE       offsetof(struct SiiRtcInfo, minute) +#define OFFSET_SECOND       offsetof(struct SiiRtcInfo, second) +#define OFFSET_STATUS       offsetof(struct SiiRtcInfo, status) +#define OFFSET_ALARM_HOUR   offsetof(struct SiiRtcInfo, alarmHour) +#define OFFSET_ALARM_MINUTE offsetof(struct SiiRtcInfo, alarmMinute) + +#define INFO_BUF(info, index) (*((u8 *)(info) + (index))) + +#define DATETIME_BUF(info, index) INFO_BUF(info, OFFSET_YEAR + index) +#define DATETIME_BUF_LEN (OFFSET_SECOND - OFFSET_YEAR + 1) + +#define TIME_BUF(info, index) INFO_BUF(info, OFFSET_HOUR + index) +#define TIME_BUF_LEN (OFFSET_SECOND - OFFSET_HOUR + 1) + +#define WR 0 // command for writing data +#define RD 1 // command for reading data + +#define CMD(n) (0x60 | (n << 1)) + +#define CMD_RESET    CMD(0) +#define CMD_STATUS   CMD(1) +#define CMD_DATETIME CMD(2) +#define CMD_TIME     CMD(3) +#define CMD_ALARM    CMD(4) + +#define GPIO_PORT_DATA        (*(vu16 *)0x80000C4) +#define GPIO_PORT_DIRECTION   (*(vu16 *)0x80000C6) +#define GPIO_PORT_READ_ENABLE (*(vu16 *)0x80000C8) + +extern vu16 GPIOPortDirection; + +static u16 sDummy; // unused variable +static bool8 sLocked; + +static int WriteCommand(u8 value); +static int WriteData(u8 value); +static u8 ReadData(); +static void EnableGpioPortRead(); +static void DisableGpioPortRead(); + +static const char AgbLibRtcVersion[] = "SIIRTC_V001"; + +void SiiRtcUnprotect() +{ +    EnableGpioPortRead(); +    sLocked = FALSE; +} + +void SiiRtcProtect() +{ +    DisableGpioPortRead(); +    sLocked = TRUE; +} + +u8 SiiRtcProbe() +{ +    u8 errorCode; +    struct SiiRtcInfo rtc; + +    if (!SiiRtcGetStatus(&rtc)) +        return 0; + +    errorCode = 0; + +    if ((rtc.status & (SIIRTCINFO_POWER | SIIRTCINFO_24HOUR)) == SIIRTCINFO_POWER +     || (rtc.status & (SIIRTCINFO_POWER | SIIRTCINFO_24HOUR)) == 0) +    { +        // The RTC is in 12-hour mode. Reset it and switch to 24-hour mode. + +        // Note that the conditions are redundant and equivalent to simply +        // "(rtc.status & SIIRTCINFO_24HOUR) == 0". It's possible that this +        // was also intended to handle resetting the clock after power failure +        // but a mistake was made. + +        if (!SiiRtcReset()) +            return 0; + +        errorCode++; +    } + +    SiiRtcGetTime(&rtc); + +    if (rtc.second & TEST_MODE) +    { +        // The RTC is in test mode. Reset it to leave test mode. + +        if (!SiiRtcReset()) +            return (errorCode << 4) & 0xF0; + +        errorCode++; +    } + +    return (errorCode << 4) | 1; +} + +bool8 SiiRtcReset() +{ +    u8 result; +    struct SiiRtcInfo rtc; + +    if (sLocked == TRUE) +        return FALSE; + +    sLocked = TRUE; + +    GPIO_PORT_DATA = 1; +    GPIO_PORT_DATA = 5; + +    GPIO_PORT_DIRECTION = 7; + +    WriteCommand(CMD_RESET | WR); + +    GPIO_PORT_DATA = 1; +    GPIO_PORT_DATA = 1; + +    sLocked = FALSE; + +    rtc.status = SIIRTCINFO_24HOUR; + +    result = SiiRtcSetStatus(&rtc); + +    return result; +} + +bool8 SiiRtcGetStatus(struct SiiRtcInfo *rtc) +{ +    u8 statusData; + +    if (sLocked == TRUE) +        return FALSE; + +    sLocked = TRUE; + +    GPIO_PORT_DATA = 1; +    GPIO_PORT_DATA = 5; + +    GPIO_PORT_DIRECTION = 7; + +    WriteCommand(CMD_STATUS | RD); + +    GPIO_PORT_DIRECTION = 5; + +    statusData = ReadData(); + +    rtc->status = (statusData & (STATUS_POWER | STATUS_24HOUR)) +                | ((statusData & STATUS_INTAE) >> 3) +                | ((statusData & STATUS_INTME) >> 2) +                | ((statusData & STATUS_INTFE) >> 1); + +    GPIO_PORT_DATA = 1; +    GPIO_PORT_DATA = 1; + +    sLocked = FALSE; + +    return TRUE; +} + +bool8 SiiRtcSetStatus(struct SiiRtcInfo *rtc) +{ +    u8 statusData; + +    if (sLocked == TRUE) +        return FALSE; + +    sLocked = TRUE; + +    GPIO_PORT_DATA = 1; +    GPIO_PORT_DATA = 5; + +    statusData = STATUS_24HOUR +               | ((rtc->status & SIIRTCINFO_INTAE) << 3) +               | ((rtc->status & SIIRTCINFO_INTME) << 2) +               | ((rtc->status & SIIRTCINFO_INTFE) << 1); + +    GPIO_PORT_DIRECTION = 7; + +    WriteCommand(CMD_STATUS | WR); + +    WriteData(statusData); + +    GPIO_PORT_DATA = 1; +    GPIO_PORT_DATA = 1; + +    sLocked = FALSE; + +    return TRUE; +} + +bool8 SiiRtcGetDateTime(struct SiiRtcInfo *rtc) +{ +    u8 i; + +    if (sLocked == TRUE) +        return FALSE; + +    sLocked = TRUE; + +    GPIO_PORT_DATA = 1; +    GPIO_PORT_DATA = 5; + +    GPIO_PORT_DIRECTION = 7; + +    WriteCommand(CMD_DATETIME | RD); + +    GPIO_PORT_DIRECTION = 5; + +    for (i = 0; i < DATETIME_BUF_LEN; i++) +        DATETIME_BUF(rtc, i) = ReadData(); + +    INFO_BUF(rtc, OFFSET_HOUR) &= 0x7F; + +    GPIO_PORT_DATA = 1; +    GPIO_PORT_DATA = 1; + +    sLocked = FALSE; + +    return TRUE; +} + +bool8 SiiRtcSetDateTime(struct SiiRtcInfo *rtc) +{ +    u8 i; + +    if (sLocked == TRUE) +        return FALSE; + +    sLocked = TRUE; + +    GPIO_PORT_DATA = 1; +    GPIO_PORT_DATA = 5; + +    GPIO_PORT_DIRECTION = 7; + +    WriteCommand(CMD_DATETIME | WR); + +    for (i = 0; i < DATETIME_BUF_LEN; i++) +        WriteData(DATETIME_BUF(rtc, i)); + +    GPIO_PORT_DATA = 1; +    GPIO_PORT_DATA = 1; + +    sLocked = FALSE; + +    return TRUE; +} + +bool8 SiiRtcGetTime(struct SiiRtcInfo *rtc) +{ +    u8 i; + +    if (sLocked == TRUE) +        return FALSE; + +    sLocked = TRUE; + +    GPIO_PORT_DATA = 1; +    GPIO_PORT_DATA = 5; + +    GPIO_PORT_DIRECTION = 7; + +    WriteCommand(CMD_TIME | RD); + +    GPIO_PORT_DIRECTION = 5; + +    for (i = 0; i < TIME_BUF_LEN; i++) +        TIME_BUF(rtc, i) = ReadData(); + +    INFO_BUF(rtc, OFFSET_HOUR) &= 0x7F; + +    GPIO_PORT_DATA = 1; +    GPIO_PORT_DATA = 1; + +    sLocked = FALSE; + +    return TRUE; +} + +bool8 SiiRtcSetTime(struct SiiRtcInfo *rtc) +{ +    u8 i; + +    if (sLocked == TRUE) +        return FALSE; + +    sLocked = TRUE; + +    GPIO_PORT_DATA = 1; +    GPIO_PORT_DATA = 5; + +    GPIO_PORT_DIRECTION = 7; + +    WriteCommand(CMD_TIME | WR); + +    for (i = 0; i < TIME_BUF_LEN; i++) +        WriteData(TIME_BUF(rtc, i)); + +    GPIO_PORT_DATA = 1; +    GPIO_PORT_DATA = 1; + +    sLocked = FALSE; + +    return TRUE; +} + +bool8 SiiRtcSetAlarm(struct SiiRtcInfo *rtc) +{ +    u8 i; +    u8 alarmData[2]; + +    if (sLocked == TRUE) +        return FALSE; + +    sLocked = TRUE; + +    // Decode BCD. +    alarmData[0] = (rtc->alarmHour & 0xF) + 10 * ((rtc->alarmHour >> 4) & 0xF); + +    // The AM/PM flag must be set correctly even in 24-hour mode. + +    if (alarmData[0] < 12) +        alarmData[0] = rtc->alarmHour | ALARM_AM; +    else +        alarmData[0] = rtc->alarmHour | ALARM_PM; + +    alarmData[1] = rtc->alarmMinute; + +    GPIO_PORT_DATA = 1; +    GPIO_PORT_DATA = 5; + +    GPIOPortDirection = 7; // Why is this the only instance that uses a symbol? + +    WriteCommand(CMD_ALARM | WR); + +    for (i = 0; i < 2; i++) +        WriteData(alarmData[i]); + +    GPIO_PORT_DATA = 1; +    GPIO_PORT_DATA = 1; + +    sLocked = FALSE; + +    return TRUE; +} + +static int WriteCommand(u8 value) +{ +    u8 i; +    u8 temp; + +    for (i = 0; i < 8; i++) +    { +        temp = ((value >> (7 - i)) & 1); +        GPIO_PORT_DATA = (temp << 1) | 4; +        GPIO_PORT_DATA = (temp << 1) | 4; +        GPIO_PORT_DATA = (temp << 1) | 4; +        GPIO_PORT_DATA = (temp << 1) | 5; +    } + +    // control reaches end of non-void function +} + +static int WriteData(u8 value) +{ +    u8 i; +    u8 temp; + +    for (i = 0; i < 8; i++) +    { +        temp = ((value >> i) & 1); +        GPIO_PORT_DATA = (temp << 1) | 4; +        GPIO_PORT_DATA = (temp << 1) | 4; +        GPIO_PORT_DATA = (temp << 1) | 4; +        GPIO_PORT_DATA = (temp << 1) | 5; +    } + +    // control reaches end of non-void function +} + +static u8 ReadData() +{ +    u8 i; +    u8 temp; +    u8 value; + +    for (i = 0; i < 8; i++) +    { +        GPIO_PORT_DATA = 4; +        GPIO_PORT_DATA = 4; +        GPIO_PORT_DATA = 4; +        GPIO_PORT_DATA = 4; +        GPIO_PORT_DATA = 4; +        GPIO_PORT_DATA = 5; + +        temp = ((GPIO_PORT_DATA & 2) >> 1); +        value = (value >> 1) | (temp << 7); // UB: accessing uninitialized var +    } + +    return value; +} + +static void EnableGpioPortRead() +{ +    GPIO_PORT_READ_ENABLE = 1; +} + +static void DisableGpioPortRead() +{ +    GPIO_PORT_READ_ENABLE = 0; +} | 
