CPU = 16F876 MHZ = 4 CONFIG 16245 ' EEPROM Memory Map ---------------------------------------------------------------------------------------------------- ' Memory Byte Stores ' 0___________________HOPPER ' 1___________________HOPPERTRIG ' 2___________________POD ' 3___________________TANKTRIGGER.Highbyte ' 4___________________TANKTRIGGER.Lowbyte ' 5___________________KONFIG ' 6___________________LOADERSAVE ' 7___________________TANKSAVE.Highbyte ' 8___________________TANKSAVE.Lowbyte ' 9___________________DAYSAVE.Highbyte ' 10__________________DAYSAVE.Lowbyte ' I/O Initialization --------------------------------------------------------------------------------------------------- INPUT A0 ; LOWVOLT OUTPUT A1 ; NO CONNECT OUTPUT A2 ; NO CONNECT OUTPUT A3 ; LCDEN OUTPUT A4 ; LCDRW OUTPUT A5 ; LCDRS INPUT B0 ; COUNTIN OUTPUT B1 ; RELAY INPUT B2 ; TANK OUTPUT B3 ; NO CONNECT INPUT B4 ; POD BUTTON INPUT B5 ; FULL BUTTON OUTPUT B6 ; NO CONNECT OUTPUT B7 ; NO CONNECT OUTPUT C0 ; NO CONNECT OUTPUT C1 ; NO CONNECT OUTPUT C2 ; NO CONNECT OUTPUT C3 ; SPEAKER OUTPUT C4 ; LCDBIT4 OUTPUT C5 ; LCDBIT5 OUTPUT C6 ; LCDBIT6 OUTPUT C7 ; LCDBIT7 ' I/O Constants -------------------------------------------------------------------------------------------------------- LOWVOLT con A0 ; pin 2 ' NOCONN con A1 ; pin 3 ' NOCONN con A2 ; pin 4 LCDEN con A3 ; pin 5 LCDRW con A4 ; pin 6 LCDRS con A5 ; pin 7 COUNTIN con B0 ; pin 21 RELAY con B1 ; pin 22 LOADER con B2 ; pin 23 ' NOCONN con B3 ; pin 24 PODPUSH con B4 ; pin 25 TANK con B5 ; pin 26 ' NOCONN con B6 ; pin 27 ' NOCONN con B7 ; pin 28 ' NOCONN con C0 ; pin 11 ' NOCONN con C1 ; pin 12 ' NOCONN con C2 ; pin 13 SPEAKER con C3 ; pin 14 LCDBIT4 con C4 ; pin 15 LCDBIT5 con C5 ; pin 16 LCDBIT6 con C6 ; pin 17 LCDBIT7 con C7 ; pin 18 ' INTERVAL con 250 ' VARIABLES ------------------------------------------------------------------------------------------------------------ KOUNT var word ' generic counting variable BEEPING var word ' workspace for beep enable INDEX var byte ' generic counting variable OBJECT var word ' generic word variable UPPERLIMIT var word ' upper limit of conditional/calculation expression FACTOR var byte ' addative byte variable SELECTFLAG var bit ' flag bit for select branching in button routine RAW var byte ' interrupt variable BUFFER var byte ' interrupt variable HOPPER var byte ' hopper capacity HOPPERTRIG var byte ' hopper warning point TANKTRIGGER var word ' tank capacity POD var byte ' pod capacity KONFIG var byte 'KONFIGuration byte ' Bit Enables/Stores '--------------------------- ' 0 Hopper Audio ' 1 Tank Audio ' 2 Agitation ' 3 unassigned ' 4 Pulse Length bit 0 ' 5 Pulse Length bit 1 ' 6 Pulse Length bit 2 ' 7 Pulse Length bit 3 TOTAL var word ' total bolt cycles CHOPR var byte ' estimated current hopper level CTANK var word ' estimated current tank level HOPWARN var byte ' display variable for hopper warning, clear/H in display TNKWARN var byte ' display variable for tank warning, clear/T in display BUTT1 var bit ' workspace for button statement BUTT2 var bit ' workspace for button statement BUTT3 var bit ' workspace for button statement BARGRAPH var byte(16) '16 byte array for bargraph subroutine ;------------------------------------------------------------------------------------------------------------------------- ;------------------------------------------------------------------------------------------------------------------------- ;------------------------------------------------------------------------------------------------------------------------- 'Set Up Interrupts ENABLE EXTINT ; enable the external interrupt on B0 SETEXTINT EXT_L2H ; set interrupt to trigger on Low to High transition ONINTERRUPT EXTINT, COUNTUP ; on interrupt branch to COUNTUP ;------------------------------------------------------------------------------------------------------------------------- ;------------------------------------------------------------------------------------------------------------------------- ;------------------------------------------------------------------------------------------------------------------------- ;------------------------------------------------------------------------------------------------------------------------- START: ; initialize display, display title screen, and beep pause 500 ; do nothing for 500ms lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [initlcd1, initlcd2, twoline, clear, home, scr] ; initialize LCD lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [clear, home, "ShotMeter IV by",| scrram+$40, "Rich Kappmeier"] ; display title screen ' Test the outputs high SPEAKER ; drive SPEAKER pin high pause 500 ; do nothing for 500ms low SPEAKER ; drive SPEAKER pin low high RELAY ; drive RELAY pin high pause 500 ; do nothing for 500ms low RELAY ; drive RELAY pin low clear ; clear all vars MAIN: ; initialize variables from memory HOPWARN = " " ; copy blank character to HOPper WARNing display variable TNKWARN = " " ; copy blank character to TaNK WARNing display variable READ 0,HOPPER ; read EEPROM location and store in varaible READ 1,HOPPERTRIG ; read EEPROM location and store in varaible READ 2,POD ; read EEPROM location and store in varaible READ 3,TANKTRIGGER.Highbyte ; read EEPROM location and store in varaible READ 4,TANKTRIGGER.Lowbyte ; read EEPROM location and store in varaible READ 5,KONFIG ; read EEPROM location and store in varaible READ 6,CHOPR ; read EEPROM location and store in varaible READ 7,CTANK.Highbyte ; read EEPROM location and store in varaible READ 8,CTANK.Lowbyte ; read EEPROM location and store in varaible READ 9,TOTAL.Highbyte ; read EEPROM location and store in varaible READ 10,TOTAL.Lowbyte ; read EEPROM location and store in varaible ;------------------------------------------------------------------------------------------------------------------------- WAITDISPLAY: ; menu screen gosub RELEASE ; branch lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [clear, home, " Clear Values-->",| scrram+$40, " Setup / Start"] ; display menu screen WAITING: button PODPUSH,1,255,2,BUTT1,1,LOOP ; branch on button = high button LOADER,1,255,2,BUTT2,1,HOPPERSIZE ; branch on button = high button TANK,1,255,2,BUTT3,1,CLEARVALUES ; branch on button = high pause 200 ; pause for 200ms goto WAITING ; loop ;------------------------------------------------------------------------------------------------------------------------- CLEARVALUES: CHOPR = HOPPER ; copy stored HOPPER value to Current HOPpeR variable CTANK = 0 ; copy zero to Current TANK variable TOTAL = 0 ; copy zero to current TOTAL shots variable lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [clear, home, "Values Cleared!"] ; display message GOTO WAITDISPLAY ; branch ;------------------------------------------------------------------------------------------------------------------------- ;------------------------------------------------------------------------------------------------------------------------- ;------------------------------------------------------------------------------------------------------------------------- LOOP: gosub RELEASE ; branch DISPLAYSTATUS: lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [home, HOPWARN, " ", dec CHOPR\3, " ", TNKWARN,| scrram+$40, "T:", dec CTANK\4 , " ", $F6, ":", dec TOTAL\4] ; display current counts and warnings ;------------------------------------------------------------------------------------------------------------------------- MAINBUTT: button PODPUSH,1,255,2,BUTT1,1,PODMAIN ; branch on button = high button LOADER,1,255,2,BUTT2,1,HOPPERMAIN ; branch on button = high button TANK,1,255,2,BUTT3,1,TANKMAIN ; branch on button = high if KOUNT = 0 then ; timer for length of RELAY pulse low RELAY ; drive pin low if KOUNT is zero else ; else condition KOUNT = KOUNT - 1 ; decrement KOUNT endif ; end of if statement if BEEPING = 0 then ; timer for length of BEEP pulse low SPEAKER ; drive SPEAKER low if BEEPING is zero else ; else condition BEEPING = BEEPING - 1 ; decrement BEEPING endif ; end of if statement if PORTA.bit0 = 0 then SAVEVALUES ; when low power detected branch if RAW = 0 then MAINBUTT ; if RAW is clear loop ;------------------------------------------------------------------------------------------------------------------------- BUFFER = BUFFER + RAW ; copy RAW to BUFFER RAW = 0 ; copy 0 to RAW ;------------------------------------------------------------------------------------------------------------------------- portb.bit1 = KONFIG.bit2 ; CONFIG.bit2 is the agitation bit TOTAL = TOTAL + BUFFER ; add RAW to TOTAL CTANK = CTANK + BUFFER ; add RAW to Current TANK to get tank level if CHOPR > 0 then ; if Current HOPpeR equals zero then branch CHOPR = CHOPR - BUFFER ; subtract RAW from Current HOPpeR to get hopper level endif ; end of if statement BUFFER = 0 ; copy 0 to BUFFER if CHOPR > HOPPER then ; when Current HOPpeR is greater than HOPPER CHOPR = 0 ; copy 0 into Current HOPpeR endif ; end of if statement if CHOPR < HOPPERTRIG then ; if the current hopper < trigger enable warning HOPWARN = "L" ; copy "L" into HOPWARN variable for display BEEPING = (KOUNT) + 200 ; copy KOUNT + 200 into BEEPING portc.bit3 = KONFIG.bit0 ; copy CONFIG.bit0 to port C bit 3 or PIN 14 endif ; end of if statement if CTANK > TANKTRIGGER then ; if the Current TANK > TANK TRIGGER point enable warning TNKWARN = "T" ; copy "T" into TaNK WARNning BEEPING = (KOUNT) + 200 ; copy KOUNT + 200 into BEEPING variable portc.bit3 = (KONFIG.bit1 | KONFIG.bit0) ; copy CONFIG.bit0 to port C bit 3 or PIN 14 endif ; end of if statement goto DISPLAYSTATUS ; branch ;------------------------------------------------------------------------------------------------------------------------- PODMAIN: ; add a POD worth of balls to the Current HOPpeR gosub RELEASE ; branch if CHOPR > (HOPPER - POD) then ; if Current HOPpeR is greater than HOPPER - POD CHOPR = HOPPER ; copy HOPPER into Current HOPpeR, upper ceiling of HOPPER else ; else condition CHOPR = POD + CHOPR ; add POD to Current HOPpeR endif ; end of if statement HOPWARN = " " ; copy " " (space character) into HOPper WARNning goto DISPLAYSTATUS ; branch ;------------------------------------------------------------------------------------------------------------------------- HOPPERMAIN: ; top-off HOPPER = fill to capacity gosub RELEASE ; branch CHOPR = HOPPER ; reset Current HOPpeR to max cap (hopper) HOPWARN = " " ; copy " " (space character) into HOPper WARNning goto DISPLAYSTATUS ; branch ;------------------------------------------------------------------------------------------------------------------------- TANKMAIN: ; zero Current TANK count gosub RELEASE ; branch CTANK = 0 ; copy 0 into Current TANK TNKWARN = " " ; copy " " (space character) into TaNK WARNning goto DISPLAYSTATUS ; branch ;------------------------------------------------------------------------------------------------------------------------- ;------------------------------------------------------------------------------------------------------------------------- ' INTERRUPTS !!!!!! ;------------------------------------------------------------------------------------------------------------------------- ;------------------------------------------------------------------------------------------------------------------------- DISABLE EXTINT ; disable external interrupt for all code below this point COUNTUP: ; increment the raw count RAW = RAW + 1 ; copy RAW+1 into RAW KOUNT = (KONFIG.highnib + 1) * 25 ; copy [(KONFIG.highnib + 1) * 25] into KOUNT ; = AGITATION pulse length RESUME ; resume program from interrupt ;------------------------------------------------------------------------------------------------------------------------- ;------------------------------------------------------------------------------------------------------------------------- ;------------------------------------------------------------------------------------------------------------------------- HOPPERSIZE: ; set the hopper capacity gosub RELEASE ; branch if HOPPER > 170 then ; if HOPPER capacity is greater then 200 HOPPER = 170 ; copy 200 to HOPPER endif ; end of if statement OBJECT = HOPPER ; copy HOPPER into OBJECT UPPERLIMIT = 250 ; copy 250 into UPPERLIMIT FACTOR = 5 ; copy 5 into FACTOR lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [clear, home, "Loader Capacity"] ; write to LCD HOPPERBUTTIN: gosub OBJECTDISPLAY ; branch gosub BUTTIN ; branch if SELECTFLAG = 0 then HOPPERBUTTIN ; if SELECTFLAG is 0 then branch HOPPER = OBJECT ; copy OBJECT back into HOPPER ;------------------------------------------------------------------------------------------------------------------------- HOPTRIG: ; set HOPPER TRIGger warning point ; limit HOPPER TRIGger to HOPPER capacity if HOPPERTRIG > HOPPER then ; limit HOPPER TRIGger to no greater than HOPPER HOPPERTRIG = HOPPER ; copy HOPPER into HOPPER TRIGger endif ; end of if statement OBJECT = HOPPERTRIG ; copy HOPPERTRIG into OBJECT UPPERLIMIT = HOPPER ; copy HOPPER into UPPERLIMIT FACTOR = 5 ; copy 5 into FACTOR lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [clear, home, "Loader Trigger"] ; write to LCD HTRIGBUTTIN: gosub OBJECTDISPLAY ; branch gosub BUTTIN ; branch if SELECTFLAG = 0 then HTRIGBUTTIN ; if SELECTFLAG is 0 then branch HOPPERTRIG = OBJECT ; copy OBJECT back into HOPPER ;------------------------------------------------------------------------------------------------------------------------- PODSIZE: ; set POD capacity if POD > 100 then ; limit POD capacity to no greater than 200 POD = 100 ; copy 200 to POD endif ; end of if statement OBJECT = POD ; copy POD to OBJECT UPPERLIMIT = 200 ; copy 200 to UPPERLIMIT FACTOR = 5 ; copy 5 to FACTOR lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [clear, home, "Pod Capacity"] ; write to LCD PODBUTT: gosub OBJECTDISPLAY ; branch gosub BUTTIN ; branch if SELECTFLAG = 0 then PODBUTT ; if SELECTFLAG is 0 then branch POD = OBJECT ; copy OBJECT to POD ;------------------------------------------------------------------------------------------------------------------------- TANKTRIG: ; set TANKTRIGGER point OBJECT = TANKTRIGGER ; copy TANKTRIGGER to OBJECT UPPERLIMIT = 2000 ; copy 2000 to UPPERLIMIT FACTOR = 50 ; copy 50 to FACTOR if OBJECT > 2000 then ; limit OBJECT to no greater than 2000 OBJECT = 400 ; copy 400 to OBJECT endif ; end of if statement lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [clear, home, "Tank Trigger"] ; write to LCD TTRIGDISP: lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [scrram+$40, dec OBJECT\4] ; write OBJECT to LCD with 4 digits TTRIGBUTT: gosub BUTTIN ; branch if SELECTFLAG = 0 then TTRIGDISP ; if SELECTFLAG is 0 then branch TANKTRIGGER = OBJECT ; copy OBJECT to TANKTRIGGER ;------------------------------------------------------------------------------------------------------------------------- HOPWARNING: ; set HOPper WARNING point if KONFIG.bit0 = 1 then ; check KONFIG.bit0 OBJECT = "Y" ; if 1 then copy "Y" to OBJECT else ; if not OBJECT = "N" ; then copy "N" to OBJECT endif ; end of if statement HOPWARNDISP: gosub RELEASE ; branch lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [clear, home, "Loader Audio: ", OBJECT] ; write OBJECT to LCD HOPWARNBUTT: button PODPUSH,1,254,2,BUTT1,1,HOPWARNON ; test button pin button LOADER,1,254,2,BUTT2,1,HOPWARNOFF ; test button pin button TANK,1,254,2,BUTT3,1,TANKWARNING ; test button pin goto HOPWARNBUTT ; loop HOPWARNOFF: OBJECT = "N" ; copy "N" to OBJECT KONFIG.bit0 = 0 ; copy 0 to KONFIG.bit0 goto HOPWARNDISP ; branch HOPWARNON: OBJECT = "Y" ; copy "Y" to OBJECT KONFIG.bit0 = 1 ; copy 1 to KONFIG.bit0 goto HOPWARNDISP ; branch ;------------------------------------------------------------------------------------------------------------------------- TANKWARNING: ; set TANK WARNING trigger point if KONFIG.bit1 = 1 then ; check KONFIG.bit1 OBJECT = "Y" ; if 1 then copy "Y" to OBJECT else ; if not OBJECT = "N" ; then copy "N" to OBJECT endif ; end of if statement TANKWARNDISP: gosub RELEASE ; branch lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [clear, home, "Tank Audio: ", OBJECT] ; write OBJECT to LCD TANKWARNBUTT: button PODPUSH,1,254,2,BUTT1,1,TANKWARNON ; test button pin button LOADER,1,254,2,BUTT2,1,TANKWARNOFF ; test button pin button TANK,1,254,2,BUTT3,1,AGITATION ; test button pin goto TANKWARNBUTT ; loop TANKWARNOFF: OBJECT = "N" ; copy "N" to OBJECT KONFIG.bit1 = 0 ; copy 0 to KONFIG.bit1 goto TANKWARNDISP ; branch TANKWARNON: OBJECT = "Y" ; copy "Y" to OBJECT KONFIG.bit1 = 1 ; copy 1 to KONFIG.bit1 goto TANKWARNDISP ; branch ;------------------------------------------------------------------------------------------------------------------------- AGITATION: ; enable/disable loader agitation if KONFIG.bit2 = 1 then ; check KONFIG.bit2 OBJECT = "Y" ; if 1 then copy "Y" to OBJECT else ; if not OBJECT = "N" ; then copy "N" to OBJECT endif ; end of if statement AGIDISP: gosub RELEASE ; branch lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [clear, home, "Agitation: ", OBJECT] ; write OBJECT to LCD AGIBUTT: button PODPUSH,1,254,2,BUTT1,1,AGION ; test button pin button LOADER,1,254,2,BUTT2,1,AGIOFF ; test button pin button TANK,1,254,2,BUTT3,1,BEEPLENGTH ; test button pin goto AGIBUTT ; loop AGIOFF: OBJECT = "N" ; copy "N" to OBJECT KONFIG.bit2 = 0 ; copy 0 to KONFIG.bit2 goto AGIDISP ; branch AGION: OBJECT = "Y" ; copy "Y" to OBJECT KONFIG.bit2 = 1 ; copy 1 to KONFIG.bit2 goto AGIDISP ; branch ;------------------------------------------------------------------------------------------------------------------------- BEEPLENGTH: ; set the BEEP LENGTH parameter gosub RELEASE ; branch lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [clear, home, "Pulse Length:"] ; write to display OBJECT = KONFIG.nib1 ; copy high nibble of KONFIG to OBJECT UPPERLIMIT = 15 ; UPPERLIMIT of display byte FACTOR = 1 ; copy 1 to FACTOR INDEX = 0 ; copy 0 to INDEX BEEPBAR: if INDEX <= OBJECT then ; assign blocks or hyphens to display addresses BARGRAPH(INDEX) = 255 ; copy 255 to current BARGRAPH byte @ INDEX else ; if not BARGRAPH(INDEX) = "-" ; copy "-" to current BARGRAPH byte @ INDEX endif ; end of if statement INDEX = INDEX + 1 ; increment INDEX if INDEX < 16 then BEEPBAR ; loop while INDEX is < 16 INDEX = 0 ; copy 0 to INDEX BEEPDISP: lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [scrram+$40, STR BARGRAPH\16] ; write all 16 bytes of BARGRAPH STRing to display BEEPBUTT: gosub BUTTIN ; branch if SELECTFLAG = 0 then BEEPBAR ; when SELECTFLAG = 1 drop to next line KONFIG.nib1 = OBJECT ; copy OBJECT to high nib of KONFIG ;------------------------------------------------------------------------------------------------------------------------- SAVEDATA: WRITE 0,HOPPER ; write HOPPER to address 0 of EEPROM WRITE 1,HOPPERTRIG ; write HOPPERTRIG to address 1 of EEPROM WRITE 2,POD ; write POD to address 2 of EEPROM WRITE 3,TANKTRIGGER.Highbyte ; write TANKTRIGGER.Highbyte to address 3 of EEPROM WRITE 4,TANKTRIGGER.Lowbyte ; write TANKTRIGGER.Lowbyte to address 4 of EEPROM WRITE 5,KONFIG ; write KONFIG to address 4 of EEPROM lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [clear, home, "Saving entries!"] ; write string to display pause 500 ; do nothing for INTERVAL milliseconds goto MAIN ; branch ;------------------------------------------------------------------------------------------------------------------------- SAVEVALUES: ; save current values on power fail WRITEDM 6,[CHOPR, CTANK.Highbyte, CTANK.Lowbyte ,TOTAL.Highbyte ,TOTAL.Lowbyte] ; write consecutive bytes starting at EEPROM address 6 lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [clear, home, " Battery Dead! "] ; write string to display low RELAY ; force RELAY low low SPEAKER ; force SPEAKER low end ; end '========================================================================================================================= '========================================================================================================================= ' GOSUBS ==================================================================================================== '========================================================================================================================= '========================================================================================================================= OBJECTDISPLAY: lcdwrite LCDRS\LCDEN\LCDRW, portc.nib1, [scrram+$40, dec OBJECT\3] ; display 3 byte OBJECT string in decimal format RETURN ; return from gosub '========================================================================================================================= BUTTIN: button PODPUSH,1,255,2,BUTT1,1,BUTTINUP ; test button pin button LOADER,1,255,2,BUTT2,1,BUTTINDOWN ; test button pin button TANK,1,255,2,BUTT3,1,BUTTINSELECT ; test button pin goto BUTTIN ; loop BUTTINUP: gosub RELEASE ; branch OBJECT = OBJECT + FACTOR ; copy OBJECT + FACTOR to OBJECT, increment OBJECT by FACTOR if OBJECT > UPPERLIMIT then ; if OBJECT exceeds UPPERLIMIT OBJECT = 0 ; copy 0 to OBJECT endif ; end of if statement SELECTFLAG = 0 ; copy 0 to SELECTFLAG goto BUTTEND ; branch BUTTINDOWN: gosub RELEASE ; branch OBJECT = OBJECT - FACTOR ; copy OBJECT - FACTOR to OBJECT, decrement OBJECT by FACTOR if OBJECT > UPPERLIMIT then ; if OBJECT exceeds UPPERLIMIT OBJECT = UPPERLIMIT ; copy UPPERLIMIT to OBJECT endif ; end of if statement SELECTFLAG = 0 ; copy 0 to SELECTFLAG goto BUTTEND ; branch BUTTINSELECT: gosub RELEASE ; branch SELECTFLAG = 1 ; copy 1 to SELECTFLAG BUTTEND: ; debounce buttons for this subroutine RETURN ; return from gosub '============================================================================================= RELEASE: ; debounce buttons for rest of program high SPEAKER ; drive SPEAKER I/O high while portb.bit4 = 1 : wend ; while PODPUSH button is pressed wait here while portb.bit2 = 1 : wend ; while LOADER button is pressed wait here while portb.bit5 = 1 : wend ; while TANK button is pressed wait here pause 50 ; do nothing for INTERVAL milliseconds low SPEAKER ; drive SPEAKER I/O low pause 100 ; do nothing for INTERVAL milliseconds RETURN ; return from gosub