| name | using-libasm |
| description | Library of routines for RISC OS C programming. Provides optimized multiplication/division, processor identification, IRQ control, floating-point preservation, and non-C (registrant) function calls. Use when C code needs assembly-level functionality or when porting code that requires these low-level operations. The library is linked by adding C:Asm.o.libAsm to the LIBS line in a makefile and included via headers like |
| metadata | {"author":"gerph@gerph.org"} |
| license | MIT |
Using libasm
Overview
libasm is a C-callable library containing optimized assembly routines that are useful
when writing RISC OS applications and modules in C. The library provides several categories
of functionality:
- Multiplication and division - 64-bit intermediate arithmetic to avoid overflow
- Processor identification - Runtime detection of ARM architecture and processor type
- IRQ control - Safe masking and unmasking of interrupts
- Floating-point preservation - Saving/restoring FPU state when using FP in SVC mode
- Non-C function calls - Calling arbitrary functions with register arguments
- Task management - Safely calling
Wimp_StartTask from SVC mode
The library is distributed as an object library that you link into your project. It should
not be used directly from BASIC - these are C helper routines.
Installation and Usage
Linking in a Makefile
To use libAsm, add it to the LIBS variable in your Makefile,fe1:
LIBS = ${CLIB} C:Asm.o.libAsm
Note: Asm.o.libAsm (32-bit) are available. The 64-bit variant is not available.
Including Headers in C Code
Include the relevant headers in your C source files using the Asm/ prefix:
#include "Asm/muldiv.h"
#include "Asm/processor.h"
#include "Asm/irqs.h"
#include "Asm/fpsvc.h"
#include "Asm/callx.h"
#include "Asm/callbacks.h"
#include "Asm/starttasksvc.h"
Available Routines
Multiplication and Division (Asm/muldiv.h)
The muldiv() function performs (a * b) / c with a 64-bit intermediate product,
avoiding overflow that would occur with standard 32-bit multiplication.
#include "Asm/muldiv.h"
int result = muldiv(100, 200, 50);
Why use muldiv? Standard C multiplication of two 32-bit values can overflow before
division. The muldiv function performs the calculation internally using 64-bit arithmetic
to preserve precision.
Processor Identification (Asm/processor.h)
processor_id() returns the ARM processor ID register value, which encodes the processor
type, architecture, and revision.
#include "Asm/processor.h"
unsigned long id = processor_id();
The header provides macros to interpret the processor ID:
| Macro | Description |
|---|
processor_architecture(id) | Returns an procarch_t enum value for the architecture |
processor_is_arm6x0(id) | Non-zero if running on an ARM6x0 series processor |
processor_is_arm7(id) | Non-zero if running on an ARM7 series processor |
Architecture enum values:
| Enum | Architecture |
|---|
arch_2 | ARM2/ARM3 |
arch_3 | ARM6/ARM60 |
arch_4 | ARM7500-style (Architecture 4) |
arch_4T | ARM7TDMI (Architecture 4T) |
arch_5 | StrongARM (Architecture 5) |
arch_5T | Architecture 5T |
arch_5TE | Architecture 5TE |
arch_5TEJ | Architecture 5TEJ |
arch_6 | Architecture 6 |
IRQ Control (Asm/irqs.h)
These functions safely manipulate the interrupt mask bits in the processor mode register.
#include "Asm/irqs.h"
unsigned long flags;
flags = ensure_irqs_off();
restore_irqs(flags);
flags = ensure_irqs_on();
restore_irqs(flags);
Why use these? Direct manipulation of the SPSR or CPSR is error-prone. These functions
provide a structured way to disable/enable interrupts while preserving the previous state,
which is essential for interrupt handlers and critical section code.
Floating-Point Preservation (Asm/fpsvc.h)
When using floating-point operations in SVC mode (e.g., from a Wimp filter), the FPU
registers must be preserved because the APCS declares F0-F3 as corruptible.
#include "Asm/fpsvc.h"
void my_svc_function(void) {
int result;
fpsvc_buf fpbuf;
fpsvc_preserve(&fpbuf);
result = calculate_value(3.14159);
fpsvc_restore(&fpbuf);
}
Why use this? Without preserving FPU state, your code may corrupt the floating-point
context of the task that called your SVC-mode code, leading to incorrect calculations or
crashes in the calling application.
Non-C Function Calls (Asm/callx.h)
_callx() allows you to call an arbitrary function with register arguments, similar to
_swix() but for function calls rather than SWIs.
#include "Asm/callx.h"
#include "swis.h"
void *func_ptr = some_function;
void *func_pw = module_pw;
int result;
int result;
_callx(func_ptr, func_pw, _INR(0,4)|_OUT(0), arg0, arg1, arg2, arg3, arg4,
&result);
Why use _callx? When you need to call a function (e.g., a registered handler
), _callx provides a clean interface for passing arguments and receiving
results, similar to how _swix works for SWIs.
Callback Triggering (Asm/callbacks.h)
trigger_callbacks() drops to user mode to allow pending callbacks to be processed.
#include "Asm/callbacks.h"
for (int i = 0; i < 1000; i++) {
do_work_step(i);
if ((i % 100) == 0) {
trigger_callbacks();
}
}
Why use this? Long-running code in SVC mode can prevent the Wimp from processing
callbacks (e.g., TaskWindow events, mouse movements). Dropping to user mode briefly
allows the system to update and respond to user input.
Wimp_StartTask from SVC (Asm/starttasksvc.h)
svc_wimp_start_task() safely calls Wimp_StartTask from SVC mode by dropping to USR
mode first.
#include "Asm/starttasksvc.h"
unsigned long taskhandle = svc_wimp_start_task("SWidget swidget", 0);
if (taskhandle != 0) {
}
Why use this? Wimp_StartTask cannot be called directly from SVC mode because it
triggers a task switch. This function handles the mode switch correctly and returns the
task handle without corrupting the USR mode stack.
Common Patterns
Safe Critical Section with IRQ Control
#include "Asm/irqs.h"
void update_shared_data(void) {
unsigned long flags = ensure_irqs_off();
shared_counter++;
shared_flag = TRUE;
restore_irqs(flags);
}
Architecture-Specific Code
#include "Asm/processor.h"
void init_hardware(void) {
unsigned long id = processor_id();
switch (processor_architecture(id)) {
case arch_4:
case arch_4T:
init_arm7_hardware();
break;
case arch_5:
case arch_5T:
case arch_5TE:
init_strongarm_hardware();
break;
default:
init_generic_hardware();
break;
}
}
Floating-Point in a SWI call
#include "kernel.h"
#include "Asm/fpsvc.h"
_kernel_oserror *my_filter(_kernel_swi_regs *r, void *wb) {
fpsvc_buf fpbuf;
fpsvc_preserve(&fpbuf);
float scale = 1.5f;
float value = compute_value() * scale;
fpsvc_restore(&fpbuf);
return NULL;
}
Long-Running Operation with Callbacks
#include "Asm/callbacks.h"
void process_large_file(void) {
for (int block = 0; block < total_blocks; block++) {
process_block(block);
if ((block % 100) == 0) {
trigger_callbacks();
}
}
}
Build Notes
- The library is only available for 32-bit builds. There is no 64-bit variant.
- The
_callx implementation is already present in the 64-bit C library.
- The standard variant (
libAsm) is for application use.
- The module variant (
libAsmzm) is position-independent and safe for ROM inclusion.
See Also
writing-cmodules - For integrating this library into RISC OS modules.
using-makefiles - For linking libraries in RISC OS makefiles.