BAS -- the Basic Assembler Supplement ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Once upon a time, this was meant to be a commercial Straylight product. I never got around to writing the documentation. BAS is Yet Another tool for people who use the BASIC assembler. The odd thing is that Straylight use Acorn's `objasm' assembler for all our `real' work. The idea was to produce a procedure library for generating linkable AOF code from BASIC, and the functionality grew from there. Features are: * Generates AOF version 2 object code. * Handles literal pools. (I'll come to them.) * Translates (a simple subset of) objasm header files. * A small collection of other tools. BAS is almost entirely written in assembler, with a BASIC procedure library thrown in. You don't need to worry about that -- the code is tacked on the end of the BASIC file, so it all comes as one package. _____________________________________________________________________________ LICENCE BAS is Free Software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. BAS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with BAS; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Of course, you can do what you like with the output of BAS. _____________________________________________________________________________ AOF CODE GENERATION I'll assume you understand how linkable objects work. We'll be here all day if you don't. All code must be in an `area'. You start an area by saying FNarea("NAME", "ATTRIBUTES") The name can be anything you like; common names are `C$$Code' for C code, and similar. Dollars are popular for some reason. The attributes define properties of areas, which in turn affect positioning and access permissions. We've tried to use the same names as Objasm here: CODE -- area contains code (implies `READONLY') COMDEF -- definitons for a common area COMMON -- overlay areas with this name (implies `NOINIT') NOINIT -- initialise this area with zeros READONLY -- I don't need to write to this area DEBUG -- contains debugging information By default, areas with the same name are concatenated. You get given symbols `NAME$$Base' and `NAME$$Limit' to tell you the boundaries of the complete area called `NAME', which is useful for building tables. Attribute names can be separated by anything you like -- they're parsed by the BASIC library using INSTR. Objasm uses commas, so I tend to too. A program generating AOF code will usually look something like this: LIBRARY "libs:BAS" PROCbas_init PROCbas_aofInit(SIZE) FOR o%=4 TO 6 STEP 2 [ opt o% FNpass FNarea("Foo$$Code", "CODE, READONLY") ... ] NEXT PROCbas_aofSave / PROCbas_aofSaveAs(FILENAME) Here, `SIZE' is the amount of space to reserve for the code, and `FILENAME' is the name to save it as. The PROCbas_aofSave function uses the value of as a default filename: this allows you to declare an alias Set Alias$BasAsm Set BAS$Output %1|mBASIC -quit %0 which you can use in Makefiles. You're not limited to one assembly per source file -- you can create any number of object files, although each one must start with a call to PROCbas_aofInit and end with PROCbas_aofSave[As]. Note that you must begin each assembly by calling FNpass -- this will set the P% and O% variables appropriately for the assembly, and make sure that the BAS code understands where you are in the assembly. You can export a synbol (so that other object files can see it) by calling FNexport; for example: FNexport("hello") .hello stmfd r13!,{r0,r14} ldr r0,FNlitsz("Hello, world") swi "OS_Write0" ldmfd r13!,{r0,pc}^ FNltorg Because AOF allows a wider range of identifiers than BASIC, you can `alias' names as you export them; for example: FNexport("foo", "My$$FooishThing") takes the value represented in the BASIC variable `foo' and exports it as `My$$FooishThing' in an object file. You import values in the same way: FNimport("malloc") FNexport("xmalloc") .xmalloc stmfd r13!,{r1-r3,r12,r14} bl malloc cmp r0,#0 ldmnefd r13!,{r1-r3,r12,r14} ldr r0,FNliterr(1,"Not enough memory") swi "OS_GenerateError" FNltorg (I'll explain the FNlit... and FNltorg macros later. Bear with me.) You can also FNimportAs a symbol with a funny name: FNimportAs("Image$$RW$$Limit","program_end") fetches the limit of the read-write area of the image, telling you where the program ends. This is quite handy. How this all works is possibly interesting. It (ab)uses offset assembly, directing output (O%) to a buffer BAS allocates for you, and starting P% at &FC000000, which is an illegal instruction, and hopefully unlikely to appear in `real' code. Once the assembly's finished, BAS runs through and picks out references to things in with addresses &FCxxxxxx and creates relocation directives for them in the AOF file. Imported things get given addresses &FDxxxxxx. Assuming these values don't appear in genuine code, we're OK. Just in case you want to make arbitrary data appear in the object, BAS has directives for disabling and reenabling relocation: FNnoreloc ... FNreloc won't munge anything between them. Finally, the call FNentry marks the entry point in an AIF program -- execution will start here. That's all there is to it. _____________________________________________________________________________ LITERALS AND LITERAL POOLS Data like strings and absolute addresses are a pain in the BASIC assembler -- you have to make up labels for them, and then reference them. BAS tries to handle this sort of thing for you, which is rather more pleasant of it. At any point in an assembly, BAS is building a `literal pool'. You create a literal using one of the supplied directives, and BAS is responsible for putting it in a literal pool; it gives you the address at which the literal will be placed in the finished output. The current literal pool will be written to the assembly when you call FNltorg. A literal pool is also written at the very end of the assembly. Essentially, then adr r0,FNlitsz("Hello, world") ... FNltorg is equivalent to adr r0,hello_string ... .hello_string equs "Hello, world" + CHR$(0) The literal creation macros provided are: * FNlitw(WORD) stores a 32-bit value WORD at a word-aligned address, e.g., FNimportAs("Image$$RW$$Limit", "prog_end") ldr r0,FNlitw(prog_end) puts the address of the end of the program in R0. * FNlits(STRING) stores a string, unterminated, and non-word- aligned. This isn't very useful. * FNlitmagic(STRING) stores an unterminated string at a word- aligned address. This is for things like ldr r1,FNlitmagic("TASK") so you don't have to remember that this is &4B534154. I remember it anyway. * FNlitsz(STRING) stores a null-terminated string at a non-word- aligned address. This is useful for all kinds of messages. * FNliterr(NUM, STRING) stores a RISC OS error block at a word- aligned address. If you need some kind of structure which isn't provided, you can build it yourself. PROClitStart informs BAS that it's meant to assemble a literal; FNliteral completes the literal, returning the address where BAS will eventually put it. FNlitAlign does the same job, only it word-aligns the literal. _____________________________________________________________________________ READING OBJASM HEADERS FNget(FILENAME) reads an Objasm-format header file. It's not perfect but it tries hard. Objasm directives supported are: `^', `#'. `EQU', and `IMPORT'. This is hopefully enough for most purposes. The `*' synonym for `EQU' is also supported. BAS can't understand Objasm macros. Instead, it understands `active comments'. The only one implemented is `LIB': ;+ LIB FILENAME which loads the BASIC procedure library FILENAME. A BAS macro library `foo' must contain a function FNfoo_test, which is used by BAS to see whether the library is loaded. _____________________________________________________________________________ OTHER USEFUL TOYS BAS defines a whole slew of constants: * r0-r15, R0-R15, a1-a4, v1-v6, sb, sl, fp, sp, SP, lr, LR, lk, LK, pc and PC are all set to the appropriate register numbers. * EQ, NE etc. are set to the appropriate condition code values. FNalign aligns the output position to a word boundary, padding with zero bytes. FNreserve(SIZE) writes SIZE zero bytes to the output. FNbin(FILENAME) inserts the contents of the file FILENAME into the output, protecting it from relocation. FNfSize(FILENAME) returns the size of the file FILENAME. This can be handy for setting up sprite areas. FNws_start clears a storage-area counter to zero. FNws_base(VALUE) sets the counter to VALUE. FNws_align word-aligns the counter. FNws(SIZE) returns the counter, and increases it by SIZE. FNws_word, and FNws_byte are equivalent to FNws(4) and FNws(1) respectively. These are useful for laying out workspace areas and data structures. FNadrl(REG, ADDR) assembles a long ADR; FNadrccl(COND, REG, ADDR) assembles a conditional long ADR. FNaddl(REG, BASE, OFFSET) assembles a long ADD; FNaddccl(COND, REG, BASE, OFFSET) does the same conditionally. FNldrl(REG, ADDR) assembles a long LDR; FNldrccl(COND, REG, ADDR) assembles a conditional long LDR. FNldrrl(REG, BASE, OFFSET) assembles a long non-PC-relative LDR; FNldrrccl(COND, REG, BASE, OFFSET) does that conditionally. _____________________________________________________________________________ BUILDING BAS FROM SOURCES This is quite involved. You need to have: * Acorn Desktop Assembler or later * An implementation of `sed' -- I recommend the port of GNU sed. * Straylight's basic library set (`header', `swis' and `stream') available from the same place you got this from. * A copy of Cy Booker's `ccrunch' BASIC compressor. If you don't have this, edit `remnames' and remove the `ccrunch' line -- the BAS output file will be a little larger, but that's OK. Build the ARM code part by running the Makefile. Now run `Setup' to mangle the nice BASIC library part, and to tack the code on the end. That should be it. _____________________________________________________________________________ WHAT USE IS BAS? Dunno. Straylight were hoping to sell it for maybe fifteen quid a go. We use it in-house for building simple AOF-outputting tools; our message-file and template-file compilers are BAS-based, for example. (These are available as part of the SDLS and Sapphire packages, if they're out yet.) _____________________________________________________________________________