Skip to content
Simon Willcocks edited this page Nov 22, 2022 · 19 revisions

Welcome to the RISC-OS-Kernel-in-C wiki!

The desktop, with fonts working. There are still problems with interrupts.

Weeks to find, minutes to fix.

I'm just going to put incidental thoughts in here, and tidy up later!

Maybe.

What do I like most about RISC OS?

  • The Wimp.
  • Application directories.
  • Treats the user as the most important thing.
  • Allows programmers to work near the bare metal.
  • Keeps programs in RAM, so there's no delay when using a program that hasn't been used for a while.
  • Fast boot up/shutdown.

Writing a GCC function that provides a SWI service.

Generally written as static inline... If there are multiple return values, I think you can safely and efficiently use pointers passed into the routine.

Don't worry about the apparent inefficiency, the compiler will optimise out unused values.

static inline void usr_OS_ConvertCardinal4( uint32_t number, char *buffer, uint32_t buffer_size, char *old_buffer, char **terminator, uint32_t *remaining_size )
{
  // Do any calculations or variable initialisations here, before
  // declaring any register variables.
  // This is important because the compiler may insert function calls
  // like memcpy or memset, which will corrupt already declared registers.

  // The inputs to the SWI
  register uint32_t n asm( "r0" ) = number;
  register char *buf asm( "r1" ) = buffer;
  register uint32_t s asm( "r2" ) = buffer_size;

  // The outputs from the SWI
  register uint32_t oldbuf asm( "r0" );
  register char *term asm( "r1" );
  register uint32_t rem asm( "r2" );

  // Call the SWI
  asm volatile ( "svc %[swi]"
    : "=r" (oldbuf) // List all the output variables
    , "=r" (term)   // or they will be optimised away.
    , "=r" (rem)
    : [swi] "i" (OS_ConvertCardinal4) // Can use an enum for the SWI number
    , "r" (n)       // List all the input register variables,
    , "r" (buf)     // or they will be optimised away.
    , "r" (s)
    : // If the SWI corrupts any registers, list them here
      // If the function is to be called in a priviledged mode, include "lr"
      "memory"
  );

  // Store the output values
  // Again, don't do anything other than simple storage or assignments
  // to non-register variables.
  if (old_buffer != 0) *old_buffer = oldbuf;
  if (terminator != 0) *terminator = term;
  if (remaining_size != 0) *remaining_size = rem;
}

For example

    {
      char buffer[20];
      usr_OS_ConvertCardinal4( 666, buffer, sizeof( buffer ), 0, 0, 0 );
      // do something with buffer, which now starts "666\0"
    }

Generates the following code at -O4:

fc0043fc:       e300029a        movw    r0, #666        ; 0x29a
fc004400:       e28d1014        add     r1, sp, #20
fc004404:       e3a02014        mov     r2, #20
fc004408:       ef0000d8        svc     0x000000d8

This is the first time I've noticed a difference whether the volatile keyword is used or not. If not, the whole function is left out, and the svc instruction not inserted into the output file. At least in this specific case.

Thoughts:

  • Could have a single include file that works for privileged and non-privileged modes (decided at compile time).
    #ifdef PRIVILEGED_RO_CODE
        , "lr"
    #endif
  • Could include exception/error handling
    #ifdef EXCEPTION_HANDLER
        "svc %[swi]+0x20000"
        "bvs " EXCEPTION_HANDLER
    else
        "svc %[swi]"
    #endif

(EXCEPTION_HANDLER is defined as a string containing the name of a non-returning function that takes an error block pointer as its only argument. It should probably longjmp or throw its way out of trouble.)

How does GCC inline assembler handle parameters?

There are four groups of settings after your inline assembler string.

    asm [volatile] ( "your inline assembler" : <outputs> : <inputs> : <clobbers> );
    asm goto ( "your inline assembler" : <outputs> : <inputs> : <clobbers> : <labels> );

Inputs and output can be associated with variables (the former with the results of expressions), clobbers is a list of registers that are not used for inputs or outputs but will be modifed by your code.

TaskSlots and Tasks

TaskSlots are user-accessible memory areas from 0x8000 up that can be switched into and out of virtual memory. Each core can have one TaskSlot mapped in at any time. I think it will be possible to have the same slot mapped into memory in multiple cores at a time, the kernel will simply have to mark them as shared or not (can default to shared to begin with).

Tasks are associated with a TaskSlot, many-to-one (or many-to-none, if the task is created for a relocatable module).

Each task has a context:

  • Integer registers (including PC and PSR)
  • Optional floating point registers
  • Optional graphics context

If there is no graphics context, the task will be running as a console program, with textual input and output.

If there is a graphics context, it will remember what sprite output has been redirected to, or if it is the screen.

Since the screen is a shared resource, only one task per core (in the whole system?) will be allowed to access the screen at a time (regulated by the Wimp?)