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 <BAS$Output> 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.)

_____________________________________________________________________________
