|
APCSOpinions on various assemblers |
|
These are my personal opinions of various RISC OS assemblers that are available. I have noted the versions tested, if a comment has been addressed in a later version, do let me know!
This is the test program:
r0 RN 0
AREA |main|, CODE, READONLY
ENTRY
ADR r0, title
SWI &02 ; OS_Write0
SWI &10 ; OS_GetEnv
SWI &02 ; OS_Write0
SWI &03 ; OS_NewLine
SWI &11 ; OS_Exit
title
= "This program was called with:", 10, 13, " ", 0
ALIGN
END
When run, the program outputs a short title, followed by the command line that started the
program. For example:
TaskWindow Server v0.01 *cat Dir. IDEFS::Willow.$.Coding.Projects.Assembler.apcstest Option 00 (Off) CSD IDEFS::Willow.$.Coding.Projects.Assembler.apcstest Lib. IDEFS::Buffy.$.library URD Unset o D/ s D/ test WR/ *test -this -is a -test This program was called with: test -this -is a -test * |
This is a simple assembler that doesn't support areas, and it outputs an Absolute file (there is
no linker stage). It is sorta a stripped-down objasm with built-in linker, a throw-back to the
days of old prior to the DDE and C code (once upon a time, Acorn suggested that major
applications would be written in pure assembler; then, they also once suggested that major
applications would be implemented as relocatable modules - duh!).
Removing the AREA and ENTRY lines will allow the test file to compile correctly.
The output is 60 bytes long, as there is no AOF header.
I cannot get this to output AOF files. I define ENTRY start and
AREA main, CODE, READONLY at the start of the program, and
ARMmaker replies "Entry point "start" not defined".
I put this down to my unfamiliarity with the program. Asking ARMmaker to output an absolute file
worked, creating a 60 byte program (identical to AAsm's output).
The code used for ARMmaker was:
r0 BINDREG R0
OUTPUT ABSOLUTE
start
ADR r0, title
SWI "OS_Write0"
SWI "OS_GetEnv"
SWI "OS_Write0"
SWI "OS_NewLine"
SWI "OS_Exit"
title
DCS "This program was called with:\n\r "
DCB 0
ALIGN
Like Nick Roberts' ASM, this assembler can convert SWI names for you. Sadly, it does not use the
'RN' register assignment convention, but it's a minor point. In it's favour, it does understand
C-style characters ("\n\r").
AS was quite happy with the original objasm input.
As a bonus, AS can also cope with SWI name conversions.
AS is not a 'better' version of objasm as it lacks the macro facilities and the more exotic
features of objasm. However, on the plus side it is supplied with source so you can add your own
custom bits, or look to see how an assembler works.
I use objasm because I have used it in the past, and because I have a basic knowledge of it. However, objasm has a main competitor... Actually, that's not really true. objasm kicks ass, but this, ASM, whips out a General Electrics Minigun and lays waste to swathes of ass. Well, it's an analogy I figured Glenn would like, so sue me. :-)
One of the more complex (and powerful) of the free assemblers, ASM supports IF...THEN, macros,
different processors, 32 bit code, includes, labels, maths in instructions (such as DCFE
12345.6 + (1 << 12)), built-in assembly-time mathematical functions (sin, cos, tan, acs,
etc etc), floating point code, NOPs, macros, full conditional assembly, structures (!!!),
limited-scope labels, pragmas, and redefinable constants.
All in all, this may rate as the best assembler. ASM leaves objasm standing... Then again, it leaves the others standing too!
ASM failed to recognise the RN directive. This isn't a big issue, as the standard (r#) and APCS
names are understood by the assembler.
It allows name assignment, and can be used to write quite readable code. The documentation
provides us with:
result = 0
lhs = result + 1
rhs = lhs + 2
EXPORT Long_Add ; Make the function external
Long_Add: STMFD sp!,{lhs ,lhs+1,link}
ADDS lhs,lhs,rhs
ADCS lhs+1,lhs+1,rhs+1
...etc...
The example has been snipped for space reasons. While objasm allows variables to be
named, I am not sure it is that flexible. Note the final ADCS instruction.
ASM requires that labels are suffixed with a colon and, like ARMmaker, it wasn't happy with the
string syntax so the more traditional EQUS/EQUB has been used.
END was not required, and as you can see ASM is capable of converting SWI names on the fly.
I am describing the differences in ASM so verbosely not because I want to pick on ASM, but
because I feel that Nick's minor changes (such as the colon) and the SWI conversion all make for
much tidier looking code. I also like the EQUx directives, I use them in BASIC and feel happier
with them than with DCx or stuff like '='.
The code used for ASM was:
AREA main, CODE, READONLY
ENTRY start
start:
ADR r0, title
SWI "OS_Write0"
SWI "OS_GetEnv"
SWI "OS_Write0"
SWI "OS_NewLine"
SWI "OS_Exit"
title:
EQUS "This program was called with:\n\r "
EQUB 0
ALIGN
As always, a descriptive manual is supplied in Impression or text format.
Download ASM ![]()
This was used as the benchmark (being Acorn's own), so was obviously happy with the input!
A capable assembler, this was supplied with the Acorn Desktop assembler package. A later and more
capable (ie, later processors/facilities) version is now provided with Acorn's C++ development
suite.
Being a macro assembler, it has very powerful options for devising macros, although the
syntax is a bit difficult for a newbie.
Here's an example from the 32bit development release:
; ****************************************************
; *** SCPSR - Set and clear bits in PSR from the ***
; *** masks $set, $clr, using register $regtmp ***
; ****************************************************
MACRO
$label SCPSR $set, $clr, $regtmp, $cond, $oldpsr
LCLS srcreg
[ "$oldpsr"=""
srcreg SETS "$regtmp"
|
srcreg SETS "$oldpsr"
]
$label mymrs $cond, $srcreg, CPSR
[ (($set) :AND: ($clr)) <> 0
! 1, "Attempt to simultaneously set and clear a bit in SCPSR"
]
CPU32_set PSRto32 $set
CPU32_clr PSRto32 $clr
[ (CPU32_set :AND: &F0000000) <> 0 :LAND: (CPU32_set :AND: &F0) <> 0
ORR$cond $regtmp, $srcreg, #CPU32_set :AND: &F0000000
ORR$cond $regtmp, $regtmp, #CPU32_set :AND: &0FFFFFFF
srcreg SETS "$regtmp"
|
[ CPU32_set <> 0
ORR$cond $regtmp, $srcreg, #CPU32_set
srcreg SETS "$regtmp"
]
]
[ (CPU32_clr :AND: &F0000000) <> 0 :LAND: (CPU32_clr :AND: &F0) <> 0
BIC$cond $regtmp, $srcreg, #CPU32_clr :AND: &F0000000
BIC$cond $regtmp, $regtmp, #CPU32_clr :AND: &0FFFFFFF
srcreg SETS "$regtmp"
|
[ CPU32_clr <> 0
BIC$cond $regtmp, $srcreg, #CPU32_clr
srcreg SETS "$regtmp"
]
]
somemsr $cond, CPSR,$srcreg, CPU32_set:OR:CPU32_clr
MEND
TLA is a very peculiar assembler. It appears to have a central core which is machine independent
along with various opcodes for the ARM processor.
It is obscure (in my opinion), and I wasn't able to get an ADR style instruction assembled. To
be precise, the OS_Write0 example (in the documentation) crashed with the same error (branch
through zero at <some address in ROM>).
But, for interests sake, I have recoded the program to work with TLA, though I would recommend
a different assembler - something that is closer to the DDE style.
The code used for TLA was:
.ENTRY start
.CODEAREA
.PROC main
start
SWI OS_WriteS
.ASCII "This program was called with:"
SWI OS_NewLine
SWI OS_WriteS
.ASCII " "
SWI OS_GetEnv
SWI OS_Write0
SWI OS_NewLine
SWI OS_Exit
.END