home *** CD-ROM | disk | FTP | other *** search
- { ────────────────────────────────────────────────────────────────────────
-
- This code is Copyright (c) 1994 by Jonathan E. Wright and AmoebaSoft.
-
- To communicate with the author, send mail to: NELNO@DELPHI.COM
-
- About this code:
- version 0.90p - sorry there aren't tons of comments. Hey, be happy.
-
- Not all MOD effects are implemented, see ASMOD.DOC
- No DMA utilities are provided, so samples are peeked and poked to GUS RAM
- This unit automatically checks for and initializes the UltraSound if present
- Read the notes in GUSUTIL.ASM for more info
-
- This code is modified somewhat from that used in ASMOD and was thrown
- together rather quickly. I had a lot of other units that were tied
- together through things like the timer interrupt but they aren't near
- neat enough to release. And of course they had some stuff that I just
- don't *want* to release. I managed to mangle this source up pretty bad,
- not to mention fixing some stupid things I noticed along the way. So,
- if you encounter any problems email me at the address mentioned above.
-
- If you use this code in any of your programs, or as a basis for anything
- else you may write, please give credit to Nelno the Amoeba. A postcard
- from your country or town would also be nice. Send it to:
-
- Nelno
- 58 1/2 Woodland Rd.
- Asheville, NC 28804-3823
- USA
-
- ──────────────────────────────────────────────────────────────────────── }
-
- {$A+,B-,D-,L-,Q-,O-,R-,S-,T-,V-,X+,Y-}
-
- UNIT GUSMod;
-
- Interface
-
- USES
- NewCrt, DOS, GUSHeap, Types, Strings;
-
- { GUSUtil stuff }
-
- CONST
- Board : BYTE = 0; { 3 = GUS }
- MODSpeed : WORD = 6; { ticks per pattern line }
- CurLine : WORD = 0; { current pattern line }
- CurPattern : WORD = 0; { current pattern }
- ScriptPos : WORD = 0;
- MODPlaying : BOOLEAN = FALSE;
- MODFlag : BYTE = 0;
- MODVolume : WORD = 100; { MOD Volume can be 0 - 100% }
-
- UpdateChannelRecs : BOOLEAN = TRUE;
- UpdateChannelWaves : BOOLEAN = FALSE;
-
- ActiveVoices : WORD = 13;
- CurVoice : BYTE = $FF;
-
- Stop = 2;
- Bit16 = 4;
- Loop = 8;
- Bidirec = 16;
- IRQAtEnd = 32;
- Backward = 64;
-
- Scale0 = 0;
- Scale8 = 1;
- Scale64 = 2;
- Scale512 = 3;
-
- RampStop = 3;
- RampRoll = 4;
- RampLoop = 8;
- RampBidir = 16;
- RampIRQ = 32;
- RampDec = 64;
-
- VAR
- GUS_Base : WORD;
- GUS_IRQ : WORD;
- GUS_Status : WORD;
- GUS_TimerCon : WORD;
- GUS_TimerData : WORD;
- GUS_IRQDMACon : WORD;
- GUS_MidiCon : WORD;
- GUS_MidiData : WORD;
- GUS_Voice : WORD;
- GUS_Command : WORD;
- GUS_DataLo : WORD;
- GUS_DataHi : WORD;
- GUS_DRAMIO : WORD;
-
- GUS_Mixer : BYTE;
-
- PreMODInt8 : POINTER;
-
- FUNCTION GUS_ReadVoicePos (Voice : BYTE): LONGINT;
- FUNCTION GUS_Peek (Address : LONGINT): SHORTINT;
- PROCEDURE GUS_Poke (Address : LONGINT; v : SHORTINT);
- FUNCTION GUS_Mem : WORD;
- PROCEDURE GUS_SetActiveVoices (Voices : BYTE);
- PROCEDURE GUS_VoiceFreq (VoiceNum : BYTE; Hertz : WORD);
- PROCEDURE GUS_VoiceAddr (VoiceNum : BYTE; CurPtr, Start, EndAddr : LONGINT);
- PROCEDURE GUS_VoiceVolume (VoiceNum : BYTE; Volume : WORD);
- PROCEDURE GUS_VoiceMode (Voice : BYTE; Mode : BYTE);
- FUNCTION GUS_ReadVoiceMode (Voice : BYTE): BYTE;
- PROCEDURE GUS_StopVoice (Voice : BYTE);
- PROCEDURE GUS_StartVoice (Voice : BYTE);
- PROCEDURE GUS_SpeakerOn;
- PROCEDURE GUS_SpeakerOff;
- PROCEDURE GUS_Reset;
- PROCEDURE GUS_VoiceBalance (Voice, Balance : BYTE);
- PROCEDURE GUS_RampRate (Voice, Increment, Scale : BYTE);
- PROCEDURE GUS_RampVolume (Voice, StartVol, EndVol : BYTE);
- PROCEDURE GUS_VolumeControl (Voice, ControlByte : BYTE);
- FUNCTION GUS_TestBaseAddress : BOOLEAN;
- PROCEDURE GUS_MoveSample (DosAddr, GUSAddr : LONGINT; Len : WORD);
- PROCEDURE GUS_SetClockRate (Rate : WORD);
- PROCEDURE GUS_SetTimer;
- PROCEDURE GUS_ResetTimer;
- PROCEDURE GUS_SetIRQ;
- PROCEDURE GUS_RestoreIRQ;
- PROCEDURE GUS_MODInit;
- PROCEDURE GUS_MODDeInit;
- PROCEDURE GUS_StartMOD;
- PROCEDURE GUS_StopMOD;
-
- PROCEDURE GUS_DetectCard;
- PROCEDURE GUS_LoadSample (FName : STRING; VAR GPtr : GUS_Ptr);
- PROCEDURE GUS_LoadRAW (FName : STRING; VAR GPtr : GUS_Ptr);
-
- { GUSMOD specific stuff }
-
- CONST
- MaxTracks = 8;
-
- DebugMOD = FALSE;
-
- TYPE
- InstrType = RECORD
- GPtr : GUS_Ptr;
- Len : WORD;
- FineTune : SHORTINT;
- Volume : BYTE;
- RepOfs : WORD;
- RepLen : WORD;
- Name : STRING [22];
- END;
-
-
- PatternPtr = ^PatternType;
-
- NoteType = RECORD
- InstNum : BYTE;
- Period : WORD;
- Effect : BYTE;
- EffectArg : BYTE;
- NoteName : BYTE;
- END;
-
- PatLineType = ARRAY [0..MaxTracks - 1] OF NoteType;
-
- PatternType = ARRAY [0..63] OF PatLineType;
-
- ModPtr = ^ModType;
-
- ModType = RECORD
- Samples : ARRAY [0..30] OF InstrType;
- Patterns : ARRAY [0..127] OF PatternPtr;
- PatScript : ARRAY [0..127] OF BYTE;
- NumPats : BYTE;
- EndJumpPos : BYTE;
- FormatTag : ARRAY [0..4] OF CHAR;
-
- NumChans : BYTE;
- TotalPats : BYTE;
- NumIns : BYTE;
-
- Name : STRING;
- END;
-
- ChannelRec = RECORD
- ChannelOn : BOOLEAN;
- ChannelVol : SHORTINT;
- ChannelHit : BYTE;
- Wave : ARRAY [0..79] OF SHORTINT;
- END;
-
- PROCEDURE GUS_CreateMOD;
- PROCEDURE GUS_LoadMod (FName : STRING);
- PROCEDURE GUS_DisposeMOD;
-
- CONST
- ModError : STRING = 'No Error.';
-
- NoteNames : ARRAY [0..61] OF STRING [3] = ('---',
- 'C-0', 'C#0', 'D-0', 'D#0',
- 'E-0', 'F-0', 'F#0', 'G-0',
- 'G#0', 'A-0', 'A#0', 'B-0',
- 'C-1', 'C#1', 'D-1', 'D#1',
- 'E-1', 'F-1', 'F#1', 'G-1',
- 'G#1', 'A-1', 'A#1', 'B-1',
- 'C-2', 'C#2', 'D-2', 'D#2',
- 'E-2', 'F-2', 'F#2', 'G-2',
- 'G#2', 'A-2', 'A#2', 'B-2',
- 'C-3', 'C#3', 'D-3', 'D#3',
- 'E-3', 'F-3', 'F#3', 'G-3',
- 'G#3', 'A-3', 'A#3', 'B-3',
- 'C-4', 'C#4', 'D-4', 'D#4',
- 'E-4', 'F-4', 'F#4', 'G-4',
- 'G#4', 'A-4', 'A#4', 'B-4',
- '+++');
-
- NotePeriods : ARRAY [1..60] OF WORD = (1712, 1616, 1525, 1440,
- 1357, 1281, 1209, 1141,
- 1077, 1017, 961, 907,
- 856, 808, 762, 720,
- 678, 640, 604, 570,
- 538, 508, 480, 453,
- 428, 404, 381, 360,
- 339, 320, 302, 285,
- 269, 254, 240, 226,
- 214, 202, 190, 180,
- 170, 160, 151, 143,
- 135, 127, 120, 113,
- 107, 101, 95, 90,
- 85, 80, 76, 71,
- 67, 64, 60, 57);
-
-
- VAR
- MODData : ModPtr; { pointer to MOD info for ASM routines }
- VoiceModes : ARRAY [0..31] OF BYTE;
- ChannelInfo : ARRAY [0..MaxTracks - 1] OF ChannelRec;
-
- Implementation
-
- { GUSMOD specific stuff }
-
- CONST
- ModTags : ARRAY [0..7] OF STRING [4] = ('M.K.', 'FLT4', 'M!K!', '4CHN',
- 'FLT8', '8CHN', 'OCTA',
- '6CHN');
-
- TYPE
- BuffPtr = ^BuffType;
- BuffType = ARRAY [0..1024] OF BYTE;
-
- VAR
- SEP : POINTER;
- Buff : BuffPtr;
- Channels : BYTE;
-
- {$L GUSUTIL}
-
- FUNCTION GUS_ReadVoicePos (Voice : BYTE): LONGINT; EXTERNAL;
- FUNCTION GUS_Peek (Address : LONGINT): SHORTINT; EXTERNAL;
- PROCEDURE GUS_Poke (Address : LONGINT; v : SHORTINT); EXTERNAL;
- FUNCTION GUS_Mem : WORD; EXTERNAL;
- PROCEDURE GUS_SetActiveVoices (Voices : BYTE); EXTERNAL;
- PROCEDURE GUS_VoiceFreq (VoiceNum : BYTE; Hertz : WORD); EXTERNAL;
- PROCEDURE GUS_VoiceAddr (VoiceNum : BYTE; CurPtr, Start, EndAddr : LONGINT); EXTERNAL;
- PROCEDURE GUS_VoiceVolume (VoiceNum : BYTE; Volume : WORD); EXTERNAL;
- PROCEDURE GUS_VoiceMode (Voice : BYTE; Mode : BYTE); EXTERNAL;
- FUNCTION GUS_ReadVoiceMode (Voice : BYTE): BYTE; EXTERNAL;
- PROCEDURE GUS_StopVoice (Voice : BYTE); EXTERNAL;
- PROCEDURE GUS_StartVoice (Voice : BYTE); EXTERNAL;
- PROCEDURE GUS_SpeakerOn; EXTERNAL;
- PROCEDURE GUS_SpeakerOff; EXTERNAL;
- PROCEDURE GUS_Reset; EXTERNAL;
- PROCEDURE GUS_VoiceBalance (Voice, Balance : BYTE); EXTERNAL;
- PROCEDURE GUS_RampRate (Voice, Increment, Scale : BYTE); EXTERNAL;
- PROCEDURE GUS_RampVolume (Voice, StartVol, EndVol : BYTE); EXTERNAL;
- PROCEDURE GUS_VolumeControl (Voice, ControlByte : BYTE); EXTERNAL;
- FUNCTION GUS_TestBaseAddress : BOOLEAN; EXTERNAL;
- PROCEDURE GUS_MoveSample (DosAddr, GUSAddr : LONGINT; Len : WORD); EXTERNAL;
- PROCEDURE GUS_SetClockRate (Rate : WORD); EXTERNAL;
- PROCEDURE GUS_SetTimer; EXTERNAL;
- PROCEDURE GUS_ResetTimer; EXTERNAL;
- PROCEDURE GUS_SetIRQ; EXTERNAL;
- PROCEDURE GUS_RestoreIRQ; EXTERNAL;
- PROCEDURE MODInt8; EXTERNAL; { DO NOT CALL!!!!!!!!! }
- PROCEDURE GUS_StartMOD; EXTERNAL;
- PROCEDURE GUS_StopMOD; EXTERNAL;
-
- PROCEDURE FreqTable; EXTERNAL; { DO NOT CALL!! NOT A PROCEDURE! }
- PROCEDURE FreqDivisors; EXTERNAL; { DO NOT CALL!! NOT A PROCEDURE! }
-
- { ╔═══════════════════════════════════════════════════════════════════════╗
- ║ ║
- ║ ║
- ╚═══════════════════════════════════════════════════════════════════════╝ }
-
- PROCEDURE NewExit; FAR;
-
- BEGIN
- ExitProc := SEP;
-
- IF GUS_Base <> 0 THEN
- BEGIN
- GUS_DisposeMOD;
- GUS_DestroyHeap;
- GUS_MODDeInit;
- GUS_RestoreIRQ;
- GUS_Reset;
- END;
- END;
-
- { ╔═══════════════════════════════════════════════════════════════════════╗
- ║ ║
- ║ Flips the high and low bytes of the passed word. The word is a VAR ║
- ║ parameter so it's changed outside the scope of this procedure. ║
- ║ ║
- ╚═══════════════════════════════════════════════════════════════════════╝ }
-
- PROCEDURE WordFlip (VAR W : WORD); ASSEMBLER;
-
- ASM
- les di,[W]
- mov ax,es:[di]
- xchg ah,al
- mov es:[di],ax
- END;
-
- { ╔═══════════════════════════════════════════════════════════════════════╗
- ║ ║
- ║ ║
- ╚═══════════════════════════════════════════════════════════════════════╝ }
-
- PROCEDURE GUS_CreateMod;
-
- VAR
- I : INTEGER;
-
- BEGIN
- NEW (MODData);
-
- WITH MODData^ DO
- BEGIN
- Name := '';
-
- FOR I := 0 to 127 DO
- Patterns [I] := NIL;
- END;
-
- IF DebugMOD THEN Writeln ('Created MOD.');
- END;
-
- { ╔═══════════════════════════════════════════════════════════════════════╗
- ║ ║
- ║ ║
- ╚═══════════════════════════════════════════════════════════════════════╝ }
-
- PROCEDURE GUS_DisposeMod;
-
- VAR
- I : INTEGER;
-
- BEGIN
- IF MODData = NIL THEN Exit;
-
- WITH MODData^ DO
- BEGIN
- FOR I := 0 to TotalPats - 1 DO
- BEGIN
- IF Patterns [I] <> NIL THEN
- BEGIN
- DISPOSE (Patterns [I]);
- Patterns [I] := NIL;
- END;
- END;
-
- FOR I := 0 to 30 DO
- IF Samples [I].Len * 2 > 0 THEN
- GUS_FreeMem (Samples [I].GPtr);
-
- END;
-
- DISPOSE (MODData);
- MODData := NIL;
-
- IF DebugMOD THEN Writeln ('Disposed of MOD.');
- END;
-
- { ╔═══════════════════════════════════════════════════════════════════════╗
- ║ ║
- ║ Attempts to load the file FName as a MOD file. ║
- ║ Halts with exitcode 252 if unsuccessful and global ErrorCode from ║
- ║ TYPES.PAS set to error number. ║
- ║ ║
- ╚═══════════════════════════════════════════════════════════════════════╝ }
-
- PROCEDURE GUS_LoadMod (FName : STRING);
-
- VAR
- LNotes : ARRAY [0..7] OF WORD;
-
- { ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
- ■ ■
- ■ ■
- ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ }
-
- FUNCTION LoadNullStr (VAR F : FILE; L : BYTE): STRING;
-
- VAR
- TempStr : PChar;
-
- BEGIN
- GetMem (TempStr, L);
- BLOCKREAD (F, TempStr^, L);
- LoadNullStr := StrPas (TempStr);
- FreeMem (TempStr, L);
- END;
-
- { ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
- ■ ■
- ■ ■
- ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ }
-
- FUNCTION LoadSampleInfo (VAR F : FILE; VAR S : InstrType): INTEGER;
-
- BEGIN
- WITH S DO
- BEGIN
- Name := LoadNullStr (F, 22);
- IF DebugMOD THEN Writeln ('InstrName: ', Name);
-
- BLOCKREAD (F, Len, 2);
- WordFlip (Len);
- IF DebugMOD THEN Writeln (' InstrLen: ', Len * 2);
-
- BLOCKREAD (F, FineTune, 1);
- { convert the signed nibble to a short integer }
-
- IF DebugMOD THEN Writeln (' OrigFTune: ', FineTune);
- ASM
- mov al,S.FineTune
- rcl al,5
- jnc @Positive
-
- or al,10000000b { turn on shortint's sign bit }
-
- @Positive:
- and al,10000111b { turn off nibble's sign bit }
- mov S.FineTune,al
- END;
-
- IF DebugMOD THEN Writeln (' FineTune: ', FineTune);
-
- BLOCKREAD (F, Volume, 1);
- IF DebugMOD THEN WriteLn (' Volume: ', Volume);
-
- BLOCKREAD (F, RepOfs, 2);
- WordFlip (RepOfs);
- IF DebugMOD THEN WriteLn (' RepeatOfs: ', RepOfs * 2);
-
- BLOCKREAD (F, RepLen, 2);
- WordFlip (RepLen);
- IF DebugMOD THEN WriteLn (' RepeatLen: ', RepLen * 2);
- END;
- END;
-
- { ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
- ■ ■
- ■ ■
- ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ }
-
- PROCEDURE LoadNote (VAR F : FILE; VAR Note : NoteType; VAR LastNote : WORD);
-
- VAR
- NBytes : ARRAY [0..3] OF BYTE;
- Count : INTEGER;
- Best : INTEGER;
- BestDif: INTEGER;
-
- BEGIN
- BLOCKREAD (F, NBytes, 4);
-
- WITH Note DO
- BEGIN
- InstNum := (NBytes [0] AND $F0) + ((NBytes [2] AND $F0) SHR 4);
- Period := (WORD (NBytes [0] AND $0F) SHL 8) + NBytes [1];
- IF (Period > 0) THEN LastNote := Period;
- Effect := NBytes [2] AND $0F;
- EffectArg := NBytes [3];
-
- { find the note that matches this period, or the period closest to
- it... don't adjust the period if there is not match! }
-
- Best := MaxInt;
- BestDif := MaxInt;
-
- IF (InstNum > 0) THEN
- BEGIN
- Count := 0;
- REPEAT
- INC (Count);
-
- IF ABS (NotePeriods [Count] - Period) < BestDif THEN
- BEGIN
- BestDif := ABS (NotePeriods [Count] - Period);
- Best := Count;
- END;
- UNTIL (Count > 60) OR (NotePeriods [Count] = LastNote);
-
- IF Count <= 60 THEN
- NoteName := Count
- ELSE
- BEGIN
- IF Best < MaxInt THEN
- NoteName := Best
- ELSE
- NoteName := 61;
- END;
- END
- ELSE NoteName := 0;
- END;
- END;
-
- { ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
- ■ ■
- ■ ■
- ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ }
-
- PROCEDURE LoadPatternLine (VAR F : FILE; VAR PLine : PatLineType; NumChans : BYTE);
-
- VAR
- I : INTEGER;
-
- BEGIN
- FOR I := 0 to NumChans - 1 DO
- LoadNote (F, PLine [I], LNotes [I]);
- END;
-
- { ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
- ■ ■
- ■ ■
- ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ }
-
- FUNCTION LoadPattern (VAR F : FILE; VAR Pat : PatternPtr; NumChans : BYTE): INTEGER;
-
- VAR
- I : INTEGER;
-
- BEGIN
- IF Pat <> NIL THEN
- BEGIN
- MODError := 'Pattern already in use.';
- LoadPattern := 252;
- Exit;
- END;
-
- NEW (Pat);
-
- FOR I := 0 to 63 DO
- LoadPatternLine (F, Pat^ [I], NumChans);
-
- LoadPattern := 0;
- END;
-
- { ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
- ■ ■
- ■ ■
- ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ }
-
- VAR
- Result : WORD;
- FSize : LONGINT;
- F : FILE;
- Count : INTEGER;
- AllSamples : LONGINT;
- BytesPerPat: LONGINT;
- Buff : POINTER;
- TempWord : LONGINT;
-
- BEGIN
- {$I-}
- ASSIGN (F, FName);
- RESET (F, 1);
- {$I+}
-
- Result := IOResult;
- IF Result <> 0 THEN
- ErrorHandler (252, Result);
-
- FSize := FileSize (F);
- IF FSize < 1084 THEN
- ErrorHandler (252, 30);
-
- GUS_CreateMOD;
-
- WITH MODData^ DO
- BEGIN
- { read the MODs tag field }
- FillChar (FormatTag, 5, 0);
-
- SEEK (F, 1080);
- BLOCKREAD (F, FormatTag, 4);
- IF DebugMOD THEN Writeln ('Tag field: ', StrPas (FormatTag));
-
- { determine what kind of MOD this is }
- Count := 0;
- WHILE (Count < 8) AND (StrPas (FormatTag) <> ModTags [Count]) DO
- INC (Count);
-
- IF Count < 4 THEN
- NumChans := 4
- ELSE IF Count < 7 THEN
- NumChans := 8
- ELSE IF Count = 7 THEN
- NumChans := 6
- ELSE IF Count > 7 THEN
- ErrorHandler (252, 31);
-
- IF DebugMOD THEN Writeln ('Channels: ', NumChans);
-
- Channels := NumChans;
-
- SEEK (F, 0);
- Name := LoadNullStr (F, 20);
- IF DebugMOD THEN Writeln ('MOD name: ', Name);
-
- AllSamples := 0;
- NumIns := 31; { only loads 31 instrument MODs }
-
- FOR Count := 0 to 30 DO
- BEGIN
- IF DebugMOD THEN Writeln ('Sample #' + ST (Count));
-
- LoadSampleInfo (F, Samples [Count]);
- INC (AllSamples, Samples [Count].Len * 2);
-
- IF DebugMOD THEN ReadKey;
- END;
- IF DebugMOD THEN WriteLn ('Length of all samples = ', AllSamples);
-
- BytesPerPat := (4 * NumChans * 64);
- TotalPats := BYTE ((FSize - LONGINT (1084 + AllSamples)) DIV BytesPerPat);
- IF DebugMOD THEN WriteLn ('Total Patterns: ', TotalPats);
-
- BLOCKREAD (F, NumPats, 1);
- IF DebugMOD THEN WriteLn ('NumPats: ', NumPats);
- BLOCKREAD (F, EndJumpPos, 1);
- IF DebugMOD THEN WriteLn ('End Jump Position: ', EndJumpPos);
- BLOCKREAD (F, PatScript, 128);
- BLOCKREAD (F, FormatTag, 4);
-
- FOR Count := 0 to TotalPats - 1 DO
- BEGIN
- Result := LoadPattern (F, Patterns [Count], NumChans);
- IF Result <> 0 THEN
- ErrorHandler (252, Result);
- END;
-
- Count := 0;
-
- { load in the sample data }
-
- WHILE (Count < 31) AND NOT (EOF (F)) DO
- BEGIN
- IF Samples [Count].Len * 2 > 0 THEN
- BEGIN
- BLOCKREAD (F, TempWord, 2);
-
- IF Samples [Count].Len * 2 > 3 THEN
- BEGIN
- INC (NumIns);
- GetMem (Buff, Samples [Count].Len * 2 - 2);
-
- GUS_GetMem (Samples [Count].GPtr, Samples [Count].Len * 2 - 2);
-
- BLOCKREAD (F, Buff^, Samples [Count].Len * 2 - 2);
-
- GUS_MoveSample (LONGINT (Buff), Samples [Count].GPtr.GPtr, Samples [Count].Len * 2 - 2);
-
- IF DebugMOD THEN Writeln ('Loaded sample #', Count, ', size ',
- (Samples [Count].Len * 2 - 2), ' bytes.');
- IF DebugMOD THEN Writeln (' Start = ', Samples [Count].Gptr.GPtr,
- ', End = ', Samples [Count].GPtr.GPtr + Samples [Count].GPtr.BLockSize - 1);
- FreeMem (Buff, Samples [Count].Len * 2 - 2);
- END;
- END
- ELSE
- BEGIN
- Samples [Count].GPtr.GPtr := 0;
- Samples [Count].GPtr.BlockSize := 0;
- Samples [Count].GPtr.OfsPtr := 0;
- Samples [Count].GPtr.Bank := 0;
- END;
-
- INC (Count);
- END;
-
- IF DebugMOD THEN
- BEGIN
- Writeln ('GUS_MemAvail = ', GUS_MemAvail);
- Writeln ('GUS_MaxAvail = ', GUS_MaxAvail);
- ReadKey;
- END;
- END;
-
- CLOSE (F);
-
- CurLine := 0;
- CurPattern := 0;
- ScriptPos := 0;
- MODSpeed := 6;
- END;
-
- { ╔═══════════════════════════════════════════════════════════════════════╗
- ║ ║
- ║ ║
- ╚═══════════════════════════════════════════════════════════════════════╝ }
-
- FUNCTION Hex (w : WORD): STRING;
-
- CONST
- hexChars: array [0..$F] of Char = '0123456789ABCDEF';
-
- VAR
- S : STRING;
-
- BEGIN
-
- S := hexChars [Hi(w) shr 4] + hexChars [Hi(w) and $F] +
- hexChars [Lo(w) shr 4] + hexChars [Lo(w) and $F];
-
- { remove leading zeros }
-
- WHILE (S [1] = '0') AND (Length (S) > 1) DO System.DELETE (S, 1, 1);
-
- Hex := S;
- END;
-
- { ╔═══════════════════════════════════════════════════════════════════════╗
- ║ ║
- ║ ║
- ╚═══════════════════════════════════════════════════════════════════════╝ }
-
- PROCEDURE GUS_InitBase (b : WORD);
-
- BEGIN
- GUS_Base := b;
- GUS_Status := GUS_Base + $06;
- GUS_TimerCon := GUS_Base + $08;
- GUS_TimerData := GUS_Base + $09;
- GUS_IRQDMACon := GUS_Base + $0B;
- GUS_MidiCon := GUS_Base + $100;
- GUS_MidiData := GUS_Base + $101;
- GUS_Voice := GUS_Base + $102;
- GUS_Command := GUS_Base + $103;
- GUS_DataLo := GUS_Base + $104;
- GUS_DataHi := GUS_Base + $105;
- GUS_DRAMIO := GUS_Base + $107;
- END;
-
- { ╔═══════════════════════════════════════════════════════════════════════╗
- ║ ║
- ║ ║
- ╚═══════════════════════════════════════════════════════════════════════╝ }
-
- PROCEDURE GUS_DetectCard;
-
- VAR
- GUSEnv : STRING;
- EnvStr : ARRAY [1..5] OF STRING [20];
- EnvCnt : INTEGER;
- Code : INTEGER;
-
- BEGIN
- GUSEnv := GetEnv ('ULTRASND');
-
- IF GUSEnv <> '' THEN
- BEGIN
- EnvCnt := 1;
-
- FOR EnvCnt := 1 TO 5 DO
- BEGIN
- EnvStr [EnvCnt] := '';
-
- WHILE (GUSEnv [1] <> ',') AND (Length (GUSEnv) > 0) DO
- BEGIN
- EnvStr [EnvCnt] := EnvStr [EnvCnt] + GUSEnv [1];
- System.DELETE (GUSEnv, 1, 1);
- END;
-
- System.DELETE (GUSEnv, 1, 1);
- END;
-
- VAL ('$' + EnvStr [1], GUS_Base, Code);
- IF Code = 0 THEN
- BEGIN
- GUS_InitBase (GUS_Base);
- VAL (EnvStr [4], GUS_IRQ, Code);
- END;
-
- IF Code <> 0 THEN
- BEGIN
- Print ('Error in ULTRASND environment settings.', $0F);
- Print ('Check the settings in your AUTOEXEC.BAT file.', $0F);
-
- GUS_InitBase (0);
- Exit;
- END;
-
- IF GUS_TestBaseAddress = FALSE THEN
- GUS_InitBase (0)
- ELSE
- BEGIN
- Print ('UltraSound with ' + ST (GUS_Mem) + 'K detected at address '
- + Hex (GUS_Base) + 'h, IRQ ' + ST (GUS_IRQ) + '.', $0F);
-
- GUS_Reset;
- GUS_InitHeap (GUS_Mem);
- GUS_SetActiveVoices (BYTE (ActiveVoices));
- GUS_SetIRQ;
- IF DebugKeys THEN Print ('GUS_SetIRQ: UltraSound enabled for IRQ ' + ST (GUS_IRQ) + '.', $0F);
- IF DebugKeys THEN Print ('GUS_MemAvail = ' + ST (GUS_MemAvail), $0F);
- IF DebugKeys THEN Print ('GUS_MaxAvail = ' + ST (GUS_MaxAvail), $0F);
- END;
- END
- ELSE
- BEGIN
- Print ('No ULTRASND environment variable settings were found.', $0F);
- Print ('The ULTRASND environment variable must be set in order for this', $0F);
- Print ('program to determine the UltraSound''s IRQ setting.', $0F);
-
- GUS_InitBase (0);
- Exit;
- END;
- END;
-
- { ╔═══════════════════════════════════════════════════════════════════════╗
- ║ ║
- ║ ║
- ╚═══════════════════════════════════════════════════════════════════════╝ }
-
- PROCEDURE GUS_LoadSample (FName : STRING; VAR GPtr : GUS_Ptr);
-
- VAR
- F : FILE;
- Result : WORD;
- FSize : LONGINT;
- Buff : POINTER;
-
- BEGIN
- {$I-}
- ASSIGN (F, FName);
- RESET (F, 1);
-
- FSize := FileSize (F);
-
- CLOSE (F);
-
- IF FSize <= 65020 THEN RESET (F, FSize);
- {$I+}
-
- Result := IOResult;
-
- IF (Result = 0) AND (FSize <= 65020) THEN
- BEGIN
- GetMem (Buff, FSize);
-
- {$I-}
- BLOCKREAD (F, Buff^, 1);
- CLOSE (F);
- {$I+}
-
- Result := IOResult;
-
- IF Result = 0 THEN
- BEGIN
- GUS_GetMem (GPtr, FSize);
- { GUS_DMATransfer (Buff, GPtr);}
- GUS_MoveSample (LONGINT (Buff), GPtr.GPtr, FSize);
- END;
-
- FreeMem (Buff, FSize);
- END;
-
- IF Result > 0 THEN
- ErrorHandler (252, Result)
- ELSE IF FSize > 65020 THEN
- ErrorHandler (252, 28);
- END;
-
- { ╔═══════════════════════════════════════════════════════════════════════╗
- ║ ║
- ║ ║
- ╚═══════════════════════════════════════════════════════════════════════╝ }
-
- PROCEDURE GUS_LoadRAW (FName : STRING; VAR GPtr : GUS_Ptr);
-
- TYPE
- BuffType = ARRAY [0..65019] OF SHORTINT;
-
- VAR
- F : FILE;
- Result : WORD;
- FSize : LONGINT;
- Buff : ^BuffType;
- Count : WORD;
-
- BEGIN
- {$I-}
- ASSIGN (F, FName);
- RESET (F, 1);
-
- FSize := FileSize (F);
-
- CLOSE (F);
-
- IF FSize <= 65020 THEN RESET (F, FSize);
- {$I+}
-
- Result := IOResult;
-
- IF (Result = 0) AND (FSize <= 65018) THEN
- BEGIN
- GetMem (Buff, FSize);
-
- {$I-}
- BLOCKREAD (F, Buff^, 1);
- CLOSE (F);
- {$I+}
-
- Buff^ [FSize] := 0;
-
- Result := IOResult;
-
- IF Result = 0 THEN
- BEGIN
- GUS_GetMem (GPtr, FSize);
- FOR Count := 0 to FSize - 1 DO
- Buff^ [Count] := SHORTINT (Buff^ [Count] XOR $80);
- GUS_MoveSample (LONGINT (Buff), GPtr.GPtr, FSize);
- END;
-
- FreeMem (Buff, FSize);
- END;
-
- IF Result > 0 THEN
- ErrorHandler (252, Result)
- ELSE IF FSize > 65020 THEN
- ErrorHandler (252, 28);
- END;
-
- { ╔═══════════════════════════════════════════════════════════════════════╗
- ║ ║
- ║ This routine builds the MOD frequency table. If you change the # of ║
- ║ active voices after calling this routine, you must call it again to ║
- ║ recalculate the table or things will be screwy. ║
- ║ ║
- ╚═══════════════════════════════════════════════════════════════════════╝ }
-
- PROCEDURE GUS_MODInit;
-
- TYPE
- TablePtr = ^TableType;
- TableType = ARRAY [0..1712] OF WORD;
-
- DivPtr = ^DivTableType;
- DivTableType = ARRAY [13..31] OF BYTE;
-
- VAR
- Temp : POINTER;
- I, J : INTEGER;
- EndIndex : INTEGER;
- NoteFreq : WORD;
- Table : TablePtr;
- DivTable : DivPtr;
-
- BEGIN
- MODSpeed := 6;
- MODPlaying := FALSE;
-
- { get the address of the frequency table which is actually in the GUSUTIL
- code segment. Turbo Pascal thinks FreqTable is a pointer to a PROCEDURE,
- but it is actually just a pointer to the frequency table data }
- Table := @FreqTable;
- DivTable := @FreqDivisors;
-
- { zero the frequency table }
- FillChar (Table^, SizeOf (TableType), 0);
-
- FOR I := (SizeOf (NotePeriods) DIV 2) DownTo 1 DO
- BEGIN
- IF I = 1 THEN
- EndIndex := 1712
- ELSE
- EndIndex := NotePeriods [I - 1];
-
- { find the correct frequency for this period }
- NoteFreq := TRUNC (7093789.2 / INT (NotePeriods [I] * 2));
- NoteFreq := NoteFreq DIV DivTable^ [ActiveVoices];
-
- { fill in the table with the correct frequency, up to the next frequency }
- FOR J := NotePeriods [I] to EndIndex DO
- Table^ [J] := NoteFreq;
- END;
-
- GetIntVec ($08, Temp);
- IF Temp <> @MODInt8 THEN
- BEGIN
- PreMODInt8 := Temp;
- SetIntVec ($08, @MODInt8);
- END;
-
- SetTimer0Rate (55); { 55 * 18.2 = 1001 interrupts / sec }
- END;
-
- { ╔═══════════════════════════════════════════════════════════════════════╗
- ║ ║
- ║ ║
- ╚═══════════════════════════════════════════════════════════════════════╝ }
-
- PROCEDURE GUS_MODDeInit;
-
- VAR
- Count : INTEGER;
-
- BEGIN
- MODPlaying := FALSE;
- CurLine := 0;
- CurPattern := 0;
- MODSpeed := 6;
-
- FOR Count := 0 to 3 DO
- GUS_StopVoice (Count);
-
- SetIntVec ($08, PreMODInt8);
- END;
-
- { ╔═══════════════════════════════════════════════════════════════════════╗
- ║ ║
- ║ ║
- ╚═══════════════════════════════════════════════════════════════════════╝ }
-
- BEGIN
- MODData := NIL;
- GUS_DetectCard;
-
- IF GUS_Base <> 0 THEN GUS_MODInit;
-
- SEP := ExitProc;
- ExitProc := @NewExit;
- END.
-