| 1 | BAS -- the Basic Assembler Supplement |
| 2 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 3 | |
| 4 | Once upon a time, this was meant to be a commercial Straylight |
| 5 | product. I never got around to writing the documentation. |
| 6 | |
| 7 | BAS is Yet Another tool for people who use the BASIC assembler. The |
| 8 | odd thing is that Straylight use Acorn's `objasm' assembler for all |
| 9 | our `real' work. The idea was to produce a procedure library for |
| 10 | generating linkable AOF code from BASIC, and the functionality grew |
| 11 | from there. Features are: |
| 12 | |
| 13 | * Generates AOF version 2 object code. |
| 14 | * Handles literal pools. (I'll come to them.) |
| 15 | * Translates (a simple subset of) objasm header files. |
| 16 | * A small collection of other tools. |
| 17 | |
| 18 | BAS is almost entirely written in assembler, with a BASIC procedure |
| 19 | library thrown in. You don't need to worry about that -- the code |
| 20 | is tacked on the end of the BASIC file, so it all comes as one |
| 21 | package. |
| 22 | |
| 23 | _____________________________________________________________________________ |
| 24 | |
| 25 | LICENCE |
| 26 | |
| 27 | BAS is Free Software; you can redistribute it and/or modify |
| 28 | it under the terms of the GNU General Public License as published by |
| 29 | the Free Software Foundation; either version 2, or (at your option) |
| 30 | any later version. |
| 31 | |
| 32 | BAS is distributed in the hope that it will be useful, but WITHOUT |
| 33 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| 34 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
| 35 | License for more details. |
| 36 | |
| 37 | You should have received a copy of the GNU General Public License |
| 38 | along with BAS; if not, write to the Free Software Foundation, Inc., |
| 39 | 675 Mass Ave, Cambridge, MA 02139, USA. |
| 40 | |
| 41 | Of course, you can do what you like with the output of BAS. |
| 42 | |
| 43 | _____________________________________________________________________________ |
| 44 | |
| 45 | AOF CODE GENERATION |
| 46 | |
| 47 | I'll assume you understand how linkable objects work. We'll be |
| 48 | here all day if you don't. |
| 49 | |
| 50 | All code must be in an `area'. You start an area by saying |
| 51 | |
| 52 | FNarea("NAME", "ATTRIBUTES") |
| 53 | |
| 54 | The name can be anything you like; common names are `C$$Code' for |
| 55 | C code, and similar. Dollars are popular for some reason. |
| 56 | |
| 57 | The attributes define properties of areas, which in turn affect |
| 58 | positioning and access permissions. We've tried to use the same |
| 59 | names as Objasm here: |
| 60 | |
| 61 | CODE -- area contains code (implies `READONLY') |
| 62 | COMDEF -- definitons for a common area |
| 63 | COMMON -- overlay areas with this name |
| 64 | (implies `NOINIT') |
| 65 | NOINIT -- initialise this area with zeros |
| 66 | READONLY -- I don't need to write to this area |
| 67 | DEBUG -- contains debugging information |
| 68 | |
| 69 | By default, areas with the same name are concatenated. You get |
| 70 | given symbols `NAME$$Base' and `NAME$$Limit' to tell you the |
| 71 | boundaries of the complete area called `NAME', which is useful for |
| 72 | building tables. |
| 73 | |
| 74 | Attribute names can be separated by anything you like -- they're |
| 75 | parsed by the BASIC library using INSTR. Objasm uses commas, so |
| 76 | I tend to too. |
| 77 | |
| 78 | A program generating AOF code will usually look something like this: |
| 79 | |
| 80 | LIBRARY "libs:BAS" |
| 81 | PROCbas_init |
| 82 | |
| 83 | PROCbas_aofInit(SIZE) |
| 84 | FOR o%=4 TO 6 STEP 2 |
| 85 | [ opt o% |
| 86 | FNpass |
| 87 | |
| 88 | FNarea("Foo$$Code", "CODE, READONLY") |
| 89 | ... |
| 90 | |
| 91 | ] |
| 92 | NEXT |
| 93 | PROCbas_aofSave / PROCbas_aofSaveAs(FILENAME) |
| 94 | |
| 95 | Here, `SIZE' is the amount of space to reserve for the code, and |
| 96 | `FILENAME' is the name to save it as. The PROCbas_aofSave function |
| 97 | uses the value of <BAS$Output> as a default filename: this allows |
| 98 | you to declare an alias |
| 99 | |
| 100 | Set Alias$BasAsm Set BAS$Output %1|mBASIC -quit %0 |
| 101 | |
| 102 | which you can use in Makefiles. |
| 103 | |
| 104 | You're not limited to one assembly per source file -- you can create |
| 105 | any number of object files, although each one must start with a |
| 106 | call to PROCbas_aofInit and end with PROCbas_aofSave[As]. |
| 107 | |
| 108 | Note that you must begin each assembly by calling FNpass -- this |
| 109 | will set the P% and O% variables appropriately for the assembly, and |
| 110 | make sure that the BAS code understands where you are in the |
| 111 | assembly. |
| 112 | |
| 113 | You can export a synbol (so that other object files can see it) by |
| 114 | calling FNexport; for example: |
| 115 | |
| 116 | FNexport("hello") |
| 117 | .hello stmfd r13!,{r0,r14} |
| 118 | ldr r0,FNlitsz("Hello, world") |
| 119 | swi "OS_Write0" |
| 120 | ldmfd r13!,{r0,pc}^ |
| 121 | FNltorg |
| 122 | |
| 123 | Because AOF allows a wider range of identifiers than BASIC, you can |
| 124 | `alias' names as you export them; for example: |
| 125 | |
| 126 | FNexport("foo", "My$$FooishThing") |
| 127 | |
| 128 | takes the value represented in the BASIC variable `foo' and exports |
| 129 | it as `My$$FooishThing' in an object file. |
| 130 | |
| 131 | You import values in the same way: |
| 132 | |
| 133 | FNimport("malloc") |
| 134 | FNexport("xmalloc") |
| 135 | |
| 136 | .xmalloc |
| 137 | stmfd r13!,{r1-r3,r12,r14} |
| 138 | bl malloc |
| 139 | cmp r0,#0 |
| 140 | ldmnefd r13!,{r1-r3,r12,r14} |
| 141 | ldr r0,FNliterr(1,"Not enough memory") |
| 142 | swi "OS_GenerateError" |
| 143 | |
| 144 | FNltorg |
| 145 | |
| 146 | (I'll explain the FNlit... and FNltorg macros later. Bear with me.) |
| 147 | |
| 148 | You can also FNimportAs a symbol with a funny name: |
| 149 | |
| 150 | FNimportAs("Image$$RW$$Limit","program_end") |
| 151 | |
| 152 | fetches the limit of the read-write area of the image, telling you |
| 153 | where the program ends. This is quite handy. |
| 154 | |
| 155 | |
| 156 | How this all works is possibly interesting. It (ab)uses offset |
| 157 | assembly, directing output (O%) to a buffer BAS allocates for you, |
| 158 | and starting P% at &FC000000, which is an illegal instruction, and |
| 159 | hopefully unlikely to appear in `real' code. Once the assembly's |
| 160 | finished, BAS runs through and picks out references to things in |
| 161 | with addresses &FCxxxxxx and creates relocation directives for them |
| 162 | in the AOF file. Imported things get given addresses &FDxxxxxx. |
| 163 | Assuming these values don't appear in genuine code, we're OK. Just |
| 164 | in case you want to make arbitrary data appear in the object, BAS |
| 165 | has directives for disabling and reenabling relocation: |
| 166 | |
| 167 | FNnoreloc |
| 168 | ... |
| 169 | FNreloc |
| 170 | |
| 171 | won't munge anything between them. |
| 172 | |
| 173 | Finally, the call |
| 174 | |
| 175 | FNentry |
| 176 | |
| 177 | marks the entry point in an AIF program -- execution will start |
| 178 | here. That's all there is to it. |
| 179 | |
| 180 | _____________________________________________________________________________ |
| 181 | |
| 182 | LITERALS AND LITERAL POOLS |
| 183 | |
| 184 | Data like strings and absolute addresses are a pain in the BASIC |
| 185 | assembler -- you have to make up labels for them, and then reference |
| 186 | them. BAS tries to handle this sort of thing for you, which is |
| 187 | rather more pleasant of it. |
| 188 | |
| 189 | At any point in an assembly, BAS is building a `literal pool'. You |
| 190 | create a literal using one of the supplied directives, and BAS is |
| 191 | responsible for putting it in a literal pool; it gives you the |
| 192 | address at which the literal will be placed in the finished output. |
| 193 | The current literal pool will be written to the assembly when you |
| 194 | call FNltorg. A literal pool is also written at the very end of |
| 195 | the assembly. |
| 196 | |
| 197 | Essentially, then |
| 198 | |
| 199 | adr r0,FNlitsz("Hello, world") |
| 200 | ... |
| 201 | FNltorg |
| 202 | |
| 203 | is equivalent to |
| 204 | |
| 205 | adr r0,hello_string |
| 206 | ... |
| 207 | .hello_string |
| 208 | equs "Hello, world" + CHR$(0) |
| 209 | |
| 210 | The literal creation macros provided are: |
| 211 | |
| 212 | * FNlitw(WORD) stores a 32-bit value WORD at a word-aligned |
| 213 | address, e.g., |
| 214 | |
| 215 | FNimportAs("Image$$RW$$Limit", "prog_end") |
| 216 | ldr r0,FNlitw(prog_end) |
| 217 | |
| 218 | puts the address of the end of the program in R0. |
| 219 | |
| 220 | * FNlits(STRING) stores a string, unterminated, and non-word- |
| 221 | aligned. This isn't very useful. |
| 222 | |
| 223 | * FNlitmagic(STRING) stores an unterminated string at a word- |
| 224 | aligned address. This is for things like |
| 225 | |
| 226 | ldr r1,FNlitmagic("TASK") |
| 227 | |
| 228 | so you don't have to remember that this is &4B534154. I |
| 229 | remember it anyway. |
| 230 | |
| 231 | * FNlitsz(STRING) stores a null-terminated string at a non-word- |
| 232 | aligned address. This is useful for all kinds of messages. |
| 233 | |
| 234 | * FNliterr(NUM, STRING) stores a RISC OS error block at a word- |
| 235 | aligned address. |
| 236 | |
| 237 | If you need some kind of structure which isn't provided, you can |
| 238 | build it yourself. PROClitStart informs BAS that it's meant to |
| 239 | assemble a literal; FNliteral completes the literal, returning |
| 240 | the address where BAS will eventually put it. FNlitAlign does |
| 241 | the same job, only it word-aligns the literal. |
| 242 | |
| 243 | _____________________________________________________________________________ |
| 244 | |
| 245 | READING OBJASM HEADERS |
| 246 | |
| 247 | FNget(FILENAME) reads an Objasm-format header file. It's not perfect |
| 248 | but it tries hard. |
| 249 | |
| 250 | Objasm directives supported are: `^', `#'. `EQU', and `IMPORT'. |
| 251 | This is hopefully enough for most purposes. The `*' synonym for |
| 252 | `EQU' is also supported. |
| 253 | |
| 254 | BAS can't understand Objasm macros. Instead, it understands `active |
| 255 | comments'. The only one implemented is `LIB': |
| 256 | |
| 257 | ;+ LIB FILENAME |
| 258 | |
| 259 | which loads the BASIC procedure library FILENAME. A BAS macro |
| 260 | library `foo' must contain a function FNfoo_test, which is used by |
| 261 | BAS to see whether the library is loaded. |
| 262 | |
| 263 | _____________________________________________________________________________ |
| 264 | |
| 265 | OTHER USEFUL TOYS |
| 266 | |
| 267 | BAS defines a whole slew of constants: |
| 268 | |
| 269 | * r0-r15, R0-R15, a1-a4, v1-v6, sb, sl, fp, sp, SP, lr, LR, |
| 270 | lk, LK, pc and PC are all set to the appropriate register |
| 271 | numbers. |
| 272 | |
| 273 | * EQ, NE etc. are set to the appropriate condition code values. |
| 274 | |
| 275 | FNalign aligns the output position to a word boundary, padding with |
| 276 | zero bytes. FNreserve(SIZE) writes SIZE zero bytes to the output. |
| 277 | |
| 278 | FNbin(FILENAME) inserts the contents of the file FILENAME into the |
| 279 | output, protecting it from relocation. FNfSize(FILENAME) returns |
| 280 | the size of the file FILENAME. This can be handy for setting up |
| 281 | sprite areas. |
| 282 | |
| 283 | FNws_start clears a storage-area counter to zero. FNws_base(VALUE) |
| 284 | sets the counter to VALUE. FNws_align word-aligns the counter. |
| 285 | FNws(SIZE) returns the counter, and increases it by SIZE. FNws_word, |
| 286 | and FNws_byte are equivalent to FNws(4) and FNws(1) respectively. |
| 287 | These are useful for laying out workspace areas and data structures. |
| 288 | |
| 289 | FNadrl(REG, ADDR) assembles a long ADR; FNadrccl(COND, REG, ADDR) |
| 290 | assembles a conditional long ADR. FNaddl(REG, BASE, OFFSET) |
| 291 | assembles a long ADD; FNaddccl(COND, REG, BASE, OFFSET) does the |
| 292 | same conditionally. |
| 293 | |
| 294 | FNldrl(REG, ADDR) assembles a long LDR; FNldrccl(COND, REG, ADDR) |
| 295 | assembles a conditional long LDR. FNldrrl(REG, BASE, OFFSET) |
| 296 | assembles a long non-PC-relative LDR; |
| 297 | FNldrrccl(COND, REG, BASE, OFFSET) does that conditionally. |
| 298 | |
| 299 | _____________________________________________________________________________ |
| 300 | |
| 301 | BUILDING BAS FROM SOURCES |
| 302 | |
| 303 | This is quite involved. You need to have: |
| 304 | |
| 305 | * Acorn Desktop Assembler or later |
| 306 | |
| 307 | * An implementation of `sed' -- I recommend the port of GNU sed. |
| 308 | |
| 309 | * Straylight's basic library set (`header', `swis' and `stream') |
| 310 | available from the same place you got this from. |
| 311 | |
| 312 | * A copy of Cy Booker's `ccrunch' BASIC compressor. If you don't |
| 313 | have this, edit `remnames' and remove the `ccrunch' line -- the |
| 314 | BAS output file will be a little larger, but that's OK. |
| 315 | |
| 316 | Build the ARM code part by running the Makefile. Now run `Setup' |
| 317 | to mangle the nice BASIC library part, and to tack the code on |
| 318 | the end. That should be it. |
| 319 | |
| 320 | _____________________________________________________________________________ |
| 321 | |
| 322 | WHAT USE IS BAS? |
| 323 | |
| 324 | Dunno. Straylight were hoping to sell it for maybe fifteen quid a |
| 325 | go. We use it in-house for building simple AOF-outputting tools; |
| 326 | our message-file and template-file compilers are BAS-based, for |
| 327 | example. (These are available as part of the SDLS and Sapphire |
| 328 | packages, if they're out yet.) |
| 329 | |
| 330 | _____________________________________________________________________________ |