-
Notifications
You must be signed in to change notification settings - Fork 35
Extension Modules
Author: Boisy Pitre
The technical information in the OS-9 manuals is packed with details that can unlock a much deeper understanding of how OS-9 works. The trouble is that some of those details can be hard to digest without a little background and a guided walk-through.
This article takes a close look at one of the more rarely explored parts of OS-9/6809 internals: system extensions, also known as P2 modules.
If you want to follow along closely, it helps to have your OS-9 technical manuals nearby, plus a printout of the appropriate os9defs file for the system you are studying.
For best results, have these on hand:
- OS-9 Level Two Technical Reference Manual, or
- OS-9 Level One Technical Information Manual together with the OS-9 Addendum Upgrade to Version 02.00.00 Manual
- a printout of the
os9defsfile for the target operating system
Boisy specifically recommends keeping separate OS-9 definition files for Level One and Level Two, such as os9defs.l1 and os9defs.l2, so it is always clear which operating system a given source file targets.
If you run mdir on an OS-9 system, you have probably seen modules such as:
-
OS9andOS9p2on Level One -
OS9p1andOS9p2on Level Two
These modules are effectively the OS-9 operating system itself. They contain the code that implements the system calls documented in the technical reference manuals.
Even IOMan is a form of system extension, since it implements the I/O call layer.
Drivers and file managers are the most common way to extend OS-9, but those are mostly confined to I/O-related behavior. System extensions are more fundamental: they can add new system calls or replace existing ones.
That power comes with risk. Replacing the behavior of an existing system call in a way that breaks compatibility is usually a bad idea. The safer and more useful pattern is to add a new call.
That is the path this article follows.
OS-9 provides operating-system services to applications through system calls.
On the 6809, those calls are built on the SWI2 instruction, which is a software interrupt. When the processor executes SWI2, control passes to the OS-9 kernel, which looks at the byte immediately following the instruction and interprets it as the system call code.
For example, assembly source like this:
lda #1
leax mess,pcr
ldy #5
os9 I$Write
rts
mess fcc "Hello"is assembled into something equivalent to:
lda #1
leax mess,pcr
ldy #5
swi2
fcb $8A
rts
mess fcc "Hello"The $8A is the encoded value for I$Write. The kernel sees that code and dispatches execution to the appropriate system routine.
Since the system call code is only one byte, a total of 256 possible call codes exist in theory. In practice, OS-9 Level One and Level Two divide those ranges up differently.
-
$00-$27— user mode system call codes -
$29-$34— privileged system mode call codes -
$80-$8F— I/O system call codes
-
$00-$7F— user mode system call codes -
$80-$8F— I/O system call codes -
$90-$FF— privileged mode system call codes
The distinction between user mode and system mode matters because OS-9 maintains separate system call tables for each.
- If a call is available to user mode, it is installed in both the user and system tables.
- If a call is privileged only, it is installed only in the system table.
For example:
-
F$Loadis callable from both user and system state, so it appears in both tables. -
F$AProcis privileged, so a user-state caller gets an error.
The range $80-$8F is reserved for I/O calls. Those are handled by IOMan, so they are not available for custom extension work.
Installing a new system call means:
- choosing a free system call code
- deciding whether it will be callable from user state, system state, or both
- building a small assembly table that describes the new service
- passing that table to the
F$SSvcsystem call
So, interestingly enough, installing a system call is itself done by using a system call.
The example discussed in the article is a module named OS9p3. It installs a new system call named F$SAYHI.
F$SAYHI behaves like this:
- if register
Xpoints to a caller-supplied string, that string is printed - if
Xis zero, a default message is printed instead - output goes to the caller’s standard error path
The service table for the module contains three-byte entries:
- one byte for the system call code
- two bytes for the address of the handler routine
The table is terminated by $80.
At startup, the module points Y at that table and calls F$SSvc, which installs the new call into the kernel’s system call tables.
Under Level Two, the system and user processes do not share the same address space in the simple way they do under Level One. That means the default message cannot just be referenced directly from user context.
Instead:
- the handler checks whether
Xpoints to a caller string - if not, it allocates space on the caller’s stack
- it then uses
F$Moveto copy the default message from system space into the caller’s address space - finally it calls
I$WritLnusing the caller’s standard error path
Because this is a true system extension module under Level Two, it must be included in the bootfile. After reboot, OS9p2 finds OS9p3, enters at the module execution offset, and the new system call becomes available.
Level One handles system extensions differently.
Because system and process address spaces are much less isolated, the code can be simpler:
- no
F$Moveis required just to access a default message - the extension does not need to be a
Systmmodule - instead, it is typically built as a
Progmodule that installs the call when executed
That means the extension is installed at runtime by simply running the installer module from the command line.
Boisy points out Printerr as a useful example. It installs a replacement F$PErr system call when you run it.
Level One has one major advantage:
- you can install a system extension without building a new bootfile and rebooting
But it also has one important hazard:
- if the module that contains the installed system call code is unlinked from memory, the call table still points to code that no longer safely exists
At that point, a crash is only a matter of time.
The article also describes a small assembly test program named tsayhi.
It works on both Level One and Level Two:
- if called with no parameters, it sets register
Xto zero and triggers the default greeting - if called with a command-line parameter, it passes the parameter string to
F$SAYHI - up to 40 characters are printed to standard error
This makes it a simple and effective way to verify that the extension was installed correctly.
System extensions are one of the most powerful ways to modify OS-9 because they operate at the system-call level.
They let you:
- add new system calls
- replace or extend existing kernel behavior
- fundamentally change how OS-9 behaves
But they also require deep knowledge of OS-9 internals, especially:
- system call dispatch
- privilege levels
- memory layout
- process versus system address space
- module lifetime and linkage behavior
The exact implementation differs significantly between Level One and Level Two, mainly because Level Two has stricter address-space separation.
If you want to experiment with extension modules:
- start by adding a new call rather than replacing an existing one
- keep separate definition files for Level One and Level Two
- be especially careful on Level One not to unlink a module that contains installed extension code
- remember that Level Two generally requires bootfile integration and rebooting
The original article includes full source listings for:
-
os9p3.afor OS-9 Level Two -
sayhi.afor OS-9 Level One -
tsayhias a test program
Those listings are useful if you want to build the example exactly as described, but the key architectural ideas are the system-call table, F$SSvc, and the different installation models used by Level One and Level Two.
NitrOS-9: Empowering 6809 CPUs with a modern, efficient operating system