Locomotive Basic is a proprietary dialect of the BASIC programming language written by Locomotive Software.

It was modified (many custom features to support the platform) for use on the Amstrad CPC as "Amstrad BASIC" (where it was built-in on ROM).

Later Locomotive BASIC-2 was produced for the IBM PC compatibles platform as a GEM application on the Amstrad PC1512 and 1640 and was a descendant of Mallard BASIC, the interpreter for CP/M supplied with the Amstrad PCW.

There are two published versions of Amstrad BASIC; 1.0 which only came with the CPC464 (and had a buggy DEC$ function), and 1.1 which corrected this and shipped with all other CPCs. BASIC 1.1 was also included in the Amstrad CPC Plus series machines, as part of the included game cartridge.

Development Development was based on existing work recently undertaken writing Mallard BASIC for Acorn Computers Z80 addon for the BBC Micro. It is reported to have taken around 12 weeks to enhance the existing code, and was "very influenced" by BBC BASIC, though adding additional functions to do things that would have required assembly language on the BBC.

The "ARNOLD" platform (the CPC464 development codename) was designed to be solid enough to attract the "serious" users Amstrad wanted. Locomotive were engaged to produce the firmware and BASIC for the platform. The mix produced a comprehensive result with technical capabilities almost unheard of in any one contemporary platform - the tidy approach to firmware access with fixed jump blocks, the hypothecation of the firmware into modules, the clever hardware allowing expandability, with multiple pages of RAM and ROM all occupying the same address-space, simultaneously accessible, re-locatable display memory etc. many of which appeared only partially, or not at all on rival platforms.

Features of the CPC platform

If a programmer is concerned only with writing in a rich BASIC dialect they have an excellent machine on which to ply their skills and there is no reliance or expectation the programmer will ultimately have to resort to machine code or study the function of the hardware to produce good quality software. However, to fully comprehend the ethos behind Amstrad BASIC and push the machine to its limits, it is necessary to understand, at least superficially, the design approach to the machine as a whole.

Fundamentally, the CPC platform consists of clever hardware with a rich firmware to exploit it; compartmentalised into sub-systems with standardised jump tables to access functionality. Amstrad BASIC was written to expose as much of this platform and firmware as possible to the programmer.

Firmware jump blocks At a machine level on the CPC platform, application software need not be concerned with code moving about in the actual firmware e.g. for different versions and re-assembly. In many contemporary machines, re-assemblies of the firmware and subsequent moving of routines can cause major headaches. A notable example was the disastrous 1983 Timex modified ZX Spectrum (the ROM modified as T/S 2000 BASIC). Due to a lack of structure, many system calls moved in the subsequent re-assembly. The resultant firmware was incapable of running many extant ZX Spectrum programs - instantly denying access to a huge software library and alienating users. With the Locomotive approach to system calls, so long as programs use the documented addresses, this is never an issue. Consider the firmware call $BB5A TXT_OUTPUT (put a single character on the screen), it is ubiquitous - but note from below the firmware vectors are different in BASIC 1.0 and BASIC 1.1 ($9400 vs $93FE). As long as programs use the published function address (i.e. $BB5A), software will run on all CPC platforms with no issues.

The firmware jumps have changed between versions but the address for TXT_OUTPUT remains $BB5A.

The firmware modules

Kernel Key Manager Text VDU Graphics VDU Screen Pack Cassette / AMSDOS (AMStrad Disc Operating System) Sound Manager Machine Pack Maths Pack

Amstrad BASIC leveraged and exposed the platform being written to take full advantage of the machine, bundling up BASIC code into the requisite calls into the firmware, providing specific commands for functionality rather than reliance on generic *FX or PEEK & POKE statements required to access features on competitor platforms.

BASIC and version differences Internal version numbers of the BASIC ROM differ from the accepted, published versions discussed here.

Although not unique, the platform was unusual among contemporaries in that it used the ASCII character set rather than its own idiosyncratic form - an arguable strength as it instantly made the CPC platform a more serious offering, somewhat underpinning Amstrad's marketing claims of it being capable of professional business use.

In common with many other dialects, multiple program statements are separated on a single line using the colon :.

WHILE/WEND is the only loop construct other than FOR/NEXT.

Reserved words (functions, commands, statements etc.) are capitalized in a program by virtue of being tokenized. This reduces program size and accelerates execution. Listing a program then decodes the token to its reserved word which are all in upper case in the token table (it was this method which revealed the presence of DEC$ in BASIC 1.0 - see below). When saving a basic program, the fully tokenized form is saved in a binary format. Alternatively, saving with option "A" creates a fully text compatible ascii file e.g. SAVE "MYPROG",A

BASIC 1.0

Supplied with the Amstrad CPC464, it was a good release with only a single notable bug, being the DEC$() function (it provides PRINT USING style output but as a string rather than screen output). No time was available to fix this before the firmware had to go for ROM chip production. Consequently, the function was simply left un-documented and pre-fixed to force a syntax error when encountered. The curious programmer found the word dec$ capitalised in their code indicating it was a recognised reserved word - inexplicable at the time, until the history of the bug and disassemblies of the BASIC ROM became available.

Exploration of the BASIC 1.0 ROM can find the remnants of the function and its crippled token table entry.

BASIC 1.1 Supplied with the CPC664/6128, besides fixing DEC$, it introduced (among others) the following additional keywords:

COPYCHR$, CURSOR, FILL, FRAME, GRAPHICS PAPER, GRAPHICS PEN and MASK - continuing in the vein of BASIC exploiting the firmware and any advancements therein.

It also introduced a clumsy but adequate method of trapping disc errors with the DERR function. Errors still had to be trapped in the usual way with ON ERROR GOTO ... but the single "Broken In" error 32 could now be expanded to provide more detail. Examining DERR gives up to ten further indicators. Values returned are the AMSDOS error number plus 128, e.g. 14 is reported as a DERR value of 142 (14+128). Possible errors included the usual file not found, disk full etc. as well as the more specific "142 - The stream is not in a suitable state" and "149 - Disc changed while files were open". DERR is a function of BASIC 1.1 and so was not available on the FDI1 expanded CPC464, with its combination of AMSDOS and BASIC 1.0. Disc errors on the CPC464 were obtainable but relied on PEEKing into the AMSDOS system area, such program being incompatible with CPC664/6128 due to the AMSDOS variable addresses having moved.

Variables and functions Variable names must begin with a letter and can be up to 40 characters long; made up of A-Z, the digits 0-9 and the . symbol. Names are case-insensitive, but case-as-typed is maintained in program listings.

Three data-types are supported signified by an identifier suffix on the variable name and can be selectively presumed by use of the DEFINT, DEFREAL and DEFSTR directives: Strings with type marker $, e.g.MyString$, up to 255 bytes in length 2-byte signed integers with type marker %, e.g.My.Integer%, in the range -32768 to +32767 5-byte real (floating point) with type marker !, e.g.My99Real!, in the approximate range +/-10E+38 to +/-4E-39 with nine significant digits. Type real is the default for un-typed variable names. All Floating point mathematics is the result of Amstrad BASIC exposing the MATHS_PACK of the firmware (very complete and entirely usable from machine code).

Unlike some dialects, variable names are unique down to type, e.g. S% and S! are separate variables.

Dimensioned variables (arrays) have 0 as their lowest element number(s), and can use square brackets around their elements which helps distinguish them from other parentheses in complex expressions in program code. e.g. A(3) can be written as A.

Named single-line functions are supported and follow the type and name convention of variables but pre-fixed with FN. e.g. FNMy.Func!

The address of a variable in memory can be determined with the @ directive, e.g. PRINT @A$. This replaces the VARPTR() function found in other dialects of BASIC and is mostly used to pass a variable pointer to machine code routines allowing bi-directional data transfer.

Integer and Real variable values are stored in the variable table (see memory map below). String variable contents are stored on the heap. This allows the aggregation of fragmented memory via Garbage Collection. Consequently, for strings, the variable table holds a three byte descriptor made up of the length of the variable and the address in the heap where the actual contents are to be found.

Two missed opportunities? BBC Basic uses a clever method for handling some integer variables, which despite claims of its heavy influence on Locomotive BASIC (and by extension Amstrad BASIC), was not replicated in the latter. BBC BASIC has a 54-byte table reserved for the values of the 27 single letter integers @%, A%, B% ... Z% so they do not occupy nor require lookup in the variable table - a simple left-shift and addition is all that is required to determine the value of a given variable. This makes access to the subject variables very fast and considering their ubiquitous nature, it is perhaps, surprising this mechanism was not used in Amstrad BASIC.

For strings, the descriptor is three bytes in the variable table, made up of a length descriptor and a further (little-endian) address of the actual payload. Had the value returned by been the two byte address in the heap (and the length prefix moved to the heap also) this would have substantially reduced access time by replacing the two address lookups with just one.

Graphics and colour Handling of graphics and colour was straightforward with commands such as DRAW, PLOT, INK, PAPER, BORDER etc.

With Amstrad BASIC V1.1 (CPC664 onwards), the FILL command permitted painting complex-shaped, bounded areas of the screen and an eight bit mask could be applied DRAW enabling dotted lines etc.

A table giving the numeric codes for the 27 system colors was printed on the casing of the built-in 3" disk drive on the 664 and later machines.

Text handling Text moves through the system using a concept of "streams" numbered 0–9, appropriate commands use the hash # symbol to pre-fix the stream number.