If you think that your c64 sounds fantastic and you make music with sequencers and synthesizers you will probably want to use your c64 as a synth module. M64 will transform your c64 into a synth module.
The following is required to use M64:
The following is recommended:
To get most out of this program:
The M64 package contains the following files:
To install M64 you need to transfer all executable files to a storage device on your c64. This manual contains a midiloader that will use your MIDI interface to receive executable files.
Type in the midiloader from the listings below. You have to choose the correct one for your MIDI interface. Save the program (so you won't have to retype everything if something goes wrong). Run it. The program will tell if it was typed in correctly.
If everything is all right the program will wait for you to press return on a SYS49152 line. Do so.
The midiloader is now waiting for you to transfer any file from the M64 package with an .MSX extension. Start sending M64demo.MSX (or M64.MSX) from your remote computer or synthesizer. The border flashes during transfer. If the file was correctly transferred just save it as you would do with any other BASIC program.
Below are three listings of the midiloader program. Choose the correct one. If you have absolutely no idea what type of interface you have you can type in the following program that will identify your MIDI interface.
5 AD=56832 10 IFPEEK(AD+2)<>255THEN50 20 IFPEEK(AD+6)<>255THEN60 30 IFPEEK(AD+8)<>255THEN70 40 PRINT"NO INTERFACE CONNECTED":END 50 PRINT"SEQUENTIAL INTERFACE":END 60 PRINT"DATEL/SEIL/JMS INTERFACE":END 70 PRINT"PASSPORT INTERFACE"
1 AD=49150:J=0:T=0:L=30 5 RT=0:READA$:IFA$=""THEN100 10 FORI=1TOLEN(A$)/2 15 GOSUB200:B=A:GOSUB200:A=B*16+A 25 POKEAD+J,A:J=J+1:RT=RT+A:NEXT:T=T+RT 27 READA:IFRT<>ATHENPRINT"DATA ERROR IN LINE "L:END 29 L=L+1:GOTO5 30 DATA "AAMAHIKJADCAKGMA",874 31 DATA "KJBFCAKGMAKJABKC",912 32 DATA "AIIFCNIGCOKNCANA",779 33 DATA "EIKJAAINCANAKCAI",792 34 DATA "CAEOMANNKKMANAPG",1339 35 DATA "MKBAPFKAAAOOCANA",1101 36 DATA "CAEOMADAEDAKAKAK",447 37 DATA "AKINECMACAEOMADA",759 38 DATA "CPCJAPAJAAJBCNOG",532 39 DATA "CNNAACOGCOEMCLMA",842 40 DATA "KJHPINAANMKNABNM",1051 41 DATA "BABEKNAINOINGDMA",871 42 DATA "CJHANAAKKJAACJAB",582 43 DATA "PAOLKNAJNOGAGIGI",1183 44 DATA "KCAACAJKMAEMHPMA",935 45 DATA "MJPHNAPEKCAICAJK",1256 46 DATA "MAKFCNKGCOIFKOIG",1055 47 DATA "KPGIINCANAFIGAEF",913 48 DATA "FCFCEPFCCBANAAEP",450 49 DATA "ELCBANAALNINMAPA",883 50 DATA "AGCANCPPOINAPFGA",1284 51 DATA "INAINOGAAIAAABAA",476 52 DATA "FAAAEAHPPA",511 53 DATA "" 100 IFT<>19827THENPRINT"DATA ERROR, CHECK PROGRAM":END 110 PRINTCHR$(147)"DATA OK, PRESS RETURN!":PRINT:PRINT:PRINT"SYS49152"CHR$(19) 120 END 200 A=ASC(LEFT$(A$,1))-65:A$=RIGHT$(A$,LEN(A$)-1):RETURN
1 AD=49150:J=0:T=0:L=30 5 RT=0:READA$:IFA$=""THEN100 10 FORI=1TOLEN(A$)/2 15 GOSUB200:B=A:GOSUB200:A=B*16+A 25 POKEAD+J,A:J=J+1:RT=RT+A:NEXT:T=T+RT 27 READA:IFRT<>ATHENPRINT"DATA ERROR IN LINE "L:END 29 L=L+1:GOTO5 30 DATA "AAMAHIKJADCAKGMA",874 31 DATA "KJBGCAKGMAKJABKC",913 32 DATA "AIIFCNIGCOKNCANA",779 33 DATA "EIKJAAINCANAKCAI",792 34 DATA "CAEOMANNKKMANAPG",1339 35 DATA "MKBAPFKAAAOOCANA",1101 36 DATA "CAEOMADAEDAKAKAK",447 37 DATA "AKINECMACAEOMADA",759 38 DATA "CPCJAPAJAAJBCNOG",532 39 DATA "CNNAACOGCOEMCLMA",842 40 DATA "KJHPINAANMKNABNM",1051 41 DATA "BABEKNAGNOINGDMA",869 42 DATA "CJHANAAKKJAACJAB",582 43 DATA "PAOLKNAHNOGAGIGI",1181 44 DATA "KCAACAJKMAEMHPMA",935 45 DATA "MJPHNAPEKCAICAJK",1256 46 DATA "MAKFCNKGCOIFKOIG",1055 47 DATA "KPGIINCANAFIGAEF",913 48 DATA "FCFCEPFCCBANAAEP",450 49 DATA "ELCBANAALNINMAPA",883 50 DATA "AGCANCPPOINAPFGA",1284 51 DATA "INAENOGAAIAAABAA",472 52 DATA "FAAAEAHPPA",511 53 DATA "" 100 IFT<>19820THENPRINT"DATA ERROR, CHECK PROGRAM":END 110 PRINTCHR$(147)"DATA OK, PRESS RETURN!":PRINT:PRINT:PRINT"SYS49152"CHR$(19) 120 END 200 A=ASC(LEFT$(A$,1))-65:A$=RIGHT$(A$,LEN(A$)-1):RETURN
1 AD=49150:J=0:T=0:L=30 5 RT=0:READA$:IFA$=""THEN100 10 FORI=1TOLEN(A$)/2 15 GOSUB200:B=A:GOSUB200:A=B*16+A 25 POKEAD+J,A:J=J+1:RT=RT+A:NEXT:T=T+RT 27 READA:IFRT<>ATHENPRINT"DATA ERROR IN LINE "L:END 29 L=L+1:GOTO5 30 DATA "AAMAHIKJADCAKGMA",874 31 DATA "KJBFCAKGMAKJABKC",912 32 DATA "AIIFCNIGCOKNCANA",779 33 DATA "EIKJAAINCANAKCAI",792 34 DATA "CAEOMANNKKMANAPG",1339 35 DATA "MKBAPFKAAAOOCANA",1101 36 DATA "CAEOMADAEDAKAKAK",447 37 DATA "AKINECMACAEOMADA",759 38 DATA "CPCJAPAJAAJBCNOG",532 39 DATA "CNNAACOGCOEMCLMA",842 40 DATA "KJHPINAANMKNABNM",1051 41 DATA "BABEKNACNOINGDMA",865 42 DATA "CJHANAAKKJAACJAB",582 43 DATA "PAOLKNADNOGAGIGI",1177 44 DATA "KCAACAJKMAEMHPMA",935 45 DATA "MJPHNAPEKCAICAJK",1256 46 DATA "MAKFCNKGCOIFKOIG",1055 47 DATA "KPGIINCANAFIGAEF",913 48 DATA "FCFCEPFCCBANAAEP",450 49 DATA "ELCBANAALNINMAPA",883 50 DATA "AGCANCPPOINAPFGA",1284 51 DATA "INAANOGAAIAAABAA",468 52 DATA "FAAAEAHPPA",511 53 DATA "" 100 IFT<>19807THENPRINT"DATA ERROR, CHECK PROGRAM":END 110 PRINTCHR$(147)"DATA OK, PRESS RETURN!":PRINT:PRINT:PRINT"SYS49152"CHR$(19) 120 END 200 A=ASC(LEFT$(A$,1))-65:A$=RIGHT$(A$,LEN(A$)-1):RETURN
Sounds are created by writing programs that can read midi controllers, act on keypresses and control the sound-chip. Programs has to be compiled before they can be "run" for speed reasons. When a sound program is running the C64 will act like a synth module to the outside world.
Sound programs are written, compiled and run in M64. Another program, The Performance Editor (under construction) will read precompiled sounds and store them in banks. Banks contain several sounds and the user can change sound via MIDI from a sequencer or synthesizer.
When you start M64 you will see the edit screen. This is where you write, compile, run, load and save ASL programs. The display looks like this:
Note that in the demo version it is not possible to edit ASL programs.
The editing is somewhat different from what you are used to if you use CBM BASIC.
First of all when you press keys you insert letters into the text instead of overwriting like CBM BASIC. Some keys have different meanings in the editor. Those are:
The top row on the display is the menu bar. Use your mouse or joystick to access it (just like GEOS). Don't worry, everything can be controlled with the keyboard (you just have to learn all the keycodes).
Pull down the File menu and you will see what it contains. The letters to the right tells you that holding the Commodore key and pressing that letter is equivalent to accessing the menu bar.
The "New" command can also be invoked by pressing CLR on the keyboard. The current ASL program will be erased and leave an empty one.
The "Open" command will request for a file to open. The current ASL program will be erased and the selected one will be read.
The "Open as MIDI" command will wait for a MIDI SYSEX file to be sent. This way you can read programs from other devices outside the c64 world. The current ASL program will be erased and a new one will be read from MIDI.
The "Save" command will rewrite the current ASL program to the current device without asking for a filename. If the file exists you will NOT be asked if you wish to remove that file.
The "Save as" command will request for a file to store. The current ASL program will be stored as the selected file. If the file exists you will be asked if you wish to remove that file. You will NOT have to add "@:" in the filename.
The "Save as MIDI" command will immediately send the current ASL program as MIDI SYSEX. Use "Open as MIDI" to read it back. This way you can save programs to other devices outside the c64 world.
The "About" command will display version and registration information.
The "Quit" command will get you back to BASIC. As long as the ASL program hasn't been corrupted you can rerun M64 without erasing your work. This is pretty handy if you want to make some calculations or scratch some files without losing your work.
The "Mark" command will toggle marking mode. You have to mark text in order to use "Cut" and "Copy".
The "Cut" command will move the marked text into the copy buffer. You might get confused because the display will be scrolled in order to place the cursor on the top line (for technical reasons). This little bug will hopefully be removed in future versions.
The "Copy" command will copy the marked text into the copy buffer.
The "Paste" command will paste the copy buffer into the current ASL program before the current line.
The "Delete line" command will delete the current line.
The "Interface" command will let you manually select which type of MIDI interface you use. A dialog will open with three buttons marked "Sequential", "Datel", and "Passport". Use mouse or joystick to select or press "S", "D" or "P".
M64 will autodetect which interface you have but in case it fails (for some strange reason) you can change it manually here. Note that this information is just stored in memory and will be forgotten each time you restart the editor. If you have problems with the autodetect, contact the author.
The "MIDI channel" command will let you select which MIDI channel M64 will receive messages on. A dialog will open with four buttons: use, cancel, up and down. Use mouse or joystick to select or press <cursor-up>, <cursor-down> to change channel, return to use the setting or stop to revert to the old setting.
The "Compile" command will parse the ASL program and generate ASL code that can be executed with the "Run" command.
The "Run" command will run the last successfully compiled ASL code.
The "Local" menu contains all default local register names. Selecting a name will paste it into the ASL program at the current cursor position like if you wrote it.
The "Global" menu contains all default global register names. Selecting a name will paste it into the ASL program at the current cursor position like if you wrote it.
The "A-L" menu contains instruction names starting with letters from A to L. Selecting a name will paste it into the ASL program at the current cursor position like if you wrote it.
The "L-Z" menu contains instruction names starting with letters from L to Z. Selecting a name will paste it into the ASL program at the current cursor position like if you wrote it.
The status line at the bottom of the display shows the device and filename of the current ASL program. A '*' before the filename shows that the ASL program has been changed since it was last saved.
The status line also shows the current row and column of the cursor.
The error line is located just below the status line. It shows the last error from the compiler.
When opening or writing files from devices other than MIDI, M64 provides a file requester.
The tabulator key (arrow left, remember?) cycles through editing filename, filetype, device and drive.
To edit filetype press 'P' for PRG, 'S' for SEQ and 'U' for USR file.
To edit device, drive and filename just move cursor with cursor left/right and edit as usual. Pressing return while editing device or drive will reread the directory. Press return while editing filename or filetype when finished. Pressing STOP cancels the requester.
Use cursor keys to move up and down in the directory. Press return to select the highlighted file. Press STOP to cancel. Pressing 'F1' and 'F7' will scroll up and down respectively one page. 'F2' and 'F8' can be pressed to jump to first/last filename.
Click on a filename to select it. Click on the arrow icons (at top and bottom of file list) to scroll one page up/down. Click on the OPEN/SAVE button when you are satisfied or CANCEL to cancel the requester.
ASL programs can be loaded either from a standard Commodore device or from MIDI.
To load a file from Commodore device select File/Open from the menu or press <commodore-o>. A file requester will appear. Refer to the File Requester section for instructions how to use it.
To load a file from MIDI, select File/Open as MIDI from the menu or press <commodore-d>. A window will appear, telling you to start sending the file. Note that the sender's MIDI OUT must be connected to C64 MIDI IN in order to make this work. Send the file from the sequencer, synthesizer or whatever you are using. If all is well the border will flash and the file will be loaded.
Select ASL/Compile from the menu or press F3 to compile the current ASL program. A window will appear that will inform you about any errors in the ASL program. When the program has been compiled press any key (except Restore) to return to the editor. If there was an error in the compile the cursor will be placed near the place where the error was detected.
Make sure that C64 MIDI IN is connected to your master synthesizer's or computer's MIDI OUT.
Press <commodore-m> or select "Edit/MIDI channel" from the menu and make sure that M64 is configured to receive on the correct MIDI channel.
Press <commodore-i> or select "Edit/Interface" from the menu and make sure that your interface has been selected.
Select ASL/Run from the menu or press F5 to run the last compiled program. The picture below shows the play window that M64 will open.
When the ASL code is running M64 is ready to receive MIDI messages. Pressing keys on your master keyboard should produce sound. The Midi led indicator will be lit if M64 receives MIDI messages on any channel. M64 will ignore all messages except those sent on the previously selected MIDI channel.
The time usage indicator shows the CPU-usage. If time usage exceeds 100% the sound will slow down (that is, always-routines will not run at the correct frequency) and the word "OVERFLOW" will appear.
When programming ASL some understanding of how the SID chip works is recommended. Consult your c64 user manual if necessary.
An ASL program consists of 6 main sections. These are called:
M64 runs ASL code as follows.
First, the globalInit routine is executed followed by one call to localInit for each channel of the SID. Now globalAlways and localAlways (for each channel) are executed frequently (50Hz if not specified). If a keydown message is sent from the master via MIDI then localKeyDown for the unused channel is executed. If a keyup message is sent then localKeyUp for the allocated channel is executed.
As we will see later, it is possible to specify exactly which code that should be executed for each channel.
To sum up, the init-routines are ideal for setting up. Always-routines should be used for handling effects like vibrato, slides and other things that need to update frequently. Keydown-routines should contain code needed to start the sound. Keyup-routines should contain code that stops the sound or fades it. Local-routines affect the channel specific things while global-routines affect everything that is not (the filter for example affects the whole SID chip, not just a channel and is therefore something that should be handled by globalInit and globalAlways).
Numeric values can be either decimal, hexadecimal or binary. If you have programmed 6502 assembler you will be happy because it is the same syntax in ASL.
A constant declaration has the form:
A constant declaration looks like this:
This will assign the value 50 to the identifier "time". It is possible to declare several constants on one line like this:
const TIME=50, DELAY=14
After a constant declaration it is possible to use that identifier in expressions. An example:
const LENGTH=50 const POSTLUDE=LENGTH-5
Expressions may contain constants, numeric values. Possible operations are plus, minus and unary minus.
Here are some examples of a valid expressions (if TIME previously has been declared as a constant):
-TIME+100 TIME--$ff %100-$100 -$1+-%0
Labels are names on lines in the ASL program. Labels are declared by writing letters at the beginning of a line (optionally followed by a colon). An example:
set SIDgate foo addi 1,a cmp a,b bne foo end
In this example "foo" is a label. Here is another example of valid labels:
set SIDgate foo addi 1,a q: cmp a,b bne foo yeah end
Here "foo", "q" and "yeah" are valid labels.
The ASL compiler needs to know where certain routines are located and therefore there are some labels that just has to exist in every ASL program. We get back to those labels later.
Registers are used to hold values. Registers are 16 bit, which means that each register can hold values from 0 to 65535. Registers wrap around if you try to exceed the limits (if you add 1 to a register containing 65535 the result will be 0).
There are two types of registers, local and global. In order to use registers they must be declared. Some registers are predeclared.
Global registers can be declared with the global directive as follows.
These are the predeclared global registers:
Registers starting with 'SIDG' are registers that will affect the SID chip.
Registers starting with 'MIDG' contain values of the controllers that has been sent via MIDI from the master synthesizer or computer.
Local registers can be declared with the local directive as follows.
These are the predeclared local registers:
The registers starting with 'SID' are registers that will affect the SID chip. Note that all SID registers are 16 bit, even the "boolean" ones! This means that all registers are treated equally and you don't need to know how many bits each register occupy. 65535 is always maximum and 0 is always minimum.
Registers starting with 'MID' contain values that has been sent via MIDI from the master synthesizer or computer.
Assume that we have written an ASL program that declares one local register, PulseCounter, and a global register, FilterFreq. As you can see from the diagram below, there are actually three versions of the local register, one for each channel. Each version can hold a unique value. The diagram also shows that there is just one version of the global register.
Let's say that the routine localAlways2 displays the PulseCounter, then "$07D1" will be shown in the run window (2001 hexadecimal, that is).
Take a look at the following code:
localInit: movei 666, PulseCounter end
The movei instruction moves a constant to a register. Since it is executed in the localInit routine the second argument is assumed to be a local register. If we replace PulseCounter with a global register, then the program will not work as expected. NOTE: The current version of the ASL compiler will not report this type of error. To read and write to global registers from local routines you should use the glob2reg and reg2glob instructions.
globalAlways: move FilterFreq, SIDGfilterFreq end
Here, the move instruction requires two global registers as arguments since this is a global routine. Any local registers as arguments would yield unpredictable results.
Instructions have the form:
There are three types of arguments: registers (reg), expressions (expr) and labels (lbl).
Below is a list of all instructions and their arguments.
SPEED_VALUE=TIME/CLOCK_SPEEDwhere CLOCK_SPEED is 1022730 for American (NTSC) monitors and 985250 for European (PAL) monitors. TIME is the interval between always-updates in seconds. If your ASL-programs always get OVERFLOW, then increase this value.
Comments are everything to the right of a semicolon just like 6502 assembler.
;This is a comment local hello clr hello ;This is also a comment ; end This line is a comment (no end here) end ;but here!
Let's look at a simple ASL program example:
; ;Name: Dummy ; ;Author: Jonas Hulten ; ;Modulations: ; pitch wheel: pitch ; const BENDERVALUE=2 globalInit: set SIDGvolume ;full volume end globalAlways: end localInit: movei $4000, SIDattack movei $8000, SIDdecay movei $D000, SIDsustain movei $9000, SIDrelease set SIDsaw end localKeyDown: move MIDfreq,SIDfreq ;play correct frequency set SIDgate ;start attack end localKeyUp: clr SIDgate ;start release end local pitch localAlways: ;pitchbend glob2reg MIDGpitch, pitch pitchbendi BENDERVALUE, pitch move MIDfreq, SIDfreq end
This program is the default ASL program that will show up in the editor when you start M64. It will produce a simple sawtooth sound that will sound like the bass in Spelunker or the melody in Burnin' Rubber. The pitch wheel can be used to change the pitch of the sound two semitones up or down.
The program runs like this: First of all it will initialize. The globalInit routine will set volume to maximum and localInit will set attack, decay and release time. It will also set sustain level and enable the sawtooth waveform. The values for attack, decay, sustain and release are deliberately written in hexadecimal form since only the first nibble really matters (if you don't know what this means, just forget it, it's not important).
When keys are pressed the localKeyDown routine will copy the correct frequency to the SID chip. The SID gate will also be set to start the sound. When keys are released the localKeyUp routine will clear the SID gate so that the release of the sound starts.
As the sound plays, the globalAlways routine will do nothing but the localAlways routine will make the pitchbend. This is done by first copying the global register MIDGpitch to a local one (pitch is declared just above the routine). Then the special instruction 'pitchbendi' does all the magic and updates local register MIDfreq with the correct frequency value. The last thing to do is to copy the frequency value to the SID chip.
To change the character of the sound we change the waveform to pulse instead of sawtooth. To make the sound constantly change we let the pulse width wary over time. To do so we place a counter in a register that will constantly count up and then use the 'triangle' or 'sinus' instruction to get the actual values to put into the SIDpulseWidth register. The program now look like this (all changes in red).
; ;Name: Not so Dummy ; ;Author: Jonas Hulten ; ;Modulations: ; pitch wheel: pitch ; const BENDERVALUE=2 const PULSESPEED=200 globalInit: set SIDGvolume ;full volume end globalAlways: end local pulseCount localInit: movei $4000, SIDattack movei $8000, SIDdecay movei $D000, SIDsustain movei $9000, SIDrelease set SIDpulse clr pulseCount end localKeyDown: move MIDfreq,SIDfreq ;play correct frequency set SIDgate ;start attack end localKeyUp: clr SIDgate ;start release end local pitch localAlways: ;pitchbend glob2reg MIDGpitch, pitch pitchbendi BENDERVALUE, pitch move MIDfreq, SIDfreq addi PULSESPEED, pulseCount triangle pulseCount, SIDpulseWidth end
Note that there are one 'pulseCount' register for each channel. All three are syncronized so if you press three keys at the same time you can hear that the pulse width is the same for all three channels. We will now make them different from each other. We do so by making three different localInit routines, one for each channel! Since just the pulseCount will be different we will reuse our code. The localInit routine now looks like this:
localInit1: clr pulseCount bra init localInit2: movei 20000, pulseCount bra init localInit3: movei 40000, pulseCount init: movei $4000, SIDattack movei $8000, SIDdecay movei $D000, SIDsustain movei $9000, SIDrelease set SIDpulse end
Note that the original localInit label has been removed. That is because otherwise it would override the other localInit labels.
Ok, let's look at another example. This time it's a monophonic sound that uses all three channels for one sound. This sound uses the syncronize feature of the SID chip to make an odd sound. We will enable sync on channel 1 which will syncronize the fundamental frequency of channel 1 with the fundamental frequency of channel 3, producing "hard sync" effects. We modulate the frequency of channel 1 with the pitch wheel (the resolution of the pitch wheel is much higher that the modulation wheel). Channel 3's frequency will be the frequency of the key we press. Here is the program:
; ;Name: Monophonic sync ; ;Author: Jonas Hulten ; ;Modulations: ; modulation wheel: channel 1 frequency ; globalInit: set SIDGvolume ;full volume set MIDGmonophonic end globalAlways: end localInit1: movei $4000, SIDattack movei $8000, SIDdecay movei $D000, SIDsustain movei $9000, SIDrelease set SIDsaw set SIDsync end localInit2: localInit3: end localKeyDown1: set SIDgate ;start attack end localKeyDown2: end localKeyDown3: move MIDfreq,SIDfreq ;play correct frequency end localKeyUp1: clr SIDgate ;start release end localKeyUp2: localKeyUp3: end local mod localAlways1: glob2reg MIDGpitch, mod move mod, SIDfreq end localAlways2: localAlways3: end
To make monophonic sounds we simply added a line in the globalInit that says
We can try the ring modulation by changing the waveform to triangle and setting SIDmodulate instead of SIDsync. localInit1 will look like this:
localInit1: movei $4000, SIDattack movei $8000, SIDdecay movei $D000, SIDsustain movei $9000, SIDrelease set SIDtriangle set SIDmodulate end
You might just feel like Jeff Minter while playing around with this sound.
There are a couple of techniques that are very useful when programming ASL.
Use constants instead of numbers so that people can adjust the sound parameters without reading the whole code.
const VIBRATOSPEED=50 ; ... addi VIBRATOSPEED, Counter
is MUCH better than:
addi 50, Counter
The following example shows how to use the 'cmp' instruction in combination with branch instructions to make a counter count from 0 to 4 and then stop counting.
cmpi 4, Counter ;compare Counter with 4. bhs noCount ;branch to noCount if Counter was ;higher or same as 4. addi 1, Counter ;increment Counter. noCount:
This can be done in a more efficient way:
addi 1, Counter ;increment Counter mini 4, Counter ;let Counter be the lowest of 4 and Counter
The following code shows how to add vibrato to a register.
const SPEED=200, AMPLITUDE=40 ;first some declarations local Counter, SinValue ;... some code here ... addi SPEED,Counter ;update Counter sinus Counter, SinValue ;get sinus value (0..65535) scalei AMPLITUDE+AMPLITUDE, SinValue ;scale down sinus value (0..80) subi AMPLITUDE, SinValue ;change offset (-40..40) add SinValue, SIDfreq ;add vibrato to SID frequency! ;... and some code here too ...
Making pitchbend with ASL can be done without the special 'pitchbend' instruction but that is not recommended. This instruction does more than it's operands tell you:
The following code will demonstrate the typical use.
const BENDERVALUE=12 ;pitchbend a full octave (12 semitones) local pitch ;first some declarations ;... some code here ... localAlways: glob2reg MIDGpitch, pitch ;copy global register to local pitchbendi BENDERVALUE, pitch ;the result is an updated MIDfreq register move MIDfreq, SIDfreq ;so just copy it to the SID! ;... and some code here too ...
Finding errors that are not compile errors are generally hard (especially if you are inexperienced). The most common errors when programming ASL are:
M64 stops completely when I run my script!
Probably because you forgot to end some routine with the 'end' instruction. It can also be an 'execute' instruction that expects some files to be loaded before starting M64.
I try to modulate pulse width with the modulation wheel but I can't get it to work!
The SIDGmodWheel is a global register. Use glob2reg in local routines to copy it to a local register (like SIDpulseWidth).
Nothing happens when I tap on the MIDI keyboard!
Make sure that MIDI OUT on the keyboard or sequencer is connected to MIDI IN on the c64 MIDI interface.
Check that the keyboard is sending on the correct channel. The receive channel can be changed by pressing <commodore-m> or selecting "Edit/MIDI channel" from the menu (the default is channel 1).
Also, check that the correct MIDI interface is selected. Press <commodore-i> in the editor or select Edit/Interface in the menu.
When running, I get OVERFLOW whenever I press a key!
Your sound is too complex to be updated at the current frequency. Use the 'settimeri' command to increase the update interval time.
Registers suddenly get strange values!
Make sure that there are no references to global registers in local routines (except in 'glob2reg' and 'reg2glob' instructions). Also make sure that there are no local registers at all in global routines.
M64 should be pretty bug-free. However, if M64 suddenly jams you should hold Run/Stop and tap the Restore key. If that didn't got you back to a BASIC prompt you have to make a soft reset by hand (this is not possible with a standard C64 without a reset switch).
Now, at the BASIC prompt, type SYS2061 and press return. You will hopefully be back in M64 with your previous ASL program. If M64 didn't start then repeat the Run/Stop-Restore procedure and instead of starting it with SYS2061, you reload M64 from your storage device and run it. If your ASL program has not been destroyed M64 will let you continue editing it.
Send bug reports directly to me, Jonas Hultén, at my first name at kollektivet.nu.
Future versions of M64 will (hopefully) feature:
This program was shareware but is nowadays freeware. Download and do what you want with it.
The program M64 and this manual is copyright © 1997 AnyWare Designs. I, the author, or anyone else at AnyWare Designs are not responsible for any kind of damage caused by this program directly or indirectly from the use or misuse of M64 or M64 related programs.
AnyWare Designs are Jonas Hultén (author of this program) and Petrus Hyvönen.
M64 was written by Jonas Hultén.
M64 was developed using the following equipment.