TECHNICAL INFO ABOUT THE PLAYERS
--------------------------------



GENERALITY
----------

The 3 players have the same main code. The ASM is the most optimised. The  BASIC
and  INTERRUPTION  player  uses  the  system  vectors  to  trigger   the   300hz
interruption, and are a bit slower, because it must  saves  some  registers  the
system needs. The INTERRUPTION takes more memory, because it must interpret  the
frequency written in the song, and play the song according to it.


RESSOURCE CONSUMPTION
---------------------
I coded the players as best as I could. The ASM player can take a maximum of  34
lines of machine-time, but will generally raise up to 30, for  a  size  of  more
than 2kb in memory ; some of it can be saved, read below.  The  player  uses  no
buffer, there's no more memory than the Player plus the song to be used.

The 2 others players takes a bit more, especially the INTERRUPTION player.

Players size :
ASM   = &98B
BASIC = &9A6
INTER = &9FC




MEMORY MANAGMENT
----------------
If you use the ASM player, you can choose  to  put  the  player  and  the  music
everywhere you want. The only condition is that the player can access the  whole
music data when being called. If the music and/or  the  player  is/are  in  bank
(completely or partially), the bank must be opened before calling the player.

The players don't modify the Stack, the interruption flag/mode, or  any  chipset
registers (except of the Z80 and PSG,  of  course).  No  barbarian  tricks  (out
(0),a) or anything like that. Every Z80 registers are corrupt.

Using the INTERRUPT Player gives more constraints : you can't close  a  bank  if
the music/player is inside, or open a bank if the music/player is in the central
RAM. On top of that, the player must be assembled from #4000, and of course must
not reach the HIMEM of the Basic (=#A67B. HIMEM decreases when ROMs are  loaded.
In order to stay compatible with most configuraion, try not to load your  player
too high in memory). However, the generator won't prevent you from  doing  this.
Don't forget that these limitations concern the player, but  the  song  data  as
well.






TWEAKING THE PLAYER or THE SONG
-------------------------------

Unlike the AMC and the Sountrakker, you're given some  addresses  you  can  play
with, so that you can modify a bit the player to make it fit your needs,  or  to
get some interesting values.

ALL addresses given are in fact OFFSETS from the beginning of the  song  or  the
player. They're all in hexadecimal.


-----
In the header of a song are... values ! I won't give you all of  them,  it'd  be
useless to you, but some are really valuable.


OFFSETS
0-3        DB "SK10"
           Just a little ASCII tag for rippers to detect STK songs easier :).
4-5        DW Base Address of the Song. #00,#04 if the song has to be loaded in
           #4000, for example.
6          DB No of the Channel used by Special Tracks Digidrums. 1,2 or 3.
           Shouldn't be really useful, but it's here.
7-8        DW Song Frequency in Hertz (13 (=#D), 25 (=#19), 50 (=#32),
           100 (=#64), 150 (=#96) or 300 (=#12C)).
           Only used by the INTERRUPTION player when initialising.

That's all. You're not allowed to search elsewhere :).



-----
You can also find the base address of the player. If it's a ASM or BASIC player,
it is coded at the offsets +#9/+#A (low byte/high).
For an INTERRUPTION player, look at the offsets +6/+7 (low/high).




-----
If you want to bypass the sending of values to the PSG, poke a &C9 here...

ASM        BASIC        INTER
&3AA       &3B3         &406

This is useful if you want, for example, to make digidrums (look at the DIGIDRUM
section below). Also could be used if you want to generate a YM-list in order to
have the fastest music code ever (but also a great amount of memory used by  the
list !). In this case, 'hearing' the song is useless, you would just have to run
the player, get the values, code them and restart until the song is  over,  then
read the YM values with your own player.

The normal value in this address is a &C3 (JUMP).

WARNING, there's a special case about the Register 7, its value is given through
A before the jump ! So if you bypass the JUMP, but  jump  to  that  code  later,
don't forget to set A with the Register 7 value you took care to save...


!!! WARNING !!! IF you set a RET with the BASIC or INTER players, it will  crash
! Why ? Because these players saves some registers  (by  PUSHing  them)  at  the
beginning, and recover them (by POPing) after having  sent  PSG  values.  So  by
bypassing it, the registers are never recovered, the RET  will  jump  at  a  bad
address. So instead of setting a RET, we'll jump directly to the code that  will
recover the registers. You must poke this :
BASIC : poke #C3 (JUMP) in #3b3, and (Address of the player + #85D)  next.  (low
byte then high byte).
INTER : poke #C3 (JUMP) in #406, and (Address of the player + #8B0)  next.  (low
byte then high byte).


BUT WARNING AGAIN !! By doing this we will be unable to get the Reg7  value,  as
it's given through A to the code that send PSG values (see below). We must  save
it first. You can do it where you want. The address #1F is used in this example,
because no one use it. But we must do it outside the player, because  this  code
addition will overflow on some other parts of the player. SO, we will  our  code
in #3C, no one goes here. Here, we save A in #1F, and then JUMP to the code that
recovers players :

Poke in ADPLAYER+#3B3 (BASIC) ou ADPLAYER+#406 the following bytes : #C3 #3C #00
(Jump #003c)
In #3c, poke this : #32,#1F,#00, the two last bytes being where you want to save
Reg7. Then poke the 3 bytes discussed above  (the  1st  WARNING).  Now  you  can
recover the Reg7 from #1f at any time.

I know it's really bad trick, I will  perhaps  improve  that  in  futur  player,
however it's not very useful except in the YM case. And most BASIC coders  won't
ever need it, so...


If you use the ASM player : don't bother with this crap, poke a RET as  said  at
the beginning, and save A !



----
Now comes the address of the code that send values to the PSG...

ASM        BASIC       INTER
&6CB       &6D4        &727

WARNING : The value of the REGISTER 7 is GIVEN through A !



----
Then, here are the addresses where you can get the values sent to the PSG. So if
you need to code an equalizer, you can get all PSG values here. Of  course,  the
addresses are various according to the players. They are quite  useless  if  you
use the INTERRUPT player, but hey, here are they anyway :

          ASM        BASIC        INTERRUPT
R0        &6D6        &6DF        &732         Low Byte Frequency, Channel 1
R1        &6F1        &6FA        &74D         High Byte Frequency, Channel 1
R2        &727        &730        &783         Low Byte Frequency, Channel 2
R3        &742        &74B        &79E         High Byte Frequency, Channel 2
R4        &778        &781        &7D4         Low Byte Frequency, Channel 3
R5        &793        &79C        &7EF         High Byte Frequency, Channel 3

R6        &7C9        &7D2        &825         Noise
R7        NOT CODED                            Mixer
(R7OLD    &7E5        &7EE        &841)

R8        &70C        &715        &768         Volume, Channel 1
R9        &75D        &766        &7B9         Volume, Channel 2
R10       &7AE        &7B7        &80A         Volume, Channel 3

R11       &7FE        &807        &85A      Low Byte Hard Enveloppe Frequency
R12       &819        &822        &875      High Byte Hard Enveloppe Frequency
R13       &834        &83D        &890      Hardware Enveloppe Shape


The Register 7 is NOT automodified ! It is GIVEN in A  to  the  code  that  send
values to the PSG (see above).

NOTE : If you add 2 to each address, you get the PREVIOUS value of the register.
BEFORE sending a value to a register, the new value is compared to the  previous
one. If they are equal, there's no need to send it  again,  hence  a  saving  of
machine time. The exception is the Register 13, which is managed in any case  if
RETRIG is >0. (Note that I gave you the address of the old value of Reg7,  since
the new one is given through A).

RETRIG :
ASM     BASIC   INTER
&83B    &844    &897


To force the sending of a velur, you can put a 255 in the previous value,  since
this value est not used by most registers (and I never noticed anything  strange
from the frequency registers).






----
SAVE A BIT OF MACHINE TIME ?

Errr... That's not possible, sorry :). I hope to  release  some  more  optimised
versions later, coz I know where the machine time leaks are.  However,  you  may
notice that the first frame takes a lot of machine time, that's because all  PSG
registers are refreshed, thus sent to the PSG (Their 'previous value' is set  to
255). To avoid this, AFTER initialisation, you can read the current  PSG  values
(through the PPI, not through the player !), and write them in their  respective
'previous values' address in the player. Thus, only the changing values will  be
played in the first frame.




----
FADE IN/OUT

It's possible to create a fade in or out in a Starkos song. However, you must do
it yourself... Indeed, in order to  save  memory  and  machine  time,  I  didn't
explicitly write a function to allow  that.  But  with  a  bit  of  trick,  it's
possible.

You simply have to bypass the values sending to the PSG.  Play  the  song  once,
read the values of the Registers 8,9 and 10, and decrease them of 1,2,3... to 15
(for a fade out), then jump to the code that send the values  (Don't  forget  to
save/restore A which contains the Register 7 value). Take care of overflows.  If
the volume is below 0, set it to 0. If it gets higher than  15  (during  a  fade
in), set it to 15.

Hardbasses are more problematic, that's why you will choose by yourself how  you
want to handle them. Their volume is ALWAYS 16 with Starkos.  You  can  decrease
them too, so they will lost their 'hard'  status,  which  will  probably  sounds
strange and flat. You can also ignore them, but only the  'normal'  sounds  will
fade. You can also cut them, simply. The choice is yours...




----
DETECTING THE END OF THE SONG

You want to detect the end of the song ? For example, to convert SKS  file  into
YM (for example  !)  ????  Ok,  this  is  possible.  Once  again,  no  hardcoded
functions. This one is really crap tricking. All apologize for this, but I found
no other way to do it. Yet it remains very simple  to  code,  just  follow  this
algorithm :

(xxxx)16 means '16 bits number found at the offset xxxx'.
        (byte at xxxx=low byte   byte at xxxx+1=high byte)

(xxxx)8 means '8 bits number found at the offset xxxx'.

ASM                                                      BASIC
---                                                      ---
STATE=0

LOOP:
Play your song

If STATE=0 : if (009D)16 <> (283)16 then goto LOOP       (00A6)16 <> (028C)16
             STATE=1 : goto LOOP

If STATE=1 : if (0270)8 <> 0 then goto LOOP              (0279)8 <> 0
             if (0015)8 <> (0017)8 then goto LOOP        (001E)8 <> (0020)8
             STATE=2 : goto LOOP

If STATE=2 : if (0015)8 <> (0017)8 then goto LOOP        (001E)8 <> (0020)8
             STATE=0
             Song End is now !


----
KILLING USELESS PARTS OF THE PLAYER

You're short in memory and you want to save it as much as possible ? STarKos  is
made for you, really :). In the BASIC and ASM players, the INITIALIZE code is at
the end of the player. So you can simply destroy it. Just  before  is  the  STOP
MUSIC code, but it's really tiny so you won't get much.

These addresses show you from where these codes begin :
        ASM        BASIC
STOPMUS &854        &866
INITMUS &870        &88B


Don't forget that you can also completely forget about the INITIALIZE  code,  by
loading the player, your song, calling the  INITIALIZE  code,  and  saving  your
player (without the INITIALIZE code). This way, the player already got  all  the
inits he needs. Of course, you won't be able to re-init the  song,  or  use  the
player for another music, but demomakers will surely find this very useful.




----
DIGIDRUMS

SO. STarKos players manage digidrums in a very flexible way. It reads the  song,
and if a digidrum is detected in the song, returns  the  number  of  the  sample
(from 1-#3f, 0=No digidrum) and the channel where  to  play  it  (1/2/3).  Basic
can't play digidrums, but I guess you can use digidrums to trigger  some  events
(it works in ASM too).
Digidrum detection shouldn't be used in any case  under  the  INTERRUPT  player,
because you might miss some events, but hey, you do what you want...


The values must be read here :

Sample Number :
ASM       BASIC     INTER
&B        &B        &8

Channel Number:
ASM       BASIC     INTER
&C        &C        &9




How to play digidrums ?
-----------------------
I won't tell technically how to play digidrums, this is  not  the  purpose  this
text (read Demoniak 5, or Quasar). However, I'd  like  to  talk  about  how  you
should manage the digidrums to play them as cleanly as possible.

The most common, easiest, yet less effective way to play digidrum  would  be  to
run the player one time, test if a digidrum must be played, and play it (cutting
the noise and sound of the channel). You can improve this.

Check DIGIDRUM.DAM / DIGIDRUM.MXM.

The method I use is a bit more complex, but provides a better sound quality.  Of
course, you're free to code it the way you want !

The main point is that we play the music but don't send any value to the PSG. If
a digidrum is detected, we prevent the player from modifying the  PSG  registers
linked to the channel where we're going to play the sample (in  fact,  only  the
VOLUME is concerned). We also cut the noise and sound of the digichannel, in the
Register 7.
Now we're ready to send the modified values to the PSG. Then, we  just  have  to
play the sample, and that's all for this frame.

In the source example, the samples addresses and size are given in a table,  but
we test if the sample is over only at the end of the frame, so if you  play  160
samples per frame (160*50 = 8000Khz, which is good for  a  digidrum),  and  your
sample is 350 bytes long, the CPC will actually play 480 bytes  (160*  3).  This
low accuracy doesn't matter when playing digidrums ; Testing the end after  each
byte would not provide any better result, and would take much more machine time.



----
Ok, enough tweaking for today, it drives the player mad.
