; Fargo IDE header ;============================================================================= ; Last save: Sun Jan 24 20:36:55 1999 ; 45.8 233.32 292.0 292.0 292.0 292.0 ;============================================================================= ;---------------------------------------------------------------------------------------------- ; This is the source of a Fargo program that is intended to be executed from the TIOS ; command line. It plays a tune, that is specified in a QBasic-like way, through the ; serial port. ; ; Author: David Kühling ; Last Modification: Jan 22 1999 ; ; Thanks to: ; Gareth James for files.txt ; David Ellsworth for Fargo and the testfunc.asm example program ;---------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------- INCLUDE "tios.h" INCLUDE "soundlib.h" ;---------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------- XDEF _main XDEF _tibasic ;---------------------------------------------------------------------------------------------- M_LEGATO EQU 128 M_NORMAL EQU 110 M_STACCATO EQU 80 ;---------------------------------------------------------------------------------------------- ; Implementation of switch. ; Input: a1.l = pointer to switch data: Value,Label,Value,Label... where Label is relative ; the Value zero ends the block, and the following Label is the Label of the ; "default" - block ; d0.w = Value ; Output: jump to the corresponding Label ; a2/a3 is destroyed ;---------------------------------------------------------------------------------------------- Switch: movea.l a1,a2 \Loop tst.w (a2) beq \Default cmp.w (a2)+,d0 movea.w (a2)+,a3 bne \Loop \DoJmp: jmp 0(a1,a3.w) \Default: movea.w 2(a2),a3 bra \DoJmp ;---------------------------------------------------------------------------------------------- ; Read a decimal number out of the string, pointed by a0. As result d0.w is the number and ; a0 points to the number's end. In case of an error d0 is set to -1. ; Input: a0.l = pointer to string ; Output: d0.w = number or -1 on error ; a0.l = pointer to first character behind number ; d1 is destroyed ;---------------------------------------------------------------------------------------------- ReadNumber: moveq #-1,d0 ; initialize return number to error code clr.w d1 ; d1.w := current character \Loop move.b (a0),d1 cmpi.w #'0',d1 ; check, whether the character is a number blt \End ; if it isn't: end the conversion cmpi.w #'9',d1 bgt \End tst.w d0 ; character is a number -> clear error code in d0 bpl \NoError clr.w d0 \NoError: sub.w #'0',d1 ; convert character in d1 to number 0...9 mulu #10,d0 ; make space for a new digit add.w d1,d0 ; add the digit addq.l #1,a0 ; goto next character bra \Loop \End: rts ;---------------------------------------------------------------------------------------------- ; Play a tune from a QBasic like string ; Input: a0.l = pointer to string ; Output: d1.w = zero (ok) or nonzero (error) ; all other registers are destroyed! ;---------------------------------------------------------------------------------------------- PlayStrTune: moveq #3,d2 ; d2 := Octave moveq #120,d3 ; d3 := Tempo (beats per minute) moveq #M_NORMAL,d4 ; d4 := Note fill duration (legato-staccato) clr.w d5 ; d5 := current character moveq #4,d6 ; d6 := length (1 = full note, 4 = quater note...) jsr soundlib::Init \Loop: clr.w d0 move.b (a0)+,d0 ; d0.w := current character beq \End btst.b #1,$60001A ; check ON key beq \End ; and exit if it is pressed ; jump to the label that corresponds to the character's command lea \SwitchChar(PC),a1 bra Switch ; switch(d0) { \SwitchChar: dc.w ' ',\Loop-\SwitchChar ; case ' ': continue; dc.w 13,\Loop-\SwitchChar ; case '\n': continue; dc.w 'A',\Tone_A-\SwitchChar ; case 'A': goto Tone_A; dc.w 'B',\Tone_B-\SwitchChar ; case 'B': goto Tone_B; dc.w 'C',\Tone_C-\SwitchChar ; case 'C': goto Tone_C; dc.w 'D',\Tone_D-\SwitchChar ; ... dc.w 'E',\Tone_E-\SwitchChar dc.w 'F',\Tone_F-\SwitchChar dc.w 'G',\Tone_G-\SwitchChar dc.w 'L',\ToneLength-\SwitchChar dc.w 'M',\FillDuration-\SwitchChar dc.w 'O',\SetOctave-\SwitchChar dc.w '>',\IncrOctave-\SwitchChar dc.w '<',\DecrOctave-\SwitchChar dc.w 'P',\Pause-\SwitchChar dc.w 'T',\Tempo-\SwitchChar dc.w 0,\Error-\SwitchChar ; default: goto Error; ; } ; *** Play Tones -- d0 is set to the number of the half-tone within the scale *** \Tone_C: moveq #0,d0 bra \PlayTone \Tone_D: moveq #2,d0 bra \PlayTone \Tone_E: moveq #4,d0 bra \PlayTone \Tone_F: moveq #5,d0 bra \PlayTone \Tone_G: moveq #7,d0 bra \PlayTone \Tone_A: moveq #9,d0 bra \PlayTone \Tone_B: moveq #11,d0 ; check, whether there is a flat or b \PlayTone: cmpi.b #'#',(a0) bne \NoFlat addq.w #1,d0 addq.l #1,a0 \NoFlat: cmpi.b #'b',(a0) bne \NoB subq.w #1,d0 addq.l #1,a0 \NoB: ; get tone's frequency and store it in d0 move.w d2,d7 ; d7 = octave*12 mulu #12,d7 add.w d7,d0 ; d0 := number of tone within 7 octave range bmi \Error ; tone has to be in range 0...12*7-1 cmpi.w #12*7-1,d0 bgt \Error lea soundlib::FrqTable,a1 ; a1 := pointer to frequency table lsl.w #1,d0 move.w 0(a1,d0.w),d0 ; d0 := frequency ; calculate total tone duration and store it in d7 \FromPause: move.l #60*1024*4,d7 divu d3,d7 ; d7 := duration of one full note ext.l d7 divu d6,d7 ; d7 := duration of current note type ; check, whether there are on or more dots behind the note \CheckDot: cmpi.b #'.',(a0) bne \NoDot ; if there is a dot: mulu #3,d7 ; increase note length by factor 1.5 (3/2) lsr.w #1,d7 addq.l #1,a0 bra \CheckDot \NoDot: ; calculate play-duration and play the tone move.w d7,d1 ; d1 := absolute note fill duration tst.w d0 bmi \DoPause mulu d4,d1 lsr.l #7,d1 jsr soundlib::Sound ; calculate pause-duration and pause sub.w d1,d7 ; d1 = pause duration move.w d7,d1 \DoPause: jsr soundlib::Pause bra \Loop ; *** process L *** \ToneLength: bsr ReadNumber ; read the number that comes after 'L' 1...64 tst.w d0 ; same as cmpi.w #0,d0 ble \Error cmpi.w #64,d0 bgt \Error move.w d0,d6 ; set the length bra \Loop ; *** Process ML MN or MS *** \FillDuration: move.b (a0)+,d0 ; d0.w = character behind 'M', may be L,N or S lea \SwitchMChar(PC),a1 ; process that character bra Switch ; switch(d0) { \SwitchMChar: dc.w 'L',\M_Legato-\SwitchMChar ; case 'L': goto M_Legato; dc.w 'N',\M_Normal-\SwitchMChar ; ... dc.w 'S',\M_Staccato-\SwitchMChar dc.w 0,\Error-\SwitchMChar \M_Legato: move.w #M_LEGATO,d4 bra \Loop \M_Normal: moveq #M_NORMAL,d4 bra \Loop \M_Staccato: moveq #M_STACCATO,d4 bra \Loop ; *** process O *** \SetOctave: bsr ReadNumber ; read the number that comes after 'O' 0...6 move.w d0,d2 ; set the octave number \CheckOctave: bmi \Error cmpi.w #6,d2 ; if number > 6: error bgt \Error bra \Loop ; *** process > *** \IncrOctave: addq.w #1,d2 ; increase octave number bra \CheckOctave ; and check whether it is in range 0...6 ; *** process < *** \DecrOctave: subq.w #1,d2 ; decrease octave number bra \CheckOctave ; and check whether it is in range 0...6 ; *** process P *** \Pause: moveq #-1,d0 ; frequency -1 means: pause bra \FromPause ; *** process T *** \Tempo: bsr ReadNumber ; read the number that comes after 'T' 32...240 cmpi.w #32,d0 bmi \Error cmpi.w #240,d0 bgt \Error move.w d0,d3 ; set the tempo bra \Loop \Error: moveq #1,d1 rts \End: moveq #0,d1 rts ;---------------------------------------------------------------------------------------------- ; MAIN ;---------------------------------------------------------------------------------------------- _main: ; get the address of the argument-string ("s") pea ArgName(PC) ; get address of argument's entry in temp folder move.w tios::DefTempHandle,-(a7) jsr tios::FindSymEntry ; a0.l := pointer to symbol entry of argument addq.l #6,a7 move.w tios::SYM_ENTRY.hVal(a0),d0 ; d0 := handle of argument tios::DEREF d0,a0 ; a0 := pointer to argument ; check, whether argument is a string move.w (a0),d1 ; d1 = length of variable move.w #130,d0 ; d0 = error number ("argument must be a string") cmpi.b #$2D,1(a0,d1) ; verify STR tag (at end of variable) bne \Error addq.l #3,a0 ; a0 := pointer to string begin bsr PlayStrTune ; play the tune move.w #910,d0 ; if d1 != 0: display "Syntax" - error tst.w d1 bne \Error rts ; Display the error message that correspond to the errno in d0 and exit \Error: move.w d0,-(a7) jsr tios::ERD_dialog addq.l #2,a7 rts ;============================================================================================== ; Data ;============================================================================================== ArgName: dc.b "s",0 ; the name of the argument (the folder name) ;============================================================================================== ; Function Definition ;============================================================================================== SECTION _tibasic dc.b $E9 dc.b $12,$E4 ; EndPrgm dc.b $00,$E8 ; : dc.b $19,$E4 ; Prgm dc.b $E5,$03 ; (s) dc.b $00,$00,$40,$DC _tibasic: END