agentleFS
gerphgerph/riscos-agent-skills/skills/using-libasm/SKILL.md

using-libasm

Skill4 starsChanged 7 months agoMITView on GitHub

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 #include "Asm/muldiv.h".

We split this skill into the rules it gives an agent

2 rules. About 0 are common, 1 is distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    When using floating-point operations in SVC mode (e.g., from a Wimp filter), the FPU registers must be preserved because the APCS declares …

    line 127
  2. Very specific:

    Why use _callx?: When you need to call a function (e.g., a registered handler ), _callx provides a clean interface for passing arguments …

    line 170

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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"      /* Multiplication and division */
#include "Asm/processor.h"   /* Processor identification */
#include "Asm/irqs.h"        /* IRQ control */
#include "Asm/fpsvc.h"       /* Floating-point preservation */
#include "Asm/callx.h"       /* Cross-mode function calls */
#include "Asm/callbacks.h"   /* Triggering callbacks */
#include "Asm/starttasksvc.h"/* Wimp_StartTask from SVC mode */

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);  /* Returns (100 * 200) / 50 = 400 */

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:

MacroDescription
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:

EnumArchitecture
arch_2ARM2/ARM3
arch_3ARM6/ARM60
arch_4ARM7500-style (Architecture 4)
arch_4TARM7TDMI (Architecture 4T)
arch_5StrongARM (Architecture 5)
arch_5TArchitecture 5T
arch_5TEArchitecture 5TE
arch_5TEJArchitecture 5TEJ
arch_6Architecture 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();   /* Disable IRQs, save previous state */
/* Critical section */
restore_irqs(flags);         /* Restore previous state */

flags = ensure_irqs_on();    /* Enable IRQs, save previous state */
/* Section where IRQs must be on */
restore_irqs(flags);         /* Restore previous state */

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);

    /* Do floating-point operations here */
    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"

/* Call a function with R0-R4 as arguments, R0 as output */
void *func_ptr = some_function;
void *func_pw = module_pw;
int result;

/* Mask format: bits 0-11 = input registers: `_IN(reg)` and `_INR(loreg,hireg)`
                bits 22-31 = output registers: `_OUT(reg)` and `_OUTR(loreg,hireg)` */
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"

/* During a long-running operation */
for (int i = 0; i < 1000; i++) {
    do_work_step(i);
    if ((i % 100) == 0) {
        trigger_callbacks();  /* Allow Wimp tasks and 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) {
    /* Task started successfully */
}

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();

    /* Atomic update of shared data structure */
    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);

    /* Use floating-point calculations */
    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);

        /* Allow callbacks every 100 blocks */
        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.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.