* MultiPlayer
* Copyright (C) 1992 Bryan Ford
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* I (the author of MultiPlayer) can be contacted on the Internet at
* "bryan.ford@m.cc.utah.edu".  See "Player.doc" for other addresses.
*
* $Id: misca.asm,v 3.1 92/05/25 07:52:21 BAF Exp $
*

        include "exec/types.i"
        include "exec/nodes.i"
        include "exec/interrupts.i"
        include "exec/funcdef.i"
        include "exec/execbase.i"
        include "exec/exec_lib.i"
        include "intuition/intuition_lib.i"
        include "intuition/intuitionbase.i"
        include "dos/dos.i"
        include "bry/macros.i"
        include "player.i"

        xref    _LinkerDB,_SysBase,_sysflags,_procpt,_songpos,_songlen
        xref    _curmod,_fadevol,_fadeinc,playervolume
        xref    _sysflags,_playing,_songtime,_songendtime
        xref    _spectrum,_flashactive,_flashytaskptr
        xref    scopechandata,notetab,scrollline,scrolllines,_scopeinterval

        xdef    intsongpos,intrepeat,intflashnote,intflashsample,intflashscroll,clearflash
        xdef    _vblankint,_scopeinterval

        xdef    ToUpper,ToUpperLong
        xdef    @ToUpper,@ToUpperLong

        xdef    StripBlanks,Stricmp
        xdef    @StripBlanks,@Stricmp

        xdef    IsAlpha,@IsAlpha

        xdef    RandomSeedClock,RandomSeed,RandomLong,Random
        xdef    @RandomSeedClock,@RandomSeed,@RandomLong,@Random

        code    text

*** IsAlpha - Test whether or not a character is alphabetic
* d0.b = character
* Returns: d0/CCR = Nonzero if character is alphabetic
* All other registers remain unchanged
IsAlpha:
@IsAlpha:
        cmp.b   #'a',d0
        blo.s   \not
        cmp.b   #'z',d0
        bls.s   \is
        cmp.b   #$e0,d0
        blo.s   \not
        cmp.b   #$fd,d0
        bhi.s   \not
        cmp.b   #$f7,d0
        beq.s   \not
\is     moveq   #1,d0
        rts
\not    moveq   #0,d0
        rts

*** ToUpper - Return the uppercase of an ASCII character if it's a letter
* d0.b = Character
* Returns: d0.b = Upper case letter, or same character
* Top part of d0 and all other registers are left unchanged
ToUpper:
@ToUpper:
        cmp.b   #'a',d0
        blo.s   9$
        cmp.b   #'z',d0
        bls.s   8$
        cmp.b   #$e0,d0
        blo.s   9$
        cmp.b   #$fd,d0
        bhi.s   9$
        cmp.b   #$f7,d0
        beq.s   9$
8$      add.b   #'A'-'a',d0
9$      rts

*** ToUpperLong - Return the uppercase of four ASCII characters in a longword
* d0.l = Four characters
* Returns: d0.l = Upper case of each character
* All other registers remain unchanged
ToUpperLong:
@ToUpperLong:
        bsr.s   ToUpper
        swap    d0
        bsr.s   ToUpper
        ror.l   #8,d0
        bsr.s   ToUpper
        swap    d0
        bsr.s   ToUpper
        rol.l   #8,d0
        rts

*** StripBlanks - Strip blanks from the beginning and end of a string
* a0 = Pointer to string
* Returns:
* a0/d0/CCR = Pointer to first non-blank character in string, NULL if string is empty
* Trailing blanks will be chopped off with a null
StripBlanks:
@StripBlanks:
1$      cmp.b   #' ',(a0)+              ; Find first non-blank
        beq.s   1$
        subq.l  #1,a0

        move.l  a0,a1                   ; Find end of string
2$      tst.b   (a1)+
        bnz.s   2$
        subq.l  #1,a1

3$      cmp.l   a0,a1                   ; Backtrack past all blanks
        beq.s   \empty
        cmp.b   #' ',-(a1)
        beq.s   3$

        clr.b   1(a1)                   ; Chop off end of string

        move.l  a0,d0
        rts

\empty:
        moveq   #0,d0                   ; String turned out empty
        move.l  d0,a0
        rts

*** Stricmp - Case-insensitive compare of two strings
* a0 = String A
* a1 = String B
* Returns: D0, CCR = <0 if A<B, ==0 if A==B, >0 if A>B
Stricmp:
@Stricmp:
1$      move.b  (a0)+,d0
        bsr     ToUpper
        move.b  d0,d1
        move.b  (a1)+,d0
        bsr     ToUpper
        cmp.b   d0,d1
        blo.s   2$
        bhi.s   3$
        tst.b   d1
        bnz.s   1$
        moveq   #0,d0
        rts
2$      moveq   #-1,d0
        rts
3$      moveq   #1,d0
        rts

        xref    _IntuitionBase

*** RandomSeedClock - Seed the random-number generator from the system clock
RandomSeedClock
@RandomSeedClock
        movem.l d0/d1/a6,-(sp)
        move.l  _IntuitionBase(a4),a6
        cmpi.w  #36,LIB_VERSION(a6)
        bhs.b   \time20
        movem.l ib_Seconds(a6),d0/d1
        movem.l d0/d1,(sp)
        bra.b   \out
\time20
        ml      sp,a0
        lea     4(sp),a1
        jsr     _LVOCurrentTime(a6)
\out
        movem.l (sp)+,d0/d1/a6
        ; fall through...
*** RandomSeed - Seed the random-number generator
* d0 = Seed longword #1
* d1 = Seed longword #2
RandomSeed
@RandomSeed
        add.l   d0,d1        ;   user seed in d0 and d1
        movem.l d0/d1,rnd(a4)
        ; fall through...
*** RandomLong - Get a 32-bit random number
* Returns: d0 = Random number (lower bits are most random)
RandomLong
@RandomLong
        movem.l d2-d3,-(sp)
        movem.l rnd(a4),d0/d1 ;   D0=LSB's, D1=MSB's of random number
        andi.b  #$0e,d0      ;   ensure upper 59 bits are an...
        ori.b   #$20,d0      ;   ...odd binary number
        move.l  d0,d2
        move.l  d1,d3
        add.l   d2,d2        ;   accounts for 1 of 17 left shifts
        addx.l  d3,d3        ;   [D2/D3] = RND*2
        add.l   d2,d0
        addx.l  d3,d1        ;   [D0/D1] = RND*3
        swap    d3           ;   shift [D2/D3] additional 16 times
        swap    d2
        move.w  d2,d3
        clr.w   d2
        add.l   d2,d0        ;   add to [D0/D1]
        addx.l  d3,d1
        movem.l d0/d1,rnd(a4);   save for next time through
        move.l  d1,d0        ;   most random part to D0
        swap    d0
        movem.l (sp)+,d2-d3
        rts

*** Random - Get a 16 bit random number from 0 to limit-1
* d0.w = Limit
* Returns: d0 = Random number 0 to limit-1
Random
@Random
        move.w  d2,-(sp)
        move.w  d0,d2        ;   save upper limit
        beq.b   10$          ;   range of 0 returns 0 always
        bsr.b   RandomLong   ;   get a longword random number
        swap    d0
        clr.w   d0           ;   use upper word (it's most random)
        swap    d0
        divu.w  d2,d0        ;   divide by range...
        clr.w   d0           ;   ...and use remainder for the value
        swap    d0           ;   result in D0.W
10$     move.w  (sp)+,d2
        rts


*** intsongpos - Set a new song position, callable from an interrupt
* d0.w = Position in song
* d1.w = Length of song
* Returns ALL registers saved (convenience for internal players)
intsongpos:
        movem.l d0-d1/a0-a1/a6,-(sp)
        lea     _LinkerDB,a1
        move.l  d0,_songpos(a1)         ; Store the new values
        move.l  d1,_songlen(a1)
        bset    #SB_TIMERUP,_sysflags(a1)
        bra.s   intsig

*** intrepeat - Signal the MultiPlayer task that the module is repeating
* Returns ALL registers saved (convenience for internal players)
intrepeat:
        movem.l d0-d1/a0-a1/a6,-(sp)
        lea     _LinkerDB,a1

        cmpi.b  #PM_OVEROVER,_playmode(a1)
        beq.b   intout

        bset    #SB_REPEAT,_sysflags(a1)

        ml      _curmod(a1),d0
        bz.b    intsig
        ml      d0,a0
        tst.w   mn_volume(a0)
        bmi.b   intsig
        btst    #MNB_ENDFADE,mn_flags(a0)
        bz.b    intsig
        mw      #128,_fadevol(a1)
        mw      #-1,_fadeinc(a1)
        bra.b   intout

intsig:
        move.l  _SysBase(a1),a6         ; Signal the main task
        move.l  _procpt(a1),a1
        move.l  #SIGBREAKF_CTRL_F,d0
        jsr     _LVOSignal(a6)
intout
        movem.l (sp)+,d0-d1/a0-a1/a6
        rts


*** vblankfunc - Vertical blank function
vblankfunc:
        ml      a1,a5

        tst.b   _playing(a5)            ; Don't do anything unless we're playing something
        bz.b    \out

        mw      _fadevol(a5),d0         ; Perform any fading in progress
        bgt     \fade
\fadeback

        movem.l _songtime(a5),d0/d1     ; Update the song time
        addq.l  #1,d0
        move.l  d0,_songtime(a5)
        tst.l   d1
        bz.b    \endback
        cmp.l   d1,d0
        bhs     \endhit
\endback

        subq.b  #1,seccnt(a5)           ; Signal the main task every second
        bz      \second
\secback

        subq.b  #1,flashycnt(a5)        ; Signal the main task every flash
        bnz.b   \flashback
        move.b  #FFperF,flashycnt(a5)
        tst.b   _flashactive(a5)        ; Only if flashy window is operative
        bnz     \flashy
\flashback

\out
        moveq   #0,d0
        rts

\fade
        add.w   _fadeinc(a5),d0
        bz.b    \fadedone
        cmp.w   #128,d0
        bls.b   \fadenotdone
        mq      #-1,d0
\fadedone
        mw      d0,_fadevol(a5)
        bset    #SB_FADEDONE,_sysflags(a5)
        pea     \fadeback(pc)
        bra.b   \int
\fadenotdone
        mw      d0,_fadevol(a5)
        exg     a4,a5
        bsr     playervolume
        exg     a4,a5
        bra.b   \fadeback

\second:                                ; Signal that a second has elapsed
        move.l  _SysBase(a5),a1
        move.b  VBlankFrequency(a1),seccnt(a5)
        bset    #SB_SECOND,_sysflags(a5)
        pea     \secback(pc)

\int:
        move.l  _procpt(a5),a1          ; Signal the main task
        move.l  #SIGBREAKF_CTRL_F,d0
        move.l  _SysBase(a5),a6
        jmp     _LVOSignal(a6)

\endhit:                                ; Signal that we (may be) repeating
        btst    #SB_REPEAT,_sysflags(a5)
        bnz     \endback
        pea     \endback(pc)
        bra     intrepeat

\flashy                                 ; Signal to update the flashy window
        mq      #4-1,d0                 ; Update the scope channels
        lea     scopechandata+scd_SIZEOF*4(a5),a0
\scopechan
        suba.w  #scd_SIZEOF,a0
        tst.w   scd_len(a0)
        bz.b    \scopechandone
        mw      scd_pos(a0),d1
        add.w   scd_inc(a0),d1
        bcs.b   \scopechanovf
        mw      d1,scd_pos(a0)
        add.w   #SCOPEWIDTH,d1
        cmp.w   scd_len(a0),d1
        bhi.b   \scopechanovf
\scopechandone
        dbra    d0,\scopechan

        move.l  _flashytaskptr(a5),d1   ; Signal the flashy task
        bz      \flashback
        ml      d1,a1
        move.l  #SIGBREAKF_CTRL_F,d0
        move.l  _SysBase(a5),a6
        pea     \flashback(pc)
        jmp     _LVOSignal(a6)

\scopechanovf                           ; Got to the end of the sample
        mw      scd_rep(a0),scd_pos(a0)
        bnz.b   \scopechandone
\scopechanoff                           ; Turn channel off
        clr.w   scd_len(a0)
        bra.b   \scopechandone


*** clearflash - Clear the flashy display
clearflash
        lea     _spectrum(a4),a0        ; Clear the spectrum display
        mq      #SPECTRUMRES/4-1,d0
1$      clr.l   (a0)+
        dbra    d0,1$
        clr.w   scopechandata+scd_len(a4) ; Stop the scope
        clr.w   scopechandata+scd_SIZEOF+scd_len(a4)
        clr.w   scopechandata+scd_SIZEOF*2+scd_len(a4)
        clr.w   scopechandata+scd_SIZEOF*3+scd_len(a4)
        clr.w   scrolllines(a4)         ; Clear the note scroller
        st.b    _flashactive(a4)
        rts

*** intflashnote - Start a new note in the spectrogram
* d0.b = Channel (0-4)
* d1.b = Note frequency (0-255)
* d2.b = Volume (0-64)
intflashnote
        push    d0-d2/a0/a4
        lea     _LinkerDB,a4

        tst.b   _flashflags(a4)
        bz.b    \out

        lsr.b   #4,d1                   ; Set the spectrogram
        ext.w   d1
        lsr.b   #1,d2
        lea     _spectrum(a4),a0
        bmi     \out
        cmp.b   0(a0,d1.w),d2
        bls.b   \nomiddle
        mb      d2,0(a0,d1.w)
\nomiddle
        lsr.w   #1,d2
        cmp.b   -1(a0,d1.w),d2
        bls.b   \noleft
        mb      d2,-1(a0,d1.w)
\noleft
        cmp.b   1(a0,d1.w),d2
        bls.b   \noright
        mb      d2,1(a0,d1.w)
\noright

        st.b    _flashactive(a4)        ; There's now something to display
\out
        pop     d0-d2/a0/a4
        rts

*** intflashsample - Start a new note in the scope
* d0.w = Channel (0-4)
* a0 = Sample data pointer
* d1.w = Period at which this sample will play
* d2.w = Sample length in bytes (0 = turn channel off)
* d3.w = Repeat part length in bytes (0 = no repeat)
intflashsample
        push    d0-d4/a1/a4
        lea     _LinkerDB,a4

        tst.b   _flashflags(a4)
        bz.b    \outout

        ext.w   d0

        cmp.w   #4,d0                   ; Protection
        bhs.b   \out
        tst.w   d1
        bz.b    \out

        mw      d0,d4                   ; Find the appropriate channel data structure
        lea     scopechandata(a4),a1
        lsl.w   #4,d4
        add.w   d4,a1

        mw      d2,scd_len(a1)
        bz.b    \out
        ml      a0,scd_sample(a1)
        clr.w   scd_pos(a1)
        sub.w   d3,d2
        bnz.b   \smallrep
        mq      #1,d2
\smallrep
        cmp.w   #2,d3
        bhi.b   \hasrep
        mq      #0,d2
\hasrep mw      d2,scd_rep(a1)
        cmp.w   scd_per(a1),d1
        beq.b   \nodiv
        ml      _scopeinterval(a4),d0
        divu.w  d1,d0
        mw      d1,scd_per(a1)
        mw      d0,scd_inc(a1)
\nodiv
*        mq      #SCOPEWIDTH,d0          ; Prevent static displays for short samples
*        add.w   scd_inc(a1),d0
*        cmp.w   scd_len(a1),d0
*        blo.b   \out
*        mw      #SCOPEWIDTH,scd_inc(a1)
\out
        st.b    _flashactive(a4)
\outout
        pop     d0-d4/a1/a4
        rts

*** intflashscroll - Change note scroller position
* Must have already set notetab and scrollmod
* d0.w = Current line number
* d1.w = Number of lines in this pattern
intflashscroll
        mw      d0,scrollline
        mw      d1,scrolllines
        st.b    _flashactive
        rts

        data    __MERGED

        xdef    _prefs,_prefsend

_prefs                                  ; All the saved preferences variables
        dcxd.b  playmode,PM_SEQUENCE
        dcxd.b  showmode,SM_SEQUENCE0

        dcxd.w  zoomwinleft,$135
        dcxd.w  zoomwintop,$a

        dcxd.w  progwinleft,$13a
        dcxd.w  progwintop,$25

        dcxd.w  prefswinleft,$3
        dcxd.w  prefswintop,$23

        dcxd.w  spectrumwinleft,$5
        dcxd.w  spectrumwintop,$a

        dcxd.w  scopewinleft,$99
        dcxd.w  scopewintop,$a

        dcxd.w  channelwinleft,$135
        dcxd.w  channelwintop,$a

        dcxd.b  flashflags,0
        dcxd.b  keepprog,0

        dcxd.w  tinywinleft,-1
        dcxd.w  tinywintop,0

        dcxd.w  qscopewinleft,$99
        dcxd.w  qscopewintop,$42

        dcxd.w  notewinleft,$5
        dcxd.w  notewintop,$42

        dcxd.w  settingswinleft,$2
        dcxd.w  settingswintop,$3

        dcxd.b  pausefade,1

        dsxd.b  hotkey,30+1

        dcxd.w  mspeed,50
        dcxd.w  mvolume,100
        dcxd.w  mbalance,0

        dcxd.w  modfrleft,0
        dcxd.w  modfrtop,0
        dcxd.w  modfrwidth,0
        dcxd.w  modfrheight,0

        dcxd.w  progfrleft,0
        dcxd.w  progfrtop,0
        dcxd.w  progfrwidth,0
        dcxd.w  progfrheight,0

        dcxd.w  infowinleft,2
        dcxd.w  infowintop,80

        dcxd.b  appicon,0

        dsxd.b  mainfontname,0
        dc.b    "topaz.font",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0          ; 31 bytes
        dsxd.b  listfontname,0
        dc.b    "topaz.font",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0          ; 31 bytes
        dcxd.w  mainfontsize,8
        dcxd.w  listfontsize,8

        dcxd.w  fontreqleft,0
        dcxd.w  fontreqtop,0
        dcxd.w  fontreqwidth,0
        dcxd.w  fontreqheight,0

        dcxd.b  contprogonerr,0

        dcxd.b  appicona,0

_prefsend

                even

rnd             ds.l    2

_vblankint:
                dc.l    0,0
                dc.b    NT_INTERRUPT,-10
                dc.l    intname
                dc.l    _LinkerDB
                dc.l    vblankfunc

intname         dc.b    "MultiPlayer VBlank",0

seccnt          dc.b    1
flashycnt       dc.b    1

        end
