Locomotive BASIC
This article needs additional citations for verification. (December 2009) |
First appeared | 1984 |
---|---|
OS | AMSDOS |
License | Proprietary |
Influenced by | |
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) and used 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,[1] the interpreter for CP/M supplied with the Amstrad PCW.
There are two versions of Amstrad; BASIC 1.0 which only came with the CPC464 (and had a buggy DEC$
function), and BASIC 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
[edit]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.[1]
Features
[edit]The Amstrad CPC firmware was compartmentalised into roughly nine different sub-sytems with standardised jump tables to access functionality.
Kernel Key Manager Text VDU Graphics VDU Screen Pack Cassette / AMSDOS Sound Manager Machine Pack Maths Firmware
Application software was not affected by code moving about in the actual firmware (e.g. for different versions and re-assembly) so long as it stuck to the documented jump block addresses. Amstrad BASIC was written to take full advantage of the firmware, bundling up BASIC code into the requisite calls into the firmware[2], but providing specific commands and functions for it rather than the reliance on generic *FX or PEEK & POKE statements.
Although not unique, the firmware 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 platform a more serious offering, underpinning Amstrad's marketing claims of it being capable of serious business use. Amstrad BASIC supported the firmware and hardware platfom very well, featuring simple, dedicated commands for most tasks. In common with many other dialects, multiple statements were seperated on a single line using the colon :
. Simple single-line functions were suported and WHILE/WEND
was the only available loop construct beyond FOR/NEXT
.
Handling of graphics and colour was straightforward (such as DRAW
, PLOT
, INK
, and PAPER
in all versions; plus FILL
in v1.1). A table giving the numeric codes for the 27 system colors was printed over the built-in 3" disk drive casing on the 664 and later machines.
Text Handling
[edit]Text moved through the system using a concept of "streams" numbered 0-9 and indicated by the hash #
symbol. Stream 8 was the centronics parallel port and stream 9 was the currently open file (either casetter or disc), e.g. to send a line of text to the printer one could use the command PRINT#8,"Hello World!"
When later, serial interfaces became available, #8 could be diverted by patching the firmware jumpblock.
Stream 0 was the default text screen, streams 1-7 were for text windows. The TEXT_VDU firmware pack allowed for the creation of multiple text windows each with their own cursor, dimensions, content, colours etc. Text windows were simple screen areas and could overlap but there was no built in provision to preserve the contents of windows "under" others, thus careful attention was required to prevent corruption. Later, machine code routines became available to intercept the firmware calls (again utilizing the power of the jump_block) and preserve the content of a window just before another was drawn over it, restoring it when the new window closed. This gave the much more useful Windows look to which we have grown accustomed - remembering "pop-ups" were a novel concept in 1984. When using the text windows, screen scrolling relied on software (as only part of the screen content moved) rather than using the hardware to scroll the entire screen by adjusting the screen-base offset of the MC6845 CRT controller in RAM. Two SCREEN_PACK firmware calls were provided for the two forms; $BC4D SCR_HW_ROLL (Scrolls the entire screen up or down by eight pixel rows i.e one character line) and $BC50 SCR_SW_ROLL (Scrolls part of the screen up or down by eight pixel lines). The latter can result in a pronounced "ripple" when scrolling large areas.
Advanced features
[edit]A stand-out feature among almost every other BASIC of the time was a timer-based software interrupt mechanism at 50 ticks per second using the EVERY
, AFTER
and REMAIN
commands (leveraging the KERNEL for its software interrupts). Four timers were available (0-3, with associated reducing priority - a BASIC imposition not a platform limitation) and allowed the programmer to run sections of their BASIC program after a given delay, repeating if required, e.g. EVERY 50,0 GOSUB <line>
produces a repeating one-second call with no further work.
Amstrad BASIC granted a relatively high level of control over the sound chip, an AY-3-8912 with 3 melodic channels and 1 noise channel and interrupt driven sound generation with comprehensive co-ordination of the three audio channels and associated BASIC commands: SQ()
(Sound Queue), ON SQ <channel> GOSUB <line>
etc. The same chip was also used on late-model ZX Spectrums, as well as the Atari ST and MSX computers, but none of those had such a complete built-in SOUND
command. Many things, from selecting a particular channel or a combination of channels, setting envelopes, volume, pitch, noise, and so on could be done with a single SOUND
command, with up to 7 parameters. Granted, especially complex and/or low-level techniques could not be done with BASIC due to their requiring more precise or direct access to the hardware, e.g. especially complex music from trackers (including simulated chords using arpeggios, etc.), the playback of digitally sampled sounds as in the game RoboCop for example, and so on.
Disk, tape, and file management were managed by BASIC itself, and were usually good enough for simple file management, with commands such as GET
, PUT
, ERASE
, SAVE
, MERGE
, RUN
, CAT
, LOAD
etc. In fact, during those years, the BASIC supplied as standard with most low-cost home computers also acted as a more or less simple operating system.
Also available was a parametric LOAD
command, allowing, for example, to load a file containing "raw" picture data into video memory, causing it to be displayed, with a couple of BASIC instructions. Adding a memory address as parameter to the commands LOAD
or SAVE
would allow easy loading of raw uncompressed 16 KB screen pictures.
Machine Code and RSXs
[edit]Machine code was well supported (but not to the amazing in-line assembler heights of the BBC Micro). There was an easy method of allocating "safe" memory (see below) and once loaded either from disc or cassette or POKE
d in, The code could be CALL
ed with the means to pass parameters in and retrieve - even using BASIC variables (including strings) as the container using CALL <addr>,Q,@X$
. Variables (and immediates) were passed directly as their value (ByVAL), the @
directive passed the address of the variable (ByREF), thus machine code could pass values directly back to a variable or manipulate its contents. This made transfer of data between a BASIC program and machine code trivial.
Another method, leveraging the firmware, allowed for named sections of machine code using a feature known as Resident System eXtensions (RSX). The code for the RSXs had a very specific header structure - the RSX names, their entry points and 4 bytes of scratchpad RAM for the firmware to link in and out of the list. A call was made using the KERNEL routine $BCD1 KL_LOG_EXT to initialise the names and they were then available to use throughout the system (by finding each named routine using $BCD4 KL_FIND_COMMAND). An advantage of RSXs to the firmware was it allowed calling to sections of code throughout the entire banked memory map. Code could simply call sideways by searching for the RSX name and from the link structure, the firmware immediately knows which ROM/RAM bank the code occupies and seamlessly does the "side call" using KL_SIDE_CALL
or KL_SIDE_PCHL
to access and run the actual program code - no matter where it resides across the entire memory map in all ROM/RAM banks - the only restriction being that it must be broken into self-contained 16KB chunks.
Although not a BASIC feature, continuing its ethos of exposing the platform to the BASIC programmer, Amstrad BASIC embraces this mechanism of RSXs. There is no way to directly load RSXs from BASIC - the machine code and data structures must be loaded into RAM as binary data and a CALL made to the initialization point, but from then on the names can be used in the program as if they were BASIC reserved keywords - each being introduced by the bar |
symbol, the remaining syntax being identical to CALL. e.g. |STORE,Q,@X$
.
Memory Allocation
[edit]In many contemporary systems, allocating a block of memory seemed an after-thought with no "proper" method of achieving it. Innovative methods were adopted by programmers which resulted in some, often, highly cryptic and fiddly methods, such as creating REM
statements with the required length and then POKE
ing the data into known addresses in the BASIC program line - the command LET L=USER 16514
was famous in ZX81 circles.
Amstrad BASIC provided a system variable to indicate the last used byte of program space; which extended for approximately 42 kilobytes from address $0170. HIMEM
being the last address available. The MEMORY
command could be used to adjust this last address and through a combination of the two, space could be reserved easily and controllably - a primitive form of malloc()
. For example, suppose a block of 1KB was required to store a machine code routine, space could be provided (with full awareness of BASIC) using the following code: MEMORY HIMEM-1024
. The machine code could them be safely loaded into and called at HIMEM+1
, safe from any other processes.
The CPC range made provision, through the SCREEN_PACK, for User Defined Graphics (UDG) whereby the 8x8 pixel matrix for a character could be redefined allowing for the creation of special characters. By default, the top 16 characters (128 bytes) were "soft" and if that was sufficient, no further adjustment was required. Following on the ethos of exposing the platform capabilities in BASIC, it passed forward this functionality and the memory for UDG was allocated from program space (i.e. the more characters, the lower HIMEM
). More memory (and thus more characters) could be allocated with the SYMBOL AFTER <char>
command - where char represents the character code from which UDG is available. e.g. SYMBOL AFTER 32
would allow the entire printable ASCII character set to be redifined, with the corresponding reduction in available program space (1784 bytes). The firmware copied the default character matrices from ROM into the newly defined space, thus redefining the character matrix was not essential for clarity. If UDG were not required and program space was tight, memory could be given back (defaulting to the fixed character matrices) with the command SYMBOL AFTER 255
.
A Specific Omission - Memory Bank Switching
[edit]Although as a BASIC implemenation, Amstrad BASIC is fairly complete and goes to great pains to support the CPC platform, one notable omission in all versions is built-in access to the memory bank switching mechanism.
Through an arrangement of the custom circuitry switching chunks of 16KB into one of the four 16KB pages in the Z80 memory map ($0xxx, $4xxx, $8xxx, $Cxxx), all CPC models supported fairly advanced (for the time) bank-switching and physical memory-map management to increase the potential memory storage. Z80 writes were always to RAM regardless of switched state, e.g. if the lower ROM (where the firmware resides, $0-$3FFF) is switched into the memory map, LD ($800),A
would write the accumulator to $800 in RAM and it would not be lost (as a normal result of trying to write to a ROM). Various configurations of where the switched banks occurred in the memory map were supported by the KERNEL.
The CPC464/664 can address upto 256KB (of which were 8, 16KB "sideways" ROMs although only six or five were avalable due to BASIC and Firmware requirements and Disc OS in the 664) and the CPC6128 could address upto 1MB (including 16, 16KB sideways ROMs - of which only 13 were available due to requirements of BASIC, Firmware and Disc OS). The Kernel provides several methods of accessing banked memory; some involve providing a pseudo 24bit address via a vector, extending the HL register pair using the C register and for ROMS, encoding the high or low ROM number in the top few bits of HL, executing the machine code there. Still others would retrieve data from a bank as if it were in the mainstream Z80 address map or retrieve data from RAM regardless of the state of the high and low ROMs being switched in. Kernel routines to access banked memory include KL_FAR_CALL, KL_FAR_JMP, KL_RAM_LAM
among others. The kernel will set up structures on the Z80 stack to ensure the safe capture of inbound RET
urns from banked memory.[3]
Despite BASIC not providing support for this kernel functionality, use of banked memory in a BASIC program was certainly possible but required the use of a CALL
to machine code routines to do the switch and take advantage of the result. Banked memory could not be used for BASIC program code, only the main, un-banked 64KB, thus BASIC program size was largest at around 42KB. Storing machine code in banked memory was the method required to increase program space.
A common use for banked memory was to store screen images and then rapidly switch between them. Some applications also implimented "RAM Discs" but these were not supported under the Cassette/AMSDOS firmware pack, being quite proprietary.
Contemporary rivals
[edit]This section possibly contains original research. (July 2019) |
Unlike the Commodore 64's built in BASIC (Commodore BASIC), which had no dedicated commands for graphics or sound, Amstrad BASIC allowed doing pretty much anything that was within the standard capabilities of the machine. This was not unimportant, as some other machines of the era required programmers to use assembler in order to access the full sound and graphics capabilities of their system. MSX, Sinclair Spectrum and some others offered a similar, more or less complete command set for their sound and graphics capabilities. The only things going clearly beyond BASIC capabilities were the overscan modes used in games and demos, 27-color graphics modes, digital sound playback, and smooth scrolling.
Unlike Sinclair BASIC or Commodore 64 BASIC, which had various keyboard command shortcuts or specialized keys for choosing symbols or colors, Amstrad BASIC keywords were typed in full and the interpreter parsed, recognized and tokenised them. However, there were abbreviations like "?
" for "PRINT
" and a few shortcuts. Programs could be saved onto Compact Cassette or floppy disk and retrieved as binary or ASCII files.
References
[edit]- ^ Smith, Tony (12 February 2014). "You're NOT fired: The story of Amstrad's amazing CPC 464". The Register. Retrieved 17 February 2014.
- ^ https://www.theregister.com/Print/2014/02/12/archaeologic_amstrad_cpc_464/
- ^ https://cpctech.cpc-live.com/docs/firmware.pdf