Thursday, December 17, 2015

Day 8, Towards Multichannel Sid Programming (Part 1)

Note: This is a multi-part post because it's taking me a lot of time. Also don't bother to write in the program right now, I still have to complete and test it, even if I consider the Commodore 64 User Manual to be correct and truthful (and I still have to see if my general algorithm works).

I'm trying to write a program, in Basic that allows me to play some music using the Sid. The music has to be stored somewhere as a songsheet, which means notes with tempo and their pitch.

To do that I must consider three things:

Fact 1.

The SID has three channels, which can operate indipendently from each other.They map their registers on similar layouts on different memory addresses, where we can POKE the values in.

Fact 2.

Each note I want to play can have a different timing from the others.

Fact 3.

Save building a multitasking os for the C64 (lol, nope, or at least, not yet) I have only one cycle available to read data.


The question becomes: how do I store the data and how I load it into the SID registers by using only one program loop?

I think I will just do something like this. I read the data about the three notes. The data being laid out like this.

[note-1-pitch],[note-1-delay],[note-2-pitch],[note-2-delay],[note-3-pitch],[note-3-delay]

I will have some counters, independent from the SID, that will tell my loop if the time has come to read another note from the DATA directives. What I'm thinking is about a mechanism that works like this "If I'm ready to retrieve note-1 (or 2 or 3) I will read it, play it and update the corresponding counter". I will be able to mix different notes in the same bus.

[note-1-pitch],[note-1-delay],[note-1-pitch],[note-1-delay],[note-2-pitch],[note-2-delay],[note-1-pitch],[note-1-delay],
[note-3-pitch],[note-3-delay]

When I put a [-1][-1] as data I will stop a channel, when all the channels are stopped then I will reset the SID and exit the application.

My workflow is something like this. Note that I use labels instead of numbering my statements, and I use the # instead of REM for labeling my comments.

    GOSUB SET-UP-SID
# we set up the continue variables here
# we want to have them visible in the main loop
    CO% = 1 #
# we can also initialize further variables here
    [...]
LOOP: 
# this is how we can emulate a while-true cycle in basic
    IF CO% = 0 GOTO END-LOOP 
# now we will read the notes and upload them to the correct channel
    [...]
# now we will wait 1/4 of a second
    [...]
# and after it we jump back
    GOTO LOOP
END-LOOP: 
    GOSUB RESET-SID
    END

SET-UP-SID:
# rem here we will set up the channels with the correct instruments
    [...]
    RETURN

RESET-SID:
# rem here we will clear the SID
    [...]
   RETURN

As you notice there are some empty boxes, but we are going to fill them up.

Setting up the SID

First we define some helper variables to keep memory locations we need to address

SV=54296  # sid volume register

W1=54276 # adsr/waveform for voice 1
W2=54283 
W3=54290

A1=54277 # attack/decay for voice 1
A2=54284
A3=54291

S1=54278 # sustain/release for voice 1
S2=54285
S3=54292

Next we will give up the right values we want to give them

POKE SV,15 # maximum volume
# We turn Sid Channel 1 into Flute
POKE W1,17 # triangle waveform
POKE A1,96
POKE S1,0

# We turn Sid Channel 2 into Harpsichord
POKE W2,33 # sawtooth waveform
POKE A2,9
POKE S2,0

# We turn Sid Channel 3 into Organ
POKE W3,17 # triangle waveform
POKE A3,0
POKE S3,240

Resetting the SID

In the end we want to set the volume back to zero.

POKE SV,0

It might also be a good thing to zero all the values we put in the various SID registers. Since SID registers are contiguous we can just use a for loop initialized to the minimum register to the upper register

FOR I = W1 TO S3
POKE I,0
NEXT


No comments:

Post a Comment