From 4cbeb40e28b0cbd7a4e2f49929600c0d6135cf3c Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Thu, 19 Oct 2023 10:10:36 -0700 Subject: [PATCH] synthmacros, snd3d progress Former-commit-id: a31a2f914b4c714e790dd12b310341ccc3f6c640 --- include/musyx/musyx_priv.h | 194 ++++++++++++++++---------------- include/musyx/synth.h | 6 + src/musyx/runtime/snd3d.c | 56 ++++++--- src/musyx/runtime/synthmacros.c | 139 +++++++++++++++++++++-- src/musyx/txwin/txwin.c | 7 +- 5 files changed, 278 insertions(+), 124 deletions(-) diff --git a/include/musyx/musyx_priv.h b/include/musyx/musyx_priv.h index 94079053..0f683332 100644 --- a/include/musyx/musyx_priv.h +++ b/include/musyx/musyx_priv.h @@ -483,100 +483,105 @@ extern VS vs; #pragma push #pragma pack(4) typedef struct SYNTH_VOICE { - SYNTH_QUEUE lowPrecisionJob; - SYNTH_QUEUE zeroOffsetJob; // offset 0xC, size 0xC - SYNTH_QUEUE eventJob; // offset 0x18, size 0xC - u64 lastLowCallTime; - u64 lastZeroCallTime; - MSTEP* addr; - MSTEP* curAddr; - struct SYNTH_VOICE* nextMacActive; - struct SYNTH_VOICE* prevMacActive; - struct SYNTH_VOICE* nextTimeQueueMacro; - struct SYNTH_VOICE* prevTimeQueueMacro; - MAC_STATE macState; - MSTEP* trapEventAddr[3]; - MSTEP* trapEventCurAddr[3]; - u8 trapEventAny; - CALLSTACK callStack[4]; - u8 callStackEntryNum; - u8 callStackIndex; - u64 macStartTime; - u64 wait; - u64 waitTime; - u8 timeUsedByInput; - u16 loop; - s32 local_vars[16]; - u32 child; - u32 parent; - u32 id; - VID_LIST* vidList; - VID_LIST* vidMasterList; - u16 allocId; - u16 macroId; - u8 keyGroup; - u32 lastVID; - u8 prio; - u16 ageSpeed; - u32 age; - u64 cFlags; - u8 block; - u8 fxFlag; - u8 vGroup; - u8 studio; - u8 track; - u8 midi; - u8 midiSet; - u8 section; - u32 sInfo; - u32 playFrq; - u16 curNote; - s8 curDetune; - u8 orgNote; - u8 lastNote; - u8 portType; - u16 portLastCtrlState; - u32 portDuration; - u32 portCurPitch; - u32 portTime; // offset 0x13C, size 0x4 - u8 vibKeyRange; // offset 0x140, size 0x1 - u8 vibCentRange; // offset 0x141, size 0x1 - u32 vibPeriod; // offset 0x144, size 0x4 - u32 vibCurTime; // offset 0x148, size 0x4 - s32 vibCurOffset; // offset 0x14C, size 0x4 - s16 vibModAddScale; // offset 0x150, size 0x2 - u32 volume; // offset 0x154, size 0x4 - u32 orgVolume; // offset 0x158, size 0x4 - f32 lastVolFaderScale; // offset 0x15C, size 0x4 - u32 lastPan; // offset 0x160, size 0x4 - u32 lastSPan; // offset 0x164, size 0x4 - f32 treCurScale; // offset 0x168, size 0x4 - u16 treScale; // offset 0x16C, size 0x2 - u16 treModAddScale; // offset 0x16E, size 0x2 - u32 panning[2]; // offset 0x170, size 0x8 - s32 panDelta[2]; // offset 0x178, size 0x8 - u32 panTarget[2]; // offset 0x180, size 0x8 - u32 panTime[2]; // offset 0x188, size 0x8 - u8 revVolScale; // offset 0x190, size 0x1 - u8 revVolOffset; // offset 0x191, size 0x1 - u8 volTable; // offset 0x192, size 0x1 - u8 itdMode; // offset 0x193, size 0x1 - s32 envDelta; // offset 0x194, size 0x4 - u32 envTarget; // offset 0x198, size 0x4 - u32 envCurrent; // offset 0x19C, size 0x4 - u32 sweepOff[2]; // offset 0x1A0, size 0x8 - s32 sweepAdd[2]; // offset 0x1A8, size 0x8 - s32 sweepCnt[2]; // offset 0x1B0, size 0x8 - u8 sweepNum[2]; // offset 0x1B8, size 0x2 - SYNTH_LFO lfo[2]; // offset 0x1BC, size 0x18 - u8 lfoUsedByInput[2]; // offset 0x1D4, size 0x2 - u8 pbLowerKeyRange; // offset 0x1D6, size 0x1 - u8 pbUpperKeyRange; // offset 0x1D7, size 0x1 - u16 pbLast; // offset 0x1D8, size 0x2 - ADSR_VARS pitchADSR; // offset 0x1DC, size 0x28 - s16 pitchADSRRange; // offset 0x204, size 0x2 - u16 curPitch; // offset 0x206, size 0x2 + // total size: 0x404 + SYNTH_QUEUE lowPrecisionJob; // offset 0x0, size 0xC + SYNTH_QUEUE zeroOffsetJob; // offset 0xC, size 0xC + SYNTH_QUEUE eventJob; // offset 0x18, size 0xC + u64 lastLowCallTime; // offset 0x24, size 0x8 + u64 lastZeroCallTime; // offset 0x2C, size 0x8 + MSTEP* addr; // offset 0x34, size 0x4 + MSTEP* curAddr; // offset 0x38, size 0x4 + struct SYNTH_VOICE* nextMacActive; // offset 0x3C, size 0x4 + struct SYNTH_VOICE* prevMacActive; // offset 0x40, size 0x4 + struct SYNTH_VOICE* nextTimeQueueMacro; // offset 0x44, size 0x4 + struct SYNTH_VOICE* prevTimeQueueMacro; // offset 0x48, size 0x4 + MAC_STATE macState; // offset 0x4C, size 0x4 + MSTEP* trapEventAddr[3]; // offset 0x50, size 0xC + MSTEP* trapEventCurAddr[3]; // offset 0x5C, size 0xC + u8 trapEventAny; // offset 0x68, size 0x1 + CALLSTACK callStack[4]; // offset 0x6C, size 0x20 + u8 callStackEntryNum; // offset 0x8C, size 0x1 + u8 callStackIndex; // offset 0x8D, size 0x1 + u64 macStartTime; // offset 0x90, size 0x8 + u64 wait; // offset 0x98, size 0x8 + u64 waitTime; // offset 0xA0, size 0x8 + u8 timeUsedByInput; // offset 0xA8, size 0x1 + u16 loop; // offset 0xAA, size 0x2 + s32 local_vars[16]; // offset 0xAC, size 0x40 + u32 child; // offset 0xEC, size 0x4 + u32 parent; // offset 0xF0, size 0x4 + u32 id; // offset 0xF4, size 0x4 + VID_LIST* vidList; // offset 0xF8, size 0x4 + VID_LIST* vidMasterList; // offset 0xFC, size 0x4 + u16 allocId; // offset 0x100, size 0x2 + u16 macroId; // offset 0x102, size 0x2 + u8 keyGroup; // offset 0x104, size 0x1 + u32 lastVID; // offset 0x108, size 0x4 + u8 prio; // offset 0x10C, size 0x1 + u16 ageSpeed; // offset 0x10E, size 0x2 + u32 age; // offset 0x110, size 0x4 + u64 cFlags; // offset 0x114, size 0x8 + u8 block; // offset 0x11C, size 0x1 + u8 fxFlag; // offset 0x11D, size 0x1 + u8 vGroup; // offset 0x11E, size 0x1 + u8 studio; // offset 0x11F, size 0x1 + u8 track; // offset 0x120, size 0x1 + u8 midi; // offset 0x121, size 0x1 + u8 midiSet; // offset 0x122, size 0x1 + u8 section; // offset 0x123, size 0x1 +#if MUSY_VERSION <= MUSY_VERSION_CHECK(1, 5, 0) + void* sAddr; +#endif + u32 sInfo; // offset 0x124, size 0x4 + u32 playFrq; // offset 0x128, size 0x4 + u16 curNote; // offset 0x12C, size 0x2 + s8 curDetune; // offset 0x12E, size 0x1 + u8 orgNote; // offset 0x12F, size 0x1 + u8 lastNote; // offset 0x130, size 0x1 + u8 portType; // offset 0x131, size 0x1 + u16 portLastCtrlState; // offset 0x132, size 0x2 + u32 portDuration; // offset 0x134, size 0x4 + u32 portCurPitch; // offset 0x138, size 0x4 + u32 portTime; // offset 0x13C, size 0x4 + u8 vibKeyRange; // offset 0x140, size 0x1 + u8 vibCentRange; // offset 0x141, size 0x1 + u32 vibPeriod; // offset 0x144, size 0x4 + u32 vibCurTime; // offset 0x148, size 0x4 + s32 vibCurOffset; // offset 0x14C, size 0x4 + s16 vibModAddScale; // offset 0x150, size 0x2 + u32 volume; // offset 0x154, size 0x4 + u32 orgVolume; // offset 0x158, size 0x4 + float lastVolFaderScale; // offset 0x15C, size 0x4 + u32 lastPan; // offset 0x160, size 0x4 + u32 lastSPan; // offset 0x164, size 0x4 + float treCurScale; // offset 0x168, size 0x4 + u16 treScale; // offset 0x16C, size 0x2 + u16 treModAddScale; // offset 0x16E, size 0x2 + u32 panning[2]; // offset 0x170, size 0x8 + s32 panDelta[2]; // offset 0x178, size 0x8 + u32 panTarget[2]; // offset 0x180, size 0x8 + u32 panTime[2]; // offset 0x188, size 0x8 + u8 revVolScale; // offset 0x190, size 0x1 + u8 revVolOffset; // offset 0x191, size 0x1 + u8 volTable; // offset 0x192, size 0x1 + u8 itdMode; // offset 0x193, size 0x1 + s32 envDelta; // offset 0x194, size 0x4 + u32 envTarget; // offset 0x198, size 0x4 + u32 envCurrent; // offset 0x19C, size 0x4 + u32 sweepOff[2]; // offset 0x1A0, size 0x8 + s32 sweepAdd[2]; // offset 0x1A8, size 0x8 + s32 sweepCnt[2]; // offset 0x1B0, size 0x8 + u8 sweepNum[2]; // offset 0x1B8, size 0x2 + SYNTH_LFO lfo[2]; // offset 0x1BC, size 0x18 + u8 lfoUsedByInput[2]; // offset 0x1D4, size 0x2 + u8 pbLowerKeyRange; // offset 0x1D6, size 0x1 + u8 pbUpperKeyRange; // offset 0x1D7, size 0x1 + u16 pbLast; // offset 0x1D8, size 0x2 + ADSR_VARS pitchADSR; // offset 0x1DC, size 0x28 + s16 pitchADSRRange; // offset 0x204, size 0x2 + u16 curPitch; // offset 0x206, size 0x2 struct setup { + // total size: 0x9 u8 vol; // offset 0x0, size 0x1 u8 pan; // offset 0x1, size 0x1 u8 midi; // offset 0x2, size 0x1 @@ -1054,7 +1059,7 @@ u16 inpGetExCtrl(SYNTH_VOICE* svoice, u8 ctrl); u16 inpGetMidiCtrl(u8 ctrl, u8 channel, u8 set); void inpSetMidiLastNote(u8 midi, u8 midiSet, u8 key); u16 inpGetModulation(SYNTH_VOICE* svoice); -void inpResetMidiCtrl(u8 ch, u8 set, u32 coldReset); +void inpResetMidiCtrl(u8 ch, u8 set, u32 coldReset) ; void inpResetChannelDefaults(u8 midi, u8 midiSet); u16 inpGetPitchBend(SYNTH_VOICE* svoice); u16 inpGetDoppler(SYNTH_VOICE* svoice); @@ -1070,6 +1075,7 @@ u16 inpGetPostAuxB(SYNTH_VOICE* svoice); u16 inpGetPedal(SYNTH_VOICE* svoice); u16 inpGetAuxA(u8 studio, u8 index, u8 midi, u8 midiSet); u16 inpGetAuxB(u8 studio, u8 index, u8 midi, u8 midiSet); +void inpInit(SYNTH_VOICE* svoice); /* TODO: Figure out what `unk` is */ void hwSetSRCType(u32 v, u8 salSRCType); diff --git a/include/musyx/synth.h b/include/musyx/synth.h index 42311990..88d99e37 100644 --- a/include/musyx/synth.h +++ b/include/musyx/synth.h @@ -19,6 +19,7 @@ extern synthITDInfo synthITDDefault[8]; extern u32 synthFlags; u32 vidGetInternalId(SND_VOICEID id); +void synthKeyStateUpdate(SYNTH_VOICE* svoice); bool synthFXSetCtrl(SND_VOICEID vid, u8 ctrl, u8 value); bool synthFXSetCtrl14(SND_VOICEID vid, u8 ctrl, u16 value); bool synthSendKeyOff(SND_VOICEID vid); @@ -35,6 +36,11 @@ bool synthIsFadeOutActive(u8 vGroup); /* TODO: Move this where it belongs */ void hwSetAUXProcessingCallbacks(u8 studio, SND_AUX_CALLBACK auxA, void* userA, SND_AUX_CALLBACK auxB, void* userB); + +MSTEP* dataGetMacro(u16 mid); + +u32 voiceAllocate(u8 priority, u8 maxVoices, u16 allocId, u8 fxFlag); +void voiceFree(SYNTH_VOICE* svoice); #ifdef __cplusplus } #endif diff --git a/src/musyx/runtime/snd3d.c b/src/musyx/runtime/snd3d.c index f1912f6e..81e88bf6 100644 --- a/src/musyx/runtime/snd3d.c +++ b/src/musyx/runtime/snd3d.c @@ -1,3 +1,4 @@ +#include "musyx/musyx.h" #include "musyx/musyx_priv.h" #include "musyx/synth.h" @@ -11,7 +12,6 @@ static u8 snd_base_studio; static u8 snd_max_studios; static u8 s3dUseMaxVoices; - static void UpdateRoomDistances() { SND_ROOM* r; // r30 SND_LISTENER* li; // r31 @@ -260,17 +260,17 @@ bool sndRemoveDoor(SND_DOOR* door) { static void CalcEmitter(struct SND_EMITTER* em, float* vol, float* doppler, float* xPan, float* yPan, float* zPan) { - struct SND_LISTENER* li; // r31 - struct SND_FVECTOR d; // r1+0x44 - struct SND_FVECTOR v; // r1+0x38 - struct SND_FVECTOR p; // r1+0x2C - float relspeed; // r60 - float distance; // r61 - float new_distance; // r59 - float ft; // r63 - float vd; // r62 - struct SND_FVECTOR pan; // r1+0x20 - unsigned long n; // r29 + SND_LISTENER* li; // r31 + SND_FVECTOR d; // r1+0x44 + SND_FVECTOR v; // r1+0x38 + SND_FVECTOR p; // r1+0x2C + float relspeed; // r60 + float distance; // r61 + float new_distance; // r59 + float ft; // r63 + float vd; // r62 + SND_FVECTOR pan; // r1+0x20 + unsigned long n; // r29 } static u8 clip127(u8 v) { @@ -289,9 +289,31 @@ static u16 clip3FFF(u32 v) { static void SetFXParameters(SND_EMITTER* em, float vol, float xPan, float yPan, float zPan, float doppler) { - unsigned long vid; // r30 - unsigned char i; // r28 - struct SND_PARAMETER* pPtr; // r31 + SND_VOICEID vid; // r30 + u8 i; // r28 + SND_PARAMETER* pPtr; // r31 + + vid = em->vid; + if ((em->flags & 0x100000) != 0) { + synthFXSetCtrl(vid, 7, clip127((em->fade * vol) * 127.f)); + } else { + synthFXSetCtrl(vid, 7, clip127(vol * 127.f)); + } + + synthFXSetCtrl(vid, 10, clip127((1.f + xPan) * 64.f)); + synthFXSetCtrl(vid, 131, clip127((1.f - zPan) * 64.f)); + synthFXSetCtrl14(vid, 132, clip3FFF(doppler * 8192.f)); + + if (em->paraInfo != NULL) { + pPtr = em->paraInfo->paraArray; + for (i = 0; i < em->paraInfo->numPara; ++pPtr, ++i) { + if (pPtr->ctrl < 0x40 || pPtr->ctrl == 0x80 || pPtr->ctrl == 0x84) { + synthFXSetCtrl14(vid, pPtr->ctrl, (pPtr->paraData).value14); + } else { + synthFXSetCtrl(vid, pPtr->ctrl, (pPtr->paraData).value7); + } + } + } } static void EmitterShutdown(SND_EMITTER* em) { @@ -859,13 +881,13 @@ void sndGet3DParameters(SND_3DINFO* info, SND_FVECTOR* pos, SND_FVECTOR* dir, f3 em.minVol = minVol / 127.f; em.volPush = comp; em.room = room; - + CalcEmitter(&em, &cvol, &pitch, &xPan, &yPan, &zPan); info->vol = clip127(cvol * 127.f); info->pan = clip127((xPan + 1.f) * 64.f); info->span = clip127((1.f - zPan) * 64.f); info->doppler = clip3FFF(pitch * 8192.f); - + hwEnableIrq(); } diff --git a/src/musyx/runtime/synthmacros.c b/src/musyx/runtime/synthmacros.c index 1f5c237b..cd73fa2c 100644 --- a/src/musyx/runtime/synthmacros.c +++ b/src/musyx/runtime/synthmacros.c @@ -1,4 +1,6 @@ #include "musyx/musyx_priv.h" +#include "musyx/seq.h" +#include "musyx/synth.h" static u8 DebugMacroSteps = 0; @@ -292,9 +294,37 @@ static void mcmdUntrapEvent(SYNTH_VOICE* svoice, MSTEP* cstep) { svoice->trapEventAny = 0; } -#pragma dont_inline on -static void mcmdLoop(SYNTH_VOICE* svoice, MSTEP* cstep) {} -#pragma dont_inline reset +static void mcmdLoop(SYNTH_VOICE* svoice, MSTEP* cstep) { + + if (svoice->loop == 0) { + if ((u8)(cstep->para[0] >> 16) & 1) { + svoice->loop = sndRand() % (s32)(cstep->para[1] >> 16); + } else { + svoice->loop = (cstep->para[1] >> 16); + } + + if (svoice->loop == 0xFFFF) { + goto skip; + } + ++svoice->loop; + } else if (svoice->loop == 0xFFFF) { + goto skip; + } + + if (--svoice->loop == 0) { + return; + } +skip: + if (((u8)(cstep->para[0] >> 8) & 1) != 0 && (svoice->cFlags & 0x10000000008) == 0x00000000008) { + svoice->loop = 0; + + } else if (((u8)(cstep->para[0] >> 0x18) & 1) && (svoice->cFlags & 0x20) == 0 && + !hwIsActive(svoice->id & 0xFF)) { + svoice->loop = 0; + } else { + svoice->curAddr = svoice->addr + ((u16)cstep->para[1]); + } +} static void mcmdPlayMacro(SYNTH_VOICE* svoice, MSTEP* cstep) { s32 key; // r29 @@ -585,6 +615,26 @@ static void DoSetPitch(SYNTH_VOICE* svoice) { static u16 kf[13] = { 4096, 4339, 4597, 4871, 5160, 5467, 5792, 6137, 6502, 6888, 7298, 7732, 8192, }; + + frq = svoice->playFrq & 0xFFFFFF; + ofrq = svoice->sInfo & 0xFFFFFF; + + if (ofrq == frq) { + svoice->curNote = svoice->sInfo >> 24; + } else if (ofrq < frq) { + f = (frq << 12) / ofrq; + for (no = 0; no < 11 && (1 << ((no + 1) & 0x3f)) < (f >> 12); ++no) { + } + + f /= (1 << (no & 0x3f)); + + for (i = 11; f <= kf[i]; i--) { + + } + + svoice->curNote = (svoice->sInfo >> 24) + no * 12 + i; + svoice->curDetune = (no - kf[i]) * 100 / (kf[i + 1] - kf[i]); + } } #pragma dont_inline reset @@ -1494,9 +1544,9 @@ void macSampleEndNotify(SYNTH_VOICE* sv) { if (sv->macState != MAC_STATE_YIELDED) { return; } -#line 3156 /* clang-format off */ + /* clang-format off */ MUSY_ASSERT(sv->addr!=NULL); - /* clang-format on */ + /* clang-format on */ if (!ExecuteTrap(sv, 1) && (sv->cFlags & 0x40000)) { macMakeActive(sv); @@ -1588,9 +1638,9 @@ void macMakeActive(SYNTH_VOICE* sv) { if (sv->macState == MAC_STATE_RUNNABLE) { return; } -#line 3297 /* clang-format off */ + /* clang-format off */ MUSY_ASSERT(sv->addr!=NULL); - /* clang-format on */ + /* clang-format on */ UnYieldMacro(sv, 0); if (sv->nextMacActive = macActiveMacroRoot) { macActiveMacroRoot->prevMacActive = sv; @@ -1605,9 +1655,9 @@ void macMakeInactive(SYNTH_VOICE* svoice, MAC_STATE newState) { return; } -#line 3333 /* clang-format off */ + /* clang-format off */ MUSY_ASSERT(svoice->addr!=NULL); - /* clang-format on */ + /* clang-format on */ if (svoice->macState == MAC_STATE_RUNNABLE) { if (svoice->prevMacActive == NULL) { macActiveMacroRoot = svoice->nextMacActive; @@ -1634,6 +1684,77 @@ u32 macStart(u16 macid, u8 priority, u8 maxVoices, u16 allocId, u8 key, u8 vol, MSTEP* addr; // r28 SYNTH_VOICE* svoice; // r31 u16 seqPrio; // r24 + + if ((addr = dataGetMacro(macid))) { + if (!(key & 0x80) && (seqPrio = seqGetMIDIPriority(midiSet, midi)) != 0xFFFF) { + priority = seqPrio; + } + + if ((voice = voiceAllocate(priority, maxVoices, allocId, (key & 0x80) ? 1 : 0)) != -1) { + svoice = &synthVoice[voice]; + vidRemoveVoiceReferences(svoice); + macMakeInactive(svoice, MAC_STATE_STOPPED); + svoice->cFlags = (svoice->cFlags & 0x10) | 2; + + if (hwIsActive(voice)) { + svoice->cFlags |= 1; + } + + svoice->wait = 0; + + if ((key & 0x80) != 0) { + svoice->fxFlag = 01; + key &= 0x7f; + inpResetMidiCtrl(voice, 0xff, 1); + inpResetChannelDefaults(voice, 0xff); + svoice->setup.midi = voice; + svoice->setup.midiSet = 0xff; + svoice->setup.section = 0; + } else { + svoice->fxFlag = 0; + svoice->setup.midi = midi; + svoice->setup.midiSet = midiSet; + svoice->setup.section = section; + } + + svoice->macroId = macid; + svoice->allocId = allocId; + svoice->age = 0x75300000; + svoice->ageSpeed = 0x400; + svoice->addr = addr; + svoice->curAddr = addr + step; + svoice->orgNote = key; + svoice->curNote = key; + svoice->curDetune = 0; + svoice->setup.vol = vol; + svoice->setup.pan = panning; + svoice->setup.track = trackid; + svoice->callStackEntryNum = 0; + svoice->callStackIndex = 0; + svoice->child = -1; + svoice->parent = -1; + svoice->lastVID = -1; + svoice->setup.vGroup = vGroup; + svoice->setup.studio = studio; + svoice->setup.itdMode = itd != 0 ? 0 : 1; + svoice->mesgNum = svoice->mesgRead = svoice->mesgWrite = 0; + svoice->id = voice | ((macid << 16) | (key << 8)); + voiceSetPriority(svoice, priority); + + if ((vid = vidMakeNew(svoice, new_vid)) != -1) { + macMakeActive(svoice); + return vid; + } + + if (hwIsActive(voice)) { + hwBreak(voice); + } + + voiceFree(svoice); + } + } + + return -1; } void macInit() { diff --git a/src/musyx/txwin/txwin.c b/src/musyx/txwin/txwin.c index 6f47d640..8b50c5c1 100644 --- a/src/musyx/txwin/txwin.c +++ b/src/musyx/txwin/txwin.c @@ -220,12 +220,11 @@ void __win_log_refresh(struct STRUCT_WIN* handle) { unsigned short index; // r1+0xC #line 506 MUSY_ASSERT_MSG(handle != NULL, "OHMYGAWD\n"); - n = (u32)handle->curr_output_line; + n = handle->curr_output_line; x = handle->x1; y = handle->y2; - i = 0; for (i = 0; i < handle->char_height; ++i) { - n = index + (u16)(n + (handle->total_lines - 1)) % (u32)handle->total_lines; - DEMOPrintf(x, (y + i) % 2, 0, "%s", handle->buffer[n]); + index = n + (u16)(n + (handle->total_lines - 1)) % (u32)handle->total_lines; + DEMOPrintf(x, (y + i) % 2, 0, "%s", handle->buffer[index]); } }