mirror of
https://github.com/pret/pokefirered.git
synced 2026-05-09 12:35:23 -05:00
Video is working
This commit is contained in:
parent
3ba0416dc6
commit
5b7fc2cab8
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -7,6 +7,7 @@
|
|||
*.diff
|
||||
*.dump
|
||||
*.elf
|
||||
*.dll
|
||||
*.exe
|
||||
*.fwjpnfont
|
||||
*.gba
|
||||
|
|
|
|||
6
Makefile
6
Makefile
|
|
@ -23,7 +23,9 @@ OBJDUMP := $(PREFIX)objdump
|
|||
AS := $(PREFIX)as
|
||||
LD := $(PREFIX)ld
|
||||
MODERN := 1
|
||||
SDL_DIR := /mnt/c/Users/josma/Desktop/pokexe/pokefirered/sdl
|
||||
SDL_DIR := /mnt/c/Users/josma/Desktop/pokexe2/SDL2-2.32.10/i686-w64-mingw32
|
||||
|
||||
|
||||
|
||||
EXE :=
|
||||
ifeq ($(OS),Windows_NT)
|
||||
|
|
@ -371,5 +373,5 @@ endif
|
|||
# Elf from object files
|
||||
LDFLAGS = -Map ../../$(MAP)
|
||||
$(ROM): $(LD_SCRIPT) $(LD_SCRIPT_DEPS) $(OBJS)
|
||||
cd $(OBJ_DIR) && i686-w64-mingw32-gcc -Wno-trigraphs -Wimplicit -Wparentheses -Wunused -m32 -std=gnu99 -fleading-underscore -fno-dce -fno-builtin -Wno-unused-function -DFIRERED=1 -DREVISION=0 -DENGLISH=1 -DPORTABLE -DNONMATCHING -D UBFIX -DMODERN=1 -O3 -Wl,--demangle $(OBJS_REL) -o $(ROM) -L$(SDL_DIR)/lib -lxinput -lkernel32 -lSDL3
|
||||
cd $(OBJ_DIR) && i686-w64-mingw32-gcc -Wno-trigraphs -Wimplicit -Wparentheses -Wunused -m32 -std=gnu99 -fleading-underscore -fno-dce -fno-builtin -Wno-unused-function -DFIRERED=1 -DREVISION=0 -DENGLISH=1 -DPORTABLE -DNONMATCHING -D UBFIX -DMODERN=1 -O3 -Wl,--demangle $(OBJS_REL) -o $(ROM) -L$(SDL_DIR)/lib -lxinput -lkernel32 -lmingw32 -lSDL2main -lSDL2
|
||||
mv $(OBJ_DIR)/$(ROM) ./
|
||||
|
|
|
|||
36
include/cgb_audio.h
Normal file
36
include/cgb_audio.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef CGB_AUDIO_H
|
||||
#define CGB_AUDIO_H
|
||||
|
||||
#define MIXED_AUDIO_BUFFER_SIZE 4907
|
||||
|
||||
struct AudioCGB{
|
||||
u16 ch1Freq;
|
||||
u8 ch1SweepCounter;
|
||||
u8 ch1SweepCounterI;
|
||||
bool8 ch1SweepDir;
|
||||
u8 ch1SweepShift;
|
||||
u8 Vol[4];
|
||||
u8 VolI[4];
|
||||
u8 Len[4];
|
||||
u8 LenI[4];
|
||||
bool8 LenOn[4];
|
||||
u8 EnvCounter[4];
|
||||
u8 EnvCounterI[4];
|
||||
bool8 EnvDir[4];
|
||||
bool8 DAC[4];
|
||||
float WAVRAM[32];
|
||||
u16 ch4LFSR [2];
|
||||
__attribute__((aligned(4))) float outBuffer[MIXED_AUDIO_BUFFER_SIZE * 2];
|
||||
};
|
||||
|
||||
void cgb_audio_init(u32 rate);
|
||||
void cgb_set_sweep(u8 sweep);
|
||||
void cgb_set_wavram();
|
||||
void cgb_toggle_length(u8 channel, bool8 state);
|
||||
void cgb_set_length(u8 channel, u8 length);
|
||||
void cgb_set_envelope(u8 channel, u8 envelope);
|
||||
void cgb_trigger_note(u8 channel);
|
||||
void cgb_audio_generate(u16 samplesPerFrame);
|
||||
float *cgb_get_buffer();
|
||||
|
||||
#endif
|
||||
2349
include/cgb_tables.h
Normal file
2349
include/cgb_tables.h
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -24,14 +24,15 @@ extern void * INTR_VECTOR;
|
|||
#define OBJ_PLTT_SIZE 0x200
|
||||
#define PLTT_SIZE (BG_PLTT_SIZE + OBJ_PLTT_SIZE)
|
||||
|
||||
extern u8 PLTT[PLTT_SIZE];
|
||||
extern u8 PLTT[PLTT_SIZE] __attribute__ ((aligned (4)));
|
||||
#define BG_PLTT PLTT
|
||||
#define OBJ_PLTT (PLTT + BG_PLTT_SIZE)
|
||||
|
||||
|
||||
|
||||
#define VRAM_SIZE 0x18000
|
||||
extern u32 VRAM[VRAM_SIZE / sizeof(u32)];
|
||||
extern u8 VRAM_[VRAM_SIZE] __attribute__ ((aligned (4)));
|
||||
#define VRAM (u32)VRAM_
|
||||
|
||||
#define BG_VRAM VRAM
|
||||
#define BG_VRAM_SIZE 0x10000
|
||||
|
|
@ -54,7 +55,7 @@ extern u32 VRAM[VRAM_SIZE / sizeof(u32)];
|
|||
#define OBJ_VRAM1_SIZE 0x4000
|
||||
|
||||
#define OAM_SIZE 0x400
|
||||
extern u8 OAM[OAM_SIZE];
|
||||
extern u8 OAM[OAM_SIZE] __attribute__ ((aligned (4)));
|
||||
|
||||
#define ROM_HEADER_SIZE 0xC0
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#define GUARD_GBA_FLASH_INTERNAL_H
|
||||
|
||||
#define FLASH_ROM_SIZE_1M 131072 // 1 megabit ROM
|
||||
extern u8 FLASH_BASE[FLASH_ROM_SIZE_1M];
|
||||
extern u8 FLASH_BASE[FLASH_ROM_SIZE_1M] __attribute__ ((aligned (4)));
|
||||
|
||||
#define FLASH_WRITE(addr, data) ((*(vu8 *)(FLASH_BASE + (addr))) = (data))
|
||||
|
||||
|
|
@ -44,11 +44,11 @@ struct FlashSetupInfo
|
|||
|
||||
extern u16 gFlashNumRemainingBytes;
|
||||
|
||||
extern u16 (*ProgramFlashByte)(u16, u32, u8);
|
||||
extern u16 (*ProgramFlashSector)(u16, void *);
|
||||
extern u16 (*EraseFlashChip)(void);
|
||||
extern u16 (*EraseFlashSector)(u16);
|
||||
extern u16 (*WaitForFlashWrite)(u8, u8 *, u8);
|
||||
extern u16 ProgramFlashByte(u16, u32, u8);
|
||||
extern u16 ProgramFlashSector(u16, void *);
|
||||
extern u16 EraseFlashChip(void);
|
||||
extern u16 EraseFlashSector(u16);
|
||||
extern u16 WaitForFlashWrite(u8, u8 *, u8);
|
||||
extern const u16 *gFlashMaxTime;
|
||||
extern const struct FlashType *gFlash;
|
||||
|
||||
|
|
@ -60,7 +60,6 @@ extern const struct FlashSetupInfo LE26FV10N1TS;
|
|||
extern const struct FlashSetupInfo DefaultFlash;
|
||||
|
||||
void SwitchFlashBank(u8 bankNum);
|
||||
u16 ReadFlashId(void);
|
||||
void StartFlashTimer(u8 phase);
|
||||
void SetReadFlash1(u16 *dest);
|
||||
void StopFlashTimer(void);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef GUARD_GBA_IO_REG_H
|
||||
#define GUARD_GBA_IO_REG_H
|
||||
|
||||
extern u8 REG_BASE[];
|
||||
extern u8 REG_BASE[0x400] __attribute__ ((aligned (4)));
|
||||
|
||||
// I/O register offsets
|
||||
|
||||
|
|
@ -643,9 +643,11 @@ extern u8 REG_BASE[];
|
|||
#define DMA_DEST_DEC 0x0020
|
||||
#define DMA_DEST_FIXED 0x0040
|
||||
#define DMA_DEST_RELOAD 0x0060
|
||||
#define DMA_DEST_MASK 0x0060
|
||||
#define DMA_SRC_INC 0x0000
|
||||
#define DMA_SRC_DEC 0x0080
|
||||
#define DMA_SRC_FIXED 0x0100
|
||||
#define DMA_SRC_MASK 0x0180
|
||||
#define DMA_REPEAT 0x0200
|
||||
#define DMA_16BIT 0x0000
|
||||
#define DMA_32BIT 0x0400
|
||||
|
|
@ -773,4 +775,6 @@ extern u8 REG_BASE[];
|
|||
#define WAITCNT_AGB (0 << 15)
|
||||
#define WAITCNT_CGB (1 << 15)
|
||||
|
||||
void printRegs();
|
||||
|
||||
#endif // GUARD_GBA_IO_REG_H
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define GUARD_GBA_M4A_INTERNAL_H
|
||||
|
||||
#include "gba/gba.h"
|
||||
#include "music_player.h"
|
||||
|
||||
// ASCII encoding of 'Smsh' in reverse
|
||||
// This is presumably short for SMASH, the developer of MKS4AGB.
|
||||
|
|
@ -152,7 +153,7 @@ struct SoundChannel
|
|||
u8 rhythmPan;
|
||||
u8 dummy3[3];
|
||||
u32 count;
|
||||
u32 fw;
|
||||
float fw;
|
||||
u32 frequency;
|
||||
struct WaveData *wav;
|
||||
s8 *currentPointer;
|
||||
|
|
@ -165,8 +166,7 @@ struct SoundChannel
|
|||
};
|
||||
|
||||
#define MAX_DIRECTSOUND_CHANNELS 12
|
||||
|
||||
#define PCM_DMA_BUF_SIZE 1584 // size of Direct Sound buffer
|
||||
#define PCM_DMA_BUF_SIZE 4907 // size of Direct Sound buffer
|
||||
|
||||
struct MusicPlayerInfo;
|
||||
|
||||
|
|
@ -205,7 +205,7 @@ struct SoundInfo
|
|||
u8 gap[3];
|
||||
s32 pcmSamplesPerVBlank;
|
||||
s32 pcmFreq;
|
||||
s32 divFreq;
|
||||
float divFreq;
|
||||
struct CgbChannel *cgbChans;
|
||||
MPlayMainFunc MPlayMainHead;
|
||||
struct MusicPlayerInfo *musicPlayerHead;
|
||||
|
|
@ -217,7 +217,7 @@ struct SoundInfo
|
|||
ExtVolPitFunc ExtVolPit;
|
||||
u8 gap2[16];
|
||||
struct SoundChannel chans[MAX_DIRECTSOUND_CHANNELS];
|
||||
s8 pcmBuffer[PCM_DMA_BUF_SIZE * 2];
|
||||
float pcmBuffer[PCM_DMA_BUF_SIZE * 2];
|
||||
};
|
||||
|
||||
struct SongHeader
|
||||
|
|
@ -408,8 +408,8 @@ extern const struct ToneData voicegroup000;
|
|||
|
||||
u32 umul3232H32(u32 multiplier, u32 multiplicand);
|
||||
void SoundMain(void);
|
||||
void SoundMainBTM(void);
|
||||
void TrackStop(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track);
|
||||
void SoundMainBTM(void *ptr);
|
||||
void TrackStop(struct MP2KPlayerState *player, struct MP2KTrack *track);
|
||||
void MPlayMain(struct MusicPlayerInfo *);
|
||||
void RealClearChain(void *x);
|
||||
|
||||
|
|
@ -430,7 +430,7 @@ void CgbOscOff(u8);
|
|||
void CgbModVol(struct CgbChannel *chan);
|
||||
u32 MidiKeyToCgbFreq(u8, u8, u8);
|
||||
void DummyFunc(void);
|
||||
void MPlayJumpTableCopy(MPlayFunc *mplayJumpTable);
|
||||
void MPlayJumpTableCopy(void **mplayJumpTable);
|
||||
void SampleFreqSet(u32 freq);
|
||||
void m4aSoundVSyncOn(void);
|
||||
void m4aSoundVSyncOff(void);
|
||||
|
|
@ -456,29 +456,29 @@ void SetPokemonCryStereo(u32 val);
|
|||
void SetPokemonCryPriority(u8 val);
|
||||
|
||||
// sound command handler functions
|
||||
void ply_fine(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_goto(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_patt(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_pend(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_rept(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void MP2K_event_fine(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_goto(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_patt(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_pend(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_rept(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void ply_memacc(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_prio(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_tempo(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_keysh(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_voice(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_vol(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_pan(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_bend(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_bendr(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_lfos(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_lfodl(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_mod(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_modt(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_tune(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_port(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void MP2K_event_prio(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_tempo(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_keysh(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_voice(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_vol(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_pan(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_bend(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_bendr(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_lfos(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_lfodl(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_mod(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_modt(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_tune(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void MP2K_event_port(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void ply_xcmd(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_endtie(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_note(u32 note_cmd, struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void MP2K_event_endtie(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
void ply_note(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
|
||||
// extended sound command handler functions
|
||||
void ply_xxx(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
|
|
|
|||
|
|
@ -31,14 +31,7 @@
|
|||
|
||||
#define CpuFastCopy(src, dest, size) CpuFastSet(src, dest, ((size)/(32/8) & 0x1FFFFF))
|
||||
|
||||
#define DmaSet(dmaNum, src, dest, control) \
|
||||
{ \
|
||||
vu32 *dmaRegs = (vu32 *)REG_ADDR_DMA##dmaNum; \
|
||||
dmaRegs[0] = (vu32)(src); \
|
||||
dmaRegs[1] = (vu32)(dest); \
|
||||
dmaRegs[2] = (vu32)(control); \
|
||||
dmaRegs[2]; \
|
||||
}
|
||||
extern void DmaSet(int dmaNum, const void * src, void * dest, u32 control);
|
||||
|
||||
#define DMA_FILL(dmaNum, value, dest, size, bit) \
|
||||
{ \
|
||||
|
|
@ -50,8 +43,8 @@
|
|||
| ((size)/(bit/8))); \
|
||||
}
|
||||
|
||||
#define DmaFill16(dmaNum, value, dest, size) DMA_FILL(dmaNum, value, dest, size, 16)
|
||||
#define DmaFill32(dmaNum, value, dest, size) DMA_FILL(dmaNum, value, dest, size, 32)
|
||||
#define DmaFill16(dmaNum, value, dest, size) CpuFill16(value, dest, size)
|
||||
#define DmaFill32(dmaNum, value, dest, size) CpuFill32(value, dest, size)
|
||||
|
||||
// Note that the DMA clear macros cause the DMA control value to be calculated
|
||||
// at runtime rather than compile time. The size is divided by the DMA transfer
|
||||
|
|
@ -75,8 +68,8 @@
|
|||
(DMA_ENABLE | DMA_START_NOW | DMA_##bit##BIT | DMA_SRC_INC | DMA_DEST_INC) << 16 \
|
||||
| ((size)/(bit/8)))
|
||||
|
||||
#define DmaCopy16(dmaNum, src, dest, size) DMA_COPY(dmaNum, src, dest, size, 16)
|
||||
#define DmaCopy32(dmaNum, src, dest, size) DMA_COPY(dmaNum, src, dest, size, 32)
|
||||
#define DmaCopy16(dmaNum, src, dest, size) CpuCopy16(src, dest, size)
|
||||
#define DmaCopy32(dmaNum, src, dest, size) CpuCopy32(src, dest, size)
|
||||
|
||||
#define DmaStop(dmaNum) \
|
||||
{ \
|
||||
|
|
|
|||
|
|
@ -31,6 +31,13 @@ typedef vu8 vbool8;
|
|||
typedef vu16 vbool16;
|
||||
typedef vu32 vbool32;
|
||||
|
||||
typedef int_fast8_t sf8;
|
||||
typedef uint_fast8_t uf8;
|
||||
typedef int_fast16_t sf16;
|
||||
typedef uint_fast16_t uf16;
|
||||
typedef int_fast32_t sf32;
|
||||
typedef uint_fast32_t uf32;
|
||||
|
||||
struct BgCnt
|
||||
{
|
||||
u16 priority:2;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#define TRY_FREE_AND_SET_NULL(ptr) if (ptr != NULL) FREE_AND_SET_NULL(ptr)
|
||||
|
||||
extern u8 gHeap[HEAP_SIZE];
|
||||
extern ALIGNED(4) u8 gHeap[HEAP_SIZE];
|
||||
void *Alloc(u32 size);
|
||||
void *AllocZeroed(u32 size);
|
||||
void Free(void *pointer);
|
||||
|
|
|
|||
19
include/mp2k_common.h
Normal file
19
include/mp2k_common.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef MP2020_COMMON_H
|
||||
#define MP2020_COMMON_H
|
||||
|
||||
#ifndef __has_builtin
|
||||
#define __has_builtin(x) defined(__GNUC__)
|
||||
#endif
|
||||
|
||||
#if ((-1 >> 1) == -1) && __has_builtin(__builtin_ctz)
|
||||
#define FLOOR_DIV_POW2(a, b) ((a) >> __builtin_ctz(b))
|
||||
#else
|
||||
#define FLOOR_DIV_POW2(a, b) ((a) > 0 ? (a) / (b) : (((a) + 1 - (b)) / (b)))
|
||||
#endif
|
||||
|
||||
#define NOT_GBA_BIOS
|
||||
#define NOT_GBA
|
||||
//#define ORIGINAL_COARSE_POSITION_CLEARING
|
||||
#define POKEMON_EXTENSIONS
|
||||
|
||||
#endif
|
||||
127
include/music_player.h
Normal file
127
include/music_player.h
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
#ifndef MUSIC_PLAYER_H
|
||||
#define MUSIC_PLAYER_H
|
||||
|
||||
#include "gba/types.h"
|
||||
//#include "m4a.h"
|
||||
#include "sound_mixer.h"
|
||||
|
||||
#define PLAYER_UNLOCKED 0x68736D53
|
||||
#define PLAYER_LOCKED PLAYER_UNLOCKED+1
|
||||
|
||||
struct WaveData2
|
||||
{
|
||||
u8 compressionFlags1;
|
||||
u8 compressionFlags2;
|
||||
u8 compressionFlags3;
|
||||
u8 loopFlags;
|
||||
u32 freq; // 22.10 fixed width decimal, freq of C4. Or just the frequency of C14.
|
||||
u32 loopStart;
|
||||
u32 size; // number of samples
|
||||
s8 data[]; // samples
|
||||
};
|
||||
|
||||
|
||||
struct MP2KInstrument {
|
||||
u8 type;
|
||||
u8 drumKey;
|
||||
u8 cgbLength;
|
||||
u8 panSweep;
|
||||
union {
|
||||
struct WaveData *wav;
|
||||
struct MP2KInstrument *group;
|
||||
u32 *cgb3Sample;
|
||||
u32 squareNoiseConfig;
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
u8 attack;
|
||||
u8 decay;
|
||||
u8 sustain;
|
||||
u8 release;
|
||||
};
|
||||
u8 *keySplitTable;
|
||||
};
|
||||
};
|
||||
|
||||
struct MP2KTrack {
|
||||
u8 status;
|
||||
u8 wait;
|
||||
u8 patternLevel;
|
||||
u8 repeatCount;
|
||||
u8 gateTime; // 0 if TIE
|
||||
u8 key;
|
||||
u8 velocity;
|
||||
u8 runningStatus;
|
||||
s8 keyShiftCalculated; // Calculated by TrkVolPitSet using fields below. Units: semitones
|
||||
u8 pitchCalculated; // Calculated by TrkVolPitSet using fields below. Units: 256ths of a semitone
|
||||
s8 keyShift; // Units: semitones
|
||||
s8 keyShiftPublic; // Units: semitones
|
||||
s8 tune; // Units: 64ths of a semitone
|
||||
u8 pitchPublic; // Units: 256ths of a semitone
|
||||
s8 bend; // Units: (bendRange / 64)ths of a semitone
|
||||
u8 bendRange;
|
||||
u8 volRightCalculated;
|
||||
u8 volLeftCalculated;
|
||||
u8 vol;
|
||||
u8 volPublic; // Used both for fades and MPlayVolumeControl
|
||||
s8 pan;
|
||||
s8 panPublic;
|
||||
s8 modCalculated; // Pitch units: 16ths of a semitone
|
||||
u8 modDepth;
|
||||
u8 modType;
|
||||
u8 lfoSpeed;
|
||||
u8 lfoSpeedCounter;
|
||||
u8 lfoDelay;
|
||||
u8 lfoDelayCounter;
|
||||
u8 priority;
|
||||
u8 echoVolume;
|
||||
u8 echoLength;
|
||||
struct MixerSource *chan;
|
||||
struct MP2KInstrument instrument;
|
||||
u8 gap[10];
|
||||
u16 unk_3A;
|
||||
u32 ct;
|
||||
u8 *cmdPtr;
|
||||
u8 *patternStack[3];
|
||||
};
|
||||
|
||||
struct MP2KPlayerState {
|
||||
struct MP2KSongHeader *songHeader;
|
||||
vu32 status;
|
||||
u8 trackCount;
|
||||
u8 priority;
|
||||
u8 cmd;
|
||||
bool8 checkSongPriority;
|
||||
u32 clock;
|
||||
u8 padding[8];
|
||||
u8 *memaccArea;
|
||||
u16 tempoRawBPM; // 150 initially... this doesn't seem right but whatever
|
||||
u16 tempoScale; // 0x100 initially
|
||||
u16 tempoInterval; // 150 initially
|
||||
u16 tempoCounter;
|
||||
u16 fadeInterval;
|
||||
u16 fadeCounter;
|
||||
u16 isFadeTemporary:1;
|
||||
u16 isFadeIn:1;
|
||||
u16 fadeVolume:7;
|
||||
u16 :7; // padding
|
||||
struct MP2KTrack *tracks;
|
||||
struct MP2KInstrument *voicegroup;
|
||||
vu32 lockStatus;
|
||||
void (*nextPlayerFunc)(void *);
|
||||
void *nextPlayer;
|
||||
};
|
||||
|
||||
struct MP2KPlayerCtor {
|
||||
struct MP2KPlayerState *player;
|
||||
struct MP2KTrack *tracks;
|
||||
u8 trackCount;
|
||||
u8 padding;
|
||||
bool16 checkSongPriority;
|
||||
};
|
||||
|
||||
void clear_modM(struct MP2KPlayerState *unused, struct MP2KTrack *track);
|
||||
void MP2K_event_endtie(struct MP2KPlayerState *unused, struct MP2KTrack *track);
|
||||
void MP2K_event_lfos(struct MP2KPlayerState *unused, struct MP2KTrack *track);
|
||||
void MP2K_event_mod(struct MP2KPlayerState *unused, struct MP2KTrack *track);
|
||||
#endif
|
||||
13
include/platform.h
Normal file
13
include/platform.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef GUARD_PLATFORM_H
|
||||
#define GUARD_PLATFORM_H
|
||||
|
||||
#include "global.h"
|
||||
|
||||
|
||||
void Platform_StoreSaveFile(void);
|
||||
void Platform_ReadFlash(u16 sectorNum, u32 offset, u8 *dest, u32 size);
|
||||
void Platform_QueueAudio(float *audioBuffer, s32 samplesPerFrame);
|
||||
u16 Platform_GetKeyInput(void);
|
||||
|
||||
|
||||
#endif
|
||||
16
include/platform/dma.h
Normal file
16
include/platform/dma.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef GUARD_DMA_H
|
||||
#define GUARD_DMA_H
|
||||
|
||||
#include "global.h"
|
||||
|
||||
#define DMA_COUNT 4
|
||||
|
||||
enum {
|
||||
DMA_NOW,
|
||||
DMA_VBLANK,
|
||||
DMA_HBLANK,
|
||||
DMA_SPECIAL
|
||||
};
|
||||
|
||||
void RunDMAs(u32 type);
|
||||
#endif
|
||||
7
include/platform/framedraw.h
Normal file
7
include/platform/framedraw.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef GUARD_FRAMEDRAW_H
|
||||
#define GUARD_FRAMEDRAW_H
|
||||
|
||||
#include "global.h"
|
||||
|
||||
void DrawFrame(uint16_t *pixels);
|
||||
#endif
|
||||
121
include/sound_mixer.h
Normal file
121
include/sound_mixer.h
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#ifndef SOUND_MIXER_H
|
||||
#define SOUND_MIXER_H
|
||||
|
||||
#include "gba/types.h"
|
||||
#include "music_player.h"
|
||||
|
||||
#define MIXER_UNLOCKED 0x68736D53
|
||||
#define MIXER_LOCKED PLAYER_UNLOCKED+1
|
||||
|
||||
struct MP2KPlayerState;
|
||||
|
||||
struct MixerSource {
|
||||
u8 status;
|
||||
u8 type;
|
||||
u8 rightVol;
|
||||
u8 leftVol;
|
||||
u8 attack;
|
||||
u8 decay;
|
||||
u8 sustain;
|
||||
u8 release;
|
||||
u8 key;
|
||||
u8 envelopeVol;
|
||||
union {
|
||||
u8 envelopeVolR;
|
||||
u8 envelopeGoal;
|
||||
}__attribute__((packed));
|
||||
union {
|
||||
u8 envelopeVolL;
|
||||
u8 envelopeCtr;
|
||||
}__attribute__((packed));
|
||||
u8 echoVol;
|
||||
u8 echoLen;
|
||||
u8 padding1;
|
||||
u8 padding2;
|
||||
u8 gateTime;
|
||||
u8 untransposedKey;
|
||||
u8 velocity;
|
||||
u8 priority;
|
||||
u8 rhythmPan;
|
||||
u8 padding3;
|
||||
u8 padding4;
|
||||
u8 padding5;
|
||||
union {
|
||||
u32 ct;
|
||||
struct {
|
||||
u8 padding6;
|
||||
u8 sustainGoal;
|
||||
u8 nrx4;
|
||||
u8 pan;
|
||||
};
|
||||
};
|
||||
union {
|
||||
float fw;
|
||||
struct {
|
||||
u8 panMask;
|
||||
u8 cgbStatus;
|
||||
u8 length;
|
||||
u8 sweep;
|
||||
};
|
||||
};
|
||||
u32 freq;
|
||||
union {
|
||||
u32 *newCgb3Sample;
|
||||
struct WaveData *wav;
|
||||
};
|
||||
union {
|
||||
u32 *oldCgb3Sample;
|
||||
s8 *current;
|
||||
};
|
||||
struct MP2KTrack *track;
|
||||
struct MixerSource *prev;
|
||||
struct MixerSource *next;
|
||||
u32 padding7; //d4
|
||||
u32 blockCount; // bdpcm block count
|
||||
};
|
||||
|
||||
enum { MAX_SAMPLE_CHANNELS = 12 };
|
||||
enum { MIXED_AUDIO_BUFFER_SIZE = 4907 };
|
||||
|
||||
struct SoundMixerState {
|
||||
vu32 lockStatus;
|
||||
vu8 dmaCounter;
|
||||
u8 reverb;
|
||||
u8 numChans;
|
||||
u8 masterVol;
|
||||
u8 freqOption;
|
||||
u8 extensionFlags;
|
||||
u8 cgbCounter15;
|
||||
u8 framesPerDmaCycle;
|
||||
u8 maxScanlines;
|
||||
u8 padding1;
|
||||
u8 padding2;
|
||||
u8 padding3;
|
||||
s32 samplesPerFrame;
|
||||
s32 sampleRate;
|
||||
float sampleRateReciprocal;
|
||||
struct MixerSource *cgbChans;
|
||||
void (*firstPlayerFunc)(void *player);
|
||||
void *firstPlayer;
|
||||
void (*cgbMixerFunc)(void);
|
||||
void (*cgbNoteOffFunc)(u8 chan);
|
||||
u32 (*cgbCalcFreqFunc)(u8 chan, u8 key, u8 pitch);
|
||||
void (**mp2kEventFuncTable)();
|
||||
void (*mp2kEventNxxFunc)(u8 clock, struct MP2KPlayerState *player, struct MP2KTrack *track);
|
||||
void *reserved1; // In poke* this is called "ExtVolPit"
|
||||
void *reserved2;
|
||||
void *reserved3;
|
||||
void *reversed4;
|
||||
void *reserved5;
|
||||
struct MixerSource chans[MAX_SAMPLE_CHANNELS];
|
||||
__attribute__((aligned(4))) float outBuffer[MIXED_AUDIO_BUFFER_SIZE * 2];
|
||||
//s8 outBuffer[MIXED_AUDIO_BUFFER_SIZE * 2];
|
||||
};
|
||||
|
||||
typedef void (*MixerRamFunc)(struct SoundMixerState *, u32, u16, s8 *, u16);
|
||||
|
||||
#ifndef NOT_GBA
|
||||
#undef REG_VCOUNT
|
||||
#define REG_VCOUNT (*(vu8*)REG_ADDR_VCOUNT)
|
||||
#endif
|
||||
#endif//SOUND_MIXER_H
|
||||
204
src/agb_flash.c
204
src/agb_flash.c
|
|
@ -8,14 +8,70 @@ static u16 sSavedIme;
|
|||
|
||||
u8 gFlashTimeoutFlag = 0;
|
||||
u8 (*PollFlashStatus)(u8 *) = NULL;
|
||||
u16 (*WaitForFlashWrite)(u8 phase, u8 *addr, u8 lastData) = NULL;
|
||||
u16 (*ProgramFlashSector)(u16 sectorNum, void *src) = NULL;
|
||||
const struct FlashType *gFlash = NULL;
|
||||
u16 (*ProgramFlashByte)(u16 sectorNum, u32 offset, u8 data) = NULL;
|
||||
|
||||
const u16 gFlashMaxTimeData[] =
|
||||
{
|
||||
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 FlashType gFlashData = {
|
||||
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
|
||||
{ { 0xCC, 0xCC } } // ID
|
||||
};
|
||||
|
||||
u16 WaitForFlashWrite(u8 phase, u8 *addr, u8 lastData);
|
||||
u16 ProgramFlashSector(u16 sectorNum, void *src);
|
||||
u16 ProgramFlashByte(u16 sectorNum, u32 offset, u8 data);
|
||||
u16 gFlashNumRemainingBytes = 0;
|
||||
u16 (*EraseFlashChip)() = NULL;
|
||||
u16 (*EraseFlashSector)(u16 sectorNum) = NULL;
|
||||
const u16 *gFlashMaxTime = NULL;
|
||||
u16 EraseFlashChip(void);
|
||||
u16 EraseFlashSector(u16 sectorNum);
|
||||
const u16 *gFlashMaxTime = gFlashMaxTimeData;
|
||||
const struct FlashType *gFlash = &gFlashData;
|
||||
|
||||
u16 WaitForFlashWrite(u8 phase, u8 *addr, u8 lastData)
|
||||
{
|
||||
// stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
u16 EraseFlashChip(void)
|
||||
{
|
||||
memset(FLASH_BASE, 0xFF, sizeof(FLASH_BASE));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u16 EraseFlashSector(u16 sectorNum)
|
||||
{
|
||||
u8 clearBuffer[0x1000] = { 0xFF };
|
||||
return ProgramFlashSector(sectorNum, &clearBuffer[0]);
|
||||
}
|
||||
|
||||
u16 ProgramFlashByte(u16 sectorNum, u32 offset, u8 data)
|
||||
{
|
||||
FLASH_BASE[(sectorNum << gFlash->sector.shift) + offset] = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u16 ProgramFlashSector(u16 sectorNum, void *src)
|
||||
{
|
||||
memcpy(&FLASH_BASE[sectorNum << gFlash->sector.shift], src, 0x1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u16 IdentifyFlash(void)
|
||||
{
|
||||
REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SetReadFlash1(u16 *dest);
|
||||
|
||||
|
|
@ -34,33 +90,7 @@ do { \
|
|||
; \
|
||||
} 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)
|
||||
{
|
||||
|
|
@ -139,123 +169,23 @@ void ReadFlash_Core(vu8 *src, u8 *dest, u32 size)
|
|||
|
||||
void ReadFlash(u16 sectorNum, u32 offset, void *dest, u32 size)
|
||||
{
|
||||
u8 *src;
|
||||
u16 i;
|
||||
vu16 readFlash_Core_Buffer[0x40];
|
||||
vu16 *funcSrc;
|
||||
vu16 *funcDest;
|
||||
void (*readFlash_Core)(vu8 *, 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 = (vu16 *)ReadFlash_Core;
|
||||
funcSrc = (vu16 *)((s32)funcSrc ^ 1);
|
||||
funcDest = readFlash_Core_Buffer;
|
||||
|
||||
i = ((s32)ReadFlash - (s32)ReadFlash_Core) >> 1;
|
||||
|
||||
while (i != 0)
|
||||
{
|
||||
*funcDest++ = *funcSrc++;
|
||||
i--;
|
||||
}
|
||||
|
||||
readFlash_Core = (void (*)(vu8 *, u8 *, u32))((s32)readFlash_Core_Buffer + 1);
|
||||
|
||||
src = FLASH_BASE + (sectorNum << gFlash->sector.shift) + offset;
|
||||
|
||||
readFlash_Core(src, dest, size);
|
||||
Platform_ReadFlash(sectorNum, offset, dest, size);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
vu16 verifyFlashSector_Core_Buffer[0x80];
|
||||
vu16 *funcSrc;
|
||||
vu16 *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 = (vu16 *)VerifyFlashSector_Core;
|
||||
funcSrc = (vu16 *)((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.
|
||||
return 0; // return 0 if verified.
|
||||
}
|
||||
|
||||
u32 VerifyFlashSectorNBytes(u16 sectorNum, u8 *src, u32 n)
|
||||
{
|
||||
u16 i;
|
||||
vu16 verifyFlashSector_Core_Buffer[0x80];
|
||||
vu16 *funcSrc;
|
||||
vu16 *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 = (vu16 *)VerifyFlashSector_Core;
|
||||
funcSrc = (vu16 *)((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);
|
||||
return 0; // return 0 if verified.
|
||||
}
|
||||
|
||||
u32 ProgramFlashSectorAndVerify(u16 sectorNum, u8 *src)
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
#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
|
||||
}
|
||||
};
|
||||
|
|
@ -1,197 +0,0 @@
|
|||
#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, void *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;
|
||||
}
|
||||
|
|
@ -382,7 +382,7 @@ void StoreSpriteCallbackInData6(struct Sprite *sprite, SpriteCallback callback)
|
|||
|
||||
static void SetCallbackToStoredInData6(struct Sprite *sprite)
|
||||
{
|
||||
u32 callback = (u16)sprite->data[6] | (sprite->data[7] << 16);
|
||||
u32 callback = (u16)sprite->data[6] | ((u16)sprite->data[7] << 16);
|
||||
|
||||
sprite->callback = (SpriteCallback)callback;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -793,7 +793,7 @@ static void AnimShakeMonOrBattleTerrain(struct Sprite *sprite)
|
|||
StoreSpriteCallbackInData6(sprite, (void *)&gSpriteCoordOffsetY);
|
||||
break;
|
||||
}
|
||||
sprite->data[4] = *(u16 *)(sprite->data[6] | (sprite->data[7] << 16));
|
||||
sprite->data[4] = *(u16 *)LoadPointerFromVars(sprite->data[6], sprite->data[7]);
|
||||
sprite->data[5] = gBattleAnimArgs[3];
|
||||
var0 = sprite->data[5] - 2;
|
||||
if (var0 < 2)
|
||||
|
|
@ -816,13 +816,13 @@ static void AnimShakeMonOrBattleTerrain_Step(struct Sprite *sprite)
|
|||
else
|
||||
{
|
||||
sprite->data[1] = sprite->data[2];
|
||||
*(u16 *)(sprite->data[6] | (sprite->data[7] << 16)) += sprite->data[0];
|
||||
*(u16 *)LoadPointerFromVars(sprite->data[6], sprite->data[7]) += sprite->data[0];
|
||||
sprite->data[0] = -sprite->data[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*(u16 *)(sprite->data[6] | (sprite->data[7] << 16)) = sprite->data[4];
|
||||
*(u16 *)LoadPointerFromVars(sprite->data[6], sprite->data[7]) = sprite->data[4];
|
||||
var0 = sprite->data[5] - 2;
|
||||
if (var0 < 2)
|
||||
for (i = 0; i < gBattlersCount; ++i)
|
||||
|
|
|
|||
|
|
@ -3862,18 +3862,19 @@ static void FreeResetData_ReturnToOvOrDoEvolutions(void)
|
|||
{
|
||||
if (!gPaletteFade.active)
|
||||
{
|
||||
FreeAllWindowBuffers(); // This needs to be moved up here to avoid a use-after-free
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK))
|
||||
{
|
||||
FreeMonSpritesGfx();
|
||||
FreeBattleResources();
|
||||
FreeBattleSpritesData();
|
||||
}
|
||||
ResetSpriteData();
|
||||
if (gLeveledUpInBattle == 0 || gBattleOutcome != B_OUTCOME_WON)
|
||||
gBattleMainFunc = ReturnFromBattleToOverworld;
|
||||
else
|
||||
gBattleMainFunc = TryEvolvePokemon;
|
||||
FreeAllWindowBuffers();
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK))
|
||||
{
|
||||
FreeMonSpritesGfx();
|
||||
FreeBattleSpritesData();
|
||||
FreeBattleResources();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3858,7 +3858,7 @@ static void Cmd_playanimation(void)
|
|||
|| gBattlescriptCurrInstr[2] == B_ANIM_SUBSTITUTE_FADE
|
||||
|| gBattlescriptCurrInstr[2] == B_ANIM_SILPH_SCOPED)
|
||||
{
|
||||
BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], *argumentPtr);
|
||||
BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], argumentPtr != NULL ? *argumentPtr : 0);
|
||||
MarkBattlerForControllerExec(gActiveBattler);
|
||||
gBattlescriptCurrInstr += 7;
|
||||
}
|
||||
|
|
@ -3872,7 +3872,7 @@ static void Cmd_playanimation(void)
|
|||
|| gBattlescriptCurrInstr[2] == B_ANIM_SANDSTORM_CONTINUES
|
||||
|| gBattlescriptCurrInstr[2] == B_ANIM_HAIL_CONTINUES)
|
||||
{
|
||||
BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], *argumentPtr);
|
||||
BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], argumentPtr != NULL ? *argumentPtr : 0);
|
||||
MarkBattlerForControllerExec(gActiveBattler);
|
||||
gBattlescriptCurrInstr += 7;
|
||||
}
|
||||
|
|
@ -3882,7 +3882,7 @@ static void Cmd_playanimation(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], *argumentPtr);
|
||||
BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], argumentPtr != NULL ? *argumentPtr : 0);
|
||||
MarkBattlerForControllerExec(gActiveBattler);
|
||||
gBattlescriptCurrInstr += 7;
|
||||
}
|
||||
|
|
@ -3902,7 +3902,7 @@ static void Cmd_playanimation_var(void)
|
|||
|| *animationIdPtr == B_ANIM_SNATCH_MOVE
|
||||
|| *animationIdPtr == B_ANIM_SUBSTITUTE_FADE)
|
||||
{
|
||||
BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, *argumentPtr);
|
||||
BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, argumentPtr != NULL ? *argumentPtr : 0);
|
||||
MarkBattlerForControllerExec(gActiveBattler);
|
||||
gBattlescriptCurrInstr += 10;
|
||||
}
|
||||
|
|
@ -3915,7 +3915,7 @@ static void Cmd_playanimation_var(void)
|
|||
|| *animationIdPtr == B_ANIM_SANDSTORM_CONTINUES
|
||||
|| *animationIdPtr == B_ANIM_HAIL_CONTINUES)
|
||||
{
|
||||
BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, *argumentPtr);
|
||||
BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, argumentPtr != NULL ? *argumentPtr : 0);
|
||||
MarkBattlerForControllerExec(gActiveBattler);
|
||||
gBattlescriptCurrInstr += 10;
|
||||
}
|
||||
|
|
@ -3925,7 +3925,7 @@ static void Cmd_playanimation_var(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, *argumentPtr);
|
||||
BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, argumentPtr != NULL ? *argumentPtr : 0);
|
||||
MarkBattlerForControllerExec(gActiveBattler);
|
||||
gBattlescriptCurrInstr += 10;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -633,6 +633,7 @@ bool8 IsBattleTransitionDone(void)
|
|||
InitTransitionData();
|
||||
FREE_AND_SET_NULL(sTransitionData);
|
||||
DestroyTask(taskId);
|
||||
SetHBlankCallback(NULL); //prevents use after free crash in HBlankCB_Phase2_Mugshots
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
|
|
@ -692,8 +693,10 @@ static bool8 Transition_StartMain(struct Task *task)
|
|||
static bool8 Transition_WaitForMain(struct Task *task)
|
||||
{
|
||||
task->tTransitionDone = FALSE;
|
||||
if (FindTaskIdByFunc(sTasks_Main[task->tTransitionId]) == TASK_NONE)
|
||||
if (FindTaskIdByFunc(sTasks_Main[task->tTransitionId]) == TASK_NONE){
|
||||
task->tTransitionDone = TRUE;
|
||||
SetVBlankCallback(NULL); // Fixes use-after-free of sTransitionData in callbacks
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
|
|||
2
src/bg.c
2
src/bg.c
|
|
@ -493,7 +493,7 @@ u16 Unused_LoadBgPalette(u8 bg, const void *src, u16 size, u16 destOffset)
|
|||
bool8 IsDma3ManagerBusyWithBgCopy(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
VBlankIntrWait();
|
||||
for (i = 0; i < 0x80; i++)
|
||||
{
|
||||
u8 div = i / 0x20;
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ static void Task_HandleYesNoMenu(u8 taskId)
|
|||
AddTextPrinterParameterized4(1, FONT_NORMAL, 0, 3, 1, 1, sTextColor, 0, gText_ClearingData);
|
||||
CopyWindowToVram(1, COPYWIN_FULL);
|
||||
ClearSaveData();
|
||||
Platform_StoreSaveFile();
|
||||
break;
|
||||
case MENU_NOTHING_CHOSEN:
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
#include "global.h"
|
||||
#include "gba/flash_internal.h"
|
||||
|
||||
u8 REG_BASE[0x400];
|
||||
struct SoundInfo *SOUND_INFO_PTR;
|
||||
u16 INTR_CHECK;
|
||||
void *INTR_VECTOR;
|
||||
u8 PLTT[PLTT_SIZE];
|
||||
u32 VRAM[VRAM_SIZE/sizeof(u32)];
|
||||
u8 OAM[OAM_SIZE];
|
||||
u8 FLASH_BASE[FLASH_ROM_SIZE_1M];
|
||||
|
||||
u8 REG_BASE[0x400] __attribute__ ((aligned (4))) = {0};
|
||||
u8 PLTT[PLTT_SIZE] __attribute__ ((aligned (4))) = {0};
|
||||
u8 VRAM_[VRAM_SIZE] __attribute__ ((aligned (4))) = {0};
|
||||
u8 OAM[OAM_SIZE] __attribute__ ((aligned (4))) = {0};
|
||||
u8 FLASH_BASE[FLASH_ROM_SIZE_1M] __attribute__ ((aligned (4))) = {0};
|
||||
|
||||
|
||||
const s16 sineTable[256] = {
|
||||
(s16)0x0000, (s16)0x0192, (s16)0x0323, (s16)0x04B5, (s16)0x0645, (s16)0x07D5, (s16)0x0964, (s16)0x0AF1,
|
||||
|
|
@ -451,6 +453,3 @@ u16 Sqrt(u32 num)
|
|||
return bound;
|
||||
}
|
||||
|
||||
void VBlankIntrWait(void)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
103
src/librfu_rfu.c
103
src/librfu_rfu.c
|
|
@ -124,73 +124,7 @@ static const char str_checkMbootLL[] = "RFU-MBOOT";
|
|||
*_dst++ = *_src++; \
|
||||
} while (0)
|
||||
|
||||
u16 rfu_initializeAPI(u32 *APIBuffer, u16 buffByteSize, IntrFunc *sioIntrTable_p, bool8 copyInterruptToRam)
|
||||
{
|
||||
u16 i;
|
||||
u16 *dst;
|
||||
const u16 *src;
|
||||
u16 buffByteSizeMax;
|
||||
|
||||
// is in EWRAM?
|
||||
if (((uintptr_t)APIBuffer & 0xF000000) == 0x2000000 && copyInterruptToRam)
|
||||
return ERR_RFU_API_BUFF_ADR;
|
||||
// is not 4-byte aligned?
|
||||
if ((u32)APIBuffer & 3)
|
||||
return ERR_RFU_API_BUFF_ADR;
|
||||
if (copyInterruptToRam)
|
||||
{
|
||||
// An assert/debug print may have existed before, ie
|
||||
// printf("%s %u < %u", "somefile.c:12345", buffByteSize, num)
|
||||
// to push this into buffByteSizeMax?
|
||||
buffByteSizeMax = RFU_API_BUFF_SIZE_RAM;
|
||||
if (buffByteSize < buffByteSizeMax)
|
||||
return ERR_RFU_API_BUFF_SIZE;
|
||||
}
|
||||
if (!copyInterruptToRam)
|
||||
{
|
||||
buffByteSizeMax = RFU_API_BUFF_SIZE_ROM; // same issue as above
|
||||
if (buffByteSize < buffByteSizeMax)
|
||||
return ERR_RFU_API_BUFF_SIZE;
|
||||
}
|
||||
gRfuLinkStatus = (void *)APIBuffer + 0;
|
||||
gRfuStatic = (void *)APIBuffer + 0xb4; // + sizeof(*gRfuLinkStatus)
|
||||
gRfuFixed = (void *)APIBuffer + 0xdc; // + sizeof(*gRfuStatic)
|
||||
gRfuSlotStatusNI[0] = (void *)APIBuffer + 0x1bc; // + sizeof(*gRfuFixed)
|
||||
gRfuSlotStatusUNI[0] = (void *)APIBuffer + 0x37c; // + sizeof(*gRfuSlotStatusNI[0]) * RFU_CHILD_MAX
|
||||
for (i = 1; i < RFU_CHILD_MAX; ++i)
|
||||
{
|
||||
gRfuSlotStatusNI[i] = &gRfuSlotStatusNI[i - 1][1];
|
||||
gRfuSlotStatusUNI[i] = &gRfuSlotStatusUNI[i - 1][1];
|
||||
}
|
||||
// remaining space in API buffer is used for `struct RfuIntrStruct`.
|
||||
gRfuFixed->STWIBuffer = (struct RfuIntrStruct *)&gRfuSlotStatusUNI[3][1];
|
||||
STWI_init_all((struct RfuIntrStruct *)&gRfuSlotStatusUNI[3][1], sioIntrTable_p, copyInterruptToRam);
|
||||
rfu_STC_clearAPIVariables();
|
||||
for (i = 0; i < RFU_CHILD_MAX; ++i)
|
||||
{
|
||||
gRfuSlotStatusNI[i]->recvBuffer = NULL;
|
||||
gRfuSlotStatusNI[i]->recvBufferSize = 0;
|
||||
gRfuSlotStatusUNI[i]->recvBuffer = NULL;
|
||||
gRfuSlotStatusUNI[i]->recvBufferSize = 0;
|
||||
}
|
||||
// rfu_REQ_changeMasterSlave is the function next to rfu_STC_fastCopy
|
||||
#if LIBRFU_VERSION < 1026
|
||||
src = (const u16 *)((uintptr_t)&rfu_STC_fastCopy & ~1);
|
||||
dst = gRfuFixed->fastCopyBuffer;
|
||||
buffByteSizeMax = ((void *)rfu_REQ_changeMasterSlave - (void *)rfu_STC_fastCopy) / sizeof(u16);
|
||||
while (buffByteSizeMax-- != 0)
|
||||
*dst++ = *src++;
|
||||
#else
|
||||
COPY(
|
||||
(uintptr_t)&rfu_STC_fastCopy & ~1,
|
||||
gRfuFixed->fastCopyBuffer,
|
||||
buffByteSizeMax,
|
||||
0x60 / sizeof(u16)
|
||||
);
|
||||
#endif
|
||||
gRfuFixed->fastCopyPtr = (void *)gRfuFixed->fastCopyBuffer + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rfu_STC_clearAPIVariables(void)
|
||||
{
|
||||
|
|
@ -317,41 +251,6 @@ u16 rfu_getRFUStatus(u8 *rfuState)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* RFU Multiboot images are loaded into IWRAM
|
||||
* struct RfuMbootLL
|
||||
* {
|
||||
* struct RfuLinkStatus status;
|
||||
* u8 filler_B4[0x3C];
|
||||
* char name[10];
|
||||
* u16 checksum;
|
||||
* }
|
||||
* Returns 1 if the packet to inherit is malformed.
|
||||
*/
|
||||
u16 rfu_MBOOT_CHILD_inheritanceLinkStatus(void)
|
||||
{
|
||||
const char *s1 = str_checkMbootLL;
|
||||
char *s2 = (char *)0x30000F0;
|
||||
u16 checksum;
|
||||
u16 *mb_buff_iwram_p;
|
||||
u8 i;
|
||||
|
||||
// if (strcmp(s1, s2) != 0) return 1;
|
||||
while (*s1 != '\0')
|
||||
if (*s1++ != *s2++)
|
||||
return 1;
|
||||
mb_buff_iwram_p = (u16 *)0x3000000;
|
||||
|
||||
// The size of struct RfuLinkStatus is 180
|
||||
checksum = 0;
|
||||
for (i = 0; i < 180/2; ++i)
|
||||
checksum += *mb_buff_iwram_p++;
|
||||
if (checksum != *(u16 *)0x30000FA)
|
||||
return 1;
|
||||
CpuCopy16((u16 *)0x3000000, gRfuLinkStatus, sizeof(struct RfuLinkStatus));
|
||||
gRfuStatic->flags |= 0x80; // mboot
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rfu_REQ_stopMode(void)
|
||||
{
|
||||
|
|
@ -434,7 +333,7 @@ void rfu_REQ_configSystem(u16 availSlotFlag, u8 maxMFrame, u8 mcTimer)
|
|||
u16 IMEBackup = REG_IME;
|
||||
|
||||
REG_IME = 0;
|
||||
gRfuStatic->linkEmergencyLimit = Div(600, mcTimer);
|
||||
gRfuStatic->linkEmergencyLimit = 600 / mcTimer;
|
||||
REG_IME = IMEBackup;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
62
src/link.c
62
src/link.c
|
|
@ -242,21 +242,6 @@ static const u8 sLinkErrorTextColor[] = { 0x00, 0x01, 0x02 };
|
|||
|
||||
bool8 IsWirelessAdapterConnected(void)
|
||||
{
|
||||
if (QL_IS_PLAYBACK_STATE)
|
||||
return FALSE;
|
||||
|
||||
SetWirelessCommType1();
|
||||
InitRFUAPI();
|
||||
RfuSetIgnoreError(TRUE);
|
||||
if (rfu_LMAN_REQBN_softReset_and_checkID() == RFU_ID)
|
||||
{
|
||||
rfu_REQ_stopMode();
|
||||
rfu_waitREQComplete();
|
||||
return TRUE;
|
||||
}
|
||||
SetWirelessCommType0_Internal();
|
||||
CloseLink();
|
||||
RestoreSerialTimer3IntrHandlers();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
@ -1637,53 +1622,6 @@ void LinkPlayerFromBlock(u32 who)
|
|||
// When this function returns TRUE the callbacks are skipped
|
||||
bool8 HandleLinkConnection(void)
|
||||
{
|
||||
bool32 main1Failed;
|
||||
bool32 main2Failed;
|
||||
|
||||
if (gWirelessCommType == 0)
|
||||
{
|
||||
gLinkStatus = LinkMain1(&gShouldAdvanceLinkState, gSendCmd, gRecvCmds);
|
||||
LinkMain2(&gMain.heldKeys);
|
||||
if ((gLinkStatus & LINK_STAT_RECEIVED_NOTHING) && IsSendingKeysOverCable() == TRUE)
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if REVISION >= 0xA
|
||||
bool32 reloadOrReset = FALSE;
|
||||
if (svc_51())
|
||||
{
|
||||
// Documentation issue:
|
||||
// Rfu_IsMaster supposedly returns bool8, but just returns gRfu.ParentChild
|
||||
// That value has three states, see librfu.h. One of those states is MODE_NEUTRAL (0xFF) which means init.
|
||||
// So the last condition basically means "rfu link status is connected, not initialising".
|
||||
// As in, it's true if value is MODE_CHILD or MODE_PARENT but not MODE_NEUTRAL (because unsigned cmp).
|
||||
if (!FuncIsActiveTask(Task_WirelessCommunicationScreen) && (InUnionRoom() || gReceivedRemoteLinkPlayers != 0 || Rfu_IsMaster() <= MODE_PARENT))
|
||||
{
|
||||
reloadOrReset = TRUE;
|
||||
}
|
||||
CloseLink();
|
||||
}
|
||||
#endif
|
||||
main1Failed = RfuMain1(); // Always returns FALSE
|
||||
main2Failed = RfuMain2();
|
||||
#if REVISION >= 0xA
|
||||
if (reloadOrReset)
|
||||
{
|
||||
// If active task is mystery gift then soft reset, otherwise reload the save.
|
||||
if (FuncIsActiveTask(Task_MysteryGift)) RfuSoftReset();
|
||||
else RfuReloadSave();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (IsSendingKeysOverCable() == TRUE)
|
||||
{
|
||||
// This will never be reached.
|
||||
// IsSendingKeysOverCable is always FALSE for wireless communication
|
||||
if (main1Failed == TRUE || IsRfuRecvQueueEmpty() || main2Failed)
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -315,14 +315,7 @@ void InitRFU(void)
|
|||
|
||||
void InitRFUAPI(void)
|
||||
{
|
||||
if (!rfu_initializeAPI(sRfuAPIBuffer, RFU_API_BUFF_SIZE_RAM, &gIntrTable[1], TRUE))
|
||||
{
|
||||
gLinkType = 0;
|
||||
// ClearSavedLinkPlayers(); // Em fix
|
||||
RfuSetIgnoreError(FALSE);
|
||||
ResetLinkRfuGFLayer();
|
||||
rfu_setTimerInterrupt(3, &gIntrTable[2]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void Task_ParentSearchForChildren(u8 taskId)
|
||||
|
|
|
|||
|
|
@ -102,18 +102,18 @@ void MoveSaveBlocks_ResetHeap(void)
|
|||
pokemonStorageCopy = (struct PokemonStorage *)(gHeap + sizeof(struct SaveBlock2) + sizeof(struct SaveBlock1));
|
||||
|
||||
// backup the saves.
|
||||
*saveBlock2Copy = *gSaveBlock2Ptr;
|
||||
*saveBlock1Copy = *gSaveBlock1Ptr;
|
||||
*pokemonStorageCopy = *gPokemonStoragePtr;
|
||||
CpuCopy32(gSaveBlock2Ptr, saveBlock2Copy, sizeof(*gSaveBlock2Ptr));
|
||||
CpuCopy32(gSaveBlock1Ptr, saveBlock1Copy, sizeof(*gSaveBlock1Ptr));
|
||||
CpuCopy32(gPokemonStoragePtr, pokemonStorageCopy, sizeof(*gPokemonStoragePtr));
|
||||
|
||||
// change saveblocks' pointers
|
||||
SetSaveBlocksPointers(); // unlike Emerald, this does not use
|
||||
// the trainer ID sum for an offset.
|
||||
|
||||
// restore saveblock data since the pointers changed
|
||||
*gSaveBlock2Ptr = *saveBlock2Copy;
|
||||
*gSaveBlock1Ptr = *saveBlock1Copy;
|
||||
*gPokemonStoragePtr = *pokemonStorageCopy;
|
||||
CpuCopy32(saveBlock2Copy, gSaveBlock2Ptr, sizeof(*saveBlock2Copy));
|
||||
CpuCopy32(saveBlock1Copy, gSaveBlock1Ptr, sizeof(*saveBlock1Copy));
|
||||
CpuCopy32(pokemonStorageCopy, gPokemonStoragePtr, sizeof(*pokemonStorageCopy));
|
||||
|
||||
// heap was destroyed in the copying process, so reset it
|
||||
InitHeap(gHeap, HEAP_SIZE);
|
||||
|
|
|
|||
80
src/m4a.c
80
src/m4a.c
|
|
@ -1,29 +1,30 @@
|
|||
#include "global.h"
|
||||
#include "gba/m4a_internal.h"
|
||||
|
||||
#include "cgb_audio.h"
|
||||
|
||||
extern const u8 gCgb3Vol[];
|
||||
|
||||
#define BSS_CODE __attribute__((section(".bss.code")))
|
||||
|
||||
BSS_CODE ALIGNED(4) char SoundMainRAM_Buffer[0x800] = {0};
|
||||
|
||||
struct SoundInfo gSoundInfo = {0};
|
||||
struct PokemonCrySong gPokemonCrySongs[MAX_POKEMON_CRIES] = {0};
|
||||
struct MusicPlayerInfo gPokemonCryMusicPlayers[MAX_POKEMON_CRIES] = {0};
|
||||
MPlayFunc gMPlayJumpTable[36] = {0};
|
||||
struct CgbChannel gCgbChans[4] = {0};
|
||||
struct MusicPlayerTrack gPokemonCryTracks[MAX_POKEMON_CRIES * 2] = {0};
|
||||
struct PokemonCrySong gPokemonCrySong = {0};
|
||||
struct MusicPlayerInfo gMPlayInfo_BGM = {0};
|
||||
struct MusicPlayerInfo gMPlayInfo_SE1 = {0};
|
||||
struct MusicPlayerInfo gMPlayInfo_SE2 = {0};
|
||||
u8 gMPlayMemAccArea[0x10] = {0};
|
||||
struct MusicPlayerInfo gMPlayInfo_SE3 = {0};
|
||||
|
||||
struct SoundInfo gSoundInfo;
|
||||
struct PokemonCrySong gPokemonCrySongs[MAX_POKEMON_CRIES];
|
||||
struct MusicPlayerInfo gPokemonCryMusicPlayers[MAX_POKEMON_CRIES];
|
||||
MPlayFunc gMPlayJumpTable[36];
|
||||
struct CgbChannel gCgbChans[4];
|
||||
struct MusicPlayerTrack gPokemonCryTracks[MAX_POKEMON_CRIES * 2];
|
||||
struct PokemonCrySong gPokemonCrySong;
|
||||
struct MusicPlayerInfo gMPlayInfo_BGM;
|
||||
struct MusicPlayerInfo gMPlayInfo_SE1;
|
||||
struct MusicPlayerInfo gMPlayInfo_SE2;
|
||||
struct MusicPlayerInfo gMPlayInfo_SE3;
|
||||
struct MusicPlayerTrack gMPlayTrack_BGM[10];
|
||||
struct MusicPlayerTrack gMPlayTrack_SE1[3];
|
||||
struct MusicPlayerTrack gMPlayTrack_SE2[9];
|
||||
struct MusicPlayerTrack gMPlayTrack_SE3[1];
|
||||
u8 gMPlayMemAccArea[0x10];
|
||||
|
||||
void MP2K_event_nxx();
|
||||
void MP2KPlayerMain();
|
||||
|
||||
u32 MidiKeyToFreq(struct WaveData *wav, u8 key, u8 fineAdjust)
|
||||
{
|
||||
|
|
@ -76,12 +77,10 @@ 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
|
||||
| SOUND_MODE_FREQ_42048
|
||||
| (12 << SOUND_MODE_MASVOL_SHIFT)
|
||||
| (5 << SOUND_MODE_MAXCHN_SHIFT));
|
||||
|
||||
|
|
@ -106,7 +105,7 @@ void m4aSoundInit(void)
|
|||
|
||||
void m4aSoundMain(void)
|
||||
{
|
||||
SoundMain();
|
||||
//RunMixerFrame();
|
||||
}
|
||||
|
||||
void m4aSongNumStart(u16 n)
|
||||
|
|
@ -279,6 +278,11 @@ void MPlayExtender(struct CgbChannel *cgbChans)
|
|||
REG_NR30 = 0;
|
||||
REG_NR50 = 0x77;
|
||||
|
||||
for(u8 i = 0; i < 4; i++){
|
||||
cgb_set_envelope(i, 8);
|
||||
cgb_trigger_note(i);
|
||||
}
|
||||
|
||||
soundInfo = SOUND_INFO_PTR;
|
||||
|
||||
ident = soundInfo->ident;
|
||||
|
|
@ -290,10 +294,10 @@ void MPlayExtender(struct CgbChannel *cgbChans)
|
|||
|
||||
#if __STDC_VERSION__ < 202311L
|
||||
gMPlayJumpTable[8] = ply_memacc;
|
||||
gMPlayJumpTable[17] = ply_lfos;
|
||||
gMPlayJumpTable[19] = ply_mod;
|
||||
gMPlayJumpTable[17] = MP2K_event_lfos;
|
||||
gMPlayJumpTable[19] = MP2K_event_mod;
|
||||
gMPlayJumpTable[28] = ply_xcmd;
|
||||
gMPlayJumpTable[29] = ply_endtie;
|
||||
gMPlayJumpTable[29] = MP2K_event_endtie;
|
||||
gMPlayJumpTable[30] = SampleFreqSet;
|
||||
gMPlayJumpTable[31] = TrackStop;
|
||||
gMPlayJumpTable[32] = FadeOutBody;
|
||||
|
|
@ -386,7 +390,7 @@ void SoundInit(struct SoundInfo *soundInfo)
|
|||
|
||||
soundInfo->maxChans = 8;
|
||||
soundInfo->masterVolume = 15;
|
||||
soundInfo->plynote = ply_note;
|
||||
soundInfo->plynote = MP2K_event_nxx;
|
||||
soundInfo->CgbSound = DummyFunc;
|
||||
soundInfo->CgbOscOff = (CgbOscOffFunc)DummyFunc;
|
||||
soundInfo->MidiKeyToCgbFreq = (MidiKeyToCgbFreqFunc)DummyFunc;
|
||||
|
|
@ -396,7 +400,7 @@ void SoundInit(struct SoundInfo *soundInfo)
|
|||
|
||||
soundInfo->MPlayJumpTable = gMPlayJumpTable;
|
||||
|
||||
SampleFreqSet(SOUND_MODE_FREQ_13379);
|
||||
SampleFreqSet(SOUND_MODE_FREQ_42048);
|
||||
|
||||
soundInfo->ident = ID_NUMBER;
|
||||
}
|
||||
|
|
@ -407,14 +411,12 @@ void SampleFreqSet(u32 freq)
|
|||
|
||||
freq = (freq & 0xF0000) >> 16;
|
||||
soundInfo->freq = freq;
|
||||
soundInfo->pcmSamplesPerVBlank = gPcmSamplesPerVBlankTable[freq - 1];
|
||||
soundInfo->pcmSamplesPerVBlank = 701;
|
||||
soundInfo->pcmDmaPeriod = PCM_DMA_BUF_SIZE / soundInfo->pcmSamplesPerVBlank;
|
||||
|
||||
// LCD refresh rate 59.7275Hz
|
||||
soundInfo->pcmFreq = (597275 * soundInfo->pcmSamplesPerVBlank + 5000) / 10000;
|
||||
soundInfo->pcmFreq = 60.0f * soundInfo->pcmSamplesPerVBlank;
|
||||
|
||||
// CPU frequency 16.78Mhz
|
||||
soundInfo->divFreq = (16777216 / soundInfo->pcmFreq + 1) >> 1;
|
||||
soundInfo->divFreq = 1.0f / soundInfo->pcmFreq;
|
||||
|
||||
// Turn off timer 0.
|
||||
REG_TM0CNT_H = 0;
|
||||
|
|
@ -424,12 +426,6 @@ void SampleFreqSet(u32 freq)
|
|||
|
||||
m4aSoundVSyncOn();
|
||||
|
||||
while (*(vu8 *)REG_ADDR_VCOUNT == 159)
|
||||
;
|
||||
|
||||
while (*(vu8 *)REG_ADDR_VCOUNT != 159)
|
||||
;
|
||||
|
||||
REG_TM0CNT_H = TIMER_ENABLE | TIMER_1CLK;
|
||||
}
|
||||
|
||||
|
|
@ -606,8 +602,8 @@ void MPlayOpen(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track
|
|||
soundInfo->MPlayMainHead = NULL;
|
||||
}
|
||||
|
||||
soundInfo->musicPlayerHead = mplayInfo;
|
||||
soundInfo->MPlayMainHead = MPlayMain;
|
||||
soundInfo->musicPlayerHead = (u32)mplayInfo;
|
||||
soundInfo->MPlayMainHead = (u32)MP2KPlayerMain;
|
||||
soundInfo->ident = ID_NUMBER;
|
||||
mplayInfo->ident = ID_NUMBER;
|
||||
}
|
||||
|
|
@ -877,6 +873,8 @@ void CgbOscOff(u8 chanNum)
|
|||
REG_NR42 = 8;
|
||||
REG_NR44 = 0x80;
|
||||
}
|
||||
cgb_set_envelope(chanNum - 1, 8);
|
||||
cgb_trigger_note(chanNum - 1);
|
||||
}
|
||||
|
||||
static inline int CgbPan(struct CgbChannel *chan)
|
||||
|
|
@ -1000,6 +998,7 @@ void CgbSound(void)
|
|||
{
|
||||
case 1:
|
||||
*nrx0ptr = channels->sweep;
|
||||
cgb_set_sweep(channels->sweep);
|
||||
// fallthrough
|
||||
case 2:
|
||||
*nrx1ptr = ((u32)channels->wavePointer << 6) + channels->length;
|
||||
|
|
@ -1013,6 +1012,7 @@ void CgbSound(void)
|
|||
REG_WAVE_RAM2 = channels->wavePointer[2];
|
||||
REG_WAVE_RAM3 = channels->wavePointer[3];
|
||||
channels->currentPointer = channels->wavePointer;
|
||||
cgb_set_wavram();
|
||||
}
|
||||
*nrx0ptr = 0;
|
||||
*nrx1ptr = channels->length;
|
||||
|
|
@ -1032,6 +1032,7 @@ void CgbSound(void)
|
|||
channels->n4 = 0x00;
|
||||
break;
|
||||
}
|
||||
cgb_set_length(ch - 1, channels->length);
|
||||
channels->envelopeCounter = channels->attack;
|
||||
if ((s8)(channels->attack & mask))
|
||||
{
|
||||
|
|
@ -1228,6 +1229,9 @@ void CgbSound(void)
|
|||
if (ch == 1 && !(*nrx0ptr & 0x08))
|
||||
*nrx4ptr = channels->n4 | 0x80;
|
||||
}
|
||||
cgb_set_envelope(ch - 1, *nrx2ptr);
|
||||
cgb_toggle_length(ch - 1, (*nrx4ptr & 0x40));
|
||||
cgb_trigger_note(ch - 1);
|
||||
}
|
||||
|
||||
channel_complete:
|
||||
|
|
|
|||
10
src/m4a_1.c
10
src/m4a_1.c
|
|
@ -1,13 +1,12 @@
|
|||
#include "global.h"
|
||||
|
||||
//stubs
|
||||
void umul3232H32(){}
|
||||
void SoundMain(){}
|
||||
void SoundMainRAM(){}
|
||||
void SoundMainBTM(){}
|
||||
|
||||
void RealClearChain(){}
|
||||
void ply_fine(){}
|
||||
void MPlayJumpTableCopy(){}
|
||||
|
||||
void ply_goto(){}
|
||||
void ply_patt(){}
|
||||
void ply_pend(){}
|
||||
|
|
@ -24,10 +23,9 @@ void ply_lfodl(){}
|
|||
void ply_modt(){}
|
||||
void ply_tune(){}
|
||||
void ply_port(){}
|
||||
void m4aSoundVSync(){}
|
||||
|
||||
void MPlayMain(){}
|
||||
void TrackStop(){}
|
||||
void ChnVolSetAsm(){}
|
||||
|
||||
void ply_note(){}
|
||||
void ply_endtie(){}
|
||||
void clear_modM(){}
|
||||
|
|
|
|||
|
|
@ -5,36 +5,36 @@
|
|||
// 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,
|
||||
MP2K_event_fine,
|
||||
MP2K_event_goto,
|
||||
MP2K_event_patt,
|
||||
MP2K_event_pend,
|
||||
MP2K_event_rept,
|
||||
MP2K_event_fine,
|
||||
MP2K_event_fine,
|
||||
MP2K_event_fine,
|
||||
MP2K_event_fine,
|
||||
MP2K_event_prio,
|
||||
MP2K_event_tempo,
|
||||
MP2K_event_keysh,
|
||||
MP2K_event_voice,
|
||||
MP2K_event_vol,
|
||||
MP2K_event_pan,
|
||||
MP2K_event_bend,
|
||||
MP2K_event_bendr,
|
||||
MP2K_event_lfos,
|
||||
MP2K_event_lfodl,
|
||||
MP2K_event_mod,
|
||||
MP2K_event_modt,
|
||||
MP2K_event_fine,
|
||||
MP2K_event_fine,
|
||||
MP2K_event_tune,
|
||||
MP2K_event_fine,
|
||||
MP2K_event_fine,
|
||||
MP2K_event_fine,
|
||||
MP2K_event_port,
|
||||
MP2K_event_fine,
|
||||
MP2K_event_endtie,
|
||||
SampleFreqSet,
|
||||
TrackStop,
|
||||
FadeOutBody,
|
||||
|
|
|
|||
35
src/main.c
35
src/main.c
|
|
@ -99,12 +99,6 @@ void EnableVCountIntrAtLine150(void);
|
|||
|
||||
#define B_START_SELECT (B_BUTTON | START_BUTTON | SELECT_BUTTON)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
AgbMain();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AgbMain()
|
||||
{
|
||||
#if REVISION >= 0xA
|
||||
|
|
@ -122,7 +116,7 @@ void AgbMain()
|
|||
InitIntrHandlers();
|
||||
m4aSoundInit();
|
||||
EnableVCountIntrAtLine150();
|
||||
InitRFU();
|
||||
//InitRFU();
|
||||
CheckForFlashMemory();
|
||||
InitMainCallbacks();
|
||||
InitMapMusic();
|
||||
|
|
@ -136,23 +130,14 @@ void AgbMain()
|
|||
|
||||
SetNotInSaveFailedScreen();
|
||||
|
||||
// Revision 10 has no calls into libisagbprn except this one.
|
||||
#if !defined(NDEBUG) || REVISION >= 0xA
|
||||
#if (LOG_HANDLER == LOG_HANDLER_MGBA_PRINT)
|
||||
(void) MgbaOpen();
|
||||
#elif (LOG_HANDLER == LOG_HANDLER_AGB_PRINT)
|
||||
AGBPrintInit();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if REVISION >= 1
|
||||
if (gFlashMemoryPresent != TRUE)
|
||||
SetMainCallback2(NULL);
|
||||
#endif
|
||||
|
||||
gLinkTransferringData = FALSE;
|
||||
|
||||
for (;;)
|
||||
int i=0;
|
||||
for (;;i++)
|
||||
{
|
||||
ReadKeys();
|
||||
|
||||
|
|
@ -190,6 +175,7 @@ void AgbMain()
|
|||
|
||||
PlayTimeCounter_Update();
|
||||
MapMusicMain();
|
||||
|
||||
WaitForVBlank();
|
||||
}
|
||||
}
|
||||
|
|
@ -272,7 +258,7 @@ void InitKeys(void)
|
|||
|
||||
static void ReadKeys(void)
|
||||
{
|
||||
u16 keyInput = REG_KEYINPUT ^ KEYS_MASK;
|
||||
u16 keyInput = Platform_GetKeyInput();
|
||||
gMain.newKeysRaw = keyInput & ~gMain.heldKeysRaw;
|
||||
gMain.newKeys = gMain.newKeysRaw;
|
||||
gMain.newAndRepeatedKeys = gMain.newKeysRaw;
|
||||
|
|
@ -363,13 +349,11 @@ static void VBlankIntr(void)
|
|||
RfuVSync();
|
||||
else if (!gLinkVSyncDisabled)
|
||||
LinkVSync();
|
||||
|
||||
if (gMain.vblankCounter1)
|
||||
(*gMain.vblankCounter1)++;
|
||||
|
||||
if (gMain.vblankCallback)
|
||||
gMain.vblankCallback();
|
||||
|
||||
gMain.vblankCounter2++;
|
||||
|
||||
CopyBufferedValuesToGpuRegs();
|
||||
|
|
@ -380,11 +364,13 @@ static void VBlankIntr(void)
|
|||
#if !defined(NDEBUG) || REVISION >= 0xA
|
||||
sVcountBeforeSound = REG_VCOUNT;
|
||||
#endif
|
||||
|
||||
m4aSoundMain();
|
||||
|
||||
#if !defined(NDEBUG) || REVISION >= 0xA
|
||||
sVcountAfterSound = REG_VCOUNT;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
TryReceiveLinkBattleData();
|
||||
Random();
|
||||
UpdateWirelessStatusIndicatorSprite();
|
||||
|
|
@ -438,10 +424,7 @@ static void IntrDummy(void)
|
|||
|
||||
static void WaitForVBlank(void)
|
||||
{
|
||||
gMain.intrCheck &= ~INTR_FLAG_VBLANK;
|
||||
|
||||
while (!(gMain.intrCheck & INTR_FLAG_VBLANK))
|
||||
;
|
||||
VBlankIntrWait();
|
||||
}
|
||||
|
||||
void SetVBlankCounter1Ptr(u32 *ptr)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include "global.h"
|
||||
#include "malloc.h"
|
||||
|
||||
u8 gHeap[HEAP_SIZE] = {0};
|
||||
ALIGNED(4) u8 gHeap[HEAP_SIZE] = {0};
|
||||
|
||||
static void *sHeapStart;
|
||||
static u32 sHeapSize;
|
||||
|
|
|
|||
405
src/multiboot.c
405
src/multiboot.c
|
|
@ -2,403 +2,8 @@
|
|||
#include "multiboot.h"
|
||||
|
||||
static u16 MultiBoot_required_data[MULTIBOOT_NCHILD];
|
||||
|
||||
static int MultiBootSend(struct MultiBootParam *mp, u16 data);
|
||||
static int MultiBootHandShake(struct MultiBootParam *mp);
|
||||
static void MultiBootWaitCycles(u32 cycles);
|
||||
static void MultiBootWaitSendDone(void);
|
||||
|
||||
void MultiBootInit(struct MultiBootParam *mp)
|
||||
{
|
||||
mp->client_bit = 0;
|
||||
mp->probe_count = 0;
|
||||
mp->response_bit = 0;
|
||||
mp->check_wait = MULTIBOOT_CONNECTION_CHECK_WAIT;
|
||||
mp->sendflag = 0;
|
||||
mp->handshake_timeout = 0;
|
||||
REG_RCNT = 0;
|
||||
REG_SIOCNT = SIO_MULTI_MODE | SIO_115200_BPS;
|
||||
REG_SIODATA8 = 0;
|
||||
}
|
||||
|
||||
int MultiBootMain(struct MultiBootParam *mp)
|
||||
{
|
||||
int i, j, k;
|
||||
|
||||
if (MultiBootCheckComplete(mp))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (mp->check_wait > MULTIBOOT_CONNECTION_CHECK_WAIT)
|
||||
{
|
||||
mp->check_wait--;
|
||||
return 0;
|
||||
}
|
||||
output_burst:
|
||||
if (mp->sendflag)
|
||||
{
|
||||
mp->sendflag = 0;
|
||||
|
||||
i = REG_SIOCNT & (SIO_MULTI_BUSY | SIO_ERROR | SIO_ID | SIO_MULTI_SD | SIO_MULTI_SI);
|
||||
if (i != SIO_MULTI_SD)
|
||||
{
|
||||
MultiBootInit(mp);
|
||||
return i ^ SIO_MULTI_SD;
|
||||
}
|
||||
}
|
||||
if (mp->probe_count >= 0xe0)
|
||||
{
|
||||
i = MultiBootHandShake(mp);
|
||||
if (i)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
if (mp->server_type == MULTIBOOT_SERVER_TYPE_QUICK
|
||||
&& mp->probe_count > 0xe1
|
||||
&& MultiBootCheckComplete(mp) == 0)
|
||||
{
|
||||
MultiBootWaitSendDone();
|
||||
goto output_burst;
|
||||
}
|
||||
|
||||
if (MultiBootCheckComplete(mp) == 0)
|
||||
{
|
||||
if (mp->handshake_timeout == 0)
|
||||
{
|
||||
MultiBootInit(mp);
|
||||
return MULTIBOOT_ERROR_HANDSHAKE_FAILURE;
|
||||
}
|
||||
mp->handshake_timeout--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
switch (mp->probe_count)
|
||||
{
|
||||
case 0:
|
||||
k = 0x0e;
|
||||
for (i = MULTIBOOT_NCHILD; i != 0; i--)
|
||||
{
|
||||
if (*(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2) != 0xffff)
|
||||
{
|
||||
break;
|
||||
}
|
||||
k >>= 1;
|
||||
}
|
||||
k &= 0x0e;
|
||||
mp->response_bit = k;
|
||||
for (i = MULTIBOOT_NCHILD; i != 0; i--)
|
||||
{
|
||||
j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
|
||||
if (mp->client_bit & (1 << i))
|
||||
{
|
||||
if (j != ((MULTIBOOT_CLIENT_INFO << 8) | (1 << i)))
|
||||
{
|
||||
k = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mp->client_bit &= k;
|
||||
if (k == 0)
|
||||
{
|
||||
mp->check_wait = MULTIBOOT_CONNECTION_CHECK_WAIT;
|
||||
}
|
||||
|
||||
if (mp->check_wait)
|
||||
{
|
||||
mp->check_wait--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mp->response_bit != mp->client_bit)
|
||||
{
|
||||
MultiBootStartProbe(mp);
|
||||
goto case_1;
|
||||
}
|
||||
}
|
||||
output_master_info:
|
||||
return MultiBootSend(mp, (MULTIBOOT_MASTER_INFO << 8) | mp->client_bit);
|
||||
case_1:
|
||||
case 1:
|
||||
mp->probe_target_bit = 0;
|
||||
for (i = MULTIBOOT_NCHILD; i != 0; i--)
|
||||
{
|
||||
j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
|
||||
if ((j >> 8) == MULTIBOOT_CLIENT_INFO)
|
||||
{
|
||||
MultiBoot_required_data[i - 1] = j;
|
||||
j &= 0xff;
|
||||
if (j == (1 << i))
|
||||
{
|
||||
mp->probe_target_bit |= j;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mp->response_bit != mp->probe_target_bit)
|
||||
{
|
||||
goto output_master_info;
|
||||
}
|
||||
mp->probe_count = 2;
|
||||
return MultiBootSend(mp, (MULTIBOOT_MASTER_START_PROBE << 8) | mp->probe_target_bit);
|
||||
case 2:
|
||||
for (i = MULTIBOOT_NCHILD; i != 0; i--)
|
||||
{
|
||||
if (mp->probe_target_bit & (1 << i))
|
||||
{
|
||||
j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
|
||||
if (j != MultiBoot_required_data[i - 1])
|
||||
{
|
||||
mp->probe_target_bit ^= 1 << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
goto output_header;
|
||||
case 0xd0:
|
||||
k = 1;
|
||||
for (i = MULTIBOOT_NCHILD; i != 0; i--)
|
||||
{
|
||||
j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
|
||||
mp->client_data[i - 1] = j;
|
||||
if (mp->probe_target_bit & (1 << i))
|
||||
{
|
||||
if ((j >> 8) != MULTIBOOT_CLIENT_INFO
|
||||
&& (j >> 8) != MULTIBOOT_CLIENT_DLREADY)
|
||||
{
|
||||
MultiBootInit(mp);
|
||||
return MULTIBOOT_ERROR_NO_DLREADY;
|
||||
}
|
||||
if (j == MultiBoot_required_data[i - 1])
|
||||
{
|
||||
k = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (k == 0)
|
||||
{
|
||||
return MultiBootSend(mp, (MULTIBOOT_MASTER_REQUEST_DLREADY << 8) | mp->palette_data);
|
||||
}
|
||||
mp->probe_count = 0xd1;
|
||||
k = 0x11;
|
||||
for (i = MULTIBOOT_NCHILD; i != 0; i--)
|
||||
{
|
||||
k += mp->client_data[i - 1];
|
||||
}
|
||||
mp->handshake_data = k;
|
||||
return MultiBootSend(mp, (MULTIBOOT_MASTER_START_DL << 8) | (k & 0xff));
|
||||
case 0xd1:
|
||||
for (i = MULTIBOOT_NCHILD; i != 0; i--)
|
||||
{
|
||||
j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
|
||||
if (mp->probe_target_bit & (1 << i))
|
||||
{
|
||||
if ((j >> 8) != MULTIBOOT_CLIENT_DLREADY)
|
||||
{
|
||||
MultiBootInit(mp);
|
||||
return MULTIBOOT_ERROR_NO_DLREADY;
|
||||
}
|
||||
}
|
||||
}
|
||||
i = MultiBoot(mp);
|
||||
if (i == 0)
|
||||
{
|
||||
mp->probe_count = 0xe0;
|
||||
mp->handshake_timeout = MULTIBOOT_HANDSHAKE_TIMEOUT;
|
||||
return 0;
|
||||
}
|
||||
MultiBootInit(mp);
|
||||
mp->check_wait = MULTIBOOT_CONNECTION_CHECK_WAIT * 2;
|
||||
return MULTIBOOT_ERROR_BOOT_FAILURE;
|
||||
default:
|
||||
for (i = MULTIBOOT_NCHILD; i != 0; i--)
|
||||
{
|
||||
if (mp->probe_target_bit & (1 << i))
|
||||
{
|
||||
j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
|
||||
if ((j >> 8) != (MULTIBOOT_MASTER_START_PROBE + 1 - (mp->probe_count >> 1))
|
||||
|| ((j & 0xff) != (1 << i)))
|
||||
{
|
||||
mp->probe_target_bit ^= 1 << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mp->probe_count == 0xc4)
|
||||
{
|
||||
mp->client_bit = mp->probe_target_bit & 0x0e;
|
||||
mp->probe_count = 0;
|
||||
goto output_master_info;
|
||||
}
|
||||
output_header:
|
||||
if (mp->probe_target_bit == 0)
|
||||
{
|
||||
MultiBootInit(mp);
|
||||
return MULTIBOOT_ERROR_NO_PROBE_TARGET;
|
||||
}
|
||||
mp->probe_count += 2;
|
||||
if (mp->probe_count == 0xc4)
|
||||
{
|
||||
goto output_master_info;
|
||||
}
|
||||
i = MultiBootSend(mp,
|
||||
(mp->masterp[mp->probe_count - 4 + 1] << 8)
|
||||
| mp->masterp[mp->probe_count - 4]);
|
||||
if (i)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
if (mp->server_type == MULTIBOOT_SERVER_TYPE_QUICK)
|
||||
{
|
||||
MultiBootWaitSendDone();
|
||||
goto output_burst;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int MultiBootSend(struct MultiBootParam *mp, u16 data)
|
||||
{
|
||||
int i = REG_SIOCNT & (SIO_MULTI_BUSY | SIO_MULTI_SD | SIO_MULTI_SI);
|
||||
if (i != SIO_MULTI_SD)
|
||||
{
|
||||
MultiBootInit(mp);
|
||||
return i ^ SIO_MULTI_SD;
|
||||
}
|
||||
REG_SIODATA8 = data;
|
||||
REG_SIOCNT = SIO_MULTI_MODE | SIO_START | SIO_115200_BPS;
|
||||
mp->sendflag = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MultiBootStartProbe(struct MultiBootParam *mp)
|
||||
{
|
||||
if (mp->probe_count != 0)
|
||||
{
|
||||
MultiBootInit(mp);
|
||||
return;
|
||||
}
|
||||
mp->check_wait = 0;
|
||||
mp->client_bit = 0;
|
||||
mp->probe_count = 1;
|
||||
}
|
||||
|
||||
void MultiBootStartMaster(struct MultiBootParam *mp, const u8 *srcp, int length, u8 palette_color, s8 palette_speed)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (mp->probe_count != 0
|
||||
|| mp->client_bit == 0
|
||||
|| mp->check_wait != 0)
|
||||
{
|
||||
MultiBootInit(mp);
|
||||
return;
|
||||
}
|
||||
mp->boot_srcp = srcp;
|
||||
length = (length + 15) & ~15;
|
||||
if (length < MULTIBOOT_SEND_SIZE_MIN || length > MULTIBOOT_SEND_SIZE_MAX)
|
||||
{
|
||||
MultiBootInit(mp);
|
||||
return;
|
||||
}
|
||||
mp->boot_endp = srcp + length;
|
||||
switch (palette_speed)
|
||||
{
|
||||
case -4:
|
||||
case -3:
|
||||
case -2:
|
||||
case -1:
|
||||
i = (palette_color << 3) | (3 - palette_speed);
|
||||
break;
|
||||
case 0:
|
||||
i = 0x38 | palette_color;
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
i = (palette_color << 3) | (palette_speed - 1);
|
||||
break;
|
||||
}
|
||||
mp->palette_data = ((i & 0x3f) << 1) | 0x81;
|
||||
mp->probe_count = 0xd0;
|
||||
}
|
||||
|
||||
bool32 MultiBootCheckComplete(struct MultiBootParam *mp)
|
||||
{
|
||||
if (mp->probe_count == 0xe9)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int MultiBootHandShake(struct MultiBootParam *mp)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
#define send_data (mp->system_work[0])
|
||||
#define must_data (mp->system_work[1])
|
||||
switch (mp->probe_count)
|
||||
{
|
||||
case_0xe0:
|
||||
case 0xe0:
|
||||
mp->probe_count = 0xe1;
|
||||
must_data = 0x0000;
|
||||
send_data = 0x100000;
|
||||
return MultiBootSend(mp, 0x0000);
|
||||
default:
|
||||
for (i = MULTIBOOT_NCHILD; i != 0; i--)
|
||||
{
|
||||
j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
|
||||
if ((mp->client_bit & (1 << i))
|
||||
&& j != must_data)
|
||||
{
|
||||
goto case_0xe0;
|
||||
}
|
||||
}
|
||||
mp->probe_count++;
|
||||
must_data = send_data & 0xffff;
|
||||
if (send_data == 0x0000)
|
||||
{
|
||||
must_data = mp->masterp[0xac] | (mp->masterp[0xad] << 8);
|
||||
send_data = must_data << 5;
|
||||
}
|
||||
send_data >>= 5;
|
||||
output_common:
|
||||
return MultiBootSend(mp, send_data);
|
||||
case 0xe7:
|
||||
case 0xe8:
|
||||
for (i = MULTIBOOT_NCHILD; i != 0; i--)
|
||||
{
|
||||
j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
|
||||
if ((mp->client_bit & (1 << i)) && j != must_data)
|
||||
{
|
||||
MultiBootInit(mp);
|
||||
return MULTIBOOT_ERROR_HANDSHAKE_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
mp->probe_count++;
|
||||
if (mp->probe_count == 0xe9)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
send_data = mp->masterp[0xae] | (mp->masterp[0xaf] << 8);
|
||||
must_data = send_data;
|
||||
goto output_common;
|
||||
}
|
||||
#undef send_data
|
||||
#undef must_data
|
||||
}
|
||||
|
||||
static NOINLINE void MultiBootWaitCycles(u32 cycles)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void MultiBootWaitSendDone(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; (i < 31069) && (REG_SIOCNT & SIO_START); i++);
|
||||
MultiBootWaitCycles(600);
|
||||
}
|
||||
void MultiBootInit(struct MultiBootParam *mp){}
|
||||
int MultiBootMain(struct MultiBootParam *mp){}
|
||||
void MultiBootStartProbe(struct MultiBootParam *mp){}
|
||||
void MultiBootStartMaster(struct MultiBootParam *mp, const u8 *srcp, int length, u8 palette_color, s8 palette_speed){}
|
||||
bool32 MultiBootCheckComplete(struct MultiBootParam *mp){}
|
||||
806
src/music_player.c
Normal file
806
src/music_player.c
Normal file
|
|
@ -0,0 +1,806 @@
|
|||
#include "mp2k_common.h"
|
||||
#include "music_player.h"
|
||||
#include "gba/types.h"
|
||||
#include "gba/m4a_internal.h"
|
||||
#include "platform.h"
|
||||
|
||||
// Don't uncomment this. vvvvv
|
||||
// #define POKEMON_EXTENSIONS
|
||||
#define MIXED_AUDIO_BUFFER_SIZE 4907
|
||||
|
||||
static u32 MidiKeyToFreq(struct WaveData2 *wav, u8 key, u8 pitch);
|
||||
extern void * const gMPlayJumpTableTemplate[];
|
||||
extern const u8 gScaleTable[];
|
||||
extern const u32 gFreqTable[];
|
||||
extern const u8 gClockTable[];
|
||||
float audioBuffer [MIXED_AUDIO_BUFFER_SIZE];
|
||||
|
||||
u32 umul3232H32(u32 a, u32 b) {
|
||||
u64 result = a;
|
||||
result *= b;
|
||||
return result >> 32;
|
||||
}
|
||||
|
||||
void SoundMainBTM(void *ptr)
|
||||
{
|
||||
CpuFill32(0, ptr, 0x40);
|
||||
}
|
||||
|
||||
// Removes chan from the doubly-linked list of channels associated with chan->track.
|
||||
// Gonna rename this to like "FreeChannel" or something, similar to VGMS
|
||||
void MP2KClearChain(struct MixerSource *chan) {
|
||||
struct MP2KTrack *track = chan->track;
|
||||
if (chan->track == NULL) {
|
||||
return;
|
||||
}
|
||||
struct MixerSource *next = chan->next;
|
||||
struct MixerSource *prev = chan->prev;
|
||||
|
||||
if (prev != NULL) {
|
||||
prev->next = next;
|
||||
} else {
|
||||
track->chan = next;
|
||||
}
|
||||
|
||||
if (next != NULL) {
|
||||
next->prev = prev;
|
||||
}
|
||||
|
||||
chan->track = NULL;
|
||||
}
|
||||
|
||||
// In case newer compilers are too dumb to remove this logic at compile time.
|
||||
#define SKIP_GBA_BIOS_CHECKS
|
||||
// #define NOT_GBA
|
||||
|
||||
#if defined(SKIP_GBA_BIOS_CHECKS) || defined(NOT_GBA)
|
||||
#define VERIFY_PTR(x) do; while (0)
|
||||
#else
|
||||
#define VERIFY_PTR(x) do {\
|
||||
uintptr_t y = (uintptr_t)(x);\
|
||||
if (y < EWRAM_START && (y < (uintptr_t)&gMPlayJumpTableTemplate || y >= 0x40000)) {\
|
||||
ret = 0;\
|
||||
}\
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
static u8 SafeDereferenceU8(u8 *addr) {
|
||||
u8 ret = *addr;
|
||||
VERIFY_PTR(addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 SafeDereferenceU32(u32 *addr) {
|
||||
u32 ret = *addr;
|
||||
VERIFY_PTR(addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u8 *SafeDereferenceU8Ptr(u8 **addr) {
|
||||
u8 *ret = *addr;
|
||||
VERIFY_PTR(addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 *SafeDereferenceU32Ptr(u32 **addr) {
|
||||
u32 *ret = *addr;
|
||||
VERIFY_PTR(addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct WaveData2 *SafeDereferenceWavDataPtr(struct WaveData2 **addr) {
|
||||
struct WaveData2 *ret = *addr;
|
||||
VERIFY_PTR(addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct MP2KInstrument *SafeDereferenceMP2KInstrumentPtr(struct MP2KInstrument **addr) {
|
||||
struct MP2KInstrument *ret = *addr;
|
||||
VERIFY_PTR(addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *SafeDereferenceVoidPtr(void **addr) {
|
||||
void *ret = *addr;
|
||||
VERIFY_PTR(addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef NOT_GBA
|
||||
// I read an article about undefined behavior today so I'm feeling especially cautious
|
||||
// I'm definitely bringing this onto compiler explorer for some laughs... heh...
|
||||
// ...Sorry
|
||||
struct MP2KInstrument SafeDereferenceMP2KInstrument(struct MP2KInstrument *addr) {
|
||||
struct MP2KInstrument instrument;
|
||||
if (addr->type == SafeDereferenceU8(&addr->type)
|
||||
&& addr->drumKey == SafeDereferenceU8(&addr->drumKey)
|
||||
&& addr->cgbLength == SafeDereferenceU8(&addr->cgbLength)
|
||||
&& addr->panSweep == SafeDereferenceU8(&addr->panSweep)) {
|
||||
instrument.type = addr->type;
|
||||
instrument.drumKey = addr->drumKey;
|
||||
instrument.cgbLength = addr->cgbLength;
|
||||
instrument.panSweep = addr->panSweep;
|
||||
} else {
|
||||
instrument.type = 0;
|
||||
instrument.drumKey = 0;
|
||||
instrument.cgbLength = 0;
|
||||
instrument.panSweep = 0;
|
||||
}
|
||||
|
||||
// I don't know how much the optimizer can eff with weird union stuff so I might as well go through
|
||||
// all the steps to check which union member to access...?
|
||||
if (instrument.type & 0xC0) {
|
||||
if (addr->group == SafeDereferenceMP2KInstrumentPtr(&addr->group)) {
|
||||
instrument.group = addr->group;
|
||||
} else {
|
||||
instrument.group = NULL;
|
||||
}
|
||||
|
||||
if (addr->keySplitTable == SafeDereferenceU8Ptr(&addr->keySplitTable)) {
|
||||
instrument.keySplitTable = addr->keySplitTable;
|
||||
} else {
|
||||
instrument.keySplitTable = NULL;
|
||||
}
|
||||
return instrument;
|
||||
} else if (instrument.type & 0x7) {
|
||||
if ((instrument.type & 0x7) == 3) {
|
||||
if (addr->cgb3Sample == SafeDereferenceU32Ptr(&addr->cgb3Sample)) {
|
||||
instrument.cgb3Sample = addr->cgb3Sample;
|
||||
} else {
|
||||
instrument.cgb3Sample = NULL;
|
||||
}
|
||||
} else {
|
||||
if (addr->squareNoiseConfig == SafeDereferenceU32(&addr->squareNoiseConfig)) {
|
||||
instrument.squareNoiseConfig = addr->squareNoiseConfig;
|
||||
} else {
|
||||
instrument.squareNoiseConfig = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (addr->wav == SafeDereferenceWavDataPtr(&addr->wav)) {
|
||||
instrument.wav = addr->wav;
|
||||
} else {
|
||||
instrument.wav = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (addr->attack == SafeDereferenceU8(&addr->attack)
|
||||
&& addr->decay == SafeDereferenceU8(&addr->decay)
|
||||
&& addr->sustain == SafeDereferenceU8(&addr->sustain)
|
||||
&& addr->release == SafeDereferenceU8(&addr->release)) {
|
||||
instrument.attack = addr->attack;
|
||||
instrument.decay = addr->decay;
|
||||
instrument.sustain = addr->sustain;
|
||||
instrument.release = addr->release;
|
||||
} else {
|
||||
instrument.attack = 0;
|
||||
instrument.decay = 0;
|
||||
instrument.sustain = 0;
|
||||
instrument.release = 0;
|
||||
}
|
||||
return instrument;
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef VERIFY_PTR
|
||||
|
||||
u8 ConsumeTrackByte(struct MP2KTrack *track) {
|
||||
u8 *ptr = track->cmdPtr++;
|
||||
return SafeDereferenceU8(ptr);
|
||||
}
|
||||
|
||||
void MPlayJumpTableCopy(void **mplayJumpTable) {
|
||||
for (uf8 i = 0; i < 36; i++) {
|
||||
mplayJumpTable[i] = SafeDereferenceVoidPtr(&gMPlayJumpTableTemplate[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Ends the current track. (Fine as in the Italian musical word, not English)
|
||||
void MP2K_event_fine(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
struct MP2KTrack *r5 = track;
|
||||
for (struct MixerSource *chan = track->chan; chan != NULL; chan = chan->next) {
|
||||
if (chan->status & 0xC7) {
|
||||
chan->status |= 0x40;
|
||||
}
|
||||
MP2KClearChain(chan);
|
||||
}
|
||||
track->status = 0;
|
||||
}
|
||||
|
||||
// Sets the track's cmdPtr to the specified address.
|
||||
void MP2K_event_goto(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
#ifdef NOT_GBA
|
||||
u8 *addr;
|
||||
memcpy(&addr, track->cmdPtr, sizeof(u8 *));
|
||||
track->cmdPtr = addr;
|
||||
#else
|
||||
u8 *cmdPtr = track->cmdPtr;
|
||||
uintptr_t addr = 0;
|
||||
for (size_t i = sizeof(uintptr_t) - 1; i > 0; i--) {
|
||||
addr |= cmdPtr[i];
|
||||
addr <<= 8;
|
||||
}
|
||||
addr |= SafeDereferenceU8(cmdPtr);
|
||||
track->cmdPtr = (u8*)addr;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Sets the track's cmdPtr to the specified address after backing up its current position.
|
||||
void MP2K_event_patt(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
u8 level = track->patternLevel;
|
||||
if (level < 3) {
|
||||
track->patternStack[level] = track->cmdPtr + sizeof(u8 *);
|
||||
track->patternLevel++;
|
||||
MP2K_event_goto(unused, track);
|
||||
} else {
|
||||
// Stop playing this track, as an indication to the music programmer that they need to quit
|
||||
// nesting patterns so darn much.
|
||||
MP2K_event_fine(unused, track);
|
||||
}
|
||||
}
|
||||
|
||||
// Marks the end of the current pattern, if there is one, by resetting the pattern to the
|
||||
// most recently saved value.
|
||||
void MP2K_event_pend(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
if (track->patternLevel != 0) {
|
||||
u8 index = --track->patternLevel;
|
||||
track->cmdPtr = track->patternStack[index];
|
||||
}
|
||||
}
|
||||
|
||||
// Loops back until a REPT event has been reached the specified number of times
|
||||
void MP2K_event_rept(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
if (*track->cmdPtr == 0) {
|
||||
// "Repeat 0 times" == loop forever
|
||||
track->cmdPtr++;
|
||||
MP2K_event_goto(unused, track);
|
||||
} else {
|
||||
u8 repeatCount = ++track->repeatCount;
|
||||
if (repeatCount < ConsumeTrackByte(track)) {
|
||||
MP2K_event_goto(unused, track);
|
||||
} else {
|
||||
track->repeatCount = 0;
|
||||
track->cmdPtr += sizeof(u8) + sizeof(u8 *);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the note priority for new notes in this track.
|
||||
void MP2K_event_prio(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
track->priority = ConsumeTrackByte(track);
|
||||
}
|
||||
|
||||
// Sets the BPM of all tracks to the specified tempo (in beats per half-minute, because 255 as a max tempo
|
||||
// kinda sucks but 510 is plenty).
|
||||
void MP2K_event_tempo(struct MP2KPlayerState *player, struct MP2KTrack *track) {
|
||||
u16 bpm = ConsumeTrackByte(track);
|
||||
bpm *= 2;
|
||||
player->tempoRawBPM = bpm;
|
||||
player->tempoInterval = (bpm * player->tempoScale) / 256;
|
||||
}
|
||||
|
||||
void MP2K_event_keysh(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
track->keyShift = ConsumeTrackByte(track);
|
||||
track->status |= 0xC;
|
||||
}
|
||||
|
||||
void MP2K_event_voice(struct MP2KPlayerState *player, struct MP2KTrack *track) {
|
||||
u8 voice = *(track->cmdPtr++);
|
||||
struct MP2KInstrument *instrument = &player->voicegroup[voice];
|
||||
#ifdef NOT_GBA
|
||||
track->instrument = *instrument;
|
||||
#else
|
||||
track->instrument = SafeDereferenceMP2KInstrument(instrument);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MP2K_event_vol(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
track->vol = ConsumeTrackByte(track);
|
||||
track->status |= 0x3;
|
||||
}
|
||||
|
||||
void MP2K_event_pan(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
track->pan = ConsumeTrackByte(track) - 0x40;
|
||||
track->status |= 0x3;
|
||||
}
|
||||
|
||||
void MP2K_event_bend(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
track->bend = ConsumeTrackByte(track) - 0x40;
|
||||
track->status |= 0xC;
|
||||
}
|
||||
|
||||
void MP2K_event_bendr(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
track->bendRange = ConsumeTrackByte(track);
|
||||
track->status |= 0xC;
|
||||
}
|
||||
|
||||
void MP2K_event_lfodl(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
track->lfoDelay = ConsumeTrackByte(track);
|
||||
}
|
||||
|
||||
void MP2K_event_modt(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
u8 type = ConsumeTrackByte(track);
|
||||
if (type != track->modType) {
|
||||
track->modType = type;
|
||||
track->status |= 0xF;
|
||||
}
|
||||
}
|
||||
|
||||
void MP2K_event_tune(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
track->tune = ConsumeTrackByte(track) - 0x40;
|
||||
track->status |= 0xC;
|
||||
}
|
||||
|
||||
void MP2K_event_port(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
// I'm really curious whether any games actually use this event...
|
||||
#ifdef NOT_GBA
|
||||
// I assume anything done by this command will get immediately overwritten by CgbSound?
|
||||
track->cmdPtr += 2;
|
||||
#else
|
||||
vu8* offset = (vu8 *)(REG_ADDR_NR10 + *(track->cmdPtr++));
|
||||
*offset = ConsumeTrackByte(track);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MP2KPlayerMain(void *voidPtrPlayer) {
|
||||
struct MP2KPlayerState *player = (struct MP2KPlayerState *)voidPtrPlayer;
|
||||
struct SoundMixerState *mixer = SOUND_INFO_PTR;
|
||||
|
||||
if (player->lockStatus != PLAYER_UNLOCKED) {
|
||||
return;
|
||||
}
|
||||
player->lockStatus = PLAYER_LOCKED;
|
||||
|
||||
if (player->nextPlayerFunc != NULL) {
|
||||
player->nextPlayerFunc(player->nextPlayer);
|
||||
}
|
||||
|
||||
if (player->status & MUSICPLAYER_STATUS_PAUSE) {
|
||||
goto returnEarly;
|
||||
}
|
||||
FadeOutBody(voidPtrPlayer);
|
||||
if (player->status & MUSICPLAYER_STATUS_PAUSE) {
|
||||
goto returnEarly;
|
||||
}
|
||||
|
||||
player->tempoCounter += player->tempoInterval;
|
||||
while (player->tempoCounter >= 150) {
|
||||
u16 trackBits = 0;
|
||||
for (u32 i = 0; i < player->trackCount; i++) {
|
||||
struct MP2KTrack *currentTrack = player->tracks + i;
|
||||
struct MixerSource *chan;
|
||||
if ((currentTrack->status & MPT_FLG_EXIST) == 0) {
|
||||
continue;
|
||||
}
|
||||
trackBits |= (1 << i);
|
||||
|
||||
chan = currentTrack->chan;
|
||||
while (chan != NULL) {
|
||||
if(chan->gateTime == 0) {
|
||||
//chan = chan->next;
|
||||
chan->status |= SOUND_CHANNEL_SF_STOP;
|
||||
break;
|
||||
}
|
||||
if ((chan->status & SOUND_CHANNEL_SF_ON) == 0) {
|
||||
ClearChain(chan);
|
||||
} else if (chan->gateTime != 0 && --chan->gateTime == 0) {
|
||||
chan->status |= SOUND_CHANNEL_SF_STOP;
|
||||
}
|
||||
chan = chan->next;
|
||||
}
|
||||
if (currentTrack->status & MPT_FLG_START) {
|
||||
CpuFill32(0, currentTrack, 0x40);
|
||||
currentTrack->status = MPT_FLG_EXIST;
|
||||
currentTrack->bendRange = 2;
|
||||
currentTrack->volPublic = 64;
|
||||
currentTrack->lfoSpeed = 22;
|
||||
currentTrack->instrument.type = 1;
|
||||
}
|
||||
|
||||
while (currentTrack->wait == 0) {
|
||||
u8 event = *currentTrack->cmdPtr;
|
||||
if (event < 0x80) {
|
||||
event = currentTrack->runningStatus;
|
||||
} else {
|
||||
currentTrack->cmdPtr++;
|
||||
if (event >= 0xBD) {
|
||||
currentTrack->runningStatus = event;
|
||||
}
|
||||
}
|
||||
|
||||
if (event >= 0xCF) {
|
||||
mixer->mp2kEventNxxFunc(event - 0xCF, player, currentTrack);
|
||||
} else if (event >= 0xB1) {
|
||||
void (*eventFunc)(struct MP2KPlayerState *, struct MP2KTrack *);
|
||||
player->cmd = event - 0xB1;
|
||||
eventFunc = mixer->mp2kEventFuncTable[player->cmd];
|
||||
eventFunc(player, currentTrack);
|
||||
|
||||
if (currentTrack->status == 0) {
|
||||
goto nextTrack;
|
||||
}
|
||||
} else {
|
||||
currentTrack->wait = gClockTable[event - 0x80];
|
||||
}
|
||||
}
|
||||
currentTrack->wait--;
|
||||
|
||||
if (currentTrack->lfoSpeed != 0 && currentTrack->modDepth != 0) {
|
||||
if (currentTrack->lfoDelayCounter != 0U) {
|
||||
currentTrack->lfoDelayCounter--;
|
||||
goto nextTrack;
|
||||
}
|
||||
|
||||
currentTrack->lfoSpeedCounter += currentTrack->lfoSpeed;
|
||||
|
||||
s8 r;
|
||||
if (currentTrack->lfoSpeedCounter >= 0x40U && currentTrack->lfoSpeedCounter < 0xC0U) {
|
||||
r = 128 - currentTrack->lfoSpeedCounter;
|
||||
} else if (currentTrack->lfoSpeedCounter >= 0xC0U) {
|
||||
// Unsigned -> signed casts where the value is out of range are implementation defined.
|
||||
// Why not add a few extra lines to make behavior the same for literally everyone?
|
||||
r = currentTrack->lfoSpeedCounter - 256;
|
||||
} else {
|
||||
r = currentTrack->lfoSpeedCounter;
|
||||
}
|
||||
r = FLOOR_DIV_POW2(currentTrack->modDepth * r, 64);
|
||||
|
||||
if (r != currentTrack->modCalculated) {
|
||||
currentTrack->modCalculated = r;
|
||||
if (currentTrack->modType == 0) {
|
||||
currentTrack->status |= MPT_FLG_PITCHG;
|
||||
} else {
|
||||
currentTrack->status |= MPT_FLG_VOLCHG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextTrack:;
|
||||
}
|
||||
player->clock++;
|
||||
if (trackBits == 0) {
|
||||
player->status = MUSICPLAYER_STATUS_PAUSE;
|
||||
goto returnEarly;
|
||||
}
|
||||
player->status = trackBits;
|
||||
player->tempoCounter -= 150;
|
||||
}
|
||||
|
||||
u32 i = 0;
|
||||
|
||||
do {
|
||||
printf( "probleman2\n");
|
||||
struct MP2KTrack *track = player->tracks + i;
|
||||
|
||||
if ((track->status & MPT_FLG_EXIST) == 0 || (track->status & 0xF) == 0) {
|
||||
continue;
|
||||
}
|
||||
TrkVolPitSet(player, track);
|
||||
|
||||
for (struct MixerSource *chan = track->chan; chan != NULL; chan = chan->next) {
|
||||
printf( "probleman3 %d\n",chan->status);
|
||||
if(chan->status & SOUND_CHANNEL_SF_STOP){ break;}
|
||||
if ((chan->status & 0xC7) == 0) {
|
||||
ClearChain(chan);
|
||||
continue;
|
||||
}
|
||||
u8 cgbType = chan->type & 0x7;
|
||||
if (track->status & MPT_FLG_VOLCHG) {
|
||||
ChnVolSetAsm(chan, track);
|
||||
if (cgbType != 0) {
|
||||
chan->cgbStatus |= 1;
|
||||
}
|
||||
}
|
||||
if (track->status & MPT_FLG_PITCHG) {
|
||||
s32 key = chan->key + track->keyShiftCalculated;
|
||||
if (key < 0) {
|
||||
key = 0;
|
||||
}
|
||||
if (cgbType != 0) {
|
||||
chan->freq = mixer->cgbCalcFreqFunc(cgbType, key, track->pitchCalculated);
|
||||
chan->cgbStatus |= 0x2;
|
||||
} else {
|
||||
chan->freq = MidiKeyToFreq(chan->wav, key, track->pitchCalculated);
|
||||
}
|
||||
}
|
||||
}
|
||||
track->status &= ~0xF;
|
||||
}
|
||||
while(++i < player->trackCount);
|
||||
printf("problema3\n");
|
||||
returnEarly: ;
|
||||
player->lockStatus = PLAYER_UNLOCKED;
|
||||
}
|
||||
|
||||
void TrackStop(struct MP2KPlayerState *player, struct MP2KTrack *track) {
|
||||
if (track->status & 0x80) {
|
||||
for (struct MixerSource *chan = track->chan; chan != NULL; chan = chan->next) {
|
||||
if (chan->status != 0) {
|
||||
u8 cgbType = chan->type & 0x7;
|
||||
if (cgbType != 0) {
|
||||
struct SoundMixerState *mixer = SOUND_INFO_PTR;
|
||||
mixer->cgbNoteOffFunc(cgbType);
|
||||
}
|
||||
chan->status = 0;
|
||||
}
|
||||
chan->track = NULL;
|
||||
}
|
||||
track->chan = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void ChnVolSetAsm(struct MixerSource *chan, struct MP2KTrack *track) {
|
||||
s8 forcedPan = chan->rhythmPan;
|
||||
u32 rightVolume = (u8)(forcedPan + 128) * chan->velocity * track->volRightCalculated / 128 / 128;
|
||||
if (rightVolume > 0xFF) {
|
||||
rightVolume = 0xFF;
|
||||
}
|
||||
chan->rightVol = rightVolume;
|
||||
|
||||
u32 leftVolume = (u8)(127 - forcedPan) * chan->velocity * track->volLeftCalculated / 128 / 128;
|
||||
if (leftVolume > 0xFF) {
|
||||
leftVolume = 0xFF;
|
||||
}
|
||||
chan->leftVol = leftVolume;
|
||||
}
|
||||
|
||||
void MP2K_event_nxx(u8 clock, struct MP2KPlayerState *player, struct MP2KTrack *track) { // ply_note
|
||||
struct SoundMixerState *mixer = SOUND_INFO_PTR;
|
||||
|
||||
// A note can be anywhere from 1 to 4 bytes long. First is always the note length...
|
||||
track->gateTime = gClockTable[clock];
|
||||
if (*track->cmdPtr < 0x80) {
|
||||
// Then the note name...
|
||||
track->key = *(track->cmdPtr++);
|
||||
if (*track->cmdPtr < 0x80) {
|
||||
// Then the velocity...
|
||||
track->velocity = *(track->cmdPtr++);
|
||||
if (*track->cmdPtr < 0x80) {
|
||||
// Then a number to add ticks to get exotic or more precise note lengths without TIE.
|
||||
track->gateTime += *(track->cmdPtr++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sp14
|
||||
s8 forcedPan = 0;
|
||||
// First r4, then r9
|
||||
struct MP2KInstrument *instrument = &track->instrument;
|
||||
// sp8
|
||||
u8 key = track->key;
|
||||
u8 type = instrument->type;
|
||||
|
||||
if (type & (TONEDATA_TYPE_RHY | TONEDATA_TYPE_SPL)) {
|
||||
u8 instrumentIndex;
|
||||
if (instrument->type & TONEDATA_TYPE_SPL) {
|
||||
instrumentIndex = instrument->keySplitTable[track->key];
|
||||
} else {
|
||||
instrumentIndex = track->key;
|
||||
}
|
||||
|
||||
instrument = instrument->group + instrumentIndex;
|
||||
if (instrument->type & (TONEDATA_TYPE_RHY | TONEDATA_TYPE_SPL)) {
|
||||
return;
|
||||
}
|
||||
if (type & TONEDATA_TYPE_RHY) {
|
||||
if (instrument->panSweep & 0x80) {
|
||||
forcedPan = ((s8)(instrument->panSweep & 0x7F) - 0x40) * 2;
|
||||
}
|
||||
key = instrument->drumKey;
|
||||
}
|
||||
}
|
||||
|
||||
// sp10
|
||||
uf16 priority = player->priority + track->priority;
|
||||
if (priority > 0xFF) {
|
||||
priority = 0xFF;
|
||||
}
|
||||
|
||||
u8 cgbType = instrument->type & TONEDATA_TYPE_CGB;
|
||||
struct MixerSource *chan;
|
||||
|
||||
if (cgbType != 0) {
|
||||
if (mixer->cgbChans == NULL) {
|
||||
return;
|
||||
}
|
||||
// There's only one CgbChannel of a given type, so we don't need to loop to find it.
|
||||
chan = mixer->cgbChans + cgbType - 1;
|
||||
|
||||
// If this channel is running and not stopped,
|
||||
if ((chan->status & SOUND_CHANNEL_SF_ON)
|
||||
&& (chan->status & SOUND_CHANNEL_SF_STOP) == 0) {
|
||||
// then make sure this note is higher priority (or same priority but from a later track).
|
||||
if (chan->priority > priority || (chan->priority == priority && chan->track < track)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uf16 p = priority;
|
||||
struct MP2KTrack *t = track;
|
||||
bool32 foundStoppingChannel = FALSE;
|
||||
chan = NULL;
|
||||
u8 maxChans = mixer->numChans;
|
||||
struct MixerSource *currChan = mixer->chans;
|
||||
|
||||
for (uf8 i = 0; i < maxChans; i++, currChan++) {
|
||||
if ((currChan->status & SOUND_CHANNEL_SF_ON) == 0) {
|
||||
// Hey, we found a completely inactive channel! Let's use that.
|
||||
chan = currChan;
|
||||
break;
|
||||
}
|
||||
|
||||
if (currChan->status & SOUND_CHANNEL_SF_STOP && !foundStoppingChannel) {
|
||||
// In the absence of a completely finalized channel, we can take over one that's about to
|
||||
// finalize. That's a tier above any channel that's currently playing a note.
|
||||
foundStoppingChannel = TRUE;
|
||||
p = currChan->priority;
|
||||
t = currChan->track;
|
||||
chan = currChan;
|
||||
} else if ((currChan->status & SOUND_CHANNEL_SF_STOP && foundStoppingChannel)
|
||||
|| ((currChan->status & SOUND_CHANNEL_SF_STOP) == 0 && !foundStoppingChannel)) {
|
||||
// The channel we're checking is on the same tier, so check the priority and track order
|
||||
if (currChan->priority < p) {
|
||||
p = currChan->priority;
|
||||
t = currChan->track;
|
||||
chan = currChan;
|
||||
} else if (currChan->priority == p && currChan->track > t) {
|
||||
t = currChan->track;
|
||||
chan = currChan;
|
||||
} else if (currChan->priority == p && currChan->track == t) {
|
||||
chan = currChan;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (chan == NULL) {
|
||||
return;
|
||||
}
|
||||
ClearChain(chan);
|
||||
|
||||
chan->prev = NULL;
|
||||
chan->next = track->chan;
|
||||
if (track->chan != NULL) {
|
||||
track->chan->prev = chan;
|
||||
}
|
||||
track->chan = chan;
|
||||
chan->track = track;
|
||||
|
||||
track->lfoDelayCounter = track->lfoDelay;
|
||||
if (track->lfoDelay != 0) {
|
||||
ClearModM(track);
|
||||
}
|
||||
TrkVolPitSet(player, track);
|
||||
|
||||
chan->gateTime = track->gateTime;
|
||||
chan->untransposedKey = track->key;
|
||||
chan->velocity = track->velocity;
|
||||
chan->priority = priority;
|
||||
chan->key = key;
|
||||
chan->rhythmPan = forcedPan;
|
||||
chan->type = instrument->type;
|
||||
chan->wav = instrument->wav;
|
||||
chan->attack = instrument->attack;
|
||||
chan->decay = instrument->decay;
|
||||
chan->sustain = instrument->sustain;
|
||||
chan->release = instrument->release;
|
||||
chan->echoVol = track->echoVolume;
|
||||
chan->echoLen = track->echoLength;
|
||||
ChnVolSetAsm(chan, track);
|
||||
|
||||
// Avoid promoting keyShiftCalculated to u8 by splitting the addition into a separate statement
|
||||
sf16 transposedKey = chan->key;
|
||||
transposedKey += track->keyShiftCalculated;
|
||||
if (transposedKey < 0) {
|
||||
transposedKey = 0;
|
||||
}
|
||||
|
||||
if (cgbType != 0) {
|
||||
//struct CgbChannel *cgbChan = (struct CgbChannel *)chan;
|
||||
chan->length = instrument->cgbLength;
|
||||
if (instrument->panSweep & 0x80 || (instrument->panSweep & 0x70) == 0) {
|
||||
chan->sweep = 8;
|
||||
} else {
|
||||
chan->sweep = instrument->panSweep;
|
||||
}
|
||||
|
||||
chan->freq = mixer->cgbCalcFreqFunc(cgbType, transposedKey, track->pitchCalculated);
|
||||
} else {
|
||||
#ifdef POKEMON_EXTENSIONS
|
||||
chan->ct = track->ct;
|
||||
#endif
|
||||
chan->freq = MidiKeyToFreq(chan->wav, transposedKey, track->pitchCalculated);
|
||||
}
|
||||
|
||||
chan->status = SOUND_CHANNEL_SF_START;
|
||||
track->status &= ~0xF;
|
||||
}
|
||||
|
||||
void MP2K_event_endtie(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
u8 key = *track->cmdPtr;
|
||||
if (key < 0x80) {
|
||||
track->key = key;
|
||||
track->cmdPtr++;
|
||||
} else {
|
||||
key = track->key;
|
||||
}
|
||||
|
||||
struct MixerSource *chan = track->chan;
|
||||
while (chan != NULL) {
|
||||
if (chan->status & 0x83 && (chan->status & 0x40) == 0 && chan->untransposedKey == key) {
|
||||
chan->status |= 0x40;
|
||||
return;
|
||||
}
|
||||
chan = chan->next;
|
||||
}
|
||||
}
|
||||
|
||||
void MP2K_event_lfos(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
track->lfoSpeed = *(track->cmdPtr++);
|
||||
if (track->lfoSpeed == 0) {
|
||||
ClearModM(track);
|
||||
}
|
||||
}
|
||||
|
||||
void MP2K_event_mod(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
|
||||
track->modDepth = *(track->cmdPtr++);
|
||||
if (track->modDepth == 0) {
|
||||
ClearModM(track);
|
||||
}
|
||||
}
|
||||
|
||||
void m4aSoundVSync(void)
|
||||
{
|
||||
struct SoundMixerState *mixer = SOUND_INFO_PTR;
|
||||
if(mixer->lockStatus-PLAYER_UNLOCKED <= 1)
|
||||
{
|
||||
s32 samplesPerFrame = mixer->samplesPerFrame * 2;
|
||||
float *m4aBuffer = mixer->outBuffer;
|
||||
float *cgbBuffer = cgb_get_buffer();
|
||||
s32 dmaCounter = mixer->dmaCounter;
|
||||
|
||||
if (dmaCounter > 1) {
|
||||
m4aBuffer += samplesPerFrame * (mixer->framesPerDmaCycle - (dmaCounter - 1));
|
||||
}
|
||||
|
||||
for(u32 i = 0; i < samplesPerFrame; i++)
|
||||
audioBuffer[i] = m4aBuffer[i] + cgbBuffer[i];
|
||||
|
||||
Platform_QueueAudio(audioBuffer, samplesPerFrame * 4);
|
||||
if((s8)(--mixer->dmaCounter) <= 0)
|
||||
mixer->dmaCounter = mixer->framesPerDmaCycle;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
// In:
|
||||
// - wav: pointer to sample
|
||||
// - key: the note after being transposed. If pitch bend puts it between notes, then the note below.
|
||||
// - pitch: how many 256ths of a semitone above `key` the current note is.
|
||||
// Out:
|
||||
// - The frequency in Hz at which the sample should be played back.
|
||||
|
||||
u32 MidiKeyToFreq(struct WaveData2 *wav, u8 key, u8 pitch) {
|
||||
if (key > 178) {
|
||||
key = 178;
|
||||
pitch = 255;
|
||||
}
|
||||
|
||||
// Alternatively, note = key % 12 and octave = 14 - (key / 12)
|
||||
u8 note = gScaleTable[key] & 0xF;
|
||||
u8 octave = gScaleTable[key] >> 4;
|
||||
u8 nextNote = gScaleTable[key + 1] & 0xF;
|
||||
u8 nextOctave = gScaleTable[key + 1] >> 4;
|
||||
|
||||
u32 baseFreq1 = gFreqTable[note] >> octave;
|
||||
u32 baseFreq2 = gFreqTable[nextNote] >> nextOctave;
|
||||
|
||||
u32 freqDifference = umul3232H32(baseFreq2 - baseFreq1, pitch << 24);
|
||||
// This is added by me. The real GBA and GBA BIOS don't verify this address, and as a result the
|
||||
// BIOS's memory can be dumped.
|
||||
u32 freq = SafeDereferenceU32(&wav->freq);
|
||||
return umul3232H32(freq, baseFreq1 + freqDifference);
|
||||
}
|
||||
#endif
|
||||
|
|
@ -336,38 +336,7 @@ bool32 MysteryGift_TrySaveStamp(const u16 * stamp)
|
|||
|
||||
void MysteryGift_LoadLinkGameData(struct MysteryGiftLinkGameData * data)
|
||||
{
|
||||
s32 i;
|
||||
CpuFill32(0, data, sizeof(*data));
|
||||
// Magic
|
||||
data->unk_00 = GAME_DATA_VALID_VAR;
|
||||
data->unk_04 = 1;
|
||||
data->unk_08 = 1;
|
||||
data->unk_0C = 1;
|
||||
data->unk_10 = VERSION_CODE;
|
||||
|
||||
// Check whether a card already exists
|
||||
if (ValidateSavedWonderCard())
|
||||
{
|
||||
// Populate fields
|
||||
data->flagId = GetSavedWonderCard()->flagId;
|
||||
data->cardMetadata = *GetSavedWonderCardMetadata();
|
||||
data->maxStamps = GetSavedWonderCard()->maxStamps;
|
||||
}
|
||||
else
|
||||
{
|
||||
data->flagId = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_QUESTIONNAIRE_WORDS; i++)
|
||||
data->questionnaireWords[i] = gSaveBlock1Ptr->mysteryGift.questionnaireWords[i];
|
||||
|
||||
CopyTrainerId(data->playerTrainerId, gSaveBlock2Ptr->playerTrainerId);
|
||||
StringCopy(data->playerName, gSaveBlock2Ptr->playerName);
|
||||
for (i = 0; i < EASY_CHAT_BATTLE_WORDS_COUNT; i++)
|
||||
data->easyChatProfile[i] = gSaveBlock1Ptr->easyChatProfile[i];
|
||||
|
||||
memcpy(data->gameCode, RomHeaderGameCode, GAME_CODE_LENGTH);
|
||||
data->version = RomHeaderSoftwareVersion;
|
||||
}
|
||||
|
||||
bool32 MysteryGift_ValidateLinkGameData(const struct MysteryGiftLinkGameData * data)
|
||||
|
|
|
|||
|
|
@ -718,10 +718,11 @@ static bool8 MainState_Exit(void)
|
|||
{
|
||||
if (!gPaletteFade.active)
|
||||
{
|
||||
if (sNamingScreen->templateNum == NAMING_SCREEN_PLAYER)
|
||||
SeedRngAndSetTrainerId();
|
||||
SetMainCallback2(sNamingScreen->returnCallback);
|
||||
DestroyTask(FindTaskIdByFunc(Task_NamingScreen));
|
||||
ResetVHBlank();
|
||||
// Fix use-after-free issues with gNamingScreenData caused by sprites calling their callbacks which attempt to read from gNamingScreenData.
|
||||
ResetSpriteData();
|
||||
FreeAllWindowBuffers();
|
||||
FREE_AND_SET_NULL(sNamingScreen);
|
||||
RestoreHelpContext();
|
||||
|
|
|
|||
|
|
@ -47,13 +47,16 @@ void SetTrainerId(u32 trainerId, u8 *dst)
|
|||
void CopyTrainerId(u8 *dst, u8 *src)
|
||||
{
|
||||
s32 i;
|
||||
for (i = 0; i < 4; i++)
|
||||
for (i = 0; i < 4; i++){
|
||||
if (src == NULL)
|
||||
dst[i] = 0;
|
||||
dst[i] = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void InitPlayerTrainerId(void)
|
||||
{
|
||||
u32 trainerId = (Random() << 0x10) | GetGeneratedTrainerIdLower();
|
||||
u32 trainerId = (Random() << 16 | Random());
|
||||
SetTrainerId(trainerId, gSaveBlock2Ptr->playerTrainerId);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1966,7 +1966,6 @@ static void DestroyPikachuOrPlatformSprites(u8 taskId, u8 spriteType)
|
|||
static void LoadTrainerPic(u16 whichPic, u16 tileOffset)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
switch (whichPic)
|
||||
{
|
||||
case MALE_PLAYER_PIC:
|
||||
|
|
@ -1988,7 +1987,6 @@ static void LoadTrainerPic(u16 whichPic, u16 tileOffset)
|
|||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
sOakSpeechResources->trainerPicTilemap = AllocZeroed(0x60);
|
||||
for (i = 0; i < 0x60; i++)
|
||||
((u8 *)sOakSpeechResources->trainerPicTilemap)[i] = i;
|
||||
|
|
|
|||
257
src/platform/cgb_audio.c
Normal file
257
src/platform/cgb_audio.c
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
#include "global.h"
|
||||
#include "cgb_audio.h"
|
||||
#include "cgb_tables.h"
|
||||
|
||||
struct AudioCGB gb;
|
||||
float soundChannelPos[4];
|
||||
const s16 *PU1Table;
|
||||
const s16 *PU2Table;
|
||||
u32 apuFrame;
|
||||
u8 apuCycle;
|
||||
u32 sampleRate;
|
||||
u16 lfsrMax[2];
|
||||
float ch4Samples;
|
||||
|
||||
void cgb_audio_init(u32 rate){
|
||||
gb.ch1Freq = 0;
|
||||
gb.ch1SweepCounter = 0;
|
||||
gb.ch1SweepCounterI = 0;
|
||||
gb.ch1SweepDir = 0;
|
||||
gb.ch1SweepShift = 0;
|
||||
for (u8 ch = 0; ch < 4; ch++){
|
||||
gb.Vol[ch] = 0;
|
||||
gb.VolI[ch] = 0;
|
||||
gb.Len[ch] = 0;
|
||||
gb.LenI[ch] = 0;
|
||||
gb.LenOn[ch] = 0;
|
||||
gb.EnvCounter[ch] = 0;
|
||||
gb.EnvCounterI[ch] = 0;
|
||||
gb.EnvDir[ch] = 0;
|
||||
gb.DAC[ch] = 0;
|
||||
soundChannelPos[ch] = 0;
|
||||
}
|
||||
soundChannelPos[1] = 1;
|
||||
PU1Table = PU0;
|
||||
PU2Table = PU0;
|
||||
sampleRate = rate;
|
||||
gb.ch4LFSR[0] = 0x8000;
|
||||
gb.ch4LFSR[1] = 0x80;
|
||||
lfsrMax[0] = 0x8000;
|
||||
lfsrMax[1] = 0x80;
|
||||
ch4Samples = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
void cgb_set_sweep(u8 sweep){
|
||||
gb.ch1SweepDir = (sweep & 0x08) >> 3;
|
||||
gb.ch1SweepCounter = gb.ch1SweepCounterI = (sweep & 0x70) >> 4;
|
||||
gb.ch1SweepShift = (sweep & 0x07);
|
||||
}
|
||||
|
||||
|
||||
void cgb_set_wavram(){
|
||||
for(u8 wavi = 0; wavi < 0x10; wavi++){
|
||||
gb.WAVRAM[(wavi << 1)] = (((*(REG_ADDR_WAVE_RAM0 + wavi)) & 0xF0) >> 4) / 7.5f - 1.0f;
|
||||
gb.WAVRAM[(wavi << 1) + 1] = (((*(REG_ADDR_WAVE_RAM0 + wavi)) & 0x0F)) / 7.5f - 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void cgb_toggle_length(u8 channel, bool8 state){
|
||||
gb.LenOn[channel] = state;
|
||||
}
|
||||
|
||||
|
||||
void cgb_set_length(u8 channel, u8 length){
|
||||
gb.Len[channel] = gb.LenI[channel] = length;
|
||||
}
|
||||
|
||||
|
||||
void cgb_set_envelope(u8 channel, u8 envelope){
|
||||
if(channel == 2){
|
||||
switch((envelope & 0xE0)){
|
||||
case 0x00: // mute
|
||||
gb.Vol[2] = gb.VolI[2] = 0;
|
||||
break;
|
||||
case 0x20: // full
|
||||
gb.Vol[2] = gb.VolI[2] = 4;
|
||||
break;
|
||||
case 0x40: // half
|
||||
gb.Vol[2] = gb.VolI[2] = 2;
|
||||
break;
|
||||
case 0x60: // quarter
|
||||
gb.Vol[2] = gb.VolI[2] = 1;
|
||||
break;
|
||||
case 0x80: // 3 quarters
|
||||
gb.Vol[2] = gb.VolI[2] = 3;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
gb.DAC[channel] = (envelope & 0xF8) > 0;
|
||||
gb.Vol[channel] = gb.VolI[channel] = (envelope & 0xF0) >> 4;
|
||||
gb.EnvDir[channel] = (envelope & 0x08) >> 3;
|
||||
gb.EnvCounter[channel] = gb.EnvCounterI[channel] = (envelope & 0x07);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void cgb_trigger_note(u8 channel){
|
||||
gb.Vol[channel] = gb.VolI[channel];
|
||||
gb.Len[channel] = gb.LenI[channel];
|
||||
if(channel != 2) gb.EnvCounter[channel] = gb.EnvCounterI[channel];
|
||||
if(channel == 3) {
|
||||
gb.ch4LFSR[0] = 0x8000;
|
||||
gb.ch4LFSR[1] = 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void cgb_audio_generate(u16 samplesPerFrame){
|
||||
float *outBuffer = gb.outBuffer;
|
||||
switch(REG_NR11 & 0xC0){
|
||||
case 0x00:
|
||||
PU1Table = PU0;
|
||||
break;
|
||||
case 0x40:
|
||||
PU1Table = PU1;
|
||||
break;
|
||||
case 0x80:
|
||||
PU1Table = PU2;
|
||||
break;
|
||||
case 0xC0:
|
||||
PU1Table = PU3;
|
||||
break;
|
||||
}
|
||||
|
||||
switch(REG_NR21 & 0xC0){
|
||||
case 0x00:
|
||||
PU2Table = PU0;
|
||||
break;
|
||||
case 0x40:
|
||||
PU2Table = PU1;
|
||||
break;
|
||||
case 0x80:
|
||||
PU2Table = PU2;
|
||||
break;
|
||||
case 0xC0:
|
||||
PU2Table = PU3;
|
||||
break;
|
||||
}
|
||||
|
||||
for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) {
|
||||
apuFrame += 512;
|
||||
if(apuFrame >= sampleRate){
|
||||
apuFrame -= sampleRate;
|
||||
apuCycle++;
|
||||
|
||||
if((apuCycle & 1) == 0){ // Length
|
||||
for(u8 ch = 0; ch < 4; ch++){
|
||||
if(gb.Len[ch]){
|
||||
if(--gb.Len[ch] == 0 && gb.LenOn[ch]){
|
||||
REG_NR52 &= (0xFF ^ (1 << ch));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if((apuCycle & 7) == 7){ // Envelope
|
||||
for(u8 ch = 0; ch < 4; ch++){
|
||||
if(ch == 2) continue; // Skip wave channel
|
||||
if(gb.EnvCounter[ch]){
|
||||
if(--gb.EnvCounter[ch] == 0){
|
||||
if(gb.Vol[ch] && !gb.EnvDir[ch]){
|
||||
gb.Vol[ch]--;
|
||||
gb.EnvCounter[ch] = gb.EnvCounterI[ch];
|
||||
}else if(gb.Vol[ch] < 0x0F && gb.EnvDir[ch]){
|
||||
gb.Vol[ch]++;
|
||||
gb.EnvCounter[ch] = gb.EnvCounterI[ch];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if((apuCycle & 3) == 2){ // Sweep
|
||||
if(gb.ch1SweepCounterI && gb.ch1SweepShift){
|
||||
if(--gb.ch1SweepCounter == 0){
|
||||
gb.ch1Freq = REG_SOUND1CNT_X & 0x7FF;
|
||||
if(gb.ch1SweepDir){
|
||||
gb.ch1Freq -= gb.ch1Freq >> gb.ch1SweepShift;
|
||||
if(gb.ch1Freq & 0xF800) gb.ch1Freq = 0;
|
||||
}else{
|
||||
gb.ch1Freq += gb.ch1Freq >> gb.ch1SweepShift;
|
||||
if(gb.ch1Freq & 0xF800){
|
||||
gb.ch1Freq = 0;
|
||||
gb.EnvCounter[0] = 0;
|
||||
gb.Vol[0] = 0;
|
||||
}
|
||||
}
|
||||
REG_NR13 = gb.ch1Freq & 0xFF;
|
||||
REG_NR14 &= 0xF8;
|
||||
REG_NR14 += (gb.ch1Freq >> 8) & 0x07;
|
||||
gb.ch1SweepCounter = gb.ch1SweepCounterI;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//Sound generation loop
|
||||
soundChannelPos[0] += freqTable[REG_SOUND1CNT_X & 0x7FF] / (sampleRate / 32);
|
||||
soundChannelPos[1] += freqTable[REG_SOUND2CNT_H & 0x7FF] / (sampleRate / 32);
|
||||
soundChannelPos[2] += freqTable[REG_SOUND3CNT_X & 0x7FF] / (sampleRate / 32);
|
||||
while(soundChannelPos[0] >= 32) soundChannelPos[0] -= 32;
|
||||
while(soundChannelPos[1] >= 32) soundChannelPos[1] -= 32;
|
||||
while(soundChannelPos[2] >= 32) soundChannelPos[2] -= 32;
|
||||
float outputL = 0;
|
||||
float outputR = 0;
|
||||
if(REG_NR52 & 0x80){
|
||||
if((gb.DAC[0]) && (REG_NR52 & 0x01)){
|
||||
if(REG_NR51 & 0x10) outputL += gb.Vol[0] * PU1Table[(int)(soundChannelPos[0])] / 15.0f;
|
||||
if(REG_NR51 & 0x01) outputR += gb.Vol[0] * PU1Table[(int)(soundChannelPos[0])] / 15.0f;
|
||||
}
|
||||
if((gb.DAC[1]) && (REG_NR52 & 0x02)){
|
||||
if(REG_NR51 & 0x20) outputL += gb.Vol[1] * PU2Table[(int)(soundChannelPos[1])] / 15.0f;
|
||||
if(REG_NR51 & 0x02) outputR += gb.Vol[1] * PU2Table[(int)(soundChannelPos[1])] / 15.0f;
|
||||
}
|
||||
if((REG_NR30 & 0x80) && (REG_NR52 & 0x04)){
|
||||
if(REG_NR51 & 0x40) outputL += gb.Vol[2] * gb.WAVRAM[(int)(soundChannelPos[2])] / 4.0f;
|
||||
if(REG_NR51 & 0x04) outputR += gb.Vol[2] * gb.WAVRAM[(int)(soundChannelPos[2])] / 4.0f;
|
||||
}
|
||||
if((gb.DAC[3]) && (REG_NR52 & 0x08)){
|
||||
bool32 lfsrMode = ((REG_NR43 & 0x08) == 8);
|
||||
ch4Samples += freqTableNSE[REG_SOUND4CNT_H & 0xFF] / sampleRate;
|
||||
int ch4Out = 0;
|
||||
if(gb.ch4LFSR[lfsrMode] & 1){
|
||||
ch4Out++;
|
||||
}else{
|
||||
ch4Out--;
|
||||
}
|
||||
int avgDiv = 1;
|
||||
while(ch4Samples >= 1){
|
||||
avgDiv++;
|
||||
bool8 lfsrCarry = 0;
|
||||
if(gb.ch4LFSR[lfsrMode] & 2) lfsrCarry ^= 1;
|
||||
gb.ch4LFSR[lfsrMode] >>= 1;
|
||||
if(gb.ch4LFSR[lfsrMode] & 2) lfsrCarry ^= 1;
|
||||
if(lfsrCarry) gb.ch4LFSR[lfsrMode] |= lfsrMax[lfsrMode];
|
||||
if(gb.ch4LFSR[lfsrMode] & 1){
|
||||
ch4Out++;
|
||||
}else{
|
||||
ch4Out--;
|
||||
}
|
||||
ch4Samples--;
|
||||
}
|
||||
float sample = ch4Out;
|
||||
if(avgDiv > 1) sample /= avgDiv;
|
||||
if(REG_NR51 & 0x80) outputL += gb.Vol[3] * sample / 15.0f;
|
||||
if(REG_NR51 & 0x08) outputR += gb.Vol[3] * sample / 15.0f;
|
||||
}
|
||||
}
|
||||
outBuffer[0] = outputL / 4.0f;
|
||||
outBuffer[1] = outputR / 4.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float *cgb_get_buffer(){
|
||||
return gb.outBuffer;
|
||||
}
|
||||
110
src/platform/dma.c
Normal file
110
src/platform/dma.c
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
#include "global.h"
|
||||
#include "platform/dma.h"
|
||||
|
||||
struct DMATransfer {
|
||||
union {
|
||||
const void *src;
|
||||
const u16 *src16;
|
||||
const u32 *src32;
|
||||
};
|
||||
union {
|
||||
void *dst;
|
||||
vu16 *dst16;
|
||||
vu32 *dst32;
|
||||
};
|
||||
u32 size;
|
||||
u16 control;
|
||||
} DMAList[DMA_COUNT];
|
||||
|
||||
void RunDMAs(u32 type)
|
||||
{
|
||||
for (int dmaNum = 0; dmaNum < DMA_COUNT; dmaNum++)
|
||||
{
|
||||
struct DMATransfer *dma = &DMAList[dmaNum];
|
||||
u32 dmaCntReg = (®_DMA0CNT)[dmaNum * 3];
|
||||
if (!((dmaCntReg >> 16) & DMA_ENABLE))
|
||||
{
|
||||
dma->control &= ~DMA_ENABLE;
|
||||
}
|
||||
|
||||
if ( (dma->control & DMA_ENABLE) &&
|
||||
(((dma->control & DMA_START_MASK) >> 12) == type))
|
||||
{
|
||||
//printf("DMA%d src=%p, dest=%p, control=%d\n", dmaNum, dma->src, dma->dest, dma->control);
|
||||
for (int i = 0; i < (dma->size); i++)
|
||||
{
|
||||
if ((dma->control) & DMA_32BIT)
|
||||
*dma->dst32 = *dma->src32;
|
||||
else *dma->dst16 = *dma->src16;
|
||||
|
||||
// process destination pointer changes
|
||||
if (((dma->control) & DMA_DEST_MASK) == DMA_DEST_INC)
|
||||
{
|
||||
if ((dma->control) & DMA_32BIT)
|
||||
dma->dst32++;
|
||||
else dma->dst16++;
|
||||
}
|
||||
else if (((dma->control) & DMA_DEST_MASK) == DMA_DEST_DEC)
|
||||
{
|
||||
if ((dma->control) & DMA_32BIT)
|
||||
dma->dst32--;
|
||||
else dma->dst16--;
|
||||
}
|
||||
else if (((dma->control) & DMA_DEST_MASK) == DMA_DEST_RELOAD) // TODO
|
||||
{
|
||||
if ((dma->control) & DMA_32BIT)
|
||||
dma->dst32++;
|
||||
else dma->dst16++;
|
||||
}
|
||||
|
||||
// process source pointer changes
|
||||
if (((dma->control) & DMA_SRC_MASK) == DMA_SRC_INC)
|
||||
{
|
||||
if ((dma->control) & DMA_32BIT)
|
||||
dma->src32++;
|
||||
else dma->src16++;
|
||||
}
|
||||
else if (((dma->control) & DMA_SRC_MASK) == DMA_SRC_DEC)
|
||||
{
|
||||
if ((dma->control) & DMA_32BIT)
|
||||
dma->src32--;
|
||||
else dma->src16--;
|
||||
}
|
||||
}
|
||||
|
||||
if (dma->control & DMA_REPEAT)
|
||||
{
|
||||
dma->size = ((®_DMA0CNT)[dmaNum * 3] & 0x1FFFF);
|
||||
if (((dma->control) & DMA_DEST_MASK) == DMA_DEST_RELOAD)
|
||||
{
|
||||
dma->dst = ((®_DMA0DAD)[dmaNum * 3]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dma->control &= ~DMA_ENABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DmaSet(int dmaNum, const void *src, void *dest, u32 control)
|
||||
{
|
||||
if (dmaNum >= DMA_COUNT)
|
||||
{
|
||||
printf("DmaSet with invalid DMA number: dmaNum=%d, src=%p, dest=%p, control=%d\n", dmaNum, src, dest, control);
|
||||
return;
|
||||
}
|
||||
|
||||
(®_DMA0SAD)[dmaNum * 3] = src;
|
||||
(®_DMA0DAD)[dmaNum * 3] = dest;
|
||||
(®_DMA0CNT)[dmaNum * 3] = control;
|
||||
|
||||
struct DMATransfer *dma = &DMAList[dmaNum];
|
||||
dma->src = src;
|
||||
dma->dst = dest;
|
||||
dma->size = control & 0x1ffff;
|
||||
dma->control = control >> 16;
|
||||
|
||||
RunDMAs(DMA_NOW);
|
||||
}
|
||||
946
src/platform/gba_easy_draw.c
Normal file
946
src/platform/gba_easy_draw.c
Normal file
|
|
@ -0,0 +1,946 @@
|
|||
|
||||
#include "global.h"
|
||||
#include <stdbool.h>
|
||||
#include "platform/dma.h"
|
||||
|
||||
#define mosaicBGEffectX (REG_MOSAIC & 0xF)
|
||||
#define mosaicBGEffectY ((REG_MOSAIC >> 4) & 0xF)
|
||||
#define mosaicSpriteEffectX ((REG_MOSAIC >> 8) & 0xF)
|
||||
#define mosaicSpriteEffectY ((REG_MOSAIC >> 12) & 0xF)
|
||||
#define applyBGHorizontalMosaicEffect(x) (x - (x % (mosaicBGEffectX+1)))
|
||||
#define applyBGVerticalMosaicEffect(y) (y - (y % (mosaicBGEffectY+1)))
|
||||
#define applySpriteHorizontalMosaicEffect(x) (x - (x % (mosaicSpriteEffectX+1)))
|
||||
#define applySpriteVerticalMosaicEffect(y) (y - (y % (mosaicSpriteEffectY+1)))
|
||||
|
||||
#define getAlphaBit(x) ((x >> 15) & 1)
|
||||
#define getRedChannel(x) ((x >> 0) & 0x1F)
|
||||
#define getGreenChannel(x) ((x >> 5) & 0x1F)
|
||||
#define getBlueChannel(x) ((x >> 10) & 0x1F)
|
||||
#define isbgEnabled(x) ((REG_DISPCNT >> 8) & 0xF) & (1 << x)
|
||||
|
||||
#define WINMASK_BG0 (1 << 0)
|
||||
#define WINMASK_BG1 (1 << 1)
|
||||
#define WINMASK_BG2 (1 << 2)
|
||||
#define WINMASK_BG3 (1 << 3)
|
||||
#define WINMASK_OBJ (1 << 4)
|
||||
#define WINMASK_CLR (1 << 5)
|
||||
#define WINMASK_WINOUT (1 << 6)
|
||||
|
||||
extern IntrFunc gIntrTable[];
|
||||
|
||||
struct scanlineData {
|
||||
uint16_t layers[4][DISPLAY_WIDTH];
|
||||
uint16_t spriteLayers[4][DISPLAY_WIDTH];
|
||||
uint16_t bgcnts[4];
|
||||
uint16_t winMask[DISPLAY_WIDTH];
|
||||
//priority bookkeeping
|
||||
char bgtoprio[4]; //background to priority
|
||||
char prioritySortedBgs[4][4];
|
||||
char prioritySortedBgsCount[4];
|
||||
};
|
||||
|
||||
struct bgPriority {
|
||||
char priority;
|
||||
char subPriority;
|
||||
};
|
||||
|
||||
static const uint16_t bgMapSizes[][2] =
|
||||
{
|
||||
{32, 32},
|
||||
{64, 32},
|
||||
{32, 64},
|
||||
{64, 64},
|
||||
};
|
||||
|
||||
static void RenderBGScanline(int bgNum, uint16_t control, uint16_t hoffs, uint16_t voffs, int lineNum, uint16_t *line)
|
||||
{
|
||||
unsigned int charBaseBlock = (control >> 2) & 3;
|
||||
unsigned int screenBaseBlock = (control >> 8) & 0x1F;
|
||||
unsigned int bitsPerPixel = ((control >> 7) & 1) ? 8 : 4;
|
||||
unsigned int mapWidth = bgMapSizes[control >> 14][0];
|
||||
unsigned int mapHeight = bgMapSizes[control >> 14][1];
|
||||
unsigned int mapWidthInPixels = mapWidth * 8;
|
||||
unsigned int mapHeightInPixels = mapHeight * 8;
|
||||
|
||||
uint8_t *bgtiles = (uint8_t *)BG_CHAR_ADDR(charBaseBlock);
|
||||
uint16_t *pal = (uint16_t *)PLTT;
|
||||
|
||||
if (control & BGCNT_MOSAIC)
|
||||
lineNum = applyBGVerticalMosaicEffect(lineNum);
|
||||
|
||||
hoffs &= 0x1FF;
|
||||
voffs &= 0x1FF;
|
||||
|
||||
for (unsigned int x = 0; x < DISPLAY_WIDTH; x++)
|
||||
{
|
||||
uint16_t *bgmap = (uint16_t *)BG_SCREEN_ADDR(screenBaseBlock);
|
||||
// adjust for scroll
|
||||
unsigned int xx;
|
||||
if (control & BGCNT_MOSAIC)
|
||||
xx = (applyBGHorizontalMosaicEffect(x) + hoffs) & 0x1FF;
|
||||
else
|
||||
xx = (x + hoffs) & 0x1FF;
|
||||
|
||||
unsigned int yy = (lineNum + voffs) & 0x1FF;
|
||||
|
||||
//if x or y go above 255 pixels it goes to the next screen base which are 0x400 WORDs long
|
||||
if (xx > 255 && mapWidthInPixels > 256) {
|
||||
bgmap += 0x400;
|
||||
}
|
||||
|
||||
if (yy > 255 && mapHeightInPixels > 256) {
|
||||
//the width check is for 512x512 mode support, it jumps by two screen bases instead
|
||||
bgmap += (mapWidthInPixels > 256) ? 0x800 : 0x400;
|
||||
}
|
||||
|
||||
//maximum width for bgtile block is 256
|
||||
xx &= 0xFF;
|
||||
yy &= 0xFF;
|
||||
|
||||
unsigned int mapX = xx / 8;
|
||||
unsigned int mapY = yy / 8;
|
||||
uint16_t entry = bgmap[mapY * 32 + mapX];
|
||||
|
||||
unsigned int tileNum = entry & 0x3FF;
|
||||
unsigned int paletteNum = (entry >> 12) & 0xF;
|
||||
|
||||
unsigned int tileX = xx % 8;
|
||||
unsigned int tileY = yy % 8;
|
||||
|
||||
// Flip if necessary
|
||||
if (entry & (1 << 10))
|
||||
tileX = 7 - tileX;
|
||||
if (entry & (1 << 11))
|
||||
tileY = 7 - tileY;
|
||||
|
||||
uint16_t tileLoc = tileNum * (bitsPerPixel * 8);
|
||||
uint16_t tileLocY = tileY * bitsPerPixel;
|
||||
uint16_t tileLocX = tileX;
|
||||
if (bitsPerPixel == 4)
|
||||
tileLocX /= 2;
|
||||
|
||||
uint8_t pixel = bgtiles[tileLoc + tileLocY + tileLocX];
|
||||
|
||||
if (bitsPerPixel == 4) {
|
||||
if (tileX & 1)
|
||||
pixel >>= 4;
|
||||
else
|
||||
pixel &= 0xF;
|
||||
|
||||
if (pixel != 0)
|
||||
line[x] = pal[16 * paletteNum + pixel] | 0x8000;
|
||||
else
|
||||
line[x] = 0;
|
||||
}
|
||||
else {
|
||||
if (pixel != 0)
|
||||
line[x] = pal[pixel] | 0x8000;
|
||||
else
|
||||
line[x] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t getBgX(int bgNumber)
|
||||
{
|
||||
if (bgNumber == 2)
|
||||
{
|
||||
return REG_BG2X;
|
||||
}
|
||||
else if (bgNumber == 3)
|
||||
{
|
||||
return REG_BG3X;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t getBgY(int bgNumber)
|
||||
{
|
||||
if (bgNumber == 2)
|
||||
{
|
||||
return REG_BG2Y;
|
||||
}
|
||||
else if (bgNumber == 3)
|
||||
{
|
||||
return REG_BG3Y;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint16_t getBgPA(int bgNumber)
|
||||
{
|
||||
if (bgNumber == 2)
|
||||
{
|
||||
return REG_BG2PA;
|
||||
}
|
||||
else if (bgNumber == 3)
|
||||
{
|
||||
return REG_BG3PA;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint16_t getBgPB(int bgNumber)
|
||||
{
|
||||
if (bgNumber == 2)
|
||||
{
|
||||
return REG_BG2PB;
|
||||
}
|
||||
else if (bgNumber == 3)
|
||||
{
|
||||
return REG_BG3PB;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint16_t getBgPC(int bgNumber)
|
||||
{
|
||||
if (bgNumber == 2)
|
||||
{
|
||||
return REG_BG2PC;
|
||||
}
|
||||
else if (bgNumber == 3)
|
||||
{
|
||||
return REG_BG3PC;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint16_t getBgPD(int bgNumber)
|
||||
{
|
||||
if (bgNumber == 2)
|
||||
{
|
||||
return REG_BG2PD;
|
||||
}
|
||||
else if (bgNumber == 3)
|
||||
{
|
||||
return REG_BG3PD;
|
||||
}
|
||||
}
|
||||
|
||||
static void RenderRotScaleBGScanline(int bgNum, uint16_t control, uint16_t x, uint16_t y, int lineNum, uint16_t *line)
|
||||
{
|
||||
vBgCnt *bgcnt = (vBgCnt *)&control;
|
||||
unsigned int charBaseBlock = bgcnt->charBaseBlock;
|
||||
unsigned int screenBaseBlock = bgcnt->screenBaseBlock;
|
||||
unsigned int mapWidth = 1 << (4 + (bgcnt->screenSize)); // number of tiles
|
||||
|
||||
uint8_t *bgtiles = (uint8_t *)(VRAM_ + charBaseBlock * 0x4000);
|
||||
uint8_t *bgmap = (uint8_t *)(VRAM_ + screenBaseBlock * 0x800);
|
||||
uint16_t *pal = (uint16_t *)PLTT;
|
||||
|
||||
if (control & BGCNT_MOSAIC)
|
||||
lineNum = applyBGVerticalMosaicEffect(lineNum);
|
||||
|
||||
|
||||
s16 pa = getBgPA(bgNum);
|
||||
s16 pb = getBgPB(bgNum);
|
||||
s16 pc = getBgPC(bgNum);
|
||||
s16 pd = getBgPD(bgNum);
|
||||
|
||||
if (pa == 0 && pb == 0 && pc == 0 && pd == 0) {
|
||||
pa = 0x0100;
|
||||
pd = 0x0100;
|
||||
}
|
||||
|
||||
int sizeX = 128;
|
||||
int sizeY = 128;
|
||||
|
||||
switch (bgcnt->screenSize)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
sizeX = sizeY = 256;
|
||||
break;
|
||||
case 2:
|
||||
sizeX = sizeY = 512;
|
||||
break;
|
||||
case 3:
|
||||
sizeX = sizeY = 1024;
|
||||
break;
|
||||
}
|
||||
|
||||
int maskX = sizeX - 1;
|
||||
int maskY = sizeY - 1;
|
||||
|
||||
int yshift = ((control >> 14) & 3) + 4;
|
||||
|
||||
/*int dx = pa & 0x7FFF;
|
||||
if (pa & 0x8000)
|
||||
dx |= 0xFFFF8000;
|
||||
int dmx = pb & 0x7FFF;
|
||||
if (pb & 0x8000)
|
||||
dmx |= 0xFFFF8000;
|
||||
int dy = pc & 0x7FFF;
|
||||
if (pc & 0x8000)
|
||||
dy |= 0xFFFF8000;
|
||||
int dmy = pd & 0x7FFF;
|
||||
if (pd & 0x8000)
|
||||
dmy |= 0xFFFF8000;*/
|
||||
|
||||
s32 currentX = getBgX(bgNum);
|
||||
s32 currentY = getBgY(bgNum);
|
||||
//sign extend 28 bit number
|
||||
currentX = ((currentX & (1 << 27)) ? currentX | 0xF0000000 : currentX);
|
||||
currentY = ((currentY & (1 << 27)) ? currentY | 0xF0000000 : currentY);
|
||||
|
||||
currentX += lineNum * pb;
|
||||
currentY += lineNum * pd;
|
||||
|
||||
int realX = currentX;
|
||||
int realY = currentY;
|
||||
|
||||
if (bgcnt->areaOverflowMode)
|
||||
{
|
||||
for (int x = 0; x < DISPLAY_WIDTH; x++)
|
||||
{
|
||||
int xxx = (realX >> 8) & maskX;
|
||||
int yyy = (realY >> 8) & maskY;
|
||||
|
||||
int tile = bgmap[(xxx >> 3) + ((yyy >> 3) << yshift)];
|
||||
|
||||
int tileX = xxx & 7;
|
||||
int tileY = yyy & 7;
|
||||
|
||||
uint8_t pixel = bgtiles[(tile << 6) + (tileY << 3) + tileX];
|
||||
|
||||
if (pixel != 0) {
|
||||
line[x] = pal[pixel] | 0x8000;
|
||||
}
|
||||
|
||||
realX += pa;
|
||||
realY += pc;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int x = 0; x < DISPLAY_WIDTH; x++)
|
||||
{
|
||||
int xxx = (realX >> 8);
|
||||
int yyy = (realY >> 8);
|
||||
|
||||
if (xxx < 0 || yyy < 0 || xxx >= sizeX || yyy >= sizeY)
|
||||
{
|
||||
//line[x] = 0x80000000;
|
||||
}
|
||||
else
|
||||
{
|
||||
int tile = bgmap[(xxx >> 3) + ((yyy >> 3) << yshift)];
|
||||
|
||||
int tileX = xxx & 7;
|
||||
int tileY = yyy & 7;
|
||||
|
||||
uint8_t pixel = bgtiles[(tile << 6) + (tileY << 3) + tileX];
|
||||
|
||||
if (pixel != 0) {
|
||||
line[x] = pal[pixel] | 0x8000;
|
||||
}
|
||||
}
|
||||
realX += pa;
|
||||
realY += pc;
|
||||
}
|
||||
}
|
||||
//the only way i could figure out how to get accurate mosaic on affine bgs
|
||||
//luckily i dont think pokemon emerald uses mosaic on affine bgs
|
||||
if (control & BGCNT_MOSAIC && mosaicBGEffectX > 0)
|
||||
{
|
||||
for (int x = 0; x < DISPLAY_WIDTH; x++)
|
||||
{
|
||||
uint16_t color = line[applyBGHorizontalMosaicEffect(x)];
|
||||
line[x] = color;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const u8 spriteSizes[][2] =
|
||||
{
|
||||
{8, 16},
|
||||
{8, 32},
|
||||
{16, 32},
|
||||
{32, 64},
|
||||
};
|
||||
|
||||
static uint16_t alphaBlendColor(uint16_t targetA, uint16_t targetB)
|
||||
{
|
||||
unsigned int eva = REG_BLDALPHA & 0x1F;
|
||||
unsigned int evb = (REG_BLDALPHA >> 8) & 0x1F;
|
||||
// shift right by 4 = division by 16
|
||||
unsigned int r = ((getRedChannel(targetA) * eva) + (getRedChannel(targetB) * evb)) >> 4;
|
||||
unsigned int g = ((getGreenChannel(targetA) * eva) + (getGreenChannel(targetB) * evb)) >> 4;
|
||||
unsigned int b = ((getBlueChannel(targetA) * eva) + (getBlueChannel(targetB) * evb)) >> 4;
|
||||
|
||||
if (r > 31)
|
||||
r = 31;
|
||||
if (g > 31)
|
||||
g = 31;
|
||||
if (b > 31)
|
||||
b = 31;
|
||||
|
||||
return r | (g << 5) | (b << 10) | (1 << 15);
|
||||
}
|
||||
|
||||
static uint16_t alphaBrightnessIncrease(uint16_t targetA)
|
||||
{
|
||||
unsigned int evy = (REG_BLDY & 0x1F);
|
||||
unsigned int r = getRedChannel(targetA) + (31 - getRedChannel(targetA)) * evy / 16;
|
||||
unsigned int g = getGreenChannel(targetA) + (31 - getGreenChannel(targetA)) * evy / 16;
|
||||
unsigned int b = getBlueChannel(targetA) + (31 - getBlueChannel(targetA)) * evy / 16;
|
||||
|
||||
if (r > 31)
|
||||
r = 31;
|
||||
if (g > 31)
|
||||
g = 31;
|
||||
if (b > 31)
|
||||
b = 31;
|
||||
|
||||
return r | (g << 5) | (b << 10) | (1 << 15);
|
||||
}
|
||||
|
||||
static uint16_t alphaBrightnessDecrease(uint16_t targetA)
|
||||
{
|
||||
unsigned int evy = (REG_BLDY & 0x1F);
|
||||
unsigned int r = getRedChannel(targetA) - getRedChannel(targetA) * evy / 16;
|
||||
unsigned int g = getGreenChannel(targetA) - getGreenChannel(targetA) * evy / 16;
|
||||
unsigned int b = getBlueChannel(targetA) - getBlueChannel(targetA) * evy / 16;
|
||||
|
||||
if (r > 31)
|
||||
r = 31;
|
||||
if (g > 31)
|
||||
g = 31;
|
||||
if (b > 31)
|
||||
b = 31;
|
||||
|
||||
return r | (g << 5) | (b << 10) | (1 << 15);
|
||||
}
|
||||
|
||||
//outputs the blended pixel in colorOutput, the prxxx are the bg priority and subpriority, pixelpos is pixel offset in scanline
|
||||
static bool alphaBlendSelectTargetB(struct scanlineData* scanline, uint16_t* colorOutput, char prnum, char prsub, int pixelpos, bool spriteBlendEnabled)
|
||||
{
|
||||
//iterate trough every possible bg to blend with, starting from specified priorities from arguments
|
||||
for (unsigned int blndprnum = prnum; blndprnum <= 3; blndprnum++)
|
||||
{
|
||||
//check if sprite is available to blend with, if sprite blending is enabled
|
||||
if (spriteBlendEnabled == true && getAlphaBit(scanline->spriteLayers[blndprnum][pixelpos]) == 1)
|
||||
{
|
||||
*colorOutput = scanline->spriteLayers[blndprnum][pixelpos];
|
||||
return true;
|
||||
}
|
||||
|
||||
for (unsigned int blndprsub = prsub; blndprsub < scanline->prioritySortedBgsCount[blndprnum]; blndprsub++)
|
||||
{
|
||||
char currLayer = scanline->prioritySortedBgs[blndprnum][blndprsub];
|
||||
if (getAlphaBit( scanline->layers[currLayer][pixelpos] ) == 1 && REG_BLDCNT & ( 1 << (8 + currLayer)) && isbgEnabled(currLayer))
|
||||
{
|
||||
*colorOutput = scanline->layers[currLayer][pixelpos];
|
||||
return true;
|
||||
}
|
||||
//if we hit a non target layer we should bail
|
||||
if ( getAlphaBit( scanline->layers[currLayer][pixelpos] ) == 1 && isbgEnabled(currLayer) && prnum != blndprnum )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
prsub = 0; //start from zero in the next iteration
|
||||
}
|
||||
//no background got hit, check if backdrop is enabled and return it if enabled otherwise fail
|
||||
if (REG_BLDCNT & BLDCNT_TGT2_BD)
|
||||
{
|
||||
*colorOutput = *(uint16_t*)PLTT;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//checks if window horizontal is in bounds and takes account WIN wraparound
|
||||
static bool winCheckHorizontalBounds(u16 left, u16 right, u16 xpos)
|
||||
{
|
||||
if (left > right)
|
||||
return (xpos >= left || xpos < right);
|
||||
else
|
||||
return (xpos >= left && xpos < right);
|
||||
}
|
||||
|
||||
// Parts of this code heavily borrowed from NanoboyAdvance.
|
||||
static void DrawSprites(struct scanlineData* scanline, uint16_t vcount, bool windowsEnabled)
|
||||
{
|
||||
int i;
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
void *objtiles = VRAM_ + 0x10000;
|
||||
unsigned int blendMode = (REG_BLDCNT >> 6) & 3;
|
||||
bool winShouldBlendPixel = true;
|
||||
|
||||
int16_t matrix[2][2] = {};
|
||||
|
||||
if (!(REG_DISPCNT & (1 << 6)))
|
||||
{
|
||||
puts("2-D OBJ Character mapping not supported.");
|
||||
}
|
||||
|
||||
for (i = 127; i >= 0; i--)
|
||||
{
|
||||
struct OamData *oam = &((struct OamData *)OAM)[i];
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
uint16_t *pixels;
|
||||
|
||||
bool isAffine = oam->affineMode & 1;
|
||||
bool doubleSizeOrDisabled = (oam->affineMode >> 1) & 1;
|
||||
bool isSemiTransparent = (oam->objMode == 1);
|
||||
bool isObjWin = (oam->objMode == 2);
|
||||
|
||||
if (!(isAffine) && doubleSizeOrDisabled) // disable for non-affine
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (oam->shape == 0)
|
||||
{
|
||||
width = (1 << oam->size) * 8;
|
||||
height = (1 << oam->size) * 8;
|
||||
}
|
||||
else if (oam->shape == 1) // wide
|
||||
{
|
||||
width = spriteSizes[oam->size][1];
|
||||
height = spriteSizes[oam->size][0];
|
||||
}
|
||||
else if (oam->shape == 2) // tall
|
||||
{
|
||||
width = spriteSizes[oam->size][0];
|
||||
height = spriteSizes[oam->size][1];
|
||||
}
|
||||
else
|
||||
{
|
||||
continue; // prohibited, do not draw
|
||||
}
|
||||
|
||||
int rect_width = width;
|
||||
int rect_height = height;
|
||||
|
||||
int half_width = width / 2;
|
||||
int half_height = height / 2;
|
||||
|
||||
pixels = scanline->spriteLayers[oam->priority];
|
||||
|
||||
int32_t x = oam->x;
|
||||
int32_t y = oam->y;
|
||||
|
||||
if (x >= DISPLAY_WIDTH)
|
||||
x -= 512;
|
||||
if (y >= DISPLAY_HEIGHT)
|
||||
y -= 256;
|
||||
|
||||
if (isAffine)
|
||||
{
|
||||
//TODO: there is probably a better way to do this
|
||||
u8 matrixNum = oam->matrixNum * 4;
|
||||
|
||||
struct OamData *oam1 = &((struct OamData *)OAM)[matrixNum];
|
||||
struct OamData *oam2 = &((struct OamData *)OAM)[matrixNum + 1];
|
||||
struct OamData *oam3 = &((struct OamData *)OAM)[matrixNum + 2];
|
||||
struct OamData *oam4 = &((struct OamData *)OAM)[matrixNum + 3];
|
||||
|
||||
matrix[0][0] = oam1->affineParam;
|
||||
matrix[0][1] = oam2->affineParam;
|
||||
matrix[1][0] = oam3->affineParam;
|
||||
matrix[1][1] = oam4->affineParam;
|
||||
|
||||
if (doubleSizeOrDisabled) // double size for affine
|
||||
{
|
||||
rect_width *= 2;
|
||||
rect_height *= 2;
|
||||
half_width *= 2;
|
||||
half_height *= 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Identity
|
||||
matrix[0][0] = 0x100;
|
||||
matrix[0][1] = 0;
|
||||
matrix[1][0] = 0;
|
||||
matrix[1][1] = 0x100;
|
||||
}
|
||||
|
||||
x += half_width;
|
||||
y += half_height;
|
||||
|
||||
// Does this sprite actually draw on this scanline?
|
||||
if (vcount >= (y - half_height) && vcount < (y + half_height))
|
||||
{
|
||||
int local_y = (oam->mosaic == 1) ? applySpriteVerticalMosaicEffect(vcount) - y : vcount - y;
|
||||
int number = oam->tileNum;
|
||||
int palette = oam->paletteNum;
|
||||
bool flipX = !isAffine && ((oam->matrixNum >> 3) & 1);
|
||||
bool flipY = !isAffine && ((oam->matrixNum >> 4) & 1);
|
||||
bool is8BPP = oam->bpp & 1;
|
||||
|
||||
for (int local_x = -half_width; local_x <= half_width; local_x++)
|
||||
{
|
||||
uint8_t *tiledata = (uint8_t *)objtiles;
|
||||
uint16_t *palette = (uint16_t *)(PLTT + 0x200);
|
||||
int local_mosaicX;
|
||||
int tex_x;
|
||||
int tex_y;
|
||||
|
||||
unsigned int global_x = local_x + x;
|
||||
|
||||
if (global_x < 0 || global_x >= DISPLAY_WIDTH)
|
||||
continue;
|
||||
|
||||
if (oam->mosaic == 1)
|
||||
{
|
||||
//mosaic effect has to be applied to global coordinates otherwise the mosaic will scroll
|
||||
local_mosaicX = applySpriteHorizontalMosaicEffect(global_x) - x;
|
||||
tex_x = ((matrix[0][0] * local_mosaicX + matrix[0][1] * local_y) >> 8) + (width / 2);
|
||||
tex_y = ((matrix[1][0] * local_mosaicX + matrix[1][1] * local_y) >> 8) + (height / 2);
|
||||
}else{
|
||||
tex_x = ((matrix[0][0] * local_x + matrix[0][1] * local_y) >> 8) + (width / 2);
|
||||
tex_y = ((matrix[1][0] * local_x + matrix[1][1] * local_y) >> 8) + (height / 2);
|
||||
}
|
||||
|
||||
/* Check if transformed coordinates are inside bounds. */
|
||||
|
||||
if (tex_x >= width || tex_y >= height || tex_x < 0 || tex_y < 0)
|
||||
continue;
|
||||
|
||||
if (flipX)
|
||||
tex_x = width - tex_x - 1;
|
||||
if (flipY)
|
||||
tex_y = height - tex_y - 1;
|
||||
|
||||
int tile_x = tex_x % 8;
|
||||
int tile_y = tex_y % 8;
|
||||
int block_x = tex_x / 8;
|
||||
int block_y = tex_y / 8;
|
||||
int block_offset = ((block_y * (REG_DISPCNT & 0x40 ? (width / 8) : 16)) + block_x);
|
||||
uint16_t pixel = 0;
|
||||
|
||||
if (!is8BPP)
|
||||
{
|
||||
pixel = tiledata[(block_offset + oam->tileNum) * 32 + (tile_y * 4) + (tile_x / 2)];
|
||||
if (tile_x & 1)
|
||||
pixel >>= 4;
|
||||
else
|
||||
pixel &= 0xF;
|
||||
palette += oam->paletteNum * 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
pixel = tiledata[(block_offset * 2 + oam->tileNum) * 32 + (tile_y * 8) + tile_x];
|
||||
}
|
||||
|
||||
if (pixel != 0)
|
||||
{
|
||||
uint16_t color = palette[pixel];;
|
||||
|
||||
//if sprite mode is 2 then write to the window mask instead
|
||||
if (isObjWin)
|
||||
{
|
||||
if (scanline->winMask[global_x] & WINMASK_WINOUT)
|
||||
scanline->winMask[global_x] = (REG_WINOUT >> 8) & 0x3F;
|
||||
continue;
|
||||
}
|
||||
//this code runs if pixel is to be drawn
|
||||
if (global_x < DISPLAY_WIDTH && global_x >= 0)
|
||||
{
|
||||
//check if its enabled in the window (if window is enabled)
|
||||
winShouldBlendPixel = (windowsEnabled == false || scanline->winMask[global_x] & WINMASK_CLR);
|
||||
|
||||
//has to be separated from the blend mode switch statement because of OBJ semi transparancy feature
|
||||
if ((blendMode == 1 && REG_BLDCNT & BLDCNT_TGT1_OBJ && winShouldBlendPixel) || isSemiTransparent)
|
||||
{
|
||||
uint16_t targetA = color;
|
||||
uint16_t targetB = 0;
|
||||
if (alphaBlendSelectTargetB(scanline, &targetB, oam->priority, 0, global_x, false))
|
||||
{
|
||||
color = alphaBlendColor(targetA, targetB);
|
||||
}
|
||||
}
|
||||
else if (REG_BLDCNT & BLDCNT_TGT1_OBJ && winShouldBlendPixel)
|
||||
{
|
||||
switch (blendMode)
|
||||
{
|
||||
case 2:
|
||||
color = alphaBrightnessIncrease(color);
|
||||
break;
|
||||
case 3:
|
||||
color = alphaBrightnessDecrease(color);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//write pixel to pixel framebuffer
|
||||
pixels[global_x] = color | (1 << 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawScanline(uint16_t *pixels, uint16_t vcount)
|
||||
{
|
||||
unsigned int mode = REG_DISPCNT & 3;
|
||||
unsigned char numOfBgs = (mode == 0 ? 4 : 3);
|
||||
int bgnum, prnum;
|
||||
struct scanlineData scanline;
|
||||
unsigned int blendMode = (REG_BLDCNT >> 6) & 3;
|
||||
unsigned int xpos;
|
||||
|
||||
|
||||
//initialize all priority bookkeeping data
|
||||
memset(scanline.layers, 0, sizeof(scanline.layers));
|
||||
memset(scanline.winMask, 0, sizeof(scanline.winMask));
|
||||
memset(scanline.spriteLayers, 0, sizeof(scanline.spriteLayers));
|
||||
memset(scanline.prioritySortedBgsCount, 0, sizeof(scanline.prioritySortedBgsCount));
|
||||
|
||||
for (bgnum = 0; bgnum < numOfBgs; bgnum++)
|
||||
{
|
||||
uint16_t bgcnt = *(uint16_t*)(REG_ADDR_BG0CNT + bgnum * 2);
|
||||
uint16_t priority;
|
||||
scanline.bgcnts[bgnum] = bgcnt;
|
||||
scanline.bgtoprio[bgnum] = priority = (bgcnt & 3);
|
||||
|
||||
char priorityCount = scanline.prioritySortedBgsCount[priority];
|
||||
scanline.prioritySortedBgs[priority][priorityCount] = bgnum;
|
||||
scanline.prioritySortedBgsCount[priority]++;
|
||||
}
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
// All backgrounds are text mode
|
||||
for (bgnum = 3; bgnum >= 0; bgnum--)
|
||||
{
|
||||
if (isbgEnabled(bgnum))
|
||||
{
|
||||
uint16_t bghoffs = *(uint16_t *)(REG_ADDR_BG0HOFS + bgnum * 4);
|
||||
uint16_t bgvoffs = *(uint16_t *)(REG_ADDR_BG0VOFS + bgnum * 4);
|
||||
|
||||
RenderBGScanline(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, scanline.layers[bgnum]);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case 1:
|
||||
// BG2 is affine
|
||||
bgnum = 2;
|
||||
if (isbgEnabled(bgnum))
|
||||
{
|
||||
RenderRotScaleBGScanline(bgnum, scanline.bgcnts[bgnum], REG_BG2X, REG_BG2Y, vcount, scanline.layers[bgnum]);
|
||||
}
|
||||
// BG0 and BG1 are text mode
|
||||
for (bgnum = 1; bgnum >= 0; bgnum--)
|
||||
{
|
||||
if (isbgEnabled(bgnum))
|
||||
{
|
||||
uint16_t bghoffs = *(uint16_t *)(REG_ADDR_BG0HOFS + bgnum * 4);
|
||||
uint16_t bgvoffs = *(uint16_t *)(REG_ADDR_BG0VOFS + bgnum * 4);
|
||||
|
||||
RenderBGScanline(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, scanline.layers[bgnum]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("Video mode %u is unsupported.\n", mode);
|
||||
break;
|
||||
}
|
||||
|
||||
bool windowsEnabled = false;
|
||||
uint16_t WIN0bottom, WIN0top, WIN0right, WIN0left;
|
||||
uint16_t WIN1bottom, WIN1top, WIN1right, WIN1left;
|
||||
bool WIN0enable, WIN1enable;
|
||||
WIN0enable = false;
|
||||
WIN1enable = false;
|
||||
|
||||
//figure out if WIN0 masks on this scanline
|
||||
if (REG_DISPCNT & DISPCNT_WIN0_ON)
|
||||
{
|
||||
//acquire the window coordinates
|
||||
WIN0bottom = (REG_WIN0V & 0xFF); //y2;
|
||||
WIN0top = (REG_WIN0V & 0xFF00) >> 8; //y1;
|
||||
WIN0right = (REG_WIN0H & 0xFF); //x2
|
||||
WIN0left = (REG_WIN0H & 0xFF00) >> 8; //x1
|
||||
|
||||
//figure out WIN Y wraparound and check bounds accordingly
|
||||
if (WIN0top > WIN0bottom) {
|
||||
if (vcount >= WIN0top || vcount < WIN0bottom)
|
||||
WIN0enable = true;
|
||||
} else {
|
||||
if (vcount >= WIN0top && vcount < WIN0bottom)
|
||||
WIN0enable = true;
|
||||
}
|
||||
|
||||
windowsEnabled = true;
|
||||
}
|
||||
//figure out if WIN1 masks on this scanline
|
||||
if (REG_DISPCNT & DISPCNT_WIN1_ON)
|
||||
{
|
||||
WIN1bottom = (REG_WIN1V & 0xFF);
|
||||
WIN1top = (REG_WIN1V & 0xFF00) >> 8;
|
||||
WIN1right = (REG_WIN1H & 0xFF);
|
||||
WIN1left = (REG_WIN1H & 0xFF00) >> 8;
|
||||
|
||||
if (WIN1top > WIN1bottom) {
|
||||
if (vcount >= WIN1top || vcount < WIN1bottom)
|
||||
WIN1enable = true;
|
||||
} else {
|
||||
if (vcount >= WIN1top && vcount < WIN1bottom)
|
||||
WIN1enable = true;
|
||||
}
|
||||
|
||||
windowsEnabled = true;
|
||||
}
|
||||
//enable windows if OBJwin is enabled
|
||||
if (REG_DISPCNT & DISPCNT_OBJWIN_ON && REG_DISPCNT & DISPCNT_OBJ_ON)
|
||||
{
|
||||
windowsEnabled = true;
|
||||
}
|
||||
|
||||
//draw to pixel mask
|
||||
if (windowsEnabled)
|
||||
{
|
||||
for (xpos = 0; xpos < DISPLAY_WIDTH; xpos++)
|
||||
{
|
||||
//win0 checks
|
||||
if (WIN0enable && winCheckHorizontalBounds(WIN0left, WIN0right, xpos))
|
||||
scanline.winMask[xpos] = REG_WININ & 0x3F;
|
||||
//win1 checks
|
||||
else if (WIN1enable && winCheckHorizontalBounds(WIN1left, WIN1right, xpos))
|
||||
scanline.winMask[xpos] = (REG_WININ >> 8) & 0x3F;
|
||||
else
|
||||
scanline.winMask[xpos] = (REG_WINOUT & 0x3F) | WINMASK_WINOUT;
|
||||
}
|
||||
}
|
||||
|
||||
if (REG_DISPCNT & DISPCNT_OBJ_ON)
|
||||
DrawSprites(&scanline, vcount, windowsEnabled);
|
||||
|
||||
//iterate trough every priority in order
|
||||
for (prnum = 3; prnum >= 0; prnum--)
|
||||
{
|
||||
for (char prsub = scanline.prioritySortedBgsCount[prnum] - 1; prsub >= 0; prsub--)
|
||||
{
|
||||
char bgnum = scanline.prioritySortedBgs[prnum][prsub];
|
||||
//if background is enabled then draw it
|
||||
if (isbgEnabled(bgnum))
|
||||
{
|
||||
uint16_t *src = scanline.layers[bgnum];
|
||||
//copy all pixels to framebuffer
|
||||
for (xpos = 0; xpos < DISPLAY_WIDTH; xpos++)
|
||||
{
|
||||
uint16_t color = src[xpos];
|
||||
bool winEffectEnable = true;
|
||||
|
||||
if (!getAlphaBit(color))
|
||||
continue; //do nothing if alpha bit is not set
|
||||
|
||||
if (windowsEnabled)
|
||||
{
|
||||
winEffectEnable = ((scanline.winMask[xpos] & WINMASK_CLR) >> 5);
|
||||
//if bg is disabled inside the window then do not draw the pixel
|
||||
if ( !(scanline.winMask[xpos] & 1 << bgnum) )
|
||||
continue;
|
||||
}
|
||||
|
||||
//blending code
|
||||
if (blendMode != 0 && REG_BLDCNT & (1 << bgnum) && winEffectEnable)
|
||||
{
|
||||
uint16_t targetA = color;
|
||||
uint16_t targetB = 0;
|
||||
char isSpriteBlendingEnabled;
|
||||
|
||||
switch (blendMode)
|
||||
{
|
||||
case 1:
|
||||
isSpriteBlendingEnabled = REG_BLDCNT & BLDCNT_TGT2_OBJ ? 1 : 0;
|
||||
//find targetB and blend it
|
||||
if (alphaBlendSelectTargetB(&scanline, &targetB, prnum, prsub+1, xpos, isSpriteBlendingEnabled))
|
||||
{
|
||||
color = alphaBlendColor(targetA, targetB);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
color = alphaBrightnessIncrease(targetA);
|
||||
break;
|
||||
case 3:
|
||||
color = alphaBrightnessDecrease(targetA);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//write the pixel to scanline buffer output
|
||||
pixels[xpos] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
//draw sprites on current priority
|
||||
uint16_t *src = scanline.spriteLayers[prnum];
|
||||
for (xpos = 0; xpos < DISPLAY_WIDTH; xpos++)
|
||||
{
|
||||
if (getAlphaBit(src[xpos]))
|
||||
{
|
||||
//check if sprite pixel draws inside window
|
||||
if (windowsEnabled && !(scanline.winMask[xpos] & WINMASK_OBJ))
|
||||
continue;
|
||||
//draw the pixel
|
||||
pixels[xpos] = src[xpos];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t *memsetu16(uint16_t *dst, uint16_t fill, size_t count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
*dst++ = fill;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawFrame(uint16_t *pixels)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
|
||||
for (i = 0; i < DISPLAY_HEIGHT; i++)
|
||||
{
|
||||
REG_VCOUNT = i;
|
||||
if(((REG_DISPSTAT >> 8) & 0xFF) == REG_VCOUNT)
|
||||
{
|
||||
REG_DISPSTAT |= INTR_FLAG_VCOUNT;
|
||||
if(REG_DISPSTAT & DISPSTAT_VCOUNT_INTR)
|
||||
gIntrTable[0]();
|
||||
}
|
||||
|
||||
// Render the backdrop color before the each individual scanline.
|
||||
// backdrop color brightness effects
|
||||
unsigned int blendMode = (REG_BLDCNT >> 6) & 3;
|
||||
uint16_t backdropColor = *(uint16_t *)PLTT;
|
||||
if (REG_BLDCNT & BLDCNT_TGT1_BD)
|
||||
{
|
||||
switch (blendMode)
|
||||
{
|
||||
case 2:
|
||||
backdropColor = alphaBrightnessIncrease(backdropColor);
|
||||
break;
|
||||
case 3:
|
||||
backdropColor = alphaBrightnessDecrease(backdropColor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memsetu16(&pixels[i * DISPLAY_WIDTH], backdropColor, DISPLAY_WIDTH);
|
||||
DrawScanline(&pixels[i * DISPLAY_WIDTH], i);
|
||||
|
||||
REG_DISPSTAT |= INTR_FLAG_HBLANK;
|
||||
|
||||
RunDMAs(DMA_HBLANK);
|
||||
|
||||
if (REG_DISPSTAT & DISPSTAT_HBLANK_INTR)
|
||||
gIntrTable[3]();
|
||||
|
||||
REG_DISPSTAT &= ~INTR_FLAG_HBLANK;
|
||||
REG_DISPSTAT &= ~INTR_FLAG_VCOUNT;
|
||||
}
|
||||
}
|
||||
2728
src/platform/gba_fast_draw.c
Normal file
2728
src/platform/gba_fast_draw.c
Normal file
File diff suppressed because it is too large
Load Diff
513
src/platform/sdl2.c
Normal file
513
src/platform/sdl2.c
Normal file
|
|
@ -0,0 +1,513 @@
|
|||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <xinput.h>
|
||||
#endif
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "global.h"
|
||||
#include "platform.h"
|
||||
#include "gba/defines.h"
|
||||
#include "gba/m4a_internal.h"
|
||||
#include "cgb_audio.h"
|
||||
#include "gba/flash_internal.h"
|
||||
#include "platform/dma.h"
|
||||
#include "platform/framedraw.h"
|
||||
|
||||
extern IntrFunc gIntrTable[];
|
||||
|
||||
SDL_Thread *mainLoopThread;
|
||||
SDL_Window *sdlWindow;
|
||||
SDL_Renderer *sdlRenderer;
|
||||
SDL_Texture *sdlTexture;
|
||||
SDL_sem *vBlankSemaphore;
|
||||
SDL_atomic_t isFrameAvailable;
|
||||
bool speedUp = false;
|
||||
unsigned int videoScale = 1;
|
||||
bool videoScaleChanged = false;
|
||||
bool isRunning = true;
|
||||
bool paused = false;
|
||||
double simTime = 0;
|
||||
double lastGameTime = 0;
|
||||
double curGameTime = 0;
|
||||
double fixedTimestep = 1.0 / 60.0; // 16.666667ms
|
||||
double timeScale = 1.0;
|
||||
|
||||
|
||||
static FILE *sSaveFile = NULL;
|
||||
|
||||
extern void AgbMain(void);
|
||||
extern void DoSoftReset(void);
|
||||
|
||||
int DoMain(void *param);
|
||||
void ProcessEvents(void);
|
||||
void VDraw(SDL_Texture *texture);
|
||||
|
||||
static void ReadSaveFile(char *path);
|
||||
static void StoreSaveFile(void);
|
||||
static void CloseSaveFile(void);
|
||||
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// Open an output console on Windows
|
||||
#ifdef _WIN32
|
||||
AllocConsole() ;
|
||||
AttachConsole( GetCurrentProcessId() ) ;
|
||||
freopen( "CON", "w", stdout ) ;
|
||||
#endif
|
||||
|
||||
ReadSaveFile("pokefirered.sav");
|
||||
|
||||
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
|
||||
{
|
||||
printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
sdlWindow = SDL_CreateWindow("pokefirered", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, DISPLAY_WIDTH * videoScale, DISPLAY_HEIGHT * videoScale, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
|
||||
if (sdlWindow == NULL)
|
||||
{
|
||||
printf("Window could not be created! SDL_Error: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_PRESENTVSYNC);
|
||||
if (sdlRenderer == NULL)
|
||||
{
|
||||
printf("Renderer could not be created! SDL_Error: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_SetRenderDrawColor(sdlRenderer, 255, 255, 255, 255);
|
||||
SDL_RenderClear(sdlRenderer);
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
|
||||
SDL_RenderSetLogicalSize(sdlRenderer, DISPLAY_WIDTH, DISPLAY_HEIGHT);
|
||||
|
||||
sdlTexture = SDL_CreateTexture(sdlRenderer,
|
||||
SDL_PIXELFORMAT_ABGR1555,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT);
|
||||
if (sdlTexture == NULL)
|
||||
{
|
||||
printf("Texture could not be created! SDL_Error: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
simTime = curGameTime = lastGameTime = SDL_GetPerformanceCounter();
|
||||
|
||||
isFrameAvailable.value = 0;
|
||||
vBlankSemaphore = SDL_CreateSemaphore(0);
|
||||
|
||||
SDL_AudioSpec want;
|
||||
|
||||
SDL_memset(&want, 0, sizeof(want)); /* or SDL_zero(want) */
|
||||
want.freq = 42048;
|
||||
want.format = AUDIO_F32;
|
||||
want.channels = 2;
|
||||
want.samples = 1024;
|
||||
cgb_audio_init(want.freq);
|
||||
|
||||
|
||||
if (SDL_OpenAudio(&want, 0) < 0)
|
||||
SDL_Log("Failed to open audio: %s", SDL_GetError());
|
||||
else
|
||||
{
|
||||
if (want.format != AUDIO_F32) /* we let this one thing change. */
|
||||
SDL_Log("We didn't get Float32 audio format.");
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
|
||||
VDraw(sdlTexture);
|
||||
mainLoopThread = SDL_CreateThread(DoMain, "AgbMain", NULL);
|
||||
|
||||
double accumulator = 0.0;
|
||||
|
||||
while (isRunning)
|
||||
{
|
||||
ProcessEvents();
|
||||
|
||||
if (!paused)
|
||||
{
|
||||
double dt = fixedTimestep / timeScale; // TODO: Fix speedup
|
||||
|
||||
curGameTime = SDL_GetPerformanceCounter();
|
||||
double deltaTime = (double)((curGameTime - lastGameTime) / (double)SDL_GetPerformanceFrequency());
|
||||
if (deltaTime > (dt * 5))
|
||||
deltaTime = dt;
|
||||
lastGameTime = curGameTime;
|
||||
|
||||
accumulator += deltaTime;
|
||||
|
||||
while (accumulator >= dt)
|
||||
{
|
||||
if (SDL_AtomicGet(&isFrameAvailable))
|
||||
{
|
||||
VDraw(sdlTexture);
|
||||
|
||||
SDL_RenderClear(sdlRenderer);
|
||||
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
|
||||
SDL_AtomicSet(&isFrameAvailable, 0);
|
||||
|
||||
REG_DISPSTAT |= INTR_FLAG_VBLANK;
|
||||
|
||||
RunDMAs(DMA_HBLANK);
|
||||
if (REG_DISPSTAT & DISPSTAT_VBLANK_INTR){
|
||||
gIntrTable[4]();
|
||||
}
|
||||
REG_DISPSTAT &= ~INTR_FLAG_VBLANK;
|
||||
|
||||
SDL_SemPost(vBlankSemaphore);
|
||||
|
||||
accumulator -= dt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (videoScaleChanged)
|
||||
{
|
||||
SDL_SetWindowSize(sdlWindow, DISPLAY_WIDTH * videoScale, DISPLAY_HEIGHT * videoScale);
|
||||
videoScaleChanged = false;
|
||||
}
|
||||
|
||||
SDL_RenderPresent(sdlRenderer);
|
||||
}
|
||||
|
||||
//StoreSaveFile();
|
||||
CloseSaveFile();
|
||||
|
||||
SDL_DestroyWindow(sdlWindow);
|
||||
SDL_Quit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ReadSaveFile(char *path)
|
||||
{
|
||||
// Check whether the saveFile exists, and create it if not
|
||||
sSaveFile = fopen(path, "r+b");
|
||||
if (sSaveFile == NULL)
|
||||
{
|
||||
sSaveFile = fopen(path, "w+b");
|
||||
}
|
||||
|
||||
fseek(sSaveFile, 0, SEEK_END);
|
||||
int fileSize = ftell(sSaveFile);
|
||||
fseek(sSaveFile, 0, SEEK_SET);
|
||||
|
||||
// Only read as many bytes as fit inside the buffer
|
||||
// or as many bytes as are in the file
|
||||
int bytesToRead = (fileSize < sizeof(FLASH_BASE)) ? fileSize : sizeof(FLASH_BASE);
|
||||
|
||||
int bytesRead = fread(FLASH_BASE, 1, bytesToRead, sSaveFile);
|
||||
|
||||
// Fill the buffer if the savefile was just created or smaller than the buffer itself
|
||||
for (int i = bytesRead; i < sizeof(FLASH_BASE); i++)
|
||||
{
|
||||
FLASH_BASE[i] = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
static void StoreSaveFile()
|
||||
{
|
||||
if (sSaveFile != NULL)
|
||||
{
|
||||
fseek(sSaveFile, 0, SEEK_SET);
|
||||
fwrite(FLASH_BASE, 1, sizeof(FLASH_BASE), sSaveFile);
|
||||
}
|
||||
}
|
||||
|
||||
void Platform_StoreSaveFile(void)
|
||||
{
|
||||
StoreSaveFile();
|
||||
}
|
||||
|
||||
void Platform_ReadFlash(u16 sectorNum, u32 offset, u8 *dest, u32 size)
|
||||
{
|
||||
printf("ReadFlash(sectorNum=0x%04X,offset=0x%08X,size=0x%02X)\n",sectorNum,offset,size);
|
||||
FILE * savefile = fopen("pokefirered.sav", "r+b");
|
||||
if (savefile == NULL)
|
||||
{
|
||||
puts("Error opening save file.");
|
||||
return;
|
||||
}
|
||||
if (fseek(savefile, (sectorNum << gFlash->sector.shift) + offset, SEEK_SET))
|
||||
{
|
||||
fclose(savefile);
|
||||
return;
|
||||
}
|
||||
if (fread(dest, 1, size, savefile) != size)
|
||||
{
|
||||
fclose(savefile);
|
||||
return;
|
||||
}
|
||||
fclose(savefile);
|
||||
}
|
||||
|
||||
void Platform_QueueAudio(float *audioBuffer, s32 samplesPerFrame)
|
||||
{
|
||||
SDL_QueueAudio(1, audioBuffer, samplesPerFrame);
|
||||
}
|
||||
|
||||
|
||||
static void CloseSaveFile()
|
||||
{
|
||||
if (sSaveFile != NULL)
|
||||
{
|
||||
fclose(sSaveFile);
|
||||
}
|
||||
}
|
||||
|
||||
// Key mappings
|
||||
#define KEY_A_BUTTON SDLK_z
|
||||
#define KEY_B_BUTTON SDLK_x
|
||||
#define KEY_START_BUTTON SDLK_RETURN
|
||||
#define KEY_SELECT_BUTTON SDLK_BACKSLASH
|
||||
#define KEY_L_BUTTON SDLK_a
|
||||
#define KEY_R_BUTTON SDLK_s
|
||||
#define KEY_DPAD_UP SDLK_UP
|
||||
#define KEY_DPAD_DOWN SDLK_DOWN
|
||||
#define KEY_DPAD_LEFT SDLK_LEFT
|
||||
#define KEY_DPAD_RIGHT SDLK_RIGHT
|
||||
|
||||
#define HANDLE_KEYUP(key) \
|
||||
case KEY_##key: keys &= ~key; break;
|
||||
|
||||
#define HANDLE_KEYDOWN(key) \
|
||||
case KEY_##key: keys |= key; break;
|
||||
|
||||
static u16 keys;
|
||||
|
||||
void ProcessEvents(void)
|
||||
{
|
||||
SDL_Event event;
|
||||
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
isRunning = false;
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
switch (event.key.keysym.sym)
|
||||
{
|
||||
HANDLE_KEYUP(A_BUTTON)
|
||||
HANDLE_KEYUP(B_BUTTON)
|
||||
HANDLE_KEYUP(START_BUTTON)
|
||||
HANDLE_KEYUP(SELECT_BUTTON)
|
||||
HANDLE_KEYUP(L_BUTTON)
|
||||
HANDLE_KEYUP(R_BUTTON)
|
||||
HANDLE_KEYUP(DPAD_UP)
|
||||
HANDLE_KEYUP(DPAD_DOWN)
|
||||
HANDLE_KEYUP(DPAD_LEFT)
|
||||
HANDLE_KEYUP(DPAD_RIGHT)
|
||||
case SDLK_SPACE:
|
||||
if (speedUp)
|
||||
{
|
||||
speedUp = false;
|
||||
timeScale = 1.0;
|
||||
SDL_ClearQueuedAudio(1);
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
switch (event.key.keysym.sym)
|
||||
{
|
||||
HANDLE_KEYDOWN(A_BUTTON)
|
||||
HANDLE_KEYDOWN(B_BUTTON)
|
||||
HANDLE_KEYDOWN(START_BUTTON)
|
||||
HANDLE_KEYDOWN(SELECT_BUTTON)
|
||||
HANDLE_KEYDOWN(L_BUTTON)
|
||||
HANDLE_KEYDOWN(R_BUTTON)
|
||||
HANDLE_KEYDOWN(DPAD_UP)
|
||||
HANDLE_KEYDOWN(DPAD_DOWN)
|
||||
HANDLE_KEYDOWN(DPAD_LEFT)
|
||||
HANDLE_KEYDOWN(DPAD_RIGHT)
|
||||
case SDLK_r:
|
||||
if (event.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL))
|
||||
{
|
||||
DoSoftReset();
|
||||
}
|
||||
break;
|
||||
case SDLK_p:
|
||||
if (event.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL))
|
||||
{
|
||||
paused = !paused;
|
||||
}
|
||||
break;
|
||||
case SDLK_SPACE:
|
||||
if (!speedUp)
|
||||
{
|
||||
speedUp = true;
|
||||
timeScale = 5.0;
|
||||
SDL_PauseAudio(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SDL_WINDOWEVENT:
|
||||
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
|
||||
{
|
||||
unsigned int w = event.window.data1;
|
||||
unsigned int h = event.window.data2;
|
||||
|
||||
videoScale = 0;
|
||||
if (w / DISPLAY_WIDTH > videoScale)
|
||||
videoScale = w / DISPLAY_WIDTH;
|
||||
if (h / DISPLAY_HEIGHT > videoScale)
|
||||
videoScale = h / DISPLAY_HEIGHT;
|
||||
if (videoScale < 1)
|
||||
videoScale = 1;
|
||||
|
||||
videoScaleChanged = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#define STICK_THRESHOLD 0.5f
|
||||
u16 GetXInputKeys()
|
||||
{
|
||||
XINPUT_STATE state;
|
||||
ZeroMemory(&state, sizeof(XINPUT_STATE));
|
||||
|
||||
DWORD dwResult = XInputGetState(0, &state);
|
||||
u16 xinputKeys = 0;
|
||||
|
||||
if (dwResult == ERROR_SUCCESS)
|
||||
{
|
||||
/* A */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_A) >> 12;
|
||||
/* B */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_X) >> 13;
|
||||
/* Start */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) >> 1;
|
||||
/* Select */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) >> 3;
|
||||
/* L */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) << 1;
|
||||
/* R */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) >> 1;
|
||||
/* Up */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) << 6;
|
||||
/* Down */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) << 6;
|
||||
/* Left */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) << 3;
|
||||
/* Right */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) << 1;
|
||||
|
||||
|
||||
/* Control Stick */
|
||||
float xAxis = (float)state.Gamepad.sThumbLX / (float)SHRT_MAX;
|
||||
float yAxis = (float)state.Gamepad.sThumbLY / (float)SHRT_MAX;
|
||||
|
||||
if (xAxis < -STICK_THRESHOLD) xinputKeys |= DPAD_LEFT;
|
||||
if (xAxis > STICK_THRESHOLD) xinputKeys |= DPAD_RIGHT;
|
||||
if (yAxis < -STICK_THRESHOLD) xinputKeys |= DPAD_DOWN;
|
||||
if (yAxis > STICK_THRESHOLD) xinputKeys |= DPAD_UP;
|
||||
|
||||
|
||||
/* Speedup */
|
||||
// Note: 'speedup' variable is only (un)set on keyboard input
|
||||
double oldTimeScale = timeScale;
|
||||
timeScale = (state.Gamepad.bRightTrigger > 0x80 || speedUp) ? 5.0 : 1.0;
|
||||
|
||||
if (oldTimeScale != timeScale)
|
||||
{
|
||||
if (timeScale > 1.0)
|
||||
{
|
||||
SDL_PauseAudio(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_ClearQueuedAudio(1);
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return xinputKeys;
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
u16 Platform_GetKeyInput(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
u16 gamepadKeys = GetXInputKeys();
|
||||
return (gamepadKeys != 0) ? gamepadKeys : keys;
|
||||
#endif
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
void VDraw(SDL_Texture *texture)
|
||||
{
|
||||
static uint16_t image[DISPLAY_WIDTH * DISPLAY_HEIGHT];
|
||||
|
||||
memset(image, 0, sizeof(image));
|
||||
DrawFrame(image);
|
||||
SDL_UpdateTexture(texture, NULL, image, DISPLAY_WIDTH * sizeof (Uint16));
|
||||
REG_VCOUNT = 161; // prep for being in VBlank period
|
||||
}
|
||||
|
||||
int DoMain(void *data)
|
||||
{
|
||||
AgbMain();
|
||||
}
|
||||
|
||||
void VBlankIntrWait(void)
|
||||
{
|
||||
SDL_AtomicSet(&isFrameAvailable, 1);
|
||||
SDL_SemWait(vBlankSemaphore);
|
||||
}
|
||||
|
||||
void printRegs(){
|
||||
printf("REG_DISPCNT %04X\n", REG_DISPCNT);
|
||||
printf("REG_DISPSTAT %04X\n", REG_DISPSTAT);
|
||||
printf("REG_VCOUNT %04X\n", REG_VCOUNT);
|
||||
printf("REG_BG0CNT %04X\n", REG_BG0CNT);
|
||||
printf("REG_BG1CNT %04X\n", REG_BG1CNT);
|
||||
printf("REG_BG2CNT %04X\n", REG_BG2CNT);
|
||||
printf("REG_BG3CNT %04X\n", REG_BG3CNT);
|
||||
printf("REG_BG0HOFS %04X\n", REG_BG0HOFS);
|
||||
printf("REG_BG0VOFS %04X\n", REG_BG0VOFS);
|
||||
printf("REG_BG1HOFS %04X\n", REG_BG1HOFS);
|
||||
printf("REG_BG1VOFS %04X\n", REG_BG1VOFS);
|
||||
printf("REG_BG2HOFS %04X\n", REG_BG2HOFS);
|
||||
printf("REG_BG2VOFS %04X\n", REG_BG2VOFS);
|
||||
printf("REG_BG3HOFS %04X\n", REG_BG3HOFS);
|
||||
printf("REG_BG3VOFS %04X\n", REG_BG3VOFS);
|
||||
printf("REG_BG2PA %04X\n", REG_BG2PA);
|
||||
printf("REG_BG2PB %04X\n", REG_BG2PB);
|
||||
printf("REG_BG2PC %04X\n", REG_BG2PC);
|
||||
printf("REG_BG2PD %04X\n", REG_BG2PD);
|
||||
printf("REG_BG2X %04X\n", REG_BG2X);
|
||||
printf("REG_BG2X_L %04X\n", REG_BG2X_L);
|
||||
printf("REG_BG2X_H %04X\n", REG_BG2X_H);
|
||||
printf("REG_BG2Y %04X\n", REG_BG2Y);
|
||||
printf("REG_BG2Y_L %04X\n", REG_BG2Y_L);
|
||||
printf("REG_BG2Y_H %04X\n", REG_BG2Y_H);
|
||||
printf("REG_BG3PA %04X\n", REG_BG3PA);
|
||||
printf("REG_BG3PB %04X\n", REG_BG3PB);
|
||||
printf("REG_BG3PC %04X\n", REG_BG3PC);
|
||||
printf("REG_BG3PD %04X\n", REG_BG3PD);
|
||||
printf("REG_BG3X %04X\n", REG_BG3X);
|
||||
printf("REG_BG3X_L %04X\n", REG_BG3X_L);
|
||||
printf("REG_BG3X_H %04X\n", REG_BG3X_H);
|
||||
printf("REG_BG3Y %04X\n", REG_BG3Y);
|
||||
printf("REG_BG3Y_L %04X\n", REG_BG3Y_L);
|
||||
printf("REG_BG3Y_H %04X\n", REG_BG3Y_H);
|
||||
printf("REG_WIN0H %04X\n", REG_WIN0H);
|
||||
printf("REG_WIN1H %04X\n", REG_WIN1H);
|
||||
printf("REG_WIN0V %04X\n", REG_WIN0V);
|
||||
printf("REG_WIN1V %04X\n", REG_WIN1V);
|
||||
printf("REG_WININ %04X\n", REG_WININ);
|
||||
printf("REG_WINOUT %04X\n", REG_WINOUT);
|
||||
printf("REG_MOSAIC %04X\n", REG_MOSAIC);
|
||||
printf("REG_BLDCNT %04X\n", REG_BLDCNT);
|
||||
printf("REG_BLDALPHA %04X\n", REG_BLDALPHA);
|
||||
printf("REG_BLDY %04X\n", REG_BLDY);
|
||||
printf("____________________________________\n");
|
||||
|
||||
}
|
||||
|
|
@ -682,7 +682,7 @@ static void Task_PlayCryWhenReleasedFromBall(u8 taskId)
|
|||
u8 wantedCry = gTasks[taskId].tCryTaskWantedCry;
|
||||
s8 pan = gTasks[taskId].tCryTaskPan;
|
||||
u16 species = gTasks[taskId].tCryTaskSpecies;
|
||||
struct Pokemon *mon = (void *)(u32)((gTasks[taskId].tCryTaskMonPtr1 << 16) | (u16)(gTasks[taskId].tCryTaskMonPtr2));
|
||||
struct Pokemon *mon = (void *)(u32)(((u16)gTasks[taskId].tCryTaskMonPtr1 << 16) | (u16)(gTasks[taskId].tCryTaskMonPtr2));
|
||||
|
||||
switch (gTasks[taskId].tCryTaskState)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -691,6 +691,7 @@ u8 HandleSavingData(u8 saveType)
|
|||
WriteSaveSectorOrSlot(FULL_SAVE_SLOT, gRamSaveSectorLocations);
|
||||
break;
|
||||
}
|
||||
Platform_StoreSaveFile();
|
||||
gMain.vblankCounter1 = backupPtr;
|
||||
#if REVISION >= 0xA
|
||||
svc_FinishSave();
|
||||
|
|
@ -709,6 +710,7 @@ u8 TrySavingData(u8 saveType)
|
|||
HandleSavingData(saveType);
|
||||
if (!gDamagedSaveSectors)
|
||||
{
|
||||
Platform_StoreSaveFile();
|
||||
gSaveAttemptStatus = SAVE_STATUS_OK;
|
||||
return SAVE_STATUS_OK;
|
||||
}
|
||||
|
|
|
|||
487
src/sound_mixer.c
Normal file
487
src/sound_mixer.c
Normal file
|
|
@ -0,0 +1,487 @@
|
|||
#include "global.h"
|
||||
#include "music_player.h"
|
||||
#include "sound_mixer.h"
|
||||
#include "mp2k_common.h"
|
||||
|
||||
#include "cgb_audio.h"
|
||||
|
||||
|
||||
#define VCOUNT_VBLANK 160
|
||||
#define TOTAL_SCANLINES 228
|
||||
|
||||
|
||||
static inline void GenerateAudio(struct SoundMixerState *mixer, struct MixerSource *chan, struct WaveData2 *wav, float *outBuffer, u16 samplesPerFrame, float sampleRateReciprocal);
|
||||
void SampleMixer(struct SoundMixerState *mixer, u32 scanlineLimit, u16 samplesPerFrame, float *outBuffer, u8 dmaCounter, u16 maxBufSize);
|
||||
static inline bool32 TickEnvelope(struct MixerSource *chan, struct WaveData2 *wav);
|
||||
void GeneratePokemonSampleAudio(struct SoundMixerState *mixer, struct MixerSource *chan, s8 *current, float *outBuffer, u16 samplesPerFrame, float sampleRateReciprocal, s32 samplesLeftInWav, signed envR, signed envL, s32 loopLen);
|
||||
static s8 sub_82DF758(struct MixerSource *chan, u32 current);
|
||||
|
||||
void RunMixerFrame(void) {
|
||||
struct SoundMixerState *mixer = SOUND_INFO_PTR;
|
||||
|
||||
if (mixer->lockStatus != MIXER_UNLOCKED) {
|
||||
return;
|
||||
}
|
||||
mixer->lockStatus = MIXER_LOCKED;
|
||||
|
||||
u32 maxScanlines = mixer->maxScanlines;
|
||||
if (mixer->maxScanlines != 0) {
|
||||
u32 vcount = REG_VCOUNT;
|
||||
maxScanlines += vcount;
|
||||
if (vcount < VCOUNT_VBLANK) {
|
||||
maxScanlines += TOTAL_SCANLINES;
|
||||
}
|
||||
}
|
||||
|
||||
if (mixer->firstPlayerFunc != NULL) {
|
||||
mixer->firstPlayerFunc(mixer->firstPlayer);
|
||||
}
|
||||
|
||||
mixer->cgbMixerFunc();
|
||||
|
||||
s32 samplesPerFrame = mixer->samplesPerFrame;
|
||||
float *outBuffer = mixer->outBuffer;
|
||||
s32 dmaCounter = mixer->dmaCounter;
|
||||
|
||||
if (dmaCounter > 1) {
|
||||
outBuffer += samplesPerFrame * (mixer->framesPerDmaCycle - (dmaCounter - 1)) * 2;
|
||||
}
|
||||
|
||||
//MixerRamFunc mixerRamFunc = ((MixerRamFunc)MixerCodeBuffer);
|
||||
SampleMixer(mixer, maxScanlines, samplesPerFrame, outBuffer, dmaCounter, MIXED_AUDIO_BUFFER_SIZE);
|
||||
|
||||
cgb_audio_generate(samplesPerFrame);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//__attribute__((target("thumb")))
|
||||
void SampleMixer(struct SoundMixerState *mixer, u32 scanlineLimit, u16 samplesPerFrame, float *outBuffer, u8 dmaCounter, u16 maxBufSize) {
|
||||
u32 reverb = mixer->reverb;
|
||||
if (reverb) {
|
||||
// The vanilla reverb effect outputs a mono sound from four sources:
|
||||
// - L/R channels as they were mixer->framesPerDmaCycle frames ago
|
||||
// - L/R channels as they were (mixer->framesPerDmaCycle - 1) frames ago
|
||||
float *tmp1 = outBuffer;
|
||||
float *tmp2;
|
||||
if (dmaCounter == 2) {
|
||||
tmp2 = mixer->outBuffer;
|
||||
} else {
|
||||
tmp2 = outBuffer + samplesPerFrame * 2;
|
||||
}
|
||||
uf16 i = 0;
|
||||
do {
|
||||
float s = tmp1[0] + tmp1[1] + tmp2[0] + tmp2[1];
|
||||
s *= ((float)reverb / 512.0f);
|
||||
tmp1[0] = tmp1[1] = s;
|
||||
tmp1+=2;
|
||||
tmp2+=2;
|
||||
}
|
||||
while(++i < samplesPerFrame);
|
||||
} else {
|
||||
// memset(outBuffer, 0, samplesPerFrame);
|
||||
// memset(outBuffer + maxBufSize, 0, samplesPerFrame);
|
||||
for (int i = 0; i < samplesPerFrame; i++) {
|
||||
float *dst = &outBuffer[i*2];
|
||||
dst[1] = dst[0] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float sampleRateReciprocal = mixer->sampleRateReciprocal;
|
||||
sf8 numChans = mixer->numChans;
|
||||
struct MixerSource *chan = mixer->chans;
|
||||
|
||||
for (int i = 0; i < numChans; i++, chan++) {
|
||||
struct WaveData2 *wav = chan->wav;
|
||||
|
||||
if (scanlineLimit != 0) {
|
||||
uf16 vcount = REG_VCOUNT;
|
||||
if (vcount < VCOUNT_VBLANK) {
|
||||
vcount += TOTAL_SCANLINES;
|
||||
}
|
||||
if (vcount >= scanlineLimit) {
|
||||
goto returnEarly;
|
||||
}
|
||||
}
|
||||
|
||||
if (TickEnvelope(chan, wav))
|
||||
{
|
||||
|
||||
GenerateAudio(mixer, chan, wav, outBuffer, samplesPerFrame, sampleRateReciprocal);
|
||||
}
|
||||
}
|
||||
returnEarly:
|
||||
mixer->lockStatus = MIXER_UNLOCKED;
|
||||
}
|
||||
|
||||
// Returns TRUE if channel is still active after moving envelope forward a frame
|
||||
//__attribute__((target("thumb")))
|
||||
static inline bool32 TickEnvelope(struct MixerSource *chan, struct WaveData2 *wav) {
|
||||
// MP2K envelope shape
|
||||
// |
|
||||
// (linear)^ |
|
||||
// Attack / \Decay (exponential) |
|
||||
// / \_ |
|
||||
// / '., Sustain |
|
||||
// / '.______________ |
|
||||
// / '-. Echo (linear) |
|
||||
// / Release (exp) ''--..|\ |
|
||||
// / \ |
|
||||
|
||||
u8 status = chan->status;
|
||||
if ((status & 0xC7) == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
u8 env = 0;
|
||||
if ((status & 0x80) == 0) {
|
||||
env = chan->envelopeVol;
|
||||
|
||||
if (status & 4) {
|
||||
// Note-wise echo
|
||||
--chan->echoVol;
|
||||
if (chan->echoVol <= 0) {
|
||||
chan->status = 0;
|
||||
return FALSE;
|
||||
} else {
|
||||
return TRUE;
|
||||
}
|
||||
} else if (status & 0x40) {
|
||||
// Release
|
||||
chan->envelopeVol = env * chan->release / 256U;
|
||||
u8 echoVol = chan->echoVol;
|
||||
if (chan->envelopeVol > echoVol) {
|
||||
return TRUE;
|
||||
} else if (echoVol == 0) {
|
||||
chan->status = 0;
|
||||
return FALSE;
|
||||
} else {
|
||||
chan->status |= 4;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
switch (status & 3) {
|
||||
uf16 newEnv;
|
||||
case 2:
|
||||
// Decay
|
||||
chan->envelopeVol = env * chan->decay / 256U;
|
||||
|
||||
u8 sustain = chan->sustain;
|
||||
if (chan->envelopeVol <= sustain && sustain == 0) {
|
||||
// Duplicated echo check from Release section above
|
||||
if (chan->echoVol == 0) {
|
||||
chan->status = 0;
|
||||
return FALSE;
|
||||
} else {
|
||||
chan->status |= 4;
|
||||
return TRUE;
|
||||
}
|
||||
} else if (chan->envelopeVol <= sustain) {
|
||||
chan->envelopeVol = sustain;
|
||||
--chan->status;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
attack:
|
||||
newEnv = env + chan->attack;
|
||||
if (newEnv > 0xFF) {
|
||||
chan->envelopeVol = 0xFF;
|
||||
--chan->status;
|
||||
} else {
|
||||
chan->envelopeVol = newEnv;
|
||||
}
|
||||
break;
|
||||
case 1: // Sustain
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
} else if (status & 0x40) {
|
||||
// Init and stop cancel each other out
|
||||
chan->status = 0;
|
||||
return FALSE;
|
||||
} else {
|
||||
// Init channel
|
||||
chan->status = 3;
|
||||
#ifdef POKEMON_EXTENSIONS
|
||||
chan->current = wav->data + chan->ct;
|
||||
chan->ct = wav->size - chan->ct;
|
||||
#else
|
||||
chan->current = wav->data;
|
||||
chan->ct = wav->size;
|
||||
#endif
|
||||
chan->fw = 0;
|
||||
chan->envelopeVol = 0;
|
||||
if (wav->loopFlags & 0xC0) {
|
||||
chan->status |= 0x10;
|
||||
}
|
||||
goto attack;
|
||||
}
|
||||
}
|
||||
|
||||
//__attribute__((target("thumb")))
|
||||
static inline void GenerateAudio(struct SoundMixerState *mixer, struct MixerSource *chan, struct WaveData2 *wav, float *outBuffer, u16 samplesPerFrame, float sampleRateReciprocal) {/*, [[[]]]) {*/
|
||||
uf8 v = chan->envelopeVol * (mixer->masterVol + 1) / 16U;
|
||||
chan->envelopeVolR = chan->rightVol * v / 256U;
|
||||
chan->envelopeVolL = chan->leftVol * v / 256U;
|
||||
|
||||
s32 loopLen = 0;
|
||||
s8 *loopStart;
|
||||
if (chan->status & 0x10) {
|
||||
loopStart = wav->data + wav->loopStart;
|
||||
loopLen = wav->size - wav->loopStart;
|
||||
}
|
||||
s32 samplesLeftInWav = chan->ct;
|
||||
s8 *current = chan->current;
|
||||
signed envR = chan->envelopeVolR;
|
||||
signed envL = chan->envelopeVolL;
|
||||
#ifdef POKEMON_EXTENSIONS
|
||||
if (chan->type & 0x30) {
|
||||
GeneratePokemonSampleAudio(mixer, chan, current, outBuffer, samplesPerFrame, sampleRateReciprocal, samplesLeftInWav, envR, envL, loopLen);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (chan->type & 8) {
|
||||
for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) {
|
||||
sf8 c = *(current++);
|
||||
|
||||
outBuffer[1] += (c * envR) / 32768.0f;
|
||||
outBuffer[0] += (c * envL) / 32768.0f;
|
||||
if (--samplesLeftInWav == 0) {
|
||||
samplesLeftInWav = loopLen;
|
||||
if (loopLen != 0) {
|
||||
current = loopStart;
|
||||
} else {
|
||||
chan->status = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chan->ct = samplesLeftInWav;
|
||||
chan->current = current;
|
||||
} else {
|
||||
float finePos = chan->fw;
|
||||
float romSamplesPerOutputSample = chan->freq * sampleRateReciprocal;
|
||||
|
||||
sf16 b = current[0];
|
||||
sf16 m = current[1] - b;
|
||||
current += 1;
|
||||
|
||||
for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) {
|
||||
// Use linear interpolation to calculate a value between the current sample in the wav
|
||||
// and the next sample. Also cancel out the 9.23 stuff
|
||||
float sample = (finePos * m) + b;
|
||||
|
||||
outBuffer[1] += (sample * envR) / 32768.0f;
|
||||
outBuffer[0] += (sample * envL) / 32768.0f;
|
||||
|
||||
finePos += romSamplesPerOutputSample;
|
||||
u32 newCoarsePos = finePos;
|
||||
if (newCoarsePos != 0) {
|
||||
finePos -= (int)finePos;
|
||||
samplesLeftInWav -= newCoarsePos;
|
||||
if (samplesLeftInWav <= 0) {
|
||||
if (loopLen != 0) {
|
||||
current = loopStart;
|
||||
newCoarsePos = -samplesLeftInWav;
|
||||
samplesLeftInWav += loopLen;
|
||||
while (samplesLeftInWav <= 0) {
|
||||
newCoarsePos -= loopLen;
|
||||
samplesLeftInWav += loopLen;
|
||||
}
|
||||
b = current[newCoarsePos];
|
||||
m = current[newCoarsePos + 1] - b;
|
||||
current += newCoarsePos + 1;
|
||||
} else {
|
||||
chan->status = 0;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
b = current[newCoarsePos - 1];
|
||||
m = current[newCoarsePos] - b;
|
||||
current += newCoarsePos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chan->fw = finePos;
|
||||
chan->ct = samplesLeftInWav;
|
||||
chan->current = current - 1;
|
||||
}
|
||||
}
|
||||
|
||||
struct WaveData
|
||||
{
|
||||
u16 type;
|
||||
u16 status;
|
||||
u32 freq;
|
||||
u32 loopStart;
|
||||
u32 size; // number of samples
|
||||
s8 data[1]; // samples
|
||||
};
|
||||
|
||||
void GeneratePokemonSampleAudio(struct SoundMixerState *mixer, struct MixerSource *chan, s8 *current, float *outBuffer, u16 samplesPerFrame, float sampleRateReciprocal, s32 samplesLeftInWav, signed envR, signed envL, s32 loopLen) {
|
||||
struct WaveData *wav = chan->wav; // r6
|
||||
float finePos = chan->fw;
|
||||
if((chan->status & 0x20) == 0) {
|
||||
chan->status |= 0x20;
|
||||
if(chan->type & 0x10) {
|
||||
s8 *waveEnd = wav->data + wav->size;
|
||||
current = wav->data + (waveEnd - current);
|
||||
chan->current = current;
|
||||
}
|
||||
if(wav->type != 0) {
|
||||
current -= (uintptr_t)&wav->data;
|
||||
chan->current = current;
|
||||
}
|
||||
}
|
||||
float romSamplesPerOutputSample = chan->type & 8 ? 1.0f : chan->freq * sampleRateReciprocal;
|
||||
if(wav->type != 0) { // is compressed
|
||||
chan->blockCount = 0xFF000000;
|
||||
if(chan->type & 0x10) { // is reverse
|
||||
current -= 1;
|
||||
sf16 b = sub_82DF758(chan, (uintptr_t)current);
|
||||
sf16 m = sub_82DF758(chan, (uintptr_t)current - 1) - b;
|
||||
|
||||
for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) {
|
||||
float sample = (finePos * m) + b;
|
||||
|
||||
outBuffer[1] += (sample * envR) / 32768.0f;
|
||||
outBuffer[0] += (sample * envL) / 32768.0f;
|
||||
|
||||
finePos += romSamplesPerOutputSample;
|
||||
int newCoarsePos = finePos;
|
||||
if (newCoarsePos != 0) {
|
||||
finePos -= (int)finePos;
|
||||
samplesLeftInWav -= newCoarsePos;
|
||||
if (samplesLeftInWav <= 0) {
|
||||
chan->status = 0;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
current -= newCoarsePos;
|
||||
b = sub_82DF758(chan, (uintptr_t)current);
|
||||
m = sub_82DF758(chan, (uintptr_t)current - 1) - b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chan->fw = finePos;
|
||||
chan->ct = samplesLeftInWav;
|
||||
chan->current = current + 1;
|
||||
}
|
||||
else {
|
||||
sf16 b = sub_82DF758(chan, (uintptr_t)current);
|
||||
sf16 m = sub_82DF758(chan, (uintptr_t)current + 1) - b;
|
||||
current += 1;
|
||||
|
||||
for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) {
|
||||
float sample = (finePos * m) + b;
|
||||
|
||||
outBuffer[1] += (sample * envR) / 32768.0f;
|
||||
outBuffer[0] += (sample * envL) / 32768.0f;
|
||||
|
||||
finePos += romSamplesPerOutputSample;
|
||||
u32 newCoarsePos = finePos; // lr
|
||||
if (newCoarsePos != 0) {
|
||||
finePos -= (int)finePos;
|
||||
samplesLeftInWav -= newCoarsePos;
|
||||
if (samplesLeftInWav <= 0) {
|
||||
if (loopLen != 0) {
|
||||
current = chan->wav->loopStart;
|
||||
newCoarsePos = -samplesLeftInWav;
|
||||
samplesLeftInWav += loopLen;
|
||||
while (samplesLeftInWav <= 0) {
|
||||
newCoarsePos -= loopLen;
|
||||
samplesLeftInWav += loopLen;
|
||||
}
|
||||
current += newCoarsePos;
|
||||
b = sub_82DF758(chan, (uintptr_t)current);
|
||||
m = sub_82DF758(chan, (uintptr_t)current + 1) - b;
|
||||
current += 1;
|
||||
} else {
|
||||
chan->status = 0;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
current += newCoarsePos - 1;
|
||||
b = sub_82DF758(chan, (uintptr_t)current);
|
||||
m = sub_82DF758(chan, (uintptr_t)current + 1) - b;
|
||||
current += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chan->fw = finePos;
|
||||
chan->ct = samplesLeftInWav;
|
||||
chan->current = current - 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(chan->type & 0x10) { // is reverse
|
||||
current -= 1;
|
||||
sf16 b = current[0];
|
||||
sf16 m = current[-1] - b;
|
||||
|
||||
for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) {
|
||||
float sample = (finePos * m) + b;
|
||||
|
||||
outBuffer[1] += (sample * envR) / 32768.0f;
|
||||
outBuffer[0] += (sample * envL) / 32768.0f;
|
||||
|
||||
finePos += romSamplesPerOutputSample;
|
||||
int newCoarsePos = finePos;
|
||||
if (newCoarsePos != 0) {
|
||||
finePos -= (int)finePos;
|
||||
samplesLeftInWav -= newCoarsePos;
|
||||
if (samplesLeftInWav <= 0) {
|
||||
chan->status = 0;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
current -= newCoarsePos;
|
||||
b = current[0];
|
||||
m = current[-1] - b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chan->fw = finePos;
|
||||
chan->ct = samplesLeftInWav;
|
||||
chan->current = current + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s8 gBDPCMBlockBuffer[64];
|
||||
extern const s8 gDeltaEncodingTable[];
|
||||
|
||||
static s8 sub_82DF758(struct MixerSource *chan, u32 current) {
|
||||
u32 blockOffset = current >> 6; // current / 64
|
||||
u8 * blockPtr;
|
||||
int i;
|
||||
//In route 102 lotad wild battle when it growls crashes the game because it decompresses out of bounds data
|
||||
//I gave it its own printf error so it wouldn't get forgotten as this needs a more proper fix
|
||||
if (chan->wav->size < blockOffset * 0x21) {
|
||||
printf("Out of bounds decompress in %s wav->size = %u blockPtr = %u\n", __func__, chan->wav->size, blockOffset * 0x21);
|
||||
return gBDPCMBlockBuffer[current & 63];
|
||||
}
|
||||
|
||||
if(chan->blockCount != blockOffset) { // decode block if not decoded
|
||||
s32 s;
|
||||
chan->blockCount = blockOffset;
|
||||
blockPtr = chan->wav->data + chan->blockCount * 0x21;
|
||||
gBDPCMBlockBuffer[0] = s = (s8)*blockPtr++;
|
||||
gBDPCMBlockBuffer[1] = s += gDeltaEncodingTable[*blockPtr++ & 0xF];
|
||||
for(i = 2; i < 64; i+=2) {
|
||||
u32 temp = *blockPtr++;
|
||||
gBDPCMBlockBuffer[i] = s += gDeltaEncodingTable[temp >> 4];
|
||||
gBDPCMBlockBuffer[i+1] = s += gDeltaEncodingTable[temp & 0xF];
|
||||
}
|
||||
}
|
||||
return gBDPCMBlockBuffer[current & 63]; // index same as current % 64
|
||||
}
|
||||
|
|
@ -3536,71 +3536,7 @@ void InitUnionRoom(void)
|
|||
|
||||
static void Task_InitUnionRoom(u8 taskId)
|
||||
{
|
||||
s32 i;
|
||||
u8 text[32];
|
||||
struct WirelessLink_URoom * data = sWirelessLinkMain.uRoom;
|
||||
|
||||
switch (data->state)
|
||||
{
|
||||
case 0:
|
||||
data->state = 1;
|
||||
break;
|
||||
case 1:
|
||||
SetHostRfuGameData(ACTIVITY_SEARCH, 0, FALSE);
|
||||
SetWirelessCommType1();
|
||||
OpenLink();
|
||||
InitializeRfuLinkManager_EnterUnionRoom();
|
||||
RfuSetIgnoreError(TRUE);
|
||||
data->state = 2;
|
||||
break;
|
||||
case 2:
|
||||
data->incomingChildList = AllocZeroed(RFU_CHILD_MAX * sizeof(struct RfuIncomingPlayer));
|
||||
ClearIncomingPlayerList(data->incomingChildList->players, RFU_CHILD_MAX);
|
||||
data->incomingParentList = AllocZeroed(RFU_CHILD_MAX * sizeof(struct RfuIncomingPlayer));
|
||||
ClearIncomingPlayerList(data->incomingParentList->players, RFU_CHILD_MAX);
|
||||
data->playerList = AllocZeroed(MAX_UNION_ROOM_LEADERS * sizeof(struct RfuPlayer));
|
||||
ClearRfuPlayerList(data->playerList->players, MAX_UNION_ROOM_LEADERS);
|
||||
data->spawnPlayer = AllocZeroed(sizeof(struct RfuPlayer));
|
||||
ClearRfuPlayerList(&data->spawnPlayer->players[0], 1);
|
||||
data->searchTaskId = CreateTask_SearchForChildOrParent(data->incomingParentList, data->incomingChildList, LINK_GROUP_UNION_ROOM_INIT);
|
||||
data->state = 3;
|
||||
break;
|
||||
case 3:
|
||||
switch (HandlePlayerListUpdate())
|
||||
{
|
||||
case PLIST_NEW_PLAYER:
|
||||
case PLIST_RECENT_UPDATE:
|
||||
if (sUnionRoomPlayerName[0] == EOS)
|
||||
{
|
||||
for (i = 0; i < PLAYER_NAME_LENGTH + 1; i++)
|
||||
{
|
||||
if (data->playerList->players[i].groupScheduledAnim == UNION_ROOM_SPAWN_IN)
|
||||
{
|
||||
CopyAndTranslatePlayerName2(text, data->playerList->players[i]);
|
||||
if (PlayerHasMetTrainerBefore(ReadAsU16(data->playerList->players[i].rfu.data.compatibility.playerTrainerId), text))
|
||||
{
|
||||
StringCopy(sUnionRoomPlayerName, text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PLIST_UNUSED:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
Free(data->spawnPlayer);
|
||||
Free(data->playerList);
|
||||
Free(data->incomingParentList);
|
||||
Free(data->incomingChildList);
|
||||
DestroyTask(data->searchTaskId);
|
||||
Free(sWirelessLinkMain.uRoom);
|
||||
LinkRfu_Shutdown();
|
||||
DestroyTask(taskId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool16 BufferUnionRoomPlayerName(void)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user