Changes
add max variable length information
Locomotive BASIC was a [http://en.wikipedia.org/wiki/BASIC_programming_language BASIC] interpreter for the Amstrad CPC range of computers.
It was availlable directly from in-built ROMs on [[CPC old generation]] and on the [[Plus System Cartridge]] for the [[Plus]] range.[https://benchmarko.github.io/CPCBasic/ Online Interpreter]
== Description ==
Locomotive BASIC, was one of the best and fastest BASIC implementations of the era. The language benefited both from a clean, well-thought out implementation of the core language by Locomotive, and by the excellent [[firmware]] of the CPC, which lent most of its advanced features to the BASIC.
Unlike the competing [[Commodore 64]], it featured a comprehensive graphic capabilities with its PLOT, DRAW, PAPER, INK, PEN, BORDER and (in BASIC 1.1) FILL commands. It had extensive sound commands, granting control of the [[AY-3-8912]] via the firmware's volume and tone envelope system. With the SOUND command, you could select channels, set envelopes, pitch, noise and volume. That was something unmatched by other computers of that era.
Also there was simple interface for memory management, with MEMORY and LOAD commands. The latter allowed for loading of raw screen data, thus providing easy picture showing. Both through this (combined with CALL, PEEK and POKE) and the firmware's [[RSX]] system, it was easy to mix BASIC and assembly code, thereby speeding up programs by coding the slowest parts directly in machine code. Many successful programs, including games such as [[Radzone]] and applications such as [[PowerPage]], made use of this technique.
The CPC implementation of Locomotive BASIC was developed directly from [[Locomotive Software]]'s existing Z80 BASIC. The existence of this is cited as one of the reasons Locomotive requested that [[Amstrad]] change the CPC's processor from a 6502 to a [[Z80]].
The 464 and Spanish 472 shipped with BASIC 1.0 on ROM.
The language was revised and debugged for the 472 (British), 664, 6128 and Plus machines to become BASIC 1.1. Changes were minor but significant for the programmer, and included:
* DEC$ bug removed (in BASIC 1.0, it required two opening brackets and was undocumented)
== Variables ==
Many contemporary BASIC interpreters only supported the first few characters of a variable name as a discriminator. For example <code>WATER$</code> and <code>WALDO$</code> would refer to the same variable on a Commodore 64 or Apple II. But Locomotive BASIC allowed variable names up to 40 characters in length, so <code>THEQUICKBROWNFOXJUMPEDOVERTHELAZYDOGFAST$</code> and <code>THEQUICKBROWNFOXJUMPEDOVERTHELAZYDOGFATS$</code> could be use as separate variables.
=== Real (Floating Point) Numbers ===
==== <code>LOAD ‹file name›[,‹address›] </code>====
==== <code>LOCATE[#‹stream expression›][,] x,y</code> ====
</pre>
:'''P:''' the period number can be a figure between 0 and 4095 (2^12-1... 12 means that we have 12 tones (inclusive half-tones) in nine octaves on the CPC in sum and their distance between is the twelves square root of two), where 8 octaves are available. E.g. Octave 0 starts on middle C with number 478.
: To calculate the period you can use following formula: '''period=1,000,000/(16*frequency)''' or in short '''period=(6562,000500/frequency)'''
: (e.g. the note "A" with the frequency 440 Hz has the period 142 on the CPC)
</pre>
==== <code>SPEED INK <n1,n2> </code> ====: ...in conjunction with the SPEED INK command SPEED defines the frequency of colour changes if a colour change was defined. The duration is calculated by n1=... or n2 =duration/50 seconds. ==== <code>SPEED KEY <start,repeat> </code> ====: SPEED KEY command defines the delay after which a key repeat (for keys that do repeat). The first parameter is the delay before the first repeat and the second parameter is the delay between further repeats. Delay is in 1/50th of seconds. :When using it with low values, it is common to associate a key to reset the default speed of 30,2.:For example to associate the key 0 from the numpad:<pre>KEY 0,"SPEED KEY 30,2"+CHR$(13)</pre> ==== <code>SPEED WRITE <n> </code> ====: SPEED WRITE defines the speed at which data is to be saved or written to a cassette unit. n=1 means 2000 baud, n=0 (default) means 1000 baud. When reading from the tape, the correct speed is automatically selected.
==== <code>SQ (channel)</code> ====
: Beware: it only works here with BIT/number conditions one figure smaller than 1, 3, 7, 15, 31, 63 and 127
==== <code>MOD</code> ====
: ['''MOD''' returns a (rounded) rest (Modulos) after dividen has been devided by the devisor...]::'''Example:'''<pre>32767 mod 256255</pre>: Note: only works with numbers in the range -32768 and 32767 (&8000 < 0 < &7FFF)
==== <code>NOT</code> ====
: '''Associated keywords''': ASC, LEFT$, RIGHT$, MID$, STR$
: '''Example''':
<pre>
10 FOR X=32 to 255
| 1 | &01 | SOH | 0-255 | PRINTS CHARACTER TO SCREEN |
| 2 | &02 | STX | NONE | TURNS TEXT CURSOR OFF |
| 3 | &03 | ETX | NONE | TURNS TEXT CURSOR ON IN IMMEDIATE || | | | | MODE |
| 4 | &04 | EOT | 0-2 | SET SCREEN MODE |
| 5 | &05 | ENQ | 0-255 | PRINT CHARACTER AT GRAPHICS CURS. |
===============================================================================
</PRE>
==== <code><big>CINT (<numeric expression>)</big></code> ====
: ''BASIC 1.0 & 1.1''
| 17 | 145 (128+17) | File already exists. |
| 18 | 146 (128+18) | File does not exists. |
| 19 | 147 (128+19) | Direcotry Directory is full. |
| 20 | 148 (128+20) | Disc is full. |
| 21 | 149 (128+21) | Disc changed while file were open. |
========================================================================================
</pre>
: '''Example for ERR 31 / 32''':
<pre>
10 ON ERROR GOTO 1000
: ''BASIC 1.0 & 1.1''
: '''FUNCTION''': The JOY function reads a bit significant result from the joystick in the <integer expression> (either 0 or 1).
<pre>
================================
run
</pre>
==== <code><big>LEN (<string expression>)</big></code> ====
: ''BASIC 1.0 & 1.1''
run
</pre>
==== <code><big>LOG (n<numeric expression>)</big></code> ====: ''BASIC 1.0 & 1.1'': '''FUNCTION''': Calculates the natural LOGarithm (base of ''e'') of <numeric expression> which mustbe greater than zero.
: Returns the natural logarithm (to base e) of n.'''Associated keywords''': EXP, LOG10
: '''Example''':<pre>PRINT LOG(9999) 9.21024037</pre>==== <code><big>LOG10 (n<numeric expression>)</big></code> ====: ''BASIC 1.0 & 1.1'': '''FUNCTION''': Returns the logarithm to base 10 of <numeric expression> which must be greater than zero.
: Returns the logarithm to base 10 of n.'''Associated keywords''': EXP, LOG
: '''Example''':<pre>PRINT LOG10(9999) 3.99995657</pre>==== <code><big>LOWER$ (se<string expression>)</big></code> ====: ''BASIC 1.0 & 1.1'': '''FUNCTION''': Returns a new string expression which is a copy of the specified <string expression> but in which all alphabetic characters in the range A to Z are converted to lower case. Useful for processing input where the answers may come in mixed upper/lower case.
: Returns a copy of se in which all alphabetical characters are converted to lower case (see also UPPER) '''Associated keywords''': ExampleUPPER$
: '''1. Example''':
<pre>
A$="AMSTRAD":PRINT LOWERA$(="A1B2c3AMSTRAD":PRINT LOWER$(A$) - print a1b2c3Amstrad
</pre>
: '''2. Example''':
<pre>
10 a$="SEE HOW THE LETTERS CHANGE TO ... NUMBERS 1234 NOT..."
20 PRINT LOWER$(a$+"LOWER CASE")
see how the letters change to ... numbers 1234 not... LOWER CASE
</pre>
==== <code><big>MAX (<list of: numeric expression>)</big></code> ====
: ''BASIC 1.0 & 1.1''
: '''FUNCTION''': Returns the MAXimum value from the <list of: numeric expression>s.
: '''Example''':<pre>10 n=6620 PRINT MAX(1,n,3,6,4,3)run 66</pre>==== <code><big>MIN (<list of: numeric expression>)</big></code> ====''BASIC 1.0 & 1.1'': '''FUNCTION''': Returns the maximum MINimum value from the given <listof: numeric expression>s. : Example'''Associated keywords''':MAX
: '''Example''':
<pre>
PRINT MAXMIN (3,8,25,16,2.999.8,9,) - prints 25 2.999
</pre>
==== <code>MIN <big>PEEK (list of n<address expression>)</big></code> ====: ''BASIC 1.0 & 1.1'': '''FUNCTION''': Reports the contents of the Z80 memory location specified in the <address expression> which should be in the range &0000 to &FFFF (0 to 65535). In all cases PEEK will return the value at the RAM address specified (not the ROM), and will be in the range &00 to &FF (0 to 255)
: Returns the minimum value from the given list (see MAX)'''Associated keywords''': POKE
: '''Example''':<pre>10 MODE 1: ZONE 720 WINDOW 1,40,1,2:WINDOW #1,1,40,3,2530 PRINT "memory-address"40 LOCATE 20,1:PRINT "memory-contents"50 FOR n=1 to 6553560 p=PEEK(n)70 PRINT #1,n,"(&";HEX$(n);")";80 PRINT #1,TAB(20);p,"(&";HEX$(p);")"90 NEXTrun</pre>==== <code>PEEK (add)<big>PI</big></code> ====: ''BASIC 1.0 & 1.1'': '''FUNCTION''': The value of the ratio between the circumference and the diameter of a circle.
: Returns the contents of the specified memory location (0-65535)'''Associated keywords''': DEG, RAD
: '''Associated keywords''': VPOS, WINDOW
: '''Example''':
<pre>
10 MODE 1: BORDER 0:LOCATE 8,220 PRINT "use cursor left/right keys"30 WINDOW 1,40,12,12:CURSOR 1,140 FOR n=1 TO 19:PRINT CHR$(9);:NEXT50 IF INKEY(1)<>-1 THEN PRINT CHR$(9);60 IF INKEY(8)<>-1 THEN PRINT CHR$(8);70 LOCATE #1,2,2480 PRINT #1,"text cursor ";90 PRINT #1,"horizontal position = ";100 PRINT #1,POS(#0) - prints 1:GOTO 50run
</pre>
==== <code><big>REMAIN (<integer expression>)</big></code> ====
: ''BASIC 1.0 & 1.1''
: '''FUNCTION''': Returns count REMAINing count from the delay timer specified in <integer expression> (= timer number in the range 0 to 3), and disable it. If the delay timer was not enabled zero will be returned.
: For the reason that REMAIN is no normal command but a "function" it is necessary in case of a proper functionality to link the command.
: a an '''example ''' in combination with an interrupt delay timer 1:
<pre>
PRINT REMAIN(1)
</pre>
: or to stop several/all interrupts at the same time...
<pre>
A=REMAIN(0)=REMAIN(1)=REMAIN(2)=REMAIN(3)
</pre>
: Hint: Take care of interrupts due to parallel working (the same cycle time) or if they are stopped with the BREAK key. Interrupts which were disabled for a certain time will be stored in buffer (for a maximum of 128 entries) and made up their task afterwards in the priorisation as declared.
: further '''example''':
<pre>
10 AFTER 500,1 GOSUB 40
20 AFTER 100,2 GOSUB 50
30 PRINT "program running":GOTO 30
40 REM this GOUSB-Routine will not be called as it is disabled in line 80
50 PRINT:PRINT "Timer 1 will now be ";
60 PRINT "disabled by REMAIN."
70 PRINT "Time-units remaining were:";
80 PRINT REMAIN(1)
run
</pre>
==== <code><big>RIGHT$ (<string expression>, <integer expression>)</big></code> ====
: ''BASIC 1.0 & 1.1''
: '''FUNCTION''': Returns the number of characters (in the range 0 to 255) specified in the <integer expression> (=required length) parameter, after extracting them from the RIGHT of the <string expression>. If the <string expression> is shorter than the <integer expression>, the whole <string expression> is returned.
: '''Example''':
<pre>
10 MODE 1:a$="AMSTRAD computer"20 FOR n=1 TO 16:LOCATE 41-n,n30 PRINT RIGHT$("ABCDEFG"a$,3n) - prints EFG40 NEXT
</pre>
==== <code><big>RND [(<numeric expression>)]</big></code> ====
: ''BASIC 1.0 & 1.1''
: '''FUNCTION''': Returns the next RaNDom number in sequence if the <numeric expression> has a positive value or is not specified.
: If the <numeric expression> yields a value of zero, RND returns a copy of the last random number generated.
: If the <numeric expression> yields a negative value, a new random number sequence is started, the first number of which is returned.
: If n = 0, the random number generated will be the same as the last random number generated'''1.: Example''':
<pre>
PRINT RND(0)
Ready
</pre>
: '''2. Example''':: Exampel Example for generating an integer random number between 1 and 1000
<pre>
10 A=INT((RND(1)*1000)+1)
30 GOTO 10
</pre>
: '''3. Example''': : Exampel for For generating a random number between a lower and higher border
<pre>
10 INPUT "Low border",low
30 A=INT(RND(1)*(high-low))+low
40 PRINT A
</pre>
: '''4. Example''':
<pre>
10 RANDOMIZE
20 FOR x=1 TO -1 STEP -1
30 PRINT "rnd parameter=";x
40 FOR n=1 TO 6
50 PRINT RND(x)
60 NEXT n,x
run
</pre>
==== <code><big>ROUND (n<numeric expression>[,i1<integer expression>])</big></code> ====: ''BASIC 1.0 & 1.1'': '''FUNCTION''': Rounds n <numeric expression> to a number of decimal places or to the power of ten specified by i<integer expression>. If i is negative<integer expression> less than zero, the n <numeric expression> is rounded to give an absolute integer with i <integer expression> number of zeros before the decimal point.: Example:
: '''Associated keywords''': ABS, CINT, FIX, INT
: '''1. Example''':
<pre>
PRINT ROUND(1562.357,2):PRINT ,ROUND(1562.375,-2) - prints 1562.36 1600
</pre>
: '''2. Example''':
<pre>
10 FOR n=4 TO -4 STEP -1
20 PRINT ROUND (1234.5678,n)
30 PRINT "with integer expression";n
40 NEXT
run
</pre>
==== <code><big>SGN (<numeric expression>)</big></code> ====
: ''BASIC 1.0 & 1.1''
: FUNCTION: Determines the SiGN of the <numeric expression>. Returns -1 if <numeric expression> is less than 0, returns 0 if <numeric expression> = 0, and returns 1 if <numeric expression> is greater than zero.
: '''Example''':<pre>10 FOR n=255 to -200 STEP -2020 PRINT "SGN returns";30 PRINT SGN(n);"for a value of";n40 NEXT nrun</pre>==== <code><big>SIN (n<numeric expression>)</big></code> ====: Returns sine ''BASIC 1.0 & 1.1'': '''FUNCTION''': Calculates the Real value for the Sine of n in degree or radian mode <numeric expression>, defaulting to the Radian (see DEG and RAD)measure mode unless otherwise declared by a DEG command.
: '''Example''':<pre>10 CLS:DEG:ORIGIN 0,2020 FOR n=0 to 72030 y=SIN(n)40 PLOT n*640/720,198*y:NEXT50 GOTO 50run</pre>==== <code><big>SPACE$(<integer expression>)</big></code> ====: ''BASIC 1.0 & 1.1'': '''FUNCTION''': Creates a string containing i of spaces of the given length, (in the range 0-to 255)specified in the <integer expression>.
<pre>
</pre>
==== <code><big>SQ (<channel>)</big></code> ====
: ''BASIC 1.0 & 1.1''
: '''FUNCTION''': Reports the state of the Sound Queue for the specified <channel> which must be an integer expression, yielding one the values:
: 1: for channel A
: 2: for channel B
: 4: for channel C
: The SQ function returns a bit significant integer, comprising the following bit settings:
: Bit 0,1 and 2: the number of free entries in the queue
: Bit 3,4 and 5: the rendezvous state at the head of this queue
: Bit 6 : the head of the queue is held
: Bit 7 : the channel is currently active
: ... where Bit 0 is the least significant bit, and Bit 7 is the most significant bit.
: It can be seen, that if Bit 6 is set, Bit 7 cannot be set at the same time, and vice versa. Similarly if Bits 3, 4, or 5 are set, Bit 6 and 7 cannot be set.
: '''Example''':<pre>10 SOUND 65,100,10020 PRINT SQ(1)run 67</pre>==== <code>STR$<big>SQR (n<numeric expression>)</big></code> ====: ''BASIC 1.0 & 1.1'': '''FUNCTION''': Returns the SQuare Root of the specified <numeric expression>.
: Returns '''Associated keywords''': none : '''Example''':<pre>PRINT SQR(9) 3</pre>==== <code><big>STR$ (<numeric expression>)</big></code> ====: ''BASIC 1.0 & 1.1'': '''FUNCTION''': Converts the string <numeric expression> to a decimal STRing representation of number n.
: Useful for converting a number to a string in case of string manipulation. E.g. after converting a figure to a string then the most left character holds the sign: a minus in case the figure is negative and a space in case the figure is positiv.
: '''Associated keywords''': BIN$, DEC$, HEX$, VAL : '''1. Example''':
<pre>
10 FIGURE=-1599
30 PRINT FIGURE$
-1599
</pre>: '''2. Example''':<pre>
10 FIGURE=1769
20 FIGURE$=STR$(FIGURE)
1769
</pre>
: '''NOTE''': (there's a blank on the left-hand of the number "1769")
: If you want to add e.g. zero before the figure in case of a high score with fix digit-number then you're able to add zero(s) in front of a string instead of a figure.
: '''3. Example''':<pre>10 a=&FF:REM 255 hex20 b=&x1111:REM 15 binary30 c$== <code>STRING"***"40 PRINT c$+STR$(a+b)+c$run*** 270***</codepre> ====
==== <code><big>STRING$ (<length>,<character specifier>)</big></code> ====: ''BASIC 1.0 & 1.1'': '''FUNCTION''': Returns i copies a string expression consisting of the string <character specifier> repeated the number of time (in the range 0 to 255) specified by sin the <length>.
: Example'''Associated keywords''':SPACE$
: '''1. Example''':
<pre>
PRINT STRING$(340,"*") - prints ****************************************
</pre>
: '''2. Example''':
<pre>
PRINT STRING(40,42)
****************************************
</pre>
: '''NOTE''': the <character specifier> 42 refers to the ASCII value of the character '*' and is also equivalent to PRINT STRING$(40,CHR$(42))
==== <code><big>TAN (<numeric expression>)</big></code> ====
: ''BASIC 1.0 & 1.1''
: '''FUNCTION''': Calculates the TANgent of the <numeric expression>, which must be in the range -200,000 to +200,000.
: '''NOTE''': DEG and RAD can be used to force the result of the calculation to degrees or radians respectively.
: Returns the tangent of n. The DEG and RAD commands can be used to force the result to either mode'''Example''': <pre>PRINT TAN(45) 1.61977519</pre>
==== <code><big>TEST (<xco-ordinate>,<yco-ordinate>)</big></code> ====: ''BASIC 1.0 & 1.1'': '''FUNCTION''': Moves the graphics cursor to the absolute position specified by the <x> and <y co-ordinate>s, and reports the value of the ink at the new location.
: Moves the graphics cursor by x and y and returns the value of the ink at that position.'''Associated keywords''': MOVE, MOVER, TESTR, XPOS, YPOS
: '''Example''':<pre>10 CLS20 PRINT "Your are using pen number";30 PRINT TEST(10,386)40 PRINT "Try changing PENs and MODEs";50 PRINT "....then RUN again."run</pre>==== <code><big>TESTR (<xoffset>,<yoffset>)</big></code> ====: ''BASIC 1.0 & 1.1'': '''FUNCTION''': Moves the graphics cursor by the amount specified in the <x> and <y offset>s relative to its current position, and reports the value of the ink at the new location.
: Moves the graphics cursor by x and y relative to its current position and returns the value of ink at that position.'''Associated keywords''': MOVE, MOVER, TEST, XPOS, YPOS
: '''Example''':<pre>10 MODE 0:FOR x=1 TO 15:LOCATE 1,x20 PEN x:PRINT STRING$(10,143);:NEXT30 MOVE 200,400:PEN 140 FOR n=1 TO 23:LOCATE 12,n50 PRINT "pen";TESTR(0,-16):NEXTrun</pre>==== <code><big>TIME</big></code> ====: Returns time ''BASIC 1.0 & 1.1'': '''FUNCTION''': Reports the elapsed time since the computer was last switched -on or reset, (excluding periods when reading or writing to disc).: One Each second of real time is equal to the returned value = TIME/300.
: Returns '''Example''':<pre>10 CLS:REM clock20 INPUT "hour";hour30 INPUT "minute";minute40 INPUT "second";second50 CLS:datum=INT(TIME/300)60 WHILE hour<1370 WHILE minute<6080 WHILE tick<6090 tick=(INT(TIME/300)-datum)+second100 LOCATE 1,1110 PRINT USING "## ";hour,minute,tick120 WEND130 tick=0:second=0:minute=minute+1140 GOTO 50150 WEND160 minute=0:hour=hour+1170 WEND180 hour=1190 GOTO 60run</pre>==== <code><big>UNT (<address expression>)</big></code> ====: ''BASIC 1.0 & 1.1'': '''FUNCTION''': Return an integer(in the range -32768 to +32767) which is the two's twos-complement equivalent of addthe unsigned value of the <address expression>.
: Example'''Associated keywords''':CINT, FIX, INT, ROUND
: '''Example''':
<pre>
PRINT UNT(&FF66) - prints -154
</pre>
==== <code><big>UPPER$(<string expression>)</big></code> ====
: ''BASIC 1.0 & 1.1''
: '''FUNCTION''': Returns a new string expression which is a copy of the specified <string expression> but in which all alphabetic characters in the range A to Z are converted to upper case. The function is useful for processing input which may come in mixed upper / lower case.
: Gives copy '''Example''':<pre>10 CLS: a$="my, how you've grown!"20 PRINT UPPER$(a$)run</pre>==== <code><big>VAL(<string expression>)</big></code> ====: ''BASIC 1.0 & 1.1'': '''FUNCTION''': Returns the numeric VALue, (including any negative sign and decimal point) of se with all alphabetic characters the first character(s) in upper casethe specified <string expression>. :If the first character is not a number, then 0 is returned. If the first character is a negative sign or decimal point followed by non-numeric characters, a 'Type mismatch' error (13) will be reported.
:Returns the numeric value (including signs) of first numeric character(s) in se'''1. Returns 0 if se starts with a non-number. :Example''':<pre>PRINT VAL("-12.34x"),VAL("A-12") - prints -12.34 0
</pre>
:'''Exception''': if <se> starts with "&" + character (and it's between "A" and "F") the whole character will be handled like a hexadezimal numeric character (...often used in DATA Loaders). The returning numeric value is a signed integer (16-Bit Word). : '''2. Example''':<pre>PRINT VAL("&A") - returns a 10
PRINT VAL("&7FFF") - returns a 32767
PRINT VAL("&8000") - returns a -32768</pre>: '''3. Example''':<pre>10 CLS: PRINT "I know my times tables!"20 PRINT:PRINT "Press a key (1 to 9)"30 a$=INKEY$:IF a$="" THEN 3040 n=VAL(a$):if n<1 or n>9 then 3050 FOR x=1 to 1260 PRINT n;"X";x;"=";n*x70 NEXT:GOTO 20run</pre>==== <code><big>VPOS (#<stream expression>)</big></code> ====: ''BASIC 1.0 & 1.1'': '''FUNCTION''': Reports the current vertical POSition of the text cursor relative to the top of the text window. The <stream expression> MUST be specified, and does NOT default to #0.
: '''Example''':<pre>10 MODE 1:BORDER 0:LOCATE 8,220 PRINT "use cursor up/down keys"30 WINDOW 39,39,1,25:CURSOR 1,140 LOCATE 1,350 IF INKEY(0)<>-1 THEN PRINT CHR$(11);60 IF INKEY(2)<>-1 THEN PRINT CHR$(10);70 LOCATE #1,3,2480 PRINT #1,"text cursor ";90 PRINT #1,"vertical position =";100 PRINT #1,VPOS(#0):GOTO 50run</pre>==== <code><big>XPOS</big></code> ====: Returns ''BASIC 1.0 & 1.1'': '''FUNCTION''': Reports the current horizontal (xX) position POSition of the graphics cursor.
== Other Basic Dialects available for the CPC ==