home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Audio 4.94 - Over 11,000 Files
/
audio-11000.iso
/
amiga
/
players
/
modplyrs
/
dstrckr2.lha
/
DTL
/
code
/
mJuke.S
< prev
next >
Wrap
Text File
|
1993-12-17
|
54KB
|
1,624 lines
;;=====================================================================;;
;; mJuke.S - Begun Wed, Jan 02, 1993 ;;
;; Last Updated: Sat, Dec 18, 1993 ;;
;;---------------------------------------------------------------------;;
;; This is version 2.0 of the Micro-Jukebox for ;;
;; destracker.library release 2.0+ (lib ver 3.0+) ;;
;;---------------------------------------------------------------------;;
;; Designed and written by Darren E. Schebek ;;
;; Copyright ⌐1993 by Darren E. Schebek ;;
;; All rights reserved. ;;
;; This source code is NOT public domain! ;;
;;=====================================================================;;
INCLUDE "dp:system.gs" ;DevPac-specific global include.
INCLUDE "hardware/custom.i" ;Something DevPac forgot about.
INCLUDE "DTLib.I" ;DES-Tracker include file.
;---------------------------------------------------------------
; System equates...
_SYSBase = 4 ;Pointer to exec library base.
CHIP = $DFF000 ;Pointer to custom chip registers.
;---------------------------------------------------------------
; Control character equates used in text strings and in string parsing...
NULL = 0 ;NULL
TAB = $09 ;TAB
LF = $0A ;LineFeed
CR = $0D ;Carriage Return
ESC = $9B ;Control sequence introducer
CEOL = $4B ;Clear to end of line
DQUOTE = $22 ;Double quote
;---------------------------------------------------------------
; Some custom macros of my own...
LOADLIB MACRO
Move.L _\1Base,A6
ENDM
SYS MACRO
Jsr _LVO\1(A6)
ENDM
BYTETAB MACRO
\1 EQU SOFFSET
SOFFSET SET SOFFSET+\2
ENDM
WORDTAB MACRO
\1 EQU SOFFSET
SOFFSET SET SOFFSET+(\2*2)
ENDM
;---------------------------------------------------------------
; Variables used by the mJuke program...
STRUCTURE MJUKE,0
WORDTAB RandTable,256 ;Prime-index table used by random number routine.
UWORD RandIndex ;Used by my random number routine. (r-256 alg.)
UBYTE DTPlayDoneSig ;Signal bit DTLib uses to signal me.
UBYTE DTIterDoneSig ;Another signal bit used with destracker.
UBYTE FadingFlag ;Set when a song is meant to fade out at end.
UBYTE PathnameFlag ;Used with scripts to tell paths and files apart.
UBYTE JukeVolume ;Holds current global playback volume.
UBYTE ShuffleFlag ;1 = Shuffle filenames, 0 = don't even think about it.
UBYTE NumTimeSpaces ;# of spaces to print before printing elapsed time.
UBYTE Pad
UWORD CurSongNum ;Keeps track of which song I'm playing in list.
UWORD NumIterations ;Holds calc'ed # of iterations to play song for.
ULONG WaitSignals ;Holds signal bit #'s I wait for while playing.
APTR ScriptAddr ;Temporary ptr used for script processing.
APTR MyFileHandle ;Temporary ptr used for loading script file.
ULONG FileSize ;Temporary. Holds size in bytes of script file.
UWORD NumOfFiles ;Holds number of song files in the play list.
APTR PtrTableAddr ;Points to array of pointers to song filenames.
ULONG PtrTableLen ;Holds length of the ptr-array in bytes.
APTR FilenameBufAddr ;Ptr to buffer containing all song filenames.
ULONG FilenameBufLen ;Holds length of filename buffer in bytes.
ULONG FilenameBufIndex ;Used for appending new song filenames to buffer.
APTR TaskPointer ;Pointer to mJuke's own TCB structure.
BPTR LockPointer ;Holds DOS Lock required for Examine() function.
BPTR OutputHandle ;Holds BPTR to standard output for text output.
APTR MyFileInfoBlock ;Ptr to file info block struct required by DOS.
APTR DosBase ;Pointer to DOS library base structure.
APTR DTBase ;Pointer to DES-Tracker library base structure.
BYTETAB FilenameString,256 ;Used to construct file/path specifications.
LABEL MJUKE_SIZE ;Size of the memory area required for variables.
;---------------------------------------------------------------
; In the beginning, there was "Main". :) I start out by allocating
; a block of memory to be used for all of this program's variables.
; This is much more elegant than simply throwing all the variables
; into the block storage segment...
Main Move.L A0,A5 ;Cache CLI argstring pointer in A5.
; Allocate space for program variables...
LOADLIB SYS ;Need SYS_Base in A6.
Move.L #MJUKE_SIZE,D0 ;Size of the variable space I need.
Move.L #MEMF_CLEAR,D1 ;Initialize everything to 0 for me. :)
SYS AllocMem ;Attempt to allocate the mem.
Tst.L D0 ;Allocate error?
Ble .ProgAbort ;Yup, leave immediately.
Move.L D0,A4 ;A4 is now sacred (pts to variable space).
Bsr Initialize ;Initialize everything.
Tst.L D0 ;Error during initialization?
Bne .Abort ;Yup, abort with error.
;-------------------------------
; Static initialization and setup is now complete, now parse the command line
; argstring, build the list of song filenames to load and play, shuffle them
; randomly, and then play the bleedin' songs...
Bsr ProcessCmdLine ;Parse cmd-line args, collect filenames.
Tst.L D0 ;Did an error occur?
Bne .Error ;Yup.
Bsr MakePtrTable ;Build and shuffle filename ptr array.
Tst.L D0 ;Error?
Bne .Error ;Yuppers. So abort.
Tst.B ShuffleFlag(A4) ;Should I shuffle the filenames?
Beq .NoShuffle ;Nope.
Bsr ShuffleSongs ;Randomize filenames in pointer array.
.NoShuffle
Bsr PlaySongs ;No problems, so go play the songs.
Tst.L D0 ;Error?
Bne .Error ;Yes, someone must have locked the library.
Move.L #JukeDoneText,D2 ;Say "Hasta la vista, baby!"
Bsr PrintNicely ;(or something along those lines).
MoveQ #0,D7 ;Indicate success upon exit.
;-------------------------------
; Deallocate everything and leave. Note that I don't bother calling
; RemStatusSignal for the two signal bits I use with DES-Tracker, since
; DES-Tracker will remove them for me when I close the library.
.Abort Bsr DeInitialize ;Free up used resources.
Move.L D7,D0 ;Return exit code in D0.
.Leave Rts ;Byeeee!
.Error MoveQ #10,D7 ;Set error exit flag & bail out.
Bra .Abort
.ProgAbort MoveQ #10,D7 ;Exit with error code in D0.
Bra .Leave
;---------------------------------------------------------------
; ProcessCmdLine routine. Parses the command line, determines whether or not
; the argument is a pathname or a filename, and calls either ReadPath or
; ReadScript as a result.
;
; Input: A5 = Ptr to the command line argstring.
;
; Output: D0 = 0 if song file list was created successfully.
; -1 if an error occurred along the way.
;
; Trashes: D1,D2,D3,A0,A1,A2,A5,A6
;
ProcessCmdLine Bsr GetFilespec ;Get filespec/pathspec from argstring.
Tst.W D0 ;Was there a file/path spec?
Bne .ShowUsage ;No, just print out usage text.
Lea FilenameString(A4),A0 ;Yes, get ptr to file/path spec.
Bsr VerifyPath ;See whether it's a path or file.
Tst.L D0 ;It's neither a path nor a file.
Bmi .BadPath ;(must be gibberish from the abuser).
Move.L D0,-(Sp) ; Save file/path status temporarily.
Move.L #CFilesText,D2 ;Say "Collecting filenames..."
Bsr PrintN2Text
Move.L (Sp)+,D0 ; Restore file/path status.
Beq .IsFile ;D0 = 0 means it's a filespec.
; User specified a pathname, add it to DES-Tracker's list of search paths...
Move.L DTBase(A4),A6 ;Get DES-Tracker base ptr.
Sub.L A0,A0 ;Pass NULL to clear all existing paths.
SYS SetModulePath ;Do it.
Lea FilenameString(A4),A0 ;Get ptr to pathspec string.
SYS SetModulePath ;Add pathspec to search path list.
Bsr ReadPath ;Read in all the filenames.
Bra .Done ;And leave. :)
; User specified a file, so first check to make sure it's a script...
.IsFile Move.L MyFileInfoBlock(A4),A0 ;Info Block filled in by DOS.
Move.L fib_Protection(A0),D0 ;Get file's protection bits.
BTst #FIBB_SCRIPT,D0 ;Is script bit set?
Bne .IsScript ;Yup, go read it in.
Move.L #NotScriptText,D2 ;Nope, tell user about it.
Bsr PrintNicely
Bra .Error ;And abort.
.IsScript Bsr ReadScript ;Read script, record song filenames.
.Done Move.L D0,-(Sp) ; Save return code temporarily.
Move.L #IPlayText,D2 ;Say "Press CTRL-C to abort..."
Bsr PrintNicely
Move.L (Sp)+,D0 ; Restore return code in D0.
.Leave Rts ;All done.
.ShowUsage Move.L #ComText,D2 ;No parms specified, show usage.
Bsr PrintNicely
Bra .Error ;And abort.
.BadPath Move.L #ComText,D2 ;User not clear on concept,
Bsr PrintNicely ; so show usage text
Move.L #BadPathText,D2 ; and then say "Illegal path...".
Bsr PrintNicely
.Error MoveQ #-1,D0 ;D0 = error return code.
Bra .Leave ;Leave.
;---------------------------------------------------------------
; ReadScript routine. Reads in the specified script file (if possible) and
; parses all the song filenames, storing them into the filename buffer. Any
; lines in the script that start with a "#" character are assumed to have a
; search pathname immediately following the "#". Search paths are added to
; the DES-Tracker library's list of search paths.
;
; Input: FilenameString(A4) must hold the complete filespec of the script file.
;
; Output: D0 = 0 if script file parsed successfully.
; -1 if an error occurred while parsing the file.
;
; Trashes: D1,D2,D3,D6,A0,A1,A2,A6
;
ReadScript Clr.W NumOfFiles(A4) ;Initialize song count.
Bsr AllocFNameBuffer ;Allocate an initial filename buffer.
Tst.L D0 ;Error?
Bmi .InsuffMem ;Yup, so abort.
; Now open the script file...
Lea FilenameString(A4),A0 ;Pointer to the script filename.
Move.L A0,D1 ; In D1.
Move.L #MODE_OLDFILE,D2
Move.L DosBase(A4),A6 ;Use DOS_Base.
SYS Open ;Attempt to open script file.
Move.L D0,MyFileHandle(A4) ;Store file handle ptr.
Beq .Error ;If 0, then file didn't open.
; Script file opened, so first determine how big the file is so I can
; allocate enough space to read it in...
Move.L D0,D1 ;Need file handle in D1.
MoveQ #0,D2
Move.L #OFFSET_END,D3 ;Seek to end of file.
SYS Seek
Move.L MyFileHandle(A4),D1 ;Restore file handle in D1.
MoveQ #0,D2
Move.L #OFFSET_BEGINNING,D3 ;Seek back to beginning of file.
SYS Seek
Move.L D0,FileSize(A4) ;D0 holds file size, save it.
; Now allocate enough memory to read in the script...
LOADLIB SYS ;Need SYS_Base to do it.
Move.L #MEMF_CLEAR,D1 ;Clear the memory for me.
SYS AllocMem ;Attempt to allocate the mem.
Move.L D0,ScriptAddr(A4) ;Save ptr to script memory.
Beq .Error ;If 0, then allocate failed.
; Now read in the script file...
Move.L DosBase(A4),A6 ;Get DOS_Base
Move.L MyFileHandle(A4),D1 ;File handle in D1.
Move.L D0,D2 ;Ptr to dest buffer in D2.
Move.L FileSize(A4),D3 ;# of bytes to read in D3.
SYS Read ;Attempt to read the script file.
Tst.L D0 ;Error?
Bgt .Cont1 ;Nope.
Move.L #ReadErrText,D2 ;Yup, tell user about it.
Bsr PrintNicely
Bra .Error ;And abort.
; Now I must parse the script file in memory...
.Cont1 Move.L FileSize(A4),D0 ;Get script file size.
Move.L ScriptAddr(A4),A0 ;Pointer to script in A0.
Lea 0(A0,D0.L),A1 ;A1 points to end of script.
.ParseLoop Bsr ReadLine ;Read in line from script.
Tst.L D0 ;Reached end of file yet?
Bne .Done ;Yup.
Lea FilenameString(A4),A2 ;Nope, get ptr to the line.
Tst.B PathnameFlag(A4) ;Is it a filename or search path?
Beq .StoreName ;It's a filename, store in buffer.
Move.L DTBase(A4),A6 ;It's a search path.
MoveM.L A0-A1,-(Sp) ; Save useful pointers.
Move.L A2,A0 ;Ptr to path string in A0
SYS SetModulePath ;Add search path to library's list.
MoveM.L (Sp)+,A0-A1 ; Restore useful pointers.
Bra .ParseLoop ;Extract & parse next line in script.
; Here, a filename string has been returned by the ReadLine routine, so I
; must copy the filename into the filename buffer...
.StoreName Lea FilenameString(A4),A3 ;Ptr to filename string in A3.
MoveM.L A0-A1,-(Sp) ; Save useful pointers.
Bsr AddFNameToBuffer ;Add filename string to buffer.
MoveM.L (Sp)+,A0-A1 ; Restore useful pointers.
Tst.L D0 ;Error?
Bmi .InsuffMem ;Yup, report it and abort.
AddQ.W #1,NumOfFiles(A4) ;Filename added, adjust file count.
Bra .ParseLoop ;Read & parse next line in file.
.Done MoveQ #0,D6 ;Indicate success in D6.
.Leave Move.L DosBase(A4),A6 ;Get DOS_Base.
Move.L MyFileHandle(A4),D1 ;File handle in D1.
Beq .1 ;If 0, then file didn't open.
SYS Close ;Close the script file.
.1 LOADLIB SYS ;Get SYS_Base.
Move.L ScriptAddr(A4),D0 ;Get ptr to script file in memory.
Beq .2 ;If ptr is 0, then allocate failed.
Move.L D0,A1 ;It didn't fail.
Move.L FileSize(A4),D0 ;Size of the memory block.
SYS FreeMem ;Free memory used by script file.
.2 Move.L D6,D0 ;Return code copied to D0.
Rts ;Leave.
.Error MoveQ #-1,D6 ;Indicate error in D6.
Bra .Leave ;And abort.
.InsuffMem Move.L #InsuffMemText,D2 ;Report "Not enough memory".
Bsr PrintNicely
Bra .Error ;And abort.
;---------------------------------------------------------------
; ReadLine routine. Extracts the next line from the script file in memory.
; This routine will automatically skip blank lines and will parse out
; comments and white space before returning a valid string.
;
; This routine also checks the first non-whitespace character of the line and,
; if the character is a "#" symbol, then it sets the PathnameFlag variable.
; This tells the calling routine that the string returned in FilenameString[]
; is a *pathname* and not a *song filename*.
;
; Input: A0 = ptr to current position in script file in memory.
;
; Output: LineBuffer(A4) holds ptr to buffer containing the parsed input line.
;
; Trashes: D0,D1,D2,A0,A1,A2
;
ReadLine Lea FilenameString(A4),A2 ;Ptr to dest string buffer in A2.
Move.L A2,D1 ;Keep copy in D1.
Bra .NextLine ;See if I've reached EOF yet.
.ReadLoop Cmp.B #";",(A0) ;Check if 1st char is a ";".
Bne .SkipWhite ;It isn't.
.SkipLine Cmp.B #LF,(A0)+ ;It is, so skip entire line.
Bne .SkipLine
Bra .NextLine ;See if I've reached EOF.
; Skip leading white space (if any)...
.SkipWhite Move.B (A0)+,D0 ;Get char from script.
Cmp.B #" ",D0 ;Is it a space character?
Beq .SkipWhite ;Yup, parse it out.
Cmp.B #TAB,D0 ;Is it a tab character?
Beq .SkipWhite ;Yup, parse it out.
Cmp.B #LF,D0 ;Reached EOL yet?
Beq .NextLine ;Yup, line's blank, check for EOF.
SubQ #1,A0 ;Char is legit, back up to it.
.RLoop Move.B (A0)+,D0 ;Fetch next char from script.
Cmp.B #LF,D0 ;Reached EOL yet?
Beq .Parse ;Yes, go parse out trailing white space.
Cmp.B #"#",D0 ;Pathname prefix character?
Beq .SetPath ;Yes, go set flag indicating so.
Cmp.B #";",D0 ;Is it a comment char?
Bne .Store ;Nope, so store char in dest string.
.SkipEOL Cmp.B #LF,(A0)+ ;Yes, so ignore remainder of this line.
Bne .SkipEOL
Bra .Parse ;Go parse what I got from this line.
.Store Move.B D0,(A2)+ ;Store char in filename string.
Cmp.L A1,A0 ;Reached EOF yet?
Blt .RLoop ;Nope, go fetch another character.
Bra .Parse ;Yup, go parse what I've got so far.
.SetPath St PathnameFlag(A4) ;Indicate that this is a pathname.
Bra .RLoop ;Go fetch next character.
; Here, I must null-terminate the string I have constructed in FilenameString[].
; I must also trim the trailing white space from the string...
.Parse Cmp.L A2,D2 ;Anything stored in dest string?
Beq .NextLine ;Nope, this line parses to a blank line.
Move.B -(A2),D0 ;Get previous char from dest string.
Cmp.B #" ",D0 ;Is it a space character?
Beq .Parse ;Yup, parse it out.
Cmp.B #TAB,D0 ;Is it a tab character?
Beq .Parse ;Yup, parse it out.
AddQ #1,A2 ;No, so move forward to next cell.
Clr.B (A2) ;NULL-terminate the string.
MoveQ #0,D0 ;Return success code in D0.
.Leave Rts ;Leave.
; This bit of code is called prior to parsing a line from the script file
; to see if I've gone past the end of the script file. Since it's always
; called before the line is parsed, I clear the PathnameFlag...
.NextLine Clr.B PathnameFlag(A4) ;Clear pathname flag.
Cmp.L A1,A0 ;Reached EOF yet?
Blt .ReadLoop ;Nope, it's OK to parse another line.
Move.L #-1,D0 ;Indicate end of file.
Bra .Leave ;And leave.
;---------------------------------------------------------------
; ReadPath routine. Reads all files from the path specified in
; FilenameString[]. The calling routine must have previously called the
; VerifyPath routine, which sets up a lock on the path (stored in LockPointer)
; and also fills out the MyFileInfoBlock structure by calling Examine().
;
; All filenames are read in from the path (using ExNext()) and stored in the
; filename buffer.
;
; Input: LockPointer(A4) must contain lock on path.
; MyFileInfoBlock(A4) must point to filled out struct for Examine().
;
; Output: D0 = 0 if all files successfully read in and stored.
; -1 if an error occurred.
;
; Trashes: D1,D2,D3,A0,A1,A3,A6
;
ReadPath
; First, allocate a buffer to hold the filenames for all files in the path...
Bsr AllocFNameBuffer ;Allocate initial filename buffer.
Tst.L D0 ;Did allocate succeed?
Bmi .InsuffMem ;Nope, so abort with error.
; And now, I call ExNext() repeatedly until all filenames have been read in. I
; must also count the number of files in the path so that I know how large the
; pointer table must be (which I create after all filenames have been read)...
Move.W #0,NumOfFiles(A4) ;Initialize filename counter.
.ReadFile Move.L DosBase(A4),A6 ;Get DOS_Base.
Move.L LockPointer(A4),D1 ;Lock ptr in D1.
Move.L MyFileInfoBlock(A4),A0 ;FileInfoBlock struct ptr in A0.
Move.L A0,D2 ;DOS wants it in D2.
SYS ExNext ;Get info for next file in path.
Tst.L D0 ;Were any files left?
Beq .Done ;Nope, all done.
; Add this filename to the buffer containing all the filenames...
Move.L MyFileInfoBlock(A4),A3 ;Pointer to file info block.
Tst.W fib_DirEntryType(A3) ;Make sure this is a file.
Bpl .ReadFile ;It's a directory, so skip it.
Lea fib_FileName(A3),A3 ;A3 points to filename string.
Bsr AddFNameToBuffer ;Add filename string to buffer.
Tst.L D0 ;Error in doing so?
Bmi .InsuffMem ;Yup, abort with error.
AddQ.W #1,NumOfFiles(A4) ;Nope, adjust filename count.
; Now go back and see if any files remain in path...
Bra .ReadFile ;Back for another file.
.Done MoveQ #0,D0 ;Return with success code.
.Leave Rts ;Leave.
.Error MoveQ #-1,D0 ;Return with error code.
Bra .Leave ;Leave.
.InsuffMem Move.L #InsuffMemText,D2 ;Tell user "Ran outa mem, dude."
Bsr PrintNicely
Bra .Error ;Abort with error.
;---------------------------------------------------------------
; MakePtrTable routine. Creates a table of pointers to each filename string
; in the filename buffer and then shuffles the pointers randomly.
;
; Input: NumOfFiles(A4) holds number of filenames in the buffer.
; FilenameBufAddr(A4) must hold ptr to filename buffer.
;
; Output: D0 = 0 if pointer table successfully created and shuffled.
; -1 if an error occurred.
;
; Trashes: D1,D2,D3,A0,A1,A6
;
MakePtrTable MoveQ #0,D0
Move.W NumOfFiles(A4),D0 ;Get # of filenames in buffer.
Bne .AreFiles ;There's at least one filename.
Move.L #NoFilesText,D2 ;No filenames in buffer.
Bsr PrintNicely
Bra .Error ;Abort with error code.
.AreFiles Add.L D0,D0 ;Make longword index out of D0.
Add.L D0,D0 ;D0 is now size of the ptr array.
Move.L D0,PtrTableLen(A4) ;Store size for deallocation later.
LOADLIB SYS ;Get SYS_Base.
Move.L #MEMF_CLEAR,D1 ;Clear the memory for me, please.
SYS AllocMem ;Attempt to allocate ptr array mem.
Move.L D0,PtrTableAddr(A4) ;Save ptr to ptr-array memory.
Bne .HavePtrMem ;Allocate was successful.
.InsuffMem Move.L #InsuffMemText,D2 ;Oop, ran out of memory. Darn.
Bsr PrintNicely
Bra .Error ;Abort with error.
; Now fill out the pointer array...
.HavePtrMem Move.L FilenameBufAddr(A4),A1 ;get ptr to filenames buffer.
Move.L D0,A0 ;A0 points to ptr-array.
Move.W NumOfFiles(A4),D0 ;Do for # of filenames in buffer.
SubQ #1,D0 ;Adjust count for "Do..Until" loop.
Bra .1
.IPALoop Tst.B (A1)+ ;Check next char in filename buffer.
Bne .IPALoop ;Search until NULL is found.
.1 Move.L A1,(A0)+ ;Store pointer to filename.
DBra D0,.IPALoop ;Find start of next filename string.
; At this point, All filenames have been read in, so I can leave now...
MoveQ #0,D0 ;Indicate success.
.Leave Move.L D0,-(Sp) ; Save return code.
Move.L LockPointer(A4),D1 ;Lock pointer in D1.
Move.L DosBase(A4),A6 ;Get DOS_Base.
SYS UnLock ;Don't need lock anymore.
Clr.L LockPointer(A4) ;So exit code knows it's gone.
Move.L (Sp)+,D0 ; Restore return code.
Rts ;And leave.
.Error MoveQ #-1,D0 ;Indicate an error occurred.
Bra .Leave ;Abort with error.
;---------------------------------------------------------------
; ShuffleSongs routine. Takes each pointer in the pointer array and swaps it
; with another pointer in the array that is chosen at random.
;
; Input: PtrTableArray(A4) must point to the pointer array to shuffle.
; NumOfFiles(A4) must holds the number of pointers in the array.
;
; Output: Nothing.
;
; Trashes: D0,D1,D6,D7,A0
;
ShuffleSongs Move.L PtrTableAddr(A4),A0 ;Get ptr to the Pointer-array.
Move.W NumOfFiles(A4),D7 ;Do for # of pointers in array.
SubQ.W #1,D7 ;Adjust loop count for "Do..Until".
.NextFile Bsr Random ;Get a random number.
MulU NumOfFiles(A4),D0 ;Create random value in range.
Swap D0 ;D0 is now 0..NumOfFiles-1.
; I have a random element, so swap the corresponding pointers...
Add.W D0,D0 ;Make long index of random index.
Add.W D0,D0
Move.W D7,D6 ;Copy loop count into D6.
Add.W D6,D6 ;Make long index of loop count.
Add.W D6,D6
Move.L 0(A0,D0.W),D1 ;Save random element's pointer.
Move.L 0(A0,D6.W),0(A0,D0.W) ;Copy cur. element to random element.
Move.L D1,0(A0,D6.W) ;Store random element in cur. element.
DBra D7,.NextFile ;Until all ptrs have been swapped.
Rts
;---------------------------------------------------------------
; PlaySongs routine. This is the routine that goes through the entire list
; of songs, loading and playing each one. Since I do not lock the library,
; I have to be wary of the user doing things with DTC like unloading a module
; on me while I'm playing it.
;
; Input: PtrTableAddr(A4) must hold ptr to array of pointers to song filenames.
; NumOfFiles(A4) must hold the number of filenames to load and play.
;
; Output: D0 = 0 if all songs were processed without any trouble.
; -1 if another program in memory locked the library.
;
; Trashes: D1,D2,D3,A0,A1,A2,A5,A6
;
PlaySongs Move.W #0,CurSongNum(A4) ;Init current song number.
Move.L PtrTableAddr(A4),A5 ;Cache ptr-array ptr in A5.
Move.L DTBase(A4),A6 ;Get DES-Tracker base.
MoveQ #0,D0
Move.B DTPlayDoneSig(A4),D0 ;Get play-done signal bit #.
Move.W #SEF_FINISH|SEF_MODUNLOADED|SEF_STOP|SEF_LIBOWNED,D1 ;Specify events I want.
SYS AddStatusSignal ;Add event signal to library.
MoveQ #0,D0
Move.B DTIterDoneSig(A4),D0 ;Get iteration-done sig bit #.
Move.W #SEF_ITERATION|SEF_VOLFADE,D1 ;Specify events I want.
SYS AddStatusSignal ;Add event signal to library.
MoveQ #0,D0
Move.B JukeVolume(A4),D0 ;Get current global juke volume.
SYS SetGlobalVolume ;Set global volume.
;-------------------------------
; Here begins the load & play loop. I use the variable WaitSignals(A4) to hold
; the signal bits that I want to wait for. I always wait for CTRL-C as well as
; the play-done signal. However, if the song is to be played for more than one
; iteration, then I also need to wait for the iteration-done signal in order for
; me to know when to start fading out the song...
.PlayLoop Bsr LoadSong ;Attempt to load song.
Tst.L D0 ;So what happened?
Beq .Go ;Song loaded fine.
Bpl .NextSkipTime ;Couldn't load song. Try next song.
Bra .Error ;Library now locked, so abort.
; At this point the module loaded successfully. First, I'll print "Playing..."
; and then I must examine the module a bit to determine how I'm to play it...
.Go Move.L dtl_ModuleStatus+ms_Name(A6),A0
MoveQ #-1,D1
.Count AddQ #1,D1
Tst.B (A0)+
Bne .Count
MoveQ #22,D0
Sub.W D1,D0
Move.B D0,NumTimeSpaces(A4)
Move.L #PlayingText,D2 ;Say "Playing "
Bsr PrintN2Text
Move.L dtl_ModuleStatus+ms_Name(A6),D2
Bsr PrintN2Text ; followed by module name.
Move.L #XtraPlayText,D2 ; followed by close quote.
Bsr PrintN2Text
;-------------------------------
; I have to determine the number of iterations for this module.
; Here's how I adjust for the number of iterations...
;
; Module's default What I convert
; # of iterations it to
;
; iter = 1 1
; iter = 0 2
; iter > 1 iter + 1
;
; Also, if the resultant number of iterations is greater than 1, then I have
; to fade the song out at the beginning of its last iteration...
MoveQ #0,D0
Move.B dtl_DefIterations(A6),D0 ;Get default # of iterations.
Cmp.W #1,D0 ;Is it a one-shot tune?
Beq .StoreIter ;Yes, keep it that way.
Bgt .AddOneIter ;>1 means add extra iteration.
MoveQ #1,D0 ;Next instruction changes it to 2.
.AddOneIter AddQ #1,D0 ;I will fade on this extra iteration.
; Here, the number of iterations is greater than 1. This means I must fade
; the song out at the beginning of the last iteration. To do this, I'll need
; to wait for the iteration-done signal bit, so I add it to WaitSignals(A4)...
Move.L WaitSignals(A4),D2 ;Get wait signal bits
Move.B DTIterDoneSig(A4),D1 ;Include the iter-done signal.
BSet D1,D2
Move.L D2,WaitSignals(A4) ;Store updated wait signal bits.
.StoreIter Move.W D0,NumIterations(A4) ;Store new number of iterations.
SYS SetIterations ;Set new # of iterations.
SYS BeginPlaySeq ;Start playing the song.
;-------------------------------
; Wait until the song is finished or something diabolical happens...
.WaitLoop LOADLIB SYS ;Get SYS_Base.
MoveQ #0,D0
Move.L WaitSignals(A4),D0 ;Wait signals in D0.
SYS Wait ;Wait for something to happen.
Move.L DTBase(A4),A6 ;Get DES-Tracker base.
;-------------------------------
; At this point, I've been signalled, so I have to determine which signals I've
; received and act on them accordingly...
MoveQ #SIGBREAKB_CTRL_C,D1 ;Get control-C signal bit #.
BTst D1,D0 ;Was CTRL-C pressed?
Beq .CheckCTRLF ;Nope.
Move.L #JukeAbortText,D2 ;Yup, so abort jukebox.
Bsr PrintNicely
Bra .Error ;Abort.
.CheckCTRLF MoveQ #SIGBREAKB_CTRL_F,D1 ;Get control-F signal bit #.
BTst D1,D0 ;Was CTRL-F pressed?
Beq .CheckDTSig ;Nope.
Move.B dtl_GlobalVolume(A6),JukeVolume(A4)
Bra .NextTune ;Yup, skip to next song.
.CheckDTSig Move.B DTPlayDoneSig(A4),D1 ;Get play-done sig bit #.
BTst D1,D0 ;Play seq done or mod unloaded?
Beq .CheckIter ;Nope.
; Here, either the (one-shot) song has finished playing or the module was unloaded
; while it was playing. Before going on to the next song, I must grab the current
; global volume setting from the library, since the user may have changed it
; while I was asleep...
Move.B dtl_GlobalVolume(A6),JukeVolume(A4) ;Get new global volume.
Bra .NextTune ;Go load and play the next song.
.CheckIter Move.B DTIterDoneSig(A4),D1 ;Get iter-done sig bit #.
BTst D1,D0 ;Iteration or volume fade done?
Beq .WaitLoop ;Nope, go back to sleep.
Tst.B FadingFlag(A4) ;Am I waiting for a fade to complete?
Bne .NextTune ;Yup, so go on to next song.
Move.B dtl_IterationsToGo(A6),D1 ;No, get iterations left to play.
Cmp.B #1,dtl_IterationsToGo(A6) ;Started last iteration?
Bgt .WaitLoop ;No, go back to sleep.
; Now song is on last iteration, so start a gobal volume fade. But, since
; the user may have changed the global volume while I was asleep, I must
; grab it and use it to reset the global volume after the volume fade has
; completed...
MoveQ #0,D0
Move.B dtl_GlobalVolume(A6),JukeVolume(A4) ;Save global volume.
MoveQ #0,D0 ;Fade to 0 volume.
SYS FadeGlobalVolume ;Start the fade.
St FadingFlag(A4) ;Indicate I'm waiting for fade now.
Bra .WaitLoop ;Go back to sleep.
;-------------------------------
; Time to go to the next song in the pointer array. I am about to unload the
; currently loaded module. Before I do this, I must tell DES-Tracker not to
; signal me when a module is unloaded. Otherwise, I'd then load up the next
; module, and when I go to sleep, I'll be immediately awakened with a
; MODUNLOADED event (for the previous module). DES-Tracker will signal me
; whether I'm asleep or not, so I must inform it that I have no interest in
; unloaded modules temporarily...
.NextTune
; Display time that song was playing for...
Lea Spaces,A0
Move.L A0,D2
MoveQ #0,D3
Move.B NumTimeSpaces(A4),D3
Bsr PrintCLI
Move.L #PlayTimeText,D2
Move.L #PlayTimeLen,D3
Bsr PrintCLI
Lea UnitsNoColon,A2
Move.B dtl_ElapsedHours(A6),D0
Bsr BinToBCD
Move.B D0,D1
Lsr.B #4,D0
And.B #$0F,D1
Add.B #'0',D0
Add.B #'0',D1
Move.B D0,(A2)
Move.B D1,1(A2)
Move.L A2,D2
MoveQ #UNoColonLen,D3
Bsr PrintCLI
Lea UnitsColon,A2
MoveQ #UColonLen,D3
Move.B dtl_ElapsedMinutes(A6),D0
Bsr BinToBCD
Move.B D0,D1
Lsr.B #4,D0
And.B #$0F,D1
Add.B #'0',D0
Add.B #'0',D1
Move.B D0,1(A2)
Move.B D1,2(A2)
Move.L A2,D2
Bsr PrintCLI
Lea UnitsColon,A2
MoveQ #UColonLen,D3
Move.B dtl_ElapsedSeconds(A6),D0
Bsr BinToBCD
Move.B D0,D1
Lsr.B #4,D0
And.B #$0F,D1
Add.B #'0',D0
Add.B #'0',D1
Move.B D0,1(A2)
Move.B D1,2(A2)
Move.L A2,D2
Bsr PrintCLI
Lea UnitsColon,A2
MoveQ #UColonLen,D3
Move.B dtl_ElapsedJiffies(A6),D0
Bsr BinToBCD
Move.B D0,D1
Lsr.B #4,D0
And.B #$0F,D1
Add.B #'0',D0
Add.B #'0',D1
Move.B D0,1(A2)
Move.B D1,2(A2)
Move.L A2,D2
Bsr PrintCLI
.NextSkipTime MoveQ #0,D0
Move.B DTPlayDoneSig(A4),D0 ;Get play-done sig bit #.
MoveQ #0,D1 ;Clear event bits for this signal so I
SYS AddStatusSignal ;don't get signalled for UnloadModule().
SYS UnloadModule ;Unload current module.
; Now I can reaffirm my interest in unloaded modules and finished play sequences...
MoveQ #0,D0
Move.B DTPlayDoneSig(A4),D0 ;Get play-done sig bit # again.
Move.W #SEF_FINISH|SEF_MODUNLOADED,D1 ;Restore the
SYS AddStatusSignal ; event bits.
; Control is passed directly to this point if an error occurred while the song
; was being loaded...
.SkipSong MoveQ #0,D0
Move.B JukeVolume(A4),D0 ;Get jukebox volume setting.
SYS SetGlobalVolume ;Reset global volume.
AddQ.W #1,CurSongNum(A4) ;Increment song number.
Move.W CurSongNum(A4),D0
Cmp.W NumOfFiles(A4),D0 ;Played all songs?
Blt .PlayLoop ;Nope, go load next song.
; At this point, all songs have been played. It's time to go.
.Done MoveQ #0,D0 ;Return success code.
.Leave Rts ;Leave.
.Error MoveQ #-1,D0 ;Return error code.
Bra .Leave ;And leave.
;---------------------------------------------------------------
; LoadSong routine. Called by PlaySongs, this routine attempts to load up
; the next song in the song list. It also resets the WaitSignals variable
; for me, as well as a few other flags.
;
; Input: A5 = Ptr to song filename pointer array.
; CurSongNum(A4) = number of the song to load (index into ptr array).
;
; Output: D0 = 0 if song loaded successfully.
; 1 if song couldn't be loaded (so go on to the next song).
; -1 if the library has been locked (abort the jukebox).
;
; Trashes: D1,D2,D3,A0,A1,A2,A6
;
LoadSong Move.L #SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_F,D0 ;CTRL-C & CTRL_F.
Move.B DTPlayDoneSig(A4),D1 ;Always wait for play-done.
BSet D1,D0
Move.L D0,WaitSignals(A4) ;Store signal bits to wait for.
Clr.B FadingFlag(A4) ;Reset "Fade Song" flag.
Move.L DTBase(A4),A6 ;Get DES-Tracker base.
Move.W CurSongNum(A4),D0 ;Get current song number to load.
Add.W D0,D0 ;Make long index out of it.
Add.W D0,D0
Move.L 0(A5,D0.W),A0 ;Extract ptr to song's filename.
Move.L A0,A2 ;Save pointer in A2 for a bit.
Move.L #LoadingText,D2 ;Print "Loading "
Bsr PrintN2Text
Move.L A2,D2 ; followed by song filename
Bsr PrintN2Text
Move.L #XtraLoadText,D2 ; followed by carriage return.
Bsr PrintN2Text
Move.L A2,A0 ;Get filename ptr back in A0.
SYS LoadModule ;Attempt to load the module.
Tst.L D0 ;Error loading module?
Bpl .Done ;Nope, everything's peachy.
Cmp.W #DE_INSUFFICIENT_MEMORY,D0 ;Yup, not enough memory?
Bne .E1 ;Nope, that's not why.
Move.L #NoMemText,D2 ;Yes, say "Not enough mem".
Move.L #NoMemLen,D3
.PErr Bsr PrintCLI
Bra .Error ;And go on to the next song.
.E1 Cmp.W #DE_FILE_LOAD_ERROR,D0 ;Couldn't find the file?
Bne .E2 ;Nope, that's not why, either.
Move.L #NotFoundText,D2 ;Yup, say "File not found".
Move.L #NotFoundLen,D3
Bra .PErr ;And go on to the next song.
.E2 Cmp.W #DE_UNKNOWN_FORMAT,D0 ;Hmm... unknown module format?
Bne .E3 ;Damn, that's not it either.
Move.L #BadFormatText,D2 ;Yup, say "Crappy file format".
Move.L #BadFormatLen,D3
Bra .PErr ;And go on to the next song.
.E3 Move.L #LibOwnedText,D2 ;Must be library locked.
Bsr PrintNicely ;So say so.
Bra .Error ;If lib is locked, I must abort.
.Done MoveQ #0,D0 ;Song loaded OK.
.Leave Rts ;Back we go.
.Error LOADLIB SYS ;SYS_Base in A6.
Move.L TaskPointer(A4),A0 ;If song didn't load
Move.L #SIGBREAKF_CTRL_F,D0 ; then clear any CTRL-F signal
Not.L D0 ; I may have received so that
SYS Forbid ; I don't wind up skipping the
And.L TC_SIGRECVD(A0),D0 ; *next* song I load.
Move.L D0,TC_SIGRECVD(A0) ;Store updated signal bits.
SYS Permit ;Allow multi-tasking once again.
MoveQ #1,D0 ;Couldn't load song. Go to next one.
Bra .Leave
;---------------------------------------------------------------
; Random routine. I originally got this algorithm from a Dr Dobbs journal,
; but the author's algorithm was called r235 (or some wierd number). Also,
; his sample code had a *VERY SERIOUS* flaw in it that I can't believe he
; didn't notice. I modified the algorithm to come up with the r256 algorithm
; which is more efficient than the r235 version because mine doesn't require
; bounds checking on the table index. Also, this particular version of r256
; has been streamlined since it only needs to return 16 rather than 32 bit
; values. The RandIndex variable always holds the index multiplied by 2 (since
; the table is made of up of word elements) so that I don't have to keep
; doubling it before I use it (that's why I add #103*2 instead of just #103).
;
; Input: Nothing, but RandTable must be initialized.
;
; Output: D0.W = A pseudo random number.
;
; Trashes: Nothing.
;
Random Move.W D1,-(Sp) ; Save D1.
Move.W RandIndex(A4),D0 ;Get current table word index.
Move.W RandTable(A4,D0.W),D1 ;D1 = RandTable[D0]
Add.W #103*2,D0 ;Add prime number to word index.
And.W #$1FF,D0 ;Keep index within table bounds.
Move.W D0,RandIndex(A4) ;Store updated index.
Eor.W D1,RandTable(A4,D0.W) ;Modify value at new index with D1.
Move.W RandTable(A4,D0.W),D0 ;Return modified value in D0.
Move.W (Sp)+,D1 ; Restore D1.
Rts ;And leave.
;---------------------------------------------------------------
; VerifyPath routine. Takes a string which might be either a pathname or a
; filespec, tries to get a lock on it, and determines whether the string
; is a pathname, filespec, or garbage. I save a lot of registers in this
; routine because I just don't trust AmigaDOS (who would?).
;
; Input: A0 = Pathname or filespec string (NULL-Terminated).
;
; Output: D0 = 0 if string is a filespec.
; >0 if string is a pathname.
; <0 if string is bloody nonsense.
;
; Trashes: Nothing.
;
VerifyPath MoveM.L D1-D7/A0-A3/A5/A6,-(Sp) ;Speed's not important here. Play it safe.
MoveQ #-1,D6 ;Assume user will type nonsense. :)
Move.L DosBase(A4),A6 ;Get DOS_Base.
Move.L A0,D1 ;Ptr to string in D1.
MoveQ #-2,D2 ;Read only mode for lock.
SYS Lock ;Attempt to get a lock.
Move.L D0,LockPointer(A4) ;Save ptr to the lock.
Beq .Leave ;Couldn't get a lock. Error.
Move.L D0,D7 ;Cache lock pointer in D7.
Move.L D7,D1 ;Put it back in D1.
Move.L MyFileInfoBlock(A4),A0 ;Get ptr to FileInfoBlock.
Move.L A0,D2 ;DOS wants it in D2.
SYS Examine ;Fill out file info block.
Tst.L D0 ;Was the path/file found?
Beq .Leave ;Apparently not.
Move.L MyFileInfoBlock(A4),A0 ;Yes, get file info block again.
Tst.W fib_DirEntryType(A0) ;Check dir entry type.
Bmi .IsFile ;Negative means it's a file.
AddQ.L #1,D6 ;It's a path, inc D6.
.IsFile AddQ.L #1,D6 ;It's a file, inc D6.
.Leave Move.L D6,D0 ;Return status in D0.
MoveM.L (Sp)+,D1-D7/A0-A3/A5/A6 ;Restore regs.
Rts ;And leave.
;---------------------------------------------------------------
; AllocFNameBuffer routine. This routine simply allocates a buffer of a
; default initial size which is used for storing filenames of songs.
;
; Input: None.
;
; Output: D0 = 0 if allocation was successful.
; -1 if allocation failed.
;
; Trashes: D1,A0,A1,A6
;
DEF_BUF_SIZE = 512 ;initial buffer size.
AllocFNameBuffer
LOADLIB SYS ;Get SYS_Base.
Move.L #DEF_BUF_SIZE,D0 ;Buffer size in D0.
Move.L #MEMF_CLEAR,D1 ;Clear mem for me, please.
SYS AllocMem ;Attempt to allcate memory.
Move.L D0,FilenameBufAddr(A4) ;Save ptr to buffer memory.
Beq .Error ;If 0, then allocation failed.
Move.L #DEF_BUF_SIZE,FilenameBufLen(A4) ;Remember buffer size.
Move.L #0,FilenameBufIndex(A4) ;Initialize buffer index.
MoveQ #0,D0 ;Return with success code.
.Leave Rts ;Leave.
.Error MoveQ #-1,D0 ;Return with error code.
Bra .Leave ;Leave.
;---------------------------------------------------------------
; AddFNameToBuffer routine. This routine copies the specified NULL-terminated
; string into the filenames buffer. If there is not enough room in the buffer
; to hold the filename, then a larger buffer is created and the old buffer
; is copied over to the new buffer after which the old buffer is killed.
;
; Input: A3 = Pointer to NULL-terminated filename to add to buffer.
;
; Output: D0 = 0 if filename was successfully added to the buffer.
; -1 if an error occurred while trying to create a larger buffer.
;
; Trashes: D1,D5,A0,A1,A3,A6
;
BUF_INC_SIZE = 256 ;Amount to enlarge buffer by if filename won't fit in it.
AddFNameToBuffer
LOADLIB SYS ;Get SYS_Base.
Move.L A3,A1 ;Count length of filename first.
MoveQ #0,D0 ;D0 will hold length of filename.
.LenLoop AddQ #1,D0 ; (length includes NULL).
Tst.B (A1)+
Bne .LenLoop
;See if this filename will fit in buffer...
Move.L FilenameBufLen(A4),D1 ;Get length of filenames buffer.
Sub.L FilenameBufIndex(A4),D1 ;Subtract current buffer index.
Cmp.L D1,D0 ;Will filename fit?
Blt .StoreFilename ;Yup, go copy it in.
; Filename doesn't fit in the buffer, so I must enlarge the buffer. The
; filenames buffer is enlarged by BUF_INC_SIZE bytes at a time...
Move.L FilenameBufLen(A4),D5 ;Get length of "old" buffer.
Add.L #BUF_INC_SIZE,D5 ;Calculate new length.
Move.L D5,D0 ;New length in D0.
Move.L #MEMF_CLEAR,D1 ;Clear mem for me, please.
SYS AllocMem ;Attempt to allocate new buffer.
Tst.L D0 ;Error?
Beq .Error ;Yup, allocate failed.
; Now copy the contents of the old buffer to the new buffer...
Move.L FilenameBufAddr(A4),A0 ;A0 pts to old buffer.
Move.L D0,A1 ;A1 pts to new buffer.
Move.L FilenameBufLen(A4),D1 ;D1 = length of old buffer.
.CopyBuf Move.B (A0)+,(A1)+ ;Copy a byte from old to new.
SubQ.L #1,D1
Bgt .CopyBuf ;Loop D1 times.
; Now deallocate the old buffer...
Move.L D0,-(Sp) ;Save ptr to new buffer.
Move.L FilenameBufAddr(A4),A1 ;A1 = ptr to old buffer.
Move.L FilenameBufLen(A4),D0 ;D0 = Length of old buffer.
SYS FreeMem ;Free up the old buffer memory.
Move.L (Sp)+,D0 ;Restore new buffer ptr.
; Now update variables for the new buffer...
Move.L D0,FilenameBufAddr(A4) ;Store ptr to new buffer.
Move.L D5,FilenameBufLen(A4) ;Store length of new buffer.
; Now copy the filename to the filenames buffer...
.StoreFilename Move.L FilenameBufAddr(A4),A0 ;A0 = ptr to filenames buffer.
Move.L FilenameBufIndex(A4),D0 ;D0 = buffer index.
.CopyNameLoop Move.B (A3)+,0(A0,D0.L) ;Copy byte into buffer.
Beq .CopyDone ;Stop copying when NULL copied.
AddQ.L #1,D0 ;Inc buffer index.
Bra .CopyNameLoop ;Copy rest of string into buffer.
.CopyDone AddQ.L #1,D0 ;Inc index for NULL.
Move.L D0,FilenameBufIndex(A4) ;Store updated buffer index.
MoveQ #0,D0 ;Return with success code.
.Leave Rts
.Error MoveQ #-1,D0 ;Return with error code.
Bra .Leave
;---------------------------------------------------------------
; GetFileSpec routine. Extracts a file specification from the command line
; args. Stores the extracted filespec in FilenameString[].
;
; Input: A5 = pointer to command line argstring.
;
; Output: FilenameString[] holds the extracted filespec.
; A5 pts to new current position in argstring.
; D0 = 0 if FilenameString[] holds a valid string.
; -1 if no filespec was specified in the argstring.
;
; Trashes: A0
;
GetFilespec Lea FilenameString(A4),A0 ;A0 = ptr to dest string buffer.
St ShuffleFlag(A4) ;Assume I'll be shuffling songs.
.Skip Move.B (A5),D0 ;Get char from argstring.
Cmp.B #DQUOTE,D0 ;Is it a double quote?
Beq.S .DoQuote ;Yup, do special quote handling.
Cmp.B #"?",D0 ;Is it a ?
Beq .NoFileSpec ;Yup, treat as if no filespec specified.
Cmp.B #"-",D0 ;Is it a command prefix character?
Beq .ProcessCmd ;Yup. Go process command.
Cmp.B #" ",D0 ;Is it a space character?
Bgt .Copy ;No, ASCII value is greater than space char.
Cmp.B #TAB,D0 ;Is it a tab?
Beq .Skip ;Yup, ignore it.
Cmp.B #LF,D0 ;Have I reached the end of the argstring?
Beq .NoFileSpec ;Yup, no filespec was specified.
AddQ #1,A5 ;No, so ignore char and get another.
Bra .Skip
.DoQuote AddQ #1,A5 ;Skip past the double quote character.
Move.B (A5),D0 ;Get char from argstring.
Cmp.B #LF,D0 ;Reached EOL yet?
Beq .Done ;Yup, no close quote, but that's OK.
Cmp.B #DQUOTE,D0 ;Is it a (close) quote?
Beq .EndQuote ;Yup, fix up argstring ptr and leave.
Move.B D0,(A0)+ ;Nope, extract this character.
Bra .DoQuote ;And go get another.
.EndQuote AddQ #1,A5 ;Skip past the close quote.
Bra .Done ;And leave.
.Copy Move.B (A5)+,(A0)+ ;Copy a character from argstring into buffer.
Cmp.B #" ",(A5) ;Is next char a space?
Beq.S .Done ;Yup, NULL-terminate buffer and leave.
Cmp.B #TAB,(A5) ;Is next char a tab?
Beq.S .Done ;Yup, all done.
Cmp.B #LF,(A5) ;Nope, reached EOL yet?
Bne.S .Copy ;Nope, go copy next character.
.Done Clr.B (A0) ;NULL-terminate string in buffer.
MoveQ #0,D0 ;Indicate valid string in buffer.
.Leave Rts ;And leave.
.NoFileSpec MoveQ #-1,D0 ;Indicate no filespec was given.
Bra .Leave ;and leave.
.ProcessCmd AddQ #1,A5 ;Skip command prefix character.
Move.B (A5)+,D0 ;Get command character.
Or.B #$20,D0 ;Make sure char is lower-case.
Cmp.B #"n",D0 ;"No Shuffle" command?
Bne .Skip ;Nope. "-n" is the only command.
Clr.B ShuffleFlag(A4) ;Yes, do not shuffle selections.
Bra .Skip ;Go try to find a filespec.
;---------------------------------------------------------------
; Initialize routine. Opens necessary libraries, initializes variables and
; tables, etc.
;
; Input: A4 = Pointer to allocated program variable space.
;
; Output: D0 = 0 if initialization was successful.
; -1 if an error occurred.
;
; Trashes: D1,D2,D3,A0,A1,A2,A6
;
Initialize
; Create Random number table for r256 pseudo-random generator. The table is
; filled with pseudo-random values using a less than efficient (and slow)
; algorithm. I use the current raster beam coordinates as a pseudo-random seed
; for this bit of code. The pseudo-random number generator routine that this
; initialization is for is much faster than this rather brutal loop...
Lea RandTable(A4),A0 ;Fetch ptr to prime-index table.
Move.W CHIP+vhposr,D1 ;Prep seed (D1) with cur. raster pos.
Move.L #256-1,D0 ;For all table elements...
Add.W #33247,D1 ;Add something prime to initial seed.
.MakeRand MulU #61141,D1 ;Multiply by a prime #.
Swap D1 ;Use low word of result.
Add.W #33247,D1 ;Add a prime number to low word.
Move.W D1,(A0)+ ;And store result in table.
DBra D0,.MakeRand ;Go build another table element.
; Now open necessary libraries...
Lea DosName,A1 ;Open dos.library.
MoveQ.L #0,D0
SYS OpenLibrary
Move.L D0,DosBase(A4)
Beq .Error
.1 Move.L D0,A6 ;Dos_Base in A6.
SYS Output ;Get standard output handle.
Move.L D0,OutputHandle(A4) ;Save bptr to it.
Move.L #HelloText,D2 ;Say hello!
Bsr PrintNicely ;Nicely 'cause you can pause it. :)
LOADLIB SYS ;SYS_Base in A6.
Lea DTLName,A1 ;Get ptr to "destracker.library"
MoveQ #3,D0 ;Library version 3.0 minimum.
SYS OpenLibrary ;Open DES-Tracker library.
Move.L D0,DTBase(A4) ;Save ptr to it.
Bne .MakeInfoBlock
Move.L #NoLibOpenText,D2 ;Couldn't open DES-Tracker.
.PrintGo Bsr PrintNicely
Bra .Error ;Oh, puh-shaw.
; Allocate a file info block structure...
.MakeInfoBlock LOADLIB SYS ;SYS_Base in A6.
Move.L #fib_SIZEOF,D0 ;Size of the structure.
Move.L #MEMF_CLEAR,D1 ;Clear it out for me, please.
SYS AllocMem ;Attempt to allocate.
Move.L D0,MyFileInfoBlock(A4) ;Did it work?
Bne .GetDTSigBits ;Yup, continue.
Move.L #InsuffMemText,D2 ;Nope, insufficient memory.
Bra .PrintGo ;Tell user and abort.
; Allocate signal bits I'll need to use with DES-Tracker...
.GetDTSigBits MoveQ #-1,D0 ;Give me any available signal bit.
Move.B D0,DTPlayDoneSig(A4)
Move.B D0,DTIterDoneSig(A4)
SYS AllocSignal ;Allocate a signal bit.
Move.B D0,DTPlayDoneSig(A4) ;Store number of the bit.
Bpl .2 ;I got a bit number.
.NoSig Move.L #NoDTSigText,D2 ;Couldn't allocate signal bit.
Bra .PrintGo ;Print error and abort.
.2 MoveQ #-1,D0 ;Allocate another signal bit.
SYS AllocSignal
Move.B D0,DTIterDoneSig(A4) ;Store its bit number.
Bmi .NoSig ;Didn't get a bit. Damn.
Move.L DTBase(A4),A6 ;DES-Tracker base in A6.
SYS UnloadModule ;Unload any loaded module.
Sub.L A0,A0 ;Clear any existing search paths.
SYS SetModulePath
MoveQ #0,D0 ;Now grab current global volume
Move.B dtl_GlobalVolume(A6),JukeVolume(A4) ;and save it.
MoveQ #10,D0 ;Set volume fade rate to
SYS SetVolumeFadeRate ; nice and slow.
LOADLIB SYS ;SYS_Base in A6.
Sub.L A0,A0 ;NULL in A0.
SYS FindTask ;Get pointer to my TCB structure.
Move.L D0,TaskPointer(A4) ;Save pointer to it.
MoveQ #0,D0 ;Indicate successful init.
.Leave Rts ;And leave.
.Error MoveQ #-1,D0 ;Indicate bad init.
Bra .Leave
;---------------------------------------------------------------
; DeInitialize routine. Frees up all used resources. The Initialize routine
; must have been previously called. :)
;
; Input: A4 = Pointer to allocated program variable space.
;
; Output: Nothing.
;
; Trashes: D0,D1,D2,D3,A0,A1,A2,A6
;
DeInitialize Move.L DosBase(A4),D0
Beq .1
Move.L D0,A6
Move.L LockPointer(A4),D1 ;Get Lock ptr in D1.
Beq .1 ;If 0.L then it doesn't exist.
SYS UnLock ;Get rid of the lock.
.1 Move.L DTBase(A4),D0 ;DES-Tracker base in D0.
Beq .2 ;If 0, then it didn't open.
Move.L D0,A6 ;It is open, put it in A6.
SYS UnloadModule ;Unload current module.
SYS ResetDefaults ;Reset library defaults.
Move.L A6,A1 ;Put library base in A1.
LOADLIB SYS ;Get SYS_Base in A6.
SYS CloseLibrary ;Close DES-Tracker library.
.2 LOADLIB SYS ;Get SYS_Base in A6.
MoveQ #0,D0
Move.B DTPlayDoneSig(A4),D0 ;Get signal bit I allocated.
Bmi .3 ;The allocation had failed.
SYS FreeSignal ;Free up the signal.
.3 MoveQ #0,D0
Move.B DTIterDoneSig(A4),D0 ;Get other signal bit.
Bmi .4 ;It didn't allocate.
SYS FreeSignal ;Free up the signal.
.4 Move.L MyFileInfoBlock(A4),D0 ;Get ptr to file info block struct.
Beq .5 ;It didn't get alloc'ed.
Move.L D0,A1
Move.L #fib_SIZEOF,D0 ;Size of the structure.
SYS FreeMem ;Free it.
.5 Move.L FilenameBufAddr(A4),D0 ;Get ptr to filename buffer mem.
Beq .6 ;If 0, it doesn't exist.
Move.L D0,A1 ;It exists.
Move.L FilenameBufLen(A4),D0 ;Get length of the buffer.
SYS FreeMem ;Free up the buffer memory.
.6 Move.L PtrTableAddr(A4),D0 ;Get addr of ptr table mem.
Beq .7 ;If 0, then it doesn't exist.
Move.L D0,A1 ;It exists.
Move.L PtrTableLen(A4),D0 ;Get its length.
SYS FreeMem ;Free up table memory.
.7 Move.L DosBase(A4),D0 ;Get pointer to DOS_Base.
Beq .8 ;It didn't open (?)
Move.L D0,A1 ;It is open,
SYS CloseLibrary ; so close it.
.8 Move.L A4,A1 ;Release program variable space.
Move.L #MJUKE_SIZE,D0
LOADLIB SYS
SYS FreeMem ;No more program variables.
Rts ;All done.
;---------------------------------------------------------------
; BinToBCD routine. Converts a binary value (0..99) to a packed BCD value.
;
; Input: D0 = Binary value to convert.
;
; Output: D0.B = The value as a packed binary-coded decimal.
;
BinToBCD MoveQ #0,D1
Move.B D0,D1
Move.L D1,D0
DivU #10,D0
Asl.B #4,D0
Move.B D0,D1
Swap D0
Or.B D1,D0
Rts
;---------------------------------------------------------------
; PrintCLI routine. Prints a text string to the standard output.
;
; Input: D2 = Pointer to the text string to print (NOT NULL-terminated).
; D3 = Length of the text string.
;
; Output: Nothing.
;
; Trashes: D0,D1,D2,D3,A0,A1,A2
;
PrintCLI Move.L A6,-(Sp)
Move.L OutputHandle(A4),D1
Move.L DosBase(A4),A6
SYS Write
Move.L (Sp)+,A6
Rts
;---------------------------------------------------------------
; PrintNText routine. Prints a NULL-terminated string to the standard output.
; Automatically appends a linefeed character to the end of the text output.
;
; Input: D2 = Pointer to the NULL-terminated text string to print.
;
; Output: Nothing.
;
; Trashes: D0,D1,D2,D3,A0,A1,A2
;
PrintNText Bsr PrintN2Text
Move.L #LineFeed,D2
MoveQ #1,D3
Bsr PrintCLI
Rts
;---------------------------------------------------------------
; PrintN2Text routine. Prints a NULL-terminated string to the standard output,
; just like PrintNText above, but does not output a linefeed at the end.
;
; Input: D2 = Pointer to the NULL-terminated text string to print.
;
; Output: Nothing.
;
; Trashes: D0,D1,D2,D3,A0,A1,A2
;
PrintN2Text MoveQ #0,D3
Move.L D2,A0
.1 AddQ #1,D3
Tst.B (A0)+
Bne .1
SubQ #1,D3
Bsr PrintCLI
Rts
;---------------------------------------------------------------
; PrintNicely routine. Prints a NULL-Terminated text string one line at a time
; (using the PrintCLI routine above) so that the output can be paused by
; pressing a key. It is important to remember that the text string passed to
; this routine MUST have a linefeed character immediately preceding the
; terminating NULL.
;
; Input: D2 = Pointer to the text string to print (LF & NULL-terminated).
;
; Output: Nothing.
;
; Trashes: D0,D1,D2,D3,A0,A1,A2
;
PrintNicely MoveM.L A3/A5,-(Sp)
Move.L D2,A5
.NextLine Move.L A5,A3
MoveQ #0,D3
.CountChars AddQ #1,D3
Move.B (A3)+,D0
Beq .Done
Cmp.B #LF,D0
Bne .CountChars
Move.L A5,D2
Move.L A3,A5
Bsr PrintCLI
Bra .NextLine
.Done MoveM.L (Sp)+,A3/A5
Rts
;===============================================================
; DATA segment. Consists of static text strings...
DATA
Even
; Run-time messages and substrings...
CFilesText Dc.B "Collecting filenames...",ESC,CEOL,NULL
LoadingText Dc.B LF,'Loading "',NULL
PlayingText Dc.B CR,'Playing "',NULL
XtraLoadText Dc.B '"',ESC,CEOL,NULL
XtraPlayText Dc.B '"',ESC,CEOL,NULL
LineFeed Dc.B LF,NULL
Spaces Dc.B " "
PlayTimeText Dc.B "Play Time: ",NULL
PlayTimeLen = *-PlayTimeText
UnitsColon Dc.B ":"
UnitsNoColon Dc.B "00"
UColonLen = *-UnitsColon
UNoColonLen = *-UnitsNoColon
; Module load error messages...
NoMemText Dc.B " -- Insufficient memory!",NULL
NoMemLen = *-NoMemText
NotFoundText Dc.B " -- File not found!",NULL
NotFoundLen = *-NotFoundText
BadFormatText Dc.B " -- Unrecognized module format!",NULL
BadFormatLen = *-BadFormatText
; Exit error messages...
JukeDoneText Dc.B LF,"MicroJuke is finished!",LF,LF,NULL
JukeAbortText Dc.B LF,"*** Jukebox aborted. ***",LF,LF,NULL
LibOwnedText Dc.B LF,"Library has been locked! Aborting ...",LF,LF,NULL
; Parsing error messages...
BadPathText Dc.B LF,"ERROR: Illegal path/file specification",LF,LF,NULL
NotScriptText Dc.B LF,"ERROR: Not a script file.",LF,LF,NULL
NoDTSigText Dc.B LF,"ERROR: Couldn't allocate signal bit!",LF,LF,NULL
InsuffMemText Dc.B LF,"ERROR: Insufficient memory",LF,LF,NULL
NoFilesText Dc.B LF,"ERROR: Path is empty",LF,LF,NULL
ReadErrText Dc.B LF,"ERROR: Couldn't read file",LF,LF,NULL
NoLibOpenText Dc.B LF,"ERROR: You need destracker.library v3.0",LF,LF,NULL
; Title, copyright, and usage text strings...
HelloText Dc.B LF,"destracker.library Micro-Jukebox",LF
Dc.B "Copyright (c)1993 by Darren Schebek",LF
Dc.B "Version 2.0 Dec 18, 1993",LF,LF,NULL
ComText Dc.B "usage: mjuke [-n] {pathname|scriptname}",LF,LF
Dc.B "where: -n = Do not shuffle selections.",LF,LF
IPlayText Dc.B $0D,"CTRL-C aborts jukebox.",LF
Dc.B "CTRL-F skips to next song.",LF,LF,NULL
; Library name strings...
DosName Dc.B "dos.library",0
DTLName DTLIBNAME
Even
End