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 >
Text File  |  1993-12-17  |  54KB  |  1,624 lines

  1. ;;=====================================================================;;
  2. ;;                 mJuke.S  -  Begun Wed, Jan 02, 1993                 ;;
  3. ;;                   Last Updated: Sat, Dec 18, 1993                   ;;
  4. ;;---------------------------------------------------------------------;;
  5. ;;            This is version 2.0 of the Micro-Jukebox for             ;;
  6. ;;           destracker.library release 2.0+ (lib ver 3.0+)            ;;
  7. ;;---------------------------------------------------------------------;;
  8. ;;              Designed and written by Darren E. Schebek              ;;
  9. ;;                Copyright ⌐1993  by Darren E. Schebek                ;;
  10. ;;                         All rights reserved.                        ;;
  11. ;;                This source code is NOT public domain!               ;;
  12. ;;=====================================================================;;
  13.  
  14.         INCLUDE    "dp:system.gs"        ;DevPac-specific global include.
  15.         INCLUDE    "hardware/custom.i"    ;Something DevPac forgot about.
  16.         INCLUDE    "DTLib.I"        ;DES-Tracker include file.
  17.  
  18. ;---------------------------------------------------------------
  19. ; System equates...
  20.  
  21. _SYSBase    = 4        ;Pointer to exec library base.
  22.  
  23. CHIP        = $DFF000    ;Pointer to custom chip registers.
  24.  
  25. ;---------------------------------------------------------------
  26. ; Control character equates used in text strings and in string parsing...
  27.  
  28. NULL    = 0    ;NULL
  29. TAB    = $09    ;TAB
  30. LF    = $0A    ;LineFeed
  31. CR    = $0D    ;Carriage Return
  32. ESC    = $9B    ;Control sequence introducer
  33. CEOL    = $4B    ;Clear to end of line
  34.  
  35. DQUOTE    = $22    ;Double quote
  36.  
  37. ;---------------------------------------------------------------
  38. ; Some custom macros of my own...
  39.  
  40. LOADLIB    MACRO
  41.      Move.L    _\1Base,A6
  42.     ENDM
  43.  
  44. SYS    MACRO
  45.      Jsr    _LVO\1(A6)
  46.     ENDM
  47.  
  48. BYTETAB    MACRO
  49. \1     EQU    SOFFSET
  50. SOFFSET     SET    SOFFSET+\2
  51.     ENDM
  52.  
  53. WORDTAB    MACRO
  54. \1     EQU    SOFFSET
  55. SOFFSET     SET    SOFFSET+(\2*2)
  56.     ENDM
  57.  
  58. ;---------------------------------------------------------------
  59. ; Variables used by the mJuke program...
  60.  
  61.     STRUCTURE MJUKE,0
  62.  
  63.     WORDTAB    RandTable,256    ;Prime-index table used by random number routine.
  64.     UWORD    RandIndex    ;Used by my random number routine. (r-256 alg.)
  65.  
  66.     UBYTE    DTPlayDoneSig    ;Signal bit DTLib uses to signal me.
  67.     UBYTE    DTIterDoneSig    ;Another signal bit used with destracker.
  68.     UBYTE    FadingFlag    ;Set when a song is meant to fade out at end.
  69.     UBYTE    PathnameFlag    ;Used with scripts to tell paths and files apart.
  70.     UBYTE    JukeVolume    ;Holds current global playback volume.
  71.     UBYTE    ShuffleFlag    ;1 = Shuffle filenames, 0 = don't even think about it.
  72.     UBYTE    NumTimeSpaces    ;# of spaces to print before printing elapsed time.
  73.     UBYTE    Pad
  74.  
  75.     UWORD    CurSongNum    ;Keeps track of which song I'm playing in list.
  76.     UWORD    NumIterations    ;Holds calc'ed # of iterations to play song for.
  77.  
  78.     ULONG    WaitSignals    ;Holds signal bit #'s I wait for while playing.
  79.  
  80.     APTR    ScriptAddr    ;Temporary ptr used for script processing.
  81.     APTR    MyFileHandle    ;Temporary ptr used for loading script file.
  82.     ULONG    FileSize    ;Temporary. Holds size in bytes of script file.
  83.     UWORD    NumOfFiles    ;Holds number of song files in the play list.
  84.     APTR    PtrTableAddr    ;Points to array of pointers to song filenames.
  85.     ULONG    PtrTableLen    ;Holds length of the ptr-array in bytes.
  86.     APTR    FilenameBufAddr    ;Ptr to buffer containing all song filenames.
  87.     ULONG    FilenameBufLen    ;Holds length of filename buffer in bytes.
  88.     ULONG    FilenameBufIndex ;Used for appending new song filenames to buffer.
  89.  
  90.     APTR    TaskPointer    ;Pointer to mJuke's own TCB structure.
  91.     BPTR    LockPointer    ;Holds DOS Lock required for Examine() function.
  92.     BPTR    OutputHandle    ;Holds BPTR to standard output for text output.
  93.     APTR    MyFileInfoBlock    ;Ptr to file info block struct required by DOS.
  94.     APTR    DosBase        ;Pointer to DOS library base structure.
  95.     APTR    DTBase        ;Pointer to DES-Tracker library base structure.
  96.  
  97.     BYTETAB    FilenameString,256 ;Used to construct file/path specifications.
  98.  
  99.     LABEL    MJUKE_SIZE    ;Size of the memory area required for variables.
  100.  
  101. ;---------------------------------------------------------------
  102. ; In the beginning, there was "Main". :)  I start out by allocating
  103. ; a block of memory to be used for all of this program's variables.
  104. ; This is much more elegant than simply throwing all the variables
  105. ; into the block storage segment...
  106.  
  107. Main        Move.L    A0,A5        ;Cache CLI argstring pointer in A5.
  108.  
  109. ; Allocate space for program variables...
  110.  
  111.         LOADLIB    SYS        ;Need SYS_Base in A6.
  112.         Move.L    #MJUKE_SIZE,D0    ;Size of the variable space I need.
  113.         Move.L    #MEMF_CLEAR,D1    ;Initialize everything to 0 for me. :)
  114.         SYS    AllocMem    ;Attempt to allocate the mem.
  115.         Tst.L    D0        ;Allocate error?
  116.         Ble    .ProgAbort    ;Yup, leave immediately.
  117.         Move.L    D0,A4        ;A4 is now sacred (pts to variable space).
  118.  
  119.         Bsr    Initialize    ;Initialize everything.
  120.         Tst.L    D0        ;Error during initialization?
  121.         Bne    .Abort        ;Yup, abort with error.
  122.  
  123. ;-------------------------------
  124. ; Static initialization and setup is now complete, now parse the command line
  125. ; argstring, build the list of song filenames to load and play, shuffle them
  126. ; randomly, and then play the bleedin' songs...
  127.  
  128.         Bsr    ProcessCmdLine    ;Parse cmd-line args, collect filenames.
  129.         Tst.L    D0        ;Did an error occur?
  130.         Bne    .Error        ;Yup.
  131.  
  132.         Bsr    MakePtrTable    ;Build and shuffle filename ptr array.
  133.         Tst.L    D0        ;Error?
  134.         Bne    .Error        ;Yuppers. So abort.
  135.  
  136.         Tst.B    ShuffleFlag(A4)    ;Should I shuffle the filenames?
  137.         Beq    .NoShuffle    ;Nope.
  138.         Bsr    ShuffleSongs    ;Randomize filenames in pointer array.
  139. .NoShuffle
  140.         Bsr    PlaySongs    ;No problems, so go play the songs.
  141.         Tst.L    D0        ;Error?
  142.         Bne    .Error        ;Yes, someone must have locked the library.
  143.  
  144.         Move.L    #JukeDoneText,D2 ;Say "Hasta la vista, baby!"
  145.         Bsr    PrintNicely    ;(or something along those lines).
  146.  
  147.         MoveQ    #0,D7        ;Indicate success upon exit.
  148.  
  149. ;-------------------------------
  150. ; Deallocate everything and leave.  Note that I don't bother calling
  151. ; RemStatusSignal for the two signal bits I use with DES-Tracker, since
  152. ; DES-Tracker will remove them for me when I close the library.
  153.  
  154. .Abort        Bsr    DeInitialize    ;Free up used resources.
  155.  
  156.         Move.L    D7,D0        ;Return exit code in D0.
  157.  
  158. .Leave        Rts            ;Byeeee!
  159.  
  160.  
  161. .Error        MoveQ    #10,D7        ;Set error exit flag & bail out.
  162.         Bra    .Abort
  163.  
  164. .ProgAbort    MoveQ    #10,D7        ;Exit with error code in D0.
  165.         Bra    .Leave
  166.  
  167. ;---------------------------------------------------------------
  168. ; ProcessCmdLine routine.  Parses the command line, determines whether or not
  169. ; the argument is a pathname or a filename, and calls either ReadPath or
  170. ; ReadScript as a result.
  171. ;
  172. ;   Input: A5 =    Ptr to the command line argstring.
  173. ;
  174. ;  Output: D0 =    0  if song file list was created successfully.
  175. ;        -1 if an error occurred along the way.
  176. ;
  177. ; Trashes: D1,D2,D3,A0,A1,A2,A5,A6
  178. ;
  179.  
  180. ProcessCmdLine    Bsr    GetFilespec    ;Get filespec/pathspec from argstring.
  181.         Tst.W    D0        ;Was there a file/path spec?
  182.         Bne    .ShowUsage    ;No, just print out usage text.
  183.  
  184.         Lea    FilenameString(A4),A0    ;Yes, get ptr to file/path spec.
  185.         Bsr    VerifyPath        ;See whether it's a path or file.
  186.  
  187.         Tst.L    D0        ;It's neither a path nor a file.
  188.         Bmi    .BadPath    ;(must be gibberish from the abuser).
  189.  
  190.         Move.L    D0,-(Sp)    ;   Save file/path status temporarily.
  191.  
  192.         Move.L    #CFilesText,D2    ;Say "Collecting filenames..."
  193.         Bsr    PrintN2Text
  194.  
  195.         Move.L    (Sp)+,D0    ;   Restore file/path status.
  196.         Beq    .IsFile        ;D0 = 0 means it's a filespec.
  197.  
  198. ; User specified a pathname, add it to DES-Tracker's list of search paths...
  199.  
  200.         Move.L    DTBase(A4),A6    ;Get DES-Tracker base ptr.
  201.         Sub.L    A0,A0        ;Pass NULL to clear all existing paths.
  202.         SYS    SetModulePath    ;Do it.
  203.  
  204.         Lea    FilenameString(A4),A0    ;Get ptr to pathspec string.
  205.         SYS    SetModulePath    ;Add pathspec to search path list.
  206.  
  207.         Bsr    ReadPath    ;Read in all the filenames.
  208.         Bra    .Done        ;And leave. :)
  209.  
  210.  
  211. ; User specified a file, so first check to make sure it's a script...
  212.  
  213. .IsFile        Move.L    MyFileInfoBlock(A4),A0    ;Info Block filled in by DOS.
  214.         Move.L    fib_Protection(A0),D0    ;Get file's protection bits.
  215.         BTst    #FIBB_SCRIPT,D0        ;Is script bit set?
  216.         Bne    .IsScript        ;Yup, go read it in.
  217.  
  218.         Move.L    #NotScriptText,D2    ;Nope, tell user about it.
  219.         Bsr    PrintNicely
  220.         Bra    .Error            ;And abort.
  221.  
  222. .IsScript    Bsr    ReadScript        ;Read script, record song filenames.
  223.         
  224. .Done        Move.L    D0,-(Sp)        ;   Save return code temporarily.
  225.         Move.L    #IPlayText,D2        ;Say "Press CTRL-C to abort..."
  226.         Bsr    PrintNicely
  227.         Move.L    (Sp)+,D0        ;   Restore return code in D0.
  228.  
  229. .Leave        Rts                ;All done.
  230.  
  231.  
  232. .ShowUsage    Move.L    #ComText,D2        ;No parms specified, show usage.
  233.         Bsr    PrintNicely
  234.         Bra    .Error            ;And abort.
  235.  
  236.  
  237. .BadPath    Move.L    #ComText,D2        ;User not clear on concept,
  238.         Bsr    PrintNicely        ; so show usage text
  239.  
  240.         Move.L    #BadPathText,D2        ; and then say "Illegal path...".
  241.         Bsr    PrintNicely
  242.  
  243. .Error        MoveQ    #-1,D0            ;D0 = error return code.
  244.         Bra    .Leave            ;Leave.
  245.  
  246. ;---------------------------------------------------------------
  247. ; ReadScript routine.  Reads in the specified script file (if possible) and
  248. ; parses all the song filenames, storing them into the filename buffer.  Any
  249. ; lines in the script that start with a "#" character are assumed to have a
  250. ; search pathname immediately following the "#".  Search paths are added to
  251. ; the DES-Tracker library's list of search paths.
  252. ;
  253. ;   Input: FilenameString(A4) must hold the complete filespec of the script file.
  254. ;
  255. ;  Output: D0 =    0  if script file parsed successfully.
  256. ;        -1 if an error occurred while parsing the file.
  257. ;
  258. ; Trashes: D1,D2,D3,D6,A0,A1,A2,A6
  259. ;
  260.  
  261. ReadScript    Clr.W    NumOfFiles(A4)        ;Initialize song count.
  262.  
  263.         Bsr    AllocFNameBuffer    ;Allocate an initial filename buffer.
  264.         Tst.L    D0            ;Error?
  265.         Bmi    .InsuffMem        ;Yup, so abort.
  266.  
  267. ; Now open the script file...
  268.  
  269.         Lea    FilenameString(A4),A0    ;Pointer to the script filename.
  270.         Move.L    A0,D1            ; In D1.
  271.         Move.L    #MODE_OLDFILE,D2
  272.         Move.L    DosBase(A4),A6        ;Use DOS_Base.
  273.         SYS    Open            ;Attempt to open script file.
  274.         Move.L    D0,MyFileHandle(A4)    ;Store file handle ptr.
  275.         Beq    .Error            ;If 0, then file didn't open.
  276.  
  277. ; Script file opened, so first determine how big the file is so I can
  278. ; allocate enough space to read it in...
  279.  
  280.         Move.L    D0,D1            ;Need file handle in D1.
  281.         MoveQ    #0,D2
  282.         Move.L    #OFFSET_END,D3        ;Seek to end of file.
  283.         SYS    Seek
  284.         Move.L    MyFileHandle(A4),D1    ;Restore file handle in D1.
  285.         MoveQ    #0,D2
  286.         Move.L    #OFFSET_BEGINNING,D3    ;Seek back to beginning of file.
  287.         SYS    Seek
  288.         Move.L    D0,FileSize(A4)        ;D0 holds file size, save it.
  289.  
  290. ; Now allocate enough memory to read in the script...
  291.  
  292.         LOADLIB    SYS            ;Need SYS_Base to do it.
  293.         Move.L    #MEMF_CLEAR,D1        ;Clear the memory for me.
  294.         SYS    AllocMem        ;Attempt to allocate the mem.
  295.         Move.L    D0,ScriptAddr(A4)    ;Save ptr to script memory.
  296.         Beq    .Error            ;If 0, then allocate failed.
  297.  
  298. ; Now read in the script file...
  299.  
  300.         Move.L    DosBase(A4),A6        ;Get DOS_Base
  301.         Move.L    MyFileHandle(A4),D1    ;File handle in D1.
  302.         Move.L    D0,D2            ;Ptr to dest buffer in D2.
  303.         Move.L    FileSize(A4),D3        ;# of bytes to read in D3.
  304.         SYS    Read            ;Attempt to read the script file.
  305.         Tst.L    D0            ;Error?
  306.         Bgt    .Cont1            ;Nope.
  307.  
  308.         Move.L    #ReadErrText,D2        ;Yup, tell user about it.
  309.         Bsr    PrintNicely
  310.         Bra    .Error            ;And abort.
  311.  
  312. ; Now I must parse the script file in memory...
  313.  
  314. .Cont1        Move.L    FileSize(A4),D0        ;Get script file size.
  315.         Move.L    ScriptAddr(A4),A0    ;Pointer to script in A0.
  316.         Lea    0(A0,D0.L),A1        ;A1 points to end of script.
  317.  
  318. .ParseLoop    Bsr    ReadLine        ;Read in line from script.
  319.         Tst.L    D0            ;Reached end of file yet?
  320.         Bne    .Done            ;Yup.
  321.  
  322.         Lea    FilenameString(A4),A2    ;Nope, get ptr to the line.
  323.         Tst.B    PathnameFlag(A4)    ;Is it a filename or search path?
  324.         Beq    .StoreName        ;It's a filename, store in buffer.
  325.  
  326.         Move.L    DTBase(A4),A6        ;It's a search path.
  327.         MoveM.L    A0-A1,-(Sp)        ;   Save useful pointers.
  328.         Move.L    A2,A0            ;Ptr to path string in A0
  329.         SYS    SetModulePath        ;Add search path to library's list.
  330.         MoveM.L    (Sp)+,A0-A1        ;   Restore useful pointers.
  331.  
  332.         Bra    .ParseLoop        ;Extract & parse next line in script.
  333.  
  334. ; Here, a filename string has been returned by the ReadLine routine, so I
  335. ; must copy the filename into the filename buffer...
  336.  
  337. .StoreName    Lea    FilenameString(A4),A3    ;Ptr to filename string in A3.
  338.         MoveM.L    A0-A1,-(Sp)        ;   Save useful pointers.
  339.         Bsr    AddFNameToBuffer    ;Add filename string to buffer.
  340.         MoveM.L    (Sp)+,A0-A1        ;   Restore useful pointers.
  341.         Tst.L    D0            ;Error?
  342.         Bmi    .InsuffMem        ;Yup, report it and abort.
  343.  
  344.         AddQ.W    #1,NumOfFiles(A4)    ;Filename added, adjust file count.
  345.         Bra    .ParseLoop        ;Read & parse next line in file.
  346.  
  347. .Done        MoveQ    #0,D6            ;Indicate success in D6.
  348.  
  349. .Leave        Move.L    DosBase(A4),A6        ;Get DOS_Base.
  350.         Move.L    MyFileHandle(A4),D1    ;File handle in D1.
  351.         Beq    .1            ;If 0, then file didn't open.
  352.         SYS    Close            ;Close the script file.
  353.  
  354. .1        LOADLIB    SYS            ;Get SYS_Base.
  355.         Move.L    ScriptAddr(A4),D0    ;Get ptr to script file in memory.
  356.         Beq    .2            ;If ptr is 0, then allocate failed.
  357.         Move.L    D0,A1            ;It didn't fail.
  358.         Move.L    FileSize(A4),D0        ;Size of the memory block.
  359.         SYS    FreeMem            ;Free memory used by script file.
  360.  
  361. .2        Move.L    D6,D0            ;Return code copied to D0.            
  362.         Rts                ;Leave.
  363.  
  364. .Error        MoveQ    #-1,D6            ;Indicate error in D6.
  365.         Bra    .Leave            ;And abort.
  366.  
  367. .InsuffMem    Move.L    #InsuffMemText,D2    ;Report "Not enough memory".
  368.         Bsr    PrintNicely
  369.         Bra    .Error            ;And abort.
  370.  
  371. ;---------------------------------------------------------------
  372. ; ReadLine routine.  Extracts the next line from the script file in memory.
  373. ; This routine will automatically skip blank lines and will parse out
  374. ; comments and white space before returning a valid string.
  375. ;
  376. ; This routine also checks the first non-whitespace character of the line and,
  377. ; if the character is a "#" symbol, then it sets the PathnameFlag variable.
  378. ; This tells the calling routine that the string returned in FilenameString[]
  379. ; is a *pathname* and not a *song filename*.
  380. ;
  381. ;   Input: A0 = ptr to current position in script file in memory.
  382. ;
  383. ;  Output: LineBuffer(A4) holds ptr to buffer containing the parsed input line.
  384. ;
  385. ; Trashes: D0,D1,D2,A0,A1,A2
  386. ;
  387.  
  388. ReadLine    Lea    FilenameString(A4),A2    ;Ptr to dest string buffer in A2.
  389.         Move.L    A2,D1            ;Keep copy in D1.
  390.  
  391.         Bra    .NextLine        ;See if I've reached EOF yet.
  392.  
  393. .ReadLoop    Cmp.B    #";",(A0)        ;Check if 1st char is a ";".
  394.         Bne    .SkipWhite        ;It isn't.
  395.  
  396. .SkipLine    Cmp.B    #LF,(A0)+        ;It is, so skip entire line.
  397.         Bne    .SkipLine
  398.  
  399.         Bra    .NextLine        ;See if I've reached EOF.
  400.  
  401. ; Skip leading white space (if any)...
  402.  
  403. .SkipWhite    Move.B    (A0)+,D0    ;Get char from script.
  404.         Cmp.B    #" ",D0        ;Is it a space character?
  405.         Beq    .SkipWhite    ;Yup, parse it out.
  406.         Cmp.B    #TAB,D0        ;Is it a tab character?
  407.         Beq    .SkipWhite    ;Yup, parse it out.
  408.         Cmp.B    #LF,D0        ;Reached EOL yet?
  409.         Beq    .NextLine    ;Yup, line's blank, check for EOF.
  410.  
  411.         SubQ    #1,A0        ;Char is legit, back up to it.
  412.  
  413. .RLoop        Move.B    (A0)+,D0    ;Fetch next char from script.
  414.         Cmp.B    #LF,D0        ;Reached EOL yet?
  415.         Beq    .Parse        ;Yes, go parse out trailing white space.
  416.         Cmp.B    #"#",D0        ;Pathname prefix character?
  417.         Beq    .SetPath    ;Yes, go set flag indicating so.
  418.         Cmp.B    #";",D0        ;Is it a comment char?
  419.         Bne    .Store        ;Nope, so store char in dest string.
  420.  
  421. .SkipEOL    Cmp.B    #LF,(A0)+    ;Yes, so ignore remainder of this line.
  422.         Bne    .SkipEOL
  423.         Bra    .Parse        ;Go parse what I got from this line.
  424.  
  425. .Store        Move.B    D0,(A2)+    ;Store char in filename string.
  426.  
  427.         Cmp.L    A1,A0        ;Reached EOF yet?
  428.         Blt    .RLoop        ;Nope, go fetch another character.
  429.  
  430.         Bra    .Parse        ;Yup, go parse what I've got so far.
  431.  
  432. .SetPath    St    PathnameFlag(A4) ;Indicate that this is a pathname.
  433.         Bra    .RLoop        ;Go fetch next character.
  434.  
  435. ; Here, I must null-terminate the string I have constructed in FilenameString[].
  436. ; I must also trim the trailing white space from the string...
  437.  
  438. .Parse        Cmp.L    A2,D2        ;Anything stored in dest string?
  439.         Beq    .NextLine    ;Nope, this line parses to a blank line.
  440.  
  441.         Move.B    -(A2),D0    ;Get previous char from dest string.
  442.         Cmp.B    #" ",D0        ;Is it a space character?
  443.         Beq    .Parse        ;Yup, parse it out.
  444.         Cmp.B    #TAB,D0        ;Is it a tab character?
  445.         Beq    .Parse        ;Yup, parse it out.
  446.  
  447.         AddQ    #1,A2        ;No, so move forward to next cell.
  448.         Clr.B    (A2)        ;NULL-terminate the string.
  449.  
  450.         MoveQ    #0,D0        ;Return success code in D0.
  451.  
  452. .Leave        Rts            ;Leave.
  453.  
  454. ; This bit of code is called prior to parsing a line from the script file
  455. ; to see if I've gone past the end of the script file.  Since it's always
  456. ; called before the line is parsed, I clear the PathnameFlag...
  457.  
  458. .NextLine    Clr.B    PathnameFlag(A4) ;Clear pathname flag.
  459.  
  460.         Cmp.L    A1,A0        ;Reached EOF yet?
  461.         Blt    .ReadLoop    ;Nope, it's OK to parse another line.
  462.  
  463.         Move.L    #-1,D0        ;Indicate end of file.
  464.         Bra    .Leave        ;And leave.
  465.  
  466. ;---------------------------------------------------------------
  467. ; ReadPath routine.  Reads all files from the path specified in
  468. ; FilenameString[].  The calling routine must have previously called the
  469. ; VerifyPath routine, which sets up a lock on the path (stored in LockPointer)
  470. ; and also fills out the MyFileInfoBlock structure by calling Examine().
  471. ;
  472. ; All filenames are read in from the path (using ExNext()) and stored in the
  473. ; filename buffer.
  474. ;
  475. ;   Input: LockPointer(A4) must contain lock on path.
  476. ;       MyFileInfoBlock(A4) must point to filled out struct for Examine().
  477. ;
  478. ;  Output: D0 = 0  if all files successfully read in and stored.
  479. ;        -1 if an error occurred.
  480. ;
  481. ; Trashes: D1,D2,D3,A0,A1,A3,A6
  482. ;
  483.  
  484. ReadPath    
  485.  
  486. ; First, allocate a buffer to hold the filenames for all files in the path...
  487.  
  488.         Bsr    AllocFNameBuffer    ;Allocate initial filename buffer.
  489.         Tst.L    D0            ;Did allocate succeed?
  490.         Bmi    .InsuffMem        ;Nope, so abort with error.
  491.  
  492. ; And now, I call ExNext() repeatedly until all filenames have been read in. I
  493. ; must also count the number of files in the path so that I know how large the
  494. ; pointer table must be (which I create after all filenames have been read)...
  495.  
  496.         Move.W    #0,NumOfFiles(A4)    ;Initialize filename counter.
  497.  
  498. .ReadFile    Move.L    DosBase(A4),A6        ;Get DOS_Base.
  499.         Move.L    LockPointer(A4),D1    ;Lock ptr in D1.
  500.         Move.L    MyFileInfoBlock(A4),A0    ;FileInfoBlock struct ptr in A0.
  501.         Move.L    A0,D2            ;DOS wants it in D2.
  502.         SYS    ExNext            ;Get info for next file in path.
  503.         Tst.L    D0            ;Were any files left?
  504.         Beq    .Done            ;Nope, all done.
  505.  
  506. ; Add this filename to the buffer containing all the filenames...
  507.  
  508.         Move.L    MyFileInfoBlock(A4),A3    ;Pointer to file info block.
  509.         Tst.W    fib_DirEntryType(A3)    ;Make sure this is a file.
  510.         Bpl    .ReadFile        ;It's a directory, so skip it.
  511.         Lea    fib_FileName(A3),A3    ;A3 points to filename string.
  512.  
  513.         Bsr    AddFNameToBuffer    ;Add filename string to buffer.
  514.         Tst.L    D0            ;Error in doing so?
  515.         Bmi    .InsuffMem        ;Yup, abort with error.
  516.  
  517.         AddQ.W    #1,NumOfFiles(A4)    ;Nope, adjust filename count.
  518.  
  519. ; Now go back and see if any files remain in path...
  520.  
  521.         Bra    .ReadFile        ;Back for another file.
  522.  
  523. .Done        MoveQ    #0,D0            ;Return with success code.
  524.  
  525. .Leave        Rts                ;Leave.
  526.  
  527. .Error        MoveQ    #-1,D0            ;Return with error code.
  528.         Bra    .Leave            ;Leave.
  529.  
  530. .InsuffMem    Move.L    #InsuffMemText,D2    ;Tell user "Ran outa mem, dude."
  531.         Bsr    PrintNicely
  532.         Bra    .Error            ;Abort with error.
  533.  
  534. ;---------------------------------------------------------------
  535. ; MakePtrTable routine.  Creates a table of pointers to each filename string
  536. ; in the filename buffer and then shuffles the pointers randomly.
  537. ;
  538. ;   Input: NumOfFiles(A4) holds number of filenames in the buffer.
  539. ;       FilenameBufAddr(A4) must hold ptr to filename buffer.
  540. ;
  541. ;  Output: D0 = 0  if pointer table successfully created and shuffled.
  542. ;        -1 if an error occurred.
  543. ;
  544. ; Trashes: D1,D2,D3,A0,A1,A6
  545. ;
  546.  
  547. MakePtrTable    MoveQ    #0,D0
  548.         Move.W    NumOfFiles(A4),D0    ;Get # of filenames in buffer.
  549.         Bne    .AreFiles        ;There's at least one filename.
  550.  
  551.         Move.L    #NoFilesText,D2        ;No filenames in buffer.
  552.         Bsr    PrintNicely
  553.         Bra    .Error            ;Abort with error code.
  554.  
  555. .AreFiles    Add.L    D0,D0            ;Make longword index out of D0.
  556.         Add.L    D0,D0            ;D0 is now size of the ptr array.
  557.         Move.L    D0,PtrTableLen(A4)    ;Store size for deallocation later.
  558.  
  559.         LOADLIB    SYS            ;Get SYS_Base.
  560.         Move.L    #MEMF_CLEAR,D1        ;Clear the memory for me, please.
  561.         SYS    AllocMem        ;Attempt to allocate ptr array mem.
  562.         Move.L    D0,PtrTableAddr(A4)    ;Save ptr to ptr-array memory.
  563.         Bne    .HavePtrMem        ;Allocate was successful.
  564.  
  565. .InsuffMem    Move.L    #InsuffMemText,D2    ;Oop, ran out of memory. Darn.
  566.         Bsr    PrintNicely
  567.         Bra    .Error            ;Abort with error.
  568.  
  569. ; Now fill out the pointer array...
  570.  
  571. .HavePtrMem    Move.L    FilenameBufAddr(A4),A1    ;get ptr to filenames buffer.
  572.         Move.L    D0,A0            ;A0 points to ptr-array.
  573.         Move.W    NumOfFiles(A4),D0    ;Do for # of filenames in buffer.
  574.         SubQ    #1,D0            ;Adjust count for "Do..Until" loop.
  575.  
  576.         Bra    .1
  577.  
  578. .IPALoop    Tst.B    (A1)+            ;Check next char in filename buffer.
  579.         Bne    .IPALoop        ;Search until NULL is found.
  580.  
  581. .1        Move.L    A1,(A0)+        ;Store pointer to filename.
  582.  
  583.         DBra    D0,.IPALoop        ;Find start of next filename string.
  584.  
  585. ; At this point, All filenames have been read in, so I can leave now...
  586.  
  587.         MoveQ    #0,D0            ;Indicate success.
  588.  
  589. .Leave        Move.L    D0,-(Sp)        ;   Save return code.
  590.         Move.L    LockPointer(A4),D1    ;Lock pointer in D1.
  591.         Move.L    DosBase(A4),A6        ;Get DOS_Base.
  592.         SYS    UnLock            ;Don't need lock anymore.
  593.         Clr.L    LockPointer(A4)        ;So exit code knows it's gone.
  594.         Move.L    (Sp)+,D0        ;   Restore return code.
  595.  
  596.         Rts                ;And leave.
  597.  
  598. .Error        MoveQ    #-1,D0            ;Indicate an error occurred.
  599.         Bra    .Leave            ;Abort with error.
  600.  
  601. ;---------------------------------------------------------------
  602. ; ShuffleSongs routine.  Takes each pointer in the pointer array and swaps it
  603. ; with another pointer in the array that is chosen at random.
  604. ;   Input: PtrTableArray(A4) must point to the pointer array to shuffle.
  605. ;       NumOfFiles(A4) must holds the number of pointers in the array.
  606. ;
  607. ;  Output: Nothing.
  608. ;
  609. ; Trashes: D0,D1,D6,D7,A0
  610. ;
  611.  
  612. ShuffleSongs    Move.L    PtrTableAddr(A4),A0    ;Get ptr to the Pointer-array.
  613.         Move.W    NumOfFiles(A4),D7    ;Do for # of pointers in array.
  614.         SubQ.W    #1,D7            ;Adjust loop count for "Do..Until".
  615.  
  616. .NextFile    Bsr    Random            ;Get a random number.
  617.         MulU    NumOfFiles(A4),D0    ;Create random value in range.
  618.         Swap    D0            ;D0 is now 0..NumOfFiles-1.
  619.  
  620. ; I have a random element, so swap the corresponding pointers...
  621.  
  622.         Add.W    D0,D0            ;Make long index of random index.
  623.         Add.W    D0,D0
  624.         Move.W    D7,D6            ;Copy loop count into D6.
  625.         Add.W    D6,D6            ;Make long index of loop count.
  626.         Add.W    D6,D6
  627.         Move.L    0(A0,D0.W),D1        ;Save random element's pointer.
  628.         Move.L    0(A0,D6.W),0(A0,D0.W)    ;Copy cur. element to random element.
  629.         Move.L    D1,0(A0,D6.W)        ;Store random element in cur. element.
  630.  
  631.         DBra    D7,.NextFile        ;Until all ptrs have been swapped.
  632.  
  633.         Rts
  634.  
  635. ;---------------------------------------------------------------
  636. ; PlaySongs routine.  This is the routine that goes through the entire list
  637. ; of songs, loading and playing each one.  Since I do not lock the library,
  638. ; I have to be wary of the user doing things with DTC like unloading a module
  639. ; on me while I'm playing it.
  640. ;
  641. ;   Input: PtrTableAddr(A4) must hold ptr to array of pointers to song filenames.
  642. ;       NumOfFiles(A4) must hold the number of filenames to load and play.
  643. ;
  644. ;  Output: D0 = 0  if all songs were processed without any trouble.
  645. ;        -1 if another program in memory locked the library.
  646. ;
  647. ; Trashes: D1,D2,D3,A0,A1,A2,A5,A6
  648. ;
  649.  
  650. PlaySongs    Move.W    #0,CurSongNum(A4)    ;Init current song number.
  651.         Move.L    PtrTableAddr(A4),A5    ;Cache ptr-array ptr in A5.
  652.  
  653.         Move.L    DTBase(A4),A6        ;Get DES-Tracker base.
  654.         MoveQ    #0,D0
  655.         Move.B    DTPlayDoneSig(A4),D0    ;Get play-done signal bit #.
  656.         Move.W    #SEF_FINISH|SEF_MODUNLOADED|SEF_STOP|SEF_LIBOWNED,D1 ;Specify events I want.
  657.         SYS    AddStatusSignal        ;Add event signal to library.
  658.  
  659.         MoveQ    #0,D0
  660.         Move.B    DTIterDoneSig(A4),D0    ;Get iteration-done sig bit #.
  661.         Move.W    #SEF_ITERATION|SEF_VOLFADE,D1 ;Specify events I want.
  662.         SYS    AddStatusSignal        ;Add event signal to library.
  663.  
  664.         MoveQ    #0,D0
  665.         Move.B    JukeVolume(A4),D0    ;Get current global juke volume.
  666.         SYS    SetGlobalVolume        ;Set global volume.
  667.  
  668. ;-------------------------------
  669. ; Here begins the load & play loop.  I use the variable WaitSignals(A4) to hold
  670. ; the signal bits that I want to wait for.  I always wait for CTRL-C as well as
  671. ; the play-done signal.  However, if the song is to be played for more than one
  672. ; iteration, then I also need to wait for the iteration-done signal in order for
  673. ; me to know when to start fading out the song...
  674.  
  675. .PlayLoop    Bsr    LoadSong        ;Attempt to load song.
  676.         Tst.L    D0            ;So what happened?
  677.         Beq    .Go            ;Song loaded fine.
  678.         Bpl    .NextSkipTime        ;Couldn't load song. Try next song.
  679.  
  680.         Bra    .Error            ;Library now locked, so abort.
  681.  
  682. ; At this point the module loaded successfully.  First, I'll print "Playing..."
  683. ; and then I must examine the module a bit to determine how I'm to play it...
  684.  
  685. .Go        Move.L    dtl_ModuleStatus+ms_Name(A6),A0
  686.         MoveQ    #-1,D1
  687. .Count        AddQ    #1,D1
  688.         Tst.B    (A0)+
  689.         Bne    .Count
  690.  
  691.         MoveQ    #22,D0
  692.         Sub.W    D1,D0
  693.         Move.B    D0,NumTimeSpaces(A4)
  694.  
  695.         Move.L    #PlayingText,D2        ;Say "Playing "
  696.         Bsr    PrintN2Text
  697.         Move.L    dtl_ModuleStatus+ms_Name(A6),D2
  698.         Bsr    PrintN2Text        ; followed by module name.
  699.         Move.L    #XtraPlayText,D2    ; followed by close quote.
  700.         Bsr    PrintN2Text
  701.  
  702. ;-------------------------------
  703. ; I have to determine the number of iterations for this module.
  704. ; Here's how I adjust for the number of iterations...
  705. ;
  706. ; Module's default    What I convert
  707. ; # of iterations        it to
  708. ;
  709. ;   iter = 1               1
  710. ;   iter = 0              2
  711. ;   iter > 1          iter + 1
  712. ;
  713. ; Also, if the resultant number of iterations is greater than 1, then I have
  714. ; to fade the song out at the beginning of its last iteration...
  715.  
  716.         MoveQ    #0,D0
  717.         Move.B    dtl_DefIterations(A6),D0 ;Get default # of iterations.
  718.         Cmp.W    #1,D0            ;Is it a one-shot tune?
  719.         Beq    .StoreIter        ;Yes, keep it that way.
  720.         Bgt    .AddOneIter        ;>1 means add extra iteration.
  721.  
  722.         MoveQ    #1,D0            ;Next instruction changes it to 2.
  723.         
  724. .AddOneIter    AddQ    #1,D0            ;I will fade on this extra iteration.
  725.  
  726. ; Here, the number of iterations is greater than 1.  This means I must fade
  727. ; the song out at the beginning of the last iteration.  To do this, I'll need
  728. ; to wait for the iteration-done signal bit, so I add it to WaitSignals(A4)...
  729.  
  730.         Move.L    WaitSignals(A4),D2    ;Get wait signal bits
  731.         Move.B    DTIterDoneSig(A4),D1    ;Include the iter-done signal.
  732.         BSet    D1,D2
  733.         Move.L    D2,WaitSignals(A4)    ;Store updated wait signal bits.
  734.  
  735. .StoreIter    Move.W    D0,NumIterations(A4)    ;Store new number of iterations.
  736.         SYS    SetIterations        ;Set new # of iterations.
  737.  
  738.         SYS    BeginPlaySeq        ;Start playing the song.
  739.  
  740. ;-------------------------------
  741. ; Wait until the song is finished or something diabolical happens...
  742.  
  743. .WaitLoop    LOADLIB    SYS            ;Get SYS_Base.
  744.         MoveQ    #0,D0
  745.         Move.L    WaitSignals(A4),D0    ;Wait signals in D0.
  746.         SYS    Wait            ;Wait for something to happen.
  747.  
  748.         Move.L    DTBase(A4),A6        ;Get DES-Tracker base.
  749.  
  750. ;-------------------------------
  751. ; At this point, I've been signalled, so I have to determine which signals I've
  752. ; received and act on them accordingly...
  753.  
  754.         MoveQ    #SIGBREAKB_CTRL_C,D1    ;Get control-C signal bit #.
  755.         BTst    D1,D0            ;Was CTRL-C pressed?
  756.         Beq    .CheckCTRLF        ;Nope.
  757.  
  758.         Move.L    #JukeAbortText,D2    ;Yup, so abort jukebox.
  759.         Bsr    PrintNicely
  760.         Bra    .Error            ;Abort.
  761.  
  762. .CheckCTRLF    MoveQ    #SIGBREAKB_CTRL_F,D1    ;Get control-F signal bit #.
  763.         BTst    D1,D0            ;Was CTRL-F pressed?
  764.         Beq    .CheckDTSig        ;Nope.
  765.         Move.B    dtl_GlobalVolume(A6),JukeVolume(A4)
  766.         Bra    .NextTune        ;Yup, skip to next song.
  767.  
  768. .CheckDTSig    Move.B    DTPlayDoneSig(A4),D1    ;Get play-done sig bit #.
  769.         BTst    D1,D0            ;Play seq done or mod unloaded?
  770.         Beq    .CheckIter        ;Nope.
  771.  
  772. ; Here, either the (one-shot) song has finished playing or the module was unloaded
  773. ; while it was playing.  Before going on to the next song, I must grab the current
  774. ; global volume setting from the library, since the user may have changed it
  775. ; while I was asleep...
  776.  
  777.         Move.B    dtl_GlobalVolume(A6),JukeVolume(A4)    ;Get new global volume.
  778.         Bra    .NextTune        ;Go load and play the next song.
  779.  
  780. .CheckIter    Move.B    DTIterDoneSig(A4),D1    ;Get iter-done sig bit #.
  781.         BTst    D1,D0            ;Iteration or volume fade done?
  782.         Beq    .WaitLoop        ;Nope, go back to sleep.
  783.  
  784.         Tst.B    FadingFlag(A4)        ;Am I waiting for a fade to complete?
  785.         Bne    .NextTune        ;Yup, so go on to next song.
  786.  
  787.         Move.B    dtl_IterationsToGo(A6),D1 ;No, get iterations left to play.
  788.         Cmp.B    #1,dtl_IterationsToGo(A6) ;Started last iteration?
  789.         Bgt    .WaitLoop        ;No, go back to sleep.
  790.  
  791. ; Now song is on last iteration, so start a gobal volume fade.  But, since
  792. ; the user may have changed the global volume while I was asleep, I must
  793. ; grab it and use it to reset the global volume after the volume fade has
  794. ; completed...
  795.  
  796.         MoveQ    #0,D0
  797.         Move.B    dtl_GlobalVolume(A6),JukeVolume(A4) ;Save global volume.
  798.  
  799.         MoveQ    #0,D0            ;Fade to 0 volume.
  800.         SYS    FadeGlobalVolume    ;Start the fade.
  801.         St    FadingFlag(A4)        ;Indicate I'm waiting for fade now.
  802.         Bra    .WaitLoop        ;Go back to sleep.
  803.  
  804. ;-------------------------------
  805. ; Time to go to the next song in the pointer array. I am about to unload the
  806. ; currently loaded module.  Before I do this, I must tell DES-Tracker not to
  807. ; signal me when a module is unloaded.  Otherwise, I'd then load up the next
  808. ; module, and when I go to sleep, I'll be immediately awakened with a
  809. ; MODUNLOADED event (for the previous module).  DES-Tracker will signal me
  810. ; whether I'm asleep or not, so I must inform it that I have no interest in
  811. ; unloaded modules temporarily...
  812.  
  813. .NextTune    
  814.  
  815. ; Display time that song was playing for...
  816.  
  817.         Lea    Spaces,A0
  818.         Move.L    A0,D2
  819.         MoveQ    #0,D3
  820.         Move.B    NumTimeSpaces(A4),D3
  821.         Bsr    PrintCLI
  822.  
  823.         Move.L    #PlayTimeText,D2
  824.         Move.L    #PlayTimeLen,D3
  825.         Bsr    PrintCLI
  826.  
  827.         Lea    UnitsNoColon,A2
  828.         Move.B    dtl_ElapsedHours(A6),D0
  829.         Bsr    BinToBCD
  830.         Move.B    D0,D1
  831.         Lsr.B    #4,D0
  832.         And.B    #$0F,D1
  833.         Add.B    #'0',D0
  834.         Add.B    #'0',D1
  835.         Move.B    D0,(A2)
  836.         Move.B    D1,1(A2)
  837.         Move.L    A2,D2
  838.         MoveQ    #UNoColonLen,D3
  839.         Bsr    PrintCLI
  840.  
  841.         Lea    UnitsColon,A2
  842.         MoveQ    #UColonLen,D3
  843.         Move.B    dtl_ElapsedMinutes(A6),D0
  844.         Bsr    BinToBCD
  845.         Move.B    D0,D1
  846.         Lsr.B    #4,D0
  847.         And.B    #$0F,D1
  848.         Add.B    #'0',D0
  849.         Add.B    #'0',D1
  850.         Move.B    D0,1(A2)
  851.         Move.B    D1,2(A2)
  852.         Move.L    A2,D2
  853.         Bsr    PrintCLI
  854.  
  855.         Lea    UnitsColon,A2
  856.         MoveQ    #UColonLen,D3
  857.         Move.B    dtl_ElapsedSeconds(A6),D0
  858.         Bsr    BinToBCD
  859.         Move.B    D0,D1
  860.         Lsr.B    #4,D0
  861.         And.B    #$0F,D1
  862.         Add.B    #'0',D0
  863.         Add.B    #'0',D1
  864.         Move.B    D0,1(A2)
  865.         Move.B    D1,2(A2)
  866.         Move.L    A2,D2
  867.         Bsr    PrintCLI
  868.  
  869.         Lea    UnitsColon,A2
  870.         MoveQ    #UColonLen,D3
  871.         Move.B    dtl_ElapsedJiffies(A6),D0
  872.         Bsr    BinToBCD
  873.         Move.B    D0,D1
  874.         Lsr.B    #4,D0
  875.         And.B    #$0F,D1
  876.         Add.B    #'0',D0
  877.         Add.B    #'0',D1
  878.         Move.B    D0,1(A2)
  879.         Move.B    D1,2(A2)
  880.         Move.L    A2,D2
  881.         Bsr    PrintCLI
  882.  
  883. .NextSkipTime    MoveQ    #0,D0
  884.         Move.B    DTPlayDoneSig(A4),D0    ;Get play-done sig bit #.
  885.         MoveQ    #0,D1        ;Clear event bits for this signal so I
  886.         SYS    AddStatusSignal    ;don't get signalled for UnloadModule().
  887.  
  888.         SYS    UnloadModule        ;Unload current module.
  889.  
  890. ; Now I can reaffirm my interest in unloaded modules and finished play sequences...
  891.  
  892.         MoveQ    #0,D0
  893.         Move.B    DTPlayDoneSig(A4),D0    ;Get play-done sig bit # again.
  894.         Move.W    #SEF_FINISH|SEF_MODUNLOADED,D1    ;Restore the
  895.         SYS    AddStatusSignal            ; event bits.
  896.  
  897. ; Control is passed directly to this point if an error occurred while the song
  898. ; was being loaded...
  899.  
  900. .SkipSong    MoveQ    #0,D0
  901.         Move.B    JukeVolume(A4),D0    ;Get jukebox volume setting.
  902.         SYS    SetGlobalVolume        ;Reset global volume.
  903.  
  904.         AddQ.W    #1,CurSongNum(A4)    ;Increment song number.
  905.         Move.W    CurSongNum(A4),D0
  906.         Cmp.W    NumOfFiles(A4),D0    ;Played all songs?
  907.         Blt    .PlayLoop        ;Nope, go load next song.
  908.  
  909. ; At this point, all songs have been played.  It's time to go.
  910.  
  911. .Done        MoveQ    #0,D0            ;Return success code.
  912.  
  913. .Leave        Rts                ;Leave.
  914.  
  915. .Error        MoveQ    #-1,D0            ;Return error code.
  916.         Bra    .Leave            ;And leave.
  917.  
  918. ;---------------------------------------------------------------
  919. ; LoadSong routine.  Called by PlaySongs, this routine attempts to load up
  920. ; the next song in the song list.  It also resets the WaitSignals variable
  921. ; for me, as well as a few other flags.
  922. ;
  923. ;   Input: A5 = Ptr to song filename pointer array.
  924. ;       CurSongNum(A4) = number of the song to load (index into ptr array).
  925. ;
  926. ;  Output: D0 = 0  if song loaded successfully.
  927. ;        1  if song couldn't be loaded (so go on to the next song).
  928. ;        -1 if the library has been locked (abort the jukebox).
  929. ;
  930. ; Trashes: D1,D2,D3,A0,A1,A2,A6
  931. ;
  932.  
  933. LoadSong    Move.L    #SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_F,D0    ;CTRL-C & CTRL_F.
  934.         Move.B    DTPlayDoneSig(A4),D1    ;Always wait for play-done.
  935.         BSet    D1,D0
  936.         Move.L    D0,WaitSignals(A4)    ;Store signal bits to wait for.
  937.  
  938.         Clr.B    FadingFlag(A4)        ;Reset "Fade Song" flag.
  939.  
  940.         Move.L    DTBase(A4),A6        ;Get DES-Tracker base.
  941.         Move.W    CurSongNum(A4),D0    ;Get current song number to load.
  942.         Add.W    D0,D0            ;Make long index out of it.
  943.         Add.W    D0,D0
  944.         Move.L    0(A5,D0.W),A0        ;Extract ptr to song's filename.
  945.  
  946.         Move.L    A0,A2            ;Save pointer in A2 for a bit.
  947.         Move.L    #LoadingText,D2        ;Print "Loading "
  948.         Bsr    PrintN2Text
  949.         Move.L    A2,D2            ; followed by song filename
  950.         Bsr    PrintN2Text
  951.         Move.L    #XtraLoadText,D2    ; followed by carriage return.
  952.         Bsr    PrintN2Text
  953.  
  954.         Move.L    A2,A0            ;Get filename ptr back in A0.
  955.         SYS    LoadModule        ;Attempt to load the module.
  956.         Tst.L    D0            ;Error loading module?
  957.         Bpl    .Done            ;Nope, everything's peachy.
  958.  
  959.         Cmp.W    #DE_INSUFFICIENT_MEMORY,D0 ;Yup, not enough memory?
  960.         Bne    .E1            ;Nope, that's not why.
  961.         Move.L    #NoMemText,D2        ;Yes, say "Not enough mem".
  962.         Move.L    #NoMemLen,D3
  963. .PErr        Bsr    PrintCLI
  964.         Bra    .Error            ;And go on to the next song.
  965.  
  966. .E1        Cmp.W    #DE_FILE_LOAD_ERROR,D0    ;Couldn't find the file?
  967.         Bne    .E2            ;Nope, that's not why, either.
  968.         Move.L    #NotFoundText,D2    ;Yup, say "File not found".
  969.         Move.L    #NotFoundLen,D3
  970.         Bra    .PErr            ;And go on to the next song.
  971.  
  972. .E2        Cmp.W    #DE_UNKNOWN_FORMAT,D0    ;Hmm... unknown module format?
  973.         Bne    .E3            ;Damn, that's not it either.
  974.         Move.L    #BadFormatText,D2    ;Yup, say "Crappy file format".
  975.         Move.L    #BadFormatLen,D3
  976.         Bra    .PErr            ;And go on to the next song.
  977.  
  978. .E3        Move.L    #LibOwnedText,D2    ;Must be library locked.
  979.         Bsr    PrintNicely        ;So say so.
  980.         Bra    .Error            ;If lib is locked, I must abort.
  981.  
  982. .Done        MoveQ    #0,D0            ;Song loaded OK.
  983.  
  984. .Leave        Rts                ;Back we go.
  985.  
  986. .Error        LOADLIB    SYS            ;SYS_Base in A6.
  987.         Move.L    TaskPointer(A4),A0    ;If song didn't load
  988.         Move.L    #SIGBREAKF_CTRL_F,D0    ; then clear any CTRL-F signal
  989.         Not.L    D0            ; I may have received so that
  990.         SYS    Forbid            ; I don't wind up skipping the
  991.         And.L    TC_SIGRECVD(A0),D0    ; *next* song I load.
  992.         Move.L    D0,TC_SIGRECVD(A0)    ;Store updated signal bits.
  993.         SYS    Permit            ;Allow multi-tasking once again.
  994.  
  995.         MoveQ    #1,D0            ;Couldn't load song. Go to next one.
  996.         Bra    .Leave
  997.  
  998. ;---------------------------------------------------------------
  999. ; Random routine.  I originally got this algorithm from a Dr Dobbs journal,
  1000. ; but the author's algorithm was called r235 (or some wierd number).  Also,
  1001. ; his sample code had a *VERY SERIOUS* flaw in it that I can't believe he
  1002. ; didn't notice.  I modified the algorithm to come up with the r256 algorithm
  1003. ; which is more efficient than the r235 version because mine doesn't require
  1004. ; bounds checking on the table index.  Also, this particular version of r256
  1005. ; has been streamlined since it only needs to return 16 rather than 32 bit
  1006. ; values.  The RandIndex variable always holds the index multiplied by 2 (since
  1007. ; the table is made of up of word elements) so that I don't have to keep
  1008. ; doubling it before I use it (that's why I add #103*2 instead of just #103).
  1009. ;
  1010. ;   Input: Nothing, but RandTable must be initialized.
  1011. ;
  1012. ;  Output: D0.W = A pseudo random number.
  1013. ;
  1014. ; Trashes: Nothing.
  1015. ;
  1016.  
  1017. Random        Move.W    D1,-(Sp)        ;   Save D1.
  1018.         Move.W    RandIndex(A4),D0    ;Get current table word index.
  1019.         Move.W    RandTable(A4,D0.W),D1    ;D1 = RandTable[D0]
  1020.         Add.W    #103*2,D0        ;Add prime number to word index.
  1021.         And.W    #$1FF,D0        ;Keep index within table bounds.
  1022.         Move.W    D0,RandIndex(A4)    ;Store updated index.
  1023.         Eor.W    D1,RandTable(A4,D0.W)    ;Modify value at new index with D1.
  1024.         Move.W    RandTable(A4,D0.W),D0    ;Return modified value in D0.
  1025.         Move.W    (Sp)+,D1        ;   Restore D1.
  1026.  
  1027.         Rts                ;And leave.
  1028.  
  1029. ;---------------------------------------------------------------
  1030. ; VerifyPath routine.  Takes a string which might be either a pathname or a
  1031. ; filespec, tries to get a lock on it, and determines whether the string
  1032. ; is a pathname, filespec, or garbage.  I save a lot of registers in this
  1033. ; routine because I just don't trust AmigaDOS (who would?).
  1034. ;
  1035. ;   Input: A0 = Pathname or filespec string (NULL-Terminated).
  1036. ;
  1037. ;  Output: D0 =  0 if string is a filespec.
  1038. ;        >0 if string is a pathname.
  1039. ;        <0 if string is bloody nonsense.
  1040. ;
  1041. ; Trashes: Nothing.
  1042. ;
  1043. VerifyPath    MoveM.L    D1-D7/A0-A3/A5/A6,-(Sp)    ;Speed's not important here. Play it safe.
  1044.  
  1045.         MoveQ    #-1,D6            ;Assume user will type nonsense. :)
  1046.  
  1047.         Move.L    DosBase(A4),A6        ;Get DOS_Base.
  1048.         Move.L    A0,D1            ;Ptr to string in D1.
  1049.         MoveQ    #-2,D2            ;Read only mode for lock.
  1050.         SYS    Lock            ;Attempt to get a lock.
  1051.         Move.L    D0,LockPointer(A4)    ;Save ptr to the lock.
  1052.         Beq    .Leave            ;Couldn't get a lock. Error.
  1053.  
  1054.         Move.L    D0,D7            ;Cache lock pointer in D7.
  1055.         Move.L    D7,D1            ;Put it back in D1.
  1056.         Move.L    MyFileInfoBlock(A4),A0    ;Get ptr to FileInfoBlock.
  1057.         Move.L    A0,D2            ;DOS wants it in D2.
  1058.         SYS    Examine            ;Fill out file info block.
  1059.         Tst.L    D0            ;Was the path/file found?
  1060.         Beq    .Leave            ;Apparently not.
  1061.  
  1062.         Move.L    MyFileInfoBlock(A4),A0    ;Yes, get file info block again.
  1063.         Tst.W    fib_DirEntryType(A0)    ;Check dir entry type.
  1064.         Bmi    .IsFile            ;Negative means it's a file.
  1065.  
  1066.         AddQ.L    #1,D6            ;It's a path, inc D6.
  1067.  
  1068. .IsFile        AddQ.L    #1,D6            ;It's a file, inc D6.
  1069.  
  1070. .Leave        Move.L    D6,D0            ;Return status in D0.
  1071.         MoveM.L    (Sp)+,D1-D7/A0-A3/A5/A6    ;Restore regs.
  1072.         Rts                ;And leave.
  1073.  
  1074. ;---------------------------------------------------------------
  1075. ; AllocFNameBuffer routine.  This routine simply allocates a buffer of a
  1076. ; default initial size which is used for storing filenames of songs.
  1077. ;
  1078. ;   Input: None.
  1079. ;
  1080. ;  Output: D0 = 0  if allocation was successful.
  1081. ;        -1 if allocation failed.
  1082. ;
  1083. ; Trashes: D1,A0,A1,A6
  1084. ;
  1085.  
  1086. DEF_BUF_SIZE    = 512    ;initial buffer size.
  1087.  
  1088. AllocFNameBuffer
  1089.         LOADLIB    SYS            ;Get SYS_Base.
  1090.         Move.L    #DEF_BUF_SIZE,D0    ;Buffer size in D0.
  1091.         Move.L    #MEMF_CLEAR,D1        ;Clear mem for me, please.
  1092.         SYS    AllocMem        ;Attempt to allcate memory.
  1093.         Move.L    D0,FilenameBufAddr(A4)    ;Save ptr to buffer memory.
  1094.         Beq    .Error            ;If 0, then allocation failed.
  1095.  
  1096.         Move.L    #DEF_BUF_SIZE,FilenameBufLen(A4) ;Remember buffer size.
  1097.         Move.L    #0,FilenameBufIndex(A4)    ;Initialize buffer index.
  1098.  
  1099.         MoveQ    #0,D0            ;Return with success code.
  1100. .Leave        Rts                ;Leave.
  1101.  
  1102. .Error        MoveQ    #-1,D0            ;Return with error code.
  1103.         Bra    .Leave            ;Leave.
  1104.  
  1105. ;---------------------------------------------------------------
  1106. ; AddFNameToBuffer routine.  This routine copies the specified NULL-terminated
  1107. ; string into the filenames buffer.  If there is not enough room in the buffer
  1108. ; to hold the filename, then a larger buffer is created and the old buffer
  1109. ; is copied over to the new buffer after which the old buffer is killed.
  1110. ;
  1111. ;   Input: A3 = Pointer to NULL-terminated filename to add to buffer.
  1112. ;
  1113. ;  Output: D0 = 0  if filename was successfully added to the buffer.
  1114. ;        -1 if an error occurred while trying to create a larger buffer.
  1115. ;
  1116. ; Trashes: D1,D5,A0,A1,A3,A6
  1117. ;
  1118.  
  1119. BUF_INC_SIZE    = 256    ;Amount to enlarge buffer by if filename won't fit in it.
  1120.  
  1121. AddFNameToBuffer
  1122.         LOADLIB    SYS            ;Get SYS_Base.
  1123.  
  1124.         Move.L    A3,A1            ;Count length of filename first.
  1125.         MoveQ    #0,D0            ;D0 will hold length of filename.
  1126. .LenLoop    AddQ    #1,D0            ; (length includes NULL).
  1127.         Tst.B    (A1)+
  1128.         Bne    .LenLoop
  1129.  
  1130. ;See if this filename will fit in buffer...
  1131.  
  1132.         Move.L    FilenameBufLen(A4),D1    ;Get length of filenames buffer.
  1133.         Sub.L    FilenameBufIndex(A4),D1    ;Subtract current buffer index.
  1134.         Cmp.L    D1,D0            ;Will filename fit?
  1135.         Blt    .StoreFilename        ;Yup, go copy it in.
  1136.  
  1137. ; Filename doesn't fit in the buffer, so I must enlarge the buffer.  The
  1138. ; filenames buffer is enlarged by BUF_INC_SIZE bytes at a time...
  1139.  
  1140.         Move.L    FilenameBufLen(A4),D5    ;Get length of "old" buffer.
  1141.         Add.L    #BUF_INC_SIZE,D5    ;Calculate new length.
  1142.         Move.L    D5,D0            ;New length in D0.
  1143.         Move.L    #MEMF_CLEAR,D1        ;Clear mem for me, please.
  1144.         SYS    AllocMem        ;Attempt to allocate new buffer.
  1145.         Tst.L    D0            ;Error?
  1146.         Beq    .Error            ;Yup, allocate failed.
  1147.  
  1148. ; Now copy the contents of the old buffer to the new buffer...
  1149.  
  1150.         Move.L    FilenameBufAddr(A4),A0    ;A0 pts to old buffer.
  1151.         Move.L    D0,A1            ;A1 pts to new buffer.
  1152.         Move.L    FilenameBufLen(A4),D1    ;D1 = length of old buffer.
  1153. .CopyBuf    Move.B    (A0)+,(A1)+        ;Copy a byte from old to new.
  1154.         SubQ.L    #1,D1
  1155.         Bgt    .CopyBuf        ;Loop D1 times.
  1156.  
  1157. ; Now deallocate the old buffer...
  1158.  
  1159.         Move.L    D0,-(Sp)        ;Save ptr to new buffer.
  1160.         Move.L    FilenameBufAddr(A4),A1    ;A1 = ptr to old buffer.
  1161.         Move.L    FilenameBufLen(A4),D0    ;D0 = Length of old buffer.
  1162.         SYS    FreeMem            ;Free up the old buffer memory.
  1163.         Move.L    (Sp)+,D0        ;Restore new buffer ptr.
  1164.  
  1165. ; Now update variables for the new buffer...
  1166.  
  1167.         Move.L    D0,FilenameBufAddr(A4)    ;Store ptr to new buffer.
  1168.         Move.L    D5,FilenameBufLen(A4)    ;Store length of new buffer.
  1169.  
  1170. ; Now copy the filename to the filenames buffer...
  1171.  
  1172. .StoreFilename    Move.L    FilenameBufAddr(A4),A0    ;A0 = ptr to filenames buffer.
  1173.         Move.L    FilenameBufIndex(A4),D0    ;D0 = buffer index.
  1174. .CopyNameLoop    Move.B    (A3)+,0(A0,D0.L)    ;Copy byte into buffer.
  1175.         Beq    .CopyDone        ;Stop copying when NULL copied.
  1176.         AddQ.L    #1,D0            ;Inc buffer index.
  1177.         Bra    .CopyNameLoop        ;Copy rest of string into buffer.
  1178.  
  1179. .CopyDone    AddQ.L    #1,D0            ;Inc index for NULL.
  1180.         Move.L    D0,FilenameBufIndex(A4)    ;Store updated buffer index.
  1181.  
  1182.         MoveQ    #0,D0            ;Return with success code.
  1183. .Leave        Rts
  1184.  
  1185. .Error        MoveQ    #-1,D0            ;Return with error code.
  1186.         Bra    .Leave
  1187.  
  1188. ;---------------------------------------------------------------
  1189. ; GetFileSpec routine.  Extracts a file specification from the command line
  1190. ; args.  Stores the extracted filespec in FilenameString[].
  1191. ;
  1192. ;   Input: A5 = pointer to command line argstring.
  1193. ;
  1194. ;  Output: FilenameString[] holds the extracted filespec.
  1195. ;       A5 pts to new current position in argstring.
  1196. ;       D0 = 0  if FilenameString[] holds a valid string.
  1197. ;        -1 if no filespec was specified in the argstring.
  1198. ;
  1199. ; Trashes: A0
  1200. ;
  1201.  
  1202. GetFilespec    Lea FilenameString(A4),A0    ;A0 = ptr to dest string buffer.
  1203.         St    ShuffleFlag(A4)        ;Assume I'll be shuffling songs.
  1204.  
  1205. .Skip        Move.B    (A5),D0        ;Get char from argstring.
  1206.         Cmp.B    #DQUOTE,D0    ;Is it a double quote?
  1207.         Beq.S    .DoQuote    ;Yup, do special quote handling.
  1208.         Cmp.B    #"?",D0        ;Is it a ?
  1209.         Beq    .NoFileSpec    ;Yup, treat as if no filespec specified.
  1210.         Cmp.B    #"-",D0        ;Is it a command prefix character?
  1211.         Beq    .ProcessCmd    ;Yup. Go process command.
  1212.         Cmp.B    #" ",D0        ;Is it a space character?
  1213.         Bgt    .Copy        ;No, ASCII value is greater than space char.
  1214.         Cmp.B    #TAB,D0        ;Is it a tab?
  1215.         Beq    .Skip        ;Yup, ignore it.
  1216.         Cmp.B    #LF,D0        ;Have I reached the end of the argstring?
  1217.         Beq    .NoFileSpec    ;Yup, no filespec was specified.
  1218.  
  1219.         AddQ    #1,A5        ;No, so ignore char and get another.
  1220.         Bra    .Skip
  1221.  
  1222. .DoQuote    AddQ    #1,A5        ;Skip past the double quote character.
  1223.         Move.B    (A5),D0        ;Get char from argstring.
  1224.         Cmp.B    #LF,D0        ;Reached EOL yet?
  1225.         Beq    .Done        ;Yup, no close quote, but that's OK.
  1226.         Cmp.B    #DQUOTE,D0    ;Is it a (close) quote?
  1227.         Beq    .EndQuote    ;Yup, fix up argstring ptr and leave.
  1228.         Move.B    D0,(A0)+    ;Nope, extract this character.
  1229.         Bra    .DoQuote    ;And go get another.
  1230.  
  1231. .EndQuote    AddQ    #1,A5        ;Skip past the close quote.
  1232.         Bra    .Done        ;And leave.
  1233.  
  1234. .Copy        Move.B    (A5)+,(A0)+    ;Copy a character from argstring into buffer.
  1235.         Cmp.B    #" ",(A5)    ;Is next char a space?
  1236.         Beq.S    .Done        ;Yup, NULL-terminate buffer and leave.
  1237.         Cmp.B    #TAB,(A5)    ;Is next char a tab?
  1238.         Beq.S    .Done        ;Yup, all done.
  1239.         Cmp.B    #LF,(A5)    ;Nope, reached EOL yet?
  1240.         Bne.S    .Copy        ;Nope, go copy next character.
  1241.  
  1242. .Done        Clr.B    (A0)        ;NULL-terminate string in buffer.
  1243.         MoveQ    #0,D0        ;Indicate valid string in buffer.
  1244.  
  1245. .Leave        Rts            ;And leave.
  1246.  
  1247. .NoFileSpec    MoveQ    #-1,D0        ;Indicate no filespec was given.
  1248.         Bra    .Leave        ;and leave.
  1249.  
  1250. .ProcessCmd    AddQ    #1,A5        ;Skip command prefix character.
  1251.         Move.B    (A5)+,D0    ;Get command character.
  1252.  
  1253.         Or.B    #$20,D0        ;Make sure char is lower-case.
  1254.         Cmp.B    #"n",D0        ;"No Shuffle" command?
  1255.         Bne    .Skip        ;Nope. "-n" is the only command.
  1256.  
  1257.         Clr.B    ShuffleFlag(A4)    ;Yes, do not shuffle selections.
  1258.         Bra    .Skip        ;Go try to find a filespec.
  1259.  
  1260. ;---------------------------------------------------------------
  1261. ; Initialize routine.  Opens necessary libraries, initializes variables and
  1262. ; tables, etc.
  1263. ;
  1264. ;   Input: A4 = Pointer to allocated program variable space.
  1265. ;
  1266. ;  Output: D0 = 0  if initialization was successful.
  1267. ;        -1 if an error occurred.
  1268. ;
  1269. ; Trashes: D1,D2,D3,A0,A1,A2,A6
  1270. ;
  1271.  
  1272. Initialize
  1273.  
  1274. ; Create Random number table for r256 pseudo-random generator.  The table is
  1275. ; filled with pseudo-random values using a less than efficient (and slow)
  1276. ; algorithm.  I use the current raster beam coordinates as a pseudo-random seed
  1277. ; for this bit of code.  The pseudo-random number generator routine that this
  1278. ; initialization is for is much faster than this rather brutal loop...
  1279.  
  1280.         Lea    RandTable(A4),A0    ;Fetch ptr to prime-index table.
  1281.         Move.W    CHIP+vhposr,D1        ;Prep seed (D1) with cur. raster pos.
  1282.         Move.L    #256-1,D0        ;For all table elements...
  1283.         Add.W    #33247,D1        ;Add something prime to initial seed.
  1284.  
  1285. .MakeRand    MulU    #61141,D1        ;Multiply by a prime #.
  1286.         Swap    D1            ;Use low word of result.
  1287.         Add.W    #33247,D1        ;Add a prime number to low word.
  1288.         Move.W    D1,(A0)+        ;And store result in table.
  1289.         DBra    D0,.MakeRand        ;Go build another table element.
  1290.  
  1291. ; Now open necessary libraries...
  1292.  
  1293.         Lea    DosName,A1        ;Open dos.library.
  1294.         MoveQ.L    #0,D0
  1295.         SYS    OpenLibrary
  1296.         Move.L    D0,DosBase(A4)
  1297.         Beq    .Error
  1298.  
  1299. .1        Move.L    D0,A6            ;Dos_Base in A6.
  1300.         SYS    Output            ;Get standard output handle.
  1301.         Move.L    D0,OutputHandle(A4)    ;Save bptr to it.
  1302.  
  1303.         Move.L    #HelloText,D2        ;Say hello!
  1304.         Bsr    PrintNicely        ;Nicely 'cause you can pause it. :)
  1305.  
  1306.         LOADLIB    SYS            ;SYS_Base in A6.
  1307.         Lea    DTLName,A1        ;Get ptr to "destracker.library"
  1308.         MoveQ    #3,D0            ;Library version 3.0 minimum.
  1309.         SYS    OpenLibrary        ;Open DES-Tracker library.
  1310.         Move.L    D0,DTBase(A4)        ;Save ptr to it.
  1311.         Bne    .MakeInfoBlock
  1312.  
  1313.         Move.L    #NoLibOpenText,D2    ;Couldn't open DES-Tracker.
  1314. .PrintGo    Bsr    PrintNicely
  1315.         Bra    .Error            ;Oh, puh-shaw.
  1316.  
  1317. ; Allocate a file info block structure...
  1318.  
  1319. .MakeInfoBlock    LOADLIB    SYS            ;SYS_Base in A6.
  1320.         Move.L    #fib_SIZEOF,D0        ;Size of the structure.
  1321.         Move.L    #MEMF_CLEAR,D1        ;Clear it out for me, please.
  1322.         SYS    AllocMem        ;Attempt to allocate.
  1323.         Move.L    D0,MyFileInfoBlock(A4)    ;Did it work?
  1324.         Bne    .GetDTSigBits        ;Yup, continue.
  1325.  
  1326.         Move.L    #InsuffMemText,D2    ;Nope, insufficient memory.
  1327.         Bra    .PrintGo        ;Tell user and abort.
  1328.  
  1329. ; Allocate signal bits I'll need to use with DES-Tracker...
  1330.  
  1331. .GetDTSigBits    MoveQ    #-1,D0            ;Give me any available signal bit.
  1332.         Move.B    D0,DTPlayDoneSig(A4)
  1333.         Move.B    D0,DTIterDoneSig(A4)
  1334.  
  1335.         SYS    AllocSignal        ;Allocate a signal bit.
  1336.         Move.B    D0,DTPlayDoneSig(A4)    ;Store number of the bit.
  1337.         Bpl    .2            ;I got a bit number.
  1338.  
  1339. .NoSig        Move.L    #NoDTSigText,D2        ;Couldn't allocate signal bit.
  1340.         Bra    .PrintGo        ;Print error and abort.
  1341.  
  1342. .2        MoveQ    #-1,D0            ;Allocate another signal bit.
  1343.         SYS    AllocSignal
  1344.         Move.B    D0,DTIterDoneSig(A4)    ;Store its bit number.
  1345.         Bmi    .NoSig            ;Didn't get a bit. Damn.
  1346.  
  1347.         Move.L    DTBase(A4),A6        ;DES-Tracker base in A6.
  1348.  
  1349.         SYS    UnloadModule        ;Unload any loaded module.
  1350.  
  1351.         Sub.L    A0,A0            ;Clear any existing search paths.
  1352.         SYS    SetModulePath
  1353.  
  1354.         MoveQ    #0,D0            ;Now grab current global volume
  1355.         Move.B    dtl_GlobalVolume(A6),JukeVolume(A4)    ;and save it.
  1356.         MoveQ    #10,D0            ;Set volume fade rate to
  1357.         SYS    SetVolumeFadeRate    ; nice and slow.
  1358.  
  1359.         LOADLIB    SYS            ;SYS_Base in A6.
  1360.         Sub.L    A0,A0            ;NULL in A0.
  1361.         SYS    FindTask        ;Get pointer to my TCB structure.
  1362.         Move.L    D0,TaskPointer(A4)    ;Save pointer to it.
  1363.  
  1364.         MoveQ    #0,D0            ;Indicate successful init.
  1365.  
  1366. .Leave        Rts                ;And leave.
  1367.  
  1368. .Error        MoveQ    #-1,D0            ;Indicate bad init.
  1369.         Bra    .Leave
  1370.  
  1371. ;---------------------------------------------------------------
  1372. ; DeInitialize routine.  Frees up all used resources.  The Initialize routine
  1373. ; must have been previously called. :)
  1374. ;
  1375. ;   Input: A4 = Pointer to allocated program variable space.
  1376. ;
  1377. ;  Output: Nothing.
  1378. ;
  1379. ; Trashes: D0,D1,D2,D3,A0,A1,A2,A6
  1380. ;
  1381.  
  1382. DeInitialize    Move.L    DosBase(A4),D0
  1383.         Beq    .1
  1384.         Move.L    D0,A6
  1385.  
  1386.         Move.L    LockPointer(A4),D1    ;Get Lock ptr in D1.
  1387.         Beq    .1            ;If 0.L then it doesn't exist.
  1388.         SYS    UnLock            ;Get rid of the lock.
  1389.  
  1390. .1        Move.L    DTBase(A4),D0        ;DES-Tracker base in D0.
  1391.         Beq    .2            ;If 0, then it didn't open.
  1392.         Move.L    D0,A6            ;It is open, put it in A6.
  1393.         SYS    UnloadModule        ;Unload current module.
  1394.         SYS    ResetDefaults        ;Reset library defaults.
  1395.  
  1396.         Move.L    A6,A1            ;Put library base in A1.
  1397.         LOADLIB    SYS            ;Get SYS_Base in A6.
  1398.         SYS    CloseLibrary        ;Close DES-Tracker library.
  1399.  
  1400. .2        LOADLIB    SYS            ;Get SYS_Base in A6.
  1401.         MoveQ    #0,D0
  1402.         Move.B    DTPlayDoneSig(A4),D0    ;Get signal bit I allocated.
  1403.         Bmi    .3            ;The allocation had failed.
  1404.  
  1405.         SYS    FreeSignal        ;Free up the signal.
  1406.  
  1407. .3        MoveQ    #0,D0
  1408.         Move.B    DTIterDoneSig(A4),D0    ;Get other signal bit.
  1409.         Bmi    .4            ;It didn't allocate.
  1410.  
  1411.         SYS    FreeSignal        ;Free up the signal.
  1412.  
  1413. .4        Move.L    MyFileInfoBlock(A4),D0    ;Get ptr to file info block struct.
  1414.         Beq    .5            ;It didn't get alloc'ed.
  1415.         Move.L    D0,A1
  1416.         Move.L    #fib_SIZEOF,D0        ;Size of the structure.
  1417.         SYS    FreeMem            ;Free it.
  1418.  
  1419. .5        Move.L    FilenameBufAddr(A4),D0    ;Get ptr to filename buffer mem.
  1420.         Beq    .6            ;If 0, it doesn't exist.
  1421.         Move.L    D0,A1            ;It exists.
  1422.         Move.L    FilenameBufLen(A4),D0    ;Get length of the buffer.
  1423.         SYS    FreeMem            ;Free up the buffer memory.
  1424.  
  1425. .6        Move.L    PtrTableAddr(A4),D0    ;Get addr of ptr table mem.
  1426.         Beq    .7            ;If 0, then it doesn't exist.
  1427.         Move.L    D0,A1            ;It exists.
  1428.         Move.L    PtrTableLen(A4),D0    ;Get its length.
  1429.         SYS    FreeMem            ;Free up table memory.
  1430.  
  1431. .7        Move.L    DosBase(A4),D0        ;Get pointer to DOS_Base.
  1432.         Beq    .8            ;It didn't open (?)
  1433.         Move.L    D0,A1            ;It is open,
  1434.         SYS    CloseLibrary        ; so close it.
  1435.  
  1436. .8        Move.L    A4,A1            ;Release program variable space.
  1437.         Move.L    #MJUKE_SIZE,D0
  1438.         LOADLIB    SYS
  1439.         SYS    FreeMem            ;No more program variables.
  1440.  
  1441.         Rts                ;All done.
  1442.  
  1443. ;---------------------------------------------------------------
  1444. ; BinToBCD routine.  Converts a binary value (0..99) to a packed BCD value.
  1445. ;
  1446. ;  Input: D0 = Binary value to convert.
  1447. ;
  1448. ; Output: D0.B = The value as a packed binary-coded decimal.
  1449. ;
  1450.  
  1451. BinToBCD    MoveQ    #0,D1
  1452.         Move.B    D0,D1
  1453.         Move.L    D1,D0
  1454.  
  1455.         DivU    #10,D0
  1456.         Asl.B    #4,D0
  1457.         Move.B    D0,D1
  1458.         Swap    D0
  1459.         Or.B    D1,D0
  1460.  
  1461.         Rts
  1462.  
  1463. ;---------------------------------------------------------------
  1464. ; PrintCLI routine.  Prints a text string to the standard output.
  1465. ;
  1466. ;   Input: D2 = Pointer to the text string to print (NOT NULL-terminated).
  1467. ;       D3 = Length of the text string.
  1468. ;
  1469. ;  Output: Nothing.
  1470. ;
  1471. ; Trashes: D0,D1,D2,D3,A0,A1,A2
  1472. ;
  1473.  
  1474. PrintCLI    Move.L    A6,-(Sp)
  1475.         Move.L    OutputHandle(A4),D1
  1476.         Move.L    DosBase(A4),A6
  1477.         SYS    Write
  1478.         Move.L    (Sp)+,A6
  1479.         Rts
  1480.  
  1481. ;---------------------------------------------------------------
  1482. ; PrintNText routine. Prints a NULL-terminated string to the standard output.
  1483. ; Automatically appends a linefeed character to the end of the text output.
  1484. ;
  1485. ;   Input: D2 = Pointer to the NULL-terminated text string to print.
  1486. ;
  1487. ;  Output: Nothing.
  1488. ;
  1489. ; Trashes: D0,D1,D2,D3,A0,A1,A2
  1490. ;
  1491.  
  1492. PrintNText    Bsr    PrintN2Text
  1493.  
  1494.         Move.L    #LineFeed,D2
  1495.         MoveQ    #1,D3
  1496.         Bsr    PrintCLI
  1497.         Rts
  1498.  
  1499. ;---------------------------------------------------------------
  1500. ; PrintN2Text routine.  Prints a NULL-terminated string to the standard output,
  1501. ; just like PrintNText above, but does not output a linefeed at the end.
  1502. ;
  1503. ;   Input: D2 = Pointer to the NULL-terminated text string to print.
  1504. ;
  1505. ;  Output: Nothing.
  1506. ;
  1507. ; Trashes: D0,D1,D2,D3,A0,A1,A2
  1508. ;
  1509.  
  1510. PrintN2Text    MoveQ    #0,D3
  1511.         Move.L    D2,A0
  1512. .1        AddQ    #1,D3
  1513.         Tst.B    (A0)+
  1514.         Bne    .1
  1515.         SubQ    #1,D3
  1516.         Bsr    PrintCLI
  1517.         Rts
  1518.  
  1519. ;---------------------------------------------------------------
  1520. ; PrintNicely routine.  Prints a NULL-Terminated text string one line at a time
  1521. ; (using the PrintCLI routine above) so that the output can be paused by
  1522. ; pressing a key.  It is important to remember that the text string passed to
  1523. ; this routine MUST have a linefeed character immediately preceding the 
  1524. ; terminating NULL.
  1525. ;
  1526. ;   Input: D2 = Pointer to the text string to print (LF & NULL-terminated).
  1527. ;
  1528. ;  Output: Nothing.
  1529. ;
  1530. ; Trashes: D0,D1,D2,D3,A0,A1,A2
  1531. ;
  1532.  
  1533. PrintNicely    MoveM.L    A3/A5,-(Sp)
  1534.         Move.L    D2,A5
  1535.  
  1536. .NextLine    Move.L    A5,A3
  1537.         MoveQ    #0,D3
  1538. .CountChars    AddQ    #1,D3
  1539.         Move.B    (A3)+,D0
  1540.         Beq    .Done
  1541.         Cmp.B    #LF,D0
  1542.         Bne    .CountChars
  1543.  
  1544.         Move.L    A5,D2
  1545.         Move.L    A3,A5
  1546.         Bsr    PrintCLI
  1547.         Bra    .NextLine
  1548.  
  1549. .Done        MoveM.L    (Sp)+,A3/A5
  1550.         Rts
  1551.  
  1552. ;===============================================================
  1553. ; DATA segment.  Consists of static text strings...
  1554.  
  1555.         DATA
  1556.         Even
  1557.  
  1558. ; Run-time messages and substrings...
  1559.  
  1560. CFilesText    Dc.B    "Collecting filenames...",ESC,CEOL,NULL
  1561.  
  1562. LoadingText    Dc.B    LF,'Loading "',NULL
  1563. PlayingText    Dc.B    CR,'Playing "',NULL
  1564. XtraLoadText    Dc.B    '"',ESC,CEOL,NULL
  1565. XtraPlayText    Dc.B    '"',ESC,CEOL,NULL
  1566. LineFeed    Dc.B    LF,NULL
  1567.  
  1568. Spaces        Dc.B    "                      "
  1569. PlayTimeText    Dc.B    "Play Time: ",NULL
  1570. PlayTimeLen    = *-PlayTimeText
  1571. UnitsColon    Dc.B    ":"
  1572. UnitsNoColon    Dc.B    "00"
  1573. UColonLen    = *-UnitsColon
  1574. UNoColonLen    = *-UnitsNoColon
  1575.  
  1576. ; Module load error messages...
  1577.  
  1578. NoMemText    Dc.B    "  --  Insufficient memory!",NULL
  1579. NoMemLen    = *-NoMemText
  1580. NotFoundText    Dc.B    "  --  File not found!",NULL
  1581. NotFoundLen    = *-NotFoundText
  1582. BadFormatText    Dc.B    "  --  Unrecognized module format!",NULL
  1583. BadFormatLen    = *-BadFormatText
  1584.  
  1585. ; Exit error messages...
  1586.  
  1587. JukeDoneText    Dc.B    LF,"MicroJuke is finished!",LF,LF,NULL
  1588. JukeAbortText    Dc.B    LF,"*** Jukebox aborted. ***",LF,LF,NULL
  1589. LibOwnedText    Dc.B    LF,"Library has been locked!  Aborting ...",LF,LF,NULL
  1590.  
  1591.  
  1592. ; Parsing error messages...
  1593.  
  1594. BadPathText    Dc.B    LF,"ERROR: Illegal path/file specification",LF,LF,NULL
  1595. NotScriptText    Dc.B    LF,"ERROR: Not a script file.",LF,LF,NULL
  1596. NoDTSigText    Dc.B    LF,"ERROR: Couldn't allocate signal bit!",LF,LF,NULL
  1597. InsuffMemText    Dc.B    LF,"ERROR: Insufficient memory",LF,LF,NULL
  1598. NoFilesText    Dc.B    LF,"ERROR: Path is empty",LF,LF,NULL
  1599. ReadErrText    Dc.B    LF,"ERROR: Couldn't read file",LF,LF,NULL
  1600. NoLibOpenText    Dc.B    LF,"ERROR: You need destracker.library v3.0",LF,LF,NULL
  1601.  
  1602.  
  1603. ; Title, copyright, and usage text strings...
  1604.  
  1605. HelloText    Dc.B    LF,"destracker.library Micro-Jukebox",LF
  1606.         Dc.B    "Copyright (c)1993 by Darren Schebek",LF
  1607.         Dc.B    "Version 2.0  Dec 18, 1993",LF,LF,NULL
  1608.  
  1609. ComText        Dc.B    "usage: mjuke [-n] {pathname|scriptname}",LF,LF
  1610.         Dc.B    "where: -n = Do not shuffle selections.",LF,LF
  1611. IPlayText    Dc.B    $0D,"CTRL-C aborts jukebox.",LF
  1612.         Dc.B    "CTRL-F skips to next song.",LF,LF,NULL
  1613.  
  1614.  
  1615. ; Library name strings...
  1616.  
  1617. DosName        Dc.B    "dos.library",0
  1618. DTLName        DTLIBNAME
  1619.  
  1620.         Even
  1621.  
  1622.         End
  1623.