                         HerMiniSID player README
                         ------------------------


Intro:

 I created this tiny player which is even smaller than FlexSID players, because
I was well inspired by 4-Mat's wonderful 256byte music called 'Pocket Universe'.


Features:

 There's no editor this time, and there's no point for an editor anyway, because
to make music that small the flexibility of an assembler (in this case 64tass)
must be utilized by the musician. That said, the player is still an universal
one which is not tailored to any specific tune in mind, and it has the typical
features that are supported by typical normal-sized players. These are:

-Orderlists (called Tracks in this case) with pattern-selection commands for
 sequencing, and additional orderlist-commands like jump, transpose and
 instrument-selection

-Patterns that can contain notes (base-pitch), note-duration changes
 and instrument-selections (which are remembered for upcoming notes)

-Instruments (that can be called 'effects' too depending on their contents)
 The instruments are command-tables where instructions can be given to
 set SID registers, arpeggio+waveform, absolute-pitch+waveform and end/jump

To see in more precise details what commands and values are used check file
'macrolib.s' which contains macros to ease the music-editing, and checking
the music.s examples can be enlightening as well.


Editable music source-files and compilation:

To write music you don't have to modify the player.asm or macrolib.s
source-code, every aspects of the music can be configured and created in
the source-file called 'music.s'. If you want to have a custom name for it,
rename it to something else at the end of 'player.asm'. To set a new title
for your music you should edit the title fields in HerMiniSID.asm
 To assemble the listenable music (exe.prg and .sid) and includeable (.prg) you
can use the 'make' tool, but if you don't have it on your system the Makefile
contains the commands to assemble it, and also some settings at its beginning
for testing and release. 64tass assembler is needed in any case, and to get an
executable output-file a single commandline should be enough:

64tass --m6502 -C -D EXPORTTYPE=1 -D RELEASE=1 HerMiniSID.asm -o HerMiniSid.prg


Music-data editing:

Making music in assembler is hard enough because you can't hear a note when
you type it into the source-file. Having a well trained musical hearing can be
beneficial, but using a piano keyboard or a tracker program with note-jamming
can help to guess what notes are to be typed or to test how some patterns would
sound together. (Checking melodies in FlexSID is a possible option here.)

The player can be configured by the definitions at the top of music.s, and you
can decide which player features you need. Depending on the enabled features the
player can take minimum 120 bytes to about a maximum of 150 bytes.

Regions started with labels called 'TRACKS', 'PATTERNS' and 'INSTRUMENTS' can be
used independently or merged/combed for saving space, there's only one rule to
adhere to: Tracks (pattern-lists/orderlists) for all channels together can't
exceed 128 bytes (counted from TRACKS baseaddress), patterns alltogether can't
take more than 128 bytes (counted from PATTERNS label), and instruments
alltogether should fit into 64 bytes to be callable from patterns. If you plan
to set instruments from Tracks (pattern-lists), those instruments should be in
the 0..31 range.

This means that your tune (including player-code) may range from about 150 bytes
(empty tune) to as 'big' as 471 bytes (totally filled up with content).
So HerMiniSID is usable to create tunes that fit into the 256 or 512byte music
categories. 256byte category (100..120 bytes available for the whole music data)
is very hard to do, it's hard as hell to make meaningful music of that size. You
can run out of bytes quite fast even with a carefully overthought arrangement.

One more restriction is presented by the player on Track1..3, Track 1 should be
the 1st data of MUSICDATA and Track1/Track2/Track3 should start 7 bytes apart.
These are fix track-playback start-positions set by the init-routine. If any
track exceeds 7 bytes it needs to jump to another region and continue there.
(Might be good to know that default patterns after init on channels start 1
 byte after corresponding tracks, and default instruments are 2 bytes after
 the beginnings of corresponding tracks, only if TRACKS/PATTERNS/INSTRUMENTS
 labels are at the same address. With very short tunes/instruments 1..2 bytes
 of unnecessary Ins()/Pat() commands might be saved this way, maybe not...)

Every pattern or track ends once in a while. Tracks can be looped by using the
JMP() command to any position (even to the middle of another track), patterns
can be ended by Note_() commands (note the underscore after the command).
Instrument-tables can be ended by ArpWF_() or AbsWF_() or SID_() commands, and
if a loop is needed the Jmp() command is your weapon, that can jump anywhere
(even into other instrument) in the INSTRUMENTS range. Note that JMP(), INS()
commands available in Tracks are uppercase, while Ins() in Patterns and Jmp()
in Instruments contain mixed-case letters as a distinction.


About Music-Notes:

And last but not least, you need to create your custom NoteTable which you can
decide how much notes it would contain. Actually you can make a full chromatic
frequency-table there but that's out of scope for Tiny SIDs. 1..3 octaves of
selected notes (not necessarily in chromatic steps/order) should suffice.
The notes of 7th last octave are only selectable without BASSNOTE_SUPPORT.

The note-table should contain the lower-case note-names predefined in macrolib.s
because depending on the BASSNOTE_SUPPORT setting correct 8 pitch-values will
be generated in their places by the assembler. It's not essential but as you
can see in some examples, notes can be given an uppercase name for easier use
in patterns, these labels should containt the note's index in 'NoteTable'.
The notes get more imprecise as you go lower in pitch because there's no full
16bit SID-pitchvalue support in the player. Even with BASSNOTE_SUPPORT where the
SID-pitchvalue precision is 10bits for low-octave notes, some are a bit detuned.
You can play around with individual note pitches in macrolib.s but I think I
found the best possible frequencies already by the small tool pitchgen.c...
The hand-crafted 8bit-only pitches without BASSNOTE_SUPPORT contain very few
distinct bassnotes and upper notes too might need some improvements by the user.

Notes entered into the Patterns are essentially indexes to the NoteTable. But
if transpose is supported by the player-config, that is added to the index.
So you can achieve different kind of transposes (not just chromatic) depending
on the contents of NoteTable. The same is true for relative arpeggio (ArpWF)
commands in instrument-tables, the arp-shiftvalue is simply added to the
pattern-note plus transpose sum and might not be a chromatic-shift, but for
example a diatonic shift if the scale is minor/major in NoteTable.
The Pattern-note being played and transpose don't affect the pitch of AbsWF
absolute pitch+waveform entries in instrument-tables (used for drum-sounds or
short noises at the beginning of bass/solo notes), though absolute pitch is
still taken from NoteTable, the AbsWF() parameter is an index to a note in it.


Outro:

I hope this summarizes a bit the usage of this player, but if you cant' find an
answer to your questions even in macrolib.s comments you can contact me by PM
at CSDb for further help.

                                              March 2022 Hermit (Mihaly Horvath)


Addendum / History (an alternative concept for small/flexible musicdata):

Development of this player took me about a week. The concepts have changed
about 5..6 times until the optimal solution was born. There was an interesting
development-version that should be mentioned: it was based on subroutine calls
entirely. Many times a musician would benefit from 'subroutine' calls in music,
simple jumps/ends are not very flexible to reuse parts of musicdata. If we
think about it, SID music is essentially a bunch of subroutine calls: Orderlists
call Patterns in succession, Patterns call instruments or effects, etc.
 Sometimes a music could be smaller by adding an extra hierarchy of structuring
like units of verse/chorus(refrain)/bridge/etc as a global 'orderlist' that
calls smaller orderlists, that in turn call patterns that may call sub-patterns
or phrases. Instrument-tables could benefit from subroutine calls by reusing
small snippets of arpeggios, filter-programs, whatever, several times.
 I brought this idea into code (it's very much like the Forth programming
language where everything is a function, a 'word' as they call it.) So there
are only very basic commands like 'set a SID register' or 'set duration',
and these atoms can be collected into small subroutines that for example set
both waveform and pitch and create a waveform+arp row that's normally present
in average SID player-routines. A note in a pattern can be realized by a
indirect-addressed (by instrument-address) call to an instrument's table
(which is a subroutine) and giving it a 'parameter' (setting the note) so the
instrument-table knows the base-note when it performs relative-pitches aka
arpeggios. A note-frequency table lookup for arpeggio can be a subroutine call
with indexable target address to any of a bunch of SID pitch-register settings.

So we collected what is needed: a jump, 3 types of subroutine calls, a return,
SID/variable setting commands, and that's all the player needs to have.

 A player-routine can be thought of as a virtual CPU but it's more like a DSP
that is probably not that Turing-complete. But at least with the addition of a
subroutine call to the typical set of jump/end/etc. instructions the flexibility
can be greatly increased. It is important that the subroutine-call takes the
least possible overhead in memory, so the most bits and value-ranges should be
dedicated to it in an instruction code. In my case (256byte music category)
the subroutine call was any value above $80, so 1 byte could make a call to a
128byte region. Return command was a value of 0, and values 1..127 had other
special commands (atoms) like setting a SID-register or player-variable in the
zeropage (that can affect instrument, duration, transpose, whatever).

 Nevertheless, this approach was a dead end for tiny SID creation in 256 bytes
because handling the subroutine-call stack added a little bit of extra code, and
on the other hand, musicdata like notes, instrument changes, etc. are not
implicitly available, every single command that is granted in typical players
should be constructed as a subroutine. This takes some extra bytes, and while
later calling these with only an 1byte subroutine-call command will be very
economical, they already take precious bytes in memory as an overhead.

 Funny enough, when I gradually changed back to the typical player-code and
data structure it could be approached by the subroutine-principle. As I
mentioned, SID music is made of specialized subroutine calls, lacking the
flexibility of universal subroutine calls, but having less overhead. The
ultimate, most flexible and yet optimal-size solution would be somewhere
inbetween, a player having the typical structuring, but with the addition
of the subroutine (or Forth stack-machine like hierarchical) concept.

If anyone wants to experiment with this idea the nearly operational
source-code of this stack-based player can be seen in the misc/stacksid
folder. Maybe for bigger music it will be worth a try to finalize it.
(Duration handling and relative note-lookup for arpeggios are not yet done.)
