diff --git a/.gitattributes b/.gitattributes
index 5f4f21206543abb51052959d389f5ae2ba055822..1f1c38906f45bb91c386016e0deff32c3d821a1a 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,4 @@
 hdr/** gitlab-language=armasm linguist-language=armasm linguist-detectable=true
+s/** gitlab-language=armasm linguist-language=armasm linguist-detectable=true
+**/s/** gitlab-language=armasm linguist-language=armasm linguist-detectable=true
+*,ffb gitlab-language=bbcbasic linguist-language=bbcbasic linguist-detectable=true
diff --git a/Changes b/Changes
new file mode 100644
index 0000000000000000000000000000000000000000..ce02a7a31786628b5344fd9dc8b3462d2bd39cbb
--- /dev/null
+++ b/Changes
@@ -0,0 +1,1471 @@
+> Text.Changes List for Proto-Arfur.
+
+
+Versions up to .078 are ancient history, man.
+
+Version .078
+============
+
+Bugfix of environment string setting
+
+Version .079
+============
+
+Converted to new error header
+Top bit R0 error indicator support removed
+Flags added to ReadLine : bit 31 R0 set => don't reflect chars not going
+                                           into the buffer
+                          bit 30    set => any reflection done with char in R4
+
+Heap robustification : checks that hpds, etc are in existing LogRam
+
+New heap call : ExtendHeap, takes hpd in R1, size to change by in R3. Gives
+two errors : BadExtend for wally changes (i.e. end moves outside existing
+RAM), and ExcessiveShrink for end moved below area in use. The heap end has
+been moved as far as possible in the second case. R3 returned as amount
+actually changed by.
+
+Default Sys$DateFormat set up to give string identical to Master TIME$.
+
+Version .08
+===========
+
+Heap changes bugfixed
+*con. monitortype added
+Default screen on 512K Weed is 80K
+If no keyboard (on ANY reset), it autoboots irrespective of other state.
+
+Version .081
+============
+
+Overlong environment strings / errors don't overwrite your breaklist.
+Service_Memory issued when AplWork size being changed : error given if
+service is claimed. Service therefore means "are you using the AplWork?"
+R2 contains the address of the currently active object, so winge if this
+address is within your code.
+
+Sources changed : conditional flags for native VDU, tube connected removed ;
+it didn't stand a chance of working anyway if you changed either flag.
+
+New Tim stuff : key up/down events, Con. monitortype does things.
+SWI to set handlers with workspace added : handlers yet to change.
+New fileswitch : buffering buggette fixed
+
+Version .082
+============
+
+Configured sizes are now "PageSize" (8 or 32K) units.
+Vset returns from module Init bugfixed.
+VSets in RMTidy better
+Bug in WriteI fixed (if an error occurred, it poked the stacked R14 with the
+error pointer, rather than the stacked R0!)
+Utility open file with winge has had open bits added (nodir,mustopen)
+
+Major bash of handlers, also 3 escapes aborting removed.
+SWI OS_ChangeEnvironment added : calls the envchange vector, default owner of
+which sets the handlers. Give handler number in R0 (see hdr.envnumbers),
+address in R1, workspace pointer in R2 (if used), buffer pointer in R3 (if
+used). All old handler setting SWIs call this internally.
+
+
+Version .083
+============
+
+Rationalised version number (cor).
+Escape handler calling bugfix
+
+
+Version .084
+============
+
+ValidateAddress acts sensibly for zero-length ranges.
+New TimStuff
+Kludge for now : workspace for error handler passed in in R0.
+Help Commands lists *Modules and *Configure.
+Assembly flag ; can assemble versions that convert exceptions into errors
+with negative numbers
+ValidateAddress allows the part of PhysRam that's in the screen.
+Bugfixes of callback, breakpoint handler setting
+*GO doesn't screw with your error handler, and supervisor doesn't reset the
+world every time it panics.
+New FileSwitch
+Default PrinterType$4 variable
+*Error and *Eval
+Aliasing bugfix : *fred. doesn't match Alias$Fred
+*Create bug fixed : *create jim now works
+All files closed on all resets
+Changes to CMOS reset : station ID unmolested (use magic Set transient!), and
+ Sync state is TOGGLED rather than set. Do R-power on twice if necessary!
+
+Version .085
+============
+
+INKEY -256 bugfix
+
+
+Version .09
+===========
+
+Version number rationalised!
+Configure language added.
+Configured language used, which meant changes to module entering :
+this sets up the envstring from anything after the filename on RMRun,
+or from the string pointed at by R2 on Enter.
+New Timstuff, new fileswitch
+Attempts to restore registers 10-12, lr on exceptions.
+
+.09/a
+=====
+
+Bugfix of cmd tail on *Go.
+Monotonically increasing time added, + SWI to read it.
+*GOS starts up the Supervisor application, which lives in the utility module
+
+.09/b
+=====
+
+Oscli Bugfix : *Volume127 not OK.
+SWI Module doesn't enable interrupts for claim, release and describe ops
+New Timstuff : should beep!
+
+.09/c
+=====
+
+Bugfix of errors from exceptions
+Ballsup in ReadLine removed!
+Sam hack of Beep code to make it beep at least a bit.
+*Unplug etc. added.
+
+.09/d
+=====
+
+Beep on hard reset moved.
+Sound CMOS set on CMOS reset : speaker 1, volume 7, channel 1.
+Reducing RMA / sysheap updates hpd proply.
+RMTidy tries to shrink RMA if tidy successful
+
+.09/e : Hard reset beep moved a bit more.
+.09/f : Silly screensizes converted to as close to 20K as poss
+        Optional 325 speed for a1 roms.
+.09/g : Reset initialises timer0 so can use CMOS RAM read write.
+.09/h : IOC Control set to FF on reset, new Tim code to do spastic things
+        with Break key.
+.09/i : Nibble mode ROM speed removed from A1 version!
+        Better Tim code, new fileswitch
+.09/j : RMReInit reports errors when inserting unplugged modules.
+.09/k : Allowed to con. screensize 0-127, max screen actually set up 480K
+        Bug in Create fixed. Dump now allows tbs -> printable masking
+        Entry Envstring for RMRun fixed.
+        Entry to Supervisor always by RMEntering utilitymod.
+        ValidateAddress allow super stack
+        Net EX/CAT CMOS reset better
+        Status/configure when listing all goes into paged mode
+.09/l : Soft break possible : FileSwitch tried and found Guilty of corrupting
+        system heap.
+        New Tim stuff, + weird country stuff in init.
+.09/m : Basic SWI code doesn't change FIQ bit.
+        Can set dump area for default exception handlers.
+        *Help says "Help on keyword flurgle" instead of just flurgle.
+        Oscli terminators table adjusted.
+.09/n : Minor help text changes
+        Fixed module command tails
+        New Tim doobries : no chars from Rodent buttons!
+        *Dump given offset=filesize winges "Outside File"
+.09/o : New fileswitch ; *Copy.
+        *Shadow implemented
+        " :" changed to ":"
+        Paged mode in *Show
+        New TimStuff
+.09/x : Timstuff
+.09/y : Show pagemode bugfix
+        Country CMOS reset to 1
+        New fileswitch
+.1    : Too much optimism and new Tim stuff
+.11   : Keyboard code in reset fixed.
+        <65junk> will expand variable 65junk
+        Syntax/help messages tidied up
+        *Time, *Ignore added
+        New Tim stuff
+        Conversion SWIs added
+        Vectored SWIs (notably osbyte) can only affect C and V
+.12   : Exception errors put PC on error msg.
+        *Shadow (no param) works, *ignore 3please faulted
+.13   : Conversions point R1 at 0
+.20   : New fileswitch
+        Language 3 set on CMOS wotsit
+.21   : PrinterType$4 not set by MOS.
+        ChangeDynamicArea does nothing if attempt to extend a heap too far.
+        Also always leaves one page in AplWork
+        Enters configured doodad if error on autoboot.
+        Redundant bits removed.
+.22   : Wierd terminator chars removed from ReadUnsniged, bit 29 flag added:
+        restrict range to 0..r2 (inclusive). Used in SWI naming stuff (won't
+        say "OK" to "Econet_&FFFFFFFF" etc.
+        Unknown SWI error gives SWI number.
+.23   : CMOS reset TV is 0,1
+        Configured RMASize is amount free in RMA after mos initialisation.
+        Fixed tbs restrict in PrintCharInGSFormat
+        Errors propagated back in utilities from file closure.
+        Exception register dump area only reset on break.
+.30   : V set return from Help_Is_Keyword code not ignored
+        Nude flieswitch
+        R12 to Help_Is_Keyword code correct
+        Bugfix of OS_ConvertNetStation : no explosion for bad net
+        New glue together
+.31   : XOS_Heap(DescribeHeap) subs 4 off right value.
+        Setting Restrict Range to R2 flag in ReadUnsigned meant only base 10 OK
+        Default R12 for event/escape handlers = -1
+        XOS_Heap(ExtendBlock) by 0 works, valid block checking better.
+        First version multiple incarnations, podule support.
+        *Modules gives numbers
+        DFlynn/AThomas memory page/size evaluation used. MOS now pagesize
+        independant.
+        Can assemble a multiple keyboard version
+        *net: bye etc fixed.
+        SWI dispatch immeasurably improved
+        Module LookupName added
+        Bugfix of *Status <module keyword>
+        Module prefixes on oscli added: can specify command only comes from
+        (a particular incarnation of) a particular module
+        Bugfix of 239: wrch errors not being dealt with
+        New fileswitch; oscli also reads any secondary FS command table
+        Interrupts enabled in SWI name <-> number conversions
+        SWI PrettyPrint & SubstituteArgs
+.32     New fileswitch
+.33     New Tim stuff: SWIs Plot & WriteN.
+        Slight changes to allow service calls to be issued before modules are
+        initialised. Note may get modechange service before Service_Reset.
+.34     Configured lang was out by 1.
+        SysHeap is shrunk to minimum, then configured extra size added (after
+        main initialisation). Should give about 16K extra on 305/310
+        Service_UKConfig conforms to (latest PRM) spec.
+        SubstituteArgs terminator corrected.
+.35     Nude TimBits: optimised plots, etc.
+        ValidateAddress looks up screensize in VDU workspace
+        ChangeDynamic funkyfied
+.36     ADev versions hurl abuse at 3 micron ARMs.
+        OS_Claim frees any links with same values first.
+        OS_AddToVector has old Claim semantics
+        INKEY-256 returns &A1
+        RMReInit clears CMOS of unpluggies before initialisation.
+        *ROMModules added as a replacement for *ROMS
+        EnumerateROM_Modules reason code for OS_Module
+        Bugfix of init strings on rmreinit.
+        Order of files changed: in * prompt, utilitymod should always be CAO
+        ChangeDynamic involving screen removes the cursors
+        SpriteSize is a 6-bit field in Status/Configure
+        Write0 shortened!
+.37     VecSwi call shortened.
+        Stu did something to FileSwitch - R6 not wally for some entries.
+.38     Obscuro bug in heap block extension - made RM manager free workspace
+        while checking block validity!
+        If ExtendBlock frees the whole block, it updates the returned block
+        pointer to -1 (i.e. really naff ptr).
+        RMClear doesn't kill podule modules.
+        New TMD stuff (some RS423 bugs fixed, faster this and that).
+.39     New big version (new DeskTop, Wimp,...)
+        New block copy/move code
+        SpriteV added
+        Escape purging of sound now uses duration 1 (at request of DFlynn)
+1.00    New RS423, FileSwitch, ADFS ...
+        CMOS reset sets 1987
+        V not cleared for CallAVector
+        Configure mode allows up to 23
+        PrettyPrint reads window width
+1.01    Status/Configure deals better with Wrch errors.
+        Modules            "  (Tutu number output fixed).
+        Guess what; ReadUnsniged fixed AGAIN !!! ...
+        FileSwitch Opt 1,n bug fixed + {on} in Count
+        SWI WriteEnv invented for JGT use in debungler
+        New TimBits: fix embarrasment with lines, palette
+1.10    Apparently this is a more aesthetically appealing version number.
+        Nuder TimBit: service_prereset lives!
+1.11    OSGBPB 5,6,7 r6 bug fixed
+        MOS 2 micronised. Default callback shrank!
+1.12    Dump code shortened + gives better error response for IsDir etc
+        Another (nastier) FileSwitch Opt 1,n bug fixed
+        Module handler returns proper w'space contents for LookUpName
+        Installing keyboard handler might work now says Tim
+        More FileSwitch changes. And yet more. Dump fixed.
+1.20    another release, another number
+1.21    Oscli whinges about *net#arf:thing etc.
+        *Modules counter doesn't wrap at 256 (and neither does *ROMModules)
+        Module init failure: doesn't free incarnation node twice, and
+        doesn't poo in the system heap.
+        RdArgs SWI added, NoSuchSWI error made non-dynamic
+        timbits/fileswitch
+1.22    Added code to issue service call on RMA claim fail, as requested for
+        ADFS. Not actually assembled in though!
+        Module death can now be recursive
+        Header fields checked for validity: word-aligned where appropriate,
+        and within code length
+        CAO during startup=32M: allow overwriting of MOSROM modules.
+        Module SWI lookup much faster: enter into hash table on init
+        Added SWI OS_ReadRAMFsLimits
+        Looks for keypad * pressed on reset; drops into supervisor.
+        T,Delete and Copy power-on added.
+        List, Type use RdArgs (File/A,TabExpand/S), and expand tabs into
+        8 spaces.
+        Buggette in Module handler: recursive death may lead to operating
+        on a module with 0 incarnations.
+        SWI despatcher goes into caller's IRQ state; individual SWIs yet to be
+        inspected.
+        Supervisor doesn't reference ESCCNT or ESCFLG any more.
+        Vecnum=Swinum SWIs enable IRQs, except for Byte & Word which disable
+        them.
+        Module SWI enables IRQs explicitly except for heap ops.
+1.23    SWI Mouse disables IRQs
+        Bugfix of Oscli: copes with buffer overflow in alias expansion
+        parm substitution.
+1.24    Simon Smith and his Amazing Dancing IRQ Despatcher! New SWIs to claim
+        and release "devices" (i.e. bits in IOC interrupt registers A and B,
+        with the podule device the only one capable of having multiple owners).
+        EnumerateROMModules call (and so Rommodules) fixed.
+        Workspace reordered so can call handlers by LDMIA R12, {R12, PC}
+        *RMFaster added.
+        Link passed to module SWIs has correct IRQ state.
+        Interruptible (and also bug-fixed) heap manager incorporated.
+        New Tim stuff
+1.25    Default IRQ2V clears the appropriate mask bit, returns.
+        SWI ModHand: ExtendBlock will return V set if appropriate.
+        Register dumping changed; may save right registers for FIQ/IRQ.
+        ValidateAddress fixed for screen memory.
+        New Timstuff; ECF origin settable.
+1.26    MOS help messages tidied up.
+        Redirection errors better.
+        Alias terminators same as module command terminators.
+        Scratchspace sorting out: "bad number of parameters" error
+        constructed in an Oscli buffer.
+        Stack full error in Oscli has a sensible number; HelpBuffer
+        allocated on the stack.
+        Redirection handles made byte-sized.
+        MOS buffers rationalised; there's now an enormous amount of sharing.
+        SWI not known error contains SWI number if it wasnt done under
+        interrupt.
+        Backspace in readline = delete
+1.27    Modules can refuse to initialise again.
+        Background FIQ handling done; Service_ClaimFIQ and Service_ReleaseFIQ
+        now come round immediately after Service_Reset, to set up the
+        default handler.
+        Stuart's HeapSort SWI added
+        SWI ExitAndDie added
+        SWIs to delink and relink application vectors added
+        Configure keyword MouseStep added; can also do nK on size fields.
+        ChangeDyn file improved; if cam map corrupted, necessary registers
+        pulled.
+        SWIs to allow user CAM-bashing; soft reset makes the application
+        space contiguous again.
+        ChangeEnvironment has a new reason code to allow setting of the
+        application space size.
+        Reset code masks CMOS; if top bit of CMOS gets set, used to be in
+        a state where lots of memory was allocated, but *status said little
+        was being requested.
+        SWI number->name conversion uses hashtab.
+        FE0-FFF initialisation removed.
+1.28    TV configure syntax nitpicked.
+        New FileSwitch
+1.29    Monotonically increasing time move to &10C; now a public location.
+        *key fixed; (re)uses EnvString
+        as does *show
+1.30    *show prettified
+        FIQ downgrade highest priority interrupt.
+        MOS FIQ claim/release handling altered; now should cope with
+        background claims and/or releases occuring during a foreground
+        claim.
+        New extensions to callback; can now ask to be put on a vector.
+        Hacked source.pmf.key so it calls the callback vector (but NOT
+        the callback handler) in readc/inkey, if its bored.
+        ChangeDynamicArea falls at the first fence if an IRQ is threaded.
+        SWI despatch in RAM; 10% faster StoreHD.
+        Bugfix of SWI node release on module death; bugfix of error percolation
+        in new incarnations.
+1.31    Supervisor printing of time taken ditched; bottom 32K, sysheap, cursor
+        user-write-protected.
+        Processor-speed dependant waits in NewReset now use Tim's magic
+        delay routine.
+        New TimStuff.
+1.32    Bugfix of heap block extension.
+        Dynamic SWI not known built in better buffer.
+        Vector stasher always terminates the buffer (unless totally spastic
+        buffer size).
+        Improvement of heap block extension; will extend into preceding block
+        if poss. Also fix of code spotting interrupted heap ops (from 1.30).
+        PrettyPrint bugfix: hard space after tab.
+        Writeability of CAO (for WIMP) via changeenv.
+        Checks for keypad 0,1,2 on "power-on" resets, and sets monitor type
+        accordingly.
+        ReadBlockSize reason code added to heapman.
+        New fileswitch.
+        ROM speed slugged.
+        Bugfix of SetMemMapEntries, soft reset copes with many pages mapped
+        to the same address.
+        Defined R0-R3 corruptible for IRQ processes.
+1.33    Cam bashing revisited; PPL kept in soft copy too.
+        ChangeDynamicArea also understands that the sysheap should be kept
+        protected too.
+        Dynamic "SWI not known" error fixed.
+        Doesn't go address exception if no kbd.
+1.34    Another instruction removed from SWI despatch.
+        Times CPU and susses in spec ROM speed at reset
+        Old style handler setters don't poo on r12 value.
+        New fileswitch & timstuff: VDU output to sprites.
+        RAMfs space protected.
+        ReadDefaultHandler SWI added; pass number from hdr.env*, returns
+        R1=address, R2=workspace, R3=buffer. 0 means not relevant.
+        CAM soft copy size doubled; 8Mbyte may work.
+1.35    New fileswitch.
+1.36    MOS Default FIQ ownership fixed.
+        New Timstuff: window width obscurities, border stuff.
+1.37    CAM bashing revisited for 8M; next bit up now included. Ta Jamie!
+        Bugfix of SubstituteArgs.
+        New Timstuff.
+        Service offered after ChangeDynamic has moved stuff.
+        When copying (if screen is being extended), ChangeDynamic copies
+        from a PhysRam address. The last page of application workspace is
+        copied into, so this MUST NOT be doubly mapped in MEMC.
+        Bugfix of RMtidy: checks CAO in RMA again.
+1.38    SWI despatch understands that modules may have zero incarnations.
+        Ballsup of ReadDefault corrected.
+1.39    ChangeDynamic overhauled: changing screensize cooperates better with
+        VDU drivers.
+        Setting of variables uses a stack buffer; scratchspace use across
+        sysheap extension a nono.
+        R0 in Service_Memory corrected. Always issues post-service, even if
+        error/nothing to move.
+        Monitortype bits expanded in status/configure.
+        r1 uncorrupted in OS_Exit for HMeekings.
+1.40    Help matches against module titles as a postscan, giving keywords
+        recognised in the module.
+        Bottom 32K protection removed.
+1.41    Soft break forces noprot on application space.
+        ChangeDynamicArea rejects out of range area numbers (instead of
+        crashing).
+        SWI Module does better when block extension has to try and extend the
+        RMA.
+        Exported HLINE takes colour information.
+        ROM unplug bits extended by using original Master frugal bits.
+1.42    Setting of numeric variables fixed.
+1.43    Default CMOS: NewADFSCMOS+2=1, DumpFormat=4.
+        Much molested prettyprint.
+1.44    DrawV added.
+1.45    New fileswitch.
+        PrettyPrint allows you to specify the MOS dictionary by setting r1=0.
+        Saved a word in CallVector (2 micron needed); stopped error gen if
+        vector number > NVectors
+ (defined r0-r3,r12 trashable on IRQ vectors)
+1.46    Made IRQ vector claim force release of any previous entries.
+        SubstituteArgs: tbs r0 on entry => don't tack any unused part of the
+        line on the end.
+        New HeapSort which can shuffle data/build array pointers for you
+        Much munged help text.
+        BGet cache put into kernel so we save about 4us per call (saves
+        branches, temp register pushes). New FileSwitch
+1.47    ROMModules fixed.
+        Added default handler for EconetV for Bruce
+        Minor speedups/space savers from Stu.
+        Fixed module name match: adfs. no longer matches adfs
+1.48    Removed fast entry for Args/GBPB/File/FSControl as it was dangerous
+        new fileswitch that matches the above, so default vechandlers changed
+        *Quit migrated.
+        BPut cache migrated into kernel; removed VecFSSwi code
+1.49    Fixed syntax message expansion.
+1.50    ChangeDynamic has 3 = Sprite area
+1.60    Richard III is dead
+        New serial stuff, apart from XON/XOFF
+        InstallKeyHandler with R0=1 => read keyboard id
+1.61    Default osbyte vec has r12=OsbyteVars
+        Error returns for bad RMA heap claim/extend improved.
+        RMA block extension needing RMA extension fixed.
+        Doesn't corrupt end of physram on reset.
+        System heap not protected just because there are idiots instead of
+        programmers out there.
+        RMEnsure. SystemSpeed CMOS removed.
+        Noo really dangerous FoolSwatch
+1.70    New title for release
+1.71    Heap Extend wallyism removed.
+        System heap protection really removed.
+        Changed help for load/save/create etc. to keep DBell happy
+        New FileSwitch
+        CRC stored in pair of bytes at end of each ROM image
+        HeapSort now does B SLVK rather than LDR pc, =BranchToSwiExit
+        Exception handlers disable FIQ asap, shorter
+        Interrupt handling fix: DiscChanged on Archie became winnie IRQ!
+        Memory speed/MEMC1a flag (tbs) saved in RAM at &110.
+        CMOS resetting modo-fied a la Tim Caspell - R/T-power-on don't reset
+        harddiscs or country, or time; DEL/COPY zap harddiscs/country, but also
+        leave the time alone.
+  ****** N. B. ********* harddiscs not being reset may lead to long waits the
+        first time a named disc is used: on current ADFSs, it will be an
+        infinite wait.
+        Added help on Break, Reset and PowerOn.
+        Module handler fudged a bit for modules with 0 incarnations; tidier
+        after somebody explodes in Initialisation/Finalisation.
+        "Module postfix given" now reads "'%' in module title".
+        Even newer FileSwitch
+        SYS OS_ReadSysInfo: give it 0 in R0, it returns what screensize is
+        set by CTRL-BREAK.
+        *type/list with only one param assume it's a filename.
+        Made memory speed a halfword; MEMC1A flag now bit 16.
+        Byte at &107 is an inconsistency semaphore; if non-zero at reset,
+        hard reset is forced. ALWAYS lower and raise with IRQ disabled.
+        New monitor type 2, compatible with UNIX monitor (mode 23 only).
+        Minisculely faster floodfill.
+        SWI OS_Confirm.
+        New multi-threadable FileSwitch (actually saved code and store !)
+        SKS fixed SWI OS_Confirm - it never cleared Z or C ! It also used wrong
+        exit criteria; &1B does not necessarily mean an escape condition exists
+        it also corrupted r3.
+        SKS fixed SWI OS_ReadSysInfo; it actually preserved r0!
+1.72    New FileSwitch + loads of modules for yet another release
+        SWI ClipBox
+        Fixed FileSwitch howler
+1.73    Help terminates on escape.
+        Autoboots if unrecognised keyboard too.
+        New FileSwitch
+        CAM remapping don't fiddle with the interrupt state; caller must think
+        this through themselves.
+        Default CMOS sez ArthurLib
+        Added periods to end of configuration help where they were missing
+        Moved more supervisor stuff into Super1
+        ReadLineV default owner gets called with suitable r12
+        Attempt to fix *-reset by delay loop
+        Calling of CallEvery events fixed; CallAfter/CallEvery SWIs don't
+        corrupt r1.
+        Made keyboard + printer buffers bigger, cut down Sound0,1,2,3,Speech
+        Reduced *-reset delay loop; moved Inter+IntKeyModules higher in list
+        Added ConvertFileSize+Fixed, CRC swi
+        IRQs off when entering callback handler.
+1.75    Made a500 version for release
+        Fixed Copy use of WimpFreeMemory <-- NB. A500 versions are wrong
+          and set SlotSize 0. Sorry chaps.
+1.76 should have been set AS SOON AS THE CODE WAS CHANGED (Stuart).
+        Extended RESET helptext. MouseStep all one word.
+        Mutter mutter. Explicitly reeenable irq before waiting for metrognome
+        change. Fixed power-on monitor type reconfigures
+1.77    Status doesn't say 0K for screen.
+        Moved OPT code to kernel so *opt1 etc. valid. Shortened PrintR0Decimal
+1.78    Made a500/a1 versions for release
+        Fixed conversion swi number to name to recognises new swis
+        New FileSwitch
+        After looking at the MOS irq device handlers, we'd better define r11
+        to be trashable on IRQ device handlers as well as r0-r3,r12! Also IRQ1
+        Default IRQ shorter - people on IRQ2V must preserve all except r12!
+        Defining r3 -> IOC for all IRQ handlers would be a very good move
+        Compressed SysComms, added MakeErrors in most of MOS + FileSwitch
+        FileSwitch now has path: stuff
+        Fixed default callback handler
+        Make redirection work with INKEY as well as RDCH
+1.79    Bugfix ReadArgs; non-abbreviated keywords with aliases now work.
+        Made RMEnsure scan for digit after getting to 16th col in module
+        help string anyway; means we can rmensure the current clib.
+        *Error GSTranses the error string.
+        Callbacks will be processed before calling the error handler.
+        Put in new SLVK exits to save space
+        Nude fileswitch
+        Fixed SWI OS_GenerateEvent so can call from non-SVC mode.
+        Should we define r9 trashable on module SWI calls ???
+        Move V test into RAM SWI area
+        Default event vector call with appropriate r12
+        Faster BPut; we have deviant wp in top vector node
+        Made error handler shorter
+        New entry to PMFWrch, gets OsbyteVars up its' entry point
+        Removed error on setting callback handler if callback outstanding;
+        came in earlier in 1.79.
+        Improved 'RM not found' and RMEnsure errors.
+        SWI OS_ServiceCall instead of BL Issue_Service in reset.
+1.80    Made image for release
+1.81    Faster SWI OS_CRC by TMD
+        Padded OS_Confirm pointer shape, put the active point on the right.
+        Added an upcall handler.
+        Font cache at 30M; ValidateAddress modified.
+        Reason code 4 in ChangeDynamic = font cache
+        SWI OS_ReadDynamicArea.
+        Regard keyboards with ids 1-31 as the same.
+        Got a dozen spare vectors
+1.82    *Time copes with oswrod errors
+        CallAVector internally preserves caller's interrupt state.
+1.83    Fix to ChangeDynamic of font area.
+        Fixed PIRQ/PFIQ despatch for case when LSB of B CONT ~= 0
+        Also made the above faster/shorter
+1.84    Fixed ChangeDynamic for sprite area; used to indirect into deep space
+        Also allow APL/BottomWS to shrink to 32K
+        Fixed ReadArgs to allow y and z in keywords.
+        Fixed code in heap block extension to satisfy comment.
+        Changed default Sys$DateFormat to "%24:%mi:%se %dy-%m3-%ce%yr"
+        Changed *Time to use OSWord 14,0
+        ExtendHeap was total garbage!
+        Make MODE 23 Unix compatible
+1.85    ReadDynamicArea returns start of screen address
+        ChangeDynamic safe in conjunction with background heap ops
+        New FileSwitch
+        Moved Quit in command table to alphabetic place
+        Put a B in TimeCPU to make result more accurate
+1.86    OS_ReleaseDeviceVector disables IRQs.
+        OS_AddCallBack disables IRQs earlier.
+        OS_Confirm mouse shape has no colour 2
+        Flushing the printer buffer makes it dormant if not a stream
+        CheckModeValid works properly with a user monitor type
+        A500 keyboard changed
+        Mouse buffer not flushed when new bounding box set, instead coords are
+        clipped against the current bounding box when read out of the buffer.
+        MODEs 21 (multisync) and 24 added
+        Set DomainId to 0 at reset
+        Made process_callback_chain disable IRQs round updating head^ so we
+        didn't lose callback vector requests when processing the chain; saved
+        2 instructions in AddCallback
+        Maximum configurable mode is the max mode the MOS implements
+        ChangeDynamic can do RAMFS if no files in it.
+        Heap Extension call rounds size to words correctly (always towards
+        zero).
+        Handlers zapped to defaults if app starts and exit handler belongs
+        to supervisor.
+        VGA modes (25..28) added
+        ChangedBox fixed for VDU 5 8x8 and 8x16 text
+1.87    Hi-Res-Mono palette changed so PP field returned is &10 or &11 or &12
+        Also changed to return full black or full white for the RGB fields, not
+        red.
+        Bugfix in *Help; Escape while printing a module summary gave a random
+        error message.
+        Swapped silly name for another silly name.
+1.88    Heap manager size call fix.
+1.89    TimeCpu code was wrong (misordered merge of 2 8-bits) and had wrong
+        cutoff point for MEMC1a
+1.90    New week number code - now conforms to BS4760 (supposedly)
+        New FileSwitch with file buffering fixed
+2.00 (15 Sep 88)
+        Econet background FIQ problem fixed
+        NetStatus and Hourglass error handling improved
+        Kernel locks out background FIQ claims while the release service call
+        is in progress.
+2.00 (03 Oct 88)
+        New FileSwitch - default run action for text files is now *Type
+        New NetFiler - version in previous MOS was out-of-date
+2.00 (04 Oct 88)
+        New ADFS (Winnie podule problem fixed)
+        New TaskManager (doesn't claim null events when window not open)
+Note:   Change all occurrences of GoVec(2)
+        May also note that default vector owners can trash r10, r11 IFF
+        they claim the vector...
+
+2.01 (02 Mar 89)
+        New version of ChangeDynamicArea which reenables interrupts.
+        Heap manager optimised in a few places.
+        Fixed bug in extend heap call (stack imbalance when returning
+         'No RAM for extending heap' error)
+        Heap manager extend block has improved IRQ latency.
+2.01 (29 Mar 89)
+        Fixed "*%" (LDREQB not LDREQ).
+2.01 (31 Mar 89)
+        Fixed OS_ReadArgs with a /E argument that evaluates to a string (always
+        used to give 'Buffer full' - also fixed 2 other bugs lurking in the
+        background, viz. did STRB of buffer addr assuming it was non-zero to
+        indicate a string, and didn't allow for length and type bytes in amount
+        free value.
+2.01 (11 May 89)
+        Fixed OS_Word &15 reason code 4 (Read unbuffered mouse position)
+        - it used to generate undefined instruction trap due to stack mismatch.
+        Fixed OS_SWINumberToString with negative numbers.
+2.01 (16 May 89)
+        Fixed ROMModules never saying 'Running'.
+        Fixed bug which occasionally left soft cursors on the screen.
+2.01 (23 May 89)
+        Made OS_SpriteOp reentrant by pushing register dump area on stack.
+        Fixed sideways scroll by one 'byte' in MODEs 3 and 6.
+2.01 (26 May 89)
+        Made OS_Byte &87 restore caller's IRQ state during call.
+2.01 (30 May 89)
+        Made OS_Word &0E,0 enable IRQs during call.
+        Made OS_Word &15,0 enable IRQs during call.
+2.01 (01 Jun 89)
+        Fixed incarnation names being terminated by 1st character.
+        Fixed *Unplug using address as extra terminator for module name.
+2.01 (30 Jun 89)
+        Fixed podule IRQ despatcher corrupting R0 (prevented it from
+         correctly disabling the podule IRQ (or podule FIQ-as-IRQ) interrupt
+         if no handler)
+        Fixed rename incarnation bug.
+2.01 (01 Sep 89)
+        Added help on LEN in *Help Eval.
+2.01 (04 Sep 89)
+        Fixed bug in prefer incarnation which returned duff error pointers.
+2.01 (06 Sep 89)
+        Changed BadTime error message from null string to
+         'Invalid time interval'
+        Fixed bug in CallAfter/Every which returned duff error pointers.
+2.01 (11 Sep 89)
+        Fixed bug in AddCallBack which freed the wrong heap node.
+2.01 (12 Sep 89)
+        Fixed bug in monadic plus/minus in EvaluateExpression (eg *Eval 50*-3)
+2.01 (25 Sep 89)
+        Fixed bug in GSRead with quoted termination.
+        Fixed bug in keyboard driver (pressing Break (as escape) key when
+         keyboard buffer full did nothing)
+2.01 (27 Sep 89)
+        Added SWI OS_ChangeRedirection
+2.01 (06 Oct 89)
+        Optimised ConvertTo5Byte routine
+2.01 (10 Oct 89)
+        Took new FileSwitch from Neil
+2.01 (12 Oct 89)
+        Added SWI OS_RemoveCallBack
+2.01 (19 Oct 89)
+        Added code to poke VIDC clock latch from bits 9 + 10 of control
+         register.
+        Modified FIFO request pointer algorithm to new one, also taking the
+         VIDC clock speed into account.
+        Changed INKEY-256 to &A2.
+2.01 (23 Oct 89)
+        Changed memory sizing routine to cope with 12 and 16MBytes (but CAM map
+         still needs extending!).
+        Added keypad-4 reset to select monitor type 4 (super-VGA).
+2.01 (26 Oct 89)
+        Put in variables for pointer to CAM map and number of CAM pages.
+        Recoded ChangeDyn and NewReset to set these up and use them.
+        CAM map is now either in low-down area for up to 8MBytes, or just above
+         IRQ stack for 12 or 16MBytes.
+2.01 (27 Oct 89)
+        Added new VDU variable VIDCClockSpeed(172), the current VIDC clock
+         speed in kHz.
+2.01 (02 Nov 89)
+        Added ModeExtend pixel rate specification interface.
+2.01 (03 Nov 89)
+        Changed fast CLS code so it can cope with screens which aren't a
+         multiple of 256 bytes long (eg 800x600x4bpp).
+2.01 (07 Nov 89)
+        Put in code to handle sync polarity programming (including ModeExtend
+         interface).
+2.01 (08 Nov 89)
+        Fixed over-zealous fast CLS code.
+        Made a 4MBit EPROM image of this.
+2.02 (09 Nov 89)
+        Changed start-up message to "RISC OS+" (removed flag Fix0).
+        Started test software integration.
+2.02 (10 Nov 89)
+        Incorporated new screen modes from Paul Swindell.
+2.02 (20 Nov 89)
+        Changed RS423 RXIRQ routine to process the 'serial input ignored'
+         OS_Byte flag after checking for XON/XOFF characters.
+2.03 (21 Nov 89)
+        Changed !Draw and !Paint run file WimpSlots
+2.04 (30 Nov 89)
+        Got new headers (including new Hdr.CMOS).
+        Removed initialisation of CatalogCMOS and ExamineCMOS in R-poweron.
+        Optimised PushModeInfoAnyMonitor so it doesn't set up VIDC stuff - this speeds
+         up SWI OS_ReadModeVariable enormously (this had slowed down due to VIDC tables
+         now being different for different monitor types).
+2.04 (01 Dec 89)
+        Fixed OS_Word &07 (SOUND) needing a word-aligned control block.
+        Added SWI OS_FindMemMapEntries.
+2.04 (04 Dec 89)
+        Changed SWI OS_FindMemMapEntries to new spec.
+2.04 (07 Dec 89)
+        Modified SpriteLoad/SpriteMerge to use OS_File instead of OS_Find/OS_Args/OS_GBPB
+        - this means the Econet broadcast loader doesn't have to sit on SWI OS_SpriteOp.
+2.05 (08 Feb 90)
+        Updated *Help PowerOn to mention 4 key on numeric keypad.
+2.05 (01 Mar 90)
+        Put in code for handling squeezed modules. Bit 31 of init entry set
+         => module is squeezed (bits 0.30 are the length of the module, at
+         the end of which are the unsqueeze information words.
+        Bit 31 of die entry set => module is not killed by RMClear (the
+         unsqueezed module image normally has this bit set).
+2.05 (05 Mar 90)
+        Changed title back to "RISC OS" from "RISC OS+".
+2.06 (27 Jun 90) (some intermediate versions missing)
+        New screen mode parameters - letter box modes for VGA
+        OS_Byte &7C and &7D changed to use SWI XOS_SetCallBack rather than
+         poking to the callback flag themselves (other bits are used in this
+         flag)
+        OS_ChangeDynamicArea now checks explicitly for areas trying to grow
+         beyond their maximum possible size (necessary for machines with
+         more than 4MBytes)
+        OS_ReadDynamicArea specification changed: if bit 7 of r0 set on
+         entry, then on exit r2 holds the maximum size of the area (and r0,
+         r1 are set up as per usual)
+2.06 (16 Aug 90)
+        Fixed bug when configured screensize is less than 20K.
+        Started splitting off MEMC-specific stuff.
+2.07 (08 Oct 90)
+        Fixed bug in OS_ReadVarVal when you test for presence of a variable with
+         R2<0 and R4=VarType_Expanded and the variable is a macro.
+2.07 (30 Oct 90)
+        Made errors from unknown OS_Module call and unknown dynamic area in
+         OS_ChangeDynamicArea be "Unknown OS_Module call" and "Unknown
+         dynamic area" respectively rather than "" and "" respectively!
+         (They share the same error number).
+2.08 (31 Oct 90)
+        Fixed three bugs in the *Help code, to do with correctly handling
+         errors from OS_WriteC (via OS_PrettyPrint).
+2.08 (16 Nov 90)
+        Changed module handler to call Podule manager to translate podule
+         number into hardware address, rather than doing it itself.
+        OS_ValidateAddress now issues Service_ValidateAddress when area
+         appears to be invalid, so modules can add valid areas.
+2.08 (19 Nov 90)
+        Fixed two bugs in RMEnsure; (a) no help string (b) help string shorter
+         than 16 chars
+2.08 (27 Nov 90)
+        Added 3 more bytes of unplug CMOS
+2.08 (28 Nov 90)
+        Divorced kernel from FileSwitch (BGET and BPUT now always go thru vector)
+2.08 (08 Jan 91)
+        Fixed bug in OS_Module 11 (copied extra word, which could corrupt RMA)
+2.08 (15 Jan 91)
+        Fixed bug in OS_Module 17 (corrupted R9 on exit)
+        Changed OS_Module 17 to cope with extension ROM modules (including directly executables).
+2.08 (23 Jan 91)
+        Changed OS_Module 19 to cope with extension ROM modules
+        Added OS_Module 20 (same as 19 but also returns version number)
+        Fixed zero-effect bug in OS_Module dispatcher - if called with maxreason+1 it jumped off the end of the table -
+         into the unknown reason code routine!
+        SpriteOps 60 and 61 (switch output to sprite/mask) now issue Service_SwitchingOutputToSprite after switching
+        Issue Service_PostInit after all modules have been initialised
+2.08 (01 Feb 91)
+        Added *RMInsert command
+        Finished off extension ROM code (hopefully!)
+2.08 (06 Feb 91)
+        Changed keyboard code to only send LED changes when state has actually changed
+2.08 (07 Feb 91)
+        Made hardware address of extension ROMs zero
+2.08 (13 Feb 91)
+        Made OS_ReadSysInfo return screensize in step with real algorithm, ie a) ignore top bit of CMOS, b) if less than 20K,
+         add in extra pages until >= 20K
+2.08 (01 Mar 91)
+        DoMicroDelay optimised by Ran Mokady
+        Started to put in Configure Mode/MonitorType/Sync Auto.
+        Conditionally remove parallel/serial drivers
+        Stopped *Configure allowing garbage on end of command, eg *Configure Delay 30 this is rubbish
+        Fixed *Status TV corrupting error pointer while printing "TV          "
+2.08 (04 Mar 91)
+        Merged Configure Mode and Configure WimpMode
+        Set 'DriversInKernel' flag for now
+        Added OS_ReadSysInfo(1) - return configured mode/wimpmode value (with Auto expanded)
+        Fixed OS_ReadSysInfo(>1) so it returns an error properly
+2.08 (08 Mar 91)
+        Put in Configure PrinterBufferSize and support code
+2.08 (11 Mar 91)
+        Used to change screen mode and print "RISC OS xK" before module initialisation. Now it changes screen mode before
+        (in case modules print during init), and afterwards (in case a module has provided a new screen mode), and then prints
+        the message.
+        Also after a monitor type reconfigure, selects default mode, not mode 0.
+2.09 (12 Mar 91)
+        Fixed *Status MouseStep corrupting error pointer while printing "MouseStep  ".
+        Fixed *Status corrupting error pointer from module code.
+2.09 (15 Mar 91)
+        Put in IOEB and LC detection, and monitor lead type detection.
+2.09 (19 Mar 91)
+        Completed monitor lead type interface.
+        Changed default Language to 4 (which is new Desktop module number).
+2.09 (21 Mar 91)
+        Improved monitor lead detection of Hsync.
+        Made InitialiseROMModule return errors correctly.
+2.09 (25 Mar 91)
+        Fixed bug in OS_SWINumberFromString to do with checking for valid module SWI chunk.
+        Fixed loads of other bugs in SWI OS_SWINumberFromString.
+2.09 (26 Mar 91)
+        Fixed similar bugs in SWI OS_SWINumberToString.
+2.11 (02 Apr 91)
+        RM - Changed time calls to use TerritoryManager calls to cope with local time.
+             OS_ConvertDateAndTime now calls Territory_ConvertDateAndTime
+             Changes made in: Source.PMF.osword  and in Source.PMF.convdate.
+2.11 (05 Apr 91)
+        Finished putting in 82C710 detection and configuration.
+2.11 (12 Apr 91)
+        Put in support for new serial drivers.
+2.11 (18 Apr 91)
+        Moved Service_PostInit to after filing system selection.
+2.12 (26 Apr 91)
+        Put in SWI XPortable_Speed in RDCH/INKEY.
+        Made *Modules and *ROMModules check Escape flag.
+2.12 (29 Apr 91)
+        TestSrc.* (Power-on Selftest)
+        -  82C710 changes addressing for FDD inuse light
+        -  Validation of CMOS checksum
+        -  Disables long memory test (blue phase) if Misc1CMOS bit 7 is set
+        NewReset
+        -  Checks CMOS checksum on Power-on{delete/copy/r/t}. Forces complete (including Econet
+           station number) reset if checksum is corrupt.
+        -  Initializes checksum if CMOS reinitialised.
+        Source.PMF.i2cutils
+        -  Addition of ValChecksum and MakeChecksum
+        -  'Write' now maintains checksum byte. Checksum byte itself may be changed.
+        Hdr.A1
+        -  Addition of ChecksumCMOS switch. FALSE removes checksum maintenance.
+2.12 (02 May 91)
+        Changed default configured caps setting to NOCAPS.
+2.12 (07 May 91)
+        CMOS checksum seed changed to 1 (CMOSxseed in Hdr.CMOS)
+        Selftest reports CMOS failure to user only if IIC fault occurs
+        Space reserved for processor test removed from TestSrc.Begin
+2.13 (13 May 91)
+        Put in code to read unique ID
+2.13 (20 May 91)
+        Added new characters to system font
+2.13 (23 May 91)
+        Put in debugging for forced hard reset
+        Updated *Help Power-on
+        Fixed bug: if configured monitortype is unknown, uses naff VIDC parameters
+2.14 (05 Jun 91)
+        Put in hex digit pairs for characters 80..8B, corrected location of new characters.
+2.16 (12 Jul 91)
+        Changed default buffer flags for MOS buffers.
+2.16 (18 Jul 91)
+        Changed default 82C710 configuration.
+2.17 (26 Jul 91)
+        Adjusted Error_Code to place constructed error at GeneralMOSBuffer+4
+        rather than GeneralMOSBuffer to avoid clashes with default error
+        handler's use of GeneralMOSBuffer.
+2.17 (26 Jul 91)
+        Adjusted default owner of error vector to copy the error text limited
+        to 252 characters, truncating if necessary. Avoids duff error blocks
+        destroying the system.
+2.17 (26 Jul 91)
+        Fix display of error by default error handler - it used to not put
+        the error number out.
+2.17 (30 Jul 91)
+        Changed the value of INKEY(-256) to &A3.
+        Modified DoInsertESC (OS_Byte 153) to only do special things on buffers 0 or 1.
+2.17 (05 Aug 91)
+        Changed default CMOS settings.
+2.18 (12 Aug 91)
+        OS_Byte(2,non-zero) code now checks if serial stream open before trying to open it again.
+2.18 (13 Aug 91)
+        Changed Messages file: removed extra space in "Extn", added space on end of "EXTROM", "PODROM".
+2.18 (16 Aug 91)
+        Added code to respond to service DeviceFSCloseRequest to close printer and serial streams.
+2.18 (19 Aug 91)
+        Changed *Error to use MessageTrans error buffers. Avoids numerous clashes in GeneralMOSBuffer.
+2.18 (23 Aug 91)
+        Made ErrorSemaphore a byte, not a word.
+        Added PortableFlag so that we don't issue more than one abortive SWI Portable_Speed.
+        Change RS423Vdu to not put bytes in the serial output buffer if
+         couldn't open serial stream, and also to report any error.
+2.18 (30 Aug 91)
+        Change error "System variable not found" to "System variable 'blah' not found"
+2.19 (05 Sep 91)
+        Change monitor type for superVGA monitor ID to 1 for LiteOn monitor.
+2.19 (06 Sep 91)
+        Added an extra space on end of "Result: Result is %0, value :" message.
+2.19 (10 Sep 91)
+        Fixed kernel messages not translated after soft reset bug.
+2.19 (11 Sep 91)
+        Fixed Configure Mode Auto always using 27.
+3.00 (25 Sep 91)
+        Amber release.
+3.01 (07 Oct 91)
+        Fixed bug A-RO-9984 (requests to close kernel device streams didn't work).
+        Removed redundant non-functional code from DoOsbyteVar which tried to close the
+         serial output stream if WrchDests no longer included serial - this code is not
+         needed because the kernel now responds to the DeviceFSCloseRequest service.
+        Put in checks on OS_SpriteOp for SLoad/SMerge.
+3.01 (15 Oct 91)
+        Fixed bug RP-0039 (OS_ChangeDynamicArea goes wrong when amount to change = &80000000).
+        Removed redundant copy of month names table from kernel.
+3.01 (17 Oct 91)
+        Added DebugHeaps option to initialise used and freed blocks in heaps.
+        Changed OS_SpriteOp check: it used to check that offset to first
+         sprite = 16, but this is too stringent - there are silly files out
+         there that have extension words in the header - so now it just
+         checks that the offset is within the file.
+3.01 (21 Oct 91)
+        Put in support for 82C711 (including OS_ReadSysInfo(3)).
+3.03 (11 Nov 91)
+        Enhance system variables: increase allowable length from 256 to
+         16M; don't assign if going to overflow (found to be bad in
+         practice).
+        Split Arthur2 into system variable stuff (GSTrans etc,
+         OS_ConvertBinaryToDecimal, OS_ReadVarVal etc) and Utility, the
+         start of the utility module.
+3.03 (13 Nov 91)
+        Change palette code to use PaletteV.
+3.03 (18 Nov 91)
+        Added support for module to tell OS the real VIDC clock speed.
+3.03 (22 Nov 91)
+        Added bit in CMOS that is set on a CMOS reset, and cleared on any other
+         reset.
+3.03 (25 Nov 91)
+        Added new screen mode 22 for visually disabled - 768 x 288 x 4bpp, XEIG=0, YEIG=1,
+         for monitor types 0 and 1.
+3.04 (20 Dec 91)
+        Fixed system SetVarVal to no GSTrans a string result from
+          EvaluateExpression (ie fix *SetEval). As a consequence a new
+          SetEval type allowed: VarType_LiteralString - string without
+          GSTransing.
+3.04 (15 Jan 92)
+        Added OS_SetColour.
+        Switching output to a sprite takes into account Fg/BgPattern setup by OS_SetColour.
+3.05 (21 Jan 92)  lrust
+        Changed mouse buffer routines to BufferManager block transfer buffers to improve
+          interrupt latency.
+3.05 (24 Jan 92) JRoach
+        Increase default mouse step from 2 to 3 as per ECR.
+3.05 (28 Jan 92) LRust
+        Changed OS_Claim/Release code to restore IRQ state prior to releasing
+          vector node to system heap (conditional on IrqsInClaimRelease) to
+          improve interrupt latency.
+3.05 (30 Jan 92) LRust
+        Changed ProcessTickEventChain to re-enable IRQ's while returning ticker nodes
+        to the system heap (conditional on TickIrqReenter).  CallEvery/After events
+        are now processed last in centisecond clock interrupt.
+3.06 (14 Feb 92) TDobson
+        Fixed dotted line bug (dotted lines with a dot pattern length > 32
+        which start outside the window where the number of undisplayed
+        pixels modulo the dot pattern length exceeds 32 go wrong).
+3.06 (14 Feb 92) LRust
+        OK lets start with the easy ones!  Fixed bug report RP-0387. 
+        Change *Error syntax in 'HelpStrs' to show error number as optional.
+3.06 (18 Feb 92) LRust
+        Fixed RP-1299 - stack imbalance for OS_Word &0F under error conditions
+3.06 (19 Feb 92) TDobson
+        Added Welsh characters and swapped direction of hex number characters
+        in kernel system font.
+3.06 (19 Feb 92) LRust
+        Fix RP-0617.  TranslateError now preserves callers IRQ state
+3.06 (20 Feb 92) JRoach
+        Install an index on system variables to speed their assignment and
+        access.
+3.06 (20 Feb 92) LRust
+        Invoke Service_SyntaxError from OS_CLI if command syntax error
+3.06 (26 Feb 92) LRust
+        Fix RP-0609 and RP-0370.  Callback handler only entered if SVC stack is
+        empty.  RTC day/months of 0 are forced to 1
+3.07 (03 Mar 92) LRust
+        Setup of printer buffer uses internal CMOS read routines, not OS_Byte
+        which enable interrupts.  This prevents system hanging during start-up
+        if keys are pressed.  Fixes RP-0990 etc.
+3.07 (03 Mar 92) LRust
+        Fixed OS_Word 11 to return palette data to r1+1, fixing RP-1429
+3.07 (04 Mar 92) LRust
+        OS_GSInit now only resets the evaluation stack (in system workspace)
+        if the string contains a '<' character.  This prevents contention between
+        high level calls to OS_GSTrans and low level use by FileSwitch while
+        opening message files.  This fixes RP-1483
+        OS_SetVarValue fixed to use XOS_GSTrans (was non X form)
+        OS_SetVarVal now returns correct error if string is bad
+3.07 (05 Mar 92) LRust
+        OS_Confirm now reads Yes/No characters from message file.  The
+        pointer shape to be displayed is now read from the Wimp sprite pool,
+        RMA first followed by ROM, looking for 'ptr_confirm'.  Fixes RP-1449
+3.07 (06 Mar 92) LRust
+        OS_Byte 129,0,255 now returns version number &A4 (was &A3) fixing RP-1638.
+3.07 (06 Mar 92) TDobson
+        Sprite load/merge now only checks offset to first sprite is < length of file,
+         fixing bug RP-0589.
+3.07 (09 Mar 92) TDobson
+        Don't reset keyboard on OS_InstallKeyHandler if we've only just got the
+         keyboard ID.
+3.07 (17 Mar 92) JRoach
+        Postpone error lookup in ChangeDynamicArea to after the
+         reconstruction of the RAM and System Heaps. Prevents double-mapping
+         of pages resulting in a dead system. G-RO-5512.
+3.08 (23 Mar 92) LRust
+        OS_EvaluateExpression now returns R1 = 0 (no returned string) if the expression
+          evaulated by GSTrans overflowed the internal buffer.  This ensures that *IF
+          reports the buffer overflow error message instead of "not a string", fixing RP-1534
+3.08 (01 Apr 92) TDobson
+        Added code to switch into user mode and issue a dummy SWI just after Service_PostInit,
+        so that any pending callbacks get serviced (fixes RP-1812).
+3.08 (02 Apr 92) TDobson
+        Added screen blanking code.
+3.08 (03 Apr 92) LRust
+        RMTidy copies errors during die to GeneralMOSBuffer to ensure not overwritten
+        during re-initialisation.
+3.08 (07 Apr 92) TDobson
+        Fixed DebugHeaps code in ExtendHeap (used to overwrite data below base of heap).
+3.09 (23-Apr-92) LRust
+        * Fixed CTRL-Break/Reset causing CMOS corruption by doing 16 IIC
+          start/stop sequences after reset to allow RTC to shift out any
+          data requested.
+        * IIC start sequence extended to allow BMU to detect a start.
+        * IIC read/write now disable IRQs during clock and data hi times
+          to prevent erroneous idle sequence detection by BMU.
+3.09 (24-Apr-92) TDobson
+        Removed range checking of buffer number in OS_Byte &15 (flush buffer).
+3.10 (30-Apr-92)
+        Disable interrupts around IIC receive to aid BMU start/stop detection
+        by preventing idle periods (clock & data hi).
+
+-- RISC OS 3.10 released to public --
+
+3.20 (xx-Apr-93) JRoach
+        Added read option to OS_SetColour
+
+3.20 (15-Apr-93) TDobson
+        Made OS_CRC return an error if increment is zero, rather than looping forever.
+
+< lots of Medusa work >
+
+3.21 (21-Jul-93) TDobson
+        First Medusa release
+
+3.22 (09-Aug-93) AGlover
+        vdugrafg/vdugrafdec: Fix bug in putsprite|mask when LH edge obscured
+        vdugrafh:            Fix bug in SwitchOutputToMask when calculating mask size
+
+     (18-Aug-93) TDobson
+        RMA, System heap, Sprite area, Font cache, RAM disc are now new-style areas.
+        Sprite area, Font cache, RAM disc, Free pool maximum sizes are now dynamic (ie total ram size),
+         and their base addresses are allocated at run-time.
+        Doubly-mapped areas are now supported, but known bug exists if
+         screen grows and needs pages in use by a doubly-mapped area.
+        Spec change to OS_DynamicArea (Create dynamic area): if workspace pointer passed in is -1, the
+         value used is the base address of the area (this is important if the base address is being
+         allocated by RISC OS).
+
+     (20-Aug-93) TDobson
+        New addition to OS_ReadDynamicArea: if r0 on entry is -1 then
+         information is returned on application space:
+          r0 = base address (&8000)
+          r1 = current size (excluding bottom 32K)
+          r2 = maximum size (excluding bottom 32K)
+
+     (23-Aug-93) AGlover
+        vdugrafK: Ensure new format sprites aren't screensaved with palettes
+
+     (03-Sep-93) TDobson
+        Areas which require particular physical pages should now work.
+        LateAborts assembly option added (untested).
+
+     (03-Sep-93) SCormie
+        Memory information SWI (OS_Memory) added (file MemInfo).
+        Processor vector indirection added, including SWI OS_ClaimProcessorVector (file ExtraSWIs).
+
+ -- Version 3.23 released --
+
+     (17-Sep-93) AGlover
+        (vdugrafdec, vdugrafj vdugrafk) Sort out screenloading of sprites with new mode format
+        mode words (untested at present).
+     (20-Sep-93) TDobson
+        Screen is now a new style area.
+        All old-style area code now removed.
+        Fixed bug MED-00488 (composite sync inverted).
+        Fixed bug MED-00583 (modes with line length > 4Kbytes didn't work).
+     (21-Sep-93) TDobson
+        Fixed bug MED-00395 (OS_Byte 70 and 71 corrupting r5).
+        Made kernel enable cache and write-buffer again.
+     (22-Sep-93) TDobson
+        Removed old screen modes 47-51 and put in new modes 47 (PCSoft) and 48,49 (Games).
+        Fixed DataControlRegister programming so that eg mode 29 works with 2M VRAM.
+        Late abort option enabled.
+
+ -- Version 3.24 released
+
+     (01-Oct-93) TDobson
+        After pre-shrink handler is called, round down shrink value returned to a page multiple.
+     (07-Oct-93) TDobson
+        Fix MED-00626: RMReInit should only write to CMOS if module was unplugged to start with
+        (solves FSLock problem).
+     (08-Oct-93) TDobson
+        Bottom level DPMS control put in - byte at location &147D controls DPMS state on blanking.
+        Modified OS_Word(&15,4) (Read unbuffered mouse position) to call PollPointer, so that if
+         no flyback IRQs happen (because hsyncs have been turned off), moving the mouse pointer
+         still unblanks the screen.
+
+ -- Version 3.25 released
+
+     (19-Oct-93) TDobson
+        Fix MED-00523: Remove pointer if would be completely off LHS of screen, otherwise can
+        upset display if it straddles hsync pulse.
+        Fix MED-00637: Creating dynamic area of zero maximum size now works.
+        Screen mode 32 changed to NColour=63 and ModeFlags=0, like all other numbered 256 colour
+         modes.
+
+ -- Version 3.26 released
+
+     (21-Oct-93) SCormie
+        *Configure MouseType should have called OS_Pointer.
+     (25-Oct-93) SCormie
+        NewReset now waits up to 1 sec for keyboard present.
+     (25-Oct-93) TDobson
+        Fix bug MED-00811 - initialise SpriteSize to zero on reset.
+     (25-Oct-93) JRoach
+        Fix bug MED-00680 - *Save syntax message not tokenised properly.
+     (26-Oct-93) JRoach
+        Fix bug MED-00570 - delete-power-on gives 1997. Problem was
+        delete-power-on set base to 1994 and so when the modulo of '3' came
+        out of the clock the kernel thought it had to be 1997.
+     (26-Oct-93) TDobson
+        Fix bug MED-00301 - mode selector block now alternates between two
+        overlapping ones, so that returned mode number changes each mode
+        change (although if someone goes eg M%=MODE:MODE 12:...:MODE M% it
+        will use the block specified by M%).
+     (28-Oct-93) AGlover : vduswis, vdudriver, vdugrafj, vdugrafk
+        Bugfixing session on sprites. Major points:-
+        Screensave works, and gives old format sprites where possible.
+        Screenload works, and no longer believes the mode number in the
+        sprite - it will always build a mode selector to suit the sprite.
+        EIG 0 and EIG 3 sprites supported - fixes DragASprite leaving
+        trails on-screen in these modes.
+        Mode 32 is now the monitor type 4 256 colour substitute mode.
+        GetSprite(User) ensures no LH wastage on new format sprites.
+        Screensave now copes with saving a full entry 256 colour palette.
+        Whenever possible GetSprite will make an old format sprite - if
+        you must have a new format sprite make it first with CreateSprite.
+        
+      While there are four of us intermittently touching bits of the kernel
+      please say which files you've actually altered. Thanks.           amg
+      
+      (03-Nov-93) AGlover : vdugrafj
+        Change substitute mode numbers used for 90 x 90 dpi sprites to 25-28
+        instead of 18-21.
+
+      (04-Nov-93) JRoach : GetAll, Kernel
+        Enable IncludeTestSrc and make Reset vector for resetting work in
+        conjunction with the test source.
+
+      (08-Nov-93) TDobson : vdudecl, vdudriver
+        Complete kernel support for DPMS.
+
+      (10-Nov-93) TDobson : source.pmf.osbyte
+        Fix bug MED-00963: INKEY(-256) now returns &A5.
+
+      (11-Nov-93) TDobson : source.vdumodes
+        Fix bug MED-?????: mode 32 now same timings as modes 29-31.
+        
+      (12-Nov-93) AGlover : source.vdugrafg
+        Fix bug MED-01112 : 32K/16M sprites not working with SpriteOp ReadPixelColour (41)
+        Fix bug MED-????? : Correct the routine which bounces palettes on new format sprites
+        
+-- Version 3.27 build
+
+      (18-Nov-93) AGlover : source.vdugrafh, source.vduswis, source.vdudriver, source.vdugrafj
+        Fix bug MED-01130 : GetMaskSpWidth was going wrong when mask exactly fitten in a word
+        Change handling of sprite type so that unknown types are handled as 32bpp - will make
+        life easier for people adding new types that the OS doesn't handle.
+
+      (23-Nov-93) AGlover : source.vdugrafg, source.vdugrafh, source.vdugrafi
+        Fix bug MED-01281 : return an error when I should for spriteops given new format
+        masks.
+        
+      (24-Nov-93) AGlover: source.vdugrafg
+        Fix bug MED-????? : sprite name matching code was being broken by changed
+        lowercase macro. Code now uses uk_lowercase which retains the original
+        behaviour of the macro keeping the sprite stuff compatible with 3.10
+
+      (25-Nov-93) TDobson: source.vdumodes
+        Fix mode definition for mode 47 (PCEm mode 360 x 480) to have sync polarity 3,
+        so it gives correct height on VGA-only monitors.
+
+      (26-Nov-93) SCormie: Source.PMF.key, Source.PMF.osinit
+        Keyboard driver and key handler now trigger each other correctly during reset.
+        Change default serial threshold from 9 to 17.
+
+      (30-Nov-93) AGlover: source.vdu23, source.vduwrch
+        Fix bug MED-01141: Split CompileTextFgBg into two separate routines and only call
+        the one actually relevant. This prevents spurious colour changes when a text colour
+        set by OS_SetColour is changed using VDU 17 or VDU 23,17 (what used to happen is
+        that because the Fore/Back Col and Tint words didn't produce the colour in use (which
+        had been modified directly by OS_SetColour) it was replacing it with whatever the
+        ForeCol and Tint produced. Note: Doing a VDU 23,17 before the first VDU 17 will still 
+        go wrong - but I don't think it's worth fixing.
+
+      (30-Nov-93) TDobson: source.vdumodes
+        Change XEigFactor for games modes 48,49 to 2 (from 1) at the request of Ran Mokady.
+
+      (01-Dec-93) TDobson: source.pmf.osword
+        Fix bug MED-00355: OS_Word(&0F) no longer requires a terminating
+        character after string (it now pushes the string on the stack and
+        terminates it, before calling the TerritoryManager).
+
+      (01-Dec-93) TDobson: NewReset
+        Fix bug MED-01513: now prints eg "RISC OS 10MB" on startup.
+
+      (02-Dec-93) TDobson: ModHand
+        Fix bug MED-01229: now uses 8 bytes of unplug CMOS for podules, + 1 for network card
+
+      (06-Dec-93) JRoach: NewReset
+        Correct mouse step default to 2 for faster mice we'll be using.
+               
+      (07-Dec-93) AGlover: source.vdugrafj, source.vdugrafk
+        Fix bug MED-?????: make sure screensave and getsprite(user) don't permit palettes
+        on sprites which it can't force back to an old screen mode
+
+      (09-Dec-93) TDobson: source.vdudriver, source.vduswis
+        Use Roger's algorithm for default XEigFactor, YEigFactor for mode selectors.
+
+      (13-Dec-93) TDobson: GetAll
+        Set AddTubeBashers flag to FALSE.
+
+      (15-Dec-93) TDobson: GetAll, NewReset
+        Set DebugROMInit and DebugROMErrors flags to FALSE.
+        Fix bug MED-01774: Change default FontSize to 64K.
+
+-- 3.28 build
+
+      (21-Dec-93) AGlover: source.vdugrafj
+        Change screenload so that should the mode selector it made fail, and the
+        sprite has a mode number, it will select that mode as a fallback. Note:
+        this won't work fully until OS_ScreenMode correctly returns VS for a mode
+        it can't do. Bug MED-01895.
+
+      (21-Dec-93) OLove: Messages
+        Changed 'Memory cannot be moved' error message.
+
+-- New version 3.29 spuriously created by Owen Love 21-Dec-93
+
+      (21-Dec-93) AGlover: arthur3
+        Fix bug MED-01583 - *eval 9999999999999 was calling msgtrans twice to return error
+          
+      (06-Jan-94) TDobson: Kernel, Source.PMF.Key, SWINaming
+        Added SWI OS_Reset - part of fix for MED-01397.
+
+      (10-Jan-94) BCockburn: Middle
+        Fixed Ethernet address generation to use the correct base
+        (&0000A4100000) value rather than the value &0000A4000000 as found
+        in the Dallas chip itself.
+
+      (10-Jan-94) TDobson: source.vdudriver, source.vduswis
+        Fixed bug MED-00563: invalid mode selector should return error from
+        mode change, not select mode 0.
+        If current monitor type is a file, now uses VGA substitute table for
+        unavailable mode numbers - eg if you select mode 16, you now get
+        mode 27, rather than mode 0.
+
+      (10-Jan-94) TDobson: source.vdudriver
+        Fixed bug MED-00483: "mode selector" not on a word boundary (eg
+        sprite mode word) is now not offered round to Service_ModeExtension,
+        so should now give error, rather than data aborts.
+
+      (12-Jan-94) JRoach: GetAll
+        Fixed bug MED-00585: configured language wasn't 10, and now is.
+
+      (13-Jan-94) TDobson: GetAll, ModHand
+        Introduced conditional flag RMTidyDoesNowt - if true (which it is on Medusa),
+        then RMTidy returns having done nothing. This is because it would always
+        fail anyway, because FSLock always refuses to die. Fixes MED-02125.
+
+      (14-Jan-94) SCormie: source.PMF.osinit
+        Changed combo chip configure sequence so that 710 configuration doesn't put
+        665 into a strange state if receiving at the same time (caused chip to generate
+        an interrupt which could not be cleared.
+
+      (14-Jan-94) JRoach: Arthur2
+        Changed GSInit/GSRead to use its stack memory as a wrapping buffer.
+        This means GSInit doesn't need to flatten it before starting which
+        means recursive calls the GSTrans work.
+
+      (17-Jan-94) TDobson: ARM600, ChangeDyn
+        Put in semaphore, so that OS_ChangeDynamicArea will fail if it is already threaded.
+        (Data abort handler clears it, in case area handler goes bang!)
+
+      (17-Jan-94) TDobson, AGlover: source.vdugrafj
+        Fixed the 21-Dec-93 fix so it assembles and hopefully works!
+
+      (18-Jan-94) TDobson: ARM600, Kernel, SWINaming
+        Added soft copy for MMU control register, and SWI OS_MMUControl to update it.
+
+      (19-Jan-94) AGlover: source.vdugrafg
+        Fix bug MED-02210 - plot mask for 1bpp masks was going wrong when plotting clipped.
+        
+      (19-Jan-94) JRoach: NewReset
+        Correct CMOS default for textured window background to match the CMOS spec.
+        (The spec says set bit 7 of &8c to 0 to turn on textured window backgrounds).
+
+      (19-Jan-94) JRoach: NewReset
+        Correct CMOS default for desktop font to '8' which is still Homerton.Medium.
+        Changed needed as Darwin has been removed and so the numbers change.
+
+      (19-Jan-94) TDobson: ARM600
+        Fixed bug MED-02452: make BangCamUpdate check if oldaddr=newaddr,
+        and don't remove page from old address if so.
+
+      (19-Jan-94) TDobson: source.vdudriver
+        Changed notional operational range for VCO to 55-110MHz (from 40-80MHz).
+
+      (21-Jan-94) TDobson: ARM600
+        Changed ROM access speed to 156.25ns (from 187.5ns)
+
+      (21-Jan-94) TDobson: source.vdudriver
+        Changed ComputeModuli to only use R-values up to 16 (not 64), to
+        improve minimum comparison frequency.
+        Change FIFO load position algorithm to use table produced by Peter
+        Watkinson.
+
+      (24-Jan-94) TDobson: GetAll, source.vdudriver, source.vduswis
+        Conditionally reversed change to XEig/YEig algorithm back to what it was
+        in 3.27.
+
+      (25-Jan-94) TDobson: source.vdudriver
+        Fixed FIFO load position table to use correct units.
+
+-- New version 3.30 created by JRoach
+
+      (28-Jan-94) AGlover: syscomms, source.vdugrafj
+        Fix bug MED-01570 (syscomms), and various bugs reports involving 
+        1bpp 2 x 2 eig modes (vdugrafj: sanitizesgetmode was mishandling
+        double pixel modes)
+
+      (28-Jan-94) TDobson: source.vdumodes
+        Update kernel's hard-wired mode tables to use new VCO range.
+
+      (28-Jan-94) TDobson: TickEvents
+        Fixed bug MED-02498: OS_CallAfter/Every and OS_RemoveTickerEvent problems.
+
+      (31-Jan-94) TDobson: TickEvents
+        Fixed register corruption in above fix.
+
+      (01-Feb-94) AGlover: hdr:sprite, vdugrafdec, vdugrafj, vdugrafk, vdugrafl 
+        Fix bug MED-02160. Make switching output to, and screenloading of, a
+        256 colour 8bpp sprite work. The hdr change is because the value of 
+        MaxSpritePaletteSize has been updated.
+
+      (03-Feb-94) TDobson: ARM600, ChangeDyn, GetAll
+        Fix bugs MED-00793, MED-01846. Dynamic areas which may require
+        specific physical pages must now have a new bit set in the dynamic
+        area flags. As a result, other areas can perform large grows in one
+        chunk, rather than being split up, because it is not necessary to
+        set up the page blocks etc. Therefore a) dragging RAMFS bar is much
+        faster and b) there's no chance that intermediate reinits of RAMFS
+        can steal pages that were to be used for subsequent chunks.
+        
+-- New version 3.31 built by TMD
+
+      (08-Feb-94) AGlover: source.vdugrafh
+        Fix bug MED-01885. WritePixelColour wasn't working correctly for
+        8bpp full palette sprites.
+
+      (09-Feb-94) TDobson: Messages
+        Fix part of bug MED-02389 by changing system heap full message.
+                                   
+      (10-Feb-94) AGlover: source.vdugrafh
+        More on MED-01885. AddTintToColour decides what to do by NColour, rather
+        that SprRdNColour which is correct for Write/ReadPixel in sprite.
+        Set NColour to SprRdNColour for the call and then restore it afterwards.
+        The scope of the bug was more than first thought - it affected writing to
+        any 8bpp sprite in anything other than an 8bpp screen mode.
+
+      (11-Feb-94) TDobson: NewReset
+        Limit maximum size of RAM disc to 16MB, because FileCore can't cope
+        with some larger sizes.
+
+-- New version 3.32 built by TMD
+
+      (14-Feb-94) TDobson: NewIRQs
+        Fix bug MED-02859 - Unknown IRQ code now copes with DMA IRQs.
+
+      (17-Feb-94) AGlover: source.vdugrafk
+        Fix bug MED-02895 - *screenload was discarding the error pointer
+        on an error return from WritePaletteFromSprite
+        
+      (18-Feb-94) AGlover: source.vdugrafj
+        Secondary fix for MED-02895. WritePaletteFromSprite was actually
+        destroying the error pointer. Remove the change to vdugrafk.
+        
+====> Medusa OS build <==========================================================
+
+      (28-Apr-94) AGlover: source.vdugrafj
+        Fix screenloading of 8bpp non-full palette sprites (deferred fix from Medusa
+        freeze). MED-03186
+      (28-Apr-94) AGlover: source.vdugrafh
+        Fix spriteop readpixel for 256 colour full palette modes (deferred fix from
+        Medusa freeze). MED-03090
+      (28-Apr-94) AGlover: source.vduswis
+        Fix readmodevariable returning 63 instead of 255 for a sprite mode word.
+        MED-03265
+      (25-May-94) AGlover: source.vdugrafg,j,k
+        Changes to allow palettes on new format sprites of 8bpp and below. Also
+        William Turner's vdugrafh,i.
+        
+**** this has been superceded by the +log file - no further entries here.
+(amg: 21st September 1994)
+        
\ No newline at end of file
diff --git a/Doc/!ReadMe b/Doc/!ReadMe
new file mode 100644
index 0000000000000000000000000000000000000000..522352ed4961c755696bdf4b41f51a2fec9f54de
--- /dev/null
+++ b/Doc/!ReadMe
@@ -0,0 +1,57 @@
+> !ReadMe
+
+Instructions on how to make/modify Arthur.
+
+Directory Asm contains the Exec files.
+
+Asm.A500 assembles the A500 kernel and then does *Asm.BuildA500
+Asm.BuildA500 glues modules to the kernel to make an image for 27512's.
+Asm.BigA500 glues modules to the kernel to make an image for 1Mbit devices.
+
+Asm.Test assembles the Archimedes kernel, for 300 and 400 series machines.
+  NB. This uses Hdr.Archie, not Hdr.Test !!!
+Asm.Test2 glues modules to the kernel to make an image for 1Mbit devices.
+
+Asm.MoveFS takes a new copy of FileSwitch (and headers) from the net.
+Asm.TransTim takes a new copy of Tim's stuff from the net.
+
+
+Directory Hdr has the header files for the various assemblies.
+
+
+Directory Text has various files, only one of which is important:
+Text.Changes should be updated whenever changes are made to Arthur.
+Text.Still has my list of things still to do.
+
+
+Quick guide to the source files:
+
+Kernel is the first, and has the SWI dispatcher, plus the initialized variables
+to copy into RAM, plus some Brazil swi handlers.
+
+Middle contains the rest of the Brazil swi stuff, plus supervisor routines.
+
+NewReset is the reset code - if you need to change this, you will need to use
+the AddTubeBashers flag, which lives in Kernel. The TubeChar macro should then
+function.
+
+Super1 is the supervisor loop, plus more Brazil oddsnsods.
+
+ArthurSWIs is ReadUnsigned, vectors, services, ValidateAddress. Note that
+the version of ChangeDynamic in here is NOT the one that's assembled.
+
+Arthur2 is variables, GSTRANs
+
+Arthur3 is command code, including Status/Configure.
+
+Other files are hopefully obvious from their names.
+
+
+Backing Up.
+===========
+
+You need to copy the whole tree from $.Kernel to the backup medium.
+You also need to take a copy of all the current header files from $.Hdr.
+A copy of all the tools in $.Library and $.Utils would also be handy.
+
+                             Have fun!
diff --git a/Doc/0197276.02 b/Doc/0197276.02
new file mode 100644
index 0000000000000000000000000000000000000000..8ec4977270d2baebb4ab83d0b5e3263efcb84459
--- /dev/null
+++ b/Doc/0197276.02
@@ -0,0 +1,1400 @@
+
+Copyright (C) Acorn Computers Ltd. 1993       0197,276/FS Issue 2 **Live**
+
+              Medusa Memory Management Software Functional Specification
+              ==========================================================
+
+                   -----------------------------------------
+                   | Drawing No : 0197,276/FS              |
+                   |      Issue : 2 **Live**               |
+                   |       Date : 15th April 1993          |
+                   |     Author : Tim Dobson               |
+                   |     Sheets : n                        |
+                   |  Last Issue: ?                        |
+                   -----------------------------------------
+
+ Contents
+ ========
+
+ 1 History
+
+ 2 Outstanding Issues
+
+ 3 Overview
+
+ 4 Technical Background
+
+ 5 User Interface
+        5.1 TaskManager and large RAM sizes
+
+ 6 Programmer Interface
+        6.1 Logical memory map
+        6.2 Free pool
+        6.3 New SWI: OS_DynamicArea
+            6.3.1 Reason codes
+                6.3.1.1 Create dynamic area
+                6.3.1.2 Remove dynamic area
+                6.3.1.3 Return information on dynamic area
+                6.3.1.4 Enumerate dynamic areas
+                6.3.1.5 Renumber dynamic area
+            6.3.2 Dynamic area handler routine
+                6.3.2.1 PreGrow
+                6.3.2.2 PostGrow
+                6.3.2.3 PreShrink
+                6.3.2.4 PostShrink
+            6.3.3 Sequence Of Actions When OS_ChangeDynamicArea Is Called
+                6.3.3.1 Service_PagesUnsafe
+                6.3.3.2 Service_PagesSafe
+            6.3.4 Implementation Notes for OS_ChangeDynamicArea
+            6.3.5 OS_DynamicArea Service Calls
+                6.3.5.1 Service_DynamicAreaCreate (&90)
+                6.3.5.2 Service_DynamicAreaRemove (&91)
+                6.3.5.2 Service_DynamicAreaRenumber (&92)
+        6.4 New SWI: OS_Memory
+            6.4.1 OS_Memory Reasons 0-5: Convert Memory Address
+            6.4.2 OS_Memory Reasons 6-8: Physical Memory
+                6.4.2.1 Read Physical Memory Arrangement Table Size
+                6.4.2.2 Read Physical Memory Arrangement Table
+                6.4.2.3 Read Amounts Of Various Sorts Of Memory
+            6.4.3 I/O Space Information
+                6.4.3.1 Read Controller Presence
+        6.5 Old SWI OS_SetMemMapEntries (&53)
+        6.6 Old SWI OS_ReadDynamicArea (&5C)
+        6.7 New SWI OS_ClaimProcessorVector
+
+ 7 External Dependancies
+
+ 8 Development Test Strategy
+
+ 9 Organisation
+
+ 10 Future Enhancements
+        10.1 Abort trappping
+            10.1.1 Technical background
+            10.1.2 New SWI: OS_AbortTrap
+                10.1.2.1 Add abort trap
+                10.1.2.2 Remove abort trap
+
+
+ 1. History
+ ==========
+
+             TMD 05-Nov-92   Started initial draft
+             TMD 08-Mar-93   More detail added
+     Issue A TMD 21-Apr-93   Released for initial review
+             JSR 11-May-93   Input from review entered
+             JSR 24-May-93   Adjustments to area grow and deletion
+                             behaviour.
+                             Add 2 controllers to list: VIDC1 and VIDC20 to
+                             allow ScreenTricks module to operate.
+             JSR 02-Jun-93   Added section to development test strategy for
+                             Wimp and Switcher testing.
+             JSR 04-Jun-93   Comments on Issue 1 spec from DLowdell.
+
+ 2. Outstanding Issues
+ =====================
+
+ What if anything should be done to the task manager's task display to cope
+ with large memory sizes?
+
+ The handling of Wimp_ClaimFreeMemory. This SWI claims the free pool making
+ it available for data transfers. The Wimp used to implement this by putting
+ the free memory on the end of the current task. This is no longer
+ necessarily going to work as there may not be any free space beyond the end
+ of the current task's memory. Also, it is quite likely that there won't be
+ enough space for all the free memory.
+
+
+ 3. Overview
+ ===========
+
+ This specification covers changes to the memory management system in RISC
+ OS for Medusa platforms.
+
+ The main features of Medusa platforms which require changes to RISC OS's
+ memory management are as follows:-
+
+  1) Ability to cope with RAM sizes up to 256+2Mbytes, rather than the
+     current maximum of 16Mbytes;
+
+  2) Ability to control DMA;
+
+  3) Requirements for second processor card drivers to claim memory;
+
+  4) Physical RAM no longer contiguous;
+
+  5) ARM600 MMU requires page table allocation;
+
+  6) Logical memory map expansion due to 32-bit address space.
+
+  7*) Changes to support emulation of VIDC1;
+
+  8*) Changes to support line-doubling;
+
+ Note: The implementation of the starred items above (VIDC1 emulation,
+ line-doubling) are not part of the Medusa project; however it is an aim of
+ the memory management work that it should be possible to implement these in
+ a soft-loaded module at a later date.
+
+ 4. Technical Background
+ =======================
+
+In RISC OS 2 and 3 (version 3.10) the memory management task was divided
+between the kernel and the wimp. The kernel ordered the memory into dynamic
+areas and an application space, and the wimp then bludgeoned the kernel into
+not getting in the way of the wimp's model of having a free pool and
+multiple application spaces, rather than just the one application space
+which had all spare memory in it. For Medusa the free pool management will
+be moved into the kernel so that there isn't such a wrestling match between
+the kernel and the wimp. The wimp will still manage the multiple application
+spaces - it being the module responsible for constructing and managing tasks
+in the desktop. The main kernel interface for memory management was
+OS_ChangeDynamicArea which simply allowed the resizing of dynamic areas.
+This has been added to for Medusa with the OS_DynamicArea SWI for the
+control, creation and deletion of dynamic areas.
+
+The dynamic area management in RISC OS 2 and 3 (version 3.10) was somewhat
+tied together with the dynamic area clients. The change dynamic area SWI
+specifically called other modules depending on which dynamic area was being
+resized. The result was that there was no flexibility with how dynamic areas
+were used.
+
+Other memory related services were not available. For example it was not
+possible to find out what memory or hardware was available on the system
+without knowing a great deal about the platform.
+
+Memory is referred to in three ways: physcial address, logical address and
+physical page number. The first two refer to the address of the memory in
+the physical address space (what IOMD presents to ARM) and logical address
+space (what the ARM memory manager presents to the ARM core) respectively.
+The physical page number is an arbitrary number assigned to RAM pages. It is
+necessary to have this last abstraction to save space. If it weren't there
+then the storage and management overheads would be prohibitive on a small
+memory machine, which, as Medusa is targetted at under 1000 pounds, is
+likely to be a majority of users. The way physical page numbers are
+allocated is that all the contiguous RAM section in physical address space
+are bunched together and the page numbers allocated from 0 upwards.
+
+There are several interfaces described here which use Page blocks. These are
+tables of 3-word records:
+        word    use
+        0       Physical page number
+        1       logical address
+        2       physical address
+That is, a block of memory 12*N bytes big where N is the number of records
+in the block. They are used to pass round lists of addresses or pages.
+
+
+ 5. User Interface
+ =================
+
+ 5.1 TaskManager and large RAM sizes
+ -----------------------------------
+
+ The TaskManager module's 'Task display' window does not cope well in this
+area at present. The combination of a 16-fold increase in maximum RAM size
+and an 8-fold decrease in page size means that the existing linear 1
+pixel-per-page model will not be practical on large memory machines. A
+number of options are possible:-
+
+  a) Do nothing. It is rare that anyone will want to manually drag an area's
+size to anything particularly big. Most big programs will set their own
+Wimp slot size;
+
+  b) Allow the user to click in the number fields and enter a number
+(presumably still in K);
+
+  c) Make the scale exponential (ie at the small sizes it goes in units of
+4K, but when the area gets bigger go in larger and larger steps).
+
+  d) Make the scale a modified exponential (1+log(x)/c).
+
+
+ 5.2 *-Command *Cache On/Off
+ ---------------------------
+
+ A module will be provided to give this functionality. It will switch both
+caching and write-buffering on and off. The corresponding SWIs will not be
+supplied as they are inapplicable to an ARM600/700 based system.
+
+
+ 6. Programmer Interface
+ =======================
+
+ 6.1 Logical Memory Map
+ ----------------------
+
+        Address         Size    Use                             Public?
+
+        00000000        16k     System workspace                Private
+        00004000        16k     Scratch space                   Public(1)
+        00008000        28M-32k Application space               Public
+        01C00000        8k      SVC stack                       Public(2)
+        01C02000        2M-8k   System heap                     Private
+        01E00000        8k      Undefined stack                 Public(2)
+        01E02000        1M-8k   Soft CAM map                    Private
+        01F00000        32k     Cursor/sound etc                Private
+        01F08000        32k     "nowhere"                       Private
+        01F10000        1M-64k  Reserved for fake screen (480k) Private
+        02000000        12M     RMA                             Public(3)
+        02C00000        4M      Level 2 page tables             Private
+        03000000        4M      I/O space                       Private(4)
+        03400000        1M      Reserved for VIDC1 emulation    Private
+        03500000        1M      VIDC20                          Private
+        03600000        1M      Reserved for Vinit &c emulation Private
+        03700000        1M      Reserved for 2nd Proc ctrl regs Private
+        03800000        8M      ROM                             Private
+        04000000        2G-64M  Dynamic areas                   Public(5)
+        80000000        512M    Copy of physical space          Private
+        A0000000        1 1/2 G More dynamic areas              Public(5)
+
+Notes:
+1) This may be used by any module. The rules for its use are:
+        Not in an IRQ routine
+        Not if you're going to call something which might use it while you
+                are
+Example clients are: FileCore to hold structures whilst working out how to
+allocate some free space; Filer to hold structures for OS_HeapSort.
+2) It may be assumed that these end on a 1M boundary and will exception if
+accessed beyond that end. However, the exact location of these stacks should
+not be assumed.
+3) The location of RMA and its maximum size may not be assumed. However, it
+is guaranteed that it will be in low 64MBytes (ie can execute 26 bit code).
+4) Except where particular device drivers export hardware addresses.
+5) Only in so far as a client may make its own dynamic area.
+
+
+ 6.2 Free pool
+ -------------
+
+ Large RAM sizes pose a problem for RISC OS, because the existing metaphor
+ that all spare memory lives in application space breaks down. The current
+ limit on application space size is 16MB, whereas the maximum RAM size on
+ Medusa platforms is 258MB. Although this limit can be moved up a little by
+ moving other areas out of the way, it is not possible to extend it enough
+ (eg I/O still has to live at &03000000 onwards).
+
+ To cope with this a new dynamic area is created, known as the "free pool"
+ (area number 6). The operation of SWI OS_ChangeDynamicArea is modified as
+ follows:-
+
+  a) When an area (other than the free pool) is grown, memory is taken from
+     the free pool, if any exists (the current application is not notified
+     of this).
+
+     If having shrunk the free pool to zero size, there is still not enough
+     memory for the growth, the kernel attempts to remove pages from the
+     application space as it does under existing versions of RISC OS.
+
+  b) When an area (other than the free pool) is shrunk, the pages recovered
+     are added to the free pool. The current application is not consulted.
+
+  c) If the free pool itself is grown, pages are taken from application
+     space to put in it (and the application consulted beforehand).
+
+  d) If the free pool is shrunk, the recovered pages are added to
+     application space (the application is consulted beforehand).
+
+ The WindowManager module also changes, to take this into account. In some
+ ways its operation is simplified, as it no longer needs to maintain its own
+ free pool. However the implementation of the following SWIs needs to
+ change:-
+
+  Wimp_TransferBlock at present puts all used task memory into application
+  space, and then copies the relevant bits over. It cannot do this any more,
+  as the total used task memory may not fit into application space. Instead
+  it must do the following:-
+
+   Split the transfer into chunks of no more than half of the maximum
+   application memory size. For each chunk, put the appropriate pages of the
+   source block in at the start of application space, and the appropriate
+   pages of the destination block above these, then perform the transfer.
+   After all chunks have been done, restore the current task's pages in
+   application space.
+
+  Wimp_ClaimFreeMemory needs to be modified, because the Wimp no longer
+  maintains control of the free pool.
+
+
+ 6.3 New SWI: OS_DynamicArea (&66)
+ ---------------------------------
+
+ This SWI performs miscellaneous operations on dynamic areas.
+
+ On entry, r0 provides a reason code which determines which operation is
+ performed.
+
+ Note that as all operations on dynamic areas work in physical page numbers
+ it is not possible to map anything other than RAM pages (DRAM and VRAM)
+ into a dynamic area. In particular EASI space cannot be mapped in.
+
+  6.3.1 Reason codes
+  ------------------
+
+    6.3.1.1 Create dynamic area
+    ---------------------------
+
+    This call creates a new dynamic area.
+
+    On entry:   r0 = 0 (reason code)
+                r1 = new area number (-1 => RISC OS should allocate number)
+                        (must not be 128-255 - see section 6.6)
+                r2 = initial size of area (in bytes)
+                r3 = base logical address of area (-1 => OS should allocate
+                      base address)
+                r4 = area flags
+                        bits 0..3 = access privileges to be given to each page
+                                     in the area (same format as for
+                                     OS_Read/SetMemMapEntries)
+
+                        bit  4    = 0 => area is singly mapped
+                                  = 1 => area is doubly mapped
+
+                        bits 5..31 must be zero
+
+                r5 = maximum size of area, or -1 if the area should be
+                      capable of growing to the total RAM size of the
+                      machine
+                r6 -> area handler routine, or 0 for no routine
+                r7 = workspace pointer to be passed in r12 on entry to
+                      area handler (should be 0 if r6=0)
+                r8 -> area description string (null terminated), eg
+                       "Font cache". This string will be displayed in the
+                       TaskManager window.
+
+    On exit:    r1 = allocated area number
+                r3 = specified or allocated base address of area
+                r5 = specified or allocated maximum size of area
+                r0,r2,r4,r6-r8 preserved
+
+    An error will be returned if:
+
+    * the given area number clashes with an existing area, or
+
+    * the logical address space occupied by the area at maximum size would
+       intersect with any other area at maximum size, or
+
+    * there is not enough contiguous logical address space to create the
+       area, or
+
+    * there is not enough memory in the free pool to allocate level 2 page
+       tables to cover the area at maximum size.
+
+    If r1 is -1 on entry the RISC OS will allocate an area number itself.
+    This will be greater than or equal to 256. This means (see section 6.6)
+    that OS_ReadDynamicArea on these areas will always return with r2
+    being the maximum area size.
+
+    For singly mapped areas the base logical address is the lowest logical
+    address used by that area. The area grows by adding pages at the high
+    address end.
+
+    For doubly mapped areas the base logical address is the (fixed) boundary
+    between the two mappings: the first mapping ends at r3-1, and the second
+    starts at r3. When one of these areas grows the pages in the first copy
+    move down to accommodate the new pages at the end, and the second copy
+    simply grows at the end.
+
+    On entry, r6 points to the area handler routine which gets called with
+    various reason codes when an an area is grown or shrunk. If zero is
+    passed in, then no routine will be called, and any shrink or growth will
+    be allowed.
+
+    Details of the entry and exit conditions for this routine are given
+    in section 6.3.2 below.
+
+    The area is created initially with size zero (ie no pages assigned to
+    it), and is then grown to the size specified in size r2, which involves
+    the area handler being called in the same way as if   
+    OS_ChangeDynamicArea was called to grow the area.
+
+    The area is created with a maximum size equal to either the amount given
+    in r5 on entry, or the total RAM size of the machine, if this is
+    smaller. If r5 is -1 on entry, then the maximum size will be set to the
+    total RAM size of the machine.
+
+    If r3 on entry is -1, then RISC OS allocates a free area of logical
+    address space which is big enough for the maximum size of the area.
+
+    Once the area has been created Service_DynamicAreaCreate will be issued
+    to inform the rest of the system about this change.
+
+    Notes for application writers
+    -----------------------------
+
+    The following facilities:
+
+     * the ability to create areas with specific area numbers
+     * the ability to create areas at specific logical addresses
+     * the ability to create doubly-mapped areas
+
+    are intended for internal system use only.
+
+    Applications should in general create only singly-mapped areas, and
+    request that RISC OS allocate area numbers and logical addresses.
+
+    This will prevent clashes of area numbers or addresses.
+
+    6.3.1.2 Remove dynamic area
+    ---------------------------
+
+    This call removes a previously created dynamic area.
+
+    On entry:   r0 = 1 (reason code)
+                r1 = area number
+
+    On exit:    All registers preserved
+
+    An error is returned if the area was not removed for any reason.
+
+    Before the area is removed, RISC OS attempts to shrink it to zero size.
+    This is done using ChangeDynamicArea. If the ChangeDynamicArea returns
+    an error then the area will be grown back to its original size using
+    ChangeDynamicArea and the remove dynamic area call will return with an
+    error. If the ChangeDynamicArea to reduce the area to 0 size worked then
+    the area will be removed.
+
+    Once the area has been removed Service_DynamicAreaRemove will be issued
+    to inform the rest of the system about this change.
+
+
+    6.3.1.3 Return information on dynamic area
+    ------------------------------------------
+
+    This call returns various information on a dynamic area.
+
+    On entry:   r0 = 2 (reason code)
+                r1 = area number
+
+    On exit:    r2 = current size of area (in bytes)
+                r3 = base logical address of area
+                r4 = area flags
+                r5 = maximum size of area
+                r6 -> area handler routine
+                r7 = workspace pointer for area handler
+                r8 -> area description string (null terminated)
+
+    Note that for doubly-mapped areas, r3 on exit from this call returns the
+    address of the boundary between the first and second copies of the area,
+    whereas OS_ReadDynamicArea returns the start address of the first copy
+    (for backwards compatibility).
+
+    6.3.1.4 Enumerate dynamic areas
+    -------------------------------
+
+    On entry:   r0 = 3 (reason code)
+                r1 = area number or -1
+
+    On exit:    r1 = next area number or -1
+
+    This allows an application to find out what dynamic areas are defined.
+    -1 is used to start the enumeration and -1 indicates that the
+    enumeration has finished.
+
+
+    6.3.1.5 Renumber dynamic area
+    -----------------------------
+
+    This call renumbers a dynamic area.
+
+    On entry:   r0 = 4 (reason code)
+                r1 = old area number
+                r2 = new area number
+
+    On exit:    All registers preserved
+
+    An error is returned if the area specified by the old area number does
+    not exist or if the new number clashes with an existing area.
+
+    This call is intended for system use only.
+
+    Once the dynamic area has been renumbered Service_DynamicAreaRenumber
+    will be issued to inform the rest of the system about this change.
+
+
+    6.3.1.6 Read size of application space
+    --------------------------------------
+
+    This call returns the maximum size of application space
+
+    On entry:   r0 = 5 (reason code)
+
+    On exit:    r5 = maximum size of application space
+
+    This call is intended for system use only.
+
+
+
+  6.3.2 Dynamic area handler routine
+  ----------------------------------
+
+  This section describes the reason codes passed to the dynamic area handler
+  routine, with their entry and exit conditions.
+
+  This routine is called when the size of an area is being changed.
+
+  On entry, r0 contains a reason code, which describes what is happening.
+
+  It should be noted that when called, OS_ChangeDynamicArea is currently at
+  work and will reject requests to resize dynamic areas. As a consequence
+  any SWIs which might resize a dynamic area should be avoided. Such things
+  as OS_Module to claim some workspace are an example, and hence most file
+  operations should be avoided (although I/O on an existing file is more
+  safe than other operations).
+
+  The reason codes are as follows:-
+
+    6.3.2.1 PreGrow (0)
+    -------------------
+
+    This reason code is issued when a call to OS_ChangeDynamicArea results
+    in an area growing. It is called before any pages are actually moved. It
+    allows the handler to specify particular physical pages if it needs them
+    (eg for the screen area), or to object to the size change.
+
+    On entry:   r0 = 0 (reason code)
+                r1 -> Page block
+                        The physical page number entries will be set to -1
+                r2 = number of entries in Page block (= number of pages
+                      area is growing by)
+                r3 = number of bytes area is growing by (= r2 * pagesize)
+                r4 = current size of area (bytes)
+                r5 = page size
+                r12 -> workspace
+
+    On exit:    If the growth is OK, then
+                  r0 is preserved
+                  If particular physical page numbers are required then all
+                   the physical page number entries must be filled in with
+                   the required pages. The other entries must be left alone.
+                  V = 0
+                else
+                  r0 -> error to return, or zero to return generic error
+                  V = 1
+                endif
+                All other registers preseved
+
+    This call permits the dynamic area handler to request that specific
+    pages be used for growing the area. If this is the case then all pages
+    must be specified. The correspondence between the Page block and memory
+    is that the first entry in the page block corresponds to the lowest
+    memory address of the extension, and the last entry in the Page block
+    the highest memory address.
+
+    If an error is returned, then the area will not change size.
+
+    6.3.2.2 PostGrow (1)
+    --------------------
+
+    This reason code is issued when a call to OS_ChangeDynamicArea results
+    in an area growing. It is called after the PreGrow reason code has been
+    issued successfully and the memory pages have been moved. It provides
+    the handler with a list of which physical pages have been moved into the
+    area.
+
+    On entry:   r0 = 1 (reason code)
+                r1 -> Page block
+                       Only the physical page number entries are defined
+                r2 = number of entries in Page block (= number of pages
+                      area grew by)
+                r3 = number of bytes area grew by
+                r4 = new size of area (bytes)
+                r5 = page size
+                r12 -> workspace
+
+    On exit:    All registers preserved
+
+    6.3.2.3 PreShrink (2)
+    ---------------------
+
+    This reason code is issued when a call to OS_ChangeDynamicArea results
+    in an area shrinking. It is called before any pages are moved. It allows
+    the handler to limit the amount of memory moved out of the area, or to
+    object to the size change altogether. The shrink amount alowed as
+    returned by this reason code is permitted to be a non-page multiple. The
+    ChangeDynamicArea code will ensure the shrink permitted is rounded down
+    to a page multiple before it is actioned.
+
+    On entry:   r0 = 2 (reason code)
+                r3 = number of bytes area is shrinking by
+                r4 = current size of area (bytes)
+                r5 = page size
+                r12 -> workspace
+
+    On exit:    If shrink (even by reduced amount) is OK, then
+                  r0 preserved
+                  r3 = number of bytes area can shrink by. This must be less
+                        than or equal to r3 on entry.
+                  V = 0
+                else
+                  r0 -> error block, or zero to return generic error
+                  r3 = 0
+                  V = 1
+                endif
+                All other registers preserved
+
+    6.3.2.4 PostShrink (3)
+    ----------------------
+
+    This reason code is issued when a call to OS_ChangeDynamicArea results
+    in an area shrinking. It is always called after the PreShrink reason
+    code has been issued successfully even if the memory pages can't be
+    moved.
+
+    On entry:   r0 = 3 (reason code)
+                r3 = number of bytes area shrunk by
+                r4 = new size of area (bytes)
+                r5 = page size
+                r12 -> workspace
+
+    On exit:    All registers preserved
+
+
+  6.3.3 Sequence Of Actions When OS_ChangeDynamicArea Is Called
+  -------------------------------------------------------------
+
+  This section has been provided to give an overview of what happens when a
+  dynamic area's size is changed. This is presented as pseudo-code for
+  clarity.
+
+  Check IRQSemaphore - reject CDA if set
+
+  Growing free pool:
+  (no check for page availability - do as much as possible!)
+  Application space being shrunk - confirm it's OK:
+      If CAO in application space then
+          UpCall_MovingMemory (asks application if it consents to
+                memory move)
+          If UpCall *not* claimed Then reject CDA
+      Else
+          Service_Memory (asks modules for objectors to the memory
+                move)
+          If Service *is* claimed then reject CDA
+      EndIf
+  Move pages from application space end to free pool
+
+  Growing other dynamic area:
+      Check for page availability - if not enough available bounce CDA with
+          error
+      Table of pages prepared:
+          Allocates memory
+          Fills in -1s for 'any page'
+      PreGrow is called:
+          replaces -1s if it wants
+          objects about resize amount perhaps
+      Check for unavailable pages:
+          If there is a non -1 which can't be grabbed then reject CDA
+      Check for application space resizing:
+          If free pool < amount needed then
+              If CAO in application space then
+                  UpCall_MovingMemory (asks application if it consents to
+                        memory move)
+                  If UpCall *not* claimed Then reject CDA
+              Else
+                  Service_Memory (asks modules for objectors to the memory
+                        move)
+                  If Service *is* claimed then reject CDA
+              EndIf
+          EndIf
+      Page replacements determined:
+          Work out swap sequences on all non -1s
+          Replace all -1s with actual pages
+          Pages get grabbed first from the free pool, then underflowing into
+          the application space
+      Issue Service_PagesUnsafe (only if PreGrow specified pages)
+      Pages get moved around:
+          Do the page moving/swapping (don't swap if pages requested are in
+                free pool)
+      Issue Service_PagesSafe (only if PreGrow specified pages)
+      PostGrow is called:
+          Sorts out structures for the new size
+
+  Shrinking free pool:
+  Check if application space OK to grow:
+      If application space < maximum then
+          If CAO in application space then
+              UpCall_MovingMemory (asks application if it consents to
+                    memory move)
+              If UpCall *not* claimed Then reject CDA
+          Else
+              Service_Memory (asks modules for objectors to the memory
+                    move)
+              If Service *is* claimed then reject CDA
+          EndIf
+      EndIf
+  Move pages from free pool to application space
+
+  Shrinking other dynamic area:
+      PreShrink is called:
+          objects about resize amount perhaps, or gives larger allowed size
+          Sorts out structures for the new smaller size as the shrink
+                  will definitely go ahead.
+      Pages get moved around:
+          Move pages from dynamic area to free pool
+      PostShrink is called:
+          Keep subsystem informed.
+
+
+
+  It should be noted that the system stack is used for the page structure
+  passed to the PreGrow routine. As a consequence there is a limit to the
+  amount that an area can be grown by at one time. To get round this problem
+  an area grow request of a large amount will be performed in several
+  steps. If one of these steps fails then the grow will terminate early with
+  the area grown by however much was achieved, but not by the full amount
+  requested.
+
+  You will notice two new service calls were used here:
+  Service_PagesUnsafe
+  Service_PagesSafe
+  which are issued around page swapping to inform any DMA subsystems (eg
+  IOMD DMA or second processor) that some pages are being swapped around.
+  Here is the detailed description of these service calls:
+
+    6.3.3.1 Service_PagesUnsafe
+    ---------------------------
+
+    On entry:   r1 = Service_PagesUnsafe
+                r2 = Page block filled in by the PreGrow routine with the
+                        two address fields filled in too.
+                r3 = number of entries in Page block
+
+    On exit:    All registers preserved
+
+    The recipient of this service call is being told that the pages
+    specified are about to be swapped around. Direct memory access
+    activities involving the specified pages should be suspended until
+    Service_PagesSafe has been received indicating the pages are safe.
+
+
+    6.3.3.2 Service_PagesSafe
+    -------------------------
+
+    On entry:   r1 = Service_PagesSafe (&8F)
+                r2 = Number of entries in each Page block
+                r3 -> Page block before move
+                r4 -> Page block after move
+
+    On exit:    All registers preserved
+
+    The recipient of this service call is being told that the pages
+    specified have been swapped for different pages and what those different
+    pages are. Note that the logical addresses in both Page blocks will
+    match. The 'before' Page block will contain the physical page numbers
+    and physical addresses of the pages which were replaced, and the 'after'
+    block the page numbers and physical addresses of the pages which
+    replaced them.
+
+ 6.3.4 Implementation Notes for OS_ChangeDynamicArea
+ ---------------------------------------------------
+
+There is an issue with OS_ChangeDynamicArea when a particular page is
+requested by the growing dynamic area which is currently in use by the page
+tables. The problem is that moving pages that themselves control where the
+pages are is a tricky operation. This is exacerbated on level 1 page tables
+even more because these are 16k (4 pages) big and must be 16k aligned. This
+means that if a level 1 page table needs moving then another 4 page block
+needs to be found - potentially resulting in more page swapping to make such
+a gap. Level 2 page tables don't have this problem as they're 4k (1 page)
+big.
+
+Having said this, unless some mobility is permitted the minimum
+configuration which would permit a '486 second processor to work would be
+4MBytes. In a 2MByte machine 1M is left free of page tables to allow the
+screen to grow, and the 2nd MByte would, as a consequence, be 'contaminated'
+with page tables and so would be unavailable for the 2nd processor. If the
+page tables could be moved they could reside in the 'screen' MByte as they
+could be moved freely about if the screen needed to grow.
+
+ 6.3.5 OS_DynamicArea Service Calls
+ ----------------------------------
+
+  These service calls are designed to keep the rest of the system informed
+  about changes to the dynamic areas. Their primary customer is the task
+  manager, although other modules could make use of them.
+
+  6.3.5.1 Service_DynamicAreaCreate (&90)
+  ---------------------------------------
+
+  On entry:      r1 = Service_DynamicAreaCreate (&90)
+                 r2 = area number of area just created
+
+  On exit:       All registers preserved
+                 This service must not be claimed
+
+  This service is issued just after the successful creation of a dynamic
+  area.
+
+
+  6.3.5.2 Service_DynamicAreaRemove (&91)
+  ---------------------------------------
+
+  On entry:      r1 = Service_DynamicAreaRemove (&91)
+                 r2 = area number of area about to be removed
+
+  On exit:       All registers preserved
+                 This service must not be claimed
+
+  This service is issued just before the removal of a dynamic
+  area. It is issued during a call to OS_DynamicArea(1), after the area has
+  been successfully reduced to zero size, but before it has been removed
+  completely.
+
+
+  6.3.5.2 Service_DynamicAreaRenumber (&92)
+  -----------------------------------------
+
+  On entry:      r1 = Service_DynamicAreaRenumber (&92)
+                 r2 = old area number
+                 r3 = new area number
+
+  On exit:       All registers preserved
+                 This service must not be claimed
+
+  This service is issued during a call to OS_DynamicArea(2), ie when an area
+  is being renumbered.
+
+
+ 6.4 New SWI: OS_Memory
+ ----------------------
+
+This SWI performs miscellaneous operations for memory management.
+
+On entry:   r0 = reason code and flags. Bits o-7 are the reason code, bits
+                8-31 are the flags which may be specific to the reason code.
+            The other registers are specific to the reason code.
+
+On exit:    The returned values are specific to the reason codes.
+
+Here are the defined reason codes:
+
+0-5: Page block Operations
+0 - General Page block Operation
+1-5 - reserved
+
+6-8 - physical memory:
+6 - read physical memory arrangement table size
+7 - read physical memory arrangement table
+8 - read amounts of various sorts of memory
+
+9-? - I/O space information:
+9 - read controller presence
+
+The details of these are given below.
+
+   6.4.1 OS_Memory Reason 0: General Page block Operation
+   ------------------------------------------------------
+
+   This reason code is used to convert between representations of memory
+   addresses. The different memory spaces are logical memory, physical
+   memory and physical pages.
+
+   On entry:   r0 = flags:
+                    bit     meaning
+                    0-7     reason code (0-5)
+                    8-9     which entry is defined in the Page block:
+                            0 - Physical page number
+                            1 - Logcial address
+                            2 - Physical address
+                    10      Physical page number will be filled in when set
+                    11      Logical address will be filled in when set
+                    12      Physical address will be filled in when set
+                    13-14   Cachability control:
+                            0 - no change
+                            1 - no change
+                            2 - disable caching on these pages
+                            3 - enable caching on these pages
+                    15-31   reserved - set to 0
+               r1 -> Page block
+               r2 = number of entries in page block
+
+   On exit:    Page block updated as necessary
+
+   The Page block will be scanned and the specified operations applied to
+   it. It is possible to do address conversions and control the cachability
+   on a per-page basis. If any page is found to be unconvertable or
+   non-existent then an error will be returned and the cachability will be
+   unaffected. Cachability is accumulated for each page. So, for example, if
+   there are 5 clients which need caching turned off on a page then each of
+   them must turn caching back on individually for that page actually to
+   become cached again.
+
+   Where an ambiguity may occur, for example in doubly-mapped areas such as
+   the screen, one of the possible results will be chosen and filled in.
+
+   This will only handle RAM addresses. The address fields may be non-page
+   aligned.
+
+   6.4.2 OS_Memory Reasons 6-8: Physical Memory
+   --------------------------------------------
+
+   These are provided to enable a program to find out what physical
+   memory there is and its arrangement. The first two calls provide
+   complete information on the available memory. The information is provided
+   in the form of a table, with each page of physical memory space having
+   one entry in the table. Due to the large number of pages the table is
+   packed down to only 4 bits per page. In each byte of the table the low
+   order 4 bits correspond to the page before the high order 4 bits, ie it
+   is little-endian. This is the meaning of a nibble in the table:
+
+        bit     meaning
+        0-2     type of memory:
+                0       not present
+                1       DRAM
+                2       VRAM
+                3       ROM
+                4       I/O
+                5-7     Undefined
+        3       0 - Page available for allocation
+                1 - Page not available for allocation
+
+   The page availability is based on whether it is RAM, and whether it has
+   already been allocated in such a way that it can't be replaced with a
+   different RAM page eg the OS's page tables or screen memory.
+
+   The third call gives a summary of available memory.
+
+
+     6.4.2.1 Read Physical Memory Arrangement Table Size
+     ---------------------------------------------------
+
+     On entry:   r0 = 6 (bits 8-31 clear)
+
+     On exit:    r1 = table size (bytes)
+                 r2 = page size (bytes)
+
+     This returns information about the memory arrangement table.
+
+
+     6.4.2.2 Read Physical Memory Arrangement Table
+     -----------------------------------------------
+
+     On entry:   r0 = 7 (bits 8-31 clear)
+                 r1 = pointer to table to be filled in
+
+     On exit:    registers preserved
+
+     This returns the physical memory arrangement table in the block of memory
+     pointed at by r1. Note the information about page availability may well
+     change between this being called before a OS_ChangeDynamicArea and the
+     PreGrow routine being called, in particular it may have been necessary
+     for OS_ChangeDynamicArea to allocate level 2 page tables for the grown
+     area, and these are not available for allocation. Hence, for
+     applications which require, say, all pages from physical address 0
+     onwards their PreGrow handler must make this call, rather than the
+     information being extracted and held before OS_ChangeDynamicArea being
+     called.
+
+
+     6.4.2.3 Read Amounts Of Various Sorts Of Memory
+     -----------------------------------------------
+
+     On entry:   r0 = bits   meaning
+                      0-7    must be 8 - its the reason code
+                      8-11   the type of memory:
+                             1 - DRAM
+                             2 - VRAM
+                             3 - ROM
+                             4 - I/O
+                      12-31  reserved - set to 0
+              
+     On exit:    r1 = number of pages of that sort of memory
+                 r2 = page size (in bytes)
+
+
+   6.4.3 I/O Space Information
+   ---------------------------
+
+   These give information about the I/O space. Controllers are identified by
+   type and sequence number so that a machine could be constructed with,
+   say, more than one IDE controller in it.
+
+     6.4.3.1 Read Controller Presence
+     --------------------------------
+
+     On entry:   r0 = 9 (bits 8-31 clear)
+                 r1 = controller ID
+                      bit       meaning
+                      0-7       controller sequence number
+                      8-31      controller type:
+                                0 - EASI card access speed control
+                                1 - EASI space
+                                2 - VIDC1
+                                3 - VIDC20
+
+     On exit:    r1 = controller base address or 0 if not present.
+
+     This returns the location of a controller on the given machine. For
+     example the EASI space gives the base address of podule N where N is
+     the sequence number given. This reason code is provided for internal
+     use only and is documented here for completeness' sake. In particular
+     you must use the Podule manager to get at this information and to
+     control your podule's EASI space access speed.
+
+ 6.5 Old SWI OS_SetMemMapEntries (&53)
+ -------------------------------------
+
+ As noted in the RISC OS 3 PRMs -1 should be used to indicate that a page
+ should become inaccessible, for future compatibility. In the Medusa kernel
+ the future has arrived - only -1 will work!
+
+ 6.6 Old SWI OS_ReadDynamicArea (&5C)
+ ------------------------------------
+
+ As noted in the RISC OS 3 PRMs if bit 7 of the dynamic area number is set
+ then r2 will be returned with the maximum area size. This is being changed
+ slightly to be that if the dynamic area number passed in is greater than or
+ equal to 128 then r2 will be returned as the dynamic area size. Also, if
+ the dynamic area number passed in is between 128 and 255 inclusive then the
+ information will be returned for the area whose number is 128 less than the
+ passed-in value. The net result is that for old dynamic area numbers (0-5)
+ the functionality is unchanged, but the number-space impact of the
+ interface is minimised - it was prety horrible to have to force bit 7 of
+ all dynamic area numbers to be clear just for this SWI, so we just prohibit
+ a small patch of low numbers instead.
+ 
+ 6.7 New SWI OS_ClaimProcessorVector
+ -----------------------------------
+
+
+ In
+   r0=Vector and flags
+         bit     meaning
+         0-7     Vector number:
+                 0 - 'Branch through 0' vector
+                 1 - Undefined instruction
+                 2 - SWI
+                 3 - Prefetch abort
+                 4 - data abort
+                 5 - address exception (only on ARM 2 & 3)
+                 6 - IRQ
+                 7+ - reserved for future use
+         8       0=release, 1=claim
+         9-31    reserved, must be 0
+   r1=replacement value
+   r2=value which should currently be on vector (only needed for release)
+ Out
+   r1=value which has been replaced (only returned on claim)
+
+ This SWI provides a means whereby a module can attach itself to one of the
+ processor's vectors. This is a direct attachment - you get no environment
+ except what the processor provides. As such, claiming and releasing the
+ vectors is somewhat primitive - the claims and releases must occur in the
+ right order (the release order being the reverse of claim order).
+
+ On release if the value in r2 doesn't match what is currently on the vector
+ then an error will be returned. This ensures correct chaining of claims and
+ releases.
+
+ [ Implementation note:
+ On break the 1st ROM instruction gets copied to location 0 and branched to
+ (MOV pc, #0). This used to be a branch direct (B thing) instruction, but
+ will be changed to a branch indirect (LDR pc, [pc, #thing]). This means the
+ indirection vector must be filled in with the correct reset address. Hence,
+ the reset vector must be a different indirection vector to the 'Branch
+ through 0' one.
+ ]
+
+
+
+ 7. External Dependencies
+ =========================
+
+The development of the new memory management system relies upon the
+availability of ARM600 (or 610) processor boards for A540s.
+
+The Window Manager will need modification to cope with the changed memory
+management system. In particular it will need to:
+        Cope with the free pool being external to the wimp
+        Change how it does TransferBlock operations
+        Change how it gets the pages for new tasks
+
+
+ 8. Development Test Strategy
+ =============================
+
+PHTester scripts will be writen to exercise the following SWIs:
+OS_Memory
+ All reasons (0-9)
+In range, edge of range, just outside range and wildly out of range values
+will be tried. Where buffer contents are returned the buffer will be output.
+
+OS_AbortTrap
+Where this implemented then the test strategy would be as follows. Due to the
+nature of this SWI unsupported PHTester scripts will not be able to do the
+job properly. A piece of support code will be written which can be loaded
+into RMA and will monitor calls to the abort trapper. A PHTester script will
+be written which will load this trapper, add it, exercise it and monitor the
+results. The trapper will then be removed twice to make sure the correct
+behaviour results.
+
+OS_ChangeDynamicArea
+Again, this is hard to test with straight PHTester scripts. Under normal
+circumstance the presence of PHTester running will prohibit dynamic areas
+growing, however, this can be got around by growing a dynamic area before
+PHTester starts, starting the script then shrink the dynamic area thus
+giving the system a free pool (PHTester will refuse to allow the application
+space to resize and thus the memory will be pushed into the free pool).
+Support code will be writen which will monitor service calls and upcalls and
+will exercise the various cases. This act of exercising must be done without
+PHTester running.
+
+OS_DynamicArea
+Again, support code will be needed for PHTester to be usable. With this
+support code PHTester scripts will be writen to add (twice) and remove
+(twice) a dynamic area. This area will then be resized testing success and
+failure of both grow and shrink, failing because of either not enough memory
+or unavilable specific pages being requeested. A straight PHTester script
+will be used to exercise enumeration, readinfo and renumber operations.
+
+
+
+Testing the Wimp and Switcher
+
+Wimp:
+
+SWI Wimp_SlotSize
+
+The behaviour of this SWI depends on a number of external (To the task and calling parameters) factors:
+        - Environment, particularly MemoryLimit and ApplicationSpace
+        - Machine state, eg. remaining amount of memory
+        - Page size, to compare with ARM 3 systems units of 32K should be used
+
+Shrinking a slot:
+        Needs to be tested when MemLimit <> AppSpace, when MemLimit=AppSpace < RealSpace
+        where RealSpace is actual memory mapped in- This feature is used extensively by flex.
+        When a shrink succeedes, the free pool should grow by the shrinkage and the RMA by a roughly
+        proportional ammount (for slot being reduced)
+
+Growing a slot:
+        Again Different environments need testing, but also effects near total memory usage:
+        Eg. A large increase is requested, but only a smaller amount is available. Also when the 
+        area has been grown, there may not be enough RMA to extend the Memory Map slot and so the
+        slot shrinks by a page.
+
+In both cases the SWI should return with the environment set to the real size and a message sent
+to the switcher.
+
+Application testing.
+        As mentioned above, flex makes good use of the features of this SWI. Loading a file into !Draw
+or !Paint should make the slot grow by a comparable amount to the file size (taking into account the
+internal storage methods). When the document is discarded the slot should reduce to the value it was 
+before (unless flex/malloc usage makes this impossible) and total RMA/system memory should be roughly
+what it was, though of course for a first document load the application may claim additional memory.
+        A small application could be written to do similar things repeatedly and could this give a
+measure to memory leaks etc. Medusa is bound to be slower because of smaller page size, more pages (which
+need to be scanned when growing a slot) and Log-Phys map (Level 2 table) being in RAM rather than on
+chip.
+
+SWI Wimp_TransferBlock
+
+Many possible cases:
+        Should always be possible even if no free memory.
+        For tasks a,b,c (c is current task)
+        a <-> b         a <-> c         c <-> c
+                For small and large lengths     (eg. 1K or 8Meg)
+                For when c takes up all/most of app space
+                For when transfers occur to/from non application space (eg. RMA)
+                For non word aligned/ size transfers
+                For transfers involving over-the-page memory, eg. 34k-38k (32-36,36-40 pages)
+        
+Application testing.
+        RAM transfer from !Paint to !Draw (this is c->a,small, prob aligned)
+        Use of !SqlFiler and !Faults (this c(RMA)->a,small, prob aligned)
+        Some combinations can only be tested on real Medusa's with 32Meg memory.
+
+SWI Wimp_ClaimFreeMemory
+        Only claimable once at a time, memory should be accessible and remain intact until free'd
+        (incidentally can be freed by any task).
+
+System testing
+        *copy, *compact ?
+
+Mode changing:
+
+SWI Wimp_SetMode, *wimpmode
+        numbers 0-255 as before (testable by script which then checks screen memory usage and VDU
+variables) and new style configuration (again script can work out from config the mem usage and VDU
+vars). Wimp should shrink screen memory as much as possible and send mode changed message to all tasks
+
+Switcher:
+        Dynamic areas used to be scanned (finding addresses for all the system pages) now RDA is used.
+Dynamic areas (all, including app-created areas) should update when window is open and a task calls
+CDA successfully. Areas which are non-fixed should be draggable, should reflect/update area as seen
+by a task. Dragging bars should update memory text as appropriate, on medusa platforms with more than 
+4Meg this should be logarithmic for large values. Switcher should behave as before on non medusa platforms.
+
+
+
+ 9. Organisation
+ ================
+
+ The software described herein resides in the RISC OS ROM.
+
+ 10. Future Enhancements
+ =======================
+
+Some sort of system to request memory to be freed in cases of memory
+shortage might be nice.
+
+OS_Heap to be extended to allow allocation of anchored moveable blocks. This
+would be used to prevent heap fragmentation.
+
+ 10.1 New SWI: OS_AbortTrap (&67)
+ --------------------------------
+
+ This has been moved to the future enhancements section as this is not
+ required by the project specification, there is insufficient time to do it
+ and it can be added later if needed. Also, there are some unresolved issues
+ regarding handling aborts of writes to &00-&1f and handling of coprocessor
+ data transfer operations.
+
+  10.1.1 Technical background
+  ---------------------------
+
+  The extra processor modes in the ARM6 and ARM7 processor cores allow for
+  complete recovery from data aborts, even if the abort happens while in
+  privileged modes, such as supervisor mode. This was not possible on
+  earlier processors, because when the abort was taken, the return address
+  (and condition codes) was placed in r14_svc, thereby corrupting it.
+
+  This allows for (amongst other things) complete software emulation of
+  hardware devices (apart from devices which cause interrupts, or which are
+  very time-critical). In particular, it becomes possible to write a module
+  which emulates VIDC1 on systems which are VIDC20 based (provided that the
+  address map is set up so that the address where VIDC1 is accessed
+  (&03400000) causes a data abort when written to).
+
+  In order to facilitate this kind of activity, RISC OS allows a module to
+  provide a trap routine for accesses within specified address ranges. This
+  is an additional feature over and above the usual abort handlers which are
+  owned by the current application.
+
+  When a data abort happens, the OS works out the range of addresses
+  accessed by the instruction.
+
+  It then looks through its MMU page tables, and splits the range into
+  sub-ranges which correspond to accesses within one page or section, as
+  appropriate.
+
+  For each sub-range, it then checks the access privileges to see if the
+  access was valid. If so, it performs the access itself. (For a load, it
+  transfers the data into a temporary stack frame from which the register(s)
+  will be subsequently loaded. For a store, the stack frame is already set
+  up with the register(s) to be stored.)
+
+  If the sub-range corresponds to a page or section which would have caused
+  an abort, the sub-range is then checked against its list of abort traps.
+  Starting with the lowest address in the sub-range, if the address is
+  contained within any of the address ranges specified in the list, then
+  the corresponding routine is called. The registers on entry and on exit
+  are as follows:-
+
+  On entry:     r0 = flags
+                        bits 1,0 = 00 undefined
+                                   01 store
+                                   10 load
+                                   11 SWP
+
+                        bit 2 = 0 => user mode access
+                                1 => privileged mode access
+
+                        bits 3..31 undefined
+
+                r1 -> block of registers to be transferred to/from
+                r2 = lowest address which faulted in the specifed address range
+                r3 = number of transferred bytes which fall within the specified address range
+                r4 -> instruction which aborted
+                r12 = workspace ptr specified in node
+                SVC26 (at the moment - I might change this to SVC32 at some stage)
+
+  On exit:      VC => instruction processed, please complete writeback
+                VS => instruction shouldn't be allowed - generate exception
+                All registers preserved (apart from PSR)
+
+  If the routine wishes to accept the transfer as valid, it must perform the
+  transfer itself by transferring the data (of length r3 bytes) in or out of
+  the block pointed to by r1. (In the case of the SWP instruction it must
+  transfer data out of, then into the block). It should then exit with V=0.
+
+  The OS will then advance its address pointer by the number of bytes
+  processed by this routine, and continue with the next sub-range (if there
+  are any more to do).
+
+  If the routine wishes to fault the transfer after all, it should exit with
+  V=1. This will cause the OS to call the normal data abort handler. (This
+  also happens if an address in a sub-range which faulted does not lie in
+  any address range on the list).
+
+  Note: this behaviour currently precludes having two or more abort traps on
+  the list which have overlapping address ranges, where each trap actually
+  is interested in a subset of accesses within that range. At the moment,
+  the OS faults if any trap routine exits VS, whereas it could carry on down
+  the list looking for further trap ranges containing the address. But then
+  you would have to have some way of returning saying you had done part of
+  the transfer.
+
+  In addition to trapping page and section faults, the OS has special code
+  to deal with writes to addresses in the range &00 to &1F inclusive, which
+  cause aborts on ARM6/7 when the processor is executing in a 26-bit PC mode
+  (assuming we are in the 32-bit PC configuration, which we are).
+
+  If the address range for the transfer includes an address in the range
+  0-&1F, and the aborting instruction is within the RISC OS "ROM" image,
+  then RISC OS executes the transfer itself (in 32-bit PC mode), as ROM code
+  is considered kosher. This is mainly to allow FIQ claimants such as ADFS
+  floppy drivers or Econet to set up their FIQ code while executing in
+  26-bit PC mode. It also allows the FPE to set up the undefined instruction
+  vector (although of course the FPE still has to know that it will get
+  called in undef_32 mode).
+
+  If the aborting instruction is not in the ROM image, then the usual abort
+  list is checked. If however the address is not in the list, or the routine
+  exits with V set, then instead of calling the current data abort handler,
+  the OS calls its default data abort handler (this was so I could then do a
+  *ShowRegs and find out where the program went bang, rather than have the C
+  exception handler swallow it up).
+
+  Note that if you set up a node to respond to vector poking, and you want
+  to accept the transfer, you'll have to switch to SVC32 to execute it (this
+  is why I may change the entry to be called in SVC32.
+
+  10.1.2 New SWI: OS_AbortTrap (&66)
+  ----------------------------------
+
+  SWI OS_AbortTrap provides calls to add or remove abort trap routines.
+
+  On entry, r0 provides a reason code which determines which operation is
+  performed.
+
+    10.1.2.1 Add abort trap (0)
+    ---------------------------
+
+    This reason code adds a trap routine to RISC OS's list.
+
+    On entry:   r0 = 0 (reason code)
+                r1 = lowest trapped address
+                r2 = highest trapped address +1
+                r3 = address of trap routine
+                r4 = workspace pointer for trap routine
+
+    On exit:    All registers preserved
+
+    The entry and exit conditions for the trap routine are described in
+    section x.y.z.
+
+    10.1.2.2 Remove abort trap (1)
+    ------------------------------
+
+    This reason code removes a trap routine from RISC OS's list.
+
+    On entry:   r0 = 1 (reason code)
+                r1 = lowest trapped address
+                r2 = highest trapped address +1
+                r3 = address of trap routine
+                r4 = workspace pointer for trap routine
+
+    On exit:    All registers preserved
+
+    Registers r1 to r4 on entry should be identical to those previously
+    passed to reason code 0.
diff --git a/Doc/5thColumn/Manual b/Doc/5thColumn/Manual
new file mode 100644
index 0000000000000000000000000000000000000000..49e9e68f415959d99f2e37ea9452f1f58d7f0574
--- /dev/null
+++ b/Doc/5thColumn/Manual
@@ -0,0 +1,323 @@
+; > 5thColumn
+
+ RISC OS Support for extension ROMs
+ ==================================
+
+ Author:        Tim Dobson
+ Status:        Draft
+ Issue:         0.03
+ History:
+
+  Date          Revision        Changes
+
+  11-Oct-90     0.00            Started
+  16-Oct-90     0.01            Completed first draft
+  08-Feb-91     0.02            Updated to reflect reality
+  23-Apr-91     0.03            Added note about directly executable
+                                 extension ROMS
+
+This document describes the enhancements to RISC OS to support extension (or
+"5th column") ROMs.
+
+Extension ROMs are ROMs fitted in addition to the main ROM set, which
+provide software modules which are automatically loaded by RISC OS on
+power-on.
+
+The availability, size and number of extension ROM sockets depends on which
+type of RISC OS computer you are using.
+
+In general, however, RISC OS recognises extension ROMs or ROM sets which are
+8, 16 or 32 bits wide, provided the ROM adheres to the specification below.
+
+32 bit wide extension ROM sets are directly executable in place, saving on
+user RAM. 8 or 16 bit wide sets have to be copied into RAM to execute.
+
+ Creating an extension ROM
+ =========================
+
+Extension ROMs appear in the ROM area of the memory map, ie between
+&03400000 and &03FFFFFF. An extension ROM set must end on a 64K boundary or
+at the start of another extension ROM. This is normally not a problem as it
+is unlikely you would want to use a ROM smaller than a 27128 (16K), and the
+normal way of addressing this would mean that the ROM would be visible in 1
+byte out of each word, ie within a 64K addressable area.
+
+Extension ROMs have a header at the end of the ROM image which indicates the
+presence of a valid extension ROM. The header is at the end because RISC OS
+scans the ROM area downwards from the top.
+
+At the end of each ROM set of size 'n' bytes must be a 16-byte header as
+follows:-
+
+        Byte address    Contents
+
+        n-16            1-word size field containing n
+        n-12            1-word checksum (bottom 32 bits of the sum of all
+                         words from addresses 0 to n-16 inclusive)
+        n-8             2-word id "ExtnROM0" indicating a valid extension
+                         ROM, ie
+
+        n-8             &45     ; "E"
+        n-7             &78     ; "x"
+        n-6             &74     ; "t"
+        n-5             &6E     ; "n"
+        n-4             &52     ; "R"
+        n-3             &4F     ; "O"
+        n-2             &4D     ; "M"
+        n-1             &30     ; "0"
+
+Note that the ROM header will not necessarily appear in the memory map in
+the last 16 bytes if the ROM set is 8 or 16 bits wide. In the 8-bit case,
+the header will appear in one of the four byte positions of the last 16
+words, and in the 16-bit case, in one of the two half-word positions of the
+last 8 words. However, RISC OS copes with this.
+
+Extension ROMs also have a header at the *start* of the ROM set, which is
+identical to the format of an expansion card's identity space. This is
+because the Podule manager module handles much of the extension ROM
+processing.
+
+The format of the header at the start is as follows:-
+
+        Byte address    Contents        Meaning
+
+        0               &00             Extended expansion card identity,
+                                         not requesting IRQ or FIQ
+        1               &03             bit 0 set => there is a chunk
+                                         directory
+                                        bit 1 set => interrupt status
+                                         pointers defined (necessary because
+                                         bit 0 is set)
+                                        bits 2,3 = 0 => 8-bits wide (NB this
+                                         should be set to 0 irrespective of
+                                         the actual width of the ROM set)
+        2               &00             Reserved, must be zero
+        3               &87             Product type (lo-byte)
+        4               &00             Product type (hi-byte)
+                                         See below
+        5               Manuf(lo)       Manufacturer code (lo-byte)
+        6               Manuf(hi)       Manufacturer code (hi-byte)
+                                         See below
+        7               Country         Country code - see below
+
+        8 to 15         &00             Interrupt status pointers (extension
+                                         ROMs do not generate interrupts!)
+        16 onwards      Chunk directory - see below
+
+Product type code: Note that &0087 has been allocated as a product type code
+for all extension ROMs.
+
+Manufacturer code: All manufacturers of expansion cards and/or extension
+ROMs should have a code for manufacturer. If you have not already been
+allocated one, you should consult Acorn.
+
+Country code: Every extension ROM should have a code for the country of
+origin. These match those used by the International module, except that the
+UK has a country code of 0 for expansion cards and extension ROMs. If you do
+not already know the correct country code for your country, you should
+consult Acorn.
+
+The chunk directory in an extension ROM is identical to that in an expansion
+card - see the chapter "Expansion Cards: Technical Details" in the RISC OS
+Programmer's Reference Manual.
+
+ Note
+ ====
+
+In extension ROMs which are directly executable (ie which are 32 bits wide),
+the word immediately preceding the start of each module must contain (size
+of module +4), ie an offset from itself to the first word after the module.
+It is recommended that all extension ROMs be created like this, irrespective
+of whether they are directly executable.
+
+ Additional interfaces to support extension ROMs
+ ===============================================
+
+ Changes to Podule manager
+ =========================
+
+The Podule manager module is responsible for recognising extension ROMs,
+although it is the kernel which is responsible for loading modules contained
+in them.
+
+The numbering scheme for expansion card slots has been extended to include
+extension ROMs. The numbers for extension ROMs are -2, -3, -4... (-1 is
+reserved for the main ROM, although the Podule manager itself does not
+accept -1 for any of its SWI calls).
+
+All Podule manager SWIs which take an expansion card slot number as a
+parameter allow an extension ROM specifier instead.
+
+The SWIs Podule_ReadID, Podule_ReadHeader, Podule_EnumerateChunks,
+Podule_ReadChunk operate as one would expect when presented with an
+extension ROM specifier.
+
+The SWIs Podule_ReadBytes, Podule_WriteBytes, Podule_CallLoader will
+normally fail because extension ROMs have no code space or loader.
+
+SWI Podule_RawRead will read successive bytes out of the extension ROM,
+taking the ROM width into account.
+
+SWI Podule_RawWrite must not be used with an extension ROM specifier, as
+writing to the ROM area can reprogram the memory and video controllers.
+
+SWI Podule_HardwareAddress returns the base address of the specified
+extension ROM, although this is not in general useful as the ROM width can
+vary.
+
+ New SWIs
+ --------
+
+SWI Podule_EnumerateChunksWithInfo
+
+ in:    R0 = chunk number (zero to start)
+        R3 = expansion card slot number or extension ROM number
+
+ out:   R0 = next chunk number (zero if final chunk enumerated)
+        R1 = size (in bytes) if R0<>0 on exit
+        R2 = operating system identity byte if R0<>0 on exit
+        R4 = pointer to a copy of the module name if the chunk is a relocatable module, else preserved
+        R5 = pointer to a copy of the module's help string if the chunk is a relocatable module, else preserved
+        R6 = address of module if the chunk is a directly executable relocatable module
+             or 0 if the chunk is a non-directly-executable relocatable module
+             else preserved
+
+SWI Podule_HardwareAddresses
+
+ in:    R3 = expansion card slot number or extension ROM number
+
+ out:   R0 = raw hardware address
+        R1 = combined hardware address
+
+For an expansion card, the "raw hardware address" is the base address of the
+expansion card, and the "combined hardware address" is the raw hardware
+address (in bits 12-25) combined with the base address of the expansion
+card's private CMOS RAM (in bits 0-11) (as returned by SWI
+Podule_HardwareAddress).
+
+For an extension ROM, the two addresses are the same, and are the start
+address of the extension ROM (ie the address of the first byte of the ROM).
+
+ Star commands
+ -------------
+
+*Podules now displays the extension ROMs in the system as well as expansion cards.
+
+ Changes to kernel
+ =================
+
+ SWI OS_Module
+ -------------
+
+OS_Module 17 (Add expansion card module) - This call now allows R3 to be
+either an expansion card slot number or an extension ROM number.
+
+OS_Module 19 (Enumerate ROM modules) - This call now enumerates
+over all ROM sections, ie extension ROM modules as well as main ROM and
+expansion card modules. R2 on entry now specifies the ROM section number to
+start scanning from, with the order of enumeration as follows:-
+
+-1 (main ROM), 0, 1, 2, 3 (expansion cards), -2, -3, -4,... (extension ROMs)
+
+Edition 1 of the PRM is incorrect when it states that on exit R1 (the
+module number to scan from) is incremented and R2 (the expansion card number
+to scan from) is preserved.
+
+In fact R1 returns the module number of the found module plus one, where
+modules are numbered from zero within each ROM section, and R2 returns the
+ROM section number of the found module, which may be in a different ROM
+section from the value passed in R2 on entry, if there are insufficient
+modules in the specified section.
+
+The values returned in R1 and R2 are therefore set up for the next call to
+OS_Module 19.
+
+The call returns the error "No more modules" (error number &107) if there
+are no more modules from the point specified in the ordering.
+
+ New call
+ --------
+
+OS_Module 20 (Enumerate ROM modules with version)
+
+This call is identical to OS_Module 19, except that on exit R6 holds a BCD
+(binary coded decimal) form of the module's version number, as derived from
+the module's help string. The top 16 bits of this value hold the integer
+part of the version number, and the bottom 16 bits hold the fractional part,
+eg if the version number of the module is "3.14" then the value returned
+would be &00031400.
+
+ Module initialisation
+ ---------------------
+
+The way in which the kernel initialises modules has been changed. If there
+is more than one version of the same module present in the ROM (which
+includes all ROM sections) then only the newest version of the module is
+initialised, where newest means the version with the highest version number.
+(If there are two copies of the same version, then directly executable
+versions (ie in main ROM or in a 32-bit wide extension ROM) are considered
+"newer". If they are equal in this respect, then the later one in scanning
+order is considered to be newer.)
+
+The kernel first scans down the list of modules in the main ROM. For each
+module in this list, the kernel initialises the newest version of that
+module.
+
+For each module in the main ROM, the newest version of that module
+ If an extension ROM contains a newer version of a module in the
+main ROM, then the newer version will be initialised at the point in the
+initialisation sequence where the main ROM version would have been
+initialised. This allows main ROM modules to be replaced without the
+problems associated with initialisation order.
+
+The kernel then applies the same rule to all of the expansion cards in turn.
+In each case the newest version of the module is initialised, but with the
+hardware address (in R11) corresponding to that of the expansion card.
+
+The kernel finally initialises any extension ROM modules that are the newest
+versions, but which have not already been initialised in lieu of a module in the
+main ROM or on an expansion card.
+
+ Star commands
+ -------------
+
+*ROMModules now displays the version number of each module, as well as the
+other information. Extension ROM modules are now included in the list. Note
+that extension ROMs are numbered 1, 2, 3... in this command - these
+correspond to ROM section numbers -2, -3, -4... respectively.
+
+*Unplug can now unplug extension ROM modules, as well as modules in the main
+ROM or in expansion cards. The syntax is now
+
+        *Unplug [<moduletitle> [<ROM section number>]]
+
+*Unplug with no parameters does the same as it used to, ie display any
+unplugged modules.
+
+*Unplug with a module name but no ROM section number specified unplugs all
+versions of that module in the system, and kills off any active module of
+that name.
+
+If a ROM section number is specified then only versions of that module
+in that ROM section are unplugged.
+
+The action of *RMReInit has changed slightly. If the specified module is
+active, then the effect is as before, ie the module is killed and then
+re-initialised.
+
+If the specified module is not active, but is in the ROM, then the unplug
+bit in CMOS RAM is cleared for all versions of the specified module, and
+then the newest version of the module is initialised.
+
+ New star command
+ ----------------
+
+*RMInsert <moduletitle> [<ROM section number>]
+
+If no ROM section number is specified, then this command clears the unplug
+bit for all versions of the specified module, without reinitialising any of
+them.
+
+If a ROM section number is specified, then this command clears the unplug
+bit for all versions of the specified module present in the given section,
+without reinitialising any of them.
diff --git a/Doc/A540Extend b/Doc/A540Extend
new file mode 100644
index 0000000000000000000000000000000000000000..a342ca6776d7401df5cf80a6f5856ac7628ded2c
--- /dev/null
+++ b/Doc/A540Extend
@@ -0,0 +1,118 @@
+; A540Extend
+
+ Title:         A540Extend
+ Author:        Tim Dobson
+ Version:       1.01
+ Started:       01-Nov-90
+ Last updated:  25-Nov-91
+ Status:        Release
+ History:
+  01-Nov-90 TMD         Created
+  25-Nov-91 TMD         Updated for RISC OS 3.03
+
+Additions to the mode extension system for A540 and similar machines
+====================================================================
+
+This document describes extensions to the RISC OS mode extension system for
+machines which have programmable VIDC clock speeds and sync polarities, such
+as the A540. Familiarity with the RISC OS 2.00 mode extension system is
+assumed (this is described in the existing Programmer's Reference Manual).
+
+The A540 has extra hardware to allow the selection of different VIDC clocks
+and to determine the polarity of the sync lines. VIDC uses its clock
+together with a set of internal dividers to provide a range of pixel rates.
+
+The format of the "VIDC list" returned from Service_ModeExtension (&50) has
+been extended to allow the pixel rate and sync polarities to be specified.
+
+On original Archimedes machines, the VIDC clock is fixed at 24MHz, and the
+pixel rate is only determined by VIDC's internal dividers, as specified in
+bits 0 and 1 of the Control Register (VIDC address &E0). This would be
+stored in the VIDC list as a word of the form &E00000xx.
+
+RISC OS now supports two different format VIDC lists.
+
+The original (type 0) VIDC list format is as follows:-
+
+        Offset          Value
+
+        0               0
+        4               VIDC base mode
+        8               VIDC parameter
+        12              VIDC parameter
+        ..              ..
+        n               -1
+
+The new (type 1) VIDC list format is as follows:-
+
+        Offset          Value
+
+        0               1
+        4               VIDC base mode
+        8               VIDC parameter
+        12              VIDC parameter
+        ..              ..
+        n               -1
+        n+4             Extended parameter
+        n+8             Extended parameter
+        ..              ..
+        m               -1
+
+where extended parameters are of the form
+
+        (0 << 24) + (pixel rate in kHz)
+
+or
+
+        (1 << 24) + (sync polarity)
+
+or
+
+        (2 << 24) + (true VIDC clock rate in kHz)
+        ** This option available only from RISC OS 3.03 onwards **
+
+The sync polarity is defined as follows:-
+
+        bit 0 = 0 => HSync +ve (as on a standard Archimedes)
+              = 1 => HSync -ve
+
+        bit 1 = 0 => VSync +ve (as on a standard Archimedes)
+              = 1 => Vsync -ve
+
+        bits 2..23 must be zero
+
+A pixel rate specifier in a type 1 VIDC list will override the settings of
+bits 0 and 1 of a Control Register specifier in the main body of the list.
+If no pixel rate is specified, then the VIDC clock is set to 24MHz, and the
+settings of the divider in the Control Register are used as normal.
+
+The A540 hardware provides the following pixel rates:-
+
+        24000 kHz, 25175 kHz, 36000 kHz         with a multiplier of 2/2
+        16000 kHz, 16783 kHz, 24000 kHz         with a multiplier of 2/3
+        12000 kHz, 12587 kHz, 18000 kHz         with a multiplier of 1/2
+         8000 kHz,  8392 kHz, 12000 kHz         with a multiplier of 1/3
+
+If the pixel rate specified is not achievable with the hardware on the
+machine, the nearest available pixel rate is used.
+
+Note: when specifying a pixel rate for a hi-res-mono display, the pixel rate
+specified should be the actual pixel rate divided by 4, ie 24000 not 96000.
+
+If no sync polarity is specified, a default of 0 is used (ie the same as a
+normal Archimedes).
+
+The true VIDC clock rate specifier (only on RISC OS 3.03 or later) is
+intended to be used in systems where the clock rate fed to VIDC is under the
+control of some external device, rather than being selected by the clock
+select latch. (For example, on the portable machine, the LCD ASIC feeds
+either 8MHz or 16MHz into VIDC when LCD modes are selected).
+
+The values programmed into the clock select latch and the VIDC divider are
+still determined either from the control register specifier or a pixel rate
+specifier assuming the same range of clock speeds as on the A540, but the
+VIDC clock rate specifier is used to determine the video memory rate,
+which in turn determines the VIDC FIFO Request Pointer values (bits 4 and 5
+of the VIDC control register). The VIDC clock rate specifier is also stored
+in VDU variable VIDCClockSpeed (&AC), which is used by the SoundDMA module
+to determine the VIDC Sound Frequency Register value.
diff --git a/Doc/Kernel b/Doc/Kernel
new file mode 100644
index 0000000000000000000000000000000000000000..562f477d7e8397eeefb8a506b8d203b8b5bd4c88
--- /dev/null
+++ b/Doc/Kernel
@@ -0,0 +1,162 @@
+> net#arf:$.a500.RiscOS+.doc.Kernel
+
+ Description: Documentation of changes to kernel for PRM
+ Author:  Tim Dobson
+ Status:  Preliminary
+ History:
+    06-Oct-89  TMD     Created
+    13-Oct-89  TMD     Added documentation of OS_RemoveCallBack
+    27-Oct-89  TMD     Added documentation of VDU variable VIDCClockSpeed
+    01-Dec-89  NRaine  Added documentation of OS_FindMemMapEntries
+    04-Dec-89  TMD     Documentation of OS_FindMemMapEntries updated slightly
+
+The description of bug fixes below is in a very rough state at the moment -
+it will need to be tidied up before publication. Not all of the information
+below is relevant to the average user.
+
+ Documentation updates since RISC OS 2.00 release
+ ------------------------------------------------
+
+ Changes applying to RISC OS 2.01
+ --------------------------------
+
+A new SWI, OS_ChangeRedirection, has been added, to allow the reading and
+writing of the handles associated with OS_CLI input/output redirection. This
+call was provided for future versions of the task window module, so that
+these handles can be made local to the task running in a task window.
+
+        SWI OS_ChangeRedirection
+
+        Read or write OS_CLI input/output redirection handles
+
+in:     R0 = new input  handle (0 => not redirected, -1 => leave alone)
+        R1 = new output handle (0 => not redirected, -1 => leave alone)
+
+out:    R0 = old input  handle (0 => not redirected)
+        R1 = old output handle (0 => not redirected)
+
+****************************************************************************
+
+In RISC OS 2.00, the SWI OS_AddCallBack allowed interrupt routines to
+request a callback, which was granted later when RISC OS was about to
+exit to a user mode routine with IRQs enabled. However there was no way to
+cancel a callback request before it was granted. This could cause problems,
+for example if a module is being killed, and it has outstanding callback
+requests, it must refuse to die, otherwise the callback may be granted after
+that memory has been reused for something else. For this reason a new SWI,
+OS_RemoveCallBack, has been added.
+
+        SWI OS_RemoveCallBack
+
+        Remove a transient callback from the list
+
+in:     R0 = address that was to be called
+        R1 = value of R12 that the routine was to be called with
+
+out:    R0 = preserved
+        R1 = preserved
+
+****************************************************************************
+
+A new VDU variable, VIDCClockSpeed (variable number 172), has been added.
+The value of this variable is the current VIDC clock rate, in kHz. This
+value changes when a screen mode is selected which requires a different
+clock rate. The value is read in the same way as other VDU variables, by
+issuing the SWI OS_ReadVduVariables.
+
+Typical values are 24000 (ie 24MHz) for TV standard modes, 25175 (ie
+25.175MHz) for VGA modes, and 36000 (ie 36MHz) for super-VGA modes.
+
+****************************************************************************
+
+A number of routines were changed to improve IRQ latency:-
+
+a) New version of ChangeDynamicArea which reenables interrupts.
+
+b) Heap manager extend block has improved IRQ latency.
+
+c) Made OS_Byte &87 restore caller's IRQ state during call.
+
+d) Made OS_Word &0E,0 enable IRQs during call.
+
+e) Made OS_Word &15,0 enable IRQs during call.
+
+The heap manager has been optimised in a few places.
+
+The routine that converts a date and time value (in hours, minutes, seconds
+etc) into a 5-byte centisecond value has been made smaller and much faster.
+
+ Bug fixes
+ ---------
+
+Fixed bug in extend heap call (stack imbalance when returning 'No RAM for
+extending heap' error)
+
+Fixed "*%" (LDREQB not LDREQ).
+
+Fixed OS_ReadArgs with a /E argument that evaluates to a string (always used
+to give 'Buffer full' - also fixed 2 other bugs lurking in the background,
+viz. did STRB of buffer addr assuming it was non-zero to indicate a string,
+and didn't allow for length and type bytes in amount free value.
+
+Fixed OS_Word &15 reason code 4 (Read unbuffered mouse position) - it used
+to generate undefined instruction trap due to stack mismatch.
+
+Fixed OS_SWINumberToString with negative numbers.
+
+Fixed ROMModules never saying 'Running'.
+
+Made OS_SpriteOp reentrant by pushing register dump area on stack.
+
+Fixed sideways scroll by one 'byte' in MODEs 3 and 6.
+
+Fixed incarnation names being terminated by 1st character.
+
+Fixed *Unplug using address as extra terminator for module name.
+
+Fixed podule IRQ despatcher corrupting R0 (prevented it from correctly
+disabling the podule IRQ (or podule FIQ-as-IRQ) interrupt if no handler)
+
+RR-2047: Fixed bug in GSRead with quoted termination.
+
+RR-2060: Fixed bug in AddCallBack which freed the wrong heap node.
+
+RR-2066: Fixed bug which occasionally left soft cursors on the screen.
+
+RR-2067: Fixed bug in keyboard driver (pressing Break (as escape) key when
+keyboard buffer full did nothing)
+
+RR-2079: Fixed bug in CallAfter/Every which returned duff error pointers.
+Changed error message from null string to 'Invalid time interval'.
+
+RR-2080: Fixed rename incarnation bug.
+
+RR-2099: Fixed bug in monadic plus/minus in EvaluateExpression (eg *Eval
+50*-3)
+
+RR-2105: Added help on LEN in *Help Eval.
+
+RR-2108: Fixed bug in prefer incarnation which returned duff error pointers.
+
+
+ Changes applying to RISC OS 2.04
+ --------------------------------
+
+A new SWI OS_FindMemMapEntries has been added to allow fast scanning of the
+soft CAM map to find the correct page numbers for a given range of addresses.
+For efficiency, the caller supplies the  probable page numbers as well as the
+addresses, so that the routine can take a quick look to check if the page
+number is already correct before scanning the rest of the CAM map.
+
+        SWI OS_FindMemMapEntries
+        In:  R0 -> table of 12-byte page entries
+                +0      4       probable page number (0..npages-1) (use 0 if no idea)
+                +4      4       logical address to match with
+                +8      4       undefined
+             terminated by a single word containing -1
+        Out: table of 12-byte entries updated:
+                +0      4       actual page number (-1 => not found)
+                +4      4       address (preserved)
+                +8      4       page protection level
+             terminator preserved
+
diff --git a/Doc/KernlSplit b/Doc/KernlSplit
new file mode 100644
index 0000000000000000000000000000000000000000..8859fa751600f4d4c5bbb54df7ab605a32298223
--- /dev/null
+++ b/Doc/KernlSplit
@@ -0,0 +1,45 @@
+; > KernlSplit
+
+Feasibility of splitting the RISC OS kernel
+===========================================
+
+Author:                 Tim Dobson
+Name:                   KernlSplit
+Document version:       0.01
+Last modified:          19-Apr-90
+
+Cbange record:
+
+ Date       Name        Description of change
+ ----       ----        ---------------------
+ 19-Apr-90  TDobson     Started
+
+This document discusses the feasibility of splitting the RISC OS kernel into
+separate modules/code segments for each device driver.
+
+Suggested device drivers to be split off:-
+
+ VDU
+        Screen hardware drivers
+                VIDC
+                Pointer
+                Palette
+                Hardware scroll/multiple display banks
+                Allocation of screen memory
+        Bitmap manipulation
+                Wrch
+                Sprites
+                Draw
+                Fonts
+                ColourTrans
+
+ Keyboard/mouse
+
+ IIC
+                Real time clock
+                CMOS RAM
+ Serial
+
+ Centronics
+
+ Memory control
diff --git a/Doc/MMUControl b/Doc/MMUControl
new file mode 100644
index 0000000000000000000000000000000000000000..277832f72984cd1cd80cd6f5abf27b692d27ac3a
--- /dev/null
+++ b/Doc/MMUControl
@@ -0,0 +1,35 @@
+; Available from RISC OS 3.30 onwards
+
+ SWI OS_MMUControl (&6B)
+
+ On entry:
+        R0 = reason code/flags (must be zero)
+        R1 = XOR mask
+        R2 = AND mask
+
+ On exit:
+        R1 = old value of control register
+        R2 = new value of control register
+
+ Interrupts:
+        Interrupt status is undefined
+        Fast interrupts are enabled
+
+ Processor mode:
+        Processor is in SVC mode
+
+ Re-entrancy:
+        Not defined
+
+ Use:
+        This call performs a read-modify-write operation on the ARM MMU
+        control register. The new value of the register is
+
+          ((old value AND R2) XOR R1)
+
+        The old value of the register is returned in R1, and the new value
+        in R2. If the call results in the C (Cache enable) bit being
+        changed, the cache is flushed.
+
+        This call is intended for internal system use only. Users wishing to
+        enable or disable the cache should use the *Cache command instead.
diff --git a/Doc/MemMaps/130 b/Doc/MemMaps/130
new file mode 100644
index 0000000000000000000000000000000000000000..8d44fc29a6f951fa135ea5cf910392d69598cbe4
--- /dev/null
+++ b/Doc/MemMaps/130
@@ -0,0 +1,38 @@
+        000     App space
+        01C     System heap/svc stack
+        01E     Soft CAM copy/und stack
+        01F     Sound/pointer/random
+        020     RMA
+        02C     L2PT and L1PT
+        030     I/O
+        038     ROM
+        040     Screen
+        060     Free pool
+        0E2     Sprites
+        164     Font cache
+        1E6     RAM disc
+        268     Another area 1
+        2EA     Another area 2
+        36C     Another area 3
+        3EE     Another area 4
+        470     Another area 5
+        4F2     Another area 6
+        574     Another area 7
+        5F6     Another area 8
+        678     Another area 9
+        6FA     Another area 10
+        77C     Another area 11
+        7FE     Nothing
+        800     Phys space copy
+        A00     Another area 12
+        A82     Another area 13
+        B04     Another area 14
+        B86     Another area 15
+        C08     Another area 16
+        C8A     Another area 17
+        D0C     Another area 18
+        D8E     Another area 19
+        E10     Another area 20
+        E92     Another area 21
+        F14     Another area 22
+        F96     Nothing
diff --git a/Doc/MemMaps/258 b/Doc/MemMaps/258
new file mode 100644
index 0000000000000000000000000000000000000000..86cfa15e3c3ab8adbfe162c3d52f8c9b9139a41d
--- /dev/null
+++ b/Doc/MemMaps/258
@@ -0,0 +1,24 @@
+        000     App space
+        01C     System heap/svc stack
+        01E     Soft CAM copy/und stack
+        01F     Sound/pointer/random
+        020     RMA
+        02C     L2PT and L1PT
+        030     I/O
+        038     ROM
+        040     Screen
+        060     Free pool
+        162     Sprites
+        264     Font cache
+        366     RAM disc
+        468     Another area 1
+        56A     Another area 2
+        66C     Another area 3
+        76E     Nothing
+        800     Phys space copy
+        A00     Another area 4
+        B02     Another area 5
+        C03     Another area 6
+        D04     Another area 7
+        E05     Another area 8
+        F06     Nothing
diff --git a/Doc/Mode22 b/Doc/Mode22
new file mode 100644
index 0000000000000000000000000000000000000000..be09035166f22f66cd2ceddddd3d3627d9a673e9
--- /dev/null
+++ b/Doc/Mode22
@@ -0,0 +1,64 @@
+
+ Title: Mode22
+ Author: Tim Dobson
+ History:
+        05-Dec-91 TMD Created
+
+From RISC OS 3.03 onwards a new screen mode (22) is available, on monitor
+types 0 and 1 only, which is suitable for use by visually impaired people.
+
+In terms of pixels and colours the mode is equivalent to mode 35 (an
+overscan mode), ie 16 colours, 768 pixels by 288 rows.
+
+However, the ratio of OS coordinates to pixels is changed so that instead of
+the screen being 1536 by 1152 coordinates like mode 35, it is only 768 by
+576 coordinates. This results in most text and graphics in the desktop being
+drawn twice as large in both X and Y directions, thus making them easier to
+see.
+
+There are currently a number of problems associated with this mode:-
+
+ a) The desktop tool sprites (ie the sprites used in window borders and the
+like) are inappropriate for this mode, causing some horizontal lines to
+become double thickness, and some vertical lines to disappear entirely.
+
+ b) Some applications (including those in the ROM) create windows of a
+certain size without scroll bars, and assume that the screen will be big
+enough in one or both directions to accommodate the whole of the window.
+Some parts of these windows may then be inaccessible.
+
+ Examples of this are:-
+
+ Filer windows with 'Full info' selected
+ !Alarm 'Setup','Set clock', 'Set alarm' (particularly repeating alarms)
+   windows
+ !Chars window
+ !Draw toolbox (goes partly off bottom)
+ !Edit 'Find text' window (particularly with 'Magic characters' or
+   'Wildcarded expressions' turned on
+
+
+ c) Some applications may create windows and then assume that the
+window has been created that size, and then create icons in that
+window assuming that size. The icons will then appear in the wrong
+place, eg overlapping other icons.
+
+ Examples of this are:-
+
+ !Paint tool window with various tools selected (eg use sprite as brush)
+
+ d) Some applications may create windows aligned with the bottom of the
+screen, such that the title bar goes completely off the top of the screen.
+The window therefore cannot be moved.
+
+ Examples of this are:-
+
+ Some !Impression windows.
+
+ e) Some applications which use sprites to update their windows, always use
+a fixed number of pixels for their windows. The inside of the window
+therefore does not appear double size.
+
+ Examples of this are:-
+
+ PC emulator (in a window).
diff --git a/Doc/Modes b/Doc/Modes
new file mode 100644
index 0000000000000000000000000000000000000000..78347140335aced1819663692dd6fa12f2ccb500
--- /dev/null
+++ b/Doc/Modes
@@ -0,0 +1,40 @@
+Modes we can do:
+
+DRAM-only system:                       Peak bandwidth used (x 1E6 bytes/sec)
+Total bandwidth available
+ = 46.5E6 bytes/sec
+
+  640 x 480 (72Hz) at 8bpp              31.5
+
+  800 x 600 (56Hz) at 8bpp              36
+  800 x 600 (60Hz) at 8bpp              40
+  800 x 600 (72Hz) at 4bpp              25
+
+ 1024 x 768 (60Hz) at 4bpp              32.5
+ 1024 x 768 (70Hz) at 4bpp              37.5
+
+1M VRAM system:
+Total bandwidth available
+= 80E6 bytes/sec
+
+  640 x 480 (72Hz) at 16bpp             63
+
+  800 x 600 (56Hz) at 16bpp             72
+  800 x 600 (60Hz) at 16bpp             80
+  800 x 600 (72Hz) at 8bpp              50
+
+ 1024 x 768 (60Hz) at 8bpp              65
+ 1024 x 768 (70Hz) at 8bpp              75
+
+2M VRAM system:
+Total bandwidth available
+= 160E6 bytes/sec
+
+  640 x 480 (72Hz) at 32bpp             126
+
+  800 x 600 (56Hz) at 32bpp             144
+  800 x 600 (60Hz) at 32bpp             160
+  800 x 600 (72Hz) at 16bpp             100
+
+ 1024 x 768 (60Hz) at 16bpp             130
+ 1024 x 768 (70Hz) at 16bpp             150
diff --git a/Doc/MonLead b/Doc/MonLead
new file mode 100644
index 0000000000000000000000000000000000000000..4acaf4afbaa7b95a671b16d2d070c06cb97260e3
--- /dev/null
+++ b/Doc/MonLead
@@ -0,0 +1,108 @@
+; > Doc.MonLead
+
+ Title:         MonLead
+ Author:        Tim Dobson
+ Version:       0.04
+ Started:       19-Mar-91
+ Last updated:  24-Apr-92
+ Status:        Incomplete
+ History:
+  19-Mar-91 TMD Created
+  19-Mar-91 TMD Updated
+  10-Apr-91 TMD Added documentation of Service_MonitorLeadTranslation
+  24-Apr-92 TMD Corrected information to match bodge for LiteOn monitor
+
+ Automatic detection of monitor type from monitor lead ID pins
+ =============================================================
+
+Some RISC OS computers have circuitry which allows the detection of the
+state of ID pins on the monitor connector. This allows the computer to
+distinguish between most types of monitor, and adjust its video output
+accordingly.
+
+To support this, a number of changes have been made to RISC OS:-
+
+a) To simplify the interface, the commands *Configure Mode and
+*Configure WimpMode have been merged. Both commands control the same CMOS
+location. Therefore the same screen mode will be selected on startup
+irrespective of whether the desktop is being used.
+
+b) The commands *Configure Mode/WimpMode, *Configure MonitorType, and
+*Configure Sync now take the keyword Auto as an alternative to a numeric
+parameter. If this option is configured, then RISC OS will determine a
+reasonable default for the particular parameter, based on the type of
+monitor plugged in.
+
+As the default is for all three to be set to Auto, the user should only have
+to change the settings if he has a type of monitor which is not recognised
+properly, or if he wishes to use a different screen mode from the chosen
+default.
+
+c) The effect of holding certain keys down on power-on is slightly changed:-
+
+Key held down on power-on       Settings of CMOS RAM
+
+R or Delete                     MonitorType Auto, Mode/WimpMode Auto, Sync Auto, and all the rest it used to
+T or Copy                       MonitorType Auto, Mode/WimpMode Auto, Sync 0 (separate syncs), and all the rest
+Keypad 0 to 9                   MonitorType 0 to 9
+Keypad dot                      MonitorType Auto, Mode/WimpMode Auto, Sync Auto
+
+d) A new service has been added which allows unknown values of the monitor
+ID to be recognised by modules and converted into the appropriate monitor
+type number, sync type and default mode, as follows:-
+
+Service_MonitorLeadTranslation (&76)
+
+ in:    R1 = service code (&76)
+        R2 = monitor lead ID (see below)
+
+ out:   If monitor lead ID is recognised, then the module should set
+        R1 = 0 (claim service)
+        R3 = default screen mode number to use on this type of monitor
+        R4 = monitor type number to use (as used in *Configure MonitorType)
+        R5 = sync type to use on this type of monitor
+         (0 => separate syncs, 1 => composite sync)
+        All other registers must be preserved.
+
+        If the monitor lead ID is not recognised, the module should preserve
+        all registers.
+
+The monitor connector provides 4 ID pins, ID0-ID3. Each of these may be
+connected to 0v, +5v or to the Hsync pin. The monitor lead ID therefore
+represents the state of the 4 ID pins by 8 bits as follows:-
+
+ Bit 0  Bit 1   State of ID0
+ Bit 2  Bit 3   State of ID1
+ Bit 4  Bit 5   State of ID2
+ Bit 6  Bit 7   State of ID3
+
+   0      0     Tied to 0v
+   1      0     Tied to +5v
+   0      1     Tied to Hsync
+   1      1     Inderminate - either the state is fluctuating
+                or machine is not capable of reading the ID
+
+The service is issued when SWI OS_ReadSysInfo is called with R0=1 (see
+document 'ReadSysInf') if any of the configured Mode/MonitorType/Sync are
+set to Auto.
+
+If the service is not claimed, then RISC OS checks the monitor lead ID
+against the following list of recognised IDs:-
+
+Monitor ID pins         Monitor type            Sync type       Default mode
+0=0v,1=+5v,H=Hsync,
+X=don't care
+ Pin 0 1 2 3
+
+     1 1 H X            1 (Multisync)           1 (composite)       27
+     1 0 1 X            3 (Mono VGA)            0 (separate)        27
+     0 1 1 X            3 (Colour VGA)          0 (separate)        27
+     0 1 0 X            1 (Multisync) *         0 (separate)        27
+     H 1 1 X            0 (TV standard)         1 (composite)       12
+
+For all other ID values RISC OS uses the TV standard monitor settings.
+
+* This entry should really be monitor type 4 (Super VGA). However the LiteOn
+monitor returns this monitor ID, even though it can do the TV standard
+modes. RISC OS therefore selects monitor type 1 instead, so the TV standard
+and VGA standard modes can be selected on this monitor.
diff --git a/Doc/PaletteV b/Doc/PaletteV
new file mode 100644
index 0000000000000000000000000000000000000000..ca729cda3d7a15d41507c305d6185998b6bcde2a
--- /dev/null
+++ b/Doc/PaletteV
@@ -0,0 +1,120 @@
+
+ Title: PaletteV
+ Author: Tim Dobson
+ History:
+        11-Nov-91 TMD Adapted from ColourTrans.Doc.PaletteV
+        25-Nov-91 TMD Changed a bit about setting flashing colours
+
+From RISC OS 3.03 onwards, the kernel has a default entry on PaletteV. Also,
+a number of additional reason codes have been added, to facilitate the
+implementation of the LCD drivers for Perth.
+
+The new specification for PaletteV is as follows:-
+
+R4 holds a reason code on entry to the vector. Any owner of the vector which
+has carried out the operation requested should set R4 to zero and claim the
+vector.
+
+ Reason codes
+ ============
+
+        1 - Read palette
+
+        in:     R0 = logical colour
+                R1 = type of colour (16,17,18,24,25)
+                R4 = 1 (reason code)
+
+        out:    R2 = 1st flash colour (&BBGGRRSS) - device colour
+                R3 = 2nd flash colour (&BBGGRRSS) - device colour
+                R4 = 0 => operation complete
+
+        2 - Set palette
+
+        in:     R0 = logical colour
+                R1 = type of colour (16,17,18,24,25)
+                R2 = &BBGGRRSS - device colour
+                R4 = 2 (reason code)
+
+        out:    R4 = 0 => operation complete
+
+        3 - Set first flash state
+
+        in:     R4 = 3 (reason code)
+
+        out:    R4 = 0 => operation complete
+
+        4 - Set second flash state
+
+        in:     R4 = 4 (reason code)
+
+        out:    R4 = 0 => operation complete
+
+        5 - Set default palette
+
+        in:     R4 = 5 (reason code)
+
+        out:    R4 = 0 => operation complete
+
+        6 - Blank/unblank screen (only available from RISC OS 3.08 onwards)
+
+        in:     R0 = -1 (read blank state)
+                   or 0 (unblank screen)
+                   or 1 (blank screen)
+                R4 = 6 (reason code)
+
+        out:    R0 = old state (0=unblanked, 1=blanked)
+                R4 = 0 => operation complete
+
+        This call blanks or unblanks the screen, independently of the current
+        palette settings.
+
+In the SS bits mentioned in calls 1 and 2 above, bit 7 is the current
+supremacy bit, other bits are reserved.
+
+ How old OS calls map onto PaletteV
+ ----------------------------------
+
+Initial suggestions were that VDU 19 and OS_Word(12) should ignore the
+bottom 4 bits of RGB values passed to them, and duplicate the top 4 bits in
+the bottom 4 bits before calling PaletteV. This was so that old style
+programs which set the low nybbles to zero would work correctly on machines
+with 8-bit per gun palette hardware.
+
+However, I believe that this damages the usefulness of the above calls
+unnecessarily. As long as the default palettes read back true 8-bit values,
+and the PaletteUtil module duplicates the nybbles when setting the colours,
+it should not be necessary to alter the parameters to VDU 19 and
+OS_Word(12).
+
+So the OS will pass the values through to PaletteV, and, assuming there is
+no-one else on the vector, will get the values itself. At this point it will
+just store the top 4 bits of each value in its soft copy, and in the
+hardware if appropriate (when setting the 1st and 2nd flashing colours it
+only updates the hardware if that flash state is current).
+
+The calls to read the palette on earlier versions of the OS return a value
+which corresponds to how the palette entry was programmed, ie 0..15 if a BBC
+style colour was selected, 16 if steady RGB colours were used, 17 or 18 for
+flashing colours, 24 for border colours and 25 for pointer colours.
+
+Since PaletteV does not return this information, the OS will try to make up
+this information itself. It can easily cope with the 24,25 cases. If the two
+colours returned are different, it will substitute 17 or 18 as appropriate,
+otherwise it will use 16. It will no longer return values in the range 0 to
+15.
+
+Also, when setting the palette, PaletteV does not understand BBC colours
+0..15. In order to provide the necessary functionality, the OS calls to set
+the palette will trap these values and convert them to type 16 calls (for
+0..7) or pairs of type 17 and type 18 calls.
+
+This requires a slight change to the specification of what type 17 and 18
+calls do. At the moment, these calls NEVER update the VIDC palette, instead
+they only update the relevant soft copy, and when/if the flash state
+changes, the colours are updated. But programming a BBC flashing colour
+takes effect immediately even if the flash state is 'frozen'.
+
+I therefore propose to make the 17 and 18 calls also update the VIDC palette
+if the current state is 1st or 2nd respectively. It's still not quite ideal
+because you really want to update both flash colours atomically, in case the
+state changed in between the two calls.
diff --git a/Doc/PrivDoc/5thColumn/Concept b/Doc/PrivDoc/5thColumn/Concept
new file mode 100644
index 0000000000000000000000000000000000000000..50565e9ee4f86bf5c823004ce45efa1b33673ea8
--- /dev/null
+++ b/Doc/PrivDoc/5thColumn/Concept
@@ -0,0 +1,59 @@
+; > 5thColumn.Concept
+
+ RISC OS Support for extension ROMs
+ ==================================
+
+ Author:        Tim Dobson
+ Status:        Draft
+ Issue:         0.02
+ History:
+
+  Date          Revision        Changes
+
+  11-Oct-90     0.00            Started
+  16-Oct-90     0.01            Completed first draft
+  04-Feb-91     0.02            Updated to reflect reality
+
+This document describes the purpose of the extension ROM system and
+discusses various design issues. For the full technical documentation, refer
+to the document "5thColumn.Manual".
+
+The extension ROM system allows the development of hardware platforms fitted
+with a normal 32 bit wide RISC OS ROM set plus one or more 8, 16 or 32 bit
+ROMs or EPROMs containing software modules which add to or replace modules
+in the main ROM set. This allows the same main ROM set to be used in a wider
+variety of hardware platforms, removing the extra cost and lead times of
+re-romming, and possibly reducing costs by allowing bulk purchase of the
+main ROM set.
+
+The extension ROM(s) appear in the memory map in unused parts of the low
+(&03400000 to &037FFFFF) or high (&03800000 to &03FFFFFF) ROM areas. A 32
+bit wide extension ROM set is directly executable in place, saving on user
+RAM. 8 or 16 bit wide sets have to be copied into RAM to execute. By using
+the low ROM area (whose access time is programmable independently from the
+high area containing the main ROM set) slow EPROMs can be used.
+
+A particularly attractive configuration might be to have 8 ROM sockets on
+the board, 4 for the main ROM set, and the other 4 capable of taking either
+one 32 bit wide set (eg a large set of applications eg Internet) or up to 4
+individual 8 bit wide ROMs containing smaller applications or utilities.
+
+The scheme also allows a machine to have limited protection against
+unauthorised access, if the extension ROM contains a module which requires a
+password to be entered before continuing.
+
+In order to allow different sizes of EPROMs to be used without having to
+have links on the board, the software will look for extension ROMs at higher
+addresses first, and work backwards. This means that the high order address
+lines (which should be tied to +5v on smaller sizes of EPROM) will be pulled
+high initially, although they will be pulled low later on when looking for
+further extension ROMs.
+
+The way in which the kernel initialises modules has been changed. If there
+is more than one version of the same module present in the ROM (which
+includes the main ROM, expansion card ROMs and extension ROMs) then only the
+newest version of the module is initialised. If an extension ROM contains a
+newer version of a module in the main ROM, then the newer version will be
+initialised at the point in the initialisation sequence where the main ROM
+version would have been initialised. This allows main ROM modules to be
+replaced without the problems associated with initialisation order.
diff --git a/Doc/PrivDoc/MMPM b/Doc/PrivDoc/MMPM
new file mode 100644
index 0000000000000000000000000000000000000000..57ea4e00e28908930be310fcfdf1554b9d1343e6
--- /dev/null
+++ b/Doc/PrivDoc/MMPM
@@ -0,0 +1,54 @@
+; > PrivDoc.MMPM
+
+Still to do on memory management, as of 26-May-93:
+
+; Must be TMD
+
+ + Make SoftCAMMap variable size
+ + Finish routine to allocate backing L2 for an area
+ + Write routine to allocate logical addresses for areas
+ + Write routine to check for overlapping areas
+ + Complete Create dynamic area routine
+    (done apart from final OS_ChangeDynamicArea to get required size)
+ + Write Remove dynamic area routine
+    (done apart from initial OS_ChangeDynamicArea to shrink to zero size)
+ + Write Return info on dynamic area routine
+ + Write Enumerate dynamic areas routine
+ + Write Renumber dynamic areas routine
+ + Change OS_ReadDynamicArea to use new list
+ + Change OS_ValidateAddress to use new list
+ + Put in new error messages properly
+ * If CreateArea fails to grow area to required size, it should kill area and return error
+ * Change ChangeDynamicArea code to use lists:
+    + Check enough is working for Wimp_ClaimFreeMemory to use OS_DynamicArea(create)
+    * Check PreShrink and PostShrink work completely OK
+    * Check PreGrow and PostGrow work (apart from passing in page blocks)
+    * Migrate existing areas to new world:
+        * Update InitDynamicAreas initially to fake up a node for the RMA, and check it works
+        * Use DynamicArea_Create to create RMA from scratch (if feasible)
+        * Update InitDynamicAreas to fake up a node for the system heap + check it (no way of using create routine)
+        * Change OS_ReadRAMFsLimits to use OS_ReadDynamicArea
+        * Write RAMFS area handlers
+        * Create RAMFS dynamic area using DynamicArea_Create, + check it works
+        * Do similar for font cache, sprite area
+
+    * Put in code to split grow block into chunks, and create page blocks (without checking for updates from PreGrow)
+    * Put in checks for PreGrow requesting particular pages, and call alternative code:
+        * Do the double shuffle
+        * Issue Service_PagesUnsafe/Safe
+        * Stop it getting the static pages (esp. cursor/sound page, L1 and maybe L2)
+    * Put in extra code to cope with doubly-mapped areas
+    * Write area handlers for screen, and move it to new world
+ * Change size of application space to 24M (check all refs to 16M in whole image)
+ * Put in indirections for hardware vector poking
+ * Change FPE to use indirections (KWelton)
+ * Move RMA to &02100000, and change size of app space to 28M
+ * Conversion to do late aborts
+ 
+; Could be done by ANOther
+
+ * OS_Memory:
+        a) conversion bits
+        b) read phys.memory arrangement
+        c) read amounts of various sorts of memory
+        d) read controller addresses
\ No newline at end of file
diff --git a/Doc/PrivDoc/ScreenMode b/Doc/PrivDoc/ScreenMode
new file mode 100644
index 0000000000000000000000000000000000000000..410e776aa196e18598fbb2f5ecd24d2c92acf93e
--- /dev/null
+++ b/Doc/PrivDoc/ScreenMode
@@ -0,0 +1,92 @@
+; > PrivDoc.ScreenMode
+
+Still to do on screen mode selection, as of 21-Jul-93:
+
+Key:    +  Done and tested
+        -  Done but not tested
+        *  Still to do
+        x  Not done for a good reason
+
+ + Make OS_ReadModeVariable work with mode selectors
+ + OS_ScreenMode(ReturnMode)
+ + OS_ScreenMode(EnumerateModes)
+     + Create variable holding video bandwidth
+     + Add this reason code to just load up video bandwidth, VideoSize and issue service
+ + Service_ModeExtension additions
+     + Load up r4 and r5 with video bandwidth, VideoSize respectively
+ + Change vdugrafg:SetUpSprModeData:04 to check for mode selector, and goto 10 if so
+ + Check other occurrences of BranchIfKnownMode to look for similar bits
+ + Put code to handle new sprite mode word into PushModeInfo (any monitor only?)
+ + Remove new sprite mode word fudging in vdugrafg:SetupSprModeData and 
+    vdugrafl:SwitchOutputToSprite
+ + Make SwitchOutputToSprite(new format) set up ECFIndex (it doesn't at the moment!)
+ + Make sure tests for equal mode numbers don't assume equal ptrs to mode selectors are equivalent
+ + Modify NewModes module to respond to Service_EnumerateScreenModes, to test enumeration.
+ + OS_ScreenMode(SetMonitorType)
+     + Allocate soft copy for monitortype
+     + Write routine to update soft copy from CMOS
+     + Call this routine in initialisation
+     + Make *Configure MonitorType update soft copy
+     + Change ReadMonitorType to read from soft copy
+     + Add this reason code to either store given value or update from CMOS
+ + Make sprites which have mode selectors as their mode word illegal
+     + Move conversion of mode selectors to new format sprite mode words
+        into PreCreateHeader, rather than PostCreateHeader, so that it
+        doesn't call SetupSprModeData with a (now illegal) mode selector
+ -> MT ScreenModes module
+
+ -> AG Make switch output to sprite for a new format sprite make mode selector for current mode?
+
+ -> AG *ScreenSave in mode 50 seems to produce a sprite with a palette.
+
+ -> NK Trying to set a WimpMode with XEigFactor=27 caused data abort.
+       Investigate and/or range-limit values.
+
+ -> AG Put in support for returning errors from PushModeInfo (for bad mode
+       selectors and new format sprite mode words):
+     + Make mode change routine check for error from PushModeInfo and FindOKMode
+     + Make FindSubstitute check errors from PushModeInfo
+     + Make FindOKMode check errors from FindSubstitute
+     + Make CheckModeValid check errors from FindOKMode
+     + Make SetupSprModeData capable of returning errors:
+         + Ditto SpriteV handler (already OK)
+         + Ditto PreCreateHeader
+         + Ditto CreateHeader
+         + Ditto GetSprite
+     -> AG Make SwitchOutputToSprite/Mask check errors from PushModeInfo
+
+ - Design and code algorithm for working out FIFO reload position for VIDC20
+   (Still need explanation from ARM of why 7 quad-words doesn't always work)
+
+ * OS_ScreenMode(SelectMode)
+     + Make normal mode selection routine into a subroutine
+     + Write veneers to put round call to this in OS_ScreenMode(SelectMode)
+     * Change actual mode change code to cope with mode selectors
+         + Prevent main routine looking at shadow bit in mode selector
+         + Modify FindOKMode to cope with mode selector
+         + Modify OS_CheckModeValid to cope with mode selector
+         + Make all pushed mode variables into words (not bytes)
+         + Modify PushModeInfo to cope with mode selector
+         + Make YEigFactor default to 2 if yres < xres/2 (and change spec. to reflect that)
+         + Make numbered modes work after loading mode file
+         + Allocate space for OS copy of mode selector
+         x Make OS mode selector part of saved VDU context
+            (not needed since sprites can't have mode selectors as their mode)
+         x Sort out internal mode variables PalIndex, ECFIndex wrt
+            converting existing mode numbers into mode selectors (no need, still use old workspace-getting code)
+         x Make mode selector blocks for all existing numbered modes
+            (no need, constructed on fly since only needed during svc call)
+         * Check that copying mode selector has no adverse effects
+         * Sort out why issuing a mode change with invalid mode selector doesn't give error
+         * Modify FindOKMode to cope with 16 and 32 bpp modes somehow
+
+ * Prevent pointer position from going into the sync pulse (causes screen picture disruption)
+
+ * Adjust borders on all modes, to cope with VIDC20 problem
+   (Needs algorithm from ARM that works!)
+
+ * Mode change happily passes round any old rubbish to Service_ModeExtension - it should:-
+        * First check that value is word-aligned - if not it may be a new sprite mode word
+        * Do a Validate_Address on fixed bit of block?
+
+ * What should *ScreenLoad do with a new format sprite?
diff --git a/Doc/ReadSysInf b/Doc/ReadSysInf
new file mode 100644
index 0000000000000000000000000000000000000000..ee05c1a7e2aa20f1fa21d61f535f7b7df18f42ea
--- /dev/null
+++ b/Doc/ReadSysInf
@@ -0,0 +1,129 @@
+; > Doc.ReadSysInf
+
+ Title:         ReadSysInf
+ Author:        Tim Dobson
+ Version:       0.03
+ Started:       19-Mar-91
+ Last updated:  21-Oct-91
+ Status:        Preliminary
+ History:
+  19-Mar-91 TMD         Created
+  04-Apr-91 TMD         Updated OS_ReadSysInfo(2)
+
+ Extensions to SWI OS_ReadSysInfo in RISC OS 2.11 and later versions
+ ===================================================================
+
+SWI OS_ReadSysInfo has been extended since RISC OS 2.00 - the full
+specification is as follows:-
+
+*****************************************************************************
+
+        SWI OS_ReadSysInfo - Read various system information
+
+ in:    R0 = reason code
+
+ out:   Depends on reason code
+
+ Reason codes:-
+
+-------------------------------------------------------------------------
+
+ in:    R0 = 0
+ out:   R0 = amount of configured screen memory, in bytes
+
+This sub-call is the same as on RISC OS 2.00, with the exception that two
+bugs in the call have been fixed:-
+
+ a) It no longer goes wrong if less than 20K configured on 8K or 16K page
+size machine;
+
+ b) It now properly ignores the top bit of the CMOS location holding the
+configured value.
+
+-------------------------------------------------------------------------
+
+ in:    R0 = 1
+ out:   R0 = Configured Mode/WimpMode
+        R1 = Configured MonitorType
+        R2 = Configured Sync
+
+Note that from RISC OS 2.09 onwards, the configured Mode and WimpMode have
+been merged. Both *Configure Mode and *Configure WimpMode control the same
+CMOS RAM location.
+
+Note also that if any of Mode/WimpMode, MonitorType or Sync have been
+configured to Auto (see "Doc.MonLead"), then the appropriate value for the
+attached monitor will be returned.
+
+-------------------------------------------------------------------------
+
+ in:    R0 = 2
+ out:   R0 = IOEB ASIC presence flag
+             0 => absent
+             1 => present (type 1)
+             Other values are reserved for future versions of IOEB which are
+              not backwards compatible.
+
+        R1 = 82C710 (or similar) presence flag
+             0 => absent
+             1 => present
+
+        R2 = LCD ASIC presence flag
+             0 => absent
+             1 => present (type 1)
+             Other values are reserved for future versions of LCD ASIC which
+              are not backwards compatible.
+
+        R3 = word 0 of unique machine ID
+        R4 = word 1 of unique machine ID
+
+Some RISC OS computers are fitted with a chip providing a machine ID number
+which is unique to each computer. Machines not fitted with an ID will return
+zero in both R3 and R4.
+
+-------------------------------------------------------------------------
+
+ in:    R0 = 3 (*** Only available from RISC OS 3.01 onwards ***)
+ out:   R0 = 82C710/82C711 basic features mask       82C710  82C711
+                Bits 0..3   Basic IDE type              1       1
+                Bits 4..7   Basic FDC type              1       1
+                Bits 8..11  Basic parallel port type    1       1
+                Bits 12..15 Basic 1st serial port type  1       1
+                Bits 16..19 Basic 2nd serial port type  0       1
+                Bits 20..23 Basic Configuration type    1       2
+                Bits 24..31 Reserved
+
+        R1 = 82C710/82C711 extra features mask
+                Reserved for upwards compatible additional functionality
+
+        R2-R4 Undefined (reserved for future expansion)
+
+The 82C710 family of chips are composed of several sub-units, each of which
+might change under future revisions of the chip. Current sub-units are as
+follows:
+
+        IDE hard disc interface
+        Floppy disc interface
+        Parallel port
+        Serial port 1
+        Serial port 2 (only present in 82C711)
+        Chip configuration (different on 82C710 and 82C711)
+
+New versions of the chip may have some sub-units which are incompatible with
+earlier versions, while leaving the functionality of other sub-units
+unchanged.
+
+This call allows drivers which are only interested in particular sub-units
+to tell whether they can work on the particular hardware running in the
+machine.
+
+Different values of each sub-field correspond to incompatible versions of
+the corresponding sub-unit. A sub-field of zero indicates that the sub-unit
+is not present.
+
+If a sub-unit gains additional backwards-compatible functionality in future
+versions of the chip, this will be indicated by having bits set in the value
+returned in R1.
+
+Information on extra sub-units will be accomodated in the remaining bits of
+R0, or in R2-R4.
diff --git a/Doc/TVmodesMed,dde b/Doc/TVmodesMed,dde
new file mode 100644
index 0000000000000000000000000000000000000000..8ab0e2848118bca337597ed6eb457f4139824bb3
--- /dev/null
+++ b/Doc/TVmodesMed,dde
@@ -0,0 +1,1337 @@
+%OP%VS4.13 (28-Apr-92), Tim Dobson, R4001 0202 1006 4799 
+%OP%FGTrinity.Medium
+%OP%FS12000
+%OP%WC162,1898,210,1494,1,22,0,0
+%CO:A,2,0%%CO:B,17,72%Medusa screen modes
+
+This spreadsheet gives the horizontal and vertical timings for the Medusa screen modes.
+For the numbered screen modes, these timings are almost identical to the RISC OS 3 timings. 
+
+However, on VIDC1 the horizontal front and back porch timings are forced to be an odd 
+number of pixel times, whereas on VIDC20 they have to be even, therefore all horizontal back 
+porch timings have been reduced by one pixel, and front porch timings increased by one pixel.
+
+The figures below show that our timings for the 800 x 600 modes differ from the VESA 
+manufacturing guidelines in that we have hsync=100, hbpch=100, whereas they have 
+hsync=72, hbpch=128; ie the total times are the same, and the time from the start of the hsync 
+pulse to the start of the display area is the same, but our sync is wider. It is likely that I will 
+change RISC OS to match the VESA standard - I don't believe this will have an effect on the 
+horizontal position of the display (this is something I will have to check out).
+
+It is likely that as well as adding the VESA modes described below, RISC OS will also 
+provide several modes obtained by doubling the dot frequency of standard modes, eg 1280 x 
+480, 1600 x 600, 2048 x 768.
+
+It may also be possible to provide 1280 x 1024 at some pixel depths, although this may 
+involve running VIDC20 and/or the VCO out of specification. I don't have any timing 
+diagrams for 1280 x 1024; if you can get hold of these (is there a VESA standard for this?) it 
+would help establish the feasibility of this (the Taxan 875 Plus LR monitor spec. gives the line 
+frequency for this at 60 and 70Hz frame rates, but not the detailed timings (eg the dot 
+frequency)).
+
+Tim (22-Mar-93).
+
+Horizontal parameters for Monitortype 1 modes
+
+
+
+0,3,4,8,11,12,14,15 
+
+
+1,2,5,6,7,9,10,13
+
+
+16,17,24
+
+
+33,34,35,36
+(overscan)
+
+18,19,20,21
+
+
+25,26,27,28
+(VGA)
+
+29,30,31
+(SVGA)
+
+37,38,39,40
+(DTP - ega)
+
+41,42,43
+(EGA - pcemu)
+
+44,45,46
+(CGA - pcemu)
+
+800 x 600 (56 Hz)
+VESA guideline
+
+800 x 600 (60 Hz)
+VESA guideline
+
+800 x 600 (72 Hz)
+VESA standard
+
+1024 x 768 (60 Hz)
+VESA guideline
+
+1024 x 768 (70 Hz)
+VESA standard
+
+
+
+Vertical parameters
+
+0,1,2,4,5,8,9,10
+12,13,15,16,24
+
+3,6,7,11,14,17
+
+ 33,34,35,36
+
+18,19,20,21
+
+25,26,27,28
+
+29,30,31
+
+37,38,39,40
+
+41,42,43
+
+44,45,46
+
+800 x 600 (56 Hz)
+VESA guideline
+
+800 x 600 (60 Hz)
+VESA guideline
+
+800 x 600 (72 Hz)
+VESA standard
+
+1024 x 768  (60 Hz)
+VESA guideline
+
+1024 x 768  (70 Hz)
+VESA standard
+%CO:C,6,60%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+   sync
+
+%V%%R%%D0%72
+%V%%R%C34/M34*1000
+
+%V%%R%%D0%36
+%V%%R%C37/M37*1000
+
+%V%%R%%D0%108
+%V%%R%C40/M40*1000
+
+%V%%R%%D0%76
+%V%%R%C43/M43*1000
+
+%V%%R%%D0%56
+%V%%R%C46/M46*1000
+
+%V%%R%%D0%96
+%V%%R%C49/M49*1000
+
+%V%%R%%D0%100
+%V%%R%C52/M52*1000
+
+%V%%R%%D0%118
+%V%%R%C55/M55*1000
+
+%V%%R%%D0%76
+%V%%R%C58/M58*1000
+
+%V%%R%%D0%72
+%V%%R%C61/M61*1000
+
+%V%%R%%D0%72
+%V%%R%C64/M64*1000
+
+%V%%R%%D0%128
+%V%%R%C67/M67*1000
+
+%V%%R%%D0%120
+%V%%R%C70/M70*1000
+
+%V%%R%%D0%136
+%V%%R%C73/M73*1000
+
+%V%%R%%D0%136
+%V%%R%C76/M76*1000
+
+
+
+(rasters)
+
+%V%%R%%D0%3
+
+
+%V%%R%%D0%3
+
+%V%%R%%D0%3
+
+%V%%R%%D0%3
+
+%V%%R%%D0%2
+
+%V%%R%%D0%2
+
+%V%%R%%D0%3
+
+%V%%R%%D0%3
+
+%V%%R%%D0%3
+
+%V%%R%%D0%2
+
+
+%V%%R%%D0%4
+
+
+%V%%R%%D0%6
+
+
+%V%%R%%D0%6
+
+
+%V%%R%%D0%6
+%CO:D,6,48%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  porch
+
+%V%%R%%D0%62
+%V%%R%D34/M34*1000
+
+%V%%R%%D0%30
+%V%%R%D37/M37*1000
+
+%V%%R%%D0%72
+%V%%R%D40/M40*1000
+
+%V%%R%%D0%82
+%V%%R%D43/M43*1000
+
+%V%%R%%D0%112
+%V%%R%D46/M46*1000
+
+%V%%R%%D0%46
+%V%%R%D49/M49*1000
+
+%V%%R%%D0%100
+%V%%R%D52/M52*1000
+
+%V%%R%%D0%58
+%V%%R%D55/M55*1000
+
+%V%%R%%D0%36
+%V%%R%D58/M58*1000
+
+%V%%R%%D0%162
+%V%%R%D61/M61*1000
+
+%V%%R%%D0%128
+%V%%R%D64/M64*1000
+
+%V%%R%%D0%88
+%V%%R%D67/M67*1000
+
+%V%%R%%D0%64
+%V%%R%D70/M70*1000
+
+%V%%R%%D0%160
+%V%%R%D73/M73*1000
+
+%V%%R%%D0%144
+%V%%R%D76/M76*1000
+
+
+
+
+
+%V%%R%%D0%16
+
+
+%V%%R%%D0%16
+
+%V%%R%%D0%19
+
+%V%%R%%D0%18
+
+%V%%R%%D0%32
+
+%V%%R%%D0%22
+
+%V%%R%%D0%9
+
+%V%%R%%D0%9
+
+%V%%R%%D0%34
+
+%V%%R%%D0%22
+
+
+%V%%R%%D0%23
+
+
+%V%%R%%D0%23
+
+
+%V%%R%%D0%29
+
+
+%V%%R%%D0%29
+%CO:E,6,36%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ border
+
+%V%%R%%D0%88
+%V%%R%E34/M34*1000
+
+%V%%R%%D0%44
+%V%%R%E37/M37*1000
+
+%V%%R%%D0%106
+%V%%R%E40/M40*1000
+
+%V%%R%%D0%0
+%V%%R%E43/M43*1000
+
+%V%%R%%D0%0
+%V%%R%E46/M46*1000
+
+%V%%R%%D0%0
+%V%%R%E49/M49*1000
+
+%V%%R%%D0%0
+%V%%R%E52/M52*1000
+
+%V%%R%%D0%0
+%V%%R%E55/M55*1000
+
+%V%%R%%D0%0
+%V%%R%E58/M58*1000
+
+%V%%R%%D0%0
+%V%%R%E61/M61*1000
+
+%V%%R%%D0%0
+%V%%R%E64/M64*1000
+
+%V%%R%%D0%0
+%V%%R%E67/M67*1000
+
+%V%%R%%D0%0
+%V%%R%E70/M70*1000
+
+%V%%R%%D0%0
+%V%%R%E73/M73*1000
+
+%V%%R%%D0%0
+%V%%R%E76/M76*1000
+
+
+
+
+
+%V%%R%%D0%17
+
+
+%V%%R%%D0%20
+
+%V%%R%%D0%0
+
+%V%%R%%D0%0
+
+%V%%R%%D0%0
+
+%V%%R%%D0%0
+
+%V%%R%%D0%0
+
+%V%%R%%D0%0
+
+%V%%R%%D0%0
+
+%V%%R%%D0%0
+
+
+%V%%R%%D0%0
+
+
+%V%%R%%D0%0
+
+
+%V%%R%%D0%0
+
+
+%V%%R%%D0%0
+%CO:F,6,24%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+display
+
+%V%%R%%D0%640
+%V%%R%F34/M34*1000
+
+%V%%R%%D0%320
+%V%%R%F37/M37*1000
+
+%V%%R%%D0%1056
+%V%%R%F40/M40*1000
+
+%V%%R%%D0%768
+%V%%R%F43/M43*1000
+
+%V%%R%%D0%640
+%V%%R%F46/M46*1000
+
+%V%%R%%D0%640
+%V%%R%F49/M49*1000
+
+%V%%R%%D0%800
+%V%%R%F52/M52*1000
+
+%V%%R%%D0%896
+%V%%R%F55/M55*1000
+
+%V%%R%%D0%640
+%V%%R%F58/M58*1000
+
+%V%%R%%D0%640
+%V%%R%F61/M61*1000
+
+%V%%R%%D0%800
+%V%%R%F64/M64*1000
+
+%V%%R%%D0%800
+%V%%R%F67/M67*1000
+
+%V%%R%%D0%800
+%V%%R%F70/M70*1000
+
+%V%%R%%D0%1024
+%V%%R%F73/M73*1000
+
+%V%%R%%D0%1024
+%V%%R%F76/M76*1000
+
+
+
+
+
+%V%%R%%D0%256
+
+
+%V%%R%%D0%250
+
+%V%%R%%D0%288
+
+%V%%R%%D0%512
+
+%V%%R%%D0%480
+
+%V%%R%%D0%600
+
+%V%%R%%D0%352
+
+%V%%R%%D0%352
+
+%V%%R%%D0%200
+
+%V%%R%%D0%600
+
+
+%V%%R%%D0%600
+
+
+%V%%R%%D0%600
+
+
+%V%%R%%D0%768
+
+
+%V%%R%%D0%768
+%CO:G,6,30%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ border
+
+%V%%R%%D0%88
+%V%%R%G34/M34*1000
+
+%V%%R%%D0%44
+%V%%R%G37/M37*1000
+
+%V%%R%%D0%106
+%V%%R%G40/M40*1000
+
+%V%%R%%D0%0
+%V%%R%G43/M43*1000
+
+%V%%R%%D0%0
+%V%%R%G46/M46*1000
+
+%V%%R%%D0%0
+%V%%R%G49/M49*1000
+
+%V%%R%%D0%0
+%V%%R%G52/M52*1000
+
+%V%%R%%D0%0
+%V%%R%G55/M55*1000
+
+%V%%R%%D0%0
+%V%%R%G58/M58*1000
+
+%V%%R%%D0%0
+%V%%R%G61/M61*1000
+
+%V%%R%%D0%0
+%V%%R%G64/M64*1000
+
+%V%%R%%D0%0
+%V%%R%G67/M67*1000
+
+%V%%R%%D0%0
+%V%%R%G70/M70*1000
+
+%V%%R%%D0%0
+%V%%R%G73/M73*1000
+
+%V%%R%%D0%0
+%V%%R%G76/M76*1000
+
+
+
+
+
+%V%%R%%D0%17
+
+
+%V%%R%%D0%20
+
+%V%%R%%D0%0
+
+%V%%R%%D0%0
+
+%V%%R%%D0%0
+
+%V%%R%%D0%0
+
+%V%%R%%D0%0
+
+%V%%R%%D0%0
+
+%V%%R%%D0%0
+
+%V%%R%%D0%0
+
+
+%V%%R%%D0%0
+
+
+%V%%R%%D0%0
+
+
+%V%%R%%D0%0
+
+
+%V%%R%%D0%0
+%CO:H,6,20%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ porch
+
+%V%%R%%D0%74
+%V%%R%H34/M34*1000
+
+%V%%R%%D0%38
+%V%%R%H37/M37*1000
+
+%V%%R%%D0%88
+%V%%R%H40/M40*1000
+
+%V%%R%%D0%98
+%V%%R%H43/M43*1000
+
+%V%%R%%D0%88
+%V%%R%H46/M46*1000
+
+%V%%R%%D0%18
+%V%%R%H49/M49*1000
+
+%V%%R%%D0%24
+%V%%R%H52/M52*1000
+
+%V%%R%%D0%28
+%V%%R%H55/M55*1000
+
+%V%%R%%D0%16
+%V%%R%H58/M58*1000
+
+%V%%R%%D0%146
+%V%%R%H61/M61*1000
+
+%V%%R%%D0%24
+%V%%R%H64/M64*1000
+
+%V%%R%%D0%40
+%V%%R%H67/M67*1000
+
+%V%%R%%D0%56
+%V%%R%H70/M70*1000
+
+%V%%R%%D0%24
+%V%%R%H73/M73*1000
+
+%V%%R%%D0%24
+%V%%R%H76/M76*1000
+
+
+
+
+
+%V%%R%%D0%3
+
+
+%V%%R%%D0%3
+
+%V%%R%%D0%2
+
+%V%%R%%D0%1
+
+%V%%R%%D0%11
+
+%V%%R%%D0%1
+
+%V%%R%%D0%0
+
+%V%%R%%D0%0
+
+%V%%R%%D0%25
+
+%V%%R%%D0%1
+
+
+%V%%R%%D0%1
+
+
+%V%%R%%D0%37
+
+
+%V%%R%%D0%3
+
+
+%V%%R%%D0%3
+%CO:I,6,10%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+   Total
+
+%V%%R%%D0%sum(C34H34)
+%V%%R%I34/M34*1000
+
+%V%%R%%D0%sum(C37H37)
+%V%%R%I37/M37*1000
+
+%V%%R%%D0%sum(C40H40)
+%V%%R%I40/M40*1000
+
+%V%%R%%D0%sum(C43H43)
+%V%%R%I43/M43*1000
+
+%V%%R%%D0%sum(C46H46)
+%V%%R%I46/M46*1000
+
+%V%%R%%D0%sum(C49H49)
+%V%%R%I49/M49*1000
+
+%V%%R%%D0%sum(C52H52)
+%V%%R%I52/M52*1000
+
+%V%%R%%D0%sum(C55H55)
+%V%%R%I55/M55*1000
+
+%V%%R%%D0%sum(C58H58)
+%V%%R%I58/M58*1000
+
+%V%%R%%D0%sum(C61H61)
+%V%%R%I61/M61*1000
+
+%V%%R%%D0%sum(C64H64)
+%V%%R%I64/M64*1000
+
+%V%%R%%D0%sum(C67H67)
+%V%%R%I67/M67*1000
+
+%V%%R%%D0%sum(C70H70)
+%V%%R%I70/M70*1000
+
+%V%%R%%D0%sum(C73H73)
+%V%%R%I73/M73*1000
+
+%V%%R%%D0%sum(C76H76)
+%V%%R%I76/M76*1000
+
+
+
+
+
+%V%%R%%D0%sum(C83H83)
+
+
+%V%%R%%D0%sum(C86H86)
+
+%V%%R%%D0%sum(C88H88)
+
+%V%%R%%D0%sum(C90H90)
+
+%V%%R%%D0%sum(C92H92)
+
+%V%%R%%D0%sum(C94H94)
+
+%V%%R%%D0%sum(C96H96)
+
+%V%%R%%D0%sum(C98H98)
+
+%V%%R%%D0%sum(C100H100)
+
+%V%%R%%D0%sum(C102H102)
+
+
+%V%%R%%D0%sum(C105H105)
+
+
+%V%%R%%D0%sum(C108H108)
+
+
+%V%%R%%D0%sum(C111H111)
+
+
+%V%%R%%D0%sum(C114H114)
+%CO:J,4,0%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+pix
+usec
+
+pix
+usec
+
+pix
+usec
+
+pix
+usec
+
+pix
+usec
+
+pix
+usec
+
+pix
+usec
+
+pix
+usec
+
+pix
+usec
+
+pix
+usec
+
+pix
+usec
+
+pix
+usec
+
+pix
+usec
+
+pix
+usec
+
+pix
+usec
+%CO:K,6,42%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  H Freq
+   KHz
+%V%%R%%D3%1000/I35
+
+
+%V%%R%%D3%1000/I38
+
+
+%V%%R%%D3%1000/I41
+
+
+%V%%R%%D3%1000/I44
+
+
+%V%%R%%D3%1000/I47
+
+
+%V%%R%%D3%1000/I50
+
+
+%V%%R%%D3%1000/I53
+
+
+%V%%R%%D3%1000/I56
+
+
+%V%%R%%D3%1000/I59
+
+
+%V%%R%%D3%1000/I62
+
+
+%V%%R%%D3%1000/I65
+
+
+%V%%R%%D3%1000/I68
+
+
+%V%%R%%D3%1000/I71
+
+
+%V%%R%%D3%1000/I74
+
+
+%V%%R%%D3%1000/I77
+
+
+
+
+
+
+%V%%R%1000000/I35/I83
+
+
+%V%%R%1000000/I38/I86
+
+%V%%R%1000000/I44/I88
+
+%V%%R%1000000/I47/I90
+
+%V%%R%1000000/I50/I92
+
+%V%%R%1000000/I53/I94
+
+%V%%R%1000000/I56/I96
+
+%V%%R%1000000/I59/I98
+
+%V%%R%1000000/I62/I100
+
+%V%%R%1000000/I65/I102
+
+
+%V%%R%1000000/I68/I105
+
+
+%V%%R%1000000/I71/I108
+
+
+%V%%R%1000000/I74/I111
+
+
+%V%%R%1000000/I77/I114
+%CO:L,6,32%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+   disp
+   cntr
+   usec
+%V%%R%(C34+D34+E34+F34/2)/M34*1000
+
+
+%V%%R%(C37+D37+E37+F37/2)/M37*1000
+
+
+%V%%R%(C40+D40+E40+F40/2)/M40*1000
+
+
+%V%%R%(C43+D43+E43+F43/2)/M43*1000
+
+
+%V%%R%(C46+D46+E46+F46/2)/M46*1000
+
+
+%V%%R%(C49+D49+E49+F49/2)/M49*1000
+
+
+%V%%R%(C52+D52+E52+F52/2)/M52*1000
+
+
+%V%%R%(C55+D55+E55+F55/2)/M55*1000
+
+
+%V%%R%(C58+D58+E58+F58/2)/M58*1000
+
+
+%V%%R%(C61+D61+E61+F61/2)/M61*1000
+
+
+%V%%R%(C64+D64+E64+F64/2)/M64*1000
+
+
+%V%%R%(C67+D67+E67+F67/2)/M67*1000
+
+
+%V%%R%(C70+D70+E70+F70/2)/M70*1000
+
+
+%V%%R%(C73+D73+E73+F73/2)/M73*1000
+
+
+%V%%R%(C76+D76+E76+F76/2)/M76*1000
+
+
+
+
+
+
+Hz
+
+
+Hz
+
+Hz
+
+Hz
+
+Hz
+
+Hz
+
+Hz
+
+Hz
+
+Hz
+
+Hz
+
+
+Hz
+
+
+Hz
+
+
+Hz
+
+
+Hz
+%CO:M,6,22%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+   pix
+ clock
+  KHz
+%V%%R%%D0%16000
+
+
+%V%%R%%D0%8000
+
+
+%V%%R%%D0%24000
+
+
+%V%%R%%D0%16000
+
+
+%V%%R%%D0%24000
+
+
+%V%%R%%D0%25175
+
+
+%V%%R%%D0%36000
+
+
+%V%%R%%D0%24000
+
+
+%V%%R%%D0%2*25175/3
+
+
+%V%%R%%D0%16000
+
+
+%V%%R%%D0%36000
+
+
+%V%%R%%D0%40000
+
+
+%V%%R%%D0%50000
+
+
+%V%%R%%D0%65000
+
+
+%V%%R%%D0%75000
+
+
+
+
+   vert
+  center
+%V%%R%%D0%(C83+D83+E83+F83/2)
+
+
+%V%%R%%D0%(C86+D86+E86+F86/2)
+
+%V%%R%%D0%(C88+D88+E88+F88/2)
+
+%V%%R%%D0%(C90+D90+E90+F90/2)
+
+%V%%R%%D0%(C92+D92+E92+F92/2)
+
+%V%%R%%D0%(C94+D94+E94+F94/2)
+
+%V%%R%%D0%(C96+D96+E96+F96/2)
+
+%V%%R%%D0%(C98+D98+E98+F98/2)
+
+%V%%R%%D0%(C100+D100+E100+F100/2)
+
+%V%%R%%D0%(C102+D102+E102+F102/2)
+
+
+%V%%R%%D0%(C105+D105+E105+F105/2)
+
+
+%V%%R%%D0%(C108+D108+E108+F108/2)
+
+
+%V%%R%%D0%(C111+D111+E111+F111/2)
+
+
+%V%%R%%D0%(C114+D114+E114+F114/2)
+%CO:N,8,0%
\ No newline at end of file
diff --git a/HelpStrs b/HelpStrs
new file mode 100644
index 0000000000000000000000000000000000000000..cc9947038e83a7e8d4a8d0c3efc61d5f2c2ce94d
--- /dev/null
+++ b/HelpStrs
@@ -0,0 +1,544 @@
+        SUBT    => HelpStrs
+
+ [ International_Help=0
+Break_Help
+     =   "The ",TokenEscapeChar,Token0," key either acts like Escape, or"
+     =   " like the Reset key. ",13
+
+Reset_Help
+     =   "When Reset is pressed, the following "
+     =   " keys have an effect:",13
+     =   "SHIFT causes an autoboot (unless Boot is configured).",13
+     =   "CTRL causes more of the machine to be reset.",13
+     =   "keypad-* causes the supervisor to be run rather than the configured"
+     =   " language.",13
+     =   "See also *Help PowerOn.",0
+
+PowerOn_Help
+     =   "When the machine is switched on, the following "
+     =   " keys have an effect:",13
+     =   "R causes the operating system's CMOS RAM to be reset.",13
+     =   "DELETE causes all the machine's CMOS RAM to be reset.",13
+     =   "T and COPY are similar to R and DELETE, but set the opposite"
+     =   " configured sync.",13
+     =   "0 to 9 on the keypad reset (just)"
+     =   " the configured monitor type."
+     =   " Keypad dot sets the configured mode, sync and monitor type to Auto."
+     =  13,"See also *Help Reset and *Help Break."0
+
+RMEnsure_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " checks that a module is present and is the given version,"
+     =   " or a more modern one."
+     =   " The command is executed if this is not the case.",13
+RMEnsure_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <moduletitle> <version number> [<*command>]",0
+
+Syntax_Help
+     =   "Symbols used in syntax descriptions:",13
+     =   "<> mark sections to be filled in, eg. <filename> indicates"
+     =   " that a filename should be supplied here.",13
+     =   "[] mark optional sections.",13
+     =   "| indicates a choice, e.g. ""A|B"" means ""A or B"".", 0
+
+Quit_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " leaves the current application.",13
+
+GOS_Syntax
+Modules_Syntax
+Quit_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0, 0
+
+RMFaster_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " moves a module from ROM to RAM.",13
+RMFaster_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <moduletitle>", 0
+
+RMKill_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " kills and deletes a relocatable module.",13
+RMKill_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <moduletitle>", 0
+
+RMLoad_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " loads and initialises a relocatable module.",13
+RMLoad_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <filename>", 0
+
+RMRun_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " runs a relocatable module.",13
+RMRun_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <filename>", 0
+
+RMTidy_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " compacts the RMA and reinitialises all the modules.",13
+RMTidy_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0, 0
+
+RMClear_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " deletes all relocatable modules from the RMA.",13
+RMClear_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0, 0
+
+RMReInit_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " reinitialises a relocatable module,"
+     =   " reversing the action of *Unplug if appropriate.",13
+RMReInit_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <moduletitle>", 0
+
+ROMModules_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " lists the relocatable modules currently in ROM, along with"
+     =   " their status.",13
+     =   "See also *Modules.",13
+ROMModules_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0, 0
+
+Set_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " assigns a string value to a system variable."
+     =   " Other types of value can be assigned with *",TokenEscapeChar,Token0
+     =   "Eval and *",TokenEscapeChar,Token0
+     =   "Macro.",13
+Set_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <varname> <value>", 0
+
+SetMacro_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " assigns a macro value to a system variable."
+     =   " Other types of value can be assigned with *Set and *SetEval.",13
+SetMacro_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <varname> <value>", 0
+
+SetEval_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " evaluates an expression and assigns it to a system variable."
+     =   " Other types of value can be assigned with *Set and *SetMacro.",13
+     =   """*Help Eval"" describes the expression syntax.",13
+SetEval_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <varname> <expression>", 0
+
+Show_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " lists system variables matching the name given,"
+     =   " or all system variables if no name is specified."
+     =   " Variables can be set with *Set, *SetEval and *SetMacro.",13
+Show_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " [<variablespec>]", 0
+
+Time_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " displays the time and date.",13
+Time_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0,0
+
+Unset_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " deletes a system variable.",13
+Unset_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <varname>", 0
+
+Echo_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " sends a string to the VDU, after transformation by GSRead.",13
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <string>",0
+
+Ignore_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " sets the printer ignore character.",13
+Ignore_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " [<number>]",0
+
+IF_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " conditionally executes another command"
+     =   " depending on the value of an expression.",13
+IF_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <expression> THEN <command> [ELSE <command>]", 0
+
+Status_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " shows the selected CMOS RAM options."
+     =   " Use *Configure to set the options.",13
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " [<option>]",0
+
+Unplug_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " stops the given ROM module being initialised.",13
+     =   "*",TokenEscapeChar,Token0
+     =   " with no argument lists the unplugged ROM modules.",13
+Unplug_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " [<moduletitle> [<podule number>]]",0
+
+RMInsert_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " reverses the effect of *Unplug, but does not reinitialise"
+     =   " the specified ROM module.",13
+RMInsert_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <moduletitle> [<podule number>]",0
+
+Error_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " generates an error with the given number and text. ",13
+Error_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " [<number>] <text>",0
+
+Eval_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " evaluates an integer or string expression. ",13
+     =   "The expression analyser has the following operators:",13
+     =   "+",9,9,9,"addition or string concatenation",13
+     =   "-, *, /, MOD",9,9,"integer operations",13
+     =   "=, <, >, <=, >=, <>",9,"string or integer comparison",13
+     =   ">>, <<",9,9,9,"arithmetic shift right and left",13
+     =   ">>>",9,9,9,"logical shift right",13
+     =   "STR, VAL",9,9,"conversion between strings and integers",13
+     =   "AND, OR, EOR, NOT",9,"(bitwise) logical operators",13
+     =   "RIGHT, LEFT",9,9,"substring extraction",13
+     =   "LEN",9,9,9,"length of a string",13,13
+     =   "Brackets can also be used.", 0
+Eval_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <expression>",0
+
+GO_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " [<address>] [; environment] "
+     =   " - go to address (hexadecimal), default &8000. "
+     =   " Text after ';' is environment string.", 0
+
+GOS_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " enters the supervisor. Use *Quit to exit.", 0
+
+Append_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " opens an existing file and subsequent lines of keyboard input are"
+     =   " appended to it, input being terminated by ESCAPE.",13
+Append_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <filename>", 0
+
+Build_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " opens a new file and subsequent lines of keyboard input are"
+     =   " directed to it, input being terminated by ESCAPE.",13
+Build_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <filename>", 0
+
+Close_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " closes all files on the current filing system.",13
+Close_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0,0
+
+Create_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " reserves space for the named file, optionally giving it"
+     =   " load and execution addresses. No data is transferred to the file."
+     =   " Length and addresses are in hexadecimal.",13
+Create_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <filename> [<length> [<exec addr> [<load addr>]]]", 0
+
+Delete_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " tries to delete the named file, returning an error if the file"
+     =   " does not exist.",13
+     =   "See also *Remove and *Wipe.",13
+Delete_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <filename>", 0
+
+Dump_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " displays the contents of the file as a hex and ASCII dump."
+     =   " The file offset and start address are in hexadecimal.",13
+Dump_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <filename> [<file offset> [<start address>]]", 0
+
+Exec_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " <filename> directs the operating system to take further input"
+     =   " from the given file.",13
+     =   "*",TokenEscapeChar,Token0
+     =   " with no filename causes the exec file to be closed.",13
+Exec_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " [<filename>]", 0
+
+FX_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " r0 [[,] r1 [[,] r2]] calls OS_Byte.", 0
+
+FX_Syntax
+     =   "*",TokenEscapeChar,Token0
+     =   " needs 1 to 3 numeric parameters.", 0
+
+Key_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " sets the function keys.",13
+Key_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <keynumber> [<value>]", 0
+
+List_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " displays the contents of the file in the configured GSRead format."
+     =   " Each line is preceded with a line number.",13
+     =   "See also *Print and *Type.",13
+List_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " [-File] <filename> [-TabExpand]", 0
+
+Load_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " with no specified address loads the named file at its own"
+     =   " load address. If a load address (hexadecimal) is specified,"
+     =   " it will be used instead.",13
+Load_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <filename> [<load addr>]", 0
+
+Print_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " displays the contents of a file by sending each byte to the VDU.",13
+     =   "See also *List and *Type.",13
+Print_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <filename>", 0
+
+Opt_Help
+        =       "*"
+        =       TokenEscapeChar,Token0
+        =       " controls various filing system actions."
+        =       13,"Opt 1 <n>",31
+        =       "Set the filing system message level (for Load/Save/Create):"
+        =       13,9,"0",9,"No filing system messages"
+        =       13,9,"1",9,"Filename printed"
+        =       13,9,"2",9,"Filename,hexadecimal addresses and length printed"
+        =       13,9,"3",9,"Filename,datestamp and length printed"
+        =       13,"Opt 4 <n>",31
+        =       "Set the filing system boot option:"
+        =       13,9,"0",9,"No boot action"
+        =       13,9,"1",9,"*Load boot file"
+        =       13,9,"2",9,"*Run boot file"
+        =       13,9,"3",9,"*Exec boot file"
+        =       13
+Opt_Syntax
+        =       "Syntax: *"
+        =       TokenEscapeChar,Token0
+        =       " [<x> [[,] <y>]]",0
+
+Remove_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " tries to delete the named file without returning an error"
+     =   " if the file does not exist.",13
+     =   "See also *Delete and *Wipe.",13
+Remove_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <filename>", 0
+
+Save_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " copies the given area of memory to the named file."
+     =   " Length and addresses are in hexadecimal.",13
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " <filename> <start addr>"
+     =   " <end addr> [<exec addr> [<load addr>]]",13
+     =   31, 31, 31, 31, " or *",TokenEscapeChar,Token0
+     =   " <filename> <start addr>"
+     =   " + <length> [<exec addr> [<load addr>]]", 0
+
+Shadow_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " makes subsequent mode changes use the other screen bank.",13
+Shadow_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " [0|1]", 0
+
+Spool_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " <filename> opens a new file and causes subsequent VDU output"
+     =   " to be directed to it, subject to the current *fx 3 status.",13
+     =   "*",TokenEscapeChar,Token0
+     =   " with no filename causes the spool file to be closed.",13
+Spool_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " [<filename>]", 0
+
+SpoolOn_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " <filename> opens an existing file and causes subsequent VDU output"
+     =   " to be appended to it, subject to the current *fx 3 status.",13
+     =   "*",TokenEscapeChar,Token0
+     =   " with no filename causes the spool file to be closed.",13
+SpoolOn_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " [<filename>]", 0
+
+TV_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " [<vertical position> [[,] <interlace>]]"
+     =   " sets the position of the display on the screen.", 0
+
+TV_Syntax
+     =   "*",TokenEscapeChar,Token0
+     =   " needs 0 to 2 parameters.", 0
+
+Type_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " displays the contents of the file"
+     =   " in the configured GSRead format.",13
+     =   "See also *List and *Print.",13
+Type_Syntax
+     =   "Syntax: *",TokenEscapeChar,Token0
+     =   " [-File] <filename> [-TabExpand]", 0
+
+Help_Help
+     =   "*",TokenEscapeChar,Token0
+     =   " <subjects> attempts to give useful information"
+     =   " on the selected topics. Special keywords include:",13
+     =   "Commands", 9, "List all the available utility commands",13
+     =   "FileCommands", 9
+     =   "List all the filing system-specific commands",13
+     =   "Modules", 9, 9, "List the module titles",13
+     =   "Syntax", 9, 9, "Explain the syntax message format",0
+ |
+Break_Help      DCB     "HUTMBRK", 0
+Reset_Help      DCB     "HUTMRES", 0
+PowerOn_Help    DCB     "HUTMPOW", 0
+RMEnsure_Help   DCB     "HUTMRME", 0
+RMEnsure_Syntax DCB     "SUTMRME", 0
+Syntax_Help     DCB     "HUTMSYN", 0
+Quit_Help       DCB     "HUTMQUI", 0
+GOS_Syntax      DCB     "SUTMGOS", 0
+Modules_Syntax  DCB     "SUTMMOD", 0
+Quit_Syntax     DCB     "SUTMQUI", 0
+RMFaster_Help   DCB     "HUTMRMF", 0
+RMFaster_Syntax DCB     "SUTMRMF", 0
+RMKill_Help     DCB     "HUTMRMK", 0
+RMKill_Syntax   DCB     "SUTMRMK", 0
+RMLoad_Help     DCB     "HUTMRML", 0
+RMLoad_Syntax   DCB     "SUTMRML", 0
+RMRun_Help      DCB     "HUTMRRN", 0
+RMRun_Syntax    DCB     "SUTMRRN", 0
+RMTidy_Help     DCB     "HUTMRMT", 0
+RMTidy_Syntax   DCB     "SUTMRMT", 0
+RMClear_Help    DCB     "HUTMRMC", 0
+RMClear_Syntax  DCB     "SUTMRMC", 0
+RMReInit_Help   DCB     "HUTMRMR", 0
+RMReInit_Syntax DCB     "SUTMRMR", 0
+ROMModules_Help   DCB   "HUTMROM", 0
+ROMModules_Syntax DCB   "SUTMROM", 0
+Set_Help        DCB     "HUTMSET", 0
+Set_Syntax      DCB     "SUTMSET", 0
+SetMacro_Help   DCB     "HUTMSTM", 0
+SetMacro_Syntax DCB     "SUTMSTM", 0
+SetEval_Help    DCB     "HUTMSTE", 0
+SetEval_Syntax  DCB     "SUTMSTE", 0
+Show_Help       DCB     "HUTMSHO", 0
+Show_Syntax     DCB     "SUTMSHO", 0
+Time_Help       DCB     "HUTMTIM", 0
+Time_Syntax     DCB     "SUTMTIM", 0
+Unset_Help      DCB     "HUTMUNS", 0
+Unset_Syntax    DCB     "SUTMUNS", 0
+Echo_Help       DCB     "HUTMECH", 0
+Ignore_Help     DCB     "HUTMIGN", 0
+Ignore_Syntax   DCB     "SUTMIGN", 0
+IF_Help         DCB     "HUTMIF", 0
+IF_Syntax       DCB     "SUTMIF", 0
+Status_Help     DCB     "HUTMSTA", 0
+Unplug_Help     DCB     "HUTMUNP", 0
+Unplug_Syntax   DCB     "SUTMUNP", 0
+RMInsert_Help   DCB     "HUTMRMI", 0
+RMInsert_Syntax DCB     "SUTMRMI", 0
+Error_Help      DCB     "HUTMERR", 0
+Error_Syntax    DCB     "SUTMERR", 0
+Eval_Help       DCB     "HUTMEVL", 0
+Eval_Syntax     DCB     "SUTMEVL", 0
+GO_Help         DCB     "HUTMGO", 0
+GOS_Help        DCB     "HUTMGOS", 0
+Append_Help     DCB     "HUTMAPP", 0
+Append_Syntax   DCB     "SUTMAPP", 0
+Build_Help      DCB     "HUTMBUI", 0
+Build_Syntax    DCB     "SUTMBUI", 0
+Close_Help      DCB     "HUTMCLO", 0
+Close_Syntax    DCB     "SUTMCLO", 0
+Create_Help     DCB     "HUTMCRE", 0
+Create_Syntax   DCB     "SUTMCRE", 0
+Delete_Help     DCB     "HUTMDEL", 0
+Delete_Syntax   DCB     "SUTMDEL", 0
+Dump_Help       DCB     "HUTMDMP", 0
+Dump_Syntax     DCB     "SUTMDMP", 0
+Exec_Help       DCB     "HUTMEXE", 0
+Exec_Syntax     DCB     "SUTMEXE", 0
+FX_Help         DCB     "HUTMFX", 0
+FX_Syntax       DCB     "SUTMFX", 0
+Key_Help        DCB     "HUTMKEY", 0
+Key_Syntax      DCB     "SUTMKEY", 0
+List_Help       DCB     "HUTMLST", 0
+List_Syntax     DCB     "SUTMLST", 0
+Load_Help       DCB     "HUTMLOD", 0
+Load_Syntax     DCB     "SUTMLOD", 0
+Print_Help      DCB     "HUTMPRI", 0
+Print_Syntax    DCB     "SUTMPRI", 0
+Opt_Help        DCB     "HUTMOPT", 0
+Opt_Syntax      DCB     "SUTMOPT", 0
+Remove_Help     DCB     "HUTMREM", 0
+Remove_Syntax   DCB     "SUTMREM", 0
+Save_Help       DCB     "HUTMSAV", 0
+Save_Syntax     DCB     "SUTMSAV", 0
+Shadow_Help     DCB     "HUTMSHA", 0
+Shadow_Syntax   DCB     "SUTMSHA", 0
+Spool_Help      DCB     "HUTMSPL", 0
+Spool_Syntax    DCB     "SUTMSPL", 0
+SpoolOn_Help    DCB     "HUTMSPO", 0
+SpoolOn_Syntax  DCB     "SUTMSPO", 0
+TV_Help         DCB     "HUTMTV", 0
+TV_Syntax       DCB     "SUTMTV", 0
+Type_Help       DCB     "HUTMTYP", 0
+Type_Syntax     DCB     "SUTMTYP", 0
+Help_Help       DCB     "HUTMHLP", 0
+ ]
+
+modules_help1   DCB     "HUTMMOD", 0
+commands_helpstr
+                DCB     "HUTMCOM", 0
+fscommands_helpstr
+                DCB     "HUTMFSC", 0
+modjack_hstr    DCB     "HUTMMD", 0
+modjack_comms   DCB     "HUTMCM", 0
+modjack_filecomms
+                DCB     "HUTMFS", 0
+modjack_confs   DCB     "HUTMCK", 0
+modjack_aob     DCB     "HUTMHK", 0
+
+        END
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..2645f117307f6219900406431c200c0380fafc9f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,109 @@
+# Copyright 1996 Acorn Computers Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Makefile for Kernel
+#
+# ***********************************
+# ***    C h a n g e   L i s t    ***
+# ***********************************
+# Date       Name         Description
+# ----       ----         -----------
+# 25-May-94  AMcC         Created.
+#
+
+#
+# Paths
+#
+EXP_HDR = <export$dir>
+
+#
+# Generic options:
+#
+MKDIR   = cdir
+AS      = aasm
+CP      = copy
+RM      = remove
+CCFLAGS = -c -depend !Depend -IC:
+ASFLAGS = -depend !Depend -Stamp -quit -To $@ -From
+CPFLAGS = ~cfr~v
+
+TOKENISE = tokenise
+TOKENS   = Hdr:Tokens
+
+#
+# Program specific options:
+#
+COMPONENT = Kernel
+SOURCE    = s.GetAll
+TARGET    = rm.${MACHINE}.Kernel
+EXPORTS   = ${EXP_HDR}.EnvNumbers \
+            ${EXP_HDR}.ModHand \
+            ${EXP_HDR}.PublicWS \
+            ${EXP_HDR}.RISCOS \
+            ${EXP_HDR}.Variables \
+            ${EXP_HDR}.VduExt
+
+#
+# Generic rules:
+#
+rom: ${TARGET}
+	@echo ${COMPONENT}: rom module built
+
+install_rom: ${TARGET}
+	${CP} ${TARGET} ${INSTDIR}.${COMPONENT} ${CPFLAGS}
+	@echo ${COMPONENT}: rom module installed
+
+clean:
+	${RM} ${TARGET}
+	${RM} TMOSHelp
+	@echo ${COMPONENT}: cleaned
+
+export: ${EXPORTS}
+	@echo ${COMPONENT}: export complete
+
+resources:
+	${MKDIR} ${RESDIR}.${COMPONENT}
+	${CP} Resources.${LOCALE}.Messages  ${RESDIR}.${COMPONENT}.Messages  ${CPFLAGS}
+	@echo ${COMPONENT}: resource files copied
+
+${TARGET}: ${SOURCE} s.TMOSHelp
+	${MKDIR} rm.${MACHINE}
+	${AS} ${ASFLAGS} ${SOURCE}
+	stamp $@
+
+s.TMOSHelp: ${TOKENS} HelpStrs
+	${TOKENISE} ${TOKENS} HelpStrs $@
+
+#
+# Exported interface headers
+#
+${EXP_HDR}.EnvNumbers: hdr.EnvNumbers
+	${CP} hdr.EnvNumbers $@ ${CPFLAGS}
+
+${EXP_HDR}.ModHand: hdr.ModHand
+	${CP} hdr.ModHand $@ ${CPFLAGS}
+
+${EXP_HDR}.PublicWS: hdr.PublicWS
+	${CP} hdr.PublicWS $@ ${CPFLAGS}
+
+${EXP_HDR}.RISCOS: hdr.RISCOS
+	${CP} hdr.RISCOS $@ ${CPFLAGS}
+
+${EXP_HDR}.VduExt: hdr.VduExt
+	${CP} hdr.VduExt $@ ${CPFLAGS}
+
+${EXP_HDR}.Variables: hdr.Variables
+	${CP} hdr.Variables $@ ${CPFLAGS}
+
+# Dynamic dependencies:
diff --git a/MkClean,fd7 b/MkClean,fd7
new file mode 100644
index 0000000000000000000000000000000000000000..e9d7ade7fd4d0f5126fb63aca74d820946350a83
--- /dev/null
+++ b/MkClean,fd7
@@ -0,0 +1,16 @@
+| Copyright 1996 Acorn Computers Ltd
+|
+| Licensed under the Apache License, Version 2.0 (the "License");
+| you may not use this file except in compliance with the License.
+| You may obtain a copy of the License at
+|
+|     http://www.apache.org/licenses/LICENSE-2.0
+|
+| Unless required by applicable law or agreed to in writing, software
+| distributed under the License is distributed on an "AS IS" BASIS,
+| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+| See the License for the specific language governing permissions and
+| limitations under the License.
+|
+Dir <Obey$Dir>
+amu_machine clean
diff --git a/MkExport,fd7 b/MkExport,fd7
new file mode 100644
index 0000000000000000000000000000000000000000..9e5f66689f85c8dd747aa9baa51c30714524a7b1
--- /dev/null
+++ b/MkExport,fd7
@@ -0,0 +1,16 @@
+| Copyright 1996 Acorn Computers Ltd
+|
+| Licensed under the Apache License, Version 2.0 (the "License");
+| you may not use this file except in compliance with the License.
+| You may obtain a copy of the License at
+|
+|     http://www.apache.org/licenses/LICENSE-2.0
+|
+| Unless required by applicable law or agreed to in writing, software
+| distributed under the License is distributed on an "AS IS" BASIS,
+| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+| See the License for the specific language governing permissions and
+| limitations under the License.
+|
+Dir <Obey$Dir>
+amu_machine export
diff --git a/MkRom,fd7 b/MkRom,fd7
new file mode 100644
index 0000000000000000000000000000000000000000..42cd1103e1a784e03f2ad8dff2bf4b4435fee5ce
--- /dev/null
+++ b/MkRom,fd7
@@ -0,0 +1,16 @@
+| Copyright 1996 Acorn Computers Ltd
+|
+| Licensed under the Apache License, Version 2.0 (the "License");
+| you may not use this file except in compliance with the License.
+| You may obtain a copy of the License at
+|
+|     http://www.apache.org/licenses/LICENSE-2.0
+|
+| Unless required by applicable law or agreed to in writing, software
+| distributed under the License is distributed on an "AS IS" BASIS,
+| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+| See the License for the specific language governing permissions and
+| limitations under the License.
+|
+Dir <Obey$Dir>
+amu_machine rom
diff --git a/NewModes/Make,feb b/NewModes/Make,feb
new file mode 100644
index 0000000000000000000000000000000000000000..50fa3365299802aa617dcfa4765b7affb0507af1
--- /dev/null
+++ b/NewModes/Make,feb
@@ -0,0 +1,17 @@
+| Copyright 1996 Acorn Computers Ltd
+|
+| Licensed under the Apache License, Version 2.0 (the "License");
+| you may not use this file except in compliance with the License.
+| You may obtain a copy of the License at
+|
+|     http://www.apache.org/licenses/LICENSE-2.0
+|
+| Unless required by applicable law or agreed to in writing, software
+| distributed under the License is distributed on an "AS IS" BASIS,
+| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+| See the License for the specific language governing permissions and
+| limitations under the License.
+|
+| > NewModes.Make
+WimpSlot -min 1024k -max 1024k
+AASM <Obey$Dir>.PSSrc <Obey$Dir>.PSModule -module -quit
diff --git a/NewModes/NEWF2 b/NewModes/NEWF2
new file mode 100644
index 0000000000000000000000000000000000000000..cf992a1d6ddca7325db060c9c538f7fefa7dd299
--- /dev/null
+++ b/NewModes/NEWF2
@@ -0,0 +1,144 @@
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; general purpose mode macros
+
+ClockShift * 9
+SyncShift * 11
+
+; pixel rate specifiers
+
+CRPix_24000 * 3 :OR: (0 :SHL: ClockShift)
+CRPix_16000 * 2 :OR: (0 :SHL: ClockShift)
+CRPix_12000 * 1 :OR: (0 :SHL: ClockShift)
+CRPix_8000  * 0 :OR: (0 :SHL: ClockShift)
+CRPix_25175 * 3 :OR: (1 :SHL: ClockShift)
+CRPix_36000 * 3 :OR: (2 :SHL: ClockShift)
+
+        MACRO
+        VIDC_List $lbpp,$hsync,$hbpch,$hlbdr,$hdisp,$hrbdr,$hfpch, $vsync,$vbpch,$vlbdr,$vdisp,$vrbdr,$vfpch,$pixrate,$sp
+        LCLA    sub
+        LCLA    syncpol
+ [ $lbpp = 3
+sub     SETA    5
+ ]
+ [ $lbpp = 2
+sub     SETA    7
+ ]
+ [ $lbpp = 1
+sub     SETA    11
+ ]
+ [ $lbpp = 0
+sub     SETA    19
+ ]
+ [ "$sp"=""
+syncpol SETA    0 :SHL: SyncShift               ; normal sync polarity
+ |
+        ASSERT $sp<=3
+syncpol SETA    $sp :SHL: SyncShift
+ ]
+        ASSERT  ($hsync :AND: 1)=0
+        ASSERT  ($hbpch :AND: 1)=1
+        ASSERT  ($hlbdr :AND: 1)=0
+        ASSERT  ($hdisp :AND: 1)=0
+        ASSERT  ($hrbdr :AND: 1)=0
+        ASSERT  ($hfpch :AND: 1)=1
+ [ (($hsync+$hbpch+$hlbdr+$hdisp+$hrbdr+$hfpch) :AND: 3)<>0
+        ! 0, "Warning: mode unsuitable for interlaced use"
+ ]
+; Horizontal
+        &       (&80:SHL:24) :OR: ((($hsync+$hbpch+$hlbdr+$hdisp+$hrbdr+$hfpch -2  )/2) :SHL: 14) ; HCR
+        &       (&84:SHL:24) :OR: ((($hsync                                    -2  )/2) :SHL: 14) ; HSWR
+        &       (&88:SHL:24) :OR: ((($hsync+$hbpch                             -1  )/2) :SHL: 14) ; HBSR
+        &       (&8C:SHL:24) :OR: ((($hsync+$hbpch+$hlbdr                      -sub)/2) :SHL: 14) ; HDSR
+        &       (&90:SHL:24) :OR: ((($hsync+$hbpch+$hlbdr+$hdisp               -sub)/2) :SHL: 14) ; HDER
+        &       (&94:SHL:24) :OR: ((($hsync+$hbpch+$hlbdr+$hdisp+$hrbdr        -1  )/2) :SHL: 14) ; HBER
+        &       (&9C:SHL:24) :OR: (((($hsync+$hbpch+$hlbdr+$hdisp+$hrbdr+$hfpch-2)/2+1)/2):SHL:14); HIR
+; Vertical
+        &       (&A0:SHL:24) :OR: (($vsync+$vbpch+$vlbdr+$vdisp+$vrbdr+$vfpch    -1)    :SHL: 14) ; VCR
+        &       (&A4:SHL:24) :OR: (($vsync                                       -1)    :SHL: 14) ; VSWR
+        &       (&A8:SHL:24) :OR: (($vsync+$vbpch                                -1)    :SHL: 14) ; VBSR
+        &       (&AC:SHL:24) :OR: (($vsync+$vbpch+$vlbdr                         -1)    :SHL: 14) ; VDSR
+        &       (&B0:SHL:24) :OR: (($vsync+$vbpch+$vlbdr+$vdisp                  -1)    :SHL: 14) ; VDER
+        &       (&B4:SHL:24) :OR: (($vsync+$vbpch+$vlbdr+$vdisp+$vrbdr           -1)    :SHL: 14) ; VBER
+; Control Register
+        &       (&E0:SHL:24) :OR: (CRPix_$pixrate) :OR: ($lbpp :SHL: 2) :OR: syncpol
+        &       -1
+        MEND
+
+        MACRO
+        VIDC_WS  $bpp,$hpix,$vpix,$multx,$multy, $dht
+
+        &       VduExt_XWindLimit, $hpix-1
+        &       VduExt_ScrRCol, ($hpix/8)-1
+        &       VduExt_LineLength, $hpix*$bpp/8
+ [ "$dht" <> ""
+        &       VduExt_ModeFlags, Flag_DoubleVertical
+        &       VduExt_ScrBRow, ($vpix/16)-1
+ |
+        &       VduExt_ScrBRow, ($vpix/8)-1
+ ]
+        &       VduExt_YWindLimit, $vpix-1
+        &       VduExt_ScreenSize, $hpix*$vpix*$bpp/8
+
+        &       VduExt_XEigFactor, $multx
+        &       VduExt_YEigFactor, $multy
+        MEND
+
+
+
+
+VLN_0   VIDC_List 0, 72,145, 48, 640, 48, 71, 3,18,18,256,17, 0,16000,0         ; MODE 0
+VLN_1   VIDC_List 1, 36, 73, 24, 320, 24, 35, 3,18,18,256,17, 0, 8000,0         ; MODE 1
+VLN_2   VIDC_List 2, 36, 73, 24, 320, 24, 35, 3,18,18,256,17, 0, 8000,0         ; MODE 2
+VLN_3   VIDC_List 1, 72,145, 48, 640, 48, 71, 3,18,22,250,19, 0,16000,0         ; MODE 3
+VLN_4   VIDC_List 0, 72,145, 48, 640, 48, 71, 3,18,18,256,17, 0,16000,0         ; MODE 4
+VLN_5   VIDC_List 1, 36, 73, 24, 320, 24, 35, 3,18,18,256,17, 0, 8000,0         ; MODE 5
+VLN_6   VIDC_List 1, 36, 73, 24, 320, 24, 35, 3,18,22,250,19, 0, 8000,0         ; MODE 6
+VLN_7   VIDC_List 2, 36, 73, 24, 320, 24, 35, 3,18,22,250,19, 0, 8000,0         ; MODE 7
+VLN_8   VIDC_List 1, 72,145, 48, 640, 48, 71, 3,18,18,256,17, 0,16000,0         ; MODE 8
+VLN_9   VIDC_List 2, 36, 73, 24, 320, 24, 35, 3,18,18,256,17, 0, 8000,0         ; MODE 9
+VLN_10  VIDC_List 3, 36, 73, 24, 320, 24, 35, 3,18,18,256,17, 0, 8000,0         ; MODE 10
+VLN_11  VIDC_List 1, 72,145, 48, 640, 48, 71, 3,18,22,250,19, 0,16000,0         ; MODE 11
+VLN_12  VIDC_List 2, 72,145, 48, 640, 48, 71, 3,18,18,256,17, 0,16000,0         ; MODE 12
+VLN_13  VIDC_List 3, 36, 73, 24, 320, 24, 35, 3,18,18,256,17, 0, 8000,0         ; MODE 13
+VLN_14  VIDC_List 2, 72,145, 48, 640, 48, 71, 3,18,22,250,19, 0,16000,0         ; MODE 14
+VLN_15  VIDC_List 3, 72,145, 48, 640, 48, 71, 3,18,18,256,17, 0,16000,0         ; MODE 15
+VLN_16  VIDC_List 2, 72,215, 46,1056, 46,101, 3,18,18,256,17, 0,24000,0         ; MODE 16
+VLN_17  VIDC_List 2, 72,215, 46,1056, 46,101, 3,18,22,250,19, 0,24000,0         ; MODE 17
+;VLN_18  VIDC_List 0, 56,183,  2, 640,  2, 13, 3,17, 1,512, 1, 0,24000,0         ; MODE 18
+;VLN_19  VIDC_List 1, 56,183,  2, 640,  2, 13, 3,17, 1,512, 1, 0,24000,0         ; MODE 19
+;VLN_20  VIDC_List 2, 56,183,  2, 640,  2, 13, 3,17, 1,512, 1, 0,24000,0         ; MODE 20
+;VLN_21  VIDC_List 3, 56,183,  2, 640,  2, 13, 3,17, 1,512, 1, 0,24000,0         ; MODE 21
+VLN_24  VIDC_List 3, 72,215, 46,1056, 46,101, 3,18,18,256,17, 0,24000,0         ; MODE 24
+VLN_33  VIDC_List 3, 74,127,  0, 768,  0, 55, 3,18, 0,288, 0, 3,16000,0         ; MODE 33
+VLN_34  VIDC_List 3, 74, 87,  0, 832,  0, 31, 3,18, 0,288, 0, 3,16000,0         ; MODE 34
+
+VLM_0   VIDC_List 0, 72, 63, 88, 640, 88, 73, 3,16,17,256,17, 3,16000,0         ; MODE 0
+VLM_1   VIDC_List 1, 36, 33, 44, 320, 44, 35, 3,16,17,256,17, 3, 8000,0         ; MODE 1
+VLM_2   VIDC_List 2, 36, 33, 44, 320, 44, 35, 3,16,17,256,17, 3, 8000,0         ; MODE 2
+VLM_3   VIDC_List 1, 72, 63, 88, 640, 88, 73, 3,16,20,250,20, 3,16000,0         ; MODE 3
+VLM_4   VIDC_List 0, 72, 63, 88, 640, 88, 73, 3,16,17,256,17, 3,16000,0         ; MODE 4
+VLM_5   VIDC_List 1, 36, 51, 24, 320, 24, 57, 3,16,17,256,17, 3, 8000,0         ; MODE 5
+VLM_6   VIDC_List 1, 36, 33, 44, 320, 44, 35, 3,16,20,250,20, 3, 8000,0         ; MODE 6
+VLM_7   VIDC_List 2, 36, 31, 44, 320, 44, 37, 3,18,22,250,16, 3, 8000,0         ; MODE 7
+VLM_8   VIDC_List 1, 72, 63, 88, 640, 88, 73, 3,16,17,256,17, 3,16000,0         ; MODE 8
+VLM_9   VIDC_List 2, 36, 33, 44, 320, 44, 35, 3,16,17,256,17, 3, 8000,0         ; MODE 9
+VLM_10  VIDC_List 3, 36, 33, 44, 320, 44, 35, 3,16,17,256,17, 3, 8000,0         ; MODE 10
+VLM_11  VIDC_List 1, 72, 63, 88, 640, 88, 73, 3,16,20,250,20, 3,16000,0         ; MODE 11
+VLM_12  VIDC_List 2, 72, 63, 88, 640, 88, 73, 3,16,17,256,17, 3,16000,0         ; MODE 12
+VLM_13  VIDC_List 3, 36, 33, 44, 320, 44, 35, 3,16,17,256,17, 3, 8000,0         ; MODE 13
+VLM_14  VIDC_List 2, 72, 63, 88, 640, 88, 73, 3,16,20,250,20, 3,16000,0         ; MODE 14
+VLM_15  VIDC_List 3, 72, 63, 88, 640, 88, 73, 3,16,17,256,17, 3,16000,0         ; MODE 15
+VLM_16  VIDC_List 2,112, 47,132,1056,132, 57, 3,16,17,256,17, 3,24000,0         ; MODE 16
+VLM_17  VIDC_List 2,112, 47,132,1056,132, 57, 3,16,20,250,20, 3,24000,0         ; MODE 17
+VLM_18  VIDC_List 0, 56,111,  2, 640,  2, 85, 3,17, 1,512, 1, 0,24000,0         ; MODE 18
+VLM_19  VIDC_List 1, 56,111,  2, 640,  2, 85, 3,17, 1,512, 1, 0,24000,0         ; MODE 19
+VLM_20  VIDC_List 2, 56,111,  2, 640,  2, 85, 3,17, 1,512, 1, 0,24000,0         ; MODE 20
+VLM_21  VIDC_List 3, 56,111,  2, 640,  2, 85, 3,17, 1,512, 1, 0,24000,0         ; MODE 21
+VLM_24  VIDC_List 3,112, 47,132,1056,132, 57, 3,16,17,256,17, 3,24000,0         ; MODE 24
+VLM_25  VIDC_List 0, 96, 47,  0, 640,  0, 15, 2,32, 0,480, 0,11,25175,3         ; MODE 25
+VLM_26  VIDC_List 1, 96, 47,  0, 640,  0, 15, 2,32, 0,480, 0,11,25175,3         ; MODE 26
+VLM_27  VIDC_List 2, 96, 47,  0, 640,  0, 15, 2,32, 0,480, 0,11,25175,3         ; MODE 27
+VLM_28  VIDC_List 3, 96, 47,  0, 640,  0, 15, 2,32, 0,480, 0,11,25175,3         ; MODE 28
+VLM_31  VIDC_List 2, 72,129,  0, 800,  0, 23, 2,22, 0,600, 0, 1,36000,0         ; MODE 31
+
+VLH_23  VIDC_List 2, 52, 47,  2, 288,  2,  1, 3,43, 4,896, 4, 0,24000,0         ; MODE 23
diff --git a/NewModes/NEWFORMAT b/NewModes/NEWFORMAT
new file mode 100644
index 0000000000000000000000000000000000000000..d0734fed9585bd87c462bfeb06175a6b00b4da86
--- /dev/null
+++ b/NewModes/NEWFORMAT
@@ -0,0 +1,89 @@
+ [ {FALSE}              ; This mode not supported by VIDC, so not used
+V32tab1
+        VIDC_List 0,36,73,24,320,24,35, 3,18,18,256,17,0,8000,0
+ ]
+
+V32tab2         ; MODES 1,5
+        VIDC_List 1,36,73,24,320,24,35, 3,18,18,256,17,0,8000,0
+
+V32tab2T        ; MODE 6
+        VIDC_List 1,36,73,24,320,24,35, 3,18,22,250,19,0,8000,0
+
+V32tab4         ; MODES 2,9
+        VIDC_List 2,36,73,24,320,24,35, 3,18,18,256,17,0,8000,0
+
+V32tab4T        ; MODE 7
+        VIDC_List 2,36,73,24,320,24,35, 3,18,22,250,19,0,8000,0
+
+V32tab8         ; MODES 10,13
+        VIDC_List 3,36,73,24,320,24,35, 3,18,18,256,17,0,8000,0
+
+
+V64tab1         ; MODES 0,4
+        VIDC_List 0,72,145,48,640,48,71, 3,18,18,256,17,0,16000,0
+
+V64tab2         ; MODE 8
+        VIDC_List 1,72,145,48,640,48,71, 3,18,18,256,17,0,16000,0
+
+V64tab2T        ; MODES 3,11
+        VIDC_List 1,72,145,48,640,48,71, 3,18,22,250,19,0,16000,0
+
+V64tab4         ; MODE 12
+        VIDC_List 2,72,145,48,640,48,71, 3,18,18,256,17,0,16000,0
+
+V64tab4T        ; MODE 14
+        VIDC_List 2,72,145,48,640,48,71, 3,18,22,250,19,0,16000,0
+
+V64tab8         ; MODE 15
+        VIDC_List 3,72,145,48,640,48,71, 3,18,18,256,17,0,16000,0
+
+V132tab4        ; MODE 16
+        VIDC_List 2,72,215,46,1056,46,101, 3,18,18,256,17,0,24000,0
+
+V132tab4T       ; MODE 17
+        VIDC_List 2,72,215,46,1056,46,101, 3,18,22,250,19,0,24000,0
+
+ [ {TRUE}
+V64tab1D        ; MODE 18
+        VIDC_List 0,56,183,2,640,2,13, 3,17,1,512,1,0,24000,0
+
+V64tab2D        ; MODE 19
+        VIDC_List 1,56,183,2,640,2,13, 3,17,1,512,1,0,24000,0
+
+V64tab4D        ; MODE 20
+        VIDC_List 2,56,183,2,640,2,13, 3,17,1,512,1,0,24000,0
+
+V64tab8D        ; MODE 21 (NEW)
+        VIDC_List 3,56,183,2,640,2,13, 3,17,1,512,1,0,24000,0
+
+ [ {FALSE} ; not used any more
+V128tab1        ; MODE 22      
+        VIDC_List 2,54,39,2,320,2,7, 2,44,1,976,1,0,24000,0
+ ]
+
+V115tab1        ; MODE 23 new Unoid monitor style, 1152x896
+                ; changed again 29-Jul-88 to give 64.4Hz
+        VIDC_List 2,52,47,2,288,2,1, 3,43,4,896,4,0,24000,0
+
+ |
+
+V64tab1D        ; MODE 18 (old)
+        VIDC_List 0,56,199,2,640,2,1, 3,17,1,512,1,0,24000,0
+
+V64tab2D        ; MODE 19 (old)
+        VIDC_List 1,56,199,2,640,2,1, 3,17,1,512,1,0,24000,0
+
+V64tab4D        ; MODE 20 (old)
+        VIDC_List 2,56,199,2,640,2,1, 3,17,1,512,1,0,24000,0
+
+V128tab1        ; MODE 22 (old)
+        VIDC_List 2,60,41,0,320,0,3, 2,44,1,976,1,0,24000,0
+
+V115tab1        ; MODE 23
+        VIDC_List 2,60,41,16,288,16,3, 2,44,57,864,57,0,24000,0
+
+ ]
+
+V132tab8        ; MODE 24 (NEW)
+        VIDC_List 3,72,215,46,1056,46,101, 3,18,18,256,17,0,24000,0
+
diff --git a/NewModes/OldFormat b/NewModes/OldFormat
new file mode 100644
index 0000000000000000000000000000000000000000..278a8a6c718dff9177970f1d45d0ec1482a919d6
--- /dev/null
+++ b/NewModes/OldFormat
@@ -0,0 +1,453 @@
+ [ {FALSE}              ; This mode not supported by VIDC, so not used
+V32tab1
+ & &803FC000
+ & &84044000
+ & &880D8000
+ & &8C0E4000
+ & &90364000
+ & &943B8000
+ & &9C200000
+ & &A04DC000
+ & &A4008000
+ & &A8050000
+ & &AC098000
+ & &B0498000
+ & &B44DC000
+ & &E0000030
+ & -1
+ ]
+
+V32tab2         ; MODES 1,5
+ & &803FC000
+ & &84044000
+ & &880D8000
+ & &8C0F4000
+ & &90374000
+ & &943B8000
+ & &9C200000
+ & &A04DC000
+ & &A4008000
+ & &A8050000
+ & &AC098000
+ & &B0498000
+ & &B44DC000
+ & &E0000034
+ & -1
+
+V32tab2T        ; MODE 6
+ & &803FC000
+ & &84044000
+ & &880D8000
+ & &8C0F4000
+ & &90374000
+ & &943B8000
+ & &9C200000
+ & &A04DC000
+ & &A4008000
+ & &A8050000
+ & &AC0A8000 ; start 4 pixels down
+ & &B0490000 ; end 2 pixels up
+ & &B44DC000
+ & &E0000034
+ & -1
+
+V32tab4         ; MODES 2,9
+ & &803FC000
+ & &84044000
+ & &880D8000
+ & &8C0FC000
+ & &9037C000
+ & &943B8000
+ & &9C200000
+ & &A04DC000
+ & &A4008000
+ & &A8050000
+ & &AC098000
+ & &B0498000
+ & &B44DC000
+ & &E0000038
+ & -1
+
+V32tab4T        ; MODE 7
+ & &803FC000
+ & &84044000
+ & &880D8000
+ & &8C0FC000
+ & &9037C000
+ & &943B8000
+ & &9C200000
+ & &A04DC000
+ & &A4008000
+ & &A8050000
+ & &AC0A8000 ; start 4 pixels down
+ & &B0490000 ; end 2 pixels up
+ & &B44DC000
+ & &E0000038
+ & -1
+
+V32tab8         ; MODES 10,13
+ & &803FC000
+ & &84044000
+ & &880D8000
+ & &8C100000
+ & &90380000
+ & &943B8000
+ & &9C200000
+ & &A04DC000
+ & &A4008000
+ & &A8050000
+ & &AC098000
+ & &B0498000
+ & &B44DC000
+ & &E000002C
+ & -1
+
+
+V64tab1         ; MODES 0,4
+ & &807FC000
+ & &8408C000
+ & &881B0000
+ & &8C1EC000
+ & &906EC000
+ & &94770000
+ & &9C400000
+ & &A04DC000
+ & &A4008000
+ & &A8050000
+ & &AC098000
+ & &B0498000
+ & &B44DC000
+ & &E0000032
+ & -1
+
+V64tab2         ; MODE 8
+ & &807FC000
+ & &8408C000
+ & &881B0000
+ & &8C1FC000
+ & &906FC000
+ & &94770000
+ & &9C400000
+ & &A04DC000
+ & &A4008000
+ & &A8050000
+ & &AC098000
+ & &B0498000
+ & &B44DC000
+ & &E0000036
+ & -1
+
+V64tab2T        ; MODES 3,11
+ & &807FC000
+ & &8408C000
+ & &881B0000
+ & &8C1FC000
+ & &906FC000
+ & &94770000
+ & &9C400000
+ & &A04DC000
+ & &A4008000
+ & &A8050000
+ & &AC0A8000    ; Start 4 pixels down
+ & &B0490000    ; End 2 pixels up
+ & &B44DC000
+ & &E0000036
+ & -1
+
+V64tab4         ; MODE 12
+ & &807FC000
+ & &8408C000
+ & &881B0000
+ & &8C204000
+ & &90704000
+ & &94770000
+ & &9C400000
+ & &A04DC000
+ & &A4008000
+ & &A8050000
+ & &AC098000
+ & &B0498000
+ & &B44DC000
+ & &E000002A
+ & -1
+
+V64tab4T        ; MODE 14
+ & &807FC000
+ & &8408C000
+ & &881B0000
+ & &8C204000
+ & &90704000
+ & &94770000
+ & &9C400000
+ & &A04DC000
+ & &A4008000
+ & &A8050000
+ & &AC0A8000    ; Start 4 pixels down
+ & &B0490000    ; End 2 pixels up
+ & &B44DC000
+ & &E000002A
+ & -1
+
+V64tab8         ; MODE 15
+ & &807FC000
+ & &8408C000
+ & &881B0000
+ & &8C208000
+ & &90708000
+ & &94770000
+ & &9C400000
+ & &A04DC000
+ & &A4008000
+ & &A8050000
+ & &AC098000
+ & &B0498000
+ & &B44DC000
+ & &E000001E
+ & -1
+
+V132tab4        ; MODE 16
+ & &80BFC000
+ & &8408C000
+ & &8823C000 ; 1B0000
+ & &8C28C000
+ & &90ACC000
+ & &94B34000 ; B20000
+ & &9C600000
+ & &A04DC000
+ & &A4008000
+ & &A8050000
+ & &AC098000
+ & &B0498000
+ & &B44DC000
+ & &E000002B
+ & -1
+
+V132tab4T       ; MODE 17
+ & &80BFC000
+ & &8408C000
+ & &8823C000 ; 1B0000
+ & &8C28C000
+ & &90ACC000
+ & &94B34000 ; B34000
+ & &9C600000
+ & &A04DC000
+ & &A4008000
+ & &A8050000
+ & &AC0A8000    ; start 4 pixels down
+ & &B0490000    ; end 2 pixels up
+ & &B44DC000
+ & &E000002B
+ & -1
+
+ [ {TRUE}
+V64tab1D        ; MODE 18
+ & &806FC000
+ & &8406C000
+ & &881DC000
+ & &8C1BC000
+ & &906BC000
+ & &946E4000
+ & &9C380000
+ & &A0854000
+ & &A4008000
+ & &A804C000
+ & &AC050000
+ & &B0850000
+ & &B4854000
+ & &E0000033
+ & -1
+
+V64tab2D        ; MODE 19
+ & &806FC000
+ & &8406C000
+ & &881DC000
+ & &8C1CC000
+ & &906CC000
+ & &946E4000
+ & &9C380000
+ & &A0854000
+ & &A4008000
+ & &A804C000
+ & &AC050000
+ & &B0850000
+ & &B4854000
+ & &E0000037
+ & -1
+
+V64tab4D        ; MODE 20
+ & &806FC000
+ & &8406C000
+ & &881DC000
+ & &8C1D4000
+ & &906D4000
+ & &946E4000
+ & &9C380000
+ & &A0854000
+ & &A4008000
+ & &A804C000
+ & &AC050000
+ & &B0850000
+ & &B4854000
+ & &E000002B
+ & -1
+
+V64tab8D        ; MODE 21 (NEW)
+ & &806FC000
+ & &8406C000
+ & &881DC000
+ & &8C1D8000
+ & &906D8000
+ & &946E4000
+ & &9C380000
+ & &A0854000
+ & &A4008000
+ & &A804C000
+ & &AC050000
+ & &B0850000
+ & &B4854000
+ & &E000000F
+ & -1
+
+ [ {FALSE} ; not used any more
+V128tab1        ; MODE 22      
+ & &8034C000
+ & &84068000
+ & &880B8000
+ & &8C0B0000
+ & &90330000
+ & &94340000
+ & &9C1A8000
+ & &A0FFC000
+ & &A4004000
+ & &A80B4000
+ & &AC0B8000
+ & &B0FF8000
+ & &B4FFC000
+ & &E000002B
+ & -1
+ ]
+
+V115tab1        ; MODE 23 new Unoid monitor style, 1152x896
+                ; changed again 29-Jul-88 to give 64.4Hz
+ & &8030C000
+ & &84064000
+ & &880C4000
+ & &8C0BC000
+ & &902FC000
+ & &9430C000
+ & &9C188000    ; changed 16-Aug-88, was 1A8
+ & &A0ED4000
+ & &A4008000
+ & &A80B4000
+ & &AC0C4000
+ & &B0EC4000
+ & &B4ED4000
+ & &E000002B
+ & -1
+
+ |
+
+V64tab1D        ; MODE 18 (old)
+ & &80704000
+ & &8406C000
+ & &881FC000
+ & &8C1DC000
+ & &906DC000
+ & &94704000
+ & &9C384000
+ & &A0854000
+ & &A4008000
+ & &A804C000
+ & &AC050000
+ & &B0850000
+ & &B4854000
+ & &E0000033
+ & -1
+
+V64tab2D        ; MODE 19 (old)
+ & &80704000
+ & &8406C000
+ & &881FC000
+ & &8C1EC000
+ & &906EC000
+ & &94704000
+ & &9C384000
+ & &A0854000
+ & &A4008000
+ & &A804C000
+ & &AC050000
+ & &B0850000
+ & &B4854000
+ & &E0000037
+ & -1
+
+V64tab4D        ; MODE 20 (old)
+ & &80704000
+ & &8406C000
+ & &881FC000
+ & &8C1F4000
+ & &906F4000
+ & &94704000
+ & &9C384000
+ & &A0854000
+ & &A4008000
+ & &A804C000
+ & &AC050000
+ & &B0850000
+ & &B4854000
+ & &E000002B
+ & -1
+
+V128tab1        ; MODE 22 (old)
+ & &8034C000
+ & &84074000
+ & &880C8000
+ & &8C0BC000
+ & &9033C000
+ & &94348000
+ & &9C1A8000    ; was wrong
+ & &A0FFC000
+ & &A4004000
+ & &A80B4000
+ & &AC0B8000
+ & &B0FF8000
+ & &B4FFC000
+ & &E000002B
+ & -1
+
+V115tab1        ; MODE 23
+ & &8034C000
+ & &84074000
+ & &880C8000
+ & &8C0DC000
+ & &9031C000
+ & &94348000
+ & &9C1A8000            ; was wrong
+ & &A0FFC000
+ & &A4004000
+ & &A80B4000
+ & &AC198000
+ & &B0F18000
+ & &B4FFC000
+ & &E000002B
+ & -1
+
+ ]
+
+V132tab8        ; MODE 24 (NEW)
+ & &80BFC000
+ & &8408C000
+ & &8823C000
+ & &8C290000
+ & &90AD0000
+ & &94B34000
+ & &9C600000
+ & &A04DC000
+ & &A4008000
+ & &A8050000
+ & &AC098000
+ & &B0498000
+ & &B44DC000
+ & &E000000F
+ & -1
+
diff --git a/NewModes/OldPSSrc b/NewModes/OldPSSrc
new file mode 100644
index 0000000000000000000000000000000000000000..c8ebe682fa2b7914e9362c8e1740a1240c5e1e41
--- /dev/null
+++ b/NewModes/OldPSSrc
@@ -0,0 +1,1002 @@
+; > NewModes.PSSrc
+; 08-Nov-89
+
+        GET     &.Hdr.ListOpts
+        GET     &.Hdr.Macros
+        GET     &.Hdr.System
+        GET     &.Hdr.NewSpace
+        GET     &.Hdr.ModHand
+        GET     &.Hdr.Services
+        GET     &.Hdr.Proc
+        GET     &.Hdr.File
+        GET     &.Hdr.NewErrors
+        GET     &.Hdr.VduExt
+        GET     &.Hdr.Debug
+        GET     &.Hdr.CMOS
+
+        LEADR   Module_LoadAddr
+
+        GBLL    Debug
+Debug   SETL    {FALSE}
+
+        GBLL    Module
+Module  SETL    {TRUE}         ; Really !
+
+TAB     *       9
+LF      *       10
+FF      *       12
+CR      *       13
+
+MonitorType_Normal * 0
+MonitorType_MultiSync * 1
+MonitorType_HiResMono * 2
+MonitorType_VGA * 3
+MonitorType_DontCare * -1
+
+Normal          *       1 :SHL: MonitorType_Normal
+MultiSync       *       1 :SHL: MonitorType_MultiSync
+HiResMono       *       1 :SHL: MonitorType_HiResMono
+VGA             *       1 :SHL: MonitorType_VGA
+
+ScreenEndAdr    *       &02000000
+Vinit           *       &03600000
+Interlace       *       &40
+
+        MACRO
+        NewMode $modeno, $monitors, $vidclist, $wslist
+        &       $modeno
+        &       $monitors
+99
+        &       ($vidclist)-%BT99
+        &       ($wslist)-%BT99
+        MEND
+
+; Module workspace allocation
+
+        ^ 0, R12
+
+ModeExt_WorkspaceSize * :INDEX: @
+
+; **************** Module code starts here **********************
+
+Module_BaseAddr
+
+        DCD     0
+        DCD     ModeExt_Init    -Module_BaseAddr
+        DCD     ModeExt_Die     -Module_BaseAddr
+        DCD     ModeExt_Service -Module_BaseAddr
+        DCD     ModeExt_Title   -Module_BaseAddr
+        DCD     ModeExt_HelpStr -Module_BaseAddr
+        DCD     ModeExt_HC_Table-Module_BaseAddr
+
+ModeExt_Title
+        =       "NewModes", 0
+
+ModeExt_HelpStr
+        =       "NewModes"
+        =       TAB
+        =       "1.3 (08 Nov 1989) test Multisync Modes", 0
+        ALIGN
+
+; *****************************************************************************
+
+ModeExt_HC_Table * Module_BaseAddr
+
+; *****************************************************************************
+;
+;       ModeExt_Init - Initialisation entry point
+;
+
+ModeExt_Init ENTRY
+
+        MOV     R0, #EventV
+        ADRL    R1, EventRoutine
+        MOV     R2, R12
+        SWI     XOS_Claim
+        MOVVC   R0, #14
+        MOVVC   R1, #Event_VSync
+        SWIVC   XOS_Byte
+        EXIT
+
+; *****************************************************************************
+;
+;       ModeExt_Die - Die entry
+;
+
+ModeExt_Die ENTRY
+
+        MOV     R0, #13
+        MOV     R1, #Event_VSync
+        SWI     XOS_Byte
+        MOV     R0, #EventV
+        ADRL    R1, EventRoutine
+        MOV     R2, R12
+        SWI     XOS_Release
+        EXITS
+
+; *****************************************************************************
+;
+;       ModeExt_Service - Main entry point for services
+;
+; in:   R1 = service reason code
+;
+
+ModeExt_Service ENTRY
+        TEQ     R1, #Service_ModeExtension
+        TEQNE   R1, #Service_PreModeChange
+        TEQNE   R1, #Service_ModeChange
+        TEQNE   R1, #Service_Reset
+        TEQNE   R1, #Service_ModeTranslation
+        EXIT    NE
+
+        TEQ     R1, #Service_PreModeChange
+        MOVEQ   R14, #0                 ; zero the word
+        STREQ   R14, [R12]
+        EXIT    EQ
+
+        TEQ     R1, #Service_ModeChange
+        BEQ     ModeChangeService
+
+        TEQ     R1, #Service_Reset
+        BEQ     ResetCode
+
+        TEQ     R1, #Service_ModeTranslation
+        BEQ     ModeTranslation
+
+        [ Debug
+        DREG    R2, "Mode = "
+        DREG    R3, "Monitor type = "
+        ]
+
+        Push    "R5-R8"
+
+        ADR     R5, NewModesList-8
+        MOV     R8, #1
+10
+        ADD     R5, R5, #8              ; skip VidC, WS
+        LDMIA   R5!, {R6, R7}           ; R6 = mode, R7 = OK monitor types
+        CMP     R6, #-1
+        Pull    "R5-R8, PC", EQ         ; mode not in list, exit
+        TEQ     R2, R6                  ; if not this mode
+        BNE     %BT10                   ; then loop
+        CMP     R3, #MonitorType_DontCare ; if any monitor type
+        BEQ     %FT20                   ; then found one
+        TST     R7, R8, LSL R3          ; else if not appropriate monitor
+        BEQ     %BT10                   ; then loop
+20
+        LDMIA   R5, {R3, R4}            ; load offsets to VidCList, WSList
+        ADD     R3, R3, R5              ; convert to addresses
+        ADD     R4, R4, R5              ; convert to addresses
+        MOV     R1, #0                  ; claim service
+        Pull    "R5-R8, PC"
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+NewModesList
+
+        NewMode  0,  MultiSync, Multi100VIDCList, Multi100WSList           ; rshifted Mode 0
+        NewMode  1,  MultiSync, Multi101VIDCList, Multi101WSList           ; rshifted Mode 1
+        NewMode  2,  MultiSync, Multi102VIDCList, Multi102WSList           ; rshifted Mode 2
+        NewMode  3,  MultiSync, Multi103VIDCList, Multi103WSList           ; rshifted Mode 3
+        NewMode  4,  MultiSync, Multi104VIDCList, Multi104WSList           ; rshifted Mode 4
+        NewMode  5,  MultiSync, Multi105VIDCList, Multi105WSList           ; rshifted Mode 5
+        NewMode  6,  MultiSync, Multi106VIDCList, Multi106WSList           ; rshifted Mode 6
+        NewMode  7,  MultiSync, Multi107VIDCList, Multi107WSList           ; rshifted Mode 7
+        NewMode  8,  MultiSync, Multi108VIDCList, Multi108WSList           ; rshifted Mode 8
+        NewMode  9,  MultiSync, Multi109VIDCList, Multi109WSList           ; rshifted Mode 9
+        NewMode 10,  MultiSync, Multi110VIDCList, Multi110WSList           ; rshifted Mode 10
+        NewMode 11,  MultiSync, Multi111VIDCList, Multi111WSList           ; rshifted Mode 11
+        NewMode 12,  MultiSync, Multi112VIDCList, Multi112WSList           ; rshifted Mode 12
+        NewMode 13,  MultiSync, Multi113VIDCList, Multi113WSList           ; rshifted Mode 13
+        NewMode 14,  MultiSync, Multi114VIDCList, Multi114WSList           ; rshifted Mode 14
+        NewMode 15,  MultiSync, Multi115VIDCList, Multi115WSList           ; rshifted Mode 15
+        NewMode 16,  MultiSync, Multi116VIDCList, Multi116WSList           ; rshifted Mode 16
+        NewMode 17,  MultiSync, Multi117VIDCList, Multi117WSList           ; rshifted Mode 17
+        NewMode 18,  MultiSync, Multi118VIDCList, Multi118WSList           ; rshifted Mode 18
+        NewMode 19,  MultiSync, Multi119VIDCList, Multi119WSList           ; rshifted Mode 19
+        NewMode 20,  MultiSync, Multi120VIDCList, Multi120WSList           ; rshifted Mode 20
+        NewMode 21,  MultiSync, Multi121VIDCList, Multi121WSList           ; rshifted Mode 21
+
+        NewMode 24,  MultiSync, Multi124VIDCList, Multi124WSList           ; rshifted Mode 24
+
+        NewMode 25,  VGA :OR: MultiSync, VGA1VIDCList, VGA1WSList2         ; VGA 1bp 640x480
+        NewMode 26,  VGA :OR: MultiSync, VGA2VIDCList, VGA2WSList2         ; VGA 2bp 640x480
+        NewMode 27,  VGA :OR: MultiSync, VGA4VIDCList, VGA4WSList2         ; VGA 4bp 640x480
+        NewMode 28,  VGA :OR: MultiSync, VGA8VIDCList, VGA8WSList2         ; VGA 8bp 640x480
+
+        NewMode 31, MultiSync, sVGA4_VIDCList, sVGA4_WSList                ; super VGA 4bp 800x600 
+        NewMode 32, MultiSync, sVGA8_VIDCList, sVGA8_WSList                ; super VGA 8bp 800x600
+
+        NewMode 33, Normal :OR: MultiSync, Mode33VIDCList, Mode33WSList    ; overscan 768x288 
+        NewMode 34, Normal :OR: MultiSync, Mode34VIDCList, Mode34WSList    ; overscan 832x288
+
+;        NewMode 45, VGA :OR: MultiSync, VGA11VIDCList, VGA11WSList2        ; VGA 1bp 640x350
+;        NewMode 46, VGA :OR: MultiSync, VGA12VIDCList, VGA12WSList2        ; VGA 2bp 640x350
+;        NewMode 47, VGA :OR: MultiSync, VGA14VIDCList, VGA14WSList2        ; VGA 4bp 640x350
+;        NewMode 48, VGA :OR: MultiSync, VGA18VIDCList, VGA18WSList2        ; VGA 8bp 640x350
+
+;        NewMode 55, VGA :OR: MultiSync, VGA21VIDCList, VGA21WSList2        ; VGA 1bp 720x400
+;        NewMode 56, VGA :OR: MultiSync, VGA22VIDCList, VGA22WSList2        ; VGA 2bp 720x400
+;        NewMode 57, VGA :OR: MultiSync, VGA24VIDCList, VGA24WSList2        ; VGA 4bp 720x400
+;        NewMode 58, VGA :OR: MultiSync, VGA28VIDCList, VGA28WSList2        ; VGA 8bp 720x400
+
+;        NewMode 60, MultiSync, Multi350VIDCList, Multi350WSList            ; MDA 640x350
+;        NewMode 61, MultiSync, Multi200VIDCList, Multi200WSList            ; CGA 640x200
+;        NewMode 62, MultiSync, Multi351VIDCList, Multi351WSList            ; EGA 640x350
+
+;        NewMode 66, MultiSync, Multi66VIDCList, Multi66WSList             ; DTP 960x384 4bpp
+;        NewMode 67, MultiSync, Multi67VIDCList, Multi67WSList             ; DTP 960x384 8bpp
+
+;        NewMode 76, MultiSync, Multi76VIDCList, Multi76WSList             ; DTP 1280x384 4bpp
+;        NewMode 77, MultiSync, Multi77VIDCList, Multi77WSList             ; DTP 1280x384 8bpp
+
+        &       -1, 0
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; general purpose mode macros
+
+ClockShift * 9
+SyncShift * 11
+
+; pixel rate specifiers
+
+CRPix_24000 * 3 :OR: (0 :SHL: ClockShift)
+CRPix_16000 * 2 :OR: (0 :SHL: ClockShift)
+CRPix_12000 * 1 :OR: (0 :SHL: ClockShift)
+CRPix_8000  * 0 :OR: (0 :SHL: ClockShift)
+CRPix_25175 * 3 :OR: (1 :SHL: ClockShift)
+CRPix_36000 * 3 :OR: (2 :SHL: ClockShift)
+
+        MACRO
+        VIDC_List $bpp,$hsync,$hbpch,$hlbdr,$hdisp,$hrbdr,$hfpch, $vsync,$vbpch,$vlbdr,$vdisp,$vrbdr,$vfpch,$pixrate,$sp
+        LCLA    sub
+        LCLA    lbpp
+        LCLA    syncpol
+ [ $bpp = 8
+sub     SETA    5
+lbpp    SETA    3
+ ]
+ [ $bpp = 4
+sub     SETA    7
+lbpp    SETA    2
+ ]
+ [ $bpp = 2
+sub     SETA    11
+lbpp    SETA    1
+ ]
+ [ $bpp = 1
+sub     SETA    19
+lbpp    SETA    0
+ ]
+ [ "$sp"="" :LOR: "$sp"="0"
+syncpol SETA    0 :SHL: SyncShift               ; normal sync polarity
+ |
+  [ "$sp"="1"
+syncpol SETA    3 :SHL: SyncShift               ; inverted H and V sync
+  |
+        ! 1,"Sync polarity must be null string or 0 (for normal syncs) or 1 (for inverted H and V syncs)"
+  ]
+ ]
+        ASSERT  ($hsync :AND: 1)=0
+        ASSERT  ($hbpch :AND: 1)=1
+        ASSERT  ($hlbdr :AND: 1)=0
+        ASSERT  ($hdisp :AND: 1)=0
+        ASSERT  ($hrbdr :AND: 1)=0
+        ASSERT  ($hfpch :AND: 1)=1
+; Horizontal
+        &       (&80:SHL:24) :OR: ((($hsync+$hbpch+$hlbdr+$hdisp+$hrbdr+$hfpch -2  )/2) :SHL: 14) ; HCR
+        &       (&84:SHL:24) :OR: ((($hsync                                    -2  )/2) :SHL: 14) ; HSWR
+        &       (&88:SHL:24) :OR: ((($hsync+$hbpch                             -1  )/2) :SHL: 14) ; HBSR
+        &       (&8C:SHL:24) :OR: ((($hsync+$hbpch+$hlbdr                      -sub)/2) :SHL: 14) ; HDSR
+        &       (&90:SHL:24) :OR: ((($hsync+$hbpch+$hlbdr+$hdisp               -sub)/2) :SHL: 14) ; HDER
+        &       (&94:SHL:24) :OR: ((($hsync+$hbpch+$hlbdr+$hdisp+$hrbdr        -1  )/2) :SHL: 14) ; HBER
+; Vertical
+        &       (&A0:SHL:24) :OR: (($vsync+$vbpch+$vlbdr+$vdisp+$vrbdr+$vfpch    -1)    :SHL: 14) ; VCR
+        &       (&A4:SHL:24) :OR: (($vsync                                       -1)    :SHL: 14) ; VSWR
+        &       (&A8:SHL:24) :OR: (($vsync+$vbpch                                -1)    :SHL: 14) ; VBSR
+        &       (&AC:SHL:24) :OR: (($vsync+$vbpch+$vlbdr                         -1)    :SHL: 14) ; VDSR
+        &       (&B0:SHL:24) :OR: (($vsync+$vbpch+$vlbdr+$vdisp                  -1)    :SHL: 14) ; VDER
+        &       (&B4:SHL:24) :OR: (($vsync+$vbpch+$vlbdr+$vdisp+$vrbdr           -1)    :SHL: 14) ; VBER
+; Control Register
+        &       (&E0:SHL:24) :OR: (CRPix_$pixrate) :OR: (lbpp :SHL: 2) :OR: syncpol
+        MEND
+
+        MACRO
+        VIDC_WS  $bpp,$hpix,$vpix,$multx,$multy, $dht
+
+        &       VduExt_XWindLimit, $hpix-1
+        &       VduExt_ScrRCol, ($hpix/8)-1
+        &       VduExt_LineLength, $hpix*$bpp/8
+ [ "$dht" <> ""
+        &       VduExt_ModeFlags, Flag_DoubleVertical
+        &       VduExt_ScrBRow, ($vpix/16)-1
+ |
+        &       VduExt_ScrBRow, ($vpix/8)-1
+ ]
+        &       VduExt_YWindLimit, $vpix-1
+        &       VduExt_ScreenSize, $hpix*$vpix*$bpp/8
+
+        &       VduExt_XEigFactor, $multx
+        &       VduExt_YEigFactor, $multy
+        MEND
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+;  Start of multi sync archy modes
+
+; Mode 0, multiscan mode 0
+Multi100VIDCList
+        &         0
+        &         0
+        VIDC_List 1,72,63,88,640,88,73, 3,16,17,256,17,3,16000
+        &        -1
+Multi100WSList
+        &         0
+        &         0
+        &        -1
+
+; Mode 1, multiscan mode 1
+Multi101VIDCList
+        &         0
+        &         1
+        VIDC_List 2,36,33,44,320,44,35, 3,16,17,256,17,3,8000
+        &        -1
+Multi101WSList
+        &         0
+        &         1
+        &        -1
+
+; Mode 2, multiscan mode 2
+Multi102VIDCList
+        &         0
+        &         2
+        VIDC_List 4,36,33,44,320,44,35, 3,16,17,256,17,3,8000
+        &        -1
+Multi102WSList
+        &         0
+        &         2
+        &        -1
+
+; Mode 3, multiscan mode 3
+Multi103VIDCList
+        &         0
+        &         3
+        VIDC_List 2,72,63,88,640,88,73, 3,16,20,250,20,3,16000
+        &        -1
+Multi103WSList
+        &         0
+        &         3
+        &        -1
+
+; Mode 4, multiscan mode 4
+Multi104VIDCList
+        &         0
+        &         4
+        VIDC_List 1,72,63,88,640,88,73, 3,16,17,256,17,3,16000
+        &        -1
+Multi104WSList
+        &         0
+        &         4
+        &        -1
+
+; Mode 5, multiscan mode 5
+Multi105VIDCList
+        &         0
+        &         5
+        VIDC_List 2,36,51,24,320,24,57, 3,16,17,256,17,3,8000
+        &        -1
+Multi105WSList
+        &         0
+        &         5
+        &        -1
+
+; Mode 6, multiscan mode 6
+Multi106VIDCList
+        &         0
+        &         6
+        VIDC_List 2,36,33,44,320,44,35, 3,16,20,250,20,3,8000
+        &        -1
+Multi106WSList
+        &         0
+        &         6
+        &        -1
+
+; Mode 7, multiscan mode 7
+Multi107VIDCList
+        &         0
+        &         7
+        VIDC_List 4,36,31,44,320,44,37, 3,18,22,250,16,3,8000
+        &        -1
+Multi107WSList
+        &         0
+        &         7
+        &        -1
+
+; Mode 8, multiscan mode 8
+Multi108VIDCList
+        &         0
+        &         8
+        VIDC_List 2,72,63,88,640,88,73, 3,16,17,256,17,3,16000
+        &        -1
+Multi108WSList
+        &         0
+        &         8
+        &        -1
+
+; Mode 9, multiscan mode 9
+Multi109VIDCList
+        &         0
+        &         9
+        VIDC_List 4,36,33,44,320,44,35, 3,16,17,256,17,3,8000
+        &        -1
+Multi109WSList
+        &         0
+        &         9
+        &        -1
+
+; Mode 10, multiscan mode 10
+Multi110VIDCList
+        &         0
+        &         10
+        VIDC_List 8,36,33,44,320,44,35, 3,16,17,256,17,3,8000
+        &        -1
+Multi110WSList
+        &         0
+        &         10
+        &        -1
+
+; Mode 11, multiscan mode 11
+Multi111VIDCList
+        &         0
+        &         11
+        VIDC_List 2,72,63,88,640,88,73, 3,16,20,250,20,3,16000
+        &        -1
+Multi111WSList
+        &         0
+        &         11
+        &        -1
+
+; Mode 12, multiscan mode 12
+Multi112VIDCList
+        &         0
+        &         12
+        VIDC_List 4,72,63,88,640,88,73, 3,16,17,256,17,3,16000
+        &        -1
+Multi112WSList
+        &         0
+        &         12
+        &        -1
+
+; Mode 13, multiscan mode 13
+Multi113VIDCList
+        &         0
+        &         13
+        VIDC_List 8,36,33,44,320,44,35, 3,16,17,256,17,3,8000
+        &        -1
+Multi113WSList
+        &         0
+        &         13
+        &        -1
+
+; Mode 14, multiscan mode 14
+Multi114VIDCList
+        &         0
+        &         14
+        VIDC_List 4,72,63,88,640,88,73, 3,16,20,250,20,3,16000
+        &        -1
+Multi114WSList
+        &         0
+        &         14
+        &        -1
+
+; Mode 15, multiscan mode 15
+Multi115VIDCList
+        &         0
+        &         15
+        VIDC_List 8,72,63,88,640,88,73, 3,16,17,256,17,3,16000
+        &        -1
+Multi115WSList
+        &         0
+        &         15
+        &        -1
+
+; Mode 16, multiscan mode 16
+Multi116VIDCList
+        &         0
+        &         16
+        VIDC_List 4,112,47,132,1056,132,57, 3,16,17,256,17,3,24000
+;
+; tv blanking!      112,139, 76,1056,114, 39,   ,3,18,16,256,16,3
+;                  4u67,5u79,3u2,44u,4u7,1u62,  21r           3r
+;         should be  =10u4              =1u65   22r           3r(2r5) 
+;philips needs bprch > 57 or goes dark (sync at 112)
+; can inc sync to 10u0, more and screen starts to wobble
+        &        -1
+Multi116WSList
+        &         0
+        &         16
+        &        -1
+
+; Mode 17, multiscan mode 17
+Multi117VIDCList
+        &         0
+        &         17
+        VIDC_List 4,112,47,132,1056,132,57, 3,16,20,250,20,3,24000
+        &        -1
+Multi117WSList
+        &         0
+        &         17
+        &        -1
+
+; Mode 18, multiscan mode 18
+Multi118VIDCList
+        &         0
+        &         18
+        VIDC_List 1,56,111,2,640,2,85, 3,17,1,512,1,0,24000
+        &        -1
+Multi118WSList
+        &         0
+        &         18
+        &        -1
+
+; Mode 19, multiscan mode 19
+Multi119VIDCList
+        &         0
+        &         19
+        VIDC_List 2,56,111,2,640,2,85, 3,17,1,512,1,0,24000
+        &        -1
+Multi119WSList
+        &         0
+        &         19
+        &        -1
+
+; Mode 20, multiscan mode 20
+Multi120VIDCList
+        &         0
+        &         20
+        VIDC_List 4,56,111,2,640,2,85, 3,17,1,512,1,0,24000
+        &        -1
+Multi120WSList
+        &         0
+        &         20
+        &        -1
+
+; Mode 21, multiscan mode 21
+Multi121VIDCList
+        &         0
+        &         21
+        VIDC_List 8,56,111,2,640,2,85, 3,17,1,512,1,0,24000
+        &        -1
+Multi121WSList
+        &         0
+        &         21
+        &        -1
+
+; Mode 24, multiscan mode 24
+Multi124VIDCList
+        &         0
+        &         24
+        VIDC_List 8,112,47,132,1056,132,57, 3,16,17,256,17,3,24000
+        &        -1
+Multi124WSList
+        &         0
+        &         24
+        &        -1
+
+
+; MODE 25 (VGA or multisync monitors, 640x480)
+VGA1VIDCList
+        &         0
+        &         18
+        VIDC_List 1,96,47,0,640,0,15, 2,32,0,480,0,11,25175,1
+        &        -1
+VGA1WSList2
+        &         0
+        &         18
+        &        -1
+
+; MODE 26 (VGA or multisync monitors, 640x480)
+VGA2VIDCList
+        &         0
+        &         19
+        VIDC_List 2,96,47,0,640,0,15, 2,32,0,480,0,11,25175,1
+        &        -1
+VGA2WSList2
+        &         0
+        &         19
+        &        -1
+
+; MODE 27 (VGA or multisync monitors,  640x480)
+VGA4VIDCList
+        &         0
+        &         20
+        VIDC_List 4,96,47,0,640,0,15, 2,32,0,480,0,11,25175,1
+        &        -1
+VGA4WSList2
+        &         0
+        &         20
+        &        -1
+
+; MODE 28 (VGA or multisync monitors,  640x480)
+VGA8VIDCList
+        &         0
+        &         21
+        VIDC_List 8,96,47,0,640,0,15, 2,32,0,480,0,11,25175,1
+        &        -1
+VGA8WSList2
+        &         0
+        &         21
+        &        -1
+
+; MODE 31 (800x600 by 4 bits per pixel)
+sVGA4_VIDCList
+        &         0
+        &         20
+        VIDC_List 4,72,129,0,800,0,23, 2,22,0,600,0,1,36000
+        &         &E000042B
+        &        -1
+sVGA4_WSList
+        &       0
+        &       20
+        VIDC_WS 4,800,600,1,1
+        &      -1
+
+; MODE 32 (800x600 by 8 bits per pixel)
+sVGA8_VIDCList
+        &         0
+        &         24
+        VIDC_List 8,72,129,0,800,0,23, 2,22,0,600,0,1,36000
+        &         &E000040F
+        &        -1
+sVGA8_WSList
+        &       0
+        &       24
+        VIDC_WS 8,800,600,1,1
+        &      -1
+
+; MODE 33 (768x288 by 8 bits per pixel)
+Mode33VIDCList
+
+        &         0
+        &         15
+        VIDC_List 8,74,127,0,768,0,55, 3,18,0,288,0,3,16000
+        &        -1
+Mode33WSList
+        &         0
+        &         15
+        VIDC_WS 8,768,288,1,2
+        &        -1
+
+; MODE 34 (832x288 by 8 bits per pixel)
+Mode34VIDCList
+        &         0
+        &         15
+        VIDC_List 8,74,87,0,832,0,31, 3,18,0,288,0,3,16000
+        &        -1
+Mode34WSList
+        &        0
+        &        15
+        VIDC_WS  8,832,288,1,2
+        &       -1
+
+;; MODE 45 (VGA or multisync monitors, 640x350)
+;VGA11VIDCList
+;        &         0
+;        &         18
+;        VIDC_List 1,96,47,0,640,0,15, 2,59,0,350,0,38
+;        &        -1
+;VGA11WSList2
+;        &        0
+;        &        18
+;        VIDC_WS  1,640,350,1,2
+;        &       -1
+
+;; MODE 46 (VGA or multisync monitors, 640x350)
+;VGA12VIDCList
+;        &         0
+;        &         19
+;        VIDC_List 2,96,47,0,640,0,15, 2,59,0,350,0,38
+;        &        -1
+;VGA12WSList2
+;        &        0
+;        &        19
+;        VIDC_WS  2,640,350,1,2
+;        &       -1
+
+;; MODE 47 (VGA or multisync monitors, 640x350)
+;VGA14VIDCList
+;        &         0
+;        &         20
+;        VIDC_List 4,96,47,0,640,0,15, 2,59,0,350,0,38
+;        &        -1
+;VGA14WSList2
+;        &        0
+;        &        20
+;        VIDC_WS  4,640,350,1,2
+;        &       -1
+
+;; MODE 48 (VGA or multisync monitors, 640x350)
+;VGA18VIDCList
+;        &         0
+;        &         21
+;        VIDC_List 8,96,47,0,640,0,15, 2,59,0,350,0,38
+;        &         &E000020F
+;        &        -1
+;VGA18WSList2
+;        &        0
+;        &        21
+;        VIDC_WS  8,640,350,1,2
+;        &       -1
+
+;; MODE 55 (VGA or multisync monitors, 720x400)
+;VGA21VIDCList
+;        &         0
+;        &         18
+;        VIDC_List 1,108,55,0,720,0,17, 2,34,0,400,0,13
+;        &        -1
+;VGA21WSList2
+;        &        0
+;        &        18
+;        VIDC_WS  1,720,400,1,2
+;        &       -1
+
+;; MODE 56 (VGA or multisync monitors, 720x400)
+;VGA22VIDCList
+;        &       0
+;        &       19
+;        VIDC_List 2,108,55,0,720,0,17, 2,34,0,400,0,13
+;        &       -1
+;VGA22WSList2
+;        &        0
+;        &        19
+;        VIDC_WS  2,720,400,1,2
+;        &       -1
+
+;; MODE 57 (VGA or multisync monitors, 720x400)
+;VGA24VIDCList
+;        &         0
+;        &         20
+;        VIDC_List 4,108,55,0,720,0,17, 2,34,0,400,0,13
+;        &        -1
+;VGA24WSList2
+;        &        0
+;        &        20
+;        VIDC_WS  4,720,400,1,2
+;        &       -1
+
+;; MODE 58 (VGA or multisync monitors, 720x400)
+;VGA28VIDCList
+;        &         0
+;        &         21
+;        VIDC_List 8,108,55,0,720,0,17, 2,34,0,400,0,13
+;        &         &E000020F
+;        &        -1
+;VGA28WSList2
+;        &        0
+;        &        21
+;        VIDC_WS  8,720,400,1,2
+;        &       -1
+
+;; IBM standard form modes
+
+;; Mode 60 MDA, multiscan
+;Multi350VIDCList
+;        &         0
+;        &         12
+;        VIDC_List 4,132,17,2,704,2,9, 17,4,0,350,0,0
+;        &        -1
+;Multi350WSList
+;        &         0
+;        &         12
+;        VIDC_WS   4,704,350,1,2
+;        &        -1
+
+;; Mode 61 CGA, multiscan
+;Multi200VIDCList
+;        &         0
+;        &         12
+;        VIDC_List 4,68,115,0,720,0,105, 3,34,0,200,0,25
+;        &        -1
+;Multi200WSList
+;        &         0
+;        &         12
+;        VIDC_WS   4,720,200,1,2
+;        &        -1
+
+;; Mode 62 EGA, multiscan
+;Multi351VIDCList
+;        &         0
+;        &         12
+;        VIDC_List 4,78,25,0,624,0,1, 13,2,0,350,0,1
+;        &        -1
+;Multi351WSList
+;        &         0
+;        &         12
+;        VIDC_WS   4,624,350,1,2
+;        &        -1
+
+
+;; Mode 66 , DTP multiscan  960x384 4bpp
+;Multi66VIDCList
+;        &         0
+;        &         20
+;        VIDC_List 4,72,101,0,960,0,39, 3,18,0,384,0,2
+;        &        -1
+;Multi66WSList
+;        &         0
+;        &         20
+;        VIDC_WS   4,960,384,1,2
+;        &        -1
+
+;; Mode 67 , DTP multiscan 960x384 8bpp
+;Multi67VIDCList
+;        &         0
+;        &         21
+;        VIDC_List 8,72,101,0,960,0,39, 3,18,0,384,0,2
+;        &        -1
+;Multi67WSList
+;        &         0
+;        &         21
+;        VIDC_WS   8,960,384,1,2
+;        &        -1
+
+;; Mode 76 , DTP multiscan  1280x296 4bpp
+;Multi76VIDCList
+;        &         0
+;        &         16
+;        VIDC_List 4,48,111,0,1280,0,53, 3,14,0,296,0,4
+;        &        -1
+;Multi76WSList
+;        &         0
+;        &         16
+;        VIDC_WS   4,1280,296,1,2
+;        &        -1
+
+;; Mode 77 , DTP multiscan 1280x296 8bpp
+;Multi77VIDCList
+;        &         0
+;        &         24
+;        VIDC_List 8,48,111,0,1280,0,53, 3,14,0,296,0,4
+;        &        -1
+;Multi77WSList
+;        &         0
+;        &         24
+;        VIDC_WS   8,1280,296,1,2
+;        &        -1
+
+    [ Debug
+    InsertDebugRoutines
+    ]
+
+; *****************************************************************************
+;
+;       Code to execute on reset
+;
+; in:   R14 already stacked
+;
+
+ResetCode ROUT
+        Push    "R0-R6"
+        BL      ModeExt_Init
+        Pull    "R0-R6,PC"
+
+; *****************************************************************************
+;
+;       EventRoutine - Routine called on Vsync
+;
+
+EventRoutine ROUT
+        TEQ     R0, #Event_VSync
+        MOVNE   PC, R14
+        ENTRY   "R0-R6"
+        LDR     R1, [R12]               ; load state flag, 0 => disabled
+        RSBS    R1, R1, #0              ; change sign
+        EXIT    EQ                      ; if zero then do nowt
+
+        STR     R1, [R12]               ; store back
+        VDWS    WsPtr
+        LDR     R2, [WsPtr, #TotalScreenSize] ; needed later as well
+        ADDMI   R1, R1, R2              ; if -ve then add TotalScreenSize
+
+        STR     R1, [WsPtr, #TeletextOffset]
+
+; now set VInit to this
+
+        LDR     R0, [WsPtr, #DisplayStart]
+        SUB     R0, R0, #ScreenEndAdr
+        ADD     R0, R0, R2                      ; make start of screen 0
+        ADD     R0, R0, R1                      ; add on teletext bank offset
+        CMP     R0, R2                          ; if out of range
+        SUBCS   R0, R0, R2                      ; then subtract total size
+SetVinitPhys
+        MOV     R1, #Vinit
+        STR     R0, [WsPtr, #VinitCopy]
+        MOV     R0, R0, LSR #4                  ; bottom 4 bits not valid
+        ORR     R0, R1, R0, LSL #2              ; OR in at correct place
+        STR     R0, [R0]                        ; any old data will do
+
+        LDR     R0, [WsPtr, #VIDCControlCopy]
+        ORR     R0, R0, #Interlace              ; turn interlace on
+        STR     R0, [WsPtr, #VIDCControlCopy]
+        MOV     R1, #VIDC
+        STR     R0, [R1]
+
+        EXIT
+
+; *****************************************************************************
+
+ModeChangeService
+        Push    "R0-R6"
+        MOV     R5, R12                 ; our workspace pointer
+        VDWS    WsPtr
+        MOV     R6, #0
+        LDR     R0, [WsPtr, #DisplayModeNo]
+        TEQ     R0, #18
+        TEQNE   R0, #19
+        TEQNE   R0, #20
+        TEQNE   R0, #21
+        TEQNE   R0, #26
+        TEQNE   R0, #27
+        BNE     %FT10
+        MOV     R0, #&A1
+        MOV     R1, #VduCMOS
+        SWI     XOS_Byte
+        ANDS    R2, R2, #MonitorTypeBits
+        BNE     %FT10
+
+        MOV     R0, #255
+        STR     R0, [WsPtr, #DisplayYWindLimit]
+        LDR     R0, [WsPtr, #YEigFactor]
+        ADD     R0, R0, #1
+        STR     R0, [WsPtr, #DisplayYEigFactor]
+        LDR     R0, =541-6
+        STR     R0, [WsPtr, #CursorFudgeFactor]
+
+        LDR     R6, [WsPtr, #LineLength]
+        MOV     R6, R6, LSR #1
+        RSB     R6, R6, #0
+10
+        STR     R6, [R5]
+        Pull    "R0-R6, PC"
+
+; *****************************************************************************
+;
+;       ModeTranslation - Code to perform VGA mode number translation
+;
+; in:   R1 = Service_ModeTranslation
+;       R2 = mode number
+;       R3 = monitor type
+;       return address stacked
+;
+; out:  R1 = 0 if claimed
+;       R2 = substitute
+;       R3 preserved
+
+ModeTranslation ROUT
+        TEQ     R3, #MonitorType_VGA
+        Pull    PC, NE
+        BIC     R1, R2, #&80    ; get rid of shadow bit
+        CMP     R1, #32         ; if in range 32..39
+        RSBCSS  R1, R1, #39
+        BCS     %FT10           ; then don't modify
+
+        [ {TRUE}
+        MOV     R2, #0
+        |
+        Push    R0
+        MOV     R0, R2
+        MOV     R1, #9          ; log2bpp
+        SWI     XOS_ReadModeVariable
+        Pull    R0
+        MOVVS   R2, #0          ; if error or invalid then use mode 32
+        MOVCS   R2, #0
+        CMP     R2, #4
+        MOVCS   R2, #3
+        ADD     R2, R2, #32
+        ]
+10
+        MOV     R1, #0          ; claim service
+        Pull    PC
+
+        END
+
diff --git a/NewModes/OldToNew,ffb b/NewModes/OldToNew,ffb
new file mode 100644
index 0000000000000000000000000000000000000000..982d10c2346510cefd9691867c6d7f539f57082e
Binary files /dev/null and b/NewModes/OldToNew,ffb differ
diff --git a/NewModes/PSSrc b/NewModes/PSSrc
new file mode 100644
index 0000000000000000000000000000000000000000..a9b05b5f3a3c6f9e764b70318345a4e2d2a1057a
--- /dev/null
+++ b/NewModes/PSSrc
@@ -0,0 +1,791 @@
+; > NewModes.PSSrc
+; 08-Nov-89
+
+        GET     &.Hdr.ListOpts
+        GET     &.Hdr.Macros
+        GET     &.Hdr.System
+        GET     &.Hdr.NewSpace
+        GET     &.Hdr.ModHand
+        GET     &.Hdr.Services
+        GET     &.Hdr.Proc
+        GET     &.Hdr.File
+        GET     &.Hdr.NewErrors
+        GET     &.Hdr.VduExt
+        GET     &.Hdr.Debug
+        GET     &.Hdr.CMOS
+
+        LEADR   Module_LoadAddr
+
+        GBLL    Debug
+Debug   SETL    {FALSE}
+
+        GBLL    Module
+Module  SETL    {TRUE}         ; Really !
+
+TAB     *       9
+LF      *       10
+FF      *       12
+CR      *       13
+
+MonitorType_Normal * 0
+MonitorType_MultiSync * 1
+MonitorType_HiResMono * 2
+MonitorType_VGA * 3
+MonitorType_DontCare * -1
+
+Normal          *       1 :SHL: MonitorType_Normal
+MultiSync       *       1 :SHL: MonitorType_MultiSync
+HiResMono       *       1 :SHL: MonitorType_HiResMono
+VGA             *       1 :SHL: MonitorType_VGA
+
+ScreenEndAdr    *       &02000000
+Vinit           *       &03600000
+Interlace       *       &40
+
+        MACRO
+        NewMode $modeno, $monitors, $vidclist, $wslist
+        &       $modeno
+        &       $monitors
+99
+        &       ($vidclist)-%BT99
+        &       ($wslist)-%BT99
+        MEND
+
+; Module workspace allocation
+
+        ^ 0, R12
+
+ModeExt_WorkspaceSize * :INDEX: @
+
+; **************** Module code starts here **********************
+
+Module_BaseAddr
+
+        DCD     0
+        DCD     ModeExt_Init    -Module_BaseAddr
+        DCD     ModeExt_Die     -Module_BaseAddr
+        DCD     ModeExt_Service -Module_BaseAddr
+        DCD     ModeExt_Title   -Module_BaseAddr
+        DCD     ModeExt_HelpStr -Module_BaseAddr
+        DCD     ModeExt_HC_Table-Module_BaseAddr
+
+ModeExt_Title
+        =       "NewModes", 0
+
+ModeExt_HelpStr
+        =       "NewModes"
+        =       TAB
+        =       "1.3 (08 Nov 1989) test Multisync Modes", 0
+        ALIGN
+
+; *****************************************************************************
+
+ModeExt_HC_Table * Module_BaseAddr
+
+; *****************************************************************************
+;
+;       ModeExt_Init - Initialisation entry point
+;
+
+ModeExt_Init ENTRY
+
+        MOV     R0, #EventV
+        ADRL    R1, EventRoutine
+        MOV     R2, R12
+        SWI     XOS_Claim
+        MOVVC   R0, #14
+        MOVVC   R1, #Event_VSync
+        SWIVC   XOS_Byte
+        EXIT
+
+; *****************************************************************************
+;
+;       ModeExt_Die - Die entry
+;
+
+ModeExt_Die ENTRY
+
+        MOV     R0, #13
+        MOV     R1, #Event_VSync
+        SWI     XOS_Byte
+        MOV     R0, #EventV
+        ADRL    R1, EventRoutine
+        MOV     R2, R12
+        SWI     XOS_Release
+        EXITS
+
+; *****************************************************************************
+;
+;       ModeExt_Service - Main entry point for services
+;
+; in:   R1 = service reason code
+;
+
+ModeExt_Service ENTRY
+        TEQ     R1, #Service_ModeExtension
+        TEQNE   R1, #Service_PreModeChange
+        TEQNE   R1, #Service_ModeChange
+        TEQNE   R1, #Service_Reset
+        TEQNE   R1, #Service_ModeTranslation
+        EXIT    NE
+
+        TEQ     R1, #Service_PreModeChange
+        MOVEQ   R14, #0                 ; zero the word
+        STREQ   R14, [R12]
+        EXIT    EQ
+
+        TEQ     R1, #Service_ModeChange
+        BEQ     ModeChangeService
+
+        TEQ     R1, #Service_Reset
+        BEQ     ResetCode
+
+        TEQ     R1, #Service_ModeTranslation
+        BEQ     ModeTranslation
+
+        [ Debug
+        DREG    R2, "Mode = "
+        DREG    R3, "Monitor type = "
+        ]
+
+        Push    "R5-R8"
+
+        ADR     R5, NewModesList-8
+        MOV     R8, #1
+10
+        ADD     R5, R5, #8              ; skip VidC, WS
+        LDMIA   R5!, {R6, R7}           ; R6 = mode, R7 = OK monitor types
+        CMP     R6, #-1
+        Pull    "R5-R8, PC", EQ         ; mode not in list, exit
+        TEQ     R2, R6                  ; if not this mode
+        BNE     %BT10                   ; then loop
+        CMP     R3, #MonitorType_DontCare ; if any monitor type
+        BEQ     %FT20                   ; then found one
+        TST     R7, R8, LSL R3          ; else if not appropriate monitor
+        BEQ     %BT10                   ; then loop
+20
+        LDMIA   R5, {R3, R4}            ; load offsets to VidCList, WSList
+        ADD     R3, R3, R5              ; convert to addresses
+        ADD     R4, R4, R5              ; convert to addresses
+        MOV     R1, #0                  ; claim service
+        Pull    "R5-R8, PC"
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+NewModesList
+
+        NewMode  0,  MultiSync, Multi100VIDCList, Multi100WSList           ; rshifted Mode 0
+        NewMode  1,  MultiSync, Multi101VIDCList, Multi101WSList           ; rshifted Mode 1
+        NewMode  2,  MultiSync, Multi102VIDCList, Multi102WSList           ; rshifted Mode 2
+        NewMode  3,  MultiSync, Multi103VIDCList, Multi103WSList           ; rshifted Mode 3
+        NewMode  4,  MultiSync, Multi104VIDCList, Multi104WSList           ; rshifted Mode 4
+        NewMode  5,  MultiSync, Multi105VIDCList, Multi105WSList           ; rshifted Mode 5
+        NewMode  6,  MultiSync, Multi106VIDCList, Multi106WSList           ; rshifted Mode 6
+        NewMode  7,  MultiSync, Multi107VIDCList, Multi107WSList           ; rshifted Mode 7
+        NewMode  8,  MultiSync, Multi108VIDCList, Multi108WSList           ; rshifted Mode 8
+        NewMode  9,  MultiSync, Multi109VIDCList, Multi109WSList           ; rshifted Mode 9
+        NewMode 10,  MultiSync, Multi110VIDCList, Multi110WSList           ; rshifted Mode 10
+        NewMode 11,  MultiSync, Multi111VIDCList, Multi111WSList           ; rshifted Mode 11
+        NewMode 12,  MultiSync, Multi112VIDCList, Multi112WSList           ; rshifted Mode 12
+        NewMode 13,  MultiSync, Multi113VIDCList, Multi113WSList           ; rshifted Mode 13
+        NewMode 14,  MultiSync, Multi114VIDCList, Multi114WSList           ; rshifted Mode 14
+        NewMode 15,  MultiSync, Multi115VIDCList, Multi115WSList           ; rshifted Mode 15
+        NewMode 16,  MultiSync, Multi116VIDCList, Multi116WSList           ; rshifted Mode 16
+        NewMode 17,  MultiSync, Multi117VIDCList, Multi117WSList           ; rshifted Mode 17
+        NewMode 18,  MultiSync, Multi118VIDCList, Multi118WSList           ; rshifted Mode 18
+        NewMode 19,  MultiSync, Multi119VIDCList, Multi119WSList           ; rshifted Mode 19
+        NewMode 20,  MultiSync, Multi120VIDCList, Multi120WSList           ; rshifted Mode 20
+        NewMode 21,  MultiSync, Multi121VIDCList, Multi121WSList           ; rshifted Mode 21
+
+        NewMode 24,  MultiSync, Multi124VIDCList, Multi124WSList           ; rshifted Mode 24
+
+        NewMode 25,  VGA :OR: MultiSync, VGA1VIDCList, VGA1WSList2         ; VGA 1bp 640x480
+        NewMode 26,  VGA :OR: MultiSync, VGA2VIDCList, VGA2WSList2         ; VGA 2bp 640x480
+        NewMode 27,  VGA :OR: MultiSync, VGA4VIDCList, VGA4WSList2         ; VGA 4bp 640x480
+        NewMode 28,  VGA :OR: MultiSync, VGA8VIDCList, VGA8WSList2         ; VGA 8bp 640x480
+
+        NewMode 31, MultiSync, sVGA4_VIDCList, sVGA4_WSList                ; super VGA 4bp 800x600 
+        NewMode 32, MultiSync, sVGA8_VIDCList, sVGA8_WSList                ; super VGA 8bp 800x600
+
+        NewMode 33, Normal :OR: MultiSync, Mode33VIDCList, Mode33WSList    ; overscan 768x288 
+        NewMode 34, Normal :OR: MultiSync, Mode34VIDCList, Mode34WSList    ; overscan 832x288
+
+        &       -1, 0
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; general purpose mode macros
+
+ClockShift * 9
+SyncShift * 11
+
+; pixel rate specifiers
+
+CRPix_24000 * 3 :OR: (0 :SHL: ClockShift)
+CRPix_16000 * 2 :OR: (0 :SHL: ClockShift)
+CRPix_12000 * 1 :OR: (0 :SHL: ClockShift)
+CRPix_8000  * 0 :OR: (0 :SHL: ClockShift)
+CRPix_25175 * 3 :OR: (1 :SHL: ClockShift)
+CRPix_36000 * 3 :OR: (2 :SHL: ClockShift)
+
+        MACRO
+        VIDC_List $lbpp,$hsync,$hbpch,$hlbdr,$hdisp,$hrbdr,$hfpch, $vsync,$vbpch,$vlbdr,$vdisp,$vrbdr,$vfpch,$pixrate,$sp
+        LCLA    sub
+        LCLA    syncpol
+ [ $lbpp = 3
+sub     SETA    5
+ ]
+ [ $lbpp = 2
+sub     SETA    7
+ ]
+ [ $lbpp = 1
+sub     SETA    11
+ ]
+ [ $lbpp = 0
+sub     SETA    19
+ ]
+ [ "$sp"=""
+syncpol SETA    0 :SHL: SyncShift               ; normal sync polarity
+ |
+        ASSERT $sp<=3
+syncpol SETA    $sp :SHL: SyncShift
+ ]
+        ASSERT  ($hsync :AND: 1)=0
+        ASSERT  ($hbpch :AND: 1)=1
+        ASSERT  ($hlbdr :AND: 1)=0
+        ASSERT  ($hdisp :AND: 1)=0
+        ASSERT  ($hrbdr :AND: 1)=0
+        ASSERT  ($hfpch :AND: 1)=1
+ [ (($hsync+$hbpch+$hlbdr+$hdisp+$hrbdr+$hfpch) :AND: 3)<>0
+        ! 0, "Warning: mode unsuitable for interlaced use"
+ ]
+; Horizontal
+        &       (&80:SHL:24) :OR: ((($hsync+$hbpch+$hlbdr+$hdisp+$hrbdr+$hfpch -2  )/2) :SHL: 14) ; HCR
+        &       (&84:SHL:24) :OR: ((($hsync                                    -2  )/2) :SHL: 14) ; HSWR
+        &       (&88:SHL:24) :OR: ((($hsync+$hbpch                             -1  )/2) :SHL: 14) ; HBSR
+        &       (&8C:SHL:24) :OR: ((($hsync+$hbpch+$hlbdr                      -sub)/2) :SHL: 14) ; HDSR
+        &       (&90:SHL:24) :OR: ((($hsync+$hbpch+$hlbdr+$hdisp               -sub)/2) :SHL: 14) ; HDER
+        &       (&94:SHL:24) :OR: ((($hsync+$hbpch+$hlbdr+$hdisp+$hrbdr        -1  )/2) :SHL: 14) ; HBER
+        &       (&9C:SHL:24) :OR: (((($hsync+$hbpch+$hlbdr+$hdisp+$hrbdr+$hfpch-2)/2+1)/2):SHL:14); HIR
+; Vertical
+        &       (&A0:SHL:24) :OR: (($vsync+$vbpch+$vlbdr+$vdisp+$vrbdr+$vfpch    -1)    :SHL: 14) ; VCR
+        &       (&A4:SHL:24) :OR: (($vsync                                       -1)    :SHL: 14) ; VSWR
+        &       (&A8:SHL:24) :OR: (($vsync+$vbpch                                -1)    :SHL: 14) ; VBSR
+        &       (&AC:SHL:24) :OR: (($vsync+$vbpch+$vlbdr                         -1)    :SHL: 14) ; VDSR
+        &       (&B0:SHL:24) :OR: (($vsync+$vbpch+$vlbdr+$vdisp                  -1)    :SHL: 14) ; VDER
+        &       (&B4:SHL:24) :OR: (($vsync+$vbpch+$vlbdr+$vdisp+$vrbdr           -1)    :SHL: 14) ; VBER
+; Control Register
+        &       (&E0:SHL:24) :OR: (CRPix_$pixrate) :OR: ($lbpp :SHL: 2) :OR: syncpol
+        MEND
+
+        MACRO
+        VIDC_WS  $bpp,$hpix,$vpix,$multx,$multy, $dht
+
+        &       VduExt_XWindLimit, $hpix-1
+        &       VduExt_ScrRCol, ($hpix/8)-1
+        &       VduExt_LineLength, $hpix*$bpp/8
+ [ "$dht" <> ""
+        &       VduExt_ModeFlags, Flag_DoubleVertical
+        &       VduExt_ScrBRow, ($vpix/16)-1
+ |
+        &       VduExt_ScrBRow, ($vpix/8)-1
+ ]
+        &       VduExt_YWindLimit, $vpix-1
+        &       VduExt_ScreenSize, $hpix*$vpix*$bpp/8
+
+        &       VduExt_XEigFactor, $multx
+        &       VduExt_YEigFactor, $multy
+        MEND
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+;  Start of multi sync archy modes
+
+; Mode 0, multiscan mode 0
+Multi100VIDCList
+        &         0
+        &         0
+        VIDC_List 0,72,63,88,640,88,73, 3,16,17,256,17,3,16000,0
+        &        -1
+Multi100WSList
+        &         0
+        &         0
+        &        -1
+
+; Mode 1, multiscan mode 1
+Multi101VIDCList
+        &         0
+        &         1
+        VIDC_List 1,36,33,44,320,44,35, 3,16,17,256,17,3,8000,0
+        &        -1
+Multi101WSList
+        &         0
+        &         1
+        &        -1
+
+; Mode 2, multiscan mode 2
+Multi102VIDCList
+        &         0
+        &         2
+        VIDC_List 2,36,33,44,320,44,35, 3,16,17,256,17,3,8000,0
+        &        -1
+Multi102WSList
+        &         0
+        &         2
+        &        -1
+
+; Mode 3, multiscan mode 3
+Multi103VIDCList
+        &         0
+        &         3
+        VIDC_List 1,72,63,88,640,88,73, 3,16,20,250,20,3,16000,0
+        &        -1
+Multi103WSList
+        &         0
+        &         3
+        &        -1
+
+; Mode 4, multiscan mode 4
+Multi104VIDCList
+        &         0
+        &         4
+        VIDC_List 0,72,63,88,640,88,73, 3,16,17,256,17,3,16000,0
+        &        -1
+Multi104WSList
+        &         0
+        &         4
+        &        -1
+
+; Mode 5, multiscan mode 5
+Multi105VIDCList
+        &         0
+        &         5
+        VIDC_List 1,36,51,24,320,24,57, 3,16,17,256,17,3,8000,0
+        &        -1
+Multi105WSList
+        &         0
+        &         5
+        &        -1
+
+; Mode 6, multiscan mode 6
+Multi106VIDCList
+        &         0
+        &         6
+        VIDC_List 1,36,33,44,320,44,35, 3,16,20,250,20,3,8000,0
+        &        -1
+Multi106WSList
+        &         0
+        &         6
+        &        -1
+
+; Mode 7, multiscan mode 7
+Multi107VIDCList
+        &         0
+        &         7
+        VIDC_List 2,36,31,44,320,44,37, 3,18,22,250,16,3,8000,0
+        &        -1
+Multi107WSList
+        &         0
+        &         7
+        &        -1
+
+; Mode 8, multiscan mode 8
+Multi108VIDCList
+        &         0
+        &         8
+        VIDC_List 1,72,63,88,640,88,73, 3,16,17,256,17,3,16000,0
+        &        -1
+Multi108WSList
+        &         0
+        &         8
+        &        -1
+
+; Mode 9, multiscan mode 9
+Multi109VIDCList
+        &         0
+        &         9
+        VIDC_List 2,36,33,44,320,44,35, 3,16,17,256,17,3,8000,0
+        &        -1
+Multi109WSList
+        &         0
+        &         9
+        &        -1
+
+; Mode 10, multiscan mode 10
+Multi110VIDCList
+        &         0
+        &         10
+        VIDC_List 3,36,33,44,320,44,35, 3,16,17,256,17,3,8000,0
+        &        -1
+Multi110WSList
+        &         0
+        &         10
+        &        -1
+
+; Mode 11, multiscan mode 11
+Multi111VIDCList
+        &         0
+        &         11
+        VIDC_List 1,72,63,88,640,88,73, 3,16,20,250,20,3,16000,0
+        &        -1
+Multi111WSList
+        &         0
+        &         11
+        &        -1
+
+; Mode 12, multiscan mode 12
+Multi112VIDCList
+        &         0
+        &         12
+        VIDC_List 2,72,63,88,640,88,73, 3,16,17,256,17,3,16000,0
+        &        -1
+Multi112WSList
+        &         0
+        &         12
+        &        -1
+
+; Mode 13, multiscan mode 13
+Multi113VIDCList
+        &         0
+        &         13
+        VIDC_List 3,36,33,44,320,44,35, 3,16,17,256,17,3,8000,0
+        &        -1
+Multi113WSList
+        &         0
+        &         13
+        &        -1
+
+; Mode 14, multiscan mode 14
+Multi114VIDCList
+        &         0
+        &         14
+        VIDC_List 2,72,63,88,640,88,73, 3,16,20,250,20,3,16000,0
+        &        -1
+Multi114WSList
+        &         0
+        &         14
+        &        -1
+
+; Mode 15, multiscan mode 15
+Multi115VIDCList
+        &         0
+        &         15
+        VIDC_List 3,72,63,88,640,88,73, 3,16,17,256,17,3,16000,0
+        &        -1
+Multi115WSList
+        &         0
+        &         15
+        &        -1
+
+; Mode 16, multiscan mode 16
+Multi116VIDCList
+        &         0
+        &         16
+        VIDC_List 2,112,47,132,1056,132,57, 3,16,17,256,17,3,24000,0
+;
+; tv blanking!      112,139, 76,1056,114, 39,   ,3,18,16,256,16,3
+;                  4u67,5u79,3u2,44u,4u7,1u62,  21r           3r
+;         should be  =10u4              =1u65   22r           3r(2r5) 
+;philips needs bprch > 57 or goes dark (sync at 112)
+; can inc sync to 10u0, more and screen starts to wobble
+        &        -1
+Multi116WSList
+        &         0
+        &         16
+        &        -1
+
+; Mode 17, multiscan mode 17
+Multi117VIDCList
+        &         0
+        &         17
+        VIDC_List 2,112,47,132,1056,132,57, 3,16,20,250,20,3,24000,0
+        &        -1
+Multi117WSList
+        &         0
+        &         17
+        &        -1
+
+; Mode 18, multiscan mode 18
+Multi118VIDCList
+        &         0
+        &         18
+        VIDC_List 0,56,111,2,640,2,85, 3,17,1,512,1,0,24000,0
+        &        -1
+Multi118WSList
+        &         0
+        &         18
+        &        -1
+
+; Mode 19, multiscan mode 19
+Multi119VIDCList
+        &         0
+        &         19
+        VIDC_List 1,56,111,2,640,2,85, 3,17,1,512,1,0,24000,0
+        &        -1
+Multi119WSList
+        &         0
+        &         19
+        &        -1
+
+; Mode 20, multiscan mode 20
+Multi120VIDCList
+        &         0
+        &         20
+        VIDC_List 2,56,111,2,640,2,85, 3,17,1,512,1,0,24000,0
+        &        -1
+Multi120WSList
+        &         0
+        &         20
+        &        -1
+
+; Mode 21, multiscan mode 21
+Multi121VIDCList
+        &         0
+        &         21
+        VIDC_List 3,56,111,2,640,2,85, 3,17,1,512,1,0,24000,0
+        &        -1
+Multi121WSList
+        &         0
+        &         21
+        &        -1
+
+; Mode 24, multiscan mode 24
+Multi124VIDCList
+        &         0
+        &         24
+        VIDC_List 3,112,47,132,1056,132,57, 3,16,17,256,17,3,24000,0
+        &        -1
+Multi124WSList
+        &         0
+        &         24
+        &        -1
+
+
+; MODE 25 (VGA or multisync monitors, 640x480)
+VGA1VIDCList
+        &         0
+        &         18
+        VIDC_List 0,96,47,0,640,0,15, 2,32,0,480,0,11,25175,3
+        &        -1
+VGA1WSList2
+        &         0
+        &         18
+        &        -1
+
+; MODE 26 (VGA or multisync monitors, 640x480)
+VGA2VIDCList
+        &         0
+        &         19
+        VIDC_List 1,96,47,0,640,0,15, 2,32,0,480,0,11,25175,3
+        &        -1
+VGA2WSList2
+        &         0
+        &         19
+        &        -1
+
+; MODE 27 (VGA or multisync monitors,  640x480)
+VGA4VIDCList
+        &         0
+        &         20
+        VIDC_List 2,96,47,0,640,0,15, 2,32,0,480,0,11,25175,3
+        &        -1
+VGA4WSList2
+        &         0
+        &         20
+        &        -1
+
+; MODE 28 (VGA or multisync monitors,  640x480)
+VGA8VIDCList
+        &         0
+        &         21
+        VIDC_List 3,96,47,0,640,0,15, 2,32,0,480,0,11,25175,3
+        &        -1
+VGA8WSList2
+        &         0
+        &         21
+        &        -1
+
+; MODE 31 (800x600 by 4 bits per pixel)
+sVGA4_VIDCList
+        &         0
+        &         20
+        VIDC_List 2,72,129,0,800,0,23, 2,22,0,600,0,1,36000,0
+        &        -1
+sVGA4_WSList
+        &       0
+        &       20
+        VIDC_WS 4,800,600,1,1
+        &      -1
+
+; MODE 32 (800x600 by 8 bits per pixel)
+sVGA8_VIDCList
+        &         0
+        &         24
+        VIDC_List 3,72,129,0,800,0,23, 2,22,0,600,0,1,36000,0
+        &        -1
+sVGA8_WSList
+        &       0
+        &       24
+        VIDC_WS 8,800,600,1,1
+        &      -1
+
+; MODE 33 (768x288 by 8 bits per pixel)
+Mode33VIDCList
+
+        &         0
+        &         15
+        VIDC_List 3,74,127,0,768,0,55, 3,18,0,288,0,3,16000,0
+        &        -1
+Mode33WSList
+        &         0
+        &         15
+        VIDC_WS 8,768,288,1,2
+        &        -1
+
+; MODE 34 (832x288 by 8 bits per pixel)
+Mode34VIDCList
+        &         0
+        &         15
+        VIDC_List 3,74,87,0,832,0,31, 3,18,0,288,0,3,16000,0
+        &        -1
+Mode34WSList
+        &        0
+        &        15
+        VIDC_WS  8,832,288,1,2
+        &       -1
+
+    [ Debug
+    InsertDebugRoutines
+    ]
+
+; *****************************************************************************
+;
+;       Code to execute on reset
+;
+; in:   R14 already stacked
+;
+
+ResetCode ROUT
+        Push    "R0-R6"
+        BL      ModeExt_Init
+        Pull    "R0-R6,PC"
+
+; *****************************************************************************
+;
+;       EventRoutine - Routine called on Vsync
+;
+
+EventRoutine ROUT
+        TEQ     R0, #Event_VSync
+        MOVNE   PC, R14
+        ENTRY   "R0-R6"
+        LDR     R1, [R12]               ; load state flag, 0 => disabled
+        RSBS    R1, R1, #0              ; change sign
+        EXIT    EQ                      ; if zero then do nowt
+
+        STR     R1, [R12]               ; store back
+        VDWS    WsPtr
+        LDR     R2, [WsPtr, #TotalScreenSize] ; needed later as well
+        ADDMI   R1, R1, R2              ; if -ve then add TotalScreenSize
+
+        STR     R1, [WsPtr, #TeletextOffset]
+
+; now set VInit to this
+
+        LDR     R0, [WsPtr, #DisplayStart]
+        SUB     R0, R0, #ScreenEndAdr
+        ADD     R0, R0, R2                      ; make start of screen 0
+        ADD     R0, R0, R1                      ; add on teletext bank offset
+        CMP     R0, R2                          ; if out of range
+        SUBCS   R0, R0, R2                      ; then subtract total size
+SetVinitPhys
+        MOV     R1, #Vinit
+        STR     R0, [WsPtr, #VinitCopy]
+        MOV     R0, R0, LSR #4                  ; bottom 4 bits not valid
+        ORR     R0, R1, R0, LSL #2              ; OR in at correct place
+        STR     R0, [R0]                        ; any old data will do
+
+        LDR     R0, [WsPtr, #VIDCControlCopy]
+        ORR     R0, R0, #Interlace              ; turn interlace on
+        STR     R0, [WsPtr, #VIDCControlCopy]
+        MOV     R1, #VIDC
+        STR     R0, [R1]
+
+        EXIT
+
+; *****************************************************************************
+
+ModeChangeService
+        Push    "R0-R6"
+        MOV     R5, R12                 ; our workspace pointer
+        VDWS    WsPtr
+        MOV     R6, #0
+        LDR     R0, [WsPtr, #DisplayModeNo]
+        TEQ     R0, #18
+        TEQNE   R0, #19
+        TEQNE   R0, #20
+        TEQNE   R0, #21
+        TEQNE   R0, #26
+        TEQNE   R0, #27
+        BNE     %FT10
+        MOV     R0, #&A1
+        MOV     R1, #VduCMOS
+        SWI     XOS_Byte
+        ANDS    R2, R2, #MonitorTypeBits
+        BNE     %FT10
+
+        MOV     R0, #255
+        STR     R0, [WsPtr, #DisplayYWindLimit]
+        LDR     R0, [WsPtr, #YEigFactor]
+        ADD     R0, R0, #1
+        STR     R0, [WsPtr, #DisplayYEigFactor]
+        LDR     R0, =541-6
+        STR     R0, [WsPtr, #CursorFudgeFactor]
+
+        LDR     R6, [WsPtr, #LineLength]
+        MOV     R6, R6, LSR #1
+        RSB     R6, R6, #0
+10
+        STR     R6, [R5]
+        Pull    "R0-R6, PC"
+
+; *****************************************************************************
+;
+;       ModeTranslation - Code to perform VGA mode number translation
+;
+; in:   R1 = Service_ModeTranslation
+;       R2 = mode number
+;       R3 = monitor type
+;       return address stacked
+;
+; out:  R1 = 0 if claimed
+;       R2 = substitute
+;       R3 preserved
+
+ModeTranslation ROUT
+        TEQ     R3, #MonitorType_VGA
+        Pull    PC, NE
+        BIC     R1, R2, #&80    ; get rid of shadow bit
+        CMP     R1, #32         ; if in range 32..39
+        RSBCSS  R1, R1, #39
+        BCS     %FT10           ; then don't modify
+
+        [ {TRUE}
+        MOV     R2, #0
+        |
+        Push    R0
+        MOV     R0, R2
+        MOV     R1, #9          ; log2bpp
+        SWI     XOS_ReadModeVariable
+        Pull    R0
+        MOVVS   R2, #0          ; if error or invalid then use mode 32
+        MOVCS   R2, #0
+        CMP     R2, #4
+        MOVCS   R2, #3
+        ADD     R2, R2, #32
+        ]
+10
+        MOV     R1, #0          ; claim service
+        Pull    PC
+
+        END
+
diff --git a/OldTestSrc/A600tlb b/OldTestSrc/A600tlb
new file mode 100644
index 0000000000000000000000000000000000000000..6481c8b3ae56a34333abc2a617bc3efe58a6a586
--- /dev/null
+++ b/OldTestSrc/A600tlb
@@ -0,0 +1,61 @@
+;
+; A600tlb
+;
+; POST procedure for checking the TLB in A600 MMU.
+;
+; for each of level 1, level 2 small-page, level 2 large-page
+;	construct page table
+; 	flush cache
+; 	start timer
+; 	for 32 addresses (with different mappings)
+;		check address mapping
+; 	save timer
+; 	for same 32 addresses
+;		check address mapping
+; 	compare test times (did 2nd test require table walk ?)
+
+
+
+
+
+Use a list of addresses that cover a good mixture of virtual addresses
+Build a page table that maps these to physical RAM addresses in various ways
+Access the addresses in such an order that the cache rotates, scrapping 
+one entry each time through the list, and loading another. So each cache
+entry gets used 31 times, then is lost.
+Choice of physical mapping should ensure that the cache entries contain
+lots of different values of page and section base addresses.
+Choice of virtual test address should ensure that cache tag varies as
+widely as posible, too.  PRBS ?
+Very widely varying values of  cache tag require that a large number
+of mappings exist .. if  these are 2-level mappings, that requires
+a lot of RAM. Page tables should be multiply-mapped.
+RISC OS puts lots of stuff below the 4M mark. Limits App space to 16M
+for backwards compatibility. Probably worth testing outside these 
+limits to ensure Gold doesn't fall over, but failure rates would be
+very low.
+
+
+
+
+;
+; POST procedure for checking access faults (was PPL test)
+;
+; for each of level 1, level 2 small-page, level 2 large-page
+;	construct page table
+;	for user, supervisor mode
+;		check address alignment fault
+;		check section translation fault
+;		check 
+;		check page translation fault
+;		for 3 domain types
+;			for 16 domains
+;	 			check access permissions
+;
+
+
+
+;
+; POST procedure for checking IDC
+;
+; 
diff --git a/OldTestSrc/Arm3 b/OldTestSrc/Arm3
new file mode 100644
index 0000000000000000000000000000000000000000..a385f75f4cbbd089f79c6ac394810fa1b4dd0109
--- /dev/null
+++ b/OldTestSrc/Arm3
@@ -0,0 +1,71 @@
+; > TestSrc.ARM3
+
+        TTL RISC OS 2+ POST ARM version determination
+;
+; Reads ARM3 version register, returns 0 if ARM 2 fitted.
+;
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name            Comment
+; ----          ----            -------
+; 20-Apr-89     ArtG            Initial version
+;
+;
+;------------------------------------------------------------------------
+
+A3Cid		CN	0
+A3Cfls		CN	1
+A3Cmod		CN	2
+A3Ccac		CN	3
+A3Cupd		CN	4
+A3Cdis		CN	5
+
+A3CON		CP	15
+
+
+
+ts_ARM_type
+	MOV	r13,lr
+;
+; First, set up an undefined instruction vector to catch an ARM 2 
+; (or a faulty ARM 3 ??) when the copro instruction is run.
+; Only applies on systems where ROM isn't mapped at zero.
+
+ [ CPU_Type = "ARM2" :LOR: CPU_Type = "ARM3"
+	MOV	r0,#0			; set a page at logical 0
+	MOV	r1,r0
+	BL	ts_set_cam
+	ADR	r0,ts_ARM_undefined
+	LDMIA	r0,{r2,r3}
+	MOV	r1,#4
+	STMIA	r1,{r2,r3}		; set the undefined instruction trap
+ ]
+;
+; Read ARM3C0 version I.D.
+;
+	MOV	r0, #(-1)		; should always be altered
+	MRC	A3CON,0,r0,A3Cid,A3Cid	; Read control register 0
+	MOV	r12, r0
+ [ CPU_Type = "ARM2" :LOR: CPU_Type = "ARM3"
+	MOV	r1,#0
+	BL	ts_set_cam_idle		; remove the vector page again
+ ]
+	MOVS 	r0, r12			; return the ID (0 for ARM 2)
+        MOV     pc,r13
+
+;
+; Trap to be taken when ARM 2 is fitted
+;
+
+ts_ARM_undefined
+	MOV	r0,#0
+	MOVS	pc,r14_svc
+10
+	ASSERT ((%10 - ts_ARM_undefined) / 4 = 2)
+
+
+
+
+        END 
+ 
diff --git a/OldTestSrc/Begin b/OldTestSrc/Begin
new file mode 100644
index 0000000000000000000000000000000000000000..b4b65b2d128da4c53e8184152d27393c5259a372
--- /dev/null
+++ b/OldTestSrc/Begin
@@ -0,0 +1,1428 @@
+; > TestSrc.Begin
+
+        TTL RISC OS 2+ Power-On Self-Test
+;
+; Startup code for RISC OS ROM Self-Test.
+;
+; Performs ROM test patterns, determines test strategy and enters
+; external or internal test code.
+;
+; A minimal set of opcodes should be used (ideally, only B, LDR and ADDS)
+; so that a processor test may be validly included in the internal test
+; sequence.
+;
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name    Rel     Comment
+; ----          ----    ---     -------
+; 23-Feb-93     ArtG    2.00    Experimental ARM 600 / Jordan mods
+; 20-Oct-93     ARTG    2.02    Changed to new conditional assembly scheme
+;
+;------------------------------------------------------------------------
+
+; TS_STATUS should be one of :
+;
+; 'R'   RISC OS POST
+; 'S'   Standalone version (with a2 memory test instead of RISCOS)
+; 'T'   Test build - development only
+;
+
+TS_STATUS       *       "R"     ;  Medusa POST version 2.0x
+;
+TS_RELEASE      *       20
+TS_CHANGES      *       4
+
+
+                GBLL    POSTenabled
+POSTenabled     SETL    {TRUE}          ; don't permit POST for ordinary startup 
+
+ts_Rom_bits     *       21                              ; Widest ROM address
+ts_Rom_length   *       1 :SHL: ts_Rom_bits             ; Longest ROM 
+ts_highaddr_bit *       1 :SHL: 25                      ; ARM address width
+ts_Alias_bits   *       (1 :SHL: 23)                    ; I/F output bits
+ts_recover_time *       (1 :SHL: 8)                     ; inter-twiddle delay
+ts_pause_time   *       200                             ; Display pause time
+ts_S5_base      *       &3350000                        ; IO register base address
+ts_IOEB_ID      *       (ts_S5_base + &50)              ; IOE_B ASIC identification
+ts_IOEB_ident   *       &5                              ; the value found there
+ts_PCaddress    *       &3010000                        ; PC IO world base address
+ts_ReadyByte_00 *       &90                             ; signal 'Here I am' to ExtIO
+ts_BBRAM        *       &A0                             ; IIC address of clock/ram chip
+ts_RamChunk     *       &2000                           ; gap between data line tests
+ts_MaxRamTest   *       4*1024*1024                     ; Max. DRAM tested on normal reset
+ts_VIDCPhys     *       &3400000                        ; Real location of VIDC
+
+;
+; Border colours used for self-test indicators
+;
+        [ VIDC_Type = "VIDC1a"
+C_ARMOK         *       &40000000+&70C  ; testing ROM
+C_RAMTEST       *       &40000000+&C70  ; testing RAM
+C_FAULT         *       &40000000+&00F  ; failed tests
+C_PASSED        *       &40000000+&7C0  ; all passed
+C_WARMSTART     *       &40000000+&777  ; not tested
+        ]
+
+        [ VIDC_Type = "VIDC20" 
+C_ARMOK         *       &40000000+&7000C0  ; testing ROM
+C_RAMTEST       *       &40000000+&C07000  ; testing RAM
+C_FAULT         *       &40000000+&0000F0  ; failed tests
+C_PASSED        *       &40000000+&70C000  ; all passed
+C_WARMSTART     *       &40000000+&707070  ; not tested
+        ]
+
+;
+; Responses to external commands
+;
+
+ErrorCmd        *       &00FF
+
+
+;
+; Control bitmasks used to indicate results of test to RISCOS
+;
+
+R_SOFT          *       0               ; not a power-on reset
+R_HARD          *       1               ; Self-test run due to POR
+R_EXTERN        *       2               ; external tests performed
+R_TESTED        *       4               ; Self-test run due to test link
+R_MEMORY        *       8               ; Memory has been tested
+R_ARM3          *       &10             ; ARM 3 fitted
+R_MEMSKIP       *       &20             ; long memory test disabled
+R_IOEB          *       &40             ; PC-style IO controller
+R_VRAM          *       &80             ; VRAM present
+
+R_STATUS        *       &1ff            ; bits that aren't a fault
+
+R_CHKFAILBIT    *       &100            ; CMOS contents failed checksum
+R_ROMFAILBIT    *       &200            ; ROM failed checksum
+R_CAMFAILBIT    *       &400            ; CAM failed 
+R_PROFAILBIT    *       &800            ; MEMC protection failed
+R_IOCFAILBIT    *       &1000           ; IOC register test failed
+R_INTFAILBIT    *       &2000           ; Cannot clear interrupts
+R_VIDFAILBIT    *       &4000           ; VIDC flyback failure
+R_SNDFAILBIT    *       &8000           ; Sound DMA failure
+R_CMSFAILBIT    *       &10000          ; CMOS unreadable
+R_LINFAILBIT    *       &20000          ; Page zero RAM failure
+R_MEMFAILBIT    *       &40000          ; Main RAM test failure
+R_CACFAILBIT    *       &80000          ; ARM 3 Cache test failure
+;
+ [ MorrisSupport
+Kludge * 96
+ |
+Kludge * 0
+ ]
+        SUBT    Exception vectors
+;
+; These vectors are available for use while the Rom is mapped into
+; low memory addresses. The Reset vector will be copied to low RAM 
+; as part of a software reset sequence : therefore it must perform
+; a fixed operation to ensure compatibility with future versions
+; of RISC-OS.
+;
+
+Reset
+ts_start
+        $DoMorrisROMHeader
+
+ [ :LNOT: MorrisSupport
+  [ ResetIndirected
+        LDR     pc,.+ResetIndirection   ; load pc from vector at &118
+  |
+        B       ts_RomPatt + PhysROM    ; Jump to normal ROM space
+  ]
+ ]
+01
+        &       ts_Rom_length           ; gets patched by ROM builder
+02
+        &       (ts_ROM_cvectors - ROM) ; pointer to code vector table
+03
+        &       (ts_ROM_dvectors - ROM) ; pointer to data vector table
+04
+        &       (ts_ROM_bvectors - ROM) ; pointer to branch table
+        B       Reset                   ; not currently used
+        B       Reset
+        B       Reset
+
+
+ts_ROMSIZE      *       %BT01 - ts_start
+ts_CVECTORS     *       %BT02 - ts_start
+ts_DVECTORS     *       %BT03 - ts_start
+ts_BVECTORS     *       %BT04 - ts_start
+
+;
+; Selftest version ID
+;
+
+00
+        ASSERT  %B00 <= (ts_start + &2c + Kludge)
+        %       ((ts_start + &2c + Kludge) - %B00)
+
+ts_ID   &       ((TS_STATUS :SHL: 24) + (TS_RELEASE :SHL: 16) + TS_CHANGES)
+
+ts_ID_text
+ts_himsg 
+        =       "SELFTEST"                      ; **DISPLAY_TEXT**
+        =       &89                             ; Cursor position
+        =       TS_STATUS
+        =       ("0" + (TS_RELEASE /     10))
+        =       "."
+        =       ("0" + (TS_RELEASE :MOD: 10))
+        =       ("0" + (TS_CHANGES :MOD: 10))
+        =       0
+
+
+;
+; These vector tables permit access by the external (or downloaded) test
+; software to data and code in the POST modules.
+; Find the start of these tables through the 2nd and 3rd vectors at
+; the start of the ROM.
+;
+
+ts_ROM_dvectors
+01
+        &       ts_ID                   ; Selftest identification number
+02
+        &       (ts_ID_text - ROM)      ; Selftest identification text
+
+
+;
+; vectors ORd with these flags to assure proper mode when
+; executed by host thro' vector table.
+;
+
+ts_runflags     *       (I_bit :OR: F_bit :OR: SVC_mode)
+
+ts_ROM_cvectors
+        &       ts_RomPatt              :OR: ts_runflags
+        &       ts_User_startup         :OR: ts_runflags
+        &       ts_Self_test_startup    :OR: ts_runflags
+        &       ts_Dealer_startup       :OR: ts_runflags
+        &       ts_Forced_startup       :OR: ts_runflags
+        &       ts_GetCommand           :OR: ts_runflags
+        &       ts_Softstart            :OR: ts_runflags
+        &       ts_Hardstart            :OR: ts_runflags
+
+
+;
+; ROM branch vectors - intended primarily so downloaded programs
+; may use standard subroutines.  This table should be in a fixed place.
+;
+
+00
+        ASSERT  %B00 <= (ts_start + 128 + Kludge)
+        %       ((ts_start + 128 + Kludge) - %B00)
+
+ts_ROM_bvectors
+        B       ts_RomPatt
+        B       ts_GetCommand
+        B       ts_SendByte
+        B       ts_SendWord
+        B       ts_GetByte
+        B       ts_GetWord
+        B       ts_SendText
+        B       ts_MoreText
+        B       ts_SendLCDCmd 
+
+
+;
+; Pad out until the location of the ResetIndirection vector
+;
+
+        ASSERT  .-ROM <= ResetIndirection
+        %       ResetIndirection-(.-ROM)
+        &       ts_RomPatt-ROM+PhysROM
+
+;
+; ROM test code
+;
+; Note : the register order in ADDS ...pc.. is often critical. 
+; If we want to adjust pc, use ADDS pc,rn,pc so that the PSR is
+; rewritten with it's original value.
+; If we want to do some pc-relative arithmetic, use ADDS rn,pc,rn
+; so that the bits from PSR are NOT used in the address calculation.
+;
+
+        SUBT    Macros
+
+        MACRO
+        MODE    $mode_bits
+        TEQP    psr,#($mode_bits :OR: I_bit :OR: F_bit)
+        NOP
+        MEND
+
+        MACRO   
+        MOV_fiq $dest,$src
+        MODE    FIQ_mode
+        MOV     $dest,$src
+        MODE    SVC_mode
+        MEND
+
+        MACRO   
+        FAULT   $code
+        MODE    FIQ_mode
+        ORR     r12_fiq,r12_fiq,$code
+        MODE    SVC_mode
+        MEND
+
+        MACRO
+        M32_fiq $dest,$src,$tmp1,$tmp2
+        SetMode FIQ32_mode,$tmp1,$tmp2
+        MOV     $dest,$src
+        msr     AL,CPSR_all,$tmp2
+        MEND
+
+        MACRO
+        FAULT32 $code,$tmp
+        SetMode FIQ32_mode,$tmp
+        ORR     r12_fiq,r12_fiq,$code
+        SetMode SVC32_mode,$tmp
+        MEND
+
+;
+; Define an area of storage with the required set of data bus patterns
+; These are used both for testing the complete width of the data bus
+; during ROM pattern testing, and will provide a tidy set of patterns
+; if the reset is held, while the ARM increments addresses.
+;
+
+        SUBT    ROM Address and Data Patterns
+
+DataPatterns
+
+        GBLA    dmask
+dmask   SETA    &80000000
+
+        DCD     &FFFFFFFF               ; first two : all set
+        DCD     &0                      ;             all clear
+
+        GBLA    OldOpt                  ; don't list all the walking 
+OldOpt  SETA    {OPT}                   ; patterns
+        OPT     OptNoList
+
+        WHILE   dmask > 0               ; then for each bit
+        DCD     &$dmask                 ; set it
+        DCD     :NOT: &$dmask           ; and clear it
+dmask   SETA    dmask :SHR: 1
+        WEND
+        OPT     OldOpt
+DEnd
+
+
+        OPT     OptList
+;
+;
+; Read the ROM at a series of addresses
+; such that :   a) all the address lines are exercised individually
+;               b) all the data lines are exercised individually
+;
+; Data and address lines are exercised as walking-0 and walking-1.
+; The test is performed as a series of LDR operations to avoid using
+; a larger instruction set.
+;
+
+ts_RomPatt ROUT
+
+        ; Patterns which will exercise most of the data bus.
+        ; All are arbitrary instructions with NV execution
+
+        DCD     &F0000000               ; walking 1
+
+OldOpt  SETA    {OPT}                   ; patterns
+        OPT     OptNoList
+
+dmask   SETA    &08000000
+        WHILE   dmask > 0
+        DCD     dmask :OR: &F0000000
+dmask   SETA    dmask :SHR: 1
+        WEND 
+
+        DCD     &FFFFFFFF               ; walking 0
+
+dmask   SETA    &08000000
+        WHILE   dmask > 0
+        DCD     (:NOT: dmask) :OR: &F0000000
+dmask   SETA    dmask :SHR: 1
+        WEND 
+
+        OPT     OldOpt
+
+        ; Now some proper code :
+        ; Initialise address pointer and make MemC safe
+
+        LDR     r0,%01
+        ADD     pc,r0,pc
+01
+        &       0                       ; useful constant
+
+        [ IO_Type = "IOC-A1"            ;;!! unsafe if we execute ROM at zero
+        LDR     r1,%02
+        ADD     pc,r0,pc
+02                                      ;;!! This remaps MEMC's ROM
+        &       &E000C :OR: MEMCADR     ;;!! addressing if it hasn't
+        STR     r1,[r1]                 ;;!! already happened.
+        ]
+
+        LDR     r5,%03                  ; Load r5 with a constant which
+        ADD     pc,r0,pc                ; may be added to ROM plus a
+03                                      ; walking-zero bitmask to create
+        &       ts_Rom_length - 3       ; a valid word address in ROM.
+        LDR     r2,%04                  ; Offset from ROM start to here
+        ADD     pc,r0,pc
+04
+        &       ROM - pcfromstart   
+
+        ADD     r2,pc,r2                ; pointer to start of ROM
+        ADD     r3,r2,r0                ; pointer to start of ROM
+pcfromstart
+        ADD     r4,r2,r0                ; pointer to start of ROM
+
+        ; assembly-time loop - only 32 iterations required
+
+OldOpt  SETA    {OPT}
+
+        GBLA    doffset
+doffset SETA    DataPatterns
+        WHILE   doffset < DEnd
+
+        LDR     r0,doffset              ; walking 1 data pattern
+        LDR     r1,doffset+4            ; walking 0 data pattern 
+        LDR     r6,[r2]                 ; walking 1 address pattern
+        LDR     r6,[r3]                 ; walking 0 address pattern
+
+        [ (doffset - DataPatterns) > ((32 - ts_Rom_bits) * 8)
+        [ (doffset - DataPatterns) < (31 * 8)
+        ADD     r2,r4,r0                ; r2 = ROM + walking 1 pattern
+        ADD     r3,r4,r1                ; r3 = ROM + walking 0 pattern
+        ADD     r3,r3,r5                ; adjust to a valid address
+        ]
+        ]
+
+        OPT     OptNoList
+
+doffset SETA    doffset + 8
+        WEND
+
+        ASSERT  (. - doffset < 4095)    ; in range without barrel shift ?
+
+        OPT     OldOpt
+
+;
+; External interface drivers - 
+; provides entry points to send byte- and word- and string-sized objects
+; and to receive byte- and word-sized objects   
+;
+; Continue into GetCommand, which determines adapter type (or no adapter)
+; and jumps to an ExtCmd handler, ts_User_startup, ts_Forced_startup or
+; ts_Dealer_startup as appropriate.
+; 
+        B       ts_GetCommand
+
+        GET     TestSrc.ExtIO
+
+;
+; External command handlers - respond to commands given through the
+; external test interface.
+;
+
+        GET     TestSrc.ExtCmd
+
+
+        SUBT    Selftest
+;
+; There is no attached test interface. Is this a power-on reset ?
+; Addressing IOC will make MEMC1a remap the ROM to high memory if
+; it hasn't already done it, so be careful to ensure that the
+; ARM is addressing normally-addressed ROM when this code runs.
+;
+
+ts_User_startup    ROUT
+        LDR     r0,%01
+        ADD     pc,r0,pc
+01
+        &       0
+;
+; IOMD will only access the ROM until a write to IOMD has been made -
+; make this write also switch on refresh so the DRAM has a chance to
+; get running before the memory test starts.
+;
+        [ MEMC_Type = "IOMD"
+        LDR     r1,%02
+        ADD     pc,r0,pc
+02
+        &       (IOMD_Base+IOMD_VREFCR)
+        LDR     r2,%03
+        ADD     pc,r0,pc
+03
+        &       IOMD_VREFCR_REF_16
+        STR     r2, [r1,#0]
+        ]
+
+        [ POSTenabled
+        LDR     r1,%12                  ; load address of IOC IRQ register
+        ADD     pc,r0,pc
+12
+        &       IOC+IOCIRQSTAA
+
+        LDR     r1, [r1,#0]             ; Get IRQSTAA register (hence POR bit)
+        LDR     r2, %13
+        ADD     pc,r0,pc                ; Constant to shift por to bit 31
+13
+        &       por_bit :SHL: 1
+14      ADD     r1,r1,r1
+        ADDS    r2,r2,r2
+        BCC     %14                     ; loop until por_bit is at bit 31
+        ADDS    r1,r1,r1                ; then shift it into carry
+        BCC     ts_Self_test_end        ; POR bit clear - do soft reset.
+
+; it's a power-on reset, so assume we can't be in 32-bit mode
+
+        MOV_fiq r12_fiq, #R_HARD
+        B       ts_Self_test_startup
+        |
+        B       CONT                    ; if user POST disabled
+        ]
+;
+; Perform self - tests
+;
+; Any distinction between test operation for Power-up, Display-only
+; and Forced tests needs to be made between these three entry points.
+;
+
+
+; This is where tests start if a dumb test link is fitted
+; (a diode from A21 to *ROMCS, disabling the ROMs when A21 is high)
+
+ts_Forced_startup  ROUT
+
+        MOV_fiq r12_fiq, #R_TESTED
+        B       ts_Self_test_startup
+
+; This is where the tests start if an external display adapter is fitted
+
+ts_Dealer_startup  ROUT
+
+        MOV_fiq r12_fiq, #R_EXTERN
+
+        LDR     r4,%FT02                ; make a pointer to signon string
+01      ADD     r4,pc,r4
+        ADD     pc,r0,pc
+02      
+        &       (ts_himsg - %BT01 - 8)
+
+        ADD     r14,pc,r0               ; make a return address for this 'call'
+        ASSERT  (.+4 = ts_Self_test_startup)    ; PC must point there already !
+        B       ts_SendText
+
+ts_Self_test_startup ROUT
+
+; This is where the power-on test starts (every user gets this)
+
+
+;
+; Processor test would go here .... if there was one.
+;
+
+;
+; From this point on we assume we can safely use all the processor
+;
+; Initialise VIDC : Sync mode 0, border covers screen
+;
+
+ts_InitVIDC
+        [ IO_Type = "IOMD"              ; If POSTbox fitted, ROM may still be mapped everywhere
+        MOV     r2,#IOMD_Base
+        MOV     r0, #IOMD_VREFCR_REF_16 ; switch on DRAM refresh
+        STR     r0, [r2, #IOMD_VREFCR]
+
+        ; choose monitor settings from ID bit 0
+        MOV     r1,#ts_VIDCPhys
+        ADRL    r2,TestVIDCTAB
+        LDR     r0,=IOMD_MonitorType
+        LDR     r0,[r0]
+        ANDS    r0,r0,#IOMD_MonitorIDMask
+        ADDEQ   r2,r2,#(TestVVIDCTAB-TestVIDCTAB)
+
+        |                               ; not IOMD
+        MOV     r1,#ts_VIDCPhys
+        ADRL    r2,TestVIDCTAB
+        ]
+
+10      LDR     r0, [r2],#4
+        CMP     r0, #-1
+        STRNE   r0, [r1]
+        BNE     %BT10
+
+        LDR     r0,=C_ARMOK     ; set initial screen colour
+        STR     r0, [r1]
+
+        B       ts_RomTest
+
+
+        LTORG
+        ROUT
+
+;
+; Calculate ROM checksum : display status and calculated checksum.
+;
+
+1
+        =       "ROM   :",0
+2
+        =       "ROM bad",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+3
+        =       "ROM size",&8A,&ff,&ff,&ff,&ff,&ff,&ff,0
+        ALIGN
+
+ts_RomTest
+        ADR     r4,%BT1
+        BL      ts_SendText
+
+        BL      ts_ROM_checksum
+        BEQ     %20
+        ADR     r4,%BT2                 ; Failed message
+        FAULT   #R_ROMFAILBIT           ; set ROM bit in r12_fiq
+        MOV     r8,r0                   ; calculated checksum
+        BL      ts_SendText
+
+        BL      ts_ROM_alias            ; Checksum failed :-
+        ADR     r4,%BT3                 ; hunt for first alias
+        MOV     r8,r0, LSL #8
+        BL      ts_SendText             ; and report it.
+20
+
+        [ IO_Type = "IOC-A1"            ; Don't use RISC OS MemSize
+                                        ; until much later - it sets up
+                                        ; the ARM600 MMU as well. 
+        B       ts_MEMCset
+
+;
+; Do MEMC setup and memory size determination (the first time).
+;
+        LTORG
+        ROUT
+
+1
+        =       "M Size :",0
+2 
+        =       "M Size",&89,&ff,&ff,&ff,&ff,".",&ff,&ff,0
+        ALIGN
+
+ts_MEMCset
+        MOV     r12,#0
+        ADR     r4,%BT1
+        BL      ts_SendText
+        LDR     r1,=(&E000C :OR: MEMCADR)       ; MemSize expects 32k page
+        STR     r1,[r1]
+        BL      MemSize
+
+;
+; MemSize returns with  r0 = page size   (now in bytes, *NOT* in MEMC control patterns), 
+;                       r1 = memory size (in bytes)  
+;                       r2 = MEMC control value
+;
+; Translate these into a number that looks like :
+;
+;                       mmmm.pp
+;
+; where mmmm is memory size in hex Kbytes, pp is page size in hex Kbytes.
+;
+        MODE    FIQ_mode                        ; Save memory size and 
+        MOV     r11_fiq,r2                      ; MEMC setup value for
+        MOV     r10_fiq,r1                      ; later use
+        MODE    SVC_mode
+
+        MOV     r8, r0, LSR #2                  ; MemSize now returns actual page size in r0
+        ADD     r8,r8,r1,LSL #6
+        ADR     r4,%BT2
+        BL      ts_SendText
+
+        ]
+
+;
+; Test data, address and byte strobe lines.
+; On MEMC systems, this calls MemSize and tests the memory that finds.
+; On IOMD systems, memory sizing is performed along with the data line
+; tests, and that result is used for address line testing.
+;
+
+        B       ts_LineTest
+
+                GBLS    tsGetMem1
+tsGetMem1       SETS    "GET TestSrc.Mem1" :CC: MEMC_Type
+                $tsGetMem1
+
+;
+; Test IOC. 
+; This shuld require vector space to work (for testing interrupts),
+; but the current version just reports the status register contents.
+; 
+; Display is    ccaabbff
+;
+;  where cc is the control register
+;        aa is IRQ status register A
+;        bb is IRQ status register B
+;        ff is FIQ status register
+;
+
+        B       ts_IOCTest
+
+        LTORG
+        ROUT
+
+        [ IO_Type = "IOMD"
+1
+        =       "IOMD  :",0
+2
+        =       "IOMD-F",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+3
+        =       "IOMD-V"
+4
+        =       &88,&ff,&ff,&ff,&ff," V.",&ff,0
+        |
+1
+        =       "IOC   :",0
+2
+        =       "IOC-F",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+3
+        =       "IOC"
+4
+        =       &88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+        ]
+        ALIGN
+
+ts_IOCTest
+        ADR     r4,%BT1
+        BL      ts_SendText
+        BL      ts_IOCreg       ; check register integrity
+        BEQ     %FT8
+        ADR     r4,%BT2
+        BL      ts_SendText     ; report if failure
+
+        FAULT   #R_IOCFAILBIT
+8
+        ADR     r4,%BT1
+        BL      ts_SendText
+        BL      ts_IOCstat
+        BEQ     %FT10           ; fail message only printed if
+        ADR     r4,%BT3         ; ID code unrecognised
+        BL      ts_SendText
+        FAULT   #R_IOCFAILBIT   ; .. and set error bit if IOMD code is wrong
+        B       %FT11   
+10
+        ADR     r4,%BT4         ; print the status value
+        BL      ts_MoreText
+11
+
+        [ IO_Type = "IOMD"
+        B       ts_CMOStest
+        |
+        B       ts_IOEBtest
+        ]
+
+        LTORG
+        ROUT
+
+;
+; Check for presence of IOEB ASIC
+; 
+
+        [ IO_Type = "IOEB"
+
+1
+        =       "IOEB  :",0
+2
+        =       "IOEB",&88,"exists",0
+
+
+        ALIGN
+
+ts_IOEBtest
+        ADR     r4,%BT1
+        BL      ts_SendText
+
+        LDR     r0,=ts_IOEB_ID                  ; read an ID register in the IOEB ASIC
+        LDRB    r0, [r0]
+        AND     r0, r0, #&f
+        CMPS    r0, #ts_IOEB_ident              ; if it looks right ( == 5) ..
+        BNE     %10
+
+        FAULT   #R_IOEB                         ; set that bit in the result word
+        ADR     r4, %BT2
+        BL      ts_SendText
+10      B       ts_CMOStest
+        ] ; IOEB IO world
+;
+; Read CMOS
+; Check the checksum, read the memory test flag.
+;
+
+1
+        =       "SRAM  :",0
+2
+        =       "SRAM-F",0
+3
+        =       "SRAM-C",&8e,&ff,&ff,0
+        ALIGN
+
+ts_CMOStest
+        ADR     r4,%BT1
+        BL      ts_SendText
+
+        [ ChecksumCMOS
+
+        LDR     r0,=(ts_BBRAM + &4000)
+        MOV     r1,#&C0                 ; Get first RAM area
+        MOV     r2,#CMOSxseed
+        BL      ts_CMOSread
+        BNE     %20
+        MOV     r2, r0
+        LDR     r0,=(ts_BBRAM + &1000)  ; Accumulate the second RAM area
+        MOV     r1,#&2F
+        BL      ts_CMOSread
+        BNE     %20
+        RSB     r2, r0, #0              ; Subtract from the checksum byte
+        LDR     r0,=(ts_BBRAM + &3F00)
+        MOV     r1,#1
+        BL      ts_CMOSread
+        BNE     %20
+        MOV     r8, r0, LSL #24
+        ANDS    r0, r0, #&FF            ; A zero result ?
+        MOV     r1, #R_CHKFAILBIT
+        ADR     r4,%BT3                 ; Report checksum failure
+        BNE     %21                     ; failed .. report error
+        ]                               ; end ChecksumCMOS
+
+        LDR     r0,=(ts_BBRAM + &FC00)  ; Read Misc1CMOS byte
+        MOV     r1,#1
+        MOV     r2,#0
+        BL      ts_CMOSread
+        BNE     %20
+        ANDS    r0,r0,#&80              ; Test the memory-test-disable bit
+        BEQ     %25     
+        FAULT   #R_MEMSKIP              ; If set, skip the memory test  
+        B       %25
+
+20
+        MOV     r1,#R_CMSFAILBIT        ; Real fault - set the fault bit
+        ADR     r4,%BT2                 ; Report fault accessing IIC 
+                                        ; (Bitmap & POST display)
+21
+        FAULT   r1
+        BL      ts_SendText             ; Report one fault or another
+25
+        B       ts_IOinit
+
+        LTORG
+        ROUT
+;
+; Initialize  various machine registers - e.g, turn off the floppy
+; drive, etc, etc.
+;
+
+1
+        =       "IOinit:",0
+        ALIGN
+
+ts_IOinit
+        ADR     r4,%BT1
+        BL      ts_SendText
+        ADRL    r2,ts_IOinitab
+10
+        LDR     r0,[r2],#4              ; Get address
+        LDR     r1,[r2],#4              ; Get initialization data
+        CMPS    r0,#(-1)
+        STRNE   r1,[r0]                 ; write to IO port
+        BNE     %10
+        B       Speedset
+;
+; Use the RISC OS MEMC setup code to guess the proper processor / memory
+; configuration. The memory speed can then be set up correctly for 
+; fastest possible working, and the memory array tested in the 
+; configuration RISC OS expects.
+;
+; Display the results of the TimeCPU test as :
+;
+;               ssss.m.r
+;
+; where ssss is the processor speed in hex kHz, 
+;       m    is 0 for MEMC, 1 for MEMC1a
+;       r    is the MEMC rom speed switch setting. 
+; 
+        ROUT
+
+1
+        =       "Speed :",0
+2
+        =       "Speed",&88,&ff,&ff,&ff,&ff,".",&ff,".",&ff,0
+
+        ALIGN
+
+Speedset
+        ADR     r4,%BT1
+        BL      ts_SendText
+
+        [ MEMC_Type = "IOMD"
+        MOV     r9,#0
+        |
+        MOV_fiq r0, r11_fiq                     ; get MEMC setup
+        MOV     r9,r0                           ; compare IOC and CPU clocks
+        ]
+
+        BL      TimeCPU
+        MOV     r0,r9
+        MOV_fiq r11_fiq,r0
+
+        MOV     r8,r7,LSL #16
+        TST     r7, #1 :SHL: 16                 ; test bit 16 of r7 : 
+        ADDNE   r8,r8,#&1000                    ; MEMC1 / MEMC1a detected
+        AND     r9,r9,#&C0                      ; get High ROM access bits
+        ADD     r8,r8,r9, LSL #2
+        ADR     r4,%BT2
+        BL      ts_SendText
+        B       RAMtest
+
+
+;
+; Long RAM test, ideally exercising all memory.
+; In order to keep boot time short, the following scheme is used :
+; 
+; Normal power-on boot - test VRAM and up to 4M of first DRAM entry
+; CMOS disable set     - test nothing
+; Test hardware fitted - test entire memory
+;
+
+        ROUT
+
+
+1
+        =       "RAM   :",0
+2
+        =       "RAM bad",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+3
+        =       &89,"skipped",0
+4
+        =       "RAM   :",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+
+
+        ALIGN
+
+RAMtest
+        ADR     r4,%BT1
+        BL      ts_SendText
+;
+; if (R_MEMSKIP && R_HARD)
+;       skip all the remaining tests
+; if (!R_LINFAILBIT) 
+;       perform the long memory test
+;
+        MOV_fiq r0,r12_fiq              ; skip this test if data line fault
+        AND     r1,r0,#(R_MEMSKIP :OR: R_HARD)  ; or the user didn't want it
+        TEQS    r1,#(R_MEMSKIP :OR: R_HARD)
+        ANDNE   r1,r1,#R_LINFAILBIT
+        TEQNE   r1,#R_LINFAILBIT
+        BNE     %12
+        ADR     r4,%BT3                 ; skipping memory test ....
+        BL      ts_MoreText 
+        B       ts_Report
+12
+        LDR     r1,=C_RAMTEST           ; doing at least part of the long memory test
+        LDR     r0,=ts_VIDCPhys         ; write the border colour
+        STR     r1,[r0]
+
+        BL      MemSize                 ; Set MMU up, mapping (some) RAM at logical address 0
+                                        ; Note that this returns with the MMU enabled,
+                                        ; the ROM remapped to it's ORGed address,
+        RSB     r4,r4,#PhysROM          ; and r4 the offset from physical to ORGed ROM addresses
+        ADD     r4,r4,#PhysSpace
+        SetMode SVC32_mode,r0           ; Must do this, as PhysSpace is outside 26 bit addressing
+        ADD     pc,pc,r4                ; Jump into the ROM at its image in PhysSpace
+        NOP                             ; this instruction skipped by pc adjustment
+
+;
+; Modify the PhysRamTable so only VRAM and the first ts_MaxRamTest of DRAM gets tested
+;
+        M32_fiq r0,r12_fiq,r1,r2        ; get the test condition flags
+
+        ANDS    r0,r0,#(R_EXTERN :OR: R_TESTED)
+        BNE     %FT16                   ; do full test if test adapter is present
+        MOV     r9,#PhysRamTable
+        ADD     r10,r9,#(PhysRamTableEnd-PhysRamTable)
+14
+        LDR     r1,[r9, #4]
+        ADD     r0,r0,r1                ; r0 = running sum of memory sizes
+        SUBS    r2,r0,#ts_MaxRamTest    ; r2 = excess over ts_MaxRamTest
+        SUBHI   r1,r1,r2                ; r1 = current size truncated
+        STRHI   r1,[r9, #4]
+        MOVHI   r0,#ts_MaxRamTest       ; truncate running sum to MaxRamTest
+
+        ADD     r9,r9,#(DRAMPhysAddrB-DRAMPhysAddrA)
+        CMPS    r9,r10
+        BNE     %BT14
+16
+        FAULT32 #R_MEMORY,r0            ; memory tests were attempted
+
+        MOV     r9,#VideoPhysAddr
+        LDR     r8,[r9]                 ; report the test address
+        ADRL    r4,%BT4
+        BL      ts_SendText
+        LDR     r0,[r9]                 ; get VRAM start address and size
+        LDR     r1,[r9,#4]
+        ADD     r0,r0,#PhysSpace
+        BL      ts_RamTest
+        BNE     %FT20                   ; failed - abort ram testing
+
+;
+; VRAM (or 1st MB of DRAM, if no VRAM fitted) looks OK - move the translation
+; table there so memory tests can proceed without smashing it.
+; 
+        MOV     r9,#PhysRamTable
+        LDR     r0,[r9,#VideoPhysAddr-PhysRamTable]     ; get address of video RAM
+        LDR     r1,[r9,#DRAMPhysAddrA-PhysRamTable]     ; get address of 1st DRAM bank
+        LDR     r3, =DRAMOffset_L2PT
+        ADD     r1, r1, r3              ; make r1 -> L2PT
+        ADD     r0, r0, r3              ; make r0 -> temporary L2PT
+        BL      ts_remap_ttab           ; copy ttab at r1 to r0 and change table base
+        
+;
+; Now run the RAM test at each DRAMPhysAddr until the end of the table or a zero entry
+; is reached. Mark tested entries by setting the PhysSpace address, so a pointer to the
+; next entry need not be kept.
+;
+18
+        MOV     r9,#DRAMPhysAddrA
+        ADD     r10,r9,#(PhysRamTableEnd-DRAMPhysAddrA)
+19
+        CMPS    r9,r10                  ; reached end of table ?
+        LDRNE   r0,[r9]
+        TSTNE   r0,r0                   ; reached unused entries ?
+        LDRNE   r1,[r9,#4]              ; or blanked-out entries ?
+        TSTNE   r1,r1
+        BEQ     %FT21                   ; .. all passed OK
+        TSTS    r0,#PhysSpace
+        ADDNE   r9,r9,#(DRAMPhysAddrB-DRAMPhysAddrA)
+        BNE     %BT19                   ; this entry done .. find the next
+
+        MOV     r8,r0                   ; report address of this block
+        ADRL    r4,%BT4
+        BL      ts_SendText
+
+        LDR     r0,[r9]                 ; start testing it
+        ADD     r0,r0,#PhysSpace
+        LDR     r1,[r9, #4]
+        STR     r0,[r9]                 ; mark block so it isn't retested
+        MOV     r2,#PhysRamTable
+        LDMIA   r2,{r3-r14}             ; save the PhysRamTable
+        STMIA   r0,{r3-r14}
+        BL      ts_RamTest
+        LDMIA   r13,{r1-r11,r14}        ; restore the PhysRamTable
+        MOV     r13,#PhysRamTable
+        STMIA   r13,{r1-r11,r14}
+        BEQ     %BT18                   ; if it passed, go look for another block
+
+20
+        FAULT32 #R_MEMFAILBIT,r2        ; failed - report fault address
+        ADRL    r4,%BT2
+        MOV     r11,r1                  ; Save failed data
+        MOV     r8,r0                   ; first failing address
+        BL      ts_SendText
+        MOV     r4,r12                  ; get fault message
+        MOV     r8,r11                  ; and fault data
+        BL      ts_SendText
+21
+
+ [ MEMM_Type = "MEMC1"
+
+;
+; Test the CAMs - for each fitted MEMC, go through all the CAM entries
+; remapping logical memory and testing against physical correspondence.
+; Then try out the protection bits in each CAM entry and various 
+; processor modes. 
+; These tests return pointers to their own fault report strings.
+;
+        B       ts_CAMtest
+        ROUT
+1
+        =       "CAMs :",0
+2
+        =       "PPLs :",0
+3
+        =       &89,"skipped",0
+        ALIGN
+
+ts_CAMtest
+        LDR     r4,=%BT1
+        BL      ts_SendText
+
+        MOV_fiq r0,r12_fiq              ; skip this test if memory fault
+        MOV     r1,#(R_LINFAILBIT :OR: R_MEMFAILBIT)
+        ANDS    r0,r0,r1
+        BEQ     %08
+        LDR     r4,=%BT3
+        BL      ts_MoreText 
+        B       %20
+
+08
+        BL      ts_CAM
+        BEQ     %10
+        BL      ts_SendText
+        FAULT   #R_CAMFAILBIT
+10
+        LDR     r4,=%BT2
+        BL      ts_SendText
+
+        MOV_fiq r0,r12_fiq              ; skip this test if memory fault
+        MOV     r1,#(R_LINFAILBIT :OR: R_MEMFAILBIT)
+        ANDS    r0,r0,r1
+        BEQ     %18
+        LDR     r4,=%BT3
+        BL      ts_MoreText 
+        B       %20
+18
+        BL      ts_memc_prot
+        BEQ     %20
+        BL      ts_SendText
+        FAULT   #R_PROFAILBIT
+20
+
+ ]
+
+;
+; After testing memory and translation, turn MMU off again before running remainder
+; of tests. This simplifies finishing up (where system must be put back into 26-bit
+; mode before initialising RISCOS) if memory tests were deselected.
+; Take care to poke the real translation table - it's been relocated to video
+; RAM during the memory tests.
+;
+
+ts_restore_physical
+        MOV     r5, pc                          ; obtain current address
+        SUB     r5, r5,#PhysSpace               ; adjust to point to unmapped version
+        MOV     r5, r5, LSR #20                 ; divide by 1MB
+        MOV     r7, r5, LSL #20                 ; r7 = physical address of base of section
+        ORR     r7, r7, #(AP_None * L1_APMult)
+        ORR     r7, r7, #L1_Section
+        MOV     r3, #VideoPhysAddr              ; find the copied translation table
+        LDR     r3, [r3]
+        ADD     r3, r3, #PhysSpace
+        ADD     r3, r3, #DRAMOffset_L1PT
+        STR     r7, [r3, r5, LSL #2]            ; store replacement entry in L1 (not U,C or B)
+
+        SetCop  r7, CR_IDCFlush                 ; flush cache + TLB just in case
+        SetCop  r7, CR_TLBFlush                 ; (data written is irrelevant)
+
+; The ROM should now be mapped at the present address less PhysSpace, which is where it
+; would be if the MMU were turned off.
+
+        MOV     r4,#PhysSpace
+        SUB     pc,pc,r4
+        NOP                             ; this instruction is skipped
+
+        MOV     r7, #MMUC_D             ; Now turn the MMU off
+        SetCop  r7, CR_Control
+
+        B       ts_VIDCtest
+
+
+;
+; The VIDC tests check vertical blanking frequency in a fixed video
+; mode and measure the time taken for sound DMA.
+;
+
+        ROUT
+
+1
+        =       "VIDC  :",0
+2
+        =       "Virq bad",&88,' ',&ff,'.',&ff,&ff,&ff,&ff,&ff,0
+3
+        =       "Sirq bad",&8B,&ff,&ff,&ff,&ff,&ff,0
+4
+        =       &8A,"Mid0 ",&ff,0
+
+        ALIGN 
+
+ts_VIDCtest
+        ADR     r4,%BT1
+        BL      ts_SendText
+        [ IO_Type = "IOMD"
+        LDR     r0,=IOMD_MonitorType    ; Indicate monitor ID bit's value
+        LDR     r0,[r0]
+        AND     r0,r0,#IOMD_MonitorIDMask
+        MOV     r8,r0,LSL #28
+        ADR     r4,%BT4
+        BL      ts_MoreText
+        ]
+
+        BL      ts_VIDC_period
+        BEQ     %10
+        ADR     r4,%B2
+        MOV     r8, r0, LSL #8
+        BL      ts_SendText             ; Display Virq fail msg
+        FAULT   #R_VIDFAILBIT
+10
+        [ IO_Type = "IOMD"
+        MOV     r3,#IOMD_Base           ; skip Sirq test on version 1 IOMD
+        LDRB    r0,[r3,#IOMD_VERSION]
+        CMPS    r0,#1
+        BEQ     %FT20
+        ]       
+        BL      ts_SIRQ_period
+        BEQ     %20
+        ADR     r4,%B3
+        MOV     r8, r0, LSL #12
+        BL      ts_SendText             ; Display Sirq fail msg
+        FAULT   #R_SNDFAILBIT
+20
+        MOV     r1,#ts_VIDCPhys         ; Restore full-screen
+        ADRL    r2,TestVIDCTAB          ; border colour.
+        [ IO_Type = "IOMD"
+        LDR     r0,=IOMD_MonitorType
+        LDR     r0,[r0]
+        ANDS    r0,r0,#IOMD_MonitorIDMask
+        ADDEQ   r2,r2,#(TestVVIDCTAB-TestVIDCTAB)
+        ]
+30      LDR     r0, [r2],#4
+        CMP     r0, #-1
+        STRNE   r0, [r1]
+        BNE     %BT30
+        LDR     r0,=C_ARMOK             ; set initial screen colour
+        STR     r0, [r1]
+
+        B       ts_ARMtype_test
+
+;
+; Read the ARM3 identification register. 
+; If memory tests failed, this won't be performed since the vector
+; page must exist for error recovery on ARM2 systems.
+;
+
+        ROUT
+1
+        =       "ARM ID:",0
+2
+        =       "ARM ID",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+3
+        =       &89,"skipped",0
+
+        ALIGN
+
+ts_ARMtype_test
+
+        ADR     r4,%BT1
+        BL      ts_SendText
+
+        MOV_fiq r0,r12_fiq              ; skip this test if memory fault
+        LDR     r1,=((R_LINFAILBIT :OR: R_MEMFAILBIT) :OR: (R_CAMFAILBIT :OR: R_PROFAILBIT))
+        ANDS    r0,r0,r1
+        BEQ     %05
+        ADR     r4,%BT3
+        BL      ts_MoreText 
+        B       %08                     ; and quit
+
+05
+        BL      ts_ARM_type
+        MOVS    r8, r0                  ; ready to display ID code
+        ADR     r4,%BT2
+
+        BEQ     %FT07                   ; ARM 2 : skip cache test
+        FAULT   #R_ARM3                 ; not really a fault, just status
+07
+        BL      ts_SendText
+
+08
+        B       ts_Report
+
+
+
+;
+; Report the test results to the user
+;
+; If this was a forced test (test adapter fitted) then pause even when 
+; test passed : otherwise, pause only on error.
+;
+
+ts_passmsg
+        =       "PASS  :",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+ts_failmsg
+        =       "FAIL  :",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+
+ts_R00  &       00
+
+ts_Report       ROUT
+        MOV_fiq r7,r12_fiq              ; check for fault bits set
+        LDR     r0,=R_STATUS
+        BICS    r0,r7,r0
+
+        ADREQ   r4, ts_passmsg          ; tests passed
+        LDREQ   r9,=C_PASSED
+
+        ADRNE   r4, ts_failmsg          ; tests failed
+        LDRNE   r9,=C_FAULT          
+
+        LDR     r0,=ts_VIDCPhys         ; write the border colour
+        STR     r9,[r0]
+
+        MOV     r8,r7
+        BL      ts_SendText             ; write the message and fault code
+
+        ; if the test adapter is present, leave green screen awhile
+        ; otherwise, wait only if there's a fault.
+
+        LDR     r3,=ts_recover_time
+00      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %B00                    ; - let the adapter recover 
+                                        ; from previous bus activity
+        ADR     r2,ts_R00
+        ORR     r2,r2,#ts_Alias_bits
+        LDR     r3,[r2]
+        MOV     r2,#-1
+        ADDS    r3,r3,r2
+        BCS     ts_Report_wait
+
+        MOV_fiq r0,r12_fiq
+        LDR     r2,=R_STATUS
+        BICS    r0,r0,r2
+        BEQ     ts_Hardstart
+
+ts_Report_wait        ROUT
+
+;
+; Indicate fault found : Set the border to the fault colour and flash 
+; the disk LED, using the fault bitmap in r12_fiq to modulate the flashing.
+
+ts_oldLED_on       *       &be0000         ; assert SEL0 and INUSE
+ts_oldLED_off      *       &ff0000         ; on machines with 1772 controller
+ts_oldLEDaddr      *       (ts_S5_base :OR: &40)
+
+ts_710LED_on       *       &100000         ; assert SEL0 and MotorEN0
+ts_710LED_off      *       &110000         ; on machines with 82C710 controller
+ts_710LEDaddr      *       (ts_PCaddress :OR: (&3f2 :SHL: 2))
+
+ts_665LED_on       *       &10          ; assert SEL0 and MotorEN0
+ts_665LED_off      *       &11          ; on machines with 37665 controller
+                                        ; and Medusa low-byte I/O world
+ts_665LEDaddr      *       (ts_PCaddress :OR: (&3f2 :SHL: 2))
+
+
+01      MOV_fiq r6,r12_fiq
+        LDR     r2,=&11111111
+        LDR     r7,=(35000 * 8)         ; 1/4 second pause loop count
+
+        [ IO_Type = "IOMD"
+        LDRNE   r1,=ts_665LEDaddr       ; set up for Medusa disc address
+        MOVNE   r8,#ts_665LED_on
+        MOVNE   r9,#ts_665LED_off
+        |
+        TST     r6, #R_IOEB             ; determine original / 710 disc controller
+        LDREQ   r1,=ts_oldLEDaddr       ; set up for Archimedes disc address
+        MOVEQ   r8,#ts_oldLED_on
+        MOVEQ   r9,#ts_oldLED_off
+        LDRNE   r1,=ts_710LEDaddr       ; set up for Brisbane disc address
+        MOVNE   r8,#ts_710LED_on
+        MOVNE   r9,#ts_710LED_off
+        ]
+
+02      MOV     r0,r7
+03      SUBS    r0,r0,#1                ; pause for a 1/4 second
+        BNE     %03
+
+        MOV     r0,r8                   ; turn the LED on
+        STR     r0,[r1]
+
+        MOV     r0,r7
+04      SUBS    r0,r0,#1                ; pause for a 1/4 second
+        BNE     %04
+        ADDS    r6,r6,r6                ; if a '1' is to be written,
+        BCC     %06
+        MOV     r0,r7,LSL #1            ; then pause another 1/2 second
+05      SUBS    r0,r0,#1
+        BNE     %05
+
+06
+        MOV     r0, r9                  ; turn the LED off
+        STR     r0,[r1]
+
+;
+; Count down 32 bits. Every 4 bits, insert an extra pause to simplify
+; reading the flashes.
+;
+        ADDS    r2,r2,r2
+        BCC     %08
+        MOV     r0,r7,LSL #2            ; then pause another second
+05      SUBS    r0,r0,#1
+        BNE     %05
+08
+        ANDS    r2,r2,r2                ; all the bits displayed now ?
+        BNE     %02
+        MOV_fiq r0,r12_fiq              ; restore the faultcode bits
+
+        ANDS    r0,r0,#(R_EXTERN :OR: R_TESTED) ; If test adapter present, 
+        BNE     Reset                   ; repeat test forever
+
+        B       CONT                    ; otherwise, run RISC OS
+
+ts_Hardstart
+        MOVS    r0,#R_HARD              ; and report a hard start
+        B       CONT                    ; to RISC OS
+
+;
+; Tests skipped : fall into RISC-OS
+;
+
+ts_Self_test_end
+
+        LDR     r1,=C_WARMSTART
+        LDR     r0,=ts_VIDCPhys         ; write the border colour
+        STR     r1,[r0]
+
+ts_Softstart
+        MOVS    r0,#R_SOFT              ; soft reset indicator
+        B       CONT
+
+
+        ROUT
+
+;
+; This table consists of a series of address/data pairs for IO
+; initialization.
+; Note that these addresses are likely to be in the IO world,
+; and hence the data written is that from the MOST significant
+; 16 bits of the data bus.
+; An 'address' of -1 terminates the table.
+;
+
+ts_IOinitab
+        [ IO_Type = "IOMD"
+        |
+        & ts_S5_base :OR: &10,  &000000         ; Printer port data
+        & ts_S5_base :OR: &18,  &000000         ; FDC control & printer strobes
+        & ts_S5_base :OR: &40,  &ff0000         ; FDD select lines
+        & ts_S5_base :OR: &48,  &000000         ; VIDC clock control
+        ]
+        & (-1)
+
+
+
+
+
+;
+;
+;---------------------------------------------------------------------------
+
+        LTORG
+
+
+; Include test modules executed by call, rather than inline 
+
+        GET     TestSrc.Mem2
+        GET     TestSrc.Mem3
+        GET     TestSrc.Mem4
+        GET     TestSrc.Mem5
+        GET     TestSrc.Vidc
+        GET     TestSrc.Ioc
+        GET     TestSrc.Cmos
+        GET     TestSrc.Arm3
+
+        END
diff --git a/OldTestSrc/Cmos b/OldTestSrc/Cmos
new file mode 100644
index 0000000000000000000000000000000000000000..610193c5070a152e06e8749a75abfbfa24d333bd
--- /dev/null
+++ b/OldTestSrc/Cmos
@@ -0,0 +1,321 @@
+; > TestSrc.Cmos
+
+        TTL RISC OS 2+ POST battery-backed RAM access
+;
+; A function to read bytes from CMOS, for use in verifying the checksum
+; and reading memory test flag & video modes.
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name            Comment
+; ----          ----            -------
+; 05-Apr-91     ArtG            Initial version, based on IICMod.
+;
+;
+;------------------------------------------------------------------------
+;
+; in:
+;       R0 = device address          (bit 8 - 15   register address    )
+;       R1 = length of block to read
+;       R2 = initial sum value
+;
+; out:  R0 = sum of all bytes in block
+;       R1 - R13 trashed
+;  
+
+ts_CMOSread     ROUT
+
+        MOV     R13,R14
+        MOV     R8,R2                   ; initialise accumulator
+        MOV     R7,R1                   ; initialise byte counter
+        MOV     R6,R0                   ; stash register address
+        MOV     R2, #IOC
+        MOV     R0, #-1                 ; ensure timer is ticking
+        STRB    R0, [R2, #Timer0LL]     ; (nonzero in input latch)
+        STRB    R0, [R2, #Timer0LH]
+        STRB    R0, [R2, #Timer0GO]     ; load the count registers
+        BL      ts_Start
+        BEQ     %FT30                   ; check clock line toggles OK
+        AND     R0, R6, #&FE
+        BL      ts_TXCheckAck           ; transmit device address (write)
+        BVS     %FT30
+        MOV     R0, R6, LSR #8
+        BL      ts_TXCheckAck           ; write register address
+        BVS     %FT30
+        BL      ts_Start                ; Extra START bit to switch modes
+        AND     R0, R6, #&FE
+        ORR     R0, R0, #1
+        BL      ts_TXCheckAck           ; transmit device address (read)
+        BVS     %FT30
+20
+        BL      ts_RXByte               ; read byte from bus
+        ADD     R8, R8, R0              ; accumulate total
+        SUBS    R7, R7, #1              ; is it last byte ?
+        MOVNE   R0, #0                  ; no, then acknowledge with 0 bit
+        MOVEQ   R0, #1                  ; yes, then don't acknowledge
+        BL      ts_ClockData            ; but always send ack clock pulse
+        TEQ     R7, #0                  ; loop, until last byte
+        BNE     %BT20
+30
+        MOVVS   R7, #-1                 ; pass error indicator to caller
+        BL      ts_Stop
+        MOV     R0, R8
+        TEQ     R7, #0                  ; return zero flag if read OK
+        MOV     PC,R13
+
+; *****************************************************************************
+;
+;       TXCheckACK - transmit a byte and wait for slave to ACK
+;
+;  out: Trashes r0,r1,r2,r3,r4,r5,r9,r10,r11,r12
+;       V bit set on error.
+;
+
+ts_TXCheckAck ROUT
+        MOV     R12,R14
+        BL      ts_TXByte
+        BL      ts_Acknowledge
+        MOVVC   PC, R12                 ; acknowledged ok, so return
+        ORRS    PC, R12, #V_bit
+
+; *****************************************************************************
+;
+;       SetC1C0 - Set clock and data lines to values in R1 and R0 respectively
+;
+; out:  Trashes r0,r1,r2,r11
+;
+
+ts_SetC1C0 ROUT
+        MOV     R11, R14
+        BIC     R14, R14, #Z_bit                ; indicate not checking clock
+ts_SetOrCheck
+        ORR     R14, R14, #I_bit                ; disable interrupts
+        TEQP    R14, #0
+
+        ADD     R0, R0, R1, LSL #1              ; R0 := C0 + C1*2
+
+        ORR     R0, R0, #&C0                    ; make sure two test bits are
+                                                ; always set to 1 !
+        MOV     R2, #IOC
+        STRB    R0, [R2, #IOCControl]
+10
+        LDREQB  R1, [R2, #IOCControl]           ; wait for clock
+        TSTEQ   R1, #i2c_clock_bit              ; to read high
+        BEQ     %BT10
+
+        MOV     R0, #10                         ; delay for >= 10/2 microsecs
+;
+; in-line do-micro-delay to save a stack level
+;
+        STRB    R0, [R2, #Timer0LR]     ; copy counter into output latch
+        LDRB    R1, [R2, #Timer0CL]     ; R1 := low output latch
+20
+        STRB    R0, [R2, #Timer0LR]     ; copy counter into output latch
+        LDRB    R14, [R2, #Timer0CL]    ; R14 := low output latch
+        TEQ     R14, R1                 ; unchanged ?
+        MOVNE   R1, R14                 ; copy anyway
+        BEQ     %BT20                   ; then loop
+        SUBS    R0, R0, #1              ; decrement count
+        BNE     %BT20                   ; loop if not finished
+;
+; end do-micro-delay
+;
+        MOV     PC, R11
+
+; Set clock and data lines to R1 and R0 and then wait for clock to be high
+
+ts_SetC1C0CheckClock ROUT
+        MOV     R11, R14
+        ORR     R14, R14, #Z_bit                ; indicate checking clock
+        B       ts_SetOrCheck
+
+
+; *****************************************************************************
+;
+;       ClockData - Clock a bit of data down the IIC bus
+;
+; in:   R0 = data bit
+;
+; out:  Trashes r0,r1,r2,r3,r10,r11
+;
+
+ts_ClockData ROUT
+        MOV     R10,R14
+
+        MOV     R3, R0                  ; save data
+        MOV     R1, #0                  ; clock LO
+        BL      ts_SetC1C0
+
+        MOV     R1, #1                  ; clock HI
+        MOV     R0, R3
+        BL      ts_SetC1C0CheckClock
+
+; Delay here must be >= 4.0 microsecs
+
+        MOV     R1, #0                  ; clock LO
+        MOV     R0, R3
+        BL      ts_SetC1C0
+
+        MOV     PC,R10
+
+; *****************************************************************************
+;
+;       Start - Send the Start signal
+;
+; out:  Trashes r0,r1,r2,r9,r11
+;       R0 (and Z flag) indicates state of clock .. should be NZ.
+;
+
+ts_Start   ROUT
+        MOV     R9,R14
+
+        MOV     R0, #1                  ; clock HI, data HI
+        MOV     R1, #1
+        BL      ts_SetC1C0
+
+; Delay here must be >= 4.0 microsecs
+
+        MOV     R0, #0                  ; clock HI, data LO
+        MOV     R1, #1
+        BL      ts_SetC1C0
+
+; Make sure clock really is high (and not shorted to gnd)
+
+        LDRB    R3, [R2, #IOCControl]
+
+; Delay here must be >= 4.7 microsecs
+
+        MOV     R0, #0                  ; clock LO, data LO
+        MOV     R1, #0
+        BL      ts_SetC1C0
+
+        ANDS    R0, R3, #i2c_clock_bit
+        MOV     PC,R9
+
+; *****************************************************************************
+;
+;       Acknowledge - Check acknowledge after transmitting a byte
+;
+; out:  Trashes r0,r1,r2,r3,r9,r11
+;       V=0 => acknowledge received
+;       V=1 => no acknowledge received
+;
+
+ts_Acknowledge ROUT
+        MOV     R9,R14
+
+        MOV     R0, #1                  ; clock LO, data HI
+        MOV     R1, #0
+        BL      ts_SetC1C0
+
+        MOV     R0, #1                  ; clock HI, data HI
+        MOV     R1, #1
+        BL      ts_SetC1C0CheckClock
+
+; Delay here must be >= 4.0 microsecs
+
+        MOV     R2, #IOC
+        LDRB    R3, [R2, #IOCControl]   ; get the data from IOC
+
+        MOV     R0, #1                  ; clock LO, data HI
+        MOV     R1, #0
+        BL      ts_SetC1C0
+
+        TST     R3, #1                  ; should be LO for correct acknowledge
+        MOV     R3, PC
+        BICEQ   R3, R3, #V_bit          ; clear V if correct acknowledge
+        ORRNE   R3, R3, #V_bit          ; set V if no acknowledge
+        TEQP    R3, #0
+
+        MOV     PC,R9
+
+; *****************************************************************************
+;
+;       Stop - Send the Stop signal
+;
+; out:  Trashes r0,r1,r2,r9,r11
+;
+
+ts_Stop    ROUT
+        MOV     R9,R14
+
+        MOV     R0, #0                  ; clock HI, data LO
+        MOV     R1, #1
+        BL      ts_SetC1C0
+
+; Delay here must be >= 4.0 microsecs
+
+        MOV     R0, #1                  ; clock HI, data HI
+        MOV     R1, #1
+        BL      ts_SetC1C0
+
+        MOV     PC,R9
+
+; *****************************************************************************
+;
+;       TXByte - Transmit a byte
+;
+; in:   R0 = byte to be transmitted
+;
+; out:  Trashes r0,r1,r2,r3,r4,r5,r9,r10,r11
+;
+
+ts_TXByte  ROUT
+        MOV     R9, R14
+        MOV     R4, R0                  ; byte goes into R4
+        MOV     R5, #&80                ; 2^7   the bit mask
+10
+        ANDS    R0, R4, R5              ; zero if bit is zero
+        MOVNE   R0, #1
+        BL      ts_ClockData            ; send the bit
+        MOVS    R5, R5, LSR #1
+        BNE     %BT10
+        MOV     PC, R9
+
+; *****************************************************************************
+;
+;       RXByte - Receive a byte
+;
+; out:  R0 = byte received
+;       Trashes r1,r2,r3,r4,r9,r11
+;
+
+ts_RXByte  ROUT
+        MOV     R9, R14
+        MOV     R3, #0                  ; byte:=0
+        MOV     R2, #IOC
+        MOV     R4, #7
+
+        MOV     R0, #1                  ; clock LO, data HI
+        MOV     R1, #0
+        BL      ts_SetC1C0
+10
+        MOV     R0, #1                  ; pulse clock HI
+        MOV     R1, #1
+        BL      ts_SetC1C0CheckClock
+
+        LDRB    R1, [R2, #IOCControl]   ; get the data from IOC
+        AND     R1, R1, #1
+        ADD     R3, R1, R3, LSL #1      ; byte:=byte*2+(IOC?0)AND1
+
+        MOV     R0, #1                  ; return clock LO
+        MOV     R1, #0
+        BL      ts_SetC1C0
+
+        SUBS    R4, R4, #1
+        BCS     %BT10
+
+        MOV     R0, R3                  ; return the result in R0  
+        MOV     PC, R9
+
+        LTORG
+
+        END
+
+
+
+
+
+
+
+
diff --git a/OldTestSrc/ExtCmd b/OldTestSrc/ExtCmd
new file mode 100644
index 0000000000000000000000000000000000000000..963d2660804f148cac499eb1115b3119699d0fd9
--- /dev/null
+++ b/OldTestSrc/ExtCmd
@@ -0,0 +1,1019 @@
+; > TestSrc.ExtCmd
+
+        TTL RISC OS 2+ POST external commands
+;
+; External test commands for RISC OS ROM.
+;
+; Provides functions to read data, write data and execute code using
+; parameters from an external controlling host.
+;
+; A minimal set of opcodes should be used (ideally, only B, LDR and ADDS)
+; so that a processor test may be validly included in the internal test
+; sequence.
+;
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name            Comment
+; ----          ----            -------
+; 27-Nov-89     ArtG            Initial version
+; 06-Dec-89     ArtG            Release 0.2 for integration
+; 30-Mar-90	ArtG		Added NOPs (ADDS r0,r0,r0) after ADDS pc,..
+; 19-Apr-90	ArtG		Speedups for read/write commands.
+; 15-May-90	ArtG		Fixed multiple %13 label in ts_W_FIW
+; 22-May-90	ArtG		Fixed bugs in ts_B_MWW, ts_W_RIB
+; 18-Jun-93	ArtG		Added Arm600 control instructions
+; 1-Jul-93	ArtG		Replaced ADDS pc.. instructions with ADD pc..
+;				for compatibility with SVC32_mode. 
+;
+;------------------------------------------------------------------------
+
+
+
+;
+; All these routines use registers as follows :
+;
+;       r0  - always zero
+;       r1
+;       r2
+;       r3  - undisturbed : used as constant by I/O routine
+;       r4  - return value from I/O routine, parameter to I/O routines
+;       r5
+;       r6
+;       r7  - saved value of command byte on entry
+;       r8  - operation counter
+;       r9  - pointer to data transfer operation
+;       r10 - increment value (0, 1 or 4) to add to pointer in r9
+;       r11 - decrement constant (-1) to add to counter in r8
+;       r12 - checksum accumulator
+;       r13 - pointer to operation code
+;       r14 - return address for calls to I/O routines
+;
+
+        SUBT    External command handlers
+;
+; Called by vectoring through command_table. 
+; R4 contains command byte (including 3 option bits)
+; Get operation count
+; Get address
+; If single-word data
+;   Get data
+;   Get checksum
+;   Reply with command byte or FF
+;   Do operation
+; Else
+;   For each word
+;     Get data
+;     Do operation 
+;   Get checksum
+;   Reply with command byte or FF
+; Return by branching to GetCommand.
+
+ts_write_memory    ROUT
+
+        ADDS    r13,r0,r4               ; save the control byte
+        ADDS    r7,r0,r4
+        ADDS    r14, r0, pc             ; setup return address for ..
+        B       ts_GetWord              ; .. get operation count word
+        ADDS    r8, r0, r4              ; r8 is operation count
+        ADDS    r12,r0,r4               ; initialise checksum 
+        ADDS    r14, r0, pc
+        B       ts_GetWord              ; r9 is initial target address
+        ADDS    r9, r0, r4
+        ADDS    r12,r12,r4              ; accumulate checksum 
+        ADDS    r10,r0,r0               ; set initial constants
+        LDR     r11,%01
+	ADD     pc,pc,r0
+01
+        DCD     (0 - 1)
+
+;
+; Check for operations which don't involve reading a block of data.
+; These are acknowledged BEFORE performing the operation.
+;
+	ADDS	r0,r0,r0
+        ADDS    r13,r13,r13             ; convert operation code to vector 
+        ADDS    r13,r13,r13
+        LDR     r4, %02
+	ADD     pc,pc,r0
+02
+        &       (ts_write_cmd_table - %03)
+        ADDS    r4,pc,r4
+        ADDS    r13,r4,r13
+03
+        LDR     r13,[r13]               ; fetch pointer to code
+        LDR     r4,%04
+	ADD     pc,pc,r0
+04
+        &       (ts_write_cmd_table - ts_W_fetch_operations) 
+	ADDS	r0,r0,r0
+        ADDS    r4,r4,r13
+        BCS     ts_Write_getdata        ; defer acknowledgement till later
+
+        ; check the above test was valid, given code layout
+	; Note - this is also required by code near ts_Write_cmd_done
+
+        ASSERT  (ts_W_RSW <  ts_W_fetch_operations)
+        ASSERT  (ts_W_RSB <  ts_W_fetch_operations)
+        ASSERT  (ts_W_RIW <  ts_W_fetch_operations)
+        ASSERT  (ts_W_RIB <  ts_W_fetch_operations)
+        ASSERT  (ts_W_FSW >= ts_W_fetch_operations)
+        ASSERT  (ts_W_FSB >= ts_W_fetch_operations)
+        ASSERT  (ts_W_FIW >= ts_W_fetch_operations)
+        ASSERT  (ts_W_FIB >= ts_W_fetch_operations)
+
+;
+; Fetch the first data word and checksum, and acknowledge
+;
+
+        ADDS    r14,r0,pc               ;get next data word
+        B       ts_GetWord
+        ADDS    r12,r12,r4              ;accumulate checksum
+        ADDS    r10,r0,r4
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ;read transmitted checksum
+        ADDS    r4,r4,r12               ;tx + total should be zero
+        LDR     r5,%05
+        ADD     pc,pc,r0
+05
+        &       (0 - 1)
+        ADDS    r5,r5,r4                ;carry set on checksum failure
+        BCS     ts_cmd_error
+
+;
+; Checksum looks OK. Send the command and the checksum back.
+;
+	LDR	r4,%06
+	ADD     pc,pc,r0
+06
+	&	ts_WriteCmdByte
+        ADDS    r4,r4,r7                ;restore the original 
+
+        ADDS    r14,r0,pc
+        B       ts_SendByte
+        ADDS    r4,r0,r12               ;then send the calculated checksum
+        ADDS    r14,r0,pc
+        B       ts_SendWord
+
+        ADDS    r4,r0,r10               ;restore the data word
+        ADDS    r10,r0,r0               ;and the zero in r10
+        B       ts_Write_usedata        ;dive off to do the work
+
+;
+; Enter the main loop, repeating the operation labelled in r13.
+;
+
+ts_Write_getdata
+        ADDS    r9,r9,r10               ;perform increment operation
+        ADDS    r8,r8,r11               ;countdown repeat counter
+        BCC     ts_Write_cmd_ack
+        ADDS    r14,r0,pc               ;get next data word
+        B       ts_GetWord
+        ADDS    r12,r12,r4              ;accumulate checksum
+	B	%07
+
+ts_Write_usedata
+        ADDS    r9,r9,r10               ;perform increment operation
+ts_Write_count
+        ADDS    r8,r8,r11               ;countdown repeat counter
+        BCC     ts_Write_cmd_done
+07
+        ADD     pc,pc,r13               ;jump back to operations
+        &       0
+
+;
+; In this table, the operation after any word fetch is vectored by
+; the 3 least significant bits of the command byte to perform some 
+; combination of writing with  : 
+;
+; bit 2 -> 0    R : repeat with same data
+;          1    F : fetch more data for next operation
+;
+; bit 1 -> 0    S : leave address static
+;          1    I : increment address after operation
+;
+; bit 0 -> 0    W : word operation
+;          1    B : byte operation
+;
+
+        ASSERT  ((ts_write_cmd_table - %07) = 8)
+
+ts_write_cmd_table
+
+        DCD     (ts_W_RSW - ts_write_cmd_table)
+        DCD     (ts_W_RSB - ts_write_cmd_table)
+        DCD     (ts_W_RIW - ts_write_cmd_table)
+        DCD     (ts_W_RIB - ts_write_cmd_table)
+        DCD     (ts_W_FSW - ts_write_cmd_table)
+        DCD     (ts_W_FSB - ts_write_cmd_table)
+        DCD     (ts_W_FIW - ts_write_cmd_table)
+        DCD     (ts_W_FIB - ts_write_cmd_table)
+
+;
+; And here are the trailers that perform these operations.
+; Each is started with the data in r4, address in r9 and completes
+; by returning to Write_getdata (to read another word) or Write_usedata
+; (to repeat with the same data) with r10 = increment value (initially 0)
+;
+
+ts_W_RSW
+        STR     r4,[r9]                 ;store word, repeat address
+        ADDS    r8,r8,r11               ;countdown repeat counter
+        BCS     ts_W_RSW
+        B       ts_Write_cmd_done
+
+ts_W_RSB
+        STRB    r4,[r9]                 ;store byte, repeat address
+	ADDS	r8,r8,r11
+	BCS	ts_W_RSB
+        B       ts_Write_cmd_done
+
+ts_W_RIW
+        LDR     r10,%11
+	ADD     pc,pc,r0
+11
+        DCD     4
+12
+        STR     r4,[r9]                 ;store word, increment word address
+        ADDS    r9,r9,r10               ;perform increment operation
+        ADDS    r8,r8,r11               ;countdown repeat counter
+	BCS	%B12
+        B       ts_Write_cmd_done
+
+
+ts_W_RIB
+	LDR	r10,%13
+	ADD     pc,pc,r0
+13
+        DCD     1
+14
+        STRB    r4,[r9]                 ;store byte, increment byte address
+	ADDS	r9,r9,r10
+	ADDS	r8,r8,r11
+	BCS	%B14
+	B	ts_Write_cmd_done
+
+
+
+ts_W_fetch_operations                   ;all past here fetch new data
+                                        ;on each loop
+
+ts_W_FSW
+        STR     r4,[r9]                 ;store word, repeat address
+        B       ts_Write_getdata
+
+ts_W_FSB
+        STRB    r4,[r9]                 ;store byte, repeat address
+        B       ts_Write_getdata
+
+ts_W_FIW
+        STR     r4,[r9]                 ;store word, increment word address
+        LDR     r10,%15
+        B       ts_Write_getdata
+15
+        DCD     4
+
+ts_W_FIB
+        STRB    r4,[r9]                 ;store byte, increment byte address
+        LDR     r10,%16
+        B       ts_Write_getdata
+16
+        DCD     1 
+
+
+;
+; Operations completed. Operations that read multiple data words from
+; the host must now checksum and acknowledge the block (even though
+; it's a bit late to do anything about it)
+;
+
+ts_Write_cmd_ack
+;
+; Operation involved multiple fetches - only now ready to ACK. 
+;
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ;read transmitted checksum
+        ADDS    r4,r4,r12               ;tx + total should be zero
+        LDR     r5,%25
+        ADD     pc,pc,r0
+25
+        &       (0 - 1)
+        ADDS    r5,r5,r4                ;carry set on checksum failure
+        BCS     ts_cmd_error
+
+;
+; Checksum looks OK. Send the command and the checksum back.
+;
+	LDR	r4,%26
+	ADD     pc,pc,r0
+26
+	&	ts_WriteCmdByte
+        ADDS    r4,r4,r7                ;restore the original 
+        ADDS    r14,r0,pc
+        B       ts_SendByte
+        ADDS    r4,r0,r12               ;then send the calculated checksum
+        ADDS    r14,r0,pc
+        B       ts_SendWord
+
+ts_Write_cmd_done
+        B       ts_GetCommand
+
+
+
+; Called by vectoring through command_table. 
+; R4 contains command byte (including 3 option bits)
+; Get operation count
+; Get address
+; Reply with command byte or FF
+; Reply with checksum
+; For each word
+;   Read data
+;   If Verbose option
+;     Send data
+; If Quiet option
+;   Send result of read operation
+; Send checksum of result packet
+; Return by branching to GetCommand.
+
+ts_read_memory    ROUT
+
+        ADDS    r13,r0,r4               ; save the control byte
+        ADDS    r7,r0,r4
+
+        ADDS    r14, r0, pc             ; setup return address for ..
+        B       ts_GetWord              ; .. get operation count word
+        ADDS    r8, r0, r4              ; r8 is operation count
+        ADDS    r12,r0,r4               ; initialise checksum 
+
+        ADDS    r14, r0, pc
+        B       ts_GetWord              ; r9 is initial target address
+        ADDS    r9, r0, r4
+        ADDS    r12,r12,r4              ; accumulate checksum 
+        ADDS    r10,r0,r0               ; set initial constants
+        LDR     r11,%01
+        ADD     pc,pc,r0
+01
+        DCD     (0 - 1)
+;
+; Convert the operation options into a code pointer
+;
+	ADDS	r0,r0,r0
+        ADDS    r13,r13,r13             ; convert operation code to vector 
+        ADDS    r13,r13,r13
+        LDR     r4, %02
+        ADD     pc,pc,r0
+02
+        &       (ts_read_cmd_table - %03)
+        ADDS    r4,pc,r4
+        ADDS    r13,r4,r13
+03
+        LDR     r13,[r13]               ; fetch pointer to code
+
+;
+; Fetch the checksum, and acknowledge
+;
+
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ;read transmitted checksum
+        ADDS    r4,r4,r12               ;tx + total should be zero
+        LDR     r5,%05
+        ADD     pc,pc,r0
+05
+        &       (0 - 1)
+        ADDS    r5,r5,r4                ;carry set on checksum failure
+        BCS     ts_cmd_error
+
+;
+; Checksum looks OK. Send the command and the checksum back.
+;
+	LDR	r4,%06
+	ADD     pc,pc,r0
+06
+	&	ts_ReadCmdByte
+        ADDS    r4,r4,r7                ;restore the original 
+        ADDS    r14,r0,pc
+        B       ts_SendByte
+        ADDS    r4,r0,r12               ;then send the calculated checksum
+        ADDS    r14,r0,pc
+        B       ts_SendWord
+
+        ADDS    r12,r0,r0               ;initialise the upload checksum 
+        B       ts_Read_count		;enter the loop
+
+;
+; Enter the main loop, repeating the operation labelled in r13.
+; This loop is for operations that finish with all data sent
+
+ts_Read_Txdata                          ;send data to host
+        ADDS    r12,r12,r4              ;accumulate the checksum
+        ADDS    r14,r0,pc
+        B       ts_SendWord             ;send this word
+        ADDS    r9,r9,r10               ;perform increment operation
+        ADDS    r8,r8,r11               ;countdown repeat counter
+        BCC     ts_Read_cmd_done
+        B       %07                     ;go off to the jump handler
+
+ts_Read_count
+        ADDS    r8,r8,r11               ;countdown repeat counter
+        BCC     ts_Read_cmd_read        ;send data at finish
+07
+        ADD     pc,pc,r13               ;jump back to operations
+        &       0
+
+;
+; In this table, the operation after any word fetch is vectored by
+; the 2 least significant bits of the command byte to perform some 
+; combination of reading with  : 
+;
+; bit 2 -> 0    Q : read data without reporting it
+;          1    V : Transmit the result of every read operation
+;
+; bit 1 -> 0    S : leave address static
+;          1    I : increment address after operation
+;
+; bit 0 -> 0    W : word operation
+;          1    B : byte operation
+;
+
+        ASSERT  ((ts_read_cmd_table - %07) = 8)
+
+ts_read_cmd_table
+
+        DCD     (ts_R_QSW - ts_read_cmd_table)
+        DCD     (ts_R_QSB - ts_read_cmd_table)
+        DCD     (ts_R_QIW - ts_read_cmd_table)
+        DCD     (ts_R_QIB - ts_read_cmd_table)
+        DCD     (ts_R_VSW - ts_read_cmd_table)
+        DCD     (ts_R_VSB - ts_read_cmd_table)
+        DCD     (ts_R_VIW - ts_read_cmd_table)
+        DCD     (ts_R_VIB - ts_read_cmd_table)
+
+;
+; And here are the trailers that perform these operations.
+; Each is started with the data in r4, address in r9 and completes
+; by returning to Write_getdata (to read another word) or Write_usedata
+; (to repeat with the same data) with r10 = increment value (initially 0)
+;
+
+ts_R_QSW
+        LDR     r4,[r9]                 ;read word, repeat address
+        ADDS    r8,r8,r11               ;countdown repeat counter
+	BCS	ts_R_QSW
+        B       ts_Read_cmd_read        ;send data at finish
+
+
+ts_R_QSB
+        LDRB    r4,[r9]                 ;read byte, repeat address
+	ADDS	r8,r8,r11
+	BCS	ts_R_QSB
+        B       ts_Read_cmd_read
+
+ts_R_QIW
+        LDR     r10,%11
+	ADD     pc,pc,r0
+11
+        DCD     4
+12
+        LDR     r4,[r9]                 ;read word, increment word address
+        ADDS    r9,r9,r10               ;perform increment operation
+        ADDS    r8,r8,r11               ;countdown repeat counter
+	BCS	%B12
+        B       ts_Read_cmd_read        ;send data at finish
+
+
+ts_R_QIB
+        LDR     r10,%13
+	ADD     pc,pc,r0
+13
+        DCD     1
+14
+        LDRB    r4,[r9]                 ;read byte, increment byte address
+        ADDS    r9,r9,r10               ;perform increment operation
+        ADDS    r8,r8,r11               ;countdown repeat counter
+	BCS	%B14
+        B       ts_Read_cmd_read        ;send data at finish
+
+
+ts_R_VSW
+        LDR     r4,[r9]                 ;read and tx word, repeat address
+        B       ts_Read_Txdata
+
+ts_R_VSB
+        LDRB    r4,[r9]                 ;read and tx byte, repeat address
+        B       ts_Read_Txdata
+
+ts_R_VIW
+        LDR     r4,[r9]                 ;read and tx word, next word address
+        LDR     r10,%15
+        B       ts_Read_Txdata
+15
+        DCD     4
+
+ts_R_VIB
+	ADDS	r0,r0,r0
+        LDRB    r4,[r9]                 ;read and tx byte, next byte address
+        LDR     r10,%16
+        B       ts_Read_Txdata
+16
+        DCD     1 
+
+
+;
+; Operations completed. Report final result and checksum back to host.
+; Quiet option only transmits read data here (this is pretty useless
+; except where only one value was read)
+;
+
+ts_Read_cmd_read
+	ADDS	r12,r12,r4
+        ADDS    r14,r0,pc               ;send result of 'quiet' read
+        B       ts_SendWord
+ts_Read_cmd_done
+        SUBS    r4,r0,r12               ;get overall checksum  - can't think
+        ADDS    r14,r0,pc               ;how to do this using only ADDS !
+        B       ts_SendWord
+
+        B       ts_GetCommand
+
+
+; Called by vectoring through command table.
+; if option 1 set, read processor mode
+; Read address
+; Read and check checksum
+; Reply with command byte or FF
+; Reply with checksum
+; if option 1 set, load SPSR
+; Jump to code
+
+
+ts_execute      ROUT
+	ADDS	r12,r0,r0		; initialise checksum adder
+	LDR	r8,%00			; initialise msr-jumper
+	ADD	pc,pc,r0
+00
+	&	4
+	ADDS	r7,r4,r4		; get operation type
+	ADDS	r7,r7,r7
+	ADD 	pc,pc,r7		; jump to pc + (r4 * 4)
+	&	0
+
+	B	%FT10
+	B	%FT08
+	B	%FT10
+	B	%FT10
+	B	%FT10
+	B	%FT10
+	B	%FT10
+	B	%FT10
+
+
+08	ADDS	r14,r0,pc		; get new processor mode
+	B	ts_GetWord
+	ADDS	r12,r0,r4
+	ADDS	r8,r0,r0		; kill msr-jumper
+10
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ; get jump address
+        ADDS    r9,r12,r4
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ; get checksum
+        ADDS    r4,r4,r9
+        LDR     r5,%11
+        ADD     pc,pc,r0
+11
+        &       (0 - 1)
+        ADDS    r4,r5,r4                ; compare total chex with zero
+        BCS     ts_cmd_error            ; carry set on error
+
+	LDR	r4,%12
+	ADD     pc,pc,r0
+12
+	&	ts_ExecuteCmdByte
+	ADDS	r0,r0,r0
+        ADDS    r14,r0,pc  	        ; echo command byte
+        B       ts_SendByte
+        ADDS    r4,r0,r9                ;return checksum (actually, the
+        ADDS    r14,r0,pc               ; entire message ..)
+        B       ts_SendWord
+
+
+; Now jump to the location given in the message, using the given status bits
+
+	ADD	pc,pc,r8		; jump over the msr instruction
+	NOP
+	&	2_11100001011010011111000000001100 ; 
+
+	ADDS	r14,pc,r0		; Load the address of %13 into r14
+			           	; to provide a return address
+        ADD     pc,r0,r9		; Do the jump
+13
+        B      ts_GetCommand
+
+
+
+; Called by vectoring through command table
+; Read operation count
+; Read target addresses
+; Read data
+; Send command byte or FF
+; Send checksum
+; For all operation count
+;   write data 
+;   if read-back option
+;     read data
+; Return by branching to GetCommand
+
+
+ts_bus_exercise	ROUT
+        ADDS    r7,r0,r4                ; save the control byte
+
+        ADDS    r14, r0, pc             ; setup return address for ..
+        B       ts_GetWord              ; .. get operation count word
+        ADDS    r8, r0, r4              ; r8 is operation count
+        ADDS    r12,r0,r4               ; initialise checksum 
+
+        ADDS    r14, r0, pc
+        B       ts_GetWord              ; r9 is first target address
+        ADDS    r9, r0, r4
+        ADDS    r12,r12,r4              ; accumulate checksum 
+        ADDS    r14, r0, pc
+        B       ts_GetWord              ; r10 is second target address
+        ADDS    r10, r0, r4
+        ADDS    r12,r12,r4              ; accumulate checksum 
+
+        ADDS    r14, r0, pc
+        B       ts_GetWord              ; r11 is first data word
+        ADDS    r11, r0, r4
+        ADDS    r12,r12,r4              ; accumulate checksum 
+        ADDS    r14, r0, pc
+        B       ts_GetWord              ; r13 is second data word
+        ADDS    r13, r0, r4
+        ADDS    r12,r12,r4              ; accumulate checksum 
+
+;
+; Fetch the checksum, and acknowledge
+;
+
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ;read transmitted checksum
+        ADDS    r4,r4,r12               ;tx + total should be zero
+        LDR     r5,%05
+        ADD     pc,pc,r0
+05
+        &       (0 - 1)
+        ADDS    r5,r5,r4                ;carry set on checksum failure
+        BCS     ts_cmd_error
+
+;
+; Checksum looks OK. Send the command and the checksum back.
+;
+	LDR	r4,%06
+	ADD     pc,pc,r0
+06
+	&	ts_BusExCmdByte
+        ADDS    r4,r4,r7                ;restore the original 
+        ADDS    r14,r0,pc
+        B       ts_SendByte
+        ADDS    r4,r0,r12               ;then send the calculated checksum
+        ADDS    r14,r0,pc
+        B       ts_SendWord
+
+	ADDS	r12,r0,r13		; Now addresses are in r9, r10
+					; and data in r11, r12.
+;
+; Convert the operation options into a code pointer
+;
+        ADDS    r13,r7,r7              ; convert operation code to vector 
+        ADDS    r13,r13,r13
+        LDR     r4, %02
+        ADD     pc,pc,r0
+02
+        &       (ts_busex_cmd_table - %03)
+        ADDS    r4,pc,r4
+        ADDS    r13,r4,r13
+03
+        LDR     r13,[r13]               ; fetch pointer to code
+	LDR	r7, %04			; set up decrementer in r8
+	ADD 	pc,pc,r0
+04
+	DCD	(0 - 1)
+07
+        ADD     pc,pc,r13               ; jump to operation
+	&	0
+
+;
+; In this table, the operation after any word fetch is vectored by
+; the 3 least significant bits of the command byte to perform some 
+; combination of writing with  : 
+;
+; bit 2 -> 0    S : Perform separate data write ops
+;          1    M : Use STM / LDM instructions 
+;
+; bit 1 -> 0    R : Perform only read operations
+;          1    W : Write before reading
+;
+; bit 0 -> 0    W : word operation
+;          1    B : byte operation
+;
+; Note that byte and multiple operations are mutually
+; exclusive.
+;
+
+        ASSERT  ((ts_busex_cmd_table - %07) = 8)
+
+ts_busex_cmd_table
+
+        DCD     (ts_B_SRW - ts_busex_cmd_table)
+        DCD     (ts_B_SRB - ts_busex_cmd_table)
+        DCD     (ts_B_SWW - ts_busex_cmd_table)
+        DCD     (ts_B_SWB - ts_busex_cmd_table)
+        DCD     (ts_B_MRW - ts_busex_cmd_table)
+        DCD     (ts_B_MRB - ts_busex_cmd_table)
+        DCD     (ts_B_MWW - ts_busex_cmd_table)
+        DCD     (ts_B_MWB - ts_busex_cmd_table)
+
+ts_B_SRW
+	LDR	r11,[r9]		; read-only separate words
+	LDR	r12,[r10]
+	ADDS	r8, r8, r7
+	BCS	ts_B_SRW
+	B	ts_B_done
+
+ts_B_SRB
+	LDRB	r11,[r9]		; read-only separate bytes
+	LDRB	r12,[r10]
+	ADDS	r8, r8, r7
+	BCS	ts_B_SRB
+	B	ts_B_done
+
+ts_B_SWW
+	STR	r11,[r9]		; write and read separate words
+	STR	r12,[r10]
+	LDR	r1,[r9]
+	LDR	r2,[r10]
+	ADDS	r8, r8, r7
+	BCS	ts_B_SWW
+	B	ts_B_done
+
+ts_B_SWB
+	STRB	r11,[r9]		; write and read separate bytes
+	STRB	r12,[r10]
+	LDRB	r1,[r9]
+	LDRB	r2,[r10]
+	ADDS	r8, r8, r7
+	BCS	ts_B_SWB
+	B	ts_B_done
+
+
+ts_B_MRW
+	LDMIA	r9,{r1,r2}		; read-only multiple words
+	LDMIA	r10,{r1,r2}
+	ADDS	r8, r8, r7
+	BCS	ts_B_MRW
+	B	ts_B_done
+
+ts_B_MWW
+	STMIA	r9,{r11,r12}		; write and read multiple words
+	LDMIA	r9,{r1,r2}
+	STMIA	r10,{r11,r12}
+	LDMIA	r10,{r1,r2}
+	ADDS	r8, r8, r7
+	BCS	ts_B_MWW
+	B	ts_B_done
+
+;
+; Orthogonally, these should be multiple byte operations - we can't do that, 
+; so they actually do a single/multiple mixture.
+; The first address argument is used for word-aligned operations and the
+; second for byte-aligned operations - so set only the second address
+; to a non-word-aligned address.
+
+ts_B_MRB
+	LDMIA	r9,{r1,r2}		; read-only multiple words
+	LDRB	r1,[r10]		; then single bytes
+	LDR 	r1,[r9]			; and single words
+	ADDS	r8, r8, r7
+	BCS	ts_B_MRB
+	B	ts_B_done
+
+ts_B_MWB
+	STMIA	r9,{r11,r12}		; store multiple words
+	STRB	r11,[r10]		; write byte
+	STR	r12,[r9]		; write words
+	LDMIA	r9,{r1,r2}
+	LDRB	r1,[r10]
+	LDR	r1,[r9]			; read single and multiple words
+	ADDS	r8, r8, r7
+	BCS	ts_B_MWB
+;	B	ts_B_done
+
+ts_B_done
+	B	ts_GetCommand
+
+
+
+;
+; All commands fall through here to respond with FF if the received
+; message block checksums fail.
+;
+
+ts_cmd_error    ROUT                    ; error in command
+        LDR     r4, %01                 ; return error response
+        ADD     pc,pc,r0
+01
+        DCD     ErrorCmd
+	ADDS	r0,r0,r0
+        ADDS    r14, r0, pc             ; send response byte to host
+        B       ts_SendByte
+
+        B       ts_GetCommand
+
+
+; generic coprocessor register names
+
+cpr0	CN	0
+cpr1	CN	1
+cpr2	CN	2
+cpr3	CN	3
+cpr4	CN	4
+cpr5	CN	5
+cpr6	CN	6
+cpr7	CN	7
+cpr8	CN	8
+cpr9	CN	9
+cpr10	CN	10
+cpr11	CN	11
+cpr12	CN	12
+cpr13	CN	13
+cpr14	CN	14
+cpr15	CN	15
+
+
+; Called by vectoring through command table.
+; Read transfer value
+; Read and check checksum
+; Extract copro register number
+; Index suitable MRC instruction
+; Perform copro write
+; Reply with command byte or FF
+; Reply with checksum
+
+ts_write_cpr15h	ROUT
+	ADDS	r4,r4,#8		; adjust opcode for high registers
+ts_write_cpr15l
+	ADDS	r7,r0,r4		; save opcode to r7
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ; get value for copro
+        ADDS    r9,r0,r4
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ; get checksum
+        ADDS    r4,r4,r9
+        LDR     r5,%01
+        ADD     pc,pc,r0
+01
+        &       (0 - 1)
+        ADDS    r4,r5,r4                ; compare total chex with zero
+        BCS     ts_cmd_error            ; carry set on error
+
+	ADDS	r13,r7,r7		; point into instruction table
+	ADDS	r13,r13,r13
+	ADDS	r13,r13,r13
+	ADD 	pc,pc,r13		; jump to pc + (r7 * 8)
+	&	0
+
+	SetCop	r9,cpr0			; transfer instructions
+	B	%02
+	SetCop	r9,cpr1
+	B	%02
+	SetCop	r9,cpr2
+	B	%02
+	SetCop	r9,cpr3
+	B	%02
+	SetCop	r9,cpr4
+	B	%02
+	SetCop	r9,cpr5
+	B	%02
+	SetCop	r9,cpr6
+	B	%02
+	SetCop	r9,cpr7
+	B	%02
+	SetCop	r9,cpr8
+	B	%02
+	SetCop	r9,cpr9
+	B	%02
+	SetCop	r9,cpr10
+	B	%02
+	SetCop	r9,cpr11
+	B	%02
+	SetCop	r9,cpr12
+	B	%02
+	SetCop	r9,cpr13
+	B	%02
+	SetCop	r9,cpr14
+	B	%02
+	SetCop	r9,cpr15
+
+02
+	LDR	r4,%03
+	ADD     pc,pc,r0
+03
+	&	ts_CPWCmdByte		; build command byte + option
+	ADDS	r4,r4,r7
+        ADDS    r14,r0,pc  	        ; echo command byte
+        B       ts_SendByte
+        ADDS    r4,r0,r9                ; return checksum 
+        ADDS    r14,r0,pc               ;
+        B       ts_SendWord
+
+	B	ts_GetCommand
+
+
+
+
+; Called by vectoring through command table.
+; Read and check checksum
+; Extract copro register number
+; Index suitable MCR instruction
+; Perform copro read
+; Reply with command byte or FF
+; Reply with checksum
+; Send transfer results
+; Send checksum
+
+ts_read_cpr15h	ROUT
+	ADDS	r4,r4,#8		; adjust opcode for high registers
+ts_read_cpr15l
+	ADDS	r7,r0,r4		; save opcode in r7
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ; get checksum to r4
+	ADDS	r9,r0,r4		; copy to r9
+        LDR     r5,%01
+        ADD     pc,pc,r0
+01
+        &       (0 - 1)
+        ADDS    r4,r5,r4                ; compare total chex with zero
+        BCS     ts_cmd_error            ; carry set on error
+
+	LDR	r4,%02
+	ADD     pc,pc,r0
+02
+	&	ts_CPRCmdByte		; build command byte + option
+	ADDS	r4,r4,r7
+        ADDS    r14,r0,pc  	        ; echo command byte
+        B       ts_SendByte
+        ADDS    r4,r0,r9                ; return checksum
+        ADDS    r14,r0,pc
+        B       ts_SendWord
+
+	ADDS	r13,r7,r7		; point into instruction table
+	ADDS	r13,r13,r13
+	ADDS	r13,r13,r13
+	ADD 	pc,pc,r13		; jump to pc + (r7 * 8)
+	&	0
+
+	ReadCop	r12,cpr0		; transfer instructions
+	B	%03
+	ReadCop	r12,cpr1
+	B	%03
+	ReadCop	r12,cpr2
+	B	%03
+	ReadCop	r12,cpr3
+	B	%03
+	ReadCop	r12,cpr4
+	B	%03
+	ReadCop	r12,cpr5
+	B	%03
+	ReadCop	r12,cpr6
+	B	%03
+	ReadCop	r12,cpr7
+	B	%03
+	ReadCop	r12,cpr8
+	B	%03
+	ReadCop	r12,cpr9
+	B	%03
+	ReadCop	r12,cpr10
+	B	%03
+	ReadCop	r12,cpr11
+	B	%03
+	ReadCop	r12,cpr12
+	B	%03
+	ReadCop	r12,cpr13
+	B	%03
+	ReadCop	r12,cpr14
+	B	%03
+	ReadCop	r12,cpr15
+
+03
+	ADDS	r4,r0,r12		; return result
+	ADDS	r14,r0,pc
+	B	ts_SendWord
+	SUBS	r4,r0,r12		; return checksum
+	ADDS	r14,r0,pc
+	B	ts_SendWord
+
+	B	ts_GetCommand
+
+
+        END
+
+
diff --git a/OldTestSrc/ExtIO b/OldTestSrc/ExtIO
new file mode 100644
index 0000000000000000000000000000000000000000..8ef956a201a997890c60749536a7896ac60b7cad
--- /dev/null
+++ b/OldTestSrc/ExtIO
@@ -0,0 +1,1089 @@
+; > TestSrc.ExtIO
+
+        TTL RISC OS 2+ POST external commands
+;
+; External interface for RISC OS ROM.
+; provides entry points to send byte- and word- and string-sized objects
+; and to receive byte- and word-sized objects   
+;
+; A minimal set of opcodes should be used (ideally, only B, LDR and ADDS)
+; so that a processor test may be validly included in the internal test
+; sequence.
+;
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name            Comment
+; ----          ----            -------
+; 06-Dec-89     ArtG            Initial version - split from `Begin`
+;                               Release 0.2 for integration
+; 31-Mar-90     ArtG            Added ts_MoreText, cursor position, hex.
+; 19-Apr-90     ArtG            Added bus exercise commands
+; 09-May-90     ArtG            Changed LCD strobe to 12 pulses
+; 15-May-90     ArtG            Added ReadyByte : improves synchronization
+;                               when ExtCmd execution toggles A21/A22.
+; 18-Jun-90	ArtG		Added CPR15 read/write functions
+;
+;
+;------------------------------------------------------------------------
+
+
+        SUBT    Test adapter interface
+
+;
+; The test adapter senses an access to the ROM with address line A21 high.
+; Current (2M addressing space) ROMs only use address lines A2 to A20,
+; so if A21 to A22 are asserted they will be ignored (the ROMS are aliased
+; into 8M of space). With no test adapter, the aliased ROM location will
+; be read and may be recognised. The test adapter may selectively disable
+; ROMs when A21 is high, causing arbitrary data to be read. This data
+; should be dependent on the previous ROM read operation, and will
+; therefore be predictably not equal to the data read when the ROMs are
+; aliased.
+; The assumption that A21 is unused may be invalidated by a later issue
+; of the PCB. A22 is therefore asserted at the same time : this will then
+; be used on a PCB where A22 is tracked to a test connector and 8Mbit ROMS
+; are used. Machines using larger ROMs than 8 Mbit (4M addressing space) 
+; will require explicit decoding or a new communication scheme.
+;
+
+
+;
+; This section determines whether the test interface adapter exists, and 
+; what variety is fitted (dumb, display or external)      
+; 3 read operations are performed (a WS operation): if all of these 
+; find a ROM alias then no adapter is fitted.
+;
+; If an adapter responds, then a RD operation is performed - 4 strobes then
+; clocking 8 bits into r4. These bits may be all zeros (a dumb adapter)
+; or all ones (a display adapter) or some other value (an external
+; adapter)
+;
+
+ts_GetCommand  ROUT
+
+        LDR     r0,%01
+        ADD     pc,pc,r0
+01
+        &       0
+
+        ; delay to make a gap before reading
+
+        LDR     r3,%02
+        ADD     pc,pc,r0
+02
+        &       ts_recover_time
+03
+        ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %03
+
+        ROUT
+
+;
+; Load up the registers for the test interface communication -
+;
+
+        LDR     r0,%01                  ; set zero in r0
+        ADD     pc,pc,r0  ;(generally useful constant - especially for skip)
+01
+        &       0
+        LDR     r1,%02                  ; set FFFFFFFF in r1
+        ADD     pc,pc,r0  ;(test value : sets carry when added to non-zero)
+02
+        &       (-1)
+        LDR     r2,%03                  ; set pointer to test address
+        ADD     pc,pc,r0  ;(points to aliased copy of a zero word)
+03
+        &       (ts_Alias_bits + (%01 - %04))
+        ADDS    r2,pc,r2                ; adjust r2 for ROM-relative address
+        ADDS    r4,r0,r0                ; clear output accumulator
+04                                      ; where pc is when added to r2
+
+        ; do an RD operation (four strobes) to ensure interface cleared
+
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+
+        ; write a byte (initially, &90) to indicate readiness
+
+        LDR     r4,%20
+        ADD     pc,pc,r0
+20
+        &       ts_ReadyByte_00
+        ADDS    r14,r0,pc
+        B       ts_SendByte
+
+        ; delay to make a gap between WRS and RD operations
+
+        LDR     r3,%05
+        ADD     pc,pc,r0
+05
+        &       ts_recover_time
+06
+        ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %06
+
+        LDR     r5,%07                  ; counter for first 5 bits
+        ADD     pc,pc,r0
+07
+        &       1 :SHL: (32 - 5)
+        LDR     r6,%08                  ; counter for last 3 bits
+        ADD     pc,pc,r0
+08
+        &       1 :SHL: (32 - 3)
+        ADDS    r4,r0,r0                ; input accumulator initialisation
+
+; put the test interface into input mode
+
+        LDR     r3,[r2]                 ; 3 bit lead-in
+        ADDS    r3,r3,r1                ; (adapter detects WS operation)
+        BCC     ts_User_startup         ; abort if no adapter present
+
+        LDR     r3,[r2]                 ; two more strobes, then waitloop
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+
+; started input operation : wait for interface to be ready
+
+09
+        LDR     r3,[r2]                 ; read start bit repeatedly
+        ADDS    r3,r3,r1                ; (adapter detects RD operation)
+        BCC     %09                     ; loop until interface is ready
+
+; read the first 5 bits into r5 and the second 3 bits into r4
+
+10      LDR     r3,[r2]                 ; read a bit of the byte
+        ADDS    r3,r3,r1                ; .. if the test adapter is present, carry bit set
+        ADCS    r4,r4,r4                ; .. shift left and add in carry
+
+        ADDS    r5,r5,r5                ; loop until 5 bits are read
+        BCC     %10
+
+        ADDS    r5,r4,r4                ; copy bits 7..3 to r5, bits 5..1 
+
+        ADDS    r4,r0,r0                ; and read the last 3 bits to r4
+11      LDR     r3,[r2]                 ; read a bit of the byte
+        ADDS    r3,r3,r1
+        ADCS    r4,r4,r4
+
+        ADDS    r6,r6,r6                ; loop until last 3 bits are read
+        BCC     %11
+
+;
+; Command byte read in (split between r4 and r5)
+; Pass the option bits (r4) to the function identified by r5.
+;
+
+        ADDS    r5,r5,r5                ; index * 2 -> index * 4 
+        LDR     r3,%12                  ; pc-relative ptr to command_table
+        ADD     pc,pc,r0
+12
+        &       ts_command_table - %13
+        ADDS    r3,pc,r3                ; absolute pointer to command table
+        ADDS    r3,r3,r5
+
+13      LDR     r3,[r3]                 ; get table entry 
+14      ADD     pc,pc,r3                ; (offset from command_table)
+
+        &       0                       ; necessary padding : pc must point
+                                        ; to command table when r3 is added.
+
+;
+; This is the table of offsets to all the built-in functions. 
+; The top 5 bits of the command are used to index, so there are
+; 32 possible entries, mostly illegal.
+; Decoding of the function modifier bits is performed by multiple
+; entries in this table.
+;
+
+; pc must point here when ADDS pc,r3,pc is executed
+
+        ASSERT  ((ts_command_table - %14)  = 8)
+
+ts_command_table
+
+        DCD     (ts_Dealer_startup - ts_command_table)   ; display interface
+ts_Windex
+        DCD     (ts_write_memory   - ts_command_table)   ; external tests
+ts_Rindex
+        DCD     (ts_read_memory    - ts_command_table)
+ts_Eindex
+        DCD     (ts_execute        - ts_command_table)
+ts_Bindex
+        DCD     (ts_bus_exercise   - ts_command_table)
+
+        DCD     (ts_GetCommand     - ts_command_table)	; dummy entry aligns CPR instructions
+							; to allow 4-bit option field
+ts_CWindex
+	DCD	(ts_write_cpr15l   - ts_command_table)
+	DCD	(ts_write_cpr15h   - ts_command_table)
+ts_CRindex
+	DCD	(ts_read_cpr15l    - ts_command_table)
+	DCD	(ts_read_cpr15h    - ts_command_table)
+
+        ; pad the table out to 31 entries 
+        ; (leave space for display vector)
+
+OldOpt  SETA    {OPT}
+        OPT     OptNoList
+doffset SETA    .
+        WHILE   doffset < (ts_command_table + (31 * 4)) ; illegal entries
+        DCD     (ts_GetCommand     - ts_command_table)
+doffset SETA    doffset + 4
+        WEND
+        OPT     OldOpt
+
+        DCD     (ts_Forced_startup - ts_command_table)  ; dumb interface
+
+;
+; The indexes into the above table are needed in ExtCmd ...
+;
+ts_WriteCmdByte         *       ((ts_Windex - ts_command_table) :SHL: 1)
+ts_ReadCmdByte          *       ((ts_Rindex - ts_command_table) :SHL: 1)
+ts_ExecuteCmdByte       *       ((ts_Eindex - ts_command_table) :SHL: 1)
+ts_BusExCmdByte         *       ((ts_Bindex - ts_command_table) :SHL: 1)
+ts_CPWCmdByte		*	((ts_CWindex  - ts_command_table) :SHL: 1)
+ts_CPRCmdByte		*	((ts_CRindex  - ts_command_table) :SHL: 1)
+
+
+;
+; Primitives for reading data from the external interface
+;
+;               - Get a byte from the interface (into r4)
+;               - Get a (4 byte) word from the interface (into r4)
+;
+; Required register setup is presumed done by a recent ts_GetCommand.
+; r0, r1 and r2 have critical values
+; r14 is the link address
+;
+
+ts_GetWord      ROUT
+
+        LDR     r6,%01                  ; counter for 4 bytes per word
+        ADD     pc,pc,r0                ; (bit set 4 left shifts from Carry)
+01
+        &       1 :SHL: (32 - 4)
+        B       ts_Getdata
+
+ts_GetByte      ROUT
+        LDR     r6,%01                  ; counter for single byte
+        ADD     pc,pc,r0
+01
+        &       1 :SHL: (32 - 1)
+
+ts_Getdata      ROUT
+        ADDS    r4,r0,r0                ; input accumulator initialisation
+
+        LDR     r3,[r2]                 ; 3 bit lead-in
+        ADDS    r3,r3,r1                ; (adapter detects RD operation)
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+
+; started input operation : now loop until r6 shifts into Carry
+
+02
+        LDR     r5,%03                  ; counter for 8 bits per byte
+        ADD     pc,pc,r0
+03
+        &       2_00000001000000010000000100000001
+04
+        LDR     r3,[r2]                 ; read start bit repeatedly
+        ADDS    r3,r3,r1
+        BCC     %04                     ; loop until interface is ready
+05
+        LDR     r3,[r2]                 ; read a bit of the byte
+        ADDS    r3,r3,r1
+        ADCS    r4,r4,r4                ; SHL r4, add carry bit.
+
+        ADDS    r5,r5,r5                ; loop until byte is read
+        BCC     %05
+
+        ADDS    r6,r6,r6                ; loop until word is read
+        BCC     %04
+
+        ADD     pc,r0,r14               ; back to the caller
+
+
+;
+; Primitives for sending data to the interface
+;
+;               - Send a byte to the interface (from r4 lsb)
+;               - Send a (4 byte) word to the interface (from r4)
+;
+; Required register setup is presumed done by a recent ts_GetCommand.
+; r0, r1 and r2 have critical values
+; r14 is the link address
+;
+
+ts_SendWord     ROUT
+        LDR     r6,%01                  ; counter for 4 bytes per word
+        ADD     pc,pc,r0                ; (bit set 4 left shifts from Carry)
+01
+        &       1 :SHL: (32 - 4)
+        B       ts_Putdata
+
+ts_SendByte      ROUT
+        LDR     r6,%01                  ; counter for single byte
+        ADD     pc,pc,r0
+01
+        &       (3 :SHL: 7)
+02      ADDS    r4,r4,r4                ;shift byte into highest 8 bits
+        ADDS    r6,r6,r6
+        BCC     %02                     ;stop when byte shifted,
+                                        ;leaving bit 31 set in r6
+
+ts_Putdata      ROUT
+
+; Wait - gap between successive WS attempts or successive bytes
+
+01      LDR     r3,%02
+        ADD     pc,pc,r0
+02
+        &       ts_recover_time
+03      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %03
+
+        LDR     r3,[r2]                 ; Test for adapter ready for data
+        ADDS    r3,r3,r1                ; (adapter detects WS operation)
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        BCC     %10                     ; skip out if adapter not present
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        BCC     %01                     ; loop back until adapter is ready
+
+; Adapter ready - loop around all the bits in the byte
+
+        LDR     r5,%04                  ; load bits-per-byte counter
+        ADD     pc,pc,r0
+04
+        &       (1 :SHL: (32-8))
+
+05      LDR     r3,%06                  ; delay before sending bit
+        ADD     pc,pc,r0
+06
+        &       ts_recover_time
+07      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %07
+
+        ; Send a single bit : 1 pulse for 1, 2 pulses for 0
+
+        LDR     r3,[r2]
+        ADDS    r4,r4,r4                ; shift current bit into Carry
+        LDRCC   r3,[r2]                 ; second pulse if bit is 0
+
+        ; repeat until 8 bits are sent
+
+        ADDS    r5,r5,r5
+        BCC     %05
+
+; Repeat for all the bytes to be sent (1 or 4)
+
+        ADDS    r6,r6,r6
+        BCC     %01
+
+; Go to TXRDY to ensure the host sees the transmit request
+
+        LDR     r3,%08                  ; delay before sending pattern
+        ADD     pc,pc,r0
+08
+        &       ts_recover_time
+09      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %09
+
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1                ; dummy - space between pulses
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+
+; All sent - r14 holds the caller's return address
+10
+        ADD     pc,r0,r14
+
+
+
+;
+; Reporting primitive
+;
+;               - Send the text (nul-terminated, at r4) to the display
+;
+; Interface registers need to be set up : this function is called from test 
+; code rather than external interface code.
+;
+; The display is assumed to be a standard 16 character LCD module using 
+; the Hitachi HD44780 display controller.
+; The 16-digit module uses a single 44780. This is an abnormal use of the
+; controller, and requires it to be set to two-line mode, with the first
+; 8 displayed characters on the first 'line', and the second 8 on the
+; second 'line'. Characters sent to the second line must be written at
+; character position 40 +. In order to permit different modules to be
+; fitted to later adapters, it is suggested that the first 7 characters 
+; be treated as a 'title' line, and the second 8 as a 'comment' line.
+; A space should always be placed at the end of the title line to
+; split the display fields, unless there is no 'comment' line.
+; Do not display characters across the two areas as though they adjoined
+; (even though they do :-) ).
+;
+; The controller is operated in its 4-bit mode, which allows the interface
+; to drive 4 bits of alpha information and 4 bits of control information.
+; The bits in a transmitted byte are assigned as :
+;
+;       bit 0   -       D4  }   4-bit mode data bus
+;           1   -       D5  }  
+;           2   -       D6  }
+;           3   -       D7  }
+;
+;           4   -       RS      Register Select : 0 for control, 1 for data
+;
+;           5   -           }   Unassigned
+;           6   -           }
+;
+;           7   -       CPEN    Interface control :     0 for enable, 
+;                                                       1 for disable
+;
+; For each message sent, the display is first initialised, using the 
+; following sequence (each byte is sent as 2 bytes, high nibble first, 
+; with RS clear in bit 4 of each byte)
+; After each byte, an RD operation is performed : this is used by the
+; interface hardware to strobe the data into the display. 
+;
+;
+; The message addressed by r4 is then sent (data mode : RS set in each byte)
+; until a 0 byte is encountered.
+;
+
+;
+; This is the command sequence sent to initialise the display
+;
+
+ts_initialise
+        =       &30,&30,&30,&20 ; power-up initialisation
+        =       &20,&80         ; 4 bit mode, two line, Font 0
+        =       &00,&C0         ; Display on, no cursor visible
+        =       &00,&60         ; Incrementing display position, no shift
+        =       &80,&00         ; Set DD RAM address 0
+        =       &00,&20         ; Cursor home
+        =       &00,&10         ; Display clear
+ts_initialise_end
+
+        ASSERT  ((ts_initialise_end - ts_initialise) / 2) < 32
+
+
+;
+; This is the command sequence sent when continuation text is sent
+;
+
+ts_extend
+        =       &00,&C0         ; Display on, cursor invisible
+        =       &00,&60         ; Incrementing display position, no shift
+ts_extend_end
+
+        ASSERT  ((ts_extend_end - ts_extend) / 2) < 32
+
+;
+; One of these commands are sent when offset text is required
+;
+
+ts_offset_table
+        =       &80,&00         ; Set DD RAM address 0
+ts_offset_table_1
+        =       &80,&10         ; Set DD RAM address 1
+        =       &80,&20         ; Set DD RAM address 2
+        =       &80,&30         ; Set DD RAM address 3
+        =       &80,&40         ; Set DD RAM address 4
+        =       &80,&50         ; Set DD RAM address 5
+        =       &80,&60         ; Set DD RAM address 6
+        =       &80,&70         ; Set DD RAM address 7
+        =       &C0,&00         ; Set DD RAM address 40
+        =       &C0,&10         ; Set DD RAM address 41
+        =       &C0,&20         ; Set DD RAM address 42
+        =       &C0,&30         ; Set DD RAM address 43
+        =       &C0,&40         ; Set DD RAM address 44
+        =       &C0,&50         ; Set DD RAM address 45
+        =       &C0,&60         ; Set DD RAM address 46
+        =       &C0,&70         ; Set DD RAM address 47
+
+
+; This assertion is forced by the code : each sequence assumed 2 bytes.
+
+        ASSERT  ((ts_offset_table_1 - ts_offset_table) = 2)
+
+
+
+        ALIGN
+
+;
+; Here starts the code ...
+;
+
+ts_SendQuit     ROUT                    ; put this code BEFORE %16
+        ADD     pc,r0,r14               ;
+
+
+
+;
+; Entry point for initialising the display and sending r4 text.
+;
+
+
+ts_SendText     ROUT
+
+;
+; Point to the command sequence to setup and clear the display
+;
+
+        LDR     r0,%10                  ; set zero in r0
+        ADD     pc,pc,r0
+10
+        &       0
+        LDR     r7,%11                  ; pointer to init sequence
+	ADDS	r7,pc,r7
+        ADD     pc,pc,r0
+11
+        &       (ts_initialise - .)
+        LDR     r6,%12                  ; length of init sequence
+        ADD     pc,pc,r0
+12
+        &       (1 :SHL: (32 - (ts_initialise_end - ts_initialise)))
+        B       ts_SendLCDCmd
+
+
+;
+; Entry point for adding text to current cursor position
+;
+
+ts_MoreText     ROUT
+
+        LDR     r0,%10                  ; set zero in r0
+        ADD     pc,pc,r0
+10
+        &       0
+        LDR     r7,%11                  ; pointer to command sequence
+	ADDS	r7,pc,r7
+        ADD     pc,pc,r0
+11
+        &       (ts_extend - .)
+        LDR     r6,%12                  ; length of command sequence
+        ADD     pc,pc,r0
+12
+        &       (1 :SHL: (32 - (ts_extend_end - ts_extend)))
+        B       ts_SendLCDCmd
+
+
+ts_PosText      ROUT
+
+;
+; Entry point for adding text at a specific cursor position
+; Used iteratively by SendText, etc if cursor position command found.
+; Offset into display is given in r6.
+;
+
+        LDR     r0,%10                  ; set zero in r0
+        ADD     pc,pc,r0
+10
+        &       0
+        LDR     r7,%11                  ; pointer to command sequence
+	ADDS	r7,pc,r7
+        ADD     pc,pc,r0
+11
+        &       (ts_offset_table - .)   ; offset * 2 into table of
+        ADDS    r6,r6,r6                ; offset command sequences
+        ADDS    r7,r7,r6
+
+        LDR     r6,%12                  ; length of command sequence
+        ADD     pc,pc,r0
+12
+        &       (1 :SHL: (32 - 2))
+
+
+;
+; Entry point for writing arbitrary command strings.
+; Set r7 to point to command string, r6 length (as tables above),
+; Set r4 to point to following Data string (null-terminated).
+;
+
+ts_SendLCDCmd
+
+        LDR     r0,%01                  ; set zero in r0
+        ADD     pc,pc,r0
+01
+        &       0
+        LDR     r1,%02                  ; set FFFFFFFF in r1
+        ADD     pc,pc,r0  ;(test value : sets carry when added to non-zero)
+02
+        &       (-1)
+        LDR     r2,%03                  ; set pointer to test address
+        ADD     pc,pc,r0  ;(points to aliased copy of a zero word)
+03
+        &       (ts_Alias_bits + (%01 - %04))
+        ADDS    r2,pc,r2                ; adjust r2 for ROM-relative address
+        ADDS    r0,r0,r0                ; dummy (to keep labels nearby !)
+04                                      ; where pc points when added to r2
+
+
+; Wait - gap between successive WS attempts or successive bytes
+
+ts_send_command_byte ROUT
+
+        LDR     r3,%14
+        ADD     pc,pc,r0
+14
+        &       ts_recover_time
+15      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %15
+        LDR     r1,%16                  ; reload test register
+        ADD     pc,pc,r0
+16
+        &       (-1)
+
+        LDR     r3,[r2]                 ; Test for adapter ready for data
+        ADDS    r3,r3,r1                ; (adapter detects WS operation)
+        BCC     ts_SendQuit             ; skip output : adapter not present
+                                        ; (backward jump helps ensure LDR r3,[r2]
+                                        ; only reads zero when adapter absent
+        LDR     r3,[r2]			; since previous bus data is nonzero)
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        BCC     ts_send_command_byte    ; loop back until adapter is ready
+
+; Adapter ready - loop around all the bits in the byte
+
+
+        LDR     r5,%21                  ; load byte-shift counter ...
+        ADD     pc,pc,r0                ; ... and bits-per-byte counter
+21
+        &       (1 :SHL: 8) + 1         ; 24 shifts + 8 shifts
+        LDRB    r1,[r7]
+22      ADDS    r1,r1,r1                ; shift byte up into m.s.d.
+        ADDS    r5,r5,r5
+        BCC     %22
+
+        ; Send a single bit : 1 pulse for 1, 2 pulses for 0
+
+23      LDR     r3,[r2]
+        ADDS    r1,r1,r1                ; shift current bit into Carry
+        LDRCC   r3,[r2]                 ; second pulse if bit is 0
+
+        ; and wait for the inter-bit time
+
+        LDR     r3,%24
+        ADD     pc,pc,r0
+24
+        &       ts_recover_time
+25      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %25
+
+        ; repeat until 8 bits are sent
+
+        ADDS    r5,r5,r5
+        BCC     %23
+
+        ; do a RD operation to strobe the data out
+
+        LDR     r5,%26
+        ADD     pc,pc,r0
+26
+        &       (1 :SHL: (32 - 12))
+27
+        LDR     r3,[r2]
+        ADDS    r5,r5,r5
+        BCC     %27
+
+; Repeat for all the bytes to be sent (ts_initialise_end - ts_initialise)
+
+        LDR     r3,%33
+        ADD     pc,pc,r0
+33
+        &       1
+        ADDS    r7,r7,r3                ; bump the pointer
+        ADDS    r6,r6,r6                ; bump the counter (shift left)
+        BCC     ts_send_command_byte
+
+
+;
+; Then send all the display bytes (in 4-bit mode) until a nul-terminator
+; is reached.
+;
+
+;
+; Send a single character (as two separate 4-bit fields)
+; First, look to see if it's one of :
+;
+;       NUL                     - end of text string
+;       0x80 - 0xfe             - cursor positioning
+;       0xff                    - introduce a hex digit
+;
+
+ts_send_text_byte       ROUT
+
+        LDR     r1,%40                  ; reload test register
+        ADD     pc,pc,r0
+40
+        &       (-1)
+
+        LDRB    r7,[r4]
+        ADDS    r3,r7,r1                ; test for nul terminator
+        BCC     ts_SendEnd
+
+;
+; Byte isn't null. Check for >= 0x80.
+;
+
+        LDR     r6,%42                  ; test for cursor control
+        ADD     pc,pc,r0
+42
+        &       (-&80)                  ; &8x means column x.
+        ADDS    r6,r7,r6
+        BCC     ts_printable_char       ; < &80 : write a character
+
+;
+; Carry set : r6 now holds (value - 0x80). Check for numeric escape (&ff).
+;
+        LDR     r3,%43
+        ADD     pc,pc,r0
+43
+        &       (-&7f)
+        ADDS    r3,r6,r3
+        BCC     %47
+
+;
+; Carry set : fetch a nybble from the top of r8 and display that.
+;
+
+        ADDS    r8,r8,r8
+        ADCS    r6,r0,r0
+        ADDS    r8,r8,r8
+        ADCS    r6,r6,r6
+        ADDS    r8,r8,r8
+        ADCS    r6,r6,r6
+        ADDS    r8,r8,r8
+        ADCS    r6,r6,r6
+
+        LDRB    r7,[pc,r6]
+        B       ts_printable_char
+45
+        =       "0123456789ABCDEF"
+
+;
+; Not &ff : r6 holds cursor positioning offset (< &80). Skip over
+; the cursor control byte and iterate thro' PosText to move
+; typing position.
+;
+
+47
+        LDR     r3, %48
+        ADD     pc,pc,r0
+48
+        &       1
+        ADDS    r4,r3,r4
+        B       ts_PosText
+
+;
+; Character is normal text : write it to the LCD.
+; The shift loop is used to generate the inter-byte delay normally 
+; provided by  ts_recover_time. Always make sure this is long enough.
+;
+
+ts_printable_char
+
+        ADDS    r6,r0,r7                ; take a copy of character
+        LDR     r5,%51                  ; load byte-shift counter ...
+        ADD     pc,pc,r0                ; ... and bits-per-byte counter
+51                                      ; as a bitmask of the shift pattern
+        &       (1:SHL:8)+(1:SHL:4)+1   ; 24 shifts + 4 shifts + 4 shifts
+52      ADDS    r6,r6,r6                ; shift byte up into m.s.d.
+        ADDS    r0,r0,r0                ; slow this loop down - ensure it's
+        ADDS    r0,r0,r0                ; always slower than ts_recover_time
+        ADDS    r5,r5,r5
+        BCC     %52
+
+        LDR     r3,[r2]                 ; Test for adapter ready for data
+        ADDS    r3,r3,r1                ; (adapter detects WS operation)
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        BCC     ts_printable_char       ; loop back until adapter is ready
+
+; Adapter ready - loop around all the bits in the byte
+
+ts_send_tbit_upper
+
+        ; wait for the inter-bit time
+
+        LDR     r3,%55
+        ADD     pc,pc,r0
+55
+        &       ts_recover_time
+56      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %56
+
+        ; Send a single bit : 1 pulse for 1, 2 pulses for 0
+
+        LDR     r3,[r2]
+        ADDS    r6,r6,r6                ; shift current bit into Carry
+        LDRCC   r3,[r2]                 ; second pulse if bit is 0
+
+        ; repeat until upper 4 bits are sent
+
+        ADDS    r5,r5,r5
+        BCC     ts_send_tbit_upper
+
+        ; then send the interface control bits
+
+        LDR     r1,%57
+        ADD     pc,pc,r0
+57
+        &       (8 :SHL: 28)            ; assert RS control pin
+
+ts_send_cbit_upper
+
+        ; wait for the inter-bit time
+
+        LDR     r3,%58
+        ADD     pc,pc,r0
+58
+        &       ts_recover_time
+59      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %59
+
+        ; Send a single bit : 1 pulse for 1, 2 pulses for 0
+
+        LDR     r3,[r2]
+        ADDS    r1,r1,r1                ; shift current bit into Carry
+        LDRCC   r3,[r2]                 ; second pulse if bit is 0
+        ADDS    r5,r5,r5
+        BCC     ts_send_cbit_upper
+
+;
+; do a RD operation to strobe the data out
+;
+
+        LDR     r3,%61
+        ADD     pc,pc,r0
+61
+        &       ts_recover_time
+62      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %62
+
+        LDR     r5,%63
+        ADD     pc,pc,r0
+63
+        &       (1 :SHL: (32 - 12))
+64
+        LDR     r3,[r2]
+        ADDS    r5,r5,r5
+        BCC     %64
+
+        ; prepare to send the lower 4 bits out
+
+        LDR     r5,%70                  ; bitcount mask for 4 data bits
+        ADD     pc,pc,r0                ; and 4 interface control bits
+70
+        &       (((1 :SHL: 4) + 1) :SHL: 24)   
+
+ts_send_text_lower
+        LDR     r3,%71
+        ADD     pc,pc,r0
+71
+        &       ts_recover_time
+72      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %72
+
+        LDR     r1,%73
+        ADD     pc,pc,r0
+73
+        &       (-1)
+
+        LDR     r3,[r2]                 ; Test for adapter ready for data
+        ADDS    r3,r3,r1                ; (adapter detects WS operation)
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        BCC     ts_send_text_lower      ; loop back until adapter is ready
+
+ts_send_tbit_lower
+
+        ; wait for the inter-bit time
+
+        LDR     r3,%76
+        ADD     pc,pc,r0
+76
+        &       ts_recover_time
+77      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %77
+
+        ; Send a single bit : 1 pulse for 1, 2 pulses for 0
+
+        LDR     r3,[r2]
+        ADDS    r6,r6,r6                ; shift current bit into Carry
+        LDRCC   r3,[r2]                 ; second pulse if bit is 0
+
+        ; repeat until lower 4 bits are sent
+
+        ADDS    r5,r5,r5
+        BCC     ts_send_tbit_lower
+
+
+        ; then send the interface control bits
+
+        LDR     r1,%78
+        ADD     pc,pc,r0
+78
+        &       (8 :SHL: 28)            ; assert RS control pin
+
+ts_send_cbit_lower
+
+        ; wait for the inter-bit time
+
+        LDR     r3,%80
+        ADD     pc,pc,r0
+80
+        &       ts_recover_time
+81      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %81
+
+        ; Send a single bit : 1 pulse for 1, 2 pulses for 0
+
+        LDR     r3,[r2]
+        ADDS    r1,r1,r1                ; shift current bit into Carry
+        LDRCC   r3,[r2]                 ; second pulse if bit is 0
+
+        ADDS    r5,r5,r5
+        BCC     ts_send_cbit_lower
+
+;
+; do a RD operation to strobe the data out
+;
+
+        ; wait for the inter-bit time
+
+        LDR     r3,%82
+        ADD     pc,pc,r0
+82
+        &       ts_recover_time
+83      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %83
+
+        LDR     r5,%84
+        ADD     pc,pc,r0
+84
+        &       1 :SHL: (32 - 12)
+85
+        LDR     r3,[r2]
+        ADDS    r5,r5,r5
+        BCC     %85
+
+; Repeat for all the bytes to be sent (until nul terminator is found)
+
+        LDR     r3,%86
+        ADD     pc,pc,r0
+86
+        &       1
+        ADDS    r4,r3,r4                ; bump text pointer
+        B       ts_send_text_byte
+
+;
+; Wait for about 1 seconds worth of LCD operation delays to
+; permit the operator to read the text. 
+; Use of the interface's monitor allows this delay to be increased 
+; or decreased externally.
+;
+
+ts_SendEnd      ROUT
+
+        LDR     r7, %01
+        ADD     pc,pc,r0
+01
+        &       (ts_pause_time + 1)     ; must be an odd number
+					; to ensure pairs of zeros
+        ASSERT ((ts_pause_time :AND: 1) = 0)
+
+02
+        LDR     r3,%03
+        ADD     pc,pc,r0
+03
+        &       ts_recover_time
+04      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %04
+        LDR     r1,%05                  ; reload test register
+        ADD     pc,pc,r0
+05
+        &       (-1)
+
+        LDR     r3,[r2]                 ; Test for adapter ready for data
+        ADDS    r3,r3,r1                ; (adapter detects WS operation)
+        BCC     ts_SendQuit             ; skip output : adapter not present
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        BCC     %02                     ; loop back until adapter is ready
+
+; Adapter ready - loop around all the bits in the byte
+; Note that each byte is actually 4 bits to the LCD module,
+; so a even number must be sent or the display will get out
+; of sync until the next display reset sequence.
+
+        LDR     r5,%10                  ; bits-per-byte counter
+        ADD     pc,pc,r0
+10
+        &       (1 :SHL: 24)
+        LDR     r3,%11
+        ADD     pc,pc,r0 
+11
+        &       ts_recover_time         ; wait before sending data bits
+12      ADDS    r3,r3,r3                ; for byte timing.
+        BCC     %12
+
+        ; Send a single bit : always 2 pulses for 0
+
+13      LDR     r3,[r2]
+        LDR     r3,[r2]
+
+        ; and wait for the inter-bit time
+
+        LDR     r3,%14
+        ADD     pc,pc,r0
+14
+        &       ts_recover_time
+15      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %15
+
+        ; repeat until 8 bits are sent
+
+        ADDS    r5,r5,r5
+        BCC     %13
+
+        ; do a RD operation to strobe the data out
+
+        LDR     r5,%16
+        ADD     pc,pc,r0
+16
+        &       1 :SHL: (32 - 12)
+17
+        LDR     r3,[r2]
+        ADDS    r5,r5,r5
+        BCC     %17
+
+        ; repeat until a sufficient number of nuls are done
+
+        ADDS    r7,r7,r1                ; count down loop counter
+        BCS     %02
+
+        ADD    pc,r0,r14                ; back to caller
+
+
+        END
diff --git a/OldTestSrc/Ioc b/OldTestSrc/Ioc
new file mode 100644
index 0000000000000000000000000000000000000000..5ec8b152fdf554b2189b3c4302c3bfcde1514918
--- /dev/null
+++ b/OldTestSrc/Ioc
@@ -0,0 +1,92 @@
+; > TestSrc.IOC
+
+        TTL RISC OS 2+ POST IO controller
+;
+; This initial IOC test simply reports the content of the IRQ and FIRQ
+; registers, to show any unexpected pending IRQs. 
+; Certain of these should really be cleared, and the effect of an
+; interrupt tested.
+;
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name            Comment
+; ----          ----            -------
+; 18-Dec-89     ArtG            Initial version
+; 29-Nov-91     ArtG            Added IOC bus test using mask registers
+; 20-Jun-93     ArtG            Modified for 29-bit IOMD test
+;
+;
+;------------------------------------------------------------------------
+
+        [ IO_Type = "IOMD"
+ts_IObase       *       IOMD_Base
+ts_IOmask       *       &1fffffff
+ts_IOreg1       *       IOMD_VIDCUR
+ts_IOreg2       *       IOMD_VIDSTART
+ts_IObswap      *       32
+ts_IOMD_ID      *       &D4E7
+        |
+ts_IObase       *       IOC
+ts_IOmask       *       &ff0000
+ts_IOreg1       *       IOCIRQMSKA
+ts_IOreg2       *       IOCIRQMSKB
+ts_IObswap      *       16
+        ]
+
+ts_IOCreg
+        MOV     r0,#0                   ; zero error accumulator
+        LDR     r3, =ts_IObase
+        MOV     r1,#(1 :SHL: 31)          ; initialise bit-set test mask
+0
+        MVN     r2,r1                   ; make bit-clear test mask
+        ANDS    r4,r1,#ts_IOmask
+        BEQ     %FT1                    ; skip if this bit isn't tested
+        STR     r1,[r3,#ts_IOreg1]
+        STR     r2,[r3,#ts_IOreg2]
+        LDR     r4,[r3,#ts_IOreg1]
+;        EOR     r4, r4, r1, LSR #ts_IObswap     ; check bit-set test was OK
+        EOR     r4, r4, r1           ; check bit-set test was OK
+        ORR     r0, r0, r4              ; accumulate errors in r0
+        LDR     r4,[r3,#ts_IOreg2]
+;        EOR     r4, r4, r2, LSR #ts_IObswap     ; check bit-clear test was OK
+        EOR     r4, r4, r2           ; check bit-clear test was OK
+        ORR     r0, r0, r4              ; accumulate errors in r0
+1
+        MOV     r1, r1, LSR #1          ; shift mask downwards
+        TEQ     r1,#0
+        BNE     %BT0                    ; and loop until all bits tested
+
+        ANDS    r8, r0, #ts_IOmask
+        MOV     pc,r14                   ; return error if any bit failed
+
+ts_IOCstat
+        LDR     r3, =ts_IObase
+        MOV     r0,#0
+        [ IO_Type = "IOMD"
+        LDRB    r1,[r3,#IOMD_ID1]
+        ORR     r0,r0,r1, LSL #(32-24)
+        LDRB    r1,[r3,#IOMD_ID0]
+        ORR     r0,r0,r1
+        LDR     r1,=ts_IOMD_ID
+        CMPS    r0,r1                   ; check IOMD identity
+        MOV     r0,r0,LSL #16
+        LDRB    r1,[r3,#IOMD_VERSION]
+        ORR     r8,r0,r1, LSL #12
+        MOV     pc,r14
+        |
+        LDRB    r1,[r3,#IOCControl]
+        ORR     r0,r0,r1, LSL #(32 - 8)
+        LDRB    r1,[r3,#IOCIRQSTAA]
+        ORR     r0,r0,r1, LSL #(32 - 16)
+        LDRB    r1,[r3,#IOCIRQSTAB]
+        ORR     r0,r0,r1, LSL #(32 - 24)
+        LDRB    r1,[r3,#IOCFIQSTA]
+        ORR     r8,r0,r1
+        ANDS    r1,r1,#0                ; return zero flag (OK)
+
+        MOV     pc,r14
+        ]
+
+        END 
+ 
diff --git a/OldTestSrc/MEMC1 b/OldTestSrc/MEMC1
new file mode 100644
index 0000000000000000000000000000000000000000..847df36843534aee6f4bee0f4244300f5e4bff17
--- /dev/null
+++ b/OldTestSrc/MEMC1
@@ -0,0 +1,552 @@
+; > MEMC1
+
+; MEMC interface file - MEMC1 version
+
+; Created by TMD 10-Aug-90
+
+VInit   * &03600000
+VStart  * &03620000
+VEnd    * &03640000
+CInit   * &03660000
+; SStart  * &03680000
+; SEnd    * &036A0000
+; SPtr    * &036C0000
+
+; *****************************************************************************
+;
+;       SetDAG - Program DMA address generator R1 with physical address R0
+;
+; in:   r0 = physical address
+;       r1 = index of DMA address generator to program, as defined in vdudecl
+;
+; out:  All registers preserved, operation ignored if illegal
+;
+
+        [ {FALSE}
+SetDAG  ENTRY   "r0"
+        CMP     r1, #MEMCDAG_MaxReason
+        EXIT    HI
+        ADR     r14, DAGAddressTable
+        LDR     r14, [r14, r1, LSL #2]          ; load base address in MEMC1
+        MOV     r0, r0, LSR #4                  ; bottom 4 bits irrelevant
+        CMP     r0, #(1 :SHL: 15)               ; ensure in range
+        ORRCC   r14, r14, r0, LSL #2
+        STRCC   r14, [r14]                      ; any old data will do
+        EXIT
+
+        GBLA    DAGIndex
+DAGIndex SETA   0
+
+        MACRO
+        DAGTab  $reason, $address
+        ASSERT  ($reason)=DAGIndex
+        &       $address
+DAGIndex SETA   DAGIndex + 1
+        MEND
+
+DAGAddressTable
+        DAGTab  MEMCDAG_VInit, VInit
+        DAGTab  MEMCDAG_VStart, VStart
+        DAGTab  MEMCDAG_VEnd, VEnd
+        DAGTab  MEMCDAG_CInit, CInit
+        ]
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; CAM manipulation utility routines
+
+BangCamUpdate ROUT
+
+; R2 = CAM entry no
+; R3 = logaddr
+; R9 = current MEMC value
+; R11 = PPL
+; set and update tables
+
+        MOV     R4, #0
+        LDR     R4, [R4, #CamEntriesPointer]
+        ORR     r0, r3, r11, LSL #28  ; top nibble is PPL
+        STR     r0, [R4, R2, LSL #2]
+
+BangCam
+
+; r0 corrupted
+; r1 corrupted
+; R2 = CAM entry no
+; R3 = logaddr
+; r4 corrupted
+; r5 spare!
+; r6 corrupted
+; r7, r8 spare
+; R9 = current MEMC value
+; r10 spare
+; R11 = PPL
+; r12 spare
+
+        AND     R4, R9, #&C           ; pagesize
+        ADR     R0, PageMangleTable
+        LDR     R0, [R0, R4]          ; load data table pointer
+        MOV     R4, #0
+01      LDR     R1, [R0], #4
+        CMP     R1, #-1
+        BEQ     %FT02
+        AND     R6, R2, R1
+        LDR     R1, [R0], #4
+        CMP     R1, #0
+        RSBMI   R1, R1, #0
+        ORRPL   R4, R4, R6, LSL R1
+        ORRMI   R4, R4, R6, LSR R1
+        B       %BT01
+
+02      LDR     R1, [R0], #4
+        CMP     R1, #-1
+        BEQ     %FT03
+        AND     R6, R3, R1
+        LDR     R1, [R0], #4
+        CMP     R1, #0
+        RSBMI   R1, R1, #0
+        ORRPL   R4, R4, R6, LSL R1
+        ORRMI   R4, R4, R6, LSR R1
+        B       %BT02
+
+03      ORR     R4, R4, #CAM
+        ORR     R4, R4, R11, LSL #8     ; stuff in PPL
+        STR     R4, [R4]                ; and write it
+        MOV     PC, LR
+
+; Data to drive CAM setting
+
+PageMangleTable
+        &       PageMangle4K
+        &       PageMangle8K
+        &       PageMangle16K
+        &       PageMangle32K
+
+; For each page size, pairs of masks and shift factors to put the bits in the
+; right place. Two sets: operations on Physical Page Number, operations on
+; Logical Page Number.
+
+; Shifts are Shift Left values (<<). Each section terminated by -1
+
+PageMangle4K
+; PPN:
+        &       2_011111111
+        &       0                       ; bits in right place
+        &       -1
+; LPN:
+        &       2_1100000000000:SHL:12
+        &       (11-12)-12              ; LPN[12:11] -> A[11:10]
+        &       2_0011111111111:SHL:12
+        &       (22-10)-12              ; LPN[10:0 ] -> A[22:12]
+        &      -1
+
+PageMangle8K
+; PPN:
+        &       2_010000000
+        &       7-7                     ; PPN[7]   -> A[7]
+        &       2_001000000
+        &       0-6                     ; PPN[6]   -> A[0]
+        &       2_000111111
+        &       6-5                     ; PPN[5:0] -> A[6:1]
+        &       -1
+; LPN:
+        &       2_110000000000:SHL:13
+        &       (11-11)-13              ; LPN[11:10] -> A[11:10]
+        &       2_001111111111:SHL:13
+        &       (22-9)-13               ; LPN[9:0]   -> A[22:13]
+        &       -1
+
+PageMangle16K
+; PPN:
+        &       2_010000000
+        &       7-7                     ; PPN[7]   -> A[7]
+        &       2_001100000
+        &       1-6                     ; PPN[6:5] -> A[1:0]
+        &       2_000011111
+        &       6-4                     ; PPN[4:0] -> A[6:2]
+        &       -1
+; LPN:
+        &       2_11000000000:SHL:14
+        &       (11-10)-14              ; LPN[10:9] -> A[11:10]
+        &       2_00111111111:SHL:14
+        &       (22-8)-14               ; LPN[8:0]  -> A[22:14]
+        &       -1
+
+PageMangle32K
+; PPN:
+        &       2_100000000
+        &       12-8                    ; PPN[8] -> A[12]
+        &       2_010000000
+        &       7-7                     ; PPN[7] -> A[7]
+        &       2_001000000
+        &       1-6                     ; PPN[6] -> A[1]
+        &       2_000100000
+        &       2-5                     ; PPN[5] -> A[2]
+        &       2_000010000
+        &       0-4                     ; PPN[4] -> A[0]
+        &       2_000001111
+        &       6-3                     ; PPN[3:0] -> A[6:3]
+        &       -1
+; LPN:
+        &       2_1100000000:SHL:15
+        &       (11-9)-15               ; LPN[9:8] -> A[11:10]
+        &       2_0011111111:SHL:15
+        &       (22-7)-15               ; LPN[7:0] -> A[22:15]
+        &       -1
+
+PageSizes
+        &       4*1024                  ; 0 is 4K
+        &       8*1024                  ; 4 is 8K
+        &       16*1024                 ; 8 is 16
+        &       32*1024                 ; C is 32
+
+PageShifts
+        =       12, 13, 0, 14           ; 1 2 3 4
+        =       0,  0,  0, 15           ; 5 6 7 8
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_UpdateMEMC: Read/write MEMC1 control register
+
+SSETMEMC ROUT
+
+        AND     r10, r0, r1
+        MOV     r12, #0
+        TEQP    pc, #SVC_mode+I_bit+F_bit
+        LDR     r0, [r12, #MEMC_CR_SoftCopy] ; return old value
+        BIC     r11, r0, r1
+        ORR     r11, r11, R10
+        BIC     r11, r11, #&FF000000
+        BIC     r11, r11, #&00F00000
+        ORR     r11, r11, #MEMCADR
+        STR     r11, [r12, #MEMC_CR_SoftCopy]
+        STR     r11, [r11]
+        TEQP    pc, #SVC_mode+I_bit
+        ExitSWIHandler
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       ClearPhysRAM - Routine to clear "all" memory
+;
+; While this routine is running, keyboard IRQs may happen. For this reason
+; it avoids LogRAM 0..31 (where hardware IRQ vector is) and PhysRAM
+; 0..31 where the IRQ workspace is.
+;
+
+ClearPhysRAM ROUT
+        MOV     R0, #0
+        MOV     R1, #0
+        MOV     R2, #0
+        MOV     R3, #0
+        MOV     R4, #0
+        MOV     R5, #0
+        MOV     R6, #0
+        MOV     R11, #0
+        MOV     R8, #PhysRam
+        CMP     R13, #512*1024
+        ADDEQ   R10, R8, #(512-64)*1024 ; get address that's logram 0
+        ADDNE   R10, R8, #512*1024
+        ADD     R13, R13, #PhysRam      ; end of memory
+        ADD     R8, R8, #4*8            ; skip minimal startup workspace
+10      CMP     R8, R10
+        ADDEQ   R8, R8, #4*8            ; skip physram that's logram 0
+        STMNEIA R8!, {R0-R6, r11}
+        CMP     R8, R13
+        BNE     %BT10
+        SUB     R13, R13, #PhysRam
+
+        LDR     R0, =OsbyteVars + :INDEX: LastBREAK
+        MOV     R1, #&80
+        STRB    R1, [R0]                ; flag the fact that RAM cleared
+        MOV     pc, lr
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       InitMEMC - Initialise memory controller
+;
+
+InitMEMC ROUT
+        LDR     R0, ResetMemC_Value
+        STR     R0, [R0]     ; set ROM access times, refresh on flyback, no DMA
+        MOV     pc, lr
+
+; -> MemSize
+
+; (non-destructive) algorithm to determine MEMC RAM configuration
+;
+; Dave Flynn and Alasdair Thomas
+; 17-March-87
+;
+; Spooling checkered by NRaine and SSwales !
+; 8MByte check bodged in by APT
+;
+; NOTE: Routines MemSize and TimeCPU are called by the power-on test software,
+; so their specifications MUST not change.
+;
+; Set MEMC for 32-k page then analyse signature of possible
+; external RAM configurations...
+; The configurations are:
+;
+; Ram Size    Page Size    Configuration    (Phys RAM) Signature
+;--------------------------------------------------------------------
+;  16MByte      32k        4*32*1Mx1         A13,A20,A21,A22,A23,A23.5 distinct
+;  16MByte      32k        16*8*256kx4       A13,A20,A21,A22,A23,A23.5 distinct
+;
+;  12MByte      32k        3*32*1Mx1         A13,A20,A21,A22,A23 OK, A23.5 fail
+;  12MByte      32k        12*8*256kx4       A13,A20,A21,A22,A23 OK, A23.5 fail
+;
+;   8MByte      32k        2*32*1Mx1         A13,A20,A21,A22 distinct, A23 fail
+;   8MByte      32k         8*8*256kx4       A13,A20,A21,A22 distinct, A23 fail
+; 
+;   4Mbyte      32k          32*1Mx1         A13,A21,A20 distinct, A22,A23 fail
+;   4Mbyte      32k         4*8*256kx4       A13,A21,A20 distinct, A22,A23 fail
+;
+;   2Mbyte      32k    expandable 2*8*256kx4 A13,A20 distinct, A21 fails
+;   2Mbyte ???  16k      fixed 2*8*256kx4    A13,A21 distinct, A20 fails
+;   
+;   1Mbyte       8k          32*256kx1       A13,A20 fail, A19,A18,A12 distinct
+;   1Mbyte       8k           8*256kx1       A13,A20 fail, A19,A18,A12 distinct
+;   1Mbyte       8k          4*8*64kx4       A13,A20 fail, A19,A18,A12 distinct
+;
+; 512Kbyte       8k    expandable 2*8*64kx4  A13,A20,A19 fail, A12,A18 distinct
+; 512Kbyte       4k      fixed 2*8*64kx4     A13,A20,A12 fail, A19,A18 distinct
+;
+; 256Kbyte       4K           8*64kx4        A13,A20,A12,A18 fail, A21,A19 ok  
+; 256Kbyte       4K          32*64kx1        A13,A20,A12,A18 fail, A21,A19 ok  
+;
+
+Z_Flag     * &40000000
+
+; MemSize routine... enter with 32K pagesize set
+; R0 returns page size
+; R1 returns memory size
+; R2 returns value set in MEMC
+; uses R3-R7
+
+MemSize ROUT
+        MOV     r7, lr
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #A13
+        BL      DistinctAddresses
+        BNE     %10
+        ADD     r1, r0, #A21
+        BL      DistinctAddresses
+        MOVNE   r0, #Page32K
+        MOVNE   r1, #2048*1024
+        BNE     MemSizeDone
+
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #4*1024*1024
+        BL      DistinctAddresses
+        MOVNE   r0, #Page32K
+        MOVNE   r1, #4*1024*1024
+        BNE     MemSizeDone
+
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #8*1024*1024
+        BL      DistinctAddresses
+        MOVNE   r0, #Page32K
+        MOVNE   r1, #8*1024*1024
+        BNE     MemSizeDone
+
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #12*1024*1024
+        BL      DistinctAddresses
+        MOV     r0, #Page32K
+        MOVNE   r1, #12*1024*1024
+        MOVEQ   r1, #16*1024*1024
+        B       MemSizeDone
+
+10      ADD     r1, r0, #A20
+        BL      DistinctAddresses
+        BNE     %20
+        MOV     r0, #Page16K
+        MOV     r1, #2048*1024
+        B       MemSizeDone
+
+20      ADD     r1, r0, #A19
+        BL      DistinctAddresses
+        BEQ     %30
+        MOV     r0, #Page8K
+        MOV     r1, #512*1024
+        B       MemSizeDone
+
+30      ADD     r1, r0, #A18
+        BL      DistinctAddresses
+        BEQ     %40
+        MOV     r0, #Page4K
+        MOV     r1, #256*1024
+        B       MemSizeDone
+
+40      ADD     r1, r0, #A12
+        BL      DistinctAddresses
+        BEQ     %50
+        MOV     r0, #Page4K
+        MOV     r1, #512*1024
+        B       MemSizeDone
+
+50      MOV     r0, #Page8K
+        MOV     r1, #1024*1024
+
+MemSizeDone
+        LDR     r2, ResetMemC_Value
+        BIC     r2, r2, #&C
+        ORR     r2, r2, r0
+        STR     r2, [r2]                        ; set MEMC to right state
+        MOV     pc, r7
+
+
+; DistinctAddresses routine...
+; r0,r1 are the addresses to check
+; uses r2-5
+; writes interleaved patterns (to prevent dynamic storage...)
+; checks writing every bit low and high...
+; return Z-flag set if distinct
+
+DistinctAddresses ROUT
+        LDR     r2, [r0] ; preserve
+        LDR     r3, [r1]
+        LDR     r4, Pattern
+        STR     r4, [r0] ; mark first
+        MOV     r5, r4, ROR #16
+        STR     r5, [r1] ; mark second
+        LDR     r5, [r0]
+        CMP     r5, r4 ; check first
+        BNE     %10    ; exit with Z clear
+        LDR     r5, [r1] ; check second
+        CMP     r5, r4, ROR #16 ; clear Z if not same
+        BNE     %10
+; now check inverse bit writes
+        STR     r4, [r1] ; mark second
+        MOV     r5, r4, ROR #16
+        STR     r5, [r0] ; mark first
+        LDR     r5, [r1]
+        CMP     r5, r4 ; check second
+        BNE     %10   ; exit with Z clear
+        LDR     r5, [r0] ; check first
+        CMP     r5, r4, ROR #16 ; clear Z if not same
+10      STR     r3, [r1] ; restore
+        STR     r2, [r0]
+        ORREQ   lr, lr, #Z_Flag
+        BICNE   lr, lr, #Z_Flag
+        MOVS    pc, lr
+
+Pattern
+        &       &AAFF5500 ; shiftable bit check pattern
+
+; init state with masked out page size
+
+ResetMemC_Value
+        & &E010C :OR: MEMCADR       ; slugged ROMs + flyback refresh only + 32K page
+
+; Constants
+;
+A21 * 1:SHL:21
+A20 * 1:SHL:20
+A19 * 1:SHL:19
+A18 * 1:SHL:18
+A13 * 1:SHL:13
+A12 * 1:SHL:12
+
+Page32K * &C ; in MEMC control reg patterns...
+Page16K * &8
+Page8K  * &4
+Page4K  * &0
+
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r0-r6 trashable
+;       r9 = Current MEMC CR
+
+; Out   r9 MEMC value with slowest ROM speed, correct pagesize
+;       r7 processor speed in kHz, tbs -> MEMC1a
+
+ncpuloops * 1024 ; don't go longer than 4ms without refresh !
+nmulloops * 128
+
+TimeCPU ROUT
+
+        BIC     r9, r9, #3 :SHL: 8
+        STR     r9, [r9]                ; turn off refresh for a bit
+
+; Time CPU/Memory speed
+
+        LDR     r1, =&7FFE              ; 32K @ 2MHz = ~16ms limit
+        MOV     r3, #IOC
+
+        MOV     r0, r1, LSR #8
+        STRB    r1, [r3, #Timer1LL]
+        STRB    r0, [r3, #Timer1LH]
+        LDR     r0, =ncpuloops
+        STRB    r0, [r3, #Timer1GO]     ; start the timer NOW
+        B       %FT10                   ; Looks superfluous, but is required
+                                        ; to get ncpuloops pipeline breaks
+
+10      SUBS    r0, r0, #1              ; 1S
+        BNE     %BT10                   ; 1N + 2S
+
+        STRB    r0, [r3, #Timer1LR]     ; latch count NOW
+        LDRB    r2, [r3, #Timer1CL]
+        LDRB    r0, [r3, #Timer1CH]
+        ADD     r2, r2, r0, LSL #8      ; count after looping is ...
+
+        SUB     r2, r1, r2              ; decrements !
+        MOV     r2, r2, LSR #1          ; IOC clock decrements at 2MHz
+
+; Time CPU/MEMC Multiply time
+
+        MOV     r4, #-1                 ; Gives worst case MUL
+
+        MOV     r0, r1, LSR #8
+        STRB    r1, [r3, #Timer1LL]
+        STRB    r0, [r3, #Timer1LH]
+        LDR     r0, =nmulloops
+        STRB    r0, [r3, #Timer1GO]     ; start the timer NOW
+        B       %FT20                   ; Looks superfluous, but is required
+                                        ; to get nmulloops pipeline breaks
+
+20      MUL     r5, r4, r4              ; 1S + 16I
+        MUL     r5, r4, r4              ; 1S + 16I
+        SUBS    r0, r0, #1              ; 1S
+        BNE     %BT20                   ; 1N + 2S
+
+        STRB    r0, [r3, #Timer1LR]     ; latch count NOW
+        LDRB    r4, [r3, #Timer1CL]
+        LDRB    r0, [r3, #Timer1CH]
+        ADD     r4, r4, r0, LSL #8      ; count after looping is ...
+
+        SUB     r4, r1, r4              ; decrements !
+        MOV     r4, r4, LSR #1          ; IOC clock decrements at 2MHz
+
+        ORR     r9, r9, #1 :SHL: 8      ; set refresh on flyback
+        STR     r9, [r9]                ; restore MEMC state a.s.a.p.
+
+; In ROM - each cpu loop took 4R cycles @ 8/f*500ns/cycle
+
+        LDR     r0, =4*(8*500/1000)*ncpuloops*1000
+        DivRem  r7, r0, r2, r1          ; r2 preserved
+        MOV     r0, #&80                ; At 8 MHz and below, run fast ROMs
+        LDR     r1, =8050               ; Over 8 MHz, need medium ROMs
+        CMP     r7, r1
+        MOVHI   r0, #&40
+        LDR     r1, =13000              ; Over 13 MHz, need slowest ROMs
+        CMP     r7, r1
+        MOVHI   r0, #&00
+        ORR     r9, r9, r0
+        STR     r9, [r9]                ; Set ROM speed appropriately
+
+ ASSERT ncpuloops = 8*nmulloops ; for given ratio cutoff <------------
+
+        MOV     r4, r4, LSL #10         ; *1024 to get resolution on divide
+        DivRem  r0, r4, r2, r1
+        LDR     r1, =1100               ; Cutoff point; MEMC1 longer than this
+        CMP     r0, r1
+        ORRLO   r7, r7, #1 :SHL: 16     ; Note MEMC1a prescence
+
+        MOV     pc, lr
+
+; Typical figures give (in ROM at 8MHz):
+
+; MEMC1  2048 CPU, 2432 MEMC -> MUL ratio 1216
+; MEMC1a 2048       864                    432
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        END
diff --git a/OldTestSrc/Mem1IOMD b/OldTestSrc/Mem1IOMD
new file mode 100644
index 0000000000000000000000000000000000000000..f1b6a14ea10f568c6dc3079989535a0db79daa94
--- /dev/null
+++ b/OldTestSrc/Mem1IOMD
@@ -0,0 +1,481 @@
+; > TestSrc.Mem1IOMD
+
+        TTL RISC OS 2+ POST memory linetest
+;
+; This test code is used to perform basic integrity tests on DRAM.
+; It doesn't test all locations - just walks patterns through data
+; and address lines.
+;
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name            Comment
+; ----          ----            -------
+; 1-Jun-93      ArtG            Derived from Mem1 for use on Medusa
+;
+;
+;------------------------------------------------------------------------
+
+;
+; Test the data and address and byte strobe lines for uniqueness.
+;
+
+        LTORG
+        ROUT
+
+1
+        =       "VRAM  :",0
+2
+        =       "VRAM-F",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+3
+        =       "DRAM ",&ff,":",0
+4
+        =       "Data  :",0
+5
+        =       &88,&ff,&ff," MByte",0
+
+        ALIGN
+
+ts_LineTest
+
+        ADR     r4,%BT1
+        BL      ts_SendText                             ; Start data line tests on VRAM
+
+        MOV     r0,#0
+        MOV_fiq r9,r0                                   ; r9-fiq records VRAM or low DRAM address
+
+        MOV     r12, #IOMD_Base
+        MOV     r2, #IOMD_VREFCR_VRAM_256Kx64 :OR: IOMD_VREFCR_REF_16 ; assume 2 banks of VRAM by default
+        STRB    r2, [r12, #IOMD_VREFCR]
+
+; Find the size, using MemSize's method
+
+        MOV     r0, #VideoPhysRam                       ; point at VRAM
+        ADD     r1, r0, #A2                             ; test A2
+        BL      DistinctAddresses
+        MOVEQ   r9, #2                                  ; we've got 2M of VRAM
+        BEQ     %FT21
+
+        MOV     r2, #IOMD_VREFCR_VRAM_256Kx32 :OR: IOMD_VREFCR_REF_16
+        STRB    r2, [r12, #IOMD_VREFCR]
+        ADD     r1, r0, #A2                             ; check for any VRAM at all
+        BL      DistinctAddresses
+        MOVEQ   r9, #1                                  ; we've got 1M of VRAM
+        MOVNE   r9, #0                                  ; no VRAM
+21
+        BNE     %FT22
+        MOV_fiq r9,r0                                   ; record VRAM address
+        FAULT   #R_VRAM                                 ; indicate VRAM present
+
+; Report size .. if this is non-zero and the data line test fails,
+; RISC OS will have problems.
+
+22
+        ADR     r4,%BT5                                 ; Add size (in hex Mbyte)
+        MOV     r8,r9, LSL #24                          ; to "VRam : " message
+        BL      ts_MoreText     
+
+; Worked out what size VRAM is, and set up IOMD register. 
+; Do a data line test on the resulting array, repeated at oddword address to 
+; ensure both banks get tested with walking 0 and walking 1
+
+        ADR     r4,%BT4
+        BL      ts_SendText
+        MOV     r1, #VideoPhysRam
+        BL      ts_Dataline
+        ADDEQ   r1,r1,#4
+        BLEQ    ts_Dataline
+        BEQ     %FT25                   ; looks OK - carry on with VRAM test
+;
+; Data line test failed. Report the bitmap that failed, then carry on.
+;
+        ADR     r4,%BT2
+        MOV     r8,r0                   ; report data fault mask
+        BL      ts_SendText
+        B       %FT30
+
+;
+; If there was some VRAM found here, and it passed the dataline test,
+; do the address and bytestrobe tests on it too.
+;
+
+25
+        ADRL    r4,%FT75                                ; announce start of address line test
+        BL      ts_SendText
+        MOV     r1,#VideoPhysRam
+        MOV     r0,r9,LSL #20                           ; size in MB determined before dataline test
+        BL      ts_Addrline
+        BEQ     %FT26
+        ADRL    r4,%FT76                                ; failed - report error mask
+        MOV     r8,r0
+        BL      ts_SendText
+        FAULT   #R_LINFAILBIT                           ; and record failure
+        B       %FT30
+26
+        ADRL    r4,%FT77                                ; announce start of byte test
+        BL      ts_SendText
+        MOV     r1,#VideoPhysRam
+        BL      ts_Byteword
+        ADDEQ   r1,r1,#4                                ; retest at an oddword boundary
+        BLEQ    ts_Byteword
+        BEQ     %FT27
+        ADRL    r4,%FT78                                ; failed - report error mask
+        MOV     r8,r0,LSL #16
+        BL      ts_SendText
+        FAULT   #R_LINFAILBIT                           ; and record failure
+27
+
+
+; Similarly, test each DRAM bank in turn, reporting failures or sizes for each
+
+30
+        MOV     r11, #IOMD_DRAMCR_DRAM_Large * &55      ; set all banks to be large initially
+        MOV     r14, #IOMD_Base
+        STRB    r11, [r14, #IOMD_DRAMCR]
+        MOV     r0,#MMUC_D                              ; enable 32-bit addressing of data
+        SetCop  r0,CR_Control
+
+        MOV     r10, #0                                 ; indicate no RAM found yet
+        MOV     r9, #IOMD_DRAMCR_DRAM_Small             ; bit to OR into DRAMCR
+        MOV     r12, #DRAM0PhysRam
+35
+        MOV     r8,r12,LSL #2                           ; indicate bank under test
+        AND     r8,r8,#(3 :SHL: 28)
+        ADR     r4,%BT3
+        BL      ts_SendText
+
+        MOV     r8,#0                                   ; r8 indicates RAM found in this bank
+        MOV     r0, r12
+        ADD     r1, r12, #A10                           ; this should be OK for both configurations
+        BL      DistinctAddresses
+        BNE     %FT50                                   ; [no RAM in this bank at all]
+
+        MOV_fiq r2,r9                                   ; if this is the first bank of DRAM or VRAM,
+        TEQS    r2,#0                                   ; put it's address in r9_fiq
+        BNE     %FT36
+        MOV_fiq r9,r0
+
+36      ADD     r1, r12, #A11                           ; test for 256K DRAM
+        BL      DistinctAddresses
+        ORRNE   r11, r11, r9                            ; it is, so select small multiplexing
+        MOVNE   r14, #IOMD_Base
+        STRNEB  r11, [r14, #IOMD_DRAMCR]                ; store new value of DRAMCR, so we can use memory immediately
+        MOVNE   r8, #1024*1024                          ; must be 1Mbyte at this address
+        BNE     %FT50
+
+; it's bigger than 256K words, so test address lines A21-A25 in sequence
+; we assume that the size of each bank is a power of 2
+
+        MOV     r8, #A21                                ; now go through address lines A21-A25
+40
+        ADD     r1, r12, r8                             ; see if this address line is unique
+        BL      DistinctAddresses
+        BNE     %FT50                                   ; if we've failed then r8 is true size, so exit
+        MOV     r8, r8, LSL #1                          ; else shift up to next
+        TEQ     r8, #A26                                ; only test up to A25
+        BNE     %BT40
+
+50
+        MOV     r13,r8                                  ; remember size of this bank in bytes
+        MOV     r8,r13,LSL #(24 - 20)                   ; and display it in 2 digits, in MBytes.
+        ADR     r4,%BT5
+        BL      ts_MoreText
+
+        ADRL    r4,%FT73                                ; announce data line test
+        BL      ts_SendText
+        MOV     r1,r12                                  ; do walking bit test
+        BL      ts_Dataline
+        BEQ     %FT55                                   ; looks OK, carry on to next bank
+
+        ADRL    r4,%FT74                                ; bit test failed, so report it
+        MOV     r8,r0
+        BL      ts_SendText                             ; and bit fault mask
+
+        CMPS    r13,#0                                  ; was any RAM thought to be here ?
+        BEQ     %FT55
+        FAULT   #R_LINFAILBIT                           ; if so, it's faulty.
+        MOV     r13,#0                                  ; so ignore it
+55
+
+;
+; If there was some RAM found here, and it passed the dataline test,
+; do the address and bytestrobe tests on it too.
+;
+        CMPS    r13,#0
+        BEQ     %FT60
+
+        ADR     r4,%FT75                                ; announce start of address line test
+        BL      ts_SendText
+        MOV     r1,r12                                  ; test address lines in this block
+        MOV     r0,r13
+        BL      ts_Addrline
+        BEQ     %FT56
+        ADR     r4,%FT76                                ; failed - report error mask
+        MOV     r8,r0
+        BL      ts_SendText
+        FAULT   #R_LINFAILBIT                           ; and record failure
+        MOV     r13,#0                                  ; then forget this memory block
+
+56
+        ADR     r4,%FT77                                ; announce start of byte test
+        BL      ts_SendText
+        MOV     r1,r12
+        BL      ts_Byteword
+        BEQ     %FT60
+        ADR     r4,%FT78                                ; failed - report error mask
+        MOV     r8,r0,LSL #16
+        BL      ts_SendText
+        FAULT   #R_LINFAILBIT                           ; and record failure
+        MOV     r13,#0                                  ; then forget this memory block
+60
+
+
+; If the RAM found still seems OK, add it's size into the r10 accumulator
+; Working or not, carry on to check the next bank.
+
+        ADD     r10,r10,r13                             ; accumulate DRAM if any found 
+        ADD     r12, r12, #DRAM1PhysRam-DRAM0PhysRam    ; move onto next bank
+        MOV     r9, r9, LSL #2                          ; shunt up position in DRAMCR
+        CMP     r9, #&100                               ; if more banks to do
+        BCC     %BT35                                   ; then loop
+
+        ADR     r4,%FT70
+        BL      ts_SendText                             ; None found .. print message
+
+        MOVS    r8,r10,LSL #(24 - 20)                   ; all finished ..
+        ADREQ   r4,%FT71                                ; did we find any DRAM?
+        ADRNE   r4,%FT72
+        BNE     %FT65
+        FAULT   #R_LINFAILBIT                           ; fault if we didn't
+65
+        BL      ts_MoreText
+        B       ts_endline
+
+
+70
+        =       "DRAM",0
+71
+        =       &88,"Failed",0
+72
+        =       &88,&ff,&ff," MByte",0
+73
+        =       "Data  :",0
+74
+        =       "Data-F",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+75
+        =       "Addrs :",0
+76
+        =       "Addrs-F",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+77
+        =       "Byte  :",0
+78
+        =       "Byte-F",&88,&ff,&ff,&ff,&ff,0
+
+
+;
+; Data line test.
+;
+; In  : r1  - start address for test
+;
+; Out : r0  - failing data pattern
+;       r1  - address of failure
+;
+;
+; This exercises data lines in attempt to find shorts/opens.
+; It goes something like :
+;
+;       for (ptr = address, pattern = 1; pattern != 0; pattern <<= 1)
+;               *ptr++ =  pattern;
+;               *ptr++ = ~pattern;
+;       for (ptr = address, pattern = 1; pattern != 0; pattern <<= 1)
+;               result |=  pattern ^ *ptr++;
+;               result |= ~pattern ^ *ptr++;
+;       return result and address
+;
+
+ts_Dataline     ROUT
+
+;
+; Write all walking-zero, walking-one patterns
+;
+10      MOV     r6,r1                   ; set pointer for a write loop
+        MOV     r5,#1                   ; set initial test pattern
+        MVN     r4,r5                   ; and it's inverse        
+11
+        STMIA   r6!,{r4-r5}             ; write the patterns
+
+        ADDS    r5,r5,r5                ; shift the pattern (into Carry)
+        MVN     r4,r5
+        BCC     %BT11                   ; repeat until all bits done
+;
+; Read back and accumulate in r0 any incorrect bits
+;
+        MOV     r6,r1                   ; set pointer for a read loop
+        MOV     r5,#1                   ; set initial test pattern
+        MVN     r4,r5                   ; and it's inverse        
+        MOV     r0,#0                   ; accumulate result
+21
+        LDMIA   r6!,{r2-r3}             ; read the patterns
+        EOR     r2,r2,r4
+        ORR     r0,r0,r2                ; OR any failed bits into r0
+        EOR     r3,r3,r5
+        ORR     r0,r0,r2
+
+        ADDS    r5,r5,r5                ; shift the pattern (into Carry)
+        MVN     r4,r5
+        BCC     %BT21                   ; repeat until all bits done
+;
+; After all checks at this address group, report back errors
+;
+        MOVS    r0,r0                   ; check for any result bits set 
+        MOV     pc,r14                  ; return r0 with error map (or 0)
+
+
+
+;
+; Address line test
+;
+; In  : r0  - size of memory block
+;       r1  - start address of memory block
+;
+; Out : r0  - failing address bit mask
+;
+; This exercises address lines in an attempt to find any which don't
+; work (i.e., don't select unique addresses).
+;
+; It works something like :
+;
+; MaxRam = PhysRam | (Memory size - 4); 
+; for (pattern = 4; pattern < memsize; pattern <<= 1 )
+;       *(PhysRam ^ pattern) = pattern;
+;       *(MaxRam  ^ pattern) = ~pattern;
+; for (pattern = 4; pattern < memsize; pattern <<= 1 )
+;       if (*PhysRam == *(PhysRam ^ pattern))
+;               result |= pattern;
+;       if (*MaxRam == *(MaxRam + pattern))
+;               result |= pattern;
+;  return result
+;
+
+
+ts_Addrline     ROUT
+
+        MOVS    r7,r0                   ; Save memory size
+        SUB     r6,r0,#4                ; Calculate MaxRam
+        ADD     r6,r6,r1                ; (all-bits-set memory address)
+;
+; Mark (walking one, walking 0) addresses with unique patterns
+;
+        LDR     r5,=&5A5AA5A5           ; initialize end markers
+        STR     r5,[r6]
+        MVN     r4,r5
+        MOV     r3,r1
+        STR     r4,[r3]
+
+        MOV     r5,#4                   ; initialize pattern
+02
+        MVN     r4,r5
+        EOR     r3,r5,r1                ; point to (start ^ pattern)
+        STR     r4,[r3]
+        EOR     r3,r5,r6                ; point to (end ^ pattern)
+        STR     r5,[r3]
+
+        MOV     r5,r5,LSL #1            ; shift test pattern up
+        CMPS    r5,r7                   ; test bit still inside memory ?
+        BCC     %02                     ; reached top bit - end this loop
+;
+; Check (walking one, walking 0) addresses for effectivity
+;
+        MOV     r5,#4                   ; initialize pattern
+        MOV     r3,r1
+        MOV     r0,#0
+04
+        MVN     r4,r5
+        EOR     r2,r5,r3                ; point to (start ^ pattern)
+        LDR     r2,[r2]
+        LDR     r1,[r3]
+        CMPS    r1,r2                   ; do contents differ ?
+        ORREQ   r0,r0,r5                ; no - record ineffective bit
+
+        EOR     r2,r5,r6                ; point to (end ^ pattern)
+        LDR     r2,[r2]
+        LDR     r1,[r6]
+        CMPS    r1,r2                   ; do contents differ ?
+        ORREQ   r0,r0,r5                ; no - record ineffective bit
+
+        MOV     r5,r5,LSL #1            ; shift test pattern up
+        CMPS    r5,r7                   ; test bit still inside memory ?
+        BCC     %04                     ; reached top bit - end this loop
+
+        MOVS    r0,r0                   ; any result bits set - return error
+        MOV     pc,r14
+
+
+;
+; Byte / word test
+;
+; In  :  r1 - memory start
+;
+; Out :  r0 - Failure indication
+;
+; This test ensures that individual bytes may be written to each part of a word
+; without affecting the other bytes in the word.
+;
+;       for (byte = 0; byte < 4; byte ++)
+;               address[0] = word_signature
+;               address[1] = ~word_signature
+;               address + byte = byte_signature
+;               if (address[0] !=
+;                                 (word_signature & (~ff << byte * 8))
+;                               | (byte_signature        << byte * 8)  )
+;                       result |= (1 << byte)
+;       if (result != 0
+;               result |= address;      /* fail at address, byte(s)     */
+;       return result;                       /* pass */
+;
+
+ts_Byteword     ROUT
+
+        LDR     r3,=&AABBCCDD           ; word signature
+        MOV     r0,#0
+        MOV     r2,r0
+;
+; byte test loop ( for bytes 0 to 4  ...)
+;
+02
+        MVN     r4,r3
+        STMIA   r1,{r3,r4}              ; write word signature
+        STRB    r2,[r1,r2]              ; write byte (0, 1, 2 or 3)
+
+        MOV     r4,r2,LSL #3            ; calculate expected result
+        MOV     r5,#&ff     
+        MVN     r5,r5,LSL r4
+        AND     r5,r5,r3                ; word signature, byte removed
+        ORR     r5,r5,r2,LSL r4         ; byte signature inserted
+
+        LDR     r4,[r1,#4]              ; read (probable) inverse data to precharge bus
+        LDR     r4,[r1]                 ; read modified word
+        CMPS    r4,r5
+        MOV     r5,#1
+        MOV     r4,r2,LSL #2
+        ORRNE   r0,r0,r5,LSL r4         ; fault : set bit in result mask
+;
+; Loop for next byte
+;
+        ADD     r2,r2,#1                ; Bump byte counter
+        CMPS    r2,#4                   ; ... until 4 byte strobes tested 
+        BLO     %BT02
+;
+; byte strobes all tested : check for errors
+;
+        CMPS    r0,#0
+        MOV     pc,r14                  ; Result : return address and fault mask.
+
+;
+; End of RAM line tests
+;
+
+ts_endline
+
+        END 
+ 
\ No newline at end of file
diff --git a/OldTestSrc/Mem1MEMC1 b/OldTestSrc/Mem1MEMC1
new file mode 100644
index 0000000000000000000000000000000000000000..632c9bf2b8e5b33faf87dc5ac37826a43174df3d
--- /dev/null
+++ b/OldTestSrc/Mem1MEMC1
@@ -0,0 +1,390 @@
+; > TestSrc.Mem1
+
+        TTL RISC OS 2+ POST memory linetest
+;
+; This test code is used to perform basic integrity tests on DRAM.
+; It doesn't test all locations - just walks patterns through data
+; and address lines.
+;
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name            Comment
+; ----          ----            -------
+; 18-Dec-89     ArtG            Initial version
+; 1-Jun-93	ArtG		Reorganised to allow separate module for Medusa
+;
+;
+;------------------------------------------------------------------------
+
+;
+; Test the data and address and byte strobe lines for uniqueness.
+;
+
+        LTORG
+        ROUT
+
+1
+        =       "Data :",0
+2
+        =       "Data @",&89,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+3
+        =       "Data-F",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+4
+        =       "Data-P",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+
+
+
+        ALIGN
+
+ts_LineTest
+
+        ADR     r4,%BT1
+        BL      ts_SendText             ; Start data line tests
+
+        MOV_fiq r0,r10_fiq
+        MOV     r1, #PhysRam
+        BL      ts_Dataline
+        BEQ     ts_address              ; OK : continue to next test
+;
+; Data line test failed. This probably also means that RISCOS got the
+; configuration wrong, so set it to 32K pages and repeat - otherwise 
+; the data line test result may be garbage.
+;
+        ADR     r4,%BT2
+        MOV     r11,r0                  ; save data & report fault address
+        MOV     r8,r1,LSL #4
+        BL      ts_SendText
+
+        MOV     r8,r11
+        ADR     r4,%BT3                 ; report data fault mask
+        BL      ts_SendText
+
+        LDR     r0,=(&E000C :OR: MEMCADR) ; set 32K page size
+        STR     r0,[r0]
+        MOV_fiq r11_fiq,r0
+
+        MOV     r0,#ts_RamChunk         ; limit test to 1 block
+        MOV     r1,#PhysRam
+        BL      ts_Dataline   
+
+        MOV     r8,r0
+        ADR     r4,%BT4                 ; ready to report data fault mask
+        B       ts_linefault
+
+;
+; Start the address line tests
+;
+        ROUT
+
+4
+        =       "Addrs :",0
+5
+        =       "Addrs",&89,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+6
+        =       "Byte :",0
+7
+        =       "Byte",&89,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+
+
+
+ts_address
+        ADR     r4,%BT4
+        BL      ts_SendText             ; Start address line tests
+
+        MOV_fiq r0,r10_fiq
+        BL      ts_Addrline
+
+        ADR     r4,%BT5
+        MOV     r8,r0,LSL #4
+        BEQ     %30                     ; Failed : report address fault
+
+ts_linefault      
+        FAULT   #R_LINFAILBIT
+        B       %31
+
+30      ADR     r4,%BT6                 ; Start Byte/Word test
+        BL      ts_SendText
+
+        MOV_fiq r0,r10_fiq              ; get memory size
+        BL      ts_Byteword
+
+        MOV     r8,r0,LSL #4            ; Get result to top of r8 
+        BEQ     %40
+        FAULT   #R_LINFAILBIT
+
+        ADR     r4,%BT7
+
+31      BL      ts_SendText
+        B       %42
+;
+; Line tests passed. Do a short test on memory that isn't there,
+; in case it's supposed to be and we want to know why it's not ..
+
+40
+        MOV_fiq r0, r10_fiq             ; if there's less than 16Mbytes ..
+        CMP     r0, #(16 * 1024 * 1024)
+        BCS     %F42
+        ADR     r4, %FT44               ; briefly test the next bit of ram
+        BL      ts_SendText             ; in case it's a duff expansion
+
+        MOV_fiq r1,r10_fiq
+        ADD     r1,r1,#PhysRam
+        MOV     r0,#ts_RamChunk
+        BL      ts_Dataline
+        ADR     r4, %FT45
+        MOV     r11, r0                 ; report the result even if OK
+        MOV     r8,r1,LSL #4
+        BL      ts_SendText             ; report address
+
+        MOV     r8,r11
+        ADR     r4,%FT46                ; report data fault mask
+        BL      ts_SendText
+;
+; End of line tests
+;
+
+42
+        B       ts_IOCTest
+
+44
+        =       "Exp? :",0
+45
+        =       "Exp? @",&89,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+46
+        =       "Exp?",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+
+
+
+;
+; Data line test.
+;
+; In  : r0  - size of memory
+;       r1  - start address for test
+;
+; Out : r0  - failing data pattern
+;       r1  - address of failure
+;
+;
+; This exercises data lines in attempt to find shorts/opens.
+; It goes something like :
+;
+; for (address = start; address < end of ram; address += ts_RamChunk)
+;       for (ptr = address, pattern = 1; pattern != 0; pattern <<= 1)
+;               *ptr++ =  pattern;
+;               *ptr++ = ~pattern;
+;       for (ptr = address, pattern = 1; pattern != 0; pattern <<= 1)
+;               result |=  pattern ^ *ptr++;
+;               result |= ~pattern ^ *ptr++;
+;       if (result |= 0)
+;               return result and address
+;
+
+ts_Dataline     ROUT
+
+        ADD     r7,r1,r0                ; end address
+;
+; Write all walking-zero, walking-one patterns
+;
+10      MOV     r6,r1                   ; set pointer for a write loop
+        MOV     r5,#1                   ; set initial test pattern
+        MVN     r4,r5                   ; and it's inverse        
+11
+        STMIA   r6!,{r4-r5}             ; write the patterns
+
+        ADDS    r5,r5,r5                ; shift the pattern (into Carry)
+        MVN     r4,r5
+        BCC     %BT11                   ; repeat until all bits done
+;
+; Read back and accumulate in r0 any incorrect bits
+;
+        MOV     r6,r1                   ; set pointer for a read loop
+        MOV     r5,#1                   ; set initial test pattern
+        MVN     r4,r5                   ; and it's inverse        
+        MOV     r0,#0                   ; accumulate result
+21
+        LDMIA   r6!,{r2-r3}             ; read the patterns
+        EOR     r2,r2,r4
+        ORR     r0,r0,r2                ; OR any failed bits into r0
+        EOR     r3,r3,r5
+        ORR     r0,r0,r2
+
+        ADDS    r5,r5,r5                ; shift the pattern (into Carry)
+        MVN     r4,r5
+        BCC     %BT21                   ; repeat until all bits done
+;
+; After all checks at this address group, report back errors
+;
+        MOVS    r0,r0                   ; check for any result bits set 
+        MOVNE   pc,r14                  ; return on error
+;
+; Bump to another address group
+;
+        ADD     r1,r1,#ts_RamChunk
+        CMPS    r1,r7                   ; test for loop end
+        BLO     %10
+
+        SUBS    r1,r1,#ts_RamChunk      ; no fault - last tested address
+        MOVS    r0,r0
+        MOV     pc,r14                  ; test complete - no failures.
+
+
+;
+; Address line test
+;
+; In  : r0  - size of memeory
+;
+; Out : r0  - failing address bit mask
+;
+; This exercises address lines in an attempt to find any which don't
+; work (i.e., don't select unique addresses).
+;
+; It works something like :
+;
+; MaxRam = PhysRam | (Memory size - 4); 
+; for (pattern = 4; pattern < memsize; pattern <<= 1 )
+;       *(PhysRam ^ pattern) = pattern;
+;       *(MaxRam  ^ pattern) = ~pattern;
+; for (pattern = 4; pattern < memsize; pattern <<= 1 )
+;       if (*PhysRam == *(PhysRam ^ pattern))
+;               result |= pattern;
+;       if (*MaxRam == *(MaxRam + pattern))
+;               result |= pattern;
+;  return result
+;
+
+
+ts_Addrline     ROUT
+
+        MOVS    r7,r0                   ; Save memory size
+        SUB     r6,r0,#4                ; Calculate MaxRam
+        ADD     r6,r6,#PhysRam          ; (all-bits-set memory address)
+;
+; Mark (walking one, walking 0) addresses with unique patterns
+;
+        LDR     r5,=&5A5AA5A5           ; initialize end markers
+        STR     r5,[r6]
+        MVN     r4,r5
+        MOV     r3,#PhysRam
+        STR     r4,[r3]
+
+        MOV     r5,#4                   ; initialize pattern
+02
+        MVN     r4,r5
+        EOR     r3,r5,#PhysRam          ; point to (start ^ pattern)
+        STR     r4,[r3]
+        EOR     r3,r5,r6                ; point to (end ^ pattern)
+        STR     r5,[r3]
+
+        MOV     r5,r5,LSL #1            ; shift test pattern up
+        CMPS    r5,r7                   ; test bit still inside memory ?
+        BCC     %02                     ; reached top bit - end this loop
+;
+; Check (walking one, walking 0) addresses for effectivity
+;
+        MOV     r5,#4                   ; initialize pattern
+        MOV     r3,#PhysRam
+        MOV     r0,#0
+04
+        MVN     r4,r5
+        EOR     r2,r5,r3                ; point to (start ^ pattern)
+        LDR     r2,[r2]
+        LDR     r1,[r3]
+        CMPS    r1,r2                   ; do contents differ ?
+        ORREQ   r0,r0,r5                ; no - record ineffective bit
+
+        EOR     r2,r5,r6                ; point to (end ^ pattern)
+        LDR     r2,[r2]
+        LDR     r1,[r6]
+        CMPS    r1,r2                   ; do contents differ ?
+        ORREQ   r0,r0,r5                ; no - record ineffective bit
+
+        MOV     r5,r5,LSL #1            ; shift test pattern up
+        CMPS    r5,r7                   ; test bit still inside memory ?
+        BCC     %04                     ; reached top bit - end this loop
+
+        MOVS    r0,r0                   ; any result bits set - return error
+        MOV     pc,r14
+
+
+;
+; Byte / word test
+;
+; In  :  r0 - memory size
+;
+; Out :  r0 - address of physical ram where failure occured
+;
+; This test ensures (for each of four possible MEMCs fitted)
+; that individual bytes may be written to each part of a word
+; without affecting the other bytes in the word.
+;
+; for (address = PhysRam; address < PhysRam + Memsize; address += 4Mbyte)
+;       for (byte = 0; byte < 4; byte ++)
+;               address[0] = word_signature
+;               address[1] = ~word_signature
+;               address + byte = byte_signature
+;               if (address[0] !=
+;                                 (word_signature & (~ff << byte * 8))
+;                               | (byte_signature        << byte * 8)  )
+;                       result |= (1 << byte)
+;       if (result != 0
+;               result |= address;      /* fail at address, byte(s)     */
+;               return result;
+;  return result;                       /* pass */
+;
+
+ts_Byteword     ROUT
+
+        ADD     r7,r0,#PhysRam          ; Set test limit address
+        MOV     r1,#PhysRam             ; Initial test address
+        LDR     r3,=&AABBCCDD           ; word signature
+;
+; MEMC test loop (for addresses 4M, 8M, ...)
+;
+01
+        MOV     r0,#0                   ; clear result register
+        MOV     r2,#0                   ; clear byte count
+;
+; byte test loop ( for bytes 0 to 4  ...)
+;
+02
+        MVN     r4,r3
+        STMIA   r1,{r3,r4}              ; write word signature
+        STRB    r2,[r1,r2]              ; write byte
+
+        MOV     r4,r2,LSL #3            ; calculate expected result
+        MOV     r5,#&ff     
+        MVN     r5,r5,LSL r4
+        AND     r5,r5,r3                ; word signature, byte removed
+        ORR     r5,r5,r2,LSL r4         ; byte signature inserted
+
+        LDR     r4,[r1,#4]
+        LDR     r4,[r1]                 ; read modified word
+        CMPS    r4,r5
+        MOV     r5,#1
+        ORRNE   r0,r0,r5,LSL r2         ; fault : set bit in result mask
+;
+; Loop for next byte
+;
+        ADD     r2,r2,#1                ; Bump byte counter
+        CMPS    r2,#4                   ; ... until 4 byte strobes tested 
+        BLO     %BT02
+;
+; byte strobes all tested : check for errors
+;
+        CMPS    r0,#0
+        ORRNE   r0,r0,r1
+        MOVNE   pc,r14                  ; Error : return address and fault.
+;
+; Loop for next MEMC
+;
+        ADD     r1,r1,#&400000          ; Bump to next MEMC
+        CMPS    r1,r7
+        BLO     %01
+
+        MOVS    r0,#0                   ; Passed - return OK
+        MOV     pc,r14
+
+
+        END 
+ 
\ No newline at end of file
diff --git a/OldTestSrc/Mem2 b/OldTestSrc/Mem2
new file mode 100644
index 0000000000000000000000000000000000000000..89f5bc2d3a13a7acf9b701c7cdf5a16209c3fec9
--- /dev/null
+++ b/OldTestSrc/Mem2
@@ -0,0 +1,278 @@
+;> MEM2C
+; 
+; RISC OS 2+ BOOT TEST SOFTWARE
+; MEMORY TEST 2 VERSION A.
+; BRIAN RICE 30-10-89
+; 06-Apr-90     ArtG    0.1     Test variable memory size
+;
+; This file will perform a simple test on all DRAM.
+; The test code for this test was taken from thhe A680 Quick memory 
+; test software. The software was copied straight but the number of times 
+; the test looped arround was cut down to two loops, because of time
+; constraints when testing the memory.
+
+Test_wks_msize      * &40               ; Space for test block size
+Test_wks_return1    * &44 		; Space for return addresses
+Test_wks_return2    * &48
+Test_code_off       * &4C               ; Where testing starts
+
+test_size           * 13 * 4            ; Size of test group
+test_mem_rsvd       * Test_code_off+test_mem_template_end-test_mem_template
+
+;
+; Quick test the RAM (pre boot style)
+;
+
+ts_RamTest ROUT
+	MOV	r13,r0
+	STR	r14,[r13,#Test_wks_return1]
+	STR	r1,[r13,#Test_wks_msize]
+
+	LDR    r0, test_quick_pattern
+	BL     test_mem_code
+	ORRS   r0,r0,r0
+	BNE    test_mem_quit
+;
+	LDR    r0, test_quick_pattern
+	MVN    r0, r0		 ; inverse pattern
+	BL     test_mem_code
+	ORRS   r0,r0,r0
+
+test_mem_quit
+	ADR	r12,%22
+	BEQ     %10
+
+; If fault detected, exit with zero flag clear, r0 pointing to failing
+; location, r1 containing faulty data and r2 pointing a suitable error
+; message indicating whether all-0 or all-1 data was expected.
+
+	LDR     r2,[r14]	        ; fetch failing instructiom
+	ANDS    r2,r2,#1	        ; calculate expected data
+	ADREQ   r12,%20	        	; and load suitable message
+	ADRNE   r12,%21
+	MOVS    r0,r0			; with zero flag set for PASS. 
+10	
+	LDR	pc,[r13,#Test_wks_return1]
+
+; Fail messages indicate incorrect data read after WRote 0 or Wrote 1
+; to all bits at that location.
+
+20
+	=       "WR-0 RD",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+21
+	=       "WR-1 RD",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+22
+	=	"??",0
+
+	ALIGN
+
+test_quick_pattern  & &0f76
+
+; Large Memory test. Generates the write + test routines in memory
+; then calls them. The routine tests patterns as defined by the bottom
+; 13 bits of r0.
+;
+; N.B. The test start address must be calculated to ensure that
+;      the loops finish exactly with r0 equal to End_memory
+;
+; The routine returns with eq true if the memory is OK.
+
+
+test_mem_code
+	ROUT
+
+	STR	r14, [r13, #Test_wks_return2]
+;
+; Copy the ram test code into low ram, modifying MOV instructions
+; to MVN in accordance with the test pattern. 
+;
+	ADR    r1, test_mem_template
+	ADD    r2, r13,        #Test_code_off
+	LDMIA  r1!, {r3-r4}		; copy initial 2 instrucions
+	STMIA  r2!, {r3-r4}
+	MOV    r4, #1
+0	MOVS   r0, r0,         ROR #1
+	LDR    r3, [r1],       #4
+	ORRCS  r3, r3,         #&00400000 ; Convert MOV => MVN
+	STR     r3, [r2],      #4
+	ADD     r4, r4,        #1
+	CMP     r4, #13
+	BLE     %B0
+;
+; Copy the load loop control and verify start instructions
+;
+	LDMIA   r1!, {r5-r9}
+	STMIA   r2!, {r5-r9}
+;
+; Copy and modify the CMP instructions
+;
+	MOV     r0, r0,        ROR #32-13
+	MOV     r4, #1
+1	MOVS    r0, r0,        ROR #1
+	LDR     r3, [r1],      #4
+	ORRCS   r3, r3,        #&00200000 ; Convert CMP => cmn
+	ORRCS   r3, r3,        #&00000001 ; Convert  #0 =>  #1
+	STR     r3, [r2],      #4
+	ADD     r4, r4,        #1
+	CMP     r4,	  #13
+	BLE     %B1
+;
+; Copy the verify loop control and finishing-up instructions
+;
+	LDMIA   r1!, {r5-r12}
+	STMIA   r2!, {r5-r12}
+	LDMIA   r1!, {r5-r12}
+	STMIA   r2!, {r5-r12}
+	LDMIA   r1!, {r5-r12}
+	STMIA   r2!, {r5-r12}
+
+; check we've copied enough
+	ASSERT  ((test_mem_stadd - test_mem_chk) = (24 * 4))
+;
+; Calculate the test start and end addresses
+;
+	LDR	r0, [r13, #Test_wks_msize]	; size of test area
+	ADD     r14, r13, r0			; end of test area
+	SUB     r1, r0, #test_mem_rsvd		; testable size
+
+	MOV     r2, #test_size		; adjust r1 to (r1 / 13*4) * (13*4)
+	DivRem  r3, r1, r2, r4
+	MUL     r1, r3, r2
+	SUB     r0, r14, r1			; rounded test start address
+
+; Do it.
+	MOV     r1, #Test_code_off
+	ADD     r1, r1, r13			; pointer to copied code
+	MOV     pc, r1
+
+;
+; The following code is copied  (and modified) into RAM for execution
+;
+
+test_mem_template 
+	ROUT
+	STR     r0, test_mem_stadd      ; save initial RAM address
+	STR	r13, test_mem_base	; save test area base address
+	MOV     r1, #0		; Converted to MVN if bit = 1
+	MOV     r2, #0		; Converted to MVN if bit = 1
+	MOV     r3, #0		; Converted to MVN if bit = 1
+	MOV     r4, #0		; Converted to MVN if bit = 1
+	MOV     r5, #0		; Converted to MVN if bit = 1
+	MOV     r6, #0		; Converted to MVN if bit = 1
+	MOV     r7, #0		; Converted to MVN if bit = 1
+	MOV     r8, #0		; Converted to MVN if bit = 1
+	MOV     r9, #0		; Converted to MVN if bit = 1
+	MOV     r10, #0	         ; Converted to MVN if bit = 1
+	MOV     r11, #0	         ; Converted to MVN if bit = 1
+	MOV     r12, #0	         ; Converted to MVN if bit = 1
+	MOV     r13, #0	         ; Converted to MVN if bit = 1
+0
+	STMIA   r0!, {r1-r13}
+	CMP     r0, r14
+	BLO     %B0
+
+	LDR     r0, test_mem_stadd
+1
+	LDMIA   r0!, {r1-r13}
+2
+	CMP     r1, #0		; Converted to cmn   if bit = 1
+	CMPEQ   r2, #0		; Converted to cmneq if bit = 1
+	CMPEQ   r3, #0		; Converted to cmneq if bit = 1
+	CMPEQ   r4, #0		; Converted to cmneq if bit = 1
+	CMPEQ   r5, #0		; Converted to cmneq if bit = 1
+	CMPEQ   r6, #0		; Converted to cmneq if bit = 1
+	CMPEQ   r7, #0		; Converted to cmneq if bit = 1
+	CMPEQ   r8, #0		; Converted to cmneq if bit = 1
+	CMPEQ   r9, #0		; Converted to cmneq if bit = 1
+	CMPEQ   r10, #0	        ; Converted to cmneq if bit = 1
+	CMPEQ   r11, #0	        ; Converted to cmneq if bit = 1
+	CMPEQ   r12, #0	        ; Converted to cmneq if bit = 1
+	CMPEQ   r13, #0	        ; Converted to cmneq if bit = 1
+test_mem_chk 
+	BNE     %F5		; go report fault data
+	CMP     r0, r14
+	BLO     %B1		; else loop for next batch
+	MOVS    r0, #0		; All OK : return with NULL r0
+4
+	LDR	r13,test_mem_base
+	LDR	pc, [r13, #Test_wks_return2]
+
+; Failed : repeat the last batch of tests one at a time, to determine
+; the first failing address and data.
+; Note that the test instructions are copied to %8 to permit individual
+; execution, and %7 is overwritten with an instruction used to copy
+; the failing data into r1. Change this code very carefully ! 
+
+5
+	LDR     r14,%2			; Obtain first test in the set
+	STR     r14,%8			; and re-execute it
+	SUB     r0,r0,#(13*4)	   	; adjust pointer to bad data
+	ADR     r14,%2			; point to first test.
+7
+	B       %8		    	; make sure %8 is refetched
+8
+	&       0		     	; redo the test here :
+	BNE     %4		    	; if it failed, exit with
+				    	; r0  =  ptr to memory
+				    	; r1  =  wrongly read data
+				    	; r14 => failing instruction
+
+	LDR     r1,[r14,#4]!	    	;fetch next instruction
+	AND     r1,r1,#&f0000		;make an instruction 
+	MOV     r1,r1,LSR #16		;to copy the next register 
+	ORR     r1,r1,#&E1000000	;down to r1
+	ORR     r1,r1,#&00A00000	;e.g. CMPEQ r10,#0
+	ORR     r1,r1,#&00001000
+	STR     r1,%7		 	;and put it at %7
+	LDR     r1,[r14]	        ;then copy the next test
+	STR     r1,%8		 	;to %8
+	ADD     r0,r0,#4	        ;bump the fault pointer
+	B       %7		    	;and execute %7 and %8.
+
+test_mem_stadd				; address of first test location
+	&       0
+test_mem_base
+	&	0			; address of test block
+
+test_mem_template_end
+
+;
+; Copy the L2 page table from r1 to r0, then remap the translation table's
+; base address in the MMU to point to an L1 page table within it.
+;
+	ROUT
+
+ts_remap_ttab
+	MOV	r2,#FixedAreasL2Size
+	ADD	r0,r0,r2		; point to locations in PhysSpace
+	ADD	r0,r0,#PhysSpace
+	ADD	r1,r1,r2
+	ADD	r1,r1,#PhysSpace
+10
+	ASSERT	((FixedAreasL2Size :AND: ((8*4)-1)) = 0)
+	LDMDB	r1!,{r3-r10}		; copy the page & section tables
+	STMDB	r0!,{r3-r10}
+	SUBS	r2,r2,#(8*4)
+	BNE	%BT10
+
+	SUB	r9,r1,r0		; r9 = offset from original to copy 
+        ADD     r0, r0, #DRAMOffset_L1PT-DRAMOffset_L2PT	; r0 -> copy of L1Phys
+	SUB	r10, r0, #PhysSpace	; keep real address of L1PT for MMU
+	ADD	r2,r0,#((1 :SHL: (32-20))*4)	; size of L1PT - 1 word per meg of memory
+11	LDR	r3,[r0],#4		; check each L1 table entry
+	ANDS	r4,r3,#3
+	CMPS	r4,#L1_Page		; if it's page mapped ..
+	SUBEQ	r3,r3,r9		; adjust the page table base address
+	STREQ	r3,[r0,#-4]
+	CMPS	r0,r2			; repeat for all the level 1 table
+	BNE	%BT11	
+
+        SetCop  r10, CR_TTabBase	; set up MMU pointer to L1
+        SetCop  r0, CR_IDCFlush		; flush cache + TLB just in case
+        SetCop  r0, CR_TLBFlush		; (data written is irrelevant)
+
+	MOV	pc,r14
+
+
+ END
+  
diff --git a/OldTestSrc/Mem3 b/OldTestSrc/Mem3
new file mode 100644
index 0000000000000000000000000000000000000000..1313275f4a55e6f7eb344e50900cdd97407b5f33
--- /dev/null
+++ b/OldTestSrc/Mem3
@@ -0,0 +1,119 @@
+        ;> RomCheck
+; 
+; RISC OS 2+ BOOT TEST SOFTWARE
+; MEMORY TEST 3 VERSION A.
+; BRIAN RICE 01-11-89
+; 24.04.90      0.10    ArtG    Added ROM size test
+; 15.05.90      1.00    ArtG    Changed to put checksum at (end - 2 words)
+; 17.05.90      1.01    ArtG    Changed to get ROM length from vectot table
+;
+;
+; This file will perform quick checksum test on the OS ROMS.
+;
+;
+; The test code for this test is a simple additive checksum routine.
+; The software will read eight words from ROM then add the contents from ROM  
+; to a register. When the test is complete the contents of the checksum
+; register is checked by adding the final word in ROM - this should give 
+; zero.
+; The program will be run from ROM, at slowest speed.
+;
+; All except the last two words are checksummed : these hold the numbers
+; that cause each individual ROM to CRC to zero, so they can't simultaneously
+; be included in an all-zero additive checksum.
+
+ts_CRCsize      *       (2 * 4)
+
+;
+;
+;r0 IS A POINTER TO THE LOCATIONS IN MEMORY.
+;r1 HAS THE CALCULATED CHECKSUM.
+;r2 HOLDS A COUNTER INDICATION HOW MANY WORDS ARE LEFT TO GET
+;r3 is a temporary variable
+;r4 TO r11 ARE USED TO LOAD THE CONTENTS OF 8 LOCATIONS FROM THE ROM.
+;
+        ROUT
+
+ts_ROM_checksum
+
+         MOV    r1, #&00                    ; initialise accumulator    
+         LDR    r0, =PhysROM                ; initialise pointer
+         LDR    r2, [r0, #ts_ROMSIZE]       ; initialise endstop
+         ADD    r2, r2, r0                  ; - must be at least 8 words 
+         SUB    r2, r2, #(10 * 4)           ; below the real endpoint
+
+loop1    LDMIA  r0!, {r4 - r11}             ;LOAD r4 TO r11 WITH THE CONTENTS
+                                            ;OF LOCATIONS POINTED TO BY r0
+                                            ;WHICH IS INCREMEMTED AUTOMATICALLY
+                                            ;TO POINT TO THE NEXT LOCATION
+01
+         ADD    r1, r1,          r4         ;ADD r4  TO CHECKSUM
+         ADD    r1, r1,          r5         ;ADD r5  TO CHECKSUM
+         ADD    r1, r1,          r6         ;ADD r6  TO CHECKSUM
+         ADD    r1, r1,          r7         ;ADD r7  TO CHECKSUM
+         ADD    r1, r1,          r8         ;ADD r8  TO CHECKSUM
+         ADD    r1, r1,          r9         ;ADD r9  TO CHECKSUM
+         ADD    r1, r1,          r10        ;ADD r10 TO CHECKSUM
+         ADD    r1, r1,          r11        ;ADD r11 TO CHECKSUM
+02
+        ASSERT ((%02 - %01) = 32)       ; else r2 won't count down correctly
+ 
+         CMPS   r0, r2
+         BCC    loop1                       ;loop until pointer reaches endstop
+
+         LDMIA  r0!, {r4 - r9}             ; get last 6 words (miss last 2 in ROM)
+03
+         ADD    r1, r1,          r4         ;ADD r4  TO CHECKSUM
+         ADD    r1, r1,          r5         ;ADD r5  TO CHECKSUM
+         ADD    r1, r1,          r6         ;ADD r6  TO CHECKSUM
+         ADD    r1, r1,          r7         ;ADD r7  TO CHECKSUM
+         ADD    r1, r1,          r8         ;ADD r8  TO CHECKSUM
+         ADD    r1, r1,          r9         ;ADD r9  TO CHECKSUM
+04
+        ASSERT  (((%04 - %03) + (2*4)) =  32) ; Change this if you like - 
+                                            ; but be careful to count nearly
+                                            ; to the top in eights, then add
+                                            ; add in the last few words.
+
+         MOVS   r0,r1                       ; should be zero if all OK
+
+         MOV    pc,r14                      ;return with zero flag set on OK
+                                            ;and the calculated sum in r0.
+
+
+;
+; ROM alias check.
+; This test looks for an aliased copy of the vector table at varying
+; distances from the start of ROM space.
+; 16K is fairly arbitrary but corresponds approximately with the size of 
+; the POST. If there's an alias below that, we've probably already crashed !
+;
+; This test is only called if the checksum fails, in order to indicate a
+; possible high ROM address line failure.
+
+ts_ROM_alias    ROUT
+
+        MOV     r0,#PhysROM             ; get some words from ROM start
+        LDR     r3,[r0, #ts_ROMSIZE]    ; get the ROM length word
+        LDMIA   r0,{r4,r5,r6,r7}
+        MOV     r1,#(16 * 1024)
+
+01      ADD     r2,r0,r1                ; get some words from possible alias
+        LDMIA   r2,{r8,r9,r10,r11}
+        CMPS    r4,r8
+        CMPNE   r5,r9
+        CMPNE   r6,r10
+        CMPNE   r7,r11
+        BEQ     %10                     ; aliased : found MS ROM address bit
+
+        MOVS    r1, r1, LSL #1          ; test the next (more significant) bit
+        CMPS    r1, r3                  ; reached the limit yet ?
+        BLT     %01                     ; no - try again.
+
+10      MOV     r0,r1                   ; reached the end, or an alias.
+        MOV     pc,lr
+
+
+  LTORG                     
+
+  END
diff --git a/OldTestSrc/Mem4 b/OldTestSrc/Mem4
new file mode 100644
index 0000000000000000000000000000000000000000..8d73e78c45242b07452cbc64e675df452f4b3f7a
--- /dev/null
+++ b/OldTestSrc/Mem4
@@ -0,0 +1,630 @@
+;> MEM4H_SCR
+;
+; RISC OS 2+ BOOT TEST SOFTWARE.
+; MEMORY TEST 4 VERSION H.      BRIAN RICE 12-01-90.
+; 04-Apr-90     ArtG    0.1     Added ts_count_cams, improved reporting
+; 11-Apr-90     ArtG    0.2     Use RISC OS routine BangCams for 
+;                               alternate MEMC configurations.  
+; 17-Apr-90     ArtG    0.3     rationalise page-counting code
+;
+; This file will be called by MEM6x_SCR for the purposes of assembly.
+; This file will perform quick walking bit test on the CAM Entry points.
+; The test code for this test was taken from the A680 test code.
+;
+; The module requires the running of the memory sizing routine used by
+; the OS to set up the page size for this module.
+;
+; This test module was designed to operate on all current and future 
+; machines. The module is designed to handle up to 512 physical pages 
+; which is the maximum number of pages in a 16 MByte FOX.
+;
+; A 16 MB FOX has 4 MEMCs in use, each MEMC is addressed by Bits 7 and
+; 12 of the logical to physical address translator. The use of bit 12
+; does have a problem in that on machines with 0.5MB of memory this is
+; used to define the logical page number. Machine with 1MB or greater bit
+; 12 is not used, therefore this test may hit problems on A305's. The
+; intention is that A305 owners will upgrade to A310's when upgrading to
+; RISC OS 2+.
+;
+; Because FOX can have up to 4 MEMCs fitted the following addressing is
+; used to determine the MEMC accessed, bit 12, bit 7
+;                                        0      0 = Master MEMC  = MEMC 0
+;                                        0      1 = Slave MEMC 1 = MEMC 1
+;                                        1      0 = Slave MEMC 2 = MEMC 2
+;                                        1      1 = Slave MEMC 3 = MEMC 3
+;
+;
+; This test will initialise the CAM entries for up to 512 physical pages.
+; The physical pages will be mapped to logical page 5. Each page will have
+; a copy of test routine vectors and a page marker. The page marker consists
+; of the page number and a code to indicate which MEMC was used. The code for
+; the MEMC used is as follows :- MEMC 0 0001 1110 = &1E
+;                                MEMC 1 0010 1101 = &2D
+;                                MEMC 2 0100 1011 = &4B
+;                                MEMC 3 1000 0111 = &87
+;
+; The page marker is arranged as follows &mm5Apppp
+;                                          |    |
+;                                          |    \-- Page Number &0000 ‰ &01FF.
+;                                          \--------MEMC Code as above.
+;
+; The patterns are chosen so that if two or more MEMCs are accessed
+; together and both RAM outputs get enabled onto the data bus simultaneously,
+; then there is a reasonable chance that the data returned will show the 
+; presence of a fault.
+;
+; When the CAM entries have been initialised the module will then check that
+; all the pages are mapped correctly. A simple walking one pattern is used
+; to check that the page is not present anywhere else in the memory area.
+; This isn't really sufficient, but keeps the test time low.
+;
+; The tests are performed with the memory protection level set to 0.
+;
+; This version uses the "my abort" routine in MEM5x_SCR instead of the
+; ts_dab_exp0 .. 5 method as taken from the A680 code.
+;
+
+ts_rst_msg      =       "RST",0
+ts_uni_msg      =       "UDF",0
+ts_swi_msg      =       "SWI",0
+ts_pab_msg      =       "PAB",0
+ts_dab_msg      =       "DAB",0
+ts_aex_msg      =       "ADX",0
+ts_irq_msg      =       "IRQ",0
+ts_fiq_msg      =       "FIQ",0
+ts_bxc_msg      =       &85,"@",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+        ALIGN
+
+
+ts_rst                                          ; Unused exception vectors
+        ADR     r4, ts_rst_msg
+        B       ts_bad_exception
+ts_uni
+        ADR     r4, ts_uni_msg
+        B       ts_bad_exception
+ts_swi
+        ADR     r4, ts_swi_msg
+        B       ts_bad_exception
+ts_pab
+        ADR     r4, ts_pab_msg
+        B       ts_bad_exception
+ts_dab_unexp
+        ADR     r4, ts_dab_msg
+        B       ts_bad_exception
+ts_aex
+        ADR     r4, ts_aex_msg
+        B       ts_bad_exception
+ts_irq
+        ADR     r4, ts_irq_msg
+        B       ts_bad_exception
+ts_fiq
+        ADR     r4, ts_fiq_msg
+        B       ts_bad_exception
+
+
+ts_bad_exception
+        SUBS    r8, r14, #8                     ; remember aborted instruction
+        BL      ts_SendText
+        ADR     r4, ts_bxc_msg                  ; display aborted address
+        BL      ts_MoreText
+        B       Reset
+
+
+;
+ts_rom_base     *       ROM                     ; Base address of the OS ROMS.
+ts_phys_mem     *       (32*1024*1024)          ; Physical Memory area.
+ts_pagemark     *       &005A0000               ; + phys page number + MEMC code.
+ts_pmark_pos    *       32                      ; Position of page mark (avoiding vectors).
+ts_cam_base     *       &3800000                ; Base address of the CAM table in MEMC.
+ts_vrest        *       &5                      ; Unused page which all pages are mapped to.
+ts_MAX_CAMS     *       512                     ; Most CAMs ever expected
+ts_memc_codes   =       &1E, &2D, &4B, &87      ; List of the memc_codes to be used.
+;
+ts_logpages                                 ; List of Logical pages.
+                 &       &0001
+                 &       &0002
+                 &       &0004
+                 &       &0008
+                 &       &0010
+                 &       &0020
+                 &       &0040
+                 &       &0080
+                 &       &0100
+                 &       &0200
+                 &       &03FF
+                 &       &03FE
+                 &       &03FD
+                 &       &03FB
+                 &       &03F7
+                 &       &03EF
+                 &       &03DF
+                 &       &03BF
+                 &       &037F
+                 &       &02FF
+                 &       &01FF
+                 &       &0000              ; Terminator for the list.
+ts_logpagesend                              ; End of the list.
+;
+;
+; Exception vectors : copied to start of each page to ensure that they will always
+; exist on page zero when arbitrary pages are mapped there.
+;
+ts_vectors
+        B       (ts_vectors-ts_start)+ts_rom_base+ts_rst
+        B       (ts_vectors-ts_start)+ts_rom_base+ts_uni
+        B       (ts_vectors-ts_start)+ts_rom_base+ts_swi
+        B       (ts_vectors-ts_start)+ts_rom_base+ts_pab
+ts_dab_vector
+        B       (ts_vectors-ts_start)+ts_rom_base+ts_dab
+        B       (ts_vectors-ts_start)+ts_rom_base+ts_aex
+        B       (ts_vectors-ts_start)+ts_rom_base+ts_irq
+        B       (ts_vectors-ts_start)+ts_rom_base+ts_fiq
+
+
+; ***************************************************************************
+;
+ts_CAM
+;
+; CAM test (full or partial)
+; Start of the CAM test, all physical pages have a copy of the vectors
+; so they may be mapped as page 0. Then each page is mapped at a series
+; of (walking 1, walking 0) logical pages and tested to be correctly
+; mapped. Other pages are set to an unused logical page by set_cam_idle
+; to prevent any CAM clashes.
+;
+; Copy the test vectors and page marker into all the pages.
+;
+        ROUT                                ; Local Branches.
+        MOV     r13, lr                     ; Preserve link register in r13.
+        BL      ts_count_CAMs               ; get log2 pagesize
+        MOV     r0, #ts_MAX_CAMS            ; r0 = last page to test
+        SUB     r0, r0, #1
+0       BL      ts_copy_vectors             ; Gosub ts_vectors.
+        SUBS    r0, r0, #&01                ; bump down to next page
+        BGE     %B0                         ; repeatuntil all pages set.
+;
+; 'C' pseudocode for the test routine.
+;
+;       for (i = &1ff; i >= 0; i--)
+;               set_cam_idle(i);               
+;
+;       find maximum page number.
+;       if (max_page != ts_count_CAMS)
+;               report CAM number error
+;
+;       for (phys = &max_page; phys >= 0; phys--) {
+;               for (logp = &logpages[0]; logp < &logpages[sizof(logpages)]; logp++) {
+;                       if (*logp == 0) {
+;                               set_cam(*logp, phys);
+;                               check_mapped(*logp, phys);
+;                       } else {
+;                               int zphys = (phys + 1) % num_pages;
+;                               set_cam(0, zphys);
+;                               set_cam(*logp, phys);
+;                               check_mapped(*logp, phys);
+;                               set_cam_idle(zphys);
+;                       }
+;               }
+;               set_cam_idle(phys);
+;       }
+;
+; Idle the pages.
+;
+        ROUT                                ; Local Branches.
+        MOV     r12, #ts_MAX_CAMS           ; always clear all 512 - just in case 4 MEMCs.
+        SUB     r12, r12, #&01              ; Subtract 1 to make max page #.
+0       MOV     r1, r12                     ; r1 = page number.
+        BL      ts_set_cam_idle
+        SUBS    r12, r12, #&01              ; bump to next page downwards
+        BGE     %B0                         ; repeatuntil page 0 done
+;
+; We need to find out what the maximum number of pages is, after running the above routine
+; all the pages will have the pagemark programed in to each page. As stated in the intro
+; programing the pages from the top down will ensure that, irrespective of the number of
+; MEMCs available, that the bottom pages are programed correctly. Therefore if we start
+; at the top, read in a page, check it's page number & memc code are correct, if so then
+; that is possibly the maximum page number. If not then subtract 1 from the page number and
+; try again until a possible good page is found.
+;
+        ROUT                                ; Local Branches.
+
+        BL      ts_count_CAMs               ; get log2 pagesize to r1  
+        MOV     r8, #ts_MAX_CAMS            ; r8= max. number of physical pages.
+0       SUBS    r8, r8, #&01                ; Subtract 1 to make it r8 - 1 Pages.
+        BEQ     ts_bad_CAM_count            ; no pages ? - shouldn't hit this!
+;
+; Calculate the expected page marker, in r4, for the current page, in r8.
+;
+        ADR     r4, ts_memc_codes           ; r4 = address of table with the memc codes.
+        LDRB    r4, [r4, r8, LSR#7]         ; r4 = Loc pointed to by r4 + (r1 >> 7).
+        ORR     r4, r8, r4, LSL #24         ; r4 = page number OR (MEMC code << 24).
+        ORR     r4, r4, #ts_pagemark        ; r4 = page id OR magic number
+;            
+; The calculated page marker is now in r4, ref_p_mark.
+; Current page in r8 - convert to physical address in r9.
+; the pagesize power-of-2 is in r1 (from ts_count_CAMs)
+;
+        MOV     r9, r8, LSL r1              ; convert PPN to phys offset
+        ORR     r9, r9, #ts_phys_mem        ; add offset to start of phys mem
+;
+; r9 now has the address of the current page - read the page marker for that page.
+;
+        LDR     r9, [r9, #ts_pmark_pos]     ; r9 = contents of loc pointed to by
+                                            ;      r9 + ts_pmark_pos.
+;
+; Check that read_p_mark is valid. 
+;
+; Either the value read is the expected pagemark, junk (no memory) or an 
+; aliased pagemark - if it's aliased, then either the memory or the MEMC 
+; isn't decoded that far.
+; Bump down and try a bit lower, until it's OK.
+;
+        CMP     r4, r9                      ; Is page-mark expected value ?
+        BNE     %B0
+
+;
+; Found a pagemarker in the proper place. Check that the number of pages that
+; appear to be present are the same as the number found by ts_count_CAMs
+; (i.e. the memory size / page size).
+;
+        SUB     r0, r0, #1              ; convert count -> max page number
+        CMPS    r0, r8
+        BNE     ts_bad_CAM_count
+;
+; If all is well, we should have the maximum usable page number in r8.
+;
+; Need to reset page 0 in the CAM entries, currently all pages are mapped to page 5.
+; We need to have logical page 0 mapped to physical page 0.
+;
+        MOV      r0, #&00                   ; r0 = &00, the page to map.
+        MOV      r1, #&00                   ; r1 = &00, the page to map to.
+        MOV      r2, #&00                   ; r2 = &00, set the protection level.
+        BL       ts_set_camp
+;
+; Check we can still see the data abort vector at physical page zero
+; - no good continuing if we can't.
+;
+        MOV     r0, #ts_phys_mem
+        LDR     r0, [r0, #(ts_dab_vector - ts_vectors)]
+        LDR     r1, ts_dab_vector
+        CMPS    r0, r1
+        BNE     ts_bad_dab_vector
+
+;
+; Now lets get on with the testing.
+;
+
+2       ADRL    r10, ts_logpages            ; logp = &logpages[0]
+
+3       LDR     r0, [r10]                   ; r0 = page to test
+        CMP     r0, #&00                    ; last entry ?
+        BNE     %F4
+        MOV     r1, r8                      ; r1 = r8, page under test
+        BL      ts_set_cam                  ; Gosub ts_set_cam.
+        LDR     r0, [r10]                   ; r0 current logical test page
+        MOV     r1, r8                      ; r1 = current test page
+        BL      ts_check_mapped             ; Gosub ts_check_mapped.
+        B       %F5
+
+4       ADD     r12, r8, #&01
+        BL      ts_count_CAMs               ; get total number of pages
+        SUB     r0,r0,#1                    ; make a mask for useable page
+        AND     r0,r0,#&7f                  ; numbers - min(128, num_pages)
+        AND     r12, r12, r0                ; r12 -> (r12 + 1) masked 
+        MOV     r0, #&00                    ; to useable page numbers.
+        MOV     r1, r12
+        BL      ts_set_cam                  ; Setup a page for vectors
+        LDR     r0, [r10]                   ; r0 = current logical test page.
+        MOV     r1, r8                      ; r1 = current physical test page.
+        BL      ts_set_cam                  ; Setup a page to test
+
+        LDR     r0, [r10]                   ; look up logical page again.
+        MOV     r1, r8                      ; recall physical page.
+        BL      ts_check_mapped             ; check the ts_set_cam worked.
+        MOV     r1, r12                     ; unmap the vector page
+        BL      ts_set_cam_idle
+
+5       ADD     r10, r10, #&04              ; next entry in test list.
+        ADRL    r0, ts_logpagesend          ; r0 = ts_logpagesend.
+        CMP     r10, r0                     ; repeat until list of logical
+        BLO     %B3                         ; pages all done.
+
+        MOV     r1, r8                      ; unmap the page we just tested
+        BL      ts_set_cam_idle
+
+        SUBS    r8, r8, #1                  ; bump phys page counter down.
+        ANDS    r8,r8,r8
+        BGE     %B2                         ; If r8 >= 0 Then branch back to 2.
+
+        ANDS    r0,r0,#0
+        MOV     pc,r13                  ; all done and passed
+
+;
+; ****************************************************************************
+;
+ts_copy_vectors 
+;
+; Copies the vectors to the physical page in r0 (preserved) also copies
+; pagemark + phypage.
+; Expects r1 (preserved) to hold log2 of pagesize
+;
+        ROUT                                ; Local Branches.
+
+        ADR     r2, ts_vectors              ; r2 = source address
+        LDMIA   r2, {r4-r11}                ; r4 - r11 = loc pointed to by r2, post inc.
+
+        MOV     r3, r0, LSL r1              ; r3 = r0 * 2**r1 .
+        ORR     r3, r3, #ts_phys_mem        ; r3 = r3 OR ts_phys_mem.
+        STMIA   r3, {r4-r11}                ; loc pointed to by r3, post inc = r4 to r11.
+;
+; find out which memc is handling the page (r0), then assign the appropiate memc_code.
+; Add in the page number and pagemark, then store into the required position in the
+; page in question.
+;
+        ADR     r2, ts_memc_codes           ; r2 = address of table with the memc codes.
+        LDRB    r2, [r2, r0, LSR#7]         ; r2 = memc code for this phys page.
+        ORR     r2, r0, r2, LSL #24         ; OR in phys page number.
+        ORR     r2, r2, #ts_pagemark        ; OR in pagemark.
+        STR     r2, [r3, #ts_pmark_pos]     ; loc pointed to by r1 + ts_pmark_pos = pagemark.
+        MOV     pc, lr                      ; Return to caller.
+;
+; ****************************************************************************
+;
+ts_set_cam_idle
+;
+; This module will program the physical page (r1) to the logical page 5, ts_vrest and
+; continue onto the next section ts_set_cam.
+;
+        ROUT                                ; Local Branches.
+        MOV     r0, #ts_vrest               ; r0 = ts_vrest, = unused logical page.
+;
+; ****************************************************************************
+;
+ts_set_cam
+;
+; This module will program the physical page (r1) to the logical page (r0) at
+; protection mode 0 and continue onto the next section ts_set_camp.
+;
+        MOV     r2, #&00                    ; r2 = &00, memory prot level 0.
+;
+; ****************************************************************************
+;
+ts_set_camp
+;
+; This module will map a range the physical pages (r1) to the logical page (r0) and
+; set the protection mode (r2). This module will return to the location from where
+; either itself or ts_set_cam or ts_set_cam_idle were called from.
+;
+; Corrupts r0,r1,r2,r3,r4,r6,r9,r11
+;
+; Calls the RISC OS routine BangCam to do the PPNO, LPNO bit switching.
+; First, jumble the registers to suit BangCam ..
+;
+; r2  = CAM entry (PPNO)
+; r3  = logical address
+; r9  = current MEMC setting (for pagesize)
+; r11 = PPL
+;
+        MOV     r3,r0           ; logical page number
+        MOV     r11,r2          ; protection level 
+        MOV     r2,r1           ; physical page number
+        MOV_fiq r0, r11_fiq     ; MEMC configuration
+        MOV     r9, r0          ; keep a copy in r9
+        MOV     r1, r9, LSR #2
+        AND     r1, r1, #3      ; calculate pagesize shift
+        ADD     r1, r1, #12
+        MOV     r3, r3, LSL r1  ; convert LPN to logaddr
+        B       BangCam         ; return thro' BangCam
+
+;
+; ****************************************************************************
+;
+ts_check_mapped
+;
+; This routine will check that the CAM has been programed correctly and that the required
+; page is responding when asked. A quick test is made to check that other pages are not 
+; responding as well.
+;
+; logical page  in r0,
+; physical page in r1,
+; test that they are the same.
+;
+; No return value : reports faults directly and returns thro' r13
+;
+; Uses (corrupts) r0,r1,r2,r3,r4,r5,r6,r7
+;
+; Find out which memc is handling the page (r1), then assign the appropiate memc_code.
+; Add in the page number and pagemark, then compare that pagemark with those found
+; in memory at the expected logical and physical addresses.
+;
+; This code assumes that any system with multiple MEMCs will always have 32K pages.
+;
+        ROUT                                ; Local Branches.
+
+        MOV     r3, r0                      ; save the current logical pagenumber.
+        MOV     r5, lr                      ; Preserve link register in case of Abort.
+        ADR     r2, ts_memc_codes           ; r2 = address of table with the memc codes.
+        LDRB    r2, [r2, r1, LSR#7]         ; fetch the memc code for this page.
+        ORR     r2, r1, r2, LSL #24         ; build the page number into the pagemark
+        ORR     r2, r2, #ts_pagemark        ; build in the pagemark magic number
+;
+; r2 should now have the page_mark for the current page (r1).
+; calculate the shift to convert page number to memory offset.
+;
+        MODE    FIQ_mode
+        MOV     r4, r11_fiq, LSR #2             ; pagesize / 4K
+        MODE    SVC_mode
+        AND     r4, r4, #3
+        ADD     r4, r4, #12
+;
+; if the mapping failed completely, the test might abort
+;
+        MOV     r6, #&00                    ; r6 = &00, clear expected abort flag.
+        MOV     r7, #&94                    ; r7 = &94, set abort expected flag.
+;
+; make the pointers and test the contents
+;
+        MOV     r0, r0, LSL r4              ; r0 = LPN * pagesize.
+        LDR     r0, [r0, #ts_pmark_pos]     ; r0 = contents of loc in r0  + ts_pmark_pos.
+        CMP     r6, #94                     ; did that fetch abort ?
+        ADREQ   r4, %F14                    ; mapping totally failed
+        BEQ     ts_CAM_fail
+        MOV     r1, r1, LSL r4              ; r1 = PPN * pagesize.
+        ORR     r1, r1, #ts_phys_mem        ; r1 = r1 ORed with ts_phys_mem.
+        LDR     r1, [r1, #ts_pmark_pos]     ; r1 = contents of loc in r1  + ts_pmark_pos.
+        CMP     r0, r1                      ; Are the read pagemarks equal ??
+        ADRNE   r4, %F10
+        BNE     ts_CAM_fail                 ; Failed : mapping not equal.
+        CMP     r0, r2                      ; 
+        ADRNE   r4, %F11
+        BNE     ts_CAM_fail                 ; Failed : map equal, but corrupt
+;
+; test that the page doesn't exist anywhere else
+;
+        MOV     r2, #1
+0       EOR     r0, r2, r3                  ; Flip a (walking) bit in the LPN.
+        CMP     r0, #ts_vrest               ; Is r0 = ts_vrest ?? Where all the pages are 
+                                            ; mapped to.
+        BEQ     %F1                         ; If r0 = ts_vrest then branch forward to 1.
+;
+; The following instruction should abort.
+;
+        MOV     r0, r0, LSL r4              ; r0 = LPN * pagesize.
+        MOV     r6, #&00                    ; r6 = &00, clear abort handled flag.
+        MOV     r7, #&94                    ; r7 = &94, set abort expected flag.
+        LDR     r0, [r0, #ts_pmark_pos]     ; get a possible pagemark from this page.
+        CMP     r6, #&94                    ; Did we go thro' the abort handler ?
+        BEQ     %F1                         ; If equal then an abort happened, good !
+;
+; Not aborted - is it page zero, where the vectors live ?
+;
+        TEQS    r2, r3
+        BEQ     %F1                        ; yes - that SHOULDN'T abort
+;
+; Fault - is the page mapped there the same as our test page ?
+;
+        CMP     r0, r1 
+        ADREQ   r4, %F12                   ; Failed : phys page also mapped here
+        ADRNE   r4, %F13                   ; Failed : page not unmapped
+        EOR     r3, r2, r3                 ; remake the duff LPN for the error display
+        B       ts_CAM_fail
+                                            ; If equal then no abort happened, not good !!
+
+1       MOV     r2, r2, LSL#1               ; bump to next-bit-set page number
+        CMP     r2, #(ts_MAX_CAMS :SHL: 1)  ; Hit number of logical pages ?
+        BLT     %B0                         ; If r2 < maximum number then loop again.
+
+        MOV     r7, #0                      ; no longer expecting aborts
+        MOV     pc, r5                      ; Return to caller.
+
+;
+;       Indicate that CAM mapping test failed (PPN is not at LPN)
+;       Display r8, the physical page number and r3, the logical page.
+;
+;    ***This error exit returns to the CALLER of check_mapped, thro' r13***
+;
+
+10
+        =       "CAM map",&88,&ff,&ff,&ff,".",&ff,&ff,&ff,&ff,0
+11
+        =       "CAM pmk",&88,&ff,&ff,&ff,".",&ff,&ff,&ff,&ff,0
+12
+        =       "CAM als",&88,&ff,&ff,&ff,".",&ff,&ff,&ff,&ff,0
+13
+        =       "CAM unm",&88,&ff,&ff,&ff,".",&ff,&ff,&ff,&ff,0
+14
+        =       "CAM abo",&88,&ff,&ff,&ff,".",&ff,&ff,&ff,&ff,0
+
+        ALIGN
+
+
+ts_CAM_fail
+        MOV     r0, r8, LSL #16         ; physical page #
+        LDR     r1, =&ffff
+        AND     r1, r1, r3
+        ORR     r0, r0, r1              ; add logical page #
+        MOV     r8, r0, LSL #4
+        MOV     r6, #0                  ; no longer expecting aborts
+        ORRS    r0, r0, #1
+        MOV     pc, r13
+
+;
+; **************************************************************************
+;
+
+; Routine to return expected number of physical pages in r0. 
+; Uses memory size determination from r10_fiq and page mode from r11_fiq.
+; Returns pagesize as power-of-two in r1, for pagenumber->address calcs.
+
+ts_count_CAMs
+
+        MODE    FIQ_mode
+        MOV     r0,r10_fiq,LSR #12      ; get values determined 
+        MOV     r1,r11_fiq,LSR #2       ; by MemSize
+        MODE    SVC_mode
+
+        AND     r1,r1,#3                ; memory / pagesize
+        MOV     r0,r0,LSR r1
+        ADD     r1,r1,#12               ; page bit-shift value
+
+        MOVS    pc,lr
+
+
+;
+; **************************************************************************
+;
+        ROUT
+
+;       Indicate that an unexpected number of CAM pages were found.
+;
+;       Display as "CAM ##    eee.fff"
+;
+;       where eee is the expected maximum page number (r0), fff is the number
+;       of of the highest page actually found (r8).
+
+0
+        =       "CAM ##",&89,&ff,&ff,&ff,".",&ff,&ff,&ff,0
+        ALIGN
+
+ts_bad_CAM_count
+        ADD     r8, r8, r0, LSL #12
+        MOV     r8, r8, LSL #8
+        ADR     r4, %B0
+        ORRS    r0, r0 ,#1
+        MOV     pc, r13
+;
+; **************************************************************************
+;
+
+;       Indicate that the DAB vector wasn't visible in physmem
+
+0
+        =       "CAM vec",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+        ALIGN
+
+ts_bad_dab_vector
+        ADR     r4, %B0
+        EOR     r8,r0,r1                ; indicate which bits are lost
+        ORRS    r0, r0, #1
+        MOV     pc, r13
+;
+; **************************************************************************
+
+;       Routine to indicate that an unexpected abort was found.
+
+0
+        =       "DAB @",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff, 0
+        ALIGN
+
+ts_unxvect
+        ADR     r4, %B0
+        SUBS    r8, r14_svc, #8         ; indicate the aborting instruction
+        BL      ts_SendText
+        ORRS    r0, r0, #1
+        MOV     pc, r13
+
+
+
+        LTORG
+
+ END
diff --git a/OldTestSrc/Mem5 b/OldTestSrc/Mem5
new file mode 100644
index 0000000000000000000000000000000000000000..607a8b8e1d1c47c9ac1c027c522a3acc14133554
--- /dev/null
+++ b/OldTestSrc/Mem5
@@ -0,0 +1,316 @@
+;>MEM5D_SCR
+;
+; RISC OS 2+ BOOT TEST SOFTWARE.
+; MEMORY TEST 5 VERSION D.      BRIAN RICE 10-01-90.
+; 04-Apr-90     ArtG    0.1     Use memory size to determine page count
+; 11-Apr-90	ArtG	0.2	Changes to permit use of BangCam
+;
+; This file will be called by MEM6x_SCR for the purposes of assembly.
+; This file requires the assembly of MEM4x_SCR to be perfromed at the
+; same time. The program will call the cam setting routines in the cam
+; test program.
+;
+; This file will test MEMCs ability to assert its protection over
+; logical pages.
+; The test code for this test was taken from the A680 test code.
+; The Arm CPU has three mode of operation, Supervisor, Operating System.
+; and User. Most of the time the machine will operate in user mode, in this.
+; mode the designers do not want the user to have full access to the memory.
+; map, therefore the MEMC(s) will check that the CPU has the appropiate
+; level of authorisation to access specific area of memory.
+; User mode is the lowest mode, allowing limited R/W access to the ram.
+; Operating System is next up the list and is allowed some more access to
+; to the ram than user mode.
+; Supervisor mode this is the highest and the CPU has unlimited access to
+; the entire memory map. 
+;
+; This version has the "my abort" routine in it not the ts_dab_exp0..5 routine as
+; coded from the A680 code.
+;
+; Set up some variables.
+;
+ts_wks_word     *   36                      ; Offset of word for workspace.
+;
+; ****************************************************************************
+;
+ts_memc_prot
+;
+; This module will map and assign protection mode 0 to all the pages. The
+; module will then perfrom a read and write operations in supervisor and
+; user modes. This is repeated for the three (four) protection modes.
+; The module will check after every protection mode level that the required
+; responses have been returned.
+;
+; Set up the memory, map and assign protection mode 0.
+;
+        ROUT                                ; Local Branches.
+        MOV     r13, lr                     ; Preserve the link register.
+        MOV     r12, #&00                   ; r12 = The physical page to test.
+
+0       ADD     r8, r12, #&01               ; Get a page to use as vectors,
+	BL	ts_count_CAMs		    ; get total number of pages
+	SUB	r0,r0,#1 		    ; make a mask for useable page
+	AND	r0,r0,#&7f		    ; numbers - min(128, num_pages)
+        AND     r8, r8, r0
+
+        MOV     r1, r8                      ; r1 = r8,  r1 = physical page 0.
+        MOV     r0, #&00                    ; r0 = &00, r0 = logical page 0.
+        BL      ts_set_cam                  ; Gosub ts_set_cam, set the CAM up.
+;
+; Set protection mode 0 and test that page.
+;
+        MOV     r2, #&00                    ; r2 = &00, r2 = protection mode 0.
+        BL      ts_mem_prot                 ; Gosub ts_mem_prot.
+        CMP     r3,#&0F                     ; Is r3 = &0F ? r3 = Super Read/Write ok.
+                                            ;                    O/S   Read/Write ok.
+                                            ;                    User  Read/Write ok.
+	MOV	r2, #0
+        BNE     ts_prot_fail                ; If r3 <> &0F Then branch to fail routine.
+;
+; Set protection mode 1 and test that page.
+;
+        MOV     r2, #&01                    ; r2 = &01, r2 = protection mode 1.
+        BL      ts_mem_prot                 ; Gosub ts_mem_prot.
+	[ CPU_Type = "ARM600"
+	CMP	r3,#&0f			    ; no ABORT line to ARM600
+	|
+	CMP     r3,#&0B                     ; Is r3 = &0B ? r3 = Super Read/Write ok.
+	]                                   ;                    O/S   Read/Write ok.
+                                            ;                    User  Read only ok.
+
+	MOV	r2,#1
+        BNE     ts_prot_fail                ; If r3 <> &0B Then branch to fail routine.
+;
+; Set protection mode 2 and test that page.
+;
+        MOV     r2, #&02                    ; r2 = &02, r2 = protection mode 2.
+        BL      ts_mem_prot                 ; Gosub ts_mem_prot.
+	[ CPU_Type = "ARM600"
+	CMP	r3,#&0f			    ; no ABORT line to ARM600
+	|
+        CMP     r3,#&03                     ; Is r3 = &03 ? r3 = Super Read/Write ok.
+	]                                   ;                    O/S   Read only ok.
+                                            ;                    User  No Access ok. 
+	MOV	r2,#2
+        BNE     ts_prot_fail                ; If r3 <> &03 Then branch to fail routine.
+;
+; Set protection mode 3 and test that page.
+;
+        MOV     r2, #&03                    ; r2 = &03, r2 = protection mode 3.
+        BL      ts_mem_prot                 ; Gosub ts_mem_prot.
+	[ CPU_Type = "ARM600"
+	CMP	r3,#&0f			    ; no ABORT line to ARM600
+	|
+        CMP     r3, #&03                    ; Is r3 = &03 ? r3 = Super Read/Write ok.
+	]                                   ;                    O/S   Read only ok.
+                                            ;                    User  No Access ok. 
+	MOV	r2,#3
+        BNE     ts_prot_fail                ; If r3 <> &03 Then branch to 
+                                            ; fail routine.
+;
+; Reset the page used to idle.
+;
+        MOV     r0, r12                     ; r0 = r12, idle the pages 
+                                            ; being used.
+        BL      ts_set_cam_idle             ; Gosub ts_set_cam_idle.
+        MOV     r0, r8                      ; r0 = r8, idle the pages 
+                                            ; being used. 
+        BL      ts_set_cam_idle             ; Gosub ts_set_cam_idle.
+;
+; Increment the physical page counter and check that all the pages are 
+; done, else finish.
+;
+        BL      ts_count_CAMs
+        ADD     r12, r12, #&01              ; do the next physical page.
+        CMP     r12, r0                     ; Done all pages ?
+        BLT     %B0                         ; If r12 <= cam_entries, 
+                                            ; branch back to 0.
+
+        ANDS    r0, r0, #0                  ; set zero flag : test passed
+        MOV     pc, r13                     ; Return to caller.
+;
+; **************************************************************************
+;
+; Branch here when ts_memc_prot fails to get the proper result from
+; ts_mem_prot.
+;
+; At this point, 
+;                 
+; r3  is a map of permitted ops (user read, user write, sys read, sys write) 
+; r2  is the memc protection mode
+; r12 is the physical page number.
+;
+; This is displayed as :   
+;
+;       PPL bad l.a.pppp
+;
+; where l is the PPL set on that page (0, 1, 2 or 3)
+;       a is a bitmap of the actual operations permitted (ur.uw.or.ow)
+;       p is the physical page number tested
+;
+
+0
+        =       "PPL bad",&88,&ff,".",&ff,".",&ff,&ff,&ff,&ff,0
+        ALIGN
+
+ts_prot_fail
+        AND     r2, r2, #&0f
+        MOV     r0, r2, LSL #20          ; mode bits
+        AND     r3, r3, #&0f
+        ORR     r0, r0, r3, LSL #16     ; permitted ops bits
+        BIC     r12, r12, #&ff000000
+        BIC     r12, r12, #&ff0000
+        ORR     r0, r0, r12             ; current page number
+
+
+        ADR     r4, %B0                 ; get fail message  
+        MOV     r8, r0, LSL #8          ; shift number to suit ts_SendText
+        ORRS    r0, r0, #1              ; fail flag
+        MOV     pc, r13
+
+
+;
+;
+; This section will test that the physical page referenced in r12 at the set 
+; protection mode. During the operation of this module, aborts are expected to happen.
+; The aborts are handled by the routine ts_dab.
+;
+; The system is running in supervisor mode and thus to check the user mode read / writes
+; the address translator flag is used. The CPU has a signal called -TRANS which when used
+; with MEMC forces the an address translation to be performed, this is not done whilst
+; in supervisor mode because it has unlimited access to the memory map. The address
+; translator falg (T) is used with STR and LDR instructions only, the effective result of
+; adding the (T) to the opcode is to force the instruction to be executed as if the CPU
+; was in user mode, thus unauthorised accesses will cause an abort to occur.
+;
+; IN:
+;       r12 - physical page.
+;       r2  - protection mode.
+; OUT:
+;       r3  - access pattern.
+;             r3 = &0F, Super Read/Write ok, O/S Read/Write ok, User Read/Write ok.
+;             r3 = &0B, Super Read/Write ok, O/S Read/Write ok, User Read only ok.
+;             r3 = &03, Super Read/Write ok, O/S Read only ok,  User No Access ok.
+;
+ts_mem_prot
+;
+; Set up data to write and read from memory.
+;
+        MOV     r10, lr                     ; Preserve link register.
+        MOV     r1, r12                     ; r1 = physical page.
+        MOV     r0, #&01                    ; r0 = logical page 1.
+        BL      ts_set_camp 
+
+        MOV     r3, #&00                    ; Initialise access pattern.
+	MOV_fiq	r5, r11_fiq		    ; get MEMC control
+	AND	r5, r5, #&C
+	ADR	r9, ts_ppl_tptrs
+	LDR	r9, [r9, r5]		    ; get test address for this pagesize
+;
+; Test 1 system mode - write.
+;
+        MOV     r6, #&00                    ; r6 = &00, clear expected abort flag.
+        MOV     r7, #&94                    ; r7 = &94, set abort expected flag.
+;
+; The following instruction may abort.
+;
+        STR     r1, [r9]                    ; Store r1 at loc pointed to by r9.
+        CMP     r6, #&00                    ; Is r6 = &00 ? If not then abort happened.
+        ORREQ   r3, r3, #&01                ; If r6 = &00, Then update r3, access pattern.
+;
+; Test 2 system mode - read.
+;
+        MOV     r6, #&00                    ; r6 = &00, clear expected abort flag.
+        MOV     r7, #&94                    ; r7 = &94, set abort expected flag.
+;
+; The following instruction may abort.
+;
+        LDR     r1, [r9]                    ; Load r1 from loc pointed to by r9.
+        CMP     r6, #&00                    ; Is r6 = &00 ? If not then abort happened.
+        ORREQ   r3, r3, #&02                ; If r6 = &00 Then update r3, access pattern.
+;
+; Test 3 user mode - write.
+;
+        MOV     r6, #&00                    ; r6 = &00, clear expected abort flag.
+        MOV     r7, #&94                    ; r7 = &94, set abort expected flag.
+;
+; The following instruction may abort.
+;
+        STRT    r1, [r9]                    ; Store r1 at loc pointed to by r9.
+        CMP     r6, #&00                    ; Is r6 = &00 ? If not then abort happened.
+        ORREQ   r3, r3, #&04                ; If r6 = &00 Then update r3, access pattern.
+;
+; Test 4 user mode - read.
+;
+        MOV     r6, #&00                    ; r6 = &00, clear expected abort flag.
+        MOV     r7, #&94                    ; r7 = &94, set expected expected flag.
+;
+; The following instruction may abort.
+;
+        LDRT    r1, [r9]                    ; Load r1 from loc pointed to by r9.
+        CMP     r6, #&00                    ; Is r6 = &00 ? If not then abort happened.
+        ORREQ   r3, r3, #&08                ; If r6 = &00 Then update r3, access pattern.
+        MOV     pc, r10                     ; Return to caller.
+
+;
+; addresses (a short way up page 1) to test PPL aborts
+;
+
+ts_ppl_tptrs
+	&	( 4 * 1024) + ts_wks_word
+	&	( 8 * 1024) + ts_wks_word
+	&	(16 * 1024) + ts_wks_word
+	&	(32 * 1024) + ts_wks_word
+;
+;
+ts_dab
+;
+; This routine provides the handling when a DATA ABORT occurs.
+; The routine will if the abort was DATA cause the program to skip over the instruction
+; that caused the abort first place.
+; Data aborts could come from a variety of sources, in this module we are only concerned
+; about a select group of aborts. This abort routine is called instead of the "usuall"
+; abort routine. All that is required from this abort routine is to set a flag to
+; indicate that an abort occured. Therefore this routine needs to be told that the
+; abort that caused the routine to be called is either one of mine or not, (expected
+; or unexpected). To achive this &94 is placed in r7. The abort routine will check
+; for the presence of &94 in r7, if present then the abort is an expected abort.
+; The abort routine will then copy r7 into r6, which is used as a flag to indicate
+; that an abort occured and that it was an expected abort. Then the routine will
+; return control to the program at the location after the instruction that caused to
+; abort to occur.
+; The return address is stored by the CPU into the link regester lr (r14), sort off.
+; It must be remembered that the PC is always 2 instructions ahead. E.G. if the
+; instruction that causes the abort is at &2000, then the lr will have &2008 in it,
+; but we want to return to the location after the abort instruction, &2004. Therefore to
+; return to the correct location &04 is removed from the lr and this is put into the pc.
+; If the abort was not expected then the routine will jump to the end and another
+; routine will show that an unexpected abort was generated.
+;
+; IN:
+;       r6 - Equals &00, cleared just before the instruction that could cause an abort.
+;       r7 - Equals &94, set just before the instruction that could cause an abort.
+;
+; OUT:
+;       r6 - Equals &94, set if an abort happened and was expected.
+;       r7 - Equals &94, preserved.
+;
+        ROUT                                ; Local Branches.
+;
+; Check that it is an expected abort and not an unexpected abort.
+;
+        CMP     r7, #&94                    ; Is r7 = &94, abort expected value.
+        BNE     ts_dab_unexp                ; If <> &94, Then branch to unexpected
+                                            ; abort handler.
+;
+; It is an expected  abort, so handle it.
+;
+        MOV     r6, r7                      ; r6 = r7, indicates that an abort happened.
+        SUB     pc, lr, #&04                ; pc = link reg - &04.
+                                            ; Skip over aborting instruction.
+                                            ; By reloading the pc we return to the area
+                                            ; of code where the abort occured but 4
+                                            ; locations further on.
+
+
+ END
diff --git a/OldTestSrc/TestMain b/OldTestSrc/TestMain
new file mode 100644
index 0000000000000000000000000000000000000000..22fcf0d1dada040ca950d51cadf2854ae7a70079
--- /dev/null
+++ b/OldTestSrc/TestMain
@@ -0,0 +1,78 @@
+; > TestMain
+
+
+; Main assembly file for isolated assembly of machine test software
+
+MEMCADR         *       &3600000
+ROM		*	&3800000
+
+ [ MEMC_Type = "IOMD"
+VideoPhysRam *  &02000000               ; Amazing - it's in the same place!
+DRAM0PhysRam *  &10000000               ; 4 DRAM banks
+DRAM1PhysRam *  &14000000
+DRAM2PhysRam *  &18000000
+DRAM3PhysRam *  &1C000000
+DRAMBaseAddressMask * &1C000000         ; used to mask off bits after stealing video RAM
+PhysSpaceSize * &20000000               ; IOMD physical map is 512M big
+PhysROM *       &00000000               ; and real ROM starts at 0
+SAMLength *     512*4                   ; SAM length in bytes for 1 bank of VRAM
+EASISpacePhys * &08000000
+EASISpace *     PhysSpace + EASISpacePhys
+ |
+VideoPhysRam *  &02000000
+PhysSpaceSize * &04000000               ; MEMC1 physical map is 64M big
+PhysROM *       &03800000
+PhysRamPhys *   &02000000               ; physical space starts here
+ ]
+
+		ORG	ROM
+
+	        GET     TestSrc/Begin
+CONT
+	        ADRL    r2,TestVIDCTAB
+		LDR	r0,=IOMD_MonitorType
+		LDR	r0,[r0]
+		ANDS	r0,r0,#IOMD_MonitorIDMask
+		ADDEQ	r2,r2,#(TestVVIDCTAB-TestVIDCTAB)
+        	MOV     r0,#ts_VIDCPhys
+08      	LDR     r1, [r2],#4
+        	CMP     r1, #-1
+        	STRNE   r1, [r0]
+        	BNE     %BT08
+
+		MOV	r9,#0
+10
+		ORR	r9,r9,#&40000000
+        	STR     r9,[r0]         ; write the border colour
+		ADD	r9,r9,#&00000005
+		ADD	r9,r9,#&00000300
+		ADD	r9,r9,#&00010000
+		AND	r9,r9,#&00ffffff
+
+		MOV	r1,#&10000
+12		ADDS	r1,r1,#(-1)
+		BNE	%BT12
+
+		B	%BT10
+
+;
+; The RISC-OS MEMC setup code is re-used to ensure similar 
+; detection of memory configuration. The MEMC1 code is modified only
+; to remove an unnecessary function.
+
+		GBLL	Module
+Module		SETL	{FALSE}
+		GBLL	AssembleSAtest
+AssembleSAtest	SETL	{FALSE}
+
+DynAreaFlags_DoublyMapped	*    1 :SHL: 6
+DynAreaFlags_NotCacheable	*    1 :SHL: 5
+DynAreaFlags_NotBufferable	*    1 :SHL: 4
+DynAreaFlags_APBits     	*   15 :SHL: 0      ; currently onl
+
+
+	        END
+
+
+
+
diff --git a/OldTestSrc/Vidc b/OldTestSrc/Vidc
new file mode 100644
index 0000000000000000000000000000000000000000..3aedb0b009aab7548ae66aa3a952f3154fdfa721
--- /dev/null
+++ b/OldTestSrc/Vidc
@@ -0,0 +1,530 @@
+; > TestSrc.VIDC
+
+        TTL RISC OS 2+ POST video controller
+;
+; The video outputs cannot be tested directly, and VIDC permits only
+; write operations on its registers. 
+; This module performs two tests to verify VIDC's operation 
+;
+;       - measure mode 0 FLBK period against IOC timer 
+;       - check that sound DMA occurs (MEMC reports DMA complete)
+;
+; This code contains timing loops affected by gross changes in processor 
+; speed, and will re-initialise MEMC with 4K pages and continous refresh.  
+;
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name            Comment
+; ----          ----            -------
+; 18-Dec-89     ArtG            Initial version
+; 04-Apr-90     ArtG            Use saved MEMC control register setting
+; 20-Jun-93     ArtG            Medusa VIDC20 / IOMD changes
+;
+;
+;------------------------------------------------------------------------
+
+
+VIDC_CLOCK_CONTROL      *       ts_S5_base :OR: &0048  ; Fox VIDC clock control
+VIDC_CLOCK_NORMAL       *       &0
+
+VIDC_VFLYWAIT           *       72000           ; 200mS timeout loop
+VIDC_SOUNDWAIT          *       40000           ; 100mS  timeout loop
+
+MEMC_Sstart             *       MEMCADR   :OR: &80000
+MEMC_SendN              *       MEMCADR   :OR: &A0000
+MEMC_Sptr               *       MEMCADR   :OR: &C0000
+MEMC_Son                *                      &00800
+
+ts_Soundbuf             *       &200            ; relative to PhysRam
+ts_Soundbuf_length      *       &400
+
+        [ VIDC_Type = "VIDC20"
+VIDSTIM0                *       &A0000000       ; base VIDC20 register values
+VIDSTIM1                *       &A1000000
+VIDSFR                  *       &B0000000
+VIDSCR                  *       &B1000005
+VIDDMAoff               *       &94000024
+VIDVCOWAIT              *       &5
+VIDVCOFREQ              *       &D0000404
+        |
+VIDSTIM0                *       &60000000       ; base VIDC register values
+VIDSTIM1                *       &64000000
+VIDSFR                  *       &C0000100
+        ]
+
+        SUBT    FLBK period test
+;
+; This test attempts to validate the video timing system by checking for
+; the proper period from the vertical flyback pulse. To make life easier, 
+; the test is performed only in mode 0 - i.e a 20mS period.
+;
+; This test contains a processor-clock timed loop as an outer limit :
+; it assumes that the processor will never run more than a factor of ten
+; faster than an 8Mhz ARM2.
+; This is valid provided that this code isn't run with an ARM3 cache enabled.
+; 
+
+; Initialise video clock control (for FOX)
+; Initialise VIDC
+; Clear IR interrupt request in IOC
+; Poll IOC until IR appears (if ever)
+; Set IOC timer 0 to 32 mS
+; Clear IR interrupt request in IOC
+; Poll IOC until IR appears (if ever)
+; Check timer 0 has counted down 20 mS (19.8 - 20.2 mS)
+; Return zero flag set on OK, clear on test failure.
+
+
+ts_VIDC_period ROUT
+
+        ; Initialise VIDC clock and VIDC
+
+        [ VIDC_Type = "VIDC1a"
+        LDR     r3, =VIDC_CLOCK_CONTROL         ; 
+        MOV     r1, #VIDC_CLOCK_NORMAL
+        STRB    r1, [r3]
+        ]
+
+        MOV     r7, #0
+        MOV     r1, #ts_VIDCPhys
+        ADRL    r6, TestVIDCTAB
+00      LDR     r0, [r6],#4                     ; setup using main table
+        CMP     r0, #-1
+        STRNE   r0, [r1]
+        BNE     %BT00
+01      LDR     r0, [r6],#4                     ; enable DMA using 2nd table 
+        CMP     r0, #-1
+        STRNE   r0, [r1]
+        BNE     %BT01
+        
+        ; Wait for the start of a flyback period
+
+04
+        LDR     r3, =IOC
+        [ MEMC_Type = "IOMD"
+        LDR     r1, [r6]                        ; get FSIZE value from end of TestVIDCTAB
+        STR     r1, [r3, #IOMD_FSIZE]
+        ]
+        MOV     r1, #vsync_bit
+        STRB    r1, [r3, #IOCIRQCLRA]
+        LDR     r2, =VIDC_VFLYWAIT              ; long timeout loop - C 200mS
+
+05      LDRB    r1, [r3, #IOCIRQSTAA] 
+        ANDS    r1, r1,  #vsync_bit    
+        BNE     %06                    
+        SUBS    r2, r2,#1               
+        BNE     %05                   
+
+        LDR     r0,=&fffff
+        ORRS    r0, r0,r7, LSL #20              ; Failed : clear 0 flag
+        MOV     pc, r14                         ; ... and quit
+
+        ; Set up IOC timer 0
+06
+        LDR     r1, =(32 * 1000 * 2)            ; 32mS upper limit
+        STRB    r1, [r3, #Timer0LL]
+        MOV     r0, r1, LSR #8
+        STRB    r0, [r3, #Timer0LH]
+        MOV     r0, #0
+        STRB    r0, [r3, #Timer0GO]             ; start the timer
+
+        ; clear the IR and T0 bits
+
+        MOV     r0, #(vsync_bit :OR: timer0_bit)
+        STRB    r0, [r3,#IOCIRQCLRA]
+
+        ; wait for what should be a complete vflyback period
+
+10      LDR     r2, =VIDC_VFLYWAIT              ; timeout loop -  C 200 msec
+11      LDRB    r0, [r3,#IOCIRQSTAA]
+        TSTS    r0, #vsync_bit
+        BNE     %14                             ; wait for end of vsync
+
+        TSTS    r0, #timer0_bit                 ; or timer underflow
+        BNE     %13
+
+12      SUBS    r2, r2, #1                      ; or last-ditch timeout 
+        BNE     %11
+
+13      ORRS    r0, r0,#1                       ; Failed : clear 0 flag
+        MOV     r0, #0                          ; but return a zero
+        MOV     pc, r14                         ; ... and quit
+
+        ; finished in reasonable time : check against margins.
+
+14      STRB    r0, [r3, #Timer0LR]             ; latch the current count
+        LDRB    r2, [r3, #Timer0CL]
+        LDRB    r0, [r3, #Timer0CH]
+        ADD     r2, r2, r0, LSL #8
+
+        SUB     r2, r1, r2
+        MOV     r0, r2, LSR #1                  ; Vertical flyback time in uS
+
+        LDR     r1, =19800                      ; inside limits ?
+        SUBS    r2, r0, r1
+        BLE     %F20
+
+        LDR     r1, =400                        ; 19.8 -> 20.2 mS
+        CMPS    r2, r1
+        BGE     %F20
+        MOV     r1,#0                           ; OK -  0 indicates pass
+
+        ; After success using the 24MHz reference clock, select the
+        ; VCO clock (also at 24MHz) and ensure the test is passed after
+        ; a few cycles to allow the VCO to settle.
+
+20
+        [ VIDC_Type = "VIDC20"
+
+        TEQ     r7,#0                           ; if this is the first loop ..
+        BNE     %FT21
+        TEQ     r1,#0                           ; and it passed OK ..
+        BNE     %FT25
+        MOV     r2,#ts_VIDCPhys
+        LDR     r3,=VIDVCOFREQ                  ; set the vco to 24MHz
+        LDR     r4,=&E0000400                   ; and use the vco clock
+        STMIA   r2,{r3,r4}
+        MOV     r7,#VIDVCOWAIT                  ; set the vco test loop count
+        B       %BT04                           ; and run around again
+
+21      ORR     r0,r0,r7,LSL #20
+        SUBS    r7,r7,#1                        ; if all attempts now made
+        BEQ     %FT25                           ; return final result
+        TEQ     r1,#0                           ; else repeat until passed
+        BNE     %BT04
+        ]
+
+        ; return with zero flag set if timers were OK
+        ; measured time (in uS) in r0 if flyback was wrong,
+        ; bits 20+ show fail loop - 0 for refclk, 1 for vcoclk.
+
+25
+        ORRS    r1,r1,r1
+        MOV     pc, r14
+
+
+        SUBT    Sound DMA test
+;
+; This test runs the sound DMA system to prove the operation of VIDC and 
+; MEMC's sound DMA control and the operation of the SIRQ sound DMA complete
+; interrupt.
+; To avoid making a noise, set the sound muting bit on.
+;
+; Initialise MEMC sound DMA
+; Initialise VIDC sound channel
+; Initialise timer 0 and timer 1 to guard-band 10mS sound duration
+; Poll IOC until IL1 (SIRQ interrupt) becomes active
+; Check timer 0 has completed and timer 1 has not
+;
+
+ts_SIRQ_period  ROUT
+
+        ; set up MEMC to point to a buffer near the start of physical RAM,
+        ; labelled in r9_fiq by the early memory size tests (not MemSize) 
+        ; Registers are set as (address / 16)
+        ; Register bits are (register * 4) in VIDC address mask
+        ; Hence values written to MEMC + register offset + (pointer / 4)    
+
+
+        [ MEMC_Type = "IOMD"
+        MOV     r3,#IOMD_Base
+        MOV     r0,#(IOMD_DMA_C_Bit :OR: IOMD_DMA_E_Bit :OR: 16)
+        STR     r0,[r3,#IOMD_SD0CR]
+        MOV_fiq r0,r9                   ; zero the DMA buffer
+        ADD     r1,r0,#ts_Soundbuf_length
+        MOV     r2,#0
+02      STR     r2,[r0],#4
+        CMPS    r0,r1
+        BNE     %BT02
+        |
+        MOV_fiq r0,r11_fiq
+        BIC     r0, r0, #MEMC_Son        ; ensure sound DMA disabled
+
+        STR     r0, [r0]
+        LDR     r1, =(MEMC_SendN :OR: ((ts_Soundbuf + ts_Soundbuf_length) / 4))
+        STR     r1, [r1]
+        LDR     r2, =(MEMC_Sstart :OR: (ts_Soundbuf / 4))
+        STR     r2, [r2]
+        LDR     r0, =MEMC_Sptr          ; initialise Sptr and set up again ..
+        STR     r0, [r0]
+        STR     r1, [r1]
+        STR     r2, [r2]
+        ]
+
+        ; Set up VIDC for 8 channels, 10uS (/8) per sample
+
+        LDR     r0, =ts_VIDCPhys
+        [ VIDC_Type = "VIDC20"
+        LDR     r1, =VIDSCR             ; VIDC10 mode, 24Mhz clock
+        STR     r1, [r0]
+        LDR     r1, =VIDDMAoff
+        STR     r1, [r0]
+        ]
+        LDR     r1, =(VIDSTIM0 + 1)     ; channel 0 at 100% left
+        LDR     r2, =((VIDSTIM1 - VIDSTIM0) + 1)
+        MOV     r3, #7
+05      STR     r1, [r0]                ; .. up to 6 at 100% right
+        ADD     r1, r1, r2
+        SUBS    r3, r3, #1
+        BNE     %05
+        SUB     r1, r1, #4              ; finally ch7 at centre again
+        STR     r1, [r0]
+
+        LDR     r1, =(VIDSFR + 8)       ; 10uS/byte
+        STR     r1, [r0]
+
+        ; Set up the timer to limit at 20 us (10uS/sample, 1024-16 bytes => 10.08 mS)
+
+        LDR     r3, =IOC
+        LDR     r1, =(20 * 1000 * 2)        ; 20 mS upper limit
+        STRB    r1, [r3, #Timer1LL]
+        MOV     r0, r1, LSR #8
+        STRB    r0, [r3, #Timer1LH]
+
+        MOV     r0, #-1
+        STRB    r0, [r3, #IOCControl]           ; mute sound (on IOC system)
+        STRB    r0, [r3, #Timer1GO]             ; start the timer
+
+        [ MEMC_Type = "IOMD"
+        MOV     r0, #(IOMD_DMA_E_Bit :OR: 16)   ; enable the IOMD DMA
+        STR     r0, [r3,#IOMD_SD0CR]
+        MOV_fiq r0,r9                           ; set the buffer pointers
+        MOV     r4,#((ts_Soundbuf_length/2) - 16)
+        STR     r0,[r3,#IOMD_SD0CURA]
+        STR     r4,[r3,#IOMD_SD0ENDA]
+        LDR     r2,[r3,#IOMD_SD0ST]
+        ORR     r4,r4,#IOMD_DMA_S_Bit
+        STR     r0,[r3,#IOMD_SD0CURB]
+        STR     r4,[r3,#IOMD_SD0ENDB]
+        |
+        MOV_fiq r0, r11_fiq
+        ORR     r0, r0, #MEMC_Son
+        STR     r0, [r0]                        ; enable the MEMC1a DMA
+        ]
+
+        ; set long timeout, clear the IL1, T0 and T1 bits
+
+        LDR     r2, =VIDC_SOUNDWAIT             ; lastditch timeout loop
+        LDR     r0, =(timer0_bit :OR: timer1_bit) 
+        STRB    r0, [r3,#IOCIRQCLRA]
+
+
+        ; Wait until sound DMA completes (or up to about 100 mS), 
+        ; then check timers.
+
+10
+        [ MEMC_Type = "IOMD"
+        LDRB    r0,[r3, #IOMD_SD0ST]
+        AND     r0, r0, #(IOMD_DMA_O_Bit :OR: IOMD_DMA_I_Bit)
+        CMPS    r0, #(IOMD_DMA_O_Bit :OR: IOMD_DMA_I_Bit)
+        BEQ     %12
+        |
+        LDRB    r0, [r3,#IOCIRQSTAB]
+        ANDS    r0, r0, #sound_IRQ_bit
+        BNE     %12
+        ]
+        LDR     r0, [r3, #IOCIRQSTAA]
+        ANDS    r0, r0,#timer1_bit              ; timeout if timer 1 expires
+        BNE     %11
+
+        SUBS    r2, r2, #1                      ; or counter reaches zero
+        BNE     %10
+
+11      ORRS    r0, r0, #1                      ; Failed : clear 0 flag
+        MOV     r2, #0                          ; return a timeout value of 0
+        B       %15                             ; ... and quit
+
+        ; finished in reasonable time : check time remaining in t1
+        ; Time for DMA should be 10.24ms (1024 bytes at 10us/byte)
+        ; less up to the time to use the final 16-byte transfer, 160us.
+
+12      STRB    r0, [r3, #Timer1LR]             ; latch the current count
+        LDRB    r2, [r3, #Timer1CL]
+        LDRB    r0, [r3, #Timer1CH]
+        ADD     r2, r2, r0, LSL #8
+
+        SUB     r2, r1, r2
+        MOV     r2, r2, LSR #1                  ; Sound DMA time in uS
+
+        LDR     r1, =10030                      ; inside limits ?
+        SUBS    r0, r2, r1
+        BLE     %F13
+
+        LDR     r1, =260                        ; 10.03  -> 10.29 mS
+        CMPS    r0, r1
+        MOVLT   r1,#0                           ; inside limits : set Z flag
+
+13      ORRS    r1,r1,r1
+
+        ; return with zero flag set if time (in r2) was within limits
+
+15
+        [ MEMC_Type = "IOMD"
+        MOV     r0, #IOMD_DMA_C_Bit
+        STR     r0, [r3,#IOMD_SD0CR]
+        |
+        BIC     r0, r0, #MEMC_Son
+        STR     r0, [r0]
+        ]
+        MOV     r0, r2                          ; return the long time value
+        MOV     pc, r14
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+; Data tables: VIDC := mode 0, all palette black
+
+TestVIDCTAB
+
+        [ VIDC_Type = "VIDC1a"
+
+        & &00000000
+        & &04000000
+        & &08000000
+        & &0C000000
+        & &10000000
+        & &14000000
+        & &18000000
+        & &1C000000
+        & &20000000
+        & &24000000
+        & &28000000
+        & &2C000000
+        & &30000000
+        & &34000000
+        & &38000000
+        & &3C000000
+        & &40000000     ; Border -> black
+        & &44000000     ; Cursor -> black
+        & &48000000
+        & &4C000000     ; Palette programmed (avoid messy screen on reset)
+;
+; standard mode 0 setup (except display area disabled)
+;
+
+        & &807FC000
+        & &8408C000
+        & &881B0000
+        & &8C1EC000     ; HDSR
+        & &906EC000     ; HDER
+        & &94770000
+        & &9C400000
+        & &A04DC000
+        & &A4008000
+        & &A8050000     ; VBSR
+        & &AC098000     ; VDSR
+        & &B0000000     ; VDER < VDSR to disable screen DMA       B0000000
+        & &B44DC000     ; VBER
+        & &E00000B2
+;
+; Additional setup : cursor blanked, sound frequency test bit set 
+;
+        & &C0000100     ; SFR  NB. TEST BIT! - also DFlynn requested value
+        & &98258000     ; HCSR
+        & &B8004000     ; VCSR
+        & &BC400000     ; VCER
+; don't mess with the stereo image registers: sound code will set them.
+        & &FFFFFFFF     ; That's the lot
+
+;
+; Further registers to turn screen DMA on again (border all over)
+; Must have a video start register before video end register to get
+; a vertical flyback interrupt.
+;
+        & &B0494000     ; VDER > VDSR to enable screen DMA
+        & &FFFFFFFF
+        ]
+
+        [ VIDC_Type = "VIDC20"
+
+; This differs from the default RISC OS VIDCTAB in running from
+; the 24MHZ video ref clock. H register contents are increased by 50%.
+
+; Program Control Register first, to clear power-down bit
+
+        & &E0000402     ; CR: FIFO load 16 words, 1 bpp, ck/1, rclk
+        & &E0000402     ; 
+        & &B1000001     ; SCR: sound disabled (+use 24MHz clock)
+
+; Don't bother programming all 256 palette entries, we'll be here all night
+; Since we're setting up a 1 bit-per-pixel mode, just do colours 0 and 1
+
+        & &10000000     ; Palette address register = 0
+        & &00000000     ; Colour 0 = black
+        & &00000000     ; Colour 1 = black
+        & &407f7f7f     ; Border colour = grey
+        & &50000000     ; Pointer colour 1 = black
+        & &60000000     ; Pointer colour 2 = black
+        & &70000000     ; Pointer colour 3 = black
+
+; Get a stable display up so we get stable signals
+
+        & &800005F8     ; HCR  = 114 + 132 + 144 + 960 + 144 + 42
+        & &8100006A     ; HSWR = 114
+        & &820000EA     ; HBSR = 114 + 132
+        & &83000174     ; HDSR = 114 + 132 + 144
+        & &84000534     ; HDER = 114 + 132 + 144 + 960
+        & &850005CA     ; HBER = 114 + 132 + 144 + 960 + 144
+        & &860000F3     ; HCSR = HDSR
+
+        & &90000137     ; VCR  = 3 + 19 + 16 + 256 + 16 + 2
+        & &91000002     ; VSWR = 3
+        & &92000015     ; VBSR = 3 + 19
+        & &93000025     ; VDSR = 3 + 19 + 16
+        & &94000024     ; VDER = VDSR -1 to disable sceeen DMA
+        & &95000135     ; VBER = 3 + 19 + 16 + 256 + 16
+        & &96000025     ; VCSR = VDSR
+        & &97000025     ; VCER = VDSR
+
+        & &C00F1003     ; EREG = comp sync, DACs on, ereg output ext lut
+        & &D000C385     ; FSYNREG, clk = (3+1)/(5+1) * 24MHz = 16MHz
+        & &F0013000     ; DCR: bus D[31:0], Hdisc
+        & &FFFFFFFF
+
+        & &94000125     ; VDER > VDSR to enable screen DMA
+        & &FFFFFFFF
+                        ; FSIZE is one less than number of rasters in Vflyback
+        & &00000037     ; (3 + 19 + 16 + 0 + 16 + 2) - 1
+
+        ; Alternate settings for VGA monitor
+
+TestVVIDCTAB
+        & &E0000402     ; CR: FIFO load 16 words, 1 bpp, ck/1, rclk
+        & &E0000402     ; 
+        & &B1000001     ; SCR: sound disabled (+use 24MHz clock)
+
+        & &10000000     ; Palette address register = 0
+        & &00000000     ; Colour 0 = black
+        & &00000000     ; Colour 1 = black
+        & &407f7f7f     ; Border colour = grey
+        & &50000000     ; Pointer colour 1 = black
+        & &60000000     ; Pointer colour 2 = black
+        & &70000000     ; Pointer colour 3 = black
+
+        & &80000310     ; HCR  = 92 + 45 + 0 + 640 + 0 + 16
+        & &81000054     ; HSWR = 92
+        & &82000080     ; HBSR = 92 + 45
+        & &83000080     ; HDSR = 92 + 45 + 0
+        & &84000300     ; HDER = 92 + 45 + 0 + 640
+        & &85000300     ; HBER = 92 + 45 + 0 + 640 + 0
+        & &86000080     ; HCSR = HDSR
+
+        & &9000020B     ; VCR  = 2 + 32 + 0 + 480 + 0 + 11
+        & &91000001     ; VSWR = 2
+        & &92000021     ; VBSR = 2 + 32
+        & &93000021     ; VDSR = 2 + 32 + 0
+        & &94000020     ; VDER = VDSR -1 to disable sceeen DMA
+        & &95000201     ; VBER = 2 + 32 + 0 + 480 + 0
+        & &96000021     ; VCSR = VDSR
+        & &97000021     ; VCER = VDSR
+
+        & &C0051003     ; EREG = sep/inv sync, DACs on, ereg output ext lut
+        & &F0013000     ; DCR: bus D[31:0], Hdisc
+        & &FFFFFFFF
+
+        ]
+
+        END 
+ 
+
+
diff --git a/Resources/UK/Messages b/Resources/UK/Messages
new file mode 100644
index 0000000000000000000000000000000000000000..d72a9dd57c9b4cbe93a0aefa4e234f1189502f5b
Binary files /dev/null and b/Resources/UK/Messages differ
diff --git a/TestSrc/A600tlb b/TestSrc/A600tlb
new file mode 100644
index 0000000000000000000000000000000000000000..6481c8b3ae56a34333abc2a617bc3efe58a6a586
--- /dev/null
+++ b/TestSrc/A600tlb
@@ -0,0 +1,61 @@
+;
+; A600tlb
+;
+; POST procedure for checking the TLB in A600 MMU.
+;
+; for each of level 1, level 2 small-page, level 2 large-page
+;	construct page table
+; 	flush cache
+; 	start timer
+; 	for 32 addresses (with different mappings)
+;		check address mapping
+; 	save timer
+; 	for same 32 addresses
+;		check address mapping
+; 	compare test times (did 2nd test require table walk ?)
+
+
+
+
+
+Use a list of addresses that cover a good mixture of virtual addresses
+Build a page table that maps these to physical RAM addresses in various ways
+Access the addresses in such an order that the cache rotates, scrapping 
+one entry each time through the list, and loading another. So each cache
+entry gets used 31 times, then is lost.
+Choice of physical mapping should ensure that the cache entries contain
+lots of different values of page and section base addresses.
+Choice of virtual test address should ensure that cache tag varies as
+widely as posible, too.  PRBS ?
+Very widely varying values of  cache tag require that a large number
+of mappings exist .. if  these are 2-level mappings, that requires
+a lot of RAM. Page tables should be multiply-mapped.
+RISC OS puts lots of stuff below the 4M mark. Limits App space to 16M
+for backwards compatibility. Probably worth testing outside these 
+limits to ensure Gold doesn't fall over, but failure rates would be
+very low.
+
+
+
+
+;
+; POST procedure for checking access faults (was PPL test)
+;
+; for each of level 1, level 2 small-page, level 2 large-page
+;	construct page table
+;	for user, supervisor mode
+;		check address alignment fault
+;		check section translation fault
+;		check 
+;		check page translation fault
+;		for 3 domain types
+;			for 16 domains
+;	 			check access permissions
+;
+
+
+
+;
+; POST procedure for checking IDC
+;
+; 
diff --git a/TestSrc/Arm3 b/TestSrc/Arm3
new file mode 100644
index 0000000000000000000000000000000000000000..a385f75f4cbbd089f79c6ac394810fa1b4dd0109
--- /dev/null
+++ b/TestSrc/Arm3
@@ -0,0 +1,71 @@
+; > TestSrc.ARM3
+
+        TTL RISC OS 2+ POST ARM version determination
+;
+; Reads ARM3 version register, returns 0 if ARM 2 fitted.
+;
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name            Comment
+; ----          ----            -------
+; 20-Apr-89     ArtG            Initial version
+;
+;
+;------------------------------------------------------------------------
+
+A3Cid		CN	0
+A3Cfls		CN	1
+A3Cmod		CN	2
+A3Ccac		CN	3
+A3Cupd		CN	4
+A3Cdis		CN	5
+
+A3CON		CP	15
+
+
+
+ts_ARM_type
+	MOV	r13,lr
+;
+; First, set up an undefined instruction vector to catch an ARM 2 
+; (or a faulty ARM 3 ??) when the copro instruction is run.
+; Only applies on systems where ROM isn't mapped at zero.
+
+ [ CPU_Type = "ARM2" :LOR: CPU_Type = "ARM3"
+	MOV	r0,#0			; set a page at logical 0
+	MOV	r1,r0
+	BL	ts_set_cam
+	ADR	r0,ts_ARM_undefined
+	LDMIA	r0,{r2,r3}
+	MOV	r1,#4
+	STMIA	r1,{r2,r3}		; set the undefined instruction trap
+ ]
+;
+; Read ARM3C0 version I.D.
+;
+	MOV	r0, #(-1)		; should always be altered
+	MRC	A3CON,0,r0,A3Cid,A3Cid	; Read control register 0
+	MOV	r12, r0
+ [ CPU_Type = "ARM2" :LOR: CPU_Type = "ARM3"
+	MOV	r1,#0
+	BL	ts_set_cam_idle		; remove the vector page again
+ ]
+	MOVS 	r0, r12			; return the ID (0 for ARM 2)
+        MOV     pc,r13
+
+;
+; Trap to be taken when ARM 2 is fitted
+;
+
+ts_ARM_undefined
+	MOV	r0,#0
+	MOVS	pc,r14_svc
+10
+	ASSERT ((%10 - ts_ARM_undefined) / 4 = 2)
+
+
+
+
+        END 
+ 
diff --git a/TestSrc/Begin b/TestSrc/Begin
new file mode 100644
index 0000000000000000000000000000000000000000..4cc4f5bfc32c9b56c5e7b05ed5577b25ab88dc33
--- /dev/null
+++ b/TestSrc/Begin
@@ -0,0 +1,1490 @@
+; > TestSrc.Begin
+
+        TTL RISC OS 2+ Power-On Self-Test
+;
+; Startup code for RISC OS ROM Self-Test.
+;
+; Performs ROM test patterns, determines test strategy and enters
+; external or internal test code.
+;
+; A minimal set of opcodes should be used (ideally, only B, LDR and ADDS)
+; so that a processor test may be validly included in the internal test
+; sequence.
+;
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name    Rel     Comment
+; ----          ----    ---     -------
+; 23-Feb-93     ArtG    2.00    Experimental ARM 600 / Jordan mods
+; 20-Oct-93     ARTG    2.02    Changed to new conditional assembly scheme
+; 18-Nov-94     RCM     2.05    Morris changes
+;
+;------------------------------------------------------------------------
+
+; TS_STATUS should be one of :
+;
+; 'R'   RISC OS POST
+; 'S'   Standalone version (with a2 memory test instead of RISCOS)
+; 'T'   Test build - development only
+;
+
+TS_STATUS       *       "R"     ;  Medusa POST version 2.0x
+;
+TS_RELEASE      *       20
+TS_CHANGES      *       5
+
+
+                GBLL    POSTenabled
+POSTenabled     SETL    {TRUE}          ; don't permit POST for ordinary startup 
+
+ts_Rom_bits     *       21                              ; Widest ROM address
+ts_Rom_length   *       1 :SHL: ts_Rom_bits             ; Longest ROM 
+ts_highaddr_bit *       1 :SHL: 25                      ; ARM address width
+ts_Alias_bits   *       (1 :SHL: 23)                    ; I/F output bits
+ts_recover_time *       (1 :SHL: 8)                     ; inter-twiddle delay
+ts_pause_time   *       200                             ; Display pause time
+ts_S5_base      *       &3350000                        ; IO register base address
+ts_IOEB_ID      *       (ts_S5_base + &50)              ; IOE_B ASIC identification
+ts_IOEB_ident   *       &5                              ; the value found there
+ts_PCaddress    *       &3010000                        ; PC IO world base address
+ts_ReadyByte_00 *       &90                             ; signal 'Here I am' to ExtIO
+ts_BBRAM        *       &A0                             ; IIC address of clock/ram chip
+ts_RamChunk     *       &2000                           ; gap between data line tests
+ts_MaxRamTest   *       4*1024*1024                     ; Max. DRAM tested on normal reset
+ts_VIDCPhys     *       &3400000                        ; Real location of VIDC
+
+;
+; Border colours used for self-test indicators
+;
+        [ VIDC_Type = "VIDC1a"
+C_ARMOK         *       &40000000+&70C  ; testing ROM
+C_RAMTEST       *       &40000000+&C70  ; testing RAM
+C_FAULT         *       &40000000+&00F  ; failed tests
+C_PASSED        *       &40000000+&7C0  ; all passed
+C_WARMSTART     *       &40000000+&777  ; not tested
+        ]
+
+        [ VIDC_Type = "VIDC20" 
+C_ARMOK         *       &40000000+&7000C0  ; testing ROM
+C_RAMTEST       *       &40000000+&C07000  ; testing RAM
+C_FAULT         *       &40000000+&0000F0  ; failed tests
+C_PASSED        *       &40000000+&70C000  ; all passed
+C_WARMSTART     *       &40000000+&707070  ; not tested
+        ]
+
+;
+; Responses to external commands
+;
+
+ErrorCmd        *       &00FF
+
+
+;
+; Control bitmasks used to indicate results of test to RISCOS
+;
+
+R_SOFT          *       0               ; not a power-on reset
+R_HARD          *       1               ; Self-test run due to POR
+R_EXTERN        *       2               ; external tests performed
+R_TESTED        *       4               ; Self-test run due to test link
+R_MEMORY        *       8               ; Memory has been tested
+R_ARM3          *       &10             ; ARM 3 fitted
+R_MEMSKIP       *       &20             ; long memory test disabled
+R_IOEB          *       &40             ; PC-style IO controller
+R_VRAM          *       &80             ; VRAM present
+
+R_STATUS        *       &1ff            ; bits that aren't a fault
+
+R_CHKFAILBIT    *       &100            ; CMOS contents failed checksum
+R_ROMFAILBIT    *       &200            ; ROM failed checksum
+R_CAMFAILBIT    *       &400            ; CAM failed 
+R_PROFAILBIT    *       &800            ; MEMC protection failed
+R_IOCFAILBIT    *       &1000           ; IOC register test failed
+R_INTFAILBIT    *       &2000           ; Cannot clear interrupts
+R_VIDFAILBIT    *       &4000           ; VIDC flyback failure
+R_SNDFAILBIT    *       &8000           ; Sound DMA failure
+R_CMSFAILBIT    *       &10000          ; CMOS unreadable
+R_LINFAILBIT    *       &20000          ; Page zero RAM failure
+R_MEMFAILBIT    *       &40000          ; Main RAM test failure
+R_CACFAILBIT    *       &80000          ; ARM 3 Cache test failure
+;
+ [ MorrisSupport
+Kludge * 96
+ |
+Kludge * 0
+ ]
+        SUBT    Exception vectors
+;
+; These vectors are available for use while the Rom is mapped into
+; low memory addresses. The Reset vector will be copied to low RAM 
+; as part of a software reset sequence : therefore it must perform
+; a fixed operation to ensure compatibility with future versions
+; of RISC-OS.
+;
+
+Reset
+ts_start
+        $DoMorrisROMHeader
+
+ [ :LNOT: MorrisSupport
+  [ ResetIndirected
+        LDR     pc,.+ResetIndirection   ; load pc from vector at &118
+  |
+        B       ts_RomPatt + PhysROM    ; Jump to normal ROM space
+  ]
+ ]
+01
+        &       ts_Rom_length           ; gets patched by ROM builder
+02
+        &       (ts_ROM_cvectors - ROM) ; pointer to code vector table
+03
+        &       (ts_ROM_dvectors - ROM) ; pointer to data vector table
+04
+        &       (ts_ROM_bvectors - ROM) ; pointer to branch table
+        B       Reset                   ; not currently used
+        B       Reset
+        B       Reset
+
+
+ts_ROMSIZE      *       %BT01 - ts_start
+ts_CVECTORS     *       %BT02 - ts_start
+ts_DVECTORS     *       %BT03 - ts_start
+ts_BVECTORS     *       %BT04 - ts_start
+
+ ! 0, "ts_Rom_length held at ":CC::STR:(%BT01 - ROM)
+
+
+;
+; Selftest version ID
+;
+
+00
+        ASSERT  %B00 <= (ts_start + &2c + Kludge)
+        %       ((ts_start + &2c + Kludge) - %B00)
+
+ts_ID   &       ((TS_STATUS :SHL: 24) + (TS_RELEASE :SHL: 16) + TS_CHANGES)
+
+ts_ID_text
+ts_himsg 
+        =       "SELFTEST"                      ; **DISPLAY_TEXT**
+        =       &89                             ; Cursor position
+        =       TS_STATUS
+        =       ("0" + (TS_RELEASE /     10))
+        =       "."
+        =       ("0" + (TS_RELEASE :MOD: 10))
+        =       ("0" + (TS_CHANGES :MOD: 10))
+        =       0
+
+
+;
+; These vector tables permit access by the external (or downloaded) test
+; software to data and code in the POST modules.
+; Find the start of these tables through the 2nd and 3rd vectors at
+; the start of the ROM.
+;
+
+ts_ROM_dvectors
+01
+        &       ts_ID                   ; Selftest identification number
+02
+        &       (ts_ID_text - ROM)      ; Selftest identification text
+
+
+;
+; vectors ORd with these flags to assure proper mode when
+; executed by host thro' vector table.
+;
+
+ts_runflags     *       (I_bit :OR: F_bit :OR: SVC_mode)
+
+ts_ROM_cvectors
+        &       ts_RomPatt              :OR: ts_runflags
+        &       ts_User_startup         :OR: ts_runflags
+        &       ts_Self_test_startup    :OR: ts_runflags
+        &       ts_Dealer_startup       :OR: ts_runflags
+        &       ts_Forced_startup       :OR: ts_runflags
+        &       ts_GetCommand           :OR: ts_runflags
+        &       ts_Softstart            :OR: ts_runflags
+        &       ts_Hardstart            :OR: ts_runflags
+
+
+;
+; ROM branch vectors - intended primarily so downloaded programs
+; may use standard subroutines.  This table should be in a fixed place.
+;
+
+00
+        ASSERT  %B00 <= (ts_start + 128 + Kludge)
+        %       ((ts_start + 128 + Kludge) - %B00)
+
+ts_ROM_bvectors
+        B       ts_RomPatt
+        B       ts_GetCommand
+        B       ts_SendByte
+        B       ts_SendWord
+        B       ts_GetByte
+        B       ts_GetWord
+        B       ts_SendText
+        B       ts_MoreText
+        B       ts_SendLCDCmd 
+
+
+;
+; Pad out until the location of the ResetIndirection vector
+;
+
+        ASSERT  .-ROM <= ResetIndirection
+        %       ResetIndirection-(.-ROM)
+        &       ts_RomPatt-ROM+PhysROM
+
+;
+; ROM test code
+;
+; Note : the register order in ADDS ...pc.. is often critical. 
+; If we want to adjust pc, use ADDS pc,rn,pc so that the PSR is
+; rewritten with it's original value.
+; If we want to do some pc-relative arithmetic, use ADDS rn,pc,rn
+; so that the bits from PSR are NOT used in the address calculation.
+;
+
+        SUBT    Macros
+
+        MACRO
+        MODE    $mode_bits
+        TEQP    psr,#($mode_bits :OR: I_bit :OR: F_bit)
+        NOP
+        MEND
+
+        MACRO   
+        MOV_fiq $dest,$src
+        MODE    FIQ_mode
+        MOV     $dest,$src
+        MODE    SVC_mode
+        MEND
+
+        MACRO   
+        FAULT   $code
+        MODE    FIQ_mode
+        ORR     r12_fiq,r12_fiq,$code
+        MODE    SVC_mode
+        MEND
+
+        MACRO
+        M32_fiq $dest,$src,$tmp1,$tmp2
+        SetMode FIQ32_mode,$tmp1,$tmp2
+        MOV     $dest,$src
+        msr     AL,CPSR_all,$tmp2
+        MEND
+
+        MACRO
+        FAULT32 $code,$tmp
+        SetMode FIQ32_mode,$tmp
+        ORR     r12_fiq,r12_fiq,$code
+        SetMode SVC32_mode,$tmp
+        MEND
+
+;
+; Define an area of storage with the required set of data bus patterns
+; These are used both for testing the complete width of the data bus
+; during ROM pattern testing, and will provide a tidy set of patterns
+; if the reset is held, while the ARM increments addresses.
+;
+
+        SUBT    ROM Address and Data Patterns
+
+DataPatterns
+
+        GBLA    dmask
+dmask   SETA    &80000000
+
+        DCD     &FFFFFFFF               ; first two : all set
+        DCD     &0                      ;             all clear
+
+        GBLA    OldOpt                  ; don't list all the walking 
+OldOpt  SETA    {OPT}                   ; patterns
+        OPT     OptNoList
+
+        WHILE   dmask > 0               ; then for each bit
+        DCD     &$dmask                 ; set it
+        DCD     :NOT: &$dmask           ; and clear it
+dmask   SETA    dmask :SHR: 1
+        WEND
+        OPT     OldOpt
+DEnd
+
+
+        OPT     OptList
+;
+;
+; Read the ROM at a series of addresses
+; such that :   a) all the address lines are exercised individually
+;               b) all the data lines are exercised individually
+;
+; Data and address lines are exercised as walking-0 and walking-1.
+; The test is performed as a series of LDR operations to avoid using
+; a larger instruction set.
+;
+
+ts_RomPatt ROUT
+
+        ; Patterns which will exercise most of the data bus.
+        ; All are arbitrary instructions with NV execution
+
+        DCD     &F0000000               ; walking 1
+
+OldOpt  SETA    {OPT}                   ; patterns
+        OPT     OptNoList
+
+dmask   SETA    &08000000
+        WHILE   dmask > 0
+        DCD     dmask :OR: &F0000000
+dmask   SETA    dmask :SHR: 1
+        WEND 
+
+        DCD     &FFFFFFFF               ; walking 0
+
+dmask   SETA    &08000000
+        WHILE   dmask > 0
+        DCD     (:NOT: dmask) :OR: &F0000000
+dmask   SETA    dmask :SHR: 1
+        WEND 
+
+        OPT     OldOpt
+
+        ; Now some proper code :
+        ; Initialise address pointer and make MemC safe
+
+        LDR     r0,%01
+        ADD     pc,r0,pc
+01
+        &       0                       ; useful constant
+
+        [ IO_Type = "IOC-A1"            ;;!! unsafe if we execute ROM at zero
+        LDR     r1,%02
+        ADD     pc,r0,pc
+02                                      ;;!! This remaps MEMC's ROM
+        &       &E000C :OR: MEMCADR     ;;!! addressing if it hasn't
+        STR     r1,[r1]                 ;;!! already happened.
+        ]
+
+        LDR     r5,%03                  ; Load r5 with a constant which
+        ADD     pc,r0,pc                ; may be added to ROM plus a
+03                                      ; walking-zero bitmask to create
+        &       ts_Rom_length - 3       ; a valid word address in ROM.
+        LDR     r2,%04                  ; Offset from ROM start to here
+        ADD     pc,r0,pc
+04
+        &       ROM - pcfromstart   
+
+        ADD     r2,pc,r2                ; pointer to start of ROM
+        ADD     r3,r2,r0                ; pointer to start of ROM
+pcfromstart
+        ADD     r4,r2,r0                ; pointer to start of ROM
+
+        ; assembly-time loop - only 32 iterations required
+
+OldOpt  SETA    {OPT}
+
+        GBLA    doffset
+doffset SETA    DataPatterns
+        WHILE   doffset < DEnd
+
+        LDR     r0,doffset              ; walking 1 data pattern
+        LDR     r1,doffset+4            ; walking 0 data pattern 
+        LDR     r6,[r2]                 ; walking 1 address pattern
+        LDR     r6,[r3]                 ; walking 0 address pattern
+
+        [ (doffset - DataPatterns) > ((32 - ts_Rom_bits) * 8)
+        [ (doffset - DataPatterns) < (31 * 8)
+        ADD     r2,r4,r0                ; r2 = ROM + walking 1 pattern
+        ADD     r3,r4,r1                ; r3 = ROM + walking 0 pattern
+        ADD     r3,r3,r5                ; adjust to a valid address
+        ]
+        ]
+
+        OPT     OptNoList
+
+doffset SETA    doffset + 8
+        WEND
+
+        ASSERT  (. - doffset < 4095)    ; in range without barrel shift ?
+
+        OPT     OldOpt
+
+
+;
+; External interface drivers - 
+; provides entry points to send byte- and word- and string-sized objects
+; and to receive byte- and word-sized objects   
+;
+; Continue into GetCommand, which determines adapter type (or no adapter)
+; and jumps to an ExtCmd handler, ts_User_startup, ts_Forced_startup or
+; ts_Dealer_startup as appropriate.
+; 
+        B       ts_GetCommand
+
+        GET     TestSrc.ExtIO
+
+;
+; External command handlers - respond to commands given through the
+; external test interface.
+;
+
+        GET     TestSrc.ExtCmd
+
+
+        SUBT    Selftest
+;
+; There is no attached test interface. Is this a power-on reset ?
+; Addressing IOC will make MEMC1a remap the ROM to high memory if
+; it hasn't already done it, so be careful to ensure that the
+; ARM is addressing normally-addressed ROM when this code runs.
+;
+
+ts_User_startup    ROUT
+        LDR     r0,%01
+        ADD     pc,r0,pc
+01
+        &       0
+;
+; IOMD will only access the ROM until a write to IOMD has been made -
+; make this write also switch on refresh so the DRAM has a chance to
+; get running before the memory test starts.
+;
+        [ MEMC_Type = "IOMD"
+        LDR     r1,%02
+        ADD     pc,r0,pc
+02
+        &       (IOMD_Base+IOMD_VREFCR)
+        LDR     r2,%03
+        ADD     pc,r0,pc
+03
+        &       IOMD_VREFCR_REF_16
+        STR     r2, [r1,#0]
+        ]
+
+        [ POSTenabled
+        LDR     r1,%12                  ; load address of IOC IRQ register
+        ADD     pc,r0,pc
+12
+        &       IOC+IOCIRQSTAA
+
+        LDR     r1, [r1,#0]             ; Get IRQSTAA register (hence POR bit)
+        LDR     r2, %13
+        ADD     pc,r0,pc                ; Constant to shift por to bit 31
+13
+        &       por_bit :SHL: 1
+14      ADD     r1,r1,r1
+        ADDS    r2,r2,r2
+        BCC     %14                     ; loop until por_bit is at bit 31
+        ADDS    r1,r1,r1                ; then shift it into carry
+        BCC     ts_Self_test_end        ; POR bit clear - do soft reset.
+
+; it's a power-on reset, so assume we can't be in 32-bit mode
+
+        MOV_fiq r12_fiq, #R_HARD
+        B       ts_Self_test_startup
+        |
+        B       CONT                    ; if user POST disabled
+        ]
+;
+; Perform self - tests
+;
+; Any distinction between test operation for Power-up, Display-only
+; and Forced tests needs to be made between these three entry points.
+;
+
+
+; This is where tests start if a dumb test link is fitted
+; (a diode from A21 to *ROMCS, disabling the ROMs when A21 is high)
+
+ts_Forced_startup  ROUT
+
+        MOV_fiq r12_fiq, #R_TESTED
+        B       ts_Self_test_startup
+
+; This is where the tests start if an external display adapter is fitted
+
+ts_Dealer_startup  ROUT
+
+        MOV_fiq r12_fiq, #R_EXTERN
+
+        LDR     r4,%FT02                ; make a pointer to signon string
+01      ADD     r4,pc,r4
+        ADD     pc,r0,pc
+02      
+        &       (ts_himsg - %BT01 - 8)
+
+        ADD     r14,pc,r0               ; make a return address for this 'call'
+        ASSERT  (.+4 = ts_Self_test_startup)    ; PC must point there already !
+        B       ts_SendText
+
+ts_Self_test_startup ROUT
+
+; This is where the power-on test starts (every user gets this)
+
+
+;
+; Processor test would go here .... if there was one.
+;
+
+ [ MorrisSupport
+;
+; On startup Morris defaults to dividing all its clocks by two and
+; accessing ROM at the slowest possible access speed. So check for
+; Morris and set to sensible values.
+;
+        MOV     r2, #IOMD_Base
+
+        LDRB    r0, [r2, #IOMD_ID0]
+        CMP     r0, #&98
+        LDRB    r0, [r2, #IOMD_ID1]
+        CMPEQ   r0, #&5B
+        BNE     %FT10
+
+;
+; PSwindell wants all prescalers set to divide by 1
+;
+        MOV     r0, #IOMD_CLKCTL_CpuclkNormal + IOMD_CLKCTL_MemclkNormal + IOMD_CLKCTL_IOclkNormal
+        STRB    r0, [r2, #IOMD_CLKCTL] ; initialise all prescalers to div1
+;
+; Set ROM speed, take care to preserve 16-bit mode bit...
+;
+; According to RJKing on 6/5/94, Kryten will use burst mode roms: use 93nS burst, 156nS initial.
+;
+; We assume that the extension ROMs are the same access time and width as the main OS ROMS.
+;
+        LDRB    r0, [r2, #IOMD_ROMCR0]
+        AND     r0, r0, #&40            ; clear all but 16-bit mode bit
+ [ NormalSpeedROMS
+   ;Normal code
+        ORR     r0, r0, #IOMD_ROMCR_Normal + IOMD_ROMCR_156 + IOMD_ROMCR_Burst93
+                                                                ; initialise ROM speed to 156.25nS, 93.75nS burst
+        ; the fast EPROMS used for Kryten testing should be able to cope even though they aren't
+        ; burst devices
+ |
+   ;Slow ROM access for PSwindells test EPROMS. Paul requested 156nS (or slower), burst off.
+        ORR     r0, r0, #IOMD_ROMCR_Normal + IOMD_ROMCR_187 + IOMD_ROMCR_BurstOff
+
+        ! 0, "*** WARNING *** Slow ROM version ment for PSwindell"
+ ]
+        STRB    r0, [r2, #IOMD_ROMCR0]
+        STRB    r0, [r2, #IOMD_ROMCR1]         ; and do the same for extension ROMs (just in case)
+;
+10
+ ]
+;
+; From this point on we assume we can safely use all the processor
+;
+; Initialise VIDC : Sync mode 0, border covers screen
+;
+
+ts_InitVIDC
+        [ IO_Type = "IOMD"              ; If POSTbox fitted, ROM may still be mapped everywhere
+        MOV     r2,#IOMD_Base
+        MOV     r0, #IOMD_VREFCR_REF_16 ; switch on DRAM refresh
+        STR     r0, [r2, #IOMD_VREFCR]
+
+        ; choose monitor settings from ID bit 0
+        MOV     r1,#ts_VIDCPhys
+        ADRL    r2,TestVIDCTAB
+        LDR     r0,=IOMD_MonitorType
+        LDR     r0,[r0]
+        ANDS    r0,r0,#IOMD_MonitorIDMask
+        ADDEQ   r2,r2,#(TestVVIDCTAB-TestVIDCTAB)
+
+        |                               ; not IOMD
+        MOV     r1,#ts_VIDCPhys
+        ADRL    r2,TestVIDCTAB
+        ]
+
+10      LDR     r0, [r2],#4
+        CMP     r0, #-1
+        STRNE   r0, [r1]
+        BNE     %BT10
+
+        LDR     r0,=C_ARMOK     ; set initial screen colour
+        STR     r0, [r1]
+
+        B       ts_RomTest
+
+
+        LTORG
+        ROUT
+
+;
+; Calculate ROM checksum : display status and calculated checksum.
+;
+
+1
+        =       "ROM   :",0
+2
+        =       "ROM bad",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+3
+        =       "ROM size",&8A,&ff,&ff,&ff,&ff,&ff,&ff,0
+        ALIGN
+
+ts_RomTest
+        ADR     r4,%BT1
+        BL      ts_SendText
+
+        BL      ts_ROM_checksum
+        BEQ     %20
+        ADR     r4,%BT2                 ; Failed message
+        FAULT   #R_ROMFAILBIT           ; set ROM bit in r12_fiq
+        MOV     r8,r0                   ; calculated checksum
+        BL      ts_SendText
+
+        BL      ts_ROM_alias            ; Checksum failed :-
+        ADR     r4,%BT3                 ; hunt for first alias
+        MOV     r8,r0, LSL #8
+        BL      ts_SendText             ; and report it.
+20
+
+        [ IO_Type = "IOC-A1"            ; Don't use RISC OS MemSize
+                                        ; until much later - it sets up
+                                        ; the ARM600 MMU as well. 
+        B       ts_MEMCset
+
+;
+; Do MEMC setup and memory size determination (the first time).
+;
+        LTORG
+        ROUT
+
+1
+        =       "M Size :",0
+2 
+        =       "M Size",&89,&ff,&ff,&ff,&ff,".",&ff,&ff,0
+        ALIGN
+
+ts_MEMCset
+        MOV     r12,#0
+        ADR     r4,%BT1
+        BL      ts_SendText
+        LDR     r1,=(&E000C :OR: MEMCADR)       ; MemSize expects 32k page
+        STR     r1,[r1]
+        BL      MemSize
+
+;
+; MemSize returns with  r0 = page size   (now in bytes, *NOT* in MEMC control patterns), 
+;                       r1 = memory size (in bytes)  
+;                       r2 = MEMC control value
+;
+; Translate these into a number that looks like :
+;
+;                       mmmm.pp
+;
+; where mmmm is memory size in hex Kbytes, pp is page size in hex Kbytes.
+;
+        MODE    FIQ_mode                        ; Save memory size and 
+        MOV     r11_fiq,r2                      ; MEMC setup value for
+        MOV     r10_fiq,r1                      ; later use
+        MODE    SVC_mode
+
+        MOV     r8, r0, LSR #2                  ; MemSize now returns actual page size in r0
+        ADD     r8,r8,r1,LSL #6
+        ADR     r4,%BT2
+        BL      ts_SendText
+
+        ]
+
+;
+; Test data, address and byte strobe lines.
+; On MEMC systems, this calls MemSize and tests the memory that finds.
+; On IOMD systems, memory sizing is performed along with the data line
+; tests, and that result is used for address line testing.
+;
+
+        B       ts_LineTest
+
+                GBLS    tsGetMem1
+tsGetMem1       SETS    "GET TestSrc.Mem1" :CC: MEMC_Type
+                $tsGetMem1
+
+;
+; Test IOC. 
+; This shuld require vector space to work (for testing interrupts),
+; but the current version just reports the status register contents.
+; 
+; Display is    ccaabbff
+;
+;  where cc is the control register
+;        aa is IRQ status register A
+;        bb is IRQ status register B
+;        ff is FIQ status register
+;
+
+        B       ts_IOCTest
+
+        LTORG
+        ROUT
+
+        [ IO_Type = "IOMD"
+1
+        =       "IOMD  :",0
+2
+        =       "IOMD-F",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+3
+        =       "IOMD-V"
+4
+        =       &88,&ff,&ff,&ff,&ff," V.",&ff,0
+        |
+1
+        =       "IOC   :",0
+2
+        =       "IOC-F",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+3
+        =       "IOC"
+4
+        =       &88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+        ]
+        ALIGN
+
+ts_IOCTest
+        ADR     r4,%BT1
+        BL      ts_SendText
+        BL      ts_IOCreg       ; check register integrity
+        BEQ     %FT8
+        ADR     r4,%BT2
+        BL      ts_SendText     ; report if failure
+
+        FAULT   #R_IOCFAILBIT
+8
+        ADR     r4,%BT1
+        BL      ts_SendText
+        BL      ts_IOCstat
+        BEQ     %FT10           ; fail message only printed if
+        ADR     r4,%BT3         ; ID code unrecognised
+        BL      ts_SendText
+        FAULT   #R_IOCFAILBIT   ; .. and set error bit if IOMD code is wrong
+        B       %FT11   
+10
+        ADR     r4,%BT4         ; print the status value
+        BL      ts_MoreText
+11
+
+        [ IO_Type = "IOMD"
+        B       ts_CMOStest
+        |
+        B       ts_IOEBtest
+        ]
+
+        LTORG
+        ROUT
+
+;
+; Check for presence of IOEB ASIC
+; 
+
+        [ IO_Type = "IOEB"
+
+1
+        =       "IOEB  :",0
+2
+        =       "IOEB",&88,"exists",0
+
+
+        ALIGN
+
+ts_IOEBtest
+        ADR     r4,%BT1
+        BL      ts_SendText
+
+        LDR     r0,=ts_IOEB_ID                  ; read an ID register in the IOEB ASIC
+        LDRB    r0, [r0]
+        AND     r0, r0, #&f
+        CMPS    r0, #ts_IOEB_ident              ; if it looks right ( == 5) ..
+        BNE     %10
+
+        FAULT   #R_IOEB                         ; set that bit in the result word
+        ADR     r4, %BT2
+        BL      ts_SendText
+10      B       ts_CMOStest
+        ] ; IOEB IO world
+;
+; Read CMOS
+; Check the checksum, read the memory test flag.
+;
+
+1
+        =       "SRAM  :",0
+2
+        =       "SRAM-F",0
+3
+        =       "SRAM-C",&8e,&ff,&ff,0
+        ALIGN
+
+ts_CMOStest
+        ADR     r4,%BT1
+        BL      ts_SendText
+
+        [ ChecksumCMOS
+
+        LDR     r0,=(ts_BBRAM + &4000)
+        MOV     r1,#&C0                 ; Get first RAM area
+        MOV     r2,#CMOSxseed
+        BL      ts_CMOSread
+        BNE     %20
+        MOV     r2, r0
+        LDR     r0,=(ts_BBRAM + &1000)  ; Accumulate the second RAM area
+        MOV     r1,#&2F
+        BL      ts_CMOSread
+        BNE     %20
+        RSB     r2, r0, #0              ; Subtract from the checksum byte
+        LDR     r0,=(ts_BBRAM + &3F00)
+        MOV     r1,#1
+        BL      ts_CMOSread
+        BNE     %20
+        MOV     r8, r0, LSL #24
+        ANDS    r0, r0, #&FF            ; A zero result ?
+        MOV     r1, #R_CHKFAILBIT
+        ADR     r4,%BT3                 ; Report checksum failure
+        BNE     %21                     ; failed .. report error
+        ]                               ; end ChecksumCMOS
+
+        LDR     r0,=(ts_BBRAM + &FC00)  ; Read Misc1CMOS byte
+        MOV     r1,#1
+        MOV     r2,#0
+        BL      ts_CMOSread
+        BNE     %20
+        ANDS    r0,r0,#&80              ; Test the memory-test-disable bit
+        BEQ     %25     
+        FAULT   #R_MEMSKIP              ; If set, skip the memory test  
+        B       %25
+
+20
+        MOV     r1,#R_CMSFAILBIT        ; Real fault - set the fault bit
+        ADR     r4,%BT2                 ; Report fault accessing IIC 
+                                        ; (Bitmap & POST display)
+21
+        FAULT   r1
+        BL      ts_SendText             ; Report one fault or another
+25
+        B       ts_IOinit
+
+        LTORG
+        ROUT
+;
+; Initialize  various machine registers - e.g, turn off the floppy
+; drive, etc, etc.
+;
+
+1
+        =       "IOinit:",0
+        ALIGN
+
+ts_IOinit
+        ADR     r4,%BT1
+        BL      ts_SendText
+        ADRL    r2,ts_IOinitab
+10
+        LDR     r0,[r2],#4              ; Get address
+        LDR     r1,[r2],#4              ; Get initialization data
+        CMPS    r0,#(-1)
+        STRNE   r1,[r0]                 ; write to IO port
+        BNE     %10
+        B       Speedset
+;
+; Use the RISC OS MEMC setup code to guess the proper processor / memory
+; configuration. The memory speed can then be set up correctly for 
+; fastest possible working, and the memory array tested in the 
+; configuration RISC OS expects.
+;
+; Display the results of the TimeCPU test as :
+;
+;               ssss.m.r
+;
+; where ssss is the processor speed in hex kHz, 
+;       m    is 0 for MEMC, 1 for MEMC1a
+;       r    is the MEMC rom speed switch setting. 
+; 
+        ROUT
+
+1
+        =       "Speed :",0
+2
+        =       "Speed",&88,&ff,&ff,&ff,&ff,".",&ff,".",&ff,0
+
+        ALIGN
+
+Speedset
+        ADR     r4,%BT1
+        BL      ts_SendText
+
+        [ MEMC_Type = "IOMD"
+        MOV     r9,#0
+        |
+        MOV_fiq r0, r11_fiq                     ; get MEMC setup
+        MOV     r9,r0                           ; compare IOC and CPU clocks
+        ]
+
+        BL      TimeCPU
+        MOV     r0,r9
+        MOV_fiq r11_fiq,r0
+
+        MOV     r8,r7,LSL #16
+        TST     r7, #1 :SHL: 16                 ; test bit 16 of r7 : 
+        ADDNE   r8,r8,#&1000                    ; MEMC1 / MEMC1a detected
+        AND     r9,r9,#&C0                      ; get High ROM access bits
+        ADD     r8,r8,r9, LSL #2
+        ADR     r4,%BT2
+        BL      ts_SendText
+        B       RAMtest
+
+
+;
+; Long RAM test, ideally exercising all memory.
+; In order to keep boot time short, the following scheme is used :
+; 
+; Normal power-on boot - test VRAM and up to 4M of first DRAM entry
+; CMOS disable set     - test nothing
+; Test hardware fitted - test entire memory
+;
+
+        ROUT
+
+
+1
+        =       "RAM   :",0
+2
+        =       "RAM bad",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+3
+        =       &89,"skipped",0
+4
+        =       "RAM   :",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+
+
+        ALIGN
+
+RAMtest
+        ADR     r4,%BT1
+        BL      ts_SendText
+;
+; if (R_MEMSKIP && R_HARD)
+;       skip all the remaining tests
+; if (!R_LINFAILBIT) 
+;       perform the long memory test
+;
+        MOV_fiq r0,r12_fiq              ; skip this test if data line fault
+        AND     r1,r0,#(R_MEMSKIP :OR: R_HARD)  ; or the user didn't want it
+        TEQS    r1,#(R_MEMSKIP :OR: R_HARD)
+        ANDNE   r1,r1,#R_LINFAILBIT
+        TEQNE   r1,#R_LINFAILBIT
+        BNE     %12
+        ADR     r4,%BT3                 ; skipping memory test ....
+        BL      ts_MoreText 
+        B       ts_Report
+12
+        LDR     r1,=C_RAMTEST           ; doing at least part of the long memory test
+        LDR     r0,=ts_VIDCPhys         ; write the border colour
+        STR     r1,[r0]
+
+        BL      MemSize                 ; Set MMU up, mapping (some) RAM at logical address 0
+                                        ; Note that this returns with the MMU enabled,
+                                        ; the ROM remapped to it's ORGed address,
+        RSB     r4,r4,#PhysROM          ; and r4 the offset from physical to ORGed ROM addresses
+        ADD     r4,r4,#PhysSpace
+        SetMode SVC32_mode,r0           ; Must do this, as PhysSpace is outside 26 bit addressing
+        ADD     pc,pc,r4                ; Jump into the ROM at its image in PhysSpace
+        NOP                             ; this instruction skipped by pc adjustment
+
+;
+; Modify the PhysRamTable so only VRAM and the first ts_MaxRamTest of DRAM gets tested
+;
+        M32_fiq r0,r12_fiq,r1,r2        ; get the test condition flags
+
+        ANDS    r0,r0,#(R_EXTERN :OR: R_TESTED)
+        BNE     %FT16                   ; do full test if test adapter is present
+        MOV     r9,#PhysRamTable
+        ADD     r10,r9,#(PhysRamTableEnd-PhysRamTable)
+14
+        LDR     r1,[r9, #4]
+        ADD     r0,r0,r1                ; r0 = running sum of memory sizes
+        SUBS    r2,r0,#ts_MaxRamTest    ; r2 = excess over ts_MaxRamTest
+        SUBHI   r1,r1,r2                ; r1 = current size truncated
+        STRHI   r1,[r9, #4]
+        MOVHI   r0,#ts_MaxRamTest       ; truncate running sum to MaxRamTest
+
+        ADD     r9,r9,#(DRAMPhysAddrB-DRAMPhysAddrA)
+        CMPS    r9,r10
+        BNE     %BT14
+16
+        FAULT32 #R_MEMORY,r0            ; memory tests were attempted
+
+        MOV     r9,#VideoPhysAddr
+        LDR     r8,[r9]                 ; report the test address
+        ADRL    r4,%BT4
+        BL      ts_SendText
+        LDR     r0,[r9]                 ; get VRAM start address and size
+        LDR     r1,[r9,#4]
+        ADD     r0,r0,#PhysSpace
+        BL      ts_RamTest
+        BNE     %FT20                   ; failed - abort ram testing
+
+;
+; VRAM (or 1st MB of DRAM, if no VRAM fitted) looks OK - move the translation
+; table there so memory tests can proceed without smashing it.
+; 
+        MOV     r9,#PhysRamTable
+        LDR     r0,[r9,#VideoPhysAddr-PhysRamTable]     ; get address of video RAM
+        LDR     r1,[r9,#DRAMPhysAddrA-PhysRamTable]     ; get address of 1st DRAM bank
+        LDR     r3, =DRAMOffset_L2PT
+        ADD     r1, r1, r3              ; make r1 -> L2PT
+        ADD     r0, r0, r3              ; make r0 -> temporary L2PT
+        BL      ts_remap_ttab           ; copy ttab at r1 to r0 and change table base
+        
+;
+; Now run the RAM test at each DRAMPhysAddr until the end of the table or a zero entry
+; is reached. Mark tested entries by setting the PhysSpace address, so a pointer to the
+; next entry need not be kept.
+;
+18
+        MOV     r9,#DRAMPhysAddrA
+        ADD     r10,r9,#(PhysRamTableEnd-DRAMPhysAddrA)
+19
+        CMPS    r9,r10                  ; reached end of table ?
+        LDRNE   r0,[r9]
+        TSTNE   r0,r0                   ; reached unused entries ?
+        LDRNE   r1,[r9,#4]              ; or blanked-out entries ?
+        TSTNE   r1,r1
+        BEQ     %FT21                   ; .. all passed OK
+        TSTS    r0,#PhysSpace
+        ADDNE   r9,r9,#(DRAMPhysAddrB-DRAMPhysAddrA)
+        BNE     %BT19                   ; this entry done .. find the next
+
+        MOV     r8,r0                   ; report address of this block
+        ADRL    r4,%BT4
+        BL      ts_SendText
+
+        LDR     r0,[r9]                 ; start testing it
+        ADD     r0,r0,#PhysSpace
+        LDR     r1,[r9, #4]
+        STR     r0,[r9]                 ; mark block so it isn't retested
+        MOV     r2,#PhysRamTable
+        LDMIA   r2,{r3-r14}             ; save the PhysRamTable
+        STMIA   r0,{r3-r14}
+        BL      ts_RamTest
+        LDMIA   r13,{r1-r11,r14}        ; restore the PhysRamTable
+        MOV     r13,#PhysRamTable
+        STMIA   r13,{r1-r11,r14}
+        BEQ     %BT18                   ; if it passed, go look for another block
+
+20
+        FAULT32 #R_MEMFAILBIT,r2        ; failed - report fault address
+        ADRL    r4,%BT2
+        MOV     r11,r1                  ; Save failed data
+        MOV     r8,r0                   ; first failing address
+        BL      ts_SendText
+        MOV     r4,r12                  ; get fault message
+        MOV     r8,r11                  ; and fault data
+        BL      ts_SendText
+21
+
+ [ MEMM_Type = "MEMC1"
+
+;
+; Test the CAMs - for each fitted MEMC, go through all the CAM entries
+; remapping logical memory and testing against physical correspondence.
+; Then try out the protection bits in each CAM entry and various 
+; processor modes. 
+; These tests return pointers to their own fault report strings.
+;
+        B       ts_CAMtest
+        ROUT
+1
+        =       "CAMs :",0
+2
+        =       "PPLs :",0
+3
+        =       &89,"skipped",0
+        ALIGN
+
+ts_CAMtest
+        LDR     r4,=%BT1
+        BL      ts_SendText
+
+        MOV_fiq r0,r12_fiq              ; skip this test if memory fault
+        MOV     r1,#(R_LINFAILBIT :OR: R_MEMFAILBIT)
+        ANDS    r0,r0,r1
+        BEQ     %08
+        LDR     r4,=%BT3
+        BL      ts_MoreText 
+        B       %20
+
+08
+        BL      ts_CAM
+        BEQ     %10
+        BL      ts_SendText
+        FAULT   #R_CAMFAILBIT
+10
+        LDR     r4,=%BT2
+        BL      ts_SendText
+
+        MOV_fiq r0,r12_fiq              ; skip this test if memory fault
+        MOV     r1,#(R_LINFAILBIT :OR: R_MEMFAILBIT)
+        ANDS    r0,r0,r1
+        BEQ     %18
+        LDR     r4,=%BT3
+        BL      ts_MoreText 
+        B       %20
+18
+        BL      ts_memc_prot
+        BEQ     %20
+        BL      ts_SendText
+        FAULT   #R_PROFAILBIT
+20
+
+ ]
+
+;
+; After testing memory and translation, turn MMU off again before running remainder
+; of tests. This simplifies finishing up (where system must be put back into 26-bit
+; mode before initialising RISCOS) if memory tests were deselected.
+; Take care to poke the real translation table - it's been relocated to video
+; RAM during the memory tests.
+;
+
+ts_restore_physical
+        MOV     r5, pc                          ; obtain current address
+        SUB     r5, r5,#PhysSpace               ; adjust to point to unmapped version
+        MOV     r5, r5, LSR #20                 ; divide by 1MB
+        MOV     r7, r5, LSL #20                 ; r7 = physical address of base of section
+        ORR     r7, r7, #(AP_None * L1_APMult)
+        ORR     r7, r7, #L1_Section
+        MOV     r3, #VideoPhysAddr              ; find the copied translation table
+        LDR     r3, [r3]
+        ADD     r3, r3, #PhysSpace
+        ADD     r3, r3, #DRAMOffset_L1PT
+        STR     r7, [r3, r5, LSL #2]            ; store replacement entry in L1 (not U,C or B)
+
+        SetCop  r7, CR_IDCFlush                 ; flush cache + TLB just in case
+        SetCop  r7, CR_TLBFlush                 ; (data written is irrelevant)
+
+; The ROM should now be mapped at the present address less PhysSpace, which is where it
+; would be if the MMU were turned off.
+
+        MOV     r4,#PhysSpace
+        SUB     pc,pc,r4
+        NOP                             ; this instruction is skipped
+
+        MOV     r7, #MMUC_D             ; Now turn the MMU off
+        SetCop  r7, CR_Control
+
+        B       ts_VIDCtest
+
+
+;
+; The VIDC tests check vertical blanking frequency in a fixed video
+; mode and measure the time taken for sound DMA.
+;
+
+        ROUT
+
+1
+        =       "VIDC  :",0
+2
+        =       "Virq bad",&88,' ',&ff,'.',&ff,&ff,&ff,&ff,&ff,0
+3
+        =       "Sirq bad",&8B,&ff,&ff,&ff,&ff,&ff,0
+4
+        =       &8A,"Mid0 ",&ff,0
+
+        ALIGN 
+
+ts_VIDCtest
+        ADR     r4,%BT1
+        BL      ts_SendText
+        [ IO_Type = "IOMD"
+        LDR     r0,=IOMD_MonitorType    ; Indicate monitor ID bit's value
+        LDR     r0,[r0]
+        AND     r0,r0,#IOMD_MonitorIDMask
+        MOV     r8,r0,LSL #28
+        ADR     r4,%BT4
+        BL      ts_MoreText
+        ]
+
+ [ MorrisSupport
+        MOV     r3, #IOMD_Base
+
+        LDRB    r0, [r3, #IOMD_ID0]
+        CMP     r0, #&98
+        LDRB    r0, [r3, #IOMD_ID1]
+        CMPEQ   r0, #&5B                ; skip Virq test on Morris
+        BEQ     %FT10
+ ]
+
+        BL      ts_VIDC_period
+        BEQ     %10
+        ADR     r4,%B2
+        MOV     r8, r0, LSL #8
+        BL      ts_SendText             ; Display Virq fail msg
+        FAULT   #R_VIDFAILBIT
+10
+        [ IO_Type = "IOMD"
+  ; RCM thinks this is no longer needed - all IOMD's are issue two
+  ;     besides, the test takes no account of Morris reusing the number space!
+  ;      MOV     r3,#IOMD_Base           ; skip Sirq test on version 1 IOMD
+  ;      LDRB    r0,[r3,#IOMD_VERSION]
+  ;      CMPS    r0,#1
+  ;      BEQ     %FT20
+        ]       
+        BL      ts_SIRQ_period
+        BEQ     %20
+        ADR     r4,%B3
+        MOV     r8, r0, LSL #12
+        BL      ts_SendText             ; Display Sirq fail msg
+        FAULT   #R_SNDFAILBIT
+20
+        MOV     r1,#ts_VIDCPhys         ; Restore full-screen
+        ADRL    r2,TestVIDCTAB          ; border colour.
+        [ IO_Type = "IOMD"
+        LDR     r0,=IOMD_MonitorType
+        LDR     r0,[r0]
+        ANDS    r0,r0,#IOMD_MonitorIDMask
+        ADDEQ   r2,r2,#(TestVVIDCTAB-TestVIDCTAB)
+        ]
+30      LDR     r0, [r2],#4
+        CMP     r0, #-1
+        STRNE   r0, [r1]
+        BNE     %BT30
+        LDR     r0,=C_ARMOK             ; set initial screen colour
+        STR     r0, [r1]
+
+        B       ts_ARMtype_test
+
+;
+; Read the ARM3 identification register. 
+; If memory tests failed, this won't be performed since the vector
+; page must exist for error recovery on ARM2 systems.
+;
+
+        ROUT
+1
+        =       "ARM ID:",0
+2
+        =       "ARM ID",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+3
+        =       &89,"skipped",0
+
+        ALIGN
+
+ts_ARMtype_test
+
+        ADR     r4,%BT1
+        BL      ts_SendText
+
+        MOV_fiq r0,r12_fiq              ; skip this test if memory fault
+        LDR     r1,=((R_LINFAILBIT :OR: R_MEMFAILBIT) :OR: (R_CAMFAILBIT :OR: R_PROFAILBIT))
+        ANDS    r0,r0,r1
+        BEQ     %05
+        ADR     r4,%BT3
+        BL      ts_MoreText 
+        B       %08                     ; and quit
+
+05
+        BL      ts_ARM_type
+        MOVS    r8, r0                  ; ready to display ID code
+        ADR     r4,%BT2
+
+        BEQ     %FT07                   ; ARM 2 : skip cache test
+        FAULT   #R_ARM3                 ; not really a fault, just status
+07
+        BL      ts_SendText
+
+08
+        B       ts_Report
+
+
+
+;
+; Report the test results to the user
+;
+; If this was a forced test (test adapter fitted) then pause even when 
+; test passed : otherwise, pause only on error.
+;
+
+ts_passmsg
+        =       "PASS  :",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+ts_failmsg
+        =       "FAIL  :",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+
+ts_R00  &       00
+
+ts_Report       ROUT
+        MOV_fiq r7,r12_fiq              ; check for fault bits set
+        LDR     r0,=R_STATUS
+        BICS    r0,r7,r0
+
+        ADREQ   r4, ts_passmsg          ; tests passed
+        LDREQ   r9,=C_PASSED
+
+        ADRNE   r4, ts_failmsg          ; tests failed
+        LDRNE   r9,=C_FAULT          
+
+        LDR     r0,=ts_VIDCPhys         ; write the border colour
+        STR     r9,[r0]
+
+        MOV     r8,r7
+        BL      ts_SendText             ; write the message and fault code
+
+        ; if the test adapter is present, leave green screen awhile
+        ; otherwise, wait only if there's a fault.
+
+        LDR     r3,=ts_recover_time
+00      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %B00                    ; - let the adapter recover 
+                                        ; from previous bus activity
+        ADR     r2,ts_R00
+        ORR     r2,r2,#ts_Alias_bits
+        LDR     r3,[r2]
+        MOV     r2,#-1
+        ADDS    r3,r3,r2
+        BCS     ts_Report_wait
+
+        MOV_fiq r0,r12_fiq
+        LDR     r2,=R_STATUS
+        BICS    r0,r0,r2
+        BEQ     ts_Hardstart
+
+ts_Report_wait        ROUT
+
+;
+; Indicate fault found : Set the border to the fault colour and flash 
+; the disk LED, using the fault bitmap in r12_fiq to modulate the flashing.
+
+ts_oldLED_on       *       &be0000         ; assert SEL0 and INUSE
+ts_oldLED_off      *       &ff0000         ; on machines with 1772 controller
+ts_oldLEDaddr      *       (ts_S5_base :OR: &40)
+
+ts_710LED_on       *       &100000         ; assert SEL0 and MotorEN0
+ts_710LED_off      *       &110000         ; on machines with 82C710 controller
+ts_710LEDaddr      *       (ts_PCaddress :OR: (&3f2 :SHL: 2))
+
+ts_665LED_on       *       &10          ; assert SEL0 and MotorEN0
+ts_665LED_off      *       &11          ; on machines with 37665 controller
+                                        ; and Medusa low-byte I/O world
+ts_665LEDaddr      *       (ts_PCaddress :OR: (&3f2 :SHL: 2))
+
+
+01      MOV_fiq r6,r12_fiq
+        LDR     r2,=&11111111
+        LDR     r7,=(35000 * 8)         ; 1/4 second pause loop count
+
+        [ IO_Type = "IOMD"
+        LDRNE   r1,=ts_665LEDaddr       ; set up for Medusa disc address
+        MOVNE   r8,#ts_665LED_on
+        MOVNE   r9,#ts_665LED_off
+        |
+        TST     r6, #R_IOEB             ; determine original / 710 disc controller
+        LDREQ   r1,=ts_oldLEDaddr       ; set up for Archimedes disc address
+        MOVEQ   r8,#ts_oldLED_on
+        MOVEQ   r9,#ts_oldLED_off
+        LDRNE   r1,=ts_710LEDaddr       ; set up for Brisbane disc address
+        MOVNE   r8,#ts_710LED_on
+        MOVNE   r9,#ts_710LED_off
+        ]
+
+02      MOV     r0,r7
+03      SUBS    r0,r0,#1                ; pause for a 1/4 second
+        BNE     %03
+
+        MOV     r0,r8                   ; turn the LED on
+        STR     r0,[r1]
+
+        MOV     r0,r7
+04      SUBS    r0,r0,#1                ; pause for a 1/4 second
+        BNE     %04
+        ADDS    r6,r6,r6                ; if a '1' is to be written,
+        BCC     %06
+        MOV     r0,r7,LSL #1            ; then pause another 1/2 second
+05      SUBS    r0,r0,#1
+        BNE     %05
+
+06
+        MOV     r0, r9                  ; turn the LED off
+        STR     r0,[r1]
+
+;
+; Count down 32 bits. Every 4 bits, insert an extra pause to simplify
+; reading the flashes.
+;
+        ADDS    r2,r2,r2
+        BCC     %08
+        MOV     r0,r7,LSL #2            ; then pause another second
+05      SUBS    r0,r0,#1
+        BNE     %05
+08
+        ANDS    r2,r2,r2                ; all the bits displayed now ?
+        BNE     %02
+        MOV_fiq r0,r12_fiq              ; restore the faultcode bits
+
+        ANDS    r0,r0,#(R_EXTERN :OR: R_TESTED) ; If test adapter present, 
+        BNE     Reset                   ; repeat test forever
+
+        B       CONT                    ; otherwise, run RISC OS
+
+ts_Hardstart
+        MOVS    r0,#R_HARD              ; and report a hard start
+        B       CONT                    ; to RISC OS
+
+;
+; Tests skipped : fall into RISC-OS
+;
+
+ts_Self_test_end
+
+        LDR     r1,=C_WARMSTART
+        LDR     r0,=ts_VIDCPhys         ; write the border colour
+        STR     r1,[r0]
+
+ts_Softstart
+        MOVS    r0,#R_SOFT              ; soft reset indicator
+        B       CONT
+
+
+        ROUT
+
+;
+; This table consists of a series of address/data pairs for IO
+; initialization.
+; Note that these addresses are likely to be in the IO world,
+; and hence the data written is that from the MOST significant
+; 16 bits of the data bus.
+; An 'address' of -1 terminates the table.
+;
+
+ts_IOinitab
+        [ IO_Type = "IOMD"
+        |
+        & ts_S5_base :OR: &10,  &000000         ; Printer port data
+        & ts_S5_base :OR: &18,  &000000         ; FDC control & printer strobes
+        & ts_S5_base :OR: &40,  &ff0000         ; FDD select lines
+        & ts_S5_base :OR: &48,  &000000         ; VIDC clock control
+        ]
+        & (-1)
+
+
+
+
+
+;
+;
+;---------------------------------------------------------------------------
+
+        LTORG
+
+
+; Include test modules executed by call, rather than inline 
+
+        GET     TestSrc.Mem2
+        GET     TestSrc.Mem3
+        GET     TestSrc.Mem4
+        GET     TestSrc.Mem5
+        GET     TestSrc.Vidc
+        GET     TestSrc.Ioc
+        GET     TestSrc.Cmos
+        GET     TestSrc.Arm3
+
+        END
diff --git a/TestSrc/Cmos b/TestSrc/Cmos
new file mode 100644
index 0000000000000000000000000000000000000000..610193c5070a152e06e8749a75abfbfa24d333bd
--- /dev/null
+++ b/TestSrc/Cmos
@@ -0,0 +1,321 @@
+; > TestSrc.Cmos
+
+        TTL RISC OS 2+ POST battery-backed RAM access
+;
+; A function to read bytes from CMOS, for use in verifying the checksum
+; and reading memory test flag & video modes.
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name            Comment
+; ----          ----            -------
+; 05-Apr-91     ArtG            Initial version, based on IICMod.
+;
+;
+;------------------------------------------------------------------------
+;
+; in:
+;       R0 = device address          (bit 8 - 15   register address    )
+;       R1 = length of block to read
+;       R2 = initial sum value
+;
+; out:  R0 = sum of all bytes in block
+;       R1 - R13 trashed
+;  
+
+ts_CMOSread     ROUT
+
+        MOV     R13,R14
+        MOV     R8,R2                   ; initialise accumulator
+        MOV     R7,R1                   ; initialise byte counter
+        MOV     R6,R0                   ; stash register address
+        MOV     R2, #IOC
+        MOV     R0, #-1                 ; ensure timer is ticking
+        STRB    R0, [R2, #Timer0LL]     ; (nonzero in input latch)
+        STRB    R0, [R2, #Timer0LH]
+        STRB    R0, [R2, #Timer0GO]     ; load the count registers
+        BL      ts_Start
+        BEQ     %FT30                   ; check clock line toggles OK
+        AND     R0, R6, #&FE
+        BL      ts_TXCheckAck           ; transmit device address (write)
+        BVS     %FT30
+        MOV     R0, R6, LSR #8
+        BL      ts_TXCheckAck           ; write register address
+        BVS     %FT30
+        BL      ts_Start                ; Extra START bit to switch modes
+        AND     R0, R6, #&FE
+        ORR     R0, R0, #1
+        BL      ts_TXCheckAck           ; transmit device address (read)
+        BVS     %FT30
+20
+        BL      ts_RXByte               ; read byte from bus
+        ADD     R8, R8, R0              ; accumulate total
+        SUBS    R7, R7, #1              ; is it last byte ?
+        MOVNE   R0, #0                  ; no, then acknowledge with 0 bit
+        MOVEQ   R0, #1                  ; yes, then don't acknowledge
+        BL      ts_ClockData            ; but always send ack clock pulse
+        TEQ     R7, #0                  ; loop, until last byte
+        BNE     %BT20
+30
+        MOVVS   R7, #-1                 ; pass error indicator to caller
+        BL      ts_Stop
+        MOV     R0, R8
+        TEQ     R7, #0                  ; return zero flag if read OK
+        MOV     PC,R13
+
+; *****************************************************************************
+;
+;       TXCheckACK - transmit a byte and wait for slave to ACK
+;
+;  out: Trashes r0,r1,r2,r3,r4,r5,r9,r10,r11,r12
+;       V bit set on error.
+;
+
+ts_TXCheckAck ROUT
+        MOV     R12,R14
+        BL      ts_TXByte
+        BL      ts_Acknowledge
+        MOVVC   PC, R12                 ; acknowledged ok, so return
+        ORRS    PC, R12, #V_bit
+
+; *****************************************************************************
+;
+;       SetC1C0 - Set clock and data lines to values in R1 and R0 respectively
+;
+; out:  Trashes r0,r1,r2,r11
+;
+
+ts_SetC1C0 ROUT
+        MOV     R11, R14
+        BIC     R14, R14, #Z_bit                ; indicate not checking clock
+ts_SetOrCheck
+        ORR     R14, R14, #I_bit                ; disable interrupts
+        TEQP    R14, #0
+
+        ADD     R0, R0, R1, LSL #1              ; R0 := C0 + C1*2
+
+        ORR     R0, R0, #&C0                    ; make sure two test bits are
+                                                ; always set to 1 !
+        MOV     R2, #IOC
+        STRB    R0, [R2, #IOCControl]
+10
+        LDREQB  R1, [R2, #IOCControl]           ; wait for clock
+        TSTEQ   R1, #i2c_clock_bit              ; to read high
+        BEQ     %BT10
+
+        MOV     R0, #10                         ; delay for >= 10/2 microsecs
+;
+; in-line do-micro-delay to save a stack level
+;
+        STRB    R0, [R2, #Timer0LR]     ; copy counter into output latch
+        LDRB    R1, [R2, #Timer0CL]     ; R1 := low output latch
+20
+        STRB    R0, [R2, #Timer0LR]     ; copy counter into output latch
+        LDRB    R14, [R2, #Timer0CL]    ; R14 := low output latch
+        TEQ     R14, R1                 ; unchanged ?
+        MOVNE   R1, R14                 ; copy anyway
+        BEQ     %BT20                   ; then loop
+        SUBS    R0, R0, #1              ; decrement count
+        BNE     %BT20                   ; loop if not finished
+;
+; end do-micro-delay
+;
+        MOV     PC, R11
+
+; Set clock and data lines to R1 and R0 and then wait for clock to be high
+
+ts_SetC1C0CheckClock ROUT
+        MOV     R11, R14
+        ORR     R14, R14, #Z_bit                ; indicate checking clock
+        B       ts_SetOrCheck
+
+
+; *****************************************************************************
+;
+;       ClockData - Clock a bit of data down the IIC bus
+;
+; in:   R0 = data bit
+;
+; out:  Trashes r0,r1,r2,r3,r10,r11
+;
+
+ts_ClockData ROUT
+        MOV     R10,R14
+
+        MOV     R3, R0                  ; save data
+        MOV     R1, #0                  ; clock LO
+        BL      ts_SetC1C0
+
+        MOV     R1, #1                  ; clock HI
+        MOV     R0, R3
+        BL      ts_SetC1C0CheckClock
+
+; Delay here must be >= 4.0 microsecs
+
+        MOV     R1, #0                  ; clock LO
+        MOV     R0, R3
+        BL      ts_SetC1C0
+
+        MOV     PC,R10
+
+; *****************************************************************************
+;
+;       Start - Send the Start signal
+;
+; out:  Trashes r0,r1,r2,r9,r11
+;       R0 (and Z flag) indicates state of clock .. should be NZ.
+;
+
+ts_Start   ROUT
+        MOV     R9,R14
+
+        MOV     R0, #1                  ; clock HI, data HI
+        MOV     R1, #1
+        BL      ts_SetC1C0
+
+; Delay here must be >= 4.0 microsecs
+
+        MOV     R0, #0                  ; clock HI, data LO
+        MOV     R1, #1
+        BL      ts_SetC1C0
+
+; Make sure clock really is high (and not shorted to gnd)
+
+        LDRB    R3, [R2, #IOCControl]
+
+; Delay here must be >= 4.7 microsecs
+
+        MOV     R0, #0                  ; clock LO, data LO
+        MOV     R1, #0
+        BL      ts_SetC1C0
+
+        ANDS    R0, R3, #i2c_clock_bit
+        MOV     PC,R9
+
+; *****************************************************************************
+;
+;       Acknowledge - Check acknowledge after transmitting a byte
+;
+; out:  Trashes r0,r1,r2,r3,r9,r11
+;       V=0 => acknowledge received
+;       V=1 => no acknowledge received
+;
+
+ts_Acknowledge ROUT
+        MOV     R9,R14
+
+        MOV     R0, #1                  ; clock LO, data HI
+        MOV     R1, #0
+        BL      ts_SetC1C0
+
+        MOV     R0, #1                  ; clock HI, data HI
+        MOV     R1, #1
+        BL      ts_SetC1C0CheckClock
+
+; Delay here must be >= 4.0 microsecs
+
+        MOV     R2, #IOC
+        LDRB    R3, [R2, #IOCControl]   ; get the data from IOC
+
+        MOV     R0, #1                  ; clock LO, data HI
+        MOV     R1, #0
+        BL      ts_SetC1C0
+
+        TST     R3, #1                  ; should be LO for correct acknowledge
+        MOV     R3, PC
+        BICEQ   R3, R3, #V_bit          ; clear V if correct acknowledge
+        ORRNE   R3, R3, #V_bit          ; set V if no acknowledge
+        TEQP    R3, #0
+
+        MOV     PC,R9
+
+; *****************************************************************************
+;
+;       Stop - Send the Stop signal
+;
+; out:  Trashes r0,r1,r2,r9,r11
+;
+
+ts_Stop    ROUT
+        MOV     R9,R14
+
+        MOV     R0, #0                  ; clock HI, data LO
+        MOV     R1, #1
+        BL      ts_SetC1C0
+
+; Delay here must be >= 4.0 microsecs
+
+        MOV     R0, #1                  ; clock HI, data HI
+        MOV     R1, #1
+        BL      ts_SetC1C0
+
+        MOV     PC,R9
+
+; *****************************************************************************
+;
+;       TXByte - Transmit a byte
+;
+; in:   R0 = byte to be transmitted
+;
+; out:  Trashes r0,r1,r2,r3,r4,r5,r9,r10,r11
+;
+
+ts_TXByte  ROUT
+        MOV     R9, R14
+        MOV     R4, R0                  ; byte goes into R4
+        MOV     R5, #&80                ; 2^7   the bit mask
+10
+        ANDS    R0, R4, R5              ; zero if bit is zero
+        MOVNE   R0, #1
+        BL      ts_ClockData            ; send the bit
+        MOVS    R5, R5, LSR #1
+        BNE     %BT10
+        MOV     PC, R9
+
+; *****************************************************************************
+;
+;       RXByte - Receive a byte
+;
+; out:  R0 = byte received
+;       Trashes r1,r2,r3,r4,r9,r11
+;
+
+ts_RXByte  ROUT
+        MOV     R9, R14
+        MOV     R3, #0                  ; byte:=0
+        MOV     R2, #IOC
+        MOV     R4, #7
+
+        MOV     R0, #1                  ; clock LO, data HI
+        MOV     R1, #0
+        BL      ts_SetC1C0
+10
+        MOV     R0, #1                  ; pulse clock HI
+        MOV     R1, #1
+        BL      ts_SetC1C0CheckClock
+
+        LDRB    R1, [R2, #IOCControl]   ; get the data from IOC
+        AND     R1, R1, #1
+        ADD     R3, R1, R3, LSL #1      ; byte:=byte*2+(IOC?0)AND1
+
+        MOV     R0, #1                  ; return clock LO
+        MOV     R1, #0
+        BL      ts_SetC1C0
+
+        SUBS    R4, R4, #1
+        BCS     %BT10
+
+        MOV     R0, R3                  ; return the result in R0  
+        MOV     PC, R9
+
+        LTORG
+
+        END
+
+
+
+
+
+
+
+
diff --git a/TestSrc/ExtCmd b/TestSrc/ExtCmd
new file mode 100644
index 0000000000000000000000000000000000000000..963d2660804f148cac499eb1115b3119699d0fd9
--- /dev/null
+++ b/TestSrc/ExtCmd
@@ -0,0 +1,1019 @@
+; > TestSrc.ExtCmd
+
+        TTL RISC OS 2+ POST external commands
+;
+; External test commands for RISC OS ROM.
+;
+; Provides functions to read data, write data and execute code using
+; parameters from an external controlling host.
+;
+; A minimal set of opcodes should be used (ideally, only B, LDR and ADDS)
+; so that a processor test may be validly included in the internal test
+; sequence.
+;
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name            Comment
+; ----          ----            -------
+; 27-Nov-89     ArtG            Initial version
+; 06-Dec-89     ArtG            Release 0.2 for integration
+; 30-Mar-90	ArtG		Added NOPs (ADDS r0,r0,r0) after ADDS pc,..
+; 19-Apr-90	ArtG		Speedups for read/write commands.
+; 15-May-90	ArtG		Fixed multiple %13 label in ts_W_FIW
+; 22-May-90	ArtG		Fixed bugs in ts_B_MWW, ts_W_RIB
+; 18-Jun-93	ArtG		Added Arm600 control instructions
+; 1-Jul-93	ArtG		Replaced ADDS pc.. instructions with ADD pc..
+;				for compatibility with SVC32_mode. 
+;
+;------------------------------------------------------------------------
+
+
+
+;
+; All these routines use registers as follows :
+;
+;       r0  - always zero
+;       r1
+;       r2
+;       r3  - undisturbed : used as constant by I/O routine
+;       r4  - return value from I/O routine, parameter to I/O routines
+;       r5
+;       r6
+;       r7  - saved value of command byte on entry
+;       r8  - operation counter
+;       r9  - pointer to data transfer operation
+;       r10 - increment value (0, 1 or 4) to add to pointer in r9
+;       r11 - decrement constant (-1) to add to counter in r8
+;       r12 - checksum accumulator
+;       r13 - pointer to operation code
+;       r14 - return address for calls to I/O routines
+;
+
+        SUBT    External command handlers
+;
+; Called by vectoring through command_table. 
+; R4 contains command byte (including 3 option bits)
+; Get operation count
+; Get address
+; If single-word data
+;   Get data
+;   Get checksum
+;   Reply with command byte or FF
+;   Do operation
+; Else
+;   For each word
+;     Get data
+;     Do operation 
+;   Get checksum
+;   Reply with command byte or FF
+; Return by branching to GetCommand.
+
+ts_write_memory    ROUT
+
+        ADDS    r13,r0,r4               ; save the control byte
+        ADDS    r7,r0,r4
+        ADDS    r14, r0, pc             ; setup return address for ..
+        B       ts_GetWord              ; .. get operation count word
+        ADDS    r8, r0, r4              ; r8 is operation count
+        ADDS    r12,r0,r4               ; initialise checksum 
+        ADDS    r14, r0, pc
+        B       ts_GetWord              ; r9 is initial target address
+        ADDS    r9, r0, r4
+        ADDS    r12,r12,r4              ; accumulate checksum 
+        ADDS    r10,r0,r0               ; set initial constants
+        LDR     r11,%01
+	ADD     pc,pc,r0
+01
+        DCD     (0 - 1)
+
+;
+; Check for operations which don't involve reading a block of data.
+; These are acknowledged BEFORE performing the operation.
+;
+	ADDS	r0,r0,r0
+        ADDS    r13,r13,r13             ; convert operation code to vector 
+        ADDS    r13,r13,r13
+        LDR     r4, %02
+	ADD     pc,pc,r0
+02
+        &       (ts_write_cmd_table - %03)
+        ADDS    r4,pc,r4
+        ADDS    r13,r4,r13
+03
+        LDR     r13,[r13]               ; fetch pointer to code
+        LDR     r4,%04
+	ADD     pc,pc,r0
+04
+        &       (ts_write_cmd_table - ts_W_fetch_operations) 
+	ADDS	r0,r0,r0
+        ADDS    r4,r4,r13
+        BCS     ts_Write_getdata        ; defer acknowledgement till later
+
+        ; check the above test was valid, given code layout
+	; Note - this is also required by code near ts_Write_cmd_done
+
+        ASSERT  (ts_W_RSW <  ts_W_fetch_operations)
+        ASSERT  (ts_W_RSB <  ts_W_fetch_operations)
+        ASSERT  (ts_W_RIW <  ts_W_fetch_operations)
+        ASSERT  (ts_W_RIB <  ts_W_fetch_operations)
+        ASSERT  (ts_W_FSW >= ts_W_fetch_operations)
+        ASSERT  (ts_W_FSB >= ts_W_fetch_operations)
+        ASSERT  (ts_W_FIW >= ts_W_fetch_operations)
+        ASSERT  (ts_W_FIB >= ts_W_fetch_operations)
+
+;
+; Fetch the first data word and checksum, and acknowledge
+;
+
+        ADDS    r14,r0,pc               ;get next data word
+        B       ts_GetWord
+        ADDS    r12,r12,r4              ;accumulate checksum
+        ADDS    r10,r0,r4
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ;read transmitted checksum
+        ADDS    r4,r4,r12               ;tx + total should be zero
+        LDR     r5,%05
+        ADD     pc,pc,r0
+05
+        &       (0 - 1)
+        ADDS    r5,r5,r4                ;carry set on checksum failure
+        BCS     ts_cmd_error
+
+;
+; Checksum looks OK. Send the command and the checksum back.
+;
+	LDR	r4,%06
+	ADD     pc,pc,r0
+06
+	&	ts_WriteCmdByte
+        ADDS    r4,r4,r7                ;restore the original 
+
+        ADDS    r14,r0,pc
+        B       ts_SendByte
+        ADDS    r4,r0,r12               ;then send the calculated checksum
+        ADDS    r14,r0,pc
+        B       ts_SendWord
+
+        ADDS    r4,r0,r10               ;restore the data word
+        ADDS    r10,r0,r0               ;and the zero in r10
+        B       ts_Write_usedata        ;dive off to do the work
+
+;
+; Enter the main loop, repeating the operation labelled in r13.
+;
+
+ts_Write_getdata
+        ADDS    r9,r9,r10               ;perform increment operation
+        ADDS    r8,r8,r11               ;countdown repeat counter
+        BCC     ts_Write_cmd_ack
+        ADDS    r14,r0,pc               ;get next data word
+        B       ts_GetWord
+        ADDS    r12,r12,r4              ;accumulate checksum
+	B	%07
+
+ts_Write_usedata
+        ADDS    r9,r9,r10               ;perform increment operation
+ts_Write_count
+        ADDS    r8,r8,r11               ;countdown repeat counter
+        BCC     ts_Write_cmd_done
+07
+        ADD     pc,pc,r13               ;jump back to operations
+        &       0
+
+;
+; In this table, the operation after any word fetch is vectored by
+; the 3 least significant bits of the command byte to perform some 
+; combination of writing with  : 
+;
+; bit 2 -> 0    R : repeat with same data
+;          1    F : fetch more data for next operation
+;
+; bit 1 -> 0    S : leave address static
+;          1    I : increment address after operation
+;
+; bit 0 -> 0    W : word operation
+;          1    B : byte operation
+;
+
+        ASSERT  ((ts_write_cmd_table - %07) = 8)
+
+ts_write_cmd_table
+
+        DCD     (ts_W_RSW - ts_write_cmd_table)
+        DCD     (ts_W_RSB - ts_write_cmd_table)
+        DCD     (ts_W_RIW - ts_write_cmd_table)
+        DCD     (ts_W_RIB - ts_write_cmd_table)
+        DCD     (ts_W_FSW - ts_write_cmd_table)
+        DCD     (ts_W_FSB - ts_write_cmd_table)
+        DCD     (ts_W_FIW - ts_write_cmd_table)
+        DCD     (ts_W_FIB - ts_write_cmd_table)
+
+;
+; And here are the trailers that perform these operations.
+; Each is started with the data in r4, address in r9 and completes
+; by returning to Write_getdata (to read another word) or Write_usedata
+; (to repeat with the same data) with r10 = increment value (initially 0)
+;
+
+ts_W_RSW
+        STR     r4,[r9]                 ;store word, repeat address
+        ADDS    r8,r8,r11               ;countdown repeat counter
+        BCS     ts_W_RSW
+        B       ts_Write_cmd_done
+
+ts_W_RSB
+        STRB    r4,[r9]                 ;store byte, repeat address
+	ADDS	r8,r8,r11
+	BCS	ts_W_RSB
+        B       ts_Write_cmd_done
+
+ts_W_RIW
+        LDR     r10,%11
+	ADD     pc,pc,r0
+11
+        DCD     4
+12
+        STR     r4,[r9]                 ;store word, increment word address
+        ADDS    r9,r9,r10               ;perform increment operation
+        ADDS    r8,r8,r11               ;countdown repeat counter
+	BCS	%B12
+        B       ts_Write_cmd_done
+
+
+ts_W_RIB
+	LDR	r10,%13
+	ADD     pc,pc,r0
+13
+        DCD     1
+14
+        STRB    r4,[r9]                 ;store byte, increment byte address
+	ADDS	r9,r9,r10
+	ADDS	r8,r8,r11
+	BCS	%B14
+	B	ts_Write_cmd_done
+
+
+
+ts_W_fetch_operations                   ;all past here fetch new data
+                                        ;on each loop
+
+ts_W_FSW
+        STR     r4,[r9]                 ;store word, repeat address
+        B       ts_Write_getdata
+
+ts_W_FSB
+        STRB    r4,[r9]                 ;store byte, repeat address
+        B       ts_Write_getdata
+
+ts_W_FIW
+        STR     r4,[r9]                 ;store word, increment word address
+        LDR     r10,%15
+        B       ts_Write_getdata
+15
+        DCD     4
+
+ts_W_FIB
+        STRB    r4,[r9]                 ;store byte, increment byte address
+        LDR     r10,%16
+        B       ts_Write_getdata
+16
+        DCD     1 
+
+
+;
+; Operations completed. Operations that read multiple data words from
+; the host must now checksum and acknowledge the block (even though
+; it's a bit late to do anything about it)
+;
+
+ts_Write_cmd_ack
+;
+; Operation involved multiple fetches - only now ready to ACK. 
+;
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ;read transmitted checksum
+        ADDS    r4,r4,r12               ;tx + total should be zero
+        LDR     r5,%25
+        ADD     pc,pc,r0
+25
+        &       (0 - 1)
+        ADDS    r5,r5,r4                ;carry set on checksum failure
+        BCS     ts_cmd_error
+
+;
+; Checksum looks OK. Send the command and the checksum back.
+;
+	LDR	r4,%26
+	ADD     pc,pc,r0
+26
+	&	ts_WriteCmdByte
+        ADDS    r4,r4,r7                ;restore the original 
+        ADDS    r14,r0,pc
+        B       ts_SendByte
+        ADDS    r4,r0,r12               ;then send the calculated checksum
+        ADDS    r14,r0,pc
+        B       ts_SendWord
+
+ts_Write_cmd_done
+        B       ts_GetCommand
+
+
+
+; Called by vectoring through command_table. 
+; R4 contains command byte (including 3 option bits)
+; Get operation count
+; Get address
+; Reply with command byte or FF
+; Reply with checksum
+; For each word
+;   Read data
+;   If Verbose option
+;     Send data
+; If Quiet option
+;   Send result of read operation
+; Send checksum of result packet
+; Return by branching to GetCommand.
+
+ts_read_memory    ROUT
+
+        ADDS    r13,r0,r4               ; save the control byte
+        ADDS    r7,r0,r4
+
+        ADDS    r14, r0, pc             ; setup return address for ..
+        B       ts_GetWord              ; .. get operation count word
+        ADDS    r8, r0, r4              ; r8 is operation count
+        ADDS    r12,r0,r4               ; initialise checksum 
+
+        ADDS    r14, r0, pc
+        B       ts_GetWord              ; r9 is initial target address
+        ADDS    r9, r0, r4
+        ADDS    r12,r12,r4              ; accumulate checksum 
+        ADDS    r10,r0,r0               ; set initial constants
+        LDR     r11,%01
+        ADD     pc,pc,r0
+01
+        DCD     (0 - 1)
+;
+; Convert the operation options into a code pointer
+;
+	ADDS	r0,r0,r0
+        ADDS    r13,r13,r13             ; convert operation code to vector 
+        ADDS    r13,r13,r13
+        LDR     r4, %02
+        ADD     pc,pc,r0
+02
+        &       (ts_read_cmd_table - %03)
+        ADDS    r4,pc,r4
+        ADDS    r13,r4,r13
+03
+        LDR     r13,[r13]               ; fetch pointer to code
+
+;
+; Fetch the checksum, and acknowledge
+;
+
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ;read transmitted checksum
+        ADDS    r4,r4,r12               ;tx + total should be zero
+        LDR     r5,%05
+        ADD     pc,pc,r0
+05
+        &       (0 - 1)
+        ADDS    r5,r5,r4                ;carry set on checksum failure
+        BCS     ts_cmd_error
+
+;
+; Checksum looks OK. Send the command and the checksum back.
+;
+	LDR	r4,%06
+	ADD     pc,pc,r0
+06
+	&	ts_ReadCmdByte
+        ADDS    r4,r4,r7                ;restore the original 
+        ADDS    r14,r0,pc
+        B       ts_SendByte
+        ADDS    r4,r0,r12               ;then send the calculated checksum
+        ADDS    r14,r0,pc
+        B       ts_SendWord
+
+        ADDS    r12,r0,r0               ;initialise the upload checksum 
+        B       ts_Read_count		;enter the loop
+
+;
+; Enter the main loop, repeating the operation labelled in r13.
+; This loop is for operations that finish with all data sent
+
+ts_Read_Txdata                          ;send data to host
+        ADDS    r12,r12,r4              ;accumulate the checksum
+        ADDS    r14,r0,pc
+        B       ts_SendWord             ;send this word
+        ADDS    r9,r9,r10               ;perform increment operation
+        ADDS    r8,r8,r11               ;countdown repeat counter
+        BCC     ts_Read_cmd_done
+        B       %07                     ;go off to the jump handler
+
+ts_Read_count
+        ADDS    r8,r8,r11               ;countdown repeat counter
+        BCC     ts_Read_cmd_read        ;send data at finish
+07
+        ADD     pc,pc,r13               ;jump back to operations
+        &       0
+
+;
+; In this table, the operation after any word fetch is vectored by
+; the 2 least significant bits of the command byte to perform some 
+; combination of reading with  : 
+;
+; bit 2 -> 0    Q : read data without reporting it
+;          1    V : Transmit the result of every read operation
+;
+; bit 1 -> 0    S : leave address static
+;          1    I : increment address after operation
+;
+; bit 0 -> 0    W : word operation
+;          1    B : byte operation
+;
+
+        ASSERT  ((ts_read_cmd_table - %07) = 8)
+
+ts_read_cmd_table
+
+        DCD     (ts_R_QSW - ts_read_cmd_table)
+        DCD     (ts_R_QSB - ts_read_cmd_table)
+        DCD     (ts_R_QIW - ts_read_cmd_table)
+        DCD     (ts_R_QIB - ts_read_cmd_table)
+        DCD     (ts_R_VSW - ts_read_cmd_table)
+        DCD     (ts_R_VSB - ts_read_cmd_table)
+        DCD     (ts_R_VIW - ts_read_cmd_table)
+        DCD     (ts_R_VIB - ts_read_cmd_table)
+
+;
+; And here are the trailers that perform these operations.
+; Each is started with the data in r4, address in r9 and completes
+; by returning to Write_getdata (to read another word) or Write_usedata
+; (to repeat with the same data) with r10 = increment value (initially 0)
+;
+
+ts_R_QSW
+        LDR     r4,[r9]                 ;read word, repeat address
+        ADDS    r8,r8,r11               ;countdown repeat counter
+	BCS	ts_R_QSW
+        B       ts_Read_cmd_read        ;send data at finish
+
+
+ts_R_QSB
+        LDRB    r4,[r9]                 ;read byte, repeat address
+	ADDS	r8,r8,r11
+	BCS	ts_R_QSB
+        B       ts_Read_cmd_read
+
+ts_R_QIW
+        LDR     r10,%11
+	ADD     pc,pc,r0
+11
+        DCD     4
+12
+        LDR     r4,[r9]                 ;read word, increment word address
+        ADDS    r9,r9,r10               ;perform increment operation
+        ADDS    r8,r8,r11               ;countdown repeat counter
+	BCS	%B12
+        B       ts_Read_cmd_read        ;send data at finish
+
+
+ts_R_QIB
+        LDR     r10,%13
+	ADD     pc,pc,r0
+13
+        DCD     1
+14
+        LDRB    r4,[r9]                 ;read byte, increment byte address
+        ADDS    r9,r9,r10               ;perform increment operation
+        ADDS    r8,r8,r11               ;countdown repeat counter
+	BCS	%B14
+        B       ts_Read_cmd_read        ;send data at finish
+
+
+ts_R_VSW
+        LDR     r4,[r9]                 ;read and tx word, repeat address
+        B       ts_Read_Txdata
+
+ts_R_VSB
+        LDRB    r4,[r9]                 ;read and tx byte, repeat address
+        B       ts_Read_Txdata
+
+ts_R_VIW
+        LDR     r4,[r9]                 ;read and tx word, next word address
+        LDR     r10,%15
+        B       ts_Read_Txdata
+15
+        DCD     4
+
+ts_R_VIB
+	ADDS	r0,r0,r0
+        LDRB    r4,[r9]                 ;read and tx byte, next byte address
+        LDR     r10,%16
+        B       ts_Read_Txdata
+16
+        DCD     1 
+
+
+;
+; Operations completed. Report final result and checksum back to host.
+; Quiet option only transmits read data here (this is pretty useless
+; except where only one value was read)
+;
+
+ts_Read_cmd_read
+	ADDS	r12,r12,r4
+        ADDS    r14,r0,pc               ;send result of 'quiet' read
+        B       ts_SendWord
+ts_Read_cmd_done
+        SUBS    r4,r0,r12               ;get overall checksum  - can't think
+        ADDS    r14,r0,pc               ;how to do this using only ADDS !
+        B       ts_SendWord
+
+        B       ts_GetCommand
+
+
+; Called by vectoring through command table.
+; if option 1 set, read processor mode
+; Read address
+; Read and check checksum
+; Reply with command byte or FF
+; Reply with checksum
+; if option 1 set, load SPSR
+; Jump to code
+
+
+ts_execute      ROUT
+	ADDS	r12,r0,r0		; initialise checksum adder
+	LDR	r8,%00			; initialise msr-jumper
+	ADD	pc,pc,r0
+00
+	&	4
+	ADDS	r7,r4,r4		; get operation type
+	ADDS	r7,r7,r7
+	ADD 	pc,pc,r7		; jump to pc + (r4 * 4)
+	&	0
+
+	B	%FT10
+	B	%FT08
+	B	%FT10
+	B	%FT10
+	B	%FT10
+	B	%FT10
+	B	%FT10
+	B	%FT10
+
+
+08	ADDS	r14,r0,pc		; get new processor mode
+	B	ts_GetWord
+	ADDS	r12,r0,r4
+	ADDS	r8,r0,r0		; kill msr-jumper
+10
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ; get jump address
+        ADDS    r9,r12,r4
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ; get checksum
+        ADDS    r4,r4,r9
+        LDR     r5,%11
+        ADD     pc,pc,r0
+11
+        &       (0 - 1)
+        ADDS    r4,r5,r4                ; compare total chex with zero
+        BCS     ts_cmd_error            ; carry set on error
+
+	LDR	r4,%12
+	ADD     pc,pc,r0
+12
+	&	ts_ExecuteCmdByte
+	ADDS	r0,r0,r0
+        ADDS    r14,r0,pc  	        ; echo command byte
+        B       ts_SendByte
+        ADDS    r4,r0,r9                ;return checksum (actually, the
+        ADDS    r14,r0,pc               ; entire message ..)
+        B       ts_SendWord
+
+
+; Now jump to the location given in the message, using the given status bits
+
+	ADD	pc,pc,r8		; jump over the msr instruction
+	NOP
+	&	2_11100001011010011111000000001100 ; 
+
+	ADDS	r14,pc,r0		; Load the address of %13 into r14
+			           	; to provide a return address
+        ADD     pc,r0,r9		; Do the jump
+13
+        B      ts_GetCommand
+
+
+
+; Called by vectoring through command table
+; Read operation count
+; Read target addresses
+; Read data
+; Send command byte or FF
+; Send checksum
+; For all operation count
+;   write data 
+;   if read-back option
+;     read data
+; Return by branching to GetCommand
+
+
+ts_bus_exercise	ROUT
+        ADDS    r7,r0,r4                ; save the control byte
+
+        ADDS    r14, r0, pc             ; setup return address for ..
+        B       ts_GetWord              ; .. get operation count word
+        ADDS    r8, r0, r4              ; r8 is operation count
+        ADDS    r12,r0,r4               ; initialise checksum 
+
+        ADDS    r14, r0, pc
+        B       ts_GetWord              ; r9 is first target address
+        ADDS    r9, r0, r4
+        ADDS    r12,r12,r4              ; accumulate checksum 
+        ADDS    r14, r0, pc
+        B       ts_GetWord              ; r10 is second target address
+        ADDS    r10, r0, r4
+        ADDS    r12,r12,r4              ; accumulate checksum 
+
+        ADDS    r14, r0, pc
+        B       ts_GetWord              ; r11 is first data word
+        ADDS    r11, r0, r4
+        ADDS    r12,r12,r4              ; accumulate checksum 
+        ADDS    r14, r0, pc
+        B       ts_GetWord              ; r13 is second data word
+        ADDS    r13, r0, r4
+        ADDS    r12,r12,r4              ; accumulate checksum 
+
+;
+; Fetch the checksum, and acknowledge
+;
+
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ;read transmitted checksum
+        ADDS    r4,r4,r12               ;tx + total should be zero
+        LDR     r5,%05
+        ADD     pc,pc,r0
+05
+        &       (0 - 1)
+        ADDS    r5,r5,r4                ;carry set on checksum failure
+        BCS     ts_cmd_error
+
+;
+; Checksum looks OK. Send the command and the checksum back.
+;
+	LDR	r4,%06
+	ADD     pc,pc,r0
+06
+	&	ts_BusExCmdByte
+        ADDS    r4,r4,r7                ;restore the original 
+        ADDS    r14,r0,pc
+        B       ts_SendByte
+        ADDS    r4,r0,r12               ;then send the calculated checksum
+        ADDS    r14,r0,pc
+        B       ts_SendWord
+
+	ADDS	r12,r0,r13		; Now addresses are in r9, r10
+					; and data in r11, r12.
+;
+; Convert the operation options into a code pointer
+;
+        ADDS    r13,r7,r7              ; convert operation code to vector 
+        ADDS    r13,r13,r13
+        LDR     r4, %02
+        ADD     pc,pc,r0
+02
+        &       (ts_busex_cmd_table - %03)
+        ADDS    r4,pc,r4
+        ADDS    r13,r4,r13
+03
+        LDR     r13,[r13]               ; fetch pointer to code
+	LDR	r7, %04			; set up decrementer in r8
+	ADD 	pc,pc,r0
+04
+	DCD	(0 - 1)
+07
+        ADD     pc,pc,r13               ; jump to operation
+	&	0
+
+;
+; In this table, the operation after any word fetch is vectored by
+; the 3 least significant bits of the command byte to perform some 
+; combination of writing with  : 
+;
+; bit 2 -> 0    S : Perform separate data write ops
+;          1    M : Use STM / LDM instructions 
+;
+; bit 1 -> 0    R : Perform only read operations
+;          1    W : Write before reading
+;
+; bit 0 -> 0    W : word operation
+;          1    B : byte operation
+;
+; Note that byte and multiple operations are mutually
+; exclusive.
+;
+
+        ASSERT  ((ts_busex_cmd_table - %07) = 8)
+
+ts_busex_cmd_table
+
+        DCD     (ts_B_SRW - ts_busex_cmd_table)
+        DCD     (ts_B_SRB - ts_busex_cmd_table)
+        DCD     (ts_B_SWW - ts_busex_cmd_table)
+        DCD     (ts_B_SWB - ts_busex_cmd_table)
+        DCD     (ts_B_MRW - ts_busex_cmd_table)
+        DCD     (ts_B_MRB - ts_busex_cmd_table)
+        DCD     (ts_B_MWW - ts_busex_cmd_table)
+        DCD     (ts_B_MWB - ts_busex_cmd_table)
+
+ts_B_SRW
+	LDR	r11,[r9]		; read-only separate words
+	LDR	r12,[r10]
+	ADDS	r8, r8, r7
+	BCS	ts_B_SRW
+	B	ts_B_done
+
+ts_B_SRB
+	LDRB	r11,[r9]		; read-only separate bytes
+	LDRB	r12,[r10]
+	ADDS	r8, r8, r7
+	BCS	ts_B_SRB
+	B	ts_B_done
+
+ts_B_SWW
+	STR	r11,[r9]		; write and read separate words
+	STR	r12,[r10]
+	LDR	r1,[r9]
+	LDR	r2,[r10]
+	ADDS	r8, r8, r7
+	BCS	ts_B_SWW
+	B	ts_B_done
+
+ts_B_SWB
+	STRB	r11,[r9]		; write and read separate bytes
+	STRB	r12,[r10]
+	LDRB	r1,[r9]
+	LDRB	r2,[r10]
+	ADDS	r8, r8, r7
+	BCS	ts_B_SWB
+	B	ts_B_done
+
+
+ts_B_MRW
+	LDMIA	r9,{r1,r2}		; read-only multiple words
+	LDMIA	r10,{r1,r2}
+	ADDS	r8, r8, r7
+	BCS	ts_B_MRW
+	B	ts_B_done
+
+ts_B_MWW
+	STMIA	r9,{r11,r12}		; write and read multiple words
+	LDMIA	r9,{r1,r2}
+	STMIA	r10,{r11,r12}
+	LDMIA	r10,{r1,r2}
+	ADDS	r8, r8, r7
+	BCS	ts_B_MWW
+	B	ts_B_done
+
+;
+; Orthogonally, these should be multiple byte operations - we can't do that, 
+; so they actually do a single/multiple mixture.
+; The first address argument is used for word-aligned operations and the
+; second for byte-aligned operations - so set only the second address
+; to a non-word-aligned address.
+
+ts_B_MRB
+	LDMIA	r9,{r1,r2}		; read-only multiple words
+	LDRB	r1,[r10]		; then single bytes
+	LDR 	r1,[r9]			; and single words
+	ADDS	r8, r8, r7
+	BCS	ts_B_MRB
+	B	ts_B_done
+
+ts_B_MWB
+	STMIA	r9,{r11,r12}		; store multiple words
+	STRB	r11,[r10]		; write byte
+	STR	r12,[r9]		; write words
+	LDMIA	r9,{r1,r2}
+	LDRB	r1,[r10]
+	LDR	r1,[r9]			; read single and multiple words
+	ADDS	r8, r8, r7
+	BCS	ts_B_MWB
+;	B	ts_B_done
+
+ts_B_done
+	B	ts_GetCommand
+
+
+
+;
+; All commands fall through here to respond with FF if the received
+; message block checksums fail.
+;
+
+ts_cmd_error    ROUT                    ; error in command
+        LDR     r4, %01                 ; return error response
+        ADD     pc,pc,r0
+01
+        DCD     ErrorCmd
+	ADDS	r0,r0,r0
+        ADDS    r14, r0, pc             ; send response byte to host
+        B       ts_SendByte
+
+        B       ts_GetCommand
+
+
+; generic coprocessor register names
+
+cpr0	CN	0
+cpr1	CN	1
+cpr2	CN	2
+cpr3	CN	3
+cpr4	CN	4
+cpr5	CN	5
+cpr6	CN	6
+cpr7	CN	7
+cpr8	CN	8
+cpr9	CN	9
+cpr10	CN	10
+cpr11	CN	11
+cpr12	CN	12
+cpr13	CN	13
+cpr14	CN	14
+cpr15	CN	15
+
+
+; Called by vectoring through command table.
+; Read transfer value
+; Read and check checksum
+; Extract copro register number
+; Index suitable MRC instruction
+; Perform copro write
+; Reply with command byte or FF
+; Reply with checksum
+
+ts_write_cpr15h	ROUT
+	ADDS	r4,r4,#8		; adjust opcode for high registers
+ts_write_cpr15l
+	ADDS	r7,r0,r4		; save opcode to r7
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ; get value for copro
+        ADDS    r9,r0,r4
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ; get checksum
+        ADDS    r4,r4,r9
+        LDR     r5,%01
+        ADD     pc,pc,r0
+01
+        &       (0 - 1)
+        ADDS    r4,r5,r4                ; compare total chex with zero
+        BCS     ts_cmd_error            ; carry set on error
+
+	ADDS	r13,r7,r7		; point into instruction table
+	ADDS	r13,r13,r13
+	ADDS	r13,r13,r13
+	ADD 	pc,pc,r13		; jump to pc + (r7 * 8)
+	&	0
+
+	SetCop	r9,cpr0			; transfer instructions
+	B	%02
+	SetCop	r9,cpr1
+	B	%02
+	SetCop	r9,cpr2
+	B	%02
+	SetCop	r9,cpr3
+	B	%02
+	SetCop	r9,cpr4
+	B	%02
+	SetCop	r9,cpr5
+	B	%02
+	SetCop	r9,cpr6
+	B	%02
+	SetCop	r9,cpr7
+	B	%02
+	SetCop	r9,cpr8
+	B	%02
+	SetCop	r9,cpr9
+	B	%02
+	SetCop	r9,cpr10
+	B	%02
+	SetCop	r9,cpr11
+	B	%02
+	SetCop	r9,cpr12
+	B	%02
+	SetCop	r9,cpr13
+	B	%02
+	SetCop	r9,cpr14
+	B	%02
+	SetCop	r9,cpr15
+
+02
+	LDR	r4,%03
+	ADD     pc,pc,r0
+03
+	&	ts_CPWCmdByte		; build command byte + option
+	ADDS	r4,r4,r7
+        ADDS    r14,r0,pc  	        ; echo command byte
+        B       ts_SendByte
+        ADDS    r4,r0,r9                ; return checksum 
+        ADDS    r14,r0,pc               ;
+        B       ts_SendWord
+
+	B	ts_GetCommand
+
+
+
+
+; Called by vectoring through command table.
+; Read and check checksum
+; Extract copro register number
+; Index suitable MCR instruction
+; Perform copro read
+; Reply with command byte or FF
+; Reply with checksum
+; Send transfer results
+; Send checksum
+
+ts_read_cpr15h	ROUT
+	ADDS	r4,r4,#8		; adjust opcode for high registers
+ts_read_cpr15l
+	ADDS	r7,r0,r4		; save opcode in r7
+        ADDS    r14,r0,pc
+        B       ts_GetWord              ; get checksum to r4
+	ADDS	r9,r0,r4		; copy to r9
+        LDR     r5,%01
+        ADD     pc,pc,r0
+01
+        &       (0 - 1)
+        ADDS    r4,r5,r4                ; compare total chex with zero
+        BCS     ts_cmd_error            ; carry set on error
+
+	LDR	r4,%02
+	ADD     pc,pc,r0
+02
+	&	ts_CPRCmdByte		; build command byte + option
+	ADDS	r4,r4,r7
+        ADDS    r14,r0,pc  	        ; echo command byte
+        B       ts_SendByte
+        ADDS    r4,r0,r9                ; return checksum
+        ADDS    r14,r0,pc
+        B       ts_SendWord
+
+	ADDS	r13,r7,r7		; point into instruction table
+	ADDS	r13,r13,r13
+	ADDS	r13,r13,r13
+	ADD 	pc,pc,r13		; jump to pc + (r7 * 8)
+	&	0
+
+	ReadCop	r12,cpr0		; transfer instructions
+	B	%03
+	ReadCop	r12,cpr1
+	B	%03
+	ReadCop	r12,cpr2
+	B	%03
+	ReadCop	r12,cpr3
+	B	%03
+	ReadCop	r12,cpr4
+	B	%03
+	ReadCop	r12,cpr5
+	B	%03
+	ReadCop	r12,cpr6
+	B	%03
+	ReadCop	r12,cpr7
+	B	%03
+	ReadCop	r12,cpr8
+	B	%03
+	ReadCop	r12,cpr9
+	B	%03
+	ReadCop	r12,cpr10
+	B	%03
+	ReadCop	r12,cpr11
+	B	%03
+	ReadCop	r12,cpr12
+	B	%03
+	ReadCop	r12,cpr13
+	B	%03
+	ReadCop	r12,cpr14
+	B	%03
+	ReadCop	r12,cpr15
+
+03
+	ADDS	r4,r0,r12		; return result
+	ADDS	r14,r0,pc
+	B	ts_SendWord
+	SUBS	r4,r0,r12		; return checksum
+	ADDS	r14,r0,pc
+	B	ts_SendWord
+
+	B	ts_GetCommand
+
+
+        END
+
+
diff --git a/TestSrc/ExtIO b/TestSrc/ExtIO
new file mode 100644
index 0000000000000000000000000000000000000000..8ef956a201a997890c60749536a7896ac60b7cad
--- /dev/null
+++ b/TestSrc/ExtIO
@@ -0,0 +1,1089 @@
+; > TestSrc.ExtIO
+
+        TTL RISC OS 2+ POST external commands
+;
+; External interface for RISC OS ROM.
+; provides entry points to send byte- and word- and string-sized objects
+; and to receive byte- and word-sized objects   
+;
+; A minimal set of opcodes should be used (ideally, only B, LDR and ADDS)
+; so that a processor test may be validly included in the internal test
+; sequence.
+;
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name            Comment
+; ----          ----            -------
+; 06-Dec-89     ArtG            Initial version - split from `Begin`
+;                               Release 0.2 for integration
+; 31-Mar-90     ArtG            Added ts_MoreText, cursor position, hex.
+; 19-Apr-90     ArtG            Added bus exercise commands
+; 09-May-90     ArtG            Changed LCD strobe to 12 pulses
+; 15-May-90     ArtG            Added ReadyByte : improves synchronization
+;                               when ExtCmd execution toggles A21/A22.
+; 18-Jun-90	ArtG		Added CPR15 read/write functions
+;
+;
+;------------------------------------------------------------------------
+
+
+        SUBT    Test adapter interface
+
+;
+; The test adapter senses an access to the ROM with address line A21 high.
+; Current (2M addressing space) ROMs only use address lines A2 to A20,
+; so if A21 to A22 are asserted they will be ignored (the ROMS are aliased
+; into 8M of space). With no test adapter, the aliased ROM location will
+; be read and may be recognised. The test adapter may selectively disable
+; ROMs when A21 is high, causing arbitrary data to be read. This data
+; should be dependent on the previous ROM read operation, and will
+; therefore be predictably not equal to the data read when the ROMs are
+; aliased.
+; The assumption that A21 is unused may be invalidated by a later issue
+; of the PCB. A22 is therefore asserted at the same time : this will then
+; be used on a PCB where A22 is tracked to a test connector and 8Mbit ROMS
+; are used. Machines using larger ROMs than 8 Mbit (4M addressing space) 
+; will require explicit decoding or a new communication scheme.
+;
+
+
+;
+; This section determines whether the test interface adapter exists, and 
+; what variety is fitted (dumb, display or external)      
+; 3 read operations are performed (a WS operation): if all of these 
+; find a ROM alias then no adapter is fitted.
+;
+; If an adapter responds, then a RD operation is performed - 4 strobes then
+; clocking 8 bits into r4. These bits may be all zeros (a dumb adapter)
+; or all ones (a display adapter) or some other value (an external
+; adapter)
+;
+
+ts_GetCommand  ROUT
+
+        LDR     r0,%01
+        ADD     pc,pc,r0
+01
+        &       0
+
+        ; delay to make a gap before reading
+
+        LDR     r3,%02
+        ADD     pc,pc,r0
+02
+        &       ts_recover_time
+03
+        ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %03
+
+        ROUT
+
+;
+; Load up the registers for the test interface communication -
+;
+
+        LDR     r0,%01                  ; set zero in r0
+        ADD     pc,pc,r0  ;(generally useful constant - especially for skip)
+01
+        &       0
+        LDR     r1,%02                  ; set FFFFFFFF in r1
+        ADD     pc,pc,r0  ;(test value : sets carry when added to non-zero)
+02
+        &       (-1)
+        LDR     r2,%03                  ; set pointer to test address
+        ADD     pc,pc,r0  ;(points to aliased copy of a zero word)
+03
+        &       (ts_Alias_bits + (%01 - %04))
+        ADDS    r2,pc,r2                ; adjust r2 for ROM-relative address
+        ADDS    r4,r0,r0                ; clear output accumulator
+04                                      ; where pc is when added to r2
+
+        ; do an RD operation (four strobes) to ensure interface cleared
+
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+
+        ; write a byte (initially, &90) to indicate readiness
+
+        LDR     r4,%20
+        ADD     pc,pc,r0
+20
+        &       ts_ReadyByte_00
+        ADDS    r14,r0,pc
+        B       ts_SendByte
+
+        ; delay to make a gap between WRS and RD operations
+
+        LDR     r3,%05
+        ADD     pc,pc,r0
+05
+        &       ts_recover_time
+06
+        ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %06
+
+        LDR     r5,%07                  ; counter for first 5 bits
+        ADD     pc,pc,r0
+07
+        &       1 :SHL: (32 - 5)
+        LDR     r6,%08                  ; counter for last 3 bits
+        ADD     pc,pc,r0
+08
+        &       1 :SHL: (32 - 3)
+        ADDS    r4,r0,r0                ; input accumulator initialisation
+
+; put the test interface into input mode
+
+        LDR     r3,[r2]                 ; 3 bit lead-in
+        ADDS    r3,r3,r1                ; (adapter detects WS operation)
+        BCC     ts_User_startup         ; abort if no adapter present
+
+        LDR     r3,[r2]                 ; two more strobes, then waitloop
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+
+; started input operation : wait for interface to be ready
+
+09
+        LDR     r3,[r2]                 ; read start bit repeatedly
+        ADDS    r3,r3,r1                ; (adapter detects RD operation)
+        BCC     %09                     ; loop until interface is ready
+
+; read the first 5 bits into r5 and the second 3 bits into r4
+
+10      LDR     r3,[r2]                 ; read a bit of the byte
+        ADDS    r3,r3,r1                ; .. if the test adapter is present, carry bit set
+        ADCS    r4,r4,r4                ; .. shift left and add in carry
+
+        ADDS    r5,r5,r5                ; loop until 5 bits are read
+        BCC     %10
+
+        ADDS    r5,r4,r4                ; copy bits 7..3 to r5, bits 5..1 
+
+        ADDS    r4,r0,r0                ; and read the last 3 bits to r4
+11      LDR     r3,[r2]                 ; read a bit of the byte
+        ADDS    r3,r3,r1
+        ADCS    r4,r4,r4
+
+        ADDS    r6,r6,r6                ; loop until last 3 bits are read
+        BCC     %11
+
+;
+; Command byte read in (split between r4 and r5)
+; Pass the option bits (r4) to the function identified by r5.
+;
+
+        ADDS    r5,r5,r5                ; index * 2 -> index * 4 
+        LDR     r3,%12                  ; pc-relative ptr to command_table
+        ADD     pc,pc,r0
+12
+        &       ts_command_table - %13
+        ADDS    r3,pc,r3                ; absolute pointer to command table
+        ADDS    r3,r3,r5
+
+13      LDR     r3,[r3]                 ; get table entry 
+14      ADD     pc,pc,r3                ; (offset from command_table)
+
+        &       0                       ; necessary padding : pc must point
+                                        ; to command table when r3 is added.
+
+;
+; This is the table of offsets to all the built-in functions. 
+; The top 5 bits of the command are used to index, so there are
+; 32 possible entries, mostly illegal.
+; Decoding of the function modifier bits is performed by multiple
+; entries in this table.
+;
+
+; pc must point here when ADDS pc,r3,pc is executed
+
+        ASSERT  ((ts_command_table - %14)  = 8)
+
+ts_command_table
+
+        DCD     (ts_Dealer_startup - ts_command_table)   ; display interface
+ts_Windex
+        DCD     (ts_write_memory   - ts_command_table)   ; external tests
+ts_Rindex
+        DCD     (ts_read_memory    - ts_command_table)
+ts_Eindex
+        DCD     (ts_execute        - ts_command_table)
+ts_Bindex
+        DCD     (ts_bus_exercise   - ts_command_table)
+
+        DCD     (ts_GetCommand     - ts_command_table)	; dummy entry aligns CPR instructions
+							; to allow 4-bit option field
+ts_CWindex
+	DCD	(ts_write_cpr15l   - ts_command_table)
+	DCD	(ts_write_cpr15h   - ts_command_table)
+ts_CRindex
+	DCD	(ts_read_cpr15l    - ts_command_table)
+	DCD	(ts_read_cpr15h    - ts_command_table)
+
+        ; pad the table out to 31 entries 
+        ; (leave space for display vector)
+
+OldOpt  SETA    {OPT}
+        OPT     OptNoList
+doffset SETA    .
+        WHILE   doffset < (ts_command_table + (31 * 4)) ; illegal entries
+        DCD     (ts_GetCommand     - ts_command_table)
+doffset SETA    doffset + 4
+        WEND
+        OPT     OldOpt
+
+        DCD     (ts_Forced_startup - ts_command_table)  ; dumb interface
+
+;
+; The indexes into the above table are needed in ExtCmd ...
+;
+ts_WriteCmdByte         *       ((ts_Windex - ts_command_table) :SHL: 1)
+ts_ReadCmdByte          *       ((ts_Rindex - ts_command_table) :SHL: 1)
+ts_ExecuteCmdByte       *       ((ts_Eindex - ts_command_table) :SHL: 1)
+ts_BusExCmdByte         *       ((ts_Bindex - ts_command_table) :SHL: 1)
+ts_CPWCmdByte		*	((ts_CWindex  - ts_command_table) :SHL: 1)
+ts_CPRCmdByte		*	((ts_CRindex  - ts_command_table) :SHL: 1)
+
+
+;
+; Primitives for reading data from the external interface
+;
+;               - Get a byte from the interface (into r4)
+;               - Get a (4 byte) word from the interface (into r4)
+;
+; Required register setup is presumed done by a recent ts_GetCommand.
+; r0, r1 and r2 have critical values
+; r14 is the link address
+;
+
+ts_GetWord      ROUT
+
+        LDR     r6,%01                  ; counter for 4 bytes per word
+        ADD     pc,pc,r0                ; (bit set 4 left shifts from Carry)
+01
+        &       1 :SHL: (32 - 4)
+        B       ts_Getdata
+
+ts_GetByte      ROUT
+        LDR     r6,%01                  ; counter for single byte
+        ADD     pc,pc,r0
+01
+        &       1 :SHL: (32 - 1)
+
+ts_Getdata      ROUT
+        ADDS    r4,r0,r0                ; input accumulator initialisation
+
+        LDR     r3,[r2]                 ; 3 bit lead-in
+        ADDS    r3,r3,r1                ; (adapter detects RD operation)
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+
+; started input operation : now loop until r6 shifts into Carry
+
+02
+        LDR     r5,%03                  ; counter for 8 bits per byte
+        ADD     pc,pc,r0
+03
+        &       2_00000001000000010000000100000001
+04
+        LDR     r3,[r2]                 ; read start bit repeatedly
+        ADDS    r3,r3,r1
+        BCC     %04                     ; loop until interface is ready
+05
+        LDR     r3,[r2]                 ; read a bit of the byte
+        ADDS    r3,r3,r1
+        ADCS    r4,r4,r4                ; SHL r4, add carry bit.
+
+        ADDS    r5,r5,r5                ; loop until byte is read
+        BCC     %05
+
+        ADDS    r6,r6,r6                ; loop until word is read
+        BCC     %04
+
+        ADD     pc,r0,r14               ; back to the caller
+
+
+;
+; Primitives for sending data to the interface
+;
+;               - Send a byte to the interface (from r4 lsb)
+;               - Send a (4 byte) word to the interface (from r4)
+;
+; Required register setup is presumed done by a recent ts_GetCommand.
+; r0, r1 and r2 have critical values
+; r14 is the link address
+;
+
+ts_SendWord     ROUT
+        LDR     r6,%01                  ; counter for 4 bytes per word
+        ADD     pc,pc,r0                ; (bit set 4 left shifts from Carry)
+01
+        &       1 :SHL: (32 - 4)
+        B       ts_Putdata
+
+ts_SendByte      ROUT
+        LDR     r6,%01                  ; counter for single byte
+        ADD     pc,pc,r0
+01
+        &       (3 :SHL: 7)
+02      ADDS    r4,r4,r4                ;shift byte into highest 8 bits
+        ADDS    r6,r6,r6
+        BCC     %02                     ;stop when byte shifted,
+                                        ;leaving bit 31 set in r6
+
+ts_Putdata      ROUT
+
+; Wait - gap between successive WS attempts or successive bytes
+
+01      LDR     r3,%02
+        ADD     pc,pc,r0
+02
+        &       ts_recover_time
+03      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %03
+
+        LDR     r3,[r2]                 ; Test for adapter ready for data
+        ADDS    r3,r3,r1                ; (adapter detects WS operation)
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        BCC     %10                     ; skip out if adapter not present
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        BCC     %01                     ; loop back until adapter is ready
+
+; Adapter ready - loop around all the bits in the byte
+
+        LDR     r5,%04                  ; load bits-per-byte counter
+        ADD     pc,pc,r0
+04
+        &       (1 :SHL: (32-8))
+
+05      LDR     r3,%06                  ; delay before sending bit
+        ADD     pc,pc,r0
+06
+        &       ts_recover_time
+07      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %07
+
+        ; Send a single bit : 1 pulse for 1, 2 pulses for 0
+
+        LDR     r3,[r2]
+        ADDS    r4,r4,r4                ; shift current bit into Carry
+        LDRCC   r3,[r2]                 ; second pulse if bit is 0
+
+        ; repeat until 8 bits are sent
+
+        ADDS    r5,r5,r5
+        BCC     %05
+
+; Repeat for all the bytes to be sent (1 or 4)
+
+        ADDS    r6,r6,r6
+        BCC     %01
+
+; Go to TXRDY to ensure the host sees the transmit request
+
+        LDR     r3,%08                  ; delay before sending pattern
+        ADD     pc,pc,r0
+08
+        &       ts_recover_time
+09      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %09
+
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1                ; dummy - space between pulses
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+
+; All sent - r14 holds the caller's return address
+10
+        ADD     pc,r0,r14
+
+
+
+;
+; Reporting primitive
+;
+;               - Send the text (nul-terminated, at r4) to the display
+;
+; Interface registers need to be set up : this function is called from test 
+; code rather than external interface code.
+;
+; The display is assumed to be a standard 16 character LCD module using 
+; the Hitachi HD44780 display controller.
+; The 16-digit module uses a single 44780. This is an abnormal use of the
+; controller, and requires it to be set to two-line mode, with the first
+; 8 displayed characters on the first 'line', and the second 8 on the
+; second 'line'. Characters sent to the second line must be written at
+; character position 40 +. In order to permit different modules to be
+; fitted to later adapters, it is suggested that the first 7 characters 
+; be treated as a 'title' line, and the second 8 as a 'comment' line.
+; A space should always be placed at the end of the title line to
+; split the display fields, unless there is no 'comment' line.
+; Do not display characters across the two areas as though they adjoined
+; (even though they do :-) ).
+;
+; The controller is operated in its 4-bit mode, which allows the interface
+; to drive 4 bits of alpha information and 4 bits of control information.
+; The bits in a transmitted byte are assigned as :
+;
+;       bit 0   -       D4  }   4-bit mode data bus
+;           1   -       D5  }  
+;           2   -       D6  }
+;           3   -       D7  }
+;
+;           4   -       RS      Register Select : 0 for control, 1 for data
+;
+;           5   -           }   Unassigned
+;           6   -           }
+;
+;           7   -       CPEN    Interface control :     0 for enable, 
+;                                                       1 for disable
+;
+; For each message sent, the display is first initialised, using the 
+; following sequence (each byte is sent as 2 bytes, high nibble first, 
+; with RS clear in bit 4 of each byte)
+; After each byte, an RD operation is performed : this is used by the
+; interface hardware to strobe the data into the display. 
+;
+;
+; The message addressed by r4 is then sent (data mode : RS set in each byte)
+; until a 0 byte is encountered.
+;
+
+;
+; This is the command sequence sent to initialise the display
+;
+
+ts_initialise
+        =       &30,&30,&30,&20 ; power-up initialisation
+        =       &20,&80         ; 4 bit mode, two line, Font 0
+        =       &00,&C0         ; Display on, no cursor visible
+        =       &00,&60         ; Incrementing display position, no shift
+        =       &80,&00         ; Set DD RAM address 0
+        =       &00,&20         ; Cursor home
+        =       &00,&10         ; Display clear
+ts_initialise_end
+
+        ASSERT  ((ts_initialise_end - ts_initialise) / 2) < 32
+
+
+;
+; This is the command sequence sent when continuation text is sent
+;
+
+ts_extend
+        =       &00,&C0         ; Display on, cursor invisible
+        =       &00,&60         ; Incrementing display position, no shift
+ts_extend_end
+
+        ASSERT  ((ts_extend_end - ts_extend) / 2) < 32
+
+;
+; One of these commands are sent when offset text is required
+;
+
+ts_offset_table
+        =       &80,&00         ; Set DD RAM address 0
+ts_offset_table_1
+        =       &80,&10         ; Set DD RAM address 1
+        =       &80,&20         ; Set DD RAM address 2
+        =       &80,&30         ; Set DD RAM address 3
+        =       &80,&40         ; Set DD RAM address 4
+        =       &80,&50         ; Set DD RAM address 5
+        =       &80,&60         ; Set DD RAM address 6
+        =       &80,&70         ; Set DD RAM address 7
+        =       &C0,&00         ; Set DD RAM address 40
+        =       &C0,&10         ; Set DD RAM address 41
+        =       &C0,&20         ; Set DD RAM address 42
+        =       &C0,&30         ; Set DD RAM address 43
+        =       &C0,&40         ; Set DD RAM address 44
+        =       &C0,&50         ; Set DD RAM address 45
+        =       &C0,&60         ; Set DD RAM address 46
+        =       &C0,&70         ; Set DD RAM address 47
+
+
+; This assertion is forced by the code : each sequence assumed 2 bytes.
+
+        ASSERT  ((ts_offset_table_1 - ts_offset_table) = 2)
+
+
+
+        ALIGN
+
+;
+; Here starts the code ...
+;
+
+ts_SendQuit     ROUT                    ; put this code BEFORE %16
+        ADD     pc,r0,r14               ;
+
+
+
+;
+; Entry point for initialising the display and sending r4 text.
+;
+
+
+ts_SendText     ROUT
+
+;
+; Point to the command sequence to setup and clear the display
+;
+
+        LDR     r0,%10                  ; set zero in r0
+        ADD     pc,pc,r0
+10
+        &       0
+        LDR     r7,%11                  ; pointer to init sequence
+	ADDS	r7,pc,r7
+        ADD     pc,pc,r0
+11
+        &       (ts_initialise - .)
+        LDR     r6,%12                  ; length of init sequence
+        ADD     pc,pc,r0
+12
+        &       (1 :SHL: (32 - (ts_initialise_end - ts_initialise)))
+        B       ts_SendLCDCmd
+
+
+;
+; Entry point for adding text to current cursor position
+;
+
+ts_MoreText     ROUT
+
+        LDR     r0,%10                  ; set zero in r0
+        ADD     pc,pc,r0
+10
+        &       0
+        LDR     r7,%11                  ; pointer to command sequence
+	ADDS	r7,pc,r7
+        ADD     pc,pc,r0
+11
+        &       (ts_extend - .)
+        LDR     r6,%12                  ; length of command sequence
+        ADD     pc,pc,r0
+12
+        &       (1 :SHL: (32 - (ts_extend_end - ts_extend)))
+        B       ts_SendLCDCmd
+
+
+ts_PosText      ROUT
+
+;
+; Entry point for adding text at a specific cursor position
+; Used iteratively by SendText, etc if cursor position command found.
+; Offset into display is given in r6.
+;
+
+        LDR     r0,%10                  ; set zero in r0
+        ADD     pc,pc,r0
+10
+        &       0
+        LDR     r7,%11                  ; pointer to command sequence
+	ADDS	r7,pc,r7
+        ADD     pc,pc,r0
+11
+        &       (ts_offset_table - .)   ; offset * 2 into table of
+        ADDS    r6,r6,r6                ; offset command sequences
+        ADDS    r7,r7,r6
+
+        LDR     r6,%12                  ; length of command sequence
+        ADD     pc,pc,r0
+12
+        &       (1 :SHL: (32 - 2))
+
+
+;
+; Entry point for writing arbitrary command strings.
+; Set r7 to point to command string, r6 length (as tables above),
+; Set r4 to point to following Data string (null-terminated).
+;
+
+ts_SendLCDCmd
+
+        LDR     r0,%01                  ; set zero in r0
+        ADD     pc,pc,r0
+01
+        &       0
+        LDR     r1,%02                  ; set FFFFFFFF in r1
+        ADD     pc,pc,r0  ;(test value : sets carry when added to non-zero)
+02
+        &       (-1)
+        LDR     r2,%03                  ; set pointer to test address
+        ADD     pc,pc,r0  ;(points to aliased copy of a zero word)
+03
+        &       (ts_Alias_bits + (%01 - %04))
+        ADDS    r2,pc,r2                ; adjust r2 for ROM-relative address
+        ADDS    r0,r0,r0                ; dummy (to keep labels nearby !)
+04                                      ; where pc points when added to r2
+
+
+; Wait - gap between successive WS attempts or successive bytes
+
+ts_send_command_byte ROUT
+
+        LDR     r3,%14
+        ADD     pc,pc,r0
+14
+        &       ts_recover_time
+15      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %15
+        LDR     r1,%16                  ; reload test register
+        ADD     pc,pc,r0
+16
+        &       (-1)
+
+        LDR     r3,[r2]                 ; Test for adapter ready for data
+        ADDS    r3,r3,r1                ; (adapter detects WS operation)
+        BCC     ts_SendQuit             ; skip output : adapter not present
+                                        ; (backward jump helps ensure LDR r3,[r2]
+                                        ; only reads zero when adapter absent
+        LDR     r3,[r2]			; since previous bus data is nonzero)
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        BCC     ts_send_command_byte    ; loop back until adapter is ready
+
+; Adapter ready - loop around all the bits in the byte
+
+
+        LDR     r5,%21                  ; load byte-shift counter ...
+        ADD     pc,pc,r0                ; ... and bits-per-byte counter
+21
+        &       (1 :SHL: 8) + 1         ; 24 shifts + 8 shifts
+        LDRB    r1,[r7]
+22      ADDS    r1,r1,r1                ; shift byte up into m.s.d.
+        ADDS    r5,r5,r5
+        BCC     %22
+
+        ; Send a single bit : 1 pulse for 1, 2 pulses for 0
+
+23      LDR     r3,[r2]
+        ADDS    r1,r1,r1                ; shift current bit into Carry
+        LDRCC   r3,[r2]                 ; second pulse if bit is 0
+
+        ; and wait for the inter-bit time
+
+        LDR     r3,%24
+        ADD     pc,pc,r0
+24
+        &       ts_recover_time
+25      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %25
+
+        ; repeat until 8 bits are sent
+
+        ADDS    r5,r5,r5
+        BCC     %23
+
+        ; do a RD operation to strobe the data out
+
+        LDR     r5,%26
+        ADD     pc,pc,r0
+26
+        &       (1 :SHL: (32 - 12))
+27
+        LDR     r3,[r2]
+        ADDS    r5,r5,r5
+        BCC     %27
+
+; Repeat for all the bytes to be sent (ts_initialise_end - ts_initialise)
+
+        LDR     r3,%33
+        ADD     pc,pc,r0
+33
+        &       1
+        ADDS    r7,r7,r3                ; bump the pointer
+        ADDS    r6,r6,r6                ; bump the counter (shift left)
+        BCC     ts_send_command_byte
+
+
+;
+; Then send all the display bytes (in 4-bit mode) until a nul-terminator
+; is reached.
+;
+
+;
+; Send a single character (as two separate 4-bit fields)
+; First, look to see if it's one of :
+;
+;       NUL                     - end of text string
+;       0x80 - 0xfe             - cursor positioning
+;       0xff                    - introduce a hex digit
+;
+
+ts_send_text_byte       ROUT
+
+        LDR     r1,%40                  ; reload test register
+        ADD     pc,pc,r0
+40
+        &       (-1)
+
+        LDRB    r7,[r4]
+        ADDS    r3,r7,r1                ; test for nul terminator
+        BCC     ts_SendEnd
+
+;
+; Byte isn't null. Check for >= 0x80.
+;
+
+        LDR     r6,%42                  ; test for cursor control
+        ADD     pc,pc,r0
+42
+        &       (-&80)                  ; &8x means column x.
+        ADDS    r6,r7,r6
+        BCC     ts_printable_char       ; < &80 : write a character
+
+;
+; Carry set : r6 now holds (value - 0x80). Check for numeric escape (&ff).
+;
+        LDR     r3,%43
+        ADD     pc,pc,r0
+43
+        &       (-&7f)
+        ADDS    r3,r6,r3
+        BCC     %47
+
+;
+; Carry set : fetch a nybble from the top of r8 and display that.
+;
+
+        ADDS    r8,r8,r8
+        ADCS    r6,r0,r0
+        ADDS    r8,r8,r8
+        ADCS    r6,r6,r6
+        ADDS    r8,r8,r8
+        ADCS    r6,r6,r6
+        ADDS    r8,r8,r8
+        ADCS    r6,r6,r6
+
+        LDRB    r7,[pc,r6]
+        B       ts_printable_char
+45
+        =       "0123456789ABCDEF"
+
+;
+; Not &ff : r6 holds cursor positioning offset (< &80). Skip over
+; the cursor control byte and iterate thro' PosText to move
+; typing position.
+;
+
+47
+        LDR     r3, %48
+        ADD     pc,pc,r0
+48
+        &       1
+        ADDS    r4,r3,r4
+        B       ts_PosText
+
+;
+; Character is normal text : write it to the LCD.
+; The shift loop is used to generate the inter-byte delay normally 
+; provided by  ts_recover_time. Always make sure this is long enough.
+;
+
+ts_printable_char
+
+        ADDS    r6,r0,r7                ; take a copy of character
+        LDR     r5,%51                  ; load byte-shift counter ...
+        ADD     pc,pc,r0                ; ... and bits-per-byte counter
+51                                      ; as a bitmask of the shift pattern
+        &       (1:SHL:8)+(1:SHL:4)+1   ; 24 shifts + 4 shifts + 4 shifts
+52      ADDS    r6,r6,r6                ; shift byte up into m.s.d.
+        ADDS    r0,r0,r0                ; slow this loop down - ensure it's
+        ADDS    r0,r0,r0                ; always slower than ts_recover_time
+        ADDS    r5,r5,r5
+        BCC     %52
+
+        LDR     r3,[r2]                 ; Test for adapter ready for data
+        ADDS    r3,r3,r1                ; (adapter detects WS operation)
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        BCC     ts_printable_char       ; loop back until adapter is ready
+
+; Adapter ready - loop around all the bits in the byte
+
+ts_send_tbit_upper
+
+        ; wait for the inter-bit time
+
+        LDR     r3,%55
+        ADD     pc,pc,r0
+55
+        &       ts_recover_time
+56      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %56
+
+        ; Send a single bit : 1 pulse for 1, 2 pulses for 0
+
+        LDR     r3,[r2]
+        ADDS    r6,r6,r6                ; shift current bit into Carry
+        LDRCC   r3,[r2]                 ; second pulse if bit is 0
+
+        ; repeat until upper 4 bits are sent
+
+        ADDS    r5,r5,r5
+        BCC     ts_send_tbit_upper
+
+        ; then send the interface control bits
+
+        LDR     r1,%57
+        ADD     pc,pc,r0
+57
+        &       (8 :SHL: 28)            ; assert RS control pin
+
+ts_send_cbit_upper
+
+        ; wait for the inter-bit time
+
+        LDR     r3,%58
+        ADD     pc,pc,r0
+58
+        &       ts_recover_time
+59      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %59
+
+        ; Send a single bit : 1 pulse for 1, 2 pulses for 0
+
+        LDR     r3,[r2]
+        ADDS    r1,r1,r1                ; shift current bit into Carry
+        LDRCC   r3,[r2]                 ; second pulse if bit is 0
+        ADDS    r5,r5,r5
+        BCC     ts_send_cbit_upper
+
+;
+; do a RD operation to strobe the data out
+;
+
+        LDR     r3,%61
+        ADD     pc,pc,r0
+61
+        &       ts_recover_time
+62      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %62
+
+        LDR     r5,%63
+        ADD     pc,pc,r0
+63
+        &       (1 :SHL: (32 - 12))
+64
+        LDR     r3,[r2]
+        ADDS    r5,r5,r5
+        BCC     %64
+
+        ; prepare to send the lower 4 bits out
+
+        LDR     r5,%70                  ; bitcount mask for 4 data bits
+        ADD     pc,pc,r0                ; and 4 interface control bits
+70
+        &       (((1 :SHL: 4) + 1) :SHL: 24)   
+
+ts_send_text_lower
+        LDR     r3,%71
+        ADD     pc,pc,r0
+71
+        &       ts_recover_time
+72      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %72
+
+        LDR     r1,%73
+        ADD     pc,pc,r0
+73
+        &       (-1)
+
+        LDR     r3,[r2]                 ; Test for adapter ready for data
+        ADDS    r3,r3,r1                ; (adapter detects WS operation)
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        BCC     ts_send_text_lower      ; loop back until adapter is ready
+
+ts_send_tbit_lower
+
+        ; wait for the inter-bit time
+
+        LDR     r3,%76
+        ADD     pc,pc,r0
+76
+        &       ts_recover_time
+77      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %77
+
+        ; Send a single bit : 1 pulse for 1, 2 pulses for 0
+
+        LDR     r3,[r2]
+        ADDS    r6,r6,r6                ; shift current bit into Carry
+        LDRCC   r3,[r2]                 ; second pulse if bit is 0
+
+        ; repeat until lower 4 bits are sent
+
+        ADDS    r5,r5,r5
+        BCC     ts_send_tbit_lower
+
+
+        ; then send the interface control bits
+
+        LDR     r1,%78
+        ADD     pc,pc,r0
+78
+        &       (8 :SHL: 28)            ; assert RS control pin
+
+ts_send_cbit_lower
+
+        ; wait for the inter-bit time
+
+        LDR     r3,%80
+        ADD     pc,pc,r0
+80
+        &       ts_recover_time
+81      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %81
+
+        ; Send a single bit : 1 pulse for 1, 2 pulses for 0
+
+        LDR     r3,[r2]
+        ADDS    r1,r1,r1                ; shift current bit into Carry
+        LDRCC   r3,[r2]                 ; second pulse if bit is 0
+
+        ADDS    r5,r5,r5
+        BCC     ts_send_cbit_lower
+
+;
+; do a RD operation to strobe the data out
+;
+
+        ; wait for the inter-bit time
+
+        LDR     r3,%82
+        ADD     pc,pc,r0
+82
+        &       ts_recover_time
+83      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %83
+
+        LDR     r5,%84
+        ADD     pc,pc,r0
+84
+        &       1 :SHL: (32 - 12)
+85
+        LDR     r3,[r2]
+        ADDS    r5,r5,r5
+        BCC     %85
+
+; Repeat for all the bytes to be sent (until nul terminator is found)
+
+        LDR     r3,%86
+        ADD     pc,pc,r0
+86
+        &       1
+        ADDS    r4,r3,r4                ; bump text pointer
+        B       ts_send_text_byte
+
+;
+; Wait for about 1 seconds worth of LCD operation delays to
+; permit the operator to read the text. 
+; Use of the interface's monitor allows this delay to be increased 
+; or decreased externally.
+;
+
+ts_SendEnd      ROUT
+
+        LDR     r7, %01
+        ADD     pc,pc,r0
+01
+        &       (ts_pause_time + 1)     ; must be an odd number
+					; to ensure pairs of zeros
+        ASSERT ((ts_pause_time :AND: 1) = 0)
+
+02
+        LDR     r3,%03
+        ADD     pc,pc,r0
+03
+        &       ts_recover_time
+04      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %04
+        LDR     r1,%05                  ; reload test register
+        ADD     pc,pc,r0
+05
+        &       (-1)
+
+        LDR     r3,[r2]                 ; Test for adapter ready for data
+        ADDS    r3,r3,r1                ; (adapter detects WS operation)
+        BCC     ts_SendQuit             ; skip output : adapter not present
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        LDR     r3,[r2]
+        ADDS    r3,r3,r1
+        BCC     %02                     ; loop back until adapter is ready
+
+; Adapter ready - loop around all the bits in the byte
+; Note that each byte is actually 4 bits to the LCD module,
+; so a even number must be sent or the display will get out
+; of sync until the next display reset sequence.
+
+        LDR     r5,%10                  ; bits-per-byte counter
+        ADD     pc,pc,r0
+10
+        &       (1 :SHL: 24)
+        LDR     r3,%11
+        ADD     pc,pc,r0 
+11
+        &       ts_recover_time         ; wait before sending data bits
+12      ADDS    r3,r3,r3                ; for byte timing.
+        BCC     %12
+
+        ; Send a single bit : always 2 pulses for 0
+
+13      LDR     r3,[r2]
+        LDR     r3,[r2]
+
+        ; and wait for the inter-bit time
+
+        LDR     r3,%14
+        ADD     pc,pc,r0
+14
+        &       ts_recover_time
+15      ADDS    r3,r3,r3                ; 16-loop delay
+        BCC     %15
+
+        ; repeat until 8 bits are sent
+
+        ADDS    r5,r5,r5
+        BCC     %13
+
+        ; do a RD operation to strobe the data out
+
+        LDR     r5,%16
+        ADD     pc,pc,r0
+16
+        &       1 :SHL: (32 - 12)
+17
+        LDR     r3,[r2]
+        ADDS    r5,r5,r5
+        BCC     %17
+
+        ; repeat until a sufficient number of nuls are done
+
+        ADDS    r7,r7,r1                ; count down loop counter
+        BCS     %02
+
+        ADD    pc,r0,r14                ; back to caller
+
+
+        END
diff --git a/TestSrc/Ioc b/TestSrc/Ioc
new file mode 100644
index 0000000000000000000000000000000000000000..1c37914c8beb0bb94e4ea6804cef835ca2da3370
--- /dev/null
+++ b/TestSrc/Ioc
@@ -0,0 +1,102 @@
+; > TestSrc.IOC
+
+        TTL RISC OS 2+ POST IO controller
+;
+; This initial IOC test simply reports the content of the IRQ and FIRQ
+; registers, to show any unexpected pending IRQs. 
+; Certain of these should really be cleared, and the effect of an
+; interrupt tested.
+;
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name            Comment
+; ----          ----            -------
+; 18-Dec-89     ArtG            Initial version
+; 29-Nov-91     ArtG            Added IOC bus test using mask registers
+; 20-Jun-93     ArtG            Modified for 29-bit IOMD test
+; 18-Nov-94     RCM             Morris changes
+;
+;
+;------------------------------------------------------------------------
+
+        [ IO_Type = "IOMD"
+ts_IObase       *       IOMD_Base
+ts_IOmask       *       &00fffff0       ;&1fffffff
+ts_IOreg1       *       IOMD_VIDEND     ;IOMD_VIDCUR
+ts_IOreg2       *       IOMD_VIDSTART
+ts_IObswap      *       32
+ts_IOMD_ID      *       &D4E7
+ [ MorrisSupport
+ts_IOMD_IDmorris *      &5B98
+ ]
+        |
+ts_IObase       *       IOC
+ts_IOmask       *       &ff0000
+ts_IOreg1       *       IOCIRQMSKA
+ts_IOreg2       *       IOCIRQMSKB
+ts_IObswap      *       16
+        ]
+
+ts_IOCreg
+        MOV     r0,#0                   ; zero error accumulator
+        LDR     r3, =ts_IObase
+        MOV     r1,#(1 :SHL: 31)          ; initialise bit-set test mask
+0
+        MVN     r2,r1                   ; make bit-clear test mask
+        LDR     r4, =ts_IOmask
+        ANDS    r4,r1,r4
+        BEQ     %FT1                    ; skip if this bit isn't tested
+        STR     r1,[r3,#ts_IOreg1]
+        STR     r2,[r3,#ts_IOreg2]
+        LDR     r4,[r3,#ts_IOreg1]
+;        EOR     r4, r4, r1, LSR #ts_IObswap     ; check bit-set test was OK
+        EOR     r4, r4, r1           ; check bit-set test was OK
+        ORR     r0, r0, r4              ; accumulate errors in r0
+        LDR     r4,[r3,#ts_IOreg2]
+;        EOR     r4, r4, r2, LSR #ts_IObswap     ; check bit-clear test was OK
+        EOR     r4, r4, r2           ; check bit-clear test was OK
+        ORR     r0, r0, r4              ; accumulate errors in r0
+1
+        MOV     r1, r1, LSR #1          ; shift mask downwards
+        TEQ     r1,#0
+        BNE     %BT0                    ; and loop until all bits tested
+
+        LDR     r8, =ts_IOmask
+        ANDS    r8, r0, r8
+        MOV     pc,r14                   ; return error if any bit failed
+
+ts_IOCstat
+        LDR     r3, =ts_IObase
+        MOV     r0,#0
+        [ IO_Type = "IOMD"
+        LDRB    r1,[r3,#IOMD_ID1]
+        ORR     r0,r0,r1, LSL #(32-24)
+        LDRB    r1,[r3,#IOMD_ID0]
+        ORR     r0,r0,r1
+        LDR     r1,=ts_IOMD_ID
+        CMPS    r0,r1                   ; check IOMD identity
+ [ MorrisSupport
+        LDRNE   r1,=ts_IOMD_IDmorris    ; allow for Morris variant
+        CMPNES  r0,r1
+ ]
+        MOV     r0,r0,LSL #16
+        LDRB    r1,[r3,#IOMD_VERSION]
+        ORR     r8,r0,r1, LSL #12
+        MOV     pc,r14
+        |
+        LDRB    r1,[r3,#IOCControl]
+        ORR     r0,r0,r1, LSL #(32 - 8)
+        LDRB    r1,[r3,#IOCIRQSTAA]
+        ORR     r0,r0,r1, LSL #(32 - 16)
+        LDRB    r1,[r3,#IOCIRQSTAB]
+        ORR     r0,r0,r1, LSL #(32 - 24)
+        LDRB    r1,[r3,#IOCFIQSTA]
+        ORR     r8,r0,r1
+        ANDS    r1,r1,#0                ; return zero flag (OK)
+
+        MOV     pc,r14
+        ]
+
+        END 
+ 
diff --git a/TestSrc/MEMC1 b/TestSrc/MEMC1
new file mode 100644
index 0000000000000000000000000000000000000000..847df36843534aee6f4bee0f4244300f5e4bff17
--- /dev/null
+++ b/TestSrc/MEMC1
@@ -0,0 +1,552 @@
+; > MEMC1
+
+; MEMC interface file - MEMC1 version
+
+; Created by TMD 10-Aug-90
+
+VInit   * &03600000
+VStart  * &03620000
+VEnd    * &03640000
+CInit   * &03660000
+; SStart  * &03680000
+; SEnd    * &036A0000
+; SPtr    * &036C0000
+
+; *****************************************************************************
+;
+;       SetDAG - Program DMA address generator R1 with physical address R0
+;
+; in:   r0 = physical address
+;       r1 = index of DMA address generator to program, as defined in vdudecl
+;
+; out:  All registers preserved, operation ignored if illegal
+;
+
+        [ {FALSE}
+SetDAG  ENTRY   "r0"
+        CMP     r1, #MEMCDAG_MaxReason
+        EXIT    HI
+        ADR     r14, DAGAddressTable
+        LDR     r14, [r14, r1, LSL #2]          ; load base address in MEMC1
+        MOV     r0, r0, LSR #4                  ; bottom 4 bits irrelevant
+        CMP     r0, #(1 :SHL: 15)               ; ensure in range
+        ORRCC   r14, r14, r0, LSL #2
+        STRCC   r14, [r14]                      ; any old data will do
+        EXIT
+
+        GBLA    DAGIndex
+DAGIndex SETA   0
+
+        MACRO
+        DAGTab  $reason, $address
+        ASSERT  ($reason)=DAGIndex
+        &       $address
+DAGIndex SETA   DAGIndex + 1
+        MEND
+
+DAGAddressTable
+        DAGTab  MEMCDAG_VInit, VInit
+        DAGTab  MEMCDAG_VStart, VStart
+        DAGTab  MEMCDAG_VEnd, VEnd
+        DAGTab  MEMCDAG_CInit, CInit
+        ]
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; CAM manipulation utility routines
+
+BangCamUpdate ROUT
+
+; R2 = CAM entry no
+; R3 = logaddr
+; R9 = current MEMC value
+; R11 = PPL
+; set and update tables
+
+        MOV     R4, #0
+        LDR     R4, [R4, #CamEntriesPointer]
+        ORR     r0, r3, r11, LSL #28  ; top nibble is PPL
+        STR     r0, [R4, R2, LSL #2]
+
+BangCam
+
+; r0 corrupted
+; r1 corrupted
+; R2 = CAM entry no
+; R3 = logaddr
+; r4 corrupted
+; r5 spare!
+; r6 corrupted
+; r7, r8 spare
+; R9 = current MEMC value
+; r10 spare
+; R11 = PPL
+; r12 spare
+
+        AND     R4, R9, #&C           ; pagesize
+        ADR     R0, PageMangleTable
+        LDR     R0, [R0, R4]          ; load data table pointer
+        MOV     R4, #0
+01      LDR     R1, [R0], #4
+        CMP     R1, #-1
+        BEQ     %FT02
+        AND     R6, R2, R1
+        LDR     R1, [R0], #4
+        CMP     R1, #0
+        RSBMI   R1, R1, #0
+        ORRPL   R4, R4, R6, LSL R1
+        ORRMI   R4, R4, R6, LSR R1
+        B       %BT01
+
+02      LDR     R1, [R0], #4
+        CMP     R1, #-1
+        BEQ     %FT03
+        AND     R6, R3, R1
+        LDR     R1, [R0], #4
+        CMP     R1, #0
+        RSBMI   R1, R1, #0
+        ORRPL   R4, R4, R6, LSL R1
+        ORRMI   R4, R4, R6, LSR R1
+        B       %BT02
+
+03      ORR     R4, R4, #CAM
+        ORR     R4, R4, R11, LSL #8     ; stuff in PPL
+        STR     R4, [R4]                ; and write it
+        MOV     PC, LR
+
+; Data to drive CAM setting
+
+PageMangleTable
+        &       PageMangle4K
+        &       PageMangle8K
+        &       PageMangle16K
+        &       PageMangle32K
+
+; For each page size, pairs of masks and shift factors to put the bits in the
+; right place. Two sets: operations on Physical Page Number, operations on
+; Logical Page Number.
+
+; Shifts are Shift Left values (<<). Each section terminated by -1
+
+PageMangle4K
+; PPN:
+        &       2_011111111
+        &       0                       ; bits in right place
+        &       -1
+; LPN:
+        &       2_1100000000000:SHL:12
+        &       (11-12)-12              ; LPN[12:11] -> A[11:10]
+        &       2_0011111111111:SHL:12
+        &       (22-10)-12              ; LPN[10:0 ] -> A[22:12]
+        &      -1
+
+PageMangle8K
+; PPN:
+        &       2_010000000
+        &       7-7                     ; PPN[7]   -> A[7]
+        &       2_001000000
+        &       0-6                     ; PPN[6]   -> A[0]
+        &       2_000111111
+        &       6-5                     ; PPN[5:0] -> A[6:1]
+        &       -1
+; LPN:
+        &       2_110000000000:SHL:13
+        &       (11-11)-13              ; LPN[11:10] -> A[11:10]
+        &       2_001111111111:SHL:13
+        &       (22-9)-13               ; LPN[9:0]   -> A[22:13]
+        &       -1
+
+PageMangle16K
+; PPN:
+        &       2_010000000
+        &       7-7                     ; PPN[7]   -> A[7]
+        &       2_001100000
+        &       1-6                     ; PPN[6:5] -> A[1:0]
+        &       2_000011111
+        &       6-4                     ; PPN[4:0] -> A[6:2]
+        &       -1
+; LPN:
+        &       2_11000000000:SHL:14
+        &       (11-10)-14              ; LPN[10:9] -> A[11:10]
+        &       2_00111111111:SHL:14
+        &       (22-8)-14               ; LPN[8:0]  -> A[22:14]
+        &       -1
+
+PageMangle32K
+; PPN:
+        &       2_100000000
+        &       12-8                    ; PPN[8] -> A[12]
+        &       2_010000000
+        &       7-7                     ; PPN[7] -> A[7]
+        &       2_001000000
+        &       1-6                     ; PPN[6] -> A[1]
+        &       2_000100000
+        &       2-5                     ; PPN[5] -> A[2]
+        &       2_000010000
+        &       0-4                     ; PPN[4] -> A[0]
+        &       2_000001111
+        &       6-3                     ; PPN[3:0] -> A[6:3]
+        &       -1
+; LPN:
+        &       2_1100000000:SHL:15
+        &       (11-9)-15               ; LPN[9:8] -> A[11:10]
+        &       2_0011111111:SHL:15
+        &       (22-7)-15               ; LPN[7:0] -> A[22:15]
+        &       -1
+
+PageSizes
+        &       4*1024                  ; 0 is 4K
+        &       8*1024                  ; 4 is 8K
+        &       16*1024                 ; 8 is 16
+        &       32*1024                 ; C is 32
+
+PageShifts
+        =       12, 13, 0, 14           ; 1 2 3 4
+        =       0,  0,  0, 15           ; 5 6 7 8
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_UpdateMEMC: Read/write MEMC1 control register
+
+SSETMEMC ROUT
+
+        AND     r10, r0, r1
+        MOV     r12, #0
+        TEQP    pc, #SVC_mode+I_bit+F_bit
+        LDR     r0, [r12, #MEMC_CR_SoftCopy] ; return old value
+        BIC     r11, r0, r1
+        ORR     r11, r11, R10
+        BIC     r11, r11, #&FF000000
+        BIC     r11, r11, #&00F00000
+        ORR     r11, r11, #MEMCADR
+        STR     r11, [r12, #MEMC_CR_SoftCopy]
+        STR     r11, [r11]
+        TEQP    pc, #SVC_mode+I_bit
+        ExitSWIHandler
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       ClearPhysRAM - Routine to clear "all" memory
+;
+; While this routine is running, keyboard IRQs may happen. For this reason
+; it avoids LogRAM 0..31 (where hardware IRQ vector is) and PhysRAM
+; 0..31 where the IRQ workspace is.
+;
+
+ClearPhysRAM ROUT
+        MOV     R0, #0
+        MOV     R1, #0
+        MOV     R2, #0
+        MOV     R3, #0
+        MOV     R4, #0
+        MOV     R5, #0
+        MOV     R6, #0
+        MOV     R11, #0
+        MOV     R8, #PhysRam
+        CMP     R13, #512*1024
+        ADDEQ   R10, R8, #(512-64)*1024 ; get address that's logram 0
+        ADDNE   R10, R8, #512*1024
+        ADD     R13, R13, #PhysRam      ; end of memory
+        ADD     R8, R8, #4*8            ; skip minimal startup workspace
+10      CMP     R8, R10
+        ADDEQ   R8, R8, #4*8            ; skip physram that's logram 0
+        STMNEIA R8!, {R0-R6, r11}
+        CMP     R8, R13
+        BNE     %BT10
+        SUB     R13, R13, #PhysRam
+
+        LDR     R0, =OsbyteVars + :INDEX: LastBREAK
+        MOV     R1, #&80
+        STRB    R1, [R0]                ; flag the fact that RAM cleared
+        MOV     pc, lr
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       InitMEMC - Initialise memory controller
+;
+
+InitMEMC ROUT
+        LDR     R0, ResetMemC_Value
+        STR     R0, [R0]     ; set ROM access times, refresh on flyback, no DMA
+        MOV     pc, lr
+
+; -> MemSize
+
+; (non-destructive) algorithm to determine MEMC RAM configuration
+;
+; Dave Flynn and Alasdair Thomas
+; 17-March-87
+;
+; Spooling checkered by NRaine and SSwales !
+; 8MByte check bodged in by APT
+;
+; NOTE: Routines MemSize and TimeCPU are called by the power-on test software,
+; so their specifications MUST not change.
+;
+; Set MEMC for 32-k page then analyse signature of possible
+; external RAM configurations...
+; The configurations are:
+;
+; Ram Size    Page Size    Configuration    (Phys RAM) Signature
+;--------------------------------------------------------------------
+;  16MByte      32k        4*32*1Mx1         A13,A20,A21,A22,A23,A23.5 distinct
+;  16MByte      32k        16*8*256kx4       A13,A20,A21,A22,A23,A23.5 distinct
+;
+;  12MByte      32k        3*32*1Mx1         A13,A20,A21,A22,A23 OK, A23.5 fail
+;  12MByte      32k        12*8*256kx4       A13,A20,A21,A22,A23 OK, A23.5 fail
+;
+;   8MByte      32k        2*32*1Mx1         A13,A20,A21,A22 distinct, A23 fail
+;   8MByte      32k         8*8*256kx4       A13,A20,A21,A22 distinct, A23 fail
+; 
+;   4Mbyte      32k          32*1Mx1         A13,A21,A20 distinct, A22,A23 fail
+;   4Mbyte      32k         4*8*256kx4       A13,A21,A20 distinct, A22,A23 fail
+;
+;   2Mbyte      32k    expandable 2*8*256kx4 A13,A20 distinct, A21 fails
+;   2Mbyte ???  16k      fixed 2*8*256kx4    A13,A21 distinct, A20 fails
+;   
+;   1Mbyte       8k          32*256kx1       A13,A20 fail, A19,A18,A12 distinct
+;   1Mbyte       8k           8*256kx1       A13,A20 fail, A19,A18,A12 distinct
+;   1Mbyte       8k          4*8*64kx4       A13,A20 fail, A19,A18,A12 distinct
+;
+; 512Kbyte       8k    expandable 2*8*64kx4  A13,A20,A19 fail, A12,A18 distinct
+; 512Kbyte       4k      fixed 2*8*64kx4     A13,A20,A12 fail, A19,A18 distinct
+;
+; 256Kbyte       4K           8*64kx4        A13,A20,A12,A18 fail, A21,A19 ok  
+; 256Kbyte       4K          32*64kx1        A13,A20,A12,A18 fail, A21,A19 ok  
+;
+
+Z_Flag     * &40000000
+
+; MemSize routine... enter with 32K pagesize set
+; R0 returns page size
+; R1 returns memory size
+; R2 returns value set in MEMC
+; uses R3-R7
+
+MemSize ROUT
+        MOV     r7, lr
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #A13
+        BL      DistinctAddresses
+        BNE     %10
+        ADD     r1, r0, #A21
+        BL      DistinctAddresses
+        MOVNE   r0, #Page32K
+        MOVNE   r1, #2048*1024
+        BNE     MemSizeDone
+
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #4*1024*1024
+        BL      DistinctAddresses
+        MOVNE   r0, #Page32K
+        MOVNE   r1, #4*1024*1024
+        BNE     MemSizeDone
+
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #8*1024*1024
+        BL      DistinctAddresses
+        MOVNE   r0, #Page32K
+        MOVNE   r1, #8*1024*1024
+        BNE     MemSizeDone
+
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #12*1024*1024
+        BL      DistinctAddresses
+        MOV     r0, #Page32K
+        MOVNE   r1, #12*1024*1024
+        MOVEQ   r1, #16*1024*1024
+        B       MemSizeDone
+
+10      ADD     r1, r0, #A20
+        BL      DistinctAddresses
+        BNE     %20
+        MOV     r0, #Page16K
+        MOV     r1, #2048*1024
+        B       MemSizeDone
+
+20      ADD     r1, r0, #A19
+        BL      DistinctAddresses
+        BEQ     %30
+        MOV     r0, #Page8K
+        MOV     r1, #512*1024
+        B       MemSizeDone
+
+30      ADD     r1, r0, #A18
+        BL      DistinctAddresses
+        BEQ     %40
+        MOV     r0, #Page4K
+        MOV     r1, #256*1024
+        B       MemSizeDone
+
+40      ADD     r1, r0, #A12
+        BL      DistinctAddresses
+        BEQ     %50
+        MOV     r0, #Page4K
+        MOV     r1, #512*1024
+        B       MemSizeDone
+
+50      MOV     r0, #Page8K
+        MOV     r1, #1024*1024
+
+MemSizeDone
+        LDR     r2, ResetMemC_Value
+        BIC     r2, r2, #&C
+        ORR     r2, r2, r0
+        STR     r2, [r2]                        ; set MEMC to right state
+        MOV     pc, r7
+
+
+; DistinctAddresses routine...
+; r0,r1 are the addresses to check
+; uses r2-5
+; writes interleaved patterns (to prevent dynamic storage...)
+; checks writing every bit low and high...
+; return Z-flag set if distinct
+
+DistinctAddresses ROUT
+        LDR     r2, [r0] ; preserve
+        LDR     r3, [r1]
+        LDR     r4, Pattern
+        STR     r4, [r0] ; mark first
+        MOV     r5, r4, ROR #16
+        STR     r5, [r1] ; mark second
+        LDR     r5, [r0]
+        CMP     r5, r4 ; check first
+        BNE     %10    ; exit with Z clear
+        LDR     r5, [r1] ; check second
+        CMP     r5, r4, ROR #16 ; clear Z if not same
+        BNE     %10
+; now check inverse bit writes
+        STR     r4, [r1] ; mark second
+        MOV     r5, r4, ROR #16
+        STR     r5, [r0] ; mark first
+        LDR     r5, [r1]
+        CMP     r5, r4 ; check second
+        BNE     %10   ; exit with Z clear
+        LDR     r5, [r0] ; check first
+        CMP     r5, r4, ROR #16 ; clear Z if not same
+10      STR     r3, [r1] ; restore
+        STR     r2, [r0]
+        ORREQ   lr, lr, #Z_Flag
+        BICNE   lr, lr, #Z_Flag
+        MOVS    pc, lr
+
+Pattern
+        &       &AAFF5500 ; shiftable bit check pattern
+
+; init state with masked out page size
+
+ResetMemC_Value
+        & &E010C :OR: MEMCADR       ; slugged ROMs + flyback refresh only + 32K page
+
+; Constants
+;
+A21 * 1:SHL:21
+A20 * 1:SHL:20
+A19 * 1:SHL:19
+A18 * 1:SHL:18
+A13 * 1:SHL:13
+A12 * 1:SHL:12
+
+Page32K * &C ; in MEMC control reg patterns...
+Page16K * &8
+Page8K  * &4
+Page4K  * &0
+
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r0-r6 trashable
+;       r9 = Current MEMC CR
+
+; Out   r9 MEMC value with slowest ROM speed, correct pagesize
+;       r7 processor speed in kHz, tbs -> MEMC1a
+
+ncpuloops * 1024 ; don't go longer than 4ms without refresh !
+nmulloops * 128
+
+TimeCPU ROUT
+
+        BIC     r9, r9, #3 :SHL: 8
+        STR     r9, [r9]                ; turn off refresh for a bit
+
+; Time CPU/Memory speed
+
+        LDR     r1, =&7FFE              ; 32K @ 2MHz = ~16ms limit
+        MOV     r3, #IOC
+
+        MOV     r0, r1, LSR #8
+        STRB    r1, [r3, #Timer1LL]
+        STRB    r0, [r3, #Timer1LH]
+        LDR     r0, =ncpuloops
+        STRB    r0, [r3, #Timer1GO]     ; start the timer NOW
+        B       %FT10                   ; Looks superfluous, but is required
+                                        ; to get ncpuloops pipeline breaks
+
+10      SUBS    r0, r0, #1              ; 1S
+        BNE     %BT10                   ; 1N + 2S
+
+        STRB    r0, [r3, #Timer1LR]     ; latch count NOW
+        LDRB    r2, [r3, #Timer1CL]
+        LDRB    r0, [r3, #Timer1CH]
+        ADD     r2, r2, r0, LSL #8      ; count after looping is ...
+
+        SUB     r2, r1, r2              ; decrements !
+        MOV     r2, r2, LSR #1          ; IOC clock decrements at 2MHz
+
+; Time CPU/MEMC Multiply time
+
+        MOV     r4, #-1                 ; Gives worst case MUL
+
+        MOV     r0, r1, LSR #8
+        STRB    r1, [r3, #Timer1LL]
+        STRB    r0, [r3, #Timer1LH]
+        LDR     r0, =nmulloops
+        STRB    r0, [r3, #Timer1GO]     ; start the timer NOW
+        B       %FT20                   ; Looks superfluous, but is required
+                                        ; to get nmulloops pipeline breaks
+
+20      MUL     r5, r4, r4              ; 1S + 16I
+        MUL     r5, r4, r4              ; 1S + 16I
+        SUBS    r0, r0, #1              ; 1S
+        BNE     %BT20                   ; 1N + 2S
+
+        STRB    r0, [r3, #Timer1LR]     ; latch count NOW
+        LDRB    r4, [r3, #Timer1CL]
+        LDRB    r0, [r3, #Timer1CH]
+        ADD     r4, r4, r0, LSL #8      ; count after looping is ...
+
+        SUB     r4, r1, r4              ; decrements !
+        MOV     r4, r4, LSR #1          ; IOC clock decrements at 2MHz
+
+        ORR     r9, r9, #1 :SHL: 8      ; set refresh on flyback
+        STR     r9, [r9]                ; restore MEMC state a.s.a.p.
+
+; In ROM - each cpu loop took 4R cycles @ 8/f*500ns/cycle
+
+        LDR     r0, =4*(8*500/1000)*ncpuloops*1000
+        DivRem  r7, r0, r2, r1          ; r2 preserved
+        MOV     r0, #&80                ; At 8 MHz and below, run fast ROMs
+        LDR     r1, =8050               ; Over 8 MHz, need medium ROMs
+        CMP     r7, r1
+        MOVHI   r0, #&40
+        LDR     r1, =13000              ; Over 13 MHz, need slowest ROMs
+        CMP     r7, r1
+        MOVHI   r0, #&00
+        ORR     r9, r9, r0
+        STR     r9, [r9]                ; Set ROM speed appropriately
+
+ ASSERT ncpuloops = 8*nmulloops ; for given ratio cutoff <------------
+
+        MOV     r4, r4, LSL #10         ; *1024 to get resolution on divide
+        DivRem  r0, r4, r2, r1
+        LDR     r1, =1100               ; Cutoff point; MEMC1 longer than this
+        CMP     r0, r1
+        ORRLO   r7, r7, #1 :SHL: 16     ; Note MEMC1a prescence
+
+        MOV     pc, lr
+
+; Typical figures give (in ROM at 8MHz):
+
+; MEMC1  2048 CPU, 2432 MEMC -> MUL ratio 1216
+; MEMC1a 2048       864                    432
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        END
diff --git a/TestSrc/Mem1IOMD b/TestSrc/Mem1IOMD
new file mode 100644
index 0000000000000000000000000000000000000000..6b966bf72fdd9193df1ab31808623b2676687a75
--- /dev/null
+++ b/TestSrc/Mem1IOMD
@@ -0,0 +1,691 @@
+; > TestSrc.Mem1IOMD
+
+        TTL RISC OS 2+ POST memory linetest
+;
+; This test code is used to perform basic integrity tests on DRAM.
+; It doesn't test all locations - just walks patterns through data
+; and address lines.
+;
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name            Comment
+; ----          ----            -------
+; 1-Jun-93      ArtG            Derived from Mem1 for use on Medusa
+; 18-Nov-94     RCM             Morris changes
+;
+;
+;------------------------------------------------------------------------
+
+;
+; Test the data and address and byte strobe lines for uniqueness.
+;
+
+        LTORG
+        ROUT
+
+1
+        =       "VRAM  :",0
+2
+        =       "VRAM-F",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+3
+        =       "DRAM ",&ff,":",0
+4
+        =       "Data  :",0
+5
+        =       &88,&ff,&ff," MByte",0
+
+        ALIGN
+
+ts_LineTest
+ [ MorrisSupport
+        MOV     r12, #IOMD_Base
+
+        LDRB    r0, [r12, #IOMD_ID0]
+        CMP     r0, #&98
+        LDRB    r0, [r12, #IOMD_ID1]
+        CMPEQ   r0, #&5B
+        BNE     ts_LineTestIOMD                         ; NOT MORRIS assume Medusa hardware
+
+;
+; ts_LineTest for Morris
+;
+        MOV     r11, #IOMD_DRAMWID_DRAM_32bit * &0F     ;set all 4 banks to be 32bit initially
+        MOV     r14, #IOMD_Base
+        STRB    r11, [r14, #IOMD_DRAMWID]
+        MOV     r0,#MMUC_D                              ; enable 32-bit addressing of data
+        SetCop  r0,CR_Control
+
+        MOV     r0,#0
+        MOV_fiq r9,r0                                   ; r9-fiq records low DRAM address for use elsewhere
+
+        MOV     r10, #0                                 ;indicate no RAM found yet
+        MOV     r9, #IOMD_DRAMWID_DRAM_16bit            ;bit to OR into DRAMWID to set 16bit
+        MOV     r12, #DRAM0PhysRam
+;
+; r12    DRAM address
+; r9    IOMD_DRAMWID_DRAM_16bit for current DRAM bank
+; r11   current IOMD_DRAMWID register contents
+;
+;ExamineDRAMBank                                        ;examine first/next DRAM bank
+2005
+;
+        MOV     r8,r12,LSL #2                           ; indicate bank under test
+        AND     r8,r8,#(3 :SHL: 28)
+        ADR     r4,%BT3
+        BL      ts_SendText
+;
+        MOV     r8,#0                                   ; r8 indicates RAM found in this bank
+
+        LDMIA   r12, {r1, r2}                            ;Preserve the two locations that we widdle on
+
+        ADRL    r3, funnypatterns                       ;We write different values to two locations
+        LDMIA   r3, {r3, r4}                            ; incase bus capacitance holds our value
+        STMIA   r12, {r3, r4}
+        LDMIA   r12, {r5, r6}                            ;Reread test locations
+        EORS    r5, r5, r3                              ;Both locations should read correctly
+        EOR     r6, r6, r4                              ; if memory is 32bits wide
+       ;TEQ     r5, #0
+        TEQEQ   r6, #0
+        BEQ     %FT2010                                   ;32bit wide memory
+
+        TST     r5, #&00FF                              ;If the bottom 16bits of each location
+        TSTEQ   r5, #&FF00                              ; are correct, the memory is 16bits wide
+        TSTEQ   r6, #&00FF
+        TSTEQ   r6, #&FF00
+        BNE     %FT2050                                 ;No memory in this bank
+
+        ORR     r11, r11, r9                            ;Bank is 16bits wide
+2010
+        STMIA   r12, {r1, r2}                            ;Restore the two locations we widdled on
+                                                        ;Must do BEFORE poking the DRAMWID register
+        MOV     r14, #IOMD_Base                         ;
+        STRB    r11, [r14, #IOMD_DRAMWID]               ;
+;
+; minimum ram test
+;
+        MOV     r0, r12
+        ADD     r1, r12, #A18
+        BL      DistinctAddresses
+        BNE     %FT2050                                 ;Less than 512KBytes, so ignore this bank
+
+        MOV_fiq r2,r9                                   ; if this is the first bank of DRAM or VRAM,
+        TEQS    r2,#0                                   ; put it's address in r9_fiq
+        BNE     %FT2012
+        MOV_fiq r9,r0
+2012
+
+
+        MOV     r6, #0                                  ;Fragment address
+        MOV     r7, #0                                  ;Fragment address
+        MOV     r8, #A19                                ; now go through address lines A19-A25
+2015
+        MOV     r0, r12
+        ADD     r1, r12, r8                              ; see if this address line is unique
+        BL      DistinctAddresses
+        BNE     %FT2020                                   ; if we've failed then r8 is true size, so exit
+        MOV     r8, r8, LSL #1                          ; else shift up to next
+        TEQ     r8, #A26                                ; only test up to A25
+        BNE     %BT2015
+        BEQ     %FT2035                           ;Bank fully occupied, DON'T test for higher fragments
+2020
+;
+; Found some DRAM, at address r0, size r8.
+; There may be one or two higher address lines connected, so scan upto A25 looking for
+; extra DRAM chunks.
+;
+        MOV     r1, r8
+2025
+        TEQ     r1, #A25
+        BEQ     %FT2035                           ;No higher active address lines found ie one lump of DRAM
+        ADD     r1, r0, r1,LSL #1
+        BL      DistinctAddresses
+        SUB     r1, r1, r0                              ;Recover bit value
+        BNE     %BT2025
+;
+; Got a 2nd fragment, at address r1 (also of size r8)
+;
+        MOV     r6, r1
+2030
+        TEQ     r1, #A25
+        BEQ     %FT2035                           ;No higher active address lines found ie two lumps of DRAM
+        ADD     r1, r0, r1,LSL #1
+        BL      DistinctAddresses
+        SUB     r1, r1, r0                              ;Recover bit value
+        BNE     %BT2030
+;
+; Got another active address line (ie total four fragments)
+;
+        MOV     r7, r1
+;
+2035
+;
+; Found 1, 2 or 4 lumps of DRAM
+;
+;NoRamInBank
+2050
+        MOV     r13, r8
+        TEQ     r6, #0
+        MOVNE   r13, r13, LSL #1
+        TEQNE   r7, #0
+        MOVNE   r13, r13, LSL #1                        ; remember size of this bank in bytes
+        MOV     r8,r13,LSL #(24 - 20)                   ; and display it in 2 digits, in MBytes.
+        ADR     r4,%BT5
+        BL      ts_MoreText
+
+
+        ADRL    r4,%FT73                                ; announce data line test
+        BL      ts_SendText
+        MOV     r1,r12                                  ; do walking bit test
+        BL      ts_Dataline
+        BEQ     %FT2055                                 ; looks OK, carry on to next bank
+
+        ADRL    r4,%FT74                                ; bit test failed, so report it
+        MOV     r8,r0
+        BL      ts_SendText                             ; and bit fault mask
+
+        CMPS    r13,#0                                  ; was any RAM thought to be here ?
+        BEQ     %FT2055
+        FAULT   #R_LINFAILBIT                           ; if so, it's faulty.
+        MOV     r13,#0                                  ; so ignore it
+2055
+2055
+;
+; If there was some RAM found here, and it passed the dataline test,
+; do the address and bytestrobe tests on it too.
+;
+        CMPS    r13,#0
+        BEQ     %FT2060
+
+        ADRL    r4,%FT75                                ; announce start of address line test
+        BL      ts_SendText
+        MOV     r1,r12                                  ; test address lines in this block
+        MOV     r0,r13, LSR #2                          ; bank may be in 4 fragments
+        BL      ts_Addrline
+        BEQ     %FT2056
+        ADRL    r4,%FT76                                ; failed - report error mask
+        MOV     r8,r0
+        BL      ts_SendText
+        FAULT   #R_LINFAILBIT                           ; and record failure
+        MOV     r13,#0                                  ; then forget this memory block
+
+2056
+        ADR     r4,%FT77                                ; announce start of byte test
+        BL      ts_SendText
+        MOV     r1,r12
+        BL      ts_Byteword
+        BEQ     %FT2060
+        ADR     r4,%FT78                                ; failed - report error mask
+        MOV     r8,r0,LSL #16
+        BL      ts_SendText
+        FAULT   #R_LINFAILBIT                           ; and record failure
+        MOV     r13,#0                                  ; then forget this memory block
+2060
+
+
+; If the RAM found still seems OK, add it's size into the r10 accumulator
+; Working or not, carry on to check the next bank.
+
+        ADD     r10,r10,r13                             ; accumulate DRAM if any found 
+        ADD     r12, r12, #DRAM1PhysRam-DRAM0PhysRam    ; move onto next bank
+        MOV     r9, r9, LSL #1                          ; shunt up position in DRAMWID
+        CMP     r9, #&0010                              ; if more banks to do
+        BLT     %BT2005                                 ; then loop
+
+        ADR     r4,%FT70
+        BL      ts_SendText                             ; None found .. print message
+
+        MOVS    r8,r10,LSL #(24 - 20)                   ; all finished ..
+        ADREQL  r4,%FT71                                ; did we find any DRAM?
+        ADRNEL  r4,%FT72
+        BNE     %FT2065
+        FAULT   #R_LINFAILBIT                           ; fault if we didn't
+2065
+        BL      ts_MoreText
+        B       ts_endline
+
+
+ ]
+
+ts_LineTestIOMD
+        ADR     r4,%BT1
+        BL      ts_SendText                             ; Start data line tests on VRAM
+
+        MOV     r0,#0
+        MOV_fiq r9,r0                                   ; r9-fiq records VRAM or low DRAM address
+
+        MOV     r12, #IOMD_Base
+        MOV     r2, #IOMD_VREFCR_VRAM_256Kx64 :OR: IOMD_VREFCR_REF_16 ; assume 2 banks of VRAM by default
+        STRB    r2, [r12, #IOMD_VREFCR]
+
+; Find the size, using MemSize's method
+
+        MOV     r0, #VideoPhysRam                       ; point at VRAM
+        ADD     r1, r0, #A2                             ; test A2
+        BL      DistinctAddresses
+        MOVEQ   r9, #2                                  ; we've got 2M of VRAM
+        BEQ     %FT21
+
+        MOV     r2, #IOMD_VREFCR_VRAM_256Kx32 :OR: IOMD_VREFCR_REF_16
+        STRB    r2, [r12, #IOMD_VREFCR]
+        ADD     r1, r0, #A2                             ; check for any VRAM at all
+        BL      DistinctAddresses
+        MOVEQ   r9, #1                                  ; we've got 1M of VRAM
+        MOVNE   r9, #0                                  ; no VRAM
+21
+        BNE     %FT22
+        MOV_fiq r9,r0                                   ; record VRAM address
+        FAULT   #R_VRAM                                 ; indicate VRAM present
+
+; Report size .. if this is non-zero and the data line test fails,
+; RISC OS will have problems.
+
+22
+        ADR     r4,%BT5                                 ; Add size (in hex Mbyte)
+        MOV     r8,r9, LSL #24                          ; to "VRam : " message
+        BL      ts_MoreText     
+
+; Worked out what size VRAM is, and set up IOMD register. 
+; Do a data line test on the resulting array, repeated at oddword address to 
+; ensure both banks get tested with walking 0 and walking 1
+
+        ADR     r4,%BT4
+        BL      ts_SendText
+        MOV     r1, #VideoPhysRam
+        BL      ts_Dataline
+        ADDEQ   r1,r1,#4
+        BLEQ    ts_Dataline
+        BEQ     %FT25                   ; looks OK - carry on with VRAM test
+;
+; Data line test failed. Report the bitmap that failed, then carry on.
+;
+        ADR     r4,%BT2
+        MOV     r8,r0                   ; report data fault mask
+        BL      ts_SendText
+        B       %FT30
+
+;
+; If there was some VRAM found here, and it passed the dataline test,
+; do the address and bytestrobe tests on it too.
+;
+
+25
+        ADRL    r4,%FT75                                ; announce start of address line test
+        BL      ts_SendText
+        MOV     r1,#VideoPhysRam
+        MOV     r0,r9,LSL #20                           ; size in MB determined before dataline test
+        BL      ts_Addrline
+        BEQ     %FT26
+        ADRL    r4,%FT76                                ; failed - report error mask
+        MOV     r8,r0
+        BL      ts_SendText
+        FAULT   #R_LINFAILBIT                           ; and record failure
+        B       %FT30
+26
+        ADRL    r4,%FT77                                ; announce start of byte test
+        BL      ts_SendText
+        MOV     r1,#VideoPhysRam
+        BL      ts_Byteword
+        ADDEQ   r1,r1,#4                                ; retest at an oddword boundary
+        BLEQ    ts_Byteword
+        BEQ     %FT27
+        ADRL    r4,%FT78                                ; failed - report error mask
+        MOV     r8,r0,LSL #16
+        BL      ts_SendText
+        FAULT   #R_LINFAILBIT                           ; and record failure
+27
+
+
+; Similarly, test each DRAM bank in turn, reporting failures or sizes for each
+
+30
+        MOV     r11, #IOMD_DRAMCR_DRAM_Large * &55      ; set all banks to be large initially
+        MOV     r14, #IOMD_Base
+        STRB    r11, [r14, #IOMD_DRAMCR]
+        MOV     r0,#MMUC_D                              ; enable 32-bit addressing of data
+        SetCop  r0,CR_Control
+
+        MOV     r10, #0                                 ; indicate no RAM found yet
+        MOV     r9, #IOMD_DRAMCR_DRAM_Small             ; bit to OR into DRAMCR
+        MOV     r12, #DRAM0PhysRam
+35
+        MOV     r8,r12,LSL #2                           ; indicate bank under test
+        AND     r8,r8,#(3 :SHL: 28)
+        ADRL    r4,%BT3
+        BL      ts_SendText
+
+        MOV     r8,#0                                   ; r8 indicates RAM found in this bank
+        MOV     r0, r12
+        ADD     r1, r12, #A10                           ; this should be OK for both configurations
+        BL      DistinctAddresses
+        BNE     %FT50                                   ; [no RAM in this bank at all]
+
+        MOV_fiq r2,r9                                   ; if this is the first bank of DRAM or VRAM,
+        TEQS    r2,#0                                   ; put it's address in r9_fiq
+        BNE     %FT36
+        MOV_fiq r9,r0
+
+36      ADD     r1, r12, #A11                           ; test for 256K DRAM
+        BL      DistinctAddresses
+        ORRNE   r11, r11, r9                            ; it is, so select small multiplexing
+        MOVNE   r14, #IOMD_Base
+        STRNEB  r11, [r14, #IOMD_DRAMCR]                ; store new value of DRAMCR, so we can use memory immediately
+        MOVNE   r8, #1024*1024                          ; must be 1Mbyte at this address
+        BNE     %FT50
+
+; it's bigger than 256K words, so test address lines A21-A25 in sequence
+; we assume that the size of each bank is a power of 2
+
+        MOV     r8, #A21                                ; now go through address lines A21-A25
+40
+        ADD     r1, r12, r8                             ; see if this address line is unique
+        BL      DistinctAddresses
+        BNE     %FT50                                   ; if we've failed then r8 is true size, so exit
+        MOV     r8, r8, LSL #1                          ; else shift up to next
+        TEQ     r8, #A26                                ; only test up to A25
+        BNE     %BT40
+
+50
+        MOV     r13,r8                                  ; remember size of this bank in bytes
+        MOV     r8,r13,LSL #(24 - 20)                   ; and display it in 2 digits, in MBytes.
+        ADRL    r4,%BT5
+        BL      ts_MoreText
+
+        ADRL    r4,%FT73                                ; announce data line test
+        BL      ts_SendText
+        MOV     r1,r12                                  ; do walking bit test
+        BL      ts_Dataline
+        BEQ     %FT55                                   ; looks OK, carry on to next bank
+
+        ADRL    r4,%FT74                                ; bit test failed, so report it
+        MOV     r8,r0
+        BL      ts_SendText                             ; and bit fault mask
+
+        CMPS    r13,#0                                  ; was any RAM thought to be here ?
+        BEQ     %FT55
+        FAULT   #R_LINFAILBIT                           ; if so, it's faulty.
+        MOV     r13,#0                                  ; so ignore it
+55
+
+;
+; If there was some RAM found here, and it passed the dataline test,
+; do the address and bytestrobe tests on it too.
+;
+        CMPS    r13,#0
+        BEQ     %FT60
+
+        ADR     r4,%FT75                                ; announce start of address line test
+        BL      ts_SendText
+        MOV     r1,r12                                  ; test address lines in this block
+        MOV     r0,r13
+        BL      ts_Addrline
+        BEQ     %FT56
+        ADR     r4,%FT76                                ; failed - report error mask
+        MOV     r8,r0
+        BL      ts_SendText
+        FAULT   #R_LINFAILBIT                           ; and record failure
+        MOV     r13,#0                                  ; then forget this memory block
+
+56
+        ADR     r4,%FT77                                ; announce start of byte test
+        BL      ts_SendText
+        MOV     r1,r12
+        BL      ts_Byteword
+        BEQ     %FT60
+        ADR     r4,%FT78                                ; failed - report error mask
+        MOV     r8,r0,LSL #16
+        BL      ts_SendText
+        FAULT   #R_LINFAILBIT                           ; and record failure
+        MOV     r13,#0                                  ; then forget this memory block
+60
+
+
+; If the RAM found still seems OK, add it's size into the r10 accumulator
+; Working or not, carry on to check the next bank.
+
+        ADD     r10,r10,r13                             ; accumulate DRAM if any found 
+        ADD     r12, r12, #DRAM1PhysRam-DRAM0PhysRam    ; move onto next bank
+        MOV     r9, r9, LSL #2                          ; shunt up position in DRAMCR
+        CMP     r9, #&100                               ; if more banks to do
+        BCC     %BT35                                   ; then loop
+
+        ADR     r4,%FT70
+        BL      ts_SendText                             ; None found .. print message
+
+        MOVS    r8,r10,LSL #(24 - 20)                   ; all finished ..
+        ADREQ   r4,%FT71                                ; did we find any DRAM?
+        ADRNE   r4,%FT72
+        BNE     %FT65
+        FAULT   #R_LINFAILBIT                           ; fault if we didn't
+65
+        BL      ts_MoreText
+        B       ts_endline
+
+
+70
+        =       "DRAM",0
+71
+        =       &88,"Failed",0
+72
+        =       &88,&ff,&ff," MByte",0
+73
+        =       "Data  :",0
+74
+        =       "Data-F",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+75
+        =       "Addrs :",0
+76
+        =       "Addrs-F",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+77
+        =       "Byte  :",0
+78
+        =       "Byte-F",&88,&ff,&ff,&ff,&ff,0
+
+
+;
+; Data line test.
+;
+; In  : r1  - start address for test
+;
+; Out : r0  - failing data pattern
+;       r1  - address of failure
+;
+;
+; This exercises data lines in attempt to find shorts/opens.
+; It goes something like :
+;
+;       for (ptr = address, pattern = 1; pattern != 0; pattern <<= 1)
+;               *ptr++ =  pattern;
+;               *ptr++ = ~pattern;
+;       for (ptr = address, pattern = 1; pattern != 0; pattern <<= 1)
+;               result |=  pattern ^ *ptr++;
+;               result |= ~pattern ^ *ptr++;
+;       return result and address
+;
+
+ts_Dataline     ROUT
+
+;
+; Write all walking-zero, walking-one patterns
+;
+10      MOV     r6,r1                   ; set pointer for a write loop
+        MOV     r5,#1                   ; set initial test pattern
+        MVN     r4,r5                   ; and it's inverse        
+11
+        STMIA   r6!,{r4-r5}             ; write the patterns
+
+        ADDS    r5,r5,r5                ; shift the pattern (into Carry)
+        MVN     r4,r5
+        BCC     %BT11                   ; repeat until all bits done
+;
+; Read back and accumulate in r0 any incorrect bits
+;
+        MOV     r6,r1                   ; set pointer for a read loop
+        MOV     r5,#1                   ; set initial test pattern
+        MVN     r4,r5                   ; and it's inverse        
+        MOV     r0,#0                   ; accumulate result
+21
+        LDMIA   r6!,{r2-r3}             ; read the patterns
+        EOR     r2,r2,r4
+        ORR     r0,r0,r2                ; OR any failed bits into r0
+        EOR     r3,r3,r5
+        ORR     r0,r0,r2
+
+        ADDS    r5,r5,r5                ; shift the pattern (into Carry)
+        MVN     r4,r5
+        BCC     %BT21                   ; repeat until all bits done
+;
+; After all checks at this address group, report back errors
+;
+        MOVS    r0,r0                   ; check for any result bits set 
+        MOV     pc,r14                  ; return r0 with error map (or 0)
+
+
+
+;
+; Address line test
+;
+; In  : r0  - size of memory block
+;       r1  - start address of memory block
+;
+; Out : r0  - failing address bit mask
+;
+; This exercises address lines in an attempt to find any which don't
+; work (i.e., don't select unique addresses).
+;
+; It works something like :
+;
+; MaxRam = PhysRam | (Memory size - 4); 
+; for (pattern = 4; pattern < memsize; pattern <<= 1 )
+;       *(PhysRam ^ pattern) = pattern;
+;       *(MaxRam  ^ pattern) = ~pattern;
+; for (pattern = 4; pattern < memsize; pattern <<= 1 )
+;       if (*PhysRam == *(PhysRam ^ pattern))
+;               result |= pattern;
+;       if (*MaxRam == *(MaxRam + pattern))
+;               result |= pattern;
+;  return result
+;
+
+
+ts_Addrline     ROUT
+
+        MOVS    r7,r0                   ; Save memory size
+        SUB     r6,r0,#4                ; Calculate MaxRam
+        ADD     r6,r6,r1                ; (all-bits-set memory address)
+;
+; Mark (walking one, walking 0) addresses with unique patterns
+;
+        LDR     r5,=&5A5AA5A5           ; initialize end markers
+        STR     r5,[r6]
+        MVN     r4,r5
+        MOV     r3,r1
+        STR     r4,[r3]
+
+        MOV     r5,#4                   ; initialize pattern
+02
+        MVN     r4,r5
+        EOR     r3,r5,r1                ; point to (start ^ pattern)
+        STR     r4,[r3]
+        EOR     r3,r5,r6                ; point to (end ^ pattern)
+        STR     r5,[r3]
+
+        MOV     r5,r5,LSL #1            ; shift test pattern up
+        CMPS    r5,r7                   ; test bit still inside memory ?
+        BCC     %02                     ; reached top bit - end this loop
+;
+; Check (walking one, walking 0) addresses for effectivity
+;
+        MOV     r5,#4                   ; initialize pattern
+        MOV     r3,r1
+        MOV     r0,#0
+04
+        MVN     r4,r5
+        EOR     r2,r5,r3                ; point to (start ^ pattern)
+        LDR     r2,[r2]
+        LDR     r1,[r3]
+        CMPS    r1,r2                   ; do contents differ ?
+        ORREQ   r0,r0,r5                ; no - record ineffective bit
+
+        EOR     r2,r5,r6                ; point to (end ^ pattern)
+        LDR     r2,[r2]
+        LDR     r1,[r6]
+        CMPS    r1,r2                   ; do contents differ ?
+        ORREQ   r0,r0,r5                ; no - record ineffective bit
+
+        MOV     r5,r5,LSL #1            ; shift test pattern up
+        CMPS    r5,r7                   ; test bit still inside memory ?
+        BCC     %04                     ; reached top bit - end this loop
+
+        MOVS    r0,r0                   ; any result bits set - return error
+        MOV     pc,r14
+
+
+;
+; Byte / word test
+;
+; In  :  r1 - memory start
+;
+; Out :  r0 - Failure indication
+;
+; This test ensures that individual bytes may be written to each part of a word
+; without affecting the other bytes in the word.
+;
+;       for (byte = 0; byte < 4; byte ++)
+;               address[0] = word_signature
+;               address[1] = ~word_signature
+;               address + byte = byte_signature
+;               if (address[0] !=
+;                                 (word_signature & (~ff << byte * 8))
+;                               | (byte_signature        << byte * 8)  )
+;                       result |= (1 << byte)
+;       if (result != 0
+;               result |= address;      /* fail at address, byte(s)     */
+;       return result;                       /* pass */
+;
+
+ts_Byteword     ROUT
+
+        LDR     r3,=&AABBCCDD           ; word signature
+        MOV     r0,#0
+        MOV     r2,r0
+;
+; byte test loop ( for bytes 0 to 4  ...)
+;
+02
+        MVN     r4,r3
+        STMIA   r1,{r3,r4}              ; write word signature
+        STRB    r2,[r1,r2]              ; write byte (0, 1, 2 or 3)
+
+        MOV     r4,r2,LSL #3            ; calculate expected result
+        MOV     r5,#&ff     
+        MVN     r5,r5,LSL r4
+        AND     r5,r5,r3                ; word signature, byte removed
+        ORR     r5,r5,r2,LSL r4         ; byte signature inserted
+
+        LDR     r4,[r1,#4]              ; read (probable) inverse data to precharge bus
+        LDR     r4,[r1]                 ; read modified word
+        CMPS    r4,r5
+        MOV     r5,#1
+        MOV     r4,r2,LSL #2
+        ORRNE   r0,r0,r5,LSL r4         ; fault : set bit in result mask
+;
+; Loop for next byte
+;
+        ADD     r2,r2,#1                ; Bump byte counter
+        CMPS    r2,#4                   ; ... until 4 byte strobes tested 
+        BLO     %BT02
+;
+; byte strobes all tested : check for errors
+;
+        CMPS    r0,#0
+        MOV     pc,r14                  ; Result : return address and fault mask.
+
+;
+; End of RAM line tests
+;
+
+ts_endline
+
+        END 
+ 
\ No newline at end of file
diff --git a/TestSrc/Mem1MEMC1 b/TestSrc/Mem1MEMC1
new file mode 100644
index 0000000000000000000000000000000000000000..632c9bf2b8e5b33faf87dc5ac37826a43174df3d
--- /dev/null
+++ b/TestSrc/Mem1MEMC1
@@ -0,0 +1,390 @@
+; > TestSrc.Mem1
+
+        TTL RISC OS 2+ POST memory linetest
+;
+; This test code is used to perform basic integrity tests on DRAM.
+; It doesn't test all locations - just walks patterns through data
+; and address lines.
+;
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name            Comment
+; ----          ----            -------
+; 18-Dec-89     ArtG            Initial version
+; 1-Jun-93	ArtG		Reorganised to allow separate module for Medusa
+;
+;
+;------------------------------------------------------------------------
+
+;
+; Test the data and address and byte strobe lines for uniqueness.
+;
+
+        LTORG
+        ROUT
+
+1
+        =       "Data :",0
+2
+        =       "Data @",&89,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+3
+        =       "Data-F",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+4
+        =       "Data-P",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+
+
+
+        ALIGN
+
+ts_LineTest
+
+        ADR     r4,%BT1
+        BL      ts_SendText             ; Start data line tests
+
+        MOV_fiq r0,r10_fiq
+        MOV     r1, #PhysRam
+        BL      ts_Dataline
+        BEQ     ts_address              ; OK : continue to next test
+;
+; Data line test failed. This probably also means that RISCOS got the
+; configuration wrong, so set it to 32K pages and repeat - otherwise 
+; the data line test result may be garbage.
+;
+        ADR     r4,%BT2
+        MOV     r11,r0                  ; save data & report fault address
+        MOV     r8,r1,LSL #4
+        BL      ts_SendText
+
+        MOV     r8,r11
+        ADR     r4,%BT3                 ; report data fault mask
+        BL      ts_SendText
+
+        LDR     r0,=(&E000C :OR: MEMCADR) ; set 32K page size
+        STR     r0,[r0]
+        MOV_fiq r11_fiq,r0
+
+        MOV     r0,#ts_RamChunk         ; limit test to 1 block
+        MOV     r1,#PhysRam
+        BL      ts_Dataline   
+
+        MOV     r8,r0
+        ADR     r4,%BT4                 ; ready to report data fault mask
+        B       ts_linefault
+
+;
+; Start the address line tests
+;
+        ROUT
+
+4
+        =       "Addrs :",0
+5
+        =       "Addrs",&89,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+6
+        =       "Byte :",0
+7
+        =       "Byte",&89,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+
+
+
+ts_address
+        ADR     r4,%BT4
+        BL      ts_SendText             ; Start address line tests
+
+        MOV_fiq r0,r10_fiq
+        BL      ts_Addrline
+
+        ADR     r4,%BT5
+        MOV     r8,r0,LSL #4
+        BEQ     %30                     ; Failed : report address fault
+
+ts_linefault      
+        FAULT   #R_LINFAILBIT
+        B       %31
+
+30      ADR     r4,%BT6                 ; Start Byte/Word test
+        BL      ts_SendText
+
+        MOV_fiq r0,r10_fiq              ; get memory size
+        BL      ts_Byteword
+
+        MOV     r8,r0,LSL #4            ; Get result to top of r8 
+        BEQ     %40
+        FAULT   #R_LINFAILBIT
+
+        ADR     r4,%BT7
+
+31      BL      ts_SendText
+        B       %42
+;
+; Line tests passed. Do a short test on memory that isn't there,
+; in case it's supposed to be and we want to know why it's not ..
+
+40
+        MOV_fiq r0, r10_fiq             ; if there's less than 16Mbytes ..
+        CMP     r0, #(16 * 1024 * 1024)
+        BCS     %F42
+        ADR     r4, %FT44               ; briefly test the next bit of ram
+        BL      ts_SendText             ; in case it's a duff expansion
+
+        MOV_fiq r1,r10_fiq
+        ADD     r1,r1,#PhysRam
+        MOV     r0,#ts_RamChunk
+        BL      ts_Dataline
+        ADR     r4, %FT45
+        MOV     r11, r0                 ; report the result even if OK
+        MOV     r8,r1,LSL #4
+        BL      ts_SendText             ; report address
+
+        MOV     r8,r11
+        ADR     r4,%FT46                ; report data fault mask
+        BL      ts_SendText
+;
+; End of line tests
+;
+
+42
+        B       ts_IOCTest
+
+44
+        =       "Exp? :",0
+45
+        =       "Exp? @",&89,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+46
+        =       "Exp?",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+
+
+
+;
+; Data line test.
+;
+; In  : r0  - size of memory
+;       r1  - start address for test
+;
+; Out : r0  - failing data pattern
+;       r1  - address of failure
+;
+;
+; This exercises data lines in attempt to find shorts/opens.
+; It goes something like :
+;
+; for (address = start; address < end of ram; address += ts_RamChunk)
+;       for (ptr = address, pattern = 1; pattern != 0; pattern <<= 1)
+;               *ptr++ =  pattern;
+;               *ptr++ = ~pattern;
+;       for (ptr = address, pattern = 1; pattern != 0; pattern <<= 1)
+;               result |=  pattern ^ *ptr++;
+;               result |= ~pattern ^ *ptr++;
+;       if (result |= 0)
+;               return result and address
+;
+
+ts_Dataline     ROUT
+
+        ADD     r7,r1,r0                ; end address
+;
+; Write all walking-zero, walking-one patterns
+;
+10      MOV     r6,r1                   ; set pointer for a write loop
+        MOV     r5,#1                   ; set initial test pattern
+        MVN     r4,r5                   ; and it's inverse        
+11
+        STMIA   r6!,{r4-r5}             ; write the patterns
+
+        ADDS    r5,r5,r5                ; shift the pattern (into Carry)
+        MVN     r4,r5
+        BCC     %BT11                   ; repeat until all bits done
+;
+; Read back and accumulate in r0 any incorrect bits
+;
+        MOV     r6,r1                   ; set pointer for a read loop
+        MOV     r5,#1                   ; set initial test pattern
+        MVN     r4,r5                   ; and it's inverse        
+        MOV     r0,#0                   ; accumulate result
+21
+        LDMIA   r6!,{r2-r3}             ; read the patterns
+        EOR     r2,r2,r4
+        ORR     r0,r0,r2                ; OR any failed bits into r0
+        EOR     r3,r3,r5
+        ORR     r0,r0,r2
+
+        ADDS    r5,r5,r5                ; shift the pattern (into Carry)
+        MVN     r4,r5
+        BCC     %BT21                   ; repeat until all bits done
+;
+; After all checks at this address group, report back errors
+;
+        MOVS    r0,r0                   ; check for any result bits set 
+        MOVNE   pc,r14                  ; return on error
+;
+; Bump to another address group
+;
+        ADD     r1,r1,#ts_RamChunk
+        CMPS    r1,r7                   ; test for loop end
+        BLO     %10
+
+        SUBS    r1,r1,#ts_RamChunk      ; no fault - last tested address
+        MOVS    r0,r0
+        MOV     pc,r14                  ; test complete - no failures.
+
+
+;
+; Address line test
+;
+; In  : r0  - size of memeory
+;
+; Out : r0  - failing address bit mask
+;
+; This exercises address lines in an attempt to find any which don't
+; work (i.e., don't select unique addresses).
+;
+; It works something like :
+;
+; MaxRam = PhysRam | (Memory size - 4); 
+; for (pattern = 4; pattern < memsize; pattern <<= 1 )
+;       *(PhysRam ^ pattern) = pattern;
+;       *(MaxRam  ^ pattern) = ~pattern;
+; for (pattern = 4; pattern < memsize; pattern <<= 1 )
+;       if (*PhysRam == *(PhysRam ^ pattern))
+;               result |= pattern;
+;       if (*MaxRam == *(MaxRam + pattern))
+;               result |= pattern;
+;  return result
+;
+
+
+ts_Addrline     ROUT
+
+        MOVS    r7,r0                   ; Save memory size
+        SUB     r6,r0,#4                ; Calculate MaxRam
+        ADD     r6,r6,#PhysRam          ; (all-bits-set memory address)
+;
+; Mark (walking one, walking 0) addresses with unique patterns
+;
+        LDR     r5,=&5A5AA5A5           ; initialize end markers
+        STR     r5,[r6]
+        MVN     r4,r5
+        MOV     r3,#PhysRam
+        STR     r4,[r3]
+
+        MOV     r5,#4                   ; initialize pattern
+02
+        MVN     r4,r5
+        EOR     r3,r5,#PhysRam          ; point to (start ^ pattern)
+        STR     r4,[r3]
+        EOR     r3,r5,r6                ; point to (end ^ pattern)
+        STR     r5,[r3]
+
+        MOV     r5,r5,LSL #1            ; shift test pattern up
+        CMPS    r5,r7                   ; test bit still inside memory ?
+        BCC     %02                     ; reached top bit - end this loop
+;
+; Check (walking one, walking 0) addresses for effectivity
+;
+        MOV     r5,#4                   ; initialize pattern
+        MOV     r3,#PhysRam
+        MOV     r0,#0
+04
+        MVN     r4,r5
+        EOR     r2,r5,r3                ; point to (start ^ pattern)
+        LDR     r2,[r2]
+        LDR     r1,[r3]
+        CMPS    r1,r2                   ; do contents differ ?
+        ORREQ   r0,r0,r5                ; no - record ineffective bit
+
+        EOR     r2,r5,r6                ; point to (end ^ pattern)
+        LDR     r2,[r2]
+        LDR     r1,[r6]
+        CMPS    r1,r2                   ; do contents differ ?
+        ORREQ   r0,r0,r5                ; no - record ineffective bit
+
+        MOV     r5,r5,LSL #1            ; shift test pattern up
+        CMPS    r5,r7                   ; test bit still inside memory ?
+        BCC     %04                     ; reached top bit - end this loop
+
+        MOVS    r0,r0                   ; any result bits set - return error
+        MOV     pc,r14
+
+
+;
+; Byte / word test
+;
+; In  :  r0 - memory size
+;
+; Out :  r0 - address of physical ram where failure occured
+;
+; This test ensures (for each of four possible MEMCs fitted)
+; that individual bytes may be written to each part of a word
+; without affecting the other bytes in the word.
+;
+; for (address = PhysRam; address < PhysRam + Memsize; address += 4Mbyte)
+;       for (byte = 0; byte < 4; byte ++)
+;               address[0] = word_signature
+;               address[1] = ~word_signature
+;               address + byte = byte_signature
+;               if (address[0] !=
+;                                 (word_signature & (~ff << byte * 8))
+;                               | (byte_signature        << byte * 8)  )
+;                       result |= (1 << byte)
+;       if (result != 0
+;               result |= address;      /* fail at address, byte(s)     */
+;               return result;
+;  return result;                       /* pass */
+;
+
+ts_Byteword     ROUT
+
+        ADD     r7,r0,#PhysRam          ; Set test limit address
+        MOV     r1,#PhysRam             ; Initial test address
+        LDR     r3,=&AABBCCDD           ; word signature
+;
+; MEMC test loop (for addresses 4M, 8M, ...)
+;
+01
+        MOV     r0,#0                   ; clear result register
+        MOV     r2,#0                   ; clear byte count
+;
+; byte test loop ( for bytes 0 to 4  ...)
+;
+02
+        MVN     r4,r3
+        STMIA   r1,{r3,r4}              ; write word signature
+        STRB    r2,[r1,r2]              ; write byte
+
+        MOV     r4,r2,LSL #3            ; calculate expected result
+        MOV     r5,#&ff     
+        MVN     r5,r5,LSL r4
+        AND     r5,r5,r3                ; word signature, byte removed
+        ORR     r5,r5,r2,LSL r4         ; byte signature inserted
+
+        LDR     r4,[r1,#4]
+        LDR     r4,[r1]                 ; read modified word
+        CMPS    r4,r5
+        MOV     r5,#1
+        ORRNE   r0,r0,r5,LSL r2         ; fault : set bit in result mask
+;
+; Loop for next byte
+;
+        ADD     r2,r2,#1                ; Bump byte counter
+        CMPS    r2,#4                   ; ... until 4 byte strobes tested 
+        BLO     %BT02
+;
+; byte strobes all tested : check for errors
+;
+        CMPS    r0,#0
+        ORRNE   r0,r0,r1
+        MOVNE   pc,r14                  ; Error : return address and fault.
+;
+; Loop for next MEMC
+;
+        ADD     r1,r1,#&400000          ; Bump to next MEMC
+        CMPS    r1,r7
+        BLO     %01
+
+        MOVS    r0,#0                   ; Passed - return OK
+        MOV     pc,r14
+
+
+        END 
+ 
\ No newline at end of file
diff --git a/TestSrc/Mem2 b/TestSrc/Mem2
new file mode 100644
index 0000000000000000000000000000000000000000..89f5bc2d3a13a7acf9b701c7cdf5a16209c3fec9
--- /dev/null
+++ b/TestSrc/Mem2
@@ -0,0 +1,278 @@
+;> MEM2C
+; 
+; RISC OS 2+ BOOT TEST SOFTWARE
+; MEMORY TEST 2 VERSION A.
+; BRIAN RICE 30-10-89
+; 06-Apr-90     ArtG    0.1     Test variable memory size
+;
+; This file will perform a simple test on all DRAM.
+; The test code for this test was taken from thhe A680 Quick memory 
+; test software. The software was copied straight but the number of times 
+; the test looped arround was cut down to two loops, because of time
+; constraints when testing the memory.
+
+Test_wks_msize      * &40               ; Space for test block size
+Test_wks_return1    * &44 		; Space for return addresses
+Test_wks_return2    * &48
+Test_code_off       * &4C               ; Where testing starts
+
+test_size           * 13 * 4            ; Size of test group
+test_mem_rsvd       * Test_code_off+test_mem_template_end-test_mem_template
+
+;
+; Quick test the RAM (pre boot style)
+;
+
+ts_RamTest ROUT
+	MOV	r13,r0
+	STR	r14,[r13,#Test_wks_return1]
+	STR	r1,[r13,#Test_wks_msize]
+
+	LDR    r0, test_quick_pattern
+	BL     test_mem_code
+	ORRS   r0,r0,r0
+	BNE    test_mem_quit
+;
+	LDR    r0, test_quick_pattern
+	MVN    r0, r0		 ; inverse pattern
+	BL     test_mem_code
+	ORRS   r0,r0,r0
+
+test_mem_quit
+	ADR	r12,%22
+	BEQ     %10
+
+; If fault detected, exit with zero flag clear, r0 pointing to failing
+; location, r1 containing faulty data and r2 pointing a suitable error
+; message indicating whether all-0 or all-1 data was expected.
+
+	LDR     r2,[r14]	        ; fetch failing instructiom
+	ANDS    r2,r2,#1	        ; calculate expected data
+	ADREQ   r12,%20	        	; and load suitable message
+	ADRNE   r12,%21
+	MOVS    r0,r0			; with zero flag set for PASS. 
+10	
+	LDR	pc,[r13,#Test_wks_return1]
+
+; Fail messages indicate incorrect data read after WRote 0 or Wrote 1
+; to all bits at that location.
+
+20
+	=       "WR-0 RD",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+21
+	=       "WR-1 RD",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+22
+	=	"??",0
+
+	ALIGN
+
+test_quick_pattern  & &0f76
+
+; Large Memory test. Generates the write + test routines in memory
+; then calls them. The routine tests patterns as defined by the bottom
+; 13 bits of r0.
+;
+; N.B. The test start address must be calculated to ensure that
+;      the loops finish exactly with r0 equal to End_memory
+;
+; The routine returns with eq true if the memory is OK.
+
+
+test_mem_code
+	ROUT
+
+	STR	r14, [r13, #Test_wks_return2]
+;
+; Copy the ram test code into low ram, modifying MOV instructions
+; to MVN in accordance with the test pattern. 
+;
+	ADR    r1, test_mem_template
+	ADD    r2, r13,        #Test_code_off
+	LDMIA  r1!, {r3-r4}		; copy initial 2 instrucions
+	STMIA  r2!, {r3-r4}
+	MOV    r4, #1
+0	MOVS   r0, r0,         ROR #1
+	LDR    r3, [r1],       #4
+	ORRCS  r3, r3,         #&00400000 ; Convert MOV => MVN
+	STR     r3, [r2],      #4
+	ADD     r4, r4,        #1
+	CMP     r4, #13
+	BLE     %B0
+;
+; Copy the load loop control and verify start instructions
+;
+	LDMIA   r1!, {r5-r9}
+	STMIA   r2!, {r5-r9}
+;
+; Copy and modify the CMP instructions
+;
+	MOV     r0, r0,        ROR #32-13
+	MOV     r4, #1
+1	MOVS    r0, r0,        ROR #1
+	LDR     r3, [r1],      #4
+	ORRCS   r3, r3,        #&00200000 ; Convert CMP => cmn
+	ORRCS   r3, r3,        #&00000001 ; Convert  #0 =>  #1
+	STR     r3, [r2],      #4
+	ADD     r4, r4,        #1
+	CMP     r4,	  #13
+	BLE     %B1
+;
+; Copy the verify loop control and finishing-up instructions
+;
+	LDMIA   r1!, {r5-r12}
+	STMIA   r2!, {r5-r12}
+	LDMIA   r1!, {r5-r12}
+	STMIA   r2!, {r5-r12}
+	LDMIA   r1!, {r5-r12}
+	STMIA   r2!, {r5-r12}
+
+; check we've copied enough
+	ASSERT  ((test_mem_stadd - test_mem_chk) = (24 * 4))
+;
+; Calculate the test start and end addresses
+;
+	LDR	r0, [r13, #Test_wks_msize]	; size of test area
+	ADD     r14, r13, r0			; end of test area
+	SUB     r1, r0, #test_mem_rsvd		; testable size
+
+	MOV     r2, #test_size		; adjust r1 to (r1 / 13*4) * (13*4)
+	DivRem  r3, r1, r2, r4
+	MUL     r1, r3, r2
+	SUB     r0, r14, r1			; rounded test start address
+
+; Do it.
+	MOV     r1, #Test_code_off
+	ADD     r1, r1, r13			; pointer to copied code
+	MOV     pc, r1
+
+;
+; The following code is copied  (and modified) into RAM for execution
+;
+
+test_mem_template 
+	ROUT
+	STR     r0, test_mem_stadd      ; save initial RAM address
+	STR	r13, test_mem_base	; save test area base address
+	MOV     r1, #0		; Converted to MVN if bit = 1
+	MOV     r2, #0		; Converted to MVN if bit = 1
+	MOV     r3, #0		; Converted to MVN if bit = 1
+	MOV     r4, #0		; Converted to MVN if bit = 1
+	MOV     r5, #0		; Converted to MVN if bit = 1
+	MOV     r6, #0		; Converted to MVN if bit = 1
+	MOV     r7, #0		; Converted to MVN if bit = 1
+	MOV     r8, #0		; Converted to MVN if bit = 1
+	MOV     r9, #0		; Converted to MVN if bit = 1
+	MOV     r10, #0	         ; Converted to MVN if bit = 1
+	MOV     r11, #0	         ; Converted to MVN if bit = 1
+	MOV     r12, #0	         ; Converted to MVN if bit = 1
+	MOV     r13, #0	         ; Converted to MVN if bit = 1
+0
+	STMIA   r0!, {r1-r13}
+	CMP     r0, r14
+	BLO     %B0
+
+	LDR     r0, test_mem_stadd
+1
+	LDMIA   r0!, {r1-r13}
+2
+	CMP     r1, #0		; Converted to cmn   if bit = 1
+	CMPEQ   r2, #0		; Converted to cmneq if bit = 1
+	CMPEQ   r3, #0		; Converted to cmneq if bit = 1
+	CMPEQ   r4, #0		; Converted to cmneq if bit = 1
+	CMPEQ   r5, #0		; Converted to cmneq if bit = 1
+	CMPEQ   r6, #0		; Converted to cmneq if bit = 1
+	CMPEQ   r7, #0		; Converted to cmneq if bit = 1
+	CMPEQ   r8, #0		; Converted to cmneq if bit = 1
+	CMPEQ   r9, #0		; Converted to cmneq if bit = 1
+	CMPEQ   r10, #0	        ; Converted to cmneq if bit = 1
+	CMPEQ   r11, #0	        ; Converted to cmneq if bit = 1
+	CMPEQ   r12, #0	        ; Converted to cmneq if bit = 1
+	CMPEQ   r13, #0	        ; Converted to cmneq if bit = 1
+test_mem_chk 
+	BNE     %F5		; go report fault data
+	CMP     r0, r14
+	BLO     %B1		; else loop for next batch
+	MOVS    r0, #0		; All OK : return with NULL r0
+4
+	LDR	r13,test_mem_base
+	LDR	pc, [r13, #Test_wks_return2]
+
+; Failed : repeat the last batch of tests one at a time, to determine
+; the first failing address and data.
+; Note that the test instructions are copied to %8 to permit individual
+; execution, and %7 is overwritten with an instruction used to copy
+; the failing data into r1. Change this code very carefully ! 
+
+5
+	LDR     r14,%2			; Obtain first test in the set
+	STR     r14,%8			; and re-execute it
+	SUB     r0,r0,#(13*4)	   	; adjust pointer to bad data
+	ADR     r14,%2			; point to first test.
+7
+	B       %8		    	; make sure %8 is refetched
+8
+	&       0		     	; redo the test here :
+	BNE     %4		    	; if it failed, exit with
+				    	; r0  =  ptr to memory
+				    	; r1  =  wrongly read data
+				    	; r14 => failing instruction
+
+	LDR     r1,[r14,#4]!	    	;fetch next instruction
+	AND     r1,r1,#&f0000		;make an instruction 
+	MOV     r1,r1,LSR #16		;to copy the next register 
+	ORR     r1,r1,#&E1000000	;down to r1
+	ORR     r1,r1,#&00A00000	;e.g. CMPEQ r10,#0
+	ORR     r1,r1,#&00001000
+	STR     r1,%7		 	;and put it at %7
+	LDR     r1,[r14]	        ;then copy the next test
+	STR     r1,%8		 	;to %8
+	ADD     r0,r0,#4	        ;bump the fault pointer
+	B       %7		    	;and execute %7 and %8.
+
+test_mem_stadd				; address of first test location
+	&       0
+test_mem_base
+	&	0			; address of test block
+
+test_mem_template_end
+
+;
+; Copy the L2 page table from r1 to r0, then remap the translation table's
+; base address in the MMU to point to an L1 page table within it.
+;
+	ROUT
+
+ts_remap_ttab
+	MOV	r2,#FixedAreasL2Size
+	ADD	r0,r0,r2		; point to locations in PhysSpace
+	ADD	r0,r0,#PhysSpace
+	ADD	r1,r1,r2
+	ADD	r1,r1,#PhysSpace
+10
+	ASSERT	((FixedAreasL2Size :AND: ((8*4)-1)) = 0)
+	LDMDB	r1!,{r3-r10}		; copy the page & section tables
+	STMDB	r0!,{r3-r10}
+	SUBS	r2,r2,#(8*4)
+	BNE	%BT10
+
+	SUB	r9,r1,r0		; r9 = offset from original to copy 
+        ADD     r0, r0, #DRAMOffset_L1PT-DRAMOffset_L2PT	; r0 -> copy of L1Phys
+	SUB	r10, r0, #PhysSpace	; keep real address of L1PT for MMU
+	ADD	r2,r0,#((1 :SHL: (32-20))*4)	; size of L1PT - 1 word per meg of memory
+11	LDR	r3,[r0],#4		; check each L1 table entry
+	ANDS	r4,r3,#3
+	CMPS	r4,#L1_Page		; if it's page mapped ..
+	SUBEQ	r3,r3,r9		; adjust the page table base address
+	STREQ	r3,[r0,#-4]
+	CMPS	r0,r2			; repeat for all the level 1 table
+	BNE	%BT11	
+
+        SetCop  r10, CR_TTabBase	; set up MMU pointer to L1
+        SetCop  r0, CR_IDCFlush		; flush cache + TLB just in case
+        SetCop  r0, CR_TLBFlush		; (data written is irrelevant)
+
+	MOV	pc,r14
+
+
+ END
+  
diff --git a/TestSrc/Mem3 b/TestSrc/Mem3
new file mode 100644
index 0000000000000000000000000000000000000000..1313275f4a55e6f7eb344e50900cdd97407b5f33
--- /dev/null
+++ b/TestSrc/Mem3
@@ -0,0 +1,119 @@
+        ;> RomCheck
+; 
+; RISC OS 2+ BOOT TEST SOFTWARE
+; MEMORY TEST 3 VERSION A.
+; BRIAN RICE 01-11-89
+; 24.04.90      0.10    ArtG    Added ROM size test
+; 15.05.90      1.00    ArtG    Changed to put checksum at (end - 2 words)
+; 17.05.90      1.01    ArtG    Changed to get ROM length from vectot table
+;
+;
+; This file will perform quick checksum test on the OS ROMS.
+;
+;
+; The test code for this test is a simple additive checksum routine.
+; The software will read eight words from ROM then add the contents from ROM  
+; to a register. When the test is complete the contents of the checksum
+; register is checked by adding the final word in ROM - this should give 
+; zero.
+; The program will be run from ROM, at slowest speed.
+;
+; All except the last two words are checksummed : these hold the numbers
+; that cause each individual ROM to CRC to zero, so they can't simultaneously
+; be included in an all-zero additive checksum.
+
+ts_CRCsize      *       (2 * 4)
+
+;
+;
+;r0 IS A POINTER TO THE LOCATIONS IN MEMORY.
+;r1 HAS THE CALCULATED CHECKSUM.
+;r2 HOLDS A COUNTER INDICATION HOW MANY WORDS ARE LEFT TO GET
+;r3 is a temporary variable
+;r4 TO r11 ARE USED TO LOAD THE CONTENTS OF 8 LOCATIONS FROM THE ROM.
+;
+        ROUT
+
+ts_ROM_checksum
+
+         MOV    r1, #&00                    ; initialise accumulator    
+         LDR    r0, =PhysROM                ; initialise pointer
+         LDR    r2, [r0, #ts_ROMSIZE]       ; initialise endstop
+         ADD    r2, r2, r0                  ; - must be at least 8 words 
+         SUB    r2, r2, #(10 * 4)           ; below the real endpoint
+
+loop1    LDMIA  r0!, {r4 - r11}             ;LOAD r4 TO r11 WITH THE CONTENTS
+                                            ;OF LOCATIONS POINTED TO BY r0
+                                            ;WHICH IS INCREMEMTED AUTOMATICALLY
+                                            ;TO POINT TO THE NEXT LOCATION
+01
+         ADD    r1, r1,          r4         ;ADD r4  TO CHECKSUM
+         ADD    r1, r1,          r5         ;ADD r5  TO CHECKSUM
+         ADD    r1, r1,          r6         ;ADD r6  TO CHECKSUM
+         ADD    r1, r1,          r7         ;ADD r7  TO CHECKSUM
+         ADD    r1, r1,          r8         ;ADD r8  TO CHECKSUM
+         ADD    r1, r1,          r9         ;ADD r9  TO CHECKSUM
+         ADD    r1, r1,          r10        ;ADD r10 TO CHECKSUM
+         ADD    r1, r1,          r11        ;ADD r11 TO CHECKSUM
+02
+        ASSERT ((%02 - %01) = 32)       ; else r2 won't count down correctly
+ 
+         CMPS   r0, r2
+         BCC    loop1                       ;loop until pointer reaches endstop
+
+         LDMIA  r0!, {r4 - r9}             ; get last 6 words (miss last 2 in ROM)
+03
+         ADD    r1, r1,          r4         ;ADD r4  TO CHECKSUM
+         ADD    r1, r1,          r5         ;ADD r5  TO CHECKSUM
+         ADD    r1, r1,          r6         ;ADD r6  TO CHECKSUM
+         ADD    r1, r1,          r7         ;ADD r7  TO CHECKSUM
+         ADD    r1, r1,          r8         ;ADD r8  TO CHECKSUM
+         ADD    r1, r1,          r9         ;ADD r9  TO CHECKSUM
+04
+        ASSERT  (((%04 - %03) + (2*4)) =  32) ; Change this if you like - 
+                                            ; but be careful to count nearly
+                                            ; to the top in eights, then add
+                                            ; add in the last few words.
+
+         MOVS   r0,r1                       ; should be zero if all OK
+
+         MOV    pc,r14                      ;return with zero flag set on OK
+                                            ;and the calculated sum in r0.
+
+
+;
+; ROM alias check.
+; This test looks for an aliased copy of the vector table at varying
+; distances from the start of ROM space.
+; 16K is fairly arbitrary but corresponds approximately with the size of 
+; the POST. If there's an alias below that, we've probably already crashed !
+;
+; This test is only called if the checksum fails, in order to indicate a
+; possible high ROM address line failure.
+
+ts_ROM_alias    ROUT
+
+        MOV     r0,#PhysROM             ; get some words from ROM start
+        LDR     r3,[r0, #ts_ROMSIZE]    ; get the ROM length word
+        LDMIA   r0,{r4,r5,r6,r7}
+        MOV     r1,#(16 * 1024)
+
+01      ADD     r2,r0,r1                ; get some words from possible alias
+        LDMIA   r2,{r8,r9,r10,r11}
+        CMPS    r4,r8
+        CMPNE   r5,r9
+        CMPNE   r6,r10
+        CMPNE   r7,r11
+        BEQ     %10                     ; aliased : found MS ROM address bit
+
+        MOVS    r1, r1, LSL #1          ; test the next (more significant) bit
+        CMPS    r1, r3                  ; reached the limit yet ?
+        BLT     %01                     ; no - try again.
+
+10      MOV     r0,r1                   ; reached the end, or an alias.
+        MOV     pc,lr
+
+
+  LTORG                     
+
+  END
diff --git a/TestSrc/Mem4 b/TestSrc/Mem4
new file mode 100644
index 0000000000000000000000000000000000000000..8d73e78c45242b07452cbc64e675df452f4b3f7a
--- /dev/null
+++ b/TestSrc/Mem4
@@ -0,0 +1,630 @@
+;> MEM4H_SCR
+;
+; RISC OS 2+ BOOT TEST SOFTWARE.
+; MEMORY TEST 4 VERSION H.      BRIAN RICE 12-01-90.
+; 04-Apr-90     ArtG    0.1     Added ts_count_cams, improved reporting
+; 11-Apr-90     ArtG    0.2     Use RISC OS routine BangCams for 
+;                               alternate MEMC configurations.  
+; 17-Apr-90     ArtG    0.3     rationalise page-counting code
+;
+; This file will be called by MEM6x_SCR for the purposes of assembly.
+; This file will perform quick walking bit test on the CAM Entry points.
+; The test code for this test was taken from the A680 test code.
+;
+; The module requires the running of the memory sizing routine used by
+; the OS to set up the page size for this module.
+;
+; This test module was designed to operate on all current and future 
+; machines. The module is designed to handle up to 512 physical pages 
+; which is the maximum number of pages in a 16 MByte FOX.
+;
+; A 16 MB FOX has 4 MEMCs in use, each MEMC is addressed by Bits 7 and
+; 12 of the logical to physical address translator. The use of bit 12
+; does have a problem in that on machines with 0.5MB of memory this is
+; used to define the logical page number. Machine with 1MB or greater bit
+; 12 is not used, therefore this test may hit problems on A305's. The
+; intention is that A305 owners will upgrade to A310's when upgrading to
+; RISC OS 2+.
+;
+; Because FOX can have up to 4 MEMCs fitted the following addressing is
+; used to determine the MEMC accessed, bit 12, bit 7
+;                                        0      0 = Master MEMC  = MEMC 0
+;                                        0      1 = Slave MEMC 1 = MEMC 1
+;                                        1      0 = Slave MEMC 2 = MEMC 2
+;                                        1      1 = Slave MEMC 3 = MEMC 3
+;
+;
+; This test will initialise the CAM entries for up to 512 physical pages.
+; The physical pages will be mapped to logical page 5. Each page will have
+; a copy of test routine vectors and a page marker. The page marker consists
+; of the page number and a code to indicate which MEMC was used. The code for
+; the MEMC used is as follows :- MEMC 0 0001 1110 = &1E
+;                                MEMC 1 0010 1101 = &2D
+;                                MEMC 2 0100 1011 = &4B
+;                                MEMC 3 1000 0111 = &87
+;
+; The page marker is arranged as follows &mm5Apppp
+;                                          |    |
+;                                          |    \-- Page Number &0000 ‰ &01FF.
+;                                          \--------MEMC Code as above.
+;
+; The patterns are chosen so that if two or more MEMCs are accessed
+; together and both RAM outputs get enabled onto the data bus simultaneously,
+; then there is a reasonable chance that the data returned will show the 
+; presence of a fault.
+;
+; When the CAM entries have been initialised the module will then check that
+; all the pages are mapped correctly. A simple walking one pattern is used
+; to check that the page is not present anywhere else in the memory area.
+; This isn't really sufficient, but keeps the test time low.
+;
+; The tests are performed with the memory protection level set to 0.
+;
+; This version uses the "my abort" routine in MEM5x_SCR instead of the
+; ts_dab_exp0 .. 5 method as taken from the A680 code.
+;
+
+ts_rst_msg      =       "RST",0
+ts_uni_msg      =       "UDF",0
+ts_swi_msg      =       "SWI",0
+ts_pab_msg      =       "PAB",0
+ts_dab_msg      =       "DAB",0
+ts_aex_msg      =       "ADX",0
+ts_irq_msg      =       "IRQ",0
+ts_fiq_msg      =       "FIQ",0
+ts_bxc_msg      =       &85,"@",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+        ALIGN
+
+
+ts_rst                                          ; Unused exception vectors
+        ADR     r4, ts_rst_msg
+        B       ts_bad_exception
+ts_uni
+        ADR     r4, ts_uni_msg
+        B       ts_bad_exception
+ts_swi
+        ADR     r4, ts_swi_msg
+        B       ts_bad_exception
+ts_pab
+        ADR     r4, ts_pab_msg
+        B       ts_bad_exception
+ts_dab_unexp
+        ADR     r4, ts_dab_msg
+        B       ts_bad_exception
+ts_aex
+        ADR     r4, ts_aex_msg
+        B       ts_bad_exception
+ts_irq
+        ADR     r4, ts_irq_msg
+        B       ts_bad_exception
+ts_fiq
+        ADR     r4, ts_fiq_msg
+        B       ts_bad_exception
+
+
+ts_bad_exception
+        SUBS    r8, r14, #8                     ; remember aborted instruction
+        BL      ts_SendText
+        ADR     r4, ts_bxc_msg                  ; display aborted address
+        BL      ts_MoreText
+        B       Reset
+
+
+;
+ts_rom_base     *       ROM                     ; Base address of the OS ROMS.
+ts_phys_mem     *       (32*1024*1024)          ; Physical Memory area.
+ts_pagemark     *       &005A0000               ; + phys page number + MEMC code.
+ts_pmark_pos    *       32                      ; Position of page mark (avoiding vectors).
+ts_cam_base     *       &3800000                ; Base address of the CAM table in MEMC.
+ts_vrest        *       &5                      ; Unused page which all pages are mapped to.
+ts_MAX_CAMS     *       512                     ; Most CAMs ever expected
+ts_memc_codes   =       &1E, &2D, &4B, &87      ; List of the memc_codes to be used.
+;
+ts_logpages                                 ; List of Logical pages.
+                 &       &0001
+                 &       &0002
+                 &       &0004
+                 &       &0008
+                 &       &0010
+                 &       &0020
+                 &       &0040
+                 &       &0080
+                 &       &0100
+                 &       &0200
+                 &       &03FF
+                 &       &03FE
+                 &       &03FD
+                 &       &03FB
+                 &       &03F7
+                 &       &03EF
+                 &       &03DF
+                 &       &03BF
+                 &       &037F
+                 &       &02FF
+                 &       &01FF
+                 &       &0000              ; Terminator for the list.
+ts_logpagesend                              ; End of the list.
+;
+;
+; Exception vectors : copied to start of each page to ensure that they will always
+; exist on page zero when arbitrary pages are mapped there.
+;
+ts_vectors
+        B       (ts_vectors-ts_start)+ts_rom_base+ts_rst
+        B       (ts_vectors-ts_start)+ts_rom_base+ts_uni
+        B       (ts_vectors-ts_start)+ts_rom_base+ts_swi
+        B       (ts_vectors-ts_start)+ts_rom_base+ts_pab
+ts_dab_vector
+        B       (ts_vectors-ts_start)+ts_rom_base+ts_dab
+        B       (ts_vectors-ts_start)+ts_rom_base+ts_aex
+        B       (ts_vectors-ts_start)+ts_rom_base+ts_irq
+        B       (ts_vectors-ts_start)+ts_rom_base+ts_fiq
+
+
+; ***************************************************************************
+;
+ts_CAM
+;
+; CAM test (full or partial)
+; Start of the CAM test, all physical pages have a copy of the vectors
+; so they may be mapped as page 0. Then each page is mapped at a series
+; of (walking 1, walking 0) logical pages and tested to be correctly
+; mapped. Other pages are set to an unused logical page by set_cam_idle
+; to prevent any CAM clashes.
+;
+; Copy the test vectors and page marker into all the pages.
+;
+        ROUT                                ; Local Branches.
+        MOV     r13, lr                     ; Preserve link register in r13.
+        BL      ts_count_CAMs               ; get log2 pagesize
+        MOV     r0, #ts_MAX_CAMS            ; r0 = last page to test
+        SUB     r0, r0, #1
+0       BL      ts_copy_vectors             ; Gosub ts_vectors.
+        SUBS    r0, r0, #&01                ; bump down to next page
+        BGE     %B0                         ; repeatuntil all pages set.
+;
+; 'C' pseudocode for the test routine.
+;
+;       for (i = &1ff; i >= 0; i--)
+;               set_cam_idle(i);               
+;
+;       find maximum page number.
+;       if (max_page != ts_count_CAMS)
+;               report CAM number error
+;
+;       for (phys = &max_page; phys >= 0; phys--) {
+;               for (logp = &logpages[0]; logp < &logpages[sizof(logpages)]; logp++) {
+;                       if (*logp == 0) {
+;                               set_cam(*logp, phys);
+;                               check_mapped(*logp, phys);
+;                       } else {
+;                               int zphys = (phys + 1) % num_pages;
+;                               set_cam(0, zphys);
+;                               set_cam(*logp, phys);
+;                               check_mapped(*logp, phys);
+;                               set_cam_idle(zphys);
+;                       }
+;               }
+;               set_cam_idle(phys);
+;       }
+;
+; Idle the pages.
+;
+        ROUT                                ; Local Branches.
+        MOV     r12, #ts_MAX_CAMS           ; always clear all 512 - just in case 4 MEMCs.
+        SUB     r12, r12, #&01              ; Subtract 1 to make max page #.
+0       MOV     r1, r12                     ; r1 = page number.
+        BL      ts_set_cam_idle
+        SUBS    r12, r12, #&01              ; bump to next page downwards
+        BGE     %B0                         ; repeatuntil page 0 done
+;
+; We need to find out what the maximum number of pages is, after running the above routine
+; all the pages will have the pagemark programed in to each page. As stated in the intro
+; programing the pages from the top down will ensure that, irrespective of the number of
+; MEMCs available, that the bottom pages are programed correctly. Therefore if we start
+; at the top, read in a page, check it's page number & memc code are correct, if so then
+; that is possibly the maximum page number. If not then subtract 1 from the page number and
+; try again until a possible good page is found.
+;
+        ROUT                                ; Local Branches.
+
+        BL      ts_count_CAMs               ; get log2 pagesize to r1  
+        MOV     r8, #ts_MAX_CAMS            ; r8= max. number of physical pages.
+0       SUBS    r8, r8, #&01                ; Subtract 1 to make it r8 - 1 Pages.
+        BEQ     ts_bad_CAM_count            ; no pages ? - shouldn't hit this!
+;
+; Calculate the expected page marker, in r4, for the current page, in r8.
+;
+        ADR     r4, ts_memc_codes           ; r4 = address of table with the memc codes.
+        LDRB    r4, [r4, r8, LSR#7]         ; r4 = Loc pointed to by r4 + (r1 >> 7).
+        ORR     r4, r8, r4, LSL #24         ; r4 = page number OR (MEMC code << 24).
+        ORR     r4, r4, #ts_pagemark        ; r4 = page id OR magic number
+;            
+; The calculated page marker is now in r4, ref_p_mark.
+; Current page in r8 - convert to physical address in r9.
+; the pagesize power-of-2 is in r1 (from ts_count_CAMs)
+;
+        MOV     r9, r8, LSL r1              ; convert PPN to phys offset
+        ORR     r9, r9, #ts_phys_mem        ; add offset to start of phys mem
+;
+; r9 now has the address of the current page - read the page marker for that page.
+;
+        LDR     r9, [r9, #ts_pmark_pos]     ; r9 = contents of loc pointed to by
+                                            ;      r9 + ts_pmark_pos.
+;
+; Check that read_p_mark is valid. 
+;
+; Either the value read is the expected pagemark, junk (no memory) or an 
+; aliased pagemark - if it's aliased, then either the memory or the MEMC 
+; isn't decoded that far.
+; Bump down and try a bit lower, until it's OK.
+;
+        CMP     r4, r9                      ; Is page-mark expected value ?
+        BNE     %B0
+
+;
+; Found a pagemarker in the proper place. Check that the number of pages that
+; appear to be present are the same as the number found by ts_count_CAMs
+; (i.e. the memory size / page size).
+;
+        SUB     r0, r0, #1              ; convert count -> max page number
+        CMPS    r0, r8
+        BNE     ts_bad_CAM_count
+;
+; If all is well, we should have the maximum usable page number in r8.
+;
+; Need to reset page 0 in the CAM entries, currently all pages are mapped to page 5.
+; We need to have logical page 0 mapped to physical page 0.
+;
+        MOV      r0, #&00                   ; r0 = &00, the page to map.
+        MOV      r1, #&00                   ; r1 = &00, the page to map to.
+        MOV      r2, #&00                   ; r2 = &00, set the protection level.
+        BL       ts_set_camp
+;
+; Check we can still see the data abort vector at physical page zero
+; - no good continuing if we can't.
+;
+        MOV     r0, #ts_phys_mem
+        LDR     r0, [r0, #(ts_dab_vector - ts_vectors)]
+        LDR     r1, ts_dab_vector
+        CMPS    r0, r1
+        BNE     ts_bad_dab_vector
+
+;
+; Now lets get on with the testing.
+;
+
+2       ADRL    r10, ts_logpages            ; logp = &logpages[0]
+
+3       LDR     r0, [r10]                   ; r0 = page to test
+        CMP     r0, #&00                    ; last entry ?
+        BNE     %F4
+        MOV     r1, r8                      ; r1 = r8, page under test
+        BL      ts_set_cam                  ; Gosub ts_set_cam.
+        LDR     r0, [r10]                   ; r0 current logical test page
+        MOV     r1, r8                      ; r1 = current test page
+        BL      ts_check_mapped             ; Gosub ts_check_mapped.
+        B       %F5
+
+4       ADD     r12, r8, #&01
+        BL      ts_count_CAMs               ; get total number of pages
+        SUB     r0,r0,#1                    ; make a mask for useable page
+        AND     r0,r0,#&7f                  ; numbers - min(128, num_pages)
+        AND     r12, r12, r0                ; r12 -> (r12 + 1) masked 
+        MOV     r0, #&00                    ; to useable page numbers.
+        MOV     r1, r12
+        BL      ts_set_cam                  ; Setup a page for vectors
+        LDR     r0, [r10]                   ; r0 = current logical test page.
+        MOV     r1, r8                      ; r1 = current physical test page.
+        BL      ts_set_cam                  ; Setup a page to test
+
+        LDR     r0, [r10]                   ; look up logical page again.
+        MOV     r1, r8                      ; recall physical page.
+        BL      ts_check_mapped             ; check the ts_set_cam worked.
+        MOV     r1, r12                     ; unmap the vector page
+        BL      ts_set_cam_idle
+
+5       ADD     r10, r10, #&04              ; next entry in test list.
+        ADRL    r0, ts_logpagesend          ; r0 = ts_logpagesend.
+        CMP     r10, r0                     ; repeat until list of logical
+        BLO     %B3                         ; pages all done.
+
+        MOV     r1, r8                      ; unmap the page we just tested
+        BL      ts_set_cam_idle
+
+        SUBS    r8, r8, #1                  ; bump phys page counter down.
+        ANDS    r8,r8,r8
+        BGE     %B2                         ; If r8 >= 0 Then branch back to 2.
+
+        ANDS    r0,r0,#0
+        MOV     pc,r13                  ; all done and passed
+
+;
+; ****************************************************************************
+;
+ts_copy_vectors 
+;
+; Copies the vectors to the physical page in r0 (preserved) also copies
+; pagemark + phypage.
+; Expects r1 (preserved) to hold log2 of pagesize
+;
+        ROUT                                ; Local Branches.
+
+        ADR     r2, ts_vectors              ; r2 = source address
+        LDMIA   r2, {r4-r11}                ; r4 - r11 = loc pointed to by r2, post inc.
+
+        MOV     r3, r0, LSL r1              ; r3 = r0 * 2**r1 .
+        ORR     r3, r3, #ts_phys_mem        ; r3 = r3 OR ts_phys_mem.
+        STMIA   r3, {r4-r11}                ; loc pointed to by r3, post inc = r4 to r11.
+;
+; find out which memc is handling the page (r0), then assign the appropiate memc_code.
+; Add in the page number and pagemark, then store into the required position in the
+; page in question.
+;
+        ADR     r2, ts_memc_codes           ; r2 = address of table with the memc codes.
+        LDRB    r2, [r2, r0, LSR#7]         ; r2 = memc code for this phys page.
+        ORR     r2, r0, r2, LSL #24         ; OR in phys page number.
+        ORR     r2, r2, #ts_pagemark        ; OR in pagemark.
+        STR     r2, [r3, #ts_pmark_pos]     ; loc pointed to by r1 + ts_pmark_pos = pagemark.
+        MOV     pc, lr                      ; Return to caller.
+;
+; ****************************************************************************
+;
+ts_set_cam_idle
+;
+; This module will program the physical page (r1) to the logical page 5, ts_vrest and
+; continue onto the next section ts_set_cam.
+;
+        ROUT                                ; Local Branches.
+        MOV     r0, #ts_vrest               ; r0 = ts_vrest, = unused logical page.
+;
+; ****************************************************************************
+;
+ts_set_cam
+;
+; This module will program the physical page (r1) to the logical page (r0) at
+; protection mode 0 and continue onto the next section ts_set_camp.
+;
+        MOV     r2, #&00                    ; r2 = &00, memory prot level 0.
+;
+; ****************************************************************************
+;
+ts_set_camp
+;
+; This module will map a range the physical pages (r1) to the logical page (r0) and
+; set the protection mode (r2). This module will return to the location from where
+; either itself or ts_set_cam or ts_set_cam_idle were called from.
+;
+; Corrupts r0,r1,r2,r3,r4,r6,r9,r11
+;
+; Calls the RISC OS routine BangCam to do the PPNO, LPNO bit switching.
+; First, jumble the registers to suit BangCam ..
+;
+; r2  = CAM entry (PPNO)
+; r3  = logical address
+; r9  = current MEMC setting (for pagesize)
+; r11 = PPL
+;
+        MOV     r3,r0           ; logical page number
+        MOV     r11,r2          ; protection level 
+        MOV     r2,r1           ; physical page number
+        MOV_fiq r0, r11_fiq     ; MEMC configuration
+        MOV     r9, r0          ; keep a copy in r9
+        MOV     r1, r9, LSR #2
+        AND     r1, r1, #3      ; calculate pagesize shift
+        ADD     r1, r1, #12
+        MOV     r3, r3, LSL r1  ; convert LPN to logaddr
+        B       BangCam         ; return thro' BangCam
+
+;
+; ****************************************************************************
+;
+ts_check_mapped
+;
+; This routine will check that the CAM has been programed correctly and that the required
+; page is responding when asked. A quick test is made to check that other pages are not 
+; responding as well.
+;
+; logical page  in r0,
+; physical page in r1,
+; test that they are the same.
+;
+; No return value : reports faults directly and returns thro' r13
+;
+; Uses (corrupts) r0,r1,r2,r3,r4,r5,r6,r7
+;
+; Find out which memc is handling the page (r1), then assign the appropiate memc_code.
+; Add in the page number and pagemark, then compare that pagemark with those found
+; in memory at the expected logical and physical addresses.
+;
+; This code assumes that any system with multiple MEMCs will always have 32K pages.
+;
+        ROUT                                ; Local Branches.
+
+        MOV     r3, r0                      ; save the current logical pagenumber.
+        MOV     r5, lr                      ; Preserve link register in case of Abort.
+        ADR     r2, ts_memc_codes           ; r2 = address of table with the memc codes.
+        LDRB    r2, [r2, r1, LSR#7]         ; fetch the memc code for this page.
+        ORR     r2, r1, r2, LSL #24         ; build the page number into the pagemark
+        ORR     r2, r2, #ts_pagemark        ; build in the pagemark magic number
+;
+; r2 should now have the page_mark for the current page (r1).
+; calculate the shift to convert page number to memory offset.
+;
+        MODE    FIQ_mode
+        MOV     r4, r11_fiq, LSR #2             ; pagesize / 4K
+        MODE    SVC_mode
+        AND     r4, r4, #3
+        ADD     r4, r4, #12
+;
+; if the mapping failed completely, the test might abort
+;
+        MOV     r6, #&00                    ; r6 = &00, clear expected abort flag.
+        MOV     r7, #&94                    ; r7 = &94, set abort expected flag.
+;
+; make the pointers and test the contents
+;
+        MOV     r0, r0, LSL r4              ; r0 = LPN * pagesize.
+        LDR     r0, [r0, #ts_pmark_pos]     ; r0 = contents of loc in r0  + ts_pmark_pos.
+        CMP     r6, #94                     ; did that fetch abort ?
+        ADREQ   r4, %F14                    ; mapping totally failed
+        BEQ     ts_CAM_fail
+        MOV     r1, r1, LSL r4              ; r1 = PPN * pagesize.
+        ORR     r1, r1, #ts_phys_mem        ; r1 = r1 ORed with ts_phys_mem.
+        LDR     r1, [r1, #ts_pmark_pos]     ; r1 = contents of loc in r1  + ts_pmark_pos.
+        CMP     r0, r1                      ; Are the read pagemarks equal ??
+        ADRNE   r4, %F10
+        BNE     ts_CAM_fail                 ; Failed : mapping not equal.
+        CMP     r0, r2                      ; 
+        ADRNE   r4, %F11
+        BNE     ts_CAM_fail                 ; Failed : map equal, but corrupt
+;
+; test that the page doesn't exist anywhere else
+;
+        MOV     r2, #1
+0       EOR     r0, r2, r3                  ; Flip a (walking) bit in the LPN.
+        CMP     r0, #ts_vrest               ; Is r0 = ts_vrest ?? Where all the pages are 
+                                            ; mapped to.
+        BEQ     %F1                         ; If r0 = ts_vrest then branch forward to 1.
+;
+; The following instruction should abort.
+;
+        MOV     r0, r0, LSL r4              ; r0 = LPN * pagesize.
+        MOV     r6, #&00                    ; r6 = &00, clear abort handled flag.
+        MOV     r7, #&94                    ; r7 = &94, set abort expected flag.
+        LDR     r0, [r0, #ts_pmark_pos]     ; get a possible pagemark from this page.
+        CMP     r6, #&94                    ; Did we go thro' the abort handler ?
+        BEQ     %F1                         ; If equal then an abort happened, good !
+;
+; Not aborted - is it page zero, where the vectors live ?
+;
+        TEQS    r2, r3
+        BEQ     %F1                        ; yes - that SHOULDN'T abort
+;
+; Fault - is the page mapped there the same as our test page ?
+;
+        CMP     r0, r1 
+        ADREQ   r4, %F12                   ; Failed : phys page also mapped here
+        ADRNE   r4, %F13                   ; Failed : page not unmapped
+        EOR     r3, r2, r3                 ; remake the duff LPN for the error display
+        B       ts_CAM_fail
+                                            ; If equal then no abort happened, not good !!
+
+1       MOV     r2, r2, LSL#1               ; bump to next-bit-set page number
+        CMP     r2, #(ts_MAX_CAMS :SHL: 1)  ; Hit number of logical pages ?
+        BLT     %B0                         ; If r2 < maximum number then loop again.
+
+        MOV     r7, #0                      ; no longer expecting aborts
+        MOV     pc, r5                      ; Return to caller.
+
+;
+;       Indicate that CAM mapping test failed (PPN is not at LPN)
+;       Display r8, the physical page number and r3, the logical page.
+;
+;    ***This error exit returns to the CALLER of check_mapped, thro' r13***
+;
+
+10
+        =       "CAM map",&88,&ff,&ff,&ff,".",&ff,&ff,&ff,&ff,0
+11
+        =       "CAM pmk",&88,&ff,&ff,&ff,".",&ff,&ff,&ff,&ff,0
+12
+        =       "CAM als",&88,&ff,&ff,&ff,".",&ff,&ff,&ff,&ff,0
+13
+        =       "CAM unm",&88,&ff,&ff,&ff,".",&ff,&ff,&ff,&ff,0
+14
+        =       "CAM abo",&88,&ff,&ff,&ff,".",&ff,&ff,&ff,&ff,0
+
+        ALIGN
+
+
+ts_CAM_fail
+        MOV     r0, r8, LSL #16         ; physical page #
+        LDR     r1, =&ffff
+        AND     r1, r1, r3
+        ORR     r0, r0, r1              ; add logical page #
+        MOV     r8, r0, LSL #4
+        MOV     r6, #0                  ; no longer expecting aborts
+        ORRS    r0, r0, #1
+        MOV     pc, r13
+
+;
+; **************************************************************************
+;
+
+; Routine to return expected number of physical pages in r0. 
+; Uses memory size determination from r10_fiq and page mode from r11_fiq.
+; Returns pagesize as power-of-two in r1, for pagenumber->address calcs.
+
+ts_count_CAMs
+
+        MODE    FIQ_mode
+        MOV     r0,r10_fiq,LSR #12      ; get values determined 
+        MOV     r1,r11_fiq,LSR #2       ; by MemSize
+        MODE    SVC_mode
+
+        AND     r1,r1,#3                ; memory / pagesize
+        MOV     r0,r0,LSR r1
+        ADD     r1,r1,#12               ; page bit-shift value
+
+        MOVS    pc,lr
+
+
+;
+; **************************************************************************
+;
+        ROUT
+
+;       Indicate that an unexpected number of CAM pages were found.
+;
+;       Display as "CAM ##    eee.fff"
+;
+;       where eee is the expected maximum page number (r0), fff is the number
+;       of of the highest page actually found (r8).
+
+0
+        =       "CAM ##",&89,&ff,&ff,&ff,".",&ff,&ff,&ff,0
+        ALIGN
+
+ts_bad_CAM_count
+        ADD     r8, r8, r0, LSL #12
+        MOV     r8, r8, LSL #8
+        ADR     r4, %B0
+        ORRS    r0, r0 ,#1
+        MOV     pc, r13
+;
+; **************************************************************************
+;
+
+;       Indicate that the DAB vector wasn't visible in physmem
+
+0
+        =       "CAM vec",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
+        ALIGN
+
+ts_bad_dab_vector
+        ADR     r4, %B0
+        EOR     r8,r0,r1                ; indicate which bits are lost
+        ORRS    r0, r0, #1
+        MOV     pc, r13
+;
+; **************************************************************************
+
+;       Routine to indicate that an unexpected abort was found.
+
+0
+        =       "DAB @",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff, 0
+        ALIGN
+
+ts_unxvect
+        ADR     r4, %B0
+        SUBS    r8, r14_svc, #8         ; indicate the aborting instruction
+        BL      ts_SendText
+        ORRS    r0, r0, #1
+        MOV     pc, r13
+
+
+
+        LTORG
+
+ END
diff --git a/TestSrc/Mem5 b/TestSrc/Mem5
new file mode 100644
index 0000000000000000000000000000000000000000..607a8b8e1d1c47c9ac1c027c522a3acc14133554
--- /dev/null
+++ b/TestSrc/Mem5
@@ -0,0 +1,316 @@
+;>MEM5D_SCR
+;
+; RISC OS 2+ BOOT TEST SOFTWARE.
+; MEMORY TEST 5 VERSION D.      BRIAN RICE 10-01-90.
+; 04-Apr-90     ArtG    0.1     Use memory size to determine page count
+; 11-Apr-90	ArtG	0.2	Changes to permit use of BangCam
+;
+; This file will be called by MEM6x_SCR for the purposes of assembly.
+; This file requires the assembly of MEM4x_SCR to be perfromed at the
+; same time. The program will call the cam setting routines in the cam
+; test program.
+;
+; This file will test MEMCs ability to assert its protection over
+; logical pages.
+; The test code for this test was taken from the A680 test code.
+; The Arm CPU has three mode of operation, Supervisor, Operating System.
+; and User. Most of the time the machine will operate in user mode, in this.
+; mode the designers do not want the user to have full access to the memory.
+; map, therefore the MEMC(s) will check that the CPU has the appropiate
+; level of authorisation to access specific area of memory.
+; User mode is the lowest mode, allowing limited R/W access to the ram.
+; Operating System is next up the list and is allowed some more access to
+; to the ram than user mode.
+; Supervisor mode this is the highest and the CPU has unlimited access to
+; the entire memory map. 
+;
+; This version has the "my abort" routine in it not the ts_dab_exp0..5 routine as
+; coded from the A680 code.
+;
+; Set up some variables.
+;
+ts_wks_word     *   36                      ; Offset of word for workspace.
+;
+; ****************************************************************************
+;
+ts_memc_prot
+;
+; This module will map and assign protection mode 0 to all the pages. The
+; module will then perfrom a read and write operations in supervisor and
+; user modes. This is repeated for the three (four) protection modes.
+; The module will check after every protection mode level that the required
+; responses have been returned.
+;
+; Set up the memory, map and assign protection mode 0.
+;
+        ROUT                                ; Local Branches.
+        MOV     r13, lr                     ; Preserve the link register.
+        MOV     r12, #&00                   ; r12 = The physical page to test.
+
+0       ADD     r8, r12, #&01               ; Get a page to use as vectors,
+	BL	ts_count_CAMs		    ; get total number of pages
+	SUB	r0,r0,#1 		    ; make a mask for useable page
+	AND	r0,r0,#&7f		    ; numbers - min(128, num_pages)
+        AND     r8, r8, r0
+
+        MOV     r1, r8                      ; r1 = r8,  r1 = physical page 0.
+        MOV     r0, #&00                    ; r0 = &00, r0 = logical page 0.
+        BL      ts_set_cam                  ; Gosub ts_set_cam, set the CAM up.
+;
+; Set protection mode 0 and test that page.
+;
+        MOV     r2, #&00                    ; r2 = &00, r2 = protection mode 0.
+        BL      ts_mem_prot                 ; Gosub ts_mem_prot.
+        CMP     r3,#&0F                     ; Is r3 = &0F ? r3 = Super Read/Write ok.
+                                            ;                    O/S   Read/Write ok.
+                                            ;                    User  Read/Write ok.
+	MOV	r2, #0
+        BNE     ts_prot_fail                ; If r3 <> &0F Then branch to fail routine.
+;
+; Set protection mode 1 and test that page.
+;
+        MOV     r2, #&01                    ; r2 = &01, r2 = protection mode 1.
+        BL      ts_mem_prot                 ; Gosub ts_mem_prot.
+	[ CPU_Type = "ARM600"
+	CMP	r3,#&0f			    ; no ABORT line to ARM600
+	|
+	CMP     r3,#&0B                     ; Is r3 = &0B ? r3 = Super Read/Write ok.
+	]                                   ;                    O/S   Read/Write ok.
+                                            ;                    User  Read only ok.
+
+	MOV	r2,#1
+        BNE     ts_prot_fail                ; If r3 <> &0B Then branch to fail routine.
+;
+; Set protection mode 2 and test that page.
+;
+        MOV     r2, #&02                    ; r2 = &02, r2 = protection mode 2.
+        BL      ts_mem_prot                 ; Gosub ts_mem_prot.
+	[ CPU_Type = "ARM600"
+	CMP	r3,#&0f			    ; no ABORT line to ARM600
+	|
+        CMP     r3,#&03                     ; Is r3 = &03 ? r3 = Super Read/Write ok.
+	]                                   ;                    O/S   Read only ok.
+                                            ;                    User  No Access ok. 
+	MOV	r2,#2
+        BNE     ts_prot_fail                ; If r3 <> &03 Then branch to fail routine.
+;
+; Set protection mode 3 and test that page.
+;
+        MOV     r2, #&03                    ; r2 = &03, r2 = protection mode 3.
+        BL      ts_mem_prot                 ; Gosub ts_mem_prot.
+	[ CPU_Type = "ARM600"
+	CMP	r3,#&0f			    ; no ABORT line to ARM600
+	|
+        CMP     r3, #&03                    ; Is r3 = &03 ? r3 = Super Read/Write ok.
+	]                                   ;                    O/S   Read only ok.
+                                            ;                    User  No Access ok. 
+	MOV	r2,#3
+        BNE     ts_prot_fail                ; If r3 <> &03 Then branch to 
+                                            ; fail routine.
+;
+; Reset the page used to idle.
+;
+        MOV     r0, r12                     ; r0 = r12, idle the pages 
+                                            ; being used.
+        BL      ts_set_cam_idle             ; Gosub ts_set_cam_idle.
+        MOV     r0, r8                      ; r0 = r8, idle the pages 
+                                            ; being used. 
+        BL      ts_set_cam_idle             ; Gosub ts_set_cam_idle.
+;
+; Increment the physical page counter and check that all the pages are 
+; done, else finish.
+;
+        BL      ts_count_CAMs
+        ADD     r12, r12, #&01              ; do the next physical page.
+        CMP     r12, r0                     ; Done all pages ?
+        BLT     %B0                         ; If r12 <= cam_entries, 
+                                            ; branch back to 0.
+
+        ANDS    r0, r0, #0                  ; set zero flag : test passed
+        MOV     pc, r13                     ; Return to caller.
+;
+; **************************************************************************
+;
+; Branch here when ts_memc_prot fails to get the proper result from
+; ts_mem_prot.
+;
+; At this point, 
+;                 
+; r3  is a map of permitted ops (user read, user write, sys read, sys write) 
+; r2  is the memc protection mode
+; r12 is the physical page number.
+;
+; This is displayed as :   
+;
+;       PPL bad l.a.pppp
+;
+; where l is the PPL set on that page (0, 1, 2 or 3)
+;       a is a bitmap of the actual operations permitted (ur.uw.or.ow)
+;       p is the physical page number tested
+;
+
+0
+        =       "PPL bad",&88,&ff,".",&ff,".",&ff,&ff,&ff,&ff,0
+        ALIGN
+
+ts_prot_fail
+        AND     r2, r2, #&0f
+        MOV     r0, r2, LSL #20          ; mode bits
+        AND     r3, r3, #&0f
+        ORR     r0, r0, r3, LSL #16     ; permitted ops bits
+        BIC     r12, r12, #&ff000000
+        BIC     r12, r12, #&ff0000
+        ORR     r0, r0, r12             ; current page number
+
+
+        ADR     r4, %B0                 ; get fail message  
+        MOV     r8, r0, LSL #8          ; shift number to suit ts_SendText
+        ORRS    r0, r0, #1              ; fail flag
+        MOV     pc, r13
+
+
+;
+;
+; This section will test that the physical page referenced in r12 at the set 
+; protection mode. During the operation of this module, aborts are expected to happen.
+; The aborts are handled by the routine ts_dab.
+;
+; The system is running in supervisor mode and thus to check the user mode read / writes
+; the address translator flag is used. The CPU has a signal called -TRANS which when used
+; with MEMC forces the an address translation to be performed, this is not done whilst
+; in supervisor mode because it has unlimited access to the memory map. The address
+; translator falg (T) is used with STR and LDR instructions only, the effective result of
+; adding the (T) to the opcode is to force the instruction to be executed as if the CPU
+; was in user mode, thus unauthorised accesses will cause an abort to occur.
+;
+; IN:
+;       r12 - physical page.
+;       r2  - protection mode.
+; OUT:
+;       r3  - access pattern.
+;             r3 = &0F, Super Read/Write ok, O/S Read/Write ok, User Read/Write ok.
+;             r3 = &0B, Super Read/Write ok, O/S Read/Write ok, User Read only ok.
+;             r3 = &03, Super Read/Write ok, O/S Read only ok,  User No Access ok.
+;
+ts_mem_prot
+;
+; Set up data to write and read from memory.
+;
+        MOV     r10, lr                     ; Preserve link register.
+        MOV     r1, r12                     ; r1 = physical page.
+        MOV     r0, #&01                    ; r0 = logical page 1.
+        BL      ts_set_camp 
+
+        MOV     r3, #&00                    ; Initialise access pattern.
+	MOV_fiq	r5, r11_fiq		    ; get MEMC control
+	AND	r5, r5, #&C
+	ADR	r9, ts_ppl_tptrs
+	LDR	r9, [r9, r5]		    ; get test address for this pagesize
+;
+; Test 1 system mode - write.
+;
+        MOV     r6, #&00                    ; r6 = &00, clear expected abort flag.
+        MOV     r7, #&94                    ; r7 = &94, set abort expected flag.
+;
+; The following instruction may abort.
+;
+        STR     r1, [r9]                    ; Store r1 at loc pointed to by r9.
+        CMP     r6, #&00                    ; Is r6 = &00 ? If not then abort happened.
+        ORREQ   r3, r3, #&01                ; If r6 = &00, Then update r3, access pattern.
+;
+; Test 2 system mode - read.
+;
+        MOV     r6, #&00                    ; r6 = &00, clear expected abort flag.
+        MOV     r7, #&94                    ; r7 = &94, set abort expected flag.
+;
+; The following instruction may abort.
+;
+        LDR     r1, [r9]                    ; Load r1 from loc pointed to by r9.
+        CMP     r6, #&00                    ; Is r6 = &00 ? If not then abort happened.
+        ORREQ   r3, r3, #&02                ; If r6 = &00 Then update r3, access pattern.
+;
+; Test 3 user mode - write.
+;
+        MOV     r6, #&00                    ; r6 = &00, clear expected abort flag.
+        MOV     r7, #&94                    ; r7 = &94, set abort expected flag.
+;
+; The following instruction may abort.
+;
+        STRT    r1, [r9]                    ; Store r1 at loc pointed to by r9.
+        CMP     r6, #&00                    ; Is r6 = &00 ? If not then abort happened.
+        ORREQ   r3, r3, #&04                ; If r6 = &00 Then update r3, access pattern.
+;
+; Test 4 user mode - read.
+;
+        MOV     r6, #&00                    ; r6 = &00, clear expected abort flag.
+        MOV     r7, #&94                    ; r7 = &94, set expected expected flag.
+;
+; The following instruction may abort.
+;
+        LDRT    r1, [r9]                    ; Load r1 from loc pointed to by r9.
+        CMP     r6, #&00                    ; Is r6 = &00 ? If not then abort happened.
+        ORREQ   r3, r3, #&08                ; If r6 = &00 Then update r3, access pattern.
+        MOV     pc, r10                     ; Return to caller.
+
+;
+; addresses (a short way up page 1) to test PPL aborts
+;
+
+ts_ppl_tptrs
+	&	( 4 * 1024) + ts_wks_word
+	&	( 8 * 1024) + ts_wks_word
+	&	(16 * 1024) + ts_wks_word
+	&	(32 * 1024) + ts_wks_word
+;
+;
+ts_dab
+;
+; This routine provides the handling when a DATA ABORT occurs.
+; The routine will if the abort was DATA cause the program to skip over the instruction
+; that caused the abort first place.
+; Data aborts could come from a variety of sources, in this module we are only concerned
+; about a select group of aborts. This abort routine is called instead of the "usuall"
+; abort routine. All that is required from this abort routine is to set a flag to
+; indicate that an abort occured. Therefore this routine needs to be told that the
+; abort that caused the routine to be called is either one of mine or not, (expected
+; or unexpected). To achive this &94 is placed in r7. The abort routine will check
+; for the presence of &94 in r7, if present then the abort is an expected abort.
+; The abort routine will then copy r7 into r6, which is used as a flag to indicate
+; that an abort occured and that it was an expected abort. Then the routine will
+; return control to the program at the location after the instruction that caused to
+; abort to occur.
+; The return address is stored by the CPU into the link regester lr (r14), sort off.
+; It must be remembered that the PC is always 2 instructions ahead. E.G. if the
+; instruction that causes the abort is at &2000, then the lr will have &2008 in it,
+; but we want to return to the location after the abort instruction, &2004. Therefore to
+; return to the correct location &04 is removed from the lr and this is put into the pc.
+; If the abort was not expected then the routine will jump to the end and another
+; routine will show that an unexpected abort was generated.
+;
+; IN:
+;       r6 - Equals &00, cleared just before the instruction that could cause an abort.
+;       r7 - Equals &94, set just before the instruction that could cause an abort.
+;
+; OUT:
+;       r6 - Equals &94, set if an abort happened and was expected.
+;       r7 - Equals &94, preserved.
+;
+        ROUT                                ; Local Branches.
+;
+; Check that it is an expected abort and not an unexpected abort.
+;
+        CMP     r7, #&94                    ; Is r7 = &94, abort expected value.
+        BNE     ts_dab_unexp                ; If <> &94, Then branch to unexpected
+                                            ; abort handler.
+;
+; It is an expected  abort, so handle it.
+;
+        MOV     r6, r7                      ; r6 = r7, indicates that an abort happened.
+        SUB     pc, lr, #&04                ; pc = link reg - &04.
+                                            ; Skip over aborting instruction.
+                                            ; By reloading the pc we return to the area
+                                            ; of code where the abort occured but 4
+                                            ; locations further on.
+
+
+ END
diff --git a/TestSrc/TestMain b/TestSrc/TestMain
new file mode 100644
index 0000000000000000000000000000000000000000..22fcf0d1dada040ca950d51cadf2854ae7a70079
--- /dev/null
+++ b/TestSrc/TestMain
@@ -0,0 +1,78 @@
+; > TestMain
+
+
+; Main assembly file for isolated assembly of machine test software
+
+MEMCADR         *       &3600000
+ROM		*	&3800000
+
+ [ MEMC_Type = "IOMD"
+VideoPhysRam *  &02000000               ; Amazing - it's in the same place!
+DRAM0PhysRam *  &10000000               ; 4 DRAM banks
+DRAM1PhysRam *  &14000000
+DRAM2PhysRam *  &18000000
+DRAM3PhysRam *  &1C000000
+DRAMBaseAddressMask * &1C000000         ; used to mask off bits after stealing video RAM
+PhysSpaceSize * &20000000               ; IOMD physical map is 512M big
+PhysROM *       &00000000               ; and real ROM starts at 0
+SAMLength *     512*4                   ; SAM length in bytes for 1 bank of VRAM
+EASISpacePhys * &08000000
+EASISpace *     PhysSpace + EASISpacePhys
+ |
+VideoPhysRam *  &02000000
+PhysSpaceSize * &04000000               ; MEMC1 physical map is 64M big
+PhysROM *       &03800000
+PhysRamPhys *   &02000000               ; physical space starts here
+ ]
+
+		ORG	ROM
+
+	        GET     TestSrc/Begin
+CONT
+	        ADRL    r2,TestVIDCTAB
+		LDR	r0,=IOMD_MonitorType
+		LDR	r0,[r0]
+		ANDS	r0,r0,#IOMD_MonitorIDMask
+		ADDEQ	r2,r2,#(TestVVIDCTAB-TestVIDCTAB)
+        	MOV     r0,#ts_VIDCPhys
+08      	LDR     r1, [r2],#4
+        	CMP     r1, #-1
+        	STRNE   r1, [r0]
+        	BNE     %BT08
+
+		MOV	r9,#0
+10
+		ORR	r9,r9,#&40000000
+        	STR     r9,[r0]         ; write the border colour
+		ADD	r9,r9,#&00000005
+		ADD	r9,r9,#&00000300
+		ADD	r9,r9,#&00010000
+		AND	r9,r9,#&00ffffff
+
+		MOV	r1,#&10000
+12		ADDS	r1,r1,#(-1)
+		BNE	%BT12
+
+		B	%BT10
+
+;
+; The RISC-OS MEMC setup code is re-used to ensure similar 
+; detection of memory configuration. The MEMC1 code is modified only
+; to remove an unnecessary function.
+
+		GBLL	Module
+Module		SETL	{FALSE}
+		GBLL	AssembleSAtest
+AssembleSAtest	SETL	{FALSE}
+
+DynAreaFlags_DoublyMapped	*    1 :SHL: 6
+DynAreaFlags_NotCacheable	*    1 :SHL: 5
+DynAreaFlags_NotBufferable	*    1 :SHL: 4
+DynAreaFlags_APBits     	*   15 :SHL: 0      ; currently onl
+
+
+	        END
+
+
+
+
diff --git a/TestSrc/Vidc b/TestSrc/Vidc
new file mode 100644
index 0000000000000000000000000000000000000000..feaa74f81d8a727015ffeaa401442e6d0c03d81c
--- /dev/null
+++ b/TestSrc/Vidc
@@ -0,0 +1,532 @@
+; > TestSrc.VIDC
+
+        TTL RISC OS 2+ POST video controller
+;
+; The video outputs cannot be tested directly, and VIDC permits only
+; write operations on its registers. 
+; This module performs two tests to verify VIDC's operation 
+;
+;       - measure mode 0 FLBK period against IOC timer 
+;       - check that sound DMA occurs (MEMC reports DMA complete)
+;
+; This code contains timing loops affected by gross changes in processor 
+; speed, and will re-initialise MEMC with 4K pages and continous refresh.  
+;
+;------------------------------------------------------------------------
+; History
+;
+; Date          Name            Comment
+; ----          ----            -------
+; 18-Dec-89     ArtG            Initial version
+; 04-Apr-90     ArtG            Use saved MEMC control register setting
+; 20-Jun-93     ArtG            Medusa VIDC20 / IOMD changes
+; 18-Nov-94     RCM             Morris changes
+;
+;
+;------------------------------------------------------------------------
+
+
+VIDC_CLOCK_CONTROL      *       ts_S5_base :OR: &0048  ; Fox VIDC clock control
+VIDC_CLOCK_NORMAL       *       &0
+
+VIDC_VFLYWAIT           *       72000           ; 200mS timeout loop
+VIDC_SOUNDWAIT          *       40000           ; 100mS  timeout loop
+
+MEMC_Sstart             *       MEMCADR   :OR: &80000
+MEMC_SendN              *       MEMCADR   :OR: &A0000
+MEMC_Sptr               *       MEMCADR   :OR: &C0000
+MEMC_Son                *                      &00800
+
+ts_Soundbuf             *       &200            ; relative to PhysRam
+ts_Soundbuf_length      *       &400
+
+        [ VIDC_Type = "VIDC20"
+VIDSTIM0                *       &A0000000       ; base VIDC20 register values
+VIDSTIM1                *       &A1000000
+VIDSFR                  *       &B0000000
+VIDSCR                  *       &B1000005
+VIDDMAoff               *       &94000024
+VIDVCOWAIT              *       &5
+VIDVCOFREQ              *       &D0000404
+        |
+VIDSTIM0                *       &60000000       ; base VIDC register values
+VIDSTIM1                *       &64000000
+VIDSFR                  *       &C0000100
+        ]
+
+        SUBT    FLBK period test
+;
+; This test attempts to validate the video timing system by checking for
+; the proper period from the vertical flyback pulse. To make life easier, 
+; the test is performed only in mode 0 - i.e a 20mS period.
+;
+; This test contains a processor-clock timed loop as an outer limit :
+; it assumes that the processor will never run more than a factor of ten
+; faster than an 8Mhz ARM2.
+; This is valid provided that this code isn't run with an ARM3 cache enabled.
+; 
+
+; Initialise video clock control (for FOX)
+; Initialise VIDC
+; Clear IR interrupt request in IOC
+; Poll IOC until IR appears (if ever)
+; Set IOC timer 0 to 32 mS
+; Clear IR interrupt request in IOC
+; Poll IOC until IR appears (if ever)
+; Check timer 0 has counted down 20 mS (19.8 - 20.2 mS)
+; Return zero flag set on OK, clear on test failure.
+
+
+ts_VIDC_period ROUT
+
+        ; Initialise VIDC clock and VIDC
+
+        [ VIDC_Type = "VIDC1a"
+        LDR     r3, =VIDC_CLOCK_CONTROL         ; 
+        MOV     r1, #VIDC_CLOCK_NORMAL
+        STRB    r1, [r3]
+        ]
+
+        MOV     r7, #0
+        MOV     r1, #ts_VIDCPhys
+        ADRL    r6, TestVIDCTAB
+00      LDR     r0, [r6],#4                     ; setup using main table
+        CMP     r0, #-1
+        STRNE   r0, [r1]
+        BNE     %BT00
+01      LDR     r0, [r6],#4                     ; enable DMA using 2nd table 
+        CMP     r0, #-1
+        STRNE   r0, [r1]
+        BNE     %BT01
+        
+        ; Wait for the start of a flyback period
+
+04
+        LDR     r3, =IOC
+        [ MEMC_Type = "IOMD"
+        LDR     r1, [r6]                        ; get FSIZE value from end of TestVIDCTAB
+        STR     r1, [r3, #IOMD_FSIZE]
+        ]
+        MOV     r1, #vsync_bit
+        STRB    r1, [r3, #IOCIRQCLRA]
+        LDR     r2, =VIDC_VFLYWAIT              ; long timeout loop - C 200mS
+
+05      LDRB    r1, [r3, #IOCIRQSTAA] 
+        ANDS    r1, r1,  #vsync_bit    
+        BNE     %06                    
+        SUBS    r2, r2,#1               
+        BNE     %05                   
+
+        LDR     r0,=&fffff
+        ORRS    r0, r0,r7, LSL #20              ; Failed : clear 0 flag
+        MOV     pc, r14                         ; ... and quit
+
+        ; Set up IOC timer 0
+06
+        LDR     r1, =(32 * 1000 * 2)            ; 32mS upper limit
+        STRB    r1, [r3, #Timer0LL]
+        MOV     r0, r1, LSR #8
+        STRB    r0, [r3, #Timer0LH]
+        MOV     r0, #0
+        STRB    r0, [r3, #Timer0GO]             ; start the timer
+
+        ; clear the IR and T0 bits
+
+        MOV     r0, #(vsync_bit :OR: timer0_bit)
+        STRB    r0, [r3,#IOCIRQCLRA]
+
+        ; wait for what should be a complete vflyback period
+
+10      LDR     r2, =VIDC_VFLYWAIT              ; timeout loop -  C 200 msec
+11      LDRB    r0, [r3,#IOCIRQSTAA]
+        TSTS    r0, #vsync_bit
+        BNE     %14                             ; wait for end of vsync
+
+        TSTS    r0, #timer0_bit                 ; or timer underflow
+        BNE     %13
+
+12      SUBS    r2, r2, #1                      ; or last-ditch timeout 
+        BNE     %11
+
+13      ORRS    r0, r0,#1                       ; Failed : clear 0 flag
+        MOV     r0, #0                          ; but return a zero
+        MOV     pc, r14                         ; ... and quit
+
+        ; finished in reasonable time : check against margins.
+
+14      STRB    r0, [r3, #Timer0LR]             ; latch the current count
+        LDRB    r2, [r3, #Timer0CL]
+        LDRB    r0, [r3, #Timer0CH]
+        ADD     r2, r2, r0, LSL #8
+
+        SUB     r2, r1, r2
+        MOV     r0, r2, LSR #1                  ; Vertical flyback time in uS
+
+        LDR     r1, =19800                      ; inside limits ?
+        SUBS    r2, r0, r1
+        BLE     %F20
+
+        LDR     r1, =400                        ; 19.8 -> 20.2 mS
+        CMPS    r2, r1
+        BGE     %F20
+        MOV     r1,#0                           ; OK -  0 indicates pass
+
+        ; After success using the 24MHz reference clock, select the
+        ; VCO clock (also at 24MHz) and ensure the test is passed after
+        ; a few cycles to allow the VCO to settle.
+
+20
+        [ VIDC_Type = "VIDC20"
+
+        TEQ     r7,#0                           ; if this is the first loop ..
+        BNE     %FT21
+        TEQ     r1,#0                           ; and it passed OK ..
+        BNE     %FT25
+        MOV     r2,#ts_VIDCPhys
+        LDR     r3,=VIDVCOFREQ                  ; set the vco to 24MHz
+        LDR     r4,=&E0000400                   ; and use the vco clock
+        STMIA   r2,{r3,r4}
+        MOV     r7,#VIDVCOWAIT                  ; set the vco test loop count
+        B       %BT04                           ; and run around again
+
+21      ORR     r0,r0,r7,LSL #20
+        SUBS    r7,r7,#1                        ; if all attempts now made
+        BEQ     %FT25                           ; return final result
+        TEQ     r1,#0                           ; else repeat until passed
+        BNE     %BT04
+        ]
+
+        ; return with zero flag set if timers were OK
+        ; measured time (in uS) in r0 if flyback was wrong,
+        ; bits 20+ show fail loop - 0 for refclk, 1 for vcoclk.
+
+25
+        ORRS    r1,r1,r1
+        MOV     pc, r14
+
+
+        SUBT    Sound DMA test
+;
+; This test runs the sound DMA system to prove the operation of VIDC and 
+; MEMC's sound DMA control and the operation of the SIRQ sound DMA complete
+; interrupt.
+; To avoid making a noise, set the sound muting bit on.
+;
+; Initialise MEMC sound DMA
+; Initialise VIDC sound channel
+; Initialise timer 0 and timer 1 to guard-band 10mS sound duration
+; Poll IOC until IL1 (SIRQ interrupt) becomes active
+; Check timer 0 has completed and timer 1 has not
+;
+
+ts_SIRQ_period  ROUT
+
+        ; set up MEMC to point to a buffer near the start of physical RAM,
+        ; labelled in r9_fiq by the early memory size tests (not MemSize) 
+        ; Registers are set as (address / 16)
+        ; Register bits are (register * 4) in VIDC address mask
+        ; Hence values written to MEMC + register offset + (pointer / 4)    
+
+
+        [ MEMC_Type = "IOMD"
+        MOV     r3,#IOMD_Base
+        MOV     r0,#(IOMD_DMA_C_Bit :OR: IOMD_DMA_E_Bit :OR: 16)
+        STR     r0,[r3,#IOMD_SD0CR]
+        MOV_fiq r0,r9                   ; zero the DMA buffer
+        ADD     r1,r0,#ts_Soundbuf_length
+        MOV     r2,#0
+02      STR     r2,[r0],#4
+        CMPS    r0,r1
+        BNE     %BT02
+        |
+        MOV_fiq r0,r11_fiq
+        BIC     r0, r0, #MEMC_Son        ; ensure sound DMA disabled
+
+        STR     r0, [r0]
+        LDR     r1, =(MEMC_SendN :OR: ((ts_Soundbuf + ts_Soundbuf_length) / 4))
+        STR     r1, [r1]
+        LDR     r2, =(MEMC_Sstart :OR: (ts_Soundbuf / 4))
+        STR     r2, [r2]
+        LDR     r0, =MEMC_Sptr          ; initialise Sptr and set up again ..
+        STR     r0, [r0]
+        STR     r1, [r1]
+        STR     r2, [r2]
+        ]
+
+        ; Set up VIDC for 8 channels, 10uS (/8) per sample
+
+        LDR     r0, =ts_VIDCPhys
+        [ VIDC_Type = "VIDC20"
+        LDR     r1, =VIDSCR             ; VIDC10 mode, 24Mhz clock
+        STR     r1, [r0]
+        LDR     r1, =VIDDMAoff
+        STR     r1, [r0]
+        ]
+        LDR     r1, =(VIDSTIM0 + 1)     ; channel 0 at 100% left
+        LDR     r2, =((VIDSTIM1 - VIDSTIM0) + 1)
+        MOV     r3, #7
+05      STR     r1, [r0]                ; .. up to 6 at 100% right
+        ADD     r1, r1, r2
+        SUBS    r3, r3, #1
+        BNE     %05
+        SUB     r1, r1, #4              ; finally ch7 at centre again
+        STR     r1, [r0]
+
+        LDR     r1, =(VIDSFR + 8)       ; 10uS/byte
+        STR     r1, [r0]
+
+        ; Set up the timer to limit at 20 us (10uS/sample, 1024-16 bytes => 10.08 mS)
+
+        LDR     r3, =IOC
+        LDR     r1, =(20 * 1000 * 2)        ; 20 mS upper limit
+        STRB    r1, [r3, #Timer1LL]
+        MOV     r0, r1, LSR #8
+        STRB    r0, [r3, #Timer1LH]
+
+        MOV     r0, #-1
+        STRB    r0, [r3, #IOCControl]           ; mute sound (on IOC system)
+        STRB    r0, [r3, #Timer1GO]             ; start the timer
+
+        [ MEMC_Type = "IOMD"
+        MOV     r0, #(IOMD_DMA_E_Bit :OR: 16)   ; enable the IOMD DMA
+        STR     r0, [r3,#IOMD_SD0CR]
+        MOV_fiq r0,r9                           ; set the buffer pointers
+        MOV     r4,#((ts_Soundbuf_length/2) - 16)
+        STR     r0,[r3,#IOMD_SD0CURA]
+        STR     r4,[r3,#IOMD_SD0ENDA]
+        LDR     r2,[r3,#IOMD_SD0ST]
+        ORR     r4,r4,#IOMD_DMA_S_Bit
+        STR     r0,[r3,#IOMD_SD0CURB]
+        STR     r4,[r3,#IOMD_SD0ENDB]
+        |
+        MOV_fiq r0, r11_fiq
+        ORR     r0, r0, #MEMC_Son
+        STR     r0, [r0]                        ; enable the MEMC1a DMA
+        ]
+
+        ; set long timeout, clear the IL1, T0 and T1 bits
+
+        LDR     r2, =VIDC_SOUNDWAIT             ; lastditch timeout loop
+        LDR     r0, =(timer0_bit :OR: timer1_bit) 
+        STRB    r0, [r3,#IOCIRQCLRA]
+
+
+        ; Wait until sound DMA completes (or up to about 100 mS), 
+        ; then check timers.
+
+10
+        [ MEMC_Type = "IOMD"
+        LDRB    r0,[r3, #IOMD_SD0ST]
+        AND     r0, r0, #(IOMD_DMA_O_Bit :OR: IOMD_DMA_I_Bit)
+        CMPS    r0, #(IOMD_DMA_O_Bit :OR: IOMD_DMA_I_Bit)
+        BEQ     %12
+        |
+        LDRB    r0, [r3,#IOCIRQSTAB]
+        ANDS    r0, r0, #sound_IRQ_bit
+        BNE     %12
+        ]
+        LDR     r0, [r3, #IOCIRQSTAA]
+        ANDS    r0, r0,#timer1_bit              ; timeout if timer 1 expires
+        BNE     %11
+
+        SUBS    r2, r2, #1                      ; or counter reaches zero
+        BNE     %10
+
+11      ORRS    r0, r0, #1                      ; Failed : clear 0 flag
+        MOV     r2, #0                          ; return a timeout value of 0
+        B       %15                             ; ... and quit
+
+        ; finished in reasonable time : check time remaining in t1
+        ; Time for DMA should be 10.24ms (1024 bytes at 10us/byte)
+        ; less up to the time to use the final 16-byte transfer, 160us.
+
+12      STRB    r0, [r3, #Timer1LR]             ; latch the current count
+        LDRB    r2, [r3, #Timer1CL]
+        LDRB    r0, [r3, #Timer1CH]
+        ADD     r2, r2, r0, LSL #8
+
+        SUB     r2, r1, r2
+        MOV     r2, r2, LSR #1                  ; Sound DMA time in uS
+
+        LDR     r1, =10030                      ; inside limits ?
+        SUBS    r0, r2, r1
+        BLE     %F13
+
+        LDR     r1, =260                        ; 10.03  -> 10.29 mS
+        CMPS    r0, r1
+        MOVLT   r1,#0                           ; inside limits : set Z flag
+
+13      ORRS    r1,r1,r1
+
+        ; return with zero flag set if time (in r2) was within limits
+
+15
+        [ MEMC_Type = "IOMD"
+        MOV     r0, #IOMD_DMA_C_Bit
+        STR     r0, [r3,#IOMD_SD0CR]
+        |
+        BIC     r0, r0, #MEMC_Son
+        STR     r0, [r0]
+        ]
+        MOV     r0, r2                          ; return the long time value
+        MOV     pc, r14
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+; Data tables: VIDC := mode 0, all palette black
+
+TestVIDCTAB
+
+        [ VIDC_Type = "VIDC1a"
+
+        & &00000000
+        & &04000000
+        & &08000000
+        & &0C000000
+        & &10000000
+        & &14000000
+        & &18000000
+        & &1C000000
+        & &20000000
+        & &24000000
+        & &28000000
+        & &2C000000
+        & &30000000
+        & &34000000
+        & &38000000
+        & &3C000000
+        & &40000000     ; Border -> black
+        & &44000000     ; Cursor -> black
+        & &48000000
+        & &4C000000     ; Palette programmed (avoid messy screen on reset)
+;
+; standard mode 0 setup (except display area disabled)
+;
+
+        & &807FC000
+        & &8408C000
+        & &881B0000
+        & &8C1EC000     ; HDSR
+        & &906EC000     ; HDER
+        & &94770000
+        & &9C400000
+        & &A04DC000
+        & &A4008000
+        & &A8050000     ; VBSR
+        & &AC098000     ; VDSR
+        & &B0000000     ; VDER < VDSR to disable screen DMA       B0000000
+        & &B44DC000     ; VBER
+        & &E00000B2
+;
+; Additional setup : cursor blanked, sound frequency test bit set 
+;
+        & &C0000100     ; SFR  NB. TEST BIT! - also DFlynn requested value
+        & &98258000     ; HCSR
+        & &B8004000     ; VCSR
+        & &BC400000     ; VCER
+; don't mess with the stereo image registers: sound code will set them.
+        & &FFFFFFFF     ; That's the lot
+
+;
+; Further registers to turn screen DMA on again (border all over)
+; Must have a video start register before video end register to get
+; a vertical flyback interrupt.
+;
+        & &B0494000     ; VDER > VDSR to enable screen DMA
+        & &FFFFFFFF
+        ]
+
+        [ VIDC_Type = "VIDC20"
+
+; This differs from the default RISC OS VIDCTAB in running from
+; the 24MHZ video ref clock. H register contents are increased by 50%.
+
+; Program Control Register first, to clear power-down bit
+
+        & &E0000402     ; CR: FIFO load 16 words, 1 bpp, ck/1, rclk
+        & &E0000402     ; 
+        & &B1000001     ; SCR: sound disabled (+use 24MHz clock)
+
+; Don't bother programming all 256 palette entries, we'll be here all night
+; Since we're setting up a 1 bit-per-pixel mode, just do colours 0 and 1
+
+        & &10000000     ; Palette address register = 0
+        & &00000000     ; Colour 0 = black
+        & &00000000     ; Colour 1 = black
+        & &407f7f7f     ; Border colour = grey
+        & &50000000     ; Pointer colour 1 = black
+        & &60000000     ; Pointer colour 2 = black
+        & &70000000     ; Pointer colour 3 = black
+
+; Get a stable display up so we get stable signals
+
+        & &800005F8     ; HCR  = 114 + 132 + 144 + 960 + 144 + 42
+        & &8100006A     ; HSWR = 114
+        & &820000EA     ; HBSR = 114 + 132
+        & &83000174     ; HDSR = 114 + 132 + 144
+        & &84000534     ; HDER = 114 + 132 + 144 + 960
+        & &850005CA     ; HBER = 114 + 132 + 144 + 960 + 144
+        & &860000F3     ; HCSR = HDSR
+
+        & &90000137     ; VCR  = 3 + 19 + 16 + 256 + 16 + 2
+        & &91000002     ; VSWR = 3
+        & &92000015     ; VBSR = 3 + 19
+        & &93000025     ; VDSR = 3 + 19 + 16
+        & &94000024     ; VDER = VDSR -1 to disable sceeen DMA
+        & &95000135     ; VBER = 3 + 19 + 16 + 256 + 16
+        & &96000025     ; VCSR = VDSR
+        & &97000025     ; VCER = VDSR
+
+        & &C00F1003     ; EREG = comp sync, DACs on, ereg output ext lut
+        & &D000C385     ; FSYNREG, clk = (3+1)/(5+1) * 24MHz = 16MHz
+        & &F0013000     ; DCR: bus D[31:0], Hdisc
+        & &FFFFFFFF
+
+        & &94000125     ; VDER > VDSR to enable screen DMA
+        & &FFFFFFFF
+                        ; FSIZE is one less than number of rasters in Vflyback
+        & &00000037     ; (3 + 19 + 16 + 0 + 16 + 2) - 1
+
+        ; Alternate settings for VGA monitor
+
+TestVVIDCTAB
+        & &E0000402     ; CR: FIFO load 16 words, 1 bpp, ck/1, rclk
+        & &E0000402     ; 
+        & &B1000001     ; SCR: sound disabled (+use 24MHz clock)
+
+        & &10000000     ; Palette address register = 0
+        & &00000000     ; Colour 0 = black
+        & &00000000     ; Colour 1 = black
+        & &407f7f7f     ; Border colour = grey
+        & &50000000     ; Pointer colour 1 = black
+        & &60000000     ; Pointer colour 2 = black
+        & &70000000     ; Pointer colour 3 = black
+
+        & &80000310     ; HCR  = 92 + 45 + 0 + 640 + 0 + 16
+        & &81000054     ; HSWR = 92
+        & &82000080     ; HBSR = 92 + 45
+        & &83000080     ; HDSR = 92 + 45 + 0
+        & &84000300     ; HDER = 92 + 45 + 0 + 640
+        & &85000300     ; HBER = 92 + 45 + 0 + 640 + 0
+        & &86000080     ; HCSR = HDSR
+
+        & &9000020B     ; VCR  = 2 + 32 + 0 + 480 + 0 + 11
+        & &91000001     ; VSWR = 2
+        & &92000021     ; VBSR = 2 + 32
+        & &93000021     ; VDSR = 2 + 32 + 0
+        & &94000020     ; VDER = VDSR -1 to disable sceeen DMA
+        & &95000201     ; VBER = 2 + 32 + 0 + 480 + 0
+        & &96000021     ; VCSR = VDSR
+        & &97000021     ; VCER = VDSR
+
+        & &C0051003     ; EREG = sep/inv sync, DACs on, ereg output ext lut
+        & &D000C385     ; FSYNREG, clk = (3+1)/(5+1) * 24MHz = 16MHz
+        & &F0013000     ; DCR: bus D[31:0], Hdisc
+        & &FFFFFFFF
+
+        ]
+
+        END 
+ 
+
+
diff --git a/Version b/Version
new file mode 100644
index 0000000000000000000000000000000000000000..d58dbdb8ca51445f918d58cb2f81c5fd71d0a96f
--- /dev/null
+++ b/Version
@@ -0,0 +1,11 @@
+; > Versions.Vicky
+
+        GBLA    Version
+        GBLS    VString
+        GBLS    Date
+
+Version SETA    360
+VString SETS    "3.60"
+Date    SETS    "13 Apr 1995"     ; release/srcfiler vers 4.27
+
+        END
diff --git a/hdr/EnvNumbers b/hdr/EnvNumbers
new file mode 100644
index 0000000000000000000000000000000000000000..68023aef1a6d65b06c8bdccaa0c57bae5e6896ad
--- /dev/null
+++ b/hdr/EnvNumbers
@@ -0,0 +1,61 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        SUBT    => &.Hdr.EnvNumbers
+
+OldOpt  SETA    {OPT}
+        OPT     OptNoList+OptNoP1List
+
+; ***********************************
+; ***    C h a n g e   L i s t    ***
+; ***********************************
+
+; Date       Name  Description
+; ----       ----  -----------
+; 15-Aug-88  SKS   Added numbers in comments
+; 09-Jun-94  AMcC  Added comment associating this header with
+;                  OS_ChangeEnvironment
+;
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Codes in R0 for OS_ChangeEnvironment
+; ====================================
+                        ^ 0
+MemoryLimit             # 1     ;  0  R2 ignored
+
+UndefinedHandler        # 1     ;  1   "    "
+PrefetchAbortHandler    # 1     ;  2   "    "
+DataAbortHandler        # 1     ;  3   "    "
+AddressExceptionHandler # 1     ;  4   "    "
+OtherExceptionHandler   # 1     ;  5  for FPU exception etc. expansion
+
+ErrorHandler            # 1     ;  6  R3 is error buffer pointer
+CallBackHandler         # 1     ;  7  R3 is register buffer ptr
+BreakPointHandler       # 1     ;  8  R3 is register buffer ptr
+
+EscapeHandler           # 1     ;  9
+EventHandler            # 1     ; 10
+ExitHandler             # 1     ; 11
+UnusedSWIHandler        # 1     ; 12
+
+ExceptionDumpArea       # 1     ; 13
+
+ApplicationSpaceSize    # 1     ; 14
+CAOPointer              # 1     ; 15
+
+UpCallHandler           # 1     ; 16
+
+MaxEnvNumber  #   1
+
+        OPT     OldOpt
+        END
diff --git a/hdr/ExportVals/!HowTo b/hdr/ExportVals/!HowTo
new file mode 100644
index 0000000000000000000000000000000000000000..ef144d4ef0f1182e51a02488881d7f9071762dd9
--- /dev/null
+++ b/hdr/ExportVals/!HowTo
@@ -0,0 +1,7 @@
+To add a Kernel workspace variable name to PublicWS:
+
+1) Add a LabelValue line for the variable to s.GetVals
+2) Run Mk
+3) See what value was generated for the variable in 'values'
+3) 'Export' the variable in hdr.KernelWS (see any other exported variable)
+4) Add the variable and its value to hdr.PublicWS
diff --git a/hdr/ExportVals/Makefile b/hdr/ExportVals/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..c2937e9da4e6416f762bc87a2858544f2c499067
--- /dev/null
+++ b/hdr/ExportVals/Makefile
@@ -0,0 +1,38 @@
+# Copyright 1996 Acorn Computers Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Makefile for ExportVals
+#
+
+#
+# Generic options:
+#
+MKDIR   = cdir
+AS      = aasm
+CP      = copy
+RM      = remove
+CCFLAGS = -c -depend !Depend -IC:
+ASFLAGS = -Stamp -quit
+CPFLAGS = ~cfr~v
+
+
+#
+# Generic rules:
+#
+all:
+	${AS} ${ASFLAGS} -To null: -From s.GetVals { > values }
+	settype values text
+	@echo ok
+
+# Dynamic dependencies:
diff --git a/hdr/ExportVals/Mk,fd7 b/hdr/ExportVals/Mk,fd7
new file mode 100644
index 0000000000000000000000000000000000000000..d645a7f6ff580e2eb720938cbc890d4c27a8a07a
--- /dev/null
+++ b/hdr/ExportVals/Mk,fd7
@@ -0,0 +1,16 @@
+| Copyright 1996 Acorn Computers Ltd
+|
+| Licensed under the Apache License, Version 2.0 (the "License");
+| you may not use this file except in compliance with the License.
+| You may obtain a copy of the License at
+|
+|     http://www.apache.org/licenses/LICENSE-2.0
+|
+| Unless required by applicable law or agreed to in writing, software
+| distributed under the License is distributed on an "AS IS" BASIS,
+| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+| See the License for the specific language governing permissions and
+| limitations under the License.
+|
+Dir <Obey$Dir>
+amu_machine
diff --git a/hdr/ExportVals/s/GetVals b/hdr/ExportVals/s/GetVals
new file mode 100644
index 0000000000000000000000000000000000000000..298047f5a1fc74635fd9aece810b226bd932ea09
--- /dev/null
+++ b/hdr/ExportVals/s/GetVals
@@ -0,0 +1,69 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        GET     Hdr:ListOpts
+        GET     Hdr:Macros
+        GET     Hdr:System
+        GET     Hdr:Machine.<Machine>
+        GET     ^.PublicWS
+        GET     ^.KernelWS
+
+        MACRO
+        LabelValue $LabelName
+        LCLS    String
+String  SETS    "Label " :CC: "$LabelName" :CC: " has the value &"
+String  SETS    "$String" :CC: :STR: $LabelName
+        !       0, "$String"
+        MEND
+
+        MACRO
+        RegisterLabelValue $LabelName
+        LCLS    String
+        LCLA    Register
+String  SETS    "Label " :CC: "$LabelName" :CC: " has the value &"
+String  SETS    "$String" :CC: :STR: ( :INDEX: $LabelName)
+Register SETA   :BASE: $LabelName
+        [       Register >= 10
+String  SETS    "$String" :CC: ", R" :CC: ((:STR: (Register+6)) :RIGHT: 2)
+        |
+String  SETS    "$String" :CC: ", R" :CC: ((:STR: Register) :RIGHT: 1)
+        ]
+        !       0, "$String"
+        MEND
+
+
+        LabelValue Export_BgEcfOraEor
+        LabelValue Export_FgEcfOraEor
+        LabelValue Export_BranchToSWIExit
+        LabelValue Export_DomainId
+        LabelValue Export_ESC_Status
+        LabelValue Export_IRQsema
+        LabelValue Export_LatchBSoftCopy
+        LabelValue Export_MEMC_CR_SoftCopy
+        LabelValue Export_RedirectInHandle
+        LabelValue Export_RedirectOutHandle
+        LabelValue Export_ScratchSpace
+        LabelValue ScratchSpaceSize
+        LabelValue Export_SoundDMABuffers
+        LabelValue Export_SoundDMABufferSize
+        LabelValue Export_SoundWorkSpace
+        LabelValue Export_SVCSTK
+        LabelValue Export_SvcTable
+        LabelValue Export_SysHeapStart
+        LabelValue Export_VduDriverWorkSpace
+        LabelValue VDWSSize
+        LabelValue ScreenBlankFlag
+        LabelValue ScreenBlankDPMSState
+
+        END
diff --git a/hdr/ExportVals/values b/hdr/ExportVals/values
new file mode 100644
index 0000000000000000000000000000000000000000..6376db1797946d8ef5c090fca54768d413cea635
--- /dev/null
+++ b/hdr/ExportVals/values
@@ -0,0 +1,37 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+ARM stand alone Macro Assembler Version 2.00
+Label Export_BgEcfOraEor has the value &000004C0
+Label Export_FgEcfOraEor has the value &00000480
+Label Export_BranchToSWIExit has the value &01F037FC
+Label Export_DomainId has the value &00000FF8
+Label Export_ESC_Status has the value &00000104
+Label Export_IRQsema has the value &00000108
+Label Export_LatchBSoftCopy has the value &00000105
+Label Export_MEMC_CR_SoftCopy has the value &00000114
+Label Export_RedirectInHandle has the value &00000AE1
+Label Export_RedirectOutHandle has the value &00000AE2
+Label Export_ScratchSpace has the value &00004000
+Label ScratchSpaceSize has the value &00004000
+Label Export_SoundDMABuffers has the value &01F06000
+Label Export_SoundDMABufferSize has the value &00001000
+Label Export_SoundWorkSpace has the value &01F04000
+Label Export_SVCSTK has the value &01C02000
+Label Export_SvcTable has the value &01F033FC
+Label Export_SysHeapStart has the value &01C02000
+Label Export_VduDriverWorkSpace has the value &00001000
+Label VDWSSize has the value &00003000
+Label ScreenBlankFlag has the value &0000047C
+Label ScreenBlankDPMSState has the value &0000047D
diff --git a/hdr/KernelWS b/hdr/KernelWS
new file mode 100644
index 0000000000000000000000000000000000000000..22d3d64faed773db109072436807f1a709673857
--- /dev/null
+++ b/hdr/KernelWS
@@ -0,0 +1,1424 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        SUBT    > Kernel WorkSpace
+
+OldOpt  SETA    {OPT}
+        OPT     OptNoList+OptNoP1List
+
+; ***********************************
+; ***    C h a n g e   L i s t    ***
+; ***********************************
+
+; Date       Name  Description
+; ----       ----  -----------
+; 02-Nov-87  APT   Added module SWI hash table
+; 03-Nov-87  APT   Modo-fied module SWI hash table info, removed BRKLST
+; 09-Nov-87  APT   Removed ESCCNT and ESFLG
+; 12-Nov-87  APT   Added IRQsema
+; 13-Nov-87  APT   Added DefaultIRQ1V codespace
+; 16-Nov-87  APT   PIRQ chain heads
+; 18-Nov-87  APT   Reordered EvtHan, EvtHan_ws
+; 19-Nov-87  APT   Moved IRQsema
+; 01-Dec-87  APT   Added interruptible heap manager workspace
+; 08-Dec-87  TMD   Added ECFShift, ECFYOffset
+; 14-Dec-87  TMD   Added DisplayNColour, DisplayModeFlags
+; 15-Dec-87  TMD   Added KeyAlphabet
+; 22-Dec-87  NDR   Using ScratchSpace
+; 13-Jan-88  APT   General scratchspace bash, low workspace reordering.
+;                  Removed spurious 32 bytes of osbyte wspace
+; 14-Jan-88  APT   *type buffer in scratchspace.
+;                  MOShasFIQ byte added
+; 20-Jan-88  APT   Workspace juggling for speed & space; also discarded
+;                  Level0 stuff.
+; 28-Jan-88  APT   MetroGnome moved to "public" location for ADFS
+; 02-Feb-88  APT   FIQclaim_interlock added
+; 05-Feb-88  APT   CallBack_Vector
+; 09-Feb-88  APT   RAM for SWI despatch
+; 17-Feb-88  TMD   Added VduSaveArea, VduSaveAreaPtr, DisplayModeNo
+; 26-Feb-88  APT   NoOfCamEntries manifest
+; 03-Mar-88  APT   Shrank SVC despatch
+; 03-Mar-88  APT   NoOfCamEntries manifest doubled
+; 07-Mar-88  TMD   Added DisplayScreenStart, VduOutputCurrentState,
+;                  SpriteMaskSelect, reordered mode variables
+; 07-Mar-88  APT   Made CamEntries always at &164
+; 07-Mar-88  TMD   Added GCharSizes, GCharSizeX, GCharSizeY
+; 08-Mar-88  TMD   Added GCharSpacing, GCharSpaceX, GCharSpaceY
+; 08-Mar-88  TMD   Moved GCharSizes..GCharSpaceY into first 1K of workspace
+; 15-Mar-88  TMD   Added HLineAddr
+; 18-Mar-88  TMD   Added DisplayXWindLimit, DisplayYWindLimit,
+;                   DisplayXEigFactor, DisplayYEigFactor, PointerXEigFactor
+; 18-Mar-88  APT   Setting variables scratchspace use revised.
+; 21-Mar-88  TMD   Removed CursorIndex
+; 22-Mar-88  TMD   Added TCharSizeX,TCharSizeY,TCharSpaceX,TCharSpaceY
+; 29-Mar-88  TMD   Added GcolOraEorAddr
+; 31-Mar-88  TMD   Removed WsFontPtr
+; 07-Apr-88  SKS   Added HeapSort use of ScratchSpace
+; 14-Apr-88  TMD   Added SerialFlags
+; 28-Apr-88  TMD   Added XONXOFFChar
+;  5-May-88  APT   Added MemorySpeed
+; 18-May-88  APT   Added CannotReset sema at &107, removed pre-1.20 changes.
+; 24-May-88  TMD   Added ClipBoxEnable, ClipBoxLCol..ClipBoxTRow, moved in
+;                   ScrLoaSpriteCB, ScrLoaBuffer, SrcSavCommon
+; 24-May-88  TMD   Flood fill uses ScratchSpace
+; 01-Jun-88  TMD   Added AlignSpace for ClipBoxCoords
+; 03-Jun-88  BCSKS Make Keyboard buffer into a useful part of the system
+;                  Also PrinterBufferSize
+; 09-Jun-88  DJS   Draw uses ScratchSpace
+; 09-Jun-88  BC    Gave Econet some private debungling space
+; 11-Jun-88  SKS   Align IRQ stack to make STMFD not cross two MEMC bdy's
+;                  Made info condit'l on AsmArf; someone had commented it out!
+; 15-Jun-88  SKS   Added two more instructions in SWIDespatch area
+;                  Moved SLVK definition into kernel; it's not public
+; 16-Jun-88  SKS   Added 3 more instructions in SWIDespatch area + nailed
+;                  SvcTable address for compatibility
+; 22-Jun-88  SKS   Moved MEMC_CR_SoftCopy into pubic ws
+; 19-Jul-88  APT   Added UpCall handler stuff
+; 20-Jul-88  SKS   Added above entry
+;                  Amended comment about overlaid workspace in vdu
+; 15-Aug-88  SKS   Inserted DomainId at FF8 (set by Wimp on task swap, used by
+;                  FileSwitch to tag resources)
+; 27-Sep-89  JSR   Added ColourTrans to users of scratch space
+; 24-Oct-89  TMD   Added CamEntriesForBigMachines, CamEntriesPointer
+; 26-Oct-89  TMD   Added MaxCamEntry, removed NoOfCamEntries symbol
+; 27-Oct-89  TMD   Added VIDCClockSpeed
+; 09-Nov-89  TMD   Added ResetIndirection
+; 15-Jan-91  TMD   Added ROMModuleChain
+; 04-Feb-91  DDV   Added DeviceFS as user of ScratchSpace.
+; 04-Feb-91  DDV   Added ColourTrans use of ScratchSpace to build diff tables.
+; 06-Mar-91  TMD   Added IOSystemType
+; 07-Mar-91  LVR   ADFS uses scratch space for floppy formatting
+; 07-Mar-91  TMD   Added MonitorLeadType
+; 08-Mar-91  TMD   Added PrinterBufferAddr, PrinterBufferSize
+; 11-Apr-91  TMD   Added SerialInHandle, SerialOutHandle
+; 24-Apr-91  TMD   Added UniqueMachineID
+; 09-Jun-91  RM    Added KernelMessagesBlock,ErrorSemaphore and MOSConvertBuffer
+; 26-Jul-91  JSR   Extend GeneralMOSBuffer by 4 bytes to make it a valid
+;                       length for the default error handler's error buffer
+; 19-Aug-91  JSR   Added *If to list of GeneralMOSBuffer users
+; 22-Aug-91  TMD   Reduced ErrorSemaphore to a byte, added PortableFlag
+; 25-Aug-91  DDV   Updated to indicate correct usage of scratch space by ColourTrans
+; 09-Jan-92  DDV   Added FgPattern, BgPattern and indicate use of ScratchSpace by OS_SetColour
+; 20-Jan-92  TMD   OS_SetColour no longer uses ScratchSpace
+; 17-Feb-92  ECN   Added CLibWord and RISCOSLibWord
+; 02-Apr-92  TMD   Added ScreenBlankFlag
+; 27-Jul-92  TMD   Create Victoria specific version
+; 28-Jul-92  TMD   Moved RAMDiscAddress
+; 29-Jul-92  TMD   Moved SpriteSpaceAddress
+; 30-Jul-92  TMD   Moved FontCacheAddress
+; 31-Jul-92  TMD   Moved ScreenEndAdr from source.vdudecl, and moved actual address!
+; 03-Aug-92  TMD   Added PhysRam (moved from hdr:System)
+; 24-Aug-92  TMD   Added AbortIndirection
+; 26-Aug-92  TMD   Added PreVeneerRegDump
+; 02-Sep-92  TMD   Added FirPalAddr, SecPalAddr
+; 10-Sep-92  DDV   Added new Vdu Variables for new text expansion buffer
+; 17-Sep-92  DDV   Moved NColour into the word initialised VDU workspace
+; 17-Sep-92  DDV   Two new colour words added for text foreground and background.
+; 27-Jan-93  TMD   Moved RMA to new position
+; 29-Jan-93  TMD   Put RMA back to old position (you can't branch to above 32M!)
+; 01-Feb-93  TMD   Added PhysRamTable
+; 02-Feb-93  TMD   Added VInitSoftCopy and VEndSoftCopy
+; 03-Feb-93  TMD   Added PhysRamTableEnd
+; 04-Feb-93  TMD   Added extra slot in PhysRamTable (in case soft-loaded OS splits a bank)
+; 08-Feb-93  TMD   Added VRAMWidth variable, and extra symbols for skipped bits
+; 24-Feb-93  TMD   Changed VRAMPhysAddr to VideoPhysAddr, and split off VideoSize from VRAMSize, to allow for
+;                   DRAM-only systems
+; 05-Mar-93  TMD   Added CMOSRAMCache
+; 19-Apr-93  TMD   Added DAList, AppSpaceDANode and DANode offset symbols
+; 26-Apr-93  TMD   Added FreePoolAddress, FreePoolMaxSize, FreePoolSize
+; 29-Apr-93  TMD   Changed FontCacheAddress, SpriteSpaceAddress, RAMDiscAddress and FreePoolAddress
+;                   in order to make way for L2PT, which is moving above 64M
+; 10-May-93  TMD   Added SoftCamMapSize
+; 11-May-93  TMD   Moved SoftCamMapSize into area that's not zapped in clearing all memory routine
+; 12-May-93  TMD   Added FreePoolDANode, removed FreePoolSize
+; 20-May-93  TMD   Moved AplWorkSize into AppSpaceDANode
+; 27-May-93  TMD   Added VideoBandwidth
+; 04-Jun-93  TMD   Added CurrentMonitorType
+; 09-Jun-93  TMD   Added CamMapCorruptDebugBlock
+; 07-Jul-93  TMD   Increased FreePoolMaxSize to 64M (had to reduce RAMDiscMaxSize to 48M and
+;                   move FreePoolAddress down to do this)
+; 15-Jul-93  TMD   Added KernelModeSelector
+; 26-Jul-93  SMC   Moved DefaultIRQ1V (had to accommodate IRQs for IOMD DMA)
+; 04-Aug-93  TMD   Added L2PTSize, removed FreePoolMaxSize
+; 14-Aug-93  TMD   Removed SpriteSpaceAddress, shuffled things down
+; 16-Aug-93  TMD   Removed RAMDiscAddress, shuffled things down
+; 17-Aug-93  TMD   Removed FontCacheAddress, shuffled things down
+;                  Corrected maximum size of system heap to 2M-8K.
+;                  Added node (in bottom 32K) for system heap.
+; 25-Aug-93  SMC   Added processor vector table at ProcVec_Start
+;                  Added ProcVecPreVeneers
+; 02-Sep-93  SMC   Moved RMA to &02100000 and changed application space size to 28M.
+; 03-Sep-93  TMD   Moved InitKbdWs into SkippedTables (was at start of screen originally)
+; 07-Oct-93  TMD   Put in OldMemoryMap option so I can still use it
+; 07-Oct-93  TMD   Added ScreenBlankDPMSState, HSWRSoftCopy, VSWRSoftCopy
+; 10-Dec-93  BC    Added RawMachineID
+; 13-Dec-93  BC    Removed UniqueMachineID
+; 14-Jan-94  TMD   Added CDASemaphore
+; 18-Jan-94  TMD   Added MMUControlSoftCopy
+; 15-Jun-94  AMcC  Renamed file (was VickySpace)
+;                  The following values are 'exported' to PublicWS:
+;                        Name:                     Used by:
+;                        ----------------------------------
+;                        BgEcfOraEor               SprExtend
+;                        FgEcfOraEor               SprExtend
+;                        BranchToSWIExit           TaskWindow
+;                        CannotReset               FileCore
+;                        DomainId                  FileSwitch
+;                        ESC_Status                ADFS, DeviceFS
+;                        IRQsema                   Draw, MsgTrans
+;                        LatchBSoftCopy            ADFS, Parallel
+;                        MEMC_CR_SoftCopy          ADFS
+;                        RedirectInHandle          TaskWindow
+;                        RedirectOutHandle         TaskWindow
+;                        ScratchSpace              ADFS, Colours, Draw, FileCore
+;                                                  FileSwitch, FontManager, NetFiler
+;                        SoundDMABufferSize        Sound0
+;                        SoundDMABuffers           Sound0
+;                        SoundWorkSpace            Portable, Sound1, Sound2, Voices
+;                        SVCSTK                    FileSwitch
+;                        SvcTable                  TaskWindow, Wimp
+;                        SysHeapStart              FileSwitch
+;                        VduDriverWorkSpace        SprExtend
+;
+; 31-Oct-94  AMcC/RM/WT  Added CLine_Softcopy for Morris monitor id
+; 03-Nov-94  AMcC        Export ScreenBlankFlag and ScreenBlankDPMSState
+;                        (for DPMSUtils: part of RISC OS releases 3.50 and 3.60)
+; 06-Feb-95  SMC         Increased SVC stack size to 12K.
+;
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Memory map:
+
+; Dynamic area node format
+
+                ^       0
+
+DANode_Link     #       4               ; points to next node
+DANode_Number   #       4               ; number of this area
+DANode_Base     #       4               ; base address of area (points in middle of doubly-mapped areas)
+DANode_Flags    #       4               ; various flags
+DANode_Size     #       4               ; current size of area
+DANode_MaxSize  #       4               ; maximum size of area
+DANode_Workspace #      4               ; workspace pointer when calling handlers
+DANode_Handler  #       4               ; pointer to handler routine for area
+DANode_Title    #       4               ; pointer to area title (variable length)
+DANode_NodeSize #       0
+
+; The addresses below are only temporary; eventually most of them will be allocated at run time (we hope!)
+
+ [ :DEF: OldMemoryMap
+AplWorkMaxSize      * &01000000 ; 16M
+RMAAddress          * &01800000
+RMAMaxSize          * &00400000 ; 4M
+ |
+AplWorkMaxSize      * &01C00000 ; 28M
+RMAAddress          * &02100000
+RMAMaxSize          * &00B00000 ; 11M
+ ]
+
+SVCStackSize        * 8*1024
+
+SysHeapChunkAddress * &01C00000
+SysHeapMaxSize      * &00200000-SVCStackSize
+
+CursorChunkAddress  * &01F00000 ; Fixed size 32K
+
+ScreenEndAdr        * &05000000 ; was &02000000
+ScreenMaxSize       * 480*1024
+
+; FontCacheAddress    * &06000000 ; was &01E00000       ; now dynamic
+; FontCacheMaxSize    * &01000000 ; 16M
+
+; SpriteSpaceAddress  * &08000000 ; was &01400000       ; now dynamic
+; SpriteSpaceMaxSize  * &01000000 ; 16M
+
+; RAMDiscAddress      * &07000000 ; was &01000000       ; now dynamic
+; RAMDiscMaxSize      * &03000000 ; 48M
+
+FreePoolAddress     * &06000000 ; may still go lower!
+
+PhysRam         *     &05000000
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; system variables
+
+        ^  0,R12
+
+OSBYTEFirstVar  #  0
+
+ByteVars  #  0                 ; The main osbyte variables, accessed
+                               ; via calls &A6 to &FF
+
+VarStart  #  2                 ; &A6,&A7
+ROMPtr    #  2                 ; &A8,&A9
+ROMInfo   #  2                 ; &AA,&AB
+KBTran    #  2                 ; &AC,&AD
+VDUvars   #  2                 ; &AE,&AF
+
+CFStime   #  1                 ; &B0
+InputStream #  1               ; &B1
+KeyBdSema #  1                 ; &B2
+
+ROMPollSema #  1               ; &B3
+OSHWM     #  1                 ; &B4
+
+RS423mode #  1                 ; &B5
+NoIgnore  #  1                 ; &B6
+CFSRFS    #  1                 ; &B7
+VULAcopy  #  2                 ; &B8,&B9
+
+ROMatBRK  #  1                 ; &BA
+BASICROM  #  1                 ; &BB
+
+ADCchanel #  1                 ; &BC
+ADCmaxchn #  1                 ; &BD
+ADCconv   #  1                 ; &BE
+
+RS423use     #  1              ; &BF
+RS423conflag #  1              ; &C0
+
+FlashCount # 1                 ; &C1
+SpacPeriod # 1                 ; &C2
+MarkPeriod # 1                 ; &C3
+
+KeyRepDelay # 1                ; &C4
+KeyRepRate  # 1                ; &C5
+
+ExecFileH   # 1                ; &C6
+SpoolFileH  # 1                ; &C7
+
+ESCBREAK    # 1                ; &C8 (200)
+
+KeyBdDisable # 1               ; &C9
+KeyBdStatus  # 1               ; &CA
+
+RS423HandShake # 1             ; &CB
+RS423InputSupr # 1             ; &CC
+RS423CFSFlag   # 1             ; &CD
+
+EconetOScall # 1               ; &CE
+EconetOSrdch # 1               ; &CF
+EconetOSwrch # 1               ; &D0
+
+SpeechSupr # 1                 ; &D1
+SoundSupr # 1                  ; &D2
+
+BELLchannel # 1                ; &D3
+BELLinfo    # 1                ; &D4
+BELLfreq    # 1                ; &D5
+BELLdur     # 1                ; &D6
+
+StartMessSupr # 1              ; &D7
+
+SoftKeyLen # 1                 ; &D8
+
+PageModeLineCount # 1          ; &D9
+
+VDUqueueItems # 1              ; &DA
+
+TABch # 1                      ; &DB
+ESCch # 1                      ; &DC
+
+IPbufferCh # 4                 ; &DD,&DE,&DF,&E0
+RedKeyCh   # 4                 ; &E1,&E2,&E3,&E4
+
+ESCaction  # 1                 ; &E5
+ESCeffect  # 1                 ; &E6
+
+u6522IRQ # 1                   ; &E7
+s6850IRQ # 1                   ; &E8
+s6522IRQ # 1                   ; &E9
+
+TubeFlag # 1                   ; &EA
+
+SpeechFlag # 1                 ; &EB
+
+WrchDest # 1                   ; &EC
+CurEdit  # 1                   ; &ED
+
+SoftResetVars # 0              ; Reset to here on soft reset
+
+KeyBase # 1                    ; &EE
+Shadow # 1                     ; &EF
+Country # 1                    ; &F0
+
+UserFlag # 1                   ; &F1
+
+SerULAreg # 1                  ; &F2
+
+TimerState # 1                 ; &F3
+
+SoftKeyConsist # 1             ; &F4
+
+PrinterDrivType   # 1          ; &F5
+PrinterIgnore     # 1          ; &F6
+
+HardResetVars # 0              ; Reset to here on hard reset
+
+BREAKvector # 3                ; &F7,&F8,&F9
+
+MemDriver  # 1                 ; &FA - where the VDU drivers write to
+MemDisplay # 1                 ; &FB - where we display from
+
+LangROM # 1                    ; &FC
+
+LastBREAK # 1                  ; &FD
+
+KeyOpt # 1                     ; &FE
+
+StartOptions # 1               ; &FF
+
+PowerOnResetVars # 0           ; Reset to here on power-on reset
+
+; These two can dovetail in here to use up 2 bytes before the AlignSpace!
+
+SerialInHandle # 1              ; Handle for serial input stream  (0 if not open currently)
+SerialOutHandle # 1             ; Handle for serial output stream (-----------""----------)
+
+        AlignSpace
+
+EventSemaphores # 32            ; One byte for each of 32 events
+
+TimerAlpha # 8                  ; As used by time (bottom 5 bytes)
+TimerBeta  # 8                  ; ................................
+; both aligned to word boundaries
+
+RealTime # 8                    ; 5-byte fast real-time
+
+PrinterActive # 4               ; Handle/active flag for printer (word aligned)
+
+IntervalTimer # 5               ; Up Counter synchronous with TIME.
+; Event generated when Zero is reached
+; bottom byte aligned to word boundary
+
+SecondsTime # 1 ; the soft copy (centi-)seconds of the RTC
+CentiTime   # 1 ; """"""""""""""""""""""""""""""""""""""""
+
+FlashState # 1 ; which flash colours are we using
+
+SecondsDirty # 1                ; the dirty flag for start up!
+
+MinTick # 1                     ; the minutes odd/even state
+
+DCDDSRCopy # 1                  ; copy of ACIA bits to check for change
+
+TVVertical # 1                  ; *TV first parameter
+
+TVInterlace # 1                 ; *TV second parameter
+
+CentiCounter # 1                ; Counter for VDU CTRL timing
+
+Alphabet # 1                    ; Current alphabet number
+
+Keyboard # 1                    ; Current keyboard number
+
+KeyAlphabet # 1                 ; Alphabet associated with current keyboard
+
+                GBLS    PrinterPrefix
+PrinterPrefix   SETS    "PrinterType$"
+
+PrinterTypeName #       6 + :LEN: (PrinterPrefix)
+
+        AlignSpace
+
+SerialFlags # 4                 ; New serial flags
+
+XONXOFFChar # 1                 ; Character to send before rest (0 if none)
+
+        AlignSpace
+
+OSBYTEVarSize * @-OSBYTEFirstVar
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+; End of variables' space
+
+
+; ***********************************
+; ***  Main Vdu Driver Workspace  ***
+; ***********************************
+
+        ^ 0
+
+FgEcf  # 4 * 8  ; Foreground Ecf, set by GCOL(a,0-127)
+BgEcf  # 4 * 8  ; Background Ecf, set by GCOL(a,128-255)
+GPLFMD # 4      ; Foreground action, set by GCOL(a,0-127)
+GPLBMD # 4      ; Background action, set by GCOL(a,128-255)
+GFCOL  # 4      ; Foreground colour, set by GCOL(a,0-127)
+GBCOL  # 4      ; Background colour, set by GCOL(a,128-255)
+
+GWLCol # 4      ; Graphics window left column  --
+GWBRow # 4      ; Graphics window bottom row     |
+GWRCol # 4      ; Graphics window right column   |
+GWTRow # 4      ; Graphics window top row      --
+
+qqqPad   # 3
+QQ       # 17   ;Queue - QQ+1 is on a word boundary
+QOffset  # 4    ;Value to add to VDUqueueItems to point to next queue posn.
+JVec     # 4    ;Jump vector to internal routines
+
+; Start of MODE table workspace
+
+ScreenSize # 4  ; number of bytes needed for this mode (assumed 1st in list)
+
+XWindLimit # 4  ; Maximum value of GWRCol (internal representation)
+
+; LineLength must be immediately after YWindLimit
+
+YWindLimit # 4  ; Maximum value of GWTRow (internal representation)
+
+LineLength # 4  ; Length of one pixel row in bytes
+
+NColour # 4     ; Number of colours minus 1
+
+; End of word mode variables
+
+YShftFactor # 4 ; Number of places to shift YCoord in address generation after
+                ; multiplying by 5, holds
+                ; 7,6,5 or 4 for 8,4,2 or 1 bits per pixel (640x256 mode) or
+                ; 6,5,4 or 3 for 8,4,2 or 1 bits per pixel (320x256 mode).
+
+ModeFlags # 4   ; Bit 0 => non-graphic, Bit 1 => teletext, Bit 2 => gap mode
+
+XEigFactor # 4  ; Number of places to shift XCoord in external to internal
+                ; coordinate conversion, holds
+                ; 1 for 640x256 mode
+                ; 2 for 320x256 mode
+                ; 3 for 160x256 (BBC micro mode 2)
+
+YEigFactor # 4  ; number of shifts to convert between internal/external Y
+
+Log2BPC # 4     ; Log to base 2 of BytesPerChar ie (0,1,2,3,4)
+
+Log2BPP # 4     ; Log to base 2 of BitsPerPix ie (0,1,2,3)
+
+ECFIndex # 4    ; Index into default ECF tables
+
+ScrRCol # 4     ; Maximum column number in this screen mode
+ScrBRow # 4     ; Maximum row number in this screen mode
+
+PalIndex # 4    ; Index into palette tables (0,1,2,3)
+
+; End of table-initialised workspace
+
+; Next 3 must be together in this order !
+
+XShftFactor # 4 ; Number of places to shift XCoord in address generation,
+                ; holds 2,3,4 or 5 for 8,4,2,1 bits per pixel respectivly
+GColAdr # 4     ; Address of Ecf to plot - either FgEcf or BgEcf
+
+ScreenStart # 4         ; Start address of screen (for VDU drivers)
+
+NPix # 4        ; Number of pixels per word minus 1, holds
+                ; holds 3,7,15 or 31 for 8,4,2,1 bits per pixel modes
+
+AspectRatio # 4 ; Pixel shape : 0 square, 1 horz rect, 2 vert rect
+
+BitsPerPix # 4  ; Bits per pixel (1,2,4,8)
+
+BytesPerChar # 4        ; Bytes per one line of character
+                        ; (same as BitsPerPix except in double pixel modes)
+
+CursorFudgeFactor # 4   ; Factor for horizontal cursor positioning
+
+RowMult # 4     ; Row multiplier for text manipulation
+
+RowLength # 4   ; Bytes per text row in this mode (eg 640,1280,5120)
+
+; The following (up to and including NewPtY) must be together in this order
+; (relied upon by DefaultWindows)
+
+TWLCol # 4      ; Text window left column  --
+TWBRow # 4      ; Text window bottom row     |
+TWRCol # 4      ; Text window right column   |
+TWTRow # 4      ; Text window top row      --
+
+OrgX # 4        ; Screen origin (external representation)
+OrgY # 4
+
+GCsX # 4        ; Graphics cursor (external representation)
+GCsY # 4
+
+OlderCsX # 4    ; Very old X coordinate (internal)
+OlderCsY # 4    ; Very old Y coordinate (internal)
+
+OldCsX # 4      ; Old graphics cursor (internal representation) --
+OldCsY # 4      ;                                                 |
+                ;                                                 |
+GCsIX  # 4      ; Graphics cursor (internal representation)       |
+GCsIY  # 4      ;                                                 |
+                ;                                                 |
+NewPtX # 4      ; Newest point (internal representation)          |
+NewPtY # 4      ;                                               --
+
+; End of together block
+
+TForeCol # 4    ; Text foreground colour
+TBackCol # 4    ; Text background colour
+
+CursorX # 4     ; Text cursor X position ; these 3 must be in same order as ...
+CursorY # 4     ; Text cursor Y position
+CursorAddr # 4  ; Screen address of (output) cursor
+
+InputCursorX # 4        ; Input cursor X position ; ... these 3
+InputCursorY # 4        ; Input cursor Y position
+InputCursorAddr # 4     ; Screen address of input cursor
+
+EORtoggle # 4   ; Toggle between gap and non-gap
+RowsToDo  # 4   ; in the CLS
+
+VduStatus # 4   ; Vdu2, Window, Shadow bits (others in CursorFlags)
+
+CBWS # 8        ; Clear block (VDU 23,8..) workspace
+CBStart # 2
+CBEnd # 2
+
+CursorDesiredState # 4
+CursorStartOffset # 4
+CursorEndOffset # 4
+CursorCounter # 4
+CursorSpeed # 4
+Reg10Copy # 4
+
+CursorFill # 4  ; Word to EOR cursor ; MUST be immediately before CursorNbit
+
+CursorNbit # 4  ; Pointer to cursor code for current mode
+
+DisplayStart # 4        ; Start address of screen (for display)
+DriverBankAddr # 4      ; Default start address for VDU drivers
+DisplayBankAddr # 4     ; Default start address for display
+DisplayNColour # 4      ; No. of colours -1 for displayed mode
+DisplayModeFlags # 4    ; ModeFlags for displayed mode
+DisplayModeNo # 4       ; ModeNo for displayed mode
+DisplayScreenStart # 4  ; Where VDU outputs to when outputting to screen
+
+DisplayXWindLimit # 4   ; Used for pointer programming
+DisplayYWindLimit # 4
+DisplayXEigFactor # 4
+DisplayYEigFactor # 4
+PointerXEigFactor # 4
+
+Ecf1 # 8        ; The Ecf patterns
+Ecf2 # 8
+Ecf3 # 8
+Ecf4 # 8
+
+DotLineStyle # 8        ; Dot dash line pattern
+
+ModeNo # 4      ; Current mode number
+
+TFTint # 4      ; Text foreground tint          (in bits 6,7)
+TBTint # 4      ; Text background tint
+GFTint # 4      ; Graphics foreground tint
+GBTint # 4      ; Graphics background tint
+
+TotalScreenSize # 4     ; Amount configured for screen (in bytes)
+
+MaxMode # 4             ; Maximum mode number allowed (20 for now)
+
+VinitCopy # 4   ; Copy of Vinit for VDU 23;12 or 13
+
+CursorFlags # 4 ; Silly Master cursor movement flags
+
+CursorStack # 4 ; Bit stack of nested cursor states (0 => on, 1 => off)
+                ; (bit 31 = TOS)
+
+ECFShift # 4    ; number of bits to rotate right ECF OR and EOR masks by
+ECFYOffset # 4  ; vertical offset to ECF index
+
+WsVdu5 # 0      ; Vdu 5 workspace
+WsScr # 4
+WsEcfPtr # 4
+; WsFontPtr # 4 ; not needed any more, kept in register
+EndVerti # 4
+StartMask # 4
+EndMask # 4
+FontOffset # 4
+TempPlain # 16  ; only used for MODE 10
+
+VIDCClockSpeed # 4      ; current VIDC clock speed in kHz
+
+CurrentMonitorType # 4  ; initialised from configured one
+
+KernelModeSelector # 4  ; pointer to block in system heap where
+                        ; current mode selector is copied
+
+GraphicWs # 300 ; All graphics workspace is overlaid here
+EndGraphicWs # 0
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+        AlignSpace 16
+
+GCharSizes  # 0
+GCharSizeX  # 4         ; width of VDU 5 chars in pixels
+GCharSizeY  # 4         ; height of VDU 5 chars in pixels
+
+GCharSpacing # 0
+GCharSpaceX  # 4        ; horizontal spacing between VDU 5 chars in pixels
+GCharSpaceY  # 4        ; vertical   ------------------""-----------------
+
+TCharSizes  # 0
+TCharSizeX  # 4         ; width of VDU 4 chars in pixels
+TCharSizeY  # 4         ; height of VDU 4 chars in pixels
+
+TCharSpacing # 0
+TCharSpaceX  # 4        ; horizontal spacing between VDU 4 chars in pixels
+TCharSpaceY  # 4        ; vertical   ------------------""-----------------
+
+HLineAddr    # 4        ; address of exported HLine
+GcolOraEorAddr # 4      ; address of FgEcfOraEor etc
+
+FirPalSetting # 4*28            ; First palette settings (not used on VIDC20)
+FirPalAddr * FirPalSetting      ; Address of block for first palette setting (only used on VIDC20)
+SecPalSetting # 4*28            ; Second palette settings (not used on VIDC20)
+SecPalAddr * SecPalSetting      ; Address of block for second palette setting (only used on VIDC20)
+
+TextFgColour    # 4             ; Fg/Bg colour stored as a colour number, computed on VDU 18 and re-poked!
+TextBgColour    # 4             ;
+
+; In this brave new world there is a pointer to the text expansion
+; buffer used for VDU 4 / 5 text plotting.
+
+; This now lives in the system heap.
+
+TextExpandArea # 4      ; Pointer to Text expand area (in system heap)
+TextExpandArea_Size * (8*1024)
+
+HSWRSoftCopy    #       4       ; soft copy of h.sync width register (for DPMS)
+VSWRSoftCopy    #       4       ; soft copy of v.sync width register (for DPMS)
+
+;ScreenBlankFlag # 1     ; 0 => unblanked, 1 => blanked
+
+Export_ScreenBlankFlag      #       1
+        ASSERT  Export_ScreenBlankFlag =  ScreenBlankFlag
+        ASSERT ?Export_ScreenBlankFlag = ?ScreenBlankFlag
+
+;ScreenBlankDPMSState # 1       ; 0 => just blank video
+                                ; 1 => blank to stand-by (hsync off)
+                                ; 2 => blank to suspend (vsync off)
+                                ; 3 => blank to off (H+V off)
+
+Export_ScreenBlankDPMSState      #       1
+        ASSERT  Export_ScreenBlankDPMSState =  ScreenBlankDPMSState
+        ASSERT ?Export_ScreenBlankDPMSState = ?ScreenBlankDPMSState
+
+
+ [ AssemblingArthur
+ ! 0,"64 ":CC::STR:@
+ ]
+        AlignSpace 64
+
+Export_FgEcfOraEor # 4*16      ; Interleaved zgora & zgeor
+        ASSERT  Export_FgEcfOraEor =  FgEcfOraEor
+        ASSERT ?Export_FgEcfOraEor = ?FgEcfOraEor
+
+Export_BgEcfOraEor # 4*16      ; Interleaved zgora & zgeor
+        ASSERT  Export_BgEcfOraEor =  BgEcfOraEor
+        ASSERT ?Export_BgEcfOraEor = ?BgEcfOraEor
+
+BgEcfStore  # 4*16      ; Interleaved zgora & zgeor to store background
+
+;Current state of pattern
+LineDotCnt # 4          ; Count down to restarting pattern
+LineDotPatLSW # 4       ; Current state of pattern LSWord
+LineDotPatMSW # 4       ;    "      "   "     "    MSWord
+
+DotLineLength # 4       ; Dot Pattern repeat length as given in *FX163,242,n
+
+BBCcompatibleECFs # 4   ; 0 => BBC compatible, 1 => native
+
+SpAreaStart # 4         ; Start of sprite area
+SpChooseName # 16       ; No comment says Richard
+SpChoosePtr # 4
+
+PointerHeights # 4      ; 4 x 1 byte
+PointerActiveXs # 4     ; 4 x 1 byte
+PointerActiveYs # 4     ; 4 x 1 byte
+PointerShapeNumber # 4  ; only bottom byte used
+PointerX # 4            ; co-ordinates of pointer (not always = mouse)
+PointerY # 4
+
+VIDCControlCopy # 4     ; Soft copy of VIDC control register
+VertAdjust # 4          ; offset to add to vertical VIDC registers
+
+TeletextOffset # 4      ; Offset to current teletext flash bank
+
+TeletextCount # 4       ; Number of vsyncs till next teletext flash
+
+WrchNbit # 4            ; Pointer to char code for current mode
+
+BeepBlock # 8           ; OSWORD block for VDU 7
+
+ScreenMemoryClaimed # 1 ; NZ => memory has been claimed or is unusable
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+     AlignSpace 16      ; Align workspace to 16 bytes
+
+TTXDoubleCounts # 25    ; Number of double height chars on each line
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+     AlignSpace 16      ; Align workspace to 16 bytes
+
+RAMMaskTb # 32*4        ; Copy of MaskTb for this mode (up to 32 words)
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+     AlignSpace 16      ; Align workspace to 16 bytes
+
+VduOutputCurrentState # 0 ; values of R0-R3 to return from SwitchOutputToSprite
+                        ; or Mask; next 4 must be in this order
+SpriteMaskSelect # 4    ; value of R0 to be given to SWI OS_SpriteOp to set up
+                        ; current state
+VduSpriteArea # 4       ; Pointer to sprite area containing VDU output sprite
+                        ; (0 if output is to screen)
+VduSprite # 4           ; Pointer to VDU output sprite (0 if output to screen)
+
+VduSaveAreaPtr # 4      ; Pointer to save area for VDU variables
+
+
+ [ AssemblingArthur
+ ! 0,"16,12 ":CC::STR:@
+ ]
+    AlignSpace 16, 12   ; Make ClipBoxCoords a valid immediate,
+                        ; with ClipBoxEnable immediately before it
+ClipBoxInfo # 0
+ClipBoxEnable # 4       ; 0 => clip box disabled, 1 => enabled
+
+ClipBoxCoords # 0       ; Internal coords of modified area of screen
+ClipBoxLCol # 4
+ClipBoxBRow # 4
+ClipBoxRCol # 4
+ClipBoxTRow # 4
+
+FgPattern       # 4*8   ; foreground pattern as defined by OS_SetColour
+BgPattern       # 4*8   ; background pattern as defined by OS_SetColour
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+     AlignSpace 16      ; Align workspace to 16 bytes
+
+TextExpand # 4*1024     ; Tim's massive text expansion table for whizzy WRCH
+; TextPlain is now always hard against the end of TextExpand for this mode
+
+TTXSoftFonts * TextExpand + 2*1024      ; Soft fonts in teletext mode
+
+ [ AssemblingArthur
+ ! 0,"64 ":CC::STR:@
+ ]
+     AlignSpace 64      ; Align workspace to 64 bytes
+
+; Teletext map and copy/move buffer are overlaid
+
+TTXMapSize      * 41*25*4       ; (&1004 bytes)
+LargeCommon     # TTXMapSize    ; the largest area
+TTXMap          * LargeCommon
+
+ScrLoaSpriteCB  * LargeCommon   ; (size = SpriteCBsize + MaxSpritePaletteSize)
+ScrLoaBuffer    * LargeCommon   ; (size = one pixel row)
+ScrSavCommon    * LargeCommon   ; (size = SpriteAreaCBsize + SpriteCBsize
+                                ;  + MaxSpritePaletteSize)
+
+FldQueueSize    * ScratchSpaceSize
+FldQueueStart   * ScratchSpace
+
+ [ AssemblingArthur
+ ! 0,"64 ":CC::STR:@
+ ]
+     AlignSpace 64      ; Align workspace to 64 bytes
+
+Font # &700             ; 7 pages of (soft) font
+
+SaveAreaSize * 12*1024-@
+
+VduSaveArea # SaveAreaSize      ; save area for switching output to sprites
+
+VDWSSize # 0
+
+                ASSERT  VDWSSize <= 12 * 1024
+
+; *****************************************************************************
+;                 Space in the first 32K is allocated below
+; *****************************************************************************
+; Real workspace definition
+
+; Basic kernel space - defined locations for external modules
+
+                       ^       &100
+IRQ1V                  #       4       ; &100
+
+Export_ESC_Status      #       1       ; &104
+        ASSERT  Export_ESC_Status =  ESC_Status
+        ASSERT ?Export_ESC_Status = ?ESC_Status
+
+Export_LatchBSoftCopy  #       1       ; &105
+        ASSERT  Export_LatchBSoftCopy =  LatchBSoftCopy
+        ASSERT ?Export_LatchBSoftCopy = ?LatchBSoftCopy
+
+IOCControlSoftCopy     #       1       ; &106
+
+Export_CannotReset     #       1       ; &107
+        ASSERT  Export_CannotReset =  CannotReset
+        ASSERT ?Export_CannotReset = ?CannotReset
+
+Export_IRQsema         #       4       ; &108
+        ASSERT  Export_IRQsema =  IRQsema
+        ASSERT ?Export_IRQsema = ?IRQsema
+
+MetroGnome             #       4       ; &10C
+MemorySpeed            #       4       ; &110
+
+Export_MEMC_CR_SoftCopy #      4       ; &114
+        ASSERT  Export_MEMC_CR_SoftCopy =  MEMC_CR_SoftCopy
+        ASSERT ?Export_MEMC_CR_SoftCopy = ?MEMC_CR_SoftCopy
+
+ResetIndirection        #      4       ; &118
+
+; Now all internal definitions
+
+; Up to here is initialized on reset
+
+; Next come handler variables
+
+MemLimit        #       4
+UndHan          #       4
+PAbHan          #       4
+DAbHan          #       4
+AdXHan          #       4
+
+ErrHan          #       4
+ErrBuf          #       4
+ErrHan_ws       #       4
+
+CallAd_ws       #       4     ; smart Rs ordering:
+CallAd          #       4     ; can do LDMIA of r12, pc
+CallBf          #       4
+
+BrkAd_ws        #       4
+BrkAd           #       4
+BrkBf           #       4
+
+EscHan_ws       #       4
+EscHan          #       4
+
+EvtHan_ws       #       4
+EvtHan          #       4
+
+; The next lot of workspace is in the space vacated by the small soft CAM map area
+; (256 words) which is no longer adequate, so we can reuse it
+
+JordanWS        #       0
+VInitSoftCopy   #       4       ; soft copy of VInit so we can set L bit correctly
+VEndSoftCopy    #       4       ; soft copy of VEnd  ------------""---------------
+DAList          #       4       ; Pointer to first node on dynamic area list
+
+                AlignSpace 16   ; skipped bit must start on 16-byte boundary
+
+SkippedTables   #       0
+PhysRamTable    #       0       ; 6 pairs of words (physaddr, size) indicating
+                                ; RAM present in machine (NB normally you would need at most 5
+                                ; on IOMD machines, but the extra one is if a soft-loaded ROM image
+                                ; causes a bank to split
+VideoPhysAddr   #       4       ; Address of video RAM (in the case of DRAM-only machines,
+VideoSize       #       4       ; this is actually a chunk out of DRAM)
+DRAMPhysAddrA   #       4       ; Next the DRAM - note that any banks with no memory
+DRAMSizeA       #       4       ; in them will be omitted from this table, so that
+DRAMPhysAddrB   #       4       ; eg DRAMPhysAddrA corresponds to the first bank with
+DRAMSizeB       #       4       ; DRAM in it, not necessarily bank 0
+DRAMPhysAddrC   #       4       ; If not all the slots are occupied, then
+DRAMSizeC       #       4       ; the remaining entries in this table have size fields
+DRAMPhysAddrD   #       4       ; of zero (and probably addresses of zero too)
+DRAMSizeD       #       4
+DRAMPhysAddrE   #       4
+DRAMSizeE       #       4
+ [ MorrisSupport
+DRAMPhysAddrExtra #     4 * 12  ; The DRAM used with MORRIS can fragment into four
+DRAMSizeExtra     #     4 * 12  ; blocks so allocate 3 extra word pairs per bank
+ ]
+PhysRamTableEnd #       0
+ ! 0, "VideoPhysAddr held at  ":CC::STR:(VideoPhysAddr)
+
+VRAMSize        #       4       ; Amount of VRAM (in bytes) (may be more than 2M)
+VRAMWidth       #       4       ; 0 => no VRAM, 1 => 32-bits wide, 2 => 64-bits wide
+VideoBandwidth  #       4       ; video bandwidth in bytes/sec
+L2PTSize        #       4       ; Amount of memory (in bytes) used for static L2PT
+                                ; - this consists of fixed size first bit, plus variable size
+                                ; bit for the free pool L2, which follows directly after it
+SoftCamMapSize  #       4       ; Amount of memory (in bytes) used for soft CAM map
+                                ; (whole number of pages)
+InitKbdWs       #       16      ; Workspace for reset keyboard IRQ code (was 12 changed for Morris)
+
+CLine_Softcopy  #        1      ; Added for Morris - Monitor id
+
+                AlignSpace 16   ; skipped bit must end on 16-byte boundary
+SkippedTablesEnd #      0
+
+CMOSRAMCache    #       240             ; Cache for CMOS RAM
+AppSpaceDANode  #       DANode_NodeSize ; Dummy area node for application space (not on list)
+FreePoolDANode  #       DANode_NodeSize ; Area node for free pool
+SysHeapDANode   #       DANode_NodeSize ; Area node for system heap
+CDASemaphore    #       4               ; Semaphore for OS_ChangeDynamicArea - non-zero => routine threaded
+MMUControlSoftCopy #    4               ; Soft copy of ARM600/700 control register
+
+AplWorkSize * AppSpaceDANode + DANode_Size
+
+ProcVec_Start           #       0       ; Start of processor vector table
+ProcVec_Branch0         #       4       ; Branch through zero
+ProcVec_UndInst         #       4       ; Undefined instruction vector
+ProcVec_SWI             #       4       ; SWI vector
+ProcVec_PrefAb          #       4       ; Prefetch abort vector
+ProcVec_DataAb          #       4       ; Data abort vector
+ProcVec_AddrEx          #       4       ; Address exception vector (not useful on ARM600/700)
+ProcVec_IRQ             #       4       ; IRQ vector
+ProcVec_End             #       0
+
+ProcVecPreVeneersSize   *       4*4     ; Space for preveneers for loading handler addresses from 0 page.
+ProcVecPreVeneers       #       ProcVecPreVeneersSize
+
+        ASSERT  @ <= &500               ; a convenient address to remember
+                #       (&500-@)
+
+CamMapCorruptDebugBlock #       &40     ; somewhere to dump registers in case of emergency
+
+        ASSERT  @ <= JordanWS+256*4
+                #       (JordanWS+256*4-@)  ; pad out to original size
+
+CamEntriesPointer #     4       ; points to where CAM soft copy is
+                                ; (CamEntries for machines up to 8MBytes,
+                                ; CamEntriesForBigMachines for larger machines)
+
+MaxCamEntry     #       4       ; maximum index into the cam map, ie
+                                ; 511 for 16MByte machines, 383 for 12MBytes
+                                ; 255 for 8MBytes, otherwise 127
+
+RAMLIMIT        #       4
+
+                #       4       ; dummy slot where AplWorkSize used to be
+
+HiServ_ws       #       4
+HiServ          #       4
+SExitA          #       4
+SExitA_ws       #       4
+UpCallHan_ws    #       4
+UpCallHan       #       4
+
+ROMModuleChain  #       4               ; pointer to head of ROM module chain
+
+; now a section that it's handy to have in simply loadable places
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+             AlignSpace 16
+
+KeyWorkSpaceSize   *  &200
+KeyWorkSpace       #  KeyWorkSpaceSize
+
+; The following were reordered on 26-Jul-91. Old ordering was:
+; GeneralMOSBuffer
+; ModuleSWI_HashTab
+; Module_List
+; Curr_Active_Object
+; VecPtrTab
+; ExceptionDump
+
+ModuleSHT_Entries  *  16
+ModuleSWI_HashTab  #  4*ModuleSHT_Entries
+
+Module_List        #  4
+Curr_Active_Object #  4
+
+; Vector Claim & Release tables etc
+
+VecPtrTab          #  NVECTORS * 4
+
+ExceptionDump      #  4
+
+; GeneralMOSBuffer: re-use with caution!
+; Here's just some of the users:
+; user                  use(s)
+; default error handler error buffer (must be 246+4 bytes big)
+; *If                   expression to be evaluated to control the *If
+;                       Command line to be submited on the expression
+;                         evaluating to non-zero (the THEN clause).
+GeneralMOSBuffer   #  256+4
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+            AlignSpace  16 ; Ensures we can MOV rn, #OsbyteVars if <=&1000
+
+OsbyteVars      #       OSBYTEVarSize
+ ASSERT OsbyteVars < &10000 ; Must keep in first 64K so address can be read by
+                            ; (and stored in) OS_Bytes &A6,&A7. SKS
+
+; These must be in first 4K
+NBuffers        *       10
+BuffInPtrs      #       4 * NBuffers
+BuffOutPtrs     #       4 * NBuffers
+
+VariableList    #       4
+
+; Oscli stuff
+OscliCBtopUID   #       4
+OscliCBbotUID   #       4
+OscliCBcurrend  #       4
+
+ReturnCode      #       4
+RCLimit         #       4
+
+SpriteSize      #       4       ; saved on startup for Sprite code and RAMFS
+RAMDiscSize     #       4
+FontCacheSize   #       4       ; and font manager
+
+TickNodeChain   #       4
+
+; Workspace
+
+EnvTime            #    5
+
+Export_RedirectInHandle   #    1
+        ASSERT  Export_RedirectInHandle =  RedirectInHandle
+        ASSERT ?Export_RedirectInHandle = ?RedirectInHandle
+
+Export_RedirectOutHandle  #    1
+        ASSERT  Export_RedirectOutHandle =  RedirectOutHandle
+        ASSERT ?Export_RedirectOutHandle = ?RedirectOutHandle
+
+MOShasFIQ          #    1
+FIQclaim_interlock #    1
+CallBack_Flag      #    1
+IRQ_CallBack_Flag * CallBack_Flag
+IOSystemType    #       1       ; 0 => old I/O subsystem, 1 => IOEB+82C710 system, 2..255 => ?
+MonitorLeadType #       1       ; some function of the monitor lead inputs, as yet undetermined
+
+                  AlignSpace
+
+EnvString         #     256
+
+
+DUMPER            #     16 * 4
+
+; more system workspace
+Page_Size         #  4
+PIRQ_Chain        #  4
+PFIQasIRQ_Chain   #  4
+
+; !!!! Free space (752 bytes) left by old IRQ despatch (new IRQ despatch
+; !!!! moved as it required more space).
+OldIRQ1Vspace       # 752
+
+ [ med_00001_debug
+ ! 0,"med-00001 queue start, queue size, cda amount at &" :CC: :STR:(OldIRQ1Vspace)
+med_00001_debug_start * OldIRQ1Vspace
+med_00001_debug_size * OldIRQ1Vspace+4
+med_00001_debug_cda * OldIRQ1Vspace+8
+ ]
+
+
+CallBack_Vector   #  4
+
+; interruptible heap manager workspace
+
+HeapSavedReg_R0     # 4
+HeapSavedReg_R1     # 4
+HeapSavedReg_R2     # 4
+HeapSavedReg_R3     # 4
+HeapSavedReg_R4     # 4
+HeapSavedReg_R13    # 4
+HeapReturnedReg_R0  # 4
+HeapReturnedReg_R1  # 4
+HeapReturnedReg_R2  # 4
+HeapReturnedReg_R3  # 4
+HeapReturnedReg_R4  # 4
+HeapReturnedReg_R13 # 4
+HeapReturnedReg_PC  # 4                 ; also acts as interlock
+
+PrinterBufferAddr   #  4                ; holds address of printer buffer
+PrinterBufferSize   #  4                ; size of printer buffer - not to be confused with PrintBuffSize
+                                        ; which is the (constant) default size for the MOS's smallish buffer
+RawMachineID        #  8                ; 64 bits for unique machine ID
+KernelMessagesBlock #  20               ; 5 Words for messagetrans message block.
+ErrorSemaphore      #  1                ; Error semaphore to avoid looping on error translation.
+ [ StorkPowerSave
+PortableFlags       #  1                ;
+PowerSave * &80
+ |
+PortableFlag        #  1                ; Non-zero => got an error from Portable_Speed, so don't try it again
+ ]
+
+        AlignSpace
+
+MOSConvertBuffer    #  12               ; Enough romm for 8 hex digits.
+AbortIndirection    #  4                ; Pointer to list of addresses and trap routines
+PreVeneerRegDump    #  17*4             ; room for r0-r15, spsr
+
+ [ AssemblingArthur
+ ! 0, "low space free ":CC::STR:(&FE8-@)
+ ]
+ ASSERT @ < &FE8
+
+; Words for old tools of assorted varieties
+; Don't move the following as their positions are assumed by other modules
+
+                        ^       &FE8
+CLibCounter             #       1               ; Counter for Shared C Library tmpnam function
+
+        AlignSpace
+
+; ECN 17-Feb-92
+; Added RISCOSLibWord and CLibWord. The ROM RISCOSLib and CLib must continue
+; to work even when they are killed since ROM apps are hard linked to the
+; ROM libraries. They cannot use the private word since the block pointed
+; to by this will be freed.
+RISCOSLibWord           #       4
+CLibWord                #       4
+FPEAnchor               #       4
+
+Export_DomainId                #       4       ; SKS added for domain identification
+        ASSERT  Export_DomainId =  DomainId
+        ASSERT ?Export_DomainId = ?DomainId
+
+Modula2_Private         #       4       ; MICK has FFC and uses it it in USR mode
+
+Export_VduDriverWorkSpace      #       VDWSSize
+        ASSERT  Export_VduDriverWorkSpace =  VduDriverWorkSpace
+        ASSERT ?Export_VduDriverWorkSpace = ?VduDriverWorkSpace
+
+ ASSERT (VduDriverWorkSpace :AND: 63) = 0 ; For Tim (VDU5)
+
+
+ [ AssemblingArthur
+ ! 0, "high space free ":CC::STR:(&4000-@)
+ ]
+
+                        ^       &4000
+ScratchSpaceSize        *       &4000
+
+Export_ScratchSpace            #       ScratchSpaceSize
+        ASSERT  Export_ScratchSpace =  ScratchSpace
+        ASSERT ?Export_ScratchSpace = ?ScratchSpace
+
+ ASSERT @ <= &8000 ; Start of apl
+
+; *****************************************************************************
+; Users of ScratchSpace declare yourself here:
+
+; NRaine: Filling a polygon uses ScratchSpace to flatten the path
+
+; DSeal: Draw module uses ScratchSpace on fill operations (this supercedes
+;   NRaine's declaration above).
+
+; SKS: HeapSort with (r1 & 0x80000000) & ~(r1 & 0x20000000) & (r5 <= 16K)
+;      uses ScratchSpace as a temp slot for data shuffling after sorting
+
+; TMD: Flood fill uses ScratchSpace for the flood queue.
+
+; Tidying the RMA uses ScratchSpace while all modules are dead
+
+; GSTRANS workspace: GSINIT puts state into the workspacem and GSREAD uses it.
+; DO NOT do any operations between GSINIT/GSREAD SWIS.
+; SWIs called: OSWord in time var code
+;              BinaryToDecimal, ReadUnsigned
+
+; LVR: ADFS uses scratch space to format floppies on 1772 based machines
+
+; DDV: ColourTrans uses scratch space to build palette tables when in
+;      ColourTrans_SelecTable/RetrunColourNumber and also whilst generating
+;      stipple pattterns.
+
+GSVarWSpace             *       ScratchSpace
+
+                        ^       0
+GSNameBuff              #       &100
+GS_Stack                #       &200
+GS_StackPtr_Lim         *       &200 / 4        ; Number of words in stack.
+GS_StackPtr             #       4
+
+                        ^      @ + ScratchSpace
+
+; Pointers for SubstituteArgs: no external calls.
+; Ensure these don't overlap FileSwitch's buffers below!
+
+MacExStartPtrs          #       44
+MacExEndPtrs            #       44
+
+; OS_CLI has a buffer for alias expansion: ReadVarVal and SubstituteArgs
+;    are called while the buffer is held. Also used for module prefixes:
+;    Module called twice in this case.
+
+AliasExpansionBuffer    #       100
+
+; *list/*type need an argument expansion buffer: ReadArgs called with it.
+
+ArgumentBuffer           *       AliasExpansionBuffer
+
+; EvaluateExpression space. Calls ReadUnsigned, BinaryToDecimal and ReadVarVal.
+
+ExprWSpace              *       @
+
+                        ^       0, R12
+ExprBuff                #       &100
+exprBracDif             #       2       ; keep exprSTRACC aligned
+tos_op                  #       2       ; 1 byte for use as STRACC-1
+ExprSVCstack            #       4
+exprSTRACC              *       @ - ExprBuff + ExprWSpace
+
+ExprStackLimit          *       exprSTRACC + &100
+ExprStackStart          *       ScratchSpace + ScratchSpaceSize
+
+
+; Tutu needs some for argument substitution + expansion for run/load types
+; Only OS call during xform is XOS_SubstituteArgs and XOS_Heap(Claim,SysHeap)
+
+                        ^       0 ; Offset from ScratchSpace
+rav_substituted         #       256
+rav_arglist             #       256
+
+TopOfPageZero           #       0
+
+                        ^       &8000 ; The actual top of Page Zero
+EconetDebugSpace        |#|     &20 * 4 ; Thirty two words (&7F80)
+
+                ASSERT  @ > TopOfPageZero ; Make sure we don't clash
+
+; *****************************************************************************
+; ***            Cursor, Sound DMA, SWI, and OSCLI workspace.               ***
+; ***        Sits in the 32K above 31M, ie. &01F000000..&01F07FFF           ***
+; ***        Has the physical address &02078000, ie. 32M + 512K - 32K       ***
+; *****************************************************************************
+
+TopOfDMAPhysRAM         *       &80000            ; OFFSET in physram
+TopOfDMAWorkSpace       *       CursorChunkAddress + 32*1024
+OffsetLogicalToPhysical *       TopOfDMAPhysRAM - TopOfDMAWorkSpace
+
+                        ^       TopOfDMAWorkSpace ; Note we will be going down
+
+; Sound
+
+SoundWorkSpaceSize      *       &1000
+
+Export_SoundDMABufferSize  *    &1000
+        ASSERT  Export_SoundDMABufferSize =  SoundDMABufferSize
+
+SoundEvtSize            *       &1000
+
+Export_SoundDMABuffers         |#|     SoundDMABufferSize * 2
+        ASSERT  Export_SoundDMABuffers =  SoundDMABuffers
+        ASSERT ?Export_SoundDMABuffers = ?SoundDMABuffers
+
+Export_SoundWorkSpace          |#|     SoundWorkSpaceSize + SoundEvtSize
+        ASSERT  Export_SoundWorkSpace =  SoundWorkSpace
+        ASSERT ?Export_SoundWorkSpace = ?SoundWorkSpace
+
+; Cursor
+
+CursorDataSize          *       &800
+CursorData              |#|     CursorDataSize
+CursorSoundRAM          *       CursorData
+CursorSoundPhysRAM      *       CursorSoundRAM + OffsetLogicalToPhysical
+
+; SWI despatcher
+
+Export_BranchToSWIExit         |#|     4
+        ASSERT  Export_BranchToSWIExit =  BranchToSWIExit
+        ASSERT ?Export_BranchToSWIExit = ?BranchToSWIExit
+
+Export_SvcTable                |#|     &400
+        ASSERT  Export_SvcTable =  SvcTable
+        ASSERT ?Export_SvcTable = ?SvcTable
+
+ ASSERT SvcTable = &01F033FC ; Required for SVC table pokers, 1.20 compatible
+SWIDespatch_Size        *       29*4
+SWIDespatch             |#|     SWIDespatch_Size
+
+
+; Buffers
+
+KeyBuffSize             *       &100
+RS423InBuffSize         *       &100
+RS423OutBuffSize        *       &C0
+PrintBuffSize           *       &400
+Sound0BuffSize          *       4
+Sound1BuffSize          *       4
+Sound2BuffSize          *       4
+Sound3BuffSize          *       4
+SpeechBuffSize          *       4
+MouseBuffSize           *       &40
+KeyBuff                 |#|     KeyBuffSize
+RS423InBuff             |#|     RS423InBuffSize
+RS423OutBuff            |#|     RS423OutBuffSize
+PrintBuff               |#|     PrintBuffSize
+Sound0Buff              |#|     Sound0BuffSize
+Sound1Buff              |#|     Sound1BuffSize
+Sound2Buff              |#|     Sound2BuffSize
+Sound3Buff              |#|     Sound3BuffSize
+SpeechBuff              |#|     SpeechBuffSize
+MouseBuff               |#|     MouseBuffSize
+
+; Oscli buffering
+
+OscliBuffSize           *       &100
+OscliNoBuffs            *       16
+OscliCircBuffLimit      |#|     0
+OscliCircBuffStart      |#|     OscliBuffSize * OscliNoBuffs
+RedirectBuff            |#|     OscliBuffSize
+
+; Default IRQ despatch moved here as a result of IOMD having an extra
+; 6 interrupts for I/O and sound DMA (this is really IOMD specific, not
+; ARM600/700 specific but for the moment it is assumed that they are
+; used on the same machines).
+ [ MorrisSupport
+DefIRQ1Vspace           *       12*4+12*23+2*256+64 + 5*12+3*4+32 ;Morris adds 5 more
+ |
+DefIRQ1Vspace           *       12*4+12*23+2*256+64     ; for size checking in MOS
+ ]
+DefaultIRQ1V            |#|     DefIRQ1Vspace
+
+ [ AssemblingArthur
+ ! 0, "Aligning IRQ stack from ":CC::STR:@
+ ]
+ [ @-7*4 :AND: 15 <> 0
+                        |#|     (@-7*4):AND:15
+ ]
+IRQSTK                  #       0       ; Overflow will give abort
+ [ AssemblingArthur
+ ! 0, "IRQ stack size ":CC::STR:(IRQSTK-CursorChunkAddress)
+ ]
+
+ ASSERT @ > ( CursorChunkAddress + &1000 ) ; Check minimum stack
+
+; *****************************************************************************
+;                        High system workspace
+; *****************************************************************************
+
+                ^       SysHeapChunkAddress
+
+                       #       SVCStackSize    ; svcstk size. Overflow will give abort
+Export_SVCSTK          #       0
+        ASSERT  Export_SVCSTK =  SVCSTK
+        ASSERT ?Export_SVCSTK = ?SVCSTK
+
+Export_SysHeapStart    #       0
+        ASSERT  Export_SysHeapStart =  SysHeapStart
+        ASSERT ?Export_SysHeapStart = ?SysHeapStart
+
+        OPT     OldOpt
+        END
diff --git a/hdr/KeyWS b/hdr/KeyWS
new file mode 100644
index 0000000000000000000000000000000000000000..392f82db98b14cd10d927401e7ec25411cc680d2
--- /dev/null
+++ b/hdr/KeyWS
@@ -0,0 +1,156 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Hdr.KeyWS
+
+; ***********************************
+; ***    C h a n g e   L i s t    ***
+; ***********************************
+
+; Date       Name  Description
+; ----       ----  -----------
+; 06-Feb-91  TMD   Added LastLED
+; 09-Mar-92  TMD   Added JustGotKbID
+; 24-Feb-93  SMC   Reorganised for new keyboard/mouse interfaces
+; 19-Jul-93  JSR   Changed conditional from A1Keyboard to Keyboard_Type = "A1A500"
+
+; Keyboard variables
+
+          GBLS  keyprefix
+keyprefix SETS  "Key$"
+
+        ^       0, R11
+
+CurrKey         #       1               ; current key in two key rollover
+OldKey          #       1               ; old key in two key rollover
+KbId            #       1
+LastKbId        #       1
+AutoRepeatCount #       1
+Debouncing      #       1               ; NZ => do delay next, Z => do repeat
+MouseButtons    #       1               ; bit0=R, bit1=C, bit2=L
+PendingAltType  #       1               ; 1 => A, 2 => SA, 3 => CA, 4 => CSA
+ModulesOK       #       1               ; bit0=1 => modules are initialised
+                                        ; bit1=1 => we have offered service
+LastLED         #       1               ; last request for LED change, so we don't send repeated ones
+MouseType       #       1               ; current pointer device type
+
+ [ Keyboard_Type = "A1A500"
+JustGotKbId     #       1
+RequestMouse    #       1
+RequestLED      #       1
+RequestSPD      #       1
+RequestKbId     #       1
+SPDRec          #       1               ; number to be received
+ResetState      #       1               ; next thing to go in reset handshake
+KeyRow          #       1               ; half received key up or down
+Reply           #       1               ; next reply to be sent (&FF if nowt)
+KbIdHalf        #       1
+MouseCount      #       1               ; 0 => X next, 1 => Y next
+MouseDelta      #       2               ; delta X,Y
+ ]
+
+                # 3 :AND: (- :INDEX: @)
+
+InkeyCounter    #       4
+MouseX          #       4
+MouseY          #       4
+SoftKeyPtr      #       4
+MouseXMult      #       4
+MouseYMult      #       4
+KeyVec          #       4
+
+ [ Keyboard_Type = "A1A500"
+SPDinput        #       4
+SPDoutput       #       4
+  [ AssemblePointerV
+MouseXCount     #       4
+MouseYCount     #       4
+  ]
+ ]
+
+MouseBounds     #       16
+MouseBoundLCol  *       MouseBounds+0
+MouseBoundBRow  *       MouseBounds+4
+MouseBoundRCol  *       MouseBounds+8
+MouseBoundTRow  *       MouseBounds+12
+
+KeysDown        #       20              ; bitmap of all down keys
+
+SoftKeyName     #       3 + :LEN:(keyprefix)    ; up to 2 digits + terminator
+
+SoftKeyExpand   #       255             ; current key expansion
+
+        ASSERT  (:INDEX: @) < KeyWorkSpaceSize
+UserKeyWorkSpaceSize *  KeyWorkSpaceSize-(:INDEX: @)
+UserKeyWorkSpace #      UserKeyWorkSpaceSize
+
+
+; PMF -> VDU communication stuff put in here because both VDU and PMF
+; 'GET' this file
+
+        GBLA    ExtEntries
+ExtEntries SETA 0
+
+        MACRO
+        AddExtEntry $EntryName
+Index_$EntryName * ExtEntries
+        [ AssemblingArthur
+Value_$ExtEntries * $EntryName
+        |
+        [ DoingVdu
+Value_$ExtEntries * $EntryName
+        ]
+        ]
+ExtEntries SETA ExtEntries +1
+        MEND
+
+        MACRO
+$Table  OutputExternals
+$Table
+        LCLA    count
+count   SETA    0
+        WHILE   count < ExtEntries
+        &       Value_$count - $Table -4
+count   SETA    count + 1
+        WEND
+        MEND
+
+        MACRO
+        ByteToNosbod $EntryName
+        [ AssemblingArthur
+        VDWS    WsPtr
+        BL      $EntryName
+        |
+        MOV     R0, #Index_$EntryName
+        BL      ByteToNosbod
+        ]
+        MEND
+
+        AddExtEntry     DoReadPOSVPOSI
+        AddExtEntry     DoReadPOSVPOSO
+        AddExtEntry     DoOSBYTE87
+        AddExtEntry     DoResetFont
+        AddExtEntry     DoReadFont
+        AddExtEntry     DoReadVDUStatus
+        AddExtEntry     DoReadVDUVariable
+        AddExtEntry     DoReadPalette
+        AddExtEntry     DoSetPalette
+        AddExtEntry     DoPointerStuff
+        AddExtEntry     DoSetScreenStart
+        AddExtEntry     DoSetDriverBank
+        AddExtEntry     DoSetDisplayBank
+        AddExtEntry     DoOsbyte163_242
+        AddExtEntry     DoOsWord13
+
+        END
diff --git a/hdr/ModHand b/hdr/ModHand
new file mode 100644
index 0000000000000000000000000000000000000000..8a8bb576a0c7287c40922143754a7027619a7796
--- /dev/null
+++ b/hdr/ModHand
@@ -0,0 +1,107 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+ SUBT Module handler reason codes etc. => &.Hdr.ModHand
+
+OldOpt SETA {OPT}
+ OPT OptNoList+OptNoP1List
+
+; ***********************************
+; ***    C h a n g e   L i s t    ***
+; ***********************************
+
+; Date       Name  Description
+; ----       ----  -----------
+; 27-Nov-86  BC    Added Module_Ticker
+; 15-Jan-87  SKS   Added SWI base addresses
+; 21-Jan-87  APT   New Module_LoadAddr added.
+; 26-Jan-87  BC    Removed Module_Ticker
+;  5-Feb-87  APT   Added flag manifests
+;  9-Feb-87  APT   Added more reason codes
+; 17-Feb-87  BC    Added Module name server entries
+; 25-Feb-87  APT   Help-is-code flag added
+;  2-Apr-87  APT   ExtendBlock reason code added
+; 23-Apr-87  APT   Help-is-code flag moved into high byte
+; 17-Jun-87  APT   NewIncarnation, AddPoduleModule reason codes
+; 24-Jun-87  APT   RenameIncarnation r.c.
+; 15-Jul-87  APT   MakePreferred
+; 29-Jul-87  APT   LookupName
+; 17-Aug-87  APT   EnumerateROM_Modules
+; 23-Jan-91  TMD   EnumerateROM_ModulesWithInfo
+
+ModHandReason_Run                          * 0
+ModHandReason_Load                         * 1
+ModHandReason_Enter                        * 2
+ModHandReason_ReInit                       * 3
+ModHandReason_Delete                       * 4
+ModHandReason_RMADesc                      * 5
+ModHandReason_Claim                        * 6
+ModHandReason_Free                         * 7
+ModHandReason_Tidy                         * 8
+ModHandReason_Clear                        * 9
+ModHandReason_AddArea                      * 10
+ModHandReason_CopyArea                     * 11
+ModHandReason_GetNames                     * 12
+ModHandReason_ExtendBlock                  * 13
+ModHandReason_NewIncarnation               * 14
+ModHandReason_RenameIncarnation            * 15
+ModHandReason_MakePreferred                * 16
+ModHandReason_AddPoduleModule              * 17
+ModHandReason_LookupName                   * 18
+ModHandReason_EnumerateROM_Modules         * 19
+ModHandReason_EnumerateROM_ModulesWithInfo * 20
+
+; Real module offsets
+
+                 ^ 0
+Module_Start     # 4
+Module_Init      # 4
+Module_Die       # 4
+Module_Service   # 4
+Module_Title     # 4
+Module_HelpStr   # 4
+Module_HC_Table  # 4       ; help and command table.
+
+; optional SWI handler offsets
+Module_SWIChunk  # 4
+Module_SWIEntry  # 4
+Module_NameTable # 4
+Module_NameCode  # 4
+
+; optional Message filename offset
+Module_MsgFile   # 4
+
+; Magic number for RM load addr
+
+Module_LoadAddr * &FFFFFA00  ; magic number from Stu/Bruce standard :
+                             ; the two zeroes are ignored.
+                             ; &FFFFFE00 on Proto-Arfur < .032
+
+Module_SWIChunkSize * 2_1000000
+
+Module_SWISystemBase      * 1 :SHL: 18
+Module_SWIApplicationBase * 2 :SHL: 18
+Module_SWIUserBase        * 3 :SHL: 18
+
+
+; flags for the information word :
+
+FS_Command_Flag     * 1 :SHL: 31
+Status_Keyword_Flag * 1 :SHL: 30
+Help_Is_Code_Flag   * 1 :SHL: 29
+International_Help  * 1 :SHL: 28
+
+        OPT OldOpt
+
+        END
diff --git a/hdr/Old/Arthur/PublicWS b/hdr/Old/Arthur/PublicWS
new file mode 100644
index 0000000000000000000000000000000000000000..c2aa021ed026e44644776409e86de08f6f88d03c
--- /dev/null
+++ b/hdr/Old/Arthur/PublicWS
@@ -0,0 +1,90 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        SUBT    > Public Work Space
+
+OldOpt  SETA    {OPT}
+        OPT     OptNoList+OptNoP1List
+
+; ***********************************
+; ***    C h a n g e   L i s t    ***
+; ***********************************
+
+; Date       Name  Description
+; ----       ----  -----------
+; 15-Jun-94  AMcC  Created - holds values 'exported' from Kernel WorkSpace
+;                   Corresponds to Values set in Space200
+;
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Memory map values:
+
+                    ^ &00000104
+ESC_Status          #         1
+
+                    ^ &00000105
+LatchBSoftCopy      #         1
+
+                    ^ &00000107
+CannotReset         #         1
+
+                    ^ &00000108
+IRQsema             #         4
+
+                    ^ &00000114
+MEMC_CR_SoftCopy    #         4
+
+                    ^ &00000480
+FgEcfOraEor         #      4*16           ; Interleaved zgora & zgeor (from Vdu Driver Workspace)
+
+                    ^ &000004C0
+BgEcfOraEor         #      4*16           ; Interleaved zgora & zgeor (from Vdu Driver Workspace)
+
+                    ^ &00000AD1           ; RedirectInHandle
+RedirectInHandle    #         1
+
+                    ^ &00000AD2           ; RedirectOutHandle
+RedirectOutHandle   #         1
+
+                    ^ &00000FF8
+DomainId            #         4           ; domain identification
+
+                    ^ &00001000
+VduDriverWorkSpace  #     &3000
+
+                    ^ &00004000
+ScratchSpace        #     &4000
+
+                    ^ &01C02000
+SVCSTK              #         0
+
+                    ^ &01C02000
+SysHeapStart        #         0
+
+                    ^ &01F033FC
+SvcTable            #      &400
+
+                    ^ &01F037FC
+BranchToSWIExit     #         4           ; from SWI despatcher
+
+                    ^ &01F04000
+SoundWorkSpace      #     &2000
+
+SoundDMABufferSize  *     &1000
+
+                    ^ &01F06000
+SoundDMABuffers     #     SoundDMABufferSize * 2
+
+        OPT     OldOpt
+        END
diff --git a/hdr/Old/Arthur/Space200 b/hdr/Old/Arthur/Space200
new file mode 100644
index 0000000000000000000000000000000000000000..8a126a1a06343d8acfe4fa809bcebebd74b16fad
--- /dev/null
+++ b/hdr/Old/Arthur/Space200
@@ -0,0 +1,1017 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        SUBT    > &.Hdr.NewSpace
+
+OldOpt  SETA    {OPT}
+        OPT     OptNoList+OptNoP1List
+
+; ***********************************
+; ***    C h a n g e   L i s t    ***
+; ***********************************
+
+; Date       Name  Description
+; ----       ----  -----------
+; 02-Nov-87  APT   Added module SWI hash table
+; 03-Nov-87  APT   Modo-fied module SWI hash table info, removed BRKLST
+; 09-Nov-87  APT   Removed ESCCNT and ESFLG
+; 12-Nov-87  APT   Added IRQsema
+; 13-Nov-87  APT   Added DefaultIRQ1V codespace
+; 16-Nov-87  APT   PIRQ chain heads
+; 18-Nov-87  APT   Reordered EvtHan, EvtHan_ws
+; 19-Nov-87  APT   Moved IRQsema
+; 01-Dec-87  APT   Added interruptible heap manager workspace
+; 08-Dec-87  TMD   Added ECFShift, ECFYOffset
+; 14-Dec-87  TMD   Added DisplayNColour, DisplayModeFlags
+; 15-Dec-87  TMD   Added KeyAlphabet
+; 22-Dec-87  NDR   Using ScratchSpace
+; 13-Jan-88  APT   General scratchspace bash, low workspace reordering.
+;                  Removed spurious 32 bytes of osbyte wspace
+; 14-Jan-88  APT   *type buffer in scratchspace.
+;                  MOShasFIQ byte added
+; 20-Jan-88  APT   Workspace juggling for speed & space; also discarded
+;                  Level0 stuff.
+; 28-Jan-88  APT   MetroGnome moved to "public" location for ADFS
+; 02-Feb-88  APT   FIQclaim_interlock added
+; 05-Feb-88  APT   CallBack_Vector
+; 09-Feb-88  APT   RAM for SWI despatch
+; 17-Feb-88  TMD   Added VduSaveArea, VduSaveAreaPtr, DisplayModeNo
+; 26-Feb-88  APT   NoOfCamEntries manifest
+; 03-Mar-88  APT   Shrank SVC despatch
+; 03-Mar-88  APT   NoOfCamEntries manifest doubled
+; 07-Mar-88  TMD   Added DisplayScreenStart, VduOutputCurrentState,
+;                  SpriteMaskSelect, reordered mode variables
+; 07-Mar-88  APT   Made CamEntries always at &164
+; 07-Mar-88  TMD   Added GCharSizes, GCharSizeX, GCharSizeY
+; 08-Mar-88  TMD   Added GCharSpacing, GCharSpaceX, GCharSpaceY
+; 08-Mar-88  TMD   Moved GCharSizes..GCharSpaceY into first 1K of workspace
+; 15-Mar-88  TMD   Added HLineAddr
+; 18-Mar-88  TMD   Added DisplayXWindLimit, DisplayYWindLimit,
+;                   DisplayXEigFactor, DisplayYEigFactor, PointerXEigFactor
+; 18-Mar-88  APT   Setting variables scratchspace use revised.
+; 21-Mar-88  TMD   Removed CursorIndex
+; 22-Mar-88  TMD   Added TCharSizeX,TCharSizeY,TCharSpaceX,TCharSpaceY
+; 29-Mar-88  TMD   Added GcolOraEorAddr
+; 31-Mar-88  TMD   Removed WsFontPtr
+; 07-Apr-88  SKS   Added HeapSort use of ScratchSpace
+; 14-Apr-88  TMD   Added SerialFlags
+; 28-Apr-88  TMD   Added XONXOFFChar
+;  5-May-88  APT   Added MemorySpeed
+; 18-May-88  APT   Added CannotReset sema at &107, removed pre-1.20 changes.
+; 24-May-88  TMD   Added ClipBoxEnable, ClipBoxLCol..ClipBoxTRow, moved in
+;                   ScrLoaSpriteCB, ScrLoaBuffer, SrcSavCommon
+; 24-May-88  TMD   Flood fill uses ScratchSpace
+; 01-Jun-88  TMD   Added AlignSpace for ClipBoxCoords
+; 03-Jun-88  BCSKS Make Keyboard buffer into a useful part of the system
+;                  Also PrinterBufferSize
+; 09-Jun-88  DJS   Draw uses ScratchSpace
+; 09-Jun-88  BC    Gave Econet some private debungling space
+; 11-Jun-88  SKS   Align IRQ stack to make STMFD not cross two MEMC bdy's
+;                  Made info condit'l on AsmArf; someone had commented it out!
+; 15-Jun-88  SKS   Added two more instructions in SWIDespatch area
+;                  Moved SLVK definition into kernel; it's not public
+; 16-Jun-88  SKS   Added 3 more instructions in SWIDespatch area + nailed
+;                  SvcTable address for compatibility
+; 22-Jun-88  SKS   Moved MEMC_CR_SoftCopy into pubic ws
+; 19-Jul-88  APT   Added UpCall handler stuff
+; 20-Jul-88  SKS   Added above entry
+;                  Amended comment about overlaid workspace in vdu
+; 15-Aug-88  SKS   Inserted DomainId at FF8 (set by Wimp on task swap, used by
+;                  FileSwitch to tag resources)
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Memory map:
+
+CursorChunkAddress  * 31*1024*1024
+FontCacheAddress    * 30*1024*1024
+SysHeapChunkAddress * 28*1024*1024
+RMAAddress          * 24*1024*1024
+SpriteSpaceAddress  * 20*1024*1024
+RAMDiscAddress      * 16*1024*1024
+
+; **********+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; system variables
+
+        ^  0,R12
+
+OSBYTEFirstVar  #  0
+
+ByteVars  #  0                 ; The main osbyte variables, accessed
+                               ; via calls &A6 to &FF
+
+VarStart  #  2                 ; &A6,&A7
+ROMPtr    #  2                 ; &A8,&A9
+ROMInfo   #  2                 ; &AA,&AB
+KBTran    #  2                 ; &AC,&AD
+VDUvars   #  2                 ; &AE,&AF
+
+CFStime   #  1                 ; &B0
+InputStream #  1               ; &B1
+KeyBdSema #  1                 ; &B2
+
+ROMPollSema #  1               ; &B3
+OSHWM     #  1                 ; &B4
+
+RS423mode #  1                 ; &B5
+NoIgnore  #  1                 ; &B6
+CFSRFS    #  1                 ; &B7
+VULAcopy  #  2                 ; &B8,&B9
+
+ROMatBRK  #  1                 ; &BA
+BASICROM  #  1                 ; &BB
+
+ADCchanel #  1                 ; &BC
+ADCmaxchn #  1                 ; &BD
+ADCconv   #  1                 ; &BE
+
+RS423use     #  1              ; &BF
+RS423conflag #  1              ; &C0
+
+FlashCount # 1                 ; &C1
+SpacPeriod # 1                 ; &C2
+MarkPeriod # 1                 ; &C3
+
+KeyRepDelay # 1                ; &C4
+KeyRepRate  # 1                ; &C5
+
+ExecFileH   # 1                ; &C6
+SpoolFileH  # 1                ; &C7
+
+ESCBREAK    # 1                ; &C8 (200)
+
+KeyBdDisable # 1               ; &C9
+KeyBdStatus  # 1               ; &CA
+
+RS423HandShake # 1             ; &CB
+RS423InputSupr # 1             ; &CC
+RS423CFSFlag   # 1             ; &CD
+
+EconetOScall # 1               ; &CE
+EconetOSrdch # 1               ; &CF
+EconetOSwrch # 1               ; &D0
+
+SpeechSupr # 1                 ; &D1
+SoundSupr # 1                  ; &D2
+
+BELLchannel # 1                ; &D3
+BELLinfo    # 1                ; &D4
+BELLfreq    # 1                ; &D5
+BELLdur     # 1                ; &D6
+
+StartMessSupr # 1              ; &D7
+
+SoftKeyLen # 1                 ; &D8
+
+PageModeLineCount # 1          ; &D9
+
+VDUqueueItems # 1              ; &DA
+
+TABch # 1                      ; &DB
+ESCch # 1                      ; &DC
+
+IPbufferCh # 4                 ; &DD,&DE,&DF,&E0
+RedKeyCh   # 4                 ; &E1,&E2,&E3,&E4
+
+ESCaction  # 1                 ; &E5
+ESCeffect  # 1                 ; &E6
+
+u6522IRQ # 1                   ; &E7
+s6850IRQ # 1                   ; &E8
+s6522IRQ # 1                   ; &E9
+
+TubeFlag # 1                   ; &EA
+
+SpeechFlag # 1                 ; &EB
+
+WrchDest # 1                   ; &EC
+CurEdit  # 1                   ; &ED
+
+SoftResetVars # 0              ; Reset to here on soft reset
+
+KeyBase # 1                    ; &EE
+Shadow # 1                     ; &EF
+Country # 1                    ; &F0
+
+UserFlag # 1                   ; &F1
+
+SerULAreg # 1                  ; &F2
+
+TimerState # 1                 ; &F3
+
+SoftKeyConsist # 1             ; &F4
+
+PrinterDrivType   # 1          ; &F5
+PrinterIgnore     # 1          ; &F6
+
+HardResetVars # 0              ; Reset to here on hard reset
+
+BREAKvector # 3                ; &F7,&F8,&F9
+
+MemDriver  # 1                 ; &FA - where the VDU drivers write to
+MemDisplay # 1                 ; &FB - where we display from
+
+LangROM # 1                    ; &FC
+
+LastBREAK # 1                  ; &FD
+
+KeyOpt # 1                     ; &FE
+
+StartOptions # 1               ; &FF
+
+PowerOnResetVars # 0           ; Reset to here on power-on reset
+
+        AlignSpace
+
+EventSemaphores # 32            ; One byte for each of 32 events
+
+TimerAlpha # 8                  ; As used by time (bottom 5 bytes)
+TimerBeta  # 8                  ; ................................
+; both aligned to word boundaries
+
+RealTime # 8                    ; 5-byte fast real-time
+
+PrinterActive # 4               ; Handle/active flag for printer (word aligned)
+
+IntervalTimer # 5               ; Up Counter synchronous with TIME.
+; Event generated when Zero is reached
+; bottom byte aligned to word boundary
+
+SecondsTime # 1 ; the soft copy (centi-)seconds of the RTC
+CentiTime   # 1 ; """"""""""""""""""""""""""""""""""""""""
+
+FlashState # 1 ; which flash colours are we using
+
+SecondsDirty # 1                ; the dirty flag for start up!
+
+MinTick # 1                     ; the minutes odd/even state
+
+DCDDSRCopy # 1                  ; copy of ACIA bits to check for change
+
+TVVertical # 1                  ; *TV first parameter
+
+TVInterlace # 1                 ; *TV second parameter
+
+CentiCounter # 1                ; Counter for VDU CTRL timing
+
+Alphabet # 1                    ; Current alphabet number
+
+Keyboard # 1                    ; Current keyboard number
+
+KeyAlphabet # 1                 ; Alphabet associated with current keyboard
+
+                GBLS    PrinterPrefix
+PrinterPrefix   SETS    "PrinterType$"
+
+PrinterTypeName #       6 + :LEN: (PrinterPrefix)
+
+        AlignSpace
+
+SerialFlags # 4                 ; New serial flags
+
+XONXOFFChar # 1                 ; Character to send before rest (0 if none)
+
+        AlignSpace
+
+OSBYTEVarSize * @-OSBYTEFirstVar
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+; End of variables' space
+
+
+; ***********************************
+; ***  Main Vdu Driver Workspace  ***
+; ***********************************
+
+        ^ 0
+
+FgEcf  # 4 * 8  ; Foreground Ecf, set by GCOL(a,0-127)
+BgEcf  # 4 * 8  ; Background Ecf, set by GCOL(a,128-255)
+GPLFMD # 4      ; Foreground action, set by GCOL(a,0-127)
+GPLBMD # 4      ; Background action, set by GCOL(a,128-255)
+GFCOL  # 4      ; Foreground colour, set by GCOL(a,0-127)
+GBCOL  # 4      ; Background colour, set by GCOL(a,128-255)
+
+GWLCol # 4      ; Graphics window left column  --
+GWBRow # 4      ; Graphics window bottom row     |
+GWRCol # 4      ; Graphics window right column   |
+GWTRow # 4      ; Graphics window top row      --
+
+qqqPad   # 3
+QQ       # 17   ;Queue - QQ+1 is on a word boundary
+QOffset  # 4    ;Value to add to VDUqueueItems to point to next queue posn.
+JVec     # 4    ;Jump vector to internal routines
+
+; Start of MODE table workspace
+
+ScreenSize # 4  ; number of bytes needed for this mode (assumed 1st in list)
+
+XWindLimit # 4  ; Maximum value of GWRCol (internal representation)
+
+; LineLength must be immediately after YWindLimit
+
+YWindLimit # 4  ; Maximum value of GWTRow (internal representation)
+
+LineLength # 4  ; Length of one pixel row in bytes
+
+; End of word mode variables
+
+YShftFactor # 4 ; Number of places to shift YCoord in address generation after
+                ; multiplying by 5, holds
+                ; 7,6,5 or 4 for 8,4,2 or 1 bits per pixel (640x256 mode) or
+                ; 6,5,4 or 3 for 8,4,2 or 1 bits per pixel (320x256 mode).
+
+ModeFlags # 4   ; Bit 0 => non-graphic, Bit 1 => teletext, Bit 2 => gap mode
+
+XEigFactor # 4  ; Number of places to shift XCoord in external to internal
+                ; coordinate conversion, holds
+                ; 1 for 640x256 mode
+                ; 2 for 320x256 mode
+                ; 3 for 160x256 (BBC micro mode 2)
+
+YEigFactor # 4  ; number of shifts to convert between internal/external Y
+
+NColour # 4     ; Number of colours minus 1
+
+Log2BPC # 4     ; Log to base 2 of BytesPerChar ie (0,1,2,3,4)
+
+Log2BPP # 4     ; Log to base 2 of BitsPerPix ie (0,1,2,3)
+
+ECFIndex # 4    ; Index into default ECF tables
+
+ScrRCol # 4     ; Maximum column number in this screen mode
+ScrBRow # 4     ; Maximum row number in this screen mode
+
+PalIndex # 4    ; Index into palette tables (0,1,2,3)
+
+; End of table-initialised workspace
+
+; Next 3 must be together in this order !
+
+XShftFactor # 4 ; Number of places to shift XCoord in address generation,
+                ; holds 2,3,4 or 5 for 8,4,2,1 bits per pixel respectivly
+GColAdr # 4     ; Address of Ecf to plot - either FgEcf or BgEcf
+
+ScreenStart # 4         ; Start address of screen (for VDU drivers)
+
+NPix # 4        ; Number of pixels per word minus 1, holds
+                ; holds 3,7,15 or 31 for 8,4,2,1 bits per pixel modes
+
+AspectRatio # 4 ; Pixel shape : 0 square, 1 horz rect, 2 vert rect
+
+BitsPerPix # 4  ; Bits per pixel (1,2,4,8)
+
+BytesPerChar # 4        ; Bytes per one line of character
+                        ; (same as BitsPerPix except in double pixel modes)
+
+CursorFudgeFactor # 4   ; Factor for horizontal cursor positioning
+
+RowMult # 4     ; Row multiplier for text manipulation
+
+RowLength # 4   ; Bytes per text row in this mode (eg 640,1280,5120)
+
+; The following (up to and including NewPtY) must be together in this order
+; (relied upon by DefaultWindows)
+
+TWLCol # 4      ; Text window left column  --
+TWBRow # 4      ; Text window bottom row     |
+TWRCol # 4      ; Text window right column   |
+TWTRow # 4      ; Text window top row      --
+
+OrgX # 4        ; Screen origin (external representation)
+OrgY # 4
+
+GCsX # 4        ; Graphics cursor (external representation)
+GCsY # 4
+
+OlderCsX # 4    ; Very old X coordinate (internal)
+OlderCsY # 4    ; Very old Y coordinate (internal)
+
+OldCsX # 4      ; Old graphics cursor (internal representation) --
+OldCsY # 4      ;                                                 |
+                ;                                                 |
+GCsIX  # 4      ; Graphics cursor (internal representation)       |
+GCsIY  # 4      ;                                                 |
+                ;                                                 |
+NewPtX # 4      ; Newest point (internal representation)          |
+NewPtY # 4      ;                                               --
+
+; End of together block
+
+TForeCol # 4    ; Text foreground colour
+TBackCol # 4    ; Text background colour
+
+CursorX # 4     ; Text cursor X position ; these 3 must be in same order as ...
+CursorY # 4     ; Text cursor Y position
+CursorAddr # 4  ; Screen address of (output) cursor
+
+InputCursorX # 4        ; Input cursor X position ; ... these 3
+InputCursorY # 4        ; Input cursor Y position
+InputCursorAddr # 4     ; Screen address of input cursor
+
+EORtoggle # 4   ; Toggle between gap and non-gap
+RowsToDo  # 4   ; in the CLS
+
+VduStatus # 4   ; Vdu2, Window, Shadow bits (others in CursorFlags)
+
+CBWS # 8        ; Clear block (VDU 23,8..) workspace
+CBStart # 2
+CBEnd # 2
+
+CursorDesiredState # 4
+CursorStartOffset # 4
+CursorEndOffset # 4
+CursorCounter # 4
+CursorSpeed # 4
+Reg10Copy # 4
+
+CursorFill # 4  ; Word to EOR cursor ; MUST be immediately before CursorNbit
+
+CursorNbit # 4  ; Pointer to cursor code for current mode
+
+DisplayStart # 4        ; Start address of screen (for display)
+DriverBankAddr # 4      ; Default start address for VDU drivers
+DisplayBankAddr # 4     ; Default start address for display
+DisplayNColour # 4      ; No. of colours -1 for displayed mode
+DisplayModeFlags # 4    ; ModeFlags for displayed mode
+DisplayModeNo # 4       ; ModeNo for displayed mode
+DisplayScreenStart # 4  ; Where VDU outputs to when outputting to screen
+
+DisplayXWindLimit # 4   ; Used for pointer programming
+DisplayYWindLimit # 4
+DisplayXEigFactor # 4
+DisplayYEigFactor # 4
+PointerXEigFactor # 4
+
+Ecf1 # 8        ; The Ecf patterns
+Ecf2 # 8
+Ecf3 # 8
+Ecf4 # 8
+
+DotLineStyle # 8        ; Dot dash line pattern
+
+ModeNo # 4      ; Current mode number
+
+TFTint # 4      ; Text foreground tint          (in bits 6,7)
+TBTint # 4      ; Text background tint
+GFTint # 4      ; Graphics foreground tint
+GBTint # 4      ; Graphics background tint
+
+TotalScreenSize # 4     ; Amount configured for screen (in bytes)
+
+MaxMode # 4             ; Maximum mode number allowed (20 for now)
+
+VinitCopy # 4   ; Copy of Vinit for VDU 23;12 or 13
+
+CursorFlags # 4 ; Silly Master cursor movement flags
+
+CursorStack # 4 ; Bit stack of nested cursor states (0 => on, 1 => off)
+                ; (bit 31 = TOS)
+
+ECFShift # 4    ; number of bits to rotate right ECF OR and EOR masks by
+ECFYOffset # 4  ; vertical offset to ECF index
+
+WsVdu5 # 0      ; Vdu 5 workspace
+WsScr # 4
+WsEcfPtr # 4
+; WsFontPtr # 4 ; not needed any more, kept in register
+EndVerti # 4
+StartMask # 4
+EndMask # 4
+FontOffset # 4
+TempPlain # 16  ; only used for MODE 10
+
+GraphicWs # 300 ; All graphics workspace is overlaid here
+EndGraphicWs # 0
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+        AlignSpace 16
+
+GCharSizes  # 0
+GCharSizeX  # 4         ; width of VDU 5 chars in pixels
+GCharSizeY  # 4         ; height of VDU 5 chars in pixels
+
+GCharSpacing # 0
+GCharSpaceX  # 4        ; horizontal spacing between VDU 5 chars in pixels
+GCharSpaceY  # 4        ; vertical   ------------------""-----------------
+
+TCharSizes  # 0
+TCharSizeX  # 4         ; width of VDU 4 chars in pixels
+TCharSizeY  # 4         ; height of VDU 4 chars in pixels
+
+TCharSpacing # 0
+TCharSpaceX  # 4        ; horizontal spacing between VDU 4 chars in pixels
+TCharSpaceY  # 4        ; vertical   ------------------""-----------------
+
+HLineAddr    # 4        ; address of exported HLine
+GcolOraEorAddr # 4      ; address of FgEcfOraEor etc
+
+FirPalSetting # 4*28    ; First palette settings
+SecPalSetting # 4*28    ; Second palette settings
+
+ [ AssemblingArthur
+ ! 0,"64 ":CC::STR:@
+ ]
+        AlignSpace 64
+
+FgEcfOraEor # 4*16      ; Interleaved zgora & zgeor
+BgEcfOraEor # 4*16      ; Interleaved zgora & zgeor
+BgEcfStore  # 4*16      ; Interleaved zgora & zgeor to store background
+
+;Current state of pattern
+LineDotCnt # 4          ; Count down to restarting pattern
+LineDotPatLSW # 4       ; Current state of pattern LSWord
+LineDotPatMSW # 4       ;    "      "   "     "    MSWord
+
+DotLineLength # 4       ; Dot Pattern repeat length as given in *FX163,242,n
+
+BBCcompatibleECFs # 4   ; 0 => BBC compatible, 1 => native
+
+SpAreaStart # 4         ; Start of sprite area
+SpChooseName # 16       ; No comment says Richard
+SpChoosePtr # 4
+
+PointerHeights # 4      ; 4 x 1 byte
+PointerActiveXs # 4     ; 4 x 1 byte
+PointerActiveYs # 4     ; 4 x 1 byte
+PointerShapeNumber # 4  ; only bottom byte used
+PointerX # 4            ; co-ordinates of pointer (not always = mouse)
+PointerY # 4
+
+VIDCControlCopy # 4     ; Soft copy of VIDC control register
+VertAdjust # 4          ; offset to add to vertical VIDC registers
+
+TeletextOffset # 4      ; Offset to current teletext flash bank
+
+TeletextCount # 4       ; Number of vsyncs till next teletext flash
+
+WrchNbit # 4            ; Pointer to char code for current mode
+
+BeepBlock # 8           ; OSWORD block for VDU 7
+
+ScreenMemoryClaimed # 1 ; NZ => memory has been claimed or is unusable
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+     AlignSpace 16      ; Align workspace to 16 bytes
+
+TTXDoubleCounts # 25    ; Number of double height chars on each line
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+     AlignSpace 16      ; Align workspace to 16 bytes
+
+RAMMaskTb # 32*4        ; Copy of MaskTb for this mode (up to 32 words)
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+     AlignSpace 16      ; Align workspace to 16 bytes
+
+VduOutputCurrentState # 0 ; values of R0-R3 to return from SwitchOutputToSprite
+                        ; or Mask; next 4 must be in this order
+SpriteMaskSelect # 4    ; value of R0 to be given to SWI OS_SpriteOp to set up
+                        ; current state
+VduSpriteArea # 4       ; Pointer to sprite area containing VDU output sprite
+                        ; (0 if output is to screen)
+VduSprite # 4           ; Pointer to VDU output sprite (0 if output to screen)
+
+VduSaveAreaPtr # 4      ; Pointer to save area for VDU variables
+
+
+ [ AssemblingArthur
+ ! 0,"16,12 ":CC::STR:@
+ ]
+    AlignSpace 16, 12   ; Make ClipBoxCoords a valid immediate,
+                        ; with ClipBoxEnable immediately before it
+ClipBoxInfo # 0
+ClipBoxEnable # 4       ; 0 => clip box disabled, 1 => enabled
+
+ClipBoxCoords # 0       ; Internal coords of modified area of screen
+ClipBoxLCol # 4
+ClipBoxBRow # 4
+ClipBoxRCol # 4
+ClipBoxTRow # 4
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+     AlignSpace 16      ; Align workspace to 16 bytes
+
+TextExpand # 4*1024     ; Tim's massive text expansion table for whizzy WRCH
+; TextPlain is now always hard against the end of TextExpand for this mode
+
+TTXSoftFonts * TextExpand + 2*1024      ; Soft fonts in teletext mode
+
+ [ AssemblingArthur
+ ! 0,"64 ":CC::STR:@
+ ]
+     AlignSpace 64      ; Align workspace to 64 bytes
+
+; Teletext map and copy/move buffer are overlaid
+
+TTXMapSize      * 41*25*4       ; (&1004 bytes)
+LargeCommon     # TTXMapSize    ; the largest area
+TTXMap          * LargeCommon
+
+ScrLoaSpriteCB  * LargeCommon   ; (size = SpriteCBsize + MaxSpritePaletteSize)
+ScrLoaBuffer    * LargeCommon   ; (size = one pixel row)
+ScrSavCommon    * LargeCommon   ; (size = SpriteAreaCBsize + SpriteCBsize
+                                ;  + MaxSpritePaletteSize)
+
+FldQueueSize    * ScratchSpaceSize
+FldQueueStart   * ScratchSpace
+
+ [ AssemblingArthur
+ ! 0,"64 ":CC::STR:@
+ ]
+     AlignSpace 64      ; Align workspace to 64 bytes
+
+Font # &700             ; 7 pages of (soft) font
+
+SaveAreaSize * 12*1024-@
+
+VduSaveArea # SaveAreaSize      ; save area for switching output to sprites
+
+VDWSSize # 0
+
+                ASSERT  VDWSSize <= 12 * 1024
+
+; *****************************************************************************
+;                 Space in the first 32K is allocated below
+; *****************************************************************************
+; Real workspace definition
+
+; Basic kernel space - defined locations for external modules
+
+                ^       &100
+IRQ1V           #       4       ; &100
+
+ESC_Status      #       1       ; &104
+LatchBSoftCopy  #       1       ; &105
+IOCControlSoftCopy #    1       ; &106
+CannotReset     #       1       ; &107
+
+IRQsema         #       4       ; &108
+MetroGnome      #       4       ; &10C
+MemorySpeed     #       4       ; &110
+
+MEMC_CR_SoftCopy #      4       ; &114
+
+
+; Now all internal definitions
+
+; Up to here is initialized on reset
+
+; Next come handler variables
+
+MemLimit        #       4
+UndHan          #       4
+PAbHan          #       4
+DAbHan          #       4
+AdXHan          #       4
+
+ErrHan          #       4
+ErrBuf          #       4
+ErrHan_ws       #       4
+
+CallAd_ws       #       4     ; smart Rs ordering:
+CallAd          #       4     ; can do LDMIA of r12, pc
+CallBf          #       4
+
+BrkAd_ws        #       4
+BrkAd           #       4
+BrkBf           #       4
+
+EscHan_ws       #       4
+EscHan          #       4
+
+EvtHan_ws       #       4
+EvtHan          #       4
+
+RAMLIMIT        #       4
+
+NoOfCamEntries  *       255                    ; we have entries 0-n.o.c.e.
+CamEntries      #       4 * (NoOfCamEntries+1) ; keep CAM entries + PPL in here
+ ASSERT CamEntries = &164 ; Fixed for PCEmulator
+
+AplWorkSize     #       4
+
+HiServ_ws       #       4
+HiServ          #       4
+SExitA          #       4
+SExitA_ws       #       4
+UpCallHan_ws    #       4
+UpCallHan       #       4
+
+
+; now a section that it's handy to have in simply loadable places
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+             AlignSpace 16
+
+KeyWorkSpaceSize   *  &200
+KeyWorkSpace       #  KeyWorkSpaceSize
+
+; general MOS buffer: re-use with caution!
+
+GeneralMOSBuffer   #  256
+
+ModuleSHT_Entries  *  16
+ModuleSWI_HashTab  #  4*ModuleSHT_Entries
+
+Module_List        #  4
+Curr_Active_Object #  4
+
+; Vector Claim & Release tables etc
+
+VecPtrTab          #  NVECTORS * 4
+
+ExceptionDump      #  4
+
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+            AlignSpace  16 ; Ensures we can MOV rn, #OsbyteVars if <=&1000
+
+OsbyteVars      #       OSBYTEVarSize
+ ASSERT OsbyteVars < &10000 ; Must keep in first 64K so address can be read by
+                            ; (and stored in) OS_Bytes &A6,&A7. SKS
+
+; These must be in first 4K
+NBuffers        *       10
+BuffInPtrs      #       4 * NBuffers
+BuffOutPtrs     #       4 * NBuffers
+
+VariableList    #       4
+
+; Oscli stuff
+OscliCBtopUID   #       4
+OscliCBbotUID   #       4
+OscliCBcurrend  #       4
+
+ReturnCode      #       4
+RCLimit         #       4
+
+SpriteSize      #       4       ; saved on startup for Sprite code and RAMFS
+RAMDiscSize     #       4
+FontCacheSize   #       4       ; and font manager
+
+TickNodeChain   #       4
+
+; Workspace
+
+EnvTime            #    5
+RedirectInHandle   #    1
+RedirectOutHandle  #    1
+MOShasFIQ          #    1
+FIQclaim_interlock #    1
+CallBack_Flag      #    1
+IRQ_CallBack_Flag * CallBack_Flag
+
+                  AlignSpace
+
+EnvString         #     256
+
+
+DUMPER            #     16 * 4
+
+; more system workspace
+Page_Size         #  4
+PIRQ_Chain        #  4
+PFIQasIRQ_Chain   #  4
+
+; IRQ despatch
+DefIRQ1Vspace       *     9*4+12*17+2*256   ; for size checking in MOS
+DefaultIRQ1V        #     DefIRQ1Vspace     ; assembly
+
+
+CallBack_Vector   #  4
+
+; interruptible heap manager workspace
+
+HeapSavedReg_R0     # 4
+HeapSavedReg_R1     # 4
+HeapSavedReg_R2     # 4
+HeapSavedReg_R3     # 4
+HeapSavedReg_R4     # 4
+HeapSavedReg_R13    # 4
+HeapReturnedReg_R0  # 4
+HeapReturnedReg_R1  # 4
+HeapReturnedReg_R2  # 4
+HeapReturnedReg_R3  # 4
+HeapReturnedReg_R4  # 4
+HeapReturnedReg_R13 # 4
+HeapReturnedReg_PC  # 4               ; also acts as interlock
+
+ [ AssemblingArthur
+ ! 0, "low space free ":CC::STR:(&FF4-@)
+ ]
+ ASSERT @ < &FF4
+
+; Words for old tools of assorted varieties
+                        ^       &FF4
+FPEAnchor               #       4
+DomainId                #       4       ; SKS added for domain identification
+Modula2_Private         #       4       ; MICK has FFC and uses it it in USR mode
+
+VduDriverWorkSpace      #       VDWSSize
+ ASSERT (VduDriverWorkSpace :AND: 63) = 0 ; For Tim (VDU5)
+
+
+ [ AssemblingArthur
+ ! 0, "high space free ":CC::STR:(&4000-@)
+ ]
+
+                        ^       &4000
+ScratchSpaceSize        *       &4000
+ScratchSpace            #       ScratchSpaceSize
+
+ ASSERT @ <= &8000 ; Start of apl
+
+; *****************************************************************************
+; Users of ScratchSpace declare yourself here:
+
+; NRaine: Filling a polygon uses ScratchSpace to flatten the path
+
+; DSeal: Draw module uses ScratchSpace on fill operations (this supercedes
+;   NRaine's declaration above).
+
+; SKS: HeapSort with (r1 & 0x80000000) & ~(r1 & 0x20000000) & (r5 <= 16K)
+;      uses ScratchSpace as a temp slot for data shuffling after sorting
+
+; TMD: Flood fill uses ScratchSpace for the flood queue.
+
+; Tidying the RMA uses ScratchSpace while all modules are dead
+
+; GSTRANS workspace: GSINIT puts state into the workspacem and GSREAD uses it.
+; DO NOT do any operations between GSINIT/GSREAD SWIS.
+; SWIs called: OSWord in time var code
+;              BinaryToDecimal, ReadUnsigned
+
+GSVarWSpace             *       ScratchSpace
+
+                        ^       0
+GSNameBuff              #       &100
+GS_Stack                #       &200
+GS_StackPtr_Lim         *       &200 / 4        ; Number of words in stack.
+GS_StackPtr             #       4
+
+                        ^      @ + ScratchSpace
+
+; Pointers for SubstituteArgs: no external calls.
+; Ensure these don't overlap FileSwitch's buffers below!
+
+MacExStartPtrs          #       44
+MacExEndPtrs            #       44
+
+; OS_CLI has a buffer for alias expansion: ReadVarVal and SubstituteArgs
+;    are called while the buffer is held. Also used for module prefixes:
+;    Module called twice in this case.
+
+AliasExpansionBuffer    #       100
+
+; *list/*type need an argument expansion buffer: ReadArgs called with it.
+
+ArgumentBuffer           *       AliasExpansionBuffer
+
+; EvaluateExpression space. Calls ReadUnsigned, BinaryToDecimal and ReadVarVal.
+
+ExprWSpace              *       @
+
+                        ^       0, R12
+ExprBuff                #       &100
+exprBracDif             #       2       ; keep exprSTRACC aligned
+tos_op                  #       2       ; 1 byte for use as STRACC-1
+ExprSVCstack            #       4
+exprSTRACC              *       @ - ExprBuff + ExprWSpace
+
+ExprStackLimit          *       exprSTRACC + &100
+ExprStackStart          *       ScratchSpace + ScratchSpaceSize
+
+
+; Tutu needs some for argument substitution + expansion for run/load types
+; Only OS call during xform is XOS_SubstituteArgs and XOS_Heap(Claim,SysHeap)
+
+                        ^       0 ; Offset from ScratchSpace
+rav_substituted         #       256
+rav_arglist             #       256
+
+TopOfPageZero           #       0
+
+                        ^       &8000 ; The actual top of Page Zero
+EconetDebugSpace        |#|     &20 * 4 ; Thirty two words (&7F80)
+
+                ASSERT  @ > TopOfPageZero ; Make sure we don't clash
+
+; *****************************************************************************
+; ***            Cursor, Sound DMA, SWI, and OSCLI workspace.               ***
+; ***        Sits in the 32K above 31M, ie. &01F000000..&01F07FFF           ***
+; ***        Has the physical address &02078000, ie. 32M + 512K - 32K       ***
+; *****************************************************************************
+
+TopOfDMAPhysRAM         *       &80000            ; OFFSET in physram
+TopOfDMAWorkSpace       *       CursorChunkAddress + 32*1024
+OffsetLogicalToPhysical *       TopOfDMAPhysRAM - TopOfDMAWorkSpace
+
+                        ^       TopOfDMAWorkSpace ; Note we will be going down
+
+; Sound
+
+SoundWorkSpaceSize      *       &1000
+SoundDMABufferSize      *       &1000
+SoundEvtSize            *       &1000
+SoundDMABuffers         |#|     SoundDMABufferSize * 2
+SoundWorkSpace          |#|     SoundWorkSpaceSize + SoundEvtSize
+
+; Cursor
+
+CursorDataSize          *       &800
+CursorData              |#|     CursorDataSize
+CursorSoundRAM          *       CursorData
+CursorSoundPhysRAM      *       CursorSoundRAM + OffsetLogicalToPhysical
+
+; SWI despatcher
+
+BranchToSWIExit         |#|     4
+SvcTable                |#|     &400
+ ASSERT SvcTable = &01F033FC ; Required for SVC table pokers, 1.20 compatible
+SWIDespatch_Size        *       29*4
+SWIDespatch             |#|     SWIDespatch_Size
+
+
+; Buffers
+
+KeyBuffSize             *       &100
+RS423InBuffSize         *       &100
+RS423OutBuffSize        *       &C0
+PrintBuffSize           *       &400
+Sound0BuffSize          *       4
+Sound1BuffSize          *       4
+Sound2BuffSize          *       4
+Sound3BuffSize          *       4
+SpeechBuffSize          *       4
+MouseBuffSize           *       &40
+KeyBuff                 |#|     KeyBuffSize
+RS423InBuff             |#|     RS423InBuffSize
+RS423OutBuff            |#|     RS423OutBuffSize
+PrintBuff               |#|     PrintBuffSize
+Sound0Buff              |#|     Sound0BuffSize
+Sound1Buff              |#|     Sound1BuffSize
+Sound2Buff              |#|     Sound2BuffSize
+Sound3Buff              |#|     Sound3BuffSize
+SpeechBuff              |#|     SpeechBuffSize
+MouseBuff               |#|     MouseBuffSize
+
+; Oscli buffering
+
+OscliBuffSize           *       &100
+OscliNoBuffs            *       16
+OscliCircBuffLimit      |#|     0
+OscliCircBuffStart      |#|     OscliBuffSize * OscliNoBuffs
+RedirectBuff            |#|     OscliBuffSize
+
+ [ AssemblingArthur
+ ! 0, "Aligning IRQ stack from ":CC::STR:@
+ ]
+ [ @-7*4 :AND: 15 <> 0
+                        |#|     (@-7*4):AND:15
+ ]
+IRQSTK                  #       0       ; Overflow will give abort
+ [ AssemblingArthur
+ ! 0, "IRQ stack size ":CC::STR:(IRQSTK-CursorChunkAddress)
+ ]
+
+ ASSERT @ > ( CursorChunkAddress + &1000 ) ; Check minimum stack
+
+; *****************************************************************************
+;                        High system workspace
+; *****************************************************************************
+
+                ^       SysHeapChunkAddress
+
+                #       8*1024          ; svcstk size. Overflow will give abort
+SVCSTK          #       0
+SysHeapStart    #       0
+
+
+        OPT     OldOpt
+        END
diff --git a/hdr/Old/NewSpace b/hdr/Old/NewSpace
new file mode 100644
index 0000000000000000000000000000000000000000..50f3233530f782e3cb8e78f3d8a3befe7493c868
--- /dev/null
+++ b/hdr/Old/NewSpace
@@ -0,0 +1,1172 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        SUBT    > &.Hdr.NewSpace
+
+OldOpt  SETA    {OPT}
+        OPT     OptNoList+OptNoP1List
+
+; ***********************************
+; ***    C h a n g e   L i s t    ***
+; ***********************************
+
+; Date       Name  Description
+; ----       ----  -----------
+; 02-Nov-87  APT   Added module SWI hash table
+; 03-Nov-87  APT   Modo-fied module SWI hash table info, removed BRKLST
+; 09-Nov-87  APT   Removed ESCCNT and ESFLG
+; 12-Nov-87  APT   Added IRQsema
+; 13-Nov-87  APT   Added DefaultIRQ1V codespace
+; 16-Nov-87  APT   PIRQ chain heads
+; 18-Nov-87  APT   Reordered EvtHan, EvtHan_ws
+; 19-Nov-87  APT   Moved IRQsema
+; 01-Dec-87  APT   Added interruptible heap manager workspace
+; 08-Dec-87  TMD   Added ECFShift, ECFYOffset
+; 14-Dec-87  TMD   Added DisplayNColour, DisplayModeFlags
+; 15-Dec-87  TMD   Added KeyAlphabet
+; 22-Dec-87  NDR   Using ScratchSpace
+; 13-Jan-88  APT   General scratchspace bash, low workspace reordering.
+;                  Removed spurious 32 bytes of osbyte wspace
+; 14-Jan-88  APT   *type buffer in scratchspace.
+;                  MOShasFIQ byte added
+; 20-Jan-88  APT   Workspace juggling for speed & space; also discarded
+;                  Level0 stuff.
+; 28-Jan-88  APT   MetroGnome moved to "public" location for ADFS
+; 02-Feb-88  APT   FIQclaim_interlock added
+; 05-Feb-88  APT   CallBack_Vector
+; 09-Feb-88  APT   RAM for SWI despatch
+; 17-Feb-88  TMD   Added VduSaveArea, VduSaveAreaPtr, DisplayModeNo
+; 26-Feb-88  APT   NoOfCamEntries manifest
+; 03-Mar-88  APT   Shrank SVC despatch
+; 03-Mar-88  APT   NoOfCamEntries manifest doubled
+; 07-Mar-88  TMD   Added DisplayScreenStart, VduOutputCurrentState,
+;                  SpriteMaskSelect, reordered mode variables
+; 07-Mar-88  APT   Made CamEntries always at &164
+; 07-Mar-88  TMD   Added GCharSizes, GCharSizeX, GCharSizeY
+; 08-Mar-88  TMD   Added GCharSpacing, GCharSpaceX, GCharSpaceY
+; 08-Mar-88  TMD   Moved GCharSizes..GCharSpaceY into first 1K of workspace
+; 15-Mar-88  TMD   Added HLineAddr
+; 18-Mar-88  TMD   Added DisplayXWindLimit, DisplayYWindLimit,
+;                   DisplayXEigFactor, DisplayYEigFactor, PointerXEigFactor
+; 18-Mar-88  APT   Setting variables scratchspace use revised.
+; 21-Mar-88  TMD   Removed CursorIndex
+; 22-Mar-88  TMD   Added TCharSizeX,TCharSizeY,TCharSpaceX,TCharSpaceY
+; 29-Mar-88  TMD   Added GcolOraEorAddr
+; 31-Mar-88  TMD   Removed WsFontPtr
+; 07-Apr-88  SKS   Added HeapSort use of ScratchSpace
+; 14-Apr-88  TMD   Added SerialFlags
+; 28-Apr-88  TMD   Added XONXOFFChar
+;  5-May-88  APT   Added MemorySpeed
+; 18-May-88  APT   Added CannotReset sema at &107, removed pre-1.20 changes.
+; 24-May-88  TMD   Added ClipBoxEnable, ClipBoxLCol..ClipBoxTRow, moved in
+;                   ScrLoaSpriteCB, ScrLoaBuffer, SrcSavCommon
+; 24-May-88  TMD   Flood fill uses ScratchSpace
+; 01-Jun-88  TMD   Added AlignSpace for ClipBoxCoords
+; 03-Jun-88  BCSKS Make Keyboard buffer into a useful part of the system
+;                  Also PrinterBufferSize
+; 09-Jun-88  DJS   Draw uses ScratchSpace
+; 09-Jun-88  BC    Gave Econet some private debungling space
+; 11-Jun-88  SKS   Align IRQ stack to make STMFD not cross two MEMC bdy's
+;                  Made info condit'l on AsmArf; someone had commented it out!
+; 15-Jun-88  SKS   Added two more instructions in SWIDespatch area
+;                  Moved SLVK definition into kernel; it's not public
+; 16-Jun-88  SKS   Added 3 more instructions in SWIDespatch area + nailed
+;                  SvcTable address for compatibility
+; 22-Jun-88  SKS   Moved MEMC_CR_SoftCopy into pubic ws
+; 19-Jul-88  APT   Added UpCall handler stuff
+; 20-Jul-88  SKS   Added above entry
+;                  Amended comment about overlaid workspace in vdu
+; 15-Aug-88  SKS   Inserted DomainId at FF8 (set by Wimp on task swap, used by
+;                  FileSwitch to tag resources)
+; 27-Sep-89  JSR   Added ColourTrans to users of scratch space
+; 24-Oct-89  TMD   Added CamEntriesForBigMachines, CamEntriesPointer
+; 26-Oct-89  TMD   Added MaxCamEntry, removed NoOfCamEntries symbol
+; 27-Oct-89  TMD   Added VIDCClockSpeed
+; 09-Nov-89  TMD   Added ResetIndirection
+; 15-Jan-91  TMD   Added ROMModuleChain
+; 04-Feb-91  DDV   Added DeviceFS as user of ScratchSpace.
+; 04-Feb-91  DDV   Added ColourTrans use of ScratchSpace to build diff tables.
+; 06-Mar-91  TMD   Added IOSystemType
+; 07-Mar-91  LVR   ADFS uses scratch space for floppy formatting
+; 07-Mar-91  TMD   Added MonitorLeadType
+; 08-Mar-91  TMD   Added PrinterBufferAddr, PrinterBufferSize
+; 11-Apr-91  TMD   Added SerialInHandle, SerialOutHandle
+; 24-Apr-91  TMD   Added UniqueMachineID
+; 09-Jun-91  RM    Added KernelMessagesBlock,ErrorSemaphore and MOSConvertBuffer
+; 26-Jul-91  JSR   Extend GeneralMOSBuffer by 4 bytes to make it a valid
+;                       length for the default error handler's error buffer
+; 19-Aug-91  JSR   Added *If to list of GeneralMOSBuffer users
+; 22-Aug-91  TMD   Reduced ErrorSemaphore to a byte, added PortableFlag
+; 25-Aug-91  DDV   Updated to indicate correct usage of scratch space by ColourTrans
+; 09-Jan-92  DDV   Added FgPattern, BgPattern and indicate use of ScratchSpace by OS_SetColour
+; 20-Jan-92  TMD   OS_SetColour no longer uses ScratchSpace
+; 17-Feb-92  ECN   Added CLibWord and RISCOSLibWord
+; 02-Apr-92  TMD   Added ScreenBlankFlag
+; 03-Aug-92  TMD   Kept in step with hdr:VickySpace, added PhysRam (from hdr:System)
+; 09-Sep-92  TMD   Added FirPalAddr, SecPalAddr
+; 10-Sep-92  DDV   Added pointer to the System heap copy of the Whizzy text expansion buffer
+; 07-Oct-92  TMD   Brought in line with VickySpace with respect to new VIDC stuff
+; 04-Jun-93  TMD   Added CurrentMonitorType
+; 15-Jul-93  TMD   Added KernelModeSelector
+; 25-Aug-93  SMC   Added processor vector table at ProcVec_Start
+;                  Added ProcVecPreVeneers
+; 07-Oct-93  TMD   Added ScreenBlankDPMSState, HSWRSoftCopy, VSWRSoftCopy
+;
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Memory map:
+
+AplWorkMaxSize      * &01000000 ; 16M
+
+RMAAddress          * &01800000
+RMAMaxSize          * &00400000 ; 4M
+
+SysHeapChunkAddress * &01C00000
+SysHeapMaxSize      * &00200000 ; 2M
+
+CursorChunkAddress  * &01F00000 ; Fixed size 32K
+
+ScreenEndAdr        * &02000000
+ScreenMaxSize       * 480*1024
+
+FontCacheAddress    * &01E00000
+FontCacheMaxSize    * &00100000 ; 1M
+
+SpriteSpaceAddress  * &01400000
+SpriteSpaceMaxSize  * &00400000 ; 4M
+
+RAMDiscAddress      * &01000000
+RAMDiscMaxSize      * &00400000 ; 4M
+
+PhysRam         *       &02000000
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; system variables
+
+        ^  0,R12
+
+OSBYTEFirstVar  #  0
+
+ByteVars  #  0                 ; The main osbyte variables, accessed
+                               ; via calls &A6 to &FF
+
+VarStart  #  2                 ; &A6,&A7
+ROMPtr    #  2                 ; &A8,&A9
+ROMInfo   #  2                 ; &AA,&AB
+KBTran    #  2                 ; &AC,&AD
+VDUvars   #  2                 ; &AE,&AF
+
+CFStime   #  1                 ; &B0
+InputStream #  1               ; &B1
+KeyBdSema #  1                 ; &B2
+
+ROMPollSema #  1               ; &B3
+OSHWM     #  1                 ; &B4
+
+RS423mode #  1                 ; &B5
+NoIgnore  #  1                 ; &B6
+CFSRFS    #  1                 ; &B7
+VULAcopy  #  2                 ; &B8,&B9
+
+ROMatBRK  #  1                 ; &BA
+BASICROM  #  1                 ; &BB
+
+ADCchanel #  1                 ; &BC
+ADCmaxchn #  1                 ; &BD
+ADCconv   #  1                 ; &BE
+
+RS423use     #  1              ; &BF
+RS423conflag #  1              ; &C0
+
+FlashCount # 1                 ; &C1
+SpacPeriod # 1                 ; &C2
+MarkPeriod # 1                 ; &C3
+
+KeyRepDelay # 1                ; &C4
+KeyRepRate  # 1                ; &C5
+
+ExecFileH   # 1                ; &C6
+SpoolFileH  # 1                ; &C7
+
+ESCBREAK    # 1                ; &C8 (200)
+
+KeyBdDisable # 1               ; &C9
+KeyBdStatus  # 1               ; &CA
+
+RS423HandShake # 1             ; &CB
+RS423InputSupr # 1             ; &CC
+RS423CFSFlag   # 1             ; &CD
+
+EconetOScall # 1               ; &CE
+EconetOSrdch # 1               ; &CF
+EconetOSwrch # 1               ; &D0
+
+SpeechSupr # 1                 ; &D1
+SoundSupr # 1                  ; &D2
+
+BELLchannel # 1                ; &D3
+BELLinfo    # 1                ; &D4
+BELLfreq    # 1                ; &D5
+BELLdur     # 1                ; &D6
+
+StartMessSupr # 1              ; &D7
+
+SoftKeyLen # 1                 ; &D8
+
+PageModeLineCount # 1          ; &D9
+
+VDUqueueItems # 1              ; &DA
+
+TABch # 1                      ; &DB
+ESCch # 1                      ; &DC
+
+IPbufferCh # 4                 ; &DD,&DE,&DF,&E0
+RedKeyCh   # 4                 ; &E1,&E2,&E3,&E4
+
+ESCaction  # 1                 ; &E5
+ESCeffect  # 1                 ; &E6
+
+u6522IRQ # 1                   ; &E7
+s6850IRQ # 1                   ; &E8
+s6522IRQ # 1                   ; &E9
+
+TubeFlag # 1                   ; &EA
+
+SpeechFlag # 1                 ; &EB
+
+WrchDest # 1                   ; &EC
+CurEdit  # 1                   ; &ED
+
+SoftResetVars # 0              ; Reset to here on soft reset
+
+KeyBase # 1                    ; &EE
+Shadow # 1                     ; &EF
+Country # 1                    ; &F0
+
+UserFlag # 1                   ; &F1
+
+SerULAreg # 1                  ; &F2
+
+TimerState # 1                 ; &F3
+
+SoftKeyConsist # 1             ; &F4
+
+PrinterDrivType   # 1          ; &F5
+PrinterIgnore     # 1          ; &F6
+
+HardResetVars # 0              ; Reset to here on hard reset
+
+BREAKvector # 3                ; &F7,&F8,&F9
+
+MemDriver  # 1                 ; &FA - where the VDU drivers write to
+MemDisplay # 1                 ; &FB - where we display from
+
+LangROM # 1                    ; &FC
+
+LastBREAK # 1                  ; &FD
+
+KeyOpt # 1                     ; &FE
+
+StartOptions # 1               ; &FF
+
+PowerOnResetVars # 0           ; Reset to here on power-on reset
+
+; These two can dovetail in here to use up 2 bytes before the AlignSpace!
+
+SerialInHandle # 1              ; Handle for serial input stream  (0 if not open currently)
+SerialOutHandle # 1             ; Handle for serial output stream (-----------""----------)
+
+        AlignSpace
+
+EventSemaphores # 32            ; One byte for each of 32 events
+
+TimerAlpha # 8                  ; As used by time (bottom 5 bytes)
+TimerBeta  # 8                  ; ................................
+; both aligned to word boundaries
+
+RealTime # 8                    ; 5-byte fast real-time
+
+PrinterActive # 4               ; Handle/active flag for printer (word aligned)
+
+IntervalTimer # 5               ; Up Counter synchronous with TIME.
+; Event generated when Zero is reached
+; bottom byte aligned to word boundary
+
+SecondsTime # 1 ; the soft copy (centi-)seconds of the RTC
+CentiTime   # 1 ; """"""""""""""""""""""""""""""""""""""""
+
+FlashState # 1 ; which flash colours are we using
+
+SecondsDirty # 1                ; the dirty flag for start up!
+
+MinTick # 1                     ; the minutes odd/even state
+
+DCDDSRCopy # 1                  ; copy of ACIA bits to check for change
+
+TVVertical # 1                  ; *TV first parameter
+
+TVInterlace # 1                 ; *TV second parameter
+
+CentiCounter # 1                ; Counter for VDU CTRL timing
+
+Alphabet # 1                    ; Current alphabet number
+
+Keyboard # 1                    ; Current keyboard number
+
+KeyAlphabet # 1                 ; Alphabet associated with current keyboard
+
+                GBLS    PrinterPrefix
+PrinterPrefix   SETS    "PrinterType$"
+
+PrinterTypeName #       6 + :LEN: (PrinterPrefix)
+
+        AlignSpace
+
+SerialFlags # 4                 ; New serial flags
+
+XONXOFFChar # 1                 ; Character to send before rest (0 if none)
+
+        AlignSpace
+
+OSBYTEVarSize * @-OSBYTEFirstVar
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+; End of variables' space
+
+
+; ***********************************
+; ***  Main Vdu Driver Workspace  ***
+; ***********************************
+
+        ^ 0
+
+FgEcf  # 4 * 8  ; Foreground Ecf, set by GCOL(a,0-127)
+BgEcf  # 4 * 8  ; Background Ecf, set by GCOL(a,128-255)
+GPLFMD # 4      ; Foreground action, set by GCOL(a,0-127)
+GPLBMD # 4      ; Background action, set by GCOL(a,128-255)
+GFCOL  # 4      ; Foreground colour, set by GCOL(a,0-127)
+GBCOL  # 4      ; Background colour, set by GCOL(a,128-255)
+
+GWLCol # 4      ; Graphics window left column  --
+GWBRow # 4      ; Graphics window bottom row     |
+GWRCol # 4      ; Graphics window right column   |
+GWTRow # 4      ; Graphics window top row      --
+
+qqqPad   # 3
+QQ       # 17   ;Queue - QQ+1 is on a word boundary
+QOffset  # 4    ;Value to add to VDUqueueItems to point to next queue posn.
+JVec     # 4    ;Jump vector to internal routines
+
+; Start of MODE table workspace
+
+ScreenSize # 4  ; number of bytes needed for this mode (assumed 1st in list)
+
+XWindLimit # 4  ; Maximum value of GWRCol (internal representation)
+
+; LineLength must be immediately after YWindLimit
+
+YWindLimit # 4  ; Maximum value of GWTRow (internal representation)
+
+LineLength # 4  ; Length of one pixel row in bytes
+
+NColour # 4     ; Number of colours minus 1
+
+; End of word mode variables
+
+YShftFactor # 4 ; Number of places to shift YCoord in address generation after
+                ; multiplying by 5, holds
+                ; 7,6,5 or 4 for 8,4,2 or 1 bits per pixel (640x256 mode) or
+                ; 6,5,4 or 3 for 8,4,2 or 1 bits per pixel (320x256 mode).
+
+ModeFlags # 4   ; Bit 0 => non-graphic, Bit 1 => teletext, Bit 2 => gap mode
+
+XEigFactor # 4  ; Number of places to shift XCoord in external to internal
+                ; coordinate conversion, holds
+                ; 1 for 640x256 mode
+                ; 2 for 320x256 mode
+                ; 3 for 160x256 (BBC micro mode 2)
+
+YEigFactor # 4  ; number of shifts to convert between internal/external Y
+
+Log2BPC # 4     ; Log to base 2 of BytesPerChar ie (0,1,2,3,4)
+
+Log2BPP # 4     ; Log to base 2 of BitsPerPix ie (0,1,2,3)
+
+ECFIndex # 4    ; Index into default ECF tables
+
+ScrRCol # 4     ; Maximum column number in this screen mode
+ScrBRow # 4     ; Maximum row number in this screen mode
+
+PalIndex # 4    ; Index into palette tables (0,1,2,3)
+
+; End of table-initialised workspace
+
+; Next 3 must be together in this order !
+
+XShftFactor # 4 ; Number of places to shift XCoord in address generation,
+                ; holds 2,3,4 or 5 for 8,4,2,1 bits per pixel respectivly
+GColAdr # 4     ; Address of Ecf to plot - either FgEcf or BgEcf
+
+ScreenStart # 4         ; Start address of screen (for VDU drivers)
+
+NPix # 4        ; Number of pixels per word minus 1, holds
+                ; holds 3,7,15 or 31 for 8,4,2,1 bits per pixel modes
+
+AspectRatio # 4 ; Pixel shape : 0 square, 1 horz rect, 2 vert rect
+
+BitsPerPix # 4  ; Bits per pixel (1,2,4,8)
+
+BytesPerChar # 4        ; Bytes per one line of character
+                        ; (same as BitsPerPix except in double pixel modes)
+
+CursorFudgeFactor # 4   ; Factor for horizontal cursor positioning
+
+RowMult # 4     ; Row multiplier for text manipulation
+
+RowLength # 4   ; Bytes per text row in this mode (eg 640,1280,5120)
+
+; The following (up to and including NewPtY) must be together in this order
+; (relied upon by DefaultWindows)
+
+TWLCol # 4      ; Text window left column  --
+TWBRow # 4      ; Text window bottom row     |
+TWRCol # 4      ; Text window right column   |
+TWTRow # 4      ; Text window top row      --
+
+OrgX # 4        ; Screen origin (external representation)
+OrgY # 4
+
+GCsX # 4        ; Graphics cursor (external representation)
+GCsY # 4
+
+OlderCsX # 4    ; Very old X coordinate (internal)
+OlderCsY # 4    ; Very old Y coordinate (internal)
+
+OldCsX # 4      ; Old graphics cursor (internal representation) --
+OldCsY # 4      ;                                                 |
+                ;                                                 |
+GCsIX  # 4      ; Graphics cursor (internal representation)       |
+GCsIY  # 4      ;                                                 |
+                ;                                                 |
+NewPtX # 4      ; Newest point (internal representation)          |
+NewPtY # 4      ;                                               --
+
+; End of together block
+
+TForeCol # 4    ; Text foreground colour
+TBackCol # 4    ; Text background colour
+
+CursorX # 4     ; Text cursor X position ; these 3 must be in same order as ...
+CursorY # 4     ; Text cursor Y position
+CursorAddr # 4  ; Screen address of (output) cursor
+
+InputCursorX # 4        ; Input cursor X position ; ... these 3
+InputCursorY # 4        ; Input cursor Y position
+InputCursorAddr # 4     ; Screen address of input cursor
+
+EORtoggle # 4   ; Toggle between gap and non-gap
+RowsToDo  # 4   ; in the CLS
+
+VduStatus # 4   ; Vdu2, Window, Shadow bits (others in CursorFlags)
+
+CBWS # 8        ; Clear block (VDU 23,8..) workspace
+CBStart # 2
+CBEnd # 2
+
+CursorDesiredState # 4
+CursorStartOffset # 4
+CursorEndOffset # 4
+CursorCounter # 4
+CursorSpeed # 4
+Reg10Copy # 4
+
+CursorFill # 4  ; Word to EOR cursor ; MUST be immediately before CursorNbit
+
+CursorNbit # 4  ; Pointer to cursor code for current mode
+
+DisplayStart # 4        ; Start address of screen (for display)
+DriverBankAddr # 4      ; Default start address for VDU drivers
+DisplayBankAddr # 4     ; Default start address for display
+DisplayNColour # 4      ; No. of colours -1 for displayed mode
+DisplayModeFlags # 4    ; ModeFlags for displayed mode
+DisplayModeNo # 4       ; ModeNo for displayed mode
+DisplayScreenStart # 4  ; Where VDU outputs to when outputting to screen
+
+DisplayXWindLimit # 4   ; Used for pointer programming
+DisplayYWindLimit # 4
+DisplayXEigFactor # 4
+DisplayYEigFactor # 4
+PointerXEigFactor # 4
+
+Ecf1 # 8        ; The Ecf patterns
+Ecf2 # 8
+Ecf3 # 8
+Ecf4 # 8
+
+DotLineStyle # 8        ; Dot dash line pattern
+
+ModeNo # 4      ; Current mode number
+
+TFTint # 4      ; Text foreground tint          (in bits 6,7)
+TBTint # 4      ; Text background tint
+GFTint # 4      ; Graphics foreground tint
+GBTint # 4      ; Graphics background tint
+
+TotalScreenSize # 4     ; Amount configured for screen (in bytes)
+
+MaxMode # 4             ; Maximum mode number allowed (20 for now)
+
+VinitCopy # 4   ; Copy of Vinit for VDU 23;12 or 13
+
+CursorFlags # 4 ; Silly Master cursor movement flags
+
+CursorStack # 4 ; Bit stack of nested cursor states (0 => on, 1 => off)
+                ; (bit 31 = TOS)
+
+ECFShift # 4    ; number of bits to rotate right ECF OR and EOR masks by
+ECFYOffset # 4  ; vertical offset to ECF index
+
+WsVdu5 # 0      ; Vdu 5 workspace
+WsScr # 4
+WsEcfPtr # 4
+; WsFontPtr # 4 ; not needed any more, kept in register
+EndVerti # 4
+StartMask # 4
+EndMask # 4
+FontOffset # 4
+TempPlain # 16  ; only used for MODE 10
+
+VIDCClockSpeed # 4      ; current VIDC clock speed in kHz
+
+CurrentMonitorType # 4  ; initialised from configured one
+
+KernelModeSelector # 4  ; pointer to block in system heap where
+                        ; current mode selector is copied
+
+GraphicWs # 300 ; All graphics workspace is overlaid here
+EndGraphicWs # 0
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+        AlignSpace 16
+
+GCharSizes  # 0
+GCharSizeX  # 4         ; width of VDU 5 chars in pixels
+GCharSizeY  # 4         ; height of VDU 5 chars in pixels
+
+GCharSpacing # 0
+GCharSpaceX  # 4        ; horizontal spacing between VDU 5 chars in pixels
+GCharSpaceY  # 4        ; vertical   ------------------""-----------------
+
+TCharSizes  # 0
+TCharSizeX  # 4         ; width of VDU 4 chars in pixels
+TCharSizeY  # 4         ; height of VDU 4 chars in pixels
+
+TCharSpacing # 0
+TCharSpaceX  # 4        ; horizontal spacing between VDU 4 chars in pixels
+TCharSpaceY  # 4        ; vertical   ------------------""-----------------
+
+HLineAddr    # 4        ; address of exported HLine
+GcolOraEorAddr # 4      ; address of FgEcfOraEor etc
+
+FirPalSetting # 4*28            ; First palette settings (not used on VIDC20)
+FirPalAddr * FirPalSetting      ; Address of block for first palette setting (only used on VIDC20)
+SecPalSetting # 4*28            ; Second palette settings (not used on VIDC20)
+SecPalAddr * SecPalSetting      ; Address of block for second palette setting (only used on VIDC20)
+
+TextFgColour    # 4             ; Fg/Bg colour stored as a colour number, computed on VDU 18 and re-poked!
+TextBgColour    # 4             ;
+
+; In this brave new world there is a pointer to the text expansion
+; buffer used for VDU 4 / 5 text plotting.
+
+; This now lives in the system heap.
+
+TextExpandArea # 4      ; Pointer to Text expand area (in system heap)
+TextExpandArea_Size * (8*1024)
+
+HSWRSoftCopy    #       4       ; soft copy of h.sync width register (for DPMS)
+VSWRSoftCopy    #       4       ; soft copy of v.sync width register (for DPMS)
+
+ScreenBlankFlag # 1     ; 0 => unblanked, 1 => blanked
+ScreenBlankDPMSState # 1        ; 0 => just blank video
+                                ; 1 => blank to stand-by (hsync off)
+                                ; 2 => blank to suspend (vsync off)
+                                ; 3 => blank to off (H+V off)
+
+ [ AssemblingArthur
+ ! 0,"64 ":CC::STR:@
+ ]
+        AlignSpace 64
+
+FgEcfOraEor # 4*16      ; Interleaved zgora & zgeor
+BgEcfOraEor # 4*16      ; Interleaved zgora & zgeor
+BgEcfStore  # 4*16      ; Interleaved zgora & zgeor to store background
+
+;Current state of pattern
+LineDotCnt # 4          ; Count down to restarting pattern
+LineDotPatLSW # 4       ; Current state of pattern LSWord
+LineDotPatMSW # 4       ;    "      "   "     "    MSWord
+
+DotLineLength # 4       ; Dot Pattern repeat length as given in *FX163,242,n
+
+BBCcompatibleECFs # 4   ; 0 => BBC compatible, 1 => native
+
+SpAreaStart # 4         ; Start of sprite area
+SpChooseName # 16       ; No comment says Richard
+SpChoosePtr # 4
+
+PointerHeights # 4      ; 4 x 1 byte
+PointerActiveXs # 4     ; 4 x 1 byte
+PointerActiveYs # 4     ; 4 x 1 byte
+PointerShapeNumber # 4  ; only bottom byte used
+PointerX # 4            ; co-ordinates of pointer (not always = mouse)
+PointerY # 4
+
+VIDCControlCopy # 4     ; Soft copy of VIDC control register
+VertAdjust # 4          ; offset to add to vertical VIDC registers
+
+TeletextOffset # 4      ; Offset to current teletext flash bank
+
+TeletextCount # 4       ; Number of vsyncs till next teletext flash
+
+WrchNbit # 4            ; Pointer to char code for current mode
+
+BeepBlock # 8           ; OSWORD block for VDU 7
+
+ScreenMemoryClaimed # 1 ; NZ => memory has been claimed or is unusable
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+     AlignSpace 16      ; Align workspace to 16 bytes
+
+TTXDoubleCounts # 25    ; Number of double height chars on each line
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+     AlignSpace 16      ; Align workspace to 16 bytes
+
+RAMMaskTb # 32*4        ; Copy of MaskTb for this mode (up to 32 words)
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+     AlignSpace 16      ; Align workspace to 16 bytes
+
+VduOutputCurrentState # 0 ; values of R0-R3 to return from SwitchOutputToSprite
+                        ; or Mask; next 4 must be in this order
+SpriteMaskSelect # 4    ; value of R0 to be given to SWI OS_SpriteOp to set up
+                        ; current state
+VduSpriteArea # 4       ; Pointer to sprite area containing VDU output sprite
+                        ; (0 if output is to screen)
+VduSprite # 4           ; Pointer to VDU output sprite (0 if output to screen)
+
+VduSaveAreaPtr # 4      ; Pointer to save area for VDU variables
+
+
+ [ AssemblingArthur
+ ! 0,"16,12 ":CC::STR:@
+ ]
+    AlignSpace 16, 12   ; Make ClipBoxCoords a valid immediate,
+                        ; with ClipBoxEnable immediately before it
+ClipBoxInfo # 0
+ClipBoxEnable # 4       ; 0 => clip box disabled, 1 => enabled
+
+ClipBoxCoords # 0       ; Internal coords of modified area of screen
+ClipBoxLCol # 4
+ClipBoxBRow # 4
+ClipBoxRCol # 4
+ClipBoxTRow # 4
+
+FgPattern       # 4*8   ; foreground pattern as defined by OS_SetColour
+BgPattern       # 4*8   ; background pattern as defined by OS_SetColour
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+
+     AlignSpace 16      ; Align workspace to 16 bytes
+
+TextExpand # 4*1024     ; Tim's massive text expansion table for whizzy WRCH
+; TextPlain is now always hard against the end of TextExpand for this mode
+
+TTXSoftFonts * TextExpand + 2*1024      ; Soft fonts in teletext mode
+
+ [ AssemblingArthur
+ ! 0,"64 ":CC::STR:@
+ ]
+     AlignSpace 64      ; Align workspace to 64 bytes
+
+; Teletext map and copy/move buffer are overlaid
+
+TTXMapSize      * 41*25*4       ; (&1004 bytes)
+LargeCommon     # TTXMapSize    ; the largest area
+TTXMap          * LargeCommon
+
+ScrLoaSpriteCB  * LargeCommon   ; (size = SpriteCBsize + MaxSpritePaletteSize)
+ScrLoaBuffer    * LargeCommon   ; (size = one pixel row)
+ScrSavCommon    * LargeCommon   ; (size = SpriteAreaCBsize + SpriteCBsize
+                                ;  + MaxSpritePaletteSize)
+
+FldQueueSize    * ScratchSpaceSize
+FldQueueStart   * ScratchSpace
+
+ [ AssemblingArthur
+ ! 0,"64 ":CC::STR:@
+ ]
+     AlignSpace 64      ; Align workspace to 64 bytes
+
+Font # &700             ; 7 pages of (soft) font
+
+SaveAreaSize * 12*1024-@
+
+VduSaveArea # SaveAreaSize      ; save area for switching output to sprites
+
+VDWSSize # 0
+
+                ASSERT  VDWSSize <= 12 * 1024
+
+; *****************************************************************************
+;                 Space in the first 32K is allocated below
+; *****************************************************************************
+; Real workspace definition
+
+; Basic kernel space - defined locations for external modules
+
+                ^       &100
+IRQ1V           #       4       ; &100
+
+ESC_Status      #       1       ; &104
+LatchBSoftCopy  #       1       ; &105
+IOCControlSoftCopy #    1       ; &106
+CannotReset     #       1       ; &107
+
+IRQsema         #       4       ; &108
+MetroGnome      #       4       ; &10C
+MemorySpeed     #       4       ; &110
+
+MEMC_CR_SoftCopy #      4       ; &114
+ResetIndirection #      4       ; &118
+
+; Now all internal definitions
+
+; Up to here is initialized on reset
+
+; Next come handler variables
+
+MemLimit        #       4
+UndHan          #       4
+PAbHan          #       4
+DAbHan          #       4
+AdXHan          #       4
+
+ErrHan          #       4
+ErrBuf          #       4
+ErrHan_ws       #       4
+
+CallAd_ws       #       4     ; smart Rs ordering:
+CallAd          #       4     ; can do LDMIA of r12, pc
+CallBf          #       4
+
+BrkAd_ws        #       4
+BrkAd           #       4
+BrkBf           #       4
+
+EscHan_ws       #       4
+EscHan          #       4
+
+EvtHan_ws       #       4
+EvtHan          #       4
+
+CamEntries      #       4 * 256 ; CAM entries + PPL in here for machines up to
+                                ; 8 MBytes - for larger machines they're stored
+                                ; in CamEntriesForBigMachines - the location
+                                ; CamEntriesPointer points to whichever is used
+ ASSERT CamEntries = &164 ; Fixed for PCEmulator (doesn't work with >= 8MBytes)
+
+CamEntriesPointer #     4       ; points to where CAM soft copy is
+                                ; (CamEntries for machines up to 8MBytes,
+                                ; CamEntriesForBigMachines for larger machines)
+
+MaxCamEntry     #       4       ; maximum index into the cam map, ie
+                                ; 511 for 16MByte machines, 383 for 12MBytes
+                                ; 255 for 8MBytes, otherwise 127
+
+RAMLIMIT        #       4
+
+AplWorkSize     #       4
+
+HiServ_ws       #       4
+HiServ          #       4
+SExitA          #       4
+SExitA_ws       #       4
+UpCallHan_ws    #       4
+UpCallHan       #       4
+
+ROMModuleChain  #       4               ; pointer to head of ROM module chain
+
+; now a section that it's handy to have in simply loadable places
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+             AlignSpace 16
+
+KeyWorkSpaceSize   *  &200
+KeyWorkSpace       #  KeyWorkSpaceSize
+
+; The following were reordered on 26-Jul-91. Old ordering was:
+; GeneralMOSBuffer
+; ModuleSWI_HashTab
+; Module_List
+; Curr_Active_Object
+; VecPtrTab
+; ExceptionDump
+
+ModuleSHT_Entries  *  16
+ModuleSWI_HashTab  #  4*ModuleSHT_Entries
+
+Module_List        #  4
+Curr_Active_Object #  4
+
+; Vector Claim & Release tables etc
+
+VecPtrTab          #  NVECTORS * 4
+
+ExceptionDump      #  4
+
+; GeneralMOSBuffer: re-use with caution!
+; Here's just some of the users:
+; user                  use(s)
+; default error handler error buffer (must be 246+4 bytes big)
+; *If                   expression to be evaluated to control the *If
+;                       Command line to be submited on the expression
+;                         evaluating to non-zero (the THEN clause).
+GeneralMOSBuffer   #  256+4
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+            AlignSpace  16 ; Ensures we can MOV rn, #OsbyteVars if <=&1000
+
+OsbyteVars      #       OSBYTEVarSize
+ ASSERT OsbyteVars < &10000 ; Must keep in first 64K so address can be read by
+                            ; (and stored in) OS_Bytes &A6,&A7. SKS
+
+; These must be in first 4K
+NBuffers        *       10
+BuffInPtrs      #       4 * NBuffers
+BuffOutPtrs     #       4 * NBuffers
+
+VariableList    #       4
+
+; Oscli stuff
+OscliCBtopUID   #       4
+OscliCBbotUID   #       4
+OscliCBcurrend  #       4
+
+ReturnCode      #       4
+RCLimit         #       4
+
+SpriteSize      #       4       ; saved on startup for Sprite code and RAMFS
+RAMDiscSize     #       4
+FontCacheSize   #       4       ; and font manager
+
+TickNodeChain   #       4
+
+; Workspace
+
+EnvTime            #    5
+RedirectInHandle   #    1
+RedirectOutHandle  #    1
+MOShasFIQ          #    1
+FIQclaim_interlock #    1
+CallBack_Flag      #    1
+IRQ_CallBack_Flag * CallBack_Flag
+IOSystemType    #       1       ; 0 => old I/O subsystem, 1 => IOEB+82C710 system, 2..255 => ?
+MonitorLeadType #       1       ; some function of the monitor lead inputs, as yet undetermined
+
+                  AlignSpace
+
+EnvString         #     256
+
+
+DUMPER            #     16 * 4
+
+; more system workspace
+Page_Size         #  4
+PIRQ_Chain        #  4
+PFIQasIRQ_Chain   #  4
+
+; IRQ despatch
+DefIRQ1Vspace       *     9*4+12*17+2*256   ; for size checking in MOS
+DefaultIRQ1V        #     DefIRQ1Vspace     ; assembly
+
+
+CallBack_Vector   #  4
+
+; interruptible heap manager workspace
+
+HeapSavedReg_R0     # 4
+HeapSavedReg_R1     # 4
+HeapSavedReg_R2     # 4
+HeapSavedReg_R3     # 4
+HeapSavedReg_R4     # 4
+HeapSavedReg_R13    # 4
+HeapReturnedReg_R0  # 4
+HeapReturnedReg_R1  # 4
+HeapReturnedReg_R2  # 4
+HeapReturnedReg_R3  # 4
+HeapReturnedReg_R4  # 4
+HeapReturnedReg_R13 # 4
+HeapReturnedReg_PC  # 4                 ; also acts as interlock
+
+PrinterBufferAddr   #  4                ; holds address of printer buffer
+PrinterBufferSize   #  4                ; size of printer buffer - not to be confused with PrintBuffSize
+                                        ; which is the (constant) default size for the MOS's smallish buffer
+UniqueMachineID     #  8                ; 64 bits for unique machine ID
+KernelMessagesBlock #  20               ; 5 Words for messagetrans message block.
+ErrorSemaphore      #  1                ; Error semaphore to avoid looping on error translation.
+PortableFlag        #  1                ; Non-zero => got an error from Portable_Speed, so don't try it again
+
+        AlignSpace
+
+MOSConvertBuffer    #  12               ; Enough romm for 8 hex digits.
+
+ProcVec_Start           #       0       ; Start of processor vector table
+ProcVec_Branch0         #       4       ; Branch through zero
+ProcVec_UndInst         #       4       ; Undefined instruction vector
+ProcVec_SWI             #       4       ; SWI vector
+ProcVec_PrefAb          #       4       ; Prefetch abort vector
+ProcVec_DataAb          #       4       ; Data abort vector
+ProcVec_AddrEx          #       4       ; Address exception vector (not useful on ARM600/700)
+ProcVec_IRQ             #       4       ; IRQ vector
+ProcVec_End             #       0
+
+ProcVecPreVeneersSize   *       4*4     ; Space for preveneers for loading handler addresses from 0 page.
+ProcVecPreVeneers       #       ProcVecPreVeneersSize
+
+ [ AssemblingArthur
+ ! 0, "low space free ":CC::STR:(&FEC-@)
+ ]
+ ASSERT @ < &FEC
+
+; Words for old tools of assorted varieties
+                        ^       &FEC
+; ECN 17-Feb-92
+; Added RISCOSLibWord and CLibWord. The ROM RISCOSLib and CLib must continue
+; to work even when they are killed since ROM apps are hard linked to the
+; ROM libraries. They cannot use the private word since the block pointed
+; to by this will be freed.
+RISCOSLibWord           #       4
+CLibWord                #       4
+FPEAnchor               #       4
+DomainId                #       4       ; SKS added for domain identification
+Modula2_Private         #       4       ; MICK has FFC and uses it it in USR mode
+
+VduDriverWorkSpace      #       VDWSSize
+ ASSERT (VduDriverWorkSpace :AND: 63) = 0 ; For Tim (VDU5)
+
+
+ [ AssemblingArthur
+ ! 0, "high space free ":CC::STR:(&4000-@)
+ ]
+
+                        ^       &4000
+ScratchSpaceSize        *       &4000
+ScratchSpace            #       ScratchSpaceSize
+
+ ASSERT @ <= &8000 ; Start of apl
+
+; *****************************************************************************
+; Users of ScratchSpace declare yourself here:
+
+; NRaine: Filling a polygon uses ScratchSpace to flatten the path
+
+; DSeal: Draw module uses ScratchSpace on fill operations (this supercedes
+;   NRaine's declaration above).
+
+; SKS: HeapSort with (r1 & 0x80000000) & ~(r1 & 0x20000000) & (r5 <= 16K)
+;      uses ScratchSpace as a temp slot for data shuffling after sorting
+
+; TMD: Flood fill uses ScratchSpace for the flood queue.
+
+; Tidying the RMA uses ScratchSpace while all modules are dead
+
+; GSTRANS workspace: GSINIT puts state into the workspacem and GSREAD uses it.
+; DO NOT do any operations between GSINIT/GSREAD SWIS.
+; SWIs called: OSWord in time var code
+;              BinaryToDecimal, ReadUnsigned
+
+; LVR: ADFS uses scratch space to format floppies on 1772 based machines
+
+; DDV: ColourTrans uses scratch space to build palette tables when in
+;      ColourTrans_SelecTable/RetrunColourNumber and also whilst generating
+;      stipple pattterns.
+
+GSVarWSpace             *       ScratchSpace
+
+                        ^       0
+GSNameBuff              #       &100
+GS_Stack                #       &200
+GS_StackPtr_Lim         *       &200 / 4        ; Number of words in stack.
+GS_StackPtr             #       4
+
+                        ^      @ + ScratchSpace
+
+; Pointers for SubstituteArgs: no external calls.
+; Ensure these don't overlap FileSwitch's buffers below!
+
+MacExStartPtrs          #       44
+MacExEndPtrs            #       44
+
+; OS_CLI has a buffer for alias expansion: ReadVarVal and SubstituteArgs
+;    are called while the buffer is held. Also used for module prefixes:
+;    Module called twice in this case.
+
+AliasExpansionBuffer    #       100
+
+; *list/*type need an argument expansion buffer: ReadArgs called with it.
+
+ArgumentBuffer           *       AliasExpansionBuffer
+
+; EvaluateExpression space. Calls ReadUnsigned, BinaryToDecimal and ReadVarVal.
+
+ExprWSpace              *       @
+
+                        ^       0, R12
+ExprBuff                #       &100
+exprBracDif             #       2       ; keep exprSTRACC aligned
+tos_op                  #       2       ; 1 byte for use as STRACC-1
+ExprSVCstack            #       4
+exprSTRACC              *       @ - ExprBuff + ExprWSpace
+
+ExprStackLimit          *       exprSTRACC + &100
+ExprStackStart          *       ScratchSpace + ScratchSpaceSize
+
+
+; Tutu needs some for argument substitution + expansion for run/load types
+; Only OS call during xform is XOS_SubstituteArgs and XOS_Heap(Claim,SysHeap)
+
+                        ^       0 ; Offset from ScratchSpace
+rav_substituted         #       256
+rav_arglist             #       256
+
+TopOfPageZero           #       0
+
+                        ^       &8000 ; The actual top of Page Zero
+EconetDebugSpace        |#|     &20 * 4 ; Thirty two words (&7F80)
+
+                ASSERT  @ > TopOfPageZero ; Make sure we don't clash
+
+; *****************************************************************************
+; ***            Cursor, Sound DMA, SWI, and OSCLI workspace.               ***
+; ***        Sits in the 32K above 31M, ie. &01F000000..&01F07FFF           ***
+; ***        Has the physical address &02078000, ie. 32M + 512K - 32K       ***
+; *****************************************************************************
+
+TopOfDMAPhysRAM         *       &80000            ; OFFSET in physram
+TopOfDMAWorkSpace       *       CursorChunkAddress + 32*1024
+OffsetLogicalToPhysical *       TopOfDMAPhysRAM - TopOfDMAWorkSpace
+
+                        ^       TopOfDMAWorkSpace ; Note we will be going down
+
+; Sound
+
+SoundWorkSpaceSize      *       &1000
+SoundDMABufferSize      *       &1000
+SoundEvtSize            *       &1000
+SoundDMABuffers         |#|     SoundDMABufferSize * 2
+SoundWorkSpace          |#|     SoundWorkSpaceSize + SoundEvtSize
+
+; Cursor
+
+CursorDataSize          *       &800
+CursorData              |#|     CursorDataSize
+CursorSoundRAM          *       CursorData
+CursorSoundPhysRAM      *       CursorSoundRAM + OffsetLogicalToPhysical
+
+; SWI despatcher
+
+BranchToSWIExit         |#|     4
+SvcTable                |#|     &400
+ ASSERT SvcTable = &01F033FC ; Required for SVC table pokers, 1.20 compatible
+SWIDespatch_Size        *       29*4
+SWIDespatch             |#|     SWIDespatch_Size
+
+
+; Buffers
+
+KeyBuffSize             *       &100
+RS423InBuffSize         *       &100
+RS423OutBuffSize        *       &C0
+PrintBuffSize           *       &400
+Sound0BuffSize          *       4
+Sound1BuffSize          *       4
+Sound2BuffSize          *       4
+Sound3BuffSize          *       4
+SpeechBuffSize          *       4
+MouseBuffSize           *       &40
+KeyBuff                 |#|     KeyBuffSize
+RS423InBuff             |#|     RS423InBuffSize
+RS423OutBuff            |#|     RS423OutBuffSize
+PrintBuff               |#|     PrintBuffSize
+Sound0Buff              |#|     Sound0BuffSize
+Sound1Buff              |#|     Sound1BuffSize
+Sound2Buff              |#|     Sound2BuffSize
+Sound3Buff              |#|     Sound3BuffSize
+SpeechBuff              |#|     SpeechBuffSize
+MouseBuff               |#|     MouseBuffSize
+
+; Oscli buffering
+
+OscliBuffSize           *       &100
+OscliNoBuffs            *       16
+OscliCircBuffLimit      |#|     0
+OscliCircBuffStart      |#|     OscliBuffSize * OscliNoBuffs
+RedirectBuff            |#|     OscliBuffSize
+
+; New soft CAM address for 12MByte or 16MByte machines
+
+CamEntriesForBigMachines |#|    512*4   ; 512 entries, each is a word
+
+ [ AssemblingArthur
+ ! 0, "Aligning IRQ stack from ":CC::STR:@
+ ]
+ [ @-7*4 :AND: 15 <> 0
+                        |#|     (@-7*4):AND:15
+ ]
+IRQSTK                  #       0       ; Overflow will give abort
+ [ AssemblingArthur
+ ! 0, "IRQ stack size ":CC::STR:(IRQSTK-CursorChunkAddress)
+ ]
+
+ ASSERT @ > ( CursorChunkAddress + &1000 ) ; Check minimum stack
+
+; *****************************************************************************
+;                        High system workspace
+; *****************************************************************************
+
+                ^       SysHeapChunkAddress
+
+                #       8*1024          ; svcstk size. Overflow will give abort
+SVCSTK          #       0
+SysHeapStart    #       0
+
+
+        OPT     OldOpt
+        END
diff --git a/hdr/Old/VickySpace b/hdr/Old/VickySpace
new file mode 100644
index 0000000000000000000000000000000000000000..c060e361fc551558c11b02b52f30938d3f371c12
--- /dev/null
+++ b/hdr/Old/VickySpace
@@ -0,0 +1,1296 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        SUBT    > &.Hdr.VickySpace
+
+OldOpt  SETA    {OPT}
+        OPT     OptNoList+OptNoP1List
+
+; ***********************************
+; ***    C h a n g e   L i s t    ***
+; ***********************************
+
+; Date       Name  Description
+; ----       ----  -----------
+; 02-Nov-87  APT   Added module SWI hash table
+; 03-Nov-87  APT   Modo-fied module SWI hash table info, removed BRKLST
+; 09-Nov-87  APT   Removed ESCCNT and ESFLG
+; 12-Nov-87  APT   Added IRQsema
+; 13-Nov-87  APT   Added DefaultIRQ1V codespace
+; 16-Nov-87  APT   PIRQ chain heads
+; 18-Nov-87  APT   Reordered EvtHan, EvtHan_ws
+; 19-Nov-87  APT   Moved IRQsema
+; 01-Dec-87  APT   Added interruptible heap manager workspace
+; 08-Dec-87  TMD   Added ECFShift, ECFYOffset
+; 14-Dec-87  TMD   Added DisplayNColour, DisplayModeFlags
+; 15-Dec-87  TMD   Added KeyAlphabet
+; 22-Dec-87  NDR   Using ScratchSpace
+; 13-Jan-88  APT   General scratchspace bash, low workspace reordering.
+;                  Removed spurious 32 bytes of osbyte wspace
+; 14-Jan-88  APT   *type buffer in scratchspace.
+;                  MOShasFIQ byte added
+; 20-Jan-88  APT   Workspace juggling for speed & space; also discarded
+;                  Level0 stuff.
+; 28-Jan-88  APT   MetroGnome moved to "public" location for ADFS
+; 02-Feb-88  APT   FIQclaim_interlock added
+; 05-Feb-88  APT   CallBack_Vector
+; 09-Feb-88  APT   RAM for SWI despatch
+; 17-Feb-88  TMD   Added VduSaveArea, VduSaveAreaPtr, DisplayModeNo
+; 26-Feb-88  APT   NoOfCamEntries manifest
+; 03-Mar-88  APT   Shrank SVC despatch
+; 03-Mar-88  APT   NoOfCamEntries manifest doubled
+; 07-Mar-88  TMD   Added DisplayScreenStart, VduOutputCurrentState,
+;                  SpriteMaskSelect, reordered mode variables
+; 07-Mar-88  APT   Made CamEntries always at &164
+; 07-Mar-88  TMD   Added GCharSizes, GCharSizeX, GCharSizeY
+; 08-Mar-88  TMD   Added GCharSpacing, GCharSpaceX, GCharSpaceY
+; 08-Mar-88  TMD   Moved GCharSizes..GCharSpaceY into first 1K of workspace
+; 15-Mar-88  TMD   Added HLineAddr
+; 18-Mar-88  TMD   Added DisplayXWindLimit, DisplayYWindLimit,
+;                   DisplayXEigFactor, DisplayYEigFactor, PointerXEigFactor
+; 18-Mar-88  APT   Setting variables scratchspace use revised.
+; 21-Mar-88  TMD   Removed CursorIndex
+; 22-Mar-88  TMD   Added TCharSizeX,TCharSizeY,TCharSpaceX,TCharSpaceY
+; 29-Mar-88  TMD   Added GcolOraEorAddr
+; 31-Mar-88  TMD   Removed WsFontPtr
+; 07-Apr-88  SKS   Added HeapSort use of ScratchSpace
+; 14-Apr-88  TMD   Added SerialFlags
+; 28-Apr-88  TMD   Added XONXOFFChar
+;  5-May-88  APT   Added MemorySpeed
+; 18-May-88  APT   Added CannotReset sema at &107, removed pre-1.20 changes.
+; 24-May-88  TMD   Added ClipBoxEnable, ClipBoxLCol..ClipBoxTRow, moved in
+;                   ScrLoaSpriteCB, ScrLoaBuffer, SrcSavCommon
+; 24-May-88  TMD   Flood fill uses ScratchSpace
+; 01-Jun-88  TMD   Added AlignSpace for ClipBoxCoords
+; 03-Jun-88  BCSKS Make Keyboard buffer into a useful part of the system
+;                  Also PrinterBufferSize
+; 09-Jun-88  DJS   Draw uses ScratchSpace
+; 09-Jun-88  BC    Gave Econet some private debungling space
+; 11-Jun-88  SKS   Align IRQ stack to make STMFD not cross two MEMC bdy's
+;                  Made info condit'l on AsmArf; someone had commented it out!
+; 15-Jun-88  SKS   Added two more instructions in SWIDespatch area
+;                  Moved SLVK definition into kernel; it's not public
+; 16-Jun-88  SKS   Added 3 more instructions in SWIDespatch area + nailed
+;                  SvcTable address for compatibility
+; 22-Jun-88  SKS   Moved MEMC_CR_SoftCopy into pubic ws
+; 19-Jul-88  APT   Added UpCall handler stuff
+; 20-Jul-88  SKS   Added above entry
+;                  Amended comment about overlaid workspace in vdu
+; 15-Aug-88  SKS   Inserted DomainId at FF8 (set by Wimp on task swap, used by
+;                  FileSwitch to tag resources)
+; 27-Sep-89  JSR   Added ColourTrans to users of scratch space
+; 24-Oct-89  TMD   Added CamEntriesForBigMachines, CamEntriesPointer
+; 26-Oct-89  TMD   Added MaxCamEntry, removed NoOfCamEntries symbol
+; 27-Oct-89  TMD   Added VIDCClockSpeed
+; 09-Nov-89  TMD   Added ResetIndirection
+; 15-Jan-91  TMD   Added ROMModuleChain
+; 04-Feb-91  DDV   Added DeviceFS as user of ScratchSpace.
+; 04-Feb-91  DDV   Added ColourTrans use of ScratchSpace to build diff tables.
+; 06-Mar-91  TMD   Added IOSystemType
+; 07-Mar-91  LVR   ADFS uses scratch space for floppy formatting
+; 07-Mar-91  TMD   Added MonitorLeadType
+; 08-Mar-91  TMD   Added PrinterBufferAddr, PrinterBufferSize
+; 11-Apr-91  TMD   Added SerialInHandle, SerialOutHandle
+; 24-Apr-91  TMD   Added UniqueMachineID
+; 09-Jun-91  RM    Added KernelMessagesBlock,ErrorSemaphore and MOSConvertBuffer
+; 26-Jul-91  JSR   Extend GeneralMOSBuffer by 4 bytes to make it a valid
+;                       length for the default error handler's error buffer
+; 19-Aug-91  JSR   Added *If to list of GeneralMOSBuffer users
+; 22-Aug-91  TMD   Reduced ErrorSemaphore to a byte, added PortableFlag
+; 25-Aug-91  DDV   Updated to indicate correct usage of scratch space by ColourTrans
+; 09-Jan-92  DDV   Added FgPattern, BgPattern and indicate use of ScratchSpace by OS_SetColour
+; 20-Jan-92  TMD   OS_SetColour no longer uses ScratchSpace
+; 17-Feb-92  ECN   Added CLibWord and RISCOSLibWord
+; 02-Apr-92  TMD   Added ScreenBlankFlag
+; 27-Jul-92  TMD   Create Victoria specific version
+; 28-Jul-92  TMD   Moved RAMDiscAddress
+; 29-Jul-92  TMD   Moved SpriteSpaceAddress
+; 30-Jul-92  TMD   Moved FontCacheAddress
+; 31-Jul-92  TMD   Moved ScreenEndAdr from source.vdudecl, and moved actual address!
+; 03-Aug-92  TMD   Added PhysRam (moved from hdr:System)
+; 24-Aug-92  TMD   Added AbortIndirection
+; 26-Aug-92  TMD   Added PreVeneerRegDump
+; 02-Sep-92  TMD   Added FirPalAddr, SecPalAddr
+; 10-Sep-92  DDV   Added new Vdu Variables for new text expansion buffer
+; 17-Sep-92  DDV   Moved NColour into the word initialised VDU workspace
+; 17-Sep-92  DDV   Two new colour words added for text foreground and background.
+; 27-Jan-93  TMD   Moved RMA to new position
+; 29-Jan-93  TMD   Put RMA back to old position (you can't branch to above 32M!)
+; 01-Feb-93  TMD   Added PhysRamTable
+; 02-Feb-93  TMD   Added VInitSoftCopy and VEndSoftCopy
+; 03-Feb-93  TMD   Added PhysRamTableEnd
+; 04-Feb-93  TMD   Added extra slot in PhysRamTable (in case soft-loaded OS splits a bank)
+; 08-Feb-93  TMD   Added VRAMWidth variable, and extra symbols for skipped bits
+; 24-Feb-93  TMD   Changed VRAMPhysAddr to VideoPhysAddr, and split off VideoSize from VRAMSize, to allow for
+;                   DRAM-only systems
+; 05-Mar-93  TMD   Added CMOSRAMCache
+; 19-Apr-93  TMD   Added DAList, AppSpaceDANode and DANode offset symbols
+; 26-Apr-93  TMD   Added FreePoolAddress, FreePoolMaxSize, FreePoolSize
+; 29-Apr-93  TMD   Changed FontCacheAddress, SpriteSpaceAddress, RAMDiscAddress and FreePoolAddress
+;                   in order to make way for L2PT, which is moving above 64M
+; 10-May-93  TMD   Added SoftCamMapSize
+; 11-May-93  TMD   Moved SoftCamMapSize into area that's not zapped in clearing all memory routine
+; 12-May-93  TMD   Added FreePoolDANode, removed FreePoolSize
+; 20-May-93  TMD   Moved AplWorkSize into AppSpaceDANode
+; 27-May-93  TMD   Added VideoBandwidth
+; 04-Jun-93  TMD   Added CurrentMonitorType
+; 09-Jun-93  TMD   Added CamMapCorruptDebugBlock
+; 07-Jul-93  TMD   Increased FreePoolMaxSize to 64M (had to reduce RAMDiscMaxSize to 48M and
+;                   move FreePoolAddress down to do this)
+; 15-Jul-93  TMD   Added KernelModeSelector
+; 26-Jul-93  SMC   Moved DefaultIRQ1V (had to accommodate IRQs for IOMD DMA)
+; 04-Aug-93  TMD   Added L2PTSize, removed FreePoolMaxSize
+; 14-Aug-93  TMD   Removed SpriteSpaceAddress, shuffled things down
+; 16-Aug-93  TMD   Removed RAMDiscAddress, shuffled things down
+; 17-Aug-93  TMD   Removed FontCacheAddress, shuffled things down
+;                  Corrected maximum size of system heap to 2M-8K.
+;                  Added node (in bottom 32K) for system heap.
+; 25-Aug-93  SMC   Added processor vector table at ProcVec_Start
+;                  Added ProcVecPreVeneers
+; 02-Sep-93  SMC   Moved RMA to &02100000 and changed application space size to 28M.
+; 03-Sep-93  TMD   Moved InitKbdWs into SkippedTables (was at start of screen originally)
+; 07-Oct-93  TMD   Put in OldMemoryMap option so I can still use it
+; 07-Oct-93  TMD   Added ScreenBlankDPMSState, HSWRSoftCopy, VSWRSoftCopy
+; 10-Dec-93  BC    Added RawMachineID
+; 13-Dec-93  BC    Removed UniqueMachineID
+; 14-Jan-94  TMD   Added CDASemaphore
+; 18-Jan-94  TMD   Added MMUControlSoftCopy
+;
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Memory map:
+
+; Dynamic area node format
+
+                ^       0
+
+DANode_Link     #       4               ; points to next node
+DANode_Number   #       4               ; number of this area
+DANode_Base     #       4               ; base address of area (points in middle of doubly-mapped areas)
+DANode_Flags    #       4               ; various flags
+DANode_Size     #       4               ; current size of area
+DANode_MaxSize  #       4               ; maximum size of area
+DANode_Workspace #      4               ; workspace pointer when calling handlers
+DANode_Handler  #       4               ; pointer to handler routine for area
+DANode_Title    #       4               ; pointer to area title (variable length)
+DANode_NodeSize #       0
+
+; The addresses below are only temporary; eventually most of them will be allocated at run time (we hope!)
+
+ [ :DEF: OldMemoryMap
+AplWorkMaxSize      * &01000000 ; 16M
+RMAAddress          * &01800000
+RMAMaxSize          * &00400000 ; 4M
+ |
+AplWorkMaxSize      * &01C00000 ; 28M
+RMAAddress          * &02100000
+RMAMaxSize          * &00B00000 ; 11M
+ ]
+
+SysHeapChunkAddress * &01C00000
+SysHeapMaxSize      * &00200000-8*1024 ; 2M - 8K
+
+CursorChunkAddress  * &01F00000 ; Fixed size 32K
+
+ScreenEndAdr        * &05000000 ; was &02000000
+ScreenMaxSize       * 480*1024
+
+; FontCacheAddress    * &06000000 ; was &01E00000       ; now dynamic
+; FontCacheMaxSize    * &01000000 ; 16M
+
+; SpriteSpaceAddress  * &08000000 ; was &01400000       ; now dynamic
+; SpriteSpaceMaxSize  * &01000000 ; 16M
+
+; RAMDiscAddress      * &07000000 ; was &01000000       ; now dynamic
+; RAMDiscMaxSize      * &03000000 ; 48M
+
+FreePoolAddress     * &06000000 ; may still go lower!
+
+PhysRam         *     &05000000
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; system variables
+
+        ^  0,R12
+
+OSBYTEFirstVar  #  0
+
+ByteVars  #  0                 ; The main osbyte variables, accessed
+                               ; via calls &A6 to &FF
+
+VarStart  #  2                 ; &A6,&A7
+ROMPtr    #  2                 ; &A8,&A9
+ROMInfo   #  2                 ; &AA,&AB
+KBTran    #  2                 ; &AC,&AD
+VDUvars   #  2                 ; &AE,&AF
+
+CFStime   #  1                 ; &B0
+InputStream #  1               ; &B1
+KeyBdSema #  1                 ; &B2
+
+ROMPollSema #  1               ; &B3
+OSHWM     #  1                 ; &B4
+
+RS423mode #  1                 ; &B5
+NoIgnore  #  1                 ; &B6
+CFSRFS    #  1                 ; &B7
+VULAcopy  #  2                 ; &B8,&B9
+
+ROMatBRK  #  1                 ; &BA
+BASICROM  #  1                 ; &BB
+
+ADCchanel #  1                 ; &BC
+ADCmaxchn #  1                 ; &BD
+ADCconv   #  1                 ; &BE
+
+RS423use     #  1              ; &BF
+RS423conflag #  1              ; &C0
+
+FlashCount # 1                 ; &C1
+SpacPeriod # 1                 ; &C2
+MarkPeriod # 1                 ; &C3
+
+KeyRepDelay # 1                ; &C4
+KeyRepRate  # 1                ; &C5
+
+ExecFileH   # 1                ; &C6
+SpoolFileH  # 1                ; &C7
+
+ESCBREAK    # 1                ; &C8 (200)
+
+KeyBdDisable # 1               ; &C9
+KeyBdStatus  # 1               ; &CA
+
+RS423HandShake # 1             ; &CB
+RS423InputSupr # 1             ; &CC
+RS423CFSFlag   # 1             ; &CD
+
+EconetOScall # 1               ; &CE
+EconetOSrdch # 1               ; &CF
+EconetOSwrch # 1               ; &D0
+
+SpeechSupr # 1                 ; &D1
+SoundSupr # 1                  ; &D2
+
+BELLchannel # 1                ; &D3
+BELLinfo    # 1                ; &D4
+BELLfreq    # 1                ; &D5
+BELLdur     # 1                ; &D6
+
+StartMessSupr # 1              ; &D7
+
+SoftKeyLen # 1                 ; &D8
+
+PageModeLineCount # 1          ; &D9
+
+VDUqueueItems # 1              ; &DA
+
+TABch # 1                      ; &DB
+ESCch # 1                      ; &DC
+
+IPbufferCh # 4                 ; &DD,&DE,&DF,&E0
+RedKeyCh   # 4                 ; &E1,&E2,&E3,&E4
+
+ESCaction  # 1                 ; &E5
+ESCeffect  # 1                 ; &E6
+
+u6522IRQ # 1                   ; &E7
+s6850IRQ # 1                   ; &E8
+s6522IRQ # 1                   ; &E9
+
+TubeFlag # 1                   ; &EA
+
+SpeechFlag # 1                 ; &EB
+
+WrchDest # 1                   ; &EC
+CurEdit  # 1                   ; &ED
+
+SoftResetVars # 0              ; Reset to here on soft reset
+
+KeyBase # 1                    ; &EE
+Shadow # 1                     ; &EF
+Country # 1                    ; &F0
+
+UserFlag # 1                   ; &F1
+
+SerULAreg # 1                  ; &F2
+
+TimerState # 1                 ; &F3
+
+SoftKeyConsist # 1             ; &F4
+
+PrinterDrivType   # 1          ; &F5
+PrinterIgnore     # 1          ; &F6
+
+HardResetVars # 0              ; Reset to here on hard reset
+
+BREAKvector # 3                ; &F7,&F8,&F9
+
+MemDriver  # 1                 ; &FA - where the VDU drivers write to
+MemDisplay # 1                 ; &FB - where we display from
+
+LangROM # 1                    ; &FC
+
+LastBREAK # 1                  ; &FD
+
+KeyOpt # 1                     ; &FE
+
+StartOptions # 1               ; &FF
+
+PowerOnResetVars # 0           ; Reset to here on power-on reset
+
+; These two can dovetail in here to use up 2 bytes before the AlignSpace!
+
+SerialInHandle # 1              ; Handle for serial input stream  (0 if not open currently)
+SerialOutHandle # 1             ; Handle for serial output stream (-----------""----------)
+
+        AlignSpace
+
+EventSemaphores # 32            ; One byte for each of 32 events
+
+TimerAlpha # 8                  ; As used by time (bottom 5 bytes)
+TimerBeta  # 8                  ; ................................
+; both aligned to word boundaries
+
+RealTime # 8                    ; 5-byte fast real-time
+
+PrinterActive # 4               ; Handle/active flag for printer (word aligned)
+
+IntervalTimer # 5               ; Up Counter synchronous with TIME.
+; Event generated when Zero is reached
+; bottom byte aligned to word boundary
+
+SecondsTime # 1 ; the soft copy (centi-)seconds of the RTC
+CentiTime   # 1 ; """"""""""""""""""""""""""""""""""""""""
+
+FlashState # 1 ; which flash colours are we using
+
+SecondsDirty # 1                ; the dirty flag for start up!
+
+MinTick # 1                     ; the minutes odd/even state
+
+DCDDSRCopy # 1                  ; copy of ACIA bits to check for change
+
+TVVertical # 1                  ; *TV first parameter
+
+TVInterlace # 1                 ; *TV second parameter
+
+CentiCounter # 1                ; Counter for VDU CTRL timing
+
+Alphabet # 1                    ; Current alphabet number
+
+Keyboard # 1                    ; Current keyboard number
+
+KeyAlphabet # 1                 ; Alphabet associated with current keyboard
+
+                GBLS    PrinterPrefix
+PrinterPrefix   SETS    "PrinterType$"
+
+PrinterTypeName #       6 + :LEN: (PrinterPrefix)
+
+        AlignSpace
+
+SerialFlags # 4                 ; New serial flags
+
+XONXOFFChar # 1                 ; Character to send before rest (0 if none)
+
+        AlignSpace
+
+OSBYTEVarSize * @-OSBYTEFirstVar
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+; End of variables' space
+
+
+; ***********************************
+; ***  Main Vdu Driver Workspace  ***
+; ***********************************
+
+        ^ 0
+
+FgEcf  # 4 * 8  ; Foreground Ecf, set by GCOL(a,0-127)
+BgEcf  # 4 * 8  ; Background Ecf, set by GCOL(a,128-255)
+GPLFMD # 4      ; Foreground action, set by GCOL(a,0-127)
+GPLBMD # 4      ; Background action, set by GCOL(a,128-255)
+GFCOL  # 4      ; Foreground colour, set by GCOL(a,0-127)
+GBCOL  # 4      ; Background colour, set by GCOL(a,128-255)
+
+GWLCol # 4      ; Graphics window left column  --
+GWBRow # 4      ; Graphics window bottom row     |
+GWRCol # 4      ; Graphics window right column   |
+GWTRow # 4      ; Graphics window top row      --
+
+qqqPad   # 3
+QQ       # 17   ;Queue - QQ+1 is on a word boundary
+QOffset  # 4    ;Value to add to VDUqueueItems to point to next queue posn.
+JVec     # 4    ;Jump vector to internal routines
+
+; Start of MODE table workspace
+
+ScreenSize # 4  ; number of bytes needed for this mode (assumed 1st in list)
+
+XWindLimit # 4  ; Maximum value of GWRCol (internal representation)
+
+; LineLength must be immediately after YWindLimit
+
+YWindLimit # 4  ; Maximum value of GWTRow (internal representation)
+
+LineLength # 4  ; Length of one pixel row in bytes
+
+NColour # 4     ; Number of colours minus 1
+
+; End of word mode variables
+
+YShftFactor # 4 ; Number of places to shift YCoord in address generation after
+                ; multiplying by 5, holds
+                ; 7,6,5 or 4 for 8,4,2 or 1 bits per pixel (640x256 mode) or
+                ; 6,5,4 or 3 for 8,4,2 or 1 bits per pixel (320x256 mode).
+
+ModeFlags # 4   ; Bit 0 => non-graphic, Bit 1 => teletext, Bit 2 => gap mode
+
+XEigFactor # 4  ; Number of places to shift XCoord in external to internal
+                ; coordinate conversion, holds
+                ; 1 for 640x256 mode
+                ; 2 for 320x256 mode
+                ; 3 for 160x256 (BBC micro mode 2)
+
+YEigFactor # 4  ; number of shifts to convert between internal/external Y
+
+Log2BPC # 4     ; Log to base 2 of BytesPerChar ie (0,1,2,3,4)
+
+Log2BPP # 4     ; Log to base 2 of BitsPerPix ie (0,1,2,3)
+
+ECFIndex # 4    ; Index into default ECF tables
+
+ScrRCol # 4     ; Maximum column number in this screen mode
+ScrBRow # 4     ; Maximum row number in this screen mode
+
+PalIndex # 4    ; Index into palette tables (0,1,2,3)
+
+; End of table-initialised workspace
+
+; Next 3 must be together in this order !
+
+XShftFactor # 4 ; Number of places to shift XCoord in address generation,
+                ; holds 2,3,4 or 5 for 8,4,2,1 bits per pixel respectivly
+GColAdr # 4     ; Address of Ecf to plot - either FgEcf or BgEcf
+
+ScreenStart # 4         ; Start address of screen (for VDU drivers)
+
+NPix # 4        ; Number of pixels per word minus 1, holds
+                ; holds 3,7,15 or 31 for 8,4,2,1 bits per pixel modes
+
+AspectRatio # 4 ; Pixel shape : 0 square, 1 horz rect, 2 vert rect
+
+BitsPerPix # 4  ; Bits per pixel (1,2,4,8)
+
+BytesPerChar # 4        ; Bytes per one line of character
+                        ; (same as BitsPerPix except in double pixel modes)
+
+CursorFudgeFactor # 4   ; Factor for horizontal cursor positioning
+
+RowMult # 4     ; Row multiplier for text manipulation
+
+RowLength # 4   ; Bytes per text row in this mode (eg 640,1280,5120)
+
+; The following (up to and including NewPtY) must be together in this order
+; (relied upon by DefaultWindows)
+
+TWLCol # 4      ; Text window left column  --
+TWBRow # 4      ; Text window bottom row     |
+TWRCol # 4      ; Text window right column   |
+TWTRow # 4      ; Text window top row      --
+
+OrgX # 4        ; Screen origin (external representation)
+OrgY # 4
+
+GCsX # 4        ; Graphics cursor (external representation)
+GCsY # 4
+
+OlderCsX # 4    ; Very old X coordinate (internal)
+OlderCsY # 4    ; Very old Y coordinate (internal)
+
+OldCsX # 4      ; Old graphics cursor (internal representation) --
+OldCsY # 4      ;                                                 |
+                ;                                                 |
+GCsIX  # 4      ; Graphics cursor (internal representation)       |
+GCsIY  # 4      ;                                                 |
+                ;                                                 |
+NewPtX # 4      ; Newest point (internal representation)          |
+NewPtY # 4      ;                                               --
+
+; End of together block
+
+TForeCol # 4    ; Text foreground colour
+TBackCol # 4    ; Text background colour
+
+CursorX # 4     ; Text cursor X position ; these 3 must be in same order as ...
+CursorY # 4     ; Text cursor Y position
+CursorAddr # 4  ; Screen address of (output) cursor
+
+InputCursorX # 4        ; Input cursor X position ; ... these 3
+InputCursorY # 4        ; Input cursor Y position
+InputCursorAddr # 4     ; Screen address of input cursor
+
+EORtoggle # 4   ; Toggle between gap and non-gap
+RowsToDo  # 4   ; in the CLS
+
+VduStatus # 4   ; Vdu2, Window, Shadow bits (others in CursorFlags)
+
+CBWS # 8        ; Clear block (VDU 23,8..) workspace
+CBStart # 2
+CBEnd # 2
+
+CursorDesiredState # 4
+CursorStartOffset # 4
+CursorEndOffset # 4
+CursorCounter # 4
+CursorSpeed # 4
+Reg10Copy # 4
+
+CursorFill # 4  ; Word to EOR cursor ; MUST be immediately before CursorNbit
+
+CursorNbit # 4  ; Pointer to cursor code for current mode
+
+DisplayStart # 4        ; Start address of screen (for display)
+DriverBankAddr # 4      ; Default start address for VDU drivers
+DisplayBankAddr # 4     ; Default start address for display
+DisplayNColour # 4      ; No. of colours -1 for displayed mode
+DisplayModeFlags # 4    ; ModeFlags for displayed mode
+DisplayModeNo # 4       ; ModeNo for displayed mode
+DisplayScreenStart # 4  ; Where VDU outputs to when outputting to screen
+
+DisplayXWindLimit # 4   ; Used for pointer programming
+DisplayYWindLimit # 4
+DisplayXEigFactor # 4
+DisplayYEigFactor # 4
+PointerXEigFactor # 4
+
+Ecf1 # 8        ; The Ecf patterns
+Ecf2 # 8
+Ecf3 # 8
+Ecf4 # 8
+
+DotLineStyle # 8        ; Dot dash line pattern
+
+ModeNo # 4      ; Current mode number
+
+TFTint # 4      ; Text foreground tint          (in bits 6,7)
+TBTint # 4      ; Text background tint
+GFTint # 4      ; Graphics foreground tint
+GBTint # 4      ; Graphics background tint
+
+TotalScreenSize # 4     ; Amount configured for screen (in bytes)
+
+MaxMode # 4             ; Maximum mode number allowed (20 for now)
+
+VinitCopy # 4   ; Copy of Vinit for VDU 23;12 or 13
+
+CursorFlags # 4 ; Silly Master cursor movement flags
+
+CursorStack # 4 ; Bit stack of nested cursor states (0 => on, 1 => off)
+                ; (bit 31 = TOS)
+
+ECFShift # 4    ; number of bits to rotate right ECF OR and EOR masks by
+ECFYOffset # 4  ; vertical offset to ECF index
+
+WsVdu5 # 0      ; Vdu 5 workspace
+WsScr # 4
+WsEcfPtr # 4
+; WsFontPtr # 4 ; not needed any more, kept in register
+EndVerti # 4
+StartMask # 4
+EndMask # 4
+FontOffset # 4
+TempPlain # 16  ; only used for MODE 10
+
+VIDCClockSpeed # 4      ; current VIDC clock speed in kHz
+
+CurrentMonitorType # 4  ; initialised from configured one
+
+KernelModeSelector # 4  ; pointer to block in system heap where
+                        ; current mode selector is copied
+
+GraphicWs # 300 ; All graphics workspace is overlaid here
+EndGraphicWs # 0
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+        AlignSpace 16
+
+GCharSizes  # 0
+GCharSizeX  # 4         ; width of VDU 5 chars in pixels
+GCharSizeY  # 4         ; height of VDU 5 chars in pixels
+
+GCharSpacing # 0
+GCharSpaceX  # 4        ; horizontal spacing between VDU 5 chars in pixels
+GCharSpaceY  # 4        ; vertical   ------------------""-----------------
+
+TCharSizes  # 0
+TCharSizeX  # 4         ; width of VDU 4 chars in pixels
+TCharSizeY  # 4         ; height of VDU 4 chars in pixels
+
+TCharSpacing # 0
+TCharSpaceX  # 4        ; horizontal spacing between VDU 4 chars in pixels
+TCharSpaceY  # 4        ; vertical   ------------------""-----------------
+
+HLineAddr    # 4        ; address of exported HLine
+GcolOraEorAddr # 4      ; address of FgEcfOraEor etc
+
+FirPalSetting # 4*28            ; First palette settings (not used on VIDC20)
+FirPalAddr * FirPalSetting      ; Address of block for first palette setting (only used on VIDC20)
+SecPalSetting # 4*28            ; Second palette settings (not used on VIDC20)
+SecPalAddr * SecPalSetting      ; Address of block for second palette setting (only used on VIDC20)
+
+TextFgColour    # 4             ; Fg/Bg colour stored as a colour number, computed on VDU 18 and re-poked!
+TextBgColour    # 4             ;
+
+; In this brave new world there is a pointer to the text expansion
+; buffer used for VDU 4 / 5 text plotting.
+
+; This now lives in the system heap.
+
+TextExpandArea # 4      ; Pointer to Text expand area (in system heap)
+TextExpandArea_Size * (8*1024)
+
+HSWRSoftCopy    #       4       ; soft copy of h.sync width register (for DPMS)
+VSWRSoftCopy    #       4       ; soft copy of v.sync width register (for DPMS)
+
+ScreenBlankFlag # 1     ; 0 => unblanked, 1 => blanked
+ScreenBlankDPMSState # 1        ; 0 => just blank video
+                                ; 1 => blank to stand-by (hsync off)
+                                ; 2 => blank to suspend (vsync off)
+                                ; 3 => blank to off (H+V off)
+ [ AssemblingArthur
+ ! 0,"64 ":CC::STR:@
+ ]
+        AlignSpace 64
+
+FgEcfOraEor # 4*16      ; Interleaved zgora & zgeor
+BgEcfOraEor # 4*16      ; Interleaved zgora & zgeor
+BgEcfStore  # 4*16      ; Interleaved zgora & zgeor to store background
+
+;Current state of pattern
+LineDotCnt # 4          ; Count down to restarting pattern
+LineDotPatLSW # 4       ; Current state of pattern LSWord
+LineDotPatMSW # 4       ;    "      "   "     "    MSWord
+
+DotLineLength # 4       ; Dot Pattern repeat length as given in *FX163,242,n
+
+BBCcompatibleECFs # 4   ; 0 => BBC compatible, 1 => native
+
+SpAreaStart # 4         ; Start of sprite area
+SpChooseName # 16       ; No comment says Richard
+SpChoosePtr # 4
+
+PointerHeights # 4      ; 4 x 1 byte
+PointerActiveXs # 4     ; 4 x 1 byte
+PointerActiveYs # 4     ; 4 x 1 byte
+PointerShapeNumber # 4  ; only bottom byte used
+PointerX # 4            ; co-ordinates of pointer (not always = mouse)
+PointerY # 4
+
+VIDCControlCopy # 4     ; Soft copy of VIDC control register
+VertAdjust # 4          ; offset to add to vertical VIDC registers
+
+TeletextOffset # 4      ; Offset to current teletext flash bank
+
+TeletextCount # 4       ; Number of vsyncs till next teletext flash
+
+WrchNbit # 4            ; Pointer to char code for current mode
+
+BeepBlock # 8           ; OSWORD block for VDU 7
+
+ScreenMemoryClaimed # 1 ; NZ => memory has been claimed or is unusable
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+     AlignSpace 16      ; Align workspace to 16 bytes
+
+TTXDoubleCounts # 25    ; Number of double height chars on each line
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+     AlignSpace 16      ; Align workspace to 16 bytes
+
+RAMMaskTb # 32*4        ; Copy of MaskTb for this mode (up to 32 words)
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+     AlignSpace 16      ; Align workspace to 16 bytes
+
+VduOutputCurrentState # 0 ; values of R0-R3 to return from SwitchOutputToSprite
+                        ; or Mask; next 4 must be in this order
+SpriteMaskSelect # 4    ; value of R0 to be given to SWI OS_SpriteOp to set up
+                        ; current state
+VduSpriteArea # 4       ; Pointer to sprite area containing VDU output sprite
+                        ; (0 if output is to screen)
+VduSprite # 4           ; Pointer to VDU output sprite (0 if output to screen)
+
+VduSaveAreaPtr # 4      ; Pointer to save area for VDU variables
+
+
+ [ AssemblingArthur
+ ! 0,"16,12 ":CC::STR:@
+ ]
+    AlignSpace 16, 12   ; Make ClipBoxCoords a valid immediate,
+                        ; with ClipBoxEnable immediately before it
+ClipBoxInfo # 0
+ClipBoxEnable # 4       ; 0 => clip box disabled, 1 => enabled
+
+ClipBoxCoords # 0       ; Internal coords of modified area of screen
+ClipBoxLCol # 4
+ClipBoxBRow # 4
+ClipBoxRCol # 4
+ClipBoxTRow # 4
+
+FgPattern       # 4*8   ; foreground pattern as defined by OS_SetColour
+BgPattern       # 4*8   ; background pattern as defined by OS_SetColour
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+     AlignSpace 16      ; Align workspace to 16 bytes
+
+TextExpand # 4*1024     ; Tim's massive text expansion table for whizzy WRCH
+; TextPlain is now always hard against the end of TextExpand for this mode
+
+TTXSoftFonts * TextExpand + 2*1024      ; Soft fonts in teletext mode
+
+ [ AssemblingArthur
+ ! 0,"64 ":CC::STR:@
+ ]
+     AlignSpace 64      ; Align workspace to 64 bytes
+
+; Teletext map and copy/move buffer are overlaid
+
+TTXMapSize      * 41*25*4       ; (&1004 bytes)
+LargeCommon     # TTXMapSize    ; the largest area
+TTXMap          * LargeCommon
+
+ScrLoaSpriteCB  * LargeCommon   ; (size = SpriteCBsize + MaxSpritePaletteSize)
+ScrLoaBuffer    * LargeCommon   ; (size = one pixel row)
+ScrSavCommon    * LargeCommon   ; (size = SpriteAreaCBsize + SpriteCBsize
+                                ;  + MaxSpritePaletteSize)
+
+FldQueueSize    * ScratchSpaceSize
+FldQueueStart   * ScratchSpace
+
+ [ AssemblingArthur
+ ! 0,"64 ":CC::STR:@
+ ]
+     AlignSpace 64      ; Align workspace to 64 bytes
+
+Font # &700             ; 7 pages of (soft) font
+
+SaveAreaSize * 12*1024-@
+
+VduSaveArea # SaveAreaSize      ; save area for switching output to sprites
+
+VDWSSize # 0
+
+                ASSERT  VDWSSize <= 12 * 1024
+
+; *****************************************************************************
+;                 Space in the first 32K is allocated below
+; *****************************************************************************
+; Real workspace definition
+
+; Basic kernel space - defined locations for external modules
+
+                ^       &100
+IRQ1V           #       4       ; &100
+
+ESC_Status      #       1       ; &104
+LatchBSoftCopy  #       1       ; &105
+IOCControlSoftCopy #    1       ; &106
+CannotReset     #       1       ; &107
+
+IRQsema         #       4       ; &108
+MetroGnome      #       4       ; &10C
+MemorySpeed     #       4       ; &110
+
+MEMC_CR_SoftCopy #      4       ; &114
+ResetIndirection #      4       ; &118
+
+; Now all internal definitions
+
+; Up to here is initialized on reset
+
+; Next come handler variables
+
+MemLimit        #       4
+UndHan          #       4
+PAbHan          #       4
+DAbHan          #       4
+AdXHan          #       4
+
+ErrHan          #       4
+ErrBuf          #       4
+ErrHan_ws       #       4
+
+CallAd_ws       #       4     ; smart Rs ordering:
+CallAd          #       4     ; can do LDMIA of r12, pc
+CallBf          #       4
+
+BrkAd_ws        #       4
+BrkAd           #       4
+BrkBf           #       4
+
+EscHan_ws       #       4
+EscHan          #       4
+
+EvtHan_ws       #       4
+EvtHan          #       4
+
+; The next lot of workspace is in the space vacated by the small soft CAM map area
+; (256 words) which is no longer adequate, so we can reuse it
+
+JordanWS        #       0
+VInitSoftCopy   #       4       ; soft copy of VInit so we can set L bit correctly
+VEndSoftCopy    #       4       ; soft copy of VEnd  ------------""---------------
+DAList          #       4       ; Pointer to first node on dynamic area list
+
+                AlignSpace 16   ; skipped bit must start on 16-byte boundary
+
+SkippedTables   #       0
+PhysRamTable    #       0       ; 6 pairs of words (physaddr, size) indicating
+                                ; RAM present in machine (NB normally you would need at most 5
+                                ; on IOMD machines, but the extra one is if a soft-loaded ROM image
+                                ; causes a bank to split
+VideoPhysAddr   #       4       ; Address of video RAM (in the case of DRAM-only machines,
+VideoSize       #       4       ; this is actually a chunk out of DRAM)
+DRAMPhysAddrA   #       4       ; Next the DRAM - note that any banks with no memory
+DRAMSizeA       #       4       ; in them will be omitted from this table, so that
+DRAMPhysAddrB   #       4       ; eg DRAMPhysAddrA corresponds to the first bank with
+DRAMSizeB       #       4       ; DRAM in it, not necessarily bank 0
+DRAMPhysAddrC   #       4       ; If not all the slots are occupied, then
+DRAMSizeC       #       4       ; the remaining entries in this table have size fields
+DRAMPhysAddrD   #       4       ; of zero (and probably addresses of zero too)
+DRAMSizeD       #       4
+DRAMPhysAddrE   #       4
+DRAMSizeE       #       4
+PhysRamTableEnd #       0
+
+VRAMSize        #       4       ; Amount of VRAM (in bytes) (may be more than 2M)
+VRAMWidth       #       4       ; 0 => no VRAM, 1 => 32-bits wide, 2 => 64-bits wide
+VideoBandwidth  #       4       ; video bandwidth in bytes/sec
+L2PTSize        #       4       ; Amount of memory (in bytes) used for static L2PT
+                                ; - this consists of fixed size first bit, plus variable size
+                                ; bit for the free pool L2, which follows directly after it
+SoftCamMapSize  #       4       ; Amount of memory (in bytes) used for soft CAM map
+                                ; (whole number of pages)
+InitKbdWs       #       12      ; Workspace for reset keyboard IRQ code
+
+                AlignSpace 16   ; skipped bit must end on 16-byte boundary
+SkippedTablesEnd #      0
+
+CMOSRAMCache    #       240             ; Cache for CMOS RAM
+AppSpaceDANode  #       DANode_NodeSize ; Dummy area node for application space (not on list)
+FreePoolDANode  #       DANode_NodeSize ; Area node for free pool
+SysHeapDANode   #       DANode_NodeSize ; Area node for system heap
+CDASemaphore    #       4               ; Semaphore for OS_ChangeDynamicArea - non-zero => routine threaded
+MMUControlSoftCopy #    4               ; Soft copy of ARM600/700 control register
+
+AplWorkSize * AppSpaceDANode + DANode_Size
+
+ProcVec_Start           #       0       ; Start of processor vector table
+ProcVec_Branch0         #       4       ; Branch through zero
+ProcVec_UndInst         #       4       ; Undefined instruction vector
+ProcVec_SWI             #       4       ; SWI vector
+ProcVec_PrefAb          #       4       ; Prefetch abort vector
+ProcVec_DataAb          #       4       ; Data abort vector
+ProcVec_AddrEx          #       4       ; Address exception vector (not useful on ARM600/700)
+ProcVec_IRQ             #       4       ; IRQ vector
+ProcVec_End             #       0
+
+ProcVecPreVeneersSize   *       4*4     ; Space for preveneers for loading handler addresses from 0 page.
+ProcVecPreVeneers       #       ProcVecPreVeneersSize
+
+        ASSERT  @ <= &500               ; a convenient address to remember
+                #       (&500-@)
+
+CamMapCorruptDebugBlock #       &40     ; somewhere to dump registers in case of emergency
+
+        ASSERT  @ <= JordanWS+256*4
+                #       (JordanWS+256*4-@)  ; pad out to original size
+
+CamEntriesPointer #     4       ; points to where CAM soft copy is
+                                ; (CamEntries for machines up to 8MBytes,
+                                ; CamEntriesForBigMachines for larger machines)
+
+MaxCamEntry     #       4       ; maximum index into the cam map, ie
+                                ; 511 for 16MByte machines, 383 for 12MBytes
+                                ; 255 for 8MBytes, otherwise 127
+
+RAMLIMIT        #       4
+
+                #       4       ; dummy slot where AplWorkSize used to be
+
+HiServ_ws       #       4
+HiServ          #       4
+SExitA          #       4
+SExitA_ws       #       4
+UpCallHan_ws    #       4
+UpCallHan       #       4
+
+ROMModuleChain  #       4               ; pointer to head of ROM module chain
+
+; now a section that it's handy to have in simply loadable places
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+             AlignSpace 16
+
+KeyWorkSpaceSize   *  &200
+KeyWorkSpace       #  KeyWorkSpaceSize
+
+; The following were reordered on 26-Jul-91. Old ordering was:
+; GeneralMOSBuffer
+; ModuleSWI_HashTab
+; Module_List
+; Curr_Active_Object
+; VecPtrTab
+; ExceptionDump
+
+ModuleSHT_Entries  *  16
+ModuleSWI_HashTab  #  4*ModuleSHT_Entries
+
+Module_List        #  4
+Curr_Active_Object #  4
+
+; Vector Claim & Release tables etc
+
+VecPtrTab          #  NVECTORS * 4
+
+ExceptionDump      #  4
+
+; GeneralMOSBuffer: re-use with caution!
+; Here's just some of the users:
+; user                  use(s)
+; default error handler error buffer (must be 246+4 bytes big)
+; *If                   expression to be evaluated to control the *If
+;                       Command line to be submited on the expression
+;                         evaluating to non-zero (the THEN clause).
+GeneralMOSBuffer   #  256+4
+
+ [ AssemblingArthur
+ ! 0,"16 ":CC::STR:@
+ ]
+            AlignSpace  16 ; Ensures we can MOV rn, #OsbyteVars if <=&1000
+
+OsbyteVars      #       OSBYTEVarSize
+ ASSERT OsbyteVars < &10000 ; Must keep in first 64K so address can be read by
+                            ; (and stored in) OS_Bytes &A6,&A7. SKS
+
+; These must be in first 4K
+NBuffers        *       10
+BuffInPtrs      #       4 * NBuffers
+BuffOutPtrs     #       4 * NBuffers
+
+VariableList    #       4
+
+; Oscli stuff
+OscliCBtopUID   #       4
+OscliCBbotUID   #       4
+OscliCBcurrend  #       4
+
+ReturnCode      #       4
+RCLimit         #       4
+
+SpriteSize      #       4       ; saved on startup for Sprite code and RAMFS
+RAMDiscSize     #       4
+FontCacheSize   #       4       ; and font manager
+
+TickNodeChain   #       4
+
+; Workspace
+
+EnvTime            #    5
+RedirectInHandle   #    1
+RedirectOutHandle  #    1
+MOShasFIQ          #    1
+FIQclaim_interlock #    1
+CallBack_Flag      #    1
+IRQ_CallBack_Flag * CallBack_Flag
+IOSystemType    #       1       ; 0 => old I/O subsystem, 1 => IOEB+82C710 system, 2..255 => ?
+MonitorLeadType #       1       ; some function of the monitor lead inputs, as yet undetermined
+
+                  AlignSpace
+
+EnvString         #     256
+
+
+DUMPER            #     16 * 4
+
+; more system workspace
+Page_Size         #  4
+PIRQ_Chain        #  4
+PFIQasIRQ_Chain   #  4
+
+; !!!! Free space (752 bytes) left by old IRQ despatch (new IRQ despatch
+; !!!! moved as it required more space).
+OldIRQ1Vspace       # 752
+
+
+CallBack_Vector   #  4
+
+; interruptible heap manager workspace
+
+HeapSavedReg_R0     # 4
+HeapSavedReg_R1     # 4
+HeapSavedReg_R2     # 4
+HeapSavedReg_R3     # 4
+HeapSavedReg_R4     # 4
+HeapSavedReg_R13    # 4
+HeapReturnedReg_R0  # 4
+HeapReturnedReg_R1  # 4
+HeapReturnedReg_R2  # 4
+HeapReturnedReg_R3  # 4
+HeapReturnedReg_R4  # 4
+HeapReturnedReg_R13 # 4
+HeapReturnedReg_PC  # 4                 ; also acts as interlock
+
+PrinterBufferAddr   #  4                ; holds address of printer buffer
+PrinterBufferSize   #  4                ; size of printer buffer - not to be confused with PrintBuffSize
+                                        ; which is the (constant) default size for the MOS's smallish buffer
+RawMachineID        #  8                ; 64 bits for unique machine ID
+KernelMessagesBlock #  20               ; 5 Words for messagetrans message block.
+ErrorSemaphore      #  1                ; Error semaphore to avoid looping on error translation.
+PortableFlag        #  1                ; Non-zero => got an error from Portable_Speed, so don't try it again
+
+        AlignSpace
+
+MOSConvertBuffer    #  12               ; Enough romm for 8 hex digits.
+AbortIndirection    #  4                ; Pointer to list of addresses and trap routines
+PreVeneerRegDump    #  17*4             ; room for r0-r15, spsr
+
+ [ AssemblingArthur
+ ! 0, "low space free ":CC::STR:(&FEC-@)
+ ]
+ ASSERT @ < &FEC
+
+; Words for old tools of assorted varieties
+                        ^       &FEC
+; ECN 17-Feb-92
+; Added RISCOSLibWord and CLibWord. The ROM RISCOSLib and CLib must continue
+; to work even when they are killed since ROM apps are hard linked to the
+; ROM libraries. They cannot use the private word since the block pointed
+; to by this will be freed.
+RISCOSLibWord           #       4
+CLibWord                #       4
+FPEAnchor               #       4
+DomainId                #       4       ; SKS added for domain identification
+Modula2_Private         #       4       ; MICK has FFC and uses it it in USR mode
+
+VduDriverWorkSpace      #       VDWSSize
+ ASSERT (VduDriverWorkSpace :AND: 63) = 0 ; For Tim (VDU5)
+
+
+ [ AssemblingArthur
+ ! 0, "high space free ":CC::STR:(&4000-@)
+ ]
+
+                        ^       &4000
+ScratchSpaceSize        *       &4000
+ScratchSpace            #       ScratchSpaceSize
+
+ ASSERT @ <= &8000 ; Start of apl
+
+; *****************************************************************************
+; Users of ScratchSpace declare yourself here:
+
+; NRaine: Filling a polygon uses ScratchSpace to flatten the path
+
+; DSeal: Draw module uses ScratchSpace on fill operations (this supercedes
+;   NRaine's declaration above).
+
+; SKS: HeapSort with (r1 & 0x80000000) & ~(r1 & 0x20000000) & (r5 <= 16K)
+;      uses ScratchSpace as a temp slot for data shuffling after sorting
+
+; TMD: Flood fill uses ScratchSpace for the flood queue.
+
+; Tidying the RMA uses ScratchSpace while all modules are dead
+
+; GSTRANS workspace: GSINIT puts state into the workspacem and GSREAD uses it.
+; DO NOT do any operations between GSINIT/GSREAD SWIS.
+; SWIs called: OSWord in time var code
+;              BinaryToDecimal, ReadUnsigned
+
+; LVR: ADFS uses scratch space to format floppies on 1772 based machines
+
+; DDV: ColourTrans uses scratch space to build palette tables when in
+;      ColourTrans_SelecTable/RetrunColourNumber and also whilst generating
+;      stipple pattterns.
+
+GSVarWSpace             *       ScratchSpace
+
+                        ^       0
+GSNameBuff              #       &100
+GS_Stack                #       &200
+GS_StackPtr_Lim         *       &200 / 4        ; Number of words in stack.
+GS_StackPtr             #       4
+
+                        ^      @ + ScratchSpace
+
+; Pointers for SubstituteArgs: no external calls.
+; Ensure these don't overlap FileSwitch's buffers below!
+
+MacExStartPtrs          #       44
+MacExEndPtrs            #       44
+
+; OS_CLI has a buffer for alias expansion: ReadVarVal and SubstituteArgs
+;    are called while the buffer is held. Also used for module prefixes:
+;    Module called twice in this case.
+
+AliasExpansionBuffer    #       100
+
+; *list/*type need an argument expansion buffer: ReadArgs called with it.
+
+ArgumentBuffer           *       AliasExpansionBuffer
+
+; EvaluateExpression space. Calls ReadUnsigned, BinaryToDecimal and ReadVarVal.
+
+ExprWSpace              *       @
+
+                        ^       0, R12
+ExprBuff                #       &100
+exprBracDif             #       2       ; keep exprSTRACC aligned
+tos_op                  #       2       ; 1 byte for use as STRACC-1
+ExprSVCstack            #       4
+exprSTRACC              *       @ - ExprBuff + ExprWSpace
+
+ExprStackLimit          *       exprSTRACC + &100
+ExprStackStart          *       ScratchSpace + ScratchSpaceSize
+
+
+; Tutu needs some for argument substitution + expansion for run/load types
+; Only OS call during xform is XOS_SubstituteArgs and XOS_Heap(Claim,SysHeap)
+
+                        ^       0 ; Offset from ScratchSpace
+rav_substituted         #       256
+rav_arglist             #       256
+
+TopOfPageZero           #       0
+
+                        ^       &8000 ; The actual top of Page Zero
+EconetDebugSpace        |#|     &20 * 4 ; Thirty two words (&7F80)
+
+                ASSERT  @ > TopOfPageZero ; Make sure we don't clash
+
+; *****************************************************************************
+; ***            Cursor, Sound DMA, SWI, and OSCLI workspace.               ***
+; ***        Sits in the 32K above 31M, ie. &01F000000..&01F07FFF           ***
+; ***        Has the physical address &02078000, ie. 32M + 512K - 32K       ***
+; *****************************************************************************
+
+TopOfDMAPhysRAM         *       &80000            ; OFFSET in physram
+TopOfDMAWorkSpace       *       CursorChunkAddress + 32*1024
+OffsetLogicalToPhysical *       TopOfDMAPhysRAM - TopOfDMAWorkSpace
+
+                        ^       TopOfDMAWorkSpace ; Note we will be going down
+
+; Sound
+
+SoundWorkSpaceSize      *       &1000
+SoundDMABufferSize      *       &1000
+SoundEvtSize            *       &1000
+SoundDMABuffers         |#|     SoundDMABufferSize * 2
+SoundWorkSpace          |#|     SoundWorkSpaceSize + SoundEvtSize
+
+; Cursor
+
+CursorDataSize          *       &800
+CursorData              |#|     CursorDataSize
+CursorSoundRAM          *       CursorData
+CursorSoundPhysRAM      *       CursorSoundRAM + OffsetLogicalToPhysical
+
+; SWI despatcher
+
+BranchToSWIExit         |#|     4
+SvcTable                |#|     &400
+ ASSERT SvcTable = &01F033FC ; Required for SVC table pokers, 1.20 compatible
+SWIDespatch_Size        *       29*4
+SWIDespatch             |#|     SWIDespatch_Size
+
+
+; Buffers
+
+KeyBuffSize             *       &100
+RS423InBuffSize         *       &100
+RS423OutBuffSize        *       &C0
+PrintBuffSize           *       &400
+Sound0BuffSize          *       4
+Sound1BuffSize          *       4
+Sound2BuffSize          *       4
+Sound3BuffSize          *       4
+SpeechBuffSize          *       4
+MouseBuffSize           *       &40
+KeyBuff                 |#|     KeyBuffSize
+RS423InBuff             |#|     RS423InBuffSize
+RS423OutBuff            |#|     RS423OutBuffSize
+PrintBuff               |#|     PrintBuffSize
+Sound0Buff              |#|     Sound0BuffSize
+Sound1Buff              |#|     Sound1BuffSize
+Sound2Buff              |#|     Sound2BuffSize
+Sound3Buff              |#|     Sound3BuffSize
+SpeechBuff              |#|     SpeechBuffSize
+MouseBuff               |#|     MouseBuffSize
+
+; Oscli buffering
+
+OscliBuffSize           *       &100
+OscliNoBuffs            *       16
+OscliCircBuffLimit      |#|     0
+OscliCircBuffStart      |#|     OscliBuffSize * OscliNoBuffs
+RedirectBuff            |#|     OscliBuffSize
+
+; Default IRQ despatch moved here as a result of IOMD having an extra
+; 6 interrupts for I/O and sound DMA (this is really IOMD specific, not
+; ARM600/700 specific but for the moment it is assumed that they are
+; used on the same machines).
+DefIRQ1Vspace           *       12*4+12*23+2*256+64     ; for size checking in MOS
+DefaultIRQ1V            |#|     DefIRQ1Vspace
+
+ [ AssemblingArthur
+ ! 0, "Aligning IRQ stack from ":CC::STR:@
+ ]
+ [ @-7*4 :AND: 15 <> 0
+                        |#|     (@-7*4):AND:15
+ ]
+IRQSTK                  #       0       ; Overflow will give abort
+ [ AssemblingArthur
+ ! 0, "IRQ stack size ":CC::STR:(IRQSTK-CursorChunkAddress)
+ ]
+
+ ASSERT @ > ( CursorChunkAddress + &1000 ) ; Check minimum stack
+
+; *****************************************************************************
+;                        High system workspace
+; *****************************************************************************
+
+                ^       SysHeapChunkAddress
+
+                #       8*1024          ; svcstk size. Overflow will give abort
+SVCSTK          #       0
+SysHeapStart    #       0
+
+
+        OPT     OldOpt
+        END
diff --git a/hdr/PublicWS b/hdr/PublicWS
new file mode 100644
index 0000000000000000000000000000000000000000..de7f3c3d2534208c743857f9fe2f1e7395b171ab
--- /dev/null
+++ b/hdr/PublicWS
@@ -0,0 +1,98 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        SUBT    > Public Work Space
+
+OldOpt  SETA    {OPT}
+        OPT     OptNoList+OptNoP1List
+
+; ***********************************
+; ***    C h a n g e   L i s t    ***
+; ***********************************
+
+; Date       Name  Description
+; ----       ----  -----------
+; 15-Jun-94  AMcC  Created - holds values 'exported' from KernelWS
+;                  Corresponds to Values previously set in VickySpace / NewSpace
+; 03-Nov-94  AMcC  Added ScreenBlankFlag and ScreenBlankDPMSState
+;
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Memory map values: (in address order)
+
+                    ^ &00000104
+ESC_Status          #         1
+
+                    ^ &00000105
+LatchBSoftCopy      #         1
+
+                    ^ &00000107
+CannotReset         #         1
+
+                    ^ &00000108
+IRQsema             #         4
+
+                    ^ &00000114
+MEMC_CR_SoftCopy    #         4
+
+                    ^ &0000047C
+ScreenBlankFlag     #         1           ; 0 => unblanked, 1 => blanked
+
+                    ^ &0000047D
+ScreenBlankDPMSState #        1           ; 0 => just blank video
+                                          ; 1 => blank to stand-by (hsync off)
+                                          ; 2 => blank to suspend (vsync off)
+                                          ; 3 => blank to off (H+V off)
+                    ^ &00000480
+FgEcfOraEor         #      4*16           ; Interleaved zgora & zgeor (from Vdu Driver Workspace)
+
+                    ^ &000004C0
+BgEcfOraEor         #      4*16           ; Interleaved zgora & zgeor (from Vdu Driver Workspace)
+
+                    ^ &00000AE1           ; RedirectInHandle
+RedirectInHandle    #         1
+
+                    ^ &00000AE2           ; RedirectOutHandle
+RedirectOutHandle   #         1
+
+                    ^ &00000FF8
+DomainId            #         4           ; domain identification
+
+                    ^ &00001000
+VduDriverWorkSpace  #     &3000
+
+                    ^ &00004000
+ScratchSpace        #     &4000
+
+                    ^ &01C02000
+SVCSTK              #         0
+
+                    ^ &01C02000
+SysHeapStart        #         0
+
+                    ^ &01F033FC
+SvcTable            #      &400
+
+                    ^ &01F037FC
+BranchToSWIExit     #         4           ; from SWI despatcher
+
+                    ^ &01F04000
+SoundWorkSpace      #     &2000
+
+SoundDMABufferSize  *     &1000
+
+                    ^ &01F06000
+SoundDMABuffers     #     SoundDMABufferSize * 2
+
+        OPT     OldOpt
+        END
diff --git a/hdr/Variables b/hdr/Variables
new file mode 100644
index 0000000000000000000000000000000000000000..697ce82b5cbeb9436c8ea64c328d8d3263e390af
--- /dev/null
+++ b/hdr/Variables
@@ -0,0 +1,27 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Hdr.Variables
+; define variable types, as got by SWI ReadVarVal, etc.
+
+VarType_String   * 0
+VarType_Number   * 1
+VarType_Macro    * 2
+VarType_Expanded * 3   ; given to Read, this always returns a string
+                       ; given to Set,  this means evaluate an expression
+VarType_LiteralString * 4   ; Only valid for Set - sets the string as is (no
+                            ; GSTrans)
+VarType_Code     * 16
+
+       END
diff --git a/hdr/VduExt b/hdr/VduExt
new file mode 100644
index 0000000000000000000000000000000000000000..793eb48dddd4abe741cb85c5ba09b7d83f8b2870
--- /dev/null
+++ b/hdr/VduExt
@@ -0,0 +1,123 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        SUBT    VDU variable numbers => &.Hdr.VduExt
+
+OldOpt  SETA    {OPT}
+        OPT     OptNoList+OptNoP1List
+
+; ************************************************************
+; ***    C h a n g e   L i s t  (better late than never!)  ***
+; ************************************************************
+
+; Date       Name  Description
+; ----       ----  -----------
+; 27-Oct-89  TMD   Added VIDCClockSpeed
+; 05-Aug-91  DDV   Added Flag_FullPalette
+; 15-Jul-93  TMD   Added NumModeVars
+
+; Sets up external symbols of the form VduExt_<var name>
+; for use with SWI OS_ReadVDUVariables
+
+        MACRO
+        NotRVVTBarWobblyBits    $var, $base
+        [ "$base"<>""
+NotRVVTBarWobblyBitscounter SETA $base
+        ]
+VduExt_$var                     *       NotRVVTBarWobblyBitscounter
+NotRVVTBarWobblyBitscounter SETA NotRVVTBarWobblyBitscounter +1
+        MEND
+
+                                GBLA   NotRVVTBarWobblyBitscounter
+NotRVVTBarWobblyBitscounter     SETA    0
+
+        NotRVVTBarWobblyBits    ModeFlags, 0
+        NotRVVTBarWobblyBits    ScrRCol
+        NotRVVTBarWobblyBits    ScrBRow
+        NotRVVTBarWobblyBits    NColour
+        NotRVVTBarWobblyBits    XEigFactor
+        NotRVVTBarWobblyBits    YEigFactor
+        NotRVVTBarWobblyBits    LineLength
+        NotRVVTBarWobblyBits    ScreenSize
+        NotRVVTBarWobblyBits    YShftFactor
+        NotRVVTBarWobblyBits    Log2BPP
+        NotRVVTBarWobblyBits    Log2BPC
+        NotRVVTBarWobblyBits    XWindLimit
+        NotRVVTBarWobblyBits    YWindLimit
+
+NumModeVars * NotRVVTBarWobblyBitscounter
+
+        NotRVVTBarWobblyBits    GWLCol, &80
+        NotRVVTBarWobblyBits    GWBRow
+        NotRVVTBarWobblyBits    GWRCol
+        NotRVVTBarWobblyBits    GWTRow
+        NotRVVTBarWobblyBits    TWLCol
+        NotRVVTBarWobblyBits    TWBRow
+        NotRVVTBarWobblyBits    TWRCol
+        NotRVVTBarWobblyBits    TWTRow
+        NotRVVTBarWobblyBits    OrgX
+        NotRVVTBarWobblyBits    OrgY
+        NotRVVTBarWobblyBits    GCsX
+        NotRVVTBarWobblyBits    GCsY
+        NotRVVTBarWobblyBits    OlderCsX
+        NotRVVTBarWobblyBits    OlderCsY
+        NotRVVTBarWobblyBits    OldCsX
+        NotRVVTBarWobblyBits    OldCsY
+        NotRVVTBarWobblyBits    GCsIX
+        NotRVVTBarWobblyBits    GCsIY
+        NotRVVTBarWobblyBits    NewPtX
+        NotRVVTBarWobblyBits    NewPtY
+        NotRVVTBarWobblyBits    ScreenStart
+        NotRVVTBarWobblyBits    DisplayStart
+        NotRVVTBarWobblyBits    TotalScreenSize
+        NotRVVTBarWobblyBits    GPLFMD
+        NotRVVTBarWobblyBits    GPLBMD
+        NotRVVTBarWobblyBits    GFCOL
+        NotRVVTBarWobblyBits    GBCOL
+        NotRVVTBarWobblyBits    TForeCol
+        NotRVVTBarWobblyBits    TBackCol
+        NotRVVTBarWobblyBits    GFTint
+        NotRVVTBarWobblyBits    GBTint
+        NotRVVTBarWobblyBits    TFTint
+        NotRVVTBarWobblyBits    TBTint
+        NotRVVTBarWobblyBits    MaxMode
+        NotRVVTBarWobblyBits    GCharSizeX
+        NotRVVTBarWobblyBits    GCharSizeY
+        NotRVVTBarWobblyBits    GCharSpaceX
+        NotRVVTBarWobblyBits    GCharSpaceY
+        NotRVVTBarWobblyBits    HLineAddr
+        NotRVVTBarWobblyBits    TCharSizeX
+        NotRVVTBarWobblyBits    TCharSizeY
+        NotRVVTBarWobblyBits    TCharSpaceX
+        NotRVVTBarWobblyBits    TCharSpaceY
+        NotRVVTBarWobblyBits    GcolOraEorAddr
+        NotRVVTBarWobblyBits    VIDCClockSpeed
+
+        NotRVVTBarWobblyBits    WindowWidth, &100
+        NotRVVTBarWobblyBits    WindowHeight
+
+; Bits in ModeFlags
+
+Flag_NonGraphic * 1
+Flag_Teletext   * 2
+Flag_GapMode    * 4
+Flag_BBCGapMode * 8
+Flag_HiResMono  * 16
+Flag_DoubleVertical * 32
+Flag_HardScrollDisabled * 64    ; set when outputting to a sprite
+Flag_FullPalette * 128          ; set when palette is not brain damaged
+
+        OPT     OldOpt
+
+        END
diff --git a/s/ARM600 b/s/ARM600
new file mode 100644
index 0000000000000000000000000000000000000000..255fec8d6bcadda5993d703322d6fee109d0cb84
--- /dev/null
+++ b/s/ARM600
@@ -0,0 +1,2646 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > ARM600
+
+        GBLL    DebugAborts
+DebugAborts SETL {FALSE}
+
+ [ Simulator
+        ! 0, "**** Warning - IOMD Simulator debugging included - will crash on real thing! ****"
+ ]
+
+; MMU interface file - ARM600 version
+
+; Created by TMD 15-Jul-92
+; Comments updated by TMD 04-Aug-93
+
+; Workspace needed for ARM600 work is as follows:
+;
+; * Level 2 page tables for a contiguous logical area starting at zero
+;     This consists of:
+;       a) a fixed size bit covering 0 to 192M (currently)
+;       b) a variable size bit covering the free pool - 192M to 192M + (memsize rounded up to 4M)
+;     Note that the 192M value is sufficient to cover all the fixed size areas at present.
+;     As more areas switch to new world, this limit will come down and down, but free pool must always
+;      start at the end of the fixed areas.
+;     (Level 2 for areas outside this region are allocated dynamically afterwards)
+;
+; * Level 1 page table (16K, stored in the middle of L2PT, where the I/O + ROM would be if it wasn't section mapped)
+;
+; * Undefined32 mode stack (8K)
+;
+; * Soft CAM map (variable size = memsize/4K*8, rounded up to 4K)
+;
+; In order to make the memory models for MEMC1 and IOMD harmonious, the MEMC1 system is considered as a section of
+; video RAM starting at &02000000 size 480K, and an area of "non-video RAM" starting at &02078000, size (totalRAM-480K)
+; IOMD has 1 area of video RAM and up to 4 areas of non-video RAM.
+;
+; (Note: when OS is soft-loaded, a 2 Mbyte chunk of DRAM is removed from the RAM map, therefore the model allows for
+;  1 area of video RAM and up to 5 areas of non-video RAM)
+;
+; The fixed system pages (which include those described above) start at the base of the first bank of non-video RAM
+; (on IOMD we allow this to be in any of the 4 RAM sites, ie you don't have to have RAM in any particular SIMM site)
+; Consequently the base of the fixed system pages is not known at assembly time, so has to be passed in a register
+; to the generic code.
+
+; Fixed page allocation is as follows
+
+        ^       0
+DRAMOffset_CursorChunk  #       32*1024         ; ie on MEMC1 this is the last 32K of DAG-addressable memory
+DRAMOffset_PageZero     #       32*1024         ; 32K at location zero
+DRAMOffset_SystemHeap   #       32*1024         ; system heap/svc stack
+DRAMOffset_L2PT         #       0               ; static L2PT (variable size, with embedded L1PT)
+DRAMOffset_L1PT         *       DRAMOffset_L2PT + 48*1024
+
+; Undefined stack memory (size 8K) starts immediately after end of L2PT (which is variable size)
+; Soft CAM map (variable size) starts immediately after end of UndStack
+
+StaticPagesSize         *       @
+
+; Logical addresses are as follows
+
+L2PT                    *       &02C00000       ; size 256K
+L1PT                    *       &02C0C000       ; in the middle of L2PT, where the mapping for 03000000 to 03FFFFFF would be
+
+FixedAreasL2Size        *       96*1024         ; amount of L2 to cover fixed areas, excluding free pool
+
+UndStackSoftCamChunk    *       &01E00000
+UndStackSize            *       8*1024
+CamEntriesForVicky      *       UndStackSoftCamChunk + &2000
+UNDSTK                  *       CamEntriesForVicky ; points to end of stack
+PhysSpace               *       &80000000       ; Map of MEMC/IOMD physical space (64M/512M in size)
+
+ [ LateAborts
+DefaultMMUControlRegister *     MMUC_M + MMUC_C + MMUC_W + MMUC_P + MMUC_D + MMUC_L + MMUC_F
+ |
+DefaultMMUControlRegister *     MMUC_M + MMUC_C + MMUC_W + MMUC_P + MMUC_D + MMUC_F
+ ]
+
+OneMByte                EQU     (1024*1024)
+SixteenMByte            EQU     (1024*1024 * 16)
+
+ [ MEMC_Type = "IOMD"
+; *****************************************************************************
+;
+;       SetDAG - Program DMA address generator R1 with physical address R0
+;       NB on IOMD this is the true physical address, not just offset into VRAM or DRAM
+;
+; in:   r0 = physical address
+;       r1 = index of DMA address generator to program, as defined in vdudecl
+;
+; out:  All registers preserved, operation ignored if illegal
+;
+
+SetDAG  ENTRY   "r0,r12"
+        MOV     r12, #IOMD_Base
+        CMP     r1, #1
+        BEQ     %FT10
+        BHI     %FT20
+
+; Program VInit
+
+00
+        ASSERT  MEMCDAG_VInit = 0
+        MOV     r14, #0
+        STR     r0, [r14, #VInitSoftCopy]       ; save VInit so that writes to VEnd can check
+        LDR     r14, [r14, #VEndSoftCopy]
+        CMP     r0, r14                         ; if VInit >= VEnd then set L bit
+        ORRCS   r0, r0, #IOMD_DMA_L_Bit
+        STR     r0, [r12, #IOMD_VIDINIT]
+        EXIT
+
+; Program VStart
+
+10
+        ASSERT  MEMCDAG_VStart = 1
+        STR     r0, [r12, #IOMD_VIDSTART]
+        EXIT
+
+20
+        CMP     r1, #3
+        EXIT    HI
+        BEQ     %FT30
+
+; Program VEnd
+
+        ASSERT  MEMCDAG_VEnd = 2
+        MOV     r14, #0
+        STR     r0, [r14, #VEndSoftCopy]        ; remember old VEnd value
+        LDR     r14, [r14, #VInitSoftCopy]      ; load old VInit
+        CMP     r14, r0                         ; if VInit >= VEnd
+        ORRCS   r14, r14, #IOMD_DMA_L_Bit       ; then set L bit
+        STR     r14, [r12, #IOMD_VIDINIT]       ; store VInit
+        STR     r0, [r12, #IOMD_VIDEND]         ; and VEnd
+        EXIT
+
+; Program CInit
+
+30
+        ASSERT  MEMCDAG_CInit = 3
+        STR     r0, [r12, #IOMD_CURSINIT]
+        EXIT
+
+ |
+
+; DMA address generators - still controlled by MEMC1 currently
+
+VInit   * &03600000
+VStart  * &03620000
+VEnd    * &03640000
+CInit   * &03660000
+
+; *****************************************************************************
+;
+;       SetDAG - Program DMA address generator R1 with physical address R0
+;
+; in:   r0 = physical address offset from 32M
+;       r1 = index of DMA address generator to program, as defined in vdudecl
+;
+; out:  All registers preserved, operation ignored if illegal
+;
+
+SetDAG  ENTRY   "r0"
+        CMP     r1, #MEMCDAG_MaxReason
+        EXIT    HI
+        ADR     r14, DAGAddressTable
+        LDR     r14, [r14, r1, LSL #2]          ; load base address in MEMC1
+        MOV     r0, r0, LSR #4                  ; bottom 4 bits irrelevant
+        CMP     r0, #(1 :SHL: 15)               ; ensure in range
+        ORRCC   r14, r14, r0, LSL #2
+        STRCC   r14, [r14]                      ; any old data will do
+        EXIT
+
+        GBLA    DAGIndex
+DAGIndex SETA   0
+
+        MACRO
+        DAGTab  $reason, $address
+        ASSERT  ($reason)=DAGIndex
+        &       $address
+DAGIndex SETA   DAGIndex + 1
+        MEND
+
+DAGAddressTable
+        DAGTab  MEMCDAG_VInit, VInit
+        DAGTab  MEMCDAG_VStart, VStart
+        DAGTab  MEMCDAG_VEnd, VEnd
+        DAGTab  MEMCDAG_CInit, CInit
+ ]
+
+
+; **************** CAM manipulation utility routines ***********************************
+
+; **************************************************************************************
+;
+;       BangCamUpdate - Update CAM entry and soft copy
+;
+; This part of the routine has to do more work on ARM600
+;
+; First look in the CamEntries table to find the logical address L this physical page is
+; currently allocated to. Then check in the Level 2 page tables to see if page L is currently
+; at page R2. If it is, then map page L to be inaccessible, otherwise leave page L alone.
+; Then map logical page R3 to physical page R2.
+;
+; in:   r2 = physical page number
+;       r3 = logical address (2nd copy if doubly mapped area)
+;       r9 = offset from 1st to 2nd copy of doubly mapped area (either source or dest, but not both)
+;       r11 = PPL + CB bits
+;
+; out:  r0, r1, r4, r6 corrupted
+;       r2, r3, r5, r7-r12 preserved
+;
+; NB Use of stack is allowed in this routine
+
+BangCamUpdate ROUT
+        TST     r11, #DynAreaFlags_DoublyMapped ; if moving page to doubly mapped area
+        SUBNE   r3, r3, r9                      ; then CAM soft copy holds ptr to 1st copy
+
+        MOV     r1, #0
+        LDR     r1, [r1, #CamEntriesPointer]
+        ADD     r1, r1, r2, LSL #3              ; point at cam entry (logaddr, PPL)
+        LDMIA   r1, {r0, r6}                    ; r0 = current logaddress, r6 = current PPL
+        STMIA   r1, {r3, r11}                   ; store new address, PPL
+        Push    "r0, r6"                        ; save old logical address, PPL
+        MOV     r1, #PhysRamTable               ; go through phys RAM table
+        MOV     r6, r2                          ; make copy of r2 (since that must be preserved)
+10
+        LDMIA   r1!, {r0, r4}                   ; load next address, size
+        SUBS    r6, r6, r4, LSR #12             ; subtract off that many pages
+        BCS     %BT10                           ; if more than that, go onto next bank
+
+        ADD     r6, r6, r4, LSR #12             ; put back the ones which were too many
+        ADD     r0, r0, r6, LSL #12             ; move on address by the number of pages left
+        LDMFD   r13, {r6}                       ; reload old logical address
+
+; now we have r6 = old logical address, r2 = physical page number, r0 = physical address
+
+        TEQ     r6, r3                          ; TMD 19-Jan-94: if old logaddr = new logaddr, then
+        BEQ     %FT20                           ; don't remove page from where it is, to avoid window
+                                                ; where page is nowhere.
+        LDR     r1, =L2PT
+        ADD     r6, r1, r6, LSR #10             ; r6 -> L2PT entry for old log.addr
+        MOV     r4, r6, LSR #12                 ; r4 = word offset into L2 for address r6
+        LDR     r4, [r1, r4, LSL #2]            ; r4 = L2PT entry for L2PT entry for old log.addr
+        TST     r4, #3                          ; if page not there
+        BEQ     %FT20                           ; then no point in trying to remove it
+
+        LDR     r4, [r6]                        ; r4 = L2PT entry for old log.addr
+        MOV     r4, r4, LSR #12                 ; r4 = physical address for old log.addr
+        TEQ     r4, r0, LSR #12                 ; if equal to physical address of page being moved
+        BNE     %FT20                           ; if not there, then just put in new page
+
+        Push    "r0, r3, r11, r14"              ; save phys.addr, new log.addr, new PPL, lr
+        ADD     r3, sp, #4*4
+        LDMIA   r3, {r3, r11}                   ; reload old logical address, old PPL
+        MOV     r0, #0                          ; cause translation fault
+        BL      BangL2PT                        ; map page out
+        Pull    "r0, r3, r11, r14"
+20
+        ADD     sp, sp, #8                      ; junk old logical address, PPL
+        B       BangCamAltEntry                 ; and branch into BangCam code
+
+; **************************************************************************************
+;
+;       BangCam - Update CAM entry, but not soft copy
+;
+; This routine maps a physical page to a given logical address
+; For ARM600, I assume that the physical page was previously not mapped
+; anywhere else - on MEMC1 it would automatically unmap any logical
+; address that the physical page was previously at, but on ARM600 it won't
+;
+; in:   r2 = physical page number
+;       r3 = logical address (2nd copy if doubly mapped)
+;       r9 = offset from 1st to 2nd copy of doubly mapped area (either source or dest, but not both)
+;       r11 = PPL
+;
+; out:  r0, r1, r4, r6 corrupted
+;       r2, r3, r5, r7-r12 preserved
+;
+; NB Can't use stack - there might not be one!
+;
+; NB Also - the physical page number MUST be in range.
+
+; This routine must work in 32-bit mode
+
+        GBLL    UsePPLCBBits
+UsePPLCBBits    SETL    {TRUE}
+
+BangCam ROUT
+        TST     r11, #DynAreaFlags_DoublyMapped ; if area doubly mapped
+        SUBNE   r3, r3, r9              ; then move ptr to 1st copy
+
+        MOV     r1, #PhysRamTable       ; go through phys RAM table
+        MOV     r6, r2                  ; make copy of r2 (since that must be preserved)
+10
+        LDMIA   r1!, {r0, r4}           ; load next address, size
+        SUBS    r6, r6, r4, LSR #12     ; subtract off that many pages
+        BCS     %BT10                   ; if more than that, go onto next bank
+
+        ADD     r6, r6, r4, LSR #12     ; put back the ones which were too many
+        ADD     r0, r0, r6, LSL #12     ; move on address by the number of pages left
+BangCamAltEntry
+        ADR     r1, PPLTrans
+        AND     r4, r11, #3             ; first use PPL bits
+        LDR     r1, [r1, r4, LSL #2]    ; get PPL bits and SmallPage indicator
+ [ UsePPLCBBits
+        TST     r11, #DynAreaFlags_NotCacheable
+        TSTEQ   r11, #PageFlags_TempUncacheableBits
+        ORREQ   r1, r1, #L2_C           ; if cacheable (area bit CLEAR + temp count zero), then OR in C bit
+        TST     r11, #DynAreaFlags_NotBufferable
+        ORREQ   r1, r1, #L2_B           ; if bufferable (area bit CLEAR), then OR in B bit
+ ]
+        ORR     r0, r0, r1
+
+        LDR     r1, =L2PT               ; point to level 2 page tables
+
+BangL2PT                                ; internal entry point used only by BangCamUpdate
+        BICS    r4, r3, #(3 :SHL: 10)   ; ensure going to be on word boundary
+ [ {FALSE}      ; this breaks too many things at the moment
+        BICEQ   r0, r0, #&30            ; if logical page zero, then make 1st 1K no user access
+        ORREQ   r0, r0, #&10
+ ]
+ [ :LNOT: UsePPLCBBits
+        LDR     r6, [r1, r4, LSR #10]   ; read current contents
+        AND     r6, r6, #L2_C :OR: L2_B ; preserve old CB bits (set up by soft loader)
+        ORR     r0, r0, r6              ; but OR in new address and PPL bits
+ ]
+        STR     r0, [r1, r4, LSR #10]!  ; update level 2 page table (and update pointer so we can use bank-to-bank offset
+        TST     r11, #DynAreaFlags_DoublyMapped ; if area doubly mapped
+        STRNE   r0, [r1, r9, LSR #10]   ; then store entry for 2nd copy as well
+        ADDNE   r3, r3, r9              ; and point logical address back at 2nd copy
+
+        SetCop  r0, CR_IDCFlush         ; flush cache
+        SetCop  r0, CR_TLBFlush         ; and TLB
+        MOV     pc, lr
+
+PPLTrans
+        &       (AP_Full * L2_APMult) + L2_SmallPage      ; R any W any
+        &       (AP_Read * L2_APMult) + L2_SmallPage      ; R any W sup
+        &       (AP_None * L2_APMult) + L2_SmallPage      ; R sup W sup
+        &       (AP_None * L2_APMult) + L2_SmallPage      ; R sup W sup
+
+PageSizes
+        &       4*1024                  ; 0 is 4K
+        &       8*1024                  ; 4 is 8K
+        &       16*1024                 ; 8 is 16
+        &       32*1024                 ; C is 32
+
+PageShifts
+        =       12, 13, 0, 14           ; 1 2 3 4
+        =       0,  0,  0, 15           ; 5 6 7 8
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_UpdateMEMC: Read/write MEMC1 control register
+
+SSETMEMC ROUT
+
+        AND     r10, r0, r1
+        MOV     r12, #0
+        TEQP    pc, #SVC_mode+I_bit+F_bit
+        LDR     r0, [r12, #MEMC_CR_SoftCopy] ; return old value
+        BIC     r11, r0, r1
+        ORR     r11, r11, R10
+        BIC     r11, r11, #&FF000000
+        BIC     r11, r11, #&00F00000
+        ORR     r11, r11, #MEMCADR
+        STR     r11, [r12, #MEMC_CR_SoftCopy]
+
+; We now have to mimic the relevant bits of the MEMC1 control register
+;
+; bits 0,1 => unused
+; bits 2,3 => page size, irrelevant since always 4K
+; bits 4,5 => low ROM access time (mostly irrelevant but set it up anyway)
+; bits 6,7 => hi  ROM access time (definitely irrelevant but set it up anyway)
+; bits 8,9 => DRAM refresh control
+; bit 10   => Video/cursor DMA enable
+; bit 11   => Sound DMA enable
+; bit 12   => OS mode
+
+ [ MEMC_Type = "IOMD"
+        MOV     r12, #IOMD_Base
+        TST     r11, #1 :SHL: 10   ; see if video DMA wants to be enabled
+        LDRB    r11, [r12, #IOMD_VIDCR]
+        AND     r11, r11, #(&7F :AND: :NOT: IOMD_VIDCR_Enable)  ; knock out bit 7 and video DMA enable bit
+        ORRNE   r11, r11, #IOMD_VIDCR_Enable
+        STRB    r11, [r12, #IOMD_VIDCR]
+ |
+
+; Just change the page size to the real MEMC page size and leave other bits alone
+
+        ORR     r11, r11, #&0C          ; force 32K page size
+        STR     r11, [r11]              ; any old data will do
+ ]
+
+        TEQP    pc, #SVC_mode+I_bit
+        ExitSWIHandler
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       ClearPhysRAM - Routine to clear "all" memory
+;
+; While this routine is running, keyboard IRQs may happen. For this reason
+; it avoids LogRAM 0..31 (where hardware IRQ vector is) and PhysRAM
+; 0..31 where the IRQ workspace is.
+;
+; We also have to avoid the L2PT (inc L1PT) and the PhysRamTable.
+; The latter is also used to tell us which areas of memory we should clear.
+
+; We don't have to worry about trampling on the ROM image as it's
+; already been excluded from PhysRamTable.
+
+; This routine must work in 32-bit mode.
+
+; in:   r7 = memory speed
+;       r8 = page size
+;       r9 = MEMC control register
+;       r13 = total RAM size
+;
+; None of the above are actually used by this routine
+;
+; out:  r7-r9, r13 preserved
+;
+
+ClearPhysRAM ROUT
+        MOV     r0, #0
+        MOV     r1, #0
+        MOV     r2, #0
+        MOV     r3, #0
+        LDR     r12, =PhysRamTable                      ; point to 5 lots of (physaddr,size)
+        ADR     r6, RamSkipTable
+        ADD     r4, r12, #PhysRamTableEnd-PhysRamTable  ; r4 -> end of table
+10
+        LDR     r5, [r6], #4                            ; load first skip offset
+
+        LDMIA   r12!, {r10, r11}                        ; load next address, size
+
+        ORR     r10, r10, #PhysSpace                    ; point to logical representation of physical space
+        ADD     r11, r11, r10                           ; r11 -> end address of this area
+15
+        ADD     r5, r5, r10                             ; r5 -> skip address if any
+20
+        TEQ     r10, r11                                ; test for end of this area?
+        BEQ     %FT30
+        TEQ     r10, r5                                 ; test for the start of a skipped region
+        STMNEIA r10!, {r0-r3}
+        BNE     %BT20
+
+        LDR     r5, [r6], #4                            ; load skip amount
+        CMP     r5, #0                                  ; if negative, then it's an offset from start of skipped bit
+        LDRLT   r5, [r10, r5]                           ; to address of word holding skip amount
+        ADD     r10, r10, r5                            ; and skip it
+        LDR     r5, [r6], #4                            ; load next skip offset (NB relative to end of last skip)
+        B       %BT15
+
+30
+        TEQ     r12, r4                                 ; have we done all areas?
+        BNE     %BT10
+
+        LDR     r0, =OsbyteVars + :INDEX: LastBREAK
+        MOV     r1, #&80
+        STRB    r1, [r0]                                ; flag the fact that RAM cleared
+
+        MOV     r0, #0
+        LDR     r1, =DefaultMMUControlRegister          ; set up MMU soft copy
+        STR     r1, [r0, #MMUControlSoftCopy]
+
+        MOV     pc, lr
+
+        GBLA    lastaddr
+lastaddr SETA   0
+        GBLA    lastregion
+lastregion SETA 0
+
+        MACRO
+        MakeSkipTable $region, $addr, $size
+ [ ($region)<>lastregion
+        &       -1
+lastaddr SETA   0
+ ]
+        &       ($addr)-lastaddr, $size
+lastaddr SETA   ($addr)+($size)
+lastregion SETA $region
+        MEND
+
+        MACRO
+        EndSkipTables
+        WHILE   lastregion < (PhysRamTableEnd-PhysRamTable)/8
+        &       -1
+lastregion SETA   lastregion +1
+        WEND
+        MEND
+
+; Note (TMD 04-Aug-93): Special bodge put in here to allow variable size skip for L2PT.
+; If skip size field is negative, then it's an offset from the start of this skipped bit to a word holding
+; the size of the skip. This relies on the L2PTSize being in page zero, which is at a lower physical address than
+; the L2 itself. Also assumes that there are no more skips in the 1st DRAM chunk after the L2PT, since the offset
+; to the next skip is relative to the end of the previous one, which isn't known at assembly time!
+
+; Tim says "Yuk, yuk, yuk!!"
+
+RamSkipTable
+ [ :LNOT: NewStyle_Screen
+        MakeSkipTable   0, 0, 32                        ; in video RAM skip 1st 32 bytes (kbd workspace)
+ ]
+        MakeSkipTable   1, DRAMOffset_PageZero + 0, 32  ; skip 1st 32 bytes of LogRAM, so IRQs work!
+        MakeSkipTable   1, DRAMOffset_PageZero + SkippedTables, SkippedTablesEnd-SkippedTables
+        MakeSkipTable   1, DRAMOffset_L2PT, DRAMOffset_PageZero + L2PTSize - DRAMOffset_L2PT
+        EndSkipTables
+
+        ASSERT  DRAMOffset_PageZero + L2PTSize < DRAMOffset_L2PT
+
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       InitMEMC - Initialise memory controller
+;
+; in:   If ResetIndirection assembly flag set, then
+;         r1 = 0 if reset, 1 if break
+;       else
+;         r1 undefined
+;       endif
+
+InitMEMC ROUT
+ [ MEMC_Type = "IOMD"
+
+; Note: On IOMD, all accesses go to ROM until the first write cycle.
+
+        MOV     r12, #IOMD_Base
+
+ [ MorrisSupport
+; Perform a dummy write to IOMD (some harmless register) to get it out of ROM force mode.
+; Reads from IOMD will return garbage before this has happened. If we're actually running out
+; of 32-bit wide ROMs on MORRIS, a write will already have happened, to get ROMCR0 from
+; 16 to 32-bit wide mode, but we can't yet determine for sure (by reading it back), so do it
+; anyway.
+
+        STRB    r12, [r12, #IOMD_DMAREQ]              ; writes to DMAREQ are ignored
+
+        LDRB    r0, [r12, #IOMD_ID0]
+        CMP     r0, #&98
+        LDRB    r0, [r12, #IOMD_ID1]
+        CMPEQ   r0, #&5B
+       ;MOVEQ   r3, #xxxx
+        BNE     MedusaInit                            ; NOT MORRIS assume Medusa hardware
+;
+; MORRIS contains IOMD equivalant circuitry. Due to lack of VRAM, presence of 16/32 bit support
+; and a different ROM speed register, we program it slightly differently.
+;
+
+;
+; PSwindell wants all prescalers set to divide by 1
+;
+        MOV     r0, #IOMD_CLKCTL_CpuclkNormal + IOMD_CLKCTL_MemclkNormal + IOMD_CLKCTL_IOclkNormal
+        STRB    r0, [r12, #IOMD_CLKCTL] ; initialise all prescalers to div1
+
+;
+; Set ROM speed, take care to preserve 16-bit mode bit...
+;
+; According to RJKing on 6/5/94, Kryten will use burst mode roms: use 93nS burst, 156nS initial.
+;
+; We assume that the extension ROMs are the same access time and width as the main OS ROMS.
+;
+        LDRB    r0, [r12, #IOMD_ROMCR0]
+        AND     r0, r0, #&40            ; clear all but 16-bit mode bit
+ [ NormalSpeedROMS
+   ;Normal code
+        ORR     r0, r0, #IOMD_ROMCR_Normal + IOMD_ROMCR_156 + IOMD_ROMCR_Burst93
+                                                                ; initialise ROM speed to 156.25nS, 93.75nS burst
+        ; the fast EPROMS used for Kryten testing should be able to cope even though they aren't
+        ; burst devices
+ |
+   ;Slow ROM access for PSwindells test EPROMS. Paul requested 156nS (or slower), burst off.
+        ORR     r0, r0, #IOMD_ROMCR_Normal + IOMD_ROMCR_187 + IOMD_ROMCR_BurstOff
+
+        ! 0, "*** WARNING *** Slow ROM version ment for PSwindell"
+ ]
+        STRB    r0, [r12, #IOMD_ROMCR0]
+        STRB    r0, [r12, #IOMD_ROMCR1]         ; and do the same for extension ROMs (just in case)
+;
+; MORRIS doesn't support VRAM. Kryten has same DRAM speed as Medusa
+;
+        MOV     r0, #IOMD_VREFCR_REF_16                         ; select 16µs refresh
+        STRB    r0, [r12, #IOMD_VREFCR]
+
+        MOV     r0, #IOMD_IOTCR_Network_TypeA :OR: IOMD_IOTCR_Combo_TypeB :OR: IOMD_IOTCR_Sound_TypeB :OR: IOMD_IOTCR_Sound_Word
+        STRB    r0, [r12, #IOMD_IOTCR]
+
+        MOV     r0, #0                          ; Podule manager wants TypeA setting by default for all podules
+        STRB    r0, [r12, #IOMD_ECTCR]
+
+ [ Select16BitSound
+; All MORRIS based machines have 16bit 'Japanese' format sound DAC's
+        MOV     r0, #2_10
+        STRB    r0, [r12, #IOMD_VIDMUX]
+ ]
+        B       CommonInit
+
+MedusaInit
+ ]
+
+ [ Simulator
+        MOV     r0, #IOMD_ROMCR_62 + IOMD_ROMCR_BurstOff        ; make faster for simulation (no point in burst mode, it's
+                                                                ; no faster than the fastest initial speed)
+ |
+  [ RISCPCBurstMode
+   [ 1 = 1
+        ReadCop r0, CR_ID
+        BIC     r0, r0, #&F     ;ignore 4 bit revision field
+        LDR     r2, =&41007100                                  ;Test for early 710's
+        CMP     r0, r2                                          ;
+        MOVEQ   r0, #IOMD_ROMCR_156 + IOMD_ROMCR_BurstOff       ;cos they can't work in burst mode!
+        MOVNE   r0, #IOMD_ROMCR_156 + IOMD_ROMCR_Burst93        ;610's 710A's and beyond can
+        ! 0, "*** WARNING *** Burst mode enabled on RISC PC iff processor can cope"
+   |
+        MOV     r0, #IOMD_ROMCR_156 + IOMD_ROMCR_Burst93
+        ! 0, "*** WARNING *** Burst mode enabled on RISC PC"
+   ]
+  |
+        MOV     r0, #IOMD_ROMCR_156 + IOMD_ROMCR_BurstOff       ; initialise ROM speed to 156.25ns (changed from 187ns 21-Jan-94)
+  ]
+ ]
+        STRB    r0, [r12, #IOMD_ROMCR0]
+        STRB    r0, [r12, #IOMD_ROMCR1]         ; and do the same for extension ROMs (just in case)
+
+        MOV     r0, #IOMD_VREFCR_VRAM_256Kx64 :OR: IOMD_VREFCR_REF_16   ; select 16µs refresh, assume 2 banks of VRAM
+        STRB    r0, [r12, #IOMD_VREFCR]
+
+        MOV     r0, #IOMD_IOTCR_Network_TypeA :OR: IOMD_IOTCR_Combo_TypeB :OR: IOMD_IOTCR_Sound_TypeB :OR: IOMD_IOTCR_Sound_Word
+        STRB    r0, [r12, #IOMD_IOTCR]
+
+        MOV     r0, #0                          ; Podule manager wants TypeA setting by default for all podules
+        STRB    r0, [r12, #IOMD_ECTCR]
+
+ |
+        LDR     R0, ResetMemC_Value
+        STR     R0, [R0]     ; set ROM access times, refresh on flyback, no DMA
+ ]
+CommonInit
+; On breaks (ie software resets) we have to turn the MMU off.
+; This is slightly tricky if we've been soft-loaded!
+
+ [ ResetIndirected
+        TEQ     r1, #0                  ; r1 = 0 if reset, 1 if break
+        BEQ     %FT03                   ; [it's a reset]
+
+        SetMode SVC32_mode, r0          ; select 32-bit mode (we know we're in 32-bit config)
+        B       %FT05
+03
+ |
+; We check for breaks by testing if we're in 32-bit configuration:
+;  - on reset we'll be put into 26-bit config, MMU off, 26-bit mode
+;  - on breaks we'll be left in 32-bit config, MMU on,  26-bit mode
+
+; In both cases we want to end up in 32-bit config with MMU off, in 32-bit mode
+
+        SetMode SVC32_mode, r1, r0      ; try to select SVC32 mode
+        mrs     AL, r2, CPSR_all        ; read back PSR
+        AND     r2, r2, #&1F            ; extract mode bits from PSR we tried to modify
+        TEQ     r2, #SVC32_mode         ; and see if we made it into SVC32
+        BEQ     %FT05                   ; [we made it so must be a Break]
+ ]
+
+; It's a reset, so select 32-bit config, MMU off
+
+ [ LateAborts
+        MOV     r2, #MMUC_P :OR: MMUC_D :OR: MMUC_L ; select 32-bit config, MMU off, late aborts
+ |
+        MOV     r2, #MMUC_P :OR: MMUC_D ; select 32-bit config, MMU off
+ ]
+        SetCop  r2, CR_Control
+        SetMode SVC32_mode, r1, r0      ; and re-select 32-bit mode (this time it'll work)
+        AND     r0, r0, #&1F            ; check original mode
+        TEQ     r0, #SVC26_mode         ; if we were in a 26-bit mode,
+        BICEQ   lr, lr, #&FC000003      ; then knock off 26-bit style PSR bits from link register
+                                        ; don't knock them off otherwise, since we may be soft-loaded above 64M
+        MOV     pc, lr                  ; and exit
+
+; It's a Break
+
+; The MMU is on and we want it off: whether we're executing out of ROM or RAM, we
+; have to jump to the physical location of our image, which means paging it in at its
+; own physical address.
+
+; On MEMC1 systems it's possible that the L1/L2 logical address is the same as the image's physical
+; address, which causes a headache, so we'd best use the physical mapping of the page tables (this
+; can't clash as IOMD only goes up to 2000 0000 and our physical mapping is above that).
+
+05
+        MOV     r0, #0
+        LDR     r0, [r0, #DRAMPhysAddrA]        ; get address of 1st DRAM bank
+        LDR     r1, =PhysSpace + DRAMOffset_L1PT ; offset to start of L1
+        ADD     r0, r0, r1                      ; r0 -> L1 in physical mapped logical space
+
+        LDR     r1, [r0, #ROM :SHR: (20-2)]     ; load L1 entry for 1st Mbyte of ROM
+        MOV     r1, r1, LSR #20                 ; knock off other bits
+        LDR     r2, =(AP_None * L1_APMult) + L1_Section
+                                                ; (svc-only access) + ~ucb + section mapped
+        ORR     r2, r2, r1, LSL #20             ; merge in address
+        STR     r2, [r0, r1, LSL #2]!           ; store in L1PT for 1st Mbyte
+        ADD     r2, r2, #1 :SHL: 20             ; move on to 2nd Mbyte
+        STR     r2, [r0, #4]                    ; and store in next entry
+
+        SetCop  r0, CR_TLBFlush                 ; flush TLB (data is ignored)
+        SetCop  r0, CR_IDCFlush                 ; and flush cache as well
+
+        MOV     r0, r1, LSL #20
+        SUB     r0, r0, #ROM                    ; form RAM-ROM offset
+        ADD     pc, pc, r0                      ; jump to RAM code (when we get onto IOMD, we'll have to be in 32-bit mode)
+        NOP                                     ; this instruction will be skipped
+
+; we're now in RAM, so it's safe to turn the MMU off, but leave us in 32-bit config (and 32-bit mode)
+
+        BIC     lr, lr, #&FC000003              ; knock out PSR bits from return address
+                                                ; (we know we were in 32-bit config, 26-bit mode on entry)
+        ADD     lr, lr, r0                      ; and add on offset - NB this may now be above 64MB (on IOMD)
+ [ LateAborts
+        MOV     r0, #MMUC_P :OR: MMUC_D :OR: MMUC_L ; turn MMU off, but leave us in 32-bit config, late aborts
+ |
+        MOV     r0, #MMUC_P :OR: MMUC_D         ; turn MMU off, but leave us in 32-bit config
+ ]
+        SetCop  r0, CR_Control
+
+        MOV     pc, lr                          ; return to caller, but in physical address space
+
+; -> MemSize
+
+; (non-destructive) algorithm to determine MEMC RAM configuration
+;
+; Dave Flynn and Alasdair Thomas
+; 17-March-87
+;
+; Spooling checkered by NRaine and SSwales !
+; 8MByte check bodged in by APT
+;
+; NOTE: Routines MemSize and TimeCPU are called by the power-on test software,
+; so their specifications MUST not change.
+;
+; Set MEMC for 32-k page then analyse signature of possible
+; external RAM configurations...
+; The configurations are:
+;
+; Ram Size    Page Size    Configuration    (Phys RAM) Signature
+;--------------------------------------------------------------------
+;  16MByte      32k        4*32*1Mx1         A13,A20,A21,A22,A23,A23.5 distinct
+;  16MByte      32k        16*8*256kx4       A13,A20,A21,A22,A23,A23.5 distinct
+;
+;  12MByte      32k        3*32*1Mx1         A13,A20,A21,A22,A23 OK, A23.5 fail
+;  12MByte      32k        12*8*256kx4       A13,A20,A21,A22,A23 OK, A23.5 fail
+;
+;   8MByte      32k        2*32*1Mx1         A13,A20,A21,A22 distinct, A23 fail
+;   8MByte      32k         8*8*256kx4       A13,A20,A21,A22 distinct, A23 fail
+;
+;   4Mbyte      32k          32*1Mx1         A13,A21,A20 distinct, A22,A23 fail
+;   4Mbyte      32k         4*8*256kx4       A13,A21,A20 distinct, A22,A23 fail
+;
+;   2Mbyte      32k    expandable 2*8*256kx4 A13,A20 distinct, A21 fails
+;   2Mbyte ???  16k      fixed 2*8*256kx4    A13,A21 distinct, A20 fails
+;
+;   1Mbyte       8k          32*256kx1       A13,A20 fail, A19,A18,A12 distinct
+;   1Mbyte       8k           8*256kx1       A13,A20 fail, A19,A18,A12 distinct
+;   1Mbyte       8k          4*8*64kx4       A13,A20 fail, A19,A18,A12 distinct
+;
+; 512Kbyte       8k    expandable 2*8*64kx4  A13,A20,A19 fail, A12,A18 distinct
+; 512Kbyte       4k      fixed 2*8*64kx4     A13,A20,A12 fail, A19,A18 distinct
+;
+; 256Kbyte       4K           8*64kx4        A13,A20,A12,A18 fail, A21,A19 ok
+; 256Kbyte       4K          32*64kx1        A13,A20,A12,A18 fail, A21,A19 ok
+;
+
+Z_Flag     * &40000000
+
+; MemSize routine... enter with 32K pagesize set
+; R0 returns page size
+; R1 returns memory size
+; R2 returns value set in MEMC
+; Can corrupt R3-R14
+
+; Note that on a soft-loaded system, the 1st word of the image may be
+; temporarily overwritten, but this is just the reset branch so it's OK.
+
+; MMU is always off at this point, so we must use the physical address of PhysRAM
+; Also we are entered in 32-bit config, 32-bit mode,
+; but we exit in 32-bit config, 26-bit mode
+
+ [ MorrisSupport
+funnypatterns
+        &       &66CC9933   ; 0110 1100 1001 0011
+        &       &CC993366   ; 1100 1001 0011 0110
+ ]
+
+MemSize ROUT
+        MOV     r13, lr                                 ;save in a register, cos we've got no stack
+
+ [ MEMC_Type = "IOMD"
+
+        MOV     r12, #IOMD_Base
+
+ [ MorrisSupport
+;
+        LDRB    r0, [r12, #IOMD_ID0]
+        CMP     r0, #&98
+        LDRB    r0, [r12, #IOMD_ID1]
+        CMPEQ   r0, #&5B
+       ;MOVEQ   r3, #xxxx
+        BNE     MemSizeIOMD                             ; NOT MORRIS assume Medusa hardware
+;
+; MemSize for Morris
+;
+        MOV     r11, #IOMD_DRAMWID_DRAM_32bit * &0F     ;set all 4 banks to be 32bit initially
+        MOV     r14, #IOMD_Base
+        STRB    r11, [r14, #IOMD_DRAMWID]
+ [ 1 = 0
+        MOV     R10, #DRAM0PhysRam
+        MOV     R11, #(2*OneMByte)
+        STMIA   R10!, {R10, R11}
+        B       AllocateTheRAM
+        ! 0, "*** WARNING *** Bodged RAM sizing version ment for PSwindell"
+ ]
+        MOV     r10, #0                                 ;indicate no RAM found yet
+        MOV     r9, #IOMD_DRAMWID_DRAM_16bit            ;bit to OR into DRAMWID to set 16bit
+        MOV     r0, #DRAM0PhysRam
+;
+; r0    DRAM address
+; r9    IOMD_DRAMWID_DRAM_16bit for current DRAM bank
+; r11   current IOMD_DRAMWID register contents
+;
+ExamineDRAMBank                                         ;examine first/next DRAM bank
+;
+        LDMIA   r0, {r1, r2}                            ;Preserve the two locations that we widdle on
+
+        ADR     r3, funnypatterns                       ;We write different values to two locations
+        LDMIA   r3, {r3, r4}                            ; incase bus capacitance holds our value
+        STMIA   r0, {r3, r4}
+        LDMIA   r0, {r5, r6}                            ;Reread test locations
+        EORS    r5, r5, r3                              ;Both locations should read correctly
+        EOR     r6, r6, r4                              ; if memory is 32bits wide
+       ;TEQ     r5, #0
+        TEQEQ   r6, #0
+        BEQ     %FT1010                                   ;32bit wide memory
+
+        TST     r5, #&00FF                              ;If the bottom 16bits of each location
+        TSTEQ   r5, #&FF00                              ; are correct, the memory is 16bits wide
+        TSTEQ   r6, #&00FF
+        TSTEQ   r6, #&FF00
+        BNE     NoRamInBank                             ;No memory in this bank
+
+        ORR     r11, r11, r9                            ;Bank is 16bits wide
+1010
+        STMIA   r0, {r1, r2}                            ;Restore the two locations we widdled on
+                                                        ;Must do BEFORE poking the DRAMWID register
+        MOV     r14, #IOMD_Base                         ;
+        STRB    r11, [r14, #IOMD_DRAMWID]               ;
+;
+; minimum ram test
+;
+        ADD     r1, r0, #A18
+        BL      DistinctAddresses
+        BNE     NoRamInBank                             ;Less than 512KBytes, so ignore this bank
+
+        MOV     r6, #0                                  ;Fragment address
+        MOV     r7, #0                                  ;Fragment address
+        MOV     r8, #A19                                ; now go through address lines A19-A25
+1015
+        ADD     r1, r0, r8                              ; see if this address line is unique
+        BL      DistinctAddresses
+        BNE     %FT1020                                   ; if we've failed then r8 is true size, so exit
+        MOV     r8, r8, LSL #1                          ; else shift up to next
+        TEQ     r8, #A26                                ; only test up to A25
+        BNE     %BT1015
+        BEQ     %FT1035                           ;Bank fully occupied, DON'T test for higher fragments
+1020
+;
+; Found some DRAM, at address r0, size r8.
+; There may be one or two higher address lines connected, so scan upto A25 looking for
+; extra DRAM chunks.
+;
+        MOV     r1, r8
+1025
+        TEQ     r1, #A25
+        BEQ     %FT1035                           ;No higher active address lines found ie one lump of DRAM
+        ADD     r1, r0, r1,LSL #1
+        BL      DistinctAddresses
+        SUB     r1, r1, r0                              ;Recover bit value
+        BNE     %BT1025
+;
+; Got a 2nd fragment, at address r1 (also of size r8)
+;
+        MOV     r6, r1
+1030
+        TEQ     r1, #A25
+        BEQ     %FT1035                           ;No higher active address lines found ie two lumps of DRAM
+        ADD     r1, r0, r1,LSL #1
+        BL      DistinctAddresses
+        SUB     r1, r1, r0                              ;Recover bit value
+        BNE     %BT1030
+;
+; Got another active address line (ie total four fragments)
+;
+        MOV     r7, r1
+;
+1035
+;
+; Found 1, 2 or 4 lumps of DRAM
+;
+  [ 1 = 1
+;
+; New code which relies on reflection to merge fragments into larger blocks
+;
+        TEQ     r10, #0                         ;Need some ram to dump block/fragment data
+        MOVEQ   r10, r0                         ;
+
+        TEQ     r6, #0                          ;Do we have one fragment?
+        MOVEQ   r1, r0                          ;EQ: First and only fragment in this bank
+        MOVEQ   r2, r8                          ;EQ:   so save actual address and size
+        ADDNE   r1, r0, r6                      ;NE: Use reflection to make 1st fragment appear
+        SUBNE   r1, r1, r8                      ;NE:   to start just below 2nd fragment
+        MOVNE   r2, r8, LSL #1                  ;NE:   treat as one double size fragment
+
+        STMIA   r10!, {r1, r2}                  ; {address, size}
+
+        TEQ     r7, #0                          ;Do 3rd and 4th fragments exist
+        ADDNE   r1, r1, r7                      ;NE: yes, merge 3 and 4 together
+        STMNEIA r10!, {r1, r2}                  ;  {address, size}
+  |
+;
+; Old code which enters each fragment as found
+;
+        TEQ     r10, #0                         ;Need some ram to dump block/fragment data
+        MOVEQ   r10, r0                         ;
+
+        STMIA   r10!, {r0, r8}                  ;First fragment
+
+        TEQ     r6, #0
+        ADDNE   r1, r0, r6
+        STMNEIA r10!, {r1, r8}                  ;Second fragment
+
+        TEQ     r7, #0
+        ADDNE   r1, r0, r7
+        STMNEIA r10!, {r1, r8}                  ;Third
+        ADDNE   r1, r1, r6
+        STMNEIA r10!, {r1, r8}                  ;and fourth fragments
+  ]
+  [ Simulator
+        TubeString r2, r3, r4, "Address  Size"
+        TubeDumpNoStack r0, r2, r3, r4
+        TubeDumpNoStack r8, r2, r3, r4
+        TubeNewlNoStack r3, r4
+
+        TEQ     R7, #0
+        BEQ     skip1
+        TubeString r2, r3, r4, "Fragment (1): "
+        TubeDumpNoStack r7, r2, r3, r4
+        TubeNewlNoStack r3, r4
+skip1
+        TEQ     R6, #0
+        BEQ     skip2
+        TubeString r2, r3, r4, "Fragment (2): "
+        TubeDumpNoStack r6, r2, r3, r4
+        TubeNewlNoStack r3, r4
+skip2
+  ]
+
+
+
+NoRamInBank
+        ADD     r0, r0, #DRAM1PhysRam-DRAM0PhysRam      ; move onto next bank
+        MOV     r9, r9, LSL #1                          ; shunt up position in DRAMWID
+        CMP     r9, #&0010                              ; if more banks to do
+        BLT     ExamineDRAMBank                         ; then loop
+
+        TEQ     r10, #0                                 ; have we got any DRAM?
+;NoDRAMPanic
+        BEQ     NoDRAMPanic                             ; we'd better stop now
+
+;
+; Having dumped our block/fragment data to the first bit of DRAM that we found.
+; We now go back through it, allocating some for the screen, and some for 'PageZero'.
+; The data has been dumped into RAM that we now allocate as screen ram, so it needs
+; to be copied into 'PageZero'.
+;
+; r10 -> byte after last fragment(address, size) pair
+;
+AllocateTheRAM
+        AND     r7, r10, #DRAMBaseAddressMask           ;point to first fragment data
+        MOV     r2, #0                                  ;MOS workspace not yet allocated
+
+        LDMIA   r7!, {r4, r5}                           ;first fragment address & size
+        CMP     r10, r7                                 ;is there only 1 fragment
+ [ 1 = 1
+;
+; New - requested by Tim Dobson
+;
+        MOVHI   r1, r5                                  ;if >1 fragments, take first fragment for the screen
+        SUBLS   r1, r5, #OneMByte                       ;if this is the only fragment, take all but 1MByte of it
+        MOV     r0, r4                                  ;screen starts at beginning of fragment
+  [ 1 = 1
+;
+; New - also requested by Tim Dobson
+;
+        CMP     r1, #SixteenMByte                       ;Limit our claim to 16Mbyte
+        MOVGT   r1, #SixteenMByte
+  ]
+ |
+        MOVHI   r1, r5                                  ;if >1 fragments, consider taking first fragment for the screen
+        MOVLS   r1, r5, LSR #1                          ;if this is the only fragment, try for half of it
+        MOV     r0, r4                                  ;screen starts at beginning of fragment
+
+        CMP     r1, #OneMByte                           ;Limit our claim to 1Mbyte
+        MOVGT   r1, #OneMByte
+ ]
+        ADD     r4, r4, r1                              ;adjust fragment for amount claimed by screen
+        SUBS    r5, r5, r1
+        BEQ     %FT1065                                 ;EQ whole fragment used
+                                                        ;NE part of fragment remains to be allocated
+1060
+        TEQ     r2, #0                                  ;allocate MOS workspace if not already done so
+        LDREQ   r2, =DRAMOffset_PageZero + DRAMPhysAddrA
+        ADDEQ   r2, r2, r4
+        MOVEQ   r3, r2
+
+        STMIA   r3!, {r4, r5}                           ;write fragment data to correct place in PageZero
+1065
+        CMP     r10, r7                                 ;any more fragment (address, size) pairs?
+        LDMHIIA r7!, {r4, r5}                           ;HI, yes so load next fragment pair (size
+        BHI     %BT1060                                 ;HI, mustbe non-zero) and loop back
+
+        STMDB   r2!, {r0, r1}                           ;write VideoPhysAddr, VideoSize
+;
+; r2 -> start of PhysRamTable
+; r3 -> byte after last used entry in PhysRamTable
+;
+        MOV     r7, r2
+       ;MOV     r2, r2                                  ; r2 -> start of PhysRamTable
+        MOV     r10, r3
+       ;MOV     r3, r3                                  ; r3 -> byte after last used entry in PhysRamTable
+
+
+;
+; r0 screen start address
+; r1 screen size
+; r2 -> start of PhysRamTable
+; r3 -> byte after last used entry in PhysRamTable
+
+        MOV     r4, #0                                  ;Morris cannot support VRAM, so...
+        STR     r4, [r2, #VRAMWidth-PhysRamTable]       ; store width of VRAM (0,1 or 2)
+        STR     r4, [r2, #VRAMSize-PhysRamTable]        ; and size of VRAM (fixes DForth's bug of 6/3/95)
+
+        MOV     r14, #IOMD_Base
+
+        MOV     r4, #IOMD_VIDCR_DRAMMode :OR: &10       ; if no VRAM, then turn on DRAM mode, and set increment to &10
+        STRB    r4, [r14, #IOMD_VIDCR]
+        STR     r0, [r14, #IOMD_VIDCUR]                 ; set up VIDCUR to start of video RAM
+        STR     r0, [r14, #IOMD_VIDSTART]               ; do same for VIDSTART
+        STR     r0, [r14, #IOMD_VIDINIT]                ; and for VIDINIT
+                                                        ; so we don't get a mess when we turn video DMA on later
+
+        LDR     r4, =46500000                           ; if no VRAM, then 46.5E6 bytes/sec bandwidth
+        STR     r4, [r2, #VideoBandwidth-PhysRamTable]  ; store video bandwidth
+
+        ADD     r4, r0, r1                              ;form VIDEND (will be on mult. of SAM)
+        SUB     r4, r4, #4096
+        STR     r4, [r14, #IOMD_VIDEND]                 ;this instruction put in on 6/3/95 after inspection of RPC code
+;
+;
+;
+        MOV     r7, r2
+        MOV     r10, r3
+
+        B       MemSizeTotalRAM
+
+MemSizeIOMD
+ ]
+
+; Right, let's find out where our memory is
+
+; First, we check out the VRAM. This is so that if there's no VRAM, we know to take out the 1st Mbyte of DRAM
+; that we find.
+
+; Don't bother checking for more than 2M of VRAM, because we don't know what the 1/2 SAM length is for larger sizes
+
+        MOV     r2, #IOMD_VREFCR_VRAM_256Kx64 :OR: IOMD_VREFCR_REF_16 ; assume 2 banks of VRAM by default
+        STRB    r2, [r12, #IOMD_VREFCR]
+
+        MOV     r0, #VideoPhysRam                       ; point at VRAM
+        ADD     r1, r0, #A2                             ; test A2
+        BL      DistinctAddresses
+        MOVEQ   r6, #2                                  ; we've got 2M of VRAM
+        BEQ     %FT08
+
+        MOV     r2, #IOMD_VREFCR_VRAM_256Kx32 :OR: IOMD_VREFCR_REF_16
+        STRB    r2, [r12, #IOMD_VREFCR]
+        ADD     r1, r0, #A2                             ; check for any VRAM at all
+        BL      DistinctAddresses
+        MOVEQ   r6, #1                                  ; we've got 1M of VRAM
+        MOVNE   r6, #0                                  ; no VRAM
+08
+ [ IgnoreVRAM
+        MOV     r6, #0                                  ; pretend there's no VRAM
+ ]
+        MOVS    r12, r6                                 ; if no VRAM, then video RAM has yet to be found
+        MOVNE   r12, r0                                 ; else point at VRAM
+
+; Now, we have to find a bank of DRAM, so we've got somewhere to store our results!
+
+        MOV     r11, #IOMD_DRAMCR_DRAM_Large * &55      ; set all banks to be large initially
+        MOV     r14, #IOMD_Base
+        STRB    r11, [r14, #IOMD_DRAMCR]
+
+        MOV     r10, #0                                 ; indicate no RAM found yet
+        MOV     r9, #IOMD_DRAMCR_DRAM_Small             ; bit to OR into DRAMCR
+        MOV     r0, #DRAM0PhysRam
+10
+        ADD     r1, r0, #A10                            ; this should be OK for both configurations
+        BL      DistinctAddresses
+        BNE     %FT25                                   ; [no RAM in this bank at all]
+
+        ADD     r1, r0, #A11                            ; test for 256K DRAM
+        BL      DistinctAddresses
+        ORRNE   r11, r11, r9                            ; it is, so select small multiplexing
+        MOVNE   r14, #IOMD_Base
+        STRNEB  r11, [r14, #IOMD_DRAMCR]                ; store new value of DRAMCR, so we can use memory immediately
+        MOVNE   r8, #1024*1024                          ; must be 1Mbyte at this address
+        BNE     %FT20
+
+; it's bigger than 256K words, so test address lines A21-A25 in sequence
+; we assume that the size of each bank is a power of 2
+
+        MOV     r8, #A21                                ; now go through address lines A21-A25
+15
+        ADD     r1, r0, r8                              ; see if this address line is unique
+        BL      DistinctAddresses
+        BNE     %FT20                                   ; if we've failed then r8 is true size, so exit
+        MOV     r8, r8, LSL #1                          ; else shift up to next
+        TEQ     r8, #A26                                ; only test up to A25
+        BNE     %BT15
+20
+        TEQ     r12, #0                                 ; have we found any video RAM yet?
+        BNE     %FT22                                   ; yes, so no worries
+
+        MOV     r12, r0                                 ; no, so use this as video RAM
+        ADD     r0, r0, #1024*1024                      ; advance RAM pointer by 1M
+        SUBS    r8, r8, #1024*1024                      ; take 1 Mbyte off the size
+        BEQ     %FT25                                   ; if that's all there was, then go look for the next bank
+22
+        TEQ     r10, #0                                 ; is this the first lot we've found?
+        LDREQ   r10, =DRAMOffset_PageZero + DRAMPhysAddrA
+        ADDEQ   r10, r10, r0                            ; then point r10 at DRAM part of PhysRamTable
+        MOVEQ   r7, r10                                 ; points to beginning of table
+        STMIA   r10!, {r0, r8}                          ; store address, size
+25
+        AND     r0, r0, #DRAMBaseAddressMask            ; move back to start of DRAM bank (in case we stole some video DRAM)
+        ADD     r0, r0, #DRAM1PhysRam-DRAM0PhysRam      ; move onto next bank
+        MOV     r9, r9, LSL #2                          ; shunt up position in DRAMCR
+        CMP     r9, #&100                               ; if more banks to do
+        BCC     %BT10                                   ; then loop
+
+        TEQ     r10, #0                                 ; have we got any DRAM?
+NoDRAMPanic
+        BEQ     NoDRAMPanic                             ; we'd better stop now
+
+; Now go back and put the VRAM information in, and also program VIDCR and VIDCUR
+
+        STR     r6, [r7, #VRAMWidth-DRAMPhysAddrA]      ; store width of VRAM (0,1 or 2)
+        CMP     r6, #1
+        MOVCC   r2, #IOMD_VIDCR_DRAMMode :OR: &10       ; if no VRAM, then turn on DRAM mode, and set increment to &10
+        MOVEQ   r2, #SAMLength/2/256                    ; if 1M VRAM, then use VRAM mode, and set increment for 1/2 SAM
+        MOVHI   r2, #SAMLength/2/256*2                  ; if 2M VRAM, then use VRAM mode, and set increment for 2*1/2 SAM
+        LDRCC   r3, =46500000                           ; if no VRAM, then 46.5E6 bytes/sec bandwidth
+        LDREQ   r3, =80000000                           ; if 1M VRAM, then 80E6   ---------""--------
+        LDRHI   r3, =160000000                          ; if 2M VRAM, then 160E6  ---------""--------
+        MOV     r14, #IOMD_Base
+        STRB    r2, [r14, #IOMD_VIDCR]
+        STR     r12, [r14, #IOMD_VIDCUR]                ; set up VIDCUR to start of video RAM
+        STR     r12, [r14, #IOMD_VIDSTART]              ; do same for VIDSTART
+        STR     r12, [r14, #IOMD_VIDINIT]               ; and for VIDINIT
+                                                        ; so we don't get a mess when we turn video DMA on later
+        STR     r3, [r7, #VideoBandwidth-DRAMPhysAddrA] ; store video bandwidth
+
+        ADD     r3, r12, #1024*1024-4096                ; add on a bit to form VIDEND (will be on mult. of SAM)
+        STR     r3, [r14, #IOMD_VIDEND]                 ; yes I know it's a bit of a bodge
+
+        MOVS    r14, r6, LSL #20                        ; convert amount of VRAM to bytes
+        STR     r14, [r7, #VRAMSize-DRAMPhysAddrA]      ; and store
+
+        MOVEQ   r14, #1024*1024                         ; if no VRAM, then video RAM size is 1M
+        STMDB   r7!, {r12, r14}                         ; store video information
+
+        MOV     r2, r7                                  ; r2 -> start of PhysRamTable
+        MOV     r3, r10                                 ; r3 -> byte after last used entry in PhysRamTable
+MemSizeTotalRAM
+; Now we have to work out the total RAM size
+
+  [ Simulator
+        TubeString r4, r5, r6, "Address  Size"
+  ]
+        MOV     r1, #0
+26
+        LDMIA   r7!, {r4, r5}                           ; get address, size
+        ADD     r1, r1, r5                              ; add on size
+  [ Simulator
+        TubeDumpNoStack r4, r6, r8, r9
+        TubeDumpNoStack r5, r6, r8, r9
+        TubeNewlNoStack r6, r8
+  ]
+        TEQ     r7, r10
+        BNE     %BT26
+
+        MOV     r0, #Page4K                             ; something to put in MEMC CR soft copy
+                                                        ; (it's probably irrelevant)
+ |
+
+; MEMC based memory sizing
+
+        MOV     r0, #PhysRamPhys
+
+        ADD     r1, r0, #A13
+        BL      DistinctAddresses
+        BNE     %05
+        ADD     r1, r0, #A21
+        BL      DistinctAddresses
+        MOVNE   r0, #Page32K
+        MOVNE   r1, #2048*1024
+        BNE     MemSizeDone
+
+        ADD     r1, r0, #4*1024*1024
+        BL      DistinctAddresses
+        MOVNE   r0, #Page32K
+        MOVNE   r1, #4*1024*1024
+        BNE     MemSizeDone
+
+        ADD     r1, r0, #8*1024*1024
+        BL      DistinctAddresses
+        MOVNE   r0, #Page32K
+        MOVNE   r1, #8*1024*1024
+        BNE     MemSizeDone
+
+        ADD     r1, r0, #12*1024*1024
+        BL      DistinctAddresses
+        MOV     r0, #Page32K
+        MOVNE   r1, #12*1024*1024
+        MOVEQ   r1, #16*1024*1024
+        B       MemSizeDone
+
+05
+        ADD     r1, r0, #A20
+        BL      DistinctAddresses
+        BNE     %10
+        MOV     r0, #Page16K
+        MOV     r1, #2048*1024
+        B       MemSizeDone
+
+10
+        ADD     r1, r0, #A19
+        BL      DistinctAddresses
+        BEQ     %15
+        MOV     r0, #Page8K
+        MOV     r1, #512*1024
+        B       MemSizeDone
+
+15
+        ADD     r1, r0, #A18
+        BL      DistinctAddresses
+        BEQ     %20
+        MOV     r0, #Page4K
+        MOV     r1, #256*1024
+        B       MemSizeDone
+
+20
+        ADD     r1, r0, #A12
+        BL      DistinctAddresses
+        BEQ     %25
+        MOV     r0, #Page4K
+        MOV     r1, #512*1024
+        B       MemSizeDone
+
+25
+        MOV     r0, #Page8K
+        MOV     r1, #1024*1024
+
+MemSizeDone
+        LDR     r3, =PhysRamPhys                ; Base of "video RAM"
+        MOV     r4, #480*1024                   ; 480K of "video RAM"
+        ADD     r5, r3, r4                      ; Base of "1st bank of DRAM"
+        SUB     r6, r1, r4                      ; Size of "1st bank of DRAM"
+
+        LDR     r2, =DRAMOffset_PageZero + PhysRamTable
+        ADD     r2, r5, r2                      ; physical address of table of physical RAM addresses/sizes
+        STMIA   r2, {r3-r6}
+
+        ADD     r3, r2, #4*4                    ; indicate just 2 areas
+
+        LDR     r4, =38400000                   ; indicate 38.4E6 bytes/sec video bandwidth
+        STR     r4, [r2, #VideoBandwidth-PhysRamTable]
+ ]
+
+        ADRL    r4, ROM                         ; use PC-relative addressing to get to start of image
+        TEQ     r4, #PhysROM                    ; then see if it's the normal ROM address
+        BEQ     %FT55                           ; if so, then we're OK
+
+        SUB     r1, r1, #OSROM_ImageSize*1024   ; if we've been soft-loaded, then we have ?M less than we thought
+        ADD     r5, r4, #OSROM_ImageSize*1024   ; point r5 at end of ROM
+
+; now go through the declared RAM areas looking for our image (we assume it's not split across two banks)
+
+        MOV     r6, r2                          ; make copy of start pointer
+30
+        TEQ     r6, r3
+        BEQ     %FT55
+        LDMIA   r6!, {r7,r8}                    ; load address, size
+        ADD     r8, r8, r7                      ; r8 -> end
+        SUBS    r9, r8, r5
+        SUBCSS  r10, r4, r7                     ; test for r4>=r7 and r5<=r8
+        BCC     %BT30
+
+; we've found where the ROM is
+
+        BNE     %FT40                           ; if NE from last comparison then r4<>r7 ie start(ROM)<>start(region)
+                                                ; else start(ROM)=start(region), so modify region to be end(ROM)..end(region)
+        TEQ     r9, #0                          ; test if region completely removed
+        STMNEDB r6, {r5, r9}                    ; if region not completely removed, store updated (address, size)
+        BNE     %FT55
+
+; region completely removed, so shunt down remaining ones
+
+35
+        TEQ     r6, r3                          ; if not got to end
+        LDMNEIA r6, {r7, r8}                    ; then load next pair
+        STMNEDB r6, {r7, r8}                    ; and store in last
+        ADD     r6, r6, #8                      ; advance pointer
+        BNE     %BT35
+        SUB     r3, r3, #8                      ; move back end pointer
+        B       %FT55
+
+; there is a lump at the start
+
+40
+        STMDB   r6, {r7, r10}                   ; reduce start bit
+        TEQ     r9, #0                          ; is there an end bit?
+        BEQ     %FT55                           ; [no, so we've finished]
+
+; now shunt up all remaining areas, to make room for new one
+
+        MOV     r8, r3
+45
+        TEQ     r8, r6
+        LDMNEDB r8, {r7, r10}
+        STMNEIA r8, {r7, r10}
+        SUBNE   r8, r8, #8
+        BNE     %BT45
+        STMIA   r8, {r5, r9}                    ; store end lump
+        ADD     r3, r3, #8                      ; advance end pointer
+
+; now store zeros to fill out table
+
+55
+        ADD     r5, r2, #PhysRamTableEnd-PhysRamTable
+        MOV     r6, #0
+        MOV     r7, #0
+57
+        CMP     r3, r5
+        STMCCIA r3!, {r6, r7}
+        BCC     %BT57
+
+; Now set up L1 + L2
+; - first work out how big static L2 needs to be
+; - then zero L1 + L2 (L1 is actually inside L2)
+
+        MOV     r3, r1, LSR #22                 ; r3 = memsize / 4M
+        TEQ     r1, r3, LSL #22                 ; if any remainder
+        ADDNE   r3, r3, #1                      ; then round up (r3 is now how many pages of L2 needed for free pool)
+        MOV     r3, r3, LSL #12                 ; convert to bytes
+        ADD     r3, r3, #FixedAreasL2Size       ; add on size of L2 for other fixed areas
+        STR     r3, [r2, #L2PTSize-PhysRamTable] ; save away for future reference
+
+        LDR     r2, [r2, #DRAMPhysAddrA-PhysRamTable]   ; get address of 1st DRAM bank
+        LDR     r5, =DRAMOffset_L2PT
+        ADD     r2, r2, r5                              ; make r2 -> L2PT
+        MOV     r5, #0                          ; value to initialise L1 and L2 to (translation faults)
+        MOV     r6, r5
+        MOV     r7, r5
+        MOV     r8, r5
+        MOV     r9, r5
+        MOV     r10, r5
+        MOV     r11, r5
+        MOV     r12, r5
+ [ :LNOT: Simulator                             ; don't bother zeroing L1/2 for Mark
+        ADD     r2, r2, r3                      ; start at end and work back
+60
+        STMDB   r2!, {r5-r12}
+        SUBS    r3, r3, #8*4
+        BNE     %BT60
+ ]
+
+; r2 ends up pointing at L2
+
+        ADD     r3, r2, #DRAMOffset_L1PT-DRAMOffset_L2PT        ; r3 -> L1Phys
+
+; now initialise all the L1 for the area covered by the static L2, as if it were all page mapped
+; - the section mapped stuff will be overwritten when we go thru MemInitTable shortly
+
+        ORR     r5, r2, #L1_Page + L1_U         ; take phys base of L2, and or in other bits to form an L1 entry
+        LDR     r6, =L2PTSize+DRAMOffset_PageZero-DRAMOffset_L2PT
+        LDR     r10, [r2, r6]                   ; r10 = size of L2 (used after this loop, too)
+        ADD     r6, r5, r10                     ; r6 = value in r5 when we've finished
+        MOV     r7, r3                          ; r7 -> where we're storing L1 entries
+61
+        STR     r5, [r7], #4                    ; store L1 entry
+        ADD     r5, r5, #1024                   ; advance L2 pointer
+        TEQ     r5, r6
+        BNE     %BT61
+
+; now go through memory initialisation table, setting up entries
+
+        ADR     r5, MemInitTable
+65
+        LDMIA   r5!, {r6-r8}                    ; load size, logaddr, indicator
+        TEQ     r6, #0                          ; if size field is zero
+        BEQ     %FT90                           ; then we've finished going through table
+
+        TST     r8, #1                          ; if bit 0 of indicator is set, then it's page mapped
+        BNE     %FT75
+
+        TST     r8, #2                          ; is it abort?
+        BNE     %FT68                           ; [no]
+
+; it's a section abort (r8=0)
+
+66
+        STR     r8, [r3, r7, LSR #20-2]         ; store zero in L1 table
+        ADD     r7, r7, #&00100000              ; increment logical address by 1M
+        SUBS    r6, r6, #&00100000              ; and decrement size by 1M
+        BNE     %BT66                           ; loop until done
+        B       %BT65
+
+
+68
+; it's section mapped
+
+        TST     r8, #ROMbit                     ; is it a ROM image offset
+        ADDNE   r8, r8, r4                      ; if so, then add in image offset
+        BICNE   r8, r8, #ROMbit                 ; and knock out the dodgy bit
+
+        TST     r8, #Vidbit                     ; is it a video memory offset
+        LDRNE   r9, =VideoPhysAddr+DRAMOffset_PageZero-DRAMOffset_L2PT
+        LDRNE   r9, [r2, r9]                    ; get physical address of video RAM
+        ADDNE   r8, r8, r9                      ; add on offset
+        BICNE   r8, r8, #Vidbit                 ; and knock out the dodgy bit
+70
+        STR     r8, [r3, r7, LSR #20-2]         ; store entry in L1 table (assumes bits 18, 19 are clear!)
+        ADD     r7, r7, #&00100000              ; increment logical address by 1M
+        ADD     r8, r8, #&00100000              ; and physical address by 1M
+        SUBS    r6, r6, #&00100000              ; and decrement size by 1M
+        BNE     %BT70                           ; if we've not finished then loop
+        B       %BT65                           ; else go back to main loop
+
+; explicit L2 setup
+
+75
+        CMP     r6, #-1                         ; if size <> -1
+        BNE     %FT80                           ; then normal
+
+; size = -1 => this is the chunk with the soft CAM map in it,
+; so we must work out a suitable size (and store it in SoftCamMapSize)
+; we also have to work out the correct offset in the DRAM bank, since this is
+; after variable size L2PT
+
+        MOV     r6, r1, LSR #24-3               ; number of pages for cam map
+        CMP     r1, r6, LSL #24-3               ; if bits dropped off
+        ADDNE   r6, r6, #1                      ; then need one more page
+        MOV     r6, r6, LSL #12
+        LDR     r9, =DRAMOffset_PageZero-DRAMOffset_L2PT+SoftCamMapSize
+        STR     r6, [r2, r9]                    ; store size used
+        ADD     r6, r6, #UndStackSize           ; chunk also includes undstack
+        ADD     r9, r10, #DRAMOffset_L2PT       ; undstack/cammap starts at offset L2PT + L2PTSize
+        ORR     r8, r8, r9                      ; OR in other misc bits from table
+80
+        LDR     r9, =DRAMOffset_PageZero-DRAMOffset_L2PT+DRAMPhysAddrA
+                                                ; offset from L2 to word containing physical address of 1st DRAM bank
+        LDR     r9, [r2, r9]                    ; r9 = address of 1st DRAM bank
+        ADD     r8, r8, r9                      ; convert offset to address
+        EOR     r8, r8, #L2_SmallPage :EOR: 1   ; make bottom 2 bits correct for L2
+        ADD     r9, r2, r7, LSR #10             ; r9 -> L2 for this page
+85
+        STR     r8, [r9], #4                    ; store entry in L2
+        ADD     r8, r8, #4*1024                 ; advance physical page address
+        SUBS    r6, r6, #4*1024                 ; one less page to do
+        BNE     %BT85
+        B       %BT65
+
+; L1 is now set up correctly, and L2 has the correct CB bits, but no accessible pages
+; Put in the L2 entries for the logical area we are going to access the L2 (and L1) at
+; r10 still holds L2PT size
+
+90
+        ADD     r5, r2, #(L2PT :SHR: 10)        ; r5 -> start of L2PT for L2 logical address
+        LDR     r6, =(AP_None * L2_APMult) + L2_SmallPage ; r6 = other gubbins to put in L2 entries (not C or B)
+        ORR     r6, r6, r2                      ; OR in physical address of L2
+        MOV     r7, r10                         ; amount to put in (L2PTSize)
+95
+        STR     r6, [r5], #4                    ; store entry
+        ADD     r6, r6, #4096                   ; move onto next page
+        SUBS    r7, r7, #4096                   ; one less page to do
+        BNE     %BT95                           ; loop until done
+
+; But before we turn on, we have to temporarily make the addresses we are currently executing out of
+; into a section mapped area straight through, so we don't crash before we can jump up into ROM area
+
+        ASSERT ((CritStart :EOR: CritEnd) :AND: &FFF00000)=0    ; make sure start and end are in the same MB chunk
+
+        ADR     r5, CritStart                   ; point at critical region start
+        MOV     r5, r5, LSR #20                 ; divide by 1MB
+        LDR     r6, [r3, r5, LSL #2]            ; get current L1 entry to put back later
+        MOV     r7, r5, LSL #20                 ; r7 = physical address of base of section
+        ORR     r7, r7, #(AP_None * L1_APMult)
+        ORR     r7, r7, #L1_Section
+        STR     r7, [r3, r5, LSL #2]            ; store replacment entry in L1 (not U,C or B)
+
+        SetCop  r3, CR_TTabBase                 ; set up MMU pointer to L1
+        ADD     r3, r3, #PhysSpace              ; when we put L1 entry back later, we need to use the copy in PhysSpace area
+
+        MOV     r7, #1
+        SetCop  r7, CR_Domains                  ; only use domain 0
+
+        SetCop  r7, CR_IDCFlush                 ; flush cache + TLB just in case
+        SetCop  r7, CR_TLBFlush                 ; (data written is irrelevant)
+
+        LDR     r7, =DefaultMMUControlRegister  ; turn on MMU, cache + write-buffer and select 32-bit configuration
+CritStart
+        SetCop  r7, CR_Control
+
+; now we can jump into the ROM space (if we're not already there)
+
+        RSB     r4, r4, #ROM                    ; make offset from current address to ROM
+        ADD     pc, pc, r4                      ; jump up into ROM area
+        NOP                                     ; this instruction will be skipped
+
+; now put back the L1 entry we messed up
+
+        STR     r6, [r3, r5, LSL #2]
+CritEnd                                         ; 2 words after we go up into ROM
+        SetCop  r7, CR_TLBFlush                 ; flush TLB (no need to flush cache, as there's nothing in it)
+
+        SetMode UND32_mode, r7
+        LDR     r13_undef, =UNDSTK              ; set up undefined mode stack pointer
+
+        SetMode SVC26_mode, r7                  ; switch into 26-bit mode
+        ADD     r13, r13, r4                    ; adjust return address
+
+        LDR     r2, ResetMemC_Value
+        BIC     r2, r2, #&C
+        ORR     r2, r2, r0
+ [ MEMC_Type <> "IOMD"
+        STR     r2, [r2]                        ; set MEMC to right state
+ ]
+        MOV     r0, #4*1024                     ; r0 = true page size (now split off
+                                                ; from MEMC control register)
+ [ MEMC_Type = "IOMD"
+  [ Simulator
+        TubeString r4, r5, r6, "Got through all of MemSize, and we're still here!"
+        TubeChar r4, r5, "MOV r5, #4", NoStack
+  ]
+ ]
+        MOV     pc, r13
+
+; Memory map initialisation table
+; Consists of word triplets (size,logaddr,type)
+; where size    is size in bytes of area (size=0 terminates list)
+;       logaddr is the base logical address of area
+;       type is one of 5 formats:
+;       a) a standard section-mapped L1 entry (physical address gets incremented for each MB in size)
+;       b) like a section-mapped L1 entry, but with bit 12 set (address field holds base offset from "ROM" image)
+;       c) like a section-mapped L1 entry, but with bit 13 set (address field holds base offset from start of video RAM)
+;       d) like a page-mapped L1 entry, which indicates a page-mapped area to fill in
+;          the L2 for. In this case the other bits are as follows:-
+;               Bits 3,2   - CB respectively
+;               Bits (11,10),(9,8),(7,6),(5,4) - access privileges
+;               Bits 31-12 - offset in 1st DRAM bank to start of these pages (in units of pages)
+;          If the size field contains -1, then it is the SoftCAMMap, and the appropriate size should be worked out,
+;           and stored in SoftCamMapSize. Also, since the size of the L2 is variable the offset into the DRAM bank
+;           of the SoftCamMap is unknown at assembly time, so the offset bits in table are zero.
+;       e) zero - indicating that this area should abort (only necessary for section mapped bits in 48M-64M, cause they
+;           have no level 2, therefore section must abort) - used for VIDC1 emulation area.
+;       Note in case d), the L1 is not actually touched (it should have already been set up to point to the right L2)
+;
+
+ROMbit  *       1 :SHL: 12
+Vidbit  *       1 :SHL: 13
+PSS     *       PhysSpaceSize           :SHR: 20  ; Number of megabytes in physical space (used in table generation)
+
+        MACRO
+        MemInitSection  $size, $U, $C, $B, $logaddr, $ap, $physaddr
+        &       ($size)*&00100000
+        &       $logaddr
+        &       (($U)*L1_U):OR:(($C)*L1_C):OR:(($B)*L1_B):OR:(($ap)*L1_APMult):OR:$physaddr:OR:L1_Section
+        MEND
+
+        MACRO
+        MemInitROMs     $size, $U, $C, $B, $logaddr, $ap
+        &       ($size)*&00100000
+        &       $logaddr
+        &       (($U)*L1_U):OR:(($C)*L1_C):OR:(($B)*L1_B):OR:(($ap)*L1_APMult):OR:ROMbit:OR:L1_Section
+        MEND
+
+        MACRO
+        MemInitVideo    $size, $U, $C, $B, $logaddr, $ap
+        &       ($size)*&00100000
+        &       $logaddr
+        &       (($U)*L1_U):OR:(($C)*L1_C):OR:(($B)*L1_B):OR:(($ap)*L1_APMult):OR:Vidbit:OR:L1_Section
+        MEND
+
+        MACRO
+        MemInitAbort    $size, $logaddr
+        &       ($size)*&00100000
+        &       $logaddr
+        &       0
+        MEND
+
+        MACRO
+        MemInitPagesL2  $size, $C, $B, $logaddr, $ap, $dramoffset
+        &       ($size)
+        &       $logaddr
+        &       (($C)*L1_C):OR:(($B)*L1_B):OR:(($ap)*L2_APMult):OR:$dramoffset:OR:L1_Page
+        MEND
+
+MemInitTable    ;       sz, U, C, B, logaddr,   (ap,     (physaddr))
+        MemInitSection   4, 1, 0, 0, &03000000, AP_None, &03000000      ; I/O
+
+        MemInitAbort     1,          &03400000                          ; VIDC1 emulation zone
+        MemInitSection   1, 1, 0, 0, &03500000, AP_None, &03400000      ; VIDC20 space
+        MemInitSection   2, 1, 0, 0, &03600000, AP_None, &03600000      ; LAGs
+
+
+        [ OSROM_ImageSize = 4096
+        MemInitROMs      4, 1, 1, 1, &03800000, AP_Read                 ; ROM
+        MemInitROMs      4, 1, 1, 1, &03C00000, AP_Read                 ; ROM
+        |
+        MemInitROMs      2, 1, 1, 1, &03800000, AP_Read                 ; ROM
+        MemInitROMs      2, 1, 1, 1, &03A00000, AP_Read                 ; ROM
+        MemInitROMs      2, 1, 1, 1, &03C00000, AP_Read                 ; ROM
+        MemInitROMs      2, 1, 1, 1, &03E00000, AP_Read                 ; ROM
+        ]
+
+ [ :LNOT: NewStyle_Screen
+        MemInitVideo    16, 1, 0, 1, &05000000, AP_None                 ; real screen (section-mapped)
+ ]
+        MemInitSection PSS, 1, 0, 0, PhysSpace, AP_None, &00000000      ; map of physical space
+
+        MemInitROMs      2, 1, 1, 1, &FF800000, AP_Read                 ; ROM
+        MemInitROMs      2, 1, 1, 1, &FFA00000, AP_Read                 ; ROM
+        MemInitROMs      2, 1, 1, 1, &FFC00000, AP_Read                 ; ROM
+        MemInitROMs      2, 1, 1, 1, &FFE00000, AP_Read                 ; ROM
+
+; Now explicit initialisation of L2 for static pages
+
+        MemInitPagesL2  &8000, 0, 0, CursorChunkAddress, AP_Read, DRAMOffset_CursorChunk
+        MemInitPagesL2  &8000, 1, 1, &00000000, AP_Full, DRAMOffset_PageZero
+        MemInitPagesL2  &8000, 1, 1, SysHeapChunkAddress, AP_Full, DRAMOffset_SystemHeap
+        MemInitPagesL2     -1, 1, 1, UndStackSoftCamChunk, AP_Full, 0   ; variable offset and size
+
+        &       0, 0, 0                                                 ; terminate table
+
+
+; DistinctAddresses routine...
+; r0,r1 are the addresses to check
+; uses r2-5
+; writes interleaved patterns (to prevent dynamic storage...)
+; checks writing every bit low and high...
+; return Z-flag set if distinct
+
+; This routine must work in 32-bit mode
+
+DistinctAddresses ROUT
+        LDR     r2, [r0] ; preserve
+        LDR     r3, [r1]
+        LDR     r4, Pattern
+        STR     r4, [r0] ; mark first
+        MOV     r5, r4, ROR #16
+        STR     r5, [r1] ; mark second
+        LDR     r5, [r0]
+        CMP     r5, r4 ; check first
+        BNE     %10    ; exit with Z clear
+        LDR     r5, [r1] ; check second
+        CMP     r5, r4, ROR #16 ; clear Z if not same
+        BNE     %10
+; now check inverse bit writes
+        STR     r4, [r1] ; mark second
+        MOV     r5, r4, ROR #16
+        STR     r5, [r0] ; mark first
+        LDR     r5, [r1]
+        CMP     r5, r4 ; check second
+        BNE     %10   ; exit with Z clear
+        LDR     r5, [r0] ; check first
+        CMP     r5, r4, ROR #16 ; clear Z if not same
+10      STR     r3, [r1] ; restore
+        STR     r2, [r0]
+        MOV     pc, lr                  ; Z flag is already set up, and other flags don't matter
+
+Pattern
+        &       &AAFF5500 ; shiftable bit check pattern
+
+; init state with masked out page size
+
+ResetMemC_Value
+        & &E010C :OR: MEMCADR       ; slugged ROMs + flyback refresh only + 32K page
+
+; Constants
+;
+A0      *       1 :SHL: 00
+A1      *       1 :SHL: 01
+A2      *       1 :SHL: 02
+A3      *       1 :SHL: 03
+A4      *       1 :SHL: 04
+A5      *       1 :SHL: 05
+A6      *       1 :SHL: 06
+A7      *       1 :SHL: 07
+A8      *       1 :SHL: 08
+A9      *       1 :SHL: 09
+A10     *       1 :SHL: 10
+A11     *       1 :SHL: 11
+A12     *       1 :SHL: 12
+A13     *       1 :SHL: 13
+A14     *       1 :SHL: 14
+A15     *       1 :SHL: 15
+A16     *       1 :SHL: 16
+A17     *       1 :SHL: 17
+A18     *       1 :SHL: 18
+A19     *       1 :SHL: 19
+A20     *       1 :SHL: 20
+A21     *       1 :SHL: 21
+A22     *       1 :SHL: 22
+A23     *       1 :SHL: 23
+A24     *       1 :SHL: 24
+A25     *       1 :SHL: 25
+A26     *       1 :SHL: 26
+A27     *       1 :SHL: 27
+A28     *       1 :SHL: 28
+A29     *       1 :SHL: 29
+A30     *       1 :SHL: 30
+A31     *       1 :SHL: 31
+
+Page32K * &C ; in MEMC control reg patterns...
+Page16K * &8
+Page8K  * &4
+Page4K  * &0
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r0-r6 trashable
+;       r9 = Current MEMC CR (true MEMC value, not fudged to look like 4K page size)
+
+; Out   r9 MEMC value with slowest ROM speed, correct pagesize
+;       r7 processor speed in kHz, bit 16 => can do STM to I/O (ie MEMC1a, MEMC2), bit 17 => MEMC2
+
+; This routine must work in 32-bit mode
+
+ncpuloops * 1024 ; don't go longer than 4ms without refresh !
+nmulloops * 128
+
+TimeCPU ROUT
+ [ MEMC_Type = "IOMD"
+        LDR     r7, =(1 :SHL: 16) :OR: 16000    ; indicate 16MHz RAM
+ |
+        BIC     r9, r9, #3 :SHL: 8
+        STR     r9, [r9]                ; turn off refresh for a bit
+
+; Time CPU/Memory speed
+
+        LDR     r1, =&7FFE              ; 32K @ 2MHz = ~16ms limit
+        MOV     r3, #IOC
+
+        MOV     r0, r1, LSR #8
+        STRB    r1, [r3, #Timer1LL]
+        STRB    r0, [r3, #Timer1LH]
+        LDR     r0, =ncpuloops
+        STRB    r0, [r3, #Timer1GO]     ; start the timer NOW
+        B       %FT10                   ; Looks superfluous, but is required
+                                        ; to get ncpuloops pipeline breaks
+
+10
+        SUBS    r0, r0, #1              ; 1S
+        BNE     %BT10                   ; 1N + 2S
+
+        STRB    r0, [r3, #Timer1LR]     ; latch count NOW
+        LDRB    r2, [r3, #Timer1CL]
+        LDRB    r0, [r3, #Timer1CH]
+        ADD     r2, r2, r0, LSL #8      ; count after looping is ...
+
+        SUB     r2, r1, r2              ; decrements !
+        MOV     r2, r2, LSR #1          ; IOC clock decrements at 2MHz
+
+; Time CPU/MEMC Multiply time
+
+        MOV     r4, #-1                 ; Gives worst case MUL
+
+        MOV     r0, r1, LSR #8
+        STRB    r1, [r3, #Timer1LL]
+        STRB    r0, [r3, #Timer1LH]
+        LDR     r0, =nmulloops
+        STRB    r0, [r3, #Timer1GO]     ; start the timer NOW
+        B       %FT20                   ; Looks superfluous, but is required
+                                        ; to get nmulloops pipeline breaks
+
+20
+        MUL     r5, r4, r4              ; 1S + 16I
+        MUL     r5, r4, r4              ; 1S + 16I
+        SUBS    r0, r0, #1              ; 1S
+        BNE     %BT20                   ; 1N + 2S
+
+        STRB    r0, [r3, #Timer1LR]     ; latch count NOW
+        LDRB    r4, [r3, #Timer1CL]
+        LDRB    r0, [r3, #Timer1CH]
+        ADD     r4, r4, r0, LSL #8      ; count after looping is ...
+
+        SUB     r4, r1, r4              ; decrements !
+        MOV     r4, r4, LSR #1          ; IOC clock decrements at 2MHz
+
+        ORR     r9, r9, #1 :SHL: 8      ; set refresh on flyback
+        STR     r9, [r9]                ; restore MEMC state a.s.a.p.
+
+; In ROM - each cpu loop took 4R cycles @ 8/f*500ns/cycle
+
+        LDR     r0, =4*(8*500/1000)*ncpuloops*1000
+        DivRem  r7, r0, r2, r1          ; r2 preserved
+        MOV     r0, #&80                ; At 8 MHz and below, run fast ROMs
+        LDR     r1, =8050               ; Over 8 MHz, need medium ROMs
+        CMP     r7, r1
+        MOVHI   r0, #&40
+        LDR     r1, =13000              ; Over 13 MHz, need slowest ROMs
+        CMP     r7, r1
+        MOVHI   r0, #&00
+        ORR     r9, r9, r0
+        STR     r9, [r9]                ; Set ROM speed appropriately
+
+ ASSERT ncpuloops = 8*nmulloops ; for given ratio cutoff <------------
+
+        MOV     r4, r4, LSL #10         ; *1024 to get resolution on divide
+        DivRem  r0, r4, r2, r1
+        LDR     r1, =1100               ; Cutoff point; MEMC1 longer than this
+        CMP     r0, r1
+        ORRLO   r7, r7, #1 :SHL: 16     ; Note MEMC1a prescence
+ ]
+        MOV     pc, lr
+
+; Typical figures give (in ROM at 8MHz):
+
+; MEMC1  2048 CPU, 2432 MEMC -> MUL ratio 1216
+; MEMC1a 2048       864                    432
+
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       SWI OS_MMUControl
+;
+; in:   r0 = 0 (reason code for modify control register)
+;       r1 = EOR mask
+;       r2 = AND mask
+;
+;       new control = ((old control AND r2) EOR r1)
+;
+; out:  r1 = old value
+;       r2 = new value
+
+        ^       0
+MMUCReason_ModifyControl        # 1
+MMUCReason_Unknown              # 0
+
+MMUControlSWI   ENTRY
+        BL      MMUControlSub
+        PullEnv
+        ORRVS   lr, lr, #V_bit
+        ExitSWIHandler
+
+MMUControlSub
+        CMP     r0, #MMUCReason_Unknown
+        ADDCC   pc, pc, r0, LSL #2
+        B       MMUControl_Unknown
+        B       MMUControl_ModifyControl
+
+MMUControl_Unknown
+        ADRL    r0, ErrorBlock_HeapBadReason
+ [ International
+        Push    lr
+        BL      TranslateError
+        Pull    lr
+ ]
+        SETV
+        MOV     pc, lr
+
+MMUControl_ModifyControl ENTRY "r3, r4"
+        MOV     r4, pc
+        ORR     lr, r4, #I_bit
+        TEQP    lr, #0                  ; disable IRQs while we modify soft copy
+        MOV     r3, #0
+        LDR     lr, [r3, #MMUControlSoftCopy]
+        AND     r2, r2, lr
+        EOR     r2, r2, r1
+        MOV     r1, lr
+        STR     r2, [r3, #MMUControlSoftCopy]
+
+        BIC     lr, r2, r1              ; lr = bits going from 0->1
+        TST     lr, #MMUC_C             ; if cache turning on
+        SetCop  r0, CR_IDCFlush, NE     ; then flush cache before we do it
+
+        SetCop  r2, CR_Control          ; write to control register
+
+        BIC     lr, r1, r2              ; lr = bits going from 1->0
+        TST     lr, #MMUC_C             ; if cache turning off
+        SetCop  r0, CR_IDCFlush, NE     ; then flush cache afterwards
+
+        TEQP    r4, #0                  ; restore IRQ state
+        EXIT
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       Exception veneers
+
+; Undefined instruction trap pre-veneer
+; in:   r13_undef -> a FD stack
+;       r14_undef -> undefined instruction +4
+;       psr_undef = PSR at time of undef'd instruction
+
+UndPreVeneer    ROUT
+
+; entered in undef32, and we've branched up to &FF80xxxx, so we need to jump
+; back to normal ROM space
+
+        BIC     pc, pc, #&FC000000      ; remove silly bits from pc
+        NOP                             ; (this instruction skipped)
+
+        Push    "r0-r7,r14"             ; push r0-r7 on undef stack, and make room for return address
+        MOV     r0, r13_undef
+
+; for the time being just merge lr and psr
+
+        mrs     AL, r1, SPSR_all                        ; r1 = saved PSR
+        AND     r2, r1, #&F0000003                      ; get saved NZCV and 26 bit modes
+        ORR     lr_undef, lr_undef, r2
+        AND     r2, r1, #I32_bit + F32_bit              ; extract I and F from new place
+        ORR     r1, lr_undef, r2, LSL #IF32_26Shift     ; r1 = combined lr and psr
+
+        mrs     AL, r2, CPSR_all        ; now switch into SVC26
+        BIC     r3, r2, #&1F
+        ORR     r3, r3, #SVC26_mode
+        msr     AL, SPSR_all, r3        ; set SPSR_undef to be CPSR but with SVC26
+        msr     AL, CPSR_all, r3        ; and select this mode now
+
+        MOV     lr_svc, r1              ; lr_svc = PC + PSR from exception
+
+        msr     AL, CPSR_all, r2        ; go back into undef mode
+
+        LDR     r1, =UndHan             ; work out address of undefined instruction handler
+        LDR     r1, [r1]
+        STR     r1, [r0, #8*4]          ; and store it as return address
+        Pull    "r0-r7, pc",,^          ; exit to handler, restoring sp_undef and entering SVC26 mode
+
+; Instruction fetch abort pre-veneer
+
+PAbPreVeneer    ROUT
+
+; entered in abort32, and we've branched up to &FF80xxxx, so we need to jump
+; back to normal ROM space
+
+        BIC     pc, pc, #&FC000000      ; remove silly bits from pc
+        NOP                             ; (this instruction skipped)
+        LDR     r13_abort, =PreVeneerRegDump
+        STMIA   r13_abort, {r0-r7}
+        MOV     r0, r13_abort
+
+; for the time being just merge lr and psr
+
+        mrs     AL, r1, SPSR_all                        ; r1 = saved PSR
+        AND     r2, r1, #&F0000003                      ; get saved NZCV and 26 bit modes
+        ORR     lr_abort, lr_abort, r2
+        AND     r2, r1, #I32_bit + F32_bit              ; extract I and F from new place
+        ORR     r1, lr_abort, r2, LSL #IF32_26Shift     ; r1 = combined lr and psr
+
+        mrs     AL, r2, CPSR_all        ; now switch into SVC26
+        BIC     r2, r2, #&1F
+        ORR     r2, r2, #SVC26_mode
+        msr     AL, CPSR_all, r2
+
+        MOV     lr_svc, r1              ; lr_svc = PC + PSR from exception
+        LDR     r1, =PAbHan
+        LDR     r1, [r1]
+        STR     r1, [r0, #8*4]
+        LDMIA   r0, {r0-r7, pc}         ; jump to prefetch abort handler
+
+; Preliminary layout of abort indirection nodes
+
+        ^       0
+AI_Link #       4
+AI_Low  #       4
+AI_High #       4
+AI_WS   #       4
+AI_Addr #       4
+
+DAbPreVeneer    ROUT
+
+; entered in abort32, and we've branched up to &FF80xxxx, so we need to jump
+; back to normal ROM space
+
+        BIC     pc, pc, #&FC000000              ; remove silly bits from pc
+        NOP                                     ; (this instruction skipped)
+        LDR     r13_abort, =PreVeneerRegDump
+        STMIA   r13_abort, {r0-r7}              ; save unbanked registers anyway
+        STR     lr_abort, [r13_abort, #15*4]    ; save old PC, ie instruction address
+
+        mrs     AL, r0, SPSR_all                ; r0 = PSR when we aborted
+        mrs     AL, r1, CPSR_all                ; r1 = CPSR
+        ADD     r2, r13_abort, #8*4             ; r2 -> saved register bank for r8 onwards
+
+        MOV     r4, lr_abort                    ; move address of aborting instruction into an unbanked register
+        BIC     r1, r1, #&1F                    ; knock out current mode bits
+        ANDS    r3, r0, #&1F                    ; extract old mode bits (and test for USR26_mode (=0))
+        TEQNE   r3, #USR32_mode                 ; if usr26 or usr32 then use ^ to store registers
+        STMEQIA r2, {r8-r14}^
+        BEQ     %FT05
+
+        ORR     r3, r3, r1                      ; and put in user's
+        msr     AL, CPSR_all, r3                ; switch to user's mode
+
+        STMIA   r2, {r8-r14}                    ; save the banked registers
+
+        mrs     AL, r5, SPSR_all                ; get the SPSR for the aborter's mode
+        STR     r5, [r2, #8*4]                  ; and store away in the spare slot on the end
+                                                ; (this is needed for LDM with PC and ^)
+05
+        ORR     r1, r1, #SVC26_mode
+        msr     AL, CPSR_all, r1                ; then switch to SVC26 for the rest of this
+
+        Push    "r0, lr_svc"                    ; save SPSR_abort and lr_svc
+        SUB     sp_svc, sp_svc, #8*4            ; make room for r8_usr to r14_usr and PC
+        STMIA   sp_svc, {r8-r15}^               ; save USR bank in case STM ^, and also so we can corrupt them
+
+        SUB     r11, r2, #8*4                   ; r11 -> register bank
+        STR     r4, [sp_svc, #7*4]              ; store aborter's PC in user register bank
+
+        LDR     r10, [r4, #-8]!                 ; r10 = actual instruction that aborted, and r4 points to it
+        AND     r9, r10, #&0E000000
+        TEQ     r9, #&08000000                  ; test for LDM/STM
+        BNE     %FT50                           ; if not LDM/STM, then it's an "easy" LDR/STR
+
+;        Write   "It's an LDM/STM"
+
+ [ DebugAborts
+        DLINE   "It's an LDM/STM"
+ ]
+
+; First count the number of transferred registers, and undo any writeback
+
+        MOV     r9, #0                          ; r9 = no. of registers in list
+        MOVS    r8, r10, LSL #16
+        BEQ     %FT20
+10
+        MOVS    r8, r8, LSL #1
+        ADDCS   r9, r9, #1
+        BNE     %BT10
+20
+        MOV     r8, r10, LSR #16
+        AND     r8, r8, #&0F                    ; base register number
+        LDR     r7, [r11, r8, LSL #2]           ; ------""----- value
+
+        TST     r10, #1 :SHL: 23                ; test up/down
+        SUBNE   r6, r7, r9, LSL #2              ; if up then subtract offset
+        ADDEQ   r6, r7, r9, LSL #2              ; else add it
+
+        TST     r10, #1 :SHL: 21                ; test if write-back bit set
+        TEQNE   r8, #15                         ; (if base is PC then write-back not allowed)
+        MOVEQ   r6, r7                          ; if not wb, original base register is OK
+
+        MOV     r1, sp                          ; r1 -> end of stack frame, and start of user-mode register bank
+        SUB     sp, sp, r9, LSL #2              ; make stack frame for registers
+        TST     r10, #1 :SHL: 20                ; if its an STM, we have to load up the stack frame
+        BNE     %FT30                           ; but if it's an LDM, we call trap routine first
+
+        STR     r6, [r11, r8, LSL #2]           ; store original base in register list, to be overwritten after 1st transfer
+
+; now go through registers, storing them into frame
+
+        MOV     r5, sp                          ; pointer to position in stack frame
+        MOV     lr, r10, LSL #16                ; extract bottom 16 bits
+        MOVS    lr, lr, LSR #16                 ; ie bitmask of which registers r0-r15 stored
+        BEQ     %FT30                           ; this shouldn't happen (it's illegal)
+
+        MOV     r3, r11                         ; current pointer into register bank
+21
+        TST     r10, #1 :SHL: 22                ; is it STM with ^
+        ANDNE   lr, lr, #&FF                    ; if so then extract bottom 8 bits (r0-r7 on 1st pass, r8-r15 on 2nd)
+22
+        MOVS    lr, lr, LSR #1                  ; shift bit into carry
+        LDRCS   r2, [r3], #4                    ; if set bit then transfer word from register bank
+        STRCS   r2, [r5], #4                    ; into stack frame
+        STRCS   r7, [r11, r8, LSL #2]           ; and after 1st transfer, store updated base into register bank
+        ADDCC   r3, r3, #4                      ; else just increment register bank pointer
+        BNE     %BT22                           ; if more bits to do, then loop
+
+        TEQ     r5, r1                          ; have we done all registers?
+        MOVNE   lr, r10, LSR #8                 ; no, then must have been doing STM with ^, and have some user-bank regs to store
+        MOVNE   r3, r1                          ; so point r3 at user-mode register bank
+        BNE     %BT21                           ; and go back into loop
+
+30
+
+; now work out address of 1st transfer
+
+        ANDS    r5, r10, #(3 :SHL: 23)          ; bit 24 set => pre, bit 23 set => inc
+        SUBEQ   r2, r6, r9, LSL #2              ; if post-dec, then 1st address = initial-nregs*4+4
+        ADDEQ   r2, r2, #4
+        BEQ     %FT32
+
+        CMP     r5, #2 :SHL: 23
+        MOVCC   r2, r6                          ; CC => post-inc, so 1st address = initial
+        SUBEQ   r2, r6, r9, LSL #2              ; EQ => pre-dec,  so 1st address = initial-nregs*4
+        ADDHI   r2, r6, #4                      ; HI => pre-inc,  so 1st address = initial+4
+32
+        ANDS    r0, r10, #1 :SHL: 20            ; r0 = 0 => STM
+        MOVNE   r0, #1                          ;    = 1 => LDM
+        LDR     r1, [r1, #8*4]                  ; get SPSR_abort
+        TST     r1, #3                          ; test if transfer took place in USR mode
+        ORRNE   r0, r0, #2                      ; if not then set bit 1 of flags word in r0
+        MOV     r1, sp                          ; block to transfer from/into
+        BIC     r2, r2, #3                      ; LDM/STM always present word-aligned address
+        MOV     r3, r9, LSL #2                  ; length of transfer in bytes, and r4 still points to aborting instruction
+        BL      ProcessTransfer
+        ADDVS   sp, sp, r9, LSL #2              ; if invalid transfer then junk stack frame
+        BVS     %FT90                           ; and generate an exception
+
+; we transferred successfully, so now check if LDM and load up register bank from block
+
+        TST     r10, #1 :SHL: 20
+        ADDEQ   sp, sp, r9, LSL #2              ; it's an STM, so junk stack frame and tidy up
+        BEQ     %FT70
+
+; now go through registers, loading them from frame
+
+        ADD     r1, sp, r9, LSL #2              ; r1 -> end of stack frame, and start of user-mode bank registers
+        MOV     r5, sp                          ; pointer to position in stack frame
+        MOV     r4, r10, LSL #16                ; extract bottom 16 bits
+        MOVS    r4, r4, LSR #16                 ; ie bitmask of which registers r0-r15 stored
+        BEQ     %FT40                           ; this shouldn't happen (it's illegal)
+
+        SUB     r3, r1, #8*4                    ; r3 -> notional start of user bank, if it began at r0 (it actually starts at r8)
+        MOV     r0, #0                          ; assume no user registers by default
+        TST     r10, #1 :SHL: 15                ; is PC in list
+        BNE     %FT34                           ; then can't be LDM of user bank
+        TST     r10, #1 :SHL: 22                ; is it LDM with ^
+        BEQ     %FT34                           ; no, then use main bank for all registers
+        LDR     r2, [r1, #8*4]                  ; get SPSR
+        ANDS    r2, r2, #3                      ; get bottom 2 bits of mode (EQ => USR26 or USR32)
+        BEQ     %FT34                           ; if USR mode then use main bank for all
+        TEQ     r2, #FIQ26_mode                 ; if FIQ mode then put r8-r14 in user bank
+        LDREQ   lr, =&7F00                      ; then put r8-r14 in user bank
+        LDRNE   lr, =&6000                      ; else put r13,r14 in user bank
+        AND     r0, r4, lr                      ; r0 = mask of registers to put into user bank
+        BIC     r4, r4, lr                      ; r4 = mask of registers to put into main bank
+        MOV     lr, #0
+34
+        MOVS    r4, r4, LSR #1                  ; shift bit into carry
+        LDRCS   r2, [r5], #4                    ; if set bit then transfer word from stack frame
+        STRCS   r2, [r11, lr, LSL #2]           ; into main register bank
+        MOVS    r0, r0, LSR #1                  ; shift bit into carry
+        LDRCS   r2, [r5], #4                    ; if set bit then transfer word from stack frame
+        STRCS   r2, [r3, lr, LSL #2]            ; into user register bank
+        ADD     lr, lr, #1
+        ORRS    r6, r0, r4                      ; have we finished both banks?
+        BNE     %BT34                           ; no, then loop
+
+; If LDM with PC in list, then add 4 to it, so the exit procedure is the same as if PC not loaded
+; Also, if it was an LDM with PC and ^, then we have to update the stacked SPSR
+
+40
+        MOV     sp, r1                          ; junk frame
+
+        TST     r10, #1 :SHL: 15                ; check PC in list
+        ADDNE   r2, r2, #4                      ; since PC is last, r2 will still hold the value loaded
+        STRNE   r2, [r11, #15*4]                ; store back into main register bank
+        TSTNE   r10, #1 :SHL: 22                ; now check LDM ^
+        BEQ     %FT70                           ; [not LDM with PC in list]
+
+        LDR     r9, [sp, #8*4]                  ; get SPSR_abort
+        AND     r8, r9, #&1F                    ; r8 = aborter's mode
+        TEQ     r8, #USR32_mode                 ; if in USR32
+        BEQ     %FT70                           ; then the ^ has no effect (actually uses CPSR)
+        TST     r8, #&10                        ; if 32-bit mode
+        LDRNE   r7, [r11, #16*4]                ; then use SPSR for the aborter's mode else use updated r15 in r2 (26-bit format)
+        ANDEQ   r7, r2, #&F0000003              ; flag and mode bits in same place
+        ANDEQ   r2, r2, #&0C000000              ; but I and F have to move to bits 7 and 6
+        ORREQ   r7, r7, r2, LSR #(26-6)
+
+; r7 is now desired PSR (in 32-bit format) to update to
+; now check which bits can actually be updated
+
+        TEQ     r8, #USR26_mode
+        BICEQ   r9, r9, #&F0000000              ; if USR26 then we can only update NZCV
+        ANDEQ   r7, r7, #&F0000000
+        ORREQ   r9, r9, r7
+        MOVNE   r9, r7                          ; else can update all bits
+        STR     r9, [sp, #8*4]                  ; store back updated SPSR_abort (to become CPSR)
+        B       %FT70                           ; now tidy up
+
+50
+
+; it's an LDR/STR - first work out offset
+
+ [ DebugAborts
+        DLINE   "It's an LDR/STR"
+ ]
+
+        TST     r10, #1 :SHL: 25                ; if immediate
+        MOVEQ   r9, r10, LSL #(31-11)           ; then extract bottom 12 bits
+        MOVEQ   r9, r9, LSR #(31-11)
+        BEQ     %FT60
+
+        AND     r8, r10, #&0F                   ; register to shift
+        LDR     r9, [r11, r8, LSL #2]           ; get actual value of register
+
+        MOV     r8, r10, LSR #7                 ; extract shift amount
+        ANDS    r8, r8, #&1F                    ; (bits 7..11)
+        MOVEQ   r8, #32                         ; if zero then make 32
+
+        ANDS    r7, r10, #&60
+        ANDEQ   r8, r8, #&1F                    ; LSL 0 is really zero
+        MOVEQ   r9, r9, LSL r8
+        TEQ     r7, #&20
+        MOVEQ   r9, r9, LSR r8
+        TEQ     r7, #&40
+        MOVEQ   r9, r9, ASR r8
+        TEQ     r7, #&60
+        MOVEQ   r9, r9, ROR r8                  ; if 32 then we haven't spoilt it!
+        TEQEQ   r8, #32                         ; if ROR #32 then really RRX
+        BNE     %FT60
+        LDR     r7, [sp, #8*4]                  ; get SPSR
+        AND     r7, r7, #C_bit
+        CMP     r7, #1                          ; set carry from original user
+        MOV     r9, r9, RRX
+60
+        TST     r10, #1 :SHL: 23                ; test for up/down
+        RSBEQ   r9, r9, #0                      ; if down then negate
+
+ [ LateAborts
+        TST     r10, #1 :SHL: 21                ; if write-back
+        MOVNE   r8, #0                          ; then no post-inc
+        RSBEQ   r8, r9, #0                      ; else post-inc = - pre-inc
+        ADD     r0, r8, r9                      ; amount to subtract off base register for correction
+
+        TST     r10, #1 :SHL: 24                ; however, if we're doing post-increment
+        MOVEQ   r8, r9                          ; then post-inc = what was pre-inc
+        MOVEQ   r0, r9                          ; and adjustment is what was added on
+        RSB     r9, r8, #0                      ; and pre-inc = -post-inc
+ |
+        TST     r10, #1 :SHL: 21                ; if write-back
+        MOVNE   r8, #0                          ; then no post-inc
+        RSBEQ   r8, r9, #0                      ; else post-inc = - pre-inc
+
+        TST     r10, #1 :SHL: 24                ; however, if we're doing post-increment
+        MOVEQ   r8, r9                          ; then post-inc = what was pre-inc
+        MOVEQ   r9, #0                          ; and pre-inc = 0
+ ]
+
+        MOV     r7, r10, LSL #31-19
+        MOV     r7, r7, LSR #28                 ; r7 = base register number
+        LDR     r6, [r11, r7, LSL #2]           ; r6 = base register value
+
+ [ LateAborts
+        SUB     r0, r6, r0                      ; compute adjusted base register
+        STR     r0, [r11, r7, LSL #2]           ; and store back in case we decide to abort after all
+ ]
+
+; no need to clear PSR bits out of R15, because PSR is separate
+
+        ADD     r9, r9, r6                      ; r2 = offset+base = illegal address
+
+ [ DebugAborts
+        DREG    r9, "Aborting address = "
+        DREG    r8, "Post-increment = "
+        DREG    r4, "Instruction where abort happened = "
+ ]
+
+        ANDS    r0, r10, #1 :SHL: 20            ; if an LDR then bit 20 set
+        MOVNE   r0, #1                          ; so make 1
+        SUBNE   sp, sp, #4                      ; then just create 1 word stack frame
+        BNE     %FT65
+
+        MOV     r5, r10, LSR #12                ; else it's an STR (r0 = 0)
+        AND     r5, r5, #&0F                    ; r5 = source register number
+        LDR     r5, [r11, r5, LSL #2]           ; r5 = value of source register
+ [ DebugAborts
+        DREG    r5, "Data value to store = "
+ ]
+        Push    "r5"                            ; create stack frame with this value in it
+65
+        LDR     r1, [sp, #(1+8)*4]              ; get SPSR_abort
+        TST     r1, #3                          ; test if transfer took place in USR mode
+        ORRNE   r0, r0, #2                      ; if not then set bit 1 of flags word in r0
+
+        MOV     r1, sp                          ; r1 -> data block
+        TST     r10, #1 :SHL: 22                ; if byte transfer
+        MOVNE   r3, #1                          ; then length of transfer = 1
+        MOVNE   r2, r9                          ; and use unmolested address
+        MOVEQ   r3, #4                          ; else length = 4
+        BICEQ   r2, r9, #3                      ; and mask out bottom 2 bits of address
+
+        BL      ProcessTransfer
+        ADDVS   sp, sp, #4                      ; if illegal transfer, junk stack frame
+        BVS     %FT90                           ; and cause exception
+
+        ADD     r6, r9, r8                      ; update base register with offset
+        STR     r6, [r11, r7, LSL #2]           ; and store back (NB if LDR and dest=base, the load overwrites the updated base)
+
+        TST     r10, #1 :SHL: 20                ; if it's STR (not LDR)
+        ADDEQ   sp, sp, #4                      ; then junk stack frame
+        BEQ     %FT70                           ; and tidy up
+
+        Pull    "r6"                            ; LDR/LDRB, so get value to load into register
+        TST     r10, #1 :SHL: 22                ; if LDRB
+        ANDNE   r6, r6, #&FF                    ; then put zero in top 3 bytes of word
+        ANDEQ   r9, r9, #3                      ; else rotate word to correct position - r9 = bottom 2 bits of address
+        MOVEQ   r9, r9, LSL #3                  ; multiply by 8 to get rotation factor
+        MOVEQ   r6, r6, ROR r9                  ; rotate to correct position in register
+
+        MOV     r5, r10, LSR #12                ; test for LDR PC
+        AND     r5, r5, #&0F                    ; r5 = dest register number
+        TEQ     r5, #15                         ; if PC
+        ADDEQ   r6, r6, #4                      ; then adjust for abort exit
+        STR     r6, [r11, r5, LSL #2]           ; store into register bank
+
+70
+
+; Tidy up routine, common to LDR/STR and LDM/STM
+
+        ADD     r2, r11, #8*4                   ; point r2 at 2nd half of main register bank
+        LDMIA   sp, {r8-r14}^                   ; reload user bank registers
+        NOP                                     ; don't access banked registers after LDM^
+        ADD     sp, sp, #8*4                    ; junk user bank stack frame
+
+        Pull    "r0, lr"                        ; r0 = (possibly updated) SPSR_abort, restore lr_svc
+
+        SetMode ABT32_mode, r1                  ; leaves r1 = current PSR
+
+        mrs     AL, r6, SPSR_all                ; get original SPSR, with aborter's original mode
+        AND     r7, r6, #&1F
+        TEQ     r7, #USR26_mode
+        TEQNE   r7, #USR32_mode                 ; test also for USR32
+        LDMEQIA r2, {r8-r14}^                   ; if user mode then just use ^ to reload registers
+        NOP
+        BEQ     %FT80
+
+        ORR     r6, r6, #I32_bit                ; use aborter's flags and mode but set I
+        msr     AL, CPSR_all, r6                ; switch to aborter's mode
+        LDMIA   r2, {r8-r14}                    ; reload banked registers
+        msr     AL, CPSR_all, r1                ; switch back to ABT32
+
+80
+        LDR     lr_abort, [r13_abort, #15*4]    ; get PC to return to
+        msr     AL, SPSR_all, r0                ; set up new SPSR (may have changed for LDM {PC}^)
+
+        LDMIA   r13_abort, {r0-r7}              ; reload r0-r7
+        SUBS    pc, lr_abort, #4                ; go back 8 to adjust for PC being 2 words out,
+                                                ; then forward 4 to skip instruction we've just executed
+
+; Call normal exception handler
+
+90
+
+; for the time being just merge lr and psr
+
+        LDR     r0, [sp, #8*4]                          ; r0 = original SPSR (can't have been modified)
+
+        LDR     lr, [r11, #15*4]                        ; get PC of aborter
+        AND     r1, r0, #&F0000000                      ; get saved NZCV
+        ORR     lr, lr, r1
+        AND     r1, r0, #I32_bit + F32_bit              ; extract I and F from new place
+        ORR     lr, lr, r1, LSL #IF32_26Shift           ; and merge
+        AND     r1, r0, #3                              ; get old mode bits (have to assume a 26-bit mode!)
+        ORR     lr, lr, r1                              ; lr = combined lr and psr
+        STR     lr, [sp, #9*4]                          ; overwrite stacked lr_svc
+        TEQ     r1, #SVC26_mode                         ; if aborter was in SVC mode
+        STREQ   lr, [r11, #14*4]                        ; then also overwrite r14 in aborter's register bank
+
+        BIC     r0, r0, #&1F                            ; clear mode bits in SPSR
+        ORR     r0, r0, #SVC26_mode :OR: I32_bit        ; and force SVC26 with I set
+ [ DebugAborts
+        DLINE   "Going to call data abort handler"
+        DREG    lr, "lr_svc will be "
+        DREG    r0, "PSR going to exit with = "
+ ]
+        STR     r0, [sp, #8*4]                          ; overwrite stacked SPSR
+
+ [ NewStyle_All
+        MOV     r0, #0                                  ; we're going to call abort handler
+        STR     r0, [r0, #CDASemaphore]                 ; so allow recovery if we were in CDA
+ ]
+
+        CMP     r2, #&20                                ; if aborting address <&20
+        ADRCCL  r0, ABORTD                              ; then use MOS's default abort handler
+        LDRCS   r0, =DAbHan                             ; else use current handler
+        LDRCS   r0, [r0]                                ; get address of data abort handler
+ [ DebugAborts
+        DREG    r0, "Handler address = "
+ ]
+        ADD     r0, r0, #4                              ; add on 4 to adjust for abort exit
+        STR     r0, [r11, #15*4]                        ; and store in pc in register bank
+        B       %BT70                                   ; then junk to normal tidy-up routine
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       ProcessTransfer - Process an abort transfer
+;
+; in:   r0 = flags
+;               bit 0 = 0 => Store to memory
+;                       1 => Load from memory
+;               bit 1 = 0 => Transfer executed in user mode
+;                       1 => Transfer executed in non-user mode
+;       r1 = block of data to transfer from/into
+;       r2 = illegal address
+;       r3 = length of transfer in bytes
+;       r4 -> instruction which aborted
+;       SVC26 mode
+;
+; out:  V=0 => transfer accomplished
+;       V=1 => transfer not accomplished
+;       All registers preserved
+;
+
+SectionSizeShift *      20
+SectionSize     *       1 :SHL: SectionSizeShift
+
+LargePageSizeShift *    16
+LargePageSize   *       1 :SHL: LargePageSizeShift
+
+SmallPageSizeShift *    12
+SmallPageSize   *       1 :SHL: SmallPageSizeShift
+
+ProcessTransfer ENTRY "r1-r7,r12"
+
+ [ DebugAborts
+        DLINE   "ProcessTransfer entered"
+        DREG    r2, "Illegal address = "
+        DREG    r3, "Length of transfer = "
+        DREG    r4, "Abort happened at address "
+        DREG    r0, "Flags = "
+        DLINE   "Data = ",cc
+
+        MOV     r5, r3
+        MOV     r6, r1
+01
+        LDR     r7, [r6], #4
+        DREG    r7," ",cc
+        SUBS    r5, r5, #4
+        BHI     %BT01
+        DLINE   ""
+ ]
+
+
+; First identify if start address should have aborted
+
+10
+        LDR     r7, =L1PT
+        MOV     lr, r2, LSR #SectionSizeShift           ; r2 as a multiple of 1Mb
+        EOR     r5, r2, lr, LSL #SectionSizeShift       ; r5 = offset within section
+        SUB     r5, r2, r5                              ; r5 -> start of section containing r2
+        ADD     r5, r5, #SectionSize                    ; r5 -> start of section after r2
+
+        LDR     lr, [r7, lr, LSL #2]                    ; get L1PT entry
+        ANDS    r7, lr, #3                              ; 00 => trans.fault, 01 => page, 10 => section, 11 => reserved (fault)
+        TEQNE   r7, #3
+        BEQ     Fault
+        TEQ     r7, #1
+        BEQ     CheckPage
+
+; it's section mapped - check section access privileges
+
+15
+        ANDS    r7, lr, #3 :SHL: 10                     ; extract ap
+        BEQ     Fault                                   ; 00 => no access for anyone (at the moment)
+        TST     r0, #2                                  ; test for non-usr access
+        BNE     %FT20                                   ; if non-usr then OK to access here
+        CMP     r7, #2 :SHL: 10
+        BCC     Fault                                   ; 01 => no usr access
+        BHI     %FT20                                   ; 11 => full user access, so OK
+        TST     r0, #1
+        BEQ     Fault                                   ; 10 => usr read-only, so stores not allowed
+
+; access OK, so copy up to end of section/sub-page
+
+20
+        TST     r0, #1                                  ; if load from memory
+        BNE     %FT60                                   ; then skip
+
+; it's a store to memory (may be a vector write)
+; do it in words if >= 4 bytes, so word writes to VIDC work for example
+
+25
+ [ {TRUE}
+        CMP     r2, #&1C                                ; if in abort area (but allow any access to &1C)
+ |
+        CMP     r2, #&20                                ; if in abort area
+ ]
+        CMPCC   r4, #ROM                                ; and executing out of RAM
+        MOVCC   r5, #&20                                ; then set end-of-section = 32
+        BCC     Fault                                   ; and check user list
+
+        SetMode SVC32_mode, lr                          ; go into SVC32 so we can poke vector area
+30
+        TEQ     r2, r5                                  ; have we gone onto a new block?
+        BEQ     %FT50                                   ; if so then exit if finished else go back to outer loop
+        SUBS    r3, r3, #4                              ; have we got at least a word to do?
+        LDRCS   lr, [r1], #4                            ; if so then copy word
+        STRCS   lr, [r2], #4
+        BHI     %BT30                                   ; and if not all done then loop
+        BEQ     %FT50                                   ; if all done then switch back to SVC26 and exit
+
+        ADDS    r3, r3, #4
+40
+        LDRB    lr, [r1], #1                            ; read byte from register bank
+        STRB    lr, [r2], #1                            ; and store to memory
+        SUBS    r3, r3, #1                              ; decrement byte count
+        BEQ     %FT50                                   ; if finished then switch back to SVC26 and exit
+        TEQ     r2, r5                                  ; have we gone onto a new block?
+        BNE     %BT40                                   ; no, then loop
+
+50
+        SetMode SVC26_mode, lr
+        CMP     r3, #0
+        BNE     %BT10
+        EXIT                                            ; exit (VC from CMP)
+
+; it's a load from memory
+
+60
+        LDRB    lr, [r2], #1                            ; read byte from memory
+        STRB    lr, [r1], #1                            ; and store to memory bank
+        SUBS    r3, r3, #1                              ; decrement byte count
+        EXIT    EQ                                      ; if finished then exit (VC from SUBS)
+        TEQ     r2, r5                                  ; have we gone onto a new block?
+        BNE     %BT60                                   ; no, then loop
+        B       %BT10                                   ; yes, then go back to start
+
+; it's page mapped, so check L2PT
+; lr = L1 table entry
+; We use the logical copy of physical space here, in order to access the entry pointed to by the L1 entry
+
+CheckPage
+        MOV     r5, r2, LSR #SmallPageSizeShift         ; r2 as a multiple of 4K
+        MOV     r5, r5, LSL #SmallPageSizeShift
+        ADD     r5, r5, #SmallPageSize                  ; if translation fault, then it applies to small page
+
+        MOV     lr, lr, LSR #10                         ; remove domain and U bits
+        MOV     lr, lr, LSL #10
+        ORR     lr, lr, #PhysSpace                      ; now physical address is converted to a logical one (in physspace)
+        AND     r7, r2, #&000FF000                      ; extract bits which are to form L2 offset
+
+        LDR     lr, [lr, r7, LSR #10]                   ; lr = L2PT entry
+        ANDS    r7, lr, #3                              ; 00 => trans.fault, 01 => large page
+                                                        ; 10 => small page, 11 => reserved (fault)
+        TEQNE   r7, #3
+        BEQ     Fault
+        TEQ     r7, #2                          ; if small page
+        MOVEQ   r7, #SmallPageSizeShift-2       ; then sub-page size = 1<<10
+        MOVNE   r7, #LargePageSizeShift-2       ; else sub-page size = 1<<14
+
+        MOV     r5, r2, LSR r7                  ; round down to start of sub-page
+        MOV     r5, r5, LSL r7
+        MOV     r6, #1
+        ADD     r5, r5, r6, LSL r7              ; then move on to start of next sub-page
+
+        MOV     r7, r2, LSR r7                  ; put sub-page number in bits 1,2
+        AND     r7, r7, #3                      ; and junk other bits
+        RSB     r7, r7, #3                      ; invert sub-page ordering
+        MOV     r7, r7, LSL #1                  ; and double it
+        MOV     lr, lr, LSL r7                  ; then shift up access privileges so that correct ones appear in bits 10,11
+        B       %BT15                           ; re-use code to check access privileges
+
+Fault
+        SUB     r5, r5, r2                      ; r5 = number of bytes we can do in this section/page/sub-page
+        Push    "r3"                            ; save number of bytes to do
+        CMP     r3, r5                          ; if more bytes than there are in this block
+        MOVHI   r3, r5
+
+; Now scan list of user abort addresses
+
+        MOV     r6, #0
+        LDR     r6, [r6, #AbortIndirection]
+        TEQ     r6, #0
+        BEQ     %FT85                           ; address not in any abort node
+75
+        LDR     r5, [r6, #AI_Low]
+        CMP     r2, r5
+        BCC     %FT80
+        LDR     r5, [r6, #AI_High]
+        CMP     r2, r5
+        BCS     %FT80
+
+        Push    "r3"                            ; save number of bytes we can do in this section/page/sub-page
+        SUB     r5, r5, r2                      ; number of bytes we can do for this node
+        CMP     r3, r5                          ; if bigger than the size of this node
+        MOVHI   r3, r5                          ; then restrict number of bytes
+
+        ADD     r5, r6, #AI_WS
+        MOV     lr, pc
+        LDMIA   r5, {r12, pc}
+
+; returns to here
+
+        ADDVS   sp, sp, #8                      ; if user abort failed, then junk both pushed r3's
+        EXIT    VS                              ; and exit
+
+        ADD     r1, r1, r3                      ; advance register block
+        ADD     r2, r2, r3                      ; and illegal address pointer
+
+        LDR     r5, [sp, #4]                    ; subtract amount done from stacked total amount to do
+        SUBS    r5, r5, r3
+        STR     r5, [sp, #4]                    ; and store back
+
+        Pull    "r5"
+        SUBS    r3, r5, r3                      ; is there more to do in this section/page/sub-page?
+        BEQ     %FT90                           ; no then skip
+80
+        LDR     r6, [r6, #AI_Link]              ; else try next node
+        TEQ     r6, #0
+        BNE     %BT75
+85
+        ADD     sp, sp, #4                      ; junk pushed r3
+        SETV                                    ; indicate access invalid
+        EXIT                                    ; and exit
+
+90
+        Pull    "r3"                            ; restore total amount left to do
+        TEQ     r3, #0
+        BNE     %BT10                           ; yes, then loop
+        EXIT                                    ; no, then exit (V=0 from SUBS)
+
+ [ DebugAborts
+        InsertDebugRoutines
+ ]
+        END
diff --git a/s/Arthur2 b/s/Arthur2
new file mode 100644
index 0000000000000000000000000000000000000000..922ae00728edc1a7899301be1cb07d362fe70cac
--- /dev/null
+++ b/s/Arthur2
@@ -0,0 +1,2253 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL  => Arthur2
+
+;        GET  $.Hdr.Variables  - got at start
+
+        MACRO
+$l      GSVarGetWSpace
+$l      LDR R12, =GSVarWSpace
+        MEND
+
+        GBLL    LongVars
+LongVars SETL {TRUE}            ; Long system variables (>255 chars)
+
+        GBLL    QuickIndex
+QuickIndex SETL {TRUE}          ; Quick index to binary chop on lookup
+
+        GBLL    DebugSysVars
+DebugSysVars SETL {FALSE}
+
+
+
+;-----------------------------------------------------------------------------------
+;
+; This file covers:
+;   System variables:
+;     InitVariables
+;     OS_ReadVarVal
+;     OS_SetVarVal
+;   GSTrans:
+;     OS_GSInit
+;     OS_GSRead
+;     OS_GSTrans
+;   OS_BinaryToDecimal
+; These have been grouped because GSTrans makes direct use of the system variables'
+; structures and OS_BinaryToDecimal is used by readvarval.
+;
+; The system variables are stored as a one way sorted alphabetically linked list hanging
+; off the zero-page location VariableList:
+;
+ [ QuickIndex
+; VariableList---->sorted table of pointers to variable blocks
+ |
+; VariableList---->1st variable--->2nd variable--....-->last variable--->||
+ ]
+;
+; The end is indicated by the link having the value 0.
+;
+; Each variable is stored in one block in the system heap. The format of each block is:
+;
+; Bytes         Use
+ [ :LNOT: QuickIndex
+; 4             Link. Points to next variable in the list. 0 indicates no more.
+ ]
+; N+1           Variable's name (length N with terminator).
+; 1             Variable's type:
+;                       0       string
+;                       1       number
+;                       2       macro
+;                       3       expanded (not valid within sysvar structure)
+;                       16      code
+; M             data - depends on the variable's type
+;
+; The structure of the data is as follows:
+;
+; Type 0 - string
+; Bytes         Use
+; 1             Length (N)
+; N             the bytes of the string - may contain any characters
+;
+; Type 1 - number
+; Bytes         Use
+; 4             its value (not necessarily word aligned)
+;
+; Type 2 - macro
+; Bytes         Use
+; 1             Length (N)
+; N             the bytes of the string - must be a valid GSTransable string
+;                       including terminator
+;
+; Type 16 - code
+; Bytes         Use
+; x             Sufficient to word align...
+; 4             Write entry
+; 4             Read entry
+; N             The rest of the code
+
+InitVariables  ROUT
+        Push  "lr"
+
+        ; Blank the sysvar list
+        MOV    R0, #0
+        LDR    R12, =VariableList
+        STR    R0, [R12]
+
+        ; Set up the preset system variables
+        ADR    R0, SystemVarList       ; R0 pointer to name
+01      MOV    R1, R0
+        LDRB   R2, [R1], #1
+        CMP    R2, #0
+        Pull  "PC", EQ
+02      LDRB   R3, [R1], #1
+        CMP    R3, #0
+        BNE    %BT02
+        LDRB   R4, [R1], #1            ; get type
+        ADD    R1, R1, #3
+        BIC    R1, R1, #3
+        LDR    R2, [R1], #4
+        SWI    XOS_SetVarVal
+        ADD    R0, R1, R2
+        B      %BT01
+
+        LTORG
+
+; System vars have Thunks :
+; read thunk returns R0 ptr to string, R2 length. R1 corruptible
+; set thunk takes R1 ptr to value, R2 length. Value is always a string.
+; Can corrupt R1, R2, R4, R10-12
+
+; The list of nodes to copy into RAM :
+; name, 0 , type, ALIGN, size of value, value
+
+SystemVarList  ROUT
+        =     "Sys$$Time", 0, VarType_Code
+        ALIGN
+        &      sv2-.-4
+        LDR    PC, %FT01
+        LDR    PC, %FT02
+01
+        &    SetTimeVar
+02
+        &    ReadTimeVar
+
+sv2     =     "Sys$$Year", 0, VarType_Code
+        ALIGN
+        &      sv3-.-4
+        LDR    PC, %FT03
+        LDR    PC, %FT04
+03
+        &    SetYear
+04
+        &    ReadYear
+
+sv3     =     "Sys$$Date", 0, VarType_Code
+        ALIGN
+        &      sv4-.-4
+        LDR    PC, %FT05
+        LDR    PC, %FT06
+05
+        &    SetDate
+06
+        &    ReadClock
+
+sv4     =     "Sys$$ReturnCode", 0, VarType_Code
+        ALIGN
+        &      sv5-.-4
+        LDR    PC, %FT07
+        LDR    PC, %FT08
+07
+        &    SetRC
+08
+        &    ReadRC
+
+sv5     =     "Sys$$RCLimit", 0, VarType_Code
+        ALIGN
+        &      sv6-.-4
+        LDR    PC, %FT09
+        LDR    PC, %FT10
+09
+        &    SetRCL
+10
+        &    ReadRCL
+
+sv6     =     "Alias$.", 0, VarType_String
+        ALIGN
+        &      sv7-.-4
+        =     "Cat ", 10
+
+sv7     =     "Sys$$DateFormat", 0, VarType_String
+        ALIGN
+        &      sv8-.-4
+
+        [ {TRUE}
+        =     "%24:%mi:%se %dy-%m3-%ce%yr", 10
+        |
+        =     "%w3,%dy %m3 %ce%yr.%24:%mi:%se", 10
+        ]
+
+sv8     =      0
+
+        ALIGN
+
+; Now the code for our system variables.
+
+ReadTimeVar
+        Push  "lr"       ; Subr to read hh:mm:ss from clock
+        BL     ReadClock
+        ADD    R0, R0, #16
+        MOV    R2, #8
+        Pull  "PC"
+SetTimeVar ROUT
+        Push  "R0, lr"
+        GSVarGetWSpace
+        ADD    R12, R12, #GSNameBuff
+        MOV    R11, #8
+        STRB   R11, [R12], #1
+        MOV    R10, #7
+01      LDRB   R11, [R1, R10]
+        STRB   R11, [R12, R10]
+        SUBS   R10, R10, #1
+        BPL    %BT01
+        SUB    R1, R12, #1
+        MOV    R0, #15
+        SWI    XOS_Word
+        Pull  "R0, PC"
+
+ReadYear
+        Push  "lr"       ; Subr to read yyyy from clock
+        BL     ReadClock
+        ADD    R0, R0, #11
+        MOV    R2, #4
+        Pull  "PC"
+SetYear ROUT
+        MOV    R2, #3    ; no chars -1
+        MOV    R4, #11   ; offset -1
+SetYD   Push  "R0-R2, lr"
+        BL     ReadClock
+        MOV    R10, #15
+        STRB   R10, [R0, #-1]
+        ADD    R0, R0, R4
+        LDR    R1, [stack, #4]
+        LDR    R2, [stack, #8]
+01      LDRB   R10, [R1, R2]
+        STRB   R10, [R0, R2]
+        SUBS   R2, R2, #1
+        BPL    %BT01
+        SUB    R1, R0, R4
+        SUB    R1, R1, #1
+        MOV    R0, #15
+        SWI    XOS_Word
+        Pull  "R0-R2, PC"
+
+ReadClock
+        Push  "lr"
+        GSVarGetWSpace       ; Subr to read ddd, nn mmm from clock
+        ADD    R1, R12, #GSNameBuff+1
+        MOV    R0, #0
+        STRB   R0, [R1]
+        MOV    R0, #14
+        SWI    XOS_Word
+        MOV    R0, R1
+        MOV    R2, #10      ; no. of chars.
+        Pull  "PC"
+SetDate MOV    R2, #9
+        MOV    R4, #0
+        B      SetYD
+
+ReadRC  ROUT
+        MOV    R0, #0
+        LDR    R0, [R0, #ReturnCode]
+        B      ReadNumSysVar
+SetRC   Push  "lr"
+        BL     SetNumSysVar
+        LDR    R4, =ReturnCode
+        STR    R2, [R4]
+        LDR    R4, =RCLimit
+        LDR    R4, [R4]
+        CMP    R2, R4
+        Pull  "lr", LS
+        BICLSS PC, lr, #V_bit
+        ADRGT  R0, ErrorBlock_RCExc
+        ADRLT  R0, ErrorBlock_RCNegative
+      [ International
+        BL     TranslateError
+      |
+        SETV
+      ]
+        Pull  "PC"
+        MakeErrorBlock RCExc
+        MakeErrorBlock RCNegative
+
+ReadRCL MOV    R0, #0
+        LDR    R0, [R0, #RCLimit]
+ReadNumSysVar
+        Push  "lr"
+        GSVarGetWSpace
+        ADD    R1, R12, #GSNameBuff
+        MOV    R2, #256
+        SWI    XOS_BinaryToDecimal
+        MOV    R0, R1
+        Pull  "PC"
+SetRCL  Push  "lr"
+        BL     SetNumSysVar
+        LDR    R4, =RCLimit
+        CMP    R2, #0              ; can't set -ve RCLimit
+        RSBMIS R2, R2, #0
+        MOVMI  R2, #0              ; BIC of MININT
+        STR    R2, [R4]
+        Pull  "PC"
+
+        LTORG
+
+SetNumSysVar   ROUT ; R1 ptr to string, R2 string length
+        Push  "lr"
+        SUBS   R2, R2, #1
+        ADDMI  R2, R2, #1     ; give 0 in R2 for bad length.
+        Pull  "PC", MI
+        LDR    R12, =GSNameBuff+GSVarWSpace
+03      LDRB   R10, [R1], #1        ; copy into a buffer so we can terminate it.
+        STRB   R10, [R12], #1
+        SUBS   R2, R2, #1
+        BPL    %BT03
+        MOV    R10, #13
+        STRB   R10, [R12], #1
+        LDR    R1, =GSNameBuff+GSVarWSpace
+        LDRB   R10, [R1]
+        MOV    R12, #0
+        CMP    R10, #"-"
+        MOVEQ  R12, #-1
+        CMPNE  R10, #"+"
+        ADDEQ  R1, R1, #1
+        MOV    R0, #0
+        SWI    XOS_ReadUnsigned
+        CMP    R12, #0
+        RSBMI  R2, R2, #0
+        Pull  "PC"
+
+
+;*****************************************************************************
+; GSINIT, GSREAD, GSTRANS
+
+; To enable GSTrans nesting to stand a chance of working don't flatten the
+; stack every FSINIT. Instead, pick up the stack pointer (any value is OK)
+; and wrap at 255. Stack overflow occurs if you increment the pointer to
+; where it started for this GSINIT, and stack underflow occurs if you
+; decrement the pointer when it is currently equal to stack limit.
+; The stack limit is held in the environment value, R2.
+; The stack is empty ascending.
+        GBLL    GS_BufferNotStack
+GS_BufferNotStack SETL {TRUE}
+
+; some semi-arbitrary flags
+GS_NoQuoteMess   * 1 :SHL: 31   ; flags passed in from user
+GS_NoVBarStuff   * 1 :SHL: 30
+GS_Spc_term      * 1 :SHL: 29   ; clear if user requested terminate on space
+GS_In_String     * 1 :SHL: 28   ; set if waiting for closing "
+GS_ReadingString * 1 :SHL: 27   ; set if reading chars from a string var.
+GS_Macroing      * 1 :SHL: 26   ; set if reading chars from a macro
+ [ GS_BufferNotStack
+        ASSERT  GS_StackPtr_Lim = &80
+GS_StackLimitBits * 7
+GS_StackLimitPos * 19           ; The bit position of the LSB of the byte
+                                ; which holds the stack limit
+; bits 0-18 hold the string length for string transfers
+ |
+; bits 24-25 are unused
+; bits 0-23 hold the string length for string transfers
+ ]
+
+; After GSINIT, R2 has these flags, and if expanding a count in the low byte
+
+GSINIT  ROUT
+;  In  : R0 pointer to string to expand
+;        R2 has flags :
+;          Bit 29 set means space is a terminator
+;          Bit 30 set means | characters will not be molested
+;          Bit 31 set means don't mess with quotes
+
+;  Out : R0, R2 are values to pass back in to GSREAD
+;        R1 is the first non-blank character
+;        EQ means char is CR or LF, i.e. string is empty.
+
+        ; Enable interupts as we've no right to have them disabled here
+        TEQP    pc, #SVC_mode
+
+ [ GS_BufferNotStack
+        AND     R2, R2, #GS_NoQuoteMess :OR: GS_NoVBarStuff :OR: GS_Spc_term
+                                      ; get caller's flags
+ ]
+
+; If no tokens to expand then don't reset evaluation stack
+; This prevents conflict with modules opening messages files at lower levels
+
+        Push    "r0"
+10      LDRB    r1, [r0], #1
+        CMP     r1, #13
+        CMPNE   r1, #10
+        CMPNE   r1, #0
+        Pull    "r0",EQ
+        BEQ     %FT20                   ; Jump if end of string, nothing to expand
+
+        TEQ     r1, #"<"                ; Possibly something to expand?
+        BNE     %BT10                   ; No then try next
+        Pull    "r0"
+
+; Expansion may be necessary so flatten evaluation stack
+
+ [ GS_BufferNotStack
+        GSVarGetWSpace
+        LDRB    R1, [R12, #GS_StackPtr]
+        AND     R1, R1, #(GS_StackPtr_Lim-1)     ; Ensure we remain in range
+        STRB    R1, [R12, #GS_StackPtr]
+        ORR     R2, R2, R1, LSL #GS_StackLimitPos
+ |
+        MOV     R1, #0
+        GSVarGetWSpace
+        STRB    R1, [R12, #GS_StackPtr]     ; no stacked R0s
+ ]
+
+20
+ [ GS_BufferNotStack
+ |
+        AND     R2, R2, #GS_NoQuoteMess :OR: GS_NoVBarStuff :OR: GS_Spc_term
+                                      ; get caller's flags
+ ]
+        EOR     R2, R2, #GS_Spc_term    ; and invert for convenience
+
+01      LDRB    R1, [R0], #1
+        CMP     R1, #" "
+        BEQ     %BT01
+        TST     R2, #GS_NoQuoteMess
+        CMPEQ   R1, #""""
+        SUBNE   R0, R0, #1            ; dec if went too far
+        ORREQ   R2, R2, #GS_In_String ; set flag if in string
+        CMP     R1, #13
+        CMPNE   R1, #10
+        CMPNE   R1, #0
+        ORREQ   lr, lr, #Z_bit    ; and move EQ/NE to return pc
+        BICNE   lr, lr, #Z_bit
+        ExitSWIHandler
+
+; -----------------------------------------------------------------------------
+
+
+GSREAD  ROUT
+;  In  : R0, R2 from last GSREAD/GSINIT
+;  Out : R1 character, R0, R2 updated.
+;        VS => "Bad String" error
+;        CS => string ended (in which case R1 = terminator)
+
+        ; enable interupts as (a) they'll get enabled by a <thing> entry
+        ; and (b) GSREAD may take some time
+        TEQP    pc, #SVC_mode
+
+        BIC     lr, lr, #C_bit
+        MOV     R10, #0
+        TST     R2, #GS_ReadingString
+        BNE     GSREAD_RStringGetNextByte         ; take byte from stringvar
+
+GSREAD_XPandGetNextByte
+        LDRB    R1, [R0], #1
+        CMP     R1, #13
+        CMPNE   R1, #10
+        CMPNE   R1, #0
+        BEQ     GSREAD_XPandGotToEnd
+        CMP     R1, #" "
+        BEQ     GSREAD_XPandGotSpace
+        BLT     GSREAD_BadStringError   ; bad string : control code in string
+        CMP     R1, #""""
+        BEQ     GSREAD_XPandGotQuote
+        CMP     R1, #"|"
+        TSTEQ   R2, #GS_NoVBarStuff
+        BEQ     GSREAD_WorkOutBarChar
+        CMP     R1, #"<"
+        BNE     GSREAD_ReturnWithChar   ; OS_Exit with valid character
+
+; got to try and get a variable value.
+        Push    "R0, R2, lr"
+        LDRB    R1, [R0]
+        CMP     R1, #">"
+        CMPNE   R1, #" "
+        BEQ     GSREAD_AngleBraDaftSoIsnt  ; <> and < > are silly.
+
+        ; Copy angle bracketed thing checking for correct termination
+        GSVarGetWSpace
+        ADD     R12, R12, #GSNameBuff
+        MOV     R11, #0
+20      LDRB    R1, [R0], #1
+        STRB    R1, [R12], #1
+        ADD     R11, R11, #1
+        CMP     R11, #255
+        CMPNE   R1, #13
+        CMPNE   R1, #10
+        CMPNE   R1, #0
+        BEQ     GSREAD_AngleBraDaftSoIsnt
+        CMP     R1, #">"
+        BNE     %BT20
+
+        ; Check for number first
+        MOV     R1, #0
+        STRB    R1, [R12, #-1]          ; terminate it
+        SUB     R1, R12, R11            ; pointer to name or number
+        Push    "R0"
+        SWI     XOS_ReadUnsigned        ; check for number
+        Pull    "R0"
+        BVS     GSREAD_AngledThingAintNumber   ; silly - has to be name
+        LDRB    R1, [R1]                ; check terminated by the null
+        CMP     R1, #0
+        BNE     GSREAD_AngledThingAintNumber
+        MOV     R1, R2                  ; character value
+        ADD     stack, stack, #4        ; discard old R0 value.
+        Pull    "R2, lr"
+        B       GSREAD_ReturnWithChar   ; exit-R1's the char value.
+
+GSREAD_AngledThingAintNumber
+        ; R0, R2, lr on stack
+        Push    "R0, R3, R4, R10"       ; corrupted by VarFindIt
+        MOV     R3, #0                 ; context ptr
+        SUB     R0, R12, R11           ; name ptr
+        BL      VarFindIt
+        Pull    "R0, R3, R4, R10", EQ    ; not found mate
+        BEQ     GSREAD_AngledThingNotThere  ; return null expansion
+; well, we've found it - better stack old R0
+        Pull    "R0"
+        GSVarGetWSpace
+ [ GS_BufferNotStack
+        LDRB    r1, [r12, #GS_StackPtr]
+        LDR     r2, [sp, #4*4]          ; r3,r4,r10,r0,r2,lr on stack, hence r2 retrieved
+        MOV     r2, r2, ASL #32-(GS_StackLimitPos+GS_StackLimitBits)
+        SUB     r2, r2, #1:SHL:(32-GS_StackLimitBits)
+        TEQ     r1, r2, LSR #32-GS_StackLimitBits
+        BEQ     GSREAD_CantNestMore
+ |
+        LDRB    R1, [R12, #GS_StackPtr]
+        CMP     R1, #GS_StackPtr_Lim
+        BHS     GSREAD_CantNestMore
+ ]
+        ADD     R12, R12, #GS_Stack
+        STR     R0, [R12, R1, LSL #2]
+        ADD     R1, R1, #1
+ [ GS_BufferNotStack
+        AND     R1, R1, #(GS_StackPtr_Lim-1)
+ ]
+        STRB    R1, [R12, #GS_StackPtr-GS_Stack]
+        MOV     R0, R4
+        LDRB    R1, [R0], #1          ; type
+        CMP     R1, #VarType_Code
+        BEQ     GSREAD_CallCodeVar
+        CMP     R1, #VarType_Number
+        LDRB    R1, [R0], #1
+
+ [ LongVars
+        LDRB    R3, [R0], #1
+        ORR     R1, R1, R3, LSL #8
+        LDRB    R3, [R0], #1
+        ORR     R1, R1, R3, LSL #16
+
+        BLO     GSREAD_GotVarAsString
+        BHI     GSREAD_GotMacroVar
+ |
+        BLO     GSREAD_GotVarAsString
+        BHI     GSREAD_GotMacroVar
+
+        ; Got number var
+        LDRB    R3, [R0], #1          ; number - build value
+        ORR     R1, R1, R3, LSL #8
+        LDRB    R3, [R0], #1
+        ORR     R1, R1, R3, LSL #16
+ ]
+        LDRB    R3, [R0], #1
+        ORR     R1, R1, R3, LSL #24
+        MOV     R0, R1
+        ADD     R1, R12, #GSNameBuff-GS_Stack
+        MOV     R2, #256
+        SWI     XOS_BinaryToDecimal
+        MOV     R0, R1
+        MOV     R1, R2
+
+; it's a string variable, by now.
+GSREAD_GotVarAsString
+        Pull    "R3, R4, R10"
+        ADD     stack, stack, #4      ; discard that R0
+        Pull    "R2, lr"
+        CMP     R1, #0
+        BEQ     ZeroLengthVar
+        ORR     R2, R2, R1            ; old flags+new count
+        ORR     R2, R2, #GS_ReadingString
+        LDRB    R1, [R0], #1
+        B       GSREAD_ReturnWithChar
+
+GSREAD_GotMacroVar
+        ; Macro - R0 is now the ptr to the macro value.
+        Pull    "R3, R4, R10"
+        ADD     stack, stack, #4
+        Pull    "R2, lr"
+        ORR     R2, R2, #GS_Macroing
+        B       GSREAD_XPandGetNextByte   ; loop, transforming chars.
+
+GSREAD_CantNestMore
+        Pull    "R3, R4, R10"           ; no room to stack pointer, so don't expand
+GSREAD_AngledThingNotThere
+        ADD     stack, stack, #4       ; skip R0 - return null string
+        Pull    "R2, lr"
+        B       GSREAD_XPandGetNextByte   ; get next char
+
+GSREAD_AngleBraDaftSoIsnt
+        Pull    "R0, R2, lr"
+        MOV     R1, #"<"
+        B       GSREAD_ReturnWithChar   ; failed to get sensible variable
+
+GSREAD_XPandGotToEnd
+        TST     R2, #GS_In_String      ; got CR or LF
+        BNE     GSREAD_BadStringError  ; bad string
+        TST     R2, #GS_Macroing
+GSREAD_GotToAnEnd
+        ORREQ   lr, lr, #C_bit          ; got terminator
+        ExitSWIHandler EQ
+
+        ; Nest out by one level
+        GSVarGetWSpace
+        LDRB    R11, [R12, #GS_StackPtr]
+ [ GS_BufferNotStack
+        SUB     R11, R11, #1
+        AND     R11, R11, #(GS_StackPtr_Lim-1)
+        MOV     r2, r2, ROR #GS_StackLimitPos+GS_StackLimitBits
+        TEQ     r11, r2, LSR #32-GS_StackLimitBits
+        MOV     r2, r2, ROR #32-(GS_StackLimitPos+GS_StackLimitBits)
+ |
+        SUBS    R11, R11, #1
+ ]
+        BICEQ   R2, R2, #GS_Macroing
+        STRB    R11, [R12, #GS_StackPtr]
+        ADD     R12, R12, #GS_Stack
+        LDR     R0, [R12, R11, LSL #2]
+        B       GSREAD_XPandGetNextByte    ; return to prevstring
+
+GSREAD_XPandGotSpace
+        TST     R2, #(GS_In_String :OR: GS_Spc_term :OR: GS_Macroing)
+                                        ; got space : check termination
+        BEQ     GSREAD_GotToAnEnd      ; terminates
+
+GSREAD_ReturnWithChar
+        ORR     R1, R1, R10             ; valid character
+        ExitSWIHandler
+
+GSREAD_XPandGotQuote
+        TST     R2, #GS_In_String
+        BEQ     GSREAD_ReturnWithChar   ; if not in string, " is valid.
+        LDRB    R1, [R0], #1
+        CMP     R1, #""""               ; "" in string?
+        BEQ     GSREAD_ReturnWithChar   ; yup
+
+ [ Fix16
+
+; TMD 25-Sep-89: Fix termination here
+
+10
+        CMP     R1, #" "
+        LDREQB  R1, [R0], #1
+        BEQ     %BT10
+        SUB     R0, R0, #1
+ |
+
+10      LDRB    R1, [R0], #1
+        CMP     R1, #" "
+        BEQ     %BT10
+ ]
+        ORR     lr, lr, #C_bit          ; got terminator (second ")
+        ExitSWIHandler                  ; and out
+
+GSREAD_WorkOutBarChar
+        LDRB    R1, [R0], #1            ; got |, do traditional escaping
+        CMP     R1, #"|"
+        CMPNE   R1, #""""
+        CMPNE   R1, #"<"
+        BEQ     GSREAD_ReturnWithChar  ; || gives |, |" gives ", |< gives <
+        CMP     R1, #"?"
+        MOVEQ   R1, #&7F              ; delete
+        BEQ     GSREAD_ReturnWithChar ; valid ch
+        CMP     R1, #"!"
+        MOVEQ   R10, #&80
+        BEQ     GSREAD_XPandGetNextByte ; tbs char
+        CMP     R1, #" "
+        BLT     GSREAD_BadStringError   ; OS_Control character is naff
+        CMP     R1, #&7F              ; CTRL-delete is delete
+        EORGT   R1, R1, #&20           ; softkey
+        BGE     GSREAD_ReturnWithChar ; now valid ch
+        CMP     R1, #"`"              ; CTRL-` = CTRL-_
+        MOVEQ   R1, #"_"
+        CMP     R1, #"@"
+        ANDGE   R1, R1, #&1F           ; transform if @<=ch<delete
+        B       GSREAD_ReturnWithChar
+
+GSREAD_RStringGetNextByte
+        SUB     R2, R2, #1            ; we're reading a string
+ [ LongVars
+  [ GS_BufferNotStack
+        MOVS    R12, R2, ASL #32-GS_StackLimitPos
+  |
+        ANDS    r12, r2, #&00ffffff
+  ]
+ |
+        TST     R2, #&FF
+ ]
+        LDRNEB  R1, [R0], #1          ; and this is already expanded
+        ExitSWIHandler NE          ; so finished
+ZeroLengthVar
+        GSVarGetWSpace
+        LDRB    R0, [R12, #GS_StackPtr] ; pull an R0 from our stack
+        SUB     R0, R0, #1
+ [ GS_BufferNotStack
+        AND     R0, R0, #(GS_StackPtr_Lim-1)
+ ]
+        STRB    R0, [R12, #GS_StackPtr]
+        ADD     R12, R12, #GS_Stack
+        LDR     R0, [R12, R0, LSL #2]
+        BIC     R2, R2, #GS_ReadingString
+        B       GSREAD_XPandGetNextByte
+
+GSREAD_BadStringError
+        ADR     R0, BadStrErr
+      [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      ]
+        ORR     lr, lr, #V_bit :OR: C_bit
+        ExitSWIHandler
+
+BadStrErr
+        MakeErrorBlock BadString
+
+GSREAD_CallCodeVar
+        ADD     R0, R0, #3 + 4  ; 3 to ALIGN, 4 to get to read entry
+        MOV     lr, PC          ; get link
+        BIC     PC, R0, #3      ; call entrypoint to Read Thunk
+        MOV     R1, R2
+        B       GSREAD_GotVarAsString
+
+; ---------------------------------------------------------------------------
+
+
+GSTRANS ROUT                    ; enables interrupts
+; In   : R0 ptr to input string
+       ; R1 ptr to Out buffer
+       ; R2 max number of chars, with flags at top.
+
+; Out  : R0 points at terminator
+       ; R1 unchanged
+       ; R2 Number of chars got,
+       ;  C set if too many chars
+       ;  V set if bad string.
+
+        BIC      lr, lr, #C_bit
+        Push    "R1, R3-R5, lr"
+        TEQP     PC, #SVC_mode             ; enable ints.
+
+        MOV      R3, R1
+        MOV      R4, R1                    ; two copies of start ptr
+        BIC      R5, R2, #&E0000000
+        ADD      R5, R5, R1                 ; 1st byte we can't write to.
+        SWI      XOS_GSInit
+01      CMP      R3, R5
+        BGE      %FT03                    ; no rheum for byte.
+        SWI      XOS_GSRead
+        BVS      %FT02                    ; bad string
+        STRB     R1, [R3], #1
+        BCC      %BT01
+04      SUB      R2, R3, R4                 ; no chars got
+        SUB      R2, R2, #1
+        Pull    "R1, R3-R5, lr"
+        ExitSWIHandler
+
+02      SUB      R2, R3, R4
+        Pull    "R1, R3-R5, lr"
+        B       SLVK_SetV               ; bad string: error set by GSRead
+
+03      SUB      R2, R3, R4
+        Pull    "R1, R3-R5, lr"
+        ORR      lr, lr, #C_bit          ; buffer overflow
+        ExitSWIHandler
+
+;****************************************************************************
+; Read/Write variables
+; Also the binary->decimal SWI.
+; All the var SWIs enable interrupts - they all take quite a while.
+
+; Variable storage format is dead simple - linear list!
+; It's not even ordered in any fashion.
+; List has fixed section of system vars that cannot die.
+
+; Node format : Link, Name bytes, 0 terminator (so Write0able), type byte,
+;               then 4 bytes for numeric
+;               or  length byte, value bytes for string/macro.
+;               then a CR if it's a macro variable.
+
+ [ QuickIndex
+VNameOff *  0
+ |
+VarLink  *  0
+VNameOff *  4
+ ]
+
+; First the lookup SWI, ReadVarValue
+; In:
+;   R0 -> name; maybe wildcarded (* and #)
+;   R1 -> buffer
+;   R2 = buffer max length
+;   R3 = 0 or pointer to name returned from previous ReadVarVal
+;   R4 = VarType_Expanded or something else
+
+; Out:
+;  Not found:
+;   R0 -> VarCantFind error
+;   R1 unaltered
+;   R2 = 0
+;   R3,r4 trashed
+;   VSet
+;  Found, r2 < 0 and r4 <> VarType_Expanded on entry:
+;   R0, R1 unaltered
+;   R2 = NOT length of value
+;   R3 -> name of variable (0-terminated)
+;   R4 = type of result
+;  Found, r2 < 0 and r4 = VarType_Expanded on entry:
+;   R0, R1 unaltered
+;   R2 = -ve
+;   R3 -> name of variable (0-terminated)
+;   R4 = type of result
+;  Found, r2 >= 0 on entry:
+;   R0, R1 unaltered
+;   R2 no chars got
+;   R3 -> name of variable. Can be passed to this SWI to continue enumeration
+;      of wildcard.
+;   R4 type of result (VarType_String, VarType_Number, VarType_Macro)
+;   VSet if buffer overflowed (R0->error block)
+
+ReadVarValue ROUT
+        TEQP    PC, #SVC_mode   ; enable interupts (mode remains unchanged)
+        Entry   "r0,r1"
+
+        MOV     r11, r4
+
+        BL      VarFindIt       ; name=r0,context=r3 -> name found in node=r3,r4=after namein,r12=prev
+        BEQ     RVVNotFound
+
+        ; Regardless of expanded or not - always call the code to get value
+        LDRB    lr, [r4], #1
+        TEQ     lr, #VarType_Code
+        BEQ     ReadVarVal_CallCode
+
+        ; Check whether expanded value wanted and pick up found variable's type
+        TEQ     r11, #VarType_Expanded
+        MOV     r11, r4
+        MOV     r4, lr
+        BEQ     ReadVarVal_ExpandedWanted
+
+        ; Unexpanded value wanted....
+
+        ; If number then want 4 bytes, else however many there are in the varval
+        TEQ     r4, #VarType_Number
+        MOVEQ   r10, #4
+
+ReadVarVal_CopyStringVarToUserBuffer
+        ; R1 -> user buffer
+        ; R2 = user buffer size
+        ; R3 -> name of sysvar found
+        ; R4 = sysvar type to return
+        ; R10 = length to transfer to user buffer (EQ only)
+        ; R11 -> length byte(s) of sysvar (NE only)
+        ;     -> bytes string to transfer (EQ only)
+ [ LongVars
+        LDRNEB  r10, [r11], #1
+        LDRNEB  lr, [r11], #1
+        ORRNE   r10, r10, lr, ASL #8
+        LDRNEB  lr, [r11], #1
+        ORRNE   r10, r10, lr, ASL #16
+ |
+        LDRNEB  r10, [r11], #1
+ ]
+
+ReadVarVal_CopyR10BytesToUserBuffer
+        ; R1 -> user buffer
+        ; R2 = user buffer size
+        ; R3 -> name of sysvar found
+        ; R4 = type byte to be returned
+        ; R10 = bytes to be copied
+        ; R11 -> bytes to be copied
+
+        CMP     R10, R2
+        BGT     ReadVarVal_BufWillOFlow
+
+VarNoOFlo
+        ; Guaranteed the the buffer won't overflow now
+        MOV     R2, R10           ; bytes he's gonna get
+; now copy R10 bytes into buffer
+02      SUBS    R10, R10, #1
+        LDRPLB  R12, [R11, R10]
+        STRPLB  R12, [R1, R10]
+        BPL     %BT02
+
+ReadVarVal_OKExit
+        PullEnv
+        ExitSWIHandler
+
+ReadVarVal_BufWillOFlow
+        ; Have determined that the buffer will overflow, so generate an error
+        ; and shorten down to the buffer's size
+        ADR     r0, BufferOFloError
+    [ International
+        BL      TranslateError
+    ]
+        STR     r0, [stack, #Proc_LocalStack + 0*4]
+        LDR     lr, [stack, #Proc_LocalStack + 2*4]
+        ORR     lr, lr, #V_bit     ; set for return
+        STR     lr, [stack, #Proc_LocalStack + 2*4]
+
+        ; ensure NOT length returned in r2 when returning with r2<0 on entry
+        CMP     r2, #0
+        MVNMI   r10, r10
+        MOVPL   r10, r2
+        B       VarNoOFlo
+
+BufferOFloError
+        MakeErrorBlock BuffOverflow
+
+ReadVarVal_CallCode
+        Push    "r0-r2"                 ; read sysvar : r4 points after type
+        ADD     r11, r4, #3 + 4         ; 3 to align and 4 to get to read entry
+        MOV     lr, pc                  ; construct link
+        BIC     pc, r11, #3             ; call read code in var
+        MOV     r11, r0                 ; ptr to value
+        MOV     r10, r2                 ; no of chars.
+        Pull    "r0-r2"
+
+        ; error may be returned from reading the var val
+        MOVVS   r0, r11
+        BVS     ReadVarVal_TestVExit
+
+        MOV     r4, #VarType_String
+        B       ReadVarVal_CopyR10BytesToUserBuffer
+
+ReadVarVal_ExpandedWanted
+        ; Request for expanded value....
+
+        ; Check for number, string or macro
+        CMP     R4, #VarType_Number
+        BLT     ReadVarVal_CopyStringVarToUserBuffer
+        BEQ     ReadVarVal_FoundNumber
+
+; macro - gstrans it. R1 buffer ptr, r2 max chars, R11+1 ptr to value.
+; Macros have a terminator after their value, to allow GSTRANS.
+
+        CMP     r2, #0          ; if negative, then don't call GSTrans because bits 29..31 have
+        MVNMI   r10, r2         ; return r2 out by this method
+        BMI     ReadVarVal_BufWillOFlow  ; a special meaning - just branch back to the normal overflow code
+
+ [ LongVars
+        ADD     r0, r11, #3             ; skip length
+ |
+        ADD     r0, r11, #1             ; skip length
+ ]
+        SWI     XOS_GSTrans
+        BVS     ReadVarVal_TestVExit
+        BCC     ReadVarVal_OKExit
+
+        ADR     R0, BufferOFloError
+      [ International
+        BL      TranslateError
+      ]
+        B       ReadVarVal_SetVExit
+
+
+ReadVarVal_FoundNumber
+        ; Found a number - extract its value and convert to string
+        LDRB    R0, [R11], #1       ; number - convert to string.
+        LDRB    R12, [R11], #1
+        ORR     R0, R0, R12, LSL #8
+        LDRB    R12, [R11], #1
+        ORR     R0, R0, R12, LSL #16
+        LDRB    R12, [R11]
+        ORR     R0, R0, R12, LSL #24
+
+        ; got number in R0, buffptr in R1, max chars in R2
+        SWI     XOS_BinaryToDecimal
+
+        MOV     r4, #VarType_String
+
+ReadVarVal_TestVExit
+        STRVS   r0, [stack, #Proc_LocalStack + 0*4]
+        PullEnv
+        B       SLVK_TestV
+
+RVVNotFound
+ [ International
+        MOV     r4, r0
+        ADR     r0, RVVNFError
+        BL      TranslateError_UseR4
+ |
+        ADR     R0, RVVNFError
+ ]
+        MOV     r2, #0                  ; indicate not found.
+
+ReadVarVal_SetVExit
+        STR     r0, [stack, #Proc_LocalStack + 0*4]
+        PullEnv
+        B       SLVK_SetV               ; general error return
+
+RVVNFError
+        MakeErrorBlock VarCantFind
+
+;***************************************************************************
+
+; The convert number to string SWI
+; In  : R0 signed 32-bit integer
+;       R1 pointer to buffer
+;       R2 max buffer length
+; Out : R0, R1 unmodified
+;       R2 actual chars given
+;       V Set if buffer overflow
+
+; Format : - if negative, leading zeros stripped.
+
+CvtToDecimal ROUT
+        Push    "R0, R3-R5"
+        MOV     R12, R2
+        MOV     R2, #0
+        CMP     R0, #0
+        BPL     %FT01
+        SUBS    R12, R12, #1
+        BMI     %FT10
+        MOV     R11, #"-"
+        STRB    R11, [R1]
+        MOV     R2, #1
+        RSB     R0, R0, #0
+
+; now do digits.
+
+01      RSB     R0, R0, #0          ; get negative so minint works.
+        ADR     R3, TenTimesTable
+        MOV     R10, #9            ; max entry
+        MOV     R4, #0             ; non-0 had flag
+02      LDR     R11, [R3, R10, LSL #2]
+        MOV     R5, #-1            ; digit value
+03      ADDS    R0, R0, R11
+        ADD     R5, R5, #1
+        BLE     %BT03
+        SUB     R0, R0, R11
+        CMP     R5, #0
+        CMPEQ   R4, #0
+        BNE     %FT04             ; put digit
+05      SUBS    R10, R10, #1
+        BPL     %BT02             ; next digit
+        CMP     R4, #0
+        BEQ     %FT04             ; R5 must be 0
+        Pull    "R0, R3-R5"
+        ExitSWIHandler
+
+04      SUBS    R12, R12, #1
+        BMI     %FT10             ; naff Exit
+        ADD     R5, R5, #"0"
+        MOV     R4, #-1
+        STRB    R5, [R1, R2]
+        ADD     R2, R2, #1
+        B       %BT05
+10
+        ADR     R0, BufferOFloError
+    [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+    ]
+        Pull    "R3"              ; discard R0 in
+        Pull    "R3-R5"
+        B       SLVK_SetV
+
+TenTimesTable
+      &        1
+      &        10
+      &        100
+      &        1000
+      &        10000
+      &        100000
+      &        1000000
+      &        10000000
+      &        100000000
+      &        1000000000
+
+; *****************************************************************************
+; SWI OS_SetVarVal : create/update/destroy a variable.
+
+; In:   R0 pointer to name (can be wildcarded for update/delete)
+;             ctrl/char or space terminated
+;       R1 pointer to value. String values must be CR or LF terminated.
+;       R2 negative means destroy the variable. +ve is update/create
+;       R3 name pointer or 0
+;       R4 type.
+;
+;  Evaluation of value : this depends on the type.
+;  VarType_String   : GSTRANS the given value
+;  VarType_Number   : Value is a 4 byte (signed) integer
+;  VarType_Macro    : Copy value (may be GSTRANSed on use)
+;  VarType_Expanded : the value is a string which should be evaluated as an
+;                     expression. Variable is then numeric or string
+;  VarType_LiteralString : Copy the given value as a string
+;
+;  VarType_Code     : R2 is the length of the code to copy in, including
+;                     padding to align the code.
+;                     Can only delete sysvars if R4 = VarType_Code
+
+; Out:  R3 new name pointer (so can delete all occurrences of f*, etc.
+;          slightly more efficiently).
+;       R4 type created for expressions
+;       V set for :
+;          1) bad name  (creation of wildcarded names is banned)
+;          2) Bad string from GSTRANS
+;          3) Bad macro value (control codes not allowed)
+;          4) Bad expression from ReadExpression
+;          5) Can't find (for deletion)
+;          6) Not enough room to create/update it (system heap full)
+;          7) Value too long (variables are limited to 256 bytes in length)
+;          8) Bad type (update/create)
+
+
+ [ LongVars
+ [ DebugSysVars
+SysVar_Write0 Entry "r0,r1"
+        MOV     r1, r0
+10
+        LDRB    r0, [r1], #1
+        CMP     r0, #" "
+        EXIT    LO
+        SWI     XOS_WriteC
+        B       %BT10
+
+ ]
+
+SetVarValue
+        ; enable IRQs
+        TEQP    pc, #SVC_mode
+
+        Entry   "r0,r1,r2,r4,r5,r6,r9"
+
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "SetVarVal ",0
+        BL      SysVar_Write0
+        SWI     XOS_NewLine
+ ]
+
+        MOV     r9, stack
+        MOV     r10, r4
+
+        CMP     r2, #0
+        BMI     SetVarVal_DeleteIt
+
+        ; Range check type
+        CMP     r10, #VarType_Code
+        CMPNE   r10, #VarType_LiteralString
+        ADRHIL  r0, ErrorBlock_BadVarType
+        BHI     SetVarValBadExit_Translate
+
+        ; Always expand a VarType_Expanded:
+        TEQ     r10, #VarType_Expanded
+        BNE     SetVarVal_AintExpanded
+
+        ; Process VarType_Expanded
+        SUB     stack, stack, #256
+
+        MOV     r0, r1                  ; ptr to expression
+        MOV     r1, stack
+        MOV     r2, #256
+        SWI     XOS_EvaluateExpression
+        BVS     SetVarVal_TestVExit
+        TEQ     r1, #0   ; integer?
+        MOVNE   r10, #VarType_LiteralString
+        MOVEQ   r10, #VarType_Number
+        STREQ   r2, [stack]
+        MOVEQ   r2, #4
+        STR     r10, [r9, #3*4]         ; r4 out
+        MOV     r1, stack
+        LDR     r0, [r9, #0*4]
+
+SetVarVal_AintExpanded
+
+        ; Setting a variable
+        BL      VarFindIt
+        BNE     SetVarVal_NodeAlreadyExists
+
+        ; Node missing....
+
+        ; Check variable name has no wildcards
+        SUB     r4, r0, #1
+05
+        LDRB    lr, [r4, #1]!
+        CMP     lr, #"#"
+        CMPNE   lr, #"*"
+        CMPNE   lr, #" "
+        BHI     %BT05
+        CMP     lr, #"#"
+        CMPNE   lr, #"*"
+        CMPNE   r4, r0
+        ADREQL  r0, ErrorBlock_BadVarNam
+        BEQ     SetVarValBadExit_Translate     ; error no. 1)
+
+ [ QuickIndex
+        ; R12 index of 1st entry in QuickIndex >= the entry we're interested in
+ |
+        ; Variable name OK, find where in list we want to insert it
+
+        LDR     r12, =VariableList
+        LDR     r11, [r12]
+        CMP     r11, #0
+        B       %FT08
+
+06
+        MOV     r3, r0
+        ADD     r4, r11, #VNameOff
+07
+        LDRB    r5, [r3], #1
+        LDRB    r6, [r4], #1
+        CMP     r5, r6                  ; can't hit terminator :
+        BEQ     %BT07                   ; not same as any on list
+        MOVHI   r12, r11
+        LDRHI   r11, [r12, #VarLink]
+        CMPHI   r11, #0
+08
+        BHI     %BT06
+
+        ; r12 now ready as pointer to prev
+ ]
+        MOV     r3, #0                  ; To indicate absence of current
+        B       SetVarVal_CreateNode
+
+SetVarVal_NodeAlreadyExists
+        MOV     r0, r3          ; If already there use that's name in case supplied name wildcarded
+        LDRB    lr, [r4]
+        TEQ     lr, #VarType_Code
+        BNE     SetVarVal_CreateNode
+        TEQ     r10, #VarType_Code
+        BEQ     SetVarVal_CreateNode
+
+        ; Assign non code value to code node
+        CMP     r10, #VarType_Number
+        BHI     SetVarVal_AssignToCodeDoIt
+
+        ; For both string and number give ourselves a stack frame
+        SUB     stack, stack, #256
+        MOV     r2, #256
+
+        BLO     SetVarVal_AssignStringToCode
+
+        ; Assign a number to the code variable
+        LDRB    r0, [r1], #1
+        LDRB    lr, [r1], #1
+        ORR     r0, r0, lr, LSL #8
+        LDRB    lr, [r1], #1
+        ORR     r0, r0, lr, LSL #16
+        LDRB    lr, [r1], #1
+        ORR     r0, r0, lr, LSL #24
+        MOV     r1, stack
+        SWI     XOS_BinaryToDecimal
+
+        B       SetVarVal_AssignToCodeDoIt
+
+SetVarVal_AssignStringToCode
+
+        ; Expand string to stack frame then do it
+        MOV     r0, r1
+        MOV     r1, stack
+        SWI     XOS_GSTrans
+        BVS     SetVarVal_TestVExit
+        ADRCSL  r0, ErrorBlock_VarTooLong
+        BCS     SetVarValBadExit_Translate
+
+SetVarVal_AssignToCodeDoIt
+
+        ADDS    r4, r4, #3 + 1          ; skip type, add 3 for ALIGN , clear V
+        MOV     lr, PC
+        BIC     PC, R4, #3              ; complete align and call
+
+        B       SetVarVal_TestVExit
+
+
+SetVarVal_CreateNode
+        ; Create a node
+        ;
+        ; r0 -> name (already confirmed non-wildcarded)
+        ; r1 -> value
+        ; r2 = length (where appropriate)
+        ; r3 = this or 0
+        ; r10 = type
+ [ QuickIndex
+        ; r12 = insertion point
+ |
+        ; r12 = prev
+ ]
+
+        MOV     r5, r1
+        MOV     r6, r3
+
+        ; first work out the length of those things we can work the length of
+
+        ; Header and name...
+        MOV     r3, #VNameOff + 1       ; Add one for the type byte
+        MOV     r1, r0
+10
+        LDRB    lr, [r1], #1
+        ADD     r3, r3, #1
+        CMP     lr, #" "
+        BHI     %BT10
+
+        ; Deal with number and string type
+        CMP     r10, #VarType_Number
+        ADDLO   r3, r3, #50             ; only an initial guess for the string type
+        ADDEQ   r3, r3, #4
+        MOVEQ   r2, #4
+        BLS     SetVarVal_GotInitialLength
+
+        CMP     r10, #VarType_Code
+        ADDEQ   r3, r3, #3              ; ALIGN
+        BICEQ   r3, r3, #3
+        ADDEQ   r3, r3, r2              ; code
+        BEQ     SetVarVal_GotInitialLength
+
+        TEQ     r10, #VarType_LiteralString
+        BEQ     %FT20
+
+        ; Macro - strlen and check the value is vaguely OK
+        MOV     r2, r5
+15
+        LDRB    lr, [r2], #1
+        CMP     lr, #" "
+        BHS     %BT15
+        TEQ     lr, #0                  ; must terminate with NUL, CR or LF
+        TEQNE   lr, #10
+        TEQNE   lr, #13
+        ADRNE   r0, ErrorBlock_BadMacVal
+        BNE     SetVarValBadExit_Translate
+        SUB     r2, r2, r5
+20
+        ADD     r3, r3, r2
+        ADD     r3, r3, #3              ; for the length bytes
+
+SetVarVal_GotInitialLength
+        ; r0 -> node's name
+        ; r2 = value length (Number, Macro and Code)
+        ; r3 = node length needed (maybe initial guess for Strings)
+        ; r5 -> value (r1 in)
+        ; r6 -> name of node to be replaced (0 if no node being replaced)
+        ; r10 = value's type (String, Number, Macro or Code)
+ [ QuickIndex
+        ; r12 -> insertion point
+ |
+        ; r12 -> prev node
+ ]
+
+        Push    "r0,r2"
+        BL      ClaimSysHeapNode
+        BVS     SetVarVal_VarNoRoom
+        Pull    "r0,r1"
+
+        ; Got a heap block - fill it in
+
+        ; Copy name
+        ADD     r4, r2, #VNameOff
+25
+        LDRB    lr, [r0], #1
+        CMP     lr, #" "
+        MOVLS   lr, #0
+        STRB    lr, [r4], #1
+        BHI     %BT25
+
+        ; Variable's type
+        TEQ     r10, #VarType_LiteralString
+        MOVEQ   lr, #VarType_String
+        MOVNE   lr, r10
+        STRB    lr, [r4], #1
+
+        TEQ     r10, #VarType_String
+        BEQ     SetVarVal_FillInString
+
+        TEQ     r10, #VarType_Code
+        ADDEQ   r4, r4, #3
+        BICEQ   r4, r4, #3
+        TEQNE   r10, #VarType_Number
+        BEQ     SetVarVal_CopyR1BytesToR4
+
+        ; For macro type fill in a length
+        TEQ     r10, #VarType_Macro
+        SUBEQ   r1, r1, #1                      ; ghastly fudge to avoid display of terminator
+        STRB    r1, [r4], #1
+        MOV     r1, r1, ROR #8
+        STRB    r1, [r4], #1
+        MOV     r1, r1, ROR #8
+        STRB    r1, [r4], #1
+        MOV     r1, r1, ROR #16
+        ADDEQ   r1, r1, #1                      ; undo ghastly fudge
+
+SetVarVal_CopyR1BytesToR4
+        B       %FT35
+30
+        LDRB    lr, [r5], #1
+        STRB    lr, [r4], #1
+35
+        SUBS    r1, r1, #1
+        BHS     %BT30
+
+        B       SetVarVal_NewNodeReady
+
+SetVarVal_FillInString
+        ; Here's the real smart bit of code
+
+        ; The idea is this:
+        ; Instead of GSTransing, we GSInit and GSRead ourselves. When the
+        ; block gets full expand it and carry on. At the end the block is shrunk to fit.
+
+        ADD     r4, r4, #3              ; for the length bytes
+        MOV     r11, r4                 ; preserve location of string start for when we've done
+        MOV     r0, r5                  ; r1 in
+        MOV     r5, r2
+        MOV     r2, #0
+        SWI     XOS_GSInit
+        BVS     SetVarVal_DisasterExpandingString
+        B       %FT45
+
+40
+        SWI     XOS_GSRead
+        BVS     SetVarVal_DisasterExpandingBadString
+        BCS     SetVarVal_StringFinishedExpanding
+        STRB    r1, [r4], #1
+        CMP     r4, r3
+        BLO     %BT40
+
+        ; Run out of room in this block - stretch it
+        Push    "r0-r2"
+        MOV     r0, #HeapReason_ExtendBlock
+        MOV     r2, r5
+        MOV     r3, #64
+        BL      DoSysHeapOpWithExtension
+        STRVS   r0, [sp]
+        SUBVC   lr, r2, r5
+        ADDVC   r4, r4, lr
+        ADDVC   r11, r11, lr
+        MOVVC   r5, r2
+        Pull    "r0-r2"
+        BVS     SetVarVal_DisasterExpandingString
+
+45
+        LDR     r3, [r5, #-4]           ; The heap block's size
+        SUB     r3, r3, #4              ; the amount we're allowed to use
+        ADD     r3, r3, r5              ; the block's end
+        B       %BT40
+
+SetVarVal_StringFinishedExpanding
+
+        ; Shorten block to required size
+        MOV     r0, #HeapReason_ExtendBlock
+        SUB     r3, r4, r3
+        MOV     r2, r5
+        BL      DoSysHeapOpWithExtension
+        BVS     SetVarVal_DisasterExpandingString
+
+        ; Relocate pointers
+        SUB     lr, r2, r5
+        ADD     r4, r4, lr
+        ADD     r11, r11, lr
+
+        ; Work out ultimate size and store it
+        SUB     lr, r4, r11
+        STRB    lr, [r11, #-3]
+        MOV     lr, lr, LSR #8
+        STRB    lr, [r11, #-2]
+        MOV     lr, lr, LSR #8
+        STRB    lr, [r11, #-1]
+
+SetVarVal_NewNodeReady
+        ; r2 -> new node
+        ; r6 -> old node's name (or is 0 if no old node)
+ [ QuickIndex
+        ; r12 = insertion point
+ |
+        ; r12 -> prevP
+ ]
+
+ [ DebugSysVars
+        Push    "r0,r1,r2"
+        SUB     sp, sp, #12
+        MOV     r0, r2
+        MOV     r1, sp
+        MOV     r2, #12
+        SWI     XOS_ConvertHex8
+        SWI     XOS_Write0
+        SWI     XOS_WriteI+" "
+        MOV     r0, r6
+        MOV     r1, sp
+        MOV     r2, #12
+        SWI     XOS_ConvertHex8
+        SWI     XOS_Write0
+        SWI     XOS_WriteI+" "
+        MOV     r0, r12
+        MOV     r1, sp
+        MOV     r2, #12
+        SWI     XOS_ConvertHex8
+        SWI     XOS_Write0
+        SWI     XOS_WriteI+" "
+        ADD     sp, sp, #12
+        Pull    "r0,r1,r2"
+ ]
+ [ QuickIndex
+        LDR     r11, =VariableList
+        LDR     r10, [r11]
+        MOV     r5, r2
+        TEQ     r6, #0
+        BEQ     SetVarVal_Insertion
+
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "-straight replace-",0
+ ]
+
+        SUB     r2, r6, #VNameOff
+        BL      FreeSysHeapNode
+        B       SetVarVal_Replace
+
+SetVarVal_Insertion
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "-insert-",0
+ ]
+        TEQ     r10, #0
+        BNE     SetVarVal_PossibleExtend
+
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "-create index-",0
+ ]
+        MOV     r3, #44         ; 10 nodes and 1 for the count
+        BL      ClaimSysHeapNode
+        BVS     SetVarVal_NoRoomForIndex
+        MOV     r10, r2
+        MOV     r4, #0
+        B       SetVarVal_DoInsertNewBlock
+
+SetVarVal_PossibleExtend
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "-extend index-",0
+ ]
+        LDR     r4, [r10]
+        LDR     lr, [r10, #-4]          ; Block length
+        SUB     lr, lr, #4+4            ; 4 for heap adjustment and 4 for entry count word
+        CMP     lr, r4, ASL #2
+        BHI     SetVarVal_DoInsert      ; we've got room with this block
+
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "-do extend-",0
+ ]
+
+        MOV     r0, #HeapReason_ExtendBlock
+        MOV     r2, r10
+        MOV     r3, #40                 ; room for 10 more nodes
+        BL      DoSysHeapOpWithExtension
+        BVS     SetVarVal_NoRoomForIndex
+
+        MOV     r10, r2
+
+SetVarVal_DoInsertNewBlock
+        STR     r10, [r11]
+SetVarVal_DoInsert
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "-doinsert-",0
+ ]
+        ADD     r0, r10, r12, ASL #2    ; insertion point
+        ADD     r1, r10, r4, ASL #2     ; rover
+        B       SetVarVal_DoInsertEnd
+
+SetVarVal_DoInsertStart
+        LDR     lr, [r1], #-4
+        STR     lr, [r1, #8]
+
+SetVarVal_DoInsertEnd
+        CMP     r1, r0
+        BHS     SetVarVal_DoInsertStart
+
+        ADD     r4, r4, #1
+        STR     r4, [r10]
+
+SetVarVal_Replace
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "-doreplace-",0
+ ]
+        STR     r5, [r10, r12, ASL #2]
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "-done-",0
+ ]
+ |
+        ; Remove old node and place new node in its place
+        TEQ     r6, #0
+        LDRNE   lr, [r6, #-VNameOff]
+        LDREQ   lr, [r12]
+        STR     lr, [r2]
+        STR     r2, [r12]
+
+        ; If old node present then junk it
+        SUBNE   r2, r6, #VNameOff
+        BLNE    FreeSysHeapNode
+ ]
+
+        ; All done
+        B       SetVarVal_TestVExit
+
+SetVarVal_DeleteIt
+        BL      VarFindIt
+
+        ; Error if not found
+        ADREQL  r0, ErrorBlock_VarCantFind ; V set no. 1)
+        BEQ     SetVarValBadExit_Translate
+
+        ; Check if found vartype code that the supplied vartype was code too
+        LDRB    lr, [r4]
+        TEQ     lr, #VarType_Code
+        BNE     %FT80
+
+        TEQ     r10, #VarType_Code
+        BNE     SetVarVal_TestVExit
+80
+ [ QuickIndex
+        LDR     r11, =VariableList
+        LDR     r10, [r11]
+        LDR     r4, [r10]
+        ADD     r0, r10, r12, ASL #2    ; rover
+        ADD     r1, r10, r4, ASL #2     ; end
+        B       SetVarVal_DoRemoveEnd
+SetVarVal_DoRemoveStart
+        LDR     lr, [r0, #4]!
+        STR     lr, [r0, #-4]
+SetVarVal_DoRemoveEnd
+        CMP     r0, r1
+        BLO     SetVarVal_DoRemoveStart
+        SUB     r2, r3, #VNameOff
+        BL      FreeSysHeapNode
+
+        ; Reduce number of nodes
+        SUB     r4, r4, #1
+        STR     r4, [r10]
+
+        ; Construct best guess context ptr to be prev node (if present)
+        TEQ     r12, #1
+        SUBHI   r12, r12, #1
+        ASSERT  VNameOff = 0
+        LDRHI   r3, [r10, r12, ASL #2]
+        MOVLS   r3, #0
+
+ |
+        ; Unlink node from chain
+        SUB     r3, r3, #VNameOff
+        LDR     r11, [r3, #VarLink]
+        STR     r11, [r12, #VarLink]
+        MOV     R2, R3                   ; node ptr.
+        BL      FreeSysHeapNode
+        ADD     R3, R12, #VNameOff       ; our best guess at a context ptr
+ ]
+
+SetVarVal_TestVExit
+        MOV     stack, r9
+        STRVS   r0, [stack]
+        PullEnv
+        B       SLVK_TestV
+
+SetVarValBadExit_Translate
+        BL      TranslateError
+        SETV
+        B       SetVarVal_TestVExit
+
+SetVarVal_DisasterExpandingString
+SetVarVal_NoRoomForIndex
+        MOV     r2, r5
+        BL      FreeSysHeapNode
+SetVarVal_VarNoRoom
+        ADR     r0, ErrorBlock_VarNoRoom
+        B       SetVarValBadExit_Translate
+
+SetVarVal_DisasterExpandingBadString
+        Push    "r0"                    ; Save bad string error
+        MOV     r2, r5
+        BL      FreeSysHeapNode
+        Pull    "r0"
+        SETV
+        B       SetVarVal_TestVExit
+
+        MakeErrorBlock BadVarType
+        MakeErrorBlock BadVarNam
+        MakeErrorBlock VarTooLong
+        MakeErrorBlock BadMacVal
+        MakeErrorBlock VarNoRoom
+
+ |
+SetVarValue ROUT
+
+        Push    "R1, R2, R4, lr"
+        CheckSpaceOnStack  512, SVStackFull, r12
+        SUB     stack, stack, #256      ; buffer space
+
+        CMP     R2, #0
+        BMI     SetVarVal_GotValueToBuffer ; deletion
+
+; now range check type
+        CMP     R4, #VarType_Code
+        BEQ     SetVarVal_GotValueToBuffer
+        CMP     R4, #VarType_Expanded
+        BGT     SetVarVal_TypeNaff
+
+; now get the value, before destroying anything
+        BEQ     SetVarVal_AssignExpanded ; evaluate an expression
+        CMP     R4, #VarType_Number
+        MOVEQ   R2, #3                  ; numbers use type byte as one byte.
+        BEQ     SetVarVal_GotValueToBuffer
+        BGT     SetVarVal_ValidateMacroValue
+; string : GSTRANS it.
+        Push    "R0"
+        MOV     R0, R1                  ; source ptr.
+        ADD     R1, stack, #4
+        MOV     R2, #255
+        SWI     XOS_GSTrans
+        BVS     VarBadStrErr
+        Pull    "R0"
+        BCC     SetVarVal_GotValueToBuffer
+
+SetVarVal_GotValueToBufferButItsTooLong
+        LDR     lr, [Stack, #4*3+256]   ; V set no. 3): value too long
+        ORR     lr, lr, #V_bit          ; Poke V into stacked lr!
+        STR     lr, [Stack, #4*3+256]
+
+SetVarVal_GotValueToBuffer
+; now got R1 pointer to value, R2 = value length
+        BL      VarFindIt
+        BEQ     SetVarVal_NodeNotFound
+
+        ; Check whether we're assigning a Code value to a Code variable
+        LDRB    R10, [R4]              ; get type
+        CMP     R10, #VarType_Code
+        BNE     %FT30
+        LDR     R10, [Stack, #4*2+256]
+        CMP     R10, #VarType_Code
+        BNE     SetVarVal_AssignNonCodeToCode
+
+30
+        ; We are assigning a code value to a code variable
+        ;               OR any value to a non-code variable
+
+        ; deleting?
+        CMP     R2, #0
+        BMI     SetVarVal_GotToJunkOldNode  ; just delete it.
+
+        ; Assigning....
+        LDR     R11, [R3, #-(VNameOff+4)] ; real no of bytes in heap node
+        SUB     R11, R11, #VNameOff+4+2
+        ADD     R11, R11, R3
+        SUB     R11, R11, R4             ; take off bytes in name
+        CMP     R11, R2
+        BGT     SetVarVal_NodeReadyToCopyInValue
+; let's copy the name, in case orig source wildcarded.
+        LDR     R0, =GSVarWSpace+GSNameBuff
+        MOV     R10, R3
+15      LDRB    R11, [R10], #1
+        STRB    R11, [R0], #1
+        CMP     R11, #0
+        BNE     %BT15
+        LDR     R0, =GSVarWSpace+GSNameBuff
+
+SetVarVal_GotToJunkOldNode
+; got to delete old node (too small). R12 previous, R3-VNameOff is this.
+        SUB     R3, R3, #VNameOff
+        LDR     R11, [R3, #VarLink]
+        STR     R11, [R12, #VarLink]     ; chain updated
+        Push    "R0-R2"
+        MOV     R2, R3                   ; node ptr.
+        BL      FreeSysHeapNode
+        Pull    "R0-R2"                   ; node gone
+        ADD     R3, R12, #VNameOff       ; our best guess at a context ptr
+        CMP     R2, #0                   ; delete?
+        BMI     SetVarValueTestExit      ; yup - exit.
+
+SetVarVal_MakeNewNode
+; here, R2 is value length, R0 ptr to name. Validate name while finding length
+        MOV     R10, #0
+16      LDRB    R11, [R0, R10]
+        ADD     R10, R10, #1
+        CMP     R11, #"#"
+        CMPNE   R11, #"*"
+        BEQ     SetVarVal_BadVarName     ; error no. 1)
+        CMP     R11, #32
+        BGT     %BT16
+        CMP     R10, #1
+        BEQ     SetVarVal_BadVarName     ; 0 char name also naff
+; now got R10 name length. Calculate node size
+        ADD     R11, R10, R2
+        ADD     R11, R11, #VNameOff+2    ; link+name terminator+type
+        Push    "R0-R3"
+        MOV     R3, R11
+        BL      ClaimSysHeapNode         ; corrupts R12
+        MOV     R4, R2
+        Pull    "R0-R3"
+        BVS     SetVarSysHeapFull
+; now need to find correct alphabetic position on chain.
+        LDR     R12, =VariableList
+        LDR     R11, [R12]
+; R4 node to insert, R12 prevnode, R11 nextnode
+        Push    "R5, R6"
+31      CMP     R11, #0
+        BEQ     %FT33
+        ADD     R11, R11, #VNameOff
+        MOV     R10, #-1
+32      ADD     R10, R10, #1
+        LDRB    R5, [R0, R10]
+        LDRB    R6, [R11, R10]
+        CMP     R5, R6                 ; can't hit terminator :
+        BEQ     %BT32                 ; not same as any on list
+        SUB     R11, R11, #VNameOff
+        MOVGT   R12, R11
+        LDRGT   R11, [R12, #VarLink]
+        BGT     %BT31
+
+33
+        ; Link node into list
+        STR     R11, [R4, #VarLink]
+        STR     R4, [R12, #VarLink]     ; new entry in
+        Pull    "R5, R6"
+
+        ; Copy name to node
+        ADD     R4, R4, #VNameOff
+18      LDRB    R11, [R0], #1
+        STRB    R11, [R4], #1
+        CMP     R11, #32
+        BGT     %BT18
+        MOV     R11, #0
+        STRB    R11, [R4, #-1]
+
+SetVarVal_NodeReadyToCopyInValue
+; now easy: just copy new value in. R2 bytes, from (R1).R4 points to type
+
+        LDR     R10, [stack, #2*4+256]  ; get original type back
+        STRB    R10, [R4], #1           ; put type in
+        CMP     R10, #VarType_Macro
+        SUBEQ   R2, R2, #1              ; fudge macro terminators
+        CMP     R10, #VarType_Code
+        ADDEQ   R4, R4, #3              ; align for code.
+        BICEQ   R4, R4, #3
+        CMP     R10, #VarType_Number
+        MOVEQ   R2, #4
+        CMPNE   R10, #VarType_Code   ; no length for numbers, sysvars
+        STRNEB  R2, [R4], #1
+        CMP     R10, #VarType_Macro
+        ADDEQ   R2, R2, #1
+05      SUBS    R2, R2, #1
+        BMI     SetVarValueTestExit    ; finished
+        LDRB    R10, [R1, R2]
+        STRB    R10, [R4, R2]
+        B       %BT05
+
+SetVarSysHeapFull
+        ADR     r0, ErrorBlock_VarNoRoom ; VS no. 2)
+        B       SetVarValueBadExit_256Translate
+
+        MakeErrorBlock VarNoRoom
+
+SetVarVal_ValidateMacroValue
+        MOV     R2, #0
+13      CMP     R2, #255
+        BGT     SetVarVal_GotValueToBufferButItsTooLong
+        LDRB    R10, [R1, R2]           ; it's a macro: check for bad chars.
+        ADD     R2, R2, #1
+        CMP     R10, #31
+        BGT     %BT13
+        CMP     R10, #13
+        CMPNE   R10, #10
+        CMPNE   R10, #0
+        BEQ     SetVarVal_GotValueToBuffer
+
+        ADR     r0, ErrorBlock_BadMacVal
+        B       SetVarValueBadExit_256Translate
+
+        MakeErrorBlock BadMacVal
+
+SetVarVal_BadVarName
+        ADR     r0, ErrorBlock_BadVarNam ; VS no. 2)
+        B       SetVarValueBadExit_256Translate
+
+        MakeErrorBlock BadVarNam
+
+SetVarVal_NodeNotFound
+        CMP     R2, #0        ; no node for it, test whether deletion
+        BPL     SetVarVal_MakeNewNode
+
+        ADRL    r0, ErrorBlock_VarCantFind ; V set no. 1)
+        B       SetVarValueBadExit_256Translate
+
+
+SetVarVal_AssignNonCodeToCode
+        CMP     R2, #0
+        BMI     SetVarValueTestExit ; deletion's a NOP, when wrong type given
+        LDR     R10, [stack, #2*4+256] ; get original type back
+        CMP     R10, #VarType_Number
+        BNE     %FT25
+        MOV     R10, R0
+        LDRB    R0, [R1], #1
+        LDRB    R2, [R1], #1
+        ORR     R0, R0, R2, LSL #8
+        LDRB    R2, [R1], #1
+        ORR     R0, R0, R2, LSL #16
+        LDRB    R2, [R1], #1
+        ORR     R0, R0, R2, LSL #24
+        ADD     R1, stack, #0
+        MOV     R2, #256
+        SWI     XOS_BinaryToDecimal
+        MOV     R0, R10        ; force string value.
+25      ADDS    R4, R4, #4      ; skip type, add 3 , clear V
+        MOV     lr, PC
+        BIC     PC, R4, #3      ; complete align and call
+
+; set thunk must take R1 ptr to value, R2 value length
+
+        BVS     SetVarValueBadExit_256
+
+SetVarValueTestExit
+        ADD     stack, stack, #256
+        Pull    "R1, R2, R4, lr"
+        TST     lr, #V_bit
+        ADRNE   R0, ErrorBlock_VarTooLong
+      [ International
+        Push    "lr",NE
+        BLNE    TranslateError
+        Pull    "lr",NE
+      ]
+        ExitSWIHandler
+
+        MakeErrorBlock VarTooLong
+
+SetVarVal_AssignExpanded
+        Push    "R0"
+        MOV     R0, R1   ; ptr to expression
+        ADD     R1, stack, #4
+        MOV     R2, #256
+        SWI     XOS_EvaluateExpression
+        BVS     NarffExpression
+        CMP     R1, #0   ; integer?
+        MOVEQ   R4, #VarType_Number
+        MOVNE   R4, #VarType_String
+        BNE     %FT40
+        ADD     R1, stack, #8
+        STR     R2, [R1]
+        MOV     R2, #3
+40
+        Pull    "R0"
+        STR     R4, [stack, #2*4+256]  ; update original type
+        B       SetVarVal_GotValueToBuffer
+
+
+NarffExpression
+        ADD     stack, stack, #256+4    ; discard an r0 and buffer
+        Pull    "R1, R2, r4, lr"
+        B       SLVK_SetV
+
+SetVarVal_TypeNaff
+
+        ADR     R0, ErrorBlock_BadVarType ; V set no. 4)
+        B       SetVarValueBadExit_256Translate
+
+        MakeErrorBlock BadVarType
+
+VarBadStrErr
+        Pull    "R1"
+
+SetVarValueBadExit_256Translate
+        BL      TranslateError
+
+SetVarValueBadExit_256
+        ADD     stack, stack, #256
+        B       SetVarValueBadExit
+
+SVStackFull
+        ADRL    r0, ErrorBlock_StackFull
+        BL      TranslateError
+
+SetVarValueBadExit
+        Pull    "R1, R2, R4, lr"
+        B       SLVK_SetV
+ ]
+
+; *****************************************************************************
+; Utility routines.
+
+; -----------------------------------------------------------------------------
+;
+; VarFindIt
+;
+; In
+;    r0 -> (wildcard) name of varibale to find
+;    r3 = context pointer
+;
+; Out
+;    r3 = name pointer
+;    r4 = pointer after name terminator
+ [ QuickIndex
+;    r12 = insertion point (1st node >= this node)
+ |
+;    r12 = address of previous node
+ ]
+;    NE if found, EQ if not found
+;
+ [ QuickIndex
+VarFindIt Entry "r0,r1,r2,r5,r6,r7,r8,r9,r10,r11"
+
+; validate R3 by looking down the list to see if we find it.
+; Crude, but effective!
+
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "VarFindIt(",0
+        BL      SysVar_Write0
+ ]
+
+        LDR     r9, =VariableList
+        LDR     r9, [r9]
+        TEQ     r9, #0
+        LDRNE   r8, [r9]
+        MOVEQ   r8, #0
+        TEQ     r3, #0
+        BEQ     %FT20
+
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "-scan-",0
+ ]
+        ; r3 non-zero - scan list for entry
+        ADD     r12, r8, #1
+        B       %FT10
+05
+        LDR     lr, [r9, r12, ASL #2]
+        CMP     lr, r3
+        BEQ     %FT70                   ; continue scan down list
+10
+        SUBS    r12, r12, #1
+        BHI     %BT05
+
+20
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "-wildcheck-",0
+ ]
+        ; not found in scan - check for name being wildcarded
+        MOV     r10, r0
+25
+        LDRB    lr, [r10], #1
+        TEQ     lr, #"*"
+        TEQNE   lr, #"#"
+        BEQ     %FT65
+        CMP     lr, #" "
+        BHI     %BT25
+
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "-bchop-",0
+ ]
+
+        ; Name not wildcarded - do binary chop search
+        ORRS    r7, r8, r8, LSR #1
+        ORR     r7, r7, r7, LSR #2
+        ORR     r7, r7, r7, LSR #4
+        ORR     r7, r7, r7, LSR #8
+        ORR     r7, r7, r7, LSR #16
+        BICS    r7, r7, r7, LSR #1      ; least 2^n <= number of entries
+        MOV     r6, #0
+
+        B       %FT60
+40
+        ADD     r5, r6, r7
+        CMP     r5, r8
+        BHI     %FT55
+
+        MOV     r1, r0
+        LDR     r4, [r9, r5, ASL #2]
+
+45
+        LDRB    r2, [r1], #1
+        CMP     r2, #" "
+        MOVLS   r2, #0
+        LDRB    r3, [r4], #1
+        CMP     r3, #" "
+        MOVLS   r3, #0
+        UpperCase R2,LR
+        UpperCase R3,LR
+        CMP     r3, r2
+        BNE     %FT50
+        CMP     r3, #0
+        BNE     %BT45
+50
+        MOVHS   r10, pc                 ; preserve last HS result we got
+        MOVHS   r11, r4
+        MOVLO   r6, r5
+55
+        MOVS    r7, r7, LSR #1
+60
+        BNE     %BT40
+
+        ; We always want the element above.
+        ; If r6<r8 then we want the preserved result
+        ; else we want the result HI
+        ADD     r6, r6, #1
+        CMP     r6, r8
+        LDRLS   r3, [r9, r6, ASL #2]
+        MOVLS   r4, r11
+        MOVHI   r3, #0
+        TEQLSP  pc, r10
+
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "-complete-",0
+        SWI     XOS_NewLine
+ ]
+        MOV     r12, r6
+        MOV     lr, pc
+        TEQP    lr, #Z_bit
+        EXIT
+
+65
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "-listscan-",0
+        SWI     XOS_NewLine
+ ]
+        ; Scan down list looking for wildmatch
+        MOV     r12, #0
+70
+        ADD     r12, r12, #1
+        CMP     r12, r8
+        BHI     %FT90                   ; end of list reached
+        LDR     r4, [r9, r12, ASL #2]
+        BL      WildMatch               ; trashes r10,r11
+        BNE     %BT70
+
+80
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "-complete-",0
+        SWI     XOS_NewLine
+ ]
+        ; Found
+        ; r4->name end
+        ; r12 = entry number
+        LDR     r3, [r9, r12, ASL #2]
+        ASSERT  VNameOff = 0
+        MOVS    r12, r12                ; set NE
+        EXIT
+
+90
+        ; Not found
+ [ DebugSysVars
+        SWI     XOS_WriteS
+        =       "-not found-",0
+        SWI     XOS_NewLine
+ ]
+        MOV     r12, #1
+        MOVS    r3, #0
+        EXIT
+ |
+VarFindIt Entry "r10,r11"
+
+; validate R3 by looking down the chain to see if we find it.
+; Crude, but effective!
+
+        CMP     R3, #0
+        BEQ     %FT03
+        SUB     R3, R3, #VNameOff         ; step back to chain ptr
+        LDR     R11, =VariableList
+        LDR     R11, [R11]
+02      CMP     R11, #0
+        CMPNE   R11, R3
+        LDRNE   R11, [R11, #VarLink]
+        BNE     %BT02
+        CMP     R11, #0
+03      LDREQ   R3, =VariableList
+01      MOV     R12, R3                  ; keep previous for creation
+        LDR     R3, [R3, #VarLink]        ; step on
+        CMP     R3, #0
+        EXIT    EQ                      ; failed
+        ADD     R4, R3, #VNameOff
+        BL      WildMatch               ; trashes r10,r11
+        BNE     %BT01
+        ADDS    R3, R3, #VNameOff       ; get node ptr and set NE
+        EXIT                            ; and back with got.
+ ]
+
+
+WildMatch ROUT
+; In  : R0 is wildcard spec ptr, R4 is name ptr.
+;       Wild Terminators are any ch <=" ", name terminator 0
+;       Wildcards are *, #
+; Out : EQ/NE for match (EQ if matches)
+;       R4 points after name terminator for found
+;       R0 preserved, R10, 11 corrupted
+
+        Push    "R0-R3"
+        MOV     R11, #0         ; this is the wild backtrack pointer
+        MOV     R3, #0          ; and this is the name backtrack ptr.
+01      LDRB    R1, [R0], #1    ; nextwild
+        CMP     R1, #"*"
+        BEQ     %FT02           ; IF nextwild = "*"
+        LDRB    R2, [R4], #1    ; nextname
+        CMP     R2, #0
+        BEQ     %FT03
+        UpperCase R1, R10
+        UpperCase R2, R10
+        CMP     R1, R2          ; IF nextwild=nextname
+        CMPNE   R1, #"#"        ;   OR nextwild = #  (terminator checked already)
+        BEQ     %BT01           ; THEN LOOP (stepped already)
+        MOV     R0, R11         ; try backtrack
+        MOVS    R4, R3          ; if * had at all
+        BNE     %FT02
+        CMP     PC, #0          ; set NE
+04      Pull    "R0-R3"         ; return NE (failed)
+        MOV     PC, lr
+
+03      CMP     R1, #" "        ; name terminated : has wildcard?
+        BHI     %BA04           ; note HI has NE set.
+        CMP     R1, R1          ; set EQ
+        Pull    "R0-R3"
+        MOV     PC, lr
+
+02      MOV     R11, R0         ; wild backtrack ptr is char after *
+        LDRB    R1, [R0], #1    ; step wild
+        CMP     R1, #"*"
+        BEQ     %BT02           ; fujj **
+        UpperCase R1, R10
+05      LDRB    R2, [R4], #1    ; step name
+        CMP     R2, #0          ; terminator?
+        BEQ     %BT03
+        UpperCase R2, R10
+        CMP     R1, R2
+        CMPNE   R1, #"#"        ; match if #
+        BNE     %BT05
+        MOV     R3, R4          ; name backtrack ptr is char after match
+        B       %BT01           ; LOOP
+
+        LTORG
+
+        END
diff --git a/s/Arthur3 b/s/Arthur3
new file mode 100644
index 0000000000000000000000000000000000000000..18e31635a826e1d3223c4510a9e44c30d69aa5c5
--- /dev/null
+++ b/s/Arthur3
@@ -0,0 +1,2454 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL     => Arthur3
+
+; the IF command
+
+IF_Code    ROUT
+        Push    "R2, lr"
+        LDR     R2, =GeneralMOSBuffer
+01
+        LDRB    R1, [R0], #1
+        STRB    R1, [R2], #1
+        CMP     R1, #10
+        CMPNE   R1, #13
+        CMPNE   R1, #0
+        BEQ     NoTHEN
+        CMP     R1, #" "
+        BNE     %BT01
+        LDRB    R1, [R0]
+        CMP     R1, #"t"
+        CMPNE   R1, #"T"
+        BNE     %BT01
+        LDRB    R1, [R0, #1]
+        CMP     R1, #"h"
+        CMPNE   R1, #"H"
+        BNE     %BT01
+        LDRB    R1, [R0, #2]
+        CMP     R1, #"e"
+        CMPNE   R1, #"E"
+        BNE     %BT01
+        LDRB    R1, [R0, #3]
+        CMP     R1, #"n"
+        CMPNE   R1, #"N"
+        BNE     %BT01
+        LDRB    R1, [R0, #4]
+        CMP     R1, #" "
+        CMPNE   R1, #13
+        CMPNE   R1, #10
+        CMPNE   R1, #0
+        BNE     %BT01
+        MOV     R1, #13
+        STRB    R1, [R2, #-1]
+        ADD     R0, R0, #4              ; skip THEN
+        Push    "R0"
+        LDR     R0, =GeneralMOSBuffer
+        MOV     R2, #-1    ; integers only mate
+        SWI     XOS_EvaluateExpression
+        BVS     WantInteger
+        Pull    "R1"
+        CMP     R2, #0
+        BEQ     %FT02                   ; false
+        LDR     R2, =GeneralMOSBuffer
+03
+        LDRB    R0, [R1], #1
+        STRB    R0, [R2], #1
+        CMP     R0, #10
+        CMPNE   R0, #13
+        CMPNE   R0, #0
+        BEQ     %FT04
+        CMP     R0, #" "
+        BLEQ    %FT05
+        BNE     %BT03
+04
+        MOV     R0, #13
+        STRB    R0, [R2, #-1]
+        LDR     R0, =GeneralMOSBuffer
+07
+        SWI     XOS_CLI
+06
+        Pull    "R2, PC"
+
+05
+        LDRB    R0, [R1]
+        CMP     R0, #"e"
+        CMPNE   R0, #"E"
+        MOVNE   PC, lr
+        LDRB    R0, [R1, #1]
+        CMP     R0, #"l"
+        CMPNE   R0, #"L"
+        MOVNE   PC, lr
+        LDRB    R0, [R1, #2]
+        CMP     R0, #"s"
+        CMPNE   R0, #"S"
+        MOVNE   PC, lr
+        LDRB    R0, [R1, #3]
+        CMP     R0, #"e"
+        CMPNE   R0, #"E"
+        MOVNE   PC, lr
+        LDRB    R0, [R1, #4]
+        CMP     R0, #" "
+        CMPNE   R1, #13
+        CMPNE   R1, #10
+        CMPNE   R1, #0
+        MOV     PC, lr
+
+02
+        LDRB    R0, [R1], #1
+        CMP     R0, #10
+        CMPNE   R0, #13
+        CMPNE   R0, #0
+        BEQ     %BT06
+        CMP     R0, #" "
+        BLEQ    %BT05
+        BNE     %BT02
+        ADD     R0, R1, #4
+        B       %BT07
+
+NoTHEN  ROUT
+        ADR     R0, %FT01
+      [ International
+        BL      TranslateError
+      ]
+IfError
+        Pull    "R2, lr"
+        ORRS    PC, lr, #V_bit
+01
+        &       ErrorNumber_Syntax
+        =       "NoThen:There is no THEN", 0
+        ALIGN
+
+WantInteger ROUT
+        CMP     R1, #0
+        Pull    "R1"
+        Pull    "R2, lr", EQ            ; integer returned, so leave expranal error there
+        ORREQS  PC, lr, #V_bit
+        ADR     R0, %FT01
+      [ International
+        BL      TranslateError
+      ]
+        B       IfError
+01
+        &       ErrorNumber_Syntax
+        =       "IsString:Expression is a string", 0
+        ALIGN
+
+;************************************************************************
+; the expression analysis SWI
+
+; truth values
+Expr_True  *  -1
+Expr_False *   0
+
+; Type symbols
+
+type_Integer  * 0
+type_String   * 1
+type_Operator * 2
+
+; operators :
+; single char syms have their ascii value
+op_Bra     * "("   ; 40
+op_Ket     * ")"   ; 41
+op_Times   * "*"   ; 42
+op_Plus    * "+"   ; 43
+op_Minus   * "-"   ; 45
+op_Divide  * "/"   ; 47
+op_LT      * "<"   ; 60
+op_EQ      * "="   ; 61
+op_GT      * ">"   ; 62
+
+; now fill in some gaps
+
+op_NE      * 44    ; <>
+op_STR     * 46    ; STR
+op_GE      * 48    ; >=
+op_LE      * 49    ; <=
+op_RShift  * 50    ; >>
+op_LShift  * 51    ; <<
+op_AND     * 52    ; AND
+op_OR      * 53    ; OR
+op_EOR     * 54    ; EOR
+op_NOT     * 55    ; NOT
+op_Right   * 56    ; RIGHT
+op_Left    * 57    ; LEFT
+op_MOD     * 58    ; MOD
+op_Bottom  * 59
+op_VAL     * 63    ; VAL
+op_LRShift * 64    ; >>>
+op_LEN     * 65    ; LEN
+
+; TMD 12-Sep-89 - add separate tokens for monadic versions of + and -
+
+op_UnaryPlus * 66  ; unary plus
+op_UnaryMinus * 67 ; unary minus
+
+; so 40-67 inclusive is filled.
+
+        MACRO
+$label  ePush  $reglist
+$label  STMFD  R11!, {$reglist}
+        CMP    R11, R10
+        BLE    StackOFloErr
+        MEND
+
+        MACRO
+$label  ePull  $reglist, $writeback, $cc
+      [ "$writeback" = ""
+        LDM$cc.FD  R11!, {$reglist}
+      |
+        LDM$cc.FD  R11, {$reglist}
+      ]
+        MEND
+
+;*************************************************************************
+; SWI EvalExp.
+; In  : R0 -> string
+;       R1 -> buffer
+;       R2 maxchars
+; Out : R0 unchanged.
+;       IF R1 = 0, R2 is an integer
+;       IF R1<>0, buffer has a string, length in R2.
+;       V set if bad expression, buffer overflow
+;*************************************************************************
+
+ExprBuffOFlo ROUT
+        ADRL    R0, ErrorBlock_BuffOverflow
+      [ International
+        BL      TranslateError
+      ]
+        STR     R0, [stack]
+        Pull    "R0-R4, lr"
+        B       SLVK_SetV
+
+ReadExpression ROUT
+        Push    "R0-R4, lr"
+        TEQP    PC, #SVC_mode   ; interrupts on, ta.
+        LDR     R12, =ExprWSpace
+        STR     R13, ExprSVCstack
+        ADR     R1, ExprBuff
+        MOV     R2, #256
+        ORR     R2, R2, #(1 :SHL: 30) :OR: (1 :SHL: 31)
+        SWI     XOS_GSTrans   ; No | transformation, no " or space termination.
+                              ; so can never go wrong!
+        BCS     ExprBuffOFlo
+        MOV     R0, #13
+        STRB    R0, [R1, R2]
+
+        LDR     R11, =ExprStackStart
+        LDR     R10, =ExprStackLimit
+        MOV     R0, #0
+        STRB    R0, exprBracDif
+        MOV     R0, #type_Operator
+        MOV     R2, #op_Bottom
+        STRB    R2, tos_op
+        STMFD   R11!, {R0, R2}  ; push "bottom"
+
+; All set : now chug round items.
+
+01
+        BL      GetFactor
+        CMP     R0, #type_Operator
+        BNE     %BT01
+
+        CMP     R2, #op_Ket
+        BNE     %FT02
+        LDRB    R3, exprBracDif
+ [ {TRUE}                       ; TMD 11-Sep-89 - save an instruction
+        SUBS    R3, R3, #1
+        BCC     BadBraErr
+ |
+        CMP     R3, #0
+        BEQ     BadBraErr
+        SUB     R3, R3, #1
+ ]
+        STRB    R3, exprBracDif
+
+03
+        LDRB    R3, tos_op
+        CMP     R3, #op_Bra
+        BLNE    compile_top_op
+        BNE     %BT03
+
+        ePull   "R0, R2"
+        CMP     R0, #type_Operator
+        BEQ     MissingOpErr
+        CMP     R0, #type_String
+        BLEQ    Pull_String
+        Push    "R0, R2"
+        ePull   "R0, R2"
+        CMP     R0, #type_Operator
+        BNE     MissingOrErr    ; discard "("
+        ePull   "R0, R2", No
+        CMP     R0, #type_Operator
+        BNE     MissingOrErr
+        STRB    R2, tos_op      ; reset tosop
+        Pull    "R0, R2"
+        CMP     R0, #type_String
+        BLEQ    Push_String
+        ePush   "R0, R2"        ; move temp result down.
+        B       %BT01
+
+02
+        CMP     R2, #op_Bra
+        LDREQB  R3, exprBracDif
+        ADDEQ   R3, R3, #1
+        STREQB  R3, exprBracDif ; bracdif +:= 1
+
+; TMD 12-Sep-89 - now check for unary plus or minus
+
+        CMP     R2, #op_Plus                    ; if EQ then CS
+        TEQNE   R2, #(op_Minus :SHL: 2),2       ; if EQ then CC
+        ePull   "R0, R4", No, EQ                ; if +/- and top item is op
+        TEQEQ   R0, #type_Operator, 0           ; then it's unary plus/minus
+        BNE     %FT10                           ; else normal
+
+        MOVCS   R2, #op_UnaryPlus               ; CS => unary plus
+        MOVCC   R2, #op_UnaryMinus              ; CC => unary minus
+10
+
+;  WHILE lp (tos.op) > rp (itemtype) DO compile.top.op ()
+
+        ADR     R4, rightprectab-op_Bra
+        LDRB    R4, [R4, R2]
+04
+        ADR     R0, leftprectab-op_Bra
+        LDRB    R3, tos_op
+        LDRB    R0, [R0, R3]
+        CMP     R0, R4
+        BLGT    compile_top_op
+        BGT     %BT04
+
+        MOV     R0, #type_Operator
+        ePush   "R0, R2"        ;  push (operator)
+        STRB    R2, tos_op
+        CMP     R2, #op_Bottom
+        BNE     %BT01
+
+; check proper expr, return it.
+; should have bum/result/bum on stack.
+
+        ePull   "R0, R2"        ; this one's forced to be bottom
+        ePull   "R0, R2"
+        CMP     R0, #type_Operator
+        BEQ     MissingOpErr
+        CMP     R0, #type_String
+        BLEQ    Pull_String
+        Push    "R0, R2"
+        ePull   "R0, R2"
+        CMP     R0, #type_Operator ; if an op's there, it has to be bottom
+        Pull    "R1, R2"
+        BNE     MissingOpErr
+
+        Pull    "R0, R3, R4"    ; original R1, R2 -> R3, R4
+        CMP     R1, #type_Integer
+        Pull    "R3, R4, lr", EQ
+        ExitSWIHandler EQ
+        CMP     R4, R2
+        BGE     ExprBuffOK
+        MOV     R2, R4          ; no chars to move.
+        ADRL    R0, BufferOFloError
+        LDR     lr, [stack, #4*2]
+        ORR     lr, lr, #V_bit
+        STR     lr, [stack, #4*2]
+ExprBuffOK
+        MOV     R1, R3
+        LDR     R4, =exprSTRACC ; get ptr to it.
+        Push    "R2"
+06
+        SUBS    R2, R2, #1
+        LDRPLB  R3, [R4, R2]
+        STRPLB  R3, [R1, R2]
+        BPL     %BT06
+        Pull    "R2-R4, lr"
+        ExitSWIHandler
+
+leftprectab
+;    Bra  Ket  Time Plus NE   Minu STR  Divi GE   LE   RShi LShift
+   = 2,   1,   8,   7,   6,   7,   9,   8,   6,   6,   6,   6
+;    AND  OR   EOR  NOT  Righ Left MOD  Bott LT   EQ   GT   VAL LRSh
+   = 5,   4,   4,   9,   9,   9,   8,   1,   6,   6,   6,   9,  6
+;    LEN  Un+  Un-
+   = 9,   9,   9
+
+rightprectab
+;    Bra  Ket  Time Plus NE   Minu STR  Divi GE   LE   RShi LShift
+   = 11,  0,   7,   6,   5,   6,   10,  7,   5,   5,   5,   5
+;    AND  OR   EOR  NOT  Righ Left MOD  Bott LT   EQ   GT   VAL LRSh
+   = 4,   3,   3,   10,  10,  10,  7,   1,   5,   5,   5,   10, 5
+;    LEN  Un+  Un-
+   = 10,  10,  10
+
+    ALIGN
+
+;*****************************************************************************
+
+compile_top_op ROUT
+; preserves the flags
+        Push    "R2-R4, lr"
+        ePull   "R0, R2"
+        CMP     R0, #type_Operator
+        BEQ     MissingOpErr            ; everybody needs a rhs op
+        CMP     R0, #type_String
+        BLEQ    Pull_String
+        ePull   "R3, R4"                ; must be tosop
+        CMP     R3, #type_Operator
+        BNE     MissingOrErr
+
+        SUB     R4, R4, #op_Bra
+        ADR     R3, Operator_Dispatch
+        LDR     R4, [R3, R4, LSL #2]
+        ADD     PC, R3, R4
+
+DispatchReturn
+        ePull   "R3, R4", No            ; pull with no writeback
+        CMP     R3, #type_Operator
+        BNE     MissingOrErr
+        STRB    R4, tos_op
+        CMP     R0, #type_String
+        BLEQ    Push_String
+        ePush   "R0, R2"                ; temp val -> stack
+
+        GRABS   "R2-R4, PC"
+
+; the routines in this table are entered with one operand popped,
+; any other op on stack ready to pop.
+; Return with temp val set up (R0, R2 and maybe exprSTRACC)
+; Can use R0, R2-R4 as reqd
+
+Operator_Dispatch
+        &       Bra_Code - Operator_Dispatch
+        &       0  ;  Ket_Code - Operator_Dispatch - can't happen
+        &       Times_Code - Operator_Dispatch
+        &       Plus_Code - Operator_Dispatch
+        &       NE_Code - Operator_Dispatch
+        &       Minus_Code - Operator_Dispatch
+        &       STR_Code - Operator_Dispatch
+        &       Divide_Code - Operator_Dispatch
+        &       GE_Code - Operator_Dispatch
+        &       LE_Code - Operator_Dispatch
+        &       RShift_Code - Operator_Dispatch
+        &       LShift_Code - Operator_Dispatch
+        &       AND_Code - Operator_Dispatch
+        &       OR_Code - Operator_Dispatch
+        &       EOR_Code - Operator_Dispatch
+        &       NOT_Code - Operator_Dispatch
+        &       Right_Code - Operator_Dispatch
+        &       Left_Code - Operator_Dispatch
+        &       MOD_Code - Operator_Dispatch
+        &       0   ; Bottom_Code - Operator_Dispatch - can't happen
+        &       LT_Code - Operator_Dispatch
+        &       EQ_Code - Operator_Dispatch
+        &       GT_Code - Operator_Dispatch
+        &       VAL_Code - Operator_Dispatch
+        &       LRShift_Code- Operator_Dispatch
+        &       LEN_Code- Operator_Dispatch
+        &       UnPlus_Code- Operator_Dispatch
+        &       UnMinus_Code- Operator_Dispatch
+
+;**************************************************************************
+; dispatch  routines
+
+;--------------------------------------------------------------------------
+; monadic operators
+
+VAL_Code    ROUT  ; VAL string (VAL integer is NOP)
+UnPlus_Code ROUT  ; + integer (same code as VAL)
+        CMP     R0, #type_String
+        BLEQ    StringToInteger
+        B       DispatchReturn
+
+STR_Code ROUT  ; STR integer (STR string is NOP)
+        CMP     R0, #type_Integer
+        BLEQ    IntegerToString
+        B       DispatchReturn
+
+LEN_Code ROUT  ; LEN string
+        CMP     R0, #type_Integer
+        BLEQ    IntegerToString
+        MOV     R0, #type_Integer   ; and R2 is length!
+        B       DispatchReturn
+
+NOT_Code     ROUT  ; NOT integer
+        CMP     R0, #type_String
+        BLEQ    StringToInteger
+        MVN     R2, R2
+        B       DispatchReturn
+
+UnMinus_Code ROUT ; - integer
+        CMP     R0, #type_String
+        BLEQ    StringToInteger
+        RSB     R2, R2, #0
+        B       DispatchReturn
+
+;--------------------------------------------------------------------------
+; diadic plus
+
+Plus_Code       ROUT  ; integer+integer ; string+string
+        ePull   "R3, R4"
+;       CMP     R3, #type_Operator      ; can't be operator as unary plus
+;       BEQ     %FT01                   ; is separately dispatched now
+        CMP     R0, #type_String
+        BEQ     %FT02
+        CMP     R3, #type_String
+        BLEQ    PullStringToInteger     ; in R4
+        ADD     R2, R2, R4
+        B       DispatchReturn
+
+02
+        CMP     R3, #type_String
+        BEQ     %FT03
+        BL      StringToInteger
+        ADD     R2, R2, R4
+        B       DispatchReturn
+
+03
+        ADD     R0, R2, R4
+        CMP     R0, #255
+        BGT     StrOFloErr
+        LDR     R3, =exprSTRACC
+        Push    "R0"                    ; new length
+        ADD     R0, R3, R0
+        ADD     R3, R3, R2
+  ; copy R2 bytes from --(R3) to --(R0)
+04
+        SUBS    R2, R2, #1
+        LDRGEB  R4, [R3, #-1]!
+        STRGEB  R4, [R0, #-1]!
+        BGE     %BT04
+; R0-exprSTRACC is no of chars in stacked string
+        LDR     R3, =exprSTRACC
+        SUB     R0, R0, R3
+05
+        SUBS    R0, R0, #1
+        LDRGEB  R2, [R11], #1
+        STRGEB  R2, [R3], #1
+        BGE     %BT05
+        ADD     R11, R11, #3
+        BIC     R11, R11, #3            ; realign stack
+        Pull    "R2"
+        MOV     R0, #type_String
+        B       DispatchReturn
+
+Minus_Code    ROUT  ; integer-integer
+;       ePull   "R3, R4", No            ; can't be unary minus - this is
+;       CMP     R3, #type_Operator      ; separately dispatched now
+;       BEQ     %FT01
+        BL      TwoIntegers
+        SUB     R2, R4, R2
+        B       DispatchReturn
+
+;---------------------------------------------------------------------------
+; integer pair only : maths
+
+Times_Code   ROUT  ; integer*integer
+        BL      TwoIntegers
+        MOV     R3, R2
+        MULTIPLY R2, R3, R4             ; get R3*R4->R2
+        B       DispatchReturn
+
+MOD_Code     ROUT  ; integer MOD integer
+        Push    "R5"
+        MOV     R5, #&80000000
+        B       DivModCommon
+
+Divide_Code  ROUT  ; integer/integer
+        Push    "R5"
+        MOV     R5, #0
+DivModCommon
+        BL      TwoIntegers             ; want R4/R2
+        CMP     R2, #0
+        Pull    "R5", EQ
+        BEQ     DivZeroErr
+        RSBMI   R2, R2, #0
+        EORMIS  R5, R5, #1
+        EORMI   R5, R5, #1              ; oops-wanted MOD, ignore this sign
+        CMP     R4, #0
+        EORMI   R5, R5, #1
+        RSBMI   R4, R4, #0
+        DivRem  R3, R4, R2, R0          ; R3 := R4 DIV R2; R4 := R4 REM R2
+        MOVS    R5, R5, LSL #1          ; CS if MOD, NE if -ve
+        MOVCS   R2, R4
+        MOVCC   R2, R3
+        RSBNE   R2, R2, #0
+        MOV     R0, #type_Integer
+        Pull    "R5"
+        B       DispatchReturn
+
+;---------------------------------------------------------------------------
+; integer pair only : logical
+
+AND_Code ROUT                   ; integer AND integer
+        BL      TwoIntegers
+        AND     R2, R2, R4
+        B       DispatchReturn
+
+OR_Code ROUT                    ; integer OR integer
+        BL      TwoIntegers
+        ORR     R2, R2, R4
+        B       DispatchReturn
+
+EOR_Code ROUT                   ; integer EOR integer
+        BL      TwoIntegers
+        EOR     R2, R2, R4
+        B       DispatchReturn
+
+;----------------------------------------------------------------------------
+; mixed operands
+
+Right_Code ROUT                 ; string RIGHT integer
+        CMP     R0, #type_Integer
+        BLNE    StringToInteger
+        MOV     R4, R2
+        ePull   "R0, R2"
+        CMP     R0, #type_String
+        BNE     %FT01
+        BL      Pull_String
+02   ; string in stracc, R2 chars available, R4 chars wanted.
+        CMP     R2, R4
+        BLO     DispatchReturn  ; ignore if R4 -ve or bigger than available
+        LDR     R0, =exprSTRACC
+        ADD     R3, R0, R2
+        SUB     R3, R3, R4      ; mov from R3 to R0, R4 bytes
+03
+        LDRB    R2, [R3], #1
+        SUBS    R4, R4, #1
+        STRGEB  R2, [R0], #1
+        BGE     %BT03
+        LDR     R2, =exprSTRACC
+        SUB     R2, R0, R2  ; get length back.
+        MOV     R0, #type_String
+        B       DispatchReturn
+
+01
+        CMP     R0, #type_Operator
+        BEQ     MissingOpErr
+        BL      IntegerToString
+        B       %BT02
+
+Left_Code ROUT                  ; string LEFT integer
+        CMP     R0, #type_Integer
+        BLNE    StringToInteger
+        MOV     R4, R2
+        ePull   "R0, R2"
+        CMP     R0, #type_String
+        BNE     %FT01
+        BL      Pull_String
+02
+        CMP     R4, R2
+        MOVLO   R2, R4          ; only use new length if +ve and < current length
+        B       DispatchReturn
+
+01
+        CMP     R0, #type_Operator
+        BEQ     MissingOpErr
+        BL      IntegerToString
+        B       %BT02
+
+;-----------------------------------------------------------------------
+; relational operators
+
+EQ_Code ROUT                    ; integer = integer ; string = string
+        BL      Comparison
+        MOVEQ   R2, #Expr_True
+        MOVNE   R2, #Expr_False
+        B       DispatchReturn
+
+NE_Code ROUT                    ; integer<>integer ; string<>string
+        BL      Comparison
+        MOVNE   R2, #Expr_True
+        MOVEQ   R2, #Expr_False
+        B       DispatchReturn
+
+GT_Code ROUT                    ; integer > integer ; string>string
+        BL      Comparison
+        MOVGT   R2, #Expr_True
+        MOVLE   R2, #Expr_False
+        B       DispatchReturn
+
+LT_Code ROUT                    ; integer < integer ; string<string
+        BL      Comparison
+        MOVLT   R2, #Expr_True
+        MOVGE   R2, #Expr_False
+        B       DispatchReturn
+
+GE_Code ROUT                    ; integer >= integer ; string>=string
+        BL      Comparison
+        MOVGE   R2, #Expr_True
+        MOVLT   R2, #Expr_False
+        B       DispatchReturn
+
+LE_Code ROUT                    ; integer <= integer ; string<=string
+        BL      Comparison
+        MOVLE   R2, #Expr_True
+        MOVGT   R2, #Expr_False
+        B       DispatchReturn
+
+;--------------------------------------------------------------------------
+; shift operators
+
+RShift_Code ROUT                ; integer >> integer
+        BL      TwoIntegers
+        CMP     R2, #0
+        RSBLT   R2, R2, #0
+        BLT     NegRShift
+NegLShift
+        CMP     R2, #32
+        MOVGE   R2, R4, ASR #31 ; sign extend all through
+        MOVLT   R2, R4, ASR R2
+        B       DispatchReturn
+
+LRShift_Code ROUT               ; integer >>> integer
+        BL      TwoIntegers
+        CMP     R2, #0
+        RSBLT   R2, R2, #0
+        BLT     NegRShift
+        CMP     R2, #32
+        MOVGE   R2, #0
+        MOVLT   R2, R4, LSR R2
+        B       DispatchReturn
+
+LShift_Code ROUT                ; integer << integer
+        BL      TwoIntegers
+        CMP     R2, #0
+        RSBLT   R2, R2, #0
+        BLT     NegLShift
+NegRShift
+        CMP     R2, #32
+        MOVGE   R2, #0
+        MOVLT   R2, R4, LSL R2
+        B       DispatchReturn
+
+;---------------------------------------------------------------------------
+; Support routines :
+
+TwoIntegers   ROUT
+        Push    "lr"
+        CMP     R0, #type_String
+        BLEQ    StringToInteger
+        ePull   "R3, R4"
+        CMP     R3, #type_Operator
+        BEQ     MissingOpErr
+        CMP     R3, #type_String
+        BLEQ    PullStringToInteger
+        Pull    "PC"
+
+Comparison    ROUT
+        Push    "lr"
+        ePull   "R3, R4"
+        CMP     R3, #type_Operator
+        BEQ     MissingOpErr
+        CMP     R0, #type_String
+        BEQ     %FT01
+        CMP     R3, #type_String
+        BLEQ    PullStringToInteger
+        CMP     R4, R2
+        Pull    "PC"
+
+01
+        CMP     R3, #type_String
+        BEQ     %FT02
+        BL      StringToInteger
+        CMP     R4, R2
+        Pull    "PC"
+
+02
+        MOV     R3, R11
+        ADD     R11, R11, R4
+        ADD     R11, R11, #3
+        BIC     R11, R11, #3
+;    $R3, length R4 against $exprSTRACC, length R2
+        Push    "R1, R2, R4, R5"
+        CMP     R2, R4
+        MOVGT   R2, R4                  ; minm length -> R2
+        LDR     R0, =exprSTRACC
+03
+        SUBS    R2, R2, #1
+        BLT     %FT04
+        LDRB    R1, [R0], #1
+        LDRB    R5, [R3], #1
+        CMP     R5, R1
+        BEQ     %BT03
+        MOV     R0, #type_Integer
+        Pull    "R1, R2, R4, R5, PC"
+
+04
+        Pull    "R1, R2, R4, R5"
+        CMP     R4, R2
+        MOV     R0, #type_Integer
+        Pull    "PC"
+
+StringToInteger ROUT
+        Push    "R1, R3, R4, lr"
+        LDR     R1, =exprSTRACC
+        ADD     R3, R1, R2              ; end pointer to check all string used.
+        MOV     R0, #13
+        STRB    R0, [R1, R2]            ; force terminator in
+01
+        LDRB    R0, [R1], #1
+        CMP     R0, #" "
+        BEQ     %BT01
+        MOV     R4, #0
+        CMP     R0, #"-"
+        MOVEQ   R4, #-1
+        CMPNE   R0, #"+"
+        SUBNE   R1, R1, #1
+        MOV     R0, #10
+        SWI     XOS_ReadUnsigned
+02
+        LDRB    R0, [R1], #1
+        CMP     R0, #" "
+        BEQ     %BT02
+        SUB     R1, R1, #1
+        CMP     R1, R3
+        BNE     BadIntegerErr
+        MOV     R0, #type_Integer
+        CMP     R4, #0
+        RSBNE   R2, R2, #0
+        Pull    "R1, R3, R4, PC"
+
+IntegerToString ROUT
+        Push    "R1, lr"
+        MOV     R0, R2
+        LDR     R1, =exprSTRACC
+        MOV     R2, #255
+        SWI     XOS_BinaryToDecimal
+        MOV     R0, #type_String
+        Pull    "R1, PC"
+
+PullStringToInteger ROUT        ; corrupts exprSTRACC
+        Push    "R0, R2, lr"
+        MOV     R2, R4
+        BL      Pull_String
+        BL      StringToInteger
+        MOV     R4, R2
+        MOV     R3, #type_Integer
+        Pull    "R0, R2, PC"
+
+;******************************************************************************
+
+GetFactor ROUT
+; return type in R0
+; if operator, R2 has op_xxx
+; if integer/string, it has been pushed
+; R1 updated, R2 corrupted.
+
+10
+        LDRB    R0, [R1], #1
+        CMP     R0, #" "
+        BEQ     %BT10
+
+        CMP     R0, #13
+        BNE     %FT11
+        MOV     R2, #op_Bottom
+        MOV     R0, #type_Operator
+        MOV     PC, lr
+
+31
+        CMP     R0, #"@"-1      ; chars >= "@" are OK
+        BGT     %FT32
+        CMP     R0, #" "        ; chars <= " " always terminate
+        MOVLE   PC, lr
+        Push    "R2, R3"
+        ADR     R2, terminatename_map-"!"
+        LDRB    R3, [R2, R0]    ; termination map for " " < char < "@"
+        CMP     R3, #0
+        Pull    "R2, R3"
+        MOVEQ   PC, lr
+32
+        STRB    R0, [R3], #1
+        MOV     PC, lr          ; return with GT for OK, LE for naff
+
+terminatename_map       ; 1 means character allowed
+        ;  !  "  #  $  %  &  '  (  )  *  +  ,  -  .  /  0  1  2  3  4  5  6  7  8  9  :  ;  <  =  >  ?
+        =  1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1
+        ALIGN
+
+11
+        CMP     R0, #"&"        ; hex number?
+        CMPNE   R0, #"0"
+        RSBGTS  R2, R0, #"9"
+        BGE     %FT03           ; got to get a number.
+
+        CMP     R0, #""""
+        BEQ     %FT04           ; string.
+
+  ; look for operator
+        Push    "R3"
+        ADR     R2, operator_table
+20
+        LDRB    R3, [R2], #1
+        CMP     R3, #0          ; end of table?
+        BEQ     %FT30
+        CMP     R0, R3
+        BEQ     %FT21
+22
+        LDRB    R3, [R2], #1
+        CMP     R3, #0
+        BNE     %BT22
+        ADD     R2, R2, #1      ;   skip op_xxx
+        B       %BT20
+21
+        Push    "R1"
+24
+        LDRB    R3, [R2], #1
+        CMP     R3, #0
+        BEQ     %FT23
+        LDRB    R0, [R1], #1
+        CMP     R0, R3
+        BEQ     %BT24
+        Pull    "R1"
+        LDRB    R0, [R1, #-1]
+        B       %BT22
+23
+        Pull    "R3"            ; junk R1
+        Pull    "R3"
+        LDRB    R2, [R2]
+        MOV     R0, #type_Operator
+        MOV     PC, lr          ; got an operator.
+
+30
+        LDR     R3, =exprSTRACC
+ ; assume variable name : try and read it.
+        Push    "lr"
+        BL      %BT31           ; check R0 for allowed in name, insert.
+        BLE     NaffItemErr
+33
+        LDRB    R0, [R1], #1
+        BL      %BT31
+        BGT     %BT33
+        SUB     R1, R1, #1
+        MOV     R0, #13
+        STRB    R0, [R3], #1
+ ; potential name in exprSTRACC
+        Push    "R1, R4"
+        LDR     R0, =exprSTRACC
+        MOV     R2, #-1         ; just test for existence first
+        MOV     R3, #0
+        MOV     R4, #0          ; no expansion
+        SWI     XOS_ReadVarVal
+        CMP     R2, #0
+        BEQ     NaffItemErr
+        LDR     R1, =exprSTRACC ; overwrite name with value
+        MOV     R0, R1          ; overwritten by VSet return
+        MOV     R2, #255
+        MOV     R3, #0
+        CMP     R4, #VarType_Macro
+        MOVEQ   R4, #VarType_Expanded
+        SWI     XOS_ReadVarVal
+        BVS     StrOFloErr
+        CMP     R4, #VarType_Number
+        LDREQ   R2, [R1]
+        MOVEQ   R0, #type_Integer
+        BLNE    Push_String
+        MOVNE   R0, #type_String
+        ePush   "R0, R2"
+        Pull    "R1, R4, lr"
+        Pull    "R3"
+        MOV     PC, lr
+
+operator_table
+        =       "("    , 0, op_Bra
+        =       ")"    , 0, op_Ket
+        =       "+"    , 0, op_Plus
+        =       "-"    , 0, op_Minus
+        =       "*"    , 0, op_Times
+        =       "/"    , 0, op_Divide
+        =       "="    , 0, op_EQ
+        =       "<>"   , 0, op_NE
+        =       "<="   , 0, op_LE
+        =       "<<"   , 0, op_LShift
+        =       "<"    , 0, op_LT
+        =       ">="   , 0, op_GE
+        =       ">>>"  , 0, op_LRShift
+        =       ">>"   , 0, op_RShift
+        =       ">"    , 0, op_GT
+        =       "AND"  , 0, op_AND
+        =       "OR"   , 0, op_OR
+        =       "EOR"  , 0, op_EOR
+        =       "NOT"  , 0, op_NOT
+        =       "RIGHT", 0, op_Right
+        =       "LEFT" , 0, op_Left
+        =       "MOD"  , 0, op_MOD
+        =       "STR"  , 0, op_STR
+        =       "VAL"  , 0, op_VAL
+        =       "LEN"  , 0, op_LEN
+        =       0
+        ALIGN
+
+03
+        SUB     R1, R1, #1      ; point at string start
+        Push    "lr"
+        MOV     R0, #10
+        SWI     XOS_ReadUnsigned
+        LDRVS   R13, ExprSVCstack
+        BVS     BumNumber2      ; already messagetransed, so don't do it again MED-01583
+        MOV     R0, #type_Integer
+        ePush   "R0, R2"
+        Pull    "PC"
+
+ExprErrCommon
+BumNumber
+        LDR     R13, ExprSVCstack
+      [ International
+        BL      TranslateError
+      ]
+BumNumber2
+        STR     R0, [stack]
+        Pull    "R0-R4, lr"
+        MOV     R1, #0          ; haven't put anything in buffer
+        B       SLVK_SetV
+BadStringErr
+        ADRL    R0, ErrorBlock_BadString
+        B       ExprErrCommon
+Bra_Code
+BadBraErr
+        ADR     R0, ErrorBlock_BadBra
+        B       ExprErrCommon
+        MakeErrorBlock BadBra
+StackOFloErr
+        ADR     R0, ErrorBlock_StkOFlo
+        B       ExprErrCommon
+        MakeErrorBlock StkOFlo
+MissingOpErr
+        ADR     R0, ErrorBlock_MissOpn
+        B       ExprErrCommon
+        MakeErrorBlock MissOpn
+MissingOrErr
+        ADR     R0, ErrorBlock_MissOpr
+        B       ExprErrCommon
+        MakeErrorBlock MissOpr
+BadIntegerErr
+        ADR     R0, ErrorBlock_BadInt
+        B       ExprErrCommon
+        MakeErrorBlock BadInt
+StrOFloErr
+        ADR     R0, ErrorBlock_StrOFlo
+        B       ExprErrCommon
+        MakeErrorBlock StrOFlo
+NaffItemErr
+        ADR     R0, ErrorBlock_NaffItm
+        B       ExprErrCommon
+        MakeErrorBlock NaffItm
+DivZeroErr
+        ADR     R0, ErrorBlock_DivZero
+        B       ExprErrCommon
+        MakeErrorBlock DivZero
+
+04
+        LDR     R2, =exprSTRACC
+05
+        LDRB    R0, [R1], #1
+        CMP     R0, #13
+        CMPNE   R0, #10
+        CMPNE   R0, #0
+        BEQ     BadStringErr
+        CMP     R0, #""""
+        BEQ     %FT06
+07
+        STRB    R0, [R2], #1    ; can't overflow - comes from buffer
+        B       %BT05
+
+06
+        LDRB    R0, [R1], #1
+        CMP     R0, #""""
+        BEQ     %BT07
+        SUB     R1, R1, #1
+        LDR     R0, =exprSTRACC
+        SUB     R2, R2, R0      ; length to R2
+        Push    "lr"
+        BL      Push_String
+        ePush   "R0, R2"
+        Pull    "PC"
+
+Push_String  ROUT
+        Push    "R2, R3"
+        SUBS    R2, R2, #1
+        BMI     %FT02
+        BIC     R2, R2, #3
+        LDR     R0, =exprSTRACC
+01
+        LDR     R3, [R0, R2]
+        ePush   "R3"
+        SUBS    R2, R2, #4
+        BGE     %BT01
+02
+        Pull    "R2, R3"
+        MOV     R0, #type_String
+        MOV     PC, lr
+
+Pull_String ROUT
+        CMP     R2, #0
+        MOVEQ   PC, lr
+        Push    "R0, R2, R3"
+        LDR     R0, =exprSTRACC
+01
+        ePull   "R3"
+        STR     R3, [R0], #4
+        SUBS    R2, R2, #4
+        BGT     %BT01
+        Pull    "R0, R2, R3"
+        MOV     PC, lr
+
+        LTORG
+
+;*****************************************************************************
+
+; Configure and Status
+
+; The configuration table : some types and macros first.
+
+ConType_NoParm  * 1
+ConType_Field   * 2
+ConType_Special * 3
+ConType_Size    * 4
+
+; Type Special has another table :
+; code to set it
+; code to show it
+; string to print for Configure listing.
+; Keep table position as offset from table entry
+
+        MACRO
+        Config_Special   $name
+        =       ConType_Special, "$name", 0
+        ALIGN
+        &       Config_$name._table - .
+        MEND
+
+; Table offset :
+Config_Special_SetCode  * 0
+Config_Special_ShowCode * 4
+Config_Special_String   * 8
+
+; Type NoParm  : *con. name
+; put $value into bits $bitoff to $bitoff+$fwidth in byte $byteoff
+
+        MACRO
+        Config_NoParm   $name, $bitoff, $fwidth, $bytoff, $value
+        =       ConType_NoParm, "$name", 0
+        ALIGN
+        =       $bitoff, $fwidth, $bytoff, $value
+        MEND
+
+; Type Field   : *con. name number
+; read value & put into bits $bitoff to $bitoff+$fwidth in byte $byteoff
+
+        MACRO
+        Config_Field   $name, $bitoff, $fwidth, $bytoff
+        =       ConType_Field, "$name", 0
+        ALIGN
+        =       $bitoff, $fwidth, $bytoff, 0
+        MEND
+
+; Type Size   : *con. name number|nK
+; read value & put into bits $bitoff to $bitoff+$fwidth in byte $byteoff
+
+        MACRO
+$l      Config_Size   $name, $bitoff, $fwidth, $bytoff
+        =       ConType_Size, "$name", 0
+        ALIGN
+$l      =       $bitoff, $fwidth, $bytoff, 0
+        MEND
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; now the table
+
+Config_Table
+        Config_Special  Baud
+AlternateBoot
+        Config_NoParm   Boot,           4, 0, DBTBCMOS, 1
+AlternateNoBoot
+        Config_NoParm   NoBoot,         4, 0, DBTBCMOS, 0
+AlternateCaps
+        Config_NoParm   Caps,           3, 2, StartCMOS, 4
+AlternateNoCaps
+        Config_NoParm   NoCaps,         3, 2, StartCMOS, 2
+ExpandShCaps
+        Config_NoParm   ShCaps,         3, 2, StartCMOS, 1
+EndListCapsFrig
+        Config_Field    Data,           5, 2, DBTBCMOS
+        Config_Field    Delay,          0, 7, KeyDelCMOS
+ExpandDir
+        Config_NoParm   Dir,            6, 0, StartCMOS, 0
+ExpandNoDir
+        Config_NoParm   NoDir,          6, 0, StartCMOS, 1
+        Config_Field    DumpFormat,     0, 4, TutuCMOS
+        Config_Size     FontSize,       0, 7, FontCMOS
+FontSizeFrig
+        Config_Special  Ignore
+        Config_Field    Language,       0, 7, LanguageCMOS
+AlternateLoud
+        Config_NoParm   Loud,           1, 0, DBTBCMOS, 1
+        Config_Special  Mode
+        Config_Special  MonitorType
+        Config_Special  MouseStep
+ [ AssemblePointerV
+        Config_Special  MouseType
+ ]
+        Config_Field    Print,          5, 2, PSITCMOS
+ [ :LNOT: DriversInKernel
+        Config_Size     PrinterBufferSize, 0, 7, PrinterBufferCMOS
+PrinterBufferFrig
+ ]
+AlternateQuiet
+        Config_NoParm   Quiet,          1, 0, DBTBCMOS, 0
+        Config_Size     RamFsSize,      0, 6, RAMDiscCMOS
+        Config_Field    Repeat,         0, 7, KeyRepCMOS
+        Config_Size     RMASize,        0, 6, RMASizeCMOS
+        Config_Size     ScreenSize,     0, 6, ScreenSizeCMOS
+ScreenSizeFrig
+AlternateScroll
+        Config_NoParm   Scroll,         3, 0, DBTBCMOS, 0
+AlternateNoScroll
+        Config_NoParm   NoScroll,       3, 0, DBTBCMOS, 1
+        Config_Size     SpriteSize,     0, 6, SpriteSizeCMOS
+        Config_Special  Sync
+        Config_Size     SystemSize,     0, 5, SysHeapCMOS
+        Config_Special  TV
+        Config_Special  WimpMode
+        =       0
+
+NoDirString =  "No"
+DirString   =  "Directory", 0
+ShCapsString = "ShiftCaps", 0
+
+        ALIGN
+
+ExpandFrig * 8    ; see code that shows NoParm options.
+ExpandTab
+        &       ExpandDir       - ExpandFrig-.
+        &       DirString       - .-1           ; another printing fudge!
+        &       ExpandNoDir     - ExpandFrig-.
+        &       NoDirString     - .-1
+        &       ExpandShCaps    - ExpandFrig-.
+        &       ShCapsString    - .-1
+        &       0
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        MACRO
+        Config_Special_Table $name, $text
+Config_$name._table
+        B       Config_$name._setcode
+        B       Config_$name._showcode
+        =       "$text", 0
+        ALIGN
+        MEND
+
+        ALIGN
+        Config_Special_Table Baud, "<D>"
+        Config_Special_Table TV, "[<D> [[,] <D>]]"
+        Config_Special_Table Mode, "<D> | Auto"
+        Config_Special_Table Ignore, "[<D>]"
+        Config_Special_Table MouseStep, "<D>"
+ [ AssemblePointerV
+        Config_Special_Table MouseType, "<D>"
+ ]
+        Config_Special_Table MonitorType, "<D> | Auto"
+        Config_Special_Table Sync, "<D> | Auto"
+        Config_Special_Table WimpMode, "<D> | Auto"
+
+;*****************************************************************************
+; Lookup : R0 -> option
+;   Exit : R2 -> table entry, EQ for not found
+;          R0 stepped on
+
+FindOption ENTRY "r1, r3-r5"
+        ADRL    r2, Config_Table+1
+04
+        MOV     r1, #0                         ; offset
+01
+        LDRB    r3, [r0, r1]
+        LDRB    r4, [r2, r1]
+        CMP     r3, #32
+        CMPLE   r4, #32
+        BLE     %FT02
+        UpperCase r3, r5
+        UpperCase r4, r5
+        CMP     r3, r4
+        ADDEQ   r1, r1, #1
+        BEQ     %BT01
+        CMP     r3, #"."
+        MOV     r3, #Z_bit
+        TEQP    r3, pc                          ; invert EQ/NE
+        CMPNE   r1, #0
+        ADDNE   r1, r1, #1                      ; skip .
+        BNE     %FT02
+03
+        LDRB    r1, [r2], #1
+        CMP     r1, #0
+        BNE     %BT03
+        ADD     r2, r2, #7                      ; skip infoword
+        BIC     r2, r2, #3
+        LDRB    r1, [r2], #1
+        CMP     r1, #0
+        BNE     %BT04
+        EXIT                                    ; failure exit
+
+02
+        ADD     r0, r0, r1                      ; point at char after option
+        SUBS    r2, r2, #1
+        EXIT                                    ; return with success
+
+;****************************************************************************
+;
+; Configure
+; IF noparms OR parm=. THEN list options : issue service call : finish listing
+;                      ELSE lookup parm1 : doit
+;    IF notfound THEN issue service
+
+Configure_Help ROUT
+        Push    "r0, lr"
+        ADR     r0, %FT01
+        MOV     r1, #Status_Keyword_Flag
+        B       KeyHelpCommon
+01
+        DCB     "HUTMCON", 0
+        ALIGN
+
+Configure_Code  ROUT
+        Push    "lr"
+        CMP     r1, #0          ; noparms?
+        MOVEQ   r3, #0
+        BEQ     ListAll         ; go listem.
+        BL      FindOption
+        BEQ     %FT01
+        LDRB    r4, [r2], #1
+03
+        LDRB    r1, [r2], #1
+        CMP     r1, #0
+        BNE     %BT03
+        ADD     r2, r2, #3
+        BIC     r2, r2, #3
+        LDR     r1, [r2]
+        CMP     r4, #ConType_Size
+        BEQ     ReadSizeParm
+
+        CMP     r4, #ConType_Field
+        ASSERT  ConType_Special > ConType_Field
+; if special dispatch it
+        ADDGT   r1, r1, r2                              ; point at node
+        ADDGT   pc, r1, #Config_Special_SetCode         ; call it
+; if noparm get value
+        MOVLT   r2, r1, LSR #24
+        BLEQ    ReadNumParm
+        BVS     BadConParm
+BaudEntry
+        BL      ConfigCheckEOL
+        Pull    "pc", VS
+IgnoreEntry
+        MOV     r0, r1                  ; info word
+        BL      ReadByte                ; current byte into R1
+
+        MOV     r3, r0, LSR #8
+        AND     r3, r3, #&FF            ; get fwidth
+        MOV     r4, #2
+        MOV     r4, r4, LSL r3
+        SUB     r4, r4, #1              ; get mask/maximum value
+        CMP     r2, r4
+        BHI     ConParmTooBig
+
+        AND     r3, r0, #&FF            ; get bitoff
+        BIC     r1, r1, r4, LSL r3      ; clear bits in correct position
+        ORR     r2, r1, r2, LSL r3      ; OR in new bits
+
+        MOV     r1, r0, LSR #16         ; get bytoff
+        AND     r1, r1, #&FF
+        MOV     r0, #WriteCMOS
+        SWI     XOS_Byte                ; and set it. Assume this clears V!
+
+        Pull    "pc"
+
+BadConParm
+        MOV     r0, #1
+        B       ConfigGenErr
+BadConParmError
+        &       ErrorNumber_Syntax
+        =       "NotNumeric:Numeric parameter needed", 0
+        ALIGN
+
+ConParmTooBig
+        MOV     r0, #2
+        B       ConfigGenErr
+ConParmTooBigError
+        &       ErrorNumber_Syntax
+        =       "ConParmTooBig:Configure parameter too big", 0
+        ALIGN
+01
+        MOV     r12, #Module_List
+conoptloop
+        LDR     r12, [r12]
+        CMP     r12, #0
+        BEQ     conoptservice
+        LDR     r1, [r12, #Module_code_pointer]
+        LDR     r2, [r1, #Module_HC_Table]
+        CMP     r2, #0
+        BEQ     conoptloop
+        MOV     r4, #Status_Keyword_Flag
+        BL      FindItem
+        BCC     conoptloop              ; next module
+        ADD     r0, r0, r3              ; point at commtail
+        LDR     r12, [r12, #Module_incarnation_list]    ; preferred life
+        ADDS    r12, r12, #Incarnation_Workspace        ; clear V
+        Push    "r1-r6"
+
+StKey_SkipSpaces
+        LDRB    r4, [r0], #1
+        CMP     r4, #" "
+        BEQ     StKey_SkipSpaces
+        SUB     r0, r0, #1
+
+        MOV     lr, pc
+        ADD     pc, r1, r5            ; call im
+        Pull    "r1-r6"
+        Pull    "pc", VC
+ConfigGenErr
+        CMP     r0, #3
+        BHI     ExitConfig
+        ADREQL  r0, Config2manyparms
+        CMP     r0, #2
+        ADREQ   r0, ConParmTooBigError
+        CMP     r0, #1
+        ADRLO   r0, BadConOptError
+        ADREQ   r0, BadConParmError
+      [ International
+        BL      TranslateError
+      ]
+ExitConfig
+        Pull    "lr"
+        ORRS    pc, lr, #V_bit
+
+conoptservice
+        MOV     r1, #Service_UKConfig
+        BL      Issue_Service
+        CMP     r1, #0
+        BNE     BadConOpt
+        CMP     r0, #0
+        BGE     ConfigGenErr
+        Pull    "pc"                    ; TBS means OK: note CMP has cleared V
+
+BadConOpt
+        MOV     r0, #0
+        B       ConfigGenErr
+BadConOptError
+        &       ErrorNumber_Syntax
+        =       "BadConOpt:Bad configure option", 0
+        ALIGN
+
+ReadNumParm ENTRY "r1"
+10
+        LDRB    r2, [r0], #1
+        CMP     r2, #" "
+        BEQ     %BT10
+        SUB     r1, r0, #1
+        SWI     XOS_ReadUnsigned
+        EXIT    VS
+        MOV     r0, r1
+        LDRB    r1, [r0]
+        CMP     r1, #" "
+        SETV    GT
+        EXIT
+
+; read a number or Auto
+; returns R2 = number or -1 for Auto
+;         R0 -> terminator
+
+ReadNumAuto ENTRY "r1,r3,r4"
+10
+        LDRB    r2, [r0], #1
+        CMP     r2, #" "
+        BEQ     %BT10
+        SUB     r1, r0, #1
+        ADR     r3, AutoString          ; string to match against
+        MOV     r4, #0                  ; no other terminators for $R1
+        BL      Module_StrCmp           ; out: EQ => match, r1 -> terminator
+                                        ;      NE => no match, r1 preserved
+                                        ;      r3 corrupted in both cases
+        MOVEQ   r2, #-1
+        BEQ     %FT20
+        SWI     XOS_ReadUnsigned
+        EXIT    VS
+20
+        MOV     r0, r1
+        LDRB    r1, [r0]
+        CMP     r1, #" "
+        SETV    GT
+        EXIT
+
+AutoString
+        =       "Auto", 0
+        ALIGN
+
+ReadSizeParm ROUT
+        Push    "r1, r8"
+        MOV     r8, r2
+02
+        LDRB    r2, [r0], #1
+        CMP     r2, #" "
+        BEQ     %BT02
+        SUB     r1, r0, #1
+        SWI     XOS_ReadUnsigned
+        Pull    "r1, r8", VS
+        BVS     BadConParm
+        MOV     r0, r1
+        LDRB    r1, [r0]
+        CMP     r1, #" "
+        BLE     %FT01
+        CMP     r1, #"k"
+        CMPNE   r1, #"K"
+        Pull   "r1, r8", NE
+        BNE     BadConParm
+ [ :LNOT: DriversInKernel
+        ADRL    r14, PrinterBufferFrig-4
+        TEQ     r8, r14                 ; if printer buffer size
+        TEQEQ   r2, #1                  ; and 1K
+        MOVEQ   r2, #0                  ; then use zero (default)
+ ]
+        ADRL    r14, FontSizeFrig-4     ; point at info word for fontsize
+        TEQ     r8, r14                 ; if fontsize
+        MOVEQ   r8, #4*1024             ; then use 4K (lucky it's a pagesize!)
+        MOVNE   r8, #0                  ; else use pagesize units
+        LDRNE   r8, [r8, #Page_Size]
+        ADRL    r14, PageShifts-1
+        LDRB    r14, [r14, r8, LSR #12]
+        SUB     r14, r14, #10           ; *1024
+        MOV     r8, r8, LSR #10         ; /1024
+        SUB     r8, r8, #1
+        ADD     r2, r2, r8
+        BIC     r2, r2, r8              ; round up to nearest pagesize
+        MOV     r2, r2, LSR r14         ; divide parm by pagesize
+        ADD     r0, r0, #1              ; point past "K" for EOL checking
+01
+        Pull    "r1, r8"
+        B       BaudEntry
+
+;*****************************************************************************
+; Status
+; list all options matched : allow . and <terminator> to match all
+; issue service
+
+Status_Code     ROUT
+        Push    "lr"
+        CMP     r1, #0          ; noparms?
+        MOVEQ   r3, #1
+        BEQ     ListAll         ; go listemall
+        CMP     r1, #1
+        BNE     %FT01
+        BL      FindOption
+        BEQ     %FT01
+        MOV     r3, #2
+        BL      ListOneConfig
+        Pull    "pc"
+
+01
+        MOV     r6, #Module_List
+statoptloop
+        LDR     r6, [r6]
+        CMP     r6, #0
+        BEQ     statoptservice
+        LDR     r1, [r6, #Module_code_pointer]
+        LDR     r2, [r1, #Module_HC_Table]
+        CMP     r2, #0
+        BEQ     statoptloop
+        MOV     r4, #Status_Keyword_Flag
+        BL      FindItem
+        BCC     statoptloop             ; next module
+        MOV     r0, #1
+        LDR     r12, [r6, #Module_incarnation_list]
+        ADD     r12, r12, #Incarnation_Workspace
+        Push    "r0-r6"
+        MOV     lr, pc
+        ADD     pc, r1, r5              ; call im
+        STRVS   r0, [sp]
+        Pull    "r0-r6, pc"
+
+statoptservice
+        MOV     r1, #Service_UKStatus
+        BL      Issue_Service
+        CMP     r1, #0
+        Pull    "pc", EQ
+        ADR     r0, %FT03
+      [ International
+        BL      TranslateError
+      ]
+        Pull    "lr"
+        ORRS    pc, lr, #V_bit
+03
+        &       ErrorNumber_Syntax
+        =       "BadStat:Bad status option", 0
+        ALIGN
+
+;*****************************************************************************
+
+; routine to list everything : on entry R3 = 0 means entered from configure
+;                                          = 1   "     "      "   status
+;                                       lr stacked for return
+
+ListAll ROUT
+        MOV     r0, #117                ; Read current VDU status
+        SWI     XOS_Byte                ; Won't fail
+        Push    "r1"
+
+      [ International
+        SWI     XOS_WriteI+14
+        BL      WriteS_Translated
+        =       "Config:Configuration",0
+        ALIGN
+        SWIVC   XOS_WriteI+" "
+      |
+        SWI     XOS_WriteS
+        =       14, "Configuration ", 0 ; paged mode on.
+        ALIGN
+      ]
+        Pull    "r1, pc", VS            ; Wrch can fail
+        CMP     r3, #0
+        ADREQ   r0, %FT06
+        ADRNE   r0, %FT08
+      [ International
+        BL      Write0_Translated
+      |
+        SWI     XOS_Write0
+      ]
+        SWIVC   XOS_NewLine
+        SWIVC   XOS_NewLine
+        Pull    "r1, pc", VS
+
+        ADRL    r2, Config_Table
+02
+        ADRL    r4, AlternateCaps
+        CMP     r4, r2
+        CMPEQ   r3, #1
+        BEQ     FrigCapsList
+        LDRB    r4, [r2]
+        CMP     r4, #0
+        BLNE    ListOneConfig
+        Pull    "r1, pc", VS
+        BNE     %BT02
+
+10
+        ADRL    r0, dotstring           ; match all
+        Push    "r3, r7"
+        MOV     r7, #Module_List
+listallmloop
+        LDR     r7, [r7]
+        CMP     r7, #0
+        BEQ     listallservice
+        LDR     r1, [r7, #Module_code_pointer]
+        LDR     r2, [r1, #Module_HC_Table]
+        CMP     r2, #0
+        BEQ     listallmloop
+listalltryfind
+        MOV     r4, #Status_Keyword_Flag
+        BL      FindItem
+        BCC     listallmloop            ; next module
+        LDR     r0, [stack]             ; pick up r3
+        LDR     r12, [r7, #Module_incarnation_list]
+        ADD     r12, r12, #Incarnation_Workspace
+        Push    "r0-r6"
+        MOV     lr, pc
+        ADD     pc, r1, r5              ; call im
+        Pull    "r0-r6"
+        ADD     r2, r2, #16             ; step to next field
+        ADRL    r0, dotstring
+        B       listalltryfind
+
+      [ International
+06
+        =       "Options:options:",0
+08
+        =       "Status:status:",0
+      |
+06
+        =       "options:",0
+08
+        =       "status:",0
+      ]
+        ALIGN
+
+listallservice
+        Pull    "r3, r7"
+        CMP     r3, #0
+        MOVEQ   r1, #Service_UKConfig
+        MOVNE   r1, #Service_UKStatus
+        MOV     r0, #0                  ; indicate list wanted
+        BL      Issue_Service
+        CMP     r3, #0
+      [ International
+        BEQ     %FT20
+        BL      GSWriteS_Translated
+        =       "STail:|J|MUse *Configure to set the options.|J|M",0
+        ALIGN
+        B       %FT30
+20
+        BL      GSWriteS_Translated
+        =       "CTail1:|J|MWhere:|J|MD is a decimal number, a hexadecimal number preceded by &,|J|M"
+        =       "or the base followed by underscore, followed|J|M",0
+        ALIGN
+        BL      GSWriteS_Translated
+        =       "CTail2:by digits in the given base.|J|MItems within [ ] are optional.|J|M"
+        =       "Use *Status to display the current settings.|J|M",0
+        ALIGN
+30
+      |
+        ADRNE   r0, statuslastline
+        ADREQ   r0, configlastline
+        SWI     XOS_Write0
+      ]
+        Pull    "r1"
+        Pull    "pc", VS                ; return error if set
+        TST     r1, #5
+        SWIEQ   XOS_WriteI+15  ; paged mode off
+        Pull    "pc"
+
+      [ :LNOT: International
+statuslastline
+        =       10,13, "Use *Configure to set the options.", 10,13,0
+configlastline
+        =       10,13, "Where:", 10,13
+        =       "D is a decimal number, " ;, 10,13
+        =       "a hexadecimal number preceded by &, ", 10,13
+        =       "or the base followed by underscore, followed", 10,13
+        =       "by digits in the given base.", 10,13
+        =       "Items within [ ] are optional.", 10,13
+        =       "Use *Status to display the current settings.", 10,13, 0
+        ALIGN
+      ]
+
+FrigCapsList
+        MOV     r3, #2
+        BL      ListOneConfig
+        Pull    "r1, pc", VS
+        MOV     r3, #1
+        ADRL    r2, EndListCapsFrig
+        B       %BT02
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+; routine to list one item :
+; R3 = 0 means entered from configure
+;    = 1   "     "      "   status
+;    = 2   "     "      "   status <item>
+; R2 points at the item, stepped to next on exit
+; Preserves flags
+
+ListOneConfig   ROUT
+        Push    "lr"
+        LDRB    r4, [r2]
+        CMP     r4, #ConType_Field
+        CMPNE   r4, #ConType_Size
+        CMPNE   r3, #0
+        BNE     %FT20
+
+        ADD     r0, r2, #1
+        SWI     XOS_Write0
+        BVS     ExitShow
+        SUB     r1, r0, r2      ; get length
+        ADD     r2, r0, #3      ; skip terminator
+        BIC     r2, r2, #3      ; and align
+
+        CMP     r4, #ConType_NoParm
+        BEQ     %FT07
+04
+        SWI     XOS_WriteI+" "
+        BVS     ExitShow
+        ADD     r1, r1, #1
+        CMP     r1, #12
+        BLS     %BT04
+
+        CMP     r3, #0
+        BNE     %FT30
+
+        CMP     r4, #ConType_Size
+        ADREQ   r0, %FT42
+        BEQ     %FT43
+
+        CMP     r4, #ConType_Field
+        ASSERT  ConType_Special > ConType_Field
+        ADREQ   r0, %FT05
+        LDRGT   r0, [r2]
+        ADDGT   r0, r0, r2                      ; point at node
+        ADDGT   r0, r0, #Config_Special_String  ; point at string
+43
+        SWI     XOS_Write0
+07
+        ADD     r2, r2, #4
+ExitShow
+        SWIVC   XOS_NewLine
+11
+        Pull    "lr"
+        BICVCS  pc, lr, #V_bit
+        ORRS    pc, lr, #V_bit
+05
+        =       "<D>", 0
+42
+        =       "<D>[K]", 0
+
+; status bits :
+
+20
+        ADD     r0, r2, #1              ; got to do *status on a NoParm or Special
+21
+        LDRB    r1, [r0], #1
+        CMP     r1, #0                  ; step past name
+        BNE     %BT21
+        ADD     r0, r0, #3
+        BIC     r0, r0, #3              ; align
+        LDR     r1, [r0]                ; get info word.
+        CMP     r4, #ConType_Special
+        ADDEQ   r1, r1, r0              ; point at node
+        ADDEQ   pc, r1, #Config_Special_ShowCode
+
+; if CRbytevalue = infowordvalue then print something
+
+        MOV     r4, r0                  ; hang on to it
+        MOV     r0, r1
+        BL      GetValue
+        MOV     r1, r1, LSR #24         ; value.
+        CMP     r0, r1
+        BNE     %FT10                   ; check for *Status <Item>
+
+; first see if expansion needed
+
+        ADRL    r0, ExpandTab
+22
+        LDR     r1, [r0], #8
+        CMP     r1, #0
+        BEQ     %FT23
+        ADD     r1, r1, r0              ; get real address
+        CMP     r1, r2
+        BNE     %BT22
+        LDR     r2, [r0, #-4]!
+14
+        ADD     r2, r2, r0              ; new string
+23
+        ADD     r2, r2, #1
+
+; now write chars with space between lowercase then upper
+
+        MOV     r1, #1                  ; indicate uppercase last
+24
+        LDRB    r0, [r2], #1
+        CMP     r0, #0
+        BEQ     %FT25
+        CMP     r0, #"Z"                ; uppercase if LE
+        CMPLE   r1, #0
+        SWILE   XOS_WriteI+" "
+        BVS     ExitShow
+        CMP     r0, #"Z"
+        MOVLE   r1, #1
+        MOVGT   r1, #0
+        SWI     XOS_WriteC
+        BVC     %BT24
+
+25
+        ADDVC   r2, r4, #4
+        B       ExitShow
+
+30
+        LDR     r0, [r2], #4            ; got to do *status for Field
+        CMP     r4, #ConType_Size
+        MOV     r4, r2
+        BL      GetValue
+        BEQ     %FT31
+        BL      PrintR0
+        B       ExitShow
+31
+        Push    "r8, r9"
+        ADRL    r8, FontSizeFrig
+        CMP     r4, r8
+        MOVNE   r8, #0
+        LDRNE   r8, [r8, #Page_Size]
+        MOVEQ   r8, #4*1024
+        ADRL    r9, PageShifts-1
+        LDRB    r9, [r9, r8, LSR #12]
+        SUB     r9, r9, #10
+        MOVS    r0, r0, LSL r9          ; size in K
+        BNE     %FT35
+ [ :LNOT: DriversInKernel
+        ADRL    r8, PrinterBufferFrig   ; if zero and PrinterBufferSize, then 1K
+        TEQ     r8, r2
+        MOVEQ   r0, #1
+        BEQ     %FT35
+ ]
+        ADRL    r8, ScreenSizeFrig      ; if zero and it's ScreenSize, then call OS_ReadSysInfo to find appropriate amount
+        TEQ     r8, r2
+        SWIEQ   XOS_ReadSysInfo         ; proper screen size (r0=0) on entry
+        MOVEQ   r0, r0, LSR #10
+35
+        Pull    "r8, r9"
+        BL      PrintR0
+        SWIVC   XOS_WriteI+"K"
+        B       ExitShow
+
+10
+        CMP     r3, #2
+        ADDNE   r2, r4, #4
+        BNE     %BT11
+
+; R0 is the value set : can corrupt R3 as this is the do-one entry
+
+        MOV     r3, r0
+        ADRL    r0, AlternateTab        ; look for option really set
+12
+        LDR     r1, [r0], #8            ; better find a match!
+        ADD     r1, r1, r0              ; get real address
+        CMP     r1, r2
+        BNE     %BT12
+        LDR     r2, [r0, #-4]!
+        ADD     r2, r2, r0              ; translation table
+        LDR     r0, [r2, r3, LSL #2]
+        B       %BT14                   ; go printit
+
+AlternateTab
+        &       AlternateBoot - ExpandFrig-.
+        &       %FT91 -.
+        &       AlternateNoBoot - ExpandFrig-.
+        &       %FT91 -.
+        &       AlternateCaps - ExpandFrig-.
+        &       %FT92 -.
+        &       AlternateNoCaps - ExpandFrig-.
+        &       %FT92 -.
+        &       ExpandShCaps - ExpandFrig-.
+        &       %FT92 -.
+        &       ExpandDir - ExpandFrig-.
+        &       %FT93 -.
+        &       ExpandNoDir - ExpandFrig-.
+        &       %FT93 -.
+        &       AlternateLoud - ExpandFrig-.
+        &       %FT95 -.
+        &       AlternateQuiet - ExpandFrig-.
+        &       %FT95 -.
+        &       AlternateScroll - ExpandFrig-.
+        &       %FT96 -.
+        &       AlternateNoScroll - ExpandFrig-.
+        &       %FT96 -.
+
+91
+        &       AlternateNoBoot -%BT91
+        &       AlternateBoot   -%BT91
+92
+        &       ShCapsString    -%BT92-1
+        &       ShCapsString    -%BT92-1
+        &       AlternateNoCaps -%BT92
+        &       AlternateNoCaps -%BT92
+        &       AlternateCaps   -%BT92
+        &       AlternateCaps   -%BT92
+        &       AlternateCaps   -%BT92
+        &       AlternateCaps   -%BT92
+93
+        &       DirString   -%BT93-1
+        &       NoDirString -%BT93-1
+95
+        &       AlternateQuiet -%BT95
+        &       AlternateLoud  -%BT95
+96
+        &       AlternateScroll   -%BT96
+        &       AlternateNoScroll -%BT96
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+; read byte from CMOS RAM : info word in R0, byte -> R1
+
+ReadByte ENTRY  "r0, r2"
+        MOV     r1, r0, LSR #16         ; get bytoff
+        AND     r1, r1, #&FF
+        MOV     r0, #ReadCMOS
+        SWI     XOS_Byte
+        MOV     r1, r2
+        EXIT
+
+; take infoword in R0, return value in R0
+
+GetValue ENTRY  "r1"
+        BL      ReadByte                ; now extract the value
+        AND     r14, r0, #&FF           ; get bitoff
+        MOV     r1, r1, LSR r14         ; throw away low bits
+        MOV     r0, r0, LSR #8
+        AND     r0, r0, #&FF            ; get fwidth
+        RSB     r0, r0, #31             ; number of positions to shift up to remove unwanted bits
+        MOV     r1, r1, LSL r0          ; shift up...
+        MOV     r0, r1, LSR r0          ; ...then down again
+        EXITS
+
+PrintR0 ENTRY   "r1, r2"
+        CMP     r0, #-1
+        BNE     %FT10
+        ADRL    r0, AutoString
+        SWI     XOS_Write0
+        EXIT
+10
+        SUB     sp, sp, #32
+        MOV     r1, sp
+        MOV     r2, #32
+        SWI     XOS_ConvertInteger4
+        SWIVC   XOS_Write0
+        ADD     sp, sp, #32
+        EXIT
+
+NoString =      "No ", 0
+        ALIGN
+
+ConfigCheckEOL  ROUT
+        LDRB    r3, [r0], #1
+        CMP     r3, #" "
+        BEQ     ConfigCheckEOL
+        CMP     r3, #13
+        CMPNE   r3, #10
+        CMPNE   r3, #0
+        MOVEQ   pc, lr
+        ADR     R0, Config2manyparms
+      [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      |
+        SETV
+      ]
+        MOV     pc, lr
+
+Config2manyparms
+        &       ErrorNumber_Syntax
+        =      "Config2manyparms:Too many parameters"
+
+;*************************************************************************
+
+IgnoreBitoff *  1
+
+Config_TV_setcode ROUT
+        LDRB    r2, [r0], #1
+        CMP     r2, #" "
+        BEQ     Config_TV_setcode
+        SUB     r1, r0, #1
+        SWI     XOS_ReadUnsigned
+        BVS     %FT01
+        CMP     r2, #3
+        SUBGT   r0, r2, #252
+        CMPGT   r0, #3
+        BHI     BadConOpt
+        CMP     r2, #3
+        ANDGT   r2, r2, #7              ; top bit set in field means 252-255
+        Push    "r2"
+        MOV     r0, #0
+03
+        LDRB    r2, [r1], #1
+        CMP     r2, #" "
+        BEQ     %BT03
+        CMP     r2, #","
+        CMPEQ   r0, #0
+        MOVEQ   r0, #","
+        BEQ     %BT03
+        SUB     r1, r1, #1
+        Push    "r0"
+        MOV     r0, #10
+        SWI     XOS_ReadUnsigned
+        Pull    "r0"
+        BVC     %FT04
+        CMP     r0, #0
+        Pull    "r0", NE
+        BNE     BadConOpt
+04
+        CMP     r2, #1
+        Pull    "r0"
+        BHI     ConParmTooBig
+        ORR     r2, r2, r0, LSL #1
+01
+        MOV     r0, r1
+        LDR     r1, %FT02
+        B       BaudEntry
+02
+        =       4, 3, MODETVCMOS, 0
+
+Config_TV_showcode
+        MOV     r4, r0
+        SWI     XOS_WriteS
+        =       "TV         ", 0
+        ALIGN
+        MOVVC   r0, #ReadCMOS
+        MOVVC   r1, #MODETVCMOS
+        SWIVC   XOS_Byte
+        MOVVC   r2, r2, LSL #24
+        MOVVC   r0, r2, ASR #29         ; get signed TV shift
+        ANDVC   r0, r0, #&FF
+        BLVC    PrintR0
+        SWIVC   XOS_WriteI+","
+        MOVVC   r0, r2, LSR #28
+        ANDVC   r0, r0, #1              ; interlace bit
+        BLVC    PrintR0
+        ADD     r2, r4, #4
+        B       ExitShow
+
+Config_Ignore_setcode ROUT
+        LDRB    r2, [r0], #1
+        CMP     r2, #" "
+        BEQ     Config_Ignore_setcode
+        SUB     r1, r0, #1
+        SWI     XOS_ReadUnsigned
+        MOV     r0, r1
+        Push    "r2"
+        ADR     lr, %FT03
+        Push    lr                      ; return reg for BaudEntry
+        MOVVS   r2, #1
+        MOVVC   r2, #0                  ; if number had clear noignore
+        LDR     r1, %FT01
+        B       BaudEntry               ; pseudo-BL
+03
+        Pull    "r2"                    ; set to 0 if noignore, but we don't care!
+        LDR     r1, %FT02
+        B       IgnoreEntry
+01
+        =       IgnoreBitoff, 0, PSITCMOS, 0
+02
+        =       0, 7, PigCMOS, 0
+
+Config_Ignore_showcode
+        MOV     r4, r0
+        MOV     r0, #ReadCMOS
+        MOV     r1, #PSITCMOS
+        SWI     XOS_Byte
+        TST     r2, # 1 :SHL: IgnoreBitoff
+        ADRNE   r0, NoString
+        SWINE   XOS_Write0
+        BVS     ExitShow
+        SWI     XOS_WriteS
+        =       "Ignore", 0
+        ALIGN
+        BVS     ExitShow
+        ADDNE   r2, r4, #4
+        BNE     ExitShow
+        MOV     r1, #PigCMOS
+        SWI     XOS_Byte
+        SWI     XOS_WriteS
+        =       "     ", 0
+        ALIGN
+        MOV     r1, #PigCMOS
+        SWIVC   XOS_Byte
+        MOVVC   r0, r2
+        BLVC    PrintR0
+        ADD     r2, r4, #4
+        B       ExitShow
+
+Config_Mode_setcode  ROUT
+Config_WimpMode_setcode ROUT
+        ADR     r1, ModeCMOSTable
+ConfigMultiField ROUT
+        BL      ReadNumAuto
+        BVS     BadConParm
+        CMP     r2, #-1
+        LDR     r14, [r1], #4                   ; get auto number
+        MOVEQ   r2, r14                         ; if auto number then replace by auto value
+        LDR     r14, [r1], #4                   ; get maximum value
+        CMPNE   r2, r14                         ; if not auto then check maximum value
+        BHI     ConParmTooBig
+        BL      ConfigCheckEOL
+        BVS     ExitConfig
+        MOV     r0, r1
+        BL      WriteMultiField
+        Pull    "pc"                            ; was already stacked by *Configure
+
+ModeCMOSTable
+        &       256                             ; Auto value
+        &       255                             ; maximum valid number
+;               address, mask from bit 0, shift to 1st bit in value, shift to 1st bit in CMOS
+ [ {TRUE} ; mode = wimpmode
+        =       WimpModeCMOS,   &FF, 0, 0       ; normal bits here
+        =       Mode2CMOS,      &01, 8, 4       ; mode auto bit
+        ASSERT  WimpModeAutoBit = 16
+ |
+        =       MODETVCMOS,     &0F, 0, 0       ; bits 0 to 3 here
+        =       VduCMOS,        &01, 4, 1       ; bit 4 here
+        =       Mode2CMOS,      &0F, 5, 0       ; bits 5 to 7, and auto bit here
+ ]
+        =       0
+        ALIGN
+
+; Write a number of CMOS RAM bit fields from a value
+
+; in:   r0 -> table
+;       r2 -> value to split
+;
+; out:  -
+
+WriteMultiField ENTRY "r0-r5"
+        MOV     r3, r0                  ; pointer to where we're at in table
+        MOV     r4, r2                  ; value
+10
+        LDRB    r1, [r3], #1
+        TEQ     r1, #0
+        EXIT    EQ
+        MOV     r0, #ReadCMOS
+        SWI     XOS_Byte
+        LDRB    r0, [r3], #1            ; r0 = mask
+        LDRB    r5, [r3], #1            ; r5 = shift to 1st bit in value
+        LDRB    r14, [r3], #1           ; r14 = shift to 1st bit in CMOS
+        BIC     r2, r2, r0, LSL r14     ; knock out previous bits
+        AND     r5, r0, r4, LSR r5      ; get new bits, at bottom of byte
+        ORR     r2, r2, r5, LSL r14     ; form new CMOS value
+        MOV     r0, #WriteCMOS
+        SWI     XOS_Byte
+        B       %BT10
+
+; Read a value formed by merging a number of CMOS RAM bit fields
+
+; in:   r0 -> table
+; out:  r0 = value
+
+ReadMultiField ENTRY "r1-r6"
+        LDR     r6, [r0, #4]            ; get maximum value allowed
+        ADD     r3, r0, #2*4            ; pointer to where we're at in table (skip auto, max)
+        MOV     r4, #0                  ; cumulative value
+10
+        LDRB    r1, [r3], #1
+        TEQ     r1, #0
+        BEQ     %FT20
+        MOV     r0, #ReadCMOS
+        SWI     XOS_Byte
+        LDRB    r0, [r3], #1            ; r0 = mask
+        LDRB    r5, [r3], #1            ; r5 = shift to 1st bit in value
+        LDRB    r14, [r3], #1           ; r14 = shift to 1st bit in CMOS
+        AND     r2, r0, r2, LSR r14     ; get relevant bits in bottom of byte
+        ORR     r4, r4, r2, LSL r5      ; merge new bits with value
+        B       %BT10
+20
+        CMP     r4, r6                  ; if within range
+        MOVLS   r0, r4                  ; then return that value
+        MOVHI   r0, #-1                 ; else return -1 indicating Auto
+        EXIT
+
+Config_Mode_showcode ROUT
+        MOV     r4, r0
+        ADR     r0, ModeSpacedString
+ModeWimpModeShowCode
+        SWI     XOS_Write0
+        BVS     %FT10
+        BL      Read_Configd_Mode
+        BL      PrintR0
+10
+        ADD     r2, r4, #4
+        B       ExitShow
+
+ModeSpacedString
+        =       "Mode       ", 0
+WimpModeSpacedString
+        =       "WimpMode   ", 0
+        ALIGN
+
+Config_WimpMode_showcode ROUT
+        MOV     r4, r0
+        ADR     r0, WimpModeSpacedString
+        B       ModeWimpModeShowCode
+
+Read_Configd_Mode ENTRY
+        ADR     r0, ModeCMOSTable
+        BL      ReadMultiField
+        EXIT
+
+Config_Baud_setcode  ROUT
+        BL      ReadNumParm
+        BVS     BadConParm
+        CMP     r2, #8
+        BGT     ConParmTooBig
+        SUBS    r2, r2, #1
+        MOVMI   r2, #6
+        LDR     r1, %FT01               ; set up info word
+        B       BaudEntry
+01
+        =       2, 2, PSITCMOS, 0
+
+Config_Baud_showcode
+        MOV     r4, r0
+        SWI     XOS_WriteS
+        =       "Baud       ", 0
+        ALIGN
+        LDRVC   r0, %BT01               ; get infoword
+        BLVC    GetValue
+        ADDVC   r0, r0, #1
+        BLVC    PrintR0
+        ADD     r2, r4, #4
+        B       ExitShow
+
+Config_MouseStep_setcode ROUT
+        LDRB    r2, [r0], #1
+        CMP     r2, #" "
+        BEQ     Config_MouseStep_setcode
+        CMP     r2, #"-"
+        Push    "r2"
+        SUBNE   r1, r0, #1
+        SWI     XOS_ReadUnsigned
+        Pull    "r0"
+        BVS     BadConParm
+        CMP     r0, #"-"
+        RSBEQ   r2, r2, #0
+        CMP     r2, #0
+        BEQ     BadConOpt
+        CMP     r2, #-128
+        BLT     BadConOpt
+        CMP     r2, #127
+        BGT     ConParmTooBig
+        MOV     r0, r1
+        LDR     r1, %FT02
+        B       BaudEntry
+02
+        =       0, 7, MouseStepCMOS, 0
+
+Config_MouseStep_showcode ROUT
+        MOV     r4, r0
+        SWI     XOS_WriteS
+        =       "MouseStep  ", 0
+        ALIGN
+        MOVVC   r0, #ReadCMOS
+        MOVVC   r1, #MouseStepCMOS
+        SWIVC   XOS_Byte
+        BVS     %FT01
+        MOVS    r2, r2, LSL #24
+        MOVNE   r0, r2, ASR #24  ; get sign extended byte
+        MOVEQ   r0, #1
+        BL      PrintR0
+01      ADD     r2, r4, #4
+        B       ExitShow
+
+Config_MouseType_setcode ROUT
+        LDRB    r2, [r0], #1
+        CMP     r2, #" "
+        BEQ     Config_MouseType_setcode
+        SUB     r1, r0, #1
+        SWI     XOS_ReadUnsigned
+        BVS     BadConParm
+        CMP     r2, #&100
+        BCS     ConParmTooBig
+        MOV     r4, r1
+        MOV     r0, #1
+        MOV     r1, r2
+        SWI     XOS_Pointer
+        MOV     r0, r4
+        LDR     r1, %FT01
+        B       BaudEntry
+01
+        =       0, 7, MouseCMOS, 0
+
+Config_MouseType_showcode
+        MOV     r4, r0
+        SWI     XOS_WriteS
+        =       "MouseType  ", 0
+        ALIGN
+        LDRVC   r0, %BT01
+        BLVC    GetValue
+        BLVC    PrintR0
+        ADD     r2, r4, #4
+        B       ExitShow
+
+Config_MonitorType_setcode
+ [ ModeSelectors
+        ADR     r1, MonitorTypeCMOSTable
+        BL      ReadNumAuto
+        BVS     BadConParm
+        MOV     r4, r2                          ; save value to store in current monitortype
+        CMP     r2, #-1
+        LDR     r14, [r1], #4                   ; get auto number
+        MOVEQ   r2, r14                         ; if auto number then replace by auto value
+        LDR     r14, [r1], #4                   ; get maximum value
+        CMPNE   r2, r14                         ; if not auto then check maximum value
+        BHI     ConParmTooBig
+        BL      ConfigCheckEOL
+        BVS     ExitConfig
+
+        LDR     r0, =VduDriverWorkSpace+CurrentMonitorType
+        STR     r4, [r0]                        ; update current value
+
+        MOV     r0, r1
+        BL      WriteMultiField
+        Pull    "pc"                            ; was already stacked by *Configure
+ |
+        ADR     r1, MonitorTypeCMOSTable
+        B       ConfigMultiField
+ ]
+
+MonitorTypeCMOSTable
+        &       MonitorTypeAuto :SHR: MonitorTypeShift          ; Auto value
+        &       MonitorTypeF :SHR: MonitorTypeShift             ; maximum valid number
+;               address, mask from bit 0, shift to 1st bit in value, shift to 1st bit in CMOS
+        =       VduCMOS,        MonitorTypeBits :SHR: MonitorTypeShift, 0, MonitorTypeShift
+        =       0
+        ALIGN
+
+Config_MonitorType_showcode ROUT
+        MOV     r4, r0
+        SWI     XOS_WriteS
+        =       "MonitorType ", 0
+        ALIGN
+        BVS     %FT10
+        BL      Read_Configd_MonitorType
+        BL      PrintR0
+10
+        ADD     r2, r4, #4
+        B       ExitShow
+
+Read_Configd_MonitorType ENTRY
+        ADR     r0, MonitorTypeCMOSTable
+        BL      ReadMultiField
+        EXIT
+
+Config_Sync_setcode
+        ADR     r1, SyncCMOSTable
+        B       ConfigMultiField
+
+SyncCMOSTable
+        &       3       ; Auto value
+        &       1       ; maximum valid number
+;               address, mask from bit 0, shift to 1st bit in value, shift to 1st bit in CMOS
+        =       VduCMOS, 1, 0, 0
+        =       VduCMOS, 1, 1, 7
+        =       0
+        ALIGN
+
+Config_Sync_showcode ROUT
+        MOV     r4, r0
+        SWI     XOS_WriteS
+        =       "Sync       ", 0
+        ALIGN
+        BVS     %FT10
+        BL      Read_Configd_Sync
+        BL      PrintR0
+10
+        ADD     r2, r4, #4
+        B       ExitShow
+
+Read_Configd_Sync ENTRY
+        ADR     r0, SyncCMOSTable
+        BL      ReadMultiField
+        EXIT
+
+ [ :LNOT: DriversInKernel
+SetUpPrinterBuffer ENTRY "r1-r3"
+        MOV     r0, #PrinterBufferCMOS
+        BL      Read
+        MOV     r2, #0
+        LDR     r2, [r2, #Page_Size]
+        MULS    r3, r2, r0
+        BEQ     %FT10                           ; if zero, then use default area & size
+
+        BL      ClaimSysHeapNode                ; else claim space from system heap
+        BVC     %FT20                           ; if no error then OK, else use default
+10
+        LDR     r2, =PrintBuff                  ; use default buffer
+        MOV     r3, #PrintBuffSize
+20
+        MOV     r0, #0
+        STR     r2, [r0, #PrinterBufferAddr]
+        STR     r3, [r0, #PrinterBufferSize]
+        EXIT
+ ]
+
+        END
diff --git a/s/ArthurSWIs b/s/ArthurSWIs
new file mode 100644
index 0000000000000000000000000000000000000000..9c9a8dee9785e679545e0abc16b80c31808b7e4b
--- /dev/null
+++ b/s/ArthurSWIs
@@ -0,0 +1,1080 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL     => ArthurSWIs - ReadUnsigned, Vectors, Bits
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+; ReadUnsigned.
+; ============
+;
+; Read an unsigned number from a string in decimal (no prefix), hex (&)
+; or given base (nn_). Leading spaces are stripped.
+; 'Bad base for number' is given if a base is not in 02..10_36
+; 'Bad number' is given if
+;      (i) No valid number was
+;  or (ii) a '<base>_' or '&' has no following valid number
+; 'Number too big' is given if the result overflowed a 32-bit word
+
+; In    r1 -> string
+;       r0 =  base to read number in (0 means any based number allowed)
+;                bit 31 set -> check term chars for ok-ness
+;                bit 30 set -> restrict range to 00..FF
+;                bit 29 set -> restrict range to 0..R2 (inclusive)
+;                               (overrides bit 30)
+
+; Out   VC : r1 -> first unused char, r2 = number
+;       VS : r1 unchanged, r2 = 0, current error block set
+
+ReadUnsigned_Routine ENTRY "r0-r1, r3-r4, r9"
+
+        TEQP    PC, #SVC_mode
+
+; first set range limit
+        MOV     r9, r2            ; limit value
+        TST     r0, #3 :SHL: 29
+        MOVEQ   r9, #-1           ; used unsigned; allows anything
+        TSTNE   r0, #1 :SHL: 30
+        MOVNE   r9, #&FF
+
+        MOV     r11, r0         ; Remember the input flags
+        BIC     r12, r0, #(2_111 :SHL: 29) ; r12 := base
+        CMP     r12, #2          ; If base nonsensical, default to 10
+        RSBGES  r14, r12, #36    ; ie. try to match most generally
+        MOVLT   r12, #10
+
+01      LDRB    r0, [r1], #1    ; Skip spaces for Bruce
+        TEQ     r0, #" "
+        BEQ     %BT01
+        SUB     r10, r1, #1      ; Keep ptr to start of string after spaces
+
+        TEQ     r0, #"&"        ; '&' always forces hex read
+        BNE     %FT20
+        MOV     r4, #16
+        BL      ReadNumberInBase
+        BVS     %FT95
+
+10      STR     r1, [sp, #4]       ; Update string^
+        TST     r11, #(1 :SHL: 31) ; Was the termcheck flag set ?
+        BEQ     %FT15
+        LDRB    r0, [r1]           ; What was the term char ?
+        CMP     r0, #" "           ; CtrlChar + space all ok
+        BGT     %FT85              ; For bad term errors
+
+15      CMP     r2, r9
+        BHI     %FT80
+        PullEnv
+        ExitSWIHandler          ; VClear already in lr
+
+
+20      SUB     r1, r1, #1      ; Skip back to first char of string
+        MOV     r4, #10         ; Try reading a decimal number
+        BL      ReadNumberInBase
+        MOVVS   r4, r12          ; If we failed to read a decimal number
+        BVS     %FT30           ; then use the one supplied (r12). r1 ok
+        LDRB    r0, [r1]        ; Is it base_number ?
+        CMP     r0, #"_"        ; If not based, use supplied base
+        MOVNE   r1, r10         ; to read from given start of string (spaces !)
+        MOVNE   r4, r12         ; restore supplied base!
+        ADDEQ   r1, r1, #1      ; Skip the '_'
+        MOVEQ   r4, r2          ; Use this as new base
+
+; Reading number in base r4
+
+30      CMP     r4, #2          ; Is base valid (2..36) ?
+        RSBGES  r0, r4, #36     ; LT -> invalid
+        BLT     %FT90
+        BL      ReadNumberInBase ; Read rest of number
+        BVS     %FT95
+        B       %BT10
+
+
+80      ADR     r2, ErrorBlock_NumbTooBig
+      [ International
+        B       %FT94
+      |
+        B       %FT95
+      ]
+
+85      ADR     r2, ErrorBlock_BadNumb
+      [ International
+        B       %FT94
+      |
+        B       %FT95
+      ]
+
+90      ADR     r2, ErrorBlock_BadBase
+
+      [ International
+94
+        Push    "r0,lr"
+        MOV     r0,r2
+        BL      TranslateError
+        MOV     r2,r0
+        Pull    "r0,lr"
+      ]
+
+95
+        STR     r2, [stack]     ; Go set the current error
+        PullEnv
+        MOV     r2, #0          ; Defined to return 0 on error
+        B       SLVK_SetV
+
+        MakeErrorBlock BadBase
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+; ReadNumberInBase
+; ================
+
+; In    r1 -> string, r4 = base (valid)
+
+; Out   VC : Number read in r2, r1 updated. r3 = number of chars used
+;       VS : r1 preserved, r2 -> error block
+
+ReadNumberInBase ENTRY "r0, r1, r12"
+
+        MOV     r2, #0          ; Result
+        MOV     r3, #0          ; Number of valid digits read
+
+10      BL      GetCharForReadNumber
+        BNE     %FT50           ; Finished ?
+        MOV     r12, r4
+
+        MOV     r14, #0         ; Multiply by repeated addition. Base <> 0 !
+20      ADDS    r14, r14, r2
+        BCS     %FT90           ; Now checks for overflow !
+        SUBS    r12, r12, #1    ; result *:= base
+        BNE     %BT20
+        ADDS    r2, r14, r0     ; result +:= digit
+        BCC     %BT10
+        B       %FT90           ; Now checks for overflow here too!
+
+50      CMP     r3, #0          ; Read any chars at all ? VClear
+        STRNE   r1, [sp, #4]    ; Update string^
+        EXIT    NE              ; Resultis r2
+
+      [ International
+        Push    "r0"
+        ADR     r0, ErrorBlock_BadNumb
+        BL      TranslateError
+        MOV     r2,r0
+        Pull    "r0"
+      |
+        ADR     r2, ErrorBlock_BadNumb
+        SETV
+      ]
+        EXIT
+        MakeErrorBlock BadNumb
+
+90
+      [ International
+        Push    "r0"
+        ADR     r0, ErrorBlock_NumbTooBig
+        BL      TranslateError
+        MOV     r2,r0
+        Pull    "r0"
+      |
+        ADR     r2, ErrorBlock_NumbTooBig
+        SETV
+      ]
+        EXIT
+        MakeErrorBlock NumbTooBig
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+; GetCharForReadNumber
+; ====================
+;
+; Read a digit and validate for reading in current base. Bases 2..36 are valid
+
+; In    r1 -> string, r4 = base for number input
+
+; Out   EQ -> r0 = valid number in [0..base-1], r1++
+;       NE -> r0 invalid, r1 same
+
+GetCharForReadNumber ENTRY
+
+        LDRB    r0, [r1]
+        CMP     r0, #"0"
+        BLO     %FT95
+        CMP     r0, #"9"
+        BLS     %FT50
+        UpperCase r0, r14
+        CMP     r0, #"A"        ; Always hex it, even if reading in decimal
+        RSBGES  r14, r0, #"Z"   ; Inverse compare as nicked from UpperCase
+        BLT     %FT95           ; GE -> in range A..Z
+        SUB     r0, r0, #"A"-("0"+10)
+50      SUB     r0, r0, #"0"
+        CMP     r0, r4          ; digit in [0..base-1] ?
+        BHS     %FT95
+        ADD     r1, r1, #1      ; r1++
+        ADD     r3, r3, #1      ; Valid digit has been read
+        CMP     r0, r0          ; EQ
+        EXIT
+
+95      CMP     r0, #-1         ; NE
+        EXIT
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Initialise_vectors()
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+            ^ 0
+TailPtr     # 4     ; order very carefully chosen!
+VecWSpace   # 4
+Address     # 4
+VecNodeSize # 0
+
+InitVectors
+
+; for vec:=0 to NVECTORS-1 do vectab!(vec*4):= defaultvectab+8*vec
+
+      MOV   R0, #NVECTORS
+      ADR   R1, defaultvectab    ; Point at the default vector table
+      LDR   R2, =VecPtrTab       ; Point at table of head pointers
+
+VecInitLoop
+      STR    R1, [R2], #4
+      ADD    R1, R1, #VecNodeSize ; defaultvectab+vns*vec
+      SUBS   R0, R0, #1             ; Next vec
+      BGT    VecInitLoop
+
+      MOV    PC, link
+      LTORG
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Call_vector (n)
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In:   r10 = vector number
+;       lr contains return address + flags/int state to set up before calling
+
+; Out:  r10, r12, lr corrupted
+
+CallVector ROUT
+
+        CMP     r10, #NVECTORS
+        MOVCSS  pc, lr                  ; return - silly value
+
+        Push    lr                      ; claimed return goes back to caller
+        TEQP    lr, #0                  ; put back caller's flags + int state
+
+        LDR     r14, =VecPtrTab         ; Point at table of head pointers
+        LDR     r10, [r14, r10, LSL #2] ; nextblock:=vecptrtab!(n*4)
+
+CallVecLoop
+        MOV     lr, pc                  ; Set up the return address
+        LDMIA   r10, {r10, r12, pc}     ; CALL the vectored routine, step chain
+
+; NB. It is the responsibility of vector code NOT to corrupt flags that are
+; part of the input conditions if they are going to pass the call on, eg. INSV
+; must not do CMP as C,V are needed by old handler
+
+        TEQ     r10, #0                 ; until nextblock points to zero
+        BNE     CallVecLoop
+
+        Pull    pc,,^                   ; we don't expect to get to here
+                                        ; (should always be claimed),
+                                        ; but return to caller, restoring flags
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;Add_To_vector(n, Addressess)
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Add_ToVector_SWICode   ROUT
+
+      CMP   R0, #NVECTORS
+      BCS   BadClaimNumber
+      Push "R0-R4, link"
+      B     GoForAddToVec
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;Claim_vector(n, Addressess)
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ClaimVector_SWICode   ROUT
+ ; On Entry : R0 = Vector number, R1 = Address, R2 = workspace reqd
+
+      CMP   R0, #NVECTORS
+      BCS   BadClaimNumber
+
+      Push "R0-R4, link"
+
+ [ IrqsInClaimRelease
+        MOV     R4, #I_bit
+        TST     R4, PC                  ; is I_bit set ?
+        TEQEQP  R4, PC                  ; No, then set it, save changed bits
+        MOVNE   R4, #0                  ; Else no changed bits
+
+        MOV     R3, #0                  ; List of de-linked nodes is empty
+        LDR     R11, =VecPtrTab         ; Get ptr to table of head pointers
+        LDR     R10, [R11, R0, LSL #2]! ; R10 "nextblock" := *oldptr, R11= root ptr
+01      BL      FindAndDelinkNode       ; R10,R11->R10,R11,R12
+        STRVC   R3, [R12, #TailPtr]     ; Attach de-linked nodes onto this node
+        MOVVC   R3, R12                 ; New head of de-linked nodes
+        BVC     %BT01                   ; Repeat until all nodes de-linked
+
+        TEQP    R4, PC                  ; Restore IRQ state
+
+; Free the list of de-linked nodes, pointed to by R3, enter with VS
+
+02      LDRVC   R3, [R3, #TailPtr]      ; Update head of de-linked nodes
+        BLVC    FreeNode                ; Free the node pointed to by R12
+        SUBS    R12, R3, #0             ; Any more nodes to free?
+        BNE     %BT02                   ; Yes then jump
+ |
+      TEQP  PC, #SVC_mode+I_bit     ; IRQs off while holding context.
+      LDR   R11, =VecPtrTab         ; Point at table of head pointers
+      LDR   R10, [R11, R0, LSL #2]! ; R10 "nextblock" := !oldptr,
+                                    ; point R11 at the table entry "oldptr"
+01    BL    FreeLink
+      BVC   %BT01                   ; loop with chain pointers still set
+ ]
+GoForAddToVec
+      LDR   R11, =VecPtrTab         ; Point at table of head pointers
+
+      ADD   R11, R11, R0, LSL #2
+      MOV   R10, R1                 ; Address
+      MOV   R4, R2                  ; TailPtr pointer is "nextblock"
+
+      MOV   R3, #VecNodeSize        ; Ask for this number of bytes
+      BL    ClaimSysHeapNode        ; The result is in R2 : R12 corrupted
+      BVS   BadClaimVector          ; Failed : Exit
+
+      TEQP  PC, #SVC_mode+I_bit     ; force noirq
+      LDR   R3, [R11]               ; "nextblock" :=vecptrtab!(n*4)
+      STMIA R2, {R3, R4, R10}       ; Atomic Operation thus links in the new
+                                    ; routine
+      STR   R2, [R11]               ; vectab!(n*4) := "thisblock"
+BadClaimVector
+      STRVS R0, [stack]
+      Pull "R0-R4, link"
+      B    SLVK_TestV
+
+BadClaimNumber
+      ADR    R0, ErrorBlock_BadClaimNum
+    [ International
+      Push   "lr"
+      BL     TranslateError
+      Pull   "lr"
+    ]
+      B      SLVK_SetV
+
+      MakeErrorBlock BadClaimNum
+
+;Release_vector(n, Addressess)
+;+++++++++++++++++++++++++
+
+ReleaseVector_SWICode
+ ; On Entry : R0 = vector number, R1 = Address, R2 = workspace, SVC mode
+
+      CMP   R0, #NVECTORS
+      SETV  CS
+      BVS   BadVectorRelease
+
+ [ IrqsInClaimRelease
+        Push    "R0-R2,R9,link"
+
+        MOV     R9, #I_bit
+        TST     R9, PC                  ; is I_bit set ?
+        TEQEQP  R9, PC                  ; No, then set it, R9 = changed bits
+        MOVNE   R9, #0                  ; Else R9=0 for no change
+
+        LDR     R11, =VecPtrTab         ; Get ptr to table of head pointers
+        LDR     R10, [R11, R0, LSL #2]! ; R10 "nextblock" := *oldptr, R11= root ptr
+        BL      FindAndDelinkNode       ; R10,R11->R10,R11,R12
+        TEQP    R9, PC                  ; Restore IRQ state
+        BLVC    FreeNode                ; If found, free the node in R12
+
+        Pull    "R0-R2,R9,link"
+ |
+      Push "R0-R2, link"
+
+      TEQP  PC, #SVC_mode+I_bit     ; IRQs off while holding context.
+      LDR   R11, =VecPtrTab         ; Point at table of head pointers
+      LDR   R10, [R11, R0, LSL #2]! ; R10 "nextblock" := !oldptr,
+                                    ; point R11 at the table entry "oldptr"
+      BL    FreeLink
+      Pull "R0-R2, link"
+ ]
+
+BadVectorRelease
+      ADRVS R0, ErrorBlock_NaffRelease
+    [ International
+      Push  "lr",VS
+      BLVS  TranslateError
+      Pull  "lr",VS
+    ]
+      B     SLVK_TestV
+
+      MakeErrorBlock NaffRelease
+
+ [ IrqsInClaimRelease
+; Find a node and de-link it from the vector chain
+; In:
+; R1 = code address
+; R2 = workspace address
+; R10 -> Node
+; R11 -> Root ptr
+; Out:
+; VC:
+; R10 -> Node following found
+; R11 -> New root ptr
+; R12 -> Node de-linked
+; VS:
+; R10,11,12 trashed - node not found
+
+10      ADD     R11, R10, #TailPtr      ; oldptr := thisblock+TailPtr
+        LDR     R10, [R11]              ; nextblock:=thisblock!TailPtr
+
+FindAndDelinkNode
+        CMP     R10, #0                 ; End of chain?
+        ORREQS  PC, lr, #V_bit          ; Yes, return error
+
+        LDR     R12, [R10, #VecWSpace]
+        CMP     R12, R2                 ; Workspace matches?
+        LDREQ   R12, [R10, #Address]
+        CMPEQ   R12, R1                 ; And code address matches?
+        BNE     %BT10                   ; No then jump, try next node
+
+; Remove node from vector chain
+
+        MOV     R12, R10                ; R12-> node to de-link
+        LDR     R10, [R12, #TailPtr]    ; Get link to next node
+        STR     R10, [R11]              ; Previous node's link -> next node
+        BICS    PC, lr, #V_bit          ; Return no error
+
+; Return node to heap space
+; In:
+; R12-> node to release
+
+FreeNode
+        Push    "R0-R2, lr"
+        MOV     R2, R12
+        BL      FreeSysHeapNode
+        STRVS   R0, [stack]
+        Pull    "R0-R2, PC"                     ; returns Vset if sysheap poo'd.
+ |
+FreeLink   ; find given vector entry from R10 currptr, R11 prevptr
+      CMP   R10, #0
+      ORREQS PC, lr, #V_bit
+
+ReleaseWLoop
+      LDR   R12, [R10, #VecWSpace]
+      CMP   R12, R2
+      LDREQ R12, [R10, #Address]
+      CMPEQ R12, R1
+      BEQ   FoundRelease         ; IF thisblock!Address=OneWanted THEN do it
+      ADD   R11, R10, #TailPtr   ; oldptr := thisblock+TailPtr
+      LDR   R10, [R11]           ; nextblock:=thisblock!TailPtr
+      CMP   R10, #0              ; IF thisblock!TailPtr = 0 THEN naff
+      BNE   ReleaseWLoop
+      ORRS  PC, lr, #V_bit       ; entry not found
+
+FoundRelease ; else !oldptr   := nextblock!TailPtr : release_block(nextblock)
+
+        LDR     R12, [R10, #TailPtr]
+        STR     R12, [R11]
+
+        Push    "R0-R2, lr"
+        MOV     R2, R10
+        MOV     R10, R12                        ; keep updated thisblk
+        BL      FreeSysHeapNode
+        STRVS   R0, [stack]
+        Pull    "R0-R2, PC"                     ; returns Vset if sysheap poo'd.
+ ]
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+      LTORG
+
+defaultvectab
+   & 0, 0, NaffVector           ; UserV  * &00
+   & 0, 0, ErrHandler           ; ErrorV * &01
+   & 0, 0, NOIRQ                ; IrqV   * &02
+   & 0, OsbyteVars, PMFWrch     ; WrchV  * &03
+
+   & 0, 0, NewRdch              ; RdchV  * &04  - start of VecNo=SWINo section
+   & 0, 0, VecOsCli
+   & 0, OsbyteVars, OsByte
+   & 0, OsbyteVars, OsWord
+   & 0, 0, NaffVector             ; filev
+   & 0, 0, NaffVector             ; argsv
+   & 0, 0, NaffVector             ; bgetv
+   & 0, 0, NaffVector             ; bputv
+   & 0, 0, NaffVector             ; gbpbv
+   & 0, 0, NaffVector             ; findv
+   & 0, OsbyteVars, VecRdLine   ; ReadlineV  * &0E - end of VecNo=SWINo
+
+   & 0, 0, NaffVector           ; fscv
+
+   & 0, EvtHan_ws, DefEvent     ; EventV * &10
+
+   & 0, 0, NaffVector           ; UPTV   * &11
+   & 0, 0, NaffVector           ; NETV   * &12
+
+ [ AssembleKEYV
+   & 0, 0, KeyVector            ; KEYV   * &13
+ |
+   & 0, 0, NaffVector           ; KEYV   * &13
+ ]
+
+   & 0, BuffParms+0, NewInsV    ; INSV   * &14
+   & 0, BuffParms+0, NewRemV    ; REMV   * &15
+   & 0, BuffParms+4, NewCnpV    ; CNPV   * &16     ; Count/Purge Buffer V
+
+   & 0, 0, NaffVector           ; UKVDU23V * &17   ; ---| VDU23 (decimal)
+
+   & 0, HiServ_ws, HighSWI      ; UKSWIV   * &18   ; ---| Unknown SWI numbers
+
+   & 0, 0, NaffVector           ; UKPLOTV  * &19   ; ---| VDU25 (decimal)
+
+   & 0, 0, ReadMouse            ; MouseV * &1A
+
+   & 0, 0, NaffVector           ; VDUXV   * &1B
+   & 0, 0, Def_100HZ            ; TickerV * &1C
+
+   & 0, UpCallHan_ws, CallUpcallHandler
+                                ; UpCallV * &1D
+   & 0, 0, AdjustOurSet         ; ChangeEnvironment * &1E
+
+   & 0, VduDriverWorkSpace, SpriteVecHandler ; SpriteV * &1F
+   & 0, 0, NaffVector           ; DrawV * &20
+   & 0, 0, NaffVector           ; EconetV * &21
+   & 0, 0, NaffVector           ; ColourV * &22
+   & 0, VduDriverWorkSpace, MOSPaletteV ; PaletteV * &23
+   & 0, 0, NaffVector           ; SerialV * &24
+
+   & 0, 0, NaffVector           ; FontV * &25
+
+   & 0, 0, NaffVector           ; PointerV * &26
+
+ ; the spares
+   & 0, 0, NaffVector           ; &27
+   & 0, 0, NaffVector           ; &28
+   & 0, 0, NaffVector           ; &29
+   & 0, 0, NaffVector           ; &2a
+   & 0, 0, NaffVector           ; &2b
+   & 0, 0, NaffVector           ; &2c
+   & 0, 0, NaffVector           ; &2d
+   & 0, 0, NaffVector           ; &2e
+   & 0, 0, NaffVector           ; &2f
+
+ assert (.-defaultvectab) = NVECTORS*VecNodeSize
+
+NaffVector ROUT
+Def_100HZ
+        Pull    lr                      ; Claim vector, do nowt
+        BICS    pc, lr, #V_bit
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWIs to save any vector entries pointing into application workspace
+;
+; Delink SWI:
+;   R0 pointer to buffer
+;   R1 buffer size
+; Returns R1 bytes left in buffer
+;   V set if buffer not large enough
+
+Application_Delink ROUT
+      Push "R0, R2-R4, lr"
+
+      CMP   R1, #4
+      BLT   %FT99                   ; invalid buffer size
+
+      MOV   R3, #NVECTORS-1
+      LDR   R4, [R3, #AplWorkSize-(NVECTORS-1)]
+      TEQP  PC, #SVC_mode+I_bit     ; IRQs off while holding context.
+
+03    LDR   R11, =VecPtrTab         ; Point at table of head pointers
+      ADD   R10, R11, R3, LSL #2
+04    MOV   R11, R10             ; step chain
+      LDR   R10, [R11]
+05    CMP   R10, #0
+      BNE   %FT02
+      SUBS  R3, R3, #1
+      BPL   %BT03                ; next vector
+      MOV   R3, #-1
+      STR   R3, [R0]
+      SUB   R1, R1, #4
+      Pull "R0, R2-R4, lr"
+      ExitSWIHandler
+
+02    LDR   R12, [R10, #Address]
+      CMP   R12, R4
+      BGT   %BT04
+      CMP   R12, #UserMemStart
+      BLT   %BT04
+
+; appl entry found: put in buffer, free it
+      CMP   R1, #12+4
+      BLT   %FT99                ; no rheum
+      LDR   R14, [R10, #VecWSpace]
+      STMIA R0!, {R3, R12, R14}
+      SUB   R1, R1, #12          ; buffer entry added
+
+      LDR   R12, [R10, #TailPtr]
+      STR   R12, [R11]           ; vector delinked
+
+        Push    "R0-R2"
+        MOV     R2, R10
+        MOV     R10, R12                        ; keep updated thisblk
+        BL      FreeSysHeapNode
+        MOVVS   lr, R0
+        Pull    "R0-R2"
+        BVC     %BT05
+
+98    STR   lr, [stack]
+      MOV   R3, #-1               ; terminate buffer even if error
+      CMP   r1, #4
+      STRGE R3, [R0]
+      SUB   R1, R1, #4
+      Pull "R0, R2-R4, lr"
+      B    SLVK_SetV
+
+99
+    [ International
+      Push  "r0"
+      ADRL  r0, ErrorBlock_BuffOverflow
+      BL    TranslateError
+      MOV   lr,r0
+      Pull  "r0"
+    |
+      ADRL  lr, ErrorBlock_BuffOverflow
+    ]
+      B     %BT98
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Relink SWI:
+;   R0 pointer to buffer as set by Delink
+; Returns V set if can't relink all
+
+Application_Relink ROUT
+      Push   "R0-R2, lr"
+      MOV     R10, R0
+02    LDR     R0, [R10], #4
+      CMP     R0, #-1
+      Pull   "R0-R2, lr", EQ
+      ExitSWIHandler EQ
+
+      LDMIA   R10!, {R1, R2}
+      SWI     XOS_AddToVector
+      BVC     %BT02
+      STR     R0, [stack]
+      Pull   "R0-R2, lr"
+      B      SLVK_SetV
+
+;********************************************************************
+; Now the stuff that issues service calls; also deals with the MOS
+;  being default default FIQ owner, and wanting to see application
+;  startup.
+;********************************************************************
+
+         GBLL FIQDebug
+FIQDebug SETL {FALSE}
+
+        GBLL  DebugNeil
+DebugNeil SETL {FALSE}          ; if TRUE, check R7-R11 preserved over services
+
+Issue_Service ROUT             ; R1 is service number, R2 may be a parameter
+                               ; registers preserved.
+       Push    "R9-R12, lr"
+
+       CMP      R1, #Service_ClaimFIQ
+       CMPNE    R1, #Service_ClaimFIQinBackground
+       BEQ      FIQclaim
+       CMP      R1, #Service_ReleaseFIQ
+       BEQ      test_FIQclaim_in_progress
+
+       CMP      r1, #Service_NewApplication
+       BEQ      checkmoshandlers
+
+05     MOV      R10, #Module_List
+03     LDR      R10, [R10, #Module_chain_Link]
+       CMP      R10, #0
+       BEQ      %FT01
+       LDR      R9, [R10, #Module_code_pointer]
+       LDR      R11, [R9, #Module_Service]
+       CMP      R11, #0
+       BEQ      %BT03
+       ADD      R9, R9, R11
+       ADD      R11, R10, #Module_incarnation_list - Incarnation_Link
+04     LDR      R11, [R11, #Incarnation_Link]
+       CMP      R11, #0
+       BEQ      %BT03
+
+       [ DebugNeil
+       Push     "R7-R11"
+       ]
+
+       ADD      R12, R11, #Incarnation_Workspace
+       MOV      lr, pc               ; link inc. PSR, mode
+       MOV      pc, R9
+
+       [ DebugNeil
+       ! 0, "Debug code included to check R7-R11 are preserved over services"
+       MOV      lr, sp
+       Push     "R1-R5"
+       LDMIA    lr, {R1-R5}
+       TEQ      R1, R7
+       TEQEQ    R2, R8
+       TEQEQ    R3, R9
+       TEQEQ    R4, R10
+       TEQEQ    R5, R11
+       MOVNE    PC, #0
+       Pull     "R1-R5"
+       ADD      sp, sp, #5*4
+       ]
+
+       CMP      R1, #Service_Serviced
+       BNE      %BT04
+       Pull    "R9-R12, PC"
+
+01     CMP      R1, #Service_ReleaseFIQ
+       Pull    "R9-R12, PC",NE
+       STRB     R1, [R1, #MOShasFIQ-Service_ReleaseFIQ]
+
+    [ FIQDebug
+     TubeChar r0, r1, "MOV r1, #""D"""
+    ]
+
+     assert (Service_ReleaseFIQ :AND: &FF) <> 0
+                                        ; MOS is default owner if nobody
+06     MOV      R1, #Service_Serviced   ; else wants it.
+       Pull    "R9-R12, PC"
+
+FIQclaim
+       MOV      R10, #0
+
+   [ FIQDebug
+   TubeChar r0, r1, "MOV r1, #""C"""
+   ]
+
+  ; first refuse request if a claim is currently in action
+
+       LDRB     R9, [R10, #FIQclaim_interlock]
+       CMP      R9, #0
+       Pull    "R9-R12, PC",NE                 ; no can do
+
+; have to issue a genuine FIQ claim call: set interlock to prevent another
+; one passing round at an awkward moment.
+
+       MOV      r9, #1
+       STRB     r9, [r10, #FIQclaim_interlock]
+
+   [ FIQDebug
+   TubeChar r0, r1, "MOV r1, #""I"""
+   ]
+
+; now safe to inspect our FIQ state
+
+       LDRB     R9, [R10, #MOShasFIQ]
+       CMP      R9, #0
+
+  [ FIQDebug
+  BEQ  sam001
+  TubeChar r0, r1, "MOV r1, #""M"""
+  CMP  r9, #0
+sam001
+  ]
+
+       STRNEB   R10, [R10, #MOShasFIQ]
+       MOVNE    r1, #Service_Serviced
+fakeservicecall
+       Push     PC, EQ                         ; return address
+       SUBEQ    stack, stack, #4*4             ; pseudo- r9-r12
+       BEQ      %BT05                          ; wacky pseudo-BL!
+       MOV      r10, #0
+       LDRB     r9, [r10, #FIQclaim_interlock]
+       STRB     r10, [r10, #FIQclaim_interlock]
+
+   [ FIQDebug
+   TubeChar r0, r1, "MOV r1, #""i"""
+   ]
+
+       CMP      r9, #1                         ; test for background release
+
+   [ FIQDebug
+   BEQ sam002
+   TubeChar r0, r1, "MOV r1, #""B"""
+   CMP r9, #1
+sam002
+   ]
+
+; if background release happened, there are 3 possibilities:
+;   foreground claim; this is defined to have succeeded. Discard release
+;   background claim, that succeeded: releaser gave it away anyway. Discard
+;       "        "     "   failed; we are holding a giveaway of FIQ, therefore
+;                                  claim service call!
+; therefore, if background release happened, always claim the service.
+
+       MOVNE    r1, #Service_Serviced
+       Pull    "r9-r12, PC"                    ; all done
+
+test_FIQclaim_in_progress
+
+   [ FIQDebug
+   TubeChar r0, r1, "MOV r1, #""R"""
+   ]
+
+       MOV      r10, #0
+       LDRB     r9, [r10, #FIQclaim_interlock]
+       CMP      r9, #0
+
+   [ {TRUE}
+       MOVEQ    r9, #1
+       STREQB   r9, [r10, #FIQclaim_interlock] ; lock out background calls
+       BEQ      fakeservicecall                ; issue call, clear flag
+   |
+       BEQ      %BT05                          ; issue call
+   ]
+
+       MOV      r9, #2                         ; mark release as occurring
+
+   [ FIQDebug
+   TubeChar r0, r1, "MOV r1, #""b"""
+   ]
+
+       STRB     r9, [r10, #FIQclaim_interlock]
+       Pull    "r9-r12, PC"
+
+; r9-r12, lr corruptible
+checkmoshandlers
+       LDR      r9, [r1, #SExitA-Service_NewApplication]
+       ADRL     r10, CLIEXIT
+       CMP      r9, r10
+       BNE      %BT05
+       Push    "r0-r7"
+       BL       DEFHAN
+       BL       DEFHN2
+       Pull    "r0-r7"
+       B        %BT05
+
+;************************************************
+; SWI to call a vector
+;************************************************
+CallAVector_SWI  ; R9 is the vector number (!!)
+       Push     "lr"
+       MOV       R10, R9
+       ORR       R14, R14, #SVC_mode
+       TEQP      PC, R14             ; restore caller CCs
+       BL        CallVector
+       MOV       R10, PC, LSR #28    ; restore CCs
+       Pull     "lr"
+       BIC       lr, lr, #&F0000000
+       ORR       lr, lr, R10, LSL #28
+       ExitSWIHandler
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+; Now some bits for the dynamic areas
+
+DoSysHeapOpWithExtension
+       Push   "R0, lr"
+       B       IntoSysHeapOp
+
+ClaimSysHeapNode ROUT ; size in R3
+       MOV     R0, #HeapReason_Get
+       Push   "R0, lr"
+IntoSysHeapOp
+       LDR     R1, =SysHeapStart
+       SWI     XOS_Heap
+       Pull   "R0, PC", VC
+
+       LDR     r14, [r0]                   ; look at error number
+       TEQ     r14, #ErrorNumber_HeapFail_Alloc
+       STRNE   r0, [stack]
+       Pull   "r0, r3, PC", NE            ; can only retry if ran out of room
+
+       Push    r3                          ; in case extension
+       LDR     r1, [stack, #4]
+       CMP     r1, #HeapReason_ExtendBlock
+       BNE     notsysheapextendblock
+
+       Push   "r5, r6"
+       LDR     r5, =SysHeapStart
+       LDR     r6, [r5, #:INDEX:hpdbase]
+       ADD     r6, r6, r5                  ; free space
+       LDR     r1, [r2, #-4]               ; pick up block size
+       ADD     r5, r1, r2                  ; block end +4
+       SUB     r5, r5, #4                  ; TMD 02-Aug-93: block size includes size field (optimisation was never taken)
+       CMP     r5, r6                      ; does block butt against end?
+       ADDNE   r3, r3, r1                  ; max poss size needed
+       Pull   "r5, r6"
+
+  ; note that this doesn't cope well with a block at the end preceded by a
+  ; free block, but tough.
+
+notsysheapextendblock
+       LDR     r1, =SysHeapStart
+       LDR     R0, hpdbase
+       LDR     R1, hpdend
+       SUB     R1, R1, R0          ; size left in heap
+       SUB     R1, R3, R1          ; size needed
+       Pull    r3
+       ADD     R1, R1, #8          ; plus safety space.
+       MOV     R0, #0
+       SWI     XOS_ChangeDynamicArea
+       LDRVC   R0, [stack]  ; and retry.
+       LDRVC   R1, =SysHeapStart
+       SWIVC   XOS_Heap
+       Pull   "R0, PC", VC
+SysClaimFail
+       ADD     stack, stack, #4
+       ADR     R0, ErrorBlock_SysHeapFull
+     [ International
+       BL      TranslateError
+     ]
+       Pull   "PC"
+       MakeErrorBlock  SysHeapFull
+
+;**************************************************************************
+;
+;       FreeSysHeapNode - Free a node in system heap
+;
+; in:   R2 -> node to free
+;
+; out:  R0 = HeapReason_Free or pointer to error if V=1
+;       R1 = SysHeapStart
+;
+
+FreeSysHeapNode ENTRY
+        MOV     R0, #HeapReason_Free
+        LDR     R1, =SysHeapStart
+        SWI     XOS_Heap
+        EXIT
+
+;**************************************************************************
+
+; ValidateAddress_Code
+; R0, R1 are limits of address range to check
+; return CC for OK, CS for naff
+
+ValidateAddress_Code ROUT
+        Push    "R1, lr"
+        CMP     R0, R1
+        SUBNE   R1, R1, #1       ; cope with zero length range sensibly
+        MOV     R10, #0
+
+        MOV     R11, #0
+        LDR     R12, [R10, #AplWorkSize]
+        BL      RangeCheck
+
+ [ :LNOT: NewStyle_Screen
+        VDWS    R11
+        MOV     R12, #ScreenEndAdr
+        LDR     R11, [R11, #TotalScreenSize]
+        ADD     R12, R12, R11
+        SUB     R11, R12, R11, LSL #1
+        BL      RangeCheck
+ ]
+
+ [ NewStyle_SysHeap
+        MOV     r11, #SysHeapChunkAddress       ; need to still check 1st 8K
+        ADD     r12, r11, #SysHeapStart-SysHeapChunkAddress
+        BL      RangeCheck
+ |
+        LDR     R11, =SysHeapStart
+        LDR     R12, [R11, #:INDEX: hpdend]
+        ADD     R12, R11, R12
+        MOV     R11, #SysHeapChunkAddress
+        BL      RangeCheck
+ ]
+
+ [ :LNOT: NewStyle_RMA
+        MOV     R11, #RMAAddress
+        LDR     R12, [R11, #:INDEX: hpdend]
+        ADD     R12, R11, R12
+        BL      RangeCheck
+ ]
+
+ [ :LNOT: NewStyle_SpriteArea
+        LDR     R12, [R10, #SpriteSize]
+        ADD     R12, R12, #SpriteSpaceAddress
+        MOV     R11, #SpriteSpaceAddress
+        BL      RangeCheck
+ ]
+
+ [ :LNOT: NewStyle_RAMDisc
+        LDR     R12, [R10, #RAMDiscSize]
+        ADD     R12, R12, #RAMDiscAddress
+        MOV     R11, #RAMDiscAddress
+        BL      RangeCheck
+ ]
+
+ [ :LNOT: NewStyle_FontArea
+        LDR     R12, [R10, #FontCacheSize]
+        ADD     R12, R12, #FontCacheAddress
+        MOV     R11, #FontCacheAddress
+        BL      RangeCheck
+ ]
+
+        MOV     R11, #CursorChunkAddress
+        ADD     R12, R11, #32*1024
+        BL      RangeCheck
+
+ [ NewCDA
+; not in one of those ranges, so check against dynamic area list
+        MOV     r10, #DAList
+10
+        LDR     r10, [r10, #DANode_Link]
+        TEQ     r10, #0                 ; end of list
+        BEQ     %FT20
+        LDR     r11, [r10, #DANode_Base]
+        LDR     r12, [r10, #DANode_Flags]
+        TST     r12, #DynAreaFlags_DoublyMapped
+        LDR     r12, [r10, #DANode_Size]
+        SUBNE   r11, r11, r12           ; if doubly mapped, move base back by size
+        MOVNE   r12, r12, LSL #1        ; and double size
+        ADD     r12, r12, r11           ; make r12 point at end (exclusive)
+        CMP     r0, r12                 ; if start >= end (excl)
+        BCS     %BT10                   ; then go onto next node
+
+        CMP     r0, r11                 ; if range starts below this area
+        BCC     %FT20                   ; then not totally within this area
+        CMP     r1, r12                 ; else if range ends before end+1 of this area
+        BCC     AddressIsValid          ; then it's valid
+20
+ ]
+
+; not in one of those ranges, so issue service so modules can add other valid areas
+
+        Push    "R2, R3"
+        MOV     R2, R0                  ; pass parameters to service in R2 and R3
+        LDR     R3, [stack, #2*4]       ; reload stacked R1 into R3
+        MOV     R1, #Service_ValidateAddress
+        BL      Issue_Service
+        TEQ     R1, #0                  ; EQ => service claimed, so OK
+        Pull    "R2, R3"
+        Pull    "R1, lr"
+        ORRNE   lr, lr, #C_bit          ; return CS if invalid
+        BICEQ   lr, lr, #C_bit          ; return CC if valid
+        ExitSWIHandler
+
+RangeCheck ; check R0 - R1 lies totally within R11 - (r12-1)
+
+        SUB     R12, R12, #1
+        CMP     R0, R11
+        CMPCS   R12, R0
+        CMPCS   R1, R11
+        CMPCS   R12, R1
+        MOVCC   PC, lr                  ; failed
+AddressIsValid
+        Pull    "R1, lr"
+        BIC     lr, lr, #C_bit
+        ExitSWIHandler
+
+        LTORG
+
+        END
diff --git a/s/ChangeDyn b/s/ChangeDyn
new file mode 100644
index 0000000000000000000000000000000000000000..9dc4632f7d7556470586fec73781dbed63eeabc1
--- /dev/null
+++ b/s/ChangeDyn
@@ -0,0 +1,4260 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL   => ChangeDyn
+
+;******************************************************************************
+; ChangeDynamic SWI
+; In  : R0 =  0 => System Heap,
+;             1 => RMA
+;             2 => Screen
+;             3 => Sprite area
+;             4 => Font cache
+;             5 => RAM disc
+;             6 => Free pool
+;       R1 = no of bytes to change by
+;
+; Out : V set if CAO in AplWork or couldn't move all the bytes requested.
+;       R1 set to bytes moved.
+;******************************************************************************
+
+; The following flag controls the operation of ReadCMOSAndConfigure and FudgeConfigureRMA
+;
+; If false, then no memory is in the free pool to start off, and these routines just allocate pages
+; starting at page R2, and update this on exit.
+;
+; If true, then routine InitDynamicAreas initially moves all non-static free memory into the free pool,
+; and then the above routines just take pages off the end of that.
+
+        GBLL    GetPagesFromFreePool    ; whether ReadCMOSAndConfigure extract pages from the free pool
+GetPagesFromFreePool SETL NewCDA :LAND: {TRUE}
+
+ [ ExpandedCamMap
+AP_AppSpace     *       0                                       ; user r/w, CB
+AP_SysHeap      *       0                                       ; user r/w, CB
+AP_RMA          *       0                                       ; user r/w, CB
+  [ NewStyle_Screen
+   [ DAF_SpecifyBit
+AP_Screen       *       0 :OR: DynAreaFlags_NotCacheable :OR: DynAreaFlags_DoublyMapped :OR: DynAreaFlags_NeedsSpecificPages
+   |
+AP_Screen       *       0 :OR: DynAreaFlags_NotCacheable :OR: DynAreaFlags_DoublyMapped
+   ]
+  |
+AP_Screen       *       0 :OR: DynAreaFlags_NotCacheable        ; user r/w, ~CB
+  ]
+AP_Sprites      *       0                                       ; user r/w, CB
+AP_FontArea     *       2                                       ; user none, CB
+AP_RAMDisc      *       2 :OR: DynAreaFlags_NotCacheable        ; user none, ~CB
+AP_Duff         *       2 :OR: DynAreaFlags_NotCacheable :OR: DynAreaFlags_NotBufferable ; user none, ~C~B
+  [ UseFreePool
+AP_FreePool     *       2 :OR: DynAreaFlags_NotCacheable        ; user none, ~CB
+  ]
+AP_CursorChunk  *       1 :OR: DynAreaFlags_NotCacheable :OR: DynAreaFlags_NotBufferable :OR: PageFlags_Unavailable
+AP_PageZero     *       0
+  [ MEMM_Type = "ARM600"
+AP_L2PT         *       2 :OR: DynAreaFlags_NotCacheable :OR: DynAreaFlags_NotBufferable ; user none, ~C~B
+AP_L1PT         *       AP_L2PT :OR: PageFlags_Unavailable
+AP_UndStackSoftCam *    PageFlags_Unavailable
+  ]
+ |
+AP_AppSpace     *       0                                       ; user r/w
+AP_SysHeap      *       0                                       ; user r/w
+AP_RMA          *       0                                       ; user r/w
+AP_Screen       *       0                                       ; user r/w
+AP_Sprites      *       0                                       ; user r/w
+AP_FontArea     *       2                                       ; user none
+AP_RAMDisc      *       2                                       ; user none
+AP_Duff         *       2
+  [ UseFreePool
+AP_FreePool     *       2                                       ; user none
+  ]
+AP_CursorChunk  *       1                                       ; user r-o
+AP_PageZero     *       0                                       ; user r/w
+ ]
+
+ChangeDyn_FreeAndApl * -2               ; special reason code for when we're sucking out of free pool and apl space
+ChangeDyn_AplSpace   * -1
+ChangeDyn_SysHeap    * 0
+ChangeDyn_RMA        * 1
+ChangeDyn_Screen     * 2
+ChangeDyn_SpriteArea * 3
+ChangeDyn_FontArea   * 4
+ChangeDyn_RamFS      * 5
+ChangeDyn_FreePool   * 6
+ [ UseFreePool
+ChangeDyn_MaxArea    * 6
+ |
+ChangeDyn_MaxArea    * 5
+ ]
+
+; Area handler reason codes
+
+DAHandler_PreGrow       *       0
+DAHandler_PostGrow      *       1
+DAHandler_PreShrink     *       2
+DAHandler_PostShrink    *       3
+
+; Number of entries in page block on stack
+
+NumPageBlockEntries *   32
+PageBlockSize   *       NumPageBlockEntries * 12
+PageBlockChunk  *       NumPageBlockEntries * 4096
+
+        InsertDebugRoutines
+
+; *** Start of old style code ***
+
+ [ :LNOT: NewStyle_All
+
+ChangeDynamicSWI ROUT
+        Push    "r0, r2-r9, r10, lr"        ; r10 is workspace
+
+  [ NewCDA2
+        B       CheckForNewArea
+IsOldArea
+  ]
+
+        CMP     r0, #ChangeDyn_MaxArea
+        MOVLS   r10, #0
+        LDRLS   r10, [r10, #IRQsema]
+        CMPLS   r10, #0
+        BHI     failure_IRQgoing
+
+ [ DebugCDA
+        DLINE   "Entering OS_ChangeDynamicArea"
+        DREG    r0, "r0 = "
+        DREG    r1, "r1 = "
+ ]
+
+ [ :LNOT: NewStyle_RAMDisc
+        CMP     r0, #ChangeDyn_RamFS
+        BEQ     CheckRAMFSChangeOK
+AllowRAMFSChange
+ ]
+
+        MOV     r6, #0
+        LDR     r6, [r6, #Page_Size]
+        SUB     r12, r6, #1
+        ADD     r1, r1, r12
+        BICS    r1, r1, r12                     ; round up to nearest page.
+        MOVEQ   r11, r0                         ; area
+        MOVEQ   r10, #0                         ; amount moved
+        BEQ     CDS_PostService                 ; zero pages!
+
+; Now form source (r11) and destination (r12) registers
+
+ [ UseFreePool
+
+        CMP     r0, #ChangeDyn_FreePool         ; if specified area is free pool
+        MOVEQ   r11, #ChangeDyn_AplSpace        ; then "other" area is aplspace
+        MOVNE   r11, #ChangeDyn_FreePool        ; else other area is free pool
+
+        CMP     r1, #0                          ; if growing area
+        MOVPL   r12, r0                         ; then dest is specified area, and src is other
+        MOVMI   r12, r11                        ; else dest is other
+        MOVMI   r11, r0                         ; and src is specified
+        RSBMI   r1, r1, #0                      ; and make change positive
+ |
+        MOVMI   r12, #ChangeDyn_AplSpace        ; dest := ApplWork
+        MOVMI   r11, r0                         ; source := R0
+        MOVPL   r12, r0                         ; dest := R0
+        MOVPL   r11, #ChangeDyn_AplSpace        ; source := ApplWork
+        RSBMI   r1, r1, #0
+ ]
+
+ [ DebugCDA
+        DREG    r11, "Initially, src = "
+        DREG    r12, "and dest = "
+ ]
+
+; amount movable = current size(source) - shrink limit(source)
+
+ [ :LNOT: NewStyle_SpriteArea
+        CMP     r11, #ChangeDyn_SpriteArea
+        LDREQ   r10, [r11, #SpriteSize-ChangeDyn_SpriteArea]
+        TEQEQ   r10, #0                 ; May have no sprite area ...
+        MOVEQ   r5, #0                  ; Shrink limit := 0
+        BEQ     gotsizeandshrink
+ ]
+
+; need to lock the heap if we are shrinking the SysHeap or the RMA
+; lock by setting the heap end to be the same as the current base;
+; this ensures that no claims will use memory about to disappear
+
+        ADRL    r10, Current_Size+4
+        LDR     r10, [r10, r11, LSL #2]                 ; get pointer to current size of area
+
+ [ NewStyle_RMA :LAND: NewStyle_SysHeap                 ; if both new
+        LDR     r10, [r10]                              ; then just get current size
+ |
+  [ NewStyle_SysHeap                                    ; if sys heap new
+        CMP     r11, #ChangeDyn_RMA                     ; then RMA must be old
+  |
+        CMP     r11, #ChangeDyn_SysHeap                 ; else sys heap is old
+   [ :LNOT: NewStyle_RMA
+        CMPNE   r11, #ChangeDyn_RMA                     ; and if RMA old as well, do 2 CMPs
+   ]
+  ]
+        LDRNE   r10, [r10]
+        BNE     CDA_GotSize
+
+; its a heap, so munge it to prevent anyone stealing memory while we are
+
+        MOV     lr, pc                                  ; save I_bit
+        TEQP    pc, #SVC_mode + I_bit                   ; disable IRQs round this bit
+        LDR     r5, [r10]                               ; current size
+        LDR     r2, [r10, #hpdbase-hpdend]              ; remove all of the contiguous free chunk from the heap
+        STR     r2, [r10]                               ; so that noone can steal any of it under IRQs
+        MOV     r10, r5
+        STR     r10, [stack, #4*9]                      ; but save the old size on stack to restore later
+        TEQP    lr, #0                                  ; restore state
+
+CDA_GotSize
+ ]
+ [ :LNOT: NewStyle_FontArea
+        CMP     r11, #ChangeDyn_FontArea
+        BNE     %FT04                                   ; source not font area
+
+        MOV     r5, r1
+        MOV     r1, #-1
+        MOV     r2, #0                                  ; in case font manager dead
+        SWI     XFont_ChangeArea
+        MOV     r1, r5                                  ; preserve r1
+        MOV     r5, r2
+        B       %FT05
+04
+ ]
+        ADR     r5, Shrink_Limits+4                     ; now see how much we can remove from src
+        LDR     r5, [r5, r11, LSL #2]                   ; load shrink limit address
+        LDR     r5, [r5]                                ; and contents
+05
+ [ :LNOT: NewStyle_SpriteArea
+        CMP     r11, #ChangeDyn_SpriteArea
+        CMPEQ   r5, #saExten                            ; if no sprites defined, can delete hdr
+        MOVEQ   r5, #0
+ ]
+
+gotsizeandshrink
+        SUB     r10, r10, r5                            ; amount removable from source
+06
+        ADR     r5, Grow_LimitsPtrs+4
+        LDR     r5, [r5, r12, LSL #2]                   ; get ptr to maximum size of destination area
+        LDR     r5, [r5]                                ; maximum size of dest area
+
+ [ MEMC_Type = "IOMD"
+        CMP     r12, #ChangeDyn_Screen                  ; if screen
+        MOVEQ   r5, #0
+        LDREQ   r5, [r5, #VideoSize]                    ; then maximum size depends on how much video RAM there is
+ ]
+
+        ADRL    r4, Current_Size+4
+        LDR     r4, [r4, r12, LSL #2]                   ; pointer to destination current size
+        LDR     r4, [r4]                                ; current size of dest
+        SUB     r5, r5, r4                              ; maximum amount we can add to destination
+
+; r10 = amount removable from src, r5 = amount addable to dest
+
+        MOV     r2, r10                                 ; save amount removable from src, in case we need it again
+        CMP     r10, r5
+        MOVHI   r10, r5                                 ; min(max removable from src, max addable to dest)
+
+; r10 is now the amount we can move
+;  IF removing from ApplWork AND amount moveable < size requested
+;    Then Error NotAllMoved
+
+        CMP     r10, r1
+        BCS     %FT10                                   ; can move all reqd (TMD 15-Oct-91; was GE)
+
+; we can't move all that is required - there are two cases to be considered
+;  a) if (src = AplSpace) and (dest <> FreePool) then give error (this can only happen in old world)
+;  b) if (src = FreePool) AND (dest <> AplSpace) then check if adding aplspace would allow us to succeed -
+;        if it does then adjust registers, else give error
+
+        CMP     r11, #ChangeDyn_AplSpace                ; if src = aplspace
+        BNE     %FT08                                   ; [not, so skip]
+        CMP     r12, #ChangeDyn_FreePool                ; and dest <> freepool
+        BNE     failure_IRQgoing                         ; then fail (case (a))
+08
+ [ UseFreePool
+        CMP     r11, #ChangeDyn_FreePool                ; if src = FreePool
+        BNE     %FT10                                   ; [skip if not]
+        CMP     r12, #ChangeDyn_AplSpace                ; and dest <> AplSpace
+        BEQ     %FT10                                   ; [skip if not]
+
+; now see if we would have enough if we had aplspace as well (r2 = amount we could remove from free pool)
+
+  [ DebugCDA
+        DLINE   "Not enough in just free pool"
+  ]
+
+        LDR     r4, Current_Size+4+(ChangeDyn_AplSpace :SHL: 2)
+        LDR     r4, [r4]                                ; r4 = current apl size
+        LDR     lr, Shrink_Limits+4+(ChangeDyn_AplSpace :SHL: 2)
+        LDR     lr, [lr]                                ; lr = shrink limit for aplspace
+        SUB     r4, r4, lr                              ; r4 = amount we could remove from aplspace
+
+        ADD     r10, r2, r4                             ; add on to amount we could remove from free pool
+        CMP     r10, r5                                 ; if more than amount area can grow
+        MOVHI   r10, r5                                 ; then limit to that
+
+        CMP     r10, r1                                 ; if still can't do it now
+        BCC     failure_IRQgoing                         ; then give error
+
+        TEQ     r2, #0                                  ; else check to see if there was any at all in free pool
+        MOVEQ   r11, #ChangeDyn_AplSpace                ; if not then just take from aplspace
+        MOVEQ   r5, r10                                 ; and do all
+
+        MOVNE   r11, #ChangeDyn_FreeAndApl              ; else make src indicator reflect that we need both
+        MOVNE   r5, r2                                  ; but save amount we are taking from freepool
+        B       %FT10
+ ]
+
+ [ (:LNOT: NewStyle_SysHeap) :LOR: (:LNOT: NewStyle_RMA)
+testrestoreheapend
+  [ NewStyle_SysHeap                                    ; if sys heap new
+        CMP     r11, #ChangeDyn_RMA                     ; then RMA must be old
+  |
+        CMP     r11, #ChangeDyn_SysHeap                 ; else sys heap is old
+   [ :LNOT: NewStyle_RMA
+        CMPNE   r11, #ChangeDyn_RMA                     ; and if RMA old as well, do 2 CMPs
+   ]
+  ]
+        LDREQ   r2, [stack, #4*9]
+        ADREQL  r3, Current_Size + 4
+        LDREQ   r3, [r3, r11, LSL #2]
+        STREQ   r2, [r3]
+        MOV     pc, lr
+ ]
+
+UserMemStartAddr & UserMemStart
+
+Shrink_Limits                                   ; locations to look at
+        &       UserMemStartAddr                ; AplWork - unfudged
+ [ NewStyle_SysHeap
+        &       &FE000000                       ; cause abort
+ |
+        &       SysHeapStart + :INDEX: hpdbase  ; SysHeap
+ ]
+ [ NewStyle_RMA
+        &       &FE000000                       ; cause abort
+ |
+        &       RMAAddress + :INDEX: hpdbase    ; RMA
+ ]
+        &       VduDriverWorkSpace + ScreenSize ; Screen
+ [ NewStyle_SpriteArea
+        &       &FE000000                       ; cause abort
+ |
+        &       SpriteSpaceAddress + saFree     ; Sprites
+ ]
+ [ NewStyle_FontArea
+        &       &FE000000                       ; cause abort
+ |
+        &       0                               ; Fonts not needed
+ ]
+ [ NewStyle_RAMDisc
+        &       &FE000000                       ; cause abort
+ |
+        &       MinRamFSSize                    ; RAMFS
+ ]
+        &       MinFreePoolSize                 ; Free pool
+
+ [ :LNOT: NewStyle_RAMDisc
+MinRamFSSize
+ ]
+MinFreePoolSize
+        &       0
+
+
+
+Grow_LimitsPtrs
+ [ NewCDA
+        &       AppSpaceDANode + DANode_MaxSize ; AplWork
+ |
+        &       AppSpaceMaxSizePtr
+ ]
+ [ NewStyle_SysHeap
+        &       &FE000000                       ; cause abort
+ |
+        &       SysHeapMaxSizePtr               ; SysHeap
+ ]
+ [ NewStyle_RMA
+        &       &FE000000                       ; cause abort
+ |
+        &       RMAMaxSizePtr                   ; RMA
+ ]
+        &       ScreenMaxSizePtr                ; Screen
+ [ NewStyle_SpriteArea
+        &       &FE000000                       ; cause abort
+ |
+        &       SpriteSpaceMaxSizePtr           ; Sprites
+ ]
+ [ NewStyle_FontArea
+        &       &FE000000                       ; cause abort
+ |
+        &       FontCacheMaxSizePtr             ; Fonts
+ ]
+ [ NewStyle_RAMDisc
+        &       &FE000000                       ; cause abort
+ |
+        &       RAMDiscMaxSizePtr               ; RAMFS
+ ]
+ [ UseFreePool
+        &       FreePoolDANode + DANode_MaxSize ; Free pool
+ ]
+
+; The following will eventually become redundant as more and more areas become new ones
+; Ultimately the whole lot can be removed when no old areas exist (perhaps in my grandson's lifetime!)
+
+ [ :LNOT: NewStyle_SysHeap
+SysHeapMaxSizePtr       &       SysHeapMaxSize
+ ]
+ [ :LNOT: NewStyle_RMA
+RMAMaxSizePtr           &       RMAMaxSize
+ ]
+ScreenMaxSizePtr        &       ScreenMaxSize
+ [ :LNOT: NewStyle_SpriteArea
+SpriteSpaceMaxSizePtr   &       SpriteSpaceMaxSize
+ ]
+ [ :LNOT: NewStyle_FontArea
+FontCacheMaxSizePtr     &       FontCacheMaxSize
+ ]
+ [ :LNOT: NewStyle_RAMDisc
+RAMDiscMaxSizePtr       &       RAMDiscMaxSize
+ ]
+ [ :LNOT: NewCDA
+AppSpaceMaxSizePtr      &       AplWorkMaxSize
+ ]
+
+
+Access_Rights
+        &       AP_AppSpace
+        &       AP_SysHeap
+        &       AP_RMA
+        &       AP_Screen
+        &       AP_Sprites
+        &       AP_FontArea
+        &       AP_RAMDisc
+ [ UseFreePool
+        &       AP_FreePool
+ ]
+
+10
+        CMP     r10, r1                         ; if can move more than asked for
+        MOVHI   r10, r1                         ; then move requested amount (TMD 15-Oct-91; was GT)
+        BCS     %FT15                           ; (TMD 15-Oct-91); was GE)
+
+; moving less than asked for: set up an error for exit
+        ADR     r0, ErrorBlock_ChDynamNotAllMoved
+        STR     r0, [stack]
+        LDR     r0, [stack, #4*10]
+        ORR     r0, r0, #V_bit
+        STR     r0, [stack, #4*10]
+        SUB     r0, r6, #1                      ; and make amount moveable
+        BICS    r10, r10, r0                    ; a pagesize multiple
+        BEQ     CDS_PostServiceWithRestore
+
+; IF CAO in ApplWork AND UpCall not claimed THEN Error ChDynamCAO
+
+15
+ [ UseFreePool
+        MOV     r1, #0                          ; default value if apl space not involved
+        CMP     r11, #ChangeDyn_AplSpace        ; if source = aplspace
+        RSBEQ   r1, r10, #0                     ; then make amount -ve
+        CMP     r11, #ChangeDyn_FreeAndApl      ; if source = free and apl
+        SUBEQ   r1, r5, r10                     ; then make it -(amount removing from apl space)
+        MOVNE   r5, r10                         ; else set up r5 to be total amount (wasn't set up above)
+        CMP     r12, #ChangeDyn_AplSpace        ; if dest = aplspace
+        MOVEQ   r1, r10                         ; then make amount +ve
+
+        TEQ     r1, #0                          ; if none of the above
+        BEQ     %FT25                           ; then skip this
+ |
+        MOV     r5, r10                         ; r5 = total amount moving (since no split removes)
+        CMP     r11, #ChangeDyn_AplSpace        ; old code - if src = apl
+        RSBEQ   r1, r10, #0                     ; then -ve
+        MOVNE   r1, r10                         ; else +ve
+ ]
+
+ [ DebugCDA
+        DREG    r11, "After checking, src = "
+        DREG    r12, "and dest = "
+        DREG    r10, "Amount moving (total) = "
+        DREG    r5,  "Amount moving (partial) = "
+
+        DLINE   "Consulting application about change"
+ ]
+
+        MOV     r2, #0
+        LDR     r3, [r2, #AplWorkSize]
+        LDR     r2, [r2, #Curr_Active_Object]
+        CMP     r2, r3                          ; check if CAO outside application space
+        BHI     %FT20                           ; [it is so issue Service not UpCall]
+
+; CAO in application space, so issue UpCall to check it's OK
+
+        MOV     r0, #UpCall_MovingMemory :AND: &FF
+        ORR     r0, r0, #UpCall_MovingMemory :AND: &FFFFFF00
+        TEQ     r1, #0
+        RSBMI   r1, r1, #0                      ; r1 passed in is always +ve (probably a bug, but should be compat.)
+
+        SWI     XOS_UpCall                      ; r1 already set up above
+        CMP     r0, #UpCall_Claimed
+        BEQ     %FT25
+
+        ADRL    r0, ErrorBlock_ChDynamCAO
+        B       ChangeDynamic_Error
+
+; IF service call claimed Then Error AplWSpaceInUse
+
+20
+        MOV     r0, r1                          ; amount removing from aplspace
+        MOV     r1, #Service_Memory
+        BL      Issue_Service
+        CMP     r1, #Service_Serviced
+        BNE     %FT25
+
+        ADRL    r0, ErrorBlock_AplWSpaceInUse
+        B       ChangeDynamic_Error
+
+; Right! r10 is amount of memory we will move
+; (if moving from free pool + apl space then r5 is amount removing from free pool)
+; r11 is the source
+; r12 is the destination
+
+25
+ [ DebugCDA
+        DLINE   "Change is going ahead"
+ ]
+ [ :LNOT: NewStyle_FontArea
+        CMP     r11, #ChangeDyn_FontArea
+        LDREQ   r1, [r11, #FontCacheSize-ChangeDyn_FontArea]
+        SUBEQ   r1, r1, r10                     ; new size
+        SWIEQ   XFont_ChangeArea
+ ]
+
+; remove the cursors if screen moving: might flash during modechange wrch
+
+        CMP     r11, #ChangeDyn_Screen
+        CMPNE   r12, #ChangeDyn_Screen
+        SWIEQ   XOS_RemoveCursors
+
+        CMP     r11, #ChangeDyn_Screen
+        RSBEQ   r0, r10, #0
+
+        MOV     r9, pc
+        TEQEQP  pc, #SVC_mode+I_bit+Z_bit
+        NOP
+    ; disable interrupts while sorting out the screen, in case (e.g) screen
+    ; start address is being reprogrammed under interrupt
+
+        BLEQ    RemovePages
+
+        TEQP    pc, r9          ; restore interrupt state
+
+; calculate addresses of blocks
+
+        MOV     r9, #0
+        LDR     r9, [r9, #MEMC_CR_SoftCopy]
+
+        MOV     r3, r11
+        BL      GetBlockEndSource
+        MOV     r0, r3                  ; R0 := blockend(source)
+
+        CMP     r12, #ChangeDyn_Screen
+        BEQ     ExtendScreen            ; dest=screen: perversion needed
+
+        MOV     r3, r12
+        BL      GetBlockEnd             ; R3 := blockend(dest)
+
+; move memory: r5 bytes from r0 backwards to r3 forwards
+        MOV     r1, #0
+30
+        SUB     r0, r0, r6
+        Push    "r5,r11"
+        ADR     r11, Access_Rights+4
+        LDR     r11, [r11, r12, LSL #2] ; get access privs (+ CB bits in new world)
+ [ DebugCDA
+        DREG    r0, "Moving page at ", cc
+        DREG    r3, " to ", cc
+        DREG    r11, " with PPL "
+ ]
+        BL      MoveCAMatR0toR3
+        Pull    "r5,r11"
+        BVS     cambust
+        ADD     r1, r1, r6
+        ADD     r3, r3, r6
+
+        CMP     r1, r5                  ; have we done all (of this lot at least)?
+        BNE     %BT30                   ; [no, so loop]
+
+        CMP     r5, r10                 ; have we done all of both lots?
+        BEQ     %FT33                   ; yes, so finished
+
+        Push    "r1,r3"
+        MOV     r3, #ChangeDyn_AplSpace ; else we have more to do, from aplspace
+        BL      GetBlockEnd             ; so get apl block end
+        MOV     r0, r3                  ; and put into src register
+        Pull    "r1,r3"
+        MOV     r5, r10
+        B       %BT30
+
+33
+        CMP     r11, #ChangeDyn_Screen
+        BNE     %FT40
+
+; source=screen: need to shuffle rest of screen down.
+
+        VDWS    r5
+        LDR     r5, [r5, #TotalScreenSize]
+        MOV     r3, #ScreenEndAdr
+35
+        SUB     r0, r0, r6
+        SUB     r3, r3, r6
+        Push    "r5"
+        MOV     r11, #AP_Screen
+        BL      MoveCAMatR0toR3
+        Pull    "r5"
+        BVS     cambust
+        SUBS    r5, r5, r6
+        BGT     %BT35
+        MOV     r11, #ChangeDyn_Screen
+
+40
+; now need to restore sizes if we have locked a heap
+
+ [ (:LNOT: NewStyle_SysHeap) :LOR: (:LNOT: NewStyle_RMA)
+        BL      testrestoreheapend
+ ]
+
+; update object sizes: current size(dest)   +:= r10
+;                      current size(source) -:= r10
+
+        MOV     r4, #0                          ; remember for later
+        LDR     r4, [r4, #SpriteSize]
+
+        CMP     r11, #ChangeDyn_FreeAndApl
+        BNE     %FT41
+        LDR     r2, Current_Size+4+(ChangeDyn_FreePool :SHL: 2)         ; r2 -> old size of free pool
+        LDR     lr, [r2]                                                ; lr = old size
+        LDR     r3, Shrink_Limits+4+(ChangeDyn_FreePool :SHL: 2)        ; r3 -> shrink limit of free pool
+        LDR     r3, [r3]                                                ; r3 = shrink limit
+        SUB     lr, lr, r3                                              ; how much we took out of it
+        STR     r3, [r2]                                                ; put shrink limit into current size
+
+        LDR     r2, Current_Size+4+(ChangeDyn_AplSpace :SHL: 2)         ; r2 -> old size of apl space
+        LDR     r3, [r2]                                                ; r3 = old size of apl space
+        SUB     r3, r3, r10                                             ; how much we would have taken out of it
+        ADD     r3, r3, lr                                              ; but don't take out the stuff which came out of free
+        STR     r3, [r2]
+        ADR     r0, Current_Size+4
+        B       %FT42
+
+41
+        ADR     r0, Current_Size+4
+        CMP     r11, #ChangeDyn_Screen          ; don't update TotalScreenSize
+        LDRNE   r2, [r0, r11, LSL #2]
+        LDRNE   r3, [r2]
+        SUBNE   r3, r3, r10
+        STRNE   r3, [r2]
+42
+        CMP     r12, #ChangeDyn_Screen          ; don't update TotalScreenSize
+        LDRNE   r2, [r0, r12, LSL #2]
+        LDRNE   r3, [r2]
+        ADDNE   r3, r3, r10
+        STRNE   r3, [r2]
+
+ [ :LNOT: NewStyle_SpriteArea
+        CMP     r11, #ChangeDyn_SpriteArea      ; watch out for sprite area creation
+        CMPNE   r12, #ChangeDyn_SpriteArea      ; or deletion
+        BNE     %FT45
+
+        MOV     r1, #0                          ; used later also!!
+        LDR     r3, [r1, #SpriteSize]
+        CMP     r3, #0                          ; if sprite area deleted
+        LDRNE   r3, =SpriteSpaceAddress         ; tell the vdu drivers
+        MOV     r14, #VduDriverWorkSpace
+        STR     r3, [r14, #SpAreaStart]
+        BEQ     %FT45                           ; and don't touch non-existent memory
+
+        CMP     r4, #0                          ; if area was null,
+        LDMNEIB r3, {r4-r6}                     ; (skip size)
+        MOVEQ   r4, #0                          ; initialise variables if was null
+        MOVEQ   r5, #saExten
+        MOVEQ   r6, #saExten
+        LDR     r0, [r1, #SpriteSize]           ; set up earlier
+        STMIA   r3, {r0,r4-r6}                  ; stash new set of variables
+45
+ ]
+ [ UseFreePool
+        CMP     r11, #ChangeDyn_AplSpace        ; if apl space involved in transfer
+        CMPNE   r11, #ChangeDyn_FreeAndApl
+        CMPNE   r12, #ChangeDyn_AplSpace
+        MOVEQ   r0, #0
+        LDREQ   r2, [r0, #AplWorkSize]          ; then reset memlimit to aplworksize
+        STREQ   r2, [r0, #MemLimit]             ; MemLimit := AplWorkSize
+ |
+        MOV     r0, #0
+        LDR     r2, [r0, #AplWorkSize]
+        STR     r2, [r0, #MemLimit]             ; MemLimit := AplWorkSize
+ ]
+
+        CMP     r12, #ChangeDyn_Screen
+        MOVEQ   r0, r10
+
+        MOV     r9, pc
+        TEQEQP  pc, #SVC_mode+I_bit+Z_bit
+    ; disable interrupts while sorting out the screen, in case (e.g) screen
+    ; start address is being reprogrammed under interrupt
+
+        BLEQ    InsertPages
+
+        TEQP    pc, r9       ; restore interrupt state
+
+        CMP     r11, #ChangeDyn_Screen
+        CMPNE   r12, #ChangeDyn_Screen
+        SWIEQ   XOS_RestoreCursors
+
+ [ :LNOT: NewStyle_FontArea
+        CMP     r12, #ChangeDyn_FontArea
+        LDREQ   r1, [r12, #FontCacheSize-ChangeDyn_FontArea]
+        SWIEQ   XFont_ChangeArea
+ ]
+
+ [ :LNOT: NewStyle_RAMDisc
+        CMP     r11, #ChangeDyn_RamFS
+        CMPNE   r12, #ChangeDyn_RamFS
+        BEQ     reinitialise_RAMFS
+ ]
+        B       CDS_PostService
+
+Current_Size
+        &       AplWorkSize                             ; AplWork
+ [ NewStyle_SysHeap
+        &       &FE000000                               ; cause abort
+ |
+        &       SysHeapStart + :INDEX: hpdend           ; SysHeap
+ ]
+ [ NewStyle_RMA
+        &       &FE000000                               ; cause abort
+ |
+        &       RMAAddress + :INDEX: hpdend             ; RMA
+ ]
+        &       VduDriverWorkSpace + TotalScreenSize    ; Screen
+ [ NewStyle_SpriteArea
+        &       &FE000000                               ; cause abort
+ |
+        &       SpriteSize                              ; sprites
+ ]
+ [ NewStyle_FontArea
+        &       &FE000000                               ; cause abort
+ |
+        &       FontCacheSize                           ; fonts
+ ]
+ [ NewStyle_RAMDisc
+        &       &FE000000                               ; cause abort
+ |
+        &       RAMDiscSize                             ; RAMFS
+ ]
+ [ UseFreePool
+        &       FreePoolDANode + DANode_Size            ; Free pool
+ ]
+
+GetBlockEndSource
+        CMP     r3, #ChangeDyn_FreeAndApl               ; if removing from free + apl
+        MOVEQ   r3, #ChangeDyn_FreePool                 ; then start by removing from free pool
+ [ (:LNOT: NewStyle_RMA) :LOR: (:LNOT: NewStyle_SysHeap) ; none of this needed if both sysheap+RMA new
+  [ NewStyle_SysHeap                                    ; if sys heap new
+        CMP     r3, #ChangeDyn_RMA                      ; then RMA must be old
+  |
+        CMP     r3, #ChangeDyn_SysHeap                  ; else sys heap is old
+   [ :LNOT: NewStyle_RMA
+        CMPNE   r3, #ChangeDyn_RMA                      ; and if RMA old as well, do 2 CMPs
+   ]
+  ]
+        BNE     GetBlockEnd
+        ADR     r4, StartAddrs+4
+        LDR     r3, [r4, r3, LSL #2]
+        LDR     r4, [stack, #4*9]
+        ADD     r3, r3, r4                              ; start + size = end
+        MOV     pc, lr
+ ]
+
+GetBlockEnd   ; R3 is area to get end of: return address in R3
+        MOV     r1, r3
+        ADR     r4, StartAddrs+4
+        LDR     r3, [r4, r3, LSL #2]
+        CMP     r1, #ChangeDyn_Screen                   ; screen ?
+        MOVEQ   pc, lr
+        ADR     r4, Current_Size+4
+        LDR     r4, [r4, r1, LSL #2]
+        LDR     r4, [r4]
+        ADD     r3, r3, r4                              ; start + size = end
+        MOV     pc, lr
+
+StartAddrs
+        &       0                               ; AplWork
+ [ NewStyle_SysHeap
+        &       &FE000000                       ; cause abort
+ |
+        &       SysHeapStart                    ; SysHeap
+ ]
+ [ NewStyle_RMA
+        &       &FE000000                       ; cause abort
+ |
+        &       RMAAddress                      ; RMA
+ ]
+        &       ScreenEndAdr                    ; Screen
+ [ NewStyle_SpriteArea
+        &       &FE000000                       ; cause abort
+ |
+        &       SpriteSpaceAddress              ; sprites
+ ]
+ [ NewStyle_FontArea
+        &       &FE000000                       ; cause abort
+ |
+        &       FontCacheAddress                ; fonts
+ ]
+ [ NewStyle_RAMDisc
+        &       &FE000000                       ; cause abort
+ |
+        &       RAMDiscAddress                  ; RAMFS
+ ]
+ [ UseFreePool
+        &       FreePoolAddress                 ; Free pool
+ ]
+
+; ExtendScreen - move memory into screen area
+;
+; in:   r0 -> logical address of end of first source area (either free pool or aplspace)
+;       r5 = amount of memory being moved from first source area
+;       r6 = page size
+;       r9 = MEMC CR
+;       r10 = total amount of memory being moved
+;       r11 = number of source area
+;       r12 = number of dest area
+
+
+ExtendScreen
+
+; screenpos -:= r10 (move all current blocks down)
+
+        Push    "r5,r11"                ; save partial amount and src area number
+        MOV     r2, #0
+        VDWS    r5
+        LDR     r5, [r5, #TotalScreenSize]
+        RSB     r3, r5, #ScreenEndAdr
+        SUB     r3, r3, r10             ; where new screen start is
+50
+        MOV     r11, #AP_Screen         ; access privileges for screen (includes CB bits in new world)
+        BL      Call_CAM_Mapping
+        ADD     r2, r2, #1
+        ADD     r3, r3, r6
+        SUBS    r5, r5, r6
+        BNE     %BT50
+        Pull    "r5,r11"
+
+        ADD     r5, r3, r5              ; logaddr of end of first part (if split) or both otherwise
+
+; r0 -> end of the source (AplSpace or FreePool)
+        Push    "r7, r8, r10-r12"
+55
+        Push    "r2, r3, r5, pc"        ; save flags, etc. too
+        ADRL    r3, PageShifts-1
+        LDRB    r3, [r3, r6, LSR #12]
+        MOV     r3, r2, LSL r3          ; r3 = pagesize*r2
+        ADD     r3, r3, #ScreenEndAdr   ; physram addr of next screen block
+
+        SUB     r0, r0, r6              ; address of last src block
+
+        MOV     r1, #Service_ClaimFIQ   ; we may be moving FIQ workspace
+        BL      Issue_Service
+        ADD     r1, r0, r6              ; end marker
+
+        TEQP    pc, #SVC_mode+I_bit     ; disable IRQs as we may be moving
+        NOP                             ; IRQ workspace
+
+; copy R6 bytes from nextscreenblock to last src block; the last
+; src block MUST NOT be doubly mapped.
+
+60
+        LDMIA   r3!, {r2, r4, r5, r7, r8, r10, r11, r12}
+        STMIA   r0!, {r2, r4, r5, r7, r8, r10, r11, r12}
+        CMP     r0, r1
+        BLT     %BT60
+
+        LDR     r2, [stack]
+        SUB     r0, r0, r6
+        MOV     r3, #0
+        LDR     r3, [r3, #CamEntriesPointer]    ; get address of soft CAM copy
+ [ ExpandedCamMap
+        ADD     r3, r3, r2, LSL #3              ; point at (address, PPL) for this page
+        LDMIA   r3, {r3, r11}                   ; and load them
+ |
+        LDR     r3, [r3, r2, LSL #2]            ; curr addr of next screen block
+        MOV     r11, r3, LSR #28                ; protection level
+        BIC     r3, r3, #&F0000000
+ ]
+
+ [ UseFreePool
+        BL      MoveCAMatR0toR3issuingPagesSafe
+ |
+        BL      MoveCAMatR0toR3                 ; last aplblock := nextscreenblock
+ ]
+
+        Pull    "r2, r3, r5, r7"                ; r7 has IRQ state to restore
+        BVS     cambust2
+                                                ; entry no in r2, logaddr in r3
+        MOV     r11, #AP_Screen
+        BL      Call_CAM_Mapping                ; nextscreenblock moves into place
+
+        TEQP    pc, r7                          ; restore IRQ state
+        NOP
+
+        MOV     r1, #Service_ReleaseFIQ
+        BL      Issue_Service
+        ADD     r3, r3, r6
+        ADD     r2, r2, #1
+
+        CMP     r3, r5                          ; have we got to end of this part
+        BNE     %BT55                           ; [no, so loop]
+
+        CMP     r3, #ScreenEndAdr
+        BEQ     %FT65
+
+        MOV     r3, #ChangeDyn_AplSpace
+        BL      GetBlockEnd                     ; get end of aplspace
+        MOV     r0, r3                          ; r0 -> end of aplspace
+        MOV     r3, r5                          ; continue dest where we left off
+        MOV     r5, #ScreenEndAdr               ; finish only at end this time
+        B       %BT55
+
+65
+        Pull    "r7, r8, r10-r12"
+        B       %BT40
+
+cambust2
+        Pull    "r7, r8, r10-r12"
+cambust
+        STR     r0, [stack]
+        Pull    "r0, r2-r6, r9, r10, lr"
+        B      SLVK_SetV
+
+ ]
+
+; *** End of old style code ***
+
+; Exit from ChangeDynamicArea with error Not all moved
+
+ [ NewStyle_All
+failure_IRQgoingClearSemaphore
+        MOV     r0, #0
+        STR     r0, [r0, #CDASemaphore]
+ ]
+failure_IRQgoing
+        ADR     r0, ErrorBlock_ChDynamNotAllMoved
+ChangeDynamic_Error
+        MOV     r10, #0
+        STR     r0, [stack]
+        LDR     lr, [stack, #4*10]
+        ORR     lr, lr, #V_bit
+        STR     lr, [stack, #4*10]
+CDS_PostServiceWithRestore
+ [ (:LNOT: NewStyle_SysHeap) :LOR: (:LNOT: NewStyle_RMA)
+        BL      testrestoreheapend
+ ]
+      [ International
+        LDR     r0, [stack]
+        BL      TranslateError
+        STR     r0, [stack]
+      ]
+
+; and drop thru to ...
+
+CDS_PostService
+        MOV     r1, #Service_MemoryMoved
+        MOV     r0, r10                 ; amount moved
+        MOVS    r2, r11                 ; which way was transfer?
+        BMI     %FT47                   ; [definitely a grow]
+        CMP     r11, #ChangeDyn_FreePool
+        BNE     %FT48                   ; [definitely a shrink]
+        CMP     r12, #ChangeDyn_AplSpace
+        BEQ     %FT48                   ; [a shrink]
+47
+        RSB     r0, r0, #0             ; APLwork or free was source
+        MOV     r2, r12                ; r2 = area indicator
+48
+        BL      Issue_Service
+
+        MOV     r1, r10                ; amount moved
+
+     [ International
+        Pull    "r0"
+        LDR     lr, [sp, #9*4]
+        TST     lr, #V_bit
+        BLNE    TranslateError
+        Pull    "r2-r9, r10, lr"
+     |
+        Pull    "r0, r2-r9, r10, lr"
+     ]
+        ExitSWIHandler
+
+        MakeErrorBlock ChDynamNotAllMoved
+
+ [ MEMM_Type = "ARM600"
+  [ 1 = 1
+; in:   r0 = logical address where page is now
+
+GetPageFlagsForR0IntoR6 ENTRY "R0-R2, R4-R5, R7"
+;
+; code from MoveCAMatR0toR3
+;
+        LDR     r5, =L2PT
+        ADD     r4, r5, r0, LSR #10             ; r4 -> L2PT for log addr r0
+        MOV     r2, r4, LSR #12
+        LDR     r2, [r5, r2, LSL #2]            ; r2 = L2PT entry for r4
+        TST     r2, #3                          ; if no page there
+        BEQ     %FT90                           ; then cam corrupt
+
+        LDR     r4, [r4]                        ; r4 = L2PT entry for r0
+        TST     r4, #3                          ; check entry is valid too
+        BEQ     %FT91
+        MOV     r4, r4, LSR #12                 ; r4 = phys addr >> 12
+
+        MOV     r2, #0
+        LDR     r6, [r2, #MaxCamEntry]
+        MOV     r5, #PhysRamTable
+10
+        CMP     r2, r6                          ; if page we've got to is > max
+        BHI     %FT92                           ; then corrupt
+        LDMIA   r5!, {r7, lr}                   ; get phys.addr, size
+        SUB     r7, r4, r7, LSR #12             ; number of pages into this bank
+        CMP     r7, lr, LSR #12                 ; if too many
+        ADDCS   r2, r2, lr, LSR #12             ; then advance physical page no.
+        BCS     %BT10                           ; and loop
+
+        ADD     r2, r2, r7                      ; add on number of pages within bank
+;
+; code from BangCamUpdate
+;
+        MOV     r1, #0
+        LDR     r1, [r1, #CamEntriesPointer]
+        ADD     r1, r1, r2, LSL #3              ; point at cam entry (logaddr, PPL)
+        LDMIA   r1, {r0, r6}                    ; r0 = current logaddress, r6 = current PPL
+        EXIT
+
+90
+        ADR     lr, NoL2ForPageBeingRemovedError ; NB don't corrupt r0 yet - we need that in block as evidence
+95
+        STR     lr, [sp]                        ; update returned r0
+        BL      StoreDebugRegs
+        PullEnv                                 ; seriously broken memory
+        SETV
+        MOV     pc, lr
+
+91
+        ADR     lr, PageBeingRemovedNotPresentError
+        B       %BT95
+
+92
+        ADR     lr, PhysicalAddressNotFoundError
+        B       %BT95
+
+
+  ]
+ ]
+
+; MoveCAMatR0toR3
+; in:   r0 = old logaddr
+;       r3 = new logaddr
+;       r9 = MEMC CR
+;       r11 = page protection level
+;
+; out:  r2 = physical page number of page moved, unless there was a serious error
+;       r0,r1,r3,r6-r12 preserved
+;       r4,r5 corrupted
+
+ [ MEMM_Type = "ARM600"
+MoveCAMatR0toR3 ENTRY "r0,r1,r6,r7"
+        LDR     r5, =L2PT
+        ADD     r4, r5, r0, LSR #10             ; r4 -> L2PT for log addr r0
+        MOV     r2, r4, LSR #12
+        LDR     r2, [r5, r2, LSL #2]            ; r2 = L2PT entry for r4
+        TST     r2, #3                          ; if no page there
+        BEQ     %FT90                           ; then cam corrupt
+
+        LDR     r4, [r4]                        ; r4 = L2PT entry for r0
+        TST     r4, #3                          ; check entry is valid too
+        BEQ     %FT91
+        MOV     r4, r4, LSR #12                 ; r4 = phys addr >> 12
+
+        MOV     r2, #0
+        LDR     r6, [r2, #MaxCamEntry]
+        MOV     r5, #PhysRamTable
+10
+        CMP     r2, r6                          ; if page we've got to is > max
+        BHI     %FT92                           ; then corrupt
+        LDMIA   r5!, {r7, lr}                   ; get phys.addr, size
+        SUB     r7, r4, r7, LSR #12             ; number of pages into this bank
+        CMP     r7, lr, LSR #12                 ; if too many
+        ADDCS   r2, r2, lr, LSR #12             ; then advance physical page no.
+        BCS     %BT10                           ; and loop
+
+        ADD     r2, r2, r7                      ; add on number of pages within bank
+        BL      BangCamUpdate
+        CLRV
+        EXIT
+
+90
+        ADR     lr, NoL2ForPageBeingRemovedError ; NB don't corrupt r0 yet - we need that in block as evidence
+95
+        STR     lr, [sp]                        ; update returned r0
+        BL      StoreDebugRegs
+        PullEnv                                 ; seriously broken memory
+        SETV
+        MOV     pc, lr
+
+91
+        ADR     lr, PageBeingRemovedNotPresentError
+        B       %BT95
+
+92
+        ADR     lr, PhysicalAddressNotFoundError
+        B       %BT95
+
+StoreDebugRegs
+        Push    "lr"
+        MOV     lr, #CamMapCorruptDebugBlock
+        STMIA   lr, {r0-lr}
+        LDR     r0, [sp, #1*4]                  ; reload stacked r0 (error pointer)
+        STR     r0, [lr, #15*4]                 ; store in stacked PC position
+        Pull    "pc"
+
+NoL2ForPageBeingRemovedError
+        &       0
+        =       "Memory Corrupt: No L2PT for page being removed", 0
+        ALIGN
+
+PageBeingRemovedNotPresentError
+        &       0
+        =       "Memory Corrupt: Page being removed was not present", 0
+        ALIGN
+
+PhysicalAddressNotFoundError
+        &       0
+        =       "Memory Corrupt: Physical address not found", 0
+        ALIGN
+ |
+
+MoveCAMatR0toR3 ROUT
+        MOV     r2, #0
+        LDR     r4, [r2, #CamEntriesPointer]
+        LDR     r2, [r2, #MaxCamEntry]
+10
+ [ ExpandedCamMap
+        LDR     r5, [r4, r2, LSL #3]
+ |
+        LDR     r5, [r4, r2, LSL #2]
+        BIC     r5, r5, #&F0000000
+ ]
+        CMP     r5, r0
+        BEQ     Call_CAM_Mapping
+        SUBS    r2, r2, #1
+        BGE     %BT10
+
+        ADR     r0, CamMapBroke
+        ORRS    pc, lr, #V_bit
+ ]
+
+CamMapBroke
+        &       0
+        =       "!!!! CAM Map Corrupt !!!!", 0
+        ALIGN
+Call_CAM_Mapping
+        Push    "r0, r1, r4, r6, lr"
+        BL      BangCamUpdate
+        Pull    "r0, r1, r4, r6, pc"
+
+ [ UseFreePool :LAND: :LNOT: NewStyle_All
+
+; MoveCAMatR0toR3issuingPagesSafe
+; in:   r0 = old logaddr
+;       r2 = old physical page number
+;       r3 = new logaddr
+;       r9 = MEMC CR
+;       r11 = page protection level
+;
+; out:  r0,r1,r3,r6-r12 preserved
+;       r2,r4,r5 corrupted
+;
+; Note: this still needs some serious work done to it to cope with doubly-mapped areas
+
+MoveCAMatR0toR3issuingPagesSafe ENTRY "r0,r1,r3,r6,r7,r12", 6*4
+        STR     r2, [sp, #0*4]                  ; store old page number at offset 0 in frame
+        LDR     r5, =L2PT
+        ADD     r4, r5, r0, LSR #10             ; r4 -> L2PT for log addr r0
+        MOV     r2, r4, LSR #12
+        LDR     r2, [r5, r2, LSL #2]            ; r2 = L2PT entry for r4
+        TST     r2, #3                          ; if no page there
+        BEQ     %FT90                           ; then cam corrupt
+
+        LDR     r4, [r4]                        ; r4 = L2PT entry for r0
+        TST     r4, #3                          ; check entry OK too
+        BEQ     %FT91
+        MOV     r12, r4, LSR #12                ; r12 = phys addr >> 12
+
+        MOV     r2, #0
+        LDR     r6, [r2, #MaxCamEntry]
+        MOV     r5, #PhysRamTable
+10
+        CMP     r2, r6                          ; if page we've got to is >= max
+        BHI     %FT92                           ; then corrupt
+        LDMIA   r5!, {r7, lr}                   ; get phys.addr, size
+        SUB     r7, r12, r7, LSR #12             ; number of pages into this bank
+        CMP     r7, lr, LSR #12                 ; if too many
+        ADDCS   r2, r2, lr, LSR #12             ; then advance physical page no.
+        BCS     %BT10                           ; and loop
+
+        ADD     r2, r2, r7                      ; add on number of pages within bank
+        STR     r2, [sp, #3*4]                  ; store new page number at offset 3 in frame
+        BL      BangCamUpdate
+        EXIT    VS
+
+; now check if page being snaffled is a L2 page, and if so, change L1 contents to point to new page
+
+        SUBS    lr, r3, #L2PT                   ; check if destination page points in L2PT area
+        BCC     %FT20                           ; below L2PT, so OK
+        CMP     lr, #4*1024*1024                ; is offset into L2PT less than size of L2PT?
+        BCS     %FT20                           ; no, so OK
+
+        LDR     r1, =L1PT
+        ADD     r1, r1, lr, LSR #(12-4)         ; address in L1 of 4 consecutive words to update
+        LDR     r2, [r1]                        ; load 1st word, to get AP etc bits
+        MOV     r2, r2, LSL #(31-9)             ; junk other bits
+        MOV     r2, r2, LSR #(31-9)
+        ORR     r2, r2, r12, LSL #12            ; merge with address bits
+        STR     r2, [r1], #4
+        ADD     r2, r2, #&400
+        STR     r2, [r1], #4
+        ADD     r2, r2, #&400
+        STR     r2, [r1], #4
+        ADD     r2, r2, #&400
+        STR     r2, [r1], #4
+20
+; now issue the service
+
+        MOV     r1, #Service_PagesSafe
+        MOV     r2, #1                          ; 1 page at a time
+        MOV     r3, sp                          ; 1st frame starts at sp
+        ADD     r4, r3, #3*4                    ; 2nd frame starts at sp +12
+        BL      Issue_Service
+        CLRV
+        EXIT
+
+90
+        ADR     lr, NoL2ForPageBeingRemovedError
+95
+        STR     lr, [sp]                        ; update returned r0
+        BL      StoreDebugRegs
+        PullEnv                                 ; seriously broken memory
+        SETV
+        MOV     pc, lr
+
+91
+        ADR     lr, PageBeingRemovedNotPresentError
+        B       %BT95
+
+92
+        ADR     lr, PhysicalAddressNotFoundError
+        B       %BT95
+
+ ]
+
+ [ :LNOT: NewStyle_RAMDisc
+;........................................
+; Old style RAMFS bashing
+
+CheckRAMFSChangeOK
+      Push   "r0-r5"
+      MOV     r0, #5
+      ADR     r1, ramcolondollardotstar
+      SWI     XOS_File
+      CMPVC   r0, #0
+      Pull   "r0-r5"
+      BVS     AllowRAMFSChange             ; ramfs not present
+      BEQ     AllowRAMFSChange             ; ramfs empty
+      ADR     r0, ErrorBlock_RAMFsUnchangeable
+      B       ChangeDynamic_Error
+      MakeErrorBlock RAMFsUnchangeable
+
+ramcolondollardotstar = "ram:$.*",0
+ramfsname = "ramfs",0
+
+      ALIGN
+
+reinitialise_RAMFS
+      Push   "r0-r6"
+      MOV     r0, #ModHandReason_EnumerateROM_Modules
+      MOV     r1, #0
+      MOV     r2, #-1
+look_for_RAMFS
+      SWI     XOS_Module
+      BVS     OKtoreinitRAMFS        ; can't find it: may be in ram
+      ADR     r5, ramfsname
+nameloop
+      LDRB    r6, [r3], #1
+      CMP     r6, #" "
+      BLE     foundramfs
+      LowerCase r6, lr
+      LDRB    lr, [r5], #1
+      CMP     lr, r6
+      BEQ     nameloop
+      B       look_for_RAMFS
+
+foundramfs
+      CMP     r4, #-1
+      BNE     OKtoreinitRAMFS
+      Pull   "r0-r6"
+      B       CDS_PostService
+
+OKtoreinitRAMFS
+      MOV     r0, #ModHandReason_ReInit
+      ADR     r1, ramfsname
+      SWI     XOS_Module
+      Pull   "r0-r6"
+      B       CDS_PostService
+ ]
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r0 bits 0..6 = area number
+;       r0 bit 7 set => return max area size in r2 (implemented 13 Jun 1990)
+;                       this will return an error if not implemented
+; Out   r0 = address of area
+;       r1 = current size of area
+;       r2 = max size of area if r0 bit 7 set on entry (preserved otherwise)
+
+; TMD 19-May-93: When this is updated to new CDA list, change meaning as follows:
+
+; r0 in range 0..&7F    return address, size of area r0
+;             &80..&FF  return address, size, maxsize of area (r0-&80)
+;             &100..    return address, size, maxsize of area r0
+
+; TMD 20-Aug-93: New bit added - if r0 = -1 on entry, then returns info on application space
+; r0 = base address (&8000)
+; r1 = current size (for current task)
+; r2 = maximum size (eg 16M-&8000)
+
+ReadDynamicArea ROUT
+
+readdyn_returnR2bit     *       &80
+        ASSERT  ChangeDyn_MaxArea < readdyn_returnR2bit
+
+ [ NewCDA
+        CMP     r0, #-1                         ; if finding out about app space
+        LDREQ   r1, [r0, #AplWorkSize+1]        ; then r1 = current size
+        LDREQ   r2, =AplWorkMaxSize             ; and r2 = max size
+        MOVEQ   r0, #&8000                      ; r0 = base address
+        SUBEQ   r1, r1, r0                      ; adjust size and maxsize
+        SUBEQ   r2, r2, r0                      ; to remove bottom 32K
+        ExitSWIHandler EQ
+
+; first check if it's one of the new ones
+
+        Push    "r1,lr"
+        CMP     r0, #&100                       ; if area >= &100
+        MOVCS   r1, r0                          ; then just use area
+        BICCC   r1, r0, #readdyn_returnR2bit    ; else knock off bit 7
+        BL      CheckAreaNumber                 ; out: r10 -> node
+        Pull    "r1,lr"
+        BCC     %FT05                           ; [not a new one, so use old code]
+
+        CMP     r0, #&80                        ; CS => load maxsize into R2
+                                                ; (do this either if bit 7 set, or area >=&100)
+        LDRCS   r2, [r10, #DANode_MaxSize]
+        LDR     r1, [r10, #DANode_Size]         ; r1 = current size
+        LDR     r0, [r10, #DANode_Base]         ; r0 -> base
+        LDR     r11, [r10, #DANode_Flags]       ; if doubly mapped
+        TST     r11, #DynAreaFlags_DoublyMapped
+        SUBNE   r0, r0, r1                      ; then return start of 1st copy for compatibility
+        ExitSWIHandler
+05
+ ]
+ [ :LNOT: NewStyle_All
+        BIC     r10, r0, #readdyn_returnR2bit
+        CMP     r10, #ChangeDyn_MaxArea
+        BLS     %FT07
+ ]
+        ADRL    r0, ErrorBlock_BadDynamicArea
+      [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      ]
+        B       SLVK_SetV
+
+ [ :LNOT: NewStyle_All
+07
+        TST     r0, #readdyn_returnR2bit        ; if bit 7 set, R2 = max size of area
+  [ MEMC_Type = "IOMD"
+        BEQ     %FT10
+        EORS    r11, r10, #ChangeDyn_Screen     ; if screen
+        LDREQ   r2, [r11, #VideoSize]           ; then use video size
+        ADRNEL  r11, Grow_LimitsPtrs+4
+        LDRNE   r2, [r11, r10, LSL #2]          ; else use grow limits
+        LDRNE   r2, [r2]                        ; (grow limits are pointers to max sizes now)
+10
+  |
+        ADRNEL  r11, Grow_LimitsPtrs+4
+        LDRNE   r2, [r11, r10, LSL #2]
+        LDRNE   r2, [r2]                        ; (grow limits are pointers to max sizes now)
+  ]
+
+        CMP     r10, #ChangeDyn_Screen    ; screen?
+        ADRL    r0, Current_Size+4
+        ADRL    r11, StartAddrs+4
+        LDR     r1, [r0, r10, LSL #2]
+        LDR     r1, [r1]                  ; r1 = current size of area
+        LDR     r0, [r11, r10, LSL #2]    ; r0 = start address of area
+        SUBEQ   r0, r0, r1                ; screen goes wackbords
+        B       SLVK
+ ]
+
+        MakeErrorBlock  BadDynamicArea
+
+; *************************************************************************
+; User access to CAM mapping
+; ReadMemMapInfo:
+; returns R0 = pagsize
+;         R1 = number of pages in use  (= R2 returned from SetEnv/Pagesize)
+; *************************************************************************
+
+ReadMemMapInfo_Code
+      MOV      R10, #0
+      LDR      R0, [R10, #Page_Size]
+      LDR      R1, [R10, #RAMLIMIT]    ; = total memory size
+      ADRL     R11, PageShifts-1
+      LDRB     R11, [R11, R0, LSR #12]
+      MOV      R1, R1, LSR R11
+      ExitSWIHandler
+
+; ************************************************************************
+; SWI ReadMemMapEntries: R0 pointer to list.
+;  Entries are three words long, the first of which is the CAM page number.
+;  List terminated by -1.
+; Returns pagenumber (unaltered)/address/PPL triads as below
+; ************************************************************************
+
+ReadMemMapEntries_Code  ROUT
+        Push    "r0,r14"
+        MOV     r14, #0
+        LDR     r10, [r14, #CamEntriesPointer]
+        LDR     r14, [r14, #MaxCamEntry]
+01
+        LDR     r12, [r0], #4
+        CMP     r12, r14
+        Pull    "r0,r14", HI
+        ExitSWIHandler HI
+ [ ExpandedCamMap
+        ADD     r11, r10, r12, LSL #3
+        LDMIA   r11, {r11, r12}
+ |
+        LDR     r11, [r10, r12, LSL #2]
+        MOV     r12, r11, LSR #28               ; PPL
+        BIC     r11, r11, #&F0000000
+ ]
+        STMIA   r0!, {r11, r12}
+        B       %BT01
+
+; ************************************************************************
+; SWI FindMemMapEntries:
+; In:  R0 -> table of 12-byte page entries
+;       +0      4       probable page number (0..npages-1) (use 0 if no idea)
+;       +4      4       logical address to match with
+;       +8      4       undefined
+;       terminated by a single word containing -1
+;
+; Out: table of 12-byte entries updated:
+;       +0      4       actual page number (-1 => not found)
+;       +4      4       address (preserved)
+;       +8      4       page protection level (3 if not found)
+;       terminator preserved
+;
+; ************************************************************************
+
+FindMemMapEntries_Code  ROUT
+ [ ExpandedCamMap
+
+; Code for expanded CAM map version
+
+        Push    "r0, r9, r14"
+        MOV     r14, #0
+        LDR     r9, [r14, #MaxCamEntry]
+        LDR     r14, [r14, #CamEntriesPointer]  ; r14 -> start of cam map
+        ADD     r9, r14, r9, LSL #3             ; r9 -> first word of last entry in cam map
+10
+        LDR     r10, [r0, #0]                   ; r10 = guess page number (or -1)
+        CMP     r10, #-1                        ; if -1 then end of list
+        Pull    "r0, r9, r14", EQ               ; so restore registers
+        ExitSWIHandler EQ                       ; and exit
+
+        LDR     r11, [r0, #4]                   ; r11 = logical address
+        ADD     r10, r14, r10, LSL #3           ; form address with 'guess' page
+        CMP     r10, r9                         ; if off end of CAM
+        BHI     %FT20                           ; then don't try to use the guess
+
+        LDR     r12, [r10]                      ; load address from guessed page
+        TEQ     r11, r12                        ; compare address
+        BEQ     %FT60                           ; if equal, then guessed page was OK
+20
+
+; for now, cheat by looking in L2PT, to see if we can speed things up
+
+        Push    "r5-r8"                         ; need some registers here!
+        LDR     r10, =L2PT
+        MOV     r8, r11, LSR #12                ; r8 = logical page number
+        ADD     r8, r10, r8, LSL #2             ; r8 -> L2PT entry for log.addr
+        MOV     r5, r8, LSR #12                 ; r5 = page offset to L2PT entry for log.addr
+        LDR     r5, [r10, r5, LSL #2]           ; r5 = L2PT entry for L2PT entry for log.addr
+        TST     r5, #3                          ; if page not there
+        SUBEQ   r10, r9, #8                     ; then invalid page so go from last one
+        BEQ     %FT45
+        LDR     r8, [r8]                        ; r8 = L2PT entry for log.addr
+        MOV     r8, r8, LSR #12                 ; r8 = physaddr / 4K
+
+        MOV     r5, #PhysRamTable
+        SUB     r10, r14, #8
+30
+        CMP     r10, r9                         ; have we run out of RAM banks?
+        BCS     %FT40                           ; then fail
+        LDMIA   r5!, {r6,r7}                    ; load next address, size
+        SUB     r6, r8, r6, LSR #12             ; number of pages into this bank
+        CMP     r6, r7, LSR #12                 ; if more than there are
+        ADDCS   r10, r10, r7, LSR #12-3         ; then advance CAM entry position
+        BCS     %BT30                           ; and loop to next bank
+
+        ADD     r10, r10, r6, LSL #3            ; advance by 2 words for each page in this bank
+40
+        SUBCS   r10, r9, #8                     ; search from last one, to fail quickly (if CS)
+45
+        Pull    "r5-r8"
+50
+        CMP     r10, r9                         ; if not just done last one,
+        LDRNE   r12, [r10, #8]!                 ; then get logical address
+        TEQNE   r11, r12                        ; compare address
+        BNE     %BT50                           ; loop if not same and not at end
+
+; either found page or run out of pages
+
+        TEQ     r11, r12                        ; see if last one matched
+                                                ; (we always load at least one!)
+60
+        LDREQ   r12, [r10, #4]                  ; if match, then r12 = PPL
+        SUBEQ   r10, r10, r14                   ; and page number=(r10-r14)>>3
+        MOVEQ   r10, r10, LSR #3
+
+        MOVNE   r10, #-1                        ; else unknown page number indicator
+        MOVNE   r12, #3                         ; and PPL=3 (no user access)
+
+        STMIA   r0!, {r10-r12}                  ; store all 3 words
+        B       %BT10                           ; and go back for another one
+ |
+
+; Code for non-expanded CAM map version
+
+        Push    "r0, r9, r14"
+        MOV     r14, #0
+        LDR     r9, [r14, #MaxCamEntry]
+        LDR     r14, [r14, #CamEntriesPointer]  ; r14 -> start of cam map
+        ADD     r9, r14, r9, LSL #2             ; r9 -> last entry in cam map
+10
+        LDR     r10, [r0, #0]                   ; r10 = guess page number (or -1)
+        CMP     r10, #-1                        ; if -1 then end of list
+        Pull    "r0, r9, r14", EQ               ; so restore registers
+        ExitSWIHandler EQ                       ; and exit
+
+        LDR     r11, [r0, #4]                   ; r11 = logical address
+        MOV     r11, r11, LSL #4                ; shift up so we can compare with
+                                                ; shifted up addresses
+        ADD     r10, r14, r10, LSL #2           ; form address with 'guess' page
+        CMP     r10, r9                         ; if off end of CAM
+        BHI     %FT15                           ; then don't try to use the guess
+
+        LDR     r12, [r10]                      ; load address+ppl from guessed page
+        TEQ     r11, r12, LSL #4                ; compare address
+        BEQ     %FT30                           ; if equal, then guessed page was OK
+15
+ [ MEMM_Type = "ARM600"
+
+; for now, cheat by looking in L2PT, to see if we can speed things up
+
+        Push    "r5-r8"                         ; need some registers here!
+        LDR     r10, =L2PT
+        CMP     r11, #256*1024*1024*4           ; if address >= 256M
+        BCS     %FT17                           ; then invalid, so search from last one
+
+        MOV     r8, r11, LSR #16                ; r8 = logaddr / 4K
+        LDR     r8, [r10, r8, LSL #2]           ; get page table entry
+        MOV     r8, r8, LSR #12                 ; r8 = physaddr / 4K
+
+        MOV     r5, #VideoPhysAddr
+        SUB     r10, r14, #4
+16
+        CMP     r10, r9                         ; have we run out of RAM banks?
+        BCS     %FT17                           ; then fail
+        LDMIA   r5!, {r6,r7}                    ; load next address, size
+        SUB     r6, r8, r6, LSR #12             ; number of pages into this bank
+        CMP     r6, r7, LSR #12                 ; if more than there are
+        ADDCS   r10, r10, r7, LSR #12-2         ; then advance CAM entry position (bank size must be multiple of 4K)
+        BCS     %BT16                           ; and loop to next bank
+
+        ADD     r10, r10, r6, LSL #2            ; advance by 1 word for each page in this bank
+17
+        SUBCS   r10, r9, #4                     ; search from last one, to fail quickly (if CS)
+        Pull    "r5-r8"
+ |
+        SUB     r10, r14, #4                    ; move pointer to start of CAM -4
+ ]
+
+20
+        CMP     r10, r9                         ; if not just done last one, get
+        LDRNE   r12, [r10, #4]!                 ; address in bits 0..27, PPL in 28..31
+        TEQNE   r11, r12, LSL #4                ; compare address
+        BNE     %BT20                           ; loop if not same and not at end
+
+; either found page or run out of pages
+
+        TEQ     r11, r12, LSL #4                ; see if last one matched
+                                                ; (we always load at least one!)
+30
+        SUBEQ   r10, r10, r14                   ; if match, then
+        MOVEQ   r10, r10, LSR #2                ; page number=(r10-r14)>>2
+        MOVEQ   r12, r12, LSR #28               ; and PPL=r12>>28
+
+        MOVNE   r10, #-1                        ; else unknown page number indicator
+        MOVNE   r12, #3                         ; and PPL=3 (no user access)
+
+        MOV     r11, r11, LSR #4                ; restore r11 to original value
+
+        STMIA   r0!, {r10-r12}                  ; store all 3 words
+        B       %BT10                           ; and go back for another one
+ ]
+
+;**************************************************************************
+; SWI SetMemMapEntries: R0 pointer to list of CAM page/address/PPL triads,
+;  terminated by -1.
+; Any address > 32M means "put the page out of the way"
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+SetMemMapEntries_Code  ROUT
+        Push    "r0-r6, r9, lr"
+        MOV     r12, r0
+
+; BangCamUpdate takes entry no in r2, logaddr to set to in r3
+; r9 current MEMC, r11 = PPL
+; corrupts r0,r1,r4,r6
+
+        MOV     r9, #0
+        LDR     r5, [r9, #MaxCamEntry]
+        LDR     r9, [r9, #MEMC_CR_SoftCopy]
+01
+        LDR     r2, [r12], #4
+        CMP     r2, r5
+        BHI     %FT02                   ; finished
+        LDMIA   r12!, {r3, r11}
+        AND     r11, r11, #3
+ [ MEMM_Type = "ARM600"
+        CMP     r3, #-1
+ |
+        CMP     r3, #32*1024*1024
+ ]
+        LDRHS   r3, =DuffEntry
+        MOVHS   r11, #AP_Duff
+        BL      BangCamUpdate
+        B       %BT01
+02
+        Pull    "r0-r6, r9, lr"
+        ExitSWIHandler
+
+        LTORG
+
+ [ :LNOT: NewCDA
+InitDynamicAreas        MOV     pc, lr          ; if not new world then nothing to initialise
+ ]
+
+ [ NewCDA
+
+;**************************************************************************
+;
+;       DynamicAreaSWI - Code to handle SWI OS_DynamicArea
+;
+; in:   r0 = reason code
+;       Other registers depend on reason code
+;
+; out:  Depends on reason code
+;
+
+DAReason_Create *       0
+DAReason_Remove *       1
+DAReason_GetInfo *      2
+DAReason_Enumerate *    3
+DAReason_Renumber *     4
+DAReason_Limit  *       5
+
+DynArea_NewAreas *      &100            ; Allocated area numbers start here
+DynArea_NewAreasBase *  &04000000       ; Allocated area addresses start here
+
+; Bits in dynamic area flags (and page flags)
+
+DynAreaFlags_APBits     *       15 :SHL: 0      ; currently only uses 2 bits, but may extend to allow svc/usr read-only
+DynAreaFlags_NotBufferable *    1 :SHL: 4
+DynAreaFlags_NotCacheable *     1 :SHL: 5
+DynAreaFlags_DoublyMapped *     1 :SHL: 6
+DynAreaFlags_NotUserDraggable * 1 :SHL: 7
+ [ DAF_SpecifyBit
+DynAreaFlags_NeedsSpecificPages *  1 :SHL: 8    ; whether area will ever require particular physical pages
+ ]
+DynAreaFlags_AccessMask *       DynAreaFlags_APBits :OR: DynAreaFlags_NotBufferable :OR: DynAreaFlags_NotCacheable :OR: DynAreaFlags_DoublyMapped
+
+; The following bits are only present in page flags
+
+TempUncacheableShift    *       9
+PageFlags_TempUncacheableBits   * 15 :SHL: TempUncacheableShift    ; temporary count of uncacheability, used by DMA mgr
+PageFlags_Unavailable   *       1 :SHL: 13      ; physical page may not be requested by a PreShrink handler
+
+; Temporary flags only used by kernel
+
+PageFlags_Required      *       1 :SHL: 14      ; physical page asked for by handler
+
+DynamicAreaSWI ENTRY
+        BL      DynAreaSub
+        PullEnv
+        ORRVS   lr, lr, #V_bit
+        ExitSWIHandler
+
+DynAreaSub
+        CMP     r0, #DAReason_Limit
+        ADDCC   pc, pc, r0, LSL #2
+        B       DynArea_Unknown
+        B       DynArea_Create
+        B       DynArea_Remove
+        B       DynArea_GetInfo
+        B       DynArea_Enumerate
+        B       DynArea_Renumber
+
+; unknown OS_DynamicArea reason code
+
+DynArea_Unknown
+        ADRL    r0, ErrorBlock_HeapBadReason
+DynArea_TranslateAndReturnError
+      [ International
+        Push    lr
+        BL      TranslateError
+        Pull    lr
+      ]
+DynArea_ReturnError
+        SETV
+        MOV     pc, lr
+
+;**************************************************************************
+;
+;       DynArea_Create - Create a dynamic area
+;
+;       Internal routine called by DynamicAreaSWI and by reset code
+;
+; in:   r0 = reason code (0)
+;       r1 = new area number, or -1 => RISC OS allocates number
+;       r2 = initial size of area (in bytes)
+;       r3 = base logical address of area, or -1 => RISC OS allocates address space
+;       r4 = area flags
+;               bits 0..3 = access privileges
+;               bit  4 = 1 => not bufferable
+;               bit  5 = 1 => not cacheable
+;               bit  6 = 0 => area is singly mapped
+;                      = 1 => area is doubly mapped
+;               bit  7 = 1 => area is not user draggable in TaskManager window
+;               bits 8..31 = 0 (bits 8..12 are used in page flags, but not for areas)
+;
+;       r5 = maximum size of area, or -1 for total RAM size
+;       r6 -> area handler routine
+;       r7 = workspace pointer for area handler (-1 => use base address)
+;       r8 -> area description string (null terminated) (gets copied)
+;
+; out:  r1 = given or allocated area number
+;       r3 = given or allocated base address of area
+;       r5 = given or allocated maximum size
+;       r0, r2, r4, r6-r9 preserved
+;       r10-r12 may be corrupted
+;
+
+DynArea_Create ENTRY "r2,r6-r8"
+        CMP     r1, #-1         ; do we have to allocate a new area number
+        BEQ     %FT10
+
+        BL      CheckAreaNumber ; see if area number is unique
+        BCC     %FT20           ; didn't find it, so OK
+
+        ADR     r0, ErrorBlock_AreaAlreadyExists
+DynArea_ErrorTranslateAndExit
+        PullEnv
+        B       DynArea_TranslateAndReturnError
+
+        MakeErrorBlock  AreaAlreadyExists
+        MakeErrorBlock  AreaNotOnPageBdy
+        MakeErrorBlock  OverlappingAreas
+        MakeErrorBlock  CantAllocateArea
+        MakeErrorBlock  CantAllocateLevel2
+        MakeErrorBlock  UnknownAreaHandler
+
+; we have to allocate an area number for him
+
+10
+        MOV     r1, #DynArea_NewAreas
+12
+        BL      CheckAreaNumber
+        ADDCS   r1, r1, #1      ; that area number already exists, so increment
+        BCS     %BT12           ; and try again
+20
+
+; now validate maximum size of area
+
+        MOV     r10, #0
+        LDR     r11, [r10, #Page_Size]
+        LDR     r10, [r10, #RAMLIMIT]   ; get total RAM size
+        CMP     r5, r10                 ; if requested maximum size is > total
+        MOVHI   r5, r10                 ; then set max to total (NB. -1 passed in always yields HI)
+
+        SUB     r10, r11, #1            ; also round up to a page multiple
+        ADD     r5, r5, r10
+        BIC     r5, r5, r10
+
+; now see if we have to allocate a logical address space
+
+        CMP     r3, #-1                 ; if we are to allocate the address space
+        BEQ     %FT30                   ; then go do it
+
+; otherwise we must check that the address does not clash with anything else
+
+        TST     r3, r10                         ; does it start on a page boundary
+        ADRNE   r0, ErrorBlock_AreaNotOnPageBdy ; if not then error
+        BNE     DynArea_ErrorTranslateAndExit
+
+        BL      CheckForOverlappingAreas        ; in: r3 = address, r4 = flags, r5 = size; out: if error, r0->error, V=1
+        BVC     %FT40
+25
+        PullEnv
+        B       DynArea_ReturnError
+
+30
+        BL      AllocateAreaAddress             ; in: r4 = flags, r5 = size of area needed; out: r3, or V=1, r0->error
+        BVS     %BT25
+40
+        BL      AllocateBackingLevel2           ; in: r3 = address, r4 = flags, r5 = size; out: VS if error
+        BVS     %BT25
+
+        Push    "r0,r1,r3"
+        MOV     r3, #DANode_NodeSize
+        BL      ClaimSysHeapNode                ; out: r2 -> node
+        STRVS   r0, [sp]
+        Pull    "r0,r1,r3"
+        BVS     %BT25                           ; failed to claim node
+
+; now store data in node (could probably use STM if we shuffled things around)
+
+        CMP     r7, #-1                         ; if workspace ptr = -1
+        MOVEQ   r7, r3                          ; then use base address
+
+        STR     r1, [r2, #DANode_Number]
+        STR     r3, [r2, #DANode_Base]
+        STR     r4, [r2, #DANode_Flags]
+        STR     r5, [r2, #DANode_MaxSize]
+        STR     r6, [r2, #DANode_Handler]
+        STR     r7, [r2, #DANode_Workspace]
+        MOV     r7, #0                          ; initial size is zero
+        STR     r7, [r2, #DANode_Size]          ; before we grow it
+
+; now make copy of string - first find out length of string
+
+        MOV     r7, r8
+45
+        LDRB    r6, [r7], #1
+        TEQ     r6, #0
+        BNE     %BT45
+
+        Push    "r0-r3"
+        SUB     r3, r7, r8                      ; r3 = length inc. term.
+        BL      ClaimSysHeapNode
+        STRVS   r0, [sp]
+        MOV     r7, r2
+        Pull    "r0-r3"
+        BVS     StringNodeClaimFailed
+
+        STR     r7, [r2, #DANode_Title]
+50
+        LDRB    r6, [r8], #1                    ; copy string into claimed block
+        STRB    r6, [r7], #1
+        TEQ     r6, #0
+        BNE     %BT50
+
+; now put node on list - list is sorted in ascending base address order
+
+        MOV     r8, #DAList
+        LDR     r6, [r2, #DANode_Base]
+60
+        MOV     r7, r8
+        ASSERT  DANode_Link = 0
+        LDR     r8, [r7, #DANode_Link]          ; get next node
+        TEQ     r8, #0                          ; if no more
+        BEQ     %FT70                           ; then put it on here
+        LDR     lr, [r8, #DANode_Base]
+        CMP     lr, r6                          ; if this one is before ours
+        BCC     %BT60                           ; then loop
+
+70
+        STR     r8, [r2, #DANode_Link]
+        STR     r2, [r7, #DANode_Link]
+
+; now we need to grow the area to its requested size
+
+        Push    "r0, r1"
+        LDR     r0, [r2, #DANode_Number]
+        LDR     r1, [sp, #2*4]                  ; reload requested size off stack
+        SWI     XOS_ChangeDynamicArea
+        BVS     %FT90
+
+; Now issue service to tell TaskManager about it
+
+        MOV     r2, r0                          ; r2 = area number
+        MOV     r1, #Service_DynamicAreaCreate
+        BL      Issue_Service
+        Pull    "r0, r1"
+
+        CLRV
+        EXIT
+
+90
+
+; The dynamic area is not being created, because we failed to grow the area to the required size.
+; The area itself will have no memory allocated to it (since if grow fails it doesn't move any).
+; We must delink the node from our list, free the string node, and then the area node itself.
+
+        STR     r0, [sp, #0*4]                  ; remember error pointer in stacked r0
+        STR     r8, [r7, #DANode_Link]          ; delink area
+        MOV     r6, r2                          ; save pointer to DANode itself
+        LDR     r2, [r6, #DANode_Title]
+        BL      FreeSysHeapNode                 ; free title string node
+        MOV     r2, r6                          ; point r2 back at DANode
+        Pull    "r0, r1"                        ; pull stacked registers, and drop thru to...
+
+; The dynamic area is not being created, because there is no room to allocate space for the title string
+; We must free the DANode we have allocated
+; It would be nice to also free the backing L2, but we'll leave that for now.
+
+; in: r2 -> DANode
+
+StringNodeClaimFailed
+        Push    "r0, r1"
+        BL      FreeSysHeapNode
+        Pull    "r0, r1"
+        PullEnv
+        B       DynArea_ReturnError
+
+;**************************************************************************
+;
+;       DynArea_Remove - Remove a dynamic area
+;
+;       Internal routine called by DynamicAreaSWI
+;
+; in:   r0 = reason code (1)
+;       r1 = area number
+;
+; out:  r10-r12 may be corrupted
+;       All other registers preserved
+;
+
+DynArea_Remove ENTRY
+        BL      CheckAreaNumber         ; check that area is there
+        BCC     UnknownDyn              ; [not found]
+
+; First try to shrink area to zero size
+
+        Push    "r0-r2"
+        MOV     r0, r1                  ; area number
+        LDR     r2, [r10, #DANode_Size] ; get current size
+        RSB     r1, r2, #0              ; negate it
+        SWI     XOS_ChangeDynamicArea
+        BVS     %FT80
+        STRVS   r0, [sp]
+        Pull    "r0-r2"
+        EXIT    VS
+
+; Issue service to tell TaskManager
+
+        Push    "r1, r2"
+        MOV     r2, r1
+        MOV     r1, #Service_DynamicAreaRemove
+        BL      Issue_Service
+        Pull    "r1, r2"
+
+; Now just de-link from list (r10 -> node, r11 -> prev)
+
+        LDR     lr, [r10, #DANode_Link] ; store our link
+        STR     lr, [r11, #DANode_Link] ; in prev link
+
+        Push    "r0-r2"
+        LDR     r2, [r10, #DANode_Title]        ; free title string block
+        BL      FreeSysHeapNode
+        MOV     r2, r10                         ; and free node block
+        BL      FreeSysHeapNode
+        Pull    "r0-r2"
+        CLRV
+        EXIT
+
+; come here if shrink failed - r0-r2 stacked
+
+80
+        STR     r0, [sp]                ; overwrite stacked r0 with error pointer
+        LDR     r0, [sp, #1*4]          ; reload area number
+        LDR     r1, [r10, #DANode_Size] ; get size after failed shrink
+        SUB     r1, r2, r1              ; change needed to restore original size
+        SWI     XOS_ChangeDynamicArea   ; ignore any error from this
+        SETV
+        EXIT
+
+UnknownDyn
+        ADRL    r0, ErrorBlock_BadDynamicArea
+ [ International
+        BL      TranslateError
+ ]
+90
+        SETV
+        EXIT
+
+;**************************************************************************
+;
+;       DynArea_GetInfo - Get info on a dynamic area
+;
+;       Internal routine called by DynamicAreaSWI
+;
+; in:   r0 = reason code (2)
+;       r1 = area number
+;
+; out:  r2 = current size of area
+;       r3 = base logical address
+;       r4 = area flags
+;       r5 = maximum size of area
+;       r6 -> area handler routine
+;       r7 = workspace pointer
+;       r8 -> title string
+;       r10-r12 may be corrupted
+;       All other registers preserved
+;
+
+DynArea_GetInfo ALTENTRY
+        BL      CheckAreaNumber         ; check area exists
+        BCC     UnknownDyn              ; [it doesn't]
+
+; r10 -> node, so get info
+
+        LDR     r2, [r10, #DANode_Size]
+        LDR     r3, [r10, #DANode_Base]
+        LDR     r4, [r10, #DANode_Flags]
+        LDR     r5, [r10, #DANode_MaxSize]
+        LDR     r6, [r10, #DANode_Handler]
+        LDR     r7, [r10, #DANode_Workspace]
+        LDR     r8, [r10, #DANode_Title]
+        CLRV
+        EXIT
+
+;**************************************************************************
+;
+;       DynArea_Enumerate - Enumerate dynamic areas
+;
+;       Internal routine called by DynamicAreaSWI
+;
+; in:   r0 = reason code (3)
+;       r1 = -1 to start enumeration, or area number to continue from
+;
+; out:  r1 = next area number or -1 if no next
+;       r10-r12 may be corrupted
+;       All other registers preserved
+
+DynArea_Enumerate ALTENTRY
+        CMP     r1, #-1                         ; if starting from beginning
+        LDREQ   r10, [r1, #DAList+1]            ; then load pointer to 1st node
+        BEQ     %FT10                           ; and skip
+
+        BL      CheckAreaNumber                 ; else check valid area number
+        BCC     UnknownDyn                      ; complain if passed in duff area number
+
+        LDR     r10, [r10, #DANode_Link]        ; find next one
+10
+        TEQ     r10, #0                         ; if at end
+        MOVEQ   r1, #-1                         ; then return -1
+        LDRNE   r1, [r10, #DANode_Number]       ; else return number
+        CLRV
+        EXIT
+
+;**************************************************************************
+;
+;       DynArea_Renumber - Renumber dynamic area
+;
+;       Internal routine called by DynamicAreaSWI
+;
+; in:   r0 = reason code (4)
+;       r1 = old area number
+;       r2 = new area number
+;
+
+DynArea_Renumber ALTENTRY
+        BL      CheckAreaNumber                 ; check valid area number
+        BCC     UnknownDyn                      ; [it's not]
+
+        Push    "r1"
+        MOV     r12, r10                        ; save pointer to node
+        MOV     r1, r2
+        BL      CheckAreaNumber                 ; check area r2 doesn't already exist
+        Pull    "r1"
+        BCS     %FT90                           ; [area r2 already exists]
+
+        STR     r2, [r12, #DANode_Number]
+
+; Now issue service to tell TaskManager
+
+        Push    "r1-r3"
+        MOV     r3, r2                          ; new number
+        MOV     r2, r1                          ; old number
+        MOV     r1, #Service_DynamicAreaRenumber
+        BL      Issue_Service
+        Pull    "r1-r3"
+
+        CLRV
+        EXIT
+
+90
+        ADRL    r0, ErrorBlock_AreaAlreadyExists
+ [ International
+        BL      TranslateError
+ ]
+        SETV
+        EXIT
+
+;**************************************************************************
+;
+;       CheckAreaNumber - Try to find area with number r1
+;
+;       Internal routine called by DynArea_Create
+;
+; in:   r1 = area number to match
+; out:  If match, then
+;         C=1, r10 -> node, r11 -> previous node
+;       else
+;         C=0, r10,r11 corrupted
+;       endif
+
+CheckAreaNumber ENTRY
+        MOV     r10, #DAList
+        ASSERT  DANode_Link = 0                 ; because DAList has only link
+10
+        MOV     r11, r10                        ; save prev
+        LDR     r10, [r10, #DANode_Link]        ; and load next
+        CMP     r10, #1                         ; any more nodes?
+        EXIT    CC                              ; no, then no match
+        LDR     lr, [r10, #DANode_Number]       ; get number
+        CMP     lr, r1                          ; does number match
+        BNE     %BT10                           ; no, try next
+        EXIT                                    ; (C=1 from CMP lr,r1)
+
+;**************************************************************************
+;
+;       CheckForOverlappingAreas - Check that given area does not overlap any existing ones
+;
+;       Internal routine called by DynArea_Create
+;
+; in:   r3 = base address
+;       r4 = area flags (NB if doubly mapped, then have to check both halves for overlap)
+;       r5 = size (of each half in doubly mapped areas)
+;
+; out:  If this area overlaps with an existing one, then
+;         r0 -> error
+;         V=1
+;       else
+;         r0 preserved
+;         V=0
+;       endif
+;
+
+CheckForOverlappingAreas ENTRY "r0-r5"
+        TST     r4, #DynAreaFlags_DoublyMapped          ; check if doubly mapped
+        BEQ     %FT05                                   ; [not, so don't mangle]
+
+        SUBS    r3, r3, r5                              ; move start address back
+        BCC     %FT20                                   ; oh dear! - it went back to below 0
+        MOVS    r5, r5, LSL #1                          ; and double size
+        BCS     %FT20                                   ; if that wrapped then that's bad, too
+05
+        ADDS    r5, r5, r3                              ; r5 -> end +1
+        BHI     %FT20                                   ; if CS, indicating wrap, and not EQ (ie just ending at 0), then bad
+
+; First, check against list of fixed areas
+
+        ADR     lr, FixedAreasTable
+10
+        LDMIA   lr!, {r0, r1}                           ; r0 = start addr, r1 = size
+        CMP     r0, #-1                                 ; if at end of list
+        BEQ     %FT30                                   ; then OK wrt fixed areas
+        ADD     r1, r1, r0                              ; r1 = end addr+1
+        CMP     r5, r0                                  ; if end of our area is <= start of fixed, then OK wrt fixed areas
+        BLS     %FT30
+        CMP     r3, r1                                  ; if start of our area is >= end of fixed, then go onto next area
+        BCS     %BT10
+
+20
+        ADRL    r0, ErrorBlock_OverlappingAreas
+ [ International
+        BL      TranslateError
+ ]
+        STR     r0, [sp]
+        SETV
+        EXIT
+
+; Now, check against DAList
+
+30
+        MOV     lr, #DAList
+        ASSERT  DANode_Link = 0
+40
+        LDR     lr, [lr, #DANode_Link]
+        CMP     lr, #0                                  ; if got to end of list (V=0)
+        BEQ     %FT50                                   ; then exit saying OK
+        LDR     r0, [lr, #DANode_Base]
+        LDR     r1, [lr, #DANode_Flags]
+        TST     r1, #DynAreaFlags_DoublyMapped
+        LDR     r1, [lr, #DANode_MaxSize]
+        SUBNE   r0, r0, r1                              ; if doubly mapped then move back
+        MOVNE   r1, r1, LSL #1                          ; and double size
+        ADD     r1, r1, r0                              ; r1 -> end
+        CMP     r5, r0                                  ; if end of our area is <= start of dyn, then OK wrt dyn areas)
+        BLS     %FT50
+        CMP     r3, r1                                  ; if start of our area is >= end of dyn, then go onto next area
+        BCS     %BT40
+        B       %BT20                                   ; else it overlaps
+
+50
+        CLRV                                            ; OK exit
+        EXIT
+
+
+FixedAreasTable                                         ; table of fixed areas (address, size)
+        &       0,                      AplWorkMaxSize  ; application space
+ [ :LNOT: NewStyle_RMA
+        &       RMAAddress,             RMAMaxSize      ; RMA (to be removed from here eventually)
+ ]
+ [ :LNOT: NewStyle_SysHeap
+        &       SysHeapChunkAddress,    SysHeapMaxSize+SVCStackSize   ; system heap (to be removed eventually)
+ ]
+        &       UndStackSoftCamChunk,   1024*1024               ; undefined stack / soft cam map
+        &       CursorChunkAddress,     64*1024                 ; 32K for cursor, 32K for "nowhere"
+        &       L2PT,                   4*1024*1024             ; L2PT (and L1PT)
+        &       &03000000,              16*1024*1024            ; I/O + ROM
+ [ :LNOT: NewStyle_Screen
+        &       ScreenEndAdr-16*1024*1024, 32*1024*1024         ; Screen (removable later)
+ ]
+ [ :LNOT: NewStyle_FontArea
+        &       FontCacheAddress,      FontCacheMaxSize ; Font cache (removable later)
+ ]
+ [ :LNOT: NewStyle_SpriteArea
+        &       SpriteSpaceAddress,  SpriteSpaceMaxSize ; Sprite area (removable later)
+ ]
+ [ :LNOT: NewStyle_RAMDisc
+        &       RAMDiscAddress,         RAMDiscMaxSize  ; RAM disc (removable later)
+ ]
+        &       PhysSpace,              512*1024*1024   ; PhysSpace
+        &       &FF800000,              &007FFFFF       ; Shadow ROM (length has been bodged to avoid wrap problems)
+        &       -1,                     0               ; termination
+
+;**************************************************************************
+;
+;       AllocateAreaAddress - Find an area of logical space to use for this area
+;
+;       Internal routine called by DynArea_Create
+;
+; in:   r4 = area flags (NB if doubly mapped, we have to find space for both halves)
+;       r5 = size (of each half in doubly mapped areas)
+;
+; out:  If successfully found an address, then
+;         r0 preserved
+;         r3 = logical address
+;         V=0
+;       else
+;         r0 -> error
+;         r3 preserved
+;         V=1
+;       endif
+
+AllocateAreaAddress ENTRY "r0-r2,r4-r7"
+        TST     r4, #DynAreaFlags_DoublyMapped          ; check if doubly mapped
+        BEQ     %FT05                                   ; [not, so don't mangle]
+        MOVS    r5, r5, LSL #1                          ; double size
+        BCS     %FT90                                   ; if that wrapped then that's bad
+05
+        LDR     r3, =DynArea_NewAreasBase               ; r3 is our current attempt
+        ADR     r0, FixedAreasTable                     ; r0 is ptr into fixed areas table
+        MOV     r1, #DAList                             ; r1 is ptr into dyn areas list
+10
+        ADDS    r7, r3, r5                              ; r7 is our end+1
+        BHI     %FT90                                   ; if we wrapped (but not end+1=0) then we failed
+        BL      GetNextRange                            ; get next range from either list (r2=start, r6=end+1)
+        CMP     r7, r2                                  ; if end(ours) <= start(next) then this is OK
+        BLS     %FT80                                   ; (note this also works when r2=-1)
+        CMP     r3, r6                                  ; else if start(ours) >= end(next)
+        BCS     %BT10                                   ; then get another
+        MOV     r3, r6                                  ; else make start(ours) := end(next)
+        B       %BT10                                   ; and go back for another try
+
+; we've succeeded - just apply unbodge for doubly-mapped areas
+
+80
+        TST     r4, #DynAreaFlags_DoublyMapped          ; if doubly mapped
+        MOVNE   r5, r5, LSR #1                          ; halve size again
+        ADDNE   r3, r3, r5                              ; and advance base address to middle
+        CLRV
+        EXIT
+
+90
+        ADRL    r0, ErrorBlock_CantAllocateArea
+  [ International
+        BL      TranslateError
+  ]
+        STR     r0, [sp]
+        SETV
+        EXIT                                    ; say we can't do it
+
+;**************************************************************************
+;
+;       GetNextRange - Get next lowest range from either fixed or dynamic list
+;
+;       Internal routine called by AllocateAreaAddress
+;
+; in:   r0 -> next entry in fixed list
+;       r1!0 -> next entry in dyn list
+;
+; out:  r2 = next lowest area base (-1 if none)
+;       r6 = end of that range (undefined if none)
+;       Either r0 or r1 updated to next one (except when r2=-1 on exit)
+;
+
+GetNextRange ENTRY "r7,r8"
+        LDMIA   r0, {r2, r6}                            ; load start, size from fixed list
+        ADD     r6, r6, r2                              ; r6 = end+1
+
+        ASSERT  DANode_Link = 0
+        LDR     r7, [r1, #DANode_Link]                  ; get next from dyn
+        TEQ     r7, #0                                  ; if none
+        MOVEQ   r8, #-1                                 ; then use addr -1
+        BEQ     %FT10
+
+        LDR     r8, [r7, #DANode_Flags]                 ; more double trouble
+        TST     r8, #DynAreaFlags_DoublyMapped
+        LDR     r8, [r7, #DANode_Base]
+        LDR     lr, [r7, #DANode_MaxSize]
+        SUBNE   r8, r8, lr
+        MOVNE   lr, lr, LSL #1
+        ADD     lr, lr, r8                              ; now r8 = start addr, lr = end+1
+10
+        CMP     r8, r2                                  ; if dyn one is earlier
+        MOVCC   r2, r8                                  ; then use dyn start
+        MOVCC   r6, lr                                  ; and end
+        MOVCC   r1, r7                                  ; and advance dyn ptr
+        EXIT    CC                                      ; then exit
+        CMP     r2, #-1                                 ; else if not at end of fixed
+        ADDNE   r0, r0, #8                              ; then advance fixed ptr
+        EXIT
+
+;**************************************************************************
+;
+;       AllocateBackingLevel2 - Allocate L2 pages for an area
+;
+;       Internal routine called by DynArea_Create
+;
+; in:   r3 = base address (will be page aligned)
+;       r4 = area flags (NB if doubly mapped, then have to allocate for both halves)
+;       r5 = size (of each half in doubly mapped areas)
+;
+; out:  If successfully allocated pages, then
+;         All registers preserved
+;         V=0
+;       else
+;         r0 -> error
+;         V=1
+;       endif
+
+AllocateBackingLevel2 ENTRY "r0-r8,r11"
+        TST     r4, #DynAreaFlags_DoublyMapped          ; if doubly mapped
+        SUBNE   r3, r3, r5                              ; then area starts further back
+        MOVNE   r5, r5, LSL #1                          ; and is twice the size
+
+; NB no need to do sanity checks on addresses here, they've already been checked
+
+; now round address range to 4M boundaries
+
+        ADD     r5, r5, r3                              ; r5 -> end
+        MOV     r0, #1 :SHL: 22
+        SUB     r0, r0, #1
+        BIC     r8, r3, r0                              ; round start address down (+ save for later)
+        ADD     r5, r5, r0
+        BIC     r5, r5, r0                              ; but round end address up
+
+; first go through existing L2PT working out how much we need
+
+        LDR     r7, =L2PT
+        ADD     r3, r7, r8, LSR #10                     ; r3 -> start of L2PT for area
+        ADD     r5, r7, r5, LSR #10                     ; r5 -> end of L2PT for area +1
+
+        ADD     r1, r7, r3, LSR #10                     ; r1 -> L2PT for r3
+        ADD     r2, r7, r5, LSR #10                     ; r2 -> L2PT for r5
+
+        TEQ     r1, r2                                  ; if no pages needed
+        BEQ     %FT30
+
+        MOV     r4, #0                                  ; number of backing pages needed
+10
+        LDR     r6, [r1], #4                            ; get L2PT entry for L2PT
+        TST     r6, #3                                  ; EQ if translation fault
+        ADDEQ   r4, r4, #1                              ; if not there then 1 more page needed
+        TEQ     r1, r2
+        BNE     %BT10
+
+; if no pages needed, then exit
+
+        TEQ     r4, #0
+        BEQ     %FT30
+
+; now we need to claim r4 pages from the free pool, if possible; return error if not
+
+        MOV     r1, #0
+        LDR     r6, [r1, #FreePoolDANode + DANode_Size]
+        SUBS    r6, r6, r4, LSL #12                     ; reduce free pool size by that many pages
+        BCS     %FT14                                   ; if enough, skip next bit
+
+; not enough pages in free pool currently, so try to grow it by the required amount
+
+        Push    "r0, r1"
+        MOV     r0, #ChangeDyn_FreePool
+        RSB     r1, r6, #0                              ; size change we want (+ve)
+        SWI     XOS_ChangeDynamicArea
+        Pull    "r0, r1"
+        BVS     %FT90                                   ; didn't manage change, so report error
+
+        MOV     r6, #0                                  ; will be no pages left in free pool after this
+14
+        STR     r6, [r1, #FreePoolDANode + DANode_Size] ; if possible then update size
+
+; after that we need to zero all these pages out (=> cause translation fault for area initially)
+
+        LDR     r0, [r1, #FreePoolDANode + DANode_Base] ; r0 -> base of free pool
+        ADD     r0, r0, r6                              ; r0 -> first byte we're taking out of free pool
+        ADD     r6, r0, r4, LSL #12                     ; r6 -> byte after last in free pool
+        Push    r8                                      ; save original logical address
+        MOV     r8, #0                                  ; 0 => translation fault (note r1 already zero)
+        MOV     r11, #0
+        MOV     lr, #0
+15
+        STMIA   r0!, {r1,r8,r11,lr}                     ; store data
+        TEQ     r0, r6
+        BNE     %BT15
+        Pull    r8
+
+; now r0 -> after end of free pool (log addr)
+
+        LDR     lr, =L1PT
+        ADD     r8, lr, r8, LSR #18                     ; point r8 at start of L1 we may be updating
+        ADD     r1, r7, r3, LSR #10                     ; point r1 at L2PT for r3 again
+        MOV     r11, #AP_L2PT                           ; access privs (+CB bits)
+20
+        LDR     r6, [r1], #4                            ; get L2PT entry again
+        TST     r6, #3                                  ; if no fault
+        BNE     %FT25                                   ; then skip
+
+        SUB     r0, r0, #4096                           ; move free pointer back
+        Push    "r2,r4,r5"
+        BL      MoveCAMatR0toR3                         ; else move page from end of free pool to r3
+        Pull    "r2,r4,r5"
+
+; now update 4 words in L1PT (corresponding to 4M of address space which is covered by the 4K of L2)
+; and point them at the physical page we've just allocated (r1!-4 will already hold physical address+bits now!)
+
+        LDR     r6, [r1, #-4]                           ; r6 = physical address for L2 page + other L2 bits
+        MOV     r6, r6, LSR #12                         ; r6 = phys.addr >> 12
+        LDR     lr, =L1_Page + L1_U                     ; form other bits to put in L1
+        ORR     lr, lr, r6, LSL #12                     ; complete L1 entry
+        STR     lr, [r8, #0]                            ; store entry for 1st MB
+        ADD     lr, lr, #1024                           ; advance L2 pointer
+        STR     lr, [r8, #4]                            ; store entry for 2nd MB
+        ADD     lr, lr, #1024                           ; advance L2 pointer
+        STR     lr, [r8, #8]                            ; store entry for 3rd MB
+        ADD     lr, lr, #1024                           ; advance L2 pointer
+        STR     lr, [r8, #12]                           ; store entry for 4th MB
+        SetCop  lr, CR_TLBFlush                         ; junk the TLB (probably not necessary)
+25
+        ADD     r3, r3, #4096                           ; advance L2PT logical address
+        ADD     r8, r8, #16                             ; move onto L1 for next 4M
+
+        TEQ     r1, r2
+        BNE     %BT20
+30
+        CLRV
+        EXIT
+
+        LTORG
+
+; Come here if not enough space in free pool to allocate level2
+
+90
+        ADRL    r0, ErrorBlock_CantAllocateLevel2
+  [ International
+        BL      TranslateError
+  ]
+        STR     r0, [sp]
+        SETV
+        EXIT
+
+;**************************************************************************
+;
+;       InitDynamicAreas - Initialise nodes for dynamic areas
+;
+;       It only initialises free pool, appspace and sysheap nodes
+;       The other areas are created properly, after the screen area has been created (improperly)
+;
+; in:   -
+; out:  -
+;
+
+InitDynamicAreas ENTRY "r0-r8,r11"
+        MOV     lr, #AppSpaceDANode
+        ADR     r0, InitAppSpaceTable
+        LDMIA   r0, {r0-r8}
+        STMIA   lr, {r0-r8}
+
+        MOV     lr, #FreePoolDANode
+        ADR     r0, InitFreePoolTable
+        LDMIA   r0, {r0-r8}                     ; copy initial data into node
+        LDR     r5, [lr, #RAMLIMIT-FreePoolDANode] ; max size is RAMLIMIT
+        STMIA   lr, {r0-r8}
+
+ [ GetPagesFromFreePool
+
+; We have to move all free pages (ie ones not occupied by the static pages) into the free pool
+; The lowest numbered physical pages must be put in last, so that when ReadCMOSAndConfigure is
+; called to put in the screen, it will get the pages starting at 0 (when the screen is created
+; as a dynamic area, this limitation can be removed).
+
+; The free pages consist of two chunks of pages, each of which having consecutive physical page
+; numbers. We start at MaxCamEntry and move back until we hit the end of the statics (which are
+; at the start of the 1st non-video-RAM chunk). We then separately do the video RAM.
+
+        MOV     r11, r3                         ; r11 = PPL (inc CB) for free pool
+        MOV     r3, r2                          ; r3 = base address of free pool, ie where to put stuff
+        MOV     r5, #0                          ; r5 = amount of memory in free pool so far (and ptr to 0)
+        LDR     r2, [r5, #MaxCamEntry]          ; r2 = 1st page to put in free pool
+        LDR     r7, [r5, #VideoSize]            ; r7 = size of video RAM
+        MOV     r7, r7, LSR #12                 ; r7 = page number of start of static chunk
+        ASSERT  SoftCamMapSize = L2PTSize +4
+        MOV     r0, #L2PTSize
+        LDMIA   r0, {r0, r8}                    ; r0 = L2PTSize, r8 = SoftCamMapSize
+        ADD     r8, r8, r0                      ; add sizes together
+        ADD     r8, r8, #StaticPagesSize + UndStackSize ; + number of bytes used for other static bits
+        ADD     r8, r7, r8, LSR #12             ; r8 = page number of 1st page in 1st chunk not used for statics
+10
+        CMP     r2, r8                          ; are we into statics already
+        SUBCC   r2, r7, #1                      ; if so, then move to last page of video RAM
+        MOVCC   r8, #0                          ; and move barrier so we never hit it again
+        BL      BangCamUpdate
+        ADD     r3, r3, #4096                   ; advance logical address
+        ADD     r5, r5, #4096
+        SUBS    r2, r2, #1                      ; decrement page number
+        BCS     %BT10                           ; if we haven't gone negative, then loop
+
+        MOV     lr, #FreePoolDANode             ; may be used below to update DAList head ptr
+        STR     r5, [lr, #DANode_Size]          ; update size of free pool in node
+
+; Now initialise the system heap by hand, so we can start creating dynamic areas
+
+        LDR     r0, =SysHeapStart
+        LDR     r1, =magic_heap_descriptor
+        MOV     r2, #Nil
+        MOV     r3, #hpdsize
+        MOV     r4, #32*1024 - (SysHeapStart-SysHeapChunkAddress)
+        STMIA   r0, {r1-r4}
+
+        MOV     r0, #0                          ; initialise module list to empty
+        STR     r0, [r0, #Module_List]
+ ]
+ [ NewStyle_SysHeap
+        MOV     lr, #SysHeapDANode              ; initialise system heap node
+        ADR     r0, InitSysHeapTable
+        LDMIA   r0, {r0-r8}
+        STMIA   lr, {r0-r8}
+ ]
+        MOV     r0, #0
+        STR     lr, [r0, #DAList]               ; store pointer to 1st node on list (either free pool or sys heap)
+
+ [ NewStyle_All
+        STR     r0, [r0, #CDASemaphore]         ; clear CDASemaphore
+ ]
+
+        EXIT
+
+InitFreePoolTable
+        &       0                               ; link: no more nodes on list
+        &       ChangeDyn_FreePool
+        &       FreePoolAddress
+        &       AP_FreePool
+        &       0                               ; size will be updated later
+        &       0                               ; max size is computed
+        &       0                               ; no workspace needed
+        &       0                               ; no handler needed
+        &       FreePoolString                  ; title
+
+InitSysHeapTable
+        &       FreePoolDANode                  ; link -> free pool node, since FreePoolAddress > SysHeapStart
+        &       ChangeDyn_SysHeap
+        &       SysHeapStart
+        &       AP_SysHeap
+        &       32*1024-(SysHeapStart-SysHeapChunkAddress) ; size
+        &       SysHeapMaxSize
+        &       SysHeapStart                    ; workspace pointer -> base of heap
+        &       DynAreaHandler_SysHeap          ; area handler
+        &       SysHeapString                   ; title
+
+InitAppSpaceTable
+        &       0                               ; link: not on list
+        &       ChangeDyn_AplSpace
+        &       0                               ; base address
+        &       AP_AppSpace
+        &       0                               ; size will be set up later
+        &       AplWorkMaxSize
+        &       0                               ; no workspace needed
+        &       0                               ; no handler needed
+        &       AppSpaceString                  ; title
+
+FreePoolString
+        =       "Free pool", 0
+AppSpaceString
+        =       "Application space", 0
+SysHeapString
+        =       "System heap", 0
+        ALIGN
+
+ ]
+
+ [ NewCDA2
+  [ NewStyle_All
+ChangeDynamicSWI ROUT
+        Push    "r0, r2-r9, r10, lr"
+
+; and drop thru to ...
+  ]
+
+;**************************************************************************
+;
+;       CheckForNewArea - Perform operation of OS_ChangeDynamicArea, if
+;                         area is on new list
+;
+; in:   r0 = area number
+;       r1 = size of change
+;       stack: r0,r2-r9,r10,lr
+;
+; out:  If not on list, then return to old routine at IsOldArea
+;       Else perform operation and exit ourselves
+;
+
+CheckForNewArea ROUT
+        MOV     r10, #0                         ; check we're not in an IRQ
+        LDR     r10, [r10, #IRQsema]
+        TEQ     r10, #0
+ [ NewStyle_All
+        LDREQ   r10, [r10, #CDASemaphore]       ; now also check whether ChangeDynamicArea is already threaded
+        TEQEQ   r10, #0
+        BNE     failure_IRQgoing
+        MOV     r10, #1
+        STR     r10, [r10, #CDASemaphore-1]     ; store non-zero value in CDASemaphore, to indicate we're threaded
+ |
+        BNE     failure_IRQgoing
+ ]
+
+ [ DebugCDA2
+        DLINE   "Entering OS_ChangeDynamicArea (new code)"
+ ]
+
+        Push    "r1"
+        MOV     r1, r0
+ [ DebugCDA2
+        DREG    r1, "Checking list for area number "
+ ]
+        BL      CheckAreaNumber                 ; check area number is on list
+        Pull    "r1"
+ [ NewStyle_All
+        BCC     failure_IRQgoingClearSemaphore
+ |
+        BCC     IsOldArea                       ; if not, then use old code
+ ]
+
+ [ DebugCDA2
+        DLINE   "Found entry on list"
+ ]
+
+        MOV     r5, #0
+        LDR     r5, [r5, #Page_Size]            ; r5 = page size throughout
+        SUB     r12, r5, #1                     ; r12 = page mask
+        ADD     r1, r1, r12
+        BICS    r1, r1, r12
+        BEQ     IssueServiceMemoryMoved         ; zero pages! (r0 = area number, r1 = size change (0))
+        BPL     AreaGrow
+
+AreaShrink
+        RSB     r1, r1, #0                      ; make size change positive
+ [ DebugCDA2
+        DREG    r0, "Shrinking area ", cc
+        DREG    r1, " by "
+ ]
+        MOV     r11, r10                        ; source is area
+        CMP     r0, #ChangeDyn_FreePool         ; if source is free pool
+        ADREQ   r12, AppSpaceDANode             ; then dest is appspace
+        ADRNE   r12, FreePoolDANode             ; else dest is free pool
+
+        ASSERT  DANode_MaxSize = DANode_Size +4
+        ADD     r2, r12, #DANode_Size
+        LDMIA   r2, {r2, r3}
+        SUB     lr, r3, r2                      ; lr = amount dest could grow
+
+        LDR     r2, [r11, #DANode_Size]         ; amount src could shrink
+        CMP     r2, lr
+        MOVCC   lr, r2                          ; lr = min(amount dest could grow, amount src could shrink)
+
+        CMP     r1, lr
+        BLS     %FT15
+
+; we can't move all that is required, so move smaller amount
+
+        MOV     r1, lr                          ; move smaller amount
+        BL      GenNotAllMovedError
+        SUB     lr, r5, #1                      ; lr = pagesize mask
+        BICS    r1, r1, lr                      ; a pagesize multiple
+        BEQ     IssueServiceMemoryMoved
+15
+        CMP     r11, #AppSpaceDANode            ; if src <> appspace
+        CMPNE   r12, #AppSpaceDANode            ; and dst <> appspace
+        BNE     %FT17                           ; then don't call app
+        Push    "r10"                           ; save -> to area we tried to shrink
+        MOV     r10, r1
+        BL      CheckAppSpace
+        Pull    "r10"
+        BVS     ChangeDynError
+17
+        BL      CallPreShrink
+        BVS     ChangeDynError                  ; (r10 still points to area we tried to shrink)
+        CMP     r2, r1                          ; can we move as much as we wanted?
+        MOVCS   r2, r1                          ; if not, then move lesser amount (r2 = amount we're moving)
+        BLCC    GenNotAllMovedError             ; store error, but continue
+
+        TEQ     r2, #0                          ; if can't move any pages
+        BEQ     NoMemoryMoved                   ; then exit, issuing Service_MemoryMoved
+
+; Now move pages starting from end of area
+
+        LDR     r0, [r11, #DANode_Base]
+        LDR     r3, [r11, #DANode_Size]
+        LDR     r6, [r11, #DANode_Flags]        ; r6 = src flags
+        Push    "r3, r6"                        ; save src old size, src flags for later
+        TST     r6, #DynAreaFlags_DoublyMapped  ; if src is doubly mapped
+        MOVNE   r9, r3                          ; then set up offset from 1st copy to 2nd copy = old src size
+        ADD     r0, r0, r3                      ; move r0 to point to after end of area (2nd copy)
+        SUB     r3, r3, r2
+        STR     r3, [r11, #DANode_Size]         ; store reduced source size
+
+        LDR     r1, [r12, #DANode_Base]         ; this is free pool or app space, so it can't be doubly mapped!
+        LDR     r3, [r12, #DANode_Size]
+        ADD     r1, r1, r3                      ; r1 -> address of 1st extra page
+
+        MOV     r4, r2
+        LDR     r6, [r12, #DANode_Flags]        ; r6 = dst flags
+        AND     r6, r6, #DynAreaFlags_AccessMask
+20
+        SUB     r0, r0, r5                      ; pre-decrement source pointer
+ [ DebugCDA2
+        DREG    r0, "Moving page at ", cc
+        DREG    r1, " to ", cc
+        DREG    r6, " with PPL "
+ ]
+        BL      MovePageAtR0ToR1WithAccessR6
+        ADD     r1, r1, r5
+        SUBS    r4, r4, r5
+        BNE     %BT20
+
+        ADD     r3, r3, r2
+        STR     r3, [r12, #DANode_Size]         ; store increased destination size
+        EORS    lr, r12, #AppSpaceDANode        ; check if dest = appspace (if so lr:=0)
+        STREQ   r3, [lr, #MemLimit]             ; update memlimit if so
+
+        Pull    "r3, r6"                        ; restore src old size, src flags
+        TST     r6, #DynAreaFlags_DoublyMapped  ; if src doubly mapped
+        SUBNES  r4, r3, r2                      ; then set r4 = number of pages to shuffle up
+        BEQ     %FT30                           ; [not doubly mapped, or no pages left, so skip]
+
+        SUB     r0, r0, r3                      ; move r0 back to end of 1st copy of pages remaining
+        ADD     r1, r0, r2                      ; r1 is end of where they're moving to (should be src base address!)
+ [ 1 = 1
+ |
+        AND     r6, r6, #DynAreaFlags_AccessMask
+ ]
+        MOV     r9, #0                          ; no funny stuff while moving these pages
+25
+        SUB     r0, r0, r5
+        SUB     r1, r1, r5
+ [ 1 = 1
+        BL      GetPageFlagsForR0IntoR6
+ ]
+        BL      MovePageAtR0ToR1WithAccessR6
+        SUBS    r4, r4, r5
+        BNE     %BT25
+
+30
+        BL      CallPostShrink
+        RSB     r1, r2, #0
+        LDR     r0, [r11, #DANode_Number]       ; reload dynamic area number
+        B       IssueServiceMemoryMoved
+
+AreaGrow
+ [ DebugCDA2
+        DREG    r0, "Growing area ", cc
+        DREG    r1, " by "
+ ]
+        MOV     r12, r10                        ; dest is area specified
+        CMP     r0, #ChangeDyn_FreePool         ; if dest is free pool
+        ADREQ   r11, AppSpaceDANode             ; then src is appspace
+        ADRNE   r11, FreePoolDANode             ; else src is free pool (may later be free+apl)
+
+        ASSERT  DANode_MaxSize = DANode_Size +4
+        ADD     r2, r12, #DANode_Size
+        LDMIA   r2, {r2, r3}
+        SUB     lr, r3, r2                      ; lr = amount dest could grow
+
+ [ DebugCDA2
+        DREG    lr, "Dest could grow by "
+ ]
+
+        LDR     r2, [r11, #DANode_Size]         ; amount src could shrink
+        CMP     r11, #AppSpaceDANode            ; if appspace
+        SUBEQ   r2, r2, #&8000                  ; then can't take away last 32K (0..&7FFF)
+
+ [ DebugCDA2
+        DREG    r2, "Src could shrink by "
+ ]
+
+        CMP     r1, lr                          ; if enough room in dest
+        CMPLS   r1, r2                          ; and enough space in src
+        MOVLS   r3, r1                          ; then can do full amount
+        BLS     %FT65                           ; so skip this bit
+
+; we can't move all that is required
+;
+; if src = AplSpace then
+;       (dest must be free pool)
+;       move reduced amount
+; else
+;       (src must be free pool)
+;       (dest <> AplSpace, cos that's a shrink!)
+;       so check if adding aplspace would allow us to succeed
+;       if it does then adjust registers, else give error
+; endif
+;
+
+ [ DebugCDA2
+        DLINE   "Can't move all required using just free pool"
+ ]
+
+        CMP     r11, #AppSpaceDANode
+        BNE     %FT62
+        MOV     r1, lr
+        CMP     r1, r2
+        MOVHI   r1, r2                          ; move min(max addable to dest, max removable from src)
+
+ [ DebugCDA2
+        DREG    r1, "Dest is free pool, moving reduced amount of "
+ ]
+
+61
+        BL      GenNotAllMovedError
+        SUB     lr, r5, #1                      ; lr = pagesize mask
+        BICS    r1, r1, lr                      ; a pagesize multiple
+        BEQ     IssueServiceMemoryMoved
+        MOV     r3, r1
+        B       %FT65
+
+62
+        MOV     r4, #AppSpaceDANode
+        LDR     r6, [r4, #DANode_Size]          ; get current size of apl space
+        SUB     r6, r6, #&8000                  ; can't take away 0-&7FFF
+        ADD     r3, r2, r6                      ; add on to amount we could remove from free pool
+
+ [ DebugCDA2
+        DREG    r6, "Can get from app space an additional ", cc
+        DREG    r3, " making a total of "
+ ]
+
+        CMP     r1, lr                          ; if not enough room in dest
+        CMPLS   r1, r3                          ; or src still doesn't have enough
+        MOVHI   r1, #0                          ; then don't move any
+        BHI     %BT61                           ; and return error
+
+        MOV     r3, r1                          ; amount actually doing
+
+        TEQ     r2, #0                          ; else check to see if there was any at all in free pool
+        MOVEQ   r11, #AppSpaceDANode            ; if not, then just take from aplspace
+        MOVEQ   r7, r3                          ; and do all
+
+        MOVNE   r11, #0                         ; else make src indicator reflect that we need both
+        MOVNE   r7, r2                          ; but save amount we are taking from freepool
+
+65
+
+        Push    "r10"
+        MOV     r10, #0                         ; default value if apl space not involved
+        CMP     r11, #AppSpaceDANode            ; if source = aplspace
+        RSBEQ   r10, r3, #0                     ; then make amount -ve
+        CMP     r11, #0                         ; if source = free and apl
+        SUBEQ   r10, r7, r3                     ; then make it -(amount removing from apl space)
+        MOVNE   r7, r3                          ; else set up r7 to be total amount (wasn't set up above)
+
+ [ DebugCDA2
+        DREG    r3, "Amount actually moving into area = "
+        DREG    r7, "Amount coming from 1st src area = "
+ ]
+
+        CMP     r10, #0                         ; if neither of the above then don't talk to app (CMP clears V)
+        BLNE    CheckAppSpace                   ; else check app agrees
+        Pull    "r10"
+        BVS     ChangeDynError
+
+; now split up grow into bite-size chunks, and call DoTheGrow to do each one
+
+        Push    "r3"                            ; save original total amount
+        TEQ     r11, #0                         ; if taking from both free + apl
+        MOVEQ   r11, #FreePoolDANode            ; then start with free
+ [ DAF_SpecifyBit
+        LDR     lr, [r12, #DANode_Flags]        ; could this area require particular physical pages at all?
+        TST     lr, #DynAreaFlags_NeedsSpecificPages
+        BNE     %FT70                           ; [yes it could, so do it in lumps]
+
+        MOV     r1, #0                          ; no page block
+        MOV     r2, r3, LSR #12                 ; number of pages to do
+        BL      CallPreGrow
+        LDRVS   r3, [sp]                        ; if error, haven't done any, so restore total as how much to do
+        BVS     %FT95
+
+        Push    "r3, r7"
+        MOV     r2, r7, LSR #12
+        BL      DoTheGrowNotSpecified
+        Pull    "r3, r7"
+        SUBS    r3, r3, r7                      ; subtract off what we just did
+
+        MOVHI   r7, r3                          ; if not finished, then start 2nd half
+        MOVHI   r11, #AppSpaceDANode            ; which is app space
+        MOVHI   r2, r7, LSR #12
+        BLHI    DoTheGrowNotSpecified
+
+        LDR     r3, [sp]                        ; restore total amount
+        MOV     r1, #0                          ; indicate no page block (and ptr to semaphore)
+        STR     r1, [r1, #CDASemaphore]         ; OK to reenter now (we've done the damage)
+        MOV     r2, r3, LSR #12
+        BL      CallPostGrow
+        BVS     %FT95
+        B       %FT80
+ ]
+
+70
+        Push    "r3, r7"
+        CMP     r7, #PageBlockChunk             ; only do 1 area, so do min(r7,page)
+        MOVHI   r7, #PageBlockChunk
+        MOV     r2, r7, LSR #12                 ; number of entries to fill in in page block
+        BL      DoTheGrow
+        Pull    "r3, r7"
+        BVS     %FT95
+        CMP     r7, #PageBlockChunk             ; if 1st area is more than 1 page
+        SUBHI   r3, r3, #PageBlockChunk         ; then reduce total
+        SUBHI   r7, r7, #PageBlockChunk         ; and partial amounts by 1 page and do it again
+        BHI     %BT70
+
+        SUBS    r3, r3, r7                      ; subtract off what we just did
+        MOVHI   r7, r3                          ; if not finished, then start 2nd half
+        MOVHI   r11, #AppSpaceDANode            ; which is app space
+        BHI     %BT70                           ; and loop
+80
+        Pull    "r3"                            ; restore total amount
+
+        MOV     r1, r3
+        LDR     r0, [r12, #DANode_Number]       ; reload dynamic area number
+        B       IssueServiceMemoryMoved
+
+95
+        Pull    "r1"                            ; restore total amount
+        SUB     r1, r1, r3                      ; subtract off amount left, to leave done amount
+        B       ChangeDynErrorSomeMoved
+
+GenNotAllMovedError ENTRY "r0"
+        ADRL    r0, ErrorBlock_ChDynamNotAllMoved
+ [ International
+        BL      TranslateError
+ ]
+        STR     r0, [sp, #2*4]          ; sp -> r0,lr, then stacked r0,r2-r9,r10,lr
+        LDR     lr, [sp, #12*4]
+        ORR     lr, lr, #V_bit
+        STR     lr, [sp, #12*4]
+        EXIT
+
+ChangeDynError
+
+; in:   r0 -> error
+;       r10 -> area that we tried to shrink/grow
+
+        MOV     r1, #0
+ChangeDynErrorSomeMoved
+        STR     r0, [sp]
+        LDR     lr, [sp, #10*4]
+        ORR     lr, lr, #V_bit
+        STR     lr, [sp, #10*4]
+        B       SomeMemoryMoved
+
+NoMemoryMoved
+        MOV     r1, #0                          ; nothing moved
+SomeMemoryMoved
+        LDR     r0, [r10, #DANode_Number]       ; reload area number
+
+; and drop thru to...
+
+IssueServiceMemoryMoved
+
+; in:   r0 = area number that was shrunk/grown
+;       r1 = amount moved (signed)
+;
+        Push    "r1"
+        MOV     r2, r0                  ; r2 = area number
+        MOV     r0, r1                  ; amount moved (signed)
+        MOV     r1, #Service_MemoryMoved
+        BL      Issue_Service
+        Pull    "r1"                    ; restore amount moved
+        TEQ     r1, #0
+        RSBMI   r1, r1, #0              ; r1 on exit = unsigned amount
+
+ [ NewStyle_All
+        MOV     r0, #0
+        STR     r0, [r0, #CDASemaphore] ; clear CDASemaphore
+ ]
+        Pull    "r0, r2-r9, r10, lr"
+        ExitSWIHandler
+
+; ***********************************************************************************
+;
+;       DoTheGrow - Do one chunk of growing, small enough to fit into the page block on the stack
+;
+; in:   r2 = number of entries to put in page block (for this chunk)
+;       r5 = page size
+;       r7 = amount taking from src area (in this chunk)
+;       (r10 -> dest area)
+;       r11 -> src area
+;       r12 -> dest area
+;
+; out:  r0-r2,r4,r6-r9 may be corrupted
+;       r3,r5,r10-r12 preserved
+;
+; Note: Removal is from one area only, the calling routine breaks the chunks up at free/app boundary.
+
+; Temporary (stack frame) workspace used by this routine
+
+                ^       0, sp
+NumEntries      #       4                       ; Number of entries to do for this chunk
+DestAddr        #       4                       ; Log addr of 1st page being added to dest
+DestFlags       #       4                       ; Page flags for destination area
+TotalAmount     #       4                       ; Total size of grow for this chunk (ie entry value of r3)
+SavedPSR        #       4                       ; PC+PSR before IRQs disabled
+Offset1To2      #       4                       ; Offset from 1st to 2nd bank
+
+ [ DAF_SpecifyBit
+DoTheGrowNotSpecifiedStackSize * :INDEX: @      ; amount of stack needed for 'not specified' version
+ ]
+
+PageBlock1      #       PageBlockSize           ; 1st page block, for original page numbers and phys. addrs
+PageBlock2      #       PageBlockSize           ; 2nd page block, for new page numbers and phys. addrs
+
+DoTheGrowStackSize *    :INDEX: @
+
+DoTheGrow ENTRY "r3,r5,r10-r12", DoTheGrowStackSize
+
+; First fill in the page block with -1 in the physical page number words
+
+        STR     r2, NumEntries                  ; save number of entries for use later
+        STR     r7, TotalAmount                 ; save amount growing by
+
+        ADR     r1, PageBlock1                  ; point at 1st page block on stack
+        ADD     lr, r2, r2, LSL #1              ; lr = number of words in page block
+        ADD     lr, r1, lr, LSL #2              ; lr -> off end of page block
+        MOV     r0, #-1
+10
+        STR     r0, [lr, #-12]!                 ; store -1, going backwards
+        STR     r0, [lr, #PageBlockSize]        ; and put -1 in 2nd page block as well
+        TEQ     lr, r1                          ; until the end
+        BNE     %BT10
+
+; Now call the pre-grow handler
+
+        MOV     r3, r7
+        BL      CallPreGrow
+        EXIT    VS
+
+; now check to see if particular pages are required
+
+        LDR     lr, [r1]                        ; load page number in 1st entry
+        CMP     lr, #-1                         ; is it -1?
+        BNE     DoTheGrowPagesSpecified         ; if not, then jump to special code
+
+; now move pages starting from end of area
+
+        MOV     r2, r3                          ; amount moving
+        LDR     r0, [r11, #DANode_Base]
+        LDR     r3, [r11, #DANode_Size]
+        ADD     r0, r0, r3                      ; move r0 to point to after end of area
+        SUB     r3, r3, r2                      ; reduce by amount moving from area
+        STR     r3, [r11, #DANode_Size]         ; store reduced source size
+        TEQ     r11, #AppSpaceDANode                    ; if just appspace
+        STREQ   r3, [r11, #MemLimit-AppSpaceDANode]     ; then store in memlimit
+
+        LDR     r1, [r12, #DANode_Base]
+        LDR     r3, [r12, #DANode_Size]
+
+        LDR     r6, [r12, #DANode_Flags]        ; r6 = dst flags
+        AND     r6, r6, #DynAreaFlags_AccessMask
+        TST     r6, #DynAreaFlags_DoublyMapped  ; check if dst is doubly mapped
+        BEQ     %FT25                           ; [it's not, so skip all this]
+
+; we must shunt all existing pages in dest area down
+
+        MOVS    r4, r3                          ; amount to do
+        BEQ     %FT20                           ; [none, so skip all this]
+        Push    "r0, r1"
+        SUB     r0, r1, r3                      ; src starts at start of 1st copy = start of 2nd - old size
+        SUB     r1, r0, r2                      ; dst start = src start - amount of room needed
+        MOV     r9, #0                          ; no funny business while moving these pages
+15
+        BL      MovePageAtR0ToR1WithAccessR6    ; move page
+        ADD     r0, r0, r5                      ; advance src ptr
+        ADD     r1, r1, r5                      ; advance dst ptr
+        SUBS    r4, r4, r5                      ; one less page to move
+        BNE     %BT15                           ; loop if more
+        Pull    "r0, r1"                        ; restore original regs
+20
+        ADD     r9, r3, r2                      ; set up offset from 1st copy to 2nd copy (= new size)
+25
+        ADD     r1, r1, r3                      ; r1 -> address of 1st extra page
+        MOV     r4, #0                          ; amount done so far
+        MOV     r10, r2                         ; move amount to do into r10, as routine returns page number in r2
+        ADR     r3, PageBlock1                  ; point at 1st entry we have to update
+30
+        SUB     r0, r0, r5                      ; pre-decrement source pointer
+ [ DebugCDA2
+        DREG    r0, "Moving page at ", cc
+        DREG    r1, " to ", cc
+        DREG    r6, " with PPL "
+ ]
+        BL      MovePageAtR0ToR1WithAccessR6ReturnPageNumber
+        STR     r2, [r3], #12                   ; store page number and move on
+        ADD     r1, r1, r5
+        ADD     r4, r4, r5
+        CMP     r4, r10                         ; have we done all of it?
+        BNE     %BT30                           ; [no, so loop]
+35
+        LDR     r3, [r12, #DANode_Size]
+        ADD     r3, r3, r10
+        STR     r3, [r12, #DANode_Size]         ; store increased destination size
+
+        MOV     r3, r10                         ; r3 = size of change
+        LDR     r2, NumEntries                  ; restore number of entries in page block
+        ADR     r1, PageBlock1                  ; point at page block 1 with page numbers filled in
+        BL      CallPostGrow
+        CLRV
+        EXIT
+
+37
+
+; Come here if a required page is not available
+; First we need to go back thru all the part of the page block we've already done,
+; marking the pages as not being used after all
+
+        ADR     r2, PageBlock1
+38
+        LDR     r4, [r1, #-12]!                 ; r4 = physical page number
+        ADD     r4, r0, r4, LSL #3              ; point at cam entry
+        LDMIA   r4, {r8, lr}
+        BIC     lr, lr, #PageFlags_Required
+        STMIA   r4, {r8, lr}
+        TEQ     r1, r2
+        BNE     %BT38
+
+; since pre-grow handler exited without an error, we have to keep our promise
+; to call the post-grow handler
+
+        MOV     r3, #0                          ; no pages moved
+        MOV     r2, #0                          ; no pages moved
+        ADR     r1, PageBlock1                  ; not really relevant
+        BL      CallPostGrow
+
+        ADR     r0, ErrorBlock_CantGetPhysMem
+ [ International
+        BL      TranslateError
+ ]
+        SETV
+        EXIT
+
+        MakeErrorBlock  CantGetPhysMem
+
+DoTheGrowPagesSpecified
+
+; First check if any of the pages requested are unavailable
+; At the same time as we're doing this, we fill in the log. and phys. addresses in the block
+
+        MOV     r0, #0
+        LDR     r0, [r0, #CamEntriesPointer]
+        LDR     r6, =L2PT
+40
+        LDR     r3, [r1], #12                   ; r4 = physical page number
+        ADD     r4, r0, r3, LSL #3              ; point at cam entry
+        LDMIA   r4, {r8, lr}                    ; r8 = log. addr, lr = PPL
+        STR     r8, [r1, #4-12]                 ; store log. addr in page block
+        STR     r8, [r1, #PageBlockSize+4-12]   ; and in 2nd page block
+
+        TST     lr, #PageFlags_Unavailable :OR: PageFlags_Required ; if page in use by someone else, or by us, then return error
+        BNE     %BT37
+        ORR     lr, lr, #PageFlags_Required     ; set bit in flags to say page will be needed
+        STR     lr, [r4, #4]                    ; and store back
+
+; work out physical address direct from physical page number, NOT from logical address, since log addr may be 01F08000 (multiply mapped)
+
+        MOV     r4, #PhysRamTable
+42
+        LDMIA   r4!, {r8, lr}                   ; load phys addr, size
+        SUBS    r3, r3, lr, LSR #12             ; subtract off number of pages in this chunk
+        BCS     %BT42
+
+        ADD     r3, r3, lr, LSR #12             ; put back what could not be subtracted
+        ADD     r8, r8, r3, LSL #12             ; and add onto base address
+        STR     r8, [r1, #8-12]                 ; store physical address in page block
+
+        SUBS    r2, r2, #1
+        BNE     %BT40
+
+; now issue Service_PagesUnsafe
+
+        ADR     r2, PageBlock1                  ; r2 -> 1st page block
+        LDR     r3, NumEntries                  ; r3 = number of entries in page block
+        MOV     r1, #Service_PagesUnsafe
+        BL      Issue_Service
+
+; now move the pages
+
+        LDR     r2, TotalAmount                 ; amount moving
+        LDR     r0, [r11, #DANode_Base]
+        LDR     r3, [r11, #DANode_Size]
+        ADD     r0, r0, r3                      ; move r0 to point to after end of area
+        SUB     r3, r3, r2                      ; reduce by amount moving from area
+        STR     r3, [r11, #DANode_Size]         ; store reduced source size
+        TEQ     r11, #AppSpaceDANode                    ; if appspace
+        STREQ   r3, [r11, #MemLimit-AppSpaceDANode]     ; then update memlimit
+
+        LDR     r1, [r12, #DANode_Base]
+        LDR     r3, [r12, #DANode_Size]
+
+        LDR     r6, [r12, #DANode_Flags]        ; r6 = dst flags
+        AND     r6, r6, #DynAreaFlags_AccessMask
+        ORR     r6, r6, #PageFlags_Unavailable  ; set unavailable bit
+        STR     r6, DestFlags                   ; save for later
+        TST     r6, #DynAreaFlags_DoublyMapped  ; check if dst is doubly mapped
+        BEQ     %FT55                           ; [it's not, so skip all this, and r9 will be irrelevant]
+
+; we must shunt all existing pages in dest area down
+
+        MOVS    r4, r3                          ; amount to do
+        BEQ     %FT50                           ; [none, so skip all this]
+        Push    "r0, r1"
+        SUB     r0, r1, r3                      ; src starts at start of 1st copy = start of 2nd - old size
+        SUB     r1, r0, r2                      ; dst start = src start - amount of room needed
+        MOV     r9, #0                          ; no funny business while moving these pages
+45
+        BL      MovePageAtR0ToR1WithAccessR6    ; move page
+        ADD     r0, r0, r5                      ; advance src ptr
+        ADD     r1, r1, r5                      ; advance dst ptr
+        SUBS    r4, r4, r5                      ; one less page to move
+        BNE     %BT45                           ; loop if more
+        Pull    "r0, r1"                        ; restore original regs
+50
+        ADD     r9, r3, r2                      ; set up offset from 1st copy to 2nd copy (= new size)
+55
+        STR     r9, Offset1To2                  ; store offset 1st to 2nd copy
+        ADD     r1, r1, r3                      ; r1 -> address of 1st extra page
+        STR     r1, DestAddr
+        ADR     r8, PageBlock1                  ; r8 -> position in 1st page block
+        SUB     r2, r0, r2                      ; r2 = lowest address being removed from src
+        MOV     r3, #0
+        LDR     r3, [r3, #CamEntriesPointer]
+        MOV     r4, r0                          ; r4 is where we're at in allocating spare logical addresses
+        LDR     r9, NumEntries                  ; number of entries still to do in 1st loop
+
+; Now before we start, we must construct the second page block, with replacement page numbers
+
+;        DLINE   "Start of 1st loop"
+
+60
+        LDR     r6, [r8], #12                   ; r6 = page number required
+        LDR     r10, [r8, #8-12]                ; r10 = phys addr
+        LDR     lr, [r3, r6, LSL #3]            ; lr = logical address for this page
+
+;        DREG    r6, "Checking page ", cc
+;        DREG    lr, "at address "
+
+        CMP     lr, r2                          ; check if address is one being taken from src anyway
+        BCC     %FT63
+        CMP     lr, r0
+        BCS     %FT63
+
+;        DLINE   "Page is being taken away anyway"
+        B       %FT68                           ; [page is being taken anyway, so use same page number + phys addr in 2nd block]
+
+; page is not one being taken away, so put in 1st replacement page that isn't required by area
+
+63
+;        DLINE   "Page is not being taken, looking for replacement"
+
+64
+        SUB     r4, r4, r5                      ; go onto next page being taken from src
+;        DREG    r4, "Considering address "
+
+        LDR     lr, =L2PT
+        LDR     lr, [lr, r4, LSR #10]           ; get L2PT entry (to get phys addr) for next free page
+        MOV     r10, lr, LSR #12                ; r10 = phys addr >>> 12
+
+; now convert phys addr to page number
+
+        MOV     r6, #0
+        MOV     r1, #PhysRamTable
+66
+        LDMIA   r1!, {r7, lr}                   ; load phys addr, size
+        SUB     r7, r10, r7, LSR #12            ; number of pages into this bank
+        CMP     r7, lr, LSR #12                 ; if more than there are here,
+        ADDCS   r6, r6, lr, LSR #12             ; then advance page number by number of pages in this bank
+        BCS     %BT66                           ; and go onto next bank
+
+        ADD     r6, r6, r7                      ; advance page number by no. of pages into this bank
+
+        ADD     r1, r3, r6, LSL #3              ; r1 -> cam entry for this page
+        LDR     r1, [r1, #4]                    ; get PPL for this page
+        TST     r1, #PageFlags_Required         ; if this page is required for the operation
+        BNE     %BT64                           ; then try next page
+
+        MOV     r10, r10, LSL #12               ; make r10 proper phys addr
+;        DREG    r6, "Using page number "
+68
+        STR     r6, [r8, #PageBlockSize-12]     ; store page number in 2nd block
+        STR     r10, [r8, #PageBlockSize+8-12]  ; and store phys addr
+
+        SUBS    r9, r9, #1                      ; one less entry to do
+        BNE     %BT60
+
+        MOV     r7, r3                          ; r7 -> camentries
+
+; Now we can go onto the 2nd loop which actually moves the pages
+
+        LDR     r1, DestAddr
+        MOV     r4, #0                          ; amount done
+        MOV     r0, r7                          ; point r0 at camentries
+        LDR     r7, TotalAmount                 ; amount to do
+        ADR     r8, PageBlock1
+        LDR     r9, Offset1To2
+70
+        STR     pc, SavedPSR                    ; save old PSR (note: stack must be flat when we do this!)
+
+        Push    "r0-r4,r7-r12"                  ; save regs used during copy
+        MOV     r1, #Service_ClaimFIQ
+        BL      Issue_Service
+
+        TEQP    pc, #SVC_mode + I_bit           ; disable IRQs round here
+        NOP
+
+        LDR     r6, [r8, #0]                    ; r6 = page number required
+        LDR     lr, [r8, #PageBlockSize+0]      ; lr = page number of replacement page
+        TEQ     r6, lr                          ; if the same
+        Pull    "r0-r4,r7-r12", EQ              ; then restore registers
+        BEQ     %FT76                           ; and skip copy and first page move
+
+        LDR     r0, [r0, lr, LSL #3]            ; r0 = log. address for replacement page (NB use logical address to write to, for cache consistency)
+        LDR     r6, [r8, #8]                    ; r6 = physical address of src for copy
+        ORR     r6, r6, #PhysSpace              ; must use physical address, as page may be mapped to 01F08000 along with others
+        ADD     lr, r6, r5                      ; lr = end src address
+72
+        LDMIA   r6!, {r2, r3, r4, r7, r9, r10, r11, r12}
+        STMIA   r0!, {r2, r3, r4, r7, r9, r10, r11, r12}
+        TEQ     r6, lr
+        BNE     %BT72
+
+; now check if page we're replacing is in L2PT, and if so then adjust L1PT entries (4 of these)
+
+        LDR     r6, [r8, #4]                    ; look at logical address of page being replaced
+        SUBS    r6, r6, #L2PT
+        BCC     %FT74                           ; address is below L2PT
+        CMP     r6, #4*1024*1024
+        BCS     %FT74                           ; address is above L2PT
+
+        LDR     r2, =L1PT
+        ADD     r2, r2, r6, LSR #(12-4)         ; address in L1 of 4 consecutive words to update
+        LDR     r3, [r2]                        ; load 1st word, to get AP etc bits
+        MOV     r3, r3, LSL #(31-9)             ; junk other bits
+        LDR     r4, [r8, #PageBlockSize+8]      ; load new physical address for page
+        ORR     r3, r4, r3, LSR #(31-9)         ; and merge with AP etc bits
+        STR     r3, [r2], #4
+        ADD     r3, r3, #&400
+        STR     r3, [r2], #4
+        ADD     r3, r3, #&400
+        STR     r3, [r2], #4
+        ADD     r3, r3, #&400
+        STR     r3, [r2], #4
+74
+        Pull    "r0-r4,r7-r12"                  ; restore registers
+
+        MOV     lr, #0
+        LDR     lr, [lr, #CamEntriesPointer]    ; lr -> soft cam map
+        ADD     lr, lr, #4                      ; point at PPLs, not addresses
+        LDR     r2, [r8, #0]                    ; need to get PPL for page being replaced
+        LDR     r11, [lr, r2, LSL #3]
+        BIC     r11, r11, #PageFlags_Required   ; knock off bits that indicate that it was a required page
+
+        ADD     lr, r8, #PageBlockSize
+        LDMIA   lr, {r2, r3}                    ; get page number, logical address
+        BL      Call_CAM_Mapping                ; move replacement page in
+76
+        LDR     r2, [r8, #0]
+        MOV     r3, r1
+        LDR     r11, DestFlags
+        BL      Call_CAM_Mapping                ; move needed page to destination
+
+        LDR     lr, SavedPSR
+        TEQP    lr, #0
+
+        Push    "r1"
+        MOV     r1, #Service_ReleaseFIQ
+        BL      Issue_Service
+        Pull    "r1"
+
+        ADD     r1, r1, r5                      ; advance dest ptr
+        ADD     r4, r4, r5                      ; increment amount done
+        ADD     r8, r8, #12                     ; advance page block ptr
+        CMP     r4, r7                          ; have we done all?
+        BNE     %BT70                           ; [no, so loop]
+
+        LDR     r3, [r12, #DANode_Size]
+        ADD     r3, r3, r7
+        STR     r3, [r12, #DANode_Size]         ; store increased destination size
+
+; now issue Service_PagesSafe
+
+        LDR     r2, NumEntries
+        ADR     r3, PageBlock1
+        ADR     r4, PageBlock2
+        MOV     r1, #Service_PagesSafe
+        BL      Issue_Service
+
+; now call Post_Grow handler
+
+        LDR     r3, TotalAmount                 ; size of grow
+        LDR     r2, NumEntries                  ; restore number of entries in page block
+        ADR     r1, PageBlock1                  ; point at page block 1 with page numbers filled in
+        BL      CallPostGrow
+        CLRV
+        EXIT
+
+ [ DAF_SpecifyBit
+; ***********************************************************************************
+;
+;       DoTheGrowNotSpecified - Do one chunk of growing, with no page block
+;                               But don't call pre-grow or post-grow either
+;
+; in:   r2 = number of pages to do (in this chunk)
+;       r5 = page size
+;       r7 = amount taking from src area (in this chunk)
+;       (r10 -> dest area)
+;       r11 -> src area
+;       r12 -> dest area
+;
+; out:  r0-r2,r4,r6-r9 may be corrupted
+;       r3,r5,r10-r12 preserved
+;
+; Note: Removal is from one area only, the calling routine breaks the chunk at free/app boundary.
+
+
+DoTheGrowNotSpecified ENTRY "r3,r5,r10-r12", DoTheGrowNotSpecifiedStackSize
+
+        STR     r2, NumEntries                  ; save number of entries for use later
+        STR     r7, TotalAmount                 ; save amount growing by
+
+; now move pages starting from end of area
+
+        MOV     r2, r7                          ; amount moving
+        LDR     r0, [r11, #DANode_Base]
+        LDR     r3, [r11, #DANode_Size]
+        ADD     r0, r0, r3                      ; move r0 to point to after end of area
+        SUB     r3, r3, r2                      ; reduce by amount moving from area
+        STR     r3, [r11, #DANode_Size]         ; store reduced source size
+        TEQ     r11, #AppSpaceDANode                    ; if just appspace
+        STREQ   r3, [r11, #MemLimit-AppSpaceDANode]     ; then store in memlimit
+
+        LDR     r1, [r12, #DANode_Base]
+        LDR     r3, [r12, #DANode_Size]
+
+        LDR     r6, [r12, #DANode_Flags]        ; r6 = dst flags
+        AND     r6, r6, #DynAreaFlags_AccessMask
+        TST     r6, #DynAreaFlags_DoublyMapped  ; check if dst is doubly mapped
+        BEQ     %FT25                           ; [it's not, so skip all this]
+
+; we must shunt all existing pages in dest area down
+
+        MOVS    r4, r3                          ; amount to do
+        BEQ     %FT20                           ; [none, so skip all this]
+        Push    "r0, r1"
+        SUB     r0, r1, r3                      ; src starts at start of 1st copy = start of 2nd - old size
+        SUB     r1, r0, r2                      ; dst start = src start - amount of room needed
+        MOV     r9, #0                          ; no funny business while moving these pages
+15
+        BL      MovePageAtR0ToR1WithAccessR6    ; move page
+        ADD     r0, r0, r5                      ; advance src ptr
+        ADD     r1, r1, r5                      ; advance dst ptr
+        SUBS    r4, r4, r5                      ; one less page to move
+        BNE     %BT15                           ; loop if more
+        Pull    "r0, r1"                        ; restore original regs
+20
+        ADD     r9, r3, r2                      ; set up offset from 1st copy to 2nd copy (= new size)
+25
+        ADD     r1, r1, r3                      ; r1 -> address of 1st extra page
+        MOV     r4, #0                          ; amount done so far
+        MOV     r10, r2                         ; move amount to do into r10
+30
+        SUB     r0, r0, r5                      ; pre-decrement source pointer
+ [ DebugCDA2
+        DREG    r0, "Moving page at ", cc
+        DREG    r1, " to ", cc
+        DREG    r6, " with PPL "
+ ]
+        BL      MovePageAtR0ToR1WithAccessR6
+        ADD     r1, r1, r5
+        ADD     r4, r4, r5
+        CMP     r4, r10                         ; have we done all of it?
+        BNE     %BT30                           ; [no, so loop]
+35
+        LDR     r3, [r12, #DANode_Size]
+        ADD     r3, r3, r10
+        STR     r3, [r12, #DANode_Size]         ; store increased destination size
+
+        CLRV
+        EXIT
+
+ ] ; DAF_SpecifyBit
+
+; ***********************************************************************************
+;
+;       CheckAppSpace - If appspace involved in transfer, issue Service or UpCall
+;
+;       Internal routine, called by OS_ChangeDynamicArea
+;
+; in:   r0 = area number passed in to ChangeDyn
+;       r10 = size of change (signed)
+;       r11 -> node for src
+;       r12 -> node for dest
+;
+; out:  If appspace not involved, or application said it was OK, then
+;         V=0
+;         All registers preserved
+;       else
+;         V=1
+;         r0 -> error
+;         All other registers preserved
+;       endif
+;
+
+CheckAppSpace ENTRY "r0-r3"
+        MOV     r2, #0
+        LDR     r3, [r2, #AplWorkSize]
+        LDR     r2, [r2, #Curr_Active_Object]
+        CMP     r2, r3                          ; check if CAO outside application space
+        BHI     %FT20                           ; [it is so issue Service not UpCall]
+
+; CAO in application space, so issue UpCall to check it's OK
+
+        MOV     r0, #UpCall_MovingMemory :AND: &FF
+        ORR     r0, r0, #UpCall_MovingMemory :AND: &FFFFFF00
+        MOVS    r1, r10
+        RSBMI   r1, r1, #0                      ; r1 passed in is always +ve (probably a bug, but should be compat.)
+
+        SWI     XOS_UpCall
+        CMP     r0, #UpCall_Claimed             ; if upcall claimed
+        EXIT    EQ                              ; then OK to move memory, so exit (V=0 from CMP)
+
+        ADR     r0, ErrorBlock_ChDynamCAO
+10
+ [ International
+        BL      TranslateError
+ ]
+        STR     r0, [sp]
+        SETV
+        EXIT
+
+; IF service call claimed Then Error AplWSpaceInUse
+
+20
+        MOV     r0, r10                         ; amount removing from aplspace
+        MOV     r1, #Service_Memory
+        BL      Issue_Service
+        CMP     r1, #Service_Serviced
+        ADREQ   r0, ErrorBlock_AplWSpaceInUse   ; if service claimed, then return error
+        BEQ     %BT10
+        CLRV                                    ; else OK
+        EXIT
+
+ ]
+        MakeErrorBlock AplWSpaceInUse
+        MakeErrorBlock ChDynamCAO
+ [ NewCDA2
+
+; ***********************************************************************************
+;
+;       CallPreShrink - Call pre-shrink routine
+;
+; in:   r1 = amount shrinking by (+ve)
+;       r5 = page size
+;       r11 -> node for area being shrunk
+;
+; out:  If handler exits VC, then r2 = no. of bytes area can shrink by
+;       else r0 -> error block or 0 for generic error, and r2=0
+;
+
+CallPreShrink ENTRY "r0,r3,r4, r12"
+        LDR     r0, [r11, #DANode_Handler]              ; check if no handler
+        CMP     r0, #0                                  ; if none (V=0)
+        EXIT    EQ                                      ; then exit
+
+        MOV     r0, #DAHandler_PreShrink                ; r0 = reason code
+        MOV     r3, r1                                  ; r3 = amount shrinking by
+        LDR     r4, [r11, #DANode_Size]                 ; r4 = current size
+        ASSERT  DANode_Handler = DANode_Workspace +4
+        ADD     r12, r11, #DANode_Workspace
+        MOV     lr, pc
+        LDMIA   r12, {r12, pc}                          ; load workspace pointer and jump to handler
+
+; shrink amount returned by handler may not be page multiple (according to spec),
+; so we'd better make it so.
+
+        SUB     lr, r5, #1
+        BIC     r2, r3, lr                              ; make page multiple and move into r2
+        EXIT    VC
+        TEQ     r0, #0                                  ; if generic error returned
+        ADREQL  r0, ErrorBlock_ChDynamNotAllMoved       ; then substitute real error message
+ [ International
+        BLEQ    TranslateError
+ ]
+        STR     r0, [sp]
+        SETV
+        EXIT
+
+; ***********************************************************************************
+;
+;       CallPostShrink - Call post-shrink routine
+;
+; in:   r2 = amount shrinking by (+ve)
+;       r5 = page size
+;       r11 -> node for area being shrunk
+;
+; out:  All registers preserved
+;
+
+CallPostShrink ENTRY "r0,r3,r4, r12"
+        LDR     r0, [r11, #DANode_Handler]              ; check if no handler
+        CMP     r0, #0                                  ; if none (V=0)
+        EXIT    EQ                                      ; then exit
+
+        MOV     r0, #DAHandler_PostShrink               ; r0 = reason code
+        MOV     r3, r2                                  ; r3 = amount shrunk by
+        LDR     r4, [r11, #DANode_Size]                 ; r4 = new size
+        ASSERT  DANode_Handler = DANode_Workspace +4
+        ADD     r12, r11, #DANode_Workspace
+        MOV     lr, pc
+        LDMIA   r12, {r12, pc}                          ; load workspace pointer and jump to handler
+
+        EXIT
+
+; ***********************************************************************************
+;
+;       CallPreGrow - Call pre-grow routine
+;
+; in:   Eventually r1 -> page block (on stack)
+;                  r2 = number of entries in block
+;       but for now these are both undefined
+;       r3 = amount area is growing by
+;       r5 = page size
+;       r12 -> node for area being grown
+;
+; out:  If can't grow, then
+;         r0 -> error
+;         V=1
+;       else
+;         page block may be updated with page numbers (but not yet!)
+;         All registers preserved
+;         V=0
+;       endif
+;
+
+CallPreGrow ENTRY "r0,r4, r12"
+        LDR     r0, [r12, #DANode_Handler]              ; check if no handler
+        CMP     r0, #0                                  ; if none (V=0)
+        EXIT    EQ                                      ; then exit
+
+        MOV     r0, #DAHandler_PreGrow                  ; r0 = reason code
+        LDR     r4, [r12, #DANode_Size]                 ; r4 = current size
+        ASSERT  DANode_Handler = DANode_Workspace +4
+        ADD     r12, r12, #DANode_Workspace
+        MOV     lr, pc
+        LDMIA   r12, {r12, pc}                          ; load workspace pointer and jump to handler
+        EXIT    VC                                      ; if no error then exit
+
+        TEQ     r0, #0                                  ; if generic error returned
+        ADREQL  r0, ErrorBlock_ChDynamNotAllMoved       ; then substitute real error message
+ [ International
+        BLEQ    TranslateError
+ ]
+        STR     r0, [sp]
+        SETV
+        EXIT
+
+; ***********************************************************************************
+;
+;       CallPostGrow - Call post-grow routine
+;
+; in:   Eventually, r1 -> page block with actual pages put in
+;                   r2 = number of entries in block
+;       r3 = size of change
+;       r5 = page size
+;       r12 -> node for area being grown
+;
+; out:  All registers preserved
+;
+
+CallPostGrow ENTRY "r0,r3,r4, r12"
+        LDR     r0, [r12, #DANode_Handler]              ; check if no handler
+        CMP     r0, #0                                  ; if none (V=0)
+        EXIT    EQ                                      ; then exit
+
+        MOV     r0, #DAHandler_PostGrow                 ; r0 = reason code
+        LDR     r4, [r12, #DANode_Size]                 ; r4 = new size
+        ASSERT  DANode_Handler = DANode_Workspace +4
+        ADD     r12, r12, #DANode_Workspace
+        MOV     lr, pc
+        LDMIA   r12, {r12, pc}                          ; load workspace pointer and jump to handler
+        EXIT
+
+; ***********************************************************************************
+;
+;       MovePageAtR0ToR1WithAccessR6
+;
+;       Internal routine, called by OS_ChangeDynamicArea
+;
+; in:   r0 = logical address where page is now
+;       r1 = logical address it should be moved to
+;       r6 = area flags (which contain access privileges, and cacheable/bufferable bits)
+;
+; out:  All registers preserved
+;
+
+MovePageAtR0ToR1WithAccessR6 ENTRY "r2-r5,r11"
+        MOV     r3, r1
+        MOV     r11, r6
+        BL      MoveCAMatR0toR3         ; use old internal routine for now
+        EXIT
+
+; Same as above, but returns with r2 = page number of page that moved
+
+MovePageAtR0ToR1WithAccessR6ReturnPageNumber ENTRY "r3-r5,r11"
+        MOV     r3, r1
+        MOV     r11, r6
+        BL      MoveCAMatR0toR3         ; use old internal routine for now
+        EXIT
+
+; ***********************************************************************************
+;
+;       DynAreaHandler_SysHeap - Dynamic area handler for system heap
+;       DynAreaHandler_RMA     - Dynamic area handler for RMA
+;
+; in:   r0 = reason code (0=>pre-grow, 1=>post-grow, 2=>pre-shrink, 3=>post-shrink)
+;       r12 -> base of area
+;
+
+DynAreaHandler_SysHeap
+DynAreaHandler_RMA ROUT
+        CMP     r0, #4
+        ADDCC   pc, pc, r0, LSL #2
+        B       UnknownHandlerError
+        B       PreGrow_Heap
+        B       PostGrow_Heap
+        B       PreShrink_Heap
+        B       PostShrink_Heap
+
+PostGrow_Heap
+PostShrink_Heap
+        STR     r4, [r12, #:INDEX:hpdend] ; store new size
+
+; and drop thru to...
+
+PreGrow_Heap
+        CLRV                            ; don't need to do anything here
+        MOV     pc, lr                  ; so just exit
+
+PreShrink_Heap
+        Push    "r0, lr"
+        MOV     lr, pc                  ; save old IRQ status
+        TEQP    pc, #SVC_mode + I_bit   ; disable IRQs round this bit
+        LDR     r0, [r12, #:INDEX:hpdbase]      ; get minimum size
+        SUB     r0, r4, r0              ; r0 = current-minimum = max shrink
+        CMP     r3, r0                  ; if requested shrink > max
+        MOVHI   r3, r0                  ; then limit it
+        SUB     r0, r5, #1              ; r0 = page mask
+        BIC     r3, r3, r0              ; round size change down to page multiple
+        SUB     r0, r4, r3              ; area size after shrink
+        STR     r0, [r12, #:INDEX:hpdend] ; update size
+
+        TEQP    lr, #0                  ; restore IRQ status
+        CLRV
+        Pull    "r0, pc"
+
+AreaName_RMA
+        =       "Module area", 0
+        ALIGN
+
+ ]
+
+ [ NewCDA
+UnknownHandlerError
+        Push    "lr"
+        ADRL    r0, ErrorBlock_UnknownAreaHandler
+  [ International
+        BL      TranslateError
+  ]
+        SETV
+        Pull    "pc"
+ ]
+
+ [ NewStyle_SpriteArea
+DynAreaHandler_Sprites
+        CMP     r0, #4
+        ADDCC   pc, pc, r0, LSL #2
+        B       UnknownHandlerError
+        B       PreGrow_Sprite
+        B       PostGrow_Sprite
+        B       PreShrink_Sprite
+        B       PostShrink_Sprite
+
+PostGrow_Sprite
+PostShrink_Sprite ENTRY "r0"
+
+; in - r3 = size change (+ve), r4 = new size, r5 = page size
+
+        MOV     lr, #VduDriverWorkSpace
+        TEQ     r4, #0                  ; if new size = 0
+        STREQ   r4, [lr, #SpAreaStart]  ; then set area ptr to zero
+        STRNE   r12, [lr, #SpAreaStart] ; else store base address
+
+        MOV     r0, #0
+        LDR     lr, [r0, #SpriteSize]   ; load old size
+        STR     r4, [r0, #SpriteSize]   ; and store new size
+        BEQ     %FT10                   ; if new size is zero, don't try to update header
+
+        STR     r4, [r12, #saEnd]       ; store new size in header
+        TEQ     lr, #0                  ; if old size was zero
+        STREQ   lr, [r12, #saNumber]    ; then initialise header (no. of sprites = 0)
+        MOVEQ   lr, #saExten
+        STREQ   lr, [r12, #saFirst]     ; ptr to first sprite -> after header
+        STREQ   lr, [r12, #saFree]      ; ptr to first free byte -> after header
+
+10
+        CLRV                            ; don't need to do anything here
+        EXIT                            ; so just exit
+
+PreGrow_Sprite
+        CLRV                            ; don't need to do anything here
+        MOV     pc, lr                  ; so just exit
+
+PreShrink_Sprite ENTRY "r0"
+        TEQ     r4, #0                  ; if current size is zero
+        BEQ     %FT10                   ; then any shrink is OK (shouldn't happen)
+
+        LDR     r0, [r12, #saFree]      ; get used amount
+        TEQ     r0, #saExten            ; if only header used,
+        MOVEQ   r0, #0                  ; then none really in use
+
+        SUB     r0, r4, r0              ; r0 = current-minimum = max shrink
+        CMP     r3, r0                  ; if requested shrink > max
+        MOVHI   r3, r0                  ; then limit it
+        SUB     r0, r5, #1              ; r0 = page mask
+        BIC     r3, r3, r0              ; round size change down to page multiple
+10
+        CLRV
+        EXIT
+
+AreaName_SpriteArea
+        =       "System sprites", 0
+        ALIGN
+
+ ]
+
+ [ NewStyle_RAMDisc
+DynAreaHandler_RAMDisc
+        CMP     r0, #4
+        ADDCC   pc, pc, r0, LSL #2
+        B       UnknownHandlerError
+        B       PreGrow_RAMDisc
+        B       PostGrow_RAMDisc
+        B       PreShrink_RAMDisc
+        B       PostShrink_RAMDisc
+
+PostGrow_RAMDisc
+PostShrink_RAMDisc ENTRY "r0-r6"
+
+; in - r3 = size change (+ve), r4 = new size, r5 = page size
+; but we don't really care about any of these
+
+; The only thing we have to do here is ReInit RAMFS, but NOT if
+; a) no modules are initialised yet (eg when we're created), or
+; b) RAMFS has been unplugged
+
+        MOV     r0, #0
+        LDR     r0, [r0, #Module_List]
+        TEQ     r0, #0                  ; any modules yet?
+        BEQ     %FT90                   ; no, then don't do anything
+
+        MOV     r0, #ModHandReason_EnumerateROM_Modules
+        MOV     r1, #0
+        MOV     r2, #-1                 ; enumerate ROM modules looking for RAMFS
+10
+        SWI     XOS_Module
+        BVS     %FT50                   ; no more modules, so it can't be unplugged
+        ADR     r5, ramfsname
+20
+        LDRB    r6, [r3], #1            ; get char from returned module name
+        CMP     r6, #" "                ; if a terminator then we have a match
+        BLS     %FT30                   ; so check for unplugged
+        LowerCase r6, lr                ; else force char to lower case
+        LDRB    lr, [r5], #1            ; get char from "ramfs" string
+        CMP     lr, r6                  ; and if matches
+        BEQ     %BT20                   ; then try next char
+        B       %BT10                   ; else try next module
+
+30
+        CMP     r4, #-1                 ; is module unplugged?
+        BEQ     %FT90                   ; if so, then mustn't reinit it
+50
+        MOV     r0, #ModHandReason_ReInit ; reinit module
+        ADR     r1, ramfsname
+        SWI     XOS_Module              ; ignore any errors from this
+90
+        CLRV
+        EXIT
+
+PreGrow_RAMDisc
+PreShrink_RAMDisc ENTRY "r0-r5"
+        MOV     r0, #0
+        LDR     r0, [r0, #Module_List]  ; first check if any modules going
+        TEQ     r0, #0
+        BEQ     %FT90                   ; if not, don't look at filing system
+
+        MOV     r0, #5
+        ADR     r1, ramcolondollardotstar
+        SWI     XOS_File
+        CMPVC   r0, #0
+        BVS     %FT90                   ; if no RAMFS then change OK
+        BEQ     %FT90                   ; or if no files, then change OK
+        ADR     r0, ErrorBlock_RAMFsUnchangeable
+ [ International
+        BL      TranslateError
+ ]
+        STR     r0, [sp]
+        SETV
+        EXIT
+
+90
+        CLRV
+        EXIT
+
+        MakeErrorBlock  RAMFsUnchangeable
+
+AreaName_RAMDisc
+        =       "RAM disc", 0
+ramcolondollardotstar
+        =       "ram:$.*", 0
+ramfsname
+        =       "ramfs", 0
+        ALIGN
+
+ ]
+
+ [ NewStyle_FontArea
+DynAreaHandler_FontArea
+        CMP     r0, #4
+        ADDCC   pc, pc, r0, LSL #2
+        B       UnknownHandlerError
+        B       PreGrow_FontArea
+        B       PostGrow_FontArea
+        B       PreShrink_FontArea
+        B       PostShrink_FontArea
+
+PostGrow_FontArea ENTRY "r0-r2"
+
+; in - r3 = size change (+ve), r4 = new size, r5 = page size
+
+        MOV     r1, #0
+        LDR     r1, [r1, #Module_List]  ; any modules active?
+        TEQ     r1, #0
+        MOVNE   r1, r4                  ; there are, so inform font manager of size change
+        SWINE   XFont_ChangeArea
+        CLRV
+        EXIT
+
+PostShrink_FontArea
+PreGrow_FontArea
+        CLRV                            ; don't need to do anything here
+        MOV     pc, lr                  ; so just exit
+
+PreShrink_FontArea ENTRY "r0-r2"
+        MOV     r1, #-1                 ; ask font manager for minimum size of font area
+        MOV     r2, #0                  ; default value if no font manager
+        SWI     XFont_ChangeArea        ; out: r2 = minimum size
+
+        SUB     r0, r4, r2              ; r0 = current-minimum = max shrink
+        CMP     r3, r0                  ; if requested shrink > max
+        MOVHI   r3, r0                  ; then limit it
+        SUB     r0, r5, #1              ; r0 = page mask
+        BIC     r3, r3, r0              ; round size change down to page multiple
+
+        SUB     r1, r4, r3              ; r1 = new size
+        SWI     XFont_ChangeArea        ; tell font manager to reduce usage
+
+        CLRV
+        EXIT
+
+AreaName_FontArea
+        =       "Font cache", 0
+        ALIGN
+
+ ]
+
+; **** New screen stuff ****
+;
+;
+; This source collects together all the new routines needed to make
+; the screen into a new dynamic area.
+;
+; It has the following dependencies elsewhere in the kernel before
+; it can be expected to work:
+;
+; * Symbol NewStyle_Screen defined TRUE in GetAll
+; * Definition of AP_Screen in ChangeDyn needs doubly_mapped and
+;   name_is_token bits set
+; * name_is_token handling needs adding (or scrap the bit designation)
+; * Call to CreateNewScreenArea from NewReset to create area
+; * Tim says doubly-mapped areas are broken - this must be fixed first
+; * Old CDA routine may be retired, since screen is its last client
+; * Has Tim completed the rest of this work?
+;
+; Once these routines work, they should be grafted into appropriate
+; places in the kernel sources
+;
+; This source is not intended for stand-alone assembly: it should be
+; plumbed into the kernel source build
+;
+; Version history - remove this once integrated with kernel sources
+;
+; Vsn  Date      Who  What
+; ---  --------  ---  ----------------------------------------------
+; 000  23/08/93  amg  Written
+; 001  24/08/93  amg  Fixes and changes following review by TMD
+; 002  03/09/93  tmd  Updated to work!
+
+; *********************************************************************
+; Create a new style dynamic area for the screen
+; *********************************************************************
+
+; Entry requirements
+; none
+
+        [ NewStyle_Screen
+AreaName_Screen
+        =       "Screen memory",0               ;needs replacing with message token
+        ALIGN
+
+; *********************************************************************
+; Handler despatch routine for screen dynamic area
+; *********************************************************************
+
+DynAreaHandler_Screen                           ;despatch routine for pre/post grow/shrink handlers
+        CMP     r0, #4
+        ADDCC   pc, pc, R0, LSL #2
+        B       UnknownHandlerError             ;already defined in ChangeDyn
+        B       PreGrow_Screen                  ;the rest are defined here
+        B       PostGrow_Screen
+        B       PreShrink_Screen
+        B       PostShrink_Screen
+
+;The sequence of events which these handlers must do is:
+;
+;Grow Screen
+;
+;Pre : Remove cursors
+;      Work out which physical page numbers are needed and return a list
+;CDA : Move existing pages lower in memory within first copy (ie change logical address
+;        associated with physical pages)
+;      Locate and free the next physical pages in line (if used a page swap must occur)
+;      Assign the new pages logical addresses in the gap between the end of the present
+;        logical range and the start of the second physical range
+;Post: Adjust screen memory contents & screen start addresses to retain screen display
+;
+;Shrink Screen
+;
+;Pre : Remove cursors
+;      Adjust screen memory contents & screen start addresses to retain screen display
+;CDA : Move pages from screen to free pool (creates a gap in first logical range)
+;      Close up the gap in logical addressing
+;Post: Restore cursors
+;
+
+; ***********************************************************************************
+; Handlers for the screen dynamic area
+; ***********************************************************************************
+
+;Pregrow entry parameters
+; R0 = 0 (reason code)
+; R1 -> page block (entries set to -1)
+; R2 = number of entries in page block == number of pages area is growing by
+; R3 = number of bytes area is growing by (r2 * pagesize)
+; R4 = current size (bytes)
+; R5 = page size
+;
+; exit with V clear, all preserved
+
+PreGrow_Screen  ENTRY   "r0-r2,r4"
+        LDR     r0, [WsPtr, #CursorFlags]       ; test if VDU inited yet
+        TEQ     r0, #0                          ; if not, CursorFlags will be zero
+        SWINE   XOS_RemoveCursors               ; if VDU inited, then remove cursors
+
+        ADRL    r0, PageShifts-1
+        LDRB    r0, [r0, r5, LSR #12]           ; grab log2Pagesize for shifting
+        MOV     r4, r4, LSR r0                  ; change present size into number of pages
+                                                ; since page numbers are 0 to n-1 thus n
+                                                ; is the first page number we want to insist on
+10
+        STR     r4, [r1], #12                   ; store physical page number and increment to next
+        SUBS    r2, r2, #1                      ; one less to do
+        ADDNE   r4, r4, #1                      ; next physical page number
+        BNE     %BT10                           ; continue until all pages done
+        CLRV                                    ; ok, so I'm paranoid...
+        EXIT
+
+; **********************************************************************
+
+;PostGrow entry parameters
+;R0 = 1 (reason code)
+;R1 -> page block (only physical page numbers are meaningful)
+;R2 = number of entries in page block (= number of pages area grew by)
+;R3 = number of bytes area grew by
+;R4 = new size of area (bytes)
+;R5 = page size
+
+PostGrow_Screen ENTRY   "r0,r5"
+        LDR     r0, [WsPtr, #CursorFlags]       ; test if VDU inited (CursorFlags=0 => not)
+        TEQ     r0, #0
+        BEQ     %FT90                           ; if not inited, do nothing
+
+        MOV     r5, pc
+        TEQP    pc, #SVC_mode+I_bit             ; disable IRQs
+
+        MOV     r0, r3                          ; move number of bytes area grew by into r0
+        BL      InsertPages                     ; only call InsertPages if VDU inited
+
+        TEQP    r5, #0                          ; restore IRQ state
+        SWI     XOS_RestoreCursors              ; and restore cursors
+90
+        CLRV
+        EXIT
+
+; ***********************************************************************
+
+;PreShrink Entry parameters
+;R0 = 2 (reason code)
+;R3 = number of bytes area is shrinking by
+;R4 = current size of area (bytes)
+;R5 = page size
+;R12 = vdu workspace
+
+PreShrink_Screen ENTRY   "R0-R2,R4-R5"
+
+        ;need to check whether the proposed shrink still leaves enough for
+        ;the current amount needed by the vdu drivers, if it doesn't we
+        ;reduce R3 to be the most we can spare (in whole pages)
+
+        SUB     R2, R5, #1                      ;make a page mask
+
+        LDR     R5, [R12, #ScreenSize]          ;get current minimum size
+
+        SUB     R1, R4, R5                      ;R1 = maximum shrink (current - screensize)
+        CMP     R3, R1                          ;if requested shrink > max...
+        MOVHI   R3, R1                          ;...then limit it, and...
+        BICS    R3, R3, R2                      ;...round down to multiple of page size
+        BEQ     %FT10                           ;don't shuffle screen data if resultant
+                                                ;shrink is 0 bytes/0 pages
+        SWI     XOS_RemoveCursors
+        MOV     R5, PC
+        TEQP    PC, #SVC_mode+I_bit             ;disable interrupts
+        RSB     R0, R3, #0                      ;R0= -(number of bytes) for RemovePages
+        BL      RemovePages                     ;entry: R0 = -(number of bytes)
+        TEQP    PC, R5                          ;restore interrupts
+10
+        CLRV
+        EXIT
+
+; ************************************************************************
+
+;PostShrink Entry parameters
+;R0 = 3 (reason code)
+;R3 = number of bytes area shrank by
+;R4 = new size of area (bytes)
+;R5 = page size
+
+PostShrink_Screen ENTRY
+        SWI     XOS_RestoreCursors
+        CLRV                                    ;ok, so I'm paranoid...
+        EXIT
+
+; ************************************************************************
+
+ ]
+
+        END
diff --git a/s/Convrsions b/s/Convrsions
new file mode 100644
index 0000000000000000000000000000000000000000..6c009b9a06fdc14b0eed49b136a83037ba2c02f5
--- /dev/null
+++ b/s/Convrsions
@@ -0,0 +1,394 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL     => Convrsions : varicose number->string routines
+
+despatchConvert ROUT
+
+        MOV     r12, #0                 ; All conversions want initial cnt = 0
+
+        CMP     R11, #OS_ConvertFixedNetStation
+        BGE     EconetStation
+
+        Push    "R1, lr"
+        SUB     R10, R11, #OS_ConvertHex1 + 1
+        BIC     R10, R10, #3
+        ADD     PC, PC, R10
+        B       Hex_Output       ; one digit     : -4
+        B       Hex_Output       ; 2,3,4,8 digits:  0
+        B       Cardinal_Output                  ;  4
+        B       Signed_Output                    ;  8
+        B       Binary_Output                    ; 12
+        B       Cardinal_Spaced_Output           ; 16
+        B       Signed_Spaced_Output             ; 20
+
+Hex_Output ROUT
+
+        SUBS    R11, R11, #OS_ConvertHex1
+        MOVNE   R11, R11, LSL #3
+        MOVEQ   R11, #4
+        MOV     R0, R0, ROR R11
+
+01      MOV     R0, R0, ROR #28
+        AND     R10, R0, #&F
+        CMP     R10, #10
+        ADDGE   R10, R10, #"A"-10
+        ADDLT   R10, R10, #"0"
+        BL      addconvchar
+        BVS     naffconversion
+        SUBS    R11, R11, #4
+        BNE     %BT01
+
+endconversion
+        MOVVC   R10, #0
+        BLVC    addconvchar
+        BVS     naffconversion
+
+        Pull    "R0, lr"
+        SUB     R2, R2, R12
+        ADD     R2, R2, #1              ; null not really a char.
+        SUB     R1, R1, #1
+        ExitSWIHandler
+
+naffconversion
+        ADRL    R0, ErrorBlock_BuffOverflow
+naffconversion_ErrorSet
+      [ International
+        BL      TranslateError
+      ]
+        Pull    "R1, lr"
+        B       SLVK_SetV
+
+Binary_Output ROUT
+
+        SUB     R11, R11, #OS_ConvertBinary1-1
+        MOV     R11, R11, LSL #3
+        MOV     R0, R0, ROR R11
+01      MOV     R0, R0, ROR #31
+        AND     R10, R0, #1
+        ADD     R10, R10, #"0"
+        BL      addconvchar
+        BVS     naffconversion
+        SUBS    R11, R11, #1
+        BNE     %BT01
+        B       endconversion
+
+
+; cardinal output very similar to BinaryToDecimal
+
+Cardinal_Output ROUT
+
+        SUB     R11, R11, #OS_ConvertCardinal1-1
+        MOV     R11, R11, LSL #3
+        MOV     R10, #-1
+        MOV     R10, R10, LSL R11
+        BIC     R0, R0, R10
+        Push    "R3-R5"
+        ADRL    R3, TenTimesTable
+        MOV     R5, #9                  ; max entry
+        MOV     R4, #0                  ; non-0 had flag
+02      LDR     R11, [R3, R5, LSL #2]
+        MOV     R10, #"0"-1             ; digit value
+03      SUBS    R0, R0, R11
+        ADD     R10, R10, #1
+        BCS     %BT03
+        ADD     R0, R0, R11
+        CMP     R10, #"0"
+        CMPEQ   R4, #0
+        BNE     %FT04                   ; put digit
+
+05      SUBS    R5, R5, #1
+        BPL     %BT02                   ; next digit
+        CMP     R4, #0
+        BEQ     %FT04                   ; R5 must be 0
+        Pull    "R3-R5"
+        B       endconversion
+
+04      MOV     R4, #-1
+        BL      addconvchar
+        BVC     %BT05
+        Pull    "R3-R5"
+        B       naffconversion
+
+
+Signed_Output ROUT
+
+        SUB     R11, R11, #OS_ConvertInteger1-1
+        MOV     R11, R11, LSL #3
+        AND     R11, R11, #31
+        RSB     R11, R11, #32
+        AND     R11, R11, #31
+        MOV     R0, R0, LSL R11
+        MOV     R0, R0, ASR R11
+        MOV     R12, R2
+        SWI     XOS_BinaryToDecimal
+        MOVVS   r2, r12
+        ADDVC   R1, R1, R2
+        Swap    R2, R12, VC
+        B       endconversion
+
+
+Cardinal_Spaced_Output ROUT
+Signed_Spaced_Output
+
+        Push    "R1, R2"
+
+; copy our code into the stack (!!)
+        ADR     R1, code_segment
+        LDMIA   R1, {R1, R2, R12}
+        ADD     R2, R2, R11
+        Pull    "R10, R11"
+        Push    "R1, R2, R12"
+
+        SUB     sp, sp, #12             ; get 12 byte buffer
+        MOV     R1, sp
+        MOV     R2, #12
+        MOV     lr, pc
+        ADD     pc, sp, #12             ; oh for an "execute" instruction!
+                                     ; note can't get VSet back from this "SWI"
+        RSB     R0, R2, #12            ; bytes got
+        MOV     R1, R10
+        MOV     R2, R11
+        MOV     R12, #0
+        MOV     R11, sp
+01      LDRB    R10, [R11], #1
+        BL      addconvchar
+        BVS     space_conv_exit
+        SUBS    R0, R0, #1
+        BEQ     space_conv_exit
+        CMP     R10, #"-"
+        BEQ     %BT01
+        CMP     R0, #3
+        CMPNE   R0, #6
+        CMPNE   R0, #9
+        BNE     %BT01
+        MOV     R10, #" "
+        BL      addconvchar
+        BVC     %BT01
+
+space_conv_exit
+        ADD     sp, sp, #12+12
+        B       endconversion
+
+code_segment
+        Push    "lr"
+        SWI     XOS_ConvertCardinal1 - OS_ConvertSpacedCardinal1
+        Pull    "PC"
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; R1 current buffer pos
+; R12 character count, R2 character limit
+; R10 character
+
+addconvchar ROUT
+
+        CMP     R2, R12
+        ORRLES  pc, lr, #V_bit
+
+        ADD     R12, R12, #1
+        STRB    R10, [R1], #1
+        BICS    pc, lr, #V_bit
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; EcoNet conversions
+
+EconetStation ROUT
+
+        CMP     R11, #OS_ConvertFixedFileSize
+        BGE     FileSizeConversion
+
+        Push    "R1, lr"
+        Push    "R0"
+        LDR     R0, [R0, #4]
+        CMP     R0, #256
+        BHS     invalidnetworknumber
+        MOV     R10, #" "
+        BL      doabyte
+        BVS     %FT01
+        CMP     R10, #"0"
+        MOVEQ   R10, #"."
+        CMPNE   R11, #OS_ConvertFixedNetStation
+        BLEQ    addconvchar
+        CMP     R10, #"."
+        MOVEQ   R10, #"0"
+
+01
+        Pull    "R0"
+        BVS     naffconversion
+        LDR     R0, [R0]
+        CMP     R0, #0
+        CMPNE   R0, #256
+        BHS     invalidstationnumber
+        BL      doabyte
+        B       endconversion
+
+invalidnetworknumber
+        INC     sp, 4                           ; Pull    "R0"
+        ADR     R0, ErrorBlock_BadNetwork
+        B       naffconversion_ErrorSet
+
+invalidstationnumber
+        ADR     R0, ErrorBlock_BadStation
+        B       naffconversion_ErrorSet
+
+ErrorBlock_BadNetwork
+        DCD     ErrorNumber_BadNetwork
+        DCB     "BadNet"                        ; The token for the Global message
+        DCB     0
+        ALIGN
+
+ErrorBlock_BadStation
+        DCD     ErrorNumber_BadStation
+        DCB     "BadStn"                        ; The token for the Global message
+        DCB     0
+        ALIGN
+
+doabyte ROUT
+ ; R0 is byte, R11 SWI number (to indicate pad or not)
+ ; return VS for oflo
+        Push    "lr"
+        CMP     R11, #OS_ConvertNetStation
+        BEQ     %FT03
+        CMP     R0, #100
+        BGE     %FT03
+        BL      addconvchar
+        Pull    "PC", VS
+02      CMP     R0, #10
+        BGE     %FT03
+        BL      addconvchar
+        Pull    "PC", VS
+03      CMP     R0, #0
+        BNE     %FT01
+        CMP     R11, #OS_ConvertNetStation
+        Pull    "PC", EQ
+        Pull    "lr"
+        B       addconvchar
+
+01      MOV     R10, R2
+        SUB     R2, R2, R12         ; bytes left
+        SWI     XOS_BinaryToDecimal
+        ADD     R12, R12, R2
+        ADD     R1, R1, R2
+        MOV     R2, R10
+        MOV     R10, #"0"
+        Pull    "PC"
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Do length as xxxx bytes or xxxx Kbytes or xxxx Mbytes
+
+; In    r0 = size in bytes
+;       r1 -> buffer
+;       r2 = buffer length
+;       r12 = 0
+
+; Out   r0 = r1in
+;       r1 -> terminating zero
+;       r2 = amount of buffer left
+
+FileSizeConversion ROUT
+
+        Push    "r1, lr"
+
+        Push    "r4-r7"
+        SUB     sp, sp, #16             ; May need temp frame
+
+        MOV     r4, #0                  ; No char by default
+        CMP     r0, #4096               ; Only go to 'K' format if > 4K
+        MOVCS   r4, #"K"                ; Kilo
+        MOVCSS  r0, r0, LSR #10         ; /1024
+        ADC     r0, r0, #0              ; Round up iff divided and bit fell out
+        CMP     r0, #4096               ; Only go to 'M' format if > 4M
+        MOVCS   r4, #"M"                ; Mega
+        MOVCSS  r0, r0, LSR #10         ; /1024 again
+        ADC     r0, r0, #0              ; Round up iff divided and bit fell out
+
+; No need to go to 'G' format as 2^32 = 4096M!
+
+        MOV     r5, r0                  ; Remember for prettiness
+
+        CMP     r11, #OS_ConvertFixedFileSize
+        BNE     %FT50
+
+        Push    "r1, r2"                ; Remembering state
+        ADD     r1, sp, #4*2            ; Point to our temp buffer
+        MOV     r2, #16
+        SWI     XOS_BinaryToDecimal     ; This will not give error
+        MOV     r7, r2                  ; Number of chars to do
+        RSBS    r6, r2, #4              ; Number of spaces needed
+        Pull    "r1, r2"
+        BLE     %FT39
+
+30      MOV     r10, #" "
+        BL      addconvchar
+        BVS     %FA95
+        SUBS    r6, r6, #1
+        BNE     %BT30
+
+39      MOV     r6, sp                  ; Stick string in punter's buffer
+40      LDRB    r10, [r6], #1
+        BL      addconvchar
+        BVS     %FA95
+        SUBS    r7, r7, #1
+        BNE     %BT40
+
+        B       %FT60
+
+
+50      MOV     r12, r2                 ; No padding on LHS, easy case
+        SWI     XOS_BinaryToDecimal
+        MOVVS   r2, r12
+        ADDVC   r1, r1, r2
+        Swap    r2, r12, VC
+
+60      MOVVC   r10, #" "
+        BLVC    addconvchar
+        BVS     %FA95
+
+        MOVS    r10, r4                 ; Char to print ? VClear
+        BNE     %FT70
+
+        CMP     r11, #OS_ConvertFixedFileSize ; VClear
+        BNE     %FT75
+
+        MOV     r10, #" "               ; Need to pad in middle
+
+70      BL      addconvchar
+
+75      MOVVC   r10, #"b"               ; 'byte'
+        BLVC    addconvchar
+        MOVVC   r10, #"y"
+        BLVC    addconvchar
+        MOVVC   r10, #"t"
+        BLVC    addconvchar
+        MOVVC   r10, #"e"
+        BLVC    addconvchar
+        BVS     %FA95
+
+        CMP     r5, #1                  ; Prettify (unpluralisationism). VClear
+        MOVNE   r10, #"s"
+        BNE     %FT90
+
+        CMP     r11, #OS_ConvertFixedFileSize ; VClear
+        BNE     %FA95
+        MOV     r10, #" "               ; Need to pad to right
+
+90      BL      addconvchar
+
+95      ADD     sp, sp, #16
+        Pull    "r4-r7"
+        B       endconversion
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        END
diff --git a/s/ExtraSWIs b/s/ExtraSWIs
new file mode 100644
index 0000000000000000000000000000000000000000..f5d13ac0a23323a956b19a50e53cdbe177f6ae60
--- /dev/null
+++ b/s/ExtraSWIs
@@ -0,0 +1,81 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > ExtraSWIs
+
+ [ ProcessorVectors
+
+;----------------------------------------------------------------------------------------
+; ClaimProcVecSWI
+;
+;       In:     r0 = vector and flags
+;                       bit     meaning
+;                       0-7     vector number
+;                               0 = 'Branch through 0' vector
+;                               1 = Undefined instruction
+;                               2 = SWI
+;                               3 = Prefetch abort
+;                               4 = Data abort
+;                               5 = Address exception (only on ARM 2 & 3)
+;                               6 = IRQ
+;                               7+ = reserved for future use
+;                       8       0 = release
+;                               1 = claim
+;                       9-31    reserved (set to 0)
+;               r1 = replacement value
+;               r2 = value which should currently be on vector (only needed for release)
+;
+;       Out:    r1 = value which has been replaced (only returned on claim)
+;
+;       Allows a module to attach itself to one of the processor vectors.
+;
+ClaimProcVecSWI ROUT
+        ENTRY   "r3-r5"
+
+        AND     r3, r0, #&FF            ; Get vector number.
+        CMP     r3, #(ProcVec_End-ProcVec_Start):SHR:2
+        ADRCSL  r0, ErrorBlock_BadClaimNum
+        BCS     %FT30
+
+        MOV     r4, r1                  ; r4 = replacement value
+        LDR     r5, =ProcVec_Start
+        PHPSEI                          ; Disable IRQs while we mess around with vectors.
+
+        TST     r0, #1:SHL:8
+        LDRNE   r1, [r5, r3, LSL #2]!   ; If claiming then return current value (r5->vector to replace).
+        BNE     %FT10
+
+        LDR     r3, [r5, r3, LSL #2]!   ; Releasing so get current value (r5->vector to replace).
+        TEQ     r2, r3                  ; Make sure it's what the caller thinks it is.
+        ADRNEL  r0, ErrorBlock_NaffRelease
+        BNE     %FT20
+10
+        STR     r4, [r5]                ; Store replacement value.
+        PLP                             ; Restore IRQs.
+        PullEnv
+        ExitSWIHandler
+
+20
+        PLP                             ; Restore IRQs and return error.
+30
+  [ International
+        BL      TranslateError
+  ]
+        PullEnv
+        B       SLVK_SetV
+
+ ]
+
+
+        END
diff --git a/s/GetAll b/s/GetAll
new file mode 100644
index 0000000000000000000000000000000000000000..998f54d97d089645eac0aaed6b91eb76e6895b32
--- /dev/null
+++ b/s/GetAll
@@ -0,0 +1,618 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > GetAll
+
+        GET     Hdr:ListOpts
+        GET     Hdr:Macros
+        GET     Hdr:System
+        GET     Hdr:Machine.<Machine>
+        GET     Hdr:ImageSize.<ImageSize>
+        $GetCPU
+        $GetIO
+        $GetMEMC
+        $GetMEMM
+        $GetVIDC
+
+; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; now the conditional flags for the version we want
+; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+; TMD 29-Apr-93: Fix1 conditioning removed for simplicity of code - always true
+
+     [ {TRUE}
+     ! 0, "Fix1: interrupts re-enabled in ChangeDynamicArea"
+     ]
+
+     GBLL Fix2
+Fix2 SETL {TRUE}
+     [ Fix2
+     ! 0, "Fix2: TMD optimisations of heap manager"
+     ]
+
+     GBLL Fix3
+Fix3 SETL {TRUE}
+     [ Fix3
+     ! 0, "Fix3: ExtendHeap stack balanced"
+     ]
+
+     GBLL Fix4
+Fix4 SETL {TRUE}
+     [ Fix4
+     ! 0, "Fix4: ExtendBlock IRQ latency improved"
+     ]
+
+     GBLL Fix5
+Fix5 SETL {TRUE}
+     [ Fix5
+     ! 0, "Fix5: SpriteOp made re-entrant"
+     ]
+
+     GBLL Fix6
+Fix6 SETL {TRUE}
+     [ Fix6
+     ! 0, "Fix6: OS_Byte &87 restores caller's IRQ state"
+     ]
+
+     GBLL Fix7
+Fix7 SETL {TRUE}
+     [ Fix7
+     ! 0, "Fix7: OS_Word &0E,0 enables IRQs"
+     ]
+
+     GBLL Fix8
+Fix8 SETL {TRUE}
+     [ Fix8
+     ! 0, "Fix8: OS_Word &15,0 enables IRQs"
+     ]
+
+     GBLL Fix9
+Fix9 SETL {TRUE}
+     [ Fix9
+     ! 0, "Fix9: Incarnation names not terminated by 1st character"
+     ]
+
+     GBLL Fix10
+Fix10 SETL {TRUE}
+     [ Fix10
+     ! 0, "Fix10: *Unplug terminated by address bug fixed"
+     ]
+
+     GBLL Fix11
+Fix11 SETL {TRUE}
+     [ Fix11
+     ! 0, "Fix11: Podule IRQ despatcher doesn't corrupt R0"
+     ]
+
+     GBLL Fix12
+Fix12 SETL {TRUE}
+     [ Fix12
+     ! 0, "Fix12: Rename incarnation fixed"
+     ]
+
+; TMD 04-Sep-89: Fix bug in prefer incarnation - corrupted error pointer if
+; module or incarnation didn't exist
+
+     GBLL Fix13
+Fix13 SETL {TRUE}
+     [ Fix13
+     ! 0, "Fix13: Prefer incarnation fixed"
+     ]
+
+; TMD 06-Sep-89: Fix bug in CallAfter/Every - the error pointer was corrupted
+; (errors caused by supplying non-positive time interval, or by being unable to
+; claim a node from the system heap)
+
+     GBLL Fix14
+Fix14 SETL {TRUE}
+     [ Fix14
+     ! 0, "Fix14: CallAfter/Every error pointer not corrupted"
+     ]
+
+; TMD 11-Sep-89: Fix bug in AddCallBack - freed wrong heap node when chaining
+; down the vector
+
+     GBLL Fix15
+Fix15 SETL {TRUE}
+     [ Fix15
+     ! 0, "Fix15: AddCallBack frees correct heap node"
+     ]
+
+; TMD 25-Sep-89: Fix bug in GSRead quoted termination - started skipping spaces
+; from the wrong character, and didn't adjust for post increment after loading
+; first non-space.
+
+     GBLL Fix16
+Fix16 SETL {TRUE}
+     [ Fix16
+     ! 0, "Fix16: GSRead quoted termination fixed"
+     ]
+
+; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; essential global variables
+; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+                GET   Version
+                GBLS  VersionNo
+VersionNo       SETS  "$VString ($Date)"
+
+                GBLS  SystemName
+SystemName      SETS  "RISC OS" ; ", p.k.a. Arthur a.k.a. Richard III"
+
+                GBLS  MosTitle
+MosTitle        SETS  "$SystemName $VersionNo"
+
+                GBLL  AddTubeBashers
+AddTubeBashers  SETL  {FALSE}
+
+Tube_Normal     *       1
+Tube_Simulator  *       2
+
+                GBLA    TubeType
+TubeType        SETA    Tube_Simulator
+
+UserMemStart    * &8000
+
+                GBLL  ExceptionsAreErrors
+ExceptionsAreErrors SETL  1=1
+
+AssemblingArthur SETL  {TRUE}
+; defined in hdr.system to allow conditionals in macros
+
+                GBLL    DoingVdu
+DoingVdu        SETL    {FALSE}         ; so can get KeyWS!
+                GBLL    Module
+Module          SETL    {FALSE}
+
+                GBLL    IncludeTestSrc  ; whether test code is included
+ [ MorrisSupport
+IncludeTestSrc  SETL    {TRUE}
+ |
+IncludeTestSrc  SETL    :LNOT: (MEMM_Type = "MEMC2") ; not on internal test versions
+ ]
+
+                GBLL    NormalSpeedROMS
+NormalSpeedROMS SETL    {TRUE}          ;use FALSE for slow EPROMS
+
+                GBLL    RISCPCBurstMode
+RISCPCBurstMode SETL    {FALSE}
+;>>>RCM says if the FRM approves the use of burst mode ROMS for
+;>>>RISC PC (no reason why it shouldn't) all references to RISCPCBurstMode
+;>>>could be replaced by NormalSpeedROMS
+
+                GBLL    Select16BitSound
+Select16BitSound SETL   {TRUE}
+
+                GBLL    Simulator               ; running on simulator?
+Simulator       SETL    {FALSE}
+
+                  GBLL   ChopOffTheGoolies
+ChopOffTheGoolies SETL  {FALSE}
+
+                GBLL   ChecksumCMOS
+ChecksumCMOS    SETL  {TRUE}
+
+                GBLL    ResetIndirected ; new flag to say if instruction at start of ROM does LDR PC, [PC, #x]
+ResetIndirected SETL    {TRUE}
+
+                GBLL    SqueezeMods     ; whether squeezed modules are allowed
+SqueezeMods     SETL    {TRUE}
+
+                GBLL    DriversInKernel ; whether serial/parallel drivers are in the kernel
+DriversInKernel SETL    {FALSE}
+
+                GBLL    International   ; whether text and error messages come from  messaeges file.
+International   SETL    {TRUE}
+
+                GBLL    MouseBufferManager      ; Whether mouse uses buffer manager
+MouseBufferManager      SETL    {TRUE}
+
+                GBLL    IrqsInClaimRelease      ; Whether OS_Claim/Release restore IRQ's before releasing heap node
+IrqsInClaimRelease      SETL  {TRUE}
+
+                GBLL    TickIrqReenter          ; Whether TickEventChain processing re-enables IRQ's
+TickIrqReenter  SETL    {TRUE}
+
+                GBLL    SoftResets              ; If false, always force a hard reset
+SoftResets      SETL    {FALSE}
+
+                GBLL    AlwaysClearRAM          ; If true, clear RAM on every break/reset
+AlwaysClearRAM  SETL    {TRUE}
+
+                GBLL    CacheCMOSRAM            ; Whether to keep a RAM copy of CMOS RAM for faster access
+CacheCMOSRAM    SETL    MEMM_Type = "ARM600"    ; (Space only allocated on ARM600 versions)
+
+                GBLL    GammaCorrection
+GammaCorrection SETL    (VIDC_Type = "VIDC20") :LAND: {TRUE}
+
+                GBLL    VIDCListType3
+VIDCListType3   SETL    (VIDC_Type = "VIDC20") :LAND: {TRUE}
+
+                GBLL    ExpandedCamMap          ; two words per entry instead of one
+ExpandedCamMap  SETL    MEMM_Type = "ARM600"    ; NB ARM600 code assumes expanded map
+
+                GBLL    UseFreePool             ; whether OS_ChangeDynamicArea puts and gets memory to and from free pool
+UseFreePool     SETL    MEMM_Type = "ARM600" :LAND: {TRUE}
+
+                GBLL    NewCDA                  ; new change dynamic area code
+NewCDA          SETL    MEMM_Type = "ARM600" :LAND: {TRUE}    ; let's give it a try!
+
+                GBLL    ModeSelectors           ; whether mode selectors are understood
+ModeSelectors   SETL    (VIDC_Type = "VIDC20") :LAND: {TRUE}
+
+                GBLL    MakeModeSelectorsForModeNumbers
+MakeModeSelectorsForModeNumbers SETL    ModeSelectors :LAND: {FALSE}    ; not actually needed after all
+
+                GBLL    IgnoreVRAM              ; if true, don't add VRAM to the RAM list (+ don't use for screen)
+IgnoreVRAM      SETL    {FALSE}
+
+                GBLL    LateAborts              ; if true, use late abort mode on ARM600 (compulsory on ARM700)
+LateAborts      SETL    MEMM_Type = "ARM600" :LAND: {TRUE}
+
+                GBLL    CheckProtectionLink     ; if true, disallow CMOS RAM changes if link in protected position
+CheckProtectionLink SETL (IO_Type = "IOMD") :LAND: {TRUE}       ; NB affects Delete/Copy/R/T and 0-9/.
+
+                GBLL    RMTidyDoesNowt          ; if true, RMTidy does nothing
+RMTidyDoesNowt  SETL    (MEMC_Type = "IOMD") :LAND: {TRUE} ; should really be "machine has FSLock in ROM"
+
+                GBLL    RogerEXEY               ; if true, use Roger's new algorithm for XEigFactor, YEigFactor
+RogerEXEY       SETL    {FALSE}                 ; Marketing don't like it!
+
+                GBLL    DAF_SpecifyBit          ; enable use of dynamic area flag which says an area may need specific pages
+DAF_SpecifyBit  SETL    {TRUE}
+
+                GBLL    DebugROMInit
+DebugROMInit    SETL    (MEMC_Type = "IOMD") :LAND: {FALSE}
+
+                GBLL    DebugROMErrors
+DebugROMErrors  SETL    (MEMC_Type = "IOMD") :LAND: {FALSE}
+
+                GBLL    DebugHeaps              ; initialise claimed and freed blocks
+DebugHeaps      SETL    {FALSE}                 ; (may slow things down unacceptably)
+
+; ChangeDynamicArea and related options
+
+        GBLL    DebugCDA
+DebugCDA SETL {FALSE}
+
+        GBLL    DebugCDA2
+DebugCDA2 SETL {FALSE}
+
+        GBLL    NewCDA2                 ; whether all the new CDA code is in there
+NewCDA2 SETL NewCDA :LAND: {TRUE}
+
+        GBLL    NewStyle_RMA            ; whether RMA is a new style area
+NewStyle_RMA    SETL NewCDA :LAND: {TRUE}
+
+        GBLL    NewStyle_SpriteArea     ; whether sprite area is a new style area
+NewStyle_SpriteArea     SETL    NewCDA :LAND: {TRUE}
+
+        GBLL    NewStyle_RAMDisc        ; whether RAM disc is a new style area
+NewStyle_RAMDisc        SETL    NewCDA :LAND: {TRUE}
+
+        GBLL    NewStyle_FontArea       ; whether font cache is a new style area
+NewStyle_FontArea       SETL    NewCDA :LAND: {TRUE}
+
+        GBLL    NewStyle_SysHeap        ; whether system heap is a new style area (node faked up)
+NewStyle_SysHeap        SETL    NewCDA :LAND: {TRUE}
+
+        GBLL    NewStyle_Screen         ; whether screen is a new style area
+NewStyle_Screen         SETL    NewCDA :LAND: {TRUE}
+
+        GBLL    NewStyle_All            ; whether all old-style areas have been converted to new-style
+NewStyle_All    SETL    NewStyle_RMA :LAND: NewStyle_SpriteArea :LAND: NewStyle_RAMDisc :LAND: NewStyle_FontArea :LAND: NewStyle_SysHeap :LAND: NewStyle_Screen
+
+                GBLL    StorkPowerSave  ;True => power saving for Stork AND A4
+StorkPowerSave  SETL    MorrisSupport   ;False=> older A4 code only
+
+                GBLL    FixR9CorruptionInExtensionSWI   ; whether R9 corruption by ExtensionSWI handler is fixed
+FixR9CorruptionInExtensionSWI   SETL    {FALSE}         ; currently FALSE as CC's !SpellMod (possibly others) rely on it being broken
+
+
+              [ DebugHeaps
+                ! 0, "*** WARNING *** Heap debugging assembled in"
+              ]
+
+                GBLS    GetMessages
+              [ International
+GetMessages     SETS    "GET s.MsgCode"
+              |
+GetMessages     SETS    ""
+              ]
+
+                GBLL    DebugForcedReset        ; debug forced hard resets
+DebugForcedReset SETL   {FALSE}
+
+                GBLA    ConfiguredLang
+ConfiguredLang  SETA    10                      ; default configured language
+
+                GBLA    FirstUnpluggableModule
+FirstUnpluggableModule SETA 8                   ; Podule, FileSwitch, ResourceFS, Messages, MessageTrans,
+                                                ; TerritoryManager, UKTerritory
+
+ [ DebugForcedReset
+Reset_CannotResetFlag           * 1
+Reset_SysHeapCorrupt            * 2
+Reset_WrongCamMapAddress        * 3
+Reset_WrongNumberOfPages        * 4
+Reset_CamMapCorrupt             * 5
+Reset_VectorChainCorrupt        * 6
+Reset_TickNodesCorrupt          * 7
+Reset_DeviceVectorCorrupt       * 8
+Reset_PoduleOrCallBackCorrupt   * 9
+ ]
+
+; Flags for RISC OS Blue changes
+;
+                        GBLL    AssembleA1KeyHandler
+AssembleA1KeyHandler    SETL    {FALSE}
+                        GBLL    AssembleKEYV
+AssembleKEYV            SETL    {TRUE}          ; Use KEYV.
+                        GBLL    AssemblePointerV
+AssemblePointerV        SETL    {TRUE}          ; Use PointerV.
+                        GBLL    PollMouse
+PollMouse               SETL    {FALSE}         ; Poll mouse.
+
+                        GBLL    ProcessorVectors
+ProcessorVectors        SETL    {TRUE}          ; Processor vectors indirected through 0 page.
+
+                    GBLS  GetUnsqueeze
+ [ SqueezeMods
+GetUnsqueeze        SETS  "GET s.Unsqueeze"
+ |
+GetUnsqueeze        SETS  ""
+ ]
+                    GBLS  GetPublicWS
+                    GBLS  GetWorkspace
+                    GBLS  GetKernelMEMC
+                    GBLS  GetPalette
+                    GBLS  GetMemInfo
+ [ MEMM_Type = "ARM600"
+GetPublicWS         SETS  "GET Hdr:PublicWS"
+GetWorkspace        SETS  "GET Hdr:KernelWS"
+GetKernelMEMC       SETS  "GET s.ARM600"
+GetMemInfo          SETS  "GET s.MemInfo"
+ |
+GetPublicWS         SETS  ""
+GetWorkspace        SETS  "GET Hdr:Old.NewSpace"
+  [ MEMM_Type = "MEMC2"
+GetKernelMEMC       SETS  "GET s.MEMC2"
+  |
+GetKernelMEMC       SETS  "GET s.MEMC1"
+  ]
+GetMemInfo          SETS  ""
+ ]
+
+ [ VIDC_Type = "VIDC20"
+GetPalette          SETS  "GET s.Vdu.VduPal20"
+ |
+GetPalette          SETS  "GET s.Vdu.VduPal10"
+ ]
+
+
+                    GBLS    GetRS423
+ [ DriversInKernel
+GetRS423            SETS  "GET s.PMF.rs423"
+ |
+GetRS423            SETS  ""
+ ]
+
+                    GBLS  GetKbdDrA1
+ [ Keyboard_Type = "A1A500"
+GetKbdDrA1          SETS  "GET s.PMF.KbdDrA1"
+ |
+GetKbdDrA1          SETS  ""
+ ]
+
+                    GBLS  GetKbdRes
+ [ Keyboard_Type = "PC"
+GetKbdRes           SETS  "GET s.KbdResPC"
+ |
+GetKbdRes           SETS  "GET s.KbdResA1"
+ ]
+
+                    GBLS  GetKey2
+ [ AssembleA1KeyHandler
+GetKey2             SETS  "GET s.PMF.Key2"
+ |
+GetKey2             SETS  ""
+ ]
+
+; control switches for med_00001 (the flood fill routines 1024 line limit).
+; Switches have the following effects:
+;
+; _userma     Will use rma if >48K is free, up to a maximum of 128K. It will
+;             try to acheive the latter by growing the rma if possible.
+; _twowords   Use two word entries in the queue. This overcomes the limitation
+;             of the original packed word format.
+; _debug      Store the queue start, end and 'amount to change the rma dynamic
+;             area by' in the first three words of OldIRQ1VSpace
+
+                    GBLL  med_00001_userma
+                    GBLL  med_00001_twowords
+                    GBLL  med_00001_debug
+
+med_00001_userma    SETL  {TRUE}
+med_00001_twowords  SETL  {TRUE}
+;med_00001_debug     SETL  {TRUE}
+
+;med_00001_userma    SETL  {FALSE}
+;med_00001_twowords  SETL  {FALSE}
+med_00001_debug     SETL  {FALSE}
+
+ [ med_00001_userma
+smallest_rma_size   * (48*1024)                  ; define the low threshold for rma use
+largest_rma_size    * (128*1024)                 ; and the ceiling for rma use
+ ]
+
+ [ med_00001_debug
+ ! 0,""
+ ! 0,",-----------------------------------------------------------------,"
+ ! 0,"| **** WARNING ****                                               |"
+ ! 0,"|                                                                 |"
+ ! 0,"| Audit trail debugging for MED-00001 is enabled. This reuses the |"
+ ! 0,"| first three words of OldIRQ1Vspace. This should be turned off   |"
+ ! 0,"| once MED-00001 has been tested and marked 'fixed'.              |"
+ ! 0,"|                                                                 |"
+ ! 0,"| Usage:                                                          |"
+ ! 0,"|   +0   start of area used by flood fill                         |"
+ ! 0,"|   +4   end+1 of area used by flood fill                         |"
+ ! 0,"|   +8   amount the rma was grown by                              |"
+ ! 0,"'-----------------------------------------------------------------'"
+ ! 0,""
+ ]
+
+; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; now get the headers
+; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        GET     Hdr:CMOS
+        GET     Hdr:Heap
+        $GetPublicWS
+        $GetWorkspace
+        GET     Hdr:Services
+        GET     Hdr:FSNumbers
+        GET     Hdr:HighFSI
+        GET     Hdr:NewErrors
+        GET     Hdr:Proc
+        GET     Hdr:Sprite
+        GET     Hdr:KeyWS
+        GET     Hdr:RS423
+        GET     Hdr:ModHand
+        GET     Hdr:Variables
+        GET     Hdr:EnvNumbers
+        GET     Hdr:UpCall
+        GET     Hdr:Sound
+        GET     Hdr:Pointer
+        GET     Hdr:Podule
+        GET     Hdr:VduExt
+;        GET     Hdr:Fox
+        GET     Hdr:Buffer
+        GET     Hdr:Font
+        GET     Hdr:DevNos
+;        GET     Hdr:IOEB
+        GET     Hdr:Territory
+        GET     Hdr:Portable
+        GET     Hdr:MsgTrans
+        GET     Hdr:PaletteV
+        GET     Hdr:Wimp
+        GET     Hdr:ColourTran
+        GET     Hdr:Debug
+        GET     s.PMF.DEF          ; Common with 6502 code in the keyboard
+        Protocol
+
+; now the main parts of the MOS
+
+        GET     s.Kernel
+        GET     s.NewIRQs
+        GET     s.Oscli
+        GET     s.SysComms
+        GET     s.HeapMan
+        GET     s.ModHand
+        $GetUnsqueeze
+        GET     s.ArthurSWIs
+        GET     s.ChangeDyn
+        GET     s.Arthur2
+        GET     s.Utility
+        GET     s.MoreComms
+        GET     s.Convrsions
+        GET     s.MoreSWIs
+        GET     s.ExtraSWIs
+        GET     s.HeapSort
+        GET     s.Arthur3
+        GET     s.SWINaming
+        GET     s.TickEvents
+        $GetKbdRes
+        GET     s.NewReset
+        $GetMessages
+        GET     s.Middle
+        GET     s.Super1
+        $GetKernelMEMC
+        $GetMemInfo
+        ! 0, "Main kernel size = &" :CC: :STR: (.-ROM)
+StartOfVduDriver
+        GET     s.vdu.VduDriver
+        GET     s.vdu.VduSWIs
+        GET     s.vdu.VduPalette
+        $GetPalette
+        GET     s.vdu.VduPlot
+        GET     s.vdu.VduGrafA
+        GET     s.vdu.VduGrafB
+        GET     s.vdu.VduGrafC
+        GET     s.vdu.VduGrafD
+        GET     s.vdu.VduGrafE
+        GET     s.vdu.VduGrafF
+        GET     s.vdu.VduGrafG
+        GET     s.vdu.VduGrafH
+        GET     s.vdu.VduGrafI
+        GET     s.vdu.VduGrafJ
+        GET     s.vdu.VduGrafK
+        GET     s.vdu.VduGrafL
+        GET     s.vdu.VduWrch
+        GET     s.vdu.Vdu23
+        GET     s.vdu.VduPointer
+        GET     s.vdu.Vdu5
+        GET     s.vdu.VduCurSoft
+        GET     s.vdu.VduTTX
+
+        GBLS    GiveMeBfontAnyDay
+        [ BleedinDaveBell
+GiveMeBfontAnyDay SETS "GET s.vdu.VduFontL1"
+        |
+GiveMeBfontAnyDay SETS "GET s.vdu.VduFont"
+        ]
+
+        $GiveMeBfontAnyDay
+
+        ! 0, "Vdu drivers size = &" :CC: :STR: (.-StartOfVduDriver)
+
+StartOfPMF
+        GET     s.PMF.osinit
+        GET     s.PMF.oseven
+        GET     s.PMF.osbyte
+        GET     s.PMF.osword
+        GET     s.PMF.realtime
+        GET     s.PMF.convdate
+        $GetRS423
+        GET     s.PMF.i2cutils
+        GET     s.PMF.oswrch
+        GET     s.PMF.buffer
+        $GetKbdDrA1
+        GET     s.PMF.key
+        $GetKey2
+        GET     s.PMF.mouse
+        ALIGN
+EndOfKernel
+        &       0                       ; for patching by BigSplit et al
+
+        ! 0, "PMF section size = &" :CC: :STR: (.-StartOfPMF)
+
+ [ med_00001_debug
+ ! 0,""
+ ! 0,",-----------------------------------------------------------------,"
+ ! 0,"| **** WARNING ****                                               |"
+ ! 0,"|                                                                 |"
+ ! 0,"| Audit trail debugging for MED-00001 is enabled. This reuses the |"
+ ! 0,"| first three words of OldIRQ1Vspace. This should be turned off   |"
+ ! 0,"| once MED-00001 has been tested and marked 'fixed'.              |"
+ ! 0,"|                                                                 |"
+ ! 0,"| Usage:                                                          |"
+ ! 0,"|   +0   start of area used by flood fill                         |"
+ ! 0,"|   +4   end+1 of area used by flood fill                         |"
+ ! 0,"|   +8   amount the rma was grown by                              |"
+ ! 0,"'-----------------------------------------------------------------'"
+ ! 0,""
+ ]
+
+        END
diff --git a/s/HeapMan b/s/HeapMan
new file mode 100644
index 0000000000000000000000000000000000000000..08065f3ca64db371e071cf230fb4f352ffa4c751
--- /dev/null
+++ b/s/HeapMan
@@ -0,0 +1,1914 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL     => HeapMan : Heap Allocation SWI
+
+; Interruptible heap SWI.
+
+; Look down the IRQ stack to see if anybody was in a heap operation.
+; If they were, then (with IRQs off) the foreground call is done first, by
+; picking up info from a fixed block. Patch the IRQ stack so that the heap SWI
+; is returned to at a "it happened in the background" fixup routine. Current
+; request can then be dealt with! Ta Nick.
+
+; Also has an interlock on the register restore area; otherwise anybody
+; with an IRQ process doing heap ops with interrupts enabled will cause
+; trouble.
+
+                    GBLS HeapBadAsModuleBRA
+                    GBLS HeapBadAsModuleKET
+HeapBadAsModuleBRA SETS "[ {FALSE}" ; "" for modular testing version
+HeapBadAsModuleKET SETS "]"          ; "" for modular testing version
+
+              GBLS RecursiveHeap
+RecursiveHeap SETS "XOS_Heap"
+
+     MACRO
+$l   jeep $r,$var
+   [ Module
+$l   ADR  $r, $var
+   |
+$l   LDR $r, =$var
+   ]
+     MEND
+
+              GBLL TubeInfo
+TubeInfo      SETL {FALSE}
+
+   $HeapBadAsModuleBRA
+
+     GET Hdr:Listopts
+     GET Hdr:Macros
+     GET Hdr:System
+     GET Hdr:ModHand
+     GET Hdr:Debug
+     GET Hdr:Heap
+     GET Hdr:NewErrors
+
+BranchToSWIExit *  0
+IRQsema         *  &108
+
+; debug macro: set the border colour
+
+      MACRO
+$l    SetBorder  $reg1, $reg2, $red, $green, $blue
+  ! 0, "Setborder used"
+$l    LDR        $reg1, =VIDC
+      LDR        $reg2, =&40000000+ $red + $green *16 + $blue *256
+      STR        $reg2, [$reg1]
+      MEND
+
+        MACRO
+        assert  $condition
+ [ :LNOT: ($condition)
+ ! 1,"Assert failed: $condition"
+ ]
+       MEND
+
+        GBLL Module
+Module  SETL {TRUE}
+
+     LEADR Module_LoadAddr
+
+HeapTestModule ROUT
+           &   0
+           &   0
+           &   0
+           &   0
+           &   Title-HeapTestModule
+           &   Title-HeapTestModule
+           &   0
+           &   1024
+
+RecursiveHeap SETS "1024 + (1:SHL:17)"
+
+           &   HeapEntry-HeapTestModule
+
+Title = "HeapTestModule",0
+
+   $HeapBadAsModuleKET
+
+        GBLL    debheap
+debheap SETL    1=0
+
+ [ DebugHeaps
+FreeSpaceDebugMask * &04000000
+UsedSpaceDebugMask * &08000000
+ ]
+
+Nil     *       0
+TTMMask *       32*1024*1024  ; thirty-two meg mask
+
+hpd     RN      r1      ; The punter sees these
+addr    RN      r2
+size    RN      r3
+
+HpTemp  RN      r10     ; But not these
+tp      RN      r11
+bp      RN      r12
+work    RN      r4      ; This is the only one we have to save.
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; +                     H E A P   O R G A N I S A T I O N                     +
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; A heap block descriptor (hpd) has the form
+
+; +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ -+ -+ -+ -+
+; |   magic   |    free   |    base   |    end    |   debug   |
+; +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+- +- +- +- +
+;  0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20
+
+         ^      0, hpd
+hpdmagic #      4
+hpdfree  #      4
+hpdbase  #      4
+hpdend   #      4       ; Needed for debugging heap, and top end validation
+ [ debheap
+hpddebug #      4       ; 0 -> No debug, ~0 -> Debug
+ ]
+
+hpdsize  *      @-hpdmagic
+
+magic_heap_descriptor * (((((("p":SHL:8)+"a"):SHL:8)+"e"):SHL:8)+"H")
+
+; hpdmagic is a unique identification field
+; hpdfree  is the offset of the first block in the free space list
+; hpdbase  is the offset of the byte above the last one used
+; hpdend   is the offset of the byte above the last one usable
+
+;                               | hpdbase
+;                              \|/
+;      +---+--------------------+--------+
+;  low |hpd|     heap blocks    | unused | high
+;      +---+--------------------+---------+
+;              /|\                       /|\
+;               | hpdfree                 | hpdend
+;               | in here somewhere.
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Blocks in the free space list have the form :
+
+; +--+--+--+--+--+--+--+--+--+ ~ -+--+
+; | long link | long size |          |
+; +--+--+--+--+--+--+--+--+--+ ~ -+--+
+;  0  1  2  3  4  5  6  7  8      (size-1)
+;
+; where the link field is an offset to the next free block
+
+           ^    0 ; Can't use register relative unfortunately as many regs used
+frelink    #    4
+fresize    #    4
+freblksize #    0
+
+; The link field is Nil (0) for the last block in the list
+
+; Block sizes must be forced to a multiple of 8 bytes for subsequent link and
+; size information to be stored in them if they are disposed of by the user.
+
+; They must also be capable of storing a 4 byte size field while allocated.
+; This field is used to size the block to free when FreeArea is called.
+
+
+        ALIGN
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; The Macros
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Check hpd valid
+
+        MACRO
+$label  ValidateHpd $faildest
+$label  BL      ValidateHpdSubr
+        BNE     $faildest._badhpd
+        MEND
+
+; check pointer sensible. Exit EQ if OK
+; valid RAM checks done elsewhere
+
+        MACRO
+$label  CheckPtr $reg,$cond,$xtrabits
+$label  TST$cond $reg, #ARM_CC_Mask
+       [ "$xtrabits" = ""
+        TSTEQ    $reg, #TTMMask    ; check < 32M
+       |
+        TSTEQ    $reg, #TTMMask + $xtrabits  ; check < 32M, with other bits.
+       ]
+        MEND
+
+   $HeapBadAsModuleBRA
+
+   [ TubeInfo
+DebugTUBE  * &03340000+1*&4000         ; tube in podule #1
+; Tube register offsets
+ ^ 0
+R1STAT # 4
+R1DATA # 4
+   ]
+
+; routine to stuff a char down the Tube
+     MACRO
+$l   TubeChar $reg1, $reg2, $chworkt, $stackthere
+  !  0, "TubeChar used."
+$l
+   [ "$stackthere"=""
+   Push "$reg1, $reg2"
+   ]
+     LDR  $reg1, =DebugTUBE
+01   LDRB $reg2, [$reg1, #R1STAT]
+     TST $reg2, #&40
+     BEQ %BT01
+     $chworkt
+     STRB $reg2, [$reg1, #R1DATA]
+   [ "$stackthere"=""
+   Pull "$reg1, $reg2"
+   ]
+   MEND
+
+   $HeapBadAsModuleKET
+
+;****************************************************************************
+
+; These bits of ExtendBlock are outside the IRQ HeapOp range because they
+; don't update the heap structure, so we can safely restore old IRQ status
+
+ [ Fix4
+CopyBackwardsInSafeZone
+        LDR     work, [stack, #3*4]     ; get user link
+        ANDS    work, work, #I_bit      ; look at I_bit
+        TEQEQP  PC, #SVC_mode           ; if was clear then clear it now
+
+        ADD     bp, bp, #4              ; new block pointer
+        STR     bp, [stack]             ; return to user
+
+; copy wackbords: HpTemp-4 bytes from addr+4 to bp, in appropriate order!
+cpe_prev
+        SUBS    HpTemp, HpTemp, #4
+        LDRGT   work, [addr, #4]!
+        STRGT   work, [bp], #4
+        BGT     cpe_prev
+
+        TEQP    PC, #SVC_mode + I_bit   ; disable IRQs before we venture back
+        B       GoodExtension           ; into danger zone
+
+ReallocateInSafeZone
+        LDR     work, [addr, hpd]!      ; get block size, set block addr
+        ADD     size, size, work
+        SUB     size, size, #4          ; block size to claim
+        ADD     addr, addr, #4
+        MOV     bp, addr                ; address to copy from
+        Push    addr                    ; save for later freeing
+
+        MOV     R0, #HeapReason_Get
+        SWI     $RecursiveHeap
+        Pull    addr, VS
+        BVS     SafeNaffExtension
+
+ [ debheap
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT06
+ DREG work, "got new block : copying "
+06
+ ]
+
+        STR     addr, [stack, #4]
+
+; claimed : copy work-4 bytes from bp to addr
+CopyForExtension
+        SUBS    work, work, #4
+        LDRGT   HpTemp, [bp],#4
+        STRGT   HpTemp, [addr],#4
+        BGT     CopyForExtension
+
+; free the old block!
+
+ [ debheap
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT08
+ WRLN "freeing old block"
+08
+ ]
+
+; recursive SWI to free old block; we have invalidated any held information
+
+        MOV     R0, #HeapReason_Free
+        Pull    addr                    ; heap block addr
+        SWI     $RecursiveHeap
+
+        MOV     R0, #HeapReason_ExtendBlock
+        TEQP    PC, #SVC_mode + I_bit   ; disable IRQs before we venture back
+        B       GoodExtension           ; into danger zone
+
+SafeNaffExtension
+        TEQP    PC, #SVC_mode + I_bit   ; disable IRQs before we venture back
+        B       NaffExtension           ; into danger zone
+
+ ]
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Here's the bit that gets returned to if the heap op was done in the
+; background. Pick up the registers, look at the saved PC to see if error
+; return or OK.
+; This bit musn't be in range of the IRQ Heap Op checking!!!
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+heapopdoneinbackground ROUT
+     $HeapBadAsModuleBRA
+     SetBorder R10, R11, 0, 0, 10      ; blue for returning
+     $HeapBadAsModuleKET
+        jeep       R12, HeapReturnedReg_R0
+
+   [ TubeInfo
+  LDR  R0, [R12]
+  BL   TubeDumpR0
+  LDR  R0, [R12, #4]
+  BL   TubeDumpR0
+  LDR  R0, [R12, #8]
+  BL   TubeDumpR0
+  LDR  R0, [R12, #24]
+  TST  R0, #V_bit
+  MOVEQ R0, #"v"
+  MOVNE R0, #"V"
+  TubeChar R1, R2, "MOV R2, r0"
+  TubeChar R0, R1, "MOV R1, #10"
+  TubeChar R0, R1, "MOV R1, #13"
+    ]
+
+        LDMIA     R12, {R0-R4, R10, R11}
+        MOV       stack, R10
+        MOV       R10, #0
+        STR       R10, [R12, #HeapReturnedReg_PC-HeapReturnedReg_R0]
+                                      ; clear the interlock
+        TST       R11, #V_bit         ; look at returned error
+        BEQ       GoodHeapExit
+        B         NaffHeapExit
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+; HeapEntry. SWI level entry
+; =========
+;
+; Perform actions on the heap block described by r1(hpd)
+
+; In    r0       =  heap action requested
+;       r1(hpd)  -> heap block
+;       r2(addr) -> start of block
+;       r3(size) =  size of block
+
+; Out   VClear -> Action performed
+;       VSet   -> Something terrible has happened, error set
+;       Rest of universe ok
+
+HeapEntry ROUT
+        Push    lr
+        MOV     lr, PC             ; hang on to interrupt state
+
+ ; First check that we aren't in an interrupted Heap Op
+        TEQP    PC, #SVC_mode+I_bit
+        MOV     R11, #IRQsema
+inspect_IRQ_stack
+        LDR     R11, [R11]
+        CMP     R11, #0
+        BEQ     iis_end
+        LDR     R10, [R11, #4*7]
+        BIC     R10, R10, #ARM_CC_Mask
+        ADR     R12, first_heap_address_to_trap
+        CMP     R10, R12
+        ADRGEL  R12, HeapCode_end
+        CMPGE   R12, R10
+        BLT     inspect_IRQ_stack
+
+    ; somebody's in the heap code! Time for perversion.
+    ; Pick up registers, do foreground op, poke IRQstack return address
+
+     $HeapBadAsModuleBRA
+     Push "R0, R1, lr"
+     MOV   R0, R10
+     [ TubeInfo
+     BL    TubeDumpR0
+     ]
+     SetBorder R0, R1, 15, 8, 6
+     Pull "R0, R1, lr"
+     $HeapBadAsModuleKET
+
+         ADRL   R10, heapopdoneinbackground
+         ORR    R10, R10, #SVC_mode+I_bit
+         STR    R10, [R11, #4*7]               ; return address zapped
+
+         Push  "R0-R3, lr"
+
+         jeep   R10, HeapSavedReg_R0
+
+; This can't happen: heap ops are non-interruptible while foreground ops
+; are waiting to complete
+;         LDR    R12, [R10, #HeapReturnedReg_PC-HeapSavedReg_R0]
+;         CMP    R12, #0
+;         BNE    HeapInUse
+
+         LDMIA  R10, {R0-R3, R10, R11}
+         SWI    $RecursiveHeap               ; with interrupts off!
+         jeep   R12, HeapReturnedReg_R0
+   ; Could we poke these into the IRQ stack too...?
+   ; would allow interruptible IRQ processes to do heap ops!!!
+         STMIA  R12, {R0-R3, R10, R11, PC}
+         Pull  "R0-R3, lr"
+
+iis_end                                 ; store the registers in the info block
+        jeep    R12, HeapSavedReg_R0
+        STMIA   R12, {R0-R4, stack}
+
+first_heap_address_to_trap              ; because register saveblock now set.
+        LDR     R12, [R12, #HeapReturnedReg_PC-HeapSavedReg_R0]
+        CMP     R12, #0
+        TEQEQP  PC, lr                  ; restore callers interrupt state
+                                        ; only if no foreground waiting to
+                                        ; complete
+
+        CMP     r0, #MaxHeapCode        ; now despatch it.
+        ADDLS   pc, pc, r0, LSL #2      ; Tutu : faster & shorter
+        B       NaffHeapReason          ; Return if unknown call reason
+
+HeapJumpTable ; Check reason codes against Hdr:Heap defs
+
+ assert ((.-HeapJumpTable) :SHR: 2) = HeapReason_Init
+        B       InitHeap
+ assert ((.-HeapJumpTable) :SHR: 2) = HeapReason_Desc
+        B       DescribeHeap
+ assert ((.-HeapJumpTable) :SHR: 2) = HeapReason_Get
+        B       GetArea
+ assert ((.-HeapJumpTable) :SHR: 2) = HeapReason_Free
+        B       FreeArea
+ assert ((.-HeapJumpTable) :SHR: 2) = HeapReason_ExtendBlock
+        B       ExtendBlock
+ assert ((.-HeapJumpTable) :SHR: 2) = HeapReason_ExtendHeap
+        B       ExtendHeap
+ assert ((.-HeapJumpTable) :SHR: 2) = HeapReason_ReadBlockSize
+        B       ReadBlockSize
+ [ debheap
+        B       ShowHeap
+ ]
+MaxHeapCode * (.-HeapJumpTable-4) :SHR: 2 ; Largest valid reason code
+
+
+NaffHeapReason
+        ADR     R0, ErrorBlock_HeapBadReason
+      [ International
+        BL      TranslateError
+      ]
+NaffHeapExit                            ; get here with R0 = error ptr
+        SETV
+GoodHeapExit                            ; V cleared on entry to SWI dispatch
+        MOV     R12, PC
+        ORR     R12, R12, #I_bit        ; IRQs off
+        TEQP    PC, R12
+        MOVNV   R0, R0
+        Pull    lr
+        ORRVS   lr, lr, #V_bit          ; VSet Exit
+
+     $HeapBadAsModuleBRA
+        MOVS   PC, lr
+     $HeapBadAsModuleKET
+
+        ExitSWIHandler                  ; Like all good SWI handlers
+
+;HeapInUse
+;     $HeapBadAsModuleBRA
+;       SetBorder R10, R11, 15, 0, 0
+;     $HeapBadAsModuleKET
+
+;        ADR     R0, ErrorBlock_HeapFail_HeapLocked
+;        B       NaffHeapExit
+
+; Errors
+       MakeErrorBlock  HeapBadReason
+       MakeErrorBlock  HeapFail_Init
+       MakeErrorBlock  HeapFail_BadDesc
+       MakeErrorBlock  HeapFail_BadLink
+       MakeErrorBlock  HeapFail_Alloc
+       MakeErrorBlock  HeapFail_NotABlock
+       MakeErrorBlock  HeapFail_BadExtend
+       MakeErrorBlock  HeapFail_ExcessiveShrink
+;       MakeErrorBlock  HeapFail_HeapLocked
+
+     $HeapBadAsModuleBRA
+
+; inline workspace for testing
+
+HeapSavedReg_R0     & 0
+HeapSavedReg_R1     & 0
+HeapSavedReg_R2     & 0
+HeapSavedReg_R3     & 0
+HeapSavedReg_R4     & 0
+HeapSavedReg_R13    & 0
+HeapReturnedReg_R0  & 0
+HeapReturnedReg_R1  & 0
+HeapReturnedReg_R2  & 0
+HeapReturnedReg_R3  & 0
+HeapReturnedReg_R4  & 0
+HeapReturnedReg_R13 & 0
+HeapReturnedReg_PC  & 0               ; also acts as interlock
+
+     $HeapBadAsModuleKET
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Subroutine to validate heap pointer
+; checks hpd points at existing LogRam
+; and also that internal offsets fall into the same block of RAM
+
+ValidateHpdSubr
+        Push   "R0-R2, lr"
+
+        TEQP    PC, #SVC_mode+I_bit  ; interrupts off for validation
+        MOV     R0, hpd
+        ADD     R1, hpd, #hpdsize+freblksize
+        SWI     XOS_ValidateAddress
+        BCS     vhpds_fail
+
+        TST     R0, #3              ; check alignment
+        LDREQ   HpTemp, =magic_heap_descriptor
+        LDREQ   tp, [R0, #:INDEX: hpdmagic]
+        CMPEQ   tp, HpTemp
+        BNE     vhpds_fail           ; failure
+
+        LDR     R1, [R0, #:INDEX: hpdend]
+        ADD     R1, R1, R0
+        SWI     XOS_ValidateAddress
+        BCS     vhpds_fail           ; failure
+
+        Pull   "R0-R2, lr"
+        ORRS    PC, lr, #Z_bit       ; success
+
+vhpds_fail
+        Pull   "R0-R2, lr"
+        BICS    PC, lr, #Z_bit       ; NE returned ; fails
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+; InitHeap. Top level HeapEntry
+; ========
+;
+; Initialise a heap descriptor block
+
+; In : hpd -> block to initialise, size = size of block
+
+; Out : VClear -> Block initialised
+;       VSet   -> Something terrible has happened
+;       Rest of universe ok
+
+; To initialise (or even reinitialise) a heap descriptor:
+; $(
+;   hpd!magic := magic_heap_descriptor
+;   hpd!free  := Nil
+;   hpd!base  := hpdsize
+;   hpd!end   := size
+; $)
+
+InitHeap ROUT
+ [ MEMM_Type <> "ARM600"        ; If on ARM600 these checks are not valid
+        CheckPtr hpd                       ; Is either hpd or size bad ?
+        CheckPtr size, EQ,  (16*1024*1024) ; If hpd ok, check size < 16M
+        BNE     NaffHeapInitialise
+ ]
+        CMP     size,#hpdsize+freblksize
+        BLT     NaffHeapInitialise        ; can't get hpd and 1 block in
+
+        Push   "R0, R1"
+        MOV     R0, hpd
+        ADD     R1, hpd, size
+        SWI     XOS_ValidateAddress
+        Pull   "R0, R1"
+        BCS     NaffHeapInitialise
+
+ [ DebugHeaps
+        ORR     lr, hpd, #FreeSpaceDebugMask    ; form word to store throughout heap
+        ADD     HpTemp, hpd, size               ; HpTemp -> end of heap
+10
+        STR     lr, [HpTemp, #-4]!              ; store word, pre-decrementing
+        TEQ     HpTemp, hpd                     ; until we get to start
+        BNE     %BT10
+ ]
+
+        LDR     HpTemp, =magic_heap_descriptor
+        STR     HpTemp, hpdmagic          ; hpd!magic := magic_heap_desc
+        MOV     HpTemp, #Nil
+        STR     HpTemp, hpdfree           ; hpd!free  := Nil
+        MOV     HpTemp, #hpdsize
+        STR     HpTemp, hpdbase           ; hpd!base  := hpdsize
+        STR     size,   hpdend            ; hpd!end   := size
+
+ [ debheap
+ MOV HpTemp, #0 ; No debugging until the punter sets this Word
+ STR HpTemp, hpddebug
+ ]
+        B       GoodHeapExit
+
+NaffHeapInitialise
+ [ debheap
+ WRLN "Unaligned/too big hpd/size: InitHeap failed"
+ ]
+        ADR     R0, ErrorBlock_HeapFail_Init
+      [ International
+        BL      TranslateError
+      ]
+        B       NaffHeapExit               ; VSet exit
+
+        LTORG
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+; DescribeHeap. Top level HeapEntry
+; ============
+;
+; Return information about the heap whose descriptor is pointed to by hpd
+
+; In : hpd -> heap descriptor
+
+; Out : VClear -> addr = max block size claimable, size = total free store
+;       VSet   -> Something wrong
+;       Rest of universe ok
+
+DescribeHeap ROUT
+        ValidateHpd describefailed
+
+ [ debheap
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT00
+ Push  link
+ WRLN "DescribeHeap"
+ BL iShowHeap
+ Pull link
+00
+ ]
+        LDR     addr, hpdend
+        LDR     HpTemp, hpdbase
+
+        SUB     addr, addr, HpTemp        ; unused area at base to end
+        MOV     size, addr
+
+        LDR     bp, hpdfree
+        ADR     tp, hpdfree
+        ADD     HpTemp, HpTemp, hpd      ; address of end of allocated memory
+        B       %FT20
+
+
+; Main loop chaining up free space list. size = total, addr = maxvec
+
+15      ADD     tp, tp, bp              ; get address of next
+        CMP     tp, HpTemp
+        BHS     describefailed_badlink  ; points outside allocated memory
+        LDR     bp, [tp, #fresize]      ; Size of this block.
+        CMP     bp, addr                ; if size > maxvec then maxvec := size
+        MOVHI   addr, bp
+        ADD     size, size, bp          ; tfree +:= size
+        LDR     bp, [tp, #frelink]      ; Get offset to next block
+20      CMP     bp,#Nil                 ; we know Nil is 0!
+        BLT     describefailed_badlink  ; -ve are naff
+        BNE     %BT15
+
+        CMP     addr, #0
+        SUBGT   addr, addr, #4          ; max block claimable
+        B       GoodHeapExit            ; VClear Exit
+
+
+describefailed_badhpd
+ [ debheap
+ WRLN "Invalid heap descriptor: DescribeHeap failed"
+ ]
+        ADR     R0, ErrorBlock_HeapFail_BadDesc
+      [ International
+        BL      TranslateError
+      ]
+        B       NaffHeapExit            ; VSet Exit
+
+describefailed_badlink
+ [ debheap
+ WRLN "Invalid heap link: DescribeHeap failed"
+ ]
+        ADR     R0, ErrorBlock_HeapFail_BadLink
+      [ International
+        BL      TranslateError
+      ]
+        B       NaffHeapExit            ; VSet Exit
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+; GetArea. Top level HeapEntry
+; =======
+;
+; Allocate a block of memory from the heap
+
+; This will allocate the first block of sufficiently large size in the free
+; list, with an oversize block being split.
+; Failure to find a large enough block on the free list will try to claim
+; space out of the heap block.
+; Fails if requesting size = 0
+
+; In : hpd -> heap pointer, size = size of block required
+
+; Out : VClear : addr -> got a block
+;       VSet   : addr = 0, couldn't get block
+;       Rest of universe ok
+
+GetArea ROUT
+        Push   "size"
+        ValidateHpd garfailed
+
+ [ debheap
+; HpTemp not critical
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT00
+ Push  "r0, link"
+ MOV r0, size
+ DREG r0, "GetArea "
+ BL iShowHeap
+ Pull "r0, link"
+00
+ ]
+
+        CMP     size, #0                        ; Can't deallocate 0, so there!
+        BLE     garfailed_zero                  ; And -ve is invalid as well!
+     ; note sizes of many megabytes thrown out by looking.
+
+        ADD     size, size, #(freblksize-1)+4   ; Make block size granular
+        BIC     size, size, #(freblksize-1)     ; with size field added
+
+        ADR     addr, hpdfree-frelink           ; addr:= @(hpd!free)-frelink
+
+garloop
+ [ Fix2
+        LDR     tp, [addr, #frelink]        ; tp := addr!fre.link
+        CMP     tp, #Nil                    ; Is this the end of the chain ?
+        BEQ     garmore                     ;  - so try main blk
+ |
+        MOV     tp, addr                    ; tp := addr Keep ptr to prev block
+        LDR     addr, [addr, #frelink]      ; addr := addr!fre.link
+        CMP     addr, #Nil                  ; Is this the end of the chain ?
+        BEQ     garmore                     ;  - so try main blk
+ ]
+        ADD     addr, addr, tp              ; convert offset
+        LDR     HpTemp, [addr, #fresize]    ; If length < size then no good
+        SUBS    HpTemp, HpTemp, size        ; In case this works, for below split
+        BLO     garloop
+
+; Now addr -> a block on the free space list that our item will fit in
+; If we have an exact fit (or as close as the granularity of the free list will
+; allow), unlink this block and return it
+
+        BNE     SplitFreeBlock
+
+ [ debheap
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT60
+ WRLN "Got an exact fit block"
+60
+ ]
+
+        LDR     HpTemp, [addr, #frelink]  ; Move this block's link field
+        CMP     HpTemp, #Nil
+ [ Fix2
+        ADDNE   HpTemp, HpTemp, tp        ; convert offset into offset from
+                                          ; previous block
+        TEQP    PC, #SVC_mode+I_bit
+        ASSERT  frelink=0
+        STR     HpTemp, [addr, -tp]       ; store in link of previous block
+ |
+        ADDNE   HpTemp, HpTemp, addr      ; convert to address (if not Nil!)
+        SUBNE   HpTemp, HpTemp, tp        ; and back to  offset
+        TEQP    PC, #SVC_mode+I_bit
+        STR     HpTemp, [tp, #frelink]    ; into link of previous free block
+ ]
+        B       ResultIsAddrPlus4
+
+SplitFreeBlock
+; Need to split the free block, returning the end portion to the caller
+
+ [ debheap
+; HpTemp critical
+ Push  HpTemp
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT70
+ WRLN "Splitting free block"
+70
+ Pull HpTemp
+ ]
+
+        TEQP    PC, #SVC_mode+I_bit
+        STR     HpTemp, [addr, #fresize]  ; Adjust size of free block remaining
+        ADD     addr, addr, HpTemp        ; addr -> free block just deallocated
+
+ResultIsAddrPlus4
+ [ DebugHeaps
+        ORR     lr, hpd, #UsedSpaceDebugMask    ; form word to store throughout block
+        ADD     HpTemp, addr, size              ; HpTemp -> end of block
+75
+        STR     lr, [HpTemp, #-4]!              ; store word, pre-decrementing
+        TEQ     HpTemp, addr
+        BNE     %BT75
+ ]
+
+        STR     size, [addr], #4        ; Store block size and increment addr
+        Pull    "size"                  ; Return original value to the punter
+                                    ; Note : real size got would be an option!
+        B       GoodHeapExit            ; RESULTIS addr
+
+
+; Got no more free blocks of length >= size, so try to allocate more heap space
+; out of the block described by hpd
+
+garmore
+ [ debheap
+; HpTemp not critical
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT80
+ WRLN "Trying to get more from main block"
+80
+ ]
+
+        LDR     addr, hpdbase
+        ADD     tp, addr, size        ; addr := (hpd!base +:= size)
+        LDR     HpTemp, hpdend
+        TEQP    PC, #SVC_mode+I_bit
+        CMP     tp, HpTemp            ; See if we'd fall out of the bottom
+        STRLS   tp, hpdbase           ; Only adjust hpdbase if valid alloc
+        ADDLS   addr, addr, hpd       ; offset conversion
+        BLS     ResultIsAddrPlus4
+ [ debheap
+ STRIM "Not enough room to allocate in main block"
+ ]
+
+garfailed
+        ADR     R0, ErrorBlock_HeapFail_Alloc
+      [ International
+        BL      TranslateError
+      ]
+ [ debheap
+ WRLN " : GetArea failed"
+ ]
+garfail_common
+        MOV     addr, #0                ; addr := 0 if we couldn't allocate
+        Pull    "size"                  ; RESULTIS 0
+        B       NaffHeapExit            ; VSet Exit
+
+garfailed_badhpd
+ [ debheap
+ STRIM "Invalid heap descriptor"
+ ]
+        ADR     R0, ErrorBlock_HeapFail_BadDesc
+      [ International
+        BL      TranslateError
+      ]
+        B garfail_common
+
+ [ debheap
+garfailed_zero
+ STRIM "Can't allocate 0 or less bytes"
+ B garfailed
+ |
+garfailed_zero * garfailed
+ ]
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+; FreeArea. Top level HeapEntry
+; ========
+;
+; Return an area of store to the heap
+
+; In : hpd -> heap descriptor, addr -> block to free
+
+; Out : VClear -> block freed
+;       VSet   -> failed to free block, size invalid
+;       Rest of universe ok
+
+; The block to be freed is matched against those on the free list and inserted
+; in it's correct place, with the list being maintained in ascending address
+; order. If possible, the freed block is merged with contigous blocks above
+; and below it to give less fragmentation, and if contiguous with main memory,
+; is merged with that. If the latter, check to see if there is a block which
+; would be made contiguous with main memory by the former's freeing, and if so,
+; merge that with main memory too. Phew !
+
+FreeArea ROUT
+        Push    "addr, size, work"
+
+ [ debheap
+; HpTemp not critical
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT00
+ Push  "r0, link"
+ STRIM "FreeArea "
+ SUB r0, addr, hpd
+ SUB r0, r0, #4
+ BL PrintOffsetLine
+ BL iShowHeap
+ Pull "r0, link"
+00
+ ]
+        BL      FindHeapBlock
+        BLVC    FreeChunkWithConcatenation
+
+        Pull    "addr, size, work"
+        BVC     GoodHeapExit
+        B       NaffHeapExit            ; VSet Exit
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+; ExtendBlock. Top level HeapEntry
+; ===========
+;
+; Extend or reallocate existing block
+
+; In : hpd -> heap descriptor, addr -> block, size = size to change by
+
+; Out : VClear -> block freed, addr new block pointer
+;       VSet   -> failed to extend block
+;       Rest of universe ok
+
+ExtendBlock
+
+        Push    "addr, size, work"
+
+ [ debheap
+; HpTemp not critical
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT00
+ Push  "r0, link"
+ DREG size, "ExtendBlock by ",concat
+ STRIM " block  at "
+ SUB r0, addr, hpd
+ SUB r0, r0, #4
+ BL PrintOffsetLine
+ BL iShowHeap
+ Pull "r0, link"
+00
+ ]
+        BL      FindHeapBlock
+        BVS     NaffExtension
+
+        ADD     size, size, #freblksize-1  ; round size as appropriate :
+        BICS    size, size, #freblksize-1  ; round up to nearest 8
+
+        BEQ     GoodExtension              ; get the easy case done.
+        BPL     MakeBlockBigger
+
+        RSB     size, size, #0
+        LDR     bp, [addr, hpd]          ; get block size
+        TEQP    PC, #SVC_mode+I_bit
+        SUBS    bp, bp, size             ; size of block left
+
+ [ debheap
+; HpTemp not critical, GE/LT critical
+ BLE %FT01
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT01
+ WRLN "Freeing part of block"
+01
+ CMP bp, #0  ; restore GE/Lt
+ ]
+
+        MOVLE    HpTemp, #-1               ; if discarding block, then
+        STRLE    HpTemp, [stack]           ; make pointer really naff.
+
+        STRGT    bp, [addr, hpd]           ; update size of block left
+        ADDGT    addr, addr, bp            ; offset of block to free
+        STRGT    size, [addr, hpd]         ; construct block for freeing
+
+        BL      FreeChunkWithConcatenation ; work still set from block lookup
+GoodExtension
+        Pull    "addr, size, work"
+ [ DebugHeaps
+        ADD     lr, size, #freblksize-1         ; work out how much we actually extended by
+        BICS    lr, lr, #freblksize-1
+        BEQ     %FT99                           ; if zero or negative
+        BMI     %FT99                           ; then nothing to do
+        LDR     HpTemp, [addr, #-4]             ; get new block size
+        SUB     HpTemp, HpTemp, #4              ; Exclude size word itself
+        ADD     HpTemp, addr, HpTemp            ; end of new block
+        SUB     lr, HpTemp, lr                  ; start of new extension
+        ORR     bp, hpd, #UsedSpaceDebugMask
+98
+        STR     bp, [HpTemp, #-4]!              ; store word
+        TEQ     HpTemp, lr
+        BNE     %BT98
+99
+ ]
+        B        GoodHeapExit
+
+MakeBlockBigger
+        LDR      HpTemp, [addr, hpd]       ; get size
+        ADD      HpTemp, HpTemp, addr      ; block end
+ [ Fix2
+; TMD 01-Mar-89: FindHeapBlock now never returns tp=Nil, only tp=hpdfree,
+; so no need for check
+        LDR      bp, [tp, hpd]             ; next free
+        CMP      bp, #Nil
+ |
+        CMP      tp, #Nil
+        LDRNE    bp, [tp, hpd]             ; next free
+        CMPNE    bp, #Nil
+ ]
+        ADDNE    bp, bp, tp
+        LDREQ    bp, hpdbase
+
+; bp is potential following block
+        CMP      HpTemp, bp
+        BNE      try_preceding_block
+
+; now get size available, see if fits
+
+        LDR      HpTemp, hpdbase
+        CMP      bp, HpTemp
+        ADDNE    HpTemp, bp, hpd
+        LDRNE    HpTemp, [HpTemp, #fresize]
+        LDREQ    HpTemp, hpdend
+        SUBEQ    HpTemp, HpTemp, bp
+        BICEQ    HpTemp, HpTemp, #(freblksize-1)
+                                           ; force it to a sensible blocksize
+        MOV      lr, PC                    ; save EQ/NE state
+
+        CMP      HpTemp, size
+        BLT      try_add_preceding_block
+
+        ORR      lr, lr, #I_bit            ; disable IRQs
+        TEQP     PC, lr
+
+ [ debheap
+; HpTemp, EQ/NE critical
+ Push "HpTemp,lr"
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT02
+ STRIM "Extending block into "
+02
+ Pull "HpTemp,lr"
+ TEQP  PC, lr
+ ]
+
+        LDR      work, [addr, hpd]         ; get size back
+        ADD      work, work, size          ; new size
+        STR      work, [addr, hpd]         ; block updated
+
+; now see which we're extending into
+        BNE      IntoFreeEntry
+
+ [ debheap
+ Push HpTemp
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT03
+ WRLN "base-end area"
+03
+ Pull HpTemp
+ ]
+        ADD      work, work, addr
+        STR      work, hpdbase
+        B        GoodExtension
+
+IntoFreeEntry
+
+ [ debheap
+ Push HpTemp
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT04
+ WRLN "free entry"
+04
+ Pull HpTemp
+ ]
+
+        CMP      HpTemp, size
+        BNE      SplitFreeBlockForExtend
+
+; free entry just right size : remove from free list
+        LDR      HpTemp, [bp, hpd]         ; free link
+        CMP      HpTemp, #Nil
+        ADDNE    HpTemp, HpTemp, bp        ; offset from heap start
+        SUBNE    HpTemp, HpTemp, tp
+        STR      HpTemp, [tp, hpd]         ; free list updated
+        B        GoodExtension
+
+SplitFreeBlockForExtend
+        LDR      work, [tp, hpd]
+        ADD      work, work, size
+        STR      work, [tp, hpd]           ; prevnode points at right place
+        ADD      work, work, tp            ; offset of new free entry
+        ADD      work, work, hpd
+        SUB      HpTemp, HpTemp, size      ; new freblk size
+        STR      HpTemp, [work, #fresize]
+        LDR      HpTemp, [bp, hpd]
+        CMP      HpTemp, #Nil
+        SUBNE    HpTemp, HpTemp, size      ; reduced offset for free link
+        STR      HpTemp, [work, #frelink]
+        B        GoodExtension
+
+try_preceding_block
+ [ {TRUE}
+ [ Fix2
+; TMD 01-Mar-89: FindHeapBlock now never returns tp=Nil, only tp=hpdfree,
+; so no need for check
+        CMP      tp, #:INDEX: hpdfree  ; no real preceder?
+ |
+        CMP      tp, #Nil              ; no free list?
+        CMPNE    tp, #:INDEX: hpdfree  ; or no real preceder?
+ ]
+        BEQ      got_to_reallocate
+        ADD      bp, tp, hpd
+        LDR      bp, [bp, #fresize]
+        ADD      bp, bp, tp            ; end of preceding block
+        CMP      addr, bp
+        BNE      got_to_reallocate
+
+; now get size available, see if fits
+
+        SUB      bp, bp, tp           ; freblk size
+        SUBS     bp, bp, size         ; compare, find free size left
+        BLT      got_to_reallocate
+
+ [ debheap
+ Push "HpTemp,lr"
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT10
+ CMP bp, #0
+ BEQ %FT11
+ STRIM "Extending block into previous free"
+ B   %FT12
+11
+ STRIM "Previous free perfect fit"
+12
+ SWI XOS_NewLine
+10
+ Pull "HpTemp,lr"
+ ]
+
+        TEQP     PC, #SVC_mode+I_bit       ; IRQs off
+
+hack_preceder
+; bp is new size of preceding block
+; tp is prevfree offset
+; work is prevprevfree offset
+; size is amount block grows by
+; addr is block offset
+        CMP      bp, #0
+        ADDNE    HpTemp, tp, hpd
+        STRNE    bp, [HpTemp, #fresize]    ; prevblock shrunk
+        BNE      copy_backwards
+
+ ; free freblk: work is still prevprevblk pointer
+        LDR      HpTemp, [tp, hpd]
+        CMP      HpTemp, #Nil
+        ADDNE    HpTemp, HpTemp, tp        ; offset from heap start
+        SUBNE    HpTemp, HpTemp, work
+        STR      HpTemp, [work, hpd]       ; free list updated
+
+copy_backwards
+        ADD      bp, bp, tp
+        LDR      HpTemp, [addr, hpd]!      ; current block size
+        ADD      size, HpTemp, size
+        STR      size, [bp, hpd]!          ; update blocksize
+
+ [ debheap
+ Push r0
+ LDR r0, hpddebug
+ CMP r0, #0
+ BEQ %FT06
+ DREG HpTemp, "copying -4+",concat
+ STRIM " from "
+ SUB  R0, addr, hpd
+ BL   PrintOffset
+ STRIM " to "
+ SUB  R0, bp, hpd
+ BL   PrintOffsetLine
+06
+ Pull r0
+ ]
+
+ [ Fix4
+; TMD 02-Mar-89: We've finished messing about with the heap structure
+; so we can branch outside danger zone and restore IRQ status while doing copy
+        B       CopyBackwardsInSafeZone
+ |
+
+        ADD      bp, bp, #4                ; new block pointer
+        STR      bp, [stack]               ; return to user
+
+; copy wackbords: HpTemp-4 bytes from addr+4 to bp, in appropriate order!
+cpe_prev
+        SUBS     HpTemp, HpTemp, #4
+        LDRGT    work, [addr, #4]!
+        STRGT    work, [bp], #4
+        BGT      cpe_prev
+        B        GoodExtension
+ ]
+    ]
+try_add_preceding_block
+    [ {TRUE}
+; HpTemp is size of following block
+        CMP      tp, #:INDEX: hpdfree  ; no real preceder?
+        BEQ      got_to_reallocate
+        Push    "work, size"           ; need prevprevblk ptr
+        SUB      size, size, HpTemp    ; size still needed
+        ADD      HpTemp, tp, hpd
+        LDR      HpTemp, [HpTemp, #fresize]
+        ADD      HpTemp, HpTemp, tp        ; end of preceding block
+        CMP      addr, HpTemp
+        BNE      got_to_reallocate2
+
+; now get size available, see if fits
+
+        SUB      HpTemp, HpTemp, tp    ; freblk size
+        SUBS     HpTemp, HpTemp, size
+        BLT      got_to_reallocate2
+
+ [ debheap
+ Push "HpTemp,lr"
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT10
+ Pull HpTemp
+ CMP HpTemp, #0
+ BEQ %FT11
+ STRIM "Extending block into previous free and block after"
+ B   %FT12
+11
+ STRIM "Previous free+nextblock perfect fit"
+12
+ SWI XOS_NewLine
+10
+ Pull "lr"
+ ]
+
+        TEQP     PC, #SVC_mode+I_bit   ; IRQs off
+   ; delink block at bp
+        LDR      work, hpdbase
+        CMP      bp, work              ; extend into free, or delink block?
+        BNE      ext_delink
+        LDR      work, hpdend
+        SUB      work, work, bp        ; get back real size
+        BIC      work, work, #(freblksize-1)
+        ADD      work, work, bp
+        STR      work, hpdbase         ; all free allocated
+        B        ext_hack
+ext_delink
+        LDR      work, [bp, hpd]
+        CMP      work, #Nil
+        ADDNE    work, work, bp
+        SUBNE    work, work, tp
+        STR      work, [tp, hpd]       ; block delinked
+ext_hack
+        MOV      bp, HpTemp
+        Pull    "work, size"
+; bp is new size of preceding block
+; tp is prevfree offset
+; work is prevprevfree offset
+; size is amount block grows by
+; addr is block offset
+        B        hack_preceder
+
+got_to_reallocate2
+       Pull     "work, size"
+  ]
+got_to_reallocate
+; claim block of new size ; copy data
+; Done by recursive SWIs: somewhat inefficient, but simple.
+
+ [ debheap
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT05
+ WRLN "reallocating block"
+05
+ ]
+
+ [ Fix4
+        B       ReallocateInSafeZone
+ |
+        TEQP     PC, #SVC_mode+I_bit       ; no interrupts during this
+        LDR      work, [addr, hpd]!        ; get block size, set block addr
+        ADD      size, size, work
+        SUB      size, size, #4            ; block size to claim
+        ADD      addr, addr, #4
+        MOV      bp, addr                  ; address to copy from
+        Push     addr                      ; save for later freeing
+
+        MOV      R0, #HeapReason_Get
+        SWI      $RecursiveHeap
+        Pull     addr, VS
+        BVS      NaffExtension
+
+ [ debheap
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT06
+ DREG work, "got new block : copying "
+06
+ ]
+
+        STR      addr, [stack, #4]
+
+; claimed : copy work-4 bytes from bp to addr
+CopyForExtension
+        SUBS     work, work, #4
+        LDRGT    HpTemp, [bp],#4
+        STRGT    HpTemp, [addr],#4
+        BGT      CopyForExtension
+
+; free the old block!
+
+ [ debheap
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT08
+ WRLN "freeing old block"
+08
+ ]
+
+; recursive SWI to free old block; we have invalidated any held information
+
+        MOV      R0, #HeapReason_Free
+        Pull     addr                  ; heap block addr
+        SWI      $RecursiveHeap
+
+        MOV      R0, #HeapReason_ExtendBlock
+        B        GoodExtension
+ ]
+
+NaffExtension
+        Pull    "addr, size, work"
+        B        NaffHeapExit
+
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+; ExtendHeap. Top level HeapEntry
+; ==========
+;
+; Extend or shrink heap
+
+; In : hpd -> heap descriptor, size = size to change by
+
+; Out : VClear -> heap size changed OK
+;       VSet   -> failed to change by specified amount
+;       size = amount changed by
+
+ExtendHeap       ROUT
+        ValidateHpd  ExtendHeap
+
+        CMP      r3, #0
+        ADDMI    r3, r3, #3          ; round towards 0
+        BIC      R3, R3, #3          ; ensure word amount
+
+        LDR      HpTemp, hpdend
+        ADD      HpTemp, HpTemp, R3  ; HpTemp := new size
+        LDR      tp, hpdbase
+        CMP      tp, HpTemp
+        BGT      ExtendHeap_badshrink
+
+        TEQP     PC, #SVC_mode+I_bit
+        Push    "R0, R1"
+        MOV      R0, hpd             ; Ensure heap will be in valid area
+        ADD      R1, hpd, HpTemp
+        SWI      XOS_ValidateAddress
+        Pull    "R0, R1"
+        BCS      ExtendHeap_nafforf
+
+ [ DebugHeaps
+        CMP     R3, #0                  ; if shrunk or stayed same
+        BLE     %FT15                   ; then nothing to do
+        ADD     tp, hpd, HpTemp         ; tp -> end of heap
+        SUB     bp, tp, R3              ; bp -> start of new bit
+        ORR     lr, hpd, #FreeSpaceDebugMask
+10
+        STR     lr, [tp, #-4]!          ; store word
+        TEQ     tp, bp
+        BNE     %BT10
+15
+ ]
+
+        STR      HpTemp, hpdend      ; uppy date him
+        B        GoodHeapExit        ; moved all the size asked for
+
+ExtendHeap_badhpd
+        ADRL     R0, ErrorBlock_HeapFail_BadDesc
+      [ International
+        BL       TranslateError
+      ]
+        MOV      size, #0
+        B        NaffHeapExit
+
+ExtendHeap_nafforf
+        ADRL     R0, ErrorBlock_HeapFail_BadExtend
+      [ International
+        BL      TranslateError
+      ]
+        MOV      size, #0
+ [ Fix3
+ |
+        Pull     lr
+ ]
+        B        NaffHeapExit
+
+ExtendHeap_badshrink
+        LDR      HpTemp, hpdend
+        STR      tp, hpdend          ; update heap
+        SUB      size, HpTemp, tp    ; size managed to change by
+        ADRL     R0, ErrorBlock_HeapFail_ExcessiveShrink
+      [ International
+        BL       TranslateError
+      ]
+        B        NaffHeapExit        ; and sort of fail
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+; ReadBlockSize. Top level HeapEntry
+; =============
+;
+
+ReadBlockSize
+
+        Push    "addr, work"
+        BL      FindHeapBlock
+        LDRVC   size, [addr, hpd]
+        Pull   "addr, work"
+        BVC     GoodHeapExit
+        B       NaffHeapExit
+
+;**************************************************************************
+; Common routines for free/extend
+
+FindHeapBlock   ROUT
+; Convert addr to address
+; Validate heap
+; check block is an allocated block
+; return tp = free list entry before the block (hpdfree if none)
+;      work = free list before that (if exists)
+; corrupts HpTemp, bp
+
+        Push    lr
+
+        ValidateHpd findfailed
+
+        SUB     addr, addr, hpd     ; convert to offset
+        SUB     addr, addr, #4      ; real block posn
+
+; Find block in heap by chaining down freelist, stepping through blocks
+ [ Fix2
+
+; TMD 01-Mar-89
+; no need to check explicitly for null free list, code drops thru OK
+
+ |
+        LDR     tp, hpdfree          ; first find bounding free blocks
+        CMP     tp, #Nil             ; no free?
+  [ debheap
+; HpTemp not critical
+; EQ critical
+ BNE %FT01
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT01
+ Push  "link"
+ WRLN "No free list - scan all blocks"
+ Pull "link"
+ CMP R0,R0 ; restore EQ
+01
+ ]
+        MOVEQ   bp, #hpdsize         ; load block limits to look through
+        LDREQ   HpTemp, hpdbase
+        BEQ     ScanAllocForAddr
+ ]
+ [ debheap
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT03
+ Push lr
+ WRLN "Scanning freelist"
+ Pull lr
+03
+ ]
+
+; step down free list to find appropriate chunk
+; get tp = free block before addr
+; HpTemp =  "     "   after   "
+;   work = block before tp
+
+        MOV     tp, #:INDEX: hpdfree
+StepDownFreeList
+        LDR     HpTemp, [hpd, tp]     ; link offset
+        CMP     HpTemp,#Nil
+        BEQ     ListEnded             ; EQ state used!
+        ADD     HpTemp, HpTemp, tp
+        CMP     HpTemp, addr
+        MOVLS   work, tp
+        MOVLS   tp, HpTemp
+        BLS     StepDownFreeList
+ListEnded
+        LDREQ   HpTemp, hpdbase      ; if EQ from CMP HpTemp, addr
+                                     ; then bad block anyway
+        CMP     tp, #:INDEX: hpdfree
+        MOVEQ   bp, #hpdsize         ; is this a fudge I see before me?
+        BEQ     ScanAllocForAddr
+ [ Fix2
+        ADD     bp, tp, #fresize
+        LDR     bp, [hpd, bp]
+ |
+        ADD     tp, tp, #fresize
+        LDR     bp, [hpd,tp]
+        SUB     tp, tp, #fresize
+ ]
+        ADD     bp, tp, bp
+
+ScanAllocForAddr
+; bp     -> start of allocated chunk
+; HpTemp -> end    "   "        "
+; scan to find addr, error if no in here
+
+       Push    work       ; keep prevlink ptr
+
+  [ debheap
+; HpTemp critical
+ Push "HpTemp, R0, link"
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT02
+ STRIM "Scan for addr from "
+ MOV   R0, bp
+ BL    PrintOffset
+ STRIM " to "
+ LDR   R0,[stack,#4]  ; HpTemp
+ BL    PrintOffsetLine
+02
+ Pull "HpTemp, r0, link"
+ ]
+        B       CheckForNullAllocn
+
+ScanAllocForAddrLoop
+        CMP     bp, addr
+        BEQ     ValidBlock
+        LDR     work, [bp, hpd]    ; get size
+        ADD     bp, bp, work
+CheckForNullAllocn
+        CMP     bp, HpTemp
+        BLT     ScanAllocForAddrLoop
+
+ [ debheap
+ Push lr
+ STRIM "Given pointer not a block"
+ Pull lr
+ ]
+       ADRL    R0, ErrorBlock_HeapFail_NotABlock
+     [ International
+       BL      TranslateError
+     ]
+       Pull   "work, lr"
+       ORRS    PC, lr, #V_bit
+
+ValidBlock    ; tp = free link offset, addr = block offset
+       Pull   "work, lr"
+       BICS    PC, lr, #V_bit
+
+findfailed_badhpd
+ [ debheap
+ Push   lr
+ STRIM "Invalid heap descriptor"
+ Pull   lr
+ ]
+        ADRL    R0, ErrorBlock_HeapFail_BadDesc
+      [ International
+        BL      TranslateError
+      ]
+        Pull    lr
+        ORRS    PC, lr, #V_bit
+
+;****************************************************************************
+
+FreeChunkWithConcatenation ROUT
+; in : addr -> block
+;      tp   -> preceding free list entry
+; out : block freed, concatenated with any free parts on either side,
+;       base reduced if can do
+; corrupts HpTemp, bp, size, addr
+
+ [ Fix2
+; TMD 01-Mar-89: FindHeapBlock now never returns tp=Nil, only tp=hpdfree,
+; so no need for check, code will get there eventually!
+ |
+        CMP     tp, #Nil
+        MOVEQ   tp, #:INDEX: hpdfree
+        BEQ     NoConcatenation
+ ]
+
+; attempt concatenation with free blocks on both/either side
+ [ debheap
+ Push "R0, lr"
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT04
+ STRIM "concatenation attempt with free ptr "
+ MOV   R0,tp
+ BL    PrintOffsetLine
+04
+ Pull  "R0, lr"
+ ]
+
+ [ DebugHeaps
+        ORR     bp, hpd, #FreeSpaceDebugMask
+        LDR     size, [addr, hpd]!
+        ADD     HpTemp, addr, size
+        SUB     HpTemp, HpTemp, #4      ; HpTemp -> last word of block
+10
+        STR     bp, [HpTemp], #-4       ; store word, then go back
+        TEQ     HpTemp, addr            ; loop until done, but don't overwrite size field
+        BNE     %BT10                   ; otherwise we might get an IRQ with a duff heap
+        SUB     addr, addr, hpd         ; make addr an offset again
+ ]
+
+        LDR     size, [addr, hpd]      ; block size
+        ADD     bp, size, addr         ; eob offset
+        LDR     HpTemp, [tp, hpd]      ; Nil doesn't matter here!
+        ADD     HpTemp, HpTemp, tp     ; offset of free block after ours
+        CMP     HpTemp, bp             ; if tp was hpdfree then <> bp
+        BNE     NoConcatWithNext       ; so will take branch
+
+ [ debheap
+ Push lr
+ LDR bp, hpddebug
+ CMP bp, #0
+ BEQ %FT05
+ WRLN "concatenating with block after"
+05
+ Pull lr
+ ]
+        ADD    bp, hpd, HpTemp
+        LDR    bp, [bp, #fresize]
+        ADD    bp, bp, size
+        TEQP   PC, #SVC_mode+I_bit
+        STR    bp, [addr, hpd]       ; enlarge our block
+        LDR    bp, [HpTemp, hpd]     ; offset in free list
+        CMP    bp, #Nil
+        ADDNE  bp, HpTemp, bp        ; offset from heap start
+        SUBNE  bp, bp, tp            ; free list offset
+        STR    bp, [tp, hpd]         ; free list updated, our block bigger
+                                     ; - but not in the free list yet!
+
+NoConcatWithNext  ; tp = free link offset, addr = block offset
+                  ; now try for concatenation with previous block
+        CMP    tp, #:INDEX: hpdfree  ; are we before any real free blocks?
+        BEQ    NoConcatenation       ; yup
+
+        ADD    HpTemp, tp, hpd
+        LDR    size, [HpTemp, #fresize]
+        ADD    bp, size, tp
+        CMP    bp, addr
+        BNE    NoConcatenation
+ [ debheap
+ Push lr
+ LDR bp, hpddebug
+ CMP bp, #0
+ BEQ %FT06
+ WRLN "concatenating with block before"
+ STRIM "prevfree = "
+ Push  R0
+ MOV   R0, work
+ BL    PrintOffsetLine
+ Pull  R0
+06
+ Pull lr
+ ]
+        LDR    bp, [addr, hpd]         ; get block size
+        ADD    size, bp, size          ; new free block size
+        TEQP   PC, #SVC_mode+I_bit
+        STR    size, [HpTemp, #fresize]
+; now check for butts against base : work is still prevnode to tp
+        ADD    HpTemp, size, tp
+        LDR    bp, hpdbase
+        CMP    bp, HpTemp
+        ORRNES PC, lr, #I_bit        ; all done : exit keeping IRQs off
+        SUB    bp, bp, size
+        STR    bp, hpdbase           ; step unused bit back
+        MOV    bp, #Nil              ; this MUST have been last free block!
+        STR    bp, [work, hpd]
+        ORRS   PC, lr, #I_bit        ; Whew!
+
+NoConcatenation ; check if block butts against base
+; tp = previous freelink offset
+        LDR     size, [addr, hpd]
+        ADD     HpTemp, size, addr
+        LDR     bp, hpdbase
+        CMP     bp, HpTemp
+        BNE     AddToFreeList
+        SUB     bp, bp, size
+        TEQP    PC, #SVC_mode+I_bit
+        STR     bp, hpdbase
+        ORRS    PC, lr, #I_bit
+
+AddToFreeList  ; block at addr, previous free at tp
+ [ debheap
+ Push "R0, lr"
+ LDR HpTemp, hpddebug
+ CMP HpTemp, #0
+ BEQ %FT07
+ STRIM "add to free list : free link "
+ MOV   R0,tp
+ BL    PrintOffset
+ STRIM ", block "
+ MOV   R0, addr
+ BL    PrintOffsetLine
+07
+ Pull "R0, lr"
+ ]
+        LDR    size, [addr, hpd]!
+        TEQP   PC, #SVC_mode+I_bit
+        STR    size, [addr, #fresize]
+        SUB    addr, addr, hpd
+        LDR    size, [hpd, tp]      ; prevlink
+        CMP    size, #Nil
+        SUBNE  size, size, addr
+        ADDNE  size, size, tp       ; form offset if not eolist
+        STR    size, [addr, hpd]
+        SUB    size, addr, tp
+        STR    size, [tp, hpd]
+        ORRS   PC, lr, #I_bit
+
+;*****************************************************************************
+
+ [ debheap
+;
+; ShowHeap. Top level HeapEntry
+; ========
+;
+; Dump the heap pointed to by hpd
+
+ShowHeap
+        Push    link
+        BL      iShowHeap       ; Needed to fudge link for SVC mode entry
+        Pull    link
+        B       GoodHeapExit
+
+
+iShowHeap ROUT ; Internal entry point for debugging heap
+
+        Push    "r0, hpd, addr, size, work, bp, tp, link"
+
+        ValidateHpd showfailed  ; debugging heaps won't work interruptibly
+
+        LDR     tp, hpdfree
+        CMP     tp, #Nil
+        ADDNE   tp, tp, #:INDEX: hpdfree
+        LDR     bp, hpdbase
+        MOV     addr, #hpdsize
+        LDR     work, hpdend
+
+        SWI     OS_NewLine              ; Initial blurb about hpd contents
+        DREG    hpd, "**** Heap map **** : hpd "
+        STRIM   "->  free"
+        MOV     r0, tp
+        BL      PrintOffset
+        STRIM   ", base"
+        MOV     r0, bp
+        BL      PrintOffsetLine
+        STRIM   "-> start"
+        MOV     r0, addr
+        BL      PrintOffset
+        STRIM   ",  end"
+        MOV     r0, work
+        BL      PrintOffsetLine
+
+        SUB     r0, work, bp            ; hpdend-hpdbase
+        DREG    r0,"Bytes free: ",concat, Word
+        SUB     r0, bp, addr            ; hpdbase-hpdsize
+        DREG    r0,", bytes used: ",, Word
+        SWI     XOS_NewLine
+
+        CMP     tp, #Nil                ; No free blocks at all ?
+        BNE     %FT10
+        WRLN    "No Free Blocks"
+
+        CMP     bp, addr                ; Is a block allocated at all ?
+        MOVNE   r0, addr ; hpdsize
+        BNE     %FT40
+        WRLN    "No Used Blocks"
+        B       %FT99
+
+
+10      CMP     tp, addr ; hpdsize       ; Allocated block below first free ?
+        BEQ     %FT15
+
+        MOV     r0, addr ; hpdbase
+        BL      HexUsedBlk
+        SUB     r0, tp, addr ; hpdfree-hpdsize
+        DREG    r0
+        SWI     XOS_NewLine
+
+; Main loop chaining up free space list
+
+15      ADD     addr, tp, hpd             ; convert to address
+        LDR     size, [addr, #fresize]    ; Size of this block
+        LDR     addr, [addr, #frelink]    ; offset to next block
+
+        STRIM   "Free Block "
+        MOV     r0, tp
+        BL      PrintOffset
+        DREG    size, ", size "
+
+        ADD     r0, tp, size ; r0 -> eob. Adjacent free blocks don't exist
+
+        CMP     addr, #Nil ; If last block, then must we see if we're = hpdbase
+        BEQ     %FT40
+
+; Used block starts at r0, ends at addr+tp - so size = (addr+tp)-r0
+
+        BL      HexUsedBlk
+        SUB     r0, addr, r0  ; addr-r0
+        ADD     r0, r0, tp    ; used block size
+        DREG    r0
+        SWI     XOS_NewLine
+
+        ADD     tp, addr, tp  ; step down free list
+        B       %BT15         ; And loop
+
+
+40      CMP     r0, bp      ; Is there any allocated space after this block ?
+        BEQ     %FT99
+        BL      HexUsedBlk
+        SUB     r0, bp, r0  ; hpdbase-sob
+        DREG    r0
+        SWI     XOS_NewLine
+
+99
+        GRABS  "r0, hpd, addr, size, work, bp, tp, pc"
+
+
+showfailed_badhpd
+        WRLN    "Invalid heap descriptor : ShowHeap failed"
+        GRABS    "r0, hpd, addr, size, work, bp, tp, pc"
+
+
+HexUsedBlk
+        Push   "lr"
+        STRIM  "Used Block "
+        BL      PrintOffset
+        STRIM  ", size"
+        Pull   "lr"
+        MOVS    PC, R14
+
+PrintOffset
+        Push   "r0, lr"
+        DREG    r0
+        CMP     R0, #0
+        ADDNE   R0, R0, hpd
+        DREG    r0," (",concat
+        STRIM   ")"
+        GRABS  "R0, PC"
+
+PrintOffsetLine
+        Push   "lr"
+        BL      PrintOffset
+        SWI     XOS_NewLine
+        Pull   "PC"
+
+ ]
+
+     [ TubeInfo
+TubeDumpR0     ROUT
+        Push  "R1, R2, lr"
+        TubeChar R0, R1, "MOV R1, #10"
+        TubeChar R0, R1, "MOV R1, #13"
+        MOV    R1, #7
+01      MOV    R0, R0, ROR #28
+        AND    R2, R0, #&F
+        TubeChar R0, R1, "ADD R1, R2, #""0"""
+        SUBS   R1, R1, #1
+        BPL    %BT01
+        Pull  "R1, R2, PC", ,^
+     ]
+
+HeapCode_end
+
+; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        $HeapBadAsModuleBRA
+        InsertDebugRoutines
+        $HeapBadAsModuleKET
+
+        END
diff --git a/s/HeapSort b/s/HeapSort
new file mode 100644
index 0000000000000000000000000000000000000000..1ded77acd20d1565985f6b6da2121424cd218daa
--- /dev/null
+++ b/s/HeapSort
@@ -0,0 +1,405 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        SUBT    > HeapSort
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; HeapSort routine. Borrowed from Knuth by Tutu. Labels h(i) correspond to
+; steps in the algorithm.
+
+; In    r0 = n
+;       r1 = array(n) of word size objects (r2 determines type)
+;              bit 31 set -> use r4,r5 on postpass
+;              bit 30 set -> build (r1) from r4,r5 in prepass
+;              bit 29 set -> use r6 as temp slot
+;       r2 = address of comparison procedure
+;              Special cases:
+;                0 -> treat r(n) as array of cardinal
+;                1 -> treat r(n) as array of integer
+;                2 -> treat r(n) as array of cardinal*
+;                3 -> treat r(n) as array of integer*
+;                4 -> treat r(n) as array of char* (case insensitive)
+;                5 -> treat r(n) as array of char* (case sensitive)
+;       r3 = wsptr for comparison procedure (only needed if r2 > 5)
+;       r4 = array(n) of things (only needed if r1 & 0xC0000000)
+;       r5 = sizeof(element)    ( ---------  ditto  ---------- )
+;       r6 = address of temp slot (only needed if r5 > 16K or r1 & 0x20000000)
+
+; r10-r12 trashable
+
+hs_array RN     r4
+hs_procadr RN   r5
+hs_i    RN      r6
+hs_j    RN      r7
+hs_K    RN      r8
+hs_R    RN      r9
+hs_l    RN      r10
+hs_r    RN      r11
+;wp     RN      r12
+
+; User sort procedure entered in SVC mode, interrupts enabled
+; r0 = contents of array(1)
+; r1 = contents of array(2)
+; r0-r3 may be trashed
+; wp = value requested (trash at your peril; you'll get the bad one next time)
+
+; User sort procedure returns:
+;       LT: if f(r0)  < f(r1)
+;       GE: if f(r0) => f(r1)
+; (ie. N_bit and V_bit only considered)
+
+HeapSortRoutine ROUT
+
+        CMP     r0, #2                  ; 0 or 1 elements? No data moved either
+        ExitSWIHandler LO               ; VClear in lr and psr
+
+        Push    "r0-r3, hs_array, hs_procadr, hs_i, hs_j, hs_K, hs_R, lr"
+
+        MVN     r14, #I_bit             ; Enable interrupts (may take ages)
+        TSTP    r14, pc
+
+ [ False
+        STR     r0, ndump               ; For debugging porpoises
+ ]
+        TST     r1, #1 :SHL: 30         ; Are we to build the pointer array?
+        BIC     r1, r1, #2_111 :SHL: 29 ; Take out flag bits for now
+        BEQ     %FT01
+
+; Build array of pointers to data blocks for the punter if he desires this
+; (lazy slobs abound ...)
+
+; for (i=0; i<n; i++) r(i) = &block + i*sizeof(element);
+
+        MOV     r10, r0                 ; n
+        MOV     r14, r1                 ; r14 -> base of pointer array
+00      STR     r4, [r14], #4
+        ADD     r4, r4, r5              ; r4 += sizeof(element)
+        SUBS    r10, r10, #1
+        BNE     %BT00
+
+
+01      SUB     hs_array, r1, #4        ; HeapSort assumes r(1..n) not (0..n-1)
+
+        MOV     hs_procadr, r2          ; Put proc address where we need it
+
+        CMP     hs_procadr, #6          ; Special procedure ?
+        ADRLO   r14, hs_Procedures
+        LDRLO   hs_procadr, [r14, hs_procadr, LSL #2]
+        ADDLO   hs_procadr, hs_procadr, r14
+
+        MOV     wp, r3                  ; Can now use r3 temp. Keep set up
+                                        ; for speed during execution
+
+        MOV     hs_l, r0, LSR #1        ; l = floor(n/2) + 1
+        ADD     hs_l, hs_l, #1
+        MOV     hs_r, r0                ; r = n
+
+
+h2      CMP     hs_l, #1
+        BEQ     %FT10
+
+        SUB     hs_l, hs_l, #1
+        LDR     hs_R, [hs_array, hs_l, LSL #2] ; R = R(l)
+        MOV     hs_K, hs_R
+        B       %FT20
+
+10      LDR     hs_R, [hs_array, hs_r, LSL #2] ; R = R(r)
+        MOV     hs_K, hs_R
+        LDR     r14, [hs_array, #4]     ; R(r) = R(1)
+        STR     r14, [hs_array, hs_r, LSL #2]
+        SUB     hs_r, hs_r, #1
+        CMP     hs_r, #1                ; IF r=1 THEN R(1) = R
+        STREQ   hs_R, [hs_array, #4]
+20
+ [ False
+ BL DoDebug
+ ]
+
+        CMP     hs_r, #1
+        BEQ     %FT90                   ; [finished sorting the array]
+
+
+h3      MOV     hs_j, hs_l
+
+
+h4      MOV     hs_i, hs_j
+        MOV     hs_j, hs_j, LSL #1
+
+ [ False
+ DREG hs_i," i ",cc
+ DREG hs_j," j "
+ ]
+        CMP     hs_j, hs_r
+        BEQ     h6
+        BHI     h8
+
+
+h5      LDR     r0, [hs_array, hs_j, LSL #2]
+                                        ; IF K(R(j)) < K(R(j+1)) THEN j +:= 1
+        ADD     r14, hs_j, #1
+        LDR     r1, [hs_array, r14, LSL #2]
+
+        MOV     lr, pc                  ; r0, r1 for comparison
+        MOV     pc, hs_procadr
+
+        ADDLT   hs_j, hs_j, #1          ; Assumes signed comparison done <<<<<<
+
+
+h6      MOV     r0, hs_K                ; IF K >= K(R(j)) THEN h8
+        LDR     r1, [hs_array, hs_j, LSL #2]
+
+        MOV     lr, pc                  ; r0, r1 for comparison
+        MOV     pc, hs_procadr
+
+ [ True ; 1.73+ optimisation, faster in all cases
+                                        ; Assumes signed comparison done <<<<<<
+;h7
+        LDRLT   r14, [hs_array, hs_j, LSL #2] ; R(i) = R(j)
+        STRLT   r14, [hs_array, hs_i, LSL #2]
+        BLT     h4
+ |
+        BGE     h8                      ; Assumes signed comparison done <<<<<<
+
+;h7
+        LDR     r14, [hs_array, hs_j, LSL #2] ; R(i) = R(j)
+        STR     r14, [hs_array, hs_i, LSL #2]
+        B       h4
+ ]
+
+
+h8      STR     hs_R, [hs_array, hs_i, LSL #2] ; R(i) = R
+        B       h2
+
+
+; Array now sorted into order
+
+90      LDR     r14, [sp, #4*1]         ; r1in
+        TST     r14, #1 :SHL: 30
+        BEQ     %FA99                   ; [no shuffle required, exit]
+
+; Reorder the blocks according to the sorted array of pointers
+
+        BIC     r2, r14, #2_111 :SHL: 29 ; r2 -> list of pointers
+                                        ; keep r14 ok for below
+
+        ADD     r1, sp, #4*4
+        LDMIA   r1, {r1, r8, r9}        ; r4,r5,r6in
+                                        ; r1 -> list of blocks
+ [ False
+ DREG r2, "pointer array   "
+ DREG r1, "base of blocks  "
+ DREG r8, "sizeof(element) "
+ ]
+        MOV     r3, r2                  ; r3 -> first item of current cycle
+        LDR     r0, [sp, #0*4]          ; r0 = n
+        ADD     r6, r2, r0, LSL #2      ; r6 -> end of array of pointers
+        TST     r14, #1 :SHL: 29        ; punter forcing use of his temp slot?
+        BNE     %FT94                   ; fine by me!
+        CMP     r8, #ScratchSpaceSize
+        LDRLS   r9, =ScratchSpace       ; r9 -> temp slot (normally ScratchSpc)
+94
+ [ False
+ DREG r9, "temp slot       "
+ ]
+
+91      SUB     r14, r3, r2
+        MOV     r14, r14, LSR #2        ; r14 = index (0..n-1) of current item
+        MLA     r4, r14, r8, r1         ; r4 -> current block
+
+        MOV     r5, r3                  ; r5 -> current item
+        BL      MoveToTempSlot          ; save first block in temp slot
+
+92      LDR     r7, [r5]                ; r7 -> next block
+        MOV     r14, #0
+        STR     r14, [r5]               ; mark item 'done'
+
+        SUB     r5, r7, r1              ; r14 := index of next item (r8 pres.)
+        DivRem  r14, r5, r8, r0         ; r5,r0 corrupt
+        ADD     r5, r2, r14, LSL #2     ; r5 -> next item
+ [ False
+ DREG r7, "  next block "
+ DREG r5, "   next item "
+ ]
+
+        CMP     r5, r3                  ; reached start of cycle?
+        MOVEQ   r7, r9                  ; get back from temp slot if last one
+        BL      MoveFromGivenSlot       ; preserves flags
+
+        MOVNE   r4, r7                  ; update r4 (current block)
+        BNE     %BT92
+
+93      LDR     r14, [r3, #4]!          ; skip already-copied items
+        CMP     r3, r6
+        BCS     %FA99                   ; [reached end]
+        CMP     r14, #0
+        BEQ     %BT93
+
+        B       %BT91                   ; [found one that hasn't been copied]
+
+
+; No error return from HeapSort
+
+99      Pull    "r0-r3, hs_array, hs_procadr, hs_i, hs_j, hs_K, hs_R, lr"
+
+; SWIHandler exit takes flags + mode from lr, not psr !!!
+
+        ExitSWIHandler
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r4 -> element to be copied
+;       r8 = sizeof(element)
+;       r9 -> temp slot
+
+; Out   all preserved
+
+MoveToTempSlot ENTRY "r4, r8, r9"
+
+        TST     r4, #3                  ; If base and element size wordy
+        TSTEQ   r8, #3                  ; then do faster copy. Also temp wordy
+        BNE     %FT01
+
+00      SUBS    r8, r8, #4
+        LDRPL   r14, [r4], #4
+        STRPL   r14, [r9], #4
+        BPL     %BT00
+        EXITS
+
+01      SUBS    r8, r8, #1
+        LDRPLB  r14, [r4], #1
+        STRPLB  r14, [r9], #1
+        BPL     %BT01
+        EXITS
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r4 -> where element is to be copied
+;       r7 -> element to be copied
+;       r8 = sizeof(element)
+
+; Out   all preserved
+
+MoveFromGivenSlot ENTRY "r4, r7, r8"
+
+        TST     r4, #3                  ; If dest and element size wordy
+        TSTEQ   r8, #3                  ; then do faster copy. Also src wordy
+        BNE     %FT01
+
+00      SUBS    r8, r8, #4
+        LDRPL   r14, [r7], #4
+        STRPL   r14, [r4], #4
+        BPL     %BT00
+        EXITS
+
+01      SUBS    r8, r8, #1
+        LDRPLB  r14, [r7], #1
+        STRPLB  r14, [r4], #1
+        BPL     %BT01
+        EXITS
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Built-in sorting procedures
+
+hs_Procedures
+
+        DCD     hs_CardinalCMP    - hs_Procedures
+        DCD     hs_IntegerCMP     - hs_Procedures
+        DCD     hs_CardinalPtrCMP - hs_Procedures
+        DCD     hs_IntegerPtrCMP  - hs_Procedures
+        DCD     hs_StringCMP      - hs_Procedures
+        DCD     hs_StringSensCMP  - hs_Procedures
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r0, r1 -> cardinals
+
+; Out   flags set on (*r0) - (*r1)
+
+hs_CardinalPtrCMP
+
+        LDR     r0, [r0]
+        LDR     r1, [r1]
+
+; .............................................................................
+; In    r0, r1 = cardinals
+
+; Out   flags set on r0 - r1
+
+hs_CardinalCMP
+
+        CMP     r0, r1
+        BICCSS  pc, lr, #N_bit :OR: V_bit       ; CS -> GE (nv)
+        BIC     lr, lr, #V_bit
+        ORRS    pc, lr, #N_bit                  ; CC -> LT (Nv)
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r0, r1 -> integers
+
+; Out   flags set on (*r0) - (*r1)
+
+hs_IntegerPtrCMP
+
+        LDR     r0, [r0]
+        LDR     r1, [r1]
+
+; .............................................................................
+; In    r0, r1 = integers
+
+; Out   flags set on r0 - r1
+
+hs_IntegerCMP
+
+        CMP     r0, r1
+        MOV     pc, lr
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Case-insensitive string comparison
+
+; In    r0, r1 -> strings - CtrlChar terminated (NB. Must be same CtrlChar !)
+
+; Out   flags set on (char *)(r0) - (char *)(r1) compare
+
+hs_StringCMP ROUT
+
+10      LDRB    r2, [r0], #1
+        LowerCase r2, r12
+        LDRB    r3, [r1], #1
+        LowerCase r3, r12
+        CMP     r2, r3                  ; Differ ?
+        MOVNE   pc, lr                  ; GE or LT
+        CMP     r2, #space-1            ; Finished ?
+        BHI     %BT10
+
+        BICS    pc, lr, #N_bit :OR: V_bit ; GE
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Case-sensitive string comparison
+
+; In    r0, r1 -> strings - CtrlChar terminated (NB. Must be same CtrlChar !)
+
+; Out   flags set on (char *)(r0) - (char *)(r1)
+
+hs_StringSensCMP ROUT
+
+10      LDRB    r2, [r0], #1
+        LDRB    r3, [r1], #1
+        CMP     r2, r3                  ; Differ ?
+        MOVNE   pc, lr                  ; GE or LT
+        CMP     r2, #space-1            ; Finished ?
+        BHI     %BT10
+
+        BICS    pc, lr, #N_bit :OR: V_bit ; GE
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        LTORG
+
+        END
diff --git a/s/KbdResA1 b/s/KbdResA1
new file mode 100644
index 0000000000000000000000000000000000000000..c869f492c8fad08182a4a832f21d0ec987f29c9f
--- /dev/null
+++ b/s/KbdResA1
@@ -0,0 +1,330 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > KbdResA1
+
+; This file contains all the old-style keyboard control stuff that the kernel does on reset
+; The only two hooks in this file used externally are IRQ_Test_CTRL_or_R_Pressed
+; and SetUpKbd.
+
+                   GBLL   KeyboardDebungling
+KeyboardDebungling SETL   {FALSE}
+
+; reset code needs to know where CTRL, SHIFT and R are in the kbd matrix
+; these are codes given by the keyboard
+
+     [ Keyboard_Type = "A1A500"
+A1CtrlLeft      * &3B
+A1CtrlRight     * &61
+A1ShiftLeft     * &4C
+A1ShiftRight    * &58
+A1CTRLLCol   *  K1kdda + (A1CtrlLeft :AND: 15)
+A1CTRLLRow   *  K1kdda + (A1CtrlLeft :SHR: 4)
+A1CTRLRCol   *  K1kdda + (A1CtrlRight :AND: 15)
+A1CTRLRRow   *  K1kdda + (A1CtrlRight :SHR: 4)
+A1SHIFTLCol  *  K1kdda + (A1ShiftLeft :AND: 15)
+A1SHIFTLRow  *  K1kdda + (A1ShiftLeft :SHR: 4)
+A1SHIFTRCol  *  K1kdda + (A1ShiftRight :AND: 15)
+A1SHIFTRRow  *  K1kdda + (A1ShiftRight :SHR: 4)
+A1R_Col      *  K1kdda +  10
+A1R_Row      *  K1kdda +  2
+A1T_Col      *  K1kdda +  11
+A1T_Row      *  K1kdda +  2
+A1Del_Col    *  K1kdda +  4
+A1Del_Row    *  K1kdda +  3
+A1Copy_Col   *  K1kdda +  5
+A1Copy_Row   *  K1kdda +  3
+     ]
+
+     [ Keyboard_Type = "A1A500"
+; old (A500) keyboard positions
+
+A500CTRLRow    *   KEYDOWN + &C
+A500CTRLCol    *   KEYDOWN +  0
+A500SHIFTRow   *   KEYDOWN + &A
+A500SHIFTCol   *   KEYDOWN +  0
+A500R_Row      *   KEYDOWN +  2
+A500R_Col      *   KEYDOWN +  7
+A500T_Row      *   KEYDOWN +  2
+A500T_Col      *   KEYDOWN +  6
+A500Del_Row    *   KEYDOWN +  5
+A500Del_Col    *   KEYDOWN +  7
+A500Copy_Row   *   KEYDOWN +  0
+A500Copy_Col   *   KEYDOWN +  8
+    ]
+
+; On ARM600, this routine must work in IRQ32 mode
+
+IRQ_Test_CTRL_or_R_Pressed ROUT
+ [ CPU_Type = "ARM600"
+        BIC     pc, pc, #&FC000000      ; take us out of the shadow ROM area
+        NOP                             ; (this instruction skipped)
+ ]
+        Push    "r0-r2, R10-R12, lr"
+
+        MOV     R12, #IOC
+
+        MOV     r2, #IOC
+        MOV     r0, #32
+        BL      DoMicroDelay    ; quick thumb twiddle until it's REALLY there
+        LDRB    R11, KARTRx     ; read byte transmitted by keyboard
+
+     [ KeyboardDebungling
+   Push  R12
+   MOV   R12, R11, LSR #4
+   TubeChar  R10, R11, "MOV R11, #""R"""
+   TubeChar  R10, R11, "ADD R11, R12, #""0"""
+   AND   R12, R11, #&F
+   TubeChar  R10, R11, "ADD R11, R12, #""0"""
+   Pull  R12
+     ]
+
+        CMP     R11, #HRDRESET  ; first check for part of reset sequence and reply accordingly
+
+        BEQ     fartaboutfornewkbd
+
+        CMP     R11, #RST1ACK
+        MOVEQ   R10, #RST2ACK
+        BEQ     send_ack_byte
+
+        CMP     R11, #RST2ACK
+        BNE     keytransmission
+
+        MOV     R10, #InitKbdWs
+        LDR     R10, [R10, #KeyDataPtr]
+        CMP     R10, #0
+        MOVNE   R10, #ACK+SCAN
+        BNE     send_ack_byte
+        MOV     R10, #ACK
+
+  [ KeyboardDebungling
+   Push  R12
+   MOV   R12, R10, LSR #4
+   TubeChar  R10, R11, "MOV R11, #""k"""
+   TubeChar  R10, R11, "ADD R11, R12, #""0"""
+   AND   R12, R10, #&F
+   TubeChar  R10, R11, "ADD R11, R12, #""0"""
+   Pull  R12
+ ]
+
+        STRB    R10, KARTTx
+        BL      PollTxBit
+        MOV     R11, #K1rqid
+        BL      SendAndPollRxBit
+
+  [ Keyboard_Type = "A1A500"
+        AND     r10, r11, #&F0
+        CMP     R11, #IDTYPE            ; a500 kbd?
+        ADREQ   R10, DataA500Kbd
+        BEQ     gotkbdid
+  ]
+        SUB     r11, r11, #K1kbid + 1
+        CMP     r11, #30
+        ADRLSL  R10, DataA1Kbd          ; only accept ID 1-31
+        MOVHI   R10, #0                 ; else don't know
+
+gotkbdid
+        MOV     R11, #InitKbdWs
+        STR     R10, [R11, #KeyDataPtr]
+ [ Keyboard_Type = "A1A500"
+ ASSERT (DataA1Kbd :AND: 255) <> 0
+ ]
+ [ Keyboard_Type = "A1A500"
+ ASSERT (DataA500Kbd :AND: 255) <> 0
+ ]
+        STRB    R10, [R11, #KB_There_Flag]
+                                        ; only there once ID understood
+        MOV     R10, #HRDRESET          ; and from the top
+        B       send_ack_byte
+
+keytransmission
+; assume it's key info
+        MOV     R10, #InitKbdWs
+        LDRB    R10, [R10]      ; the "had a byte" flag
+        CMP     R10, #0
+        BNE     hadabyteofkey
+        MOV     R10, #InitKbdWs
+        STRB    R11, [R10]      ; first part of 2 byte protocol: set flag
+        MOV     R10, #ACK+&F
+        B       send_ack_byte
+
+fartaboutfornewkbd
+
+kickitagain
+        MOV     R11, #HRDRESET
+        BL      SendAndPollRxBit        ; get a byte to R11
+        BL      PollTxBit
+        CMP     R11, #HRDRESET
+        BNE     kickitagain
+        MOV     R11, #RST1ACK
+        BL      SendAndPollRxBit        ; get a byte to R11
+        BL      PollTxBit
+        CMP     R11, #RST1ACK
+        BNE     kickitagain
+        MOV     R10, #RST2ACK
+        B       send_ack_byte
+
+hadabyteofkey
+; now got 1st byte in R10, second byte in R11 : test for CTRL or R
+        MOV     R0, #InitKbdWs
+        LDR     R0, [R0, #KeyDataPtr]
+10      LDRB    R1, [R0], #1
+        CMP     R1, #0
+        BEQ     %FT11
+        CMP     R1, R10
+        LDRB    R1, [R0], #1
+        CMPEQ   R1, R11
+        LDRB    R1, [R0], #1
+        BNE     %BT10
+        MOV     R11, #InitKbdWs
+        STRB    R1, [R11, R1]           ; non-zero means pressed
+11
+        MOV     R10, #ACK+SCAN
+send_ack_byte
+
+     [ KeyboardDebungling
+   Push  R12
+   MOV   R12, R10, LSR #4
+   TubeChar  R10, R11, "MOV R11, #""T"""
+   TubeChar  R10, R11, "ADD R11, R12, #""0"""
+   AND   R12, R10, #&F
+   TubeChar  R10, R11, "ADD R11, R12, #""0"""
+   Pull  R12
+     ]
+
+        STRB    R10, KARTTx             ; assume always able to transmit?
+        CMP     R10, #ACK+&F
+        MOVNE   R11, #InitKbdWs
+        STRNEB  R11, [R11]            ; clear "one byte of 2 byte seq had" flag
+
+        Pull    "r0-r2, R10-R12, lr"
+        SUBS    PC, R14, #4
+
+ DCD 0 ; temp fudge
+
+ [ Keyboard_Type = "A1A500"
+  [ . :AND: 255 = 0
+ DCB "S" ; Odd length, should throw us
+  ]
+DataA1Kbd
+    =   A1CTRLLRow,  A1CTRLLCol,  CTRL_Down_Flag
+    =   A1CTRLRRow,  A1CTRLRCol,  CTRL_Down_Flag
+    =   A1SHIFTRRow, A1SHIFTRCol, SHIFT_Down_Flag
+    =   A1SHIFTLRow, A1SHIFTLCol, SHIFT_Down_Flag
+    =   A1R_Row,     A1R_Col,     R_Down_Flag
+    =   A1T_Row,     A1T_Col,     T_Down_Flag
+    =   A1Del_Row,   A1Del_Col,   Del_Down_Flag
+    =   A1Copy_Row,  A1Copy_Col,  Copy_Down_Flag
+    =   0
+ ]
+
+ [ Keyboard_Type = "A1A500"
+  [ . :AND: 255 = 0
+ DCB "K"
+  ]
+DataA500Kbd
+    =   A500CTRLRow,  A500CTRLCol,  CTRL_Down_Flag
+    =   A500SHIFTRow, A500SHIFTCol, SHIFT_Down_Flag
+    =   A500R_Row,    A500R_Col,    R_Down_Flag
+    =   A500T_Row,    A500T_Col,    T_Down_Flag
+    =   A500Del_Row,  A500Del_Col,  Del_Down_Flag
+    =   A500Copy_Row, A500Copy_Col, Copy_Down_Flag
+    =   0
+ ]
+
+    ALIGN
+
+        LTORG
+
+PollTxBit ROUT
+
+01      LDRB    R10, [R12, #IOCIRQSTAB]
+        TST     R10, #KARTTxBit
+        BEQ     %BT01
+        MOV     pc, lr
+
+
+SendAndPollRxBit ROUT
+
+        Push    lr
+
+     [ KeyboardDebungling
+   Push  R12
+   MOV   R12, R11, LSR #4
+   TubeChar  R10, R11, "MOV R11, #""t"""
+   TubeChar  R10, R11, "ADD R11, R12, #""0"""
+   AND   R12, R11, #&F
+   TubeChar  R10, R11, "ADD R11, R12, #""0"""
+   Pull  R12
+     ]
+
+        STRB    R11, KARTTx
+
+01      LDRB    R10, [R12, #IOCIRQSTAB]
+        TST     R10, #KARTRxBit
+        BEQ     %BT01
+
+        MOV    r2, #IOC
+        MOV    r0, #32
+        BL     DoMicroDelay
+        LDRB   R11, KARTRx              ; purge KART, or get reply
+
+     [ KeyboardDebungling
+   Push  R12
+   MOV   R12, R11, LSR #4
+   TubeChar  R10, R11, "MOV R11, #""r"""
+   TubeChar  R10, R11, "ADD R11, R12, #""0"""
+   AND   R12, R11, #&F
+   TubeChar  R10, R11, "ADD R11, R12, #""0"""
+   Pull  R12
+     ]
+
+        Pull   pc
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+SetUpKbd
+; set up keyboard: initialise baud rate, send dummy, read dummy
+        MOV     R12, #IOC               ; code ripped off from pmf.Key
+        MOV     R0, #1
+        STRB    R0, Timer3Low
+        MOV     R0, #0
+        STRB    R0, Timer3High
+        STRB    R0, Timer3Go            ; baud rate set and going
+
+        STRB    R0, KARTTx              ; send dummy
+
+        MOV     r1, r13
+        MOV     r13, #&8000             ; need a quick stack - scratchspace
+        Push    r1                      ; probably the best bet.
+
+        MOV     r0, #&800*2             ; magic delay
+        MOV     r2, #IOC
+        BL      DoMicroDelay
+
+        LDMFD   r13, {r13}              ; finished with stack
+
+        LDRB    R0, KARTRx              ; ensure no wally byte in KARTRx
+
+        BL      PollTxBit
+        MOV     R0, #HRDRESET           ; start reset protocol
+        STRB    R0, KARTTx
+
+        B       SetUpKbdReturn
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+KeyboardDebungling SETL {FALSE}
+
+        END
diff --git a/s/KbdResPC b/s/KbdResPC
new file mode 100644
index 0000000000000000000000000000000000000000..4bd1ddd92b7e57a8a398724bfab89b89813bd772
--- /dev/null
+++ b/s/KbdResPC
@@ -0,0 +1,187 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > KbdResPC
+
+; This file contains the minimal PC keyboard control stuff that the kernel does on reset.
+; The only two hooks in this file used externally are IRQ_Test_CTRL_or_R_Pressed
+; and SetUpKbd.
+
+; For now, use development podule in slot 0.
+ [ IO_Type = "IOMD"
+IOBase          *       IOMD_Base
+IOData          *       IOCSERTX
+IOStatus        *       IOMD_KBDCR
+IOControl       *       IOMD_KBDCR
+stat_RXF        *       IOMD_KBDCR_RxF
+stat_TXE        *       IOMD_KBDCR_TxE
+ctl_Enable      *       IOMD_KBDCR_Enable
+ctl_EnableIRQ   *       0       ; not needed on IOMD
+ |
+IOBase          *       &03000000
+IOData          *       0
+IOStatus        *       4
+IOControl       *       4
+stat_RXF        *       &20
+stat_TXE        *       &80
+ctl_Enable      *       &08
+ctl_EnableIRQ   *       &80
+ ]
+
+
+; PC keyboard codes we are interested in.
+PCReset         *       &AA
+PCSpecial       *       &E0
+PCCTRLL         *       &14
+PCCTRLR         *       &14     ; Preceded by &E0
+PCSHIFTL        *       &12
+PCSHIFTR        *       &59
+PCR             *       &2D
+PCT             *       &2C
+PCDelete        *       &71     ; Preceded by &E0
+PCEnd           *       &69     ; Preceded by &E0
+
+KeyData
+        DCB     PCCTRLL,  CTRL_Down_Flag
+        DCB     PCSHIFTL, SHIFT_Down_Flag
+        DCB     PCSHIFTR, SHIFT_Down_Flag
+        DCB     PCR,      R_Down_Flag
+        DCB     PCT,      T_Down_Flag
+        DCB     0
+        ALIGN
+
+SpecialData
+        DCB     PCCTRLR,  CTRL_Down_Flag
+        DCB     PCDelete, Del_Down_Flag
+        DCB     PCEnd,    Copy_Down_Flag
+        DCB     0
+        ALIGN
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+SetUpKbd
+        MOV     r0, #IOBase
+        MOV     r1, #ctl_Enable + ctl_EnableIRQ
+        STRB    r1, [r0, #IOControl]
+10
+        LDRB    r1, [r0, #IOStatus]
+        TST     r1, #stat_TXE
+        MOVNE   r1, #&FF
+        STRNEB  r1, [r0, #IOData]
+        BEQ     %BT10
+
+ [ MorrisSupport
+        LDRB    R1, [R0, #IOMD_ID0]             ;Are we running on Morris
+        CMP     R1, #&98
+        LDRB    R1, [R0, #IOMD_ID1]
+        CMPEQ   R1, #&5B
+        BNE     %FT30                           ;NE: no, assume IOMD, so only one PS2 port
+
+        MOV     R1, #IOMD_MSECR_Enable          ;yes, so initialise 2nd PS2 (mouse) port cos
+        STRB    R1, [R0, #IOMD_MSECR]           ;keyboard may be connected there instead
+20
+        LDRB    R1, [R0, #IOMD_MSECR]
+        TST     R1, #IOMD_MSECR_TxE             ;Is port ready to accept data
+        MOVNE   R1, #&FF                        ;NE: port ready, so send 'reset' command
+        STRNEB  R1, [R0, #IOMD_MSEDAT]          ;
+        BEQ     %BT20                           ;EQ: loop til port ready
+
+        MOV     R1, #IOMD_MouseRxFull_IRQ_bit
+        STRB    R1, [R0, #IOMD_IRQMSKD]
+
+        MOV     R0, #InitKbdWs
+        MOV     R1, #2
+        STRB    R1, [R0, #Port2Present]
+30
+ ]
+        MOV     r0, #InitKbdWs
+        ADR     r1, KeyData
+        STR     r1, [r0, #KeyDataPtr]
+
+        B       SetUpKbdReturn
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+; On ARM600, this routine must work in IRQ32 mode
+
+IRQ_Test_CTRL_or_R_Pressed ROUT
+ [ CPU_Type = "ARM600"
+        BIC     pc, pc, #&FC000000      ; take us out of the shadow ROM area
+        NOP                             ; (this instruction skipped)
+ ]
+        SUB     lr, lr, #4
+        Push    "r0-r2,lr"
+
+        MOV     r2, #IOBase
+ [ MorrisSupport
+        MOV     lr, #InitKbdWs
+        LDRB    r1, [lr, #Port2Present] ;Check if 2nd PS2 port (in Morris) is available
+        TEQ     r1, #0
+
+        LDRNEB  r0, [r2, #IOMD_MSECR]   ;NE: yes, so check if interrupt is from it
+        TSTNE   r0, #IOMD_MSECR_RxF     ;
+        LDRNEB  r2, [r2, #IOMD_MSEDAT]  ;NE: 2nd port present and interrupting, get scan code
+        MOVNE   r1, #2                  ;NE: indicate which port
+        BNE     %FT5                    ;NE: process it
+                                        ;EQ: 2nd port not present or interrupting
+                                        ;    drop through and check 1st port
+ ]
+        LDRB    r0, [r2, #IOStatus]
+        TST     r0, #stat_RXF           ; If not keyboard then
+        Pull    "r0-r2,pc",EQ,^         ;   exit.
+
+        LDRB    r2, [r2, #IOData]       ; Get scan code.
+
+ [ MorrisSupport
+        MOV     r1, #1
+5
+        LDRB    r0, [lr, #KB_There_Flag]
+
+        TEQ     r2, #0                  ;Assume that zero is the end of a mouse AA 00 start up
+        BICEQ   r0, r0, r1              ; sequence, so clear keyboard present indication.
+        STREQB  r0, [lr, #KB_There_Flag]
+        Pull    "r0-r2,pc",EQ,^         ; and exit
+
+        ORRNE   r0, r0, r1              ;Not zero, mark keyboard present
+ ]
+
+        MOV     lr, #InitKbdWs
+
+        STRB    r0, [lr, #KB_There_Flag]        ; Keyboard must be there (r0<>0 from above).
+
+        ADR     r1, SpecialData
+
+        TEQ     r2, #PCSpecial          ; If special code then
+        STREQ   r1, [lr, #KeyDataPtr]   ;   switch tables
+        Pull    "r0-r2,pc",EQ,^         ;   and exit.
+
+        LDR     r0, [lr, #KeyDataPtr]   ; Get pointer to current table.
+
+        TEQ     r0, r1                  ; Only use special table once, then
+        ADREQ   r1, KeyData             ;   switch back to normal table.
+        STREQ   r1, [lr, #KeyDataPtr]
+10
+        LDRB    r1, [r0], #2            ; Get key code from table.
+        TEQ     r1, #0                  ; If at end of table then
+        Pull    "r0-r2,pc",EQ,^         ;   ignore key.
+
+        TEQ     r1, r2                  ; If not this key then
+        BNE     %BT10                   ;   try the next.
+
+        LDRB    r1, [r0, #-1]           ; Get flag.
+        STRB    r1, [lr, r1]            ; Non-zero means pressed.
+
+        Pull    "r0-r2,pc",,^
+
+        END
diff --git a/s/Kernel b/s/Kernel
new file mode 100644
index 0000000000000000000000000000000000000000..9f15fbb54727d2c6141e3be4c7b56280b0977262
--- /dev/null
+++ b/s/Kernel
@@ -0,0 +1,1315 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL     => Kernel : SWI Despatch, simple SWIs
+        SUBT    Arthur Variables
+        OPT     4
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; handy macros:
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+         MACRO
+$l       CheckSpaceOnStack   $space, $faildest, $tmp
+ [ True ; SKS
+$l      MOV     $tmp, sp, LSR #15       ; Stack base on 32K boundary
+        SUB     $tmp, sp, $tmp, LSL #15 ; Amount of stack left
+        CMP     $tmp, #$space           ; Must have at least this much left
+        BMI     $faildest
+ |
+$l       MOV        $tmp, #32*1024   ; assume stack ends at next 32K boundary
+         SUB        $tmp, $tmp, #1
+         AND        $tmp, $tmp, stack
+         CMP        $tmp, #$space
+         BLT        $faildest
+ ]
+         MEND
+
+        MACRO
+        assert  $condition
+ [ :LNOT: ($condition)
+ ! 1,"Assert failed: $condition"
+ ]
+        MEND
+
+; one that builds a module command table entry:
+; set Module_BaseAddr to module base before use.
+
+                GBLA    Module_BaseAddr
+Module_BaseAddr SETA    0
+
+;        Command $cmd, $max, $min   - declared in hdr.macros.
+
+; debug macro: set the border colour
+
+        MACRO
+$l      SetBorder  $reg1, $reg2, $red, $green, $blue, $delay
+        ! 0, "Setborder used"
+$l      LDR     $reg1, =VIDC
+ [ VIDC_Type = "VIDC20"
+; Note $reg, $green and $blue are 4 bit values
+        LDR     $reg2, =&40000000+(($red)*&11)+(($green)*&1100)+(($blue)*&110000)
+ |
+        LDR     $reg2, =&40000000+ $red + $green *16 + $blue *256
+ ]
+        STR     $reg2, [$reg1]
+ [ "$delay"<>""
+        MOV     $reg1, #$delay
+10
+        SUBS    $reg1, $reg1, #1
+        BNE     %BT10
+ ]
+        MEND
+
+    [ AddTubeBashers
+ [ TubeType = Tube_Normal
+DebugTUBE  * &03340000+3*&4000         ; tube in podule #3
+; Tube register offsets
+ ^ 0
+R1STAT # 4
+R1DATA # 4
+ |
+DebugTUBE * &03000000           ; simulator tube address
+R1DATA * 0
+ ]
+   ]
+
+; routine to stuff a char down the Tube
+; should be inside above conditional, but AAsm winges pitifully.
+     MACRO
+$l   TubeChar $reg1, $reg2, $charset, $stackthere
+  !  0, "TubeChar used."
+$l
+   [ "$stackthere"=""
+   Push "$reg1, $reg2"
+   ]
+     LDR  $reg1, =DebugTUBE
+ [ TubeType = Tube_Normal               ; normal tubes have status register, simulator one doesn't
+01   LDRB $reg2, [$reg1, #R1STAT]
+     TST $reg2, #&40
+     BEQ %BT01
+ ]
+     $charset
+     STRB $reg2, [$reg1, #R1DATA]
+   [ "$stackthere"=""
+   Pull "$reg1, $reg2"
+   ]
+     MEND
+
+        MACRO
+$l      TubeString $reg1, $reg2, $reg3, $string, $cc
+        LDR     $reg1, =DebugTUBE
+        ADR     $reg2, %FT20
+10
+ [ TubeType = Tube_Normal
+        LDRB    $reg3, [$reg1, #R1STAT]
+        TST     $reg3, #&40
+        BEQ     %BT10
+ ]
+
+        LDRB    $reg3, [$reg2], #1
+        TEQ     $reg3, #0
+        STRNEB  $reg3, [$reg1, #R1DATA]
+        BNE     %BT10
+        B       %FT30
+20
+        =       "$string"
+ [ "$cc" = ""
+        =       10, 13
+ ]
+        =       0
+        ALIGN
+30
+        MEND
+
+        MACRO
+$l      TubeDumpNoStack $dump, $t1, $t2, $t3
+$l      MOV    $t1, #7
+01
+        ADRL   $t2, HexTable
+        TubeChar $t3, $t2, "LDRB $t2, [$t2, $dump, LSR #28]", NoStack
+        MOV    $dump, $dump, ROR #28
+        SUBS   $t1, $t1, #1
+        BPL    %BT01
+        TubeChar $t3, $t2, "MOV $t2, #"" """, NoStack
+        MEND
+
+        MACRO
+$l      TubeNewlNoStack $t1, $t2
+$l      TubeChar $t1, $t2, "MOV $t2, #10", NoStack
+        TubeChar $t1, $t2, "MOV $t2, #13", NoStack
+        MEND
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Various constants
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+MinAplWork * 40*1024         ; minimum size of AplWork
+
+; Fixed addresses
+
+MEMCADR  * &3600000
+ROM      * &3800000
+OSMD     * &11111111
+
+ [ MEMC_Type = "IOMD"
+VideoPhysRam *  &02000000               ; Amazing - it's in the same place!
+DRAM0PhysRam *  &10000000               ; 4 DRAM banks
+DRAM1PhysRam *  &14000000
+DRAM2PhysRam *  &18000000
+DRAM3PhysRam *  &1C000000
+DRAMBaseAddressMask * &1C000000         ; used to mask off bits after stealing video RAM
+PhysSpaceSize * &20000000               ; IOMD physical map is 512M big
+PhysROM *       &00000000               ; and real ROM starts at 0
+SAMLength *     512*4                   ; SAM length in bytes for 1 bank of VRAM
+EASISpacePhys * &08000000
+EASISpace *     PhysSpace + EASISpacePhys
+ |
+VideoPhysRam *  &02000000
+PhysSpaceSize * &04000000               ; MEMC1 physical map is 64M big
+PhysROM *       &03800000
+PhysRamPhys *   &02000000               ; physical space starts here
+ ]
+
+; Manifests
+
+CR * 13
+LF * 10
+space * " "
+
+; Registers
+
+SPIRQ RN R13
+
+; Callback byte bits:
+CBack_OldStyle  * 1
+CBack_Postpone  * 2
+CBack_VectorReq * 4
+
+; Set up symbols for the SWIs which aren't really there yet
+
+ [ :LNOT: ModeSelectors
+ScreenModeSWI * NoSuchSWI
+ ]
+ [ :LNOT: NewCDA
+DynamicAreaSWI * NoSuchSWI
+ ]
+AbortTrapSWI * NoSuchSWI
+
+        SUBT    Arthur Code
+        OPT     4
+
+; *****************************************************************************
+;
+;  Now ready to start the code: off we go!
+;
+; *****************************************************************************
+
+        ORG     ROM
+
+        GBLS    DoMorrisROMHeader
+
+ [ MorrisSupport
+DoMorrisROMHeader SETS  " GET s.Morris"
+ |
+DoMorrisROMHeader SETS  ""
+ ]
+
+; now include the test code, if there is any
+
+        GBLS    DoTestThings
+
+ [ IncludeTestSrc
+DoTestThings    SETS    " GET TestSrc.Begin"
+ |
+DoTestThings    SETS    ""
+ ]
+        $DoTestThings
+
+ [ IncludeTestSrc
+DoMorrisROMHeader SETS  ""
+ ]
+
+        $DoMorrisROMHeader
+
+ [ ProcessorVectors
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; If there is no test code then we want a branch table at the start of ROM to
+; handle reset and any aborts etc. in the reset code.
+; If MorrisSupport we've already generated 16/32 ROM entry code, so skip this bit
+
+  [ :LNOT: IncludeTestSrc :LAND: :LNOT: MorrisSupport
+   [ ResetIndirected
+        LDR     pc, .+ResetIndirection ; load PC, PC relative
+   |
+    [ MEMC_Type = "IOMD"
+        B       CONT                            ; PhysROM is at zero on IOMD
+    |
+        B       MOSROMVecs+CONT                 ; executed out of ROM or RAM
+    ]
+   ]
+        B       UndInstInReset
+        B       SWIInReset
+        B       PrefAbInReset
+        B       DataAbInReset
+        B       AddrExInReset
+        B       IRQInReset
+
+UndInstInReset
+        SUB     pc, pc, #8
+SWIInReset
+        SUB     pc, pc, #8
+PrefAbInReset
+        SUB     pc, pc, #8
+DataAbInReset
+        SUB     pc, pc, #8
+AddrExInReset
+        SUB     pc, pc, #8
+IRQInReset
+        SUB     pc, pc, #8
+  ]
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; This bit (up to EndFiq) is copied to location 0.  Processor vectors are
+; indirected through 0 page locations so that they can be claimed using
+; OS_ClaimProcessorVector.  IRQs are initially handled by a branch so that the
+; keyboard can be handled during reset but the branch is replaced with a load
+; PC, PC relative later on.
+
+MOSROMVecs
+        LDR     pc, MOSROMVecs+ProcVec_Branch0
+        LDR     pc, MOSROMVecs+ProcVec_UndInst
+        LDR     pc, MOSROMVecs+ProcVec_SWI
+        LDR     pc, MOSROMVecs+ProcVec_PrefAb
+        LDR     pc, MOSROMVecs+ProcVec_DataAb
+        LDR     pc, MOSROMVecs+ProcVec_AddrEx
+        B       MOSROMVecs+IRQ_Test_CTRL_or_R_Pressed
+
+        MOV     r10, #IOC               ; we dunno what to do with it -
+                                        ; so cut it off - right off!
+        STRB    r10, [r10, #IOCFIQMSK]  ; :LSB: IOC = 0
+        SUBS    pc, r14, #4
+EndFiq
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; This is the table of default processor vectors which is copied to 0 page.
+
+DefaultProcVecs
+        &       RESET1
+        &       UndPreVeneer
+        &       SVC
+        &       PAbPreVeneer
+        &       DAbPreVeneer
+        &       AdXPreVeneer
+        &       Initial_IRQ_Code
+
+        ASSERT  (.-DefaultProcVecs) = ProcVec_End-ProcVec_Start
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; These are preveneers which must be copied to 0 page locations so that the
+; relevant handler can be branched to.  This is mainly for non-ARM600 platforms
+; although the address exception preveneer (which should not actually be required
+; on ARM600) is always copied.
+
+DefaultPreVeneers
+  [ CPU_Type <> "ARM600"
+UndPreVeneer    *       ProcVecPreVeneers+(.-DefaultPreVeneers)
+        LDR     PC, DefaultPreVeneers-ProcVecPreVeneers+UndHan
+PAbPreVeneer    *       ProcVecPreVeneers+(.-DefaultPreVeneers)
+        LDR     PC, DefaultPreVeneers-ProcVecPreVeneers+PAbHan
+DAbPreVeneer    *       ProcVecPreVeneers+(.-DefaultPreVeneers)
+        LDR     PC, DefaultPreVeneers-ProcVecPreVeneers+DAbHan
+  |
+        DCD     0
+        DCD     0
+        DCD     0
+  ]
+AdXPreVeneer    *       ProcVecPreVeneers+(.-DefaultPreVeneers)
+        LDR     PC, DefaultPreVeneers-ProcVecPreVeneers+AdXHan
+
+        ASSERT  (.-DefaultPreVeneers) = ProcVecPreVeneersSize
+
+ |
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; The bit to copy to 0
+; - if no test software present, then it's at the start of the ROM and will get
+; executed out of ROM, or from the RAM copy later on
+; - if test software is present, then it's not at the start of the ROM, so the
+; code will only work when copied to 0!
+
+MOSROMVecs
+  [ ResetIndirected
+        LDR     pc, MOSROMVecs+ResetIndirection ; load PC, PC relative
+  |
+   [ MEMC_Type = "IOMD"
+        B       CONT                            ; PhysROM is at zero on IOMD
+   |
+        B       MOSROMVecs+CONT                 ; executed out of ROM or RAM
+                                                ; or not at all if test software present
+   ]
+  ]
+
+  [ CPU_Type = "ARM600"
+        B       MOSROMVecs+UndPreVeneer
+        B       MOSROMVecs+SVC
+        B       MOSROMVecs+PAbPreVeneer
+        B       MOSROMVecs+DAbPreVeneer
+  |
+        LDR     PC, MOSROMVecs+UndHan
+        B       MOSROMVecs+SVC
+        LDR     PC, MOSROMVecs+PAbHan
+        LDR     PC, MOSROMVecs+DAbHan
+  ]
+        LDR     PC, MOSROMVecs+AdXHan
+        B       MOSROMVecs+IRQ_Test_CTRL_or_R_Pressed
+        MOV     R10, #IOC               ; we dunno what to do with it -
+                                        ; so cut it off - right off!
+        STRB    R10, [R10, #IOCFIQMSK]  ; :LSB: IOC = 0
+        SUBS    PC, R14, #4
+EndFiq
+
+ ]
+
+ [ ResetIndirected :LAND: :LNOT: IncludeTestSrc
+
+; We now waste space until the offset into the ROM is the same as the RAM address of ResetIndirection
+; This is so that
+;  a) on a reset, ROM is paged in at the bottom, so we jump to CONT, and
+;  b) on a break, RAM is paged in at the bottom, so we jump to CONT_Break
+
+        ASSERT  .-ROM <= ResetIndirection
+        %       ResetIndirection-(.-ROM)
+        &       CONT-ROM+PhysROM        ; address of reset code in physical space
+ ]
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Now some initialised workspace/vectors that go at &100
+
+; All the stuff from here to after the DirtyBranch instruction is read
+; consecutively out of ROM, so don't put anything in between without changing
+; the code
+
+
+StartData
+        ASSERT IRQ1V = &100
+        & DefaultIRQ1V
+
+        ASSERT ESC_Status = IRQ1V+4
+        & &00FF0000       ; IOCControl set to FF on reset
+
+        ASSERT IRQsema = ESC_Status+4
+        & 0
+EndData
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI return handler: checks callback
+
+SVCDespatcher ROUT
+
+SWIRelocation * SVCDespatcher-SWIDespatch
+
+SLVK_SetV * {PC}-SWIRelocation
+
+        ORR     lr, lr, #V_bit
+
+SLVK_TestV * {PC}-SWIRelocation
+ ! 0,"SLVK_TestV at ":CC:(:STR:SLVK_TestV)
+
+        ORRVS   lr, lr, #V_bit
+
+SLVK * {PC}-SWIRelocation
+ ! 0,"SLVK       at ":CC:(:STR:SLVK)
+
+        Pull    r11
+        TST     lr, #V_bit
+        BNE     %FT50
+
+SWIReturn * {PC}-SWIRelocation
+ ! 0,"SWIReturn  at ":CC:(:STR:SWIReturn)
+
+40      MOV     r10, #0
+        LDRB    r11, [r10, #CallBack_Flag]
+        CMP     r11, #0
+        Pull    "r10-r12", EQ
+        MOVEQS  pc, lr
+
+        B       callback_checking + SWIRelocation
+
+
+ ! 0,"VSetReturn at ":CC:(:STR:({PC}-SWIRelocation))
+50      TST     r11, #Auto_Error_SWI_bit
+        BNE     %BT40
+
+        B       VSet_GenerateError + SWIRelocation
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; The SWI Despatch routine
+
+SVC * {PC}-SWIRelocation
+
+ [ CPU_Type = "ARM600"
+
+; For now, branch back up to ROM to perform ARM600 contortions
+
+        B       SVCARM600 + SWIRelocation
+
+SVCContinue * {PC}-SWIRelocation
+
+ |
+        Push    "r10-r12"
+ ]
+
+        BIC     r12, r14, #ARM_CC_Mask
+        LDR     r11, [r12, #-4]
+        BIC     r11, r11, #&FF000000
+        Push    r11
+        BICS    r11, r11, #Auto_Error_SWI_bit
+        BEQ     SWIWriteC + SWIRelocation
+
+        ORR     r10, r14, #SVC_mode
+        TEQP    r10, #0                 ; restore caller's IRQ state
+
+        CMP     r11, #OS_BreakPt
+        CMPNE   r11, #OS_CallAVector
+        BICNE   r14, r14, #V_bit        ; clear V unless BreakPoint/CallVector
+
+        CMP     r11, #OS_WriteI
+        LDRCC   pc, [pc, r11, LSL #2]
+
+        B       NotMainMOSSwi + SWIRelocation
+
+ ASSERT {PC}-SVCDespatcher = SWIDespatch_Size
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; The SWI table
+
+JTABLE  & SWIWriteC             ; this entry never gets used (see ^)
+        & SWIWriteS
+        & SWIWrite0
+        & SWINewLine
+
+; next section is one where VectorNumber = SWINumber
+        & VecSwiDespatch        ; readc
+        & VecSwiDespatch        ; cli
+        & NoIrqVecSwiDespatch   ; byte
+        & NoIrqVecSwiDespatch   ; word
+        & VecSwiDespatch        ; file
+        & VecSwiDespatch        ; args
+        & BGetSWI               ; bget
+        & BPutSWI               ; bput
+        & VecSwiDespatch        ; gbpb
+        & VecSwiDespatch        ; find
+        & VecSwiDespatch        ; readline
+
+        & SCTRL
+        & SWI_GetEnv_Code
+        & SEXIT
+        & SSTENV
+        & SINTON
+        & SINTOFF
+        & SCALLB
+        & SENTERSWI
+        & SBRKPT
+        & SBRKCT
+        & SUNUSED
+        & SSETMEMC
+        & SSETCALL
+        & VecMouse
+        & HeapEntry
+        & ModuleHandler
+        & ClaimVector_SWICode
+        & ReleaseVector_SWICode
+        & ReadUnsigned_Routine
+        & GenEvent
+        & ReadVarValue
+        & SetVarValue
+        & GSINIT
+        & GSREAD
+        & GSTRANS
+        & CvtToDecimal
+        & FSControlSWI
+        & ChangeDynamicSWI
+        & GenErrorSWI
+        & ReadEscapeSWI
+        & ReadExpression
+        & SwiSpriteOp
+        & SWIReadPalette
+        & Issue_Service_SWI
+        & SWIReadVduVariables
+        & SwiReadPoint
+        & DoAnUpCall
+        & CallAVector_SWI
+        & SWIReadModeVariable
+        & SWIRemoveCursors
+        & SWIRestoreCursors
+        & SWINumberToString_Code
+        & SWINumberFromString_Code
+        & ValidateAddress_Code
+        & CallAfter_Code
+        & CallEvery_Code
+        & RemoveTickerEvent_Code
+        & InstallKeyHandler
+        & SWICheckModeValid
+        & ChangeEnvironment
+        & SWIClaimScreenMemory
+        & ReadMetroGnome
+        & XOS_SubstituteArgs_code
+        & XOS_PrettyPrint_code
+        & SWIPlot
+        & SWIWriteN
+        & Add_ToVector_SWICode
+        & WriteEnv_SWICode
+        & RdArgs_SWICode
+        & ReadRAMFSLimits_Code
+        & DeviceVector_Claim
+        & DeviceVector_Release
+        & Application_Delink
+        & Application_Relink
+        & HeapSortRoutine
+        & TerminateAndSodOff
+        & ReadMemMapInfo_Code
+        & ReadMemMapEntries_Code
+        & SetMemMapEntries_Code
+        & AddCallBack_Code
+        & ReadDefaultHandler
+        & SWISetECFOrigin
+        & SerialOp
+        & ReadSysInfo_Code
+        & Confirm_Code
+        & SWIChangedBox
+        & CRC_Code
+        & ReadDynamicArea
+        & SWIPrintChar
+        & ChangeRedirection
+        & RemoveCallBack
+        & FindMemMapEntries_Code
+        & SWISetColour
+        & NoSuchSWI                     ; Added these to get round OS_ClaimSWI and
+        & NoSuchSWI                     ; OS_ReleaseSWI (should not have been allocated here).
+ [ AssemblePointerV
+        & PointerSWI
+ |
+        & NoSuchSWI
+ ]
+        & ScreenModeSWI
+        & DynamicAreaSWI
+        & AbortTrapSWI
+ [ CPU_Type = "ARM600"
+        & MemorySWI
+ |
+        & NoSuchSWI
+ ]
+ [ ProcessorVectors
+        & ClaimProcVecSWI
+ |
+        & NoSuchSWI
+ ]
+        & PerformReset
+ [ CPU_Type = "ARM600"
+        & MMUControlSWI
+ |
+        & NoSuchSWI
+ ]
+
+MaxSwi * (.-JTABLE)/4
+
+ ASSERT MaxSwi < OS_ConvertStandardDateAndTime
+
+; SWIs for time/date conversion are poked in specially
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; The fudge branch to exit a dirty SWI handler
+
+DirtyBranch
+        B       SLVK +DirtyBranch-BranchToSWIExit
+
+; All the stuff from MOSROMVecs to here is read consecutively out of ROM,
+; so don't put anything in between without changing the code
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ [ CPU_Type = "ARM600"
+
+; in: SVC32_mode
+; For now, only allow SWIs to be executed from below 64M
+
+SVCARM600 ROUT
+        Push    "r10-r12"               ; instruction normally done in SWI despatch area
+        mrs     AL, r11, SPSR_all                       ; r11 = saved PSR
+        AND     r12, r11, #&F0000003                    ; get saved NZCV and 26 bit modes
+        ORR     lr, lr, r12
+        AND     r12, r11, #I32_bit + F32_bit            ; extract I and F from new place
+        ORR     lr, lr, r12, LSL #IF32_26Shift          ; lr = combined lr and psr
+
+        SetMode SVC26_mode, r12                         ; now switch into SVC26
+
+        B       SVCContinue             ; go back into the SWI despatch area
+ ]
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r10-r12 stacked, lr has bits for return
+
+VSet_GenerateError ROUT
+
+        Push    lr
+        MOV     r1, #Service_Error
+        BL      Issue_Service
+
+        MOV     r10, #ErrorV
+        BL      CallVector              ; Normally gets to default handler...
+
+        Pull    lr                      ; which raises error; otherwise just
+        BIC     lr, lr, #V_bit          ; return with V clear: error claimed!
+        B       SWIReturn
+
+        LTORG
+
+; ....................... default owner of ErrorV .............................
+; In    r0  -> error in current error block
+
+; Out   Exits to user's error handler routine as given by ErrHan
+;       r1-r9 possibly corrupt. Indeed r10-r12 MAY be duff ... eg. REMOTE
+
+ErrHandler ROUT
+
+        BL      OscliTidy               ; close redirection, restore curr FS
+
+        MOV     r12, #0
+        LDR     r11, [r12, #ErrBuf]     ; Get pointer to error buffer
+
+        LDR     sp_svc, =SVCSTK-4*4     ; Just below top of stack
+                                        ; Note gives WRONG pc if error
+        Pull    r14                     ; generated by any but bottom SWI
+        STR     r14, [r11], #4          ; Return PC for error
+
+        LDR     r14, [r0], #4           ; Copy error number
+        STR     r14, [r11], #4
+
+        ; Copy error string - truncating at 252
+        MOV     r10, #256-4
+
+10      LDRB    r14, [r0], #1
+        SUBS    r10, r10, #1
+        MOVLS   r14, #0
+        STRB    r14, [r11], #1
+        TEQ     r14, #0
+        BNE     %BT10
+
+        LDR     r14, [r12, #ErrHan]     ; And go to error handler
+        BIC     r14, r14, #ARM_CC_Mask
+        STR     r14, [r12, #Curr_Active_Object]
+        LDR     r0,  [r12, #ErrHan_ws]  ; r0 is his wp
+
+        LDRB    r10, [r12, #CallBack_Flag]
+        CMP     r10, #0
+
+        Pull    "r10-r12", EQ
+        MOVEQS  pc, r14                 ; USR mode, IRQs enabled
+
+        B       Do_CallBack             ; Can't need postponement, r0,r14
+                                        ; such that callback code will normally
+                                        ; call error handler
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; check for CallBack possible
+
+callback_checking
+
+        TST     lr, #SVC_mode :OR: I_bit ; user mode, ints enabled?
+        Pull    "r10-r12", NE
+        MOVNES  pc, lr                  ; Skip the branch for SVC code speed
+
+; Further checks: postpone callback if returning V set and R0->RAM
+
+        TST     lr, #V_bit
+        BEQ     Do_CallBack
+        TST     r11, #CBack_Postpone    ; only one postponement
+        BNE     Do_CallBack             ; allowed.
+        CMP     r0, #32*1024*1024
+        BGE     Do_CallBack
+
+        TEQP    PC, #SVC_mode+I_bit            ; ints off while flag updated
+        LDRB    r11, [r10, #CallBack_Flag]
+        ORR     r11, r11, #CBack_Postpone      ; signal to IRQs
+        STRB    r11, [r10, #CallBack_Flag]
+back_to_user
+        Pull   "r10-r12"
+        MOVS    PC, lr
+
+Do_CallBack                                    ; CallBack allowed:
+        TST     r11, #CBack_VectorReq          ; now process any vector entries
+        Push    lr, NE
+        BLNE    process_callback_chain
+        Pull    lr, NE
+
+        TST     r11, #CBack_OldStyle
+        BEQ     back_to_user
+ [ {TRUE}                                       ; LRust, Fix RP-0609
+; Check that SVC_sp is empty (apart from r10-r12), i.e. system truly is idle
+
+        LDR     r11, =SVCSTK-3*4                ; What SVC_sp should be if system idle
+        CMP     sp, r11                         ; Stack empty?
+        BLO     back_to_user                    ; No then no call back
+ ]
+        TEQP    PC, #SVC_mode+I_bit             ; ints off while flag updated
+        LDRB    r11, [r10, #CallBack_Flag]
+        BIC     r11, r11, #CBack_Postpone+CBack_OldStyle
+        STRB    r11, [r10, #CallBack_Flag]
+
+        MOV     r12, #0
+        LDR     R12, [R12, #CallBf]
+        STR     r14, [r12, #4*15]             ; user PC
+        MOV     r14, r12
+        Pull   "r10-r12"
+        STMIA   r14, {r0-r14}^                ; user registers
+
+        MOV     R12, #CallAd_ws
+        LDMIA   R12, {R12, PC}                ; jump to CallBackHandler
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Also called from source.pmf.key, during readc
+
+process_callback_chain ROUT
+
+        Push   "r0-r6, r10-r12, lr"             ; save some for the callee too.
+        MOV     r10, #0
+
+        TEQP    PC, #SVC_mode+I_bit             ; ints off while flag updated
+        LDRB    r11, [r10, #CallBack_Flag]
+        BIC     r11, r11, #CBack_VectorReq
+        STRB    r11, [r10, #CallBack_Flag]
+
+ [ True
+  [ Fix15
+
+; TMD 11-Sep-89; used to free the wrong node here - usually would try to free
+; a null node, and claimed node would not get freed - however, if more than one
+; request, then it could free the 2nd node before it was used.
+
+01      TEQP    PC, #SVC_mode+I_bit             ; ints off while head updated
+        MOV     r2, #0
+        LDR     r2, [r2, #CallBack_Vector]
+        TEQ     r2, #0
+        Pull   "r0-r6, r10-r12, PC",EQ,^
+
+        LDMIA   r2, {r10, r11, r12}             ; link, addr, r12
+        MOV     r0, #HeapReason_Free
+        STR     r10, [r0, #CallBack_Vector-HeapReason_Free] ; Keep head valid
+
+        TEQP    PC, #SVC_mode                   ; enable ints for long bits
+        LDR     r1, =SysHeapStart
+        SWI     XOS_Heap
+
+        MOV     lr, pc
+        MOV     pc, r11                         ; call im, with given r12
+
+        B       %BT01                           ; loop
+
+  |
+01      TEQP    PC, #SVC_mode+I_bit             ; ints off while head updated
+        MOV     r10, #0
+        LDR     r10, [r10, #CallBack_Vector]
+        TEQ     r10, #0
+        Pull   "r0-r6, r10-r12, PC",EQ,^
+
+        LDMIA   r10, {r2, r11, r12}             ; link, addr, r12
+        MOV     r0, #HeapReason_Free
+        STR     r2, [r0, #CallBack_Vector-HeapReason_Free] ; Keep head valid
+
+        TEQP    PC, #SVC_mode                   ; enable ints for long bits
+        LDR     r1, =SysHeapStart
+        SWI     XOS_Heap
+
+        MOV     lr, pc
+        MOV     pc, r11                         ; call im, with given r12
+
+        B       %BT01                           ; loop
+
+  ]
+ | ; old one
+
+        TEQP    PC, #SVC_mode
+
+        LDR     r10, [r10, #CallBack_Vector]
+01      MOV     r2, r10                         ; for freeing
+        LDMIA   r10, {r10, r11, r12}            ; link, addr, r12
+        MOV     r0, #HeapReason_Free
+        STR     r10, [r0, #CallBack_Vector-HeapReason_Free]
+        LDR     r1, =SysHeapStart
+        SWI     XOS_Heap
+        MOV     lr, pc
+        MOV     pc, r11                         ; call im
+        CMP     r10, #0
+        BNE     %BT01
+        Pull   "r0-r6, r10-r12, PC",,^
+ ]
+
+        LTORG
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_WriteC
+
+; In    r11 = 0 (look, up there ^) !
+
+SWIWriteC ROUT
+
+        TEQP    pc, #SVC_mode           ; enable interrupts
+
+        BIC     lr, lr, #V_bit          ; clear caller's V cos we didn't before
+        Push    lr
+
+        LDR     r11, [r11, #VecPtrTab+WrchV*4] ; load top node pointer
+        CMP     r11, #ROM
+        Push    pc, CS                 ; push address of ReturnFromVectoredWrch
+        BCS     PMFWrchDirect
+        BCC     WrchThruVector
+
+ReturnFromVectoredWrch
+        Pull    lr
+        B       SLVK_TestV
+
+
+WrchThruVector
+        MOV     r10, #WrchV
+        BL      CallVector
+        B       ReturnFromVectoredWrch
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+SWINewLine ROUT
+
+        MOV     r11, lr
+        SWI     XOS_WriteI+10
+        SWIVC   XOS_WriteI+13
+        MOV     lr, r11
+        B       SLVK_TestV
+
+; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_WriteI+n
+
+SWIWriteI ROUT
+
+        MOV     r10, r0
+        AND     r0, r11, #&FF
+        MOV     r11, lr                 ; NB. Order !!!
+        SWI     XOS_WriteC
+        MOVVC   r0, r10
+        MOV     lr, r11
+        B       SLVK_TestV              ; return setting V
+
+; .............................................................................
+; define module SWI node format
+
+ModSWINode_CallAddress * 0
+ModSWINode_MListNode   * 4
+ModSWINode_Link        * 8
+ModSWINode_Number      * 12
+ModSWINode_Size        * 16     ; not a field - the node size!
+
+        MACRO
+$l      ModSWIHashvalOffset     $swino, $startreg
+     [ "$startreg"=""
+$l      MOV     $swino, $swino, LSR #4
+     |
+$l      MOV     $swino, $startreg, LSR #4
+     ]
+        AND     $swino, $swino, #ModuleSHT_Entries*4-1
+        MEND
+
+        MACRO
+$l      ModSWIHashval   $swino, $startreg
+$l      ModSWIHashvalOffset $swino, $startreg
+        ADD     $swino, $swino, #ModuleSWI_HashTab
+        MEND
+
+
+
+NotMainMOSSwi ; Continuation of SWI despatch
+
+        CMP     R11, #&200
+        BCC     SWIWriteI
+
+; .............................................................................
+; Look round RMs to see if they want it
+
+ExtensionSWI ROUT
+
+        Push    "r9, lr"                ; first construct the link to pass on.
+        AND     r10, lr, #&FC000000     ; copy in user CCodes (inc. IRQ state)
+        ADR     lr, %FT02 + SVC_mode
+        ORR     lr, lr, r10
+
+        BIC     r12, r11, #Module_SWIChunkSize-1
+;        BIC     r12, r12, #Auto_Error_SWI_bit  ; r11 already has this bit knocked off
+        ModSWIHashvalOffset r10, r12
+        LDR     r10, [r10, #ModuleSWI_HashTab]
+loopoverhashchain
+        CMP     r10, #0
+        BEQ     VectorUserSWI
+        LDR     r9, [r10, #ModSWINode_Number]
+        CMP     r9, r12
+        LDRNE   r10, [r10, #ModSWINode_Link]
+        BNE     loopoverhashchain
+
+        LDMIA   r10, {r10, r12}
+        LDR     r12, [r12, #Module_incarnation_list]  ; preferred life
+        CMP     r12, #0
+
+ [ FixR9CorruptionInExtensionSWI
+        Pull    "r9", NE                ; restore corrupted r9 before calling SWI handler
+                                        ;RCM added 'NE' above to fix MED=04655
+ ]
+
+        ANDNE   r11, r11, #Module_SWIChunkSize-1
+        ADDNE   r12, r12, #Incarnation_Workspace
+        MOVNE   pc, r10
+
+
+VectorUserSWI                   ; Not in a module, so call vec
+ [ FixR9CorruptionInExtensionSWI
+        Pull    "r9"            ; restore corrupted r9 before calling UKSWIV
+ ]
+        MOV     r10, #UKSWIV    ; high SWI number still in R11
+        B       CallVector      ; lr still has user CCs & points at%FT02
+
+
+02
+ [ FixR9CorruptionInExtensionSWI
+        Pull    "lr"                    ; r9 already pulled off stack before calling SWI handler or UKSWIV
+ |
+        Pull    "r9,lr"
+ ]
+        BIC     lr, lr, #&F0000000      ; Can mangle any/all of punter flags
+        MOV     r10, pc, LSR #(32-4)
+        ORR     lr, lr, r10, LSL #(32-4)
+        B       SLVK
+
+; ....................... default owner of UKSWIV .............................
+; Call UKSWI handler
+; Also used to call the upcall handler
+
+; In    r12 = HiServ_ws (or UpCallHan_ws)
+
+CallUpcallHandler
+HighSWI ROUT                          ; no one on vec wants it: give to handler
+
+        Pull    lr                    ; the link pointing at %BT02 to pass in.
+        LDMIA   r12, {r12, pc}
+
+; ........................ default UKSWI handler ..............................
+
+NoSuchSWI ROUT
+
+        Push    lr
+        BL      NoHighSWIHandler
+        Pull    lr
+        B       SLVK_SetV
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+NoHighSWIHandler ROUT
+
+        MOV     r0, #0
+        LDR     r0, [r0, #IRQsema]
+        CMP     r0, #0
+        ADR     r0, ErrorBlock_NoSuchSWI ; Must return static error here
+        ORRNES  pc, lr, #V_bit
+
+; Not in IRQ: can safely build a dynamic error
+
+      [ International
+        Push    "r1-r4, lr"
+        SUB     sp, sp,#12
+        MOV     r1, sp
+        MOV     r2, #12
+        MOV     r0, r11
+        SWI     XOS_ConvertHex6         ; SWI argument is 00xxxxxx
+
+        MOV     r4, r0                  ; now strip leading 0s
+02      LDRB    r2, [r4], #1
+        CMP     r2, #"0"
+        BEQ     %BT02
+
+        SUB     r4,r4,#1
+        ADR     r0, ErrorBlock_NoSuchSWI1
+        BL      TranslateError_UseR4
+        ADD     sp,sp,#12
+
+        Pull    "r1-r4, lr"
+        ORRS    pc, lr, #V_bit
+
+        MakeErrorBlock NoSuchSWI1
+
+      |
+        Push    "r1-r3, lr"
+        LDR     r1, =EnvString
+        LDMIA   r0!, {r2, r3}           ; number, "SWI "
+        STMIA   r1!, {r2, r3}
+        MOV     r2, #"&"
+        STRB    r2, [r1], #1
+        MOV     r3, r0
+        MOV     r0, r11
+        MOV     r2, #256
+        SWI     XOS_ConvertHex6         ; SWI argument is 00xxxxxx
+
+; now strip leading 0s
+
+        MOV     r1, r0
+02      LDRB    r2, [r1], #1
+        CMP     r2, #"0"
+        BEQ     %BT02
+        CMP     r2, #0
+        ADDEQ   r1, r0, #1
+        BEQ     %FT03
+04      STRB    r2, [r0], #1
+        LDRB    r2, [r1], #1
+        CMP     r2, #0
+        BNE     %BT04
+        MOV     r1, r0
+03      MOV     r2, #" "
+01      STRB    r2, [r1], #1
+        CMP     r2, #0
+        LDRNEB  r2, [r3], #1
+        BNE     %BT01
+
+        Pull    "r1-r3, lr"
+        LDR     r0, =EnvString
+        ORRS    pc, lr, #V_bit
+       ]
+
+        MakeErrorBlock NoSuchSWI
+
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Fast SWI handlers for BGet and BPut caches
+
+BGetSWI ROUT                            ; Done separately for highest speed
+
+ [ {FALSE}      ; Kernel now divorced from FileSwitch
+        Push    lr
+        LDR     r12, =VecPtrTab
+        LDR     r10, [r12, r11, LSL #2] ; Load top node pointer for vector
+        CMP     r10, #ROM
+        BCC     BGetThruVector
+
+; Cache technology deemed to be ok (ensures consistent with ROM FileSwitch)
+
+        LDMIB   r10, {wp}                       ; Get wp from vector node
+
+ ASSERT :INDEX: BGet_shiftedbase = 0
+ ASSERT :INDEX: BGet_bufferdata = 4
+ ASSERT :INDEX: BGet_scb        = 8
+ ASSERT :INDEX: BGet_shift      = 12
+ ASSERT :INDEX: BGet_handle     = 16
+        LDMIA   wp, {r0, r10, r11, r12, r14}    ; Get cached data
+        TEQ     r14, r1                         ; BGet on cached handle ?
+        LDREQ   r14, [r11, #:INDEX: scb_fileptr] ; Check fileptr in that buffer
+        TEQEQ   r0, r14, LSR r12
+        LDREQB  r0, [r10, r14]                  ; Get byte from buffer
+        ADDEQ   r14, r14, #1
+        STREQ   r14, [r11, #:INDEX: scb_fileptr] ; Increment fileptr
+        Pull    lr, EQ                          ; Punter lr has VClear
+        BICEQ   lr, lr, #C_bit                  ; CClear -> ~EOF
+        BEQ     SLVK
+
+BGetThruVector
+ |
+        Push    lr
+ ]
+        MOV     r10, #BGetV             ; Cache hit failed, call victor
+        BL      CallVector
+        Pull    lr                      ; Punter lr has VClear
+        BIC     lr, lr, #C_bit          ; Copy C,V to punter lr
+        ORRCS   lr, lr, #C_bit
+        B       SLVK_TestV
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+BPutSWI ROUT                            ; Done separately for highest speed
+
+ [ {FALSE}      ; Kernel now divorced from FileSwitch
+        Push    "r9, lr"                ; Needs one other temp register
+        LDR     r12, =VecPtrTab
+        LDR     r10, [r12, r11, LSL #2] ; Load top node pointer for vector
+        CMP     r10, #ROM
+        BCC     BPutThruVector
+
+; Cache technology deemed to be ok (ensures consistent with ROM FileSwitch)
+
+        LDMIB   r10, {wp}                       ; Get wp from vector node
+                                                ; NB. This is not normal wp
+;       BPut_shiftedbase                        ; r9
+ ASSERT BPut_bufferdata = BPut_shiftedbase + 4  ; r10
+ ASSERT BPut_scb        = BPut_shiftedbase + 8  ; r11
+ ASSERT BPut_shift      = BPut_shiftedbase + 12 ; r12
+ ASSERT BPut_handle     = BPut_shiftedbase + 16 ; r14
+        LDMIA   wp, {r9, r10, r11, r12, r14}    ; Get cached data
+        TEQ     r14, r1                         ; BPut on cached handle ?
+        LDREQ   r14, [r11, #:INDEX: scb_fileptr] ; Check fileptr in that buffer
+        TEQEQ   r9, r14, LSR r12
+        STREQB  r0, [r10, r14]                  ; Put byte to buffer
+        ADDEQ   r14, r14, #1
+        STREQ   r14, [r11, #:INDEX: scb_fileptr] ; Increment fileptr
+        Pull    "r9, lr", EQ                    ; Destack punter r9 and lr(VC)
+        BEQ     SLVK
+
+BPutThruVector
+        MOV     r10, #BPutV                     ; Cache hit failed, call victor
+        BL      CallVector
+        Pull    "r9, lr"                        ; Destack punter r9 and lr(VC)
+        B       SLVK_TestV
+ |
+        Push    "lr"
+        MOV     r10, #BPutV                     ; Cache hit failed, call victor
+        BL      CallVector
+        Pull    "lr"                            ; Destack lr(VC)
+        B       SLVK_TestV
+ ]
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI handlers for all the vectored SWIs that have vecnum=swinum
+
+; All defined to affect C & V at most
+
+FSControlSWI ROUT
+
+        MOV     r11, #FSCV              ; Pretend to be vecnum = swinum swi
+                                        ; and just drop through to ...
+
+VecSwiDespatch ROUT
+
+        Push    lr                      ; this is user's link
+        MOV     r10, r11                ; SWI number from R11->R10
+        ORR     r14, lr, #SVC_mode
+        BIC     r14, r14, #I_bit        ; Enable IRQs
+        TEQP    r14, #0
+        BL      CallVector
+
+; So the vectored routine can update the pushed link CCodes if wanted
+; No update return is therefore LDMIA stack!, {PC}^ (sort of)
+; Update return pulls lr, molests it, then MOVS PC, lr
+; Note either return enables IRQ, FIQ
+
+; ???? Is the DEFAULT owner allowed to corrupt r10,r11 IFF he claims it ????
+
+        Pull    lr                      ; Punter lr has VClear
+        BIC     lr, lr, #C_bit          ; Copy C,V to punter lr
+        ORRCS   lr, lr, #C_bit
+        B       SLVK_TestV
+
+
+NoIrqVecSwiDespatch ROUT
+
+        Push    lr                      ; this is user's link
+        MOV     r10, r11                ; SWI number from R11->R10
+        ORR     r14, lr, #SVC_mode+I_bit ; Disable IRQ
+        TEQP    r14, #0
+        BL      CallVector
+        Pull    lr                      ; Punter lr has VClear
+        BIC     lr, lr, #C_bit          ; Copy C,V to punter lr
+        ORRCS   lr, lr, #C_bit
+        B       SLVK_TestV
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_GetEnv
+
+SWI_GetEnv_Code ROUT
+
+        LDR     r0, =EnvString
+        MOV     r1, #0
+        LDR     r1, [r1, #MemLimit]
+        LDR     r2, =EnvTime
+        B       SLVK
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_Exit
+
+SEXIT ROUT
+
+        BL      OscliTidy               ; shut redirection, restore FS
+
+; now see if it's an abort Exit
+
+        LDR     r12, ABEX
+        CMP     r1, r12
+        TSTEQ   r0, #ARM_CC_Mask
+        MOVNE   r2,  #0
+        MOV     r12, #0
+        STR     r2,  [r12, #ReturnCode]
+        LDR     r12, [r12, #RCLimit]
+        CMP     r2, r12
+        SWIHI   OS_GenerateError        ; really generate an error
+
+        Pull    "r9-r12"                ; junk R9
+        MOV     r0, #0
+        LDR     lr, [r0, #SExitA]
+        STR     lr, [r0, #Curr_Active_Object]
+        LDR     r12, [r0, #SExitA_ws]
+        LDR     sp_svc, =SVCSTK
+        BICS    pc, lr, #ARM_CC_Mask
+
+ABEX    =       "ABEX"                  ; Picked up as word
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_CallBack: Set/read callback buffer and handler
+
+SCALLB  MOV     r10, #CallBackHandler
+
+handlecomm
+        Push    "r2, r3, lr"
+        MOV     r3, r0          ; buffer
+        MOV     r0, r10
+        BL      CallCESWI
+        MOV     r0, r3
+        Pull    "r2, r3, lr"
+        B       SLVK_TestV
+
+; .............................................................................
+; SWI OS_BreakSet: Set/read breakpoint buffer and handler
+
+SBRKCT  MOV     r10, #BreakPointHandler
+        B       handlecomm
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_ReadEscapeState
+
+ReadEscapeSWI ROUT
+
+        MOV     r10, #0
+        LDRB    r10, [r10, #ESC_Status]
+        TST     r10, #1 :SHL: 6
+        BICEQ   lr, lr, #C_bit
+        ORRNE   lr, lr, #C_bit
+        B       SLVK
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_ServiceCall
+
+Issue_Service_SWI ROUT
+
+        Push    lr
+        BL      Issue_Service
+        Pull    lr
+        B       SLVK
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_GenerateError
+
+GenErrorSWI * SLVK_SetV
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        END
diff --git a/s/MEMC1 b/s/MEMC1
new file mode 100644
index 0000000000000000000000000000000000000000..2709f3201a43931d8b487ae4f412577e9e3442ea
--- /dev/null
+++ b/s/MEMC1
@@ -0,0 +1,571 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > MEMC1
+
+; MEMC interface file - MEMC1 version
+
+; Created by TMD 10-Aug-90
+
+VInit   * &03600000
+VStart  * &03620000
+VEnd    * &03640000
+CInit   * &03660000
+; SStart  * &03680000
+; SEnd    * &036A0000
+; SPtr    * &036C0000
+
+; *****************************************************************************
+;
+;       SetDAG - Program DMA address generator R1 with physical address R0
+;
+; in:   r0 = physical address
+;       r1 = index of DMA address generator to program, as defined in vdudecl
+;
+; out:  All registers preserved, operation ignored if illegal
+;
+
+SetDAG  ENTRY   "r0"
+        CMP     r1, #MEMCDAG_MaxReason
+        EXIT    HI
+        ADR     r14, DAGAddressTable
+        LDR     r14, [r14, r1, LSL #2]          ; load base address in MEMC1
+        MOV     r0, r0, LSR #4                  ; bottom 4 bits irrelevant
+        CMP     r0, #(1 :SHL: 15)               ; ensure in range
+        ORRCC   r14, r14, r0, LSL #2
+        STRCC   r14, [r14]                      ; any old data will do
+        EXIT
+
+        GBLA    DAGIndex
+DAGIndex SETA   0
+
+        MACRO
+        DAGTab  $reason, $address
+        ASSERT  ($reason)=DAGIndex
+        &       $address
+DAGIndex SETA   DAGIndex + 1
+        MEND
+
+DAGAddressTable
+        DAGTab  MEMCDAG_VInit, VInit
+        DAGTab  MEMCDAG_VStart, VStart
+        DAGTab  MEMCDAG_VEnd, VEnd
+        DAGTab  MEMCDAG_CInit, CInit
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; CAM manipulation utility routines
+
+BangCamUpdate ROUT
+
+; R2 = CAM entry no
+; R3 = logaddr
+; R9 = current MEMC value
+; R11 = PPL
+; set and update tables
+
+        MOV     R4, #0
+        LDR     R4, [R4, #CamEntriesPointer]
+        ORR     r0, r3, r11, LSL #28  ; top nibble is PPL
+        STR     r0, [R4, R2, LSL #2]
+
+BangCam
+
+; r0 corrupted
+; r1 corrupted
+; R2 = CAM entry no
+; R3 = logaddr
+; r4 corrupted
+; r5 spare!
+; r6 corrupted
+; r7, r8 spare
+; R9 = current MEMC value
+; r10 spare
+; R11 = PPL
+; r12 spare
+
+        AND     R4, R9, #&C           ; pagesize
+        ADR     R0, PageMangleTable
+        LDR     R0, [R0, R4]          ; load data table pointer
+        MOV     R4, #0
+01      LDR     R1, [R0], #4
+        CMP     R1, #-1
+        BEQ     %FT02
+        AND     R6, R2, R1
+        LDR     R1, [R0], #4
+        CMP     R1, #0
+        RSBMI   R1, R1, #0
+        ORRPL   R4, R4, R6, LSL R1
+        ORRMI   R4, R4, R6, LSR R1
+        B       %BT01
+
+02      LDR     R1, [R0], #4
+        CMP     R1, #-1
+        BEQ     %FT03
+        AND     R6, R3, R1
+        LDR     R1, [R0], #4
+        CMP     R1, #0
+        RSBMI   R1, R1, #0
+        ORRPL   R4, R4, R6, LSL R1
+        ORRMI   R4, R4, R6, LSR R1
+        B       %BT02
+
+03      ORR     R4, R4, #CAM
+        ORR     R4, R4, R11, LSL #8     ; stuff in PPL
+        STR     R4, [R4]                ; and write it
+        MOV     PC, LR
+
+; Data to drive CAM setting
+
+PageMangleTable
+        &       PageMangle4K
+        &       PageMangle8K
+        &       PageMangle16K
+        &       PageMangle32K
+
+; For each page size, pairs of masks and shift factors to put the bits in the
+; right place. Two sets: operations on Physical Page Number, operations on
+; Logical Page Number.
+
+; Shifts are Shift Left values (<<). Each section terminated by -1
+
+PageMangle4K
+; PPN:
+        &       2_011111111
+        &       0                       ; bits in right place
+        &       -1
+; LPN:
+        &       2_1100000000000:SHL:12
+        &       (11-12)-12              ; LPN[12:11] -> A[11:10]
+        &       2_0011111111111:SHL:12
+        &       (22-10)-12              ; LPN[10:0 ] -> A[22:12]
+        &      -1
+
+PageMangle8K
+; PPN:
+        &       2_010000000
+        &       7-7                     ; PPN[7]   -> A[7]
+        &       2_001000000
+        &       0-6                     ; PPN[6]   -> A[0]
+        &       2_000111111
+        &       6-5                     ; PPN[5:0] -> A[6:1]
+        &       -1
+; LPN:
+        &       2_110000000000:SHL:13
+        &       (11-11)-13              ; LPN[11:10] -> A[11:10]
+        &       2_001111111111:SHL:13
+        &       (22-9)-13               ; LPN[9:0]   -> A[22:13]
+        &       -1
+
+PageMangle16K
+; PPN:
+        &       2_010000000
+        &       7-7                     ; PPN[7]   -> A[7]
+        &       2_001100000
+        &       1-6                     ; PPN[6:5] -> A[1:0]
+        &       2_000011111
+        &       6-4                     ; PPN[4:0] -> A[6:2]
+        &       -1
+; LPN:
+        &       2_11000000000:SHL:14
+        &       (11-10)-14              ; LPN[10:9] -> A[11:10]
+        &       2_00111111111:SHL:14
+        &       (22-8)-14               ; LPN[8:0]  -> A[22:14]
+        &       -1
+
+PageMangle32K
+; PPN:
+        &       2_100000000
+        &       12-8                    ; PPN[8] -> A[12]
+        &       2_010000000
+        &       7-7                     ; PPN[7] -> A[7]
+        &       2_001000000
+        &       1-6                     ; PPN[6] -> A[1]
+        &       2_000100000
+        &       2-5                     ; PPN[5] -> A[2]
+        &       2_000010000
+        &       0-4                     ; PPN[4] -> A[0]
+        &       2_000001111
+        &       6-3                     ; PPN[3:0] -> A[6:3]
+        &       -1
+; LPN:
+        &       2_1100000000:SHL:15
+        &       (11-9)-15               ; LPN[9:8] -> A[11:10]
+        &       2_0011111111:SHL:15
+        &       (22-7)-15               ; LPN[7:0] -> A[22:15]
+        &       -1
+
+PageSizes
+        &       4*1024                  ; 0 is 4K
+        &       8*1024                  ; 4 is 8K
+        &       16*1024                 ; 8 is 16
+        &       32*1024                 ; C is 32
+
+PageShifts
+        =       12, 13, 0, 14           ; 1 2 3 4
+        =       0,  0,  0, 15           ; 5 6 7 8
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_UpdateMEMC: Read/write MEMC1 control register
+
+SSETMEMC ROUT
+
+        AND     r10, r0, r1
+        MOV     r12, #0
+        TEQP    pc, #SVC_mode+I_bit+F_bit
+        LDR     r0, [r12, #MEMC_CR_SoftCopy] ; return old value
+        BIC     r11, r0, r1
+        ORR     r11, r11, R10
+        BIC     r11, r11, #&FF000000
+        BIC     r11, r11, #&00F00000
+        ORR     r11, r11, #MEMCADR
+        STR     r11, [r12, #MEMC_CR_SoftCopy]
+        STR     r11, [r11]
+        TEQP    pc, #SVC_mode+I_bit
+        ExitSWIHandler
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       ClearPhysRAM - Routine to clear "all" memory
+;
+; While this routine is running, keyboard IRQs may happen. For this reason
+; it avoids LogRAM 0..31 (where hardware IRQ vector is) and PhysRAM
+; 0..31 where the IRQ workspace is.
+;
+; r7 contains memory speed and must be preserved
+; r8 contains page size and must be preserved
+; r9 contains MEMC control register and must be preserved
+;
+
+ClearPhysRAM ROUT
+        MOV     r0, #0
+        MOV     r1, #0
+        MOV     r2, #0
+        MOV     r3, #0
+        MOV     r4, #0
+        MOV     r5, #0
+        MOV     r6, #0
+        MOV     r11, #0
+        MOV     r12, #PhysRam
+        CMP     r13, #512*1024
+        ADDEQ   r10, r12, #(512-64)*1024 ; get address that's logram 0
+        ADDNE   r10, r12, #512*1024
+        ADD     r13, r13, #PhysRam      ; end of memory
+        ADD     r12, r12, #4*8          ; skip minimal startup workspace
+10      CMP     r12, R10
+        ADDEQ   r12, r12, #4*8          ; skip physram that's logram 0
+        STMNEIA r12!, {r0-r6, r11}
+        CMP     r12, r13
+        BNE     %BT10
+        SUB     r13, r13, #PhysRam
+
+        LDR     r0, =OsbyteVars + :INDEX: LastBREAK
+        MOV     r1, #&80
+        STRB    r1, [r0]                ; flag the fact that RAM cleared
+        MOV     pc, lr
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       InitMEMC - Initialise memory controller
+;
+
+InitMEMC ROUT
+        LDR     R0, ResetMemC_Value
+        STR     R0, [R0]     ; set ROM access times, refresh on flyback, no DMA
+        MOV     pc, lr
+
+; -> MemSize
+
+; (non-destructive) algorithm to determine MEMC RAM configuration
+;
+; Dave Flynn and Alasdair Thomas
+; 17-March-87
+;
+; Spooling checkered by NRaine and SSwales !
+; 8MByte check bodged in by APT
+;
+; NOTE: Routines MemSize and TimeCPU are called by the power-on test software,
+; so their specifications MUST not change.
+;
+; Set MEMC for 32-k page then analyse signature of possible
+; external RAM configurations...
+; The configurations are:
+;
+; Ram Size    Page Size    Configuration    (Phys RAM) Signature
+;--------------------------------------------------------------------
+;  16MByte      32k        4*32*1Mx1         A13,A20,A21,A22,A23,A23.5 distinct
+;  16MByte      32k        16*8*256kx4       A13,A20,A21,A22,A23,A23.5 distinct
+;
+;  12MByte      32k        3*32*1Mx1         A13,A20,A21,A22,A23 OK, A23.5 fail
+;  12MByte      32k        12*8*256kx4       A13,A20,A21,A22,A23 OK, A23.5 fail
+;
+;   8MByte      32k        2*32*1Mx1         A13,A20,A21,A22 distinct, A23 fail
+;   8MByte      32k         8*8*256kx4       A13,A20,A21,A22 distinct, A23 fail
+;
+;   4Mbyte      32k          32*1Mx1         A13,A21,A20 distinct, A22,A23 fail
+;   4Mbyte      32k         4*8*256kx4       A13,A21,A20 distinct, A22,A23 fail
+;
+;   2Mbyte      32k    expandable 2*8*256kx4 A13,A20 distinct, A21 fails
+;   2Mbyte ???  16k      fixed 2*8*256kx4    A13,A21 distinct, A20 fails
+;
+;   1Mbyte       8k          32*256kx1       A13,A20 fail, A19,A18,A12 distinct
+;   1Mbyte       8k           8*256kx1       A13,A20 fail, A19,A18,A12 distinct
+;   1Mbyte       8k          4*8*64kx4       A13,A20 fail, A19,A18,A12 distinct
+;
+; 512Kbyte       8k    expandable 2*8*64kx4  A13,A20,A19 fail, A12,A18 distinct
+; 512Kbyte       4k      fixed 2*8*64kx4     A13,A20,A12 fail, A19,A18 distinct
+;
+; 256Kbyte       4K           8*64kx4        A13,A20,A12,A18 fail, A21,A19 ok
+; 256Kbyte       4K          32*64kx1        A13,A20,A12,A18 fail, A21,A19 ok
+;
+
+Z_Flag     * &40000000
+
+; MemSize routine... enter with 32K pagesize set
+; R0 returns page size
+; R1 returns memory size
+; R2 returns value set in MEMC
+; uses R3-R7
+
+MemSize ROUT
+        MOV     r7, lr
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #A13
+        BL      DistinctAddresses
+        BNE     %10
+        ADD     r1, r0, #A21
+        BL      DistinctAddresses
+        MOVNE   r0, #Page32K
+        MOVNE   r1, #2048*1024
+        BNE     MemSizeDone
+
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #4*1024*1024
+        BL      DistinctAddresses
+        MOVNE   r0, #Page32K
+        MOVNE   r1, #4*1024*1024
+        BNE     MemSizeDone
+
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #8*1024*1024
+        BL      DistinctAddresses
+        MOVNE   r0, #Page32K
+        MOVNE   r1, #8*1024*1024
+        BNE     MemSizeDone
+
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #12*1024*1024
+        BL      DistinctAddresses
+        MOV     r0, #Page32K
+        MOVNE   r1, #12*1024*1024
+        MOVEQ   r1, #16*1024*1024
+        B       MemSizeDone
+
+10      ADD     r1, r0, #A20
+        BL      DistinctAddresses
+        BNE     %20
+        MOV     r0, #Page16K
+        MOV     r1, #2048*1024
+        B       MemSizeDone
+
+20      ADD     r1, r0, #A19
+        BL      DistinctAddresses
+        BEQ     %30
+        MOV     r0, #Page8K
+        MOV     r1, #512*1024
+        B       MemSizeDone
+
+30      ADD     r1, r0, #A18
+        BL      DistinctAddresses
+        BEQ     %40
+        MOV     r0, #Page4K
+        MOV     r1, #256*1024
+        B       MemSizeDone
+
+40      ADD     r1, r0, #A12
+        BL      DistinctAddresses
+        BEQ     %50
+        MOV     r0, #Page4K
+        MOV     r1, #512*1024
+        B       MemSizeDone
+
+50      MOV     r0, #Page8K
+        MOV     r1, #1024*1024
+
+MemSizeDone
+        LDR     r2, ResetMemC_Value
+        BIC     r2, r2, #&C
+        ORR     r2, r2, r0
+        STR     r2, [r2]                        ; set MEMC to right state
+        ADR     r3, PageSizes
+        LDR     r0, [r3, r0]                    ; r0 = convert from page size indicator into actual page size (in bytes)
+        MOV     pc, r7
+
+
+; DistinctAddresses routine...
+; r0,r1 are the addresses to check
+; uses r2-5
+; writes interleaved patterns (to prevent dynamic storage...)
+; checks writing every bit low and high...
+; return Z-flag set if distinct
+
+DistinctAddresses ROUT
+        LDR     r2, [r0] ; preserve
+        LDR     r3, [r1]
+        LDR     r4, Pattern
+        STR     r4, [r0] ; mark first
+        MOV     r5, r4, ROR #16
+        STR     r5, [r1] ; mark second
+        LDR     r5, [r0]
+        CMP     r5, r4 ; check first
+        BNE     %10    ; exit with Z clear
+        LDR     r5, [r1] ; check second
+        CMP     r5, r4, ROR #16 ; clear Z if not same
+        BNE     %10
+; now check inverse bit writes
+        STR     r4, [r1] ; mark second
+        MOV     r5, r4, ROR #16
+        STR     r5, [r0] ; mark first
+        LDR     r5, [r1]
+        CMP     r5, r4 ; check second
+        BNE     %10   ; exit with Z clear
+        LDR     r5, [r0] ; check first
+        CMP     r5, r4, ROR #16 ; clear Z if not same
+10      STR     r3, [r1] ; restore
+        STR     r2, [r0]
+        ORREQ   lr, lr, #Z_Flag
+        BICNE   lr, lr, #Z_Flag
+        MOVS    pc, lr
+
+Pattern
+        &       &AAFF5500 ; shiftable bit check pattern
+
+; init state with masked out page size
+
+ResetMemC_Value
+        & &E010C :OR: MEMCADR       ; slugged ROMs + flyback refresh only + 32K page
+
+; Constants
+;
+A21 * 1:SHL:21
+A20 * 1:SHL:20
+A19 * 1:SHL:19
+A18 * 1:SHL:18
+A13 * 1:SHL:13
+A12 * 1:SHL:12
+
+Page32K * &C ; in MEMC control reg patterns...
+Page16K * &8
+Page8K  * &4
+Page4K  * &0
+
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r0-r6 trashable
+;       r9 = Current MEMC CR
+
+; Out   r9 MEMC value with slowest ROM speed, correct pagesize
+;       r7 processor speed in kHz, tbs -> MEMC1a
+
+ncpuloops * 1024 ; don't go longer than 4ms without refresh !
+nmulloops * 128
+
+TimeCPU ROUT
+
+        BIC     r9, r9, #3 :SHL: 8
+        STR     r9, [r9]                ; turn off refresh for a bit
+
+; Time CPU/Memory speed
+
+        LDR     r1, =&7FFE              ; 32K @ 2MHz = ~16ms limit
+        MOV     r3, #IOC
+
+        MOV     r0, r1, LSR #8
+        STRB    r1, [r3, #Timer1LL]
+        STRB    r0, [r3, #Timer1LH]
+        LDR     r0, =ncpuloops
+        STRB    r0, [r3, #Timer1GO]     ; start the timer NOW
+        B       %FT10                   ; Looks superfluous, but is required
+                                        ; to get ncpuloops pipeline breaks
+
+10      SUBS    r0, r0, #1              ; 1S
+        BNE     %BT10                   ; 1N + 2S
+
+        STRB    r0, [r3, #Timer1LR]     ; latch count NOW
+        LDRB    r2, [r3, #Timer1CL]
+        LDRB    r0, [r3, #Timer1CH]
+        ADD     r2, r2, r0, LSL #8      ; count after looping is ...
+
+        SUB     r2, r1, r2              ; decrements !
+        MOV     r2, r2, LSR #1          ; IOC clock decrements at 2MHz
+
+; Time CPU/MEMC Multiply time
+
+        MOV     r4, #-1                 ; Gives worst case MUL
+
+        MOV     r0, r1, LSR #8
+        STRB    r1, [r3, #Timer1LL]
+        STRB    r0, [r3, #Timer1LH]
+        LDR     r0, =nmulloops
+        STRB    r0, [r3, #Timer1GO]     ; start the timer NOW
+        B       %FT20                   ; Looks superfluous, but is required
+                                        ; to get nmulloops pipeline breaks
+
+20      MUL     r5, r4, r4              ; 1S + 16I
+        MUL     r5, r4, r4              ; 1S + 16I
+        SUBS    r0, r0, #1              ; 1S
+        BNE     %BT20                   ; 1N + 2S
+
+        STRB    r0, [r3, #Timer1LR]     ; latch count NOW
+        LDRB    r4, [r3, #Timer1CL]
+        LDRB    r0, [r3, #Timer1CH]
+        ADD     r4, r4, r0, LSL #8      ; count after looping is ...
+
+        SUB     r4, r1, r4              ; decrements !
+        MOV     r4, r4, LSR #1          ; IOC clock decrements at 2MHz
+
+        ORR     r9, r9, #1 :SHL: 8      ; set refresh on flyback
+        STR     r9, [r9]                ; restore MEMC state a.s.a.p.
+
+; In ROM - each cpu loop took 4R cycles @ 8/f*500ns/cycle
+
+        LDR     r0, =4*(8*500/1000)*ncpuloops*1000
+        DivRem  r7, r0, r2, r1          ; r2 preserved
+        MOV     r0, #&80                ; At 8 MHz and below, run fast ROMs
+        LDR     r1, =8050               ; Over 8 MHz, need medium ROMs
+        CMP     r7, r1
+        MOVHI   r0, #&40
+        LDR     r1, =13000              ; Over 13 MHz, need slowest ROMs
+        CMP     r7, r1
+        MOVHI   r0, #&00
+        ORR     r9, r9, r0
+        STR     r9, [r9]                ; Set ROM speed appropriately
+
+ ASSERT ncpuloops = 8*nmulloops ; for given ratio cutoff <------------
+
+        MOV     r4, r4, LSL #10         ; *1024 to get resolution on divide
+        DivRem  r0, r4, r2, r1
+        LDR     r1, =1100               ; Cutoff point; MEMC1 longer than this
+        CMP     r0, r1
+        ORRLO   r7, r7, #1 :SHL: 16     ; Note MEMC1a prescence
+
+        MOV     pc, lr
+
+; Typical figures give (in ROM at 8MHz):
+
+; MEMC1  2048 CPU, 2432 MEMC -> MUL ratio 1216
+; MEMC1a 2048       864                    432
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        END
diff --git a/s/MEMC2 b/s/MEMC2
new file mode 100644
index 0000000000000000000000000000000000000000..ea1afe572048e1e368a32bc56402e747d278bcd9
--- /dev/null
+++ b/s/MEMC2
@@ -0,0 +1,713 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > MEMC2
+
+; MEMC interface file - MEMC2 version
+
+; Created by TMD 10-Aug-90
+
+PhysRAML2PT * &02000000 + (512+64)*1024
+
+; Synonyms
+
+VInit                   *       MEMC2Address + MEMC2_VINITe
+VStart                  *       MEMC2Address + MEMC2_VSTRTe
+VEnd                    *       MEMC2Address + MEMC2_VENDe
+CInit                   *       MEMC2Address + MEMC2_CINIT
+
+; *****************************************************************************
+;
+;       SetDAG - Program DMA address generator R1 with physical address R0
+;
+; in:   r0 = physical address
+;       r1 = index of DMA address generator to program, as defined in vdudecl
+;
+; out:  All registers preserved, operation ignored if illegal
+;
+
+SetDAG  ENTRY   "r0,r1"
+        CMP     r1, #MEMCDAG_MaxReason
+        EXIT    HI
+        ADR     r14, DAGAddressTable
+        LDR     r14, [r14, r1, LSL #2]          ; load base address in MEMC2
+        MOV     r1, r0, LSR #16                 ; r1 is top 16 bits
+        EOR     r0, r0, r1, LSL #16             ; and r0 is bottom 16 bits
+        BIC     r0, r0, #&0F                    ; bits 0..3 must be clear
+        BIC     r1, r1, #&F000                  ; and bits 28..31 must be clear
+        STMIA   r14, {r0, r1}                   ; atomic update (we believe)
+        EXIT
+
+        GBLA    DAGIndex
+DAGIndex SETA   0
+
+        MACRO
+        DAGTab  $reason, $address
+        ASSERT  ($reason)=DAGIndex
+        &       $address
+DAGIndex SETA   DAGIndex + 1
+        MEND
+
+DAGAddressTable
+        DAGTab  MEMCDAG_VInit, VInit
+        DAGTab  MEMCDAG_VStart, VStart
+        DAGTab  MEMCDAG_VEnd, VEnd
+        DAGTab  MEMCDAG_CInit, CInit
+
+; **************** CAM manipulation utility routines ***********************************
+
+; **************************************************************************************
+;
+;       BangCamUpdate - Update CAM entry and soft copy
+;
+; This part of the routine has to do more work on MEMC2
+;
+; First look in the CamEntries table to find the logical address L this physical page is
+; currently allocated to. Then check in the Level 2 page tables to see if page L is currently
+; at page R2. If it is, then map page L to be inaccessible, otherwise leave page L alone.
+; Then map logical page R3 to physical page R2.
+;
+; in:   r2 = physical page number
+;       r3 = logical address
+;       r9 = current MEMC1 control register (irrelevant on MEMC2)
+;       r11 = PPL
+;
+; out:  r0, r1, r4, r6 corrupted
+;       r2, r3, r5, r7-r12 preserved
+;
+; NB Use of stack is allowed in this routine
+
+BangCamUpdate ROUT
+        MOV     r1, #0
+        LDR     r1, [r1, #CamEntriesPointer]
+        LDR     r0, [r1, r2, LSL #2]            ; r0 = current logaddress + PPL for phys page r2
+        ORR     r4, r3, r11, LSL #28            ; new entry for CamEntries
+        STR     r4, [r1, r2, LSL #2]            ; update
+
+        BIC     r0, r0, #&F0000000              ; just get logical address
+        LDR     r1, =PhysRAML2PT                ; point to page tables
+        LDR     r4, [r1, r0, LSR #11]           ; get physical page + PPL for this logical page
+        TEQ     r2, r4, LSR #3                  ; see if still there
+        BNE     %FT10                           ; if not there, then just put in new page
+
+        Push    "r3, r14"
+        MOV     r3, r0                          ; map out old page at this logical address
+        MOV     r0, #0                          ; physical page 0 but PPL(MEMC2)=0 ie no access, not even for me!
+        BL      BangL2PT                        ; map page out
+        Pull    "r3, r14"
+10
+
+;       and drop thru to ...
+
+; **************************************************************************************
+;
+;       BangCam - Update CAM entry, but not soft copy
+;
+; This routine maps a physical page to a given logical address
+; For MEMC2, I assume that the physical page was previously not mapped
+; anywhere else - on MEMC1 it would automatically unmap any logical
+; address that the physical page was previously at, but on MEMC2 it won't
+;
+; in:   r2 = physical page number
+;       r3 = logical address
+;       r9 = current MEMC1 control register (irrelevant on MEMC2)
+;       r11 = PPL
+;
+; out:  r0, r1, r4, r6 corrupted
+;       r2, r3, r5, r7-r12 preserved
+;
+; NB Can't use stack - there might not be one!
+
+BangCam
+        ADR     r0, PPLTrans            ; translate MEMC1 PPL to MEMC2 PPL
+        LDRB    r0, [r0, r11]
+        ORR     r0, r0, r2, LSL #3      ; value to store in level 2 page table
+                                        ; is PPL :OR: (phys page number << 3)
+
+        LDR     r1, =PhysRAML2PT        ; point to level 2 page tables
+
+BangL2PT                                ; internal entry point used only by BangCamUpdate
+        BICS    r4, r3, #(3 :SHL: 11)   ; ensure going to be on word boundary (EQ => logical page zero)
+        STR     r0, [r1, r4, LSR #11]   ; update level 2 page table
+
+        MOV     r6, #MEMC2Address
+        STREQ   r0, [r6, #MEMC2_SuperPageZero] ; if logical page 0 then update special entry
+
+        MOV     r0, #0                  ; now flush the TLB
+        STR     r0, [r6, #MEMC2_Flush]
+
+        MOV     pc, lr
+
+PPLTrans
+        =       6                       ; R any W any
+        =       3                       ; R any W sup
+        =       2                       ; R sup W sup
+        =       2                       ; R sup W sup
+
+PageSizes
+        &       4*1024                  ; 0 is 4K
+        &       8*1024                  ; 4 is 8K
+        &       16*1024                 ; 8 is 16
+        &       32*1024                 ; C is 32
+
+PageShifts
+        =       12, 13, 0, 14           ; 1 2 3 4
+        =       0,  0,  0, 15           ; 5 6 7 8
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_UpdateMEMC: Read/write MEMC1 control register
+
+SSETMEMC ROUT
+
+        AND     r10, r0, r1
+        MOV     r12, #0
+        TEQP    pc, #SVC_mode+I_bit+F_bit
+        LDR     r0, [r12, #MEMC_CR_SoftCopy] ; return old value
+        BIC     r11, r0, r1
+        ORR     r11, r11, R10
+        BIC     r11, r11, #&FF000000
+        BIC     r11, r11, #&00F00000
+        ORR     r11, r11, #MEMCADR
+        STR     r11, [r12, #MEMC_CR_SoftCopy]
+
+; We now have to mimic the relevant bits of the MEMC1 control register
+;
+; bits 0,1 => unused
+; bits 2,3 => page size, irrelevant since always 8K
+; bits 4,5 => low ROM access time, irrelevant since this has to be fixed
+; bits 6,7 => hi  ROM access time, -----------------""------------------
+; bits 8,9 => DRAM refresh control, irrelevant (refresh must always be on)
+; bit 10   => Video/cursor DMA enable, corresponds to bit venbe of VATT
+;              (and possibly bit venbo of IATT for interlaced displays)
+;              Unfortunately VATT (and IATT) is a write-only register. Later on
+;              we might have a soft copy of these, but for now just write whole
+;              register.
+; bit 11   => Sound DMA enable, ignore for now
+; bit 12   => OS mode, ignore
+
+; Program all of VATT
+;
+; vdis = 0 (don't disable DMA after one buffer)
+; venbe = (bit 10 of r11)
+; vrnw = 1 (read from RAM, not write)
+; vmske = 0 (no interrupts at end of buffer)
+
+        MOV     r12, # (0 * VATT_vdis) + (0 * VATT_venbe) + (1 * VATT_vrnw) + (0 * VATT_vmske)
+        TST     r11, # (1 :SHL: 10)
+        ORRNE   r12, r12, # VATT_venbe
+        MOV     r10, #MEMC2Address
+        STR     r12, [r10, #MEMC2_VATT]
+
+        TEQP    pc, #SVC_mode+I_bit
+        ExitSWIHandler
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       ClearPhysRAM - Routine to clear "all" memory
+;
+; While this routine is running, keyboard IRQs may happen. For this reason
+; it avoids LogRAM 0..31 (where hardware IRQ vector is) and PhysRAM
+; 0..31 where the IRQ workspace is.
+;
+; On MEMC2 it also has to avoid the pages where the level 2 page tables are
+;
+; r7 contains memory speed and must be preserved
+; r8 contains page size and must be preserved
+; r9 contains MEMC control register and must be preserved
+;
+
+ClearPhysRAM ROUT
+        MOV     r0, #0
+        MOV     r1, #0
+        MOV     r2, #0
+        MOV     r3, #0
+        MOV     r11, #0
+        MOV     r4, #PhysRam
+        CMP     r13, #512*1024
+        ADDEQ   r10, r4, #(512-64)*1024 ; get address that's logram 0
+        ADDNE   r10, r4, #512*1024
+        ADD     r13, r13, #PhysRam      ; end of memory
+        ADD     r12, r4, #PhysRAML2PT-PhysRam
+        ADD     r4, r4, #4*8            ; skip minimal startup workspace
+10
+        CMP     r4, r10
+        ADDEQ   r4, r4, #4*8            ; skip physram that's logram 0
+        CMP     r4, r12
+        ADDEQ   r4, r4, #32*1024        ; skip 32K of L2PT
+        STMNEIA r4!, {r0-r3}
+        CMP     r4, r13
+        BNE     %BT10
+        SUB     r13, r13, #PhysRam
+
+        LDR     r0, =OsbyteVars + :INDEX: LastBREAK
+        MOV     r1, #&80
+        STRB    r1, [r0]                ; flag the fact that RAM cleared
+        MOV     pc, lr
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       InitMEMC - Initialise memory controller
+;
+
+InitMEMC ROUT
+        MOV     r0, # MEMC2Address
+        MOV     r1, # Control_romfast
+        STR     r1, [r0, #MEMC2_Control]        ; make ROMS go fast
+
+        ADR     r1, ClockTimingTable            ; initialise CLK block
+        LDMIA   r1, {r2-r5}
+        ADD     r1, r0, #MEMC2_clkj0
+        STMIA   r1, {r2-r5}
+
+        ADR     r1, DRAMTimingTable             ; initialise DRAM block
+        LDMIA   r1, {r2-r8}
+        ADD     r1, r0, #MEMC2_rwt0
+        STMIA   r1, {r2-r8}
+
+        LDR     r1, rtype_value
+        STR     r1, [r0, #MEMC2_rtype]
+
+        LDR     r1, refr_value
+        STR     r1, [r0, #MEMC2_refr]
+
+        MOV     r1, # (PhysRAML2PT-PhysRam)/(8*1024)            ; level 1 entry (paged) for 0..16M
+        MOV     r2, # (PhysRAML2PT+8*1024-PhysRam)/(8*1024)     ; level 1 entry (paged) for 16M..32M
+        MOV     r3, # Prot_UrwSRW+(L1D_RAM :SHL: 3)+(L1D_RAM :SHL: 5)+(L1D_RAM :SHL: 7)+(L1D_RAM :SHL: 9)+(0 :SHL: 11)
+                                                                ; level 1 entry (direct) for 32M..48M (all RAM, base 0)
+        LDR     r4, = Prot_URwSRW+(L1D_IO :SHL: 3)+(L1D_PROG :SHL: 5)+(L1D_ROM :SHL: 7)+(L1D_ROM :SHL: 9)+(0 :SHL: 11)
+                                                                ; level 1 entry (direct) for 48M..64M (IO,PROG,ROM,ROM)
+        ADD     r5, r0, #MEMC2_Level1+L1_Paged+L1_Sec0
+        ADD     r6, r5, #4*2*16                 ; cycle thru all 4 bus masters and (USR,SPV)
+        ADD     r7, r5, #(L1_Direct+L1_Sec2)-(L1_Paged+L1_Sec0)
+10
+        STMIA   r5, {r1,r2}                     ; set up the paged sections 0,1
+        STMIA   r7, {r3,r4}                     ; set up the direct sections 2,3
+        ADD     r5, r5, #16
+        ADD     r7, r7, #16
+        TEQ     r5, r6                          ; have we got to the end ?
+        BNE     %BT10
+
+; Now turn on the translation, but don't set up the level 2 page tables until later,
+; when we know the DRAM multiplex option
+
+        LDR     r1, = Control_ton + Control_l1on + Control_romfast
+        STR     r1, [r0, #MEMC2_Control]
+
+; now set up VINC
+
+        MOV     r1, #&10                ; low bits
+        MOV     r2, #&00                ; high bits
+        ADD     r3, r0, #MEMC2_VINC
+        STMIA   r3, {r1,r2}
+
+        MOV     pc, lr
+
+ClockTimingTable
+J0      &       &F200   ; 3   / 2
+J1      &       &F200   ; 3   / 2
+RSPEED  &       &1C00   ; 3   / 3
+ISPEED  &       &B800   ; 2.5 / 2.5
+
+DRAMTimingTable
+RWT0    &       &2A320
+RRD0    &       &2AC80
+RSQ0    &       &0B200
+B0Dummy &       0
+RWT1    &       &2A320
+RRD1    &       &2AC80
+RSQ1    &       &0B200
+
+rtype_value     &       &3333   ; Bank Type [x,bank,s1,s0] * 4 ; set largest memory type by default
+
+refr_value      &       &1086   ; Enable refresh, refresh length = 10 hclk ticks, refresh period = 12us
+
+; -> MemSize
+
+; (non-destructive) algorithm to determine MEMC RAM configuration
+;
+; Dave Flynn and Alasdair Thomas
+; 17-March-87
+;
+; Spooling checkered by NRaine and SSwales !
+; 8MByte check bodged in by APT
+;
+; NOTE: Routines MemSize and TimeCPU are called by the power-on test software,
+; so their specifications MUST not change.
+;
+; Set MEMC for 32-k page then analyse signature of possible
+; external RAM configurations...
+; The configurations are:
+;
+; Ram Size    Page Size    Configuration    (Phys RAM) Signature
+;--------------------------------------------------------------------
+;  16MByte      32k        4*32*1Mx1         A13,A20,A21,A22,A23,A23.5 distinct
+;  16MByte      32k        16*8*256kx4       A13,A20,A21,A22,A23,A23.5 distinct
+;
+;  12MByte      32k        3*32*1Mx1         A13,A20,A21,A22,A23 OK, A23.5 fail
+;  12MByte      32k        12*8*256kx4       A13,A20,A21,A22,A23 OK, A23.5 fail
+;
+;   8MByte      32k        2*32*1Mx1         A13,A20,A21,A22 distinct, A23 fail
+;   8MByte      32k         8*8*256kx4       A13,A20,A21,A22 distinct, A23 fail
+;
+;   4Mbyte      32k          32*1Mx1         A13,A21,A20 distinct, A22,A23 fail
+;   4Mbyte      32k         4*8*256kx4       A13,A21,A20 distinct, A22,A23 fail
+;
+;   2Mbyte      32k    expandable 2*8*256kx4 A13,A20 distinct, A21 fails
+;   2Mbyte ???  16k      fixed 2*8*256kx4    A13,A21 distinct, A20 fails
+;
+;   1Mbyte       8k          32*256kx1       A13,A20 fail, A19,A18,A12 distinct
+;   1Mbyte       8k           8*256kx1       A13,A20 fail, A19,A18,A12 distinct
+;   1Mbyte       8k          4*8*64kx4       A13,A20 fail, A19,A18,A12 distinct
+;
+; 512Kbyte       8k    expandable 2*8*64kx4  A13,A20,A19 fail, A12,A18 distinct
+; 512Kbyte       4k      fixed 2*8*64kx4     A13,A20,A12 fail, A19,A18 distinct
+;
+; 256Kbyte       4K           8*64kx4        A13,A20,A12,A18 fail, A21,A19 ok
+; 256Kbyte       4K          32*64kx1        A13,A20,A12,A18 fail, A21,A19 ok
+;
+
+Z_Flag     * &40000000
+
+; MemSize routine... enter with 32K pagesize set
+; R0 returns page size
+; R1 returns memory size
+; R2 returns value set in MEMC
+; uses R3-R7
+
+MemSize ROUT
+ [ {TRUE}                               ; now work on different configurations, but only bank zero
+        MOV     r7, lr
+
+; first find out appropriate rtype value
+; initial routine has set DRAM type to 3
+
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #A9             ; if A9 ghosts
+        BL      DistinctAddresses
+        MOVNE   r0, #2_00               ; then type 00
+        BNE     %FT10
+
+        ADD     r1, r0, #A11            ; else if A11 ghosts
+        BL      DistinctAddresses
+        MOVNE   r0, #2_01               ; then type 01
+        BNE     %FT10
+
+        ADD     r1, r0, #A12            ; else if A12 ghosts
+        BL      DistinctAddresses
+        MOVNE   r0, #2_01               ; then type 01
+        MOVEQ   r0, #2_11               ; else type 11
+10
+        LDR     r1, rtype_value
+        BIC     r1, r1, #2_11
+        ORR     r1, r1, r0
+        MOV     r0, #MEMC2Address
+        STR     r1, [r0, #MEMC2_rtype]
+
+; having set up the DRAM multiplexing correctly, we can now zap the L2PT
+; to no access for any page
+
+        LDR     r1, =PhysRAML2PT
+        ADD     r2, r1, #2*8*1024               ; two L2PT tables at the moment
+        MOV     r3, #0                          ; page 0, no access
+        MOV     r4, #0
+        MOV     r5, #0
+        MOV     r6, #0
+15
+        STMIA   r1!,{r3-r6}
+        TEQ     r1, r2
+        BNE     %BT15
+
+        STR     r3, [r0, #MEMC2_SuperPageZero]  ; don't forget super page zero
+
+; now find out the memory size
+
+        MOV     r0, #PhysRam
+        MOV     r6, #256*1024
+20
+        ADD     r1, r0, r6
+        BL      DistinctAddresses       ; try next address line
+        BNE     %FT30                   ; if ghosts or not there then finish
+        MOV     r6, r6, LSL #1
+        CMP     r6, #16*1024*1024       ; give up if we've got 16MBytes or more
+        BCC     %BT20
+30
+        MOV     r1, r6
+        LDR     r2, ResetMemC_Value
+        BIC     r2, r2, #&C
+        ORR     r2, r2, #Page8K
+        MOV     r0, #8*1024             ; fixed 8K page size
+        MOV     pc, r7
+ |
+        MOV     r7, lr
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #A13
+        BL      DistinctAddresses
+        BNE     %10
+        ADD     r1, r0, #A21
+        BL      DistinctAddresses
+        MOVNE   r0, #Page32K
+        MOVNE   r1, #2048*1024
+        BNE     MemSizeDone
+
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #4*1024*1024
+        BL      DistinctAddresses
+        MOVNE   r0, #Page32K
+        MOVNE   r1, #4*1024*1024
+        BNE     MemSizeDone
+
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #8*1024*1024
+        BL      DistinctAddresses
+        MOVNE   r0, #Page32K
+        MOVNE   r1, #8*1024*1024
+        BNE     MemSizeDone
+
+        MOV     r0, #PhysRam
+        ADD     r1, r0, #12*1024*1024
+        BL      DistinctAddresses
+        MOV     r0, #Page32K
+        MOVNE   r1, #12*1024*1024
+        MOVEQ   r1, #16*1024*1024
+        B       MemSizeDone
+
+10      ADD     r1, r0, #A20
+        BL      DistinctAddresses
+        BNE     %20
+        MOV     r0, #Page16K
+        MOV     r1, #2048*1024
+        B       MemSizeDone
+
+20      ADD     r1, r0, #A19
+        BL      DistinctAddresses
+        BEQ     %30
+        MOV     r0, #Page8K
+        MOV     r1, #512*1024
+        B       MemSizeDone
+
+30      ADD     r1, r0, #A18
+        BL      DistinctAddresses
+        BEQ     %40
+        MOV     r0, #Page4K
+        MOV     r1, #256*1024
+        B       MemSizeDone
+
+40      ADD     r1, r0, #A12
+        BL      DistinctAddresses
+        BEQ     %50
+        MOV     r0, #Page4K
+        MOV     r1, #512*1024
+        B       MemSizeDone
+
+50      MOV     r0, #Page8K
+        MOV     r1, #1024*1024
+
+MemSizeDone
+        LDR     r2, ResetMemC_Value
+        BIC     r2, r2, #&C
+        ORR     r2, r2, r0
+        STR     r2, [r2]                        ; set MEMC to right state
+        MOV     pc, r7
+
+ ]
+
+; DistinctAddresses routine...
+; r0,r1 are the addresses to check
+; uses r2-5
+; writes interleaved patterns (to prevent dynamic storage...)
+; checks writing every bit low and high...
+; return Z-flag set if distinct
+
+DistinctAddresses ROUT
+        LDR     r2, [r0] ; preserve
+        LDR     r3, [r1]
+        LDR     r4, Pattern
+        STR     r4, [r0] ; mark first
+        MOV     r5, r4, ROR #16
+        STR     r5, [r1] ; mark second
+        LDR     r5, [r0]
+        CMP     r5, r4 ; check first
+        BNE     %10    ; exit with Z clear
+        LDR     r5, [r1] ; check second
+        CMP     r5, r4, ROR #16 ; clear Z if not same
+        BNE     %10
+; now check inverse bit writes
+        STR     r4, [r1] ; mark second
+        MOV     r5, r4, ROR #16
+        STR     r5, [r0] ; mark first
+        LDR     r5, [r1]
+        CMP     r5, r4 ; check second
+        BNE     %10   ; exit with Z clear
+        LDR     r5, [r0] ; check first
+        CMP     r5, r4, ROR #16 ; clear Z if not same
+10      STR     r3, [r1] ; restore
+        STR     r2, [r0]
+        ORREQ   lr, lr, #Z_Flag
+        BICNE   lr, lr, #Z_Flag
+        MOVS    pc, lr
+
+Pattern
+        &       &AAFF5500 ; shiftable bit check pattern
+
+; init state with masked out page size
+
+ResetMemC_Value
+        & &E010C :OR: MEMCADR       ; slugged ROMs + flyback refresh only + 32K page
+
+; Constants
+;
+A0      *       1 :SHL: 00
+A1      *       1 :SHL: 01
+A2      *       1 :SHL: 02
+A3      *       1 :SHL: 03
+A4      *       1 :SHL: 04
+A5      *       1 :SHL: 05
+A6      *       1 :SHL: 06
+A7      *       1 :SHL: 07
+A8      *       1 :SHL: 08
+A9      *       1 :SHL: 09
+A10     *       1 :SHL: 10
+A11     *       1 :SHL: 11
+A12     *       1 :SHL: 12
+A13     *       1 :SHL: 13
+A14     *       1 :SHL: 14
+A15     *       1 :SHL: 15
+A16     *       1 :SHL: 16
+A17     *       1 :SHL: 17
+A18     *       1 :SHL: 18
+A19     *       1 :SHL: 19
+A20     *       1 :SHL: 20
+A21     *       1 :SHL: 21
+A22     *       1 :SHL: 22
+A23     *       1 :SHL: 23
+A24     *       1 :SHL: 24
+A25     *       1 :SHL: 25
+A26     *       1 :SHL: 26
+A27     *       1 :SHL: 27
+A28     *       1 :SHL: 28
+A29     *       1 :SHL: 29
+A30     *       1 :SHL: 30
+A31     *       1 :SHL: 31
+
+Page32K * &C ; in MEMC control reg patterns...
+Page16K * &8
+Page8K  * &4
+Page4K  * &0
+
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r0-r6 trashable
+;       r9 = Current MEMC CR
+
+; Out   r9 MEMC value with slowest ROM speed, correct pagesize
+;       r7 processor speed in kHz, bit 16 => can do STM to I/O (ie MEMC1a, MEMC2), bit 17 => MEMC2
+
+ncpuloops * 1024 ; don't go longer than 4ms without refresh !
+nmulloops * 128
+
+TimeCPU ROUT
+ [ {TRUE}
+;       fudge it for now
+        LDR     r7, =6000 + (3 :SHL: 16) ; pretend 6MHz system, and MEMC2
+        MOV     pc, lr
+ |
+        BIC     r9, r9, #3 :SHL: 8
+        STR     r9, [r9]                ; turn off refresh for a bit
+
+; Time CPU/Memory speed
+
+        LDR     r1, =&7FFE              ; 32K @ 2MHz = ~16ms limit
+        MOV     r3, #IOC
+
+        MOV     r0, r1, LSR #8
+        STRB    r1, [r3, #Timer1LL]
+        STRB    r0, [r3, #Timer1LH]
+        LDR     r0, =ncpuloops
+        STRB    r0, [r3, #Timer1GO]     ; start the timer NOW
+        B       %FT10                   ; Looks superfluous, but is required
+                                        ; to get ncpuloops pipeline breaks
+
+10      SUBS    r0, r0, #1              ; 1S
+        BNE     %BT10                   ; 1N + 2S
+
+        STRB    r0, [r3, #Timer1LR]     ; latch count NOW
+        LDRB    r2, [r3, #Timer1CL]
+        LDRB    r0, [r3, #Timer1CH]
+        ADD     r2, r2, r0, LSL #8      ; count after looping is ...
+
+        SUB     r2, r1, r2              ; decrements !
+        MOV     r2, r2, LSR #1          ; IOC clock decrements at 2MHz
+
+; Time CPU/MEMC Multiply time
+
+        MOV     r4, #-1                 ; Gives worst case MUL
+
+        MOV     r0, r1, LSR #8
+        STRB    r1, [r3, #Timer1LL]
+        STRB    r0, [r3, #Timer1LH]
+        LDR     r0, =nmulloops
+        STRB    r0, [r3, #Timer1GO]     ; start the timer NOW
+        B       %FT20                   ; Looks superfluous, but is required
+                                        ; to get nmulloops pipeline breaks
+
+20      MUL     r5, r4, r4              ; 1S + 16I
+        MUL     r5, r4, r4              ; 1S + 16I
+        SUBS    r0, r0, #1              ; 1S
+        BNE     %BT20                   ; 1N + 2S
+
+        STRB    r0, [r3, #Timer1LR]     ; latch count NOW
+        LDRB    r4, [r3, #Timer1CL]
+        LDRB    r0, [r3, #Timer1CH]
+        ADD     r4, r4, r0, LSL #8      ; count after looping is ...
+
+        SUB     r4, r1, r4              ; decrements !
+        MOV     r4, r4, LSR #1          ; IOC clock decrements at 2MHz
+
+        ORR     r9, r9, #1 :SHL: 8      ; set refresh on flyback
+        STR     r9, [r9]                ; restore MEMC state a.s.a.p.
+
+; In ROM - each cpu loop took 4R cycles @ 8/f*500ns/cycle
+
+        LDR     r0, =4*(8*500/1000)*ncpuloops*1000
+        DivRem  r7, r0, r2, r1          ; r2 preserved
+        MOV     r0, #&80                ; At 8 MHz and below, run fast ROMs
+        LDR     r1, =8050               ; Over 8 MHz, need medium ROMs
+        CMP     r7, r1
+        MOVHI   r0, #&40
+        LDR     r1, =13000              ; Over 13 MHz, need slowest ROMs
+        CMP     r7, r1
+        MOVHI   r0, #&00
+        ORR     r9, r9, r0
+        STR     r9, [r9]                ; Set ROM speed appropriately
+
+ ASSERT ncpuloops = 8*nmulloops ; for given ratio cutoff <------------
+
+        MOV     r4, r4, LSL #10         ; *1024 to get resolution on divide
+        DivRem  r0, r4, r2, r1
+        LDR     r1, =1100               ; Cutoff point; MEMC1 longer than this
+        CMP     r0, r1
+        ORRLO   r7, r7, #1 :SHL: 16     ; Note MEMC1a prescence
+
+        MOV     pc, lr
+
+; Typical figures give (in ROM at 8MHz):
+
+; MEMC1  2048 CPU, 2432 MEMC -> MUL ratio 1216
+; MEMC1a 2048       864                    432
+
+ ]
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        END
diff --git a/s/MOSDict b/s/MOSDict
new file mode 100644
index 0000000000000000000000000000000000000000..6bcc4f159e64340762048c3bcad8f1bd30987c81
--- /dev/null
+++ b/s/MOSDict
@@ -0,0 +1,69 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+  TTL  ==> MOSDict
+
+TokenEscapeChar * 27
+Token0          * 0
+         MACRO
+        TokenString $n,$s
+Token$n        *  $n
+               =  ?Token$n.String +1
+Token$n.String =  $s
+         MEND
+
+MOSdictionary
+   TokenString  1,"""Syntax: *"",TokenEscapeChar,Token0,0"
+   TokenString  2,""" the "",0"
+   TokenString  3,"""director"",0"
+   TokenString  4,"""filing system"",0"
+   TokenString  5,"""current"",0"
+   TokenString  6,""" to a variable. Other types of value can be assigned with *"",0"
+   TokenString  7,"""file"",0"
+   TokenString  8,"""default "",0"
+   TokenString  9,"""tion"",0"
+   TokenString  10,"""*Configure "",0"
+   TokenString  11,"""name"",0"
+   TokenString  12,""" server"",0"
+   TokenString  13,"""number"",0"
+   TokenString  14,"TokenEscapeChar,Token1,"" <"",0"
+   TokenString  15,""" one or more "",TokenEscapeChar,Token7,""s that match"",TokenEscapeChar,Token2,""given wildcard"",0"
+   TokenString  16,""" and "",0"
+   TokenString  17,"""relocatable module"",0"
+   TokenString  18,"13,""C(onfirm)"",9,""Prompt for confirma"",TokenEscapeChar,Token9,"" of each "",0"
+   TokenString  19,"""sets"",TokenEscapeChar,Token2,0"
+   TokenString  20,"TokenEscapeChar,Token1,"" [<disc spec.>]"",0"
+   TokenString  21,"""}"",13,""V(erbose)"",9,""Print informa"",TokenEscapeChar,Token9,"" on each "",TokenEscapeChar,Token7,"" "",0"
+   TokenString  22, 0
+   TokenString  23,"TokenEscapeChar,Token31,""Landscape [<XScale> [<YScale> [<Margin> [<Threshold>]]]]]"",0"
+   TokenString  24,"TokenEscapeChar,Token41,""used"",TokenEscapeChar,Token40,""print a hard copy of"",TokenEscapeChar,Token2,""screen on EPSON-"",0"
+   TokenString  25,"""."",13,""Op"",TokenEscapeChar,Token9,""s: (use ~"",TokenEscapeChar,Token40,""force off, eg. ~"",0"
+   TokenString  26,"""printe"",0"
+   TokenString  27,"TokenEscapeChar,Token14,TokenEscapeChar,Token7,TokenEscapeChar,Token11,"">"",0"
+   TokenString  28,""" select"",0"
+   TokenString  29,"""xpression"",0"
+   TokenString  30,"TokenEscapeChar,Token1,"" ["",0"
+   TokenString  31,"""sprite"",0"
+   TokenString  32,""" displays"",0"
+   TokenString  33,"""free space"",0"
+   TokenString  34,""" {off}"",0"
+   TokenString  35,"""library"",0"
+   TokenString  36,"""parameter"",0"
+   TokenString  37,"""object"",0"
+   TokenString  38,""" all "",0"
+   TokenString  39,"""disc"",0"
+   TokenString  40,""" to "",0"
+   TokenString  41,""" is "",0"
+   =  0,0,0,0
+   END
diff --git a/s/MemInfo b/s/MemInfo
new file mode 100644
index 0000000000000000000000000000000000000000..990ab16c1693d5d06ae449841b1b6147f73f482b
--- /dev/null
+++ b/s/MemInfo
@@ -0,0 +1,611 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > MemInfo
+
+        LTORG
+
+;----------------------------------------------------------------------------------------
+; MemorySWI
+;
+;       In:     r0 = reason code and flags
+;                       bits 0-7  = reason code
+;                       bits 3-31 = reason specific flags
+;       Out:    specific to reason codes
+;
+;       Perform miscellaneous operations for memory management.
+;
+MemorySWI       ROUT
+        Push    lr                              ; Save real return address.
+        AND     lr, r0, #&FF                    ; Get reason code.
+        CMP     lr, #(%40-%30):SHR:2            ; If valid reason code then
+        ADDCC   lr, lr, #(%30-%10):SHR:2        ;   determine where to jump to in branch table,
+        ADDCC   lr, pc, lr, LSL #2
+        Push    lr, CC                          ;   save address so we can
+10
+        ADRCC   lr, MemReturn                   ;   set up default return address for handler routines
+        Pull    pc, CC                          ;   and jump into branch table.
+20
+        ADRL    r0, ErrorBlock_HeapBadReason    ; Otherwise, unknown reason code.
+        SETV
+        ; Drop through to...
+
+MemReturn
+ [ International
+        BLVS    TranslateError
+ ]
+        Pull    lr                              ; Get back real return address.
+        BVS     SLVK_SetV
+        ExitSWIHandler
+
+30
+        B       MemoryConvert
+        B       %BT20                           ; Reason codes 1-5 are reserved.
+        B       %BT20
+        B       %BT20
+        B       %BT20
+        B       %BT20
+        B       MemoryPhysSize
+        B       MemoryReadPhys
+        B       MemoryAmounts
+        B       MemoryIOSpace
+40
+
+
+;----------------------------------------------------------------------------------------
+; MemoryConvert
+;
+;       In:     r0 = flags
+;                       bit     meaning
+;                       0-7     0 (reason code)
+;                       8       page number provided when set
+;                       9       logical address provided when set
+;                       10      physical address provided when set
+;                       11      fill in page number when set
+;                       12      fill in logical address when set
+;                       13      fill in physical address when set
+;                       14-15   0,1=don't change cacheability
+;                               2=disable caching on these pages
+;                               3=enable caching on these pages
+;                       16-31   reserved (set to 0)
+;               r1 -> page block
+;               r2 = number of 3 word entries in page block
+;
+;       Out:    r1 -> updated page block
+;
+;       Converts between representations of memory addresses. Can also set the
+;       cacheability of the specified pages.
+;
+
+; Declare symbols used for decoding flags (given and wanted are used
+; so that C can be cleared by rotates of the form a,b). We have to munge
+; the flags a bit to make the rotates even.
+;
+ppn             *       1:SHL:0         ; Bits for address formats.
+logical         *       1:SHL:1
+physical        *       1:SHL:2
+all             *       ppn :OR: logical :OR: physical
+given           *       24              ; Rotate for given fields.
+wanted          *       20              ; Rotate for wanted fields.
+ppn_bits        *       ((ppn :SHL: 4) :OR: ppn)
+logical_bits    *       ((logical :SHL: 4) :OR: logical)
+physical_bits   *       ((physical :SHL: 4) :OR: physical)
+cacheable_bit   *       1:SHL:15
+alter_cacheable *       1:SHL:16
+flush_cache     *       1:SHL:31        ; Internal flag.
+
+MemoryConvert   ROUT
+        ENTRY   "r0-r11"                ; Need lots of registers!!
+
+        BIC     lr, r0, #all,given      ; Need to munge r0 to get rotates to work (must be even).
+        AND     r0, r0, #all,given
+        ORR     r0, r0, lr, LSL #1      ; Move bits 11-30 to 12-31.
+
+        TST     r0, #all,given          ; Check for invalid argument (no fields provided)
+        TEQNE   r2, #0                  ;   (no entries in table).
+        ADREQL  r0, ErrorBlock_BadParameters
+        BEQ     %FT95
+
+        EOR     lr, r0, r0, LSL #given-wanted   ; If flag bits 8-10 and 12-14 contain common bits then
+        AND     lr, lr, #all,wanted             ;   clear bits in 12-14 (ie. don't fill in fields already given).
+        EOR     lr, lr, #all,wanted
+        BIC     r0, r0, lr
+
+        MOV     r6, #0
+        LDR     r7, [r6, #MaxCamEntry]
+        LDR     r6, [r6, #CamEntriesPointer]
+        LDR     r8, =L2PT
+
+        BIC     r0, r0, #flush_cache    ; Bit set if any page is really made uncacheable (we must flush the cache).
+10
+        SUBS    r2, r2, #1
+        BCC     %FT70
+
+        LDMIA   r1!, {r3-r5}            ; Get next three word entry (PN,LA,PA) and move on pointer.
+
+        TST     r0, #physical,wanted    ; If PA not wanted
+        BEQ     %FT20                   ;   then skip.
+        TST     r0, #logical,given      ; If LA not given (rotate clears C) then
+        BLEQ    ppn_to_logical          ;   get it from PN (PA wanted (not given) & LA not given => PN given).
+        BLCC    logical_to_physical     ; Get PA from LA.
+        BCS     %FT80
+        TST     r0, #logical,wanted
+        STRNE   r4, [r1, #-8]           ; Store back LA if wanted.
+        STR     r5, [r1, #-4]           ; Store back PA.
+20
+        TST     r0, #alter_cacheable    ; If altering cacheability
+        EORNE   lr, r0, #ppn,given      ;   and PN not given
+        TSTNE   lr, #ppn,given
+        TSTEQ   r0, #ppn,wanted         ;   OR PN wanted then don't skip
+        BEQ     %FT30                   ; else skip.
+        TST     r0, #physical_bits,given        ; If PA not given and PA not wanted (rotate clears C) then
+        BLEQ    logical_to_physical             ;   get it from LA (PN wanted/not given & PA not given => LA given).
+        BLCC    physical_to_ppn         ; Get PN from PA.
+        BCS     %FT80
+        TST     r0, #ppn,wanted
+        STRNE   r3, [r1, #-12]          ; Store back PN if wanted.
+30
+        TST     r0, #logical,wanted     ; If LA wanted
+        EORNE   lr, r0, #physical,wanted
+        TSTNE   lr, #physical,wanted    ;   and PA not wanted then don't skip
+        BEQ     %FT40                   ; else skip.
+        TST     r0, #alter_cacheable    ; If not changing cacheability (already have PN)
+        TSTEQ   r0, #ppn_bits,given     ;   and PN not given and PN not wanted (rotate clears C) then
+        BLEQ    physical_to_ppn         ;   get it from PA (LA wanted (not given) & PN not given => PA given).
+        BLCC    ppn_to_logical          ; Get LA from PN.
+        BCS     %FT80
+        STR     r4, [r1, #-8]           ; Store back LA.
+40
+        TST     r0, #alter_cacheable
+        BEQ     %BT10
+
+        CMP     r7, r3                  ; Make sure page number is valid (might not have done any conversion).
+        BCC     %FT80
+
+        ADD     r3, r6, r3, LSL #3      ; Point to CAM entry for this page.
+        LDMIA   r3, {r4,r5}             ; Get logical address and PPL.
+
+        AND     lr, r5, #PageFlags_TempUncacheableBits
+        TST     r0, #cacheable_bit
+        BNE     %FT50
+
+        TEQ     lr, #PageFlags_TempUncacheableBits      ; Make uncacheable (increment count).
+        BEQ     %BT10                                   ; If count has reached max then go no further (should not happen).
+        TEQ     lr, #0                                  ; EQ => we have to change L2.
+        ADD     r5, r5, #1:SHL:TempUncacheableShift
+        B       %FT60
+50
+        TEQ     lr, #0                                  ; Make cacheable (decrement count).
+        BEQ     %BT10                                   ; If count is already 0 then go no further (page already cacheable).
+        SUB     r5, r5, #1:SHL:TempUncacheableShift
+        TST     r5, #PageFlags_TempUncacheableBits      ; EQ => we have to change L2.
+60
+        STR     r5, [r3, #4]            ; Write back new PPL.
+        BNE     %BT10                   ; Do next entry if we don't have to change L2.
+
+        MOV     r4, r4, LSR #12
+        ADD     r4, r8, r4, LSL #2      ; Address of L2 entry for logical address.
+        LDR     r5, [r4]                ; Get L2 entry (safe as we know address is valid).
+        TST     r0, #cacheable_bit
+        BICEQ   r5, r5, #L2_C           ; Disable/enable cacheability.
+        ORREQ   r0, r0, #flush_cache    ; If disable then we must flush cache later.
+        ORRNE   r5, r5, #L2_C
+        STR     r5, [r4]                ; Write back new L2 entry.
+
+        B       %BT10                   ; Do next entry.
+
+70
+        TST     r0, #flush_cache        ; If any page has been made uncacheable in L2 then
+        SetCop  r9, CR_IDCFlush, NE     ;   flush the cache.
+        EXIT
+
+80
+        TST     r0, #alter_cacheable    ; If we haven't changed any cacheability stuff then
+        BEQ     %FT90                   ;   just return error.
+
+        AND     lr, r0, #all,wanted             ; Get wanted flags.
+        LDMIA   sp, {r0,r1,r3}                  ; Get back original flags, pointer and count.
+        ORR     r0, r0, lr, LSR #given-wanted   ; Wanted fields are now also given as we have done the conversion.
+        BIC     r0, r0, #all:SHL:11             ; Clear wanted flags, we only want to change cacheability.
+        EOR     r0, r0, #cacheable_bit          ; If we made them uncacheable then make them cacheable again & v.v.
+        SUB     r2, r3, r2
+        SUBS    r2, r2, #1              ; Change back the entries we have changed up to (but excluding) the error entry.
+        BLNE    MemoryConvert
+90
+        ADRL    r0, ErrorBlock_BadAddress
+95
+        STR     r0, [sp]
+        SETV
+        EXIT
+
+
+;----------------------------------------------------------------------------------------
+; ppn_to_logical
+;
+;       In:     r3 = page number
+;               r5 = physical address if given
+;               r6 = CamEntriesPointer
+;               r7 = MaxCamEntry
+;
+;       Out:    r9 corrupted
+;               CC => r4 = logical address
+;               CS => invalid page number
+;
+;       Convert physical page number to logical address.
+;
+ppn_to_logical
+        CMP     r7, r3                  ; Validate page number.
+        ORRCCS  pc, lr, #C_bit          ; Invalid so return C set.
+
+        LDR     r4, [r6, r3, LSL #3]    ; If valid then lookup logical address.
+        TST     r0, #physical,given     ; If physical address was given then
+        LDRNE   r9, =&FFF
+        ANDNE   r9, r5, r9              ;   mask off page offset
+        ORRNE   r4, r4, r9              ;   and combine with logical address.
+        BICS    pc, lr, #C_bit          ; Return C clear.
+
+
+;----------------------------------------------------------------------------------------
+; logical_to_physical
+;
+;       In:     r4 = logical address
+;               r8 = L2PT
+;
+;       Out:    r9 corrupted
+;               CC => r5 = physical address
+;               CS => invalid logical address, r5 corrupted
+;
+;       Convert logical address to physical address.
+;
+logical_to_physical
+        MOV     r9, r4, LSR #12         ; r9 = logical page number
+        ADD     r9, r8, r9, LSL #2      ; r9 -> L2PT entry for logical address
+        MOV     r5, r9, LSR #12         ; r5 = page offset to L2PT entry for logical address
+        LDR     r5, [r8, r5, LSL #2]    ; r5 = L2PT entry for L2PT entry for logical address
+        EOR     r5, r5, #2_10           ; Check for valid page.
+        TST     r5, #3
+        ORRNES  pc, lr, #C_bit
+
+        LDR     r5, [r9]                ; r5 = L2PT entry for logical address
+        EOR     r5, r5, #2_10           ; Check for valid page.
+        TST     r5, #3
+        ORRNES  pc, lr, #C_bit
+
+        LDR     r9, =&FFF               ; Valid so
+        BIC     r5, r5, r9              ;   mask off bits 0-11,
+        AND     r9, r4, r9              ;   get page offset from logical page
+        ORR     r5, r5, r9              ;   combine with physical page address.
+        BICS    pc, lr, #C_bit
+
+
+;----------------------------------------------------------------------------------------
+; physical_to_ppn
+;
+;       In:     r5 = physical address
+;               r7 = MaxCamEntry
+;
+;       Out:    r9-r11 corrupted
+;               CC => r3 = page number
+;               CS => invalid physical address, r3 corrupted
+;
+;       Convert physical address to physical page number.
+;
+physical_to_ppn ROUT
+        MOV     r9, #PhysRamTable
+        MOV     r3, #0                  ; Start at page 0.
+10
+        CMP     r7, r3                  ; Stop if we run out of pages
+        ORRCCS  pc, lr, #C_bit          ;   (return with C set).
+
+        LDMIA   r9!, {r10,r11}          ; Get start address and size of next block.
+        SUB     r10, r5, r10            ; Determine if given address is in this block.
+        CMP     r10, r11
+        ADDCS   r3, r3, r11, LSR #12    ; Move on to next block.
+        BCS     %BT10
+
+        ADD     r3, r3, r10, LSR #12
+        BICS    pc, lr, #C_bit          ; Return with C clear.
+
+
+;----------------------------------------------------------------------------------------
+; Symbols used in MemoryPhysSize and MemoryReadPhys
+;
+
+; Shifts to determine number of bytes/words to allocate in table.
+BitShift        *       10
+ByteShift       *       BitShift + 3
+WordShift       *       ByteShift + 2
+
+; Bit patterns for different types of memory.
+NotPresent      *       &00000000
+DRAM_Pattern    *       &11111111
+VRAM_Pattern    *       &22222222
+ROM_Pattern     *       &33333333
+IO_Pattern      *       &44444444
+NotAvailable    *       &88888888
+
+
+;----------------------------------------------------------------------------------------
+; MemoryPhysSize
+;
+;       In:     r0 = 6 (reason code with flag bits 8-31 clear)
+;
+;       Out:    r1 = table size (in bytes)
+;               r2 = page size (in bytes)
+;
+;       Returns information about the memory arrangement table.
+;
+MemoryPhysSize
+        MOV     r1, #PhysSpaceSize :SHR: ByteShift
+        MOV     r2, #4*1024
+        MOV     pc, lr
+
+
+;----------------------------------------------------------------------------------------
+; MemoryReadPhys
+;
+;       In:     r0 = 7 (reason code with flag bits 8-31 clear)
+;               r1 -> memory arrangement table to be filled in
+;
+;       Out:    r1 -> filled in memory arrangement table
+;
+;       Returns the physical memory arrangement table in the given block.
+;
+MemoryReadPhys  ROUT
+        ENTRY   "r1-r10"
+
+ [ OSROM_ImageSize = 4096
+        ; &00000000 to &00400000 is ROM.
+        MOV     r2, #(&00400000-&00000000) :SHR: WordShift
+        LDR     r3, =ROM_Pattern :OR: NotAvailable
+        BL      fill_words
+
+        ; &00400000 to &02000000 is allocated to ROM but is not present.
+        MOV     r2, #(&02000000-&00400000) :SHR: WordShift
+        LDR     r3, =NotPresent :OR: NotAvailable
+        BL      fill_words
+ |
+        ; &00000000 to &00200000 is ROM.
+        MOV     r2, #(&00200000-&00000000) :SHR: WordShift
+        LDR     r3, =ROM_Pattern :OR: NotAvailable
+        BL      fill_words
+
+        ; &00200000 to &02000000 is allocated to ROM but is not present.
+        MOV     r2, #(&02000000-&00200000) :SHR: WordShift
+        LDR     r3, =NotPresent :OR: NotAvailable
+        BL      fill_words
+ ]
+
+        ; &02000000 to &02200000 is VRAM or not present.
+        MOV     r4, #0
+        LDR     r4, [r4, #VRAMSize]     ; Get amount of VRAM (in bytes).
+        TEQ     r4, #0
+        MOVNE   r2, r4, LSR #WordShift  ; If there is some then fill part of table.
+        LDRNE   r3, =VRAM_Pattern :OR: NotAvailable
+        BLNE    fill_words
+
+        ; End of VRAM to &03000000 is not present.
+        RSB     r4, r4, #&03000000-&02000000
+        MOV     r2, r4, LSR #WordShift
+        LDR     r3, =NotPresent :OR: NotAvailable
+        BL      fill_words
+
+        ; &03000000 to &03800000 is I/O.
+        MOV     r2, #(&03800000-&03000000) :SHR: WordShift
+        LDR     r3, =IO_Pattern :OR: NotAvailable
+        BL      fill_words
+
+        ; &03800000 to &08000000 is not present.
+        MOV     r2, #(&08000000-&03800000) :SHR: WordShift
+        LDR     r3, =NotPresent :OR: NotAvailable
+        BL      fill_words
+
+        ; &08000000 to &10000000 is I/O (EASI space).
+        MOV     r2, #(&10000000-&08000000) :SHR: WordShift
+        LDR     r3, =IO_Pattern :OR: NotAvailable
+        BL      fill_words
+
+        ; &10000000 to &20000000 is DRAM or not present.
+        MOV     r2, #&10000000          ; Current physical address.
+        MOV     r3, #0                  ; Next word to store in table.
+        MOV     r4, #32                 ; How much more we have to shift r3 before storing it.
+        MOV     r5, #0                  ; Current page number.
+        MOV     r6, #PhysRamTable
+        LDR     r7, [r3, #CamEntriesPointer]
+        ADD     r7, r7, #4              ; Point to PPL entries.
+        LDR     r8, [r3, #MaxCamEntry]
+10
+        LDMIA   r6!, {r9,r10}           ; Get physical address and size of next block.
+
+        CMP     r9, #&10000000          ; If not DRAM then
+        ADDCC   r5, r5, r10, LSR #12    ;   adjust current page number
+        BCC     %BT10                   ;   and try next block.
+
+        ADD     r10, r10, r9            ; Add amount of unused space between current and start of block.
+        SUB     r10, r10, r2            ; size = size + (physaddr - current)
+20
+        SUBS    r4, r4, #4              ; Reduce shift.
+        MOVCS   r3, r3, LSR #4          ; If more space in current word then shift it.
+        STRCC   r3, [r1], #4            ; Otherwise, store current word
+        MOVCC   r3, #0                  ;   and start a new one.
+        MOVCC   r4, #28
+
+        CMP     r2, r9                  ; If not reached start of block then page is not present.
+        ORRCC   r3, r3, #(NotPresent :OR: NotAvailable) :SHL: 28
+        BCC     %FT30
+        LDR     lr, [r7, r5, LSL #3]    ; Page is there so get PPL and determine if it's available or not.
+        TST     lr, #PageFlags_Unavailable
+        ORREQ   r3, r3, #DRAM_Pattern :SHL: 28
+        ORRNE   r3, r3, #(DRAM_Pattern :OR: NotAvailable) :SHL: 28
+        ADD     r5, r5, #1              ; Increment page count.
+30
+        ADD     r2, r2, #&1000          ; Increase current address.
+        SUBS    r10, r10, #&1000        ; Decrease size of block.
+        BGT     %BT20                   ; Stop if no more block left.
+
+        CMP     r8, r5                  ; Stop if we run out of pages.
+        BCS     %BT10
+
+        TEQ     r3, #0                          ; If not stored last word then
+        MOVNE   r3, r3, LSR r4                  ;   put bits in correct position
+        ADDNE   r2, r2, r4, LSL #BitShift       ;   adjust current address
+        RSBNE   r4, r4, #32                     ;   rest of word is not present
+        LDRNE   lr, =NotPresent :OR: NotAvailable
+        ORRNE   r3, r3, lr, LSL r4
+        STRNE   r3, [r1], #4                    ;   and store word.
+
+        ; End of last block of DRAM to &20000000 is not present.
+        RSBS    r2, r2, #&20000000
+        MOVNE   r2, r2, LSR #WordShift
+        LDRNE   r3, =NotPresent :OR: NotAvailable
+        BLNE    fill_words
+
+        EXIT
+
+
+fill_words
+        STR     r3, [r1], #4
+        SUBS    r2, r2, #1
+        BNE     fill_words
+        MOV     pc, lr
+
+
+;----------------------------------------------------------------------------------------
+; MemoryAmounts
+;
+;       In:     r0 = flags
+;                       bit     meaning
+;                       0-7     8 (reason code)
+;                       8-11    1=return amount of DRAM
+;                               2=return amount of VRAM
+;                               3=return amount of ROM
+;                               4=return amount of I/O space
+;                       12-31   reserved (set to 0)
+;
+;       Out:    r1 = number of pages of the specified type of memory
+;               r2 = page size (in bytes)
+;
+;       Return the amount of the specified type of memory.
+;
+MemoryAmounts   ROUT
+        ENTRY   "r3"
+
+        BICS    lr, r0, #&FF            ; Get type of memory required (leave bits 12-31, non-zero => error).
+        BEQ     %FT30                   ; Don't understand 0 (so the spec says).
+
+        TEQ     lr, #4:SHL:8            ; Check for IO space.
+        LDREQ   r1, =136*1024*1024      ; Just return 136M (includes VIDC and EASI space).
+        BEQ     %FT20
+
+        TEQ     lr, #3:SHL:8            ; Check for ROM.
+        LDREQ   r1, =OSROM_ImageSize*1024
+        BEQ     %FT20
+
+        TEQ     lr, #2:SHL:8            ; Check for VRAM.
+        MOVEQ   r1, #0                  ; Return amount of VRAM.
+        LDREQ   r1, [r1, #VRAMSize]
+        BEQ     %FT20
+
+        TEQ     lr, #1:SHL:8            ; Check for DRAM.
+        BNE     %FT30
+        MOV     r1, #0
+        LDR     lr, [r1, #RAMLIMIT]
+        LDR     r1, [r1, #VRAMSize]
+        SUB     r1, lr, r1              ; Return amount of RAM - amount of VRAM.
+
+20
+        MOV     r1, r1, LSR #12         ; Return as number of pages.
+        MOV     r2, #4*1024             ; Return page size.
+        EXIT
+
+30
+        ADRL    r0, ErrorBlock_BadParameters
+        SETV
+        EXIT
+
+
+;----------------------------------------------------------------------------------------
+; MemoryIOSpace
+;
+;       In:     r0 = 9 (reason code with flag bits 8-31 clear)
+;               r1 = controller ID
+;                       bit     meaning
+;                       0-7     controller sequence number
+;                       8-31    controller type:
+;                               0 = EASI card access speed control
+;                               1 = EASI space
+;                               2 = VIDC1
+;                               3 = VIDC20
+;
+;       Out:    r1 = controller base address or 0 if not present
+;
+;       Return the location of the specified controller.
+;
+MemoryIOSpace   ROUT
+        ENTRY   "r2"
+
+        AND     r2, r1, #&FF            ; Get sequence number.
+        MOV     r1, r1, LSR #8          ; Get controller type.
+        CMP     r1, #4                  ; Make sure it's in range.
+        BCS     %FT20
+
+        ADR     lr, controller_types
+        LDR     r1, [lr, r1, LSL #2]    ; Get base address or offset to table.
+        TEQ     r1, #0                  ; If not present or
+        CMPNE   r1, #1024               ;     not offset to table then
+        EXIT    GE                      ;   return.
+
+        ADD     lr, lr, r1              ; Point to table indexed by sequence number.
+        LDR     r1, [lr], #4            ; Get sequence number limit.
+        CMP     r2, r1                  ; Make sure it's in range.
+        BCS     %FT20
+        LDR     r1, [lr, r2, LSL #2]    ; Get base address.
+        EXIT
+
+20
+        ADRL    r0, ErrorBlock_BadParameters
+        SETV
+        EXIT
+
+
+controller_types
+ [ IO_Type = "IOMD"
+        DCD     IOMD_Base + IOMD_ECTCR  ; Expansion card timing control.
+        DCD     easi_space_table - controller_types
+ |
+        DCD     0
+        DCD     0
+ ]
+ [ VIDC_Type = "VIDC20"
+        DCD     0
+        DCD     VIDC
+ |
+        DCD     VIDC
+        DCD     0
+ ]
+
+ [ IO_Type = "IOMD"
+easi_space_table
+        DCD     8                       ; Maximum of 8 expansion cards.
+        DCD     PhysSpace + IOMD_EASI_Base0
+        DCD     PhysSpace + IOMD_EASI_Base1
+        DCD     PhysSpace + IOMD_EASI_Base2
+        DCD     PhysSpace + IOMD_EASI_Base3
+        DCD     PhysSpace + IOMD_EASI_Base4
+        DCD     PhysSpace + IOMD_EASI_Base5
+        DCD     PhysSpace + IOMD_EASI_Base6
+        DCD     PhysSpace + IOMD_EASI_Base7
+ ]
+
+        END
diff --git a/s/Middle b/s/Middle
new file mode 100644
index 0000000000000000000000000000000000000000..f6be1a0af2bebba3c04e58100a71d873cf01cc0d
--- /dev/null
+++ b/s/Middle
@@ -0,0 +1,1293 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL     => Middle
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; VecRdLine - Read line from input stream (OSWORD 0 equivalent)
+
+; In    r0 -> buffer for characters
+;       r1 = max length of line (excluding carriage return)
+;       r2 = lowest character put into buffer
+;       r3 = highest character put into buffer
+;       wp -> OsbyteVars
+
+; Out   r1 = length of line (excluding carriage return)
+;       C=0 => line terminated by return (CR or LF)
+;       C=1 => line terminated by ESCAPE
+
+; ReadLine: suggested extensions (available RSN)
+;
+; are 1) stopping it reflecting control characters/characters not put in the
+;        buffer
+;     2) making any reflection print a given character (for hiding passwords
+;        etc
+;
+; So, definition is :
+;
+; Top byte R0 contains flags :
+;   Bit 31 set means don't reflect characters that don't go in the buffer
+;   Bit 30 set means reflect with the character in R4
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+VecRdLine ROUT
+
+        Push    "R4-R7"
+        AND     R7, R0, #&C0000000      ; extract flags
+        AND     R4, R4, #&FF
+        ORR     R7, R7, R4              ; got flags, potential echo byte in R7
+
+        BIC     R4, R0, #ARM_CC_Mask-3  ; R4 -> buffer
+
+        MOV     R6, #0                  ; R6 = index into buffer
+        STRB    R6, PageModeLineCount   ; reset page lines
+
+        B       %FT10
+        LTORG
+
+05
+        SWI     XOS_WriteC
+        BVS     ReadLine_Vset
+10
+        SWI     XOS_ReadC
+        BVS     ReadLine_Vset
+        BCS     %FT70                   ; ESCAPE
+
+        LDRB    R5, WrchDest            ; does output include VDU ?
+        TST     R5, #2
+        BNE     %FT30                   ; no, then still buffer
+
+        LDRB    R5, VDUqueueItems       ; is VDU queueing ?
+        TEQ     R5, #0
+        BNE     %BT05                   ; yes, then just print
+
+30
+        TEQ     R0, #127                ; is it DELETE ?
+        TEQNE   R0, #8                  ; or backspace?
+        BNE     %FT40                   ; no, then skip
+
+        TEQ     R6, #0                  ; yes, then have we any chars ?
+        BEQ     %BT10                   ; no, then loop
+        SUB     R6, R6, #1              ; go back 1 char
+        MOV     R0, #127
+        B       %BT05                   ; print DEL and loop
+
+40
+        TEQ     R0, #21                 ; is it CTRL-U ?
+        BNE     %FT50                   ; no, then skip
+
+        TEQ     R6, #0                  ; yes, then have we any chars ?
+        BEQ     %BT10                   ; no, then loop
+
+45
+        SWI     XOS_WriteI+127          ; print DELs to start of line
+        BVS     ReadLine_Vset
+        SUBS    R6, R6, #1
+        BNE     %BT45
+        B       %BT10                   ; then loop
+
+50
+        TEQ     R0, #13                 ; is it CR ?
+        TEQNE   R0, #10                 ; or LF ?
+        MOVEQ   r0, #13                 ; always store CR if so
+        STRB    R0, [R4, R6]            ; store byte in buffer
+        BEQ     %FT60                   ; Exit if either
+
+        CMP     R6, R1                  ; is buffer full ?
+        MOVCS   R0, #7                  ; if so, beep and loop
+        BCS     %BT05
+
+        CMP     R0, R2                  ; is char >= min ?
+        CMPCS   R3, R0                  ; and char <= max ?
+        ADDCS   R6, R6, #1              ; if so, then inc pointer
+        BCS     %FT80
+        CMP     R7, #0                  ; no reflection
+        BMI     %BT10                   ; of non-entered chars
+
+80      TST     R7, #1:SHL:30
+        ANDNE   R0, R7, #&FF            ; echo char -> R0
+
+        B       %BT05                   ; echo character
+
+60      SWI     XOS_NewLine
+        BVS     ReadLine_Vset
+
+; insert code here to call NetVec with R0 = &0D
+
+70
+        CLRV
+EndReadLine
+        MOVVC   R0, R4                  ; restore R0
+        MOV     R5, #0
+        LDRB    R5, [R5, #ESC_Status]
+        MOVS    R5, R5, LSL #(32-6)     ; shift esc bit into carry
+
+        MOV     R1, R6                  ; R1 := length
+        Pull    "R4-R7, lr"             ; Always claiming vector
+        BIC     lr, lr, #V_bit :OR: C_bit
+        MOV     r12, pc
+        AND     r12, r12, #V_bit :OR: C_bit
+        ORRS    pc, lr, r12             ; back with C, V affected.
+
+ReadLine_Vset
+        SETV
+        B       EndReadLine
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_Control (deprecated): set handlers
+
+SCTRL   Push    "R0-R3, lr"
+
+        TEQP    PC, #SVC_mode+I_bit ; need IRQs off to get consistent state
+        MOV     R0, #EventHandler
+        MOV     R1, R3
+        BL      CallCESWI
+        STR     R1, [stack, #3*4]
+
+        MOV     R0, #EscapeHandler
+        LDR     R1, [stack, #2*4]
+        BL      CallCESWI
+        STR     R1, [stack, #2*4]
+
+        MOV     R0, #ErrorHandler
+        Pull    "R1, R3"
+        BL      CallCESWI
+        MOV     R0, R1
+        MOV     R1, R3
+
+        Pull    "R2, R3, lr"
+        ExitSWIHandler
+
+
+CallCESWI
+        Push    lr
+        MOV     r2, #0                  ; readonly
+        SWI     XOS_ChangeEnvironment
+        Pull    pc
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_SetEnv (deprecated): Environment setting
+
+SSTENV  Push    "R0, R1, lr"
+        TEQP    PC, #SVC_mode+I_bit     ; no irqs - want consistent set.
+        MOV     R0, #AddressExceptionHandler
+        MOV     R1, R7
+        SWI     XOS_ChangeEnvironment
+        MOV     R7, R1
+
+        MOV     R0, #DataAbortHandler
+        MOV     R1, R6
+        SWI     XOS_ChangeEnvironment
+        MOV     R6, R1
+
+        MOV     R0, #PrefetchAbortHandler
+        MOV     R1, R5
+        SWI     XOS_ChangeEnvironment
+        MOV     R5, R1
+
+        MOV     R0, #UndefinedHandler
+        MOV     R1, R4
+        SWI     XOS_ChangeEnvironment
+        MOV     R4, R1
+
+        MOV     R0, #MemoryLimit
+        LDR     R1, [stack, #4]
+        SWI     XOS_ChangeEnvironment
+        STR     R1, [stack, #4]
+
+        MOV     R0, #ExitHandler
+        Pull    "R1"
+        BL      CallCESWI
+        Push    "R1"
+
+        MOV     R12, #0
+        LDR     R2, [R12, #RAMLIMIT]    ; this is read-only
+        MOV     R3, #0                  ; never any Brazil-type buffering
+                                        ; m2 tools will complain if there is!
+        Pull    "R0, R1, lr"
+        ExitSWIHandler
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Change user state SWIs
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+SINTON  BIC     lr, lr, #I_bit
+        ExitSWIHandler
+
+SINTOFF ORR     lr, lr, #I_bit
+        ExitSWIHandler
+
+SENTERSWI ORR   lr, lr, #SVC_mode
+        ExitSWIHandler
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_BreakPt: user breakpoint - unsuitable for debugging SVC mode code!
+
+SBRKPT ROUT
+
+        ADD     sp, sp, #4              ; discard stacked R11
+        MOV     r12, #0
+        LDR     r12, [r12, #BrkBf]
+        SUB     r14, R14, #4
+        STR     r14, [r12, #15*4]       ; PC of the SWI put in.
+        TST     r14, #SVC_mode
+        BNE     %FT01
+        STMIA   r12!, {r0}
+        MOV     r0, r12
+        LDMFD   sp, {r10-r12}
+        STMIA   r0, {r1-r12, r13_usr, r14_usr}^ ; user mode case done.
+
+10      LDR     stack, =SVCSTK
+        MOV     r12, #BrkAd_ws
+        LDMIA   r12, {r12, pc}          ; call breakpoint handler
+
+
+01      TST     r14, #1                 ; SWI mode?
+        TSTNE   r14, #2
+        BNE     %FT02                   ; [yes]
+
+        STMIA   r12!, {r0}
+        MOV     r0, r12
+        LDMFD   sp, {r10-r12}           ; Not banked if IRQ mode
+        TEQP    pc, R14                 ; get at registers
+        NOP
+        STMIA   r0, {r1-r14}
+        TEQP    pc, #SVC_mode
+        B       %BT10
+
+
+02      MOV     r14, r12                ; supervisor mode. R14 in buffer dead
+        LDMFD   sp!, {r10-r12}
+        STMIA   r14, {r0-r14}
+        B       %BT10
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_UnusedSWI (deprecated): Set the unused SWI handler
+
+SUNUSED Push    "R1, lr"
+        MOV     R1, R0
+        MOV     R0, #UnusedSWIHandler
+        SWI     XOS_ChangeEnvironment
+        MOV     R0, R1
+        Pull    "R1, lr"
+        ExitSWIHandler
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_SetCallBack: Set callback flag
+
+SSETCALL ROUT
+
+        TEQP    pc, #SVC_mode+I_bit
+        MOV     r10, #0
+        LDRB    r11, [r10, #CallBack_Flag]
+        ORR     r11, r11, #CBack_OldStyle
+        STRB    r11, [r10, #CallBack_Flag]
+        Pull    "r11"
+        Pull    "r10-r12"
+        MOVS    pc, lr                  ; Do NOT exit via normal mechanism
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI read mouse information
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+VecMouse
+     MOV   R10, #MouseV
+     TEQP  PC, #SVC_mode+I_bit ; no irqs
+     Push "lr"
+     BL    CallVector
+     Pull "lr"
+     ExitSWIHandler
+
+ [ :LNOT: DriversInKernel
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_SerialOp when not in kernel
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+SerialOp ROUT
+        MOV     r10, #SerialV
+        Push    lr
+        BL      CallVector
+        Pull    lr
+        BICCC   lr, lr, #C_bit
+        ORRCS   lr, lr, #C_bit
+        B       SLVK_TestV
+ ]
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Supervisor routines to set default handlers
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+; Reset Error and Escape handlers
+
+DEFHAN LDR  R1, =GeneralMOSBuffer         ; decent length error buffer
+       ADR  R0, ERRORH
+       ADR  R2, ESCAPH
+       MOV  R3, #0
+       MOV  R4, R14
+       SWI  XOS_Control
+
+       MOV  r0, #UpCallHandler
+       ADR  r1, DefUpcallHandler
+       MOV  r2, #0
+       SWI  XOS_ChangeEnvironment
+
+       MOV  PC, R4
+
+; Reset Exception, Event, BreakPoint, UnusedSWI, Exit and CallBack handlers
+
+DEFHN2 MOV  R12, R14
+       MOV  R0, #0
+       MOV  R1, #0
+       MOV  R2, #0
+       ADR  R3, EVENTH
+       SWI  XOS_Control
+       LDR  R0, =DUMPER
+       ADR  R1, NOCALL
+       SWI  XOS_CallBack
+       ADRL R0, CLIEXIT
+       MOV  R1, #0
+       MOV  R2, #0
+       ADR  R4, UNDEF
+       ADR  R5, ABORTP
+       ADRL R6, ABORTD
+       ADRL R7, ADDREX
+       SWI  XOS_SetEnv
+       LDR  R0, =DUMPER
+       ADR  R1, DEFBRK
+       SWI  XOS_BreakCtrl
+       ADRL R0, NoHighSWIHandler
+       SWI  XOS_UnusedSWI
+       MOV  PC, R12
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; system handlers
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+DefUpcallHandler
+ESCAPH
+EVENTH  MOV     pc, lr
+
+
+NOCALL  MOV     r0, #0                  ; default callback routine
+        LDR     r14, [r0, #CallBf]
+        LDMIA   r14, {r0-r12, r13_usr, r14_usr}^ ; load user's regs
+        NOP
+        LDR     r14, [r14, #4*15]
+        MOVS    pc, r14
+
+
+ERRORH ROUT
+
+      [ International
+        ADR     R0,KernelMessagesBlock+4
+        ADR     R1,ErrorToken
+        MOV     R2,#0
+        SWI     XMessageTrans_Lookup
+        MOVVC   R12,R2
+        ADRVS   R12,ErrorText
+
+01
+        LDRB    R0,[R12],#1
+        CMP     R0,#32
+        BLT     %FT99
+        CMP     R0,#"%"
+        SWINE   XOS_WriteC
+        BVS     %FT99
+        BNE     %BT01
+
+        LDRB    R0,[R12],#1              ; Found a %
+        CMP     R0,#32
+        BLT     %FT99
+
+        CMP     R0,#"0"
+        LDREQ   R0,=GeneralMOSBuffer+8
+        BEQ     %FT10
+        CMP     R0,#"1"
+        BNE     %BT01
+
+        LDR     R3,=GeneralMOSBuffer+4
+        LDR     R0,[R3]
+        LDR     R1,=MOSConvertBuffer
+        MOV     R2,#12
+        SWI     XOS_ConvertHex8
+
+02
+        LDRB    R1, [R0], #1
+        CMP     R1, #"0"
+        BEQ     %BT02
+        CMP     R1, #0
+        SUBEQ   R0, R0, #1
+        SUB     R0, R0, #1
+
+10
+        SWI     XOS_Write0
+        BVC     %BT01
+
+99
+        SWI     XOS_NewLine
+
+        MOV     R0, #0
+        STRB    R0, [R0, #ErrorSemaphore]                 ; Enable error translation, in case we got here in the
+                                                        ; middle of looking up an error
+
+        B       GOSUPV
+
+ErrorToken      =       "Error:"
+ErrorText       =       "Error: %0 (Error Number &%1)",0
+        ALIGN
+      |
+
+        SWI     OS_WriteS
+        =       10,13,"Error:",10,13, 0
+        ALIGN
+        LDR     r0, =GeneralMOSBuffer+4
+        BL      PrintError
+        B       GOSUPV
+      ]
+
+
+DEFBRK
+      [ International
+        TEQP    pc, #0
+        MOV     r0, r0
+        MOV     r13, #0
+        LDR     r13, [r13, #BrkBf]
+        LDR     r0, [r13, #15*4]
+        LDR     r1, =GeneralMOSBuffer
+        MOV     r2, #?GeneralMOSBuffer
+        SWI     OS_ConvertHex8
+
+; r0 -> address
+
+        MOV     R4,R0
+        ADR     R0,KernelMessagesBlock+4
+        ADR     R1,BreakToken
+        LDR     R2,=GeneralMOSBuffer+16
+        MOV     R3,#256-16
+        SWI     XMessageTrans_Lookup
+        MOVVC   R0,R2
+        ADRVS   R0,BreakText
+        SWI     OS_Write0
+        SWI     OS_NewLine
+
+      |
+        SWI     OS_WriteS
+        = "Stopped at break point at &", 0
+        ALIGN
+        TEQP    pc, #0
+        MOV     r0, r0
+        MOV     r13, #0
+        LDR     r13, [r13, #BrkBf]
+        LDR     r0, [r13, #15*4]
+        LDR     r1, =GeneralMOSBuffer
+        MOV     r2, #?GeneralMOSBuffer
+        SWI     OS_ConvertHex8
+        SWI     OS_Write0
+        SWI     OS_NewLine
+      ]
+
+Quit_Code
+        SWI     OS_Exit
+
+      [ International
+
+BreakToken      =       "BreakPt:"
+BreakText       =       "Stopped at break point at &%0",0
+        ALIGN
+
+      ]
+
+PrintError
+        MOV    R12, R14
+        LDR    R10, [R0], #4
+        SWI    XOS_Write0
+        BVS    %FT02
+      [ :LNOT: International
+        SWI    XOS_WriteS
+        =     " (Error number &", 0
+        BVS    %FT02
+      ]
+        LDR    R1,=GeneralMOSBuffer
+        MOV    R2, #&E0
+        MOV    R0, R10
+        SWI    XOS_ConvertHex8       ; can't fail!
+01      LDRB   R1, [R0], #1
+        CMP    R1, #"0"
+        BEQ    %BT01
+        CMP    R1, #0
+        SUBEQ  R0, R0, #1
+      [ International                   ; We might not have any stack so ..
+        SUB    R11,R0, #1               ; R11 -> Error number
+        ADR    R0, KernelMessagesBlock+4
+        ADR    R1, PrintErrorString
+        MOV    R2,#0                    ; Don't copy message
+        SWI    XMessageTrans_Lookup
+        ADRVS  R2,PrintErrorString+4    ; If no MessageTrans point at english text.
+11
+        LDRB   R0,[R2],#1
+        CMP    R0,#32
+        BLT    %FT13
+        CMP    R0,#"%"
+        SWINE  XOS_WriteC
+        BVS    %FT13
+        BNE    %BT11
+
+        LDRB    R0,[R2],#1
+        CMP     R0,#32
+        BLT     %FT13                   ; Just in case the % is the last character !
+
+        CMP     R0,#"0"                 ; We only know about %0
+        BNE     %BT11
+
+12
+        LDRB    R0,[R11],#1              ; Print error number.
+        CMP     R0,#32
+        BLT     %BT11
+        SWI     XOS_WriteC
+        BVC     %BT12
+
+13
+      |
+        SUB    R0, R0, #1
+        SWI    XOS_Write0
+        SWIVC  XOS_WriteI+")"
+      ]
+        SWIVC  XOS_NewLine
+02      MOVS   PC, R12
+
+      [ International
+PrintErrorString
+        =     "Err: (Error number &%0)", 0
+        ALIGN
+      ]
+
+        LTORG
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Exception handling
+
+DumpyTheRegisters  ROUT
+        LDR     R1, [R0, -R0]         ; PC when exception happened
+        STR     R1, [R0, #(15-8)*4]   ; In the right slot now ...
+        TST     R1, #SVC_mode
+        STMEQIA R0, {R8-R14}^         ; user mode case done.
+        BEQ     UNDEF1
+
+        TST     R1, #1                ; SWI mode?
+        TSTNE   R1, #2
+        BNE     %FT02
+        ORR     R1, R1, #I_bit :OR: F_bit
+                                 ; keep interrupts off until handlers restored
+        TEQP    R1, #0                  ; get at registers
+        NOP
+        STMIA   R0, {R8-R14}
+        TEQP    PC, #SVC_mode :OR: I_bit :OR: F_bit
+        AND     R1, R1, #SVC_mode
+        EORS    R2, R1, #FIQ_mode       ; Was we in FIQ ? Zero if so
+        MOVEQ   R3, #IOC
+        STREQB  R2, [R3, #IOCFIQMSK]    ; Blow away all FIQ sources
+        B       UNDEF1
+
+02      STMIA   R0, {R8-R14}
+
+; ... and fall into
+
+UNDEF1  LDR     sp, =SVCSTK             ; Flatten superstack
+
+ [ ExceptionsAreErrors
+        MOV     R0, #0
+        LDR     R0, [R0, #ExceptionDump]
+        ADD     R1, R0, #10*4           ; point at dumped R10
+        LDMIA   R1, {R10-R12}           ; try and put back user registers
+        Push    "R10-R12"               ; for error handler to find on stack
+        LDR     R1, [R0, #15*4]
+        Push    R1
+
+        LDR     R0, BranchThroughZeroInstruction ; load the B RESET1 instr.
+        STR     R0, [R1, -R1]           ; and store it at zero again
+
+        BIC     R14, R14, #ARM_CC_Mask
+        LDR     R0, =GeneralMOSBuffer+128 ; so can do deviant sharing !
+
+       [ International
+        MOV     r10, #0
+        LDRB    r10, [r10, #ErrorSemaphore]
+        TEQ     r10, #0                 ; if ok to translate error
+        MOVEQ   r10, r14
+        BEQ     %FT10
+       ]
+
+        LDR     r11, [r14], #4          ; Copy error number
+        STR     r11, [r0], #4
+
+01      LDRB    r11, [r14], #1          ; Copy error string
+        STRB    r11, [r0], #1
+        CMP     r11, #"%"               ; Test for "%" near end of string
+        BNE     %BT01
+10
+        SUB     r1, r0, #1              ; point, ready for conversion
+
+        LDR     R2, =GeneralMOSBuffer+?GeneralMOSBuffer
+        SUB     r2, r2, r1              ; amount left in buffer
+
+        MOV     R0, #0
+        LDR     R0, [R0, #ExceptionDump]
+        LDR     R0, [R0, #15*4]         ; saved PC
+        BIC     R0, R0, #ARM_CC_Mask
+        SWI     XOS_ConvertHex8
+
+      [ International
+        MOV     r4, #0
+        LDRB    r4, [r4, #ErrorSemaphore]
+        TEQ     r4, #0
+        LDRNE   R0, =GeneralMOSBuffer+128
+        MOVEQ   R4, R0
+        MOVEQ   R0, R10
+        BLEQ    TranslateError_UseR4
+      |
+        LDR     R0, =GeneralMOSBuffer+128
+      ]
+
+        TEQP    PC, #IRQ_mode+I_bit
+        MOV     R1, #0
+        STR     R1, [R1, #IRQsema]
+        LDR     SPIRQ, =IRQSTK
+        SWI     OS_GenerateError
+   |
+        Push    R14
+        BL      DEFHAN
+        BL      DEFHN2
+        Pull    R0
+        TEQP    PC, #IRQ_mode
+        NOP
+        LDR     SPIRQ, =IRQSTK
+        BIC     R0, R0, #ARM_CC_Mask
+        SWI     OS_Write0
+        B       UNDEF3
+    ]
+
+        LTORG
+
+UNDEF ROUT
+        TEQP    pc, #F_bit :OR: I_bit :OR: SVC_mode ; FIQ off too
+        STR     R14, [R0, -R0]
+        MOV     R14, #0
+        LDR     R14, [R14, #ExceptionDump]
+        STMIA   R14!, {R0-R7}
+        MOV     R0, R14
+        BL      DumpyTheRegisters
+ [ ExceptionsAreErrors
+        MakeErrorBlock UndefinedInstruction
+ |
+        =       "Undefined instruction at ", 0
+        ALIGN
+ ]
+
+ABORTP ROUT
+        TEQP    pc, #F_bit :OR: I_bit :OR: SVC_mode ; FIQ off too
+        STR     R14, [R0, -R0]
+        MOV     R14, #0
+        LDR     R14, [R14, #ExceptionDump]
+        STMIA   R14!, {R0-R7}
+        MOV     R0, R14
+        BL      DumpyTheRegisters
+
+ [ ExceptionsAreErrors
+        MakeErrorBlock InstructionAbort
+ |
+        =       "Abort on instruction fetch at ", 0
+        ALIGN
+ ]
+
+ABORTD ROUT
+        TEQP    pc, #F_bit :OR: I_bit :OR: SVC_mode ; FIQ off too
+        STR     R14, [R0, -R0]
+        MOV     R14, #0
+        LDR     R14, [R14, #ExceptionDump]
+        STMIA   R14!, {R0-R7}
+        MOV     R0, R14
+        BL      DumpyTheRegisters
+ [ ExceptionsAreErrors
+        MakeErrorBlock DataAbort
+ |
+        =       "Abort on data transfer at ", 0
+        ALIGN
+ ]
+
+
+ADDREX ROUT
+        TEQP    pc, #F_bit :OR: I_bit :OR: SVC_mode ; FIQ off too
+        STR     R14, [R0, -R0]
+        MOV     R14, #0
+        LDR     R14, [R14, #ExceptionDump]
+        STMIA   R14!, {R0-R7}
+        MOV     R0, R14
+        BL      DumpyTheRegisters
+
+ [ ExceptionsAreErrors
+        MakeErrorBlock AddressException
+ |
+        =       "Address exception at ", 0
+        ALIGN
+ ]
+
+
+; Can branch through zero in any mode
+
+RESET1 ROUT
+
+        STR     R1, [R0, -R0]
+        MOV     R1, #0
+        LDR     R1, [R1, #ExceptionDump]
+        STMIA   R1, {R0-R15}
+        LDR     R0, [R0, -R0]
+        STR     R0, [R1, #1*4]
+        SWI     XOS_EnterOS
+
+ [ ExceptionsAreErrors
+        BL      UNDEF1
+        MakeErrorBlock BranchThrough0
+ |
+        LDR     sp, =SVCSTK
+        TEQP    PC, #IRQ_mode
+        LDR     SPIRQ, =IRQSTK
+        TEQP    PC, #0
+        BL      DEFHAN
+        BL      DEFHN2
+        SWI     OS_WriteS
+        =      "Entering Supervisor because of branch through 0", 0
+        B       UNDEF2
+ ]
+
+BranchThroughZeroInstruction
+ [ ProcessorVectors
+        LDR     PC, .+ProcVec_Branch0
+ |
+        B       .+(RESET1-0)
+ ]
+
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI to call the UpCall vector
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+DoAnUpCall ROUT
+
+        Push    lr                  ; better have one of these to pull later !
+        AND     r10, lr, #&F0000000 ; copy user flags (I_bit clear)
+        TEQP    r10, #SVC_mode      ; ints on, stay in SVC mode, flags in psr
+        MOV     r10, #UpCallV
+        BL      CallVector
+        Pull    lr
+        BIC     lr, lr, #&F0000000
+        MOV     R10, PC, LSR #(32-4)
+        ORR     lr, lr, R10, LSL #(32-4)
+        ExitSWIHandler
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_ChangeEnvironment: Call the environment change vector
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ChangeEnvironment ROUT
+
+        Push    lr
+        MOV     R10, #ChangeEnvironmentV
+        BL      CallVector
+        Pull    lr
+        B       SLVK_TestV
+
+
+; ..... and the default handler .....
+
+AdjustOurSet
+        TEQP    PC, #SVC_mode+I_bit
+        CMP     R0, #MaxEnvNumber
+        BHI     AOS_Silly
+
+  [ False
+        CMP     r0, #CallBackHandler
+        BLEQ    testcallbackpending
+  ]
+
+        ADR     R10, AOS_Table
+        ADD     R11, R0, R0, LSL #1   ; number * 3
+        ADD     R10, R10, R11, LSL #2 ; point at entry
+
+        MOV     R12, R1
+        LDR     R11, [R10]
+        CMP     R11, #0
+        LDRNE   R1,  [R11]
+        CMPNE   R12, #0
+        STRNE   R12, [R11]
+
+        MOV     R12, R2
+        LDR     R11, [R10, #4]
+        CMP     R11, #0
+        LDRNE   R2,  [R11]
+        CMPNE   R12, #0
+        STRNE   R12, [R11]
+
+        MOV     R12, R3
+        LDR     R11, [R10, #8]
+        CMP     R11, #0
+        LDRNE   R3,  [R11]
+        CMPNE   R12, #0
+        STRNE   R12, [R11]
+
+        Pull    pc,,^
+
+AOS_Silly
+        ADR     r0, ErrorBlock_BadEnvNumber
+      [ International
+        BL      TranslateError
+      ]
+exit_AOS
+        Pull    "lr"
+        ORRS    pc, lr, #V_bit
+        MakeErrorBlock BadEnvNumber
+
+   [ False
+
+testcallbackpending
+        CMP     r1, #0                ; OK if only reading
+        CMPEQ   r2, #0
+        CMPEQ   r3, #0
+        LDRNEB  r10, [r0, #CallBack_Flag-CallBackHandler]
+        TSTNE   r10, #CBack_OldStyle
+        MOVEQ   pc, r14
+  SetBorder r0, r14, 15, 0, 0
+        ADR     r0, ErrorBlock_CallbackPending
+        B       exit_AOS
+        MakeErrorBlock CallbackPending
+   ]
+
+AOS_Table
+        &  MemLimit     ;  MemoryLimit
+        &  0
+        &  0
+
+        &  UndHan       ; UndefinedHandler
+        &  0
+        &  0
+
+        &  PAbHan       ; PrefetchAbortHandler
+        &  0
+        &  0
+
+        &  DAbHan       ; DatabortHandler
+        &  0
+        &  0
+
+        &  AdXHan       ; AddressExceptionHandler
+        &  0
+        &  0
+
+        &  0            ; OtherExceptionHandler
+        &  0
+        &  0
+
+        &  ErrHan       ; ErrorHandler
+        &  ErrHan_ws
+        &  ErrBuf
+
+        &  CallAd       ; CallBackHandler
+        &  CallAd_ws
+        &  CallBf
+
+        &  BrkAd        ; BreakPointHandler
+        &  BrkAd_ws
+        &  BrkBf
+
+        &  EscHan       ; EscapeHandler
+        &  EscHan_ws
+        &  0
+
+        &  EvtHan       ; EventHandler
+        &  EvtHan_ws
+        &  0
+
+        &  SExitA       ; ExitHandler
+        &  SExitA_ws
+        &  0
+
+        &  HiServ       ; UnusedSWIHandler
+        &  HiServ_ws
+        &  0
+
+        &  ExceptionDump ; ExceptionDumpArea
+        &  0
+        &  0
+
+        &  AplWorkSize  ; application space size
+        &  0
+        &  0
+
+        &  Curr_Active_Object
+        &  0
+        &  0
+
+        &  UpCallHan
+        &  UpCallHan_ws
+        &  0
+
+        assert (.-AOS_Table)/12 = MaxEnvNumber
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_ReadDefaultHandler
+
+; In    r0 = environment number
+
+ReadDefaultHandler ROUT
+
+        CMP     r0, #(dhte-defhantab)/12
+        MOVHS   r0, #0                  ; return wally value
+        ADD     r10, r0, r0, LSL #1     ; *3
+        ADD     r10, pc, r10, LSL #2    ; *4 (pc = defhantab-4)
+        LDMIB   r10, {r1-r3}            ; gives additional +4
+        ExitSWIHandler
+
+defhantab
+     & 0                ; wally entry
+     & 0
+     & 0
+
+     & UNDEF            ; UndefinedHandler
+     & 0
+     & 0
+
+     & ABORTP           ; PrefetchAbortHandler
+     & 0
+     & 0
+
+     & ABORTD           ; DataAbortHandler
+     & 0
+     & 0
+
+     & ADDREX           ; AddressExceptionHandler
+     & 0
+     & 0
+
+     & 0                ; OtherExceptionHandler
+     & 0
+     & 0
+
+     & ERRORH           ; ErrorHandler
+     & 0
+     & GeneralMOSBuffer
+
+     & NOCALL           ; CallBackHandler
+     & 0
+     & DUMPER
+
+     & DEFBRK           ; BreakPointHandler
+     & 0
+     & DUMPER
+
+     & ESCAPH           ; EscapeHandler
+     & 0
+     & GeneralMOSBuffer
+
+     & EVENTH           ; EventHandler
+     & 0
+     & 0
+
+     & CLIEXIT          ; ExitHandler
+     & 0
+     & 0
+
+     & NoHighSWIHandler ; UnusedSWIHandler
+     & 0
+     & 0
+
+     & 0                ; exception dump
+     & 0
+     & 0
+
+     & 0                ; app space size
+     & 0
+     & 0
+
+     & 0                ; cao pointer
+     & 0
+     & 0
+
+     & DefUpcallHandler ; upcall handler
+     & 0
+     & 0
+
+dhte
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r0 = sysinfo handle
+
+; Out   r0 = sysinfo for r0in
+
+ReadSysInfo_Code ROUT
+        CMP     r0, #1
+        BCC     %FT00                   ; r0 = 0
+        BEQ     %FT10                   ; r0 = 1
+        CMP     r0, #3
+        BCC     %FT20                   ; r0 = 2
+        BEQ     %FT30                   ; R0 = 3
+        CMP     r0, #5
+        BCC     %FT40                   ; R0 = 4
+        BEQ     %FT50                   ; R0 = 5
+
+        ;       R0 > 5, so illegal value
+
+        ADR     r0, ErrorBlock_BadReadSysInfo
+      [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      ]
+        ORR     lr, lr, #V_bit
+        ExitSWIHandler
+
+        MakeErrorBlock BadReadSysInfo
+
+; ReadSysInfo(0) - return configured screensize in r0
+
+00
+        Push    "r1, r2, lr"
+        MOV     r0, #ReadCMOS
+        MOV     r1, #ScreenSizeCMOS
+        SWI     XOS_Byte
+        AND     r0, r2, #&7F            ; top bit is reserved
+        MOV     r10, #0
+        LDR     r10, [r10, #Page_Size]
+        MUL     r0, r10, r0
+        BL      MassageScreenSize       ; adjust for min and max or default values
+        Pull    "r1, r2, lr"
+        ExitSWIHandler
+
+; ReadSysInfo(1) - returns configured mode/wimpmode in r0
+;                          configured monitortype in r1
+;                          configured sync in r2
+;                          (all de-autofied)
+
+10
+        Push    "r3-r5, lr"
+        BL      Read_Configd_Sync
+        MOV     r2, r0
+ [ ModeSelectors
+        LDR     r1, =VduDriverWorkSpace+CurrentMonitorType      ; read current monitortype
+        LDR     r1, [r1]
+ |
+        BL      Read_Configd_MonitorType
+        MOV     r1, r0
+ ]
+        BL      Read_Configd_Mode
+        CMP     r0, #-1                         ; if none of the three are auto, don't bother with translation
+        CMPNE   r1, #-1
+        CMPNE   r2, #-1
+        BNE     %FT15
+        BL      TranslateMonitorLeadType        ; issue service or work it out ourselves
+        CMP     r0, #-1                         ; if mode auto
+        MOVEQ   r0, r3                          ; then replace with default
+        CMP     r1, #-1                         ; if monitortype auto
+        MOVEQ   r1, r4                          ; then replace with default
+        CMP     r2, #-1                         ; if sync auto
+        MOVEQ   r2, r5                          ; then replace with default
+15
+        Pull    "r3-r5, lr"
+        ExitSWIHandler
+
+; ReadSysInfo(2)
+;
+; in:   r0 = 2
+;
+; out:  r0 = hardware configuration word 0
+;               bits 0-7 = special functions chip type
+;                               0 => none
+;                               1 => IOEB
+;               bits 8-15 = I/O control chip type
+;                               0 => IOC
+;                               1 => IOMD
+;               bits 16-23 = memory control chip type
+;                               0 => MEMC1/MEMC1a
+;                               1 => IOMD
+;               bits 24-31 = video control chip type
+;                               0 => VIDC1a
+;                               1 => VIDC20
+;       r1 = hardware configuration word 1
+;               bits 0-7 = I/O chip type
+;                               0 => absent
+;                               1 => 82C710/711 or SMC'665 or similar
+;               bits 8-31 reserved (set to 0)
+;       r2 = hardware configuration word 2
+;               bits 0-7 = LCD controller type
+;                               0 => absent
+;                               1 => present (type 1) eg A4 portable
+;                               2 => present (type 2) eg Stork portable
+ [ MorrisSupport
+;               bits 8-15 = IOMD variant
+;                               0 => IOMD
+;                               1 => IOMDL ie ARM7500 (Morris)
+;               bits 16-23 = VIDC20 variant
+;                               0 => VIDC20
+;                               1 => VIDC2L ie ARM7500 (Morris)
+;               bits 24-31 reserved (set to 0)
+ |
+;               bits 8-31 reserved (set to 0)
+ ]
+;       r3 = word 0 of unique machine ID, or 0 if unavailable
+;       r4 = word 1 of unique machine ID, or 0 if unavailable
+
+; Bits in IOSystemType
+
+IOST_IOEB       * 1     ; On IOMD systems this really means IOMD.
+IOST_82C710     * 2
+IOST_LC         * 4
+IOST_82C711     * 8
+IOST_37C665     * 16
+ [ MorrisSupport
+IOST_7500       * 32    ;Running on ARM7500 (Morris) so actually IOMDL and VIDC2L
+IOST_BATMAN     * 64    ;Stork keyboard/battery controller seems to be present
+ ]
+20
+        MOV     r0, #0
+        LDR     r3, [r0, #RawMachineID+0]
+        LDR     r4, [r0, #RawMachineID+4]
+        MOV     r3, r3, LSR #8                  ; lose first 8 bits
+        ORR     r3, r3, r4, LSL #24             ; and put bits 0..7 of r3 into bits 24..31 of r2
+        MOV     r4, r4, LSL #8                  ; lose CRC bits
+        MOV     r4, r4, LSR #16                 ; and move the rest down to the bottom
+        LDRB    r0, [r0, #IOSystemType]
+
+        ANDS    r2, r0, #IOST_LC
+        MOVNE   r2, #1                          ; make r2 0 or 1
+ [ MorrisSupport
+        TST     r0, #IOST_BATMAN
+        MOVNE   r2, #2                          ;NE, its a Stork portable
+        TST     r0, #IOST_7500
+        ORRNE   r2, r2, #&00000100              ;NE, Morris based machine with IOMDL
+        ORRNE   r2, r2, #&00010000              ;NE, and VIDC2L
+ ]
+        ANDS    r1, r0, #IOST_82C710 :OR: IOST_82C711 :OR: IOST_37C665
+        MOVNE   r1, #1                          ; make r1 0 or 1
+
+ [ IO_Type = "IOMD"
+   [ VIDC_Type = "VIDC20"
+        LDR     r0, =&01010100
+   |
+        LDR     r0, =&00010100
+   ]
+ |
+        ASSERT  IOST_IOEB = 1
+        AND     r0, r0, #IOST_IOEB              ; and r0 0 or 1
+   [ VIDC_Type = "VIDC20"
+        ORR     r0, r0, #&01000000
+   ]
+ ]
+        ExitSWIHandler
+
+; ReadSysInfo(3)
+;
+; in:   r0 = 3
+;
+; out:  r0 = I/O chip base features mask                710     711     665
+;               Bits 0..3   Base IDE type               1       1       1
+;               Bits 4..7   Base FDC type               1       1       1
+;               Bits 8..11  Base parallel type          1       1       1
+;               Bits 12..15 Base 1st serial type        1       1       1
+;               Bits 16..19 Base 2nd serial type        0       1       1
+;               Bits 20..23 Base Config type            1       2       3
+;               Bits 24..31 Reserved                    0       0       0
+;
+;       r1 = I/O chip extra features mask               710     711     665
+;               Bits 0..3   IDE extra features          0       0       0
+;               Bits 4..7   FDC extra features          0       0       0
+;               Bits 8..11  parallel extra features     0       0       1
+;               Bits 12..15 1st serial extra features   0       0       1
+;               Bits 16..19 2nd serial extra features   0       0       1
+;               Bits 20..23 config extra features       0       0       0
+;               Bits 24..31 Reserved                    0       0       0
+;
+;       r2-r4 undefined (reserved for future expansion)
+;
+
+30
+        MOV     r0, #0                          ; used as index and as default value
+        LDRB    r1, [r0, #IOSystemType]
+        TST     r1, #IOST_82C710
+        LDRNE   r0, =&00101111
+        MOVNE   r1, #0
+        TST     r1, #IOST_82C711
+        LDRNE   r0, =&00211111
+        MOVNE   r1, #0
+        TST     r1, #IOST_37C665
+        LDRNE   r0, =&00311111
+        LDRNE   r1, =&00011100
+        MOV     r2, #0
+        MOV     r3, #0
+        MOV     r4, #0
+        ExitSWIHandler
+
+; OS_ReadSysInfo 4 (SWI &58)
+;
+; On entry:       r0 = 4 (reason code)
+;
+; On exit:        r0 = LSW of Ethernet Network Address (or 0)
+;                 r1 = MSW of Ethernet Network Address (or 0)
+;
+; Use:            Code loaded from the dedicated Network Expansion Card or
+;                 from a normal Expansion Card should use the value returned
+;                 by this call in preference to a locally provided value.
+
+40
+        MOV     r0, #0
+        LDRB    r1, [ r0, #RawMachineID ]               ; The family byte
+        TEQ     r1, #&81                                ; Is this a custom part?
+        BNE     ExitNoEthernetAddress
+        LDR     r1, [ r0, #RawMachineID+4 ]             ; Acorn's ID and part#
+  [  True
+        BIC     r1, r1, #&FF000000                      ; Remove the CRC
+        LDR     r0, =&0050A4                            ; Acorn's ID is &005
+  | ; Version for no checking of Manufacture's ID
+        MOV     r1, r1, LSL #20
+        MOV     r1, r1, LSR #20                         ; Remove CRC and ID
+        LDR     r0, =&0A4
+  ]
+        TEQ     r1, r0                                  ; Is this one of our chips?
+        BNE     ExitNoEthernetAddress
+        MOV     r0, #0
+        LDR     r0, [ r0, #RawMachineID ]
+        MOV     r0, r0, LSR #8                          ; Lose family byte
+        LDR     r1, =&0000A4100000                      ; Base Ethernet address
+        ADD     r0, r1, r0                              ; Add Dallas part to base
+        MOV     r1, #0                                  ; Top 16 bits are zero
+        ExitSWIHandler
+
+ExitNoEthernetAddress
+        MOV     r0, #0
+        MOV     r1, #0
+        ExitSWIHandler
+
+; OS_ReadSysInfo 5
+;
+; On entry:       r0 = 5 (reason code)
+;
+; On exit:        r0 = LSW of Raw data from Dallas Chip
+;                 r1 = MSW of Raw data from Dallas Chip
+
+50
+        MOV     r0, #0
+        LDR     r1, [r0, #RawMachineID+4]
+        LDR     r0, [r0, #RawMachineID+0]
+        ExitSWIHandler
+
+        LTORG
+
+        END
diff --git a/s/ModHand b/s/ModHand
new file mode 100644
index 0000000000000000000000000000000000000000..90a9883615aec83552a8848c97cc7bd445f8731d
--- /dev/null
+++ b/s/ModHand
@@ -0,0 +1,2784 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL     => ModHand - the Relocatable Module Handler
+
+ExtraRMANeeded * 24*1024 ; Amount you get extra on top of what you configured
+
+
+; Test version, incorporating multiple incarnation attempt
+
+; The module handler needs to know the structure of the HPD, and nodes.
+; See RMTidy in particular.
+
+;**************************************************************
+;
+; Module chain structure: ModuleList points at a (singly-linked) list of
+; nodes with following fields:
+
+                        ^  0
+Module_chain_Link       #  4    ; the link to the next module info block
+Module_code_pointer     #  4    ; pointer to the module.
+Module_Hardware         #  4    ; hardware base for podules; 0 for soft loaders
+Module_incarnation_list #  4    ; pointer to list of incarnation specifiers
+Module_ROMModuleNode    #  4    ; pointer to ROM module node if in ROM (main, podule, extn), else zero
+
+ModInfo                 *  @
+
+; The incarnation list is a list of sub-nodes, one for each incarnation.
+
+                        ^  0
+Incarnation_Link        #  4   ; link to next incarnation
+Incarnation_Workspace   #  4   ; 4 private bytes for this life
+Incarnation_Postfix     #  0   ; postfix string starts here
+
+; Incarnations are distinguished by their postfix, which is separated
+; from the module name by a special character:
+
+Postfix_Separator       *  "%"
+
+;**************************************************************
+
+; Handler initialisation.
+; registers preserved
+
+; ROM module descriptor format
+
+                        ^       0
+ROMModule_Link          #       4               ; pointer to next node
+ROMModule_Name          #       4               ; pointer to module name (either directly in ROM, or in an RMA block)
+ROMModule_BaseAddress   #       4               ; start of module, if directly accessible
+ROMModule_Version       #       4               ; BCD version number, decimal point between bits 15,16 eg "1.23" => &00012300
+ROMModule_PoduleNumber  #       4               ; podule number (0..3 = normal podule, -1 = main ROM, -2..-n = extension ROM)
+ROMModule_ChunkNumber   #       4               ; chunk number if in podule or extension ROM, unused (?) if in main ROM
+ROMModule_OlderVersion  #       4               ; pointer to node holding the next older version of this module, 0 if none
+ROMModule_NewerVersion  #       4               ; pointer to node holding the next newer version of this module, 0 if none
+ROMModule_CMOSAddrMask  #       4               ; CMOS address of frugal bit (bits 0..15) and bit mask (16..23)
+                                                ; and 'initialised' flag in bit 24 (bits 25..31 = 0)
+ROMModule_Initialised   *       ROMModule_CMOSAddrMask + 3
+ROMModule_Size          #       4               ; size of module
+ROMModule_NodeSize      #       0
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+UnplugCMOSTable                         ; in reverse order
+        =       Unplug17CMOS
+        =       Unplug16CMOS, Unplug15CMOS
+        =       Unplug14CMOS, Unplug13CMOS
+        =       Unplug12CMOS, Unplug11CMOS
+        =       Unplug10CMOS, Unplug9CMOS
+        =       Unplug8CMOS, Unplug7CMOS
+        =       FrugalCMOS+1, FrugalCMOS+0
+        =       MosROMFrugalCMOS+3, MosROMFrugalCMOS+2
+        =       MosROMFrugalCMOS+1
+UnplugCMOSTableEnd                      ; used for backwards indexing
+        =       MosROMFrugalCMOS+0
+        =       0
+        ALIGN
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ModuleInit   ENTRY "r0-r12"                             ; call here on system startup
+
+        MOV     r0, #HeapReason_Init                    ; first initialise the heap
+        MOV     r1, #RMAAddress
+        LDR     r3, [r1, #:INDEX: hpdend]               ; saved for us during init.
+        SWI     XOS_Heap
+
+; first initialise the podule manager - this must be the second module (ie the 1st after UtilityModule)
+
+        ADRL    r6, SysModules_Info+4
+        LDR     r1, [r6, #-4]
+        ADD     r1, r6, r1
+        LDR     r14, [r1, #-4]
+        TEQ     r14, #0
+        MOVNE   r0, #ModHandReason_AddArea
+        SWINE   XOS_Module
+
+; now for each module in the main ROM, create a node for it
+
+        ASSERT  ROMModule_Link = 0
+
+        LDR     r9, =ROMModuleChain                     ; pointer to 'previous' node
+        MOV     r8, #0                                  ; initial head ptr is zero
+        STR     r8, [r9]                                ; set up null list
+        MOV     r3, #-1                                 ; podule -1 is main ROM
+        MOV     r10, #0                                 ; chunk number 0 to start
+10
+        LDR     r7, [r6, #-4]                           ; get size of this module
+        TEQ     r7, #0                                  ; if zero
+        BEQ     %FT20                                   ; then no more main rom modules
+
+        LDR     r4, [r6, #Module_Title]                 ; r4 = offset to module name
+        ADD     r4, r6, r4                              ; r4 -> module name
+        LDR     r5, [r6, #Module_HelpStr]               ; r5 = help offset
+        TEQ     r5, #0                                  ; if no help string
+        ADDEQ   r5, r6, #Module_HelpStr                 ; then use help offset as string (null string)
+        ADDNE   r5, r6, r5                              ; otherwise point to help string
+
+        ADR     r11, UnplugCMOSTable
+        SUBS    r14, r10, #FirstUnpluggableModule       ; subtract number of first module that has an unplug bit
+        MOVCS   r1, r14, LSR #3                         ; get byte number
+        ANDCS   r14, r14, #7                            ; get bit number
+        ADDCS   r14, r14, #16                           ; bit mask stored in bits 16 onwards
+        RSBCSS  r1, r1, #(UnplugCMOSTableEnd-UnplugCMOSTable) ; invert table offset, and check in range
+        LDRCSB  r11, [r11, r1]                          ; load table value if in range
+        MOVCS   r12, #1
+        ORRCS   r11, r11, r12, LSL r14                  ; merge with bit mask
+        MOVCC   r11, #0                                 ; otherwise zero
+
+        BL      AddROMModuleNode
+        BVS     %FT50                                   ; if failed then can't add any more ROMs!
+
+        MOV     r9, r2                                  ; this node is now previous one
+        ADD     r6, r6, r7                              ; go on to next module
+        ADD     r10, r10, #1                            ; chunk number +=1
+        B       %BT10
+
+; now do podule ROMs
+
+20
+        MOV     r3, #0                                  ; start at podule 0
+21
+        MOV     r10, #0                                 ; for each podule start at chunk 0
+        CMP     r3, #-1
+        MOVGT   r12, #0                                 ; if real podule then start at CMOS bit number 0 for this podule
+                                                        ; else carry on from where we're at
+22
+        MOV     r0, r10
+        SWI     XPodule_EnumerateChunksWithInfo
+        BVS     %FT40                                   ; bad podule or some such
+        CMP     r0, #0                                  ; no more chunks?
+        BEQ     %FT45                                   ; then step to next podule
+        CMP     r2, #OSType_Module
+        MOVNE   r10, r0
+        BNE     %BT22
+
+; now claim a block to copy module title into
+
+        MOV     r7, r1                                  ; pass size in r7
+        Push    "r0, r3, r4"
+        MOV     r3, #0
+23
+        LDRB    r14, [r4, r3]                           ; find length of title string
+        ADD     r3, r3, #1                              ; increment length (include zero at end)
+        TEQ     r14, #0
+        BNE     %BT23
+
+        BL      ClaimSysHeapNode
+        Pull    "r0, r3, r14"                           ; restore chunk no., podule no., old ptr to title
+        BVS     %FT50                                   ; if error then no more ROMs (doesn't matter that error ptr is naff)
+
+        MOV     r4, r2                                  ; save pointer to block
+24
+        LDRB    r1, [r14], #1                           ; now copy string into block
+        STRB    r1, [r2], #1
+        TEQ     r1, #0
+        BNE     %BT24
+
+        MOV     r14, #(1 :SHL: 16)                      ; bit mask ready to shift
+        CMP     r3, #-1
+        BLT     %FT30
+
+; doing podule ROM
+
+ [ IO_Type = "IOMD"
+
+        ASSERT  ?PoduleFrugalCMOS = 8                   ; ensure we're using the correct Hdr:CMOS
+        CMP     r12, #7                                 ; if bit number <= 7
+        CMPLS   r3, #8                                  ; then if podule number <= 8
+        ADDCC   r11, r3, #PoduleFrugalCMOS              ;      then use one of the 8 PoduleFrugalCMOS bytes
+        MOVEQ   r11, #NetworkFrugalCMOS                 ;      elif podule number = 8 then use network card CMOS
+        MOVHI   r11, #0                                 ; otherwise no CMOS
+        ORRLS   r11, r11, r14, LSL r12                  ; OR in bit mask
+        B       %FT36
+ |
+        ASSERT  ?PoduleFrugalCMOS >= 4
+        CMP     r3, #4                                  ; if podule number < 4
+        CMPCC   r12, #8                                 ; and bit number < 8
+        ADDCC   r11, r3, #PoduleFrugalCMOS              ; then compute CMOS address
+        ORRCC   r11, r11, r14, LSL r12                  ; and OR in bit mask
+ ]
+        B       %FT35
+
+; doing extension ROM
+30
+        CMP     r12, #16                                ; 2 bytes of CMOS for extension ROMs
+        MOVCC   r1, #ExtnUnplug1CMOS                    ; form CMOS address in r1
+        ADDCC   r1, r1, r12, LSR #3
+        ANDCC   r11, r12, #7                            ; get bit mask
+        ORRCC   r11, r1, r14, LSL r11                   ; and OR in
+35
+        MOVCS   r11, #0                                 ; if out of range then no CMOS
+36
+        ADD     r12, r12, #1                            ; increment bit
+        BL      AddROMModuleNode
+        BVS     %FT50
+
+        MOV     r10, r0                                 ; go onto next chunk
+        MOV     r9, r2                                  ; this node is now previous one
+        B       %BT22
+
+40
+        CMP     r3, #0                                  ; are we doing extension ROMs
+        BMI     %FT50                                   ; if so, then stop if we get an error
+45
+        TEQ     r3, #0                                  ; if doing extension ROMs
+        SUBMI   r3, r3, #1                              ; then go backwards
+        BMI     %BT21
+        ADD     r3, r3, #1                              ; go onto next podule
+        CMP     r3, #16                                 ; more podules than you could ever fit
+        MOVEQ   r3, #-2                                 ; if got to end, try extension ROMs
+        MOVEQ   r12, #0                                 ; start by using bit 0 of CMOS
+        B       %BT21
+
+; now go down ROM module chain, initialising things
+
+50
+        LDR     r12, =ROMModuleChain
+        LDR     r12, [r12]
+55
+        TEQ     r12, #0                                 ; if no more modules
+        BEQ     %FT90                                   ; then skip
+
+        LDR     r3, [r12, #ROMModule_CMOSAddrMask]      ; get CMOS for LOCATION version
+        ANDS    r2, r3, #&FF
+        MOVNE   r1, r2
+        MOVNE   r0, #ReadCMOS                           ; if there is a CMOS address
+        SWINE   XOS_Byte                                ; then read it
+        TST     r2, r3, LSR #16                         ; test bit
+        BNE     %FT80                                   ; [LOCATION unplugged, so don't initialise here]
+
+        MOV     r11, r12                                ; start with current one
+
+; now find the newest version that isn't unplugged
+
+; first find the newest version
+
+60
+        LDR     r14, [r11, #ROMModule_NewerVersion]
+        TEQ     r14, #0                                 ; if there is a newer version
+        MOVNE   r11, r14                                ; then link to it
+        BNE     %BT60                                   ; and loop
+
+; now work backwards until we find a version that isn't unplugged - there must be one, since LOCATION version is not unplugged
+
+65
+        TEQ     r11, r12                                ; back to LOCATION version?
+        BEQ     %FT68                                   ; [yes, so use that version]
+        LDR     r3, [r11, #ROMModule_CMOSAddrMask]      ; get CMOS for CODE version
+        ANDS    r2, r3, #&FF
+        MOVNE   r1, r2
+        MOVNE   r0, #ReadCMOS                           ; if there is a CMOS address
+        SWINE   XOS_Byte                                ; then read it
+        TST     r2, r3, LSR #16                         ; test bit
+        LDRNE   r11, [r11, #ROMModule_OlderVersion]     ; CODE is unplugged, so try next older version
+        BNE     %BT65
+
+68
+        LDR     r7, [r12, #ROMModule_PoduleNumber]      ; get podule number (for LOCATION version)
+        CMP     r7, #-1                                 ; is it an extension ROM
+        BGE     %FT70                                   ; if not then initialise newer one
+
+; it's an extension ROM, so only initialise if it's the newest, and hasn't yet been initialised
+
+        TEQ     r11, r12
+        LDREQB  r10, [r11, #ROMModule_Initialised]      ; only initialise if this is zero and r11=r12
+        TEQEQ   r10, #0
+        BNE     %FT80                                   ; don't initialise
+
+; not an extension ROM, so initialise the newest version (r11) of this module
+
+70
+ [ DebugROMInit
+        SWI     XOS_WriteS
+        =       "About to initialise module "
+        LDR     r0, [r11, #ROMModule_Name]
+        SWI     XOS_Write0
+        SWI     XOS_NewLine
+ ]
+        BL      InitialiseROMModule
+ [ DebugROMErrors                                       ; print errors in ROM module init for debugging
+        BVC     %FT80
+        SWI     XOS_WriteS
+        =       "Error in ROM module init: ",0
+        ALIGN
+        ADDVC   r0, r0, #4
+        SWIVC   XOS_Write0
+        SWIVC   XOS_NewLine
+ ]
+80
+        LDR     r12, [r12, #ROMModule_Link]
+        B       %BT55
+
+90
+        MOV     r1, #RMASizeCMOS
+        MOV     r0, #ReadCMOS
+        SWI     XOS_Byte
+        MOV     r0, #0
+        LDR     r0, [r0, #Page_Size]
+        MUL     r3, r0, r2
+        ADD     r3, r3, #ExtraRMANeeded
+        MOV     r0, #ModHandReason_Claim
+        SWI     XOS_Module
+        MOV     r0, #ModHandReason_Free
+        SWI     XOS_Module
+        EXIT
+
+;******************************************************************************************************
+;
+;       InitialiseROMModule - Initialise a ROM module
+;
+; in:   r11 -> ROM module node for CODE version
+;       r12 -> ROM module node for LOCATION version
+;
+; out:  All registers preserved
+;
+
+InitialiseROMModule ENTRY "r0-r12"
+        MOV     r14, #1
+        STRB    r14, [r11, #ROMModule_Initialised]      ; indicate it's been initialised
+        LDR     r2, [r11, #ROMModule_ChunkNumber]
+        LDR     r3, [r11, #ROMModule_PoduleNumber]
+        LDR     r4, [r11, #ROMModule_Name]
+        LDR     r6, [r11, #ROMModule_BaseAddress]
+        LDR     r7, [r12, #ROMModule_PoduleNumber]
+        ADRL    r1, crstring
+        MOV     lr, pc                                  ; ADRS lr, %FT10
+        ADD     lr, lr, #%FT20-%FT10                    ; ADRS lr, %FT20
+10
+        Push    "r0-r7,r9,lr"
+        LDR     r1, [r11, #ROMModule_Size]
+        MOV     r5, r11                                 ; r5 -> ROM module node
+        B       APMInitEntry
+20
+        STRVS   r0, [sp]                                ; if error, preserve r0
+        EXIT
+
+;******************************************************************************************************
+;
+;       AddROMModuleNode - Create a ROM module node and link it with the chain
+;
+; in:   R3 = podule number
+;       R4 -> module name
+;       R5 -> module help string
+;       R6 -> module base if directly executable, otherwise zero
+;       R7 = module size
+;       R8 = 0
+;       R9 -> previous node
+;       R10 = chunk number
+;       R11 = CMOS address (in bits 0..15) and bit mask (in bits 16..23) for unplugging (0 if none)
+;
+; out:  R2 -> node created
+;       All other registers preserved, except if error (when R0 -> error)
+;
+
+AddROMModuleNode ENTRY "r0,r1,r3-r12"
+        MOV     r3, #ROMModule_NodeSize                 ; claim a rom module node
+        BL      ClaimSysHeapNode                        ; r0,r1 corrupted, r2 -> block
+        STRVS   r0, [stack]
+        EXIT    VS
+
+        STR     r8, [r2, #ROMModule_Link]               ; set link for this node to 0
+        STR     r7, [r2, #ROMModule_Size]               ; store size in node
+        STR     r4, [r2, #ROMModule_Name]               ; store pointer to title string
+        STR     r6, [r2, #ROMModule_BaseAddress]        ; store base address
+        MOV     r0, r5
+        BL      GetVerNoFromHelpString                  ; read version number in BCD into r1
+        STR     r1, [r2, #ROMModule_Version]            ; store version number
+        LDR     r3, [stack, #2*4]                       ; reload podule number
+        STR     r3, [r2, #ROMModule_PoduleNumber]       ; store podule number
+        STR     r10, [r2, #ROMModule_ChunkNumber]       ; store chunk number
+        STR     r11, [r2, #ROMModule_CMOSAddrMask]      ; store CMOS address and mask
+
+; now check if module is a copy of one already on the list
+
+        MOV     r10, #0                                 ; next oldest node
+        MOV     r11, #0                                 ; next newest node
+        CMP     r3, #-1                                 ; if in main ROM, no need to look for duplicates
+        BEQ     %FT40
+
+        MOV     r1, r4                                  ; make r1 -> additional module's name
+        MOV     r4, #0                                  ; zero terminator for Module_StrCmp
+        MOV     r12, #0                                 ; search from start of chain
+        BL      FindROMModule
+        TEQ     r12, #0                                 ; did we find it?
+        BEQ     %FT40                                   ; no, then module is unique
+
+        TEQ     r6, #0                                  ; set r6 to 1 if extra is directly executable, otherwise 0
+        MOVNE   r6, #1
+        CMP     r3, #-1                                 ; set r3 to 1 if extra is an extension ROM, otherwise 0
+        MOVGE   r3, #0
+        MOVLT   r3, #1
+        LDR     r1, [r2, #ROMModule_Version]            ; reload version number of extra node
+        BL      CompareVersions                         ; compare r2 version with r12 version
+        BCC     %FT30                                   ; extra one is older than this one, so search down older chain
+
+; extra one is newer than this one, so search down newer chain
+
+20
+        MOV     r10, r12                                ; old = this
+        LDR     r12, [r12, #ROMModule_NewerVersion]     ; this = newer(this)
+        MOVS    r11, r12                                ; new = this
+        BEQ     %FT40                                   ; if no newer then that's it!
+        BL      CompareVersions
+        BCS     %BT20
+
+; extra one is older than this one, so search down older chain
+
+30
+        MOV     r11, r12                                ; new = this
+        LDR     r12, [r12, #ROMModule_OlderVersion]     ; this = older(this)
+        MOVS    r10, r12                                ; old = this
+        BEQ     %FT40
+        BL      CompareVersions
+        BCC     %BT30
+
+40
+        STR     r10, [r2, #ROMModule_OlderVersion]      ; older(extra)=old
+        STR     r11, [r2, #ROMModule_NewerVersion]      ; newer(extra)=new
+        TEQ     r10, #0                                 ; if old <> 0
+        STRNE   r2, [r10, #ROMModule_NewerVersion]      ; then newer(old)=extra
+        TEQ     r11, #0                                 ; if new <> 0
+        STRNE   r2, [r11, #ROMModule_OlderVersion]      ; then older(new)=extra
+
+        STR     r2, [r9]                                ; point previous node at this one
+        CLRV
+        EXIT
+
+CompareVersions ENTRY
+        LDR     r14, [r12, #ROMModule_Version]          ; r14 = version(this)
+        CMP     r1, r14
+        EXIT    NE                                      ; exit with this condition codes, unless equal
+        LDR     r14, [r12, #ROMModule_BaseAddress]
+        TEQ     r14, #0                                 ; set r14 to 1 if this one is directly executable, otherwise 0
+        MOVNE   r14, #1
+        CMP     r6, r14
+        EXIT    NE                                      ; directly executables are "newer"
+        LDR     r14, [r12, #ROMModule_PoduleNumber]
+        CMP     r14, #-1                                ; set r14 to 1 if ext. ROM, otherwise 0
+        MOVGE   r14, #0
+        MOVLT   r14, #1
+        CMP     r3, r14                                 ; extension ROMs are "newer" than anything else
+        EXIT                                            ; if equal in all other respects, the later one is "newer"
+
+;******************************************************************************************************
+;
+;       FindROMModule - Find a named module in the ROM module list
+;
+; in:   R1 -> name to match
+;       R4 = potential additional termintor for R1 string
+;       R12 -> node before 1st node to be checked (0 => search from start)
+;
+; out:  R12 -> found node, or 0 if no match
+;       If match, then R1 -> terminator of R1 string, otherwise preserved
+;       All other registers preserved
+;
+
+FindROMModule ENTRY
+        TEQ     r12, #0                                 ; if zero passed in on entry
+        LDREQ   r12, =ROMModuleChain                    ; then search from start of chain
+10
+        LDR     r12, [r12, #ROMModule_Link]             ; go to next module
+        TEQ     r12, #0                                 ; any more modules?
+        EXIT    EQ                                      ; no, then exit
+        Push    "r1, r3"
+        LDR     r3, [r12, #ROMModule_Name]              ; point to name of module on chain
+        BL      Module_StrCmp                           ; compare names
+        STREQ   r1, [sp]                                ; if match, then patch stacked r1
+        Pull    "r1, r3"
+        BNE     %BT10                                   ; if different then try next one
+        EXIT
+
+; start of module handler SWI
+
+     GBLA  mhrc
+mhrc SETA 0
+
+     MACRO
+$l   ModuleDispatchEntry $entry
+$l   B      Module_$entry
+     ASSERT ModHandReason_$entry = mhrc
+mhrc SETA   mhrc + 1
+     MEND
+
+ModuleHandler ROUT
+
+     CMP      r0, #(NaffSWI - (.+12))/4     ; Range check
+     ADDLO    pc, pc, r0, LSL #2            ; dispatch
+     B        NaffSWI
+
+     ModuleDispatchEntry Run
+     ModuleDispatchEntry Load
+     ModuleDispatchEntry Enter
+     ModuleDispatchEntry ReInit
+     ModuleDispatchEntry Delete
+     ModuleDispatchEntry RMADesc
+     ModuleDispatchEntry Claim
+     ModuleDispatchEntry Free
+     ModuleDispatchEntry Tidy
+     ModuleDispatchEntry Clear
+     ModuleDispatchEntry AddArea
+     ModuleDispatchEntry CopyArea
+     ModuleDispatchEntry GetNames
+     ModuleDispatchEntry ExtendBlock
+     ModuleDispatchEntry NewIncarnation
+     ModuleDispatchEntry RenameIncarnation
+     ModuleDispatchEntry MakePreferred
+     ModuleDispatchEntry AddPoduleModule
+     ModuleDispatchEntry LookupName
+     ModuleDispatchEntry EnumerateROM_Modules
+     ModuleDispatchEntry EnumerateROM_ModulesWithInfo
+
+NaffSWI                                     ; Set V and return
+        ADR     R0, ErrorBlock_BadModuleReason
+      [ International
+BumDealInModule_Translate
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      ]
+BumDealInModule
+        B       SLVK_SetV
+
+     MakeErrorBlock BadModuleReason
+
+;*************************************************************
+
+Module_Run      ROUT
+       TEQP     PC, #SVC_mode               ; interrupts on
+       Push    "R9, lr"
+       BL       Load_Module
+       BVS      LoadFailed
+  ;     BL       EnvStringSkipName - done in load
+EnterIt
+ ; R9 now ptr to node, R10 ptr to command string to set up.
+ ; Enters preferred incarnation.
+
+       LDR      R12, [R9, #Module_incarnation_list]
+       ADD      R12, R12, #Incarnation_Workspace
+
+       LDR      R9, [R9, #Module_code_pointer]
+       LDR      R11, [R9, #Module_Start]
+       TEQ      R11, #0
+       Pull    "R9, lr", EQ
+       ExitSWIHandler EQ
+
+       Push    "R1-R3"
+       MOV      R1, R10
+       MOV      R0, #FSControl_StartApplication
+       MOV      R2, R9
+       LDR      R3, [R9, #Module_Title]     ; prefix with module title
+       ADD      R3, R3, R9
+
+       SWI      XOS_FSControl
+       BVS      CantGoIntoModule
+
+       LDR      stack, =SVCSTK
+       MOV      R0, R10
+       TEQP     pc, #0
+       MOV      r0, r0                      ; NOP because we've changed mode
+
+       TST      R11, #ARM_CC_Mask           ; check for B startit, etc.
+       MOVNE    R11, #0
+       ADD      PC, R9, R11
+
+CantGoIntoModule
+       Pull    "R1-R3"
+LoadFailed
+       Pull    "R9, lr"
+       B        BumDealInModule
+
+;*************************************************************
+
+Module_Load     ROUT
+        TEQP    PC, #SVC_mode               ; interrupts on
+        Push    "R9, lr"
+        BL      Load_Module
+        Pull    "R9, lr"
+        B       SLVK_TestV
+
+;*************************************************************
+
+Module_Enter    ROUT
+       Push    "R9, lr"                     ; ready for EnterIt
+       Push    "R0-R4"
+       BL       lookup_commoned
+
+       STRVS    R0, [stack]
+       Pull    "R0-R4, R9, lr", VS
+       BVS      BumDealInModule
+
+       BLGT     PreferIncarnation
+       Pull    "R0-R4"
+       MOV      R10, R2                     ; envstring pointer
+       B        EnterIt
+
+;*************************************************************
+
+Module_ReInit   ROUT
+       Push    "R0-R4, R9, lr"
+
+       BL       lookup_commoned
+       BVS      %FT01
+
+       ADDEQ    R3, R9, #Module_incarnation_list
+       LDREQ    R12, [R9, #Module_incarnation_list]
+
+ ;    R12 -> incarnation node, R3  -> previous incarnation
+
+       MOV      R10, #1                     ; fatal die
+       BL       CallDie
+       BVS      %FT03
+
+       SUB      R10, R1, #1
+       BL       EnvStringSkipName
+
+       BL       CallInit
+       BLVS     LoseModuleSpace_if_its_the_only_incarnation
+       STRVC    R12, [R3, #Incarnation_Link]
+03     STRVS    R0, [stack]
+       Pull    "R0-R4, R9, lr"
+        B       SLVK_TestV
+
+
+01     LDR      R11, [R0]
+       LDR      R2, =ErrorNumber_RMNotFound
+       CMP      R11, R2
+       BEQ      %FT02
+05
+       SETV
+       B        %BT03
+
+02     MOV      R0, #0
+       BL       AddModuleIfInROM
+       B        %BT03
+
+;*************************************************************
+
+Module_Delete   ROUT
+       Push    "R0-R4, R9, lr"
+       BL       lookup_commoned
+       BVS      %FT01
+
+       ADDEQ    R3, R9, #Module_incarnation_list
+       LDREQ    R12, [R9, #Module_incarnation_list]
+
+ ;    R12 -> incarnation node, R3  -> previous incarnation
+
+       BL       KillIncarnation
+01     STRVS    R0, [stack]
+       Pull    "R0-R4, R9, lr"
+        B       SLVK_TestV
+
+;*************************************************************
+
+Module_Free      ROUT
+Module_RMADesc
+         Push   "R0, R1, lr"
+
+         SUB     R0, R0, #(ModHandReason_RMADesc-HeapReason_Desc)
+ ASSERT HeapReason_Desc-HeapReason_Free=ModHandReason_RMADesc-ModHandReason_Free
+         MOV     R1, #RMAAddress
+         SWI     XOS_Heap
+         STRVS   R0, [stack]
+         Pull   "R0, R1, lr"
+        B       SLVK_TestV
+
+;*************************************************************
+
+Module_Claim  ROUT
+         Push   "R0, R1, lr"
+         BL      RMAClaim_Chunk
+         STRVS   R0, [stack]
+         Pull   "R0, R1, lr"
+        B       SLVK_TestV
+
+;*************************************************************
+; Garbage collect the RMA. We know there's always one module,
+; and some RMA space.
+
+ [ RMTidyDoesNowt                               ; on Medusa we do nothing, because we would always fail
+                                                ; due to FSLock being Captain Scarlet
+Module_Tidy *   SLVK
+ |
+Module_Tidy      ROUT
+         Push   "R0-R6, R9, lr"
+
+         TEQP    PC, #SVC_mode
+
+         MOV     r0, #0
+         LDR     r0, [r0, #Curr_Active_Object]
+         MOV     r1, #RMAAddress
+         LDR     r2, [r1, #:INDEX:hpdend]
+         SUBS    r0, r0, r1
+         CMPGT   r2, r0
+         ADRGTL  r0, ErrorBlock_CantKill
+       [ International
+         BLGT    TranslateError
+       |
+         SETV    GT
+       ]
+
+         BLVC    Genocide                   ; warn all of impending calamity
+         BVS     ExitRMTidy
+
+; now for the great adventure. R2 is old contents of ModuleList
+; First build a list of block addresses, together with address of pointer to
+; block, in ascending order.
+
+         LDR     R2, =ScratchSpace
+         STR     R9, [R2], #4               ; save original module list
+         MOV     R3, R2
+
+; in loop, R0 is block address, R1 is pointer to pointer to block
+; R2 is ptr to list start
+; R3 is list limit
+
+01      ADD      R1, R9, #Module_code_pointer
+        LDR      R0, [R1]
+        BL       %FT10                      ; insert pair
+
+        LDR      R1, [R9, #Module_incarnation_list]
+02      ADD      R1, R1, #Incarnation_Workspace
+        LDR      R0, [R1]
+        CMP      R0, #0
+        BLNE     %FT10                      ; insert workspace block if there
+        LDR      R1, [R1, #-Incarnation_Workspace]
+        CMP      R1, #0
+        BNE      %BT02
+
+        LDR      R9, [R9, #Module_chain_Link] ; next module
+        CMP      R9, #0
+        BNE      %BT01
+
+; Now iterate over claimed blocks, to discard non-heap pointers.
+
+        MOV      R5, R2                     ; currblock ptr
+
+; if hpdfree <> hpdsize then
+;    doblock (hpdsize, hpdfree=Nil -> hpdbase, hpdfree)
+
+        MOV      R12, #RMAAddress
+        LDR      R4, [R12, #:INDEX: hpdfree]
+        CMP      R4, #Nil
+        ADDNE    R4, R4, #:INDEX: hpdfree   ; convert to heapstart offset
+
+        CMP      R4, #hpdsize
+        BEQ      %FT04
+        MOV      R0, #hpdsize
+        CMP      R4, #Nil
+        LDREQ    R1, [R12, #:INDEX: hpdbase]
+        MOVNE    R1, R4
+        BL       ScanAllocBlock
+        CMP      R4, #Nil
+        BEQ      BlocksScanned
+
+;  while hpdfree <> Nil
+;     doblock (hpdfree+fresize, step(hpdfree)=Nil -> hpdbase, hpdfree)
+
+04      ADD      R4, R4, R12
+        LDR      R1, [R4, #frelink]
+        LDR      R0, [R4, #fresize]
+        SUB      R4, R4, R12
+
+        ADD      R0, R0, R4
+        CMP      R1, #Nil
+        ADDNE    R4, R4, R1
+        MOVNE    R1, R4
+        LDREQ    R1, [R12, #:INDEX: hpdbase]
+        BL       ScanAllocBlock
+        BNE      %BT04
+
+BlocksScanned
+        MOV      R3, R5                     ; new list end
+
+; copy blocks, relocate ptrs
+; R2, R3 list limits
+; R12 heap start
+
+        ADD      R0, R12, #hpdsize          ; get addr for first block
+StripBlocks
+        CMP      R2, R3
+        BEQ      %FT09                      ; nowt to copy
+        LDR      R1, [R2], #8
+        CMP      R1, #0
+        BEQ      StripBlocks
+        LDR      R4, [R1]                   ; block size
+        CMP      R1, R0
+        ADDEQ    R0, R4, R0
+        BEQ      StripBlocks
+
+; R1 address of first block, R0 address to copy to - gopher it!
+; R4 size
+        LDR      R5, [R2, #-4]              ; pointer to
+        ADD      R0, R0, #4
+        STR      R0, [R5]                   ; relocate ptr
+        SUB      R0, R0, #4
+
+CopyBlock
+        LDR      R5, [R1], #4
+        STR      R5, [R0], #4
+        SUBS     R4, R4, #4
+        BGT      CopyBlock
+        B        StripBlocks
+
+09
+; Update Hpd
+        SUB      R0, R0, R12                ; convert to offset
+        STR      R0, [R12, #:INDEX: hpdbase]
+
+        MOV      R0, #Nil
+        STR      R0, [R12, #:INDEX: hpdfree] ; no free list.
+
+ ; for restarting, we need
+ ; R1 -> prevmod to R9
+ ; R9 -> the whinger, R12 -> incarnation list, R2 stop point
+ ; R4 -> dead module list
+ ; R3 -> previnc
+
+        MOV      R9, #Module_List           ; "module" that's linked at end
+        LDR      R4, =ScratchSpace
+        LDR      R4, [R4]                   ; dead list
+        MOV      R2, #0                     ; persuade it to step module
+        MOV      R12, #0                    ; immediately.
+        BL       RestartModuleStructure
+        BVS      ExitRMTidy
+
+        MOV      R0, #1
+        MOV      R1, #-16*1024*1024
+        SWI      XOS_ChangeDynamicArea
+        CLRV
+
+ExitRMTidy
+        STRVS    R0, [stack]
+        Pull    "R0-R6, R9, lr"
+        B       SLVK_TestV
+
+;-------------------------------------------------------------------------
+; RMTidy support routines
+
+; Insertion routine. (R0, 1) is pair to insert, R2 list start, R3 list end.
+; Uses R10, 11, 4
+
+10      MOV      R11, R2           ; take curr posn ptr
+
+        SUB      R0, R0, #4        ; genuine internal heap pointer value
+11      CMP      R11, R3           ; list ended?
+        BEQ      %FT13
+        LDR      R4, [R11], #8
+        CMP      R4, R0            ; or right posn?
+        BLO      %BT11
+
+        SUB      R11, R11, #8      ; where we will store new entry to
+        SUB      R4, R3, #4        ; R4 is OS_Word to move from.
+12      LDR      R10, [R4], #-4    ; now copy between R11 and R3 up 8.
+        STR      R10, [R4, #12]
+        CMP      R4, R11
+        BHS      %BT12
+
+13      ADD      R3, R3, #8        ; update list end
+        STMIA    R11, {R0, R1}     ; new entry in
+        ADD      R0, R0, #4        ; and back to link.
+        MOV      PC, lr
+
+;-------------------------------------------------------------------
+ScanAllocBlock    ROUT
+ ; R0 block start
+ ; R1 block end
+ ; R12 heap start
+ ; R3  list end
+ ; R5  current list entry
+
+ ; poke out list entries that aren't proper heap pointers
+
+        Push     "R0, R1, R7, R8"
+        MOV       R8, #0
+        ADD       R0, R0, R12     ; convert to addressi
+        ADD       R1, R1, R12
+
+01      CMP       R5, R3
+        BGE       %FT02
+        LDR       R7, [R5], #8
+
+        CMP       R7, R0          ; while entry at R5 LT R0 pokeout
+        STRLT     R8, [R5, #-8]
+        BLT       %BT01
+
+        SUBGT     R5, R5, #8
+
+        LDR       R7, [R0]
+        ADD       R0, R0, R7      ; next block
+        CMP       R0, R1          ; step block until end
+        BLT       %BT01
+02
+        Pull     "R0, R1, R7, R8"
+        MOVS      PC, lr
+
+        LTORG
+
+;-----------------------------------------------------------------------------
+
+Genocide ROUT ; non-fatally kill de lot of em, stiff the module chain
+              ; corrupts R1-R5, R9, R10, R12
+              ; returns R9 = original module chain
+
+         Push   "lr"
+         MOV     R4, #0                     ; chain so far
+
+FindChainEnd
+         MOV     R1, #Module_List             ; prevnode
+         LDR     R9, [R1, #Module_chain_Link] ; currnode
+         CMP     R9, #0
+         BNE     %FT01
+         MOV     R9, R4
+         Pull   "PC"
+
+01       LDR     R11, [R9, #Module_chain_Link] ; lastnode?
+         CMP     R11, #0
+         MOVNE   R1, R9                     ; step chain
+         MOVNE   R9, R11
+         BNE     %BT01
+
+         LDR     R2, [R9, #Module_incarnation_list] ; keep chain head
+         ADD     R3, R9, #Module_incarnation_list
+02       LDR     R12, [R3, #Incarnation_Link]       ; currinc
+         CMP     R12, #0
+         BNE     %FT03
+         STR     R12, [R1, #Module_chain_Link]      ; remove from chain
+         STR     R2, [R9, #Module_incarnation_list] ; replace incarnations
+         STR     R4, [R9, #Module_chain_Link]       ; make into dead head
+         MOV     R4, R9
+         B       FindChainEnd
+
+03       MOV     R10, #0                    ; not fatal indicator
+ [ {FALSE} ; debug RMTidy
+        Push    "r0"
+        LDR     r0, [r9, #Module_code_pointer]
+        LDR     r14, [r0, #Module_Title]
+        ADD     r0, r0, r14
+        SWI     XOS_WriteS
+        =       "RMTidy: killing '", 0
+        ALIGN
+        SWI     XOS_Write0
+        SWI     XOS_WriteS
+        =       "'", 10, 13, 0
+        ALIGN
+        Pull    "r0"
+ ]
+         BL      CallDie
+         BVC     %BT02
+
+; Copy the error in case overwritten
+
+        MOV     R5,R0                           ; Error block
+        LDR     R0,=GeneralMOSBuffer                ; R0-> stashed error
+        LDR     LR,[R5],#4
+        STR     LR,[R0],#4                      ; Copy error number
+05      LDRB    LR,[R5],#1
+        STRB    LR,[R0],#1
+        CMP     LR,#' '                         ; End of string?
+        BGE     %BT05                           ; No then more
+        LDR     R0,=GeneralMOSBuffer            ; R0-> stashed error
+        SETV
+
+         MOV     R5, R12
+         MOV     R12, R2
+         MOV     R2, R5     ; r12 now incarnation to start, R2 stop point
+         BL      RestartModuleStructure
+                ; somebody winged, so try and restore consistency before error.
+         Pull   "PC"
+
+;------------------------------------------------------------------------------
+
+RestartModuleStructure ROUT
+
+ ; R1 -> prevmod to R9
+ ; R9 -> the whinger, R12 -> incarnation list, R2 stop point
+ ; R4 -> dead module list
+ ; R3 -> previnc
+
+         Push   "R0, lr"
+
+11       CMP     R2, R12
+         BNE     %FT12                      ; more incarnations to do
+
+         CMP     R4, #0
+         Pull   "R0, PC", EQ, ^
+
+         MOV     R1, R9
+14       MOV     R9, R4
+         LDR     R4, [R4, #Module_chain_Link]
+         MOV     R2,#0                      ; indicate reinit all incarnations
+         LDR     R12, [R9, #Module_incarnation_list]
+         STR     R2,  [R9, #Module_incarnation_list]
+         ADD     R3, R9, #Module_incarnation_list ; previnc ptr
+
+         STR     R2, [R9, #Module_chain_Link]     ; relink next
+         STR     R9, [R1, #Module_chain_Link]
+         B       %BT11                            ; start incarnations
+
+12       LDR     R11, [R12, #Incarnation_Link]    ; get next in case problems
+         ADRL    R10, crstring                    ; no environment
+         BL      CallInit                         ; frees node if error
+         BVC     %FT15
+
+         STR     R0, [stack]
+         LDR     R0, [stack, #4]
+         ORR     R0, R0, #V_bit
+         STR     R0, [stack, #4]
+         Push   "R2"
+         MOV     R2, R1                     ; prevnode
+         BL      LoseModuleSpace_if_its_the_only_incarnation
+         Pull   "R2"
+
+         CMP     R9, #0                     ; did we just discard that module?
+         BEQ     %BT14                      ; yup - next one
+
+15       LDRVC   R0,  [R3, #Incarnation_Link]
+         STRVC   R0,  [R12,#Incarnation_Link]
+         STRVC   R12, [R3, #Incarnation_Link]
+         MOVVC   R3, R12
+
+         MOV     R12, R11
+         B       %BT11                      ; next incarnation
+ ] ; endif <RMTidyDoesNowt>
+
+;****************************************************************************
+
+Module_Clear ENTRY "r0-r3"
+        TEQP    pc, #SVC_mode                           ; interrupts on
+        MOV     r3, #0                                  ; position in chain
+
+; now find entry in chain to kill : one with successor = R3
+
+MHC_GetEndOne
+        MOV     r2, #Module_List                        ; prevnode for killing
+        LDR     r0, [r2, #Module_chain_Link]
+        CMP     r0, r3
+        PullEnv EQ
+        ExitSWIHandler EQ
+MHC_StepOn
+        LDR     r1, [r0, #Module_chain_Link]
+        CMP     r1, r3
+        MOVNE   r2, r0
+        MOVNE   r0, r1
+        BNE     MHC_StepOn
+
+        LDR     r11, [r0, #Module_ROMModuleNode]        ; don't kill if it's a ROM module (note that this would also
+        CMP     r11, #1                                 ; account for squeezed ROM modules, so the invincible bit in the
+        LDRCC   r11, [r0, #Module_code_pointer]         ; die entry is not strictly necessary any more, but never mind!)
+        LDRCC   r11, [r11, #Module_Die]                 ; Check for invincible module
+        CMPCC   r11, #&80000000                         ; (die entry has top bit set)
+        MOVCS   r3, r0                                  ; step if not about to delete
+                                                        ; - don't assassinate ROM modules.
+        BLCC    KillAndFree
+        BVC     MHC_GetEndOne
+
+        LDR     r3, [r2, #Module_chain_Link]
+        STR     r0, [stack]
+        LDR     r0, [stack, #4*4]
+        ORR     r0, r0, #V_bit
+        STR     r0, [stack, #4*4]
+        B       MHC_GetEndOne
+
+;*************************************************************
+; AddArea:
+; Entry;  R1 -> module in memory to add, leaving it in place.
+; Return: registers preserved, V set if problem
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Module_AddArea   ROUT
+         Push   "R9, lr"
+         TEQP    PC, #SVC_mode               ; interrupts on
+         ADRL    R10, crstring               ; null environment
+         BL      ModuleIn_CheckForDuplicate  ; altentry to Load_Module
+         Pull   "R9, lr"
+        B       SLVK_TestV
+
+;*************************************************************
+; CopyArea
+;    R1 -> area of memory to add to the module list,
+;          copying into the RMA
+;    R2 =  size of the area.
+
+Module_CopyArea  ROUT
+         Push   "R0-R5, R9, lr"
+         TEQP    PC, #SVC_mode
+
+  ; R1 address, R2 size
+         BL      CheckHeader
+         BVS     AreaFail
+
+         MOV     R10, R1
+
+         LDR     R1, [R10, #Module_Title]
+         ADD     R1, R1, R10
+         BL      LookUp_Module               ; check for duplicate
+         BLNE    KillAndFree
+         STRVS   R0, [stack]
+         Pull   "R0-R5, R9, lr", VS
+         BVS     SLVK_TestV
+
+; R10 points at area
+         LDR     R3, [stack, #4*2]           ; get size back
+         BL      RMAClaim_Chunk
+         ADRVSL  R0, ErrorBlock_MHNoRoom
+       [ International
+         BLVS    TranslateError
+       ]
+         BVS     AreaFail
+
+         MOV     R9, R2                      ; new module pointer
+
+; copy R3 bytes from R10 to R2
+01       LDR     R1, [R10], #4
+         STR     R1, [R2], #4
+         SUBS    R3, R3, #4
+         BHI     %BT01                       ; was BPL, which is wrong!
+
+         ADRL    R10, crstring               ; no environment string
+         MOV     R11, #0                     ; not podular
+         BL      LinkAndInit
+
+AreaFail
+         STRVS   R0, [stack]
+         Pull   "R0-R5, R9, lr"
+        B       SLVK_TestV
+
+;*************************************************************
+; Enumerate modules
+; Entry:  R0 Reason code
+;         R1 module number
+;         R2 incarnation number
+; Exit:   R1, R2 updated to refer to next existing module
+;         R3 -> module code
+;         R4    private word contents
+;         R5 -> postfix string
+
+Module_GetNames  ROUT
+         TEQP    PC, #SVC_mode               ; interrupts on
+         MOV     R11, R1
+         MOV     R12, R2
+         MOV     R10, #Module_List
+01       LDR     R10, [R10, #Module_chain_Link]
+         CMP     R10, #0
+         BEQ     %FT10                       ; no more modules
+         SUBS    R11, R11, #1
+         BPL     %BT01
+         LDR     R3, [R10, #Module_code_pointer]
+         ADD     R10, R10, #Module_incarnation_list
+02       LDR     R10, [R10, #Incarnation_Link]
+         CMP     R10, #0
+         BEQ     %FT11                       ; no more incarnations
+         SUBS    R12, R12, #1
+         BPL     %BT02
+         LDR     R4, [R10, #Incarnation_Workspace]
+         ADD     R5, R10, #Incarnation_Postfix
+         LDR     R10, [R10, #Incarnation_Link]
+20       CMP     R10, #0
+         ADDNE   R2, R2, #1
+         MOVEQ   R2, #0
+         ADDEQ   R1, R1, #1
+         ExitSWIHandler
+
+10       ADR     R0, ErrorBlock_NoMoreModules
+       [ International
+         B       BumDealInModule_Translate
+       |
+         B       BumDealInModule
+       ]
+         MakeErrorBlock NoMoreModules
+
+11       CMP     r2, #0
+         LDREQ   r4, =&DEADDEAD
+         MOVEQ   r10, #0
+         BEQ     %BT20         ; fudge for modules that go bang in init/die
+         ADR     R0, ErrorBlock_NoMoreIncarnations
+       [ International
+         B       BumDealInModule_Translate
+       |
+         B       BumDealInModule
+       ]
+         MakeErrorBlock NoMoreIncarnations
+         LTORG
+
+;*************************************************************
+
+Module_ExtendBlock ROUT
+         Push   "R0, r1, R3, lr"
+
+         ADD     R3, R3, #15
+         BIC     R3, R3, #15
+
+         MOV     R0, #HeapReason_ExtendBlock
+         BL      DoRMAHeapOpWithExtension
+
+         STRVS   R0, [stack]
+         Pull   "R0, r1, R3, lr"
+         B       SLVK_TestV
+
+;*************************************************************
+; New Incarnation
+;    R1 -> module%newpostfix
+
+Module_NewIncarnation ROUT
+         Push   "R0-R4, R9, lr"
+         TEQP    PC, #SVC_mode
+         BL      LookUp_Module
+         BEQ     CheckTheROM
+         CMP     R12, #0
+         BEQ     Incarnation_needed
+         BGT     Incarnation_exists
+         MOV     R9, R0                      ; node pointer
+         MOV     R0, R1                      ; postfix
+         MOV     R10, R1
+         BL      EnvStringSkipName           ; envstring ptr in R10
+         BL      Add_Incarnation
+01       STRVS   R0, [stack]
+         Pull   "R0-R4, R9, lr"
+         B      SLVK_TestV
+
+CheckTheROM
+         MOV     R0, #Postfix_Separator      ; passed string must have postfix
+         BL      AddModuleIfInROM
+         B       %BT01
+
+Incarnation_needed
+         Pull   "R0-R4, R9, lr"
+         ADR     R0, ErrorBlock_PostfixNeeded
+       [ International
+         B       BumDealInModule_Translate
+       |
+         B       BumDealInModule
+       ]
+         MakeErrorBlock PostfixNeeded
+
+Incarnation_exists
+         Pull   "R0-R4, R9, lr"
+         ADR     R0, ErrorBlock_IncarnationExists
+       [ International
+         B       BumDealInModule_Translate
+       |
+         B       BumDealInModule
+       ]
+         MakeErrorBlock IncarnationExists
+
+;*************************************************************
+; Rename Incarnation
+; R1 -> current module title
+; R2 -> new postfix.
+
+Module_RenameIncarnation ROUT
+         Push   "R0-R4, R9, lr"
+         BL      lookup_commoned
+         BVS     %FT01
+
+; R12 -> incarnation node    (0 for not specified)
+; R3  -> previous incarnation
+
+         MOV     R11, R12
+         MOV     R0, R9                      ; check incarnation
+         LDR     R1, [stack, #4*2]           ; not already there
+ [ Fix12
+         Push    R3                          ; preserve pointer to
+         BL      FindIncarnation
+         Pull    R3                          ; previous incarnation
+ |
+         BL      FindIncarnation
+ ]
+         BNE     %FT03                       ; already exists
+         MOV     R12, R11
+
+         CMP     R12, #0
+         ADDEQ   R3, R9, #Module_incarnation_list
+         LDREQ   R12, [R9, #Module_incarnation_list]
+         MOV     R11, R3
+
+         ADD     R1, R12, #Incarnation_Postfix
+         BL      %FT10                       ; old postfix length -> R0
+         MOV     R10, R0
+         LDR     R1, [stack, #4*2]           ; new postfix
+         BL      %FT10                       ; new length - > R0
+         SUB     R3, R0, R10
+
+         MOV     R2, R12                     ; incarnation node
+         MOV     R0, #HeapReason_ExtendBlock
+         BL      DoSysHeapOpWithExtension
+         BVS     %FT01
+
+         STR     R2, [R11, #Incarnation_Link] ; relink
+         ADD     R2, R2, #Incarnation_Postfix
+         LDR     R1, [stack, #4*2]
+02       LDRB    R0, [R1], #1
+         CMP     R0, #" "
+         MOVLE   R0, #0
+         STRB    R0, [R2], #1
+         BGT     %BT02
+01       STRVS   R0, [stack]
+         Pull   "R0-R4, R9, lr"
+         B      SLVK_TestV
+
+03       ADR     R0, ErrorBlock_IncarnationExists
+       [ International
+         Push   "LR"
+         BL     TranslateError
+         Pull   "LR"
+       |
+         SETV
+       ]
+         B       %BT01
+
+10       MOV     R0, #0
+11       LDRB    R3, [R1, R0]
+         CMP     R3, #" "
+         ADDGT   R0, R0, #1
+         BGT     %BT11
+         MOV     PC, lr
+
+;*************************************************************
+; MakePreferred
+;   R1 -> name
+
+Module_MakePreferred ROUT
+        Push    "R0-R4, R9, lr"
+        BL      lookup_commoned
+        BVS     %FT01
+        BLGT    PreferIncarnation       ; only prefer it if found!
+01
+ [ Fix13
+        STRVS   R0, [sp, #0]
+ ]
+        Pull    "R0-R4, R9, lr"
+        B       SLVK_TestV
+
+;*************************************************************
+; AddPoduleModule
+;
+; in:   R1 -> envstring
+;       R2 = chunk number
+;       R3 = podule number
+;
+; out:  All registers preserved
+
+Module_AddPoduleModule ENTRY
+        TEQP    pc, #SVC_mode                   ; interrupts on
+        BL      APMEntry
+        PullEnv
+        B       SLVK_TestV
+
+APMEntry ENTRY "r0-r7,r9"
+        MOV     r0, r2
+        SWI     XPodule_EnumerateChunksWithInfo ; out: r1=size, r2=type, r4->name, r5->help string, r6=module address if in ROM
+        BVS     %FT99
+        CMP     r2, #OSType_Module
+        BNE     %FT98
+
+        MOV     r7, r3
+        MOV     r5, #0                          ; indicate not a ROM module (although strictly speaking, it is!)
+APMInitEntry
+        Push    "r1"                            ; size
+        MOV     r1, r4
+        BL      LookUp_Module                   ; check for duplicate
+        BLNE    KillAndFree
+        Pull    "r3"                            ; get size back
+        BVS     %FT99
+
+        MOVS    r1, r6                          ; if module address non-zero, then it's a directly executable ext. ROM
+        BNE     %FT10                           ; and don't claim a block, or read the chunk
+
+        BL      RMAClaim_Chunk
+        BVS     %FT99
+        LDR     r0, [stack, #4*2]
+        LDR     r3, [stack, #4*3]
+        SWI     XPodule_ReadChunk
+        MOV     r1, r2                          ; r1 = address of module
+10
+        LDR     r2, [r1, #-4]                   ; r2 = size
+        BLVC    CheckHeader
+        BVS     %FT97                           ; free space too (doesn't matter that it fails for extension ROM)
+
+        MOV     r9, r1
+        LDR     r10, [stack, #4]                ; envptr
+
+        MOVS    r3, r7                          ; if not a podule (r7 < 0)
+        MOVMI   r11, #0                         ; then use hardware address zero
+        BMI     %FT20
+        Push    "r1"                            ; else compute hardware address from 'fake' podule number
+        SWI     XPodule_HardwareAddresses       ; get raw hardware address for podule r3 into r0 (r1 = combined)
+        Pull    "r1"
+        BVS     %FT97
+        MOV     r11, r0                         ; move into r11
+20
+        BL      LinkAndInit
+        STRVC   r5, [r9, #Module_ROMModuleNode] ; store zero or pointer to ROM module node (if no error in init)
+99
+        STRVS   r0, [stack]
+        EXIT
+
+98
+        ADR     r0, ErrorBlock_ChunkNotRM
+      [ International
+        BL      TranslateError
+      ]
+96
+        SETV
+        B       %BT99
+        MakeErrorBlock ChunkNotRM
+
+97
+        MOV     r2, r1                          ; free claimed RMA space
+        MOV     r1, #RMAAddress
+        Push    "r0"
+        MOV     r0, #HeapReason_Free
+        SWI     XOS_Heap
+        Pull   "r0"
+        B       %BT96
+
+        LTORG
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; LookupName
+;    Take module name, return info on it suitable for use with Enumeration
+;      (e.g. to get all incarnations of it)
+;  In :   R1 -> name
+;  Out:   R1 module number          \  of THIS module; first enumerate
+;         R2 incarnation number     /  call will give back this module
+;         R3 -> module code
+;         R4    private word contents
+;         R5 -> postfix string
+
+Module_LookupName ROUT
+         Push   "R0-R4, R9, lr"
+         BL      lookup_commoned
+         BVC     %FT01
+         STR     R0, [stack]
+         Pull   "R0-R4, R9, lr"
+         B      SLVK_SetV
+
+01       MOV     R1, #0               ; module number
+         MOV     R0, #Module_List
+
+; R9  -> module chain node
+; R12 -> incarnation node    (0 for not specified, -1 for not found)
+
+         LDREQ   R12, [R9, #Module_incarnation_list]  ; preferred inc.
+
+02       LDR     R0, [R0]
+         CMP     R0, R9
+         ADDNE   R1, R1, #1
+         BNE     %BT02
+         ADD     R0, R0, #Module_incarnation_list
+         MOV     R2, #0
+03       LDR     R0, [R0]
+         CMP     R0, R12
+         ADDNE   R2, R2, #1
+         BNE     %BT03
+         LDR     R3, [R9, #Module_code_pointer]
+         LDR     R4, [R12, #Incarnation_Workspace]
+         ADD     R5, R12, #Incarnation_Postfix
+         LDR     r0, [sp], #5*4            ; Load r0, skip r1-r4
+         Pull   "R9, lr"
+         ExitSWIHandler
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; EnumerateROM_Modules and EnumerateROM_ModulesWithInfo
+;
+;  In :   R1 = module number
+;         R2 = -1    => ROM
+;            = other => Podule R2
+;
+;  Out:   R1 = incremented: next call will return next module
+;         R2 = preserved
+;         R3 -> name
+;         R4 = -1 => unplugged
+;            =  0 => inserted but not currently in the module chain
+;            =  1 => active
+;            =  2 => running
+;         R5 =  chunk number of podule RM
+;         If R0 = ModHandReason_EnumerateROM_ModulesWithInfo then
+;          R6 = BCD version number of module (decimal point between top and bottom half-words)
+
+Module_EnumerateROM_Modules ROUT
+Module_EnumerateROM_ModulesWithInfo ROUT
+        LDR     r12, =ROMModuleChain
+        MOV     r10, r1                                 ; module count
+10
+        LDR     r12, [r12, #ROMModule_Link]             ; follow link to next module
+        TEQ     r12, #0                                 ; if no more modules
+        ADREQL  r0, ErrorBlock_NoMoreModules
+      [ International
+        Push    "lr",EQ
+        BLEQ    TranslateError
+        Pull    "lr",EQ
+      ]
+        BEQ     SLVK_SetV                               ; then report error
+        LDR     r11, [r12, #ROMModule_PoduleNumber]
+        CMP     r2, #-1                                 ; if searching for podule -1, then this one must be ">="
+        BEQ     %FT30
+        BGT     %FT20                                   ; searching from normal podules onwards
+
+; searching from extension ROMs onwards
+
+        CMP     r11, r2                                 ; so if r11 > r2 then not there yet
+        BGT     %BT10
+
+; searching from normal podules onwards
+
+20
+        CMP     r11, #-1                                ; if found one is extension ROM
+        BLT     %FT30                                   ; then will be OK
+        CMP     r11, r2                                 ; else is only OK if r11 >= r2
+        BLT     %BT10
+30
+        CMP     r11, r2                                 ; check for equality
+        MOVNE   r1, #0                                  ; if not correct podule then this is the one to return
+        BNE     %FT50
+
+        SUBS    r10, r10, #1                            ; decrement module count
+        BCS     %BT10                                   ; not there yet, so go back
+50
+        Push    "r0-r2, lr"
+        LDR     r10, [r12, #ROMModule_CMOSAddrMask]     ; get CMOS address and mask
+        ANDS    r2, r10, #&FF                           ; extract address
+        MOVNE   r1, r2                                  ; if there is a CMOS address
+        MOVNE   r0, #ReadCMOS
+        SWINE   XOS_Byte                                ; then read it
+        TST     r2, r10, LSR #16                        ; test bit
+        Pull    "r0-r2"
+        Push    "r8, r9"
+        MOVNE   r4, #-1                                 ; indicate unplugged
+        BNE     %FT90
+
+; not unplugged, so check for module in module list
+
+        MOV     r4, #Module_List
+60
+        LDR     r4, [r4, #Module_chain_Link]
+        TEQ     r4, #0                                  ; module not active
+        BEQ     %FT90
+        LDR     r11, [r4, #Module_ROMModuleNode]        ; get active module's pointer to ROM module node
+        TEQ     r11, r12                                ; if it matches
+        BNE     %BT60
+        LDR     r10, [r4, #Module_code_pointer]         ; get pointer to code
+        MOV     r11, #0
+        LDR     r11, [r11, #Curr_Active_Object]
+        LDR     r4, [r10, #-4]                          ; node size of code
+        ADD     r4, r4, r10
+        CMP     r11, r10
+        CMPCS   r4, r11
+        MOVHI   r4, #2                                  ; indicate running
+        MOVLS   r4, #1                                  ; indicate just active
+90
+        LDR     r2, [r12, #ROMModule_PoduleNumber]      ; reload podule number
+        CMP     r2, #-1                                 ; if not main ROM
+        LDRNE   r5, [r12, #ROMModule_ChunkNumber]       ; then load chunk number
+        LDR     r3, [r12, #ROMModule_Name]              ; load pointer to name
+        ADD     r1, r1, #1                              ; move module number onto next one
+        TEQ     r0, #ModHandReason_EnumerateROM_ModulesWithInfo
+        LDREQ   r6, [r12, #ROMModule_Version]
+        Pull    "r8, r9, lr"                            ; restore registers
+        ExitSWIHandler                                  ; and exit
+
+;*************************************************************
+; Support routines.
+;
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Load_Module
+;     takes filename pointer in R1, and loads and initialises the given file.
+;     Returns R9 as a pointer to the node claimed
+;     and  V/current error set if fails
+
+Load_Module ROUT
+
+        Push   "R0-R5, lr"
+
+        MOV     r0, #OSFile_ReadInfo
+        SWI     XOS_File
+        BVS     modfailxit              ; return FileSwitch error
+        CMP     r0, #object_file
+        BNE     HeNotFile
+
+        BIC     R2, R2, #&FF            ; low byte ignored by me.
+        CMP     R2, #Module_LoadAddr
+        BNE     NotAModule
+
+; it's a module, so try and claim.
+        MOV     R10, R1                 ; keep string pointer
+        MOV     R3, R4                  ; size of vector needed
+        BL      RMAClaim_Chunk
+        BVS     modfailxit
+
+02      MOV     R9, R2                  ; keep a copy of node ptr.
+        MOV     R1, R10
+        MOV     R0, #OSFile_Load
+        MOV     R3, #0                  ; load to R2 posn
+        SWI     XOS_File
+        BVS     modfailxit              ; return FileSwitch error
+
+50      MOV     R11, #0                 ; not loaded from hardware.
+
+; R9 address, R9!-4 size
+        MOV     R1, R9
+        LDR     R2, [R9, #-4]
+        BL      CheckHeader
+        BVS     Duplicate_Immortal      ; actually means naff header field
+
+; now we've got it, see if any other modules have the same name.
+
+        LDR     R1, [R9, #Module_Title]
+        ADD     R1, R1, R9
+        BL      LookUp_Module
+        BEQ     %FT01                   ; no module at all
+        CMP     R12, #0
+        BNE     nopostfixwanted         ; postfix given: bad name
+        BL      KillAndFree
+        BVS     Duplicate_Immortal
+
+; now claim a link
+; R9 module pointer, R10 environment
+
+01      BL      EnvStringSkipName
+        BL      LinkAndInit             ; takes R2 prevnode from lookup
+
+        STRVS   R0, [stack]
+        Pull   "R0-R5, pc"
+
+Duplicate_Immortal                      ; free space claimed for loading
+        STR     R0, [stack]
+        MOV     R2, R9
+        MOV     R0, #HeapReason_Free
+        MOV     R1, #RMAAddress
+        SWI     XOS_Heap
+        Pull   "R0-R5, lr"
+        ORRS    PC, lr, #V_bit
+
+        MakeErrorBlock MHNoRoom
+
+nopostfixwanted
+        ADR     R0, ErrorBlock_ModulePostfix
+      [ International
+        BL      TranslateError
+      ]
+        B       modfailxit
+
+        MakeErrorBlock ModulePostfix
+
+        MakeErrorBlock NotMod
+
+NotAModule
+        ADR     R0, ErrorBlock_NotMod
+      [ International
+        BL      TranslateError
+      ]
+modfailxit
+        STR     R0, [stack]
+        Pull   "R0-R5, lr"
+        ORRS    PC, lr, #V_bit
+
+HeNotFile
+        MOV     r2, r0
+        MOV     r0, #OSFile_MakeError
+        SWI     XOS_File
+        B       modfailxit
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; ModuleIn_CheckForDuplicate
+; Altentry to Load_Module for AddArea: module already in, initialise it.
+
+ModuleIn_CheckForDuplicate
+         Push   "R0-R5, lr"
+         MOV     R9, R1           ; move module ptr to handy place
+         B       %BT50
+
+;*************************************************************
+; AddModuleIfInROM
+; in: R1 -> name
+;     R0 = Postfix_Separator => called from AddNewIncarnation when module is not active
+;                               find newest version in ROM, and initialise it in its own location (even if unplugged)
+;                               then rename the base incarnation of it to specified postfix
+;
+;        = 0                 => called from ReInit when module is not active
+;                               plug in all versions of module, and initialise newest one in its own location
+;
+; out: R5-R8 preserved
+;      Other registers may be corrupted
+;
+
+AddModuleIfInROM ENTRY "r5-r8"
+        MOV     r4, r0
+        MOV     r12, #0                                 ; search entire ROM set
+        MOV     r7, r1                                  ; save pointer to beginning of name
+        BL      FindROMModule
+        TEQ     r12, #0
+        BNE     %FT10
+        BL      MakeNotFoundError                       ; in:  r1 -> module name 'foo'
+                                                        ; out: r0 -> "Module 'foo' not found" error, V=1
+        EXIT
+
+10
+        MOV     r6, r1                                  ; save pointer to terminator of module name
+15
+        LDR     r14, [r12, #ROMModule_OlderVersion]     ; find oldest version
+        TEQ     r14, #0
+        MOVNE   r12, r14
+        BNE     %BT15
+
+20
+        TEQ     r4, #0                                  ; if doing AddIncarnation rather than ReInit
+        BNE     %FT30                                   ; then don't plug module in
+        MOV     r5, #&FF                                ; set up byte mask (and indicate found)
+        LDR     r1, [r12, #ROMModule_CMOSAddrMask]
+        AND     r3, r5, r1, LSR #16                     ; get bit mask
+        ANDS    r1, r1, r5
+        BEQ     %FT30                                   ; if no CMOS, then look for another module
+        MOV     r0, #ReadCMOS
+        SWI     XOS_Byte
+        EXIT    VS
+        TST     r2, r3                                  ; test if module unplugged
+        BEQ     %FT30                                   ; if not, then don't write to CMOS (so RMReInit works when FSLock enabled)
+        BIC     r2, r2, r3                              ; otherwise clear bit
+        MOV     r0, #WriteCMOS
+        SWI     XOS_Byte
+        EXIT    VS
+30
+        LDR     r14, [r12, #ROMModule_NewerVersion]
+        TEQ     r14, #0
+        MOVNE   r12, r14
+        BNE     %BT20
+
+        TEQ     r4, #0                                  ; if AddIncarnation then check that name terminator is "%"
+        LDRNEB  r14, [r6], #1                           ; load next character (and skip it)
+        TEQNE   r14, #Postfix_Separator
+        BEQ     %FT40
+        ADRL    r0, ErrorBlock_PostfixNeeded
+      [ International
+        BL      TranslateError
+      |
+        SETV
+      ]
+        EXIT
+
+40
+        MOV     r11, r12
+        BL      InitialiseROMModule                     ; in both cases initialise newest version
+                                                        ; (in AddIncarnation case it may still be unplugged)
+        EXIT    VS
+
+        TEQ     r4, #0                                  ; if ReInit then we've finished (V=0 from above)
+        EXIT    EQ
+
+        SUB     r8, r6, r7                              ; length of module name including '%'
+        ADD     r8, r8, #4+1+3                          ; allow for 'Base<0>' and round up to whole number of words
+        BIC     r8, r8, #3
+        SUB     stack, stack, r8
+
+        MOV     r0, stack
+50
+        LDRB    r14, [r7], #1                           ; copy name, including '%'
+        STRB    r14, [r0], #1
+        TEQ     r7, r6
+        BNE     %BT50
+
+        ADR     r1, base_postfix
+60
+        LDRB    r14, [r1], #1                           ; copy 'Base<0>'
+        STRB    r14, [r0], #1
+        TEQ     r14, #0
+        BNE     %BT60
+
+        MOV     r0, #ModHandReason_RenameIncarnation
+        MOV     r1, stack                               ; pointer to '<module>%Base<0>'
+        MOV     r2, r6                                  ; pointer to 'newinc'
+        SWI     XOS_Module
+        ADD     stack, stack, r8                        ; junk name
+        EXIT
+
+;*************************************************************
+; LinkAndInit :
+;     module pointer in R9
+;     module list position in R2 : added at end if posn not found
+;     environment string pointer in R10
+;    "hardware" in R11
+;     returns module node pointer in R9
+
+LinkAndInit ENTRY "r2, r3"
+        MOV     r3, #ModInfo
+        BL      ClaimSysHeapNode
+        EXIT    VS
+
+        STR     r9, [r2, #Module_code_pointer]
+        STR     r11, [r2, #Module_Hardware]
+        MOV     r9, r2                                  ; keep node pointer
+
+        MOV     r0, #0
+        STR     r0, [r2, #Module_ROMModuleNode]         ; assume not a ROM module
+        STR     r0, [r2, #Module_incarnation_list]      ; terminate list
+        ADR     r0, base_postfix
+        BL      Add_Incarnation                         ; add Base incarnation
+        BVS     %FT01
+
+        Pull    "r2"
+        ADR     r0, Module_List
+05
+        LDR     r1, [r0]
+        CMP     r1, #0
+        CMPNE   r0, r2
+        MOVNE   r0, r1
+        BNE     %BT05
+
+; add module to chain end - give ROM modules priority.
+
+        STR     r1, [r9, #Module_chain_Link]
+        STR     r9, [r0, #Module_chain_Link]
+        Pull    "r3, pc"                                ; V clear from EQ compare with 0
+
+01
+        Push    "r0"
+        LDR     r2, [r9, #Module_code_pointer]
+        MOV     r1, #RMAAddress
+        MOV     r0, #HeapReason_Free
+        SWI     XOS_Heap
+        MOV     r2, r9                                  ; node pointer
+        BL      FreeSysHeapNode
+        Pull    "r0, r2, r3, lr"
+        ORRS    pc, lr, #V_bit
+
+base_postfix
+        =       "Base",0                                ; postfix used for 1st incarnation
+        ALIGN
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Add_Incarnation
+;       takes postfix pointer in R0 (terminated by <= space
+;       module node pointer in R9
+;       envstring in R10
+;   Adds an incarnation node, reinitialises the module
+
+Add_Incarnation  ROUT
+         Push   "R0-R3, lr"
+
+         MOV     R3, #Incarnation_Postfix      ; node size needed
+01       LDRB    R1, [R0], #1
+         ADD     R3, R3, #1
+         CMP     R1, #" "
+         BGT     %BT01
+         BL      ClaimSysHeapNode
+         STRVS   R0, [stack]
+         Pull   "R0-R3, PC", VS
+
+         LDR     R0, [stack]
+         ADD     R3, R2, #Incarnation_Postfix
+02       LDRB    R1, [R0], #1
+         CMP     R1, #" "
+         STRGTB  R1, [R3], #1
+         BGT     %BT02
+         MOV     R1, #0
+         STRB    R1, [R3]
+
+         MOV     R3, #0
+         STR     R3, [R2, #Incarnation_Workspace] ; zero private word
+         MOV     R12, R2
+         BL      CallInit
+;         BLVS    FreeIncarnation done by CallInit
+         STRVS   R0, [stack]
+
+         LDRVC   R3, [R9, #Module_incarnation_list]
+         STRVC   R3, [R2, #Incarnation_Link]
+         STRVC   R2, [R9, #Module_incarnation_list]
+         Pull   "R0-R3, PC"
+
+;*************************************************************
+CallInit         ROUT
+;    take R9  -> module node
+;         R12 -> incarnation node
+;         R10 -> envstring
+;    set R11 appropriately
+;    initialise module with R10 given
+
+        Push   "R0-R6, R11, R12, lr"
+
+ [ SqueezeMods
+        BL      CheckForSqueezedModule                  ; unsqueeze module if squeezed
+        BVS     %FT02
+ ]
+
+  ; see if we need to set up a module swi node
+
+        BL      CheckForSWIEntries
+        LDR     R12, [stack, #4*(6+2)]
+        BNE     %FT03
+
+  ; the module really does have a SWI chunk. Add node to hashtable.
+
+        MOV     R4, R0
+        MOV     R11, R1
+        MOV     R3, #ModSWINode_Size
+        BL      ClaimSysHeapNode
+        BVS     %FT02
+        STR     R9,  [R2, #ModSWINode_MListNode]
+        STR     R4,  [R2, #ModSWINode_CallAddress]
+        STR     R11, [R2, #ModSWINode_Number]
+        ModSWIHashvalOffset R11
+        LDR     R6, [R11, #ModuleSWI_HashTab]!  ;  link into table.
+        STR     R6, [R2, #ModSWINode_Link]
+        STR     R2, [R11]
+
+03  ; now prepared to look at module
+
+        LDR     R3, [R9, #Module_code_pointer]
+
+        LDR     R4, [R3, #Module_Init]
+        CMP     R4, #0
+        Pull   "R0-R6, R11, R12, PC", EQ      ; V clear
+
+        ADD     R12, R12, #Incarnation_Workspace
+        MOV     R11, #0
+        ADD     R5, R9, #Module_incarnation_list - Incarnation_Link
+01      LDR     R5, [R5, #Incarnation_Link]
+        CMP     R5, #0                        ; count incarnations
+        ADDNE   R11, R11, #1
+        BNE     %BT01
+        CMP     R11, #0
+        LDREQ   R11, [R9, #Module_Hardware]
+
+  ; R11, R12 now set: initialise
+
+        MOV     lr, PC                        ; pseudo BL
+        ADD     PC, R3, R4                    ; call 'im
+
+        Pull   "R0-R6, R11, R12, lr", VC
+        BICVCS  PC, lr, #V_bit
+
+02      LDR     R12, [stack, #4*(6+2)]
+        BL      FreeIncarnation
+        BL      FreeSWIEntry
+        STR     R0, [stack]
+        Pull   "R0-R6, R11, R12, PC"           ; V set return
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Enter with module pointer in R1
+;              "    size    in R2
+
+CheckHeader ROUT
+        Push   "R3, lr"
+        LDR     R3, [R1, #Module_HC_Table]
+        BL      %FT11
+        LDR     R3, [R1, #Module_HelpStr]
+        BL      %FT11
+        LDR     R3, [R1, #Module_Title]
+        BL      %FT11
+        LDR     R3, [R1, #Module_Service]
+        BL      %FT10
+        LDR     R3, [R1, #Module_Die]
+        BIC     R3, R3, #&80000000              ; ignore top-bit (means cannot be RMCleared)
+        BL      %FT10
+        LDR     R3, [R1, #Module_Init]
+        TST     R3, #&80000000
+        BLEQ    %FT10                           ; only check init offset if an unsqueezed module
+        Pull   "R3, lr"
+        BICS    PC, lr, #V_bit
+
+10      TST     R3, #3
+        BNE     %FT99
+11      CMP     R3, R2
+        MOVLO   PC, lr
+99
+        Pull   "R3, lr"
+        ADR     R0, ErrorBlock_BadRMHeaderField
+      [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      ]
+        ORRS    PC, lr, #V_bit
+        MakeErrorBlock BadRMHeaderField
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Enter with module node pointer in R9
+; Sets R12 to module code pointer, R0 SWI code offset, R1 to SWI number
+; Only returns SWIs extant if no incarnations of module yet
+
+CheckForSWIEntries ROUT
+         LDR     R12, [R9, #Module_incarnation_list]
+         CMP     R12, #0
+         LDREQ   R12, [R9, #Module_code_pointer]
+         LDREQ   R1, [R12, #Module_SWIChunk]
+         BICEQ   R1, R1, #Auto_Error_SWI_bit
+         TSTEQ   R1, #Module_SWIChunkSize-1
+         TSTEQ   R1, #&FF000000
+         MOVNE   PC, lr                         ; naff chunk number.
+         CMP     R1, #0
+         LDRNE   R0, [R12, #Module_SWIEntry]
+         CMPNE   R0, #0
+         BEQ     %FT01
+         TST     R0, #3
+         MOVNE   PC, lr
+         Push   "R5"
+         LDR     R5, [R12, #-4]
+         CMP     R5, R0
+         Pull   "R5"
+01       BICLSS  PC, lr, #Z_bit                 ; NE return
+         ADD     R0, R0, R12
+         ORRS    PC, lr, #Z_bit                 ; EQ for success
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Takes R9 pointer to module node; frees any module SWI hashtab node
+
+FreeSWIEntry ROUT
+         Push   "R0-R4, R12, lr"
+         BL      CheckForSWIEntries
+         Pull   "R0-R4, R12, PC", NE, ^
+         MOV     R3, R1                ; copy of SWIno
+         ModSWIHashval R1
+         LDR     R2, [R1], #-ModSWINode_Link
+
+  ; R1 predecessor, R2 currnode, R0 call address, R3 SWIno
+  ; look down chain until find right call address and number
+01       CMP     R2, #0
+         Pull   "R0-R4, R12, PC", EQ, ^
+         LDR     R4, [R2, #ModSWINode_CallAddress]
+         CMP     R4, R0
+         LDREQ   R4, [R2, #ModSWINode_Number]
+         CMPEQ   R4, R3
+         MOVNE   R1, R2
+         LDRNE   R2, [R2, #ModSWINode_Link]
+         BNE     %BT01
+         LDR     R4, [R2, #ModSWINode_Link]
+         STR     R4, [R1,#ModSWINode_Link]
+         BL      FreeSysHeapNode
+         Pull   "R0-R4, R12, PC",,^
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+FreeIncarnation  ROUT
+  ; copy error, free any workspace, incarnation link.
+  ;   R12 incarnation pointer
+         Push   "R0-R2, lr"
+         BL      Module_CopyError
+         STR     R0, [stack]
+         LDR     R2, [R12, #Incarnation_Workspace]
+         CMP     R2, #0
+         MOV     R0, #HeapReason_Free
+         MOV     R1, #RMAAddress
+         SWINE   XOS_Heap
+         MOV     R2, R12
+         BL      FreeSysHeapNode
+         Pull   "R0-R2, PC",,^
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; KillAndFree:
+;      R0 -> module
+;      R2 -> prevmodule
+;  Kills all incarnations, frees all space.
+
+KillAndFree      ROUT
+         Push   "R2, R3, R9, R12, lr"
+         MOV     R9, R0
+         ADDS    R3, R9, #Module_incarnation_list  ; ensure V clear
+01       LDR     R12, [R9, #Module_incarnation_list]
+         BL      KillIncarnation
+         Pull   "R2, R3, R9, R12, PC", VS
+         CMP     R9, #0
+         BNE     %BT01                      ; more incarnations yet
+         Pull   "R2, R3, R9, R12, PC"
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; KillIncarnation
+;     R9  module ptr
+;     R12 incarnation ptr
+;     R3  previous incarnation
+;     R2  previous module
+; Exit: R9 zeroed if module completely gone
+;
+KillIncarnation  ROUT
+         Push   "R0-R2, R10, lr"
+         MOV     R10, #1                     ; fatal die
+         CMP     r12, #0                     ; fudge for 0 incarnations:
+         BLNE    CallDie                     ; tidy up anyway
+         STRVS   R0, [stack]
+         Pull   "R0-R2, R10, PC", VS
+
+         MOV     R2, R12
+         BL      FreeSysHeapNode                ; free incarnation node
+01       LDR     R0, [R9, #Module_incarnation_list]
+         CMP     R0, #0                      ; last incarnation?
+         LDREQ   R2, [stack, #2*4]
+         BLEQ    DelinkAndFreeModule
+         MOVEQ   R9, #0
+         Pull   "R0-R2, R10, PC",,^
+
+LoseModuleSpace_if_its_the_only_incarnation
+         Push   "R0-R2, R10, lr"
+         B       %BT01
+
+;*************************************************************
+; CallDie
+;    take R9  -> module node
+;         R12 -> incarnation node
+;         R3  -> previous incarnation
+;         R10 =  fatality
+; check against CAO
+; delink incarnation, issue die, deal with workspace.
+
+CallDie ROUT
+         Push   "R0-R6, R11, R12, lr"
+
+         MOV     R11, #0
+         LDR     R11, [R11, #Curr_Active_Object]
+
+ ; check killability
+
+         LDR     R0,  [R9, #Module_code_pointer]
+         BL      %FT10                        ; is_CAO?
+         BLT     %FT04                        ; code block is CAO
+
+ ; check if workspace block could be CAO: may have handler in there
+
+         LDR     R0, [R12, #Incarnation_Workspace]
+         BL      %FT20                        ; check block before getting size
+         BVS     ModuleIsntCAO                ; not heap block - we don't
+         BL      %FT10                        ; know what's going on.
+         BGE     ModuleIsntCAO                ; not CAO
+
+04       CMP     R10, #0                      ; fatal?
+         BNE     CantKill
+         LDR     R0, [R12, #Incarnation_Workspace]
+         BL      %FT20
+         BVS     ModuleIsntCAO                ; soft die of non-heap module OK
+CantKill
+         ADR     R0, ErrorBlock_CantKill
+       [ International
+         BL      TranslateError
+       ]
+01       STR     R0, [stack]
+         LDR     R3, [stack, #4*3]
+         LDR     R12, [stack, #4*(6+2)]
+         STR     R12, [R3, #Incarnation_Link] ; relink
+         Pull   "R0-R6, R11, R12, lr"
+         ORRS    PC, lr, #V_bit
+         MakeErrorBlock CantKill
+
+ModuleIsntCAO
+         MOV     R11, #0                     ; set R11 to incarnation number.
+         LDR     R0, [R9, #Module_incarnation_list]
+03       CMP     R0, R12
+         ADDNE   R11, R11, #1
+         LDRNE   R0, [R0, #Incarnation_Link]
+         BNE     %BT03
+
+         LDR     R0, [R12, #Incarnation_Link]
+         STR     R0, [R3, #Incarnation_Link] ; delink
+         ADD     R12, R12, #Incarnation_Workspace
+         LDR     R1, [R9, #Module_code_pointer]
+         LDR     R0, [R1, #Module_Die]
+         BIC     R0, R0, #&80000000          ; knock out invincibility bit
+         CMP     R0, #0                      ; WARNING: don't try to combine these 2 instructions in a BICS, V is used below
+         MOV     lr, PC
+         ADDNE   PC, R1, R0                  ; call.
+         BVS     %BT01
+         BL      FreeSWIEntry
+
+         CMP     R10, #0                     ; soft die?
+         BEQ     %FT02
+
+         LDR     R12, [stack, #4*(6+2)]
+         LDR     R2, [R12, #Incarnation_Workspace]
+         CMP     R2, #0
+         MOVNE   R1, #RMAAddress
+         MOVNE   R0, #HeapReason_Free
+         SWINE   XOS_Heap
+         MOV     R0, #0
+         STR     R0, [R12, #Incarnation_Workspace]   ; orgone
+02
+         Pull   "R0-R6, R11, R12, lr"
+         BICS    PC, lr, #V_bit
+
+; check if block @ R0 contains address R11
+10       LDR     R1, [R0, #-4]
+         ADD     R1, R1, R0
+         CMP     R0, R11
+         CMPLS   R11, R1
+         MOV     PC, lr                      ; return LT for Yes
+
+; check block @ R0 is a valid RMA heap block
+20
+         Push   "R0-R3, lr"
+         MOV     R2, R0
+         MOV     R0, #HeapReason_ExtendBlock
+         MOV     R1, #RMAAddress
+         MOV     R3, #0
+         SWI     XOS_Heap
+         Pull   "R0-R3, PC"                 ; V set if not.
+
+;*************************************************************
+; DelinkAndFreeModule
+;       R9 -> Module
+;       R2 -> prevmodule
+
+DelinkAndFreeModule ROUT
+        Push    "R0-R2, lr"
+
+;   loop here to find predecessor; make death re-entrant
+        MOV     R0, #Module_List
+01
+        LDR     R1, [R0, #Module_chain_Link]
+        CMP     R1, R9
+        MOVNE   R0, R1
+        BNE     %BT01
+
+        LDR     R1, [R9, #Module_chain_Link]
+        STR     R1, [R0, #Module_chain_Link] ; delinked
+
+        LDR     R2, [R9, #Module_code_pointer]
+        MOV     R1, #RMAAddress
+        MOV     R0, #HeapReason_Free
+        SWI     XOS_Heap
+
+        MOV     R2, R9
+        BL      FreeSysHeapNode
+
+        Pull   "R0-R2, PC",,^
+
+;*************************************************************************
+;  common lookup for reinit, enter, die
+
+lookup_commoned ROUT
+       BIC      lr, lr, #I_bit
+       TEQP     PC, #SVC_mode
+       Push    "lr"
+       BL       LookUp_Module               ; node ptr in R0
+       BEQ      %FT01                       ; not found
+       CMP      R12, #0
+       BLT      %FT02                       ; incarnation not found
+       MOV      R9, R0
+       Pull    "PC"
+01
+       ADR      R0, ErrorBlock_RMNotFound
+       Push    "r1-r6"
+       MOV      r3, #0
+       LDR      r3, [r3, #IRQsema]
+       CMP      r3, #0
+       BNE      %FT03
+     [ International
+       MOV      R4, R1
+       BL       TranslateError_UseR4
+       Push     "r0"
+     |
+       BL       GetOscliBuffer
+       Push     r5
+       LDR      r2, [r0], #4
+       STR      r2, [r5], #4
+       BL       rmecopystr
+copynfrmname
+       LDRB     r2, [r1], #1
+       CMP      r2, #32
+       STRGTB   r2, [r5], #1
+       BGT      copynfrmname
+
+       BL       rmecopystr
+       STRB     r2, [r5]                   ; terminate
+     ]
+       Pull    "r0-r6"
+
+
+03
+       Pull    "lr"
+       ORRS     PC, lr, #V_bit
+       MakeErrorBlock  RMNotFound
+02
+       ADR      R0, ErrorBlock_IncarnationNotFound
+     [ International
+       BL      TranslateError
+     ]
+       B        %BT03
+       MakeErrorBlock  IncarnationNotFound
+
+MakeNotFoundError                         ; r1 -> module name
+       Push     lr
+       B        %BT01
+
+;*************************************************************
+; Lookup_Module
+; Entry:  R1  -> module name
+; Exit:   R0  -> module chain node   (0 for not found)
+;         R1  -> postfix of name
+;         R12 -> incarnation node    (0 for not specified, -1 for not found)
+;         R2  -> previous module      for potential delinking
+;         R3  -> previous incarnation  "      "         "
+;         NE for found/EQ for not
+
+
+LookUp_Module ROUT
+         Push   "R4, R5, lr"
+         TEQP    PC, #SVC_mode               ; interrupts on
+         LDR     R2, =Module_List
+01       LDR     R0, [R2, #Module_chain_Link]
+         CMP     R0, #0
+         Pull   "R4, R5, PC", EQ             ; return if not found
+         LDR     R4, [R0, #Module_code_pointer]
+         LDR     R3, [R4, #Module_Title]
+         ADD     R3, R3, R4                  ; got ptr to title
+         MOV     R4, #Postfix_Separator      ; allowed terminator for StrCmp
+         BL      Module_StrCmp               ; compare with abbreviation.
+         MOVNE   R2, R0
+         BNE     %BT01                       ; loop if not found
+         LDRB    R4, [R1], #1                ; get terminator
+         CMP     R4, #Postfix_Separator
+         BEQ     %FT02
+
+   ; now a quick fudge to spot recursive ModHand calls during module death.
+         LDR     R12, [R0, #Module_incarnation_list]
+         CMP     R12, #0
+         MOVEQ   R12, #-1                    ; no incarnations!
+         MOVNE   R12, #0                     ; no postfix/incarnation specified
+         CMP     PC, #0
+         Pull   "R4, R5, PC"                 ; back with NE
+
+02       LDRB    R4, [R1]
+         CMP     R4, #" "
+         Pull   "R4, R5, lr", LE
+         ORRLES  PC, lr, #Z_bit              ; not found: naff postfix
+         Push   "R1"                         ; updated value to return
+         BL      FindIncarnation
+         MOVEQ   R12, #-1                    ; failed to find postfix.
+         CMP     PC, #0                      ; force NE
+         Pull   "R1, R4, R5, PC"             ; back with NE
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; FindIncarnation
+;    R0 is node pointer, need to loop to find incarnation ($R1)
+;    Return EQ if not found,
+;           NE => R12 -> incarnation, R3 -> previnc
+
+FindIncarnation  ROUT
+         Push   "lr"
+         ADD     R3, R0, #Module_incarnation_list  ; previnc
+03       LDR     R12, [R3, #Incarnation_Link]
+         CMP     R12, #0
+         Pull   "PC", EQ                     ; failed to find postfix.
+ [ Fix9
+         Push   "R3,R4"
+         ADD     R3, R12, #Incarnation_Postfix
+         MOV     R4, #0                      ; no special terminator
+         BL      Module_StrCmp
+         Pull   "R3,R4"
+ |
+         Push   "R3"
+         ADD     R3, R12, #Incarnation_Postfix
+         BL      Module_StrCmp
+         Pull   "R3"
+ ]
+         MOVNE   R3, R12
+         BNE     %BT03
+         CMP     PC, #0                      ; force NE
+         Pull   "PC"
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       Module_StrCmp
+;
+; Do a string comparison, given pointers in R1, R3.
+; Ignore case, allow $R1 to be an abbreviation of $R3
+; Strings are terminated by ctrl-char, or ASC(R4) for $R1
+;
+; out:  EQ => match found, R1 -> terminator of R1 string
+;       NE => match not found, R1 preserved
+;       R3 corrupted in all cases
+;
+
+Module_StrCmp ENTRY "r1,r2,r5-r7"
+        MOV     r2, #0
+01
+        LDRB    r7, [r1], #1
+        LDRB    r5, [r3], #1
+        CMP     r7, r4
+        CMPNE   r7, #32
+        CMPLE   r5, #32
+        BLE     %FT02
+        UpperCase r7, r6
+        UpperCase r5, r6
+        CMP     r7, r5
+        ADDEQ   r2, r2, #1
+        BEQ     %BT01
+        CMP     r2, #0
+        MOV     r2, #Z_bit
+        TEQP    r2, pc                          ; invert EQ/NE
+        CMPEQ   r7, #"."                        ; success if abbreviation
+        EXIT    NE
+        CMP     r5, #" "                        ; reject abbreviation
+        EXIT    LT                              ; after full match
+        ADD     r1, r1, #1
+02
+        SUB     r1, r1, #1
+        CMP     r2, #0                          ; reject 0-length match
+        MOV     r2, #Z_bit
+        TEQP    r2, pc                          ; invert EQ/NE
+        STREQ   r1, [stack]                     ; r1 -> terminator
+        EXIT                                    ; return with success
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+EnvStringSkipName ROUT
+         Push   "R0"
+01       LDRB    R0, [R10], #1
+         CMP     R0, #" "
+         BGT     %BT01
+02       LDREQB  R0, [R10], #1
+         CMP     R0, #" "
+         BEQ     %BT02
+         SUB     R10, R10, #1
+         Pull   "R0"
+         MOV     PC, lr
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; PreferIncarnation
+;    R9  -> module node
+;    R12 -> incarnation node
+;    R3  -> previous incarnation
+PreferIncarnation
+         Push   "R0"
+         LDR     R0,  [R12, #Incarnation_Link]
+         STR     R0,  [R3,  #Incarnation_Link]
+         LDR     R0,  [R9,  #Module_incarnation_list]
+         STR     R0,  [R12, #Incarnation_Link]
+         STR     R12, [R9,  #Module_incarnation_list]
+         Pull   "R0"
+         MOV     PC, R14
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Module_CopyError ROUT
+; grab an oscli buffer for the error,
+; rather than having a permanent module buffer
+       Push     "R0, R2, R5, R6, lr"
+       BL        GetOscliBuffer
+
+       STR       R5, [stack]
+       LDR       R2, [R0], #4
+       STR       R2, [R5], #4
+01     LDRB      R2, [R0], #1
+       STRB      R2, [R5], #1
+       CMP       R2, #0
+       BNE       %BT01
+       Pull     "R0, R2, R5, R6, PC"
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;  Claim chunk from RMA : increase RMA if can,
+;  force size to multiple of 16 -4 to keep alignment OK
+
+RMAClaim_Chunk   ROUT
+         MOV     R0, #HeapReason_Get
+         Push   "R0, R3, lr"
+
+         ADD     R3, R3, #15+4               ; now force size to 16*n-4
+         BIC     R3, R3, #15                 ; so heap manager always has
+         SUB     R3, R3, #4                  ;  4-word aligned blocks
+
+         B       IntoRMAHeapOp
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+DoRMAHeapOpWithExtension
+         Push   "R0, R3, lr"
+
+IntoRMAHeapOp
+         MOV     R1, #RMAAddress
+         SWI     XOS_Heap
+         Pull   "R0, R3, PC", VC
+
+         LDR     r14, [r0]                   ; look at error number
+         TEQ     r14, #ErrorNumber_HeapFail_Alloc
+         STRNE   r0, [stack]
+         Pull   "r0, r3, PC", NE            ; can only retry if ran out of room
+
+         Push    r3                         ; in case extension
+         LDR     r1, [stack, #4]
+         CMP     r1, #HeapReason_ExtendBlock
+         BNE     notRMAextendblock
+         Push   "r5, r6"
+         LDR     r1, [r2, #-4]               ; pick up block size
+         ADD     r5, r1, r2                  ; block end +4
+         SUB     r5, r5, #4                  ; TMD 02-Aug-93: block size includes size field (optimisation was never taken)
+         MOV     r6, #RMAAddress
+         LDR     r6, [r6, #:INDEX:hpdbase]
+         ADD     r6, r6, #RMAAddress         ; free space
+         CMP     r5, r6                      ; does block butt against end?
+         ADDNE   r3, r3, r1                  ; max poss size needed
+         Pull   "r5, r6"
+
+  ; note that this doesn't cope well with a block at the end preceded by a
+  ; free block, but tough.
+
+notRMAextendblock
+         MOV     r1, #RMAAddress
+         LDR     R0, [R1, #:INDEX: hpdbase]
+         LDR     R1, [R1, #:INDEX: hpdend]
+         SUB     R1, R1, R0                  ; bytes free
+         SUB     R1, R3, R1                  ; bytes needed
+
+         Pull    r3
+         ADD     R1, R1, #8                  ; safety factor
+
+         MOV     R0, #1                      ; try and expand RMA.
+         SWI     XOS_ChangeDynamicArea
+         Pull   "R0"                         ; heap reason code back
+         MOV     R1, #RMAAddress
+         SWIVC   XOS_Heap
+01
+         ADRVSL  R0, ErrorBlock_MHNoRoom
+       [ International
+         Push   "LR",VS
+         BLVS    TranslateError
+         Pull   "LR",VS
+       ]
+         Pull   "r3, PC"
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Data
+
+crstring =       13
+         ALIGN
+
+;*****************************************************************************
+; *Unplug code.
+
+Unplug_Code ENTRY "r7-r9"
+        CMP     r1, #0
+        BNE     ZapTheModule
+
+; If name not given, list unplugged modules
+
+;       MOV     r1, #0                          ; start with module 0 (r1 is already zero indicating no parameters to command!)
+        MOV     r2, #-1                         ; start with main ROMs
+        MOV     r7, #0                          ; flag indicating whether we've had any unplugged modules already
+10
+        MOV     r0, #ModHandReason_EnumerateROM_ModulesWithInfo
+        SWI     XOS_Module
+        BVS     %FT30                           ; no more, so finish off
+        CMP     r4, #-1                         ; is it unplugged?
+        BNE     %BT10                           ; no, then go for another one
+
+        MOV     r8, r1                          ; save module and podule numbers
+        MOV     r9, r2
+        TEQ     r7, #0                          ; if already printed header message
+        BNE     %FT20                           ; then skip
+      [ International
+        BL      WriteS_Translated
+        =       "Unp:Unplugged modules are:", 10, 13, 0
+        ALIGN
+      |
+        ADR     r0, AreUnpluggedMessage
+        SWI     XOS_Write0
+      ]
+        EXIT    VS
+        MOV     r7, #1
+20
+        MOV     r0, r3
+        SWI     XOS_Write0
+        EXIT    VS
+        CMP     r2, #-1
+        BEQ     %FT25                           ; if a main ROM, then no blurb after name (and V=0)
+      [ International
+        MOV     r4, r2
+      |
+        ADRGT   r4, podbra                      ; is a podule module
+        ADRLT   r4, extnbra                     ; is an extn rom module
+      ]
+        MVNLT   r2, r2                          ; so invert number
+        SUB     r0, r0, r3                      ; length of module name
+        RSB     r0, r0, #24                     ; number of spaces to pad out to column 24 (may be -ve)
+21
+        SWI     XOS_WriteI + " "                ; always print at least one space
+        EXIT    VS
+        SUBS    r0, r0, #1
+        BGT     %BT21
+      [ :LNOT: International
+        MOV     r0, r4
+        SWI     XOS_Write0
+        EXIT    VS
+      ]
+        SUB     sp, sp, #3*4                    ; make buffer on stack
+        MOV     r0, r2
+        MOV     r1, sp
+        MOV     r2, #3*4
+        SWI     XOS_ConvertCardinal4
+      [ International
+        CMP     r4,#-1
+        MOV     r4,r0                           ; r4 -> number
+        BLT     %FT23
+        BL      WriteS_Translated_UseR4
+        =       "Podule:(Podule %0)",0
+        ALIGN
+        B       %FT24
+23
+        BL      WriteS_Translated_UseR4
+        =       "Extn:(Extn ROM  %0)",0
+        ALIGN
+24
+        ADD     sp, sp, #3*4                    ; restore stack
+      |
+        SWIVC   XOS_Write0
+        ADD     sp, sp, #3*4                    ; restore stack
+        SWIVC   XOS_WriteI + ")"
+      ]
+25
+        SWIVC   XOS_NewLine
+        MOVVC   r1, r8                          ; restore module and podule number
+        MOVVC   r2, r9
+        BVC     %BT10
+        EXIT
+
+30
+        CMP     r7, #0                          ; NB will clear V in any case
+      [ International
+        BNE     %FT31
+        BL      WriteS_Translated
+        =       "NoUnp:No modules are unplugged", 10, 13, 0
+        ALIGN
+31
+        EXIT
+      |
+        ADREQ   r0, NoUnpluggedMessage
+        SWIEQ   XOS_Write0
+        EXIT                                    ; exit with V=0 unless error in Write0
+
+AreUnpluggedMessage
+        =       "Unplugged modules are:", 10, 13, 0
+NoUnpluggedMessage
+        =       "No modules are unplugged", 10, 13, 0
+podbra
+        =       "("
+rommposp
+        =       "Podule ", 0
+extnbra
+        =       "("
+rommposer
+        =       "Extn ROM ", 0
+        ALIGN
+      ]
+
+ZapTheModule
+        MOV     r9, #0                          ; indicate unplug, not insert
+UnplugInsertEntry
+        MOV     r12, #0                         ; search from start of chain
+        MOV     r7, r0                          ; name pointer
+        MOV     r4, #0                          ; no extra terminator
+        MOV     r5, #0                          ; indicate no versions found yet
+
+        MOV     r6, #0                          ; indicate no version found that was initialised
+        MOV     r1, r7
+        BL      SkipToSpace                     ; leaves r1 pointing to 1st space or control char
+        BL      SkipSpaces                      ; leaves r1 -> 1st non-space, r0 = 1st non-space char
+        CMP     r0, #&7F
+        CMPNE   r0, #" "                        ; if a ctrl char, then
+        MOVLS   r8, #&80000000                  ; indicate to unplug all versions
+        BLS     %FT40
+        CMP     r0, #"-"
+        ADDEQ   r1, r1, #1
+        MOVEQ   r8, #-1
+        MOVNE   r8, #1
+        MOV     r0, #1 :SHL: 31                 ; check terminator is control char or space
+        SWI     XOS_ReadUnsigned
+        EXIT    VS
+        MUL     r8, r2, r8                      ; apply sign
+40
+        MOV     r1, r7
+        BL      FindROMModule
+        TEQ     r12, #0
+        BEQ     %FT60                           ; no versions of this module found, so report error
+
+42
+        LDR     r14, [r12, #ROMModule_OlderVersion]     ; find oldest version of this module
+        TEQ     r14, #0
+        MOVNE   r12, r14
+        BNE     %BT42
+
+45
+        TEQ     r8, #&80000000                  ; if not doing any old podule
+        LDRNE   r14, [r12, #ROMModule_PoduleNumber]
+        TEQNE   r14, r8                         ; and podule number doesn't match
+        BNE     %FT50                           ; then skip this one
+
+        LDRB    r14, [r12, #ROMModule_Initialised] ; if this version of CODE was initialised then keep pointer to it
+        TEQ     r14, #0
+        MOVNE   r6, r12                         ; save pointer to it
+        MOV     r5, #&FF                        ; set up byte mask (and indicate found)
+        LDR     r1, [r12, #ROMModule_CMOSAddrMask]
+        AND     r3, r5, r1, LSR #16             ; get bit mask
+        ANDS    r1, r1, r5
+        BEQ     %FT50                           ; if no CMOS, then look for another module
+        MOV     r0, #ReadCMOS
+        SWI     XOS_Byte
+        EXIT    VS
+        TEQ     r9, #0
+        ORREQ   r2, r2, r3                      ; set unplug bit
+        BICNE   r2, r2, r3                      ; or clear it as appropriate
+        MOV     r0, #WriteCMOS
+        SWI     XOS_Byte
+        EXIT    VS
+50
+        LDR     r14, [r12, #ROMModule_NewerVersion] ; go to next newer version
+        TEQ     r14, #0
+        MOVNE   r12, r14
+        BNE     %BT45
+
+60
+        TEQ     r5, #0                          ; if we've seen any versions, then don't report error
+        BNE     %FT70
+        ADR     r0, ErrorBlock_RMNotFoundInROM
+      [ International
+        BL      TranslateError
+      |
+        SETV
+      ]
+        EXIT
+
+70
+        CMP     r9, #1                          ; if doing unplug not insert
+        CMPNE   r6, #0                          ; and we found a match on an initialised version (else V=0)
+ [ 1 = 1
+;RCM's fix for MED-04173
+        EXIT    EQ
+
+; see if module is active, by checking for module in module list
+        MOV     r0, #Module_List
+60
+        LDR     r0, [r0, #Module_chain_Link]
+        TEQ     r0, #0                          ; module not active
+        BEQ     %FT90
+        LDR     r1, [r0, #Module_ROMModuleNode] ; get active module's pointer to ROM module node
+        TEQ     r1, r12                         ; if it matches
+        BNE     %BT60
+
+        MOV     r0, #ModHandReason_Delete       ; then tell him he's dead
+        LDR     r1, [r6, #ROMModule_Name]
+        SWI     XOS_Module
+90
+ |
+        MOVNE   r0, #ModHandReason_Delete       ; then tell him he's dead
+        LDRNE   r1, [r6, #ROMModule_Name]
+        SWINE   XOS_Module
+ ]
+        EXIT
+
+RMInsert_Code ALTENTRY
+        MOV     r9, #1                          ; indicate insert, not unplug
+        B       UnplugInsertEntry
+
+        MakeErrorBlock RMNotFoundInROM
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        END
diff --git a/s/MoreComms b/s/MoreComms
new file mode 100644
index 0000000000000000000000000000000000000000..0a55cdce6e91028bb4e3368ee99df6a4c64a27ed
--- /dev/null
+++ b/s/MoreComms
@@ -0,0 +1,531 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL     => MoreComms
+
+Error_Code ROUT
+        ; Use MessageTrans to construct an error block by substituting the error text into
+        ; an error
+
+        Push    "r7,r8,lr"
+
+        ASSERT  ?Error_CodeTag <= 8
+        ADR     lr, Error_CodeTag
+        LDMIA   lr, {r1,r2}
+        Push    "r1,r2"
+
+        MOV     r1, r0          ; pointer
+        MOV     r0, #0          ; base
+        SWI     XOS_ReadUnsigned
+        Push    "r2"
+
+        MOV     r0, sp          ; Error block
+        BL      TranslateError_VClear
+
+        ; If error number's changed don't substitute
+        LDR     r3, [r0]
+        TEQ     r3, r2
+        BNE     %FT90
+
+        ; GSTrans the text into the error buffer
+        MOV     r3, r0
+        MOV     r0, r1
+        ADD     r1, r3, #4
+        MOV     r2, #252
+        SWI     XOS_GSTrans
+
+        ; Ensure 0-terminated
+        MOV     r0, #0
+        CMP     r2, #252
+        MOVHI   r2, #252
+        STRB    r0, [r1, r2]
+
+        MOV     r0, r3
+90
+        ADD     sp, sp, #12
+        Pull    "r7,r8,lr"
+        ORRS    pc, lr, #V_bit
+
+Error_CodeTag DCB "ErrSub", 0
+
+        LTORG
+
+Eval_Code ROUT
+             Push  "lr"
+             LDR    R1, =GeneralMOSBuffer
+             MOV    R2, #256
+             SWI    XOS_EvaluateExpression
+             Pull  "PC", VS
+           [ :LNOT: International
+             SWI    XOS_WriteS
+             =     "Result is a",0
+             ALIGN
+             Pull  "PC", VS
+             CMP    R1, #0
+             ADREQ  R0, %FT01
+             ADRNE  R0, %FT02
+             SWI    XOS_Write0
+             Pull  "PC", VS
+             SWI    XOS_WriteS
+             =     ", value : ",0
+             ALIGN
+             Pull  "PC", VS
+           |
+             Push   "r4"
+             CMP    R1, #0
+             ADREQ  R0, %FT01
+             ADRNE  R0, %FT02
+             BL     FindToken
+             MOV    R4,R0
+             BL     WriteS_Translated_UseR4
+             =      "Result:Result is %0, value :",0
+             ALIGN
+             Pull   "r4"
+
+           ]
+             LDREQ  R1, =GeneralMOSBuffer
+             MOVEQ  R0, R2
+             MOVEQ  R2, #256
+             SWIEQ  XOS_BinaryToDecimal
+             MOV    R5, #-1
+03           ADD    R5, R5, #1
+             CMP    R5, R2
+             BEQ    %FT04
+             LDRB   R0, [R1, R5]
+             CMP    R0, #&7F
+             MOVEQ  R0, #"?"-"@"   ; preversion!!
+             CMP    R0, #31
+             ADDLE  R0, R0, #"@"
+             SWILE  XOS_WriteI+"|"
+             Pull  "PC", VS
+             CMP    R0, #"|"
+             CMPNE  R0, #""""
+             CMPNE  R0, #"<"
+             SWIEQ  XOS_WriteI+"|"
+             SWIVC  XOS_WriteC
+             BVC    %BT03
+
+04           SWIVC  XOS_NewLine
+             Pull  "PC"
+
+        [ International
+01
+    =    "Integer:an integer",0
+02
+    =    "String:a string",0
+        |
+01
+    =    "n integer",0
+02
+    =    " string",0
+        ]
+
+    ALIGN
+
+;****************************************************************************
+; Coupla utility commands
+
+Time_Code    ROUT
+     Push   "lr"
+     LDR     R1, =GeneralMOSBuffer
+     MOV     R0, #0
+     STRB    R0, [R1]
+     MOV     R0, #14
+     SWI     XOS_Word
+     MOVVC   R0, R1
+     MOVVC   R1, #24
+     SWIVC   XOS_WriteN
+     SWIVC   XOS_NewLine
+     Pull   "PC"
+
+
+Ignore_Code  ROUT
+     Push   "lr"
+     MOVS    R4, R1
+     MOV     R1, R0
+     MOV     R0, #10+ (1:SHL:30)
+     SWINE   XOS_ReadUnsigned
+     Pull   "PC", VS
+     MOV     R6, R2        ; maybe number
+     BL      CheckEOL
+     BNE     %FT01
+
+     CMP     R4, #0
+     MOV     R0, #&B6
+     MOVEQ   R1, #255
+     MOVNE   R1, #0
+     MOV     R2, #0
+     SWI     XOS_Byte
+     MOV     R0, #6
+     MOV     R1, R6
+     SWINE   XOS_Byte
+     Pull   "PC"
+
+01   ADRL    R0, ErrorBlock_BadNumb
+   [ International
+     BL      TranslateError
+   |
+     SETV
+   ]
+     Pull   "PC"
+
+;*****************************************************************************
+
+ROMModules_Code ENTRY
+      [ International
+        BL      GSWriteS_Translated
+        =       "ROMMTitle:No. Position|IModule Name|I|IVersion|IStatus|M|J",0
+        EXIT    VS
+      |
+        ADRL    r0, romm_helpstr
+        MOV     r1, #0
+        SWI     XOS_PrettyPrint
+        EXIT    VS
+      ]
+        MOV     r1, #0
+        MOV     r2, #-1
+01
+        SWI     XOS_ReadEscapeState
+        PullEnv CS
+        BCS     AckEscape
+        MOV     r0, #ModHandReason_EnumerateROM_ModulesWithInfo
+        SWI     XOS_Module
+        EXITS   VS                                      ; exit V clear
+
+; R1 = module number +1
+; R2 = podule number
+; R3 -> name
+; R4 = status (-1 unplugged, 0 dormant, 1 active, 2 running)
+; R5 = chunk number
+; R6 = version number
+
+; Copy info into buffer and prettyprint
+
+        MOV     r5, r1                                  ; save r1 and r2 for next call to OS_Module
+        MOV     r10, r2
+        MOV     r0, r1
+        LDR     r1, =GeneralMOSBuffer
+        MOV     r2, #256
+        SWI     XOS_ConvertCardinal2
+        SUB     r12, r1, r0                             ; characters in buffer
+02
+        CMP     r12, #3                                 ; tab out to 3 characters
+        SWICC   XOS_WriteI+" "
+        EXIT    VS
+        ADDCC   r12, r12, #1
+        BCC     %BT02
+        MOV     r0, #" "
+        BL      %FT20                                   ; add space
+        CMP     r10, #-1
+      [ International
+        ADREQL  r0, rommpossysrom
+        ADRLTL  r0, rommposextrom
+        ADRGTL  r0, rommpospodule
+        BL      FindToken
+      |
+        ADREQL  r0, rommpossr
+        ADRLTL  r0, rommposer
+        ADRGTL  r0, rommposp
+      ]
+        BL      %FT21                                   ; add string
+        MOVGT   r0, r10                                 ; if normal podule then use plain number (flags still set from CMP)
+        MVNLT   r0, r10                                 ; if extension ROM then NOT it (-2 => 1, -3 => 2 etc)
+        SWINE   XOS_ConvertCardinal1
+        MOV     r0, #TAB
+        BL      %FT20                                   ; tab to col. 16
+        MOV     r0, r3
+        BL      %FT21                                   ; copy name in
+        MOV     r3, r0                                  ; string length
+        MOV     r0, #TAB
+03
+        CMP     r3, #24
+        ADDCC   r3, r3, #8
+        BLCC    %FT20
+        BCC     %BT03
+
+        MOV     r11, #"0"
+        TST     r6, #&F0000000                          ; 1st digit of integer part
+        ORRNE   r0, r11, r6, LSR #28
+        BLNE    %FT20
+        MOV     r6, r6, LSL #4
+        TSTEQ   r6, #&F0000000                          ; 2nd digit of integer part
+        ORRNE   r0, r11, r6, LSR #28
+        BLNE    %FT20
+        MOV     r6, r6, LSL #4
+        TSTEQ   r6, #&F0000000                          ; 3rd digit of integer part
+        ORRNE   r0, r11, r6, LSR #28
+        BLNE    %FT20
+        MOV     r6, r6, LSL #4
+        ORR     r0, r11, r6, LSR #28                    ; 4th digit of integer part
+        BL      %FT20
+        MOV     r0, #"."
+        BL      %FT20
+        MOV     r6, r6, LSL #4
+        ORR     r0, r11, r6, LSR #28                    ; 1st digit of decimal part
+        BL      %FT20
+        MOV     r6, r6, LSL #4
+        ORR     r0, r11, r6, LSR #28                    ; 2nd digit of decimal part
+        BL      %FT20
+        MOVS    r6, r6, LSL #4                          ; only print 3rd and 4th digits of decimal part if non-zero
+        ORRNE   r0, r11, r6, LSR #28
+        BLNE    %FT20
+        MOVS    r6, r6, LSL #4
+        ORRNE   r0, r11, r6, LSR #28
+        BLNE    %FT20
+        MOV     r0, #TAB
+        BL      %FT20
+
+        CMP     r4, #0
+      [ International
+        ADRMIL  r0, rommstatu
+        ADREQL  r0, rommstatd
+        ADRGTL  r0, rommstata
+        BL      FindToken
+      |
+        ADRMIL  r0, rommstu
+        ADREQL  r0, rommstd
+        ADRGTL  r0, rommsta
+      ]
+        CMP     r4, #2
+      [ International
+        ADREQL  r0, rommstatr
+        BLEQ    FindToken
+      |
+        ADREQL  r0, rommstr
+      ]
+        BL      %FT21
+        MOV     r0, #13
+        BL      %FT20
+        MOV     r0, #0
+        BL      %FT20
+        LDR     r0, =GeneralMOSBuffer
+        MOV     r1, #0
+        SWI     XOS_PrettyPrint
+        EXIT    VS
+        MOV     r1, r5
+        MOV     r2, r10
+        B       %BT01
+
+; R1 buffer ptr, R2 bufflen left
+
+20
+        SUBS    r2, r2, #1
+        STRPLB  r0, [r1], #1
+        MOVS    pc, lr
+21
+        Push    "r0, lr"
+        MOV     r12, r0
+22
+        LDRB    r0, [r12], #1
+        CMP     r0, #TokenEscapeChar
+        BEQ     %FT23
+        CMP     r0, #0
+      [ International
+        CMPNE   r0, #10
+      ]
+        BLNE    %BT20
+        BNE     %BT22
+        Pull    "r0, lr"
+        SUB     r0, r12, r0       ; length of string
+        SUB     r0, r0, #1
+        MOVS    pc, lr
+
+23
+        BL      %BT20
+        LDRB    r0, [r12], #1
+        BL      %BT20
+        B       %BT22
+
+      [ International
+rommpossysrom
+     =   "SYSROM:System ROM", 0
+rommposextrom
+     =   "EXTROM:Extn ROM", 0
+rommpospodule
+     =   "PODROM:Podule", 0
+
+rommstata
+     =   "Active:Active", 0
+
+rommstatd
+     =   "Dormant:Dormant", 0
+
+rommstatu
+     =   "Unplugged:Unplugged", 0
+
+rommstatr
+     =   "Running:Running",0
+
+     ALIGN
+      ]
+
+;*****************************************************************************
+
+RMEnsure_Code ENTRY "r0, r1"
+        MOV     r1, r0                          ; name pointer
+        MOV     r0, #ModHandReason_LookupName
+        SWI     XOS_Module
+        MOVVS   r10, r0                         ; module handler will build a nice error
+        BVS     RMEDoCommand                    ; module not found
+        LDR     r0, [stack]                     ; now find version number
+01
+        LDRB    r6, [r0], #1
+        CMP     r6, #" "
+        BNE     %BT01
+        BL      RMEGetVerNo
+        MOV     r6, r1
+        LDR     r0, [r3, #Module_HelpStr]
+        TEQ     r0, #0
+        BEQ     RMEDoCommand                    ; no help string, so do command
+        ADD     r0, r0, r3
+        BL      GetVerNoFromHelpString
+        CMP     r1, r6
+        EXIT    GE
+        MOV     r10, #0
+RMEDoCommand
+        Pull    "r0, r1"
+        CMP     r1, #2
+        BEQ     BuildRMEnsureError
+04
+        LDRB    r1, [r0], #1
+        CMP     r1, #" "
+        BNE     %BT04
+05
+        LDRB    r1, [r0], #1
+        CMP     r1, #" "
+        BEQ     %BT05
+06
+        LDRB    r1, [r0], #1
+        CMP     r1, #" "
+        BNE     %BT06
+        SUB     r0, r0, #1
+03
+        SWI     XOS_CLI
+        Pull    PC
+
+BuildRMEnsureError
+        MOVS    r0, r10
+        Pull    lr, NE
+        ORRNES  pc, lr, #V_bit
+        ADR     r0, ErrorBlock_ModuleTooOld
+      [ International
+        LDR     r4,[r3, #Module_Title]
+        ADD     r4,r4,r3
+        BL      TranslateError_UseR4
+        Pull    "LR"
+        ORRS    PC,LR,#V_bit
+      |
+        BL      GetOscliBuffer
+        MOV     r10, r5
+        LDR     r2, [r0], #4
+        STR     r2, [r5], #4
+        BL      rmecopystr
+        MOV     r6, r0
+
+        LDR     r2, [r3, #Module_Title]         ; r3 still module pointer
+        ADD     r0, r2, r3
+        BL      rmecopystr
+        MOV     r0, r6
+        BL      rmecopystr
+        STRB    r2, [r5]                        ; terminate
+        B       BuildRMEnsureError
+      ]
+
+rmecopystr
+        LDRB    r2, [r0], #1
+        CMP     r2, #32
+        STRGEB  r2, [r5], #1
+        BGE     rmecopystr
+        MOV     pc, lr
+
+        MakeErrorBlock ModuleTooOld
+
+; *************************************************************************
+;
+;       RMEGetVerNo - Read version number from a string
+;
+; in:   R0 -> string
+;
+; out:  R0, R4, R5, R12 corrupted
+;       R1 = version number in BCD with the decimal point between bits 15 and 16
+;            eg "2.34" => &00023400, "5.6789" => &00056789, "17" => &00170000
+;            only the last 4 digits of the integer part, and the first 4 decimal places are stored
+;
+
+RMEGetVerNo ENTRY
+        MOV     r1, #0
+10
+        LDRB    r12, [r0], #1
+        CMP     r12, #" "
+        BEQ     %BT10
+11
+        SUB     r12, r12, #"0"
+        CMP     r12, #9
+        ORRLS   r1, r12, r1, LSL #4             ; just keep nibbles - we only need the
+        LDRLSB  r12, [r0], #1                   ; result to be ordered, not continous
+        BLS     %BT11
+        MOV     r5, #0
+        CMP     r12, #"."-"0"
+        BNE     %FT13
+        MOV     r4, #16
+12
+        SUBS    r4, r4, #4
+        BMI     %FT13
+        LDRB    r12, [r0], #1
+        SUB     r12, r12, #"0"
+        CMP     r12, #9
+        ORRLS   r5, r5, r12, LSL r4
+        BLS     %BT12
+13
+        ORR     r1, r5, r1, LSL #16
+        EXIT
+
+; *************************************************************************
+;
+;       GetVerNoFromHelpString - Read version number from a module help string
+;
+; in:   R0 -> module help string
+;
+; out:  R1 = version number in BCD with the decimal point between bits 15 and 16
+;            eg "2.34" => &00023400, "5.6789" => &00056789, "17" => &00170000
+;            only the last 4 digits of the integer part, and the first 4 decimal places are stored
+;       All other registers preserved
+;
+
+GetVerNoFromHelpString ENTRY "r0, r4, r5, r12"
+        MOV     r5, #0                          ; char count
+10
+        LDRB    r1, [r0], #1
+        CMP     r1, #0                          ; check character
+        EXIT    EQ                              ; if end of string then no version number so return zero
+        ADD     r5, r5, #1
+        CMP     r1, #TAB
+        ADDEQ   r5, r5, #7
+        BICEQ   r5, r5, #7
+        CMP     r5, #16                         ; hit verno col yet?
+        BLT     %BT10
+20
+        LDRB    r1, [r0], #1
+        CMP     r1, #TAB
+        CMPNE   r1, #31                         ; if a control character (except TAB)
+        MOVLT   r1, #0                          ; then no version number so return zero
+        EXIT    LT
+        SUB     r1, r1, #"0"
+        CMP     r1, #9                          ; if not a digit
+        BHI     %BT20                           ; then try next character
+        SUB     r0, r0, #1                      ; was a digit so go back to it
+        BL      RMEGetVerNo                     ; read version number from here
+        EXIT
+
+        END
diff --git a/s/MoreSWIs b/s/MoreSWIs
new file mode 100644
index 0000000000000000000000000000000000000000..b82d60853710192d5f375adafdbc8c026f4ab5ba
--- /dev/null
+++ b/s/MoreSWIs
@@ -0,0 +1,1147 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL     => MoreSWIs
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;  SubstituteArgs
+;    in:  R0 -> arglist (space delimited args, terminated by 10,13,0)
+;               top bit set => don't append unused part of line
+;         R1 -> buffer
+;         R2 =  bufflen
+;         R3 -> string to mangle
+;         R4 =  no of chars in $R3
+;    out: R2 =  no of chars in buffer
+
+XOS_SubstituteArgs_code ROUT
+
+      TEQP    PC, #SVC_mode     ; enable IRQs
+
+      Push   "R0-R8, lr"
+      BIC     r0, r0, #&80000000
+      ADD     R8, R4, R3
+
+; try and get parameter positions.
+; Items starting with " can have spaces in
+
+      MOV   R6, #0       ; parameter number
+      LDR   R12, =MacExStartPtrs
+      LDR   R4, =MacExEndPtrs
+35    LDRB  R5, [R0], #1
+      CMP   R5, #" "
+      BEQ   %BT35
+      MOV   R2, #" "
+      CMP   R5, #""""
+      MOVEQ R2, #""""       ; quoted param
+      CMP   r6, #10         ; "rest of line" item?
+      MOVEQ r2, #-1         ; terminate on EOL only
+      SUB   R0, R0, #1
+      STR   R0, [R12, R6, LSL #2]
+      CMP   r5, #""""
+      ADDEQ R0, R0, #1
+36    LDRB  R5, [R0], #1
+      BL    suba_chktrm
+      CMPNE R5, R2
+      BNE   %BT36
+      CMP   R5, #""""
+      LDREQB R5, [R0]
+      CMPEQ R5, #""""   ; check for "" in string
+      ADDEQ R0, R0, #1
+      BEQ   %BT36
+      CMP   R2, #""""
+      SUBNE R0, R0, #1
+      STR   R0, [R4, R6, LSL #2]
+      ADD   R6, R6, #1
+      CMP   R6, #11           ; Parameters 0-9 and a "rest" set.
+      BNE   %BT35
+
+; Keep track of highest param used, so can tack any unused stuff on end.
+; R3 points at string to get chars from
+; R12 at start ptrs
+; R4 at end ptrs
+
+      MOV    R6, #0          ; count.
+      MOV    R7, #0          ; highest param used.
+      LDR    R2, [stack, #4*2]
+37    BL     suba_getchar
+      BEQ    %FT41
+      CMP    R5, #"%"
+      BEQ    %FT44
+38    BL     suba_addchar
+      B      %BT37
+
+PCnotparm
+      ADD    R5, R5, #"0"
+      MOV    R11, R5
+      MOV    R5, #"%"
+      BL     suba_addchar
+      MOV    R5, R11
+      B      %BT38
+
+44    BL     suba_getchar
+      MOVEQ  R5, #"%"
+      BEQ    %FT40
+      CMP    R5, #"%"
+      BEQ    %BT38
+      CMP    R5, #"*"
+      BEQ    DoStarParams
+      SUBS   R5, R5, #"0"
+      BMI    PCnotparm
+      CMP    R5, #9
+      BGT    PCnotparm
+
+; itsa parameter! Get ptrs from R12, R4
+      CMP    R5, R7
+      ADDGE  R7, R5, #1
+      LDR    R11, [R4, R5, LSL #2]
+CopyToR11FromParamR5
+      LDR    R10, [R12, R5, LSL #2]  ; start ptr
+39    LDRB   R5, [R10], #1
+      CMP    R10, R11
+      BGT    %BT37
+      BL     suba_addchar
+      B      %BT39
+
+DoStarParams ; had %* : find limits to copy between
+      BL     suba_getchar
+      BEQ    PCStarTerminates
+      SUBS   R5, R5, #"0"
+      BMI    PCStarNoDigit
+      CMP    R5, #9
+      MOVLE  R7, #11                  ; flag * used
+      LDRLE  R11, [R4, #10*4]         ; always to EOL
+      BLE    CopyToR11FromParamR5
+PCStarNoDigit
+      ADD    R5, R5, #"0"
+      MOV    R11, R5
+      MOV    R5, #"%"
+      BL     suba_addchar
+      MOV    R5, #"*"
+      BL     suba_addchar
+      MOV    R5, R11
+      B      %BT38
+
+PCStarTerminates
+      MOV    R5, #"%"
+      BL     suba_addchar
+      MOV    R5, #"*"
+40    BL     suba_addchar
+41    CMP    r7, #11
+      LDREQ  r12, [r4, #10*4]        ; no more to copy
+      BEQ    %FT42
+      LDR    r0, [stack]
+      CMP    r0, #0
+      LDRPL  R12, [R12, R7, LSL #2]  ; ptr to rest of command line : copy
+      LDRMI  r12, [r4, #10*4]        ; caller wants no appending.
+42    LDRB   R5, [R12], #1
+      BL     suba_addchar
+      BL     suba_chktrm
+      BNE    %BT42
+
+      STR    R6, [stack, #4*2]
+      Pull  "R0-R8, lr"
+      ExitSWIHandler
+
+suba_addchar
+      ADD    R6, R6, #1
+      CMP    R6, R2
+      STRNEB R5, [R1], #1
+      MOVNES PC, lr
+
+      ADRL   R0, ErrorBlock_BuffOverflow
+    [ International
+      BL     TranslateError
+    ]
+      STR    R0, [stack]
+      Pull  "R0-R8, lr"
+      B     SLVK_SetV
+
+suba_getchar
+      CMP    R3, R8
+      LDRNEB R5, [R3], #1
+      MOV    PC, lr
+
+suba_chktrm
+      CMP   R5, #13
+      CMPNE R5, #10
+      CMPNE R5, #0
+      MOV   PC, lr
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Take R0 as pointer to null-terminated string, print it
+; with line breaks before words that won't fit on the current line.
+; CR forces a newline. TAB=tab to next column of 8. CHR$31 = hard space
+; <27,n> => substitute nth dictionary entry:
+;  dictionary is pointed at by r1, consists of a list of null-terminated
+;  strings, with preceding length bytes.
+; 0th dictentry => use string at r2.
+
+XOS_PrettyPrint_code ROUT
+
+; Inside here, r1 is the current string pointer
+;              R3 is the linelength to format to
+;              R4 is the current position in the line.
+;              r6 is the return addr in word counting
+;              r7 is the r1 restore value in reading word length
+;              r8 is used in dictionary selection in getbytepp
+;              r9 is the stack restoration level for exiting.
+;              r11 is the dictionary pointer (0 means use MOS dict)
+;              r12 is the special token pointer
+
+          TEQP    PC, #SVC_mode
+
+          Push   "r0-r9, lr"
+
+          BL      ReadWindowWidth    ; read linelength
+          MOV     R3, R0
+          MOV     R0, #165           ; read output cursor
+          SWI     XOS_Byte
+          ORR     R4, R1, #&80000000 ; no leading space even if in line.
+          LDMFD   stack, {r1, r11, r12}   ; reload strptr, dictptr, tokptr
+          CMP     r11, #0
+          ADREQL  r11, MOSdictionary
+          MOV     r9, stack
+
+; loop over all words
+01        BL      getwordlength
+          CMP     R2, #0
+          BLEQ    getbytepp
+          BEQ     %FT21                ; null word - test for done
+
+          CMP     R4, #0
+          ADDGT   R2, R2, #1           ; allow for separator
+          ADD     R0, R2, R4
+          BIC     R0, R0, #&80000000   ; clear R4 flag
+          CMP     R0, R3
+          BLE     %FT10
+          CMP     R4, #0
+          SUBGT   R2, R2, #1          ; remove leading space length
+          MOV     R4, #0              ; if word too long to fit, do newline
+          SWI     XOS_NewLine
+          BVS     exitpp
+
+10        CMP     R4, #0
+          BIC     R4, R4, #&80000000  ; clear "no leading space" flag
+          ADD     R4, R4, R2
+          SUBGT   R2, R2, #1
+          SWIGT   XOS_WriteI+" "    ; output separator if not 1st on line
+          BVS     exitpp
+
+04        BL      getbytepp
+          CMP     R0, #31           ; hard space?
+          MOVEQ   R0, #" "
+          SWI     XOS_WriteC
+          BVS     exitpp
+          SUBS    R2, R2, #1
+          BNE     %BT04
+
+21        CMP     R0, #13
+          MOVEQ   R4, #0
+          SWIEQ   XOS_NewLine
+          BVS     exitpp
+
+          CMP     R0, #TAB
+          BEQ     %FT20
+          CMP     R0, #0
+          BNE     %BT01
+
+exitpp    MOV     stack, r9
+          STRVS   r0, [stack]
+          Pull   "r0-r9,lr"
+          B      SLVK_TestV
+
+; TAB had: align to next multiple of 8
+20        BIC     R4, R4, #&80000000
+   ; first want to get next word length, to see if it's worth doing
+          ADD     R5, R4, #8
+          BIC     R5, R5, #7
+          SUB     R5, R5, R4           ; spaces for this tab
+
+24        BL      getwordlength
+          CMP     R2, #0
+          BNE     %FT23              ; got the word
+          BL      getbytepp
+          CMP     R0, #13
+          BEQ     %BT21              ; TAB, CR - whalley!
+          CMP     R0, #TAB
+          SUBNE   R5, R5, #1         ; leading spaces, junk ignored
+          ADDEQ   R5, R5, #8         ; multiple tabs OK tho.
+          CMP     r0, #0             ; terminator?
+          BNE     %BT24              ; only case to allow zero-length word
+          SUB     r1, r1, #1         ; we know this rewinds OK
+
+23        ADD     R0, R4, R5
+          ADD     R0, R0, R2
+          CMP     R0, R3
+          MOVGT   R0, #13           ; next tab stop too far : newline
+          BGT     %BT21
+22        SWI     XOS_WriteI+" "
+          BVS     exitpp
+          ADD     R4, R4, #1
+          SUBS    r5, r5, #1
+          BNE     %BT22
+          ORR     R4, R4, #&80000000 ; set top bit to disable leading space
+          B       %BT01
+
+getwordlength
+          MOV     r6, lr
+          MOV     r10, r9            ; first copy context
+          MOV     r2, stack
+          MOV     r7, r1
+copycontextpp
+          CMP     r9, r2
+          LDRNE   r0, [r9, #-4]!
+          Push    r0, NE
+          BNE     copycontextpp
+
+          MOV     r2, #0             ; word length
+02        BL      getbytepp
+          CMP     R0, #31
+          CMPNE   R0, #" "+1
+          ADDGE   r2, r2, #1
+          BGE     %BT02
+          MOV     r1, r7
+          MOV     stack, r9
+          MOV     r9, r10
+          MOV     pc, r6
+
+getbytepp LDRB    r0, [r1], #1
+          CMP     r0, #TokenEscapeChar
+          BEQ     gettokenpp
+          CMP     r0, #0
+          MOVNES  pc, lr
+          CMP     stack, r9
+          MOVHSS  pc, lr
+          Pull    r1                ; back to previous token
+          B       getbytepp
+
+gettokenpp
+          LDRB    r0, [r1], #1      ; tokno
+          Push    r1                ; save context
+          CMP     r0, #0
+          MOVEQ   r1, r12
+          BEQ     getbytepp
+          MOV     r1, r11
+gtoklp    SUBS    r0, r0, #1
+          LDRNEB  r8, [r1]
+          ADDNE   r1, r1, r8
+          BNE     gtoklp
+          ADD     r1, r1, #1
+          B       getbytepp
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ReadWindowWidth ROUT    ; read "number of chars printable after Newline
+                        ;       before we're on the next line"
+        MOV     R0, #VduExt_WindowWidth
+ReadVduVarForSam
+        Push   "R0, R1, lr"      ; R1 reserves space
+        MOV     R0, #-1
+        STR     R0, [stack, #4]  ; overwrite R1 value
+        MOV     R0, sp
+        MOV     R1, sp
+        SWI     XOS_ReadVduVariables
+        Pull   "R0, R14, PC"
+
+;*****************************************************************************
+; R0 -> envstring, R1 -> 5 byte time
+
+WriteEnv_SWICode ROUT ; for debugger use
+        CMP     R0, #0
+        BEQ     %FT01
+        LDR     R10, =EnvString
+        MOV     R11, R0
+02      LDRB    R12, [R11], #1
+        CMP     R12, #" "
+        MOVLT   R12, #0
+        STRB    R12, [R10], #1
+        BGE     %BT02
+
+01      CMP     R1, #0
+        ExitSWIHandler EQ
+        LDR     R10, =EnvTime
+        MOV     R11, #4
+03      LDRB    R12, [R1, R11]
+        STRB    R12, [R10, R11]
+        SUBS    R11, R11, #1
+        BPL     %BT03
+        ExitSWIHandler
+
+;*****************************************************************************
+
+; LET RdArgs() BE
+;  R0 ptr to key defn string
+;  R1 ptr to command line to decode
+;  R2 ptr to workspace
+;  R3 size of workspace
+; Returns R3 size left
+
+; Flags used in initial args decoding
+
+AFlag       *  1:SHL:8     ; flags shifted up a byte; avoid looking like chars.
+KFlag       *  1:SHL:9
+SFlag       *  1:SHL:10
+EFlag       *  1:SHL:11
+GFlag       *  1:SHL:12
+UnsetFlag   *  1:SHL:31
+PresentFlag * -1 :AND::NOT:UnsetFlag
+AbsentFlag  *  0
+
+; Type flags
+
+EndType     *   0
+FlagType    *   1
+KeywordType *   2
+ItemType    *   3
+
+RdArgs_SWICode ROUT
+
+      TEQP    PC, #SVC_mode
+
+      Push   "R4, R8, R9, lr"
+      MOV     R10, R2             ; R10 points at next available word
+      MOV     R12, R0
+01    MOV     R11, #UnsetFlag
+      SUBS    R3, R3, #4
+      BMI     %FT99               ; insufficient space
+      STR     R11, [R10], #4
+02    LDRB    R11, [R12], #1
+      CMP     R11, #"/"
+      BNE     %FT03
+      LDRB    R11, [R12], #1
+      UpperCase R11, R9
+      CMP     R11, #"A"
+      MOVEQ   R11, #AFlag
+      CMP     R11, #"K"
+      MOVEQ   R11, #KFlag
+      CMP     R11, #"S"
+      MOVEQ   R11, #SFlag
+      CMP     R11, #"E"
+      MOVEQ   R11, #EFlag
+      CMP     R11, #"G"
+      MOVEQ   R11, #GFlag
+      CMP     R11, #256
+      LDRGE   R9, [R10, #-4]
+      ORRGE   R9, R11, R9
+      STRGE   R9, [R10, #-4]
+03
+      CMP     R11, #","
+      BEQ     %BT01
+      CMP     R11, #" "
+      BGE     %BT02
+
+; Initialisation complete: all flags set, R10 implies number of args.
+      MOV     R8, R10
+
+10    BL      RdItem
+      BVS     %FT90
+      CMP     R12, #KeywordType
+      BNE     %FT11
+      ADD     R11, R2, R4, LSL #2           ; keyword ptr
+      BL      RdItem
+      BVS     %FT90
+      CMP     R12, #ItemType
+      BNE     %FT98
+      BL      SetKeyword
+      BVS     %FT90
+      B       %BT10
+
+11    CMP     R12, #ItemType
+      BNE     %FT12
+
+; next positional arg := itemptr
+
+      MOV     R11, R2
+20    CMP     R11, R8
+      BEQ     %FT98               ; no more positional args
+      LDR     R12, [R11], #4
+      TST     R12, #UnsetFlag
+      BEQ     %BT20
+      TST     R12, #KFlag :OR: SFlag
+      BNE     %BT20
+      SUB     R11, R11, #4
+      BL      SetKeyword
+      BVS     %FT90
+      B       %BT10
+
+12    CMP     R12, #EndType
+      BNE     %BT10
+
+; postscan to check all /a args set.
+      MOV     R12, R2
+30    CMP     R12, R8
+      BEQ     %FT31
+      LDR     R11, [R12], #4
+      TST     R11, #UnsetFlag
+      BEQ     %BT30
+      TST     R11, #AFlag
+      BNE     %FT98          ; bum args error
+      MOV     R11, #AbsentFlag
+      STR     R11, [R12, #-4]     ; force "not present"
+      B       %BT30
+
+31
+      Pull   "R4, R8, R9, lr"
+      ExitSWIHandler
+
+98    ADR     R0, ErrorBlock_BadParameters
+    [ International
+      BL      TranslateError
+    ]
+      B       %FT90
+
+99    ADRL    R0, ErrorBlock_BuffOverflow
+    [ International
+      BL      TranslateError
+    ]
+90
+      Pull   "R4, R8, R9, lr"
+      B      SLVK_SetV
+
+      MakeErrorBlock BadParameters
+      MakeErrorBlock ArgRepeated
+      ALIGN
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; RdItem: strips next item from command line
+; In:  R1 -> cmd line
+; Out: R1 updated
+;      R12 contains type
+;      R4 contains ptr for Item, argno for Flag/Keyword
+; VS means buffer full
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+RdItem    ROUT
+      Push  "R1, R7, R8, lr"
+      MOV    R8, #0              ; not demanding new arg
+01    LDRB   R12, [R1], #1
+      CMP    R12, #" "
+      BEQ    %BT01
+      MOVLT  R12, #EndType
+      Pull  "R1, R7, R8, lr", LT
+      BICLTS PC, lr, #V_bit
+
+      CMP    R12, #""""
+      BEQ    ohnoitsquotedargumentsagain
+
+      CMP    R12, #"-"      ; options?
+      BNE    %FT02          ; nope - boring item
+05    BL     GetArg
+      LDRVS  R1, [stack]
+      BVS    %FT02          ; not recognised - boring item
+      STR    R1, [stack]    ; new restore value.
+      Push   PC             ; save EQ/NE
+      LDR    R12, [R2, R4, LSL #2]
+      TST    R12, #UnsetFlag
+      ADREQ   R0, ErrorBlock_ArgRepeated
+    [ International
+      BLEQ   TranslateError
+    ]
+      ADDEQ  stack, stack, #4 ; discard PC
+      Pull  "R1, R7, R8, lr", EQ
+      ORREQS PC, lr, #V_bit
+
+      ANDS   R12, R12, #SFlag
+      MOVEQ  R12, #KeywordType
+      ADDEQ  stack, stack, #4 ; discard PC
+      Pull  "R1, R7, R8, lr",EQ
+      BICEQS PC, lr, #V_bit
+
+      MOV    R12, #PresentFlag
+      STR    R12, [R2, R4, LSL #2]
+
+; now deal with flag elision: if nextchar valid keyword char, rescan
+      Pull   R12
+      TST    R12, #Z_bit
+      BEQ    %FT20          ; GetArg returned NE, so not single char abbr
+      Push  "R2, R3"
+      LDRB   R2, [R1]
+      BL     CheckR2OKChar
+      Pull  "R2, R3"
+      MOVEQ  R8, #1        ; MUST get another arg
+      BEQ    %BT05
+20    MOV    R12, #FlagType
+      Pull  "R1, R7, R8, lr"
+      BICS   PC, lr, #V_bit
+
+02    CMP    R8, #0
+      ADRNE  R0, ErrorBlock_BadParameters
+    [ International
+      BLNE   TranslateError
+    ]
+      Pull  "R1, R7, R8, lr", NE
+      ORRNES PC, lr, #V_bit
+
+   ; copy arg until <" "
+
+      MOV    R7, #" "
+      SUB    R1, R1, #1
+06    MOV    R4, R10
+03    LDRB   R12, [R1], #1
+      CMP    R12, R7
+      CMPNE  R12, #" "-1
+      BLE    %FT04
+10
+      SUBS   R3, R3, #1
+      STRPLB R12, [R10], #1
+      BPL    %BT03
+23
+      ADRL   R0, ErrorBlock_BuffOverflow
+    [ International
+      BL     TranslateError
+    ]
+      Pull  "R1, R7, R8, lr"
+      ORRS   PC, lr, #V_bit     ; overflow
+
+04    CMP    R7, #""""
+      BNE    %FT07
+      CMP    R12, #""""
+      BNE    %FT08
+      LDRB   R12, [R1], #1
+      CMP    R12, #""""
+      BEQ    %BT10
+07    MOV    R12, #0            ; terminate
+      SUBS   R3, R3, #1
+      BMI    %BT23
+      STRB   R12, [R10], #1
+      MOV    R12, #ItemType
+      SUB    R1, R1, #1
+      STR    R1, [stack]
+      Pull  "R1, R7, R8, lr"
+      BICS   PC, lr, #V_bit
+
+ohnoitsquotedargumentsagain
+      MOV    R7, #""""
+      B      %BT06
+
+08    ADRL   R0, ErrorBlock_BadString
+    [ International
+      BL     TranslateError
+    ]
+      Pull  "R1, R7, R8, lr"
+      ORRS   PC, lr, #V_bit
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; GetArg
+; Look through keys: allow only full match on first pass, then
+;  single char abbreviation on second pass
+; Return V set if not key, EQ if single letter abbreviation
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+GetArg    ROUT
+; In:  R0 ptr to keystring
+;      R1 ptr to potential option.
+; Out: VS if nomatch or
+;      R1 updated
+;      R4 argument number
+
+      Push   "R0-R3, R5-R6, lr"
+      MOV     R6, #-1             ; pass number
+20    MOV     R4, #0
+
+01    LDRB    R2, [R0], #1
+      CMP     R2, #" "
+      BEQ     %BT01
+
+02    BL      CheckR2OKChar
+      BNE     %FT03             ; matched in full
+      LDRB    R3, [R1], #1
+      UpperCase R2, R5
+      UpperCase R3, R5
+      CMP     R2, R3
+      LDREQB  R2, [R0], #1
+      BEQ     %BT02
+
+      CMP     R6, #0
+      BLT     %FT04
+
+; 2nd pass: allow abbreviation
+; IF single char abbreviation THEN success ELSE skip
+      LDR     R2, [stack, #4]
+      SUB     R2, R1, R2          ; length of match+1
+      CMP     R2, #2
+      SUBEQ   R1, R1, #1
+      BEQ     %FT13               ; success
+
+; skip to next keyword
+04    LDRB    R2, [R0], #1
+      CMP     R2, #" "
+      BLT     %FT05
+      CMP     R2, #","
+      ADDEQ   R4, R4, #1
+      CMPNE   R2, #"="
+      BNE     %BT04
+      LDR     R1, [stack, #4]
+      B       %BT01
+
+03  ; NE on first pass: check input string terminated OK
+      LDRB    R2, [R1]            ; check for end of input word
+      BL      CheckR2OKChar
+      BNE     %FT13               ; yaay! full & correct match
+      SUB     R0, R0, #1
+      B       %BT04
+
+05    ADDS    R6, R6, #1
+      LDMLEFD stack, {R0, R1}
+      BLE     %BT20
+      Pull   "R0-R3, R5-R6, lr"
+      ORRS    PC, lr, #V_bit      ; back with failure
+
+13    STR     R1, [stack, #4]
+      MVN     R0, #V_bit
+      TSTP    R0, PC              ; clrV (just)
+      Pull   "R0-R3, R5-R6, PC"   ; back with success
+
+CheckR2OKChar ROUT
+      CMP     R2, #"A"
+      RSBGES  R3, R2, #"Z"
+      ORRGES  PC, lr, #Z_bit
+      CMP     R2, #"a"
+      RSBGES  R3, R2, #"z"
+      ORRGES  PC, lr, #Z_bit
+      CMP     R2, #"0"
+      RSBGES  R3, R2, #"9"
+      ORRGES  PC, lr, #Z_bit
+      CMP     R2, #"_"
+      BICNES  PC, lr, #Z_bit
+      ORRS    PC, lr, #Z_bit
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+SetKeyword    ROUT
+; R11 ptr at keyword flags
+; R4 ptr to value
+      Push   "R0-R2, R5, R6, lr"
+      LDR     R0, [R11]
+      TST     R0, #EFlag :OR: GFlag
+      STR     R4, [R11]           ; result will always be here anyway
+      BEQ     %FT01
+      SUB     R5, R10, R4         ; length of value
+      ADD     R3, R3, R5          ; increase free space
+      ADD     R2, R5, #11         ; round up to nearest word, then 1
+      BIC     R2, R2, #3
+
+      STR     stack, [stack, -R2]! ; reserve stack frame
+
+      ADD     R2, stack, #4        ; input value ptr
+03    LDRB    R6, [R4, R5]
+      STRB    R6, [R2, R5]
+      SUBS    R5, R5, #1
+      BPL     %BT03               ; copy the value
+
+      TST     R0, #EFlag
+      MOV     R0, R2
+      BNE     %FT02
+      ADD     R1, R4, #2          ; free space pointer
+      SUBS    R3, R3, #2
+      MOV     R2, R3
+      BMI     %FT04
+      ORR     R2, R2, #1:SHL:31
+      SWI     XOS_GSTrans
+05    SUB     R3, R3, R2
+      ADD     R10, R1, R2        ; update freespace pointer
+      STRB    R2, [R1, #-2]
+      MOV     R2, R2, LSR #8
+      STRB    R2, [R1, #-1]
+      BCS     %FT04
+
+90    LDR     stack, [stack, #0]  ; unwind frame (and don't let AAsm use
+      STRVS   R0, [stack]         ;   postindexed!!!)
+      Pull   "R0-R2, R5, R6,lr"
+      BICVCS  PC, lr, #V_bit
+      ORRS    PC, lr, #V_bit
+
+04    ADRL    R0, ErrorBlock_BuffOverflow
+    [ International
+      BL      TranslateError
+    |
+      SETV
+    ]
+      B       %BT90
+
+02    ADD     R1, R4, #3        ; free space pointer
+      SUBS    R3, R3, #3        ; adjust for type (1 byte) + length (2 bytes)
+      BMI     %BT04
+      MOV     R2, R3
+      SWI     XOS_EvaluateExpression
+      BVS     %BT90
+      TEQ     R1, #0, 2         ; if non-zero, then string, so update length
+      MOVNE   R14, #1           ; set type byte to definitely non-zero
+      STRNEB  R14, [R4]
+      BNE     %BT05             ; (C=0 so no buffer overflow, V=0 from SWI)
+      STRB    R1, [R4]          ; set type byte to zero (=> integer)
+      SUBS    R3, R3, #5
+      BMI     %BT04
+      STRB    R2, [R4, #1]
+      MOV     R2, R2, LSR #8
+      STRB    R2, [R4, #2]
+      MOV     R2, R2, LSR #8
+      STRB    R2, [R4, #3]
+      MOV     R2, R2, LSR #8
+      STRB    R2, [R4, #4]
+      ADD     R10, R4, #5
+      B       %BT90
+
+01
+      Pull   "R0-R2, R5, R6,lr"
+      BICS    PC, lr, #V_bit
+
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; ReadRAMFSLimits
+; Return R0 start, R1 end (i.e. 1st byte out of area)
+
+ReadRAMFSLimits_Code ROUT
+ [ NewStyle_RAMDisc
+        Push    "lr"
+        MOV     r0, #ChangeDyn_RamFS
+        SWI     XOS_ReadDynamicArea     ; out: r0 = base, r1 = current size
+        Pull    "lr"
+        ADDVC   r1, r1, r0              ; if no error, make r1 -> after end
+        ORRVS   lr, lr, #V_bit          ; if error, then set V_bit on return
+ |
+        MOV     R0, #RAMDiscAddress
+        MOV     R1, #0
+        LDR     R1, [R1, #RAMDiscSize]
+        ADD     R1, R1, R0
+ ]
+        ExitSWIHandler
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; ExitAndDie
+; r0-r2 parameters for Exit
+; r3 pointer to module name
+
+TerminateAndSodOff ROUT
+
+        Push    "r0-r2"
+        MOV     r10, #0
+        STR     r10, [r10, #Curr_Active_Object]
+        MOV     r1, r3
+        MOV     r0, #ModHandReason_Delete
+        SWI     XOS_Module
+        Pull    "r0-r2"
+        SWI     XOS_Exit
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; AddCallBack entry to callback vector
+; r0 address to call
+; r1 workspace ptr
+
+AddCallBack_Code  ROUT
+
+      Push   "r0-r3, lr"
+      MOV     r3, #12
+      BL      ClaimSysHeapNode
+      BVS     %FT99
+      Pull   "r0, r1"
+      MOV     r10, #0
+      TEQP    PC, #SVC_mode+I_bit       ; IRQs off while holding context.
+      LDR     r3, [r10, #CallBack_Vector]
+ [ True
+      STR     r3, [r2, #0]
+      STMIB   r2, {r0, r1}
+      STR     r2, [r10, #CallBack_Vector]
+      Pull   "r2, r3, lr"
+ |
+      STR     r3, [r2], #4
+      STMIA   r2, {r0, r1}
+      SUB     r2, r2, #4
+      STR     r2, [r10, #CallBack_Vector]
+      Pull   "r2, r3, lr"
+      MOV     r10, #0
+ ]
+      LDRB    r11, [r10, #CallBack_Flag]
+      ORR     r11, r11, #CBack_VectorReq
+      STRB    r11, [r10, #CallBack_Flag]
+      B       SLVK
+
+99      STR     r0, [stack]
+        Pull    "r0-r3, lr"
+        B       SLVK_SetV
+
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       RemoveCallBack - Entry for SWI OS_RemoveCallBack
+;
+;       Removes an entry on the callback vector before it has been called
+;
+; in:   r0 = address that would have been called
+;       r1 = workspace ptr
+;
+; out:  -
+;
+
+RemoveCallBack ROUT
+        Push    "r2-r4"
+        MOV     r10, PC                 ; save old I bit
+10
+        TEQP    PC, #SVC_mode+I_bit     ; disable IRQs while looking at list
+        MOV     r11, #0
+        LDR     r2, [r11, #CallBack_Vector]! ; r11 -> prev, r2 -> this
+20
+        TEQ     r2, #0
+        Pull    "r2-r4", EQ
+        ExitSWIHandler EQ
+
+        LDMIA   r2, {r3,r4,r12}         ; r3 -> next, r4 = addr, r12 = ws
+        TEQ     r4, r0                  ; check if correct address
+        TEQEQ   r12, r1                 ; and correct ws ptr
+        MOVNE   r11, r2                 ; if not, then prev:=this
+        MOVNE   r2, r3                  ; and this:=next
+        BNE     %BT20                   ; and loop
+
+        STR     r3, [r11]               ; prev.link := next
+        TEQP    r10, #0                 ; safe now to restore IRQ status
+
+        Push    "r0, r1, lr"            ; now free this node
+        BL      FreeSysHeapNode         ; and then start again (NB we must
+                                        ; restart from head of list again, as
+                                        ; enabling IRQs could have changed list
+        STRVS   r0, [sp, #0]
+        Pull    "r0, r1, lr"
+        BVC     %BT10
+
+        Pull    "r2-r4"                 ; had an error while releasing block
+        ORR     lr, lr, #V_bit
+        ExitSWIHandler
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; SWI OS_Confirm
+
+; If pointer visible, change pointer shape and look at mouse keys too.
+;                     Flush mouse buffer first.
+; Wait until key pressed.
+; Returns: (lowercased) character in r0 (if mouse scanned, LH button = 'y',
+;                                        other buttons = 'n')
+; C set for Escape condition
+; EQ set if r0 = 'y'
+
+confirm_yn      DCB "YesNo:yn",0        ; Token to lookup message for Yes/No
+ptr_confirm     DCB "ptr_confirm",0     ; Wimp's confirm pointer sprite shape
+confirm_scale   DCD 1,1,1,1             ; Scaling factor for sprite op
+Confirm_Code ROUT
+        TEQP    pc, #SVC_mode           ; enable IRQs
+        Push   "r0-r7, lr"
+
+; Lookup the symbols for Y/N
+
+        ADR     r0, confirm_yn
+        BL      FindToken               ; Lookup in messages, use default if error
+        LDRB    r1, [r0], #1            ; Y character
+        LDRB    r2, [r0], #1            ; N character
+        ORR     r1, r1, r2, LSL #8
+        Push    "r1"                    ; Y=[sp], N=[sp+1]
+
+; Save current pointer selected
+
+        MOV     r0, #106
+        MOV     r1, #255                ; read pointer state
+        SWI     XOS_Byte
+        BVS     confirm_error
+        BIC     r2, r1, #&80
+        MOV     r10, r1
+        TST     r10, #15                ; Pointer enabled?
+        BEQ     confirm_waitloop        ; Jump if not
+
+; Find Wimp's "ptr_confirm" sprite, searching first RMA then ROM
+
+        SWI     XWimp_BaseOfSprites
+        MOVVS   r10, #0                 ; If error then don't use mouse
+        BVS     confirm_waitloop        ; And jump to poll keyboard
+
+        MOV     r11, r0                 ; Save -> ROM sprites, r1 -> RMA sprites
+        ADR     r2, ptr_confirm         ; -> sprite name
+        MOV     r0, #&128               ; Read sprite info
+        SWI     XOS_SpriteOp
+        BVC     %FT10                   ; Jump if sprite in RMA
+
+        ADR     r2, ptr_confirm         ; -> sprite name
+        MOV     r1, r11                 ; -> ROM sprites
+        MOV     r0, #&128               ; Read sprite info
+        SWI     XOS_SpriteOp
+        MOVVS   r10, #0                 ; If error then don't use mouse
+        BVS     confirm_waitloop        ; And jump to poll keyboard
+
+; Set pointer shape from the sprite area found (r1)
+
+10      MOV     r7, #0                  ; No pixel translation
+        ADR     r6, confirm_scale       ; pointer scaling factor (1:1)
+        MOV     r5, #0                  ; y hot spot offset
+        MOV     r4, #0                  ; x hot spot offset
+        MOV     r3, #&23                ; No palette, shape 3
+        ADR     r2, ptr_confirm         ; -> sprite name
+        MOV     r0, #&124               ; Set pointer shape
+        SWI     XOS_SpriteOp            ; Ignore errors
+
+        MOV     r0, #21
+        MOV     r1, #9
+        SWI     XOS_Byte                ; flush mouse buffer
+
+        MOV     r0, #&C4
+        MOV     r1, #0
+        MOV     r2, #255
+        SWI     XOS_Byte                ; read current repeat rate.
+        MOV     r0, #0
+        LDR     r0, [r0, #MetroGnome]
+        ADD     r11, r1, r0             ; time to wait for
+
+confirm_mouserepeat
+        SWI     XOS_Mouse
+        CMP     r2, #0                  ; any buttons down?
+        BEQ     confirm_waitloop
+
+        CMP     r3, r11
+        BMI     confirm_mouserepeat
+
+confirm_waitloop
+        MOV     r0, #129
+        MOV     r1, #1
+        MOV     r2, #0
+        SWI     XOS_Byte                ; scan for key
+        BVS     confirm_error
+        CMP     r2, #255
+        BNE     confirm_gotkey
+
+        TST     r10, #15
+        BEQ     confirm_waitloop        ; no mouse scan wanted.
+
+        SWI     XOS_Mouse
+        BVS     confirm_error
+        CMP     r2, #0
+        BEQ     confirm_waitloop
+
+        TST     r2, #4                  ; LH button?
+        LDRNEB  r1, [sp]                ; Yes
+        LDREQB  r1, [sp, #1]            ; No
+
+confirm_gotkey
+        CMP     r2, #&1B                ; ESCAPE or normal char read ?
+        ORREQ   r11, r1, #&100
+        MOVNE   r11, r1
+        LowerCase r11, r1
+
+        TSTS    r10, #15                ; Was pointer changed?
+        MOV     r1, r10
+        MOV     r0, #106
+        SWINE   XOS_Byte                ; Yes then restore shape
+
+        Pull   "r10"                    ; r10=YN
+        AND     r10, r10, #&ff          ; Retain Y byte
+        Pull   "r0-r7, lr"
+        BIC     lr, lr, #C_bit :OR: Z_bit ; SKS. Punter lr has VClear
+        AND     r0, r11, #&FF
+        TST     r11, #&100              ; ESCAPE condition ? (SKS)
+        ORRNE   lr, lr, #C_bit
+        CMP     r0, r10
+        ORREQ   lr, lr, #Z_bit
+        B       SLVK
+
+confirm_error
+        ADD     sp, sp, #4              ; Drop YN
+        STR     r0, [stack]
+        Pull    "r0-r7, lr"
+        B       SLVK_SetV
+
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r0 = continuation crc (0 to start)
+;       r1 -> start of core
+;       r2 -> end of core
+;       r3 = increment (typically 1, 2 or 4), but can be -ve
+
+; Out   r0 = crc so far
+
+CRC_Code ROUT
+
+        [ {TRUE}
+        TEQP    pc, #SVC_mode           ; Could take a long time
+
+        Push    "r3, r14"
+        TEQ     r3, #0                  ; is increment 0? - if so, it's silly
+        BEQ     %FT80                   ; so return error, rather than going on forever
+
+        MOV     r10, r1
+        MOV     r12, #&A000             ; CRC EOR pattern = &A001
+        ORR     r12, r12, #&0001
+10
+        CMP     r10, r2                 ; Finished ?
+        BEQ     %FA90                   ; Must be exact end, allows -ve offset
+
+        LDRB    r14, [r10], r3
+        EOR     r0, r0, r14             ; EOR the data with current crc lo
+        CMP     r10, r2                 ; length might be odd so check again
+        MOVEQ   r11, #8                 ; if equal then only do 8 bits
+        BEQ     %FT20
+
+        LDRB    r14, [r10], r3
+        EOR     r0, r0, r14, LSL #8     ; EOR the data with current crc hi
+        MOV     r11, #16                ; do 16 bits
+
+20
+        MOVS    r0, r0, LSR #1          ; acc >>= 1; CS/CC
+        EORCS   r0, r0, r12             ; CS -> eor, CC -> ok
+        SUBS    r11, r11, #1            ; 8 bits per byte
+        BNE     %BT20
+        B       %BT10
+
+80
+        ADRL    r0, ErrorBlock_BadParameters    ; return "Bad parameters" error
+ [ International
+        BL      TranslateError
+ ]
+        Pull    "r3, r14"
+        B       SLVK_SetV
+
+90
+        Pull    "r3, r14"
+        ExitSWIHandler
+        |
+        TEQP    pc, #SVC_mode           ; Could take a long time
+
+        Push    "r3, r4, lr"
+        MOV     r10, r1
+        MOV     r12, #&A000             ; CRC EOR pattern = &A001
+        ORR     r12, r12, #&0001
+        MOV     r14, #1                 ; A single bit to be shifted
+
+10      CMP     r10, r2                 ; Finished ?
+        BEQ     %FA90                   ; Must be exact end, allows -ve offset
+
+        LDRB    r4, [r10], r3
+        MOV     r11, #0                 ; Go round the bits
+
+20      TST     r4, r14, LSL r11        ; Is data bit = carry ?; NE/EQ
+        BEQ     %FT30
+
+        MOVS    r0, r0, LSR #1          ; acc >>= 1; CS/CC
+        EORCC   r0, r0, r12             ; NE, CC -> eor, NE, CS -> ok
+        B       %FT40
+
+30      MOVS    r0, r0, LSR #1          ; acc >>= 1; CS/CC
+        EORCS   r0, r0, r12             ; EQ, CS -> eor, EQ, CC -> ok
+
+40      ADD     r11, r11, #1            ; 8 bits per byte
+        CMP     r11, #8
+        BLO     %BT20
+
+        B       %BT10
+
+90      Pull    "r3, r4, lr"
+        ExitSWIHandler
+        ]
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        END
diff --git a/s/Morris b/s/Morris
new file mode 100644
index 0000000000000000000000000000000000000000..94d04b9b668760149d205039a786970023501f8a
--- /dev/null
+++ b/s/Morris
@@ -0,0 +1,105 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > Morris
+
+;
+; ROM entry code for RISC OS Black
+;
+
+MorrisHeaderCodeSize * (24*4)   ;Code occupies less than this, we pad to this boundary so POST code
+                                ;has a nicely defined place for its romsize and code entry points
+ [ 1 = 1
+; VECTOR AREA:
+
+        LDR     pc, .+ResetIndirection
+        LDR     PC, UNDEF_VEC
+        LDR     PC, SWI_VEC
+        LDR     PC, PREF_VEC
+        LDR     PC, DATA_VEC
+        LDR     PC, RES_VEC                   ; addr. exn not generated
+        LDR     PC, IRQ_VEC
+        LDR     PC, FIQ_VEC
+
+        ! 0, "*** WARNING *** assembling Morris code"
+
+; The above 8 instructions will operate as expected in 32-bit ROM mode,
+; or in 16-bit ROM mode with a 16-bit ROM used.  In 16-bit ROM mode, and
+; with 32-bit wide ROMs in use, they will instead be intepreted as 4
+; NV-condition instructions (exact meaning not determined and should be
+; irrelevant) which should do nothing and so allow control to drop
+; through to this point, still in 16-bit mode.  Force IOMD into 32-bit
+; ROM mode for bank 0.  The following instruction sequence has been
+; produced in 16-in-32 form by extracting hex values from a listing...
+
+        DCD     &0000B632, &0000E3A0    ; 20: MOV R11, #IO+IOMDREGS - point at IOMD
+        DCD     &00000000, &0000E3A0    ; 24: MOV R0, #&0 - ROMCR:32b,slow,218.75us,no burst
+        DCD     &00000080, &0000E5CB    ; 28: STRB R0,[R11,#ROMCR0] - switch mode
+        DCD     &0000F000, &0000E3A0    ; 2C: MOV PC, #0 - jump to 0 (this instr pre-fetched)
+ |
+;
+; Kludged to cope with Morris Bugs
+;
+
+        ! 0,"<><><><><><><> This kernel version will not softload on a Risc PC <><><><><><><>"
+
+        DCD     &E3A0F010
+        DCD     &FFFFE3A0
+
+        DCD     &E59FF034
+        DCD     &E59FF034
+
+        DCD     &E59FF034
+        DCD     &E59FF034
+
+        DCD     &E59FF034
+        DCD     &E59FF034
+
+        DCD     &E3A0B632
+        DCD     &E3A0E3A0               ; 20: MOV R11, #IO+IOMDREGS - point at IOMD
+
+        DCD     &E3A00000
+        DCD     &E3A0E3A0               ; 24: MOV R0, #&0 - ROMCR:32b,slow,218.75us,no burst
+
+        DCD     &E5CB0080
+        DCD     &E3A0E5CB               ; 28: STRB R0,[R11,#ROMCR0] - switch mode
+
+        DCD     &E59FF0F4
+        DCD     &E5CBE59F    ; 2C: MOV PC, #0 - jump to 0 (this instr pre-fetched)
+ ]
+; vector absolute targets for use from physical vector instructions
+
+
+
+UNDEF_VEC       DCD     UndInstInReset-ROM
+SWI_VEC         DCD     SWIInReset    -ROM
+PREF_VEC        DCD     PrefAbInReset -ROM
+DATA_VEC        DCD     DataAbInReset -ROM
+RES_VEC         DCD     AddrExInReset -ROM
+IRQ_VEC         DCD     IRQInReset    -ROM
+FIQ_VEC         DCD     FIQInReset    -ROM
+
+UndInstInReset
+SWIInReset
+PrefAbInReset
+DataAbInReset
+AddrExInReset
+IRQInReset
+FIQInReset
+        SUB     pc, pc, #8
+
+        ASSERT  .-ROM <= MorrisHeaderCodeSize
+        %       MorrisHeaderCodeSize-(.-ROM)
+
+        END
diff --git a/s/MsgCode b/s/MsgCode
new file mode 100644
index 0000000000000000000000000000000000000000..19ece4fecb88e029fc243aef303acc583b37bf00
--- /dev/null
+++ b/s/MsgCode
@@ -0,0 +1,332 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; Kernel.Messages
+;
+; This file deals with translating text and errors in an international
+; kernel.
+;
+;
+; TranslateError
+; Entry:
+;       R0 = Pointer to error block (Error number followed by token:default).
+; Exit
+;       R0 = pointer to error block to use.
+;            If the error semaphore is set a pointer to the original block is
+;            returned.
+;       V Set.
+;
+TranslateError_VClear  ROUT
+        Push    "r4,LR"
+        MOV     r4,#0
+        BL      TranslateError_UseR4
+        Pull    "r4,PC",,^
+
+TranslateError  ROUT
+        Push    "r4,LR"
+        MOV     r4,#0
+        BL      TranslateError_UseR4
+        Pull    "r4,PC"
+
+TranslateError_UseR4
+        Push    "R8,LR"
+
+        MOV     R8,#0
+        LDRB    R8, [R8, #ErrorSemaphore]
+        CMP     R8,#0
+        Pull    "R8,LR",NE
+        ORRNES  PC,LR,#V_bit
+
+        MOV     R8,LR
+ [ {TRUE}
+        ORR     LR, LR, #SVC_mode               ; SVC mode, preserve IRQ state
+        TEQP    PC, LR
+ |
+        TEQP    PC,#SVC_mode
+ ]
+        MOV     R0,R0
+        Push    "R0-R7,LR"
+
+        MOV     R2,#0
+        MOV     R1,#-1                          ; We are looking up an error, don't bother
+        STRB    R1, [R2, #ErrorSemaphore]       ; translating other errors.
+
+        ADR     R1,KernelMessagesBlock+4
+        MOV     R5,#0
+        MOV     R6,#0
+        MOV     R7,#0
+        SWI     XMessageTrans_ErrorLookup
+        LDR     R14,[R0]
+        LDR     R1,[SP]
+        LDR     R1,[R1]
+        CMP     R14,R1
+        STREQ   R0,[SP]
+
+        MOV     R1,#0
+        STRB    R1, [R1 ,#ErrorSemaphore]       ; Clear error semaphore
+
+        Pull    "R0-R7,LR"                      ; Exit with new block and V set.
+
+        TEQP    PC,R8                           ; Back to original mode.
+        MOV     R0,R0
+        Pull    "R8,LR"
+        ORRS    PC,R14,#V_bit                   ; original R0.
+
+;----------------------------------------------------------------------------------------
+;
+;WriteS_Translated
+;Entry:
+;       R14 -> Token.
+;*NOTE*
+;      MUST BE TOKEN:DEFAULT
+;
+;Exit:
+;       Message printed
+;       Returns to word after end of token.
+;
+WriteS_Translated       ROUT
+        Push    "r0-r7,LR"
+        MOV     r4,#0
+        B       Int_WriteS_Translated_UseR4
+
+WriteS_Translated_UseR4
+
+        Push    "r0-r7,LR"
+
+Int_WriteS_Translated_UseR4
+        BIC     r1,LR,#3                        ; r1 -> Token.
+        BIC     r1,r1,#2_111111 :SHL: 26
+        MOV     r0,#0
+        LDR     r0,[r0,#KernelMessagesBlock]
+        CMP     r0,#0                           ; If no messages file, try the global one.
+        ADRNE   r0,KernelMessagesBlock+4
+        MOV     r2,#0                           ; Use message in place.
+        MOV     r3,#0
+        MOV     r5,#0
+        MOV     r6,#0
+        MOV     r7,#0                           ; No arguments.
+        SWI     XMessageTrans_Lookup
+        BVC     %FT01
+
+        MOV     R2,R1                           ; MessageTrans not present or token not found.
+00
+        LDRB    r0,[r2],#1                      ; Skip to after ":"
+        CMP     r0,#":"
+        BNE     %BT00
+
+; Now
+; r1 -> terminator
+; r2 -> message
+; Print the message.
+
+01
+        LDRB    r0,[r2],#1                      ; Print all characters of message
+        CMP     r0,#" "
+        BLT     %FT02
+        CMP     r0,#"%"
+        SWINE   XOS_WriteC
+        BNE     %BT01
+
+        LDRB    r0,[r2],#1                      ; Found a %
+        CMP     r0,#" "
+        BLT     %FT02                           ; Trailing % sign!
+        CMP     r0,#"0"
+        SWINE   XOS_WriteI+"%"
+        SWINE   XOS_WriteC
+        BNE     %BT01                           ; Just in case it isn't %0
+
+        CMP     r4,#0                           ; r4 = original parameter
+        BEQ     %BT01
+
+11
+        LDRB    R0,[R4],#1
+        CMP     R0,#" "
+        SWIGE   XOS_WriteC
+        BGE     %BT11
+        B       %BT01
+                                                ; Now skip to end of token.
+02
+        LDR     r1,[sp,#8*4]                    ; Get original token pointer
+        BIC     r1,r1,#3                        ; r1 -> Token.
+        BIC     r1,r1,#2_111111 :SHL: 26
+03
+        LDRB    r0,[r1],#1
+        CMP     r0,#32
+        BGE     %BT03                           ; Skip to control character.
+04
+        CMP     r0,#0                           ; Print all control characters to terminating 0.
+        SWINE   XOS_WriteC
+        LDRNEB  r0,[r1],#1
+        BNE     %BT04
+
+; r1 now points at byte after end of token.
+
+        ADD     r1,r1,#3                        ; Round up to next word.
+        BIC     r1,r1,#3
+
+        LDR     r2,[sp,#8*4]
+        MOV     r3,#2_111111 :SHL:26
+        ORR     r3,r3,#2_11
+        AND     r2,r2,r3                        ; Just the flags and mode bits.
+        ORR     r1,r1,r2
+        ORRVS   r1,r1,#V_bit
+        STR     r1,[sp,#8*4]                    ; Store back as return address on stack
+
+        Pull    "r0-r7,PC",,^                   ;Return.
+
+;----------------------------------------------------------------------------------------
+;
+;GSWriteS_Translated
+;Entry:
+;       R14 -> Token.
+;*NOTE*
+;      MUST BE TOKEN:DEFAULT
+;
+;Exit:
+;       Message printed
+;       Returns to word after end of token.
+;
+GSWriteS_Translated       ROUT
+        Push    "r0-r7,LR"
+        MOV     r4,#0
+        B       Int_GSWriteS_Translated_UseR4
+
+GSWriteS_Translated_UseR4
+
+        Push    "r0-r7,LR"
+
+Int_GSWriteS_Translated_UseR4
+        BIC     r1,LR,#3                        ; r1 -> Token.
+        BIC     r1,r1,#2_111111 :SHL: 26
+        MOV     r0,#0
+        LDR     r0,[r0,#KernelMessagesBlock]
+        CMP     r0,#0                           ; If no messages file, try the global one.
+        ADRNE   r0,KernelMessagesBlock+4
+        LDR     r2,=GeneralMOSBuffer
+        MOV     r3,#256
+        MOV     r5,#0
+        MOV     r6,#0
+        MOV     r7,#0                           ; No arguments.
+        SWI     XMessageTrans_GSLookup
+        BVC     %FT01
+
+        MOV     R2,R1                           ; MessageTrans not present or token not found.
+00
+        LDRB    r0,[r2],#1                      ; Skip to after ":"
+        CMP     r0,#":"
+        BNE     %BT00
+
+; Now
+; r1 -> terminator
+; r2 -> message
+; Print the message using OS_PrettyPrint.
+
+01
+        LDR     r0, =GeneralMOSBuffer
+        MOV     r1, #0
+        SWI     XOS_PrettyPrint
+                                                ; Now skip to end of token.
+02
+        LDR     r1,[sp,#8*4]                    ; Get original token pointer
+        BIC     r1,r1,#3                        ; r1 -> Token.
+        BIC     r1,r1,#2_111111 :SHL: 26
+03
+        LDRB    r0,[r1],#1
+        CMP     r0,#0
+        BNE     %BT03                           ; Skip to 0.
+
+; r1 now points at byte after end of token.
+
+        ADD     r1,r1,#3                        ; Round up to next word.
+        BIC     r1,r1,#3
+
+        LDR     r2,[sp,#8*4]
+        MOV     r3,#2_111111 :SHL:26
+        ORR     r3,r3,#2_11
+        AND     r2,r2,r3                        ; Just the flags and mode bits.
+        ORR     r1,r1,r2
+        ORRVS   r1,r1,#V_bit
+        STR     r1,[sp,#8*4]                    ; Store back as return address on stack
+
+        Pull    "r0-r7,PC",,^                    ;Return.
+
+;----------------------------------------------------------------------------------------
+;FindToken
+;
+;Entry:
+;       R0 -> Token.
+;*NOTE*
+;      MUST BE TOKEN:DEFAULT
+;
+;Exit:
+;       r0 -> Message, or after the : if MessageTrans is dead.
+;
+;
+FindToken      ROUT
+        Push    "r0-r7,LR"
+
+        MOV     r1,r0
+        MOV     r0,#0
+        LDR     r0,[r0,#KernelMessagesBlock]
+        CMP     r0,#0                           ; If no messages file, try the global one.
+        ADRNE   r0,KernelMessagesBlock+4
+        MOV     r2,#0
+        MOV     r3,#0
+        MOV     r4,#0
+        MOV     r5,#0
+        MOV     r6,#0
+        MOV     r7,#0                           ; No arguments.
+        SWI     XMessageTrans_Lookup
+        BVC     %FT01
+
+        MOV     R2,R1                           ; MessageTrans not present or token not found.
+00
+        LDRB    r0,[r2],#1                      ; Skip to after ":"
+        CMP     r0,#":"
+        BNE     %BT00
+01
+        STR     r2,[sp]
+
+        Pull    "r0-r7,PC",,^
+;----------------------------------------------------------------------------------------
+;Write0_Translated
+;
+;Entry:
+;       R0 -> Token.
+;*NOTE*
+;      MUST BE TOKEN:DEFAULT
+;
+;Exit:
+;        Message printed, r0->Message.
+;
+Write0_Translated ROUT
+        Push    "r0,r1,LR"
+        BL      FindToken
+        MOV     R1,R0
+01
+        LDRB    R0,[R1],#1
+        CMP     R0,#31
+        SWIGT   XOS_WriteC
+        STRVS   r0,[SP]
+        Pull    "r0,r1,PC",VS
+        BGT     %BT01
+
+        Pull    "r0,r1,PC",,^
+
+        END
+
+
+
+
+
diff --git a/s/NewIRQs b/s/NewIRQs
new file mode 100644
index 0000000000000000000000000000000000000000..2ed883bec2215d1723b1feac7f0c9a2b424315e4
--- /dev/null
+++ b/s/NewIRQs
@@ -0,0 +1,1186 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL     => NewIRQs
+
+; *****************************************************************************
+;
+; Main IRQ routine:
+;     Push  workregs,lr
+;     IRQsema -> TOS
+;     stack -> IRQsema
+;     call IRQ1V
+;     IRQs off
+;     TOS ->IRQsema
+;     process callback, pulling workregs,pc at some point
+;
+; *****************************************************************************
+
+Initial_IRQ_Code ROUT
+        SUB      lr, lr, #4
+        Push     "r0-r3, r11, r12, lr"
+; ** For Pete's sake remember to change the heap manager if you change the above
+; ** register list!!!!!!! And the [sp_irq, #4*6] below
+ [ IRQSTK - 7*4 :AND: 15 <> 0
+ ! 0,"IRQ STM/LDM making extra S cycle into N"
+ ]
+
+ [ CPU_Type = "ARM600"
+        BIC     pc, pc, #&FC000000      ; get back out of shadow ROM
+        NOP                             ; (this instruction skipped)
+        mrs     AL, r0, SPSR_all        ; r0 = saved PSR
+        AND     r1, r0, #I32_bit + F32_bit ; r1 = caller I&F flags, in ARM6 place
+        ORR     lr, lr, r1, LSL #IF32_26Shift ; put IF in place
+        AND     r1, r0, #&F0000003      ; r1 = caller NZCV and mode bits
+        ORR     lr, lr, r1              ; lr = 26 bit style return addr + PSR
+        STR     lr, [sp_irq, #4*6]      ; store back into stack
+
+        BIC     r0, r0, #&1F            ; clear out foreground mode bits
+        ORR     r0, r0, #I32_bit + IRQ26_mode ; force IRQ_26 mode and I_bit set
+        msr     AL, CPSR_all, r0
+ ]
+
+        MOV     r12, #0
+        LDR     r0, [r12, #IRQsema]
+        Push    r0
+        STR     sp_irq, [r12, #IRQsema]
+        MOV     lr, pc
+        LDR     pc, [r12, #IRQ1V]
+
+; IRQ1V called with r0-r3,r11,r12 trashable. r12=0
+
+; Stu has a theory that 1N cycle can be saved by the default IRQ1V pointing
+; at a location containing a branch to our code; we then do something like
+;  LDR R0, [R12, #IRQ1V]
+;  CMP R0, #OurIRQ1V
+;  BNE somebodysonIRQ1V
+;  .... fall into default IRQ1V code
+
+        MOV      r11, #0
+        Pull     r0
+        STR      r0, [r11, #IRQsema]
+
+        LDRB     r11, [r11, #CallBack_Flag]
+        CMP      r11, #0
+        Pull     "r0-r3, r11, r12, pc", EQ, ^
+
+        TST      r11, #CBack_Postpone
+        LDREQ    lr, [sp_irq, #4*6]
+        TSTEQ    lr, #SVC_mode :OR: I_bit
+        Pull     "r0-r3, r11, r12, pc", NE, ^
+
+; Do a CallBack: asked for, not postponed, and we're returning into user mode.
+
+        Pull     "r0-r3, r11, r12"
+        TEQP     pc, #SVC_mode
+        MOVNV    r0, r0
+        Push     "r10-r12"
+        TEQP     pc, #IRQ_mode
+        MOVNV    r0, r0
+        Pull     "r12"                  ; lr really
+        TEQP     pc, #SVC_mode
+        MOVNV    r0, r0
+        MOV      lr, r12
+        MOV      r10, #0
+        LDRB     r11, [r10, #CallBack_Flag]
+        B        Do_CallBack
+
+        LTORG
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Default IRQ1V: despatch on interrupting device
+
+; Now copied to RAM, together with vector entries and device tables
+
+DefaultIRQ1Vcode ROUT
+
+ [ IO_Type = "IOMD"
+        MOV     r3, #IOC                ; base for IOC and IOMD
+        LDRB    r0, [r3, #IOMD_DMAREQ]
+        TEQ     r0, #0
+        ADRNE   r1, IrqDMADevnos
+        LDREQB  r0, [r3, #IOCIRQREQB]   ; if not DMA then assume IRQB until we know otherwise
+        ADREQ   r1, IrqReqBDevnos
+
+ [ MorrisSupport
+;>>>RCM Says should we use separate Morris specific code, cos Morris doesn't support
+;>>>RCM all IOMD_DMAREQ bits and non Morris machines don't have IOMD_IRQRQD.
+;>>>RCM Look at use of NoInterrupt.
+        TEQEQ   r0, #0
+        LDREQB  r0, [r3, #IOMD_IRQRQD]
+        ADREQ   r1, IrqReqDDevnos
+ ]
+        TEQEQ   r0, #0
+        LDREQB  r0, [r3, #IOCIRQREQA]   ; not DMA and not IRQB so assume IRQA
+        ADREQ   r1, IrqReqADevnos
+
+        LDRB    r0, [r1, r0]            ; pick up offset in device despatcher
+        ADD     r1, pc, r0, LSL #2      ; so table contains DevNo * 3
+        LDMIA   r1, {r12, pc}
+ |
+        MOV     r3, #IOC
+        LDRB    r0, [r3, #IOCIRQREQB]
+        CMP     r0, #0
+        LDREQB  r0, [r3, #IOCIRQREQA]
+
+        ADREQ   r1, IrqReqADevnos
+        ADRNE   r1, IrqReqBDevnos
+        LDRB    r0, [r1, r0]            ; pick up offset in device despatcher
+
+        ADD     r1, pc, r0, LSL #2      ; so table contains DevNo * 3
+        LDMIA   r1, {r12, pc}
+ ]
+
+; ******* IRQ device handlers entered with r0-r3,r11,r12,r14 trashable *******
+;   r3  -> IOC
+;   r12 =  what they asked for
+;   r14 =  return address to MOS IRQ exit sequence
+
+ [ IO_Type = "IOMD"
+  [ MorrisSupport
+NoInterrupt * 27 ; Morris has IOMD's extra interrupts plus 5 of its own
+  |
+NoInterrupt * 22 ; IOMD has 6 more interrupts for DMA
+  ]
+ |
+NoInterrupt * 16 ; internal devno; when ReqA = 0!
+ ]
+
+Devices
+
+; Register A devices
+; pbusy handler
+        & 0             ; R12 value
+        & IRQ           ; call address
+        & 0             ; link
+; ringing handler
+        & 0
+        & IRQ
+        & 0
+; printer acknowledge
+ [ DriversInKernel
+        & OsbyteVars
+        & PrinterIRQ
+        & 0
+ |
+        & 0
+        & IRQ
+        & 0
+ ]
+; vsync handler
+        & OsbyteVars
+        & VsyncIRQ
+        & 0
+; power on reset: this can't happen, but call IRQ2V if it does.
+        & 0
+        & IRQ
+        & 0
+; timer0
+        & OsbyteVars
+        & TickOne
+        & 0
+; timer1
+        & 0
+        & IRQ
+        & 0
+; FIQ downgrade
+        & 0
+        & IRQ
+        & 0
+
+; register B devices
+; PFIQ downgrade
+        & PFIQasIRQ_Chain - (PodDesp_Link-PodDesp_R12Val)
+        & PFIQasIRQ_Despatch
+        & 0
+; sound
+        & 0
+        & IRQ
+        & 0
+; serial
+ [ DriversInKernel
+        & OsbyteVars
+        & RS423IRQ
+        & 0
+ |
+        & 0
+        & IRQ
+        & 0
+ ]
+; winnie IRQ
+        & 0
+        & IRQ
+        & 0
+; Disc changed
+        & 0
+        & IRQ
+        & 0
+; podule IRQ
+        & PIRQ_Chain - (PodDesp_Link-PodDesp_R12Val)
+        & PIRQ_Despatch
+        & 0
+; serial TX     (Keyboard serial transmit register empty)
+        & IOC
+ [ Keyboard_Type = "A1A500"
+        & IrqTx
+ |
+        & IRQ
+ ]
+        & 0
+; serial RX     (Keyboard serial receive register full)
+        & IOC
+ [ Keyboard_Type = "A1A500"
+        & IrqRx
+ |
+        & IRQ
+ ]
+        & 0
+
+ [ IO_Type = "IOMD"
+; IOMD DMA devices
+; DMA channel 0
+        & 0
+        & IRQ
+        & 0
+; DMA channel 1
+        & 0
+        & IRQ
+        & 0
+; DMA channel 2
+        & 0
+        & IRQ
+        & 0
+; DMA channel 3
+        & 0
+        & IRQ
+        & 0
+; Sound DMA channel 0
+        & 0
+        & IRQ
+        & 0
+; Sound DMA channel 1
+        & 0
+        & IRQ
+        & 0
+ ]
+
+ [ MorrisSupport
+; register D devices
+; Mouse port Rx full
+        & IOC
+        & IRQ
+        & 0
+; Mouse port Tx empty
+        & IOC
+        & IRQ
+        & 0
+; AtoD (Joystick)
+        & 0
+        & IRQ
+        & 0
+; Nevent1
+        & 0
+        & IRQ
+        & 0
+; Nevent2
+        & 0
+        & IRQ
+        & 0
+ ]
+
+; Neither A or B is interrupting, which is impossible: just call IRQ2V anyway
+        & 0
+        & IRQ
+        & 0
+
+
+; Following tables encode the priority of the devices within each register
+
+       GBLA  DTabC
+DTabC  SETA 1
+
+; generic IRQA bits
+IrqReqAPrio0 * por_bit
+IrqReqADev0  * PowerOn_DevNo
+
+IrqReqAPrio2 * pbusy_bit
+IrqReqADev2 * PrinterBusy_DevNo
+
+IrqReqAPrio4 * timer1_bit
+IrqReqADev4 * Timer1_DevNo
+
+IrqReqAPrio5 * vsync_bit
+IrqReqADev5 * VSync_DevNo
+
+IrqReqAPrio6 * timer0_bit
+IrqReqADev6 * Timer0_DevNo
+
+IrqReqAPrio7 * force_bit
+IrqReqADev7 * FIQDowngrade_DevNo
+
+; Machine specific IRQB bits
+
+ [ IO_Type = "IOC-A1" :LOR: IO_Type = "IOC-A500"
+IrqReqAPrio1 * ring_bit
+IrqReqADev1 * Ringing_DevNo
+
+IrqReqAPrio3 * pack_bit
+IrqReqADev3 * PrinterAck_DevNo
+ ]
+
+ [ IO_Type = "IOEB"
+IrqReqAPrio1 * IOEB_battery_low_bit
+IrqReqADev1 * Ringing_DevNo
+
+IrqReqAPrio3 * IOEB_floppy_index_bit
+IrqReqADev3 * PrinterAck_DevNo
+ ]
+
+ [ IO_Type = "IOMD"
+IrqReqAPrio1 * 1:SHL:1          ; not used
+IrqReqADev1 * Ringing_DevNo
+
+IrqReqAPrio3 * IOMD_floppy_index_bit
+IrqReqADev3 * PrinterAck_DevNo
+ ]
+
+IrqReqADevnos
+  =  NoInterrupt*3
+  WHILE DTabC <256
+  [ (DTabC:AND:IrqReqAPrio7)<>0
+  = IrqReqADev7*3
+  |
+  [ (DTabC:AND:IrqReqAPrio6)<>0
+  = IrqReqADev6*3
+  |
+  [ (DTabC:AND:IrqReqAPrio5)<>0
+  = IrqReqADev5*3
+  |
+  [ (DTabC:AND:IrqReqAPrio4)<>0
+  = IrqReqADev4*3
+  |
+  [ (DTabC:AND:IrqReqAPrio3)<>0
+  = IrqReqADev3*3
+  |
+  [ (DTabC:AND:IrqReqAPrio2)<>0
+  = IrqReqADev2*3
+  |
+  [ (DTabC:AND:IrqReqAPrio1)<>0
+  = IrqReqADev1*3
+  |
+  [ (DTabC:AND:IrqReqAPrio0)<>0
+  = IrqReqADev0*3
+  ]
+  ]
+  ]
+  ]
+  ]
+  ]
+  ]
+  ]
+DTabC SETA DTabC+1
+  WEND
+
+; generic IRQB bits
+IrqReqBPrio2 * podule_FIQ_as_IRQ_bit
+IrqReqBDev2  * PFIQasIRQ_DevNo
+
+IrqReqBPrio3 * serial_Tx_bit
+IrqReqBDev3 * SerialTx_DevNo
+
+IrqReqBPrio4 * serial_Rx_bit
+IrqReqBDev4 * SerialRx_DevNo
+
+IrqReqBPrio5 * podule_IRQ_bit
+IrqReqBDev5 * Podule_DevNo
+
+IrqReqBPrio7 * serial_bit
+IrqReqBDev7 * Serial_DevNo
+
+; Machine specific IRQB bits
+
+ [ IO_Type = "IOC-A500"
+IrqReqBPrio0 * 0
+IrqReqBDev0 * NoInterrupt
+
+IrqReqBPrio1 * sound_IRQ_bit
+IrqReqBDev1 * Sound_DevNo
+
+IrqReqBPrio6 * winnie_IRQ_bit :OR: winnie_DRQ_bit
+IrqReqBDev6 * WinnieIRQ_DevNo
+ ]
+
+ [ IO_Type = "IOC-A1"
+IrqReqBPrio0 * winnie_DRQ_bit
+IrqReqBDev0 * DiscChanged_DevNo
+
+IrqReqBPrio1 * sound_IRQ_bit
+IrqReqBDev1 * Sound_DevNo
+
+IrqReqBPrio6 * winnie_IRQ_bit
+IrqReqBDev6 * WinnieIRQ_DevNo
+ ]
+
+ [ IO_Type = "IOEB"
+IrqReqBPrio0 * IOEB_ide_IRQ_bit
+IrqReqBDev0 * DiscChanged_DevNo
+
+IrqReqBPrio1 * IOEB_sound_IRQ_bit
+IrqReqBDev1 * Sound_DevNo
+
+IrqReqBPrio6 * IOEB_floppy_IRQ_bit
+IrqReqBDev6 * WinnieIRQ_DevNo
+ ]
+
+ [ IO_Type = "IOMD"
+IrqReqBPrio0 * IOMD_floppy_IRQ_bit
+IrqReqBDev0 * DiscChanged_DevNo
+
+IrqReqBPrio1 * IOMD_HardDisc_IRQ_bit
+IrqReqBDev1 * Sound_DevNo
+
+IrqReqBPrio6 * IOMD_Network_IRQ_bit
+IrqReqBDev6 * WinnieIRQ_DevNo
+ ]
+
+
+
+DTabC  SETA 1
+
+IrqReqBDevnos
+  =  NoInterrupt*3
+
+  WHILE DTabC <256
+  [ (DTabC:AND:IrqReqBPrio7)<>0
+  = IrqReqBDev7*3
+  |
+  [ (DTabC:AND:IrqReqBPrio6)<>0
+  = IrqReqBDev6*3
+  |
+  [ (DTabC:AND:IrqReqBPrio5)<>0
+  = IrqReqBDev5*3
+  |
+  [ (DTabC:AND:IrqReqBPrio4)<>0
+  = IrqReqBDev4*3
+  |
+  [ (DTabC:AND:IrqReqBPrio3)<>0
+  = IrqReqBDev3*3
+  |
+  [ (DTabC:AND:IrqReqBPrio2)<>0
+  = IrqReqBDev2*3
+  |
+  [ (DTabC:AND:IrqReqBPrio1)<>0
+  = IrqReqBDev1*3
+  |
+  [ (DTabC:AND:IrqReqBPrio0)<>0
+  = IrqReqBDev0*3
+  ]
+  ]
+  ]
+  ]
+  ]
+  ]
+  ]
+  ]
+DTabC SETA DTabC+1
+  WEND
+
+
+ [ IO_Type = "IOMD"
+; Prioritised IOMD DMA device numbers
+
+IrqDMAPrio0     * 1:SHL:5
+IrqDMADev0      * IOMD_DMASound1_DevNo
+
+IrqDMAPrio1     * 1:SHL:4
+IrqDMADev1      * IOMD_DMASound0_DevNo
+
+IrqDMAPrio2     * 1:SHL:3
+IrqDMADev2      * IOMD_DMAChannel3_DevNo
+
+IrqDMAPrio3     * 1:SHL:2
+IrqDMADev3      * IOMD_DMAChannel2_DevNo
+
+IrqDMAPrio4     * 1:SHL:1
+IrqDMADev4      * IOMD_DMAChannel1_DevNo
+
+IrqDMAPrio5     * 1:SHL:0
+IrqDMADev5      * IOMD_DMAChannel0_DevNo
+
+
+DTabC SETA 1
+
+IrqDMADevnos
+  = NoInterrupt*3
+
+; Top 2 bits are always 0 so table need only be 64 bytes
+  WHILE DTabC <64
+  [ (DTabC:AND:IrqDMAPrio5)<>0
+  = IrqDMADev5*3
+  |
+  [ (DTabC:AND:IrqDMAPrio4)<>0
+  = IrqDMADev4*3
+  |
+  [ (DTabC:AND:IrqDMAPrio3)<>0
+  = IrqDMADev3*3
+  |
+  [ (DTabC:AND:IrqDMAPrio2)<>0
+  = IrqDMADev2*3
+  |
+  [ (DTabC:AND:IrqDMAPrio1)<>0
+  = IrqDMADev1*3
+  |
+  [ (DTabC:AND:IrqDMAPrio0)<>0
+  = IrqDMADev0*3
+  ]
+  ]
+  ]
+  ]
+  ]
+  ]
+DTabC SETA DTabC+1
+  WEND
+
+ ]
+
+
+ [ MorrisSupport
+; Prioritised IRQD device numbers
+
+IrqReqDPrio0     * 1:SHL:4
+IrqReqDDev0      * IOMD_Event2_DevNo
+
+IrqReqDPrio1     * 1:SHL:3
+IrqReqDDev1      * IOMD_Event1_DevNo
+
+IrqReqDPrio2     * 1:SHL:2
+IrqReqDDev2      * IOMD_AtoD_DevNo
+
+IrqReqDPrio3     * 1:SHL:1
+IrqReqDDev3      * IOMD_MouseTxEmpty_DevNo
+
+IrqReqDPrio4     * 1:SHL:0
+IrqReqDDev4      * IOMD_MouseRxFull_DevNo
+
+
+DTabC  SETA 1
+
+IrqReqDDevnos
+  =  NoInterrupt*3
+
+; Top 3 bits are always 0 so table need only be 32 bytes
+  WHILE DTabC <32
+  [ (DTabC:AND:IrqReqDPrio4)<>0
+  = IrqReqDDev4*3
+  |
+  [ (DTabC:AND:IrqReqDPrio3)<>0
+  = IrqReqDDev3*3
+  |
+  [ (DTabC:AND:IrqReqDPrio2)<>0
+  = IrqReqDDev2*3
+  |
+  [ (DTabC:AND:IrqReqDPrio1)<>0
+  = IrqReqDDev1*3
+  |
+  [ (DTabC:AND:IrqReqDPrio0)<>0
+  = IrqReqDDev0*3
+  ]
+  ]
+  ]
+  ]
+  ]
+DTabC SETA DTabC+1
+  WEND
+
+ ]
+
+
+DefaultIRQ1Vcode_end
+
+  ASSERT DefaultIRQ1Vcode_end - DefaultIRQ1Vcode <= DefIRQ1Vspace
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Specialist despatchers for podules
+
+                  ^  0
+PodDesp_Address   #  4     ; address of IRQ status byte
+PodDesp_Mask      #  4     ; for use on above
+PodDesp_R12Val    #  4
+PodDesp_CallAddr  #  4     ; address to call if ?Address AND Mask <> 0
+PodDesp_Link      #  4     ; next node
+PodDesp_NodeSize  #  0
+
+ [ True
+
+; In    r12 =    PFIQasIRQ_Chain - (PodDesp_Link-PodDesp_R12Val)
+;             or PIRQ_Chain - (PodDesp_Link-PodDesp_R12Val)     from despatcher
+
+PFIQasIRQ_Despatch ROUT
+
+PIRQ_Despatch ; All the same thing now
+
+ |
+
+PFIQasIRQ_Despatch  ROUT
+
+        LDR     r12, =PFIQasIRQ_Chain - (PodDesp_Link-PodDesp_R12Val)
+        B       %FT01
+
+PIRQ_Despatch ; NOROUT
+
+        LDR     r12, =PIRQ_Chain - (PodDesp_Link-PodDesp_R12Val)
+ ]
+
+
+01      LDR     r12, [r12, #PodDesp_Link-PodDesp_R12Val]
+        LDMIA   r12!, {r1, r2}           ; address and mask
+
+ [ Fix11
+; TMD 09-Jun-89: Don't corrupt r0 - it's needed by the default IRQ2 routine
+        LDRB    r1, [r1]
+        ANDS    r1, r1, r2
+ |
+        LDRB    r0, [r1]
+        ANDS    r0, r0, r2
+ ]
+        BEQ     %BT01
+        LDMIA   r12, {r12, pc}
+
+
+Default_PIRQHandler_Node
+Default_PFIQasIRQHandler_Node
+        &       .+4                     ; address we know has non-zero value!
+        &       -1                      ; mask
+        &       0                       ; handler r12
+        &       IRQ                     ; handler code
+        &       0                       ; null link for naff release checking
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Claim of device vectors
+
+; r0 = Device number
+; r1 = call address
+; r2 = r12 value
+; r0 = PFIQ|PIRQ devno -> r3 = interrupt location
+;                         r4 = interrupt mask
+
+DeviceVector_Claim ROUT
+
+        Push    "r0-r3, lr"
+
+01      SWI     XOS_ReleaseDeviceVector ; Release until bored
+        BVC     %BT01
+
+        LDR     r0, [sp]
+        CMP     r0, #NoInterrupt
+        BGE     DV_Fail_NaffDevNo
+
+        CMP     r0, #Podule_DevNo
+        CMPNE   r0, #PFIQasIRQ_DevNo
+        BEQ     PoduleChainClaim
+
+        MOV     r3, #12
+        BL      ClaimSysHeapNode
+        BVS     DV_Exit
+        LDR     r0, [sp]
+        ADD     r0, r0, r0, LSL #1      ; *3
+        LDR     r1, =DefaultIRQ1V-DefaultIRQ1Vcode+Devices
+        ADD     r1, r1, r0, LSL #2
+        TEQP    pc, #SVC_mode+I_bit     ; IRQs off for update
+        LDMIA   r1, {r0, r3, r10}
+        STMIA   r2, {r0, r3, r10}       ; copy current head into node
+        LDR     r10, [sp, #4*2]         ; r12 value
+        LDR     r11, [sp, #4*1]         ; call address
+        MOV     r12, r2
+        STMIA   r1, {r10-r12}           ; copy given info into head
+
+
+DV_Exit STRVS   r0, [sp]                ; Common exit for both claim + release
+        Pull    "r0-r3, lr"
+        B       SLVK_TestV
+
+
+DV_Fail_NaffDevNo
+        ADR     r0, ErrorBlock_NaffDevNo
+      [ International
+        BL      TranslateError
+      |
+        SETV
+      ]
+        B       DV_Exit
+
+        MakeErrorBlock NaffDevNo
+
+
+PoduleChainClaim
+        MOV     r3, #PodDesp_NodeSize
+        BL      ClaimSysHeapNode
+        BVS     DV_Exit
+        MOV     r10, r2
+        LDMFD   sp, {r0-r3}
+        STR     r1, [r10, #PodDesp_CallAddr]
+        STR     r2, [r10, #PodDesp_R12Val]
+        STR     r3, [r10, #PodDesp_Address]
+        STR     r4, [r10, #PodDesp_Mask]
+        CMP     r0, #Podule_DevNo
+        LDREQ   r0, =PIRQ_Chain
+        LDRNE   r0, =PFIQasIRQ_Chain
+        TEQP    pc, #SVC_mode+I_bit     ; IRQs off for update
+        LDR     r1, [r0]
+        STR     r1, [r10, #PodDesp_Link]
+        STR     r10, [r0]
+        B       DV_Exit
+
+; .............................................................................
+; Release of device vectors
+
+; r0 = Device number
+; r1 = call address
+; r2 = r12 value
+; r0 = PFIQ|PIRQ devno -> r3 = interrupt location (LDRB always used)
+;                         r4 = interrupt mask
+
+DeviceVector_Release ROUT
+
+        Push    "r0-r3, lr"             ; Ensure same regset as above
+        CMP     r0, #NoInterrupt
+        BGE     DV_Fail_NaffDevNo
+
+        TEQP    pc, #SVC_mode + I_bit   ; IRQs off while holding context
+        CMP     r0, #Podule_DevNo
+        CMPNE   r0, #PFIQasIRQ_DevNo
+        BEQ     PoduleChainRelease
+
+        ADD     r0, r0, r0, LSL #1      ; *3
+        LDR     r12, =DefaultIRQ1V-DefaultIRQ1Vcode+Devices
+        ADD     r12, r12, r0, LSL #2    ; address of node
+        MOV     r11, #-1                ; "fudge" predecessor node
+
+01      LDMIA   r12, {r3, r10}
+        CMP     r3, r2
+        CMPEQ   r10, r1
+        BEQ     %FT02                   ; found it
+        MOV     r11, r12
+        LDR     r12, [r12, #8]          ; get the link
+        CMP     r12, #0
+        BNE     %BT01
+
+11      ADR     r0, ErrorBlock_BadDevVecRel
+      [ International
+        BL      TranslateError
+      |
+        SETV
+      ]
+        B       DV_Exit
+
+        MakeErrorBlock BadDevVecRel
+
+
+02      CMP     r11, #-1
+        BEQ     %FT03
+        MOV     r2, r12
+        LDR     r12, [r2, #8]
+        STR     r12, [r11, #8]          ; node delinked
+        B       %FT04
+
+03      LDR     r2, [r12, #8]           ; freeable = nextnode
+        LDMIA   r2,  {r0, r1, r3}       ; copy next node into head posn
+        STMIA   r12, {r0, r1, r3}
+
+04
+        BL      FreeSysHeapNode         ; free block
+        B       DV_Exit
+
+
+PoduleChainRelease
+        CMP     r0, #Podule_DevNo
+        LDREQ   r0, =PIRQ_Chain-PodDesp_Link
+        LDRNE   r0, =PFIQasIRQ_Chain-PodDesp_Link
+
+10      LDR     r12, [r0, #PodDesp_Link]
+        CMP     r12, #0
+        BEQ     %BT11
+        LDR     r11, [r12, #PodDesp_Address]
+        CMP     r11, r3
+        LDREQ   r11, [r12, #PodDesp_Mask]
+        CMPEQ   r11, r4
+        LDREQ   r11, [r12, #PodDesp_CallAddr]
+        CMPEQ   r11, r1
+        LDREQ   r11, [r12, #PodDesp_R12Val]
+        CMPEQ   r11, r2
+        MOVNE   r0, r12
+        BNE     %BT10
+
+        LDR     r11, [r12, #PodDesp_Link]
+        STR     r11, [r0,  #PodDesp_Link]
+        MOV     r2, r12
+        B       %BT04
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Default device owner for IRQ not recognised by system: pass to IRQ2V
+
+IRQ ROUT
+
+        Push    "r10, lr"
+ [ False
+ ASSERT VIDC_Type <> "VIDC20"
+ MOV r14, #11
+ MUL r14, r0, r14
+ MOV r14, r14, LSR #5
+ ADR r10, irq_vtable
+ LDR r14, [r10, r14, LSL #2]
+ MOV r10, #VIDC
+ STR r14, [r10]
+ ]
+        MOV     r10, #IrqV
+        BL      CallVector
+
+        Pull    "r10, pc"               ; return: someone will always claim it.
+
+ [ False
+irq_vtable
+ DCD &40000000 + &444
+ DCD &40000000 + &008
+ DCD &40000000 + &080
+ DCD &40000000 + &088
+
+ DCD &40000000 + &800
+ DCD &40000000 + &808
+ DCD &40000000 + &880
+ DCD &40000000 + &FA8
+
+ DCD &40000000 + &8AF
+ DCD &40000000 + &00F
+ DCD &40000000 + &0F0
+ DCD &40000000 + &0FF
+
+ DCD &40000000 + &F00
+ DCD &40000000 + &F0F
+ DCD &40000000 + &FF0
+ DCD &40000000 + &FFF
+ ]
+
+; *****************************************************************************
+; Default IRQ2V:
+;   r0  must still have devno*3 in it
+;   r12 is 0 (from vector)
+
+; Clear mask, clear IRQ as appropriate/possible
+
+; NB. a cheap way of dividing by ~3 is *11,/32: accurate for 0..31 result ...
+
+NOIRQ ROUT
+
+ [ False
+ ASSERT VIDC_Type <> "VIDC20"
+ MOV r14, #11
+ MUL r14, r0, r14
+ MOV r14, r14, LSR #5
+ ADR r10, irq_vtable
+ LDR r14, [r10, r14, LSL #2]
+ MOV r10, #VIDC
+ STR r14, [r10]
+ ]
+01      SUBS    r0, r0, #3
+        ADDGE   r12, r12, #1
+        BGT     %BT01                   ; r12 := r0 DIV 3
+
+ [ MorrisSupport
+; RCM 31-Jan-95 fix MED-04355, need to cope with interrupts from new Morris register
+        CMP     R12, #IOMD_MouseRxFull_DevNo
+        SUBHS   R12, R12, #IOMD_MouseRxFull_DevNo       ;reduce to bit number 0..7
+        MOVHS   R0, #IOMD_IRQMSKD                       ; in IRQ D interrupt register
+        BHS     %FT03
+ ]
+
+; ** TMD 14-Feb-94 - insert code here to fix MED-02859
+; ** Old code did not cope with the DMA case here
+
+ [ IO_Type = "IOMD"
+        CMP     r12, #16                ; check if a DMA device
+        BCC     %FT02                   ; it's not, so skip
+
+        SUB     r12, r12, #16           ; convert to a bit number in mask
+        MOV     r1, #1
+        MOV     r1, r1, LSL r12         ; convert to bit mask
+        LDR     r0, =IOC+IOMD_DMAMSK    ; point at mask register
+        LDRB    r12, [r0]               ; load mask
+        BIC     r12, r12, r1            ; knock out bit
+        STRB    r12, [r0]               ; store back
+        Pull    pc,,^                   ; and exit, claiming vector
+
+02
+ ]
+
+; ** end of insertion
+
+        CMP     r12, #8
+        MOVGE   r0, #IOCIRQMSKB
+        SUBGE   r12, r12, #8
+        MOVLT   r0, #IOCIRQMSKA
+ [ MorrisSupport
+03
+ ]
+        ADD     r0, r0, #IOC
+        MOV     r1, #1
+        MOV     r1, r1, LSL r12         ; bit to clear
+
+ [ :LNOT: NewClockChip                  ; fudge winnieDRQ on A500
+        CMP     r1, #winnie_IRQ_bit
+        CMPEQ   r0, #IOCIRQMSKB
+        ORREQ   r1, r1, #winnie_DRQ_bit ; turn both off.
+ ]
+        MOV     lr, pc
+        TEQP    pc, #IRQ_mode+I_bit+F_bit
+        LDRB    r12, [r0]       ; FIQs off for updating IOCIRQMSKA
+        BIC     r12, r12, r1
+        STRB    r12, [r0]       ; relevant IRQ disabled
+        TEQP    lr, #0          ; absolute minimum FIQ disable period
+
+        STRB    r1, [r0, #IOCIRQCLRA-IOCIRQMSKA] ; Clear IRQ
+        Pull    pc,,^           ; claim vector
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; The following bits have been appropriated from source.pmf.oseven to make
+; sure Tim's old code doesn't overwrite us when he gets back!
+
+; SWI OS_GenerateEvent: call event vector if enabled
+
+GenEvent ROUT
+
+        Push    lr
+        TEQP    pc, #SVC_mode+I_bit     ; Disable IRQs. MUST call these ones
+        BL      OSEVEN                  ; in SVC mode as people expect it
+        Pull    lr
+        B       SLVK
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Subroutine call version
+
+; In    r0 = event type
+;       r1,r2 parameters
+
+; Out   C=0 => event was enabled, or was >= 32 anyway
+;       C=1 => event was disabled, so vector not called
+
+OSEVEN ROUT
+
+        Push    lr
+
+        CMP     r0, #31                 ; Events >= 32 are ALWAYS raised. SKS
+                                        ; flags are HI if so, ie. NE
+        LDRLSB  r14, [r0, #OsbyteVars + :INDEX: EventSemaphores]
+                                        ; get semaphore for this event 0..31
+        CMPLS   r14, #0                 ; non-zero => enabled
+        Pull    pc, EQ                  ; if disabled, exit with C=1
+
+        Push    "r0-r3, r10-r12" ; r3 excessive ???
+        MOV     r10, #EventV            ; call event vector
+        BL      CallVector
+        CLC                             ; indicate event enabled
+        Pull    "r0-r3, r10-r12, pc"
+
+; ...................... default owner of EventV ..............................
+; Call Event handler
+
+; In    r12 = EvtHan_ws
+
+DefEvent ROUT
+
+        MOV     lr, pc                  ; link with all the bits
+        LDMIA   r12, {r12, pc}          ; call EventHandler, returns to ...
+
+        TEQ     r12, #1
+        Pull    pc,NE,^
+
+        LDRB    r14, [r12, #CallBack_Flag-1] ; IRQs are still disabled
+        ORR     r14, r14, #CBack_OldStyle
+        STRB    r14, [r12, #CallBack_Flag-1]
+        Pull    pc,,^                   ; claim EventV
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Process timer zero IRQ device (100Hz clock)
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+TickOne ROUT
+
+        Push    r14
+
+        MOV     R0, #timer0_bit
+        STRB    R0, [R3, #IOCIRQCLRA]   ; clear timer 0 interrupt
+
+        LDRB    R0, CentiCounter        ; Counter for VDU CTRL timing
+        SUBS    R0, R0, #1
+        STRCSB  R0, CentiCounter        ; decrement if not zero
+
+        LDR     R0, IntervalTimer +0
+        ADDS    R0, R0, #1              ; Increment the low 4 bytes
+        STR     R0, IntervalTimer +0
+
+        LDREQB  R0, IntervalTimer +4
+        ADDEQ   R0, R0, #1              ; and carry into 5th byte if necessary
+        STREQB  R0, IntervalTimer +4
+
+        Push    "R4,R12"                ; R0-R3 already pushed
+
+        TEQEQ   R0, #&100               ; has interval timer crossed zero ?
+        MOVEQ   R0, #Event_IntervalTimer ; Event ITCZ
+        BLEQ    OSEVEN
+
+        BL      CentiSecondTick         ; Notify keyboard of a centisecond
+
+        Pull    "R4,R12"
+
+        [ :LNOT: NewClockChip
+        LDRB    R1, SecondsDirty
+        TEQ     R1, #0                  ; if seconds dirty
+        BNE     %FT10                   ; we haven't synced yet
+        ]
+
+        LDR     R0, RealTime +0         ; Increment 5-byte real time
+        ADDS    R0, R0, #1
+        STR     R0, RealTime +0
+        LDRCSB  R0, RealTime +4
+        ADDCS   R0, R0, #1              ; Won't wrap until 2248 and then it
+        STRCSB  R0, RealTime +4         ; all falls over anyway
+
+        [ :LNOT: NewClockChip
+        LDRB    R0, CentiTime
+        ADD     R0, R0, #1
+        TEQ     R0, #100
+        MOVEQ   R0, #0
+        STRB    R0, CentiTime
+
+        LDRB    R0, SecondsTime
+        ADDEQ   R0, R0, #1              ; increment only if wrap from centisecs
+        TEQEQ   R0, #60
+        MOVEQ   R0, #0
+        STRB    R0, SecondsTime
+
+        B       NoTickThisTime          ; don't do dirty code
+10
+        LDRB    R0, MinTick
+        MOV     R1, #IOC
+        LDRB    R3, [R1, #IOCControl]   ; IOC control register
+        AND     R3, R3, #rtc_minutes_bit
+        TEQ     R3, R0                  ; Look for transition
+        BEQ     NoTickThisTime
+        TEQ     R3, #rtc_minutes_bit    ; from zero to one = minute!!!
+        STRB    R3, MinTick             ; One to zero = 30 seconds
+        MOV     R0, #0
+        STRB    R0, SecondsDirty        ; Mark the seconds as OK now !
+        STRB    R0, CentiTime ; When the minutes tick, ZERO the centiseconds!
+        MOVEQ   R0, #30
+        STRB    R0, SecondsTime         ; And set the seconds to 0 or 30 !
+
+        LDR     R0, =ticksperminute     ; get offset for 1 minute (60 secs)
+        MOVEQ   R0, R0, LSR #1          ; halve it if 30 second tick
+
+        LDR     R1, RealTime +0
+        ADDS    R1, R1, R0
+        STR     R1, RealTime +0
+        LDRCSB  R1, RealTime +4
+        ADDCS   R1, R1, #1
+        STRCSB  R1, RealTime +4
+
+NoTickThisTime
+        ]
+
+        LDRB    R0, TimerState          ; get switch state
+        TEQ     R0, #5                  ; toggles between 5 and 10
+
+        LDREQ   R1, TimerAlpha +0       ; either load from one
+        LDREQB  R2, TimerAlpha +4
+
+        LDRNE   R1, TimerBeta +0        ; or the other
+        LDRNEB  R2, TimerBeta +4
+
+        ADREQ   R3, TimerBeta +0        ; and point to t'other
+        ADRNE   R3, TimerAlpha +0
+
+        ADDS    R1, R1, #1              ; increment
+        ADC     R2, R2, #0              ; with carry
+
+        STR     R1, [R3]                ; and store back
+        STRB    R2, [R3, #4]
+
+        EOR     R0, R0, #&0F            ; 5 <-> 10
+        STRB    R0, TimerState
+
+        Push    R10
+
+ [ TickIrqReenter
+        MOV     R10, #TickerV           ; call 100Hz vector
+        BL      CallVector              ; IRQ's still disabled
+
+        BL      ProcessTickEventChain   ; Re-enables IRQs
+ |
+        BL      ProcessTickEventChain
+
+        MOV     R10, #TickerV           ; call 100Hz vector
+        BL      CallVector
+ ]
+        Pull    "R10,PC"
+
+; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Process VSync IRQ device
+; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+VsyncIRQ ROUT
+
+        Push    r14
+
+        MOV     R0, #vsync_bit
+        STRB    R0, [R3, #IOCIRQCLRA]   ; Clear the vsync interrupt
+
+        LDRB    R0, CFStime             ; decrement 'CFS' timer !
+        SUB     R0, R0, #1
+        STRB    R0, CFStime
+
+        VDWS    WsPtr                   ; Do our stuff before issuing VSYNC event
+        BL      VsyncCall
+        BYTEWS  WsPtr
+
+        MOV     R0, #Event_VSync        ; VSYNC event number
+        BL      OSEVEN
+
+        LDRB    R1, FlashCount
+        SUBS    R1, R1, #1
+        Pull    PC, CC                  ; was zero, so frozen
+        STRNEB  R1, FlashCount          ; else if now non-zero, store it back
+        Pull    PC, NE                  ; not time to flash yet
+
+        LDRB    R1, FlashState          ; Get the state and
+        EORS    R1, R1, #1              ; flip to the other one (setting flags)
+        STRB    R1, FlashState
+
+        LDREQB  R2, SpacPeriod          ; get appropriate new period
+        LDRNEB  R2, MarkPeriod
+        STRB    R2, FlashCount          ; and put into counter
+
+        VDWS    WsPtr
+        Push    R4
+        BEQ     dothesecondflash
+
+dothefirstflash
+        BL      DoFirstFlash
+        Pull    "R4, PC"
+
+dothesecondflash
+        BL      DoSecondFlash
+        Pull    "R4, PC"
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        END
diff --git a/s/NewReset b/s/NewReset
new file mode 100644
index 0000000000000000000000000000000000000000..cb1538db82f2b94542e8ad7d5ef00f656d0dd6f7
--- /dev/null
+++ b/s/NewReset
@@ -0,0 +1,2189 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        SUBT    => NewReset
+
+DuffEntry *     31*1024*1024+32*1024    ; Never any memory at this address
+
+SoftReset       * 0                     ; Reset types
+PowerOnReset    * 1
+ControlReset    * 2
+
+; CMOS RAM resetting stuff:
+CMOSLimit       * &F0
+
+; Keyboard flags
+                ^ 1
+CTRL_Down_Flag  # 1
+SHIFT_Down_Flag # 1
+KB_There_Flag   # 1
+
+R_Down_Flag     # 1      ; note that these 4 form one word!!
+T_Down_Flag     # 1
+Del_Down_Flag   # 1
+Copy_Down_Flag  # 1
+
+KeyDataPtr      # 4
+ [ MorrisSupport
+Port2Present    # 4
+ ]
+
+ [ MEMM_Type = "ARM600"
+
+; On ARM600, InitKbdWs is in zero page - check it's big enough
+
+        ASSERT  @ <= ?InitKbdWs
+ |
+
+; Else use PhysRam start
+
+InitKbdWs       *       PhysRam
+ ]
+
+ [ MEMM_Type <> "ARM600"
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; routine to set up a 32K chunk (for fixed areas)
+; Enter with R8 = PageSize, R2 = physram offset, R3 logaddr
+; R8 preserved on exit
+
+; This routine is not needed on ARM600, as MemSize sets up the L2PT for the static pages
+
+SlapIn32K
+        MOV     R12, lr
+        ADRL    R1, PageShifts-1
+        LDRB    R1, [R1, R8, LSR #12]
+        MOV     R2, R2, LSR R1          ; DivRem  R0, R2, R8, R1
+                                        ; R2 := cam entry no
+        ADD     R5, R3, #32*1024
+SlapInNext
+        BL      BangCam
+        ADD     R2, R2, #1
+        ADD     R3, R3, R8
+        CMP     R3, R5
+        BNE     SlapInNext
+        MOV     PC, R12
+ ]
+
+
+;       AddCamEntries
+;
+; in:   r0 -> appropriate part of CAM map
+;       r1 = ap (+ CB bits in new world)
+;       r2 = log address
+;       r7 = amount of cam map to do
+;       r8 = PageSize
+;
+; out:  r0, r3-r12 preserved
+;       r1 corrupted
+;       r2 updated by relevant amount
+
+AddCamEntries ROUT
+ [ ExpandedCamMap
+        Push    "r0, lr"
+        MOV     lr, r1                  ; access privs (PPL)
+        MOV     r1, r7
+01
+        STMIA   r0!, {r2, lr}           ; store logaddr, PPL
+        ADD     r2, r2, r8              ; increment address by 1 page
+        SUBS    r1, r1, #8              ; decrement count of how much to do
+        BNE     %BT01
+        Pull    "r0, pc"
+ |
+        MOV     R1, #0
+        CMP     r2, #CursorChunkAddress
+        ORREQ   r2, r2, #&10000000      ; appropriate PPL
+01
+        STR     R2, [R0, R1]
+        ADD     R1, R1, #4
+        ADD     R2, R2, R8
+        CMP     R1, R7
+        BNE     %BT01
+        MOVS    pc, lr
+ ]
+
+; GetConfiguredSize - convert CMOS address into amount of memory
+; in:   r0 = CMOS address
+; out:  r0 corrupted
+;       r2 = amount in bytes
+
+; NB this routine doesn't do screen size mangling (yet!)
+
+GetConfiguredSize ENTRY "r1"
+        MOV     r1, #0
+        LDR     r1, [r1, #Page_Size]
+        ADRL    lr, PageShifts-1
+        LDRB    r1, [lr, r1, LSR #12]   ; r1 = log2 pagesize
+        MOV     r2, #127                ; mask of value
+
+        TEQ     r0, #FontCMOS           ; if fontsize
+        MOVEQ   r1, #12                 ; then in units of 4K, not pagesize
+        MOVEQ   r2, #255                ; and use full byte
+
+        BL      Read                    ; read CMOS ram
+        AND     r2, r0, r2              ; value anded with mask
+        MOV     r2, r2, LSL r1          ; and shifted up accordingly
+        EXIT
+
+FudgeConfigureRMA
+        Push   lr
+        B      ConfigureRMA
+
+ReadCMOSAndConfigure ROUT
+; R0 = index into CMOS RAM of byte with size in
+; R1 = place to save amount moved
+; R2 = CAM entry number to start at: updated
+; R3 = LogRam Address to move memory to
+; r11  PPL
+; Check for memory left, move as much as poss
+        Push    lr
+        BL      Read                    ; CMOS byte -> R0
+
+ [ :LNOT: NewStyle_FontArea
+        CMP     r3, #FontCacheAddress
+        MOVEQ   r0, r0, LSL #12         ; *4K
+        BEQ     NotScreen
+ ]
+
+        AND     R0, R0, #127            ; mask to same bitfield as status
+ConfigureRMA
+        MOV     R10, #0
+        LDR     R10, [R10, #Page_Size]
+        MUL     R0, R10, R0             ; get size in bytes
+        CMP     R3, #ScreenEndAdr       ; screen?
+        BNE     NotScreen
+
+; quick pokery for sensible screensize
+
+        BL      MassageScreenSize
+        SUB     R3, R3, R0              ; step back.
+NotScreen
+        MOV     R5, #0                  ; amount moved
+        CMP     R0, #0
+        BEQ     NoMoreMemory
+
+ [ GetPagesFromFreePool
+
+; r0 = amount of memory to move
+; r1 = address to store size in
+; (r2 = page number to start at, ignored in our method)
+; r3 = address of where to put memory
+; r10 = page size
+; r11 = ap + CB
+
+        MOV     r6, r11                         ; r6 = ap + CB
+        MOV     r4, #FreePoolDANode
+        LDR     r7, [r4, #DANode_Base]
+        LDR     r8, [r4, #DANode_Size]
+        ADD     r7, r7, r8                      ; r7 -> end of free pool +1
+10
+        CMP     r8, r10                         ; if no free memory left
+        BCC     %FT20                           ; then tidy up
+        SUB     r7, r7, r10                     ; move free pool pointer backwards
+        Push    "r0, r1"
+        MOV     r0, r7
+        MOV     r1, r3
+        BL      MovePageAtR0ToR1WithAccessR6
+        Pull    "r0, r1"
+        ADD     r3, r3, r10                     ; advance "to" pointer
+        SUB     r8, r8, r10                     ; one less page of free memory
+        ADD     r5, r5, r10                     ; one more page done
+        SUBS    r0, r0, r10
+        BNE     %BT10
+20
+        STR     r8, [r4, #DANode_Size]
+ |
+        LDR     R8, [R5, #RAMLIMIT]
+
+; now set R6 = first entry not to use
+;         R7 = end of gap   "   "  "
+;         R8 = last entry we can use
+
+  [ MEMM_Type = "ARM600"
+        MOV     r7, #0
+        LDR     r6, [r7, #VideoSize]            ; find out how many pages in video area
+        MOV     r6, r6, LSR #12                 ; = page number of start of skipped bit
+        ASSERT  SoftCamMapSize = L2PTSize +4
+        MOV     r7, #L2PTSize
+        LDMIA   r7, {r7, lr}                    ; r7 = L2PTSize, lr = SoftCamMapSize
+        ADD     r7, r7, lr                      ; add sizes together
+        ADD     r7, r7, #StaticPagesSize + UndStackSize ; + number of bytes used for other static bits
+        ADD     r7, r6, r7, LSR #12             ; r7 = page number after static bit
+        MOV     r8, r8, LSR #12                 ; make r8 into highest page number+1
+  |
+        CMP     R8, #512*1024
+        MOVEQ   R6, #(512-96)*1024
+        MOVEQ   R7, #512*1024
+        MOVNE   R6, #(512-32)*1024
+   [ MEMM_Type = "MEMC2"
+        MOVNE   R7, #(512+96)*1024      ; if MEMC2, skip L2PT as well
+   |
+        MOVNE   R7, #(512+64)*1024
+   ]
+        Push    "R2"
+
+        ADRL    R2, PageShifts-1
+        LDRB    R2, [R2, R10, LSR #12]
+        MOV     R6, R6, LSR R2          ; DivRem  R6, R6, R10, R2
+        MOV     R7, R7, LSR R2
+        MOV     R8, R8, LSR R2
+
+        Pull    "R2"
+  ]
+
+CAMZapLoop
+        CMP     R2, R6                  ; if at gap, skip
+        MOVEQ   R2, R7
+        CMP     R2, R8                  ; if no more memory, give in
+        BEQ     NoMoreMemory
+        ADD     R5, R5, R10
+        Push    "R0, R1, R6"
+        BL      BangCamUpdate
+        Pull    "R0, R1, R6"
+        ADD     R2, R2, #1
+        ADD     R3, R3, R10
+        SUBS    R0, R0, R10
+        BGT     CAMZapLoop
+ ]
+NoMoreMemory
+        STR     R5, [R1]
+        Pull    "PC"
+
+; MassageScreenSize - called from ReadCMOSAndConfigure (above) and also from
+; ReadSysInfo
+
+MassageScreenSize ROUT
+        CMP     r0, #0
+        BNE     CmosScreenWillDo
+        LDR     r0, [r0, #RAMLIMIT]
+        CMP     r0, #512*1024
+        MOVEQ   r0, #80*1024
+        MOVNE   r0, #160*1024
+CmosScreenWillDo
+        CMP     r0, #20*1024            ; ensure mode 0 gettable
+        ADDCC   r0, r0, r10             ; if not, then add another page
+        BCC     CmosScreenWillDo
+        CMP     r0, #ScreenMaxSize
+        MOVHI   r0, #ScreenMaxSize
+        MOV     pc, lr
+
+        LTORG
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Data tables: VIDC := mode 0, all palette black
+
+VIDCTAB
+ [ VIDC_Type = "VIDC20"
+
+; Program Control Register first, to clear power-down bit
+
+        & &E0000400     ; CR: FIFO load 16 words, 1 bpp, ck/1, vclk
+
+; Don't bother programming all 256 palette entries, we'll be here all night
+; Since we're setting up a 1 bit-per-pixel mode, just do colours 0 and 1
+
+        & &10000000     ; Palette address register = 0
+        & &00000000     ; Colour 0 = black
+        & &00000000     ; Colour 1 = black
+        & &40000000     ; Border colour = black
+        & &50000000     ; Pointer colour 1 = black
+        & &60000000     ; Pointer colour 2 = black
+        & &70000000     ; Pointer colour 3 = black
+
+; Get a stable display up so we get stable signals
+
+        & &800003F8     ; HCR  = 76 + 88 + 96 + 640 + 96 + 28
+        & &81000044     ; HSWR = 76
+        & &82000098     ; HBSR = 76 + 88
+        & &830000F2     ; HDSR = 76 + 88 + 96
+        & &84000372     ; HDER = 76 + 88 + 96 + 640
+        & &850003D8     ; HBER = 76 + 88 + 96 + 640 + 96
+        & &860000F3     ; HCSR = HDSR
+
+        & &90000137     ; VCR  = 3 + 19 + 16 + 256 + 16 + 2
+        & &91000002     ; VSWR = 3
+        & &92000015     ; VBSR = 3 + 19
+        & &93000025     ; VDSR = 3 + 19 + 16
+ [ MEMC_Type = "IOMD"
+  [ Simulator
+        & &94000025     ; No displayed lines, so we don't get video FIFO u/flow when simulating
+  |
+        & &94000125     ; VDER = 3 + 19 + 16 + 256
+  ]
+ |
+        & &94000125     ; VDER = 3 + 19 + 16 + 256
+ ]
+        & &95000135     ; VBER = 3 + 19 + 16 + 256 + 16
+        & &96000025     ; VCSR = VDSR
+        & &97000025     ; VCER = VDSR
+
+        & &B1000001     ; SCR: sound disabled (+use 24MHz clock)
+
+        & &C00F1003     ; EREG = comp sync, DACs on, ereg output ext lut
+        & &D0000305     ; FSYNREG, clk = (3+1)/(5+1) * 24MHz = 16MHz
+        & &F0013000     ; DCR: bus D[31:0], Hdisc       ;RCM 29/9/94: changed from &F0012000 at PSwindells request
+        & &FFFFFFFF     ; That's the lot
+ |
+        & &00000000
+        & &04000000
+        & &08000000
+        & &0C000000
+        & &10000000
+        & &14000000
+        & &18000000
+        & &1C000000
+        & &20000000
+        & &24000000
+        & &28000000
+        & &2C000000
+        & &30000000
+        & &34000000
+        & &38000000
+        & &3C000000
+        & &40000000
+        & &44000000     ; Cursor -> black
+        & &48000000
+        & &4C000000     ; Palette programmed (avoid messy screen on reset)
+
+        & &807FC000     ; HCR  Get a stable display up so we get stable signals
+        & &8408C000     ; HSWR
+        & &881B0000     ; HBSR
+        & &94770000     ; HBER
+        & &A04E0000     ; VCR
+        & &A4024000     ; VSWR
+        & &A8050000     ; VBSR
+        & &B44E0000     ; VBER
+        & &C0000100     ; SFR  NB. TEST BIT !!! - also DFlynn requested value
+        & &E00000B2     ; CR   Set 640*256, 1 bit per pixel, rate of 12MHz
+                        ; change bottom byte to 22 for Linear Microvitecs (CS)
+                        ;                       B2 for Monochrome/Philips (SS)
+        & &8C208000     ; HDSR
+        & &90708000     ; HDER
+        & &98258000     ; HCSR
+        & &9C400000     ; HIR
+        & &AC098000     ; VDSR
+        & &B0498000     ; VDER
+        & &B8098000     ; VCSR
+        & &BC498000     ; VCER
+; don't mess with the stereo image registers: sound code will set them.
+        & &FFFFFFFF     ; That's the lot
+ ]
+
+
+ [ MEMM_Type <> "ARM600"
+        GBLL    ARM3_Support
+ ]
+
+ [ :DEF:ARM3_Support
+ARM3            CP     15
+
+CR0             CN      0
+CR_ID           CN      0
+CR_Flush        CN      1
+CR_Control      CN      2
+CR_Cacheable    CN      3
+CR_Updateable   CN      4
+CR_Disruptive   CN      5
+
+UndefVec *      &00000004
+
+; Default cacheable areas
+;
+; ROM                   Cacheable       (&03400000..&03FFFFFF)
+; I/O                   Not cacheable   (&03000000..&033FFFFF)
+; Phys RAM              Not cacheable   (&02000000..&02FFFFFF)
+; Log RAM (screen)      Not cacheable   (&01E00000..&01FFFFFF)
+; Log RAM (RAM Disc)    Not cacheable   (&01000000..&013FFFFF)
+;                       (No point in caching RAM Disc)
+; Log RAM (non-screen)  Cacheable       (&00000000..&01DFFFFF)
+Cacheable_Default DCD     2_11111100000000000111110011111111
+
+; Default updateable areas
+;
+; ROM/CAM               Not updateable  (&03800000..&03FFFFFF)
+; ROM/DAG               Not updateable  (&03400000..&037FFFFF)
+; I/O                   Not updateable  (&03000000..&033FFFFF)
+; Phys RAM              Not updateable  (&02000000..&02FFFFFF)
+; Log RAM (screen)      Not updateable  (&01E00000..&01FFFFFF)
+; Log RAM               Updateable      (&00000000..&01DFFFFF)
+Updateable_Default DCD    2_00000000000000000111111111111111
+
+; Default disruptive areas
+;
+; CAM                   Disruptive      (&03800000..&03FFFFFF)
+; DAG                   Not disruptive  (&03400000..&037FFFFF)
+; I/O                   Not disruptive  (&03000000..&033FFFFF)
+; Phys RAM              Not disruptive  (&02000000..&02FFFFFF)
+; Log RAM               Not disruptive  (&00000000..&01FFFFFF)
+Disruptive_Default *    2_11110000000000000000000000000000
+
+UndefRet
+        MOVS    pc, lr
+ ]
+
+VIDCPhys        *       &03400000       ; used to address VIDC when MMU is off
+
+; Entered here after RESET (or BREAK)
+
+; This code must be capable of being executed at a non-ROM address and with the MMU off,
+; and running in 32-bit mode up until the call to MemSize.
+
+CONT_Break
+        MOV     r1, #1          ; parameter passed to InitMEMC, to indicate Break not Reset
+        B       Continue
+
+CONT    ROUT
+        MOV     r1, #0          ; parameter passed to InitMEMC, to indicate Reset not Break
+Continue
+
+; First bang MEMC to ensure safety
+
+        BL      InitMEMC        ; initialise MEMC CR, and turn off MMU if it's on
+
+; VInit etc set on ze mode change: no DMA going yet so don't set owt.
+
+        MOV     R1, #VIDCPhys   ; Must ALWAYS initialise VIDC on reset or else
+        ADR     R2, VIDCTAB     ; we may get vsync interrupts that stiff us
+10      LDR     R0, [R2], #4    ; permanently as VIDC is in an undefined state
+        CMP     R0, #-1         ; so have mode 0 with all black palette
+        STRNE   R0, [R1]
+        BNE     %BT10
+
+; Now bang IOC (disable all but keyboard interrupts)
+
+        MOV     R1, #IOC
+        MOV     R0, #&FF                ; all inputs
+        STRB    R0, [R1, #IOCControl]   ; in case called by Tim
+
+        MOV     R0, #0
+        STRB    R0, [R1, #IOCIRQMSKA]   ; kein interrupts
+        STRB    R0, [R1, #IOCFIQMSK]    ; disable FIQs
+ [ IO_Type = "IOMD"
+        STRB    R0, [R1, #IOMD_DMAMSK]  ; disable DMA interrupts, too
+ ]
+
+ [ Keyboard_Type = "PC" :LAND: IO_Type <> "IOMD"
+        MOV     R0, #podule_IRQ_bit     ; used for PC keyboard h/w on a podule
+ |
+        MOV     R0, #KARTRxBit          ; used for Archi keyboard or IOMD PC keyboard
+ ]
+        STRB    R0, [R1, #IOCIRQMSKB]   ; allow communication with kbd, when I_bit gets cleared
+
+; now bits to allow CMOS read/write : need timer
+
+        LDR     R0, =20000-1    ; R0 = Timer delay (units of 0.5 microsecond)
+                                ; 20000*0.5E-6 = 0.01 Seconds (100Hz ticker)
+                                ; TMD 21-May-93: "-1" correction applied
+
+        STRB    R0, [R1, #Timer0LL]     ; Set up the delay
+        MOV     R0, R0, LSR #8
+        STRB    R0, [R1, #Timer0LH]
+        STRB    R0, [R1, #Timer0GO]     ; and start the ticks
+
+        MOV     R0, #timer0_bit
+        STRB    R0, [R1, #IOCIRQCLRA]   ; Clear pending t0 interrupt j.i.c.
+
+; now size memory
+
+        BL      MemSize                 ; out: r0 = page size, r1 = memory size, r2 = MEMC CR value, r3-r14 corrupt
+
+        MOV     R8, R0                  ; R8 = page size in bytes
+        MOV     R13, R1                 ; R13 is now the RAM size
+        MOV     R9, R2                  ; need this to set soft copy right
+
+        BL      TimeCPU                 ; r7 := CPU speed in kHz/MEMC1a flag
+
+; first ensure nothing dangerous in MEMC
+
+ [ MEMM_Type <> "ARM600"                ; all this stuff is done in MemSize
+ [ {TRUE}                               ; new code which allows for MEMC2's small pages
+        ADRL    r2, PageShifts-1
+        LDRB    r2, [r2, r8, LSR #12]   ; get log2(pagesize)
+        MOV     R2, R13, LSR R2         ; number of pages=total memory / pagesize
+        CMP     R2, #128                ; but if fewer than 128 (eg 64 on A305)
+        MOVCC   R2, #128                ; then use 128 (all MEMC1's pages need initialising,
+                                        ; even if half of them are not in use)
+ |
+        CMP     R13, #8*1024*1024       ; if >= 8MBytes,
+        MOVCS   R2, R13, LSR #15        ; then there are R13/32K pages
+        MOVCC   R2, #128                ; else there are 128 pages
+ ]
+        SUB     R2, R2, #1              ; maximum page number
+
+        LDR     R3, =DuffEntry          ; a non-existant piece of memory
+        MOV     R11, #AP_Duff           ; go away, users
+EmptyCamEntries
+        BL      BangCam                 ; ensure not mapped to anything
+        SUBS    R2, R2, #1              ; dangerous
+        BPL     EmptyCamEntries
+
+; Put in position all fixed areas of memory
+;   32K for cursor etc     : CAM entries for 512-32 to 512K in PhysRam
+
+; SlapIn32K needs R8 = pagesize
+; This is now already set up, so no need to compute it from
+; MEMC CR (which would be wrong on ARM600 anyway)
+
+        MOV     R11, #1                 ; user write protected
+        MOV     R3, #CursorChunkAddress
+        MOV     R2, #(512-32)*1024
+        BL      SlapIn32K
+
+        MOV     R3, #SysHeapChunkAddress ; 32K forced in system heap area
+; use phys 512+32 to 512+64K if R13<>512K,  512-96 to 512-64K if R13=512K
+        CMP     R13, #512*1024
+        MOVEQ   R2, #(512-96)*1024
+        MOVNE   R2, #(512+32)*1024
+
+        MOV     r11, #0
+        BL      SlapIn32K
+
+        MOV     R3, #0                  ; 32K system work area
+
+; use phys 512 to 512+32K if R13<>512K,  512-64 to 512-32K if R13=512K
+
+        CMP     R13, #512*1024
+        MOVEQ   R2, #(512-64)*1024
+        MOVNE   R2, #512*1024
+
+        BL      SlapIn32K
+ ]
+
+ [ :DEF:ARM3_Support
+        MOV     r0, #UndefVec
+        LDR     r1, [r0]
+        LDR     r2, UndefRet
+        MOV     r5, #0
+        STR     r2, [r0]
+        MRC     ARM3, 0, r5, CR_ID, CR0
+        STR     r1, [r0]
+        AND     r5, r5, #&ff00
+        TEQ     r5, #&0300
+        BNE     noarm3
+        LDR     r0, Cacheable_Default
+        LDR     r1, Updateable_Default
+        MOV     r2, #Disruptive_Default
+        MCR     ARM3, 0, r0, CR_Cacheable, CR0
+        MCR     ARM3, 0, r1, CR_Updateable, CR0
+        MCR     ARM3, 0, r2, CR_Disruptive, CR0
+        MRC     ARM3, 0, r3, CR_Cacheable, CR0
+        MRC     ARM3, 0, r4, CR_Updateable, CR0
+        MRC     ARM3, 0, r5, CR_Disruptive, CR0
+        TEQ     r0, r3
+        TEQEQ   r1, r4
+        TEQEQ   r2, r5
+        BNE     noarm3
+        MOV     r0, #3
+        MCR     ARM3, 0, r0, CR_Control, CR0
+noarm3
+ ]
+
+; the fixed areas give us : IRQ stack (in cursor), SVC stack (in sysheap base)
+; and bottom block makes most sense done here
+
+; now keyboard initialisation: initialise baud rate, send dummy, read dummy
+
+        MOV     R0, #InitKbdWs
+        MOV     R1, #0
+        STRB    R1, [R0, #CTRL_Down_Flag] ; clear CTRL down flag, R down flag
+        STRB    R1, [R0, #SHIFT_Down_Flag]
+        STRB    R1, [R0, #KB_There_Flag]
+        STR     R1, [R0, #R_Down_Flag]  ; all CMOS reset flags
+        STR     R1, [R0, #KeyDataPtr]
+ [ MorrisSupport
+        STR     R1, [R0, #Port2Present]
+ ]
+        B       SetUpKbd                ; No stack yet so branch and branch back.
+SetUpKbdReturn
+
+
+; set up reset interrupt handler (reads, discards keys, setting flags if CTRL or R)
+; NB on ARM600 we need to go into 32-bit mode, so we can safely overwrite vectors
+
+ [ CPU_Type = "ARM600"
+        mrs     AL, r0, CPSR_all        ; switch into IRQ32, still IRQs disabled
+        BIC     r0, r0, #&1F
+        ORR     r1, r0, #IRQ32_mode
+        msr     AL, CPSR_all, r1
+
+        LDR     sp_irq, =IRQSTK         ; set up sp_irq
+
+        ADRL    R2, MOSROMVecs          ; pick up from table
+        LDR     R2, [R2, #&18]          ; this gets overwritten while active,
+        MOV     R3, #0
+        STR     R2, [R3, #&18]          ; but hopefully by the same value!
+
+        ORR     r0, r0, #SVC32_mode     ; switch into SVC32
+        BIC     r0, r0, #I32_bit        ; and enable IRQs
+        msr     AL, CPSR_all, r0
+
+ ; in SVC32 from now until we've finished poking around with vectors
+
+ |
+        TEQP    pc, #IRQ_mode + I_bit   ; set up IRQ stack
+        NOP
+        LDR     sp_irq, =IRQSTK
+
+        ADRL    R0, MOSROMVecs          ; pick up from table
+        LDR     R0, [R0, #&18]          ; this gets overwritten while active,
+        MOV     R1, #0                  ; could have been changed by keyboard initialisation
+        STR     R0, [R1, #&18]          ; but hopefully by the same value!
+
+        TEQP    pc, #SVC_mode           ; Enable interrupts, back to supervisor mode
+        NOP
+ ]
+
+
+ [ :LNOT: AlwaysClearRAM
+
+; IF por OR FX200 bit set THEN clear memory
+        MOV     R0, #IOC
+        LDRB    R1, [R0, #IOCIRQSTAA]
+        ANDS    R1, R1, #por_bit
+        BNE     %FT20
+
+        LDR     R0, =OsbyteVars + :INDEX: ESCBREAK
+        LDRB    R1, [R0]
+        CMP     R1, #2                  ; unlike popular rumour, bit 1 ain't
+        CMPNE   R1, #3                  ; a flag
+        BNE     %FT30
+20
+ ]
+        BL      ClearPhysRAM
+30
+        MOV     r0, #0
+        STR     r9, [r0, #MEMC_CR_SoftCopy] ; set soft copy.
+        STR     r7, [r0, #MemorySpeed]  ; Remember CPU speed/MEMC1a flag
+        STR     r8, [r0, #Page_Size]    ; r8 is still page size from way up there
+        STR     r13, [r0, #RAMLIMIT]    ; save sussed memory size in LogRam
+
+        LDR     sp, =SVCSTK             ; set up a stack
+
+; do as much other initialisation as possible, to give keyboard stuff time
+
+ [ ProcessorVectors
+; Copy default processor vector table and default preveneers.
+; Code relies on preveneers being immediately after processor vector table
+; but could easily be changed into 2 copy loops.
+        ASSERT  ProcVecPreVeneers = ProcVec_End
+        ASSERT  DefaultPreVeneers = DefaultProcVecs+ProcVec_End-ProcVec_Start
+
+        ADRL    R0, DefaultProcVecs
+        LDR     R1, =ProcVec_Start
+        MOV     R2, #ProcVec_End-ProcVec_Start+ProcVecPreVeneersSize
+39
+        LDR     R3, [R0], #4
+        STR     R3, [R1], #4
+        SUBS    R2, R2, #4
+        BNE     %BT39
+ ]
+
+; copy handler addresses
+        ADRL    R1, MOSROMVecs+4        ; don't copy to 0: key stuff is using
+                                        ; it as workspace!
+        MOV     R0, #4
+
+40
+        LDR     R2, [R1], #4            ; N.B. IRQ handler better be same as the one in there
+        STR     R2, [R0], #4
+        TEQ     R0, #EndFiq-MOSROMVecs
+        BNE     %BT40
+
+ [ CPU_Type = "ARM600"
+
+; Now we have set up the hardware vectors we can drop back to SVC26 mode
+
+        mrs     AL, r0, CPSR_all
+        BIC     r0, r0, #&1F
+        ORR     r0, r0, #SVC26_mode
+        msr     AL, CPSR_all, r0
+ ]
+
+
+; Ensure any CMOS operation aborted
+
+        MOV     R1,#16                          ; Two bytes in case RTC transmitting
+35
+        BL      Start                           ; Start/clock edge
+        BL      Stop
+        SUBS    R1,R1,#1
+        BNE     %BT35
+
+ [ CacheCMOSRAM
+        BL      InitCMOSCache           ; initialise cache of CMOS RAM
+ ]
+
+; Now copy the initialised data
+        MOV     R0, #IRQ1V
+
+; first save IOC soft copy so can restore it
+
+        LDRB    R2, [R0, #IOCControlSoftCopy-IRQ1V]
+        Push    "R2"
+        LDRB    R2, [R0, #CannotReset-IRQ1V]
+        Push    "R2"
+
+        ADRL    r1, StartData
+DatCopy
+        LDR     R2, [R1], #4
+        STR     R2, [R0], #4
+        TEQ     R0, #(EndData-StartData+IRQ1V)
+        BNE     DatCopy
+
+ [ ResetIndirected
+        ADR     r2, CONT_Break
+        MOV     r0, #0
+        STR     r2, [r0, #ResetIndirection]
+ ]
+
+ [ CPU_Type = "ARM600"
+        MOV     r0, #0                  ; initialise abort list
+        STR     r0, [r0, #AbortIndirection]
+ ]
+
+; Now the SWI despatch + low part of SWI table
+        ADRL    R3, DirtyBranch
+        LDR     R0, =SWIDespatch
+SVCTabCopy                              ; so copy the table
+        LDR     R2, [R1], #4
+        STR     R2, [R0], #4
+        TEQ     R1, R3
+        BNE     SVCTabCopy
+
+; pad to 1K table here, rather than use ROM space
+        ADRL    R2, NoSuchSWI
+        LDR     R4, =1024+SvcTable      ; end of table
+PadSVCTable
+        CMP     R0, R4
+        STRNE   R2, [R0], #4
+        BNE     PadSVCTable
+
+; now the dirty branch
+        LDR     R1, [R1]
+        STR     R1, [R0]
+
+; now the time/date conversions
+
+        LDR     R0, =SvcTable
+        ADRL    R1, ConvertStandardDateAndTime
+        STR     R1, [R0, #OS_ConvertStandardDateAndTime*4]
+        ADD     R1, R1, #ConvertDateAndTime - ConvertStandardDateAndTime ; SKS
+        STR     R1, [R0, #OS_ConvertDateAndTime*4]
+
+; other conversion SWIs, all go through same entry point
+
+        ADRL    R1, despatchConvert
+        MOV     R2, #OS_ConvertHex1
+conversionSWIfill
+        STR     R1, [R0, R2, LSL #2]
+        ADD     R2, R2, #1
+        CMP     R2, #OS_ConvertFileSize+1
+        BNE     conversionSWIfill
+
+
+; Initialise CAO ptr to none.
+
+        MOV     R0, #0
+        MOV     R1, #32*1024*1024       ; nothing will be here!!
+        STR     R1, [R0, #Curr_Active_Object]
+
+
+KeyWait *       200000                  ; 1/5 sec wait (in microseconds)
+
+    [ KeyWait <> 0
+; Check for keyboard there every 1/5 sec. but give up after 2 secs.
+        MOV     r2, #IOC
+        MOV     r3, #10                 ; Check for keyboard 10 times (2 secs max).
+        MOV     r4, #InitKbdWs
+kbdwait
+        LDRB    r5, [r4, #KB_There_Flag]
+        LDR     r0, =KeyWait*2          ; Wait 1/5 second (give keys down a chance to come in).
+        BL      DoMicroDelay
+        TEQ     r5, #0                  ; If keyboard was there 1/5 second ago then
+        BNE     kbdthere                ;   continue reset
+        SUBS    r3, r3, #1              ; else wait a maximum of 2 seconds.
+        BNE     kbdwait
+kbdthere
+    ]
+
+
+; IF power-on bit set in IOC AND R/T/Del/Copy pressed THEN reset CMOS RAM
+; note that memory cleared if POR, so key info has had plenty of time!
+        MOV     R0, #IOC
+        LDRB    R1, [R0, #IOCIRQSTAA]
+        ANDS    R1, R1, #por_bit
+        BEQ     no_cmos_reset
+
+ [ CheckProtectionLink
+        LDR     r0, =IOMD_MonitorType
+
+; on Issue A's the protection bit is only weakly pulled up,
+; so force it high, then read it back
+
+        LDRB    r1, [r0]
+        ORR     r1, r1, #IOMD_ProtectionLinkBit
+        STRB    r1, [r0]
+
+        LDRB    r1, [r0]
+        TST     r1, #IOMD_ProtectionLinkBit
+        BEQ     no_cmos_reset           ; if zero then CMOS is protected
+ ]
+
+        MOV     R0, #InitKbdWs
+        LDR     R7, [R0, #R_Down_Flag]
+        CMP     R7, #0
+        BEQ     no_cmos_reset           ; power on bit checked again there
+
+        ADD     sp, sp, #4              ; junk CannotReset flag from stack
+
+; CMOS reset detectified.
+; **************************************************************************
+; Note: if this CMOS reset code ever needs changing again, it's probably
+; better to rewrite it. The Compact apparently has a table of default
+; CMOS values; R-p.o. just writes the table, DEL-p.o. zeroes all, then
+; writes the table. With skipping of the time CMOS, and post-prodding of
+; the sync, that would probably be a better algorithm.
+; **************************************************************************
+
+     SetBorder  R0, R1, 15, 0, 0        ; flash the border as warning!
+
+        MOVS    R3, R7, LSR #16         ; full reset or just system?
+        MOVNE   R3, #-1                 ; Del or Copy does all RAM
+        MOVEQ   R3, #UserCMOS           ; R or T just system
+    [ ChecksumCMOS
+        BL      ValChecksum             ; unless the CMOS ram's corrupt ..
+        MOVNE   R3, #-1                 ; .. then blast it anyway.
+        MOVNE   R0, #0                  ; even the econet station number
+        MOVEQ   R0, #1
+    |
+        MOV     R0, #1                  ; leave the econet station number
+    ]
+        MOV     R1, #0                  ; zero it first
+cmrlp   BL      Write                   ; CMOS(R0) := R1
+        ADD     R0, R0, #1
+        CMP     R0, R3
+        MOVEQ   R0, #&80                ; skip user cmos
+        CMP     r0, #YearCMOS
+        ADDEQ   r0, r0, #2
+        CMP     r3, #UserCMOS           ; system only?
+        BNE     skipskipforTC
+        CMP     r0, #NewADFSCMOS
+        CMPNE   r0, #CountryCMOS        ; skip these if so
+        ADDEQ   r0, r0, #1
+skipskipforTC
+        CMP     R0, #CMOSLimit
+        BNE     cmrlp
+    [ ChecksumCMOS
+        BL      MakeChecksum            ; create a valid checksum
+    ]
+; now put nice values in where necessary
+; first full reset defaults
+        CMP     r3, #-1
+        BNE     not_full_reset
+        MOV     r0, #CountryCMOS
+        MOV     r1, #1                  ; country UK
+        BL      Write
+        MOV     r0, #NewADFSCMOS
+        MOV     r1, #&41                ; floppies=1, ST506=0, IDE=1 (changed 01-Sep-93)
+        BL      Write
+not_full_reset
+
+; IF R or Delete pressed THEN set sync 0 ELSE set sync Auto
+        MOV     R0, #InitKbdWs
+        LDRB    R1, [R0, #R_Down_Flag]
+        CMP     R1, #0
+        LDREQB  R1, [R0, #Del_Down_Flag]
+        CMPEQ   R1, #0
+        MOV     R0, #VduCMOS
+        MOVNE   R1, #MonitorTypeAuto :OR: Sync_Auto     ; if R or Del
+        MOVEQ   R1, #MonitorTypeAuto :OR: Sync_Separate ; if T or Copy
+        BL      Write
+
+ [ MorrisSupport
+        MOV     R8, #IOMD_Base
+        LDRB    R0, [R8, #IOMD_ID0]
+        CMP     R0, #&98
+        LDRB    R0, [R8, #IOMD_ID1]
+        CMPEQ   r0, #&5B
+        BNE     dont_program_mousetype
+;
+; Morris based machines use PS2 mice/tracker balls
+;
+        MOV     R0, #MouseCMOS
+        MOV     R1, #PointerDevice_PS2Mouse     ;type 3
+        BL      Write
+
+  [ Select16BitSound
+; set print and sound CMOS (16bit sound)
+        B       Config16BitSound
+  ]
+dont_program_mousetype
+ ]
+
+ [ Select16BitSound
+        LDR     r0, =IOMD_MonitorType
+
+; on Issue A's the protection bit is only weakly pulled up,
+; so force it high, then read it back
+
+        LDR     r1, [r0]
+        ORR     r1, r1, #IOMD_SoundsystemLinkBit
+        STR     r1, [r0]
+
+        LDR     r1, [r0]
+        TST     r1, #IOMD_SoundsystemLinkBit
+        BEQ     Config16BitSound                ; if zero, must be Rimmer, so assume 16bit sound hardware present
+
+; set print and sound CMOS (8bit sound)
+        MOV     R0, #TutuCMOS
+        MOV     R1, #2_0100  ; tbs chars valid, ctrlchars '|x'
+        BL      Write
+        B       ConfigSoundDone
+
+Config16BitSound
+; set print and sound CMOS (16bit sound)
+        MOV     R0, #TutuCMOS
+        MOV     R1, #2_10100100  ; tbs chars valid, ctrlchars '|x'
+        BL      Write
+
+ConfigSoundDone
+ ]
+
+        ADR     R8, DefaultCMOSTable
+50
+        LDRB    R0, [R8], #1
+        CMP     R0, #&FF
+        BEQ     hard_reset              ; power on bit musta bin set
+        LDRB    R1, [R8], #1
+        BL      Write
+        B       %BT50
+
+        LTORG
+
+DefaultCMOSTable ; list of non-zero options wanted :
+; byte pairs of offset, value
+; terminated by offset &FF
+        =       KeyDelCMOS,     32
+        =       FileLangCMOS,   8
+        =       FontCMOS,       16      ; TMD 15-Dec-93: Changed to 64K from 32K - fixes MED-01774
+        =       PigCMOS,        10
+        =       KeyRepCMOS,     8
+        =       RMASizeCMOS,    0
+        =       SpriteSizeCMOS, 0
+        =       MODETVCMOS,     &10     ; TV 0,1
+        =       NetFSIDCMOS,    254
+        =       NetPSIDCMOS,    235
+        =       PSITCMOS,      (3:SHL:2) :OR: (1:SHL:5)
+                              ; Baud 3
+                              ;                print 1
+
+        =       DBTBCMOS,      (1:SHL:4) :OR: (4:SHL:5)
+                              ; Boot (changed from NoBoot 01-Sept-93)
+                              ;                Data 4
+
+        =       StartCMOS,     (4:SHL:0) :OR: (2:SHL:3) :OR: (1:SHL:6)
+                              ; Drive 4 (changed from Drive 0 01-Sept-93)
+                              ;                NOCAPS (changed from CAPS 02-May-91)
+                              ;                               NODIR
+
+     [ NewClockChip                     ; only on A1's!
+        =       NewADFSCMOS+1,  &FF     ; step 3 for each drive
+     ]
+
+        =       NewADFSCMOS+2,  1       ; ADFSBuffers 1
+        =       SoundCMOS,      &F0     ; speaker on, volume 7, channel 1
+
+        =       LanguageCMOS,   ConfiguredLang
+        =       YearCMOS,       95      ; changed from 93 to 95 on 12-Jan-95 to fix MED-04318
+        =       YearCMOS+1,     19
+ [ :LNOT: Select16BitSound
+        =       TutuCMOS,       2_0100  ; tbs chars valid, ctrlchars '|x'
+ ]
+        =       NetFilerCMOS,  (0:SHL:0) :OR: (1:SHL:1) :OR: (0:SHL:2)
+                              ; FS list order by name
+                              ;                Use $.Arthurlib
+                              ;                               Large icons
+
+     ;  =       Mode2CMOS,      WimpModeAutoBit :OR: CMOSResetBit ;AKA SystemSpeedCMOS - removed by RManby and SCormie 8/3/95
+        =       DesktopCMOS,    2_01000000      ; verbose ON
+        =       WimpFlagsCMOS,  2_01101111      ; instant effects, drags off screen
+        =       ProtectionCMOS, 2_01110110      ; allow only peek and user RPC
+        =       MouseStepCMOS,  2
+        =       FileSwitchCMOS,(1:SHL:0) :OR: (1:SHL:1) :OR: (0:SHL:2) :OR: (0:SHL:3) :OR: (0:SHL:6)
+                              ; truncate names
+                              ;                Use DragASprite (changed 01-Sept-93)
+                              ;                               Interactive file copying
+                              ;                                              Wimp dither colours off
+                              ;                                                             last shutdown ordinary
+
+        =       DesktopFeaturesCMOS,(1:SHL:0) :OR: (8:SHL:1) :OR: (0:SHL:7)
+                              ;      3D look
+                              ;                     Homerton.Medium
+                              ;                                    tiled window background
+
+        =       SystemSpeedCMOS,(1:SHL:0):OR:(0:SHL:1):OR:(1:SHL:2):OR:(0:SHL:3):OR:(1:SHL:4):OR:(0:SHL:5):OR:(1:SHL:6):OR:(0:SHL:7)
+                              ;  AUN ROMBoot Enabled
+                              ;               AUN auto-station numbering off
+                              ;                            Delete-etc reset
+                              ;                                         power saving off
+                              ;                                                      WimpMode auto
+                              ;                                                                  Cache on
+                              ;                                                                               Broadcast loader disabled
+                              ;                                                                                            broadcast loader colours off
+
+        =       FontMax2CMOS,   &2C     ; 32 point
+        =       FontMax3CMOS,   &38     ; 32 point
+        =       AlarmAndTimeCMOS,2_00010000 ; !Alarm autosave on
+        =       FSLockCMOS+5,   &EA     ; Checksum for no password
+        =       CDROMFSCMOS,    &60     ; drives = 0, buffer size = 32K
+        =       &FF
+        ALIGN
+
+
+no_cmos_reset                           ; R1 has por_bit set if power on
+        Push    "r1"
+        MOV     r0, #SystemSpeedCMOS
+        BL      Read
+        BIC     r1, r0, #CMOSResetBit   ; clear bit indicating CMOS reset
+        MOV     r0, #SystemSpeedCMOS
+        BL      Write
+        Pull    "r1"
+
+        Pull    r0                      ; always pull CannotReset flag
+ [ SoftResets
+        TST     r1, #por_bit
+        BNE     hard_reset              ; it was a power-on, so it's a hard reset
+        CMP     r0, #0
+ [ DebugForcedReset
+        MOVNE   r2, #Reset_CannotResetFlag
+ ]
+        BNE     hard_reset_forced
+
+; IF control pressed OR memory implausible (Check SysHpd, CAM map sensible) THEN hard reset
+
+        LDR     R0, =SysHeapStart
+        LDR     R8, [R0, #:INDEX: hpdmagic]
+        LDR     R2, =magic_heap_descriptor
+        CMP     R8, R2                  ; check sysheap initialised
+ [ DebugForcedReset
+        MOVNE   r2, #Reset_SysHeapCorrupt
+ ]
+        BNE     hard_reset_forced
+
+; also check CAM map sensible
+
+ [ {TRUE}                               ; new code which allows for MEMC2's small pages
+        MOV     R5, #0
+        LDR     R4, [R5, #Page_Size]    ; R4 = page size
+        ADRL    R3, PageShifts-1
+        LDRB    R4, [R3, R4, LSR #12]   ; R4 = log2(pagesize)
+        LDR     R3, [R5, #RAMLIMIT]     ; R3 = total RAM size
+        MOV     R2, R3, LSR R4          ; number of pages=total memory / pagesize
+        CMP     R2, #256                ; but if fewer than 128 (eg 64 on A305) (NB if <256 then <=128)
+        MOVCC   R2, #128                ; then use 128 (all MEMC1's pages need initialising,
+                                        ; even if half of them are not in use)
+        SUB     R2, R2, #1
+ [ MEMM_Type = "ARM600"
+        LDR     R3, =CamEntriesForVicky
+ |
+        LDRLS   R3, =CamEntries         ; if <= 256 pages, then should be using
+                                        ;  low-down cam soft copy
+        LDRHI   R3, =CamEntriesForBigMachines ; else should be using hi up one
+ ]
+
+        LDR     R4, [R5, #MaxCamEntry]  ; get highest CAM entry
+        LDR     R5, [R5, #CamEntriesPointer] ; and pointer to CAM soft copy
+ |
+        MOV     R5, #0
+        LDR     R3, [R5, #RAMLIMIT]     ; read amount of RAM
+        LDR     R4, [R5, #MaxCamEntry]  ; and highest CAM entry
+        LDR     R5, [R5, #CamEntriesPointer] ; and pointer to CAM soft copy
+
+        CMP     R3, #8*1024*1024        ; if >= 8MBytes then there are
+        MOVCS   R2, R3, LSR #15         ; RAM/32k pages
+        MOVCC   R2, #128                ; else there are 128 pages
+        SUB     R2, R2, #1              ; what highest cam index should be
+
+        LDRLS   R3, =CamEntries         ; if <= 8MBytes, should be using
+                                        ; low-down cam soft copy
+        LDRHI   R3, =CamEntriesForBigMachines ; else should be using hi up one
+ ]
+
+        CMP     R5, R3                  ; if not the same
+ [ DebugForcedReset
+        MOVNE   r2, #Reset_WrongCamMapAddress
+        BNE     hard_reset_forced
+ ]
+        CMPEQ   R4, R2                  ; or number of pages not the same
+ [ DebugForcedReset
+        MOVNE   r2, #Reset_WrongNumberOfPages
+ ]
+        BNE     hard_reset_forced       ; then do a hard reset
+
+; now check all cam contains sensible values
+
+        MOV     R4, #0
+        LDR     R4, [R4, #Page_Size]
+        SUB     R4, R4, #1
+ [ CPU_Type = "ARM600"
+        ORR     R4, R4, #&F0000000      ; can have addresses above 64M
+ |
+        ORR     R4, R4, #&FC000000
+ ]
+CamCheck
+        LDR     R3, [R5, R2, LSL #2]
+        BIC     r3, r3, #&F0000000      ; remove PPL
+        TST     R3, R4
+ [ DebugForcedReset
+        MOVNE   r2, #Reset_CamMapCorrupt
+ ]
+        BNE     hard_reset_forced       ; wally entry: not pagesize multiple, or >= 32M
+        SUBS    R2, R2, #1
+        BPL     CamCheck
+
+; leave CTRL test till last, so the keyboard's had as much time to
+; wiggle the wet string as we can give it
+        MOV     R0, #InitKbdWs
+        LDRB    R1, [R0, #CTRL_Down_Flag]
+        CMP     R1, #0
+        BNE     hard_reset
+
+soft_reset
+; clear out 4K of scratchspace, to use as a reverse CAM soft copy;
+; set bytes to indicate page mapped to that address. Can then recalculate
+; end of memory.
+
+; This code doesn't currently work on ARM600 versions -
+; 4K of workspace isn't enough to do this with a 4K page size
+; We'd probably want to do it differently anyway, using the L2PT
+; But since we're removing soft resets it's not worth the effort
+
+        ASSERT  MEMM_Type <> "ARM600"
+
+        MOV     R5, #ScratchSpace
+        MOV     R1, #4*1024
+        MOV     R2, #0
+clrscratch
+        SUBS    R1, R1, #4
+        STRPL   R2, [R5, R1]
+        BPL     clrscratch
+
+        LDR     R2, [R2, #Page_Size]
+        ADRL    R8, PageShifts-1
+        LDRB    R8, [R8, R2, LSR #12]
+
+        MOV     r7, #0
+        LDR     r2, [r7, #RAMLIMIT]
+        MOV     r2, r2, LSR r8          ; last valid page
+        SUB     r2, r2, #1
+
+        LDR     R7, [R7, #CamEntriesPointer]
+        LDR     R12, =DuffEntry
+
+restoreCAMloop
+        LDR    R3, [R7, R2, LSL #2]
+        MOV    r11, r3, LSR #28
+        BIC    r3, r3, #&F0000000
+
+        MOV    R0, R3, LSR R8              ; logram page number
+        LDRB   R4, [R5, R0]
+        CMP    r4, #0                      ; check for doubly mapped pages
+        BEQ    rclon
+
+        ORR    r3, r12, #&30000000         ; force to invalid place if so.
+        STR    r3, [R7, R2, LSL #2]
+        MOV    r3, r12
+        MOV    r11, #3                     ; protected
+        MOV    R0, R3, LSR R8
+        LDRB   R4, [R5, R0]
+rclon
+        CMP    r3, #16*1024*1024           ; in application space?
+        MOVLT  r11, #0                     ; noprot if so
+        STRLT  r3, [R7, R2, LSL #2]
+        ADD    R4, R4, #1
+        STRB   R4, [R5, R0]                ; sema for interesting pages
+        BL     BangCam
+        SUBS   R2, R2, #1
+        BPL    restoreCAMloop
+
+; now do post-scan to see if we need to do more CAM bashing to get pages back.
+; any entries that aren't validateable should be remapped.
+
+        MOV     R7, #0
+        MOV     R12, #ScratchSpace
+        LDR     R2, [R7, #Page_Size]
+findapplend
+        LDRB    R3, [R12], #1
+        CMP     R3, #0
+        ADDNE   R7, R7, R2
+        BNE     findapplend
+        MOV     R1, #0
+        STR     R7, [R1, #AplWorkSize]  ; verified value
+        LDR     R3, [R1, #RAMLIMIT]     ; calc last valid page:
+        MOV     R3, R3, LSR R8          ; RAMLIMIT >> R8
+        MOV     R11, #0                 ; no PPL
+        LDR     R4, [R11, #CamEntriesPointer]
+testforremap
+        SUBS    R3, R3, #1
+        BMI     finishedremap
+        LDR     R0, [R4, R3, LSL #2]
+        BIC     r0, r0, #&F0000000      ; remove PPL
+        ADD     R1, R0, R2
+        SWI     XOS_ValidateAddress
+        BCC     testforremap
+
+        Push    "R2-R4"
+        MOV     R0, R0, LSR R8          ; curr logram page number
+        LDRB    R4, [R5, R0]
+        SUB     R4, R4, #1
+        STRB    R4, [R5, R0]            ; dec sema
+        MOV     R2, R3                  ; entry no
+        MOV     R3, R7                  ; addr to set to
+        BL      BangCamUpdate
+        Pull    "R2-R4"
+holefilled
+        ADD     R7, R7, R2
+        LDRB    R0, [R12], #1           ; reinspect our reverse map
+        CMP     R0, #0
+        BNE     holefilled
+        MOV     R0, #0
+        STR     R7, [R0, #AplWorkSize]
+        B       testforremap
+
+finishedremap
+        MOV     R12, #NVECTORS-1
+        LDR     R11, =VecPtrTab
+freenextvec
+        LDR     R2, [R11, R12, LSL #2]
+loseveclink
+        LDR     R3, [R2, #TailPtr]
+        BL      FreeSysHeapNode
+        MOVVC   R2, R3
+        BVC     loseveclink
+        CMP     R3, #0                  ; were we at the end of the chain?
+ [ DebugForcedReset
+        MOVNE   r2, #Reset_VectorChainCorrupt
+ ]
+        BNE     hard_reset_forced
+        SUBS    R12, R12, #1
+        BPL     freenextvec
+; so that's the code for restore default vectors, basically
+        BL      InitVectors
+        MOV     R0, #0
+        LDR     R1, [R0, #AplWorkSize]
+        STR     R1, [R0, #MemLimit]
+
+        LDR     R3, =TickNodeChain
+        LDR     R2, [R3]
+
+loseticknodes
+        CMP     R2, #0
+        BEQ     ticknodesallgone
+
+        LDR     R3, [R2]
+        BL      FreeSysHeapNode
+ [ DebugForcedReset
+        MOVVS   r2, #Reset_TickNodesCorrupt
+ ]
+        BVS     hard_reset_forced
+        MOV     R2, R3
+        B       loseticknodes
+
+ticknodesallgone
+; and now it's time to free the IRQ structures
+
+        MOV     R12, #(NoInterrupt-1)*12+8      ; last device link offset
+        LDR     R11, =DefaultIRQ1V-DefaultIRQ1Vcode+Devices
+freenextdev
+        LDR     R2, [R11, R12]
+losedevlink
+        CMP     R2, #0
+        BEQ     stepdevice
+
+        LDR     R3, [R2, #8]
+        BL      FreeSysHeapNode
+ [ DebugForcedReset
+        MOVVS   r2, #Reset_DeviceVectorCorrupt
+ ]
+        BVS     hard_reset_forced
+        MOV     R2, R3
+        B       losedevlink
+stepdevice
+        SUBS    R12, R12, #12
+        BPL     freenextdev
+
+; now the PIRQ structures and CallBack_Vector
+        LDR     R11, =PIRQ_Chain
+        MOV     r4, #PodDesp_Link
+losepirqchain
+        LDR     R2, [R11]
+        CMP     r2, #0                  ; for CallBack_Vector
+        BEQ     doobry
+losepirqlink
+        LDR     R3, [R2, r4]
+        BL      FreeSysHeapNode
+        MOVVC   R2, R3
+        BVC     losepirqlink
+        CMP     R3, #0                  ; were we at the end of the chain?
+ [ DebugForcedReset
+        MOVNE   r2, #Reset_PoduleOrCallBackCorrupt
+ ]
+        BNE     hard_reset_forced
+        LDR     R2, =PIRQ_Chain
+        CMP     R11, R2
+        LDR     R2, =PFIQasIRQ_Chain
+        MOVEQ   R11, R2
+        CMPNE   r11, r2
+        LDREQ   r11, =CallBack_Vector
+    [ PodDesp_Link <> 0
+        MOVEQ   r4, #0
+    ]
+        BEQ     losepirqchain
+
+
+doobry  Pull    "R1"                    ; IOCControl restoration
+        MOV     R0, #0
+        STRB    R1, [R0, #IOCControlSoftCopy]
+        MOV     R0, #IOC                ; and bash the hardware
+        STRB    R1, [R0, #IOCControl]
+
+        MOV     R0, #SoftReset
+        B       ResetPart1Done
+
+ |
+
+; if soft resets are disabled, drop thru into hard reset code
+
+ ] ; end of code to do with soft resets
+
+hard_reset
+ [ DebugForcedReset
+        MOV     r2, #0                  ; indicate normal hard reset
+ ]
+hard_reset_forced
+ [ DebugForcedReset
+        STR     r2, [r0, -r0]           ; store to logical address zero
+ ]
+
+        Pull    "R2"                    ; discard old IOC state
+
+; fill in relevant CamMap entries, so can soft start.
+
+        MOV     R8, #0
+        LDR     R8, [R8, #Page_Size]
+        ADRL    R1, PageShifts-1
+        LDRB    R1, [R1, R8, LSR #12]
+
+ [ ExpandedCamMap
+        MOV     r2, #0
+  [ MEMM_Type = "ARM600"
+        LDR     r0, [r2, #VideoSize]    ; offset from start of physical pages to static part
+  |
+        LDR     r0, =480*1024
+  ]
+        MOV     r0, r0, LSR r1          ; r0 := cam entry number
+        MOV     r0, r0, LSL #3          ; r0 := offset into CAM map for start of static part
+
+        MOV     R7, #32*1024*8
+        MOV     R7, R7, LSR R1          ; r7 := cam entry offset for 32K
+
+ |
+        MOV     R0, #(512-32)*1024*4
+        MOV     R0, R0, LSR R1          ; r0 := cam entry number * 4
+
+        MOV     R7, #32*1024*4
+        MOV     R7, R7, LSR R1          ; r7 := cam entry offset for 32K
+
+        MOV     R2, #0
+ ]
+        LDR     R12, [R2, #RAMLIMIT]    ; R12 = total RAM size
+
+                                        ; new code which allows for MEMC2's small pages
+        MOV     R1, R12, LSR R1         ; R1 = number of pages
+        CMP     R1, #256                ; but if fewer than 128 (eg 64 on A305) (NB if <256 then <=128)
+        MOVCC   R1, #128                ; then use 128 (all MEMC1's pages need initialising,
+                                        ; even if half of them are not in use)
+
+        SUB     R3, R1, #1
+        STR     R3, [R2, #MaxCamEntry]
+
+ [ MEMM_Type = "ARM600"
+        LDR     R1, =CamEntriesForVicky
+ |
+        LDRLS   R1, =CamEntries                 ; if <=256 pages (was if <=8MBytes) then small CAM copy
+        LDRHI   R1, =CamEntriesForBigMachines   ; else use big CAM copy
+ ]
+        STR     R1, [R2, #CamEntriesPointer]
+
+
+ [ MEMM_Type = "ARM600"
+
+; On ARM600 we must zap all the soft CAM map before adding any entries,
+; since the old contents are used in BangCamUpdate
+
+        Push    "r0"
+        ADD     r2, r1, r3, LSL #3      ; r2 -> last entry to do
+        LDR     r0, =DuffEntry
+        MOV     lr, #AP_Duff            ; PPL = no access
+WallopDuffOnes
+        STMDA   r2!, {r0, lr}           ; store address, PPL
+        CMP     r2, r1
+        BCS     WallopDuffOnes
+        Pull    "r0"
+ ]
+        ADD     R0, R0, R1
+
+        MOV     R2, #CursorChunkAddress
+        LDR     r1, =AP_CursorChunk
+        BL      AddCamEntries
+
+        CMP     R12, #512*1024
+        SUBEQ   R0, R0, R7              ; previous entries
+        ADDNE   R0, R0, R7              ; next entries
+        MOV     R2, #0
+        MOV     r1, #AP_PageZero
+        BL      AddCamEntries
+
+        CMP     R12, #512*1024
+        SUBEQ   R0, R0, R7              ; previous entries
+        ADDNE   R0, R0, R7              ; next entries
+        MOV     R2, #SysHeapChunkAddress
+        MOV     r1, #AP_SysHeap :OR: PageFlags_Unavailable
+        BL      AddCamEntries
+
+ [ MEMM_Type = "ARM600"
+        ADD     R0, R0, R7              ; next entries (ignore 512K machines)
+        MOV     r7, #0
+        LDR     r7, [r7, #L2PTSize]
+        MOV     r7, r7, LSR #12-3       ; number of pages * 8
+        LDR     R2, =L2PT
+        LDR     r1, =AP_L2PT :OR: PageFlags_Unavailable
+        BL      AddCamEntries
+
+        ADD     r2, r0, #((L1PT-L2PT):SHR:(12-3)) + 4   ; point at PPL for 1st L1 page
+        LDR     r1, =AP_L1PT
+        STR     r1, [r2], #8            ; store 4 CAM entries for 4 x 4K = 16K of L1
+        STR     r1, [r2], #8            ; mark them as unavailable for removal
+        STR     r1, [r2], #8
+        STR     r1, [r2], #8
+
+        ADD     R0, R0, R7              ; add on enough pages for L2PT
+
+        MOV     r7, #0
+        LDR     r7, [r7, #SoftCamMapSize] ; number of bytes in soft cam map
+        ADD     r7, r7, #UndStackSize
+        MOV     r7, r7, LSR #12-3       ; number of bytes of cam map for this
+        LDR     R2, =UndStackSoftCamChunk
+        MOV     r1, #AP_UndStackSoftCam
+        BL      AddCamEntries
+ |
+  [ MEMM_Type = "MEMC2"
+; CCs from CMP still valid
+        SUBEQ   R0, R0, R7              ; previous entries
+        ADDNE   R0, R0, R7              ; next entries
+        LDR     R2, =CursorChunkAddress+64*1024 ; 01F10000
+        MOV     r1, #AP_CursorChunk
+        BL      AddCamEntries
+  ]
+ ]
+
+; let's boogie with the CMOS for a bit
+; read info and move as much memory as we can
+
+        BL      InitDynamicAreas
+
+ [ NewStyle_Screen
+        Push    "r0-r12"
+        MOV     r0, #ScreenSizeCMOS
+        BL      Read
+
+        MOV     r5, #0
+        LDR     r10, [r5, #Page_Size]   ; needed by MassageScreenSize
+        MUL     r0, r10, r0             ; convert to bytes
+        LDR     r5, [r5, #VideoSize]    ; maximum size
+        BL      MassageScreenSize
+
+        MOV     r1, #ChangeDyn_Screen   ; area number
+        MOV     r2, r0                  ; initial size
+        MOV     r3, #ScreenEndAdr       ; base address (start of 2nd copy)
+        LDR     r4, =AP_Screen          ; area flags
+        ADRL    r6, DynAreaHandler_Screen ; handler
+        VDWS    r7                      ; workspace pointer
+        MOV     r8, #0
+        STR     r8, [r7, #CursorFlags]  ; put zero in CursorFlags as an indication that VDU not yet inited
+        STR     r2, [r7, #TotalScreenSize]
+        ADRL    r8, AreaName_Screen     ; area name
+        BL      DynArea_Create
+        Pull    "r0-r12"
+ |
+        MOV     R2, #0                  ; CAM entry number to use (irrelevant for new code)
+        MOV     R0, #ScreenSizeCMOS     ; CMOS byte to read
+        MOV     R3, #ScreenEndAdr       ; where to put it (screen fudged)
+        LDR     R1, =VduDriverWorkSpace+TotalScreenSize
+                                        ; variable to save size set
+        MOV     r11, #AP_Screen         ; Unprotected
+        BL      ReadCMOSAndConfigure    ; Screen must be done first to get video RAM pages
+ ]
+
+ [ NewStyle_RMA
+        Push    "r0-r12"
+        MOV     r1, #ChangeDyn_RMA      ; Area number
+        MOV     r2, #4096               ; Initial size
+        MOV     r3, #RMAAddress         ; Base address
+        MOV     r4, #AP_RMA             ; Area flags
+        MOV     r5, #RMAMaxSize         ; Maximum size
+        ADRL    r6, DynAreaHandler_RMA  ; Pointer to handler
+        MOV     r7, r3                  ; Workspace ptr points at area itself
+        ADRL    r8, AreaName_RMA        ; Title string - node will have to be reallocated
+                                        ; after module init, to internationalise it
+        BL      DynArea_Create          ; ignore any error, we're stuffed if we get one!
+        Pull    "r0-r12"
+ |
+        MOV     R0, #1                  ; One page. Unprotected
+        MOV     R3, #RMAAddress
+        ADD     R1, R3, #:INDEX: hpdend ; see comment about SysHeap!
+        MOV     r11, #AP_RMA
+        BL      FudgeConfigureRMA
+ ]
+
+ [ NewStyle_SpriteArea
+        Push    "r0-r12"
+        MOV     r0, #0                  ; initialise SpriteSize to zero
+        STR     r0, [r0, #SpriteSize]   ; (fixes bug MED-00811)
+
+        MOV     r0, #SpriteSizeCMOS     ; find out how much spritesize configured
+        BL      GetConfiguredSize       ; in: r0 = CMOS address, out: r2 = size
+
+        MOV     r1, #ChangeDyn_SpriteArea ; Area number
+        MOV     r3, #-1                 ; Base address dynamic
+        MOV     r4, #AP_Sprites         ; Area flags
+        MOV     r5, #-1                 ; Maximum size
+        ADRL    r6, DynAreaHandler_Sprites ; Pointer to handler
+        MOV     r7, #-1                 ; Use base address as workspace ptr
+        ADRL    r8, AreaName_SpriteArea ; Title string - node will have to be reallocated
+                                        ; after module init, to internationalise it
+        BL      DynArea_Create          ; ignore any error, we're stuffed if we get one!
+        Pull    "r0-r12"
+ |
+        MOV     R0, #SpriteSizeCMOS
+        MOV     R3, #SpriteSpaceAddress
+        LDR     R1, =SpriteSize         ; for Richard. Unprotected
+        MOV     r11, #AP_Sprites
+        BL      ReadCMOSAndConfigure
+ ]
+
+ [ NewStyle_RAMDisc
+        Push    "r0-r12"
+        MOV     r0, #RAMDiscCMOS        ; find out how much RAM disc configured
+        BL      GetConfiguredSize       ; in: r0 = CMOS address, out: r2 = size
+
+        MOV     r1, #ChangeDyn_RamFS    ; Area number
+        MOV     r3, #-1                 ; Base address dynamic
+        MOV     r4, #AP_RAMDisc         ; Area flags
+        MOV     r5, #16*1024*1024       ; Limit maximum size to 16MB until JSR fixes FileCore
+        ADRL    r6, DynAreaHandler_RAMDisc ; Pointer to handler
+        MOV     r7, #-1                 ; Use base address as workspace ptr
+        ADRL    r8, AreaName_RAMDisc    ; Title string - node will have to be reallocated
+                                        ; after module init, to internationalise it
+        BL      DynArea_Create          ; ignore any error, we're stuffed if we get one!
+        Pull    "r0-r12"
+ |
+        MOV     R0, #RAMDiscCMOS
+        MOV     R3, #RAMDiscAddress
+        LDR     R1, =RAMDiscSize        ; for RAMFS
+        MOV     r11, #AP_RAMDisc        ; protected
+        BL      ReadCMOSAndConfigure
+ ]
+
+ [ NewStyle_FontArea
+        Push    "r0-r12"
+        MOV     r0, #FontCMOS           ; find out how much font cache configured
+        BL      GetConfiguredSize       ; in: r0 = CMOS address, out: r2 = size
+
+        MOV     r1, #ChangeDyn_FontArea ; Area number
+        MOV     r3, #-1                 ; Base address dynamic
+        MOV     r4, #AP_FontArea        ; Area flags
+        MOV     r5, #-1                 ; Maximum size
+        ADRL    r6, DynAreaHandler_FontArea ; Pointer to handler
+        MOV     r7, #-1                 ; Use base address as workspace ptr
+        ADRL    r8, AreaName_FontArea   ; Title string - node will have to be reallocated
+                                        ; after module init, to internationalise it
+        BL      DynArea_Create          ; ignore any error, we're stuffed if we get one!
+        Pull    "r0-r12"
+ |
+        MOV     R0, #FontCMOS
+        MOV     R3, #FontCacheAddress
+        LDR     R1, =FontCacheSize        ; for Fontmanager
+        MOV     r11, #AP_FontArea
+        BL      ReadCMOSAndConfigure      ; still protected
+ ]
+
+; get here with R2 = highest CAM entry used (in old model)
+
+ [ MEMM_Type = "ARM600"
+        LDR     R0, =(AplWorkMaxSize-32*1024):SHR:12    ; maximum number of pages in aplspace
+        MOV     R3, #32*1024            ; aplwork start
+        LDR     R1, =AplWorkSize        ; aplwork size
+        MOV     r11, #AP_AppSpace
+        BL      FudgeConfigureRMA       ; put as much as possible in aplspace
+
+  [ :LNOT: GetPagesFromFreePool         ; memory is already in free pool, so don't mess around
+        MOV     r0, #0
+        LDR     r0, [r0, #RAMLIMIT]
+        MOV     r0, r0, LSR #12         ; only limit on free pool is ram size
+        MOV     R3, #FreePoolAddress
+        LDR     R1, =FreePoolDANode + DANode_Size
+        MOV     r11, #AP_FreePool
+        BL      FudgeConfigureRMA       ; and the rest in FreePool
+  ]
+ |
+        MOV     R0, #4096               ; try and get maximum amount possible
+        MOV     R3, #32*1024            ; aplwork start
+        LDR     R1, =AplWorkSize        ; aplwork size
+        MOV     r11, #AP_AppSpace
+        BL      FudgeConfigureRMA       ; stuff rest on AplWork
+ ]
+
+        MOV     R0, #0
+        LDR     R1, [R0, #AplWorkSize]
+        ADD     R1, R1, #32*1024
+        STR     R1, [R0, #AplWorkSize]
+        STR     R1, [R0, #MemLimit]
+
+ [ MEMM_Type <> "ARM600"
+
+; Now add all the NaffEntries to CamEntries
+; R2 highest CAM entry used
+; NB This is not done on ARM600, as the whole soft CAM map has to be zapped before
+; we start, because BangCamUpdate uses the old contents
+
+        LDR     R3, [R0, #MaxCamEntry]  ; (R0 still zero from above)
+        CMP     R2, R3
+        BGT     CAMallset
+        LDR     R0, [R0, #CamEntriesPointer] ; (R0 still zero from above)
+ [ ExpandedCamMap
+        ADD     r3, r0, r3, LSL #3      ; r3 -> last pair of words to do
+        ADD     r0, r0, r2, LSL #3      ; r0 -> first pair of words to do
+        LDR     r1, =DuffEntry          ; address
+        MOV     r2, #AP_Duff            ; PPL
+WallopDuffOnes
+        STMIA   r0!, {r1, r2}           ; store address, PPL
+        CMP     r0, r3
+        BLS     WallopDuffOnes
+ |
+        LDR     R1, =DuffEntry :OR: (AP_Duff :SHL: 28)
+WallopDuffOnes
+        STR     R1, [R0, R2, LSL #2]
+        ADD     R2, R2, #1
+        CMP     R2, R3
+        BLE     WallopDuffOnes
+ ]
+CAMallset
+ ]
+
+ [ :LNOT: NewCDA
+; System heap only initialised here on older software models
+; On new ones it's already been initialised by hand in InitDynamicAreas,
+; so that we can create dynamic areas above here.
+
+        MOV     R0, #HeapReason_Init    ; initialise system heap
+        LDR     R1, =SysHeapStart
+        MOV     R3, #32*1024 - (SysHeapStart-SysHeapChunkAddress)
+        STR     R3, [R1, #:INDEX: hpdend] ; keep ValidateAddress happy
+        SWI     XOS_Heap                  ; tuff if it fails
+ ]
+        BL      InitVectors               ; ready for OsByte to read mode
+
+ [ :LNOT: GetPagesFromFreePool
+        MOV     R0, #0                  ; initialise module list to empty
+        STR     R0, [R0, #Module_List]
+ ]
+        LDR     R1, =ModuleSWI_HashTab
+        MOV     R2, #ModuleSHT_Entries-1
+clearmswis
+        STR     R0, [R1, R2, LSL #2]
+        SUBS    R2, R2, #1
+        BGE     clearmswis
+
+     [  International
+        MOV     R1, #-1                                 ; We don't have a message file yet !
+        STRB    R1, [R0, #ErrorSemaphore]               ; Don't translate errors.
+        STR     R0, [R0, #KernelMessagesBlock]          ; No message file open.
+     ]
+
+        MOV     R0, #IOC
+        LDRB    R1, [R0, #IOCIRQSTAA]
+        ANDS    R1, R1, #por_bit
+        STRNEB  R1, [R0, #IOCIRQCLRA]   ; clear POR if set
+        LDREQ   R0, =OsbyteVars + :INDEX: LastBREAK
+        LDREQB  R0, [R0]
+        TSTEQ   R0, #&80                ; tbs set if memory cleared
+        MOVNE   R0, #PowerOnReset
+        MOVEQ   R0, #ControlReset
+
+
+ResetPart1Done                          ; R0 is reset type
+        TEQP    PC, #SVC_mode + I_bit   ; interrupts off since kbd bash done
+        LDR     R1, =OsbyteVars + :INDEX: LastBREAK
+        STRB    R0, [R1]
+
+        MOV     R1, #InitKbdWs
+        LDRB    R0, [R1, #KB_There_Flag]
+        LDRB    R1, [R1, #SHIFT_Down_Flag]
+        Push    "R0, R1"                ; save until after MOSInit
+
+; copy IRQ handler: not done with rest of copying
+; because soft break needs the info to free any claimed blocks.
+
+        LDR     R0, =DefaultIRQ1V
+        ADRL    R1, DefaultIRQ1Vcode
+        ADRL    R2, DefaultIRQ1Vcode_end
+CopyDefaultIRQ1V
+        LDR     R3, [R1], #4
+        STR     R3, [R0], #4
+        CMP     R1, R2
+        BNE     CopyDefaultIRQ1V
+
+        LDR     R0, =PIRQ_Chain
+        ADRL    R1, Default_PIRQHandler_Node
+        STR     R1, [R0]
+        STR     R1, [R0, #PFIQasIRQ_Chain-PIRQ_Chain]
+ ASSERT Default_PFIQasIRQHandler_Node = Default_PIRQHandler_Node
+
+        MOV     R0, #0                  ; put in IRQ handler, word at 0
+        STRB    r0, [r0, #FIQclaim_interlock]
+        STRB    r0, [r0, #CallBack_Flag]
+        STR     r0, [r0, #CallBack_Vector]
+
+ [ CPU_Type = "ARM600"
+
+; we're poking locations 0 and &18 here, so we'd best go back to SVC32
+
+        mrs     AL, r2, CPSR_all
+        BIC     r3, r2, #&1F
+        ORR     r3, r3, #SVC32_mode
+        msr     AL, CPSR_all, r3
+ ]
+
+ [ DebugForcedReset
+        LDR     R1, [R0]
+        TEQ     R1, #0                                  ; if normal hard reset
+        LDREQ   R1, BranchThroughZeroInstruction2       ; then get branchthruzero code
+ |
+        LDR     R1, BranchThroughZeroInstruction2
+ ]
+        STR     R1, [R0]                                ; put branch through 0 code at 0
+
+        LDR     R1, RealIRQHandler
+        STR     R1, [R0, #&18]
+
+ [ CPU_Type = "ARM600"
+
+; now back to SVC26
+
+        msr     AL, CPSR_all, r2
+ ]
+        MOV     R1, #&100
+        STR     R1, [R0, #RCLimit]
+        STR     R0, [R0, #ReturnCode]
+        STR     R0, [R0, #TickNodeChain]
+
+;now put in error handler and escape handler
+        BL      DEFHAN
+        BL      DEFHN2
+        MOV     R0, #ExceptionDumpArea
+        LDR     R1, =DUMPER
+        SWI     XOS_ChangeEnvironment
+
+        VDWS    WsPtr                   ; main MOS initialisation
+        BL      VduInit
+        BL      ExecuteInit
+        BL      KeyInit
+        BL      MouseInit
+        BL      OscliInit               ; before initialising modules
+
+        TEQP    pc, #SVC_mode           ; enable IRQs
+        NOP
+
+        BL      InitialiseMode          ; select correct screen mode, in case any
+                                        ; module prints anything in initialisation
+
+        MOV     R0, #&FD                ; read last reset type
+        MOV     R1, #0
+        MOV     R2, #&FF
+        SWI     XOS_Byte
+        CMP     R1, #SoftReset          ; soft reset?
+        BEQ     SkipHardResetPart2
+
+; HardResetPart2
+        BL      InitVariables
+        BL      ModuleInit              ; initialise modules
+                                        ; scan podules, copy modules.
+
+        MOV     R0, #0                  ; shrink sysheap as far as will go.
+        SUB     R1, R0, #4*1024*1024
+        SWI     XOS_ChangeDynamicArea
+        MOV     R0, #ReadCMOS
+        MOV     R1, #SysHeapCMOS
+        SWI     XOS_Byte
+        AND     R2, R2, #2_111111       ; mask to same size as status
+        MOV     R0, #0
+        LDR     R0, [R0, #Page_Size]
+        MULTIPLY R3, R0, R2             ; size spare wanted
+        BL      ClaimSysHeapNode
+        MOV     R0, #HeapReason_Free
+        SWI     XOS_Heap
+
+        MOV     R0, #ReadCMOS
+        MOV     R1, #FileLangCMOS
+        SWI     XOS_Byte
+        MOV     R1, R2
+        MOV     R0, #FSControl_SelectFS ; set configured filing system
+        SWI     XOS_FSControl
+
+        MOV     r1, #Service_PostInit   ; issue post-initialisation service
+        BL      Issue_Service
+
+; New code added here by TMD 01-Apr-92
+; Go into user mode, issue a dummy SWI, then go back into SVC mode
+
+        TEQP    PC, #0                  ; enter USR mode (IRQs, FIQs enabled)
+        NOP                             ; wait for it to take effect
+        SWI     XOS_WriteI+0            ; I hope it doesn't generate an error
+                                        ; otherwise the callback will get deferred!
+        SWI     XOS_EnterOS             ; switch back to SVC mode (IRQs, FIQs enabled)
+
+; end of added code
+
+      [ International                   ; Open the kernel messages file.
+        ADR     r0, KernelMessagesBlock+4
+        ADR     r1, MessageFileName
+        MOV     r2, #0                  ; Use file directly.
+        SWI     XMessageTrans_OpenFile
+        MOVVC   r0, #-1
+        STRVC   r0, [r0, #KernelMessagesBlock+1]  ; Message file is now open.
+      ]
+
+SkipHardResetPart2                      ; code executed on all types of reset
+      [ International
+        MOV     r0, #0
+        LDR     r1, [r0, #KernelMessagesBlock] ; if we've managed to open message file
+        TEQ     r1, #0
+        STRNEB  r0, [r0, #ErrorSemaphore] ; then allow errors to be translated
+      ]
+
+        BL      InitialiseMode
+        SWI     XOS_WriteS
+        =       10, "$SystemName ", 0   ; now RISC OS (no +) again
+        ALIGN
+
+        MOV     R0, #0
+        LDR     R0, [R0, #RAMLIMIT]
+      [ {TRUE}                          ; Give startup message in megabytes
+        MOV     R0, R0, LSR #20         ; /(1024*1024)
+        LDR     R1, =GeneralMOSBuffer
+        MOV     R2, #?GeneralMOSBuffer
+        SWI     XOS_ConvertInteger4
+        SWI     XOS_Write0
+        SWI     XOS_WriteS
+        =       "MB", 10,13, 10, 0      ; title complete
+        ALIGN
+      |
+        MOV     R0, R0, LSR #10         ; /1024
+        LDR     R1, =GeneralMOSBuffer
+        MOV     R2, #?GeneralMOSBuffer
+        SWI     XOS_ConvertInteger4
+        SWI     XOS_Write0
+        SWI     XOS_WriteS
+        =       "K", 10,13, 10, 0       ; title complete
+        ALIGN
+      ]
+        MOV     r0, #0                  ; Set DomainId to 0 every reset
+        STR     r0, [r0, #DomainId]     ; before calling anyone
+
+; issue reset service call
+
+        MOV     R1, #Service_Reset
+        SWI     XOS_ServiceCall
+
+; now set up the default FIQ owner
+
+        MOV     R1, #Service_ClaimFIQ
+        SWI     XOS_ServiceCall
+
+        MOV     R1, #Service_ReleaseFIQ
+        SWI     XOS_ServiceCall
+
+        MOV     R0, #FSControl_Shut     ; Open files get closed at reset
+        SWI     XOS_FSControl
+
+        BL      PostInit
+
+        MOV     r0, #&FD                ; read last reset type (again!)
+        MOV     r1, #0
+        MOV     r2, #&FF
+        SWI     XOS_Byte
+        CMP     r1, #SoftReset          ; a softie?
+        SWINE   XOS_WriteI+7            ; go beep! Yaay!
+
+        CMP     r1, #PowerOnReset
+        BNE     %FT75
+
+ [ CheckProtectionLink
+        LDR     r1, =IOMD_MonitorType   ; check link bit again
+        LDRB    r1, [r1]                ; no need to preload bus, since should
+        TST     r1, #IOMD_ProtectionLinkBit ; be still there from earlier
+        BEQ     %FT75                   ; zero => protected
+ ]
+
+
+; if any monitor key pressed, reconfigure, otherwise hang around for a bit
+; till keys get a chance to come in again after being reset for the umpteenth
+; time by yet another keyboard handler! SKS 07-Jun-88
+
+        MOV     r3, #0
+        LDR     r3, [r3, #MetroGnome]
+        ADD     r3, r3, #10             ; Hang about for a little while
+
+KeypadStar_key  * -92
+
+HorologicalDelayLoop1
+        MOV     r0, #&79                        ; scan keyboard
+        MOV     r1, #&FF                        ; starting at (&FF + 1) AND &FF
+60
+        ADD     r1, r1, #1
+        AND     r1, r1, #&FF
+        SWI     XOS_Byte
+        TEQ     r1, #&FF                        ; if no key down
+        BEQ     %FT70                           ; then check if we've run out of time
+
+        ADR     r2, MonitorKeypadTable
+62
+        LDRB    r14, [r2], #2                   ; search for key in table
+        TEQ     r14, #&FF
+        BEQ     %FT70
+        TEQ     r1, r14
+        BNE     %BT62
+        LDRB    r3, [r2, #-1]                   ; get corresponding CMOS bits
+        MOV     r0, #ReadCMOS
+        MOV     r1, #VduCMOS
+        SWI     XOS_Byte
+        BIC     r2, r2, #MonitorTypeBits
+        ORR     r2, r2, r3
+        MOV     r0, #WriteCMOS
+        SWI     XOS_Byte
+
+        TEQ     r3, #MonitorTypeAuto            ; if we're setting monitortype auto
+        BNE     %FT64
+        ADRL    r0, ModeCMOSTable +8            ; then configure mode auto
+        LDR     r2, [r0, #-8]                   ; (load auto value)
+        BL      WriteMultiField
+        ADRL    r0, SyncCMOSTable +8            ; and configure sync auto
+        LDR     r2, [r0, #-8]                   ; (load auto value)
+        BL      WriteMultiField
+
+64
+        BL      InitialiseMode
+      [ International
+        SWI     XOS_WriteI+10
+        BLVC    WriteS_Translated
+        =       "MonType:Monitor type reconfigured.",10,13,10,0
+        ALIGN
+      |
+        SWI     XOS_WriteS
+        =       10,"Monitor type reconfigured.",10,13,10,0
+        ALIGN
+      ]
+        B       %FT75
+
+BranchThroughZeroInstruction2
+ [ ProcessorVectors
+        LDR     PC, .+ProcVec_Branch0
+ |
+        B       .+(RESET1-0)
+ ]
+
+MonitorKeypadTable      ; internal key number, CMOS bits
+        =       106, MonitorType0
+        =       107, MonitorType1
+        =       124, MonitorType2
+        =       108, MonitorType3
+        =       122, MonitorType4
+        =       123, MonitorType5
+        =       26,  MonitorType6
+        =       27,  MonitorType7
+        =       42,  MonitorType8
+        =       43,  MonitorType9
+        =       76,  MonitorTypeAuto    ; keypad dot
+        =       &FF
+        ALIGN
+      [ International
+MessageFileName DCB     "Resources:$.Resources.Kernel.Messages",0
+        ALIGN
+      ]
+
+70
+        MOV     r14, #0
+        LDR     r14, [r14, #MetroGnome]
+        CMP     r14, r3
+        BLO     HorologicalDelayLoop1
+75
+
+
+; Deal with SHIFT pressed/SHIFT-BREAK configured:
+; do appropriate FSControl if wanted
+
+        Pull    "R0"                    ; first check kbd there
+
+        CMP     R0, #0
+        BEQ     AutoBootCosNoKbd
+
+        MOV     R0, #&FF
+        MOV     R1, #0
+        MOV     R2, #&FF                ; read shifty state
+        SWI     XOS_Byte
+        AND     R0, R1, #8              ; picka da bit
+        EOR     R0, R0, #8              ; invert sense
+        Pull    "R1"
+        CMP     R1, #0
+        MOVNE   R1, #8
+        EORS    R1, R1, R0
+        BEQ     %FT80
+
+Hortoculture_Kicking
+        MOV     R0, #FSControl_BootupFS
+        SWI     XOS_FSControl
+        BVC     %FT80
+
+        Push    "r3,r4"
+        ADD     r1, r0, #4              ; Set Boot$Error if it failed (Desktop will report it).
+        ADR     r0, str_booterror
+        MOV     r2, #1024               ; Big enough that terminator will be reached.
+        MOV     r3, #0
+        MOV     r4, #VarType_String
+        SWI     XOS_SetVarVal
+        SUBVS   r0, r1, #4              ; If setting Boot$Error failed then report original error as before.
+        BLVS    PrintError
+        SWIVS   XOS_NewLine
+        Pull    "r3,r4"
+80
+; if either * pressed, drop into * prompt, otherwise hang around for a bit
+; till keys get a chance to come in again after being reset for the umpteenth
+; time by yet another keyboard handler! SKS 01-Jun-88
+
+        MOV     r3, #0
+        LDR     r3, [r3, #MetroGnome]
+        ADD     r3, r3, #10             ; Hang about for a little while
+
+HorologicalDelayLoop2
+        MOV     r1, #KeypadStar_key :AND: &FF
+        BL      IsKeyPressedAtReset
+        BEQ     DoStartSuper            ; EQ -> start up supervisor
+
+        MOV     r0, #0
+        LDR     r0, [r0, #MetroGnome]
+        CMP     r0, r3
+        BLO     HorologicalDelayLoop2
+
+
+; Start configured language module if keypad-* wasn't pressed
+
+        MOV     R0, #ReadCMOS
+        MOV     R1, #LanguageCMOS
+        SWI     XOS_Byte
+
+        MOV     R0, #ModHandReason_GetNames
+        SUB     R1, R2, #1
+        MOV     R2, #0                  ; preferred incarnation
+        SWI     XOS_Module
+        ADRVSL  R3, UtilityMod
+        LDR     R2, [R3, #Module_Title]
+        CMP     R2, #0
+        ADDNE   R1, R3, R2
+DoStartSuper
+        ADREQL  R1, UtilModTitle        ; ALWAYS enter via SWI: sets CAO etc.
+        MOV     R0, #ModHandReason_Enter
+        ADRL    R2, crstring            ; no environment
+        SWI     XOS_Module
+        CMP     r0, r0                  ; set EQ if failed to enter config.lang
+        B       DoStartSuper            ; -> force Super entry
+
+
+str_booterror   DCB     "Boot$Error",0
+                ALIGN
+
+
+AutoBootCosNoKbd
+      [ International
+        SWI     XOS_WriteI+7
+        BLVC    WriteS_Translated
+        =       "NoKbd:No keyboard present - autobooting", 10,13,0
+        ALIGN
+      |
+        SWI     XOS_WriteS
+        =       7, "No keyboard present - autobooting", 10,13,0
+        ALIGN
+      ]
+        B       Hortoculture_Kicking
+
+
+RealIRQHandler
+ [ ProcessorVectors
+        LDR     PC, .-&18+ProcVec_IRQ
+ |
+        B     Initial_IRQ_Code+.-&18
+ ]
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r1 = INKEY -ve key code to look for
+
+; Out   EQ: key pressed
+;       NE: key not pressed
+
+IsKeyPressedAtReset ENTRY "r0-r2"
+
+        MOV     r0, #129
+        MOV     r2, #&FF
+        SWI     XOS_Byte
+        TEQ     r1, #&FF
+        TEQEQ   r2, #&FF
+        EXIT
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+    [ AddTubeBashers
+TubeDumpR0     ROUT
+        Push  "R1, R2, lr"
+        ADR   lr, HexTable
+        TubeChar r0, r1, "MOV r1, #"" """
+        MOV    R1, #7
+01      MOV    R0, R0, ROR #28
+        AND    R2, R0, #&F
+        TubeChar R0, R1, "LDRB R1, [lr, R2]"
+        SUBS   R1, R1, #1
+        BPL    %BT01
+        TubeChar r0, r1, "MOV r1, #"" """
+        Pull  "R1, R2, PC", ,^
+
+TubeNewl
+        TubeChar R0, R1, "MOV R1, #10"
+        TubeChar R0, R1, "MOV R1, #13"
+        MOVS   pc, lr
+
+HexTable = "0123456789ABCDEF"
+
+    ]
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        END
diff --git a/s/Oscli b/s/Oscli
new file mode 100644
index 0000000000000000000000000000000000000000..0159cbfcb74aa1001c75debaa9c57961a8572a6b
--- /dev/null
+++ b/s/Oscli
@@ -0,0 +1,1322 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL     => Oscli - main Oscli code and system commands.
+
+        GBLL    redirectinkey
+redirectinkey SETL True
+
+        MACRO
+$l      CheckUID $reg, $tmp
+$l      MOV      $tmp, #0
+        LDR      $tmp, [$tmp, #OscliCBbotUID]
+        CMP      $reg, $tmp
+        MEND
+; exits with HI if buffer OK.
+
+;******************************************************************************
+; redirection utility routines
+
+; In:   R10 points at filename, V clear
+;       R1 is type
+
+; Out:  R0, R1, R12 corrupted, redirection done, flags preserved (or V set)
+
+doredirect ROUT
+
+           Push   "R1, R2, lr"
+60         LDRB    R0, [R10], #1
+           CMP     R0, #" "
+           BEQ     %BT60
+           MOV     R2, R1
+           LDR     R1, =RedirectBuff
+61         CMP     R0, #" "             ; we know it's terminated by space
+           STRNEB  R0, [R1], #1
+           LDRNEB  R0, [R10], #1
+           BNE     %BT61
+           SUB     R10, R10, #1
+           MOV     R0, #13
+           STRB    R0, [R1]
+           LDR     R1, =RedirectBuff
+           MOV     R0, #0
+           CMP     R2, #&40
+           LDREQB  R1, [R0, #RedirectInHandle]
+           LDRNEB  R1, [R0, #RedirectOutHandle]
+           STREQB  R0, [R0, #RedirectInHandle]
+           STRNEB  R0, [R0, #RedirectOutHandle]
+           CMP     R1, #0
+           SWINE   XOS_Find             ; close any previous File
+           MOV     R12, R1              ; don't really care if handle was invalid
+           LDR     R1, =RedirectBuff
+           ORR     R0, R2, #open_mustopen + open_nodir
+           SWI     XOS_Find             ; Open the File
+           BVS     abort_redirect       ; bad name etc
+           CMP     R0, #0               ; worked?
+           BEQ     %FT63
+           CMP     R2, #&40
+           MOV     R1, #0
+           STREQB  R0, [R1, #RedirectInHandle]
+ [ redirectinkey
+ BEQ %FT00
+ |
+           MOVEQ   R0, #RdchV
+           ADREQL  R1, RedirectRdch
+ ]
+           STRNEB  R0, [R1, #RedirectOutHandle]
+           MOVNE   R0, #WrchV
+           ADRNEL  R1, RedirectWrch
+           CMP     R12, #0              ; Ensure only vectored the once
+           MOVEQ   R2, #0
+           SWIEQ   XOS_Claim
+           BVS     abort_redirect       ; Claim will leave "Sysheap full" msg.
+00
+           LDR     R2, [stack]
+           CMP     R2, #&C0
+           GRABS  "R1, R2, PC", NE
+
+; >> file, so move to EOF
+
+           MOV     R1, #0
+           LDRB    R1, [R1, #RedirectOutHandle]
+           MOV     R0, #2               ; read extent
+           SWI     XOS_Args
+           MOVVC   R0, #1               ; write ptr
+           SWIVC   XOS_Args
+           GRABS  "R1, R2, PC", VC
+
+           B       abort_redirect
+
+           MakeErrorBlock   RedirectFail
+
+63         ADR     R0, ErrorBlock_RedirectFail
+         [ International
+           BL      TranslateError
+         ]
+abort_redirect                          ; current error set
+           Pull   "R1, R2, lr"
+           ORRS    PC, lr, #V_bit
+
+
+ParseRDNSpec        ROUT
+; In : R11 points at string to test.
+; Out : EQ/NE for "is it a rdnspec?". V always clear.
+; If EQ : R11 points after filename
+;         R10 points at start of filename
+;         R1=&40 => redirect input,
+;           =&80 => redirect output,
+;           =&C0 => redirect & append output
+         MOV        R1, #&40
+01       LDRB       R10, [R11], #1
+         CMP        R10, #" "
+         BEQ        %BT01               ; skip leading spaces
+         CMP        R10, #"<"
+         BEQ        %FT04
+         CMP        R10, #">"
+         MOVNE      PC, lr
+         MOV        R1, #&80
+         LDRB       R10, [R11], #1
+         CMP        R10, #">"
+         MOVEQ      R1, #&C0
+04       LDREQB     R10, [R11], #1
+         CMP        R10, #" "
+         MOVNE      PC, lr
+         SUB        R10, R11, #1        ; filename start ptr
+         Push      "R0"
+02       LDRB       R0, [R11], #1
+         CMP        R0, #" "
+         BGT        %BT02
+         Pull      "R0"
+         MOV        PC, lr              ; it's EQ if had space at end.
+
+         MakeErrorBlock StackFull
+
+OscliStackFull
+         ADR        R0, ErrorBlock_StackFull
+       [ International
+         BL         TranslateError
+       ]
+         Pull      "lr"
+         ORRS       PC, lr, #V_bit
+
+;******************************************************************************
+; Main OSCLI code
+
+VecOsCli ROUT
+
+; first check for rheum on the stack.
+; Oscli will have pushed 5 registers when it calls module code : let's
+; guarantee 256 bytes (=64 registers) for the module code
+; so check half a K left, leaving 236 bytes for any interrupt processing.
+
+         CheckSpaceOnStack  512, OscliStackFull, R10
+
+         Push      "R0-R2"      ; lr on stack from caller
+
+; first skip * and space
+
+01       LDRB       R10, [R0], #1
+         CMP        R10, #" "
+         CMPNE      R10, #"*"
+         BEQ        %BT01
+         CMP        R10, #"%"
+         LDREQB     R10, [R0]           ; fixed 29-Mar-89; was LDREQ
+         CMP        R10, #13
+         CMPNE      R10, #10
+         CMPNE      R10, #0
+         CMPNE      R10, #"|"
+         GRABS     "R0-R2, PC", EQ   ; V clear return
+
+; now check for redirection.
+; Redirection setter ::= "{ " [ >= 1 Redirection spec] "}"
+; where
+; Redirection spec ::=  "> "filename" " | "< "filename" " | ">> "filename" "
+; Also check terminator in first 256 chars.
+         SUB        R11, R0, #1
+         MOV        R1, #0
+         ADD        R2, R0, #256
+02       LDRB       R10, [R11], #1
+         CMP        R11, R2
+         BEQ        OscliLineTooLong
+         CMP        R10, #13
+         CMPNE      R10, #10
+         CMPNE      R10, #0
+         BEQ        %FT58
+         CMP        R10, #""""
+         EOREQ      R1, R1, R10
+         CMP        R10, #"{"
+         CMPEQ      R1, #0
+         BNE        %BT02
+         Push      "R11"
+         LDRB       R10, [R11], #1
+         CMP        R10, #" "
+         BLEQ       ParseRDNSpec
+         Pull      "R11", NE
+         BNE        %BT02
+60       BL         ParseRDNSpec
+         BEQ        %BT60
+         CMP        R10, #"}"
+         Pull      "R11"
+         BNE        %BT02
+; R11 points 1 char after {
+         Push      "R5, R6"
+         BL         GetOscliBuffer    ; get a buffer
+         SUB        R0, R0, #1
+         MOV        R2, #0
+50       LDRB       R10, [R0], #1
+         CMP        R0, R11
+         STRNEB     R10, [R5, R2]
+         ADDNE      R2, R2, #1
+         BNE        %BT50
+
+61       BL         ParseRDNSpec
+         BLEQ       doredirect
+         BVS        RedirectionError  ; close any redirection set up
+         BEQ        %BT61
+
+53       LDRB       R10, [R11], #1
+         CMP        R10, #" "
+         BEQ        %BT53
+         SUB        R11, R11, #1
+
+52       LDRB       R10, [R11], #1
+         STRB       R10, [R5, R2]
+         ADD        R2, R2, #1
+         CMP        R2, #OscliBuffSize
+         BEQ        %FT51
+         CMP        R10, #13
+         CMPNE      R10, #10
+         CMPNE      R10, #0
+         BNE        %BT52
+         MOV        R2, R6           ; buffer UID
+         ADD        R0, R5, #1        ; point after 1st char.
+         Pull      "R5, R6"
+         B          %FT03
+
+51       MOV       R2, R6            ; longer than 256
+         ADR       R0, ErrorBlock_OscliLongLine
+       [ International
+         BL        TranslateError
+       ]
+RedirectionError
+         BL        ReleaseBuff
+         BL        OscliTidy        ; shut down the redirection just done.
+         Pull     "R5, R6"
+OscliFailure
+         STR       R0, [stack]
+         Pull     "R0-R2, lr"
+         ORRS      PC, lr, #V_bit
+
+OscliLineTooLong
+         ADR       R0, ErrorBlock_OscliLongLine
+       [ International
+         BL        TranslateError
+       ]
+         B         OscliFailure
+         MakeErrorBlock  OscliLongLine
+
+58       MOV       R2, #-1     ; naff buffer UID.
+; Redirection dealt with. R0 points after 1st ch command
+03
+         Push     "R2"        ; save buffer UID
+
+; now check for filing system name as prefix
+         Push     "R3"
+         MOV       R3, #0     ; j.i.c. fileswitch is dead!!
+         SUB       R1, R0, #1
+         MOV       R0, #FSControl_StarMinus
+         SWI       XOS_FSControl
+
+; here we have:
+; V set if -nafffsname encountered
+; VC: R2 = -1 if no fs name found
+;     R3 = 0 if no specials encountered
+
+         BVS       letmodprefatit
+         CMP       R3, #0
+         BEQ       letmodprefatit
+         Pull     "R3"
+         Push     "R2"        ; save "temp FS set" indicator
+         ADR       R0, ErrorBlock_NoOscliSpecials
+       [ International
+         BL        TranslateError
+       |
+         SETV
+       ]
+         B         OscliExit
+         MakeErrorBlock NoOscliSpecials
+
+letmodprefatit
+         Pull     "R3"
+         Push     "R2"        ; save "temp FS set" indicator
+         BL        CheckForModuleAsPrefix
+         BVS       OscliExit
+
+; special char checks
+pfssss   LDRB      R10, [R1], #1
+         CMP       R10, #" "
+         BEQ       pfssss
+         SUB       R0, R1, #1
+         CMP       R2, #-1
+         RSBLT     R11, R2, #0
+         Push     "R2",LT
+         BLT       OnlyOneModuleWanted
+
+         CMP       R10, #"/"
+         BEQ       %FT06
+
+; see if skip macro expansion
+         CMP       R10, #"%"
+         LDREQB    R10, [R0], #1
+         BEQ       %FT05
+
+         CMP       R10, #"."    ; fudge .
+         BEQ       %FT07
+
+; try macro expansion : if find it, expand into a buffer.
+; Also scan for parameters while expanding.
+; R0 ptr to command, R10 first char.
+; If success : Recursively call OSCLI for each line in expansion.
+
+      Push  "R0, R3-R6"
+    [ International
+      MOV    R3,#0
+      LDRB   R6,[R3,#ErrorSemaphore]            ; We are about to get lots of buffer overflow errors,
+      SUB    R6,R6,#1
+      STRB   R6,[R3,#ErrorSemaphore]
+    ]
+      MOV    R6, R0
+      MOV    R3, #:LEN: "Alias$"
+31    SUB    R3, R3, #:LEN: "Alias$"
+      MOV    R2, #-1        ; negative length means just look for it.
+      ADR    R0, AliasStr
+      SWI    XOS_ReadVarVal
+      CMP    R2, #0         ; V always set anyway
+    [ International
+      LDREQB R0,[R2,#ErrorSemaphore]
+      ADDEQ  R0,R0,#1
+      STREQB R0,[R2,#ErrorSemaphore]
+    ]
+      BEQ    %FT10
+
+      ADD   R3, R3, #:LEN: "Alias$"
+
+; match $R6 with $R3
+
+      MOV   R1, #0                         ; offset
+32    LDRB  R4, [R6, R1]
+      LDRB  R5, [R3, R1]
+      CMP   R4, #&80                       ; in table ?
+      ADRCCL R2, Up_ItAndTerm_Check_Table
+      LDRCCB R4, [R2, R4]
+      CMP   R4, #" "
+      CMPLE R5, #" "
+      BLE   %FT33
+      UpperCase R5, R2
+      CMP   R4, R5
+      ADDEQ R1, R1, #1
+      BEQ   %BT32
+      CMP   R1, #0
+      BEQ   %BT31                        ; failed
+      CMP   R5, #" "
+      BLE   %BT31
+      CMP   R4, #"."
+      BNE   %BT31
+      ADD   R1, R1, #1
+33
+; success : copy name, read value
+
+    [ International
+      MOV   R4,#0
+      LDRB  R0,[R4,#ErrorSemaphore]
+      ADD   R0,R0,#1
+      STRB  R0,[R4,#ErrorSemaphore]     ; We can go back to translating errors.
+    ]
+
+      SUB   R3, R3, #:LEN: "Alias$"
+99    LDR   R0, =AliasExpansionBuffer
+      ADD   R6, R6, R1                       ; save arglist ptr
+      MOV   R4, #0
+34    LDRB  R5, [R3], #1
+      STRB  R5, [R0, R4]
+      ADD   R4, R4, #1
+      CMP   R5, #0
+      BNE   %BT34
+      MOV   R1, R0               ; output buffer same as input!
+      MOV   R2, #256
+      MOV   R3, #0
+      MOV   R4, #VarType_Expanded
+      SWI   XOS_ReadVarVal
+      BVS   AliasOscliTooLong
+      MOV   R3, #13
+      STRB  R3, [R1, R2]
+
+      MOV    R3, R1
+      MOV    R0, R6              ; arglist
+      MOV    R4, R2              ; no of chars got.
+      BL     GetOscliBuffer      ; gives buffer ptr in R5, ID in R6
+      MOV    R1, R5
+      MOV    R2, #OscliBuffSize
+      SWI    XOS_SubstituteArgs
+      BVS    AliasOscliTooLong
+
+; Whew! Now ready to recursively call OSCLI with all lines in the buffer.
+      MOV    R0, R1
+      ADD    R2, R1, R2
+43    SWI    XOS_CLI
+      BVS    %FT46
+      CheckUID R6, R1    ; check buffer still valid.
+      BLS    FailInAlias
+44    LDRB   R1, [R0], #1
+      CMP    R1, #13
+      CMPNE  R1, #10
+      CMPNE  R1, #0
+      BNE    %BT44
+      CMP    R0, R2
+      BLO    %BT43
+      MOV    R2, R6
+      BL     ReleaseBuff ; release buffer, UID in R2
+      Pull  "R0, R3-R6"
+      CLRV
+      B      OscliExit
+
+      MakeErrorBlock  OscliTooHard
+
+FailInAlias
+      CheckUID R2, R1
+      BLGT   ReleaseBuff
+      ADR    R0, ErrorBlock_OscliTooHard
+    [ International
+      BL     TranslateError
+    ]
+46    STR    R0, [stack]
+      Pull  "R0, R3-R6"
+      SETV
+      B      OscliExit
+
+AliasOscliTooLong
+      MOV    R2, R6            ; buffer UID
+      BL     ReleaseBuff
+      Pull  "R0, R3-R6"
+      ADRL   R0, ErrorBlock_OscliLongLine
+    [ International
+      BL     TranslateError
+    |
+      SETV
+    ]
+      B      OscliExit
+
+      LTORG
+
+AliasStr = "Alias$*", 0
+AliasDot = "Alias$"
+dotstring = ".", 0
+      ALIGN
+
+10  ; Failed macro expansion.
+         Pull   "R0, R3-R6"
+
+; try for system command first.
+05       LDRB      R1, [R0]                     ; quick check for . tho
+         CMP       R1, #"."
+         BEQ       PercentDot
+
+         ADRL      R1, SysCommsModule
+         MOV       R2, #SCHCTab-SysCommsModule
+         TEQP      PC, #C_bit :OR: SVC_mode     ; carry set means sys module
+         BL        ModCommsLookUp
+         BCS       OscliExit
+
+; now try looking round the modules.
+         MOV       R11, #Module_List
+         Push     "R2"
+74       LDR       R2, [stack]
+         CMP       R2, #0
+         BMI       OneModule_Failed
+         LDR       R11, [R11, #Module_chain_Link]
+         CMP       R11, #0
+         BEQ       %FT75
+OnlyOneModuleWanted
+         LDR       R1, [R11, #Module_code_pointer]
+         LDR       R2, [R1, #Module_HC_Table]
+         CMP       R2, #0
+         BEQ       %BT74
+         LDR       R12, [R11, #Module_incarnation_list] ; preferred life
+         ADD       R12, R12, #Incarnation_Workspace
+         TEQP      PC, #SVC_mode              ; clear C
+         BL        ModCommsLookUp
+         BCC       %BT74
+         ADD       stack, stack, #4           ; discard R2
+         B         OscliExit
+
+75
+  ; not in a module : try for current filing system command
+         STR       R0, [stack]                 ; pull R2, push R0
+         MOV       R0, #FSControl_ReadModuleBase
+         SWI       XOS_FSControl
+         Pull     "R0"
+         CMP       R1, #0
+         BEQ       NoFSCommands                  ; no selected FS!
+         MOV       R12, R2                       ; module's workspace ptr
+         LDR       R2, [R1, #Module_HC_Table]
+         CMP       R2, #0
+         BEQ       SecondaryFSCTab
+         ORR       R2, R2, #&80000000            ; FS command needed flag
+         TEQP      PC, #SVC_mode                 ; clear C
+         BL        ModCommsLookUp
+         BCC       SecondaryFSCTab
+         B         OscliExit
+
+SecondaryFSCTab
+         Push     "R0"
+         MOV       R0, #FSControl_ReadSecondaryModuleBase
+         SWI       XOS_FSControl
+         Pull     "R0"
+         MOVVS     R1, #0
+         CMP       R1, #0
+         BEQ       NoFSCommands
+         MOV       R12, R2                       ; module's workspace ptr
+         LDR       R2, [R1, #Module_HC_Table]
+         CMP       R2, #0
+         BEQ       NoFSCommands
+         ORR       R2, R2, #&80000000            ; FS command needed flag
+         TEQP      PC, #SVC_mode                 ; clear C
+         BL        ModCommsLookUp
+         BCC       NoFSCommands
+         B         OscliExit
+
+NoFSCommands
+         MOV       R1, #Service_UKCommand
+         BL        Issue_Service
+         CMP       R1, #0
+         BNE       UKCNotClaimed
+         CMP       R0, #0                        ; any error?
+         SETV      NE                            ; V clear if EQ
+         B         OscliExit
+
+OneModule_Failed
+         ADD       stack, stack, #4
+         ADRL      R0, ErrorBlock_BadCommand
+      [  International
+         BL        TranslateError
+      |
+         SETV
+      ]
+         B         OscliExit
+
+UKCNotClaimed
+         MOV       R1, R0
+DoFSCV_Run
+         MOV       R0, #FSControl_RUN
+71       SWI       XOS_FSControl
+OscliExit
+         Pull     "R2"
+         Push     "R0, PC"
+         CMP       R2, #0                        ; -ve means no FS selected.
+         MOVGE     R0, #FSControl_RestoreCurrent
+         SWIGE     XOS_FSControl
+         Pull     "R0, R1"
+
+         Pull     "R2"
+         CMP       R2, #-1
+         BEQ       %FT80
+         BL        ReleaseBuff
+         BL        RemoveOscliCharJobs  ; shut down redirection
+80       TST       R1, #V_bit
+         STRNE     R0, [stack]           ; error pointer
+         Pull     "R0-R2, lr"
+         BICEQS    PC, lr, #V_bit
+         ORRS      PC, lr, #V_bit
+
+06       ADD       R1, R0, #1       ; */ so skip the /, do RUN reason code
+         B         DoFSCV_Run
+
+07
+         Push     "R0, R3-R6"
+         MOV       R6, R0
+         ADR       R0, AliasDot
+         MOV       R3, #0
+         MOV       R2, #-1         ; negative length means just look for it.
+         SWI       XOS_ReadVarVal
+         CMP       R2, #0          ; V always set anyway
+         MOVNE     R1, #1          ; index to step past .
+         BNE       %BT99
+         Pull     "R0, R3-R6"
+PercentDot                        ; entry for *%.
+         ADD       R1, R0, #1
+         MOV       R0, #FSControl_CAT   ; *., skip .
+         B         %BT71
+
+;***************************************************************************
+
+; Routine to look through a module table for a command, and call it.
+; Set up R12 yourself if needed.
+; R0 points at command to find
+; R1 points at module
+; R2 offset of command table : top bit set for "want FS command"
+; C set means allow messy matching, i.e. it's the system command table
+
+; Return C set if found and called, V flag from code called
+; Might not return if module starts up as current object.
+
+ModCommsLookUp ROUT
+         Push   "R0, R2-R10, lr"
+         MOV     R4, #0   ; want all flags clear
+         TEQ     R2, #0   ; don't corrupt C!
+         MOVMI   R4, #FS_Command_Flag
+         BICMI   R2, R2, #&80000000
+         BL      FindItem
+         Pull   "R0, R2-R10, lr", CC
+         BICCCS  PC, lr, #V_bit + C_bit
+         Push    r4               ; save pointer in case needed for syntax mess
+
+; check number of arguments, error with syntaxmessage if naff.
+
+         ADD     R2, R2, R1       ; get R2 back to pointer.
+         ADD     R0, R0, R3       ; point at terminator.
+09       LDRB    R4, [R0], #1
+         CMP     R4, #" "        ; skip spaces.
+         BEQ     %BT09
+         MOV     R3, R1          ; hang on to module ptr.
+         MOV     R1, #0          ; no of parms.
+         MOV     R7, #-1         ; flag for buffer got for GSTRANSing
+
+         MOV     R6, R0
+         SUB     R0, R0, #1
+
+; Now we have :
+; R0 -> commtail, ready for module
+; R1 number of parameters
+; R2 -> info block for command
+; R3 -> module
+; R4 current char
+; R5 execute offset
+; R6 working commtail ptr
+; R7 -1 or buffer UID
+; R8 becomes gstrans map
+; R9 may be a workin gstrans map copy
+; R10 may be a working buffer ptr for copying
+
+         LDRB    R8, [R2, #5]    ; get gstrans_map
+         MOVS    R9, R8
+         BEQ     nogstransingta
+
+         Push   "R2, R5, R6"
+         BL      GetOscliBuffer
+         MOV     R0, R5          ; buffer ptr
+         MOV     R7, R6          ; buffer UID
+         MOV     R10, #0         ; buffer offset for copying
+         Pull   "R2, R5, R6"
+
+nogstransingta
+         CMP     R4, #13         ; check for more to scan
+         CMPNE   R4, #10
+         CMPNE   R4, #0
+         BEQ     %FT12
+
+         MOVS    R9, R9, LSR #1
+         BCC     stripnextparm
+
+    ; gstrans next ; scan afterwards for naffchars
+         Push   "R0-R2"
+
+         ADD     R1, R0, R10                  ; buffer pointer
+         RSB     R2, R10, #OscliBuffSize      ; room left
+         ORR     R2, R2, #GS_Spc_term
+
+         SUB     R0, R6, #1                   ; parameter pointer
+         SWI     XOS_GSTrans
+         BCS     buffer_overflowed_oh_bother
+         BVS     bad_string
+
+         CMP     R2, #0
+         BEQ     preversion_detectified       ; empty expansions are naff
+         ADD     R10, R10, R2
+nastycharscan
+         LDRB    R2, [R1], #1
+         CMP     R2, #&7F
+         CMPNE   R2, #" "
+         BLE     preversion_detectified
+         CMP     R1, R10
+         BLT     nastycharscan
+
+         MOV     R6, R0
+         Pull   "R0-R2"
+         B       next_parameter
+
+preversion_detectified
+         ADR     R0, ErrorBlock_BadParmString
+       [ International
+         BL      TranslateError
+       ]
+         B       pgstcomm
+buffer_overflowed_oh_bother
+         ADRL    R0, ErrorBlock_OscliLongLine
+       [ International
+         BL      TranslateError
+       ]
+         B       pgstcomm
+bad_string
+         ADR     R0, ErrorBlock_BadParmString
+       [ International
+         BL      TranslateError
+       ]
+pgstcomm
+         STR     R0, [stack]
+         MOV     R2, R7
+         BL      ReleaseBuff
+         Pull   "R0-R2, r4"
+         B       unpleasantness_in_ModCommsLookUp
+
+         MakeErrorBlock   BadParmString
+
+AddCharForGSTP
+         CMP     R8, #0
+         MOVEQ   PC, lr
+         CMP     R10, #OscliBuffSize
+         STRLTB  R4, [R0, R10]
+         ADDLT   R10, R10, #1
+         MOVLT   PC, lr
+         Push   "R0-R2"
+         B       buffer_overflowed_oh_bother
+
+stripnextparm
+         Push   "R11"
+         CMP     R4, #""""
+         MOVEQ   R11, #&80000000
+         ORREQ   R1, R1, R11
+         MOVNE   R11, #0
+stripnextchar
+         BL      AddCharForGSTP
+         LDRB    R4, [R6], #1
+         CMP     R4, #""""
+         EOREQ   R1, R1, R11
+         CMP     R4, #" "
+         TSTEQ   R1, #&80000000
+         BEQ     parmfinished
+         CMP     R4, #13
+         CMPNE   R4, #10
+         CMPNE   R4, #0
+         BNE     stripnextchar
+parmfinished
+         BIC     R1, R1, #&80000000
+         Pull   "R11"
+
+next_parameter
+         MOV     R4, #" "
+         BL      AddCharForGSTP
+         SUB     R6, R6, #1
+
+30       LDRB    R4, [R6], #1
+         CMP     R4, #" "
+         BEQ     %BT30
+
+         ADD     R1, R1, #1
+         B       nogstransingta  ; next parameter
+
+  ; parameters counted : check number
+
+12       BL      AddCharForGSTP   ; terminate the copy
+
+         BIC     R1, R1, #&80000000
+         LDR     R4, [R2, #4]
+         MOV     R6, R4, LSR #16  ; max no parms
+         AND     R6, R6, #&FF
+         AND     R4, R4, #&FF     ; min no parms
+         CMP     R1, R4
+         CMPGE   R6, R1
+         BLT     %FT11
+
+   ; checks finished : call the man.
+
+         Push   "R7"              ; j.i.c. module writer can't read
+
+         MOV     lr, PC           ; make link
+         ADD     PC, R3, R5       ; and call
+
+         Pull   "R2, r4"
+         BL      ReleaseBuff      ; discard buffer got for GSTRANSing
+
+         STRVS   R0, [stack]
+         Pull   "R0, R2-R10, lr"
+
+         ORR     lr, lr, #C_bit
+         BICVCS  PC, lr, #V_bit
+         ORRS    PC, lr, #V_bit
+
+; Return a command syntax error.  First issue Service_SyntaxError for translation
+11
+ [ International                  ; Internationalize syntax error messages
+         Pull    "r4"             ; r4-> command string in module
+         MOV     r1, #Service_SyntaxError ; r2->info block for command, r3->module
+         BL      Issue_Service    ; Issue SyntaxError service for possible translation
+         CMP     r1, #0           ; Service claimed?
+         BEQ     unpleasantness_in_ModCommsLookUp ; Yes then r0-> error block
+
+         Push    "r4"             ; Save -> command string
+ ]
+
+         LDR     r4, [r2, #4]
+         MOV     r7, r2
+         BL      GetOscliBuffer   ; get space for error
+         MOV     R2, #ErrorNumber_BadNoParms
+         STR     R2, [R5]
+         LDR     R2, [R7, #8]
+         CMP     R2, #0
+         ADREQ   R0, %FT13        ; default error message
+         ADDNE   R0, R2, R3       ; point at message
+         ORREQ   r4, r4, #International_Help
+         TST     r4, #International_Help
+         ADREQL  r4, MOSdictionary
+         BEQ     %FT37
+         MOV     r7, r0
+         SUB     sp, sp, #16
+         LDR     r2, [r3, #-4]
+         LDR     r0, [r3, #Module_MsgFile]
+         TST     r0, #12,2
+         CMPEQ   r0, #1
+         CMPCS   r2, r0
+         MOVLS   r0, #0
+         BLS     %FT33
+         ADD     r1, r3, r0
+         MOV     r2, #0
+         MOV     r0, sp
+         SWI     XMessageTrans_OpenFile
+         MOVVS   r0, #0
+33       MOV     r1, r7
+         MOV     r7, r0
+         MOV     r2, #0
+         SWI     XMessageTrans_Lookup
+         ADDVS   r2, r0, #4
+         SWI     XMessageTrans_Dictionary
+         ADDVS   r2, r0, #4
+         MOV     r4, r0
+         MOV     r0, r2
+         LDR     r2, [sp, #16]
+         ADD     r1, r5, #4
+         BL      expandsyntaxmessage
+         MOVS    r0, r7
+         SWINE   XMessageTrans_CloseFile
+         ADD     sp, sp, #16
+         Pull    r2
+         B       %FT39
+37
+         Pull    r2
+         ADD     r1, r5, #4
+         BL      expandsyntaxmessage
+39
+         MOV     r0, r5
+
+unpleasantness_in_ModCommsLookUp
+         STR     R0, [stack]
+         Pull   "R0, R2-R10, lr"
+         ORRS    PC, lr, #V_bit+C_bit
+13
+         DCB     "NumParm", 0
+
+        ALIGN
+
+expandsyntaxmessage
+         LDRB    R3, [R0], #1
+         CMP     r3, #TokenEscapeChar
+         BEQ     esm_tok
+         STRB    R3, [R1], #1
+         CMP     R3, #0
+         BNE     expandsyntaxmessage
+         SUB     r1, r1, #1
+         MOV     pc, lr
+
+esm_tok  LDRB    r3, [r0], #1
+         Push   "r0, lr"
+         CMP     r3, #0
+         MOVEQ   r0, r2
+         BEQ     esm001
+         MOV     r0, r4
+esmlp    SUBS    r3, r3, #1
+         LDRNEB  r14, [r0]      ; ECN: Use R14 instead of R4 as using R4 corrupts
+         ADDNE   r0, r0, r14    ; the dictionary pointer thus disallowing recusive tokens
+         BNE     esmlp
+         ADD     r0, r0, #1
+esm001   BL      expandsyntaxmessage
+         Pull   "r0, lr"
+         B       expandsyntaxmessage
+
+;---------------------------------------------------------------------------
+; routine to just find a keyword in a table that has the flags specified.
+; R0 points at command to find
+; R1 points at module
+; R2 offset of command table
+; R4 word to EOR with flags : demand 0 result for match
+; C set means allow messy matching, i.e. it's the system command table
+; Uses R2-R5
+
+; Return C set if found : R5 is execute offset, R3 length of string
+; R2 is offset of execute offset of field found
+; r4 is command pointer
+
+FindItem ROUT
+         Push   "R4, R6"
+         ADD     R2, R2, R1
+         LDRB    R4, [R2]
+         CMP     R4, #0
+         BEQ     FindItem_EOTab
+05       MOV     R3, #0           ; offset
+01       LDRB    R4, [R0, R3]
+         LDRB    R5, [R2, r3]
+         CMP     R4, #&80         ; in table ?
+         ADRCC   R6, Up_ItAndTerm_Check_Table
+         LDRCCB  R4, [R6, R4]
+         CMP     R4, #32
+         CMPLE   R5, #32
+         BLE     %FT04           ; matched, and we're at the terminator
+         UpperCase R5, R6
+         CMP     R4, R5
+         ADDEQ   R3, R3, #1
+         BEQ     %BT01
+         CMP     R4, #"."         ; success if abbreviation
+         BEQ     %FT02
+         TST     lr, #C_bit
+         BEQ     %FT07           ; nomatch
+         CMP     R5, #32
+         BGT     %FT07
+         CMP     R4, #"A"
+         RSBGES  R6, R4, #"Z"
+         BLT     %FT04             ; matched, at terminator
+07       LDRB    R5, [R2], #1
+         CMP     R5, #32
+         BGT     %BT07             ; skip to terminator
+         ADD     R2, R2, #3
+         BIC     R2, R2, #3        ; ALIGN
+06       ADD     R2, R2, #16       ; !!! DEPENDANT ON TABLE FORMAT!!!
+         LDRB    R5, [R2]
+         CMP     R5, #0
+         BNE     %BT05
+FindItem_EOTab
+         Pull   "R4, R6"
+         BICS    PC, lr, #C_bit   ; back with not found.
+
+Up_ItAndTerm_Check_Table
+; Table to uppercase and test for terminators for passed * commands
+;      0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
+   =   0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0     ; 0
+   =   0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0     ; 1
+   =   0 , "!", 0 , "#" , 0, 0, 0, "'", "(", ")", "*" , "+", 0 , "-", ".", "/"    ; 2
+   =  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", 0, ";", 0 , "=", 0 , "?"    ; 3
+   =  "@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"    ; 4
+   =  "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", 0, "]", 0, "_"    ; 5
+   =  "`", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"    ; 6
+   =  "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "{", 0 , "}", "~", 0     ; 7
+
+;  WHILE . < Up_ItAndTerm_Check_Table+256 ; top bit stuff done by CMP #&80
+;   = . - Up_ItAndTerm_Check_Table
+;  WEND                            ; entry for chars > 127 = char
+
+02       CMP     R5, #32          ; only success if $R2 not terminated.
+         BLE     %BT07
+         ADD     R3, R3, #1       ; skip .
+04       MOV     r6, r2
+         ADD     r2, r2, r3
+08       LDRB    R5, [R2], #1
+         CMP     R5, #0
+         BNE     %BT08            ; demand NULL terminator
+         ADD     R2, R2, #3
+         BIC     R2, R2, #3       ; ALIGN
+         LDR     R5, [R2, #4]     ; get information word
+
+         AND     R5, R5, #&C0000000 :AND::NOT:Help_Is_Code_Flag
+                                  ; clear param numbers/low flags.
+         LDR     R4, [stack]
+         EORS    R5, R5, R4
+         BNE     %BT06            ; flags don't match
+
+         LDR     R5, [R2]         ; get Execute offset
+         CMP     R5, #0
+         BEQ     %BT06            ; not a command
+         STR     r6, [stack]      ; return r4
+         Pull   "R4, R6"
+         SUB     R2, R2, R1       ; get back to offset.
+         ORRS    PC, lr, #C_bit
+
+;****************************************************************************
+; variegated routines
+;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+OscliInit
+; circular buffer initialisation :
+;  botUID := topUID := 0 ; currend := circbuffstart
+; Redirection handles := 0
+         MOV        R0, #0
+         STR        R0, [R0, #OscliCBbotUID]
+         STR        R0, [R0, #OscliCBtopUID]
+         LDR        R1, =OscliCircBuffStart
+         STR        R1, [R0, #OscliCBcurrend]
+         STRB       R0, [R0, #RedirectInHandle]
+         STRB       R0, [R0, #RedirectOutHandle]
+         MOV        PC, R14
+
+; buffer is valid if botUID < UID
+
+
+; GetOscliBuffer used in module handler to get error buffers - saves workspace!
+
+GetOscliBuffer ROUT
+ ; return ptr in R5 to next buffer in Oscli's circular job
+ ; UID in R6
+ ; corrupts R2
+
+; currend +:= 256
+      MOV    R2, #0
+      LDR    R5, [R2, #OscliCBcurrend]
+      ADD    R5, R5, #OscliBuffSize
+
+; IF currend >= circbufflimit
+;  THEN currend := circbuffstart
+      LDR    R6, =OscliCircBuffLimit
+      CMP    R5, R6
+      LDRHS  R5, =OscliCircBuffStart
+      STR    R5, [R2, #OscliCBcurrend]
+
+; topUID +:= 1
+      LDR    R6, [R2, #OscliCBtopUID]
+      ADD    R6, R6, #1
+      STR    R6, [R2, #OscliCBtopUID]
+; IF topUID > botUID + noBuffers
+; THEN botUID +:= 1
+      LDR    R5, [R2, #OscliCBbotUID]
+      SUB    R2, R6, R5
+      CMP    R2, #OscliNoBuffs
+      MOV    R2, #0
+      ADDGE  R5, R5, #1
+      STRGE  R5, [R2, #OscliCBbotUID]
+
+; RETURN currend, topUID
+      LDR    R5, [R2, #OscliCBcurrend]
+      MOV    PC, lr
+
+
+ReleaseBuff ; take UID in R2, check whether can step back topUID
+     Push  "R1-R4"
+     MOV    R1, #0
+     LDR    R3, [R1, #OscliCBtopUID]
+     LDR    R4, [R1, #OscliCBbotUID]
+; IF UID = topUID AND topUID <> botUID
+     CMP    R3, R4
+     Pull  "R1-R4",EQ
+     MOVEQS PC, lr
+     CMP    R2, R3
+     Pull  "R1-R4",NE
+     MOVNES PC, lr
+; THEN $(
+;    topUID -:= 1
+     SUB    R3, R3, #1
+     STR    R3, [R1, #OscliCBtopUID]
+
+;    IF currend = circbuffstart THEN currend := circbufflimit
+     LDR    R3, [R1, #OscliCBcurrend]
+     LDR    R4, =OscliCircBuffStart
+     CMP    R3, R4
+     LDREQ  R3, =OscliCircBuffLimit
+; currend -:= 256
+     SUB    R3, R3, #OscliBuffSize
+     STR    R3, [R1, #OscliCBcurrend]
+     Pull  "R1-R4"
+     MOVS   PC, lr
+
+        LTORG                   ; needed now not at top level
+
+OscliRestoreFS
+    Push    "R0, lr"
+    MOV      R0, #FSControl_RestoreCurrent
+    SWI      XOS_FSControl
+    Pull    "R0, PC"
+
+OscliTidy    ROUT  ; shut down redirection, restore permanent FS
+     Push   "lr"
+     BL      RemoveOscliCharJobs
+     BL      OscliRestoreFS
+     Pull   "PC"
+
+RemoveOscliCharJobs ROUT
+     Push   "R0-R2, lr"
+     MOV     R0, #0
+     LDRB    R1, [R0, #RedirectInHandle]
+     CMP     R1, #0
+     STRNEB  R0, [R0, #RedirectInHandle]
+     SWINE   XOS_Find
+     MOV     R0, #0 ; May have got error (discarded)
+     LDRB    R1, [R0, #RedirectOutHandle]
+     CMP     R1, #0
+     STRNEB  R0, [R0, #RedirectOutHandle]
+     SWINE   XOS_Find
+     MOV     R2, #0
+ [ :LNOT: redirectinkey
+     MOV     R0, #RdchV
+     ADR     R1, RedirectRdch
+     SWI     XOS_Release
+ ]
+     MOV     R0, #WrchV
+     ADR     R1, RedirectWrch
+     SWI     XOS_Release
+     Pull   "R0-R2, PC",,^
+
+ [ :LNOT: redirectinkey
+RedirectRdch ROUT
+     Push   "R1"
+     MOV     R0, #0
+     LDRB    R1, [R0, #ESC_Status]
+     TST     R1, #1:SHL:6
+     BNE     %FT01
+     LDRB    R1, [R0, #RedirectInHandle]
+     SWI     XOS_BGet
+     BVS     RedirectError
+     Pull   "R1, lr", CC
+     BICCCS  PC, lr, #C_bit
+01   ADR     R1, RedirectRdch
+     MOV     R0, #RdchV
+     Push   "R2"
+     MOV     R2, #0
+     SWI     XOS_Release
+     Pull   "R2"
+     MOV     R0, #0
+     LDRB    R1, [R0, #RedirectInHandle]
+     STRB    R0, [R0, #RedirectInHandle]
+     SWI     XOS_Find
+     Pull   "R1, lr"
+     BICEQS  PC, lr, #C_bit     ; EQ/NE still Escape state!
+     ORRNES  PC, lr, #C_bit
+ ]
+
+RedirectWrch ROUT
+     Push   "R1"
+     MOV     R1, #0
+     LDRB    R1, [R1, #RedirectOutHandle]
+     SWI     XOS_BPut
+     Pull   "R1, pc", VC, ^             ; VClear in stacked pc/psr
+
+RedirectError
+     BL      RemoveOscliCharJobs
+     Pull   "R1, lr"
+     ORRS    PC, lr, #V_bit
+
+; **************************************************************************
+;
+;       SWI OS_ChangeRedirection - Read/write redirection handles
+;
+; in:   R0 = new input  handle (0 => not redirected, -1 => leave alone)
+;       R1 = new output handle (0 => not redirected, -1 => leave alone)
+;
+; out:  R0 = old input  handle (0 => not redirected)
+;       R1 = old output handle (0 => not redirected)
+;
+
+ChangeRedirection ROUT
+        MOV     R12, #0
+        LDRB    R10, [R12, #RedirectInHandle]
+        LDRB    R11, [R12, #RedirectOutHandle]
+
+; do input handle
+
+        CMP     R0, #&100               ; if out of range then just read
+        BCS     %FT20
+
+        STRB    R0, [R12, #RedirectInHandle]
+
+ [ :LNOT: redirectinkey
+        CMP     R0, #1                  ; CS <=> (R0 non-zero)
+        TEQ     R10, #0                 ; NE <=> (R10 non-zero)
+        BHI     %FT20                   ; [both non-zero, skip]
+
+        BCS     %FT10                   ; [just R0 non-zero, so claim]
+        BEQ     %FT20                   ; [both zero, skip]
+
+; R10 non-zero, R0 zero, so release vector
+
+        Push    "R0-R2, lr"             ; set up registers for claim or release
+        MOV     R0, #RdchV
+        ADR     R1, RedirectRdch
+        MOV     R2, #0
+        SWI     XOS_Release
+        STRVS   R0, [sp, #0*4]
+        Pull    "R0-R2, lr"
+        ORRVS   lr, lr, #V_bit
+        ExitSWIHandler VS
+        B       %FT20
+
+; R10 zero, R0 non-zero, so claim vector
+
+10
+        Push    "R0-R2, lr"
+        MOV     R0, #RdchV
+        ADR     R1, RedirectRdch
+        MOV     R2, #0
+        SWI     XOS_Claim
+        STRVS   R0, [sp, #0*4]
+        Pull    "R0-R2, lr"
+        ORRVS   lr, lr, #V_bit
+        ExitSWIHandler VS
+ ]
+
+20
+
+; do output handle
+
+        CMP     R1, #&100               ; if out of range then just read
+        BCS     %FT40
+
+        STRB    R1, [R12, #RedirectOutHandle]
+        CMP     R1, #1                  ; CS <=> (R1 non-zero)
+        TEQ     R11, #0                 ; NE <=> (R11 non-zero)
+        BHI     %FT40                   ; [both non-zero, skip]
+
+        BCS     %FT30                   ; [just R1 non-zero, so claim]
+        BEQ     %FT40                   ; [both zero, skip]
+
+; R11 non-zero, R1 zero, so release vector
+
+        Push    "R0-R2, lr"             ; set up registers for claim or release
+        MOV     R0, #WrchV
+        ADR     R1, RedirectWrch
+        MOV     R2, #0
+        SWI     XOS_Release
+        STRVS   R0, [sp, #0*4]
+        Pull    "R0-R2, lr"
+        ORRVS   lr, lr, #V_bit
+        ExitSWIHandler VS
+        B       %FT40
+
+; R11 zero, R1 non-zero, so claim vector
+
+30
+        Push    "R0-R2, lr"
+        MOV     R0, #WrchV
+        ADR     R1, RedirectWrch
+        MOV     R2, #0
+        SWI     XOS_Claim
+        STRVS   R0, [sp, #0*4]
+        Pull    "R0-R2, lr"
+        ORRVS   lr, lr, #V_bit
+        ExitSWIHandler VS
+
+40
+        MOV     R0, R10
+        MOV     R1, R11
+        ExitSWIHandler
+
+;**************************************************************************
+; Module selection
+;  Entered with V set if FileSwitch found a - : must get module or give error
+;  R2 >= 0 if filing system selected: do nothing
+;  R1 -> command prefix
+;  Out: R1 updated
+;    R2 = -1: no module
+;    R2 = -<larger>:  R2 is selected module node
+;         Selected prefix also preferred
+
+CheckForModuleAsPrefix ROUT
+     Push   "R0-R6, lr"
+     ADDVS   R1, R1, #1         ; skip -
+     MOVVS   R2, #"-"
+     BVS     %FT02
+     CMP     R2, #-1
+     BNE     %FT01
+     MOV     R2, #":"           ; terminators for lookup
+
+02   ADR     R5, %FT10
+     STR     R1, [stack, #4]
+03   LDRB    R3, [R1], #1
+     UpperCase R3, R4
+     LDRB    R4, [R5], #1
+     CMP     R3, R4
+     BEQ     %BT03
+     CMP     R4, #0
+     LDRNE   R1, [stack, #4]
+     SUBEQ   R1, R1, #1
+     MOV     R6, R1
+     LDR     R1, =AliasExpansionBuffer
+05   LDRB    R3, [R6], #1
+     CMP     R3, #"."           ; disallow abbreviations: they're confusing!
+     CMPNE   R3, #" "
+     BLE     %FT01
+     CMP     R3, R2
+     STRNEB  R3, [R1], #1
+     BNE     %BT05
+
+     MOV     R3, #0
+     STRB    R3, [R1]
+
+     MOV     R0, #ModHandReason_LookupName
+     LDR     R1, =AliasExpansionBuffer
+     SWI     XOS_Module
+     BVS     %FT01
+     MOV     R2, R1
+     LDR     R1, =AliasExpansionBuffer
+     MOV     R0, #ModHandReason_MakePreferred
+     SWI     XOS_Module
+
+     MOV     R0, #Module_List
+04   LDR     R0, [R0]
+     SUBS    R2, R2, #1
+     BPL     %BT04
+     MOV     R1, R6                 ; point at rest of command line.
+     RSB     R2, R0, #0
+     LDR     R0, [stack], #12       ; Pull R0, skip R1,R2
+     Pull   "R3-R6, lr"
+     BICS    PC, lr, #V_bit
+
+01
+     Pull   "R0-R6, PC",, ^    ; return fileswitch error if set
+10
+     =      "MODULE#",0
+     ALIGN
+
+     END
diff --git a/s/PMF/Buffer b/s/PMF/Buffer
new file mode 100644
index 0000000000000000000000000000000000000000..a9e84696a51ed9a222a95357f3397207e6a292f3
--- /dev/null
+++ b/s/PMF/Buffer
@@ -0,0 +1,283 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.PMF.Buffer
+
+; *****************************************************************************
+;
+;       NewInsV - Routine for InsVec
+;
+; in:   R0 = character to be inserted
+;       R1 = buffer number
+;
+; out:  R0, R1, R3-R12 preserved
+;       R2 undefined
+;       C=1 <=> insertion failed
+;
+
+NewInsV ROUT
+        CMP     R1, #NBuffers
+        MOVCS   PC, R14                         ; not known about, pass it on
+
+        Push    "R3-R5"
+        MOV     R3, R1, LSL #2                  ; make index a word index
+        ADR     R2, BuffParms
+        ADD     R2, R2, R3, LSL #1
+        LDMIA   R2, {R2, R5}                    ; get address, size
+
+        LDR     R4, [R3, #BuffInPtrs]
+        STRB    R0, [R2, R4]                    ; store byte anyway
+        ADD     R4, R4, #1                      ; increment pointer
+
+        TEQ     R4, R5
+        MOVEQ   R4, #0
+
+        LDR     R5, [R3, #BuffOutPtrs]          ; does inptr=outptr ?
+        CMP     R4, R5                          ; C=1 <=> R4 >= R5
+        CMPHI   R5, R4                          ; C=1 <=> R4 = R5
+        STRCC   R4, [R3, #BuffInPtrs]           ; if not, then safe
+
+        BCC     %FT10                           ; no event, cos not full
+        TEQ     R1, #(Buff_Mouse :SHL: 2), 2    ; clear carry and test if mouse
+        CMPNE   R1, #Buff_RS423Out              ; C=1 => output buffer
+        BCS     %FT10                           ; no event, cos not input
+
+        MOV     R2, R0                          ; put character in 'Y'
+        MOV     R0, #Event_InputFull            ; event number
+        BL      OSEVEN                          ; preserves R0-R3
+        MOV     R0, R2                          ; restore character
+        SEC                                     ; indicate buffer full
+10
+        Pull    "R3-R5,PC"                      ; claim call
+
+; *****************************************************************************
+;
+;       NewRemV - Routine for RemVec
+;
+; in:   R1 = buffer number (0 => keyboard buffer)
+;       V=0 => remove character
+;       V=1 => examine only
+;
+; out:  R0 = R2 = next character for examine option or character removed
+;       R1, R3-R12 preserved
+;       C=1 <=> buffer was empty on entry
+;
+
+NewRemV ROUT
+        BVS     Examine
+
+        CMP     R1, #NBuffers
+        MOVCSS  PC, R14         ; not known about, pass it on (preserving V)
+
+        Push    "R3-R5"
+
+        MOV     R3, R1, LSL #2
+        LDR     R4, [R3, #BuffOutPtrs]
+        LDR     R5, [R3, #BuffInPtrs]
+
+        CMP     R4, R5
+        CMPHI   R5, R4                          ; C=1 <=> (R4 = R5) ie empty
+        BCS     RemVExit
+
+        ADR     R2, BuffParms
+        ADD     R2, R2, R3, LSL #1
+        LDMIA   R2, {R2, R5}                    ; get address, size
+
+        LDRB    R2, [R2, R4]                    ; get next byte to be read out
+        MOV     R0, R2
+        ADD     R4, R4, #1                      ; increment out pointer
+
+        TEQ     R4, R5                          ; wrap pointer if necessary
+        MOVEQ   R4, #0
+
+        STR     R4, [R3, #BuffOutPtrs]          ; bugfix - was STRB
+        TEQ     R1, #Buff_Mouse                 ; mouse => not output buffer
+        BEQ     RemVExit                        ; exit (C=0) if not
+        CMP     R1, #Buff_RS423Out              ; C=1 => output buffer
+        BCC     RemVExit                        ; exit (C=0) if not
+
+        LDR     R5, [R3, #BuffInPtrs]           ; reload in-ptr
+        TEQ     R4, R5                          ; are ptrs same now ?
+        BNE     RemVExitCLC                     ; no, then exit setting C=0
+
+        Push    R0                              ; save character
+        MOV     R0, #Event_OutputEmpty          ; output buffer empty event
+        BL      OSEVEN                          ; generate event
+        Pull    R0                              ; restore character
+
+RemVExitCLC
+        CLC                                     ; make sure carry clear
+
+RemVExit
+        Pull    "R3-R5,PC"
+
+Examine
+        CMP     R1, #NBuffers
+        MOVCSS  PC, R14         ; not known about, pass it on (preserving V)
+
+        Push    "R3-R5"
+
+        MOV     R3, R1, LSL #2
+        ADR     R2, BuffParms
+
+        LDR     R2, [R2, R3, LSL #1]            ; R2 -> buffer
+
+        LDR     R4, [R3, #BuffOutPtrs]
+        LDR     R5, [R3, #BuffInPtrs]
+
+        CMP     R4, R5
+        CMPHI   R5, R4                          ; C=1 <=> (R4 = R5) ie empty
+
+        LDRCCB  R2, [R2, R4]                    ; if ok then examine byte
+        MOVCC   R0, R2
+
+        Pull    "R3-R5,PC"
+
+; *****************************************************************************
+;
+;       NewCnpV - Routine for CnpVec
+;
+; in:   R1 = buffer number (0 => keyboard)
+;       V=0, C=0 => count entries
+;       V=0, C=1 => count spaces
+;       V=1 => purge buffer
+;
+; out:  R0 undefined
+;       (purge) R1-R12 preserved
+;       (count) R1,R2 = count, R3-R12 preserved
+;
+
+NewCnpV
+        CMP     R1, #NBuffers
+        MOVCSS  PC, R14         ; not known about, pass it on (preserving V)
+
+        Push    "R3-R5"
+
+        TEQP    R14, #0                 ; restore V and C
+
+        MOV     R3, R1, LSL #2
+
+        LDR     R4, [R3, #BuffOutPtrs]
+
+        STRVS   R4, [R3, #BuffInPtrs]   ; if purge, then make in=out
+        Pull    "R3-R5,PC", VS          ; and return
+
+        LDR     R5, [R3, #BuffInPtrs]
+        SUB     R1, R5, R4              ; in - out (don't stamp on carry)
+
+        ADR     R5, BuffParms+4
+        LDR     R5, [R5, R3, LSL #1]    ; get size
+
+        TEQ     R1, #0                  ; don't stamp on carry
+        ADDMI   R1, R1, R5              ; wrap number of chars if negative
+
+        SUBCS   R1, R5, R1              ; C=1 => convert to spaces
+        SUBCS   R1, R1, #1              ; one fewer spaces than BuffSizes
+
+        MOV     R2, R1, LSR #8          ; make R2 = hi-byte
+        AND     R1, R1, #&FF            ; and  R1 = lo-byte
+
+        Pull    "R3-R5,PC"
+
+; *****************************************************************************
+
+BuffParms
+        &       KeyBuff
+        &       KeyBuffSize
+
+        &       RS423InBuff
+        &       RS423InBuffSize
+
+        &       RS423OutBuff
+        &       RS423OutBuffSize
+
+        &       PrintBuff
+        &       PrintBuffSize
+
+        &       Sound0Buff
+        &       Sound0BuffSize
+
+        &       Sound1Buff
+        &       Sound1BuffSize
+
+        &       Sound2Buff
+        &       Sound2BuffSize
+
+        &       Sound3Buff
+        &       Sound3BuffSize
+
+        &       SpeechBuff
+        &       SpeechBuffSize
+
+        &       MouseBuff
+        &       MouseBuffSize
+
+; *****************************************************************************
+;
+;       DoInsertESC - Insert character into buffer, checking for escape
+;
+; in:   R1 = buffer id
+;       R2 = character
+;
+
+DoInsertESC
+        CMP     R1, #2                          ; if not keyboard or serial input
+        BCS     INSERT                          ; then don't treat as special
+
+        LDROSB  R0, RS423mode                   ; Z => simulate keyboard
+        TST     R0, R1                          ; NZ => RS423 input and RS8Bit
+        BNE     INSERT
+
+        Push    R14
+
+        LDROSB  R0, ESCch                       ; escape character
+        TEQ     R2, R0                          ; if escape character
+        LDROSB  R0, ESCaction, EQ
+        TEQEQ   R0, #0                          ; and FX229,0
+        BNE     CKESCY                          ; not escape or let it thru
+
+; ESCAPE detected
+
+        LDROSB  R0, ESCBREAK                    ; FX 200
+        TST     R0, #1                          ; bit 0 set ?
+        BNE     %FT10                           ; escape ignored
+
+        MOV     R0, #Event_Escape
+        BL      OSEVEN                          ; exits carry set if disabled
+        BCC     %FT10                           ; [event enabled, so don't do
+                                                ; normal escape action]
+        Push    "R1, R12"
+        BL      DoOsbyte7D                      ; generate escape condition
+        Pull    "R1, R12"
+10
+        CLC                                     ; character inserted OK
+        Pull    PC
+
+CKESCY
+        MOV     R0, #Event_CharInput
+        BL      OSEVEN                          ; preserves R0-R2
+        Pull    R14
+INSERT
+        MOV     R0, R2
+INSRT
+        Push    "R10,R12,R14"
+        MOV     R10, #INSV
+GoVec
+        BL      GoVec2
+        Pull    "R10,R12,PC"
+
+GoVec2
+        CallAVector
+
+        END
diff --git a/s/PMF/Def b/s/PMF/Def
new file mode 100644
index 0000000000000000000000000000000000000000..049ca858c90b06e1c537280f0ecee99080d7b046
--- /dev/null
+++ b/s/PMF/Def
@@ -0,0 +1,148 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.PMF.Def
+ MACRO
+ Protocol
+;
+; Protocol constants
+;
+; 4 bit codes for input commands
+;
+LEDON * &00
+LEDOFF * &10
+REQUEST * &20
+ACK * &30
+SPDDATA * &40 ;Bottom four bits are data to convert
+RSTREQ * &80 ;Request for reset
+;
+; 4 bit input data types
+;
+; Requests
+;
+KBID * 0
+SPDRESET * 1
+MDATA * 2 ;New mouse position, even if it hasn't moved
+;
+; Acks
+;
+BYTE * 0
+SCAN * 1
+MOUSE * 2
+ALL * 3
+;
+; data output
+;
+; Type         d7 d6 d5 d4 d3 d2 d1 d0 number of bytes
+; Reset        1  x  x  x  x  x  x  x  1
+; Key up       0  0  1  1  x  x  x  x  2 (row then column)
+; Key down     0  0  1  0  x  x  x  x  2 (row then column)
+; Mouse change 0  1  x  x  x  x  x  x  2 (X, Y)
+; SPD data     0  0  0  0  x  x  x  x  8
+; KB Ids       0  0  0  1  x  x  x  x  2 (low nibble then high nibble)
+;
+; The keyboard type
+;
+IDTYPE * &10
+KBTYPE * 0 ;This is keyboard 0
+;
+; Key transitions
+;
+KEYDOWN * &20
+KEYUP * &30
+;
+; Mouse transitions
+;
+MMOVED * &40
+;
+; SPD converted data
+;
+SPDDONE * 0
+;
+; Reset types
+;
+HRDRESET * &FF
+RST1ACK * &FE
+RST2ACK * &FD
+
+; New keyboard protocols
+
+; Keyboard -> ARM
+
+K1mdat  * &00           ; 0xxx xxxx     Mouse data from keyboard
+K1kbid  * &80           ; 10xx xxxx     Keyboard ID from keyboard
+K1kdda  * &C0           ; 1100 xxxx     Key down data
+K1kuda  * &D0           ; 1101 xxxx     Key up data
+K1pdat  * &E0           ; 1110 xxxx     SPD data from keyboard (won't happen)
+K1rak2  * &FD           ; 1111 1101     Reset acknowledge 2
+K1rak1  * &FE           ; 1111 1110     Reset acknowledge 1
+K1hrst  * &FF           ; 1111 1111     Hard reset
+
+K1kbidmask * &3F        ; 0011 1111     Valid bits in keyboard id
+
+K1notmousedata * &80
+
+; ARM -> Keyboard
+
+;
+; The IOC registers
+;
+        ^       &04, R12
+KARTTx  #       0
+KARTRx  #       0
+
+        ^       &20, R12
+IRQStatusB #    4
+IRQReqB    #    4
+IRQMaskB   #    4
+
+        ^       &70, R12
+Timer3Low   #   4
+Timer3High  #   4
+Timer3Go    #   4
+Timer3Latch #   4
+
+; Register bits
+
+KARTRxBit   *   &80
+KARTTxBit   *   &40
+KARTIRQBits *   KARTTxBit :OR: KARTRxBit
+
+K1leds  * &00           ; 0000 0xxx     Set LED states
+K1rqid  * &20           ; 0010 0000     Request keyboard id
+K1prst  * &21           ; 0010 0001     SPD reset
+K1rqmp  * &22           ; 0010 0010     Request mouse position
+K1nack  * &30           ; 0011 0000     Acknowledge (keys- mouse-)
+K1sack  * &31           ; 0011 0001     Acknowledge (keys+ mouse-)
+K1mack  * &32           ; 0011 0010     Acknowledge (keys- mouse+)
+K1smak  * &33           ; 0011 0011     Acknowledge (keys+ mouse+)
+K1back  * &3F           ; 0011 1111     Byte acknowledge (between 2 data bytes)
+K1rqpd  * &40           ; 0100 xxxx     Request SPD data conversion
+
+; Keyboard vector offsets
+
+        ^ 0
+
+KVKeyTran          # 4
+KVKeyTranSize      # 4
+KVInkeyTran        # 4
+KVShiftingList     # 4
+KVSpecialList      # 4
+KVSpecialCodeTable # 4
+KVInit             # 4
+KVPendingAltCode   # 4
+KVPendingAltSpecial # 4             ; Used only internally
+
+ MEND
+ END
diff --git a/s/PMF/Internat b/s/PMF/Internat
new file mode 100644
index 0000000000000000000000000000000000000000..24eb70526d8e2e993360df98327535bf4949f935
--- /dev/null
+++ b/s/PMF/Internat
@@ -0,0 +1,250 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > Internat
+
+The International module
+========================
+
+Author: Tim Dobson
+Name: International
+Version: 0.01
+Circulation: Acorn
+History:
+    0.01: 17-Mar-89 to 03-Apr-89: First draft.
+
+This document describes the International module version 1.13, as found in
+RISC OS 2.00. It also describes the differences between this version and
+version 1.05 (the version in Arthur 1.20).
+
+The International module allows the user to tailor his machine for use in
+different countries by setting:-
+
+ a) the mapping of keys to character codes (the *keyboard*), and
+
+ b) the mapping from character codes to characters (the *alphabet*), or
+
+ c) both mappings a) and b) at once (the *country*).
+
+This module, in conjunction with the MOS, controls the selection of these
+mappings, but the interface allows the actual mappings to be implemented in
+one or more separate relocatable modules, via the service mechanism.
+
+Explanation of terms
+--------------------
+
+Each country has a *country name* and an associated *country number*.
+
+Each alphabet has an *alphabet name* and an associated *alphabet number*.
+
+Each country has an associated alphabet, but a given alphabet may be
+associated with more than one country.
+
+Each country also has an associated keyboard, but keyboards do not have their
+own names or numbers; keyboards are specified by country name or country
+number.
+
+Country numbers are in the range 0 to 99, alphabet numbers are in the
+range 100 to 126.
+
+Modules can provide new country/alphabet names/numbers by responding to
+various sub-reason codes of service call Service_International (&43).
+
+Operating system interface
+--------------------------
+
+The operating system maintains the following variables:-
+
+1) The current alphabet number, ie the alphabet number of the currently
+selected alphabet.
+
+2) The current keyboard number, ie the country number of the currently
+selected keyboard.
+
+3) The current country number, ie the country number of the currently
+selected country. NB This may not be relevant if the alphabet and/or keyboard
+have been set separately.
+
+These variables are controlled by a number of OS_Byte calls:-
+
+OS_Byte &46 - Read/set country
+------------------------------
+
+in:     R1 <> 127 : Select country whose country number is R1.
+
+out:    R1 = old country number, if R1 on entry was valid.
+        R1 = 0 => R1 on entry did not correspond to a known country (checked
+        for by issuing the service to convert the country number into an
+        alphabet number).
+
+
+in:     R1 = 127 : Read country number.
+
+out:    R1 = current country number.
+
+
+OS_Byte &47 - Read/set alphabet/keyboard
+----------------------------------------
+
+in:     R1 in range 0..126: Select alphabet from alphabet number or country
+        number specified by R1.
+
+out:    R1 = old alphabet number, if R1 on entry was valid.
+        R1 = 0 => R1 on entry did not correspond to a known country or
+        alphabet.
+
+
+in:     R1 = 127: Read alphabet number.
+
+out:    R1 = current alphabet number.
+
+
+in:     R1 in range 128..254: Select keyboard from country number specified
+        by (R1-128).
+
+out:    R1 = old keyboard number, if R1 on entry was valid.
+        R1 = 0 => R1 on entry did not correspond to a known country.
+
+
+in:     R1 = 255: Read keyboard number.
+
+out:    R1 = current keyboard number.
+
+
+OS_Byte &F0 - Read country number
+---------------------------------
+
+in:     R1 = 0
+        R2 = &FF
+
+out:    R1 = current country number
+
+This call is provided for backwards compatibility with international versions
+of the Master Compact.
+
+
+Service calls
+-------------
+
+The service call Service_International (R1=&43) is issued at various times by
+the operating system and by the International module. The reason for the call
+is specified by the contents of R2, as follows:-
+
+R2 = &00: Convert country name to country number
+
+in:     R3 -> null-terminated country name string (may be abbreviated
+              with '.')
+
+out:    All registers preserved if country not recognised, otherwise:-
+        R1 = 0 (call claimed)
+        R4 = country number
+
+R2 = &01: Convert alphabet name to alphabet number
+
+in:     R3 -> null-terminated alphabet name string (may be abbreviated
+              with '.')
+
+out:    All registers preserved if alphabet not recognised, otherwise:-
+        R1 = 0 (call claimed)
+        R4 = alphabet number
+
+R2 = &02: Convert country number to country name
+
+in:     R3 = country number
+        R4 -> buffer for name
+        R5 = buffer length
+
+out:    All registers preserved if country number not recognised, otherwise:-
+        R1 = 0 (call claimed)
+        Buffer holds country name (no terminator) truncated to buffer length
+        R5 = number of characters put into buffer
+
+R2 = &03: Convert alphabet number to alphabet name
+
+in:     R3 = alphabet number
+        R4 -> buffer for name
+        R5 = buffer length
+
+out:    All registers preserved if alphabet number not recognised, otherwise:-
+        R1 = 0 (call claimed)
+        Buffer holds alphabet name (no terminator) truncated to buffer length
+        R5 = number of characters put into buffer
+
+
+R2 = &04: Convert country number to alphabet number
+
+in:     R3 = country number
+
+out:    All registers preserved if country number not recognised, otherwise:-
+        R1 = 0 (call claimed)
+        R4 = alphabet number
+
+
+R2 = &05: Define a range of characters from a given alphabet
+
+in:     R3 = alphabet number
+        R4 = ASCII code of first character in range
+        R5 = ASCII code of last character in range
+
+out:    All registers preserved if alphabet number not recognised, otherwise:-
+        R1 = 0 (call claimed)
+
+If the alphabet number is recognised by the module, it should define all
+characters in the range R4 to R5 inclusive with the appropriate character
+shapes (using VDU 23,code,...). Any characters which do not have defined
+shapes in the specified alphabet (eg codes &80-&9F in Latin1) should be left
+unchanged.
+
+R2 = &06: Notification that the keyboard number has changed.
+
+in:     R3 = new keyboard number
+        R4 = alphabet number associated with the keyboard number (not
+             necessarily the same as the current alphabet number)
+
+out:    All registers preserved (call should never be claimed).
+
+This call is issued so that modules providing keyboard handlers.
+
+
+OS_CLI commands provided
+------------------------
+
+    *Alphabet [<country name> | <alphabet name>]
+
+*Alphabet sets an alphabet from a country or alphabet name.
+*Alphabet with no parameter displays the currently selected alphabet.
+
+
+    *Country [<country name>]
+
+*Country sets the appropriate alphabet and keyboard driver for a particular
+country.
+*Country with no parameter displays the currently selected country.
+
+
+    *Keyboard [<country name>]
+
+*Keyboard sets the keyboard driver for a particular country.
+*Keyboard with no parameter displays the currently selected keyboard.
+
+
+    *Alphabets
+
+*Alphabets lists the names of the available alphabets.
+
+
+    *Countries
+
+*Countries lists the names of known countries
+
diff --git a/s/PMF/KbdDrA1 b/s/PMF/KbdDrA1
new file mode 100644
index 0000000000000000000000000000000000000000..cce231fc850ff93b1d06e599fc15d9093837301a
--- /dev/null
+++ b/s/PMF/KbdDrA1
@@ -0,0 +1,611 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > Sources.PMF.KbdDrA1
+
+; Archimedes keyboard driver.
+
+; ***********************************
+; ***    C h a n g e   L i s t    ***
+; ***********************************
+
+; Date      Who Description
+; ----      --- -----------
+; 24-Feb-93 SMC Split from file "key".
+;               Use generic keyboard/mouse interfaces.
+
+        [ PollMouse
+K1ack   *       K1sack
+        |
+K1ack   *       K1smak
+        ]
+
+        MACRO
+$lab    RXonTXoff  $reg, $cond
+$lab
+        LDR$cond.B $reg, IRQMaskB
+        BIC$cond   $reg, $reg, #KARTTxBit
+        ORR$cond   $reg, $reg, #KARTRxBit
+        STR$cond.B $reg, IRQMaskB
+        MEND
+
+        MACRO
+$lab    TXonRXoff  $reg, $cond
+$lab
+        LDR$cond.B $reg, IRQMaskB
+        BIC$cond   $reg, $reg, #KARTRxBit
+        ORR$cond   $reg, $reg, #KARTTxBit
+        STR$cond.B $reg, IRQMaskB
+        MEND
+
+        MACRO
+$lab    RXon       $reg, $cond
+$lab
+        LDR$cond.B $reg, IRQMaskB
+        ORR$cond   $reg, $reg, #KARTRxBit
+        STR$cond.B $reg, IRQMaskB
+        MEND
+
+        MACRO
+$lab    TXon       $reg, $cond
+$lab
+        LDR$cond.B $reg, IRQMaskB
+        ORR$cond   $reg, $reg, #KARTTxBit
+        STR$cond.B $reg, IRQMaskB
+        MEND
+
+        MACRO
+$lab    TXoff      $reg, $cond
+$lab
+        LDR$cond.B $reg, IRQMaskB
+        BIC$cond   $reg, $reg, #KARTTxBit
+        STR$cond.B $reg, IRQMaskB
+        MEND
+
+        GBLS    irqregs
+irqregs SETS    """R4-R10, PC"""
+
+; *****************************************************************************
+;
+;       Start of code
+
+ArthurKeyDriver
+
+; *****************************************************************************
+;
+;       Initialise driver.
+;
+A1KeyInit
+; Initialise the baud rate generator
+
+        MOV     R12, #IOC
+        MOV     R0, #1
+        STRB    R0, Timer3Low
+        MOV     R0, #0
+        STRB    R0, Timer3High
+        STRB    R0, Timer3Go
+
+        STRB    R0, KARTTx              ; Write dummy byte
+
+        MOV     R0, #&800
+10
+        SUBS    R0, R0, #1              ; copy Jon's loop
+        BNE     %BT10
+
+        LDRB    R0, KARTRx              ; Read dummy byte
+
+ [ AssemblePointerV
+        MOV     r0, #PointerV
+        ADR     r1, A1Pointer
+        Push    lr
+        SWI     OS_Claim
+        Pull    lr
+ ]
+
+ [ AssembleKEYV
+        MOV     r0, #KEYV
+        ADR     r1, A1KeyVec
+        MOV     r2, r12
+        Push    lr
+        SWI     OS_Claim
+        Pull    pc
+ |
+        MOV     pc, lr
+ ]
+
+; *****************************************************************************
+;
+;       Reset keyboard hardware.
+;
+A1Reset
+        MOV     R0, #HRDRESET
+        STRB    R0, ResetState
+
+        ASSERT  HRDRESET = &FF
+        STRB    R0, KeyRow              ; no key being received
+        STRB    R0, Reply
+        STRB    R0, LastKbId
+        STRB    R0, KbIdHalf
+        STRB    R0, RequestLED
+
+        MOV     R0, #K1rqid
+        STRB    R0, RequestKbId
+
+        MOV     R0, #0
+        STRB    R0, JustGotKbId
+        STRB    R0, MouseCount          ; start with an X coordinate
+        STRB    R0, SPDRec
+        STRB    R0, RequestSPD
+        STRB    R0, RequestMouse
+ [ AssemblePointerV
+        STR     r0, MouseXCount
+        STR     r0, MouseYCount
+ ]
+
+        TXonRXoff       R0              ; enable Tx IRQ, disable Rx IRQ
+
+        MOV     pc, lr
+
+; *****************************************************************************
+;
+;       Handle enable and update LEDs (r1=state (bit 2=scroll, 1=num, 0=ctrl)).
+;
+;       In:     r0 = reason code 3 or 4
+;               r1 = state (bit 2=scroll lock, 1=num lock, 0=caps lock) if r0=3
+;               r12 = IOC
+;
+A1KeyVec
+        TEQ     r0, #3                  ; if not set LED
+        TEQNE   r0, #4                  ;     and not enable then
+        MOVNE   pc, lr                  ;   pass it on
+
+        Push    "r0,r1,r11,lr"
+
+        MOV     r11, #KeyWorkSpace
+
+        TEQ     r0, #4                  ; if enable then
+        MOVEQ   r0, #0
+        STREQB  r0, JustGotKbId         ;   clear flag
+        RXon    r0, EQ                  ;   enable RX IRQs
+        Pull    "r0,r1,r11,pc",EQ       ;   and pass on call
+
+        LDRB    r0, KbId
+        CMP     r0, #1                  ; if id >= 1 then new (C=1)
+        BCS     %FT10
+
+        TST     r1, #1
+        MOVEQ   r1, #LEDOFF
+        MOVNE   r1, #LEDON
+10
+        STRB    r1, RequestLED
+        TXon    r0
+
+        Pull    "r0,r1,r11,pc"
+
+ [ AssemblePointerV
+
+; *****************************************************************************
+;
+;       A1Pointer - Return mouse movements on PointerV poll.
+;
+;       In:     r0 = reason code 0
+;               r1 = pointer device type (must be 0 for us)
+;       Out:    r2 = signed 32-bit X movement
+;               r3 = signed 32-bit Y movement
+;
+A1Pointer
+        TEQ     r0, #0                  ; If not poll
+        TEQEQ   r1, #0                  ;     or not type 0 then
+        MOVNE   pc, lr                  ;   pass on call.
+
+        LDR     r2, MouseXCount
+        STR     r0, MouseXCount
+        LDR     r3, MouseYCount
+        STR     r0, MouseYCount
+
+        Pull    pc                      ; Claim call.
+ ]
+
+; *****************************************************************************
+;
+;       IRQ routine
+;
+; in:   R2 = IOC request B flags
+;       R0-R3, R11, R12 already saved, R14 irrelevant
+
+
+        [ AssemblingArthur
+IrqRx   ROUT
+        Push    "R4-R10, R14"           ; stack regs if new MOS IRQ vector
+        |
+KeyIrq  ROUT
+        TST     R2, #KARTTxBit          ; transmit empty ?
+        BNE     IrqTx
+        Push    "R4-R10"
+        MOV     R12, #IOC               ; already set up in new IRQ scheme
+        ]
+        MOV     R11, #KeyWorkSpace
+
+; Keyboard receive interrupt
+
+; We now have to wait around for a period of 16 microseconds (or so they say)
+; because of the hardware design.
+
+; This is doubly annoying because I have no idea what speed this processor is
+; going at, so I don't know how many S-cycles this is, and there aren't enough
+; hardware timers around to waste one doing this thankless task.
+
+; In addition, because I am on the IRQ vector, the other IRQ users have
+; probably wasted at least 16 microseconds anyway - at least the code seems
+; to work perfectly well without this delay loop.
+
+; Nevertheless, until we can come up with a better solution, I shall do a
+; delay of (about) 16*8 S-cycles.
+;
+
+        MOV     R0, #16*8/5             ; delay for an unspecified period
+IrqRxDelayLoop
+        SUBS    R0, R0, #1              ; this breaks my heart,
+        BNE     IrqRxDelayLoop          ; it really does !
+
+        LDRB    R0, KARTRx              ; get data byte
+        LDRB    R1, ResetState          ; and what we sent last
+
+        CMP     R0, #K1rak2             ; is it a reset thingy ?
+        BCS     ProcessReset            ; [yes, so check it]
+
+        CMP     R1, #K1rak2             ; are we resetting anyway ?
+        BCS     IrqBadRx                ; if so then bad reset
+
+        AND     R2, R0, #&F0            ; get reason code
+
+        LDRB    R1, LastKbId            ; get last keyboard ID
+        TEQ     R1, #&FF                ; is it valid yet ?
+        BNE     ValidKbId               ; [yes, so we know what to expect]
+
+        TEQ     R2, #IDTYPE             ; is it old keyboard id
+        BEQ     IsOldKeyboard           ; [is old keyboard]
+
+        BIC     R2, R2, #K1kbidmask     ; check for new keyboard id
+        TEQ     R2, #K1kbid
+        BNE     IrqBadRx                ; not a keyboard id, so reset
+
+        AND     R1, R0, #K1kbidmask     ; get relevant bits
+ [ AssembleA1KeyHandler
+        ADRL    R0, NewKeyStruct
+ ]
+        B       AcknowledgeId
+
+IsOldKeyboard
+        AND     R0, R0, #&0F            ; get ID part
+        LDRB    R1, KbIdHalf
+        TST     R1, #&80
+        STRNEB  R0, KbIdHalf            ; got half of keyboard id
+        MOVNE   R0, #K1nack
+        BNE     IrqRxAck
+
+        ORR     R1, R1, R0, LSL #4      ; get full keyboard id
+        MOV     R0, #&FF
+        STRB    R0, KbIdHalf
+ [ AssembleA1KeyHandler
+        ADRL    R0, OldKeyStruct
+ ]
+
+AcknowledgeId
+ [ AssembleA1KeyHandler
+        LDRB    R8, LastKbId            ; get last keyboard id
+        TEQ     R8, R1                  ; is it same
+        STRNE   R0, KeyVec              ; if different, set up our handler
+ ]
+        MOV     R0, #&FF
+        STRB    R0, JustGotKbId         ; tell TX handler to disable interrupts until KEYV enable call
+
+ [ AssembleKEYV
+        MOV     r0, #0
+        MOV     r10, #KEYV
+        BL      CallVector
+ |
+        BL      GotKbId
+ ]
+
+        B       IrqRxAckScan            ; send ack
+
+; R1 = keyboard ID - now dispatch code
+
+ValidKbId
+        TEQ     R1, #0
+        BNE     NewKeyboardDispatch
+
+OldKeyboardDispatch
+        TST     R0, #MMOVED             ; is it mouse data ?
+        BNE     ProcessOldMouseData
+        TEQ     R2, #KEYDOWN            ; is it key down ?
+        BEQ     ProcessOldKeyDown
+        TEQ     R2, #KEYUP              ; is it key up ?
+        BEQ     ProcessOldKeyUp
+        TEQ     R2, #SPDDONE            ; is it SPD data ?
+        BEQ     ProcessOldSPDData
+        B       IrqBadRx                ; spurious
+
+NewKeyboardDispatch
+        TST     R2, #K1notmousedata     ; is it mouse data ?
+        BEQ     ProcessNewMouseData
+        TEQ     R2, #K1kdda             ; is it key down ?
+        BEQ     ProcessNewKeyDown
+        TEQ     R2, #K1kuda             ; is it key up ?
+        BEQ     ProcessNewKeyUp
+        TEQ     R2, #K1pdat             ; is it SPD data ?
+        BEQ     ProcessNewSPDData
+        B       IrqBadRx                ; spurious
+
+
+; *****************************************************************************
+;
+;       ProcessReset - Process reset code from keyboard
+;
+; in:   R0 = code from keyboard
+;       R1 = ResetState
+
+ProcessReset ROUT
+
+; Check sequencing
+
+        TEQ     R1, R0                  ; is reply what was expected
+        BNE     IrqBadRx                ; no, so reset
+
+; Now continue the sequence
+
+        TEQ     R1, #K1rak2             ; end of sequence ?
+        MOVEQ   R1, #K1nack             ; then send a nack
+        SUBNE   R1, R1, #1              ; else next thing to go
+        STRB    R1, ResetState          ; store back
+
+        TXonRXoff R0
+
+        Pull    $irqregs
+
+IrqBadRx
+
+; Restart the reset sequence
+
+        BL      A1Reset
+        Pull    $irqregs
+
+; *****************************************************************************
+
+ProcessOldSPDData
+ProcessNewSPDData
+        LDRB    R1, SPDRec
+        SUBS    R1, R1, #1
+        STRCSB  R1, SPDRec                      ; dec number to go (if not 0)
+
+        LDRCS   R1, SPDoutput
+        MOVCS   R1, R1, LSR #4
+        ORRCS   R1, R1, R0, LSL #28             ; put in new data
+        STRCS   R1, SPDoutput
+
+        B       IrqRxAckScan
+
+; *****************************************************************************
+
+ProcessOldMouseData                     ; R0 = 01xx xxxx
+        TST     R0, #&20                ; get sign bit of data (bit 5)
+        BICEQ   R0, R0, #&40            ; move to bit 6 (where it is on new)
+ProcessNewMouseData
+        LDRB    R1, MouseCount
+        ADR     R2, MouseDelta
+        STRB    R0, [R2, R1]            ; no need to clear top bit
+
+        EORS    R1, R1, #1              ; move to other coordinate
+        STRB    R1, MouseCount
+
+        MOVNE   R0, #K1back
+        BNE     IrqRxAck
+
+        LDRB    R3, [R2, #1]            ; get delta Y
+        MOV     R3, R3, LSL #25         ; sign extend it
+        MOV     R3, R3, ASR #25
+
+        LDRB    R2, [R2]                ; get delta X
+        MOV     R2, R2, LSL #25         ; sign extend it
+        MOV     R2, R2, ASR #25
+
+ [ AssemblePointerV
+        LDR     r0, MouseXCount
+        ADD     r0, r0, r2
+        STR     r0, MouseXCount
+        LDR     r0, MouseYCount
+        ADD     r0, r0, r3
+        STR     r0, MouseYCount
+ |
+        BL      ProcessMouseXY
+ ]
+
+        B       IrqRxAckScan
+
+; *****************************************************************************
+
+; in:   R1 = keyboard id
+
+ProcessOldKeyDown
+ProcessNewKeyDown ROUT
+        LDRB    R2, KeyRow
+        TEQ     R2, #&FF                ; have we had a row already ?
+        STREQB  R0, KeyRow              ; no so store row
+        MOVEQ   R0, #K1back
+        BEQ     IrqRxAck                ; and acknowledge Rx
+
+        EOR     R3, R0, R2              ; test if save movement type
+        TST     R3, #&F0
+        BNE     IrqBadRx                ; not same, so reset
+
+        AND     R0, R0, #&0F            ; get new data
+        AND     R2, R2, #&0F            ; and row data
+
+        TEQ     R1, #0
+        ORREQ   R2, R2, R0, LSL #4      ; old keyboard number
+        ORRNE   R2, R0, R2, LSL #4      ; new key number
+
+        MOV     R0, #&FF
+        STRB    R0, KeyRow              ; reset 'had row' flag
+
+        MOV     r0, #2                  ; indicate key down
+        MOV     r1, r2
+ [ AssembleKEYV
+        MOV     r10, #KEYV
+        BL      CallVector
+        MOV     r12, #IOC
+ |
+        BL      GotKey
+ ]
+
+IrqRxAckScan
+
+; Re-enable Tx interrupts and queue an acknowledge
+
+        MOV     R0, #K1ack              ; either sack or smak as appropriate
+IrqRxAck
+        STRB    R0, Reply
+        TXonRXoff       R0
+        Pull    $irqregs                ; claimed irq, so grab link and PC
+
+; *****************************************************************************
+
+; in:   R1 = keyboard id
+
+ProcessOldKeyUp
+ProcessNewKeyUp ROUT
+        LDRB    R2, KeyRow
+        TEQ     R2, #&FF                ; have we had a row already ?
+        STREQB  R0, KeyRow              ; no so store row
+        MOVEQ   R0, #K1back
+        BEQ     IrqRxAck                ; and acknowledge Rx
+
+        EOR     R3, R0, R2              ; test if save movement type
+        TST     R3, #&F0
+        BNE     IrqBadRx                ; not same, so reset
+
+        AND     R0, R0, #&0F            ; get new data
+        AND     R2, R2, #&0F            ; and row data
+
+        TEQ     R1, #0
+        ORREQ   R2, R2, R0, LSL #4      ; old key number
+        ORRNE   R2, R0, R2, LSL #4      ; new key number
+
+        MOV     R0, #&FF
+        STRB    R0, KeyRow              ; reset 'had row' flag
+
+        MOV     r0, #1                  ; indicate key up
+        MOV     r1, r2
+ [ AssembleKEYV
+        MOV     r10, #KEYV
+        BL      CallVector
+        MOV     r12, #IOC
+ |
+        BL      GotKey
+ ]
+
+        B       IrqRxAckScan
+
+; *****************************************************************************
+
+IrqTx   ROUT
+        [ AssemblingArthur
+        Push    "R4-R10, R14"           ; stack regs if new MOS IRQ vector
+        |
+        Push    "R4-R10"
+        MOV     R12, #IOC               ; already set up in new IRQ scheme
+        ]
+        MOV     R11, #KeyWorkSpace
+
+; First see if we're in a reset sequence
+
+        LDRB    R0, ResetState          ; are we in a reset ?
+        TEQ     R0, #0
+        BEQ     %FT05                   ; not in a reset
+
+        CMP     R0, #K1rak2             ; are we sending the reset nack ?
+        BCS     %FT25                   ; no, just send reset code
+        MOV     R1, #0                  ; yes, zero the reset state
+        STRB    R1, ResetState
+        STRB    R0, KARTTx
+        Pull    $irqregs                ; don't disable TX
+
+; Now see if any outstanding requests
+
+05
+        LDRB    R0, RequestSPD          ; is there an SPD request ?
+        TEQ     R0, #0
+        BEQ     %FT10                   ; [no SPD request]
+
+        MOV     R1, #K1prst             ; code to send keyboard
+        MOV     R2, #0                  ; no further SPD request
+        STRB    R2, RequestSPD
+        MOV     R2, #8
+        STRB    R2, SPDRec              ; nibbles still to be sent/received
+        STRB    R1, KARTTx              ; send the byte
+        Pull    $irqregs                ; exit without disabling Tx
+
+10
+        LDRB    R0, RequestKbId         ; is there a pending keyboard request ?
+        TEQ     R0, #0
+        MOVNE   R1, #0
+        STRNEB  R1, RequestKbId         ; no further request
+        STRNEB  R0, KARTTx              ; send the byte
+        Pull    $irqregs, NE            ; exit without disabling Tx
+
+        LDRB    R0, RequestMouse        ; is there a pending mouse request ?
+        TEQ     R0, #0
+        MOVNE   R1, #0
+        STRNEB  R1, RequestMouse        ; no further request
+        STRNEB  R0, KARTTx
+        Pull    $irqregs, NE            ; exit without disabling Tx
+
+        LDRB    R0, RequestLED          ; is there a pending LED request ?
+        TEQ     R0, #&FF
+        MOVNE   R1, #&FF
+        STRNEB  R1, RequestLED
+        STRNEB  R0, KARTTx
+        Pull    $irqregs, NE            ; exit without disabling Tx
+
+        LDRB    R0, SPDRec              ; are we converting some SPD data
+        TEQ     R0, #0
+        BEQ     %FT20                   ; branch if not
+
+        LDR     R1, SPDinput
+        AND     R2, R1, #&F             ; get nybble to be sent
+        ORR     R2, R2, #K1rqpd
+        MOV     R1, R1, LSR #4          ; shift out the nybble sent
+        STR     R1, SPDinput
+        STRB    R2, KARTTx
+        B       %FT30                   ; disable Tx, so we don't send another
+                                        ; nibble before we get the conversion
+20
+        LDRB    R0, Reply
+        TEQ     R0, #&FF
+        BEQ     %FT30                   ; no reply to send
+25
+        STRB    R0, KARTTx              ; send the reply
+        MOV     R0, #&FF
+        STRB    R0, Reply               ; nothing else to send
+
+        LDRB    R0, JustGotKbId
+        TEQ     R0, #0                  ; if just got keyboard id then
+        TXoff   R0, NE                  ;   disable all interrupts
+        Pull    $irqregs, NE            ;   and wait for KEYV enable call
+30
+        RXonTXoff R0
+        Pull    $irqregs
+
+        END
diff --git a/s/PMF/RS423 b/s/PMF/RS423
new file mode 100644
index 0000000000000000000000000000000000000000..81fb7f238cf936bb79e82fd625f69267c5f1a77b
--- /dev/null
+++ b/s/PMF/RS423
@@ -0,0 +1,714 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.PMF.rs423
+
+; ************************************************************
+; ***    C h a n g e   L i s t  (better late than never!)  ***
+; ************************************************************
+
+; Date       Description
+; ----       -----------
+; 15-Feb-88  Changed ModifyControl6850 to fix GTE/CMD control register
+;             sensitivity problem
+; 08-Apr-88  Transmit code doesn't need to reload DCDDSRCopy, it's got the
+;             register in R2
+; 12-Apr-88  Removed DCDDSRCopy - bits are now in SerialFlags
+;            Started code to do SerialFlags bits and SWI SerialOp
+; 18-Apr-88  Started XON/XOFF stuff
+; 13-May-88  Added new SerialOps to read/set baud rates
+
+; *****************************************************************************
+;
+;       RS423IRQ - Entry point for 6551 interrupt
+;
+
+RS423IRQ ROUT
+        [ AssemblingArthur
+        Push    R14                     ; stack R14 if new MOS IRQ vector
+        |
+        BYTEWS  WsPtr                   ; already set up in new IRQ scheme
+        ]
+        LDR     R11, =ACIA
+        LDRB    R2, ACIAStatus          ; clear interrupt and read status
+
+; if we want to support 6850 IRQ mask variable, then some code needed here
+; to mess around with the bits
+
+        TST     R2, #ACIATDRE           ; if TDRE bit set then push status
+        ADRNE   R0, %FT20               ; and address of TX processing routine
+        Push    "R0,R2", NE
+
+        TST     R2, #ACIARDRF           ; if RDRF bit set then push status
+        ADRNE   R0, %FT10               ; and address of RX processing routine
+        Push    "R0,R2", NE
+
+        LDR     R1, SerialFlags                 ; get old DCD,DSR bits
+        EOR     R0, R1, R2, LSL #DCDDSRShift    ; difference between old/new
+        ANDS    R0, R0, #(1:SHL:SF_DCDHigh):OR:(1:SHL:SF_DSRHigh)
+        Pull    PC, EQ                          ; [same, so return]
+
+        EOR     R1, R1, R0                      ; R1 := new
+        STR     R1, SerialFlags                 ; store new version
+        TST     R1, #(1:SHL:SF_DCDIgnore)       ; if ignoring DCD
+        BNE     %FT04                           ; then don't generate event
+        TST     R1, #(1:SHL:SF_DCDHigh)         ; if DCD is now non-zero
+        BNE     RS423Error                      ; then generate event
+04
+        TST     R1, #(1:SHL:SF_DSRIgnore)       ; if ignoring DSR
+        TSTEQ   R1, #(1:SHL:SF_DSRHigh)         ; or it's still high
+        Pull    PC, NE                          ; then no action
+
+        TST     R0, #(1:SHL:SF_DSRHigh)         ; else if DSR has changed
+        BLNE    RSBUSY                          ; then it's changed to zero,
+                                                ; so reenable TX IRQ if poss
+        Pull    PC                              ; then finish IRQ
+
+; Process receive interrupt
+
+10
+        Pull    R2                              ; pull stacked status
+        LDRB    R3, RS423conflag                ; RXirq enabled ?
+        TST     R3, #RXEN6850
+        Pull    PC, EQ                          ; no, then exit
+
+        LDRB    R0, ACIARxData                  ; read data (and clear RDRF)
+        LDR     R1, SerialFlags
+        MOV     R3, #(ACIAOverRun :OR: ACIAFramingError :OR: ACIAParityError)
+        TST     R1, #(1:SHL:SF_DCDIgnore)       ; if not ignoring DCD
+        ORREQ   R3, R3, #ACIADCD                ; then it causes event
+        TST     R2, R3
+        BNE     RS423Error
+
+; no error on receive
+
+        TST     R1, #(1:SHL:SF_XONXOFFOn)       ; if not doing xon/xoff
+        BEQ     %FT15                           ; then skip
+        TEQ     R0, #XOFFChar                   ; else if XOFF char
+        ORREQ   R1, R1, #(1:SHL:SF_HeXOFFedMe)  ; then set bit
+        STREQ   R1, SerialFlags                 ; store back
+        Pull    PC, EQ                          ; and exit
+
+        TEQ     R0, #XONChar                    ; else if XON char
+        BICEQ   R1, R1, #(1:SHL:SF_HeXOFFedMe)  ; then clear bit
+        STREQ   R1, SerialFlags                 ; store back
+        BEQ     EnableTXI                       ; and reenable TXI
+
+15
+        LDRB    R2, RS423InputSupr              ; RS423 input suppressed ?
+        TEQ     R2, #0                          ; NZ => suppressed
+        Pull    PC, NE                          ; return if suppressed
+
+        MOV     R1, #Buff_RS423In               ; RS423 input buffer id
+        MOV     R2, R0                          ; R2 := character
+        BL      DoInsertESC                     ; insert into buffer
+                                                ; possibly checking for ESC
+        BL      CountRS                         ; count spaces left
+
+        Pull    PC, CS                          ; enough left, so exit
+        B       RLO                             ; this pulls PC
+
+; Process transmit interrupt
+
+20
+        Pull    R2                              ; restore stacked status
+        LDR     R1, SerialFlags
+        TST     R1, #(1:SHL:SF_DSRIgnore)       ; if ignoring DSR
+        BNE     %FT25                           ; then skip
+        TST     R2, #ACIADSR                    ; else if DSR high
+        BNE     TXDINT                          ; then don't send char
+25
+        LDRB    R0, XONXOFFChar                 ; is there an XON or XOFF
+        TEQ     R0, #0                          ; to send (R0 = 0 if not)
+        MOVNE   R1, #0                          ; If so, then zero character
+        STRNEB  R1, XONXOFFChar
+        BNE     %FT40                           ; and send this char
+
+        TST     R1, #(1:SHL:SF_HeXOFFedMe)      ; if we are XOFFed
+        BNE     TXDINT                          ; then shut up
+28
+        CLRV
+        MOV     R1, #Buff_RS423Out              ; RS423 output buffer id
+        BL      REMOVE
+        BCC     %FT30                           ; character to send
+
+        LDRB    R0, PrinterDrivType
+        TEQ     R0, #2                          ; RS423 printer ?
+        BNE     TXDINT                          ; no, then shut down RS423
+
+        CLRV
+        MOV     R1, #Buff_Print                 ; printer buffer id
+        BL      REMOVE
+
+        MOVCS   R1, #0                          ; if no char, mark
+        STRCS   R1, PrinterActive               ; printer driver dormant
+        BCS     TXDINT                          ; and shut down RS423
+30
+        LDR     R1, SerialFlags
+        TST     R1, #(1:SHL:SF_XONXOFFOn)       ; if xon/xoff not on
+        BEQ     %FT40                           ; then skip
+        TEQ     R0, #XOFFChar                   ; else if XOFF char
+        ORREQ   R1, R1, #(1:SHL:SF_UserXOFFedHim) ; then set user bit
+        TEQ     R0, #XONChar                    ; else if XON char
+        BICEQ   R1, R1, #(1:SHL:SF_UserXOFFedHim) ; then clear user bit
+        STR     R1, SerialFlags                 ; store back always
+        BNE     %FT40                           ; and skip if not XON char
+
+; user sending an XON char, so only let it thru if we're not trying to XOFF him
+
+        TST     R1, #(1:SHL:SF_IXOFFedHim)      ; if we're trying to XOFF him
+        BNE     %BT28                           ; then don't send XON, go and
+                                                ; look for another character
+40
+        STRB    R0, ACIATxData                  ; write data
+        Pull    PC
+
+
+TXDINT                                          ; disable TX interrupt
+        MOV     R0, #&80                        ; indicate RS423
+        STRB    R0, RS423use                    ; not in use
+
+        LDR     R0, SerialFlags
+        TST     R0, #(1:SHL:SF_XONXOFFOn)       ; if XON/XOFF
+        BNE     DisableTXI                      ; then just disable TXI
+
+        LDRB    R0, RS423conflag
+        TST     R0, #RXEN6850
+        BEQ     RLO                             ; RXI disabled, so shut him up
+
+        BL      CountRS
+        BCS     DisableTXI                      ; no overflow, just disable TXI
+RLO
+        LDR     R1, SerialFlags
+        TST     R1, #(1:SHL:SF_XONXOFFOn)       ; if XON/XOFF
+        ORRNE   R1, R1, #(1:SHL:SF_IXOFFedHim)
+        STRNE   R1, SerialFlags
+        MOVNE   R1, #XOFFChar
+        BNE     SendXONXOFF
+
+        MOV     R1, #RLOTXD6850                 ; tell him to shut up
+        B       RSED
+
+SendXONXOFF
+        STRB    R1, XONXOFFChar
+EnableTXI
+        MOV     R1, #RHITXE6850
+        B       RSED
+
+DisableTXI
+        MOV     R1, #RHITXD6850                 ; just disable transmit irq
+RSED
+        MOV     R2, #&9F                        ; alter bits 5,6 of control
+        Pull    R14
+
+; and drop thru to ...
+
+; *****************************************************************************
+;
+;       ModifyControl6850 - Simulate update of 6850 control register
+;
+;       Changed 15-Feb-88 to only modify control register if different
+;        (GTE/CMD chips corrupt received characters if control register is
+;        written to (unnecessarily) just after a character has been received)
+;       Change uses R4 as a temporary, which is therefore pushed + pulled
+;        in addition to R1 and R2, in the middle of the routine.
+;
+; in:   WsPtr -> OsbyteVars
+;       R2 = AND mask
+;       R1 = EOR mask
+;
+; out:  R1 = old value of register (taken from soft copy)
+;       R0, R2, R3 preserved
+;
+
+ModifyControl6850 ROUT
+        Push    "R0, R3, R4, R11, R14"
+
+        PHPSEI                                  ; doesn't really "push" it
+        LDR     R11, =ACIA
+
+        LDRB    R3, RS423conflag                ; load soft copy
+        AND     R0, R2, R3                      ; AND with mask
+        EOR     R0, R0, R1                      ; EOR with mask
+        STRB    R0, RS423conflag                ; store back to soft copy
+        MOV     R4, R1                          ; save EOR mask in R4
+        MOV     R1, R3                          ; old copy into R1
+
+; now R0 = new register, R1 and R2 must be preserved
+
+        AND     R3, R0, #DIVBITS6850
+        TEQ     R3, #DIVRESET6850               ; reset ACIA ?
+        STREQB  R0, ACIAReset                   ; any old data will do
+        BEQ     %FT20                           ; and finish there
+
+        Push    "R1, R2"
+        MVN     R2, R2                          ; NOT (AND mask)
+        ORR     R2, R2, R4                      ; (EOR mask) OR NOT (AND mask)
+        TST     R2, #WSB6850                    ; if zero then not modifying
+        LDREQB  R1, ACIACommand                 ; format bits, so fake new bits
+        ANDEQ   R1, R1, #(ACIAPME :OR: ACIAPMC0 :OR: ACIAPMC1) ; in com. reg
+        BEQ     %FT10                           ; and leave control reg alone
+
+        AND     R3, R0, #WSB6850                ; get word select bits
+
+        ADR     R1, ControlDPS                  ; point to table
+        LDRB    R1, [R1, R3, LSR #WSBSHIFT]     ; get bits for control reg
+
+        LDRB    R2, ACIAControl                 ; get current register
+        BIC     R4, R2, #ACIAWSB                ; zero those bits in current
+        ORR     R4, R4, R1                      ; or in new bits
+        TEQ     R4, R2                          ; if different
+        STRNEB  R4, ACIAControl                 ; then store back
+
+        ADR     R1, CommandDPS
+        LDRB    R1, [R1, R3, LSR #WSBSHIFT]     ; get bits for command reg
+                                                ; ABC00000
+10
+        AND     R0, R0, #RXEN6850 :OR: RTBITS6850 ; isolate RXI,RTS/TXI
+        ORR     R1, R1, R0, LSR #3              ; ABCgeF00
+        TST     R0, #RXEN6850
+        EORNE   R1, R1, #(&10 :OR: ACIARIRD)    ; ABC0eFg0
+        TST     R1, #&04
+        EOREQ   R1, R1, #&08                    ; ABC0EFg0
+        EOR     R1, R1, #ACIARIRD               ; ABC0EFG0
+        LDRB    R3, ACIACommand
+        AND     R3, R3, #(ACIAREM :OR: ACIADTR :OR: ACIATIC0 :OR: ACIATIC1)
+
+        LDR     R4, SerialFlags
+        TST     R4, #(1:SHL:SF_SendingBreak)    ; if sending break
+        BICNE   R1, R1, #(ACIATIC0 :OR: ACIATIC1) ; then use hardware bits
+        BICEQ   R3, R3, #(ACIATIC0 :OR: ACIATIC1) ; else use software bits
+
+        ORR     R1, R1, R3
+        STRB    R1, ACIACommand
+        Pull    "R1, R2"
+20
+        PLP
+        Pull    "R0, R3, R4, R11, PC"
+
+; *****************************************************************************
+;
+; Comes here either a) when DCD or DSR have changed to non-zero,
+; or b) when a receive interrupt happens and error bits are set
+;
+; Converts 6551 status register in R2 into
+; 6850 status register shifted right one bit in R1 (DSR in bit 2)
+; then calls event (with char in R2 for case b) above)
+;
+
+RS423Error ROUT
+
+; R2 = x | DSR | DCD | x | x | OV | FE | PE
+
+        MOVS    R1, R2, LSR #1          ; bit 1=OV, bit 0=FE, C=PE
+        MOV     R1, R1, LSL #3          ; bit 4=OV, bit 3=FE
+        AND     R1, R1, #(1:SHL:4) :OR: (1:SHL:3) ; remove other bits
+        ORRCS   R1, R1, #(1:SHL:5)      ; bit 5=PE, bit 4=OV, bit 3=FE
+        AND     R2, R2, #(1:SHL:6) :OR: (1:SHL:5) ; R2 bit 6=DSR, bit 5=DCD
+        ORR     R1, R1, R2, LSR #4      ; 0|0|PE|OV|FE|DSR|DCD|0
+
+        MOV     R2, R0                  ; char in R2 (if receive)
+        MOV     R0, #Event_RS423Error
+        BL      OSEVEN
+
+        Pull    PC
+
+
+; *****************************************************************************
+
+; Tables for converting Data/Parity/Stop bits in 6850
+; to control and command registers in 6551
+
+ControlDPS
+        =       ControlBits7e2, ControlBits7o2, ControlBits7e1, ControlBits7o1
+        =       ControlBits8n2, ControlBits8n1, ControlBits8e1, ControlBits8o1
+
+CommandDPS
+        =       CommandBits7e2, CommandBits7o2, CommandBits7e1, CommandBits7o1
+        =       CommandBits8n2, CommandBits8n1, CommandBits8e1, CommandBits8o1
+
+        ALIGN
+
+; *****************************************************************************
+;
+;       CountRS - See if there are "enough" spaces left in RS423 buffer
+;
+; in:   WsPtr -> OsbyteVars
+;
+; out:  C=0 => spaces < limit
+;       C=1 => spaces >= limit
+;
+
+CountRS ROUT
+        Push    R14
+        MOV     R1, #Buff_RS423In       ; RS423 input buffer id
+        CMP     R1, #0                  ; V=0, C=1 so count spaces
+        BL      CnpEntry
+        ORR     R1, R1, R2, LSL #8      ; get combined result
+        LDRB    R2, RS423HandShake      ; limiting number of spaces
+        CMP     R1, R2                  ; C=1 => (spaces >= limit)
+        Pull    PC
+
+; *****************************************************************************
+;
+;       SerialOp - Handler for SWI OS_SerialOp
+;
+
+SerialOp ROUT
+        TEQP    PC, #SVC_mode :OR: I_bit        ; disable IRQs
+        LDR     R11, =ACIA
+        BYTEWS  R12
+        CMP     R0, #(SerialOpTableEnd-SerialOpTable) :SHR: 2
+        BCS     BadSerialOp
+        LDR     R10, [PC, R0, LSL #2]           ; SerialOpTable must follow
+        ADD     PC, PC, R10                     ; immediately
+
+SerialOpTable
+        &       ReadWriteFlags-SerialOpTable-4
+        &       ReadWriteDataFormat-SerialOpTable-4
+        &       SendBreak-SerialOpTable-4
+        &       SendByte-SerialOpTable-4
+        &       GetByte-SerialOpTable-4
+        &       SetRXBaudRate-SerialOpTable-4
+        &       SetTXBaudRate-SerialOpTable-4
+SerialOpTableEnd
+
+BadSerialOp
+        ADR     R0, ErrorBlock_UnknownSerialOp
+        ORR     R14, R14, #V_bit
+        ExitSWIHandler
+
+        MakeErrorBlock UnknownSerialOp
+
+; *****************************************************************************
+;
+;       ReadWriteFlags - Read/write serial flags
+;
+; in:   R1 = EOR mask
+;       R2 = AND mask
+;       R11 -> ACIA
+;       R12 -> BYTEWS
+;
+; out:  R1 = old flags
+;       R2 = new flags
+;
+; Flags are defined in $.Hdr.RS423
+; Still to do - if setting DSRIgnore then possibly wake up TX
+;
+
+ReadWriteFlags ROUT
+        Push    "R9, R14"
+        MVN     R10, #SF_ModifyBits     ; R10 = bits that aren't modifyable
+        BIC     R1, R1, R10             ; EOR mask must have those bits clear
+        ORR     R2, R2, R10             ; AND mask must have those bits set
+
+        LDR     R10, SerialFlags
+
+        MOV     R14, #IOC               ; now read ring indicator bit
+        LDRB    R14, [R14, #IOCIRQSTAA]
+        TST     R14, #ring_bit
+        BICEQ   R10, R10, #1:SHL:SF_Ringing ; clear or set bit accordingly
+        ORRNE   R10, R10, #1:SHL:SF_Ringing
+
+        LDRB    R14, ACIACommand        ; now read DTR bit
+        TST     R14, #ACIADTR           ; NZ => on
+        BICNE   R10, R10, #1:SHL:SF_DTROff
+        ORREQ   R10, R10, #1:SHL:SF_DTROff
+
+; R10 is now the complete 'old' SerialFlags
+
+        AND     R9, R10, R2
+        EOR     R2, R9, R1
+        MOV     R1, R10
+
+; now reflect 'new' SerialFlags DTR setting in hardware
+
+        TST     R2, #1:SHL:SF_DTROff
+        BICNE   R14, R14, #ACIADTR      ; R14 is new command register setting
+        ORREQ   R14, R14, #ACIADTR
+        STRB    R14, ACIACommand        ; (R11 still points to ACIA)
+
+; if DSRIgnore has just been turned on and DSR is high then reenable TX
+
+        EOR     R10, R1, R2             ; form mask of bits which have changed
+        AND     R10, R10, R2            ; from 0 to 1
+
+        TST     R10, #1:SHL:SF_XONXOFFOn ; if we just enabled xon/xoff
+        BICNE   R2, R2, #(1:SHL:SF_HeXOFFedMe):OR:(1:SHL:SF_IXOFFedHim):OR:(1:SHL:SF_UserXOFFedHim)                     ; then clear all these bits
+        STR     R2, SerialFlags
+
+        TST     R10, #1:SHL:SF_DSRIgnore ; if DSR ignore has changed to high
+        TSTNE   R2, #1:SHL:SF_DSRHigh   ; and actual state is high
+        BLNE    DoRSBUSY                ; then reenable transmit
+
+        Pull    "R9, R14"
+        ExitSWIHandler
+
+DoRSBUSY ROUT
+        Push    "R0-R2,R14"
+        BL      RSBUSY
+        Pull    "R0-R2,PC"
+
+; *****************************************************************************
+;
+;       ReadWriteDataFormat - Read/write data format (word length, stop bits
+;        and parity)
+;
+; in:   R1 = -1 => just read format
+;       R1 <> -1 => set format to R1
+;
+;       Bits 0,1 : 0 => 8 bit word
+;                  1 => 7 bit word
+;                  2 => 6 bit word
+;                  3 => 5 bit word
+;
+;       Bit 2    : 0 => 1 stop bit
+;                  1 => 2 stop bits
+;               OR 1.5 stop bits if 5 bit word without parity
+;               OR 1   stop bit  if 8 bit word with parity
+;
+;       Bit 3    : 0 => parity disabled (no parity bit)
+;                  1 => parity enabled
+;
+;       Bits 4,5 : 0 => odd parity
+;                  1 => even parity
+;                  2 => parity bit always 1 on TX (ignored on RX)
+;                  3 => parity bit always 0 on TX (------""-----)
+;
+;       Thus bits 0..2 go to bits 5..7 of the control register, and
+;            bits 3..5 go to bits 5..7 of the command register.
+;
+;       R11 -> ACIA
+;       R12 -> BYTEWS
+;
+; out:  R1 = old format in bits 0..5 as above
+;
+
+ReadWriteDataFormat ROUT
+        Push    "R9, R14"
+
+        LDRB    R10, ACIAControl
+        MOV     R9, R10, LSR #5         ; R9 = control format bits in 0..2
+        EOR     R10, R10, R9, LSL #5    ; R10 = control non-format bits
+
+        LDRB    R14, ACIACommand
+        AND     R12, R14, #(ACIAPME :OR: ACIAPMC0 :OR: ACIAPMC1)
+        EOR     R14, R14, R12           ; R14 = command non-format bits
+
+        ORR     R9, R9, R12, LSR #5-3   ; R9 = complete old state
+
+        CMP     R1, #-1                 ; just reading ?
+        BEQ     %FT10
+
+; now update real registers
+
+        ORR     R10, R10, R1, LSL #5    ; new control register
+        STRB    R10, ACIAControl        ; (bits above 7 don't matter)
+
+        MOV     R10, R1, LSR #3
+        ORR     R14, R14, R10, LSL #5   ; new command register
+        STRB    R14, ACIACommand        ; (bits above 7 don't matter)
+
+; now work out if new format corresponds to a BBC format, and if so, update
+; 6850 control register copy
+
+        AND     R1, R1, #&3F            ; only use bits 0..5
+        ADR     R14, ReverseFormatTable
+        LDRB    R14, [R14, R1]
+        TEQ     R14, #&FF               ; if &FF then not BBC format
+        BEQ     %FT10                   ; so don't modify 6850 soft copy
+
+        BYTEWS  R12
+        LDRB    R10, RS423conflag
+        BIC     R10, R10, #WSB6850      ; clear existing word bits out
+        ORR     R10, R10, R14           ; OR in new bits
+        STRB    R10, RS423conflag       ; and store back
+10
+        MOV     R1, R9                  ; R1 = old state
+        Pull    "R9, R14"
+        ExitSWIHandler
+
+ReverseFormatTable
+        =       &14, &FF, &FF, &FF, &10, &FF, &FF, &FF
+        =       &1C, &0C, &FF, &FF, &1C, &04, &FF, &FF
+        =       &14, &FF, &FF, &FF, &10, &FF, &FF, &FF
+        =       &18, &08, &FF, &FF, &18, &00, &FF, &FF
+        =       &14, &FF, &FF, &FF, &10, &FF, &FF, &FF
+        =       &FF, &FF, &FF, &FF, &FF, &FF, &FF, &FF
+        =       &14, &FF, &FF, &FF, &10, &FF, &FF, &FF
+        =       &FF, &FF, &FF, &FF, &FF, &FF, &FF, &FF
+        ALIGN
+
+; *****************************************************************************
+;
+;       SendBreak - Send a break for R1 centiseconds
+;
+; in:   R1 = length of break in centiseconds
+;       R11 -> ACIA
+;       R12 -> BYTEWS
+;
+; out:  R1 preserved
+;
+
+SendBreak ROUT
+        Push    "R9, R14"
+        LDR     R10, SerialFlags        ; protect command register during break
+        ORR     R10, R10, #(1:SHL:SF_SendingBreak)
+        STR     R10, SerialFlags
+
+        LDRB    R10, ACIACommand
+;       BIC     R10, R10, #(ACIATIC0 :OR: ACIATIC1)
+        ASSERT  RonBreak = (ACIATIC0 :OR: ACIATIC1)
+        ORR     R10, R10, #RonBreak
+        STRB    R10, ACIACommand
+
+        TEQP    PC, #SVC_mode           ; enable IRQs
+
+        [ AssemblingArthur :LOR: Module
+        MOV     R14, #MetroGnome        ; use monotonic variable now
+        |
+        ADR     R14, RealTime           ; doesn't exist in my world
+        ]
+        LDR     R10, [R14]
+        ADD     R10, R10, R1            ; R10 = target time
+10
+        LDR     R9, [R14]
+        CMP     R9, R10
+        BMI     %BT10                   ; not BLT (these are clock numbers)
+
+        TEQP    PC, #SVC_mode :OR: I_bit ; disable IRQs again
+
+        LDR     R10, SerialFlags        ; deprotect command register
+        BIC     R10, R10, #(1:SHL:SF_SendingBreak)
+        STR     R10, SerialFlags
+
+        Push    "R1, R2"                ; now update 6551 command register
+        MOV     R1, #0                  ; from the 6850 control soft copy
+        MOV     R2, #&FF                ; (to put back the RTS setting)
+        BL      ModifyControl6850
+        Pull    "R1, R2, R9, R14"
+
+        ExitSWIHandler
+
+; *****************************************************************************
+;
+;       SendByte - Attempt to put a byte into transmit buffer, and wake up
+;        transmit process
+;
+; in:   R1 = character
+;       R11 -> ACIA
+;       R12 -> BYTEWS
+;
+; out:  R1 preserved
+;       C=0 => character inserted
+;       C=1 => buffer was full
+;
+
+SendByte ROUT
+        Push    "R0-R2, R14"
+        MOV     R0, R1                  ; put character in R0
+        MOV     R1, #Buff_RS423Out
+        BL      INSRT
+        Pull    "R0-R2, R14", CS        ; if failed then nowt else to do
+        ORRCS   R14, R14, #C_bit
+        ExitSWIHandler CS
+
+; character successfully inserted, so try to wake up transmit process
+; (R12 still -> ByteWS)
+
+        BL      RSBUSY
+        Pull    "R0-R2, R14"
+        BIC     R14, R14, #C_bit
+        ExitSWIHandler
+
+; *****************************************************************************
+;
+;       GetByte - Attempt to remove a byte from receive buffer, and reenable
+;        RTS if now enough space in buffer
+;
+; in:   R11 -> ACIA
+;       R12 -> BYTEWS
+;
+; out:  C=0 => R1 = character read
+;       C=1 => R1 preserved, buffer was empty
+;
+
+GetByte ROUT
+        Push    "R0-R2, R14"
+        MOV     R1, #Buff_RS423In
+        CLRV
+        BL      REMOVE                  ; out: R2 = char, if C=0
+        STRCC   R2, [R13, #1*4]         ; store char in stacked R1
+        LDR     R14, [R13, #3*4]        ; reload stacked R14
+        BICCC   R14, R14, #C_bit        ; set/clear carry appropriately
+        ORRCS   R14, R14, #C_bit
+        STR     R14, [R13, #3*4]        ; store back
+
+; (R12 -> BYTEWS still)
+
+        BLCC    RSETX                   ; if removed char, then reenable RTS
+                                        ; if enough spaces
+        Pull    "R0-R2, R14"
+        ExitSWIHandler
+
+        LTORG
+
+; *****************************************************************************
+;
+;       SetRXBaudRate - Set/read receive baud rate
+;
+; in:   R1 = -1 => read RX baud rate
+;       R1 = 0..15 => set RX baud rate to this
+
+SetRXBaudRate ROUT
+        Push    "R0,R2,R3, R14"
+        LDRB    R11, SerULAreg
+        AND     R11, R11, #&78          ; get RX bits
+        MOV     R11, R11, LSR #3
+        ADR     R10, ReverseSerProcTable
+        LDRB    R11, [R10, R11]
+        CMP     R1, #-1
+        BLNE    DoOsbyte07              ; if setting then call setting routine
+        MOV     R1, R11
+        Pull    "R0,R2,R3, R14"
+        ExitSWIHandler
+
+; *****************************************************************************
+;
+;       SetTXBaudRate - Set/read transmit baud rate
+;
+; in:   R1 = -1 => read TX baud rate
+;       R1 = 0..15 => set TX baud rate to this
+
+SetTXBaudRate ROUT
+        Push    "R0,R2,R3, R14"
+        LDRB    R11, SerULAreg
+        AND     R11, R11, #&87          ; get TX bits
+        TST     R11, #&80
+        EORNE   R11, R11, #&88          ; put bit 7 into bit 3
+        ADR     R10, ReverseSerProcTable
+        LDRB    R11, [R10, R11]
+        CMP     R1, #-1
+        BLNE    DoOsbyte08              ; if setting then call setting routine
+        MOV     R1, R11
+        Pull    "R0,R2,R3, R14"
+        ExitSWIHandler
+
+
+ReverseSerProcTable
+        =       8, 4, 6, 2, 7, 3, 5, 1
+        =       15, 11, 13, 9, 14, 10, 12, 16
+
+
+        END
diff --git a/s/PMF/convdate b/s/PMF/convdate
new file mode 100644
index 0000000000000000000000000000000000000000..8566c866e739ab735cad4430e6438c563e0bf2d4
--- /dev/null
+++ b/s/PMF/convdate
@@ -0,0 +1,553 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.PMF.ConvDate
+
+        MACRO
+        CDATT   $mnemonic, $stackoffset, $digits
+        ASSERT  (:LEN: "$mnemonic") = 2
+        ASSERT  ((CDAT$mnemonic-(CDATBranch+8)) :AND: &FFFFFC03) = 0
+        ASSERT  ($stackoffset >=0) :LAND: ($stackoffset < 64)
+        LCLA    digits
+        [       "$digits"=""
+digits  SETA    2
+        |
+digits  SETA    $digits
+        ]
+        ASSERT  (digits >= 1) :LAND: (digits <= 3)
+
+        DCB     digits :OR: ($stackoffset :SHL: 2)
+        DCB     (CDAT$mnemonic-(CDATBranch+8)) :SHR: 2
+        =       "$mnemonic"
+        MEND
+
+; *****************************************************************************
+;
+;       ConvertStandardDateAndTime - Convert from 5-byte cs representation to
+;                                    format specified in <SYS$DateFormat>
+;
+; in:   R0 -> time block
+;       [R0, #0..4] = 5-byte centisecond representation
+;       R1 -> buffer to accept conversion
+;       R2 = size of buffer
+;
+; out:  V=0 => successful conversion
+;       R0 = input value of R1
+;       R1 = updated pointer to buffer
+;       R2 = updated size of buffer
+;
+;       V=1 => failed conversion
+;       R0 -> error block
+;       R1 = input value of R1
+;       R2 = input value of R2
+;
+
+
+ConvertStandardDateAndTime ROUT
+        BIC     R10, R13, #&00FF
+        BIC     R10, R10, #&7F00        ; R10 -> assumed start of stack
+        SUB     R10, R13, R10           ; R10 := free space on stack
+        CMP     R10, #&200              ; to be on the safe side
+        BCS     %FT10
+
+; not enough stack for operation
+
+        ADR     R0, ErrorBlock_CDATStackOverflow
+      [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      ]
+        SWI     XOS_GenerateError
+        ORR     R14, R14, #V_bit
+        ExitSWIHandler
+
+        MakeErrorBlock CDATStackOverflow
+
+10
+        SUB     R13, R13, #&100         ; room for reading <SYS$DateFormat>
+        Push    "R0-R4, R14"
+        ADR     R0, DateFormatVarName   ; R0 -> "SYS$DateFormat"
+        ADD     R1, R13, #6*4           ; R1 -> buffer for string
+        MOV     R2, #255                ; R2 = maximum length of string
+        MOV     R3, #0                  ; not wild carding
+        MOV     R4, #VarType_Expanded   ; convert to string
+        SWI     XOS_ReadVarVal
+        BVS     %FT30
+        MOV     R0, #0
+        STRB    R0, [R1, R2]            ; terminate string with zero
+        MOV     R3, R1                  ; R3 -> format string
+        Pull    "R0-R2"
+        SWI     XOS_ConvertDateAndTime
+        Pull    "R3,R4, R14"
+20
+        ADD     R13, R13, #&100
+        ORRVS   R14, R14, #V_bit
+        ExitSWIHandler
+
+; format specifier not found
+
+30
+        ADD     R13, R13, #4            ; throw away stacked R0
+        Pull    "R1-R4, R14"
+        B       %BT20
+
+DateFormatVarName
+        =       "SYS$DateFormat",0
+        ALIGN
+
+; *****************************************************************************
+;
+;       ConvertDateAndTime - Convert from 5-byte cs representation to
+;                            format specified by user
+;
+; in:   R0 -> time block
+;       [R0, #0..4] = 5-byte centisecond representation
+;       R1 -> buffer to accept conversion
+;       R2 = size of buffer
+;       R3 -> format string
+;
+; out:  V=0 => successful conversion
+;       R0 = input value of R1
+;       R1 = updated pointer to buffer
+;       R2 = updated size of buffer
+;
+;       V=1 => failed conversion
+;       R0 -> error block
+;       R1 = input value of R1
+;       R2 = input value of R2
+;
+
+ConvertDateAndTime ROUT
+
+      [ {TRUE}                          ; International vesion just calls territory manager.
+        Push    "R4,R14"
+
+
+        MOV     R4,R3                   ; Territory SWI wants things one register up.
+        MOV     R3,R2
+        MOV     R2,R1
+        MOV     R1,R0
+        MOV     R0,#-1                  ; Use configured territory.
+        SWI     XTerritory_ConvertDateAndTime
+        Pull    "R4,R14"
+        ORRVS   R14, R14, #V_bit        ; set V in R14 for exit
+        ExitSWIHandler
+
+      |
+        Push    "R1-R11, R14"
+
+        LDRB    R4, [R0, #0]            ; read the 5 byte value to convert
+        LDRB    R5, [R0, #1]
+        ORR     R4, R4, R5, LSL #8
+        LDRB    R5, [R0, #2]
+        ORR     R4, R4, R5, LSL #16
+        LDRB    R5, [R0, #3]
+        ORR     R4, R4, R5, LSL #24     ; R4 contains bottom 4 bytes
+        LDRB    R5, [R0, #4]            ; R5 contains 5th byte
+
+        MOV     R6, R4, LSR #8
+        ORR     R6, R6, R5, LSL #24     ; R6 := centiseconds DIV 256
+        LDR     R7, =ticksperday/256    ; (ticksperday is a multiple of 256)
+        DivRem  R8, R6, R7, R9          ; R8 = number of days since 1900
+        AND     R4, R4, #&FF            ; R4 := centiseconds MOD 256
+        ORR     R6, R4, R6, LSL #8      ; R6 := centiseconds today
+
+; first work out bits from R6
+
+        LDR     R7, =ticksperhour
+        DivRem  R4, R6, R7, R9          ; R4 := hours
+        LDR     R7, =ticksperminute
+        DivRem  R5, R6, R7, R9          ; R5 := minutes
+        LDR     R7, =tickspersecond
+        DivRem  R10, R6, R7, R9         ; R10 := seconds
+                                        ; R6 := centiseconds
+        Push    "R4,R5,R6,R10"
+
+; now work out bits from R8
+
+        ADD     R11, R8, #1             ; R11 := fudged copy of days since 1900
+        MOV     R5, #7
+        DivRem  R6, R11, R5, R7         ; R11 := days MOD 7 (ie day of week)
+        ADD     R11, R11, #1            ; make in range 1..7
+
+        MOV     R5, #00                 ; units of years = 00
+        MOV     R4, #19                 ; hundreds of years = 19
+10
+        MOV     R6, #0                  ; 0 if not a leap year
+        TST     R5, #3                  ; if not divis by 4 then not leap year
+        BNE     %FT30
+        TEQ     R5, #0                  ; elif not divis by 100 then leap
+        BNE     %FT20
+        TST     R4, #3                  ; elif not divis by 400 then not leap
+        BNE     %FT30
+20
+        MOV     R6, #1                  ; 1 if leap year
+30
+        LDR     R7, =365                ; normally take off 365 days per year
+        ADD     R7, R7, R6              ; extra day if leap year
+
+        SUBS    R8, R8, R7              ; try taking off 365 or 366 days
+        BCC     %FT40                   ; [failed the subtract]
+        ADD     R5, R5, #1              ; increment year if successful
+        CMP     R5, #100
+        MOVEQ   R5, #0
+        ADDEQ   R4, R4, #1
+        B       %BT10
+
+40
+        ADD     R8, R8, R7              ; add back on if we couldn't do it
+                                        ; R8 is day of year (0..365)
+        Push    "R4,R5"                 ; push yearhi, yearlo
+
+        ADD     R7, R8, #1              ; R7 = day number in range 1-366
+        Push    "R7, R11"               ; push d-o-y, d-o-w
+
+; now compute week number
+
+        SUBS    R7, R11, #2             ; dow (Sun=-1, Mon=0,... ,Sat=5)
+        ADDCC   R7, R7, #7              ; dow (Mon=0,... ,Sun=6)
+        SUB     R7, R8, R7              ; day-of-year no. of start of week
+
+        ADD     R7, R7, #6              ; work out week number as if
+                                        ; 1st part week is week 0
+        MOV     R10, #7
+        DivRem  R11, R7, R10, R9        ; R11 = week number (0..53)
+                                        ; R7 (remainder) indicates dayofweek
+                                        ; of start of year (Mon=6,Tue=5..Sun=0)
+        CMP     R7, #3                  ; if year starts on Mon..Thu
+        ADDCS   R11, R11, #1            ; then 1st part week is week 1
+
+        TEQ     R7, #4                  ; if a Wednesday
+        TEQEQ   R6, #1                  ; in a leap year
+        TEQNE   R7, #3                  ; or a Thursday in any year
+        MOVEQ   R9, #53                 ; then allow 53 weeks in year
+        MOVNE   R9, #52                 ; else only 52 weeks
+        CMP     R11, R9                 ; if more than this
+        MOVHI   R11, #1                 ; then week 1 of next year
+
+        TEQ     R11, #0                 ; if not week 0
+        BNE     %FT45                   ; then finished
+
+        CMP     R7, #1                  ; HI => Fri, EQ => Sat, CC => Sun
+        ADC     R11, R11, #52           ; Fri => 53, Sun => 52, Sat => dunno
+        BNE     %FT45
+
+; 1st day of year is Saturday
+; if previous year was leap, then is week 53, else is week 52
+
+        SUBS    R5, R5, #1              ; decrement year
+        MOVCC   R5, #99
+        SUBCC   R4, R4, #1
+
+        TST     R5, #3                  ; if not divis by 4 then not leap year
+        BNE     %FT42
+        TEQ     R5, #0                  ; elif not divis by 100 then leap
+        BNE     %FT45
+        TST     R4, #3                  ; elif not divis by 400 then not leap
+42
+        MOVNE   R11, #52                ; not leap, so must be week 52
+45
+        Push    "R11"                   ; push weekno
+
+        ADRL    R7, MonthLengths+1      ; R7 -> Jan(31) (Feb is stored as 29)
+        EOR     R6, R6, #1              ; R6 = 1 <=> not a leap year
+        MOV     R9, #1                  ; month number (1 = Jan)
+50
+        LDRB    R10, [R7], #1           ; get next month
+        CMP     R9, #2                  ; if we're trying for Feb
+        SUBEQ   R10, R10, R6            ; and not leap then subtract a day
+        SUBS    R8, R8, R10             ; subtract off month value
+        ADDCS   R9, R9, #1              ; if successful month +:= 1
+        BCS     %BT50
+
+        ADD     R8, R8, R10             ; add the month back on if we failed
+        ADD     R8, R8, #1              ; day of month in range 1..31
+
+        Push    "R8,R9"                 ; push d-o-m, month
+CDATMainLoop
+        SUBS    R2, R2, #1              ; decrement buffer size
+        BCC     CDATBufferError         ; error: buffer too small
+        LDRB    R0, [R3], #1            ; get byte from format string
+        TEQ     R0, #"%"                ; is it a escape sequence ?
+        BEQ     %FT65                   ; yes, then do specially
+        STRB    R0, [R1], #1            ; no, then store in output buffer
+        TEQ     R0, #0                  ; end of format string ?
+        BNE     CDATMainLoop            ; no, then loop
+
+; end of format string, so finish
+
+        SUB     R1, R1, #1              ; make R1 point to 0 byte
+
+        ADD     R13, R13, #11*4         ; junk dom,month,woy,doy
+                                        ; junk dow,yearhi,yearlo,hours
+                                        ; junk mins,secs,centisecs
+
+        Pull    "R0,R3"                 ; R0 := input R1; junk input R2
+        Pull    "R3-R11, R14"           ; restore other registers
+        ExitSWIHandler                  ; and exit
+
+; come here if run out of buffer space for string
+
+CDATBufferError
+        ADR     R0, ErrorBlock_CDATBufferOverflow ; point R0 to error block
+CDATError
+        ADD     R13, R13, #11*4         ; junk dom,month,woy,doy
+                                        ; junk dow,yearhi,yearlo,hours
+                                        ; junk mins,secs,centisecs
+        Pull    "R1-R11,R14"            ; restore all registers apart from R0
+        ORR     R14, R14, #V_bit        ; set V in R14 for exit
+        ExitSWIHandler
+
+        MakeErrorBlock CDATBufferOverflow
+
+        MakeErrorBlock CDATBadField
+
+; process "%" escape sequences
+
+65
+        LDRB    R0, [R3], #1            ; get next character
+        TEQ     R0, #"0"                ; if char = "0"
+        MOVEQ   R0, #0                  ; convert to <0>
+        TEQNE   R0, #"%"                ; or if char = "%", store "%"
+        STREQB  R0, [R1], #1            ; store <0> or "%"
+        BEQ     CDATMainLoop            ; and loop
+
+        UpperCase R0, R5                ; convert character to upper case
+        EORS    R7, R0, #"Z"            ; zero if we have "Z" specifier
+        BNE     %FT67                   ; not "Z"
+
+        LDRB    R0, [R3], #1            ; is "Z", so get another char
+        UpperCase R0, R5                ; convert character to upper case
+67
+        TEQ     R0, #0                  ; are they a wally!
+        BEQ     CDATFieldError          ; yes, then bomb out (avoid data abort)
+
+        LDRB    R4, [R3], #1            ; get next char
+        UpperCase R4, R5                ; and convert that to upper case
+
+        ORR     R0, R0, R4, LSL #8      ; 2 chars in bottom two bytes
+
+        ADR     R4, CDATEscTab          ; point to table
+        ADR     R5, CDATEscTabEnd       ; end of table
+70
+        TEQ     R4, R5                  ; are we at end of table
+CDATFieldError
+        ADREQ   R0, ErrorBlock_CDATBadField
+        BEQ     CDATError               ; yes, then invalid escape sequence
+        LDR     R6, [R4], #4            ; chars in top two bytes
+        EOR     R6, R6, R0, LSL #16     ; if match, then must be < 1:SHL:16
+        CMP     R6, #(1 :SHL: 16)
+        BCS     %BT70                   ; no match, so loop
+
+; found mnemonic match
+
+        AND     R0, R6, #&03            ; R0 = number of digits to print
+        AND     R4, R6, #&FC            ; R4 = stack offset
+        LDR     R4, [R13, R4]           ; R4 = data item
+        MOV     R6, R6, LSR #8          ; R6 = code offset in words
+CDATBranch
+        ADD     PC, PC, R6, LSL #2      ; go to routine
+
+CDATEscTab
+        CDATT   DY, 0
+        CDATT   ST, 0
+        CDATT   MN, 1
+        CDATT   MO, 1
+        CDATT   M3, 1
+        CDATT   WK, 2
+        CDATT   DN, 3, 3
+        CDATT   WN, 4, 1
+        CDATT   W3, 4, 1
+        CDATT   WE, 4, 1
+        CDATT   CE, 5
+        CDATT   YR, 6
+        CDATT   24, 7
+        CDATT   12, 7
+        CDATT   AM, 7
+        CDATT   PM, 7
+        CDATT   MI, 8
+        CDATT   CS, 9
+        CDATT   SE, 10
+CDATEscTabEnd
+
+; routine to print R0 digits of the number held in R4,
+; R7=0 <=> suppress leading zeroes
+
+CDATDY
+CDATMN
+CDATWK
+CDATDN
+CDATWN
+CDATCE
+CDATYR
+CDAT24
+CDATMI
+CDATCS
+CDATSE
+CDATDecR4 ROUT
+        ADD     R2, R2, #1              ; undo initial subtract
+        ADR     R6, PowersOfTen
+10
+        MOV     R5, #0
+        TEQ     R0, #1                  ; if on last digit
+        MOVEQ   R7, #1                  ; definitely don't suppress
+20
+        LDRB    R8, [R6, R0]            ; get power of ten to subtract
+        SUBS    R4, R4, R8              ; subtract value
+        ADDCS   R5, R5, #1
+        BCS     %BT20
+        ADD     R4, R4, R8              ; undo failed subract
+
+        ORRS    R7, R7, R5              ; Z => suppress it
+        BEQ     %FT30                   ; [suppressing]
+
+        ORR     R5, R5, #"0"            ; convert to ASCII digit
+        SUBS    R2, R2, #1              ; one less space in buffer
+        BCC     CDATBufferError
+        STRB    R5, [R1], #1            ; store character
+30
+        SUBS    R0, R0, #1              ; next digit
+        BNE     %BT10                   ; [another digit to do]
+        B       CDATMainLoop
+
+PowersOfTen
+        =       0, 1, 10, 100
+        ALIGN
+
+; Hours in 12 hour format
+
+CDAT12
+        CMP     R4, #12                 ; if in range 12..23
+        SUBCS   R4, R4, #12             ; then make in range 00..11
+        TEQ     R4, #0                  ; if 00
+        MOVEQ   R4, #12                 ; then make 12
+        B       CDATDecR4
+
+; AM or PM indication
+
+CDATAM
+CDATPM
+        CMP     R4, #12                 ; if earlier than 12 o'clock
+        ADRCC   R4, CDATamstr           ; then am
+        ADRCS   R4, CDATpmstr           ; else pm
+CDATdostr
+        LDRB    R0, [R4], #1            ; get byte from string
+        TEQ     R0, #0                  ; if zero, then end of string
+        BEQ     CDATMainLoop            ; so loop
+CDATdostrloop
+        STRB    R0, [R1], #1            ; we know there's room for one char
+CDATdostr2
+        LDRB    R0, [R4], #1            ; get byte from string
+        TEQ     R0, #0                  ; if zero, then end of string
+        BEQ     CDATMainLoop            ; so loop
+        SUBS    R2, R2, #1              ; dec R2 for next char
+        BCS     CDATdostrloop           ; OK to do another char
+        B       CDATBufferError         ; ran out of buffer space
+
+CDATST
+        TEQ     R4, #1
+        TEQNE   R4, #21
+        TEQNE   R4, #31
+        ADREQ   R4, CDATststr
+        BEQ     CDATdostr
+        TEQ     R4, #2
+        TEQNE   R4, #22
+        ADREQ   R4, CDATndstr
+        BEQ     CDATdostr
+        TEQ     R4, #3
+        TEQNE   R4, #23
+        ADREQ   R4, CDATrdstr
+        ADRNE   R4, CDATthstr
+        B       CDATdostr
+
+CDATW3
+        ADRL    R0, DayNameTable-4      ; Sun is 1
+        B       CDATdo3
+CDATM3
+        ADRL    R0, MonthNameTable-4    ; Jan is month 1
+CDATdo3
+        ADD     R4, R0, R4, LSL #2      ; point to short month name
+        LDRB    R0, [R4], #1            ; get 1st char of month
+        BIC     R0, R0, #&20            ; upper case
+        STRB    R0, [R1], #1            ; and store
+        B       CDATdostr2              ; then do rest
+
+CDATWE
+        ADD     R4, R4, #12             ; skip months
+CDATMO
+        ADR     R0, LongMonthTable-1    ; Jan is month 1
+        LDRB    R4, [R0, R4]            ; get offset to month string
+        ADD     R4, R0, R4              ; point to start of string
+        B       CDATdostr
+
+CDATamstr
+        =       "am", 0
+CDATpmstr
+        =       "pm", 0
+CDATststr
+        =       "st", 0
+CDATndstr
+        =       "nd", 0
+CDATrdstr
+        =       "rd", 0
+CDATthstr
+        =       "th", 0
+
+LongMonthTable
+        =       LongJan-(LongMonthTable-1)
+        =       LongFeb-(LongMonthTable-1)
+        =       LongMar-(LongMonthTable-1)
+        =       LongApr-(LongMonthTable-1)
+        =       LongMay-(LongMonthTable-1)
+        =       LongJun-(LongMonthTable-1)
+        =       LongJul-(LongMonthTable-1)
+        =       LongAug-(LongMonthTable-1)
+        =       LongSep-(LongMonthTable-1)
+        =       LongOct-(LongMonthTable-1)
+        =       LongNov-(LongMonthTable-1)
+        =       LongDec-(LongMonthTable-1)
+        =       LongSun-(LongMonthTable-1)
+        =       LongMon-(LongMonthTable-1)
+        =       LongTue-(LongMonthTable-1)
+        =       LongWed-(LongMonthTable-1)
+        =       LongThu-(LongMonthTable-1)
+        =       LongFri-(LongMonthTable-1)
+        =       LongSat-(LongMonthTable-1)
+
+LongJan =       "January", 0
+LongFeb =       "February", 0
+LongMar =       "March", 0
+LongApr =       "April", 0
+LongMay =       "May", 0
+LongJun =       "June", 0
+LongJul =       "July", 0
+LongAug =       "August", 0
+LongSep =       "September", 0
+LongOct =       "October", 0
+LongNov =       "November", 0
+LongDec =       "December", 0
+LongSun =       "Sunday", 0
+LongMon =       "Monday", 0
+LongTue =       "Tuesday", 0
+LongWed =       "Wednesday", 0
+LongThu =       "Thursday", 0
+LongFri =       "Friday", 0
+LongSat =       "Saturday", 0
+
+        ALIGN
+      ]
+        LTORG
+
+        END
diff --git a/s/PMF/i2cutils b/s/PMF/i2cutils
new file mode 100644
index 0000000000000000000000000000000000000000..140481975a9fd3a65eb3538892a932cedb8251f3
--- /dev/null
+++ b/s/PMF/i2cutils
@@ -0,0 +1,857 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.PMF.i2cutils
+
+; Authors JBiggs (m2), PFellows, TDobson, AGodwin
+
+PhysChecksum    *       (((CheckSumCMOS + &30) :MOD: &F0) + &10)
+
+; *****************************************************************************
+;
+;       HexToBCD - Convert byte in hex to BCD
+;
+; in:   R0 = byte in hex
+;
+; out:  R0 = byte in BCD (ie R0 := (R0 DIV 10)*16 + R0 MOD 10)
+;       All other registers preserved
+;
+
+HexToBCD ROUT
+        Push    "R1,R2, R14"
+        MOV     R1, #10
+        DivRem  R2, R0, R1, R14                 ; R2=R0 DIV 10; R0=R0 MOD 10
+        ADD     R0, R0, R2, LSL #4
+        Pull    "R1,R2, PC"
+
+; *****************************************************************************
+;
+;       BCDToHex - Convert byte in BCD to hex
+;
+; in:   R0 = byte in BCD (ie x*16 + y)
+;
+; out:  R0 = byte in hex (ie x*10 + y)
+;       All other registers preserved
+;
+
+BCDToHex ROUT
+        Push    "R14"
+        MOV     R14, R0, LSR #4         ; R14 := x
+        ADD     R14, R14, R14, LSL #1   ; R14 := x*3
+        SUB     R0, R0, R14, LSL #1     ; R0 := R0 - x*6 = x*10
+        Pull    "PC"
+
+; *****************************************************************************
+;
+;       SetC1C0 - Set clock and data lines to values in R1 and R0 respectively
+;
+; out:  All registers preserved, including PSR
+;
+
+SetC1C0 ROUT
+        Push    "R0-R2,R14"
+        ADD     R0, R0, R1, LSL #1              ; R0 := C0 + C1*2
+
+        [ AssemblingArthur :LOR: Module
+        MOV     R2, #0                          ; prepare to index soft copy
+        LDRB    R1, [R2, #IOCControlSoftCopy]   ; read soft copy
+        BIC     R1, R1, #&03                    ; clear clock and data
+        ORR     R0, R1, R0                      ; put in new clock and data
+        ORR     R0, R0, #&C0                    ; make sure two test bits are
+                                                ; always set to 1 !
+        STRB    R0, [R2, #IOCControlSoftCopy]   ; store back to soft copy
+        |
+        ORR     R0, R0, #&FC                    ; set other bits to 1
+        ]
+
+        MOV     R2, #IOC
+        STRB    R0, [R2, #IOCControl]
+
+        MOV     R0, #10                         ; delay for >= 10/2 microsecs
+        BL      DoMicroDelay
+
+        Pull    "R0-R2,PC",,^
+
+; *****************************************************************************
+;
+;       DoMicroDelay - Delay for >= R0/2 microseconds
+;
+; in:   R0 = time delay in 1/2 microsecond units
+;       R2 -> IOC
+;       On ARM600, we may or may not be in a 32-bit mode
+;
+; out:  R0,R1 corrupted
+;
+
+DoMicroDelay ROUT
+        Push    R14
+
+        STRB    R0, [R2, #Timer0LR]     ; copy counter into output latch
+        LDRB    R1, [R2, #Timer0CL]     ; R1 := low output latch
+10
+        STRB    R0, [R2, #Timer0LR]     ; copy counter into output latch
+        LDRB    R14, [R2, #Timer0CL]    ; R14 := low output latch
+        TEQ     R14, R1                 ; unchanged ?
+        BEQ     %BT10                   ; then loop
+        MOV     R1, R14                 ; copy anyway
+        SUBS    R0, R0, #1              ; decrement count
+        BNE     %BT10                   ; loop if not finished
+
+        Pull    PC
+
+        LTORG
+
+; *****************************************************************************
+;
+;       ClockData - Clock a bit of data down the IIC bus
+;
+; in:   R0 = data bit
+;
+; out:  All registers preserved, including PSR
+;
+
+ClockData ROUT
+        Push    "R1, R14"
+
+        MOV     R1, #0                  ; Clock lo
+        BL      SetC1C0
+
+ [ {TRUE}
+; Disable interrupts to ensure clock hi with data hi is only transient
+; This allows BMU to detect idle condition by polling
+
+        MOV     R1,PC
+        ORR     R1,R1,#I_bit
+        TEQP    PC,R1
+ ]
+        MOV     R1, #1                  ; Clock hi
+        BL      SetC1C0
+
+; Delay here must be >= 4.0 microsecs
+
+        MOV     R1, #0                  ; Clock lo
+        BL      SetC1C0
+
+        Pull    "R1, PC",,^
+
+ClockData0 ROUT                                 ; Clock a zero bit
+        Push    "R0, R14"
+        MOV     R0, #0
+        BL      ClockData
+        Pull    "R0, PC",,^
+
+; *****************************************************************************
+;
+;       Start - Send the Start signal
+;
+; out:  All registers preserved, including PSR
+;
+
+Start   ROUT
+        Push    "R0-R2,R14"
+
+        MOV     R0, #1                  ; clock HI, data HI
+        MOV     R1, #1
+        BL      SetC1C0
+
+; Delay here must be >= 4.0 microsecs
+
+        MOV     R0, #0                  ; clock HI, data LO
+        MOV     R1, #1
+        BL      SetC1C0
+
+ [ {TRUE}
+; Hold start condition for BMU
+
+        MOV     R2, #IOC
+        MOV     R0,#10
+        BL      DoMicroDelay
+ ]
+
+; Delay here must be >= 4.7 microsecs
+
+        MOV     R0, #0                  ; clock LO, data LO
+        MOV     R1, #0
+        BL      SetC1C0
+
+        Pull    "R0-R2,PC",,^
+
+; *****************************************************************************
+;
+;       Acknowledge - Check acknowledge after transmitting a byte
+;
+; out:  All registers preserved
+;       V=0 => acknowledge received
+;       V=1 => no acknowledge received
+;
+
+Acknowledge ROUT
+        Push    "R0-R2,R14"
+
+        MOV     R0, #1                  ; clock LO, data HI
+        MOV     R1, #0
+        BL      SetC1C0
+
+ [ {TRUE}
+; Disable interrupts to ensure clock hi with data hi is only transient
+; This allows BMU to detect idle condition by polling
+
+        MOV     R1,PC
+        Push    "R1"
+        ORR     R1,R1,#I_bit
+        TEQP    PC,R1
+ ]
+        MOV     R0, #1                  ; clock HI, data HI
+        MOV     R1, #1
+        BL      SetC1C0
+
+; Delay here must be >= 4.0 microsecs
+
+        MOV     R2, #IOC
+        LDRB    R2, [R2, #IOCControl]   ; get the data from IOC
+
+        MOV     R0, #0
+        MOV     R1, #0                  ; clock lo
+        BL      SetC1C0
+
+ [ {TRUE}
+        Pull    "R1"
+        TEQP    PC,R1
+ ]
+
+        TST     R2, #1                  ; should be LO for correct acknowledge
+        MOV     R2, PC
+        BICEQ   R2, R2, #V_bit          ; clear V if correct acknowledge
+        ORRNE   R2, R2, #V_bit          ; set V if no acknowledge
+        TEQP    R2, #0
+
+        Pull    "R0-R2,PC"
+
+; *****************************************************************************
+;
+;       Stop - Send the Stop signal
+;
+; out:  All registers preserved, including PSR
+;
+
+Stop    ROUT
+        Push    "R0,R1,R14"
+
+        MOV     R0, #0                  ; clock HI, data LO
+        MOV     R1, #1
+        BL      SetC1C0
+
+; Delay here must be >= 4.0 microsecs
+
+        MOV     R0, #1                  ; clock HI, data HI
+        MOV     R1, #1
+        BL      SetC1C0
+
+        Pull    "R0,R1,PC",,^
+
+; *****************************************************************************
+;
+;       TXByte - Transmit a byte
+;
+; in:   R0 = byte to be transmitted
+;
+; out:  All registers preserved, including PSR
+;
+
+TXByte  ROUT
+        Push    "R0-R2,R14"
+        MOV     R2, R0                  ; byte goes into R2
+        MOV     R1, #&80                ; 2^7   the bit mask
+10
+        ANDS    R0, R2, R1              ; zero if bit is zero
+        MOVNE   R0, #1
+        BL      ClockData               ; send the bit
+        MOVS    R1, R1, LSR #1
+        BNE     %BT10
+        Pull    "R0-R2,PC",,^
+
+; *****************************************************************************
+;
+;       RXByte - Receive a byte
+;
+; out:  R0 = byte received
+;       All other registers preserved, including PSR
+;
+
+RXByte  ROUT
+        Push    "R1-R4, R14"
+        MOV     R3, #0                  ; byte:=0
+        MOV     R2, #IOC
+        MOV     R4, #7
+
+        MOV     R0, #1                  ; clock LO, data HI
+        MOV     R1, #0
+        BL      SetC1C0
+10
+ [ {TRUE}
+; Disable interrupts to ensure clock hi with data hi is only transient
+; This allows BMU to detect idle condition by polling
+
+        MOV     R1,PC
+        Push    "R1"
+        ORR     R1,R1,#I_bit
+        TEQP    PC,R1
+ ]
+        MOV     R0, #1                  ; pulse clock HI
+        MOV     R1, #1
+        BL      SetC1C0
+
+        LDRB    R1, [R2, #IOCControl]   ; get the data from IOC
+        AND     R1, R1, #1
+        ADD     R3, R1, R3, LSL #1      ; byte:=byte*2+(IOC?0)AND1
+
+        MOV     R0, #1                  ; return clock LO
+        MOV     R1, #0
+        BL      SetC1C0
+
+ [ {TRUE}
+        Pull    "R1"
+        TEQP    PC,R1
+ ]
+        SUBS    R4, R4, #1
+        BCS     %BT10
+
+        MOV     R0, R3                  ; return the result in R0
+        Pull    "R1-R4, PC",,^
+
+; *****************************************************************************
+;
+;       Write - Write a byte of CMOS RAM specified by logical address
+;
+; in:   R0 = address in CMOS RAM
+;       R1 = data
+;
+; out:  All registers preserved
+;
+
+Write   ROUT
+        Push    "R0-R4, R14"
+        BL      MangleCMOSAddress
+        Pull    "R0-R4, PC", CS        ; if invalid, then exit
+
+        MOV     R2, R0
+        MOV     R3, R1
+    [ ChecksumCMOS
+        BL      ReadStraight            ; calculate new checksum :
+        MOV     R4, R0
+        MOV     R0, #PhysChecksum
+        BL      ReadStraight
+        SUB     R0, R0, R4              ; = oldsum - oldcontents
+        ADD     R4, R0, R3              ;          + newcontents
+
+        AND     R4, R4, #&FF
+        CMPS    R2, #PhysChecksum       ; don't write new checksum ...
+        ORREQ   R4, R4, #&100           ; if checksum is being written
+    ]
+10
+ [ CacheCMOSRAM
+        ADR     R0, CMOSRAMCache-16     ; update cache, but always write to
+        STRB    R3, [R0, R2]            ; real hardware as well
+ ]
+        BL      Start
+
+        MOV     R0, #&A0
+        BL      TXAck
+
+        MOV     R0, R2                  ; address
+        BL      TXAck
+
+        MOV     R0, R3                  ; data
+        BL      TXAck
+
+        BL      Stop
+    [ ChecksumCMOS
+        TST     R4, #&100               ; loop again to write new checksum
+        MOV     R3, R4
+        MOV     R2, #PhysChecksum
+        ORR     R4, R4, #&100           ; but ensure it only happens once
+        BEQ     %BT10
+    ]
+        Pull    "R0-R4, PC"
+
+; *****************************************************************************
+;
+;       Read - Read a byte of CMOS RAM specified by logical address
+;       ReadStraight - Read a byte of CMOS RAM specified by physical address
+;
+; in:   R0 = address in CMOS RAM
+;
+; out:  R0 = data (illegal address return 0)
+;       All other registers preserved
+;
+
+ReadStraight ROUT
+        Push    "R1,R2,R14"
+        B       %FT10
+
+Read
+        Push    "R1,R2,R14"
+        BL      MangleCMOSAddress
+        MOVCS   R0, #0          ; pretend illegal addresses contain 0
+        Pull    "R1,R2,PC", CS
+10
+ [ CacheCMOSRAM
+        TEQ     R0, #&40                ; is it Econet station number
+        BEQ     %FT15                   ; if so then don't use cache
+  [ {FALSE}                             ; CMOS read debugging
+        MOV     R1, #0
+        LDR     R1, [R1]                ; if !0 < &100 then it's a handle to bput to
+        CMP     R1, #&100
+        SWICC   XOS_BPut
+  ]
+        CMP     R0, #&10                ; only cache values &10..&3F,&41..&FF
+        ADRCS   R2, CMOSRAMCache-&10    ; if in range
+        LDRCSB  R0, [R2, R0]            ; read from cache
+        Pull    "R1,R2,PC", CS          ; and exit
+15
+
+; else drop thru into real CMOS reading code
+ ]
+
+        MOV     R2,R0           ; save the address
+
+        BL      Start
+
+        MOV     R0, #&A0
+        BL      TXAck
+
+        MOV     R0, R2          ; address
+        BL      TXAck
+
+        BL      Start
+
+        MOV     R0, #&A1
+        BL      TXAck
+
+        BL      RXByte          ; returned in R0
+        MOV     R2, R0          ; copy to R2 for now
+
+        MOV     R0, #1
+        BL      ClockData
+
+        BL      Stop
+
+        MOV     R0, R2          ; return the result
+
+        Pull    "R1,R2,PC"
+
+; *****************************************************************************
+;
+;       MangleCMOSAddress - Convert from logical to physical CMOS address
+;
+; in:   R0 = logical address (&00..&FF)
+;
+; out:  C=0 => valid logical address (ie &00..&EF)
+;       R0 = physical address (&40..&FF,&10..&3F)
+;
+;       C=1 => invalid logical address (ie &F0..&FF)
+;       R0 preserved
+;
+
+MangleCMOSAddress ROUT
+        CMP     R0, #&F0
+        ORRCSS  PC, R14, #C_bit         ; indicate invalid
+        ADD     R0, R0, #&40            ; now in range &40..&13F
+        CMP     R0, #&100
+        SUBCS   R0, R0, #(&100-&10)     ; now in range &40..&FF, &10..&3F
+        BICS    PC, R14, #C_bit         ; indicate valid
+
+; *****************************************************************************
+
+HTBTA   ROUT
+        Push    R14
+        BL      HexToBCD
+        BL      TXAck
+        Pull    PC
+
+TXAck   ROUT
+        Push    R14
+        BL      TXByte
+        BL      Acknowledge
+        Pull    PC
+
+CD0RBTH ROUT
+        Push    R14
+        BL      ClockData0
+        BL      RXByte
+        BL      BCDToHex
+        Pull    PC
+
+; *****************************************************************************
+;
+;       ValChecksum - test to see if the CMOS checksum is OK
+;       This routine performs MangleCMOSAddress inherently, and
+;       assumes the checksum is the last logical location.
+;
+; in:   none
+;
+; out:  R0 = calculated checksum
+;       Z    set if checksum is valid
+;       All other registers preserved
+;
+
+    [ ChecksumCMOS
+
+ValChecksum     ROUT
+        Push    "R1,R2,R3,R14"
+
+        MOV     R2, #&40
+        MOV     R3, #CMOSxseed
+;
+; Write a memory word address to the 8583
+10
+        BL      Start
+
+        MOV     R0, #&A0        ; 8583 write address
+        BL      TXAck
+
+        MOV     R0, R2          ; memory word address
+        BL      TXAck
+
+        BL      Start
+
+        MOV     R0, #&A1        ; 8583 read address
+        BL      TXAck
+;
+; Read data from the 8583 from locations &40 through &FF, then
+; loop back to &10 (we have to write the address again) and read
+; on until &3E .. thus missing out the checksum location.
+;
+20
+        BL      RXByte          ; returned in R0
+        ADD     R3, R3, R0      ; accumulate into R3
+
+        ADD     R2, R2, #1      ; increment R2 to phys. address
+        TST     R2, #&100       ; limit, or until checksum.
+        BNE     %30
+        CMPS    R2, #PhysChecksum
+        BEQ     %30
+
+        MOV     R0, #0          ; not done .. ACK that byte
+        BL      ClockData
+        B       %BT20           ; .. and continue reading
+30
+        MOV     R0, #1          ; finish off reading block
+        BL      ClockData
+        BL      Stop
+        TST     R2, #&100       ; either go back for bytes 10 - 3f
+        MOVNE   R2, #&10        ; or finish up completely.
+        BNE     %BT10
+
+;
+; R3 contains the actual checksum. Compare it with the recorded checksum
+;
+        MOV     R0, #PhysChecksum
+        BL      ReadStraight
+        AND     R2, R0, #&FF    ; value from checksum location
+        AND     R0, R3, #&FF    ; calculated value into R0
+        CMPS    R0, R2
+
+        Pull    "R1,R2,R3,PC"
+    ]
+
+; *****************************************************************************
+;
+;       MakeChecksum - calculate and write a correct checksum
+;
+; in:   none
+;
+; out:  R0 = calculated checksum
+;       All other registers preserved
+;
+
+    [ ChecksumCMOS
+
+MakeChecksum    ROUT
+        Push    R14
+        BL      ValChecksum
+        MOV     R1, R0
+        MOV     R0, #CheckSumCMOS
+        BL      Write
+        Pull    PC
+    ]
+
+; *****************************************************************************
+;
+;       SetTime - Write the CMOS clock time
+;
+; in:   R0 = hours
+;       R1 = minutes
+;       R2 = day of month
+;       R3 = month
+;       R5 = year (lo)
+;       R6 = year (hi)
+;       R7 = seconds
+;       R8 = centiseconds
+;
+;       Any of the above, if -1, will not be written to
+;
+
+SetTime ROUT
+        Push    "R4, R14"               ; save registers
+
+        [ :LNOT: NewClockChip
+        CMP     R1, #-1                 ; test if setting mins
+        BEQ     %FT10                   ; [no, then skip]
+
+        Push    R0
+        MOV     R7, #0                  ; zero seconds and centiseconds
+        MOV     R8, #0
+        STRB    R8, CentiTime
+        STRB    R7, SecondsTime
+        STRB    R7, SecondsDirty        ; seconds are in sync now
+
+        BL      Start
+        MOV     R0, #&D0
+        BL      TXAck
+        MOV     R0, #&20                ; zero the seconds on the chip
+        BL      TXAck
+        BL      Stop
+        Pull    R0
+10
+        CMP     R3, #-1                 ; test if setting months
+        BEQ     %FT20                   ; [no, then skip]
+
+        Push    "R0,R1"
+        MOV     R1, R3
+        MOV     R0, #MonthCMOS
+        BL      Write                   ; remember last updated months
+
+        CMP     R3, #2                  ; if Jan
+        CMPEQ   R2, #28                 ; or Feb 1..28
+        MOVLS   R1, #0                  ; not had 29th of Feb
+        MOVHI   R1, #1                  ; have had 29th of Feb
+        MOV     R0, #LeapCMOS
+        BL      Write                   ; set 29thFeb flag
+        Pull    "R0,R1"
+20
+        ]
+
+; write year to CMOS RAM
+
+        Push    "R0,R1"
+        MOVS    R1, R5
+        MOVPL   R0, #YearCMOS
+        BLPL    Write
+        MOVS    R1, R6
+        MOVPL   R0, #YearCMOS+1
+        BLPL    Write
+        Pull    "R0,R1"
+
+        MOV     R4, R0                  ; save hours in R4
+        CMP     R4, #-1                 ; are we writing time ?
+        BEQ     %FT30                   ; [no, then skip]
+
+        [ NewClockChip
+        BL      Start
+        MOV     R0, #&A0
+        BL      TXAck
+        MOV     R0, #1                  ; start at address 1
+        BL      TXAck
+        MOV     R0, R8                  ; centiseconds
+        BL      HTBTA
+        MOV     R0, R7                  ; seconds
+        BL      HTBTA
+        MOV     R0, R1                  ; minutes
+        BL      HTBTA
+        MOV     R0, R4                  ; hours
+        BL      HTBTA
+        BL      Stop
+        |
+        BL      Start
+        MOV     R0, #&D0
+        BL      TXAck
+        MOV     R0, #0                  ; start at address 0
+        BL      TXAck
+        MOV     R0, R4                  ; hours
+        BL      HTBTA
+        MOV     R0, R1                  ; minutes
+        BL      HTBTA
+        BL      Stop
+        ]
+30
+        CMP     R2, #-1                 ; are we writing date ?
+        BEQ     %FT40                   ; [no, then skip]
+
+        [ NewClockChip
+        BL      Start
+        MOV     R0, #&A0
+        BL      TXAck
+        MOV     R0, #5                  ; start at address 5
+        BL      TXAck
+        MOV     R0, R2                  ; day of month
+        BL      HexToBCD
+        ORR     R0, R0, R5, LSL #6      ; year in bits 6,7; day in bits 0..5
+        BL      TXAck
+        MOV     R0, R3                  ; months
+        BL      HTBTA
+        BL      Stop
+        |
+        BL      Start
+        MOV     R0, #&D0
+        BL      TXAck
+        MOV     R0, #2                  ; start at address 2
+        BL      TXAck
+        MOV     R0, R2                  ; days
+        BL      HTBTA
+        MOV     R0, R3                  ; months
+        BL      HTBTA
+        BL      Stop
+        ]
+40
+        MOV     R0, R4                  ; put hours back in R0
+        BL      RTCToRealTime
+
+        Pull    "R4, PC"
+
+; *****************************************************************************
+;
+;       ReadTime - Read the CMOS clock time
+;
+; in:   -
+;
+; out:  R0 = hours
+;       R1 = minutes
+;       R2 = days
+;       R3 = months
+;       R5 = year (lo)
+;       R6 = year (hi)
+;       R7 = seconds
+;       R8 = centiseconds
+;
+
+ReadTime ROUT
+        Push    "R4, R14"
+
+        [ NewClockChip
+        BL      Start
+        MOV     R0, #&A0
+        BL      TXAck
+        MOV     R0, #&01                ; start at address 1
+        BL      TXAck
+        BL      Start
+        MOV     R0, #&A1
+        BL      TXAck
+        BL      RXByte
+        BL      BCDToHex
+        MOV     R8, R0                  ; centiseconds
+        BL      CD0RBTH
+        MOV     R7, R0                  ; seconds
+        BL      CD0RBTH
+        MOV     R1, R0                  ; minutes
+        BL      CD0RBTH
+        MOV     R4, R0                  ; hours
+        BL      ClockData0
+        BL      RXByte
+        AND     R0, R0, #&3F            ; day of month (clear year bits)
+        BL      BCDToHex
+        MOV     R2, R0
+        BL      ClockData0
+        BL      RXByte
+        AND     R0, R0, #&1F            ; month (clear day of week bits)
+        BL      BCDToHex
+        MOV     R3, R0
+        MOV     R0, #1
+        BL      ClockData
+        BL      Stop
+        |
+        BL      Start
+        MOV     R0, #&D0
+        BL      TXAck
+        MOV     R0, #&00                ; start at address 0
+        BL      TXAck
+        BL      Start
+        MOV     R0, #&D1
+        BL      TXAck
+        BL      RXByte
+        BL      BCDToHex
+        MOV     R4, R0          ; hours
+        BL      CD0RBTH
+        MOV     R1, R0          ; minutes
+        BL      CD0RBTH
+        MOV     R2, R0          ; days
+        BL      CD0RBTH
+        MOV     R3, R0          ; months
+        MOV     R0, #1
+        BL      ClockData
+        BL      Stop
+        LDRB    R7, SecondsTime
+        LDRB    R8, CentiTime
+        ]
+
+        MOV     R0, #YearCMOS
+        BL      Read
+        MOV     R5, R0          ; year (lo)
+
+        MOV     R0, #YearCMOS+1
+        BL      Read
+        MOV     R6, R0          ; year (hi)
+
+        MOV     R0, R4          ; put hours in R0
+
+; Ensure day/month are non-zero, fixes RTCToRealTime
+ [ {TRUE}                               ; LRust, fix RP-0370
+        TEQ     R2, #0                  ; Valid day?
+        MOVEQ   R2, #1                  ; No then force 1st
+        TEQ     R3, #0                  ; Invalid month?
+        MOVEQ   R3, #1                  ; Yes then force Jan
+ ]
+        Pull    "R4, PC"
+
+; *****************************************************************************
+;
+;       InitCMOSCache - Initialise cache of CMOS RAM
+
+ [ CacheCMOSRAM
+
+InitCMOSCache   ENTRY "r0-r3"
+        MOV     R2, #16
+        ADR     R3, CMOSRAMCache-16
+
+        BL      Start
+
+        MOV     R0, #&A0        ; 8583 write address
+        BL      TXAck
+
+        MOV     R0, R2          ; memory word address
+        BL      TXAck
+
+        BL      Start
+
+        MOV     R0, #&A1        ; 8583 read address
+        BL      TXAck
+
+10
+        BL      RXByte          ; returned in R0
+        STRB    R0, [R3, R2]
+        ADD     R2, R2, #1      ; increment R2 to phys. address
+        CMP     R2, #&100
+        BEQ     %FT20
+
+        MOV     R0, #0          ; not done .. ACK that byte
+        BL      ClockData
+        B       %BT10           ; .. and continue reading
+20
+        MOV     R0, #1          ; finish off reading block
+        BL      ClockData
+        BL      Stop
+        EXIT
+
+ ]
+        END
diff --git a/s/PMF/key b/s/PMF/key
new file mode 100644
index 0000000000000000000000000000000000000000..6a467b862b12feba356ad3dff96068d7565971b9
--- /dev/null
+++ b/s/PMF/key
@@ -0,0 +1,1428 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.PMF.Key
+
+; ARTHUR keyboard code
+
+; Authors       Tim Dobson, Jon Thackray
+; Started       13-Oct-86
+
+; ************************************************************
+; ***    C h a n g e   L i s t  (better late than never!)  ***
+; ************************************************************
+
+; Date      Who Description
+; ----      --- -----------
+; 17-Feb-88     Added Sam's code to call the callback vector in RDCH/INKEY
+;                idle loop
+; 02-Mar-88     Initialise KeyVec to NewKeyStruct before the keyboard has told
+;                us its ID, so Sam can call INKEY(-ve) with no keyboard
+; 13-Apr-88     Fixed RDCH from RS423 if treating as keyboard, getting NUL+char
+;                (didn't try to reenable RTS on 2nd char)
+; 11-Jun-88     Put input redirection where it was needed really. SKS
+; 12-Aug-88     Read mouse position from buffer forces inside bounding box
+; 02-Sep-88     Buffered mouse coords stored in absolute terms, not relative to
+;                origin at time of click. Made relative after reading out and
+;                clipping to bounding box. Mouse event coords are still relative
+;                to origin at time of click.
+; 06-Feb-91     LastLED added to stop unnecessary LED transmissions
+;
+; 24-Feb-93 SMC Split off Archimedes keyboard driver into file KbdDrA1.
+;               Split off mouse stuff into file mouse.
+;               Added new interfaces for generic keyboard driver.
+; 23-Aug-93 SMC GotKbId now sets KbId but remembers the last in LastKbId.
+; 24-Aug-93 SMC Key handler can now get keyboard stuff going.
+; 18-Nov-93 SMC Fixed bug in key handler stuff (no handler => KeyVec=-1).
+; 25-Nov-93 SMC Key handler and keyboard driver now trigger each other correctly.
+; 25-Apr-94 RCM ReadCh modified for Stork's power saving scheme.
+;
+
+        GBLL    MouseBufferFix
+MouseBufferFix SETL {TRUE}
+
+
+        [ :LNOT: AssemblingArthur
+        GBLL    redirectinkey
+redirectinkey   SETL    {FALSE}
+        ]
+
+; *****************************************************************************
+;
+;       Entry point for keyboard code - Initialisation
+;
+
+KeyInit ROUT
+        MOV     R11, #KeyWorkSpace
+        Push    R14
+
+        [ AssemblingArthur
+        TEQP PC,#I_bit :OR: SVC_mode
+        |
+        SWI     OS_IntOff
+        SWI     OS_EnterOS
+        ]
+
+ [ Keyboard_Type = "A1A500"
+        BL      A1KeyInit
+ ]
+
+        [ :LNOT: AssemblingArthur
+        MOV     R0, #RdchV
+        ADRL    R1, NewRdch
+        SWI     OS_Claim
+        ]
+
+ [ AssembleA1KeyHandler
+        ADRL    R0, NewKeyStruct        ; point to new structure for now
+        STR     R0, KeyVec
+ |
+        MOV     R0, #-1                 ; no default key handler
+        STR     R0, KeyVec
+ ]
+
+        MOV     R0, #&FF                ; indicate no previous keyboard id
+        STRB    R0, LastKbId
+        STRB    R0, KbId
+
+        BL      ClearKbd
+
+ [ Keyboard_Type = "A1A500"
+        BL      A1Reset
+ ]
+
+        [ :LNOT: AssemblingArthur
+        TEQP    PC, #I_bit
+        MOVNV   R0, R0
+        ]
+
+        Pull    PC                      ; go back to user
+
+; *****************************************************************************
+;
+;       KeyPostInit - Called after modules have initialised
+;
+
+KeyPostInit ROUT
+        Push    R14
+        MOV     R11, #KeyWorkSpace
+        PHPSEI                          ; disable interrupts round this bit
+        Push    R14                     ; save I_bit indication
+        LDRB    R1, LastKbId
+        TEQ     R1, #&FF                ; if no keyboard initialised yet then
+        LDREQB  R1, KbId                ;   try now
+        BLEQ    GotKbId
+        Pull    R14                     ; restore I_bit indication
+        PLP                             ; set I_bit from this
+
+        LDROSB  R1, LastBREAK           ; is it a soft reset ?
+        TEQ     R1, #0
+        Pull    PC, EQ                  ; if so, then exit
+
+        MOV     R0, #OsbyteSetCountry
+        LDROSB  R1, Country
+        SWI     XOS_Byte
+        Pull    PC
+
+; *****************************************************************************
+
+ClearKbd ROUT
+        Push    R14
+
+        MOV     R0, #&FF
+        STRB    R0, CurrKey             ; no current key
+        STRB    R0, OldKey
+        STRB    R0, LastLED
+
+; Set up keyboard table
+
+        MOV     R0, #0                  ; All keys up
+        STR     R0, KeysDown            ; zero 160 bits = 5 words
+        STR     R0, KeysDown +4
+        STR     R0, KeysDown +8
+        STR     R0, KeysDown +12
+        STR     R0, KeysDown +16
+
+        Pull    PC
+
+; *****************************************************************************
+;
+;       UpdateLEDs - Update the LED(s) from the keyboard status byte
+;
+; in:   R11 -> keyboard workspace
+;       R12 -> IOC
+;
+; out:  R0, R1 corrupted
+;
+
+UpdateLEDs ROUT
+        LDRB    r0, KbId                ; get keyboard id
+        TEQ     r0, #&FF                ; if not found yet
+        MOVEQ   pc, lr                  ; then exit
+
+        LDROSB  r0, KeyBdStatus         ; Build current LED state byte.
+        TST     r0, #KBStat_NoCapsLock
+        MOVEQ   r1, #1
+        MOVNE   r1, #0
+        TST     r0, #KBStat_NoNumLock
+        ORREQ   r1, r1, #2
+        TST     r0, #KBStat_ScrollLock
+        ORRNE   r1, r1, #4
+
+        LDRB    r0, LastLED             ; Only update if different.
+        TEQ     r0, r1
+        MOVEQ   pc,lr
+
+        STRB    r1, LastLED
+
+        MOV     r0, #3
+ [ AssembleKEYV
+        Push    "r10-r12,lr"
+        MOV     r11, pc                 ; Save current PSR.
+        TEQP    pc, #SVC_mode + I_bit   ; Call KEYV in SVC mode, no IRQs.
+        MOV     r10, #KEYV
+        Push    "lr"                    ; Save SVC lr.
+        BL      CallVector
+        Pull    "lr"                    ; Restore SVC lr.
+        TEQP    r11, #0                 ; Go back to old mode.
+        NOP
+        Pull    "r10-r12,pc"
+ |
+  [ Keyboard_Type = "A1A500"
+        B       A1KeyVec
+  |
+        MOV     pc, lr
+  ]
+ ]
+
+; *****************************************************************************
+;
+;       Handle new keyboard id.
+;       In:     r1 = keyboard id
+;               r11 = KeyWorkSpace
+;       Out:    preserve flags
+;
+GotKbId
+        Push    lr
+
+        STRB    r1, KbId                ; Store the new keyboard id.
+
+        LDR     r0, KeyVec
+        Push    r0                      ; Save old key handler so we know if it's changed.
+
+        LDRB    r8, LastKbId
+        TEQ     r8, r1                  ; If we have a different keyboard id then
+        BLNE    IssueKeyboardService    ;   issue service.
+
+        LDR     lr, KeyVec              ; Get possibly new key handler.
+        Pull    r0                      ; And old handler.
+        TEQ     r0, lr                  ; If key handler has not changed then
+        BLEQ    KeyboardEnable          ;   handler has not initialised with OS_InstallKeyHandler so we try.
+
+        Pull    pc,,^
+
+; *****************************************************************************
+; Initialise keyboard handler and enable keyboard driver.
+;
+; In:   r1 = keyboard id
+;
+KeyboardEnable
+        Push    lr
+
+        BL      ClearKbd
+
+        LDR     r0, KeyVec
+        CMP     r0, #-1                 ; If no key handler
+        LDRNEB  r1, KbId                ;     or no keyboard yet then
+        TEQNE   r1, #&FF
+        Pull    pc, EQ                  ;   can't initialise.
+
+        STRB    r1, LastKbId            ; Remember last keyboard id that initialised.
+
+        LDR     r8, [r0, #KVInit]       ; Initialise key handler.
+        ADD     r8, r8, r0
+        BL      CallUserKeyCode
+
+        MOV     r0, #4                  ; Initialise keyboard driver.
+ [ AssembleKEYV
+        MOV     r10, #KEYV
+        BL      CallVector
+ |
+  [ Keyboard_Type = "A1A500"
+        BL      A1KeyVec
+  ]
+ ]
+        Pull    pc
+
+ [ AssembleKEYV
+; *****************************************************************************
+;
+;       Default KEYV handler (deal with keyboard id, keys up/down).
+;
+;       In:     r0 = reason code 0
+;               r1 = keyboard id
+;       or
+;               r0 = reason code 1 or 2
+;               r1 = key code
+;
+KeyVector ROUT
+        CMP     r0, #3                  ; If not id/key up/key down then
+        Pull    pc, CS                  ;  just claim call.
+
+        Push    "r0-r12"
+
+        MOV     r11, #KeyWorkSpace
+
+        TEQ     r0, #0                  ; If keyboard id then
+        BLEQ    GotKbId                 ;   handle it
+        Pull    "r0-r12,pc",EQ          ;   and claim call.
+ |
+GotKey ROUT
+        Push    lr
+ ]
+
+        MOV     r2, r1
+        SUB     r1, r0, #1
+
+        CMP     R2, #&A0
+        BCS     %FT05
+        ADR     R0, KeysDown
+        MOV     lr, R2, LSR #5
+        LDR     lr, [R0, lr, LSL #2]!   ; load appropriate word
+        MOV     R3, #&80000000          ; index 0 is in top bit
+        TEQ     r1, #0
+        BNE     %FT03
+
+        TST     lr, R3, ROR R2          ; if going up and key already up (keyboard reinitialised) then
+        BEQ     %FT50                   ;   nothing to do
+        B       %FT04                   ; else clear flag, generate event etc.
+03
+        TST     lr, R3, ROR R2          ; if going down and key already down (weird) then
+        BNE     %FT50                   ;   nothing to do else...
+04
+        EOR     lr, lr, R3, ROR R2      ; switch state of flag
+        STR     lr, [R0]                ; store back
+05
+        BL      KeyboardEvent           ; generate key up/down event
+
+        BL      CheckForShiftingKey
+        BCC     %FT10                   ; [not shifting key]
+
+        BL      CallSpecialReturnNChars
+        B       %FT50
+
+10
+        TEQ     r1, #0                  ; if key up then
+        BEQ     %FT30                   ;   go and deal with it
+
+        LDRB    R0, CurrKey
+        TEQ     R0, #&FF                ; have we got a current key ?
+        BEQ     %FT20
+
+        LDRB    R1, OldKey
+        TEQ     R1, #&FF                ; have we got an old key ?
+        BNE     %FT50                   ; ignore new - we've got 2 down already
+
+        STRB    R0, OldKey              ; make current key old
+20
+        STRB    R2, CurrKey             ; update current
+        MOV     R0, #2
+        STRB    R0, Debouncing
+        STRB    R0, AutoRepeatCount     ; generate char after 2 100Hz ticks
+
+        B       %FT50
+
+30
+        LDRB    R0, OldKey
+        TEQ     R0, R2                  ; is it old key going up ?
+        BNE     %FT40
+
+; Old key going up
+
+        LDRB    R0, CurrKey             ; current key is one to ignore in scan
+        BL      ScanKeys
+
+        STRPLB  R0, OldKey              ; found key, so current -> old
+        BPL     %BT20                   ; and R2 -> current
+
+        MOV     R0, #&FF                ; else mark old key invalid
+        STRB    R0, OldKey
+        B       %FT50                   ; and return
+
+40
+        LDRB    R1, CurrKey
+        TEQ     R1, R2                  ; is it current key going up ?
+        BNE     %FT50                   ; not interested if not
+
+        BL      ScanKeys                ; R0 was OldKey
+        BPL     %BT20                   ; was a key so make that current
+
+        STRB    R2, CurrKey             ; mark current key up (R2 = -1)
+
+50
+ [ AssembleKEYV
+        Pull    "r0-r12,pc"
+ |
+        Pull    pc
+ ]
+
+; *****************************************************************************
+;
+;       KeyboardEvent - Generate key up/down event
+;
+; in:   R1 = 0 for up, 1 for down
+;       R2 = key index
+;
+
+KeyboardEvent ROUT
+        LDRB    R3, KbId                ; tell event user the keyboard id
+        MOV     R0, #Event_Keyboard
+        B       OSEVEN
+
+; *****************************************************************************
+;
+;       Scan keyboard for keys down, ignoring key number R0 and shifting keys
+;
+; in:   R0 = key number to ignore
+;
+; out:  N=0 => R2 = key number found
+;       N=1 => no key found; R2 = -1
+;       R0 preserved
+;
+
+ScanKeys ROUT
+        Push    "R0, R14"
+        ADR     R1, KeysDown
+        MOV     R2, #4
+10
+        LDR     R3, [R1, R2, LSL #2]    ; get the word
+        TEQ     R3, #0                  ; if any keys in this down, skip
+        BNE     %FT20
+15
+        SUBS    R2, R2, #1              ; N=1 last time round
+        BPL     %BT10
+        Pull    "R0, PC"
+
+20
+        MOV     R2, R2, LSL #5          ; multiply by 32
+        ADD     R2, R2, #32             ; and add 32
+30
+        TEQ     R3, #0                  ; no more bits ?
+        MOVEQ   R2, R2, LSR #5          ; then reset R2 to word offset
+        BEQ     %BT15                   ; and continue word loop
+        SUB     R2, R2, #1              ; decrement key number
+        MOVS    R3, R3, LSR #1          ; shift out bit
+        BCC     %BT30
+
+        CMP     R2, R0                  ; is it old key (C=1 if it is)
+        BLNE    CheckForShiftingKeyR0R3 ; check that it's not shifting key
+        BCS     %BT30                   ; C=1 => invalid, so loop
+
+        TEQ     R2, #0                  ; N := 0
+        Pull    "R0, PC"
+
+; *****************************************************************************
+;
+;       CheckForShiftingKey - either going down or going up
+;
+; in:   R2 = key number
+;
+; out:  C=1 <=> is shifting key, so don't set current key etc
+;       R0 -> key structure
+;       R4 = shifting key index, or 0 if not shifting key
+;       R3,R5 undefined
+;       R1,R2,R6-R12 preserved
+;
+
+CheckForShiftingKeyR0R3 ROUT              ; version that saves R0, for ScanKeys
+        Push    "R0,R3,R14"
+        BL      CheckForShiftingKey
+        Pull    "R0,R3,PC"
+
+CheckForShiftingKey ROUT
+        LDR     R0, KeyVec
+ [ :LNOT: AssembleA1KeyHandler
+        CMP     R0, #-1
+        MOVEQ   PC, R14
+ ]
+        LDR     R3, [R0, #KVKeyTranSize] ; maximum internal key number +1
+        CMP     R2, R3                  ; is it outside table ?
+        LDRCC   R3, [R0, #KVKeyTran]    ; no, R3 := offset to keytran
+        ADDCC   R3, R3, R0              ; R3 -> keytran
+        LDRCC   R3, [R3, R2, LSL #2]    ; R3 = table word for this key
+        CMNCC   R3, #1                  ; C=1 <=> outside table or is special
+        MOVCC   PC, R14                 ; can't be shifting key
+
+        LDR     R3, [R0, #KVShiftingList] ; R3 = offset to shifting key list
+        LDRB    R4, [R3, R0]!           ; R4 = length of shifting key list
+        TEQ     R4, #0
+10
+        LDRNEB  R5, [R3, R4]
+        TEQNE   R5, R2
+        SUBNES  R4, R4, #1
+        BNE     %BT10
+
+        CMP     R4, #1                  ; C=1 <=> shifting key
+        MOV     PC, R14                 ; not one of the shifting keys
+
+; *****************************************************************************
+;
+;       CallSpecialCode - Call code for a special key
+;
+; in:   R0 -> Key structure
+;       R1 = 0 for up, 1 for down (shifting keys); 2 for first, 3 for repeat
+;       R2 = key number
+;
+
+CallSpecialCode ROUT
+        ADR     R6, NullCharList
+        LDR     R3, [R0, #KVSpecialList] ; R3 = offset to special list
+        LDRB    R4, [R3, R0]!           ; R4 = length of special list
+        TEQ     R4, #0
+        MOVEQ   PC, R14                 ; no special keys, so can't be one
+10
+        LDRB    R5, [R3, R4]
+        TEQ     R5, R2
+        BEQ     %FT20
+        SUBS    R4, R4, #1
+        BNE     %BT10
+        MOV     PC, R14
+
+20
+        LDR     R3, [R0, #KVSpecialCodeTable] ; R3 = offset to special table
+        ADD     R3, R3, R0              ; R3 -> special code table
+        SUB     R5, R3, #4              ; 0th entry is for 1st special
+
+        LDR     R8, [R5, R4, LSL #2]    ; R8 = offset to code for this special
+        ADD     R8, R8, R3              ; R8 = address of code for this special
+        ADR     R3, ReturnVector
+
+; and drop thru to ...
+
+CallUserKeyCode ROUT
+        Push    R14
+        LDROSB  R5, KeyBdStatus
+        LDRB    R7, PendingAltType
+        BL      %FT10
+        STRB    R7, PendingAltType
+        STROSB  R5, KeyBdStatus, R12
+        Pull    R14
+        MOV     R12, #IOC
+        B       UpdateLEDs
+
+10
+        ADRL    R12, UserKeyWorkSpace
+        MOV     PC, R8
+
+NullCharList
+        =       0
+        ALIGN
+
+ReturnVector
+        B       MouseButtonChange
+        B       DoBreakKey
+
+; *****************************************************************************
+;
+;       Centisecond tick routine
+;
+; out:  R12 corrupted
+
+CentiSecondTick ROUT
+        Push    "R11, R14"
+        MOV     R11, #KeyWorkSpace
+        MOV     R12, #IOC
+
+        [ PollMouse
+        MOV     R0, #K1rqmp
+        STRB    R0, RequestMouse
+        TXon    R0
+        ]
+
+        LDR     R0, InkeyCounter
+        SUBS    R0, R0, #1              ; decrement
+        STRCS   R0, InkeyCounter        ; store back unless was frozen at 0
+
+        LDRB    R2, CurrKey
+        TEQ     R2, #&FF
+        Pull    "R11,PC", EQ            ; no current key, so no auto-repeat
+
+        BL      UpdateLEDs              ; update LEDs from keyboard status
+
+        LDRB    R0, AutoRepeatCount
+        SUBS    R0, R0, #1              ; decrement count (if frozen then C:=0)
+        STRHIB  R0, AutoRepeatCount ; store back if now non-zero and not frozen
+        Pull    "R11,PC", NE            ; return if non-zero or was frozen
+
+        LDRB    R1, Debouncing          ; get debounce flag
+        TEQ     R1, #0
+
+        STRNEB  R0, Debouncing          ; if not zero, zero it
+        LDROSB  R0, KeyRepDelay, NE     ; and load delay
+        MOVNE   R1, #2                  ; indicate first time
+
+        LDROSB  R0, KeyRepRate, EQ      ; if zero, then load repeat
+        MOVEQ   R1, #3                  ; indicate subsequent time
+
+        STRB    R0, AutoRepeatCount     ; in any case, store back
+
+        Push    "R4-R10"                ; save registers
+        BL      GenerateChar            ; R2 = key number
+        Pull    "R4-R11,PC"
+
+; *****************************************************************************
+;
+;       DoBreakKey - Called by key handler when break key up or down
+;
+; in:   R0 -> key structure
+;       R1 = 0 for up, 1 for down (shouldn't be 2 or 3)
+;       R2 = ARM internal key number
+;       R3 = address of ReturnVector
+;       R4 = special number 1..n
+;       R5 = keyboard status
+;
+; out:  R6 -> list of chars to return
+;
+
+DoBreakKey ROUT
+        TST     R5, #KBStat_ShiftEngaged        ; shift down ?
+        MOVEQ   R3, #31
+        MOVNE   R3, #29
+
+        TST     R5, #KBStat_CtrlEngaged         ; ctrl down ?
+        BICNE   R3, R3, #4
+
+        LDROSB  R2, BREAKvector
+        MOVS    R2, R2, LSL R3                  ; put relevant bits in C,N
+
+        MOVCS   PC, R14                         ; 2 or 3 => ignore
+        BPL     %FT10                           ; 0 => do a reset
+
+        TEQ     R1, #1                          ; is it key down ?
+        ADREQ   R6, EscList                     ; yes, return ESCAPE
+        MOV     PC, R14                         ; else just return
+
+10
+        [ AssemblingArthur
+        TEQ     R1, #0                          ; is it key up ?
+        MOVNE   PC, R14                         ; no, then return
+
+; This entry point is used by new SWI OS_Reset (TMD 06-Jan-94)
+
+PerformReset
+
+; offer the pre-reset service
+
+        TEQP    PC, #ARM_CC_Mask                ; set FI bits, SVC mode
+
+; Ensure any CMOS operation aborted
+
+        MOV     R1,#16                          ; Two bytes in case RTC transmitting
+20      BL      Start                           ; Start/clock edge
+        BL      Stop
+        SUBS    R1,R1,#1
+        BNE     %BT20
+
+        MOV     R1, #Service_PreReset
+        IssueService
+
+        TEQP    PC, #ARM_CC_Mask                ; set FI bits, SVC mode
+                                                ; just in case!
+        B       CONT_Break
+        |
+        MOV     PC, R14                         ; do nowt
+        ]
+
+EscList
+        =       1, &1B
+        ALIGN
+
+; *****************************************************************************
+;
+;       Generate a character in keyboard buffer, if necessary
+;
+; in:   R1 = 2 if first press; 3 if repetition
+;       R2 = key number
+;       R12 -> IOC
+;
+
+GenerateChar ROUT
+        Push    R14
+        LDR     R0, KeyVec
+ [ :LNOT: AssembleA1KeyHandler
+        CMP     R0, #-1
+        Pull    PC,EQ
+ ]
+        LDR     R3, [R0, #KVKeyTranSize]        ; get size
+        CMP     R2, R3                          ; if outside table
+        BCS     %FT04                           ; then assume special
+
+        LDR     R3, [R0, #KVKeyTran]            ; R3 = offset to KeyTran
+        ADD     R3, R3, R0                      ; R3 -> KeyTran
+
+; now modify for CTRL and SHIFT
+
+        LDROSB  R5, KeyBdStatus
+
+        TST     R5, #KBStat_CtrlEngaged
+        ADDNE   R3, R3, #2
+
+        TST     R5, #KBStat_ShiftEngaged
+        ADDNE   R3, R3, #1
+
+        LDRB    R3, [R3, R2, LSL #2]            ; get real code
+
+; apply CAPS lock modifying
+
+        BIC     R6, R3, #&20                    ; get upper-case code
+        CMP     R6, #"A"
+        RSBCSS  R6, R6, #"Z"                    ; is it alphabetic ?
+        BCC     %FT20
+
+        TST     R5, #KBStat_ShiftEnable         ; if SHCAPS
+        EORNE   R3, R3, #&20                    ; then swap case
+
+        TSTEQ   R5, #KBStat_NoCapsLock          ; else if CAPS
+        BICEQ   R3, R3, #&20                    ; force upper case
+20
+        TEQ     R3, #&FF                        ; is it a special ?
+        BEQ     %FT04                           ; [yes, so skip]
+
+        LDROSB  R6, ESCch                       ; if ESCAPE character
+        TEQ     R3, R6
+        LDROSB  R6, ESCaction, EQ               ; and normal ESCAPE action
+        TEQEQ   R6, #0
+        LDROSB  R6, ESCBREAK, EQ                ; and ESCAPE not disabled
+        TSTEQ   R6, #1
+        BICEQ   R5, R5, #KBStat_PendingAlt      ; then cancel pending alt
+        STROSB  R5, KeyBdStatus, R6, EQ         ; and store back
+
+        TST     R5, #KBStat_PendingAlt          ; is there a pending Alt ?
+        BNE     ProcessPendingAlt
+
+        TEQ     R3, #0                          ; is it NUL ?
+        BNE     %FT10                           ; no, so skip
+
+        ADR     R6, NULNULList                  ; then insert NUL NUL
+        B       ReturnNChars
+
+CallSpecialReturnNChars
+        Push    R14
+04
+        BL      CallSpecialCode
+
+ReturnNChars
+        LDRB    R3, [R6], #1                    ; R1 = count of characters
+
+; TMD 25-Sep-89: Fix bug which resulted in Break key (acting as Escape) not
+; working if buffer was full - only count spaces if more than 1 character going
+; into buffer
+
+ [ {TRUE}
+        CMP     R3, #1
+        Pull    PC, CC                          ; no chars, so exit now
+        BEQ     %FT05                           ; only 1 char, don't count
+ |
+        TEQ     R3, #0                          ; no chars?
+        Pull    PC, EQ                          ; then exit now
+ ]
+
+        MOV     R1, #Buff_Key
+        CMP     PC, #0                          ; C=1, V=0 so count spaces
+        BL      CnpEntry
+        ORR     R1, R1, R2, LSL #8              ; R1 = number of spaces
+        CMP     R3, R1                          ; are there enough ?
+        Pull    PC, HI                          ; no, then forget them
+
+05
+        LDRB    R2, [R6], #1                    ; send chars
+        BL      InsertKeyZCOE                   ; one at a time
+        SUBS    R3, R3, #1
+        BNE     %BT05
+        Pull    PC
+
+10
+        Pull    R14                             ; restore stacked R14
+        MOV     R2, R3
+
+; and drop thru to ...
+
+; *****************************************************************************
+;
+;       InsertKeyZCOE - Insert key zeroing count on escape
+;
+; in:   R2 = character
+;
+
+InsertKeyZCOE
+        LDROSB  R0, KeyBdDisable                ; disable insertion of codes ?
+        TEQ     R0, #0
+        MOVNE   PC, R14                         ; [disabled]
+        LDROSB  R0, ESCch                       ; escape character
+        TEQ     R0, R2                          ; if is esc char
+        LDROSB  R0, ESCaction, EQ
+        TEQEQ   R0, #0                          ; and FX229,0
+
+        STREQB  R0, AutoRepeatCount             ; then zero repeat counter
+
+; and drop thru to ...
+
+; *****************************************************************************
+;
+;       RDCHS - Insert character into keyboard buffer
+;
+; in:   R2 = character
+;
+
+RDCHS   ROUT
+        MOV     R1, #Buff_Key                   ; keyboard buffer id
+
+; Insert character R2 into buffer R1, checking for escape character
+
+        B       DoInsertESC
+
+; *****************************************************************************
+
+NULNULList                                      ; list for returning NUL NUL
+        =       2, 0, 0
+        ALIGN
+
+; *****************************************************************************
+
+ProcessPendingAlt
+        ADR     R6, NullCharList
+        LDR     R8, [R0, #KVPendingAltCode]
+        ADD     R8, R8, R0
+        BL      CallUserKeyCode
+        B       ReturnNChars
+
+; *****************************************************************************
+;
+;       Read character entry point
+;
+; in:   -
+; out:  R0 = character
+;       C=1 => ESCAPE
+;       R1-R13 preserved
+;
+
+NewRdch
+        Push    "R1-R4,R11"
+        MOV     R11, #KeyWorkSpace
+        MOV     R4, #1                  ; indicate RDCH not INKEY
+        BL      RdchInkey
+        Pull    "R1-R4,R11,PC"
+
+; *****************************************************************************
+;
+;       RDCH/INKEY
+;
+; in:   R4 = 0  => INKEY
+;       R4 <> 0 => RDCH                 ; *** TMD This changed 25-Apr-91 ***
+;
+; out:  V=1 => error (and possibly R0 -> error block if you're lucky!)
+;
+
+RdchInkey ENTRY
+
+; Enable interrupts so that keyboard can work properly
+
+        TEQP    pc, #SVC_mode
+
+ [ redirectinkey
+        MOV     r1, #0
+        LDRB    r1, [r1, #RedirectInHandle]
+        TEQ     r1, #0
+        BEQ     %FT10
+
+; Tutu doesn't believe that an escape condition should break redirection
+; - similar to exec if you turn off escape ack side-effects
+
+        SWI     XOS_BGet                ; get byte from redirection handle
+        BVS     RedirectBadExit
+        BCC     ReturnChar              ; (C=0)
+
+; EOF, so close redirect file and read from exec file or keyboard
+
+; stop redirecting, BEFORE closing file, in case the CLOSE gets an error
+
+        MOV     r0, #0
+        STRB    r0, [r0, #RedirectInHandle] ; Convenient, huh ?
+        SWI     XOS_Find                ; close file (R0=0, R1=handle)
+        EXIT    VS
+
+10
+ ]
+
+; First check for EXEC file
+
+        LDROSB  R1, ExecFileH           ; read EXEC handle
+        TEQ     R1, #0
+        BEQ     %FT20                   ; no exec file
+
+        SWI     XOS_BGet                ; get byte from exec handle
+        BVS     ExecBadExit
+        BCC     ReturnChar              ; (C=0)
+
+; EOF, so close exec file and read from keyboard
+
+; stop EXECing, BEFORE closing file, in case the CLOSE gets an error
+
+        STROSB  R0, ExecFileH, R0       ; (STROSB sets temp reg to 0)
+        SWI     XOS_Find                ; close file (R0=0, R1=handle)
+        EXIT    VS
+20
+        Push    "R5,R6"
+        MOV     R5, #0
+ [ StorkPowerSave
+        LDRB    R5, [R5, #PortableFlags] ; 0 if not a portable, else Portable_Features result
+ |
+        LDRB    R5, [R5, #PortableFlag] ; 0 if want to try issuing SWI, 1 if know it's hopeless
+ ]
+
+RdchLoop
+        MOV     R0, #0
+        LDRB    R0, [R0, #ESC_Status]
+        MOVS    R0, R0, LSL #(32-6)     ; shift relevant bit into carry
+        MOVCS   R0, #27                 ; escape detected
+        BCS     ReturnChar2
+
+        LDROSB  R1, InputStream         ; 0 => keyboard, 1 => RS423
+        BL      RDCHG
+        BCC     ReturnChar2
+
+; Sam's hack to call the callback vector if appropriate
+
+        [ AssemblingArthur
+        MOV     R0, #0
+        LDRB    R14, [R0, #CallBack_Flag]
+        TST     R14, #CBack_VectorReq
+        BLNE    process_callback_chain
+        ]
+
+; here endeth the hack
+
+        TEQ     R4, #0                  ; EQ => inkey, NE => rdch
+        LDREQ   R0, InkeyCounter        ; if inkey
+        TEQEQ   R0, #0                  ; and count expired
+        BEQ     InkeyTimeout
+
+ [ StorkPowerSave
+        TST     R5, #PortableFeature_Idle
+        SWINE   Portable_Idle
+        TST     R5, #PowerSave
+        BNE     RdchLoop                ; if we've gone slow already, then loop
+        TST     R5, #PortableFeature_Speed
+        BEQ     RdchLoop                ; if speed change doesn't work, then loop
+        MOV     R0, #1                  ; go slow
+        MOV     R1, #0
+        SWI     XPortable_Speed         ; out: R0 = old speed, R1 = new speed
+        ORRVC   R5, R5, #PowerSave      ; if OK, indicate power save mode
+        MOVVC   R6, R0                  ; and remember old speed
+        MOVVS   R5, #0                  ; if got error, then indicate we don't want to try again
+        STRVSB  R5, [R5, #PortableFlags] ; and store this back for future RDCHs
+ |
+        CMP     R5, #1                  ; if we've gone slow already (2), or there's no portable module (1), then loop
+        BCS     RdchLoop
+        MOV     R0, #1                  ; go slow
+        MOV     R1, #0                  ; ignore old speed
+        SWI     XPortable_Speed         ; out: R0 = old speed, R1 = new speed
+        MOVVC   R5, #2                  ; if OK, indicate we've successfully gone slow
+        MOVVC   R6, R0                  ; and remember old speed
+        MOVVS   R5, #1                  ; if got error, then indicate we don't want to try again
+        STRVSB  R5, [R5, #PortableFlag-1] ; and store this back for future RDCHs
+ ]
+        B       RdchLoop
+
+InkeyTimeout
+        MOV     R0, #&FF                ; indicate timeout
+        SEC                             ; and set carry
+ReturnChar2
+        TEQ     R5, #2                  ; NB preserves carry
+        BLEQ    RestoreSpeed
+        Pull    "R5,R6"
+ReturnChar
+        CLRPSR  V_bit, R14
+        EXIT
+
+RestoreSpeed ENTRY "R0"
+        MOV     R0, R6                  ; restore old speed
+        MOV     R1, #0                  ; AND mask of 0
+        SWI     XPortable_Speed
+        EXITS                           ; restore R0 and carry
+
+ExecBadExit                             ; got an error from BGET
+        Push    R0                      ; save error pointer
+        STROSB  R0, ExecFileH, R0       ; (STROSB sets temp reg to 0)
+        SWI     XOS_Find                ; close file (R0=0, R1=handle)
+        Pull    "R1, R14"               ; pull registers
+        MOVVC   R0, R1                  ; if closed OK, then restore old error
+        ORRS    PC, R14, #V_bit         ; still indicate error
+
+ [ redirectinkey
+RedirectBadExit                         ; got an error from BGET
+        BL      RemoveOscliCharJobs     ; preserves r0
+        Pull    "R14"                   ; pull register
+        ORRS    PC, R14, #V_bit         ; still indicate error
+ ]
+
+; *****************************************************************************
+;
+;       RDCHG - Fetch character from input buffer
+;       Expand soft keys as necessary
+;       Pass cursor control keys to VDU driver
+;       Return carry set if character not available
+;
+; in:   R1 = input buffer id (0 => keyboard, 1 => RS423)
+
+RDCHG   ROUT
+        Push    R14
+
+; insert check here for ECONET interception of RDCH
+
+RDCHNM
+        LDROSB  R0, SoftKeyLen          ; are we expanding a soft key
+        TEQ     R0, #0
+        BEQ     RDCHG1                  ; not expanding
+
+        LDROSB  R2, RS423mode
+        TST     R1, R2                  ; if RS423 and 8 bit data
+        BNE     RDCHG1                  ; ignore soft keys
+
+        LDR     R2, SoftKeyPtr
+        LDRB    R2, [R2, -R0]           ; get character out of buffer
+
+        SUB     R0, R0, #1              ; decrement character count
+        STROSB  R0, SoftKeyLen, R3      ; store back
+
+        MOV     R0, R2                  ; put character in R0
+        CLC                             ; and exit with carry clear
+        Pull    PC
+
+RDCHG1
+        BL      KeyREMOVECheckRS423     ; remove character, if none, exit CS
+
+        LDROSB  R2, RS423mode           ; 0 => treat RS423 as keyboard
+        TST     R1, R2                  ; NZ => let RS423 deliver 8-bit codes
+        BNE     RDCHGCLC
+
+        TEQ     R0, #0                  ; is it NUL ?
+        BNE     %FT10
+
+        BL      KeyREMOVECheckRS423     ; get another char, if none then
+                                        ; spurious, so ignore
+
+        TEQ     R0, #0                  ; is it NUL NUL ?
+        BNE     RDCHGCLC                ; no, then return this character
+
+        LDRB    R2, [R0, #OsbyteVars + :INDEX: IPbufferCh]!
+                                        ; R0 was 0, so now -> 1st of 8 keybases
+        ADD     R3, R0, #8
+05
+        TEQ     R2, #2                  ; is this key base = 2 ?
+        MOVEQ   R0, #0                  ; if so then return NUL NUL
+        BEQ     ReturnNULR0
+        LDRB    R2, [R0, #1]!           ; load next key base
+        TEQ     R0, R3                  ; if not tried all of them
+        BNE     %BT05                   ; then loop
+        MOV     R0, #0                  ; no special key bases,
+                                        ; so just return NUL
+10
+        TST     R0, #&80
+        BEQ     RDCHGCLC
+
+; now check for cursor key movement
+
+        AND     R3, R0, #&0F            ; save bottom nybble
+        CMP     R3, #&0B                ; is it a cursor key ?
+        BCC     NotCursorKey
+
+        TST     R0, #&40                ; don't let Cx-Fx be cursor keys
+        BNE     NotCursorKey
+
+        LDROSB  R2, CurEdit             ; FX 4 state
+        CMP     R2, #1
+        ADDLS   R0, R3, #&87-&0B        ; 0 or 1 => force in range &87-&8B
+        BCC     ItsCursorEdit           ; 0 => cursor edit
+        BEQ     RDCHGCLC                ; 1 => return these codes
+
+NotCursorKey
+        MOV     R0, R0, LSR #4
+        EOR     R0, R0, #&0C            ; 4..7, 0..3
+        LDRB    R2, [R0, #OsbyteVars+IPbufferCh-OSBYTEFirstVar]
+                                        ; get key variable
+        CMP     R2, #1                  ; is it 0 (ignore) or 1 (softkey)
+        BCC     RDCHG1                  ; get another char if 0
+
+        BEQ     ExpandSoftKey           ; expand soft key if 1
+
+        TEQ     R2, #2                  ; is it special Compact option ?
+        EOREQ   R0, R0, #&0C            ; undo that mangling !
+        ORREQ   R0, R3, R0, LSL #4      ; if so, then return NUL <code>
+        BEQ     ReturnNULR0
+
+        ADD     R0, R2, R3              ; add offset to base
+        AND     R0, R0, #&FF            ; make it wrap
+
+RDCHGCLC
+        CLC
+        Pull    PC
+
+
+ItsCursorEdit
+        LDROSB  R2, WrchDest
+        TST     R2, #2                  ; if wrch not to VDU
+        BNE     RDCHG1                  ; then ignore character
+
+        Push    "R1,R4-R12"
+        [ AssemblingArthur
+        VDWS    WsPtr
+        BL      DoCursorEdit
+        |
+        BL      DCE10
+        ]
+        Pull    "R1,R4-R12"
+
+        BCS     RDCHG1                  ; no character yet, so loop
+        Pull    PC                      ; NB carry clear - no ESCAPE !
+
+        [ :LNOT: AssemblingArthur
+DCE10
+        MOV     R1, #VduDriver
+        ADD     PC, R1, #CursorEdit
+        ]
+
+; *****************************************************************************
+;
+;       ReturnNULR0 - Return NUL followed by R0 from RDCH
+;
+
+ReturnNULR0 ROUT
+        ADR     R2, SoftKeyExpand       ; store code in SoftKeyExpand +0
+        STRB    R0, [R2], #1            ; and set ptr to SoftKeyExpand +1
+        STR     R2, SoftKeyPtr
+        MOV     R2, #1                  ; set key length to 1
+        STROSB  R2, SoftKeyLen, R0      ; (sets R0 to 0!)
+        B       RDCHGCLC                ; return NUL as first character
+
+; *****************************************************************************
+
+KeyREMOVECheckRS423 ROUT
+        Push    R14
+        BL      KeyREMOVE
+        Pull    "R14, PC", CS           ; pull stacked R14 if CS
+
+; new code inserted here 14/8/87 to try to reenable RTS for RS423 input
+
+ [ DriversInKernel
+        TEQ     R1, #1                  ; RS423 input ?
+        Pull    PC, NE                  ; no, then exit
+        Push    "R0,R1,R12"             ; preserve char + buffer id + R12
+        BYTEWS  R12
+        BL      RSETX                   ; reenable RTS if now enough spaces
+        Pull    "R0,R1,R12, PC"         ; restore char, buffer id and exit
+ |
+        Pull    PC
+ ]
+
+; *****************************************************************************
+
+KeyREMOVE
+        Push    "R10,R12,R14"
+        CLRV                                    ; do remove not examine
+        MOV     R10, #REMV
+        B       GoVec
+
+
+; expand a soft key as a variable (R3 = key number)
+
+ExpandSoftKey ROUT
+        Push    "R1,R4"
+        BL      SetupKeyName
+        ADR     R1, SoftKeyExpand
+        MOV     R2, #255                        ; max length of string
+        MOV     R3, #0                          ; no name pointer
+        MOV     R4, #VarType_Expanded
+        SWI     XOS_ReadVarVal
+
+        Pull    "R1,R4", VS
+        BVS     RDCHG1                          ; no string or bad
+
+        STROSB  R2, SoftKeyLen, R0              ; store length (may be zero)
+        ADD     R1, R1, R2                      ; R1 -> last char+1
+        STR     R1, SoftKeyPtr
+        Pull    "R1,R4"
+        B       RDCHNM                          ; try to expand it
+
+KeyName
+        =       keyprefix,0
+        ALIGN
+
+; *****************************************************************************
+;
+;       SetupKeyName - Set up the name <keyprefix><n><0> in SoftKeyName
+;
+; in:   R11 -> KeyWS
+;       R3 = key number
+;
+; out:  R0 -> SoftKeyName, which contains <keyprefix><n><0>
+;       R2-R4 corrupted
+;
+
+SetupKeyName ROUT
+        ADR     R2, KeyName
+        ADR     R0, SoftKeyName
+10
+        LDRB    R4, [R2], #1                    ; copy keyprefix in
+        TEQ     R4, #0
+        STRNEB  R4, [R0], #1
+        BNE     %BT10                           ; now put digits at R0
+
+        ORR     R3, R3, #"0"
+        CMP     R3, #"9"+1
+
+        MOVCS   R2, #"1"                        ; if >=10 then put in "1"
+        STRCSB  R2, [R0], #1
+        SUBCS   R3, R3, #10                     ; and subtract 10
+
+        STRB    R3, [R0], #1
+        STRB    R4, [R0]                        ; (R4=0) terminate
+
+        ADR     R0, SoftKeyName
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       DoInkeyOp - Perform INKEY
+
+DoInkeyOp
+        TST     R2, #&80                ; INKEY(+ve) ?
+        BNE     NewInkeyNeg
+
+NewInkeyPos
+        Push    R4
+        MOV     R11, #KeyWorkSpace
+        AND     R1, R1, #&FF            ; no funny business
+        AND     R2, R2, #&FF            ; ditto
+        ORR     R1, R1, R2, LSL #8      ; get combined count
+        STR     R1, InkeyCounter
+
+        MOV     R4, #0                  ; indicate inkey not rdch
+        BL      RdchInkey
+
+        MOV     R1, R0                  ; make X the character
+        MOVCC   R2, #0                  ; Y := 0 if normal exit
+        MOVCS   R2, R0                  ; Y := &1B or &FF for ESC or timeout
+
+        Pull    "R4,PC"                 ; return preserving V and R0
+
+NewInkeyNeg
+        EOR     R1, R1, #&7F            ; invert bits for scan call
+        BL      BBCScanKeys
+        Pull    PC
+
+; *****************************************************************************
+;
+;       BBCScanKeys - Test individual key or scan for key depression
+;
+; in:   R1 = 0..&7F => scan keyboard from BBC internal key R1
+; out:  C=0 => R1 = BBC internal key found
+;       C=1 => R1 = &FF (no key found)
+;
+; in:   R1 = &80..&FF => test if BBC internal key (R1 EOR &80) is down
+; out:  C=0, R1=R2=&00 => key is up
+;       C=1, R1=R2=&FF => key is down
+;
+
+BBCScanKeys ROUT
+        Push    R11
+        MOV     R11, #KeyWorkSpace
+        AND     R1, R1, #&FF            ; trap wallies
+
+        LDR     R2, KeyVec
+
+        TST     R1, #&80                ; >=&80 => test single key
+                                        ; < &80 => scan for key
+        BEQ     DoBBCScan               ; [is scanning not testing]
+
+ [ :LNOT: AssembleA1KeyHandler
+        ADD     R0, R2, #1
+        CMP     R0, #1                  ; if no key handler then
+        MOVCC   R1, #0                  ;   return key up (C=0)
+        BCC     ExitBBCScan
+ ]
+
+        LDR     R0, [R2, #KVInkeyTran]
+        ADD     R0, R2, R0              ; R0 -> InkeyTran or InkeyTran2
+
+        ADD     R0, R0, #4 * &FF        ; R0 -> InkeyTran+4*&FF
+        LDR     R0, [R0, -R1, LSL #2]   ; get word of indexes into KeysDown
+        MOV     R2, #&FF000000
+02
+        CMP     R0, #-1                 ; is it all FF's
+        MOVEQ   R1, #0                  ; if so then none of keys down
+        BEQ     %FT04
+
+        AND     R1, R0, #&FF            ; just get bottom byte
+        ADR     R3, KeysDown            ; look up in KeysDown
+        MOV     R1, R1, LSR #5
+        LDR     R3, [R3, R1, LSL #2]    ; get word of 32 bits
+        AND     R1, R0, #31
+        MOV     R3, R3, LSL R1          ; put relevant bit into top bit
+        MOVS    R1, R3, LSR #31         ; R1 = 0 if up, 1 if down
+        ORREQ   R0, R2, R0, LSR #8      ; shift down, putting FF in top byte
+        BEQ     %BT02
+04
+        CMP     R1, #1                  ; C=1 <=> at least one of keys down
+        MOVCC   R1, #0
+        MOVCS   R1, #&FF
+        MOV     R2, R1
+ExitBBCScan
+        Pull    R11
+        MOV     PC, R14
+
+DoBBCScan
+ [ :LNOT: AssembleA1KeyHandler
+        CMP     R2, #-1                 ; if no key handler then
+        MOVEQ   r1, #&FF                ;   return all keys up (C=1)
+        BEQ     ExitBBCScan
+ ]
+        LDR     R0, [R2, #KVInkeyTran]
+        ADD     R0, R2, R0              ; R0 -> InkeyTran or InkeyTran2
+
+        Push    "R4, R5"
+        ADD     R0, R0, #4 * &7F        ; R0 -> InkeyTran+4*&7F
+        MOV     R4, #&FF000000
+10
+        LDR     R3, [R0, -R1, LSL #2]   ; get word of indexes into KeysDown
+15
+        CMP     R3, #-1                 ; all FFs ?
+        BEQ     %FT18                   ; then not one of these keys
+
+        AND     R5, R3, #&FF
+        ADR     R2, KeysDown
+        MOV     R5, R5, LSR #5
+        LDR     R2, [R2, R5, LSL #2]    ; get word of bits
+        AND     R5, R3, #31
+        MOV     R2, R2, LSL R5          ; put relevant bit into top bit
+        MOVS    R5, R2, LSR #31         ; R5 = 0 for up, 1 for down
+        BNE     %FT20                   ; [down, so stop]
+        ORR     R3, R4, R3, LSR #8      ; up -> shift down putting FF in top
+        B       %BT15
+18
+        ADD     R1, R1, #1              ; go to next key
+        TEQ     R1, #&80                ; if not run out of keys
+        BNE     %BT10                   ; then loop
+        MOV     R1, #&FF                ; indicate no key
+20
+        CMP     R1, #&FF                ; C=0 <=> found key
+        Pull    "R4,R5,R11"
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       Write keys down information
+;
+; in:   R1 = Current key (in BBC internal key format)
+;       R2 = Old key     (------------""------------)
+;
+; out:  R1, R2 preserved
+;
+
+WriteKeysDown ROUT
+        Push    R14
+        MOV     R11, #KeyWorkSpace
+        MOV     R0, R1
+        BL      ConvertInternalKey
+        STRB    R0, CurrKey
+        MOV     R0, R2
+        BL      ConvertInternalKey
+        STRB    R0, OldKey
+        Pull    PC
+
+ConvertInternalKey
+        TST     R0, #&80                ; if not in range &80..&FF
+        MOVEQ   R0, #&FF                ; return value &FF (key not valid)
+        MOVEQ   PC, R14
+
+        EOR     R0, R0, #&7F            ; else convert to inkey value
+        Push    R4
+        LDR     R3, KeyVec
+ [ :LNOT: AssembleA1KeyHandler
+        CMP     R3, #-1                 ; if no key handler then
+        MOVEQ   R0, #&FF                ;   return no key
+        MOVEQ   PC, R14
+ ]
+        LDR     R4, [R3, #KVInkeyTran]
+        ADD     R3, R3, R4              ; R3 -> InkeyTran or InkeyTran2
+        Pull    R4
+
+        SUB     R3, R3, #&80*4          ; R3 -> InkeyTran-4*&80
+
+        LDRB    R0, [R3, R0, LSL #2]    ; convert to ARM internal key
+                                        ; (just get 1st key for this key)
+        MOV     PC, R14
+
+
+; *****************************************************************************
+;
+;       InstallKeyHandler - Install user key handler
+;
+; in:   R0 = new key handler
+;        0 => just read old key handler
+;        1 => just read keyboard id
+;
+; out:  R0 = old key handler, or
+;       R0 = keyboard id if R0 was 1 on entry (&FF => no keyboard id yet)
+;
+
+InstallKeyHandler ROUT
+        MOV     R11, PC
+        TST     R11, #I_bit
+        TEQEQP  R11, #I_bit             ; disable IRQs
+
+        MOV     R11, #KeyWorkSpace
+        TEQ     R0, #1                  ; asking for keyboard id ?
+        LDREQB  R0, KbId                ; then load it
+        ExitSWIHandler EQ               ; and exit
+
+        LDR     R10, KeyVec             ; R10 -> old key handler
+        TEQ     R0, #0                  ; if not just reading it
+        STRNE   R0, KeyVec              ; then store new one
+        MOV     R0, R10                 ; R0 -> old key handler
+        ExitSWIHandler EQ               ; exit if just reading
+
+        Push    "R0-R12,LR"
+        BL      KeyboardEnable
+        Pull    "R0-R12,LR"
+        ExitSWIHandler
+
+; *****************************************************************************
+;
+;       IssueKeyboardService - Issue keyboard handler service
+;
+; in:   R11 -> KeyWorkSpace
+;
+; out:  R0,PSR preserved
+;
+
+IssueKeyboardService
+        Push    "R0,R14"
+        MOV     R1, #Service_KeyHandler
+        LDRB    R2, KbId
+        IssueService
+        Pull    "R0,PC",,^
+
+
+        END
diff --git a/s/PMF/key2 b/s/PMF/key2
new file mode 100644
index 0000000000000000000000000000000000000000..6a9420154c28aa549377498892ba0f15abf2092e
--- /dev/null
+++ b/s/PMF/key2
@@ -0,0 +1,943 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.PMF.Key2
+
+; ARTHUR keyboard code (Keyboard table handler)
+
+; Author        Tim Dobson
+; Started       15-Apr-87
+
+; ************************************************************
+; ***    C h a n g e   L i s t  (better late than never!)  ***
+; ************************************************************
+
+; Date       Description
+; ----       -----------
+; 19-Feb-88  Modified InkeyTran2 so that INKEY(-95) returns state of new key
+;             next to left shift key on international keyboards
+; 11-Aug-88  Modified A500 keyboard as follows:-
+;             LOOKS = ALT; MENU = F0; F10 = F10; keypad # = F11;
+;             keypad % = F12; keypad / = F13
+
+        MACRO
+        IKT     $A, $B, $C, $D, $E, $F, $G, $H
+        IKT2    $A
+        IKT2    $B
+        IKT2    $C
+        IKT2    $D
+        IKT2    $E
+        IKT2    $F
+        IKT2    $G
+        IKT2    $H
+        MEND
+
+        MACRO
+        IKT2    $TE
+        LCLA    T
+T       SETA    $TE
+        [ (T :AND: &FF00) = 0
+T       SETA    T :OR: &FF00
+        ]
+        [ (T :AND: &FF0000) = 0
+T       SETA    T :OR: &FF0000
+        ]
+        [ (T :AND: &FF000000) = 0
+T       SETA    T :OR: &FF000000
+        ]
+        &       T
+        MEND
+
+
+; Special keys
+
+K0ShiftKey      * &0A
+K0LooksKey      * &0B
+K0ControlKey    * &0C
+K0RightMouse    * &0D
+K0CentreMouse   * &0E
+K0LeftMouse     * &0F
+K0TabKey        * &30
+K0CapsKey       * &40
+K0BreakKey      * &05 ; key marked ESCAPE is BREAK
+K0NumPadHash    * &04
+K0NumPadPercent * &17
+K0NumPadSlash   * &07
+K0NumPadStar    * &06
+K0NumPad7       * &14
+K0NumPad8       * &77
+K0NumPad9       * &27
+K0NumPadMinus   * &16
+K0NumPad4       * &24
+K0NumPad5       * &97
+K0NumPad6       * &67
+K0NumPadPlus    * &87
+K0NumPad1       * &64
+K0NumPad2       * &37
+K0NumPad3       * &57
+K0NumPadEnter   * &47
+K0NumPad0       * &94
+K0NumPadDot     * &44
+
+K1ShiftLeft     * &4C
+K1ShiftRight    * &58
+K1CtrlLeft      * &3B
+K1CtrlRight     * &61
+K1AltLeft       * &5E
+K1AltRight      * &60
+K1RightMouse    * &72
+K1CentreMouse   * &71
+K1LeftMouse     * &70
+K1TabKey        * &26
+K1CapsKey       * &5D
+K1NumKey        * &22
+K1ScrollKey     * &0E
+K1BreakKey      * &0F
+K1BackTickKey   * &10
+K1PoundKey      * &1D
+K1NumPadSlash   * &23
+K1NumPadStar    * &24
+K1NumPadHash    * &25
+K1NumPad7       * &37
+K1NumPad8       * &38
+K1NumPad9       * &39
+K1NumPadMinus   * &3A
+K1NumPad4       * &48
+K1NumPad5       * &49
+K1NumPad6       * &4A
+K1NumPadPlus    * &4B
+K1NumPad1       * &5A
+K1NumPad2       * &5B
+K1NumPad3       * &5C
+K1NumPadEnter   * &67
+K1NumPad0       * &65
+K1NumPadDot     * &66
+
+; UserKeyWorkSpace allocation
+
+        ^ 0, R12
+ShiftCount      # 1
+CtrlCount       # 1
+MyMouseButtons  # 1
+KeyReturn       # 2     ; length byte (1), value byte
+KeyNULReturn    # 3     ; length byte (2), NUL, value byte
+
+        ASSERT (:INDEX: @) <= UserKeyWorkSpaceSize
+
+; *****************************************************************************
+
+OldKeyStruct
+        &       KeyTran-OldKeyStruct
+        &       (KeyTranEnd-KeyTran) :SHR: 2
+        &       InkeyTran-OldKeyStruct
+        &       ShiftingKeyList-OldKeyStruct
+        &       SpecialList-OldKeyStruct
+        &       SpecialCodeTable-OldKeyStruct
+        &       KeyStructInit-OldKeyStruct
+        &       PendingAltCode-OldKeyStruct
+
+ShiftingKeyList
+        =       ShiftingKeyListEnd-ShiftingKeyList-1
+        =       K0ShiftKey, K0ControlKey, K0LooksKey
+        =       K0RightMouse, K0CentreMouse, K0LeftMouse
+        =       K0BreakKey
+ShiftingKeyListEnd
+        ALIGN
+
+SpecialList
+        [ Keyboard_Type = "A1A500"
+        =       SpecialListEnd-SpecialList-1
+        =       K0ShiftKey, K0ControlKey, K0LooksKey
+        =       K0RightMouse, K0CentreMouse, K0LeftMouse
+        =       K0CapsKey, K0TabKey, K0BreakKey
+SpecialListPad
+        =       K0NumPadHash, K0NumPadPercent, K0NumPadSlash, K0NumPadStar
+        =       K0NumPad7, K0NumPad8, K0NumPad9, K0NumPadMinus
+        =       K0NumPad4, K0NumPad5, K0NumPad6, K0NumPadPlus
+        =       K0NumPad1, K0NumPad2, K0NumPad3, K0NumPadEnter
+        =       K0NumPad0, K0NumPadDot
+        ]
+SpecialListEnd
+        ALIGN
+
+SpecialCodeTable
+        [ Keyboard_Type = "A1A500"
+        &       ProcessKShift-SpecialCodeTable
+        &       ProcessKCtrl-SpecialCodeTable
+        &       ProcessKAlt-SpecialCodeTable
+        &       ProcessKRight-SpecialCodeTable
+        &       ProcessKCentre-SpecialCodeTable
+        &       ProcessKLeft-SpecialCodeTable
+        &       ProcessKCaps-SpecialCodeTable
+        &       ProcessKTab-SpecialCodeTable
+        &       ProcessKBreak-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        &       ProcessK0Pad-SpecialCodeTable
+        ]
+
+; *****************************************************************************
+
+NewKeyStruct
+        &       KeyTran2-NewKeyStruct
+        &       (KeyTran2End-KeyTran2) :SHR: 2
+        &       InkeyTran2-NewKeyStruct
+        &       NewShiftingKeyList-NewKeyStruct
+        &       NewSpecialList-NewKeyStruct
+        &       NewSpecialCodeTable-NewKeyStruct
+        &       KeyStructInit-NewKeyStruct
+        &       PendingAltCode-NewKeyStruct
+
+NewShiftingKeyList
+        =       NewShiftingKeyListEnd-NewShiftingKeyList-1
+        =       K1ShiftLeft, K1ShiftRight, K1CtrlLeft, K1CtrlRight
+        =       K1RightMouse, K1CentreMouse, K1LeftMouse, K1BreakKey
+NewShiftingKeyListEnd
+        ALIGN
+
+NewSpecialList
+        [ Keyboard_Type = "A1A500"
+        =       NewSpecialListEnd-NewSpecialList-1
+        =       K1ShiftLeft, K1ShiftRight, K1CtrlLeft, K1CtrlRight
+        =       K1AltLeft, K1AltRight
+        =       K1RightMouse, K1CentreMouse, K1LeftMouse
+        =       K1CapsKey, K1TabKey
+        =       K1NumKey, K1ScrollKey
+        =       K1BreakKey, K1BackTickKey, K1PoundKey
+NewSpecialListPad
+        =       K1NumPadSlash, K1NumPadStar, K1NumPadHash
+        =       K1NumPad7, K1NumPad8, K1NumPad9, K1NumPadMinus
+        =       K1NumPad4, K1NumPad5, K1NumPad6, K1NumPadPlus
+        =       K1NumPad1, K1NumPad2, K1NumPad3, K1NumPadEnter
+        =       K1NumPad0, K1NumPadDot
+        ]
+NewSpecialListEnd
+        ALIGN
+
+NewSpecialCodeTable
+        [ Keyboard_Type = "A1A500"
+        &       ProcessKShift-NewSpecialCodeTable
+        &       ProcessKShift-NewSpecialCodeTable
+        &       ProcessKCtrl-NewSpecialCodeTable
+        &       ProcessKCtrl-NewSpecialCodeTable
+        &       ProcessKAlt-NewSpecialCodeTable
+        &       ProcessKAlt-NewSpecialCodeTable
+        &       ProcessKRight-NewSpecialCodeTable
+        &       ProcessKCentre-NewSpecialCodeTable
+        &       ProcessKLeft-NewSpecialCodeTable
+        &       ProcessKCaps-NewSpecialCodeTable
+        &       ProcessKTab-NewSpecialCodeTable
+        &       ProcessKNum-NewSpecialCodeTable
+        &       ProcessKScroll-NewSpecialCodeTable
+        &       ProcessKBreak-NewSpecialCodeTable
+        &       ProcessKBackTick-NewSpecialCodeTable
+        &       ProcessKPound-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        &       ProcessK1Pad-NewSpecialCodeTable
+        ]
+
+; Now the code to handle it
+
+; Initialise keyboard table handler
+;
+; in:   R12 -> my workspace
+;       R5 = KeyBdStatus
+;       R7 = PendingAltType
+;
+; out:  R5 = new KeyBdStatus
+;       R7 = new PendingAltType
+
+KeyStructInit ROUT
+        MOV     R0, #0                  ; no shift or ctrl keys down
+        STRB    R0, ShiftCount
+        STRB    R0, CtrlCount
+        STRB    R0, MyMouseButtons
+        STRB    R0, KeyNULReturn+1      ; NUL for NUL char return
+        MOV     R0, #1                  ; string length for single key return
+        STRB    R0, KeyReturn+0
+        MOV     R0, #2                  ; length for NUL char return
+        STRB    R0, KeyNULReturn+0
+
+        BIC     R5, R5, #(KBStat_ShiftEngaged :OR: KBStat_CtrlEngaged :OR: KBStat_PendingAlt)
+        MOV     PC, R14
+
+ProcessKShift ROUT
+        ADR     R0, ShiftCount
+        MOV     R2, #KBStat_ShiftEngaged
+ProcessShiftOrCtrl
+        TEQ     R1, #0                  ; R1=1 => down, R1=0 => up
+        LDRB    R3, [R0]
+        ADDNE   R3, R3, #1              ; if down then increment
+        SUBEQ   R3, R3, #1              ; if up then decrement
+        STRB    R3, [R0]
+        TEQ     R3, #0
+        ORRNE   R5, R5, R2              ; one or more shift/ctrl keys down
+        BICEQ   R5, R5, R2              ; zero shift/ctrl keys down
+        MOV     PC, R14
+
+ProcessKCtrl ROUT
+        ADR     R0, CtrlCount
+        MOV     R2, #KBStat_CtrlEngaged
+        B       ProcessShiftOrCtrl
+
+ProcessKRight ROUT
+        MOV     R2, #1
+ProcessMouseButton
+        TEQ     R1, #0
+        LDRB    R0, MyMouseButtons
+        ORRNE   R0, R0, R2              ; button going down
+        BICEQ   R0, R0, R2              ; button going up
+        STRB    R0, MyMouseButtons
+        MOV     PC, R3                  ; call his routine and exit
+
+ProcessKCentre ROUT
+        MOV     R2, #2
+        B       ProcessMouseButton
+
+ProcessKAlt ROUT
+        TST     R5, #KBStat_ShiftEngaged
+        MOVEQ   R7, #1                          ; Alt
+        MOVNE   R7, #2                          ; Shift-Alt
+        TST     R5, #KBStat_CtrlEngaged
+        ADDNE   R7, R7, #2                      ; Ctrl-Alt, Ctrl-Shift-Alt
+        ORR     R5, R5, #KBStat_PendingAlt      ; indicate a pending alt
+        MOV     PC, R14
+
+ProcessKLeft ROUT
+        MOV     R2, #4
+        B       ProcessMouseButton
+
+ProcessKCaps ROUT
+        TEQ     R1, #2                          ; is it first press ?
+        MOVNE   PC, R14                         ; don't auto-repeat
+
+        TST     R5, #KBStat_ShiftEngaged        ; if shift down
+        BICNE   R5, R5, #KBStat_NoCapsLock      ; then force CAPS on
+        ORRNE   R5, R5, #KBStat_ShiftEnable     ; and SHIFT CAPS state
+        EOREQ   R5, R5, #KBStat_NoCapsLock      ; else toggle caps lock state
+        BICEQ   R5, R5, #KBStat_ShiftEnable     ; and cancel shift enable
+
+        MOV     PC, R14
+
+ProcessKTab ROUT
+        LDROSB  R0, TABch                       ; TAB key code
+        TST     R0, #&80                        ; top bit set ?
+        BEQ     ReturnOneChar                   ; no, don't shift or ctrl it
+        TST     R5, #KBStat_ShiftEngaged
+        EORNE   R0, R0, #&10                    ; modify for shift
+        TST     R5, #KBStat_CtrlEngaged
+        EORNE   R0, R0, #&20                    ; modify for ctrl
+ReturnOneChar
+        ADR     R6, KeyReturn                   ; pass pointer back to MOS
+        STRB    R0, [R6, #1]                    ; having poked byte in
+        MOV     PC, R14
+
+ProcessKNum ROUT
+        TEQ     R1, #2                          ; is it first press ?
+        EOREQ   R5, R5, #KBStat_NoNumLock       ; yes, then toggle num lock
+        MOV     PC, R14                         ; (don't auto-repeat)
+
+ProcessKScroll ROUT
+        TEQ     R1, #2                          ; is it first press ?
+        EOREQ   R5, R5, #KBStat_ScrollLock      ; yes, then toggle scroll lock
+        MOV     PC, R14                         ; (don't auto-repeat)
+
+ProcessKBreak ROUT
+        ADD     PC, R3, #4                      ; offset for break routine
+
+        [ Keyboard_Type = "A1A500"
+ProcessKBackTick ROUT
+        Push    R14
+        BL      TestForBfont
+        Pull    R14
+        MOVEQ   R0, #&BB                        ; Bfont back tick
+        MOVNE   R0, #&60                        ; anything else back tick
+        B       ReturnNULChar
+
+ProcessKPound ROUT
+        Push    R14
+        BL      TestForBfont
+        Pull    R14
+        BNE     %FT10
+        TST     R5, #KBStat_ShiftEngaged
+        MOVEQ   R0, #&60                        ; Bfont pound
+        MOVNE   R0, #&9E                        ; Bfont currency
+        B       ReturnNULChar
+10
+        TST     R5, #KBStat_ShiftEngaged
+        MOVEQ   R0, #&A3                        ; anything else 'pound'
+        MOVNE   R0, #&A4                        ; anything else currency
+        B       ReturnNULChar
+        ]
+
+        [ Keyboard_Type = "A1A500"
+ProcessK0Pad ROUT
+        ADR     R0, PadK0NumTran-(SpecialListPad-SpecialList) ; on
+        ADR     R2, PadK0CurTran-(SpecialListPad-SpecialList) ; off
+        B       ProcessPad
+        ]
+        [ Keyboard_Type = "A1A500"
+ProcessK1Pad ROUT
+        ADR     R0, PadK1NumTran-(NewSpecialListPad-NewSpecialList) ; on
+        ADR     R2, PadK1CurTran-(NewSpecialListPad-NewSpecialList) ; off
+        ]
+ProcessPad
+        TST     R5, #KBStat_NoNumLock           ; test num lock
+        MOVNE   R0, R2                          ; numlock off -> use R2
+        LDRB    R0, [R0, R4]                    ; get table entry
+        TEQ     R0, #&FF                        ; dummy key ?
+        MOVEQ   PC, R14                         ; then exit
+
+        LDROSB  R2, KeyBase                     ; add on numeric key base
+        SUB     R0, R0, #"0"
+        ADD     R0, R0, R2
+
+        LDROSB  R2, KeyOpt                      ; zero => ctrl/shift modifies
+        TEQ     R2, #0
+        BNE     %FT10                           ; [don't modify]
+
+        TST     R0, #&80                        ; top bit set ?
+        BEQ     %FT10                           ; no, then don't modify
+
+        TST     R5, #KBStat_ShiftEngaged
+        EORNE   R0, R0, #&10                    ; modify for shift
+        TST     R5, #KBStat_CtrlEngaged
+        EORNE   R0, R0, #&20                    ; modify for ctrl
+10
+        B       ReturnOneChar
+
+
+        [ Keyboard_Type = "A1A500"
+PadK0NumTran
+        =       "#%/*789-456+123",13,"0."
+PadK0CurTran
+        =       "#%/*",&1E,&8F,&9F,"-",&8C,&FF,&8D,"+",&8B,&8E,&9E,13,&CD,&7F
+        ]
+        [ Keyboard_Type = "A1A500"
+PadK1NumTran
+        =       "/*#789-456+123",13,"0."
+PadK1CurTran
+        =       "/*#",&1E,&8F,&9F,"-",&8C,&FF,&8D,"+",&8B,&8E,&9E,13,&CD,&7F
+        ]
+        ALIGN
+
+        [ Keyboard_Type = "A1A500"
+; *****************************************************************************
+;
+;       TestForBfont - Check if keyboard corresponds to a Bfont font
+;
+; in:   IRQ mode
+;
+; out:  Z => Bfont, NZ => anything else
+;
+
+TestForBfont ROUT
+
+        [ {TRUE}
+        Push    R14
+        LDROSB  R14, KeyAlphabet
+        TEQ     R14, #100                       ; EQ => Bfont
+        Pull    PC
+        |
+
+        [ BleedinDaveBell
+FTDefault * %FT20
+        |
+FTDefault * %FT10
+        ]
+
+        Push    "R0-R4,R14"
+
+        TEQP    R14, #(SVC_mode :EOR: IRQ_mode) ; change to SVC mode
+        MOVNV   R0, R0                          ; wait for it to happen
+
+        Push    R14                             ; save R14_svc
+
+        MOV     R0, #OsbyteSetAlphKey
+        MOV     R1, #&FF                        ; indicate read keyboard no.
+        SWI     XOS_Byte
+        BVS     FTDefault                       ; error - indicate default
+
+        MOV     R3, R1                          ; R3 = keyboard country no.
+        MOV     R1, #Service_International
+        MOV     R2, #Inter_CNoToANo             ; convert to alphabet number
+        SWI     XOS_ServiceCall
+        BVS     FTDefault
+
+        TEQ     R1, #0                          ; was it claimed ?
+        BNE     FTDefault                       ; no, then indicate default
+
+        TEQ     R4, #100                        ; is it alphabet bfont ?
+        BEQ     %FT10
+20
+        Pull    R14                             ; restore R14_svc
+        MOV     R0, PC
+        TEQP    R0, #(SVC_mode :EOR: IRQ_mode)  ; go back to IRQ mode
+        MOVNV   R0, R0
+        Pull    "R0-R4,R14"
+        BICS    PC, R14, #Z_bit                 ; indicate not bfont
+
+10
+        Pull    R14                             ; restore R14_svc
+        MOV     R0, PC
+        TEQP    R0, #(SVC_mode :EOR: IRQ_mode)  ; go back to IRQ mode
+        MOVNV   R0, R0
+        Pull    "R0-R4,R14"
+        ORRS    PC, R14, #Z_bit                 ; indicate is bfont
+        ]
+        ]
+
+; *****************************************************************************
+;
+;       PendingAltCode - Process ALT+char
+;
+; in:   R0 -> key structure
+;       R2 = internal key number for char
+;       R3 = character
+;       R5 = keyboard status
+;       R7 = pending alt type
+;
+; out:  R6 -> returned key list
+;
+
+PendingAltCode ROUT
+        BIC     R5, R5, #KBStat_PendingAlt ; cancel pending alt
+        TEQ     R7, #4                  ; is it CTRL-SHIFT-ALT char ?
+        MOVNE   R0, R3
+        BNE     ReturnOneChar
+        ORR     R0, R3, #&80            ; set top bit
+ReturnNULChar
+        ADR     R6, KeyNULReturn
+        STRB    R0, [R6, #2]
+        MOV     PC, R14
+
+; *****************************************************************************
+
+KeyTran ROUT
+        [ Keyboard_Type = "A1A500"
+; Column 0
+
+00
+        =       &82, &92, &A2, &B2      ; f2
+        =       &83, &93, &A3, &B3      ; f3
+        =       &84, &94, &A4, &B4      ; f4
+        =       &86, &96, &A6, &B6      ; f6
+        =       &CB, &DB, &EB, &FB      ; pad #, now f11
+        &       -1                      ; ESCAPE
+        &       -1                      ; pad *
+        =       &CD, &DD, &ED, &FD      ; pad /, now f13
+        =       &88, &98, &A8, &B8      ; f8
+        =       &CA, &DA, &EA, &FA      ; f10 - now really f10!
+
+        &       -1,-1,-1,-1,-1,-1       ; SHIFT,LOOKS,CMD,RIGHT,CENTRE,LEFT
+
+; Column 1
+
+10
+        =       &81, &91, &A1, &B1      ; f1
+        =       "3#3#"                  ; 3
+        =       "4$4$"                  ; 4
+        =       &85, &95, &A5, &B5      ; f5
+        &       -1                      ; pad 7
+        =       "````"                  ; and copyright sign ?
+        &       -1                      ; pad -
+        =       &CC, &DC, &EC, &FC      ; pad %, now f12
+        =       &87, &97, &A7, &B7      ; f7
+        =       &89, &99, &A9, &B9      ; f9
+
+        &       -1,-1,-1,-1,-1,-1
+
+; Column 2
+
+20
+        =       "2""2"""                ; 2
+        =       "eE", &05, &05          ; E
+        =       "5%5%"                  ; 5
+        =       "6&6&"                  ; 6
+        &       -1                      ; pad 4
+        =       "^~", &1E, &1E          ; ^
+        &       -1                      ; not fitted
+        &       -1                      ; pad 9
+        =       "8(8("                  ; 8
+        =       "0000"                  ; 0
+
+        &       -1,-1,-1,-1,-1,-1
+
+; Column 3
+
+30
+        &       -1                      ; TAB
+        =       "zZ", &1A, &1A          ; Z
+        =       "gG", &07, &07          ; G
+        =       "bB", &02, &02          ; B
+        =       &8C, &9C, &AC, &BC      ; left arrow
+        =       ":*:*"                  ; :
+        &       -1                      ; not fitted
+        &       -1                      ; pad 2
+        =       "kK", &0B, &0B          ; K
+        =       ";+;+"                  ; ;
+
+        &       -1,-1,-1,-1,-1,-1
+
+; Column 4
+
+40
+        &       -1                      ; CAPS lock
+        =       "xX", &18, &18          ; X
+        =       "vV", &16, &16          ; V
+        =       "    "                  ; SPACE BAR
+        &       -1                      ; pad .
+        =       &8B,&9B,&AB,&BB         ; AGAIN
+        &       -1                      ; not fitted
+        &       -1                      ; ENTER
+        =       ",<,<"                  ; ,
+        =       "/?/?"                  ; /
+
+        &       -1,-1,-1,-1,-1,-1
+
+; Column 5
+
+50
+        =       &80, &90, &A0, &B0      ; MENU now acts like f0
+        =       "\|", &1C, &1C          ; \
+        =       "cC", &03, &03          ; C
+        =       "nN", &0E, &0E          ; N
+        =       &8E, &9E, &AE, &BE      ; down arrow
+        =       &0D, &0D, &0D, &0D      ; RETURN
+        &       -1                      ; not fitted
+        &       -1                      ; pad 3
+        =       "mM", &0D, &0D          ; M
+        =       ".>.>"                  ; .
+
+        &       -1,-1,-1,-1,-1,-1
+
+; Column 6
+
+60
+        =       "1!1!"                  ; 1
+        =       "qQ", &11, &11          ; Q
+        =       "tT", &14, &14          ; T
+        =       "yY", &19, &19          ; Y
+        &       -1                      ; pad 1
+        =       "[{", &1B, &1B          ; [
+        &       -1                      ; not fitted
+        &       -1                      ; pad 6
+        =       "iI", &09, &09          ; I
+        =       "pP", &10, &10          ; P
+
+        &       -1,-1,-1,-1,-1,-1
+
+; Column 7
+
+70
+        =       &1B, &1B, &1B, &1B      ; HELP (another ESC key for Tutu)
+        =       "wW", &17, &17          ; W
+        =       "rR", &12, &12          ; R
+        =       "7'7'"                  ; 7
+        =       &8F, &9F, &AF, &BF      ; up arrow
+        =       &7F, &7F, &7F, &7F      ; DELETE
+        &       -1                      ; not fitted
+        &       -1                      ; pad 8
+        =       "9)9)"                  ; 9
+        =       "-=-="                  ; -
+
+        &       -1,-1,-1,-1,-1,-1
+
+; Column 8
+
+80
+        =       &8B, &9B, &AB, &BB      ; COPY ?
+        =       "aA", &01, &01          ; A
+        =       "dD", &04, &04          ; D
+        =       "hH", &08, &08          ; H
+        =       &8D, &9D, &AD, &BD      ; right arrow
+        =       "]}", &1D, &1D          ; ]
+        &       -1                      ; not fitted
+        &       -1                      ; pad +
+        =       "jJ", &0A, &0A          ; J
+        =       "lL", &0C, &0C          ; L
+
+        &       -1,-1,-1,-1,-1,-1
+
+; Column 9
+
+90
+        =       "__", &1F, &1F          ; _
+        =       "sS", &13, &13          ; S
+        =       "fF", &06, &06          ; F
+        =       "uU", &15, &15          ; U
+        &       -1                      ; pad 0
+        &       -1                      ; not fitted
+        &       -1                      ; not fitted
+        &       -1                      ; pad 5
+        =       "oO", &0F, &0F          ; O
+        =       "@@", &00, &00          ; @ (and grave accent ?)
+
+        &       -1,-1,-1,-1,-1,-1
+99
+        ASSERT  %10-%00 = 64
+        ASSERT  %20-%10 = 64
+        ASSERT  %30-%20 = 64
+        ASSERT  %40-%30 = 64
+        ASSERT  %50-%40 = 64
+        ASSERT  %60-%50 = 64
+        ASSERT  %70-%60 = 64
+        ASSERT  %80-%70 = 64
+        ASSERT  %90-%80 = 64
+        ASSERT  %99-%90 = 64
+        ]
+KeyTranEnd
+
+
+; *****************************************************************************
+
+; Keyboard table for new keyboard
+
+KeyTran2 ROUT
+        [ Keyboard_Type = "A1A500"
+; Column 0
+
+00
+        =       &1B, &1B, &1B, &1B      ; Escape
+        =       &81, &91, &A1, &B1      ; f1
+        =       &82, &92, &A2, &B2      ; f2
+        =       &83, &93, &A3, &B3      ; f3
+        =       &84, &94, &A4, &B4      ; f4
+        =       &85, &95, &A5, &B5      ; f5
+        =       &86, &96, &A6, &B6      ; f6
+        =       &87, &97, &A7, &B7      ; f7
+        =       &88, &98, &A8, &B8      ; f8
+        =       &89, &99, &A9, &B9      ; f9
+        =       &CA, &DA, &EA, &FA      ; f10 (or is it f0)
+        =       &CB, &DB, &EB, &FB      ; f11
+        =       &CC, &DC, &EC, &FC      ; f12
+        =       &80, &90, &A0, &B0      ; Print
+        &       -1                      ; Scroll Lock
+        &       -1                      ; Break
+
+; Column 1
+
+10
+        =       &FF, "~", &FF, "~"      ; back tick (») and ~
+        =       "1!1!"                  ; 1
+        =       "2@", &00, &00          ; 2
+        =       "3#3#"                  ; 3
+        =       "4$4$"                  ; 4
+        =       "5%5%"                  ; 5
+        =       "6^", &1E, &1E          ; 6
+        =       "7&7&"                  ; 7
+        =       "8*8*"                  ; 8
+        =       "9(9("                  ; 9
+        =       "0)0)"                  ; 0
+        =       "-_", &1F, &1F          ; -
+        =       "=+=+"                  ; =
+        &       -1                      ; pound and currency (ž)
+        =       &08, &08, &08, &08      ; backspace
+        =       &CD, &DD, &ED, &FD      ; Insert
+
+; Column 2
+
+20
+        =       &1E, &1E, &1E, &1E      ; Home
+        =       &9F, &8F, &BF, &AF      ; Page Up
+        &       -1                      ; Num Lock
+        &       -1                      ; pad /
+        &       -1                      ; pad *
+        &       -1                      ; pad #
+        &       -1                      ; Tab
+        =       "qQ", &11, &11          ; Q
+        =       "wW", &17, &17          ; W
+        =       "eE", &05, &05          ; E
+        =       "rR", &12, &12          ; R
+        =       "tT", &14, &14          ; T
+        =       "yY", &19, &19          ; Y
+        =       "uU", &15, &15          ; U
+        =       "iI", &09, &09          ; I
+        =       "oO", &0F, &0F          ; O
+
+; Column 3
+
+30
+        =       "pP", &10, &10          ; P
+        =       "[{", &1B, &1B          ; [
+        =       "]}", &1D, &1D          ; ]
+        =       "\|", &1C, &1C          ; \
+        =       &7F, &7F, &7F, &7F      ; Delete
+        =       &8B, &9B, &AB, &BB      ; Copy
+        =       &9E, &8E, &BE, &AE      ; Page Down
+        &       -1                      ; pad 7
+        &       -1                      ; pad 8
+        &       -1                      ; pad 9
+        &       -1                      ; pad -
+        &       -1                      ; Ctrl (left)
+        =       "aA", &01, &01          ; A
+        =       "sS", &13, &13          ; S
+        =       "dD", &04, &04          ; D
+        =       "fF", &06, &06          ; F
+
+; Column 4
+
+40
+        =       "gG", &07, &07          ; G
+        =       "hH", &08, &08          ; H
+        =       "jJ", &0A, &0A          ; J
+        =       "kK", &0B, &0B          ; K
+        =       "lL", &0C, &0C          ; L
+        =       ";:;:"                  ; ;
+        =       "'""'"""                ; '
+        =       &0D, &0D, &0D, &0D      ; Return
+        &       -1                      ; pad 4
+        &       -1                      ; pad 5
+        &       -1                      ; pad 6
+        &       -1                      ; pad +
+        &       -1                      ; Shift (left)
+        &       -1                      ; not fitted
+        =       "zZ", &1A, &1A          ; Z
+        =       "xX", &18, &18          ; X
+
+; Column 5
+
+50
+        =       "cC", &03, &03          ; C
+        =       "vV", &16, &16          ; V
+        =       "bB", &02, &02          ; B
+        =       "nN", &0E, &0E          ; N
+        =       "mM", &0D, &0D          ; M
+        =       ",<,<"                  ; ,
+        =       ".>.>"                  ; .
+        =       "/?/?"                  ; /
+        &       -1                      ; Shift (right)
+        =       &8F, &9F, &AF, &BF      ; cursor up
+        &       -1                      ; pad 1
+        &       -1                      ; pad 2
+        &       -1                      ; pad 3
+        &       -1                      ; Caps Lock
+        &       -1                      ; Alt (left)                    ***
+        =       "    "                  ; space bar
+
+; Column 6
+
+60
+        &       -1                      ; Alt (right)                   ***
+        &       -1                      ; Ctrl (right)
+        =       &8C, &9C, &AC, &BC      ; cursor left
+        =       &8E, &9E, &AE, &BE      ; cursor down
+        =       &8D, &9D, &AD, &BD      ; cursor right
+
+        [ {FALSE}
+        &       -1                      ; pad 0
+        &       -1                      ; pad .
+        &       -1                      ; pad Enter
+        &       -1                      ; not fitted
+        &       -1                      ; not fitted
+        &       -1                      ; not fitted
+        &       -1                      ; not fitted
+        &       -1                      ; not fitted
+        &       -1                      ; not fitted
+        &       -1                      ; not fitted
+        &       -1                      ; not fitted
+
+; Column 7
+
+70
+        &       -1                      ; mouse left
+        &       -1                      ; mouse centre
+        &       -1                      ; mouse right
+        ]
+        ASSERT  %10-%00 = 16*4
+        ASSERT  %20-%10 = 16*4
+        ASSERT  %30-%20 = 16*4
+        ASSERT  %40-%30 = 16*4
+        ASSERT  %50-%40 = 16*4
+        ASSERT  %60-%50 = 16*4
+        ]
+KeyTran2End
+
+
+; *****************************************************************************
+;
+;       Reverse table lookup for INKEY(-ve)
+;
+
+InkeyTran
+    [ Keyboard_Type = "A1A500"
+        IKT     &FF, &FF, &FF, &37, &97, &24, &84, &51          ; 80-87
+        IKT     &19, &08, &03, &13, &01, &00, &10, &0570        ; 88-8F
+        IKT     &FF, &FF, &FF, &57, &64, &94, &8045, &49        ; 90-97
+        IKT     &59, &48, &58, &33, &42, &43, &31, &30          ; 98-9F
+        IKT     &FF, &FF, &FF, &FF, &06, &04, &75, &85          ; A0-A7
+        IKT     &39, &89, &53, &83, &32, &52, &91, &FF          ; A8-AF
+        IKT     &FF, &FF, &FF, &44, &FF, &07, &55, &35          ; B0-B7
+        IKT     &99, &38, &88, &63, &92, &41, &81, &40          ; B8-BF
+        IKT     &FF, &FF, &07, &47, &16, &87, &74, &65          ; C0-C7
+        IKT     &69, &98, &93, &23, &72, &82, &20, &60          ; C8-CF
+        IKT     &FF, &FF, &FF, &FF, &27, &77, &54, &90          ; D0-D7
+        IKT     &29, &78, &68, &73, &62, &21, &71, &50          ; D8-DF
+        IKT     &FF, &09, &17, &04, &14, &67, &34, &25          ; E0-E7
+        IKT     &79, &18, &28, &02, &22, &12, &11, &61          ; E8-EF
+        IKT     &FF, &FF, &FF, &FF, &0D, &0E, &0F, &0B          ; F0-F7
+        IKT     &0C, &0A, &0B, &0C, &0A, &0B, &0C, &0A          ; F8-FF
+     ]
+
+InkeyTran2
+     [ Keyboard_Type = "A1A500"
+        IKT     &FF, &FF, &FF, &5B, &49, &48, &64, &33          ; 80-87
+        IKT     &09, &08, &06, &05, &03, &02, &01, &00          ; 88-8F
+        IKT     &FF, &FF, &FF, &5C, &5A, &65, &35, &57          ; 90-97
+        IKT     &56, &55, &54, &52, &51, &5F, &4E, &26          ; 98-9F
+        IKT     &FF, &4D, &1C, &FF, &24, &25, &34, &32          ; A0-A7
+        IKT     &45, &44, &53, &41, &40, &50, &3D, &FF          ; A8-AF
+        IKT     &46, &36, &22, &66, &FF, &23, &47, &45          ; B0-B7
+        IKT     &12, &43, &42, &2C, &3F, &4F, &3C, &5D          ; B8-BF
+        IKT     &21, &20, &1F, &67, &3A, &4B, &59, &31          ; C0-C7
+        IKT     &30, &2F, &2D, &16, &2A, &3E, &12, &11          ; C8-CF
+        IKT     &1E, &1D, &10, &0F, &39, &38, &63, &1B          ; D0-D7
+        IKT     &1A, &19, &2E, &17, &2B, &29, &28, &0D          ; D8-DF
+        IKT     &0E, &0A, &0C, &0B, &37, &4A, &62, &16          ; E0-E7
+        IKT     &1B, &07, &18, &04, &15, &14, &13, &27          ; E8-EF
+        IKT     &FF, &FF, &FF, &FF, &72, &71, &70, &60          ; F0-F7
+        IKT     &61, &58, &5E, &3B, &4C, &5E60, &3B61, &4C58    ; F8-FF
+    ]
+
+        END
diff --git a/s/PMF/mouse b/s/PMF/mouse
new file mode 100644
index 0000000000000000000000000000000000000000..ddc4929b94dd73bdaa557477bddc6f622ddc5070
--- /dev/null
+++ b/s/PMF/mouse
@@ -0,0 +1,395 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.PMF.Mouse
+
+; Mouse driving code
+
+; Author:       Steve Cormie
+; Started:      24-Feb-93
+
+; Change history:
+;
+; Date          Who     Description
+; ----          ---     -----------
+; 24-Feb-93     SMC     Created.
+
+; *****************************************************************************
+;
+;        Mouse initialisation
+;
+MouseInit
+        Push    "lr"
+        MOV     r11, #KeyWorkSpace
+
+ [ :LNOT: AssemblingArthur
+        MOV     r0, #MouseV
+        ADRL    r1, ReadMouse
+        SWI     OS_Claim
+ ]
+
+        MOV     r0, #MouseStepCMOS      ; setup mouse multipliers from CMOS
+        BL      Read
+        MOV     r0, r0, LSL #24         ; sign extend it
+        MOVS    r0, r0, ASR #24
+        MOVEQ   r0, #1                  ; if would be zero, set to 1
+        STR     r0, MouseXMult
+        STR     r0, MouseYMult
+
+        MOV     r0, #0
+        STRB    r0, MouseButtons
+
+        MOV     r0, #MouseCMOS
+        BL      Read
+        STRB    r0, MouseType
+
+        Pull    "pc"
+
+; *****************************************************************************
+;
+;       MouseButtonChange - Called by keyboard handler when mouse button change
+;
+; in:   R0 = state of buttons (bit0=R, bit1=C, bit2=L)
+;       R11 -> KeyWorkSpace
+;
+
+MouseButtonChange ROUT
+        Push    "R0-R5, R12, R14"
+
+        VDWS    WsPtr
+        STRB    R0, MouseButtons        ; save it for ReadMouse calls
+        MOV     R3, R0
+
+        LDR     R1, MouseX
+        LDR     R0, [WsPtr, #OrgX]
+        SUB     R1, R1, R0              ; mouse X
+
+        LDR     R2, MouseY
+        LDR     R0, [WsPtr, #OrgY]
+        SUB     R2, R2, R0              ; mouse Y
+
+        [ AssemblingArthur :LOR: Module
+        MOV     R4, #0
+        LDR     R4, [R4, #MetroGnome]   ; use monotonic variable now
+        |
+        BYTEWS  WsPtr
+        LDR     R4, RealTime            ; doesn't exist in my world
+        ]
+
+        MOV     R0, #Event_Mouse
+        BL      OSEVEN
+        MOV     WsPtr, #IOC
+
+ [ :LNOT:MouseBufferManager
+        [ MouseBufferFix
+        LDR     R0, MouseX
+        |
+        MOV     R5, R2                  ; save mouse Y
+        MOV     R0, R1
+        ]
+        BL      MouseInsert             ; send mouse X low
+        BCS     %FT10                   ; buffer full, so don't send rest
+
+        MOV     R0, R0, LSR #8          ; send mouse X high
+        BL      MouseInsert
+
+        [ MouseBufferFix
+        LDR     R0, MouseY
+        |
+        MOV     R0, R5
+        ]
+        BL      MouseInsert             ; send mouse Y low
+
+        MOV     R0, R0, LSR #8          ; send mouse Y high
+        BL      MouseInsert
+
+        MOV     R0, R3
+        BL      MouseInsert             ; send buttons
+
+        MOV     R0, R4
+        BL      MouseInsert             ; send realtime(0)
+
+        MOV     R0, R4, LSR #8
+        BL      MouseInsert             ; send realtime(1)
+
+        MOV     R0, R4, LSR #16
+        BL      MouseInsert             ; send realtime(2)
+
+        MOV     R0, R4, LSR #24
+        BL      MouseInsert             ; send realtime(3)
+ |
+; Use buffer manager's 'block insert' function
+
+ [ {TRUE}
+
+; TMD 26-Feb-93: Fix bug - if X is negative, Y would be inserted in the buffer as -1
+
+        LDR     R0, MouseX              ; 16 bits, sign-extended to 32 bits
+        MOV     R0, R0, LSL #16
+        LDR     R1, MouseY              ; ditto
+        MOV     R1, R1, LSL #16
+        ORR     R0, R1, R0, LSR #16     ; combine, having knocked off the troublesome bits
+ |
+        LDR     R0, MouseX              ; 16 bits
+        LDR     R1, MouseY              ; 16 bits
+        ORR     R0, R0, R1, LSL #16     ; R0 = Combined 16bit X/Y mouse position
+ ]
+        ORR     R1, R3, R4, LSL #8      ; R1 = Combined 8bit buttons and 24 LSB's of time
+        MOV     R2, R4, LSR #24         ; R2 = MSB of time
+        SUB     SP, SP, #3*4            ; Create local mouse data buffer
+        STMIA   SP, {R0,R1,R2}          ; Write mouse data to buffer
+
+        MOV     R3, #9                  ; Mouse packet size
+        MOV     R2, SP                  ; R2-> block to insert
+        MOV     R1, #(Buff_Mouse:OR:(1:SHL:31)) ; Block insert to mouse buffer
+        Push    "R10,R12"
+        MOV     R10, #INSV              ; Insert
+        BL      GoVec2                  ; Call the vector in R10
+        Pull    "R10,R12"
+        ADD     SP, SP, #3*4            ; Destroy mouse data buffer
+ ]
+10
+        Pull    "R0-R5, R12, PC"
+
+ [ :LNOT:MouseBufferManager
+MouseInsert
+        Push    "R10,R12,R14"
+        MOV     R10, #INSV
+        MOV     R1, #Buff_Mouse
+        B       GoVec
+ ]
+
+; *****************************************************************************
+;
+;       Read mouse position
+;
+
+ReadMouse ROUT
+        Push    "R4-R6,R10-R12"
+        MOV     R11, #KeyWorkSpace
+
+ [ :LNOT:MouseBufferManager
+        MOV     R1, #Buff_Mouse
+        BL      KeyREMOVE
+        BCS     %FT10                   ; MouseAhead buffer empty
+
+        MOV     R4, R2, LSL #16         ; Mouse X Low
+        BL      KeyREMOVE
+        ORR     R4, R4, R2, LSL #24     ; R4 := Mouse X << 16
+
+        BL      KeyREMOVE
+        MOV     R5, R2, LSL #16         ; Mouse Y Low
+        BL      KeyREMOVE
+        ORR     R5, R5, R2, LSL #24     ; R5 := Mouse Y << 16
+
+        BL      KeyREMOVE
+        MOV     R6, R2                  ; Button state
+
+        BL      KeyREMOVE               ; get realtime
+        MOV     R3, R2
+        BL      KeyREMOVE
+        ORR     R3, R3, R2, LSL #8
+        BL      KeyREMOVE
+        ORR     R3, R3, R2, LSL #16
+        BL      KeyREMOVE
+        ORR     R3, R3, R2, LSL #24
+
+        MOV     R0, R4, ASR #16         ; sign extend mouse coords
+        MOV     R1, R5, ASR #16
+        MOV     R2, R6
+ |
+        SUB     SP, SP, #3*4            ; Create 9 byte local mouse data buffer
+        MOV     R3, #9                  ; Mouse packet size
+        MOV     R2, SP                  ; R2-> buffer for data
+        MOV     R1, #(Buff_Mouse:OR:(1:SHL:31)) ; Block remove from mouse buffer
+        CLRV                            ; Remove not examine
+        Push    "R10,R12"
+        MOV     R10, #REMV
+        BL      GoVec2                  ; Call the vector in R10
+        Pull    "R10,R12"
+
+        LDMCCIA SP, {R4,R5,R6}
+        ADD     SP, SP, #3*4            ; Destroy mouse data buffer
+        BCS     %FT10                   ; Jump if no buffered data
+
+        MOV     R0, R4, LSL #16
+        MOV     R0, R0, ASR #16         ; R0 = sign extended x coord
+        MOV     R1, R4, ASR #16         ; R1 = sign extended y coord
+        AND     R2, R5, #&FF            ; R2 = button state
+        MOV     R3, R5, LSR #8          ; R3 = 3 low order bytes of time
+        ORR     R3, R3, R6, LSL #24     ; R3 = time
+ ]
+
+; code inserted here 12-Aug-88 to force position read from buffer to be inside
+; CURRENT bounding box; this removes the need to flush buffer when changing
+; the bounding box.
+
+        ADR     R4, MouseBounds
+        LDMIA   R4, {R4-R6,R10}         ; R4=LCol; R5=BRow; R6=RCol; R10=TRow;
+        CMP     R0, R4
+        MOVLT   R0, R4
+        CMP     R0, R6
+        MOVGT   R0, R6
+        CMP     R1, R5
+        MOVLT   R1, R5
+        CMP     R1, R10
+        MOVGT   R1, R10
+
+        [ MouseBufferFix
+        B       %FT20                   ; correct for origin after clipping
+        |
+        Pull    "R4-R6,R10-R12,PC"
+        ]
+
+10
+        LDRB    R2, MouseButtons
+
+        [ AssemblingArthur :LOR: Module
+        MOV     R3, #0
+        LDR     R3, [R3, #MetroGnome]           ; use monotonic variable now
+        |
+        BYTEWS  WsPtr
+        LDR     R3, RealTime                    ; doesn't exist in my world
+        ]
+
+        LDR     R0, MouseX
+        LDR     R1, MouseY
+20
+        VDWS    WsPtr
+
+        LDR     R4, [WsPtr, #OrgX]
+        SUB     R0, R0, R4
+
+        LDR     R4, [WsPtr, #OrgY]
+        SUB     R1, R1, R4
+
+        Pull    "R4-R6,R10-R12,PC"
+
+; *****************************************************************************
+;
+;       ProcessMouseXY - Called to update mouse position.
+;
+;       in:     r2  = signed 32-bit X movement
+;               r3  = signed 32-bit Y movement
+;               r11 ->KeyWorkSpace
+;       out:    r2,r3 corrupted
+;
+ProcessMouseXY
+        Push    "r4,lr"
+
+; process X movement
+        CMP     r2, #0
+        BEQ     %FT10
+
+        MOV     r2, r2, LSL #16         ; move delta X to top 16 bits
+
+        LDR     r4, MouseXMult
+        MUL     r2, r4, r2
+
+        LDR     r4, MouseX
+        ADD     r2, r2, r4, LSL #16     ; add signed value in top 16 bits
+        MOV     r2, r2, ASR #16         ; sign extend to 32 bits
+
+        LDR     r4, MouseBoundLCol      ; bound to bounding box
+        CMP     r2, r4
+        MOVLT   r2, r4
+        LDR     r4, MouseBoundRCol
+        CMP     r4, r2
+        MOVLT   r2, r4
+        STR     r2, MouseX
+
+10
+; process Y movement
+        CMP     r3, #0
+        Pull    "r4,pc",EQ
+
+        MOV     r3, r3, LSL #16         ; move delta Y to top 16 bits
+
+        LDR     r4, MouseYMult
+        MUL     r3, r4, r3
+
+        LDR     r4, MouseY
+        ADD     r3, r3, r4, LSL #16     ; add signed value in top 16 bits
+        MOV     r3, r3, ASR #16         ; sign extend to 32 bits
+
+        LDR     r4, MouseBoundBRow      ; bound to bounding box
+        CMP     r3, r4
+        MOVLT   r3, r4
+        LDR     r4, MouseBoundTRow
+        CMP     r4, r3
+        MOVLT   r3, r4
+        STR     r3, MouseY
+
+        Pull    "r4,pc"
+
+ [ AssemblePointerV
+
+; *****************************************************************************
+;
+;       PollPointer - Called on VSync to get mouse changes.
+;
+;       out:    corrupts r0-r3,r9-r11
+;
+PollPointer
+        Push    "r12,lr"
+        MOV     r11, #KeyWorkSpace
+
+        MOV     r0, #0                  ; Request pointer state.
+        LDRB    r1, MouseType
+        MOV     r2, #0                  ; Default to no movement.
+        MOV     r3, #0
+        MOV     r9, pc                  ; Save current PSR.
+        TEQP    pc, #SVC_mode + I_bit   ; Call PointerV in SVC mode, no IRQs.
+        MOV     r10, #PointerV          ; Call PointerV to get movements & button states
+        Push    "lr"                    ; Save SVC lr.
+        BL      CallVector
+        Pull    "lr"                    ; Restore SVC lr.
+        TEQP    r9, #0
+        NOP
+
+        BL      ProcessMouseXY
+
+        Pull    "r12,pc"
+
+
+; *****************************************************************************
+;
+;       PointerSWI - Handle SWI OS_Pointer calls (read/set pointer type).
+;
+PointerSWI
+        MOV     r11, #KeyWorkSpace
+        TEQ     r0, #0
+        LDREQB  r0, MouseType
+        BEQ     SLVK
+
+        TEQ     r0, #1
+        BNE     %FT10
+
+        Push    "r0,r10,r12,lr"
+        STRB    r1, MouseType
+        MOV     r0, #2
+        MOV     r10, #PointerV
+        BL      CallVector
+        Pull    "r0,r10,r12,lr"
+        B       SLVK
+
+10
+        ADRL    r0, ErrorBlock_BadParameters
+  [ International
+        BL      TranslateError
+  ]
+        B       SLVK_SetV
+ ]
+
+        END
diff --git a/s/PMF/osbyte b/s/PMF/osbyte
new file mode 100644
index 0000000000000000000000000000000000000000..e024e54c400459c198c7db1668241e86624a856e
--- /dev/null
+++ b/s/PMF/osbyte
@@ -0,0 +1,1389 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.PMF.osbyte
+; New version of OSBYTE which claims the ByteV(ector) properly
+; PMF 18/9/86
+; Updates:
+; Kernel
+; Version  Date        Who  Why
+; 2.01     15-June-90  JSR  Change OS_Byte 124/125/126 to update the CallBack_Flag byte
+;                            correctly, rather than setting it to 1. The bug caused vector
+;                            callbacks to be delayed over much when escape was pressed.
+
+OsbyteLowLimit * &6A    ; osbytes lower than this get Y set to 0
+OsbyteVeryLow  * &1A    ; osbytes lower than this are all recognised
+OsbyteSetCountry * &46
+OsbyteSetAlphKey * &47
+OsbyteWrchDests * &EC   ; only OS_Byte variable which isn't pure any more!
+
+ArthurINKEY256 * &A6    ; INKEY-256 value
+
+; *****************************************************************************
+
+        GBLS    ByteRegs
+        GBLA    StackOffset
+        [ AssemblingArthur :LOR: Module
+ByteRegs SETS    ""
+StackOffset SETA 4*4            ; stack offset in osbyte routines to user pc
+        |
+ByteRegs SETS    "R11, WsPtr,"
+StackOffset SETA 6*4            ; stack offset in osbyte routines to user pc
+        ]
+
+        MACRO
+        MyOsbyte $cond
+        B$cond  GoMyOsbyte
+        MEND
+
+        MACRO
+        MyOsWord $cond
+        B$cond  GoMyOsword
+        MEND
+
+        MACRO
+        Unused  $cond
+        MOV$cond PC, R14 ; just return and let the next person have a go
+        MEND
+
+        MACRO
+        ByteReturnV $cond
+        [ AssemblingArthur :LOR: ErrorsInR0
+
+        ASSERT  "$cond"="" :LOR: "$cond"="VS"
+
+        [ "$cond"=""
+        Pull    "R0,R3,$ByteRegs R14,PC", VC
+        ]
+        ADDVS   R13, R13, #4                    ; junk stacked R0
+        Pull    "R3,$ByteRegs R14,PC", VS
+
+        |
+        Pull    "R0,R3,$ByteRegs R14,PC", $cond        ; for GenError systems
+        ]
+        MEND
+
+; Main OSbyte entry point
+; R0,R1,R2 are parameters
+
+OsByte
+        [       AssemblingArthur :LOR: Module
+        Push    "R0, R3, $ByteRegs R14"
+        BL      OsByteGo                        ; Call the subsid entry pt.
+        Pull    "R0,R3"
+        Push    "R0-R4"
+        LDMIA   R13, {R2-R4}                    ; R2=A, R3=X, R4=Y
+        MOV     R1, #Service_UKByte             ; osbyte service reason
+        IssueService
+        TEQ     R1, #0
+        STMEQIA R13, {R2-R4}                    ; if claimed, then update
+                                                ; returned R0-R2
+        CLRPSR  V_bit, R3                       ; clear V flag
+
+        Pull    R0
+        ADRNE   R0, BadCommandError             ; not claimed, R0 -> error
+      [ International
+        BLNE    TranslateError
+      ]
+        SWINE   XOS_GenerateError               ; set V if not claimed
+        Pull    "R1-R4, $ByteRegs R14, PC"
+
+BadCommandError MakeErrorBlock BadCommand
+        |
+        Push    "R0, R3, $ByteRegs R14"
+        BL      OsByteGo
+        Pull    "R0, R3, $ByteRegs PC"        ; no services, so pass it on
+        ]
+
+GoMyOsbyte
+        CLRPSR  V_bit, R3
+        Pull    "R0,R3, $ByteRegs R14,PC" ; pull the world AND the PC to return
+
+
+; *****************************************************************************
+
+OsByteGo ROUT
+        [ :LNOT: AssemblingArthur :LAND: :LNOT: Module
+        BYTEWS  WsPtr
+        ]
+        AND     R0, R0, #&FF            ; no funny business!
+        SUBS    R3, R0, #OsbyteLowLimit ; is it a low one ?
+        BCS     HiOsbyte
+        MOV     R2, #0                  ; lo one, so set Y to 0
+        CMP     R0, #OsbyteVeryLow      ; is it one we recognise ?
+10
+        ADDCC   PC, PC, R0, LSL #2      ; then go thru despatch table
+        B       TryInternational        ; else issue unknown osbyte service
+20
+        ASSERT  %BT20-%BT10 = 8
+
+        BAL     Osbyte00
+        BAL     Osbyte01
+        BAL     Osbyte02
+        BAL     Osbyte03
+        BAL     Osbyte04
+        BAL     Osbyte05
+        BAL     Osbyte06
+        BAL     Osbyte07
+
+        BAL     Osbyte08
+        BAL     Osbyte09
+        BAL     Osbyte0A
+        BAL     Osbyte0B
+        BAL     Osbyte0C
+        BAL     Osbyte0D
+        BAL     Osbyte0E
+        BAL     Osbyte0F
+
+        BAL     Osbyte10
+        BAL     Osbyte11
+        BAL     Osbyte12
+        BAL     Osbyte13
+        BAL     Osbyte14
+        BAL     Osbyte15
+        BAL     Osbyte16
+        BAL     Osbyte17
+
+        BAL     Osbyte18
+        BAL     Osbyte19
+
+HiOsbyte
+        CMP     R0, #MainVars           ; is it a variable ?
+30
+        ADDCC   PC, PC, R3, LSL #2
+        B       DoOsbyteVar             ; yes, then do variable mangling
+40
+        ASSERT  %BT40-%BT30=8
+
+        BAL     Osbyte6A
+        BAL     Osbyte6B
+        BAL     Osbyte6C
+        BAL     Osbyte6D
+        BAL     Osbyte6E
+        BAL     Osbyte6F
+
+        BAL     Osbyte70
+        BAL     Osbyte71
+        BAL     Osbyte72
+        BAL     Osbyte73
+        BAL     Osbyte74
+        BAL     Osbyte75
+        BAL     Osbyte76
+        BAL     Osbyte77
+
+        BAL     Osbyte78
+        BAL     Osbyte79
+        BAL     Osbyte7A
+        BAL     Osbyte7B
+        BAL     Osbyte7C
+        BAL     Osbyte7D
+        BAL     Osbyte7E
+        BAL     Osbyte7F
+
+        BAL     Osbyte80
+        BAL     Osbyte81
+        BAL     Osbyte82
+        BAL     Osbyte83
+        BAL     Osbyte84
+        BAL     Osbyte85
+        BAL     Osbyte86
+        BAL     Osbyte87
+
+        BAL     Osbyte88
+        BAL     Osbyte89
+        BAL     Osbyte8A
+        BAL     Osbyte8B
+        BAL     Osbyte8C
+        BAL     Osbyte8D
+        BAL     Osbyte8E
+        BAL     Osbyte8F
+
+        BAL     Osbyte90
+        BAL     Osbyte91
+        BAL     Osbyte92
+        BAL     Osbyte93
+        BAL     Osbyte94
+        BAL     Osbyte95
+        BAL     Osbyte96
+        BAL     Osbyte97
+
+        BAL     Osbyte98
+        BAL     Osbyte99
+        BAL     Osbyte9A
+        BAL     Osbyte9B
+        BAL     Osbyte9C
+        BAL     Osbyte9D
+        BAL     Osbyte9E
+        BAL     Osbyte9F
+
+        BAL     OsbyteA0
+        BAL     OsbyteA1
+        BAL     OsbyteA2
+        BAL     OsbyteA3
+        BAL     OsbyteA4
+        BAL     OsbyteA5
+
+TryInternational                        ; special ones in the middle
+        TEQ     R0, #OsbyteSetCountry
+        BEQ     DoOsbyteSetCountry
+        TEQ     R0, #OsbyteSetAlphKey
+        BEQ     DoOsbyteSetAlphKey
+        MOV     PC, R14
+
+; *****************************************************************************
+
+; The Osbyte routines themselves
+
+
+; Mos version number and title string
+; R1 = 0 -> give an error with and string MosTitle
+; R1 <>0 -> RETURN with R1 = MosVer
+
+; R2 is Preserved
+
+Osbyte00 ROUT
+        TEQ     R1, #0
+        MOVNE   R1, #MosVer
+        MyOsbyte NE
+        ADR     R0, FX0Error
+        SWI     XOS_GenerateError
+        ByteReturnV
+
+FX0Error
+        &       &F7
+        =       "$MosTitle",0
+        ALIGN
+
+; *****************************************************************************
+
+; Write User Flag
+Osbyte01
+V2B156
+        ADD     R0, R0, #&F0            ; convert 1,5,6 to &F1,&F5,&F6
+        B       DoOsbyteVar
+
+; Select input stream
+Osbyte02 ROUT
+        AND     R0, R1, #1              ; new buffer id
+        TEQ     R1, #0                  ; 0 => disable RXI
+ [ DriversInKernel
+        MOVNE   R1, #RXEN6850           ; else enable RXI
+        MOV     R2, #(&FF :EOR: RXEN6850) ; AND mask (R1 = EOR mask)
+        BL      ModifyControl6850       ; on exit, R1 = old control reg
+        LDRB    R2, RS423conflag        ; R2 = new control reg
+        Push    "R1, R2"
+        BL      RSETX                   ; try to enable RX interrupts
+        Pull    "R1, R2"
+        TEQ     R1, R2
+        BEQ     %FT10                   ; no change in RXI
+
+        Push    R11                     ; now purge data register when going
+        LDR     R11, =ACIA              ; from disabled -> enabled; (also
+        LDRB    R1, ACIARxData          ; does it for enabled -> disabled, this
+                                        ; is irrelevant)
+        Pull    R11
+10
+ |
+        Push    "r0"
+        BNE     %FT10                   ; [enabling serial]
+
+; disable serial by closing stream
+
+        LDRB    r1, SerialInHandle
+        TEQ     r1, #0
+        MOVNE   r0, #0                  ; close file if handle non-zero
+        STRNEB  r0, SerialInHandle      ; zero handle first
+        SWINE   XOS_Find
+        B       %FT20
+
+; enable serial by opening stream
+
+10
+        LDRB    r0, SerialInHandle
+        TEQ     r0, #0                  ; if a stream open already
+        BNE     %FT20                   ; then skip
+
+        MOV     r0, #open_read + open_mustopen
+        ADR     r1, SerialInFilename    ; open serial stream for input
+        SWI     XOS_Find
+        STRVCB  r0, SerialInHandle      ; if did open then store handle
+                                        ; (may store same value if already open, but who cares?)
+20
+        Pull    "r0"
+ ]
+        LDRB    R1, InputStream         ; old input stream
+        STRB    R0, InputStream
+        MyOsbyte
+
+        LTORG
+
+ [ :LNOT: DriversInKernel
+SerialInFilename
+        =       "Serial#Buffer1:", 0
+        ALIGN
+ ]
+
+; Select output stream
+Osbyte03
+
+Osbyte04                                ; select cursor keys actions
+V2B34
+        ADD     R0, R0, #&E9            ; convert 3,4 to &EC,&ED
+        B       DoOsbyteVar
+
+; Write Printer driver type
+Osbyte05 ROUT
+        BL      MakePrinterDormant      ; for netprint
+        MOV     R14, PC                 ; for restoring I afterwards
+10
+        MVN     R3, #I_bit
+        TSTP    R3, PC                  ; CLI
+        TEQP    R14, #0                 ; restore old I
+
+        MOV     R3, #0
+        LDRB    R3, [R3, #ESC_Status]
+        TST     R3, #&40
+        MyOsbyte NE                     ; ESCAPE, so don't change
+
+        LDR     R3, PrinterActive
+        TEQ     R3, #0
+        BNE     %BT10                   ; still active, then loop
+
+; insert code here to notify UPTVEC of change
+
+        B       V2B156                  ; R0 = 5, update variable
+
+; Write Printer Ignore Character
+Osbyte06
+        STRB    R2, NoIgnore            ; (R2=0) allow chars to be ignored
+        B       V2B156
+
+ [ DriversInKernel
+; Write RS423 receive rate
+Osbyte07
+        BL      DoOsbyte07
+        MyOsbyte
+
+; Write RS432 transmit rate
+Osbyte08
+        BL      DoOsbyte08
+        MyOsbyte
+ |
+; Write RS423 receive rate
+; Write RS432 transmit rate
+Osbyte07
+Osbyte08
+        SUB     r0, r0, #2              ; 7 -> 5; 8 -> 6
+        SWI     XOS_SerialOp
+        MyOsbyte
+ ]
+
+ [ DriversInKernel
+; Modified 30-Mar-88 to handle new baud rates
+; Format of SerULAreg is as follows:-
+; Bit   Contents
+; 7     TX3     ; *** NEW ***
+; 6     RX3     ; *** NEW ***
+; 5     RX2
+; 4     RX1
+; 3     RX0
+; 2     TX2
+; 1     TX1
+; 0     TX0
+
+; FX7/8         RX/TX bits above        Baud
+; value         3   2   1   0           rate
+;
+; 1             0   1   1   1           75
+; 2             0   0   1   1           150
+; 3             0   1   0   1           300
+; 4             0   0   0   1           1200
+; 5             0   1   1   0           2400
+; 6             0   0   1   0           4800
+; 7 or 0        0   1   0   0           9600
+; 8             0   0   0   0           19200
+; 9             1   0   1   1           50
+; 10            1   1   0   1           109.92
+; 11            1   0   0   1           134.58
+; 12            1   1   1   0           600
+; 13            1   0   1   0           1800
+; 14            1   1   0   0           3600
+; 15            1   0   0   0           7200
+; 16            1   1   1   1           Undefined
+
+
+DoOsbyte07 ROUT
+        Push    "R11, R14"
+        LDRB    R2, SerULAreg
+        CMP     R1, #16+1
+        BCS     %FT10                   ; invalid
+
+        ADR     R3, SerBaudTable        ; point to silly serproc table
+        LDRB    R3, [R3, R1]            ; get entry
+        BIC     R0, R2, #&78            ; clear old bits
+        ORR     R0, R0, R3, LSL #3      ; or in new bits
+DoFx7or8
+        STRB    R0, SerULAreg
+
+; first set up the carry flag to indicate whether RX = TX
+
+        AND     R1, R0, #&07            ; get Tx bits 0-2
+        TST     R0, #&80
+        ORRNE   R1, R1, #&08            ; R1 = Tx bits
+        EOR     R3, R1, R0, LSR #3      ; EOR Rx and Tx bits
+        AND     R3, R3, #15             ; ignore other bits
+        CMP     R3, #1                  ; C=1 => different
+
+; now program both RX and TX (start with TX)
+
+        ADR     R3, TxBaudTable         ; (R1 = Tx bits)
+        LDRB    R3, [R3, R1]
+
+        MOV     R1, R0, LSR #3          ; R1 = RX bits for later
+        AND     R1, R1, #15
+
+        PHPSEI                          ; NB preserves carry
+        LDR     R11, =ACIA
+        LDRB    R0, ACIAControl         ; replace old baud bits with new
+        AND     R0, R0, #(ACIASBN :OR: ACIAWL1 :OR: ACIAWL0)
+        ORR     R0, R0, R3              ; external RX clock by default
+        ORRCC   R0, R0, #&10            ; if RX=TX use internal RX clock
+        STRB    R0, ACIAControl
+
+        BCC     %FT20                   ; if RX=TX then we've finished
+
+        ADR     R3, RxBaudTable         ; point to timer latch values table
+        LDR     R3, [R3, R1, LSL #2]    ; get entry
+
+        MOV     R0, #IOC
+        STRB    R3, [R0, #Timer2LL]
+        MOV     R3, R3, LSR #8
+        STRB    R3, [R0, #Timer2LH]
+        STRB    R3, [R0, #Timer2GO]
+20
+        PLP
+10
+        MOV     R1, R2                  ; R1 = R2 = "old serproc contents"
+        Pull    "R11, PC"
+
+DoOsbyte08
+        Push    "R11, R14"
+        LDRB    R2, SerULAreg
+        CMP     R1, #16+1
+        BCS     %BT10                   ; invalid
+
+        ADR     R3, SerBaudTable        ; point to silly serproc table
+        LDRB    R3, [R3, R1]            ; get entry
+        BIC     R0, R2, #&87            ; clear old bits
+        TST     R3, #8                  ; if bit 3 is set
+        EORNE   R3, R3, #&88            ; then move to bit 7
+        ORR     R0, R0, R3              ; OR in new bits
+        B       DoFx7or8
+
+        LTORG
+
+SerBaudTable
+        =       4, 7, 3, 5, 1, 6, 2, 4, 0       ; silly table for BBC compat
+        =       11, 13, 9, 14, 10, 12, 8, 15
+
+TxBaudTable  ; ordered on values in SerBaudTable
+        =       Baud19200, Baud1200, Baud4800, Baud150
+        =       Baud9600, Baud300, Baud2400, Baud75
+        =       Baud7200, Baud135, Baud1800, Baud50
+        =       Baud3600, Baud110, Baud600, BaudUndef
+        ALIGN
+
+RxBaudTable  ; ordered on values in SerBaudTable
+        &       2, 51, 12, 416, 6, 207, 25, 832
+        &       8, 463, 34, 1249, 16, 568, 103, 0
+
+ ]
+
+; Write First Flash Time
+Osbyte09
+        MOV     R2, #1
+; and drop thru to ...
+
+; Write Second Flash Time
+Osbyte0A                                        ; (R2=0)
+Osbyte910
+        MOV     R0, R1                          ; new period
+        LDRB    R1, [R2, #OsbyteVars + :INDEX: SpacPeriod] ; get old state
+        STRB    R0, [R2, #OsbyteVars + :INDEX: SpacPeriod] ; store new
+
+        LDRB    R3, FlashCount
+        TEQ     R3, #0                          ; are we frozen ?
+        MyOsbyte NE                             ; no, then finish
+
+        STRB    R0, FlashCount                  ; kick the counter
+        STRB    R2, FlashState                  ; force new state
+
+        VDWS    WsPtr
+
+        TEQ     R2, #0
+        BEQ     ForceSecondState
+
+        Push    "R1,R2"
+        BL      DoFirstFlash
+        Pull    "R1,R2"
+        MyOsbyte
+
+ForceSecondState
+        Push    "R1,R2"
+        BL      DoSecondFlash
+        Pull    "R1,R2"
+        MyOsbyte
+
+
+; Write Keyboard Delay
+Osbyte0B
+V2BBC
+        ADD     R0, R0, #(&C4-&0B)
+        B       DoOsbyteVar
+
+; Write Keyboard Rate
+Osbyte0C
+        TEQ     R1, #0
+        BNE     V2BBC
+        CLRPSR  I_bit, R0       ; this may take some time
+        BL      ReadKeyDefaults
+        MyOsbyte
+
+; *****************************************************************************
+
+; Disable / Enable Events
+; R1 = Event number. Decrement/Increment semaphore for this event
+
+Osbyte0D ROUT
+Osbyte0E ROUT
+        CMP     R1, #32                 ; if illegal event number
+        MOVCS   R2, #0                  ; then return + say was disabled
+        BCS     %FT10
+
+        ADD     R3, WsPtr, #:INDEX: EventSemaphores
+        LDRB    R2, [R3, R1]            ; get semaphore for this event
+
+        CMP     R0, #13                 ; 13 => disable, 14 => enable
+        SUBEQ   R0, R2, #1              ; decrement semaphore
+        ADDNE   R0, R2, #1              ; increment semaphore
+
+        CMP     R0, #&100               ; C=1 => wrapped, so don't store back
+        STRCCB  R0, [R3, R1]
+10
+        MOV     R1, R2                  ; R1 = R2 = old semaphore
+        MyOsbyte
+
+; *****************************************************************************
+
+; Flush Buffer
+Osbyte0F ROUT
+        TEQ     R1, #0
+        BNE     FlushInput
+        BL      FlushAll
+        MyOsbyte
+
+; flush all buffers
+
+FlushAll
+        Push    R14
+        MOV     R1, #(NBuffers-1)
+10
+        BL      FlushThis
+        SUBS    R1, R1, #1
+        BPL     %BT10
+        Pull    PC
+
+; flush input buffer
+
+FlushInput
+        LDROSB  R1, InputStream         ; get buffer id of input stream
+        BL      FlushThis
+        MyOsbyte
+
+; *****************************************************************************
+
+; Clear out the softkeys
+Osbyte12 ROUT
+        MOV     R0, #0
+        STRB    R0, SoftKeyLen          ; purge current expansion
+        MOV     R11, #KeyWorkSpace      ; can corrupt R11
+        Push    R4
+        MOV     R1, #15
+10
+        MOV     R3, R1
+        BL      SetupKeyName            ; exits with R0 -> SoftKeyName
+        MOV     R2, #-1                 ; destroy variable
+        MOV     R3, #0                  ; context pointer 0
+        MOV     R4, #0                  ; type irrelevant
+        SWI     XOS_SetVarVal           ; V will be set if not present
+        SUBS    R1, R1, #1
+        BPL     %BT10
+        Pull    R4
+        MyOsbyte
+
+; *****************************************************************************
+
+; Wait for Vsync
+
+Osbyte13 ROUT
+
+        MOV     R14, PC
+
+        ; bug fix for MED-03165. Having a DPMS-blanked screen stopped printing.
+        ; The reason is that HSyncs stop and VSyncs stop as a consequence,
+        ; but the Hourglass module uses this call to wait for the next VSync
+        ; before animating the hourglass.
+        ; When the screen is DPMS-blanked this osbyte will now return
+        ; immediately. This is equivalent to the operation of the DPMSUtils
+        ; module shipped with OS 3.50.
+
+        VDWS    R2
+
+        LDRB    R0, [R2,#ScreenBlankFlag]
+        LDRB    R1, [R2,#ScreenBlankDPMSState]
+
+        TEQ     R0, #0                  ; NE => blanked
+        TSTNE   R1, #1                  ; NE => blanked and DPMS turned off HSyncs
+        BNE     %FT20                   ; if true exit immediately
+
+        LDRB    R0, CFStime
+10
+        TEQP    R14, #I_bit             ; CLI
+        TEQP    R14, #0                 ; SEI
+        LDRB    R1, CFStime
+        TEQ     R1, R0
+        BEQ     %BT10
+20
+        MyOsbyte
+
+; *****************************************************************************
+
+; Restore font definitions
+Osbyte14
+        MOV     R1, #1                  ; start at character 1*32
+        MOV     R2, #3                  ; do 3 pages
+        B       ResetPartFont
+
+; *****************************************************************************
+
+; Flush Selected Buffer
+Osbyte15        ROUT
+;
+; TMD 24-Apr-92: Don't check buffer number, as this prevents the flushing
+; of buffer manager buffers.
+;
+;        CMP     R1, #NBuffers
+;        BCS     %FT10                   ; invalid buffer number
+        BL      FlushThis
+;10
+        MyOsbyte
+
+
+FlushThis
+
+; code inserted here to zero PrinterActive iff you are flushing the printer
+; buffer and the print destination is not a stream one
+
+ [ DriversInKernel
+        TEQ     R1, #Buff_Print         ; is it the printer buffer ?
+        BNE     %FT15                   ; no, then skip
+
+        LDRB    R0, PrinterDrivType     ; if printer type 0, 1 or 2
+        CMP     R0, #3
+        MOVCC   R0, #0                  ; then mark printer dormant
+        STRCC   R0, PrinterActive
+15
+ ]
+        CMP     R1, #Buff_RS423Out      ; is it an input buffer ? (not mouse)
+        BCS     %FT20                   ; no, then branch
+
+        MOV     R0, #0
+        STRB    R0, SoftKeyLen          ; kill soft key expansion
+        STRB    R0, VDUqueueItems       ; flush VDU queue
+20
+        SETV                            ; indicate purge not count
+        B       CnpEntry
+
+; Reset Group of font definitions
+Osbyte19 ROUT
+        CMP     R1, #8
+        MyOsbyte CS                     ; not in range 0..7, ignore
+        TEQ     R1, #0
+
+        MOVEQ   R1, #1                  ; if 0 then start at 1*32, do 7 pages
+        MOVEQ   R2, #7
+
+        MOVNE   R2, #1                  ; else start at n*32, do 1 page
+ResetPartFont
+
+; first offer to International module
+
+        Push    "R1, R2, R4, R5"
+        MOV     R4, R1, LSL #5          ; R4 = start character
+        ADD     R5, R4, R2, LSL #5      ; R5 = end character+1
+        SUB     R5, R5, #1              ; R5 = end character
+        LDRB    R3, Alphabet
+        MOV     R2, #Inter_Define
+        BL      OfferInternationalService
+        Pull    "R1, R2, R4, R5"
+        MyOsbyte EQ                     ; if claimed, don't use hard font
+
+        ByteToNosbod DoResetFont
+        MyOsbyte
+
+; *****************************************************************************
+
+; Set country number
+; in:   R1 = country number
+
+DoOsbyteSetCountry ROUT
+        TEQ     R1, #&7F                ; if 127, just read country
+        LDREQB  R1, Country
+        MyOsbyte EQ
+
+        BL      GetCountry
+        Push    R4
+        BL      ConvertCNoToANo         ; convert country no. to alphabet no.
+        Pull    R4, NE
+        MOVNE   R1, #0                  ; if not claimed, return with X=0
+        MyOsbyte NE
+
+; was claimed, so have country number in R1 and R3, alphabet no. in R4
+
+        LDRB    R1, Country             ; save old country
+        STRB    R3, Country             ; store new country
+        STRB    R4, Alphabet            ; and new alphabet
+        BL      NewKeyboard             ; R3=new keyboard, R4=alphabet for it
+        BL      SetAlphabet
+        Pull    R4
+        MyOsbyte
+
+SetAlphabet
+        Push    "R1,R5,R14"
+        MOV     R2, #Inter_Define       ; now redefine the chars
+        MOV     R3, R4
+        MOV     R4, #32
+        MOV     R5, #255
+        BL      OfferInternationalService
+        Pull    "R1,R5,PC"
+
+ConvertCNoToANo
+        MOV     R3, R1                  ; put country no. in R3
+        MOV     R2, #Inter_CNoToANo
+OfferInternationalService
+        Push    R14
+        MOV     R1, #Service_International
+        IssueService
+        TEQ     R1, #0                  ; set Z flag if claimed
+        Pull    PC
+
+; Notify keyboard handler of new keyboard
+
+NewKeyboard
+        Push    "R1,R4,R14"
+        STRB    R3, Keyboard
+        STRB    R4, KeyAlphabet
+        MOV     R2, #Inter_Keyboard
+        BL      OfferInternationalService
+        Pull    "R1,R4,PC",,^
+
+; *****************************************************************************
+
+; Set keyboard/alphabet for a particular country
+
+DoOsbyteSetAlphKey ROUT
+        TST     R1, #&80                ; if set then setting keyboard
+        BNE     %FT10                   ; [setting keyboard]
+
+; setting alphabet
+
+        TEQ     R1, #&7F                ; 127 => just read alphabet
+        LDREQB  R1, Alphabet
+        MyOsbyte EQ
+
+; 20/8/87 added code to do setting of default alphabet
+
+        BL      GetCountry
+        Push    R4
+        BL      ConvertCNoToANo         ; try to convert R1 to alphabet number
+        MOVNE   R4, R3                  ; if failed, try without converting
+        BL      SetAlphabet             ; try to set this alphabet
+        Pull    R4
+        MOVNE   R1, #0                  ; if not claimed, return with X=0
+        MyOsbyte NE
+
+        LDRB    R1, Alphabet
+        STRB    R3, Alphabet
+        MyOsbyte
+
+; setting keyboard
+
+10
+        AND     R1, R1, #&7F
+        TEQ     R1, #&7F                ; 127 => just read keyboard
+        LDREQB  R1, Keyboard
+        MyOsbyte EQ
+
+        BL      GetCountry
+        Push    R4
+        BL      ConvertCNoToANo         ; validating country no.
+        Pull    R4, NE
+        MOVNE   R1, #0                  ; if not claimed, return with X=0
+        MyOsbyte NE
+
+        LDRB    R1, Keyboard            ; load old keyboard
+        BL      NewKeyboard             ; R3=new keyboard, R4=alphabet for it
+        Pull    R4
+        MyOsbyte
+
+; *****************************************************************************
+
+; All osbytes from &1A to &69 are unused (apart from international ones!)
+
+; End of unused block
+
+; Write pointer shape number, mouse linkage
+;
+; R1 = 0        => pointer off
+; R1 = 1..4     => use pointer shape 1..4, linked to mouse
+; R1 = &81..&84 => use pointer shape 1..4, unlinked
+;
+
+Osbyte6A
+        VDWS    R0
+        LDRB    R3, [R0, #PointerShapeNumber]   ; get old shape number
+        AND     R2, R1, #&7F                    ; allow 0..4, &80..&84
+        CMP     R2, #4+1
+        STRCCB  R1, [R0, #PointerShapeNumber]
+        MOV     R1, R3
+        MyOsbyte
+
+; Set vdu driver's screen number
+Osbyte70
+        ByteToNosbod DoSetDriverBank
+        MyOsbyte
+
+; Set displayed screen number
+Osbyte71
+        ByteToNosbod DoSetDisplayBank
+        MyOsbyte
+
+; *SHADOW
+Osbyte72
+        MOV     R0, #&EF                ; redirect to shadow variable
+        MOV     R2, #0
+        B       DoOsbyteVar
+
+; *****************************************************************************
+
+; Read VDU Status
+Osbyte75
+        Push    R2
+        ByteToNosbod DoReadVDUStatus
+        Pull    R2
+        MyOsbyte
+
+; Reflect Keyboard Status In LEDs
+Osbyte76
+        MOV     R11, #KeyWorkSpace
+        MOV     R12, #IOC
+        BL      UpdateLEDs
+        MyOsbyte
+
+; Write Keys Pressed Info
+Osbyte78
+        BL      WriteKeysDown
+        MyOsbyte
+
+; Perform Keyboard Scan from 16
+Osbyte7A
+        MOV     R1, #&10
+; and drop thru to ...
+
+; Perform Keyboard Scan
+Osbyte79
+        BL      BBCScanKeys
+        MyOsbyte
+
+; Inform OS Printer Driver going Dormant
+Osbyte7B
+        BL      MakePrinterDormant
+        ByteReturnV
+
+; Clear Escape Condition
+Osbyte7C
+        BL      DoOsbyte7C
+        MyOsbyte
+
+; Set Escape Condition
+Osbyte7D
+        BL      DoOsbyte7D
+        MyOsbyte
+
+DoOsbyte7C
+        Push    "R11, R14"
+        MOV     R11, #0
+        B       Osbyte7C7D
+
+DoOsbyte7D
+        Push    "R11, R14"
+        MOV     R11, #&FF
+Osbyte7C7D
+        [ AssemblingArthur :LOR: Module
+        MOV     R12, #EscHan_ws
+        STRB    R11, [R12, #ESC_Status-EscHan_ws]   ; set escape flag
+        MOV     R14, PC
+        LDMIA   R12, {R12, PC}
+        |
+        MOV     R12, #0
+        STRB    R11, [R12, #ESC_Status]         ; set escape flag
+        MOV     R14, PC                         ; ADRS R14, Exit7D
+        LDR     PC, [R12, #EscHan]
+        ]
+Exit7D
+        TEQ     R12, #1
+ [ Version >= 201
+        Pull    "R11, PC", NE
+
+        MOV     R11, PC                         ; Preserve old processor state
+        ORR     R12, R11, #SVC_mode             ; Switch to SVC mode preserving IRQ_bit
+        TEQP    R12, #0
+        NOP
+        Push    R14                             ; Preserve SVC_R14
+        SWI     XOS_SetCallBack
+        ORRVS   R11, R11, #V_bit                ; Preserve V_bit
+        Pull    R14                             ; Restore SVC_R14
+        TEQP    R11, #0                         ; Switch back to original mode, with V_bit intact from SWI
+        NOP
+
+        Pull    "R11, PC"
+ |
+        STREQB  R12, [R12, #IRQ_CallBack_Flag-1]
+ ]
+        Pull    "R11, PC"
+
+; Acknowledge ESCAPE
+Osbyte7E ROUT
+        MOV     R3, #0
+        LDRB    R3, [R3, #ESC_Status]
+        TST     R3, #&40
+        BEQ     NoESCToAck                      ; escape flag clear
+
+        LDRB    R0, ESCeffect
+        TEQ     R0, #0
+        BNE     NoESCToAck                      ; escape effects disabled
+
+        CLRPSR  I_bit, R0               ; enable interrupts (doing SOUNDs and
+                                        ; closing files may take some time!)
+
+        [ AssemblingArthur :LOR: Module
+        SWI     XSound_QInit
+        BVS     %FT99                           ; no noises anyway!
+        LDR     R0, =&01010008                  ; channel 8, amplitude &101
+        MOV     R1, #&00010000                  ; pitch 0, duration 1
+10
+        SWI     XSound_ControlPacked
+        BVS     %FT99                           ; (R0 would be corrupted)
+        SUB     R0, R0, #1                      ; decrement channel
+        TST     R0, #&FF                        ; if channel <> 0 then loop
+        BNE     %BT10
+99
+        MOV     R0, #0
+        ]
+
+        STRB    R0, PageModeLineCount           ; zero line count
+
+        LDRB    R1, ExecFileH
+        CMP     R1, #0                          ; is EXEC file open (and V:=0)
+        STRNEB  R0, ExecFileH                   ; if so, zero handle and close
+                                                ; (will enable IRQs for me)
+        SWINE   XOS_Find                        ; (R0=0, R1=handle)
+        ByteReturnV VS                          ; if error then bomb out
+
+        BL      FlushAll
+
+NoESCToAck
+        BL      DoOsbyte7C
+        ANDS    R1, R3, #&40                    ; set R1 to 0 if wasn't escape,
+        MOVNE   R1, #&FF                        ; &FF if was
+        MyOsbyte
+
+
+; Check for EOF
+Osbyte7F
+        MOV     R0, #OSArgs_EOFCheck
+        SWI     XOS_Args                        ; result comes back in R2
+        MOV     R1, R2
+        ByteReturnV
+
+; *****************************************************************************
+
+; Read ADC or buffer status
+Osbyte80
+        AND     R1, R1, #&FF            ; no funny business
+
+        TST     R1, #&80                ; is it ADVAL(-n)
+        BEQ     AdvalPositive           ; no, then do adval(+ve)
+        EOR     R1, R1, #&FF            ; convert to buffer number
+        CLC                             ; (C:=0 and V:=0)
+        TEQ     R1, #Buff_Mouse         ; is it mouse (only input buf >= 2) ?
+        CMPNE   R1, #Buff_RS423Out ; C=1 <=> output buffer, so count spaces
+                                        ; V=0, so will do count not purge
+
+        ADR     R14, MyOsbyte80
+CnpEntry
+        Push    "R10,R12,R14"
+        MOV     R10, #CNPV
+        B       GoVec
+
+MyOsbyte80
+        MyOsbyte
+
+AdvalPositive ROUT
+        TEQ     R1, #7
+        TEQNE   R1, #8
+        Unused  NE
+
+        Push    R11
+
+        MOV     R11, R1                 ; save adval number
+        SWI     XOS_Mouse
+        Pull    R11, VS
+        ByteReturnV VS
+
+        TEQ     R11, #7
+        MOVEQ   R1, R0                  ; R1 is required value
+
+        MOV     R2, R1, LSR #8          ; put lo in R1, hi in R2
+        AND     R1, R1, #&FF
+        AND     R2, R2, #&FF
+
+        Pull    R11
+        MyOsbyte
+
+; *****************************************************************************
+
+; Perform INKEY operation
+Osbyte81 ROUT
+        TST     R2, #&80        ; is it negative inkey ?
+        BEQ     %FT10           ; no, then not INKEY-256
+        ANDS    R1, R1, #&FF    ; zero => INKEY(-256)
+        MOVEQ   R1, #ArthurINKEY256     ; then X := OS version number
+        MOVEQ   R2, #0                  ; and  Y := 0
+        MyOsbyte EQ             ; if was INKEY-256 then claim
+10
+        Push    R14             ; save return address for if passing on
+        ADR     R14, My81
+        Push    R14             ; stack 'claiming' return address
+        BL      DoInkeyOp       ; R14 = 'passing on' return address
+NotMy81                         ; DoInkeyOp passed it on
+
+        Pull    "R3,R14"        ; Throw away 'claiming' return address
+                                ; and restore real passing on return address
+        Unused                  ; else pass it on still
+
+My81
+        Pull    R14             ; throw away real passing on address
+        ByteReturnV
+
+
+; *****************************************************************************
+
+; Read Machine High Order Address
+Osbyte82
+        MOV     R1, #&FF                ; Pretend we're an I/O processor !
+        MOV     R2, #&FF
+        MyOsbyte
+
+; *****************************************************************************
+
+; Read OSHWM
+Osbyte83
+        LDRB    R2, OSHWM               ; Read from silly variable
+        MOV     R1, #0                  ; lo-byte is 0
+        MyOsbyte
+
+; *****************************************************************************
+
+; Read Text Cursor Position (input cursor if split)
+Osbyte86
+        ByteToNosbod DoReadPOSVPOSI     ; Results in R1, R2 (i.e. POS, VPOS)
+        MyOsbyte
+
+; *****************************************************************************
+
+; Read Screen Mode and Character at text cursor position
+Osbyte87
+ [ Fix6
+        LDR     R3, [R13, #StackOffset] ; get user's psr
+        ANDS    R3, R3, #I_bit          ; EQ => irqs were on
+        CLRPSR  I_bit, R3, EQ           ; so clear I_bit now
+ ]
+        ByteToNosbod DoOSBYTE87         ; Results in R1, R2 (i.e. char, mode)
+        MyOsbyte
+
+; *****************************************************************************
+
+; Insert Character Into Buffer
+Osbyte8A
+        BL      INSERT
+        MyOsbyte
+
+; *****************************************************************************
+
+; Write Filing System Options : *OPT
+Osbyte8B
+        MOV     R0, #FSControl_OPT
+        SWI     XOS_FSControl
+        ByteReturnV
+
+; *****************************************************************************
+
+; Issue Paged ROM Service Request
+Osbyte8F
+        IssueService
+        MyOsbyte
+
+; *****************************************************************************
+
+; Select vertical screen shift and interlace option :*TV
+Osbyte90
+        LDRB    R0, TVVertical
+        STRB    R1, TVVertical
+        MOV     R1, R0                  ; old vertical in R1
+
+        AND     R0, R2, #1
+        LDRB    R2, TVInterlace         ; old interlace in R2
+        STRB    R0, TVInterlace
+
+        MyOsbyte
+
+; *****************************************************************************
+
+; Get Character From Buffer
+Osbyte91
+        CLRV                                    ; remove not examine
+RemVEntry
+        ADR     R14, MyOsbyte80
+REMOVE
+        Push    "R10,R12,R14"
+        MOV     R10, #REMV
+        B       GoVec
+
+; *****************************************************************************
+
+; Examine Buffer Status
+Osbyte98
+        SETV                                    ; examine not remove
+        B       RemVEntry
+
+; *****************************************************************************
+
+; Insert Character Code Into buffer checking for ESCAPE
+Osbyte99
+        BL      DoInsertESC
+        MyOsbyte
+
+; *****************************************************************************
+
+; Update pseudo 6850 control register and soft copy
+
+Osbyte9C
+        MOV     r0, #7                          ; OS_SerialOp to modify 6850 control register
+        SWI     XOS_SerialOp
+        MyOsbyte
+
+; *****************************************************************************
+
+; 'Fast TUBE BPUT'
+
+Osbyte9D
+        MOV     R0, R1                  ; R0 := character
+        MOV     R1, R2                  ; R1 := handle
+        SWI     XOS_BPut
+        ByteReturnV
+
+; *****************************************************************************
+
+; Read VDU Variable (0..15 implemented)
+OsbyteA0
+        ByteToNosbod DoReadVDUVariable
+        MyOsbyte
+
+; *****************************************************************************
+
+; Read CMOS RAM
+OsbyteA1    ; R1 = address , R2 = result
+        CLRPSR  I_bit, R0       ; this may take some time
+        MOV     R0, R1
+        BL      Read            ; Read CMOS ram at address <R0>
+        MOV     R2, R0          ; Result in R0, return in R2
+        MyOsbyte
+
+; Write CMOS RAM
+OsbyteA2
+        CLRPSR  I_bit, R0       ; this may take some time
+        ANDS    R0, R1, #&FF    ; only look at bottom byte
+        [ ProtectStationID
+        MyOsbyte EQ
+        ]
+        MOV     R1, R2
+        BL      Write
+        MOV     R1, R0          ; R1 is supposed to be preserved
+        MyOsbyte
+
+; *****************************************************************************
+
+; OsByte 163,... applications call
+OsbyteA3 ROUT
+        TEQ     R1, #242
+        Unused  NE                      ; not 242 - pass it on
+        BL      %FT10
+        MyOsbyte                        ; if come to here, has been claimed
+
+10
+        Push    R14
+        ByteToNosbod DoOsbyte163_242
+        Pull    R14                     ; if come to here, wasn't claimed
+        Unused
+
+; *****************************************************************************
+
+; Read Output cursor Position
+OsbyteA5
+        ByteToNosbod DoReadPOSVPOSO     ; Result in R1,R2   (Horiz,vert)
+        MyOsbyte
+
+; *****************************************************************************
+;
+; All calls &A6 to &FF are implemented together.
+; <NEW VALUE> = (<OLD VALUE> AND R2 ) EOR R1
+; The old value is returned in R1 and the next location is returned in R2
+;
+; *****************************************************************************
+
+DoOsbyteVar
+        SUB     R0, R0, #MainVars       ; Point to this block starting at &A6
+        LDRB    R3, [WsPtr, R0]         ; Load the byte
+        AND     R11, R3, R2             ; Mangle it as required by the law
+        EOR     R11, R11, R1            ; ................................
+        MOV     R1, R3                  ; Return old value in R1
+        STRB    R11, [R0, WsPtr]!       ; R0 +:= WsPtr
+        LDRB    R2, [R0, #1]            ; Return contents of next loc in R2
+ [ {FALSE}                              ; This code didn't work in 3.00 because R0 isn't the offset from MainVars
+                                        ; anymore - anyway, the code is not needed because the kernel closes the
+                                        ; stream on an open request.
+        TEQ     R0, #OsbyteWrchDests-MainVars
+        MyOsbyte NE                     ; fast exit if not setting wrch destinations
+        TST     R11, #1                 ; is serial output now disabled
+        MyOsbyte NE
+        LDRB    R1, SerialOutHandle     ; see if serial stream open
+        TEQ     R1, #0                  ; if it is
+        MOVNE   R0, #0                  ; then close it
+        STRNEB  R0, SerialOutHandle     ; zeroing handle beforehand
+        SWINE   XOS_Find
+ ]
+        MyOsbyte
+
+        LTORG
+
+; All the unused OS_Byte calls
+
+; ADC stuff
+Osbyte10 ROUT
+Osbyte11 ROUT
+; Incr/Decr Polling Int
+Osbyte16
+Osbyte17
+; Unused
+Osbyte18
+; Write 1MHz bus selection
+Osbyte6B
+; Write Usage of Shadow memory for normal access
+Osbyte6C
+; Make temporary filing system permanent
+Osbyte6D
+; &6E and &6F are reserved by 3rd parties
+Osbyte6E
+Osbyte6F
+; &73 and &74 reserved for Electron
+Osbyte73
+Osbyte74
+; Close SPOOL(ON) & EXEC files
+Osbyte77
+; Read top of USER RAM
+Osbyte84
+; Read top of user RAM for given mode
+Osbyte85
+; *CODE
+Osbyte88
+; *MOTOR
+Osbyte89
+; *TAPE
+Osbyte8C
+;  *ROM
+Osbyte8D
+; Enter Language ROM
+Osbyte8E
+; Access Mem.Map.IO &92..&97
+Osbyte92
+Osbyte93
+Osbyte94
+Osbyte95
+Osbyte96
+Osbyte97
+; Write to VidULA & COPY
+Osbyte9A
+Osbyte9B
+; Old Style Speech
+Osbyte9E
+Osbyte9F
+; Check Processor Type
+OsbyteA4
+        Unused
+
+; *****************************************************************************
+;
+;       GetCountry - Read country
+;
+; in:   R1 = country number or alphabet number
+;
+; out:  IF R1=0 THEN
+;         R1:=Configured Country
+;         IF R1=0 THEN
+;           R1:=LastKbId
+;           IF R1>=&20 THEN R1:=0
+;         ENDIF
+;       ENDIF
+;
+;       R0 undefined
+;
+
+GetCountry ROUT
+        TEQ     R1, #0                          ; if not setting default, exit
+        MOVNE   PC, R14
+
+        Push    R14
+        MOV     R0, #CountryCMOS                ; read configured country
+        BL      Read
+        MOVS    R1, R0                          ; if not Country Default, exit
+        Pull    PC, NE
+
+        MOV     R0, #KeyWorkSpace
+        LDRB    R1, [R0, #:INDEX: LastKbId]     ; read last valid keyboard id
+        CMP     R1, #&20                        ; if <&20 then use this
+        MOVCS   R1, #0                          ; else set to 0
+        Pull    PC
+
+        END
diff --git a/s/PMF/oseven b/s/PMF/oseven
new file mode 100644
index 0000000000000000000000000000000000000000..7afa0ee96741b854f73b3b4ea245683c4a717378
--- /dev/null
+++ b/s/PMF/oseven
@@ -0,0 +1,391 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+;  > $.Source.PMF.oseven
+
+ [ DriversInKernel
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       PrinterIRQ - Process printer acknowledge IRQ
+;
+
+PrinterIRQ ROUT
+        [ AssemblingArthur
+        Push    R14                     ; stack R14 if new MOS IRQ vector
+        |
+        BYTEWS  WsPtr                   ; already set up in new IRQ scheme
+        ]
+        MOV     R0, #pack_bit
+        STRB    R0, [R3, #IOCIRQCLRA]   ; clear interrupt even if centronics
+                                        ; printer not selected (unlike Master!)
+        LDRB    R1, PrinterDrivType
+        TEQ     R1, #1                  ; parallel printer selected ?
+        BLEQ    STROBE                  ; if so, then try to send another char
+        Pull    PC
+
+; *****************************************************************************
+;
+;       STROBE - Remove char from printer buffer and "print" it
+;
+; in:   WsPtr -> OsbyteVars
+;       MUST be called with IRQs disabled
+
+STROBE  ROUT
+        MOV     R1, #Buff_Print                 ; printer buffer id
+        LDRB    R0, PrinterDrivType
+        CMP     R0, #3                          ; is it user/net ?
+        MOVCS   PC, R14                         ; don't do anything
+
+        CMP     R0, #1
+        BCC     FlushThis                       ; printer sink - flush buffer
+        BNE     %FT10                           ; must be RS423
+
+; centronics printer
+
+        Push    R14
+        BL      REMOVE                          ; note V=0 from CMP above
+                                                ; so this is remove
+        MOV     R3, #IOC
+
+; disable FIQs around this bit
+
+        MOV     R14, #F_bit
+        TST     R14, PC                         ; (preserves carry)
+        MOVNE   R14, #0
+        TEQP    R14, PC
+
+        LDRB    R2, [R3, #IOCIRQMSKA]
+        ORRCC   R2, R2, #pack_bit               ; if char, enable printer IRQ
+        BICCS   R2, R2, #pack_bit               ; else disable printer IRQ
+        STRB    R2, [R3, #IOCIRQMSKA]
+
+        TEQP    R14, PC                         ; restore old F bit
+        MOVCC   R2, #1                          ; printer active
+        MOVCS   R2, #0                          ; printer dormant
+        STR     R2, PrinterActive
+
+        Pull    PC, CS                          ; no more chars to print
+
+        LDR     R2, =PrinterData
+        STRB    R0, [R2]                        ; write data out
+
+        Push    R1
+
+        MOV     R0, #pstrobe_bit        ; enable strobe
+        MVN     R1, R0                  ; L:= (L AND NOT strobe) EOR strobe
+        BL      UpdateLatchB
+
+        MOV     R0, #0                  ; and disable it ( >.5 microsecs later)
+        BL      UpdateLatchB            ; L:= (L AND NOT strobe) EOR 0
+
+        Pull    "R1,PC"
+
+10                                              ; RS423 printer
+        STR     R0, PrinterActive               ; mark printer active (R0<>0)
+RSBUSY
+        MOV     R0, #0                          ; mark RS423 in use (+ve)
+        STRB    R0, RS423use
+        LDR     R0, SerialFlags
+        TST     R0, #(1:SHL:SF_XONXOFFOn)       ; if xon/xoff protocol
+        Push    R14, NE
+        BNE     EnableTXI                       ; then enable TXI regardless
+RSETX
+        Push    R14
+        BL      CountRS
+        Pull    PC, CC                          ; RX overflow, so exit
+
+        LDR     R1, SerialFlags
+        TST     R1, #(1:SHL:SF_XONXOFFOn)       ; if not xon/xoff
+        BEQ     EnableTXI                       ; then enable transmit
+                                                ; (pulls PC to exit)
+        BIC     R2, R1, #(1:SHL:SF_IXOFFedHim)  ; clear my XOFF bit
+        STR     R2, SerialFlags                 ; and store back
+        TST     R2, #(1:SHL:SF_UserXOFFedHim)   ; if user still XOFFing him
+        Pull    PC, NE                          ; then return
+        TST     R1, #(1:SHL:SF_IXOFFedHim)      ; else if we weren't XOFFing
+        Pull    PC, EQ                          ; then return
+
+; we were XOFFing him and user wasn't, so XON him now
+
+        MOV     R1, #XONChar
+        B       SendXONXOFF
+
+        LTORG
+
+; *****************************************************************************
+;
+;       CheckBusy - Check for printer being busy
+;
+; in:   R12 -> BYTEWS
+;
+; out:  Z=1 => printer wasn't busy, so OK to send char
+;       Z=0 => printer was busy, so p_ack IRQ has been reenabled
+;
+
+CheckBusy ROUT
+        LDRB    R0, PrinterDrivType             ; centronics printer ?
+        TEQ     R0, #1
+        ORRNES  PC, R14, #Z_bit                 ; no, then can't be busy
+        MOV     R0, #IOC
+        LDRB    R1, [R0, #IOCIRQSTAA]
+        TST     R1, #pbusy_bit                  ; 0 => busy
+        ORRNES  PC, R14, #Z_bit                 ; indicate not busy
+
+        LDRB    R1, [R0, #IOCIRQMSKA]           ; is busy, so enable
+        ORR     R1, R1, #pack_bit               ; pack IRQ
+        STRB    R1, [R0, #IOCIRQMSKA]
+
+        MOV     R1, #1                          ; and mark printer active
+        STR     R1, PrinterActive
+
+        BICS    PC, R14, #Z_bit                 ; indicate busy
+
+ ]
+
+; *****************************************************************************
+;
+;       SWIPrintChar - Entry for SWI OS_PrintChar
+;
+; in:   R0 = character
+
+SWIPrintChar ROUT
+        Push    "R0-R9, R14"            ; save registers
+        BYTEWS  WsPtr
+        CLRV                            ; R14 on entry must have V=0
+        BL      SWIPrintCharEntry
+        STRVS   R0, [R13, #0]           ; if error then poke R0 to stack
+        Pull    "R0-R9, R14"
+        B       SLVK_TestV              ; return appropriately
+
+; *****************************************************************************
+;
+;       MOSDoPrint - Print a character via VDU 1
+;
+; in:   R0 = character
+;
+; out:  V=0 => printed OK
+;       V=1 => error, R0 -> error block
+;
+
+MOSDoPrint ROUT
+        Push    "R12,R14"
+        BYTEWS  WsPtr
+        BL      MOSDoPrintWS
+        Pull    "R12,PC"
+
+MOSDoPrintWS                                    ; entry point when R12-> BYTEWS
+        BIC     R14, R14, #V_bit                ; clear V by default
+        LDRB    R1, WrchDest
+        TST     R1, #4                          ; is printer disabled ?
+        MOVNES  PC, R14                         ; yes, then return (and V:=0)
+SWIPrintCharEntry
+        LDRB    R1, PrinterDrivType             ; is it user/net ???
+ [ DriversInKernel
+        CMP     R1, #3
+        BCS     FudgePrinterInsert              ; then output to stream
+
+        Push    R14
+        SETPSR  I_bit, R2                       ; disable IRQs
+
+        MOV     R1, #Buff_Print                 ; printer buffer id
+        BL      WRITE                           ; write char to buffer,
+                                                ; waiting if necessary
+        Pull    R14, CS
+        MOVCSS  PC, R14                         ; escape, so bomb out,
+                                                ; clearing V and restoring I
+
+        LDR     R0, PrinterActive
+        TEQ     R0, #0                          ; 0 => dormant
+
+        BLEQ    CheckBusy                       ; check for busy signal
+        BLEQ    STROBE                          ; dormant, so wake it up
+        Pull    R14
+        MOVS    PC, R14                         ; go back
+                                                ; clearing V and restoring I
+ |
+        B       FudgePrinterInsert
+ ]
+
+; *****************************************************************************
+;
+;       WRITE - Insert character into buffer
+;       Retries until successful or ESCAPE condition
+;
+; in:   R0 = character
+;       R1 = buffer number
+;
+
+WRITE   ROUT
+        Push    R14
+10
+        BL      INSRT
+        Pull    PC, CC
+
+; insert code here to turn LEDs on
+
+        MOV     R2, #0
+        LDRB    R2, [R2, #ESC_Status]
+        MOVS    R2, R2, LSL #(32-6)
+        Pull    PC, CS                  ; escape
+
+        MVN     R2, #I_bit              ; mask to clear I_bit
+        MOV     R14, PC
+        TSTP    R2, PC                  ; CLI
+        TEQP    PC, R14                 ; restore I
+        B       %BT10
+
+; *****************************************************************************
+;
+;       FudgePrinterInsert - Write byte to net printer
+;
+; in:   R0 = character to send
+;       R1 = printer type (3..255)
+;       R12 -> BYTEWS
+;       R14 has V_bit clear
+;
+; out:  V=0 => printed OK
+;       V=1 => error, R0 -> error block
+;
+
+FudgePrinterInsert ROUT
+        Push    R14
+
+        LDR     R2, PrinterActive               ; R1=handle for printer stream
+        TEQ     R2, #0
+        MOVNE   R1, R2                          ; already have handle, so can
+        BNE     %FT10                           ; corrupt R1
+
+        Push    R0                              ; save character
+        ADR     R0, PrinterTypeString
+        ADR     R2, PrinterTypeName
+05
+        LDRB    R3, [R0], #1                    ; copy string from ROM to RAM
+        TEQ     R3, #0
+        STRNEB  R3, [R2], #1
+        BNE     %BT05
+
+        BL      OutputDecimalR1ToR2
+
+        MOV     R0, #0                          ; terminate with 0
+        STRB    R0, [R2]
+        ADR     R0, PrinterTypeName+1           ; pointer to variable name
+        MOV     R1, R2                  ; dummy expansion pointer
+                                        ; (saving ptr to end of string!)
+        MOV     R2, #-1                         ; don't accept chars
+        MOV     R3, #0                          ; no wildcarding
+        MOV     R4, #VarType_Expanded
+        SWI     XOS_ReadVarVal                  ; on exit, R2=0 => no such var
+        TEQ     R2, #0
+        Pull    "R0,PC", EQ, ^                  ; so ignore this stream (V:=0)
+
+        MOV     R0, #">"                        ; now stick ">",13 on end
+        STRB    R0, [R1], #1
+        MOV     R0, #13
+        STRB    R0, [R1]
+
+        ADR     R1, PrinterTypeName             ; point to "<PrinterType$nnn>"
+        MOV     R0, #(open_write + open_mustopen + open_nodir)
+        SWI     XOS_Find
+        BLVS    StopPrinting                    ; stop printing
+        Pull    "R1, PC", VS                    ; exit V set, not corrupting R0
+
+        MOV     R1, R0                          ; will always be non-zero
+        Pull    R0                              ; restore character
+
+        STR     R1, PrinterActive               ; store new handle
+10
+        SWI     XOS_BPut                        ; R0 = byte to send
+        Pull    PC, VC                          ; no error, so exit
+
+        BL      StopPrinting                    ; preserves R0,R1
+        Push    R0                              ; save error pointer
+        MOV     R0, #0                          ; CLOSE reason code
+        STR     R0, PrinterActive               ; zero handle
+        SWI     XOS_Find                        ; close channel
+        Pull    "R0,R14"
+        ORRS    PC, R14, #V_bit                 ; return V set
+
+
+PrinterTypeString
+        =       "<",PrinterPrefix,0
+        ALIGN
+
+MakePrinterDormant
+ [ DriversInKernel
+        LDRB    R3, PrinterDrivType
+        CMP     R3, #3                          ; only dormitorise user/net (will clear V)
+        MOVCC   PC, R14
+ ]
+        LDR     R3, PrinterActive               ; printer handle
+        CMP     R3, #0                          ; no active handle if 0 (also clears V if zero)
+        MOVEQ   PC, R14
+        Push    "R0,R1,R14"
+        MOV     R1, R3                          ; R1 = handle
+        MOV     R0, #0                          ; close reason code
+        STR     R0, PrinterActive               ; zero handle
+        SWI     XOS_Find
+        BLVS    StopPrinting
+        Pull    R0, VC                          ; if no error, preserve R0
+        ADDVS   R13, R13, #4                    ; else junk R0
+        Pull    "R1,PC"
+
+; *****************************************************************************
+;
+;       StopPrinting - Clear bits 3 and 5 of FX3, bit 0 of VduStatus
+;
+;       Preserves all registers (including status)
+;
+
+StopPrinting ROUT
+        Push    "R0,R1"
+        LDRB    R0, WrchDest
+        BIC     R0, R0, #(1:SHL:3) :OR: (1:SHL:5)
+        STRB    R0, WrchDest
+        VDWS    R1
+        LDR     R0, [R1, #VduStatus]
+        BIC     R0, R0, #1                      ; clear VDU 2 bit
+        STR     R0, [R1, #VduStatus]
+        Pull    "R0,R1"
+        MOVS    PC, R14                         ; returning preserving flags
+
+; *****************************************************************************
+;
+;       OutputDecimalR1ToR2 - Output a decimal byte
+;
+; in:   R1 = number to output
+;       R2 = buffer to accept chars
+;
+; out:  R0, R3 corrupt
+
+OutputDecimalR1ToR2 ROUT
+        MOV     R0, #100                        ; do hundreds first
+10
+        MOV     R3, #"0"
+20
+        SUBS    R1, R1, R0
+        ADDCS   R3, R3, #1
+        BCS     %BT20
+        ADD     R1, R1, R0
+        CMP     R3, #"0"                        ; if digit non-zero
+        STRNEB  R3, [R2], #1                    ; then output
+        TEQ     R0, #10
+        MOVNE   R0, #10                         ; then do tens digit
+        BNE     %BT10
+
+        ORR     R1, R1, #"0"
+        STRB    R1, [R2], #1                    ; output units digit
+        MOV     PC, R14
+
+
+        END
diff --git a/s/PMF/osinit b/s/PMF/osinit
new file mode 100644
index 0000000000000000000000000000000000000000..b88106838ba88ec4185e1073946b50862c4dec7d
--- /dev/null
+++ b/s/PMF/osinit
@@ -0,0 +1,1246 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.PMF.osinit
+
+        GBLL    ErrorsInR0
+ErrorsInR0 SETL Module                  ; if FALSE, use XOS_GenerateError for
+                                        ; RAM version
+                                        ; if TRUE, return error ptr in R0
+
+        GBLL    ProtectStationID        ; if TRUE, disallow OSBYTE &A2,0,n
+ProtectStationID SETL {TRUE}
+
+; *****************************************************************************
+
+ExecuteInit ROUT
+        Push    R14
+
+       ; Point to OsbyteVars
+       ; and initialise them
+
+        BYTEWS  WsPtr
+
+        LDRB    R1, LastBREAK           ; 0 => soft, 1 => power-on, 2 => hard
+        CMP     R1, #1
+        ADRCC   R2, SoftResetVars
+        ADREQ   R2, PowerOnResetVars
+        ADRHI   R2, HardResetVars
+
+        LDRCCB  R3, NoIgnore            ; preserve NoIgnore over soft reset
+        MOVCS   R3, #0                  ; if hard or power-on reset, zero it
+        STRCS   R3, TimerAlpha +0       ; and zero both copies of TIME
+        STRCS   R3, TimerAlpha +4
+        STRCS   R3, TimerBeta +0
+        STRCS   R3, TimerBeta +4
+
+        MOV     R1, WsPtr               ; start at the beginning
+        ADR     R11, ByteVarInitTable
+
+ByteVarInitLoop
+        LDRB    R0, [R11], #1           ; copy a byte from table
+        STRB    R0, [R1], #1            ; to vars
+        TEQ     R1, R2                  ; at end ?
+        BNE     ByteVarInitLoop         ; [no, then loop]
+
+        STRB    R3, NoIgnore            ; put NoIgnore back
+
+; Initialise buffer pointers
+
+        MOV     R0, #4*(NBuffers-1)     ; index to pointer
+        MOV     R1, #0                  ; value to store
+BuffPtrInitLoop
+        STR     R1, [R0, #BuffInPtrs]
+        STR     R1, [R0, #BuffOutPtrs]
+        SUBS    R0, R0, #4
+        BPL     BuffPtrInitLoop
+
+; mark printer as dormant
+
+        STR     R1, PrinterActive       ; (R1=0)
+
+; Initialise event semaphores
+
+        ADR     R0, EventSemaphores
+        ADD     R2, R0, #32
+10
+        STR     R1, [R0], #4            ; clear all 32 event semaphores
+        TEQ     R0, R2
+        BNE     %BT10
+
+        STRB    R1, FlashState
+        STRB    R1, SerialInHandle      ; zero serial handles
+        STRB    R1, SerialOutHandle
+
+; Initialise LatchB and soft copy
+
+;        MOV     R1, #0          ; AND with 0 (was omitted on earlier MOS) ; R1 already zero
+        MOV     R0, #0          ; EOR with 0
+        BL      UpdateLatchB
+
+ [ DriversInKernel
+        MOV     R1, #DIVRESET6850       ; reset 6551
+        MOV     R2, #0
+        BL      ModifyControl6850
+ ]
+
+; set up IOC timer 0 before we read from CMOS (cos it uses timer for delays)
+
+        MOV     R1, #IOC
+        LDR     R0, =20000-1    ; R0 = Timer delay (units of 0.5 microsecond)
+                                ; 20000*0.5E-6 = 0.01 Seconds 100Hz
+                                ; TMD 21-May-93: "-1" correction applied
+
+        STRB    R0, [R1, #Timer0LL]     ; Set up the delay
+        MOV     R0, R0, LSR #8
+        STRB    R0, [R1, #Timer0LH]
+        STRB    R0, [R1, #Timer0GO]     ; and start the ticks
+
+        LDRB    R0, LastBREAK
+        TEQ     R0, #0
+        BEQ     %FT20
+
+        BL      ReadMachineType
+        BL      ReadUniqueID
+        BL      ReadHardCMOSDefaults
+20
+        BL      ReadCMOSDefaults
+ [ StorkPowerSave
+        BL      PowerHardware           ;On Stork, ensure Combo chip, Winnie, Floppy etc are powered
+ ]
+ [ IO_Type = "IOMD"
+        BL      Configure37C665         ;RiscPC, Kryten and Stork use only SMC 37C665
+ |
+        BL      Configure82C710         ;Earlier code copes with 82C710,82C711 and 37C665
+ ]
+
+ [ DriversInKernel
+; reset ACIA
+
+        LDR     R11, =ACIA
+
+        LDRB    R0, ACIAStatus          ; set up DCD, DSR bits in SerialFlags
+        AND     R0, R0, #(ACIADSR :OR: ACIADCD)
+        MOV     R0, R0, LSL #DCDDSRShift
+        STR     R0, SerialFlags         ; other bits are zero
+
+        LDRB    R0, ACIACommand
+        ORR     R0, R0, #ACIADTR        ; enable transmit and receive
+        STRB    R0, ACIACommand
+ ]
+
+        MOV     R1, #IOC
+
+        [ :LNOT: NewClockChip
+        MOV     R0, #0
+        STRB    R0, SecondsTime         ; zero seconds and centiseconds
+        STRB    R0, CentiTime
+
+        LDRB    R0, [R1, #IOCControl]   ; load IOC control register
+        AND     R0, R0, #rtc_minutes_bit
+        STRB    R0, MinTick             ; Initialise the minute odd/even flag
+
+        MOV     R0, #1
+        STRB    R0, SecondsDirty        ; mark the seconds as dirty (non-zero)
+        ]
+
+ [ DriversInKernel
+        MOV     R0, #timer0_bit :OR: pack_bit :OR: vsync_bit
+ |
+        MOV     r0, #timer0_bit :OR: vsync_bit
+ ]
+        STRB    R0, [R1, #IOCIRQCLRA]   ; clear pending tim0, vsync irqs (+ pack irq if appropriate)
+        LDRB    R0, [R1, #IOCIRQMSKA]
+        ORR     R0, R0, #timer0_bit :OR: vsync_bit
+        STRB    R0, [R1, #IOCIRQMSKA]   ; Enable timer 0 + vsync irqs
+
+ [ DriversInKernel
+        LDRB    R0, [R1, #IOCIRQMSKB]   ; enable 6551 interrupt
+        ORR     R0, R0, #serial_bit
+        STRB    R0, [R1, #IOCIRQMSKB]
+ ]
+
+        BL      CheckYear               ; check for year wrap scenario
+        BL      RTCToRealTime
+
+; insert soft key 10
+
+        MOV     R2, #&CA
+        BL      RDCHS
+        Pull    PC
+
+        LTORG
+
+; *****************************************************************************
+;
+;       ReadHardCMOSDefaults - Read CMOS values for a hard/power-on reset
+;       NB must be called in supervisor mode
+
+ReadHardCMOSDefaults
+        Push    R14
+
+        MOV     R0, #PigCMOS
+        BL      Read
+        STRB    R0, PrinterIgnore
+
+        MOV     R0, #PSITCMOS
+        BL      Read
+        TST     R0, #2                  ; NoIgnore bit
+        MOVEQ   R1, #0
+        MOVNE   R1, #&80
+        STRB    R1, NoIgnore
+
+        MOV     R1, R0, LSR #5          ; printer type now in bits 0..2
+        STRB    R1, PrinterDrivType
+
+        MOV     R0, #MODETVCMOS
+        BL      Read
+        MOV     R2, R0, LSR #4          ; bit0:=interlace, bits 1-3 := vertical
+        AND     R1, R2, #1
+        STRB    R1, TVInterlace
+        MOV     R2, R2, LSL #31-3       ; bits 29-31 := vertical
+        MOV     R2, R2, ASR #29         ; sign extend
+        STRB    R2, TVVertical
+
+        MOV     R0, #DBTBCMOS
+        BL      Read
+        LDRB    R1, StartOptions
+        TST     R0, #&10                ; bit 4 = boot bit
+        ORREQ   R1, R1, #8              ; noboot => set bit 3
+        BICNE   R1, R1, #8              ; boot => clear bit 3
+        STRB    R1, StartOptions
+
+        LDR     R2, =VduDriverWorkSpace+CursorFlags
+        ANDS    R1, R0, #8              ; noscroll bit - put 0 or 1
+        MOVNE   R1, #1                  ; in bottom byte of CursorFlags
+        STRB    R1, [R2]                ; leave other bytes alone
+
+        MOV     R0, #CountryCMOS        ; read country CMOS and store in var
+        BL      Read                    ; but don't bind 'Default' to a fixed
+        STRB    R0, Country             ; country at this stage
+
+ [ :LNOT: DriversInKernel
+        BL      SetUpPrinterBuffer
+ ]
+        Pull    PC
+
+; *****************************************************************************
+;
+;       ReadCMOSDefaults - Read CMOS values for any reset
+;       NB must be called in supervisor mode
+
+ReadCMOSDefaults
+        Push    R14
+
+ [ DriversInKernel
+        MOV     R0, #PSITCMOS
+        BL      Read
+        MOV     R1, R0, LSR #2          ; baud bits now in bits 0..2
+        AND     R1, R1, #7              ; 0 => 75, ... ,7 => 19200
+        ADD     R1, R1, #1              ; 1 => 75, ... ,8 => 19200
+        Push    R1
+        BL      DoOsbyte07
+        Pull    R1
+        BL      DoOsbyte08
+ ]
+
+        MOV     R0, #DBTBCMOS
+        BL      Read
+        TST     R0, #2                  ; NZ => loud
+        MOVEQ   R1, #&D0                ; (quiet)
+        MOVNE   R1, #&90                ; (LOUD)
+        STRB    R1, BELLinfo
+
+ [ DriversInKernel
+        MOV     R1, R0, LSR #5          ; bits 5..7 -> bits 0..2
+        MOV     R1, R1, LSL #2          ; put in bits 2..4
+        ORR     R1, R1, #&42            ; or in default 6850 bits
+        MOV     R2, #0                  ; replace old value
+        BL      ModifyControl6850
+ ]
+
+        MOV     R0, #StartCMOS
+        BL      Read
+        MOVS    R0, R0, LSL #(32-5)     ; bit 5 -> carry, bit 4 -> N bit
+        MOVPL   R0, #KBStat_NoShiftLock + KBStat_ShiftEnable    ; SHCAPS
+        MOVMI   R0, #KBStat_NoShiftLock + KBStat_NoCapsLock     ; NOCAPS
+        MOVCS   R0, #KBStat_NoShiftLock                         ; CAPS
+        STRB    R0, KeyBdStatus
+
+ [ ModeSelectors
+        BL      Read_Configd_MonitorType
+        LDR     r1, =VduDriverWorkSpace+CurrentMonitorType ; set current to default
+        STR     r0, [r1]
+ ]
+
+        Pull    R14
+
+; and drop thru to ...
+
+ReadKeyDefaults
+        Push    R14
+
+        MOV     R0, #KeyDelCMOS         ; Read the default out of CMOS RAM
+        BL      Read                    ; comes back in R0
+        STRB    R0, KeyRepDelay
+
+        MOV     R0, #KeyRepCMOS         ; Read the default out of CMOS RAM
+        BL      Read                    ; comes back in R0
+        STRB    R0, KeyRepRate
+
+        Pull    PC
+
+; *****************************************************************************
+;
+;       PostInit - Called by Sam after modules have been initialised
+;
+
+PostInit ROUT
+        Push    R14
+        BYTEWS  WsPtr
+        LDRB    R0, LastBREAK           ; get reset type
+        TEQ     R0, #0
+        BEQ     %FT10                   ; [soft reset, skip]
+
+ [ StorkPowerSave
+        SWI     XPortable_ReadFeatures
+        BVC     %FT01
+;
+        MOV     R0, #0
+        MOV     R1, #0
+        SWI     XPortable_Speed         ; attempt to make the portable go fast!
+        MOVVC   R1, #PortableFeature_Speed
+        MOVVS   R1, #0
+01
+        AND     R0, R1, #(PortableFeature_Speed :OR: PortableFeature_Idle :OR: PortableFeature_Stop)
+        STRB    r0, [r0, #PortableFlags]
+ |
+        MOV     r0, #0                  ; allow SWI Portable_Speed to be issued
+        STRB    r0, [r0, #PortableFlag]
+ ]
+
+        [ BleedinDaveBell
+        MOV     R0, #1                  ; indicate keyboard UK
+        MOV     R1, #101                ; indicate alphabet Latin1
+        |
+        MOV     R0, #2                  ; indicate keyboard Master
+        MOV     R1, #100                ; indicate alphabet Bfont
+        ]
+        STRB    R0, Keyboard
+        STRB    R1, Alphabet
+        STRB    R1, KeyAlphabet         ; alphabet corresponding to keyboard
+
+10
+        Pull    R14
+        B       KeyPostInit
+
+; *****************************************************************************
+;
+;       UpdateLatchB - update latch B and soft copy
+;
+;       LATCHB := (LATCHB AND R1) EOR R0
+;
+
+UpdateLatchB
+        Push    "R2, R3, R14"
+        MOV     R14, PC
+        ORR     R2, R14, #I_bit
+        TEQP    R2, #0                  ; disable IRQ
+
+        MOV     R2, #0
+        LDRB    R3, [R2, #LatchBSoftCopy]
+        AND     R3, R3, R1
+        EOR     R3, R3, R0
+        STRB    R3, [R2, #LatchBSoftCopy]
+
+ [ IO_Type <> "IOMD"                          ; there ain't no Latch B on IOMD!
+        LDR     R2, =LatchB
+        STRB    R3, [R2]
+ ]
+
+        TEQP    R14, #0
+        Pull    "R2, R3, PC"
+
+
+; *****************************************************************************
+;
+;       UpdateMonitorTypeLatch - update monitor type latch and soft copy
+;
+;       Returns the result in R4
+
+UpdateMonitorTypeLatch
+        Push    "R2, R3, R14"
+        MOV     R14, PC
+        ORR     R2, R14, #I_bit
+        TEQP    R2, #0                  ; disable IRQ
+
+        MOV     R2, #0
+        LDRB    R3, [R2, #CLine_Softcopy]
+        MOV     R3, #1                  ;Set our bit only
+        STRB    R3, [R2, #CLine_Softcopy]
+
+        MOV     R2, #IOMD_Base
+        STRB    R3, [R2, #IOMD_CLINES]  ;Write the new reg out
+        LDRB    R3, [R2, #IOMD_CLINES]  ;Read it back
+
+        AND     R4, R3, #1              ;Clear all but our bit into R4
+
+        TEQP    R14, #0                 ;Re-enable IRQ
+        Pull    "R2, R3, PC"
+
+
+; *****************************************************************************
+
+; The initial values for all of the osbyte variables
+; as decreed by arthur.
+
+ByteVarInitTable
+                        ; The main osbyte variables, accessed
+                        ; via calls &A6 to &FF
+
+  DCW OsbyteVars-&A6    ; VarStart  #  2     ; &A6,&A7
+  = 0,0                 ; ROMPtr    #  2     ; &A8,&A9
+  = 0,0                 ; ROMInfo   #  2     ; &AA,&AB
+  = 0,0                 ; KBTran    #  2     ; &AC,&AD
+  = 0,0                 ; VDUvars   #  2     ; &AE,&AF
+                        ;
+  = 0                   ; CFStime   #  1     ; &B0
+  = 0                   ; InputStream #  1   ; &B1
+  = &FF                 ; KeyBdSema #  1     ; &B2
+                        ;
+  = &00                 ; ROMPollSema # 1    ; &B3
+  = &80                 ; OSHWM     #  1     ; &B4 (hi-byte of &8000)
+                        ;
+  = 1                   ; RS423mode #  1     ; &B5
+  = 0                   ; NoIgnore  #  1     ; &B6
+  = &00                 ; CFSRFS    #  1     ; &B7
+  = &00,&00             ; VULAcopy  #  2     ; &B8,&B9
+                        ;
+  = &00                 ; ROMatBRK  #  1     ; &BA
+  = &FF                 ; BASICROM  #  1     ; &BB
+                        ;
+  = &04                 ; ADCchanel #  1     ; &BC
+  = &04                 ; ADCmaxchn #  1     ; &BD
+  = &00                 ; ADCconv   #  1     ; &BE
+                        ;
+  = &FF                 ; RS432use     #  1  ; &BF
+  = &42                 ; RS432conflag #  1  ; &C0
+                        ;
+  = 0   ;&19            ; FlashCount # 1     ; &C1      /* changed to fx 9 0 by RCM 31/10/91 */
+  = &19                 ; SpacPeriod # 1     ; &C2
+  = 0   ;&19            ; MarkPeriod # 1     ; &C3      /* changed to fx 9 0 by RCM 31/10/91 */
+                        ;
+  = &32                 ; KeyRepDelay # 1    ; &C4
+  = &08                 ; KeyRepRate  # 1    ; &C5
+                        ;
+  = &00                 ; ExecFileH   # 1    ; &C6
+  = &00                 ; SpoolFileH  # 1    ; &C7
+                        ;
+  = &00                 ; ESCBREAK    # 1    ; &C8 (200)
+                        ;
+  = &00                 ; KeyBdDisable # 1   ; &C9
+  = &20                 ; KeyBdStatus  # 1   ; &CA
+                        ;
+  = &11                 ; RS423HandShake # 1 ; &CB
+  = &00                 ; RS423InputSupr # 1 ; &CC
+  = &00                 ; RS423CFSFlag   # 1 ; &CD
+                        ;
+  = &00                 ; EconetOScall # 1   ; &CE
+  = &00                 ; EconetOSrdch # 1   ; &CF
+  = &00                 ; EconetOSwrch # 1   ; &D0
+                        ;
+  = &00                 ; SpeechSupr # 1     ; &D1
+  = &00                 ; SoundSupr # 1      ; &D2
+                        ;
+  = &01                 ; BELLchannel # 1    ; &D3
+  = &90                 ; BELLinfo    # 1    ; &D4
+  = &64                 ; BELLfreq    # 1    ; &D5
+  = &06                 ; BELLdur     # 1    ; &D6
+                        ;
+  = &81                 ; StartMessSupr # 1  ; &D7
+                        ;
+  = &00                 ; SoftKeyLen # 1     ; &D8
+                        ;
+  = &00                 ; PageModeLineCount # 1          ; &D9
+                        ;
+  = &00                 ; VDUqueueItems # 1  ; &DA
+                        ;
+  = &09                 ; TABch # 1          ; &DB
+  = &1B                 ; ESCch # 1          ; &DC
+                        ;
+  = &01,&D0,&E0,&F0     ; IPbufferCh # 4     ; &DD,&DE,&DF,&E0
+  = &01,&80,&90,&00     ; RedKeyCh   # 4     ; &E1,&E2,&E3,&E4
+                        ;
+  = &00                 ; ESCaction  # 1     ; &E5
+  = &00                 ; ESCeffect  # 1     ; &E6
+                        ;
+  = &00                 ; u6522IRQ # 1       ; &E7
+  = &00                 ; s6850IRQ # 1       ; &E8
+  = &00                 ; s6522IRQ # 1       ; &E9
+                        ;
+  = &00                 ; TubeFlag # 1       ; &EA
+                        ;
+  = &00                 ; SpeechFlag # 1     ; &EB
+                        ;
+  = &00                 ; WrchDest # 1       ; &EC
+  = &00                 ; CurEdit  # 1       ; &ED
+                        ;
+
+  = &30                 ; KeyBase            ; &EE
+  = &01                 ; Shadow             ; &EF
+  = &00                 ; Country            ; &F0
+                        ;
+  = &00                 ; UserFlag # 1       ; &F1
+                        ;
+  = &64                 ; SerULAreg # 1      ; &F2
+                        ;
+  = &05                 ; TimerState # 1     ; &F3
+                        ;
+  = &FF                 ; SoftKeyConsist # 1 ; &F4
+                        ;
+  = &01                 ; PrinterDrivType   # 1          ; &F5
+  = &0A                 ; PrinterIgnore     # 1          ; &F6
+                        ;
+  = &01,&00,&00         ; BREAKvector # 3    ; &F7,&F8,&F9
+                        ;
+  = &00                 ; DRIVER             ; &FA
+  = &00                 ; DISPLAY            ; &FB
+                        ;
+  = &FF                 ; LangROM # 1        ; &FC
+                        ;
+  = &01                 ; LastBREAK # 1      ; &FD
+                        ;
+  = &0F                 ; KeyOpt # 1         ; &FE
+                        ;
+  = &08                 ; StartOptions # 1   ; &FF
+                        ;
+                        ;
+ByteVarInitTableEnd
+
+ByteVarInitTableSize * ByteVarInitTableEnd - ByteVarInitTable
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        LTORG
+
+oldirqowner & IRQ
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       ReadMachineType - Determine machine type and store it in IOSystemType
+;
+
+ReadMachineType ENTRY "r0-r12"
+ [ IO_Type = "IOMD"
+  [ MorrisSupport
+        MOV     r12, #IOMD_Base
+        LDRB    r0, [r12, #IOMD_ID0]
+        CMP     r0, #&98
+        LDRB    r0, [r12, #IOMD_ID1]
+        CMPEQ   r0, #&5B
+        MOVEQ   r11, #IOST_7500                         ;EQ, Morris
+        MOVNE   r11, #0                                 ;NE, assume Medusa
+;
+; On Kryten, Morris pin Event2 is tied low so bit Nevent2 is a ONE
+; On Stork, Morris pin Event2 is tied high so bit Nevent2 is a ZERO
+;
+        LDREQB  r0, [r12, #IOMD_IRQSTD]                 ;EQ, Morris
+        TSTEQ   r0, #IOMD_Nevent2_bit
+        ORREQ   r11, r11, #IOST_BATMAN                  ;EQ, Stork ie Morris & BATMAN
+
+        ORR     r0, r11, #IOST_IOEB                     ; pretend we've got IOEB
+;
+; r11 holds  0                        for  IOMD   (Risc PC)
+;        or  IOST_7500                for  Morris (Kryten)
+;        or  IOST_7500 + IOST_BATMAN  for  Morris (Stork)
+  |
+        MOV     r0, #IOST_IOEB                          ; pretend we've got IOEB
+  ]
+ |
+        TEQP    pc, #SVC_mode + I_bit + F_bit           ; disable IRQs and FIQs
+        MOVNV   r0, r0
+        LDR     r1, =IOEB_ASICPresent
+        MOV     r0, #&FA
+        STRB    r0, [r1]
+        LDRB    r0, [r1]
+        TEQP    pc, #SVC_mode + I_bit
+        AND     r0, r0, #&0F
+        TEQ     r0, #5                                  ; must read 5 is ASIC present
+        MOVNE   r0, #0                                  ; no IOEB, LC or 82C710
+        BNE     %FT10
+
+; there is an IOEB ASIC present, so it's safe to test for LC ASIC
+
+        LDR     r1, =LC_ASICPresent
+        LDRB    r0, [r1]
+        AND     r0, r0, #&0E                            ; bits 3210 should be 010x
+        TEQ     r0, #&04
+        MOVNE   r0, #IOST_IOEB                          ; IOEB but no LC
+        MOVEQ   r0, #IOST_IOEB :OR: IOST_LC             ; IOEB and LC
+10
+ ]
+        MOV     r1, #0                                  ; normal Hsync and address pointer
+        STRB    r0, [r1, #IOSystemType]
+
+; now read monitor lead type
+
+        TEQ     r0, #0                                  ; no IOEB
+        MOVEQ   r2, #&FF                                ; then return all ones for monitor lead type
+        BEQ     %FT90
+
+ [ VIDC_Type = "VIDC20"
+        LDR     r0, =VIDC                       ; on VIDC20 we invert HSYNC by writing to External Register
+ |
+        LDR     r0, =VIDCClockSelect            ; on VIDC1 we write to latch instead
+ ]
+ [ IO_Type = "IOMD"
+        LDR     r3, =IOMD_MonitorType
+ |
+        LDR     r3, =IOEB_MonitorType
+ ]
+
+ [ VIDC_Type = "VIDC20"
+        LDR     r1, =VIDCExternal+Ext_InvertCompVSYNC+Ext_DACsOn+Ext_ERegExt ; normal HSYNC value
+        STR     r1, [r0]
+        ORR     r2, r1, #Ext_InvertHSYNC        ; value for inverted HSYNC
+ |
+        STRB    r1, [r0]                        ; set up normal Hsync
+        MOV     r2, #4                          ; inverted Hsync
+ ]
+
+ [ MorrisSupport
+        TST     R11, #IOST_7500
+        BLNE    UpdateMonitorTypeLatch          ;Result back in R4
+        LDREQB  r4, [r3]
+ |
+        LDRB    r4, [r3]                        ; base value
+ ]
+        AND     r4, r4, #&0F                    ; only use bits 0..3
+        MOV     r5, #0                          ; bits 0..3   = bits which have ever changed
+                                                ; bits 4..7   = bits whose deinverted value was high last time
+                                                ; bits 8..11  = bits whose deinverted value just went high-low
+                                                ; bits 12..15 = bits whose deinverted value just went high-low-low
+                                                ; bits 16..19 = bits which could be hsync
+                                                ;  ie after every high-low there was low-low (after deinversion)
+                                                ; bits 20..23 = bits which are definitely random
+        MOV     r10, #&0F                       ; used inside CheckBits
+        MOV     r12, #256                       ; number of iterations
+20
+ [ VIDC_Type = "VIDC20"
+        STR     r2, [r0]
+ |
+        STRB    r2, [r0]
+ ]
+
+ [ MorrisSupport
+        TST     R11, #IOST_7500
+        BLNE    UpdateMonitorTypeLatch          ;Result back in R4
+        MOVNE   r6, r4
+        LDREQB  r6, [r3]
+ |
+        LDRB    r6, [r3]                        ; read value with inverted sync
+ ]
+
+ [ VIDC_Type = "VIDC20"
+        STR     r1, [r0]
+ |
+        STRB    r1, [r0]
+ ]
+
+ [ MorrisSupport
+        TST     R11, #IOST_7500
+        BLNE    UpdateMonitorTypeLatch          ;Result back in R4
+        MOVNE   r7,r4
+        LDREQB  r7,[r3]
+ |
+        LDRB    r7, [r3]                        ; read value with normal sync
+ ]
+        AND     r6, r6, #&0F
+        AND     r7, r7, #&0F
+        EOR     r8, r6, r4                      ; bits which have changed from steady value to inverted one
+        ORR     r5, r5, r8                      ; OR into mask of bits which have ever changed
+        EOR     r8, r7, r4                      ; bits which have changed from steady value to normal one
+        ORR     r5, r5, r8                      ; OR into mask of bits which have ever changed
+        EOR     r6, r6, #&0F                    ; deinvert inverted value
+        BL      CheckBits                       ; call check routine with first value
+        MOV     r6, r7
+        BL      CheckBits                       ; call check routine with second value
+        SUBS    r12, r12, #1
+        BNE     %BT20
+
+; now process result
+
+ [ VIDC_Type = "VIDC20"
+        LDR     r1, =VIDCExternal+Ext_InvertCompVSYNC+Ext_InvertCompHSYNC+Ext_DACsOn+Ext_ERegExt        ; put back default value
+        STR     r1, [r0]
+ ]
+
+        BIC     r4, r4, r5                      ; don't put port value in for bits that have changed
+        BIC     r5, r5, r5, LSR #16             ; make bits 0..3 of r5 indicate random bits
+
+        ANDS    r2, r4, #1                      ; for each bit pair 00 => low, 01 => high, 10 => Hsync, 11 => random
+        TST     r5, #1 :SHL: 16
+        MOVNE   r2, #2 :SHL: 0
+        TST     r5, #1
+        MOVNE   r2, #3 :SHL: 0
+
+        TST     r4, #2
+        ORRNE   r2, r2, #1 :SHL: 2
+        TST     r5, #2 :SHL: 16
+        ORRNE   r2, r2, #2 :SHL: 2
+        TST     r5, #2
+        ORRNE   r2, r2, #3 :SHL: 2
+
+        TST     r4, #4
+        ORRNE   r2, r2, #1 :SHL: 4
+        TST     r5, #4 :SHL: 16
+        ORRNE   r2, r2, #2 :SHL: 4
+        TST     r5, #4
+        ORRNE   r2, r2, #3 :SHL: 4
+
+        TST     r4, #8
+        ORRNE   r2, r2, #1 :SHL: 6
+        TST     r5, #8 :SHL: 16
+        ORRNE   r2, r2, #2 :SHL: 6
+        TST     r5, #8
+        ORRNE   r2, r2, #3 :SHL: 6
+
+ [ IO_Type = "IOMD"
+        ASSERT  IOMD_MonitorIDMask = 1
+        AND     r2, r2, #3                      ; only bit 0 of ID valid on IOMD-based systems
+ ]
+
+90
+        MOV     r1, #0
+        STRB    r2, [r1, #MonitorLeadType]
+        EXIT
+
+CheckBits ROUT
+        AND     r8, r10, r5, LSR #12            ; bits that were H-L-L
+        BIC     r8, r8, r6                      ; bits that are H-L-L-L
+        ORR     r5, r5, r8, LSL #16             ; OR into bits that could be hsync
+        ORR     r8, r5, r5, LSR #4
+        AND     r8, r6, r8, LSR #8              ; bits that just went H-L-H or H-L-L-H
+        AND     r8, r8, r5, LSR #16             ; bits that just went H-L-H or H-L-L-H and could have been hsync
+        ORR     r5, r5, r8, LSL #20             ; they're definitely random now
+        BIC     r5, r5, r8, LSL #16             ; and they're definitely not hsync now
+        AND     r8, r5, #&FF :SHL: 4            ; get H bits, and H-L bits
+        BIC     r8, r8, r6, LSL #4              ; knock out bits that were H and are now H
+        BIC     r8, r8, r6, LSL #8              ; knock out bits that were H-L and are now H
+        BIC     r5, r5, #&FF :SHL: 8            ; knock out all H-L and H-L-L bits
+        ORR     r5, r5, r8, LSL #4              ; put in new H-L and H-L-L bits
+        BIC     r5, r5, #&F :SHL: 4             ; knock out old H bits
+        ORR     r5, r5, r6, LSL #4              ; put in new H bits
+        BIC     r5, r5, r5, LSR #20             ; knock out H bits if we know it's random
+        MOV     pc, lr
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       TranslateMonitorLeadType - Determine monitor type and default mode + sync from monitor lead type
+;
+; in:   Monitor lead type in variable MonitorLeadType (surprisingly!)
+;
+; out:  r3 = default mode to use
+;       r4 = default monitortype to use
+;       r5 = default sync to use
+;
+
+TranslateMonitorLeadType ENTRY "r0-r2"
+        MOV     r1, #Service_MonitorLeadTranslation
+        LDRB    r2, [r1, #MonitorLeadType-Service_MonitorLeadTranslation]
+        SWI     XOS_ServiceCall
+        TEQ     r1, #0                          ; if service claimed, then exit with these numbers
+        EXIT    EQ
+
+        ADR     r0, MonitorLeadList
+10
+        LDR     r14, [r0], #4
+        EOR     r1, r2, r14, LSR #24            ; differences
+        EOR     r14, r14, #&FF000000            ; make don't cares into zero
+        TST     r14, #&C0000000
+        BICEQ   r1, r1, #&C0                    ; knock out difference pairs if don't care
+        TST     r14, #&30000000
+        BICEQ   r1, r1, #&30
+        TST     r14, #&0C000000
+        BICEQ   r1, r1, #&0C
+        TST     r14, #&03000000
+        BICEQ   r1, r1, #&03
+        TEQ     r1, #0                          ; if still have differences, then loop
+        BNE     %BT10
+
+        MOV     r0, #&FF
+        AND     r3, r0, r14                     ; mode in bits 0..7
+        AND     r4, r0, r14, LSR #8             ; monitortype in bits 8..15
+        AND     r5, r0, r14, LSR #16            ; sync in bits 16..23
+        EXIT
+
+        MACRO
+        MonitorLeadItem $lead, $mode, $monitortype, $sync
+        ASSERT $lead < 256
+        ASSERT $mode < 256
+        ASSERT $monitortype < 256
+        ASSERT $sync < 256
+        DCD     (($lead):SHL:24):OR:(($sync):SHL:16):OR:(($monitortype):SHL:8):OR:($mode)
+        MEND
+
+ [ IO_Type = "IOMD"
+MonitorLeadList
+        MonitorLeadItem 4_3330,  27, 3, 0                       ; VGA-capable monitors
+        MonitorLeadItem 4_3333,  12, 0, 1                       ; Others - assume TV standard
+ |
+MonitorLead_MonoVGA     *       4_3101
+MonitorLead_ColourVGA   *       4_3110
+MonitorLead_ColourSVGA  *       4_3010          ; should be type 4, but for the LiteOn we bodge it to 1
+MonitorLead_Multisync   *       4_3211
+MonitorLead_TV          *       4_3112
+MonitorLead_NoConnect   *       4_3111
+MonitorLead_Undefined   *       4_3333
+
+MonitorLeadList
+        MonitorLeadItem MonitorLead_Multisync,  27, 1, 1
+        MonitorLeadItem MonitorLead_MonoVGA,    27, 3, 0
+        MonitorLeadItem MonitorLead_ColourVGA,  27, 3, 0
+        MonitorLeadItem MonitorLead_ColourSVGA, 27, 1, 0        ; bodge for LiteOn (should be 27, 4, 0)
+        MonitorLeadItem MonitorLead_Undefined,  12, 0, 1        ; used for all other combinations
+ ]
+
+ [ StorkPowerSave
+;
+; List of latch addresses and initial values.
+;
+PowerTab
+        DCD     HWLatchPA, InitLatchPA
+        DCD     HWLatchPB, InitLatchPB
+        DCD     HWLatchMC, InitLatchMC
+        DCD     HWLatchMA, InitLatchMA
+        DCD     0
+
+PowerHardware           ;On Stork, ensure Combo chip, Winnie, Floppy etc are powered
+        ENTRY "r0,r1"
+
+        MOV     r0, #0
+        LDRB    lr, [r0, #IOSystemType]
+        TST     lr, #IOST_BATMAN
+        EXIT    EQ              ;EQ, not Stork, so hardware already powered
+;
+; On Stork.
+;
+; Now would be a good time to hit the power control latches
+; to ensure everything is powered up.
+;
+        ADR     R14, PowerTab
+05
+        LDMIA   R14!, {R0, R1}
+        TEQ     R0, #0
+        STRNEB  R1, [R0]
+        BNE     %BT05
+
+10
+        EXIT
+ ]
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       Configure82C710 - Configure 82C710/82C711/SMC 665 if present
+;
+
+; 82C710 stuff
+
+CnTbase         *       &03010000       ; Base address of 82C710 = PC/AT I/O 000H
+CRI710          *       &0390
+CRI710Off       *       CRI710*4        ; 82C710 Configuration Register Index port
+CAP710Off       *       CRI710Off +4    ; 82C710 Configuration Access Port (data)
+ConRegA710      *       &02FA*4
+ConRegB710      *       &03FA*4
+
+CRI711Off       *       &03F0*4         ; 82C711 Configuration Register Index port
+CAP711Off       *       CRI711Off +4    ; 82C711 Configuration Access Port (data)
+
+
+ [ IO_Type <> "IOMD"
+Configure82C710 ENTRY "r0,r1"
+        MOV     r0, #0
+        LDRB    lr, [r0, #IOSystemType]
+        TST     lr, #IOST_IOEB          ; no IOEB, then don't bother
+        EXIT    EQ
+
+        TEQP    pc, #SVC_mode + I_bit + F_bit   ; Disable FIQ and IRQ
+        LDR     r0, =CnTbase            ; R0-> 82C710 base address
+
+; First try to configure 82C711 or SMC665
+
+        MOV     lr, #&55
+        STRB    lr, [r0, #CRI711Off]    ; Write &55 to CRI711 twice
+        STRB    lr, [r0, #CRI711Off]    ; to enter configuration mode
+
+        MOV     lr, #&0D                ; Check for SMC 665
+        STRB    lr, [r0, #CRI711Off]
+        LDRB    lr, [r0, #CAP711Off]
+        TEQ     lr, #&65
+        ADREQ   r1, ConfigSMCTable      ; different table for SMC 665
+        ADRNE   r1, Config711Table      ; R1-> 82C711 configuration data
+        Push    "lr"                    ; need to test for 665 again
+20
+        LDRB    lr, [r1], #1            ; get config index
+        STRB    lr, [r0, #CRI711Off]
+        TEQ     lr, #&AA                ; end of table?
+        LDRNEB  lr, [r1], #1            ; if not then get config data
+        STRNEB  lr, [r0, #CAP711Off]    ; and write it
+        BNE     %BT20
+
+        Pull    "lr"
+        TEQ     lr, #&65                ; if we configured SMC 665 then
+        MOVEQ   r1, #IOST_37C665        ;   set flag in IOSystemType
+        BEQ     %FT30                   ;   and no need to faff about for 710/711
+
+; Now try to configure 82C710 (won't work on 711)
+
+        MOV     lr, #&55                ; Magic number from 82C710 data sheet
+        STRB    lr, [r0, #ConRegA710]   ; Start configuration mode
+        MVN     lr, lr                  ; Complement the magic number
+        STRB    lr, [r0, #ConRegB710]
+        MOV     lr, #&36                ; Another magic number from C&T
+        STRB    lr, [r0, #ConRegB710]
+        MOV     r1, #CRI710 /4          ; 82C710 configuration address /4
+        STRB    r1, [r0, #ConRegB710]   ; Write configuration address
+        MVN     lr, r1                  ; Complement the data
+        STRB    lr, [r0, #ConRegA710]   ; Complete configuration sequence
+
+        ADR     r1, Config710Table      ; R1-> 82C710 configuration data
+10
+        LDRB    lr, [r1], #1            ; Get next 82C710 config reg index, R1++
+        STRB    lr, [r0, #CRI710Off]    ; Write index
+        TEQ     lr, #&0F                ; Configuration register selected?
+        LDRB    lr, [r1], #1            ; Get config data, R1++
+        STRB    lr, [r0, #CAP710Off]    ; Write config data
+        BNE     %BT10                   ; Repeat until config register written
+
+; now if we're on 82C710 then 1st serial port is at &3F8
+; and if we're on 82C711 then 1st serial port is at &2F8
+
+        STRB    lr, [r0, #&2FF * 4]     ; store &AA in serial scratchpad register for 82C711 port 1
+        MOV     lr, #&55
+        STRB    lr, [r0, #&3FF * 4]     ; store &55 in serial scratchpad register for 82C710
+        STRB    lr, [r0, #CRI711Off]    ; Write &55 to CRI711 twice
+        STRB    lr, [r0, #CRI711Off]    ; to reenter configuration mode
+        MOV     lr, #&02                ; select config register 2
+        STRB    lr, [r0, #CRI711Off]
+        MOV     lr, #2_00011100         ; move 1st serial port to &3F8
+        STRB    lr, [r0, #CAP711Off]
+        MOV     lr, #&AA
+        STRB    lr, [r0, #CRI711Off]    ; exit config mode
+
+        MOV     r1, #0                  ; by default no 82C710 or 82C711
+        LDRB    lr, [r0, #&3FF * 4]     ; read scratchpad register
+        TEQ     lr, #&55                ; &55 => 82C710
+        MOVEQ   r1, #IOST_82C710
+        TEQ     lr, #&AA
+        MOVEQ   r1, #IOST_82C711
+30
+        MOV     r0, #0
+        LDRB    lr, [r0, #IOSystemType]
+        BIC     lr, lr, #IOST_82C710 :OR: IOST_82C711 :OR: IOST_37C665
+        ORR     lr, lr, r1
+        STRB    lr, [r0, #IOSystemType]
+
+        TEQP    pc, #SVC_mode + I_bit   ; Restore IRQ/FIQ state
+        EXIT
+ ]
+
+ [ IO_Type <> "IOMD"
+Config710Table ; Index, Data            ; 82C710 configuration data
+        DCB     &01, 2_01000000         ; Bi-dir parallel
+        DCB     &02, 2_00001000         ; Default, UART clock
+        DCB     &04, &FE                ; Default, UART base address= &3F8 (COM1)
+        DCB     &06, &9E                ; Default, Parallel base address= &278 (LPT3)
+        DCB     &09, &9E                ; GPCS=&278 (so writes to printer control go to PAL too)
+        DCB     &0A, 2_00001010         ; GPCS A1=1 (to detect printer control), GPCS enabled
+        DCB     &0B, 2_01110000         ; IRQs active lo, GPCS is an o/p
+        DCB     &0C, 2_10100001         ; IDE/FDC enabled, mouse disabled
+        DCB     &0D, 0                  ; Default, mouse address
+        DCB     &0E, 2_00000000         ; Default, test modes disabled
+        DCB     &00, 2_10101100         ; Valid config, OSC/BR on, Ser/Par enable
+        DCB     &0F, 0                  ; Write config register == end of list
+
+Config711Table
+        DCB     &01, 2_10000111         ; Enable config read, IRQ active low, parallel powered/extended, default addr.
+        DCB     &02, 2_00001101         ; 2nd serial port disabled, 1st enabled at &2F8 (to be moved to &3F8 later)
+        DCB     &03, 0                  ; Test mode disabled
+        DCB     &00, 2_10111011         ; Valid config, OSC/BR on, FDC enabled/powered, IDE AT,enabled
+        DCB     &AA, 0                  ; Exit config mode
+ ]
+
+ConfigSMCTable
+        DCB     &01, 2_10000111         ; Enable config read, IRQ active low, parallel powered/extended, default addr.
+        DCB     &02, 2_00011100         ; 2nd serial port disabled, 1st enabled at &3F8
+        DCB     &03, &78                ; extra stuff for SMC
+        DCB     &04, 2_00000011         ; allow extended parallel port modes
+        DCB     &05, 0
+        DCB     &06, &FF
+        DCB     &07, 0
+        DCB     &08, 0
+        DCB     &09, 0
+        DCB     &0A, 0
+        DCB     &00, 2_10111011         ; Valid config, OSC/BR on, FDC enabled/powered, IDE AT,enabled
+        DCB     &AA, 0                  ; Exit config mode
+
+        ALIGN
+
+ [ IO_Type = "IOMD"
+;
+; Simplified version of Configure82C710 programs SMC 37C665 only.
+;
+Configure37C665 ENTRY "r0,r1"
+        TEQP    pc, #SVC_mode + I_bit + F_bit   ; Disable FIQ and IRQ
+        LDR     r0, =CnTbase            ; R0-> SMC 665 base address
+
+; First try to configure the SMC665
+
+        MOV     lr, #&55
+        STRB    lr, [r0, #CRI711Off]    ; Write &55 to CRI711 twice
+        STRB    lr, [r0, #CRI711Off]    ; to enter configuration mode
+
+        MOV     lr, #&0D                ; Check for SMC 665
+        STRB    lr, [r0, #CRI711Off]
+        LDRB    lr, [r0, #CAP711Off]
+        TEQ     lr, #&65
+        MOVNE   r1, #0                  ;NE: not a SMC 665 this should never happen
+        BNE     %FT30                   ;NE: on a RiscPC, Kryten or Stork
+
+        ADR     r1, ConfigSMCTable      ; R1-> SMC 665 configuration data
+20
+        LDRB    lr, [r1], #1            ; get config index
+        STRB    lr, [r0, #CRI711Off]
+        TEQ     lr, #&AA                ; end of table?
+        LDRNEB  lr, [r1], #1            ; if not then get config data
+        STRNEB  lr, [r0, #CAP711Off]    ; and write it
+        BNE     %BT20
+
+        MOV     r1, #IOST_37C665
+30
+        MOV     r0, #0
+        LDRB    lr, [r0, #IOSystemType]
+        BIC     lr, lr, #IOST_82C710 :OR: IOST_82C711 :OR: IOST_37C665
+        ORR     lr, lr, r1
+        STRB    lr, [r0, #IOSystemType]
+
+        TEQP    pc, #SVC_mode + I_bit   ; Restore IRQ/FIQ state
+        EXIT
+ ]
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;
+;       ReadUniqueID - Read unique machine ID
+;
+; 10-Dec-93  BCockburn  Modified to leave raw 64 bit ID from chip in RawMachineID
+
+defaultlatch *  20000-1 ; TMD 21-May-93: "-1" correction applied
+
+Tsyc    *       5       ; time between frames - minimum of 1µs, so give it a bit more
+Trstl   *       500     ; time reset pulse held low - minimum of 480µs, so give it a bit more
+Trsth   *       500     ; time reset pulse held high - minimum of 480µs, so give it a bit more
+Tlow0   *       80      ; time for write0 low - min 60µs, max 120µs
+Tlow1   *       5       ; time for write1 low - min 1µs, max 15µs
+Tslot   *       90      ; time for total read/write slot - min 60µs, max 120µs
+Trdlow  *       5       ; time for read slot low before release - min 1µs, max 15µs
+Trddat  *       3       ; time after read slot high before read it
+
+        ASSERT  Tslot-Tlow0 > Tsyc
+        ASSERT  Trdlow+Trddat < 15
+
+; Macro to set wire to a given state, and optionally count transitions (starting at low) while waiting for a given time
+
+        MACRO
+        SetWire $hilo, $time, $monstate, $count
+        LCLS    reg
+ [ "$hilo"="LOW"
+reg     SETS    "r4"
+ |
+        ASSERT  "$hilo"="HIGH"
+reg     SETS    "r5"
+ ]
+ [ ($time) = 0
+        STRB    $reg, [r1, #IOCControl]         ; set appropriate level on line
+ |
+        ASSERT  ($time) < 32768
+        MOV     r12, #(($time)*2):AND:&FF
+        STRB    r12, [r1, #Timer0LL]            ; program low latch
+        MOV     r12, #(($time)*2):SHR:8
+        STRB    r12, [r1, #Timer0LH]            ; program high latch
+        STRB    $reg, [r1, #IOCControl]         ; set appropriate level on line
+        STRB    r12, [r1, #Timer0GO]            ; and start timer
+        LDRB    r12, [r1, #IOCIRQSTAA]          ; dummy instruction to avoid bug in IOC
+        STRB    r11, [r1, #IOCIRQCLRA]          ; immediately clear IRQ bit
+  [ "$monstate"<>""
+        MOV     $monstate, #0
+  ]
+  [ "$count"<>""
+        MOV     $count, #0
+  ]
+10
+        LDRB    r12, [r1, #IOCIRQSTAA]
+        TST     r12, r11
+  [ "$count"<>""
+        ADDEQ   $count, $count, #1
+  ]
+  [ "$monstate"=""
+        BEQ     %BT10                                   ; not timed out, so just loop
+  |
+        BNE     %FT30                                   ; timed out
+        LDRB    r12, [r1, #IOCControl]
+        TST     r12, #IOEB_unique_machine_ID_bit
+        BEQ     %BT10                                   ; if still low then loop to 10
+
+        ADD     $monstate, $monstate, #1                ; increment number of transitions
+20
+        LDRB    r12, [r1, #IOCIRQSTAA]
+        TST     r12, r11
+   [ "$count"<>""
+        ADDEQ   $count, $count, #1
+   ]
+        BNE     %FT30                                   ; timed out
+        LDRB    r12, [r1, #IOCControl]
+        TST     r12, #IOEB_unique_machine_ID_bit
+        BNE     %BT20                                   ; if still high then loop to 20
+        ADD     $monstate, $monstate, #1                ; increment number of transitions
+        B       %BT10
+30
+  ]
+ ]
+        MEND
+
+ReadUniqueID    ENTRY "r0-r12"
+        MOV     r0, #0
+        LDR     r1, =IOC
+        TEQP    pc, #SVC_mode + I_bit + F_bit
+        LDRB    r3, [r0, #IOCControlSoftCopy]
+        BIC     r4, r3, #IOEB_unique_machine_ID_bit     ; r4 is value to pull ID line low
+        ORR     r5, r3, #IOEB_unique_machine_ID_bit     ; r5 is value to pull ID line high
+        MOV     r11, #timer0_bit
+        BL      SendResetPulse
+        BVS     ResetFailed
+        BL      SendCommandWord
+
+        MOV     r7, #-8                                 ; -no. of bytes to store = 8 bits type + 48 bits ID + 8 bits checksum
+10
+        BL      GetAByte
+        STRB    r6, [r7, #RawMachineID+8]
+        ADDS    r7, r7, #1
+        BNE     %BT10
+
+        BL      RestoreIOCState
+        BL      CheckCRC
+        BVS     IDError
+        EXIT
+
+ResetFailed
+        BL      RestoreIOCState
+IDError
+        MOV     r0, #0
+        STR     r0, [r0, #RawMachineID+0]            ; indicate no ID by putting zero here
+        STR     r0, [r0, #RawMachineID+4]
+        EXIT
+
+RestoreIOCState ENTRY
+        STRB    r3, [r1, #IOCControl]                   ; put back old value
+        MOV     r12, #defaultlatch :AND: &FF
+        STRB    r12, [r1, #Timer0LL]                    ; and restore old timer 0 latch values
+        MOV     r12, #defaultlatch :SHR: 8
+        STRB    r12, [r1, #Timer0LH]
+        STRB    r12, [r1, #Timer0GO]
+        TEQP    pc, #SVC_mode + I_bit                   ; restore old interrupt state
+        EXIT
+
+SendResetPulse ROUT
+        SetWire HIGH, Tsyc
+        SetWire LOW, Trstl,,r6
+        SetWire HIGH, Trsth,r10
+        TEQ     r6, #0
+;        ADREQ   r0, IOCBugHappenedError
+        ORREQS  pc, lr, #V_bit
+        CMP     r10, #3                                 ; H-L-H is ok
+        BICEQS  pc, lr, #V_bit
+;        ADRHI   r0, TooManyTransitionsError             ; H-L-H-L...
+;        CMP     r10, #2
+;        ADREQ   r0, NeverWentHighAgainError             ; H-L
+;        CMP     r10, #1
+;        ADREQ   r0, NeverWentLowError                   ; H
+;        ADRCC   r0, NeverWentHighError                  ; stayed low permanently even though we released it
+        ORRS    pc, lr, #V_bit
+
+ [ {FALSE} ; only for debugging
+NeverWentHighError
+        =       "Never went high", 0
+NeverWentLowError
+        =       "Never went low", 0
+NeverWentHighAgainError
+        =       "Never went high again", 0
+TooManyTransitionsError
+        =       "Too many transitions", 0
+IOCBugHappenedError
+        =       "IOC bug happened", 0
+        ALIGN
+ ]
+
+SendCommandWord ROUT
+        LDR     r6, =&10F               ; &0F is command word
+10
+        MOVS    r6, r6, LSR #1
+        BICEQS  pc, lr, #V_bit
+        BCS     SendOne
+        SetWire LOW, Tlow0
+        SetWire HIGH, Tslot-Tlow0
+        B       %BT10
+
+SendOne
+        SetWire LOW, Tlow1
+        SetWire HIGH, Tslot-Tlow1
+        B       %BT10
+
+GetAByte ROUT
+        MOV     r6, #&80
+10
+        SetWire LOW, Trdlow
+        SetWire HIGH, Trddat
+        LDRB    r10, [r1, #IOCControl]
+        SetWire HIGH, Tslot-Trdlow-Trddat
+        MOVS    r10, r10, LSR #IOEB_ID_bit_number+1    ; move bit into carry
+        MOVS    r6, r6, RRX
+        BCC     %BT10
+        MOV     r6, r6, LSR #24
+        BICS    pc, lr, #V_bit
+
+CheckCRC ROUT
+        LDR     r1, =RawMachineID               ; pointer to current byte
+        MOV     r2, #0
+        MOV     r3, #7                          ; number of bytes to do
+10
+        LDRB    r4, [r1], #1
+        EOR     r2, r2, r4
+        MOV     r4, #8                          ; number of bits to do
+20
+        MOVS    r2, r2, LSR #1                  ; shift bit out into carry
+        EORCS   r2, r2, #&8C                    ; feedback carry into other bits
+        SUBS    r4, r4, #1                      ; one less bit to do
+        BNE     %BT20                           ; loop until done whole byte
+        SUBS    r3, r3, #1                      ; one less byte to do
+        BNE     %BT10                           ; loop until done all 7 bytes
+        LDRB    r4, [r1], #1                    ; read CRC
+        TEQ     r4, r2                          ; if incorrect
+        ORRNES  pc, lr, #V_bit                  ; then exit indicating error
+        BICS    pc, lr, #V_bit
+
+        LTORG
+
+        END
diff --git a/s/PMF/osword b/s/PMF/osword
new file mode 100644
index 0000000000000000000000000000000000000000..4110cc60e391a18b45b161a5bd312ec00c8a3a0f
--- /dev/null
+++ b/s/PMF/osword
@@ -0,0 +1,1258 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.PMF.osword
+
+maxword * &16                                   ; highest known osword
+
+; *****************************************************************************
+
+        MACRO
+        WordReturnV $cond
+        [ AssemblingArthur :LOR: ErrorsInR0
+
+        ASSERT  "$cond"="" :LOR: "$cond"="VS"
+
+        [ "$cond"=""
+        Pull    "R0-R4,R11,WsPtr,link,PC", VC
+        ]
+        ADDVS   R13, R13, #4                    ; junk stacked R0
+        Pull    "R1-R4,R11,WsPtr,link,PC", VS
+
+        |
+        Pull    "R0-R4, R11, R12, R14, PC", $cond
+        ]
+        MEND
+
+; Main OsWord entry point
+; R0,R1,R2 are parameters
+
+
+OsWord
+        Push    "R0-R4, R11, R12, R14"
+        CMP     R0, #(maxword+1)
+        BLCC    OsWordGo                        ; Call the subsid entry pt.
+        LDMIA   R13, {R2-R4}                    ; R2=A, R3=X, R4=Y
+        MOV     R1, #Service_UKWord             ; osword service reason
+        CLRPSR  V_bit, R0                       ; in case there's no service
+        IssueService
+        TEQ     R1, #0
+        STMEQIA R13, {R2-R4}                    ; if claimed, then update
+                                                ; returned R0-R2
+        Pull    "R0-R4, R11, R12, PC"           ; pass V back from service
+
+GoMyOsword
+        CLRPSR  V_bit, R4
+        Pull    "R0-R4, R11, R12, R14, PC"
+
+; *****************************************************************************
+
+OsWordGo ROUT
+        BYTEWS  WsPtr
+10                                              ; Point to despatch table
+        ADD     PC, PC, R0, LSL #2              ; add in the action*4 and go
+        &       0
+        ASSERT  DespatchWord-%BT10 = 8
+
+; *****************************************************************************
+
+DespatchWord
+
+        BAL     OsWord00
+        BAL     OsWord01
+        BAL     OsWord02
+        BAL     OsWord03
+        BAL     OsWord04
+        BAL     OsWord05
+        BAL     OsWord06
+        BAL     OsWord07
+
+        BAL     OsWord08
+        BAL     OsWord09
+        BAL     OsWord0A
+        BAL     OsWord0B
+        BAL     OsWord0C
+        BAL     OsWord0D
+        BAL     OsWord0E
+        BAL     OsWord0F
+
+        BAL     OsWord10
+        BAL     OsWord11
+        BAL     OsWord12
+        BAL     OsWord13
+        BAL     OsWord14
+        BAL     OsWord15
+        BAL     OsWord16
+
+; *****************************************************************************
+; That's All Folks
+; *****************************************************************************
+
+; *****************************************************************************
+; The OsWord routines themselves
+; *****************************************************************************
+
+; Osword Zero : Input a line
+
+OsWord00 ROUT
+        LDRB    R0, [R1, #0]            ; lo-byte of address
+        LDRB    R2, [R1, #1]            ; hi-byte of address
+        ORR     R0, R0, R2, LSL #8      ; R0 := address
+        LDRB    R2, [R1, #3]            ; lo limit
+        LDRB    R3, [R1, #4]            ; hi limit
+        LDRB    R1, [R1, #2]            ; length of buffer
+        SWI     XOS_ReadLine
+        WordReturnV VS
+
+        MOV     R2, R1                  ; put line length into R2
+        Pull    "R0,R1,R3"              ; don't overwrite R2
+        Pull    "R3, R4, R11, R12, R14, PC"
+
+; *****************************************************************************
+
+; Read/Write System Clock
+; entered with IRQs off
+; in:   R0 = 1 => read clock
+;       R0 = 2 => write clock
+
+OsWord01 ROUT
+OsWord02 ROUT
+        CMP     R0, #2                  ; C=1 <=> write clock
+        LDRB    R0, TimerState
+        EORCS   R0, R0, #&0F            ; if writing, then write to other state
+                                        ; in case user resets in middle
+        TEQ     R0, #5                  ; 5 => alpha, 10 => beta (C preserved)
+        ADREQ   R2, TimerAlpha
+        ADRNE   R2, TimerBeta
+        Swap    R1, R2, CS              ; if writing then R2 is destination
+        MOV     R3, #5
+10
+        LDRB    R4, [R2], #1
+        STRB    R4, [R1], #1
+        SUBS    R3, R3, #1
+        BNE     %BT10
+        STRB    R0, TimerState          ; if writing, switch state
+                                        ; (if reading, write current state)
+        MyOsWord
+
+; *****************************************************************************
+
+; Read/Write Interval Timer
+; entered with IRQs off
+; in:   R0 = 3 => read interval timer
+;       R0 = 4 => write interval timer
+
+OsWord03 ROUT
+OsWord04 ROUT
+        CMP     R0, #4                  ; C=1 => write timer
+        MOVCS   R2, R1                  ; if writing then R1 is source
+        ADRCS   R1, IntervalTimer
+        ADRCC   R2, IntervalTimer       ; else R2 is source
+        MOV     R0, #5
+10
+        LDRB    R3, [R2], #1
+        STRB    R3, [R1], #1
+        SUBS    R0, R0, #1
+        BNE     %BT10
+        MyOsWord
+
+; *****************************************************************************
+
+; Perform a SOUND command
+OsWord07 ROUT
+        TST     R1, #3
+        LDMEQIA R1, {R0,R1}
+        SWIEQ   XSound_ControlPacked
+        MyOsWord EQ
+
+; Block not word aligned, so push it on the stack
+
+        SUB     R13, R13, #8            ; create stack frame of 8 bytes
+        MOV     R0, #7
+10
+        LDRB    R2, [R1, R0]            ; copy block into stack frame
+        STRB    R2, [R13, R0]
+        SUBS    R0, R0, #1
+        BCS     %BT10
+
+        Pull    "R0, R1"                ; then pull stack frame into R0 and R1
+        SWI     XSound_ControlPacked
+        MyOsWord
+
+; *****************************************************************************
+; Read the logical colour of a Pixel ( BASIC'S POINT function)
+; Uses SWI ReadPoint
+
+OsWord09 ROUT
+        Push    R1                      ; save pointer
+
+        LDRB    R2, [R1, #0]            ; X lo-byte
+        LDRB    R0, [R1, #1]            ; X hi-byte
+        ORR     R0, R2, R0, LSL #8
+
+        MOV     R0, R0, LSL #16         ; sign extend X
+        MOV     R0, R0, ASR #16
+
+        LDRB    R2, [R1, #2]            ; Y lo-byte
+        LDRB    R1, [R1, #3]            ; Y hi-byte
+        ORR     R1, R2, R1, LSL #8
+
+        MOV     R1, R1, LSL #16         ; sign extend Y
+        MOV     R1, R1, ASR #16
+
+        SWI     XOS_ReadPoint   ; in: R0=X, R1=Y
+                                ; out: R2=colour, R3=tint, R4=0/-1 (on/off)
+        Pull    R1
+        STRB    R2, [R1, #4]
+        WordReturnV
+
+; *****************************************************************************
+
+; Read a character definition
+
+OsWord0A ROUT
+        ByteToNosbod DoReadFont
+        MyOsWord
+
+; *****************************************************************************
+
+; Read the palette setting (VDU19,L,P,R,G,B)
+
+OsWord0B ROUT
+        ByteToNosbod DoReadPalette
+        MyOsWord
+
+; *****************************************************************************
+
+; Write the palette setting (see VDU19)
+
+OsWord0C ROUT
+        ByteToNosbod DoSetPalette
+        MyOsWord
+
+; *****************************************************************************
+
+; Read the last two graphics cursor positions
+
+OsWord0D ROUT
+         ByteToNosbod DoOsWord13
+         MyOsWord
+
+; *****************************************************************************
+
+; Osword 14 (&0E) -- Read Real Time Clock
+; Four (was six) different calls
+
+; Read CMOS clock
+OsWord0E ROUT
+        Push    "R5-R8, R14"            ; R0-R4 saved by Osword
+
+        MOV     R4, R1                  ; pointer to the Osword Block
+
+        LDRB    R0, [R4, #0]
+        CMP     R0, #1
+        BCC     OsWord0EAlpha
+        BEQ     OsWord0EBeta
+        CMP     R0, #3
+        BCC     OsWord0EGamma
+        BEQ     OsWord0EDelta
+        [ {FALSE}
+        CMP     R0, #5
+        BCC     OsWord0EEpsilon
+        BEQ     OsWord0EZeta            ; this is getting ridiculous !
+        ]
+
+        Pull    "R5-R8, PC"             ; unknown option
+
+; *****************************************************************************
+;
+;       StoreHexPair - Store a hex number in R0 at R4+R6 (hi) and R4+R6+1 (lo)
+;       Corrupts R5,R6
+
+StoreHexPair
+        Push    R14
+        BL      HexToBCD
+        Pull    R14
+StoreBCDPair
+        MOV     R5, R0, LSR #4
+        ADD     R5, R5, #"0"
+        STRB    R5, [R6, R4]!
+        AND     R5, R0, #&0F
+        ADD     R5, R5, #"0"
+        STRB    R5, [R6, #1]
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       StoreTLA - Store 3-letter alpha string in R0 at R4+R6+(0..2),
+;       uppercasing first character
+;
+; out:  R0, R6 corrupt
+
+StoreTLA
+        BIC     R0, R0, #&20            ; upper case 1st char
+        STRB    R0, [R6, R4]!           ; store 1st char
+        MOV     R0, R0, LSR #8
+        STRB    R0, [R6, #1]            ; store 2nd char
+        MOV     R0, R0, LSR #8
+        STRB    R0, [R6, #2]            ; store 3rd char
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       OsWord0EAlpha - Read time as a string in the form
+;       eg Wed,01 Jan 1986.12:34:56
+;
+; in:   R1 -> buffer for string
+;
+
+OsWord0EAlpha ROUT
+        [ {TRUE}
+ [ Fix7
+
+; TMD 30-May-89: We want to enable IRQs here, but OS_ConvertDateAndTime
+; loads bytes out of the block, and if IRQs are on it might end using an
+; inconsistent value, so we must make a copy of the block on the stack
+; and use that. The label OsWord0EDandT was used by a commented out routine
+; in file 'RealTime' which will have to be rewritten if it needs to be
+; included again.
+
+        ADR     R0, RealTime            ; load snapshot of 5 bytes of real time
+        LDMIA   R0, {R0, R2}            ; while IRQs are still off
+        Push    "R0, R2"                ; save on stack
+        CLRPSR  I_bit, R0               ; enable IRQs now
+OSWord0EReturnString
+        MOV     R0, R13                 ; point to stacked copy
+        MOV     R2, #25                 ; 24 + CR terminator
+        ADR     R3, TimeFormat
+        SWI     XOS_ConvertDateAndTime
+        ADD     R13, R13, #8            ; junk stack frame
+ |
+        ADR     R0, RealTime
+        MOV     R2, #25                 ; 24 + CR terminator
+        ADR     R3, TimeFormat
+OsWord0EDandT
+        SWI     XOS_ConvertDateAndTime
+ ]
+        MOVVC   R0, #13                 ; if no error
+        STRVCB  R0, [R1]                ; overwrite terminating 0 with CR
+
+        Pull    "R5-R8,R14"
+        WordReturnV
+
+TimeFormat
+        =       "%w3,%dy %m3 %ce%yr.%24:%mi:%se", 0
+        ALIGN
+
+        |
+        CLRPSR  I_bit, R0       ; this may take some time
+
+        BL      CheckYear       ; check for year wrap
+
+        BL      ReadTime        ; read clock
+
+        Push    "R5, R6"        ; save year lo and hi
+
+; do the hours
+
+        MOV     R6, #16         ; hours at offset 16 and 17
+        BL      StoreHexPair
+
+; do the minutes
+
+        MOV     R0, R1
+        MOV     R6, #19         ; minutes at offset 19 and 20
+        BL      StoreHexPair
+
+; do the seconds
+
+        MOV     R0, R7
+        MOV     R6, #22         ; seconds at offset 22 and 23
+        BL      StoreHexPair
+
+; do the day of month
+
+        MOV     R0, R2
+        MOV     R6, #4          ; day of month at offset 4 and 5
+        BL      StoreHexPair
+
+; now the month number is still in R3
+
+        ADRL    R0, MonthNameTable-4    ; point to the entry
+        LDR     R0, [R0, R3, LSL #2]  ; Get the data in the format "jan",0 etc.
+        MOV     R6, #7                  ; store month at offsets 7,8,9
+        BL      StoreTLA
+
+; do the year
+
+        Pull    "R7, R8"                ; R7=year lo, R8=year hi
+
+        MOV     R0, R7
+        MOV     R6, #13                 ; year(lo) at offsets 13 and 14
+        BL      StoreHexPair
+
+; do the high part of the year
+
+        MOV     R0, R8
+        MOV     R6, #11                 ; year(hi) at offsets 11 and 12
+        BL      StoreHexPair
+
+; now compute day of week from month, date, and year
+; at the moment R7 = units of years, R8 = hundreds of years
+; we want to compute (year MOD 400)
+
+        MOVS    R8, R8, LSL #31         ; C=200, N=100
+        ADDCS   R7, R7, #200
+        ADDMI   R7, R7, #100
+
+; R7 is now year MOD 400
+
+        BL      DayOfTheWeek            ; Returns R0 = 0..6 for Sun..Sat
+
+        ADR     R1, DayNameTable        ; Point to the correct entry
+        LDR     R0, [R1, R0, LSL #2]    ; and get it (word)
+        MOV     R6, #0                  ; day of week stored at offsets 0,1,2
+        BL      StoreTLA
+
+; do the format characters
+
+        B       DoFormatChars
+        ]
+
+; *****************************************************************************
+;
+;       OsWord0EBeta - Read time in BCD format
+;
+; in:   R4 -> parameter block
+;
+; out:  [R4, #0] = year         (00-99)
+;       [R4, #1] = month        (01-12)
+;       [R4, #2] = day of month (01-31)
+;       [R4, #3] = day of week  (01-07) Sun=01
+;       [R4, #4] = hours        (00-23)
+;       [R4, #5] = minutes      (00-59)
+;       [R4, #6] = seconds      (00-59)
+;
+
+OsWord0EBeta ROUT
+
+     [ {TRUE}                           ; International version uses Territory manager.
+
+        ADR     R0, RealTime            ; load snapshot of 5 bytes of real time
+        LDMIA   R0, {R0, R2}            ; while IRQs are still off
+        Push    "R0, R2"                ; save on stack
+        CLRPSR  I_bit, R0               ; this may take some time
+
+        MOV     R0,#-1
+        MOV     R1,SP
+        SUB     SP,SP,#36               ; Space for ordinals.
+        MOV     R2,SP
+        SWI     XTerritory_ConvertTimeToOrdinals
+        ADDVS   SP,SP,#36+(2*4)
+        BVS     OSWord0Eerror
+
+;   [R2]    = CS.                     ; all values are for LOCAL time
+;   [R2+4]  = Second
+;   [R2+8]  = Minute
+;   [R2+12] = Hour (out of 24)
+;   [R2+16] = Day number in month.
+;   [R2+20] = Month number in year.
+;   [R2+24] = Year number.
+;   [R2+28] = Day of week.
+;   [R2+32] = Day of year
+
+        LDR     R0,[R2,#24]             ; Get year
+        LDR     R1,=1900
+        SUB     R0,R0,R1
+01
+        CMP     R0,#100
+        SUBGT   R0,R0,#100
+        BGT     %BT01                   ; Get year MOD 100.
+
+        STRB    R0,[R4,#0]              ; Store it.
+        LDR     R0,[R2,#20]
+        STRB    R0,[R4,#1]
+        LDR     R0,[R2,#16]
+        STRB    R0,[R4,#2]
+        LDR     R0,[R2,#28]
+        STRB    R0,[R4,#3]
+        LDR     R0,[R2,#12]
+        STRB    R0,[R4,#4]
+        LDR     R0,[R2,#8]
+        STRB    R0,[R4,#5]
+        LDR     R0,[R2,#4]
+        STRB    R0,[R4,#6]
+
+        ADD     SP,SP,#36+(2*4)        ; junk stack frame and 5 byte time.
+
+     |
+
+
+        CLRPSR  I_bit, R0               ; this may take some time
+
+        BL      CheckYear               ; check for year wrap
+
+        BL      ReadTime                ; read the time
+
+        STRB    R0, [R4, #4]            ; store hours in hex
+        STRB    R1, [R4, #5]            ; store minutes in hex
+        STRB    R2, [R4, #2]            ; store dayofmonth in hex
+        STRB    R3, [R4, #1]            ; store month in hex
+        STRB    R5, [R4, #0]            ; store year(lo) in hex
+        STRB    R7, [R4, #6]            ; store seconds in hex
+
+     ]
+
+; now we have the time in hex in the parameter block
+; so convert each item into BCD
+
+        MOV     R1, #6                  ; seven bytes to convert
+
+10
+        LDRB    R0, [R4, R1]
+        BL      HexToBCD
+        STRB    R0, [R4, R1]
+        SUBS    R1, R1, #1
+        BPL     %BT10
+
+     [ {FALSE}                          ; Done in territory for international version
+
+        MOV     R7, R5                  ; R7 = year(lo)
+
+; now compute day of week from month, date, and year
+; at the moment R7 = units of years, R6 = hundreds of years
+; we want to compute (year MOD 400)
+
+        MOVS    R6, R6, LSL #31         ; C=200, N=100
+        ADDCS   R7, R7, #200
+        ADDMI   R7, R7, #100
+
+; R7 is now year MOD 400
+
+        BL      DayOfTheWeek            ; Returns R0 = 0..6 for Sun..Sat
+
+        ADD     R0, R0, #1              ; put into range 1..7
+        STRB    R0, [R4, #3]            ; (hex = BCD)
+
+     ]
+
+        B       OsWord0Eend
+
+; *****************************************************************************
+;
+;       OsWord0EGamma - Convert time in BCD format (at offsets 1..7)
+;       into string format at offsets (0..24)
+;
+; in:   R4 -> BCD time
+;
+
+OsWord0EGamma ROUT
+
+        [ {TRUE}                        ; International version.
+;build a block for Territory_ConvertOrdinalsToTime
+;   [R2]    = CS.                     ; all values are for LOCAL time
+;   [R2+4]  = Second
+;   [R2+8]  = Minute
+;   [R2+12] = Hour (out of 24)
+;   [R2+16] = Day number in month.
+;   [R2+20] = Month number in year.
+;   [R2+24] = Year number.
+
+        SUB     SP,SP,#28
+        MOV     R2,SP
+        MOV     R0,#0
+        STR     R0,[R2]
+
+        LDRB    R0,[R4,#7]              ; Seconds
+        BL      BCDToHex
+        STR     R0,[R2,#4]
+
+        LDRB    R0,[R4,#6]              ; Minutes
+        BL      BCDToHex
+        STR     R0,[R2,#8]
+
+        LDRB    R0,[R4,#5]              ; Hours
+        BL      BCDToHex
+        STR     R0,[R2,#12]
+
+        LDRB    R0,[R4,#3]              ; Day of month
+        BL      BCDToHex
+        STR     R0,[R2,#16]
+
+        LDRB    R0,[R4,#2]              ; Month
+        BL      BCDToHex
+        STR     R0,[R2,#20]
+
+
+        LDRB    R0,[R4,#1]              ; Year
+        BL      BCDToHex
+        LDR     R1,=1900
+        ADD     R0,R0,R1
+        STR     R0,[R2,#24]
+
+        MOV     R0,#-1
+        ADD     R1,SP,#20               ; Put value on satck
+        SWI     XTerritory_ConvertOrdinalsToTime
+        ADDVS   SP,SP,#28
+        BVS     OSWord0Eerror
+
+        ADD     SP,SP,#20
+        MOV     R1,R4
+        B       OSWord0EReturnString    ; Now we have 5 byte value on stack,
+                                        ; use same code as OSWord0EAlpha
+      |
+
+; do the seconds
+
+        LDRB    R0, [R4, #7]
+        MOV     R6, #22                 ; seconds at offset 22 and 23
+        BL      StoreBCDPair
+
+; do the minutes
+
+        LDRB    R0, [R4, #6]
+        MOV     R6, #19                 ; minutes at offset 19 and 20
+        BL      StoreBCDPair
+
+; do the hours
+
+        LDRB    R0, [R4, #5]
+        MOV     R6, #16                 ; hours at offset 16 and 17
+        BL      StoreBCDPair
+
+; do the year
+
+        LDRB    R0, [R4, #1]
+        MOV     R6, #13                 ; year(lo) at offsets 13 and 14
+        BL      StoreBCDPair
+
+        MOV     R0, #"1"
+        STRB    R0, [R4, #11]           ; year(hi) at offsets 11 and 12
+        MOV     R0, #"9"
+        STRB    R0, [R4, #12]
+
+; do the month
+
+        LDRB    R0, [R4, #2]            ; get the month number
+        BL      BCDToHex                ; make it hex
+        ADR     R3, MonthNameTable-4    ; Point to entry (months start at 1)
+        LDR     R0, [R3, R0, LSL #2]    ; Get the data in format "jan",0 etc
+        MOV     R6, #7                  ; store month name at offsets 7,8,9
+        BL      StoreTLA
+
+; day-of-the-week
+
+        LDRB    R3, [R4, #4]            ; day number in range 1..7
+        ADR     R0, DayNameTable-4      ; point to correct dayname eg "Wed",0
+        LDR     R0, [R0, R3, LSL #2]
+        MOV     R6, #0                  ; store day name at offsets 0,1,2
+        BL      StoreTLA
+
+; do day of month
+
+        LDRB    R0, [R4, #3]
+        MOV     R6, #4                  ; day of month at offset 4 and 5
+        BL      StoreBCDPair
+
+; do the format characters
+
+DoFormatChars
+        MOV     R0, #","
+        STRB    R0, [R4, #3]
+        MOV     R0, #" "
+        STRB    R0, [R4, #6]
+        STRB    R0, [R4, #10]
+        MOV     R0, #"."
+        STRB    R0, [R4, #15]
+        MOV     R0, #":"
+        STRB    R0, [R4, #18]
+        STRB    R0, [R4, #21]
+        MOV     R0, #13
+        STRB    R0, [R4, #24]
+
+        B OsWord0Eend
+
+DayNameTable
+ = "sun",0
+ = "mon",0
+ = "tue",0
+ = "wed",0
+ = "thu",0
+ = "fri",0
+ = "sat",0
+
+        ]
+
+; *****************************************************************************
+;
+;       OsWord0EDelta - Read 5-byte RealTime
+;
+; in:   R4 -> block
+;
+; out:  [R4, #0..4] = RealTime
+;
+
+OsWord0EDelta ROUT
+        LDR     R1, RealTime +0
+        STRB    R1, [R4, #0]
+        MOV     R1, R1, LSR #8
+        STRB    R1, [R4, #1]
+        MOV     R1, R1, LSR #8
+        STRB    R1, [R4, #2]
+        MOV     R1, R1, LSR #8
+        STRB    R1, [R4, #3]
+        LDRB    R1, RealTime +4
+        STRB    R1, [R4, #4]
+
+; and drop thru to ...
+
+OsWord0Eend
+        Pull    "R5-R8, R14"
+        MyOsWord
+
+OSWord0Eerror
+        Pull    "R5-R8, R14"
+        WordReturnV
+
+; *****************************************************************************
+;
+;       GetDecimalPair - Get pair of decimal digits from [R4+R1+0..1]
+;
+; in:   R1 is offset from R4 to find 1st digit
+;
+; out:  if valid, R1=value of pair of digits, C=0
+;       if invalid, R1=undefined, C=1
+;       R10 is corrupted
+;
+
+GetDecimalPair ROUT
+        LDRB    R10, [R1, R4]!          ; get hi-digit
+        SUB     R10, R10, #"0"          ; put in range 0..9
+        CMP     R10, #10                ; C=1 if bad digit
+        ADD     R10, R10, R10, LSL #2   ; R10 = 5*hi
+
+        LDRB    R1, [R1, #1]            ; get lo-digit
+        SUB     R1, R1, #"0"            ; put in range 0..9
+        CMPCC   R1, #10                 ; C=1 if bad digit
+        ADD     R1, R1, R10, LSL #1     ; R1 = lo + 2*(5*hi)
+
+        MOV     PC, R14
+
+; *****************************************************************************
+
+; Osword 15 (&0F) Write the Real Time Clock.
+; Three different calls
+
+OsWord0F ROUT
+        CLRPSR  I_bit, R0               ; this may take some time
+
+        Push    "R5-R10, R14"
+        MOV     R4, R1                  ; Copy the parameter block pointer
+        LDRB    R0, [R1]
+        MOV     R9, #0
+
+        TEQ     R0, #8                  ; write hours, minutes, seconds
+        MOVEQ   R9, #1
+
+        TEQ     R0, #15                 ; write day, date, month, year
+        MOVEQ   R9, #2
+
+        TEQ     R0, #24                 ; write all of time
+        MOVEQ   R9, #3
+
+        TEQ     R9, #0
+        Pull    "R5-R10, PC", EQ         ; unknown call, pass it on
+
+     [  {TRUE}                          ; international version uses Territory_ConvertTimeStringToOrdinals
+
+        ADD     r10, r0, #3+1           ; round up number of bytes in block to word boundary, including null terminator
+        BIC     r10, r10, #3
+        SUB     sp, sp, r10
+
+        ADD     r2, r1, #1              ; point at actual string
+        MOV     r1, #0
+02
+        LDRB    r14, [r2, r1]           ; copy string (not terminated) on stack
+        STRB    r14, [sp, r1]
+        ADD     r1, r1, #1
+        TEQ     r1, r0                  ; have we copied all bytes of string?
+        BNE     %BT02                   ; loop if not
+
+        MOV     r14, #0                 ; null terminator
+        STRB    r14, [sp, r0]
+
+        MOV     r0,#-1                  ; set things up for territory SWI - r0 = -1 for current territory
+        MOV     r1, r9                  ; r1 = reason code (1, 2 or 3)
+        MOV     r2, sp                  ; r2 -> terminated string on stack
+        SUB     sp, sp, #36             ; get space for result.
+        MOV     r3, sp
+
+        SWI     XTerritory_ConvertTimeStringToOrdinals
+        ADDVS   sp, sp, #36             ; if error then junk return block
+        ADDVS   sp, sp, r10             ; and junk variable length string on stack
+        BVS     Bad0F
+
+        CMP     r9, #2                  ; if just writing the date, write it !
+        BEQ     %FT10
+        BGT     %FT05                   ; if writing everything just get UTC time
+
+; We only have the time from the string, we now need the date
+; because changing the time may change it.
+
+        ADR     r0, RealTime
+        LDMIA   r0, {r0,r1}             ; LDM is atomic wrt interrupts
+
+        Push    "r0,r1"                 ; put value on stack
+        MOV     r0,#-1                  ; use configured territory.
+        ADD     r2, sp, #8
+        LDMIA   r2, {r3-r6}             ; preserve time values from entry string
+        MOV     r1, sp
+        SWI     XTerritory_ConvertTimeToOrdinals        ; get ordinals for current time
+        ADDVS   sp, sp, #44             ; 36 From above + 8 for 5 byte time
+        ADDVS   sp, sp, r10             ; and junk string as well
+        BVS     Bad0F
+        ADD     sp, sp, #8              ; dump 5 byte time on TOS
+        STMIA   r2, {r3-r6}             ; restore the time we read from the string.
+
+05
+; Now [SP] -> ordinals in local time, but we want time in UTC
+; First convert the ordinals to 5 byte UTC time
+
+        MOV     r0, #-1                 ; use configured territory.
+        MOV     r2, sp                  ; r2 -> ordinals block
+        SUB     sp, sp, #8              ; two more words to contain 5 byte time
+        MOV     r1, sp
+        SWI     XTerritory_ConvertOrdinalsToTime
+        ADDVS   sp, sp, #44             ; 36 From above + 8 for 5 byte time.
+        ADDVS   sp, sp, r10             ; and junk string as well
+        BVS     Bad0F
+
+; Now we have a 5 byte UTC time, convert it to UTC ordinals
+
+        MOV     r1, sp                  ; our 5 byte time
+        ADD     r2, sp, #8              ; place to put ordinals
+        SWI     XTerritory_ConvertTimeToUTCOrdinals
+        ADDVS   sp, sp, #44             ; 36 bytes ordinals + 8 for 5 byte time.
+        ADDVS   sp, sp, r10             ; and junk string as well
+        BVS     Bad0F
+
+        ADD     sp, sp, #8              ; discard 5 byte time
+
+10
+; Load the registers. (SP->Ordinals)
+
+        LDR     r8, [sp], #4            ; centiseconds
+        LDR     r7, [sp], #4            ; seconds
+        LDR     r1, [sp], #4            ; minutes
+        Pull    "r0,r2,r3,r5"           ; hours, day of month,  month, year
+        ADD     sp, sp, #8              ; junk day of week, day of year
+        ADD     sp, sp, r10             ; and string on stack
+        MOV     r4, #100
+        DivRem  r6, r5, r4, r14         ; r5 = Year (lo), r6 = Year (hi)
+
+     |
+
+; first set up data in registers as follows :-
+; R0 = hours
+; R1 = minutes
+; R2 = days
+; R3 = months
+; (R4 -> block)
+; R5 = year(lo)
+; R6 = year(hi)
+; R7 = seconds
+; R8 = centiseconds
+
+; first do the date part, if appropriate
+
+        TST     R9, #2
+        MOVEQ   R2, #-1                 ; if not doing date, set days,
+        MOVEQ   R3, #-1                 ; months, year(lo), year(hi) to -1
+        MOVEQ   R5, #-1
+        MOVEQ   R6, #-1
+        BEQ     %FT50                   ; [not doing date]
+
+; do the months
+
+        LDRB    R1, [R4, #8]            ; get first char
+        ORR     R1, R1, #&20            ; lower case it
+        LDRB    R2, [R4, #9]            ; get second char
+        ORR     R2, R2, #&20            ; lower case it
+        ORR     R1, R1, R2, LSL #8      ; merge
+        LDRB    R2, [R4, #10]           ; get third char
+        ORR     R2, R2, #&20            ; lower case it
+        ORR     R1, R1, R2, LSL #16     ; merge
+
+        ADR     R2, MonthNameTable-4    ; months start at 1
+        MOV     R3, #12                 ; try December first
+10
+        LDR     R0, [R2, R3, LSL #2]    ; get month word
+        TEQ     R0, R1
+        SUBNES  R3, R3, #1              ; if not equal, decrement month
+        BNE     %BT10                   ; and if non-zero, loop
+
+        TEQ     R3, #0                  ; failed to match ?
+        BEQ     Bad0F                   ; then ignore all
+
+; R3 now contains the month number
+
+; do the days
+
+        MOV     R1, #5                  ; offset to days
+        BL      GetDecimalPair
+        BCS     Bad0F                   ; bad digits
+
+        MOVS    R2, R1
+        BEQ     Bad0F                   ; zero days not allowed
+
+        ADR     R0, MonthLengths        ; now validate days against
+        LDRB    R1, [R0, R3]            ; maximum number of days in that month
+        CMP     R1, R2                  ; (29 for Feb) and eliminate if invalid
+        BCC     Bad0F
+
+; R2 now contains the days
+
+; now do the years so we can check whether 29 Feb is legal
+
+        MOV     R1, #12                 ; offset to year(hi)
+        BL      GetDecimalPair
+        MOVCC   R6, R1
+
+        MOVCC   R1, #14                 ; offset to year(lo)
+        BLCC    GetDecimalPair
+        MOVCC   R5, R1
+
+        BCS     Bad0F
+
+; now check for Feb 29
+
+        TEQ     R3, #2                  ; month = Feb ?
+        TEQEQ   R2, #29                 ; and day = 29 ?
+        BNE     %FT20                   ; [not 29 Feb]
+
+        TST     R5, #3                  ; is year multiple of 4
+        BNE     Bad0F                   ; no, then 29 Feb is bad
+
+        TEQ     R5, #0                  ; is it a century year ?
+        BNE     %FT20                   ; no, then 29 Feb is good
+
+        TST     R6, #3                  ; is it a multiple of 400 ?
+        BNE     Bad0F                   ; no, then 29 Feb is bad
+
+20
+        ADD     R4, R4, #16             ; move on to time part
+
+; now do the time part, if appropriate
+
+50
+        TST     R9, #1
+        MOVEQ   R0, #-1                 ; if not doing time, set hours,
+        MOVEQ   R1, #-1                 ; minutes and seconds to -1
+        MOVEQ   R7, #-1
+        MOVEQ   R8, #-1
+        BEQ     %FT80                   ; [not doing time part]
+
+; do the seconds
+
+        MOV     R1, #7                  ; offset to seconds
+        BL      GetDecimalPair
+        CMPCC   R1, #60
+        MOVCC   R7, R1
+
+; zero the centiseconds
+
+        MOV     R8, #0
+
+; do the hours
+
+        MOVCC   R1, #1                  ; offset to hours
+        BLCC    GetDecimalPair
+        CMPCC   R1, #24
+        MOVCC   R0, R1
+
+; do the minutes
+
+        MOVCC   R1, #4
+        BLCC    GetDecimalPair
+        CMPCC   R1, #60
+
+        BCS     Bad0F
+80
+
+; we have now completely validated the settings
+
+    ] ;International version.
+
+        BL      SetTime                 ; also updates 5-byte RealTime
+
+Bad0F                                   ; come here if setting invalid
+        Pull    "R5-R10, R14"
+        MyOsWord
+
+; *****************************************************************************
+
+ [ {FALSE}      ; not needed for internationalised kernel
+MonthNameTable
+ = "jan",0
+ = "feb",0
+ = "mar",0
+ = "apr",0
+ = "may",0
+ = "jun",0
+ = "jul",0
+ = "aug",0
+ = "sep",0
+ = "oct",0
+ = "nov",0
+ = "dec",0
+ ]
+
+; *****************************************************************************
+;
+;       CheckYear - Check for year wrap (if months have gone down)
+;       and for leap year fudging
+;
+
+CheckYear ROUT
+        [ NewClockChip
+        Push    "R0,R1,R2,R14"
+        MOV     R0, #5                  ; year address
+        BL      ReadStraight
+        MOV     R1, R0, LSR #6          ; year in range 0..3
+        MOV     R0, #YearCMOS
+        BL      Read
+        AND     R2, R0, #3
+        SUBS    R2, R1, R2              ; same year ?
+        Pull    "R0,R1,R2,PC", EQ       ; [yes, so no bother]
+        ADDCC   R2, R2, #4              ; if lower, then must be carry
+        ADD     R2, R0, R2              ; new year value
+        CMP     R2, #100
+        BCC     %FT10                   ; no carry thru to next century
+
+        SUB     R2, R2, #100
+        MOV     R0, #YearCMOS +1
+        BL      Read
+        ADD     R1, R0, #1
+        TEQ     R1, #100
+        MOVEQ   R1, #0                  ; wrap century
+        MOV     R0, #YearCMOS +1
+        BL      Write
+10
+        MOV     R1, R2
+        MOV     R0, #YearCMOS
+        BL      Write
+
+        BL      RTCToRealTime
+        Pull    "R0,R1,R2,PC"
+
+        |
+        Push    "R0-R8, R14"
+
+        BL      ReadTime                ; R3 = clock month (in hex)
+
+        MOV     R0, #MonthCMOS
+        BL      Read                    ; R0 = CMOS RAM month
+
+        CMP     R3, R0
+        BLCC    HappyNewYear            ; The months have gone down
+
+; now make sure that Feb 29th is dealt with correctly
+
+        CMP     R3, #3                  ; if Jan or Feb then no problem
+        BCC     %FT10
+
+        MOV     R0, #LeapCMOS           ; if already fudged it
+        BL      Read                    ; then no problem
+        TEQ     R0, #0
+        BNE     %FT10
+
+        TST     R5, #3                  ; if year MOD 4 is not zero
+        BNE     %FT10                   ; then no problem
+
+        TEQ     R5, #0                  ; if not century start
+        BNE     %FT20                   ; then is leap year
+
+        TST     R6, #3                  ; else if not mult of 400
+        BNE     %FT10                   ; then no problem
+
+20                                      ; it is a leap year
+        SUBS    R2, R2, #1              ; day := day-1
+        SUBEQ   R3, R3, #1              ; if day=0 then month := month-1
+        ADREQ   R4, MonthLengths
+        LDREQB  R2, [R4, R3]            ; and day := last day of month
+
+        MOV     R0, #-1                 ; don't change hours
+        MOV     R1, #-1                 ; or minutes
+        MOV     R7, #-1                 ; or seconds
+        MOV     R8, #-1                 ; or centiseconds
+        BL      SetTime
+
+        MOV     R1, #1                  ; we have now fudged it
+        MOV     R0, #LeapCMOS
+        BL      Write
+
+        MOV     R1, R3                  ; remember the month
+        MOV     R0, #MonthCMOS
+        BL      Write
+10
+        Pull    "R0-R8, PC"
+
+; *****************************************************************************
+;
+;       HappyNewYear - Update the year
+;
+; in:   R3 = months
+;       R5 = year(lo)
+;       R6 = year(hi)
+;
+; out:  R5 = new year(lo)
+;       R6 = new year(hi)
+
+HappyNewYear ROUT
+        Push    "R0,R1, R14"
+
+        ADD     R5, R5, #1              ; R2 := new year(lo)
+        TEQ     R5, #100
+        BNE     %FT10                   ; [don't increment century]
+
+        MOV     R5, #0                  ; wrap year
+        ADD     R6, R6, #1              ; R1 := new year(hi)
+        TEQ     R6, #100
+        MOVEQ   R6, #0                  ; wrap century
+
+        MOV     R1, R6
+        MOV     R0, #YearCMOS+1
+        BL      Write                   ; write year(hi)
+10
+        MOV     R1, R5
+        MOV     R0, #YearCMOS
+        BL      Write                   ; write year(lo)
+
+        BL      RTCToRealTime           ; update 5-byte RealTime
+
+        MOV     R1, R3
+        MOV     R0, #MonthCMOS
+        BL      Write                   ; write new months
+
+        MOV     R1, #0
+        MOV     R0, #LeapCMOS
+        BL      Write                   ; clear leapyear flag
+
+        Pull    "R0,R1, PC"
+
+        ]
+
+; *****************************************************************************
+
+MonthLengths
+ ;  F  J  F  M  A  M  J  J  A  S  O  N  D
+ = 28,31,29,31,30,31,30,31,31,30,31,30,31
+         ALIGN
+
+; *****************************************************************************
+;
+;       DayOfTheWeek - Compute day of the week
+;
+; in:   R2 = day of the month
+;       R3 = month
+;       R7 = year MOD 400
+;
+; out:  R0 = day of week (0..6 = Sun..Sat) (I think!)
+;       Other registers preserved
+;
+
+DayOfTheWeek
+        Push    "R1,R4-R7, R14"
+        SUB     R0, R2, #1              ; R0 = day of the month starting at 0
+        ADR     R4, DaysToDate-4        ; adjust cos months start at 1
+        LDR     R5, [R4, R3, LSL #2]    ; get the month start offset
+        ADD     R0, R0, R5              ; number of days since start of year
+
+; now adjust for this year being leap
+
+        TST     R7, #3
+        BNE     NotLeapYr               ; non-multiples of 3 are not leap
+
+        TEQ     R7, #100
+        TEQNE   R7, #200
+        TEQNE   R7, #300
+        BEQ     NotLeapYr               ; if 100 or 200 or 300 then not leap
+
+IsLeapYr
+        CMP     R3, #3                  ; is leap, so if March 1st or after
+        ADDCS   R0, R0, #1              ; then add extra day
+NotLeapYr
+        TEQ     R7, #0                  ; if mult of 400
+        MOVEQ   R7, #400                ; then set to 400
+        ADD     R0, R0, R7              ; add one for each year
+
+        SUB     R7, R7, #1              ; R7 = last year to cope with
+        ADD     R0, R0, R7, LSR #2      ; add one for each leap year
+
+        CMP     R7, #100                ; subtract one if counting year 100
+        SUBCS   R0, R0, #1
+        CMP     R7, #200                ; subtract one if counting year 200
+        SUBCS   R0, R0, #1
+        CMP     R7, #300                ; subtract one if counting year 300
+        SUBCS   R0, R0, #1
+
+;       ADD     R0, R0, #0              ; fudge factor cos 1 Jan 1986 is Wed
+        MOV     R5, #7
+        DivRem  R1, R0, R5, R6
+
+        Pull    "R1,R4-R7, PC"
+
+; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+DaysToDate
+ & 0,31,59,90,120,151,181,212,243,273,304,334
+;  J F  M  A  M   J   J   A   S   O   N   D
+
+; *****************************************************************************
+
+; Define hardware cursor size, shape and active point
+
+OsWord15
+        ByteToNosbod DoPointerStuff
+        MyOsWord
+
+; *****************************************************************************
+
+; Set start of screen address (for VDU drivers and display)
+; [R1, #0] = bit mask (bit0 set => change drivers; bit1 set => change display)
+; [R1, #1..4] = byte offset from start of screen memory
+
+OsWord16
+        ByteToNosbod DoSetScreenStart
+        MyOsWord
+
+; **************************************************************************
+
+; All the unused OS_Word calls
+
+; Read Byte of I/O proc memory
+OsWord05 ROUT
+; Write byte of I/O proc memory
+OsWord06 ROUT
+; Define an ENVELOPE
+OsWord08 ROUT
+; Allocated to the net
+OsWord10
+OsWord11
+OsWord12
+OsWord13
+OsWord14
+        Unused
+
+        END
diff --git a/s/PMF/oswrch b/s/PMF/oswrch
new file mode 100644
index 0000000000000000000000000000000000000000..882e3da81ed1567d3569ce34b5f844072c0cd66d
--- /dev/null
+++ b/s/PMF/oswrch
@@ -0,0 +1,199 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.PMF.oswrch
+
+        GBLS    WrchLimReg
+
+; *****************************************************************************
+;
+;       PMFWrch - Entry point for WriteC vector
+;       This routine used to be nice and structured before I
+;       optimised it for plain wrch !
+;
+; in:   R0 = character
+;
+; out:  All registers preserved
+;
+
+        [ {FALSE}
+WrchLimReg      SETS    "R9"            ; would like to only push R0-R9, but
+                                        ; PMFWrchDirect is called by SWIs like
+                                        ; OS_WriteN etc, which need R10-R12
+
+PMFWrchDirect
+        BYTEWS  WsPtr                   ; if called direct, then set up R12
+PMFWrch ROUT
+        Push    "R0-$WrchLimReg"        ; if called thru vec, already set up
+        |
+WrchLimReg      SETS    "R12"           ; 0.046N, so need to save R0-R12
+
+PMFWrchDirect
+PMFWrch ROUT
+        Push    "R0-$WrchLimReg"
+        BYTEWS  WsPtr
+        ]
+
+        LDRB    R1, WrchDest
+        LDRB    R2, SpoolFileH
+        ORRS    R3, R1, R2
+        BNE     %FT10
+
+        [ AssemblingArthur
+        VDWS    WsPtr
+        BL      Vdu
+        |
+        BL      WrchVdu                 ; call VDU
+        ]
+        BVS     %FT45
+        Pull    "R0-$WrchLimReg,PC", CC
+        B       %FT15
+
+10
+        TST     R1, #&22                ; branch if wrch not to VDU
+        BNE     %FT50                   ; or wrch to extension vector
+
+        [ AssemblingArthur
+        VDWS    WsPtr
+        BL      Vdu
+        |
+        BL      WrchVdu                 ; call VDU
+        ]
+15
+        BVS     %FT45                   ; error from VDU
+        LDMFD   R13, {R0}               ; reload R0 with character
+        BYTEWS  WsPtr                   ; reload workspace pointer
+        LDRB    R1, WrchDest            ; and wrch destinations
+        BCS     PrintVdu                ; VDU says "Print it"
+20
+        TST     R1, #8                  ; printer enabled, independent of
+                                        ; CTRL-B and CTRL-C ?
+        BNE     PrintVdu                ; yes, then branch
+40
+        TST     R1, #1                  ; output to RS423 ?
+        BNE     RS423Vdu                ; yes, then do it
+42
+        LDRB    R2, SpoolFileH          ; spool file open ?
+        CMP     R2, #0                  ; (set V=0 for if we drop thru)
+        BLNE    SpoolVdu                ; yes, then go
+45
+        [ AssemblingArthur :LOR: ErrorsInR0
+        Pull    "R0-$WrchLimReg, PC", VC
+        ADD     R13, R13, #4
+        Pull    "R1-$WrchLimReg, PC"
+        |
+        Pull    "R0-$WrchLimReg, PC"            ; that's it (phew!)
+        ]
+
+; Come here when Wrch not to VDU or Wrch to VDUXV
+
+50
+        TST     R1, #&02                ; wrch not to VDU at all ?
+        BNE     %BT20                   ; then skip
+                                        ; else must be VDU sent thru VDUXV
+        MOV     R10, #VDUXV
+        BL      GoVec2                  ; call vector
+        B       %BT15
+
+; *****************************************************************************
+
+PrintVdu
+        TST     R1, #&40                ; only print via VDU 1 ?
+        BNE     %BT40                   ; yes, then skip
+
+        LDRB    R2, PrinterIgnore       ; is it ignore character ?
+        TEQ     R0, R2
+        LDREQB  R2, NoIgnore            ; and ignoring enabled ?
+        TSTEQ   R2, #&80
+        BEQ     %BT40                   ; yes, then don't print it
+
+        BL      MOSDoPrintWS            ; else print it (R12 -> ByteWS)
+        BVS     %BT45                   ; error in printing
+        LDMFD   R13, {R0}               ; reload R0 with character
+        LDRB    R1, WrchDest            ; and reload wrchdest
+        B       %BT40
+
+RS423Vdu
+ [ DriversInKernel
+        PHPSEI
+        Push    "R0,R1,R14"             ; I know what I'm doing, honest!
+        MOV     R1, #Buff_RS423Out      ; RS423 output buffer id
+        SETV                            ; indicate examine buffer
+        BL      REMOVE
+        BLCS    RSBUSY                  ; buff empty, so reawaken TXIRQ
+        LDMFD   R13, {R0}               ; get char back
+        MOV     R1, #Buff_RS423Out      ; RS423 output buffer id
+        BL      WRITE                   ; write to buffer (waiting)
+        Pull    "R0,R1,R14"
+        PLP                             ; restore interrupt state (V preserved)
+        B       %BT42                   ; carry on with rest
+ |
+        Push    "r0,r1"
+        LDRB    r1, SerialOutHandle
+        TEQ     r1, #0
+        BNE     %FT60
+        MOV     r0, #open_write
+        ADR     r1, SerialOutFilename
+        SWI     XOS_Find
+        BVS     %FT70                   ; if can't open serial output stream, report error
+                                        ; and don't put anything in buffer
+        STRB    r0, SerialOutHandle
+        LDMFD   sp, {r0}                ; get char back
+60
+        PHPSEI
+        Push    "r14"                   ; save IRQ indication
+        MOV     r1, #Buff_RS423Out      ; RS423 output buffer id
+        BL      WRITE                   ; write to buffer (waiting)
+        Pull    "r14"
+        PLP                             ; restore IRQ state from lr
+        Pull    "r0,r1"
+        B       %BT42
+
+; we got an error from the open, so in order to report it,
+; we'd better stop outputting to RS423
+
+70
+        ADD     sp, sp, #4              ; junk stacked r0
+        Pull    "r1"
+        BIC     r1, r1, #1              ; clear RS423 output bit
+        STRB    r1, WrchDest            ; write back to OS_Byte
+        B       %BT45                   ; report error
+ ]
+
+ [ :LNOT: DriversInKernel
+SerialOutFilename
+        =       "Serial#Buffer2:", 0
+        ALIGN
+ ]
+
+SpoolVdu                                ; entered with V=0
+        TST     R1, #&10                ; spooling enabled ?
+        MOVNE   PC, R14                 ; no, then return
+
+        Push    R14                     ; cos we're doing a SWI in SVC mode
+        MOV     R1, R2                  ; put handle in R1
+        SWI     XOS_BPut                ; put byte to file
+        Pull    PC, VC                  ; if no error, return with V clear
+                                        ; (no need to reload R1, since SPOOL
+                                        ; is done last)
+SpoolBadExit
+        Push    R0
+        MOV     R0, #0                  ; stop spooling FIRST
+        STRB    R0, SpoolFileH
+        SWI     XOS_Find                ; and close file (R0=0, R1=handle)
+        Pull    "R1, R14"
+        MOVVC   R0, R1                  ; if closed OK, then restore old error
+        ORRS    PC, R14, #V_bit         ; still indicate error
+
+        END
diff --git a/s/PMF/realtime b/s/PMF/realtime
new file mode 100644
index 0000000000000000000000000000000000000000..9e2dc15def51cbe5b13b878ae15ea0326ba9600c
--- /dev/null
+++ b/s/PMF/realtime
@@ -0,0 +1,331 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.PMF.realtime
+
+; *****************************************************************************
+;
+;       RTCToRealTime - Set RealTime from actual RTC
+;
+; in:   R12 -> BYTEWS
+; out:  all registers preserved
+;
+
+        [ {FALSE}
+Construct5Byte ROUT
+        Push    R14
+        B       Construct5ByteEntry
+        ]
+
+RTCToRealTime ROUT
+        Push    "R0-R9, R14"
+        BL      ReadTime                ; R0 := hours, R1 := minutes
+                                        ; R2 := days, R3 := months
+                                        ; R5 := year(lo), R6 := year(hi)
+                                        ; R7 := seconds, R8 := centiseconds
+        BL      ConvertTo5Byte
+        BL      Store5ByteInRealTime
+        Pull    "R0-R9, PC"
+
+        [ {FALSE}                       ; not used
+RegToRealTime ROUT
+        Push    "R0-R9, R14"
+        BL      ConvertTo5Byte
+        BL      Store5ByteInRealTime
+        Pull    "R0-R9, PC"
+        ]
+
+ConvertTo5Byte ROUT
+        Push    R14
+
+        MOV     R4, R5                  ; R4 := year MOD 100
+        MOV     R5, R6                  ; R5 := year DIV 100
+        MOV     R6, R7                  ; R6 := seconds
+        MOV     R7, R8                  ; R7 := centiseconds
+
+Construct5ByteEntry
+        MOV     R9, #24
+        SUB     R2, R2, #1              ; decrement day (day=1 => nowt to add)
+        MLA     R0, R9, R2, R0          ; R0 = hours + day*24
+        MOV     R9, #60
+        MLA     R1, R0, R9, R1          ; R1 = mins + hours*60
+        MLA     R6, R1, R9, R6          ; R6 = secs + mins*60
+        MOV     R9, #100
+        MLA     R7, R6, R9, R7          ; R7 = centisecs + secs*100
+
+        ADR     R0, STMonths-4          ; Point to table (month = 1..12)
+        LDR     R1, [R0, R3, LSL #2]    ; get word of offset
+        ADD     R7, R7, R1              ; add to total
+
+; if not had leap day in this year yet, then exclude this year from the
+; leap day calculations
+
+        CMP     R3, #3                  ; if month >= 3
+        SBCS    R0, R4, #0              ; then R0,R1 = R4,R5
+        MOVCC   R0, #99                 ; else R0,R1 = R4,R5 -1
+        SBC     R1, R5, #0
+
+; want (yl+100*yh) DIV 4 - (yl+100*yh) DIV 100 + (yl+100*yh) DIV 400
+; = (yl DIV 4)+ (25*yh) - yh + (yh DIV 4)
+; = (yl >> 2) + 24*yh + (yh >> 2)
+
+        MOV     R0, R0, LSR #2          ; yl >> 2
+        ADD     R0, R0, R1, LSR #2      ; + yh >> 2
+        ADD     R0, R0, R1, LSL #4      ; + yh * 16
+        ADD     R0, R0, R1, LSL #3      ; + yh * 8
+
+; now subtract off the number of leap days in first 1900 years = 460
+
+        SUBS    R0, R0, #460
+        BCC     BadYear                 ; before 1900, so bad
+        CMP     R0, #86                 ; if more than 86 days, then it's
+        BCS     BadYear                 ; after 2248, so bad
+
+        LDR     R9, =ticksperday        ; multiply by ticksperday and add to
+        MLA     R7, R9, R0, R7          ; total (no overflow possible as this
+                                        ; can never be more than 85+31 days)
+
+; now add on (year-1900)*ticksperyear
+
+        SUBS    R5, R5, #19             ; subtract off 1900
+        BCC     BadYear
+        MOV     R9, #100
+        MLA     R4, R9, R5, R4          ; R4 = year-1900
+
+        LDR     R0, =ticksperyear       ; lo word of amount to add on
+        MOV     R1, #0                  ; hi word of amount to add on
+        MOV     R8, #0                  ; hi word of result
+10
+        MOVS    R4, R4, LSR #1
+        BCC     %FT15
+
+        ADDS    R7, R7, R0              ; if bit set then add on amount
+        ADCS    R8, R8, R1
+        BCS     BadYear                 ; overflow => bad time value
+15
+        ADDS    R0, R0, R0              ; shift up amount
+        ADCS    R1, R1, R1
+        TEQ     R4, #0                  ; if still bits to add in
+        BNE     %BT10                   ; then loop
+
+        CMP     R8, #&100               ; R8 must only be a byte
+        Pull    PC, CC
+
+BadYear
+        MOV     R7, #-1
+        MOV     R8, #-1
+        Pull    PC
+
+
+Store5ByteInRealTime
+        Push    R14
+        PHPSEI                          ; disable IRQs for this bit
+        STR     R7, RealTime +0
+        STRB    R8, RealTime +4
+
+        [ :LNOT: AssemblingArthur :LAND: :LNOT: Module
+; for now, also put into normal time
+
+        LDRB    R0, TimerState
+        TEQ     R0, #5
+
+        ADREQ   R3, TimerAlpha +0
+        ADRNE   R3, TimerBeta +0
+
+        STR     R7, [R3]
+        STRB    R8, [R3, #4]
+        ]
+        PLP
+
+        Pull    PC
+
+; *****************************************************************************
+
+tickspersecond  * 100
+ticksperminute  * tickspersecond * 60
+ticksperhour    * ticksperminute * 60
+ticksperday     * ticksperhour   * 24
+ticksperyear    * ticksperday    * 365  ; &BBF81E00
+
+STMonths
+        &       &00000000       ; Jan
+        &       &0FF6EA00       ; Feb
+        &       &1E625200       ; Mar
+        &       &2E593C00       ; Apr
+        &       &3DCC5000       ; May
+        &       &4DC33A00       ; Jun
+        &       &5D364E00       ; Jul
+        &       &6D2D3800       ; Aug
+        &       &7D242200       ; Sep
+        &       &8C973600       ; Oct
+        &       &9C8E2000       ; Nov
+        &       &AC013400       ; Dec
+        &       &F0000000       ; terminator, must be less than this (+1)
+
+        [ {FALSE}
+; *****************************************************************************
+;
+;       DecodeAcornTime - Convert from 5-byte cs representation to
+;                         "01:23:45 on 01-Jan-1988<0D>" format
+;
+; in:   R1 -> block
+;       [R1, #1..5] = 5-byte centisecond representation
+;
+; out:  [R1, #0..23] = string representation
+;
+
+OsWord0EEpsilon
+DecodeAcornTime ROUT
+        ADD     R0, R1, #1              ; R0 -> 5 bytes of centiseconds
+        MOV     R2, #24
+        ADR     R3, AcornTimeFormat
+        B       OsWord0EDandT           ; needs to be recoded if re-included
+
+AcornTimeFormat
+        =       "%24:%mi:%se on %dy-%m3-%ce%yr", 0
+        ALIGN
+
+        LTORG
+
+; *****************************************************************************
+;
+;       EncodeAcornTime - Convert string representation (as above) into
+;                         5-byte centisecond value
+;
+; in:   R1 -> block
+;       [R1, #1..24] = string representation
+;
+; out:  [R1, #0..4] = 5 byte cs representation
+;
+
+OsWord0EZeta
+EncodeAcornTime ROUT
+        Push    "R7-R10"
+
+; Firstly, the months (into R3)
+
+        LDRB    R0, [R1, #16]           ; Get first char and lowercase it
+        ORR     R0, R0, #&20
+        LDRB    R2, [R1, #16+1]         ; And the second
+        ORR     R2, R2, #&20
+        LDRB    R3, [R1, #16+2]         ; And the third
+        ORR     R3, R3, #&20
+        ORR     R0, R0, R2, LSL #8      ; Make a word of the chars
+        ORR     R0, R0, R3, LSL #16     ; eg. 0naj, 0bef etc.
+
+        MOV     R3, #0
+        ADRL    R2, MonthNameTable
+10      ADD     R3, R3, #1
+        CMP     R3, #13
+        BCS     BadEncodeTime
+        LDR     R4, [R2], #4            ; Test month name against list
+        TEQ     R4, R0
+        BNE     %BT10
+
+; Ok, suss the years
+
+        MOV     R10, #20                ; Read pair from hundreds/thousands
+        BL      GetPair
+        BCS     BadEncodeTime
+        MOV     R5, R0
+
+        MOV     R10, #20+2              ; And from tens/years
+        BL      GetPair
+        BCS     BadEncodeTime
+        MOV     R4, R0
+
+; The days, please
+
+        MOV     R10, #13                ; Read pair from days field
+        BL      GetPair
+        BCS     BadEncodeTime
+        MOV     R2, R0
+
+; Then the seconds
+
+        MOV     R10, #7                 ; Read pair from seconds field
+        BL      GetPair
+        CMPCC   R0, #60                 ; 00..59 valid
+        BCS     BadEncodeTime
+        MOV     R6, R0
+
+; Now the minutes
+
+        MOV     R10, #4                 ; Read pair from minutes field
+        BL      GetPair
+        CMPCC   R0, #60                 ; 00..59 valid
+        BCS     BadEncodeTime
+        MOV     R9, R0                  ; Can't disturb R1 yet !
+
+; Lastly the hours
+
+        MOV     R10, #1                 ; Read pair from hours field
+        BL      GetPair
+        CMPCC   R0, #24                 ; 00..23 valid
+        BCS     BadEncodeTime
+
+        MOV     R10, R1
+        MOV     R1, R9                  ; Get minutes back
+        MOV     R7, #0                  ; Centiseconds = 0
+        BL      Construct5Byte          ; Has R0-R7 parameters; R7,R8 on exit
+EncodeExit
+        STRB    R7, [R10, #0]
+        MOV     R7, R7, LSR #8
+        STRB    R7, [R10, #1]
+        MOV     R7, R7, LSR #8
+        STRB    R7, [R10, #2]
+        MOV     R7, R7, LSR #8
+        STRB    R7, [R10, #3]
+        STRB    R8, [R10, #4]
+
+        Pull    "R7-R10"
+        B       OsWord0Eend
+
+
+BadEncodeTime
+        MOV     R8, #-1                 ; Set date to out-of-bounds value
+        MOV     R7, #-1                 ; (This would be a command file)
+        MOV     R10, R1
+        B       EncodeExit
+
+; *****************************************************************************
+;
+;       GetPair - Get a pair of decimal digits
+;
+; in:   [R1,R10], [R1,R10+1] contain digits
+;
+; out:  C=0 => R0 = 10*msdigit + lsdigit
+;       C=1 => invalid pair of digits
+;
+
+GetPair ROUT
+        Push    "R2, R10, R14"
+
+        LDRB    R2, [R10, R1]!          ; Get msdigit and convert to decimal
+        SUB     R2, R2, #"0"
+        CMP     R2, #10                 ; Keep CState for exit
+
+        LDRB    R0, [R10, #1]           ; Get lsdigit and convert to decimal
+        SUB     R0, R0, #"0"
+        CMPCC   R0, #10
+
+        ADD     R2, R2, R2, LSL #2      ; Multiply msdigit by 5
+        ADD     R0, R0, R2, LSL #1      ; complete mult by 10 and add
+
+        Pull    "R2, R10, PC"
+        ]
+
+        LTORG
+
+        END
diff --git a/s/SWINaming b/s/SWINaming
new file mode 100644
index 0000000000000000000000000000000000000000..1b0797b01574e92d837ffd8d95757c906f5fffaf
--- /dev/null
+++ b/s/SWINaming
@@ -0,0 +1,641 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL     => SWINaming
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        MACRO
+$l      AddSwiNameToDecodeTab   $name
+$l      = "$name", 0
+SwisInSystemTable SETA SwisInSystemTable+1
+        MEND
+
+;***********************************************************************
+;
+;       SWI OS_SWINumberToString
+;
+; in:   R0 = SWI number
+;       R1 = buffer pointer
+;       R2 = buffer length
+;
+; out:  Buffer holds SWI name, null-terminated
+;
+
+SWINumberToString_Code    ROUT
+        TEQP    pc, #SVC_mode           ; interrupts on!
+        MOV     r12, lr                 ; keep here so subroutines can update flags
+        Push    "r3, r9"
+        MOV     r9, r0                  ; r0 to pass back
+        MOV     r3, r2                  ; buffer limit
+        MOV     r2, #0                  ; characters so far
+
+        TST     r0, #Auto_Error_SWI_bit
+        MOVNE   r10, #"X"
+        BLNE    AddChar
+        BIC     r0, r0, #Auto_Error_SWI_bit
+        CMP     r0, #512
+        BCS     NotASystemSWI           ; TMD 11-May-89: changed from GE
+
+        ADR     r11, OS_Prefix
+        BL      AddString
+        MOV     r10, #"_"
+        BL      AddChar
+
+        CMP     r0, #256
+        BCS     Swi_Is_WriteI           ; TMD 11-May-89: changed from GE
+        CMP     r0, #MaxSwi
+        BCS     SWINotInTable           ; TMD 11-May-89: changed from GE
+
+        ADR     r11, System_Swi_Names
+        BL      GetStringFromTable
+
+AddStringAndExit
+        BL      AddString
+
+ExitSwiSwi
+        MOV     r10, #0
+        BL      AddChar
+
+        MOV     r0, r9                  ; error pointer or restoration
+        Pull    "r3, r9"
+        MOV     lr, r12
+        ExitSWIHandler
+
+                  GBLA SwisInSystemTable
+SwisInSystemTable SETA -1
+
+OS_Prefix =    "OS",0
+
+System_Swi_Names
+        AddSwiNameToDecodeTab WriteC
+        AddSwiNameToDecodeTab WriteS
+        AddSwiNameToDecodeTab Write0
+        AddSwiNameToDecodeTab NewLine
+        AddSwiNameToDecodeTab ReadC
+        AddSwiNameToDecodeTab CLI
+        AddSwiNameToDecodeTab Byte
+        AddSwiNameToDecodeTab Word
+        AddSwiNameToDecodeTab File
+        AddSwiNameToDecodeTab Args
+        AddSwiNameToDecodeTab BGet
+        AddSwiNameToDecodeTab BPut
+        AddSwiNameToDecodeTab GBPB
+        AddSwiNameToDecodeTab Find
+        AddSwiNameToDecodeTab ReadLine
+        AddSwiNameToDecodeTab Control
+        AddSwiNameToDecodeTab GetEnv
+        AddSwiNameToDecodeTab Exit
+        AddSwiNameToDecodeTab SetEnv
+        AddSwiNameToDecodeTab IntOn
+        AddSwiNameToDecodeTab IntOff
+        AddSwiNameToDecodeTab CallBack
+        AddSwiNameToDecodeTab EnterOS
+        AddSwiNameToDecodeTab BreakPt
+        AddSwiNameToDecodeTab BreakCtrl
+        AddSwiNameToDecodeTab UnusedSWI
+        AddSwiNameToDecodeTab UpdateMEMC
+        AddSwiNameToDecodeTab SetCallBack
+        AddSwiNameToDecodeTab Mouse
+
+        AddSwiNameToDecodeTab Heap
+        AddSwiNameToDecodeTab Module
+        AddSwiNameToDecodeTab Claim
+        AddSwiNameToDecodeTab Release
+        AddSwiNameToDecodeTab ReadUnsigned
+        AddSwiNameToDecodeTab GenerateEvent
+        AddSwiNameToDecodeTab ReadVarVal
+        AddSwiNameToDecodeTab SetVarVal
+        AddSwiNameToDecodeTab GSInit
+        AddSwiNameToDecodeTab GSRead
+        AddSwiNameToDecodeTab GSTrans
+        AddSwiNameToDecodeTab BinaryToDecimal
+        AddSwiNameToDecodeTab FSControl
+        AddSwiNameToDecodeTab ChangeDynamicArea
+        AddSwiNameToDecodeTab GenerateError
+        AddSwiNameToDecodeTab ReadEscapeState
+        AddSwiNameToDecodeTab EvaluateExpression
+        AddSwiNameToDecodeTab SpriteOp
+        AddSwiNameToDecodeTab ReadPalette
+        AddSwiNameToDecodeTab ServiceCall
+        AddSwiNameToDecodeTab ReadVduVariables
+        AddSwiNameToDecodeTab ReadPoint
+        AddSwiNameToDecodeTab UpCall
+        AddSwiNameToDecodeTab CallAVector
+        AddSwiNameToDecodeTab ReadModeVariable
+        AddSwiNameToDecodeTab RemoveCursors
+        AddSwiNameToDecodeTab RestoreCursors
+        AddSwiNameToDecodeTab SWINumberToString
+        AddSwiNameToDecodeTab SWINumberFromString
+        AddSwiNameToDecodeTab ValidateAddress
+        AddSwiNameToDecodeTab CallAfter
+        AddSwiNameToDecodeTab CallEvery
+        AddSwiNameToDecodeTab RemoveTickerEvent
+        AddSwiNameToDecodeTab InstallKeyHandler
+        AddSwiNameToDecodeTab CheckModeValid
+        AddSwiNameToDecodeTab ChangeEnvironment
+        AddSwiNameToDecodeTab ClaimScreenMemory
+        AddSwiNameToDecodeTab ReadMonotonicTime
+        AddSwiNameToDecodeTab SubstituteArgs
+        AddSwiNameToDecodeTab PrettyPrint
+        AddSwiNameToDecodeTab Plot
+        AddSwiNameToDecodeTab WriteN
+        AddSwiNameToDecodeTab AddToVector
+        AddSwiNameToDecodeTab WriteEnv
+        AddSwiNameToDecodeTab ReadArgs
+        AddSwiNameToDecodeTab ReadRAMFsLimits
+        AddSwiNameToDecodeTab ClaimDeviceVector
+        AddSwiNameToDecodeTab ReleaseDeviceVector
+        AddSwiNameToDecodeTab DelinkApplication
+        AddSwiNameToDecodeTab RelinkApplication
+        AddSwiNameToDecodeTab HeapSort
+        AddSwiNameToDecodeTab ExitAndDie
+        AddSwiNameToDecodeTab ReadMemMapInfo
+        AddSwiNameToDecodeTab ReadMemMapEntries
+        AddSwiNameToDecodeTab SetMemMapEntries
+        AddSwiNameToDecodeTab AddCallBack
+        AddSwiNameToDecodeTab ReadDefaultHandler
+        AddSwiNameToDecodeTab SetECFOrigin
+        AddSwiNameToDecodeTab SerialOp
+        AddSwiNameToDecodeTab ReadSysInfo
+        AddSwiNameToDecodeTab Confirm
+        AddSwiNameToDecodeTab ChangedBox
+        AddSwiNameToDecodeTab CRC
+        AddSwiNameToDecodeTab ReadDynamicArea
+        AddSwiNameToDecodeTab PrintChar
+        AddSwiNameToDecodeTab ChangeRedirection
+        AddSwiNameToDecodeTab RemoveCallBack
+        AddSwiNameToDecodeTab FindMemMapEntries
+        AddSwiNameToDecodeTab SetColour
+        AddSwiNameToDecodeTab ClaimSWI          ; These two are not actually
+        AddSwiNameToDecodeTab ReleaseSWI        ; kernel SWIs.
+        AddSwiNameToDecodeTab Pointer
+        AddSwiNameToDecodeTab ScreenMode
+        AddSwiNameToDecodeTab DynamicArea
+        AddSwiNameToDecodeTab AbortTrap
+        AddSwiNameToDecodeTab Memory
+        AddSwiNameToDecodeTab ClaimProcessorVector
+        AddSwiNameToDecodeTab Reset
+        AddSwiNameToDecodeTab MMUControl
+        = 0
+
+ [ SwisInSystemTable+1 <> MaxSwi
+ ! 1, :CHR:10:CC::CHR:13:CC::CHR:7:CC::CHR:7:CC:"Swi Disassembly table not consistent with despatch table":CC::CHR:10:CC::CHR:13
+ ]
+
+convswitab = "OS",0
+
+Conversion_Swi_Names
+        AddSwiNameToDecodeTab  ConvertHex1
+        AddSwiNameToDecodeTab  ConvertHex2
+        AddSwiNameToDecodeTab  ConvertHex4
+        AddSwiNameToDecodeTab  ConvertHex6
+        AddSwiNameToDecodeTab  ConvertHex8
+        AddSwiNameToDecodeTab  ConvertCardinal1
+        AddSwiNameToDecodeTab  ConvertCardinal2
+        AddSwiNameToDecodeTab  ConvertCardinal3
+        AddSwiNameToDecodeTab  ConvertCardinal4
+        AddSwiNameToDecodeTab  ConvertInteger1
+        AddSwiNameToDecodeTab  ConvertInteger2
+        AddSwiNameToDecodeTab  ConvertInteger3
+        AddSwiNameToDecodeTab  ConvertInteger4
+        AddSwiNameToDecodeTab  ConvertBinary1
+        AddSwiNameToDecodeTab  ConvertBinary2
+        AddSwiNameToDecodeTab  ConvertBinary3
+        AddSwiNameToDecodeTab  ConvertBinary4
+        AddSwiNameToDecodeTab  ConvertSpacedCardinal1
+        AddSwiNameToDecodeTab  ConvertSpacedCardinal2
+        AddSwiNameToDecodeTab  ConvertSpacedCardinal3
+        AddSwiNameToDecodeTab  ConvertSpacedCardinal4
+        AddSwiNameToDecodeTab  ConvertSpacedInteger1
+        AddSwiNameToDecodeTab  ConvertSpacedInteger2
+        AddSwiNameToDecodeTab  ConvertSpacedInteger3
+        AddSwiNameToDecodeTab  ConvertSpacedInteger4
+        AddSwiNameToDecodeTab  ConvertFixedNetStation
+        AddSwiNameToDecodeTab  ConvertNetStation
+        AddSwiNameToDecodeTab  ConvertFixedFileSize
+        AddSwiNameToDecodeTab  ConvertFileSize
+        =  0
+        ALIGN
+
+
+SWINotInTable
+        SUB     r11, r0, #OS_ConvertHex1
+        CMP     r11, #OS_ConvertFileSize - OS_ConvertHex1
+        BHI     %FT10
+
+        MOV     r0, r11
+        addr    r11, Conversion_Swi_Names
+        BL      GetStringFromTable
+        B       AddStringAndExit
+
+10
+        ADR     r11, %FT01
+        CMP     r0, #OS_ConvertStandardDateAndTime
+        ADREQ   r11, %FT05
+        CMP     r0, #OS_ConvertDateAndTime
+        ADREQ   r11, %FT06
+        B       AddStringAndExit
+01
+        =       "Undefined", 0
+
+othersysswitab
+        =       "OS", 0
+05
+        =       "ConvertStandardDateAndTime", 0
+06
+        =       "ConvertDateAndTime", 0
+        =       0
+
+02
+        =       "User", 0
+
+andfiddleaboutwithWriteI
+        =       "OS", 0
+        =       "WriteI", 0
+        =       0
+03
+        =       "WriteI+", 0
+
+        ALIGN
+
+NotASystemSWI
+        Push    "r9, r12"
+        BIC     r10, r0, #Module_SWIChunkSize-1
+        ModSWIHashvalOffset r9, r10
+        LDR     r9, [r9, #ModuleSWI_HashTab]
+lohc
+        CMP     r9, #0
+        BEQ     giveemaboringname
+        LDR     r12, [r9, #ModSWINode_Number]
+        CMP     r10, r12
+        LDRNE   r9, [r9, #ModSWINode_Link]
+        BNE     lohc
+
+        LDR     r12, [r9, #ModSWINode_MListNode]
+        LDR     r9, [r12, #Module_code_pointer]
+        LDR     r10, [r9, #Module_NameTable]
+        LDR     r14, [r9, #-4]                          ; get module size
+        CMP     r10, #1                                 ; must be non-zero
+        CMPCS   r14, r10                                ; and must be within code
+        BLS     trymodule_SWIdecode_code
+        ADD     r11, r10, r9
+        Pull    "r9, r12"
+        BL      AddString
+        MOV     r10, #"_"
+        BL      AddChar
+        AND     r0, r0, #Module_SWIChunkSize-1
+        Push    "r0"
+        BL      GetStringFromTable
+        Pull    "r0"
+        BVC     AddStringAndExit
+        B       AddNumericBit                           ; not in table
+
+trymodule_SWIdecode_code
+        LDR     r10, [r9, #Module_NameCode]
+        TST     r10, #12,2                              ; test bottom 2 bits and clear carry
+        CMPEQ   r10, #1                                 ; must be non-zero
+        CMPCS   r14, r10                                ; and must be within code
+        BLS     usethemoduletitle
+
+; got r0 is SWI number, r1 buffer pointer, r2 is buffer offset to use
+; r3 is buffer limit
+        CMP     r2, r3
+        BGE     dont_confuse_the_poor_dears
+        Push    "r4-r6"
+        AND     r0, r0, #Module_SWIChunkSize - 1
+        LDR     r12, [r12, #Module_incarnation_list]
+        ADDS    r12, r12, #Incarnation_Workspace        ; force V clear
+        MOV     lr, pc
+        ADD     pc, r9, r10
+        Pull    "r4-r6, r9, r12"
+        BVC     ExitSwiSwi
+
+dont_confuse_the_poor_dears
+      [ International
+        Push    "r0"
+        ADRL    r0, BufferOFloError
+        BL      TranslateError
+        MOV     r9,r0
+        Pull    "r0"
+      |
+        ADRL    r9, BufferOFloError
+      ]
+        ORR     r12, r12, #V_bit
+        B       ExitSwiSwi
+
+usethemoduletitle
+        LDR     r10, [r9, #Module_Title]
+        ADD     r11, r10, r9
+        Pull    "r9, r12"
+        BL      AddString
+        MOV     r10, #"_"
+        BL      AddChar
+        AND     r0, r0, #Module_SWIChunkSize-1
+        B       AddNumericBit
+
+giveemaboringname                       ; not found anywhere interesting
+        Pull    "r9, r12"
+        ADR     r11, %BT02
+        B       AddStringAndExit
+
+Swi_Is_WriteI
+        ADR     r11, %BT03
+        BL      AddString
+
+        AND     r0, r0, #255
+        CMP     r0, #32
+        BLT     AddNumericBit
+        CMP     r0, #127
+        BCS     AddNumericBit
+        MOV     r10, #""""
+        BL      AddChar
+        MOV     r10, r0
+        BL      AddChar
+        MOV     r10, #""""
+        BL      AddChar
+        B       ExitSwiSwi
+
+AddNumericBit
+        Push    "r1, r2"
+        ADD     r1, r1, r2              ; point at remaining buffer
+        SUB     r2, r3, r2              ; buffer left
+        SWI     XOS_BinaryToDecimal
+        ORRVS   r12, r12, #V_bit
+        MOVVS   r9, r0
+        MOV     r0, r2
+        Pull    "r1, r2"
+        ADD     r2, r2, r0              ; adjust chars given
+        B       ExitSwiSwi
+
+; AddChar
+;
+; in:   R1 = buffer pointer
+;       R2 = buffer position
+;       R3 = buffer size
+;       R10 = character
+;
+; out:  If overflow, V_bit set in R12, and R9 -> error
+;       PSR preserved
+
+AddChar ROUT
+        CMP     r2, r3
+        BGE     %FT01
+        STRB    r10, [r1, r2]
+        ADD     r2, r2, #1
+        MOVS    pc, lr
+
+01
+      [ International
+        Push    "r0,lr"
+        ADRL    r0, BufferOFloError
+        BL      TranslateError
+        MOV     r9,r0
+        Pull    "r0,lr"
+      |
+        ADRL    r9, BufferOFloError
+      ]
+        ORR     r12, r12, #V_bit
+        MOVS    pc, lr
+
+; AddString
+;
+; in:   R11 points at string to add
+;
+; out:  R10, R11 corrupted
+
+AddString ENTRY
+01
+        LDRB    r10, [r11], #1
+        CMP     r10, #0
+        BLNE    AddChar
+        BNE     %BT01
+        EXIT
+
+; GetStringFromTable
+;
+; in:   R0 is table offset
+;       R11 points at first SWI name in table
+;
+; out:  R11 -> string or V set if not in table
+;       R0, R10 corrupted
+
+GetStringFromTable ROUT
+        LDRB    r10, [r11]
+        CMP     r10, #0
+        ORREQS  pc, lr, #V_bit           ; end of table
+        SUBS    r0, r0, #1
+        BICMIS  pc, lr, #V_bit
+01
+        LDRB    r10, [r11], #1
+        CMP     r10, #0
+        BNE     %BT01
+        B       GetStringFromTable
+
+;***********************************************************************
+
+; R1 pointer to name terminated by char <= " "
+; return R0 as SWI number
+
+SWINumberFromString_Code ENTRY "r1,r2"
+
+        TEQP    pc, #SVC_mode                   ; enable interrupts
+        LDRB    R10, [R1]
+        CMP     R10, #"X"
+        MOVEQ   R0, #Auto_Error_SWI_bit
+        ADDEQ   R1, R1, #1
+        MOVNE   R0, #0
+
+        MOV     r10, #0                         ; indicate doing OS SWIs, so disallow OS_<number>
+        ADRL    r11, OS_Prefix                  ; point at system table
+        BL      LookForSwiName
+        BVC     GotTheSWIName
+
+        BIC     r0, r0, #255
+        ADRL    r11, othersysswitab
+        BL      LookForSwiName
+        ORRVC   r0, r0, #OS_ConvertStandardDateAndTime
+        BVC     GotTheSWIName
+
+        BIC     r0, r0, #255
+        ADRL    r11, convswitab
+        BL      LookForSwiName
+        ADDVC   r0, r0, #OS_ConvertHex1
+        BVC     GotTheSWIName
+
+        BIC     r0, r0, #255
+        ADRL    r11, andfiddleaboutwithWriteI
+        BL      LookForSwiName
+        ORRVC   r0, r0, #OS_WriteI
+        BVC     GotTheSWIName
+
+        MOV     r12, #Module_List
+10
+        LDR     r12, [r12]
+        CMP     r12, #0
+        BEQ     this_swi_nexiste_pas
+        LDR     r10, [r12, #Module_code_pointer]
+        LDR     r11, [r10, #Module_SWIChunk]            ; first validate swi chunk
+        BICS    r11, r11, #Auto_Error_SWI_bit
+        BEQ     %BT10                                   ; SWI chunk zero not allowed
+        TST     r11, #Module_SWIChunkSize-1
+        TSTEQ   r11, #&FF000000
+        BNE     %BT10                                   ; invalid SWI chunk
+
+        LDR     r2, [r10, #-4]
+        LDR     r11, [r10, #Module_SWIEntry]
+        TST     r11, #12,2                              ; test bottom 2 bits and clear carry
+        CMPEQ   r11, #1                                 ; must be non-zero
+        CMPCS   r2, r11                                 ; and must be within code
+        BLS     %BT10
+
+        LDR     r11, [r10, #Module_NameTable]
+        CMP     r11, #1                                 ; must be non-zero
+        CMPCS   r2, r11                                 ; and must be within code
+        BLS     %FT20                                   ; if no name table, try name code
+
+        ADD     r11, r10, r11
+        BIC     r0, r0, #Module_SWIChunkSize-1
+        BL      LookForSwiName
+        BVS     %FT20
+gotmodulejobbie
+        LDR     r11, [r10, #Module_SWIChunk]
+        BIC     r11, r11, #Auto_Error_SWI_bit
+        ORR     r0, r0, r11
+        B       GotTheSWIName
+
+; call module code if it exists
+
+20
+        LDR     r11, [r10, #Module_NameCode]
+        TST     r11, #12,2                              ; test bottom 2 bits and clear carry
+        CMPEQ   r11, #1                                 ; must be non-zero
+        CMPCS   r2, r11                                 ; and must be within code
+        BLS     %FT30                                   ; try <module-title>_<numeric>
+
+; got R1 string pointer
+
+        Push    "r0-r6, r12"
+        MOV     r0, #-1                                 ; indicate string being given
+        LDR     r12, [r12, #Module_incarnation_list]
+        ADDS    r12, r12, #Incarnation_Workspace        ; force V clear
+        MOV     lr, pc
+        ADD     pc, r11, r10
+        ADDS    r2, r0, #0                              ; NB clears V for SWI return
+        Pull    "r0"
+        ADDPL   r0, r0, r2
+        Pull    "r1-r6, r12"
+        BPL     gotmodulejobbie
+
+; check against module title
+
+30
+        LDR     r11, [r10, #Module_Title]
+        ADD     r11, r10, r11
+        Push    r10
+        MOV     r10, #1                                 ; indicate only check for prefix_numeric
+        BL      LookForSwiName
+        Pull    r10
+        BVS     %BT10
+        B       gotmodulejobbie
+
+this_swi_nexiste_pas
+        SETV
+GotTheSWIName
+        PullEnv
+        ADRVS   r0, ErrorBlock_NoSuchSWI2
+      [ International
+        Push    "lr",VS
+        BLVS    TranslateError
+        Pull    "lr",VS
+      ]
+        B       SLVK_TestV
+
+        MakeErrorBlock NoSuchSWI2
+
+
+LookForSwiName ROUT
+; R11 points at name table
+; R1 points at name
+; R10 = 0 => allow only prefix_name (for OS_SWI)
+; R10 = 1 => allow only prefix_numeric (for checking moduletitle_numeric)
+;       otherwise => allow prefix_name or prefix_numeric
+; return R0 ORed with number if found
+; V set if not
+
+        Push    "r8,r9, r12, lr"
+        MOV     r12, #0                                 ; offset in name
+
+; first check that prefix matches
+10
+        LDRB    r14, [r1, r12]
+        CMP     r14, #" "                               ; if we terminate before we get an "_", then fail
+        BLE     %FT50
+        ADD     r12, r12, #1
+        LDRB    r9, [r11], #1
+        CMP     r9, r14
+        BEQ     %BT10
+
+        CMP     r14, #"_"                               ; check correct terminators
+        CMPEQ   r9,  #0
+        BNE     %FT50
+
+; prefix OK: scan table for rest of name
+
+        TEQ     r10, #1                                 ; if doing modulename_numeric
+        BEQ     CheckForNumericPostFix                  ; don't look for any names
+        MOV     r8, r12                                 ; keep pointer to after prefix
+20
+        LDRB    r14, [r11], #1
+        CMP     r14, #0
+        BEQ     CheckForNumericPostFix
+30
+        LDRB    r9, [r1, r12]
+        ADD     r12, r12, #1
+        CMP     r14, #0
+        CMPEQ   r9, #" "
+        Pull    "r8,r9, r12, lr", LE
+        BICLES  pc, lr, #V_bit
+
+        CMP     r9, r14
+        LDREQB  r14, [r11], #1
+        BEQ     %BT30
+
+        MOV     r12, r8                                 ; restore name pointer
+        ADD     r0, r0, #1                              ; step SWI number
+40
+        CMP     r14, #0                                 ; find end of failed name
+        LDRNEB  r14, [r11], #1
+        BNE     %BT40
+        B       %BT20
+
+50
+        Pull    "r8,r9, r12, lr"                        ; restore registers
+        ORRS    pc, lr, #V_bit                          ; and exit V set
+
+CheckForNumericPostFix
+; [R1, R12] points at postfix
+        Push    "r0-r2"
+        ADD     r1, r1, r12
+        CMP     r10, #0                                 ; if OS SWI then EQ,VC else NE,VC
+        SETV    EQ                                      ; if OS SWI then VS else VC
+        MOVVC   r0, #10 + (1 :SHL: 29)
+        MOVVC   r2, #Module_SWIChunkSize -1
+        SWIVC   XOS_ReadUnsigned
+        Pull    "r0"
+        BIC     r0, r0, #Module_SWIChunkSize -1
+        ADDVC   r0, r0, r2
+        Pull    "r1, r2, r8,r9, r12, pc"
+
+        END
diff --git a/s/Super1 b/s/Super1
new file mode 100644
index 0000000000000000000000000000000000000000..16e1a3038323e7a4afeadb8f65f419749c72e3f5
--- /dev/null
+++ b/s/Super1
@@ -0,0 +1,99 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL    => Super1
+
+CliDPrompt    =  "CLI$Prompt",0
+DefaultPrompt = "*"
+        ALIGN
+
+StartSuper ; Start entry for UtilModule
+        BL      DEFHAN                  ; set error handler in case spooling
+      [ International                   ; We are in USR mode and have no stack ...
+        ADR     R0,KernelMessagesBlock+4
+        ADR     R1,%FT11
+        MOV     R2,#0
+        SWI     XMessageTrans_Lookup
+        MOVVS   R2,R1
+01
+        LDRB    R0,[R2],#1
+        CMP     R0,#31
+        SWIGT   OS_WriteC
+        BGT     %BT01
+        SWI     OS_NewLine
+        SWI     OS_NewLine
+        B       CLILOP
+11
+        =       "Supervisor",0
+        ALIGN
+      |
+        SWI     OS_WriteS
+        =       "Supervisor",10,13,10,13,0
+        ALIGN
+        B       CLILOP
+      ]
+
+
+CLIEXIT BL      DEFHN2                  ; restore all our world
+
+GOSUPV  TEQP    PC, #0
+        BL      DEFHAN                  ; including error handler!
+
+CLILOP ROUT
+
+        ADR     R0, CliDPrompt          ; try looking it up
+        LDR     R1, =GeneralMOSBuffer
+        MOV     R2, #?GeneralMOSBuffer
+        MOV     R3, #0
+        MOV     R4, #VarType_Expanded
+        SWI     XOS_ReadVarVal
+        ADRVS   r1, DefaultPrompt       ; gnot gthere or gnaff
+        MOVVS   r2, #1
+        MOV     r0, r1
+        MOV     r1, r2
+        SWI     OS_WriteN
+        LDR     R0, =GeneralMOSBuffer
+        LDR     R1, =?GeneralMOSBuffer-1
+        MOV     R2, #" "
+        MOV     R3, #255
+        SWI     OS_ReadLine
+        BCS     ESCAPE
+        MOV     lr, pc                  ; construct lr for wallies to return to
+        SWI     XOS_CLI
+        BVC     CLILOP
+
+        SWI     XOS_NewLine
+        BL      PrintError
+        B       CLILOP
+
+        LTORG
+
+ESCAPE  MOV     R0, #&7E
+        SWI     OS_Byte                 ; May yield error
+      [ International
+        SWI     XOS_EnterOS             ; GO into SVC mode to get some stack
+        SWI     OS_NewLine
+        BLVC    WriteS_Translated
+        =       "Escape:Escape",10,13,0
+        ALIGN
+        TEQP    PC,#0                   ; Back to user mode.
+        MOV     R0,R0
+      |
+        SWI     OS_WriteS
+        =       10,13, "Escape", 10,13, 0
+        ALIGN
+      ]
+        B       CLILOP
+
+        END
diff --git a/s/SysComms b/s/SysComms
new file mode 100644
index 0000000000000000000000000000000000000000..4f1e180a6e9d8db38646fb6b67135a33b110da4d
--- /dev/null
+++ b/s/SysComms
@@ -0,0 +1,1725 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL     => SysComms , the system commands file: Load save dump etc.
+
+TAB      *      9
+FSUTtemp RN     r10
+
+         GBLS   UtilRegs        ; Save the same set so we can common up exits
+UtilRegs SETS   "r7-r10"        ; Sam will preserve r0-r6 on module star entry
+
+AnyNoParms  *   &FF0000         ; Between 0 and 255 parameters: all flags clear
+
+
+SysCommsModule ROUT
+
+Module_BaseAddr SETA SysCommsModule
+
+        &       0               ; No Start entry
+        &       0               ; Not initialised
+        &       0
+        &       0
+        &       0
+        &       SysTitle-SysCommsModule
+        &       SCHCTab-SysCommsModule
+        &       0
+        &       0
+        &       0
+        &       0
+ [ International_Help <> 0
+        &       MessageFileName-SysCommsModule
+ |
+        &       0
+ ]
+
+SysTitle
+        = "$SystemName", 9
+   [ :LEN: "$SystemName" < 8
+        =  9
+   ]
+        =  "$VersionNo", 0
+
+SCHCTab ; Alphabetically ordered so it's easier to find stuff
+
+        Command Append,  1,  1, International_Help
+        Command Build,   1,  1, International_Help
+        Command Close,   0,  0, International_Help
+        Command Create,  4,  1, International_Help
+        Command Delete,  1,  1, International_Help
+        Command Dump,    3,  1, International_Help
+        Command Exec,    1,  0, International_Help
+        Command FX,      5,  1, International_Help    ; 1-3 parms, but up to 2 commas may be there
+        Command GO,    255,  0, International_Help
+HelpText
+        Command Help,  255,  0, International_Help
+        Command Key,   255,  1, International_Help
+        Command Load,    2,  1, International_Help ; Fudge order for compatibility (*L.)
+        Command List,    3,  1, International_Help
+        Command Opt,     2,  0, International_Help
+        Command Print,   1,  1, International_Help
+        Command Quit,    0,  0, International_Help
+        Command Remove,  1,  1, International_Help
+        Command Save,    6,  2, International_Help ; *SAVE Fn St + Le Ex Lo (compatibility)
+        Command Shadow,  1,  0, International_Help
+        Command Spool,   1,  0, International_Help
+        Command SpoolOn, 1,  0, International_Help
+        Command TV,      3,  0, International_Help
+        Command Type,    3,  1, International_Help ; -file fred -tabexpand
+        =       0
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Most help and syntax messages go together to save ALIGNing wastage
+
+        GET       s.MosDict
+        GET       s.TMOSHelp
+
+GO_Syntax   * Module_BaseAddr
+Help_Syntax * Module_BaseAddr
+
+        ALIGN                   ; Just the one, please !
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+HelpBufferSize * 512
+
+Help_Code ROUT       ; got R0 ptr to commtail, R1 no parameters
+        Push    "r7, lr"
+
+; first pass round a service call to let wally user code do things.
+        MOV     r2, r1
+        MOV     r1, #Service_Help
+        BL      Issue_Service
+        CMP     r1, #0
+        Pull    "r7, pc", EQ
+
+        CMP     r2, #0
+        MOVNE   r6, r0
+        addr    r6, HelpText, EQ
+
+        MOV     r0, #117                ; Read current VDU status
+        SWI     XOS_Byte                ; Won't fail
+        SWI     XOS_WriteI+14           ; paged mode on.
+        Pull    "r7, pc", VS            ; Wrch can fail
+
+        Push    "r1"                    ; Save page mode state
+        MOV     r0, r6
+        MOV     r7, #0                  ; anyhelpdoneyet flag
+
+DoHelpOnNextKeyWord
+; now look at syscomms module.
+        addr    r1, SysCommsModule
+        BL      ShowHelpInModule
+        BVS     %FT67
+
+; now try looking round the modules.
+        MOV      R2, #Module_List
+11      LDR      R2, [R2]
+        CMP      R2, #0
+        BEQ      tryagainstmodulename
+        LDR      R1, [R2, #Module_code_pointer]
+        LDR      R12, [R2, #Module_incarnation_list]
+        ADD      R12, R12, #Incarnation_Workspace
+        BL       ShowHelpInModule
+        BVS      %FT67
+        B        %BT11
+
+tryagainstmodulename
+        Push     "r0"
+        MOV       r1, #0
+        MOV       r2, #0
+tamn_loop
+        MOV       r0, #ModHandReason_GetNames
+        SWI       XOS_Module
+        Pull      r0, VS
+        BVS       %FT02
+        LDR       r0, [stack]      ; kword ptr
+        Push     "r1, r2"
+        LDR       r4, [r3, #Module_Title]
+        CMP       r4, #0
+        BEQ       tamn_notit
+        ADD       r4, r4, r3
+        MOV       R5, #0           ; offset
+tamn_chk
+        LDRB      R1, [R0, R5]
+        LDRB      R2, [R4, R5]
+        CMP       R1, #32
+        CMPLE     R2, #32
+        BLE       tamn_dojacko    ; matched at terminator
+        UpperCase R1, R6
+        UpperCase R2, R6
+        CMP       R1, R2
+        ADDEQ     R5, R5, #1
+        BEQ       tamn_chk
+        RSBS      R2, R2, #33     ; only if not terminated
+        CMPLE     R1, #"."        ; success if abbreviation
+        BNE       tamn_notit
+tamn_dojacko
+        BL        ModuleJackanory
+        STRVS     r0, [stack, #2*4]
+tamn_notit
+        Pull     "r1, r2"
+        Pull      r0, VS
+        BVS       %FT67
+        CMP       r2, #0
+        ADDNE     r1, r1, #1
+        MOVNE     r2, #0
+        B         tamn_loop
+
+02      LDRB      R1, [R0], #1
+        CMP       R1, #"."
+        CMPNE     R1, #" "
+        BGT       %BT02
+        CMP       R1, #" "
+        BLT       %FT67
+66      LDRB      R1, [R0], #1
+        CMP       R1, #" "
+        BEQ       %BT66
+        SUBGT     R0, R0, #1
+        BGT       DoHelpOnNextKeyWord
+67      BLVC      testnohelpdone
+        Pull     "R1"
+        MOV       r6, pc
+        TST       R1, #5
+        SWIEQ     XOS_WriteI+15  ; paged mode off
+        TEQP      PC, r6         ; restore V state
+        MOVNV     r0, r0
+        Pull     "r7, PC"
+
+testnohelpdone
+        CMP       r7, #0
+        MOVNES    pc, lr
+        MOV       r7, lr
+      [ International
+        BL        WriteS_Translated
+        =         "NoHelp:No help found.",10,13,0
+        ALIGN
+      |
+        SWI       XOS_WriteS
+        =         "No help found.",10,13,0
+        ALIGN
+      ]
+        MOV       pc, r7
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ModuleJackanory   ROUT      ; give summary of info in module @ r3
+        Push     "r0, lr"
+        CheckSpaceOnStack HelpBufferSize+256, modjack_noroom, r6
+        SUB       stack, stack, #HelpBufferSize
+        MOV       r2, r3
+        LDR       r3, [r2, #Module_Title]
+        CMP       r3, #0
+        ADDNE     r3, r3, r2
+        ADREQL    r3, NoRIT
+        BL        PrintMatch
+        BVS       %FT99
+        LDR       r3, [r2, #Module_HelpStr]
+        CMP       r3, #0
+        BEQ       nohstring
+        STMDB     sp!, {r2, r3}
+        MOV       r0, #0                        ; Try our message file before Global.
+        LDR       r0, [r0, #KernelMessagesBlock]
+        TEQ       r0, #0
+        ADRNE     r0, KernelMessagesBlock+4
+        ADRL      r1, modjack_hstr
+        MOV       r2, #0
+        SWI       XMessageTrans_Lookup
+        SWIVC     XMessageTrans_Dictionary
+        MOVVC     r1, r0
+        MOVVC     r0, r2
+        SWIVC     XOS_PrettyPrint
+        SWIVC     XOS_WriteI + 32
+        LDMIA     sp!, {r2, r3}
+        ADDVC     r0, r2, r3
+        SWIVC     XOS_PrettyPrint
+        BVS       %FT99
+nohstring
+        MOV       r3, stack                ; buffer address
+        MOV       r1, #0                   ; flags for commands
+        ADRL      r0, modjack_comms
+        BL        OneModuleK_Lookup
+        MOVVC     r1, #FS_Command_Flag
+        ADRVCL    r0, modjack_filecomms
+        BLVC      OneModuleK_Lookup
+        MOVVC     r1, #Status_Keyword_Flag
+        ADRVCL    r0, modjack_confs
+        BLVC      OneModuleK_Lookup
+        MOVVC     r1, #-1
+        ADRVCL    r0, modjack_aob
+        BLVC      OneModuleK_Lookup
+99      ADD       stack, stack, #HelpBufferSize
+        SWIVC     XOS_NewLine
+98      STRVS     r0, [stack]
+        Pull     "r0, PC"
+modjack_noroom
+        ADRL      r0, ErrorBlock_StackFull
+      [ International
+        BL        TranslateError
+      |
+        SETV
+      ]
+        B         %BT98
+
+OneModuleK_Lookup
+        STMDB   sp!, {r1-r3, lr}
+        MOV     r1, r0
+        MOV     r0, #0                          ; Try our message file before Global.
+        LDR     r0, [r0, #KernelMessagesBlock]
+        TEQ     r0, #0
+        ADRNE   r0, KernelMessagesBlock+4
+        MOV     r2, #0
+        SWI     XMessageTrans_Lookup
+        MOVVC   r0, r2
+        LDMIA   sp!, {r1-r3, lr}
+        MOVVS   pc, lr
+        B       OneModuleK
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Print match header, keyword @ r3
+
+PrintMatch      ROUT
+      [ International
+        Push   "r0-r4, lr"
+        SWI     XOS_ReadEscapeState
+        BLCS    AckEscape
+
+        SWI     XOS_WriteI+CR
+        MOVVC   r4,r3
+        BL      WriteS_Translated_UseR4
+        =       "HelpFound:==> Help on keyword %0",0
+        ALIGN
+        SWIVC   XOS_NewLine
+        STRVS   R0, [stack]
+        MOV     R7, #1
+        Pull   "r0-r4, PC"
+      |
+        Push   "r0-r2, lr"
+        SWI     XOS_ReadEscapeState
+        BLCS    AckEscape
+        ADRVC   r0, HelpMatchString
+        MOV     r2, r3
+        SWIVC   XOS_PrettyPrint    ; print matched keyword
+        SWIVC   XOS_NewLine
+        STRVS   R0, [stack]
+        MOV     R7, #1
+        Pull   "r0-r2, PC"
+
+HelpMatchString =  CR, "==> Help on keyword ",TokenEscapeChar,Token0, 0
+        ALIGN
+      ]
+
+;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ShowHelpInModule  ROUT    ; take module ptr in R1, give relevant help.
+
+        Push   "R2-R6, lr"
+
+        LDR     R2, [R1, #Module_HC_Table]
+        CMP     R2, #0
+        Pull   "R2-R6, PC", EQ
+
+        ADD     R2, R1, R2        ; point at table
+fujjnulltables
+        LDRB    R5, [R2]
+        CMP     R5, #0
+        BNE     %FT21
+        Pull   "R2-R6, PC"       ; finished
+
+21      MOV     R3, #0           ; offset
+22      LDRB    R4, [R0, R3]
+        LDRB    R5, [R2, R3]
+        CMP     R4, #32
+        CMPLE   R5, #32
+        BLE     %FT25           ; matched at terminator
+        UpperCase R4, R6
+        UpperCase R5, R6
+        CMP     R4, R5
+        ADDEQ   R3, R3, #1
+        BEQ     %BT22
+        RSBS    R5, R5, #33       ; only if not terminated
+        CMPLE   R4, #"."          ; success if abbreviation
+        BEQ     %FT25
+        ADD     R2, R2, R3
+23      LDRB    R5, [R2], #1
+        CMP     R5, #32
+        BGT     %BT23             ; skip to terminator
+        ADD     R2, R2, #3
+        BIC     R2, R2, #3        ; ALIGN
+24      ADD     R2, R2, #16
+        B       fujjnulltables
+
+25      ADD     R2, R2, R3
+        SUB     R3, R2, R3        ; hang on to keyword ptr
+28      LDRB    R5, [R2], #1
+        CMP     R5, #0
+        BNE     %BT28           ; demand null terminator
+        ADD     R2, R2, #3
+        BIC     R2, R2, #3        ; ALIGN
+
+        LDR     R5, [R2, #12]    ; get help offset
+        CMP     R5, #0
+        BEQ     %BT24          ; no help.
+
+        BL      PrintMatch           ; r3 -> keyword
+        Pull   "R2-R6, PC", VS
+
+        LDR     R6, [R2, #4]     ; get info word
+        TST     R6, #Help_Is_Code_Flag
+        BNE     CallHelpKeywordCode
+
+        Push   "R0-r3"
+        TST     R6, #International_Help
+        BEQ     %FT35
+        SUB     sp, sp, #16
+        LDR     r2, [r1, #-4]
+        LDR     r0, [r1, #Module_MsgFile]
+        TST     r0, #12,2
+        CMPEQ   r0, #1
+        CMPCS   r2, r0
+        MOVLS   r0, #0
+        BLS     %FT29
+        ADD     r1, r1, r0
+        MOV     r2, #0
+        MOV     r0, sp
+        SWI     XMessageTrans_OpenFile
+        MOVVS   r0, #0
+29      MOV     r6, r0
+        LDR     r1, [sp, #16 + 1 * 4]
+        ADD     r1, r5, r1
+        MOV     r2, #0
+        SWI     XMessageTrans_Lookup
+        ADDVS   r2, r0, #4
+        SWI     XMessageTrans_Dictionary
+        MOVVS   r0, #0
+        MOV     r1, r0
+        MOV     r0, r2
+        LDR     r2, [sp, #16 + 3 * 4]
+        SWI     XOS_PrettyPrint         ; Error check not done yet
+        SWI     XOS_NewLine
+        MOV     r0, r6
+        LDR     r2, [sp, #16 + 2 * 4]
+        LDR     R5, [r2, #8]
+        CMP     R5, #0
+        BEQ     %FT30                   ; Should print default message?
+        LDR     r1, [sp, #16 + 1 * 4]
+        ADD     r1, r5, r1
+        MOV     r2, #0
+        SWI     XMessageTrans_Lookup
+        ADDVS   r2, r0, #4
+        SWI     XMessageTrans_Dictionary
+        MOVVS   r0, #0
+        MOV     r1, r0
+        MOV     r0, r2
+        LDR     r2, [sp, #16 + 3 * 4]
+        SWI     XOS_PrettyPrint         ; No Error check!!!
+        SWI     XOS_NewLine
+30      MOVS    r0, r6
+        SWINE   XMessageTrans_CloseFile
+        ADD     sp, sp, #16
+        Pull    "R0-R3"
+        B       %BT24
+
+35      ADD     R0, R5, R1
+        MOV     r1, #0
+        MOV     r2, r3
+        SWI     XOS_PrettyPrint
+        SWIVC   XOS_NewLine
+        STRVS   R0, [stack]
+        Pull   "R0-r3"
+        Pull   "R2-R6, PC", VS
+        B       %BT24
+
+helpnostack
+        ADRL    R0, ErrorBlock_StackFull
+      [ International
+        BL      TranslateError
+      ]
+        Pull   "R2-R6, lr"
+        ORRS    PC, lr, #V_bit
+
+CallHelpKeywordCode
+        CheckSpaceOnStack HelpBufferSize+256, helpnostack, R6
+        SUB     stack, stack, #HelpBufferSize
+        Push   "R0-R2, R12"     ; code allowed to corrupt R1-R6, R12
+        MOV     R2, R1
+        ADD     R0, stack, #4*4
+        MOV     R1, #HelpBufferSize
+        MOV     lr, PC          ; R12 set by our caller
+        ADD     PC, R5, R2
+        BVS     hkc_threwawobbly
+        CMP     R0, #0
+        MOVNE   r1, #0
+        SWINE   XOS_PrettyPrint
+        SWIVC   XOS_NewLine
+hkc_threwawobbly
+        STRVS   R0, [stack]
+        Pull   "R0-R2, R12"
+        ADD     stack, stack, #HelpBufferSize
+        BVC     %BT24
+        Pull   "R2-R6, PC"
+
+;**************************************************************************
+
+GO_Code ROUT
+        Push  "R7, lr"
+        MOV    R4, R0
+        BL     SPACES
+        CMP    R5, #13
+        TEQNE  R5, #";"
+        MOVLS  R7, #UserMemStart
+        BCC    GOEX
+        BEQ    GOEX0
+        BL     ReadHex
+        MOVVS  R7, #UserMemStart
+        BL     SPACES
+GOEX0   TEQ    R5, #";"
+        BLEQ   SPACES
+GOEX    SUB    R1, R4, #1
+        MOV    R0, #FSControl_StartApplication
+        MOV    R2, R7
+        ADR    R3, GOSMUDGE
+        SWI    XOS_FSControl
+        Pull  "R7, PC", VS
+
+        LDR    sp_svc, =SVCSTK ; remove supervisor stuff
+        BICS   PC, R7, #ARM_CC_Mask
+
+GOSMUDGE = "GO ", 0
+         ALIGN
+
+ReadHex Push    "R0-R2, lr"
+        MOV      R0, #16
+        SUB      R1, R4, #1
+        SWI      XOS_ReadUnsigned
+        MOV      R4, R1
+        MOV      R7, R2
+        Pull    "R0-R2, PC"
+
+SPACES  LDRB R5, [R4], #1
+        TEQ R5, #" "
+        BEQ SPACES
+        MOV PC, R14
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Close_Code ENTRY
+
+        MOV    R0, #0
+        MOV    R1, #0
+        SWI    XOS_Find
+        EXIT
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+FX_Code ENTRY
+
+        MOV     R1, #0
+        MOV     R2, #0
+        Push    "R1-R2"         ; Put optional parms (default 0, 0) on stack
+        MOV     R1, R0
+        MOV     R0, #10
+        SWI     XOS_ReadUnsigned
+        Push    "R2"            ; Pulled as R0 when OSByte called
+        BVS     %FT90
+        BL      %FT10
+
+TV_EntryPoint
+
+        MOV     R0, #10
+        SWI     XOS_ReadUnsigned
+        BVS     %FT90
+        STR     R2, [stack, #4]
+        BL      %FT10
+        MOV     R0, #10
+        SWI     XOS_ReadUnsigned
+        BVS     %FT90
+        STR     R2, [stack, #8]
+
+        BL      %FT10                ; check for EOL: goes to 05 if end
+        ADR     R0, ErrorBlock_TooManyParms
+      [ International
+        BL      TranslateError
+      |
+        SETV
+      ]
+90      ADD     sp, sp, #12    ; Error before we'd pulled r0-r2 for osbyte
+        EXIT
+
+        MakeErrorBlock  TooManyParms
+
+05
+        Pull    "R0-R2"
+        SWI     XOS_Byte
+        EXIT
+
+; Skip leading spaces and optional comma
+
+10      LDRB    R2, [R1], #1
+        CMP     R2, #" "
+        BEQ     %BT10
+        CMP     R2, #","
+        MOVEQ   PC, lr         ; Let ReadUnsigned strip leading spaces.
+        CMP     R2, #CR
+        CMPNE   R2, #LF
+        CMPNE   R2, #0
+        BEQ     %BT05          ; Terminated command, so execute the osbyte
+        SUB     R1, R1, #1     ; Back to first char
+        MOV     PC, lr
+
+
+TV_Code ALTENTRY ; must be same as FX_Code !
+
+        MOV     R1, #144        ; OSBYTE number
+        MOV     R2, #0
+        MOV     R14, #0
+        Push   "R1, R2, lr"
+        MOV     R1, R0
+        B       TV_EntryPoint
+
+
+Shadow_Code ENTRY
+
+       CMP      R1, #0
+       MOV      R1, R0
+       MOV      R0, #10 + (1:SHL:30)
+       SWINE    XOS_ReadUnsigned
+       MOVEQ    R2, #0
+       EXIT     VS
+       BL       CheckEOL
+       BNE      ShadowNaff
+       MOV      R0, #114
+       MOV      R1, R2
+       SWI      XOS_Byte
+       EXIT
+
+ShadowNaff
+       ADRL     R0, ErrorBlock_BadNumb
+     [ International
+       BL       TranslateError
+     |
+       SETV
+     ]
+       EXIT
+
+CheckEOL
+       LDRB     R0, [R1], #1
+       CMP      R0, #" "
+       CMPNE    R0, #13
+       CMPNE    R0, #10
+       CMPNE    R0, #0
+       MOV      PC, lr
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Key_Code ENTRY
+
+        SUB     sp, sp, #32
+        MOV     R1, R0
+        MOV     r6, sp
+        LDR     R2, %FT02               ; Load 'Key$'
+        STR     R2, [R6], #4
+        MOV     R0, #10 + (1 :SHL: 29)  ; default base
+        MOV     R2, #15                 ; maximum key
+        SWI     XOS_ReadUnsigned
+        BVS     %FT90
+        CMP     R2, #10
+        MOVGE   R4, #"1"
+        STRGEB  R4, [R6], #1
+        SUBGE   R2, R2, #10
+        ADD     R2, R2, #"0"
+        STRB    R2, [R6]
+        MOV     R2, #0
+        STRB    R2, [R6, #1]
+        MOV     R0, sp
+        MOV     R3, #0
+        MOV     R4, #VarType_String
+        SWI     XOS_SetVarVal
+
+80      ADD     sp, sp, #32
+        EXIT
+
+90      ADR     r0, ErrorBlock_BadKey
+      [ International
+        BL      TranslateError
+      |
+        SETV    ; It needs setting here !
+      ]
+        B       %BT80
+
+        MakeErrorBlock BadKey
+02
+        DCB     "Key$"                  ; Must be aligned
+        ALIGN
+
+        LTORG
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Exec, Spool and SpoolOn share a common body
+
+Exec_Code ENTRY "$UtilRegs"
+
+        MOV     r3, #&40        ; OPENIN exec
+        MOV     r4, #198        ; r0b for osbyte exec
+        B       %FT01
+
+
+Spool_Code ALTENTRY
+
+        MOV     r3, #&80        ; OPENOUT spool
+        B       %FT00
+
+
+SpoolOn_Code ALTENTRY
+
+        MOV     r3, #&C0        ; OPENUP spoolon
+
+00      MOV     r4, #199        ; r0b for osbyte spool/spoolon
+
+01      MOV     r5, r0          ; Save filename^
+        MOV     r6, r1          ; Save n parms
+
+        MOV     r0, r4          ; Read old exec/spool handle
+        MOV     r1, #0          ; Write 0 as handle; we may be just closing
+        MOV     r2, #0          ; and keep zero if open error (cf. RUN)
+        SWI     XOS_Byte        ; Won't cause error
+        BL      CloseR1
+        EXIT    VS
+
+        CMP     r6, #0          ; No filename present ?
+        EXIT    EQ              ; ie. just closing down exec/spool ?
+
+        MOV     r1, r5          ; -> filename
+        MOV     r0, r3          ; OPENIN exec/OPENUP spoolon/OPENOUT spool
+        BL      OpenFileWithWinge
+        EXIT    VS
+
+        CMP     r3, #&C0        ; Doing SPOOLON ? VClear
+        BLEQ    MoveToEOF       ; PTR#r1:= EXT#r1
+
+        MOV     r0, r4          ; Write new exec/spool handle (r1)
+        MOV     r2, #0
+        SWIVC   XOS_Byte        ; May have got error in moving to EOF
+        EXIT
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; List, Print and Type share a common body
+
+listopt   RN    r2
+lastchar  RN    r3
+linecount RN    r4
+charcount RN    r5
+
+lnum      *     2_01    ; Line numbers
+type      *     2_10    ; GSREAD format ?
+
+filterprinting  * 2_10000000            ; Bits controlling printing
+linenumbering   * 2_01000000
+expandtabs      * 2_00100000
+
+forcetbsrange   * 2_00001000
+allowtbschar    * 2_00000100
+unprintangle    * 2_00000010
+unprinthex      * 2_00000001
+unprintdot      * 2_00000001
+
+Print_Code ENTRY "$UtilRegs"
+
+        MOV     listopt, #0             ; No line numbers, raw ASCII
+        B       %FT01
+
+
+List_Code ALTENTRY
+
+        MOV     listopt, #(filterprinting + linenumbering)
+        MOV     linecount, #0
+        B       %FT00
+
+
+TypeArgs = "File/A,TabExpand/S",0
+        ALIGN
+
+Type_Code ALTENTRY
+
+        MOV     listopt, #filterprinting
+
+00      MOV     r6, r1                  ; no. params
+        BL      ReadGSFormat            ; Read configured GSFormat bits
+        EXIT    VS                      ; I2C could be faulty ! File not open
+        ORR     listopt, listopt, r1
+
+        CMP     r6, #1
+        BEQ     %FT01
+        Push   "R2, R3"
+        MOV     R1, R0                  ; args given
+        ADR     R0, TypeArgs
+        LDR     R2, =ArgumentBuffer
+        MOV     R3, #256
+        SWI     XOS_ReadArgs
+        Pull   "R2, R3", VS
+        EXIT    VS
+        LDR     R0, [R2, #4]            ; tabflag
+        CMP     R0, #0
+        LDR     R0, [R2, #0]            ; filename ptr
+        Pull   "R2, R3"
+        ORRNE   listopt, listopt, #expandtabs
+
+01      MOV     lastchar, #0            ; Reset NewLine indicator
+
+        MOV     r1, r0                  ; Point to filename
+        MOV     r0, #&40                ; han := OPENIN <filename>
+        BL      OpenFileWithWinge
+        EXIT    VS
+
+10      SWI     XOS_ReadEscapeState     ; Won't cause error
+        BCS     CloseThenAckEscape
+
+        MOV     charcount, #0           ; No characters printed this line
+
+        SWI     XOS_BGet                ; Get first character of line
+        BVS     UtilityExitCloseR1
+        BCS     UtilityExitCloseR1      ; EOF ?
+
+        TST     listopt, #linenumbering ; Do we want to print the line number ?
+        BLNE    LineNumberPrint
+        BVS     UtilityExitCloseR1
+
+; Doing ASCII (print) or GSREAD (type, list) ?
+
+30      TST     listopt, #filterprinting
+        BEQ     %FT35
+
+; GSREAD format printing
+
+        CMP     r0, #CR                 ; CR and LF both line terminators
+        CMPNE   r0, #LF
+        BEQ     %FT70
+
+        CMP     r0, #TAB
+        BNE     %FT31
+        TST     listopt, #expandtabs
+        BEQ     %FT31
+
+; simple tab expansion: 8 spaces
+        SWI     XOS_WriteS
+        =      "        ",0
+        ALIGN
+        B       %FT32
+
+31      MOV     lastchar, r0
+        CMP     r0, #""""               ; Don't display quotes in GSREAD though
+        CMPNE   r0, #"<"                ; Or left angle
+        BEQ     %FT35
+        BL      PrintCharInGSFormat
+
+32      BVC     %FT40
+
+35      SWIVC   XOS_WriteC
+        BVS     UtilityExitCloseR1
+        ADD     charcount, charcount, #1
+
+40      SWI     XOS_ReadEscapeState     ; Won't cause error
+        BCS     CloseThenAckEscape
+
+        SWI     XOS_BGet                ; Get another character on this line
+        BVS     UtilityExitCloseR1
+        BCC     %BT30                   ; Loop if not EOF
+
+50      TST     listopt, #filterprinting
+        SWINE   XOS_NewLine             ; Terminate with NewLine if not *Print
+        B       UtilityExitCloseR1
+
+
+; Hit LF or CR in GSFormat mode, decide whether to give a NewLine or not
+
+70      CMP     r0, lastchar            ; NewLine if same as last time eg LF,LF
+        SWIEQ   XOS_NewLine
+        BVS     UtilityExitCloseR1
+        BEQ     %BT10                   ; Loop back and do another line
+
+        CMP     lastchar, #CR           ; Don't give another NewLine if we've
+        CMPNE   lastchar, #LF           ; had CR, LF or LF, CR
+        MOVEQ   lastchar, #0            ; Reset NewLine indicator so more will
+        BEQ     %BT40                   ; Loop and do more chars in this line
+
+        MOV     lastchar, r0            ; Save char forcing this NewLine
+        SWI     XOS_NewLine             ; Do NewLine then another line
+        BVC     %BT10
+        B       UtilityExitCloseR1
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Dump_Code ENTRY "$UtilRegs"
+
+        MOV     r7, r1                  ; Remember nparms
+        MOV     r8, r0                  ; Remember filename^ for below
+        MOV     r1, r0                  ; -> filename
+
+        MOV     r0, #&40                ; han := OPENIN <filename>
+        BL      OpenFileWithWinge
+        EXIT    VS                      ; File not yet open
+        MOV     r9, r1                  ; Save handle
+
+        MOV     r0, #OSFile_ReadInfo    ; Read file info
+        MOV     r1, r8                  ; Restore filename^
+        SWI     XOS_File                ; Must exist and be a file now
+        MOV     r1, r9                  ; Restore handle in case of error/exit
+        BVS     UtilityExitCloseR1
+
+        CMP     r4, #0                  ; Zero length ?
+        BEQ     UtilityExitCloseR1      ; Nothing to do then ! VClear
+
+        MOV     r5, r9                  ; Standard place for handle below
+
+; Default display at load addr of file (r2) unless special
+
+        BL      ReadGSFormat            ; Only interested in some bits
+        BVS     UtilityExitCloseR1
+        AND     r10, r1, #forcetbsrange + allowtbschar
+
+        CMP     r2, #0                  ; Command file ?
+        CMPEQ   r3, #-1
+        BEQ     %FT04
+
+        MOV     r14, r2, ASR #(32-12)   ; Date stamped file ?
+        CMP     r14, #-1                ; &FFFtttdd
+04      MOVEQ   r2, #0                  ; Display start = 0
+
+        MOV     r3, #0                  ; Default PTR# = 0
+                                        ; Funny order 'cos of ReadOptLoadAndExe
+
+        CMP     r7, #1                  ; Only filename specified ?
+        BEQ     %FA10                   ; If so, use loadaddr and ptr=0
+
+        MOV     r1, r8                  ; Get back filename^
+        BL      SkipToSpace             ; Over the filename
+        BL      ReadOptionalLoadAndExec ; Abuse ! r3 := start, r2 := disp start
+        MOVVS   r1, r5
+        BVS     UtilityExitCloseR1
+
+        ADD     r2, r3, r2              ; Display offset = disparm/loadaddr+ptr
+
+10      Swap    r2, r3                  ; r2 := start, r3 := disp start
+        CMP     r2, r4                  ; Is ptr > ext ? VClear
+        MOVHS   r1, r5
+        BLHS    SetErrorOutsideFile
+
+        MOVVC   r0, #OSArgs_SetPTR
+        MOVVC   r1, r5
+        SWIVC   XOS_Args                ; PTR#r1 := start offset
+        MOVVC   r7, #0
+
+        BLVC    ReadWindowWidth
+        BVS     UtilityExitCloseR1
+
+        SUB     r9, r0, #12+1           ; -ExtraFields (address and separators)
+        MOV     r9, r9, LSR #2   ; Two nibbles, one space and one char per byte
+
+        Push    r10                     ; Save format bits
+        MOV     r10, #(1 :SHL: 31)      ; Move 1 bit down until not zero
+12      MOVS    r10, r10, LSR #1
+        MOVEQ   r9, #1                  ; Always 1 byte per line at least
+        BEQ     %FT15
+        TST     r10, r9                 ; Mask against r9 (byte count)
+        BEQ     %BT12                   ; Not hit yet ?
+
+        AND     r9, r9, r10, LSR #1     ; Take the bit one lower down
+        ORR     r9, r9, r10             ; And the bit we matched. Yowzay !
+
+15      MOV     r1, r5                  ; Get handle back
+        Pull    r10                     ; Get format back
+
+
+        SUB     sp, sp, #256            ; Need temp frame now
+
+; Main Dump loop
+
+30      SWI     XOS_ReadEscapeState     ; Won't cause error
+        ADDCS   sp, sp, #256
+        BCS     CloseThenAckEscape
+
+; Get line of data (r9 bytes). Keep track of how many bytes were read in r4
+
+        MOV     r2, sp                  ; Temp buffer
+        MOV     r4, #0
+35      SWI     XOS_BGet                ; Fall out of loop if EOF
+        BVS     UtilityExitCloseR1_256
+        STRCCB  r0, [r2, r4]
+        ADDCC   r4, r4, #1
+        CMPCC   r4, r9
+        BCC     %BT35
+
+        CMP     r4, #0                  ; No bytes to do this line ?
+        BEQ     UtilityExitCloseR1_256
+
+; Must preserve r4 till end for testing
+
+        ANDS    r7, r7, #15             ; Print title every 16 lines of data
+        BNE     %FT54
+
+
+      [ International
+        SWI     XOS_NewLine
+        BL      WriteS_Translated
+        DCB     "Address:Address  :",0
+        ALIGN
+      |
+        SWI     XOS_WriteS              ; Print title start
+        DCB     LF, CR
+        DCB     "Address  :", 0
+        ALIGN
+      ]
+        BVS     UtilityExitCloseR1_256
+
+        MOV     r8, #0
+50      ADD     r0, r3, r8              ; Print byte == LSB of
+        SWI     XOS_WriteI+" "          ; display across page
+        BLVC    HexR0Byte
+        BVS     UtilityExitCloseR1_256
+        ADD     r8, r8, #1
+        CMP     r8, r9
+        BNE     %BT50
+
+        CMP     r9, #11                 ; No room to print title end ?
+        BLO     %FT52                   ; VClear
+
+        SWI     XOS_WriteS
+        DCB     " : ", 0
+        ALIGN
+        BVS     UtilityExitCloseR1_256
+
+        SUB     r8, r9, #10             ; Centre 'ASCII data' over data
+        MOVS    r8, r8, LSR #1
+51      SUBS    r8, r8,#1               ; VClear
+        SWI     XOS_WriteI+" "
+        BVS     UtilityExitCloseR1_256
+        BPL     %BT51
+
+      [ International
+        BL      WriteS_Translated
+        DCB     "ASCII:ASCII data",0
+        ALIGN
+      |
+        SWI     XOS_WriteS
+        DCB     "ASCII data", 0
+        ALIGN
+      ]
+
+52      SWIVC   XOS_NewLine
+        SWIVC   XOS_NewLine
+
+54      MOVVC   r0, r3                  ; Print start of line address
+        BLVC    HexR0LongWord
+        SWIVC   XOS_WriteI+" "
+        SWIVC   XOS_WriteI+":"
+        BVS     UtilityExitCloseR1_256
+
+; Print line of data in hex
+
+        MOV     r5, #0
+55      LDRB    r0, [r2, r5]
+        SWI     XOS_WriteI+" "
+        BVS     UtilityExitCloseR1_256
+        CMP     r5, r4                  ; Byte valid ?
+        SWICS   XOS_WriteI+" "
+        BVS     UtilityExitCloseR1_256
+        SWICS   XOS_WriteI+" "
+        BVS     UtilityExitCloseR1_256
+        BLCC    HexR0Byte               ; Alters C, so do last
+        BVS     UtilityExitCloseR1_256
+        ADD     r5, r5, #1
+        CMP     r5, r9
+        BCC     %BT55
+
+        SWI     XOS_WriteS
+        DCB     " : ", 0
+        ALIGN
+        BVS     UtilityExitCloseR1_256
+
+; Print line of data in ASCII
+
+        MOV     r5, #0
+65      LDRB    r0, [r2, r5]
+        TST     r10, #forcetbsrange     ; Forcing into 00..7F ?
+        BICNE   r0, r0, #&80
+        TST     r10, #allowtbschar
+        BEQ     %FT66
+        CMP     r0, #&80                ; Print tbs unmolested
+        BHS     %FT67
+66      CMP     r0, #" "                ; Space through twiddle are valid
+        RSBGES  r14, r0, #&7E
+        MOVLT   r0, #"."
+67      SWI     XOS_WriteC
+        BVS     UtilityExitCloseR1_256
+        ADD     r5, r5, #1
+        CMP     r5, r4
+        BCC     %BT65
+
+        SWI     XOS_NewLine
+        BVS     UtilityExitCloseR1_256
+        ADD     r7, r7, #1              ; Increment line count
+        ADD     r3, r3, r9              ; Increment display address by r9 bytes
+        CMP     r4, r9                  ; Loop till we couldn't fill a line
+        BEQ     %BT30
+
+; .............................................................................
+
+UtilityExitCloseR1_256
+
+        ADD     sp, sp, #256            ; Kill temp frame
+
+; .............................................................................
+
+UtilityExitCloseR1
+
+        BL      CloseR1                 ; Accumulates V
+
+        Pull    "$UtilRegs, pc"         ; Back to *Command handler
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Append and Build share a common body
+
+Append_Code ENTRY "$UtilRegs"
+
+        MOV     r1, r0          ; -> filename
+        MOV     r0, #&C0        ; OPENUP
+        B       %FT01
+
+
+Build_Code ALTENTRY
+
+        MOV     r1, r0          ; -> filename
+        MOV     r0, #&80        ; OPENOUT
+
+01      BL      OpenFileWithWinge
+        EXIT    VS
+
+        SUB     sp, sp, #256
+
+        MOV     r5, r1          ; Save handle for later
+        BL      MoveToEOF       ; Can do this anyhow as ext#(openout)=0
+
+        MOV     linecount, #0
+
+10      BLVC    LineNumberPrint ; Escape is tested for on OS_ReadLine.Err below
+        BVS     UtilityExitCloseR1_256
+
+        MOV     r0, sp          ; Get a line from Joe Punter
+        MOV     r1, #256-1      ; -1 for terminator
+        MOV     r2, #" "
+        MOV     r3, #&FF
+        SWI     XOS_ReadLine
+        MOVVS   r1, r5
+        BVS     UtilityExitCloseR1_256
+        MOV     r3, #CR                 ; Terminate source string
+        STRB    r3, [r0, r1]
+        MOVCC   FSUTtemp, #0            ; Ended with ESCAPE ?
+        MOVCS   FSUTtemp, #-1
+        MOVCS   r0, #&7E                ; Ack. ESCAPE, not error
+        SWICS   XOS_Byte
+        BVS     UtilityExitCloseR1_256
+
+        MOV     r2, sp                  ; r2 -> buffer to translate
+        MOV     r1, r5                  ; Get handle back
+18      LDRB    r0, [r2], #1            ; Put all the spaces out ourselves !
+        CMP     r0, #" "                ; VClear
+        SWIEQ   XOS_BPut
+        BVS     UtilityExitCloseR1_256
+        BEQ     %BT18
+
+        SUB     r0, r2, #1         ; r0 -> past spaces, rest to translate
+        MOV     r2, #(1 :SHL: 31)  ; No quote funnies, don't end on space, do |
+        SWI     XOS_GSInit
+
+20      SWIVC   XOS_GSRead              ; Get a char
+        MOVVS   R1, R5
+        BVS     UtilityExitCloseR1_256
+        BCS     %FT30                   ; End of string ?
+        MOV     r3, r0                  ; Save GSState
+        MOV     r0, r1                  ; Char from GSRead
+        MOV     r1, r5                  ; Get handle back
+        SWI     XOS_BPut
+        BVS     UtilityExitCloseR1_256
+        MOV     r0, r3                  ; Restore GSState
+        B       %BT20                   ; And loop
+
+30      CMP     FSUTtemp, #0            ; Did we read ESCAPE ? VClear
+        MOV     r1, r5                  ; In any case, we want r1 handle
+        MOV     r0, #CR                 ; If not, stick a CR on eoln
+        SWIEQ   XOS_BPut
+        BEQ     %BT10                   ; Finished if ESCAPE was pressed
+                                        ; Catch error back there too
+
+        SWIVC   XOS_NewLine
+
+        B       UtilityExitCloseR1_256
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; OSFile routines: Load, Save, Create, Delete and Remove
+
+Load_Code ENTRY "$UtilRegs"
+
+        MOV     r7, r0                  ; -> filename
+        CMP     r1, #1                  ; Just filename (1 parm) ?
+        MOVEQ   r3, #&FF                ; Load at its own address if so
+        BEQ     %FT90
+
+        BL      SkipNameAndReadAddr
+        EXIT    VS
+        BLCS    SetErrorBadAddress      ; Must check for trailing junk
+        MOVVC   r3, #0                  ; Got load address from command line
+
+90      MOVVC   r0, #OSFile_Load
+        MOVVC   r1, r7                  ; Get filename^ back
+        SWIVC   XOS_File
+        EXIT
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Save_Error
+        DCD       ErrorNumber_Syntax
+      [ International
+        DCB       "BadSav:Bad parameters for *Save", 0
+      ]
+        ALIGN
+
+Save_Code ENTRY "$UtilRegs"
+
+        BL      SkipNameAndReadAddr
+        EXIT    VS
+
+        MOV     r4, r2          ; Got start address
+        BL      SkipSpaces
+        MOV     r5, r0          ; Preserve state
+        CMP     r0, #"+"        ; Is it a +<length> parm ?
+        ADDEQ   r1, r1, #1      ; Skip '+' then
+        BLEQ    SkipSpaces      ; And any trailing spaces
+        BL      ReadAtMost8Hex  ; Read a word anyway
+        EXIT    VS
+
+        CMP     r5, #"+"
+        MOVNE   r5, r2          ; <end addr> ?
+        ADDEQ   r5, r2, r4      ; Form <end addr> := <start addr> + <length>
+        MOV     r2, r4          ; r2, r3 := both r4 by default
+        MOV     r3, r4
+        BL      ReadOptionalLoadAndExec
+        SETV    CS
+        ADRVS   R0, Save_Error  ; If there's anything on the end, it's an error
+      [ International
+        BLVS    TranslateError
+      ]
+        MOVVC   r0, #OSFile_Save
+        MOVVC   r1, r7          ; Get filename^ back
+        SWIVC   XOS_File
+        EXIT
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Create_Code ENTRY "r1, $UtilRegs"
+
+        CMP     r1, #1                  ; Filename only -> length 0, dated
+        MOVEQ   r7, r0                  ; Filename^
+        MOVEQ   r2, #0                  ; Will be copied to r5 in a bit
+        BLNE    SkipNameAndReadAddr
+        EXIT    VS
+        MOV     r5, r2                  ; Got length, put in as end address
+        MOV     r4, #0                  ; So start addr shall be .. 0 !
+
+        LDR     r14, [sp]               ; No load, exec -> datestamp FFD
+        CMP     r14, #3
+        MOVLO   r0, #OSFile_CreateStamp
+        LDRLO   r2, =&FFFFFFFD          ; Only bottom 12 bits are of interest
+        MOVHS   r0, #OSFile_Create      ; Makes it an immediate constant
+        MOVHS   r2, #0                  ; Load/Exec default to 0
+        MOVHS   r3, #0
+        BLHS    ReadOptionalLoadAndExec
+
+        MOVVC   r1, r7                  ; Get filename^ back
+        SWIVC   XOS_File
+        EXIT
+
+        LTORG
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Delete and Remove share a common body
+
+Delete_Code ENTRY
+
+        MOV     r6, #0                  ; Give error if file doesn't exist
+        B       %FT01                   ; Use a reg. not affected by OSFile !
+
+Remove_Code ALTENTRY
+
+        MOV     r6, #-1                 ; Don't winge
+
+01      MOV     r1, r0                  ; -> filename
+        MOV     r0, #OSFile_Delete
+        SWI     XOS_File
+        EXIT    VS
+
+        CMP     r0, r6                  ; Are we going to winge ?
+        MOVEQ   r0, #OSFile_MakeError   ; Give pretty error now, says Tutu
+        MOVEQ   r2, #object_nothing
+        SWIEQ   XOS_File
+        EXIT
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Gosh, doesn't use UtilRegs !!!
+
+Opt_Code ENTRY
+
+        MOV     r3, #0                  ; Default parms are 0, 0
+        MOV     r4, #0
+        CMP     r1, #0                  ; No parms ? VClear
+        BEQ     %FT50
+
+        MOV     r1, r0
+        MOV     r0, #10                 ; Default base 10, allow naff term ','
+        SWI     XOS_ReadUnsigned        ; Read first parm
+        EXIT    VS
+        MOV     r3, r2
+
+        BL      FS_SkipSpaces           ; Try getting another parm anyway
+        BCC     %FT50                   ; End of the line
+
+        TEQ     r0, #","                ; commas too !
+        ADDEQ   r1, r1, #1
+        BLEQ    FS_SkipSpaces
+        CMP     r0, #space              ; Anything here ?
+        BEQ     %FT99
+
+        MOV     r0, #(1 :SHL: 31) + 10  ; Default base 10, no bad terms
+        SWI     XOS_ReadUnsigned        ; Read second parm
+        MOVVC   r4, r2
+
+50      MOVVC   r0, #FSControl_Opt
+        MOVVC   r1, r3
+        MOVVC   r2, r4
+        SWIVC   XOS_FSControl
+        EXIT
+
+99      ADR     r0, SyntaxError_StarOpt
+      [ International
+        BL      TranslateError
+      |
+        SETV
+      ]
+        EXIT
+
+
+SyntaxError_StarOpt
+        DCD     ErrorNumber_Syntax
+        DCB     "OptErr:Syntax: *Opt [<x> [[,] <y>]]", 0
+        ALIGN
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+;              H o r r i b l e   l i t t l e   s u b r o u t i n e s
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Variegated output routines
+
+HexR0LongWord ENTRY "r0"
+
+        MOV     r0, r0, ROR #16
+        BL      HexR0Word
+        MOVVC   r0, r0, ROR #32-16
+        BLVC    HexR0Word
+        STRVS   r0, [sp]
+        EXIT
+
+
+HexR0Word ENTRY "r0"
+
+        MOV     r0, r0, ROR #8
+        BL      HexR0Byte
+        MOVVC   r0, r0, ROR #32-8
+        BLVC    HexR0Byte
+        STRVS   r0, [sp]
+        EXIT
+
+
+HexR0Byte ENTRY "r0"
+
+        MOV     r0, r0, ROR #4
+        BL      HexR0Nibble
+        MOVVC   r0, r0, ROR #32-4
+        BLVC    HexR0Nibble
+        STRVS   r0, [sp]
+        EXIT
+
+
+HexR0Nibble ENTRY "r0"
+
+        AND     r0, r0, #15
+        CMP     r0, #10
+        ADDCC   r0, r0, #"0"
+        ADDCS   r0, r0, #"A"-10
+        SWI     XOS_WriteC
+        STRVS   r0, [sp]
+        EXIT
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+SkipOverNameAndSpaces
+        Push    lr
+        BL      SkipToSpace
+        Pull    lr
+
+SkipSpaces ROUT
+
+10      LDRB    r0, [r1], #1
+        CMP     r0, #" "        ; Leave r1 -> ~space
+        BEQ     %BT10
+        SUB     r1, r1, #1
+        BICS    pc, lr, #V_bit  ; r0 = first ~space. Can't really fail
+
+
+SkipToSpace ENTRY "r0"
+
+10      LDRB    r0, [r1], #1
+        CMP     r0, #&7F
+        CMPNE   r0, #" "        ; Leave r1 -> space or CtrlChar
+        BHI     %BT10
+        SUB     r1, r1, #1
+        EXITS
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r1 -> string
+
+; Out   flags from CMP r0, #space for eol detection
+
+FS_SkipSpaces ROUT
+
+10      LDRB    r0, [r1], #1
+        CMP     r0, #space      ; Leave r1 -> ~space
+        BEQ     %BT10
+        SUB     r1, r1, #1
+        MOV     pc, lr          ; r0 = first ~space
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r0 = open mode
+;       r1 -> filename
+
+; Out   VC: r0 = r1 = handle
+;       VS: r0 -> error (FilingSystemError or 'NotFound')
+
+OpenFileWithWinge ENTRY
+
+        ORR     r0, r0, #(open_mustopen :OR: open_nodir) ; Saves us code here
+        SWI     XOS_Find
+        MOVVC   r1, r0
+        EXIT
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; PTR#handle := EXT#handle
+
+; In    r1 = handle to use
+
+; Out   VC: PTR moved
+;       VS: r0 -> Filing System Error
+
+MoveToEOF ENTRY "r0, r2"
+
+        MOV     r0, #OSArgs_ReadEXT
+        SWI     XOS_Args
+        MOVVC   r0, #OSArgs_SetPTR
+        SWIVC   XOS_Args
+        STRVS   r0, [sp]
+        EXIT
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+CloseThenAckEscape
+
+        BL      CloseR1
+
+        BL      AckEscape
+        Pull    "$UtilRegs, pc"         ; Common exit - back to MOS
+
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r1 = handle to close, or 0 means don't do anything
+
+; Out   VC: file closed, or nothing done
+;       VS: r0 -> Filing System Error, or VSet on entry
+
+CloseR1 ENTRY "r0"
+
+        CMP     r1, #0                  ; Is there a handle to close ? VClear
+        MOVNE   r0, #0                  ; CLOSE#han
+        SWINE   XOS_Find
+        EXITS   VC                      ; Accumulate V
+
+        STR     r0, [sp]
+        EXIT
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Increment then print a line number in decimal
+
+LineNumberPrint ENTRY "r0-r1"
+
+        ADD     linecount, linecount, #1 ; r0 := ++linecount
+        MOV     r0, linecount
+        MOV     r1, #1                  ; Print leading spaces
+        BL      PrintR0Decimal
+        SWIVC   XOS_WriteI+" "
+        STRVS   r0, [sp]
+        EXIT
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r0 = number to print
+;       r1 = 0 -> strip spaces
+;            1 -> print leading spaces
+
+; Number gets printed RJ in a field of 4 if possible, or more as necessary
+
+PrintR0Decimal ENTRY "r0-r3"
+
+        SUB     sp, sp, #32
+        MOV     r3, r1                  ; Save flag
+        MOV     r1, sp
+        MOV     r2, #32
+        SWI     XOS_BinaryToDecimal     ; No errors from this
+        CMP     r3, #0                  ; If not doing spaces or >= 4 chars
+        CMPNE   r2, #4                  ; in the number, son't print any
+
+        ADRLT   r0, %FT98-1             ; Point to right amount of spaces
+        ADDLT   r0, r0, r2
+        SWILT   XOS_Write0
+
+10      LDRVCB  r0, [r1], #1
+        SWIVC   XOS_WriteC
+        BVS     %FT99
+        SUBS    r2, r2, #1
+        BNE     %BT10
+
+99      ADD     sp, sp, #32
+        STRVS   r0, [sp]
+        EXIT
+
+98
+        DCB     "   ", 0                ; Three spaces, null
+        ALIGN
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Read configured info of Tutu's wally byte
+
+; Out   r1 = bits read from CMOS ram
+
+ReadGSFormat ENTRY "r0, r2"
+
+        MOV     r0, #ReadCMOS
+        MOV     r1, #TutuCMOS
+        SWI     XOS_Byte
+        ANDVC   r1, r2, #2_1111         ; Mask out all but my bits
+        STRVS   r0, [sp]
+        EXIT
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r0b = char to print using current listopt
+
+; Out   r0 corrupt
+
+PrintCharInGSFormat ENTRY "r1, listopt"
+
+        AND     r0, r0, #&FF    ; Just in case
+        TST     listopt, #forcetbsrange ; Forcing tbs into 00..7F ?
+        BIC     listopt, listopt, #forcetbsrange
+        BICNE   r0, r0, #&80            ; Take top bit out if so
+
+        CMP     r0, #" "        ; Do we need to do this at all ?
+        RSBGES  r14, r0, #&7E
+        BLT     %FT10           ; LT if not in range &20-&7E
+        CMP     r0, #"|"        ; Solidus ? VClear
+        CMPNE   r0, #""""       ; Quote ?
+        CMPNE   r0, #"<"        ; Left angle ?
+        SWINE   XOS_WriteC      ; Nope, so let's print the char and exit
+        EXIT    VS
+        EXIT    NE
+
+
+10      TST     listopt, #allowtbschar  ; International format bit ?
+        BIC     listopt, listopt, #allowtbschar
+        BEQ     %FT15
+        CMP     r0, #&80
+        BHS     %FT45                   ; Print tbs char and exit
+
+15      TST     listopt, #unprintangle  ; Angle bracket format ?
+        BNE     %FT50
+
+        TST     listopt, #unprintdot    ; Doing unprintable dot format (2_01) ?
+        BEQ     %FT16
+
+        CMP     r0, #" "                ; Only space to twiddle are printable
+        RSBGES  r14, r0, #&7E
+        MOVLT   r0, #"."                ; All others are dot
+        B       %FT45                   ; Print char and exit
+
+
+; Normal BBC GSREAD format (2_00)
+
+16      CMP     r0, #&80                ; Deal with tbs first
+        BIC     r0, r0, #&80
+        BLO     %FT17
+        SWI     XOS_WriteI+"|"
+        SWIVC   XOS_WriteI+"!"
+        EXIT    VS
+
+17      CMP     r0, #&7F                ; Delete ? -> |?. VClear
+        MOVEQ   r0, #"?"
+        CMPNE   r0, #""""               ; Quote ? -> |"
+        CMPNE   r0, #"|"                ; Solidus ? -> ||
+        SWIEQ   XOS_WriteI+"|"
+        EXIT    VS
+        CMP     r0, #&1F                ; CtrlChar ? -> |<char+@>. VClear
+        ADDLS   r0, r0, #"@"
+        SWILS   XOS_WriteI+"|"
+
+45      SWI     XOS_WriteC              ; Used from above
+        EXIT
+
+
+50 ; Angle bracket format, either hex (2_11) or decimal (2_10)
+
+        SWI     XOS_WriteI+"<"
+        TST     listopt, #unprinthex
+        BNE     %FT60
+        MOV     r1, #0                  ; Strip leading spaces
+        BLVC    PrintR0Decimal
+        SWIVC   XOS_WriteI+">"
+        EXIT
+
+
+60      SWIVC   XOS_WriteI+"&"
+        BLVC    HexR0Byte
+        SWIVC   XOS_WriteI+">"
+        EXIT
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r0 -> filename. Used by most MOS OSFile routines for initial decoding
+
+; Out   r1 -> past address read
+;       r2 = address read
+;       r7 = initial filename^
+;       VS : failed to read address, r0 -> error
+;       CS : text present comes immediately after address
+
+SkipNameAndReadAddr ENTRY
+
+        MOV     r7, r0          ; Save filename^
+        MOV     r1, r0          ; -> filename
+        BL      SkipToSpace     ; Over the filename
+        BL      SkipSpaces      ; To the address
+        BL      ReadAtMost8Hex  ; Go read it Floyd !
+        LDRVCB  r0, [r1]        ; Anything on the end ?
+        CMPVC   r0, #" "+1      ; CtrlChar + space ok
+        EXIT                    ; VC/VS from readhex or VC, CC/CS from CMP
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; In    r2, r3 = load/exec addresses to use if none provided. r1 -> string
+
+; Out   r2, r3 conditionally updated, r1 updated, past any trailing spaces
+;       VS if failed, error ('Bad Address' or whatever) set
+;       CS if something comes afterwards ...
+
+ReadOptionalLoadAndExec ENTRY "r0, FSUTtemp"
+
+        MOV     FSUTtemp, r2    ; Save initial value
+
+        BL      SkipSpaces
+        CMP     r0, #" "        ; No more parms ?
+        EXIT    LO              ; VClear, r2, r3 unaffected
+
+        BL      ReadAtMost8Hex
+        BVS     %FT99
+        MOV     r3, r2
+        MOV     r2, FSUTtemp
+        BL      SkipSpaces
+        CMP     r0, #" "        ; No more parms ?
+        EXIT    LO              ; VClear, r2 unaffected, r3 updated
+
+        BL      ReadAtMost8Hex
+        BLVC    SkipSpaces      ; Anything on the end ?
+        CMPVC   r0, #" "
+99      STRVS   r0, [sp]
+        EXIT                    ; VC/VS from readhex or VC, CC/CS from CMP
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Read a hex (default) address from a string
+
+; In    r1 -> string
+
+; Out   VC: r1 -> first char not used in building number, r2 = number
+;       VS: error ('Bad Address') set
+
+ReadAtMost8Hex ENTRY "r0, r3-r4"
+
+        MOV     R0, #16                 ; default base, don't trap bad terms
+        SWI     XOS_ReadUnsigned
+        STRVS   r0, [sp]
+        EXIT                    ; VClear -> good hex number / VSet -> bad
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+; Set various errors: VSet always on exit
+
+SetErrorBadAddress
+
+        ADR     r0, ErrorBlock_BadAddress
+      [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      ]
+        ORRS    pc, lr, #V_bit
+
+        MakeErrorBlock BadAddress
+
+
+SetErrorOutsideFile
+
+        ADR     r0, ErrorBlock_OutsideFile
+      [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      ]
+        ORRS    pc, lr, #V_bit
+
+        MakeErrorBlock OutsideFile
+
+
+SetErrorEscape
+
+        ADR     r0, ErrorBlock_Escape
+      [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      ]
+        ORRS    pc, lr, #V_bit
+
+        MakeErrorBlock Escape
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+AckEscape
+        Push   "r1-r2, lr"
+        MOV     r0, #&7E
+        SWI     XOS_Byte
+
+        BLVC    SetErrorEscape          ; Only set ESCAPE error if no override
+        Pull    "r1-r2, pc"
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+        END
diff --git a/s/TMOSHelp b/s/TMOSHelp
new file mode 100644
index 0000000000000000000000000000000000000000..82300e707720391622af155b4352703ac52b706c
--- /dev/null
+++ b/s/TMOSHelp
@@ -0,0 +1,880 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+     TTL ==> s.TMOSHelp
+
+        SUBT    => HelpStrs
+
+ [ International_Help=0
+Break_Help
+      ;=   "The ",TokenEscapeChar,Token0," key either acts like Escape, or"
+     =  "The ",TokenEscapeChar,Token0," key either acts like Escape, "
+     =  "or like",TokenEscapeChar,Token2,"Reset key. ", 13
+      ;=   " like the Reset key. ",13
+
+Reset_Help
+      ;=   "When Reset is pressed, the following "
+     =  "When Reset",TokenEscapeChar,Token41,"pressed,"
+     =  TokenEscapeChar,Token2,"following  keys have an effect:", 13
+     =  "SHIFT causes an autoboot (unless Boot",TokenEscapeChar,Token41
+     =  "configured).", 13,"CTRL causes more of",TokenEscapeChar,Token2
+     =  "machine",TokenEscapeChar,Token40,"be reset.", 13,"keypad-* ca"
+     =  "uses",TokenEscapeChar,Token2,"supervisor"
+     =  TokenEscapeChar,Token40,"be run rather than"
+     =  TokenEscapeChar,Token2,"configured language.", 13,"See also "
+     =  "*Help PowerOn.",  0
+      ;=   " keys have an effect:",13
+      ;=   "SHIFT causes an autoboot (unless Boot is configured).",13
+      ;=   "CTRL causes more of the machine to be reset.",13
+      ;=   "keypad-* causes the supervisor to be run rather than the configured"
+      ;=   " language.",13
+      ;=   "See also *Help PowerOn.",0
+
+PowerOn_Help
+      ;=   "When the machine is switched on, the following "
+     =  "When",TokenEscapeChar,Token2,"machine",TokenEscapeChar,Token41
+     =  "switched on,",TokenEscapeChar,Token2,"following  keys have a"
+     =  "n effect:", 13,"R causes",TokenEscapeChar,Token2,"operating "
+     =  "system's CMOS RAM",TokenEscapeChar,Token40,"be reset.", 13,"D"
+     =  "ELETE causes",TokenEscapeChar,Token38,"the machine's CMOS RAM"
+     =  TokenEscapeChar,Token40,"be reset.", 13,"T"
+     =  TokenEscapeChar,Token16,"COPY are similar"
+     =  TokenEscapeChar,Token40,"R",TokenEscapeChar,Token16,"DELETE,"
+     =  " but set",TokenEscapeChar,Token2,"opposite configured sync.", 13
+     =  "0",TokenEscapeChar,Token40,"9 on",TokenEscapeChar,Token2,"k"
+     =  "eypad reset (just)",TokenEscapeChar,Token2,"configured monit"
+     =  "or type. Keypad dot ",TokenEscapeChar,Token19,"configured mod"
+     =  "e, sync",TokenEscapeChar,Token16,"monitor type"
+     =  TokenEscapeChar,Token40,"Auto.", 13,"See also *Help Reset"
+     =  TokenEscapeChar,Token16,"*Help Break.",  0
+      ;=   " keys have an effect:",13
+      ;=   "R causes the operating system's CMOS RAM to be reset.",13
+      ;=   "DELETE causes all the machine's CMOS RAM to be reset.",13
+      ;=   "T and COPY are similar to R and DELETE, but set the opposite"
+      ;=   " configured sync.",13
+      ;=   "0 to 9 on the keypad reset (just)"
+      ;=   " the configured monitor type."
+      ;=   " Keypad dot sets the configured mode, sync and monitor type to Auto."
+      ;=  13,"See also *Help Reset and *Help Break."0
+
+RMEnsure_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," checks that a module"
+     =  TokenEscapeChar,Token41,"present",TokenEscapeChar,Token16,"i"
+     =  "s",TokenEscapeChar,Token2,"given version, or a more modern o"
+     =  "ne. The command",TokenEscapeChar,Token41,"executed if this"
+     =  TokenEscapeChar,Token41,"not",TokenEscapeChar,Token2,"case."
+     =   13
+      ;=   " checks that a module is present and is the given version,"
+      ;=   " or a more modern one."
+      ;=   " The command is executed if this is not the case.",13
+RMEnsure_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token14,"moduletitle> <version "
+     =  TokenEscapeChar,Token13,"> [<*command>]",  0
+      ;=   " <moduletitle> <version number> [<*command>]",0
+
+Syntax_Help
+      ;=   "Symbols used in syntax descriptions:",13
+     =  "Symbols used in syntax descrip",TokenEscapeChar,Token9,"s:", 13
+     =  "<> mark sec",TokenEscapeChar,Token9,"s",TokenEscapeChar,Token40
+     =  "be filled in, eg. <",TokenEscapeChar,Token7
+     =  TokenEscapeChar,Token11,"> indicates that a "
+     =  TokenEscapeChar,Token7,TokenEscapeChar,Token11," should be"
+     =  " supplied here.", 13,"[] mark op",TokenEscapeChar,Token9,"al"
+     =  " sec",TokenEscapeChar,Token9,"s.", 13,"| indicates a choice,"
+     =  " e.g. ""A|B"" means ""A or B"".",  0
+      ;=   "<> mark sections to be filled in, eg. <filename> indicates"
+      ;=   " that a filename should be supplied here.",13
+      ;=   "[] mark optional sections.",13
+      ;=   "| indicates a choice, e.g. ""A|B"" means ""A or B"".", 0
+
+Quit_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," leaves",TokenEscapeChar,Token2
+     =  TokenEscapeChar,Token5," applica",TokenEscapeChar,Token9,"."
+     =   13
+      ;=   " leaves the current application.",13
+
+GOS_Syntax
+Modules_Syntax
+Quit_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0, 0
+     =  TokenEscapeChar,Token1,  0
+
+RMFaster_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," moves a module from ROM"
+     =  TokenEscapeChar,Token40,"RAM.", 13
+      ;=   " moves a module from ROM to RAM.",13
+RMFaster_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token14,"moduletitle>",  0
+      ;=   " <moduletitle>", 0
+
+RMKill_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," kills",TokenEscapeChar,Token16,"d"
+     =  "eletes a ",TokenEscapeChar,Token17,".", 13
+      ;=   " kills and deletes a relocatable module.",13
+RMKill_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token14,"moduletitle>",  0
+      ;=   " <moduletitle>", 0
+
+RMLoad_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," loads",TokenEscapeChar,Token16,"i"
+     =  "nitialises a ",TokenEscapeChar,Token17,".", 13
+      ;=   " loads and initialises a relocatable module.",13
+RMLoad_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token27,  0
+      ;=   " <filename>", 0
+
+RMRun_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," runs a ",TokenEscapeChar,Token17
+     =  ".", 13
+      ;=   " runs a relocatable module.",13
+RMRun_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token27,  0
+      ;=   " <filename>", 0
+
+RMTidy_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," compacts",TokenEscapeChar,Token2
+     =  "RMA",TokenEscapeChar,Token16,"reinitialises"
+     =  TokenEscapeChar,Token38,"the modules.", 13
+      ;=   " compacts the RMA and reinitialises all the modules.",13
+RMTidy_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0, 0
+     =  TokenEscapeChar,Token1,  0
+
+RMClear_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," deletes",TokenEscapeChar,Token38
+     =  TokenEscapeChar,Token17,"s from",TokenEscapeChar,Token2,"RM"
+     =  "A.", 13
+      ;=   " deletes all relocatable modules from the RMA.",13
+RMClear_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0, 0
+     =  TokenEscapeChar,Token1,  0
+
+RMReInit_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," reinitialises a "
+     =  TokenEscapeChar,Token17,", reversing",TokenEscapeChar,Token2
+     =  "ac",TokenEscapeChar,Token9," of *Unplug if appropriate.", 13
+      ;=   " reinitialises a relocatable module,"
+      ;=   " reversing the action of *Unplug if appropriate.",13
+RMReInit_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token14,"moduletitle>",  0
+      ;=   " <moduletitle>", 0
+
+ROMModules_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," lists",TokenEscapeChar,Token2
+     =  TokenEscapeChar,Token17,"s ",TokenEscapeChar,Token5,"ly in "
+     =  "ROM, along with their status.", 13,"See also *Modules.", 13
+      ;=   " lists the relocatable modules currently in ROM, along with"
+      ;=   " their status.",13
+      ;=   "See also *Modules.",13
+ROMModules_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0, 0
+     =  TokenEscapeChar,Token1,  0
+
+Set_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," assigns a string value"
+     =  TokenEscapeChar,Token40,"a system variable. Other types of va"
+     =  "lue can be assigned with *",TokenEscapeChar,Token0,"Eval"
+     =  TokenEscapeChar,Token16,"*",TokenEscapeChar,Token0,"Macro.", 13
+      ;=   " assigns a string value to a system variable."
+      ;=   " Other types of value can be assigned with *",TokenEscapeChar,Token0
+      ;=   "Eval and *",TokenEscapeChar,Token0
+      ;=   "Macro.",13
+Set_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token14,"var",TokenEscapeChar,Token11,"> <va"
+     =  "lue>",  0
+      ;=   " <varname> <value>", 0
+
+SetMacro_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," assigns a macro value"
+     =  TokenEscapeChar,Token40,"a system variable. Other types of va"
+     =  "lue can be assigned with *Set",TokenEscapeChar,Token16,"*SetE"
+     =  "val.", 13
+      ;=   " assigns a macro value to a system variable."
+      ;=   " Other types of value can be assigned with *Set and *SetEval.",13
+SetMacro_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token14,"var",TokenEscapeChar,Token11,"> <va"
+     =  "lue>",  0
+      ;=   " <varname> <value>", 0
+
+SetEval_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," evaluates an e"
+     =  TokenEscapeChar,Token29,TokenEscapeChar,Token16,"assigns it"
+     =  TokenEscapeChar,Token40,"a system variable. Other types of va"
+     =  "lue can be assigned with *Set",TokenEscapeChar,Token16,"*SetM"
+     =  "acro.", 13,"""*Help Eval"" describes",TokenEscapeChar,Token2,"e"
+     =  TokenEscapeChar,Token29," syntax.", 13
+      ;=   " evaluates an expression and assigns it to a system variable."
+      ;=   " Other types of value can be assigned with *Set and *SetMacro.",13
+      ;=   """*Help Eval"" describes the expression syntax.",13
+SetEval_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token14,"var",TokenEscapeChar,Token11,"> <e"
+     =  TokenEscapeChar,Token29,">",  0
+      ;=   " <varname> <expression>", 0
+
+Show_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," lists system variables matching"
+     =  TokenEscapeChar,Token2,TokenEscapeChar,Token11," given, or"
+     =  TokenEscapeChar,Token38,"system variables if no "
+     =  TokenEscapeChar,Token11,TokenEscapeChar,Token41,"specified."
+     =  " Variables can be set with *Set, *SetEval",TokenEscapeChar,Token16
+     =  "*SetMacro.", 13
+      ;=   " lists system variables matching the name given,"
+      ;=   " or all system variables if no name is specified."
+      ;=   " Variables can be set with *Set, *SetEval and *SetMacro.",13
+Show_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token30,"<variablespec>]",  0
+      ;=   " [<variablespec>]", 0
+
+Time_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0,TokenEscapeChar,Token32
+     =  TokenEscapeChar,Token2,"time",TokenEscapeChar,Token16,"date"
+     =  ".", 13
+      ;=   " displays the time and date.",13
+Time_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0,0
+     =  TokenEscapeChar,Token1,  0
+
+Unset_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," deletes a system variable.", 13
+      ;=   " deletes a system variable.",13
+Unset_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token14,"var",TokenEscapeChar,Token11,">",  0
+      ;=   " <varname>", 0
+
+Echo_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," sends a string to"
+     =  TokenEscapeChar,Token2,"VDU, after transforma"
+     =  TokenEscapeChar,Token9," by GSRead.", 13
+     =  TokenEscapeChar,Token14,"string>",  0
+      ;=   " sends a string to the VDU, after transformation by GSRead.",13
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+      ;=   " <string>",0
+
+Ignore_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," ",TokenEscapeChar,Token19
+     =  TokenEscapeChar,Token26,"r ignore character.", 13
+      ;=   " sets the printer ignore character.",13
+Ignore_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token30,"<",TokenEscapeChar,Token13,">]",  0
+      ;=   " [<number>]",0
+
+IF_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," condi",TokenEscapeChar,Token9,"a"
+     =  "lly executes another command depending on",TokenEscapeChar,Token2
+     =  "value of an e",TokenEscapeChar,Token29,".", 13
+      ;=   " conditionally executes another command"
+      ;=   " depending on the value of an expression.",13
+IF_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token14,"e",TokenEscapeChar,Token29,"> THEN "
+     =  "<command> [ELSE <command>]",  0
+      ;=   " <expression> THEN <command> [ELSE <command>]", 0
+
+Status_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," shows",TokenEscapeChar,Token2,"s"
+     =  "elected CMOS RAM op",TokenEscapeChar,Token9,"s. Use "
+     =  TokenEscapeChar,Token10,"to set",TokenEscapeChar,Token2,"op"
+     =  TokenEscapeChar,Token9,"s.", 13,TokenEscapeChar,Token30,"<"
+     =  "op",TokenEscapeChar,Token9,">]",  0
+      ;=   " shows the selected CMOS RAM options."
+      ;=   " Use *Configure to set the options.",13
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+      ;=   " [<option>]",0
+
+Unplug_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," stops",TokenEscapeChar,Token2,"g"
+     =  "iven ROM module being initialised.", 13,"*"
+     =  TokenEscapeChar,Token0," with no argument lists"
+     =  TokenEscapeChar,Token2,"unplugged ROM modules.", 13
+      ;=   " stops the given ROM module being initialised.",13
+      ;=   "*",TokenEscapeChar,Token0
+      ;=   " with no argument lists the unplugged ROM modules.",13
+Unplug_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token30,"<moduletitle> [<podule "
+     =  TokenEscapeChar,Token13,">]]",  0
+      ;=   " [<moduletitle> [<podule number>]]",0
+
+RMInsert_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," reverses",TokenEscapeChar,Token2
+     =  "effect of *Unplug, but does not reinitialise"
+     =  TokenEscapeChar,Token2,"specified ROM module.", 13
+      ;=   " reverses the effect of *Unplug, but does not reinitialise"
+      ;=   " the specified ROM module.",13
+RMInsert_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token14,"moduletitle> [<podule "
+     =  TokenEscapeChar,Token13,">]",  0
+      ;=   " <moduletitle> [<podule number>]",0
+
+Error_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," generates an error with"
+     =  TokenEscapeChar,Token2,"given ",TokenEscapeChar,Token13
+     =  TokenEscapeChar,Token16,"text. ", 13
+      ;=   " generates an error with the given number and text. ",13
+Error_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token30,"<",TokenEscapeChar,Token13,">] <tex"
+     =  "t>",  0
+      ;=   " [<number>] <text>",0
+
+Eval_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," evaluates an integer or string e"
+     =  TokenEscapeChar,Token29,". ", 13,"The e",TokenEscapeChar,Token29
+     =  " analyser has",TokenEscapeChar,Token2,"following operators:", 13
+     =  "+",  9,  9,  9,"addi",TokenEscapeChar,Token9," or string c"
+     =  "oncatena",TokenEscapeChar,Token9, 13,"-, *, /, MOD",  9,  9
+     =  "integer opera",TokenEscapeChar,Token9,"s", 13,"=, <, >, <=, "
+     =  ">=, <>",  9,"string or integer comparison", 13,">>, <<",  9,  9
+     =    9,"arithmetic shift right",TokenEscapeChar,Token16,"left", 13
+     =  ">>>",  9,  9,  9,"logical shift right", 13,"STR, VAL",  9,  9
+     =  "conversion between strings",TokenEscapeChar,Token16,"integers"
+     =   13,"AND, OR, EOR, NOT",  9,"(bitwise) logical operators", 13,"R"
+     =  "IGHT, LEFT",  9,  9,"substring extrac",TokenEscapeChar,Token9
+     =   13,"LEN",  9,  9,  9,"length of a string", 13, 13,"Bracket"
+     =  "s can also be used.",  0
+      ;=   " evaluates an integer or string expression. ",13
+      ;=   "The expression analyser has the following operators:",13
+      ;=   "+",9,9,9,"addition or string concatenation",13
+      ;=   "-, *, /, MOD",9,9,"integer operations",13
+      ;=   "=, <, >, <=, >=, <>",9,"string or integer comparison",13
+      ;=   ">>, <<",9,9,9,"arithmetic shift right and left",13
+      ;=   ">>>",9,9,9,"logical shift right",13
+      ;=   "STR, VAL",9,9,"conversion between strings and integers",13
+      ;=   "AND, OR, EOR, NOT",9,"(bitwise) logical operators",13
+      ;=   "RIGHT, LEFT",9,9,"substring extraction",13
+      ;=   "LEN",9,9,9,"length of a string",13,13
+      ;=   "Brackets can also be used.", 0
+Eval_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token14,"e",TokenEscapeChar,Token29,">",  0
+      ;=   " <expression>",0
+
+GO_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," [<address>] [; environment]  - g"
+     =  "o",TokenEscapeChar,Token40,"address (hexadecimal), "
+     =  TokenEscapeChar,Token8,"&8000.  Text after ';'"
+     =  TokenEscapeChar,Token41,"environment string.",  0
+      ;=   " [<address>] [; environment] "
+      ;=   " - go to address (hexadecimal), default &8000. "
+      ;=   " Text after ';' is environment string.", 0
+
+GOS_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," enters",TokenEscapeChar,Token2
+     =  "supervisor. Use *Quit",TokenEscapeChar,Token40,"exit.",  0
+      ;=   " enters the supervisor. Use *Quit to exit.", 0
+
+Append_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," opens an existing "
+     =  TokenEscapeChar,Token7,TokenEscapeChar,Token16,"subsequent"
+     =  " lines of keyboard input are appended",TokenEscapeChar,Token40
+     =  "it, input being terminated by ESCAPE.", 13
+      ;=   " opens an existing file and subsequent lines of keyboard input are"
+      ;=   " appended to it, input being terminated by ESCAPE.",13
+Append_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token27,  0
+      ;=   " <filename>", 0
+
+Build_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," opens a new "
+     =  TokenEscapeChar,Token7,TokenEscapeChar,Token16,"subsequent"
+     =  " lines of keyboard input are directed",TokenEscapeChar,Token40
+     =  "it, input being terminated by ESCAPE.", 13
+      ;=   " opens a new file and subsequent lines of keyboard input are"
+      ;=   " directed to it, input being terminated by ESCAPE.",13
+Build_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token27,  0
+      ;=   " <filename>", 0
+
+Close_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," closes",TokenEscapeChar,Token38
+     =  TokenEscapeChar,Token7,"s on",TokenEscapeChar,Token2
+     =  TokenEscapeChar,Token5," ",TokenEscapeChar,Token4,".", 13
+      ;=   " closes all files on the current filing system.",13
+Close_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0,0
+     =  TokenEscapeChar,Token1,  0
+
+Create_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," reserves space for"
+     =  TokenEscapeChar,Token2,TokenEscapeChar,Token11,"d "
+     =  TokenEscapeChar,Token7,", op",TokenEscapeChar,Token9,"ally"
+     =  " giving it load",TokenEscapeChar,Token16,"execu"
+     =  TokenEscapeChar,Token9," addresses. No data"
+     =  TokenEscapeChar,Token41,"transferred",TokenEscapeChar,Token40
+     =  "the ",TokenEscapeChar,Token7,". Length",TokenEscapeChar,Token16
+     =  "addresses are in hexadecimal.", 13
+      ;=   " reserves space for the named file, optionally giving it"
+      ;=   " load and execution addresses. No data is transferred to the file."
+      ;=   " Length and addresses are in hexadecimal.",13
+Create_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token27," [<length> [<exec addr> [<load addr>"
+     =  "]]]",  0
+      ;=   " <filename> [<length> [<exec addr> [<load addr>]]]", 0
+
+Delete_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," tries",TokenEscapeChar,Token40,"d"
+     =  "elete",TokenEscapeChar,Token2,TokenEscapeChar,Token11,"d "
+     =  TokenEscapeChar,Token7,", returning an error if"
+     =  TokenEscapeChar,Token2,TokenEscapeChar,Token7," does not "
+     =  "exist.", 13,"See also *Remove",TokenEscapeChar,Token16,"*Wipe"
+     =  ".", 13
+      ;=   " tries to delete the named file, returning an error if the file"
+      ;=   " does not exist.",13
+      ;=   "See also *Remove and *Wipe.",13
+Delete_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token27,  0
+      ;=   " <filename>", 0
+
+Dump_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0,TokenEscapeChar,Token32
+     =  TokenEscapeChar,Token2,"contents of",TokenEscapeChar,Token2
+     =  TokenEscapeChar,Token7," as a hex",TokenEscapeChar,Token16,"A"
+     =  "SCII dump. The ",TokenEscapeChar,Token7," offset"
+     =  TokenEscapeChar,Token16,"start address are in hexadecimal.", 13
+      ;=   " displays the contents of the file as a hex and ASCII dump."
+      ;=   " The file offset and start address are in hexadecimal.",13
+Dump_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token27," [<",TokenEscapeChar,Token7," offs"
+     =  "et> [<start address>]]",  0
+      ;=   " <filename> [<file offset> [<start address>]]", 0
+
+Exec_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," <",TokenEscapeChar,Token7
+     =  TokenEscapeChar,Token11,"> directs",TokenEscapeChar,Token2,"o"
+     =  "perating system",TokenEscapeChar,Token40,"take further input "
+     =  "from",TokenEscapeChar,Token2,"given ",TokenEscapeChar,Token7
+     =  ".", 13,"*",TokenEscapeChar,Token0," with no "
+     =  TokenEscapeChar,Token7,TokenEscapeChar,Token11," causes"
+     =  TokenEscapeChar,Token2,"exec ",TokenEscapeChar,Token7
+     =  TokenEscapeChar,Token40,"be closed.", 13
+      ;=   " <filename> directs the operating system to take further input"
+      ;=   " from the given file.",13
+      ;=   "*",TokenEscapeChar,Token0
+      ;=   " with no filename causes the exec file to be closed.",13
+Exec_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token30,"<",TokenEscapeChar,Token7
+     =  TokenEscapeChar,Token11,">]",  0
+      ;=   " [<filename>]", 0
+
+FX_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," r0 [[,] r1 [[,] r2]] calls OS_By"
+     =  "te.",  0
+      ;=   " r0 [[,] r1 [[,] r2]] calls OS_Byte.", 0
+
+FX_Syntax
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," needs 1",TokenEscapeChar,Token40
+     =  "3 numeric ",TokenEscapeChar,Token36,"s.",  0
+      ;=   " needs 1 to 3 numeric parameters.", 0
+
+Key_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," ",TokenEscapeChar,Token19,"func"
+     =  TokenEscapeChar,Token9," keys.", 13
+      ;=   " sets the function keys.",13
+Key_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token14,"key",TokenEscapeChar,Token13,"> [<v"
+     =  "alue>]",  0
+      ;=   " <keynumber> [<value>]", 0
+
+List_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0,TokenEscapeChar,Token32
+     =  TokenEscapeChar,Token2,"contents of",TokenEscapeChar,Token2
+     =  TokenEscapeChar,Token7," in",TokenEscapeChar,Token2,"confi"
+     =  "gured GSRead format. Each line",TokenEscapeChar,Token41,"prec"
+     =  "eded with a line ",TokenEscapeChar,Token13,".", 13,"See also "
+     =  "*Print",TokenEscapeChar,Token16,"*Type.", 13
+      ;=   " displays the contents of the file in the configured GSRead format."
+      ;=   " Each line is preceded with a line number.",13
+      ;=   "See also *Print and *Type.",13
+List_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token30,"-File] <",TokenEscapeChar,Token7
+     =  TokenEscapeChar,Token11,"> [-TabExpand]",  0
+      ;=   " [-File] <filename> [-TabExpand]", 0
+
+Load_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," with no specified address loads"
+     =  TokenEscapeChar,Token2,TokenEscapeChar,Token11,"d "
+     =  TokenEscapeChar,Token7," at its own load address. If a load "
+     =  "address (hexadecimal)",TokenEscapeChar,Token41,"specified, it"
+     =  " will be used instead.", 13
+      ;=   " with no specified address loads the named file at its own"
+      ;=   " load address. If a load address (hexadecimal) is specified,"
+      ;=   " it will be used instead.",13
+Load_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token27," [<load addr>]",  0
+      ;=   " <filename> [<load addr>]", 0
+
+Print_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0,TokenEscapeChar,Token32
+     =  TokenEscapeChar,Token2,"contents of a ",TokenEscapeChar,Token7
+     =  " by sending each byte",TokenEscapeChar,Token40,"the VDU.", 13
+     =  "See also *List",TokenEscapeChar,Token16,"*Type.", 13
+      ;=   " displays the contents of a file by sending each byte to the VDU.",13
+      ;=   "See also *List and *Type.",13
+Print_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token27,  0
+      ;=   " <filename>", 0
+
+Opt_Help
+         ;=       "*"
+     =  "*",TokenEscapeChar,Token0," controls various "
+     =  TokenEscapeChar,Token4," ac",TokenEscapeChar,Token9,"s.", 13
+     =  "Opt 1 <n>", 31,"Set",TokenEscapeChar,Token2
+     =  TokenEscapeChar,Token4," message level (for Load/Save/Create"
+     =  "):", 13,  9,"0",  9,"No ",TokenEscapeChar,Token4," messages"
+     =   13,  9,"1",  9,"File",TokenEscapeChar,Token11," "
+     =  TokenEscapeChar,Token26,"d", 13,  9,"2",  9,"File"
+     =  TokenEscapeChar,Token11,",hexadecimal addresses"
+     =  TokenEscapeChar,Token16,"length ",TokenEscapeChar,Token26,"d"
+     =   13,  9,"3",  9,"File",TokenEscapeChar,Token11,",datestamp"
+     =  TokenEscapeChar,Token16,"length ",TokenEscapeChar,Token26,"d"
+     =   13,"Opt 4 <n>", 31,"Set",TokenEscapeChar,Token2
+     =  TokenEscapeChar,Token4," boot op",TokenEscapeChar,Token9,":"
+     =   13,  9,"0",  9,"No boot ac",TokenEscapeChar,Token9, 13,  9
+     =  "1",  9,"*Load boot ",TokenEscapeChar,Token7, 13,  9,"2",  9
+     =  "*Run boot ",TokenEscapeChar,Token7, 13,  9,"3",  9,"*Exec "
+     =  "boot ",TokenEscapeChar,Token7, 13
+         ;=       TokenEscapeChar,Token0
+         ;=       " controls various filing system actions."
+         ;=       13,"Opt 1 <n>",31
+         ;=       "Set the filing system message level (for Load/Save/Create):"
+         ;=       13,9,"0",9,"No filing system messages"
+         ;=       13,9,"1",9,"Filename printed"
+         ;=       13,9,"2",9,"Filename,hexadecimal addresses and length printed"
+         ;=       13,9,"3",9,"Filename,datestamp and length printed"
+         ;=       13,"Opt 4 <n>",31
+         ;=       "Set the filing system boot option:"
+         ;=       13,9,"0",9,"No boot action"
+         ;=       13,9,"1",9,"*Load boot file"
+         ;=       13,9,"2",9,"*Run boot file"
+         ;=       13,9,"3",9,"*Exec boot file"
+         ;=       13
+Opt_Syntax
+         ;=       "Syntax: *"
+     =  TokenEscapeChar,Token30,"<x> [[,] <y>]]",  0
+         ;=       TokenEscapeChar,Token0
+         ;=       " [<x> [[,] <y>]]",0
+
+Remove_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," tries",TokenEscapeChar,Token40,"d"
+     =  "elete",TokenEscapeChar,Token2,TokenEscapeChar,Token11,"d "
+     =  TokenEscapeChar,Token7," without returning an error if"
+     =  TokenEscapeChar,Token2,TokenEscapeChar,Token7," does not "
+     =  "exist.", 13,"See also *Delete",TokenEscapeChar,Token16,"*Wipe"
+     =  ".", 13
+      ;=   " tries to delete the named file without returning an error"
+      ;=   " if the file does not exist.",13
+      ;=   "See also *Delete and *Wipe.",13
+Remove_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token27,  0
+      ;=   " <filename>", 0
+
+Save_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," copies",TokenEscapeChar,Token2
+     =  "given area of memory",TokenEscapeChar,Token40,"the "
+     =  TokenEscapeChar,Token11,"d ",TokenEscapeChar,Token7,". Leng"
+     =  "th",TokenEscapeChar,Token16,"addresses are in hexadecimal.", 13
+     =  TokenEscapeChar,Token27," <start addr> <end addr> [<exec addr"
+     =  "> [<load addr>]]", 13, 31, 31, 31, 31," or *"
+     =  TokenEscapeChar,Token0," <",TokenEscapeChar,Token7
+     =  TokenEscapeChar,Token11,"> <start addr> + <length> [<exec add"
+     =  "r> [<load addr>]]",  0
+      ;=   " copies the given area of memory to the named file."
+      ;=   " Length and addresses are in hexadecimal.",13
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+      ;=   " <filename> <start addr>"
+      ;=   " <end addr> [<exec addr> [<load addr>]]",13
+      ;=   31, 31, 31, 31, " or *",TokenEscapeChar,Token0
+      ;=   " <filename> <start addr>"
+      ;=   " + <length> [<exec addr> [<load addr>]]", 0
+
+Shadow_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," makes subsequent mode changes us"
+     =  "e",TokenEscapeChar,Token2,"other screen bank.", 13
+      ;=   " makes subsequent mode changes use the other screen bank.",13
+Shadow_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token30,"0|1]",  0
+      ;=   " [0|1]", 0
+
+Spool_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," <",TokenEscapeChar,Token7
+     =  TokenEscapeChar,Token11,"> opens a new ",TokenEscapeChar,Token7
+     =  TokenEscapeChar,Token16,"causes subsequent VDU output"
+     =  TokenEscapeChar,Token40,"be directed",TokenEscapeChar,Token40
+     =  "it, subject to",TokenEscapeChar,Token2,TokenEscapeChar,Token5
+     =  " *fx 3 status.", 13,"*",TokenEscapeChar,Token0," with no "
+     =  TokenEscapeChar,Token7,TokenEscapeChar,Token11," causes"
+     =  TokenEscapeChar,Token2,"spool ",TokenEscapeChar,Token7
+     =  TokenEscapeChar,Token40,"be closed.", 13
+      ;=   " <filename> opens a new file and causes subsequent VDU output"
+      ;=   " to be directed to it, subject to the current *fx 3 status.",13
+      ;=   "*",TokenEscapeChar,Token0
+      ;=   " with no filename causes the spool file to be closed.",13
+Spool_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token30,"<",TokenEscapeChar,Token7
+     =  TokenEscapeChar,Token11,">]",  0
+      ;=   " [<filename>]", 0
+
+SpoolOn_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," <",TokenEscapeChar,Token7
+     =  TokenEscapeChar,Token11,"> opens an existing "
+     =  TokenEscapeChar,Token7,TokenEscapeChar,Token16,"causes sub"
+     =  "sequent VDU output",TokenEscapeChar,Token40,"be appended"
+     =  TokenEscapeChar,Token40,"it, subject to",TokenEscapeChar,Token2
+     =  TokenEscapeChar,Token5," *fx 3 status.", 13,"*"
+     =  TokenEscapeChar,Token0," with no ",TokenEscapeChar,Token7
+     =  TokenEscapeChar,Token11," causes",TokenEscapeChar,Token2,"s"
+     =  "pool ",TokenEscapeChar,Token7,TokenEscapeChar,Token40,"be "
+     =  "closed.", 13
+      ;=   " <filename> opens an existing file and causes subsequent VDU output"
+      ;=   " to be appended to it, subject to the current *fx 3 status.",13
+      ;=   "*",TokenEscapeChar,Token0
+      ;=   " with no filename causes the spool file to be closed.",13
+SpoolOn_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token30,"<",TokenEscapeChar,Token7
+     =  TokenEscapeChar,Token11,">]",  0
+      ;=   " [<filename>]", 0
+
+TV_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," [<vertical posi"
+     =  TokenEscapeChar,Token9,"> [[,] <interlace>]] "
+     =  TokenEscapeChar,Token19,"posi",TokenEscapeChar,Token9," of"
+     =  TokenEscapeChar,Token2,"display on",TokenEscapeChar,Token2
+     =  "screen.",  0
+      ;=   " [<vertical position> [[,] <interlace>]]"
+      ;=   " sets the position of the display on the screen.", 0
+
+TV_Syntax
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," needs 0",TokenEscapeChar,Token40
+     =  "2 ",TokenEscapeChar,Token36,"s.",  0
+      ;=   " needs 0 to 2 parameters.", 0
+
+Type_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0,TokenEscapeChar,Token32
+     =  TokenEscapeChar,Token2,"contents of",TokenEscapeChar,Token2
+     =  TokenEscapeChar,Token7," in",TokenEscapeChar,Token2,"confi"
+     =  "gured GSRead format.", 13,"See also *List",TokenEscapeChar,Token16
+     =  "*Print.", 13
+      ;=   " displays the contents of the file"
+      ;=   " in the configured GSRead format.",13
+      ;=   "See also *List and *Print.",13
+Type_Syntax
+      ;=   "Syntax: *",TokenEscapeChar,Token0
+     =  TokenEscapeChar,Token30,"-File] <",TokenEscapeChar,Token7
+     =  TokenEscapeChar,Token11,"> [-TabExpand]",  0
+      ;=   " [-File] <filename> [-TabExpand]", 0
+
+Help_Help
+      ;=   "*",TokenEscapeChar,Token0
+     =  "*",TokenEscapeChar,Token0," <subjects> attempts"
+     =  TokenEscapeChar,Token40,"give useful informa"
+     =  TokenEscapeChar,Token9," on",TokenEscapeChar,Token2,"selec"
+     =  "ted topics. Special keywords include:", 13,"Commands",  9,"Lis"
+     =  "t",TokenEscapeChar,Token38,"the available utility commands", 13
+     =  "FileCommands",  9,"List all",TokenEscapeChar,Token2
+     =  TokenEscapeChar,Token4,"-specific commands", 13,"Modules",  9
+     =    9,"List",TokenEscapeChar,Token2,"module titles", 13,"Synta"
+     =  "x",  9,  9,"Explain",TokenEscapeChar,Token2,"syntax message"
+     =  " format",  0
+      ;=   " <subjects> attempts to give useful information"
+      ;=   " on the selected topics. Special keywords include:",13
+      ;=   "Commands", 9, "List all the available utility commands",13
+      ;=   "FileCommands", 9
+      ;=   "List all the filing system-specific commands",13
+      ;=   "Modules", 9, 9, "List the module titles",13
+      ;=   "Syntax", 9, 9, "Explain the syntax message format",0
+ |
+Break_Help      DCB     "HUTMBRK", 0
+Reset_Help      DCB     "HUTMRES", 0
+PowerOn_Help    DCB     "HUTMPOW", 0
+RMEnsure_Help   DCB     "HUTMRME", 0
+RMEnsure_Syntax DCB     "SUTMRME", 0
+Syntax_Help     DCB     "HUTMSYN", 0
+Quit_Help       DCB     "HUTMQUI", 0
+GOS_Syntax      DCB     "SUTMGOS", 0
+Modules_Syntax  DCB     "SUTMMOD", 0
+Quit_Syntax     DCB     "SUTMQUI", 0
+RMFaster_Help   DCB     "HUTMRMF", 0
+RMFaster_Syntax DCB     "SUTMRMF", 0
+RMKill_Help     DCB     "HUTMRMK", 0
+RMKill_Syntax   DCB     "SUTMRMK", 0
+RMLoad_Help     DCB     "HUTMRML", 0
+RMLoad_Syntax   DCB     "SUTMRML", 0
+RMRun_Help      DCB     "HUTMRRN", 0
+RMRun_Syntax    DCB     "SUTMRRN", 0
+RMTidy_Help     DCB     "HUTMRMT", 0
+RMTidy_Syntax   DCB     "SUTMRMT", 0
+RMClear_Help    DCB     "HUTMRMC", 0
+RMClear_Syntax  DCB     "SUTMRMC", 0
+RMReInit_Help   DCB     "HUTMRMR", 0
+RMReInit_Syntax DCB     "SUTMRMR", 0
+ROMModules_Help   DCB   "HUTMROM", 0
+ROMModules_Syntax DCB   "SUTMROM", 0
+Set_Help        DCB     "HUTMSET", 0
+Set_Syntax      DCB     "SUTMSET", 0
+SetMacro_Help   DCB     "HUTMSTM", 0
+SetMacro_Syntax DCB     "SUTMSTM", 0
+SetEval_Help    DCB     "HUTMSTE", 0
+SetEval_Syntax  DCB     "SUTMSTE", 0
+Show_Help       DCB     "HUTMSHO", 0
+Show_Syntax     DCB     "SUTMSHO", 0
+Time_Help       DCB     "HUTMTIM", 0
+Time_Syntax     DCB     "SUTMTIM", 0
+Unset_Help      DCB     "HUTMUNS", 0
+Unset_Syntax    DCB     "SUTMUNS", 0
+Echo_Help       DCB     "HUTMECH", 0
+Ignore_Help     DCB     "HUTMIGN", 0
+Ignore_Syntax   DCB     "SUTMIGN", 0
+IF_Help         DCB     "HUTMIF", 0
+IF_Syntax       DCB     "SUTMIF", 0
+Status_Help     DCB     "HUTMSTA", 0
+Unplug_Help     DCB     "HUTMUNP", 0
+Unplug_Syntax   DCB     "SUTMUNP", 0
+RMInsert_Help   DCB     "HUTMRMI", 0
+RMInsert_Syntax DCB     "SUTMRMI", 0
+Error_Help      DCB     "HUTMERR", 0
+Error_Syntax    DCB     "SUTMERR", 0
+Eval_Help       DCB     "HUTMEVL", 0
+Eval_Syntax     DCB     "SUTMEVL", 0
+GO_Help         DCB     "HUTMGO", 0
+GOS_Help        DCB     "HUTMGOS", 0
+Append_Help     DCB     "HUTMAPP", 0
+Append_Syntax   DCB     "SUTMAPP", 0
+Build_Help      DCB     "HUTMBUI", 0
+Build_Syntax    DCB     "SUTMBUI", 0
+Close_Help      DCB     "HUTMCLO", 0
+Close_Syntax    DCB     "SUTMCLO", 0
+Create_Help     DCB     "HUTMCRE", 0
+Create_Syntax   DCB     "SUTMCRE", 0
+Delete_Help     DCB     "HUTMDEL", 0
+Delete_Syntax   DCB     "SUTMDEL", 0
+Dump_Help       DCB     "HUTMDMP", 0
+Dump_Syntax     DCB     "SUTMDMP", 0
+Exec_Help       DCB     "HUTMEXE", 0
+Exec_Syntax     DCB     "SUTMEXE", 0
+FX_Help         DCB     "HUTMFX", 0
+FX_Syntax       DCB     "SUTMFX", 0
+Key_Help        DCB     "HUTMKEY", 0
+Key_Syntax      DCB     "SUTMKEY", 0
+List_Help       DCB     "HUTMLST", 0
+List_Syntax     DCB     "SUTMLST", 0
+Load_Help       DCB     "HUTMLOD", 0
+Load_Syntax     DCB     "SUTMLOD", 0
+Print_Help      DCB     "HUTMPRI", 0
+Print_Syntax    DCB     "SUTMPRI", 0
+Opt_Help        DCB     "HUTMOPT", 0
+Opt_Syntax      DCB     "SUTMOPT", 0
+Remove_Help     DCB     "HUTMREM", 0
+Remove_Syntax   DCB     "SUTMREM", 0
+Save_Help       DCB     "HUTMSAV", 0
+Save_Syntax     DCB     "SUTMSAV", 0
+Shadow_Help     DCB     "HUTMSHA", 0
+Shadow_Syntax   DCB     "SUTMSHA", 0
+Spool_Help      DCB     "HUTMSPL", 0
+Spool_Syntax    DCB     "SUTMSPL", 0
+SpoolOn_Help    DCB     "HUTMSPO", 0
+SpoolOn_Syntax  DCB     "SUTMSPO", 0
+TV_Help         DCB     "HUTMTV", 0
+TV_Syntax       DCB     "SUTMTV", 0
+Type_Help       DCB     "HUTMTYP", 0
+Type_Syntax     DCB     "SUTMTYP", 0
+Help_Help       DCB     "HUTMHLP", 0
+ ]
+
+modules_help1   DCB     "HUTMMOD", 0
+commands_helpstr
+                DCB     "HUTMCOM", 0
+fscommands_helpstr
+                DCB     "HUTMFSC", 0
+modjack_hstr    DCB     "HUTMMD", 0
+modjack_comms   DCB     "HUTMCM", 0
+modjack_filecomms
+                DCB     "HUTMFS", 0
+modjack_confs   DCB     "HUTMCK", 0
+modjack_aob     DCB     "HUTMHK", 0
+
+        END
diff --git a/s/TickEvents b/s/TickEvents
new file mode 100644
index 0000000000000000000000000000000000000000..6907bb431d2f55a9874c9395dccf2a5700cd3388
--- /dev/null
+++ b/s/TickEvents
@@ -0,0 +1,249 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL   => TickEvents
+
+; This file revised by TMD 28-Jan-94 to
+; a) Correct internals to time n ticks not n+1
+; b) Change externals to add 1 to compensate for user subtracting 1
+; c) Fix RemoveTickerEvent to add this nodes time onto the next one
+; These fix bug MED-02498.
+
+; There are two (centisecond) ticker SWIs :
+; SWI CallAfter calls the given address once, after the given number of ticks
+; SWI CallEvery  "     "    "    "      every N centiseconds
+
+; In :  R0 is (signed) number of centiseconds
+;       R1 is address to call
+;       R2 is value of R12 to pass to code
+
+CallAfter_Code   ROUT
+        MOV     r10, #0
+TickTockCommon
+        Push    "r0-r3, lr"
+        CMP     r0, #0
+        BLE     %FT99
+
+        MOV     r3, #TickNodeSize
+        BL      ClaimSysHeapNode
+        BVS     %FT97
+
+        MOV     r3, r2
+        LDMFD   stack, {r0-r2}
+        STR     r1,  [r3, #TickNodeAddr]
+        STR     r10, [r3, #TickNodeRedo]
+        STR     r2,  [r3, #TickNodeR12]
+        MOV     r1, r3
+
+        ADD     r0, r0, #1              ; compensate for n+1 bug
+        BL      InsertTickerEvent
+
+        Pull    "r0-r3, lr"
+        ExitSWIHandler
+
+99      ADR     r0, ErrorBlock_BadTime
+      [ International
+        BL      TranslateError
+      ]
+97
+ [ Fix14
+        ADD     sp, sp, #1*4            ; junk old R0
+        Pull    "r1-r3, lr"
+ |
+        Pull    "r0-r3, lr"
+ ]
+        B       SLVK_SetV
+
+        MakeErrorBlock BadTime
+
+CallEvery_Code
+        ADD     r10, r0, #1             ; compensate for n+1 bug
+        B       TickTockCommon
+
+; Data structure :
+; chain of nodes
+;
+;       +----------------------------------+
+;       |   Link to next node or 0         |
+;       +----------------------------------+
+;       |   Reload flag                    |
+;       +----------------------------------+
+;       |   Address to call                |
+;       +----------------------------------+
+;       |   Value of R12                   |
+;       +----------------------------------+
+;       |   No of ticks to go before call  |
+;       +----------------------------------+
+;
+; The head node's no of ticks is decremented until 0
+;  Subsequent nodes contain the no of ticks to wait when they reach the
+;  chain head.
+; If the reload flag is non-0, the node is reinserted at that number of ticks
+; down the chain after every use.
+
+             ^  0
+TickNodeLink #  4        ;
+TickNodeRedo #  4        ; These are together
+TickNodeAddr #  4        ;
+TickNodeR12  #  4        ;  so can LDM them
+
+TickNodeLeft #  4
+
+TickNodeSize #  0
+
+InsertTickerEvent   ROUT
+; R1 is node pointer, R0 ticks to wait
+; R10-R12 corrupted
+
+        Push    "r0"
+        MOV     r10, pc
+        ORR     r10, r10, #I_bit
+        TEQP    r10, #0
+        LDR     r10, =TickNodeChain
+01
+        MOV     r11, r10
+        LDR     r10, [r11, #TickNodeLink]
+        CMP     r10, #0
+        BEQ     %FT02                           ; end of chain
+        LDR     r12,  [r10, #TickNodeLeft]
+        SUBS    r0, r0, r12
+        BGE     %BT01
+        ADD     r0, r0, r12
+        SUB     r12, r12, r0
+        STR     r12, [r10, #TickNodeLeft]
+02
+        STR     r1, [r11, #TickNodeLink]
+        STR     r0, [r1, #TickNodeLeft]
+        STR     r10, [r1, #TickNodeLink]
+
+        Pull    "r0"
+        MOVS    pc, lr
+
+ProcessTickEventChain  ROUT
+; R0-R3, R10-R12 corruptible
+        LDR     r3, =TickNodeChain
+
+        LDR     r1, [r3, #MetroGnome-TickNodeChain]
+        ADD     r1, r1, #1
+        STR     r1, [r3, #MetroGnome-TickNodeChain]
+
+        LDR     r1, [r3, #TickNodeLink]
+        CMP     r1, #0
+        MOVEQ   pc, lr                          ; no timers
+
+        LDR     r2, [r1, #TickNodeLeft]
+        SUBS    r2, r2, #1
+        STR     r2, [r1, #TickNodeLeft]
+        MOVGT   pc, lr                          ; nothing to call yet (was MOVPL)
+
+        Push    "lr"                            ; save IRQ_lr
+ [ TickIrqReenter
+        TEQP    pc, #SVC_mode+I_bit             ; switch to SVC mode, IRQ's off
+        NOP
+        Push    "lr"                            ; save SVC_lr
+ ]
+01
+        LDMIA   r1, {r2, r10, r11, r12}         ; load next ptr, redo state,
+                                                ;     address and R12val
+        STR     r2, [r3]                        ; de-link from chain
+        MOV     lr, pc
+        MOV     pc, r11                         ; call event handler
+
+        LDR     r0, [r1, #TickNodeRedo]
+        CMP     r0, #0                          ; CallEvery?
+        BLGT    InsertTickerEvent               ; yes, then re-insert timer
+        BGT     %FT10
+
+; Return spent ticker node to heap
+
+ [ TickIrqReenter
+        TEQP    pc, #SVC_mode                   ; IRQ's ON for the  S L O W  bit
+        MOV     r2, r1                          ; R2->node to free
+        LDR     r1, =SysHeapStart
+        MOV     r0, #HeapReason_Free
+        SWI     XOS_Heap
+        TEQP    pc, #SVC_mode+I_bit             ; IRQ's off again
+
+; Check for more events down the list
+10
+        LDR     r1, [r3, #TickNodeLink]         ; get top of list
+        CMP     r1, #0                          ; list empty?
+        BEQ     %FT02                           ; yes then exit
+
+        LDR     r0, [r1, #TickNodeLeft]
+        CMP     r0, #0                          ; timed out?
+        BLE     %BT01                           ; yes then jump
+02
+        Pull    "lr"                            ; restore SVC_lr
+        TEQP    pc, #IRQ_mode+I_bit             ; back to IRQ mode
+        NOP
+        Pull    "pc"                            ; pull IRQ_lr from IRQ stack
+ |
+        TEQP    pc, #SVC_mode+I_bit
+        MOV     r0, #HeapReason_Free
+        Push    "r2, lr"
+        MOV     r2, r1
+        LDR     r1, =SysHeapStart
+        SWI     XOS_Heap
+        Pull    "r2, lr"
+        TEQP    pc, #IRQ_mode+I_bit
+10
+        CMP     r2, #0                          ; end of list?
+        BEQ     %FT02                           ; yes then exit
+        LDR     r0, [r2, #TickNodeLeft]
+        CMP     r0, #0
+        MOVLE   r1, r2
+        BLE     %BT01
+02
+        Pull    "pc"
+ ]
+
+RemoveTickerEvent_Code
+; R0 is address of code to remove, R1 the R12 value
+        TEQP    pc, #SVC_mode+I_bit
+        LDR     r10, =TickNodeChain
+01
+        LDR     r11, [r10]
+        CMP     r11, #0
+        ExitSWIHandler EQ
+        LDR     r12, [r11, #TickNodeAddr]
+        CMP     r12, r0
+        LDREQ   r12, [r11, #TickNodeR12]
+        CMPEQ   r12, r1
+        MOVNE   r10, r11
+        BNE     %BT01
+
+        Push    "r0-r2, lr"
+        MOV     r2, r11
+        LDR     r11, [r11, #TickNodeLink]       ; prev->link = this->link
+        STR     r11, [r10]
+
+        TEQ     r11, #0                         ; if next node exists
+        LDRNE   r14, [r11, #TickNodeLeft]       ; then add our time-to-go onto its
+        LDRNE   r0, [r2, #TickNodeLeft]
+        ADDNE   r14, r14, r0
+        STRNE   r14, [r11, #TickNodeLeft]
+
+        BL      FreeSysHeapNode
+        Pull    "r0-r2, lr"
+        B       %BT01
+
+ReadMetroGnome
+        MOV     r0, #0
+        LDR     r0, [r0, #MetroGnome]
+        ExitSWIHandler
+
+        LTORG
+
+        END
diff --git a/s/UnSqueeze b/s/UnSqueeze
new file mode 100644
index 0000000000000000000000000000000000000000..1536493dc1eeb7d904377854ba7d1f6b84d53857
--- /dev/null
+++ b/s/UnSqueeze
@@ -0,0 +1,352 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+;
+; s.UnSqueeze by RCC 25-Aug-87
+; This is a bit of code to be included in self-decompressing images to
+; expand the image in place.  See elsewhere for details of the compression
+; algorithm.
+;
+; ***********************************
+; ***    C h a n g e   L i s t    ***
+; ***********************************
+
+; Date       Name       Description
+; ----       ----       -----------
+; 13-Feb-90  TDobson    Minor optimisation which saves 1 instruction for
+;                       every output word that isn't a "short" or a "long".
+; 15-Feb-90  TDobson    Started conversion for inclusion in RISC OS kernel
+
+;        GET     Hdr:Debug
+
+; Constants defining partition of nibble value space: these must match
+; corresponding values in mod.squeeze.
+
+NibsLong    * 7
+NibsShort   * (14-NibsLong)
+MinShort    * (2+NibsLong)
+MinLong     * 2
+
+; **************************************************************************
+;
+;       CheckForSqueezedModule - Check whether a module is squeezed, and
+;                                unsqueeze it if it is
+;
+; in:   R9 -> module node
+;       R10 -> environment string
+;       R12 -> incarnation node
+;
+; out:  R9 preserved, but module node pointed to may have code entry changed
+;       to point to expanded copy of module
+;       R10, R12 preserved
+;       R0-R6 may be corrupted
+;
+; If offset to init entry is negative, then (offset BIC &80000000) is the
+; offset from the size of the encoded module. The last 5 words of this are
+; as follows
+
+        ^       -5*4
+Squeeze_DecodedSize # 4                                 ; size of decoded image     (bytes)
+Squeeze_EncodedSize # 4                                 ; size of encoded image     (--"--)
+Squeeze_TablesSize  # 4                                 ; size of short+long tables (--"--)
+Squeeze_NShorts     # 4                                 ; number of "short" entries
+Squeeze_NLongs      # 4                                 ; number of "long" entries
+        ASSERT  @=0
+
+CheckForSqueezedModule ROUT
+        BIC     lr, lr, #V_bit                          ; prepare for normal exit (V clear)
+        LDR     R6, [R9, #Module_code_pointer]          ; R6 -> module code
+        LDR     R5, [R6, #Module_Init]                  ; R5 = offset to init entry
+        EORS    R5, R5, #&80000000                      ; take off top bit
+        MOVMIS  PC, lr                                  ; if -ve now, then it's a normal module, so exit doing nothing
+
+; it's a squeezed module, R5 = size of compressed module
+
+        Push    "R6-R12,lr"     ; save other registers (and pointer to module base)
+
+;        DLINE   "Unsqueezing module"
+
+        ADD     R5, R6, R5      ; R5 -> byte after end of module
+        LDMDB   R5!, {R8-R12}   ; load all the data - R8=decoded size, R9=encoded size
+                                ; R10=tables size, R11=no. of shorts, R12=no. of longs
+
+        SUB     R10, R5, R10    ; R10 -> start (lowest address) of encoded tables
+                                ; = highest address +1 of encoded data
+        SUB     R9, R10, R9     ; R9 -> lowest address of encoded data
+
+;        DREG    R8, "Claiming block for module of size "
+
+        MOV     R3, R8          ; now claim a block for the expanded code
+        BL      RMAClaim_Chunk
+        BVS     ExpandFailed1
+
+;        DREG    R2, "Successfully claimed block for expanded code at "
+
+        MOV     R7, R2          ; R7 -> start of expanded module
+
+        ADD     R3, R11, R12    ; R3 = no. of shorts and longs
+        MOV     R3, R3, LSL #2  ; convert to bytes
+
+;        DREG    R3, "Claiming block for shorts+longs of size "
+
+        BL      RMAClaim_Chunk
+        BVS     ExpandFailed2   ; must free module block before exiting!
+
+;        DREG    R2, "Successfully claimed block for shorts+longs at "
+
+        MOV     R6, R2          ; R6 -> start of expanded table of shorts+longs
+
+        ADD     R8, R7, R8      ; R8 -> highest address of decoded image +1
+
+; We only need nLongs and nShorts while we are decoding the tables.
+; Afterwards we will re-use the registers for pointers to start of tables.
+
+        MOV   R5, R10       ; R5 is ptr into encoded tables
+        MOV   R4, #0        ; this is the first table el
+
+;        DLINE   "Expanding shorts+longs table"
+
+decodeTab
+        ; Require:  R11 -- no of els left to decode
+        ;           R6  -- ptr into decoded table
+        ;           R5  -- ptr into encoding
+        ;           R4  -- = 0 iff this is the shorts table (i.e. 4-byte vals)
+
+; I believe this loop could be made good deal smaller and possibly
+; faster, but it's only a couple of hundred bytes and it works.
+
+        MOV   R2, R6        ; stash away base of first table
+        MOV   R3, #-1       ; start as if previous entry was -1
+decodeEntry
+        SUBS  R11, R11, #1  ; while (--nEntries >= 0) {
+        BLT   decodedTab    ; assert: previous word is in R3
+        LDRB  R1, [R5], #1  ; byte = *p++
+        SUBS  R0, R1, #10
+        BGE   greaterThan9
+literalOrOnes
+        CMPS  R1, #0
+        BNE   ones
+literal
+        LDRB  R0, [R5], #1
+        LDRB  R1, [R5], #1
+        ORR   R0, R0, R1, LSL #8
+        LDRB  R1, [R5], #1
+        ORR   R0, R0, R1, LSL #16
+        CMPS  R4, #0                 ; in the 4-byte (short encodings) table?
+        LDREQB R1, [R5], #1          ; yes, so include the 4th byte
+        ORREQ  R0, R0, R1, LSL #24   ; in the resultant word
+        ADD   R3, R3, R0
+        STR   R3, [R6], #4
+        B     decodeEntry
+ones
+        SUB   R11, R11, R1
+        ADD   R11, R11, #1
+anotherOne        ; Have number of increment-by-ones in R1
+        ADD   R3, R3, #1
+        STR   R3, [R6], #4
+        SUBS  R1, R1, #1
+        BGT   anotherOne
+        B     decodeEntry
+greaterThan9
+        CMPS  R1, #92
+        ADDLT R3, R3, R0
+        STRLT R3, [R6], #4
+        BLT   decodeEntry
+greaterThan91
+        SUBS  R0, R1, #174
+        BLT   oneMore
+twoMore
+        LDRB  R1, [R5], #1
+        ORR   R0, R1, R0, LSL #16
+        LDRB  R1, [R5], #1
+        ORR   R0, R0, R1, LSL #8
+        ADD   R3, R3, R0
+        STR   R3, [R6], #4
+        B     decodeEntry
+oneMore
+        SUBS  R0, R1, #92
+        LDRB  R1, [R5], #1
+        ORR   R0, R1, R0, LSL #8
+        ADD   R3, R3, R0
+        STR   R3, [R6], #4
+        B     decodeEntry   ; } /* end while (--nEntries >= 0) { */
+
+decodedTab
+        CMPS  R4, #0        ; if isShorts then
+        BNE   finishLongs   ; else finishLongs
+finishShorts
+        MOV   R11, R12      ; no of els to decode = nLongs
+        MOV   R12, R2       ; R12 = &shorts[0]
+        MOV   R2, R6        ; stash away start of longs table
+        MOV   R4, #1        ; next table is longs
+        B     decodeTab
+finishLongs
+        MOV   R11, R2       ; R11 = &longs[0]
+
+;        DLINE   "Finished expanding shorts+longs table"
+
+decodedBothTabs
+        ; Now have:  R12 = &shorts[0]
+        ;            R11 = &longs[0]
+        ;            R10 = highest address +1 of encoded data
+        ;            R9  = lowest address of encoded data
+        ;            R8  = highest address +1 of decoded data
+        ;
+        ; R0..R7 are free for workspace
+
+;        DREG    R12, "Shorts table at "
+;        DREG    R11, "Longs table at "
+;        DREG    R9, "Encoded data start "
+;        DREG    R10, "Encoded data end+1 "
+;        DREG    R8, "Decoded data end+1 "
+
+decodePair
+        CMPS  R10, R9           ; Have we reached the base ?
+        BLE   doneDecode
+        LDRB  R6, [R10, #-1]!   ; byte value
+        ; The words will be put in R4 and R5, to be STMDB'd
+        AND   R3, R6, #15       ; first nibble
+        SUBS  R0, R3, #MinShort ; idx = (val - 8)
+        BLT   notshort0
+short0
+        LDRB  R1, [R10, #-1]!
+        ORR   R0, R1, R0, LSL #8
+        LDR   R4, [R12, R0, LSL #2]    ; w = shorts[(nibble-8)<<8 | *p--]
+        B     gotFirst
+notshort0
+        SUBS  R0, R3, #MinLong         ; idx = (val - 2)
+        BLT   notlong0
+long0
+        LDRB  R1, [R10, #-1]!
+        ORR   R0, R1, R0, LSL #8
+        LDR   R0, [R11, R0, LSL #2]    ; w = longs[(nibble-2)<<8 | *p--]
+        LDRB  R1, [R10, #-1]!
+        ORR   R4, R1, R0, LSL #8
+        B gotFirst
+notlong0
+        MOVS  R4, R3            ; TMD 13-Feb-90: combine 2 instructions here
+                                ; used to be CMPS R3,#0; MOVEQ R4,R3
+        BEQ   gotFirst
+literal0
+        LDRB  R0, [R10, #-1]!
+        LDRB  R1, [R10, #-1]!
+        ORR   R0, R0, R1, LSL #8
+        LDRB  R1, [R10, #-1]!
+        ORR   R0, R0, R1, LSL #16
+        LDRB  R1, [R10, #-1]!
+        ORR   R4, R0, R1, LSL #24
+
+gotFirst
+        ; Phew!  We have the first word of the pair (in R4), now we have
+        ; to do (almost) the same again, result in R5, and STMDB.
+
+        MOV   R3, R6, LSR #4     ; second nibble
+        SUBS  R0, R3, #MinShort  ; idx = (val - 8)
+        BLT   notshort1
+short1
+        LDRB  R1, [R10, #-1]!
+        ORR   R0, R1, R0, LSL #8
+        LDR   R5, [R12, R0, LSL #2]    ; w = shorts[(nibble-8)<<8 | *p--]
+        STMDB R8!, {R4,R5}
+        B     decodePair
+notshort1
+        SUBS  R0, R3, #MinLong        ; idx = (val - 2)
+        BLT   notlong1
+long1
+        LDRB  R1, [R10, #-1]!
+        ORR   R0, R1, R0, LSL #8
+        LDR   R0, [R11, R0, LSL #2]    ; w = longs[(nibble-2)<<8 | *p--]
+        LDRB  R1, [R10, #-1]!
+        ORR   R5, R1, R0, LSL #8
+        STMDB R8!, {R4,R5}
+        B     decodePair
+notlong1
+        MOVS  R5, R3            ; TMD 13-Feb-90: combine 2 instructions here
+                                ; used to be CMPS R3,#0; MOVEQ R5,R3
+
+                                       ; This doesn't pay off much
+        STMEQDB R8!, {R4,R5}           ; might be better to swap round
+        BEQ   decodePair               ; literal and zero, to save 3S on
+literal1                               ; the longer path ?
+        LDRB  R0, [R10, #-1]!
+        LDRB  R1, [R10, #-1]!          ; If I had the right byte-sex and
+        ORR   R0, R0, R1, LSL #8       ; a couple of registers to spare,
+        LDRB  R1, [R10, #-1]!          ; could do this in 15S instead of 22S
+        ORR   R0, R0, R1, LSL #16      ; using the load non-aligned word code
+        LDRB  R1, [R10, #-1]!          ; given in ARM CPU Manual.
+        ORR   R5, R0, R1, LSL #24
+        STMDB R8!, {R4,R5}
+        B decodePair
+
+doneDecode
+
+;        DREG    R8, "Finished decoding, module at "
+
+; now R8 -> the completely unsqueezed module
+
+; so first, free the shorts+longs table block
+; R12 -> shorts, which is first of the two
+
+        MOV     R2, R12
+
+;        DREG    R2, "Freeing shorts+longs table at "
+
+        BL      FreeRMABlock
+
+; ignore any error from this
+        MOV     R3, R8                  ; save pointer to expanded module
+        Pull    "R2,R7-R12"             ; pull pointer to original module base into R2 and restore other registers
+
+;        DREG    R2, "Freeing original module block at "
+
+        BL      FreeRMABlock            ; may fail because original module is in ROM, so ignore error
+
+;        DLINE   "Returning new module to OS"
+
+        STR     R3, [R9, #Module_code_pointer]  ; point module node at expanded module
+        Pull    PC,,^                   ; exit (VC)
+
+; come here if failed to claim block for tables
+
+ExpandFailed2
+
+;        DLINE   "Failed to claim table block, freeing module block"
+
+        Push    R0                      ; save original error pointer
+        MOV     R2, R7
+        BL      FreeRMABlock
+        Pull    R0                      ; restore error pointer, and drop thru to ...
+
+; come here if failed to claim block for expanded module
+
+ExpandFailed1
+        Pull    "R6-R12, lr"            ; restore registers
+        ORRS    PC, lr, #V_bit          ; and exit V set
+
+; subroutine to free a block in RMA
+; in: R2 -> block
+; out: R0,R1 corrupted
+
+FreeRMABlock ENTRY
+;        LDR     R0, [R2, #-4]
+;        DREG    R0, "FreeRMABlock called, block size purports to be "
+
+        MOV     R0, #HeapReason_Free
+        MOV     R1, #RMAAddress
+        SWI     XOS_Heap
+        EXIT
+
+;        InsertDebugRoutines
+
+        END
diff --git a/s/Utility b/s/Utility
new file mode 100644
index 0000000000000000000000000000000000000000..e2b8e2259f0375d5929b211df7292492724fd018
--- /dev/null
+++ b/s/Utility
@@ -0,0 +1,755 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+        TTL  => Utility
+
+
+; *****************************************************************************
+; Arthur Utility commands
+
+SysModules_Info ROUT     ; start of ROM modules chain
+        &       EndOfKernel-SysModules_Info
+
+UtilityMod
+        &       StartSuper-UtilityMod
+        &       0                               ; no initialisation
+        &       Util_Die-UtilityMod
+ [ DriversInKernel
+        &       0                               ; no services ta
+ |
+        &       Util_Service-UtilityMod         ; code to register printer buffer with Buffer manager
+ ]
+        &       UtilModTitle-UtilityMod
+        &       UtilHelpStr-UtilityMod
+        &       UtilHelpTab-UtilityMod
+        &       0
+        &       0
+        &       0
+        &       0
+ [ International_Help <> 0
+        &       MessageFileName-UtilityMod
+ |
+        &       0
+ ]
+
+Module_BaseAddr SETA UtilityMod
+
+ [ :LNOT: DriversInKernel
+Util_Service ROUT
+        TEQ     r1, #Service_BufferStarting
+        TEQNE   r1, #Service_DeviceFSCloseRequest
+        MOVNES  pc, lr
+
+        TEQ     r1, #Service_BufferStarting
+        BNE     %FT10
+        Push    "r0-r3,lr"
+
+; Register buffers with buffer manager in order of speed - first is fast
+
+ [ MouseBufferManager
+        MOV     r0, #BufferFlags_GenerateInputFull
+        LDR     r1, =MouseBuff
+        LDR     r2, =MouseBuff + MouseBuffSize
+        MOV     r3, #Buff_Mouse
+        SWI     XBuffer_Register                ; register mouse input buffer
+ ]
+        MOV     r0, #BufferFlags_GenerateInputFull :OR: BufferFlags_SendThresholdUpCalls
+        LDR     r1, =RS423InBuff
+        LDR     r2, =RS423InBuff + RS423InBuffSize
+        MOV     r3, #Buff_RS423In
+        SWI     XBuffer_Register                ; register serial input buffer
+
+        MOV     r0, #BufferFlags_GenerateOutputEmpty
+        LDR     r1, =RS423OutBuff
+        LDR     r2, =RS423OutBuff + RS423OutBuffSize
+        MOV     r3, #Buff_RS423Out
+        SWI     XBuffer_Register                ; register serial output buffer
+
+        MOV     r0, #0                          ; used as index to PrinterBufferThing
+        LDR     r1, [r0, #PrinterBufferAddr]    ; r1 -> start of buffer
+        LDR     r2, [r0, #PrinterBufferSize]    ; r2 = size
+        ADD     r2, r2, r1                      ; r2 -> end+1 of buffer
+        MOV     r3, #Buff_Print
+        MOV     r0, #BufferFlags_GenerateOutputEmpty
+        SWI     XBuffer_Register                ; register the MOS's printer buffer
+
+        Pull    "r0-r3,pc"
+
+; service DeviceFSCloseRequest
+; in:   r2 = handle we are requested to close
+;       if r2 = PrinterActive (word) or SerialInHandle (byte) or SerialOutHandle (byte)
+;       then zero appropriate variable and close file, then claim service
+;       NB there is a disadvantage to doing it for SerialInHandle, in that it won't get
+;       opened again, but we assume they accidentally left it in an fx2,2 state
+;
+
+10
+        Push    "r0,r1,lr"
+        MOV     r0, #0
+        LDR     r1, [r0, #OsbyteVars + :INDEX: PrinterActive]
+        TEQ     r1, r2
+        STREQ   r0, [r0, #OsbyteVars + :INDEX: PrinterActive]
+        BEQ     %FT20
+        LDRB    r1, [r0, #OsbyteVars + :INDEX: SerialInHandle]
+        TEQ     r1, r2
+        STREQB  r0, [r0, #OsbyteVars + :INDEX: SerialInHandle]
+        BEQ     %FT20
+        LDRB    r1, [r0, #OsbyteVars + :INDEX: SerialOutHandle]
+        TEQ     r1, r2
+        Pull    "r0,r1,pc",NE,^                 ; if not any of these then exit preserving everything
+        STRB    r0, [r0, #OsbyteVars + :INDEX: SerialOutHandle]
+20
+        SWI     XOS_Find                        ; close file, ignore errors (if we get an error it's closed anyway)
+        MOV     r1, #Service_Serviced           ; indicate we closed it
+        Pull    "r0,lr,pc",,^                   ; restore r0, junk r1, and exit restoring flags
+
+ ]
+
+Util_Die   ROUT
+           CMP   R10, #0
+           MOVEQ PC, lr      ; non-fatal : can cope
+           ADR   R0, %FT01
+ [ International
+           Push  "lr"
+           BL    TranslateError
+           Pull  "pc"
+ |
+           ORRS  PC, lr, #V_bit
+ ]
+
+01
+           &     ErrorNumber_CantKill
+ [ International
+           =    "CantKill", 0
+ |
+           =    "Deleting the utility module is foolish", 0
+ ]
+
+
+UtilModTitle =  "UtilityModule", 0
+
+UtilHelpStr  =  "MOS Utilities", 9, "$VersionNo", 0
+
+UtilHelpTab
+           Command   Break,       0,  0, International_Help   ; just help
+           Command   Configure, 255,  0, Help_Is_Code_Flag :OR: International_Help
+           Command   Commands,    0,  0, Help_Is_Code_Flag :OR: International_Help
+           Command   Echo,      255,  0, International_Help
+           Command   Error,     255,  1, International_Help
+           Command   Eval,      255,  1, International_Help
+           Command   FileCommands,0,  0, Help_Is_Code_Flag :OR: International_Help
+           Command   GOS,         0,  0, International_Help
+           Command   IF,        255,  2, International_Help
+           Command   Ignore,      1,  0, International_Help
+           Command   Modules,     0,  0, Help_Is_Code_Flag :OR: International_Help
+           Command   PowerOn,     0,  0, International_Help   ; just help
+           Command   Reset,       0,  0, International_Help   ; just help
+           Command   RMClear,     0,  0, International_Help
+           Command   RMEnsure,  255,  2, International_Help
+           Command   RMFaster,    1,  1, International_Help
+           Command   RMInsert,    2,  1, International_Help
+           Command   RMKill,      1,  1, International_Help
+           Command   RMLoad,    255,  1, International_Help
+           Command   RMReInit,  255,  1, International_Help
+           Command   RMRun,     255,  1, International_Help
+           Command   RMTidy,      0,  0, International_Help
+           Command   ROMModules,  0,  0, International_Help
+           Command   Set,       255,  2, International_Help
+           Command   SetEval,   255,  2, International_Help
+           Command   SetMacro,  255,  2, International_Help
+           Command   Show,        1,  0, International_Help   ; *show = *show *
+           Command   Status,    255,  0, International_Help
+           Command   Syntax,      0,  0, International_Help
+           Command   Time,        0,  0, International_Help
+           Command   Unplug,      2,  0, International_Help
+           Command   Unset,       1,  1, International_Help
+           =   0
+
+Configure_Syntax     * Module_BaseAddr
+Commands_Code        * Module_BaseAddr
+Commands_Syntax      * Module_BaseAddr
+Syntax_Code          * Module_BaseAddr
+Syntax_Syntax        * Module_BaseAddr
+Echo_Syntax          * Module_BaseAddr
+Status_Syntax        * Module_BaseAddr
+FileCommands_Code    * Module_BaseAddr
+FileCommands_Syntax  * Module_BaseAddr
+Reset_Code           * Module_BaseAddr
+Reset_Syntax         * Module_BaseAddr
+Break_Code           * Module_BaseAddr
+Break_Syntax         * Module_BaseAddr
+PowerOn_Code         * Module_BaseAddr
+PowerOn_Syntax       * Module_BaseAddr
+
+RMFaster_Code
+           Push "lr"
+           MOV R1, R0
+           MOV R0, #ModHandReason_LookupName
+           SWI XOS_Module
+           Pull "PC", VS
+           CMP   R3, #ROM
+           BLT   RMFast_notinROM
+           MOV   R1, R3
+           LDR   R2, [R1, #-4]
+           MOV   R0, #ModHandReason_CopyArea
+           SWI   XOS_Module
+           Pull  PC
+
+RMFast_notinROM
+           ADRL R0, ErrorBlock_RMNotFoundInROM
+         [ International
+           BL   TranslateError
+         ]
+           Pull lr
+           ORRS PC, lr, #V_bit
+
+RMKill_Code
+           MOV R6, #ModHandReason_Delete
+
+Rmcommon   Push "lr"
+           MOV r1, r0
+           MOV r0, r6
+           SWI   XOS_Module
+           Pull "PC"
+
+RMLoad_Code
+           MOV R6, #ModHandReason_Load
+           B   Rmcommon
+
+RMRun_Code
+           MOV R6, #ModHandReason_Run
+           B   Rmcommon
+
+RMTidy_Code
+           MOV R6, #ModHandReason_Tidy
+           B   Rmcommon
+
+RMClear_Code
+           MOV R6, #ModHandReason_Clear
+           B   Rmcommon
+
+RMReInit_Code
+           MOV R6, #ModHandReason_ReInit
+           B   Rmcommon
+
+Modules_Help   ROUT
+          Push  "r2, r3, lr"
+          MOV   r0, #0                          ; Try our own message file before Global.
+          LDR   r0, [r0, #KernelMessagesBlock]
+          TEQ   r0, #0
+          ADRNE r0, KernelMessagesBlock+4
+          ADRL  r1, modules_help1
+          MOV   r2, #0
+          SWI   XMessageTrans_Lookup
+          SWIVC XMessageTrans_Dictionary
+          MOVVC r1, r0
+          MOVVC r0, r2
+          SWIVC XOS_PrettyPrint
+          Pull  "r2, r3, PC", VS
+          MOV    R1, #Module_List
+03        LDR    R1, [R1]
+          CMP    R1, #0
+          BEQ    %FT05
+          LDR    R0, [R1, #Module_code_pointer]
+          BL     PrintTitle
+          BVC    %BT03
+05        MOVVC  R0, #0
+          Pull  "r2, r3, PC"
+
+PrintTitle ; of module at R0 : corrupts R0
+        Push  "R1, lr"
+        LDR    R1, [R0, #Module_HelpStr]
+        CMP    R1, #0
+        ADREQ  R0, NoRIT
+      [ International
+        BLEQ   Write0_Translated
+        ADDNE  R0, R1, R0
+        SWINE  XOS_PrettyPrint
+      |
+        ADDNE  R0, R1, R0
+        SWI    XOS_PrettyPrint
+      ]
+        SWIVC  XOS_NewLine
+        Pull  "R1, PC"
+
+Modules_Code ROUT
+        Push   "R7, lr"
+
+      [ International
+        BL     WriteS_Translated
+        =      "Modules:No. Position Workspace Name", 10, 13, 0
+        ALIGN
+      |
+        SWI     XOS_WriteS
+        =      "No. Position Workspace Name", 10, 13, 0
+        ALIGN
+      ]
+        Pull   "R7, PC", VS
+
+        MOV     R1, #0
+        MOV     R2, #0
+        MOV     R6, #0
+        MOV     R7, #0
+06
+        SWI     XOS_ReadEscapeState
+        Pull    "R7, lr", CS
+        BCS     AckEscape
+        MOV     R0, #ModHandReason_GetNames
+        SWI     XOS_Module
+        Pull   "R7, lr", VS
+        BICVSS  PC, lr, #V_bit           ; back, clearing V
+
+        Push   "R1, R2"
+        CMP     R6, #0
+        MOVNE   R1, #0
+        BNE     %FT02
+        ADD     R7, R7, #1
+        MOV     R0, R7
+        LDR     R1, =GeneralMOSBuffer
+        MOV     R2, #256
+        SWI     XOS_ConvertCardinal2
+        SUB     R1, R1, R0          ; characters in buffer
+02      CMP     R1, #3
+        SWILT   XOS_WriteI+" "
+        BVS     %FT03
+        ADDLT   R1, R1, #1
+        BLT     %BT02
+03
+        Pull   "R1, R2"
+        BVS     %FT04
+        CMP     R6, #0
+        SWIEQ   XOS_Write0
+        SWIVC   XOS_WriteI+" "
+        MOV     R0, R3
+        BLVC    HexR0LongWord
+        SWIVC   XOS_WriteI+" "
+        MOV     R0, R4
+        BLVC    HexR0LongWord
+        SWIVC   XOS_WriteI+" "
+        SWIVC   XOS_WriteI+" "
+        BLVC    %FT01         ; title out
+        SWIVC   XOS_NewLine
+        BVC     %BT06
+04
+        Pull   "R7, PC"
+01
+        Push   "lr"
+        LDR     R0, [R3, #Module_Title]
+        CMP     R0, #0
+        ADDNE   R0, R3, R0
+        ADREQ   R0, NoRIT
+      [ International
+        BLEQ    Write0_Translated
+        SWINE   XOS_Write0
+      |
+        SWI     XOS_Write0
+      ]
+        Pull   "PC", VS
+        CMP     R6, #0
+        CMPEQ   R2, #0
+        MOV     R6, R2
+        Pull   "PC", EQ       ; only one incarnation
+        SWI     XOS_WriteI + Postfix_Separator
+        MOV     R0, R5
+        SWIVC   XOS_Write0
+        Pull   "PC"
+
+      [ International
+NoRIT   =   "Untitled:<Untitled>", 0
+      |
+NoRIT   =   "<Untitled>", 0
+      ]
+starstr =   "*", 13
+        ALIGN
+
+
+
+Show_Code
+        CMP     r1, #0                  ; *show only?
+        ADREQ   r0, starstr
+        Entry   "r0",8+256
+
+        ADD     r6, sp, #8              ; initial buffer for var expansions
+        MOV     lr, #256+4
+        STR     lr, [sp, #4]            ; fake heap block size
+        MOV     lr, #&ffffffff
+        STR     lr, [sp, #0]            ; inhibit page mode off on exit
+
+        ; Read current VDU status and save it away
+        MOVVC   r0, #117
+        SWIVC   XOS_Byte
+        STRVC   R1, [sp, #0]
+        SWIVC   XOS_WriteI+14           ; paged mode on.
+        BVS     ShowBang
+
+        MOV     r3, #0          ; enumeration pointer
+01
+        LDR     r0, [sp, #Proc_LocalStack + 0*4]        ; wildcard
+10
+        MOV     r1, r6          ; 'heap' block
+        LDR     r2, [r6, #-4]   ; block size
+        SUB     r2, r2, #4
+        MOV     r4, #0          ; no expansion
+        SWI     XOS_ReadVarVal
+        BVS     Show_ErrorReading
+
+        ; Varname
+        MOV     r0, r3
+        SWI     XOS_Write0
+        BVS     ShowBang
+
+        ; (Number) or (Macro) as appropriate
+        CMP     R4, #VarType_String
+        BEQ     skipvalprt
+      [ :LNOT: International
+        SWI     XOS_WriteS
+        =       " (", 0
+        ALIGN
+        BVS     ShowBang
+      ]
+        CMP     R4, #VarType_Number
+        MOVEQ   R2, #256
+        LDREQ   R0, [R1]
+        SWIEQ   XOS_BinaryToDecimal
+        ADREQ   R0, %FT02
+        ADRHI   R0, %FT03
+      [ International
+        BLVC    Write0_Translated
+      |
+        SWIVC   XOS_Write0
+        SWIVC   XOS_WriteI+")"
+      ]
+        BVS     ShowBang
+skipvalprt
+        ; " : "
+        SWI     XOS_WriteS
+        =       " : ", 0
+        ALIGN
+        BVS     ShowBang
+
+        ; Now output the value's value
+        MOV     R5, #-1
+05      ADD     R5, R5, #1
+        CMP     R5, R2
+        BEQ     %FT06
+        SWI     XOS_ReadEscapeState
+        BLCS    AckEscape
+        BVS     ShowBang
+        LDRB    R0, [R1, R5]
+        CMP     R0, #&7F
+        MOVEQ   R0, #"?"-"@"
+        CMP     R0, #31
+        ADDLE   R0, R0, #"@"
+        SWILE   XOS_WriteI+"|"
+        BVS     ShowBang
+
+        CMP     R0, #"|"
+        CMPNE   R0, #""""
+        CMPNE   R0, #"<"
+        SWINE   XOS_WriteC
+        BVS     ShowBang
+        BNE     %BT05
+
+        CMP     R4, #VarType_Macro
+        SWINE   XOS_WriteI+"|"
+        SWIVC   XOS_WriteC
+        BVC     %BT05
+ShowBang
+        STR     r0, [sp, #Proc_LocalStack + 0*4]
+        LDR     lr, [sp, #Proc_LocalStack + 1*4]        ; lr in
+        ORR     lr, lr, #V_bit
+        STR     lr, [sp, #Proc_LocalStack + 1*4]
+
+Show_Exit
+        ; Release buffer if necessary
+        ADD     lr, sp, #8              ; stack buffer
+        TEQ     r6, lr
+        MOVNE   r0, #ModHandReason_Free
+        MOVNE   r2, r6
+        SWINE   XOS_Module
+
+        ; Switch paged mode off if necessary
+        LDR     lr, [sp, #0]
+        TST     lr, #5
+        SWIEQ   XOS_WriteI+15  ; paged mode off
+
+        EXITS
+
+06      SWI     XOS_NewLine
+        BVS     ShowBang
+        B       %BT01
+
+Show_ErrorReading
+        ; Error from OS_ReadVarVal:
+        ; VarCantFind - end of *show
+        ; BuffOverflow - try and extend buffer
+        ; other - return as error
+        LDR     r5, [r0]
+        LDR     lr, =ErrorNumber_VarCantFind
+        TEQ     r5, lr
+        BEQ     Show_Exit
+        LDR     lr, =ErrorNumber_BuffOverflow
+        TEQ     r5, lr
+        BNE     ShowBang
+
+        ; try and extend the buffer
+        CLRV
+        MOV     r1, r3                  ; actual name so retry gets this node exactly
+        ADD     lr, sp, #8              ; stack buffer
+        TEQ     r6, lr
+        MOV     r0, #ModHandReason_Free
+        MOV     r2, r6
+        MOV     r6, lr                  ; to prevent any attempt at freeing in ShowBang
+        SWINE   XOS_Module
+        BVS     ShowBang
+
+        MOV     r0, r1
+        MOV     r1, #ARM_CC_Mask
+        MOV     r2, #-1                 ; To sence size
+        MOV     r3, #0                  ; 1st var
+        MOV     r4, #0                  ; unexpanded
+        SWI     XOS_ReadVarVal
+        CLRV                            ; error will be buffer overflow
+        MOV     r1, r3                  ; varname again
+        MVN     r3, r2
+        ADD     r3, r3, #&ff            ; round up to 256 byte boundary
+        BIC     r3, r3, #&ff
+        MOV     r0, #ModHandReason_Claim
+        SWI     XOS_Module
+        BVS     ShowBang
+        MOV     r6, r2
+        MOV     r0, r1
+        MOV     r3, #0                  ; restart from that node
+        B       %BT10
+
+      [ International
+02
+        =       "Number:(Number)", 0
+03
+        =       "Macro:(Macro)", 0
+      |
+02
+        =       "Number", 0
+03
+        =       "Macro", 0
+      ]
+
+Set_Code ROUT
+        MOV    R4, #VarType_String
+01
+        Push  "lr"
+
+        ; space terminated name in R0
+
+        ; Skip name in R1
+        MOV    R1, R0
+02      LDRB   R2, [R1], #1
+        CMP    R2, #" "
+        BNE    %BT02
+
+        ; Then skip spaces
+03      LDRB   R2, [R1], #1
+        CMP    R2, #" "
+        BEQ    %BT03
+        SUB    R1, R1, #1
+
+        ; r2 +ve to set, r3 = 0 for 1st var
+        MOV    R2, #1
+        MOV    R3, #0
+        SWI    XOS_SetVarVal
+        Pull  "PC"
+
+        LTORG
+
+SetMacro_Code MOV    R4, #VarType_Macro
+        B  %BT01
+
+SetEval_Code MOV    R4, #VarType_Expanded
+        B  %BT01
+
+Unset_Code ROUT
+        Push  "lr"
+        MOV    R2, #-1
+        MOV    R3, #0
+01      SWI    XOS_SetVarVal
+        BVC    %BT01
+        Pull  "lr"
+        BICS   PC, lr, #V_bit
+
+Echo_Code ROUT
+        Push  "lr"
+        MOV    R2, #GS_NoQuoteMess
+        SWI    XOS_GSInit
+01      SWI    XOS_GSRead
+        BVS    %FT02
+        MOVCC  R3, R0
+        MOVCC  R0, R1
+        SWICC  XOS_WriteC
+        BVS    %FT02
+        MOVCC  R0, R3
+        BCC    %BT01
+        SWI    XOS_NewLine
+02
+        Pull  "PC"
+
+Commands_Help  ROUT
+        Push  "R0, lr"         ; keep buffer pointer
+        ADRL   R0, commands_helpstr
+        MOV    R1, #0
+KeyHelpCommon                  ; also used by *Configure
+        Push   r1
+; R2 & R3 can be junked here?
+        MOV    r1, r0
+        MOV    r0, #0          ; Try our own message file before Global.
+        LDR    r0, [r0, #KernelMessagesBlock]
+        TEQ    r0, #0
+        ADRNE  r0, KernelMessagesBlock+4
+        MOV    r2, #0
+        SWI    XMessageTrans_Lookup
+        SWIVC  XMessageTrans_Dictionary
+        MOVVC  r1, r0
+        MOVVC  r0, r2
+        SWIVC  XOS_PrettyPrint
+        Pull   "r1,lr,PC", VS  ; if error, pull R1, junk stacked R0 and exit
+        Pull   "r1,r3"         ; restore r1 and get buffer pointer
+        MOV    r0, #0
+        ADRL   R2, SysCommsModule
+        BL     OneModuleK
+        BVS    %FT10
+        MOV    R6, #Module_List
+12      LDR    R6, [R6]
+        CMP    R6, #0
+        BEQ    %FT10
+        LDR    R2, [R6, #Module_code_pointer]
+        BL     OneModuleK
+        BVC    %BT12
+10      MOVVC  R0, #0
+        Pull  "PC"
+
+FileCommands_Help
+        Push  "R0, lr"
+        ADRL   R0, fscommands_helpstr
+        MOV    R1, #FS_Command_Flag
+        B      KeyHelpCommon
+
+; take module code pointer in r2
+;                    flags in r1
+;    HelpBufferSize buffer in r3
+;          string to print in r0
+
+OneModuleK     ROUT
+        Push  "r2-r6, lr"
+        LDR    R4, [R2, #Module_HC_Table]
+        CMP    R4, #0
+        Pull  "r2-r6, PC", EQ       ; no table
+        ORR    R3, R3, #&80000000   ; buffer position ptr and flag
+        MOV    R5, #0               ; buffer offset
+
+        ADD    R2, R2, R4           ; point at table start.
+03      MOV    R6, R2
+        LDRB   R4, [R2]
+        CMP    R4, #0
+        BEQ    %FT06
+
+04      LDRB   R4, [R6], #1
+        CMP    R4, #0
+        BNE    %BT04
+        ADD    lr, R6, #3
+        BIC    lr, lr, #3           ; align but leave r6 at end of command for below (05)
+        LDR    R4, [lr, #0]         ; code offset
+        CMP    r1, #-1              ; fudge?
+        BEQ    %FT78
+        CMP    R4, #0
+        ADDEQ  R2, lr, #16
+        BEQ    %BT03
+        LDRB   R4, [lr, #7]
+        BIC    R4, R4, #(Help_Is_Code_Flag:OR:International_Help) :SHR: 24
+        CMP    R4, R1, LSR #24      ; move flags into bottom byte
+79      ADDNE  R2, lr, #16
+        BNE    %BT03
+        TST    R3, #&80000000
+        BEQ    %FT05
+        SWI    XOS_NewLine
+        SWIVC  XOS_NewLine
+        BVS    %FT77
+        MOV    r4, r0
+        CMP    r0, #0
+        BEQ    OneModuleK_PrintTitle ; Don't trust MessageTrans to preserve Z
+        SWI    XMessageTrans_Dictionary
+        STMDB  sp!, {r1}
+        MOVVC  r1, r0
+        MOVVC  r0, r4
+        SWIVC  XOS_PrettyPrint
+        LDMIA  sp!, {r1}
+        B      %FT77
+OneModuleK_PrintTitle
+        LDR    r0, [stack]
+        BL     PrintTitle
+77
+        MOVVC  r0, r4
+        Pull  "r2-r6, PC", VS
+        BIC    R3, R3, #&80000000
+05
+        SUB    lr, r6, r2
+        RSB    r4, r5, #HelpBufferSize
+        CMP    r4, lr                   ; have we got enough space for command+tab
+        BCS    %FT07
+        MOV    r4, #0                   ; no, so 0 terminate what we've got and print it
+        SUB    r5, r5, #1               ; write 0 over trailing tab
+        STRB   r4, [r3, r5]
+        MOV    r4, r0
+        MOV    r0, r3
+        SWI    XOS_PrettyPrint
+        SWIVC  XOS_NewLine
+        Pull   "r2-r6,pc",VS
+        MOV    r0, r4
+        MOV    r5, #0                   ; start again with empty buffer
+07
+        LDRB   r4, [r2], #1             ; copy command
+        CMP    r4, #0
+        STRNEB r4, [r3, r5]
+        ADDNE  r5, r5, #1
+        BNE    %BT07
+        MOV    r4, #TAB                 ; add tab
+        STRB   r4, [r3, r5]
+        ADD    r5, r5, #1
+        ADD    r2, r2, #3+16            ; align and move on to next command
+        BIC    r2, r2, #3
+        B      %BT03
+
+78      CMP    r4, #0
+        B      %BT79
+
+06      TST    R3, #&80000000
+        Pull  "r2-r6, PC", NE
+        Push  "R0"
+        MOV    R0, #0
+        SUB    R5, R5, #1
+        STRB   R0, [R5, R3]
+        MOV    R0, R3
+        SWI    XOS_PrettyPrint
+        STRVS  r0, [stack]
+        Pull  "R0, r2-r6, PC"
+
+; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+GOS_Code
+        Push    lr
+        MOV     r2, r0
+        addr    R1, UtilModTitle
+        MOV     R0, #ModHandReason_Enter
+        SWI     XOS_Module
+        Pull    pc
+
+        END
diff --git a/s/vdu/vdu23 b/s/vdu/vdu23
new file mode 100644
index 0000000000000000000000000000000000000000..601dec2f8afe9d52a12e02eb5d03633a098e9d6f
--- /dev/null
+++ b/s/vdu/vdu23
@@ -0,0 +1,1677 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.Vdu23
+
+; *****************************************************************************
+;
+;       DefineChar - define soft character
+;
+; in:   R0 = char to be defined
+;
+
+DefineChar
+        ADD     R1, WsPtr, #(Font-32*8)
+        ADD     R1, R1, R0, LSL #3              ; point to char definition
+
+        ADD     R2, WsPtr, #QQ+1
+        LDMIA   R2, {R2, R3}                    ; load 8 bytes
+        STMIA   R1, {R2, R3}                    ; store 8 bytes
+Nop
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       VDU 23,7,m,d,z| - Scroll window directly
+;
+;       m=0     Scroll text window
+;       m=1     Scroll entire screen
+;
+;       d=0     Scroll right
+;       d=1     Scroll left
+;       d=2     Scroll down
+;       d=3     Scroll up
+;       d=4     Scroll "right"
+;       d=5     Scroll "left"
+;       d=6     Scroll "down"
+;       d=7     Scroll "up"
+;
+;       z=0     Scroll by 1 character cell
+;       z=1     Scroll by 1 char vertically, 1 byte (whatever that is) horiz.
+;
+
+Vdu23_7 ROUT
+        LDRB    R0, [WsPtr, #QQ+1]      ; R0 := m
+        LDRB    R1, [WsPtr, #QQ+2]      ; R1 := d
+        LDRB    R2, [WsPtr, #QQ+3]      ; R2 := z
+Scroll012
+        LDR     R6, [WsPtr, #CursorFlags]
+        AND     R1, R1, #7              ; ignore higher bits
+        CMP     R1, #4
+        BCC     %FT10
+        LDR     R4, ScrollDirWord
+        CMP     R1, #6
+        MOVCS   R4, R4, ROR #16
+        AND     R3, R6, #&0E
+        MOV     R3, R3, LSL #1
+        MOV     R4, R4, LSR R3
+        MOVS    R1, R1, LSR #1
+        AND     R1, R4, #&0F
+        EORCS   R1, R1, #1
+10
+        Push    R14
+        TST     R6, #ClipBoxEnableBit   ; if calculating clip box
+        BLNE    ClipScroll              ; then add text window or whole screen
+        CMP     R0, #1                  ; C=1 => whole screen
+        ADC     R1, R1, R1              ; put m into bit 0
+        TEQ     R2, #0
+        ORRNE   R1, R1, #&08            ; put z into bit 3
+        ADR     R0, ScrollTab
+        LDR     R2, [R0, R1, LSL #2]
+        ADR     R14, ScrollRetn
+        ADD     PC, R0, R2              ; carry on entry is m
+
+ScrollRetn
+        Pull    R14
+
+; insert code here to re-address input cursor as well
+
+        B       AddressCursors          ; re-address cursor positions
+
+ScrollDirWord
+        &       &33221010
+
+ScrollTab
+        &       ScrollRightChar-ScrollTab ; window right char
+        &       ScrollRightChar-ScrollTab ; screen right char
+        &       ScrollLeftChar-ScrollTab ; window left char
+        &       ScrollLeftChar-ScrollTab ; screen left char
+        &       ScrollDown-ScrollTab    ; window down char
+        &       HardScrollDown-ScrollTab ; screen down char
+        &       ScrollUp-ScrollTab      ; window up char
+        &       HardScrollUp-ScrollTab  ; screen up char
+        &       ScrollRightByte-ScrollTab ; window right byte
+        &       ScrollRightByte-ScrollTab ; screen right byte
+        &       ScrollLeftByte-ScrollTab ; window left byte
+        &       ScrollLeftByte-ScrollTab ; screen left byte
+        &       ScrollDown-ScrollTab    ; window down byte
+        &       HardScrollDown-ScrollTab ; screen down byte
+        &       ScrollUp-ScrollTab      ; window up byte
+        &       HardScrollUp-ScrollTab  ; screen up byte
+
+
+; *****************************************************************************
+;
+;       VDU 23,17,c,t| - Set tint
+;
+;       c=0     Set text foreground tint (default &FF)
+;       c=1     Set text background tint (default &00)
+;       c=2     Set graphics foreground tint (default &FF)
+;       c=3     Set graphics background tint (default 0)
+;       c=4     t=0 => BBC compatible ECFs, t=1 => native ECFs (default 0)
+;       c=5     Swap foreground and background text colours/tints
+;       c=6     VDU 23,17,6,x;y; - Set ECF origin to (x,y)
+;       c=7     VDU 23,17,7,flags,x;y;0,0 - Set character size(s)
+;       c=8-255 Reserved
+;
+;       t=tint  Only bits 6,7 relevant with this hardware
+
+Vdu23_17
+        LDRB    R0, [WsPtr, #QQ+1]              ; get c
+        CMP     R0, #7
+        MOVHI   R0, #17                         ; if unknown (>7), reload R0
+        BHI     UnknownVdu23                    ; and call UKVDU23 vector
+        BEQ     SetCharSizes
+
+        CMP     R0, #6
+        BEQ     SetECFOrigin
+
+        CMP     R0, #4
+        BHI     SwapColours                     ; 5 => swap colours
+
+        LDRB    R1, [WsPtr, #QQ+2]              ; tint or BBC parm
+        STREQ   R1, [WsPtr, #BBCcompatibleECFs] ; VDU 23,17,4,t...
+        MOVEQ   PC, R14
+
+        ADD     R2, WsPtr, #TFTint
+        LDR     R3, [R2, R0, LSL #2]            ; get old tint
+        STR     R1, [R2, R0, LSL #2]
+
+        CMP     R0, #2                          ; graphics tint ?
+        BCS     SetColour                       ; then update masks
+
+        EOR     R1, R1, R3
+        TST     R1, #&C0                        ; are top two bits different ?
+        MOVEQ   PC, R14                         ; no need to redo table
+
+        ; amg: use R1 here instead of 'bpp' which is R0. I need R0 in a moment.
+        LDR     R1, [WsPtr, #BitsPerPix]
+        CMP     R1, #8                          ; are we in 8 bits-per-pixel or more?
+        MOVCC   PC, R14                         ; no, then exit
+
+        Push    "lr"
+
+        ; amg: only do foreground or background as appropriate
+        CMP     R0,#1
+        BLNE    CompileTextFg
+        BLEQ    CompileTextBg                   ; update TextFgColour/TextBgColour
+
+        Pull    "lr"
+
+SetColoursDirty
+        LDR     R6, [WsPtr, #CursorFlags]
+        ORR     R6, R6, #TEUpdate               ; yes, then set colours dirty
+        STR     R6, [WsPtr, #CursorFlags]
+        MOV     PC, R14
+
+; *****************************************************************************
+
+SwapColours ROUT
+        LDR     R1, [WsPtr, #TForeCol]
+        LDR     R2, [WsPtr, #TBackCol]
+        STR     R1, [WsPtr, #TBackCol]
+        STR     R2, [WsPtr, #TForeCol]
+
+        LDR     R1, [WsPtr, #TextFgColour]
+        LDR     R2, [WsPtr, #TextBgColour]
+        STR     R1, [WsPtr, #TextBgColour]
+        STR     R2, [WsPtr, #TextFgColour]
+
+        LDR     R3, [WsPtr, #BitsPerPix]
+        CMP     R3, #8                          ; C=1 <=> 256 colour mode
+        BCC     %FT10                           ; [not 256 colour mode]
+
+        LDR     R1, [WsPtr, #TFTint]
+        LDR     R2, [WsPtr, #TBTint]
+        STR     R1, [WsPtr, #TBTint]
+        STR     R2, [WsPtr, #TFTint]
+10
+        B       SetColoursDirty                 ; don't bother optimising the case
+                                                ; when both colours are the same
+
+; *****************************************************************************
+;
+;       SetECFOrigin - Set the origin of the ECF pattern
+;
+;       Called by VDU 23,17,6,x;y;0,0,0
+;
+; in:   QQ+2, QQ+3 = xlo, xhi
+;       QQ+4, QQ+5 = ylo, yhi
+;
+
+SetECFOrigin ROUT
+
+; *****Change made by DJS
+; Original code was:
+;        LDRB    R0, [WsPtr, #QQ+3]      ; xhi
+;        LDRB    R1, [WsPtr, #QQ+2]      ; xlo
+;        PackXtnd R0, R0, R1             ; pack 2 bytes and sign extend
+;        LDRB    R1, [WsPtr, #QQ+5]      ; yhi
+;        LDRB    R2, [WsPtr, #QQ+4]      ; ylo
+;        PackXtnd R1, R1, R2             ; pack 2 bytes and sign extend
+
+        LoadCoordPair R0, R1, WsPtr, QQ+2
+
+; *****End of change made by DJS
+
+SetECFOriginCommon
+        Push    R14
+        ADD     R8, WsPtr, #GCsX        ; save ECursor away, cos EIG changes it
+        LDMIA   R8, {R6, R7}            ; and we don't want it to!
+
+        MOV     R2, #&FF                ; convert external-to-internal
+        BL      EIG                     ; as absolute coordinates
+
+; now R0 = internal X; R1 = internal Y
+
+        LDR     R3, [WsPtr, #NPix]
+        LDR     R4, [WsPtr, #Log2BPC]
+
+        AND     R0, R0, R3              ; X MOD (number of pixels per word)
+
+; *****Change made by DJS
+; Original code was:
+;        MOVS    R0, R0, LSL R4          ; number of bits to rotate ECF left by
+;        MOVEQ   R0, #32                 ; if zero then make 32
+;        RSB     R0, R0, #32             ; so we make it zero again!
+; I don't think anything cares whether this value is 0 or 32 in this case - it
+; is only used as Rm in a couple of MOV Rn, Rn, ROR Rm instructions. So change
+; to:
+        MOV     R0, R0, LSL R4          ; number of bits to rotate ECF left by
+        RSB     R0, R0, #32
+; *****End of change made by DJS
+
+        STR     R0, [WsPtr, #ECFShift]  ; number of bits to rotate right by
+
+        LDR     R3, [WsPtr, #YWindLimit]
+; *****Change made by DJS
+; Need to increase R3 by one to produce correct alignment of the ECF patterns,
+; as YWindLimit is an inclusive bound, not an exclusive one.
+        ADD     R3, R3, #1
+; *****End of change made by DJS
+        SUB     R1, R3, R1              ; invert Y
+        AND     R1, R1, #7              ; modulo 8
+        STR     R1, [WsPtr, #ECFYOffset]
+
+        STMIA   R8, {R6, R7}
+
+        BL      SetColour               ; update colours now
+
+        Pull    PC
+
+; *****************************************************************************
+;
+;       SWISetECFOrigin - Entry point for SWI OS_SetECFOrigin
+;
+; in:   R0 = x-coordinate (external coords)
+;       R1 = y-coordinate (-------""------)
+;
+
+SWISetECFOrigin ROUT
+        Push    "R0-R9, R14"
+        VDWS    WsPtr
+        BL      SetECFOriginCommon
+        Pull    "R0-R9, R14"
+        ExitSWIHandler
+
+; *****************************************************************************
+;
+;       Scroll left and right
+;
+; in:   C=0 => scroll window
+;       C=1 => scroll screen
+
+ScrollLeftChar
+        MOV     R9, #0
+        B       ScrollLeft
+
+ScrollLeftByte
+        LDR     R9, [WsPtr, #Log2BPP]
+        LDR     R10, [WsPtr, #ModeFlags]
+        TST     R10, #Flag_BBCGapMode           ; if in BBC gap mode
+ [ True
+
+; TMD 23-May-89: BBC gap modes were done wrong here - it scrolled by 1/2 a
+; BBC byte instead of 1
+
+        SUBNE   R9, R9, #1                      ; then 1 BBC byte = 2 ARM bytes
+ |
+        MOVNE   R9, #1                          ; then 1 BBC byte = 2 ARM bytes
+ ]
+
+ScrollLeft
+        LDR     R10, [WsPtr, #Log2BPC]
+        SUB     R9, R10, R9
+
+        MOV     R10, #1
+        MOV     R9, R10, LSL R9                 ; R9 = bytes to move by
+
+        ADC     R0, R0, R0                      ; bit0(R0) set => whole screen
+        TST     R6, #TeletextMode               ; if teletext, then do special
+        BNE     TTXScrollLeft
+
+        Push    "R0, R14"                       ; save link and carry
+        MOVS    R0, R0, LSR #1                  ; C=0/1 => window/screen
+        BL      GetScrWindInfo
+        BL      ScrollLeft2
+        Pull    R0
+        MOVS    R0, R0, LSR #1
+
+; now clear right hand column
+
+        ADDCC   R0, WsPtr, #TWBRow              ; C=0 => window
+        LDMCCIA R0, {R1-R3}                     ; R1 = TWBRow; R2 = TWRCol
+        MOVCC   R0, R2                          ; R3 = TWTRow; R0 = TWRCol
+
+        ADDCS   R0, WsPtr, #ScrRCol             ; C=1 => screen
+        LDMCSIA R0, {R0, R1}                    ; R0 = ScrRCol; R1 = ScrBRow
+        MOVCS   R2, R0                          ; R2 = ScrRCol; R3 = 0
+        MOVCS   R3, #0
+
+;       R9 = width of column to clear (in bytes)
+
+        BL      GetBoxInfo              ; get info for right hand char column
+                                        ; R2 -> top left of right hand char col
+                                        ; R5 := number of bytes for one char
+        ADD     R2, R2, R5              ; R2 -> off right of top line
+        MOV     R5, R9                  ; R5 := real number of bytes to do
+        SUB     R2, R2, R5              ; R2 -> top left of column to clear
+        Pull    R14
+        B       ClearThisBox
+
+; *****************************************************************************
+
+GetScrWindInfo
+        BCC     GetWindowInfo
+GetScreenInfo
+        MOV     R0, #0
+        LDR     R1, [WsPtr, #ScrBRow]
+        LDR     R2, [WsPtr, #ScrRCol]
+        MOV     R3, #0
+        B       GetBoxInfo
+
+; *****************************************************************************
+;
+;       ScrollLeft2 - Scroll area of screen left (don't clear right column)
+;
+; in:   R2 -> screen(left,top)
+;       R5 = width of box in bytes
+;       R6 = number of scan lines in box
+;       R7 = linelength
+;       R9 = number of bytes to scroll by
+;
+
+ScrollLeft2 ROUT
+        SUBS    R5, R5, R9                      ; number of bytes to scroll
+        MOVEQ   PC, R14                         ; none to scroll
+
+        MOV     R10, R9, LSL #3                 ; number of bits moving by
+        CMP     R10, #32
+        MOVCS   R10, #32                        ; only do 32 at a time
+        RSB     R11, R10, #32                   ; 32-number of bits moving by
+
+ScrollLineLeft
+        MOV     R0, R2                          ; to addr (left)
+        ADD     R1, R0, R9                      ; from addr (right)
+        ADD     R3, R0, R5                      ; end+1 to addr
+10
+        TST     R0, #3                          ; if not on word boundary
+        LDRNEB  R4, [R1], #1                    ; then copy a byte from R1
+        STRNEB  R4, [R0], #1                    ; to R0
+        CMPNE   R0, R3                          ; if not on bdy and not at end
+        BNE     %BT10                           ; then loop
+
+20
+        ADD     R1, R1, #3                      ; round R1 up to next word
+        BIC     R1, R1, #3
+
+        TEQ     R11, #0                         ; if no shifts, use fast code
+        BEQ     %FT60
+
+        SUB     R3, R3, #3                      ; fudge factors anonymous
+        CMP     R0, R3                          ; if can't do any words
+        BCS     %FT40                           ; then skip
+        LDR     R8, [R0]                        ; get word to be put into R4
+30
+        MOV     R4, R8, LSR R10                 ; get 2nd bits of old word
+        LDR     R8, [R1], #4                    ; load new word
+        ORR     R4, R4, R8, LSL R11             ; merge with 1st bits of new
+        STR     R4, [R0], #4                    ; and store
+        CMP     R0, R3                          ; if more words to do then loop
+        BCC     %BT30
+40
+        ADD     R3, R3, #3                      ; put it back again
+
+;       finish off the odd bytes
+;       R0 -> next dest byte
+
+        ADD     R1, R0, R9                      ; R1 -> next source byte
+        CMP     R0, R3                          ; if not at end
+50
+        LDRCCB  R4, [R1], #1                    ; then copy a byte from R1
+        STRCCB  R4, [R0], #1                    ; to R0
+        CMPCC   R0, R3                          ; check for end
+        BCC     %BT50                           ; and loop if not
+
+        ADD     R2, R2, R7                      ; goto next line down
+        SUBS    R6, R6, #1                      ; one less line to do
+        BNE     ScrollLineLeft
+        MOV     PC, R14
+
+; fast code for when R10=32, R11=0
+
+60
+        SUB     R3, R3, #7                      ; must be able to do 8 bytes
+        CMP     R0, R3
+70
+        LDMCCIA R1!, {R4, R8}                   ; if OK then copy 2 words
+        STMCCIA R0!, {R4, R8}
+        CMPCC   R0, R3                          ; check for end
+        BCC     %BT70
+        ADD     R3, R3, #4                      ; must be able to do 4 bytes
+        CMP     R0, R3
+80
+        LDRCC   R4, [R1], #4                    ; if OK then copy a word
+        STRCC   R4, [R0], #4
+        CMPCC   R0, R3                          ; check for end
+        BCC     %BT80
+        B       %BT40                           ; do odd bytes at end
+
+; *****************************************************************************
+
+ScrollRightChar
+        MOV     R9, #0
+        B       ScrollRight
+
+ScrollRightByte
+        LDR     R9, [WsPtr, #Log2BPP]
+        LDR     R10, [WsPtr, #ModeFlags]
+        TST     R10, #Flag_BBCGapMode           ; if in BBC gap mode
+
+ [ True
+
+; TMD 23-May-89: BBC gap modes were done wrong here - it scrolled by 1/2 a
+; BBC byte instead of 1
+
+        SUBNE   R9, R9, #1                      ; then 1 BBC byte = 2 ARM bytes
+ |
+        MOVNE   R9, #1                          ; then 1 BBC byte = 2 ARM bytes
+ ]
+
+ScrollRight
+        LDR     R10, [WsPtr, #Log2BPC]
+        SUB     R9, R10, R9
+
+        MOV     R10, #1
+        MOV     R9, R10, LSL R9                 ; R9 = bytes to move by
+
+        ADC     R0, R0, R0                      ; bit0(R0) set => whole screen
+        TST     R6, #TeletextMode               ; if teletext, then do special
+        BNE     TTXScrollRight
+
+        Push    "R0, R14"                       ; save link and carry
+        MOVS    R0, R0, LSR #1                  ; C=0/1 => window/screen
+        BL      GetScrWindInfo
+        BL      ScrollRight2
+        Pull    R0
+        MOVS    R0, R0, LSR #1
+
+; now clear left column
+
+        ADDCC   R0, WsPtr, #TWLCol              ; C=0 => window
+        LDMCCIA R0, {R0-R3}                     ; R0 = TWLCol; R1 = TWBRow
+        MOVCC   R2, R0                          ; R2 = TWLCol; R3 = TWTRow
+
+        MOVCS   R0, #0
+        LDRCS   R1, [WsPtr, #ScrBRow]
+        MOVCS   R2, R0
+        MOVCS   R3, #0
+
+;       R9 = width of column to clear (in bytes)
+
+        BL      GetBoxInfo              ; get info for right hand char column
+                                        ; R2 -> top left of left hand char col
+                                        ; R5 := number of bytes for one char
+        MOV     R5, R9                  ; R5 := real number of bytes to do
+        Pull    R14
+        B       ClearThisBox
+
+; *****************************************************************************
+;
+;       ScrollRight2 - Scroll area of screen right (don't clear left column)
+;
+; in:   R2 -> screen(left,top)
+;       R5 = width of box in bytes
+;       R6 = number of scan lines in box
+;       R7 = linelength
+;       R9 = number of bytes to scroll by
+;
+
+ScrollRight2 ROUT
+        ADD     R2, R2, R5                      ; R2 -> off top right
+        SUB     R2, R2, #1                      ; R2 -> top right
+
+        SUBS    R5, R5, R9                      ; number of bytes to scroll
+        MOVEQ   PC, R14                         ; none to scroll
+
+        MOV     R10, R9, LSL #3                 ; number of bits moving by
+        CMP     R10, #32
+        MOVCS   R10, #32                        ; only do 32 at a time
+        RSB     R11, R10, #32                   ; 32-number of bits moving by
+
+ScrollLineRight
+        MOV     R0, R2                          ; to addr (right)
+        SUB     R1, R0, R9                      ; from addr (left)
+        SUB     R3, R0, R5                      ; end-1 to addr
+
+        MOV     R8, #(3 :SHL: 30)
+10
+        CMP     R8, R0, LSL #30                 ; EQ if (R0 AND 3)=3
+        LDRNEB  R4, [R1], #-1                   ; if not then copy a byte
+        STRNEB  R4, [R0], #-1
+        CMPNE   R0, R3                          ; if not aligned and not end
+        BNE     %BT10                           ; then loop
+
+        SUB     R1, R1, #3                      ; round R1 down to next word
+        BIC     R1, R1, #3
+
+        SUB     R0, R0, #3                      ; make R0 -> word boundary
+        TEQ     R11, #0                         ; if no shifts, use fast code
+        BEQ     %FT60
+
+        CMP     R0, R3                          ; if no words
+        BLS     %FT40                           ; then skip
+        LDR     R8, [R0]                        ; get word to be put into R4
+30
+        MOV     R4, R8, LSL R10
+        LDR     R8, [R1], #-4
+        ORR     R4, R4, R8, LSR R11
+        STR     R4, [R0], #-4
+        CMP     R0, R3                          ; if R3 < R1 then no more words
+        BHI     %BT30
+40
+        ADD     R0, R0, #3                      ; put it back again
+
+;       finish off the odd bytes
+;       R0 -> next dest byte
+
+        SUB     R1, R0, R9                      ; R1 -> next source byte
+        CMP     R0, R3                          ; check for end
+50
+        LDRHIB  R4, [R1], #-1                   ; if not end then copy byte
+        STRHIB  R4, [R0], #-1
+        CMPHI   R0, R3                          ; check for end
+        BHI     %BT50
+
+        ADD     R2, R2, R7                      ; move to next row
+        SUBS    R6, R6, #1                      ; one less row to do
+        BNE     ScrollLineRight
+        MOV     PC, R14
+
+; fast code for when R10=32, R11=0
+
+60
+        ADD     R3, R3, #4                      ; need to do at least 8 bytes
+        CMP     R0, R3
+70
+        LDMHIDA R1!, {R4, R8}                   ; if OK then copy 2 words
+        STMHIDA R0!, {R4, R8}
+        CMPHI   R0, R3                          ; check for end
+        BHI     %BT70
+
+        SUB     R3, R3, #4                      ; put it back
+        CMP     R0, R3
+80
+        LDRHI   R4, [R1], #-4                   ; if OK then copy 1 word
+        STRHI   R4, [R0], #-4
+        CMPHI   R0, R3                          ; check for end
+        BHI     %BT80
+        B       %BT40
+
+; *****************************************************************************
+;
+;       VDU 23,16,x,y| - Change cursor flags
+;
+;       new := (old AND y) EOR x
+
+Vdu23_16
+        LDRB    R0, [WsPtr, #CursorFlags]       ; just affect bottom byte
+        LDRB    R1, [WsPtr, #QQ+1]              ; x
+        LDRB    R2, [WsPtr, #QQ+2]              ; y
+        AND     R0, R0, R2
+        EOR     R0, R0, R1
+        STRB    R0, [WsPtr, #CursorFlags]
+
+        TST     R0, #1
+        BEQ     RCRLF                           ; leaving 81 column mode
+                                                ; so release CRLF
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       Move cursor to R0 chars from boundary
+;       and check for being legal
+;
+; out:  C=1 => was within window
+;       C=0 => outside window
+
+        ASSERT  CursorY = CursorX +4
+
+CursorBdyCheck
+        Push    R14
+        BL      CursorBdy
+        ADD     R9, WsPtr, #CursorX
+        LDMIA   R9, {R9, R10}
+        CMP     R4, R9
+        CMPCS   R3, R10
+        Pull    PC
+
+; *****************************************************************************
+;
+;       Move cursor to boundary indicated by value of R6 in bits 1-3
+;       (0 or 4 = left, 2 or 6 = right, 8 or 10 = up, 12 or 14 = down)
+;       + R0 character positions in
+;
+; out:  R7, R8, R11 undefined
+;       R0, R1, R6, R9, R10, R12, R13 preserved
+;       R2-R5 are window coords (left,down,right,up)
+;
+        ASSERT  TWBRow = TWLCol + 4
+        ASSERT  TWRCol = TWLCol + 8
+        ASSERT  TWTRow = TWLCol + 12
+
+InputCursorB0
+        MOV     R0, #0
+InputCursorBdy
+        ADD     R11, WsPtr, #InputCursorX
+        B       CBDY05
+
+CursorB0
+        MOV     R0, #0
+CursorBdy
+        ADD     R11, WsPtr, #CursorX
+CBDY05
+        ADD     R7, WsPtr, #TWLCol      ; point to window coords
+        LDMIA   R7, {R2-R5}
+
+        ADR     R7, CBDYTab
+DoJumpTable
+        AND     R8, R6, #&0E
+        LDR     R8, [R7, R8, LSL #1]
+        ADD     PC, R7, R8
+
+CBDYTab
+        &       CursorLBdy-CBDYTab
+        &       CursorRBdy-CBDYTab
+        &       CursorLBdy-CBDYTab
+        &       CursorRBdy-CBDYTab
+        &       CursorUBdy-CBDYTab
+        &       CursorUBdy-CBDYTab
+        &       CursorDBdy-CBDYTab
+        &       CursorDBdy-CBDYTab
+
+; Move cursor to left boundary + R0 characters in
+
+CursorLBdy
+        ADD     R7, R0, R2
+CBDY10
+        STR     R7, [R11, #CursorX-CursorX]
+        MOV     PC, R14
+
+; Move cursor to right boundary + R0 characters in
+
+CursorRBdy
+        SUB     R7, R4, R0
+        B       CBDY10
+
+; Move cursor to up boundary + R0 characters in
+
+CursorUBdy
+        ADD     R7, R0, R5
+CBDY20
+        STR     R7, [R11, #CursorY-CursorX]
+        MOV     PC, R14
+
+; Move cursor to down boundary + R0 characters in
+
+CursorDBdy
+        SUB     R7, R3, R0
+        B       CBDY20
+
+; *****************************************************************************
+;
+;       CursorMove - Move cursor in direction corresponding to R6 bits 1-3
+;       (0,4 = right; 2,6 = left; 8,10 = down; 12,14 = up)
+;       R6 is preserved
+
+        ASSERT  InputCursorY-InputCursorX = CursorY-CursorX
+        ASSERT  InputCursorAddr-InputCursorX = CursorAddr-CursorX
+
+InputCursorMove
+        ADD     R11, WsPtr, #InputCursorX
+        B       CurM10
+
+CursorMove
+        ADD     R11, WsPtr, #CursorX
+CurM10
+        ADR     R7, CMVTab
+        B       DoJumpTable
+
+CMVTab
+        &       MoveRight-CMVTab
+        &       MoveLeft-CMVTab
+        &       MoveRight-CMVTab
+        &       MoveLeft-CMVTab
+        &       MoveDown-CMVTab
+        &       MoveDown-CMVTab
+        &       MoveUp-CMVTab
+        &       MoveUp-CMVTab
+
+; Move cursor right if possible - on exit C=1 iff at RHS
+
+MoveRight
+        LDR     R0, [R11, #CursorX-CursorX]
+        LDR     R4, [WsPtr, #TWRCol]
+        CMP     R0, R4
+        MOVCS   PC, R14
+        LDR     R2, [R11, #CursorAddr-CursorX]
+        LDR     R3, [WsPtr, #BytesPerChar]
+        ADD     R0, R0, #1
+        ADD     R2, R2, R3
+        STR     R0, [R11, #CursorX-CursorX]
+        STR     R2, [R11, #CursorAddr-CursorX]
+        MOV     PC, R14
+
+; Move cursor left if possible - on exit C=1 iff at LHS
+
+MoveLeft
+        LDR     R0, [R11, #CursorX-CursorX]
+        LDR     R4, [WsPtr, #TWLCol]
+        CMP     R4, R0
+        MOVCS   PC, R14
+        LDR     R2, [R11, #CursorAddr-CursorX]
+        LDR     R3, [WsPtr, #BytesPerChar]
+        SUB     R0, R0, #1
+        SUB     R2, R2, R3
+        STR     R0, [R11, #CursorX-CursorX]
+        STR     R2, [R11, #CursorAddr-CursorX]
+        MOV     PC, R14
+
+; Move cursor down if possible - on exit C=1 iff at bottom
+
+MoveDown
+        LDR     R1, [R11, #CursorY-CursorX]
+        LDR     R4, [WsPtr, #TWBRow]
+        CMP     R1, R4
+        MOVCS   PC, R14
+        LDR     R2, [R11, #CursorAddr-CursorX]
+        LDR     R3, [WsPtr, #RowLength]
+        ADD     R1, R1, #1
+        ADD     R2, R2, R3
+        STR     R1, [R11, #CursorY-CursorX]
+        STR     R2, [R11, #CursorAddr-CursorX]
+        MOV     PC, R14
+
+; Move cursor up if possible - on exit C=1 iff at top
+
+MoveUp
+        LDR     R1, [R11, #CursorY-CursorX]
+        LDR     R4, [WsPtr, #TWTRow]
+        CMP     R4, R1
+        MOVCS   PC, R14
+        LDR     R2, [R11, #CursorAddr-CursorX]
+        LDR     R3, [WsPtr, #RowLength]
+        SUB     R1, R1, #1
+        SUB     R2, R2, R3
+        STR     R1, [R11, #CursorY-CursorX]
+        STR     R2, [R11, #CursorAddr-CursorX]
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       Special HT - used when abnormal cursor direction selected
+;                    or if C81Bit set
+;
+; in:   R6 = CursorFlags
+;
+
+SpecialHT
+        TST     R6, #Vdu5Bit
+        BNE     Vdu5HT
+        Push    "R6,R14"
+        BL      RCRLFR6
+        Pull    R6
+        BL      CursorMove              ; try to move in appropriate direction
+        Pull    PC, CC                  ; if successful, finish
+        BL      CursorB0                ; else move cursor to -ve X boundary
+        BL      AddressCursor           ; re-address cursor
+        Pull    R14
+        B       VduLF                   ; and do a line-feed
+
+; *****************************************************************************
+;
+;       CHT - version of HT used after printing character
+;       will set pending CRLF if necessary
+;
+; in:   R6 = CursorFlags
+
+CHT
+        TST     R6, R6, ROR #10 ; test for Vdu5 or &1E or C81Bit, latter not
+                                ; possible as we just printed a char
+        BNE     SpecialCHT
+
+        LDR     R0, [WsPtr, #CursorX]
+        LDR     R2, [WsPtr, #CursorAddr]
+        LDR     R3, [WsPtr, #BytesPerChar]
+        LDR     R4, [WsPtr, #TWRCol]
+
+        ADD     R0, R0, #1
+        ADD     R2, R2, R3
+        CMP     R0, R4
+        STRLS   R0, [WsPtr, #CursorX]
+        STRLS   R2, [WsPtr, #CursorAddr]
+        MOVLS   PC, R14
+
+        TST     R6, #1                          ; are we in "81-column" mode
+        BNE     CHT10                           ; yes, then just set C81Bit
+
+        BSR     PageTest
+
+        LDR     R0, [WsPtr, #TWLCol]
+        LDR     R1, [WsPtr, #CursorY]
+        LDR     R4, [WsPtr, #TWBRow]
+
+        ADD     R1, R1, #1
+        CMP     R1, R4
+        BLS     CursorR0R1                       ; not on bottom line
+
+        STR     R0, [WsPtr, #CursorX]
+        BSR     ScrollUp
+        B       AddressCursor                    ; re-address cursor position
+
+CHT10
+        ORR     R6, R6, #C81Bit
+        STR     R6, [WsPtr, #CursorFlags]
+        MOV     PC, R14
+
+SpecialCHT
+        TST     R6, #Vdu5Bit
+        BNE     Vdu5HT
+        BSR     CursorMove              ; try to move in appropriate direction
+        MOVCC   PC, R14                 ; if successful, finish
+
+        TST     R6, #1
+        BNE     CHT10
+
+        Push    R14
+        BL      CursorB0                ; else move cursor to -ve X boundary
+        BL      AddressCursor           ; re-address cursor
+        Pull    R14
+        B       VduLF                   ; and do a line-feed
+
+; *****************************************************************************
+;
+;       Special BS - used when abnormal cursor direction selected
+;                    or Vdu5 mode
+;                    or if C81Bit was set
+;
+; in :  R6 = CursorFlags
+;
+
+SpecialBS
+        TST     R6, #Vdu5Bit            ; VDU 5 mode ?
+        BNE     Vdu5BS                  ; then branch
+
+        TST     R6, #C81Bit             ; did we come here cos of C81Bit
+        BICNE   R6, R6, #C81Bit         ; if so then clear the bit
+        STRNE   R6, [WsPtr, #CursorFlags] ; store it back
+        MOVNE   PC, R14                 ; and leave cursor where it is
+
+        EOR     R6, R6, #6              ; move "left"
+        Push    R14
+        BL      CursorMove
+        Pull    PC, CC                  ; if successful
+        BL      CursorB0                ; move to +ve X boundary
+        BL      AddressCursor           ; re-address cursor
+        Pull    R14
+        B       VT                      ; and do reverse line-feed
+
+; *****************************************************************************
+;
+;       Special LF - used when abnormal cursor direction selected
+;
+; in :  R6 = CursorFlags
+;
+
+SpecialLF
+        EOR     R6, R6, #8              ; move "down"
+        Push    R14
+        BL      CursorMove
+        Pull    PC, CC                  ; if successful
+        BL      CanScroll               ; see if we can scroll
+        Pull    PC, CS                  ; if failed, exit with all done
+        Pull    R14
+        MOV     R0, #0                  ; scroll window
+        MOV     R1, #7                  ; "up"
+        MOV     R2, #0                  ; by 1 char
+        B       Scroll012
+
+; *****************************************************************************
+;
+;       Special VT - used when abnormal cursor direction selected
+;
+; in :  R6 = CursorFlags
+;
+
+SpecialVT
+        EOR     R6, R6, #14             ; move "up"
+        Push    R14
+        BL      CursorMove
+        Pull    PC, CC                  ; if successful
+        BL      CanScroll               ; see if we can scroll
+        Pull    PC, CS                  ; if failed, exit with all done
+        Pull    R14
+        MOV     R0, #0                  ; scroll window
+        MOV     R1, #6                  ; "down"
+        MOV     R2, #0                  ; by 1 char
+        B       Scroll012
+
+; *****************************************************************************
+;
+;       CanScroll - check whether we can scroll in direction R6
+;
+; in:   R6 = CursorFlags
+
+CanScroll ROUT
+        Push    R14
+        TST     R6, #&10            ; do we scroll or move to other edge
+        BNE     CantScroll
+
+        TST     R6, #CursorsSplit
+        BEQ     %FT10                   ; cursors not split, so input cursor
+                                        ; not relevant
+        EOR     R6, R6, #6
+        BL      InputCursorMove         ; if cursor fails to move, it's OK
+                                        ; where it is
+        BL      AddressInputCursor      ; readdress it anyway, just in case
+        EOR     R6, R6, #6              ; put R6 back
+10
+        CLC
+        Pull    PC
+
+CantScroll
+        BL      CursorB0                ; move to opposite edge
+        BL      AddressCursor
+        SEC
+        Pull    PC
+
+; *****************************************************************************
+;
+;       Release any pending CRLF
+;
+
+RCRLF
+        LDR     R6, [WsPtr, #CursorFlags]
+
+; Extra entry point when R6 already loaded
+
+RCRLFR6
+        TST     R6, #C81Bit
+        MOVEQ   PC, R14                 ; no pending CRLF
+        BSR     CR10                    ; (clears C81Bit)
+        B       VduLF
+
+; *****************************************************************************
+;
+;       VDU 23,8,t1,t2,x1,y1,x2,y2| - Clear block of text
+;
+;       t=0     Top left of window
+;       t=1     Top of cursor column
+;       t=2     Off top right of window
+;
+;       t=4     Left end of cursor line
+;       t=5     Cursor position
+;       t=6     Off right of cursor line
+;
+;       t=8     Bottom left of window
+;       t=9     Bottom of cursor column
+;       t=10    Off bottom right of window
+;
+
+Vdu23_8
+        MOV     R0, #0                  ; top left of window
+        STRB    R0, [WsPtr, #CBWS+0]
+        STRB    R0, [WsPtr, #CBWS+1]
+        BSR     CP81                    ; cursor position
+        STRB    R0, [WsPtr, #CBWS+2]
+        STRB    R1, [WsPtr, #CBWS+3]
+        BSR     WBotRig                 ; bottom right of window
+        ADD     R0, R0, #1              ; off bottom right of window
+        STRB    R0, [WsPtr, #CBWS+4]
+        STRB    R1, [WsPtr, #CBWS+5]
+
+        ADD     R3, WsPtr, #CBWS
+        ADD     R2, R3, #QQ+2-CBWS
+        LDRB    R0, [WsPtr, #QQ+1]      ; t1
+        BSR     CLBL50
+        LDRB    R0, [WsPtr, #QQ+2]      ; t2
+        BSR     CLBL50
+
+; Check end is after start
+
+        LDRB    R2, [WsPtr, #CBStart]   ; start X
+        LDRB    R3, [WsPtr, #CBStart+1] ; start Y
+        LDRB    R4, [WsPtr, #CBEnd]     ; end X
+        LDRB    R5, [WsPtr, #CBEnd+1]   ; end Y
+
+        CMP     R5, R3
+        CMPEQ   R4, R2
+        MOVLS   PC, R14                 ; end <= start so do nothing
+
+        LDR     R0, [WsPtr, #CursorX]   ; save cursor position
+        LDR     R1, [WsPtr, #CursorY]
+        Push    "R0,R1"
+
+        MOV     R0, R3                  ; First row to clear
+        MOV     R1, R2                  ; Start position for first row
+        LDRB    R7, [WsPtr, #CBWS+4]    ; End position for all but last row
+
+        ADD     R6, WsPtr, #TWLCol
+        LDMIA   R6, {R8-R11}
+
+        LDR     R6, [WsPtr, #CursorFlags]
+CLBL20
+        TEQ     R0, R5
+        BEQ     CLBL40
+        BSR     RowClear
+        ADD     R0, R0, #1
+        MOV     R1, #0                  ; start position for subsequent rows
+        B       CLBL20
+
+CLBL40
+        MOV     R7, R4                  ; end position for last row
+        BSR     RowClear
+
+        Pull    "R0,R1"
+        STR     R0, [WsPtr, #CursorX]
+        STR     R1, [WsPtr, #CursorY]
+        B       AddressCursor
+
+CLBL50
+        AND     R1, R0, #3
+        MOV     R1, R1, LSL #1
+        LDRB    R6, [WsPtr, #CBWS+4]
+        BSR     CLBL60
+        MOV     R1, R0, LSR #1
+        ORR     R1, R1, #1
+        LDRB    R6, [WsPtr, #CBWS+5]
+CLBL60
+        LDRB    R4, [R3, R1]            ; get base coordinate to use
+        LDRB    R5, [R2, #1]!           ; get x or y offset
+
+        MOV     R5, R5, LSL #24
+        ADDS    R4, R4, R5, ASR #24     ; add sign extended offset to base
+        MOVMI   R4, #0                  ; if <0 then make =0
+        CMP     R4, R6
+        MOVHI   R4, R6                  ; is this stringent enough ?
+
+        STRB    R4, [R2, #CBStart-(QQ+3)]
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       RowClear - Clear a "row" {R1 <= X < R7, Y = R0}
+
+RowClear
+        Push    "R0-R11,R14"
+        SUB     R7, R7, #1
+        CMP     R1, R7
+        BHI     RowCL10         ; left > right, so ignore
+
+        TST     R6, #8
+
+        MOVEQ   R2, R1          ; left
+        MOVEQ   R3, R0          ; bottom
+        MOVEQ   R4, R7          ; right
+        MOVEQ   R5, R0          ; top
+
+        MOVNE   R2, R0          ; left
+        MOVNE   R3, R7          ; bottom
+        MOVNE   R4, R0          ; right
+        MOVNE   R5, R1          ; top
+
+        TST     R6, #2
+
+        ADDEQ   R0, R8, R2
+        ADDEQ   R2, R8, R4
+
+        SUBNE   R0, R10, R4
+        SUBNE   R2, R10, R2
+
+        TST     R6, #4
+
+        ADDEQ   R1, R11, R3
+        ADDEQ   R3, R11, R5
+
+        SUBNE   R1, R9, R5
+        SUBNE   R3, R9, R3
+
+        BL      ClearBox
+
+RowCL10
+        Pull    "R0-R11,PC"
+
+        [ :LNOT: AssemblingArthur
+
+; *****************************************************************************
+;
+;       DoVduExternal - External entry point for
+;       a) reading input cursor position
+;       b) reading output cursor position
+;       c) reading character at cursor position and screen mode
+;       d) resetting all or part of font
+;       e) reading font definitions
+;       f) reading VDU status
+;       g) reading VDU variables
+;       h) reading palette (OSWORD &0B)
+;       i) setting palette (OSWORD &0C)
+;       j) various mouse/pointer stuff
+;       k) set top of screen address
+;       l) set VDU driver bank
+;       m) set display bank
+;
+; in:   R0 = 0 => read input cursor position
+;       R0 = 1 => read output cursor position
+;       R0 = 2 => OSBYTE &87 - R1 := char at cursor, R2 := screen mode
+;       R0 = 3 => reset font (R1*32 = start char, R2 = number of pages)
+;       R0 = 4 => OSWORD 10 (R1 -> control block)
+;       R0 = 5 => read VDU status
+;       R0 = 6 => read VDU variables number R1, R1+1
+;       R0 = 7 => read palette
+;       R0 = 8 => set palette
+;       R0 = 9 => mouse/pointer control
+;       R0 = 10 => set top of screen address
+;       R0 = 11 => set VDU driver bank
+;       R0 = 12 => set display bank
+;
+; out:  a,b; R1 = X, R2 = Y
+;       c; R1=char, R2=screen mode
+;       f; R1=VDU status
+;       g; R1=variable(n), R2=variable(n+1)
+;       R0, R3 destroyed
+;       R4-R11 preserved
+;
+
+DoVduExternal   ROUT
+        CMP     R0, #ExtEntries
+        MOVCS   PC, R14
+        LDR     R3, [PC, R0, LSL #2]
+        ADD     PC, PC, R3
+
+ExtTable OutputExternals
+
+        ]
+
+; *****************************************************************************
+
+DoReadPOSVPOSI ROUT
+        MOV     R0, #0
+        B       %FT05
+DoReadPOSVPOSO
+        MOV     R0, #1
+05
+        Push    "R4-R11, R14"
+        LDR     R6, [WsPtr, #CursorFlags]
+        TST     R6, #CursorsSplit               ; if cursors are split
+        TEQNE   R0, #1                          ; and reading input cursor
+        BNE     %FT10                           ; then use input cursor
+        BL      CP81                            ; else read output cursor
+        B       %FT20
+10
+        BL      CP79
+20
+        MOV     R2, R1
+        MOV     R1, R0
+        Pull    "R4-R11, PC"
+
+; *****************************************************************************
+;
+;       DoSetScreenStart - Set top of screen address (display + drivers)
+;
+; in:   R1 -> control block
+;       [R1, #0] = bit mask;    bit 0 set => change drivers address
+;                               bit 1 set => change display address
+;
+;       [R1, #1..4] = byte offset from lowest screen address
+;
+; out:  -
+;
+
+DoSetScreenStart ROUT
+        Push    R14
+        LDRB    R3, [R1, #0]                    ; R3 = bitmask
+        LDRB    R0, [R1, #1]
+        LDRB    R2, [R1, #2]
+        ORR     R0, R0, R2, LSL #8
+        LDRB    R2, [R1, #3]
+        ORR     R0, R0, R2, LSL #16
+        LDRB    R2, [R1, #4]
+        ORR     R0, R0, R2, LSL #24             ; R0 is now the offset
+        LDR     R2, [WsPtr, #TotalScreenSize]
+        CMP     R0, R2
+        BCS     %FT10                           ; offset too large
+        Push    "R0,R2,R3,R5-R10"
+
+        MOV     R0, #1
+        STRB    R0, [WsPtr, #ScreenMemoryClaimed]
+                                                ; indicate memory not available
+        BL      PreWrchCursor
+        Pull    "R0,R2,R3,R5-R10"
+        ADD     R0, R0, #ScreenEndAdr
+        SUB     R0, R0, R2                      ; make into a LOGRAM address
+
+        TST     R3, #1
+        BEQ     %FT05
+        Push    R0
+        BL      NewScreenStart
+        Pull    R0
+05
+        TST     R3, #2
+        BLNE    SetVinit               ; program vinit and set DisplayStart
+
+        Push    "R5-R10"                        ; save registers
+        BL      PostWrchCursor
+        Pull    "R5-R10"
+10
+        Pull    PC
+
+; *****************************************************************************
+;
+;       NewScreenStart - Update ScreenStart and readdress cursors
+;
+; in:   R0 = new screen start
+;
+; out:  R0, R2 corrupted; all others preserved
+;
+
+NewScreenStart ROUT
+        LDR     R2, [WsPtr, #VduSprite]         ; test if outputting to sprite
+        TEQ     R2, #0                          ; Z => outputting to screen
+
+        LDR     R2, [WsPtr, #DisplayScreenStart]
+        STR     R0, [WsPtr, #DisplayScreenStart]
+        MOVNE   PC, R14                 ; if outputting to sprite, don't update
+                                        ; Screenstart or cursor addresses
+
+        STR     R0, [WsPtr, #ScreenStart]
+        SUB     R0, R0, R2                      ; R0 := new-old
+        Push    R14
+AdjustCursorVars
+        LDR     R14, [WsPtr, #CursorAddr]
+        ADD     R14, R14, R0
+        STR     R14, [WsPtr, #CursorAddr]
+        LDR     R14, [WsPtr, #InputCursorAddr]
+        ADD     R14, R14, R0
+        STR     R14, [WsPtr, #InputCursorAddr]
+        Pull    PC
+
+; *****************************************************************************
+;
+;       DoSetDriverBank - Set VDU driver's screen bank (OSBYTE &70)
+;
+; in:   R1 = bank number (1..n) (NOTE: starts at 1)
+;
+
+DoSetDriverBank ROUT
+        Push    R14
+        MOVS    R2, R1
+        BNE     %FT10
+        LDR     R1, [WsPtr, #VduStatus]
+        TST     R1, #Shadowing
+        MOVEQ   R2, #1
+        MOVNE   R2, #2
+10
+        MOV     R0, #0
+        LDRB    R1, [R0, #OsbyteVars + :INDEX: MemDriver] ; old value
+
+        Push    "R1,R4-R10"             ; save registers
+        BL      ConvertBankToAddress    ; R3 := start address of NEXT bank
+        CMP     R3, #ScreenEndAdr       ; if after end, can't do it
+        Pull    "R1,R4-R10,PC", HI      ; so exit
+
+        STRB    R2, [WsPtr, #ScreenMemoryClaimed]
+                                        ; indicate memory not available (R2<>0)
+        STRB    R2, [R0, #OsbyteVars + :INDEX: MemDriver] ; store new value
+
+        LDR     R0, [WsPtr, #DriverBankAddr] ; R0:=old default bank start
+        LDR     R2, [WsPtr, #DisplayScreenStart] ; R2:=old current screen start
+        SUB     R0, R2, R0              ; R0 := (current-default)
+                                        ; in range 1-TotalSize..TotalSize-1
+
+        LDR     R2, [WsPtr, #ScreenSize]
+        SUB     R3, R3, R2              ; default start of THIS bank
+        STR     R3, [WsPtr, #DriverBankAddr] ; R3 := new default bank start
+
+        ADD     R0, R0, R3              ; new current start (not bound checked)
+
+        LDR     R2, [WsPtr, #TotalScreenSize]
+
+        RSBS    R0, R0, #ScreenEndAdr
+        ADDLS   R0, R0, R2
+        CMP     R0, R2
+        SUBHI   R0, R0, R2              ; now in bounds (but inverted)
+        RSB     R0, R0, #ScreenEndAdr
+
+        Push    R0
+        BL      PreWrchCursor
+        Pull    R0
+
+        BL      NewScreenStart
+
+        BL      PostWrchCursor
+        Pull    "R1,R4-R10,PC"
+
+; *****************************************************************************
+;
+;       DoSetDisplayBank - Set displayed screen bank (OSBYTE &71)
+;
+; in:   R1 = bank number (1..n) (NOTE: starts at 1)
+;
+
+DoSetDisplayBank ROUT
+        Push    R14
+        MOVS    R2, R1
+        BNE     %FT10
+        LDR     R1, [WsPtr, #VduStatus]
+        TST     R1, #Shadowing
+        MOVEQ   R2, #1
+        MOVNE   R2, #2
+10
+        MOV     R0, #0
+        LDRB    R1, [R0, #OsbyteVars + :INDEX: MemDisplay] ; old value
+        Push    "R1,R4-R5"              ; save registers
+        BL      ConvertBankToAddress    ; R3 := start address of NEXT bank
+        CMP     R3, #ScreenEndAdr       ; if after end, can't do it
+        Pull    "R1,R4-R5,PC", HI      ; so exit
+
+        STRB    R2, [WsPtr, #ScreenMemoryClaimed]
+                                        ; indicate memory not available (R2<>0)
+        STRB    R2, [R0, #OsbyteVars + :INDEX: MemDisplay] ; store new value
+
+        LDR     R0, [WsPtr, #DisplayBankAddr] ; R0 := old default bank start
+        LDR     R2, [WsPtr, #DisplayStart]    ; R2 := old current display start
+        SUB     R0, R2, R0              ; R0 := (current-default)
+                                        ; in range 1-TotalSize..TotalSize-1
+
+        LDR     R2, [WsPtr, #ScreenSize]
+        SUB     R3, R3, R2              ; default start of THIS bank
+        STR     R3, [WsPtr, #DisplayBankAddr] ; R3 := new default bank start
+
+        ADD     R0, R0, R3              ; new current start (not bound checked)
+
+        LDR     R2, [WsPtr, #TotalScreenSize]
+
+        RSBS    R0, R0, #ScreenEndAdr
+        ADDLS   R0, R0, R2
+        CMP     R0, R2
+        SUBHI   R0, R0, R2              ; now in bounds (but inverted)
+        RSB     R0, R0, #ScreenEndAdr
+
+        BL      SetVinit                ; program vinit and set DisplayStart
+        Pull    "R1,R4-R5,PC"
+
+; *****************************************************************************
+;
+;       CP81 - Read output cursor position,
+;       taking C81Bit and CursorFlags into account
+;
+; out:  R0=X, R1=Y
+
+CP81
+        Push    R14
+        BL      CP80
+        LDR     R6, [WsPtr, #CursorFlags]
+        TST     R6, #C81Bit
+        ADDNE   R0, R0, #1                      ; in hidden column, so inc X
+        Pull    PC
+
+; *****************************************************************************
+;
+;       CP79 - Read input cursor position,
+;       taking CursorFlags into account
+;
+; out:  R0=X, R1=Y
+
+CP79
+        MOV     R1, #InputCursorX-TWLCol
+        B       CP80R1
+
+; *****************************************************************************
+;
+;       WBotRig - Read coordinates of "bottom right" in user coords
+;
+; out:  R0=X, R1=Y
+
+WBotRig
+        ADD     R5, WsPtr, #TWLCol
+        LDR     R6, [WsPtr, #CursorFlags]
+        LDR     R0, [R5, #TWLCol-TWLCol]            ; get TWLCol
+        LDR     R1, [R5, #TWRCol-TWLCol]            ; get TWRCol
+        SUB     R4, R1, R0
+        MOV     R2, #0
+        MOV     R1, #0
+        B       CP8010
+
+; *****************************************************************************
+;
+;       CP80 - Read cursor position, taking CursorFlags into account
+
+CP80
+        MOV     R1, #CursorX-TWLCol
+CP80R1
+        ADD     R5, WsPtr, #TWLCol
+        LDR     R6, [WsPtr, #CursorFlags]
+        MOV     R2, #2
+        MOV     R0, #0
+        BSR     CP8020
+        MOV     R4, R2
+        MOV     R2, #4
+CP8010
+        ADD     R1, R1, #4
+        MOV     R0, #TWTRow-TWLCol
+        BSR     CP8020
+        MOV     R0, R2
+        MOV     R1, R2
+        TST     R6, #8
+        MOVEQ   R0, R4
+        MOVNE   R1, R4
+
+        MOV     PC, R14
+
+CP8020
+        TST     R2, R6
+        EORNE   R0, R0, #8
+        LDR     R2, [R5, R0]
+        LDR     R3, [R5, R1]
+        SUBNE   R2, R2, R3
+        SUBEQ   R2, R3, R2
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       Vdu23_0 - Program "6845"
+;
+
+Vdu23_0
+        LDRB    R1, [WsPtr, #QQ+1]      ; get register number
+        AND     R2, R1, #31             ; address register is 5 bits
+        CMP     R2, #8
+        MOVCC   PC, R14
+        BEQ     Vdu23_0_8
+        CMP     R2, #10
+        BEQ     Vdu23_0_10
+        CMP     R2, #12
+        BCC     Vdu23_0_11
+
+        [ DoVdu23_0_12
+        BEQ     Vdu23_0_12
+        CMP     R2, #13
+        BEQ     Vdu23_0_13
+        ]
+        B       UnknownVdu23
+
+
+; *****************************************************************************
+;
+;       Vdu23_0_8 - Program interlace
+;
+; in:   R1 = unmasked register number (bits 5-7 may be set)
+
+Vdu23_0_8 ROUT
+        LDRB    R0, [WsPtr, #QQ+2]      ; value to program into interlace
+        TST     R0, #&80                ; if negative, don't EOR with *TV
+        TSTEQ   R1, #&E0                ; if not register 8, don't EOR
+        BNE     %FT10                   ; don't EOR
+
+        LDROSB  R1, TVInterlace
+        TST     R1, #1
+        EORNE   R0, R0, #1              ; toggle if *TV n,1 and number +ve
+10
+        LDR     R1, [WsPtr, #VIDCControlCopy]
+        BIC     R1, R1, #CR_Interlace
+        TST     R0, #1
+        ORRNE   R1, R1, #CR_Interlace    ; zero => no interlace
+
+        MOV     R0, #VIDC
+        STR     R1, [R0]                ; program VIDC
+        MOV     PC, R14
+
+        [ DoVdu23_0_12
+
+; *****************************************************************************
+;
+;       Vdu23_0_12 - Program "hi" byte of start of screen
+;
+
+Vdu23_0_12
+        MOV     R2, #11                 ; starting bit number
+V23012_10
+        LDRB    R0, [WsPtr, #QQ+2]      ; get parameter
+        LDR     R1, [WsPtr, #VinitCopy]
+        MOV     R1, R1, ROR R2          ; move changing bits to bottom
+        BIC     R1, R1, #&FF            ; clear old bits out
+        ORR     R1, R1, R0              ; put in new bits
+        RSB     R2, R2, #32             ; fudge cos we ain't got ROL
+        MOV     R0, R1, ROR R2          ; put back to correct position
+        B       SetVinitPhys
+
+; *****************************************************************************
+;
+;       Vdu23_0_13 - Program "lo" byte of start of screen
+;
+
+Vdu23_0_13
+        MOV     R2, #3                  ; starting bit number
+        B       V23012_10
+
+        ]
+
+; *****************************************************************************
+;
+;       Vdu23_9  - Equivalent of FX 9
+;       Vdu23_10 - Equivalent of FX 10
+;
+; in:   R0 = 9 or 10
+;
+
+Vdu23_9
+Vdu23_10
+        Push    R14                             ; for the SWI
+        LDRB    R1, [WsPtr, #QQ+1]              ; get X parameter
+        SWI     XOS_Byte
+        Pull    PC, VC                          ; no error, then return
+
+        Pull    R14                             ; get stack back
+        B       VduBadExit                      ; and tell the world
+
+; *****************************************************************************
+;
+;       DoReadVDUStatus - Read VDU status
+;
+; out:  R1 = status
+;       bit 0 set =>    printer enabled by VDU 2
+;       bit 1 set =>    N/A
+;       bit 2 set =>    in page mode
+;       bit 3 set =>    text window in force
+;       bit 4 set =>    in a shadow mode
+;       bit 5 set =>    in VDU 5 mode
+;       bit 6 set =>    cursor editing in progress
+;       bit 7 set =>    VDU disabled (by VDU 21)
+;
+;       R2 corrupted
+;
+
+DoReadVDUStatus
+        LDR     R1, [WsPtr, #VduStatus]         ; Vdu2, Window, Shadow bits
+
+        LDR     R2, [WsPtr, #CursorFlags]
+
+        TST     R2, #PageMode
+        ORRNE   R1, R1, #&04
+
+        TST     R2, #Vdu5Bit
+        ORRNE   R1, R1, #&20
+
+        TST     R2, #CursorsSplit
+        ORRNE   R1, R1, #&40
+
+        TST     R2, #VduDisabled
+        ORRNE   R1, R1, #&80
+
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       DoReadVDUVariable - Read BBC-style VDU variables
+;
+; in:   R1 = variable to read (only &00..&0F are supported)
+;
+; out:  R1 = value of that variable
+;       R2 = value of next variable
+;
+
+DoReadVDUVariable
+        CMP     R1, #(BBCVduVarsEnd-BBCVduVars)
+        MOVCS   PC, R14                 ; don't support this variable
+        ADR     R3, BBCVduVars          ; point to lookup table
+        LDRB    R0, [R3, R1]!           ; get index for first variable
+        LDRB    R1, [WsPtr, R0]         ; get first variable
+        LDRB    R0, [R3, #1]            ; get index for second variable
+        LDRB    R2, [WsPtr, R0]         ; get second variable
+        MOV     PC, R14
+
+BBCVduVars
+        EQUB    GWLCol
+        EQUB    GWLCol+1
+        EQUB    GWBRow
+        EQUB    GWBRow+1
+        EQUB    GWRCol
+        EQUB    GWRCol+1
+        EQUB    GWTRow
+        EQUB    GWTRow+1
+        EQUB    TWLCol
+        EQUB    TWBRow
+        EQUB    TWRCol
+        EQUB    TWTRow
+        EQUB    OrgX
+        EQUB    OrgX+1
+        EQUB    OrgY
+        EQUB    OrgY+1
+BBCVduVarsEnd
+
+; *****************************************************************************
+;
+;       SetCharSizes - Set char sizes for VDU 4 or VDU 5 text
+;
+; in:   QQ+2 = flags:   bit 0 set => set VDU 4 size
+;                       bit 1 set => set VDU 5 size
+;                       bit 2 set => set VDU 5 spacing
+;       QQ+3,4  x size in pixels
+;       QQ+5,6  y size in pixels
+;
+
+        ASSERT  YEigFactor = XEigFactor +4
+
+SetCharSizes ROUT
+        LDRB    R0, [WsPtr, #QQ+3]
+        LDRB    R2, [WsPtr, #QQ+4]
+        ORR     R0, R0, R2, LSL #8      ; R0 = x size
+
+        LDRB    R1, [WsPtr, #QQ+5]
+        LDRB    R2, [WsPtr, #QQ+6]
+        ORR     R1, R1, R2, LSL #8      ; R1 = y size
+
+        LDRB    R2, [WsPtr, #QQ+2]
+
+        ADD     R3, WsPtr, #GCharSizes
+        TST     R2, #2                  ; if modifying VDU 5 size
+        STMNEIA R3, {R0, R1}
+
+        ADD     R3, R3, #8
+        TST     R2, #4                  ; if modifying VDU 5 spacing
+        STMNEIA R3, {R0, R1}
+
+        MOV     PC, R14
+
+        END
diff --git a/s/vdu/vdu5 b/s/vdu/vdu5
new file mode 100644
index 0000000000000000000000000000000000000000..4fda4da0bfc8a910202f06fda5c7e0e968353f54
--- /dev/null
+++ b/s/vdu/vdu5
@@ -0,0 +1,662 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.Vdu5
+
+; *****************************************************************************
+;
+;       Print a character in VDU 5 mode
+;
+
+; *****This file has been extensively modified by DJS
+
+; Alter these at your peril !
+
+cxl     RN      0
+scr     RN      0
+
+ecfptr  RN      1
+cword0  RN      1
+
+cyb     RN      2
+cyt2    RN      2
+scrend  RN      2
+cword1  RN      2
+
+cxr     RN      3
+cyb2    RN      3
+ormask  RN      3
+
+cxl2    RN      4
+eormask RN      4
+
+cyb3    RN      5
+charmsk RN      5
+cword2  RN      5
+
+cyt     RN      6
+llength RN      6
+chartmp RN      6
+
+sizex   RN      7
+charptr RN      7
+
+sizey   RN      8
+invcharshf RN   8
+
+charbyte RN     9
+
+cxr2    RN      10
+charshf RN      10
+
+lgBPC   RN      11
+
+Vdu5Wrch
+        Push    R14
+        BL      Vdu5DoChar
+        Pull    R14
+        B       PostCharMove
+
+; Vdu23_18
+;        LDR     R0, [WsPtr, #QQ+1]
+Vdu5DoChar
+        ADD     ecfptr, WsPtr, #FgEcfOraEor  ;Base address of OR/EOR pairs
+Vdu5DoChar10
+        Push    "R14"
+        MOV     charbyte, R0                    ;Put character out of harm's
+                                                ;  way
+        ASSERT  cxl < cyt
+        ASSERT  GCsIY = GCsIX +4
+        ADD     cxl, WsPtr, #GCsIX
+        LDMIA   cxl, {cxl, cyt}                 ; cxl=where left pixel would be
+                                                ; cyt=where top pixel would be
+
+        ASSERT  sizex < sizey
+        ADD     sizex, WsPtr, #GCharSizes
+        LDMIA   sizex, {sizex, sizey}
+        CMP     sizey, #16                      ; if SizeY=8 or SizeY=16
+        TEQNE   sizey, #8                       ;   (leaving CS <=> SizeY=16)
+        TEQEQ   sizex, #8                       ; and SizeX=8, then do it fast
+        BNE     SlowVdu5                        ; else do by scaled char
+
+        TEQ     charbyte, #127                  ; Which font do we use?
+        ADDNE   charptr, WsPtr, #(Font-32*8)    ; Soft if character not DELETE
+        ADREQL  charptr, HardFont-32*8          ; Hard if character is DELETE
+        ADD     charptr, charptr, charbyte, LSL #3      ;Point at char def
+
+        MOVCS   charptr, charptr, LSL #1        ; Double address if SizeY = 16
+                                                ;   (tricky code, originally
+                                                ;   designed by TMD)
+
+        SUB     cyb, cyt, sizey
+        ADD     cyb, cyb, #1                    ; where bottom pixel would be
+        ADD     cxr, cxl, #7                    ; where right pixel would be
+
+        ASSERT  GWBRow = GWLCol+4
+        ASSERT  GWRCol = GWLCol+8
+        ASSERT  GWTRow = GWLCol+12
+        ASSERT  cyb3 > cxl2
+        ASSERT  cxr2 > cyb3
+        ASSERT  R14 > cxr2
+        ADD     cxl2, WsPtr, #GWLCol
+        LDMIA   cxl2, {cxl2, cyb3, cxr2, R14}
+
+        Greatest cxl2, cxl2, cxl                ; cxl2 := windowed left
+                                                ;   (2 instructions)
+        Least   cxr2, cxr2, cxr                 ; cxr2 := windowed right
+                                                ;   (2 instructions)
+        Greatest cyb2, cyb3, cyb                ; cyb2 := windowed bottom
+                                                ;   (3 instructions)
+        Least   cyt2, R14, cyt                  ; cyt2 := windowed top
+                                                ;   (3 instructions)
+
+        SUBS    cyb2, cyt2, cyb2                ; top >= bottom ?
+        CMPGE   cxr2, cxl2                      ; and right >= left ?
+        Pull    "PC", LT                        ; Exit if not. Otherwise cyb2
+                                                ;   has become clipped height
+
+        LDR     R14, [WsPtr, #CursorFlags]      ; update changed box, if wanted
+        TST     R14, #ClipBoxEnableBit
+        BLNE    ClipVdu5
+
+        ADD     charptr, charptr, cyt           ; adjust charptr to point at
+        SUB     charptr, charptr, cyt2          ;   first used row of character
+
+        TEQ     sizey, #16                      ; If double height, signal this
+        RSBEQ   charptr, charptr, #0            ;   by making fontptr negative
+
+        MOV     charmsk, #&FF                   ; Produce mask to do left/right
+        SUB     R14, cxl2, cxl                  ;   clipping
+        MOV     charmsk, charmsk, LSR R14
+        SUB     R14, cxr2, cxl2
+        ADD     R14, R14, #1
+        BIC     charmsk, charmsk, charmsk, LSR R14
+
+        ASSERT  llength > scr
+        ASSERT  LineLength = YWindLimit+4
+        LDR     lgBPC, [WsPtr, #Log2BPC]        ; Address point at (cxl, cyt2),
+        MOV     charshf, cxl, LSL lgBPC         ;   putting address in scr, bit
+        ADD     scr, WsPtr, #YWindLimit         ;   offset in charshf and
+        LDMIA   scr, {scr, llength}             ;   adjusting ecfptr
+        SUB     scr, scr, cyt2
+        AND     R14, scr, #7
+        ADD     ecfptr, ecfptr, R14, LSL #3
+        LDR     R14, [WsPtr, #ScreenStart]
+        MLA     scr, llength, scr, R14
+        MOV     R14, charshf, ASR #5
+        ADD     scr, scr, R14, LSL #2
+        AND     charshf, charshf, #31
+
+        MLA     scrend, llength, cyb2, scr      ;Calculate terminating address
+
+        RSB     invcharshf, charshf, #32        ;And inverse shift
+
+Vdu5NextLine
+        ASSERT  eormask > ormask
+        LDMIA   ecfptr!, {ormask, eormask}      ;Pick up ecf info and advance
+        TST     ecfptr, #63                     ;  ecf pointer, wrapping if
+        SUBEQ   ecfptr, ecfptr, #64             ;  necessary
+
+        MVNS    charbyte, charptr               ;Pick up character definition
+        LDRPLB  charbyte, [charbyte, -charbyte, LSR #1] ;  byte, using tricky
+        SUBPL   charptr, charptr, #1            ;  code for double height (and
+        LDRMIB  charbyte, [charptr], #1         ;  simple code for single!)
+
+        ANDS    charbyte, charbyte, charmsk     ;Clip left & right; skip loop
+        BEQ     Vdu5TryNextLine                 ;  body if nothing to plot
+
+        Push    "ecfptr,scrend,charmsk,llength,charptr"
+
+      [ VIDC_Type = "VIDC20"
+        LDR     R14, [WsPtr, #TextExpandArea]   ;Address correct expansion
+      |
+        ADD     R14, WsPtr, #TextExpand         ;Address correct expansion
+      ]
+        ADD     charptr, charbyte, #&100        ;  table entry, assuming that
+        ADD     charptr, R14, charptr, LSL lgBPC        ;  this is not mode 10 or greater
+
+        CMP     lgBPC, #1                       ;Pick up this entry:
+        LDRLSB  cword0, [charptr, #0]           ;  1 byte if lgBPC = 0
+        LDREQB  R14, [charptr, #1]              ;  2 bytes if lgBPC = 1
+        ORREQ   cword0, cword0, R14, LSL #8
+        MOV     cword1, #0
+        BLS     Vdu5DoOneWord
+        CMP     lgBPC, #3
+        LDRLO   cword0, [charptr, #0]           ;  1 word if lgBPC = 2
+        BLO     Vdu5DoOneWord
+        BHI     Vdu5Mode10                      ;  4 words if lgBPC = 4
+        LDMIA   charptr, {cword0, cword1}       ;  2 words if lgBPC = 3
+        MOVS    cword2, cword1, LSR invcharshf
+Vdu5DoTwoWords
+        LDRNE   chartmp, [scr, #8]
+        ANDNE   R14, ormask, cword2
+        ORRNE   chartmp, chartmp, R14
+        ANDNE   R14, eormask, cword2
+        EORNE   chartmp, chartmp, R14
+        STRNE   chartmp, [scr, #8]
+        MOV     cword1, cword1, LSL charshf
+Vdu5DoOneWord
+        ORRS    cword1, cword1, cword0, LSR invcharshf
+        LDRNE   chartmp, [scr, #4]
+        ANDNE   R14, ormask, cword1
+        ORRNE   chartmp, chartmp, R14
+        ANDNE   R14, eormask, cword1
+        EORNE   chartmp, chartmp, R14
+        STRNE   chartmp, [scr, #4]
+        MOVS    cword0, cword0, LSL charshf
+        LDRNE   chartmp, [scr]
+        ANDNE   R14, ormask, cword0
+        ORRNE   chartmp, chartmp, R14
+        ANDNE   R14, eormask, cword0
+        EORNE   chartmp, chartmp, R14
+        STRNE   chartmp, [scr]
+
+Vdu5LinePainted
+        Pull    "ecfptr,scrend,charmsk,llength,charptr"
+
+Vdu5TryNextLine
+        TEQ     scr, scrend
+        ADDNE   scr, scr, llength
+        BNE     Vdu5NextLine
+
+        Pull    "PC"
+
+; This is mode 10 (or similar) - we must do bit expansion on the fly
+
+Vdu5Mode10
+      [ VIDC_Type = "VIDC20"
+        CMP     lgBPC, #5                       ; is this a 32 bit per pixel mode, if so then ignore the
+        BEQ     Vdu5Mode32                      ; existing code and use a newer function
+      ]
+
+        ADRL    charptr, C16BTab
+        AND     R14, charbyte, #&0F
+        ADD     R14, charptr, R14, LSL #3
+        LDMIA   R14, {cword0, cword1}
+
+        MOVS    cword2, cword1, LSR invcharshf
+        LDRNE   chartmp, [scr, #16]
+        ANDNE   R14, ormask, cword2
+        ORRNE   chartmp, chartmp, R14
+        ANDNE   R14, eormask, cword2
+        EORNE   chartmp, chartmp, R14
+        STRNE   chartmp, [scr, #16]
+        MOV     cword1, cword1, LSL charshf
+        ORRS    cword1, cword1, cword0, LSR invcharshf
+        LDRNE   chartmp, [scr, #12]
+        ANDNE   R14, ormask, cword1
+        ORRNE   chartmp, chartmp, R14
+        ANDNE   R14, eormask, cword1
+        EORNE   chartmp, chartmp, R14
+        STRNE   chartmp, [scr, #12]
+        MOVS    cword2, cword0, LSL charshf
+
+        AND     R14, charbyte, #&F0
+        ADD     R14, charptr, R14, LSR #1
+        LDMIA   R14, {cword0, cword1}
+        ORRS    cword2, cword2, cword1, LSR invcharshf
+        B       Vdu5DoTwoWords
+
+; Expand the character data out to 32 bit per pixel (mode 48 or similar)
+
+      [ VIDC_Type = "VIDC20"
+
+        MACRO
+$l      PixMunge32 $reg, $mask, $offset
+$l
+        TST     $reg, #$mask
+        LDRNE   chartmp, [scr, #$offset]
+        ORRNE   chartmp, chartmp, ormask
+        EORNE   chartmp, chartmp, eormask
+        STRNE   chartmp, [scr, #$offset]
+        MEND
+
+; Perform bit expansion on the fly, this is a word based operation, no need to
+; extract sub-pixels from the ORR / EOR masks, simply perform the bit test and
+; then write the character data to the screen!
+
+Vdu5Mode32
+
+        PixMunge32 charbyte, 1<<7, 0
+        PixMunge32 charbyte, 1<<6, 4
+        PixMunge32 charbyte, 1<<5, 8
+        PixMunge32 charbyte, 1<<4, 12
+        PixMunge32 charbyte, 1<<3, 16
+        PixMunge32 charbyte, 1<<2, 20
+        PixMunge32 charbyte, 1<<1, 24
+        PixMunge32 charbyte, 1<<0, 28
+
+        B       Vdu5LinePainted                 ; flow down and try the next line
+
+      ]
+
+; *****************************************************************************
+;
+;       Slow VDU 5 - Print char by scaled method (in SprExtend)
+;
+; in:   R1 (ecfptr)   = pointer to ecf pattern
+;       R7 (sizex)    = SizeX
+;       R8 (sizey)    = SizeY
+;       R9 (charbyte) = character to plot
+;
+;       Stack:  Return address
+;
+
+        ASSERT  ecfptr   = R1
+        ASSERT  sizex    = R7
+        ASSERT  sizey    = R8
+        ASSERT  charbyte = R9
+
+SlowVdu5 ROUT
+
+        MOV     R10, R1                         ; R10 := ecfptr
+
+; now save current GCOL on stack if necessary
+
+        ADD     R11, WsPtr, #FgEcfOraEor
+        TEQ     R10, R11                        ; if going to use this one
+        BEQ     %FT20                           ; then skip
+
+        MOV     R0, #64                         ; 64 bytes
+10
+        LDMIA   R11, {R3-R6}                    ; copy old GCOL into stack
+        Push    "R3-R6"                         ;   frame, reversing order of
+                                                ;   16 byte chunks
+        LDMIA   R10!, {R3-R6}                   ; copy new colour
+        STMIA   R11!, {R3-R6}                   ; into GCOL
+        SUBS    R0, R0, #16
+        BNE     %BT10
+
+20
+        MOV     R4, #8
+        MOV     R5, #8
+        Push    "R4,R5"
+        Push    "R7,R8"
+        MOV     R6, R13                         ; R6 -> scaling block
+
+        SUB     R1, R8, #1                      ; SizeY-1
+        LDR     R5, [WsPtr, #YEigFactor]
+        ADD     R3, WsPtr, #GCsX
+        LDMIA   R3, {R3, R4}                    ; R3 = ext X; R4 = ext Y (top)
+        SUB     R4, R4, R1, LSL R5              ; R4 = ext Y (bottom)
+        MOV     R1, R9                          ; R1 = char
+        MOV     R0, #SpriteReason_PaintCharScaled
+        SWI     XOS_SpriteOp
+
+        ADD     R13, R13, #4*4                  ; junk scaling block
+
+        TEQ     R10, R11                        ; if we didn't copy GCOLs
+        Pull    "PC", EQ                        ; then return, else copy back
+
+        MOV     R0, #64
+30
+        Pull    "R3-R6"                         ; Reverse order of 16 byte
+        STMDB   R11!, {R3-R6}                   ; chunks during copy again
+        SUBS    R0, R0, #16
+        BNE     %BT30
+        Pull    PC
+
+
+; *****************************************************************************
+;
+;       VDU 5 - Start printing text at graphics cursor
+
+ENQ
+      [ {TRUE}
+        GraphicsMode R0
+        MOVNE   PC, R14
+      |
+        LDR     R0, [WsPtr, #NPix]              ; Graphics mode ?
+        TEQ     R0, #0
+        MOVEQ   PC, R14                         ; no, then return
+      ]
+
+        MOV     R1, #0
+        BSR     CursorOnOff                     ; turn cursor off without
+                                                ; saving to copy
+
+        LDR     R6, [WsPtr, #CursorFlags]
+        ORR     R6, R6, #Vdu5Bit
+        B       R6toCursorFlags
+
+; *****************************************************************************
+;
+;       VDU 4 - Return to printing text at text cursor
+
+EOT
+      [ {TRUE}
+        GraphicsMode R0
+        MOVNE   PC,LR
+      |
+        LDR     R0, [WsPtr, #NPix]              ; Graphics mode ?
+        TEQ     R0, #0
+        MOVEQ   PC, R14                         ; no, then return
+      ]
+
+        MOV     R1, #1
+        BSR     CursorOnOff                     ; restore cursor from copy
+
+        LDR     R6, [WsPtr, #CursorFlags]
+        BIC     R6, R6, #Vdu5Bit
+        B       R6toCursorFlags
+
+; *****************************************************************************
+;
+;       Vdu5HT - Move cursor "right" when in VDU 5 mode
+;
+; in:   R6 = CursorFlags
+
+Vdu5HT
+        Push    R14
+        BL      GCursorMove             ; try to move in +ve X direction
+        BCC     EndVdu5HT               ; if successful, finish
+        BL      GCursorB0               ; else move cursor to -ve X boundary
+Vdu5HT10
+        EOR     R6, R6, #8              ; change to +ve Y direction
+        BL      GCursorMove             ; try to move in that direction
+        BLCS    GCursorB0               ; if unsuccessful, move to -ve Y bdy
+EndVdu5HT
+        Pull    R14
+        B       IEG
+
+; *****************************************************************************
+;
+;       Vdu5BS - Move cursor "left" when in VDU 5 mode
+;
+; in:   R6 = CursorFlags
+
+Vdu5BS
+        EOR     R6, R6, #6              ; go to -ve X direction
+        B       Vdu5HT                  ; dead easy, huh ?
+
+; *****************************************************************************
+;
+;       Vdu5LF - Move cursor "down" when in VDU 5 mode
+;
+; in:   R6 = CursorFlags
+
+Vdu5LF
+        Push    R14
+        B       Vdu5HT10
+
+; *****************************************************************************
+;
+;       Vdu5VT - Move cursor "up" when in VDU 5 mode
+;
+; in:   R6 = CursorFlags
+
+Vdu5VT
+        EOR     R6, R6, #6              ; go to -ve Y direction (eventually)
+        B       Vdu5LF
+
+; *****************************************************************************
+;
+;       Vdu5CR - Carriage return in VDU 5 mode
+;
+; in:   R6 = CursorFlags
+
+Vdu5CR
+        BSR     GCursorB0
+        B       IEG
+
+; *****************************************************************************
+;
+;       Vdu5FF - Clear screen in VDU 5 mode
+;
+; in:   R6 = CursorFlags
+
+Vdu5FF
+        BSR     Home
+        B       CLG
+
+; *****************************************************************************
+;
+;       Vdu5TAB - TAB(X,Y) in VDU 5 mode
+;
+; in:   R0 = X, R1 = Y, R6 = CursorFlags
+
+Vdu5TAB
+        BSR     GCursorBdy
+        EOR     R6, R6, #8
+        MOV     R0, R1
+        BSR     GCursorBdy
+        B       IEG
+
+; *****************************************************************************
+;
+;       Vdu5Delete - Delete in VDU 5 mode (already done backspace)
+;
+; in:   R6 = CursorFlags
+
+Vdu5Delete
+        ADD     ecfptr, WsPtr, #BgEcfStore ; STORE background colour/ecf
+        MOV     R0, #127                   ; uses hard font for this char
+        B       Vdu5DoChar10
+
+; *****************************************************************************
+;
+;       GCursorMove - Move graphics cursor in direction specified by R6
+;       (0,4 = right; 2,6 = left; 8,10 = down; 12,14 = up)
+;       R6 is preserved
+
+GCursorMove
+        ADD     R0, WsPtr, #GCsIX
+        LDMIA   R0, {R0, R1}                    ; R0 = GCsIX; R1 = GCsIY
+        ADD     R2, WsPtr, #GCharSpacing+8      ; +8 needed to address it
+        LDMDB   R2, {R2, R3}                    ; R2=GCharSpaceX;R3=GCharSpaceY
+        WINDow  R0, R1, R8,R9,R10,R11           ; return LT if outside window
+        ADR     R7, GCMVTab
+        B       DoJumpTable
+
+GCMVTab
+        &       GMoveRight-GCMVTab
+        &       GMoveLeft-GCMVTab
+        &       GMoveRight-GCMVTab
+        &       GMoveLeft-GCMVTab
+        &       GMoveDown-GCMVTab
+        &       GMoveDown-GCMVTab
+        &       GMoveUp-GCMVTab
+        &       GMoveUp-GCMVTab
+
+; Move graphics cursor right if possible - on exit C=0 iff OK to move
+
+GMoveRight
+        ADD     R0, R0, R2              ; add on GCharSpaceX
+GMove10
+        STR     R0, [WsPtr, #GCsIX]
+GMove15
+        BLT     GMoveOK                 ; if we were outside already
+        TST     R6, #&40                ; or we are in nowrap mode
+        BNE     GMoveOK                 ; then we were OK to move
+        WINDow  R0, R1, R8,R9,R10,R11
+        BGE     GMoveOK                 ; if inside now, then OK
+        SEC
+        MOV     PC, R14
+
+GMoveOK
+        CLC
+        MOV     PC, R14
+
+; Move graphics cursor left if possible - on exit C=0 iff OK to move
+
+GMoveLeft
+        SUB     R0, R0, R2              ; subtract off GCharSpaceX
+        B       GMove10
+
+; Move graphics cursor down if possible - on exit C=0 iff OK to move
+
+GMoveDown
+        SUB     R1, R1, R3              ; subtract off GCharSpaceY
+GMove20
+        STR     R1, [WsPtr, #GCsIY]
+        B       GMove15
+
+; Move graphics cursor up if possible - on exit C=0 iff OK to move
+
+GMoveUp
+        ADD     R1, R1, R3              ; add on GCharSpaceY
+        B       GMove20
+
+; *****************************************************************************
+;
+;       Move graphics cursor to boundary indicated by value of R6 in bits 1-3
+;       (0 or 4 = left, 2 or 6 = right, 8 or 10 = up, 12 or 14 = down)
+;       + R0 character positions in
+;
+
+GCursorB0
+        MOV     R0, #0
+GCursorBdy
+        ADR     R7, GCBDYTab
+        B       DoJumpTable
+
+GCBDYTab
+        &       GCursorLBdy-GCBDYTab
+        &       GCursorRBdy-GCBDYTab
+        &       GCursorLBdy-GCBDYTab
+        &       GCursorRBdy-GCBDYTab
+        &       GCursorUBdy-GCBDYTab
+        &       GCursorUBdy-GCBDYTab
+        &       GCursorDBdy-GCBDYTab
+        &       GCursorDBdy-GCBDYTab
+
+; Move graphics cursor to left boundary + R0 characters in
+
+GCursorLBdy
+        LDR     R7, [WsPtr, #GWLCol]
+        LDR     R2, [WsPtr, #GCharSpaceX]
+        MLA     R0, R2, R0, R7                  ; GCsIX := GWLCol + (X*SpaceX)
+        STR     R0, [WsPtr, #GCsIX]
+        MOV     PC, R14
+
+; Move graphics cursor to right boundary + R0 characters in
+; (adjust for character size)
+
+GCursorRBdy
+        LDR     R7, [WsPtr, #GWRCol]
+        LDR     R2, [WsPtr, #GCharSizeX]
+        SUB     R7, R2, R7                      ; R7 = SizeX-GWRCol
+        LDR     R2, [WsPtr, #GCharSpaceX]
+        MLA     R0, R2, R0, R7                  ; R0 = SizeX-GWRCol+X*SpaceX
+        RSB     R0, R0, #1                      ; GWRCol-X*SpaceX-(SizeX-1)
+        STR     R0, [WsPtr, #GCsIX]
+        MOV     PC, R14
+
+; Move graphics cursor to up boundary + R0 characters in
+
+GCursorUBdy
+        LDR     R7, [WsPtr, #GWTRow]
+        LDR     R2, [WsPtr, #GCharSpaceY]
+        RSB     R2, R2, #0                      ; -SizeY
+        MLA     R0, R2, R0, R7                  ; GWTRow-Y*SizeY
+        STR     R0, [WsPtr, #GCsIY]
+        MOV     PC, R14
+
+; Move graphics cursor to down boundary + R0 characters in
+; (adjust for character size)
+
+GCursorDBdy
+        LDR     R7, [WsPtr, #GWBRow]
+        LDR     R2, [WsPtr, #GCharSpaceY]
+        MLA     R0, R2, R0, R7                  ; GWBRow+Y*SpaceY
+        LDR     R2, [WsPtr, #GCharSizeY]
+        ADD     R0, R0, R2
+        SUB     R0, R0, #1                      ; GWBRow+Y*SpaceY+(SizeY-1)
+        STR     R0, [WsPtr, #GCsIY]
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       ClipVdu5 - Add a VDU 5 char to clip box
+;
+; in:   cxl2 = left
+;       cyb2 = top-bottom
+;       cxr2 = right
+;       cyt2 = top
+;
+; out:  All registers preserved
+;
+
+        ASSERT  (cxl2=R4) :LAND: (cyb2=R3) :LAND: (cxr2=R10) :LAND: (cyt2=R2)
+
+ClipVdu5 ROUT
+        Push    "R0-R3, R4, R10, R14"
+        MOV     R0, cxl2
+        SUB     R1, cyt2, cyb2       ; top-(top-bottom) = bottom
+        MOV     R3, cyt2
+        MOV     R2, cxr2
+        BL      MergeClipBox
+        Pull    "R0-R3, R4, R10, PC"
+
+        END
diff --git a/s/vdu/vducursoft b/s/vdu/vducursoft
new file mode 100644
index 0000000000000000000000000000000000000000..4876557f9a6fe8df7670825a4307ce967b1c6bb0
--- /dev/null
+++ b/s/vdu/vducursoft
@@ -0,0 +1,665 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduCurSoft
+
+SlowCursorSpeed * 16
+FastCursorSpeed * 8
+OnFlashTime * 48
+OffFlashTime * 16
+
+;       InitCursor - initialise cursor shape and address
+
+InitCursor ROUT
+        Push    R14
+        LDR     R6, [WsPtr, #CursorFlags]
+        TST     R6, #TeletextMode               ; if teletext mode
+        MOVNE   R0, #&72                        ; then start at 9 (slow flash)
+        MOVEQ   R0, #&67                        ; else start at 7 (slow flash)
+        BL      ProgReg10AndCopy
+
+        MOV     R0, #1                          ; set to flash immediately
+        STR     R0, [WsPtr, #CursorCounter]     ; AFTER setting CursorSpeed
+                                                ; in case VSYNC happens
+        LDR     R1, [WsPtr, #VduSprite]
+        TEQ     R1, #0                          ; if outputting to sprite
+        MOVNE   R0, #&20                        ; then turn cursor off
+        BLNE    ProgReg10AndCopy
+
+        LDR     R0, [WsPtr, #RowMult]           ; 8 or 10 or 16 or 20
+        Pull    R14
+
+; and drop thru to ...
+
+SetCursorBottom
+        LDR     R1, [WsPtr, #LineLength]
+        MUL     R2, R1, R0
+        STR     R2, [WsPtr, #CursorEndOffset]
+        MOV     PC, R14
+
+SetCursorTop
+        LDR     R1, [WsPtr, #LineLength]
+        MUL     R2, R1, R0
+        STR     R2, [WsPtr, #CursorStartOffset]
+        MOV     PC, R14
+
+; *****************************************************************************
+
+; Cursors are split, so remove output cursor
+
+DoOutputCursor ROUT
+        Push    R14
+        LDR     R1, [WsPtr, #LineLength]
+        MOV     R1, R1, LSL #3                  ; and end after 8 rows
+        LDR     R0, [WsPtr, #ModeFlags]
+        TST     R0, #Flag_DoubleVertical        ; if double vertical
+        ADDNE   R1, R1, R1                      ; end after 16 rows
+        MOV     R0, #0                          ; start on top line
+        BL      EORCursor
+        LDR     R2, [WsPtr, #InputCursorAddr]   ; flashing cursor is at input
+        Pull    R14
+        B       PreWC10
+
+; *****************************************************************************
+;
+;       PreWrchCursor - Remove cursors prior to Wrch
+;
+; out:  R6 = new CursorFlags; R5,R7 preserved; others corrupted
+;
+
+PreWrchCursor
+
+; Need to disable IRQs here to stop Vsync modifying CursorFlags in between
+; us reading it and writing it
+
+        SETPSR  I_bit, R1
+        LDR     R6, [WsPtr, #CursorFlags]
+        MOVS    R0, R6, LSL #(32-InWrchBitPosn) ; CS => was already off
+        ORRCC   R6, R6, #InWrchBit              ; protect against vsyncs
+        STRCC   R6, [WsPtr, #CursorFlags]
+
+        LDR     R0, [WsPtr, #CursorStack]
+        MOV     R0, R0, RRX                     ; shift down + put current
+                                                ; state in top bit
+        STR     R0, [WsPtr, #CursorStack]
+
+        MOVCSS  PC, R14                         ; already off, so exit
+                                                ; (restoring I_bit)
+        TEQP    R14, #0                         ; restore old I_bit
+
+        LDR     R2, [WsPtr, #CursorAddr]        ; point to output
+        TST     R6, #CursorsSplit
+        BNE     DoOutputCursor
+PreWC10
+        TST     R6, #ActualState
+        MOVEQ   PC, R14                         ; flash cursor is off anyway
+        BIC     R6, R6, #ActualState
+        STR     R6, [WsPtr, #CursorFlags]
+
+; and drop thru to EORFlashCursor
+
+; *****************************************************************************
+;
+;       EORCursor - Exclusive-OR cursor with screen
+;
+; in:   R0 = Start offset from top of cursor
+;       R1 = End+1 offset
+;       R2 = Screen address of appropriate cursor
+;       R6 = CursorFlags
+;
+; out:  R5-R7 preserved; R0-R4, R8-R11 corrupted
+;
+
+EORFlashCursor
+        ASSERT  CursorEndOffset-CursorStartOffset=4
+        ADD     R0, WsPtr, #CursorStartOffset
+        LDMIA   R0, {R0,R1}
+EORCursor
+        CMP     R0, R1
+        MOVCS   PC, R14                 ; top >= bottom, so nowt
+
+        ADD     R1, R1, R2
+
+        LDR     R3, [WsPtr, #LineLength]
+
+        ASSERT  CursorNbit = CursorFill +4
+        ADD     R4, WsPtr, #CursorFill
+        LDMIA   R4, {R4, PC}            ; load CursorFill and CursorNbit
+
+Cursor1bit
+        LDRB    R8, [R0, R2]!
+        EOR     R8, R8, R4
+        STRB    R8, [R0], R3
+        TEQ     R0, R1
+        MOVEQ   PC, R14
+Cursor1loop
+        LDRB    R8, [R0]
+        EOR     R8, R8, R4
+        STRB    R8, [R0], R3
+        TEQ     R0, R1
+        BNE     Cursor1loop
+        MOV     PC, R14
+
+Cursor2bit
+        ADD     R0, R0, R2
+Cursor2loop
+        LDRB    R8, [R0, #1]
+        EOR     R8, R8, R4
+        STRB    R8, [R0, #1]
+        LDRB    R8, [R0]
+        EOR     R8, R8, R4
+        STRB    R8, [R0], R3
+        TEQ     R0, R1
+        BNE     Cursor2loop
+        MOV     PC, R14
+
+CursorTeletext
+        Push    "R0, R1, R14"
+        ADD     R0, R0, #40*1024                ; go to other screen
+        ADD     R1, R1, #40*1024
+        BL      Cursor4bit
+        Pull    "R0, R1, R14"
+
+; and drop thru to ...
+
+Cursor4bit
+        LDR     R8, [R0, R2]!
+        EOR     R8, R8, R4
+        STR     R8, [R0], R3
+        TEQ     R0, R1
+        MOVEQ   PC, R14
+Cursor4loop
+        LDR     R8, [R0]
+        EOR     R8, R8, R4
+        STR     R8, [R0], R3
+        TEQ     R0, R1
+        BNE     Cursor4loop
+        MOV     PC, R14
+
+Cursor8bit
+        ADD     R0, R0, R2
+Cursor8loop
+        LDMIA   R0, {R8,R9}
+        EOR     R8, R8, R4
+        EOR     R9, R9, R4
+        STMIA   R0, {R8,R9}
+        ADD     R0, R0, R3
+        TEQ     R0, R1
+        BNE     Cursor8loop
+        MOV     PC, R14
+
+Cursor16bit
+        ADD     R0, R0, R2
+Cursor16loop
+        LDMIA   R0, {R8-R11}
+        EOR     R8, R8, R4
+        EOR     R9, R9, R4
+        EOR     R10, R10, R4
+        EOR     R11, R11, R4
+        STMIA   R0, {R8-R11}
+        ADD     R0, R0, R3
+        TEQ     R0, R1
+        BNE     Cursor16loop
+        MOV     PC, R14
+
+Cursor32bit
+        ADD     R0, R0, R2
+        SUB     R3, R3, #32
+Cursor32loop
+        LDMIA   R0, {R8-R11}
+        EOR     R8, R8, R4
+        EOR     R9, R9, R4
+        EOR     R10, R10, R4
+        EOR     R11, R11, R4
+        STMIA   R0!, {R8-R11}
+        LDMIA   R0, {R8-R11}
+        EOR     R8, R8, R4
+        EOR     R9, R9, R4
+        EOR     R10, R10, R4
+        EOR     R11, R11, R4
+        STMIA   R0!, {R8-R11}
+        ADD     R0, R0, R3
+        TEQ     R0, R1
+        BNE     Cursor32loop
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       PostWrchCursor - Put back cursors after Wrch
+;
+; out:  R6 = new CursorFlags, R5,R7 preserved, all others undefined
+;
+
+PostWrchCursor
+        LDR     R6, [WsPtr, #CursorFlags]
+        LDR     R0, [WsPtr, #CursorStack]
+        MOVS    R0, R0, LSL #1
+        STR     R0, [WsPtr, #CursorStack]
+        MOVCS   PC, R14                         ; we're still off, so do nowt
+
+        Push    R14
+        [       ForceMark
+        LDR     R0, [WsPtr, #CursorCounter]
+        TEQ     R0, #0                          ; are we flashing
+        LDRNE   R0, [WsPtr, #CursorSpeed]       ; force to start of mark state
+        STRNE   R0, [WsPtr, #CursorCounter]
+        MOVNE   R0, #ActualState
+        STRNE   R0, [WsPtr, #CursorDesiredState]
+        ]
+
+        [       RePlot
+        LDR     R1, [WsPtr, #CursorDesiredState]
+        EOR     R1, R1, R6              ; EOR of desired and actual
+        ANDS    R1, R1, #ActualState    ; just get that bit
+        BEQ     PWC10                   ; same, then go home
+
+        EOR     R6, R1, R6              ; EOR actual bit
+
+        TST     R6, #CursorsSplit
+        LDRNE   R2, [WsPtr, #InputCursorAddr]
+        LDREQ   R2, [WsPtr, #CursorAddr]
+        BL      EORFlashCursor
+PWC10
+        ]
+
+        BIC     R6, R6, #InWrchBit              ; coming out of wrch now
+        TST     R6, #CursorsSplit
+        STREQ   R6, [WsPtr, #CursorFlags]
+        Pull    PC, EQ                          ; return if no output cursor
+
+        LDR     R2, [WsPtr, #CursorAddr]
+        LDR     R1, [WsPtr, #LineLength]
+        MOV     R1, R1, LSL #3                  ; and end after 8 rows
+        LDR     R0, [WsPtr, #ModeFlags]
+        TST     R0, #Flag_DoubleVertical        ; if double vertical
+        ADDNE   R1, R1, R1                      ; end after 16 rows
+        MOV     R0, #0                          ; start on top line
+        BL      EORCursor
+        STR     R6, [WsPtr, #CursorFlags]       ; only clear it now ?
+        Pull    PC
+
+
+; *****************************************************************************
+
+VsyncCall ROUT
+        Push    "R0-R11,R14"
+
+ [ AssemblePointerV
+        BL      PollPointer
+ ]
+        BL      UpdatePointer
+
+        LDR     R6, [WsPtr, #CursorFlags]
+        TST     R6, #TeletextMode
+        BLNE    TeletextFlashTest               ; if TTX, do other stuff
+VsyncReturn
+        LDR     R1, [WsPtr, #CursorDesiredState]
+        LDR     R0, [WsPtr, #CursorCounter]
+        SUBS    R0, R0, #1
+        LDREQ   R0, [WsPtr, #CursorSpeed]       ; if is zero, reload
+        EOREQ   R1, R1, #ActualState            ; and toggle desired state
+        STREQ   R1, [WsPtr, #CursorDesiredState]
+
+        STRCS   R0, [WsPtr, #CursorCounter]     ; store back unless was frozen
+
+        TST     R6, #InWrchBit
+        Pull    "R0-R11,PC", NE         ; in wrch, so don't touch screen
+                                        ; or modify CursorFlags
+
+        EOR     R1, R1, R6              ; EOR of desired and actual
+        ANDS    R1, R1, #ActualState    ; just get that bit
+        Pull    "R0-R11,PC", EQ         ; same, then go home
+
+        EOR     R6, R1, R6              ; EOR actual bit
+        STR     R6, [WsPtr, #CursorFlags]
+
+        TST     R6, #CursorsSplit
+        LDRNE   R2, [WsPtr, #InputCursorAddr]
+        LDREQ   R2, [WsPtr, #CursorAddr]
+        BL      EORFlashCursor
+
+        Pull    "R0-R11,PC"
+
+; *****************************************************************************
+
+TeletextFlashTest ROUT
+        LDR     R3, [WsPtr, #TeletextCount]
+        SUBS    R3, R3, #1
+        BNE     %FT20                           ; count not expired
+
+        LDR     R1, [WsPtr, #TeletextOffset]
+        EORS    R1, R1, #40*1024                ; switch to other flash bank
+        STR     R1, [WsPtr, #TeletextOffset]
+        MOVEQ   R3, #OnFlashTime
+        MOVNE   R3, #OffFlashTime
+        LDR     R0, [WsPtr, #DisplayStart]
+        BL      SetVinit                        ; preserves R3
+20
+        STR     R3, [WsPtr, #TeletextCount]
+        B       VsyncReturn
+
+; *****************************************************************************
+;
+;       Vdu23_0_10 - Program cursor start, flash/steady, on/off
+;
+
+Vdu23_0_10
+        LDRB    R0, [WsPtr, #QQ+2]      ; get parameter
+ProgReg10AndCopy
+        STR     R0, [WsPtr, #Reg10Copy]
+
+; and drop thru to ...
+
+ProgReg10
+        AND     R1, R0, #&60
+        CMP     R1, #&40
+        BCS     IsFlashing
+        MOV     R2, #0
+        STR     R2, [WsPtr, #CursorCounter]       ; freeze the flashing
+        TST     R1, #&20
+        MOVEQ   R2, #ActualState                ; steady cursor
+        STR     R2, [WsPtr, #CursorDesiredState]
+        B       PR1010
+
+IsFlashing
+        TST     R1, #&20
+        MOVEQ   R2, #FastCursorSpeed
+        MOVNE   R2, #SlowCursorSpeed
+        STR     R2, [WsPtr, #CursorSpeed]
+        LDR     R2, [WsPtr, #CursorCounter]
+        TEQ     R2, #0                          ; was flashing frozen ?
+                                                ; if not, don't perturb flash
+        MOVEQ   R2, #1                          ; set to flash immediately
+        STREQ   R2, [WsPtr, #CursorCounter]
+
+PR1010
+        AND     R0, R0, #&1F                    ; get start position bits
+        TST     R6, #TeletextMode               ; if teletext mode
+        MOVNE   R0, R0, LSR #1                  ; then divide by 2
+        LDR     R1, [WsPtr, #ModeFlags]
+        TST     R1, #Flag_DoubleVertical        ; if double vertical
+        MOVNE   R0, R0, LSL #1                  ; then double cursor value
+
+        LDR     R1, [WsPtr, #RowMult]
+        CMP     R0, R1                          ; is it > rowmult ?
+        MOVHI   R0, R1                          ; set to rowmult if so
+        B       SetCursorTop
+
+
+; *****************************************************************************
+;
+;       Vdu23_0_11 - Program cursor end
+;
+
+Vdu23_0_11
+        LDRB    R0, [WsPtr, #QQ+2]      ; get parameter
+        TST     R6, #TeletextMode       ; if teletext
+        MOVNE   R0, R0, LSR #1          ; then divide by 2
+
+        ADD     R0, R0, #1              ; get end line +1
+
+        LDR     R1, [WsPtr, #ModeFlags]
+        TST     R1, #Flag_DoubleVertical ; if double vertical
+        MOVNE   R0, R0, LSL #1           ; then double cursor value
+
+        LDR     R1, [WsPtr, #RowMult]
+        CMP     R0, R1
+        MOVHI   R0, R1                  ; if > rowmult, set to rowmult
+        B       SetCursorBottom
+
+; *****************************************************************************
+;
+;       Vdu23_1 - Program cursor
+;
+
+Vdu23_1
+        LDR     R0, [WsPtr, #CursorFlags]
+        TST     R0, #Vdu5Bit
+        MOVNE   PC, R14                 ; none of this nonsense in VDU5 mode
+
+        LDRB    R1, [WsPtr, #QQ+1]      ; get parameter
+
+CursorOnOff
+        CMP     R1, #1
+        MOVCC   R0, #&20                ; 0 -> just turn off
+        LDRCS   R0, [WsPtr, #Reg10Copy] ; 1,2,3 -> read old state
+        BLS     ProgReg10               ; 0,1 -> program hardware
+        TEQ     R1, #2
+        BICEQ   R0, R0, #&60            ; 2 -> steady
+        ORRNE   R0, R0, #&60            ; 3 -> slow flashing
+        STR     R0, [WsPtr, #Reg10Copy] ; save in copy
+        B       ProgReg10
+
+; *****************************************************************************
+;
+;       DoCursorEdit
+;
+; in:   R0 = &87 => COPY
+;            &88 => cursor left
+;            &89 => cursor right
+;            &8A => cursor down
+;            &8B => cursor up
+;
+; out:  C=0 => doing COPY and valid character
+;       C=1    otherwise
+;
+
+
+DoCursorEdit
+        LDR     R6, [WsPtr, #CursorFlags]
+        TEQ     R0, #&87
+        BNE     IsCursorMove
+
+; COPY character under cursor
+
+        Push    R14
+        TST     R6, #CursorsSplit
+        BEQ     BadCopyExit             ; cursors not split so don't copy
+
+        TST     R6, #Vdu5Bit
+        BNE     BadCopyExit             ; can't copy in VDU5 mode
+
+        BL      ReadCharacter
+
+        TEQ     R0, #0
+        BEQ     BadCopyExit
+
+        BL      VDUBE                   ; is a cursor movement valid ?
+        BNE     DoCE10
+
+        Push    R0
+        BL      PreWrchCursor
+        BL      InputCursorHT
+        BL      PostWrchCursor
+        Pull    R0
+
+DoCE10
+        CLC
+        Pull    PC
+
+BadCopyExit
+        BL      BEL                     ; make bell sound
+        SEC
+        Pull    PC
+
+; *****************************************************************************
+;
+;       VDUBE - Check if valid to move cursor
+;
+; out:  R0 preserved
+;       R1 = 0 => OK, R1<>0 => OK
+;       R6 = CursorFlags
+;       Z => OK, NZ => not OK
+;       C = 1
+
+VDUBE
+        LDR     R6, [WsPtr, #CursorFlags]
+        LDROSB  R1, VDUqueueItems               ; zero if not buffering
+        TEQ     R1, #0
+
+        ANDEQS  R1, R6, #Vdu5Bit                ; zero if not in VDU 5 mode
+
+; insert check for vdu disabled here
+
+        CMP     R1, #0                          ; set Z on R1, C:=1
+        MOV     PC, R14
+
+; *****************************************************************************
+
+IsCursorMove
+        Push    R14
+        BL      VDUBE
+        Pull    PC, NE
+
+        Push    R0
+        BL      PreWrchCursor                   ; remove both cursors
+        Pull    R0
+        BL      ICM10
+        BL      PostWrchCursor
+
+        SEC
+        Pull    PC
+
+ICM10
+        TST     R6, #CursorsSplit
+        BNE     AlreadySplit
+
+        Push    R0
+        LDR     R0, [WsPtr, #Reg10Copy]
+        AND     R0, R0, #&DF                    ; use double flash rate
+        BSR     ProgReg10
+
+        LDR     R0, [WsPtr, #CursorX]           ; copy parameters
+        STR     R0, [WsPtr, #InputCursorX]
+        LDR     R0, [WsPtr, #CursorY]
+        STR     R0, [WsPtr, #InputCursorY]
+        LDR     R0, [WsPtr, #CursorAddr]
+        STR     R0, [WsPtr, #InputCursorAddr]
+
+        ORR     R6, R6, #CursorsSplit
+        STR     R6, [WsPtr, #CursorFlags]
+
+        Pull    R0
+
+AlreadySplit
+        CMP     R0, #&89
+        BCC     InputCursorLeft         ; &88
+        BEQ     InputCursorRight        ; &89
+        CMP     R0, #&8B
+        BCC     InputCursorDown         ; &8A
+
+; and drop thru to ...
+
+InputCursorUp
+        LDR     R1, [WsPtr, #InputCursorY]
+        LDR     R2, [WsPtr, #InputCursorAddr]
+        LDR     R3, [WsPtr, #RowLength]
+        LDR     R4, [WsPtr, #TWTRow]
+
+        SUB     R1, R1, #1
+        SUB     R2, R2, R3
+        CMP     R1, R4
+        LDRLT   R1, [WsPtr, #TWBRow]
+        STR     R1, [WsPtr, #InputCursorY]      ; need signed comparison
+        STRGE   R2, [WsPtr, #InputCursorAddr]   ; in case Y went -ve
+        MOVGE   PC, R14
+        B       AddressInputCursor
+
+InputCursorDown
+        LDR     R1, [WsPtr, #InputCursorY]
+        LDR     R2, [WsPtr, #InputCursorAddr]
+        LDR     R3, [WsPtr, #RowLength]
+        LDR     R4, [WsPtr, #TWBRow]
+
+        ADD     R1, R1, #1
+        ADD     R2, R2, R3
+        CMP     R1, R4
+        LDRHI   R1, [WsPtr, #TWTRow]
+        STR     R1, [WsPtr, #InputCursorY]
+        STRLS   R2, [WsPtr, #InputCursorAddr]
+        MOVLS   PC, R14
+
+; and drop thru to ...
+
+AddressInputCursor
+        Push    R14
+        LDR     R0, [WsPtr, #InputCursorX]
+        LDR     R1, [WsPtr, #InputCursorY]
+        BL      AddressR0R1
+        STR     R2, [WsPtr, #InputCursorAddr]
+        Pull    PC
+
+AddressCursors
+        LDR     R6, [WsPtr, #CursorFlags]
+        TST     R6, #CursorsSplit
+        BEQ     AddressCursor
+        BSR     AddressInputCursor
+        B       AddressCursor
+
+
+InputCursorLeft
+        LDR     R0, [WsPtr, #InputCursorX]
+        LDR     R2, [WsPtr, #InputCursorAddr]
+        LDR     R3, [WsPtr, #BytesPerChar]
+        LDR     R4, [WsPtr, #TWLCol]
+
+        SUB     R0, R0, #1
+        SUB     R2, R2, R3
+        CMP     R0, R4
+        LDRLT   R0, [WsPtr, #TWRCol]
+        STR     R0, [WsPtr, #InputCursorX]
+        STRGE   R2, [WsPtr, #InputCursorAddr]   ; I do mean GE !
+        MOVGE   PC, R14
+
+        BSR     AddressInputCursor
+        B       InputCursorUp
+
+InputCursorRight
+        LDR     R0, [WsPtr, #InputCursorX]
+        LDR     R2, [WsPtr, #InputCursorAddr]
+        LDR     R3, [WsPtr, #BytesPerChar]
+        LDR     R4, [WsPtr, #TWRCol]
+
+        ADD     R0, R0, #1
+        ADD     R2, R2, R3
+        CMP     R0, R4
+        LDRHI   R0, [WsPtr, #TWLCol]
+        STR     R0, [WsPtr, #InputCursorX]
+        STRLS   R2, [WsPtr, #InputCursorAddr]
+        MOVLS   PC, R14
+
+        BSR     AddressInputCursor
+        B       InputCursorDown
+
+; *****************************************************************************
+;
+;       InputCursorHT - move input cursor "right" after copying
+;
+
+InputCursorHT
+        Push    R14
+        LDR     R6, [WsPtr, #CursorFlags]
+        BL      InputCursorMove
+        BCC     ICHTExit
+
+        BL      InputCursorB0
+        BL      AddressInputCursor
+        EOR     R6, R6, #8
+        BL      InputCursorMove
+        BLCS    InputCursorB0
+ICHTExit
+        Pull    R14
+        B       AddressInputCursor
+
+        END
diff --git a/s/vdu/vdudecl b/s/vdu/vdudecl
new file mode 100644
index 0000000000000000000000000000000000000000..bd604275739e1f8feb9d35e0eb949a0100b9d5d2
--- /dev/null
+++ b/s/vdu/vdudecl
@@ -0,0 +1,556 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduDecl
+
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Vdu driver workspace and macro declarations
+;
+; Author R C Manby
+; Date   5.9.86
+;
+
+        GBLL ForceMark                  ; whether we force start of mark state
+ForceMark    SETL   {FALSE}             ; of cursor on exit from WRCH
+
+        GBLL RePlot                     ; Re-plot cursor after wrch
+RePlot  SETL {TRUE}
+
+        GBLL UseVLineOnSolidLines       ; When TRUE VLine is assembled and used
+UseVLineOnSolidLines  SETL {TRUE}       ; to plot vertical solid lines
+
+;
+; Register usage
+; ==============
+;
+StkPtr  RN 13    ;Restore on exit to keep BASIC happy!!
+Link    RN 14
+
+;
+; Manifest constants
+; ==================
+;
+ [ VIDC_Type = "VIDC20"
+
+; Registers
+
+VIDCPalAddress          *       &10000000       ; used in palette programming
+
+LCDOffsetRegister0      *       &30000000
+LCDOffsetRegister1      *       &31000000
+
+HorizCycle              *       &80000000
+HorizSyncWidth          *       &81000000
+HorizBorderStart        *       &82000000
+HorizDisplayStart       *       &83000000
+HorizDisplayEnd         *       &84000000
+HorizBorderEnd          *       &85000000
+HorizCursorStart        *       &86000000       ; used in pointer programming
+HorizInterlace          *       &87000000
+
+VertiCycle              *       &90000000
+VertiSyncWidth          *       &91000000       ; Needed to set up FSIZE register in IOMD
+VertiBorderStart        *       &92000000       ; First register affected by *TV
+VertiDisplayStart       *       &93000000
+VertiDisplayEnd         *       &94000000
+VertiBorderEnd          *       &95000000
+VertiCursorStart        *       &96000000
+VertiCursorEnd          *       &97000000       ; Last register affected by *TV
+
+VIDCExternal            *       &C0000000
+VIDCFSyn                *       &D0000000
+VIDCControl             *       &E0000000
+VIDCDataControl         *       &F0000000
+
+; Pseudo-registers used to return additional information to kernel
+
+PseudoRegister_ClockSpeed *     &FC000000       ; used to indicate real VIDC rclock speed
+PseudoRegister_DPMSState *      &FD000000       ; used to return desired DPMS state
+
+
+; Bits in external register
+
+Ext_HSYNCbits   *       3 :SHL: 16
+Ext_InvertHSYNC *       1 :SHL: 16
+Ext_CompHSYNC   *       2 :SHL: 16
+Ext_InvertCompHSYNC *   3 :SHL: 16
+Ext_VSYNCbits   *       3 :SHL: 18
+Ext_InvertVSYNC *       1 :SHL: 18
+Ext_CompVSYNC   *       2 :SHL: 18
+Ext_InvertCompVSYNC *   3 :SHL: 18
+Ext_HiResMono   *       1 :SHL: 14
+Ext_LCDGrey     *       1 :SHL: 13
+Ext_DACsOn      *       1 :SHL: 12
+Ext_PedsOn      *       7 :SHL: 8
+Ext_PedsShift   *       8
+Ext_ERegShift   *       4
+Ext_ECKOn       *       1 :SHL: 2
+Ext_ERegBits    *       3 :SHL: 0
+Ext_ERegRed     *       0 :SHL: 0
+Ext_ERegGreen   *       1 :SHL: 0
+Ext_ERegBlue    *       2 :SHL: 0
+Ext_ERegExt     *       3 :SHL: 0       ; use this for lowest power
+
+; Bits in Frequency Synthesizer Register
+
+FSyn_VShift     *       8
+FSyn_RShift     *       0
+FSyn_ClearV     *       1 :SHL: 15
+FSyn_ForceLow   *       1 :SHL: 14
+FSyn_ClearR     *       1 :SHL: 7
+FSyn_ForceHigh  *       1 :SHL: 6
+
+FSyn_ResetValue *       FSyn_ClearV :OR: FSyn_ClearR :OR: FSyn_ForceLow :OR: (63 :SHL: FSyn_RShift) :OR: (0 :SHL: FSyn_VShift)           ; value to get PLL working properly
+
+; Bits in Control Register
+
+CR_DualPanel    *       1 :SHL: 13
+CR_Interlace    *       1 :SHL: 12
+CR_FIFOLoadShift *      8
+CR_LBPP0        *       0 :SHL: 5
+CR_LBPP1        *       1 :SHL: 5
+CR_LBPP2        *       2 :SHL: 5
+CR_LBPP3        *       3 :SHL: 5
+CR_LBPP4        *       4 :SHL: 5
+CR_LBPP5        *       6 :SHL: 5 ; spot the gap!
+CR_PixelDivShift *      2
+CR_VCLK         *       0 :SHL: 0
+CR_HCLK         *       1 :SHL: 0
+CR_RCLK         *       2 :SHL: 0
+
+; Bits in Data Control Register
+
+DCR_VRAMOff     *       0 :SHL: 18
+DCR_VRAMDiv1    *       1 :SHL: 18
+DCR_VRAMDiv2    *       2 :SHL: 18
+DCR_VRAMDiv4    *       3 :SHL: 18
+DCR_BusBits     *       3 :SHL: 16
+DCR_Bus31_0     *       1 :SHL: 16
+DCR_Bus63_32    *       2 :SHL: 16
+DCR_Bus63_0     *       3 :SHL: 16
+DCR_HDis        *       1 :SHL: 13
+DCR_Sync        *       1 :SHL: 12
+DCR_HDWRShift   *       0
+
+ |
+
+; Registers
+                                                                                                                                                                                                     
+HorizDisplayStart       *       &8C000000       ; used in mode change code
+HorizCursorStart        *       &98000000       ; used in pointer programming
+
+VertiBorderStart        *       &A8000000       ; First register affected by *TV
+VertiDisplayStart       *       &AC000000
+VertiCursorStart        *       &B8000000
+VertiCursorEnd          *       &BC000000       ; Last register affected by *TV
+
+SoundFrequency          *       &C0000000
+VIDCControl             *       &E0000000
+
+; Bits in control register
+
+CR_Interlace            *       &40             ; 0 - no interlace, 64 - interlace
+CompSync        *       &80     ; Controls sync signal on CS/VS pin
+                                ; 0 - output vertical sync, 128 - composite sync.
+; Other bits
+
+SupBit          *       &1000   ; Supremacy bit in palette
+ ]
+
+PhysCursorStartAdr * CursorSoundPhysRAM
+
+; Reason codes for generalised DAG interface - independent of MEMC type
+
+MEMCDAG_VInit   *       0
+MEMCDAG_VStart  *       1
+MEMCDAG_VEnd    *       2
+MEMCDAG_CInit   *       3
+
+MEMCDAG_MaxReason *     3
+
+ [ ModeSelectors
+
+; OS_ScreenMode reason codes
+
+ScreenModeReason_SelectMode             *       0
+ScreenModeReason_ReturnMode             *       1
+ScreenModeReason_EnumerateModes         *       2
+ScreenModeReason_SelectMonitorType      *       3
+ScreenModeReason_Limit                  *       4
+
+; Mode selector format
+
+                        ^       0
+ModeSelector_Flags      #       4       ; flags word
+ModeSelector_XRes       #       4       ; x-resolution in pixels
+ModeSelector_YRes       #       4       ; y-resolution in pixels
+ModeSelector_PixelDepth #       4       ; pixel depth (=Log2BPP)
+ModeSelector_FrameRate  #       4       ; nominal frame rate (in Hz)
+ModeSelector_ModeVars   #       0       ; start of pairs of (mode var index, value)
+
+ModeSelectorFlags_FormatMask    *       &FF
+ModeSelectorFlags_ValidFormat   *       1
+
+ModeSelector_MaxSize    *       ModeSelector_ModeVars+(NumModeVars * 8)+4
+                                        ; maximum size of a mode selector, with each mode variable overridden
+                                        ; plus terminator on end
+
+ ]
+
+
+
+;
+; Macro Definitions
+; =================
+;
+
+;
+; Macro Sort - Sort two values into increasing order
+;
+        MACRO
+        Sort    $lo, $hi
+        CMP     $hi, $lo
+        EORLT   $lo, $lo, $hi
+        EORLT   $hi, $lo, $hi
+        EORLT   $lo, $lo, $hi
+        MEND
+
+;
+; Macro SortT - Sort two values into increasing order using a temporary reg
+;
+        MACRO
+        SortT   $lo, $hi, $temp
+        SUBS    $temp, $hi, $lo
+        MOVLT   $hi, $lo
+        ADDLT   $lo, $lo, $temp
+        MEND
+
+;
+; Macro CompSwap - Compare and sort a pair of coordinates into
+;                    order of increasing Y
+;                  If Y values equal, sort in order of decreasing X
+;
+        MACRO
+        CompSwap $xl,$yl, $xh,$yh
+        CMP     $yh, $yl
+        EORLT   $yl, $yl, $yh
+        EORLT   $yh, $yl, $yh
+        EORLT   $yl, $yl, $yh
+        CMPEQ   $xl, $xh
+        EORLT   $xl, $xl, $xh
+        EORLT   $xh, $xl, $xh
+        EORLT   $xl, $xl, $xh
+        MEND
+
+;
+; Macro CompSwapT - Compare and sort a pair of coordinates into
+;                    order of increasing Y
+;                   If Y values equal, sort in order of decreasing X
+;                   Uses a temporary register
+;
+        MACRO
+        CompSwapT $xl,$yl, $xh,$yh, $temp
+        SortT   $yl, $yh, $temp
+        CMPEQ   $xl, $xh
+        EORLT   $xl, $xl, $xh
+        EORLT   $xh, $xl, $xh
+        EORLT   $xl, $xl, $xh
+        MEND
+
+;
+; Macro Difference - rc := ABS(ra-rb)
+;
+;                    Test GE/LT for ra>=rb / ra<rb
+;
+        MACRO
+        Difference $rc,$ra,$rb
+        SUBS $rc,$ra,$rb
+        RSBLT $rc,$rc,#0
+        MEND
+
+;
+; Macro Least - Select the smallest value (signed)
+;
+        MACRO
+        Least $rc,$ra,$rb
+        CMP $ra,$rb
+       [ $rc = $ra
+       |
+        MOVLE $rc,$ra
+       ]
+       [ $rc = $rb
+       |
+        MOVGT $rc,$rb
+       ]
+        MEND
+
+;
+; Macro Greatest - Select the largest (signed) value
+;
+        MACRO
+        Greatest $rc,$ra,$rb
+        CMP $ra,$rb
+       [ $rc = $ra
+       |
+        MOVGE $rc,$ra
+       ]
+       [ $rc = $rb
+       |
+        MOVLT $rc,$rb
+       ]
+        MEND
+
+;
+; Macro PackXtnd - pack 2 bytes into 1 word and sign extend
+;
+
+        MACRO
+        PackXtnd $result,$hi,$lo
+        [ $lo = $result
+          ADD $result,$lo,$hi,LSL #8
+          MOV $result,$result,LSL #16
+          MOV $result,$result,ASR #16
+        |
+          MOV $result,$hi,LSL #24
+          ORR $result,$lo,$result,ASR #16
+        ]
+        MEND
+
+        MACRO
+        LoadCoordPair   $x, $y, $basereg, $offset
+        ASSERT  $x < $y
+        [ ($offset) :AND: 3 = 2
+          ADD   $x, $basereg, #($offset)-2
+          LDMIA $x, {$x, $y}                ; (Xh,Xl,??,??) (??,??,Yh,Yl)
+          MOV   $x, $x, ASR #16             ; (Xs,Xs,Xh,Xl)
+          MOV   $y, $y, LSL #16             ;               (Yh,Yl, 0, 0)
+          MOV   $y, $y, ASR #16             ;               (Ys,Ys,Yh,Yl)
+        |
+          [ ($offset) :AND: 3 = 0
+            LDR   $x, [$basereg, #$offset]  ; (Yh,Yl,Xh,Xl)
+          |
+          [ ($offset) :AND: 3 = 1
+            ADD   $x, $basereg, #($offset)-1
+            LDMIA $x, {$x, $y}              ; (Yl,Xh,Xl,??) (??,??,??,Yh)
+            MOV   $x, $x, LSR #8            ; ( 0,Yl,Xh,Xl)
+            ORR   $x, $x, $y, LSL #24       ; (Yh,Yl,Xh,Xl)
+          |
+            ADD   $x, $basereg, #($offset)-3
+            LDMIA $x, {$x, $y}              ; (Xl,??,??,??) (??,Yh,Yl,Xh)
+            MOV   $x, $x, LSR #24           ; ( 0, 0, 0,Xl)
+            ORR   $x, $x, $y, LSL #8        ; (Yh,Yl,Xh,Xl)
+          ]
+          ]
+          MOV   $y, $x, ASR #16             ;               (Ys,Ys,Yh,Yl)
+          MOV   $x, $x, LSL #16             ; (Xh,Xl, 0, 0)
+          MOV   $x, $x, ASR #16             ; (Xs,Xs,Xh,Xl)
+        ]
+        MEND
+
+;
+; Macro SaveRetAdr - Push R14 to our pseudo stack
+;
+        MACRO
+        SaveRetAdr
+        Push    R14
+        MEND
+
+;
+; Macro Return - Pull from stack into PC
+;
+        MACRO
+        Return $cond
+        LDM$cond.FD StkPtr!, {PC}
+        MEND
+
+;
+; Macro SuperMode - Set supervisor mode
+;
+        MACRO
+        SuperMode
+        SWI &16
+        MEND
+
+;
+; Macro UserMode - Return to user mode
+;
+        MACRO
+        UserMode
+        TEQP PC,#0        ; Return to user mode
+        MOV R0,R0         ; Force a NOP so it works
+        MEND
+
+;
+; Macro WINDow - Compare coordinate against graphics window
+;
+;                Test GE/LT for within/outside window
+;
+        MACRO
+        WINDow $rx,$ry, $rl,$rb,$rr,$rt
+; ASSERT ($rl < $rb) AND ($rb < $rr) AND ($rr < $rt)
+        ADD $rt,WsPtr,#GWLCol
+        LDMIA $rt,{$rl,$rb,$rr,$rt}
+        CMP $rx,$rl
+        CMPGE $rr,$rx
+        CMPGE $ry,$rb
+        CMPGE $rt,$ry
+        MEND
+
+;
+; Macro WindowRes - Window a coordinate, giving status word
+;
+;           Result word is as follows:
+;
+;                |      |
+;           1001 | 1000 | 1010
+;                |      |
+;           -----+------+----- GWTRow
+;                |      |
+;           0001 | 0000 | 0010
+;                |      |
+;           -----+------+----- GWBRow
+;                |      |
+;           0101 | 0100 | 0110
+;                |      |
+;
+;              GWLCol GWRCol
+;
+;
+        MACRO
+        WindowRes $result, $rx,$ry, $rl,$rb,$rr,$rt
+; ASSERT ($rl < $rb) AND ($rb < $rr) AND ($rr < $rt)
+        MOV $result,#0
+        ADD $rt,WsPtr,#GWLCol
+        LDMIA $rt,{$rl,$rb,$rr,$rt}
+        CMP $rx,$rl
+        ORRLT $result,$result,#1        ;Set bit 0 if X < window
+        CMP $rr,$rx
+        ORRLT $result,$result,#2        ;Set bit 1 if X > window
+        CMP $ry,$rb
+        ORRLT $result,$result,#4        ;Set bit 2 if Y < window
+        CMP $rt,$ry
+        ORRLT $result,$result,#8        ;Set bit 3 if Y > window
+        MEND
+
+        MACRO
+$lab    EQUB    $var
+        ASSERT  $var >= &00
+        ASSERT  $var <= &FF
+$lab    =       $var
+        MEND
+
+        MACRO
+        OrrEor $d,$s, $or,$eor
+        ORR $d,$s,$or
+        EOR $d,$d,$eor
+        MEND
+
+
+        MACRO                           ;Scr:=ScrOR(oraANDmsk)EOR(eorANDmsk)
+        OrrEorMASK $scr,$msk, $ora,$eor, $tmp
+        AND $tmp,$msk,$ora
+        ORR $scr,$scr,$tmp
+        AND $tmp,$msk,$eor
+        EOR $scr,$scr,$tmp
+        MEND
+
+
+        MACRO
+        ORoreorEORoreor  $d,$s, $oo,$eo,$oe,$ee, $tmp
+        OrrEor $tmp,$s, $oo,$eo
+        ORR $d,$d,$tmp
+        OrrEor $tmp,$s, $oe,$ee
+        EOR $d,$d,$tmp
+        MEND
+
+
+        MACRO
+        ORoreorEORoreorMASK  $d,$s,$m, $oo,$eo,$oe,$ee, $tmp
+        OrrEor $tmp,$s, $oo,$eo
+        AND $tmp,$tmp,$m
+        ORR $d,$d,$tmp
+        OrrEor $tmp,$s, $oe,$ee
+        AND $tmp,$tmp,$m
+        EOR $d,$d,$tmp
+        MEND
+
+
+        MACRO
+        ShiftR $d,$e, $r,$rcomp
+        MOV $d,$d,LSR $r
+        ORR $d,$d,$e,LSL $rcomp
+        MEND
+
+        MACRO
+        ShiftL $d,$e, $r,$rcomp
+        MOV $e,$e,LSL $rcomp
+        ORR $e,$e,$d,LSR $r
+        MEND
+
+
+        MACRO
+        BitLOffset $b,$x, $xshftfactor,$npix,$log2bpc
+        AND $b,$x,$npix
+        MOV $b,$b,LSL $log2bpc
+        MEND
+
+
+        MACRO
+        BitROffset $b,$x, $xshftfactor,$npix,$log2bpc
+        AND $b,$x,$npix
+        ADD $b,$b,#1
+        MOV $b,$b,LSL $log2bpc
+        SUB $b,$b,#1
+        MEND
+
+
+        MACRO
+        WordOffset $w,$x, $xshftfactor,$npix,$log2bpc
+        MOV $w,$x,ASR $xshftfactor
+        MEND
+
+
+        MACRO
+        OffsetWordAndBit $o,$b,$x,$tmp
+        LDR $tmp,[WsPtr,#XShftFactor]
+        MOV $o,$x,ASR $tmp                      ;Word offset into scanline
+        LDR $tmp,[WsPtr,#NPix]
+        AND $b,$x,$tmp                          ;Pixel offset into word
+        LDR $tmp,[WsPtr,#Log2BPC]
+        MOV $b,$b,LSL $tmp                      ;Bit offset into word
+        MEND
+
+
+        MACRO
+$label  ErrorMsg $num,$string
+$label  DCD $num
+        DCB "$string", 0
+        ALIGN
+        MEND
+
+;
+; Macro when given a register will return the state to indicate
+; if we are in a graphics mode.  Originally lots of code used to simply
+; load NPix and look for a null parameter (fair enough in 1-8 bit per pixel)
+; but now we look at the mode flags, the choice of a new generation!
+;
+        MACRO
+$label  GraphicsMode $scrap
+$label  LDR     $scrap, [WsPtr, #ModeFlags]
+        TST     $scrap, #Flag_NonGraphic                ;NE then non-graphic mode!
+        MEND
+
+        END
diff --git a/s/vdu/vdudriver b/s/vdu/vdudriver
new file mode 100644
index 0000000000000000000000000000000000000000..c5a31f057ef467da44c80855bf831026044f7ba6
--- /dev/null
+++ b/s/vdu/vdudriver
@@ -0,0 +1,3092 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduDriver
+;
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Vdu driver code - Vdu queue, mode, default windows etc.
+;
+; Author R C Manby
+; Date   5.9.86
+;
+        GBLL NewStyleEcfs
+NewStyleEcfs   SETL  1=1
+
+        GBLL    DoVdu23_0_12
+DoVdu23_0_12   SETL  {FALSE}
+
+        GBLL    BleedinDaveBell
+BleedinDaveBell SETL {TRUE}
+
+        GET s.vdu.VduDecl
+        GET s.vdu.VduGrafDec
+
+        MACRO
+        HostVdu
+        Push    "R0,R14"
+        LDR     R0, [R0, -R0]
+        TEQ     R0, #0
+        ADREQ   R0, %FT01
+        SWIEQ   OS_CLI
+        Pull    "R0,R14"
+        B       %FT02
+01
+        =       "HOSTVDU", 0
+        ALIGN
+02
+        MEND
+
+        MACRO
+        Print   $string
+        Push    "R0,R14"
+        LDR     R0, [R0, -R0]
+        TEQ     R0, #0
+        BNE     %FT01
+        SWI     OS_WriteS
+        =       "$string", 0
+        ALIGN
+        SWI     OS_ReadC
+        Pull    "R0,R14",CS
+        SWICS   OS_BreakPt
+        SWI     OS_NewLine
+01
+        Pull    "R0,R14"
+        MEND
+
+        MACRO
+        RMVT    $var, $BW
+        [ "$BW"="B"
+        =       (wk$var-wkstart)*2
+        |
+        [ "$BW"="W"
+        =       (wk$var-wkstart)*2 +1
+        |
+        .... Invalid option on RMVT ....
+        ]
+        ]
+        MEND
+
+        MACRO
+        RVVT    $var
+        ASSERT  ($var >= 0) :LAND: ($var < &400) :LAND: (($var :AND: 3)=0)
+        =       $var :SHR: 2
+        MEND
+
+        MACRO
+        IssueService
+        BL      Issue_Service
+        MEND
+
+        MACRO
+        MALIGN  $length, $phase
+        LCLA    temp
+        LCLS    string
+temp    SETA    .-ArthurVduDriver
+        [ "$phase"=""
+string  SETS    "ALIGN $length"
+        |
+string  SETS    "ALIGN $length, $phase"
+        ]
+        $string
+temp    SETA    .-ArthurVduDriver-temp
+        [ temp=0
+        !       0, string :CC: " fitted exactly"
+        |
+        !       0, string :CC: " wasted " :CC: :STR: temp
+        ]
+        MEND
+
+ [ ModeSelectors
+
+; Macro to load up video bandwidth and video memory size
+
+        MACRO
+        GetBandwidthAndSize  $bw, $size
+  [ MEMC_Type = "IOMD"
+        MOV     $size, #0
+        LDR     $bw, [$size, #VideoBandwidth]   ; load bandwidth
+        LDR     $size, [$size, #VideoSize]      ; and total amount of video RAM
+  |
+        LDR     $bw, =38400000                  ; if no ARM600 then must be MEMC based
+        LDR     $size, =480*1024
+  ]
+        MEND
+ ]
+
+; *****************************************************************************
+
+; Vdu status bits
+
+Vdu2ModeBitPosn *       0
+Vdu2Mode        *       1 :SHL: Vdu2ModeBitPosn
+Windowing       *       1 :SHL: 3
+Shadowing       *       1 :SHL: 4
+
+; *****************************************************************************
+
+ArthurVduDriver
+
+; *****************************************************************************
+;
+; VduInit - Once only initialisation of Vdu drivers eg after Break
+; =======
+;
+VduInit ROUT
+        Push    R14
+        MOV     R0, #0
+        STRB    R0, [R0, #OsbyteVars + :INDEX: VDUqueueItems] ;purge queue
+        STRB    R0, [WsPtr, #ScreenBlankFlag]   ; not blanked
+        STR     R0, [WsPtr, #CursorCounter]
+        STR     R0, [WsPtr, #CursorDesiredState]
+        STR     R0, [WsPtr, #VduStatus]
+        STR     R0, [WsPtr, #PointerHeights]    ; zero all 4 heights
+        STRB    R0, [WsPtr, #PointerShapeNumber] ; make sure pointer off
+        STR     R0, [WsPtr, #CursorStack]       ; 0 bits => on
+        STR     R0, [WsPtr, #VduSaveAreaPtr]    ; indicate no save area yet
+        STR     R0, [WsPtr, #ClipBoxEnable]     ; no clip box calculating
+
+        LDR     R0, =RangeC+SpriteReason_SwitchOutputToSprite
+        STR     R0, [WsPtr, #SpriteMaskSelect]
+
+        MOV     R0, #InitialCursorFlags         ; TMD 25/9/86
+        STR     R0, [WsPtr, #CursorFlags]
+
+        ADRL    R0, NUL                 ; point to MOV PC,R14
+        STR     R0, [WsPtr, #WrchNbit]  ; just in case ...
+
+        ADRL    R0, ExportedHLine
+        STR     R0, [WsPtr, #HLineAddr]
+        ADD     R0, WsPtr, #FgEcfOraEor
+        STR     R0, [WsPtr, #GcolOraEorAddr]
+
+        MOV     R0, #maxmode
+        STR     R0, [WsPtr, #MaxMode]   ; constant now
+
+
+        LDROSB  R0, LastBREAK           ; is it a hard reset ?
+        TEQ     R0, #0
+        BEQ     %FT10                   ; [no, don't reset font]
+
+ [ VIDC_Type = "VIDC20"
+
+; allocate buffer for Tim's whizzy text expansion.  This now lives
+; in the system heap, unless the claim request fails.  If the request
+; fails then the pointer points at the original buffer in the Vdu
+; driver workspace.
+
+; This allows the user to access depths upto 16 bit per pixel before
+; strings things things become afoot at the Circle K.
+
+        LDR     R3, =TextExpandArea_Size
+        BL      ClaimSysHeapNode        ; allocate buffer for heap data
+        ADDVS   R2, WsPtr, #TextExpand
+        STR     R2, [WsPtr, #TextExpandArea]
+
+  [ ModeSelectors
+        LDR     R3, =ModeSelector_MaxSize+4     ; get block for two overlapping copies
+        BL      ClaimSysHeapNode                ; of mode selector, 1 word apart
+        STR     R2, [WsPtr, #KernelModeSelector]
+  ]
+
+ ]
+
+; now reset soft font from hard font
+
+        MOV     R1, #1                  ; start at character 1*32
+        STRB    R1, [WsPtr, #ScreenMemoryClaimed]
+                                        ; can't claim memory till a mode change
+        MOV     R2, #7                  ; copy 7 pages
+        BL      DoResetFont
+
+ [ VIDC_Type = "VIDC20"
+  [ GammaCorrection
+        MOV     r3, #(256+1+3)*4*4+3*256 ; logical and physical copies of both flash states
+                                        ; plus 3 lookup tables for r,g,b
+  |
+        MOV     r3, #(256+1+3)*4*2      ; soft copy of palette (2 flash states)
+  ]
+        BL      ClaimSysHeapNode        ; this had better succeed!
+        STR     r2, [WsPtr, #FirPalAddr]
+
+        ADD     r3, r2, #260*4
+        STR     r3, [WsPtr, #SecPalAddr]
+
+; now initialise entries for pointer palette so they will actually program pointer palettte
+; because VDU20 doesn't change pointer palette
+
+        MOV     r3, #&50000000
+        MOV     r4, #&60000000
+        MOV     r5, #&70000000
+
+        ADD     r2, r2, #260*4          ; store in 1st copy of logical
+        STMDB   r2, {r3-r5}             ; (last 3 entries)
+
+        ADD     r2, r2, #260*4
+        STMDB   r2, {r3-r5}             ; store in 2nd copy of logical
+
+ [ GammaCorrection
+        ADD     r2, r2, #260*4
+        STMDB   r2, {r3-r5}             ; store in 1st copy of physical
+
+        ADD     r2, r2, #260*4
+        STMDB   r2, {r3-r5}             ; store in 2nd copy of physical
+
+; r2 now points off end of all 4 copies, ie start of rgb tables
+; initialise red, green and blue transfer function tables to 1-1 mapping
+
+        MOV     r0, #0
+05
+        STRB    r0, [r2, #&200]                 ; store in blue table
+        STRB    r0, [r2, #&100]                 ; store in green table
+        STRB    r0, [r2], #1                    ; store in red table, and advance
+        ADD     r0, r0, #1
+        CMP     r0, #256
+        BCC     %BT05
+  ]
+ ]
+10
+        BL      SpriteInit
+
+        LDR     R0, [WsPtr, #TotalScreenSize]
+        RSB     R0, R0, #ScreenEndAdr
+        STR     R0, [WsPtr, #DisplayStart]
+        STR     R0, [WsPtr, #DisplayScreenStart]
+        STR     R0, [WsPtr, #ScreenStart]
+        STR     R0, [WsPtr, #CursorAddr]
+        STR     R0, [WsPtr, #InputCursorAddr]
+
+        Pull    PC
+
+        LTORG
+
+; *****************************************************************************
+;
+;       InitialiseMode - Select mode number given by ModeNo variable
+;
+;       Called by MOS once before initialisation, and then afterwards
+;       before printing "RISC OS ..."
+;
+; in:   -
+; out:  All registers may be corrupted (except R13_svc !)
+;
+
+InitialiseMode ENTRY
+ [ SoftResets
+        MOV     r0, #&FD                ; read last reset type
+        MOV     r1, #0
+        MOV     r2, #&FF
+        SWI     XOS_Byte
+        CMP     r1, #SoftReset
+        LDREQ   r0, =VduDriverWorkSpace+ModeNo
+        LDREQ   r0, [r0]                ; use previous mode if a soft reset
+        MOVNE   r0, #1                  ; otherwise read configured mode
+        SWINE   XOS_ReadSysInfo
+ |
+        MOV     r0, #1                  ; no need to check for soft reset,
+        SWI     XOS_ReadSysInfo         ; always use configured value
+ ]
+ [ ModeSelectors
+        MOV     r1, r0
+        MOV     r0, #ScreenModeReason_SelectMode
+        SWI     XOS_ScreenMode
+        EXIT    VC
+ |
+        SWI     XOS_WriteI+22
+        SWIVC   XOS_WriteC
+        EXIT    VC
+ ]
+
+        MOV     r0, #114                ; failed, so get rid of any shadow
+        MOV     r1, #1
+        SWI     XOS_Byte
+        SWI     XOS_WriteI+22
+        SWIVC   XOS_WriteI+0            ; and if we can't get mode 0, we're really fooked!!!
+        EXIT
+
+;
+;------------------------------------------------------------------------------
+;
+; Vdu -  Main VDU driver entry point
+; ===    Queue up bytes and dispatch via JVec
+;
+; in:   R0 = character to be printed
+;
+; out:  C=1 <=> send character to printer if enabled
+;
+
+Vdu     ROUT
+        MOV     R11, #0                 ; NB used later in Vdu07 as well
+        LDRB    R10, [R11, #OsbyteVars + :INDEX: VDUqueueItems]
+        MOVS    R9, R10, LSL #24        ; move up to top byte (for sign extend)
+        BEQ     Vdu07                   ; not queueing, then start Vdu sequence
+
+; *****Comment made by DJS: Changing this to fall through if not queueing
+; and branch if queueing might be a good idea - it would speed up all
+; printable characters and simple cursor movements.
+
+        LDR     R8, [WsPtr, #QOffset]
+        STRB    R0, [R8, R9, ASR #24]   ; add byte to queue
+
+        ADD     R10, R10, #1            ; move on pointer
+        STRB    R10, [R11, #OsbyteVars + :INDEX: VDUqueueItems]
+
+        CMP     R10, #&100              ; finished sequence ?
+        MOVCC   PC, R14                 ; no, then return (no printing)
+
+        Push    "R0, R14"
+        BL      PreWrchCursor           ; exits with R6 = CursorFlags
+        Pull    "R0"
+05
+        MOV     R14, PC
+        ADD     R14, R14, #(VduPrintExit-%BT05-8) ; push address of SEC exit
+                                                  ; (with flags)
+        Push    R14
+        BL      Vdu05
+        BL      PostWrchCursor
+        CLC                             ; also clears V
+        Pull    "R14,PC"
+
+VduPrintExit                             ; exit used if print required
+        BL      PostWrchCursor
+        SEC                             ; also clears V
+        Pull    PC
+
+; *****************************************************************************
+
+Vdu05   ROUT
+        TST     R6, #VduDisabled        ; VDU enabled ?
+        LDREQ   PC, [WsPtr, #JVec]      ; yes, then do it (no printing)
+
+; Queue sequence has just completed, but VDU is disabled
+; Only interesting case is VDU 1 (SOH), which must still print
+; the character if the printer is enabled
+
+        LDR     R10, [WsPtr, #JVec]
+        ADR     R11, SOH
+        TEQ     R10, R11                ; JVec -> SOH ?
+        MOVNE   PC, R14                 ; no, then return (no printing)
+SOH
+        LDR     R1, [WsPtr, #VduStatus]  ; in VDU 2 mode ?
+        TST     R1, #Vdu2Mode
+        MOVEQ   PC, R14                 ; no, then return
+
+        Push    R14
+        BL      MOSDoPrint
+        Pull    PC, VC                  ; good exit, so return
+
+        Pull    R14                     ; bad exit, return with error
+        B       VduBadExit
+
+; *****************************************************************************
+
+Vdu07   ROUT
+        AND     R0, R0, #&FF            ; Only look at bottom byte!
+
+        CMP     R0,#127                 ;If DELETE, change R0 to 32 and
+        MOVEQ   R0,#32                  ;  drop through to control char code
+        CMPNE   R0,#31                  ;Otherwise, branch to printable char
+        BHI     Vdu20                   ;  code if not a control char
+
+; *****Further desirable change: Make printable characters fall through,
+; control characters take the branch - speeding up printable characters is
+; important!
+
+        ADR     R10, VduJTb             ; Address of beginning of jump table
+        LDR     R9, [R10, R0, LSL #2]   ; Get routine address from VduJTB
+        STR     R9, [WsPtr, #JVec]      ; save address
+
+        ADD     R10, R10, #(VduQTb-VduJTb)
+
+        LDRB    R9, [R10, R0]           ; Pick up QQ + length of queue
+        RSBS    R10, R9, #QQ            ; -(length of queue) & test if none
+
+        STRNEB  R10, [R11, #OsbyteVars + :INDEX: VDUqueueItems]
+                                        ; yes, then set up Osbyte variable
+        ADDNE   R9, R9, WsPtr
+        STRNE   R9, [WsPtr, #QOffset]   ; and QOffset
+        MOVNE   PC, R14
+
+        Push    "R0,R14"
+        BL      PreWrchCursor           ; exits with R6 = CursorFlags
+        Pull    "R0"
+10
+        MOV     R14, PC
+        ADD     R14, R14, #(VduPrintExit-%BT10-8) ; push address of SEC exit
+                                                  ; (with flags)
+        Push    R14
+        BL      Vdu10
+        BL      PostWrchCursor
+        CLC                             ; also clears V
+        Pull    "R14,PC"
+
+;
+; This is the only byte of a single byte Vdu sequence
+;
+;       R6 = CursorFlags
+;       R11 = 0 (used to index VDUqueueItems)
+;
+
+Vdu10   ROUT
+        TST     R6, #CursorsSplit       ; are we cursor editing ?
+        BNE     Vdu15
+Vdu10Continue
+
+; TMD 31/7/87; bug fixed here - chars 8 to 13 should still go to printer if
+; enabled, even if VDU is disabled
+
+        CMP     R0, #8                  ; is char in range 8-13 ?
+        RSBCSS  R1, R0, #13             ; if so then we want to print it
+        LDRCS   R1, [WsPtr, #VduStatus]
+        MOVCSS  R1, R1, LSR #(Vdu2ModeBitPosn +1) ; providing printer enabled
+        Pull    R14, CS                 ; so pull old R14
+
+        TST     R6, #VduDisabled        ; are we disabled
+        LDREQ   PC, [WsPtr, #JVec]      ; enabled, so go get em floyd !
+
+        TEQ     R0, #6                  ; disabled, so is it ACK (enable VDU)
+        BICEQ   R6, R6, #VduDisabled
+        STREQ   R6, [WsPtr, #CursorFlags]
+
+NUL ;Does nothing
+ESC ;Does nothing
+        MOV     PC, R14                 ; return anyway
+
+; *****************************************************************************
+
+Vdu15
+        TEQ     R0, #13                 ; and is it a carriage return ?
+        BNE     Vdu10Continue
+
+        Push    "R0, R14"
+        BIC     R6, R6, #CursorsSplit   ; then stop cursor editing
+        STR     R6, [WsPtr, #CursorFlags]
+        MOV     R1, #1                  ; restore old Reg10Copy
+        BL      CursorOnOff
+        Pull    "R0, R14"
+        B       Vdu10Continue
+
+; *****************************************************************************
+
+Vdu20   ROUT
+        Push    "R0,R14"
+        BL      PreWrchCursor           ; exits with R6 = CursorFlags
+        Pull    "R0"
+05
+        MOV     R14, PC
+        ADD     R14, R14, #(VduPrintExit-%BT05-8) ; push address of SEC exit
+                                                  ; (with flags)
+        Push    R14
+        BL      TimWrch
+        BL      PostWrchCursor
+        CLC                             ; also clears V
+        Pull    "R14,PC"
+
+; *****************************************************************************
+
+VduJTb                  ; Table of addresses
+ & NUL                          ; Does nothing
+ & SOH                          ; Next char to printer only
+ & STX                          ; Enable printer
+ & ETX                          ; Disable printer
+ & EOT                          ; Write text at text cursor
+ & ENQ                          ; Write text at graphics cursor
+ & NUL                          ; Enable VDU drivers
+ & BEL                          ; Beep
+ & BS                           ; Cursor left
+ & HT                           ; Cursor right
+ & VduLF                        ; Cursor down
+ & VT                           ; Cursor up
+ & FF                           ; Clear text area (CLS)
+ & VduCR                        ; Carriage return
+ & SO                           ; Page mode on
+ & SI                           ; Page mode off
+ & DLE                          ; Clear graphics area (CLG)
+ & DC1                          ; Define text colour (COLOUR)
+ & DC2                          ; Define graphics colour and action (GCOL)
+ & DC3                          ; Define logical colour
+ & VDU20                        ; Restore default logical colours
+ & NAK                          ; Disable VDU drivers
+ & SYN                          ; Select screen mode (MODE)
+ & ETB                          ; Reprogram display character (VDU23,...)
+ & CAN                          ; Define graphics window
+ & EM                           ; (PLOT k,x,x,y,y)
+ & DefaultWindows               ; Restore default windows
+ & ESC                          ; Does nothing
+ & FS                           ; Define text window
+ & GS                           ; Define graphics origin
+ & RS                           ; Home cursor to "top left"
+ & US                           ; Move text cursor (TAB x,y)
+ & Delete                       ; Delete character (127)
+
+        ASSERT  QQ+9 < 256
+
+VduQTb          ; QQ + length of queue for each of the above
+        DCB     QQ+0, QQ+1, QQ+0, QQ+0, QQ+0, QQ+0, QQ+0, QQ+0
+        DCB     QQ+0, QQ+0, QQ+0, QQ+0, QQ+0, QQ+0, QQ+0, QQ+0
+        DCB     QQ+0, QQ+1, QQ+2, QQ+5, QQ+0, QQ+0, QQ+1, QQ+9
+        DCB     QQ+8, QQ+5, QQ+0, QQ+0, QQ+4, QQ+4, QQ+0, QQ+2
+        DCB     QQ+0 ; (delete)
+        ALIGN
+
+; *****************************************************************************
+
+WrchNbitTab
+        &       Wrch1bit-WrchNbitTab
+        &       Wrch2bit-WrchNbitTab
+        &       Wrch4bit-WrchNbitTab
+        &       Wrch8bit-WrchNbitTab
+        &       Wrch16bit-WrchNbitTab
+        &       Wrch32bit-WrchNbitTab
+WrchNbitDoubleTab
+        &       Wrch1bitDouble-WrchNbitDoubleTab
+
+CursorNbitTab
+        &       Cursor1bit-CursorNbitTab
+        &       Cursor2bit-CursorNbitTab
+        &       Cursor4bit-CursorNbitTab
+        &       Cursor8bit-CursorNbitTab
+        &       Cursor16bit-CursorNbitTab
+        &       Cursor32bit-CursorNbitTab
+
+; *****************************************************************************
+;
+;       SYN - Perform MODE change
+;
+;       External routine
+;
+; in:   Vdu queue contains mode number
+;
+
+SYN     ROUT                            ; Select screen mode (MODE)
+        Push    lr
+        LDRB    R2, [WsPtr, #QQ]        ; Read mode number from queue
+        BL      ModeChangeSub
+        Pull    lr
+        MOVVC   pc, lr                  ; if no error, exit to Vdu
+                                        ; (which calls PostWrchCursor)
+; else drop thru into ...
+
+VduBadExit                              ; jumped to if an error in VDU code
+        Push    R0                      ; save error pointer
+        BL      PostWrchCursor
+        SETV
+        Pull    "R0, R14,PC"
+
+ModeChangeSub ROUT
+        Push    lr
+        MOV     R1, #Service_PreModeChange
+        IssueService
+        TEQ     R1, #0                  ; was service claimed ?
+        BNE     %FT03                   ; no, so continue
+
+        CMP     R0, #0                  ; service claimed; generate error ?
+        Pull    PC, EQ                  ; no, just exit (V=0 from CMP)
+        B       %FT07                   ; yes, then generate error
+03
+        MOV     R0, R2                  ; put mode (possibly changed) in R0
+        MOV     R2, R0, LSR #7          ; R2 = 0/1 if bit 7 of mode clear/set
+ [ ModeSelectors
+        CMP     r2, #2                  ; if mode number >= 256 then mode selector
+        MOVCS   r2, #0                  ; so no shadow
+ ]
+        LDROSB  R1, Shadow
+        TEQ     R1, #0                  ; if shadow 0 then force shadow mode
+        MOVEQ   R2, #1
+
+        BL      FindOKMode              ; out: R1 = mode we are going to use
+        BVS     %FT07
+
+        TEQ     R0, R1                  ; if substitute mode
+        MOVNE   R2, #0                  ; then don't use shadow
+
+ [ ModeSelectors
+        CMP     r1, #&100
+        BICCC   r10, r1, #&80
+        MOVCS   r10, r1
+ |
+        BIC     R10, R1, #&80
+ ]
+        MOV     R11, R10
+        BL      PushModeInfo
+        BVS     %FT07                   ; [probably duff mode selector]
+
+        LDR     R11, [R13, #wkScreenSize] ; get screen size for this mode
+        LDR     R9, [WsPtr, #TotalScreenSize] ; maximum allowed amount
+
+        Push    R1                      ; save proper mode
+        RSBS    R1, R9, R11, LSL R2     ; extra amount we need
+        BLS     %FT08                   ; enough memory, so skip
+
+; try to extend the amount of screen memory
+
+        MOV     R0, #2                  ; expand screen memory
+        SWI     XOS_ChangeDynamicArea
+        BVC     %FT08
+
+        ADD     R13, R13, #PushedInfoSize + 1*4 ; junk stacked info + mode no.
+        ADR     R0, ErrorBlock_BadMODE
+      [ International
+        BL      TranslateError
+      ]
+07
+        SETV                            ; indicate error
+        Pull    PC
+
+; valid mode and enough memory
+
+08
+        Pull    R0                      ; restore mode we are using
+ [ ModeSelectors
+        CMP     r0, #&100               ; if not mode selector
+        BICCC   r0, r0, #&80            ; then knock off shadow bit
+        BCC     %FT12
+
+; it's a mode selector, so copy it to our static mode selector block
+
+        LDR     r1, [WsPtr, #KernelModeSelector] ; point at block
+
+        SUBS    r3, r0, r1              ; if r0 -> 1st mode block position
+        TEQNE   r3, #4                  ; or r0 -> 2nd mode block position
+        MOVEQ   r1, r0                  ; then use it in place
+        BEQ     %FT09
+
+        LDR     r3, [WsPtr, #DisplayModeNo] ; else check if current mode is a mode selector
+        SUB     r3, r3, r1              ; r3 = offset from start of block
+        CMP     r3, #8                  ; if 0 or 4
+        EORCC   r3, r3, #4              ; then make 4 or 0 (ie toggle between them)
+        ADDCC   r1, r1, r3              ; and add on base
+
+        ASSERT  (ModeSelector_ModeVars+4) :AND: 7 = 0
+09
+        MOV     r3, #0
+10
+        LDR     r6, [r0, r3]            ; copy 1st word - after fixed bit this will be previous var value
+        STR     r6, [r1, r3]
+        ADD     r3, r3, #4
+        LDR     r6, [r0, r3]            ; copy 2nd word - after fixed bit this will be next var index
+        STR     r6, [r1, r3]
+        ADD     r3, r3, #4
+        CMP     r3, #ModeSelector_ModeVars + 4  ; only exit if we've done the essential bit
+        CMPCS   r6, #-1                 ; AND we've had a -1 as the var index (NOT as the value)
+        CMPCC   r3, #ModeSelector_MaxSize ; OR we've gone off the end of our block
+        BCC     %BT10                   ; [we haven't, so loop]
+
+        CMP     r3, #ModeSelector_MaxSize       ; if we did go off the end
+        MOVCS   r6, #-1
+        STRCS   r6, [r1, #ModeSelector_MaxSize-4] ; then terminate it properly
+
+        MOV     r0, r1                  ; point at static block
+12
+ |
+        BIC     R0, R0, #&80            ; knock out shadow bit
+ ]
+        STR     R0, [WsPtr, #DisplayModeNo] ; store the new display mode
+
+; now issue Service_ModeChanging
+
+        MOV     R1, #Service_ModeChanging
+        BL      IssueModeService
+
+; R13 -> mode variables
+
+        LDR     R3, [R13, #wkScreenSize]
+        STR     R3, [WsPtr, #ScreenSize] ; store screensize BEFORE calling
+                                        ; ConvertBankToAddress (was a bug!)
+
+        TEQ     R2, #0                  ; Shadowing or not ?
+        LDR     R3, [WsPtr, #VduStatus]
+        BICEQ   R3, R3, #Shadowing
+        ORRNE   R3, R3, #Shadowing
+        STR     R3, [WsPtr, #VduStatus]
+
+        STRB    R2, [WsPtr, #ScreenMemoryClaimed] ; only allow ADFS to claim
+                                        ; if non-shadow (simplifies things!)
+
+        BL      ConvertBankToAddress    ; R3 := default start for this bank
+        STR     R3, [WsPtr, #DriverBankAddr]
+        STR     R3, [WsPtr, #DisplayBankAddr]
+
+        MOV     R6, #0
+        STRB    R6, [R6, #OsbyteVars + :INDEX:MemDriver]  ; indicate default
+        STRB    R6, [R6, #OsbyteVars + :INDEX:MemDisplay] ; for both of these
+
+        LDR     R6, [R13, #wkModeFlags]
+        STR     R6, [WsPtr, #DisplayModeFlags]
+
+        MOV     R2, #wkend-wkdispstart  ; number of bytes to do
+        ADD     R1, R13, #wkdispstart
+
+        ADD     R4, WsPtr, #PalIndex    ; first display mode variable
+15
+        LDR     R3, [R1], #4            ; copy variables
+        STR     R3, [R4], #4
+        SUBS    R2, R2, #4              ; loop until all done
+        BNE     %BT15
+
+; now set up other mode variables by calling SwitchOutput
+
+        ADD     R3, WsPtr, #VduSaveArea+InitFlag
+        STR     R2, [R3]                ; indicate uninitialised (R2=0)
+        TST     R6, #Flag_Teletext
+        MOVNE   R3, #0                  ; if teletext, then no save area
+        MOVEQ   R3, #1                  ; else MOS's save area
+        MOV     R1, #0                  ; just in case
+        MOV     R0, #SpriteReason_SwitchOutputToSprite
+        SWI     XOS_SpriteOp
+
+; now create other variables from simple ones
+
+        LDR     R3, [WsPtr, #NColour]
+        STR     R3, [WsPtr, #DisplayNColour]
+
+        ASSERT  YWindLimit = XWindLimit +4
+        ASSERT  DisplayYWindLimit = DisplayXWindLimit +4
+        ADD     R0, WsPtr, #XWindLimit
+        LDMIA   R0, {R3, R4}
+        ADD     R0, WsPtr, #DisplayXWindLimit
+        STMIA   R0, {R3, R4}
+
+        ASSERT  YEigFactor = XEigFactor +4
+        ASSERT  DisplayYEigFactor = DisplayXEigFactor +4
+        ADD     R0, WsPtr, #XEigFactor
+        LDMIA   R0, {R3, R4}
+        ADD     R0, WsPtr, #DisplayXEigFactor
+        STMIA   R0, {R3, R4}
+
+        ASSERT  Log2BPP = Log2BPC +4
+        ADD     R0, WsPtr, #Log2BPC
+        LDMIA   R0, {R0, R1}                    ; R0 = Log2BPC; R1 = Log2BPP
+        SUB     R3, R3, R0                      ; adjust XEig for double pixels
+        ADD     R3, R3, R1
+        STR     R3, [WsPtr, #PointerXEigFactor]
+
+; finished doing other variables
+
+ [ VIDC_Type = "VIDC20"
+        LDROSB  R2, TVInterlace
+        TST     R2, #1
+        MOVNE   R4, #0
+        MOVEQ   R4, #CR_Interlace
+ |
+        BL      ReadSyncType            ; out: r4 = configured sync (0 or 1) and NE if 1
+        MOVNE   R4, #CompSync
+
+        LDROSB  R2, TVInterlace
+        TST     R2, #1
+        ORREQ   R4, R4, #CR_Interlace   ; 0 or &40
+ ]
+
+        LDROSB  R5, TVVertical
+        MOV     R5, R5, LSL #24         ; sign extend to 32 bits
+ [ VIDC_Type = "VIDC20"
+        MOV     R5, R5, ASR #24-3       ; and multiply by 8
+ |
+        MOV     R5, R5, ASR #24-3-14    ; and multiply by 8*(2^14)
+ ]
+        LDR     R1, [WsPtr, #ModeFlags]
+        TST     R1, #Flag_GapMode       ; gap mode ?
+        ADDNE   R5, R5, R5, ASR #2      ; add on 2 rows if so
+        TST     R1, #Flag_DoubleVertical ; if double vertical
+        ADDNE   R5, R5, R5              ; then double it
+
+        ADD     R0, R13, #wkwordsize    ; R0 -> table of VIDC parms
+        MOV     R7, R0                  ; keep copy for if we go wrong
+
+ [ VIDC_Type = "VIDC20"
+        LDR     R1, [R0, #(PseudoRegister_DPMSState:SHR:22)-&80*4] ; get DPMS state if specified
+        CMP     R1, #-1                 ; if not specified
+        MOVEQ   R1, #0                  ; then use zero
+        ANDNE   R1, R1, #3              ; else only use bits 0 and 1
+        STRB    R1, [WsPtr, #ScreenBlankDPMSState]
+
+        MOV     R1, #0                  ; always select 24MHz clock to feed into VIDC, and normal syncs
+ |
+; now set up the VIDC clock latch, before we program any VIDC registers
+; and also set up the sync polarity while we're at it
+
+        LDR     R1, [R0, #(&E0-&80)]    ; get CR with clock+sync bits
+        EOR     R1, R1, R4              ; adjust vertical/composite flag
+        TST     R1, #CompSync           ; if doing composite sync
+        BICNE   R1, R1, #SyncControlMask ; then ensure normal syncs
+
+        AND     R1, R1, #ClockControlMask :OR: SyncControlMask
+
+        ASSERT  SyncControlShift = ClockControlShift+2
+
+        MOV     R1, R1, LSR #ClockControlShift
+ ]
+ [ IO_Type <> "IOMD"
+        LDR     R3, =VIDCClockSelect
+        STRB    R1, [R3]
+ ]
+
+ [ {FALSE}                              ; TMD 02-Sep-92: This bit removed - no production h/w has this latch
+        BL      ReadMonitorType         ; now mute sound pin if not monitortype 0 (sound pin = VGA test pin)
+        TEQ     R3, #0                  ; if monitortype 0 then program 0
+        MOVNE   R3, #1                  ; else program 1
+        LDR     R1, =IOEB_SoundSuppress
+        STRB    R3, [R1]                ; OK to do this on old machines
+ ]
+
+        MOV     R3, #VIDC               ; point to VidC
+18
+ [ VIDC_Type = "VIDC20"
+        MOV     R1, #124*4              ; number of bytes to do (register &FC is the pseudo one to tell
+                                        ; the OS the real VIDC clock rate)
+ |
+        MOV     R1, #31*4               ; number of bytes to do (register &FC is the pseudo one to tell
+                                        ; the OS the real VIDC clock rate)
+ ]
+20
+        LDR     R2, [R0], #4            ; Get data from table
+
+        CMP     R2, #-1                 ; unprogrammed register ?
+        BEQ     %FT80                   ; then skip
+
+        AND     R6, R2, #&FF000000
+
+        TEQ     R6, #HorizDisplayStart
+        STREQ   R2, [WsPtr, #CursorFudgeFactor] ; save for later !
+
+25
+        CMP     R6, #VertiBorderStart
+        RSBCSS  R14, R6, #VertiCursorEnd
+        BCC     %FT40                   ; not a vertical register
+
+; programming one of the registers affected by *TV
+
+        SUB     R8, R2, R5              ; subtract offset
+        TEQ     R6, #VertiDisplayStart  ; test for display start
+        BICEQ   R14, R8, #&FF000000     ; get rid of register bits
+        STREQ   R14, [WsPtr, #VertAdjust] ; save for pointer programming
+
+        EOR     R14, R8, R2             ; see if now programming different reg
+        MOVS    R14, R14, LSR #24       ; zero if OK
+
+        MOVNE   R5, #0                  ; we've gone wrong, so set 0 adjust
+        MOVNE   R0, R7                  ; and go back to the beginning
+        BNE     %BT18
+
+        MOV     R2, R8                  ; otherwise update register
+40
+ [ VIDC_Type = "VIDC20"
+        TEQ     R6, #HorizSyncWidth             ; if h.sync width register
+        STREQ   R2, [WsPtr, #HSWRSoftCopy]      ; then save for DPMS stuff
+        TEQ     R6, #VertiSyncWidth             ; likewise v.sync width
+        STREQ   R2, [WsPtr, #VSWRSoftCopy]
+
+        TEQ     R6, #VIDCExternal       ; check for external register (which contains syncs)
+        BNE     %FT50
+
+        Push    "r4"
+        BL      ReadSyncType
+        Pull    "r4"
+        BICNE   R2, R2, #(Ext_HSYNCbits :OR: Ext_VSYNCbits)     ; if composite sync then don't invert syncs
+        ORRNE   R2, R2, #Ext_InvertCompVSYNC :OR: Ext_InvertCompHSYNC ; and force both syncs to be composite (because of lack of
+                                                                ; swap in A540 VIDC card)
+        B       %FT75
+50
+        TEQ     R6, #VIDCFSyn
+        BNE     %FT60
+
+        LDR     R8, =FSyn_ResetValue    ; set test bits on, and r > v
+        STR     R8, [R3]
+
+; we may need some delay in here...
+
+        LDR     R8, =FSyn_ClearR :OR: FSyn_ClearV :OR: FSyn_ForceLow :OR: FSyn_ForceHigh
+        ORR     R2, R2, R8
+        BIC     R2, R2, #FSyn_ForceHigh ; force test bits on, except this one
+        STR     R2, [R3]
+
+; we may also need some delay in here...
+
+        BIC     R2, R2, R8              ; remove test bits
+        B       %FT75
+
+60
+  [ MEMC_Type = "IOMD"
+        TEQ     r6, #VIDCDataControl
+        BNE     %FT65
+
+        BIC     r2, r2, #DCR_BusBits
+        MOV     r14, #0
+        LDR     r14, [r14, #VRAMWidth]
+        CMP     r14, #2                 ; if using 64-bit wide VRAM
+        ORRCS   r2, r2, #DCR_Bus63_0    ; then data on all 64 bits
+        ORRCC   r2, r2, #DCR_Bus31_0    ; else for 32-bit wide VRAM or DRAM-only,
+                                        ; data is on low 32 bits
+        BCC     %FT65
+
+; dual-bank VRAM, so HDWR value needs to be halved
+
+        MOV     r14, r2, LSL #(31-10)   ; get HDWR bits at top - NB allow bit 10 to be used here!
+        BIC     r2, r2, r14, LSR #(31-10) ; knock off bits
+        TST     r14, #1 :SHL: (31-10)   ; see if bottom bit would get knocked off
+        ORRNE   r2, r2, #DCR_HDis       ; if so, then disable HDis mechanism (for eg mode 29)
+        ORREQ   r2, r2, r14, LSR #(31-9) ; otherwise, put bits back one bit further down
+
+65
+  ]
+ ]
+        TEQ     R6, #VIDCControl        ; if control register
+        BNE     %FT75
+
+; programming control register, so EOR sync/interlace bits, save in soft copy
+; then work out CursorFudgeFactor from HorizDisplayStart (in CursorFudgeFactor)
+; and bits-per-pixel in control register
+
+        EOR     R2, R2, R4              ; then EOR sync/interlace bits
+
+ [ VIDC_Type = "VIDC20"
+  [ MorrisSupport
+    ;    MOV     R10, #IOMD_Base
+    ;    LDRB    R9, [R10, #IOMD_ID0]
+    ;    CMP     R9, #&98
+    ;    LDRB    R9, [R10, #IOMD_ID1]
+    ;    CMPEQ   R9, #&5B
+    ;    MOVEQ   R9, #32000              ;Morris clocks VIDC20L at 32Mhz
+    ;    LDRNE   R9, =24000              ;RISC PC clocks VIDC20 at 24MHz
+        MOV     R9, #0
+        LDRB    R9, [R9, #IOSystemType]
+        TST     R9, #IOST_7500
+        LDREQ   R9, =24000              ;RISC PC clocks VIDC20 at 24MHz
+        MOVNE   R9, #32000              ;Morris clocks VIDC20L at 32Mhz                         ;
+  |
+        LDR     R9, =24000
+  ]
+        STR     R9, [WsPtr, #VIDCClockSpeed]
+  [ {FALSE}
+
+; The following code computes the actual pixel rate used, but we don't actually
+; need to know this!
+
+        AND     R8, R2, #3
+        CMP     R8, #1
+        MOVEQ   R9, #0                  ; dunno what HCLK is, so assume 0
+        MOVCS   R8, #1                  ; r-modulus not used, so 1
+        BCS     %FT71                   ; PLL not used
+
+        LDR     R10, [R7, #(&D0-&80)*4] ; get FreqSyn register
+        MOV     R8, R10, LSR #8
+        AND     R8, R8, #63
+        ADD     R8, R8, #1              ; r8 = v-modulus
+        MUL     R9, R8, R9
+
+        AND     R8, R10, #63
+        ADD     R8, R8, #1              ; r8 = r-modulus
+71
+        MOV     R10, R2, LSR #2
+        AND     R10, R10, #7
+        ADD     R10, R10, #1            ; r10 = clock divider
+        MUL     R8, R10, R8             ; r8 = global divider
+        DivRem  R10, R9, R8, R11        ; r10 = pixel rate in kHz
+  ]
+        STR     R2, [WsPtr, #VIDCControlCopy] ; and save in copy
+
+  [ MEMC_Type = "IOMD"
+; now compute FSIZE properly
+        LDR     R10, [R7, #(&94-&80)*4] ; get vertidisplayend
+        BIC     R10, R10, #&FF000000
+        LDR     R8, [R7, #(&93-&80)*4]  ; get vertidisplaystart
+        BIC     R8, R8, #&FF000000
+        SUB     R10, R10, R8            ; verti displayed
+        LDR     R8, [R7, #(&90-&80)*4]  ; verti total
+        BIC     R8, R8, #&FF000000
+        SUB     R10, R8, R10
+        ADD     R10, R10, #1            ; vidc parms are n-2, we want n-1
+        MOV     R8, #IOMD_Base
+        STRB    R10, [R8, #IOMD_FSIZE]
+  ]
+
+        LDR     R14, [WsPtr, #CursorFudgeFactor] ; R14 = horiz display start (-18)
+        BIC     R14, R14, #&FF000000
+        ADD     R14, R14, #(18-17)      ; horiz cursor start is programmed with n-17
+        STR     R14, [WsPtr, #CursorFudgeFactor]
+ |
+
+; new algorithm for working out DMA request values from MemorySpeed,
+; bits/pixel, pixel rate, and VIDC clock rate
+; value n to program is >= (25-11v/m)/8
+; therefore n = 3-INT((11v/m-1)/8), forced into range 0..3,
+; where m=memory rate (in kHz)
+;       v=screen memory rate (in kbytes/second (NOT Kbytes/second))
+;        = <vc=VIDC clock>*<vs=value out of MemorySpeedTab>/48
+;
+; ie n = 3-INT((vc*vs*11/(48*m)-1)/8)
+
+        LDR     R9, [R7, #&FC-&80]      ; see if the module has told us the real VIDC clock
+        CMP     R9, #-1
+        BNE     %FT71
+        AND     R8, R2, #ClockControlMask ; extract VIDC clock bits
+        ADR     R9, VIDCClockSpeeds
+        LDR     R9, [R9, R8, LSR #ClockControlShift-2]  ; R9 = vc (in kHz)
+71
+        STR     R9, [WsPtr, #VIDCClockSpeed] ; store away for reading by RVV
+        AND     R8, R2, #15             ; pixel rate in 0,1, bits/pixel in 2,3
+        ADR     R10, MemorySpeedTab     ; now load memory rate relative to 24M
+        LDRB    R8, [R10, R8]           ; R8 = vs
+        MUL     R8, R9, R8              ; R8 = vc*vs
+        ADD     R9, R8, R8, LSL #2      ; R9 = vc*vs*5
+        ADD     R8, R8, R9, LSL #1      ; R8 = vc*vs*11
+        MOV     R9, #0
+        LDR     R9, [R9, #MemorySpeed]  ; memory speed in kHz in bottom 16 bits
+        BIC     R9, R9, #&FF000000
+        BIC     R9, R9, #&00FF0000      ; R9 = m
+        ADD     R9, R9, R9, LSL #1      ; R9 = m*3
+        MOV     R9, R9, LSL #4          ; R9 = m*48
+        DivRem  R10, R8, R9, R11        ; R10 = vc*vs*11/(48*m)
+        SUB     R10, R10, #1            ; R10 = vc*vs*11/(48*m)-1
+        MOVS    R10, R10, ASR #3        ; R10 = (vc*vs*11/(48*m)-1)/8
+        MOVMI   R10, #0                 ; if going to be > 3 then make 3
+        RSBS    R10, R10, #3            ; R10 = 3-(vc*vs*11/(48*m)-1)/8
+        MOVMI   R10, #0                 ; if -ve then make 0
+        BIC     R2, R2, #(3:SHL:4) :OR: ClockControlMask
+                                        ; knock out FIFO + clock bits
+        ORR     R2, R2, R10, LSL #4     ; put result in bits 4,5
+        STR     R2, [WsPtr, #VIDCControlCopy] ; and save in copy
+
+        LDR     R14, [WsPtr, #CursorFudgeFactor] ; R14 = horiz display start
+        BIC     R14, R14, #&FF000000    ; lbpp = 0, 1, 2, 3
+        MOV     R14, R14, LSR #14       ; R14 = (m-19, m-11, m-7, m-5)/2
+
+        MOV     R8, R2, LSR #2          ; put bits 2,3 (lbpp) into bits 0,1
+        AND     R8, R8, #3              ; just look at these bits
+        RSB     R8, R8, #3              ; R8 = 3, 2, 1, 0
+        MOV     R9, #1
+        ADD     R14, R14, R9, LSL R8    ; R14 = (m-3, m-3, m-3, m-3)/2
+        MOV     R14, R14, LSL #1        ; R14 = m-3
+        SUB     R14, R14, #3            ; R14 = m-6
+        STR     R14, [WsPtr, #CursorFudgeFactor]
+ ]
+75
+
+ [ {FALSE} ; *** debugging
+        Push    "r0"
+        MOV     r0, r2
+        BL      TubeDumpR0
+        Pull    "r0"
+        TubeString r8, r9, r10, " ",cc ; pad out to 10 chars
+ ]
+
+        STR     R2, [R3]                ; stuff it into VidC
+80
+        SUBS    R1, R1, #4
+        BNE     %BT20
+
+        ADD     R13, R13, #PushedInfoSize       ; junk stacked data
+
+        MOV     R0, #(1 :SHL: 10)       ; enable video DMA
+        ORR     R1, R0, #(1 :SHL: 9)    ; refresh only in vflyback
+        SWI     XOS_UpdateMEMC
+
+        MOV     R0, #VertiCursorStart + 0       ; program cursor start and end
+        STR     R0, [R3]
+        MOV     R0, #VertiCursorEnd + 0         ; to zero
+        STR     R0, [R3]
+
+        BL      SetVendDefault                  ; set to ScreenEndAdr-16
+
+        MOV     R1, #ScreenEndAdr               ; need to reload cos corrupt
+        LDR     R2, [WsPtr, #TotalScreenSize]
+        SUB     R0, R1, R2                      ; R0 = Vstart
+        BL      SetVstart
+        MOV     R0, #0
+        STRB    R0, [WsPtr, #PointerShapeNumber]
+        STR     R0, [WsPtr, #TeletextOffset]
+        STR     R0, [WsPtr, #CursorStack]       ; restore cursor on a mode
+
+        BL      PalInit                 ; set default palette
+        BL      UnblankScreen
+        BL      SetMouseRectangle
+        BL      FF
+
+        MOV     R1, #Service_ModeChange
+        BL      IssueModeService
+
+        CLRV                            ; indicate no error
+        Pull    PC                      ; return to caller
+
+        MakeErrorBlock BadMODE
+
+        LTORG
+
+ [ :LNOT: (VIDC_Type = "VIDC20")
+MemorySpeedTab
+        =       2, 3, 4, 6,  4, 6, 8, 12,  8, 12, 16, 24,  16, 24, 32, 48
+        ALIGN
+
+VIDCClockSpeeds
+        &       24000
+        &       25175
+        &       36000
+        &       00000
+ ]
+
+; The following symbols, apart from being used in NColourTable,
+; are also used in constructing mode selectors for mode numbers in VduModes
+
+NColour_0       *       1
+NColour_1       *       3
+NColour_2       *       15
+NColour_3       *       63
+NColour_4       *       &FFFF
+NColour_5       *       &FFFFFFFF
+
+        GET     s.vdu.VduModes
+
+; *****************************************************************************
+;
+;       IssueModeService - Issue service (either ModeChanging or ModeChange)
+;
+; in:   R1 = service code
+;
+; out:  R1 corrupted
+;
+
+IssueModeService ENTRY "r2,r3"
+        BL      ReadMonitorType
+        LDR     r2, [WsPtr, #DisplayModeNo]
+        IssueService
+        EXITS
+
+; *****************************************************************************
+;
+;       PushModeInfo - Push appropriate mode table and VIDC parms
+;       onto stack, having generated it by possibly issuing service
+;
+; in:   R10 = mode to try for (issue service if not in range 0..20,22..23)
+;       R11 = mode to use if service not claimed
+;       R10 and R11 should have bit 7 CLEAR
+;
+; out:  If r10 is an invalid mode selector or invalid new format sprite word then
+;         V=1
+;         r0 -> error
+;         stack flat
+;       else
+;         V=1
+;         Stack holds a mode table (size wkwordsize) and VIDC parms (size 32*4)
+;         (total size PushedInfoSize)
+;       endif
+;       All other registers preserved
+;
+
+PushModeInfoAnyMonitor ROUT
+        SUB     sp, sp, #PushedInfoSize
+        Push    "r2-r4,r7-r11, lr"
+        MOV     r3, #-1
+        MOV     r7, #-1                 ; indicate no VIDC stuff necessary
+ [ ModeSelectors
+        CMP     r10, #&100              ; is it a mode selector
+        BCS     PushModeInfoCommonNoService
+ ]
+        BranchIfKnownMode r10, PushModeInfoCommonNoService
+        B       PushModeInfoCommon
+
+PushModeInfo ROUT
+        SUB     sp, sp, #PushedInfoSize
+        Push    "r2-r4,r7-r11, lr"
+        MOV     r7, #0                  ; indicate VIDC stuff IS necessary
+        BL      ReadMonitorType
+PushModeInfoCommon
+        MOV     r2, r10                 ; r2 = original mode
+        BL      OfferModeExtension
+ [ ModeSelectors
+        BEQ     %FT30                   ; [service claimed]
+
+        CMP     r2, #&100               ; service not claimed - check if mode selector
+        MOVCC   r10, r11                ; unrecognised mode number, so use substitute
+        BCC     PushModeInfoCommonNoService
+
+; service not claimed and it's a mode selector - return error "Screen mode not available"
+
+        ADR     r0, ErrorBlock_ModeNotAvailable
+  [ International
+        BL      TranslateError
+  ]
+        B       %FT40
+
+        MakeErrorBlock ModeNotAvailable
+
+30
+        TEQ     r4, #0                  ; if r4 returned zero, then a mode selector was used
+                                        ; either initially or after translation from mode number
+        BEQ     %FT35                   ; so no ws list
+        LDR     r2, [r4, #4]!           ; else if claimed, then find ws base mode
+        CMP     r2, #&100               ; if ws base mode is a mode selector, it's invalid
+        ANDCC   r2, r2, #&7F            ; else knock off shadow bit
+        BCC     %FT35
+        MOV     r10, r11                ; invalid ws base mode, so pretend service not responded to
+
+; and drop thru to PushModeInfoCommonNoService
+
+ |
+        MOVNE   r10, r11                ; not claimed, so use substitute
+        BNE     PushModeInfoCommonNoService
+        LDR     r2, [r4, #4]!           ; if claimed, then find ws base mode
+        AND     r2, r2, #&7F            ; no funny business
+        B       %FT35
+ ]
+PushModeInfoCommonNoService
+        MOV     r2, r10                 ; else use provided mode
+        MOV     r3, #0
+        MOV     r4, #0
+35
+        ADD     r9, sp, #9*4            ; adjust for pushed registers
+ [ ModeSelectors :LOR: {TRUE}           ; mode selectors or sprite mode words
+        CMP     r2, #&100
+        BCC     %FT45
+        BL      GenerateModeSelectorVars ; also copes with new sprite mode word
+        BVC     %FT55
+
+; we got an error
+
+40
+        SETV
+        Pull    "r2-r4,r7-r11,lr"       ; restore registers
+        ADD     sp, sp, #PushedInfoSize ; junk stack frame
+        MOV     pc, lr                  ; exit VS, r0 -> error
+
+45
+ ]
+47
+        ADRL    r14, Vwstab
+        LDR     r10, [r14, r2, LSL #2]
+        ADD     r14, r14, r10           ; r14 -> mode table
+        MOV     r10, #wkwordsize-4
+50
+        LDR     r2, [r14, r10]
+        STR     r2, [r9, r10]
+        SUBS    r10, r10, #4
+        BCS     %BT50
+
+; now change any variables specified in workspace block or mode selector overrides
+
+55
+        TEQ     r4, #0                  ; if service was claimed
+        ADDNE   r4, r4, #4              ; then skip ws base mode
+        BLNE    ProcessModeVarPairs
+
+; hopefully, R7 is still set from up there to be NZ if no VIDC stuff necessary
+
+        CMP     r7, #0
+        Pull    "r2-r4,r7-r11, pc", NE  ; if no VIDC stuff required, exit (NB V=0 from CMP)
+
+; now set up VIDC table, first from MOS table, then changes from module
+
+; first clear out all 32 (or 128) VIDC entries with -1
+
+        ADD     R14, R9, #wkwordsize
+        MOV     R10, #VIDCParmsSize
+        MOV     R8, #-1
+60
+        STR     R8, [R14], #4
+        SUBS    R10, R10, #4
+        BNE     %BT60
+
+; now copy over MOS's table
+
+        ADD     R9, R9, #wkwordsize-VIDCParmsSize
+
+        TEQ     R3, #0                  ; if no module claimed service
+        MOVEQ   R2, R11                 ; then use provided mode
+        BEQ     %FT62
+ [ VIDCListType3
+        LDR     r2, [r3, #0]
+        TEQ     r2, #3                  ; if VIDC list type 3
+        BEQ     ProcessVIDCListType3    ; then don't copy any MOS data into table, just process list
+ ]
+        LDR     r2, [r3, #4]            ; else just load VIDC list base mode, and copy MOS's table for that
+
+62
+        Push    R3
+        BL      ReadMonitorType         ; get monitor type in R3
+        CMP     R3, #NumMonitorTypes    ; monitor type must be in range
+        CMPCC   R2, #NumModes           ; and mode must be in range
+        MOVCC   R11, #NumModes
+        MLACC   R11, R3, R11, R2        ; then form monitortype*numberofmodes + modenumber
+        MOVCS   R11, #0                 ; if illegal then use mode 0 monitortype 0
+        ADRL    R14, BigVIDCTable       ; point to big table
+        LDR     R11, [R14, R11, LSL #2] ; and load offset
+        CMP     R11, #-1                ; if table offset is valid
+        ADDCC   R11, R14, R11           ; then add to table address
+        BLCC    UpdateVIDCTable         ; and fetch data
+        Pull    R3
+
+ [ VIDC_Type = "VIDC20"
+        TEQ     R3, #0
+        LDRNE   R2, [R3, #0]            ; get VIDC table type
+        TSTNE   R2, #2                  ; test for VIDC20 compatible table
+        ADDNE   R11, R3, #8             ; if exists
+        BLNE    UpdateVIDCTable         ; then modify parameters
+
+        CLRV
+        Pull    "R2-R4,R7-R11, PC"      ; ignore any second list for the time being (it's all coded in main list)
+ |
+
+; now copy modules changes
+
+        TEQ     R3, #0
+        ADDNE   R11, R3, #8             ; if module list exists, then
+        BLNE    UpdateVIDCTable         ; modify the table
+
+        LDRNE   R2, [R3, #0]            ; get VIDC table type
+        TSTNE   R2, #1                  ; bit 0 set if has second list
+        BNE     %FT70                   ; has 2nd list
+65
+        CLRV
+        Pull    "R2-R4,R7-R11, PC"      ; if no module table, or no second list
+                                        ; then exit
+70
+        LDR     R2, [R11], #4
+        CMP     R2, #-1
+        BEQ     %BT65                   ; exit VC if found end of list
+
+        MOV     R7, R2, LSR #24         ; R7=type of data (0=pixel rate,1=sync, 2=real VIDC clock)
+        BIC     R2, R2, R7, LSL #24     ; knock out data type
+
+; now the change to allow the real VIDC clock rate to be declared by the module
+
+        CMP     R7, #2
+        STREQ   R2, [R13, #9*4+wkwordsize+&FC-&80] ; store in pseudo-register &FC
+        BEQ     %BT70
+        CMP     R7, #1
+        BHI     %BT70                   ; if > 1 then unknown, so skip
+        BEQ     %FT90                   ; if 1 then sync polarity specification
+
+; R2 is requested pixel rate in kHz - scan through available pixel rates to
+; find match
+
+        MOV     R3, #-1                 ; least error so far
+        MOV     R4, #(0 :SHL: ClockControlShift) :OR: 3
+                                        ; clock and internal pixel rates merged
+                                        ; bits 9 and 10 are clock rate
+                                        ; bits 0 and 1 are internal pixel rate
+                                        ; 0 => n/3, 1 => n/2, 2 => 2n/3, 3 => n
+75
+        ADRL    R9, VIDCClockSpeeds     ; point at table of available rates
+        LDR     R9, [R9, R4, LSR #ClockControlShift-2]  ; get clock rate
+        TST     R4, #2
+        MOVNE   R9, R9, LSL #1          ; if bit 1 set then multiply by 2
+        TST     R4, #1
+        MOVNE   R10, R9, LSR #1         ; if bit 0 set then divide by 2
+        BNE     %FT80                   ; and skip
+        MOV     R7, #3
+        DivRem  R10, R9, R7, R14        ; if bit 0 clear then divide by 3
+80
+        SUBS    R14, R10, R2            ; difference between desired and actual
+        MOVEQ   R8, R4
+        BEQ     %FT85                   ; found exact match, so use it
+        RSBMI   R14, R14, #0            ; get absolute error
+        CMP     R14, R3                 ; if less than least error
+        MOVCC   R3, R14                 ; then R3 = new least error
+        MOVCC   R8, R4                  ; and R8 = best fit
+        TST     R4, #3                  ; if not just tried pixel rate 0
+        SUBNE   R4, R4, #1              ; then try next pixel rate
+        BNE     %BT75
+        ADD     R4, R4, #1 :SHL: ClockControlShift ; move to next clock rate
+        TEQ     R4, #4 :SHL: ClockControlShift  ; if not finished
+        ORRNE   R4, R4, #3              ; then set internal pixel rate to 3
+        BNE     %BT75                   ; and loop
+
+; R8 is best fit, so store it in the VIDC list
+
+85
+        ADD     R9, R13, #9*4+wkwordsize+&E0-&80 ; point at control register
+        LDR     R10, [R9]               ; get previously specified CR
+        BIC     R10, R10, #ClockControlMask ; knock out clock select
+        BIC     R10, R10, #3            ; and pixel rate select
+        ORR     R10, R10, R8
+        STR     R10, [R9]
+        B       %BT70                   ; go back and see if any more
+
+; R2 = sync polarity specification
+; bit 0 set => -ve Hsync
+; bit 1 set => -ve Vsync
+
+90
+        ADD     R9, R13, #9*4+wkwordsize+&E0-&80 ; point at control register
+        LDR     R10, [R9]               ; get previously specified CR
+        BIC     R10, R10, #SyncControlMask ; knock out sync selects
+        ORR     R10, R10, R2, LSL #SyncControlShift ; and insert new ones
+        STR     R10, [R9]
+        B       %BT70                   ; go back and see if any more
+
+ ]
+
+; *****************************************************************************
+;
+;       GenerateModeSelectorVars - Work out mode variables from mode selector
+;                                    or new format sprite mode word
+;
+; Note: the algorithms used to generate these variables are duplicated in
+; s.vdu.vduswis in the OS_ReadModeVariable code.
+
+; in:   r2 = new format sprite word or pointer to mode selector
+;       r9 -> stack frame to store vars in
+;
+; out:  If valid then
+;           V=0
+;           r0 preserved
+;       else
+;           V=1
+;           r0 -> error
+;       endif
+;       All other registers preserved
+;
+
+GenerateModeSelectorVars ENTRY "r0,r1,r3-r8,r10-r12"
+ [ ModeSelectors
+        ASSERT  ModeSelector_Flags = 0
+        ASSERT  ModeSelector_XRes = 4
+        ASSERT  ModeSelector_YRes = 8
+        ASSERT  ModeSelector_PixelDepth = 12
+ ]
+        TST     r2, #1                          ; is it a new format sprite mode word?
+        BNE     %FT50                           ; [yes, so skip]
+ [ ModeSelectors
+        MOV     r0, r2
+        BL      ValidateModeSelector
+        BVS     %FT95                           ; invalid - return error
+        LDMIB   r2, {r4-r6}                     ; r4 = xres; r5 = yres; r6 = pixdepth
+
+        STR     r6, [r9, #wkLog2BPC]            ; log2bpc = log2bpp = pixdepth
+        STR     r6, [r9, #wkLog2BPP]
+        ADR     lr, NColourTable
+        LDR     lr, [lr, r6, LSL #2]            ; load NColour value
+        STR     lr, [r9, #wkNColour]
+        ADR     lr, PalIndexTable
+        LDRB    lr, [lr, r6]
+        STR     lr, [r9, #wkPalIndex]
+        ADR     lr, ECFIndexTable
+        LDRB    lr, [lr, r6]
+        STR     lr, [r9, #wkECFIndex]
+
+        MOV     lr, #0
+        STR     lr, [r9, #wkYShftFactor]        ; yshftfactor = 0 (obsolete)
+        STR     lr, [r9, #wkModeFlags]          ; modeflags = 0
+
+ [ RogerEXEY
+; TMD 09-Dec-93
+; New algorithms for xeig, yeig from Roger:
+;       xeig = 1: yeig = 1
+;       if yres<xres/2 OR yres<400 then yeig = 2
+;       if (xres<<xeig)<(yres<<yeig) then xeig = 2
+
+        CMP     r5, r4, LSR #1                  ; if yres < xres/2
+        CMPCS   r5, #400                        ; or yres < 400
+        MOVCC   r7, #2                          ; then yeig = 2
+        MOVCS   r7, #1                          ; else yeig = 1
+        STR     r7, [r9, #wkYEigFactor]
+
+        MOV     r7, r5, LSL r7                  ; r7 = yres << yeig
+        CMP     r7, r4, LSL #1                  ; if (xres<<1) < (yres<<yeig)
+        MOVHI   r7, #2                          ; then xeig = 2
+        MOVLS   r7, #1                          ; else xeig = 1
+        STR     r7, [r9, #wkXEigFactor]
+
+        MOV     lr, #1
+ |
+        MOV     lr, #1
+        STR     lr, [r9, #wkXEigFactor]         ; xeig = 1
+        CMP     r5, r4, LSR #1                  ; if yres < xres/2
+        MOVCC   r7, #2                          ; then yeig = 2
+        MOVCS   r7, #1                          ; else yeig = 1
+        STR     r7, [r9, #wkYEigFactor]
+ ]
+        RSB     r7, lr, r4, LSR #3              ; scrrcol = (xres >> 3) -1
+        STR     r7, [r9, #wkScrRCol]
+        RSB     r7, lr, r5, LSR #3              ; scrbrow = (yres >> 3) -1
+        STR     r7, [r9, #wkScrBRow]
+
+        SUB     r7, r4, #1
+        STR     r7, [r9, #wkXWindLimit]         ; xwindlimit = xres-1
+        SUB     r7, r5, #1
+        STR     r7, [r9, #wkYWindLimit]         ; ywindlimit = yres-1
+
+        MOV     r7, r4, LSL r6                  ; r7 = xres << pixdepth
+        MOV     lr, r7, LSR #3
+        STR     lr, [r9, #wkLineLength]         ; linelen = (xres << pixdepth) >> 3
+
+        MUL     r7, r5, r7                      ; r7 = (xres * yres) << pixdepth
+        MOV     lr, r7, LSR #3
+        STR     lr, [r9, #wkScreenSize]         ; screensize = ((xres * yres) << pixdepth) >> 3
+
+        ADD     r4, r2, #ModeSelector_ModeVars  ; now do pairs of mode variables
+        BL      ProcessModeVarPairs
+
+        CLRV
+        EXIT
+
+ |
+        B       %FT90                           ; it's not a new format sprite word, and mode selectors not enabled
+                                                ; so return error
+ ]
+
+; store info for new format sprite word in stack frame
+
+50
+        MOV     r0, #0
+        STR     r0, [r9, #wkYShftFactor]        ; yshftfactor = 0
+        STR     r0, [r9, #wkModeFlags]          ; modeflags = 0
+
+        MOV     r0, r2, LSR #27                 ; get type
+        CMP     r0, #SpriteType_MAX             ; check for legality - NB type 0 is illegal because r2>=&100
+        MOVCS   r0, #SpriteType_Substitute      ; substitute if unknown
+        ADRL    lr, NSM_bpptable-4
+        LDR     r0, [lr, r0, LSL #2]            ; get the bpp from table
+
+        STR     r0, [r9, #wkLog2BPC]
+        STR     r0, [r9, #wkLog2BPP]
+
+        ADR     r1, NColourTable
+        LDR     r1, [r1, r0, LSL #2]
+        STR     r1, [r9, #wkNColour]
+
+        ADR     r1, PalIndexTable
+        LDRB    r1, [r1, r0]
+        STR     r1, [r9, #wkPalIndex]
+
+        ADR     r1, ECFIndexTable
+        LDRB    r1, [r1, r0]
+        STR     r1, [r9, #wkECFIndex]
+
+        MOV     r1, r2, LSL #(31-13)
+        MOV     r1, r1, LSR #(31-13)+1          ; extract xdpi (bits 1..13)
+
+        TEQ     r1, #180                        ; 180 => xeig=0
+        MOVEQ   r1, #0
+        BEQ     %FT70
+
+        TEQ     r1, #22                         ; 22/23 => xeig=3
+        TEQNE   r1, #23
+        MOVEQ   r1, #3
+        BEQ     %FT70
+
+        TEQ     r1, #(45 :SHL: 2), 2            ; check if 45   (EQ,CC if so)
+        CMPNE   r1, #90                         ; or 90         (EQ,CS if so)
+        BNE     %FT80
+        MOVCC   r1, #2                          ; 45 => xeig=2
+        MOVCS   r1, #1                          ; 90 => xeig=1
+70
+        STR     r1, [r9, #wkXEigFactor]
+
+        MOV     r1, r2, LSL #(31-26)
+        MOV     r1, r1, LSR #(31-26)+14         ; extract ydpi (bits 14..26)
+
+        TEQ     r1, #180                        ; 180 => yeig=0
+        MOVEQ   r1, #0
+        BEQ     %FT71
+
+        TEQ     r1, #22                         ; 22/23 => yeig=3
+        TEQNE   r1, #23
+        MOVEQ   r1, #3
+        BEQ     %FT71
+
+        TEQ     r1, #(45 :SHL: 2), 2            ; check if 45   (EQ,CC if so)
+        CMPNE   r1, #90                         ; or 90         (EQ,CS if so)
+        BNE     %FT80
+        MOVCC   r1, #2                          ; 45 => yeig=2
+        MOVCS   r1, #1                          ; 90 => yeig=1
+71
+        STR     r1, [r9, #wkYEigFactor]
+
+        CLRV
+        EXIT
+
+80
+        ADR     r0, ErrorBlock_Sprite_BadDPI
+90
+ [ International
+        BL      TranslateError
+ ]
+95
+        STR     r0, [sp]                        ; update saved r0
+        SETV                                    ; indicate error
+        EXIT
+
+NColourTable    &       NColour_0, NColour_1, NColour_2
+                &       NColour_3, NColour_4, NColour_5
+PalIndexTable   =       0, 1, 2, 3, 6, 7
+        ALIGN                                   ; makes ECFIndexTable more accessible
+ECFIndexTable   =       4, 2, 3, 5, 5, 5
+        ALIGN
+
+        MakeErrorBlock  BadPixelDepth
+        MakeErrorBlock  Sprite_BadDPI
+
+ [ ModeSelectors
+; *****************************************************************************
+;
+;       ValidateModeSelector - Check a mode selector is valid
+;
+; in:   r0 -> mode selector
+;
+; out:  If OK, then
+;         V=0
+;         All registers preserved
+;       else
+;         V=1
+;         r0 -> error
+;         All other registers preserved
+;       endif
+;
+
+ValidateModeSelector ENTRY
+        LDR     lr, [r0, #ModeSelector_Flags]
+        AND     lr, lr, #ModeSelectorFlags_FormatMask
+        TEQ     lr, #ModeSelectorFlags_ValidFormat
+        ADRNE   r0, ErrorBlock_BadMSFlags
+        BNE     %FT90
+        LDR     lr, [r0, #ModeSelector_PixelDepth]
+        CMP     lr, #6
+        ADRCS   r0, ErrorBlock_BadPixelDepth
+        BCS     %FT90
+        CLRV
+        EXIT
+
+90
+  [ International
+        BL      TranslateError
+  ]
+        SETV
+        EXIT
+
+        MakeErrorBlock BadMSFlags
+
+ ]
+
+; *****************************************************************************
+;
+;       ProcessModeVarPairs - Modify stacked variable info from
+;                              mode variable (index, value) pairs
+;
+;       Internal routine used to do Service_ModeExtension workspace lists and
+;        mode selectors
+;
+; in:   r4 -> first pair (may be none)
+;       r9 -> stack frame
+;
+; out:  All registers preserved
+
+ProcessModeVarPairs ENTRY "r4, r8, r10"
+        ADRL    r14, RMVTab
+10
+        LDR     r10, [r4], #4           ; get next entry in ws table
+        CMP     r10, #-1
+        EXIT    EQ                      ; no more to do
+        CMP     r10, #(SWIRVVTabModeEnd-SWIRVVTab) ; is it a mode variable ?
+        BCS     %BT10                   ; no, then ignore
+        LDRB    r10, [r14, r10]         ; load index out of RMVTab
+        MOVS    r10, r10, LSR #1        ; shift byte/word flag into carry
+        LDR     r8, [r4], #4            ; load value
+        STRCCB  r8, [r9, r10]           ; either store byte
+        STRCS   r8, [r9, r10]           ; or store word
+        B       %BT10
+
+ [ VIDCListType3
+
+                                ^       4
+VIDCList3_PixelDepth            #       4
+VIDCList3_HorizSyncWidth        #       4
+VIDCList3_HorizBackPorch        #       4
+VIDCList3_HorizLeftBorder       #       4
+VIDCList3_HorizDisplaySize      #       4
+VIDCList3_HorizRightBorder      #       4
+VIDCList3_HorizFrontPorch       #       4
+VIDCList3_VertiSyncWidth        #       4
+VIDCList3_VertiBackPorch        #       4
+VIDCList3_VertiTopBorder        #       4
+VIDCList3_VertiDisplaySize      #       4
+VIDCList3_VertiBottomBorder     #       4
+VIDCList3_VertiFrontPorch       #       4
+VIDCList3_PixelRate             #       4
+VIDCList3_SyncPol               #       4
+VIDCList3_ControlList           #       0
+
+; Indices in control list
+
+                                ^       1
+ControlList_LCDMode             #       1
+ControlList_LCDDualPanelMode    #       1
+ControlList_LCDOffset0          #       1
+ControlList_LCDOffset1          #       1
+ControlList_HiResMode           #       1
+ControlList_DACControl          #       1
+ControlList_RGBPedestals        #       1
+ControlList_ExternalRegister    #       1
+ControlList_HClockSelect        #       1
+ControlList_RClockFrequency     #       1
+ControlList_DPMSState           #       1
+ControlList_InvalidReason       #       0
+
+;
+;       ProcessVIDCListType3 - Convert type3 VIDC list into VIDC20 parameters
+;
+; in:   r3 -> VIDC list (type 3)
+;       r9 -> VIDC table (where R9!(nn << 2) holds parameter for register nnxxxxxx
+;              for nn=80 to FF
+;       stacked r2-r4, r7-r11, lr
+
+ProcessVIDCListType3 ROUT
+        LDR     r2, [r3, #VIDCList3_HorizSyncWidth]
+        BIC     r2, r2, #1              ; must be even
+        SUB     r2, r2, #8              ; horiz parameters start off at n-8
+        ORR     r14, r2, #HorizSyncWidth
+        STR     r14, [r9, #HorizSyncWidth :SHR: 22]
+
+        LDR     r4, [r3, #VIDCList3_HorizBackPorch]
+        ADD     r2, r2, r4
+        BIC     r2, r2, #1
+        SUB     r2, r2, #4              ; HBSR is N-12
+        ORR     r14, r2, #HorizBorderStart
+        STR     r14, [r9, #HorizBorderStart :SHR: 22]
+
+        LDR     r4, [r3, #VIDCList3_HorizLeftBorder]
+        ADD     r2, r2, r4
+        BIC     r2, r2, #1
+        SUB     r2, r2, #6              ; HDSR is N-18
+        ORR     r14, r2, #HorizDisplayStart
+        STR     r14, [r9, #HorizDisplayStart :SHR: 22]
+
+        LDR     r4, [r3, #VIDCList3_HorizDisplaySize]
+        BIC     r4, r4, #1
+        LDR     r7, [r3, #VIDCList3_PixelDepth]
+        MOV     r10, r4, LSL r7         ; number of bits in one displayed raster (not needed later any more)
+
+        ANDS    r8, r10, #31            ; if line length not multiple of 32
+        MOVNE   r8, #DCR_HDis           ; then set HDis bit
+        ORR     r8, r8, r10, LSR #5     ; OR in number of words per line
+
+; Note - the DCR_Bus bits get overridden and the HDWR bits modified further down the line by the mode change code
+; on the basis of how much VRAM we've got
+
+ [ MEMC_Type = "IOMD"
+        ORR     r8, r8, #DCR_VRAMOff :OR: DCR_Bus31_0 :OR: DCR_Sync
+ |
+        ORR     r8, r8, #DCR_VRAMOff :OR: DCR_Bus31_0
+ ]
+        ORR     r8, r8, #VIDCDataControl
+        STR     r8, [r9, #VIDCDataControl :SHR: 22]
+
+        ADD     r2, r2, r4              ; HDER is also N-18
+        ORR     r14, r2, #HorizDisplayEnd
+        STR     r14, [r9, #HorizDisplayEnd :SHR: 22]
+
+        LDR     r4, [r3, #VIDCList3_HorizRightBorder]
+        ADD     r2, r2, r4
+        ADD     r2, r2, #6              ; HBER is N-12
+        BIC     r2, r2, #1
+        ORR     r14, r2, #HorizBorderEnd
+        STR     r14, [r9, #HorizBorderEnd :SHR: 22]
+
+        LDR     r4, [r3, #VIDCList3_HorizFrontPorch]
+        ADD     r2, r2, r4
+        ADD     r2, r2, #4              ; HCR is N-8
+        BIC     r2, r2, #3              ; must be mult of 4
+        ORR     r14, r2, #HorizCycle
+        STR     r14, [r9, #HorizCycle :SHR: 22]
+
+        ADD     r2, r2, #8              ; HIR is N/2
+        MOV     r2, r2, LSR #1
+        ORR     r14, r2, #HorizInterlace
+        STR     r14, [r9, #HorizInterlace :SHR: 22]
+
+        LDR     r2, [r3, #VIDCList3_VertiSyncWidth]
+        SUB     r2, r2, #2              ; vertical registers are N-2
+        ORR     r14, r2, #VertiSyncWidth
+        STR     r14, [r9, #VertiSyncWidth :SHR: 22]
+
+        LDR     r4, [r3, #VIDCList3_VertiBackPorch]
+        ADD     r2, r2, r4
+        ORR     r14, r2, #VertiBorderStart
+        STR     r14, [r9, #VertiBorderStart :SHR: 22]
+
+        LDR     r4, [r3, #VIDCList3_VertiTopBorder]
+        ADD     r2, r2, r4
+        ORR     r14, r2, #VertiDisplayStart
+        STR     r14, [r9, #VertiDisplayStart :SHR: 22]
+
+        LDR     r4, [r3, #VIDCList3_VertiDisplaySize]
+        ADD     r2, r2, r4
+        ORR     r14, r2, #VertiDisplayEnd
+        STR     r14, [r9, #VertiDisplayEnd :SHR: 22]
+
+        LDR     r4, [r3, #VIDCList3_VertiBottomBorder]
+        ADD     r2, r2, r4
+        ORR     r14, r2, #VertiBorderEnd
+        STR     r14, [r9, #VertiBorderEnd :SHR: 22]
+
+        LDR     r4, [r3, #VIDCList3_VertiFrontPorch]
+        ADD     r2, r2, r4
+        ORR     r14, r2, #VertiCycle
+        STR     r14, [r9, #VertiCycle :SHR: 22]
+
+        LDR     r4, [r3, #VIDCList3_SyncPol]
+        MOV     r14, #VIDCExternal
+        TST     r4, #1
+        ORRNE   r14, r14, #Ext_InvertHSYNC
+        TST     r4, #2
+        ORRNE   r14, r14, #Ext_InvertVSYNC
+        ORR     r14, r14, #Ext_DACsOn
+        ORR     r14, r14, #Ext_ERegExt
+        STR     r14, [r9, #VIDCExternal :SHR: 22]
+
+        Push    "r0, r1"
+        LDR     r0, [r3, #VIDCList3_PixelRate]  ; get pixel rate
+        MOV     r10, r0, LSL r7                 ; peak mem b/w (x 1E3 bits/sec) - save for FIFO calculation
+
+ [ MorrisSupport
+   ;     MOV     R14, #IOMD_Base
+   ;     LDRB    R1, [R14, #IOMD_ID0]
+   ;     CMP     R1, #&98
+   ;     LDRB    R1, [R14, #IOMD_ID1]
+   ;     CMPEQ   R1, #&5B
+   ;     MOVEQ   R1, #32000              ;Morris clocks VIDC20L at 32Mhz
+   ;     LDRNE   R1, =24000              ;RISC PC clocks VIDC20 at 24MHz
+        MOV     R1, #0
+        LDRB    R1, [R1, #IOSystemType]
+        TST     R1, #IOST_7500
+        LDREQ   R1, =24000              ;RISC PC clocks VIDC20 at 24MHz
+        MOVNE   R1, #32000              ;Morris clocks VIDC20L at 32Mhz
+;>>>RCM says can we replace the above by
+;>>>    LDR     R1, [WsPtr, #VIDCClockSpeed]
+ |
+        LDR     r1, =rclk       ; eventually will need to replace this if specified in control list
+ ]
+        BL      ComputeModuli   ; out: r0 = FSync bits, r1 = CR bits
+        ORR     r0, r0, #VIDCFSyn
+        STR     r0, [r9, #VIDCFSyn :SHR: 22]
+
+        TEQ     r7, #5          ; if 32 bpp, then stick in 6 not 5
+        MOVEQ   r7, #6
+        ORR     r0, r1, r7, LSL #5
+
+; now work out FIFO load position - r10 is b/w in thousands of bytes/sec
+
+ [ {TRUE}
+
+; do it by means of a binary chop on 3 bits
+
+        ADR     r4, FIFOLoadTable
+        LDR     r2, [r4, #4*4]                  ; load 0-3/4-7 split
+        CMP     r10, r2
+        MOVLS   r7, #0                          ; if <=, then bottom half
+        MOVHI   r7, #4                          ; else top half
+        ADDHI   r4, r4, #4*4                    ; and advance table pointer
+
+        LDR     r2, [r4, #2*4]
+        CMP     r10, r2
+        ORRHI   r7, r7, #2
+        ADDHI   r4, r4, #2*4
+
+        LDR     r2, [r4, #1*4]
+        CMP     r10, r2
+        ORRHI   r7, r7, #1
+ |
+        CMP     r10, #&10000 :SHL: 3            ; this value (65.536 Mbytes/sec) lies above the point at which 7 works
+                                                ; and below the point at which 7 is needed
+        MOVCC   r7, #6
+        MOVCS   r7, #7
+ ]
+
+        ORR     r0, r0, r7, LSL #CR_FIFOLoadShift
+        ORR     r0, r0, #VIDCControl
+        STR     r0, [r9, #VIDCControl :SHR: 22]
+
+; Now go through VIDC control parameters list (not all indices can be handled yet)
+
+        ADD     r3, r3, #VIDCList3_ControlList-8  ; point at 1st entry -8
+50
+        LDR     r4, [r3, #8]!                   ; load next index
+        CMP     r4, #-1                         ; if -1 then end of list
+        BEQ     %FT60                           ; so skip
+
+        CMP     r4, #0                          ; if non-zero (CS if zero)
+        CMPNE   r4, #ControlList_InvalidReason  ; and if known reason
+        LDRCC   r2, [r3, #4]                    ; then load value
+        BLCC    ProcessControlListItem          ; and process this item
+        B       %BT50                           ; go onto next item in list
+
+ [ {TRUE}
+FIFOLoadTable
+  [ {TRUE}      ; put a minimum of 4, cos 800 x 600 x 1bpp don't work otherwise
+        &       0                               ; dummy entry (not used)
+        &       0                               ; never use 0
+        &       0                               ; use 1 up to (and including) here
+        &       0                               ; use 2 up to (and including) here
+        &       0                               ; use 3 up to (and including) here
+        &       60000 :SHL: 3                   ; use 4 up to (and including) here
+        &       75000 :SHL: 3                   ; use 5 up to (and including) here
+        &       90000 :SHL: 3                   ; use 6 up to (and including) here
+                                                ; else use 7
+  |
+        &       0                               ; dummy entry (not used)
+        &       0                               ; never use 0
+        &       12000 :SHL: 3                   ; use 1 up to (and including) here
+        &       24000 :SHL: 3                   ; use 2 up to (and including) here
+        &       36000 :SHL: 3                   ; use 3 up to (and including) here
+        &       60000 :SHL: 3                   ; use 4 up to (and including) here
+        &       75000 :SHL: 3                   ; use 5 up to (and including) here
+        &       90000 :SHL: 3                   ; use 6 up to (and including) here
+                                                ; else use 7
+  ]
+ ]
+
+
+60
+
+; Now, for debugging purposes, output data to a file
+
+ [ {FALSE}
+
+        ! 0, "**** WARNING: Mode change debugging assembled in ****"
+
+        MOV     r0, #&80
+        ADR     r1, ModeFilename
+        SWI     XOS_Find
+
+        Pull    "r0, r1, r2-r4, r7-r11, pc", VS
+
+        MOV     r1, r0
+        MOV     r0, #2
+        ADD     r2, r9, #VIDCParmsSize  ; r2 -> data
+        MOV     r3, #VIDCParmsSize
+        SWI     XOS_GBPB
+
+        MOV     r0, #0
+        SWI     XOS_Find
+
+        Pull    "r0, r1, r2-r4, r7-r11, pc"
+
+ModeFilename
+        =       "SCSI::HD4.$.ModeData", 0
+        ALIGN
+ |
+        Pull    "r0, r1, r2-r4, r7-r11, pc"
+ ]
+
+; *****************************************************************************
+;
+;       ProcessControlListItem
+;
+; in:   r2 = value for item
+;       r4 = index for item (guaranteed in range)
+;       r9 -> VIDC register array
+;
+; out:  r0-r2, r4, r7, r8, r10, r11 may be corrupted
+;       r3, r9, r12 must be preserved
+
+ProcessControlListItem ENTRY
+        LDR     pc, [pc, r4, LSL #2]
+        NOP
+        &       ProcessControlListNOP                   ; 0 - NOP
+        &       ProcessControlListLCDMode               ; 1 - LCD mode
+        &       ProcessControlListLCDDualPanelMode      ; 2 - LCD dual-panel mode
+        &       ProcessControlListLCDOffsetRegister0    ; 3 - LCD offset register 0
+        &       ProcessControlListLCDOffsetRegister1    ; 4 - LCD offset register 1
+        &       ProcessControlListHiResMode             ; 5 - Hi-res mode
+        &       ProcessControlListDACControl            ; 6 - DAC control
+        &       ProcessControlListRGBPedestals          ; 7 - RGB pedestal enables
+        &       ProcessControlListExternalRegister      ; 8 - External register
+        &       ProcessControlListNOP                   ; 9 - HClk select/specify
+        &       ProcessControlListNOP                   ; 10 - RClk frequency
+        &       ProcessControlListDPMSState             ; 11 - DPMS state
+
+ProcessControlListLCDMode
+        MOV     r1, #Ext_LCDGrey
+05
+        MOV     r0, #VIDCExternal
+10
+        MOV     r7, r1
+        TEQ     r2, #0                          ; if value non-zero
+        MOVNE   r2, r1                          ; then use value in r1
+15
+        AND     r2, r2, r7                      ; ensure only relevant bits set
+        LDR     lr, [r9, r0, LSR #22]           ; load word from register bank
+        BIC     lr, lr, r7                      ; knock out bits in mask
+        ORR     lr, lr, r2                      ; OR in new bits
+        STR     lr, [r9, r0, LSR #22]           ; and store in array
+ProcessControlListNOP
+        EXIT
+
+ProcessControlListHiResMode
+        MOV     r1, #Ext_HiResMono              ; bit of a misnomer, it's not nec. mono
+        B       %BT05
+
+ProcessControlListDACControl
+        MOV     r1, #Ext_DACsOn
+        B       %BT05
+
+ProcessControlListRGBPedestals
+        MOV     r0, #VIDCExternal
+        MOV     r2, r2, LSL #Ext_PedsShift
+        MOV     r7, #Ext_PedsOn
+        B       %BT15
+
+ProcessControlListExternalRegister
+        MOV     r0, #VIDCExternal
+        MOV     r7, #&FF
+        B       %BT15
+
+ProcessControlListLCDDualPanelMode
+        MOV     r0, #VIDCControl
+        MOV     r1, #CR_DualPanel
+        B       %BT10
+
+ProcessControlListLCDOffsetRegister0
+        MOV     r0, #LCDOffsetRegister0
+20
+        ORR     r2, r2, r0                      ; put high bits of register at top
+        STR     r2, [r9, r0, LSR #22]           ; and store in array
+        EXIT
+
+ProcessControlListLCDOffsetRegister1
+        MOV     r1, #LCDOffsetRegister1
+        B       %BT20
+
+ProcessControlListDPMSState
+        MOV     r0, #PseudoRegister_DPMSState   ; pseudo-register holding DPMS state
+        ORR     r2, r2, r0                      ; form combined value
+        STR     r2, [r9, r0, LSR #22]           ; store in register
+        EXIT
+
+
+; *****************************************************************************
+;
+;       ComputeModuli - Work out VCO moduli for a given frequency
+;
+; in:   r0 = desired frequency (kHz)
+;       r1 = rclk frequency (kHz) (normally 24000)
+;
+; out:  r0 = bits to put in bits 0..15 of Frequency Synthesizer Register
+;       r1 = bits to put in bits 0..4 of Control Register
+
+rclk    *       24000           ; Reference clock into VIDC20 (in kHz)
+VCO_Min *       55000           ; minimum VCO frequency (in kHz)
+VCO_Max *      110000           ; maximum VCO frequency (in kHz)
+
+fpshf   *       11              ; Shift value for fixed point arithmetic
+
+        ^       0, sp
+
+BestDInOrOutOfRange     #       4
+BestRInOrOutOfRange     #       4
+BestVInOrOutOfRange     #       4
+BestDInRange            #       4
+BestRInRange            #       4
+BestVInRange            #       4
+BestRangeError          #       4
+ComputeModuliStack      *       :INDEX: @
+
+ComputeModuli ENTRY "r2-r12", ComputeModuliStack
+        MOV     r12, #-1                ; smallest error for values in or out of VCO range
+        MOV     r11, #-1                ; smallest error for values in VCO range
+        STR     r11, BestDInRange
+        STR     r11, BestVInRange
+        STR     r11, BestRInRange
+        STR     r11, BestDInOrOutOfRange
+        STR     r11, BestVInOrOutOfRange
+        STR     r11, BestRInOrOutOfRange
+        STR     r11, BestRangeError
+        MOV     r5, r1                  ; r5 = rclk frequency, normally 24000 (32000 on Morris)
+        LDR     r1, =VCO_Min            ; r1 = minimum VCO frequency (in kHz)
+        LDR     r2, =VCO_Max            ; r2 = maximum VCO frequency (in kHz)
+        MOV     r3, #1                  ; r3 = D
+10
+        MOV     r4, #1                  ; r4 = R
+15
+        MUL     r6, r0, r3              ; r6 = xD
+        MUL     r7, r6, r4              ; r7 = xRD
+        ADD     r7, r7, r5, LSR #1      ; r7 = xRD + vref/2
+        DivRem  r8, r7, r5, r9          ; r8 = (xRD + vref/2) DIV vref = V value
+
+        TEQ     r4, #1                  ; if R=1 then V must be 1, else it's no good
+        BNE     %FT20
+        TEQ     r8, #1
+        BNE     %FT50
+        BEQ     %FT25
+20
+        CMP     r8, #2                  ; if R<>1 then V must be in range 2..64
+        RSBCSS  r7, r8, #64
+        BCC     %FT50                   ; V out of range, so skip
+25
+        MUL     r7, r5, r8              ; r7 = V * vref
+        MOV     r7, r7, LSL #fpshf      ; r7 = (V * vref) << fixedpointshift
+        DivRem  r9, r7, r4, r14         ; r9 = ((V * vref) << fixedpointshift)/R = VCO frequency << fixedpointshift
+        MOV     r6, r9
+        DivRem  r7, r9, r3, r14         ; r7 = output frequency << fixedpointshift
+        SUBS    r7, r7, r0, LSL #fpshf
+        RSBCC   r7, r7, #0              ; r7 = absolute error << fixedpointshift
+
+        TEQ     r4, #1                  ; if R=1 then no need to check VCO range
+        BEQ     %FT27                   ; because VCO won't be used, so it's a 1st class citizen
+
+        CMP     r6, r1, LSL #fpshf      ; test if VCO freq >= min
+        RSBCSS  r14, r6, r2, LSL #fpshf ; and <= max
+        BCC     %FT40                   ; not in range, so not a first class citizen
+27
+        CMP     r7, r11
+        BHI     %FT40                   ; worse than the best case for in VCO range, so ignore
+        BCC     %FT30                   ; is definitely better than the best case for in or out
+
+        LDR     r14, BestRInRange       ; is equal best for in, so check R value
+        CMP     r4, r14                 ; is newR < bestR
+        BCS     %FT40                   ; is greater or equal R value (ie not higher comp. freq., so not best)
+30
+        MOV     r11, r7
+        STR     r3, BestDInRange
+        STR     r4, BestRInRange
+        STR     r8, BestVInRange
+        MOV     r14, #0
+        B       %FT45
+
+40
+        RSBS    r14, r6, r1, LSL #fpshf ; r14 = min-this, if this<min
+        SUBCC   r14, r6, r2, LSL #fpshf ; else r14 = this-max, ie r14 = how much this is outside range
+
+        CMP     r7, r12
+        BHI     %FT50                   ; worse than the best case for in or out of VCO range, so ignore
+        BCC     %FT45                   ; is definitely better than the best case for in or out
+
+        LDR     r9, BestRangeError      ; is equal best for in or out, so check error
+        CMP     r14, r9
+        BCS     %FT50                   ; not lower error, so skip
+45
+        MOV     r12, r7
+        STR     r3, BestDInOrOutOfRange
+        STR     r4, BestRInOrOutOfRange
+        STR     r8, BestVInOrOutOfRange
+        STR     r14, BestRangeError
+50
+        ADD     r4, r4, #1
+        CMP     r4, #16                 ; R goes from 2 to 16 (was 2 to 64)
+        BLS     %BT15
+
+        ADD     r3, r3, #1
+        CMP     r3, #8                  ; D goes from 1 to 8
+        BLS     %BT10
+
+        ADR     r2, BestDInRange
+        LDR     r3, [r2]
+        CMP     r3, #-1
+        ADDEQ   r2, r2, #BestDInOrOutOfRange - BestDInRange
+        LDREQ   r3, [r2]                ; r3 = Best D
+        LDR     r4, [r2, #BestRInRange - BestDInRange]  ; r4 = Best R
+        LDR     r5, [r2, #BestVInRange - BestDInRange]  ; r5 = Best V
+
+        SUBS    r4, r4, #1              ; values in FSyn are n-1
+        MOVEQ   r4, #63                 ; if R=V=1 then use max R
+        MOVEQ   r5, #2                  ; and min V to make VCO go really slow
+
+        SUB     r5, r5, #1              ; for both v and r
+        ASSERT  FSyn_RShift = 0
+        ORR     r0, r4, r5, LSL #FSyn_VShift
+
+        SUB     r3, r3, #1              ; D is also stored as n-1
+        MOV     r1, r3, LSL #CR_PixelDivShift
+        ASSERT  CR_VCLK = 0
+        ORREQ   r1, r1, #CR_RCLK        ; if using VCO then set for VCLK, else RCLK
+
+        EXIT
+
+ ]
+
+; *****************************************************************************
+;
+;       UpdateVIDCTable - Add changes to a pushed VIDC table
+;
+; in:   R9 + nn        -> entry on stack for register nn000000
+;    or R9 + (nn << 2) -> ditto, for VIDC20
+;       R11 -> change table, terminated with -1
+;
+; out:  R11 -> word after -1 terminator
+;       All other registers preserved (including PSR)
+;
+ [ MorrisSupport
+UpdateVIDCTable ROUT
+        Push    "R0,R14"
+        MOV     R0, #0
+        LDRB    R0, [R0, #IOSystemType]
+        TST     R0, #IOST_7500
+        MOVEQ   R0, #1                          ;if rclk is 24MHz, stop at first -1
+        MOVNE   R0, #2                          ;if rclk is 32MHz, overwrite clock dividers with different data
+10
+        LDR     R14, [R11], #4
+        CMP     R14, #-1
+        LDREQ   R14, [R11], #4                  ;EQ, on terminator, so skip it
+        SUBEQS  R0, R0, #1
+        Pull    "R0,PC",EQ,^                    ;EQ, quit on first (iff rclk=24MHz) or second terminator (iff rclk=32MHz)
+
+        CMP     R14, #&80000000                 ; must be in range &80..&FF
+        STRCS   R14, [R9, R14, LSR #22]         ; NB bits 23 and 22 are assumed to be zero
+        B       %BT10
+ |
+UpdateVIDCTable ROUT
+        Push    "R14"
+10
+        LDR     R14, [R11], #4
+        CMP     R14, #-1
+        Pull    "PC",EQ,^
+        CMP     R14, #&80000000                 ; must be in range &80..&FF
+  [ VIDC_Type = "VIDC20"
+        STRCS   R14, [R9, R14, LSR #22]         ; NB bits 23 and 22 are assumed to be zero
+  |
+        STRCS   R14, [R9, R14, LSR #24]
+  ]
+        B       %BT10
+ ]
+
+; *****************************************************************************
+;
+;       OfferModeExtension - Issue mode extension service
+;
+; in:   R2 = mode specifier
+;
+; out:  EQ => service claimed, R3 -> VIDC list, R4 -> workspace list
+;       NE => service not claimed, R3,R4 preserved
+;       All other registers preserved
+;
+
+OfferModeExtensionAnyMonitor ROUT
+        MOV     r3, #-1
+OfferModeExtension ROUT
+ [ ModeSelectors
+        Push    "r1,r2,r4,r5,r14"
+
+; TMD 10-Jan-94 - added code here to check for erroneous passing in of a sprite mode word.
+; This prevents data aborts when modules try to index off a bad address.
+;
+; We could have done OS_ValidateAddress, but that would be rather slow, and mode selectors
+; are of indeterminate length.
+;
+; If we detect one of these, we pretend the service wasn't claimed. Hopefully this should
+; ensure that the mode change returns an error.
+
+; Fixes bug MED-00483.
+
+        BICS    r14, r2, #&FF                   ; NE if not a mode number
+        TSTNE   r2, #3                          ; NE if not a mode number, but invalid mode selector
+        Pull    "r1,r2,r4,r5,pc", NE            ; so exit NE, pretending that service not claimed
+
+        GetBandwidthAndSize     r4, r5
+        MOV     r1, #Service_ModeExtension
+        IssueService
+        TEQ     r1, #0                          ; if service claimed
+        CMPNE   r3, #-1                         ; or if "don't care" monitortype
+        BEQ     %FT90                           ; then we can't do any more
+
+        CMP     r2, #&100                       ; if it's a mode selector
+        BCS     %FT90                           ; then we can't help them either
+        BranchIfNotKnownMode r2, %FA90          ; if we don't recognise screen mode number we can't either
+
+; it is a known numbered mode, so create a mode selector on the stack that we can pass to service
+
+        Push    "r6,r7"
+        SUB     sp, sp, #ModeSelector_ModeVars+4        ; make room for block including terminator
+        MOV     r6, #ModeSelectorFlags_ValidFormat
+        STR     r6, [sp, #ModeSelector_Flags]
+        ADRL    r6, FrameRateTable
+        LDRB    r6, [r6, r2]
+        STR     r6, [sp, #ModeSelector_FrameRate]
+
+        ADRL    r6, Vwstab
+        LDR     r14, [r6, r2, LSL #2]
+        ADD     r6, r6, r14
+        LDR     r14, [r6, #wkLog2BPP]
+        STR     r14, [sp, #ModeSelector_PixelDepth]     ; pixdepth = log2bpp
+
+        LDR     r7, [r6, #wkLog2BPC]
+        SUB     r14, r7, r14                            ; r14 = log2bpc-log2bpp
+
+        LDR     r7, [r6, #wkXWindLimit]
+        ADD     r7, r7, #1
+        MOV     r7, r7, LSL r14
+        STR     r7, [sp, #ModeSelector_XRes]
+
+        LDR     r7, [r6, #wkYWindLimit]
+        ADD     r7, r7, #1
+        STR     r7, [sp, #ModeSelector_YRes]
+
+        MOV     r7, #-1
+        STR     r7, [sp, #ModeSelector_ModeVars]
+
+        MOV     r2, sp
+        IssueService
+        TEQ     r1, #0
+        BEQ     %FT10                                   ; service was claimed
+
+; not claimed, so try again with -1 as frame rate
+
+        MOV     r7, #-1
+        STR     r7, [sp, #ModeSelector_FrameRate]
+        IssueService
+        TEQ     r1, #0
+10
+        ADD     sp, sp, #ModeSelector_ModeVars+4        ; junk mode selector
+        Pull    "r6, r7"
+90
+        CMP     r2, #&100                               ; if we started or ended up with a mode selector
+        MOVCS   r4, #0                                  ; then return r4 = 0 (if claimed)
+
+        TEQ     r1, #0
+        STREQ   r4, [sp, #2*4] ; if service claimed, then return r4 from service, else preserve it
+        Pull    "r1,r2,r4,r5,pc"
+ |
+        Push    "r1, lr"
+        MOV     r1, #Service_ModeExtension
+        IssueService
+        TEQ     r1, #0
+        Pull    "r1, pc"
+ ]
+
+; *****************************************************************************
+;
+; ETB - Redefine character
+; ===   & other stuff
+;
+;       VDU 23,0,r,v,0|         Talk to 6845 !
+;       VDU 23,1,n,m,r,g,b|     Program cursor
+;       VDU 23,2,n1..n8         Ecf pattern 1
+;       VDU 23,3,n1..n8         Ecf pattern 2
+;       VDU 23,4,n1..n8         Ecf pattern 3
+;       VDU 23,5,n1..n8         Ecf pattern 4
+;       VDU 23,6,n1..n8         Dot dash line style
+;       VDU 23,7,m,d,z|         Scroll window directly
+;       VDU 23,8,t1,t2,x1,y1,x2,y2|     Clear block
+;       VDU 23,9,n|             Set 1st flash time
+;       VDU 23,10,n|            Set 2nd flash time
+;       VDU 23,11|              Default Ecf patterns
+;       VDU 23,12,n1..n8        Ecf pattern 1 (simple setting)
+;       VDU 23,13,n1..n8        Ecf pattern 2 (simple setting)
+;       VDU 23,14,n1..n8        Ecf pattern 3 (simple setting)
+;       VDU 23,15,n1..n8        Ecf pattern 4 (simple setting)
+;       VDU 23,16,x,y|          Cursor movement control
+;       VDU 23,17,c,t|          Set colour tints, ECF info, char sizes
+;
+;
+ETB
+        LDRB    R0, [WsPtr, #QQ+0]
+        CMP     R0, #32         ; defining a normal character ?
+        BCS     DefineChar
+        LDR     R2, [PC, R0, LSL #2]
+        ADD     PC, PC, R2      ; enters routine with R0 => byte after 23
+
+
+ETBtab
+        &       Vdu23_0-ETBtab-4
+        &       Vdu23_1-ETBtab-4
+        &       ComplexEcfPattern-ETBtab-4
+        &       ComplexEcfPattern-ETBtab-4
+        &       ComplexEcfPattern-ETBtab-4
+        &       ComplexEcfPattern-ETBtab-4
+        &       LineStyle-ETBtab-4
+        &       Vdu23_7-ETBtab-4
+        &       Vdu23_8-ETBtab-4
+        &       Vdu23_9-ETBtab-4
+        &       Vdu23_10-ETBtab-4
+        &       DefaultEcfPattern-ETBtab-4
+        &       SimpleEcfPattern-ETBtab-4
+        &       SimpleEcfPattern-ETBtab-4
+        &       SimpleEcfPattern-ETBtab-4
+        &       SimpleEcfPattern-ETBtab-4
+        &       Vdu23_16-ETBtab-4
+        &       Vdu23_17-ETBtab-4
+        &       Vdu23_18-ETBtab-4
+        &       Vdu23_19-ETBtab-4
+        &       Vdu23_20-ETBtab-4
+        &       Vdu23_21-ETBtab-4
+        &       Vdu23_22-ETBtab-4
+        &       Vdu23_23-ETBtab-4
+        &       Vdu23_24-ETBtab-4
+        &       Vdu23_25-ETBtab-4
+        &       Vdu23_26-ETBtab-4
+        &       Vdu23_27-ETBtab-4
+        &       Vdu23_28-ETBtab-4
+        &       Vdu23_29-ETBtab-4
+        &       Vdu23_30-ETBtab-4
+        &       Vdu23_31-ETBtab-4
+
+; NB All other labels for Vdu23 are in TMD files so I don't have to pester RCM
+
+Vdu23_18
+Vdu23_19
+Vdu23_20
+Vdu23_21
+Vdu23_22
+Vdu23_23
+Vdu23_24
+Vdu23_25
+Vdu23_26
+; Vdu23_27      ; Assigned to Richard (well some of it, anyway)
+Vdu23_28
+Vdu23_29
+Vdu23_30
+Vdu23_31
+UnknownVdu23
+
+; R0 already contains first parameter to VDU23
+
+        MOV     R10, #UKVDU23V
+        Push    "WsPtr, R14"            ; calling a vector corrupts R12
+        BL      VduQQVec                ; so we have to preserve it
+        Pull    "WsPtr, PC", VC         ; before we return to PostWrchCursor
+
+; error in UKVDU23 vector, so go to vdu error exit
+
+        Pull    "WsPtr, R14"
+        B       VduBadExit
+
+; *****************************************************************************
+;
+; DLE
+; CLG - Clear graphics window
+; ===
+;
+; On exit, R0..R11 corrupt
+;
+DLE
+CLG     ROUT
+        GraphicsMode R0
+        MOVNE   PC,LR                   ; check for graphics mode (changed by DDV 15/9/92)
+
+        ADD     R0, WsPtr, #BgEcfOraEor ; point at background colour
+        STR     R0, [WsPtr, #GColAdr]
+        ADD     R11, WsPtr, #GWLCol     ; load window coordinates into
+        LDMIA   R11, {R0-R3}            ; RectFill's parameter space
+
+        LDR     R6, [WsPtr, #CursorFlags] ; if clip box is not enabled
+        TST     R6, #ClipBoxEnableBit   ; then goto code directly
+        BEQ     RectFillA
+
+        Push    R14                     ; else merge graphics window (in R0-R3)
+        BL      MergeClipBox            ; with clip box
+        Pull    R14
+        B       RectFillA
+
+; *****************************************************************************
+;
+; DC2
+; GCol - Set Graphics action and colour
+; ====
+;
+; On entry, R0 holds GCol action
+;           R1 holds GCol colour, 0..127 means program fg
+;                                 128..255 means program bg
+;
+; In 256-colour modes, the extra colours are accessed via TINT
+;
+DC2
+GCol    ROUT
+        LDRB    R0, [WsPtr, #QQ+0]      ; GCol action, eg store, eor etc.
+        LDRB    R1, [WsPtr, #QQ+1]      ; GCol colour
+        LDR     R2, [WsPtr, #NColour]   ; number of colours-1
+        TST     R1, #&80
+        AND     R1, R1, R2              ; limit colour to range available
+
+        STREQ   R0, [WsPtr, #GPLFMD]    ; GCOL(a,0..127) is foreground
+        STREQ   R1, [WsPtr, #GFCOL]
+
+        STRNE   R0, [WsPtr, #GPLBMD]    ; GCOL(a,128..255) is background
+        STRNE   R1, [WsPtr, #GBCOL]
+                                        ; drop into SetColour to....
+
+; SetColour - Setup FgEcf & BgEcf, used after GCOL or setting of Ecfs
+; =========   or default palette (does a gcol).
+
+SetColour
+        Push    R14
+        LDR     R1, [WsPtr, #GPLFMD]    ; setup FgEcf, maybe solid or Ecf
+        LDR     R0, [WsPtr, #GFCOL]
+        ADD     R2, WsPtr, #FgEcf
+        LDR     R3, [WsPtr, #GFTint]    ; tint only used in 256 colour modes
+        ADD     R4, WsPtr, #FgPattern   ; used if OS_SetColour call
+        BL      SetCol10
+
+        LDR     R1, [WsPtr, #GPLBMD]    ; and BgEcf
+        LDR     R0, [WsPtr, #GBCOL]
+        ADD     R2, WsPtr, #BgEcf
+        LDR     R3, [WsPtr, #GBTint]
+        ADD     R4, WsPtr, #BgPattern   ; used if OS_SetColour call
+        BL      SetCol10
+
+        ADD     R0, WsPtr, #FgEcf       ; setup FgEcfOraEor
+        ADD     R1, WsPtr, #BgEcf
+        ADD     R2, WsPtr, #FgEcfOraEor
+        LDR     R3, [WsPtr, #GPLFMD]
+        BL      SetCol60
+
+        ADD     R0, WsPtr, #BgEcf       ; and BgEcfOraEor
+        ADD     R1, WsPtr, #FgEcf
+        ADD     R2, WsPtr, #BgEcfOraEor
+        LDR     R3, [WsPtr, #GPLBMD]
+        BL      SetCol60
+
+        ADD     R0, WsPtr, #BgEcf       ; and BgEcfStore
+        ADD     R1, WsPtr, #FgEcf
+        ADD     R2, WsPtr, #BgEcfStore
+        MOV     R3, #0
+        BL      SetCol60
+
+        Pull    PC
+
+; SetCol10 - Internal to SetColour
+;          Build up an Ecf, given action and colour numbers
+;
+; On entry, R0 holds colour (0..255, where 127..255 means 0..127)
+;           R1 holds action (may indicate ecf)
+;           R2 holds address to write data (FgEcf or BgEcf)
+;           R3 holds tint information (only used in 256 colour modes)
+;           R4 holds pointer to suitable pattern table for OS_SetColour call
+
+SetCol10 ROUT
+        ANDS    R1, R1, #&F0            ; actions >=16 mean Ecf
+        BNE     SetCol30
+
+        Push    R14
+        LDR     R4, [WsPtr, #NColour]   ; else use given colour number
+        AND     R0, R0, R4
+        AND     R0, R0, #63             ; another bodge in the house of bodges
+        TST     R4, #&F0                ; if 256 colour (ie 8bpp)
+        BLNE    AddTintToColour         ; then combine tint with colour
+
+; R0 contains colour number for current mode
+
+        LDR     LR, [WsPtr, #BitsPerPix]
+10
+        TEQ     LR, #32
+        ORRNE   R0, R0, R0, LSL LR      ; replicate again
+        MOVNE   LR, LR, LSL #1          ; doubling the shift for each pass
+        BNE     %BT10
+
+        STR     R0, [R2]
+        STR     R0, [R2, #4]
+        STR     R0, [R2, #8]
+        STR     R0, [R2, #12]
+        STR     R0, [R2, #16]
+        STR     R0, [R2, #20]
+        STR     R0, [R2, #24]
+        STR     R0, [R2, #28]
+
+        Pull    "PC"
+
+; R1 = ecf number as 16,32,48,64,80,96
+; R2 -> destination
+; R4 -> pattern block (if R1 =96!)
+
+SetCol30
+        CMP     R1, #96                 ; special internal plot?
+        MOVCS   R0, R4                  ; yes, so point at pattern to be copied
+        MOVCS   R3, #1                  ; col step =1
+        MOVCS   R4, #0                  ; row step =0 (already there!)
+        BCS     SetCol35
+
+        CMP     R1, #80                 ; 80 => giant ecf (>80 does same)
+        ADDCS   R0, WsPtr, #Ecf1        ; then point at ecf0
+        MOVCS   R3, #8                  ; col step=8
+        MOVCS   R4, #(1-8*4)            ; row step, back to 1st ecf on 1 byte
+        BCS     SetCol35
+
+        ADD     R0, WsPtr, #(Ecf1-8)
+        ADD     R0, R0, R1, LSR #1      ; else point R0 at Ecf1,2,3 or 4
+
+        LDR     R4, [WsPtr, #BitsPerPix] ; if BitsPerPix <> 'fudged' BitsPerPix
+        LDR     R5, [WsPtr, #BytesPerChar]
+        TEQ     R4, R5
+        BNE     SetCol52                ; then double up the pixels
+                                        ; else its a normal Ecf
+        MOV     R3, #0                  ; col step=0, same byte each coloum
+        MOV     R4, #1                  ; row step=1
+SetCol35
+        MOV     R5, #8                  ; do 8 rows
+SetCol40
+        MOV     R6, #4                  ; of 4 columns
+SetCol50
+        LDRB    R7, [R0], R3            ; read from source & move by col step
+        STRB    R7, [R2], #1            ; write to dest, update dest pointer
+        SUBS    R6, R6, #1
+        BNE     SetCol50
+        ADD     R0, R0, R4              ; step source pointer to next row
+        SUBS    R5, R5, #1
+        BNE     SetCol40
+        MOV     PC, R14
+
+; Double up the pixels for Mode2 etc
+;
+; R0 points to Ecf(n)
+; R2 points to destination
+;
+; Uses
+;
+;     R3  - NColour used as PixMsk
+;     R4  - BitsPerPix
+;     R5  - BytesPerChar (unused)
+;     R6  - byte cntr 7..0
+;     R7  - source byte
+;     R8  - ExtrtShftFact
+;     R9  - InsrtShftFact
+;     R10 - result word
+;     R11 - temp
+
+SetCol52
+        LDR     R3, [WsPtr, #NColour]   ; mask for extracting pixels from ecf
+        TST     R3, #&F0                ; ** if 256 colour mode
+        MOVNE   R3, #&FF                ; ** then use &FF (TMD 25-Mar-87)
+        LDR     R4, [WsPtr, #BitsPerPix]
+        MOV     R6, #7                  ; 8 bytes/rows to do
+SetCol54
+        LDRB    R7, [R0, R6]            ; get byte of Ecf(n)
+        RSB     R8, R4, #8
+        RSB     R9, R4, #32
+        MOV     R10, #0                 ; clear result word
+SetCol57
+        AND     R11, R3, R7, ROR R8     ; extract 1 pixel from Ecf
+        ORR     R10, R10, R11, LSL R9   ; double it into result word
+        SUB     R9, R9, R4
+        ORR     R10, R10, R11, LSL R9
+
+        SUB     R8, R8, R4
+        AND     R8, R8, #7
+
+        SUBS    R9, R9, R4
+        BGE     SetCol57                ; process next pixel in result word
+
+        STR     R10, [R2, R6, LSL #2]   ; write expanded word to (Fg/Bg)Ecf
+
+        SUBS    R6, R6, #1
+        BGE     SetCol54                ; process next row/byte
+        MOV     PC, R14
+
+; Tables of full colours for 2,4 & 16 colour modes (256 colour modes
+; use colour number directly).
+;
+; N.B. these are tables of bytes
+
+TBFullCol
+        =       &FF                     ; not used - remove sometime
+                                        ; (DJS comment: don't bother!)
+        =       &00, &FF                ; 2 colour mode
+        =       &00, &55, &AA, &FF      ; 4 colour mode
+
+        =       &FF, &FF, &FF, &FF      ; not used but cannot be removed
+        =       &FF, &FF, &FF, &FF      ; (8 colour mode!)
+
+        =       &00, &11, &22, &33      ; 16 colour mode
+        =       &44, &55, &66, &77
+        =       &88, &99, &AA, &BB
+        =       &CC, &DD, &EE, &FF
+
+        ALIGN
+
+; *****************************************************************************
+;
+;       SetCol60 - Build up an ecf, ORed and EORed appropriate to GCOL action
+;
+;       Internal to SetColour
+;
+; in:   R0 -> ecf colour (FgEcf/BgEcf)
+;       R1 -> transparent colour (BgEcf/FgEcf)
+;       R2 -> destination FgEcfOraEor/BgEcfOraEor/BgEcfStore
+;       R3 = gcol action number (may indicate ecf)
+;
+; uses: R4 = index into ecf (7..0)
+;       R5 = ecf colour word
+;       R6 = transparency mask, set to &FFFFFFFF for NO transparency
+;       R7 -> zgoo..zgee for gcol action
+;       R8 = mask for pixel under examination
+;       R9 = shift factor to move mask to next pixel (BytesPerChar)
+;       R10, R11 temporary
+;
+
+SetCol60 ROUT
+        MOV     R4, #7                          ; 7..0 words to process
+        AND     R3, R3, #&F                     ; extract action bits
+        AND     R11, R3, #7                     ; 0-7 Store etc
+                                                ; 8-15 ditto with transparency
+        MOV     R11, R11, LSL #2                ; 4 bits for each
+        LDR     R7, =TBscrmasks
+        MOV     R7, R7, ROR R11                 ; relevant bits are in top 4
+        AND     R7, R7, #&F0000000              ; isolate these bits (N,Z,C,V)
+SetCol70
+        LDR     R5, [R0, R4, LSL #2]            ; get ecf word
+        TST     R3, #8                          ; if action < 8
+        MOVEQ   R6, #&FFFFFFFF
+        BEQ     SetCol90                        ; then not transparent
+                                                ; else build transparency mask
+        LDR     R8, [WsPtr, #RAMMaskTb]         ; fetch mask for leftmost pixel
+        LDR     R9, [WsPtr, #BytesPerChar]      ; shift factor for next pixel
+        LDR     R6, [R1, R4, LSL #2]            ; get 'transparent' colour
+        EOR     R6, R6, R5
+SetCol80
+        TST     R6, R8                          ; if pixels the same,
+                                                ; then it's transparent
+        ORRNE   R6, R6, R8                      ; else set mask to plot it
+        MOVS    R8, R8, LSL R9
+        BNE     SetCol80
+
+SetCol90
+        TEQP    R7, #SVC_mode                   ; put bits into N, Z, C, V
+                                                ;              OO,EO,OE,EE
+
+        MOVCC   R10, R5                         ; if ORing with &00000000
+        MOVCS   R10, #&FFFFFFFF                 ; if ORing with &FFFFFFFF
+        MVNVS   R10, R10                        ; if EORing with &FFFFFFFF
+
+;       MOVPL   R5, R5                          ; if ORing with &00000000
+        MOVMI   R5, #&FFFFFFFF                  ; if ORing with &FFFFFFFF
+        MVNEQ   R5, R5                          ; if EORing with &FFFFFFFF
+
+; now R5 = OR mask, R10 = EOR mask
+
+        AND     R5, R5, R6                      ; then clear 'transparent'
+        AND     R10, R10, R6                    ; pixels
+
+        LDR     R11, [WsPtr, #ECFShift]
+        MOV     R5, R5, ROR R11                 ; rotate OR and EOR masks
+        MOV     R10, R10, ROR R11               ; to correct for ECF X origin
+
+        LDR     R11, [WsPtr, #ECFYOffset]
+        ADD     R11, R11, R4                    ; add on ECF Y offset
+        AND     R11, R11, #7                    ; and wrap
+
+        ADD     R11, R2, R11, LSL #3
+        STMIA   R11, {R5, R10}                  ; write to (Fg/Bg)EcfOraEor
+
+        SUBS    R4, R4, #1
+        BCS     SetCol70
+
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       AddTintToColour - in 256 colour modes
+;
+;       Internal to SetColour (derived from TMD's FudgeColour)
+;
+; in:   R0 = colour (0..255), where 6 LSBits are used
+;       R3 = tint
+;
+; out:  R0 holds colour byte with tint added
+;       R1-R3 preserved
+;       R4 undefined
+;       PSR preserved
+;
+
+        !       0,"WARNING: AddTintToColour returns > 8 bit values now, check ECF handling!"
+
+AddTintToColour
+        Push    "R3,LR"
+        AND     R0, R0, #63             ; extract suitable set of bits
+        AND     R3, R3, #192            ; and another set
+        ORR     R0, R0, R3
+        BL      ConvertGCOLToColourNumber
+        Pull    "R3,PC"
+
+; *****************************************************************************
+;
+;       CAN - Define graphics window
+;
+;       External routine
+;
+; in:   The window is given by bytes in the vdu queue, as follows :-
+;         QQ+0 = leftLo
+;         QQ+1 = leftHi
+;         QQ+2 = bottomLo
+;         QQ+3 = bottomHi
+;         QQ+4 = rightLo
+;         QQ+5 = rightHi
+;         QQ+6 = topLo
+;         QQ+7 = topHi
+;
+;       These are relative to the current graphics origin.
+;       The resultant window must obey the following rules :-
+;         RCol >= LCol
+;         TRow >= BRow
+;         LCol >= 0
+;         BRow >= 0
+;         YWindLimit >= TRow
+;         XWindLimit >= RCol
+;
+
+CAN     ROUT
+        Push    R14
+        ADD     R8, WsPtr, #GCsX        ; save ECursor away, cos EIG changes it
+        LDMIA   R8, {R6, R7}            ; and we don't want it to!
+
+; *****Change made by DJS
+; Original code was:
+;        LDRB    R0, [WsPtr, #QQ+5]      ; rightHi
+;        LDRB    R1, [WsPtr, #QQ+4]      ; rightLo
+;        PackXtnd R0,R0,R1               ; pack 2 bytes and sign extend
+;
+;        LDRB    R1, [WsPtr, #QQ+7]      ; topHi
+;        LDRB    R2, [WsPtr, #QQ+6]      ; topLo
+;        PackXtnd R1,R1,R2               ; pack 2 bytes and sign extend
+
+        LoadCoordPair R0, R1, WsPtr, QQ+4 ;Get top right point
+
+; *****End of change made by DJS
+
+        MOV     R2, #&FF                ; convert external-to-internal
+        BL      EIG                     ; as absolute coordinates
+
+        MOV     R4, R0                  ; move internal version of top right
+        MOV     R5, R1                  ; out of harm's way
+
+; *****Change made by DJS
+; Original code was:
+;        LDRB    R0, [WsPtr, #QQ+1]      ; leftHi
+;        LDRB    R1, [WsPtr, #QQ+0]      ; leftLo
+;        PackXtnd R0,R0,R1               ; pack 2 bytes and sign extend
+;
+;        LDRB    R1, [WsPtr, #QQ+3]      ; bottomHi
+;        LDRB    R2, [WsPtr, #QQ+2]      ; bottomLo
+;        PackXtnd R1,R1,R2               ; pack 2 bytes and sign extend
+
+        LoadCoordPair R0, R1, WsPtr, QQ+0 ;Get bottom left point
+
+; *****End of change made by DJS
+
+        MOV     R2, #&FF                ; convert external-to-internal
+        BL      EIG                     ; as absolute coordinates
+
+; For a valid window, the following must be true
+
+        CMP     R4, R0                          ;  RCol >= LCol
+        CMPGE   R5, R1                          ;  TRow >= BRow
+        CMPGE   R0, #0                          ;  LCol >= 0
+        CMPGE   R1, #0                          ;  BRow >= 0
+        LDRGE   R2, [WsPtr, #YWindLimit]        ;  YWindLimit >= TRow
+        CMPGE   R2, R5
+        LDRGE   R2, [WsPtr, #XWindLimit]        ;  XWindLimit >= RCol
+        CMPGE   R2, R4
+
+        ADD     R2, WsPtr, #GWLCol
+        STMGEIA R2, {R0,R1, R4,R5}      ; if the new window is OK, update it
+
+        STMIA   R8, {R6, R7}            ; restore ECursor (EIG corrupted it)
+        Pull    PC
+
+; *****************************************************************************
+;
+;       DefaultWindows - Restore default windows
+;
+;       External routine, and called by mode change + switch output to sprite
+;
+;       Set default text and graphics windows,
+;       Clear graphics origin and both cursors
+;
+
+DefaultWindows ROUT
+        Push    R14
+        MOV     R0, #0
+        MOV     R1, #0
+        ADD     R4, WsPtr, #GWLCol
+
+        ASSERT  YWindLimit = XWindLimit +4
+
+        ADD     R2, WsPtr, #XWindLimit
+        LDMIA   R2, {R2,R3}     ; R2 := XWindLimit; R3 := YWindLimit
+        STMIA   R4, {R0-R3}     ; zero GWLCol, GWBRow
+                                ; GWRCol:=XWindLimit; GWTRow:=YWindLimit
+        MOV     R3, #0
+        LDR     R1, [WsPtr, #ScrBRow]
+        LDR     R2, [WsPtr, #ScrRCol]
+        ADD     R4, WsPtr, #TWLCol      ; zero TWLCol, TWTRow
+        STMIA   R4!, {R0-R3}            ; TWRCol := ScrRCol; TWBRow := ScrBRow
+
+        MOV     R1, #0
+        MOV     R2, #0
+        STMIA   R4!, {R0-R3}    ; zero OrgX, OrgY, GCsX, GCsY
+        STMIA   R4!, {R0-R3}    ; zero OlderCsX, OlderCsY, OldCsX, OldCsY
+        STMIA   R4!, {R0-R3}    ; zero GCsIX, GCsIY, NewPtX, NewPtY
+
+        LDR     R0, [WsPtr, #ModeFlags]
+        TST     R0, #Flag_HardScrollDisabled
+
+        LDR     R0, [WsPtr, #VduStatus]         ; if not outputting to sprite
+        BICEQ   R0, R0, #Windowing              ; then indicate no text window
+        ORRNE   R0, R0, #Windowing              ; else indicate is text window
+        STR     R0, [WsPtr, #VduStatus]
+
+        BL      HomeVdu4                        ; home TEXT cursor
+                                                ; (even in VDU 5 mode)
+        Pull    PC
+
+; *****************************************************************************
+;
+;       GS - Define graphics origin
+;
+;       External routine
+;
+; in:   The origin is given by bytes in the vdu queue, as follows :-
+;         QQ+0 = xLo
+;         QQ+1 = xHi
+;         QQ+2 = yLo
+;         QQ+3 = yHi
+;
+;       The coordinates are in external 16 bit form.
+;       This does not move the windows, but does move the graphics cursor
+;
+
+GS      ROUT
+        Push    R14
+
+; *****Change made by DJS
+; Original code was:
+;        LDRB    R0, [WsPtr, #QQ+1]      ; xHi
+;        LDRB    R1, [WsPtr, #QQ+0]      ; xLo
+;        PackXtnd R0,R0,R1               ; pack 2 bytes and sign extend
+;        LDRB    R1, [WsPtr, #QQ+3]      ; yHi
+;        LDRB    R2, [WsPtr, #QQ+2]      ; yLo
+;        PackXtnd R1,R1,R2               ; pack 2 bytes and sign extend
+
+        LoadCoordPair R0, R1, WsPtr, QQ+0
+
+; *****End of change made by DJS
+
+        ADD     R2, WsPtr, #OrgX
+        STMIA   R2, {R0,R1}             ; write the new origin
+        BL      IEG                     ; update external cursor
+        Pull    PC
+
+        LTORG
+
+;------------------------------------------------------------------------------
+;
+; OS_SetColour implementation
+; ------------
+;
+; This call can be used to change the current GCOL/pattern table used for
+; plotting with the VDU primitives.
+;
+; in    R0 = flags / logical operation
+;               bit 0-3 = logical operation
+;               bit 4   = set => bg, else fg flag
+;               bit 5   = set => pattern block supplied
+;               bit 6   = set => set text colour
+;               bit 7   = set => read colour
+;       R1 = colour number / -> pattern block to use
+; out   -
+;
+;------------------------------------------------------------------------------
+
+setcol_LogicOpMask      * &0F
+setcol_FgBgFlag         * &10
+setcol_PatternFlag      * &20
+setcol_TextColour       * &40
+setcol_ReadFlag         * &80
+
+        ASSERT  WsPtr > 9
+
+SWISetColour ROUT
+
+        Push    "R0-R9,WsPtr,R14"
+
+        VDWS    WsPtr                           ; Obtain base of the VDU driver workspace
+
+        TST     R0, #setcol_ReadFlag            ; Are we reading?
+        BNE     %FT75
+
+        TST     R0, #setcol_TextColour          ; Are we changing the text colour?
+        BNE     %FT70
+
+        AND     R2, R0, #setcol_LogicOpMask     ; Get the logical operation
+        ORR     R2, R2, #&60                    ; Mark as being a special kind of pattern
+
+        TST     R0, #setcol_FgBgFlag
+        STREQ   R2, [WsPtr, #GPLFMD]            ; Store the relevant logical operation away for fg/bg
+        STRNE   R2, [WsPtr, #GPLBMD]
+        ADDEQ   R3, WsPtr, #FgPattern
+        ADDNE   R3, WsPtr, #BgPattern           ; Setup the pointer to a store for the pattern
+
+        TST     R0, #setcol_PatternFlag         ; Did the caller specify a pattern block?
+        BNE     %FT50                           ; Yes so don't try to expand colour value to pattern
+
+        MOV     R2, #1
+        LDR     R4, [WsPtr, #Log2BPP]           ; Get the Log2 depth of the mode
+        MOV     R4, R2, ASL R4                  ; R4 = bits per pixel
+        RSB     R2, R2, R2, LSL R4              ; Get a mask to extract only meaningful bits from word
+        AND     R1, R1, R2                      ; Extract bits suitable for this depth of mode
+10
+        TEQ     R4, #32                         ; Do we need to do any more replication
+        ORRNE   R1, R1, R1, LSL R4              ; Yes so or word with itself shifted
+        MOVNE   R4, R4, LSL #1                  ; and double amount to shift next time
+        BNE     %BT10
+
+        MOV     R2, #8
+20
+        STR     R1, [R3], #4                    ; now copy word 8 times into block
+        SUBS    R2, R2, #1
+        BNE     %BT20
+        B       %FT60
+
+50
+        LDMIA   R1,{R0,R2,R4-R9}
+        STMIA   R3,{R0,R2,R4-R9}                ; Copy the pattern into the buffer (assumes word aligned)
+60
+        BL      SetColour                       ; And then setup the internal GCOL tables
+65
+        Pull    "R0-R9,WsPtr,R14"
+        ExitSWIHandler
+70
+        TST     R0, #setcol_FgBgFlag            ; Store the foreground or background colour?
+        STREQ   R1, [WsPtr, #TextFgColour]
+        STRNE   R1, [WsPtr, #TextBgColour]
+
+        LDR     R0, [WsPtr, #CursorFlags]       ; Indicate the text colour needs re-computing!
+        ORR     R0, R0, #TEUpdate
+        STR     R0, [WsPtr, #CursorFlags]
+
+        B       %BT65                           ; Leave gracefully ....
+
+75
+        ; Reading the colour...
+        TST     R0, #setcol_TextColour
+        BEQ     %FT80
+
+        ; Reading text colour
+        TST     R0, #setcol_FgBgFlag
+        LDREQ   R1, [WsPtr, #TextFgColour]
+        LDRNE   R1, [WsPtr, #TextBgColour]
+        BIC     R0, R0, #setcol_PatternFlag :OR: setcol_ReadFlag
+
+77
+        ; Standard exit for reading the colour
+        STMIA   SP, {R0,R1}
+        B       %BT65
+
+80
+        ; Reading graphics colour
+        TST     R0, #setcol_FgBgFlag
+        LDREQ   R2, [WsPtr, #GPLFMD]            ; Get the relevant logical operation for fg/bg
+        LDRNE   R2, [WsPtr, #GPLBMD]
+
+        ; SetColour setting - copy block
+        ADDEQ   R3, WsPtr, #FgEcf
+        ADDNE   R3, WsPtr, #BgEcf
+
+        ; Copy the pattern to the user's buffer
+        LDMIA   R3,{R0,R3,R4-R9}
+        STMIA   R1,{R0,R3,R4-R9}
+
+        ; Construct a suitable reason code
+        AND     R0, R2, #setcol_LogicOpMask
+        ORRNE   R0, R0, #setcol_FgBgFlag
+        ORR     R0, R0, #setcol_PatternFlag
+        B       %BT77
+
+        END
diff --git a/s/vdu/vdufont b/s/vdu/vdufont
new file mode 100644
index 0000000000000000000000000000000000000000..5d14964a7be3ec1b86309c3778d5069b7e67686a
--- /dev/null
+++ b/s/vdu/vdufont
@@ -0,0 +1,243 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduFont
+
+HardFont
+        = &00, &00, &00, &00, &00, &00, &00, &00
+        = &18, &18, &18, &18, &18, &00, &18, &00
+        = &6C, &6C, &6C, &00, &00, &00, &00, &00
+        = &36, &36, &7F, &36, &7F, &36, &36, &00
+        = &0C, &3F, &68, &3E, &0B, &7E, &18, &00
+        = &60, &66, &0C, &18, &30, &66, &06, &00
+        = &38, &6C, &6C, &38, &6D, &66, &3B, &00
+        = &0C, &18, &30, &00, &00, &00, &00, &00
+        = &0C, &18, &30, &30, &30, &18, &0C, &00
+        = &30, &18, &0C, &0C, &0C, &18, &30, &00
+        = &00, &18, &7E, &3C, &7E, &18, &00, &00
+        = &00, &18, &18, &7E, &18, &18, &00, &00
+        = &00, &00, &00, &00, &00, &18, &18, &30
+        = &00, &00, &00, &7E, &00, &00, &00, &00
+        = &00, &00, &00, &00, &00, &18, &18, &00
+        = &00, &06, &0C, &18, &30, &60, &00, &00
+        = &3C, &66, &6E, &7E, &76, &66, &3C, &00
+        = &18, &38, &18, &18, &18, &18, &7E, &00
+        = &3C, &66, &06, &0C, &18, &30, &7E, &00
+        = &3C, &66, &06, &1C, &06, &66, &3C, &00
+        = &0C, &1C, &3C, &6C, &7E, &0C, &0C, &00
+        = &7E, &60, &7C, &06, &06, &66, &3C, &00
+        = &1C, &30, &60, &7C, &66, &66, &3C, &00
+        = &7E, &06, &0C, &18, &30, &30, &30, &00
+        = &3C, &66, &66, &3C, &66, &66, &3C, &00
+        = &3C, &66, &66, &3E, &06, &0C, &38, &00
+        = &00, &00, &18, &18, &00, &18, &18, &00
+        = &00, &00, &18, &18, &00, &18, &18, &30
+        = &0C, &18, &30, &60, &30, &18, &0C, &00
+        = &00, &00, &7E, &00, &7E, &00, &00, &00
+        = &30, &18, &0C, &06, &0C, &18, &30, &00
+        = &3C, &66, &0C, &18, &18, &00, &18, &00
+        = &3C, &66, &6E, &6A, &6E, &60, &3C, &00
+        = &3C, &66, &66, &7E, &66, &66, &66, &00
+        = &7C, &66, &66, &7C, &66, &66, &7C, &00
+        = &3C, &66, &60, &60, &60, &66, &3C, &00
+        = &78, &6C, &66, &66, &66, &6C, &78, &00
+        = &7E, &60, &60, &7C, &60, &60, &7E, &00
+        = &7E, &60, &60, &7C, &60, &60, &60, &00
+        = &3C, &66, &60, &6E, &66, &66, &3C, &00
+        = &66, &66, &66, &7E, &66, &66, &66, &00
+        = &7E, &18, &18, &18, &18, &18, &7E, &00
+        = &3E, &0C, &0C, &0C, &0C, &6C, &38, &00
+        = &66, &6C, &78, &70, &78, &6C, &66, &00
+        = &60, &60, &60, &60, &60, &60, &7E, &00
+        = &63, &77, &7F, &6B, &6B, &63, &63, &00
+        = &66, &66, &76, &7E, &6E, &66, &66, &00
+        = &3C, &66, &66, &66, &66, &66, &3C, &00
+        = &7C, &66, &66, &7C, &60, &60, &60, &00
+        = &3C, &66, &66, &66, &6A, &6C, &36, &00
+        = &7C, &66, &66, &7C, &6C, &66, &66, &00
+        = &3C, &66, &60, &3C, &06, &66, &3C, &00
+        = &7E, &18, &18, &18, &18, &18, &18, &00
+        = &66, &66, &66, &66, &66, &66, &3C, &00
+        = &66, &66, &66, &66, &66, &3C, &18, &00
+        = &63, &63, &6B, &6B, &7F, &77, &63, &00
+        = &66, &66, &3C, &18, &3C, &66, &66, &00
+        = &66, &66, &66, &3C, &18, &18, &18, &00
+        = &7E, &06, &0C, &18, &30, &60, &7E, &00
+        = &7C, &60, &60, &60, &60, &60, &7C, &00
+        = &00, &60, &30, &18, &0C, &06, &00, &00
+        = &3E, &06, &06, &06, &06, &06, &3E, &00
+        = &18, &3C, &66, &42, &00, &00, &00, &00
+        = &00, &00, &00, &00, &00, &00, &00, &FF
+        = &1C, &36, &30, &7C, &30, &30, &7E, &00
+        = &00, &00, &3C, &06, &3E, &66, &3E, &00
+        = &60, &60, &7C, &66, &66, &66, &7C, &00
+        = &00, &00, &3C, &66, &60, &66, &3C, &00
+        = &06, &06, &3E, &66, &66, &66, &3E, &00
+        = &00, &00, &3C, &66, &7E, &60, &3C, &00
+        = &1C, &30, &30, &7C, &30, &30, &30, &00
+        = &00, &00, &3E, &66, &66, &3E, &06, &3C
+        = &60, &60, &7C, &66, &66, &66, &66, &00
+        = &18, &00, &38, &18, &18, &18, &3C, &00
+        = &18, &00, &38, &18, &18, &18, &18, &70
+        = &60, &60, &66, &6C, &78, &6C, &66, &00
+        = &38, &18, &18, &18, &18, &18, &3C, &00
+        = &00, &00, &36, &7F, &6B, &6B, &63, &00
+        = &00, &00, &7C, &66, &66, &66, &66, &00
+        = &00, &00, &3C, &66, &66, &66, &3C, &00
+        = &00, &00, &7C, &66, &66, &7C, &60, &60
+        = &00, &00, &3E, &66, &66, &3E, &06, &07
+        = &00, &00, &6C, &76, &60, &60, &60, &00
+        = &00, &00, &3E, &60, &3C, &06, &7C, &00
+        = &30, &30, &7C, &30, &30, &30, &1C, &00
+        = &00, &00, &66, &66, &66, &66, &3E, &00
+        = &00, &00, &66, &66, &66, &3C, &18, &00
+        = &00, &00, &63, &6B, &6B, &7F, &36, &00
+        = &00, &00, &66, &3C, &18, &3C, &66, &00
+        = &00, &00, &66, &66, &66, &3E, &06, &3C
+        = &00, &00, &7E, &0C, &18, &30, &7E, &00
+        = &0C, &18, &18, &70, &18, &18, &0C, &00
+        = &18, &18, &18, &00, &18, &18, &18, &00
+        = &30, &18, &18, &0E, &18, &18, &30, &00
+        = &31, &6B, &46, &00, &00, &00, &00, &00
+        = &FF, &FF, &FF, &FF, &FF, &FF, &FF, &FF
+        = &66, &00, &3C, &66, &7E, &66, &66, &00
+        = &3C, &66, &3C, &66, &7E, &66, &66, &00
+        = &3F, &66, &66, &7F, &66, &66, &67, &00
+        = &3C, &66, &60, &60, &60, &66, &3C, &60
+        = &0C, &18, &7E, &60, &7C, &60, &7E, &00
+        = &66, &3C, &66, &66, &66, &66, &3C, &00
+        = &66, &00, &66, &66, &66, &66, &3C, &00
+        = &7E, &C3, &9D, &B1, &9D, &C3, &7E, &00
+        = &00, &18, &38, &7F, &38, &18, &00, &00
+        = &00, &18, &1C, &FE, &1C, &18, &00, &00
+        = &18, &18, &18, &18, &7E, &3C, &18, &00
+        = &00, &18, &3C, &7E, &18, &18, &18, &18
+        = &30, &18, &3C, &06, &3E, &66, &3E, &00
+        = &30, &18, &3C, &66, &7E, &60, &3C, &00
+        = &66, &00, &3C, &66, &7E, &60, &3C, &00
+        = &3C, &66, &3C, &66, &7E, &60, &3C, &00
+        = &66, &00, &3C, &06, &3E, &66, &3E, &00
+        = &3C, &66, &3C, &06, &3E, &66, &3E, &00
+        = &00, &00, &3F, &0D, &3F, &6C, &3F, &00
+        = &00, &00, &3C, &66, &60, &66, &3C, &60
+        = &0C, &18, &3C, &66, &7E, &60, &3C, &00
+        = &66, &00, &3C, &66, &66, &66, &3C, &00
+        = &66, &00, &66, &66, &66, &66, &3E, &00
+        = &30, &18, &00, &38, &18, &18, &3C, &00
+        = &3C, &66, &00, &38, &18, &18, &3C, &00
+        = &30, &18, &00, &3C, &66, &66, &3C, &00
+        = &3C, &66, &00, &3C, &66, &66, &3C, &00
+        = &30, &18, &00, &66, &66, &66, &3E, &00
+        = &3C, &66, &00, &66, &66, &66, &3E, &00
+        = &66, &00, &66, &66, &66, &3E, &06, &3C
+        = &00, &66, &3C, &66, &66, &3C, &66, &00
+        = &3C, &60, &3C, &66, &3C, &06, &3C, &00
+        = &3C, &66, &3C, &00, &00, &00, &00, &00
+        = &00, &00, &00, &18, &18, &18, &18, &18
+        = &00, &00, &00, &1F, &00, &00, &00, &00
+        = &00, &00, &00, &1F, &18, &18, &18, &18
+        = &00, &00, &00, &F8, &00, &00, &00, &00
+        = &00, &00, &00, &F8, &18, &18, &18, &18
+        = &00, &00, &00, &FF, &00, &00, &00, &00
+        = &00, &00, &00, &FF, &18, &18, &18, &18
+        = &18, &18, &18, &18, &00, &00, &00, &00
+        = &18, &18, &18, &18, &18, &18, &18, &18
+        = &18, &18, &18, &1F, &00, &00, &00, &00
+        = &18, &18, &18, &1F, &18, &18, &18, &18
+        = &18, &18, &18, &F8, &00, &00, &00, &00
+        = &18, &18, &18, &F8, &18, &18, &18, &18
+        = &18, &18, &18, &FF, &00, &00, &00, &00
+        = &18, &18, &18, &FF, &18, &18, &18, &18
+        = &00, &00, &00, &07, &0C, &18, &18, &18
+        = &00, &00, &00, &E0, &30, &18, &18, &18
+        = &18, &18, &0C, &07, &00, &00, &00, &00
+        = &18, &18, &30, &E0, &00, &00, &00, &00
+        = &18, &00, &18, &18, &30, &66, &3C, &00
+        = &18, &00, &18, &18, &18, &18, &18, &00
+        = &36, &6C, &00, &66, &76, &6E, &66, &00
+        = &36, &6C, &00, &7C, &66, &66, &66, &00
+        = &18, &7E, &18, &18, &18, &18, &18, &00
+        = &18, &7E, &18, &18, &18, &7E, &18, &00
+        = &18, &18, &18, &00, &00, &00, &00, &00
+        = &30, &18, &0C, &00, &00, &00, &00, &00
+        = &3F, &7B, &7B, &3B, &1B, &1B, &1F, &00
+        = &00, &00, &00, &18, &18, &00, &00, &00
+        = &03, &03, &06, &06, &76, &1C, &0C, &00
+        = &AA, &55, &AA, &55, &AA, &55, &AA, &55
+        = &3E, &63, &67, &6B, &73, &63, &3E, &00
+        = &1C, &36, &63, &63, &7F, &63, &63, &00
+        = &7E, &33, &33, &3E, &33, &33, &7E, &00
+        = &7F, &63, &60, &60, &60, &60, &60, &00
+        = &1C, &1C, &36, &36, &63, &63, &7F, &00
+        = &7F, &33, &30, &3E, &30, &33, &7F, &00
+        = &7E, &66, &0C, &18, &30, &66, &7E, &00
+        = &77, &33, &33, &3F, &33, &33, &77, &00
+        = &3E, &63, &63, &7F, &63, &63, &3E, &00
+        = &3C, &18, &18, &18, &18, &18, &3C, &00
+        = &63, &66, &6C, &78, &6C, &66, &63, &00
+        = &1C, &1C, &36, &36, &63, &63, &63, &00
+        = &63, &77, &7F, &6B, &63, &63, &63, &00
+        = &63, &73, &7B, &6F, &67, &63, &63, &00
+        = &7E, &00, &00, &3C, &00, &00, &7E, &00
+        = &3E, &63, &63, &63, &63, &63, &3E, &00
+        = &7F, &36, &36, &36, &36, &36, &36, &00
+        = &7E, &33, &33, &3E, &30, &30, &78, &00
+        = &7F, &63, &30, &18, &30, &63, &7F, &00
+        = &7E, &5A, &18, &18, &18, &18, &18, &00
+        = &66, &66, &66, &3C, &18, &18, &3C, &00
+        = &3E, &08, &3E, &6B, &3E, &08, &3E, &00
+        = &63, &63, &36, &1C, &36, &63, &63, &00
+        = &3E, &08, &6B, &6B, &3E, &08, &3E, &00
+        = &3E, &63, &63, &63, &36, &36, &63, &00
+        = &7F, &63, &63, &36, &36, &1C, &1C, &00
+        = &18, &18, &7E, &18, &18, &00, &7E, &00
+        = &00, &7E, &00, &18, &18, &7E, &18, &18
+        = &18, &18, &18, &18, &18, &18, &18, &00
+        = &36, &36, &36, &36, &36, &36, &36, &00
+        = &00, &66, &66, &66, &66, &66, &3C, &00
+        = &00, &3C, &66, &66, &66, &66, &66, &00
+        = &00, &03, &3E, &67, &6B, &73, &3E, &60
+        = &00, &00, &3B, &6E, &66, &6E, &3B, &00
+        = &1E, &33, &33, &3E, &33, &33, &3E, &60
+        = &00, &00, &66, &36, &1C, &18, &30, &30
+        = &3C, &60, &30, &3C, &66, &66, &3C, &00
+        = &00, &00, &1E, &30, &1C, &30, &1E, &00
+        = &3E, &0C, &18, &30, &60, &60, &3E, &06
+        = &00, &00, &7C, &66, &66, &66, &06, &06
+        = &3C, &66, &66, &7E, &66, &66, &3C, &00
+        = &00, &00, &18, &18, &18, &18, &0C, &00
+        = &00, &00, &66, &6C, &78, &6C, &66, &00
+        = &60, &30, &18, &1C, &36, &63, &63, &00
+        = &00, &00, &33, &33, &33, &33, &3E, &60
+        = &00, &00, &63, &33, &1B, &1E, &1C, &00
+        = &3C, &60, &60, &3C, &60, &60, &3E, &06
+        = &00, &00, &3E, &63, &63, &63, &3E, &00
+        = &00, &00, &7F, &36, &36, &36, &36, &00
+        = &00, &00, &3C, &66, &66, &7C, &60, &60
+        = &00, &00, &3F, &66, &66, &66, &3C, &00
+        = &00, &00, &7E, &18, &18, &18, &0C, &00
+        = &00, &00, &73, &33, &33, &33, &1E, &00
+        = &00, &00, &3E, &6B, &6B, &3E, &18, &18
+        = &00, &00, &66, &36, &1C, &1C, &36, &33
+        = &00, &00, &63, &6B, &6B, &3E, &18, &18
+        = &00, &00, &36, &63, &6B, &7F, &36, &00
+        = &38, &0C, &06, &3E, &66, &66, &3C, &00
+        = &00, &31, &6B, &46, &00, &7F, &00, &00
+        = &00, &7E, &00, &7E, &00, &7E, &00, &00
+        = &07, &1C, &70, &1C, &07, &00, &7F, &00
+        = &06, &0C, &7E, &18, &7E, &30, &60, &00
+        = &70, &1C, &07, &1C, &70, &00, &7F, &00
+        = &FF, &FF, &FF, &FF, &FF, &FF, &FF, &FF
+
+        END
diff --git a/s/vdu/vdufontl1 b/s/vdu/vdufontl1
new file mode 100644
index 0000000000000000000000000000000000000000..3abae2186b0a4ff35ea218bb699dac53425f45c3
--- /dev/null
+++ b/s/vdu/vdufontl1
@@ -0,0 +1,258 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduFontL1
+
+HardFont
+        = &00,&00,&00,&00,&00,&00,&00,&00 ;ISO  "space"
+        = &18,&18,&18,&18,&18,&00,&18,&00 ;ISO  "exclamation mark"
+        = &6C,&6C,&6C,&00,&00,&00,&00,&00 ;ISO  "quotation mark"
+        = &36,&36,&7F,&36,&7F,&36,&36,&00 ;ISO  "number sign (hash)"
+        = &0C,&3F,&68,&3E,&0B,&7E,&18,&00 ;ISO  "dollar sign"
+        = &60,&66,&0C,&18,&30,&66,&06,&00 ;ISO  "percent sign"
+        = &38,&6C,&6C,&38,&6D,&66,&3B,&00 ;ISO  "ampersand"
+        = &18,&18,&18,&00,&00,&00,&00,&00 ;ISO  "apostrophe" changed 09-Sep-88
+        = &0C,&18,&30,&30,&30,&18,&0C,&00 ;ISO  "left parenthesis"
+        = &30,&18,&0C,&0C,&0C,&18,&30,&00 ;ISO  "right parenthesis"
+        = &00,&18,&7E,&3C,&7E,&18,&00,&00 ;ISO  "asterisk"
+        = &00,&18,&18,&7E,&18,&18,&00,&00 ;ISO  "plus sign"
+        = &00,&00,&00,&00,&00,&18,&18,&30 ;ISO  "comma"
+        = &00,&00,&00,&7E,&00,&00,&00,&00 ;ISO  "hyphen or minus sign"
+        = &00,&00,&00,&00,&00,&18,&18,&00 ;ISO  "full stop, period"
+        = &00,&06,&0C,&18,&30,&60,&00,&00 ;ISO  "solidus"
+        = &3C,&66,&6E,&7E,&76,&66,&3C,&00 ;ISO  "digit zero"
+        = &18,&38,&18,&18,&18,&18,&7E,&00 ;ISO  "digit one"
+        = &3C,&66,&06,&0C,&18,&30,&7E,&00 ;ISO  "digit two"
+        = &3C,&66,&06,&1C,&06,&66,&3C,&00 ;ISO  "digit three"
+        = &0C,&1C,&3C,&6C,&7E,&0C,&0C,&00 ;ISO  "digit four"
+        = &7E,&60,&7C,&06,&06,&66,&3C,&00 ;ISO  "digit five"
+        = &1C,&30,&60,&7C,&66,&66,&3C,&00 ;ISO  "digit six"
+        = &7E,&06,&0C,&18,&30,&30,&30,&00 ;ISO  "digit seven"
+        = &3C,&66,&66,&3C,&66,&66,&3C,&00 ;ISO  "digit eight"
+        = &3C,&66,&66,&3E,&06,&0C,&38,&00 ;ISO  "digit nine"
+        = &00,&00,&18,&18,&00,&18,&18,&00 ;ISO  "colon"
+        = &00,&00,&18,&18,&00,&18,&18,&30 ;ISO  "semi-colon"
+        = &0C,&18,&30,&60,&30,&18,&0C,&00 ;ISO  "less-than sign"
+        = &00,&00,&7E,&00,&7E,&00,&00,&00 ;ISO  "equals sign"
+        = &30,&18,&0C,&06,&0C,&18,&30,&00 ;ISO  "greater-than sign"
+        = &3C,&66,&0C,&18,&18,&00,&18,&00 ;ISO  "question mark"
+        = &3C,&66,&6E,&6A,&6E,&60,&3C,&00 ;ISO  "commercial at"
+        = &3C,&66,&66,&7E,&66,&66,&66,&00 ;ISO  "capital A"
+        = &7C,&66,&66,&7C,&66,&66,&7C,&00 ;ISO  "capital B"
+        = &3C,&66,&60,&60,&60,&66,&3C,&00 ;ISO  "capital C"
+        = &78,&6C,&66,&66,&66,&6C,&78,&00 ;ISO  "capital D"
+        = &7E,&60,&60,&7C,&60,&60,&7E,&00 ;ISO  "capital E"
+        = &7E,&60,&60,&7C,&60,&60,&60,&00 ;ISO  "capital F"
+        = &3C,&66,&60,&6E,&66,&66,&3C,&00 ;ISO  "capital G"
+        = &66,&66,&66,&7E,&66,&66,&66,&00 ;ISO  "capital H"
+        = &7E,&18,&18,&18,&18,&18,&7E,&00 ;ISO  "capital I"
+        = &3E,&0C,&0C,&0C,&0C,&6C,&38,&00 ;ISO  "capital J"
+        = &66,&6C,&78,&70,&78,&6C,&66,&00 ;ISO  "capital K"
+        = &60,&60,&60,&60,&60,&60,&7E,&00 ;ISO  "capital L"
+        = &63,&77,&7F,&6B,&6B,&63,&63,&00 ;ISO  "capital M"
+        = &66,&66,&76,&7E,&6E,&66,&66,&00 ;ISO  "capital N"
+        = &3C,&66,&66,&66,&66,&66,&3C,&00 ;ISO  "capital O"
+        = &7C,&66,&66,&7C,&60,&60,&60,&00 ;ISO  "capital P"
+        = &3C,&66,&66,&66,&6A,&6C,&36,&00 ;ISO  "capital Q"
+        = &7C,&66,&66,&7C,&6C,&66,&66,&00 ;ISO  "capital R"
+        = &3C,&66,&60,&3C,&06,&66,&3C,&00 ;ISO  "capital S"
+        = &7E,&18,&18,&18,&18,&18,&18,&00 ;ISO  "capital T"
+        = &66,&66,&66,&66,&66,&66,&3C,&00 ;ISO  "capital U"
+        = &66,&66,&66,&66,&66,&3C,&18,&00 ;ISO  "capital V"
+        = &63,&63,&6B,&6B,&7F,&77,&63,&00 ;ISO  "capital W"
+        = &66,&66,&3C,&18,&3C,&66,&66,&00 ;ISO  "capital X"
+        = &66,&66,&66,&3C,&18,&18,&18,&00 ;ISO  "capital Y"
+        = &7E,&06,&0C,&18,&30,&60,&7E,&00 ;ISO  "capital Z"
+        = &7C,&60,&60,&60,&60,&60,&7C,&00 ;ISO  "left square bracket"
+        = &00,&60,&30,&18,&0C,&06,&00,&00 ;ISO  "reverse solidus"
+        = &3E,&06,&06,&06,&06,&06,&3E,&00 ;ISO  "right square bracket"
+        = &3C,&66,&00,&00,&00,&00,&00,&00 ;ISO  "circumflex accent"
+        = &00,&00,&00,&00,&00,&00,&00,&FF ;ISO  "low line"
+        = &30,&18,&00,&00,&00,&00,&00,&00 ;ISO  "grave accent"
+        = &00,&00,&3C,&06,&3E,&66,&3E,&00 ;ISO  "small a"
+        = &60,&60,&7C,&66,&66,&66,&7C,&00 ;ISO  "small b"
+        = &00,&00,&3C,&66,&60,&66,&3C,&00 ;ISO  "small c"
+        = &06,&06,&3E,&66,&66,&66,&3E,&00 ;ISO  "small d"
+        = &00,&00,&3C,&66,&7E,&60,&3C,&00 ;ISO  "small e"
+        = &1C,&30,&30,&7C,&30,&30,&30,&00 ;ISO  "small f"
+        = &00,&00,&3E,&66,&66,&3E,&06,&3C ;ISO  "small g"
+        = &60,&60,&7C,&66,&66,&66,&66,&00 ;ISO  "small h"
+        = &18,&00,&38,&18,&18,&18,&3C,&00 ;ISO  "small i"
+        = &18,&00,&38,&18,&18,&18,&18,&70 ;ISO  "small j"
+        = &60,&60,&66,&6C,&78,&6C,&66,&00 ;ISO  "small k"
+        = &38,&18,&18,&18,&18,&18,&3C,&00 ;ISO  "small l"
+        = &00,&00,&36,&7F,&6B,&6B,&63,&00 ;ISO  "small m"
+        = &00,&00,&7C,&66,&66,&66,&66,&00 ;ISO  "small n"
+        = &00,&00,&3C,&66,&66,&66,&3C,&00 ;ISO  "small o"
+        = &00,&00,&7C,&66,&66,&7C,&60,&60 ;ISO  "small P"
+        = &00,&00,&3E,&66,&66,&3E,&06,&07 ;ISO  "small q"
+        = &00,&00,&6C,&76,&60,&60,&60,&00 ;ISO  "small r"
+        = &00,&00,&3E,&60,&3C,&06,&7C,&00 ;ISO  "small s"
+        = &30,&30,&7C,&30,&30,&30,&1C,&00 ;ISO  "small t"
+        = &00,&00,&66,&66,&66,&66,&3E,&00 ;ISO  "small u"
+        = &00,&00,&66,&66,&66,&3C,&18,&00 ;ISO  "small v"
+        = &00,&00,&63,&6B,&6B,&7F,&36,&00 ;ISO  "small w"
+        = &00,&00,&66,&3C,&18,&3C,&66,&00 ;ISO  "small x"
+        = &00,&00,&66,&66,&66,&3E,&06,&3C ;ISO  "small y"
+        = &00,&00,&7E,&0C,&18,&30,&7E,&00 ;ISO  "small z"
+        = &0C,&18,&18,&70,&18,&18,&0C,&00 ;ISO  "left curly bracket"
+        = &18,&18,&18,&18,&18,&18,&18,&00 ;ISO  "vertical line"
+        = &30,&18,&18,&0E,&18,&18,&30,&00 ;ISO  "right curly bracket"
+        = &31,&6B,&46,&00,&00,&00,&00,&00 ;ISO  "tilde"
+        = &FF,&FF,&FF,&FF,&FF,&FF,&FF,&FF ;BFNT "Solid block"
+
+        ;
+        ; This block of character definitions consists characters displayed as pairs of
+        ; hex digits, followed by a number of definitions
+        ; added in RISC OS 2.14 onwards that give greater continuity between system font
+        ; and outline fonts. These definitions appear in the kernel Latin1, and in
+        ; Latin1-4 in the International module.
+
+        ; From 3.06 onwards four Welsh characters were added, and the hex characters were
+        ; changed to slope from bottom-left to top-right
+
+        =       &06,&09,&09,&69,&96,&60,&90,&60 ; "80"
+        =       &1C,&63,&6B,&6B,&7F,&77,&63,&00 ; "W circumflex" (81)
+        =       &1C,&36,&00,&6B,&6B,&7F,&36,&00 ; "w circumflex" (82)
+        =       &06,&01,&06,&61,&96,&60,&90,&60 ; "83"
+        =       &05,&05,&07,&61,&91,&60,&90,&60 ; "84"
+        =       &18,&66,&42,&66,&3C,&18,&18,&00 ; "Y circumflex" (85)
+        =       &18,&66,&00,&66,&66,&3E,&06,&3C ; "y circumflex" (86)
+        =       &07,&01,&02,&64,&94,&60,&90,&60 ; "87"
+        =       &06,&09,&06,&69,&96,&60,&90,&60 ; "88"
+        =       &06,&09,&07,&61,&96,&60,&90,&60 ; "89"
+        =       &06,&09,&0F,&69,&99,&60,&90,&60 ; "8A"
+        =       &0E,&09,&0E,&69,&9E,&60,&90,&60 ; "8B"
+        =       &00,&00,&00,&00,&00,&DB,&DB,&00 ; "ellipsis"            (8C)
+        =       &F1,&5B,&55,&51,&00,&00,&00,&00 ; "trademark"           (8D)
+        =       &C0,&CC,&18,&30,&60,&DB,&1B,&00 ; "perthousand"         (8E)
+        =       &00,&00,&3C,&7E,&7E,&3C,&00,&00 ; "bullet"              (8F)
+        =       &0C,&18,&18,&00,&00,&00,&00,&00 ; "quoteleft"           (90)
+        =       &0C,&0C,&18,&00,&00,&00,&00,&00 ; "quoteright"          (91)
+        =       &00,&0C,&18,&30,&30,&18,&0C,&00 ; "guilsinglleft"       (92)
+        =       &00,&30,&18,&0C,&0C,&18,&30,&00 ; "guilsinglright"      (93)
+        =       &1B,&36,&36,&00,&00,&00,&00,&00 ; "quotedblleft"        (94)
+        =       &36,&36,&6C,&00,&00,&00,&00,&00 ; "quotedblright"       (95)
+        =       &00,&00,&00,&00,&00,&36,&36,&6C ; "quotedblbase"        (96)
+        =       &00,&00,&00,&3C,&00,&00,&00,&00 ; "endash"              (97)
+        =       &00,&00,&00,&FF,&00,&00,&00,&00 ; "emdash"              (98)
+        =       &00,&00,&00,&7E,&00,&00,&00,&00 ; "minus"               (99)
+        =       &77,&CC,&CC,&CF,&CC,&CC,&77,&00 ; "OE"                  (9A)
+        =       &00,&00,&6E,&DB,&DF,&D8,&6E,&00 ; "oe"                  (9B)
+        =       &18,&18,&7E,&18,&18,&18,&18,&18 ; "dagger"              (9C)
+        =       &18,&18,&7E,&18,&7E,&18,&18,&18 ; "daggerdbl"           (9D)
+        =       &3C,&66,&60,&F6,&66,&66,&66,&00 ; "fi"                  (9E)
+        =       &3E,&66,&66,&F6,&66,&66,&66,&00 ; "fl"                  (9F)
+
+        = &00,&00,&00,&00,&00,&00,&00,&00 ;ISO  "space"
+        = &18,&00,&18,&18,&18,&18,&18,&00 ;ISO  "inverted exclamation mark"
+        = &08,&3E,&6B,&68,&6B,&3E,&08,&00 ;ISO  "cent sign"
+        = &1C,&36,&30,&7C,&30,&30,&7E,&00 ;ISO  "pound sign"
+        = &00,&66,&3C,&66,&66,&3C,&66,&00 ;ISO  "general currency sign"
+                                          ; changed 09-Sep-88
+        = &66,&3C,&18,&18,&7E,&18,&18,&00 ;ISO  "yen sign"
+        = &18,&18,&18,&00,&18,&18,&18,&00 ;ISO  "Broken vertical bar"
+        = &3C,&60,&3C,&66,&3C,&06,&3C,&00 ;ISO  "section sign, paragraph"
+        = &66,&00,&00,&00,&00,&00,&00,&00 ;ISO  "diaeresis or umlaut mark"
+        = &3C,&42,&99,&A1,&A1,&99,&42,&3C ;ISO  "copyright sign"
+        = &1C,&06,&1E,&36,&1E,&00,&3E,&00 ;ISO  "ordinal indicator, feminine"
+        = &00,&33,&66,&CC,&CC,&66,&33,&00 ;ISO  "angle quotation left"
+        = &7E,&06,&00,&00,&00,&00,&00,&00 ;ISO  "NOT symbol" changed 21/7/87
+        = &00,&00,&00,&7E,&00,&00,&00,&00 ;ISO  "hyphen or minus sign"
+        = &3C,&42,&B9,&A5,&B9,&A5,&42,&3C ;ISO  "registered sign"
+        = &7E,&00,&00,&00,&00,&00,&00,&00 ;ISO  "macron"
+        = &3C,&66,&3C,&00,&00,&00,&00,&00 ;ISO  "ring"
+        = &18,&18,&7E,&18,&18,&00,&7E,&00 ;ISO  "plus or minus sign"
+        = &38,&04,&18,&20,&3C,&00,&00,&00 ;ISO  "superscript two"
+        = &38,&04,&18,&04,&38,&00,&00,&00 ;ISO  "superscript three"
+        = &0C,&18,&00,&00,&00,&00,&00,&00 ;ISO  "acute accent"
+        = &00,&00,&33,&33,&33,&33,&3E,&60 ;ISO  "micro sign"
+        = &03,&3E,&76,&76,&36,&36,&3E,&00 ;ISO  "pilcrow"
+        = &00,&00,&00,&18,&18,&00,&00,&00 ;ISO  "middle dot"
+        = &00,&00,&00,&00,&00,&00,&18,&30 ;ISO  "cedilla" changed 21/7/87
+        = &10,&30,&10,&10,&38,&00,&00,&00 ;ISO  "superscript one" 12/8/87
+        = &1C,&36,&36,&36,&1C,&00,&3E,&00 ;ISO  "ordinal indicator, masculine"
+        = &00,&CC,&66,&33,&33,&66,&CC,&00 ;ISO  "angle quotation right"
+        = &40,&C0,&40,&48,&48,&0A,&0F,&02 ;ISO  "fraction one-quarter"
+        = &40,&C0,&40,&4F,&41,&0F,&08,&0F ;ISO  "fraction one-half"
+        = &E0,&20,&E0,&28,&E8,&0A,&0F,&02 ;ISO  "fraction three-quarters"
+        = &18,&00,&18,&18,&30,&66,&3C,&00 ;ISO  "inverted question mark"
+        = &30,&18,&00,&3C,&66,&7E,&66,&00 ;ISO  "capital A with grave accent"
+        = &0C,&18,&00,&3C,&66,&7E,&66,&00 ;ISO  "capital A with acute accent"
+        = &18,&66,&00,&3C,&66,&7E,&66,&00 ;ISO  "capital A with circumflex"
+        = &36,&6C,&00,&3C,&66,&7E,&66,&00 ;ISO  "capital A with tilde"
+        = &66,&66,&00,&3C,&66,&7E,&66,&00 ;ISO  "capital A with umlaut mark"
+        = &3C,&66,&3C,&3C,&66,&7E,&66,&00 ;ISO  "capital A with ring"
+        = &3F,&66,&66,&7F,&66,&66,&67,&00 ;ISO  "capital AE dipthong"
+        = &3C,&66,&60,&60,&66,&3C,&30,&60 ;ISO  "capital C with cedilla"
+        = &30,&18,&7E,&60,&7C,&60,&7E,&00 ;ISO  "capital E with grave accent"
+        = &0C,&18,&7E,&60,&7C,&60,&7E,&00 ;ISO  "capital E with acute accent"
+        = &3C,&66,&7E,&60,&7C,&60,&7E,&00 ;ISO  "capital E with circumflex"
+        = &66,&00,&7E,&60,&7C,&60,&7E,&00 ;ISO  "capital E with umlaut"
+        = &30,&18,&00,&7E,&18,&18,&7E,&00 ;ISO  "capital I with grave accent"
+        = &0C,&18,&00,&7E,&18,&18,&7E,&00 ;ISO  "capital I with acute accent"
+        = &3C,&66,&00,&7E,&18,&18,&7E,&00 ;ISO  "capital I with circumflex"
+        = &66,&66,&00,&7E,&18,&18,&7E,&00 ;ISO  "capital I with umlaut"
+        = &78,&6C,&66,&F6,&66,&6C,&78,&00 ;ISO  "capital D with stroke"
+        = &36,&6C,&00,&66,&76,&6E,&66,&00 ;ISO  "capital N with tilde"
+        = &30,&18,&3C,&66,&66,&66,&3C,&00 ;ISO  "capital O with grave accent"
+        = &0C,&18,&3C,&66,&66,&66,&3C,&00 ;ISO  "capital O with acute accent"
+        = &3C,&66,&3C,&66,&66,&66,&3C,&00 ;ISO  "capital O with circumflex"
+        = &36,&6C,&3C,&66,&66,&66,&3C,&00 ;ISO  "capital O with tilde"
+        = &66,&00,&3C,&66,&66,&66,&3C,&00 ;ISO  "capital O with umlaut"
+        = &00,&63,&36,&1C,&1C,&36,&63,&00 ;ISO  "multiply sign"
+        = &3D,&66,&6E,&7E,&76,&66,&BC,&00 ;ISO  "capital O with slash"
+        = &30,&18,&66,&66,&66,&66,&3C,&00 ;ISO  "capital U with grave accent"
+        = &0C,&18,&66,&66,&66,&66,&3C,&00 ;ISO  "capital U with acute accent"
+        = &3C,&66,&00,&66,&66,&66,&3C,&00 ;ISO  "capital U with circumflex"
+        = &66,&00,&66,&66,&66,&66,&3C,&00 ;ISO  "capital U with umlaut"
+        = &0C,&18,&66,&66,&3C,&18,&18,&00 ;ISO  "capital Y with acute accent"
+        = &F0,&60,&7C,&66,&7C,&60,&F0,&00 ;ISO  "capital thorn, Icelandic"
+        = &3C,&66,&66,&6C,&66,&66,&6C,&C0 ;ISO  "small sharp s, German"
+        = &30,&18,&3C,&06,&3E,&66,&3E,&00 ;ISO  "small a with grave accent"
+        = &0C,&18,&3C,&06,&3E,&66,&3E,&00 ;ISO  "small a with acute accent"
+        = &18,&66,&3C,&06,&3E,&66,&3E,&00 ;ISO  "small a with circumflex"
+        = &36,&6C,&3C,&06,&3E,&66,&3E,&00 ;ISO  "small a with tilde"
+        = &66,&00,&3C,&06,&3E,&66,&3E,&00 ;ISO  "small a with umlaut mark"
+        = &3C,&66,&3C,&06,&3E,&66,&3E,&00 ;ISO  "small a with ring"
+        = &00,&00,&3F,&0D,&3F,&6C,&3F,&00 ;ISO  "small ae dipthong"
+        = &00,&00,&3C,&66,&60,&66,&3C,&60 ;ISO  "small c with cedilla"
+        = &30,&18,&3C,&66,&7E,&60,&3C,&00 ;ISO  "small e with grave accent"
+        = &0C,&18,&3C,&66,&7E,&60,&3C,&00 ;ISO  "small e with acute accent"
+        = &3C,&66,&3C,&66,&7E,&60,&3C,&00 ;ISO  "small e with cirumflex"
+        = &66,&00,&3C,&66,&7E,&60,&3C,&00 ;ISO  "small e with umlaut"
+        = &30,&18,&00,&38,&18,&18,&3C,&00 ;ISO  "small i with grave accent"
+        = &0C,&18,&00,&38,&18,&18,&3C,&00 ;ISO  "small i with acute accent"
+        = &3C,&66,&00,&38,&18,&18,&3C,&00 ;ISO  "small i with circumflex"
+        = &66,&00,&00,&38,&18,&18,&3C,&00 ;ISO  "small i with umlaut"
+        = &18,&3E,&0C,&06,&3E,&66,&3E,&00 ;ISO  "small eth, Icelandic"
+        = &36,&6C,&00,&7C,&66,&66,&66,&00 ;ISO  "small n with tilde"
+        = &30,&18,&00,&3C,&66,&66,&3C,&00 ;ISO  "small o with grave accent"
+        = &0C,&18,&00,&3C,&66,&66,&3C,&00 ;ISO  "small o with acute accent"
+        = &3C,&66,&00,&3C,&66,&66,&3C,&00 ;ISO  "small o with circumflex"
+        = &36,&6C,&00,&3C,&66,&66,&3C,&00 ;ISO  "small o with tilde"
+        = &66,&00,&00,&3C,&66,&66,&3C,&00 ;ISO  "small o with umlaut"
+        = &00,&18,&00,&FF,&00,&18,&00,&00 ;ISO  "divide sign"
+        = &00,&02,&3C,&6E,&76,&66,&BC,&00 ;ISO  "small o with slash"
+        = &30,&18,&00,&66,&66,&66,&3E,&00 ;ISO  "small u with grave accent"
+        = &0C,&18,&00,&66,&66,&66,&3E,&00 ;ISO  "small u with acute accent"
+        = &3C,&66,&00,&66,&66,&66,&3E,&00 ;ISO  "small u with circumflex"
+        = &66,&00,&00,&66,&66,&66,&3E,&00 ;ISO  "small u with umlaut"
+        = &0C,&18,&66,&66,&66,&3E,&06,&3C ;ISO  "small y with acute accent"
+        = &60,&60,&7C,&66,&7C,&60,&60,&00 ;ISO  "small thorn, Icelandic"
+        = &66,&00,&66,&66,&66,&3E,&06,&3C ;ISO  "small y with umlaut"
+
+        ASSERT (.-HardFont)=(256-32)*8
+
+        END
diff --git a/s/vdu/vdugrafa b/s/vdu/vdugrafa
new file mode 100644
index 0000000000000000000000000000000000000000..a8faef18368d641dfdcc79b2409b64f9c4306ed2
--- /dev/null
+++ b/s/vdu/vdugrafa
@@ -0,0 +1,359 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduGrafA
+;
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Vdu driver code - Graphics rectangle, triangle
+;
+; Author R C Manby
+; Date   5.9.86
+;
+
+; *****************************************************************************
+;
+;       GenLineParm - Generate a control block for a line
+;
+;       Internal routine, called by TriangleFill, LowerTri, ParallelogramFill,
+;        SegmentLineO5, GenArcParmBlk, GenSegParmBlk, LineDraw
+;
+; in:   R0 (X), R1(Y) hold start of line
+;       R2 (X), R3(Y) hold end of line
+;
+; out:  R0..R6 hold the following control block
+;         R0 - StartX
+;         R1 - StartY
+;         R2 - Bres
+;         R3 - DeltaX
+;         R4 - DeltaY
+;         R5 - StepX (+1/-1) (Equv bit6 of Sign in 6502 version)
+;         R6 - StepY (+1/-1) (Equv bit7 of Sign in 6502 version)
+;         R7 - EndX
+;         R8 - EndY
+;
+
+GenLineParm ROUT
+        MOV     R7, R2          ; EndX,EndY - might be worth passing them
+        MOV     R8, R3          ;            in R7,R8 not R2,R3
+
+        SUB     R3, R7, R0      ; Crude deltaX
+        SUB     R4, R8, R1      ; Crude deltaY
+
+        CMP     R4, R3
+        MOVLT   R2, #0          ; if deltaY >= deltaX R2 := -1
+        MOVGE   R2, #-1         ;                else R2 := 0
+                                ; this is used to fudge the bres
+                                ; variable so lines of grad -1 are
+                                ; the same as lines of grad 1
+
+        MOVS    R5, R3, ASR #31 ; R5 := SGN(DeltaX)
+        MOVPL   R5, #1
+        RSBMI   R3, R3, #0      ; DeltaX := ABS(DeltaX)
+
+        MOVS    R6, R4, ASR #31 ; R6 := SGN(DeltaY)
+        MOVPL   R6, #1
+        RSBMI   R4, R4, #0      ; DeltaY := ABS(DeltaY)
+
+        CMP     R4, R3          ; Bres = MAX(DeltaX,DeltaY) + fudge factor
+        ADDPL   R2, R4, R2
+        ADDMI   R2, R3, R2
+
+        RSB     R2, R4, R2, ASR #1 ; Bres = Bres/2 - DeltaY
+        MOVS    PC, R14         ; Return
+
+; *****************************************************************************
+;
+;       AdvLineParm - Advance a set of line parameters
+;
+;       Internal routine, called by TrapFill, SegLineStep, ArcLineStep,
+;        LineDraw, InYWind, InXWind
+;
+; in:   R0..R6, (R7,R8) hold a line parameter block
+;
+; out:  R0 (X), R1 (Y) R2 (Bres) updated
+;
+; Format of a control block
+;       R0 - StartX (CurrentX)
+;       R1 - StartY (CurrentY)
+;       R2 - Bres
+;       R3 - DeltaX
+;       R4 - DeltaY
+;       R5 - StepX (+1/-1) (Equv bit6 of Sign in 6502 version)
+;       R6 - StepY (+1/-1) (Equv bit7 of Sign in 6502 version)
+;       R7 - EndX            (Not used in this routine, so)
+;       R8 - EndY            (doesnt need to be passed in)
+;
+
+AdvLineParm ROUT
+        CMP     R2, #0          ; If Bres +ve, advance in X dirn only
+        ADDLT   R1, R1, R6      ; Advance in Y dirn
+        ADDLTS  R2, R2, R3      ; Add DeltaX to Bres
+                                ; If new Bres +ve advance X as well
+                                ; Advance X
+        SUBGE   R2, R2, R4      ; Sub DeltaY from Bres to advance in X dirn
+        ADDGE   R0, R0, R5      ; Add or subtract 1 dependent on sign DeltaX
+        MOVS    PC, R14
+
+; *****************************************************************************
+;
+;       AdvLineParm_GE - Macro to advance line in XDirn if flags are GE
+;
+;       Used by Triangles,Parallelograms,Arcs etc
+;
+; Use:          CMP R2,#0
+;               AdvLineParm_GE
+;
+; Instead of:   CMP R2,#0
+;               BLGE AdvLineParm
+;
+
+        MACRO
+        AdvLineParm_GE
+        SUBGE   R2, R2, R4
+        ADDGE   R0, R0, R5
+        MEND
+
+; *****************************************************************************
+;
+;       RectangleFill - Fill a rectangle
+;
+;       External routine, and RectFillA entry point used by BlockCopyMove, CLG
+;        and RectFillB entry point used by PlotMask
+;
+; in:   ICursor & NewPt mark a diagonal of the rectangle
+;
+; out:  R0-R11 corrupt
+;
+;
+
+RectangleFill ROUT
+        ADD     R11,WsPtr,#GCsIX        ; load ICursor & NewPt
+        LDMIA   R11, {R0-R3}
+RectFillA
+        Push    R14
+RectFillB
+        SortT   R0, R2, R11             ; ensure R0 holds the lesser X value
+        SortT   R3, R1, R11             ; ensure R1 holds the higher Y value
+        MOV     R11, R3                 ; place end Y out of harm's way
+10
+        BL      NewHLine
+        SUB     R1, R1, #1              ; step Y down a line
+        CMP     R1, R11
+        BGE     %BT10
+        Pull    PC
+
+; *****************************************************************************
+;
+;       TriangleFill - Triangular area fill
+;
+;       External routine
+;
+; in:   OldCs, ICursor & NewPt are verticies of the triangle
+;
+; out:  R0-R11 corrupt
+;
+
+TriangleFill ROUT
+        Push    R14
+        ADD     R11, WsPtr, #OldCsX             ; vertices are OldCs,ICursor
+        LDMIA   R11, {R0-R5}                    ; and NewPt
+        BL      LowerTri                        ; sort vertices and fill the
+                                                ; lower triangle
+        ADD     R11, WsPtr, #Vertex2X           ; restart TLine1 to run from
+        LDMIA   R11, {R0-R3}                    ; Vertex2 to Vertex3
+        ADD     R11, WsPtr, #TLine1
+TriangleFil5                                    ; entry point from par fill
+        BL      GenLineParm
+        STMIA   R11, {R0-R8}                    ; initialise parameter block
+        STR     R8, [WsPtr, #TEndY]             ; end Y point for both lines
+        BL      TrapFill                        ; fill the upper trapezoid
+        MOV     R0, R9                          ; fill top line LeftX,RightX
+        MOV     R2, R10                         ; leftY already in R1
+        BL      NewHLine
+        Pull    PC
+
+; *****************************************************************************
+;
+;       LowerTri - Sort 3 vertices and fill the lower triangle between the
+;        points up to but excluding the horizontal through the middle point
+;
+;       Internal routine, called by TriangleFill and ParallelogramFill
+;
+
+LowerTri ROUT
+        Push    R14
+        CompSwapT R0,R1, R2,R3, R14 ; bubble highest vertex into R4,R5
+        CompSwapT R2,R3 ,R4,R5, R14
+
+        CompSwapT R0,R1, R2,R3, R14 ; place lowest in R0,R1 and middle in R2,R3
+
+        ADD     R11, WsPtr, #Vertex1X
+        STMIA   R11, {R0-R7}                    ; save all our vertices
+
+        MOV     R9, R0                          ; initalise TLeftX,TRightX
+        MOV     R10, R0                         ; to lowest point
+        BL      GenLineParm                     ; Run TLine1 from lowest to
+        ADD     R11, WsPtr, #TLine1             ; middle point
+        STMIA   R11, {R0-R8}
+        STR     R8, [WsPtr, #TEndY]             ; this line ends first
+
+        ADD     R2, WsPtr, #Vertex3X            ; run TLine2 from lowest to
+        LDMIA   R2, {R2, R3}                    ; highest point
+        BL      GenLineParm
+        ADD     R11, WsPtr, #TLine2
+        STMIA   R11, {R0-R8}
+        BL      TrapFill                        ; fill the lower trapezoid
+
+        Pull    PC
+
+; *****************************************************************************
+;
+;       ParallelogramFill - Fill a parallelogram
+;
+;       External routine
+;
+; in:   OldCs, ICursor & NewPt are 3 vertices of the parallelogram
+;
+; out:  R0-R11 corrupt
+;
+
+ParallelogramFill ROUT
+        Push    R14
+        ADD     R11, WsPtr, #OldCsX             ; vertices are OldCs,ICursor
+        LDMIA   R11, {R0-R5}                    ; and NewPt
+
+; now calculate Vertex4 from other three
+
+        ADD     R6, R0, R4      ; Vertex4X := Vertex1X + Vertex3X - Vertex2X
+        SUB     R6, R6, R2
+
+        ADD     R7, R1, R5      ; Vertex4Y := Vertex1Y + Vertex3Y - Vertex2Y
+        SUB     R7, R7, R3
+
+        CompSwapT R0,R1, R2,R3, R14 ; Bubble the highest point into R6,R7
+        CompSwapT R2,R3 ,R4,R5, R14
+        CompSwapT R4,R5 ,R6,R7, R14
+
+        BL      LowerTri        ; sort other vertices and draw lower triangle
+
+        ADD     R11, WsPtr, #Vertex2X           ; restart TLine1 to run from
+        LDMIA   R11!, {R0-R3}                   ; Vertex2 to Vertex4
+        STR     R3, [WsPtr, #TEndY]             ; and indicate line Vertex1 to
+        LDMIA   R11, {R2,R3}                    ; Vertex3 as the next to finish
+
+        BL      GenLineParm
+        ADD     R11, WsPtr, #TLine1
+        STMIA   R11, {R0-R8}                    ; TLine1 parameter block
+
+        BL      TrapFill                        ; fill the middle trapezoid
+
+        ADD     R11, WsPtr, #Vertex3X           ; restart TLine2 to run from
+        LDMIA   R11, {R0-R3}                    ; Vertex3 to Vertex4
+        ADD     R11, WsPtr, #TLine2
+        B       TriangleFil5                    ; Use common code with
+                                                ; triangles to initialise and
+                                                ; fill upper trapezoid
+
+; *****************************************************************************
+;
+;       TrapFill - Fill a trapezoid
+;
+;       Internal routine, called by TriangleFill, LowerTri, ParallelogramFill
+;
+; in:   R9  = TLeftX    }  the line limits for this scanline
+;       R10 = TRightX   }
+;
+; out:  R9,R10 updated
+;
+
+TrapFill ROUT
+        Push    R14
+10
+        ADD     R11, WsPtr, #TLine1     ; Advance TLine1 until currentY about
+        BL      TrapLineStep            ; to change, or end of line reached
+
+        ADD     R11, WsPtr, #TLine2     ; ditto TLine2
+        BL      TrapLineStep
+
+        LDR     R11, [WsPtr, #TEndY]
+        CMP     R11, R1                 ; if CurrentY = EndY
+        Pull    PC, EQ                  ; then quit
+
+        MOV     R0, R9                  ; LeftX
+        MOV     R2, R10                 ; RightX
+                                        ; LeftY already in R1
+        BL      NewHLine                ; Plot current scanline
+
+        ADD     R11, WsPtr, #TLine1     ; advance TLine1 to next scanline
+        LDMIA   R11, {R0-R6}
+        BL      AdvLineParm
+        STMIA   R11, {R0-R2}            ; save the changes
+
+        MOV     R9, R0                  ; assume currentX will be LeftX
+
+        ADD     R11, WsPtr, #TLine2     ; advance TLine2 to next scanline
+        LDMIA   R11, {R0-R6}
+        BL      AdvLineParm
+        STMIA   R11, {R0-R2}            ; save the changes
+
+        CMP     R0, R9                  ; LeftX=Min(CurrentX,CurrentY)
+        MOVGE   R10, R0                 ; RightX=Max(CurrentX,CurrentY)
+        MOVLT   R10, R9
+        MOVLT   R9, R0
+        B       %BT10                   ; continue with next scanline
+
+
+; *****************************************************************************
+;
+;       TrapLineStep - Step line parameters until CurrentY about to change
+;        or end of line reached - compares CurrentX with line limits
+;        (LeftX,RightX) and widens them if needed
+;
+;       Internal routine, called by TrapFill
+;
+; in:   R9  = LeftX
+;       R10 = RightX
+;       R11 = address of parameter block
+;
+; out:  R0-R8 parameter block for this line
+;       R9, R10 updated
+;       R11 preserved
+;
+
+TrapLineStep ROUT
+        Push    R14
+        LDMIA   R11, {R0-R8}            ; get line parameter block
+        CMP     R8, R1                  ; if on last scanline
+        MOVEQ   R0, R7                  ; then set currentX to EndX
+        BEQ     TrapLin20               ;     (bres unchanged, but who cares)
+
+        CMP     R2, #0                  ; else While bres >= 0 do AdvLineParm
+TrapLin10                               ;      {step until Y about to change}
+        ADDGE   R0, R0, R5
+        SUBGES  R2, R2, R4
+        BGE     TrapLin10
+
+TrapLin20
+        STMIA   R11, {R0-R2}            ; update the changes to parameter block
+        CMP     R0, R9                  ; LeftX=Min(LeftX,CurrentX)
+        MOVLT   R9, R0
+        CMP     R0, R10
+        MOVGT   R10, R0                 ; RightX=Max(RightX,CurrentX)
+        Pull    PC
+
+
+
+        END
diff --git a/s/vdu/vdugrafb b/s/vdu/vdugrafb
new file mode 100644
index 0000000000000000000000000000000000000000..acf1d3cf0a38afe8c31caecc3c4d68162353c2b9
--- /dev/null
+++ b/s/vdu/vdugrafb
@@ -0,0 +1,1116 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduGrafB
+;
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Vdu driver code - Graphics circle outline, fill, arc, sector & segment
+;
+; Author R C Manby
+; Date   5.9.86
+;
+
+; *****************************************************************************
+;
+;       CircleOutline - Circle outline
+;
+;       External routine
+;
+; in:   ICursor is the centre of the circle
+;       NewPt is a point on the circumference
+;
+; out:  R0-R11 corrupt
+;
+
+CircleOutline ROUT
+        Push    R14
+        ADD     R11, WsPtr, #GCsIX              ; R0 := CentreX; R1 := CentreY
+        LDMIA   R11, {R0-R3}                    ; R2 := NewPtX; R3 := NewPtY
+        BL      GenCircleParm                   ; set up parm. block in R0-R7
+        ADD     R9, WsPtr, #CircleBlk           ; R9 -> saved R0
+        ADD     R11, R9, #5*4                   ; R11 -> saved R5
+        STMIA   R11, {R5-R7}                    ; save R5-R7 once and for all
+10
+        STMIA   R9, {R0-R4}                     ; save R0-R4 before plotting pt
+
+        SUB     R10, R5, R0                     ; upper left point
+        ADD     R0, R5, R0                      ; upper right point
+        ADD     R1, R6, R1
+        BL      PlotPoint                       ; do upper right point
+
+        CMP     R10, R0
+        MOVNE   R0, R10                         ; do upper left point
+        BLNE    PlotPoint                       ; unless same as upper right
+
+        LDMIA   R9, {R0,R1}                     ; reload X, Y
+        CMP     R1, #0                          ; if yPnt=0, skip lower
+        BEQ     %FT20                           ; pixel pair
+
+        LDMIA   R11, {R5,R6}                    ; reload CentreX, CentreY
+
+        SUB     R10, R5, R0                     ; lower left point
+        ADD     R0, R5, R0                      ; lower right point
+        SUB     R1, R6, R1
+        BL      PlotPoint
+
+        CMP     R10, R0
+        MOVNE   R0, R10                         ; do lower left point
+        BLNE    PlotPoint                       ; unless same as lower right
+
+20
+        LDMIA   R9, {R0-R7}                     ; reload the parameter block
+        TEQ     R0, #0                          ; if xPnt<>0
+        BLNE    AdvCircleParm                   ; then step to next point
+        BNE     %BT10                           ; and go round again
+
+        Pull    PC                              ; else finish
+
+; *****************************************************************************
+;
+;       CircleFill - Circular area fill
+;
+;       External routine
+;
+; in:   ICursor is the centre of the circle
+;       NewPt is a point on the circumference
+;
+; out:  R0-R11 corrupt
+;
+
+CircleFill ROUT
+        Push    R14
+        ADD     R11, WsPtr, #GCsIX              ; centre is ICursor
+        LDMIA   R11, {R0-R3}                    ; point is NewPt
+        BL      GenCircleParm                   ; set up parameter block
+        ADD     R11, WsPtr, #CircleBlk          ; in R0-R6
+10
+        STMIA   R11, {R0-R7}                    ; save prior to line drawing
+        ADD     R2, R5, R0                      ; RightX = CentreX+xPnt
+        SUB     R11, R6, R1                     ; LowerY = CentreY-yPnt
+
+        SUB     R0, R5, R0                      ; LeftX  = CentreX-xPnt
+        ADD     R1, R6, R1                      ; UpperY = CentreY+yPnt
+        BL      NewHLine                        ; draw upper slice
+
+        CMP     R11, R1                         ; unless UpperY=LowerY
+        MOV     R1, R11
+        BLNE    NewHLine                        ; do draw lower slice
+
+        ADDS    R11, WsPtr, #CircleBlk          ; (C := 0)
+        LDMIA   R11, {R0-R7}                    ; reload the parameter block
+20
+        TEQ     R0, #0
+        Pull    PC, EQ                          ; finish if xPnt=0
+        BL      AdvCircleParm                   ; else step to next point
+        BCC     %BT20                           ; step until yPnt changes
+        B       %BT10                           ; do next slice
+
+; *****************************************************************************
+;
+;       CircleArc - Circular arc outline
+;
+;       External routine
+;
+; in:   OldCs is the centre of the circle
+;       ICursor is the start of the arc
+;       NewPt is the finishing point of the arc
+;
+; out:  R0-R11 corrupt
+;
+
+CircleArc ROUT
+        Push    R14
+        BL      GenArcParmBlk
+        ADD     R11, WsPtr, #CircleBlk
+        LDMIA   R11, {R0-R7}                    ; reload the parameter block
+10
+        STMIA   R11, {R0-R7}                    ; save prior to line drawing
+        BL      Reflect
+        BL      UpdateQuadrants
+
+        LDRB    R8, [WsPtr, #Quad0Draw]         ; if LSBit set, plot this point
+        TST     R8, #1
+        LDRNE   R0, [WsPtr, #ArcPoint0X]
+        LDRNE   R1, [WsPtr, #ArcPoint0Y]
+        BLNE    PlotPoint
+
+        LDR     R0, [WsPtr, #CircleBlk]         ; if xPnt=0, ignore left pixel
+        CMP     R0, #0
+        BEQ     %FT20
+
+        LDRB    R8, [WsPtr, #Quad1Draw]         ; if LSBit set, plot this point
+        TST     R8, #1
+        LDRNE   R0, [WsPtr, #ArcPoint1X]
+        LDRNE   R1, [WsPtr, #ArcPoint1Y]
+        BLNE    PlotPoint
+20
+        LDR     R0, [WsPtr, #(CircleBlk+4)]     ; if yPnt=0, skip lower
+        CMP     R0, #0                          ; pixel pair
+        BEQ     %FT30
+
+        LDRB    R8, [WsPtr, #Quad3Draw]         ; if LSBit set, plot this point
+        TST     R8, #1
+        LDRNE   R0, [WsPtr, #ArcPoint3X]
+        LDRNE   R1, [WsPtr, #ArcPoint3Y]
+        BLNE    PlotPoint
+
+        LDR     R0, [WsPtr, #CircleBlk]         ; if xPnt=0, ignore left pixel
+        CMP     R0, #0
+        BEQ     %FT30
+
+        LDRB    R8, [WsPtr, #Quad2Draw]         ; if LSBit set, plot this point
+        TST     R8,#1
+        LDRNE   R0, [WsPtr, #ArcPoint2X]
+        LDRNE   R1, [WsPtr, #ArcPoint2Y]
+        BLNE    PlotPoint
+30
+        ADD     R11, WsPtr, #CircleBlk
+        LDMIA   R11, {R0-R7}                    ; reload the parameter block
+        TEQ     R0, #0
+        Pull    PC, EQ                          ; finish if xPnt=0
+
+        BL      AdvCircleParm                   ; else step to next point
+        B       %BT10                           ; and go round again
+
+; *****************************************************************************
+;
+;       SegmentFill - Circular segment fill
+;
+;       External routine
+;
+; in:   OldCs is the centre of the circle
+;       ICursor is the start of the segment
+;       NewPt is the finishing point of the segment
+;
+; out:  R0-R11 corrupt
+;
+
+SegmentFill ROUT
+        Push    R14
+        BL      GenArcParmBlk
+        BL      GenSegParmBlk
+        ADD     R11, WsPtr,#CircleBlk
+        LDMIA   R11, {R0-R7}                    ; reload the parameter block
+10
+        STMIA   R11, {R0-R7}                    ; save prior to line drawing
+        BL      Reflect
+        BL      UpdateQuadrants
+        LDR     R0, [WsPtr, #Quad0StateChange]  ; if any quadrant changes state
+        CMP     R0, #0
+        BLNE    SegmentLineOn                   ; try starting segment line
+
+        LDR     R7, [WsPtr, #ArcPoint1X]        ; limits of segment line
+        LDR     R8, [WsPtr, #ArcPoint0X]
+        LDR     R9, [WsPtr, #ArcPoint1Y]        ; current scanline
+        LDR     R11, [WsPtr, #UpperSegLinePtr]  ; holds 0 or points at CLine2
+        CMP     R11, #0
+        BLNE    SegLineStep                     ; and advance if active
+
+        LDR     R0, [WsPtr, #ArcPoint1X]
+        LDR     R1, [WsPtr, #ArcPoint1Y]
+        LDR     R2, [WsPtr, #ArcPoint0X]
+        LDRB    R3, [WsPtr, #Quad1Draw]
+        LDRB    R4, [WsPtr, #Quad0Draw]
+        BL      SegmentSlice
+
+        LDR     R0, [WsPtr, #(CircleBlk+4)]     ; if yPnt=0, skip lower line
+        CMP     R0, #0
+        BEQ     %FT15
+
+        LDR     R7, [WsPtr, #ArcPoint2X]        ; limits of segment line
+        LDR     R8, [WsPtr, #ArcPoint3X]
+        LDR     R9, [WsPtr, #ArcPoint3Y]        ; current scanline
+        LDR     R11, [WsPtr, #LowerSegLinePtr]  ; holds 0 or points at CLine3
+        CMP     R11, #0
+        BLNE    SegLineStep                     ; and advance if active
+
+        Swap    R7,R8
+
+        LDR     R0, [WsPtr, #ArcPoint3X]
+        LDR     R1, [WsPtr, #ArcPoint3Y]
+        LDR     R2, [WsPtr, #ArcPoint2X]
+        LDRB    R3, [WsPtr, #Quad3Draw]
+        LDRB    R4, [WsPtr, #Quad2Draw]
+        BL      SegmentSlice
+15
+        LDR     R0, [WsPtr, #Quad0StateChange]  ; if any quadrant state changes
+        CMP     R0, #0                          ; left, kill segment line
+        BLNE    SegmentLineOff
+
+        ADD     R11, WsPtr, #CircleBlk
+        LDMIA   R11, {R0-R7}                    ; reload the parameter block
+20
+        TEQ     R0, #0
+        Pull    PC, EQ                          ; finish if xPnt=0
+
+        BL      AdvCircleParm                   ; else step to next point
+        BCS     %BT10                           ; do next slice
+        BCC     %BT20                           ; step until yPnt changes
+
+; *****************************************************************************
+;
+;       SegLineStep - Advance the segment line, limited to be within the circle
+;
+;       Internal routine, called by SegmentFill
+;
+; in:   R7 = left circleX
+;       R8 = right circleX
+;       R9 = current scanline
+;       R11 = pointer to Line (CLine2 or CLine3) (can't hold zero any more)
+;
+
+SegLineStep ROUT
+        Push    R14
+        LDMIA   R11, {R0-R6,R10} ; N.B. EndX is in R10
+        CMP     R4, #0          ; if line is horizontal
+        MOVEQ   R9, R10         ; then line limits are in R0 & R10,
+        BEQ     %FT50           ;      R9 := R10 and branch
+                                ; else find limits
+
+        CMP     R9, R1          ; advance line until currentY = circleY
+20
+        BLNE    AdvLineParm     ; (this usually takes one step)
+        CMP     R9, R1
+        BNE     %BT20
+
+        MOV     R9, R0          ; assume CurrentX is the left most point
+
+        CMP     R10, R0         ; if currentX=EndX
+        BEQ     %FT40           ; then no need to advance line
+
+        CMP     R2, #0          ; else While bres >= 0 do AdvLineParm
+30
+        AdvLineParm_GE          ; this leaves us furthest point on segment
+        CMP     R10, R0
+        BEQ     %FT40
+        CMP     R2, #0          ; line for this scanline in R0
+        BGE     %BT30
+40
+        STMIA   R11, {R0-R4}    ; update the line parameter block
+50
+        CMP     R9, R0          ; R9 := LeftX
+        MOVGT   R10, R9
+        MOVGT   R9, R0
+        MOVLE   R10, R0         ; R10 := RightX
+
+        CMP     R8, R10         ; force R10 into range R7..R8
+        MOVLT   R10, R8
+        CMP     R7, R10
+        MOVGT   R10, R7
+
+        CMP     R7, R9          ; force R9 into range R7..R8
+        MOVGT   R9, R7
+        CMP     R8, R9
+        MOVLT   R9, R8
+
+        MOV     R8, R10
+        MOV     R7, R9
+        Pull    PC
+
+; *****************************************************************************
+;
+;       SegmentSlice - Draw a slice of a circular segment
+;
+;       Internal routine, called by SegmentFill
+;
+; in:   R0 = circleX left
+;       R1 = circleY
+;       R2 = circleX right
+;       R3 = left quadrant control word  (TopL or BotR)
+;       R4 = right quadrant control word (TopR or BotL)
+;       R7 = left most point of segment line for this slice
+;       R8 = right most point of segment line for this slice
+;
+; out:  R0-R11 corrupt
+;
+
+SegmentSlice ROUT
+        TST     R3, #1          ; if both quadrants empty
+        TSTEQ   R4, #1          ; or not at start of segment
+        MOVEQ   PC, R14         ; then return
+
+        TST     R3, #1          ; if both quadrants to be filled
+        TSTNE   R4, #1          ; or filling below segment line
+        BNE     HLine           ; then draw a slice of circle
+
+;              0 0              ; ......
+;              1 1              ; (----)
+
+;              0 1              ;     \)    ;     /) ;  \---)    ;  /---)
+;              1 0              ; (/        ; (\     ; (---/     ; (---\
+
+; From here on, all plotting decisions can be made from
+; the LSBit of one quadrant field
+
+        TST     R3, #1
+        MOVEQ   R0, R7          ; Draw ---)
+        BEQ     HLine
+
+        MOV     R2, R8          ; Draw (---
+        B       HLine
+
+; *****************************************************************************
+;
+;       SegmentLineOn - Try to start segment line
+;
+;       Internal routine, called by SegmentFill
+;
+
+SegmentLineOn ROUT
+        Push    R14
+                                                ; quadrant 0
+        LDRB    R10, [WsPtr, #Quad0StateChange] ; state change 0..2
+        ADD     R9, WsPtr, #ArcPoint0X          ; address of point on circle
+        ADD     R8, WsPtr, #CLine2              ; line block to use if starting
+        LDR     R11, [WsPtr, #UpperSegLinePtr]  ; 0 or ptr to active line
+        BL      SegmentLineO5
+        STRB    R10, [WsPtr, #Quad0StateChange] ; new state change flag
+
+        LDRB    R10, [WsPtr, #Quad1StateChange] ; quadrant 1
+        ADD     R9, WsPtr, #ArcPoint1X
+        ADD     R8, WsPtr, #CLine2
+        BL      SegmentLineO5
+        STRB    R10, [WsPtr, #Quad1StateChange]
+        STR     R11, [WsPtr, #UpperSegLinePtr]  ; unchanged/updated lineptr
+
+                                                ; lower hemisphere
+        LDRB    R10, [WsPtr, #Quad2StateChange] ; quadrant 2
+        ADD     R9, WsPtr, #ArcPoint2X
+        ADD     R8, WsPtr, #CLine3
+        LDR     R11, [WsPtr, #LowerSegLinePtr]
+        BL      SegmentLineO5
+        STRB    R10, [WsPtr, #Quad2StateChange]
+
+        LDRB    R10, [WsPtr, #Quad3StateChange] ; quadrant 3
+        ADD     R9, WsPtr, #ArcPoint3X
+        ADD     R8, WsPtr, #CLine3
+        BL      SegmentLineO5
+        STRB    R10, [WsPtr, #Quad3StateChange]
+        STR     R11, [WsPtr, #LowerSegLinePtr]
+
+        Pull    PC
+
+; *****************************************************************************
+;
+;       SegmentLineO5 - On state change, start segment line and update
+;        statechange - if line already active, do nothing
+;
+;       Internal routine, called by SegmentLineOn
+;
+; in:   R8  -> CLine(2..3)
+;       R9  -> ArcPoint(0..3)X
+;       R10 = Quad(0..3)StateChange
+;         0 means no change
+;         1 means one line hit
+;         2 means both lines hit
+;       R11 = 0 or pointer to segment line
+;
+; out:  R10 = updated Quad(0..3)StateChange
+;       R11 = 0 or points at newly created line
+;
+
+SegmentLineO5 ROUT
+        CMP     R10, #0         ; if state unchanged
+        MOVEQ   PC, R14         ; then go home
+
+        CMP     R11, #0         ; else if segment line active
+        MOVNE   PC, R14         ;      then go home
+
+        Push    R14             ;      else start the segment line
+        MOV     R11, R8
+        LDMIA   R9, {R0,R1}     ; run from point on circle
+        LDMIA   R11, {R2,R3}    ; to other end
+        BL      GenLineParm
+        STMIA   R11, {R0-R8}
+        MOV     R10, R10, LSR #1 ; state1->state0, state2->state1
+        Pull    PC
+
+; *****************************************************************************
+;
+;       SegmentLineOff - Try to kill segment line
+;
+;       Internal routine, called by SegmentFill
+;
+; in:   R0 = stateflags for each quadrant
+;
+; out:  R0 preserved
+;       R1 corrupted
+;
+
+SegmentLineOff ROUT
+        MOV     R1, #0
+        TST     R0, #&3                         ; if statechange occurred in
+        TSTEQ   R0, #&300                       ; Quad0 or Quad1
+        STRNE   R1, [WsPtr, #UpperSegLinePtr]   ; then kill upper segment line
+
+        TST     R0, #&30000                     ; if statechange occured in
+        TSTEQ   R0, #&3000000                   ; Quad2 or Quad3
+        STRNE   R1, [WsPtr, #LowerSegLinePtr]   ; then kill lower segment line
+
+        MOVS    PC, R14
+
+; *****************************************************************************
+;
+;       SectorFill - Circular sector (pie) fill
+;
+;       External routine
+;
+; in:   OldCs is the centre of the circle
+;       ICursor is the start of the sector
+;       NewPt is the finishing point of the sector
+;
+; out:  R0-R11 corrupt
+;
+
+SectorFill ROUT
+        Push    R14
+        BL      GenArcParmBlk
+        ADD     R11, WsPtr, #CircleBlk
+        LDMIA   R11, {R0-R7}                    ; reload the parameter block
+SectorFi10
+        STMIA   R11, {R0-R7}                    ; save prior to line drawing
+        BL      Reflect
+        BL      UpdateQuadrants
+
+        LDR     R0, [WsPtr, #(CircleBlk+4)]     ; if yPnt=0, panic
+        CMP     R0, #0
+        BEQ     SectorFi40
+
+        LDR     R0, [WsPtr, #ArcPoint1X]
+        LDR     R1, [WsPtr, #ArcPoint1Y]
+        LDR     R2, [WsPtr, #ArcPoint0X]
+        LDRB    R3, [WsPtr, #Quad1Draw]
+        LDRB    R4, [WsPtr, #Quad0Draw]
+        BL      SectorSlice
+
+        LDR     R0, [WsPtr, #ArcPoint3X]
+        LDR     R1, [WsPtr, #ArcPoint3Y]
+        LDR     R2, [WsPtr, #ArcPoint2X]
+        LDRB    R3, [WsPtr, #Quad3Draw]
+        LDRB    R4, [WsPtr, #Quad2Draw]
+        BL      SectorSlice
+
+SectorFi20
+        ADD     R11, WsPtr, #CircleBlk
+        LDMIA   R11, {R0-R7}                    ; reload the parameter block
+SectorFi30
+        TEQ     R0, #0
+        Pull    PC, EQ                          ; finish if xPnt=0
+        BL      AdvCircleParm                   ; else step to next point
+        BCS     SectorFi10                      ; do next slice
+        BCC     SectorFi30                      ; step until yPnt changes
+
+SectorFi40
+        LDR     R0, [WsPtr, #CLine0Near]        ; equal to CLine1NearX &
+        LDR     R1, [WsPtr, #ArcPoint0Y]        ; centre of circle
+        LDR     R3, [WsPtr, #CLine0Far]
+        LDR     R4, [WsPtr, #CLine1Far]
+
+        Greatest R2, R0,R3              ; draw from rightmost of CLine0, Cline1
+        Greatest R2, R2,R4
+
+        Least R0, R0,R3                 ; to left most of CLine0, Cline1
+        Least R0, R0,R4
+
+        LDR     R3, [WsPtr, #Quad0Draw] ; all 4 drawing control bytes
+
+        TST     R3, #&00000001          ; if Quad0 or
+        TSTEQ   R3, #&01000000          ;    Quad3 fills against circle
+        LDRNE   R2, [WsPtr, #ArcPoint0X] ; then override R2
+
+        TST     R3, #&00000100          ; If Quad1 or
+        TSTEQ   R3, #&00010000          ;    Quad2 fills against circle
+        LDRNE   R0, [WsPtr, #ArcPoint1X] ; then override R0
+
+        BL      NewHLine                ; draw the line (sorted coords)
+        B       SectorFi20
+
+
+;
+; Internal subroutine for sector (pie) fills
+;
+; On entry, R0 - circleX left
+;           R1 - circleY
+;           R2 - circleX right
+;           R3 - left quadrant control word  (TopL or BotR)
+;           R4 - right quadrant control word (TopR or BotL)
+;
+; On exit, R0-R11 corrupt
+;
+SectorSlice
+        CMP   R4,#&57                           ; (--//)
+        LDREQ R3,[WsPtr,#CLine0Far]
+        LDREQ R4,[WsPtr,#CLine1Near]
+        BEQ DoubleHLine
+
+        CMP   R3,#&73                           ; (\\--)
+        LDREQ R3,[WsPtr,#CLine0Near]
+        LDREQ R4,[WsPtr,#CLine1Far]
+        BEQ DoubleHLine
+
+        CMP   R4,#&07                           ; (-\/-)
+        CMPEQ R3,#&03
+        LDREQ R3,[WsPtr,#CLine0Near]
+        LDREQ R4,[WsPtr,#CLine1Near]
+        BEQ DoubleHLine
+
+        CMP   R4,#&07                           ;    /-)
+        LDREQ R0,[WsPtr,#CLine1Near]
+        BEQ HLine
+        CMP   R3,#&03                           ; (-\
+        LDREQ R2,[WsPtr,#CLine0Near]
+        BEQ HLine
+
+        CMP   R4,#&3A                           ;    /-/
+        LDREQ R0,[WsPtr,#CLine1Near]
+        LDREQ R2,[WsPtr,#CLine0Far]
+        BEQ HLine
+
+        CMP   R3,#&1E                           ; \-\
+        LDREQ R0,[WsPtr,#CLine1Far]
+        LDREQ R2,[WsPtr,#CLine0Near]
+        BEQ HLine
+
+        CMP   R4,#1                             ; ...--)
+        MOVLT PC,Link                           ;Nothing in either quadrant
+        LDRGT R2,[WsPtr,#CLine0Far]             ; ...--/
+        CMP   R3,#1                             ; (--...
+        LDRGT R0,[WsPtr,#CLine1Far]             ; \--...
+        B HLine
+;
+;
+;
+;------------------------------------------------------------------------------
+;
+; Reflect - Generate four point on a circle by
+; =======   reflection about its centre
+;
+; On entry, R0..R7 hold a circle parameter block
+; On exit,  R0 (X), R1  (Y) point in Quadrant0
+;           R2 (X), R3  (Y) point in Quadrant1
+;           R7 (X), R8  (Y) point in Quadrant2
+;           R9 (X), R10 (Y) point in Quadrant3
+;           R11 points at ArcPoint0X
+;
+;           ArcPoint(0..3) updated
+;
+; Format of a circle control block
+;       R0 - xPnt   (CurrentX - relative to centre)
+;       R1 - yPnt   (CurrentY - relative to centre)
+;       R2 - sum (Bres)
+;       R3 - upcnt
+;       R4 - downcnt
+;       R5 - CentreX
+;       R6 - CentreY
+;       R7 - Aspect (pixel shape : 0 square, 1 horz rect, 2 vert rect)
+;
+Reflect
+        ADD R9,R5,R0    ;Quad 3  CentreX+xPnt   ;Calculate all 4 points
+        SUB R10,R6,R1   ;        CentreY-yPnt   ; by reflection about centre
+
+        SUB R7,R5,R0    ;Quad 2  CentreX-xPnt
+        SUB R8,R6,R1    ;        CentreY-yPnt
+
+        SUB R2,R5,R0    ;Quad 1  CentreX-xPnt
+        ADD R3,R6,R1    ;        CentreY+yPnt
+
+        ADD R0,R5,R0    ;Quad 0  CentreX+xPnt
+        ADD R1,R6,R1    ;        CentreY+yPnt
+
+        ADD R11,WsPtr,#ArcPoint0X
+        STMIA R11,{R0,R1, R2,R3, R7,R8, R9,R10} ;And store the lot for later on
+
+        MOVS PC,Link
+;
+;
+;
+;  update lines & quadrant data
+;
+;  use R9  as offset from WsPtr to ArcPoint(0..3)X
+;  use R10 as offset from WsPtr to QuadControl(0..3)
+;  use R11 as address of line parameter block(0..1)
+;
+;
+UpdateQuadrants
+        SaveRetAdr
+
+        MOV R0,#0
+        STR R0,[WsPtr,#Quad0StateChange]        ;Clear flags for each quadrant
+
+        LDR R0,[WsPtr,#Quad0Control]            ;Update the 4 'drawing'
+        STR R0,[WsPtr,#Quad0Draw]               ; control bytes
+
+                                                ;Start by looking at quadrant 0
+        ADD R10,WsPtr,#Quad0Control             ;Address of control byte
+        ADD R9,WsPtr,#ArcPoint0X                ;Address of point on circle
+        BL UpdateQuadr10
+
+        ADD R10,R10,#(Quad1Control-Quad0Control)        ;Quadrant 1
+        ADD R9,R9,#(ArcPoint1X-ArcPoint0X)
+        BL UpdateQuadr10
+
+        ADD R10,R10,#(Quad1Control-Quad0Control)        ;Quadrant 2
+        ADD R9,R9,#(ArcPoint1X-ArcPoint0X)
+        BL UpdateQuadr10
+
+        ADD R10,R10,#(Quad1Control-Quad0Control)        ;Quadrant 3
+        ADD R9,R9,#(ArcPoint1X-ArcPoint0X)
+        BL UpdateQuadr10
+
+        Return
+
+
+UpdateQuadr10
+        LDRB R0,[R10]           ;Get control block for quadrant
+        TST R0,#&2              ;If 0 or 1
+        MOVEQ PC,Link           ; then nothing to do
+
+        SaveRetAdr              ; else update the line
+
+        LDMIA R9,{R7,R8}                        ;Point on circle in this quad
+
+        TST R0,#4
+        ADDEQ R11,WsPtr,#CLine0                 ;Load parm blk for line(0/1)
+        ADDNE R11,WsPtr,#CLine1
+
+        LDMIA R11,{R0,R1,R2,R3,R4,R5,R6}        ;EndX,EndY (R7,R8) not needed
+        BL ArcLineStep
+        STMIA R11,{R0,R1,R2,R3,R4,R5,R6,R8}     ;Update the changes, EndX
+                                                ; used for NearX
+
+        CMP R7,#1                               ; 'change state' flag
+        MOV R7,#1                               ;Convert to one line hit
+        STRGEB R7,[R10,#(Quad0StateChange-Quad0Control)]
+
+        LDRB R0,[R10]           ;Get control block for quadrant and look at
+        MOV R0,R0,LSR #3        ; next control field
+        STRGEB R0,[R10]         ;If 'change state' write back next field
+
+                                                        ;If outside circle
+        TSTEQ R0,#1                                     ; or changing into a
+        STRGTB R0,[R10,#(Quad0Draw-Quad0Control)]       ; plotting state update
+                                                        ; drawing control byte
+
+        TST R0,#2                               ;If new field doesnt advance a
+        Return EQ                               ; line then quit
+                                                ; else update second line
+        Push R0
+
+        LDMIA R9,{R7,R8}                        ;Point on circle in this quad
+
+        TST R0,#4
+        ADDEQ R11,WsPtr,#CLine0                 ;Load parm blk for line(0/1)
+        ADDNE R11,WsPtr,#CLine1
+
+        LDMIA R11,{R0,R1,R2,R3,R4,R5,R6}        ;EndX,EndY (R7,R8) not needed
+        BL ArcLineStep
+        STMIA R11,{R0,R1,R2,R3,R4,R5,R6,R8}     ;Update the changes, EndX
+                                                ; used for NearX
+
+        CMP R7,#1                               ; 'change state' flag
+        MOV R7,#2                               ;Convert to both lines hit
+        STRGEB R7,[R10,#(Quad0StateChange-Quad0Control)]
+
+        Pull R0         ;Use earlier value instead of reloading control block,
+                        ; as whally lines (dy/dx = 0/0) blewup when 2nd line
+                        ; terminated before 1st line. This case should not
+                        ; now get through, but you never now.
+
+        MOV R0,R0,LSR #3        ;Next field in control field
+        STRGEB R0,[R10]         ;If 'changing state' write this back
+
+        STRGTB R0,[R10,#(Quad0Draw-Quad0Control)]       ; and update drawing
+                                                        ; control byte
+        Return
+;
+;
+;
+; ArcLineStep - Step line parameter block checking for interception
+; ===========     with circle.
+;
+;               Return first ('near') and last ('far') points of line
+;                 that fall on this scanline, limiting 'far' to the point on
+;                 the circle if interception occurs.
+;
+; On entry, R0..R6 hold a line parameter block (EndX,EndY are not loaded)
+;
+; Format of a line control block
+;           R0 - StartX (CurrentX)
+;           R1 - StartY (CurrentY)
+;           R2 - Bres
+;           R3 - DeltaX
+;           R4 - DeltaY
+;           R5 - StepX (+1/-1) (Equv bit6 of Sign in 6502 version)
+;           R6 - StepY (+1/-1) (Equv bit7 of Sign in 6502 version)
+;                (R7 - EndX            Not used in this routine,)
+;                (R8 - EndY            so not passed in)
+;
+;           R7,R8  CircleX,CircleY
+;
+; On exit,  R0 (X), R1 (Y), R2 (bres) updated
+;           R7 0/1/2 for within/on/outside circle
+;           R8 nearX
+;
+;
+; R9,R10,R11 unused
+;
+ArcLineStep
+        SaveRetAdr
+
+        CMP R8,R1               ;Advance line until CurrentY = CircleY
+ArcLineSt10
+        BLNE AdvLineParm
+        CMP R8,R1               ; {this usually takes one step}
+        BNE ArcLineSt10
+
+        MOV R8,R0               ;This point is nearX
+                                ; ie the first point on this scanline
+
+        CMP R0,R7               ;If  (CurrentX=CircleX) then farX is on circle
+        TEQNE R5,PC             ;If ((CurrentX-CircleX) EOR StepX is +ve)
+                                ;then farX is outside circle
+        MOVPL R0,R7             ;      limit farX to circleX
+        MOVPL R7,#2             ;      set change flag =2 for outside
+        MOVEQ R7,#1             ;                      =1 for on circle
+        Return PL               ;      and return
+
+
+        CMP R2,#0               ;While bres >= 0 and within circle AdvLineParm
+ArcLineSt20                     ; this leaves us with farX,farY in R0,R1
+        AdvLineParm_GE
+
+        CMP R0,R7               ;If (CurrentX=CircleX) then farX is on circle
+        MOVEQ R7,#1             ;      set change flag
+        Return EQ               ;      and return
+                                ; else within circle
+
+        CMP R2,#0               ;If y about to change, return farX,farY
+        BGE ArcLineSt20         ; else loop back to step the line
+        MOV R7,#0
+        Return
+;
+;
+;
+; Assumes  R9, R10 & R11 are not corrupted over calls to GenLineParm
+;
+;
+GenArcParmBlk
+        SaveRetAdr
+
+        ADD R11,WsPtr,#OldCsX                   ;Build parm block for a circle
+        LDMIA R11,{R0,R1,R2,R3}                 ; centre OldCs, point on
+        BL GenCircleParm                        ; circumference in ICursor
+
+        ADD R11,WsPtr,#CircleBlk
+        STMIA R11,{R0,R1,R2,R3,R4,R5,R6,R7}
+
+        ADD R10,WsPtr,#CLine0
+        ADD R11,WsPtr,#OldCsX
+
+        LDMIA R11!,{R0,R1,R2,R3}                ;CLine0 gives start of arc
+        BL GenLineParm                          ; (OldCs->ICursor)
+        STMIA R10!,{R0,R1,R2,R3,R4,R5,R6,R7,R8}
+;
+; Use StepX and StepY for 'start of arc' line to form the beginnings of
+; an index into the arc control block table
+; StepX & StepY each hold +1 or -1 and the value left in R9 is
+;
+;     R9  | Quad
+;   ------+------
+;    0000 |  0
+;    0100 |  1
+;    1100 |  2
+;    1000 |  3
+;
+;
+        AND R5,R5,#&4                           ;
+        AND R6,R6,#&8                           ; See above for details
+        ORR R9,R6,R5                            ;
+
+        LDMIA R11,{R2,R3}                       ;CLine1 gives end line of arc
+                                                ; (OldCs->NewPt)
+
+        CMP R0,R2                               ;If OldCs=NewPt
+        CMPEQ R1,R3                             ; use OldCs->(NewPtX+1,NewPtY)
+        ADDEQ R2,R2,#1                          ; for compatability with Master
+
+        BL GenLineParm
+        STMIA R10,{R0,R1,R2,R3,R4,R5,R6,R7,R8}
+
+        AND R5,R5,#&4
+        AND R6,R6,#&8
+        ORR R6,R6,R5                            ; 'end of arc'
+
+        CMP R9,R6               ;If start and end quadrants different
+        ORRNE R9,R9,R6,LSL #2   ;  then index = start OR (end <<2)
+        BNE GenArcPar10         ;  and branch
+
+                                ;else special case code for index, based
+                                ; on gradients of line
+
+        LDR R7,[WsPtr,#(CLine0+12)]     ; DeltaX0.DeltaY1
+        LDR R8,[WsPtr,#(CLine1+16)]
+        MUL R0, R7, R8
+
+        LDR R7,[WsPtr,#(CLine0+16)]     ; DeltaX1.DeltaY0
+        LDR R8,[WsPtr,#(CLine1+12)]
+        MUL R9, R7, R8
+
+        CMP R0,R9                       ;For gradients <,>,=
+        ORRLT R9,R6,#&40                ; generate index into table
+        ORRGT R9,R6,#&50
+        ORREQ R9,R6,R6,LSL #2
+
+GenArcPar10
+        ADR R0,GenArcTb                 ; GenArcTb
+
+        LDR R0,[R0,R9]
+        STR R0,[WsPtr,#Quad0Control]    ;Setup all FOUR control BYTEs
+
+        Return
+
+
+
+;
+;
+; 00 000 000  &00     no points to be plotted
+; 00 000 001  &01     all points to be plotted
+; 00 001 x10  &0A,0E  plot nothing until line x hits circle then start plotting
+; 00 000 x11  &03,07  plot points until line x hits circle then stop
+; 00 y11 x10  &3A,1E  plot points only between line x and line y
+; 01 y10 x11  &73,57  plot points from x axis to line x, then line y to y axis
+;
+;
+GenArcTb
+ & &0000003A    ; 0->0 (short arc)
+ & &01010307    ; 1->0
+ & &03000007    ; 3->0
+ & &010A0007    ; 2->0
+
+ & &00000E0A    ; 0->1
+ & &00001E00    ; 1->1 (short arc)
+ & &03000E01    ; 3->1
+ & &010A0E01    ; 2->1
+
+ & &0E01010A    ; 0->3
+ & &0E010300    ; 1->3
+ & &1E000000    ; 3->3 (short arc)
+ & &0E0A0000    ; 2->3
+
+ & &0007010A    ; 0->2
+ & &00070300    ; 1->2
+ & &03070101    ; 3->2
+ & &003A0000    ; 2->2 (short arc)
+
+
+ & &01010157    ; 0->0 (long arc)    grad line0 > grad line1
+ & &00001E00    ; 1->1 (short arc)
+ & &1E000000    ; 3->3 (short arc)
+ & &01570101    ; 2->2 (long arc)
+
+ & &0000003A    ; 0->0 (short arc)   grad line0 < grad line 1
+ & &01017301    ; 1->1 (long arc)
+ & &73010101    ; 3->3 (long arc)
+ & &003A0000    ; 2->2 (short arc)
+;
+;
+;
+
+
+GenSegParmBlk
+        SaveRetAdr
+
+        ADD R11,WsPtr,#CLine1           ;Get all line data except EndX,EndY
+        LDMIA R11,{R0,R1,R2,R3,R4,R5,R6}
+
+        LDR R11,[WsPtr,#AspectRatio]    ;Frigging non square frigging pixels
+                                        ; 0=Sq, 1=horz, 2=vert
+        CMP R11,#1
+        MOVEQ R3,R3,LSL #1              ;If horz, scale up deltaX
+        MOVGT R4,R4,LSL #1              ;If vert, scale up deltaY
+
+        [ {TRUE}
+        LDR     R8, [WsPtr, #CircleRadSquare] ; R8 = r2
+        MUL     R9, R3, R3              ; R9 = DX2
+        MUL     R10, R4, R4             ; R10 = DY2
+        ADD     R11, R9, R10            ; R11 = R2
+
+        MOV     R3, R10
+        BL      DoubleMulDivSquareRoot
+        MOV     R4, R3                  ; R4 = dy
+
+        MOV     R3, R9
+        BL      DoubleMulDivSquareRoot  ; R3 = dx
+        |
+        MUL R9,R3,R3                    ;R9 := Square(deltaX)
+        MOV R2,R9                       ;R2 := Square(deltaX)
+
+        LDR R8,[WsPtr,#CircleRadSquare]
+        MUL R3,R9,R8                    ;R3 := Square(radius) * Square(deltaX)
+
+        MUL R9,R4,R4                    ;R9 := Square(deltaY)
+        ADD R2,R2,R9                    ;R2 := Square(deltaX) + Square(deltaY)
+
+        MUL R10,R9,R8                   ;R10 := Square(radius) * Square(deltaY)
+
+        MOV R9,R2
+; *****Change made by DJS
+; Use new DivRem macro, not old DIVREM
+; Original code was:
+;        DIVREM R7,R10,R9, R8            ;R7 := (rad^2 * deltaY^2)/R2
+        DivRem R7,R10,R9, R8            ;R7 := (rad^2 * deltaY^2)/R2
+; *****End of change made by DJS
+        BL SquareRoot                   ;Iy left in R8
+        MOV R4,R8
+
+; *****Change made by DJS
+; Use new DivRem macro, not old DIVREM
+; Original code was:
+;        DIVREM R7,R3,R2, R8             ;R7 := (rad^2 * deltaX^2)/R2
+        DivRem R7,R3,R2, R8             ;R7 := (rad^2 * deltaX^2)/R2
+; *****End of change made by DJS
+        BL SquareRoot                   ;Ix left in R8
+        MOV R3,R8
+        ]
+
+        LDR R11,[WsPtr,#AspectRatio]    ; 0=Sq, 1=horz, 2=vert
+        CMP R11,#1
+        MOVEQ R3,R3,LSR #1              ;If horz, scale down deltaX
+        MOVGT R4,R4,LSR #1              ;If vert, scale down deltaY
+
+        CMP R5,#0                       ;If StepX >= 0
+        ADDGE R0,R0,R3                  ; then R0 := StartX+R3
+        SUBLT R0,R0,R3                  ; else R0 := StartX-R3
+
+        CMP R6,#0                       ;If StepY >= 0
+        ADDGE R1,R1,R4                  ; then R1 := StartY+R4
+        SUBLT R1,R1,R4                  ; else R1 := StartY-R4
+
+;
+; R0,R1 is the intercept of CLine1 and the circle
+;       so, segment line runs from here to endpoint of CLine0
+;
+
+        LDR R2,[WsPtr,#CLine0EndX]
+        LDR R3,[WsPtr,#CLine0EndY]
+
+        CompSwapT R0,R1, R2,R3, R4       ; Order coords
+
+;
+; If segment line crosses X axis
+; then initialise both upper & lower segment lines
+; else leave endpoints for later use
+;
+
+        LDR R4,[WsPtr,#Quad0Control]    ;All 4 control bytes
+        ORR R4,R4,R4,LSR #8             ;If bit1 =0, not in upper hemisphere
+                                        ;If bit9 =0, not in lower hemisphere
+        AND R4,R4,R4,LSR #16
+        TST R4,#2                       ;If bit1 =0
+        BEQ GenSegParmB10               ; then line does not cross X axis
+
+                                        ; else start both CLine2 & CLine3
+                                        ;      running, so..
+
+        BL GenLineParm                          ;Initialise CLine2 as the
+        ADD R11,WsPtr,#CLine2                   ; upper hemisphere segment line
+        STMIA R11,{R0,R1,R2,R3,R4,R5,R6,R7,R8}
+        STR R11,[WsPtr,#UpperSegLinePtr]
+
+        MOV R2,R0                               ;Run CLine3, in the opposite
+        MOV R3,R1                               ; direction for use as the
+        MOV R0,R7                               ; lower hemisphere segment line
+        MOV R1,R8
+        BL GenLineParm
+        ADD R11,WsPtr,#CLine3
+        STMIA R11,{R0,R1,R2,R3,R4,R5,R6,R7,R8}
+        STR R11,[WsPtr,#LowerSegLinePtr]
+
+        Return
+
+GenSegParmB10                           ;Line does not cross X axis, so..
+        ADD R11,WsPtr,#CLine2           ;Upper hemisphere segment line, runs
+        STMIA R11,{R2,R3}               ; to R2,R3  (if it runs at all)
+
+        ADD R11,WsPtr,#CLine3           ;Lower hemisphere segment line, runs
+        STMIA R11,{R0,R1}               ; to R0,R1  (if it runs at all)
+
+        MOV R11,#0                              ;Both lines inactive
+        STR R11,[WsPtr,#UpperSegLinePtr]
+        STR R11,[WsPtr,#LowerSegLinePtr]
+
+        Return
+
+; *****************************************************************************
+;
+;       DoubleMulDivSquareRoot - Compute SQR(a*b/c) in double precision
+;
+; in:   R3 = a
+;       R8 = b
+;       R11 = c
+;
+; out:  R3 = result
+;       R2, R7 corrupted
+;
+
+DoubleMulDivSquareRoot ROUT
+        Push    "R8-R11,R14"
+        MOV     R2, R3, LSR #16                 ; R2 = ah
+        EOR     R7, R3, R2, LSL #16             ; R7 = al
+        MOV     R9, R8, LSR #16                 ; R9 = bh
+        EOR     R10, R8, R9, LSL #16            ; R10 = bl
+
+        MUL     R3, R7, R10                     ; R3 = al.bl
+        MUL     R14, R7, R9                     ; R14 = al.bh
+        MLA     R14, R10, R2, R14               ; R14 = al.bh + ah.bl
+        MUL     R8, R2, R9                      ; R8 = ah.bh
+
+        ADDS    R3, R3, R14, LSL #16            ; R3 = lower 32 bits of a.b
+        ADC     R8, R8, R14, LSR #16            ; R8 = upper 32 bits of a.b
+
+; now do divide of a.b by c
+; we know that a.b < 2^61, so no problem with top bit of a.b
+
+        MOV     R9, R11                         ; R9 = low 32 bits of shifted c
+        MOV     R10, #0                         ; R10 = hi 32 bits of shifted c
+10
+        ADDS    R9, R9, R9                      ; shift R9,R10 left one place
+        ADC     R10, R10, R10
+        CMP     R9, R3                          ; compare R9,R10 with a.b
+        SBCS    R14, R10, R8
+        BCC     %BT10                           ; if lower then loop
+
+        MOV     R7, #0                          ; zero result
+20
+        CMP     R3, R9                          ; if a.b >= R9,R10
+        SBCS    R14, R8, R10
+        SUBCS   R3, R3, R9                      ; then a.b -:= R9,R10
+        MOVCS   R8, R14
+        ADC     R7, R7, R7                      ; shift result up with new bit
+        MOVS    R10, R10, LSR #1                ; shift R9,R10 right one bit
+        MOV     R9, R9, RRX
+        BNE     %BT20                           ; for termination, R10 = 0
+        CMP     R9, R11                         ; and R9 < R11
+        BCS     %BT20
+
+        BL      SquareRoot                      ; in: R7 = arg
+                                                ; out: R8 = result, R9-R11 corrupt
+        MOV     R3, R8
+        Pull    "R8-R11,PC"
+
+
+
+        END
diff --git a/s/vdu/vdugrafc b/s/vdu/vdugrafc
new file mode 100644
index 0000000000000000000000000000000000000000..a088b7839bab07d57e1cc3b53248453c667583e2
--- /dev/null
+++ b/s/vdu/vdugrafc
@@ -0,0 +1,441 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduGrafC
+;
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Vdu driver code - Ellipse outline & fill
+;
+; Author R C Manby
+; Date   29.9.86
+;
+
+
+
+
+;
+;
+;
+;------------------------------------------------------------------------------
+;
+; EllipseOutline
+; ==============
+;
+; On entry, OldCs is centre of the ellipse
+;           ICursor is a point giving the width of the ellipse
+;           NewPt is the highest/lowest point on the ellipse
+;
+; On exit, R0..R11 corrupt
+;
+EllipseOutline
+        SaveRetAdr
+
+        BL GenEllParm
+        B EllipseOut20
+EllipseOut10
+        BL AdvEllParm
+EllipseOut20
+                                ; Registers hold
+                                ;   R4 , R5  , R6  , R7  , R8  , R9  , R10
+                                ;  ellY,prevL,prevR,thisL,thisR,nextL,nextR
+
+        MOV R0,R7               ;Left line runs from thisL rightwards
+        MOV R1,R4
+        Greatest R3,R5,R9
+        SUB R3,R3,#1
+        Greatest R3,R3,R7       ; to max(thisL, max(prevL-1,nextL-1))
+
+        MOV R2,R8               ;Right line runs from thisR leftwards
+        Least R4,R6,R10
+        ADD R4,R4,#1
+        Least R4,R4,R8          ; to min(thisR, min(prevR+1,nextR+1))
+
+        BL EllDoubleHLine       ;Any overlap handled by EllDoubleHLine
+
+        LDR R0,[WsPtr,#EllBlkSliceCnt]
+        CMP R0,#0
+        BGE EllipseOut10
+        B EllipseFi30           ;Use common code with ellipse fill for
+                                ; last scanline
+;
+;
+;
+;
+;------------------------------------------------------------------------------
+;
+; EllipseFill
+; ===========
+;
+; On entry, OldCs is centre of the ellipse
+;           ICursor is a point giving the width of the ellipse
+;           NewPt is the highest/lowest point on the ellipse
+;
+; On exit, R0..R11 corrupt
+;
+EllipseFill
+        SaveRetAdr
+
+        BL GenEllParm
+        B EllipseFi20
+EllipseFi10
+        BL AdvEllParm
+EllipseFi20
+                                ; Registers hold
+                                ;   R4 , R5  , R6  , R7  , R8  , R9  , R10
+                                ;  ellY,prevL,prevR,thisL,thisR,nextL,nextR
+
+        MOV R0,R7                       ;Draw slice thisL -> thisR
+        MOV R1,R4
+        MOV R2,R8
+
+        BL EllHLine
+
+        LDR R0,[WsPtr,#EllBlkSliceCnt]
+        CMP R0,#0
+        BGE EllipseFi10
+EllipseFi30
+        LDR R0,[WsPtr,#EllNextL]        ;Last slice nextL -> nextR
+        LDR R1,[WsPtr,#EllBlkEllY]
+        ADD R1,R1,#1
+        LDR R2,[WsPtr,#EllNextR]
+
+        BL EllHLine
+
+        Return
+;
+;
+;
+; EllHLine - Draw a slice then reflect around ellipse origin
+; ========
+;
+; On entry, R0 (X) - Left point of line
+;           R1 (Y) - Y ordinate of line
+;           R2 (X) - Right point of line
+;
+; On exit,  R0..R11 corrupt
+;
+EllHLine
+        SaveRetAdr
+
+        LDR R3,[WsPtr,#OldCsX]          ;All points relative to OldCs
+        LDR R4,[WsPtr,#OldCsY]
+
+        ADD R11,WsPtr,#EllHLineWs       ;Save points given plus centre of
+        STMIA R11,{R0-R4}               ; ellipse for later use
+
+        ADD R0,R3,R0                    ;OldCsX + LeftX
+        ADD R1,R4,R1                    ;OldCsY + Y
+        ADD R2,R3,R2                    ;OldCsX + RightX
+
+        BL NewHLine                     ;Draw top slice
+
+        ADD R11,WsPtr,#EllHLineWs
+        LDMIA R11,{R3-R7}
+
+        CMP R4,#0                       ;If EllY = 0
+        Return EQ                       ; then quit to prevent double plotting
+                                        ; else reflect around ellipse origin
+
+        SUB R0,R6,R5                    ;OldCsX - RightY
+        SUB R1,R7,R4                    ;OldCsY - Y
+        SUB R2,R6,R3                    ;OldCsX - LeftY
+
+        BL NewHLine
+
+        Return
+;
+;
+;
+; EllDoubleHLine
+; ==============
+;
+; On entry, R0 (X) - Left most point
+;           R1 (Y) - y ordinate of line
+;           R2 (X) - Right most point
+;           R3 (X) - end of left most line
+;           R4 (X) - start of right most line
+;
+EllDoubleHLine
+        CMP R3,R4                               ;If end of left line overlaps
+                                                ; start of right
+        BGE EllHLine                            ;then draw and reflect R0->R2
+                                                ;else
+        SaveRetAdr
+
+        ADD R11,WsPtr,#EllDoubleHLineWs
+        STMIA R11,{R0-R4}
+
+        MOV R2,R3                               ;Draw and reflect R0->R3
+        BL EllHLine
+
+        ADD R11,WsPtr,#EllDoubleHLineWs
+        LDMIA R11,{R0-R4}
+        MOV R0,R4
+        BL EllHLine                             ;Draw and reflect R4->R2
+
+        Return
+;
+;
+;
+;------------------------------------------------------------------------------
+;
+; GenEllParm - Generate a control block for an ellipse
+; ==========
+;
+; On entry, OldCs is centre of the ellipse
+;           ICursor is a point giving the width of the ellipse
+;           NewPt is the highest/lowest point on the ellipse
+;
+; On exit, EllBlk holds the ellipse parameter block
+;
+;          EllThisL..EllNextR setup
+;
+;          R4      - EllY
+;          R5, R6  - prevL,prevR
+;          R7, R8  - thisL,thisR
+;          R9, R10 - nextL,nextR
+;
+;
+; Format of ellipse parameter block
+;
+; Var      R0 - SliceCnt            count of slices remaining
+;          R1 - EllYSqr             square of current slice height
+;          R2 - OddNo               used to give next EllYSqr value
+;          R3 - XOffset  [bb.bb]    offset in X dirn along shear line
+;          R4 - EllY
+; Const    R5 - ShearX   [bb.bb]    shear factor per scanline
+;          R6 - SliceX   [0bb.b]    axis ratio
+;          R7 - MaxYSqr  [bbbb]
+;
+;
+;
+GenEllParm
+        SaveRetAdr
+
+        ADD R11,WsPtr,#OldCsX
+        LDMIA R11,{R0,R1, R2,R3, R4,R5}         ;OldCs, ICursor, NewPt
+
+        SUBS R6,R2,R0
+        RSBLT R6,R6,#0                  ;R6 := ABS(width of slice)
+                                        ;R6 is sliceX (ie maxwidth)
+        SUBS R7,R5,R1
+        MOV R8,R7
+        RSBLT R7,R7,#0                  ;R7 := ABS(height of ellipse)
+
+;
+; Leave R0,R1,R2 intact upto here
+;       R6 is ABS(width of slice)
+
+        BEQ EllipseZeroHeight           ;If NewPtY=OldCsY draw a single line
+
+        SUBS R5,R4,R0
+        EOR R8,R8,R5
+        RSBLT R5,R5,#0                  ;R5 := ABS(shear at top of ellipse)
+
+;
+; R5 holds shear in x dirn
+; R6 holds width
+; R7 holds height (also shearY)
+; R8 sign bit is slope of shearline
+;
+
+        MOV R0,R7                       ;Setup SliceCnt
+
+       ;MOV R7,R0                       ;Div R5,R5,R0 using R7,R9,R10
+        MOV R9,R5,LSL #16
+; *****Change made by DJS
+; Use new DivRem macro, not old DIVREM
+; Original code was:
+;        DIVREM R5,R9,R7,R10             ;R5 := totalshear/ellipseheight
+        DivRem R5,R9,R7,R10             ;R5 := totalshear/ellipseheight
+; *****End of change made by DJS
+
+       ;BIC R5,R5,#&FF                  ;Slugg accuracy for compatability?
+
+        CMP R8,#0
+        RSBLT R5,R5,#0                  ;Correct for negative slope
+
+        MOV R7,R0                       ;Div R6,R6,R0 using R7,R9,R10
+        MOV R9,R6,LSL #8
+; *****Change made by DJS
+; Use new DivRem macro, not old DIVREM
+; Original code was:
+;        DIVREM R6,R9,R7,R10             ;R6 := slicewidth/ellipseheight
+        DivRem R6,R9,R7,R10             ;R6 := slicewidth/ellipseheight
+; *****End of change made by DJS
+
+
+; R5 holds shear per scanline
+; R6 ratio of ellipse axis
+
+        MUL R7,R0,R0    ;MaxYSqr
+
+        MOV R1,#0       ;EllYSqr
+        MOV R2,#1       ;Oddno
+        MOV R3,#0       ;XOffset
+        MOV R4,#-2      ;EllY
+
+        ADD R11,WsPtr,#EllBlk
+        STMIA R11,{R0-R7}
+
+        BL AdvEllP20                    ;Start by calculating first two slices
+        STR R9,[WsPtr,#EllThisL]        ;Limits passed back in R9,R10
+        STR R10,[WsPtr,#EllThisR]
+
+        BL AdvEllP20
+
+        ADD R11,WsPtr,#EllThisL
+        LDMIA R11,{R7,R8}
+
+        RSB R5,R10,#0                   ;prevL := -nextR
+        RSB R6,R9,#0                    ;prevR := -nextL
+
+                                        ; Stretch slice if disconnected
+
+        Least R7,R7,R6                  ; thisL := min(thisL,prevR)
+        Least R7,R7,R10                 ; thisL := min(thisL,nextR)
+
+        Greatest R8,R8,R5               ; thisR := max(thisR,prevL)
+        Greatest R8,R8,R9               ; thisR := max(thisR,nextL)
+
+        STMIA R11,{R7,R8,R9,R10}
+
+        Return                  ; On return, registers hold
+                                ;   R4 , R5  , R6  , R7  , R8  , R9  , R10
+                                ;  ellY,prevL,prevR,thisL,thisR,nextL,nextR
+;
+;
+;
+; EllipseZeroHeight - Draw a single line, no pips
+; =================
+;
+; On entry, R0 (X), R1 (Y) holds centre of ellipse
+;           R6 is ABS(width of slice/2)
+;
+EllipseZeroHeight
+
+        ADD R2,R0,R6
+        SUB R0,R0,R6
+
+        Pull Link                       ;Return address into ellipse code
+        Pull Link                       ;Return address to whoever called
+                                        ; ellipses
+
+        B NewHLine                      ;Sorted coords
+                                        ;This will return to whoever called
+                                        ; the ellipse code
+;
+;
+;
+;------------------------------------------------------------------------------
+;
+; AdvEllParm
+; ==========
+;
+; On entry, R11 points to ellipse parameter block
+;
+; On exit, EllBlk, EllThisL..EllNextR updated
+;
+;          R4      - EllY
+;          R5, R6  - prevL,prevR
+;          R7, R8  - thisL,thisR
+;          R9, R10 - nextL,nextR
+;
+;
+; Format of ellipse parameter block
+;
+; Var      R0 - SliceCnt            count of slices remaining
+;          R1 - EllYSqr             square of current slice height
+;          R2 - OddNo               used to give next EllYSqr value
+;          R3 - XOffset  [bb.bb]    offset in X dirn along shear line
+;          R4 - EllY
+; Const    R5 - ShearX   [bb.bb]    shear factor per scanline
+;          R6 - SliceX   [0bb.b]    axis ratio
+;          R7 - MaxYSqr  [bbbb]
+;
+;
+;
+AdvEllParm
+        SaveRetAdr
+
+        BL AdvEllP20            ;Advance scanline
+                                ; On return, registers hold
+                                ;   R4 ,   R9  , R10
+                                ;  ellY,  nextL,nextR
+
+                                ;Shuffle points
+                                ;R9/R10 -> nextLR
+                                ;nextLR -> thisLR - stretch if disconnected
+                                ;thisLR -> prevLR
+
+        ADD R11,WsPtr,#EllThisL
+        LDMIA R11,{R5,R6, R7,R8}
+                                        ; Stretch slice if disconnected
+        Greatest R8,R8,R9               ; thisR := max(thisR,nextL)
+        Least R7,R7,R10                 ; thisL := min(thisL,nextR)
+
+        STMIA R11,{R7,R8, R9,R10}
+        Return                  ;   R4 , R5  , R6  , R7  , R8  , R9  , R10
+                                ;  ellY,prevL,prevR,thisL,thisR,nextL,nextR
+;
+;
+;
+;
+;
+;
+;
+AdvEllP20
+        SaveRetAdr
+
+        ADD R11,WsPtr,#EllBlk
+        LDMIA R11,{R0-R7}       ;Load ellipse parm block
+
+        SUB R7,R7,R1            ;Sqroot(MaxYSqr-EllYSqr)
+        MOV R11,#24             ;Use 24 instead of 16 iterations in the
+        BL SquareRootAlt        ; SquareRoot routine, gives result [0bb.b]
+
+                                ;Mult by axis ratio
+        MOV R7,R6               ; [0bb.b] * [0bb.b]
+        MUL R9,R7,R8            ; Result in R9 is [bb.bb]
+
+        ADD R10,R3,R9           ;NextR   [bb.bb]
+        ADD R10,R10,#&8000
+        MOV R10,R10,ASR #16     ;         [00bb]
+
+        SUB R9,R3,R9            ;NextL   [bb.bb]
+        ADD R9,R9,#&8000
+        MOV R9,R9,ASR #16       ;         [00bb]
+
+        ADD R1,R1,R2            ;New value of EllYSqr by adding oddno
+        ADD R2,R2,#2            ;Next oddno in sequence
+
+        ADD R3,R3,R5            ;XOffSet for next scanline
+        SUB R0,R0,#1            ;Decrement count of slices left
+        ADD R4,R4,#1            ;Increment EllY
+
+        ADD R11,WsPtr,#EllBlk
+        STMIA R11,{R0,R1,R2,R3,R4}      ;Save updated section
+
+        Return                  ;   R4 ,   R9  , R10
+                                ;  ellY,  nextL,nextR
+
+;
+;
+;
+;------------------------------------------------------------------------------
+
+
+        END
diff --git a/s/vdu/vdugrafd b/s/vdu/vdugrafd
new file mode 100644
index 0000000000000000000000000000000000000000..e8f96993a4e4ff3ee1e013fcdaea65225db6145b
--- /dev/null
+++ b/s/vdu/vdugrafd
@@ -0,0 +1,566 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduGrafD
+
+;
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Vdu driver code - Block Copy and Move
+;
+; Author R C Manby
+; Date   2.10.86
+;
+
+; *****************************************************************************
+;
+;       BlockCopyMove - Copy/Move a rectangular area
+;
+; in:   OldCs marks start of area to copy/move
+;       ICursor marks end of area to copy/move
+;       NewPt is lower left of destination area
+;       R2 = plot number &B8..&BF
+;
+; out:  R0..R11 corrupt
+;
+
+BlockCopyMove ROUT
+        TST     R2, #3                          ; Do nowt on 'MoveOnly' codes
+        MOVEQS  PC, R14
+
+        SaveRetAdr
+
+        ADD     R0, WsPtr, #BgEcfStore          ; Select 'store' in background
+        STR     R0, [WsPtr, #GColAdr]           ; to be used by HLine when
+                                                ; clearing source & dest lines
+
+; Build up source & destination data as follows
+;
+; R0   R1   R2   R3   R4    R5    R6    R7    R8
+; SrcL,SrcB,SrcR,SrcT,DestL,DestB,DestR,DestT,CopyFlag
+
+        AND     R8, R2, #2                      ; 0/2 means Move/Copy
+
+        ADD     R11, WsPtr, #OldCsX
+        LDMIA   R11, {R0,R1, R2,R3, R4,R5}      ; OldCs, ICursor, NewPt
+
+        SortT   R0, R2, R6                      ; Order SrcL,SrcB,SrcR,SrcT
+        SortT   R1, R3, R6                      ;       R0 , R1 , R2 , R3
+
+        SUB     R6, R2, R0
+        ADD     R6, R6, R4                      ; DestR := SrcR-SrcL+DestL
+
+        SUB     R7, R3, R1
+        ADD     R7, R7, R5                      ; DestT := SrcT-SrcB+DestB
+
+        ADD     R11, WsPtr, #CMSrc
+        STMIA   R11, {R0-R7,R8}                 ; Unclipped src & dest areas
+                                                ; and CopyFlag
+        LDR     R11, [WsPtr, #CursorFlags]
+        TST     R11, #ClipBoxEnableBit
+        BLNE    ClipBlockCopyMove
+
+; now work out ACTUAL area to copy
+; first, we window destination area
+
+        LDR     R10, [WsPtr, #GWLCol]
+        LDR     R11, [WsPtr, #GWRCol]
+
+        SUBS    R8, R10, R4                     ; R8 = GWLCol - DestL
+        ADDGT   R4, R4, R8                      ; if > 0 then DestL := GWLCol
+        ADDGT   R0, R0, R8                      ; and adjust SrcL to match
+
+        SUBS    R8, R6, R11                     ; R8 = DestR - GWRCol
+        SUBGT   R6, R6, R8                      ; if > 0 then DestR := GWRCol
+        SUBGT   R2, R2, R8                      ; and adjust SrcR to match
+
+        CMP     R6, R4                          ; check DestR >= DestL
+        BLT     EraseSource                     ; if not then just erase source
+                                                ; if a block move
+
+        STR     R4, [WsPtr, #CMDest2L]          ; save horizontal dest coords
+        STR     R6, [WsPtr, #CMDest2R]          ; windowed at dest
+
+        SUBS    R8, R10, R0                     ; R8 = GWLCol - SrcL
+        ADDGT   R0, R0, R8                      ; if > 0 then SrcL := GWLCol
+        ADDGT   R4, R4, R8                      ; and adjust DestL to match
+
+        SUBS    R8, R2, R11                     ; R8 = SrcR - GWRCol
+        SUBGT   R2, R2, R8                      ; if > 0 then SrcR := GWRCol
+        SUBGT   R6, R6, R8                      ; and adjust DestR to match
+
+        LDR     R10, [WsPtr, #GWBRow]
+        LDR     R11, [WsPtr, #GWTRow]
+
+        SUBS    R8, R10, R5                     ; R8 = GWBRow - DestB
+        ADDGT   R5, R5, R8                      ; if > 0 then DestB := GWBRow
+        ADDGT   R1, R1, R8                      ; and adjust SrcB to match
+
+        SUBS    R8, R7, R11                     ; R8 = DestT - GWTRow
+        SUBGT   R7, R7, R8                      ; if > 0 then DestT := GWTRow
+        SUBGT   R3, R3, R8                      ; and adjust SrcT to match
+
+        CMP     R7, R5                          ; check DestT >= DestB
+        BLT     EraseSource                     ; if not then just erase source
+                                                ; if a block move
+
+        STR     R5, [WsPtr, #CMDest2B]          ; save vertical dest coords
+        STR     R7, [WsPtr, #CMDest2T]          ; windowed at dest
+
+        SUBS    R8, R10, R1                     ; R8 = GWBRow - SrcB
+        ADDGT   R1, R1, R8                      ; if > 0 then SrcB := GWBRow
+        ADDGT   R5, R5, R8                      ; and adjust DestB to match
+
+        SUBS    R8, R3, R11                     ; R8 = SrcT - GWTRow
+        SUBGT   R3, R3, R8                      ; if > 0 then SrcT := GWTRow
+        SUBGT   R7, R7, R8                      ; and adjust DestT to match
+
+; now R0-R3 is source windowed both ways
+;     R4-R7 is dest   windowed both ways
+
+        ADD     R8, WsPtr, #CMDest3L
+        STMIA   R8, {R4-R7}                     ; save destination windowed
+                                                ; both ways
+
+        CMP     R2, R0                          ; check SrcR >= SrcL
+        SUBGES  R8, R3, R1                      ; and SrcT >= SrcB (R8=lines-1)
+        BLT     EraseDest                       ; if not, then go to wiping out
+                                                ; destination stage
+
+        STR     R8, [WsPtr, #CMVertCount]       ; no. of vertical lines -1
+
+        LDR     R9, [WsPtr, #Log2BPC]
+        LDR     R10, [WsPtr, #XShftFactor]
+        LDR     R11, [WsPtr, #NPix]
+
+        MOV     R8, R6, LSR R10                 ; DestR word offset
+        SUB     R8, R8, R4, LSR R10             ; -DestL word offset
+        STR     R8, [WsPtr, #CMDestCount]       ; No. of dest words -1
+
+        AND     R8, R6, R11                     ; R8 = DestR pixel offset
+        ADD     R14, WsPtr,#RAMMaskTb
+        LDR     R8, [R14, R8, LSL #2]           ; R8 = mask for right pixel
+        SUB     R14, R8, #1             ; In rh mask, set all bits lower than
+        ORR     R14, R14, R8            ; pixel, RHM := RHM OR (RHM-1)
+        STR     R14, [WsPtr, #CMRMask]
+
+        AND     R8, R4, R11                     ; R8 = DestL pixel offset
+        ADD     R14, WsPtr, #RAMMaskTb
+        LDR     R8, [R14, R8, LSL #2]           ; R8 = mask for left pixel
+        RSB     R14, R8, #0             ; In lh mask, set all bits higher than
+        ORR     R14, R14, R8            ; pixel, LHM := LHM OR (0-LHM)
+        STR     R14, [WsPtr, #CMLMask]
+
+        CMP     R0, R4                          ; test whether SrcL >= DestL
+        BLT     SrcHLTDest
+
+; source is to the right of dest, so start at left hand side
+
+        AND     R8, R4, R11                     ; R8 = DestL pixel offset
+        AND     R14, R0, R11                    ; R14 = SrcL pixel offset
+        SUB     R11, R14, R8                    ; R11 = Src-Dest pixel offset
+        MOV     R11, R11, LSL R9                ; R11 = Src-Dest bit offset
+
+        CMP     R11, #0                         ; if rightshift < 0
+        ADDLT   R11, R11, #32                   ; if < 0, correct result
+        MOVGE   R10, #0
+        MOVLT   R10, #-4                        ; and subtract 4 off src addr
+
+        STR     R11, [WsPtr, #CMRShift]
+        RSB     R11, R11, #32
+        STR     R11, [WsPtr, #CMLShift]
+
+        LDR     R6, [WsPtr, #LineLength]        ; (no longer need DestR)
+
+        CMP     R1, R5                          ; if SrcB >= DestB
+        RSBGE   R6, R6, #0                      ; then go upwards
+        STR     R6, [WsPtr, #CMVertDir]
+
+        MOVLT   R1, R3                          ; else go down
+        MOVLT   R5, R7                          ; so use top coords
+
+        BL      ScreenAddr                      ; R2 = address of src corner
+        ADD     R2, R2, R10
+        STR     R2, [WsPtr, #CMSourceAddr]
+
+        MOV     R0, R4
+        MOV     R1, R5
+        BL      ScreenAddr                      ; R2 = address of dest corner
+        STR     R2, [WsPtr, #CMDestAddr]
+
+        ADD     R11, WsPtr, #CMStuff
+        LDMIA   R11, {R0-R6}            ; src,dest,cnt,rtshf,lfshf,rtmsk,ltmsk
+10
+        TEQ     R2, #0                  ; only one word on a line ?
+        ANDEQ   R5, R5, R6              ; then rightmask:=rightmask AND lftmask
+        LDREQ   R14, [R0], #4           ; and load first word up
+        BEQ     %FT45                   ; and do endword
+
+; do first word
+
+        LDMIA   R0!, {R11,R14}          ; load 2 words
+        ShiftR  R11, R14, R3, R4        ; shift them
+        AND     R11, R11, R6            ; AND with leftmask
+        LDR     R10, [R1]               ; load word from dest
+        BIC     R10, R10, R6            ; clear out bits in leftmask
+        ORR     R10, R10, R11           ; OR in new bits
+        STR     R10, [R1], #4
+        SUBS    R2, R2, #1              ; decrement count
+        BEQ     %FT40                   ; if zero, do finish word
+
+        SUBS    R2, R2, #7              ; can we do 7 words ?
+        BCC     %FT30                   ; no, then do 1 word at a time
+
+; do 7 words at a time
+
+        TEQ     R3, #0                  ; if rightshf = 0
+        BEQ     %FT60                   ; then do non-shifting version
+20
+ [ {TRUE}                               ; TMD optimisation 12-May-93
+        MOV     R5, R14, LSR R3
+        LDMIA   R0!, {R6-R11,R14}
+        ORR     R5, R5, R6, LSL R4
+ |
+        MOV     R5, R14
+        LDMIA   R0!, {R6-R11,R14}       ; load next 7 words
+        ShiftR  R5, R6, R3, R4
+ ]
+        ShiftR  R6, R7, R3, R4
+        ShiftR  R7, R8, R3, R4
+        ShiftR  R8, R9, R3, R4
+        ShiftR  R9, R10, R3, R4
+        ShiftR  R10, R11, R3, R4
+        ShiftR  R11, R14, R3, R4
+        STMIA   R1!, {R5-R11}
+        SUBS    R2, R2, #7
+        BCS     %BT20
+
+30
+        ADDS    R2, R2, #7
+        BEQ     %FT40                   ; if count expired, do last word
+
+; do 1 word at a time
+
+35
+        MOV     R5, R14
+        LDR     R14, [R0], #4
+        ShiftR  R5, R14, R3, R4
+        STR     R5, [R1], #4
+        SUBS    R2, R2, #1
+        BNE     %BT35
+
+; do last word
+
+40
+        LDR     R5, [WsPtr, #CMRMask]   ; load right mask
+45
+
+; now test if any bits would be used (so we don't go off end of memory)
+
+        MOV     R11, #&FFFFFFFF
+        TST     R5, R11, LSL R4         ; NE => safe to read from here
+        LDRNE   R11, [R0], #4           ; R14 = left word; R11 = right word
+        ShiftR  R14, R11, R3, R4        ; form single word
+        AND     R14, R14, R5            ; mask source word
+        LDR     R10, [R1]               ; load dest word
+        BIC     R10, R10, R5            ; mask dest word
+        ORR     R10, R10, R14           ; OR two words
+        STR     R10, [R1], #4           ; store back
+
+; now go on to next row
+
+        LDR     R7, [WsPtr, #CMVertCount]
+        SUBS    R7, R7, #1
+        BCC     EraseDest               ; finished, so go to erase dest stage
+        STR     R7, [WsPtr, #CMVertCount]
+        ADD     R11, WsPtr, #CMStuff
+        LDMIA   R11, {R0-R6}            ; load up info again
+        LDR     R7, [WsPtr, #CMVertDir]
+        ADD     R0, R0, R7              ; move source pointer
+        ADD     R1, R1, R7              ; and dest pointer
+        STMIA   R11, {R0,R1}            ; store these back
+        B       %BT10                   ; and loop
+
+; non-shifting version, for speed
+; do 7 words at a time
+
+60
+        MOV     R5, R14
+        LDMIA   R0!, {R6-R11,R14}       ; load next 7 words
+        STMIA   R1!, {R5-R11}
+        SUBS    R2, R2, #7
+        BCS     %BT60
+        ADDS    R2, R2, #7
+        BNE     %BT35                   ; count not expired, do last few words
+        B       %BT40                   ; count expired, do last word
+
+; *****************************************************************************
+
+; source is to the left of dest, so start at right hand side
+
+SrcHLTDest ROUT
+        MOV     R0, R2                          ; rt coords are relevant ones
+        MOV     R4, R6
+
+        AND     R8, R4, R11                     ; R8 = DestR pixel offset
+        AND     R14, R0, R11                    ; R14 = SrcR pixel offset
+        SUB     R11, R14, R8                    ; R11 = Src-Dest pixel offset
+        MOV     R11, R11, LSL R9                ; R11 = Src-Dest bit offset
+
+        RSB     R11, R11, #32                   ; R11 = leftshift
+        CMP     R11, #32                        ; if >= 32
+        SUBCS   R11, R11, #32                   ; then put in range
+        MOVCC   R10, #4                         ; else add 4
+        MOVCS   R10, #0                         ; to src addr
+
+        STR     R11, [WsPtr, #CMLShift]
+        RSB     R11, R11, #32
+        STR     R11, [WsPtr, #CMRShift]
+
+        LDR     R6, [WsPtr, #LineLength]        ; (no longer need R6)
+
+        CMP     R1, R5                          ; if SrcB >= DestB
+        RSBGE   R6, R6, #0                      ; then go upwards
+        STR     R6, [WsPtr, #CMVertDir]
+
+        MOVLT   R1, R3                          ; else go down
+        MOVLT   R5, R7                          ; so use top coords
+
+        BL      ScreenAddr                      ; R2 = address of src corner
+        ADD     R2, R2, R10
+        STR     R2, [WsPtr, #CMSourceAddr]
+
+        MOV     R0, R4
+        MOV     R1, R5
+        BL      ScreenAddr                      ; R2 = address of dest corner
+        STR     R2, [WsPtr, #CMDestAddr]
+
+        ADD     R11, WsPtr, #CMStuff
+        LDMIA   R11, {R0-R6}            ; src,dest,cnt,rtshf,lfshf,rtmsk,ltmsk
+10
+        TEQ     R2, #0                  ; only one word on a line ?
+        ANDEQ   R6, R6, R5              ; then leftmask:=leftmask AND rightmask
+        LDREQ   R5, [R0], #-4           ; and load first word up
+        BEQ     %FT45                   ; and do endword
+
+; do first word
+
+        LDMDA   R0!, {R11,R14}          ; load 2 words
+        ShiftL  R11, R14, R3, R4        ; shift them
+        AND     R14, R14, R5            ; AND with rightmask
+        LDR     R10, [R1]               ; load word from dest
+        BIC     R10, R10, R5            ; clear out bits in rightmask
+        ORR     R10, R10, R14           ; OR in new bits
+        STR     R10, [R1], #-4
+        MOV     R5, R11
+        SUBS    R2, R2, #1              ; decrement count
+        BEQ     %FT40                   ; if zero, do finish word
+
+        SUBS    R2, R2, #7              ; can we do 7 words ?
+        BCC     %FT30                   ; no, then do 1 word at a time
+
+; do 7 words at a time
+
+        TEQ     R4, #0                  ; if leftshf=0
+        BEQ     %FT60                   ; then do non-shifting version
+20
+ [ {TRUE}                               ; TMD optimisation 12-May-93
+        MOV     R14, R5, LSL R4
+        LDMDA   R0!, {R5-R11}
+        ORR     R14, R14, R11, LSR R3
+ |
+        MOV     R14, R5
+        LDMDA   R0!, {R5-R11}           ; load next 7 words
+        ShiftL  R11, R14, R3, R4
+ ]
+        ShiftL  R10, R11, R3, R4
+        ShiftL  R9, R10, R3, R4
+        ShiftL  R8, R9, R3, R4
+        ShiftL  R7, R8, R3, R4
+        ShiftL  R6, R7, R3, R4
+        ShiftL  R5, R6, R3, R4
+        STMDA   R1!, {R6-R11,R14}
+        SUBS    R2, R2, #7
+        BCS     %BT20
+
+30
+        ADDS    R2, R2, #7
+        BEQ     %FT40                   ; if count expired, do last word
+
+; do 1 word at a time
+
+35
+        MOV     R14, R5
+        LDR     R5, [R0], #-4
+        ShiftL  R5, R14, R3, R4
+        STR     R14, [R1], #-4
+        SUBS    R2, R2, #1
+        BNE     %BT35
+
+; do last word
+
+40
+        LDR     R6, [WsPtr, #CMLMask]   ; load left mask
+45
+
+; now test if any bits would be used (so we don't go off start of memory)
+
+        MOV     R11, #&FFFFFFFF
+        TST     R6, R11, LSR R3         ; NE => safe to read from here
+        LDRNE   R11, [R0], #-4          ; R11 = left word; R5 = right word
+        ShiftL  R11, R5, R3, R4         ; form single word
+        AND     R5, R5, R6              ; mask source word
+        LDR     R10, [R1]               ; load dest word
+        BIC     R10, R10, R6            ; mask dest word
+        ORR     R10, R10, R5            ; OR two words
+        STR     R10, [R1], #-4          ; store back
+
+; now go on to next row
+
+        LDR     R7, [WsPtr, #CMVertCount]
+        SUBS    R7, R7, #1
+        BCC     EraseDest               ; finished, so go to erase dest stage
+        STR     R7, [WsPtr, #CMVertCount]
+        ADD     R11, WsPtr, #CMStuff
+        LDMIA   R11, {R0-R6}            ; load up info again
+        LDR     R7, [WsPtr, #CMVertDir]
+        ADD     R0, R0, R7              ; move source pointer
+        ADD     R1, R1, R7              ; and dest pointer
+        STMIA   R11, {R0,R1}            ; store these back
+        B       %BT10                   ; and loop
+
+; non-shifting version, for speed
+; do 7 words at a time
+
+60
+        MOV     R14, R5
+        LDMDA   R0!, {R5-R11}           ; load next 8 words
+        STMDA   R1!, {R6-R11,R14}
+        SUBS    R2, R2, #7
+        BCS     %BT60
+        ADDS    R2, R2, #7
+        BNE     %BT35                   ; count not expired, do last few words
+        B       %BT40                   ; count expired, do last word
+
+; *****************************************************************************
+
+; Erase the area Dest2\Dest3
+
+EraseDest ROUT
+
+; first do the flat rectangle
+
+        ADD     R8, WsPtr, #CMDest2L
+        LDMIA   R8, {R0-R7}             ; R0..R3 = Dest2; R4..R7 = Dest3
+        BL      EraseDifference
+
+; and drop thru to ...
+
+EraseSource
+        LDR     R8, [WsPtr, #CMCopyFlag] ; 0 => move, 2 => copy
+        TEQ     R8, #0                  ; is it a move
+        Return  NE                      ; no, then exit
+
+        ADD     R8, WsPtr, #CMSrc       ; R0..R3 = unclipped src
+        LDMIA   R8, {R0-R7}             ; R4..R7 = unclipped dest
+
+; window both source and destination in source domain
+
+        LDR     R10, [WsPtr, #GWLCol]
+        LDR     R11, [WsPtr, #GWRCol]
+
+        SUBS    R8, R10, R0             ; R8 = GWLCol - SrcL
+        ADDGT   R0, R0, R8              ; if > 0 then SrcL := GWLCol
+        ADDGT   R4, R4, R8              ; and adjust DestL to match
+
+        SUBS    R8, R2, R11             ; R8 = SrcR - GWRCol
+        SUBGT   R2, R2, R8              ; if > 0 then SrcR := GWRCol
+        SUBGT   R6, R6, R8              ; and adjust DestR to match
+
+        CMP     R2, R0                  ; check SrcR >= SrcL
+        Return  LT                      ; if not then nothing to erase
+
+        LDR     R10, [WsPtr, #GWBRow]
+        LDR     R11, [WsPtr, #GWTRow]
+
+        SUBS    R8, R10, R1             ; R8 = GWBRow - SrcB
+        ADDGT   R1, R1, R8              ; if > 0 then SrcB := GWBRow
+        ADDGT   R5, R5, R8              ; and adjust DestB to match
+
+        SUBS    R8, R3, R11             ; R8 = SrcT - GWTRow
+        SUBGT   R3, R3, R8              ; if > 0 then SrcT := GWTRow
+        SUBGT   R7, R7, R8              ; and adjust DestT to match
+
+        CMP     R3, R1                  ; check SrcT >= SrcB
+        Return  LT                      ; if not then nothing to erase
+
+; now window the dest coords to the source
+
+        CMP     R7, R3                  ; if DestT >= SrcT
+        MOVGE   R7, R3                  ; then DestT := SrcT
+        MOVLT   R5, R1                  ; else DestB := SrcB
+        CMP     R6, R2                  ; if DestR >= SrcR
+        MOVGE   R6, R2                  ; then DestR := SrcR
+        MOVLT   R4, R0                  ; else DestL := SrcL
+
+        Pull    R14
+
+; and drop thru to ...
+
+; *****************************************************************************
+;
+;       EraseDifference - Erase the difference between two rectangles with at
+;                         least one vertical and one horizontal shared boundary
+;
+; in:   R0-R3 = larger one
+;       R4-R7 = smaller one
+;
+
+EraseDifference ROUT
+
+; first do the flat rectangle
+
+        CMP     R6, R4                  ; check for Dest3 being null
+        CMPGE   R7, R5
+        BLT     RectFillA               ; if is, just clear Dest2
+
+        Push    "R0-R7,R14"
+
+        TEQ     R3, R7                  ; if Dest2T = Dest3T
+        SUBEQ   R3, R5, #1              ; then top = Dest3B -1
+        ADDNE   R1, R7, #1              ; else bottom = Dest3T +1
+        CMP     R3, R1                  ; if top >= bottom
+        BLGE    RectFillA
+
+; now do the tall rectangle
+
+        Pull    "R0-R7"
+
+        TEQ     R3, R7                  ; if Dest2T = Dest3T
+        MOVEQ   R1, R5                  ; then bottom = Dest3B
+        MOVNE   R3, R7                  ; else top = Dest3T
+
+        TEQ     R0, R4                  ; if Dest2L = Dest3L
+        ADDEQ   R0, R6, #1              ; then left = Dest3R +1
+        SUBNE   R2, R4, #1              ; else right = Dest3L -1
+
+        CMP     R3, R1                  ; if top >= bottom
+        CMPGE   R2, R0                  ; and right >= left
+        BLGE    RectFillA               ; then fill it
+
+        Pull    PC
+
+
+        END
diff --git a/s/vdu/vdugrafdec b/s/vdu/vdugrafdec
new file mode 100644
index 0000000000000000000000000000000000000000..d7311c39c88772763b1f371a90f5956ecf50d5ea
--- /dev/null
+++ b/s/vdu/vdugrafdec
@@ -0,0 +1,308 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > &.Source.VduGrafDec
+;
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Graphics workspace and macro declarations
+;
+; Author R C Manby
+; Date   5.9.86
+;
+
+        GBLL UseNewerHLine
+UseNewerHLine SETL 1=1
+
+
+;
+; Graphics work space, overlaid in main vdu w/s area
+;
+
+
+  ^ GraphicWs
+
+Line1Blk # 4*9  ;Parameter block for a crudeline
+        ASSERT @ < EndGraphicWs
+
+
+  ^ GraphicWs
+
+LineBlk # 4*5  ;Parameter block for a crudeline
+
+LineEcfBase # 4                                         ; --
+LineEcfIndx # 4                                         ;   |
+LineDeltaY  # 4                                         ;   |
+LineScanStp # 4                                         ; --
+
+LineEndPtFlags # 4      ;Bit0 set - plot start pt, bit1 set - plot end pt
+LineDotPtr     # 4      ;Holds 0/Adr(LineDotCnt) for Solid/Dotted lines
+
+DotCycleStartX # 4
+DotCycleStartY # 4
+DotCycleEndX   # 4
+DotCycleEndY   # 4
+DotCycleCount  # 4
+PostCycleCount # 4
+
+;The following are defined in Hdr.System
+; LineDotCnt     # 4      ;Count down to restarting pattern
+; LineDotPatLSW  # 4      ;Current state of pattern LSWord
+; LineDotPatMSW  # 4      ;   "      "   "     "    MSWord
+        ASSERT @ < EndGraphicWs
+
+
+  ^ GraphicWs
+
+TLine1  # 4*9   ;Line parameters used by Tri & Par fill
+TLine2  # 4*9
+TEndY   # 4
+Vertex1X # 4    ;                                        --
+Vertex1Y # 4    ;                                          |
+Vertex2X # 4    ;                                          |
+Vertex2Y # 4    ;                                          |
+Vertex3X # 4    ;                                          |
+Vertex3Y # 4    ;                                          |
+Vertex4X # 4    ;                                          |
+Vertex4Y # 4    ;                                        --
+        ASSERT @ < EndGraphicWs
+
+
+  ^ GraphicWs
+
+CircleBlk # 4*8 ;Parameter block for circle arc segments etc   --
+CLine0  # 4*9   ;Line parameters used by Arc, Segment & Sector   |
+CLine1  # 4*9   ;                                              --
+CLine2  # 4*9   ;CLine2 & CLine3 used only for segments
+CLine3  # 4*9   ;
+
+UpperSegLinePtr # 4     ;Address of CLine2 or 0 - used only for segments
+LowerSegLinePtr # 4     ;Address of CLine3 or 0 - used only for segments
+
+CircleRadSquare # 4     ;Square of radius of circle
+
+CLine0EndX * CLine0+7*4
+CLine0EndY * CLine0+8*4
+CLine1EndX * CLine1+7*4
+CLine1EndY * CLine1+8*4
+CLine0Far  * CLine0
+CLine0Near * CLine0EndX
+CLine1Far  * CLine1
+CLine1Near * CLine1EndX
+
+Quad0Control # 1        ;Control bytes for each quadrant for   --
+Quad1Control # 1        ; Arc, Segment & Sector plotting         |
+Quad2Control # 1        ;                                        |
+Quad3Control # 1        ;                                        |
+                        ;                                        |
+Quad0StateChange # 1    ;Flag to indicate line/circle            |
+Quad1StateChange # 1    ; intersection                           |
+Quad2StateChange # 1    ;                                        |
+Quad3StateChange # 1    ;                                        |
+                        ;                                        |
+Quad0Draw # 1           ;Controls point/line plotting            |
+Quad1Draw # 1           ;                                        |
+Quad2Draw # 1           ;                                        |
+Quad3Draw # 1           ;                                      --
+
+ArcPoint0X   # 4        ;                                      --
+ArcPoint0Y   # 4        ;                                        |
+ArcPoint1X   # 4        ;                                        |
+ArcPoint1Y   # 4        ;                                        |
+ArcPoint2X   # 4        ;                                        |
+ArcPoint2Y   # 4        ;                                        |
+ArcPoint3X   # 4        ;                                        |
+ArcPoint3Y   # 4        ;                                      --
+
+        ASSERT @ < EndGraphicWs
+
+
+  ^ GraphicWs
+
+EllBlk     # 4*8        ;Parameter block for ellipses
+
+EllBlkSliceCnt * EllBlk
+EllBlkEllY     * EllBlk + 4*4
+
+
+EllPrevL   # 4          ;Slice limits for previous line        --
+EllPrevR   # 4          ;                                        |
+EllThisL   # 4          ;                 current line           |
+EllThisR   # 4          ;                                        |
+EllNextL   # 4          ;                 next line              |
+EllNextR   # 4          ;                                      --
+
+EllHLineWs # 4*5        ;This could be pushed to the stack
+EllDoubleHLineWs # 4*5  ;This could be pushed to the stack
+        ASSERT @ < EndGraphicWs
+
+
+  ^ GraphicWs
+
+CMSrc       # 4*4       ;Unclipped source area                 --
+CMDest      # 4*4       ;Unclipped destination area              |
+CMCopyFlag  # 4         ; 0/2 means Move/Copy area             --
+
+CMDest2L    # 4         ; destination coords clipped at dest
+CMDest2B    # 4
+CMDest2R    # 4
+CMDest2T    # 4
+CMDest3L    # 4         ; destination coords clipped both ways
+CMDest3B    # 4
+CMDest3R    # 4
+CMDest3T    # 4
+
+CMStuff # 0             ; these 7 loaded together
+CMSourceAddr # 4        ; source screen address
+CMDestAddr # 4          ; destination screen address
+CMDestCount # 4         ; no. of destination words per line -1
+CMRShift # 4            ; LSR shift factor
+CMLShift # 4            ; LSL shift factor
+CMRMask # 4             ; right mask
+CMLMask # 4             ; left mask
+
+CMVertCount # 4         ; no. of lines to do -1
+CMVertDir # 4           ; offset to add to source/dest on each line
+
+        ASSERT @ < EndGraphicWs
+
+
+  ^ GraphicWs
+
+LineFillBlk # 4 * 11
+
+FldLeftXLimit  # 4                              ;--
+FldY           # 4                              ;  |
+FldRightXLimit # 4                              ;--
+
+FldBoundaryCol  # 4
+FldBoundaryFlag # 4
+FldYWindLimit   # 4
+
+QueuePtrs       # 4*4   ; head, tail, end, start
+
+FldSaveArea     # 5*4   ; saved Y, target colour, NPix, zgora, zgeor
+FldSaveY * FldSaveArea +0
+
+FldStackLevel   # 4
+                [ med_00001_userma
+flood_cda_rma   # 4     ; amount we've changed the rma size by
+                ]
+
+        ASSERT @ < EndGraphicWs
+
+
+  ^ GraphicWs
+
+RetnReg0  # 4           ;Save area for SWI SpriteOp    --
+RetnReg1  # 4           ;                                |
+RetnReg2  # 4           ;                                |
+RetnReg3  # 4           ;                                |
+RetnReg4  # 4           ;                                |
+RetnReg5  # 4           ;                                |
+RetnReg6  # 4           ;                                |
+RetnReg7  # 4           ;                                |
+RetnReg8  # 4           ;                                |
+RetnReg9  # 4           ;                                |
+RetnLink  # 4           ;                              --
+
+SprReadNColour  # 4     ;Vdu vars for the mode the     --
+SprWriteNColour # 4     ; the sprite is in               |
+SprBytesPerChar # 4     ;                                |
+SprXShftFactor  # 4     ;                                |
+SprNPix         # 4     ;                                |
+SprLog2BPC      # 4     ;                              --
+
+NameBuf   # 16          ; 12 char name + gap for good measure
+
+SpriteWs # 0
+
+
+
+  ^ SpriteWs            ;Sprite plot & ScreenLoad
+
+SPltWidth   # 4         ;Don't try re-arranging this lot unless
+SPltHeight  # 4         ; you fully understand the code!!
+SPltScrOff  # 4
+SPltMemOff  # 4
+SPltScrAdr  # 4
+SPltColCnt  # 4
+SPltMemAdr  # 4
+SPltShftR   # 4
+SPltShftL   # 4
+SPltMskAdr  # 4
+SPltLMask   # 4
+SPltRMask   # 4
+; SPltzgooPtr # 4
+SPltEcfPtr  # 4
+SPltEcfIndx # 4
+SPltPixPerWord # 4
+SPltBPP     # 4
+SPltMaskBit # 4
+SPltMaskPtr # 4
+SPltMaskRowBit # 4
+SPltMaskRowPtr # 4
+SPltMaskRowLen # 4
+
+SPltzgooMasks # 16      ; zgoo, zgeo, zgoe, zgee
+
+ScrLoaHandle  # 4       ;     --
+ScrLoaBufAdr  # 4       ;       |
+ScrLoaBytes   # 4       ;       |
+ScrLoaFilPtr  # 4       ;       |
+ScrLoaFilOfst # 4       ;     --
+
+ScrLoaAreaCB   # SpriteAreaCBsize
+
+SPltAction # 4                          ; Plot action used (0 => store)
+
+SloadModeSel # 48                       ; Mode selector for screenloading new sprites
+
+        ASSERT @ < EndGraphicWs
+
+
+  ^ SpriteWs            ;SGet,SCreate & ScreenSave
+
+SGetTopLeft # 8         ; top and left of 'on screen' area
+
+SGetTopMargin     # 4
+SGetBotMargin     # 4
+SGetLWrdMargin    # 4
+SGetLBitMargin    # 4
+SGetRWrdMargin    # 4
+SGetRBitMargin    # 4
+
+SGetColWCnt  # 4
+SGetRowOfst  # 4
+SGetEcfIndx  # 4
+
+SGetNext          # 4                               ; --
+SGetName          # 12  ;Name is 3 words (12 chars) ;   |
+SGetWidth         # 4                               ;   |
+SGetHeight        # 4                               ;   |
+SGetLBit          # 4                               ;   |
+SGetRBit          # 4                               ;   |
+SGetImage         # 4                               ;   |
+SGetTrans         # 4                               ;   |
+SGetMode          # 4                               ;   |
+SGetPalette       # 0                               ; --
+
+        ASSERT @ < EndGraphicWs
+
+        ^       ScrSavCommon
+ScrSavAreaCB      # SpriteAreaCBsize
+ScrSavSpriteCB    # SpriteCBsize + MaxSpritePaletteSize
+
+        END
diff --git a/s/vdu/vdugrafe b/s/vdu/vdugrafe
new file mode 100644
index 0000000000000000000000000000000000000000..7cebda2714ba152ca6bbfbf23a068666aef5d035
--- /dev/null
+++ b/s/vdu/vdugrafe
@@ -0,0 +1,874 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduGrafE
+;
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Vdu driver code - Line Fill L & R and Flood Fill
+;
+; Author R C Manby
+; Date   17.10.86
+;
+
+; *****************************************************************************
+;
+; FillLRnonBg
+; ===========
+;
+; On entry, R0 (X), R1 (Y) hold point to fill from
+;
+; On exit, R0..R11 corrupt
+;
+
+FillLRtoFg
+        ADD     R2, WsPtr, #FgEcf
+        MOV     R3, #0
+        B       FillLRnonBg10           ; Fill L <-> R
+
+FillLRnonBg
+        ADD     R2, WsPtr, #BgEcf
+        MOV     R3, #&80000000
+FillLRnonBg10
+        SaveRetAdr
+        BL      FillAlong2
+        SUBCS   R1, R1, #1              ; indicate nothing filled by Y-=1
+        STR     R0, [WsPtr, #GCsIX]     ; left X
+        STR     R1, [WsPtr, #GCsIY]     ; Y
+        Return  CS                      ; external new pt hasn't changed
+        STR     R2, [WsPtr, #NewPtX]    ; right X
+        MOV     R0, R2
+        BL      IEGB                    ; update external version of new NewPt
+        Return                          ; then things get shuffled
+
+; *****************************************************************************
+;
+; FillLRtoBg    (Really only fill right!)
+; ==========
+;
+; On entry, R0 (X), R1 (Y) hold point to fill from
+;
+; On exit, R0..R11 corrupt
+;
+
+FillLRnonFg
+        ADD     R2, WsPtr, #FgEcf
+        MOV     R3, #&80000000
+        B       FillLRtoBg10            ; Fill -> R
+
+FillLRtoBg
+        ADD     R2, WsPtr, #BgEcf
+        MOV     R3, #&0
+FillLRtoBg10
+        SaveRetAdr
+        STR     R0, [WsPtr, #GCsIX]     ; "left hand point" = given point
+        STR     R1, [WsPtr, #GCsIY]
+        BL      GenLineFillParm         ; if C=1 (nothing can be plotted)
+        SUBCS   R0, R0, #1              ; then NewPtX-=1 ie NewPtX < GCsIX
+        BLCC    FillLineRightRegs       ; else fill right
+        STR     R0, [WsPtr, #NewPtX]    ; Convert NewPt(X,Y) (internal) into
+        LDR     R1, [WsPtr, #NewPtY]    ; GCs(X,Y) (external)
+        BL      IEGB
+        Return                          ; On returning, cursors are shuffled
+                                        ; which updates GCsI(X,Y)
+
+; *****************************************************************************
+;
+; GenLineFillParm
+; ===============
+;
+; On entry, R0 (X), R1 (Y) hold point to fill from
+;           R2 points to delimiting colour (FgEcf/BgEcf)
+;           R3 fill flag, 0/&80000000 for fill to/to-non
+;
+; On exit, Carry=0, valid parameter setup, R0-R10 contain copy of control block
+;          Carry=1, point outside window, R0 preserved, R1-R10 undefined
+;
+;  Format of control block
+;
+;  R0  - StartX
+;  R1  - DelimitColour
+;  R2  - FillFlag
+;  R3  - ScreenAdr
+;  R4  - PixelMsk
+;  R5  - zgora
+;  R6  - zgeor
+;  R7  - GWLCol
+;  R8  - GWRCol
+;  R9  - BytesPerChar
+;  R10 - NPix
+;
+GenLineFillParm
+        WINDow  R0,R1, R7,R8,R9,R10     ; Window(R0,R1) using R7-R10
+                                        ;  gives GE if in window
+        ORRLTS  PC, Link, #C_bit        ; If point outside window
+                                        ;  then quit with carry set
+        Push    "R2,R3,R7,R9,Link"      ; Save colourptr, fillflag
+                                        ; GWLCol, GWRCol
+        BL      ScreenAddr              ; Returns R2 = Addr, R3 = Mask
+
+        LDR     R9, [WsPtr, #YWindLimit]
+        SUB     R9, R9, R1              ; Flip Ycoord
+        AND     R9, R9, #7              ; Line within ecf
+        LDR     R5, [WsPtr, #GColAdr]   ; Base address of ecf pattern
+        ADD     R5, R5, R9, LSL #3
+        LDMIA   R5, {R5, R6}            ; Get zgora,zgeor
+
+        MOV     R4, R3
+        MOV     R3, R2
+
+        Pull    "R1,R2,R7,R8,Link"      ; restore other registers
+        LDR     R1, [R1, R9, LSL #2]    ; load delimiting colour
+        LDR     R9, [WsPtr, #BytesPerChar]
+        LDR     R10, [WsPtr, #NPix]
+
+        ADD     R11, WsPtr, #LineFillBlk ; now save completed control block
+        STMIA   R11, {R0-R10}
+
+        LDR     R11, [R3]               ; load screen
+        EOR     R11, R11, R1            ; screen EOR boundary
+        TST     R11, R4                 ; just look at this pixel
+        TEQ     R2, PC, LSL #1          ; N := (Z EOR R2) ie invert test if
+                                        ; stopping on non-colour
+        BICPLS  PC, Link, #C_bit        ; point can be filled, exit CC
+        ORRMIS  PC, Link, #C_bit        ; point cannot be filled, exit CS
+
+; *****************************************************************************
+;
+; FillLineRight - Fill right from point upto delimiting colour
+; =============    or graphics window
+;
+;
+;  R0  - RightLimitX
+;  R1  - LimitColour
+;  R2  - FillFlag
+;  R3  - ScreenAdr
+;  R4  - PixelMsk
+;  R5  - zgora
+;  R6  - zgeor
+;  R7  - GWLCol /BPC/NPIX
+;  R8  - GWRCol
+;  R9  -          ScreenWord
+;  R10 - NPix
+;  R11 -          MultPixMsk     / temp
+;
+; out:  PSR must be preserved
+;
+FillLineRight ROUT
+        ADD     R11, WsPtr, #LineFillBlk
+        LDMIA   R11, {R0-R10}
+FillLineRightRegs
+        Push    R14
+        RSB     R7, R9, #32             ; shift factor for next pixel position
+        SUB     R0, R8, R0
+10
+        MOV     R11, #0                 ; clear partial word mask
+        LDR     R9, [R3]                ; screen word EOR delimit colour
+        EOR     R14, R9, R1
+30
+        TST     R14, R4                 ; test pixel position for delimiting
+        TEQ     R2, PC, LSL #1          ; invert condition if necessary
+
+        ORRPL   R11, R11, R4            ; add pixel to partial word mask
+        SUBPLS  R0, R0, #1              ; "increment" X; if on edge of window
+        BMI     %FT40                   ; then give up and plot partial word
+
+        TEQ     R4, #0                  ; test for end of word
+        MOV     R4, R4, ROR R7          ; shift pixel mask left
+        BPL     %BT30
+
+        AND     R14, R5, R11            ; fill partial word, using pixel mask
+        AND     R11, R6, R11            ; in R11
+        ORR     R9, R9, R14
+        EOR     R9, R9, R11
+        STR     R9, [R3], #4
+
+        CMP     R2, #2                  ; CC => filling until pixel=target
+        BCC     %BT10                   ; so do it slowly (goto next word)
+
+        RSB     R0, R0, #0              ; make X in range GWLCol-GWRCol .. 0
+        ADDS    R0, R0, R10             ; move R0 to end of word
+        BGT     %FT36                   ; outside window, continue slowly
+
+        ORR     R14, R1, R5             ; word to store if going fast
+        EOR     R14, R14, R6
+
+32
+        LDR     R9, [R3]                ; screen word
+        CMP     R9, R1                  ; EOR target colour (CS if EQ)
+        BNE     %FT36
+        STR     R14, [R3], #4
+        ADCS    R0, R0, R10             ; C=1, so add NPix+1
+        BLE     %BT32
+36
+        SUBS    R0, R10, R0             ; R0 back to start of word and negate
+        BGE     %BT10                   ; if still inside, continue slowly
+        B       %FT50
+
+40
+        AND     R14, R5, R11            ; fill partial word, using pixel mask
+        AND     R11, R6, R11            ; in R11
+        ORR     R9, R9, R14
+        EOR     R9, R9, R11
+        STR     R9, [R3]
+50
+        SUB     R0, R8, R0              ; make back into normal coord again
+        SUB     R0, R0, #1              ; correct end point
+        Pull    R14
+        MOVS    PC, R14
+
+; *****************************************************************************
+;
+; FillLineLeft - Fill left from point upto delimiting colour
+; ============    or graphics window
+;
+;                N.B. Starts one pixel left of point given, since
+;                      FillLineRight will have already plotted it.
+;
+;  R0  - LeftLimitX
+;  R1  - LimitColour
+;  R2  - FillFlag
+;  R3  - ScreenAdr
+;  R4  - PixelMsk
+;  R5  - zgora
+;  R6  - zgeor
+;  R7  - GWLCol                 } Slightly different from FillLineRight
+;  R8  - GWRCol /BPC/NPIX       }
+;  R9  -          ScreenWord
+;  R10 -          LimitEorScreen / temp
+;  R11 -          MultPixMsk     / temp
+;
+;
+;
+FillLineLeft ROUT
+        ADD     R11, WsPtr, #LineFillBlk
+        LDMIA   R11, {R0-R10}
+FillLineLeftRegs
+        Push    R14
+        MOV     R8, R9                  ; shift factor for next pixel position
+
+        SUB     R0, R0, R7
+        LDR     R9, [R3]                ; screen word EOR delimit colour
+        EOR     R14, R9, R1
+        MOVS    R11, #0                 ; clear partial word mask (and set PL)
+        B       %FT31
+
+10
+        MOV     R11, #0                 ; clear partial word mask
+        LDR     R9, [R3]                ; screen word EOR delimit colour
+        EOR     R14, R9, R1
+
+30
+        TST     R14, R4                 ; test pixel position for delimiting
+        TEQ     R2, PC, LSL #1          ; invert condition if appropriate
+        ORRPL   R11, R11, R4            ; add pixel to partial word mask
+31
+        SUBPLS  R0, R0, #1              ; decrement X; if on edge of window
+        BMI     %FT40                   ; then give up and plot partial word
+
+        MOVS    R4, R4, ROR R8          ; test for end of word
+        BPL     %BT30                   ; loop until mask exhausted
+
+        AND     R14, R5, R11            ; fill partial word, using pixel mask
+        AND     R11, R6, R11            ; in R11
+        ORR     R9, R9, R14
+        EOR     R9, R9, R11
+        STR     R9, [R3], #-4
+
+        CMP     R2, #2                  ; CC => filling until pixel=target
+        BCC     %BT10                   ; so do it slowly (goto next word)
+
+        SUBS    R0, R0, R10             ; move R0 to beginning of word
+        BCC     %FT36                   ; if outside window continue slowly
+        ADD     R10, R10, #1
+        ORR     R14, R1, R5             ; compute target word
+        EOR     R14, R14, R6
+32
+        LDR     R9, [R3]                ; screen word
+        CMP     R9, R1
+        BNE     %FT35                   ; NZ => terminate
+        STR     R14, [R3], #-4
+        SUBS    R0, R0, R10
+        BCS     %BT32
+35
+        SUB     R10, R10, #1
+36
+        ADDS    R0, R0, R10             ; point R0 back to end of word
+        BGE     %BT10                   ; if still inside, continue but slowly
+        B       %FT50
+
+40
+        AND     R14, R5, R11            ; fill partial word, using pixel mask
+        AND     R11, R6, R11            ; in R11
+        ORR     R9, R9, R14
+        EOR     R9, R9, R11
+        STR     R9, [R3]
+50
+        ADD     R0, R0, R7              ; add GWLCol back on
+        ADD     R0, R0, #1              ; Correct endpoint value
+        Pull    R14
+        MOVS    PC, R14
+
+; *****************************************************************************
+;
+; FillAlong - Fill left and right from point given
+; =========
+;
+; On entry, R0 (X), R1(Y) hold point to fill from
+;
+; On exit,  R0 (X) End of scan left
+;           R1 (Y) Preserved
+;           R2 (X) End of scan right
+;
+;           Carry set = no fill performed ie outside window or already filled
+;           Carry clear = fill occured
+;
+        ASSERT  FldBoundaryFlag = FldBoundaryCol +4
+
+FillAlong
+        ADD     R2, WsPtr, #FldBoundaryCol
+        LDMIA   R2, {R2,R3}             ; R2 = colour, R3 = flag
+FillAlong2                              ; entry point when R2, R3 loaded
+        Push    "R1, R14"
+        BL      GenLineFillParm         ; On exit C=1 if nothing can be plotted
+        BLCC    FillLineRightRegs
+        Push    R0                      ; New RightX
+        BLCC    FillLineLeft
+        Pull    R2                      ; Recover RightX
+        Pull    "R1, PC"                ; Recover Y, C=1 <=> fill occured
+
+; *****************************************************************************
+;
+; FloodFill
+; =========
+;
+; On entry, R0 (X), R1 (Y) hold point to flood from
+;           R2 Delimit colour, pointer to FgEcf/BgEcf
+;           R3 0/&80000000 for flood until/over
+;
+        ASSERT  FldBoundaryFlag = FldBoundaryCol +4
+        ASSERT  FldY = FldLeftXLimit +4
+        ASSERT  FldRightXLimit = FldLeftXLimit +8
+
+FloodToFg
+        ADD     R2, WsPtr, #FgEcf       ; delimit colour
+        MOV     R3, #0                  ; flood until
+        B       FloodFill
+
+FloodNonBg
+        ADD     R2, WsPtr, #BgEcf       ; delimit colour
+        MOV     R3, #&80000000          ; flood over
+FloodFill ROUT
+        Push    R14
+        STR     R13, [WsPtr, #FldStackLevel]    ; save stack level !
+
+        ADD     R14, WsPtr, #FldBoundaryCol
+        LDR     R4, [WsPtr, #YWindLimit]
+        STMIA   R14, {R2-R4}            ; store colour, flag, YWindLimit
+
+        [ med_00001_userma
+
+        ; the idea here is to try to use between 48k and 128k of rma instead of 16k of
+        ; scratchspace. if there's less than 48k of rma in the first place we go back to
+        ; scratchspace. if there's 128k or more we claim the 128k and proceed. For values
+        ; between 48k and 128k we first grow the rma by 128k-largest_free_space. If this
+        ; results in a lump of 128k now being free we claim it and proceed. If the largest
+        ; free space hasn't grown to 128k we then claim the additional balance needed to
+        ; have 128k available. Should either grow fail, we just use whatever (over 48k)
+        ; was available
+
+        ;R3-R7 spare at presenet
+        Push    "R0-R2"
+        MOV     R0, #0
+        STR     R0, [WsPtr, #flood_cda_rma]                     ; set amount to change dyn. area by
+        MOV     R0, #ModHandReason_RMADesc
+        SWI     XOS_Module                                      ; get the largest free space in rma
+        BVC     %FT91                                           ; if that failed, use scratchspace
+
+use_scratchspace
+        LDR     R3, =FldQueueStart                              ; head ptr (== scratchspace)
+        MOV     R4, R3                                          ; tail ptr
+        ADD     R5, R3, #FldQueueSize                           ; end ptr (== scratchspace size)
+        MOV     R6, R3                                          ; start ptr
+        B       %FT92
+91
+        CMP     R2, #smallest_rma_size                          ; compare against small threshold
+        BLT     use_scratchspace                                ; not enough free - use scratchspace
+        CMP     R2, #largest_rma_size                           ; compare against high threshold
+        BHS     %FT96                                           ; lots free - use largest ceiling value
+
+        RSB     R1, R2, #largest_rma_size                       ; work out how much more to claim
+        MOV     R0, #1                                          ; rma
+        SWI     XOS_ChangeDynamicArea
+        STRVC   R1, [WsPtr, #flood_cda_rma]                     ; save the amount we grew the rma
+        BVS     %FT94                                           ; if it failed, use the minimum<size<maximum lump
+
+        MOV     R0, #ModHandReason_RMADesc                      ; reread the largest free space to see if
+        SWI     XOS_Module                                      ; it has risen to maximum
+        BVS     use_scratchspace
+        CMP     R2, #largest_rma_size
+        BHS     %FT96                                           ; it has, so claim the maximum
+
+        LDR     R1, [WsPtr, #flood_cda_rma]                     ; the free space didn't grow to maximum, so the
+        RSB     R1, R1, #largest_rma_size                       ; largest space wasn't at the end. Do a second
+        MOV     R0, #1                                          ; grow of the rma to make the maximum
+        SWI     XOS_ChangeDynamicArea
+        LDRVC   R0, [WsPtr, #flood_cda_rma]                     ; if it succeeded, update the amount that we
+        ADDVC   R0, R0, R1                                      ; need to shrink the rma by
+        STRVC   R0, [WsPtr, #flood_cda_rma]
+                                                                ; and fall into the claim largest space routine
+94
+        MOV     R0, #ModHandReason_RMADesc
+        SWI     XOS_Module
+        BVS     use_scratchspace                                ; find the largest lump now available
+        CMP     R2, #largest_rma_size
+96
+        MOVGT   R3, #largest_rma_size
+        MOVLE   R3, R2                                          ; cap it at the maximum size we want
+        MOV     R0, #ModHandReason_Claim                        ; try to claim it
+        SWI     XOS_Module
+        BVS     use_scratchspace
+
+        ADD     R5, R2, R3                                      ; do it in a slightly different order so
+        MOV     R3, R2                                          ; we can use the R2 and R3 back from the claim
+        MOV     R4, R3                                          ; (R2=where, R3=size)
+        MOV     R6, R3
+
+92
+        ADD     R7, WsPtr, #QueuePtrs
+        STMIA   R7, {R3, R4, R5, R6}    ; initialise queue
+        Pull    "R0-R2"
+        |
+        LDR     R3, =FldQueueStart      ; head ptr
+        MOV     R4, R3                  ; tail ptr
+        ADD     R5, R3, #FldQueueSize   ; end ptr
+        MOV     R6, R3                  ; start ptr
+        ADD     R7, WsPtr, #QueuePtrs
+        STMIA   R7, {R3, R4, R5, R6}    ; initialise queue
+        ]
+
+        BL      FillAlong               ; try filling L&R from given point
+        Pull    PC, CS                  ; quit if can't be filled
+
+        MOV     R3, #3                  ; bit0 => fill up, bit1 => fill down
+10
+        ADD     R14, WsPtr, #FldLeftXLimit
+        STMIA   R14, {R0-R2}            ; store leftX, Y, rightX
+
+        TST     R3, #1
+        BEQ     %FT20
+
+        LDR     R10, [WsPtr, #GWTRow]   ; if below Top of window
+        CMP     R10, R1
+        BLE     %FT20
+
+        ADD     R4, WsPtr, #FldBoundaryCol      ; R4=target colour ptr,R5=flag,
+        LDMIA   R4, {R4,R5,R6}                  ; R6=YWindLimit
+
+        ORR     R5, R5, #FillingUpBit   ; fill line above
+        Push    R3
+        BL      CheckAlong              ; (will bomb out if queue explodes)
+        Pull    R3
+
+        ADD     R14, WsPtr, #FldLeftXLimit
+        LDMIA   R14, {R0,R1}            ; reload leftX, Y
+20
+        TST     R3, #2
+        BEQ     %FT30
+
+        LDR     R10, [WsPtr, #GWBRow]   ; if above bottom of window
+        CMP     R1, R10
+        BLE     %FT30
+
+        ADD     R4, WsPtr, #FldBoundaryCol      ; R4=target colour ptr,R5=flag,
+        LDMIA   R4, {R4,R5,R6}                  ; R6=YWindLimit
+
+;        BIC     R5, R5, #FillingUpBit  ; fill line below
+        BL      CheckAlong              ; (will bomb out if queue explodes)
+30
+        ADD     R11, WsPtr, #QueuePtrs  ; unqueue one item
+        LDMIA   R11, {R3,R4,R5}         ; head,tail,limit
+        TEQ     R3, R4                  ; if queue empty
+        [ med_00001_userma
+        BLEQ    release_memory
+        ]
+        Pull    PC, EQ                  ; then exit
+
+        [ med_00001_twowords
+        LDR     R1, [R3], #4
+        MOV     R0, R1, LSR #16         ; LeftX
+        MOV     R2, R1
+        EOR     R2, R2, R0, LSL #16     ; RightX (with LeftX EORed out)
+        LDR     R1, [R3], #4            ; Recover Y
+        |
+        LDR     R1, [R3], #4            ; then load word
+        MOV     R2, R1, LSR #21         ; Recover RightX
+        EOR     R1, R1, R2, LSL #21     ; clear those bits out
+        MOV     R0, R1, LSR #10         ; Recover LeftX
+        EOR     R1, R1, R0, LSL #10     ; Recover Y
+        ]
+        TEQ     R3, R5                  ; if at limit
+        LDREQ   R3, [R11, #12]          ; then reload with start
+        STR     R3, [R11, #0]           ; store head ptr back
+
+        MOV     R3, #2                  ; indicate fill down
+        CMP     R2, R0                  ; if right < left then subtract 1 off
+                                        ; larger and swap
+        SBCCC   R0, R0, R2              ; R0 := large-small-1
+        ADDCC   R2, R0, R2              ; R2 := (large-small-1)+small = large-1
+        SUBCC   R0, R2, R0              ; R0 := (large-1)-(large-small-1)=small
+        MOVCC   R3, #1                  ; and indicate fill up
+        B       %BT10
+
+; *****************************************************************************
+;
+; CheckAlong - Fill and skip background between LeftXLimit & RightXLimit
+; ==========
+;
+; On entry, R0 LeftXLimit
+;           R1 Y
+;           R4 target colour
+;           R5 fill flag
+;           R6 YWindLimit
+;
+; Exits using R14 if normal termination
+; Exits using FldStackLevel if queue full
+;
+
+FillingUpBit * 1
+
+        ASSERT  FldBoundaryFlag = FldBoundaryCol +4
+        ASSERT  FldYWindLimit = FldBoundaryCol +8
+
+        ASSERT  FldY = FldLeftXLimit +4
+        ASSERT  FldRightXLimit = FldLeftXLimit +8
+
+CheckAlong ROUT
+        Push    R14
+
+        TST     R5, #FillingUpBit               ; if going up
+        ADDNE   R1, R1, #1                      ; then increment Y
+        SUBEQ   R1, R1, #1                      ; else decrement Y
+
+        BL      ScreenAddr                      ; R2=screen addr, R3=mask
+
+        SUB     R6, R6, R1                      ; flip Y
+        AND     R6, R6, #7                      ; ecf offset
+        LDR     R4, [R4, R6, LSL #2]            ; R4=target colour
+        LDR     R7, [WsPtr, #GColAdr]           ; base address of plot ecf
+        ADD     R7, R7, R6, LSL #3              ; R7 -> zgora, zgeor
+        LDMIA   R7, {R10,R11}                   ; R10=zgora, R11=zgeor
+        LDR     R6, [WsPtr, #BytesPerChar]      ; R6=pixel shift
+        LDR     R9, [WsPtr, #NPix]              ; R9=no. of pixels per word-1
+
+        ADD     R7, WsPtr, #FldSaveArea         ; save Y, target colour,
+        STMIA   R7, {R1, R4, R9, R10, R11}      ; NPix, zgora, zgeor
+
+; test first pixel, to see if we can go left
+
+        LDR     R7, [R2]
+        EOR     R1, R7, R4
+        TST     R1, R3
+        TEQ     R5, PC, LSL #1          ; PL => it's fillable
+        RSBMI   R6, R6, #32             ; not fillable, reverse pixel shift
+        LDRMI   R8, [WsPtr, #GWRCol]    ; load right window limit
+        BMI     %FT37                   ; and skip left+right filling bit
+
+        Push    "R0, R2, R3"            ; save X, screen address, mask
+        LDR     R8, [WsPtr, #GWLCol]
+        SUB     R0, R0, R8              ; make X in range 0..GWRCol-GWLCol
+        MOVS    R14, #0                 ; clear partial word mask (and set PL)
+        B       %FT14
+
+10
+        MOV     R14, #0                 ; clear partial word mask
+        LDR     R7, [R2]                ; R7 = screen
+        EOR     R1, R7, R4
+12
+        TST     R1, R3                  ; test pixel position for delimiter
+        TEQ     R5, PC, LSL #1          ; PL => it's fillable
+        ORRPL   R14, R14, R3            ; add pixel to partial word mask
+14
+        SUBPLS  R0, R0, #1              ; decrement X; if on edge of window
+        BMI     %FT22                   ; then give up and plot partial word
+
+        MOVS    R3, R3, ROR R6          ; test for end of word
+        BPL     %BT12                   ; loop until mask wraps
+
+        AND     R1, R10, R14            ; R1 := zgora AND pixmask
+        ORR     R7, R7, R1              ; screen := screen OR R1
+        AND     R1, R11, R14            ; R1 := zgeor AND pixmask
+        EOR     R7, R7, R1              ; screen := screen EOR R1
+        STR     R7, [R2], #-4
+
+        CMP     R5, #2                  ; CC => filling until target
+        BCC     %BT10                   ; so do it slowly (do next word)
+
+        SUBS    R0, R0, R9              ; move R0 to start of word
+        BCC     %FT20                   ; if outside window continue slowly
+
+        ADD     R9, R9, #1              ; R9 := pixels per word
+        ORR     R1, R4, R10             ; compute word to plonk on screen
+        EOR     R1, R1, R11
+16
+        LDR     R7, [R2]                ; screen word
+        CMP     R7, R4
+        BNE     %FT18                   ; NZ => terminate
+        STR     R1, [R2], #-4
+        SUBS    R0, R0, R9              ; back another word
+        BCS     %BT16
+18
+        SUB     R9, R9, #1              ; R9 := pixels per word -1
+20
+        ADDS    R0, R0, R9              ; point R0 back to end of word
+        BGE     %BT10                   ; if still inside, continue but slowly
+        B       %FT24                   ; else exit
+
+        [ :LNOT: AssemblingArthur
+        MALIGN   16, 0                  ; try to get %FT30 aligned !
+        ]
+22
+        AND     R1, R10, R14            ; R1 := zgora AND pixmask
+        ORR     R7, R7, R1              ; screen := screen OR R1
+        AND     R1, R11, R14            ; R1 := zgeor AND pixmask
+        EOR     R7, R7, R1              ; screen := screen EOR R1
+        STR     R7, [R2]
+24
+        ADD     R0, R0, R8              ; add GWLCol back on
+        ADD     R0, R0, #1
+        Pull    "R1, R2, R3"            ; restore X, screen addr, mask
+        Push    R0                      ; save left-of-line coordinate
+        MOV     R0, R1                  ; R0 = start X
+        RSB     R6, R6, #32             ; reverse pixel shift
+        LDR     R8, [WsPtr, #GWRCol]    ; load right window limit
+
+; now the filling right part
+
+26
+        SUB     R0, R8, R0              ; make X in range GWRCol-GWLCol .. 0
+
+; start of each word
+
+27
+        MOV     R14, #0
+        LDR     R7, [R2]                ; screen word
+        EOR     R1, R7, R4
+28
+        TST     R1, R3                  ; test pixel position for delimiter
+        TEQ     R5, PC, LSL #1          ; PL => it's fillable
+        ORRPL   R14, R14, R3            ; add pixel to partial word mask
+        SUBPLS  R0, R0, #1              ; "increment" X; if on edge of window
+        BMI     %FT34                   ; then give up and plot partial word
+
+        TEQ     R3, #0                  ; test for end of word
+        MOV     R3, R3, ROR R6          ; shift pixel mask
+        BPL     %BT28                   ; loop until mask wraps
+
+        AND     R1, R10, R14            ; R1 := zgora AND pixmask
+        ORR     R7, R7, R1              ; screen := screen OR R1
+        AND     R1, R11, R14            ; R1 := zgeor AND pixmask
+        EOR     R7, R7, R1              ; screen := screen EOR R1
+        STR     R7, [R2], #4
+
+        CMP     R5, #2                  ; CC => filling until target
+        BCC     %BT27                   ; so do it slowly (do next word)
+
+        RSB     R0, R0, #0              ; make X in range GWLCol-GWRCol .. 0
+        ADDS    R0, R0, R9              ; move R0 to end of word
+        BGT     %FT32                   ; outside window, continue slowly
+
+        ORR     R1, R4, R10             ; word to store if going fast
+        EOR     R1, R1, R11
+
+        [ :LNOT: AssemblingArthur
+        ASSERT  ((.-ArthurVduDriver):AND:15) = 0
+        ]
+30
+        LDR     R7, [R2]                ; screen word
+        CMP     R7, R4                  ; EOR target colour (CS if EQ)
+        BNE     %FT32                   ; NZ => terminate
+        STR     R1, [R2], #4
+        ADCS    R0, R0, R9              ; C=1, so add NPix +1
+        BLE     %BT30
+32
+        SUBS    R0, R9, R0              ; R0 back to start of word and negate
+        BGE     %BT27                   ; if still inside, continue slowly
+        B       %FT36                   ; else exit
+
+34
+        AND     R1, R10, R14            ; R1 := zgora AND pixmask
+        ORR     R7, R7, R1              ; screen := screen OR R1
+        AND     R1, R11, R14            ; R1 := zgeor AND pixmask
+        EOR     R7, R7, R1              ; screen := screen EOR R1
+        STR     R7, [R2]
+36
+        SUB     R0, R8, R0              ; make back into normal coord again
+
+; now queue this segment
+
+        Pull    R4                      ; R4 := LH end point
+        SUB     R7, R0, #1              ; R7 := corrected RH end point
+        LDR     R1, [WsPtr, #FldSaveY]  ; reload Y coordinate of THIS segment
+        BL      Queue
+
+        ADD     R14, WsPtr, #FldLeftXLimit      ; load lxlim, (Y+?), rxlim
+        LDMIA   R14, {R7,R10,R11}
+
+        EOR     R5, R5, #FillingUpBit   ; invert direction
+
+        SUB     R7, R7, #2              ; lxlim -2
+        CMP     R4, R7                  ; if leftX <= lxlim-2
+        BLLE    Queue                   ; then Q LH end bit in opposite dir
+
+        ADD     R4, R11, #2             ; rxlim +2
+        SUB     R7, R0, #1              ; R7 = RH end point
+        CMP     R7, R4                  ; if rightX >= rxlim+2
+        BLGE    Queue                   ; then Q RH end bit in opposite dir
+
+        EOR     R5, R5, #FillingUpBit   ; invert direction back again
+
+        SUB     R4, R4, #3              ; rxlim -1
+        CMP     R7, R4                  ; if rightX >= rxlim-1
+        Pull    PC, GE                  ; no point in skipping any more
+
+; now do the skipping
+; R0 points to first non-fillable pixel; R3=correct mask for this pixel
+
+        ADD     R7, WsPtr, #FldSaveArea         ; reload target colour, NPix,
+        LDMIB   R7, {R4, R9, R10, R11}          ; zgora, zgeor
+37
+        LDR     R1, [WsPtr, #FldRightXLimit]
+38
+        LDR     R7, [R2], #4
+        EOR     R7, R7, R4                      ; screen EOR target
+40
+        TST     R7, R3                          ; if pixel is fillable
+        TEQ     R5, PC, LSL #1
+        SUBPL   R2, R2, #4                      ; then correct address
+        Push    R0, PL                          ; and push new X coord
+        BPL     %BT26                           ; and go to fill right bit
+50
+        CMP     R0, R1                          ; on limit of segment ?
+        Pull    PC, CS                          ; yes, so give up
+        ADD     R0, R0, #1                      ; step coord
+        TEQ     R3, #0                          ; test for mask wrap
+        MOV     R3, R3, ROR R6                  ; rotate mask to next pixel
+        BPL     %BT40                           ; continue within this word
+        B       %BT38                           ; continue with next word
+
+; *****************************************************************************
+;
+;       Queue - Queue a line segment
+;
+; in:   R1 = Y coordinate of segment
+;       R4 = left X coordinate
+;       R5 = fill flag (Bit0=0/1 => look downwards/upwards)
+;       R7 = right X coordinate
+;
+; out:  R0-R8, R11, R12 preserved
+;       R9,R10 corrupted
+;       exits by pulling stacked R14 if queue was full
+;
+
+Queue   ROUT
+        Push    "R0,R11,R14"
+        TEQ     R5, R5, LSR #1          ; CS => mangle coords
+
+        MOVCC   R9, R4
+        MOVCC   R10, R7
+
+        SUBCS   R9, R7, R4              ; R9 = R-L
+        SUBCS   R10, R7, R9             ; R10 = R-(R-L) = L
+        ADCCS   R9, R9, R10             ; R9 = (R-L)+L+1 = R+1
+
+        [ med_00001_twowords
+        ; revised packing, expanded to two words, LeftX in 31..16 of word 1,
+        ; RightX in 15..0 of word 1, and Y in 15..0 of word 2
+
+        MOV     R9, R9, LSL #16         ; shift LeftX up
+        ORR     R9, R9, R10             ; combine in RightX
+
+        ADD     R0, WsPtr, #QueuePtrs
+        LDMIA   R0, {R10, R11, R14}     ; head,tail,limit
+
+        STR     R9, [R11], #4           ; store word at tail and move on
+        STR     R1, [R11], #4           ; store second word
+        |
+        ; pack Y into bits 0..9, L into bits 10..20, R into bits 21..31
+
+        ORR     R9, R1, R9, LSL #10     ; R9 = Y + (L << 10)
+        ORR     R9, R9, R10, LSL #21    ; R9 = Y + (L << 10) + (R <<21)
+
+        ADD     R0, WsPtr, #QueuePtrs
+        LDMIA   R0, {R10, R11, R14}     ; head,tail,limit
+
+        STR     R9, [R11], #4           ; store word at tail and move on
+        ]
+
+        TEQ     R11, R14                ; if at limit
+        LDREQ   R11, [R0, #12]          ; then reload with start ptr
+        CMP     R11, R10                ; EQ and CS if queue full
+        STRNE   R11, [R0, #4]           ; store tail ptr back if OK
+
+        Pull    "R0,R11,PC", NE         ; queue not full, terminate normally
+
+        [ med_00001_userma
+        BL      release_memory
+        ]
+        LDR     R13, [WsPtr, #FldStackLevel]
+        Pull    PC                      ; else bomb out
+
+; *****************************************************************************
+
+        [ med_00001_userma
+release_memory
+        ROUT
+        Push   "R0-R5,lr"
+
+        ADD    R0, WsPtr, #QueuePtrs
+        LDMIA  R0, {R0-R3}
+
+        [ med_00001_debug
+        LDR    R0, =med_00001_debug_start
+        STR    R3, [R0], #4
+        STR    R2, [R0], #4
+        LDR    R5, [WsPtr, #flood_cda_rma]
+        STR    R5, [R0]
+        ]
+
+        LDR    R5, =FldQueueStart
+
+        CMP    R3, R5                   ; did we use scratchspace ?
+        BEQ    %FT20
+
+        MOV    R2, R3
+        MOV    R0, #ModHandReason_Free
+        SWI    XOS_Module
+20
+        LDR    R0, [WsPtr, #flood_cda_rma]
+        CMP    R0, #0
+        BEQ    %FT30                    ; no need to shrink rma
+
+        RSB    R1, R0, #0               ; get the signed negative value
+        MOV    R0, #1                   ; rma
+        SWI    XOS_ChangeDynamicArea
+30
+        CMP    r0, r0                   ; ensure we return EQ set
+        Pull   "r0-r5,pc"
+        ]
+
+; *****************************************************************************
+
+        END
diff --git a/s/vdu/vdugraff b/s/vdu/vdugraff
new file mode 100644
index 0000000000000000000000000000000000000000..b86a3bd4b4aa7f4789dfbd4437cfe3f4f90c0cae
--- /dev/null
+++ b/s/vdu/vdugraff
@@ -0,0 +1,880 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > &.Source.VduGrafF
+;
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Vdu driver code - Solid & Dotted Line drawing routines
+;
+; Author R C Manby
+; Date   27.10.86
+;
+
+
+;
+;
+;
+;------------------------------------------------------------------------------
+;
+; DoOsbyte163_242 - Set dotted line length and return status information
+; ===============
+;
+; We are only interested in OsByte 163,242,0..66
+;
+; OsByte 163,242,0..64 set dotted line length
+;        163,242,0      set default dot pattern and length
+;        163,242,1..64  set specified pattern length
+;
+; OsByte 163,242,65 return graphics status
+;                   returns R1 = &Cn (means GXR on & flood on (for GXR))
+;                                    (means SpriteRom on (for master/compact))
+;                                  n = dotted line length MOD 64
+;                           R2 = number of sprite pages
+;
+; OsByte 163,242,66 return sprite status
+;                   returns R1 = R2 = 0 if no sprite selected
+;                           R1 = current sprite width
+;                           R2 = current sprite height
+;
+; When entered, we know it is FX 163,242,n
+; Exit by MOV PC, R14 if still don't understand (ie n>66)
+; Otherwise exit with Pull PC
+
+DoOsbyte163_242 ROUT
+        CMP R2,#66
+        MOVHI PC, R14                   ; Not 0..66, so pass it on
+
+        Push "R3-R12"                   ; Preserve the world
+        VDWS WsPtr                      ; Point R12 at Vdu driver workspace
+
+        CMP R2,#65
+        BLLT SetPatLength               ; R2 holds 0/1..64    ; PSR preserved
+        BLT OsByte_QuitA
+        BGT OsByte163_242_66
+
+OsByte163_242_65                        ; OsByte 163,242,65 - graphics status
+        LDR R1,[WsPtr,#DotLineLength]
+        AND R1,R1,#&3F                  ; Dot count MOD 64
+        ORR R1,R1,#&C0                  ; GXR on, Flood on
+        LDR R2,[WsPtr,#SpAreaStart]
+        CMP R2,#0
+        LDRNE R2,[R2,#saEnd]            ; SpriteWS size
+        B OsByte_QuitA
+
+OsByte163_242_66                        ; OsByte 163,242,66 - sprite status
+
+        MOV R0,#SpriteReason_ReadSpriteSize
+        LDR R2,=SpChooseName
+        ADD R2,R2,WsPtr                 ; Sprite name ptr
+
+        SWI XOS_SpriteOp
+
+        MOVVS R1,#0                     ; If no sprite memory, or no sprite
+        MOVVS R2,#0                     ; chosen, return 0,0 as width,height
+
+        MOVVC R1,R3                     ; else return width,height in pixels
+        MOVVC R2,R4
+
+OsByte_QuitA
+        Pull "R3-R12,PC"                ; restore registers and claim call
+;
+;
+;
+;------------------------------------------------------------------------------
+
+
+
+
+        MACRO
+        WriteToScreen $Adr,$Msk,$Ecf, $a,$b,$c
+        LDMIA $Ecf,{$a,$b}
+        AND $a,$a,$Msk
+        AND $b,$b,$Msk
+        LDR $c,[$Adr]
+        ORR $c,$c,$a
+        EOR $c,$c,$b
+        STR $c,[$Adr]
+        MEND
+
+
+;
+;
+;
+;------------------------------------------------------------------------------
+
+
+
+
+rXCnt    RN 0
+rYCnt    RN 1
+rBres    RN 2
+rDeltaY  RN 3
+rPixShft RN 4
+rScrAdr  RN 5
+rPixMsk  RN 6
+rDotPtr  RN 7
+
+rEcfPtr  RN 8
+
+rEcfBase RN 8
+rEcfIndx RN 9
+rDeltaX  RN 10
+rScanStp RN 11
+
+rDotCnt    RN 9
+rDotPatLSW RN 10
+rDotPatMSW RN 11
+
+rDOTCnt    RN 10
+rDOTPatLSW RN 11
+rDOTPatMSW RN 14
+
+
+;
+;------------------------------------------------------------------------------
+;
+; LineDraw - General line drawing routine
+; ========
+;
+; On entry, ICursor is the start point of the line
+;           NewPt is the end point
+;
+;           R2 holds plot code, where bits mean :-
+;              bit 5  clear/set - include/exclude initial point
+;              bit 4  clear/set - solid/dotted line
+;              bit 3  clear/set - include/exclude final point
+;
+;              bit 5  implies   - restart/continue dot pattern
+;
+LineDrawSolid
+        TST     R2, #&03                ; if a no action one
+        MOVEQ   PC, R14                 ; then do nowt, just shunt coords
+LineDrawDotted ROUT
+        EOR     R11, R2, #&18   ; Flip solid/dotted flag, and final point flag
+                                ; bit 4 set/clear - solid/dotted line
+                                ; bit 3 set/clear - include/exclude final point
+        MOV     R9, #0
+        ADD     R10, WsPtr, #LineDotCnt
+
+        TST     R11, #&10
+        STRNE   R9, [WsPtr, #LineDotPtr]        ; Solid line
+        STREQ   R10, [WsPtr, #LineDotPtr]       ; Dotted line
+
+        TSTEQ   R11, #&20               ; If dotted line & first point included
+        STREQ   R9, [WsPtr, #LineDotCnt] ; then force a restart of the pattern
+
+        STR     R9, [WsPtr, #PostCycleCount]    ; Assume all dots get plotted
+
+        ADD R0,WsPtr,#GCsIX             ;Start ICursor
+        LDMIA R0,{R0,R1,R2,R3}          ;End NewPt
+
+        TEQ     R1, R3                  ; If line is horizontal
+        BEQ     TryHLine                ; try to use HLine
+        [ UseVLineOnSolidLines
+        TEQ     R0, R2                  ; If line is vertical
+        BEQ     TryVLine                ; try to use VLine
+CantUseVLine
+        ]
+CantUseHLineOrVLine
+        SaveRetAdr
+
+        Difference R4,R0,R2
+        Difference R5,R1,R3
+        Greatest R10,R4,R5
+        ADD R10,R10,#1                  ;Total number of dots on line
+
+        BL GenLineParm                  ;Generate line control block
+                                        ; in R0..R8
+
+        TST R11,#&20                    ;If first point excluded
+        BLNE AdvLineParm                ; then advance to first pixel and
+        SUBNE R10,R10,#1                ;      dec DotCycleCount
+
+        TST R11,#&08                    ;If last point excluded
+        SUBEQ R10,R10,#1                ; then dec DotCycleCount
+
+        CMP R10,#0                      ;IF DotCycleCount <= 0
+        Return LE                       ; then nothing to plot
+
+        STR R10,[WsPtr,#DotCycleCount]
+        STR R11,[WsPtr,#LineEndPtFlags]
+
+        WINDow R0,R1, R9,R10,R11,R14    ;If start point outside window
+        BLLT LineStartsOutsideWindow    ; then panic {won't return if whole
+                                        ;             line outside window}
+
+        WINDow R7,R8, R9,R10,R11,R14    ;If end point outside window
+        BLLT LineEndsOutsideWindow      ; then panic as well
+
+;
+; R0    ,R1    ,R2  ,R3    ,R4    ,R5   ,R6
+; StartX,StartY,Bres,DeltaX,DeltaY,StepX,StepY
+;
+
+                                        ;Modify StepX (+1/-1 means right/left)
+        CMP R5,#0                       ; to give PixelMask shift factor
+        LDR R5,[WsPtr,#BytesPerChar]    ; "Left"
+        RSBPL R5,R5,#0                  ; "Right"
+
+                                        ;Modify StepY (+1/-1 means up/down)
+        CMP R6,#0                       ; to give offset to next scan line
+        LDR R6,[WsPtr,#LineLength]      ; "Down"
+        RSBPL R6,R6,#0                  ; "Up"
+
+        Push "R2-R6"                    ;Bres,DeltaX,DeltaY,StepX,StepY
+
+        BL ScreenAddr
+        MOV R7,R2                       ;Screen Adr
+        MOV R8,R3                       ;Pixel Mask
+
+        LDR R9,[WsPtr, #YWindLimit]
+        SUB R9,R9,R1                    ;subtract Y from YWindLimit
+        AND R9,R9,#7                    ;EcfIndx
+
+        Pull "R2-R6"                    ;Bres,DeltaX,DeltaY,StepX,StepY
+
+        LDR R0,[WsPtr,#DotCycleCount]   ;Number of on screen pixels
+        CMP R0,#0                       ; An LPO line starting outside & ending
+        Return LE                       ;  on the window leaves zero dots!
+
+        LDR R1,[WsPtr,#LineDotPtr]
+        CMP R1,#0
+        BNE DotDashLine
+
+SolidLine ROUT
+        Push    R12
+        LDR     R1, [WsPtr, #GColAdr]           ; Base address of ECF
+        ADD     R1, R1, R9, LSL #3              ; current address of ECF
+;
+; R0    ,R1     ,R2  ,R3    ,R4    ,R5     ,R6     ,R7    ,R8  ,R9
+; PixCnt,EcfBase,Bres,DeltaX,DeltaY,MskShft,LineStp,ScrAdr,Mask,Indx
+;
+
+        LDMIA   R1,  {R9, R10}                  ; R9 = zgora; R10 = zgeor
+20
+        MOV     R12, R8
+30
+        SUBS    R0, R0, #1                      ; Dec pixel count
+        BEQ     %FT57                           ; if zero, then finish off
+
+40
+; Advance the screen address and pixel mask one pixel
+
+        TEQ     R2, #0                  ; If Bres positive
+        BPL     %FT55                   ; then advance in X dirn only
+                                        ; else advance in Y direction, which
+                                        ; may involve advancing X afterwards
+45
+        AND     R14, R9, R12                    ; R14 = zgora AND pixmask
+        LDR     R11, [R7]                       ; R11 = word from screen
+        ORR     R11, R11, R14                   ; OR with screen
+        AND     R14, R10, R12                   ; R14 = zgeor AND pixmask
+        EOR     R11, R11, R14                   ; EOR with screen
+        STR     R11, [R7], R6                   ; and store back, moving on
+
+        CMP     R6, #&80000000          ; C=1 => going up the screen
+        TSTCS   R1, #63                 ; so check being at word 0 of ECF
+        SUBCS   R1, R1, #8              ; and then subtract 2 words
+        ADDCC   R1, R1, #8              ; else add on 2 words first
+        TSTCC   R1, #63                 ; and then test for wrap
+        BEQ     %FT60
+        LDMIA   R1,  {R9, R10}          ; reload zgora and zgeor
+        ADDS    R2, R2, R3              ; Advance Bres, in Y dirn
+
+        BMI     %BT20                   ; [don't need to move in X direction]
+50
+        MOV     R12, #0                 ; clear total pixel mask
+55
+;
+; Advance in X direction
+;
+        CMP     R5, #&80000000          ; if +ve then RORing, so test if
+        TSTCC   R8, #1                  ; bottom bit set before shifting
+        MOV     R8, R8, ROR R5          ; shift word
+        TSTCS   R8, #1                  ; else test after shifting
+        SUB     R2, R2, R4              ; always advance Bres in X direction
+        ORREQ   R12, R12, R8            ; if not wrapped, OR in new pixel
+        BEQ     %BT30                   ; and loop
+
+57
+        AND     R14, R9, R12                    ; R14 = zgora AND pixmask
+        LDR     R11, [R7]                       ; R11 = word from screen
+        ORR     R11, R11, R14                   ; OR with screen
+        AND     R14, R10, R12                   ; R14 = zgeor AND pixmask
+        EOR     R11, R11, R14                   ; EOR with screen
+        STRCC   R11, [R7], #-4                  ; and store back
+        STRCS   R11, [R7], #4                   ; incrementing or decrementing
+
+        MOV     R12, R8                 ; reset total pixel mask
+
+        SUBS    R0, R0, #1
+        Pull    "R12, PC", LT           ; exit if COMPLETELY finished
+        BEQ     %BT57                   ; if no more to do, then output word!
+        TEQ     R2, #0                  ; test Bres again
+        BPL     %BT55
+        B       %BT45
+
+; come here when ECF wraps (ie every eight Y coords)
+
+60
+        ADDCS   R1, R1, #64             ; if wrap and going up, then add 64
+        SUBCC   R1, R1, #64             ; if wrap and going down, subtract 64
+        LDMIA   R1,  {R9, R10}          ; reload zgora and zgeor
+        ADDS    R2, R2, R3              ; Advance Bres, in Y dirn
+        BMI     %BT20                   ; [don't need to move in X direction]
+        B       %BT50
+
+; *****************************************************************************
+;
+;       TryHLine - Try to use HLine (we already know Y1=Y2)
+;
+; in:   R0 = X1
+;       R1 = R3 = Y
+;       R2 = X2
+;       R11 = plot code EOR &18
+;         bit 3 set => include last point
+;         bit 4 set => solid
+;         bit 5 set => exclude first point
+;
+
+TryHLine ROUT
+        TST     R11, #&10               ; is it dotted
+        BEQ     CantUseHLineOrVLine     ; yes, then can't use HLine
+
+        CMP     R2, R0
+        MOVGE   R4, #1
+        MOVLT   R4, #-1
+        TST     R11, #&20               ; if first point excluded
+        ADDNE   R0, R0, R4              ; then move R0 one pixel towards R2
+        TST     R11, #&08               ; if last point excluded
+        SUBEQ   R2, R2, R4              ; then move R2 one pixel towards R0
+
+        CMP     R2, R0                  ; check order again
+        EORLT   R0, R0, R2              ; make sure R0 <= R2
+        EORLT   R2, R0, R2
+        EORLT   R0, R0, R2
+        RSBLT   R4, R4, #0              ; if swapped, then invert sign of R4
+        TEQNE   R4, #1                  ; if order is now different
+                                        ; (and they're not equal now)
+        MOVNE   PC, R14                 ; then there's nothing to plot
+        B       NewHLine                ; else go and do it!
+
+; *****************************************************************************
+;
+;       TryVLine - Try to use VLine (we already know X1=X2)
+;
+; in:   R0 = R2 = X
+;       R1 = Y1
+;       R3 = Y2
+;       R11 = plot code EOR &18
+;         bit 3 set => include last point
+;         bit 4 set => solid
+;         bit 5 set => exclude first point
+;
+
+TryVLine ROUT
+        TST     R11, #&10               ; is it dotted
+        BEQ     CantUseHLineOrVLine     ; yes, then can't use VLine (or HLine)
+
+; now make sure that we are using a solid pattern (not an ECF)
+; this is true if the appropriate GCOL action is < 8
+
+        AND     R4, R11, #3             ; look at bottom 2 bits of R11
+        CMP     R4, #2                  ; to check which action
+                                        ; (already ruled out 0 (no action))
+        LDRCC   R4, [WsPtr, #GPLFMD]    ; <2 => 1 => foreground action
+        MOVEQ   R4, #4                  ; =2 => 2 => invert action
+        LDRHI   R4, [WsPtr, #GPLBMD]    ; >2 => 3 => background action
+        CMP     R4, #8                  ; is it a solid action
+        BCS     CantUseVLine
+
+        CMP     R3, R1
+        MOVGE   R4, #1
+        MOVLT   R4, #-1
+        TST     R11, #&20               ; if first point excluded
+        ADDNE   R1, R1, R4              ; then move R1 one pixel towards R3
+        TST     R11, #&08               ; if last point excluded
+        SUBEQ   R3, R3, R4              ; then move R3 one pixel towards R1
+
+        CMP     R3, R1                  ; check order again
+        EORLT   R1, R1, R3              ; make sure R1 <= R3
+        EORLT   R3, R1, R3
+        EORLT   R1, R1, R3
+        RSBLT   R4, R4, #0              ; if swapped, then invert sign of R4
+        TEQNE   R4, #1                  ; if order is now different
+                                        ; (and they're not equal now)
+        MOVNE   PC, R14                 ; then there's nothing to plot
+        B       NewVLine                ; else go and do it!
+
+
+;
+;
+;
+DotDashLine
+
+;
+; R0    ,R1    ,R2  ,R3    ,R4    ,R5     ,R6     ,R7    ,R8  ,R9
+; PixCnt,DotPtr,Bres,DeltaX,DeltaY,MskShft,LineStp,ScrAdr,Mask,Indx
+;
+
+DotDash20
+        LDMIA R1,{rDOTCnt,rDOTPatLSW,rDOTPatMSW}
+
+        CMP rDOTCnt,#0
+
+        ADDEQ rDOTCnt,WsPtr,#DotLineStyle               ;Restart pattern
+        LDMEQIA rDOTCnt,{rDOTPatLSW,rDOTPatMSW}
+        LDREQ rDOTCnt,[WsPtr,#DotLineLength]
+
+        SUB rDOTCnt,rDOTCnt,#1
+
+        MOVS rDOTPatMSW,rDOTPatMSW,LSL #1
+        ORR rDOTPatMSW,rDOTPatMSW,rDOTPatLSW,LSR #31
+        MOV rDOTPatLSW,rDOTPatLSW,LSL #1
+
+        STMIA R1,{rDOTCnt,rDOTPatLSW,rDOTPatMSW}
+
+        BCC DotDash30                           ;Don't plot this dot
+
+        LDR R10,[WsPtr,#GColAdr]                ;Base address of ECF
+        ADD R10,R10,R9,LSL #3                   ;Address of ECFora & ECFeor
+        WriteToScreen R7,R8,R10, R10,R11,R14
+
+DotDash30
+        SUBS R0,R0,#1                           ;Dec pixel count
+        BEQ DotDash60                           ;Finished on screen
+
+DotDash40
+;
+; Advance the screen address and pixel mask one pixel
+;
+
+        CMP R2,#0               ;If Bres positive
+        BPL DotDash50           ;then advance in X dirn only
+                                ;else advance in Y direction, which may
+                                ;     involve advancement in X afterwards
+
+        CMP R6,#0               ;Advance Ecf to next scanline
+        SUBLT R9,R9,#1          ; "Up" = (Old-1) Mod 7
+        ADDGE R9,R9,#1          ; "Doun" = (Old+1) Mod 7
+        AND R9,R9,#7
+
+        ADD R7,R7,R6                    ;Advance screen address one scanline
+
+        ADDS R2,R2,R3                   ;Advance Bres, in Y dirn
+        BMI DotDash20                   ;
+                                        ; may now need advancing in X dirn
+DotDash50
+;
+; Advance in X direction
+;
+; Rotate PixMsk to next pixel position, altering ScrAdr if we cross to
+;  the next word.
+;
+
+        TST     R8, R5                  ;If PixMsk at MSEnd of word
+                                        ; and shifting left
+        ADDMI   R7, R7, #4              ;then inc ScrAdr  {PixMsk will wrap}
+
+        MOVS    R8, R8, ROR R5          ;Move PixMsk
+
+                                        ;If PixMsk now at MSEnd of word
+        RSBMIS  R11, R5, #0             ; and shift was right
+        SUBMI   R7, R7, #4              ;then dec ScrAdr  {PixMsk wrapped}
+
+        SUB     R2, R2, R4              ;Advance Bres, in X dirn
+        B       DotDash20
+
+DotDash60
+
+        LDR R0,[WsPtr,#PostCycleCount]
+        CMP R0,#0
+        BLNE AdvanceDotPattern
+
+        Return
+
+        LTORG
+
+ DCD 0 ; *** Inserted for diagnostic purposes !  ***
+
+;
+;
+;
+;------------------------------------------------------------------------------
+;
+; LineEndsOutSideWindow
+; =====================
+;
+LineEndsOutsideWindow
+
+        Push "R0-R8,Link"       ;Push whole parameter block
+
+        ADD R0,WsPtr,#GCsIX             ;Start ICursor
+        LDMIA R0,{R0,R1,R2,R3}          ;End NewPt
+        Swap R0,R2
+        Swap R1,R3
+
+        BL GenLineParm                  ;Generate line control block
+                                        ; in R0..R8
+        LDR R11,[WsPtr,#LineEndPtFlags]
+
+        TST R11,#&08                    ;If last point excluded
+        BLEQ AdvLineParm                ; then advance to actual last point
+
+        Push "R0,R1"                    ;EndX,EndY
+
+        WindowRes R11,R0,R1, R7,R8,R9,R10       ;R11 := Window(End)
+
+;
+; R0  ,R1  ,R2  ,R3    ,R4    ,R5   ,R6   ,        ,R11
+; EndX,EndY,Bres,DeltaX,DeltaY,StepX,StepY,        ,WEnd
+;
+
+        TST R11,#&C                             ;If above/below window
+        BEQ LEO10
+
+        BL InYWind                              ; then bring Y into window
+
+        WindowRes R11,R0,R1, R7,R8,R9,R10       ;R11 := Window(NewEnd)
+
+LEO10
+        TST R11,#&3
+        BEQ LEO20                       ;If start outside X window
+
+        BL InXWind
+
+        WindowRes R11,R0,R1, R7,R8,R9,R10       ;R11 := Window(NewEnd)
+
+LEO20
+        Pull "R9,R10"                           ;EndX,EndY
+
+        Difference R0,R0,R9
+        Difference R1,R1,R10
+        Greatest R0,R0,R1
+
+        LDR R1,[WsPtr,#DotCycleCount]
+        SUB R1,R1,R0
+        STR R1,[WsPtr,#DotCycleCount]
+        STR R0,[WsPtr,#PostCycleCount]
+
+        Pull "R0-R8,PC"
+;
+;
+;
+;------------------------------------------------------------------------------
+;
+; LineStartsOutSideWindow
+; =======================
+;
+LineStartsOutsideWindow
+
+        Push Link
+        Push "R0,R1"                    ;StartX,StartY
+
+        Push "R5,R6"
+        WindowRes R10,R0,R1, R5,R6,R9,R14       ;R10 := Window(Start)
+        WindowRes R11,R7,R8, R5,R6,R9,R14       ;R11 := Window(End)
+        Pull "R5,R6"
+
+        TST R10,R11
+        BNE LineOutsideWindow           ;Line completely outside window
+
+
+;
+; R0    ,R1    ,R2  ,R3    ,R4    ,R5   ,R6   ,R7  ,R8    ,R10   ,R11
+; StartX,StartY,Bres,DeltaX,DeltaY,StepX,StepY,EndX,EndY  ,WStart,WEnd
+;
+
+        TST R10,#&C                             ;If above/below window
+        BEQ LSO10
+
+        Push R11
+        BL InYWind                              ; then bring Y into window
+        Pull R11
+
+        Push "R6-R8"
+        WindowRes R10,R0,R1, R6,R7,R8,R9       ;R10 := Window(NewStart)
+        Pull "R6-R8"
+
+        TST R10,R11
+        BNE LineOutsideWindow           ;Line completely outside window
+LSO10
+        TST R10,#&3
+        BEQ LSO20                       ;If start outside X window
+
+        Push R11
+        BL InXWind
+        Pull R11
+
+        Push "R6-R8"
+        WindowRes R10,R0,R1, R6,R7,R8,R9        ;R10 := Window(NewStart)
+        Pull "R6-R8"
+        CMP R10,#0
+
+        BNE LineOutsideWindow           ;Cannot clip to window
+LSO20
+        Pull "R9,R10"           ;StartX,StartY
+
+        Push "R0-R8"
+        Difference R0,R0,R9
+        Difference R1,R1,R10
+        Greatest R0,R0,R1
+        BL AdvanceDotPattern
+        Pull "R0-R8"
+
+        Pull "PC"
+
+
+LineOutsideWindow
+        Pull "R0,R1"                    ;Balance the stack
+        LDR R0,[WsPtr,#DotCycleCount]
+        BL AdvanceDotPattern
+
+        Pull "Link"
+        Return                          ;To caller of the line routine
+;
+;
+;
+;------------------------------------------------------------------------------
+;
+
+
+lpStartX  RN 0
+lpStartY  RN 1
+lpBres    RN 2
+lpDeltaX  RN 3
+lpDeltaY  RN 4
+lpStepX   RN 5
+lpStepY   RN 6
+
+
+
+
+
+
+;
+; InYWind - Bring a set of line parameters into the Y window
+; =======
+;
+; On entry, R0-R6 contain a line parameter block
+;       R0 - StartX
+;       R1 - StartY
+;       R2 - Bres
+;       R3 - DeltaX
+;       R4 - DeltaY
+;       R5 - StepX (+1/-1) (Equv bit6 of Sign in 6502 version)
+;       R6 - StepY (+1/-1) (Equv bit7 of Sign in 6502 version)
+;       R7 - EndX
+;       R8 - EndY
+;
+;       R9  - gwbrow
+;       R10 - gwtrow
+;
+; Algorithm:
+;  1. Calculate distance to Y window
+;  2. Change StartY by (distance-1)
+;  3. Add (distance-1)*DeltaX to Bres
+;  4. Divide Bres by DeltaY
+;  5. Subtract (quotient+1)*DeltaY from Bres
+;  6. Change StartX by (quotient+1)
+;  7. Do one more pixel advance by AdvLineParm
+;      (N.B. this is always the Bres -ve case)
+;
+;
+;
+InYWind
+        SaveRetAdr
+
+        LDR R9,[WsPtr,#GWBRow]
+        LDR R10,[WsPtr,#GWTRow]
+                                        ;(1)
+        CMP lpStepY,#0
+        SUBGE R11,R9,lpStartY
+        SUBLT R11,lpStartY,R10
+        SUB R11,R11,#1                  ;(Distance to window) - 1
+
+        BL InYW30                       ;Steps 2-6
+
+        BL AdvLineParm                  ;Step to first pixel in window
+        Return
+;
+; Flags still valid, GE/LT
+;
+; R11 holds distance to window -1
+;
+InYW30                                  ;(2)
+        ADDGE lpStartY,lpStartY,R11             ;StartY := GWBRow-1
+        SUBLT lpStartY,lpStartY,R11             ;StartY := GWTRow+1
+                                        ;(3)
+        MOV R10,lpDeltaX
+        MUL R9,R11,R10
+        ADDS lpBres,lpBres,R9                   ;Bres := Bres+(dist-1)*DeltaX
+
+                                                ;If lpBres now -ve,
+        MOVLT PC,Link                           ;then don't modify StartX
+                                                ;     (quotient+1 is 0)
+                                                ;else
+        MOV R10,lpDeltaY
+; *****Change made by DJS
+; Use new DivRem macro, not old DIVREM
+; Original code was:
+;        DIVREM R11,lpBres,R10,R9
+        DivRem R11,lpBres,R10,R9
+; *****End of change made by DJS
+        SUB lpBres,lpBres,lpDeltaY
+
+        ADD R11,R11,#1                          ; quotient := 1+bres/deltay
+                                        ;(6)
+        CMP lpStepX,#0
+        ADDGE lpStartX,lpStartX,R11
+        SUBLT lpStartX,lpStartX,R11
+
+        MOV PC,Link
+
+
+
+
+;
+; InXWind - Bring a set of line parametres into the X window
+; =======
+;
+; On entry, R0-R6 contain a line parameter block
+;       R0 - StartX
+;       R1 - StartY
+;       R2 - Bres
+;       R3 - DeltaX
+;       R4 - DeltaY
+;       R5 - StepX (+1/-1) (Equv bit6 of Sign in 6502 version)
+;       R6 - StepY (+1/-1) (Equv bit7 of Sign in 6502 version)
+;       R7 - EndX
+;       R8 - EndY
+;
+;       R9  - gwlcol
+;       R10 - gwrcol
+;
+; Algorithm:
+;  1. Replace Bres by -Bres-1
+;  2. Swap StartX and StartY
+;  3. Swap DeltaX and DeltaY
+;  3a Swap StepX and StepY
+;  4. Calculate distance to X window
+;  5. Do steps 2-6 of InYWind
+;  6. Repeat steps 1-3
+;  7. Do one more pixel advance by AdvLineParm
+;      (N.B. this is always the Bres +ve case)
+;
+InXWind
+        SaveRetAdr
+
+        LDR R9,[WsPtr,#GWLCol]
+        LDR R10,[WsPtr,#GWRCol]
+                                        ;(1)
+        MVN lpBres,lpBres
+                                        ;(2)(3)
+        Push "lpStartX,lpDeltaX,lpStepX"
+        Push "lpStartY,lpDeltaY,lpStepY"
+        Pull "lpStartX,lpDeltaX,lpStepX"
+        Pull "lpStartY,lpDeltaY,lpStepY"
+
+        CMP lpStepY,#0                  ;Really StepX
+        SUBGE R11,R9,lpStartY
+        SUBLT R11,lpStartY,R10
+        SUB R11,R11,#1                  ;(Distance to window) - 1
+
+        BL InYW30                               ;Steps 2-6
+                                        ;(1)
+        MVN lpBres,lpBres
+                                        ;(2)(3)
+        Push "lpStartX,lpDeltaX,lpStepX"
+        Push "lpStartY,lpDeltaY,lpStepY"
+        Pull "lpStartX,lpDeltaX,lpStepX"
+        Pull "lpStartY,lpDeltaY,lpStepY"
+
+        BL AdvLineParm                  ;Step to first pixel in window
+        Return
+
+
+
+
+;
+; rDotPtr    RN 7
+; rDotCnt    RN 9
+; rDotPatLSW RN 10
+; rDotPatMSW RN 11
+
+;
+; On entry R0 holds number of places to step dot pattern
+;
+AdvanceDotPattern
+        LDR R1,[WsPtr,#DotCycleCount]
+        SUB R1,R1,R0
+        STR R1,[WsPtr,#DotCycleCount]
+
+        LDR rDotPtr,[WsPtr,#LineDotPtr]
+        CMP rDotPtr,#0
+        MOVEQ PC,Link
+
+        LDR R1,[WsPtr,#DotLineLength]
+        MOV R2,R1
+; *****Change made by DJS
+; Use new DivRem macro, not old DIVREM
+; Original code was:
+;        DIVREM R3,R0,R2,R4              ;R0:=R0 REM DotLineLength
+        DivRem R3,R0,R2,R4              ;R0:=R0 REM DotLineLength
+; *****End of change made by DJS
+
+        LDMIA rDotPtr,{rDotCnt,rDotPatLSW,rDotPatMSW}
+
+        CMP rDotCnt,R0
+        SUBLT R0,R0,rDotCnt
+
+        ADDLT rDotCnt,WsPtr,#DotLineStyle               ;Restart pattern
+        LDMLTIA rDotCnt,{rDotPatLSW,rDotPatMSW}
+        LDRLT rDotCnt,[WsPtr,#DotLineLength]
+
+        SUB rDotCnt,rDotCnt,R0                          ;New value
+
+ [ {TRUE}
+
+; need special code if R0 > 32
+
+        RSBS    R1, R0, #32
+        MOVLT   rDotPatMSW, rDotPatLSW
+        MOVLT   rDotPatLSW, #0                          ; probably not necessary
+        SUBLT   R0, R0, #32
+        RSBLT   R1, R0, #32
+
+        MOV rDotPatMSW,rDotPatMSW,LSL R0
+        ORR rDotPatMSW,rDotPatMSW,rDotPatLSW,LSR R1
+        MOV rDotPatLSW,rDotPatLSW,LSL R0
+ |
+
+; old code
+
+        RSB R1,R0,#32
+        MOV rDotPatMSW,rDotPatMSW,LSL R0
+        ORR rDotPatMSW,rDotPatMSW,rDotPatLSW,LSR R1
+        MOV rDotPatLSW,rDotPatLSW,LSL R0
+ ]
+
+        STMIA rDotPtr,{rDotCnt,rDotPatLSW,rDotPatMSW}
+
+        MOVS PC,Link
+;
+;---------------------------------------------------------------------------
+;
+
+        END
diff --git a/s/vdu/vdugrafg b/s/vdu/vdugrafg
new file mode 100644
index 0000000000000000000000000000000000000000..3808b187f434e9781ab80dec8afa978e85bc786a
--- /dev/null
+++ b/s/vdu/vdugrafg
@@ -0,0 +1,2685 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > &.Source.VduGrafG
+;
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Vdu driver code - Sprite stuff
+;
+; Author R C Manby
+; Date   10.11.86
+;
+
+RangeB * 256
+RangeC * 512
+
+; Macros for various sprite operations
+
+        MACRO
+        KillSpChoosePtr
+        MOV     R0, #0
+        STR     R0, [WsPtr, #SpChoosePtr]
+        MEND
+
+        MACRO
+        CopyDown $to,$from,$bytes, $tmp, $tmp2
+        LDR     $tmp2, [WsPtr, #VduSprite]      ; sprite being output to by Vdu
+        ADD     $tmp, $from, $bytes
+        SUB     $tmp, $tmp, #1                  ; from + bytes -1
+        CMP     $tmp2, $to                      ; if VOTS >= to
+        CMPCS   $tmp, $tmp2                     ; and from+bytes-1 >= VOTS
+        BCC     %FT00
+        Push    "R0, R14"
+        SUB     R0, $to, $from                  ; then adjust address vars
+        BL      AdjustSpriteAddress             ; by offset = to-from
+        Pull    "R0, R14"
+00
+        CMP     $bytes, #0                      ; bytes must be a multiple of 4
+01
+        LDRNE   $tmp, [$from], #4
+        STRNE   $tmp, [$to], #4
+        SUBNES  $bytes, $bytes, #4
+        BNE     %BT01
+        MEND
+
+        MACRO
+        CopyUp $to,$from,$bytes, $tmp, $tmp2
+        LDR     $tmp2, [WsPtr, #VduSprite]      ; sprite being output to by Vdu
+        ADD     $tmp, $to, $bytes
+        SUB     $tmp, $tmp, #1                  ; to + bytes -1
+        CMP     $tmp2, $from                    ; if VOTS >= from
+        CMPCS   $tmp, $tmp2                     ; and to+bytes-1 >= VOTS
+        BCC     %FT00
+        Push    "R0, R14"
+        SUB     R0, $to, $from                  ; then adjust address vars
+        BL      AdjustSpriteAddress             ; by offset = to-from
+        Pull    "R0, R14"
+00
+01
+        SUBS    $bytes, $bytes, #4
+        LDRCS   $tmp, [$from, $bytes]
+        STRCS   $tmp, [$to, $bytes]
+        BHI     %BT01
+        MEND
+
+; copy R0 bytes from R3 to R2 (R2+:=R0; R3+:=R0)
+; corrupts R0, R4-R11, R14
+; NB not used at present
+
+        MACRO
+        CopyDownFast
+        SUBS    R0, R0, #9*4
+10
+        LDMCSIA R3!, {R4-R11,R14}
+        STMCSIA R2!, {R4-R11,R14}
+        SUBCSS  R0, R0, #9*4
+        BCS     %BT10
+        ADDS    R0, R0, #9*4
+20
+        LDRNE   R4, [R3], #4
+        STRNE   R4, [R2], #4
+        SUBNES  R0, R0, #4
+        BNE     %BT20
+        MEND
+
+        MACRO
+        ClearWords $from,$words, $tmp
+        MOV     $tmp,#0
+01
+        SUBS    $words, $words, #1
+        STRCS   $tmp, [$from], #4
+        BHI     %BT01
+        MEND
+
+; *****************************************************************************
+;
+;       AdjustSpriteAddress - Move VduSprite, ScreenStart, CursorAddr and
+;        InputCursorAddr by R0
+;
+;       Internal routine, called by routines that use macros
+;        CopyDown, CopyUp
+;
+; in:   R0 = no. of bytes to add on (can be -ve)
+;
+
+AdjustSpriteAddress ROUT
+        Push    R14
+        LDR     R14, [WsPtr, #VduSprite]
+        ADD     R14, R14, R0
+        STR     R14, [WsPtr, #VduSprite]
+        LDR     R14, [WsPtr, #ScreenStart]
+        ADD     R14, R14, R0
+        STR     R14, [WsPtr, #ScreenStart]
+        B       AdjustCursorVars        ; update CursorAddr, InputCursorAddr
+
+; *****************************************************************************
+;
+;       SpriteInit - Setup sprite workspace on reset (called whenever break
+;                    is pressed)
+;
+
+SpriteInit ROUT
+        Push    R14
+        [ :LNOT: AssemblingArthur
+        LDR     R0, =SvcTable                   ; intercept SWI SpriteOp
+        ADR     R1, SwiSpriteOp
+        STR     R1, [R0, #(OS_SpriteOp*4)]
+
+        ADRL    R1, SwiReadPoint                ; might as well catch ReadPoint
+        STR     R1, [R0, #(OS_ReadPoint*4)]     ; here
+        ]
+
+        BL      ClearSpritePtrName      ; clear SpChoosePtr,SpChooseName
+
+ [ :LNOT: NewStyle_SpriteArea
+        [ AssemblingArthur
+        MOV     R0, #0
+        LDRB    R0, [R0, #OsbyteVars + :INDEX: LastBREAK]
+        CMP     R0, #0          ; Soft=0,POR=1,Hard=2
+        BNE     %FT10           ; If CtrlBreak or PowerOnReset setup default
+        Pull    PC, EQ          ; sprite area
+
+;        LDR     R1, [WsPtr, #SpAreaStart]
+;        CMP     R1, #0
+;        BEQ     %FT20
+;        BL      ValidateSpriteArea
+;        BVC     %FT20
+        ]
+10
+        [ AssemblingArthur :LOR: Module
+        MOV     R2, #0
+        LDR     R2, [R2, #SpriteSize]   ; Tony sez this is (0..n)*(8or32)Kbytes
+        CMP     R2, #0
+        STREQ   R2, [WsPtr, #SpAreaStart]       ; EQ means no workspace
+        Pull    PC, EQ
+
+        LDR     R1, =SpriteSpaceAddress
+        STR     R1, [WsPtr, #SpAreaStart]
+;       SUB     R2, R2, #16             ; Lose 4 words in case we overshoot
+
+        MOV     R3, #0                  ; no sprites
+        MOV     R4, #SpriteAreaCBsize   ; saFirst
+        MOV     R5, #SpriteAreaCBsize   ; saFree
+
+        STMIA   R1, {R2,R3,R4,R5}       ; saEnd,saNumber,saFirst,saFree
+        |
+        MOV     R1, #0
+        STR     R1, [WsPtr, #SpAreaStart]       ; no sprite workspace
+        ]
+20
+ ]
+        Pull    PC
+
+        LTORG
+
+; *****************************************************************************
+;
+;       Vdu23_27 - SCHOOSE/SGET a numbered sprite
+;
+; in:   QQ?0 = 0 => Select sprite STR$(QQ?1) for plotting
+;       QQ?0 = 1 => Sget an area of screen and put it in sprite STR$(QQ?1)
+;
+
+Vdu23_27 ROUT
+        LDRB    R0, [WsPtr, #QQ+1]
+        CMP     R0, #1
+        MOVCC   R3, #SpriteReason_SelectSprite          ; 0-Select sprite
+        MOVEQ   R3, #SpriteReason_GetSprite             ; 1-Pickup sprite
+        MOVHI   PC, R14
+
+        Push    R14
+
+        LDRB    R0, [WsPtr, #QQ+2]      ; sprite number
+        ADD     R1, WsPtr, #NameBuf
+        MOV     R2, #4
+        SWI     XOS_BinaryToDecimal
+        MOV     R0, #13
+        STRB    R0, [R1, R2]            ; $NameBuf := STR$(n)
+
+        MOV     R0, R3                  ; R0 = sprite-op reason code
+        MOV     R2, R1                  ; R2 -> sprite name
+        MOV     R3, #0                  ; extension gap size
+        SWI     XOS_SpriteOp            ; perform operation, ignore errors
+
+        Pull    PC
+
+; *****************************************************************************
+;
+;       SpritePlot - PLOT &E8-&EF,X,Y
+;
+; in:   R2 = plot code
+;
+; The 2 LSBits of the plot code specify fg/bg colour and action as :-
+;  0 => No effect eqv. of Gcol(5,c)
+;  1 => Plot sprite using foreground Gcol action
+;  2 => Invert eqv. of Gcol(4,c)
+;  3 => Plot mask in background colour and Gcol action
+;
+
+SpritePlot ROUT
+        Push    R14
+
+        AND     R3, R2, #3              ; 2 LSBits of plot code
+        CMP     R3, #1
+        MOVCC   R5, #5                  ; gcol action - no effect
+        LDREQ   R5, [WsPtr, #GPLFMD]    ;              foreground
+        MOVHI   R5, #4                  ;              invert or background
+
+        AND     R5, R5, #&0F            ; knock out any ecf bits
+
+        LDR     R2, [WsPtr, #SpChoosePtr]      ; If ChoosePtr <> 0
+        CMP     R2, #0
+        BNE     %FT10                   ; then use as pointer to sprite
+
+        MOV     R0, #SpriteReason_SelectSprite ; else select it first
+        LDR     R2, =SpChooseName
+        ADD     R2, R2, WsPtr           ; sprite name ptr
+        SWI     XOS_SpriteOp
+        LDR     R2, [WsPtr, #SpChoosePtr]
+
+10                                      ; R2 points to the sprite
+        CMP     R3, #3
+        MOVNE   R0, #RangeC+SpriteReason_PutSprite
+        LDREQ   R0, =RangeC+SpriteReason_PlotMask
+        LDR     R1, [WsPtr, #SpAreaStart]
+        SWI     XOS_SpriteOp            ; perform operation, ignoring errors
+
+        Pull    PC
+
+; *****************************************************************************
+;
+;       SwiSpriteOp - Entry point for SWI OS_SpriteOp
+;
+; in:   R0 = sprite op reason code
+;       R1 -> sprite area (usually)
+;       R2 -> sprite (usually)
+;       R3..? parameters
+;
+
+SwiSpriteOp ROUT
+        Push    R14
+        BranchNotJustUs %F10, SpriteV, R11, R14
+
+; we are sole owners of SpriteV, so call our internal routine
+
+        Push    PC                      ; push address of SwiSpriteReturn
+        B       SpriteVecHandler
+        &       0
+
+SwiSpriteReturn
+        Pull    R14
+        ORRVS   R14, R14, #V_bit        ; if error, set V_bit in link
+        ExitSWIHandler
+
+; we are not the sole owners of SpriteV, so call the vector
+
+10
+        MOV     R10, #SpriteV
+        BL      %FT20
+        Pull    R14
+        ORRVS   R14, R14, #V_bit        ; if error, set V_bit in link
+        ExitSWIHandler
+
+20
+        CallAVector
+
+; *****************************************************************************
+;
+;       SpriteVecHandler - Default owner of SpriteV
+;
+; in:   R0-R7 contain our entry parameters
+;
+; out:  R0-R7 contain exit parameters
+;       R8-R12 are preserved
+;
+
+        MACRO
+        SpriteOpDispatch $cond
+        ADD$cond PC, R10, R11, ASR #8
+        MEND
+
+        MACRO
+        SpriteOpEntry   $addr, $flags
+        &       (($addr - SwiSpriteOpCallTb) :SHL: 8) + $flags
+        MEND
+
+; The end of this code is put before the beginning, so that
+; SpriteDispatchReturn + SVC_mode is within ADR reach of the dispatcher
+
+BadReasonCode ROUT
+        ADRL    R0, SpriteErr_BadReasonCode
+      [ International
+        BL      TranslateError
+      ]
+        B       %FT20
+SpriteIsCurrentDest
+        ADRL    R0, SpriteErr_SpriteIsCurrentDest
+      [ International
+        BL      TranslateError
+      ]
+        B       %FT20
+
+15
+        ADRL    R0, SpriteErr_DoesntExist
+      [ International
+        BL      TranslateError
+      ]
+
+20                                      ; Exit SWI with error, error code in R0
+        STR     R0, [WsPtr, #RetnReg0]
+30
+        SETV                            ; indicate an error
+
+; and drop thru to...
+
+SpriteDispatchReturn
+        ADD     R11, WsPtr, #RetnReg0
+        LDMIA   R11, {R0-R9}            ; Restore R0-R9
+ [ Fix5
+        MOV     R10, R13                ; Point at old returned registers
+        Push    "R0-R9"                 ; Save new returned registers on stack
+        LDMIA   R10, {R0-R9}            ; Load old returned registers
+        STMIA   R11, {R0-R9}            ; and put them in the dump area
+        Pull    "R0-R9"                 ; restore new returned registers
+        ADD     R13, R13, #10*4         ; remove stack frame
+ ]
+        [ :LNOT: AssemblingArthur :LAND: :LNOT: Module
+        SWIVS   XOS_GenerateError
+        ]
+        Pull    "R10-R12,PC"
+
+SpriteVecHandler
+        Push    "R10-R12"
+        TEQP    PC, #SVC_mode           ; Re-enable interupts
+
+        VDWS    WsPtr                   ; Point R12 at Vdu driver workspace
+        ADD     R11, WsPtr, #RetnReg0
+
+ [ Fix5
+        SUB     R13, R13, #10*4         ; Create stack frame for old RetnRegs
+        MOV     R10, R13                ; Keep pointer to this frame
+        Push    "R0-R9"                 ; Save new regs while we copy old ones
+        LDMIA   R11, {R0-R9}            ; Load old regs
+        STMIA   R10, {R0-R9}            ; and push them onto stack
+        Pull    "R0-R9"                 ; Restore new regs
+ ]
+        STMIA   R11, {R0-R9}            ; Dump R0-R9
+
+        CMP     R0, #RangeC + &100      ; if top bits out of range
+        BCS     BadReasonCode           ; then error
+
+        CMP     R0, #RangeB             ; if Range A type
+        LDRCC   R1, [WsPtr, #SpAreaStart] ; then point at MOS sprite area
+
+        AND     R0, R0, #&FF            ; Kill the range bits
+        CMP     R0, #SpriteReason_BadReasonCode
+        BCS     BadReasonCode
+
+        ADR     R10, SwiSpriteOpCallTb
+        ADR     R14, SpriteDispatchReturn + SVC_mode ; return address
+        LDR     R11, [R10, R0, LSL #2]  ; load (offset<<8) + flags
+
+        TST     R11, #SSO_ScreenNotAllowed ; if call can specify screen (R2=0)
+        TEQEQ   R2, #0                  ; and it is specified
+        MOVEQ   R1, #0                  ; then make sprite area ptr 0 as well
+        TSTNE   R11, #SSO_NeedsSomething ; or nothing needed anyway
+        SpriteOpDispatch EQ             ; then dispatch
+
+        TEQ     R1, #0                  ; else needs sprite area ptr
+        ADREQ   R0, SpriteErr_NoWorkSpace ; so if zero then invalid
+      [ International
+        BLEQ    TranslateError
+      ]
+        BEQ     %BT20
+
+        TST     R11, #SSO_DangerAreaOp  ; if not a danger area op
+        BEQ     %FT32                   ; then skip
+
+        LDR     R9, [WsPtr, #VduSpriteArea] ; else check if sprite area is same
+        TEQ     R9, R1
+        BEQ     SpriteIsCurrentDest     ; and error if so
+
+32
+        TST     R11, #SSO_NeedsSprite   ; if doesn't need sprite
+        BNE     %FT33
+
+        TEQ     R0,#SpriteReason_CreateSprite
+        BEQ     %FT21
+
+        TEQ     R0,#SpriteReason_ScreenSave
+        TEQNE   R0,#SpriteReason_GetSprite
+        TEQNE   R0,#SpriteReason_GetSpriteUserCoords
+
+        SpriteOpDispatch NE             ; let it go if we're not interested in it
+
+        LDR     R9,[WsPtr, #Log2BPP]    ; fetch the current bpp
+
+        CMP     R9,#4
+        SpriteOpDispatch CC             ; let it go if below 16bpp
+22
+        CMP     R3,#0
+        BNE     %FT34                   ; bang if it has a palette
+
+        SpriteOpDispatch                ; then dispatch
+
+21      ;createsprite. R6=mode number or sprite mode word, or => mode descriptor
+        Push    "R0-R3,R14"
+        MOV     R0,R6
+        MOV     R1,#VduExt_Log2BPP
+        SWI     XOS_ReadModeVariable
+        MOVCS   R2,#0
+        CMP     R2,#4
+        Pull    "R0-R3,R14"
+        SpriteOpDispatch CC
+        B       %BT22
+
+33
+        TEQ     R2, #0                  ; if sprite ptr is zero it's a boob
+        BEQ     %BT15                   ; so say SpriteDoesntExist
+
+        LDR     R9, [WsPtr, #RetnReg0]
+        CMP     R9, #RangeC             ; if not range C then must look up
+        BCS     %FT35                   ; sprite name and convert to pt4
+        Push    R14
+        BL      SpriteCtrlBlk           ; in: R2 -> name; out: R2 -> sprite
+        Pull    R14
+        BVS     %BT15                   ; no such sprite
+
+35
+
+;medusa note.
+;
+;On Medusa masks and palettes for 16/32bpp sprites are in a transient state
+;Medusa will fault 16/32bpp mask/palette operations, in readiness for the
+;introduction of new (more compact) mask/palette formats.
+;
+;another medusa note.
+;
+;Mask operations using 1bpp masks on new format sprites are now being included.
+;However palettes on 16/32bpp are still not allowed.
+;
+
+;amg 12/11/93, sort out the logic here so that palettes on new format sprites
+;really do get faulted
+
+;amg 25th May 1994. We now allow palettes on new format sprites of 8bpp
+;and below
+
+        ; find the sprite type
+        LDR    R9,[R2,#spMode]
+
+        MOVS   R9,R9,LSR #27            ; put the sprite type at b0
+        BEQ    %FT37                    ; t=0 (ie old format)
+39
+
+        CMP    R9,#SpriteType_New16bpp  ; check sprite type number
+        BCC    %FT37                    ; despatch if new format and under 8bpp
+
+        ; so, does it have a palette
+        Push   "R14"                    ; I need another register for CMP
+
+        ; now check for a palette
+        ADD    R9,R2,#spImage
+        LDMIA  R9,{R9,R14}              ; pick up offsets to mask & image
+        CMP    R9,R14
+        MOVGT  R9,R14                   ; R9->top of palette block
+        SUBS   R9,R9,#spPalette         ; R9 = size of palette block
+        Pull   "R14"
+        BEQ    %FT38                    ; no palette, so no error
+
+34
+        ADRL    R0, SpriteErr_NoMaskOrPaletteAllowedInThisDepth
+        [ International
+        BL      TranslateError
+        ]
+        B       %BT20
+
+37      ;however, until mode selectors work there are some 16/32bpp old modes
+        Push    "R0-R3,R14"             ; save context
+        MOV     R0,R9
+        MOV     R1,#VduExt_Log2BPP
+        SWI     XOS_ReadModeVariable    ; read log2bpp for the sprite's mode
+        MOV     R9,R2                   ; and move it for posterity
+        Pull    "R0-R3,R14"
+        CMP     R9,#4
+        BCS     %BT39                   ; log2bpp of 4 = 16bpp, so see if we want to fault it
+38
+        TST     R11, #SSO_DangerOp      ; if a destructive op
+        BEQ     %FT40
+
+        LDR     R9, [WsPtr, #VduSprite]
+        TEQ     R9, R2
+        BEQ     SpriteIsCurrentDest     ; then stop him!
+
+40
+        TST     R11, #SSO_NeedsSpriteModeData
+        SpriteOpDispatch EQ
+
+        Push    "R5-R7, R10, R11, R14"
+        BL      SetupSprModeData
+        Pull    "R5-R7, R10, R11, R14"
+        SpriteOpDispatch VC
+
+        B       %BT30                   ; Invalid spMode field
+
+
+SSO_ScreenNotAllowed    * 1 :SHL: 0
+SSO_NeedsSomething      * 1 :SHL: 1
+SSO_NeedsSprite         * 1 :SHL: 2
+SSO_NeedsSpriteModeData * 1 :SHL: 3
+SSO_DangerOp            * 1 :SHL: 4
+SSO_DangerAreaOp        * 1 :SHL: 5
+
+Group1  * SSO_ScreenNotAllowed
+Group2  * Group1 + SSO_NeedsSomething
+Group3  * Group2 + SSO_NeedsSprite
+Group4  * Group3 + SSO_NeedsSpriteModeData
+Group5  * SSO_NeedsSomething + SSO_NeedsSprite
+
+SwiSpriteOpCallTb
+        SpriteOpEntry DoesNowt, Group1
+
+       [ AssemblingArthur :LOR: Module
+        SpriteOpEntry DoesNowt, Group1
+       |
+        SpriteOpEntry ClaimSpace, Group1        ; *SSpace <size>
+       ]
+
+        SpriteOpEntry ScreenSave, Group1
+        SpriteOpEntry ScreenLoad, Group1
+        SpriteOpEntry DoesNowt, Group1
+        SpriteOpEntry DoesNowt, Group1
+        SpriteOpEntry DoesNowt, Group1
+        SpriteOpEntry DoesNowt, Group1
+
+; The following need valid workspace
+
+        SpriteOpEntry ReadAreaCB, Group2
+        SpriteOpEntry ClearSprites, Group2 + SSO_DangerAreaOp   ; *SNew
+        SpriteOpEntry LoadSpriteFile, Group2 + SSO_DangerAreaOp
+                                                ; *SLoad <filename>
+        SpriteOpEntry MergeSpriteFile, Group2 + SSO_DangerAreaOp
+                                                ; *SMerge <filename>
+        SpriteOpEntry SaveSpriteFile, Group2    ; *SSave <filename>
+        SpriteOpEntry ReturnName, Group2
+        SpriteOpEntry GetSprite, Group2         ; *SGet <name>
+        SpriteOpEntry CreateSprite, Group2
+        SpriteOpEntry GetSpriteUserCoords, Group2
+        SpriteOpEntry DoesNowt, Group2
+        SpriteOpEntry DoesNowt, Group2
+        SpriteOpEntry DoesNowt, Group2
+        SpriteOpEntry DoesNowt, Group2
+        SpriteOpEntry DoesNowt, Group2
+        SpriteOpEntry DoesNowt, Group2
+        SpriteOpEntry DoesNowt, Group2
+
+; The following need a sprite
+
+        SpriteOpEntry SelectSprite, Group3      ; *SChoose <n> [<m>]
+        SpriteOpEntry DeleteSprite, Group3 + SSO_DangerOp       ; *SDelete <n>
+        SpriteOpEntry RenameSprite, Group3                      ; *SRename
+        SpriteOpEntry CopySprite, Group3                        ; *SCopy
+        SpriteOpEntry PutSprite, Group3
+        SpriteOpEntry CreateMask, Group3
+        SpriteOpEntry RemoveMask, Group3 + SSO_DangerOp
+        SpriteOpEntry InsertRow, Group3 + SSO_DangerOp
+        SpriteOpEntry DeleteRow, Group3 + SSO_DangerOp
+        SpriteOpEntry FlipAboutXAxis, Group3
+        SpriteOpEntry PutSpriteUserCoords, Group3
+        SpriteOpEntry DoesNowt, Group3
+        SpriteOpEntry DoesNowt, Group3
+        SpriteOpEntry DoesNowt, Group3
+        SpriteOpEntry DoesNowt, Group3
+        SpriteOpEntry DoesNowt, Group3
+
+; The following need sprite mode data
+
+        SpriteOpEntry ReadSpriteSize, Group4
+        SpriteOpEntry ReadPixelColour, Group4
+        SpriteOpEntry WritePixelColour, Group4
+        SpriteOpEntry ReadPixelMask, Group4
+        SpriteOpEntry WritePixelMask, Group4
+        SpriteOpEntry InsertCol, Group4 + SSO_DangerOp
+        SpriteOpEntry DeleteCol, Group4 + SSO_DangerOp
+        SpriteOpEntry FlipAboutYAxis, Group4 + SSO_DangerOp
+        SpriteOpEntry PlotMask, Group4
+        SpriteOpEntry PlotMaskUserCoords, Group4
+        SpriteOpEntry DoesNowt, Group4  ; 50            ; PlotMaskScaled
+        SpriteOpEntry DoesNowt, Group4  ; 51            ; PaintCharScaled
+        SpriteOpEntry DoesNowt, Group4  ; 52            ; PutSpriteScaled
+        SpriteOpEntry DoesNowt, Group4  ; 53            ; PutSpriteGreyScaled
+        SpriteOpEntry RemoveLeftHandWastage, Group4
+        SpriteOpEntry DoesNowt, Group4                  ; 55 PlotMaskTransformed
+        SpriteOpEntry DoesNowt, Group4                  ; 56 PutSpriteTransformed
+        SpriteOpEntry DoesNowt, Group4                  ; 57 InsertDeleteRows
+        SpriteOpEntry DoesNowt, Group4                  ; 58 InsertDeleteColumns
+        SpriteOpEntry DoesNowt, Group4                  ; 59 pseudo reason used by Wimp
+
+; The following need (sprite area + sprite) or (anything + 0), meaning screen
+
+        SpriteOpEntry SwitchOutputToSprite, Group5 ; 60
+        SpriteOpEntry SwitchOutputToMask, Group5   ; 61
+        SpriteOpEntry ReadSaveAreaSize, Group5     ; 62
+
+; *****************************************************************************
+;
+;       SetupSprModeData - Set up registers and variables from the sprite mode
+;
+;       Internal routine: called by sprite dispatch, CreateHeader
+;
+; in:   R2 -> sprite
+;
+; out:  R0-R5 preserved
+;       R6 = ReadNColour (for a single screen pixel)
+;       R7 = WriteNColour (=R6 except in double pixel modes)
+;       R8 = BytesPerChar }
+;       R9 = XShftFactor  } calculated from Log2BPC which is BitsPerPix
+;       R10 = NPix        }  corrected for double pixel modes
+;       R11 = Log2BPC     }
+;
+;       SprReadNColour..SprLog2BPC setup accordingly
+;
+;       If error, then RetnReg0 updated
+
+SetupSprModeData ROUT
+        Push    R14
+        LDR     R11, [R2, #spMode]
+ [ ModeSelectors
+        CMP     r11, #&100              ; check for mode selector/new format sprite word
+        BCC     %FT05                   ; [it's a mode number so check for known ones]
+        TST     r11, #1                 ; if it's a new format sprite word
+        BNE     %FT10                   ; then call pushmodeinfo to get info on it
+        B       %FT20                   ; else it's a mode selector, which is illegal as a sprite mode word
+05
+ ]
+        BranchIfKnownMode R11, %FA10
+        Push    "R2-R4"
+        MOV     R2, R11
+        BL      OfferModeExtensionAnyMonitor
+        Pull    "R2-R4"
+        BNE     %FT20
+
+10
+        MOV     R10, R11
+        BL      PushModeInfoAnyMonitor
+        BVS     %FT30                   ; if duff new format sprite word, return error
+
+        LDR     R11, [R13, #wkModeFlags]
+        TST     R11, #Flag_NonGraphic
+        BNE     %FT15                   ; non-graphic mode
+
+        LDR     R6, [R13, #wkNColour]                   ; ReadNColour
+        ;changed by amg 12/11/93 to stop it mistaking 16/32bpp for 8
+;        TST     R6, #&F0                                ; 256 colour mode ?
+;        MOVNE   R6, #&FF                                ; then use &FF
+        CMP     R6,#63
+        MOVEQ   R6,#255
+        LDR     R11, [R13, #wkLog2BPC]                  ; Log2BPC
+
+        ADD     R13, R13, #PushedInfoSize
+11
+        MOV     R7, #1
+        RSB     R9, R11, #5             ; XShftFactor
+        RSB     R10, R7, R7, LSL R9     ; NPix
+        MOV     R8, R7, LSL R11         ; BytesPerChar
+        RSB     R7, R7, R7, LSL R8      ; WriteNColour
+
+        Push    R5
+        ADD     R5, WsPtr, #SprReadNColour
+        STMIA   R5, {R6-R11}            ; SprRead..SprLog2BPC
+        Pull    "R5, R14"
+        BICS    PC, R14, #V_bit
+
+15
+        ADD     R13, R13, #PushedInfoSize
+20
+        ADRL    R0, SpriteErr_InvalidSpriteMode
+      [ International
+        BL      TranslateError
+      ]
+30
+        STR     R0, [WsPtr, #RetnReg0]
+        SETV
+        Pull    pc
+
+
+; *****************************************************************************
+
+; Blocks for sprite errors
+
+SpriteErr_NoWorkSpace                       MakeErrorBlock Sprite_NoWorkSpace
+SpriteErr_NoRoom                            MakeErrorBlock Sprite_NoRoom
+SpriteErr_DoesntExist                       MakeErrorBlock Sprite_DoesntExist
+SpriteErr_NoSprites                         MakeErrorBlock Sprite_NoSprites
+SpriteErr_NotGraphics                       MakeErrorBlock Sprite_NotGraphics
+SpriteErr_NotEnoughRoom                     MakeErrorBlock Sprite_NotEnoughRoom
+SpriteErr_BadSpriteFile                     MakeErrorBlock Sprite_BadSpriteFile
+SpriteErr_NoRoomToMerge                     MakeErrorBlock Sprite_NoRoomToMerge
+SpriteErr_Bad2ndPtr                        MakeErrorBlock Sprite_Bad2ndPtr
+SpriteErr_InvalidRowOrCol                   MakeErrorBlock Sprite_InvalidRowOrCol
+SpriteErr_InvalidHeight                     MakeErrorBlock Sprite_InvalidHeight
+SpriteErr_InvalidWidth                      MakeErrorBlock Sprite_InvalidWidth
+SpriteErr_NoRoomToInsert                    MakeErrorBlock Sprite_NoRoomToInsert
+SpriteErr_SpriteAlreadyExists               MakeErrorBlock Sprite_SpriteAlreadyExists
+SpriteErr_InvalidSpriteMode                 MakeErrorBlock Sprite_InvalidSpriteMode
+SpriteErr_BadReasonCode                     MakeErrorBlock Sprite_BadReasonCode
+SpriteErr_CantInTeletext                    MakeErrorBlock Sprite_CantInTeletext
+SpriteErr_InvalidSaveArea                   MakeErrorBlock Sprite_InvalidSaveArea
+SpriteErr_SpriteIsCurrentDest               MakeErrorBlock Sprite_SpriteIsCurrentDest
+SpriteErr_NoMaskOrPaletteAllowedInThisDepth MakeErrorBlock Sprite_NoMaskOrPaletteAllowedInThisDepth
+
+; *****************************************************************************
+;
+;       ClearSprites - Clear sprite area (*SNEW)
+;
+;       External routine + dropped thru to
+;
+
+ClearSprites ROUT
+        LDR     R2, [R1, #saFirst]
+        STR     R2, [R1, #saFree]
+        MOV     R2, #0
+        STR     R2, [R1,#saNumber]
+
+        LDR     R0, [WsPtr, #RetnReg0]  ; if rangeb or rangec
+        CMP     R0, #RangeB
+        BICCSS  PC, R14, #V_bit         ; then return immediately
+
+; else its rangea, so drop thru to ...
+
+ClearSpritePtrName ROUT
+        MOV     R0, #0
+        STR     R0, [WsPtr, #SpChoosePtr]
+        STR     R0, [WsPtr, #SpChooseName]
+        STR     R0, [WsPtr, #SpChooseName+4]
+        STR     R0, [WsPtr, #SpChooseName+8]
+        MOV     R0, #13
+        STRB    R0, [WsPtr, #SpChooseName+12]   ; *SChoose <null name>
+DoesNowt
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       ReadAreaCB - Read information from sprite area CB into registers
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;
+; out:  R2 = offset to end of sprite area (ie total size)
+;       R3 = number of sprites in area
+;       R4 = offset to first sprite
+;       R5 = offset to first free word
+;
+
+ReadAreaCB ROUT
+        LDMIA   R1, {R2,R3,R4,R5}               ; saEnd,saNumber,saFirst,saFree
+        ADD     R11, WsPtr, #RetnReg2
+        STMIA   R11, {R2,R3,R4,R5}
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       SelectSprite - Select a named sprite for use by PLOT &E8..&EF
+;
+;       External routine + called by GetSprite
+;
+; in:   R2 -> sprite CB
+;
+; out:  R0, R9..R11 corrupted
+;       If not using system sprite area, then R2 -> address of sprite
+;
+
+SelectSprite ROUT
+        Push    R14
+        LDR     R0, [WsPtr, #RetnReg0]          ; if not in system sprite area
+        CMP     R0, #RangeB
+        STRCS   R2, [WsPtr, #RetnReg2]          ; return the sprite address
+        Pull    PC, CS
+
+        STR     R2, [WsPtr, #SpChoosePtr]       ; else store name & address
+        ADD     R14, R2, #spName                ; for use by PLOT
+        LDMIA   R14, {R9,R10,R11}               ; load 12 bytes of name
+        LDR     R14, =SpChooseName
+        ADD     R14, R14, WsPtr                 ; RetnReg2 NOT altered, so user
+        STMIA   R14, {R9,R10,R11}               ; can't poke the workspace
+        Pull    PC
+
+; *****************************************************************************
+;
+;       ReturnName - Return name of nth sprite in sprite area as a string
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;       R2 -> buffer
+;       R3 = max buffer length
+;       R4 = sprite number (n)
+;
+; out:  R3 actual string length
+;       RetnReg3 updated
+;
+
+ReturnName ROUT
+        LDR     R5, [R1, #saNumber]     ; check for 1 <= R4 <= saNumber
+        CMP     R4, #1
+        CMPGE   R5, R4
+
+        ADRLTL  R0, SpriteErr_DoesntExist ; out of range, so generate error
+      [ International
+        Push    "lr",LT
+        BLLT    TranslateError_VClear
+        Pull    "lr",LT
+      ]
+        STRLT   R0, [WsPtr, #RetnReg0]
+        ORRLTS  PC, R14, #V_bit
+
+        LDR     R5, [R1, #saFirst]
+        ADD     R5, R5, R1              ; ptr to 1st sprite
+10
+        SUBS    R4, R4, #1
+        LDRNE   R6, [R5, #spNext]       ; if not sprite we want
+        ADDNE   R5, R5, R6              ; chain to next one
+        BNE     %BT10
+
+        ADD     R5, R5, #spName         ; found sprite, R5 -> name
+
+        SUBS    R3, R3, #1
+        MOVLS   R3, #0                  ; if was 0 or 1 then set R3 to 0
+        STRLS   R3, [WsPtr, #RetnReg3]
+        STREQB  R3, [R2]                ; if was 1 then store 0 terminator
+        BICLSS  PC, R14, #V_bit         ; if was 0 or 1 then exit
+
+        CMP     R3, #SpriteNameSize     ; if length > maximum sprite name len
+        MOVHI   R3, #SpriteNameSize     ; then limit to maximum sprite name len
+
+; R3 is now maximum number of characters to store, excluding terminator
+
+        MOV     R6, R2                  ; remember start address
+20
+        LDRB    R4, [R5], #1            ; load a byte from sprite name
+        CMP     R4, #" "                ; if char > " "
+        STRHIB  R4, [R2], #1            ; then store character and inc ptr
+        SUBHIS  R3, R3, #1              ; and decrement character count
+        BHI     %BT20                   ; loop until char<=" " or count expired
+
+        MOV     R4, #0                  ; store terminating 0
+        STRB    R4, [R2]
+        SUB     R3, R2, R6              ; R3 = no. of characters,
+        STR     R3, [WsPtr, #RetnReg3]  ; excluding terminator
+        BICS    PC, R14, #V_bit         ; indicate no error
+
+; *****************************************************************************
+;
+;       RenameSprite - Rename a sprite
+;
+;       External routine (AlreadyExists is used by CopySprite)
+;
+; in:   R2 -> sprite
+;       R3 -> new name
+;
+
+RenameSprite ROUT
+        Push    "R2, R14"
+        MOV     R2, R3
+        BL      GetName                 ; returns name in R9-R11
+        BL      SpriteCtrlBlk           ; try to find sprite of that name
+        BVC     AlreadyExists
+        Pull    "R2, R14"
+        ADD     R8, R2, #spName
+        STMIA   R8, {R9-R11}
+        KillSpChoosePtr                 ; in case it points to renamed sprite
+        BICS    PC, R14, #V_bit
+
+AlreadyExists                           ; sprite with new name already exists
+        Pull    "R3, R14"
+        TEQ     R2, R3                  ; if it's the same one, then exit VC
+        BICEQS  PC, R14, #V_bit         ; (SRename/SCopy fred fred is allowed)
+
+        ADRL    R0, SpriteErr_SpriteAlreadyExists
+      [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      ]
+        STR     R0, [WsPtr,#RetnReg0]
+        ORRS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       CopySprite - Make a copy of a sprite
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;       R3 -> new name
+;
+
+CopySprite ROUT
+        Push    "R2, R14"               ; save ptr to sprite to be copied
+        MOV     R2, R3
+        BL      GetName                 ; returns new name in R9-R11
+        BL      SpriteCtrlBlk           ; try to find sprite of that name
+        BVC     AlreadyExists           ; [we found one of that name]
+        Pull    R2
+        LDR     R8, [R1, #saFree]
+        ADD     R8, R8, R1              ; address sprite will be copied to
+        ADD     R8, R8, #spName         ; address of its name field
+        BL      AppendSprite            ; copy it
+        STMVCIA R8, {R9-R11}            ; if copy OK, rename it
+        Pull    PC                      ; exit with V clear/set appropriately
+
+; *****************************************************************************
+;
+;       ReadSpriteSize - Read sprite size and other info
+;
+;       External routine
+;
+; in:   R2 -> sprite
+;
+; out:  R1,R2 preserved
+;       R3 = width in pixels
+;       R4 = height in pixels
+;       R5 = 0/1 for solid/transparent
+;       R6 = mode sprite was defined in
+;
+;       RetnReg3..RetnReg6 updated
+;
+
+ReadSpriteSize ROUT
+        ADD     R3, R2, #spWidth
+        LDMIA   R3, {R3,R4,R5,R6}       ; spWidth,spHeight,spLBit,spRBit
+        ADD     R4, R4, #1              ; R4 := height in pixels
+        ADD     R3, R3, #1              ; R3 := width in words
+        RSB     R3, R5, R3, LSL #5      ; convert to bits and sub LH wastage
+        SUB     R3, R3, #31             ; subtract RH wastage
+        ADD     R3, R3, R6
+        LDR     R11, [WsPtr, #SprLog2BPC]
+        MOV     R3, R3, LSR R11         ; number of pixels in row
+        LDR     R5, [R2, #spImage]
+        LDR     R6, [R2, #spTrans]
+        SUBS    R5, R5, R6
+        MOVNE   R5, #1                  ; if spImage=spTrans then no mask
+        LDR     R6, [R2, #spMode]
+
+        ADD     R11, WsPtr, #RetnReg3
+        STMIA   R11, {R3-R6}
+
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       ReadSpriteWidth - Read width of a sprite
+;
+;       Internal routine, called by PlotMask, InsertCol, DeleteCol
+;
+; in:   R2 -> sprite
+;
+; out:  R0 = sprite width in pixels
+;       All other registers preserved
+;
+
+ReadSpriteWidth ROUT
+        Push    "R4-R6, R14"
+        ADD     R0, R2, #spWidth
+        LDMIA   R0, {R0,R4,R5,R6}       ; spWidth,spHeight,spLBit,spRBit
+        ADD     R0, R0, #1              ; width in words
+        RSB     R0, R5, R0, LSL #5
+        SUB     R0, R0, #31
+        ADD     R0, R0, R6              ; total number of bits in row
+        LDR     R5, [WsPtr, #SprLog2BPC]
+        MOV     R0, R0, LSR R5          ; number of pixels in row
+        Pull    "R4-R6, PC"
+
+; *****************************************************************************
+;
+;       DeleteSpriteByName
+;
+;       Internal routine, called by MergeSprite, GetSprite, CreateSprite
+;
+; in:   R1 -> sprite area
+;       R2 -> name of sprite to be deleted
+;
+; out:  All registers preserved
+;
+
+DeleteSpriteByName ROUT
+        Push    "R2-R4, R14"
+        BL      SpriteCtrlBlk           ; R2: In , SpriteNamePtr
+                                        ;     Out, SpriteCBptr
+        BLVC    DeleteSprite            ; if found, then delete it
+        Pull    "R2-R4, PC"
+
+; *****************************************************************************
+;
+;       DeleteSprite
+;
+;       External routine + called by DeleteSpriteByName
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;
+; out:  All registers preserved
+;
+
+DeleteSprite ROUT
+        Push    "R0, R3,R4, R14"
+        LDR     R3, [R2, #spNext]
+        ADD     R3, R3, R2
+        LDR     R0, [R1, #saFree]
+        ADD     R0, R0, R1
+        SUB     R0, R0, R3
+        CopyDown R2, R3, R0, R14, R4
+        LDR     R3, [R1, #saNumber]     ; decrement sprite count
+        SUB     R3, R3, #1
+        STR     R3, [R1, #saNumber]
+        SUB     R3, R2, R1              ; (R2 points to first free location)
+        STR     R3, [R1, #saFree]       ; update saFree
+
+        KillSpChoosePtr                 ;Because the sprites have moved
+
+        Pull    "R0, R3,R4, PC"
+
+; *****************************************************************************
+;
+;       GetName - Read sprite name into buffer, lower cased, padded to 12 chars
+;
+;       Internal routine, called by RenameSprite, CopySprite,
+;        SpriteCtrlBlk, CreateHeader
+;
+; in:   R2 -> sprite name (terminated by char <= 32)
+;
+; out:  Name returned in R9-R11 and NameBuf
+;       All other registers preserved
+;
+
+GetName ROUT
+        Push    "R2-R5, R14"
+        ADD     R3, WsPtr, #NameBuf
+        MOV     R4, #SpriteNameSize
+10
+        LDRB    R5, [R2], #1
+        uk_LowerCase R5, R14
+        CMP     R5, #" "
+        STRHIB  R5, [R3], #1
+        SUBHIS  R4, R4, #1              ; loop until char<=32 or done 12 chars
+        BHI     %BT10
+
+        MOV     R5, #0
+        CMP     R4, #0                  ; pad with 0 or more nulls
+20
+        STRHIB  R5, [R3], #1
+        SUBHIS  R4, R4, #1
+        BHI     %BT20
+
+        ADD     R9, WsPtr, #NameBuf
+        LDMIA   R9, {R9-R11}            ; name returned in R9-R11 and NameBuf
+
+        Pull    "R2-R5, PC"
+
+; *****************************************************************************
+;
+;       SpriteCtrlBlk - Search for control block of named sprite
+;
+;       Internal routine, called by sprite dispatch, RenameSprite,
+;        CopySprite, DeleteSpriteByName
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite name
+;
+; out:  V=0 => R2 -> sprite
+;       V=1 => sprite not found
+;              R2 -> first free byte (this fact not used by anyone yet)
+;       All other registers preserved
+;
+
+SpriteCtrlBlk ROUT
+        Push    "R3-R11, R14"
+        LDMIA   R1, {R3,R4,R5}          ; saEnd, saNumber, saFirst
+        ADD     R3, R5, R1              ; point to first sprite/free space
+        CMP     R4, #0
+        BEQ     %FT20                   ; no sprites, exit with R3 pointing
+                                        ; at free space and R4=0
+        BL      GetName                 ; search name in R9-R11 and NameBuf
+10
+        LDMIA   R3, {R5, R6,R7,R8}      ; spNext, spName(0..2)
+        CMP     R6, R9
+        CMPEQ   R7, R10
+        CMPEQ   R8, R11                 ; (V:=0 if equal)
+        BEQ     %FT30                   ; sprite found
+
+        ADD     R3, R3, R5
+        SUBS    R4, R4, #1              ; try next sprite
+        BNE     %BT10
+20
+        SETV                            ; indicate not found
+30
+        MOV     R2, R3                  ; R2 -> sprite or to free space
+        Pull    "R3-R11, PC"
+
+; *****************************************************************************
+;
+;       InternaliseCoords - Convert from external to internal coords
+;                           for sprite plotting
+;
+;       Internal routine called by PutSpriteUserCoords, PlotMaskUserCoords
+;
+; in:   R3 = external X coordinate
+;       R4 = external Y coordinate
+;
+; out:  R3 = internal X coordinate
+;       R4 = internal Y coordinate
+;       R1, R2, R5 preserved
+;
+
+InternaliseCoords ROUT
+        Push    "R1,R2,R5, R14"
+        MOV     R0, R3                  ; put external coordinate in R0,R1
+        MOV     R1, R4
+        ADD     R7, WsPtr, #GCsX
+        LDMIA   R7, {R8,R9}             ; preserve GCsX,GCsY around EIG
+        MOV     R2, #4                  ; indicate absolute coords
+        BL      EIG
+        STMIA   R7, {R8,R9}             ; restore GcsX,GCsY
+        MOV     R3, R0                  ; internal coord of plotting point
+        MOV     R4, R1
+        Pull    "R1,R2,R5, PC"
+
+; *****************************************************************************
+;
+;       PutSpriteUserCoords - Draw sprite to screen using given ext. coords
+;
+;       External routine
+;
+; in:   R1 -> sprite area (not used)
+;       R2 -> sprite
+;       R3 = X coordinate to plot at
+;       R4 = Y coordinate to plot at
+;       R5 = GCOL action to be used
+;
+
+PutSpriteUserCoords ROUT
+        Push    R14
+        BL      InternaliseCoords
+        Pull    R14
+        B       PutSpri20
+
+; *****************************************************************************
+;
+;       PutSprite - Draw sprite to screen at NewPt
+;
+;       External routine + PutSpri20 called by PutSpriteUserCoords
+;        (also external)
+;
+; in:   R1 -> sprite area (not used)
+;       R2 -> sprite
+;       R5 = GCOL action to be used
+;
+
+; Values held in Ram
+;
+; N.B. The ordering of these variables is VERY important
+;      rearrange at your peril
+;
+; Within the 'memory to screen' drawing loop, WsPtr (R12) points at
+;  ScrAdr, variables either side being loaded/saved in groups using
+;  LDMDB / STMIA / LDMIB
+;
+;           SPltWidth   }
+;           SPltHeight  }
+;           SPltScrOff  }
+;           SPltMemOff  } LDMDB
+; WsPtr ->  SPltScrAdr    } STMIA
+;           SPltColCnt    } } LDMIB
+;           SPltMemAdr      }
+;           SPltShftR       }
+;           SPltShftL       }
+;
+;           SPltMskAdr
+;
+;           SPltLMask
+;           SPltRMask
+;
+;           SPltzgooPtr
+;
+;
+
+PutSprite ROUT
+        ASSERT  NewPtY = NewPtX +4
+        ADD     R3, WsPtr, #NewPtX
+        LDMIA   R3, {R3, R4}            ; plot sprite at NewPt(X,Y)
+PutSpri20
+   [ {TRUE}
+        GraphicsMode R0
+        ADRNEL  R0, SpriteErr_NotGraphics
+      [ International
+        Push    "lr",NE
+        BLNE    TranslateError
+        Pull    "lr",NE
+      ]
+        STRNE   R0, [WsPtr, #RetnReg0]
+        ORRNES  PC, R14, #V_bit         ; quit with error if not graphics mode
+   |
+        LDR     R0, [WsPtr, #NPix]
+        TEQ     R0, #0
+        ADREQL  R0, SpriteErr_NotGraphics
+      [ International
+        Push    "lr",EQ
+        BLEQ    TranslateError
+        Pull    "lr",EQ
+      ]
+        STREQ   R0, [WsPtr, #RetnReg0]
+        ORREQS  PC, R14, #V_bit         ; quit with error if not graphics mode
+    ]
+        Push    "WsPtr, R14"            ; push both so R12,R14 are free for use
+
+        Push    "R2"                    ; save the pointer to the sprite
+
+        BL      GenSpritePlotParmBlk
+
+        Pull    "R11"
+
+        BVS     SpriteOffScreen
+
+        SWI     XOS_RemoveCursors       ; assume no error can occur!
+
+        LDR     R5, [WsPtr, #SPltMemAdr]
+        LDR     R6, [WsPtr, #SPltMskAdr]
+        TEQ     R6, #0
+        BEQ     %FT20
+
+        ADD     R6, R6, R5
+        STR     R6, [WsPtr, #SPltMskAdr]
+
+        LDR     R14, [R11, #spMode]
+        MOV     R14,R14,LSR #27
+
+        ;note: new format 1bpp sprites are thrown at the old routine, since it
+        ;will render them faster
+
+        CMP     R14, #2
+        BCC     TransPlot               ; it's got transparent bits in it! (
+        BCS     NewTransPlot            ; it's got 1bpp transparent bits in it!!!
+20
+
+; Plot sprite ignoring transparency mask (if any)
+
+
+        ADD     R11, WsPtr, #SPltzgooMasks
+        LDMIA   R11, {R8-R11}           ; masks for screen access (zgoo..zgee)
+
+        LDR     R2, [WsPtr, #SPltAction]  ; GCOL action
+        ADD     WsPtr, WsPtr, #SPltScrAdr ; repoint WsPtr at SPltScrAdr
+        LDMIA   WsPtr, {R0-R1,R5-R7}
+
+        TEQ     R2, #0
+        BNE     SolPl10                 ; not store, do it slowly
+
+; SimpleCase
+
+SolPlFast10
+
+;       R0    ,R1    ,     R5    ,R6   ,R7   ,(R8  ,R9  ,R10 ,R11 )
+;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,(zgoo,zgeo,zgoe,zgee)
+
+; Plot the first (leftmost) word
+
+        LDMIA   R5, {R2,R3}
+        ADD     R5, R5, #4
+
+        ShiftR  R2,R3, R6,R7            ; shift R3,R2 right R6 places
+                                        ; we only need result word R2
+        LDR     R4, [WsPtr, #SPltLMask-SPltScrAdr] ; on leftmost word
+        AND     R2, R2, R4              ; mask down to just the required pixels
+
+        LDR     R3, [R0]                ; plot 1 word
+        BIC     R3, R3, R4              ; knock out mask bits
+        ORR     R3, R3, R2              ; and or sprite bits
+        STR     R3, [R0], #4
+
+        SUBS    R1, R1, #1
+        BLT     SolPlFast50             ; if all plotted, try next scanline
+                                        ; else try for blocks of 7
+        SUBS    R1, R1, #7
+        BLT     SolPlFast30
+
+SolPlFast20
+
+;       R0    ,R1         ,R5    ,R6   ,R7
+;       ScrAdr,ColCnt,    ,MemAdr,ShftR,ShftL
+
+        LDMIA   R5, {R2-R4,R8-R11,R14}  ; 8 words needed, gives 7 after shift
+        ADD     R5, R5, #7*4            ; advance source ptr 7 words
+
+        ShiftR  R2,R3, R6,R7            ; shift right R6 bits
+        ShiftR  R3,R4, R6,R7            ; we only want result words
+        ShiftR  R4,R8, R6,R7            ; R2-R4, R8-R11
+        ShiftR  R8,R9, R6,R7
+        ShiftR  R9,R10, R6,R7
+        ShiftR  R10,R11, R6,R7
+        ShiftR  R11,R14, R6,R7
+
+        STMIA   R0!, {R2-R4,R8-R11}     ; write 7 words back to screen
+        SUBS    R1, R1, #7
+        BGE     SolPlFast20
+
+SolPlFast30                             ; try 1 word at a time
+        ADDS    R1, R1, #7
+
+;       R0    ,R1    ,     R5    ,R6   ,R7
+;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL
+
+; If EQ this is rightmost word
+
+SolPlFast40
+        LDMIA   R5, {R2,R3}
+        ADD     R5, R5, #4
+
+        ShiftR  R2,R3, R6,R7            ; shift R3,R2 right R6 places
+                                        ; we only need result word R2
+        LDREQ   R4, [WsPtr, #SPltRMask-SPltScrAdr] ; if rightmost word,
+        ANDEQ   R2, R2, R4              ; mask down to just the required pixels
+
+        LDREQ   R3, [R0]
+        BICEQ   R3, R3, R4
+        ORREQ   R2, R2, R3
+        STR     R2, [R0], #4
+
+        SUBS    R1, R1, #1
+        BGE     SolPlFast40
+
+SolPlFast50                             ; now try the next scanline
+        LDMDB   WsPtr, {R1,R2,R3,R4}    ; R1   ,R2    ,R3    ,R4
+                                        ; Width,Height,ScrOff,MemOff
+        ADD     R0, R0, R3
+        ADD     R5, R5, R4
+        SUBS    R2, R2, #1
+        STRGE   R2, [WsPtr, #SPltHeight-SPltScrAdr]
+        BGE     SolPlFast10             ; plot next scanline
+ComplicatedExit
+        SWI     XOS_RestoreCursors
+SpriteOffScreen
+        Pull    "WsPtr, R14"
+        BICS    PC, R14, #V_bit
+
+; Complicated case
+
+SolPl10
+
+;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11
+;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee
+
+; Plot the first (leftmost) word
+
+        LDMIA   R5, {R2,R3}
+        ADD     R5, R5, #4
+
+        ShiftR  R2,R3, R6,R7            ; shift R3,R2 right R6 places
+                                        ; we only need result word R2
+
+        OrrEor  R3,R2, R10,R11          ; form EOR mask
+        OrrEor  R2,R2, R8,R9            ; form OR mask
+
+        LDR     R4, [WsPtr, #SPltLMask-SPltScrAdr] ; on leftmost word
+        AND     R2, R2, R4              ; mask down to just the required pixels
+        AND     R3, R3, R4
+
+        LDR     R4, [R0]                ; plot 1 word
+        OrrEor  R4,R4, R2,R3
+        STR     R4, [R0], #4
+
+        SUBS    R1, R1, #1
+        BLT     SolPl50                 ; if all plotted, try next scanline
+                                        ; else try for blocks of 4
+        SUBS    R1, R1, #4
+        BLT     SolPl30
+
+SolPl20
+        STMIA   WsPtr, {R0,R1}          ; save ScrAdr,ColCnt
+
+;       R0    ,R1         ,R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11
+;       ScrAdr,ColCnt,    ,MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee
+
+        LDMIA   R5, {R0-R4}             ; 5 words needed, gives 4 after shift
+        ADD     R5, R5, #16             ; advance source ptr 4 words
+        STR     R5, [WsPtr, #SPltMemAdr-SPltScrAdr]
+
+        ShiftR  R0,R1, R6,R7            ; shift R4-R0 right R6 bits
+        ShiftR  R1,R2, R6,R7            ; we only want result words R3-R0
+        ShiftR  R2,R3, R6,R7
+        ShiftR  R3,R4, R6,R7
+
+        LDR     R4, [WsPtr]             ; get screen address
+        LDMIA   R4, {R4-R7}             ; get 4 screen words
+
+        ORoreorEORoreor R4,R0, R8,R9,R10,R11, R14
+        ORoreorEORoreor R5,R1, R8,R9,R10,R11, R14
+        ORoreorEORoreor R6,R2, R8,R9,R10,R11, R14
+        ORoreorEORoreor R7,R3, R8,R9,R10,R11, R14
+
+        LDR     R0, [WsPtr]             ; screen address
+        STMIA   R0!, {R4-R7}            ; write 4 words back to screen
+        LDMIB   WsPtr, {R1,R5-R7}       ; reload anything we shat on
+
+        SUBS    R1, R1, #4
+        BGE     SolPl20
+
+SolPl30                                  ; try 1 word at a time
+        ADDS    R1, R1, #4
+
+;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11
+;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee
+
+; If EQ this is rightmost word
+
+SolPl40
+        LDMIA   R5, {R2,R3}
+        ADD     R5, R5, #4
+
+        ShiftR  R2,R3, R6,R7            ; shift R3,R2 right R6 places
+                                        ; we only need result word R2
+        OrrEor  R3,R2, R10,R11          ; form EOR mask
+        OrrEor  R2,R2, R8,R9            ; form OR
+
+        LDREQ   R4, [WsPtr, #SPltRMask-SPltScrAdr] ; if rightmost word,
+        ANDEQ   R2, R2, R4              ; mask down to just the
+        ANDEQ   R3, R3, R4              ; required pixels
+
+        LDR     R4, [R0]
+        OrrEor  R4,R4, R2,R3
+        STR     R4, [R0], #4
+
+        SUBS    R1, R1, #1
+        BGE     SolPl40
+
+SolPl50                                 ; now try the next scanline
+        LDMDB   WsPtr, {R1,R2,R3,R4}    ; R1   ,R2    ,R3    ,R4
+                                        ; Width,Height,ScrOff,MemOff
+        ADD     R0, R0, R3
+        ADD     R5, R5, R4
+        SUBS    R2, R2, #1
+        STRGE   R2, [WsPtr, #SPltHeight-SPltScrAdr]
+        BGE     SolPl10                 ; plot next scanline
+        B       ComplicatedExit
+
+; *****************************************************************************
+;
+;       NewTransPlot - Plot sprite using 1 bpp transparency mask
+;       Called with R11 -> sprite
+;       TransPlot is used for 1bpp new sprites since it will be faster
+
+NewTransPlot ROUT
+
+        ; need to derive: bpp, and 1<<(5-log2bpp)
+
+        LDR     R10, [R11, #spMode]
+        MOV     R10, R10, LSR #27
+        ADRL    R0, NSM_bpptable-4
+        LDR     R10, [R0, R10, LSL #2]   ; get the log2bpp to R10
+
+        ;R10=log2bpp, R11=>sprite
+
+        LDR     R8, [R11, #spWidth]      ; words-1
+
+        ADD     R9, R8, #1
+        MOV     R9, R9, LSL #2           ; number of bytes per row
+        STR     R9, [WsPtr, #SPltMaskRowPtr]
+
+        LDR     R9, [R11, #spRBit]       ; last bit used
+        ADD     R9, R9, #1               ; change to number of bits
+        MOV     R9, R9, LSR R10          ; number of pixels
+
+        RSB     R0, R10, #5
+        MOV     R8, R8, LSL R0           ; number of pixels for full words
+
+        ADD     R8, R8, R9               ; total number of pixels
+
+        ANDS    R9, R8, #&1F
+        MOVNE   R9, #4
+        BIC     R8, R8, #&1F
+        ADD     R9, R9, R8, LSR #3       ; number of bytes per mask row
+
+        STR     R9, [WsPtr, #SPltMaskRowLen]
+
+        LDR     R8, [R11, #spImage]
+        LDR     R9, [R11, #spTrans]
+        ADD     R8, R8, R11
+        ADD     R9, R9, R11
+
+        ;RSB     R0, R10, #5            <- DONE ABOVE
+
+        MOV     R1, #1
+        MOV     R2, R1, LSL R0          ; r2 holds number of pixels per word
+
+        MOV     R3, R1, LSL R10         ; r3 holds bits per pixel
+
+        ADD     WsPtr, WsPtr, #SPltScrAdr       ; repoint WsPtr, at SPltScrAdr
+
+        LDMIA   WsPtr, {R0-R1,R5-R7,R14}
+
+        STR     R2, [WsPtr, #SPltPixPerWord-SPltScrAdr]
+        STR     R3, [WsPtr, #SPltBPP-SPltScrAdr]
+
+        ; sort out where to begin in 1bpp mask (rewritten Aug '93)
+
+        MOV     LR, R9                  ; LR = mask pointer
+        MOV     R3, #0                  ; R3 = mask bit
+        CMP     R5, R8                  ; R5=>data to plot, R8=>start of sprite
+
+        BEQ     %FT11                   ; nothing to do, go store R3 & LR
+        BCC     %FT13                   ; start is before sprite data
+
+        ;R3, R9, r10, R11 free
+        ;R2 comes in holding pix per word
+
+        ;if R6 is non zero the image will be a word early.
+        MOV     R9, R5                  ; working copy of pointer within sprite
+        CMP     R6, #0
+        ADDNE   R9, R9, #4              ; take out the extra word for now
+
+        LDR     R11,[WsPtr, #SPltMaskRowLen-SPltScrAdr] ; bytes per mask row
+
+        SUBS    R10, R9, R8             ; difference in bytes
+
+        LDR     R8,[WsPtr, #SPltMaskRowPtr-SPltScrAdr] ; loaded with bytes per image row
+        BEQ     %FT13
+14
+        ;is it less than a row of data
+        CMP     R10, R8
+        BCC     %FT12                   ; yes it is
+
+        ;deal with a row (or more of data)
+        SUB     R10, R10, R8
+        ADD     LR, LR, R11
+        B       %BT14
+
+12      ;start point is on this row. subtract 4 from difference and add
+        ;pix per word to mask bit/ptr until diff=0
+        CMP     R10, #0
+        BEQ     %FT13
+
+        SUB     R10, R10, #4
+
+        ADD     R3, R3, R2
+        CMP     R3, #32
+        SUBCS   R3, R3, #32
+        ADDCS   LR, LR, #4
+        CMP     R10, #0
+        BNE     %BT12
+
+13
+        ;deal with R6
+        CMP     R6,#0
+        BEQ     %FT11
+
+        SUBS    R3, R3, R2                ;subtract pix per word
+        ADDMI   R3, R3, #32
+        SUBMI   LR, LR, #4                ;deal with going into previous word
+
+11
+        STR     R3, [WsPtr, #SPltMaskBit-SPltScrAdr]
+        STR     LR, [WsPtr, #SPltMaskPtr-SPltScrAdr]
+
+        ; and save it for the end of row increments
+        STR     R3, [WsPtr, #SPltMaskRowBit-SPltScrAdr]
+        STR     LR, [WsPtr, #SPltMaskRowPtr-SPltScrAdr]
+
+        ADD     R11, WsPtr, #SPltzgooMasks-SPltScrAdr
+        LDMIA   R11, {R8-R11}           ; masks for screen access (zgoo..zgee)
+
+NTrnPl10
+
+;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11,    R14
+;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee,
+;
+; As far as possible this is based on the original transplot.
+;
+
+; Plot the first (leftmost) word
+
+        BL      getmaskword
+        MOV     R2, R3
+
+        BL      getmaskword_noinc
+        ShiftR  R2,R3, R6,R7            ; we only need result word R2
+
+        LDMIA   R5, {R3,R4}             ; fetch and shift image
+        ADD     R5, R5, #4
+        ShiftR  R3,R4, R6,R7            ; shift R4,R3 right R6 places
+
+        OrrEor  R4,R3, R10,R11          ; form EOR mask
+        OrrEor  R3,R3, R8,R9            ; form OR mask
+
+        AND     R3, R3, R2              ; clear out any transparent pixels
+        AND     R4, R4, R2
+
+        LDR     R2, [WsPtr, #SPltLMask-SPltScrAdr] ; on leftmost word
+        AND     R3, R3, R2              ; mask down to just the required pixels
+        AND     R4, R4, R2
+
+        LDR     R2, [R0]                ; plot 1 word
+        OrrEor  R2,R2, R3,R4
+        STR     R2, [R0], #4
+
+        SUBS    R1, R1, #1
+        BLT     NTrnPl50                 ; if all plotted, try next scanline
+                                        ; else try for blocks of 2
+        SUBS    R1, R1, #2
+        BLT     NTrnPl30
+
+NTrnPl20
+        STMIA   WsPtr, {R0,R1}          ; ScrAdr,ColCnt
+
+;       R0    ,R1         ,R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11 ,   R14
+;       ScrAdr,ColCnt,    ,MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee,   MskAdr
+
+
+        BL     getmaskword
+        MOV    R0, R3
+        BL     getmaskword
+        MOV    R1, R3
+
+        BL     getmaskword_noinc
+        MOV    R2, R3
+        ShiftR R0,R1, R6,R7
+        ShiftR R1,R2, R6,R7             ; aligned mask in R0,R1
+
+        LDMIA  R5, {R2-R4}              ; 3 image words needed, gives 2 after
+        ADD    R5, R5, #8               ; shifting
+        STR    R5, [WsPtr, #SPltMemAdr-SPltScrAdr]
+        ShiftR R2,R3, R6,R7
+        ShiftR R3,R4, R6,R7             ; aligned image in R2,R3
+
+        LDR    R4, [WsPtr]              ; screen address
+        LDMIA  R4, {R4,R5}              ; 2 screen words
+
+        ORoreorEORoreorMASK R4,R2,R0, R8,R9,R10,R11, R14
+        ORoreorEORoreorMASK R5,R3,R1, R8,R9,R10,R11, R14
+
+        LDR    R0, [WsPtr]              ; screen address
+        STMIA  R0!, {R4,R5}             ; write 2 words back to screen
+        LDMIB  WsPtr, {R1,R5,R6}        ; reload anything we shat on
+
+        SUBS   R1, R1, #2
+        BGE    NTrnPl20
+
+NTrnPl30                                 ; try 1 word at a time
+        ADDS   R1, R1, #2
+
+;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11
+;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee
+
+; If EQ this is rightmost word
+
+NTrnPl40
+
+        BL      getmaskword
+        MOV     R2, R3
+
+        BL      getmaskword_noinc
+        ShiftR  R2,R3, R6,R7    ; shift R3,R2 right R6 places, result in R2
+
+        LDMIA   R5, {R3,R4}     ; fetch and align image
+        ADD     R5, R5, #4
+        ShiftR  R3,R4, R6,R7    ; shift R4,R3 right R6 places, result in R3
+
+        OrrEor  R4,R3, R10,R11  ; form EOR mask
+        OrrEor  R3,R3, R8,R9    ; form OR
+
+        AND     R3, R3, R2      ; clear out transparant pixels
+        AND     R4, R4, R2
+
+        LDREQ   R2, [WsPtr, #SPltRMask-SPltScrAdr] ; if rightmost word,
+        ANDEQ   R3, R3, R2              ; mask down to just the
+        ANDEQ   R4, R4, R2              ; required pixels
+
+        LDR     R2, [R0]
+        OrrEor  R2,R2, R3,R4
+        STR     R2, [R0], #4
+
+        SUBS    R1, R1, #1
+        BGE     NTrnPl40
+
+NTrnPl50
+        ; now try the next scanline
+
+        ; BEWARE ... genspriteplotparmblock returns values for memoff and
+        ; scroff which take account of alignment of screen and sprite data
+        ;
+        ; Do not alter the logic below unless you really know what
+        ; to expect from this routine!!
+
+        LDR     LR, [WsPtr, #SPltMaskRowPtr-SPltScrAdr]
+        LDR     R3, [WsPtr, #SPltMaskRowBit-SPltScrAdr]
+
+        LDR     R2, [WsPtr, #SPltMaskRowLen-SPltScrAdr]
+
+        ADD     LR, LR, R2
+
+        STR     LR, [WsPtr, #SPltMaskPtr-SPltScrAdr]
+        STR     R3, [WsPtr, #SPltMaskBit-SPltScrAdr]
+        STR     LR, [WsPtr, #SPltMaskRowPtr-SPltScrAdr]
+        STR     R3, [WsPtr, #SPltMaskRowBit-SPltScrAdr]
+
+        LDMDB   WsPtr, {R1,R2,R3,R4}    ; R1   ,R2    ,R3    ,R4
+                                        ; Width,Height,ScrOff,MemOff
+
+        ADD     R0, R0, R3
+        ADD     R5, R5, R4
+
+        SUBS    R2, R2, #1
+        STRGE   R2, [WsPtr, #SPltHeight-SPltScrAdr]
+        BGE     NTrnPl10                 ; plot next scanline
+
+        SWI     XOS_RestoreCursors
+
+        Pull    "WsPtr, R14"
+        BICS    PC, R14, #V_bit
+
+; get a mask word without incrementing the pointers
+
+getmaskword_noinc ROUT
+        Push    "R4, R5, LR"
+
+        LDR     R4, [WsPtr, #SPltMaskBit-SPltScrAdr]
+        LDR     R5, [WsPtr, #SPltMaskPtr-SPltScrAdr]
+        BL      getmaskword
+        STR     R4, [WsPtr, #SPltMaskBit-SPltScrAdr]
+        STR     R5, [WsPtr, #SPltMaskPtr-SPltScrAdr]
+
+        LDMFD   R13!,{R4,R5,R15}^
+
+; get a mask word, and increment pointers
+
+getmaskword ROUT
+        Push    "R2,R4,R5,R6,R7,R14"
+
+;R6 -> mask
+;R7 = bit offset
+
+;return in R3, update R6, R7, restore other registers
+
+        MOV     R3, #0                   ; initial result
+        LDR     LR, [WsPtr, #SPltPixPerWord-SPltScrAdr]  ; pixels per word
+        LDR     R2, [WsPtr, #SPltBPP-SPltScrAdr]    ; bpp
+
+        LDR     R7, [WsPtr, #SPltMaskBit-SPltScrAdr]
+        LDR     R6, [WsPtr, #SPltMaskPtr-SPltScrAdr]
+
+        LDR     R4, [R6]                 ; get the mask word
+10
+        CMP     R7, #0
+        LDREQ   R4, [R6]                 ; fetch a new word if required
+        MOV     R5, R4, LSR R7           ; shift desired bit down to bit 0
+
+        MOV     R3, R3, LSR #1           ; shift down the result by one bit
+        ORR     R3, R3, R5, LSL #31      ; and put bit 0 in at bit 31
+
+        ; now use an ASR of bpp-1 to finish off
+        SUB     R5, R2, #1
+        MOV     R3, R3, ASR R5
+
+        ADD     R7, R7, #1               ; next mask bit
+
+        CMP     R7, #32                  ; on to next word ?
+        ADDEQ   R6, R6, #4               ; increment mask word pointer
+        MOVEQ   R7, #0                   ; bit pointer back to 0
+                                         ; but don't fetch new word until it is needed!
+
+        SUBS    LR, LR, #1               ; one pixel done
+        BNE     %BT10
+
+        STR     R7, [WsPtr, #SPltMaskBit-SPltScrAdr]
+        STR     R6, [WsPtr, #SPltMaskPtr-SPltScrAdr]
+
+        ;result in R3, MaskBit/MaskPtr adjusted
+
+        LDMFD   R13!,{R2,R4,R5,R6,R7,R15}^ ; must save flags
+
+        LTORG
+
+; *****************************************************************************
+;
+;       TransPlot - Plot sprite using transparency mask
+;
+
+TransPlot
+        ADD     R11, WsPtr, #SPltzgooMasks
+        LDMIA   R11, {R8-R11}           ; masks for screen access (zgoo..zgee)
+
+        ADD     WsPtr, WsPtr, #SPltScrAdr       ; repoint WsPtr, at SPltScrAdr
+
+        LDMIA   WsPtr, {R0-R1,R5-R7,R14}
+
+TrnPl10
+
+;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11,    R14
+;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee,   MskAdr
+
+; Plot the first (leftmost) word
+
+        LDMIA   R14, {R2,R3}            ; fetch and shift mask right R6 places
+        ADD     R14, R14, #4
+        ShiftR  R2,R3, R6,R7            ; we only need result word R2
+
+        LDMIA   R5, {R3,R4}             ; fetch and shift image
+        ADD     R5, R5, #4
+        ShiftR  R3,R4, R6,R7            ; shift R4,R3 right R6 places
+                                        ; we only need result word R3
+        OrrEor  R4,R3, R10,R11          ; form EOR mask
+        OrrEor  R3,R3, R8,R9            ; form OR mask
+
+        AND     R3, R3, R2              ; clear out any transparent pixels
+        AND     R4, R4, R2
+
+        LDR     R2, [WsPtr, #SPltLMask-SPltScrAdr] ; on leftmost word
+        AND     R3, R3, R2              ; mask down to just the required pixels
+        AND     R4, R4, R2
+
+        LDR     R2, [R0]                ; plot 1 word
+        OrrEor  R2,R2, R3,R4
+        STR     R2, [R0], #4
+
+        SUBS    R1, R1, #1
+        BLT     TrnPl50                 ; if all plotted, try next scanline
+                                        ; else try for blocks of 2
+        SUBS    R1, R1, #2
+        BLT     TrnPl30
+
+TrnPl20
+        STMIA   WsPtr, {R0,R1}          ; ScrAdr,ColCnt
+
+;       R0    ,R1         ,R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11 ,   R14
+;       ScrAdr,ColCnt,    ,MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee,   MskAdr
+
+        LDMIA  R14, {R0-R2}             ; 3 mask words, gives 2 after shifting
+        ADD    R14, R14, #8             ; advance mask ptr 2 words
+        ShiftR R0,R1, R6,R7
+        ShiftR R1,R2, R6,R7             ; aligned mask in R0,R1
+
+        LDMIA  R5, {R2-R4}              ; 3 image words needed, gives 2 after
+        ADD    R5, R5, #8               ; shifting
+        STR    R5, [WsPtr, #SPltMemAdr-SPltScrAdr]
+        ShiftR R2,R3, R6,R7
+        ShiftR R3,R4, R6,R7             ; aligned image in R2,R3
+
+        LDR    R4, [WsPtr]              ; screen address
+        LDMIA  R4, {R4,R5}              ; 2 screen words
+
+        ORoreorEORoreorMASK R4,R2,R0, R8,R9,R10,R11, R6
+        ORoreorEORoreorMASK R5,R3,R1, R8,R9,R10,R11, R6
+
+        LDR    R0, [WsPtr]              ; screen address
+        STMIA  R0!, {R4,R5}             ; write 2 words back to screen
+        LDMIB  WsPtr, {R1,R5-R6}        ; reload anything we shat on
+
+        SUBS   R1, R1, #2
+        BGE    TrnPl20
+
+TrnPl30                                 ; try 1 word at a time
+        ADDS   R1, R1, #2
+
+;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11
+;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee
+
+; If EQ this is rightmost word
+
+TrnPl40
+        LDMIA   R14, {R2,R3}    ; fetch and align trans mask
+        ADD     R14, R14, #4
+        ShiftR  R2,R3, R6,R7    ; shift R3,R2 right R6 places, result in R2
+
+        LDMIA   R5, {R3,R4}     ; fetch and align image
+        ADD     R5, R5, #4
+        ShiftR  R3,R4, R6,R7    ; shift R4,R3 right R6 places, result in R3
+
+        OrrEor  R4,R3, R10,R11  ; form EOR mask
+        OrrEor  R3,R3, R8,R9    ; form OR
+
+        AND     R3, R3, R2      ; clear out transparant pixels
+        AND     R4, R4, R2
+
+        LDREQ   R2, [WsPtr, #SPltRMask-SPltScrAdr] ; if rightmost word,
+        ANDEQ   R3, R3, R2              ; mask down to just the
+        ANDEQ   R4, R4, R2              ; required pixels
+
+        LDR     R2, [R0]
+        OrrEor  R2,R2, R3,R4
+        STR     R2, [R0], #4
+
+        SUBS    R1, R1, #1
+        BGE     TrnPl40
+
+TrnPl50                                 ; now try the next scanline
+        LDMDB   WsPtr, {R1,R2,R3,R4}    ; R1   ,R2    ,R3    ,R4
+                                        ; Width,Height,ScrOff,MemOff
+        ADD     R0, R0, R3
+        ADD     R5, R5, R4
+        ADD     R14, R14, R4
+        SUBS    R2, R2, #1
+        STRGE   R2, [WsPtr, #SPltHeight-SPltScrAdr]
+        BGE     TrnPl10                 ; plot next scanline
+
+        SWI     XOS_RestoreCursors
+
+        Pull    "WsPtr, R14"
+        BICS    PC, R14, #V_bit
+
+        LTORG
+
+; *****************************************************************************
+;
+;       PlotMaskUserCoords - Draw a rectangle through a sprite mask
+;                            using given external coordinates
+;
+;       External routine
+;
+; in:   R1 -> sprite area (not used)
+;       R2 -> sprite
+;       R3 = X coordinate to plot at
+;       R4 = Y coordinate to plot at
+;
+
+PlotMaskUserCoords ROUT
+        Push    R14
+        BL      InternaliseCoords
+        Pull    R14
+        B       PlotMa20
+
+; *****************************************************************************
+;
+; PlotNewMask
+;
+; Version of PlotMask for new 1bpp format sprites
+
+PlotNewMask ROUT
+
+        LDR     R10, [R2, #spMode]
+        MOV     R10, R10, LSR #27
+        ADRL    R0, NSM_bpptable-4
+        LDR     R10, [R0, R10, LSL #2]          ;get the log2bpp
+
+        ;derive mask row length
+        ;r10=log2bpp, r2=>sprite
+
+        LDR     R8, [R2, #spWidth]       ; words-1
+
+        ADD     R9, R8, #1               ; total words for a line of image
+        MOV     R9, R9, LSL #2           ; total bytes for a line of image
+        STR     R9, [WsPtr, #SPltMaskRowPtr]
+                                         ; save it for determining mask start position
+
+        LDR     R9, [R2, #spRBit]        ; last bit used
+        ADD     R9, R9, #1               ; change to number of bits
+        MOV     R9, R9, LSR R10          ; number of pixels
+
+        RSB     R0, R10, #5
+        MOV     R8, R8, LSL R0           ; number of pixels for full words
+
+        ADD     R8, R8, R9               ; total number of pixels
+
+        ANDS    R9, R8, #&1F
+        MOVNE   R9, #4
+        BIC     R8, R8, #&1F
+        ADD     R9, R9, R8, LSR #3       ; number of bytes per mask row
+
+        STR     R9, [WsPtr, #SPltMaskRowLen]
+
+        MOV     R1, #1
+        MOV     R0, R1, LSL R0                  ;number of pixels
+
+        MOV     R6, R1, LSL R10                 ;bits per pixel
+
+        STR     R0, [WsPtr, #SPltPixPerWord]
+        STR     R6, [WsPtr, #SPltBPP]
+
+        LDR     R0, [R2, #spHeight]
+        ADD     R0, R0, R4
+        LDR     R10, [WsPtr, #GWTRow]
+        Least   R0, R0, R10             ; top scanline within window
+
+        LDR     R10, [WsPtr, #YWindLimit]
+        SUB     R0, R10, R0             ; flip Y
+        AND     R0, R0, #7
+        STR     R0, [WsPtr, #SPltEcfIndx] ; index into Ecf
+
+        ADD     R0, WsPtr, R0, LSL #3
+        ADD     R0, R0, #BgEcfOraEor
+        STR     R0, [WsPtr, #SPltEcfPtr] ; ptr to ECF for highest row plotted
+
+        ;STR     R2, [WsPtr, #SPltMaskRowPtr] ;temp, to save it
+
+        LDR     R8, [R2, #spImage]
+        LDR     R9, [R2, #spTrans]
+        ADD     R8, R8, R2
+        ADD     R9, R9, R2
+
+        Push    "WsPtr, R14"            ; push both so R12,R14 are free for use
+        Push    "R2"
+
+        BL      GenSpritePlotParmBlk
+
+        Pull    "R2"
+
+        BVS     SpriteOffScreen
+        SWI     XOS_RemoveCursors       ; assume no error can occur!
+
+        ;LDR     R2, [WsPtr, #SPltMaskRowPtr]  ;recover pointer to sprite
+
+        LDR     R8, [R2, #spImage]            ;offset to image
+        LDR     R9, [R2, #spTrans]            ;offset to mask
+        ADD     R8, R8, R2                    ;change to address of image
+        ADD     R9, R9, R2                    ;change to address of mask
+
+        LDR     R5, [WsPtr, #SPltMemAdr]      ;start memory address to plot
+        LDR     LR, [WsPtr, #SPltMskAdr]      ;start mask address to plot
+        TEQ     LR, #0                        ;off screen ?
+        BEQ     SpriteOffScreen               ;so plot nothing
+
+        ;amg 19/1/94 rip out the original algorithm and replace it with the correct one
+        ;used with newtransplot which correctly handles genspriteplotparmblk's oddities
+
+        ;now deal with sprites where we aren't starting
+        ;at the beginning....
+        LDR     R2, [WsPtr, #SPltPixPerWord]  ;pick up pixels per word
+
+        MOV     LR, R9                        ;save mask address
+        MOV     R3, #0                        ;set mask pixel counter to 0
+        CMP     R5, R8                        ;memory address to plot from = image address ?
+        BEQ     %FT11                         ;yes - no fudging needed
+        BCC     %FT13
+
+        MOV     R9, R5                        ;working copy of plot start within sprite
+        CMP     R6, #0
+        ADDNE   R9, R9, #4                    ;take out the extra word for now
+
+        LDR     R11, [WsPtr, #SPltMaskRowLen] ;bytes per mask row
+        SUBS    R10, R9, R8                   ;difference between plot start & sprite start
+        LDR     R8, [WsPtr, #SPltMaskRowPtr]  ;bytes per image row
+        BEQ     %FT13                         ;no difference
+
+14
+        ;is it less than a row of data different ?
+        CMP     R10, R8
+        BCC     %FT12                         ;yes, it is
+
+        ;deal with whole rows
+        SUB     R10, R10, R8                  ;decrease difference by size of image row
+        ADD     LR, LR, R11                   ;increase mask pointer by size of mask row
+        B       %BT14                         ;and loop until less than a row to do
+
+12      ;start pointer is on this row. reduce the difference and increase the mask start
+        ;point until they match
+
+        CMP     R10, #0                       ;check for nothing to do
+        BEQ     %FT13
+
+        SUB     R10, R10, #4                  ;reduce image by a word
+
+        ADD     R3, R3, R2                    ;increase mask start pixel by the number of
+                                              ;pixels in that word
+
+        CMP     R3, #32
+        SUBCS   R3, R3, #32
+        ADDCS   LR, LR, #4                    ;get the mask bit back into a word's worth
+
+        CMP     R10, #0                       ;extra test down here to avoid taking two
+        BNE     %BT12                         ;branches
+
+13      ;remember R6 ? uncompensate now
+        CMP     R6, #0
+        BEQ     %FT11
+
+        SUBS    R3, R3, R2                    ;go back by the number of pixels in a word
+        ADDMI   R3, R3, #32
+        SUBMI   LR, LR, #4                    ;deal with going back into previous mask word
+
+11
+        STR     R3, [WsPtr, #SPltMaskBit]     ;starting mask bit to plot
+        STR     LR, [WsPtr, #SPltMaskPtr]     ;starting mask word to plot
+
+        ; and save it for the end of row increments
+        STR     R3, [WsPtr, #SPltMaskRowBit]
+        STR     LR, [WsPtr, #SPltMaskRowPtr]
+
+        STR     LR, [WsPtr, #SPltMskAdr]
+        STR     LR, [WsPtr, #SPltMemAdr]
+
+        ADD     WsPtr, WsPtr, #SPltScrAdr ; repoint WsPtr at SPltScrAdr
+
+        LDMIA   WsPtr, {R0-R1,R5-R7}
+
+        LDR     R5, [WsPtr, #SPltMaskPtr-SPltScrAdr]
+        LDR     R8, [WsPtr, #SPltEcfPtr-SPltScrAdr]
+10
+        LDMIA   R8, {R8,R9}             ; ora,eor for this row
+
+;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8 ,R9
+;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,ora,eor
+
+;       Plot the first (Leftmost) word
+
+        BL      getmaskword
+        MOV     R2, R3
+
+        BL      getmaskword_noinc
+
+        ShiftR  R2,R3, R6,R7            ; shift R3,R2 right R6 places
+                                        ; we only need result word R2
+        LDR     R10, [WsPtr, #SPltLMask-SPltScrAdr] ; on leftmost word
+        AND     R2, R2, R10             ; mask down to just the required pixels
+
+        LDR     R4, [R0]                ; plot 1 word
+        OrrEorMASK R4,R2, R8,R9, R14
+        STR     R4, [R0], #4
+
+        SUBS    R1, R1, #1
+        BLT     %FT50                   ; if all plotted, try next scanline
+                                        ; else try for blocks of 4
+        SUBS    R1, R1, #4
+        BLT     %FT30
+
+20
+        STMIA   WsPtr, {R0,R1}          ; save ScrAdr,ColCnt
+
+;       R0    ,R1         ,R5    ,R6   ,R7
+;       ScrAdr,ColCnt,    ,MemAdr,ShftR,ShftL
+
+
+        BL     getmaskword
+        MOV    R0, R3
+        BL     getmaskword
+        MOV    R1, R3
+        BL     getmaskword
+        MOV    R2, R3
+        BL     getmaskword
+        MOV    R8, R3
+        BL     getmaskword_noinc
+        MOV    R4,R3
+        MOV    R3,R8
+
+        ShiftR  R0,R1, R6,R7            ; shift R4-R0 right R6 bits
+        ShiftR  R1,R2, R6,R7            ; we only want result words R3-R0
+        ShiftR  R2,R3, R6,R7
+        ShiftR  R3,R4, R6,R7
+
+        LDR     R4, [WsPtr]             ; get screen address
+        LDMIA   R4, {R8-R11}            ; get 4 screen words
+
+        LDR     R6, [WsPtr, #SPltEcfPtr-SPltScrAdr]
+        LDMIA   R6, {R6,R7}             ; ora,eor for this row
+
+        OrrEorMASK R8,R0,  R6,R7, R14   ; Scr:=ScrOR(oraANDmsk)EOR(eorANDmsk)
+        OrrEorMASK R9,R1,  R6,R7, R14
+        OrrEorMASK R10,R2, R6,R7, R14
+        OrrEorMASK R11,R3, R6,R7, R14
+
+        LDR     R0, [WsPtr]             ; screen address
+        STMIA   R0!, {R8-R11}           ; write 4 words back to screen
+        LDMIB   WsPtr, {R1,R5-R7}       ; reload anything we shat on
+
+        SUBS    R1, R1, #4
+        BGE     %BT20
+
+30                                      ; try 1 word at a time
+        ADDS    R1, R1, #4
+
+;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8 ,R9
+;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,ora,eor
+
+; If EQ this is rightmost word
+
+        LDR     R8, [WsPtr, #SPltEcfPtr-SPltScrAdr]
+        LDMIA   R8, {R8,R9}             ; ora,eor for this row
+        LDR     R10, [WsPtr, #SPltRMask-SPltScrAdr]
+40
+
+        BL      getmaskword
+        MOV     R2, R3
+
+        BL      getmaskword_noinc
+        ShiftR  R2,R3, R6,R7    ; shift R3,R2 right R6 places, result in R2
+                                ; we only need result word R2
+
+        ANDEQ   R2, R2, R10     ; if rightmost word, mask down to just the
+                                ; required pixels
+        LDR     R4, [R0]
+        OrrEorMASK R4,R2, R8,R9, R14
+        STR     R4, [R0], #4
+
+        SUBS    R1, R1, #1
+        BGE     %BT40
+
+50                              ; now try the next scanline
+        ; BEWARE ... genspriteplotparmblock returns values for memoff and
+        ; scroff which take account of alignment of screen and sprite data
+        ;
+        ; Do not alter the logic below unless you really know what
+        ; to expect from this routine!!
+
+        LDMDB   WsPtr, {R1,R2,R3,R4}    ; R1   ,R2    ,R3    ,R4
+                                        ; Width,Height,ScrOff,MemOff
+
+
+        LDR     LR, [WsPtr, #SPltMaskRowPtr-SPltScrAdr]
+        LDR     R3, [WsPtr, #SPltMaskRowBit-SPltScrAdr]
+
+        LDR     R2, [WsPtr, #SPltMaskRowLen-SPltScrAdr]
+
+        ADD     LR, LR, R2
+
+        STR     LR, [WsPtr, #SPltMaskPtr-SPltScrAdr]
+        STR     R3, [WsPtr, #SPltMaskBit-SPltScrAdr]
+        STR     LR, [WsPtr, #SPltMaskRowPtr-SPltScrAdr]
+        STR     R3, [WsPtr, #SPltMaskRowBit-SPltScrAdr]
+
+        LDMDB   WsPtr, {R1,R2,R3,R4}    ; R1   ,R2    ,R3    ,R4
+                                        ; Width,Height,ScrOff,MemOff
+        ADD     R0, R0, R3
+
+        ADD     R10, WsPtr, #(SPltEcfPtr-SPltScrAdr)
+
+        LDMIA   R10, {R8,R9}    ; EcfPtr,EcfIndx
+        ADD     R8, R8, #8      ; step both to next row in Ecf
+        ADD     R9, R9, #1
+        CMP     R9, #8
+        MOVGE   R9, #0          ; it's a wrap!
+        SUBGE   R8, R8, #(8*8)
+        STMIA   R10, {R8,R9}
+
+        SUBS    R2, R2, #1
+        STRGE   R2, [WsPtr, #SPltHeight-SPltScrAdr]
+        BGE     %BT10                   ; plot next scanline
+
+        SWI XOS_RestoreCursors
+
+        Pull    "WsPtr, R14"
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       PlotMask - Draw a rectangle through a sprite mask
+;
+;       External routine + PlotMa20 called by PlotMaskUserCoords
+;        (also external)
+;
+; in:   R1 -> sprite area (not used)
+;       R2 -> sprite
+;
+
+PlotMask ROUT
+        ASSERT  NewPtY = NewPtX +4
+        ADD     R3, WsPtr, #NewPtX
+        LDMIA   R3, {R3, R4}            ; plot sprite at NewPt(X,Y)
+PlotMa20
+    [ {TRUE}
+        GraphicsMode R0
+        ADRNEL  R0, SpriteErr_NotGraphics
+      [ International
+        Push    "lr",NE
+        BLNE    TranslateError
+        Pull    "lr",NE
+      ]
+        STRNE   R0, [WsPtr, #RetnReg0]
+        ORRNES  PC, Link, #V_bit        ; quit with error if not graphics mode
+    |
+        LDR     R0, [WsPtr, #NPix]
+        TEQ     R0, #0
+        ADREQL  R0, SpriteErr_NotGraphics
+      [ International
+        Push    "lr",EQ
+        BLEQ    TranslateError
+        Pull    "lr",EQ
+      ]
+        STREQ   R0, [WsPtr, #RetnReg0]
+        ORREQS  PC, Link, #V_bit        ; quit with error if not graphics mode
+    ]
+
+        LDR     R5, [WsPtr, #GPLBMD]    ; background GCOL action
+        ORR     R5, R5, #8              ; force 'use mask'
+        LDR     R10, [R2, #spImage]
+        LDR     R11, [R2, #spTrans]
+        TEQ     R10, R11                ; spImage=spTrans if no mask
+        BEQ     %FT90                   ; so plot a rectangle
+
+        LDR     R0, [R2, #spMode]
+        MOVS    R0, R0, LSR #27
+        BNE     PlotNewMask
+
+        LDR     R0, [R2, #spHeight]
+        ADD     R0, R0, R4
+        LDR     R10, [WsPtr, #GWTRow]
+        Least   R0, R0, R10             ; top scanline within window
+
+        LDR     R10, [WsPtr, #YWindLimit]
+        SUB     R0, R10, R0             ; flip Y
+        AND     R0, R0, #7
+        STR     R0, [WsPtr, #SPltEcfIndx] ; index into Ecf
+
+        ADD     R0, WsPtr, R0, LSL #3
+        ADD     R0, R0, #BgEcfOraEor
+        STR     R0, [WsPtr, #SPltEcfPtr] ; ptr to ECF for highest row plotted
+
+        Push    "WsPtr, R14"            ; push both so R12,R14 are free for use
+        BL      GenSpritePlotParmBlk
+        BVS     SpriteOffScreen
+        SWI     XOS_RemoveCursors       ; assume no error can occur!
+
+        LDR     R5, [WsPtr, #SPltMemAdr]
+        LDR     R6, [WsPtr, #SPltMskAdr]
+        TEQ     R6, #0
+        BEQ     SpriteOffScreen
+
+        ADD     R6, R6, R5
+        STR     R6, [WsPtr, #SPltMskAdr]
+        STR     R6, [WsPtr, #SPltMemAdr]
+
+        ADD     WsPtr, WsPtr, #SPltScrAdr ; repoint WsPtr at SPltScrAdr
+
+        LDMIA   WsPtr, {R0-R1,R5-R7}
+        LDR     R8, [WsPtr, #SPltEcfPtr-SPltScrAdr]
+10
+        LDMIA   R8, {R8,R9}             ; ora,eor for this row
+
+;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8 ,R9
+;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,ora,eor
+
+;       Plot the first (Leftmost) word
+
+        LDMIA   R5, {R2,R3}
+        ADD     R5, R5, #4
+
+        ShiftR  R2,R3, R6,R7            ; shift R3,R2 right R6 places
+                                        ; we only need result word R2
+        LDR     R10, [WsPtr, #SPltLMask-SPltScrAdr] ; on leftmost word
+        AND     R2, R2, R10             ; mask down to just the required pixels
+
+        LDR     R4, [R0]                ; plot 1 word
+        OrrEorMASK R4,R2, R8,R9, R14
+        STR     R4, [R0], #4
+
+        SUBS    R1, R1, #1
+        BLT     %FT50                   ; if all plotted, try next scanline
+                                        ; else try for blocks of 4
+        SUBS    R1, R1, #4
+        BLT     %FT30
+
+20
+        STMIA   WsPtr, {R0,R1}          ; save ScrAdr,ColCnt
+
+;       R0    ,R1         ,R5    ,R6   ,R7
+;       ScrAdr,ColCnt,    ,MemAdr,ShftR,ShftL
+
+        LDMIA   R5, {R0-R4}             ; 5 words needed, gives 4 after shift
+        ADD     R5, R5, #16             ; advance source ptr 4 words
+        STR     R5, [WsPtr, #SPltMemAdr-SPltScrAdr]
+
+        ShiftR  R0,R1, R6,R7            ; shift R4-R0 right R6 bits
+        ShiftR  R1,R2, R6,R7            ; we only want result words R3-R0
+        ShiftR  R2,R3, R6,R7
+        ShiftR  R3,R4, R6,R7
+
+        LDR     R4, [WsPtr]             ; get screen address
+        LDMIA   R4, {R8-R11}            ; get 4 screen words
+
+        LDR     R6, [WsPtr, #SPltEcfPtr-SPltScrAdr]
+        LDMIA   R6, {R6,R7}             ; ora,eor for this row
+
+        OrrEorMASK R8,R0,  R6,R7, R14   ; Scr:=ScrOR(oraANDmsk)EOR(eorANDmsk)
+        OrrEorMASK R9,R1,  R6,R7, R14
+        OrrEorMASK R10,R2, R6,R7, R14
+        OrrEorMASK R11,R3, R6,R7, R14
+
+        LDR     R0, [WsPtr]             ; screen address
+        STMIA   R0!, {R8-R11}           ; write 4 words back to screen
+        LDMIB   WsPtr, {R1,R5-R7}       ; reload anything we shat on
+
+        SUBS    R1, R1, #4
+        BGE     %BT20
+
+30                                      ; try 1 word at a time
+        ADDS    R1, R1, #4
+
+;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8 ,R9
+;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,ora,eor
+
+; If EQ this is rightmost word
+
+        LDR     R8, [WsPtr, #SPltEcfPtr-SPltScrAdr]
+        LDMIA   R8, {R8,R9}             ; ora,eor for this row
+        LDR     R10, [WsPtr, #SPltRMask-SPltScrAdr]
+40
+        LDMIA   R5, {R2,R3}
+        ADD     R5, R5, #4
+
+        ShiftR  R2,R3, R6,R7    ; shift R3,R2 right R6 places
+                                ; we only need result word R2
+
+        ANDEQ   R2, R2, R10     ; if rightmost word, mask down to just the
+                                ; required pixels
+        LDR     R4, [R0]
+        OrrEorMASK R4,R2, R8,R9, R14
+        STR     R4, [R0], #4
+
+        SUBS    R1, R1, #1
+        BGE     %BT40
+
+50                              ; now try the next scanline
+
+        LDMDB   WsPtr, {R1,R2,R3,R4}    ; R1   ,R2    ,R3    ,R4
+                                        ; Width,Height,ScrOff,MemOff
+        ADD     R0, R0, R3
+        ADD     R5, R5, R4
+
+        ADD     R10, WsPtr, #(SPltEcfPtr-SPltScrAdr)
+
+        LDMIA   R10, {R8,R9}    ; EcfPtr,EcfIndx
+        ADD     R8, R8, #8      ; step both to next row in Ecf
+        ADD     R9, R9, #1
+        CMP     R9, #8
+        MOVGE   R9, #0          ; it's a wrap!
+        SUBGE   R8, R8, #(8*8)
+        STMIA   R10, {R8,R9}
+
+        SUBS    R2, R2, #1
+        STRGE   R2, [WsPtr, #SPltHeight-SPltScrAdr]
+        BGE     %BT10                   ; plot next scanline
+
+        SWI XOS_RestoreCursors
+
+        Pull    "WsPtr, R14"
+        BICS    PC, R14, #V_bit
+
+; Sprite has no mask or gcol says dont use it, so draw a rectangle
+
+90
+        Push    R14
+        BL      ReadSpriteWidth         ; on exit R0=width in pixels
+        ADD     R5, R3, R0
+        MOV     R0, R3                  ; x0
+        MOV     R1, R4                  ; y0
+
+        LDR     R3, [R2, #spHeight]
+        ADD     R3, R3, R4              ; y1
+        SUB     R2, R5, #1              ; x1
+
+        LDR     R4, [WsPtr, #CursorFlags]
+        TST     R4, #ClipBoxEnableBit
+        BLNE    ClipPlotMask
+
+        ADD     R4, WsPtr, #BgEcfOraEor ; select Bg colour & action
+        STR     R4, [WsPtr, #GColAdr]
+        B       RectFillB
+
+ClipPlotMask ROUT
+        Push    "R0-R7,R10,R11, R14"
+        ADD     R10, R13, #2*4          ; R10 -> last point (R2,R3)
+        MOV     R11, #2                 ; merge two points
+        BL      MergeR11PointsFromR10
+        Pull    "R0-R7,R10,R11, PC"
+
+; *****************************************************************************
+;
+;       GenSpritePlotParmBlk - Generate lots of useful variables to help us
+;                              to plot sprite
+;
+;       Internal routine, called by PutSprite, PlotMask, ScreenLoad
+;
+; in:   R2 -> sprite
+;       R3 = X coordinate to plot at
+;       R4 = Y coordinate to plot at
+;       R5 = GCOL action
+;
+
+GenSpritePlotParmBlk ROUT
+        Push    R14
+        AND     R5, R5, #&F             ; lose any ECF colour bits
+        LDR     R6, [R2, #spImage]
+        LDR     R7, [R2,#spTrans]
+        SUB     R6, R7, R6              ; offset from Image to Trans mask
+                                        ; =0 if no mask
+        STR     R5, [WsPtr, #SPltAction] ; save action for simple case spotting
+        CMP     R5, #8                  ; if GCOL action < 8
+        MOVLT   R6, #0                  ; plot as solid anyway
+        STR     R6, [WsPtr, #SPltMskAdr]
+
+        AND     R5, R5, #7
+        MOV     R5, R5, LSL #2          ; 4 bits for each
+        LDR     R6, =TBscrmasks
+        MOV     R6, R6, ROR R5          ; put correct bits in top 4 bits
+        MOV     R7, R6, ASR #31         ; set R7 to 0 or -1 on bit 31
+        MOV     R6, R6, LSL #1
+        MOV     R8, R6, ASR #31         ; set R8 to 0 or -1 on bit 30
+        MOV     R6, R6, LSL #1
+        MOV     R9, R6, ASR #31         ; set R9 to 0 or -1 on bit 29
+        MOV     R6, R6, LSL #1
+        MOV     R10, R6, ASR #31        ; set R10 to 0 or -1 on bit 28
+
+        ADD     R6, WsPtr, #SPltzgooMasks
+        STMIA   R6, {R7-R10}            ; store zgoo, zgeo, zgoe, zgee
+
+        MOV     R14, R2                 ; leave sprite CB ptr in R14
+        MOV     R0, R3                  ; leave X coord in R0
+        MOV     R1, R4                  ; leave Y coord in R1
+
+        ADD     R11, R14, #spWidth
+        LDMIA   R11, {R2,R3}            ; Width-1, Height-1
+
+        ADD     R4, WsPtr, #GWLCol      ; R4  ,R5  ,R6  ,R7
+        LDMIA   R4, {R4,R5,R6,R7}       ; LCol,BRow,RCol,TRow
+
+        SUBS    R5, R5, R1
+        MOVLT   R5, #0                  ; no. of rows below window
+
+        ADD     R1, R1, R3              ; Coord of topLH of sprite
+
+        SUBS    R7, R1, R7
+        MOVLT   R7, #0                  ; no. of rows above window
+        SUB     R1, R1, R7              ; clipped topLH coord
+
+        ADD     R5, R5, R7              ; reduction factor for height
+
+        ADD     R2, R2, #1
+
+        MOV     R8, R14
+        LDR     R9, [WsPtr, #CursorFlags]
+        TST     R9, #ClipBoxEnableBit
+        BLNE    ClipSpritePlot
+        MOV     R14, R8
+
+        MUL     R3, R7, R2              ; word offset into sprite image
+
+        Push    "R3,R5"                 ; word offset and height reduction
+
+        Push    R14
+        BL      ScreenAddr
+        Pull    R14
+        MOV     R4, R2                  ; address of top left corner
+
+        LDR     R5, [R14, #spImage]
+        ADD     R5, R5, R14             ; address of sprite image
+
+        Pull    R6
+        ADD     R5, R5, R6, LSL #2
+
+        LDR     R9, [WsPtr, #XShftFactor]
+        LDR     R10, [WsPtr, #NPix]
+        LDR     R11, [WsPtr, #Log2BPC]
+
+        BitLOffset R7,R0, R9,R10,R11    ; R7 := bit position to align to
+        WordOffset R8,R0, R9,R10,R11    ; R8 := word offset on line
+
+        LDR     R0, [WsPtr, #GWRCol]
+        BitROffset R1,R0, R9,R10,R11
+        WordOffset R2,R0, R9,R10,R11
+        Push    "R1,R2"
+
+        LDR     R0, [WsPtr, #GWLCol]
+        BitLOffset R1,R0, R9,R10,R11
+        WordOffset R2,R0, R9,R10,R11
+
+        Push    "R1,R2,R8"
+
+        ADD     R11, R14, #spWidth
+        LDMIA   R11, {R0,R1,R6,R8}      ; Width-1, Height-1, LBit, RBit
+
+        MOV     R3, #0                  ; offset to next row in sprite
+
+        SUBS    R6, R6, R7              ; no. of bits to shift sprite
+                                        ; before plotting
+                                        ; use R7 as LBit
+        SUBS    R8, R8, R6              ; calculate new RBit
+        ADDLT   R8, R8, #32
+        ADDLT   R3, R3, #4
+
+        CMP     R8, #32
+        SUBGE   R8, R8, #32
+        SUBGE   R3, R3, #4
+
+; R9  Offset on line to plot point, R7 LBit
+; R11 Offset on line to GWLCol      R10 bit position
+
+        Pull    "R10,R11"
+        Pull    R9
+
+        SUBS    R9, R11, R9
+        SUBLT   R11, R11, R9
+
+        ADDGT   R4, R4, R9, LSL #2
+        ADDGT   R5, R5, R9, LSL #2
+        ADDGT   R3, R3, R9, LSL #2
+
+        CMPEQ   R10, R7
+        MOVGT   R7, R10
+
+; R10 Offset to GWRCol, R9 bit position
+; R11 Offset to RHedge of sprite, R8 RBit
+
+        ADD     R11, R11, R0
+        SUB     R11, R11, R3, ASR #2
+
+        Pull    "R9,R10"
+
+        SUBS    R10, R11, R10
+        ADDGT   R3, R3, R10, LSL #2
+
+        CMPEQ   R8, R9
+        MOVGT   R8, R9
+
+        Pull    R10
+
+        SUBS    R1, R1, R10             ; correct height
+        BLT     %FT20
+
+        SUBS    R0, R0, R3, ASR #2      ; corrected width
+        BLT     %FT20
+
+        LDR     R2,[WsPtr,#LineLength]
+        SUB     R2, R2, #4
+        SUB     R2, R2, R0, LSL #2      ; offset to next screen line
+
+        MOV     R9, #&FFFFFFFE          ; RHand partial word mask
+        MVN     R9, R9, LSL R8
+        MOV     R8, #&FFFFFFFF
+        MOV     R8, R8, LSL R7          ; LHand partial word mask
+
+        ANDEQ   R8, R8, R9              ; if width=0, combine LH & RH masks
+        MOVEQ   R9, R8
+
+        CMP     R6, #0
+        ADDLT   R6, R6, #32             ; correct if neg
+        SUBLT   R5, R5, #4
+        RSB     R7, R6, #32             ; its complement
+
+        ADD     R11, WsPtr, #SPltWidth
+        STMIA   R11, {R0,R1,R2,R3,R4}   ; SPltWidth..SPltScrAdr
+
+        ADD     R11, WsPtr, #SPltColCnt
+        STMIA   R11, {R0,R5,R6,R7}      ; SPltColCnt..SPltShftL
+
+        ADD     R11, WsPtr, #SPltMskAdr
+        STMIB   R11, {R8,R9}            ; SPltLMask,SPltRMask
+
+        Pull    R14
+        BICS    PC, R14, #V_bit         ; VC some/all of sprite in window
+
+20
+        Pull    R14
+        ORRS    PC, R14, #V_bit         ; VS sprite completely outside window
+
+
+        END
diff --git a/s/vdu/vdugrafh b/s/vdu/vdugrafh
new file mode 100644
index 0000000000000000000000000000000000000000..b7f6206b171c3834981d7e7d8ce1bc9213f9707d
--- /dev/null
+++ b/s/vdu/vdugrafh
@@ -0,0 +1,1189 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduGrafH
+;
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Vdu driver code - Sprite stuff
+;
+; Authors   RManby, TDobson
+; Started   10.11.86
+
+; ************************************************************
+; ***    C h a n g e   L i s t  (better late than never!)  ***
+; ************************************************************
+
+; Date       Description
+; ----       -----------
+; 17-Feb-88  Created change list
+;            Fixed bug in SLOAD (errors weren't reported)
+; 08-Apr-88  Changed LoadFile to use open with nodir + mustopen
+; 20-May-88  Changed NoRoomToLoad to NotEnoughRoom
+
+; *****************************************************************************
+;
+;       MergeSpriteFile - Merge sprite file from filing system
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;       R2 -> filename
+;
+
+MergeSpriteFile ROUT
+        Push    R14
+        KillSpChoosePtr
+        LDR     R3, [R1, #saFree]
+        ADD     R3, R3, R1
+        BL      LoadFile        ; R1 -> sprite area, R2 -> filename
+        Pull    PC, VS          ; R3 -> free space
+        SUB     R2, R3, #4
+        BL      MergeSpriteAreas ; in: R1 -> destination, R2 -> source, V=0
+        Pull    R14
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       LoadSpriteFile - Load sprite file from filing system
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;       R2 -> filename
+;
+
+LoadSpriteFile ROUT
+        KillSpChoosePtr
+        ADD     R3, R1, #saNumber       ; R3 = load address
+
+; and drop thru to LoadFile (V will be set/cleared by that routine)
+
+; *****************************************************************************
+;
+;       LoadFile - Load sprite file to particular address
+;
+;       Internal routine, called by LoadSpriteFile, MergeSpriteFile
+;
+; in:   R1 -> sprite area
+;       R2 -> filename
+;       R3 = load address
+;
+; out:  VS => error during load, R0 -> error
+;       VC => loaded OK, R0 undefined
+;       R1-R11 preserved
+;
+
+LoadFile ROUT
+        Push    "R1-R6, R14"
+
+ ; new version (07-Dec-89) to help broadcast loader a bit
+
+        Push    "R2,R3"                 ; filename ptr, load address
+        LDR     R6, [R1, #saEnd]
+        ADD     R6, R6, R1
+        SUB     R6, R6, R3              ; free space = saEnd-LoadAddress
+
+        MOV     R0, #OSFile_ReadInfo
+        MOV     R1, R2                  ; filename ptr
+        SWI     XOS_File                ; OSFILE(5), returns R0 type, R4 length
+        BVS     %FT10                   ; R0 file type, R4 length
+
+        TEQ     R0, #object_file        ; if not a file
+        BNE     %FT08                   ; then make into suitable error
+
+        CMP     R6, R4                  ; will file fit in available space ?
+        ADRCCL  R0, SpriteErr_NotEnoughRoom
+      [ International
+        BLCC    TranslateError
+      ]
+        BCC     %FT10
+
+; There is room to load file, so load the lot
+
+        MOV     R0, #OSFile_Load
+        Pull    "R1, R2"                ; filename ptr, load address
+        MOV     R3, #0                  ; use given address
+        SWI     XOS_File
+        BVS     %FT20
+
+; TMD 07-Oct-91 (G-RO-9262)
+; New code inserted here
+; Check validity of sprite file
+; R4 = file length from XOS_File above
+
+; TMD 06-Mar-92 (RP-0589)
+; Validity check weakened to only check that
+; offset to first sprite is < length of file
+
+        LDR     R3, [sp, #2*4]          ; R3 = load address
+        LDR     R1, [R3, #saFirst-4]    ; offset to first sprite must be < length of file
+        CMP     R1, R4
+        Pull    "R1-R6, PC",CC          ; R0 is corrupt, R1-R11 preserved, V=0
+
+; it was a bad file, so make it look like an empty sprite area before erroring
+; so that in SLoad case we don't get a naff sprite area.
+
+05
+        MOV     R0, #0
+        STR     R0, [R3, #saNumber-4]
+        MOV     R0, #SpriteAreaCBsize
+        STR     R0, [R3, #saFirst-4]
+        STR     R0, [R3, #saFree-4]
+
+        ADRL    R0, SpriteErr_BadSpriteFile
+      [ International
+        BL      TranslateError
+      ]
+        B       %FT20
+
+08
+        MOV     R2, R0
+        MOV     R0, #OSFile_MakeError   ; then make into suitable error
+        SWI     XOS_File
+10
+        ADD     R13, R13, #2*4          ; balance stack
+20
+        Pull    "R1-R6, R14"            ; return with error
+        STR     R0, [WsPtr, #RetnReg0]
+        ORRS    PC, R14, #V_bit         ; R1-R11 preserved
+
+; *****************************************************************************
+;
+;       SaveSpriteFile - Save sprite area to filing system
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;       R2 -> filename
+;
+
+SaveSpriteFile ROUT
+        LDR     R3, [R1, #saNumber]             ; if no sprites
+        TEQ     R3, #0
+        BEQ     %FT10                           ; then quit with error
+
+        Push    R14
+        ADD     R4, R1, #saNumber               ; save sprite area, excluding
+        LDR     R5, [R1, #saFree]               ; free space or saEnd
+        ADD     R5, R5, R1
+
+        [ AssemblingArthur :LOR: Module
+        MOV     R0, #OSFile_SaveStamp
+        MOV     R1, R2                          ; filename ptr
+        LDR     R2, =&FF9                       ; type=SpriteFile
+        MOV     R3, #0                          ; junk
+        |
+        MOV     R0, #0                          ; normal save
+        MOV     R1, R2                          ; filename ptr
+        MOV     R2, #0
+        MOV     R3, #0
+        ]
+
+        SWI     XOS_File                        ; save file
+        STRVS   R0, [WsPtr, #RetnReg0]
+        Pull    PC
+
+10
+        ADRL    R0, SpriteErr_NoSprites
+      [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      ]
+        STR     R0, [WsPtr, #RetnReg0]
+        ORRS    PC, R14, #V_bit
+
+        LTORG
+
+; *****************************************************************************
+;
+;       MergeSpriteAreas - Merge two sprite areas
+;
+;       Internal routine, called by MergeSpriteFile
+;
+; in:   R1 -> destination sprite area
+;       R2 -> source sprite area
+;       V=0
+;
+; out:  All registers preserved
+;
+
+MergeSpriteAreas ROUT
+        TEQ     R2, #0                  ; validate R2 (R1 already checked)
+        BEQ     %FT30
+        Push    "R1-R4, R14"
+
+        LDR     R4, [R2, #saNumber]     ; number of sprites to merge
+        TEQ     R4, #0
+        BEQ     %FT20                   ; nothing to merge
+
+        LDR     R3, [R2, #saFirst]
+        ADD     R2, R2, R3
+10                                      ; R1 -> dest area CB,
+        BL      MergeSprite             ; R2 -> to sprite CB
+                                        ; return R2 -> next sprite
+        BVS     %FT20                   ; error 'NoRoomToMerge'
+        SUBS    R4, R4, #1              ; (V:=0 if becoming zero)
+        BNE     %BT10
+20
+        Pull    "R1-R4, PC"             ; exit with V already set up
+
+30
+        ADRL    R0, SpriteErr_Bad2ndPtr
+      [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      ]
+        STR     R0, [WsPtr, #RetnReg0]
+        ORRS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       MergeSprite - Merge sprite into sprite area
+;
+;       Internal routine, called by MergeSpriteAreas
+;
+; in:   R1 -> destination area
+;       R2 -> source sprite
+;
+; out:  R2 -> past end of source sprite (ie next sprite)
+;
+
+MergeSprite ROUT
+        Push    "R1, R3-R5, R14"
+        ADD     R2, R2, #spName
+        BL      DeleteSpriteByName      ; in: R1-R2; out: all preserved
+        SUB     R2, R2, #spName
+AddSprite
+        LDR     R3, [R1, #saFree]
+        LDR     R5, [R1, #saEnd]
+        SUB     R5, R5, R3              ; amount of free ram in dest area
+
+        LDR     R4, [R2, #spNext]       ; space needed for sprite
+        CMP     R5, R4
+        BCC     %FT10
+
+        ADD     R3, R3, R1              ; first free locn in dest. area
+        CopyDown R3,R2,R4,R5,R14        ; NB CopyDown will exit with R3 -> end
+                                        ; of dest area
+        LDR     R4, [R1, #saNumber]     ; update number of sprites
+        ADD     R4, R4, #1
+        STR     R4, [R1, #saNumber]
+
+        SUB     R3, R3, R1              ; and free space offset
+        STR     R3, [R1, #saFree]
+
+        Pull    "R1, R3-R5, R14"
+        BICS    PC, R14, #V_bit         ; ignore 'not found' from DeleteSprite
+
+10
+        Pull    "R1, R3-R5, R14"
+        ADRL    R0, SpriteErr_NoRoomToMerge
+      [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      ]
+        STR     R0, [WsPtr, #RetnReg0]
+        ORRS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       AppendSprite - Append sprite to sprite area
+;
+;       Internal routine, called by CopySprite
+;
+; in:   R1 -> destination area
+;       R2 -> source sprite
+;
+; out:  R2 -> past end of source sprite (ie next sprite)
+;
+
+AppendSprite ROUT
+        Push    "R1, R3-R5, R14"
+        B       AddSprite
+
+; *****************************************************************************
+;
+;       ExtendHorizontally - Add one column to RHS of the sprite
+;
+;       Internal routine, called by InsertCol
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;
+
+ExtendHorizontally ROUT
+        Push    "R3-R6, R9-R11, R14"
+        LDR     R3, [R2, #spRBit]
+        LDR     R4, [WsPtr, #SprBytesPerChar]
+        ADD     R3, R3, R4              ; alter spRBit to include next pixel
+        CMP     R3, #32
+        BCC     %FT20
+                                        ; >=32 means add 1 word to each row
+        LDR     R3, [R2, #spHeight]
+        ADD     R3, R3, #1              ; extend by (spHeight+1) words
+        BL      ExtendSprite            ; (space doubled if mask present)
+        Pull    "R3-R6, R9-R11, PC", VS ; no room, then give error
+
+        MOV     R4, #0
+        BL      MaskOffset              ; use spHeight+1 as row counter,
+        MOVNE   R3, R3, LSL #1          ; doubled if mask present
+
+        LDRNE   R5, [R2, #spTrans]      ; correct mask ptr, if mask present
+        ADDNE   R5, R5, R3, LSL #1      ; R3 is words*2, hence LSL
+        STRNE   R5, [R2, #spTrans]
+
+        BL      InsertWords             ; Insert R3 words at position R4 in
+                                        ; sprite, ie at beginning
+        LDR     R5, [R2, #spWidth]
+        ADD     R5, R5, #1              ; new spWidth
+        STR     R5, [R2, #spWidth]
+
+        LDR     R9, [R2, #spImage]
+        ADD     R9, R9, R2              ; to
+        ADD     R10, R9, R3, LSL #2     ; from
+
+10
+        MOV     R11, R5, LSL #2
+                                        ; move one row
+        CopyDown R9,R10,R11,R14,R6      ; To,From,Size, Temp
+
+        STR     R4, [R9], #4            ; add 1 word at RH end of row
+        SUBS    R3, R3, #1
+        BHI     %BT10                   ; next row
+
+        LDR     R3, [WsPtr, #SprBytesPerChar]
+        SUBS    R3, R3, #1
+20
+        STR     R3, [R2, #spRBit]
+        Pull    "R3-R6, R9-R11, R14"
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       ReduceHorizontally - Remove some bits from the RHS of the sprite
+;
+;       Internal routine, called by DeleteCol
+;
+; in:   R0 = number of bits to delete off right hand side
+;       R1 -> sprite area
+;       R2 -> sprite
+;
+
+ReduceHorizontally ROUT
+        Push    "R3-R6, R9-R11, R14"
+        LDR     R3, [R2, #spRBit]
+        SUBS    R3, R3, R0              ; alter spRBit to exclude those bits
+        ADDCC   R3, R3, #32             ; if underflow then extract whole word
+        STR     R3, [R2, #spRBit]
+        BCS     %FT20
+                                        ; < 0 means remove 1 word per row
+        LDR     R3, [R2, #spHeight]
+        ADD     R3, R3, #1              ; remove (spHeight+1) words
+        MOV     R4, #0
+        BL      MaskOffset
+        MOVNE   R3, R3, LSL #1          ; doubled if mask present
+
+        LDRNE   R5, [R2, #spTrans]      ; correct mask ptr, if mask present
+        SUBNE   R5, R5, R3, LSL #1      ; R3 is words*2, hence LSL
+        STRNE   R5, [R2, #spTrans]
+
+        LDR     R9, [R2, #spImage]
+        ADD     R9, R9, R2              ; to
+        MOV     R10, R9                 ; from
+        LDR     R5, [R2, #spWidth]
+10
+        MOV     R11, R5, LSL #2
+                                        ; move one row
+        CopyDown R9,R10,R11,R14,R6      ; To,From,Size, Temp
+
+
+        ADD     R10, R10, #4            ; skip unwanted word
+        SUBS    R3, R3, #1
+        BHI     %BT10                   ; next row
+
+; R9 -> past end of this sprite
+; R10 -> next sprite
+
+        SUB     R3, R10, R9
+        MOV     R3, R3, LSR #2          ; no. of words to remove
+        SUB     R4, R9, R2
+        LDR     R9, [R2, #spImage]
+        SUB     R4, R4, R9              ; byte offset within image
+
+        BL      RemoveWords
+
+        LDR     R5, [R2, #spWidth]
+        SUB     R5, R5, #1              ; new spWidth
+        STR     R5, [R2, #spWidth]
+20
+        Pull    "R3-R6, R9-R11, R14"
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       InsertRow - Insert blank row into sprite
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;       R3 = row number to insert below (0 => bottom, spHeight+1 => above top)
+;
+
+InsertRow ROUT
+        Push    "R0,R4,R14"
+        MOV     R0, #57                 ; SpriteOp reason code for insert/delete rows
+        ORR     R0, R0, #512            ;Set it to use pointers to user area & sprite
+        MOV     R4, #1                  ; We're only inserting one row! (put 1 in)
+        SWI     XOS_SpriteOp
+        BVS     %FT20
+
+        Pull    "R0,R4,R14"
+        BICS    PC, R14, #V_bit         ; exit OK
+
+20
+        STR     R0, [WsPtr, #RetnReg0]
+        Pull    "R0,R4,R14"
+        ORRS    PC, R14, #V_bit         ; exit with error
+
+; *****************************************************************************
+;
+;       DeleteRow - Delete row from sprite
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;       R3 = row number to remove (0=bottom, spHeight=top)
+;
+
+DeleteRow ROUT
+        Push    "R0,R4,R14"
+        MOV     R0, #57                 ; SpriteOp reason code for insert/delete rows
+        ORR     R0, R0, #512            ;Set it to use pointers to user area & sprite
+        MVN     R4, #0                  ; We're only removing one row! (put -1 in)
+        SWI     XOS_SpriteOp
+        BVS     %FT20
+
+        Pull    "R0,R4,R14"
+        BICS    PC, R14, #V_bit         ; exit OK
+
+20
+        STR     R0, [WsPtr, #RetnReg0]
+        Pull    "R0,R4,R14"
+        ORRS    PC, R14, #V_bit         ; exit with error
+
+; *****************************************************************************
+;
+;       InsertCol - Insert blank column into sprite
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;       R3 = column to insert at (0 => before left..width => after right)
+;
+
+InsertCol ROUT
+        Push    "R0,R4,R14"
+        MOV     R0, #58                 ; SpriteOp reason code for insert/delete cols
+        ORR     R0, R0, #512            ;Set it to use pointers to user area & sprite
+        MOV     R4, #1                  ; We're only inserting one column! (put 1 in)
+        SWI     XOS_SpriteOp
+        BVS     %FT20
+
+        Pull    "R0,R4,R14"
+        BICS    PC, R14, #V_bit         ; exit OK
+
+20
+        STR     R0, [WsPtr, #RetnReg0]
+        Pull    "R0,R4,R14"
+        ORRS    PC, R14, #V_bit         ; exit with error
+
+; *****************************************************************************
+;
+;       DeleteCol - Delete column from sprite
+;
+;       External routine, and LHWastageEntry called from RemoveLeftHandWastage
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;       R3 = column number to remove (0 => left, width-1 => right)
+;
+
+DeleteCol ROUT
+        Push    "R0,R4,R14"
+        MOV     R0, #58                 ; SpriteOp reason code for insert/delete cols
+        ORR     R0, R0, #512            ;Set it to use pointers to user area & sprite
+        MVN     R4, #0                  ; We're only removing one row! (put -1 in)
+        SWI     XOS_SpriteOp
+        BVS     %FT20
+
+        Pull    "R0,R4,R14"
+        BICS    PC, R14, #V_bit         ; exit OK
+
+20
+        STR     R0, [WsPtr, #RetnReg0]
+        Pull    "R0,R4,R14"
+        ORRS    PC, R14, #V_bit         ; exit with error
+
+; *****************************************************************************
+;
+;       ExtendSprite - Add R3 words to the end of the sprite (R3*2 if mask)
+;
+;       Internal routine, called by ExtendHorizontally, InsertRow, CreateMask,
+;        and ExtendSpriteByR3 called by GetSprite
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;       R3 = no. of words to insert (gets doubled if mask present)
+;
+
+ExtendSpriteByR3 ROUT
+        Push    "R3, R8-R11, R14"
+        B       ExtendSprite10
+
+ExtendSprite ROUT
+        Push    "R3, R8-R11, R14"
+        BL      MaskOffset
+        MOVNE   R3, R3, LSL #1          ; double no. of words if mask present
+ExtendSprite10
+        LDR     R10, [R1, #saEnd]
+        LDR     R11, [R1, #saFree]
+        SUB     R10, R10, R3, LSL #2
+        CMP     R10, R11
+        BCC     %FT10
+
+        LDR     R10, [R2, #spNext]
+        ADD     R10, R10, R2            ; copy source
+        ADD     R9, R10, R3, LSL #2     ; copy destination
+
+        LDR     R11, [R1, #saFree]
+        ADD     R11, R11, R1
+        SUB     R11, R11, R10           ; size (bytes) to copy
+        CopyUp  R9,R10,R11, R14, R8     ; To,From,Size,Temp, Temp2
+
+        LDR     R9, [R1, #saFree]
+        ADD     R9, R9, R3, LSL #2
+        STR     R9, [R1, #saFree]       ; update saFree
+
+        LDR     R9, [R2, #spNext]
+        ADD     R9, R9, R3, LSL #2
+        STR     R9, [R2, #spNext]       ; update spNext
+
+        Pull    "R3, R8-R11, R14"
+        BICS    PC, R14, #V_bit
+
+10
+        ADRL    R0, SpriteErr_NoRoomToInsert
+      [ International
+        BL      TranslateError
+      ]
+        STR     R0, [WsPtr, #RetnReg0]
+        Pull    "R3, R8-R11, R14"
+        ORRS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       InsertWords - Insert R3 words into given sprite at specified position
+;
+;       Internal routine, called by ExtendHorizontally, InsertRow
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;       R3 = number of words to insert
+;       R4 = insertion point (byte offset within sprite image)
+;
+;       NB Assumes ExtendSprite has been called to leave R3 extra words
+;       at the end of the sprite
+;
+; out:  All registers preserved
+;
+
+InsertWords ROUT
+        Push    "R8-R11, R14"
+        LDR     R10, [R2, #spImage]
+        ADD     R10, R10, R2
+        ADD     R10, R10, R4            ; copy source
+        ADD     R9, R10, R3, LSL #2     ; copy destination
+        LDR     R11, [R2, #spNext]
+        ADD     R11, R11, R2
+        SUB     R11, R11, R9            ; size (bytes) to copy
+        CopyUp  R9,R10,R11, R14,R8      ; To,From,Size,Temp, Temp2
+        Pull    "R8-R11, R14"
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       ClearWords - Clear R3 words in sprite
+;
+;       Internal routine, called by InsertRow, CreateSprite
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;       R3 = number of words to clear
+;       R4 = byte offset within sprite image to clear from
+;
+; out:  All registers preserved
+;
+
+ClearWords ROUT
+        Push    "R9-R11, R14"
+        LDR     R10, [R2, #spImage]
+        ADD     R10, R10, R2
+        ADD     R10, R10, R4            ; clear from
+        MOVS    R9, R3
+        MOVNE   R11, #0
+10
+        STRNE   R11, [R10], #4
+        SUBNES  R9, R9, #1
+        BNE     %BT10
+        Pull    "R9-R11, R14"
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       RemoveWords - Delete R3 words from given sprite
+;
+;       Internal routine, called by ReduceHorizontally, DeleteRow, RemoveMask,
+;        GetSprite
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;       R3 = number of words to remove
+;       R4 = removal point (byte offset within sprite image)
+;
+; out:  All registers preserved
+;       spNext (in sprite) and spFree (in sprite area) updated
+;
+
+RemoveWords ROUT
+        Push    "R8-R11, R14"
+        LDR     R9, [R2, #spImage]
+        ADD     R9, R9, R2
+        ADD     R9, R9, R4              ; copy destination
+        ADD     R10, R9, R3, LSL #2     ; copy source
+        LDR     R11, [R1, #saFree]
+        ADD     R11, R11, R1
+        SUB     R11, R11, R10           ; size (bytes) to copy
+        CopyDown R9,R10,R11, R14,R8     ; To,From,Size,Temp, temp2
+        SUB     R9, R9, R1
+        STR     R9, [R1, #saFree]       ; update saFree
+        LDR     R9, [R2, #spNext]
+        SUB     R9, R9, R3, LSL #2
+        STR     R9, [R2, #spNext]       ; update spNext
+        Pull    "R8-R11, PC"
+
+; *****************************************************************************
+;
+;       MaskOffset - Read mask size (0 if absent)
+;
+;       Internal routine, called by ExtendHorizontally, ReduceHorizontally,
+;        InsertRow, DeleteRow, ExtendSprite, FlipAboutXAxis
+;
+; in:   R2 -> sprite
+;
+; out:  R0 = 0 if no mask, otherwise mask size
+;       EQ if no mask, NE if mask present
+;
+
+MaskOffset ROUT
+        Push    R14
+        LDR     R0, [R2, #spImage]
+        LDR     R14, [R2, #spTrans]
+        SUBS    R0, R14, R0             ; offset from Image to Trans mask
+                                        ; =0 if no mask
+        Pull    PC                      ; return EQ/NE for nomask/mask
+
+; *****************************************************************************
+;
+;       ReadPixelColour - Read colour of a pixel in a given sprite
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;       R3 = X coordinate of pixel (0 = left)
+;       R4 = Y coordinate of pixel (0 = bottom)
+;
+; out:  RetnReg5 = colour  (0..NColour) or  0..63
+;       RetnReg6 = tint     0           or  0/64/128/192
+;
+
+ReadPixelColour ROUT
+        Push    R14
+        BL      SpriteGenAddr           ; returns R6,      R7
+        Pull    PC, VS                  ;         Address, Bit position
+        LDR     R5, [R6]                ; word from sprite
+        LDR     R6, [WsPtr, #SprReadNColour]
+
+        CMP     R6, #63                 ; check for 256 colour
+        MOVEQ   R6, #255
+
+        AND     R0, R6, R5, LSR R7      ; extract one pixel (bit0..)
+
+        CMP     R6, #255
+
+        MOVNE   R2, R0                  ; colour = pixel
+        MOVNE   R3, #0                  ; tint   = 0
+        BNE     %FT10
+
+        ;now check for size of palette
+        ADD     R2, R2, #spImage        ; point to image/mask start
+        LDMIA   R2, {R2, R3}
+        CMP     R3, R3
+        MOVGT   R3, R3
+        SUB     R2, R2, #spPalette
+        CMP     R2, #&0800
+
+        ;see comment below - for this call to work we have to temporarily
+        ;set NColour to SprReadNColour
+
+        LDRNE   R8,[WsPtr,#NColour]
+        STRNE   R6,[WsPtr,#NColour]
+
+        BLNE    ExtractTintAndColour    ; else extract colour & tint from pixel
+
+        STRNE   R8,[WsPtr,#NColour]
+
+        MOVEQ   R2, R0
+        MOVEQ   R3, #0
+10
+        STR     R2, [WsPtr, #RetnReg5]  ; pass colour in R5
+        STR     R3, [WsPtr, #RetnReg6]  ; and tint in R6
+        Pull    R14
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       WritePixelColour - Write a pixel in a given sprite
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;       R3 = X coordinate of pixel (0 = left)
+;       R4 = Y coordinate of pixel (0 = bottom)
+;       R5 = pixel colour
+;       R6 = tint
+;
+; out:  R1-R11 preserved
+;
+
+; amg: note. need to handle full palettes differently here - since the GCOL and TINT
+; model should not apply. Check for a full palette on an 8bpp sprite and deal with
+; it accordingly.
+; amg: bug fix to MED-01885. Although ReadPixel and WritePixel use SprReadNColour,
+; then call AddTintToColour which uses NColour - being the screen's value not the
+; sprites. Therefore, the safest fix this near freeze is to simply temporarily change
+; the NColour value for the call, and then restore it.
+
+WritePixelColour ROUT
+        Push    "R1-R4, R14"
+        LDR     R8, [WsPtr, #SprReadNColour]
+
+        AND     R0, R5, R8              ; limit colour to 0..NColour
+
+        CMP     R8, #63                 ; check for 256 colours
+        CMPNE   R8, #255
+        BNE     %FT08                   ; despatch non 8bpp
+
+        ;now need to determine size of 8bpp sprite's palette
+        ADD     R3, R2, #spImage        ; point to image/mask start
+        LDMIA   R3, {R2, R3}            ; fetch them
+        CMP     R2, R3                  ; which is higher
+        MOVGT   R2, R3                  ; use the lesser
+        SUB     R2, R2, #spPalette      ; subtract start offset
+        CMP     R2, #&800
+
+        MOVNE   R3, R6                  ; then combine
+
+        ;see comment above - for this call to work we have to temporarily
+        ;set NColour to SprReadNColour
+
+        LDRNE   R7,[WsPtr,#NColour]
+        STRNE   R8,[WsPtr,#NColour]
+
+        BLNE    AddTintToColour         ; colour & tint
+
+        STRNE   R7,[WsPtr,#NColour]
+
+        B       %FT05                   ; 8 bpp take this branch
+08
+        ADDCC   R0, R0, R8              ; else index into full colour table
+        ADRCCL  R5, TBFullCol
+        LDRCCB  R0, [R5, R0]            ; N.B. a table of bytes
+        BCC     %FT05                   ; 1,2,4 bpp take this branch
+
+        ; if 16bpp only need to shift round once, if 32bpp not at all
+        LDR     LR, [R2, #spMode]
+        MOV     LR, LR, LSR #27
+        CMP     LR, #6
+        MOV     R5, R0
+        BCS     %FT06                   ; 32 bpp takes this branch
+
+        B       %FT07                   ; and 16 bpp takes this one
+05
+        ORR     R5, R0, R0, LSL #8      ; expand byte value into a word
+07
+        ORR     R5, R5, R5, LSL #16
+06
+        Pull    "R1-R4"
+        BL      SpriteGenAddr           ; returns R6,      R7
+        Pull    PC, VS                  ;         Address, Bit position
+        LDR     R8, [WsPtr, #SprWriteNColour]
+        AND     R5, R5, R8              ; limit colour to pixel width
+        LDR     R0, [R6]                ; word from sprite
+        BIC     R0, R0, R8, LSL R7
+        ORR     R0, R0, R5, LSL R7
+        STR     R0, [R6]
+        Pull    R14
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       ReadPixelMask - Read mask state for a pixel in a given sprite
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;       R3 = X coordinate of pixel (0 = left)
+;       R4 = Y coordinate of pixel (0 = bottom)
+;
+; out:  RetnReg5 = 0/1 (transparent/solid)
+;
+
+ReadPixelMask ROUT
+        Push    R14
+        LDR     R5, [R2, #spImage]
+        LDR     R6, [R2, #spTrans]
+        SUBS    R5, R6, R5              ; offset from Image to Trans mask
+        MOVEQ   R5, #1                  ; if =0, no mask so pixel is solid
+        BEQ     %FT10
+        BL      SpriteMaskAddr          ; returns R6,      R7
+        Pull    PC, VS                  ;         Address, Bit position
+        LDR     R5, [R6, R5]            ; word from mask
+
+        LDR     LR, [R2, #spMode]       ; check for 1bpp masks
+        MOVS    LR, LR, LSR #27
+        MOVNE   R6, #1
+        LDREQ   R6, [WsPtr, #SprReadNColour]
+
+        ANDS    R5, R6, R5, LSR R7      ; extract one mask pixel (bit0..)
+        MOVNE   R5, #1
+10
+        STR     R5, [WsPtr, #RetnReg5]
+        Pull    R14
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       WritePixelMask - Write a pixel in the mask for a given sprite
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;       R3 = X coordinate of pixel (0 = left)
+;       R4 = Y coordinate of pixel (0 = bottom)
+;       R5 = pixel mask 0/1 (transparent/solid)
+;
+
+WritePixelMask ROUT
+        Push    R14
+        LDR     R8, [R2, #spImage]
+        LDR     R9, [R2, #spTrans]
+        SUBS    R9, R9, R8              ; offset from Image to Trans mask
+        BEQ     %FT10                   ; if =0, no mask so quit
+        BL      SpriteMaskAddr          ; returns R6,      R7
+        Pull    PC, VS                  ;         Address, Bit position
+
+        LDR     LR, [R2, #spMode]
+        MOVS    LR, LR, LSR #27
+        LDREQ   R8, [WsPtr, #SprWriteNColour]
+        MOVNE   R8, #1                  ; adjust for new format sprites
+
+        TEQ     R5, #0
+        MOVNE   R5, R8
+        LDR     R0, [R6, R9]            ; word from mask
+        BIC     R0, R0, R8, LSL R7
+        ORR     R0, R0, R5, LSL R7
+        STR     R0, [R6, R9]
+10
+        Pull    R14
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       SpriteGenAddr - Generate address for a given (X,Y) position
+;
+;       SpriteMaskAddr - For use on mask (copes with old/new masks)
+;
+;       Internal routine, called by InsertCol, DeleteCol, ReadPixelColour,
+;        WritePixelColour, ReadPixelMask, WritePixelMask
+;
+;       Note that InsertCol and DeleteCol are *not* being altered for the
+;       present round of 1bpp mask work.
+;
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;       R3 = X coordinate of pixel (0 = left)
+;       R4 = Y coordinate of pixel (0 = bottom)
+;
+; out:  R6 = address
+;       R7 = bit position of pixel
+;       R1-R5, R8-R11 preserved
+;       V=1 => outside sprite, R0 -> error
+;
+
+SpriteMaskAddr ROUT
+        Push    "R1-R5, R8-R11, LR"
+
+        LDR     LR, [R2, #spMode]
+        MOVS    LR, LR, LSR #27         ; get the sprite type from the mode word
+        BEQ     %FT10                   ; branch: old format, use old routine as is
+
+        ADD     R5, R2, #spWidth
+        LDMIA   R5, {R5-R8}             ; R5     ,R6      ,R7    ,R8
+                                        ; spWidth,spHeight,spLBit,spRBit
+        SUBS    R4, R6, R4              ; invert Y coord
+        BCC     %FT90                   ; must be in range 0..spHeight
+
+        BL      GetMaskspWidth          ; change R5 to suit the mask width
+                                        ; and R8 to new last bit used
+
+        MLA     R0, R5, R4, R4          ; word offset to row = Y*(width+1)
+
+        ;for new format masks the depth is fixed, so...
+
+        MOV     R9, #5                  ; XShftFactor
+        MOV     R10, #31                ; SprNPix
+        MOV     R11, #0                 ; Log2BPC
+
+        B       %FT20                   ; and continue in the old code
+
+SpriteGenAddr
+        Push    "R1-R5, R8-R11, LR"
+10
+        ADD     R5, R2, #spWidth
+        LDMIA   R5, {R5-R8}             ; R5     ,R6      ,R7    ,R8
+                                        ; spWidth,spHeight,spLBit,spRBit
+        SUBS    R4, R6, R4              ; invert Y coord
+        BCC     %FT90                   ; must be in range 0..spHeight
+
+        MLA     R0, R5, R4, R4          ; word offset to row = Y*(width+1)
+
+        LDR     R9, [WsPtr, #SprXShftFactor]
+        LDR     R10, [WsPtr, #SprNPix]
+        LDR     R11, [WsPtr, #SprLog2BPC]
+20
+        BitLOffset R6,R3, R9,R10,R11
+        WordOffset R3,R3, R9,R10,R11
+
+; sprite starts LBit bits into word
+; so add LBit to bit offset
+
+        ADD     R7, R6, R7
+        ADD     R3, R3, R7, LSR #5      ; if offset>=32 then inc word address
+        AND     R7, R7, #31             ; force offset into range 0..31
+
+        CMP     R3, R5                  ; R3 should now be in range 0..spWidth
+        CMPEQ   R7, R8                  ; if R3=spWidth, then check bit posn
+        BHI     %FT90                   ; is within sprite
+
+        ADD     R6, R0, R3              ; word offset into sprite
+        ADD     R6, R2, R6, LSL #2
+        LDR     R8, [R2, #spImage]
+        ADD     R6, R6, R8              ; byte address of word in sprite
+
+        Pull    "R1-R5, R8-R11, LR"
+        BICS    PC,Link,#V_bit
+
+90
+        ADRL    R0, SpriteErr_InvalidRowOrCol
+      [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      ]
+        STR     R0, [WsPtr, #RetnReg0]
+        Pull    "R1-R5, R8-R11, LR"
+        ORRS    PC, R14, #V_bit
+
+
+; *****************************************************************************
+;
+;       GetMaskspWidth - convert spWidth for data to spWidth for mask (1bpp masks)
+;
+;       Internal routine, called from spritemaskaddr & switchouputtomask
+;
+; in:   R5 = spWidth (ie width in words-1)
+;       (expects R2->sprite)
+;
+; out:  R5 = spWidth (words -1) for mask data
+;       R8 modified for new last bit used in mask data
+
+; should only be called for new format sprites, but will cope with old too
+
+;NOTE: If any changes are made to this routine, please look at the SpriteExtend
+;source too, as there is a very similiar routine there too (in SprAdjSize). WT
+
+GetMaskspWidth ROUT
+        Push    "R0,LR"
+
+        LDR     LR, [R2, #spMode]       ; fetch the sprite mode
+        MOVS    LR, LR, LSR #27         ; isolate the sprite type and test for =0
+
+        Pull    "R0,PC",EQ              ; if an old format sprite, return R5 unchanged
+
+        ; treat any T>max sprites as 32bpp
+        CMP     LR, #SpriteType_MAX
+        MOVCS   LR, #SpriteType_Substitute
+
+        ; bugfix 9/8/93: get log2bpp this way
+        ADRL    R0, NSM_bpptable-4
+        LDR     LR, [R0, LR, LSL #2]    ; get the log2bpp to LR
+
+        RSB     LR, LR, #5              ; and change to 5-log2bpp
+
+        MOV     R5, R5, LSL LR          ; number of pixels for full words
+
+        RSB     LR, LR, #5              ; now switch back to log2bpp
+        LDR     R0, [R2, #spRBit]
+        ADD     R0, R0, #1
+        ADD     R5, R5, R0, LSR LR
+
+        ANDS    LR, R5, #&1F            ; fit exactly in a number of words ?
+        SUB     R8, LR, #1              ; alter the last bit used for the mask data
+                                        ; fix bug MED-01130....
+        AND     R8, R8, #&1F            ; ....bring back into range 00-1F (may be -1 here)
+        MOVNE   LR, #1                  ; if not, add an extra word
+        ADD     R5, LR, R5, LSR #5      ; add the whole number of words
+        SUB     R5, R5, #1              ; returns as words-1
+
+        Pull    "R0,PC"
+
+
+; *****************************************************************************
+;
+;       RemoveLeftHandWastage - Remove left-hand wastage from a sprite
+;
+;       Internal routine, but made external for testing
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;
+
+RemoveLeftHandWastage ROUT
+        LDR     R0, [R2, #spLBit]
+        TEQ     R0, #0                  ; is there any wastage ?
+        BICEQS  PC, R14, #V_bit         ; no, then exit straight away
+
+        Push    "R1, R2, R14"           ; get stack the same as in DeleteCol
+        LDR     R11, [R2, #spImage]
+        ADD     R11, R11, R2            ; R11 := address of first word
+        MOV     R7, #0                  ; bit position of first pixel
+        STR     R7, [R2, #spLBit]       ; pretend LBit = 0
+        MOV     R10, #0                 ; byte offset from LH end to delete pt.
+
+        LDR     R9, [R2, #spWidth]
+        MOV     R9, R9, LSL #2          ; byte offset from delete pt to LH end
+                                        ; of next row -4
+        LDR     R8, [R2, #spNext]
+        ADD     R8, R8, R2              ; first byte after sprite
+
+        MOV     R2, R0                  ; number of bits to delete
+LHWastageEntry
+        RSB     R3, R2, #32                     ; LShft := 32-RShft
+
+        MOV     R4, #1
+        RSB     R4, R4, R4, LSL R7      ; mask for pixels left of deletion pt.
+        MVN     R5, R4                  ; inclusive & right of extractn. pt.
+
+; R0,   R1,   R2   ,R3   ,R4   ,R5   ,R6   ,R7     ,R8    ,R9     ,R10   .R11
+;   ,     ,   RShft,LShft,LMask,RMask,     ,WordCnt,EndAdr,WordOff,RowOff,Adr
+
+; R11 -> LH end of row
+
+10
+        ADD     R11, R11, R10           ; step to deletion point
+        LDR     R0, [R11]
+        AND     R1, R5, R0, LSR R2      ; extract & shift rightmost pixels
+                                        ; (ie MSBits)
+        AND     R0, R4, R0              ; extract leftmost pixels (ie LSBits)
+        ORR     R0, R0, R1              ; recombine (unwanted pixel removed)
+        LDR     R1, [R11, #4]           ; shift leftmost pixel of next word
+        ORR     R0, R0, R1, LSL R3      ; in at rightmost end of this word
+        STR     R0, [R11], #4           ; NB #4 to cope with naff rowoff (R10)
+        CMP     R9, #0
+        BEQ     %FT30
+        MOV     R7, R9
+20
+        LDMIA   R11,{R0,R1}             ; now do a 1 pixel shift left
+        MOV     R0, R0, LSR R2          ; of the rest of the row
+        ORR     R0, R0, R1, LSL R3
+        STR     R0, [R11], #4
+        SUBS    R7, R7, #4
+        BGT     %BT20
+
+; R11 -> LH end of next row
+
+30
+        CMP     R8, R11
+        BHI     %BT10                   ; if EndAdr>Adr, do next row
+
+        MOV     R0, R2                  ; R0 = number of bits to delete
+        LDMFD   R13, {R1,R2}
+        BL      ReduceHorizontally
+        Pull    "R1-R2, R14"
+        BICS    PC, R14, #V_bit
+
+60
+        STR     R0, [WsPtr, #RetnReg0]
+70
+        Pull    "R1-R2, R14"
+        ORRS    PC, R14, #V_bit
+
+
+; ******************************************************************************
+;
+;        bounce_new_format_masks - object to masks on new format sprites
+;
+;        enter with R2->sprite
+;        either returns with all registers preserved, or VS and R0->error
+
+bounce_new_format_masks ROUT
+        STMFD   R13!,{R0,R14}
+        LDR     LR, [R2, #spMode]       ; fetch the sprites mode
+        MOVS    LR, LR, LSR #27         ; set NE if new format
+        LDMEQFD R13!,{R0,R15}           ; out now if old format
+        BL      MaskOffset              ; returns R0=mask size, EQ if no mask, NE if mask
+        LDMEQFD R13!,{R0,R15}           ; out now if no mask
+        ADRL    R0, SpriteErr_NoMaskOrPaletteAllowedInThisDepth
+        [ International
+        BL      TranslateError
+        ]
+        SETV
+        STR     R0,[R13]
+        STR     R0,[WsPtr, #RetnReg0]
+        LDMFD   R13!,{R0,R15}
+
+        END
diff --git a/s/vdu/vdugrafi b/s/vdu/vdugrafi
new file mode 100644
index 0000000000000000000000000000000000000000..459ab061fd38a1c9e250af1b32b886903b5f1ea0
--- /dev/null
+++ b/s/vdu/vdugrafi
@@ -0,0 +1,346 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduGrafI
+;
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Vdu driver code - Sprite stuff
+;
+; Author R C Manby
+; Date   10.11.86
+;
+; CHANGES
+; -------
+; 12.04.94   W Turner   Updated to allow the use of 1bpp masks
+; 12.01.95   G Simms    Fixed MED-04130 where New format sprites with
+;                       LH wastage were being created.
+
+; *****************************************************************************
+;
+;       FlipAboutXAxis - Flip sprite about X axis
+;
+;       External routine
+;
+; in:   R2 -> sprite
+;
+
+FlipAboutXAxis ROUT
+        Push    R14
+
+        BL      MaskOffset
+        MOV     R7, R0                  ; 0/offset for nomask/mask
+        LDR     R4, [R2, #spWidth]      ; width-1
+        LDR     R5, [R2, #spHeight]     ; height-1
+        LDR     R6, [R2, #spImage]
+        TEQ     R5, #0
+        BEQ     %FT05                   ; nothing to do, if only 1 row
+
+        ADD     R0, R2, R6              ; R0 -> top row in image
+        ADD     R3, R4, #1              ; width
+        MUL     R1, R5, R3              ; R1 = word offset to bottom row
+        ADD     R1, R0, R1, LSL #2      ; R1 -> bottom row in image
+
+        Push    "R0-R1, R2, R4-R5, R7"      ; preserve ptrs & mask offset
+        BL      FlipAbX10               ; flip main image
+        Pull    "R0-R1, R2, R4-R5, R7"
+
+        CMP     R7, #0
+        BEQ     %FT05                   ; No mask so skip this bit
+
+        ADD     R0, R0, R7              ; Update the start pointer
+
+        ;If a new format sprite (we know it has a mask)
+        ;redo R1 & call the 'GetMaskspWidth' routine to alter R3
+
+        LDR     R8, [R2, #spMode]       ; Get sprite mode
+        MOVS    R8, R8, LSR #27         ; Isolate sprite type & test for 0
+
+        ADDEQ   R1, R1, R7              ; Old format, so simple
+        BEQ     %FT04
+
+        ;Here, we know it is a 1bpp mask
+        MOV     R6, R5                  ; Better keep R5 safe, we'll need it in a bit
+        MOV     R5, R4                  ; The sub wants R4 in r5
+        BL      GetMaskspWidth          ; Update the 'width' if needed
+        ADD     R3, R5, #1              ; R3=width in words
+
+        MUL     R1, R6, R3              ; R1 = word offset to bottom row
+        ADD     R1, R0, R1, LSL #2      ; R1 -> Bottom row in image
+04
+        BL      FlipAbX10               ; flip mask
+05
+        Pull    R14
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       FlipAbX10 - Flip area given ptrs and width
+;
+;       Internal routine, called by FlipAboutXAxis
+;
+; in:   R0 -> top row
+;       R1 -> bottom row
+;       R3 = width in words
+;
+; out:  R0-R2,R4-R11 corrupted
+;       R3 preserved
+;
+
+FlipAbX10 ROUT
+10
+        SUBS    R2, R3, #4              ; initialise width count
+        BLT     %FT30
+20                                      ; flip in blocks of 4 words
+        LDMIA   R0, {R4-R7}
+        LDMIA   R1, {R8-R11}
+        STMIA   R1!, {R4-R7}
+        STMIA   R0!, {R8-R11}
+        SUBS    R2, R2, #4
+        BGE     %BT20
+30
+        ADDS    R2, R2, #4
+        BLE     %FT50
+40                                      ; do remaining words one by one
+        LDR     R4, [R0]
+        LDR     R8, [R1]
+        STR     R4, [R1], #4
+        STR     R8, [R0], #4
+        SUBS    R2, R2, #1
+        BNE     %BT40
+50
+        SUB     R1, R1, R3, LSL #3      ; point to previous row
+        CMP     R1, R0
+        BHI     %BT10                   ; loop until pointers meet or cross
+        MOVS    PC, R14                 ; only R3 preserved
+
+; *****************************************************************************
+;
+;       FlipAboutYAxis - Flip a sprite about Y axis
+;
+;       External routine
+;
+; in:   R2 -> sprite
+;
+
+FlipAboutYAxis ROUT
+        Push    R14
+
+        BL      MaskOffset
+        MOV     R8, R0                  ;Bung result in R8 'till we want it...
+
+        ADD     R3, R2, #spWidth
+        LDMIA   R3, {R3-R7}     ; R3     ,R4      ,R5    ,R6    ,R7
+                                ; spWidth,spHeight,spLBit,spRBit,spImage
+        ADD     R3, R3, #1              ; use width as row offset (words)
+
+        MUL     R4, R3, R4              ; R4=width x (height-1) in words
+        ADD     R4, R4, R3              ; R4=width x height in words
+        ADD     R4, R7, R4, LSL #2      ; offset past end of sprite image
+        ADD     R4, R4, R2              ; address past end of image
+
+        RSB     R5, R5, #31             ; reflect LBit & RBit
+        RSB     R6, R6, #31
+        STR     R5, [R2, #spRBit]       ; new RBit := 31- old LBit
+        STR     R6, [R2, #spLBit]       ; new LBit := 31- old RBit
+        ADD     R0, R2, R7              ; R0 -> start of first row
+        LDR     R11, [WsPtr, #SprBytesPerChar]  ; shift factor to
+                                                ; reach next pixel
+        LDR     R9, [WsPtr, #SprWriteNColour]   ; mask for leftmost pixel
+        MOV     R10, R9, ROR R11                ; mask for rightmost pixel
+
+        Push    "R0, R2, R5-R8"
+        BL      FlipAbY10               ; Do the sprite image
+        Pull    "R0, R2, R5-R8"
+
+        Push    "R0,R8"
+        LDR     R8, [R2, #spMode]       ; Get sprite mode
+        MOVS    R8, R8, LSR #27         ; Isolate sprite type & test for 0
+        BEQ     %FT03                   ; If old format ignore next bit
+
+        ; If this is a new format sprite we may have to remove any LH
+        ; wastage that was created by the flip.
+        CMP     R6, #0                  ; Is there any LH wastage?
+        BEQ     %FT03                   ; If not skip the next bit.
+        MOV     R8, R6
+        BL      RemLHWastage
+        ; If this is a new format sprite then LH wastage = 0 and the RH wastage
+        ; is the same as it was to start with.
+        RSB     R5, R6, #31             ; restore old RBit
+        MOV     R6, #0
+        STR     R5, [R2, #spRBit]       ; new RBit := 31- old LBit
+        STR     R6, [R2, #spLBit]       ; new LBit := 0
+03
+        Pull    "R0,R8"
+        CMP     R8, #0                  ; Does the sprite have a mask?
+        BEQ     %FT05                   ; Nope, so skip the mask flip!
+
+        ;Now, is it an old or new sprite?
+        LDR     R1, [R2, #spMode]       ; Get sprite mode
+        ADD     R0, R0, R8              ; R0 points to start of mask
+        MOVS    R1, R1, LSR #27         ; Isolate sprite type & test for 0
+
+        ADDEQ   R4, R4, R8              ;R4 points to end of mask now
+        BEQ     %FT04                   ;Skip the next bit (it's for new format only)
+
+        Push    "R5"
+        LDR     R5, [R2, #spWidth]
+        BL      GetMaskspWidth
+        ADD     R3, R5, #1              ;R3 is new row offset (words)
+        Pull    "R5"
+
+        Push    "R8"                    ;Last Bit used for mask, this will enable
+                                        ;us to remove the left hand wastage after
+                                        ;the flip
+
+        LDR     R4, [R2, #spHeight]
+        LDR     R8, [R2, #spTrans]
+        MUL     R4, R3, R4              ; R4=width x (height-1) in words
+        ADD     R4, R4, R3              ; R4=width x height in words
+        ADD     R4, R8, R4, LSL #2      ; offset past end of sprite mask
+        ADD     R4, R4, R2              ; address past end of mask
+
+        ADD     R0, R2, R8              ; R0 -> start of first row of mask
+;        LDR     R11, [WsPtr, #SprBytesPerChar]  ; shift factor to
+                                                ; reach next pixel
+;        LDR     R9, [WsPtr, #SprWriteNColour]   ; mask for leftmost pixel
+
+        MOV     R11, #1
+        MOV     R9, #1
+;**************
+        Push    R0
+        MOV     R0, #&6000
+        STR     R9, [R0]
+        STR     R11, [R0, #4]
+        Pull    R0
+;**************
+
+        MOV     R10, R9, ROR R11                ; mask for rightmost pixel
+
+	Push    "R0"
+        BL      FlipAbY10               ; Now do the mask
+        Pull    "R0"
+        Pull    "R8"                    ; Retrieve last bit used to find LHwastage
+        RSB     R8, R8, #31
+        CMP     R8, #0
+        BEQ     %FT05
+        BL      RemLHWastage
+        B       %FT05
+04
+        BL      FlipAbY10               ; Now do the mask
+05
+        Pull    R14
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       FlipAbY10 - Flip area given ptrs and width
+;
+;       Internal routine, called by FlipAboutYAxis
+;
+; N.B. This reflects 'user pixels', ie in double pixel modes it
+;      reflects pairs of screen pixels, this should not matter.
+;
+; ON ENTRY, we have R0, R3-R4, R9-R11
+; ON EXIT, of these, only R0 is corrupted
+
+; Internally:
+;       R0  RowPtr, R1 LPtr , R2 RPtr , R3 Row offset, R4 EndAdr
+;       R5  LWord , R6 RWord, R7 LTemp, R8 RTemp     , R9 LPixMask
+;       R10 RPixMask, R11 ShftFactor, R14 Cnt
+
+; R0-R2, R5-R8 get corrupted
+
+FlipAbY10
+        Push    R14
+10
+        MOV     R1, R0                  ; R1 -> left end of row
+        ADD     R0, R0, R3, LSL #2      ; R0 -> past right end of row
+        MOV     R2, R0                  ; R2 := R0
+20
+        LDR     R5, [R1]
+        LDR     R6, [R2, #-4]!
+        MOV     R14, #32                ; total number of bits to process
+30                                      ; circular pixel shift of LWord & RWord
+        AND     R7, R5, R9              ; leftmost pixel (LSPart of LWord)
+        AND     R8, R6, R10             ; rightmost pixel (MSPart of RWord)
+        ORR     R5, R8, R5, LSR R11
+        ORR     R6, R7, R6, LSL R11
+        SUBS    R14, R14, R11           ; process next pixel
+        BNE     %BT30                   ; TMD 12-Jan-88 bug fixed here
+                                        ; I had changed RCM's code and put BCS
+
+        STR     R5, [R1], #4
+        STR     R6, [R2]
+        CMP     R2, R1
+        BHI     %BT20                   ; loop until pointers meet or cross
+
+        CMP     R0, R4
+        BCC     %BT10                   ; if address < end, reflect next row
+
+        Pull    R14
+        MOVS    PC, R14
+
+; *****************************************************************************
+;
+;       RemLHWastage - Dedicated routine to remove x bits of LH wastage
+;                      after a new format Sprite has been flipped
+;
+;       On Entry:    R0 = Start of data
+;                    R3 = Row Offset
+;                    R4 = End of data
+;                    R8 = No. Bits to Remove
+;       R0  RowPtr, R1 LPtr , R2 RPtr , R3 Row offset, R4 EndAdr
+
+RemLHWastage
+        Push    "R1,R2,R5-R8,R14"
+10
+;start of loop for each line
+        MOV     R1, R0                  ; R1 -> left end of row
+        ADD     R0, R0, R3, LSL #2      ; R0 -> past right end of row
+        SUB     R2, R0, #4              ; R2 -> last word in row
+                                        ;         (used for loop check)
+        LDR     R5, [R1]                ; load current word
+20
+;Start of loop for each word
+        CMP     R2, R1
+        ;If we have reached the last word then just shift and store
+        MOVEQ   R5, R5, LSR R8
+        STREQ   R5, [R1]
+        BEQ     %FT99
+        ;There are more words left so we need to shift in the LSBits from the next
+        ;word.
+        LDR     R6, [R1, #4]           ; load next word
+        MOV     R5, R5, LSR R8         ; Throw away wastage bits in current word
+        RSB     R14, R8, #32
+        MOV     R7, R6, LSL R14        ; Move LSBs from next word to current
+        ORR     R5, R5, R7
+        STR     R5, [R1],#4            ; store current word
+	MOV     R5, R6                 ; move next word to current word ready for
+	                               ; next loop iteration.
+
+        B       %BT20
+;        CMP     R2, R1
+;        BHI     %BT20                   ; loop until pointers meet or cross
+
+99
+        CMP     R0, R4
+        BCC     %BT10                   ; if address < end, reflect next row
+
+        Pull    "R1,R2,R5-R8,R14"
+        MOVS    PC, R14
+
+
+        END
diff --git a/s/vdu/vdugrafj b/s/vdu/vdugrafj
new file mode 100644
index 0000000000000000000000000000000000000000..987d96e8d2b808491001072ec380f6209c5dc0ca
--- /dev/null
+++ b/s/vdu/vdugrafj
@@ -0,0 +1,1321 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduGrafJ
+;
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Vdu driver code - Sprite stuff
+;
+; Author R C Manby
+; Date   10.11.86
+;
+
+; *****************************************************************************
+;
+;       GetSpriteUserCoords - Pick up area of screen as sprite using
+;                             given external coordinates
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite name
+;       R3 = 0 => exclude palette data
+;            1 => include palette data
+;       R4,R5 = (X,Y) EXTERNAL coordinates of one corner of box
+;       R6,R7 = (X,Y) EXTERNAL coordinates of opposite corner of box
+;
+
+GetSpriteUserCoords ROUT
+        Push    "R1-R3, R14"
+        ADD     R8, WsPtr, #GCsX
+        LDMIA   R8, {R9,R10}            ; preserve GCsX,GCsY around EIG
+
+        MOV     R0, R4
+        MOV     R1, R5
+        MOV     R2, #4                  ; indicate absolute coord
+        BL      EIG
+        MOV     R4, R0
+        MOV     R5, R1
+
+        MOV     R0, R6
+        MOV     R1, R7
+        BL      EIG
+        MOV     R6, R0
+        MOV     R7, R1
+
+        STMIA   R8, {R9,R10}            ; restore GcsX,GCsY
+        Pull    "R1-R3, R14"
+        B       GetSpr05
+
+; *****************************************************************************
+;
+;       GetSprite - Pick up area of screen bounded by OldCs and GCsI as sprite
+;
+;       External routine + GetSpr05 called by GetSpriteUserCoords
+;        (also external)
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite name
+;       R3 = 0 => exclude palette data
+;            1 => include palette data
+;       OldCsX,OldCsY = (X,Y) INTERNAL coordinates of one corner of box
+;       GCsIX, GCsIY  = (X,Y) INTERNAL coordinates of opposite corner of box
+;
+
+GetSprite ROUT
+        ADD     R4, WsPtr, #OldCsX      ; pickup area given by OldCs
+        LDMIA   R4, {R4-R7}             ; and GCsIX
+GetSpr05
+        Push    R14
+      [ {TRUE}
+        GraphicsMode R0
+        BNE     %FT70
+      |
+        LDR     R0, [WsPtr, #NPix]
+        TEQ     R0, #0
+        BEQ     %FT70                   ; quit with error if not graphics mode
+      ]
+
+        KillSpChoosePtr
+
+        SortT   R4, R6, R8              ; R4 ,R5, R6 ,R7 N.B. BotL &
+        SortT   R5, R7, R8              ; sL ,sB, sR ,sT      TopR
+
+        LDR     R8, [WsPtr, #YWindLimit]
+        SUB     R8, R8, R7              ; use inverted sT as index
+        AND     R8, R8, #7              ; into EcfPatternTable
+        STR     R8, [WsPtr, #SGetEcfIndx]
+
+        LDR     R0, [WsPtr, #ModeNo]
+        STR     R0, [WsPtr, #SGetMode]  ; needs setting up before CreateHeader
+
+        Push    R2
+        BL      SpriteCtrlBlk
+        BVC     %FT90           ; sprite already exists, so be clever
+        Pull    R2              ; restore name pointer
+
+                                ;      R1     ,R2     ,R3       ,R4,R5,R6,R7
+        BL      CreateHeader    ; In : AreaPtr,NamePtr,Palette  ,sl,sb,sr,st
+                                ; Out:                 ImageSize,lx,ty,
+        BVS     %FT80           ; Error, (no room/not a graphics mode)
+
+        BL      GetSpriteData
+
+; R1 -> sprite area, R2 -> sprite
+; now add the sprite to the sprite area
+
+        LDR     R3, [R2, #spNext]       ; total size of new sprite
+        LDMIA   R1, {R4-R7}             ; saEnd,saNumber,saFirst,saFree
+        ADD     R5, R5, #1
+        ADD     R7, R7, R3
+        STMIA   R1, {R4-R7}
+
+; have we made a new format sprite ? if so no left hand wastage is allowed.
+
+        LDR     R3, [R2, #spMode]
+        CMP     R3, #256
+
+        BLCS    RemoveLeftHandWastage
+
+        BL      SelectSprite
+        SWI     XOS_RestoreCursors
+        Pull    R14
+        BICS    PC, R14, #V_bit
+
+70
+        ADRL    R0, SpriteErr_NotGraphics
+      [ International
+        BL      TranslateError
+      ]
+75
+        STR     R0, [WsPtr, #RetnReg0]
+80                                      ; return point after an error
+        Pull    R14
+        ORRS    PC, R14, #V_bit
+
+; come here if sprite already exists
+; we want to extend or reduce existing sprite as necessary
+
+90
+        ADD     R13, R13, #4            ; throw away stacked name ptr
+        LDR     R14, [WsPtr, #VduSprite]
+        TEQ     R14, R2                 ; if same as vdu output sprite
+        ADREQL  R0, SpriteErr_SpriteIsCurrentDest
+      [ International
+        BLEQ    TranslateError
+      ]
+        BEQ     %BT75                   ; then error
+
+        ADR     R14, %FT95 + SVC_mode
+        Push    "R1, R14"
+        ADD     R8, WsPtr, #NameBuf
+        LDMIA   R8, {R9-R11}            ; load 3 words of name
+        ADD     R8, WsPtr, #SGetName
+        STMIA   R8, {R9-R11}            ; and store in SGetName
+
+        Push    "R1, R2, R3"            ; save sprite area, sprite, palflag
+        BL      PreCreateHeader
+        Pull    "R1, R2"                ; restore sprite area ptr + sprite ptr
+        BVS     %FT93
+                                        ; R4 = total size of sprite
+        Push    R4                      ; save new size of sprite
+        LDR     R0, [R2, #spNext]
+        SUBS    R3, R4, R0              ; compare required size with existing
+        MOV     R3, R3, ASR #2          ; no. of words to extend/reduce by
+        BEQ     %FT94                   ; [is exactly right already]
+        BHI     %FT92                   ; need to extend sprite
+
+; need to reduce sprite
+
+        RSB     R3, R3, #0              ; no. of words to reduce by
+        LDR     R4, [R2, #spImage]
+        SUB     R4, R0, R4              ; offset from Image to Next
+        SUB     R4, R4, R3, LSL #2      ; dest. start as offset from spImage
+        BL      RemoveWords
+        RSB     R3, R3, #0              ; put R3 back to no. of words to extend
+        B       %FT94
+
+; need to extend sprite
+
+92
+        BL      ExtendSpriteByR3
+93
+        ADDVS   R13, R13, #4*4          ; junk size, palflag,
+                                        ; sprite area, fake return address
+        BVS     %BT80                   ; no room to extend sprite
+94
+        Pull    R4                      ; restore new size of sprite
+        B       PostCreateHeader
+
+; come back to here after PostCreateHeader exits
+; R1 -> sprite area, R2 -> sprite, R3 = no. of words to extend by
+
+95
+        BL      GetSpriteData
+        BL      SelectSprite
+
+; have we made a new format sprite ? if so no left hand wastage is allowed.
+
+        LDR     R3, [R2, #spMode]
+        CMP     R3, #256
+        BLCS    RemoveLeftHandWastage
+
+        SWI     XOS_RestoreCursors
+        Pull    R14
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+
+GetSpriteData ROUT
+        Push    "R1-R3, R14"
+        SWI     XOS_RemoveCursors
+        MOV     R0, R4
+        MOV     R1, R5
+        BL      ScreenAddr
+        MOV     R0, R2                          ; screen addr of TopL of area
+        LDMIA   R13, {R4,R5}                    ; R4->sprite area, R5->sprite
+        LDR     R1, [WsPtr, #SGetImage]
+        ADD     R1, R1, R5                      ; memory address
+        LDR     R2, [WsPtr, #SGetWidth]
+        ADD     R2, R2, #1                      ; sprite width (words)
+        LDR     R3, [WsPtr, #SGetHeight]        ; height
+        ADD     R3, R3, #1
+
+        LDR     R8, [WsPtr, #SGetTopMargin]     ; gap above window (rows)
+        LDR     R9, [WsPtr, #SGetBotMargin]     ;     below        (rows)
+        LDR     R10, [WsPtr, #SGetLWrdMargin]   ;     left of      (words)
+        LDR     R11, [WsPtr, #SGetRWrdMargin]   ;     right of     (words)
+
+        SUB     R2, R2, R10
+        SUBS    R2, R2, R11             ; number words in window per scanline
+        BLEQ    PaintSprite             ; Left or Right of window
+        BEQ     %FT60
+
+        SUB     R3, R3, R8
+        SUBS    R3, R3, R9              ; number of rows in window
+        BLEQ    PaintSprite             ; above or below window
+        BEQ     %FT60
+
+        LDR     R14, [WsPtr, #LineLength] ; offset from RHend of row to LHend
+        SUB     R14, R14, R2, LSL #2    ; of next row
+        STR     R14, [WsPtr, #SGetRowOfst]
+
+        LDR     R11, [WsPtr, #SGetLBitMargin]
+        MOV     R5, #&FFFFFFFF
+        MOV     R5, R5, LSL R11         ; LmarginMask
+
+        LDR     R11, [WsPtr, #SGetRBitMargin]
+        MOV     R6, #&FFFFFFFE
+        MVN     R6, R6, LSL R11         ; RmarginMask
+
+        SUBS    R2, R2, #1
+        STR     R2, [WsPtr, #SGetColWCnt] ; if only one word in window per row
+        ANDEQ   R5, R5, R6              ; then combine L&R masks
+        MOVEQ   R6, R5
+        STR     R5, [WsPtr, #SGetLBitMargin]
+        STR     R6, [WsPtr, #SGetRBitMargin]
+
+        LDR     R5, [WsPtr, #SGetTopMargin]     ; paint TopMargin (if any)
+        CMP     R5, #0
+        BLNE    PaintBlock
+
+; R0    ,R1    ,R2     ,R3    ,R4      ..       R11
+; ScrAdr,MemAdr,ColWCnt,RowCnt,{8 words from screen},
+
+10
+        LDR     R4, [WsPtr, #SGetLWrdMargin] ; paint 1 row of LHmargin (if any)
+        CMP     R4, #0
+        BLNE    PaintRow        ; on exit R6 holds word of BgEcf, if not called
+                                ; R6 is corrupt, but it doesn't matter
+        LDR     R2, [WsPtr, #SGetColWCnt] ; on screen word count ( >0 in words)
+        LDR     R5, [WsPtr, #SGetLBitMargin]
+        LDR     R4, [R0], #4    ; get first on screen word
+        AND     R4, R4, R5
+        BIC     R6, R6, R5      ; Write BgEcf (or nonsense) to out of window
+        ORR     R4, R4, R6      ; pixels
+        STR     R4, [R1], #4
+        SUBS    R2, R2, #1
+        BLT     %FT50           ; if all plotted
+
+        SUBS    R2, R2, #8      ; try for 8 words
+20
+        LDMCSIA R0!, {R4-R11}   ; copy 8 words from screen to memory
+        STMCSIA R1!, {R4-R11}
+        SUBCSS  R2, R2, #8
+        BCS     %BT20
+30
+        ADDS    R2, R2, #8
+        LDR     R6, [WsPtr,#SGetEcfIndx]
+        ADD     R6, WsPtr, R6, LSL #2
+        LDR     R6, [R6, #BgEcf]        ; BgEcf for this scanline
+        LDR     R5, [WsPtr,#SGetRBitMargin]
+        BIC     R6, R6, R5
+40
+        LDR     R4, [R0], #4
+        ANDEQ   R4, R4, R5
+        ORREQ   R4, R4, R6
+        STR     R4, [R1], #4
+        SUBS    R2, R2, #1
+        BCS     %BT40
+50
+        LDR     R4, [WsPtr, #SGetRWrdMargin]
+        CMP     R4, #0
+        BLNE    PaintRow
+
+        LDR     R2, [WsPtr, #SGetColWCnt]
+        LDR     R4, [WsPtr, #SGetRowOfst]
+        LDR     R5, [WsPtr,#SGetEcfIndx]
+        ADD     R0, R0, R4                      ; offset ScrAdr to next row
+        ADD     R5, R5, #1
+        AND     R5, R5, #7
+        STR     R5, [WsPtr, #SGetEcfIndx]       ; update EcfIndx to next row
+        SUBS    R3, R3, #1
+        BGT     %BT10                           ; do next screen line
+
+        LDR     R5, [WsPtr, #SGetBotMargin]     ; paint bottom margin (if any)
+        CMP     R5, #0
+        BLNE    PaintBlock
+60
+        Pull    "R1-R3, PC"
+
+
+; *****************************************************************************
+;
+;       PaintSprite - Paint the whole of the sprite in background colour
+;
+;       Internal routine, called by GetSprite when all area is outside window
+;
+; in:   R1 -> first byte in sprite
+;
+
+PaintSprite ROUT
+        LDR     R5, [WsPtr, #SGetHeight]
+        ADD     R5, R5, #1              ; R5 = number of rows in sprite
+
+; and drop thru to ...
+
+; *****************************************************************************
+;
+;       PaintBlock - Paint a number of rows of the sprite in background colour
+;
+;       Internal routine, called by GetSprite to do area above and below window
+;        and dropped thru to by PaintSprite
+;
+; in:   R1 -> start of first row to paint
+;       R5 = number of rows to do
+;
+; out:  Flags preserved
+
+PaintBlock ROUT
+        Push    R14
+        LDR     R4, [WsPtr, #SGetWidth]
+        ADD     R4, R4, #1
+10
+        BL      PaintRow
+        LDR     R6, [WsPtr, #SGetEcfIndx]
+        ADD     R6, R6, #1
+        AND     R6, R6, #7
+        STR     R6, [WsPtr, #SGetEcfIndx]
+        SUBS    R5, R5, #1
+        BNE     %BT10
+
+        Pull    R14
+        MOVS    PC, R14                 ; we must preserve the flags
+
+; *****************************************************************************
+;
+;       PaintRow - Paint part of a row in sprite with background colour
+;
+;       Internal routine, called by GetSprite to do areas left+right of window
+;        and by PaintBlock
+;
+; in:   R1 -> first word to paint
+;       R4 = number of words to paint
+;
+; out:  R4 preserved
+;
+
+PaintRow ROUT
+        Push    R4
+        LDR     R6, [WsPtr, #SGetEcfIndx]
+        ADD     R6, WsPtr, R6, LSL #2
+        LDR     R6, [R6, #BgEcf]        ; BgEcf for this scanline
+10
+        STR     R6, [R1], #4
+        SUBS    R4, R4, #1
+        BNE     %BT10
+        Pull    R4
+        MOVS    PC, R14
+
+; *****************************************************************************
+;
+;       CreateSprite - Create a sprite with given attributes
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite name
+;       R3 = 0/1 => exclude/include palette data
+;       R4 = width in pixels
+;       R5 = height in pixels
+;       R6 = mode number of sprite
+;
+
+CreateSprite ROUT
+        Push    R14
+        KillSpChoosePtr
+        BL      DeleteSpriteByName      ; delete any existing sprite
+        STR     R6, [WsPtr, #SGetMode]  ; needs setting up before CreateHeader
+        SUB     R6, R4, #1              ; width in pixels-1
+        SUB     R7, R5, #1              ; height-1
+        MOV     R4, #0
+        MOV     R5, #0
+                                        ;      R3     ,R4,R5,R6,R7
+        BL      CreateHeader            ; In : Palette,sl,sb,sr,st
+                                        ; Out: ImageSize
+        Pull    PC, VS                  ; if error, then bomb out
+
+        MOV     R4, #0                  ; clear R3 words at offset 0 in sprite
+        BL      ClearWords              ; ie clear image to 0
+
+ ; Now add the sprite to the sprite area
+
+        LDR     R3, [R2, #spNext]       ; total size of new sprite
+        LDMIA   R1, {R4-R7}             ; saEnd,saNumber,saFirst,saFree
+        ADD     R5, R5, #1
+        ADD     R7, R7, R3
+        STMIA   R1, {R4-R7}
+
+        Pull    R14
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       CreateHeader - Create a header and info for a sprite
+;
+;       Internal routine, called by GetSprite, CreateSprite, ScreenSave
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite name
+;       R3 = 0/1 => exclude/include palette data
+;       R4,R5 = (X,Y) INTERNAL coordinate of bottom left
+;       R6,R7 = (X,Y) INTERNAL coordinate of top right
+;
+; out:  R1 preserved
+;       R2 -> new sprite
+;       R3 = size of image in words
+;       R4,R5 = (X,Y) INTERNAL coordinate of top left of on screen area
+;       R0, R6-R11 corrupted
+;
+
+CreateHeader ROUT
+        Push    "R1, R14"
+        Push    R3
+
+        BL      GetName                 ; name returned in R9-R11
+        ADD     R8, WsPtr, #SGetName
+        STMIA   R8, {R9-R11}            ; save the name away
+
+        BL      PreCreateHeader
+
+        Pull    "R0, R1, PC", VS
+
+; now the updating the sprite area bit
+
+        LDR     R1, [R13, #1*4]         ; reload sprite area ptr off stack
+        LDR     R2, [R1, #saFree]
+        LDR     R5, [R1, #saEnd]
+        SUB     R5, R5, R2
+        CMP     R5, R4
+        BCC     %FT10
+
+        ADD     R2, R2, R1              ; address of new sprite
+
+PostCreateHeader
+        ADD     R5, WsPtr, #SGetName
+        LDMIA   R5, {R5-R11}
+
+; R4 spNext, R5-R7 spName(0..2),
+; R8 spWidth, R9 spHeight, R10 spLBit, R11 spRBit
+
+        STMIA   R2, {R4-R11}            ; write control block for sprite
+        LDR     R11, [WsPtr, #SGetImage]
+        STR     R11, [R2, #spImage]
+        STR     R11, [R2, #spTrans]     ; spImage=spTrans ie no mask
+
+        LDR     R11, [WsPtr, #SGetMode]
+
+ [ {FALSE}      ; TMD 08-Jun-93: SGetMode sanitation done in PreCreateHeader now
+; Previously we simply copied the current SGetMode straight into spmode.
+; With the advent of mode descriptors and the new sprite mode word this
+; has to get a bit more intelligent. New logic is:
+; i) is the value > 256 (unsigned) ? Y - mode descriptor - have to make
+;    a new sprite mode word, N - proceed to next...
+; ii) is this higher than 8bpp ? Y - have to use a new sprite mode word
+;    N - use the mode number
+
+; Note: Once all the mode descriptor work is complete the second test
+; may be removed, since no 16/32bpp modes will have mode numbers
+; associated with them. However, until then it stays.
+
+        !       0,"vdugrafj: remove marked section when mode descriptors"
+        !       0,"          work, and no 16/32bpp modes have old mode numbers."
+
+        CMP     R11, #256
+        BCC     %FT20                    ;branch if under 256
+
+30      ; this mode is not available in RISC OS 3, so give it a new format
+        ; sprite mode word instead
+
+        Push    "R0-R3"
+
+        MOV     R0,R11
+        MOV     R1,#VduExt_Log2BPP
+        SWI     XOS_ReadModeVariable
+
+        MOV     R11,#1                   ;bit 0 is always set
+
+        ADD     LR, R2, #1               ;turn it into the sprite type
+        ORR     R11, R11, LR, LSL #27    ;and put it into position
+
+        MOV     R1, #VduExt_XEigFactor
+        SWI     XOS_ReadModeVariable
+
+        MOV     R4, #180
+        MOV     LR, R4, LSR R2           ;convert to a dpi (180 >> eig)
+        ORR     R11, R11, LR, LSL #1     ;and put it into position
+
+        MOV     R1, #VduExt_YEigFactor
+        SWI     XOS_ReadModeVariable
+
+        MOV     LR, R4, LSR R2           ;convert to a dpi (180 >> eig)
+        ORR     R11, R11, LR, LSL #14    ;and put it into position
+
+        Pull    "R0-R3"
+
+        B       %FT40
+
+20      ;**************************************************************************
+        ;The test and branch below should be removed once mode descriptors are done
+        ;**************************************************************************
+
+        ; ,---------------- BEGIN REMOVE SECTION ##################################
+        ; v                                                                       #
+        Push    "R0-R3"                  ;                                        #
+        MOV     R0,R11                   ;                                        #
+        MOV     R1,#VduExt_Log2BPP       ;                                        #
+        SWI     XOS_ReadModeVariable     ;                                        #
+        CMP     R2, #4                   ;                                        #
+        Pull    "R0-R3"                  ;                                        #
+        BCS     %BT30                    ;over 3 ?, put in a new sprite mode word #
+        ; ^                                                                       #
+        ; '---------------- END REMOVE SECTION ####################################
+
+        ; it is an old fashioned mode number, so use that to allow RO 3.00/3.10
+        ; to understand this sprite
+40
+ ]
+        STR     R11, [R2, #spMode]
+        MOVS    LR, R11, LSR #27         ; do we have an old or new sprite ? EQ=old
+
+        ADD     R4, WsPtr, #SGetTopLeft
+        LDMIA   R4, {R4, R5}            ; (R4,R5) = TopLeft of 'on screen' area
+        Pull    R11                     ; R11 = 0/1 for (ex/in)clude palette
+
+;amg 25th May 1994. We now allow palettes on new format sprites in 8bpp and below
+
+        CMP     LR,#SpriteType_New16bpp
+        BCS     %FT11                   ; check for new 16/32 bpp
+
+        TEQ     R11,#0                  ; was a palette wanted in the first place?
+        BLNE    WritePaletteToSprite    ; do it if so
+
+;        ;only allow palette data to be written if EQ and R11<>0
+;
+;        BNE     %FT11
+;
+;        TEQ     R11, #0
+;        BLNE    WritePaletteToSprite
+
+11
+        Pull    "R1, R14"
+        BICS    PC, R14, #V_bit
+
+10
+        ADRL    R0, SpriteErr_NoRoom
+      [ International
+        BL      TranslateError
+      ]
+        STR     R0, [WsPtr, #RetnReg0]
+        Pull    "R0, R1, R14"           ; junk palflag, sprite area ptr
+        ORRS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       SanitizeSGetMode - Convert SGetMode into a new format sprite word if necessary
+;
+;       If SGetMode is either   a) a mode selector pointer, or
+;                               b) a mode number which has more than 8bpp
+;       then SGetMode is replaced by a suitable sprite mode word
+;
+; amg: 15/10/93: changed to be more keen to generate old format mode numbers. It is
+;                now also called from createsprite, so it will pass through a new
+;                sprite mode word unchanged. Mode numbers will be unchanged. Mode
+;                selectors will be changed to a mode number if one of suitable
+;                eigs and depth exists --- size of screen is *not* taken into
+;                account here.
+
+
+; in:   WsPtr -> VDU workspace
+;
+; out:  If OK, then
+;         V=0
+;         All registers preserved
+;       else
+;         V=1
+;         r0 -> error
+;         RetnReg0 -> error
+;       endif
+;
+
+SanitizeSGetMode ENTRY "r0-r4,r11"
+        LDR     r11, [WsPtr, #SGetMode]
+
+        CMP     r11, #&100
+        BCC     %FT20                   ; [not a mode selector or new format sprite word]
+
+        TST     r11, #1                 ; is it already a new format sprite word?
+        EXIT    NE
+10
+        MOV     r0, r11                 ; r0 -> mode selector
+ [ ModeSelectors
+        BL      ValidateModeSelector
+        STRVS   r0, [sp]
+        STRVS   r0, [WsPtr, #RetnReg0]
+        EXIT    VS
+ ]
+
+15
+; convert to new format sprite word
+
+        MOV     r4, r11                 ; preserve the mode for later
+
+        MOV     r11,#1                  ; bit 0 is always set
+
+        MOV     r1, #VduExt_XEigFactor
+        SWI     XOS_ReadModeVariable
+
+        MOV     lr, #180
+        MOV     lr, lr, LSR r2          ; cope with 45, 90, 180 dpi
+
+        ORR     r11, r11, lr, LSL #1    ; put into xdpi position
+
+        MOV     r1, #VduExt_YEigFactor
+        SWI     XOS_ReadModeVariable
+
+        MOV     lr, #180
+        MOV     lr, lr, LSR r2
+        ORR     r11, r11, lr, LSL #14   ; put into ydpi position
+
+        ;amg: add check for log2bpp=log2bpc
+
+        MOV     r0, r4
+        MOV     r1, #VduExt_Log2BPC
+        SWI     XOS_ReadModeVariable
+        MOV     R3,R2
+
+        MOV     r0, r4
+        MOV     r1, #VduExt_Log2BPP
+        SWI     XOS_ReadModeVariable
+
+        CMP     R3, R2
+        BNE     %FT20
+
+        ADD     lr, r2, #1              ; turn it into the sprite type
+        ORR     r11, r11, lr, LSL #27   ; and put it into position
+
+        STR     r11, [WsPtr, #SGetMode] ; store new value
+
+        ;now check if we can force it back to a mode number
+
+        ;if the bpp is > 8 the answer is no
+        CMP     r2, #4
+        EXIT    CS
+
+        BIC     r0, r11, #&F8000000     ; take off the type information
+        ADR     r1, substitute_list
+        ADD     r2, r1, #12             ; end of list
+27
+        LDR     r3, [r1], #4
+        TEQ     r3, r0
+        BEQ     %FT28
+        TEQ     r1, r2
+        EXIT    EQ                      ; can't do anything with it
+        BNE     %BT27
+28
+        ADD     r1, r1, #8              ; point at modes word, allowing for post inc
+        ADD     r1, r1, r11, LSR #27    ; add in the sprite's type
+        SUB     r1, r1, #1              ; and reduce it by one
+        LDRB    r1, [r1]                ; fetch the right mode number
+
+        ;if we got 255, we can't save the day
+        CMP     r1,#255
+        STRNE   r1, [WsPtr, #SGetMode]  ; and store it
+
+        EXIT
+
+substitute_list
+        DCD     &001680B5               ;90 X 90 DPI, X/Y EIG 1 1
+        DCD     &000B40B5               ;90 X 45 DPI, X/Y EIG 1 2
+        DCD     &000B405B               ;45 X 45 DPI, X/Y EIG 2 2
+
+        ;amg: used to use mode 4 for 2 colour eig 2 x 2 - now doesn't because of
+        ;confusion about double pixels
+
+        DCD     &1C1B1A19               ;modes  25, 26, 27, 28 for 90 x 90
+        DCD     &0F0C0800               ;modes   0,  8, 12, 15 for 90 x 45
+        DCD     &0D0901FF               ;modes n/a,  1,  9, 13 for 45 x 45
+20
+        MOV     r0, r11                 ; check if bpp for mode is > 8
+        MOV     r1, #VduExt_Log2BPP
+        SWI     XOS_ReadModeVariable
+        CMP     r2, #4
+        BCS     %BT15                   ; if so then convert to new format sprite as well
+        EXIT
+
+; *****************************************************************************
+
+PreCreateHeader ROUT
+        Push    R14
+        BL      SanitizeSGetMode        ; convert SGetMode to new format sprite if nec.
+        Pull    PC, VS                  ; duff mode selector
+
+        ;amg 25th May 1994
+        ;We now allow palettes on new format sprites of 8bpp and below
+
+;        ;force to no palette space if a new format sprite
+;        LDR     LR, [WsPtr, #SGetMode]  ; get the mode
+;        MOVS    LR, LR, LSR #27         ; set NE if new
+;        MOVNE   R3, #0                  ; turn off palette
+
+        LDR     LR, [WsPtr, #SGetMode]   ; get the sprite mode word
+        MOV     LR, LR, LSR #27          ; isolate the sprite type
+        CMP     LR, #SpriteType_New16bpp ; check for 16/32bpp
+        MOVCS   R3, #0                   ; turn off the palette request
+
+        TEQ     R3, #0                  ; convert R3 into mask to (ex/in)clude
+        MOVNE   R3, #&FF                ; space for palette data
+
+; amg need more palette space in case it's going to be a full palette
+        ORRNE   R3, R3, #&300
+
+        Push    "R6, R7"                ; preserve R6,R7 over the call
+        ADD     R2, WsPtr, #SGetNext
+        BL      SetupSprModeData
+
+;      R6    ,R7    ,R8    ,R9   ,R10 ,R11
+; Out: RdNCol,WrNCol,BytePC,XShft,NPix,Log2BPC
+
+; amg 26th October 1993 - kill another bit of Arthur compatibility in favour
+; of full palette 8bpp sprites
+;        AND     R6, R6, #63     ; make 64 palette entries like MOS 1.2
+
+        LDR     R7,[WsPtr,#ModeFlags]
+        TST     R7, #Flag_FullPalette
+        ANDEQ   R6, R6, #63
+
+        ADD     R6, R6, #1      ; number of palette entries in this mode
+        AND     R3, R3, R6      ; if (palette not wanted) OR (256 colour mode)
+                                ; then R3=0 else R3=number of palette entries
+                                ; N.B. in 256 colour modes we end up ignoring
+                                ;      the palette
+        Pull    "R6,R7"
+        Pull    PC, VS          ; error, not a graphics mode
+
+        MOV     R3, R3, LSL #3  ; two words per palette entry
+        ADD     R3, R3, #SpriteCBsize
+        STR     R3, [WsPtr, #SGetImage] ; R0-R3 now free for use
+                                        ; R4 ,R5, R6 ,R7
+                                        ; sL ,sB, sR ,sT
+        SUB     R0, R7, R5              ; height-1
+        STR     R0, [WsPtr, #SGetHeight]
+        ADD     R0, R0, #1              ; actual height in rows
+
+        LDR     R1, [WsPtr, #GWTRow]    ; if SpriteTopRow > GWTopRow
+        SUBS    R2, R7, R1
+        MOVGT   R7, R1                  ; then clip for ScreenAddr's benefit
+        MOVLE   R2, #0
+        Least   R2, R2, R0
+        STR     R2, [WsPtr, #SGetTopMargin] ; number of blank rows at top
+
+        LDR     R1, [WsPtr, #GWBRow]
+        SUBS    R2, R1, R5
+        MOVLT   R2, #0
+        Least   R2, R2, R0
+        STR     R2, [WsPtr, #SGetBotMargin] ; number of blank rows at bottom
+
+        WordOffset R0,R4, R9,R10,R11    ; offset to sL
+        WordOffset R1,R6, R9,R10,R11    ;       to sR
+        SUB     R2, R1, R0              ; width-1
+        STR     R2, [WsPtr, #SGetWidth]
+        ADD     R2, R2, #1              ; actual width in words
+
+        BitLOffset R3,R4, R9,R10,R11    ; LBit
+        STR     R3, [WsPtr, #SGetLBit]
+        BitROffset R3,R6, R9,R10,R11    ; RBit
+        STR     R3, [WsPtr, #SGetRBit]
+
+        LDR     R8, [WsPtr, #GWLCol]
+        Greatest R4,R4,R8
+        WordOffset R3,R4, R9,R10,R11    ; offset to clipL
+        SUB     R3, R3, R0
+        Least   R3,R3,R2
+        STR     R3, [WsPtr, #SGetLWrdMargin]    ; no. of blank words at left
+        BitLOffset R3,R4, R9,R10,R11
+        STR     R3, [WsPtr, #SGetLBitMargin]    ; no. of blank words at right
+
+        LDR     R8, [WsPtr, #GWRCol]
+        Least   R6, R6, R8
+        WordOffset R3,R6, R9,R10,R11    ; offset to clipR
+        SUB     R3, R1, R3
+        Least   R3, R3, R2
+        STR     R3, [WsPtr, #SGetRWrdMargin]
+        BitROffset R3,R6, R9,R10,R11
+        STR     R3, [WsPtr, #SGetRBitMargin]
+
+        ADD     R0, WsPtr, #SGetTopLeft
+        STMIA   R0, {R4, R7}            ; store top & left of 'on screen' area
+        LDR     R0, [WsPtr, #SGetWidth]
+        LDR     R1, [WsPtr, #SGetHeight]
+        ADD     R0, R0, #1              ; width in words
+        ADD     R1, R1, #1              ; height in words
+
+        MUL     R3, R1, R0              ; image size in words
+        LDR     R4, [WsPtr, #SGetImage]
+        ADD     R4, R4, R3, LSL #2      ; total size in bytes
+
+        Pull    R14
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       Decide mask size
+;
+;       Internal routine called from CreateMask
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;       R3 =  size of image data (bytes)
+;
+; out:  R3 = size of mask data (words)
+
+DecideMaskSize ROUT
+        Push "R0-R2,R4-R5,R14"
+
+        LDR     LR, [R2, #spMode]       ; get the sprite mode
+        MOVS    LR, LR, LSR #27         ; isolate the type
+
+        MOVEQ   R3,R3,LSR #2            ; if T=0 then return the same size as
+        Pull    "R0-R2,R4-R5,R15",EQ    ; the image (but returns in words not
+                                        ; bytes)
+
+        ADRL    R5, NSM_bpptable-4
+        LDR     R4, [R5, LR, LSL #2]    ; get the log2bpp value
+
+        LDR     R5, [R2, #spWidth]      ; number of words-1 per row
+
+        LDR     LR, [R2, #spRBit]       ; fetch the last bit used
+        ADD     LR, LR, #1              ; turn into a number of bits rather than bit number
+        MOV     LR, LR, LSR R4          ; turn into a number of pixels
+
+        RSB     R4, R4, #5
+        MOV     R5, R5, LSL R4          ; number of pixels on the row for the full words
+
+        ADD     R5, R5, LR
+
+        ANDS    LR, R5, #&1F            ; is it a whole number of words
+        MOVNE   LR, #1                  ; if not start at 1 not 0
+        ADD     LR, LR, R5, LSR #5      ; add the number of whole words
+
+                                        ; now have number of words per mask row
+
+        LDR     R5, [R2, #spHeight]     ; number of rows (minus 1)
+        ADD     R5, R5, #1
+        MUL     R3, LR, R5              ; number of words for the mask
+
+        Pull "R0-R2,R4-R5,R15"
+
+; *****************************************************************************
+;
+;       CreateMask - Add mask to sprite or set existing mask to 'solid'
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;
+
+CreateMask ROUT
+        Push    R14
+        KillSpChoosePtr
+        LDR     R4, [R2, #spNext]
+        LDR     R5, [R2, #spImage]      ; NB Image=Trans if NO mask
+        LDR     R6, [R2, #spTrans]
+        SUB     R3, R4, R6
+
+        BL      DecideMaskSize          ; returns R3=size of mask (words)
+
+        TEQ     R5, R6
+        BNE     %FT10                   ; mask exists
+
+        MOV     R6, R4
+        BL      ExtendSprite
+        ADRVSL  R0, SpriteErr_NotEnoughRoom ; only error is NoRoomToInsert
+      [ International
+        BLVS    TranslateError
+      ]
+        STRVS   R0, [WsPtr, #RetnReg0]  ; correct this to 'Not enough room'
+        Pull    PC, VS
+
+        STR     R6, [R2, #spTrans]      ; new spTrans := old spNext
+10                                      ; R3 mask size (words), R6 spTrans
+        ADD     R6, R6, R2
+        MOV     R4, #&FFFFFFFF
+20
+        STR     R4, [R6], #4
+        SUBS    R3, R3, #1
+        BNE     %BT20
+
+        Pull    R14
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       RemoveMask - Remove mask from sprite
+;
+;       External routine
+;
+; in:   R1 -> sprite area
+;       R2 -> sprite
+;
+
+RemoveMask ROUT
+        Push    R14
+        KillSpChoosePtr
+        LDR     R4, [R2, #spNext]
+        LDR     R5, [R2, #spImage]      ; NB spTrans = spImage, if NO mask
+        LDR     R6, [R2, #spTrans]
+        TEQ     R5, R6
+        BEQ     %FT10                   ; no mask so ignore
+
+        SUB     R3, R4, R6
+
+        BL      DecideMaskSize          ; returns R3=size in words
+
+        SUB     R4, R6, R5
+        BL      RemoveWords
+        LDR     R5, [R2, #spImage]      ; spTrans := spImage, ie NO mask
+        STR     R5, [R2, #spTrans]
+10
+        Pull    R14
+        BICS    PC, R14, #V_bit
+
+; *****************************************************************************
+;
+;       WritePaletteToSprite - Write palette information into sprite CB
+;
+;       Internal routine, called by CreateHeader
+;
+; in:   R2 -> sprite
+;
+; out:  All registers preserved
+;
+
+WritePaletteToSprite ROUT
+        Push    "R0-R4, R14"
+        LDR     R0, [WsPtr, #SprReadNColour]    ; highest palette entry
+
+; amg 26th October 1993 - this bit of Arthur compatibility bites the
+; dust to make screensaving full palette sprites work properly
+;        AND     R0, R0, #63             ; make 63 if 255 like MOS 1.20
+        LDR     R4, [WsPtr,#ModeFlags]
+        TST     R4, #Flag_FullPalette
+        ANDEQ   R0, R0, #63
+
+        ADD     R4, R2, R0, LSL #3
+        ADD     R4, R4, #spPalette      ; ptr to last pal pos in spPalette
+10
+        MOV     R1, #16                 ; read 'normal' colour
+        SWI     XOS_ReadPalette
+        STMIA   R4, {R2,R3}
+        SUB     R4, R4, #8
+        SUBS    R0, R0, #1
+        BCS     %BT10
+        Pull    "R0-R4,PC"
+
+; *****************************************************************************
+;
+;       WritePaletteFromSprite - Write palette from information in sprite CB
+;
+;       Internal routine, called by ScreenLoad
+;
+; in:   R2 -> sprite
+;
+; out:  All registers preserved
+;
+
+WritePaletteFromSprite ROUT
+        Push    "R0-R6, R14"
+        LDR     R0, [WsPtr, #ModeNo]
+        LDR     R1, [R2, #spMode]
+
+        [ {TRUE} :LAND: ModeSelectors
+
+        ;logic for this routine
+        ;
+        ;[WsPtr, #ModeNo] is the current mode/ptr to mode selector
+        ;R2 points at sprite data
+        ;[WsPtr, #SloadModeSel] is 36 bytes for building a mode selector for the sprite
+        ;
+        ;sprite mode < 256 ?
+        ;yes: equal to current mode ?
+        ;     yes: already in correct mode. done.
+        ;     no:  change to mode sprite wants, done.
+        ;no:  build a mode selector for the sprite
+        ;     check pixel depth, xres, yres, xdpi and ydpi
+        ;     all identical ?
+        ;     yes: already in suitable mode. done.
+        ;     no:  change mode. done.
+        ;if we do a mode change, remember to re-remove cursors
+
+        ;amg 15 Oct '93 Screensave is about to be changed to use a representative
+        ;mode number of the eigs and depth (only), so screenload no longer believes
+        ;the screen mode number in the file.
+
+        ;amg 21 Dec '93 Slight modification - if the screen mode change failed, and
+        ;we have an old screen mode number, use that as a last gasp
+
+;        CMP     R1, #256
+;        BCS     %FT30                   ;branch if a new format sprite mode word
+
+;        CMP     R1, R0                  ;are we in the right (old style) mode ?
+;        BEQ     %FT10
+
+;        MOV     R0,#ScreenModeReason_SelectMode
+;        SWI     XOS_ScreenMode
+;        STRVS   R0, [WsPtr, #RetnReg0]  ;exit on error
+;        Pull    "R0-R6,PC", VS
+;        B       %FT40                   ;otherwise get on with it
+
+30      ; new format sprite mode word
+        ; build the mode selector at SLoadModeSel
+
+        MOV     R5, R1                  ;keep the mode number/sprite mode word safe
+
+        ;do the absolutes first
+        MOV     R3, #-1
+        STR     R3, [WsPtr, #SloadModeSel+ModeSelector_FrameRate]
+        STR     R3, [WsPtr, #SloadModeSel+ModeSelector_ModeVars+16] ;list terminator after two pairs
+        STR     R3, [WsPtr, #SloadModeSel+ModeSelector_ModeVars+32] ;list terminator after four pairs
+        MOV     R3, #128
+        STR     R3, [WsPtr, #SloadModeSel+ModeSelector_ModeVars+20]  ;modeflags value, if needed
+        MOV     R3, #VduExt_NColour
+        STR     R3, [WsPtr, #SloadModeSel+ModeSelector_ModeVars+24]
+        MOV     R3, #255
+        STR     R3, [WsPtr, #SloadModeSel+ModeSelector_ModeVars+28]
+
+
+        MOV     R3, #1
+        STR     R3, [WsPtr, #SloadModeSel+ModeSelector_Flags]
+        MOV     R3, #VduExt_XEigFactor
+        STR     R3, [WsPtr, #SloadModeSel+ModeSelector_ModeVars]    ; modevar 1 = xeig
+        MOV     R3, #VduExt_YEigFactor
+        STR     R3, [WsPtr, #SloadModeSel+ModeSelector_ModeVars+8]  ; modevar 2 = Yeig
+
+        ;now the things from the sprite
+;        MOV     R3, R1, LSR #27         ;sprite type
+;        ADRL    R4, NSM_bpptable-4      ;readmodevar's table
+;        LDR     R3, [R4, R3, LSL #2]    ;word index
+;        STR     R3, [WsPtr, #SloadModeSel+ModeSelector_PixelDepth]
+
+        ;change to calling read mode variable to cope with mode number or sprite mode word
+        MOV     R4, R2                  ;save the sprite pointer
+        MOV     R0, R5                  ;sprite mode word/mode number
+        MOV     R1, #VduExt_Log2BPP
+        SWI     XOS_ReadModeVariable
+        STR     R2, [WsPtr, #SloadModeSel+ModeSelector_PixelDepth]
+        MOV     R3, R2
+
+        ;if log2bpp=3, and size of palette data indicates full palette, we need to force
+        ;a suitable mode
+        CMP     R3, #3
+        BNE     %FT40
+
+        ADD     LR, R4, #spImage        ;point to image/mask start
+        LDMIA   LR,{R2,LR}              ;fetch them
+        CMP     R2,LR                   ;which is bigger ?
+        MOVGT   R2,LR                   ;use the least
+        SUB     R2,R2,#spPalette        ;and the palette size is...
+
+        CMP     R2,#&800                ;full entry 256 colour
+
+        ;change the mode selector so it includes a modeflags word
+        ;(following two words already set up)
+
+        MOVEQ   R2, #0
+        ;amg 28/4/94 bugfix - following inst wasn't conditional
+        STREQ   R2, [WsPtr, #SloadModeSel+ModeSelector_ModeVars+16]
+
+40
+        MOV     R2, R4                  ;restore the sprite pointer
+
+        LDR     R4, [R2, #spWidth]      ;number of words
+        MOV     R4, R4, LSL #5          ;convert to a number of bits
+        LDR     LR, [R2, #spRBit]       ;last bit used
+        ADD     LR, LR, #1              ;convert to number of bits
+        ADD     R4, R4, LR              ;combine
+        MOV     R4, R4, LSR R3          ;and convert to pixels
+        STR     R4, [WsPtr, #SloadModeSel+ModeSelector_XRes]
+
+        LDR     R3, [R2, #spHeight]
+        ADD     R3, R3, #1
+        STR     R3, [WsPtr, #SloadModeSel+ModeSelector_YRes]
+        MOV     R6, R2                  ;save the sprite pointer for later
+
+        ;that leaves the x and y eig factors, which are derived
+        ;from the dpi
+
+        MOV     R0, R5                  ;R0 = sprite mode word
+        MOV     R1, #VduExt_XEigFactor
+        SWI     XOS_ReadModeVariable
+        STRCC   R2, [WsPtr, #SloadModeSel+ModeSelector_ModeVars+4]
+        MOVCC   R1, #VduExt_YEigFactor
+        SWICC   XOS_ReadModeVariable
+        STRCC   R2, [WsPtr, #SloadModeSel+ModeSelector_ModeVars+12]
+        BCS     %FT90                   ;we canna take it captain....
+
+        ;do the comparison which involve the mode selectors first
+
+        ;depth
+        LDR     LR, [WsPtr, #ModeNo]
+
+        LDR     R3, [LR, #ModeSelector_PixelDepth]
+        LDR     R4, [WsPtr, #SloadModeSel+ModeSelector_PixelDepth]
+        TEQ     R3, R4
+        BNE     %FT80                   ;need to change mode to new mode selr
+
+        LDR     R3, [LR, #ModeSelector_XRes]
+        LDR     R4, [WsPtr, #SloadModeSel+ModeSelector_XRes]
+        TEQ     R3, R4
+        BNE     %FT80                   ;need to change mode to new mode selr
+
+        LDR     R3, [LR, #ModeSelector_YRes]
+        LDR     R4, [WsPtr, #SloadModeSel+ModeSelector_YRes]
+        TEQ     R3, R4
+        BNE     %FT80                   ;need to change mode to new mode selr
+
+        ;now the eigs
+        LDR     R3, [WsPtr, #XEigFactor]
+        LDR     R4, [WsPtr, #SloadModeSel+ModeSelector_Flags+4]
+        TEQ     R3, R4
+        BNE     %FT80                   ;need to change mode to new mode selr
+
+        LDR     R3, [WsPtr, #YEigFactor]
+        LDR     R4, [WsPtr, #SloadModeSel+ModeSelector_Flags+12]
+        TEQ     R3, R4
+
+        BEQ     %FT10                   ;this mode is suitable
+
+80
+        MOV     R0,#ScreenModeReason_SelectMode
+        ADD     R1,WsPtr,#SloadModeSel
+        SWI     XOS_ScreenMode
+
+
+        [ {TRUE}
+        ;ensure we preserve the error pointer in situations where we can't try to
+        ;fall back to the mode number in the sprite header
+
+        ;if it errored try again if there's a mode number available
+        BVC     %FT40
+
+        LDR     R1, [R6, #spMode]
+        BICS    R14, R1, #&FF           ; EQ if sprite mode is a number (< 256), (V still set afterwards)
+        MOVEQ   R0, #ScreenModeReason_SelectMode
+        SWIEQ   XOS_ScreenMode          ; if called, will set V appropriately
+        |
+        ;if it errored try again if there's a mode number available
+        BVC     %FT40
+
+        MOV     R0, #ScreenModeReason_SelectMode
+        LDR     R1, [R6, #spMode]
+        BICS    R14, R1, #&FF           ; EQ if sprite mode is a number (< 256), (V still set afterwards)
+        SWIEQ   XOS_ScreenMode          ; if called, will set V appropriately
+        ]
+
+        STRVS   R0, [WsPtr, #RetnReg0]  ;exit on error
+        Pull    "R0-R6,PC", VS
+        B       %FT40                   ;otherwise get on with it
+90
+        ADRL    R0,SpriteErr_InvalidSpriteMode
+        [ International
+        BL      TranslateError
+        ]
+        STR     R0, [WsPtr, #RetnReg0]
+        SETV
+        Pull    "R0-R6,PC"
+        |
+
+        ;as originally done this code tended to compare mode specifiers against new
+        ;sprite mode words, and worse still tried to select a mode from a new sprite
+        ;mode word. the rewrite above takes a more logical approach
+
+        CMP     R0, R1                  ; if already in correct mode
+        BEQ     %FT10                   ; then skip
+
+ [ ModeSelectors
+        MOV     r0, #ScreenModeReason_SelectMode
+        SWI     XOS_ScreenMode
+ |
+        MOV     R0, #22
+        SWI     XOS_WriteC
+        MOVVC   R0, R1
+        SWIVC   XOS_WriteC
+ ]
+        STRVS   R0, [WsPtr, #RetnReg0]
+        Pull    "R0-R6,PC", VS
+        ]
+40
+        SWI     XOS_RemoveCursors       ; remove cursors again
+10
+        MOV     R2, R6
+        LDR     R3, [R2, #spImage]
+        CMP     R3, #spPalette          ; will clear V if EQ
+        Pull    "R0-R6, PC", EQ         ; no palette data
+
+        LDR     R4, [WsPtr, #NColour]
+
+        ADD     R3, R2, #spPalette
+        ADD     R3, R3, R4, LSL #3
+20
+        LDMIA   R3, {R1,R2}
+        MOV     R0, R4
+
+        BL      SendPalettePair
+        Pull    "R0-R6, PC", VS
+
+        SUB     R3, R3, #8
+        SUBS    R4, R4, #1              ; (V will be cleared by this)
+        BCS     %BT20
+        Pull    "R0-R6, PC"
+
+; *****************************************************************************
+;
+;       SendPalettePair - Program palette with flash pair
+;
+;       Internal routine, called by WritePaletteFromSprite
+;
+; in:   R0 = logical colour
+;       R1 = first flash colour
+;       R2 = second flash colour
+;
+; out:  R1 corrupted
+;
+
+SendPalettePair ROUT
+        Push    "R0-R3, R14"
+        TEQ     R1, R2                  ; are colours the same ?
+        BNE     %FT10                   ; if not then do in two halves
+
+        MOV     R3, #16
+        BL      SendPaletteEntry        ; then send with 16
+        Pull    "R0-R3, PC"
+10
+        MOV     R3, #17                 ; else send 1st flash with 17
+        BL      SendPaletteEntry
+        MOVVC   R1, R2
+        MOVVC   R3, #18                 ; then 2nd flash with 18
+        BLVC    SendPaletteEntry
+        Pull    "R0-R3, PC"
+
+; *****************************************************************************
+;
+;       SendPaletteEntry - Program one palette entry
+;
+;       Internal routine, called by SendPalettePair
+;
+; in:   R0 = logical colour
+;       R1 = physical colour BGRx
+;       R3 = PP field to use
+;
+; out:  All registers preserved
+;
+
+SendPaletteEntry ROUT
+        Push    "R0,R1, R14"
+        BIC     R1, R1, #&7F            ; clear all bits except sup. bit
+        ORR     R1, R1, R3              ; or in new bits
+        MOV     R0, R0, LSL #24         ; move log. col. up to top 8 bits
+        Push    "R0, R1"                ; create an OSWORD block at R13+3
+
+        MOV     R0, #12
+        ADD     R1, R13, #3             ; R1 -> block
+        SWI     XOS_Word
+        STRVS   R0, [WsPtr, #RetnReg0]
+        ADD     R13, R13, #8
+        Pull    "R0,R1, PC"
+
+        END
diff --git a/s/vdu/vdugrafk b/s/vdu/vdugrafk
new file mode 100644
index 0000000000000000000000000000000000000000..e6079e3269e85a2c9fdb8085ebb3e5a789105969
--- /dev/null
+++ b/s/vdu/vdugrafk
@@ -0,0 +1,449 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduGrafK
+;
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Vdu driver code - Sprite stuff
+;
+; Author R C Manby
+; Date   5.3.87
+;
+
+; *****************************************************************************
+;
+;       ScreenSave - Save screen within graphics window as a sprite file
+;
+;       External routine
+;
+; in:   R2 -> file name
+;       R3 = 0/1 => exclude/include palette data
+;
+
+ScreenSave ROUT
+        Push    R14
+        SWI     XOS_RemoveCursors
+
+      [ {TRUE}
+        GraphicsMode R0
+        BNE     %FT20
+      |
+        LDR     R0, [WsPtr, #NPix]
+        CMP     R0, #0
+        BEQ     %FT20                   ; quit with error if not graphics mode
+      ]
+
+                                        ; build a temporary sprite area header
+        ADD     R1, WsPtr, #ScrSavAreaCB
+        MOV     R4, #&7FFFFFFF                  ; size, very large
+        MOV     R5, #1                          ; one sprite
+        LDR     R6, =(ScrSavSpriteCB-ScrSavAreaCB) ;saFirst
+        MOV     R7, R6                          ; saFree=saFirst
+        STMIA   R1, {R4-R7}
+
+        ADD     R11, WsPtr, #GWLCol             ; R4 ,R5 ,R6 ,R7
+        LDMIA   R11, {R4,R5,R6,R7}              ; wL ,wB ,wR ,wT
+
+        LDR     R0, [WsPtr, #ModeNo]
+        STR     R0, [WsPtr, #SGetMode]  ; needs setting up before CreateHeader
+
+        Push    R2                      ; preserve file name pointer
+
+        ; if it is a mode selector mode with >256 colours force it to have no
+        ; palette. CreateHeader will deal with <=256.
+        LDR     R2, [WsPtr, #Log2BPP]
+        CMP     R2, #4
+        MOVCS   R3, #0
+
+        ; amg 25th May 1994. The above is no longer completely true. We still
+        ; don't allow palettes on 16/32bpp. Below that (whether new or old
+        ; format of sprite) palettes are now allowed. No actual code change
+        ; here, but CreateHeader has been changed.
+
+        ADR     R2, ScrSavSpriteName
+                                        ;      R3     ,R4,R5,R6,R7
+        BL      CreateHeader            ; In : Palette,sl,sb,sr,st
+        ADDVS   sp, sp, #4              ; (if error, junk stacked filename ptr and exit)
+        BVS     %FT35
+                                        ; Out: ImageSize
+                                        ; now add the sprite to the sprite area
+        LDR     R3, [R2, #spNext]       ; total size of new sprite
+        LDMIA   R1, {R4,R5,R6,R7}       ; saEnd,saNumber,saFirst,saFree
+        ADD     R7, R7, R3              ; new saFree
+        MOV     R4, R7
+        STMIA   R1, {R4,R5,R6,R7}
+
+; Create file to prevent "Can't extend"
+
+        [ AssemblingArthur :LOR: Module
+        MOV     R0, #OSFile_CreateStamp
+        LDR     R1, [R13, #0]           ; R1 -> filename
+        LDR     R2, =&FF9               ; Type=SpriteFile
+        MOV     R3, #0                  ; Junk
+        MOV     R4, #0
+        SUB     R5, R7, #4              ; File size (ie exclude saEnd)
+        SWI     XOS_File
+        |
+        CLRV
+        ]
+        Pull    R1
+        BVS     %FT30
+
+; OpenUp file and save the Sprite area and sprite headers
+
+        [ AssemblingArthur :LOR: Module
+        MOV     R0, #open_update :OR: open_mustopen :OR: open_nodir
+        |
+        MOV     R0, #open_write
+        ]
+        SWI     XOS_Find
+        BVS     %FT30
+
+        MOV     R1, R0
+        MOV     R0, #2                  ; write bytes to file
+
+        LDR     R2, =(ScrSavAreaCB+saNumber)
+        ADD     R2, R2, WsPtr
+        LDR     R3, =(ScrSavSpriteCB+spImage)
+        LDR     R3, [WsPtr, R3]
+        ADD     R3, R3, #(SpriteAreaCBsize-saNumber)
+
+        MOV     R4, #0
+        SWI     XOS_GBPB
+        BVS     %FT40                   ; FileSwitchGotYa !
+
+        Push    R1
+        LDR     R0, [WsPtr, #GWLCol]
+        LDR     R1, [WsPtr, #GWTRow]
+        BL      ScreenAddr              ; R2 = ScrAdr of TopL of area
+        Pull    R1
+
+        LDR     R5, [WsPtr, #SGetWidth]
+        ADD     R5, R5, #1
+        MOV     R5, R5, LSL #2           ; width (bytes)
+        LDR     R6, [WsPtr, #SGetHeight] ; height-1
+
+        LDR     R7, [WsPtr, #LineLength]
+        SUBS    R7, R7, R5              ; zero then can do as one lump
+        MLAEQ   R5, R6, R5, R5          ; R5 = R5*(R6+1)
+        MOVEQ   R6, #0                  ; only one chunk to do
+
+;
+; R0    ,R1    ,R2    ,R3    ,R4    ,R5   ,R6    ,R7
+;       ,Handle,ScrAdr,Size  ,      ,Width,RowCnt,RowOfSt
+
+10
+        MOV     R0, #2
+        MOV     R3, R5
+        SWI     XOS_GBPB
+        BVS     %FT40                   ; something went wrong
+
+        ADD     R2, R2, R7              ; step to next screen line
+        SUBS    R6, R6, #1
+        BGE     %BT10
+
+        MOV     R0, #0                  ; close file
+        SWI     XOS_Find
+        BVS     %FT30
+        SWI     XOS_RestoreCursors
+        Pull    R14
+        BICS    PC, R14, #V_bit         ; no error, return VC
+
+20
+        ADRL    R0, SpriteErr_NotGraphics
+      [ International
+        BL      TranslateError
+      |
+        SETV
+      ]
+        B       %FT30
+
+40                                      ; return point after an error
+        STR     R0, [WsPtr, #RetnReg0]  ; R0 ptr to message, R1 file handle
+        MOV     R0, #0                  ; close file
+        SWI     XOS_Find
+30                                      ; return point after an error
+        STRVS   R0, [WsPtr, #RetnReg0]  ; R0 ptr to message
+35
+        SWI     XOS_RestoreCursors
+        Pull    R14
+        ORRS    PC, R14, #V_bit
+
+ScrSavSpriteName
+        = "screendump", 0
+        ALIGN
+
+        LTORG
+
+; *****************************************************************************
+;
+;       ScreenLoad - Plot sprite file directly into graphics window
+;
+;       External routine
+;
+; in:   R2 -> file name
+;
+
+ScreenLoad ROUT
+        Push    R14
+        SWI     XOS_RemoveCursors
+
+        MOV     R0, #open_read+open_mustopen+open_nodir
+        MOV     R1, R2
+        SWI     XOS_Find
+        BVS     %FT80
+
+        STR     R0, [WsPtr, #ScrLoaHandle]
+        MOV     R1, R0
+        MOV     R0, #4                          ; read areaCB from file
+        ADD     R2, WsPtr, #ScrLoaAreaCB+saNumber
+        MOV     R3, #(SpriteAreaCBsize-saNumber)
+        MOV     R4, #0
+        SWI     XOS_GBPB
+        BVS     %FT70                           ; FileSwitchGotYa !
+
+        MOV     R0, #3                          ; read spriteCB from file
+        ADD     R2, WsPtr, #ScrLoaSpriteCB
+        MOV     R3, #SpriteCBsize
+        ADD     R3, R3, #MaxSpritePaletteSize
+        LDR     R4, [WsPtr, #(ScrLoaAreaCB+saFirst)]
+        SUB     R4, R4, #4
+        SWI     XOS_GBPB
+        BVS     %FT70
+
+        ADD     R2, WsPtr, #ScrLoaSpriteCB
+        BL      WritePaletteFromSprite          ; mode change (if needed)
+        ;branch to 75 rather than 70 because RetnRegR0 is already set up
+        BVS     %FT75                           ; and palette setting
+
+      [ {TRUE}
+        GraphicsMode R0
+        BNE     %FT60
+      |
+        LDR     R0, [WsPtr, #NPix]
+        CMP     R0, #0
+        BEQ     %FT60                   ; quit with error if not graphics mode
+      ]
+
+; now check for being able to do it all at once
+
+        ADD     R0, WsPtr, #GWLCol              ; R3=GWLCol; R4=GWBRow
+        LDMIA   R0, {R3-R6}                     ; R5=GWRCol; R6=GWTRow
+
+        ADD     R0, WsPtr, #ScrLoaSpriteCB
+        ADD     R0, R0, #spWidth                ; R7=width-1; R8=height-1
+        LDMIA   R0, {R7-R10}                    ; R9=LBit; R10=RBit
+
+        SUB     R5, R5, R3                      ; R5 = window width
+        LDR     R0, [WsPtr, #XWindLimit]
+        TEQ     R0, R5                          ; if window width=full screen
+        TEQEQ   R9, #0                          ; and LBit=0
+        TEQEQ   R10, #31                        ; and RBit=31
+        BNE     %FT05
+
+        ADD     R5, R5, #1                      ; R5 = screen width in pixels
+        ADD     R7, R7, #1                      ; R7 = sprite width in words
+        LDR     R0, [WsPtr, #NPix]
+        MLA     R0, R7, R0, R7                  ; R0 = width*(npix+1)
+        TEQ     R0, R5                          ; and spritewidth=full screen
+        BNE     %FT05
+
+; we know we can do it all in one chunk
+; work out screen address and sprite offset
+
+        LDR     R1, [WsPtr, #CursorFlags]       ; if computing clip box
+        TST     R1, #ClipBoxEnableBit
+        Push    "R0-R4", NE
+        BLNE    SetClipBoxToFullScreen          ; then set to full screen
+        Pull    "R0-R4", NE                     ; above routine preserves PSR
+
+        MOV     R7, R7, LSL #2                  ; R7 = line width in bytes
+        ADD     R1, R8, R4                      ; R1 = height-1 + GWBRow = YT
+        SUBS    R9, R1, R6                      ; if YT > GWTRow then
+                                                ; start at row (YT-GWTRow)
+        MOVHI   R1, R6                          ; and YT=GWTRow, else
+        MOVLS   R9, #0                          ; start at row 0 (and YT=YT)
+
+        LDR     R0, [WsPtr, #ScrLoaAreaCB+saFirst]
+        ADD     R2, WsPtr, #ScrLoaSpriteCB
+        LDR     R2, [R2, #spImage]
+        ADD     R0, R0, R2
+        SUB     R0, R0, #4                      ; R0=offset into file of image
+        MLA     R4, R7, R9, R0                  ; add on vertical wastage*width
+
+        SUB     R9, R8, R9                      ; R9 = height-1-wastage
+        MLA     R9, R7, R9, R7                  ; number of bytes to transfer
+
+        MOV     R0, #0
+        BL      ScreenAddr                      ; R2 := screen address
+        MOV     R3, R9
+
+        LDR     R1, [WsPtr, #ScrLoaHandle]
+        MOV     R0, #3                          ; read from this position
+        SWI     XOS_GBPB
+        BVS     %FT70                           ; if error
+        B       %FT52                           ; no error
+
+; can't do it all at once; R3 = GWLCol, R4 = GWBRow
+
+05
+        ADD     R2, WsPtr, #ScrLoaSpriteCB      ; point at the spriteCB
+        MOV     R5, #0
+        BL      GenSpritePlotParmBlk            ; "plotting" at (GWLCol,GWBRow)
+        BVS     %FT55                           ; off screen (not an error)
+
+        ADD     R2, WsPtr, #ScrLoaSpriteCB
+        LDR     R4, [WsPtr, #SPltMemAdr]        ; convert MemAdr into
+        LDR     R5, [WsPtr, #ScrLoaAreaCB+saFirst]
+        SUB     R4, R4, #4
+        SUB     R4, R4, R2
+        ADD     R4, R4, R5
+        STR     R4, [WsPtr, #ScrLoaFilPtr]      ; file ptr
+
+        LDR     R4, [R2, #spWidth]              ; convert spWidth into
+        ADD     R4, R4, #1
+        MOV     R4, R4, LSL #2
+        STR     R4, [WsPtr, #ScrLoaFilOfst]     ; file ptr offset
+
+        LDR     R4, [WsPtr, #SPltColCnt]
+        ADD     R4, R4, #2
+        MOV     R4, R4, LSL #2
+        STR     R4, [WsPtr, #ScrLoaBytes]
+
+        ADD     R4, WsPtr, #ScrLoaBuffer
+        STR     R4, [WsPtr, #SPltMemAdr]
+        STR     R4, [WsPtr, #ScrLoaBufAdr]
+
+10                                              ; read row from file
+        ADD     R1, WsPtr, #ScrLoaHandle
+        LDMIA   R1, {R1,R2,R3,R4}               ; Handle,BufAdr,Bytes,FilPtr
+        MOV     R0, #3
+        SWI     XOS_GBPB
+        BVS     %FT70
+
+        ADD     R0, WsPtr, #SPltScrAdr
+        LDMIA   R0, {R0-R1,R5-R7}       ; R0    ,R1    ,     R5    ,R6   ,R7
+                                        ; ScrAdr,ColCnt,     BufAdr,ShftR,ShftL
+
+        LDMIA   R5, {R2,R3}             ; plot the first (leftmost) word
+        ADD     R5, R5, #4
+
+        ShiftR  R2,R3, R6,R7            ; shift R3,R2 right R6 places
+                                        ; we only need result word R2
+
+        LDR     R3, [WsPtr, #SPltLMask] ; on leftmost word, mask down
+        AND     R2, R2, R3              ; to just the required pixels
+
+        LDR     R4, [R0]                ; plot 1 word
+        BIC     R4, R4, R3
+        ORR     R4, R4, R2
+        STR     R4, [R0], #4
+
+        SUBS    R1, R1, #1
+        BLT     %FT50                   ; if all plotted, try next scanline
+                                        ; else try for blocks of 4
+        SUBS    R1, R1, #4
+        BLT     %FT30
+
+20
+        ;R0    ,R1         ,R5    ,R6   ,R7
+        ;ScrAdr,ColCnt,    ,BufAdr,ShftR,ShftL
+
+        LDMIA   R5, {R2,R3,R8-R10}      ; 5 words needed, gives 4 after shift
+        ADD     R5, R5, #16             ; advance source ptr 4 words
+
+        ShiftR  R2,R3, R6,R7            ; shift R4-R0 right R6 bits
+        ShiftR  R3,R8, R6,R7            ; we only want result words R3-R0
+        ShiftR  R8,R9, R6,R7
+        ShiftR  R9,R10,R6,R7
+
+        STMIA   R0!, {R2,R3,R8-R9}      ; write 4 words back to screen
+        SUBS    R1, R1, #4
+        BGE     %BT20
+
+30                                      ; try 1 word at a time
+        ADDS    R1, R1, #4
+
+;R0    ,R1    ,     R5    ,R6   ,R7
+;ScrAdr,ColCnt,     BufAdr,ShftR,ShftL
+;
+; If EQ this is rightmost word
+
+        BEQ     %FT45
+40
+        LDMIA   R5, {R2,R3}
+        ADD     R5, R5, #4
+
+        ShiftR  R2,R3, R6,R7            ; shift R3,R2 right R6 places
+                                        ; we only need result word R2
+        STR     R2, [R0], #4
+        SUBS    R1, R1, #1
+        BGT     %BT40
+
+45
+        LDMIA   R5, {R2,R3}
+        ADD     R5, R5, #4
+
+        ShiftR  R2,R3, R6,R7            ; shift R3,R2 right R6 places
+                                        ; we only need result word R2
+
+        LDR     R3, [WsPtr, #SPltRMask] ; rightmost word, so mask down to
+        AND     R2, R2, R3              ; just the required pixels
+
+        LDR     R4, [R0]
+        BIC     R4, R4, R3
+        ORR     R4, R4, R2
+        STR     R4, [R0], #4
+
+50                                      ; now try the next scanline
+        ADD     R11, WsPtr, #SPltWidth
+        LDMIA   R11, {R1,R2,R3,R4}      ; Width,Height,ScrOff,MemOff
+        ADD     R5, R0, R3
+        SUBS    R2, R2, #1
+        STMIA   R11, {R1,R2,R3,R4,R5}   ; Width,Height,ScrOff,MemOff,ScrAdr
+
+        ADD     R11, WsPtr, #ScrLoaHandle ; R1    ,R2    ,R3   ,R4    ,R5
+        LDMIA   R11, {R1,R2,R3,R4,R5}     ; Handle,BufAdr,Bytes,FilPtr,FilOfst
+        ADD     R4, R4, R5
+        STR     R4, [WsPtr, #ScrLoaFilPtr]
+        BGE     %BT10                   ; plot next scanline
+52
+        MOV     R0, #0                  ; close file
+        SWI     XOS_Find
+        BVS     %FT80
+55
+        SWI     XOS_RestoreCursors
+        Pull    R14
+        BICS    PC, R14, #V_bit
+
+60
+        ADRL    R0,SpriteErr_NotGraphics
+      [ International
+        BL      TranslateError
+      ]
+70                                      ; return point after an error
+        STR     R0, [WsPtr, #RetnReg0]  ; R0 ptr to message, R1 file handle
+75
+        MOV     R0, #0                  ; close file
+        SWI     XOS_Find
+80                                      ; error, file not open
+        STRVS   R0, [WsPtr, #RetnReg0]
+        SWI     XOS_RestoreCursors
+        Pull    R14
+        ORRS    PC, R14, #V_bit
+
+
+        END
diff --git a/s/vdu/vdugrafl b/s/vdu/vdugrafl
new file mode 100644
index 0000000000000000000000000000000000000000..8be3f53acc503f32821dbe236a593274593acf72
--- /dev/null
+++ b/s/vdu/vdugrafl
@@ -0,0 +1,675 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduGrafL
+;
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Vdu driver code - Sprite stuff
+;
+; Author T M Dobson
+; Date   22-Sep-87
+;
+
+; *****************************************************************************
+;
+;       ReadSaveAreaSize - Read size of a VDU context save area
+;
+; in:   R0 = SpriteReason_ReadSaveAreaSize
+;       R1 -> sprite area
+;       R2 -> sprite (0 => screen (possibly))
+;
+; out:  R3 = size of an area suitable for this sprite
+;
+
+ReadSaveAreaSize ROUT
+        MOV     R3, #MainSize           ; I was kidding about it depending
+        STR     R3, [WsPtr, #RetnReg3]  ; on the sprite!
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       SwitchOutputToSprite - Make VDU(graphics) output go to a sprite
+;
+;       External routine
+;
+; in:   R0 = SpriteReason_SwitchOutputTo <Sprite or Mask>
+;       R1 -> sprite area
+;       R2 -> sprite (0 => screen)
+;       R3 -> save area for VDU variables relating to sprite
+;        0 => no save area, initialise variables from mode number of sprite
+;             or display mode number
+;        1 => use MOS's save area
+;
+;       First word of save area = 0      => uninitialised save area
+;                                 "VOTS" => initialised save area
+;                                 else error
+;
+
+; From Medusa onwards, things are further complicated here by the fact that
+; the sprite may have a new sprite mode word rather than a mode number. When
+; the former case occurs calling pushmodeinfoanymonitor doesn't really help
+; very much. Instead, when a sprite mode word is given, it will derive or
+; fudge all the variables that it is really interested from the sprite mode
+; word directly.
+
+SwitchOutputToSprite ROUT
+SwitchOutputToMask ROUT
+        CMP     R3, #1                  ; check for values 0 and 1
+        MOVCC   R4, #0                  ; R3=0 => no save area, so not inited
+
+        ADDEQ   R5, WsPtr, #VduSaveArea ; R3=1 => MOS's area
+        MOVHI   R5, R3                  ; else user area
+        LDRCS   R4, [R5, #InitFlag]     ; if is an area, load R4 with init flag
+        LDR     R5, VOTS                ; compare with initialised identifier
+        TEQ     R4, R5                  ; if not initialised
+        TEQNE   R4, #0                  ; and not uninitialised
+        BNE     InvalidSaveArea
+
+; no more errors can be generated, so update returned registers
+
+        ADD     R5, WsPtr, #VduOutputCurrentState
+        LDMIA   R5, {R6-R9}
+        ADD     R5, WsPtr, #RetnReg0
+        STMIA   R5, {R6-R9}
+
+        Push    "R0-R4,R14"
+
+        ASSERT  SpriteMaskSelect = VduOutputCurrentState +0
+        ASSERT  VduSpriteArea    = VduOutputCurrentState + 4
+        ASSERT  VduSprite        = VduOutputCurrentState + 8
+
+        ORR     R0, R0, #RangeC         ; make R0 into &2nn
+
+        ADD     R5, WsPtr, #VduOutputCurrentState
+        STMIA   R5, {R0-R2}
+
+        BL      PreWrchCursor           ; remove cursor
+        BL      PackVars                ; save away current vars into save area
+                                        ; (if any)
+        Pull    "R0-R4"
+        STR     R3, [WsPtr, #VduSaveAreaPtr] ; save new save area ptr
+
+        TEQ     R2, #0                  ; if switching to screen
+        LDREQ   R9, [WsPtr, #DisplayScreenStart] ; then load up new ScreenStart
+        LDREQ   R11, [WsPtr, #DisplayModeNo] ; and mode number
+        BEQ     %FT20                   ; and skip sprite stuff
+
+; do stuff for switching to sprite
+
+        Push    R0
+        BL      RemoveLeftHandWastage   ; then remove LH wastage from sprite
+        Pull    R0                      ; (spLBit := 0)
+
+        ASSERT  spHeight = spWidth +4
+        ASSERT  spLBit   = spWidth +8
+        ASSERT  spRBit   = spWidth +12
+        ASSERT  spImage  = spWidth +16
+        ASSERT  spTrans  = spWidth +20
+        ASSERT  spMode   = spWidth +24
+
+        ADD     R5, R2, #spWidth        ; R5=width:R6=height:R7=LBit(=0)
+        LDMIA   R5, {R5-R11}            ; R8=RBit:R9=Image:R10=Trans:R11=Mode
+
+        TEQ     R0, #SpriteReason_SwitchOutputToMask
+        BNE     %FT23
+
+        MOV     R9, R10                 ; point at mask instead of image
+        MOVS    R0, R11, LSR #27
+        BEQ     %FT23                   ; check for old format masks
+
+        BL      GetMaskspWidth          ; adjust R5 and R8 to mask dimensions
+        BIC     R11, R11, #&F8000000
+        ORR     R11, R11, #&08000000    ; force it to a type 1 (1bpp) sprite
+23
+        ADD     R9, R9, R2              ; R9 -> sprite data or mask
+20
+        STR     R9, [WsPtr, #ScreenStart]
+        STR     R11, [WsPtr, #ModeNo]           ; new mode number
+
+        MOV     R10, R11
+        BL      PushModeInfoAnyMonitor
+        MOV     R11, R13
+
+        TEQ     R2, #0                          ; if outputting to screen
+        LDREQ   R5, [R11, #wkLineLength]        ; then load up values
+        LDREQ   R6, [R11, #wkYWindLimit]        ; from mode table
+        LDREQ   R7, [R11, #wkXWindLimit]
+        LDREQ   R8, [R11, #wkScrRCol]
+        LDREQ   R10, [R11, #wkScrBRow]
+        BEQ     %FT30                           ; and skip more sprite stuff
+
+        ADD     R5, R5, #1
+        MOV     R5, R5, LSL #2                  ; R5 = width in bytes
+
+        ADD     R7, R8, R5, LSL #3              ; R7 = LineLength*8 + spRBit
+        SUB     R7, R7, #31                     ; R7=active area width in bits
+
+        LDR     R8, [R11, #wkLog2BPC]
+        MOV     R7, R7, LSR R8                  ; R7 = width in pixels
+        MOV     R8, R7, LSR #3                  ; R8 = width in text columns
+        SUB     R7, R7, #1                      ; R7 = max value of GWRCol
+
+        SUB     R8, R8, #1                      ; R8 = max column number
+
+        ADD     R10, R6, #1                     ; R10 = no. of pixel rows
+        MOV     R10, R10, LSR #3                ; R10 = number of char rows
+        SUB     R10, R10, #1                    ; R4 = maximum row number
+
+30
+        STR     R5, [WsPtr, #LineLength]
+        STR     R6, [WsPtr, #YWindLimit]
+        STR     R7, [WsPtr, #XWindLimit]
+        STR     R8, [WsPtr, #ScrRCol]
+        STR     R10, [WsPtr, #ScrBRow]
+
+        LDR     R0,[R11, #wkNColour]
+        STR     R0,[WsPtr, #NColour]            ; copy number of colours -1
+
+        ADD     R11, R11, #wkmiddle
+        MOV     R0, #wkmidend-wkmiddle  ; number of bytes to do
+        ADD     R1, WsPtr, #YShftFactor ; first mode variable that we do
+
+40
+        LDR     R5, [R11], #4           ; copy byte from mode table
+        STR     R5, [R1], #4            ; into word variable
+        SUBS    R0, R0, #4              ; decrement count
+        BNE     %BT40                   ; loop until finished
+
+        ADD     R13, R13, #PushedInfoSize       ; junked stacked info
+
+; now create other variables from simple ones
+
+        LDR     R0, [WsPtr, #Log2BPP]
+        LDR     R1, [WsPtr, #Log2BPC]
+        LDR     R5, [WsPtr, #XEigFactor]
+        LDR     R6, [WsPtr, #ModeFlags]
+        TEQ     R2, #0
+        ORRNE   R6, R6, #Flag_HardScrollDisabled ; if sprite then disable hard
+        BICEQ   R6, R6, #Flag_HardScrollDisabled ; scroll, else enable it
+        STR     R6, [WsPtr, #ModeFlags]
+
+        ;if switching to a sprite, check for full palette 8bpp, and set modeflags and
+        ;NColour to suit.
+
+        TEQ     R2, #0
+        BEQ     %FT65                   ; switching to a sprite ?
+
+        CMP     R0, #3
+        BNE     %FT65                   ; which is 8bpp ?
+
+        ADD     R7, R2, #spImage        ; point R7 at the image/mask start pair
+        LDMIA   R7, {R7, LR}            ; fetch them
+        CMP     R7, LR                  ; which is lower
+        MOVGT   R7, LR                  ; use the lowest
+        SUB     R7, R7, #spPalette      ; get the size of the palette
+        CMP     R7, #&800               ; full 8bpp palette ?
+        BNE     %FT65
+
+        ORR     R6, R6, #Flag_FullPalette
+        STR     R6, [WsPtr, #ModeFlags] ; set the full palette flag
+
+        MOV     R7, #255
+        STR     R7, [WsPtr, #NColour]   ; and set the number of colours
+
+65
+        TST     R6, #Flag_DoubleVertical
+        ADREQL  R7, WrchNbitTab         ; point to correct table
+        ADRNEL  R7, WrchNbitDoubleTab
+        LDR     R8, [R7, R1, LSL #2]    ; get offset to correct code
+        ADD     R8, R8, R7              ; convert to absolute address
+        STR     R8, [WsPtr, #WrchNbit]
+
+        ADRL    R7, CursorNbitTab
+        LDR     R8, [R7, R1, LSL #2]
+        ADD     R8, R8, R7
+        TST     R6, #Flag_Teletext
+        ADRNEL  R8, CursorTeletext
+        STR     R8, [WsPtr, #CursorNbit]
+
+        TST     R6, #Flag_NonGraphic
+        MOVEQ   R7, #32                 ; if graphic mode
+        MOVEQ   R7, R7, LSR R1          ; then = (32 >> lbpc)-1
+        SUBEQ   R7, R7, #1
+        MOVNE   R7, #0                  ; else = 0 ;;; NOT REALLY SAFE BET ANYMORE!!!
+        STR     R7, [WsPtr, #NPix]
+
+        RSB     R7, R1, #5              ; XShftFactor = 5-log2bpc
+        STR     R7, [WsPtr, #XShftFactor]
+
+        LDR     R7, [WsPtr, #YEigFactor]
+        SUBS    R7, R5, R7              ; XEigFactor-YEigFactor
+        MOVLT   R7, #2                  ; X<Y => 2 (vert rect)
+        MOVGT   R7, #1                  ; X>Y => 1 (horz rect)
+                                        ; else X=Y => 0 (square)
+        STR     R7, [WsPtr, #AspectRatio]
+
+        MOV     R8, #1
+        MOV     R7, R8, LSL R0          ; bpp = 1 << lbpp
+        STR     R7, [WsPtr, #BitsPerPix]
+
+        MOV     R7, R8, LSL R1          ; bpc = 1 << lbpc
+        STR     R7, [WsPtr, #BytesPerChar]
+
+        TST     R6, #Flag_BBCGapMode    ; is it a BBC gap mode ?
+        MOVNE   R7, #&55                ; yes, then use colour 1
+        BNE     %FT70
+        TEQ     R0, #2                  ; if (1<<2=4) bits per pixel
+        MOVEQ   R7, #&77                ; then use colour 7 for cursor
+        MOVNE   R7, #&FF                ; else use colour 15
+70
+        ORR     R7, R7, R7, LSL #8      ; fill out to whole word
+        ORR     R7, R7, R7, LSL #16
+        STR     R7, [WsPtr, #CursorFill]
+
+        TST     R6, #Flag_DoubleVertical
+        MOVEQ   R7, #8                  ; if single vertical then 8 pixels
+        MOVNE   R7, #16                 ; if double vertical then 16 pixels
+        STR     R7, [WsPtr, #TCharSizeY]
+        TST     R6, #Flag_GapMode
+        ADDNE   R7, R7, R7, LSR #2      ; make 10 or 20 if gap mode
+        STR     R7, [WsPtr, #RowMult]
+        STR     R7, [WsPtr, #TCharSpaceY]
+        MOV     R8, #8
+        STR     R8, [WsPtr, #TCharSizeX]
+        STR     R8, [WsPtr, #TCharSpaceX]
+
+        LDR     R8, [WsPtr, #LineLength]
+        MUL     R7, R8, R7
+        STR     R7, [WsPtr, #RowLength]
+
+; finished doing other variables
+
+        SWI     XColourTrans_InvalidateCache    ; let ColourTrans know we've changed mode
+                                                ; so that colours get set up correctly
+
+        TEQ     R4, #0                  ; initialising from a save area ?
+        BEQ     %FT80                   ; no, then set to defaults
+
+        BL      UnpackVars
+        BL      AddressCursors
+        BL      ValidateVars
+        BL      PlainBit
+        BL      SetColour
+        LDR     R6, [WsPtr, #CursorFlags]
+        ORR     R6, R6, #TEUpdate       ; TextExpand needs updating
+        STR     R6, [WsPtr, #CursorFlags]
+        B       %FT90
+80
+
+; *****Change made by DJS
+; Original code was:
+;        MOV     R0, #0
+;        STR     R0, [WsPtr, #ECFShift]
+;        STR     R0, [WsPtr, #ECFYOffset]
+; This needed to be changed to make the bottom left of the screen (rather
+; than the top left) be the default ECF origin.
+        LDR     R0, [WsPtr, #YWindLimit]
+        ADD     R0, R0, #1
+        AND     R0, R0, #7
+        STR     R0, [WsPtr, #ECFYOffset]
+        MOV     R0, #0
+        STR     R0, [WsPtr, #ECFShift]
+; *****End of change made by DJS
+
+        STR     R0, [WsPtr, #ClipBoxEnable]
+        STRB    R0, [R0, #OsbyteVars + :INDEX: VDUqueueItems]
+
+        MOV     R0, #8
+        STR     R0, [WsPtr, #GCharSizeX]        ; chars are 8x8 by default
+        STR     R0, [WsPtr, #GCharSizeY]
+        STR     R0, [WsPtr, #GCharSpaceX]       ; and with 8x8 spacing
+        STR     R0, [WsPtr, #GCharSpaceY]
+
+        LDR     R1, [WsPtr, #ModeFlags]
+
+        LDR     R0, [WsPtr, #CursorFlags]
+        BIC     R0, R0, #(ActualState :OR: Vdu5Bit)
+        BIC     R0, R0, #(CursorsSplit :OR: PageMode :OR: TeletextMode :OR: ClipBoxEnableBit)
+        TST     R1, #Flag_Teletext              ; is it teletext ?
+        ORRNE   R0, R0, #TeletextMode           ; yes, then set bit
+        STR     R0, [WsPtr, #CursorFlags]
+        BLNE    TeletextInit                    ; initialise TTX if appropriate
+
+        BL      InitCursor                      ; initialise cursor after
+                                                ; cursorflags
+        BL      PlainBit                ; also sets up RAMMaskTb
+        BL      DefaultColours          ; N.B. SetColour is called by both
+        BL      DefaultEcfPattern       ; of these.
+        BL      DefaultLineStyle
+        BL      DefaultWindows
+        BL      Home
+90
+        BL      PostWrchCursor
+
+        MOV     R1, #Service_SwitchingOutputToSprite    ; call Ran's service
+        ADD     R2, WsPtr, #VduOutputCurrentState
+        LDMIA   R2, {R2-R5}                             ; load the registers that were in R0-R3 on entry
+        IssueService
+
+        Pull    R14
+        BICS    PC, R14, #V_bit
+
+InvalidSaveArea
+        ADRL    R0, SpriteErr_InvalidSaveArea
+      [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      ]
+        STR     R0, [WsPtr, #RetnReg0]
+        ORRS    PC, R14, #V_bit
+
+VOTS    =       "VOTS"
+
+
+; *****************************************************************************
+
+; Specially saved items
+
+                        ^       0
+InitFlag                #       4       ; 0 => uninit, "VOTS" => init
+SavedSpoolFileH         #       1       ; an OSBYTE variable
+SavedWrchDest           #       1       ; --------""--------
+SavedVDUqueueItems      #       1       ; --------""--------
+                        #       1       ; padding to word align it
+SavedDataOffset         #       0       ; start of compressed data
+
+        GBLA    FirstOffset
+        GBLA    NextOffset
+        GBLA    CompressedSize
+
+        MACRO
+        CompStart
+FirstOffset SETA -1
+NextOffset SETA -1
+CompressedSize SETA 0
+        MEND
+
+        MACRO
+        Compress $v0, $v1, $v2, $v3, $v4, $v5, $v6, $v7
+        [ "$v0"<>""
+        Compo $v0
+        ]
+        [ "$v1"<>""
+        Compo $v1
+        ]
+        [ "$v2"<>""
+        Compo $v2
+        ]
+        [ "$v3"<>""
+        Compo $v3
+        ]
+        [ "$v4"<>""
+        Compo $v4
+        ]
+        [ "$v5"<>""
+        Compo $v5
+        ]
+        [ "$v6"<>""
+        Compo $v6
+        ]
+        [ "$v7"<>""
+        Compo $v7
+        ]
+        MEND
+
+        MACRO
+        Compo   $var
+        [ FirstOffset <> -1
+         [ $var = NextOffset
+NextOffset SETA ($var)+4
+         |
+         DCW FirstOffset
+         DCW NextOffset
+CompressedSize SETA CompressedSize + (NextOffset-FirstOffset)
+FirstOffset SETA $var
+NextOffset SETA ($var)+4
+         ]
+        |
+FirstOffset SETA $var
+NextOffset SETA ($var)+4
+        ]
+        MEND
+
+        MACRO
+        CompMult $start, $size
+        [ FirstOffset <> -1
+         [ $start = NextOffset
+NextOffset SETA $start + $size
+         |
+         DCW FirstOffset
+         DCW NextOffset
+CompressedSize SETA CompressedSize + (NextOffset-FirstOffset)
+FirstOffset SETA $start
+NextOffset SETA $start + $size
+         ]
+        |
+FirstOffset SETA $start
+NextOffset SETA $start + $size
+        ]
+        MEND
+
+        MACRO
+        CompRange $start, $end
+        CompMult $start, ($end+4-$start)
+        MEND
+
+        MACRO
+        CompEnd
+        [ FirstOffset <> -1
+        DCW FirstOffset
+        DCW NextOffset
+CompressedSize SETA CompressedSize + (NextOffset-FirstOffset)
+        ]
+        &       -1
+        MEND
+
+CompressionTable
+        CompStart
+
+;        CompMult  FgEcf, 8*4        ; recreated from GCOLs + ecfs by
+;        CompMult  BgEcf, 8*4        ; call to SetColour
+
+        Compress  GPLFMD, GPLBMD, GFCOL, GBCOL, GWLCol, GWBRow, GWRCol, GWTRow
+        CompRange qqqPad, JVec
+
+;        Compress  ScreenStart ; worked out from sprite address each time
+
+        Compress  TWLCol, TWBRow, TWRCol, TWTRow
+        CompRange OrgX, NewPtY
+        Compress  TForeCol, TBackCol, CursorX, CursorY
+
+;       Compress  CursorAddr, InputCursorAddr - computed from (Input)CursorX,Y
+
+        Compress  InputCursorX, InputCursorY
+
+        Compress  VduStatus
+        Compress  CursorDesiredState, CursorStartOffset, CursorEndOffset
+        Compress  CursorCounter, CursorSpeed, Reg10Copy
+
+;       Compress  CursorFill, CursorNbit - computed from mode variables
+
+;       Compress  DriverBankAddr - refers to screen always
+
+        CompRange Ecf1, Ecf4+4
+        CompMult  DotLineStyle, 8
+
+;       Compress  ModeNo - stored in sprite itself
+
+        Compress  TFTint, TBTint, GFTint, GBTint
+        Compress  CursorFlags, CursorStack
+        Compress  ECFShift, ECFYOffset
+
+        Compress  GCharSizeX, GCharSizeY, GCharSpaceX, GCharSpaceY
+;       Compress  TCharSizeX, TCharSizeY        ; recomputed from mode number
+;       Compress  TCharSpaceX, TCharSpaceY      ; each time
+
+;       CompMult  FgEcfOraEor, 64      ; recreated from GCOLs and ecfs
+;       CompMult  BgEcfOraEor, 64      ; by call to SetColour
+;       CompMult  BgEcfStore, 64       ;
+
+        Compress  LineDotCnt, LineDotPatLSW, LineDotPatMSW, DotLineLength
+        Compress  BBCcompatibleECFs
+
+;       Compress  WrchNbit - computed from mode number
+
+        CompRange ClipBoxEnable, ClipBoxTRow
+
+        CompMult  FgPattern, 4*8
+        CompMult  BgPattern, 4*8
+
+        Compress  TextFgColour
+        Compress  TextBgColour
+
+        CompEnd
+CompressionTableEnd
+MainSize * CompressedSize + SavedDataOffset
+
+        ASSERT  MainSize <= SaveAreaSize
+        ! 0, "Space free in VDU save area = ":CC::STR:(SaveAreaSize-MainSize)
+
+        [ {FALSE}                       ; don't allow teletext mode for now
+TTXCompressionTable
+        CompStart
+        CompMult  TTXDoubleCounts, 28   ; (25 rounded up to a word)
+        CompMult  TTXMap, 41*25*4
+        CompEnd
+TTXCompressionTableEnd
+TTXSize * CompressedSize
+        ]
+
+
+; *****************************************************************************
+;
+;       PackVars - Pack variables into save area
+;
+
+PackVars ROUT
+        CLC                             ; clear carry - indicate packing
+PackOrUnpack
+        Push    "R0-R4,R14"
+        LDR     R0, [WsPtr, #VduSaveAreaPtr]
+        TEQ     R0, #0
+        Pull    "R0-R4,PC", EQ          ; ptr=0 => no area
+
+        TEQ     R0, #1
+        ADDEQ   R0, WsPtr, #VduSaveArea ; ptr=1 => use MOS's save area
+
+        BL      DoSpecialVars                   ; process special vars
+
+        ADD     R0, R0, #SavedDataOffset        ; move on to compressed data
+        ADR     R1, CompressionTable
+10
+        LDR     R2, [R1], #4
+        MVNS    R3, R2                  ; set Z if it was -1 (end of table)
+        Pull    "R0-R4,PC", EQ          ; (preserves C)
+
+        ADD     R3, WsPtr, R2, LSR #16          ; R3 = end pointer
+        MOV     R2, R2, LSL #16
+        ADD     R2, WsPtr, R2, LSR #16          ; R2 = start pointer
+20
+        LDRCC   R4, [R2], #4                    ; load a word from vars
+        LDRCS   R4, [R0], #4                    ; or from save area
+        STRCC   R4, [R0], #4                    ; store into save area
+        STRCS   R4, [R2], #4                    ; or into vars
+        TEQ     R2, R3                          ; end of this block ?
+        BNE     %BT20                           ; [no, so loop]
+        B       %BT10                           ; go back for another block
+
+; *****************************************************************************
+;
+;       UnpackVars - Unpack variables from save area
+;
+
+UnpackVars ROUT
+        SEC                             ; set carry - indicate unpacking
+        B       PackOrUnpack
+
+; *****************************************************************************
+;
+;       DoSpecialVars - Pack/unpack special vars: CursorAddr, InputCursorAddr,
+;        (stored relative to ScreenStart); SpoolFileH, WrchDest, VDUqueueItems
+;
+; in:   R0 -> save area
+;
+; out:  R0 preserved
+;       R1-R2 corrupted
+;       Flags preserved
+;
+
+DoSpecialVars ROUT
+        BYTEWS  R1
+        BCS     %FT10
+        LDRB    R2, [R1, #:INDEX: SpoolFileH]
+        STRB    R2, [R0, #SavedSpoolFileH]
+        LDRB    R2, [R1, #:INDEX: WrchDest]
+        STRB    R2, [R0, #SavedWrchDest]
+        LDRB    R2, [R1, #:INDEX: VDUqueueItems]
+        STRB    R2, [R0, #SavedVDUqueueItems]
+        LDR     R1, VOTS                ; initialised save area identifier
+        STR     R1, [R0, #InitFlag]
+        MOV     PC, R14
+
+; unpack special vars (R1 -> ByteWS)
+
+10
+        LDRB    R2, [R0, #SavedSpoolFileH]
+        STRB    R2, [R1, #:INDEX: SpoolFileH]
+        LDRB    R2, [R0, #SavedWrchDest]
+        STRB    R2, [R1, #:INDEX: WrchDest]
+        LDRB    R2, [R0, #SavedVDUqueueItems]
+        STRB    R2, [R1, #:INDEX: VDUqueueItems]
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       ValidateVars - Validate unpacked variables (windows, cursor posns)
+;
+
+ValidateVars ROUT
+        Push    R14
+        ASSERT  ScrBRow = ScrRCol + 4
+        ADD     R4, WsPtr, #ScrRCol
+        LDMIA   R4, {R4, R5}            ; R4 = ScrRCol; R5 = ScrBRow
+
+        ADD     R6, WsPtr, #TWLCol      ; R0 = TWLCol; R1 = TWBRow
+        LDMIA   R6, {R0-R3}             ; R2 = TWRCol; R3 = TWTRow
+
+        MOV     R7, #0
+        MOV     R8, R5
+        MOV     R9, R4
+        MOV     R10, #0
+        STMIA   R6, {R7-R10}            ; set up default window
+
+        BL      FSRegs                  ; and attempt to define text window
+
+        ASSERT  YWindLimit = XWindLimit + 4
+        ADD     R0, WsPtr, #XWindLimit
+        LDMIA   R0, {R2, R3}            ; R2 = XWindLimit; R3 = YWindLimit
+
+        ADD     R8, WsPtr, #GWLCol      ; R4 = GWLCol; R5 = GWBRow
+        LDMIA   R8, {R4-R7}             ; R6 = GWRCol; R7 = GWTRow
+
+        CMP     R6, R2                  ; if GWRCol > XWindLimit
+        CMPLS   R7, R3                  ; or GWTRow > YWindLimit
+
+        MOVHI   R0, #0
+        MOVHI   R1, #0
+        STMHIIA R8, {R0-R3}             ; then set default graphics window
+
+        Pull    PC
+
+        END
diff --git a/s/vdu/vdumodes b/s/vdu/vdumodes
new file mode 100644
index 0000000000000000000000000000000000000000..e3dedce8d9dc6d179530a47a78013a4ac9ebee61
--- /dev/null
+++ b/s/vdu/vdumodes
@@ -0,0 +1,997 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Kernel.Source.VduModes
+; 24-May-91
+
+; general purpose mode macros
+
+; pixel rate specifiers
+
+ [ VIDC_Type = "VIDC20"
+
+        MACRO
+        Moduli  $pixrate, $v, $r, $d
+ [ ($v=$r)
+CRPix_$pixrate  *       CR_RCLK :OR: ($d-1) :SHL: CR_PixelDivShift
+FSyn_$pixrate   *       ((64-1):SHL:FSyn_VShift) :OR: ((64-1):SHL:FSyn_RShift)
+ |
+CRPix_$pixrate  *       CR_VCLK :OR: ($d-1) :SHL: CR_PixelDivShift
+FSyn_$pixrate   *       (($v-1):SHL:FSyn_VShift) :OR: (($r-1):SHL:FSyn_RShift)
+ ]
+        MEND
+
+  [ MorrisSupport
+; Morris only values by RCM 28-Oct-94
+
+        MACRO
+        Moduli32  $pixrate, $v, $r, $d
+ [ ($v=$r)
+CRPix32_$pixrate  *     CR_RCLK :OR: ($d-1) :SHL: CR_PixelDivShift
+FSyn32_$pixrate   *     ((64-1):SHL:FSyn_VShift) :OR: ((64-1):SHL:FSyn_RShift)
+ |
+CRPix32_$pixrate  *     CR_VCLK :OR: ($d-1) :SHL: CR_PixelDivShift
+FSyn32_$pixrate   *     (($v-1):SHL:FSyn_VShift) :OR: (($r-1):SHL:FSyn_RShift)
+ ]
+        MEND
+
+
+;
+; Values suitable for any machine with rclk=32MHz (eg Morris based machines)
+;
+        Moduli32  24000,  6,  2,  4
+        Moduli32  16000,  2,  2,  2
+        Moduli32  12000,  6,  2,  8
+        Moduli32   8000,  2,  2,  4
+        Moduli32   6000,  2,  2,  5       ;  6.4000000
+
+        Moduli32  25175, 22,  7,  4       ; 25.1428571
+        Moduli32  16783, 21,  8,  5       ; 16.8000000
+        Moduli32  12587, 11,  4,  7       ; 12.5714286
+        Moduli32   8392, 21, 10,  8       ;  8.4000000
+
+        Moduli32  36000,  9,  4,  2
+
+        Moduli32   8800, 11, 5,  8        ;  8.8000000
+        Moduli32  17600, 11, 4,  5        ; 17.6000000
+        Moduli32  35200, 11, 5,  2        ; 35.3000000
+  ]
+;
+; Updated by TMD 28-Jan-94 to use new VCO range 55-110MHz
+;
+; Values suitable for any machine with rclk=24MHz (eg Medussa)
+;
+        Moduli  24000,  2,  2,  1
+        Moduli  16000,  8,  2,  6
+        Moduli  12000,  2,  2,  2
+        Moduli   8000,  2,  2,  3
+        Moduli   6000,  2,  2,  4
+
+        Moduli  25175, 21,  5,  4       ; 25.2000000
+        Moduli  16783,  7,  2,  5       ; 16.8000000
+        Moduli  12587, 21,  5,  8       ; 12.6000000
+        Moduli   8392, 14,  5,  8       ;  8.4000000
+
+        Moduli  36000,  6,  2,  2
+
+        Moduli   8800, 44, 15,  8       ;  8.8
+        Moduli  17600, 11,  3,  5       ; 17.6
+        Moduli  35200, 22,  5,  3       ; 35.2
+
+
+ |
+CRPix_24000 * 3 :OR: (0 :SHL: ClockControlShift)
+CRPix_16783 * 2 :OR: (1 :SHL: ClockControlShift)
+CRPix_16000 * 2 :OR: (0 :SHL: ClockControlShift)
+CRPix_12587 * 1 :OR: (1 :SHL: ClockControlShift)
+CRPix_12000 * 1 :OR: (0 :SHL: ClockControlShift)
+CRPix_8392  * 0 :OR: (1 :SHL: ClockControlShift)
+CRPix_8000  * 0 :OR: (0 :SHL: ClockControlShift)
+CRPix_25175 * 3 :OR: (1 :SHL: ClockControlShift)
+CRPix_36000 * 3 :OR: (2 :SHL: ClockControlShift)
+ ]
+
+        MACRO
+$label  VIDC_List $lbpp,$hsync,$hbpch,$hlbdr,$hdisp,$hrbdr,$hfpch, $vsync,$vbpch,$vlbdr,$vdisp,$vrbdr,$vfpch,$pixrate,$sp
+        [ VIDC_Type = "VIDC20"
+$label  VIDC_List20 $lbpp,$hsync,$hbpch,$hlbdr,$hdisp,$hrbdr,$hfpch, $vsync,$vbpch,$vlbdr,$vdisp,$vrbdr,$vfpch,$pixrate,$sp
+        |
+$label  VIDC_List10 $lbpp,$hsync,$hbpch,$hlbdr,$hdisp,$hrbdr,$hfpch, $vsync,$vbpch,$vlbdr,$vdisp,$vrbdr,$vfpch,$pixrate,$sp
+        ]
+        MEND
+
+; VIDC1 format VIDC list
+
+        MACRO
+$label  VIDC_List10 $lbpp,$hsync,$hbpch,$hlbdr,$hdisp,$hrbdr,$hfpch, $vsync,$vbpch,$vlbdr,$vdisp,$vrbdr,$vfpch,$pixrate,$sp
+
+$label
+        LCLA    sub
+        LCLA    sp
+        LCLA    syncpol
+        LCLA    hbpch
+        LCLA    hfpch
+        [ :LNOT: AssemblingArthur
+        &       0       ; VIDC list type (default)
+        &       0       ; base mode (irrelevant, all entries overwritten)
+        ]
+ [ $lbpp = 3
+sub     SETA    5
+ ]
+ [ $lbpp = 2
+sub     SETA    7
+ ]
+ [ $lbpp = 1
+sub     SETA    11
+ ]
+ [ $lbpp = 0
+sub     SETA    19
+ ]
+
+ [ "$sp"=""
+syncpol SETA    0 :SHL: SyncControlShift               ; normal sync polarity
+ |
+        ASSERT $sp<=3
+syncpol SETA    $sp :SHL: SyncControlShift
+ ]
+
+; Note that on VIDC1, hbpch and hfpch must be odd, and on VIDC20 they must be even
+; The macro now specifies them as even.
+; So increase back porch and reduce front porch by 1
+
+        ASSERT  ($hsync :AND: 1)=0
+        ASSERT  ($hbpch :AND: 1)=0
+hbpch   SETA    ($hbpch) + 1
+        ASSERT  ($hlbdr :AND: 1)=0
+        ASSERT  ($hdisp :AND: 1)=0
+        ASSERT  ($hrbdr :AND: 1)=0
+        ASSERT  ($hfpch :AND: 1)=0
+hfpch   SETA    ($hfpch) - 1
+ [ (($hsync+hbpch+$hlbdr+$hdisp+$hrbdr+hfpch) :AND: 3)<>0
+        ! 0, "Warning: mode unsuitable for interlaced use"
+ ]
+; Horizontal
+        &       (&80:SHL:24) :OR: ((($hsync+hbpch+$hlbdr+$hdisp+$hrbdr+hfpch -2  )/2) :SHL: 14) ; HCR
+        &       (&84:SHL:24) :OR: ((($hsync                                    -2  )/2) :SHL: 14) ; HSWR
+        &       (&88:SHL:24) :OR: ((($hsync+hbpch                             -1  )/2) :SHL: 14) ; HBSR
+        &       (&8C:SHL:24) :OR: ((($hsync+hbpch+$hlbdr                      -sub)/2) :SHL: 14) ; HDSR
+        &       (&90:SHL:24) :OR: ((($hsync+hbpch+$hlbdr+$hdisp               -sub)/2) :SHL: 14) ; HDER
+        &       (&94:SHL:24) :OR: ((($hsync+hbpch+$hlbdr+$hdisp+$hrbdr        -1  )/2) :SHL: 14) ; HBER
+        &       (&9C:SHL:24) :OR: (((($hsync+hbpch+$hlbdr+$hdisp+$hrbdr+hfpch-2)/2+1)/2):SHL:14); HIR
+; Vertical
+        &       (&A0:SHL:24) :OR: (($vsync+$vbpch+$vlbdr+$vdisp+$vrbdr+$vfpch    -1)    :SHL: 14) ; VCR
+        &       (&A4:SHL:24) :OR: (($vsync                                       -1)    :SHL: 14) ; VSWR
+        &       (&A8:SHL:24) :OR: (($vsync+$vbpch                                -1)    :SHL: 14) ; VBSR
+        &       (&AC:SHL:24) :OR: (($vsync+$vbpch+$vlbdr                         -1)    :SHL: 14) ; VDSR
+        &       (&B0:SHL:24) :OR: (($vsync+$vbpch+$vlbdr+$vdisp                  -1)    :SHL: 14) ; VDER
+        &       (&B4:SHL:24) :OR: (($vsync+$vbpch+$vlbdr+$vdisp+$vrbdr           -1)    :SHL: 14) ; VBER
+; Control Register
+        &       (&E0:SHL:24) :OR: (CRPix_$pixrate) :OR: ($lbpp :SHL: 2) :OR: syncpol
+        &       -1
+        MEND
+
+        MACRO
+$label  VIDC_List20 $lbpp,$hsync,$hbpch,$hlbdr,$hdisp,$hrbdr,$hfpch, $vsync,$vbpch,$vlbdr,$vdisp,$vrbdr,$vfpch,$pixrate,$sp
+
+$label
+        LCLA    sp
+        LCLA    syncpol
+        LCLA    dwidth
+        GBLA    framerate
+        LCLA    framepixels
+        [ :LNOT: AssemblingArthur
+        &       0       ; VIDC list type (default)
+        &       0       ; base mode (irrelevant, all entries overwritten)
+        ]
+ [ "$sp"=""
+sp      SETA    0                               ; normal sync polarity
+ |
+        ASSERT $sp<=3
+sp      SETA    $sp
+ ]
+syncpol SETA    0
+ [ (sp :AND: 1) <> 0
+syncpol SETA    syncpol :OR: Ext_InvertHSYNC
+ ]
+ [ (sp :AND: 2) <> 0
+syncpol SETA    syncpol :OR: Ext_InvertVSYNC
+ ]
+
+        ASSERT  ($hsync :AND: 1)=0
+        ASSERT  ($hbpch :AND: 1)=0
+        ASSERT  ($hlbdr :AND: 1)=0
+        ASSERT  ($hdisp :AND: 1)=0
+        ASSERT  ($hrbdr :AND: 1)=0
+        ASSERT  ($hfpch :AND: 1)=0
+
+        ASSERT (($hsync+$hbpch+$hlbdr+$hdisp+$hrbdr+$hfpch) :AND: 3)=0
+
+dwidth  SETA    $hdisp :SHL: $lbpp
+        ASSERT  (dwidth :AND: 31) = 0
+dwidth  SETA    dwidth / 32
+
+framepixels SETA ($hsync+$hbpch+$hlbdr+$hdisp+$hrbdr+$hfpch)*($vsync+$vbpch+$vlbdr+$vdisp+$vrbdr+$vfpch)
+framerate SETA  ($pixrate*1000+framepixels/2)/framepixels
+
+F_$label *      framerate       ; set up frame rate symbol
+
+; Horizontal
+        &       (&80:SHL:24) :OR: ($hsync+$hbpch+$hlbdr+$hdisp+$hrbdr+$hfpch-8)         ; HCR
+        &       (&81:SHL:24) :OR: ($hsync                                   -8)         ; HSWR
+        &       (&82:SHL:24) :OR: ($hsync+$hbpch                           -12)         ; HBSR
+        &       (&83:SHL:24) :OR: ($hsync+$hbpch+$hlbdr                    -18)         ; HDSR
+        &       (&84:SHL:24) :OR: ($hsync+$hbpch+$hlbdr+$hdisp             -18)         ; HDER
+        &       (&85:SHL:24) :OR: ($hsync+$hbpch+$hlbdr+$hdisp+$hrbdr      -12)         ; HBER
+        &       (&87:SHL:24) :OR: ($hsync+$hbpch+$hlbdr+$hdisp+$hrbdr+$hfpch  )/2       ; HIR
+; Vertical
+        &       (&90:SHL:24) :OR: ($vsync+$vbpch+$vlbdr+$vdisp+$vrbdr+$vfpch -2)        ; VCR
+        &       (&91:SHL:24) :OR: ($vsync                                    -2)        ; VSWR
+        &       (&92:SHL:24) :OR: ($vsync+$vbpch                             -2)        ; VBSR
+        &       (&93:SHL:24) :OR: ($vsync+$vbpch+$vlbdr                      -2)        ; VDSR
+        &       (&94:SHL:24) :OR: ($vsync+$vbpch+$vlbdr+$vdisp               -2)        ; VDER
+        &       (&95:SHL:24) :OR: ($vsync+$vbpch+$vlbdr+$vdisp+$vrbdr        -2)        ; VBER
+; Data Control Register
+ [ MEMC_Type = "IOMD"
+; Note: the bus bits get overwritten by the mode change code, to set it up correctly for 1 or 2 MBytes of VRAM
+        &       VIDCDataControl :OR: DCR_VRAMOff :OR: DCR_Bus31_0 :OR: (dwidth :SHL: DCR_HDWRShift) :OR: DCR_Sync
+ |
+        &       VIDCDataControl :OR: DCR_VRAMOff :OR: DCR_Bus31_0 :OR: (dwidth :SHL: DCR_HDWRShift)
+ ]
+; External Register
+        &       VIDCExternal :OR: Ext_DACsOn :OR: Ext_ERegExt :OR: syncpol
+; Frequency Synthesizer Register (rclk=24MHz)
+        &       VIDCFSyn :OR: (FSyn_$pixrate)
+; Control Register (rclk=24MHz)
+        &       VIDCControl :OR: (4 :SHL: CR_FIFOLoadShift) :OR: (CR_LBPP$lbpp) :OR: (CRPix_$pixrate)
+        &       -1
+ [ MorrisSupport
+; Frequency Synthesizer Register (32MHz)
+        &       VIDCFSyn :OR: (FSyn32_$pixrate)
+; Control Register (32MHz)
+        &       VIDCControl :OR: (4 :SHL: CR_FIFOLoadShift) :OR: (CR_LBPP$lbpp) :OR: (CRPix32_$pixrate)
+        &       -1
+ ]
+        MEND
+
+NumMonitorTypes *       5
+NumModes        *       50
+maxmode         *       NumModes-1
+minmode         *       0
+
+; These macro are used by various routines in the kernel to check for a valid mode number
+; MUST be kept up-to-date with the list of invalid modes between 0 and maxmode
+
+        MACRO
+        BranchIfKnownMode $reg, $address
+ [ VIDC_Type = "VIDC20"
+        CMP     $reg, #NumModes
+ |
+        CMP     $reg, #32               ; if not 32 (EQ => CS)
+        CMPNE   $reg, #NumModes         ; and less than ??
+ ]
+        BCC     $address                ; then branch
+        MEND
+
+        MACRO
+        BranchIfNotKnownMode $reg, $address
+ [ VIDC_Type = "VIDC20"
+        CMP     $reg, #NumModes
+ |
+        CMP     $reg, #32               ; if not 32 (EQ => CS)
+        CMPNE   $reg, #NumModes         ; and less than ??
+ ]
+        BCS     $address                ; branch if *NOT* known
+        MEND
+
+BigVIDCTable
+        &       VLN_0  - BigVIDCTable   ; 0
+        &       VLN_1  - BigVIDCTable   ; 1
+        &       VLN_2  - BigVIDCTable   ; 2
+        &       VLN_3  - BigVIDCTable   ; 3
+        &       VLN_4  - BigVIDCTable   ; 4
+        &       VLN_5  - BigVIDCTable   ; 5
+        &       VLN_6  - BigVIDCTable   ; 6
+        &       VLN_7  - BigVIDCTable   ; 7
+        &       VLN_8  - BigVIDCTable   ; 8
+        &       VLN_9  - BigVIDCTable   ; 9
+        &       VLN_10 - BigVIDCTable   ; 10
+        &       VLN_11 - BigVIDCTable   ; 11
+        &       VLN_12 - BigVIDCTable   ; 12
+        &       VLN_13 - BigVIDCTable   ; 13
+        &       VLN_14 - BigVIDCTable   ; 14
+        &       VLN_15 - BigVIDCTable   ; 15
+        &       VLN_16 - BigVIDCTable   ; 16
+        &       VLN_17 - BigVIDCTable   ; 17
+        &       -1                      ; 18
+        &       -1                      ; 19
+        &       -1                      ; 20
+        &       -1                      ; 21
+        &       VLN_22 - BigVIDCTable   ; 22
+        &       -1                      ; 23
+        &       VLN_24 - BigVIDCTable   ; 24
+        &       -1                      ; 25
+        &       -1                      ; 26
+        &       -1                      ; 27
+        &       -1                      ; 28
+        &       -1                      ; 29
+        &       -1                      ; 30
+        &       -1                      ; 31
+        &       -1                      ; 32
+        &       VLN_33 - BigVIDCTable   ; 33
+        &       VLN_34 - BigVIDCTable   ; 34
+        &       VLN_35 - BigVIDCTable   ; 35
+        &       VLN_36 - BigVIDCTable   ; 36
+        &       -1                      ; 37
+        &       -1                      ; 38
+        &       -1                      ; 39
+        &       -1                      ; 40
+        &       -1                      ; 41
+        &       -1                      ; 42
+        &       -1                      ; 43
+        &       -1                      ; 44
+        &       -1                      ; 45
+        &       -1                      ; 46
+        &       -1                      ; 47
+        &       -1                      ; 48
+        &       -1                      ; 49
+
+        ASSERT  (.-BigVIDCTable)=((NumModes*1):SHL:2)
+
+        &       VLM_0  - BigVIDCTable   ; 0
+        &       VLM_1  - BigVIDCTable   ; 1
+        &       VLM_2  - BigVIDCTable   ; 2
+        &       VLM_3  - BigVIDCTable   ; 3
+        &       VLM_4  - BigVIDCTable   ; 4
+        &       VLM_5  - BigVIDCTable   ; 5
+        &       VLM_6  - BigVIDCTable   ; 6
+        &       VLM_7  - BigVIDCTable   ; 7
+        &       VLM_8  - BigVIDCTable   ; 8
+        &       VLM_9  - BigVIDCTable   ; 9
+        &       VLM_10 - BigVIDCTable   ; 10
+        &       VLM_11 - BigVIDCTable   ; 11
+        &       VLM_12 - BigVIDCTable   ; 12
+        &       VLM_13 - BigVIDCTable   ; 13
+        &       VLM_14 - BigVIDCTable   ; 14
+        &       VLM_15 - BigVIDCTable   ; 15
+        &       VLM_16 - BigVIDCTable   ; 16
+        &       VLM_17 - BigVIDCTable   ; 17
+        &       VLM_18 - BigVIDCTable   ; 18
+        &       VLM_19 - BigVIDCTable   ; 19
+        &       VLM_20 - BigVIDCTable   ; 20
+        &       VLM_21 - BigVIDCTable   ; 21
+        &       VLM_22 - BigVIDCTable   ; 22
+        &       -1                      ; 23
+        &       VLM_24 - BigVIDCTable   ; 24
+        &       VLM_25 - BigVIDCTable   ; 25
+        &       VLM_26 - BigVIDCTable   ; 26
+        &       VLM_27 - BigVIDCTable   ; 27
+        &       VLM_28 - BigVIDCTable   ; 28
+        &       VLM_29 - BigVIDCTable   ; 29
+        &       VLM_30 - BigVIDCTable   ; 30
+        &       VLM_31 - BigVIDCTable   ; 31
+ [ VIDC_Type = "VIDC20"
+        &       VLM_32 - BigVIDCTable   ; 32
+ |
+        &       -1                      ; 32
+ ]
+        &       VLM_33 - BigVIDCTable   ; 33 Ovscn
+        &       VLM_34 - BigVIDCTable   ; 34
+        &       VLM_35 - BigVIDCTable   ; 35
+        &       VLM_36 - BigVIDCTable   ; 36
+        &       VLM_37 - BigVIDCTable   ; 37 dtp
+        &       VLM_38 - BigVIDCTable   ; 38
+        &       VLM_39 - BigVIDCTable   ; 39
+        &       VLM_40 - BigVIDCTable   ; 40
+        &       VLM_41 - BigVIDCTable   ; 41 EGA
+        &       VLM_42 - BigVIDCTable   ; 42
+        &       VLM_43 - BigVIDCTable   ; 43
+        &       VLM_44 - BigVIDCTable   ; 44 CGA
+        &       VLM_45 - BigVIDCTable   ; 45
+        &       VLM_46 - BigVIDCTable   ; 46
+        &       VLM_47 - BigVIDCTable   ; 47 PCSoft
+        &       VLM_48 - BigVIDCTable   ; 48 Games mode
+        &       VLM_49 - BigVIDCTable   ; 49 Games mode
+
+        ASSERT  (.-BigVIDCTable)=((NumModes*2):SHL:2)
+
+        &       -1                      ; 0
+        &       -1                      ; 1
+        &       -1                      ; 2
+        &       -1                      ; 3
+        &       -1                      ; 4
+        &       -1                      ; 5
+        &       -1                      ; 6
+        &       -1                      ; 7
+        &       -1                      ; 8
+        &       -1                      ; 9
+        &       -1                      ; 10
+        &       -1                      ; 11
+        &       -1                      ; 12
+        &       -1                      ; 13
+        &       -1                      ; 14
+        &       -1                      ; 15
+        &       -1                      ; 16
+        &       -1                      ; 17
+        &       -1                      ; 18
+        &       -1                      ; 19
+        &       -1                      ; 20
+        &       -1                      ; 21
+        &       -1                      ; 22
+        &       VLH_23 - BigVIDCTable   ; 23
+        &       -1                      ; 24
+        &       -1                      ; 25
+        &       -1                      ; 26
+        &       -1                      ; 27
+        &       -1                      ; 28
+        &       -1                      ; 29
+        &       -1                      ; 30
+        &       -1                      ; 31
+        &       -1                      ; 32
+        &       -1                      ; 33
+        &       -1                      ; 34
+        &       -1                      ; 35
+        &       -1                      ; 36
+        &       -1                      ; 37
+        &       -1                      ; 38
+        &       -1                      ; 39
+        &       -1                      ; 40
+        &       -1                      ; 41
+        &       -1                      ; 42
+        &       -1                      ; 43
+        &       -1                      ; 44
+        &       -1                      ; 45
+        &       -1                      ; 46
+        &       -1                      ; 47
+        &       -1                      ; 48
+        &       -1                      ; 49
+
+        ASSERT  (.-BigVIDCTable)=((NumModes*3):SHL:2)
+
+        &       VgaX_0  - BigVIDCTable  ; 0
+        &       VgaX_1  - BigVIDCTable  ; 1
+        &       VgaX_2  - BigVIDCTable  ; 2
+        &       VgaX_3  - BigVIDCTable  ; 3
+        &       VgaX_4  - BigVIDCTable  ; 4
+        &       VgaX_5  - BigVIDCTable  ; 5
+        &       VgaX_6  - BigVIDCTable  ; 6
+        &       VgaX_7  - BigVIDCTable  ; 7
+        &       VgaX_8  - BigVIDCTable  ; 8
+        &       VgaX_9  - BigVIDCTable  ; 9
+        &       VgaX_10 - BigVIDCTable  ; 10
+        &       VgaX_11 - BigVIDCTable  ; 11
+        &       VgaX_12 - BigVIDCTable  ; 12
+        &       VgaX_13 - BigVIDCTable  ; 13
+        &       VgaX_14 - BigVIDCTable  ; 14
+        &       VgaX_15 - BigVIDCTable  ; 15
+        &       -1                      ; 16
+        &       -1                      ; 17
+        &       -1                      ; 18
+        &       -1                      ; 19
+        &       -1                      ; 20
+        &       -1                      ; 21
+        &       -1                      ; 22
+        &       -1                      ; 23
+        &       -1                      ; 24
+        &       VLM_25 - BigVIDCTable   ; 25
+        &       VLM_26 - BigVIDCTable   ; 26
+        &       VLM_27 - BigVIDCTable   ; 27
+        &       VLM_28 - BigVIDCTable   ; 28
+        &       -1                      ; 29
+        &       -1                      ; 30
+        &       -1                      ; 31
+        &       -1                      ; 32
+        &       -1                      ; 33
+        &       -1                      ; 34
+        &       -1                      ; 35
+        &       -1                      ; 36
+        &       -1                      ; 37
+        &       -1                      ; 38
+        &       -1                      ; 39
+        &       -1                      ; 40
+        &      VgaX_41 - BigVIDCTable   ; 41 EGA
+        &      VgaX_42 - BigVIDCTable   ; 42
+        &      VgaX_43 - BigVIDCTable   ; 43
+        &      VgaX_44 - BigVIDCTable   ; 44 CGA
+        &      VgaX_45 - BigVIDCTable   ; 45
+        &      VgaX_46 - BigVIDCTable   ; 46
+        &       VLM_47 - BigVIDCTable   ; 47 PCSoft
+        &       VLM_48 - BigVIDCTable   ; 48 Games mode
+        &       VLM_49 - BigVIDCTable   ; 49 Games mode
+
+        ASSERT  (.-BigVIDCTable)=((NumModes*4):SHL:2)
+
+        &       VgaX_0  - BigVIDCTable  ; 0
+        &       VgaX_1  - BigVIDCTable  ; 1
+        &       VgaX_2  - BigVIDCTable  ; 2
+        &       VgaX_3  - BigVIDCTable  ; 3
+        &       VgaX_4  - BigVIDCTable  ; 4
+        &       VgaX_5  - BigVIDCTable  ; 5
+        &       VgaX_6  - BigVIDCTable  ; 6
+        &       VgaX_7  - BigVIDCTable  ; 7
+        &       VgaX_8  - BigVIDCTable  ; 8
+        &       VgaX_9  - BigVIDCTable  ; 9
+        &       VgaX_10 - BigVIDCTable  ; 10
+        &       VgaX_11 - BigVIDCTable  ; 11
+        &       VgaX_12 - BigVIDCTable  ; 12
+        &       VgaX_13 - BigVIDCTable  ; 13
+        &       VgaX_14 - BigVIDCTable  ; 14
+        &       VgaX_15 - BigVIDCTable  ; 15
+        &       -1                      ; 16
+        &       -1                      ; 17
+        &       -1                      ; 18
+        &       -1                      ; 19
+        &       -1                      ; 20
+        &       -1                      ; 21
+        &       -1                      ; 22
+        &       -1                      ; 23
+        &       -1                      ; 24
+        &       VLM_25 - BigVIDCTable   ; 25
+        &       VLM_26 - BigVIDCTable   ; 26
+        &       VLM_27 - BigVIDCTable   ; 27
+        &       VLM_28 - BigVIDCTable   ; 28
+        &       VLM_29 - BigVIDCTable   ; 29
+        &       VLM_30 - BigVIDCTable   ; 30
+        &       VLM_31 - BigVIDCTable   ; 31
+ [ VIDC_Type = "VIDC20"
+        &       VLM_32 - BigVIDCTable   ; 32
+ |
+        &       -1                      ; 32
+ ]
+        &       -1                      ; 33
+        &       -1                      ; 34
+        &       -1                      ; 35
+        &       -1                      ; 36
+        &       -1                      ; 37
+        &       -1                      ; 38
+        &       -1                      ; 39
+        &       -1                      ; 40
+        &      VgaX_41 - BigVIDCTable   ; 41 EGA
+        &      VgaX_42 - BigVIDCTable   ; 42
+        &      VgaX_43 - BigVIDCTable   ; 43
+        &      VgaX_44 - BigVIDCTable   ; 44 CGA
+        &      VgaX_45 - BigVIDCTable   ; 45
+        &      VgaX_46 - BigVIDCTable   ; 46
+        &       VLM_47 - BigVIDCTable   ; 47 PCSoft
+        &       VLM_48 - BigVIDCTable   ; 48 Games mode
+        &       VLM_49 - BigVIDCTable   ; 49 Games mode
+
+        ASSERT  (.-BigVIDCTable)=((NumModes*5):SHL:2)
+
+VLN_0   VIDC_List 0, 76, 88, 96, 640, 96, 28, 3,19,16,256,16, 2,16000,0         ; MODE 0
+VLN_1   VIDC_List 1, 38, 44, 48, 320, 48, 14, 3,19,16,256,16, 2, 8000,0         ; MODE 1
+VLN_2   VIDC_List 2, 38, 44, 48, 320, 48, 14, 3,19,16,256,16, 2, 8000,0         ; MODE 2
+VLN_3   VIDC_List 1, 76, 88, 96, 640, 96, 28, 3,19,19,250,19, 2,16000,0         ; MODE 3
+VLN_4   VIDC_List 0, 76, 88, 96, 640, 96, 28, 3,19,16,256,16, 2,16000,0         ; MODE 4
+VLN_5   VIDC_List 1, 38, 44, 48, 320, 48, 14, 3,19,16,256,16, 2, 8000,0         ; MODE 5
+VLN_6   VIDC_List 1, 38, 44, 48, 320, 48, 14, 3,19,19,250,19, 2, 8000,0         ; MODE 6
+VLN_7   VIDC_List 2, 38, 44, 48, 320, 48, 14, 3,19,19,250,19, 2, 8000,0         ; MODE 7
+VLN_8   VIDC_List 1, 76, 88, 96, 640, 96, 28, 3,19,16,256,16, 2,16000,0         ; MODE 8
+VLN_9   VIDC_List 2, 38, 44, 48, 320, 48, 14, 3,19,16,256,16, 2, 8000,0         ; MODE 9
+VLN_10  VIDC_List 3, 38, 44, 48, 320, 48, 14, 3,19,16,256,16, 2, 8000,0         ; MODE 10
+VLN_11  VIDC_List 1, 76, 88, 96, 640, 96, 28, 3,19,19,250,19, 2,16000,0         ; MODE 11
+VLN_12  VIDC_List 2, 76, 88, 96, 640, 96, 28, 3,19,16,256,16, 2,16000,0         ; MODE 12
+VLN_13  VIDC_List 3, 38, 44, 48, 320, 48, 14, 3,19,16,256,16, 2, 8000,0         ; MODE 13
+VLN_14  VIDC_List 2, 76, 88, 96, 640, 96, 28, 3,19,19,250,19, 2,16000,0         ; MODE 14
+VLN_15  VIDC_List 3, 76, 88, 96, 640, 96, 28, 3,19,16,256,16, 2,16000,0         ; MODE 15
+VLN_16  VIDC_List 2,114,132, 96,1056, 96, 42, 3,19,16,256,16, 2,24000,0         ; MODE 16
+VLN_17  VIDC_List 2,114,132, 96,1056, 96, 42, 3,19,19,250,19, 2,24000,0         ; MODE 17
+VLN_22  VIDC_List 2, 76,120,  0, 768,  0, 60, 3,19, 0,288, 0, 2,16000,0         ; MODE 22
+VLN_24  VIDC_List 3,114,132, 96,1056, 96, 42, 3,19,16,256,16, 2,24000,0         ; MODE 24
+VLN_33  VIDC_List 0, 76,120,  0, 768,  0, 60, 3,19, 0,288, 0, 2,16000,0         ; MODE 33
+VLN_34  VIDC_List 1, 76,120,  0, 768,  0, 60, 3,19, 0,288, 0, 2,16000,0         ; MODE 34
+VLN_35  VIDC_List 2, 76,120,  0, 768,  0, 60, 3,19, 0,288, 0, 2,16000,0         ; MODE 35
+VLN_36  VIDC_List 3, 76,120,  0, 768,  0, 60, 3,19, 0,288, 0, 2,16000,0         ; MODE 36
+
+VLM_0   VIDC_List 0, 72, 62, 88, 640, 88, 74, 3,16,17,256,17, 3,16000,0         ; MODE 0
+VLM_1   VIDC_List 1, 36, 30, 44, 320, 44, 38, 3,16,17,256,17, 3, 8000,0         ; MODE 1
+VLM_2   VIDC_List 2, 36, 30, 44, 320, 44, 38, 3,16,17,256,17, 3, 8000,0         ; MODE 2
+VLM_3   VIDC_List 1, 72, 62, 88, 640, 88, 74, 3,16,20,250,20, 3,16000,0         ; MODE 3
+VLM_4   VIDC_List 0, 72, 62, 88, 640, 88, 74, 3,16,17,256,17, 3,16000,0         ; MODE 4
+VLM_5   VIDC_List 1, 36, 30, 44, 320, 44, 38, 3,16,17,256,17, 3, 8000,0         ; MODE 5
+VLM_6   VIDC_List 1, 36, 30, 44, 320, 44, 38, 3,16,20,250,20, 3, 8000,0         ; MODE 6
+VLM_7   VIDC_List 2, 36, 30, 44, 320, 44, 38, 3,16,20,250,20, 3, 8000,0         ; MODE 7
+VLM_8   VIDC_List 1, 72, 62, 88, 640, 88, 74, 3,16,17,256,17, 3,16000,0         ; MODE 8
+VLM_9   VIDC_List 2, 36, 30, 44, 320, 44, 38, 3,16,17,256,17, 3, 8000,0         ; MODE 9
+VLM_10  VIDC_List 3, 36, 30, 44, 320, 44, 38, 3,16,17,256,17, 3, 8000,0         ; MODE 10
+VLM_11  VIDC_List 1, 72, 62, 88, 640, 88, 74, 3,16,20,250,20, 3,16000,0         ; MODE 11
+VLM_12  VIDC_List 2, 72, 62, 88, 640, 88, 74, 3,16,17,256,17, 3,16000,0         ; MODE 12
+VLM_13  VIDC_List 3, 36, 30, 44, 320, 44, 38, 3,16,17,256,17, 3, 8000,0         ; MODE 13
+VLM_14  VIDC_List 2, 72, 62, 88, 640, 88, 74, 3,16,20,250,20, 3,16000,0         ; MODE 14
+VLM_15  VIDC_List 3, 72, 62, 88, 640, 88, 74, 3,16,17,256,17, 3,16000,0         ; MODE 15
+VLM_16  VIDC_List 2,108, 72,106,1056,106, 88, 3,16,17,256,17, 3,24000,0         ; MODE 16
+VLM_17  VIDC_List 2,108, 72,106,1056,106, 88, 3,16,20,250,20, 3,24000,0         ; MODE 17
+VLM_18  VIDC_List 0, 56,112,  0, 640,  0, 88, 3,18, 0,512, 0, 1,24000,0         ; MODE 18
+VLM_19  VIDC_List 1, 56,112,  0, 640,  0, 88, 3,18, 0,512, 0, 1,24000,0         ; MODE 19
+VLM_20  VIDC_List 2, 56,112,  0, 640,  0, 88, 3,18, 0,512, 0, 1,24000,0         ; MODE 20
+VLM_21  VIDC_List 3, 56,112,  0, 640,  0, 88, 3,18, 0,512, 0, 1,24000,0         ; MODE 21
+VLM_22  VIDC_List 2, 76, 82,  0, 768,  0, 98, 3,19, 0,288, 0, 2,16000,0         ; MODE 22
+VLM_24  VIDC_List 3,108, 72,106,1056,106, 88, 3,16,17,256,17, 3,24000,0         ; MODE 24
+VLM_25  VIDC_List 0, 96, 46,  0, 640,  0, 18, 2,32, 0,480, 0,11,25175,3         ; MODE 25
+VLM_26  VIDC_List 1, 96, 46,  0, 640,  0, 18, 2,32, 0,480, 0,11,25175,3         ; MODE 26
+VLM_27  VIDC_List 2, 96, 46,  0, 640,  0, 18, 2,32, 0,480, 0,11,25175,3         ; MODE 27
+VLM_28  VIDC_List 3, 96, 46,  0, 640,  0, 18, 2,32, 0,480, 0,11,25175,3         ; MODE 28
+VLM_29  VIDC_List 0, 72,128,  0, 800,  0, 24, 2,22, 0,600, 0, 1,36000,0         ; MODE 29
+VLM_30  VIDC_List 1, 72,128,  0, 800,  0, 24, 2,22, 0,600, 0, 1,36000,0         ; MODE 30
+VLM_31  VIDC_List 2, 72,128,  0, 800,  0, 24, 2,22, 0,600, 0, 1,36000,0         ; MODE 31
+
+VLM_33  VIDC_List 0, 76,82,  0, 768,  0, 98, 3,19, 0,288, 0, 2,16000,0         ; MODE 33
+VLM_34  VIDC_List 1, 76,82,  0, 768,  0, 98, 3,19, 0,288, 0, 2,16000,0         ; MODE 34
+VLM_35  VIDC_List 2, 76,82,  0, 768,  0, 98, 3,19, 0,288, 0, 2,16000,0         ; MODE 35
+VLM_36  VIDC_List 3, 76,82,  0, 768,  0, 98, 3,19, 0,288, 0, 2,16000,0         ; MODE 36
+
+VLM_37  VIDC_List 0,118, 58,  0, 896,  0, 28, 3, 9, 0,352, 0, 0,24000,2         ; DTP 896x352
+VLM_38  VIDC_List 1,118, 58,  0, 896,  0, 28, 3, 9, 0,352, 0, 0,24000,2         ; EGA std
+VLM_39  VIDC_List 2,118, 58,  0, 896,  0, 28, 3, 9, 0,352, 0, 0,24000,2
+VLM_40  VIDC_List 3,118, 58,  0, 896,  0, 28, 3, 9, 0,352, 0, 0,24000,2
+
+VLM_41  VIDC_List 0, 76, 36,  0, 640,  0, 16, 3, 9, 0,352, 0, 0,16783,2         ; EGA
+VLM_42  VIDC_List 1, 76, 36,  0, 640,  0, 16, 3, 9, 0,352, 0, 0,16783,2
+VLM_43  VIDC_List 2, 76, 36,  0, 640,  0, 16, 3, 9, 0,352, 0, 0,16783,2
+
+VLM_44  VIDC_List 0, 72,162,  0, 640,  0,146, 3,34, 0,200, 0,25,16000,0         ; CGA
+VLM_45  VIDC_List 1, 72,162,  0, 640,  0,146, 3,34, 0,200, 0,25,16000,0
+VLM_46  VIDC_List 2, 72,162,  0, 640,  0,146, 3,34, 0,200, 0,25,16000,0
+
+VLM_47  VIDC_List 3, 64, 62,  0, 360,  0, 46, 2,32, 0,480, 0,11,16783,3         ; PC Soft
+
+VLM_48  VIDC_List 2, 48, 22,  0, 320,  0, 10, 2,32, 0,480, 0,11,12587,3         ; Games mode
+VLM_49  VIDC_List 3, 48, 22,  0, 320,  0, 10, 2,32, 0,480, 0,11,12587,3         ; Games mode
+
+; New modes for VIDC20
+
+ [ VIDC_Type = "VIDC20"
+VLM_32  VIDC_List 3, 72,128,  0, 800,  0, 24, 2,22, 0,600, 0, 1,36000,0         ; MODE 32 (800 x 600 x 8bpp)
+ ]
+
+
+VLH_23  VIDC_List 2, 52, 46,  2, 288,  2,  2, 3,43, 4,896, 4, 0,24000,0         ; MODE 23
+
+VgaX_0   VIDC_List 0, 96, 46,  0, 640,  0,18, 2,106,0,256, 0,85,25175,2         ; TV modes in VGA_350)
+VgaX_1   VIDC_List 1, 48, 22,  0, 320,  0,10, 2,106,0,256, 0,85,12587,2
+VgaX_2   VIDC_List 2, 48, 22,  0, 320,  0,10, 2,106,0,256, 0,85,12587,2
+VgaX_3   VIDC_List 1, 96, 46,  0, 640,  0,18, 2,109,0,250, 0,88,25175,2
+VgaX_4   VIDC_List 0, 96, 46,  0, 640,  0,18, 2,106,0,256, 0,85,25175,2
+VgaX_5   VIDC_List 1, 48, 22,  0, 320,  0,10, 2,106,0,256, 0,85,12587,2
+VgaX_6   VIDC_List 1, 48, 22,  0, 320,  0,10, 2,109,0,250, 0,88,12587,2
+VgaX_7   VIDC_List 2, 48, 22,  0, 320,  0,10, 2,109,0,250, 0,88,12587,2
+VgaX_8   VIDC_List 1, 96, 46,  0, 640,  0,18, 2,106,0,256, 0,85,25175,2
+VgaX_9   VIDC_List 2, 48, 22,  0, 320,  0,10, 2,106,0,256, 0,85,12587,2
+VgaX_10  VIDC_List 3, 48, 22,  0, 320,  0,10, 2,106,0,256, 0,85,12587,2
+VgaX_11  VIDC_List 1, 96, 46,  0, 640,  0,18, 2,109,0,250, 0,88,25175,2
+VgaX_12  VIDC_List 2, 96, 46,  0, 640,  0,18, 2,106,0,256, 0,85,25175,2
+VgaX_13  VIDC_List 3, 48, 22,  0, 320,  0,10, 2,106,0,256, 0,85,12587,2
+VgaX_14  VIDC_List 2, 96, 46,  0, 640,  0,18, 2,109,0,250, 0,88,25175,2
+VgaX_15  VIDC_List 3, 96, 46,  0, 640,  0,18, 2,106,0,256, 0,85,25175,2
+
+VgaX_41  VIDC_List 0, 96, 46,  0, 640,  0,18, 2,58, 0,352, 0,37,25175,2         ; EGA
+VgaX_42  VIDC_List 1, 96, 46,  0, 640,  0,18, 2,58, 0,352, 0,37,25175,2
+VgaX_43  VIDC_List 2, 96, 46,  0, 640,  0,18, 2,58, 0,352, 0,37,25175,2
+
+VgaX_44  VIDC_List 0, 96, 46,  0, 640,  0,18,2,134, 0,200,0,113,25175,2         ; CGA
+VgaX_45  VIDC_List 1, 96, 46,  0, 640,  0,18,2,134, 0,200,0,113,25175,2
+VgaX_46  VIDC_List 2, 96, 46,  0, 640,  0,18,2,134, 0,200,0,113,25175,2
+
+ [ ModeSelectors
+
+; Table of ideal frame rate for each numbered mode, to put in dummy mode selector
+; if numbered mode number is not directly available on this monitortype
+
+  [ VIDC_Type <> "VIDC20"
+F_VLM_32 * 0
+  ]
+
+FrameRateTable
+        =       F_VLN_0, F_VLN_1, F_VLN_2, F_VLN_3, F_VLN_4, F_VLN_5, F_VLN_6, F_VLN_7
+        =       F_VLN_8, F_VLN_9, F_VLN_10, F_VLN_11, F_VLN_12, F_VLN_13, F_VLN_14, F_VLN_15
+        =       F_VLN_16, F_VLN_17, F_VLM_18, F_VLM_19, F_VLM_20, F_VLM_21, F_VLN_22, F_VLH_23
+        =       F_VLN_24, F_VLM_25, F_VLM_26, F_VLM_27, F_VLM_28, F_VLM_29, F_VLM_30, F_VLM_31
+        =       F_VLM_32, F_VLN_33, F_VLN_34, F_VLN_35, F_VLN_36, F_VLM_37, F_VLM_38, F_VLM_39
+        =       F_VLM_40, F_VLM_41, F_VLM_42, F_VLM_43, F_VLM_44, F_VLM_45, F_VLM_46, F_VLM_47
+        =       F_VLM_48, F_VLM_49
+        ASSERT  . - FrameRateTable = NumModes
+        ALIGN
+
+ ]
+
+Vwstab
+ & VW_0  - Vwstab      ; MODE  0
+ & VW_1  - Vwstab      ; MODE  1
+ & VW_2  - Vwstab      ; MODE  2
+ & VW_3  - Vwstab      ; MODE  3
+ & VW_4  - Vwstab      ; MODE  4
+ & VW_5  - Vwstab      ; MODE  5
+ & VW_6  - Vwstab      ; MODE  6
+ & VW_7  - Vwstab      ; MODE  7
+ & VW_8  - Vwstab      ; MODE  8
+ & VW_9  - Vwstab      ; MODE  9
+ & VW_10 - Vwstab      ; MODE  10
+ & VW_11 - Vwstab      ; MODE  11
+ & VW_12 - Vwstab      ; MODE  12
+ & VW_13 - Vwstab      ; MODE  13
+ & VW_14 - Vwstab      ; MODE  14
+ & VW_15 - Vwstab      ; MODE  15
+ & VW_16 - Vwstab      ; MODE  16
+ & VW_17 - Vwstab      ; MODE  17
+ & VW_18 - Vwstab      ; MODE  18
+ & VW_19 - Vwstab      ; MODE  19
+ & VW_20 - Vwstab      ; MODE  20
+ & VW_21 - Vwstab      ; MODE  21
+ & VW_22 - Vwstab      ; MODE  22 (new mode for visually handicapped)
+ & VW_23 - Vwstab      ; MODE  23
+ & VW_24 - Vwstab      ; MODE  24
+ & VW_25 - Vwstab      ; MODE  25
+ & VW_26 - Vwstab      ; MODE  26
+ & VW_27 - Vwstab      ; MODE  27
+ & VW_28 - Vwstab      ; MODE  28
+ & VW_29 - Vwstab      ; MODE  29 exp
+ & VW_30 - Vwstab      ; MODE  30 exp
+ & VW_31 - Vwstab      ; MODE  31
+ & VW_32 - Vwstab      ; MODE  32
+ & VW_33 - Vwstab      ; MODE  33
+ & VW_34 - Vwstab      ; MODE  34
+ & VW_35 - Vwstab      ; MODE  35
+ & VW_36 - Vwstab      ; MODE  36
+ & VW_37 - Vwstab      ; MODE  37
+ & VW_38 - Vwstab      ; MODE  38
+ & VW_39 - Vwstab      ; MODE  39
+ & VW_40 - Vwstab      ; MODE  40
+ & VW_41 - Vwstab      ; MODE  41
+ & VW_42 - Vwstab      ; MODE  42
+ & VW_43 - Vwstab      ; MODE  43
+ & VW_44 - Vwstab      ; MODE  44
+ & VW_45 - Vwstab      ; MODE  45
+ & VW_46 - Vwstab      ; MODE  46
+ & VW_47 - Vwstab      ; MODE  47
+ & VW_48 - Vwstab      ; MODE  48
+ & VW_49 - Vwstab      ; MODE  49
+
+; To change the order of mode variables, change the following:-
+;  a)   The order of the 'wk' labels below
+;  b)   The order of the output variables in macro VWSTAB below
+;  c)   The order of the variables in '$.Hdr.Workspace' and '$.Hdr.NewSpace'
+
+        ^ 0
+wkstart         # 0
+wkScreenSize    # 4
+wkXWindLimit    # 4
+wkYWindLimit    # 4
+wkLineLength    # 4
+wkNColour       # 4             ; DDV; defined to be a word (17-Sep-92)
+wkmiddle        # 0
+wkYShftFactor   # 4
+wkModeFlags     # 4
+wkXEigFactor    # 4
+wkYEigFactor    # 4
+wkLog2BPC       # 4
+wkLog2BPP       # 4
+wkECFIndex      # 4
+wkmidend        # 0
+wkScrRCol       # 4
+wkScrBRow       # 4
+wkdispstart     # 0
+wkPalIndex      # 4
+wkend           # 0
+wksize * wkend-wkstart
+wkwordsize * (wksize + 3) :AND: :NOT: 3
+wklim * wksize-(wkmiddle-wkstart)
+
+ [ VIDC_Type = "VIDC20"
+VIDCParmsSize * (128*4) ; 128 words from 80xxxxxx to FFxxxxxx step 01000000
+ |
+VIDCParmsSize * (32*4)  ; 32 words from 80xxxxxx to FCxxxxxx step 04000000
+ ]
+
+PushedInfoSize * wkwordsize + VIDCParmsSize
+
+M22S * 1280*976/8  ;  screen size
+M23S * 1152*896/8
+M25S *  640*480/8
+M31S *  800*600/8
+M37S *  896*352/8
+M41S *  640*352/8
+M44S *  640*200/8
+M47S *  360*480/8
+
+        MACRO
+$label  VWSTAB1 $BaseMode, $ScreenSize,$LineLength,$XWindLimit,$YWindLimit,$YShftFactor,    $XEigFactor,$YEigFactor,$NColour,$ScrRCol,$ScrBRow,$Log2BPC,$Log2BPP,$PalIndex, $ECFIndex,$ModeFlags
+$label
+        LCLS    ScrSize
+        LCLA    xres
+        LCLA    yres
+        LCLA    pixdepth
+        LCLA    yeig
+        LCLA    scrsz
+        [ ("$ScreenSize" :RIGHT: 1) = "K"
+ScrSize SETS ("$ScreenSize" :LEFT: ((:LEN: "$ScreenSize")-1))
+scrsz   SETA    $ScrSize * 1024
+        |
+scrsz   SETA    $ScreenSize
+        ]
+        &       scrsz
+        &       $XWindLimit, $YWindLimit, $LineLength, $NColour
+        &       $YShftFactor, $ModeFlags, $XEigFactor, $YEigFactor
+        &       $Log2BPC, $Log2BPP, $ECFIndex
+        &       $ScrRCol, $ScrBRow, $PalIndex
+        ALIGN
+ [ MakeModeSelectorsForModeNumbers
+xres    SETA    (($XWindLimit+1):SHL:($Log2BPC)):SHR:($Log2BPP)
+yres    SETA    ($YWindLimit)+1
+pixdepth SETA   $Log2BPP
+  [ yres < xres/2
+yeig    SETA    2
+  |
+yeig    SETA    1
+  ]
+
+ModeSelector_$label
+        &       1, xres, yres, pixdepth
+        &       -1                                              ; frame rate (is this going to be OK?)
+  [ {FALSE} ; don't need any of this stuff - we're only getting the VIDC stuff via this mechanism
+   [ ($ModeFlags)<>0
+        &       VduExt_ModeFlags, $ModeFlags
+   ]
+   [ ($ScrRCol) <> (xres :SHR: 3)-1
+        &       VduExt_ScrRCol, $ScrRCol
+   ]
+   [ ($ScrBRow) <> (yres :SHR: 3)-1
+        &       VduExt_ScrBRow, $ScrBRow
+   ]
+   [ ($NColour) <> NColour_$Log2BPP
+        &       VduExt_NColour, $NColour
+   ]
+   [ ($XEigFactor) <> 1
+        &       VduExt_XEigFactor, $XEigFactor
+   ]
+   [ ($YEigFactor) <> yeig
+        &       VduExt_YEigFactor, $YEigFactor
+   ]
+   [ ($LineLength) <> (xres :SHL: pixdepth) :SHR: 3
+        &       VduExt_LineLength, $LineLength
+   ]
+   [ scrsz <> ((xres * yres) :SHL: pixdepth) :SHR: 3
+        &       VduExt_ScreenSize, scrsz
+   ]
+   [ ($Log2BPC) <> pixdepth
+        &       VduExt_Log2BPC, $Log2BPC
+   ]
+  ]
+        &       -1                                              ; terminator
+ ]
+        MEND
+
+        MACRO
+$label  VWSTAB2 $BaseMode, $ScreenSize,$LineLength,$XWindLimit,$YWindLimit,$YShftFactor,    $XEigFactor,$YEigFactor,$NColour,$ScrRCol,$ScrBRow,$Log2BPC,$Log2BPP,$PalIndex, $ECFIndex,$ModeFlags
+$label
+        LCLS ScrSize
+        &       0       ; wslist type (default)
+        &       $BaseMode
+        [ ("$ScreenSize" :RIGHT: 1) = "K"
+ScrSize SETS ("$ScreenSize" :LEFT: ((:LEN: "$ScreenSize")-1))
+        &       VduExt_ScreenSize, $ScrSize * 1024
+        |
+        &       VduExt_ScreenSize, $ScreenSize
+        ]
+        &       VduExt_LineLength, $LineLength
+        &       VduExt_XWindLimit, $XWindLimit
+        &       VduExt_YWindLimit, $YWindLimit
+        &       VduExt_YShftFactor, $YShftFactor
+        &       VduExt_XEigFactor, $XEigFactor
+        &       VduExt_YEigFactor, $YEigFactor
+        &       VduExt_NColour, $NColour
+        &       VduExt_ScrRCol, $ScrRCol
+        &       VduExt_ScrBRow, $ScrBRow
+        &       VduExt_Log2BPC, $Log2BPC
+        &       VduExt_Log2BPP, $Log2BPP
+        &       VduExt_ModeFlags, $ModeFlags
+        &       -1
+        MEND
+
+        MACRO
+$label  VWSTAB $BaseMode, $ScreenSize,$LineLength,$XWindLimit,$YWindLimit,$YShftFactor,    $XEigFactor,$YEigFactor,$NColour,$ScrRCol,$ScrBRow,$Log2BPC,$Log2BPP,$PalIndex, $ECFIndex,$ModeFlags
+        [ AssemblingArthur
+$label  VWSTAB1 $BaseMode, $ScreenSize,$LineLength,$XWindLimit,$YWindLimit,$YShftFactor,    $XEigFactor,$YEigFactor,$NColour,$ScrRCol,$ScrBRow,$Log2BPC,$Log2BPP,$PalIndex, $ECFIndex,$ModeFlags
+        |
+$label  VWSTAB2 $BaseMode, $ScreenSize,$LineLength,$XWindLimit,$YWindLimit,$YShftFactor,    $XEigFactor,$YEigFactor,$NColour,$ScrRCol,$ScrBRow,$Log2BPC,$Log2BPP,$PalIndex, $ECFIndex,$ModeFlags
+        ]
+        MEND
+
+VW_0    VWSTAB  0, 20K, 80, 639,255,4,1,2, 1, 79, 31,0,0,0,1,0                                             ; MODE 0
+VW_1    VWSTAB  1, 20K, 80, 319,255,4,2,2, 3, 39, 31,1,1,1,2,0                                             ; MODE 1
+VW_2    VWSTAB  2, 40K,160, 159,255,5,3,2,15, 19, 31,3,2,2,3,0                                             ; MODE 2
+VW_3    VWSTAB  3, 40K,160, 639,249,5,1,2, 1, 79, 24,1,1,0,0,Flag_NonGraphic+Flag_GapMode+Flag_BBCGapMode  ; MODE 3
+VW_4    VWSTAB  4, 20K, 80, 319,255,4,2,2, 1, 39, 31,1,0,0,4,0                                             ; MODE 4
+VW_5    VWSTAB  5, 20K, 80, 159,255,4,3,2, 3, 19, 31,2,1,1,2,0                                             ; MODE 5
+VW_6    VWSTAB  6, 20K, 80, 319,249,4,2,2, 1, 39, 24,1,1,0,0,Flag_NonGraphic+Flag_GapMode+Flag_BBCGapMode  ; MODE 6
+VW_7    VWSTAB  7, 80K,160, 319,249,5,2,2,15, 39, 24,2,2,4,0,Flag_NonGraphic+Flag_GapMode+Flag_Teletext    ; MODE 7
+VW_8    VWSTAB  8, 40K,160, 639,255,5,1,2, 3, 79, 31,1,1,1,2,0                                             ; MODE 8
+VW_9    VWSTAB  9, 40K,160, 319,255,5,2,2,15, 39, 31,2,2,2,3,0                                             ; MODE 9
+VW_10   VWSTAB 10, 80K,320, 159,255,6,3,2,63, 19, 31,4,3,3,5,0                                             ; MODE 10
+VW_11   VWSTAB 11, 40K,160, 639,249,5,1,2, 3, 79, 24,1,1,1,2,Flag_GapMode                                  ; MODE 11
+VW_12   VWSTAB 12, 80K,320, 639,255,6,1,2,15, 79, 31,2,2,2,3,0                                             ; MODE 12
+VW_13   VWSTAB 13, 80K,320, 319,255,6,2,2,63, 39, 31,3,3,3,5,0                                             ; MODE 13
+VW_14   VWSTAB 14, 80K,320, 639,249,6,1,2,15, 79, 24,2,2,2,3,Flag_GapMode                                  ; MODE 14
+VW_15   VWSTAB 15,160K,640, 639,255,7,1,2,63, 79, 31,3,3,3,5,0                                             ; MODE 15
+VW_16   VWSTAB 16,132K,528,1055,255,0,1,2,15,131, 31,2,2,2,3,0                                             ; MODE 16
+VW_17   VWSTAB 17,132K,528,1055,249,0,1,2,15,131, 24,2,2,2,3,Flag_GapMode                                  ; MODE 17
+VW_18   VWSTAB 18, 40K, 80, 639,511,4,1,1, 1, 79, 63,0,0,0,4,0                                             ; MODE 18
+VW_19   VWSTAB 19, 80K,160, 639,511,5,1,1, 3, 79, 63,1,1,1,2,0                                             ; MODE 19
+VW_20   VWSTAB 20,160K,320, 639,511,6,1,1,15, 79, 63,2,2,2,3,0                                             ; MODE 20
+VW_21   VWSTAB 21,320K,640, 639,511,7,1,1,63, 79, 63,3,3,3,5,0                                             ; MODE 21
+VW_22   VWSTAB 22,108K,384, 767,287,0,0,1,15, 95, 35,2,2,2,3,0                                             ; MODE 22
+VW_23   VWSTAB 23,M23S,144,1151,895,0,1,1, 1,143, 55,0,0,5,4,Flag_HiResMono+Flag_DoubleVertical            ; MODE 23
+VW_24   VWSTAB 24,264K,1056,1055,255,0,1,2,63,131,31,3,3,3,5,0                                             ; MODE 24
+VW_25 VWSTAB 25,M25S  , 80, 639,479,4,1,1, 1, 79, 59,0,0,0,4,0                                             ; MODE 25
+VW_26 VWSTAB 26,M25S*2,160, 639,479,5,1,1, 3, 79, 59,1,1,1,2,0                                             ; MODE 26
+VW_27 VWSTAB 27,M25S*4,320, 639,479,6,1,1,15, 79, 59,2,2,2,3,0                                             ; MODE 27
+VW_28 VWSTAB 28,M25S*8,640, 639,479,7,1,1,63, 79, 59,3,3,3,5,0                                             ; MODE 28
+
+VW_29 VWSTAB 29,M31S  ,100, 799,599,0,1,1, 1, 99, 74,0,0,0,4,0                                             ; MODE 29
+VW_30 VWSTAB 30,M31S*2,200, 799,599,0,1,1, 3, 99, 74,1,1,1,2,0                                             ; MODE 30
+VW_31 VWSTAB 31,M31S*4,400, 799,599,0,1,1,15, 99, 74,2,2,2,3,0                                             ; MODE 31
+ [ VIDC_Type = "VIDC20"
+VW_32 VWSTAB 32,M31S*8,800, 799,599,0,1,1,63, 99, 74,3,3,3,5,0                                             ; MODE 32
+ |
+VW_32 * VW_31
+ ]
+
+VW_33   VWSTAB 33, 27K, 96, 767,287,0,1,2, 1, 95, 35,0,0,0,4,0                                             ; MODE 33
+VW_34   VWSTAB 34, 54K,192, 767,287,0,1,2, 3, 95, 35,1,1,1,2,0                                             ; MODE 34
+VW_35   VWSTAB 35,108K,384, 767,287,0,1,2,15, 95, 35,2,2,2,3,0                                             ; MODE 35
+VW_36   VWSTAB 36,216K,768, 767,287,0,1,2,63, 95, 35,3,3,3,5,0                                             ; MODE 36
+
+VW_37 VWSTAB 37,M37S  ,112, 895,351,0,1,2, 1,111, 43,0,0,0,4,0                                             ; MODE 37
+VW_38 VWSTAB 38,M37S*2,224, 895,351,0,1,2, 3,111, 43,1,1,1,2,0                                             ; MODE 38
+VW_39 VWSTAB 39,M37S*4,448, 895,351,0,1,2,15,111, 43,2,2,2,3,0                                             ; MODE 39
+VW_40 VWSTAB 40,M37S*8,896, 895,351,0,1,2,63,111, 43,3,3,3,5,0                                             ; MODE 40
+
+VW_41 VWSTAB 41,M41S  , 80, 639,351,0,1,2, 1, 79, 43,0,0,0,4,0                                             ; EGA 1,2,4bpp
+VW_42 VWSTAB 42,M41S*2,160, 639,351,0,1,2, 3, 79, 43,1,1,1,2,0                                             ;
+VW_43 VWSTAB 43,M41S*4,320, 639,351,0,1,2,15, 79, 43,2,2,2,3,0                                             ; 640x352
+
+VW_44 VWSTAB 44,M44S  , 80, 639,199,0,1,2, 1, 79, 24,0,0,0,4,0                                             ; CGA 1,2,4bpp
+VW_45 VWSTAB 45,M44S*2,160, 639,199,0,1,2, 3, 79, 24,1,1,1,2,0                                             ;
+VW_46 VWSTAB 46,M44S*4,320, 639,199,0,1,2,15, 79, 24,2,2,2,3,0                                             ; 640x200
+
+VW_47 VWSTAB 47,M47S*8,360, 359,479,0,2,2,63, 89, 59,3,3,3,5,0                                             ; PCSoft 360 x 480 x 8bpp
+VW_48 VWSTAB 48,   75K,160, 319,479,0,2,1,15, 39, 59,2,2,2,3,0                                             ; Games 320 x 480 x 4bpp
+VW_49 VWSTAB 49,  150K,320, 319,479,0,2,1,63, 39, 59,3,3,3,5,0                                             ; Games 320 x 480 x 8bpp
+
+;            $BaseMode               $YShftFactor  $ScrBRow   $ModeFlags
+;               $ScreenSize            $XEigFactor    $Log2BPC
+;                      $LineLength       $YEigFactor    $Log2BPP
+;                           $XWindLimit    $NColour       $PalIndex
+;                                $YWindLimit  $ScrRCol      $ECFIndex
+
+ [ MakeModeSelectorsForModeNumbers
+ModeSelectorTable
+        GBLA    modecount
+        GBLS    h
+        GBLS    l
+modecount SETA  0
+        ASSERT  NumModes <= 100
+        WHILE   modecount < NumModes
+  [ modecount >= 10
+h       SETS    :CHR:(&30 + modecount/10)
+  |
+h       SETS    ""
+  ]
+l       SETS    :CHR:(&30 + modecount :MOD: 10)
+        &       ModeSelector_VW_$h$l-ModeSelectorTable
+modecount SETA  modecount + 1
+        WEND
+
+        ! 0,    "Mode selector table at ":CC: :STR: ModeSelectorTable
+ ]
+
+        END
diff --git a/s/vdu/vdupal10 b/s/vdu/vdupal10
new file mode 100644
index 0000000000000000000000000000000000000000..02bfdf8a4818f679b3bb40c0fe60045526ca3030
--- /dev/null
+++ b/s/vdu/vdupal10
@@ -0,0 +1,595 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > VduPal10
+
+; Palette programming for VIDC10 (ie VIDC1 or VIDC1a)
+
+; *****************************************************************************
+
+; PaletteV handler
+; ----------------
+
+; *****************************************************************************
+;
+;       MOSPaletteV - Default owner of PaletteV
+;
+
+        ASSERT  paletteV_Complete = 0
+        ASSERT  paletteV_Read = 1
+        ASSERT  paletteV_Set = 2
+        ASSERT  paletteV_1stFlashState = 3
+        ASSERT  paletteV_2ndFlashState = 4
+        ASSERT  paletteV_SetDefaultPalette = 5
+        ASSERT  paletteV_BlankScreen = 6
+
+MOSPaletteV ROUT
+        CMP     r4, #1
+        MOVCCS  pc, lr
+        BEQ     PV_ReadPalette
+        CMP     r4, #3
+        BCC     PV_SetPalette
+        BEQ     PV_1stFlashState
+        CMP     r4, #5
+        BCC     PV_2ndFlashState
+        BEQ     PV_SetDefaultPalette
+        CMP     r4, #7
+        BCC     PV_BlankScreen
+        MOVS    pc, lr                  ; reason code not known, so pass it on
+
+; *****************************************************************************
+
+PV_SetDefaultPalette ROUT
+        Push    "r0-r3,r5-r9"
+        LDR     r0, [WsPtr, #PalIndex]  ; the new index 0-5
+        ADR     r1, paldptab
+        LDR     r2, [r1, r0, LSL #2]    ; offset from r1 to start of table
+        ADD     r0, r0, #1              ; point to next item
+        LDR     r5, [r1, r0, LSL #2]    ; offset from r1 to end of table +1
+        ADD     r2, r2, r1              ; r2 -> start of table
+        ADD     r5, r5, r1              ; r5 -> end of table
+        MOV     r0, #0                  ; start at palette index 0
+        MOV     r1, #3                  ; set both halves
+10
+        LDR     r6, [r2], #4
+        MOVS    r3, r6, LSL #17         ; get 1st half word and set carry if flashing
+        MOV     r3, r3, LSR #17
+        MOVCC   r4, #0
+        LDRCS   r4, =&FFF               ; flashing so invert 2nd half RGB
+        BL      UpdateSettingStraightRGB
+        ADD     r0, r0, #1
+        MOVS    r3, r6, LSL #1          ; get 2nd half word and set carry if flashing
+        MOV     r3, r3, LSR #17
+        MOVCC   r4, #0
+        LDRCS   r4, =&FFF
+        BL      UpdateSettingStraightRGB
+        ADD     r0, r0, #1
+        TEQ     r2, r5
+        BNE     %BT10
+
+; now ensure all palette entries from 0..15 are initialised
+
+        MOV     r3, #0                  ; set unused (and border) to black
+        MOV     r4, #0                  ; no flashing
+20
+        CMP     r0, #16
+        BLCC    UpdateSettingStraightRGB
+        ADDCC   r0, r0, #1
+        BCC     %BT20
+
+        MOV     r2, #0                  ; Set border to black (sup 0)
+        BL      BorderInitEntry
+
+        MOV     r4, #0                  ; indicate PaletteV operation complete
+        Pull    "r0-r3,r5-r9,pc"        ; restore registers and claim vector
+
+        LTORG
+
+; *****************************************************************************
+
+; Table of offsets from paldata_pointer to palette data
+
+paldptab
+        &       paldat1-paldptab        ; 2  Colour Modes
+        &       paldat2-paldptab        ; 4
+        &       paldat4-paldptab        ; 16
+        &       paldat8-paldptab        ; 256
+        &       paldatT-paldptab        ; teletext mode
+        &       paldatHR-paldptab       ; Hi-res mono mode
+        &       paldatend-paldptab      ; end of table marker
+
+paldat1 ; Data for 1 bit modes - only necessary to program registers 0 and 1
+
+;               FSBGR
+
+        DCW     &0000           ; 0  Black
+        DCW     &0FFF           ; 1  White
+
+paldat2 ; Data for 2 bit modes - only necessary to program registers 0..3
+
+;               FSBGR
+
+        DCW     &0000           ; 0  Black
+        DCW     &000F           ; 1  Red
+        DCW     &00FF           ; 2  Yellow
+        DCW     &0FFF           ; 3  White
+
+paldat4 ; Data for 4 bit modes - program all registers
+        ; Flashing Colours will be needed here
+
+;               FSBGR
+
+        DCW     &0000           ; 0  Black
+        DCW     &000F           ; 1  Red
+        DCW     &00F0           ; 2  Green
+        DCW     &00FF           ; 3  Yellow
+        DCW     &0F00           ; 4  Blue
+        DCW     &0F0F           ; 5  Magenta
+        DCW     &0FF0           ; 6  Cyan
+        DCW     &0FFF           ; 7  White
+        DCW     &8000           ; 8  Flashing Black
+        DCW     &800F           ; 9  Flashing Red
+        DCW     &80F0           ; 10 Flashing Green
+        DCW     &80FF           ; 11 Flashing Yellow
+        DCW     &8F00           ; 12 Flashing Blue
+        DCW     &8F0F           ; 13 Flashing Magenta
+        DCW     &8FF0           ; 14 Flashing Cyan
+        DCW     &8FFF           ; 15 Flashing White
+
+paldat8 ; Data for 8 bit modes - Program all registers
+        ; PP field is 16 for all these, cos not true BBC colours
+
+;               FSBGR
+
+        DCW     &0000           ; 0
+        DCW     &0111           ; 1
+        DCW     &0222           ; 2
+        DCW     &0333           ; 3
+        DCW     &0004           ; 4
+        DCW     &0115           ; 5
+        DCW     &0226           ; 6
+        DCW     &0337           ; 7
+        DCW     &0400           ; 8
+        DCW     &0511           ; 9
+        DCW     &0622           ; A
+        DCW     &0733           ; B
+        DCW     &0404           ; C
+        DCW     &0515           ; D
+        DCW     &0626           ; E
+        DCW     &0737           ; F
+
+paldatT ; Data for teletext mode
+
+        DCW     &0000           ; 0 Black
+        DCW     &000F           ; 1 Red
+        DCW     &00F0           ; 2 Green
+        DCW     &00FF           ; 3 Yellow
+        DCW     &0F00           ; 4 Blue
+        DCW     &0F0F           ; 5 Magenta
+        DCW     &0FF0           ; 6 Cyan
+        DCW     &0FFF           ; 7 White
+
+; Colours 8 to 15 have supremacy bit set
+
+        DCW     &1000           ; 8 Supremacy+ Black
+        DCW     &100F           ; 9            Red
+        DCW     &10F0           ; 10           Green
+        DCW     &10FF           ; 11           Yellow
+        DCW     &1F00           ; 12           Blue
+        DCW     &1F0F           ; 13           Magenta
+        DCW     &1FF0           ; 14           Cyan
+        DCW     &1FFF           ; 15           White
+
+paldatHR  ; data for Hi-res mono mode
+        DCW     &0000           ; Only red gun necessary
+        DCW     &0111           ; but setting all three makes
+        DCW     &0222           ; reading it more natural
+        DCW     &0333
+        DCW     &0444
+        DCW     &0555
+        DCW     &0666
+        DCW     &0777
+        DCW     &0888
+        DCW     &0999
+        DCW     &0AAA
+        DCW     &0BBB
+        DCW     &0CCC
+        DCW     &0DDD
+        DCW     &0EEE
+        DCW     &0FFF
+
+        DCW     &0000           ; border black
+        DCW     &0010           ; fixed pointer colours
+        DCW     &0020
+        DCW     &0030
+paldatend
+
+
+; *****************************************************************************
+
+; PaletteV call to set palette
+; in:   R0 = logical colour
+;       R1 = colour type (16,17,18,24,25)
+;       R2 = BBGGRRS0
+;       R4 = PaletteV reason code
+;
+; out:  R4 = 0, claim vector if recognised
+;       otherwise preserve R4 and pass on
+;
+
+PV_SetPalette ROUT
+        Push    "r0-r3"
+        TEQ     r1, #16                 ; if 16 then set both colours
+        MOVEQ   r1, #3
+        BEQ     UpdateNormalColour
+
+        TEQ     r1, #17                 ; elif 17 then set 1st colour
+        MOVEQ   r1, #1
+        BEQ     UpdateNormalColour
+
+        TEQ     r1, #18                 ; elif 18 then set 2nd colour
+        MOVEQ   r1, #2
+        BEQ     UpdateNormalColour
+
+        TEQ     r1, #24                 ; elif 24 then border colour
+        BEQ     BorderColour
+
+        TEQ     r1, #25                 ; elif 25 then pointer colour
+        BEQ     PointerColour
+
+        Pull    "r0-r3"
+        MOVS    pc, lr                  ; else not defined
+
+; *****************************************************************************
+
+UpdateNormalColour ROUT
+        LDR     lr, [WsPtr, #DisplayNColour] ; get the mask
+        AND     r0, r0, lr              ; and mask it off
+        AND     r0, r0, #15             ; maximum 15
+        BL      UpdateSettingAndVIDC
+        MOV     r4, #0                  ; indicate successful PaletteV op
+        Pull    "r0-r3, pc"
+
+BorderInitEntry ENTRY "r0-r3"           ; entry used in default palette setting
+BorderColour ROUT
+        LDR     r0, [WsPtr, #PalIndex]  ; if hi res mono
+        TEQ     r0, #5
+        BICEQ   r2, r2, #&00300000      ; then knock out bits 0,1 of green palette
+
+        MOV     r0, #16                 ; palette index for border colour
+        MOV     r1, #3                  ; both colours
+        BL      UpdateSettingAndVIDC
+
+; Now test for BBC gap mode (ie 3 or 6)
+; if so then set colour 2 to same as border, and colour 3 to inverse
+
+        LDR     lr, [WsPtr, #DisplayModeFlags]
+        TST     lr, #Flag_BBCGapMode
+        BEQ     %FT10
+
+        MOV     r0, #2                  ; make colour 2 (gap) same as border
+        BL      UpdateSettingAndVIDC
+
+        MOV     r0, #3                  ; make colour 3 inverse gap
+        MVN     r2, r2                  ; invert R, G and B
+        EOR     r2, r2, #&FF            ; but use same supremacy
+        BL      UpdateSettingAndVIDC
+10
+        MOV     r4, #0                  ; indicate successful PaletteV op
+        Pull    "r0-r3, pc"
+
+
+PointerColour ROUT
+        LDR     r1, [WsPtr, #PalIndex]  ; if hi res mono, then don't allow
+        TEQ     r1, #5                  ; pointer palette changes
+        ANDNES  r0, r0, #3              ; force pointer colour number in range 1..3
+        BEQ     %FT10                   ; zero is invalid
+        ADD     r0, r0, #16             ; form palette index 17..19
+        MOV     r1, #3
+        BL      UpdateSettingAndVIDC
+10
+        MOV     r4, #0                  ; indicate successful PaletteV op
+        Pull    "r0-r3,pc"
+
+UpdateSettingAndVIDC ROUT
+        AND     r4, r2, #&F0000000
+        MOV     r3, r4, LSR #(28-8)     ; move blue to bits 8..11
+        AND     r4, r2, #&00F00000
+        ORR     r3, r3, r4, LSR #(20-4) ; move green to bits 4..7
+        AND     r4, r2, #&0000F000
+        ORR     r3, r3, r4, LSR #(12-0) ; move red to bits 0..3
+        AND     r4, r2, #&00000080
+        ORR     r3, r3, r4, LSL #(12-7) ; move sup to bit 12
+
+        CMP     r0, #16                 ; if not normal colour
+        BCS     %FT10                   ; then OK for hi-res-mono
+        LDR     r4, [WsPtr, #PalIndex]
+        TEQ     r4, #5
+        BEQ     UpdateHiResRGB
+10
+        MOV     r4, #0                  ; indicate no EORing between parts
+
+; and drop thru to ...
+
+UpdateSettingStraightRGB ENTRY "r2,r5,r6"
+        PHPSEI                          ; protect against IRQs
+        ORR     r3, r3, r0, LSL #26     ; form VIDC register number at top
+        LDRB    r5, [WsPtr, #ScreenBlankFlag]
+        TEQ     r5, #0
+        MOVNE   r5, #&00FFFFFF          ; bits to knock out if blanked
+
+        LDROSB  r2, FlashState          ; 0 => second, 1 => first
+        CMP     r2, #1                  ; C=0 => second, C=1 => first
+
+        TST     r1, #1
+        BEQ     %FT10                   ; skip if not setting 1st colour
+        ADD     r2, WsPtr, #FirPalSetting
+        STR     r3, [r2, r0, LSL #2]
+        MOVCS   r2, #VIDC
+        BICCS   r6, r3, r5              ; knock out bits for blanking
+        STRCS   r6, [r2]                ; poke VIDC if setting 1st colour and in 1st state
+10
+        TST     r1, #2
+        BEQ     %FT20                   ; skip if not setting 2nd colour
+        ADD     r2, WsPtr, #SecPalSetting
+        EOR     r3, r3, r4              ; toggle requested bits for 2nd half
+        STR     r3, [r2, r0, LSL #2]
+        MOVCC   r2, #VIDC
+        BICCC   r6, r3, r5              ; knock out bits for blanking
+        STRCC   r6, [r2]                ; poke VIDC if setting 2nd colour and in 2nd state
+20
+        PLP
+        EXITS                           ; restore registers, claim vector
+
+; *****************************************************************************
+;
+;       UpdateHiResRGB - Routine to program normal palette for Hi-Res-Mono display
+;
+; in:   r0 = logical colour
+;       r1 = mask of which states to update (bit 0 = 1st flash state, bit 1 = 2nd)
+;       r3 = &0000SBGR
+;
+; out:  r3, r4 may be corrupted
+;
+
+UpdateHiResRGB ENTRY "r5"
+        PHPSEI
+        Push    "lr"
+        LDROSB  r5, FlashState
+        TEQ     r5, #0                  ; 0 => 2nd state, 1 => 1st state
+        MOVNE   r5, #VIDC               ; 1st state => r5 = VIDC, else = 0
+
+        TST     r1, #1
+        ADDNE   r4, WsPtr, #FirPalSetting
+        BLNE    UpdateOneHiResSetting
+
+        EOR     r5, r5, #VIDC           ; 2nd state => r5 = VIDC, else = 0
+        TST     r1, #2
+        ADDNE   r4, WsPtr, #SecPalSetting
+        BLNE    UpdateOneHiResSetting
+        Pull    "lr"
+        PLP
+        EXITS
+
+UpdateOneHiResSetting ENTRY "r1,r2,r6-r9"
+        LDRB    lr, [WsPtr, #ScreenBlankFlag]
+        TEQ     lr, #0
+        MOVNE   lr, #&00FFFFFF          ; if blanked, mask off these bits
+
+        LDR     r2, =&FFF
+        TEQ     r0, #0
+        MOVEQ   r1, r3                  ; if programming colour 0 then 1st colour is r3
+        LDREQ   r6, [r4, #15*4]         ; and 2nd colour is what's in colour 15
+        LDRNE   r1, [r4, #0*4]          ; else 1st colour is what's in colour 0
+        MOVNE   r6, r3                  ; and 2nd colour is r3
+
+        EOR     r6, r6, #&008           ; invert bit R3 of colour 15
+        EOR     r6, r6, r1              ; inverted EOR of colours 0 and 15
+        ANDS    r6, r6, #&008           ; just use R3 for black/white
+        MOVNE   r6, r2                  ; if non-zero make BIC mask full RGB else 0
+        ANDS    r1, r1, #&008           ; if colour 0 = white then make EOR mask full RGB else 0
+        MOVNE   r1, r2
+
+        ADD     r7, r4, #16*4           ; end of table
+        MOV     r8, #0                  ; start
+        LDR     r2, =&04000111          ; amount to add on each time round
+
+10
+        BIC     r9, r8, r6              ; take value and knock out BIC mask
+        EOR     r9, r9, r1              ; then toggle EOR mask
+        STR     r9, [r4], #4            ; store in soft copy
+        TEQ     r5, #0                  ; if updating VIDC
+        BICNE   r9, r9, lr              ; knock out bits (for blanking)
+        STRNE   r9, [r5]                ; then do it
+        ADD     r8, r8, r2              ; move on to next value
+        TEQ     r4, r7                  ; if not done all 16 entries
+        BNE     %BT10                   ; then loop
+
+        EXITS
+
+        LTORG
+
+; *****************************************************************************
+;
+;       PV_ReadPalette - PaletteV read palette handler
+;
+; in:   R0 = logical colour
+;       R1 = 16 (read normal colour)
+;            24 (read border colour)
+;            25 (read cursor colour)
+;
+; out:  R2 = first flash setting   (BBGGRRS0), supremacy bit 7
+;       R3 = second flash setting  (BBGGRRS0), supremacy bit 7
+;
+
+PV_ReadPalette ROUT
+        Push    "r10,r11"
+        LDR     r10, [WsPtr, #DisplayNColour] ; logical colours in this mode -1
+        TEQ     r1, #24                 ; is it reading border palette
+        MOVEQ   r11, #16                ; then set up border index
+        BEQ     %FT10                   ; and go
+
+        TEQ     r1, #25                 ; is it reading pointer palette
+        BEQ     %FT05
+        AND     r11, r0, r10            ; no, then force into suitable range
+        AND     r11, r11, #15           ; only allow 0..15
+        LDR     r2, [WsPtr, #PalIndex]
+        TEQ     r2, #5                  ; if hi res mono
+        TEQEQ   r11, #1                 ; and reading colour 1
+        MOVEQ   r11, #15                ; then read colour 15 instead
+        B       %FT10                   ; always skip
+05
+        ANDS    r11, r0, #3             ; else force logical colour 0..3
+        BEQ     %FT99                   ; and 0 is illegal, so do nothing
+        ADD     r11, r11, #16           ; set up correct index
+10
+        CMP     r11, #16                ; is it normal one (not border/cursor)
+        MOVCSS  r3, #0                  ; no, then don't fudge colours; Z=1
+                                        ; (carry preserved from CMP)
+        ANDCCS  r3, r10, #&F0           ; yes, then fudge if 256 colour mode
+
+        ADD     r10, WsPtr, #FirPalSetting
+
+        LDR     r11, [r10, r11, LSL #2]! ; r11 := 1st XX00SBGR
+        BLNE    FudgeRGB
+
+        AND     lr, r11, #&F00          ; lr  := 1st 00000B00
+        MOV     r2, lr, LSL #20         ; r2  := 1st B0000000
+        AND     lr, r11, #&0F0          ; lr  := 1st 000000G0
+        ORR     r2, r2, lr, LSL #16     ; r2  := 1st B0G00000
+        AND     lr, r11, #&00F          ; lr  := 1st 0000000R
+        ORR     r2, r2, lr, LSL #12     ; r2  := 1st B0G0R000
+        ORR     r2, r2, r2, LSR #4      ; r2  := 1st BBGGRR00
+        AND     lr, r11, #&1000
+        ORR     r2, r2, lr, LSR #5      ; r2  := 1st BBGGRRS0
+
+        LDR     r11, [r10, #SecPalSetting-FirPalSetting]
+        BLNE    FudgeRGB
+
+        AND     lr, r11, #&F00          ; lr  := 2nd 00000B00
+        MOV     r3, lr, LSL #20         ; r3  := 2nd B0000000
+        AND     lr, r11, #&0F0          ; lr  := 2nd 000000G0
+        ORR     r3, r3, lr, LSL #16     ; r3  := 2nd B0G00000
+        AND     lr, r11, #&00F          ; lr  := 2nd 0000000R
+        ORR     r3, r3, lr, LSL #12     ; r3  := 2nd B0G0R000
+        ORR     r3, r3, r3, LSR #4      ; r3  := 2nd BBGGRR00
+        AND     lr, r11, #&1000
+        ORR     r3, r3, lr, LSR #5      ; r3  := 2nd BBGGRRS0
+99
+        MOV     r4, #0
+        Pull    "r10, r11, pc"
+
+FudgeRGB ROUT
+        BIC     r11, r11, #&C8          ; knock out top bit R, top 2 bits G
+        BIC     r11, r11, #&800         ; knock out top bit B
+
+        TST     r0, #&10                ; override top bit of red
+        ORRNE   r11, r11, #&8
+        TST     r0, #&20                ; override next to top bit of green
+        ORRNE   r11, r11, #&40
+        TST     r0, #&40                ; override top bit of green
+        ORRNE   r11, r11, #&80
+        TST     r0, #&80                ; override top bit of blue
+        ORRNE   r11, r11, #&800
+        MOVS    pc, lr
+
+; *****************************************************************************
+;
+;       PV_1stFlashState - PaletteV routine to set first flash state
+;
+
+PV_1stFlashState ROUT
+        Push    "r0-r3"
+        ADD     r0, WsPtr, #FirPalSetting
+DoR0Flash
+        MOV     r1, #15                 ; logical colour
+DoAllUpdate
+        LDRB    lr, [WsPtr, #ScreenBlankFlag]
+        TEQ     lr, #0
+        MOVEQ   lr, #&FF000000          ; unblanked, just knock off top 8 bits
+        MOVNE   lr, #&FFFFFFFF          ; blanked, knock off all bits!
+        MOV     r2, #VIDC
+10
+        LDR     r3, [r0, r1, LSL #2]
+        BIC     r3, r3, lr              ; get rid of top bits, or all if blanked
+        ORR     r3, r3, r1, LSL #26     ; OR in register number
+        STR     r3, [r2]                ; program VidC
+        SUBS    r1, r1, #1
+        BPL     %BT10
+
+        MOV     r4, #0
+        Pull    "r0-r3, pc"
+
+; *****************************************************************************
+;
+;       PV_2ndFlashState - PaletteV routine to set second flash state
+;
+
+PV_2ndFlashState ROUT
+        Push    "r0-r3"
+        ADD     r0, WsPtr, #SecPalSetting
+        B       DoR0Flash
+
+; *****************************************************************************
+;
+;       UpdateAllPalette - Update all VIDC palette entries
+;
+
+UpdateAllPalette ENTRY "r0-r3"          ; "r0-r3,lr" stacked ready to branch to code
+        LDROSB  r0, FlashState
+        CMP     r0, #1
+        ADDCS   r0, WsPtr, #FirPalSetting ; FlashState = 1 => 1st state, 0 => 2nd state
+        ADDCC   r0, WsPtr, #SecPalSetting
+        MOV     r1, #19                 ; do 0-15 and border + 3 pointer
+        B       DoAllUpdate
+
+; *****************************************************************************
+;
+;       PV_BlankScreen - Blank/unblank screen
+;
+; in:   R0 = -1 => read blank state
+;       R0 = 0 => unblank screen
+;       R0 = 1 => blank screen
+;
+; out:  R0 = old state (0=unblanked, 1=blanked)
+;       R4 = 0
+
+PV_BlankScreen ROUT
+        Push    "r1-r3"
+        LDRB    r3, [WsPtr, #ScreenBlankFlag]
+        CMP     r0, #1
+        BHI     %FT99
+
+        TEQ     r0, r3                  ; changing to same state? (carry preserved)
+        BEQ     %FT99                   ; if so, do nothing
+
+        STRB    r0, [WsPtr, #ScreenBlankFlag] ; update new state
+
+        MOVCC   r0, #(1 :SHL: 10) :OR: (0 :SHL: 8) ; unblank: video DMA on, no refresh
+        MOVCS   r0, #(0 :SHL: 10) :OR: (3 :SHL: 8) ; blank:   video DMA off, continuous refresh
+        MOV     r1, #(1 :SHL: 10) :OR: (3 :SHL: 8) ; bits to modify
+        SWI     XOS_UpdateMEMC
+
+        MOV     r0, pc
+        ORR     lr, r0, #I_bit          ; disable IRQs so we don't get a flash in the middle
+        TEQP    lr, #0
+        BL      UpdateAllPalette        ; update all palette, including border + pointer
+        TEQP    r0, #0                  ; restore old IRQ state
+99
+        MOV     r0, r3
+        MOV     r4, #0
+        Pull    "r1-r3, pc"
+
+
+        END
diff --git a/s/vdu/vdupal20 b/s/vdu/vdupal20
new file mode 100644
index 0000000000000000000000000000000000000000..1e8d48285b060d8818dcb56d1a2ea14e44083a55
--- /dev/null
+++ b/s/vdu/vdupal20
@@ -0,0 +1,1013 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > VduPal20
+
+; Palette programming for VIDC20
+
+; *****************************************************************************
+
+; PaletteV handler
+; ----------------
+
+; *****************************************************************************
+;
+;       MOSPaletteV - Default owner of PaletteV
+;
+
+        ASSERT  paletteV_Complete = 0
+        ASSERT  paletteV_Read = 1
+        ASSERT  paletteV_Set = 2
+        ASSERT  paletteV_1stFlashState = 3
+        ASSERT  paletteV_2ndFlashState = 4
+        ASSERT  paletteV_SetDefaultPalette = 5
+        ASSERT  paletteV_BlankScreen = 6
+        ASSERT  paletteV_BulkRead = 7
+        ASSERT  paletteV_BulkWrite = 8
+ [ GammaCorrection
+        ASSERT  paletteV_GammaCorrection = 9
+ ]
+
+MOSPaletteV ROUT
+        CMP     r4, #1
+        MOVCCS  pc, lr
+        BEQ     PV_ReadPalette
+        CMP     r4, #3
+        BCC     PV_SetPalette
+        BEQ     PV_1stFlashState
+        CMP     r4, #5
+        BCC     PV_2ndFlashState
+        BEQ     PV_SetDefaultPalette
+        CMP     r4, #7
+        BCC     PV_BlankScreen
+        BEQ     PV_BulkRead
+        CMP     r4, #9
+        BCC     PV_BulkWrite
+
+ [ GammaCorrection
+        BEQ     PV_GammaCorrect
+ ]
+        MOVS    pc, lr                  ; reason code not known, so pass it on
+
+; *****************************************************************************
+
+PV_SetDefaultPalette ROUT
+        Push    "r0-r3,r5-r9"
+        LDR     r0, [WsPtr, #PalIndex]  ; the new index 0-7
+        ADR     r1, paldptab
+        LDR     r2, [r1, r0, LSL #2]    ; offset from r1 to start of table
+        ADD     r0, r0, #1              ; point to next item
+        LDR     r5, [r1, r0, LSL #2]    ; offset from r1 to end of table +1
+        ADDS    r2, r2, r1              ; r2 -> start of table
+        BICMI   pc, r2, #&80000000      ; if negative then it's a routine
+        ADD     r5, r5, r1              ; r5 -> end of table
+        BIC     r5, r5, #&80000000
+        MOV     r0, #0                  ; start at palette index 0
+        MOV     r1, #3                  ; set both halves
+10
+        LDR     r6, [r2], #4
+        MOVS    r3, r6, LSL #17         ; get 1st half word and set carry if flashing
+        MOV     r3, r3, LSR #17
+        MOVCC   r4, #0
+        LDRCS   r4, =&FFF               ; flashing so invert 2nd half RGB
+        BL      UpdateSettingStraightRGB
+        ADD     r0, r0, #1
+        MOVS    r3, r6, LSL #1          ; get 2nd half word and set carry if flashing
+        MOV     r3, r3, LSR #17
+        MOVCC   r4, #0
+        LDRCS   r4, =&FFF
+        BL      UpdateSettingStraightRGB
+        ADD     r0, r0, #1
+        TEQ     r2, r5
+        BNE     %BT10
+
+; now ensure all palette entries from 0..255 are initialised
+
+        MOV     r3, #0                  ; set unused (and border) to black
+        MOV     r4, #0                  ; no flashing
+20
+        CMP     r0, #256
+        BLCC    UpdateSettingStraightRGB
+        ADDCC   r0, r0, #1
+        BCC     %BT20
+
+FinishDefault
+        MOV     r2, #0                  ; set border to black (and setup colours 2,3 in BBC gap modes)
+        BL      BorderInitEntry
+
+        MOV     r4, #0                  ; indicate PaletteV operation complete
+        Pull    "r0-r3,r5-r9,pc"        ; restore registers and claim vector
+
+        LTORG
+
+; *****************************************************************************
+
+; Table of offsets from paldata_pointer to palette data
+
+paldptab
+        &       paldat1-paldptab        ; 2  Colour Modes
+        &       paldat2-paldptab        ; 4
+        &       paldat4-paldptab        ; 16
+        &       (paldat8-paldptab) :OR: &80000000  ; 256 (VIDC10 compatible) - use routine
+        &       paldatT-paldptab        ; teletext mode
+        &       paldatHR-paldptab       ; Hi-res mono mode
+        &       (paldat16-paldptab) :OR: &80000000 ; 16 bpp - use routine
+        &       (paldat32-paldptab) :OR: &80000000 ; 32 bpp (or 256 greys - they're identical!) - use routine
+        &       paldatend-paldptab      ; end of table marker
+
+paldat1 ; Data for 1 bit modes - only necessary to program registers 0 and 1
+
+;               FSBGR
+
+        DCW     &0000           ; 0  Black
+        DCW     &0FFF           ; 1  White
+
+paldat2 ; Data for 2 bit modes - only necessary to program registers 0..3
+
+;               FSBGR
+
+        DCW     &0000           ; 0  Black
+        DCW     &000F           ; 1  Red
+        DCW     &00FF           ; 2  Yellow
+        DCW     &0FFF           ; 3  White
+
+paldat4 ; Data for 4 bit modes - program all registers
+        ; Flashing Colours will be needed here
+
+;               FSBGR
+
+        DCW     &0000           ; 0  Black
+        DCW     &000F           ; 1  Red
+        DCW     &00F0           ; 2  Green
+        DCW     &00FF           ; 3  Yellow
+        DCW     &0F00           ; 4  Blue
+        DCW     &0F0F           ; 5  Magenta
+        DCW     &0FF0           ; 6  Cyan
+        DCW     &0FFF           ; 7  White
+        DCW     &8000           ; 8  Flashing Black
+        DCW     &800F           ; 9  Flashing Red
+        DCW     &80F0           ; 10 Flashing Green
+        DCW     &80FF           ; 11 Flashing Yellow
+        DCW     &8F00           ; 12 Flashing Blue
+        DCW     &8F0F           ; 13 Flashing Magenta
+        DCW     &8FF0           ; 14 Flashing Cyan
+        DCW     &8FFF           ; 15 Flashing White
+
+; Routine to initialise palette for VIDC10-compatible 8bpp modes
+; Note this must still be in between paldat4 and paldatT
+
+paldat8 ROUT
+        MOV     r1, #3                  ; set both halves of palette
+        MOV     r0, #0                  ; starting index
+10
+        AND     r2, r0, #3              ; get tint bits
+        ORR     r2, r2, r2, LSL #4      ; and duplicate into bits 8,9,12,13,16,17,20,21,24,25,28,29
+        ORR     r2, r2, r2, LSL #8
+        ORR     r2, r2, r2, LSL #16
+        BIC     r2, r2, #&FF
+        TST     r0, #4
+        ORRNE   r2, r2, #&00004400
+        TST     r0, #8
+        ORRNE   r2, r2, #&44000000
+        TST     r0, #&10
+        ORRNE   r2, r2, #&00008800
+        TST     r0, #&20
+        ORRNE   r2, r2, #&00440000
+        TST     r0, #&40
+        ORRNE   r2, r2, #&00880000
+        TST     r0, #&80
+        ORRNE   r2, r2, #&88000000
+        BL      UpdateSettingAndVIDC
+        ADD     r0, r0, #1
+        TEQ     r0, #&100
+        BNE     %BT10
+        B       FinishDefault
+
+paldatT ; Data for teletext mode
+
+        DCW     &0000           ; 0 Black
+        DCW     &000F           ; 1 Red
+        DCW     &00F0           ; 2 Green
+        DCW     &00FF           ; 3 Yellow
+        DCW     &0F00           ; 4 Blue
+        DCW     &0F0F           ; 5 Magenta
+        DCW     &0FF0           ; 6 Cyan
+        DCW     &0FFF           ; 7 White
+
+; Colours 8 to 15 have supremacy bit set
+
+        DCW     &1000           ; 8 Supremacy+ Black
+        DCW     &100F           ; 9            Red
+        DCW     &10F0           ; 10           Green
+        DCW     &10FF           ; 11           Yellow
+        DCW     &1F00           ; 12           Blue
+        DCW     &1F0F           ; 13           Magenta
+        DCW     &1FF0           ; 14           Cyan
+        DCW     &1FFF           ; 15           White
+
+paldatHR  ; data for Hi-res mono mode
+        DCW     &0000           ; Only red gun necessary
+        DCW     &0111           ; but setting all three makes
+        DCW     &0222           ; reading it more natural
+        DCW     &0333
+        DCW     &0444
+        DCW     &0555
+        DCW     &0666
+        DCW     &0777
+        DCW     &0888
+        DCW     &0999
+        DCW     &0AAA
+        DCW     &0BBB
+        DCW     &0CCC
+        DCW     &0DDD
+        DCW     &0EEE
+        DCW     &0FFF
+
+        DCW     &0000           ; border black
+        DCW     &0010           ; fixed pointer colours
+        DCW     &0020
+        DCW     &0030
+
+paldat16 ROUT
+        ADR     r5, paldat16tab
+palmetatab
+        MOV     r1, #3                  ; set both halves of palette
+        MOV     r0, #0                  ; starting index
+10
+        MOV     r8, r5
+        MOV     r2, #0
+        MOV     r6, r0
+20
+        LDR     r7, [r8], #4
+        MOVS    r6, r6, LSR #1
+        ORRCS   r2, r2, r7
+        BNE     %BT20
+        BL      UpdateSettingAndVIDC
+        ADD     r0, r0, #1
+        TEQ     r0, #&100
+        BNE     %BT10
+        B       FinishDefault
+
+paldat16tab
+        &       &00000800       ; palette bit 0
+        &       &00081000       ;             1
+        &       &08102100       ;             2
+        &       &10214200       ;             3
+        &       &21428400       ;             4
+        &       &42840000       ;             5
+        &       &84000000       ;             6
+        &       &00000000       ;             7
+
+paldat32 ROUT
+        ADR     r5, paldat32tab
+        B       palmetatab
+
+paldat32tab
+        &       &01010100       ; palette bit 0
+        &       &02020200       ;             1
+        &       &04040400       ;             2
+        &       &08080800       ;             3
+        &       &10101000       ;             4
+        &       &20202000       ;             5
+        &       &40404000       ;             6
+        &       &80808000       ;             7
+
+paldatend
+
+; *****************************************************************************
+
+; PaletteV call to set palette in bulk
+; in:   R0 => list of colours, or 0
+;       R1 =  colour type (16,17,18,24,25) in b24-31 & number to do in b23-b00
+;       R2 => list of palette entries (both flash states if 16, one if 17/18)
+;       R4 = PaletteV reason code
+;
+; out:  R4 = 0, claim vector if recognised
+;       otherwise preserve R4 and pass on
+
+PV_BulkWrite    ROUT
+        Push    "R0-R3,R5-R11"          ; pc already stacked
+
+        ;register usage:
+        ;[R6] colour list
+        ;R7   colour type
+        ;R8   max number
+        ;[R9] palette entries
+        ;R10  loop counter
+        ;R11  colour number
+
+        MOV     R7,R1,LSR #24
+        BIC     R8,R1,#&FF000000
+        MOV     R6,R0
+        MOV     R9,R2
+
+        MOV     R10,#0
+10
+        TEQ     R6,#0
+        MOVEQ   R11,R10
+        LDRNE   R11,[R6],#4
+
+        TEQ     R7,#16
+        TEQNE   R7,#17
+
+        MOVEQ   R0,R11
+        MOVEQ   R1,#1
+        LDREQ   R2,[R9],#4
+        BLEQ    UpdateNormalColour
+
+        TEQ     R7,#16
+        TEQNE   R7,#18
+
+        MOVEQ   R0,R11
+        MOVEQ   R1,#2
+        LDREQ   R2,[R9],#4
+        BLEQ    UpdateNormalColour
+
+        TEQ     R7,#24
+
+        MOVEQ   R0,R11
+        LDREQ   R2,[R9],#4
+        BLEQ    BorderColour
+
+        TEQ     R7,#25
+
+        MOVEQ   R0,R11
+        LDREQ   R2,[R9],#4
+        BLEQ    PointerColour
+
+        ADD     R10,R10,#1
+        CMP     R10,R8
+        BCC     %BT10
+
+        MOV     R4,#0
+        Pull    "R0-R3,R5-R11,PC"
+
+; *****************************************************************************
+
+; PaletteV call to set palette
+; in:   R0 = logical colour
+;       R1 = colour type (16,17,18,24,25)
+;       R2 = BBGGRRS0
+;       R4 = PaletteV reason code
+;
+; out:  R4 = 0, claim vector if recognised
+;       otherwise preserve R4 and pass on
+;
+
+;amg 19/4/93 - change this routine to make all the calls subroutines rather
+; than branches. Although it will slow this down a bit, it makes the bulk
+; write a lot simpler and involves less duplication of mungeing code.
+
+PV_SetPalette ROUT
+        Push    "r0-r3"
+        TEQ     r1, #16                 ; if 16 then set both colours
+        MOVEQ   r1, #3
+        BEQ     Call_UpdateNormalColour
+
+        TEQ     r1, #17                 ; elif 17 then set 1st colour
+        MOVEQ   r1, #1
+        BEQ     Call_UpdateNormalColour
+
+        TEQ     r1, #18                 ; elif 18 then set 2nd colour
+        MOVEQ   r1, #2
+        BEQ     Call_UpdateNormalColour
+
+        TEQ     r1, #24                 ; elif 24 then border colour
+        BEQ     Call_BorderColour
+
+        TEQ     r1, #25                 ; elif 25 then pointer colour
+        BEQ     Call_PointerColour
+10
+        Pull    "r0-r3"
+        MOVS    pc, lr                  ; else not defined
+
+Call_UpdateNormalColour
+        BL      UpdateNormalColour
+        Pull    "r0-r3,pc"
+
+BorderInitEntry Push "r0-r3,lr"           ; entry used in default palette setting
+Call_BorderColour
+        BL      BorderColour
+        Pull    "r0-r3,pc"
+
+Call_PointerColour
+        BL      PointerColour
+        Pull    "r0-r3,pc"
+
+; *****************************************************************************
+
+UpdateNormalColour ROUT
+        Push    "LR"
+        LDR     lr, [WsPtr, #DisplayNColour] ; get the mask
+        TEQ     lr, #63                 ; is it brain-damaged VIDC10-compatible 256 colour mode?
+        BEQ     %FT10
+        AND     r0, r0, lr              ; and mask it off
+        AND     r0, r0, #255            ; definitely no more than 256 palette entries
+        BL      UpdateSettingAndVIDC
+05
+        MOV     r4, #0                  ; indicate successful PaletteV op
+        Pull    "pc"
+
+10
+        AND     r0, r0, #15             ; starting palette entry
+20
+        LDR     r3, =&88CC8800          ; r3 = bits controlled by bits 4..7 of pixel value
+        BIC     r2, r2, r3
+        TST     r0, #&10                ; test bit 4 (r3,7)
+        ORRNE   r2, r2, #&00008800
+        TST     r0, #&20                ; test bit 5 (g2,6)
+        ORRNE   r2, r2, #&00440000
+        TST     r0, #&40                ; test bit 6 (g3,7)
+        ORRNE   r2, r2, #&00880000
+        TST     r0, #&80                ; test bit 7 (b3,7)
+        ORRNE   r2, r2, #&88000000
+        BL      UpdateSettingAndVIDC
+        ADD     r0, r0, #&10
+        CMP     r0, #&100
+        BCC     %BT20
+        B       %BT05
+
+BorderColour ROUT
+        Push    "LR"
+        MOV     r0, #&40000000          ; pseudo-palette-index for border colour
+        MOV     r1, #3                  ; both colours
+        BL      UpdateSettingAndVIDC
+
+; Now test for BBC gap mode (ie 3 or 6)
+; if so then set colour 2 to same as border, and colour 3 to inverse
+
+        LDR     lr, [WsPtr, #DisplayModeFlags]
+        TST     lr, #Flag_BBCGapMode
+        BEQ     %FT10
+
+        MOV     r0, #2                  ; make colour 2 (gap) same as border
+        BL      UpdateSettingAndVIDC
+
+        MOV     r0, #3                  ; make colour 3 inverse gap
+        MVN     r2, r2                  ; invert R, G and B
+        EOR     r2, r2, #&FF            ; but use same supremacy
+        BL      UpdateSettingAndVIDC
+10
+        MOV     r4, #0                  ; indicate successful PaletteV op
+        Pull    "pc"
+
+
+PointerColour ROUT
+        Push    "LR"
+        ANDS    r0, r0, #3              ; force pointer colour number in range 1..3
+        BEQ     %FT10                   ; zero is invalid
+        MOV     r0, r0, LSL #28         ; move up to top nybble
+        ORR     r0, r0, #&40000000      ; form pseudo-palette-index
+        MOV     r1, #3
+        BL      UpdateSettingAndVIDC
+10
+        MOV     r4, #0                  ; indicate successful PaletteV op
+        Pull    "pc"
+
+; UpdateSettingStraightRGB
+;
+; in:   r0 = logical colour (border = 4 << 28, pointer colours = 5,6,7 << 28)
+;       r1 = bit mask of which flash states to update (bit 0 set => 1st, bit 1 set => 2nd)
+;       r3 = SBGR
+;       r4 = SBGR to EOR with to go from 1st to 2nd flash state
+
+; out:  r0,r1,r2,r4 preserved
+;       r3 corrupted
+
+UpdateSettingStraightRGB ENTRY "r2,r5,r6,r7"
+        ANDS    r5, r3, #1 :SHL: 12     ; get supremacy bit in 1st colour
+        MOVNE   r5, #1 :SHL: 27         ; put in r5 in new position
+        AND     r6, r3, #&FF0           ; r6 = 00000BG0
+        ORR     r5, r5, r6, LSL #8      ; r5 = 0s0BG000
+        AND     r6, r3, #&0FF           ; r6 = 000000GR
+        ORR     r5, r5, r6, LSL #4      ; r5 = 0s0BGGR0
+        AND     r6, r3, #&00F           ; r6 = 0000000R
+        ORR     r5, r5, r6              ; r5 = 0s0BGGRR
+        AND     r6, r3, #&F00           ; r6 = 00000B00
+        ORR     r3, r5, r6, LSL #12     ; r3 = 0sBBGGRR
+
+        ANDS    r5, r4, #1 :SHL: 12     ; get supremacy bit in EOR mask
+        MOVNE   r5, #1 :SHL: 27         ; put in r5 in new position
+        AND     r6, r4, #&FF0           ; r6 = 00000BG0
+        ORR     r5, r5, r6, LSL #8      ; r5 = 0s0BG000
+        AND     r6, r4, #&0FF           ; r6 = 000000GR
+        ORR     r5, r5, r6, LSL #4      ; r5 = 0s0BGGR0
+        AND     r6, r4, #&00F           ; r6 = 0000000R
+        ORR     r5, r5, r6              ; r5 = 0s0BGGRR
+        AND     r6, r4, #&F00           ; r6 = 00000B00
+        ORR     r4, r5, r6, LSL #12     ; r5 = 0sBBGGRR
+        B       UpdateSettingCommon
+
+
+; UpdateSettingAndVIDC
+;
+; in:   r0 = logical colour (border = 4 << 28, pointer colours = 5,6,7 << 28)
+;       r1 = bit mask of which flash states to update (bit 0 set => 1st, bit 1 set => 2nd)
+;       r2 = BBGGRRS0
+;
+; out:  r0, r1, r2 preserved
+;       r3, r4 corrupted
+;
+
+UpdateSettingAndVIDC ALTENTRY
+
+        ;amg: changed to handle 4 bits of supremacy
+
+        MOV     r3, r2, LSR #8          ; r3 = 00bbggrr
+        ANDS    r2, r2, #&F0            ; r2 = 000000s0
+        ORRNE   r3, r3, r2, LSL #20     ; r3 = 0sbbggrr
+
+        MOV     r4, #0                  ; indicate no EORing between parts
+
+; ... and drop thru to
+
+; UpdateSettingCommon
+;
+; in:   r0 = logical colour (border = 4 << 28, pointer colours = 5,6,7 << 28)
+;       r1 = bit mask of which flash states to update (bit 0 set => 1st, bit 1 set => 2nd)
+;       r3 = 0sBBGGRR   (s in bits 24-27)
+;       r2, r5, r6, r7, lr stacked
+;
+; out:  r0, r1, r2, r4 preserved
+;       r3 corrupted
+;
+ [ GammaCorrection
+UpdateSettingCommon ROUT
+        PHPSEI                          ; protect against IRQs
+
+        Push    "r0, r8, r9"
+        MOV     r7, #VIDC
+
+        TST     r0, #&40000000          ; if border or pointer
+        ORRNE   r3, r3, r0              ; then merge with RGB
+        MOVNE   r0, r0, LSR #28         ; and make r0 into index for soft copy
+        ADDNE   r0, r0, #(256-4)        ; ie 256, 257, 258 or 259
+
+        ORREQ   r5, r0, #VIDCPalAddress ; else set up palette index register
+        STREQ   r5, [r7]
+
+        LDRB    r5, [WsPtr, #ScreenBlankFlag]
+        TEQ     r5, #0
+        MOVNE   r5, #&0FFFFFFF          ; bits to knock out if blanked (EBBGGRR)
+
+        LDROSB  r2, FlashState          ; 0 => second, 1 => first
+        CMP     r2, #1                  ; C=0 => second, C=1 => first
+
+        TST     r1, #1
+        BEQ     %FT10                   ; skip if not setting 1st colour
+        LDR     r2, [WsPtr, #FirPalAddr]
+        ADD     r8, r2, #(256+1+3)*4*4  ; r8 -> rgb transfer tables
+        STR     r3, [r2, r0, LSL #2]!   ; store in logical colour and write back pointer
+        AND     r6, r3, #&FF            ; r6 = red
+        LDRB    r6, [r8, r6]            ; r6 = gamma(red)
+        ADD     r8, r8, #&100           ; r8 -> green transfer
+        AND     r9, r3, #&FF00          ; r9 = green << 8
+        LDRB    r9, [r8, r9, LSR #8]    ; r9 = gamma(green)
+        ORR     r6, r6, r9, LSL #8      ; r6 = gamma(red) + (gamma(green)<<8)
+        ADD     r8, r8, #&100           ; r8 -> blue transfer
+        AND     r9, r3, #&FF0000        ; r9 = blue << 16
+        LDRB    r9, [r8, r9, LSR #16]   ; r9 = gamma(blue)
+        ORR     r6, r6, r9, LSL #16     ; r6 = gamma(red) + (gamma(green)<<8) + (gamma(blue)<<16)
+        AND     r9, r3, #&FF000000      ; knock out rgb from original
+        ORR     r6, r6, r9              ; and or in new bits
+        STR     r6, [r2, #(256+1+3)*4*2] ; store in physical copy
+
+        BICCS   r6, r6, r5              ; knock out bits for blanking
+        STRCS   r6, [r7]                ; poke VIDC if setting 1st colour and in 1st state
+10
+        EOR     r3, r3, r4              ; toggle requested bits for 2nd half
+        TST     r1, #2
+        BEQ     %FT20                   ; skip if not setting 2nd colour
+        LDR     r2, [WsPtr, #SecPalAddr]
+        ADD     r8, r2, #(256+1+3)*4*3  ; r8 -> rgb transfer tables
+        STR     r3, [r2, r0, LSL #2]!   ; store in logical copy and write back
+
+        AND     r6, r3, #&FF            ; r6 = red
+        LDRB    r6, [r8, r6]            ; r6 = gamma(red)
+        ADD     r8, r8, #&100           ; r8 -> green transfer
+        AND     r9, r3, #&FF00          ; r9 = green << 8
+        LDRB    r9, [r8, r9, LSR #8]    ; r9 = gamma(green)
+        ORR     r6, r6, r9, LSL #8      ; r6 = gamma(red) + (gamma(green)<<8)
+        ADD     r8, r8, #&100           ; r8 -> blue transfer
+        AND     r9, r3, #&FF0000        ; r9 = blue << 16
+        LDRB    r9, [r8, r9, LSR #16]   ; r9 = gamma(blue)
+        ORR     r6, r6, r9, LSL #16     ; r6 = gamma(red) + (gamma(green)<<8) + (gamma(blue)<<16)
+        AND     r9, r3, #&FF000000      ; knock out rgb from original
+        ORR     r6, r6, r9              ; and or in new bits
+        STR     r6, [r2, #(256+1+3)*4*2] ; store in physical copy
+
+        BICCC   r6, r6, r5              ; knock out bits for blanking
+        STRCC   r6, [r7]                ; poke VIDC if setting 2nd colour and in 2nd state
+20
+        PLP
+        Pull    "r0, r8, r9"
+        EXITS                           ; restore registers, claim vector
+ |
+UpdateSettingCommon ROUT
+        PHPSEI                          ; protect against IRQs
+
+        Push    "r0"
+        MOV     r7, #VIDC
+
+        TST     r0, #&40000000          ; if border or pointer
+        ORRNE   r3, r3, r0              ; then merge with RGB
+        MOVNE   r0, r0, LSR #28         ; and make r0 into index for soft copy
+        ADDNE   r0, r0, #(256-4)        ; ie 256, 257, 258 or 259
+
+        ORREQ   r5, r0, #VIDCPalAddress ; else set up palette index register
+        STREQ   r5, [r7]
+
+        LDRB    r5, [WsPtr, #ScreenBlankFlag]
+        TEQ     r5, #0
+        MOVNE   r5, #&0FFFFFFF          ; bits to knock out if blanked (EBBGGRR)
+
+        LDROSB  r2, FlashState          ; 0 => second, 1 => first
+        CMP     r2, #1                  ; C=0 => second, C=1 => first
+
+        TST     r1, #1
+        BEQ     %FT10                   ; skip if not setting 1st colour
+        LDR     r2, [WsPtr, #FirPalAddr]
+        STR     r3, [r2, r0, LSL #2]
+        BICCS   r6, r3, r5              ; knock out bits for blanking
+        STRCS   r6, [r7]                ; poke VIDC if setting 1st colour and in 1st state
+10
+        EOR     r3, r3, r4              ; toggle requested bits for 2nd half
+        TST     r1, #2
+        BEQ     %FT20                   ; skip if not setting 2nd colour
+        LDR     r2, [WsPtr, #SecPalAddr]
+        STR     r3, [r2, r0, LSL #2]
+        BICCC   r6, r3, r5              ; knock out bits for blanking
+        STRCC   r6, [r7]                ; poke VIDC if setting 2nd colour and in 2nd state
+20
+        PLP
+        Pull    "r0"
+        EXITS                           ; restore registers, claim vector
+ ]
+
+; *****************************************************************************
+;
+; PV_BulkRead - Read multiple palette entries with one call
+;
+; in:   R0 => list of colours wanted, or 0 to start with first and increment
+;       R1 =  b24-b31 - colour type: 16/17/18/24/25
+;             b00-b23 - number of colours to do
+;
+;       R2 => memory for first flash state colours (and second if R3=0)
+;       R3 => memory for second flash state colours (if 0, intermingle with R2 instead)
+;
+; out:  all preserved (R4 set to 0 to show call handled)
+
+; flags used to control routine
+
+PV_BR_WantFirst *       1               ; doing 16 or 17
+PV_BR_WantSecond *      2               ; doing 16 or 18
+PV_BR_HaveList *        4               ; we have a list of colours
+PV_BR_TwoLists *        8               ; we have two output areas (R2 & R3 valid)
+PV_BR_Border   *        16              ; doing 24
+PV_BR_Mouse    *        32              ; doing 25
+
+PV_BulkRead ROUT
+        Push    "R0-R3,R6-R11"             ; return addr already stacked
+
+        MOV     R6,R1,LSR #24           ; isolate the colour type
+
+        MOV     R7,#(PV_BR_WantFirst + PV_BR_WantSecond)
+
+        CMP     R6,#17                  ; do we want both flash states ?
+        BICEQ   R7,R7,#PV_BR_WantSecond ; if 17 only want first flash state
+
+        CMP     R6,#18
+        BICEQ   R7,R7,#PV_BR_WantFirst  ; if 18 only want second flash state
+
+        CMP     R6,#24
+        ORREQ   R7,R7,#PV_BR_Border
+        ORRGT   R7,R7,#PV_BR_Mouse
+
+        ;now set up other control flags
+        CMP     R0,#0
+        ORRNE   R7,R7,#PV_BR_HaveList   ; we have a list of colours
+
+        CMP     R3,#0
+        ORRNE   R7,R7,#PV_BR_TwoLists   ; we have two output areas
+
+        ;set up a mask for the number of colours
+        LDR     R8,[WsPtr,#DisplayNColour]
+        TEQ     R8,#63
+        MOVEQ   R8,#255                 ; deal with braindamaged 8BPP case
+
+        ;take the colour type off the top of R1, leaving #colours wanted
+        BIC     R1,R1,#&FF000000
+
+        ; register usage:
+        ; [R0]: colour list
+        ; R1: number of colours
+        ; [R2]: first flash state list
+        ; [R3]: second flash state list
+        ; R7: control flags
+        ; R8: mask for colour number
+        ; R9: loop counter
+        ; R10: misc
+        ; LR: misc
+
+        MOV     R9,#0
+30
+        TST     R7,#PV_BR_HaveList
+        LDRNE   LR,[R0],#4
+        MOVEQ   LR,R9                   ; LR = wanted colour number
+
+        AND     LR,LR,R8                ; ensure it is sensible
+
+        TST     R7,#PV_BR_Border
+        MOVNE   LR,#256                 ; border is stored as colour 256
+
+        TST     R7,#PV_BR_Mouse
+        BEQ     %FT40
+
+        ANDS    LR,LR,#3
+        TEQ     LR,#0
+        BEQ     %FT50                   ;colour 0 is invalid
+        ADD     LR,LR,#256              ;bring into range (257-259)
+
+40
+        TST     R7,#PV_BR_WantFirst
+
+        LDRNE   R10,[WsPtr,#FirPalAddr]
+
+        LDRNE   R10,[R10,LR,LSL#2]      ; xsbbggrr - could be 4 sup. bits
+        MOVNE   R11,R10,LSL #8          ; bbggrr00
+        ANDNE   R10,R10,#&0F000000      ; 0s000000
+        ORRNE   R11,R11,R10,LSR #20     ; bbggrrs0
+        STRNE   R11,[R2],#4
+
+        TST     R7,#PV_BR_WantSecond
+        BEQ     %FT60                   ; have to use a branch here - another TST coming up
+
+        LDR     R10,[WsPtr,#SecPalAddr]
+
+        LDR     R10,[R10,LR,LSL#2]      ; xsbbggrr
+        MOV     R11,R10,LSL #8          ; bbggrr00
+        ANDNE   R10,R10,#&0F000000      ; 0s000000
+        ORR     R11,R11,R10,LSR #20     ; bbggrrs0
+
+        TST     R7,#PV_BR_TwoLists
+
+        STREQ   R11,[R2],#4
+        STRNE   R11,[R3],#4
+
+60      ADD     R9,R9,#1
+        CMP     R9,R1
+        BCC     %BT30
+50
+        MOV     R4,#0
+        Pull    "R0-R3,R6-R11,PC"          ; return addr already stacked
+
+
+; *****************************************************************************
+;
+;       PV_ReadPalette - PaletteV read palette handler
+;
+; in:   R0 = logical colour
+;       R1 = 16 (read normal colour)
+;            24 (read border colour)
+;            25 (read cursor colour)
+;
+; out:  R2 = first flash setting   (BBGGRRS0), supremacy bit 7
+;       R3 = second flash setting  (BBGGRRS0), supremacy bit 7
+;
+
+PV_ReadPalette ROUT
+        Push    "r10,r11"
+        LDR     r10, [WsPtr, #DisplayNColour] ; logical colours in this mode -1
+        TEQ     r10, #63                ; if bodgy 256 colour mode
+        MOVEQ   r10, #255               ; then use AND mask of 255
+
+        TEQ     r1, #24                 ; is it reading border palette
+        MOVEQ   r11, #&100              ; then set up border index
+        BEQ     %FT10                   ; and go
+
+        TEQ     r1, #25                 ; is it reading pointer palette
+        BEQ     %FT05
+        AND     r11, r0, r10            ; no, then force into suitable range
+        B       %FT10                   ; always skip
+05
+        ANDS    r11, r0, #3             ; else force logical colour 0..3
+        BEQ     %FT99                   ; and 0 is illegal, so do nothing
+        ADD     r11, r11, #&100         ; set up correct index
+10
+
+; note no need to fudge 256-colour modes, since we have the correct full 256 entry palette
+
+        LDR     r10, [WsPtr, #FirPalAddr]
+        LDR     r10, [r10, r11, LSL #2] ; r10 := 1st XSBBGGRR
+        MOV     r2, r10, LSL #8         ; r2  := 1st BBGGRR00
+        TST     r10, #1 :SHL: 27
+        ORRNE   r2, r2, #&80            ; r2  := 1st BBGGRRS0
+
+        LDR     r10, [WsPtr, #SecPalAddr]
+        LDR     r10, [r10, r11, LSL #2] ; r10 := 2nd XSBBGGRR
+        MOV     r3, r10, LSL #8         ; r3  := 2nd BBGGRR00
+        TST     r10, #1 :SHL: 27
+        ORRNE   r3, r3, #&80            ; r3  := 2nd BBGGRRS0
+99
+        MOV     r4, #0
+        Pull    "r10, r11, pc"
+
+; *****************************************************************************
+;
+;       PV_1stFlashState - PaletteV routine to set first flash state
+;
+
+PV_1stFlashState ROUT
+        Push    "r0-r3"
+        LDR     r0, [WsPtr, #FirPalAddr]
+DoR0Flash
+        MOV     r1, #256                ; just update normal palette
+DoAllUpdate
+ [ GammaCorrection
+        ADD     r0, r0, #(256+1+3)*4*2  ; move pointer to physical palette copy
+ ]
+        ADD     r1, r0, r1, LSL #2
+        MOV     r2, #VIDC
+        MOV     r3, #VIDCPalAddress + 0 ; initialise palette address to 0
+        PHPSEI                          ; disable IRQs round this bit
+        STR     r3, [r2]
+
+        LDRB    r4, [WsPtr, #ScreenBlankFlag]
+        TEQ     r4, #0                  ; if unblanked, leave all bits alone
+        MOVNE   r4, #&0FFFFFFF          ; blanked, knock off all bits, except register bits
+10
+        LDR     r3, [r0], #4
+        BIC     r3, r3, r4
+        STR     r3, [r2]
+        TEQ     r0, r1
+        BNE     %BT10
+
+        PLP
+        MOV     r4, #0
+        Pull    "r0-r3, pc"
+
+; *****************************************************************************
+;
+;       PV_2ndFlashState - PaletteV routine to set second flash state
+;
+
+PV_2ndFlashState ROUT
+        Push    "r0-r3"
+        LDR     r0, [WsPtr, #SecPalAddr]
+        B       DoR0Flash
+
+; *****************************************************************************
+;
+;       UpdateAllPalette - Update all VIDC palette entries
+;
+
+UpdateAllPalette ENTRY "r0-r3"          ; "r0-r3,lr" stacked ready to branch to code
+        LDROSB  r0, FlashState
+        CMP     r0, #1
+        LDRCS   r0, [WsPtr, #FirPalAddr] ; FlashState = 1 => 1st state, 0 => 2nd state
+        LDRCC   r0, [WsPtr, #SecPalAddr]
+        MOV     r1, #260                ; update normal palette and border/pointer
+        B       DoAllUpdate
+
+; *****************************************************************************
+;
+;       PV_BlankScreen - Blank/unblank screen
+;
+; in:   R0 = -1 => read blank state
+;       R0 = 0 => unblank screen
+;       R0 = 1 => blank screen
+;
+; out:  R0 = old state (0=unblanked, 1=blanked)
+;       R4 = 0
+
+PV_BlankScreen ROUT
+        Push    "r1-r3"
+        LDRB    r3, [WsPtr, #ScreenBlankFlag]
+        CMP     r0, #1
+        BHI     %FT99
+
+        TEQ     r0, r3                  ; changing to same state? (carry preserved)
+        BEQ     %FT99                   ; if so, do nothing
+
+        STRB    r0, [WsPtr, #ScreenBlankFlag] ; update new state
+        MOV     r4, #VIDC
+        LDRB    r0, [WsPtr, #ScreenBlankDPMSState]
+        BCC     %FT50
+
+; blanking
+
+        TST     r0, #1                  ; if hsyncs should be off,
+        LDRNE   r1, =HorizSyncWidth + ((1:SHL:14) -1)   ; maximum value in h.sync width register
+        STRNE   r1, [r4]
+        TST     r0, #2                  ; if vsyncs should be off,
+        LDRNE   r1, =VertiSyncWidth + ((1:SHL:13) -1)   ; maximum value in v.sync width register
+        STRNE   r1, [r4]
+
+        MOV     r0, #(0 :SHL: 10) :OR: (3 :SHL: 8) ; blank:   video DMA off, continuous refresh
+        B       %FT60
+
+50
+
+; unblanking
+
+        TST     r0, #1                  ; if hsyncs were turned off,
+        LDRNE   r1, [WsPtr, #HSWRSoftCopy] ; then restore from soft copy
+        STRNE   r1, [r4]
+        TST     r0, #2                  ; if vsyncs were turned off,
+        LDRNE   r1, [WsPtr, #VSWRSoftCopy] ; then restore from soft copy
+        STRNE   r1, [r4]
+
+        MOV     r0, #(1 :SHL: 10) :OR: (0 :SHL: 8) ; unblank: video DMA on, no refresh
+60
+        MOV     r1, #(1 :SHL: 10) :OR: (3 :SHL: 8) ; bits to modify
+        SWI     XOS_UpdateMEMC
+
+        MOV     r0, pc
+        ORR     lr, r0, #I_bit          ; disable IRQs so we don't get a flash in the middle
+        TEQP    lr, #0
+        BL      UpdateAllPalette        ; update all palette, including border + pointer
+        TEQP    r0, #0                  ; restore old IRQ state
+99
+        MOV     r0, r3
+        MOV     r4, #0
+        Pull    "r1-r3, pc"
+
+ [ GammaCorrection
+
+; *****************************************************************************
+;
+;       PV_GammaCorrect - Update gamma correction tables
+;
+; in:   r0 -> red table
+;       r1 -> green table
+;       r2 -> blue table
+;
+; out:  r4 = 0
+
+PV_GammaCorrect ROUT
+        Push    "r0-r3,r5-r8"
+        LDR     r4, [WsPtr, #FirPalAddr]
+        ADD     r4, r4, #(256+1+3)*4*4          ; point to gamma tables
+        ADD     r3, r4, #256
+10
+        LDR     lr, [r0], #4
+        STR     lr, [r4], #4
+        TEQ     r4, r3
+        BNE     %BT10
+
+        ADD     r3, r4, #256
+20
+        LDR     lr, [r1], #4
+        STR     lr, [r4], #4
+        TEQ     r4, r3
+        BNE     %BT20
+
+        ADD     r3, r4, #256
+30
+        LDR     lr, [r2], #4
+        STR     lr, [r4], #4
+        TEQ     r4, r3
+        BNE     %BT30
+
+; now go through the logical palette, recomputing the physical from it using the new tables
+
+        SUB     r0, r4, #3*256                  ; r0 -> red table
+        SUB     r1, r4, #2*256                  ; r1 -> green table
+        SUB     r2, r4, #1*256                  ; r2 -> blue table
+
+        LDR     r4, [WsPtr, #FirPalAddr]        ; r4 -> start of logical palette
+        ADD     r5, r4, #260*4*2                ; r5 -> start of physical palette
+        MOV     r6, r5                          ; r6 = r5 = end of logical palette
+40
+        LDR     r7, [r4], #4                    ; get word
+        AND     r8, r7, #&FF                    ; r8 = red
+        LDRB    r8, [r0, r8]                    ; r8 = gamma(red)
+        AND     lr, r7, #&FF00                  ; lr = green << 8
+        LDRB    lr, [r1, lr, LSR #8]            ; lr = gamma(green)
+        ORR     r8, r8, lr, LSL #8              ; r8 = gamma(red) + (gamma(green)<<8)
+        AND     lr, r7, #&FF0000                ; lr = blue << 16
+        LDRB    lr, [r2, lr, LSR #16]           ; lr = gamma(blue)
+        ORR     r8, r8, lr, LSL #16             ; r8 = gamma(red) + (gamma(green)<<8) + (gamma(blue)<<16)
+        AND     lr, r7, #&FF000000              ; lr = other bits
+        ORR     r8, r8, lr                      ; r8 = gamma-corrected combined value
+        STR     r8, [r5], #4                    ; store word
+        TEQ     r4, r6
+        BNE     %BT40
+
+        BL      UpdateAllPalette
+
+        MOV     r4, #0
+        Pull    "r0-r3,r5-r8, pc"
+ ]
+        END
diff --git a/s/vdu/vdupalette b/s/vdu/vdupalette
new file mode 100644
index 0000000000000000000000000000000000000000..303db429a42195333ecfe81b5379f8c55c6be6e9
--- /dev/null
+++ b/s/vdu/vdupalette
@@ -0,0 +1,227 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > VduPalette
+;
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Vdu driver code - Palette setting (VIDC independent parts)
+;
+; *****************************************************************************
+
+; Routines above PaletteV
+; -----------------------
+
+; *****************************************************************************
+;
+;       PalInit - Restore default palette for the displayed mode
+;
+; out:  R6 is preserved (may contain CursorFlags)
+;
+
+PalInit ROUT
+        MOV     r4, #paletteV_SetDefaultPalette
+        B       CallPaletteV
+
+; *****************************************************************************
+;
+;       UnblankScreen - Unblank screen (called on mode change)
+;
+
+UnblankScreen ROUT
+        MOV     r0, #0
+        MOV     r4, #paletteV_BlankScreen
+        B       CallPaletteV
+
+; *****************************************************************************
+;
+;       DoSetPalette - Entry point for OSWORD 12 (program palette)
+;
+; in:   R1 -> Control block
+;       [R1, #0] = logical colour
+;       [R1, #1] = physical colour or action
+;       [R1, #2] = red
+;       [R1, #3] = green
+;       [R1, #4] = blue
+;
+;       R12 -> VDUWS
+;
+;       R0-R4, R11, R12 may be corrupted
+;
+;
+DoSetPalette
+        Push    "R5-R10,R14"
+        BL      SetPal
+        Pull    "R5-R10,PC"
+
+; *****************************************************************************
+;
+;       DC3 - Entry point for VDU 19 (program palette)
+;
+; in:   QQ+0..QQ+4 contain the parameters in the same format as OSWORD
+;
+
+DC3     ROUT
+        ADD     R1, WsPtr, #QQ          ; Point R1 at the queued data
+                                        ; and drop thru to Palette OSWORD code
+SetPal  ENTRY
+        LDR     R6, [WsPtr, #DisplayModeFlags]
+        TST     R6, #Flag_Teletext      ; if teletext mode
+        EXITS   NE                      ; then ignore VDU 19
+
+; Now we must check for BBC style colours (VDU 19,n,0..15,...) and map these onto
+; 16 or (17 and 18)
+
+        MOV     lr, r1
+        LDRB    r0, [lr, #0]            ; r0 = logical colour
+        LDRB    r1, [lr, #1]
+        AND     r2, r1, #&80            ; r2 = sup bit
+        BIC     r1, r1, #&80            ; r1 = type of colour
+        CMP     r1, #16                 ; r1 < 16 => BBC style colour
+        BCS     %FT10
+
+        TST     r1, #1                  ; bit 0 set => red full on
+        ORRNE   r2, r2, #&0000FF00
+        TST     r1, #2                  ; bit 1 set => green full on
+        ORRNE   r2, r2, #&00FF0000
+        TST     r1, #4                  ; bit 2 set => blue full on
+        ORRNE   r2, r2, #&FF000000
+
+        LDRB    lr, [WsPtr, #PalIndex]
+        CMP     lr, #3                  ; only flash colours if PalIndex = 0, 1 or 2
+        BICCS   r1, r1, #8
+
+        TST     r1, #8
+        MOVEQ   r1, #16
+        BEQ     %FT20                   ; not a flashing colour, so just set it once
+
+        MOV     r1, #17                 ; set first flash colour
+        BL      CallSetPalette
+        MVN     r2, r2                  ; then toggle all bits of R, G and B
+        EOR     r2, r2, #&FF            ; (don't toggle supremacy)
+        MOV     r1, #18                 ; set second flash colour
+        B       %FT20
+
+10
+        LDRB    r3, [lr, #2]            ; r3 = red
+        ORR     r2, r2, r3, LSL #8      ; r2 = &0000RRS0
+        LDRB    r3, [lr, #3]            ; r3 = green
+        ORR     r2, r2, r3, LSL #16     ; r2 = &00GGRRS0
+        LDRB    r3, [lr, #4]            ; r3 = blue
+        ORR     r2, r2, r3, LSL #24     ; r2 = &BBGGRRS0
+20
+        BL      CallSetPalette
+        EXITS
+
+
+CallSetPalette
+        MOV     r4, #paletteV_Set
+CallPaletteV
+        Push    "r9,lr"
+        MOV     r9, #PaletteV
+        SWI     XOS_CallAVector
+        Pull    "r9,pc",,^
+
+; *****************************************************************************
+;
+;       SWIReadPalette - SWI ReadPalette handler
+;
+; in:   R0 = logical colour
+;       R1 = 16 (read normal colour)
+;            24 (read border colour)
+;            25 (read cursor colour)
+;
+; out:  R2 = first flash setting   (B0G0R0PP), supremacy bit 7
+;       R3 = second flash setting  (B0G0R0PP), supremacy bit 7
+;       R10-R12 corrupted (preserved by Sam), all others preserved
+;
+
+SWIReadPalette ENTRY "r4"
+        VDWS    WsPtr
+        MOV     r4, #paletteV_Read
+        BL      CallPaletteV
+        BIC     r2, r2, #&7F            ; knock out any weird bits
+        BIC     r3, r3, #&7F
+        ORR     r2, r2, r1              ; put in nominal PP field
+        ORR     r3, r3, r1
+        TEQ     r1, #16                 ; if reading normal colour
+        BNE     %FT10
+        TEQ     r2, r3                  ; and colours different
+        ORRNE   r2, r2, #1              ; then make 1st PP field 17
+        ORRNE   r3, r3, #2              ; and 2nd PP field 18
+10
+        PullEnv
+        ExitSWIHandler
+
+; *****************************************************************************
+;
+;       DoReadPalette - Entry point from OSWORD 11 (read palette)
+;
+; in:   R1 -> control block
+;       [R1, #0] = logical colour to read
+;
+; out:  [R1, #1] (bits 0..6) = physical colour 0..15 or 16; (bit 7) = supremacy
+;       [R1, #2] = red   (in bits 4..7)
+;       [R1, #3] = green (-----""-----)
+;       [R1, #4] = blue  (-----""-----)
+;       R0-R4, R11, R12 can be corrupted
+;
+
+DoReadPalette ENTRY
+        MOV     r4, r1                  ; save pointer to block
+        LDRB    r0, [r4]                ; r0 = logical colour
+        MOV     r1, #16
+        SWI     XOS_ReadPalette
+        LDROSB  r0, FlashState
+        CMP     r0, #1                  ; CS => 1st state, CC => 2nd state
+        MOVCC   r2, r3                  ; r2 = current state
+        MOV     r1, #4
+10
+        STRB    r2, [r4, #1]!           ; store 4 bytes of data in block, starting R1+1
+        MOV     r2, r2, LSR #8
+        SUBS    r1, r1, #1
+        BNE     %BT10
+        EXIT
+
+; *****************************************************************************
+;
+;       DoFirstFlash - Set palette to first palette setting
+;                      Called in either SVC or IRQ mode
+;
+
+DoFirstFlash ENTRY "r0,r4"
+        MOV     r4, #paletteV_1stFlashState
+SetFlashState
+        MOV     r0, pc
+        TEQP    pc, #SVC_mode + I_bit
+        NOP
+        Push    "lr"
+        BL      CallPaletteV
+        Pull    "lr"
+        TEQP    r0, #0
+        NOP
+        EXITS
+
+; *****************************************************************************
+;
+;       DoSecondFlash - Set palette to second palette setting
+;                       Called in either SVC or IRQ mode
+;
+
+DoSecondFlash ALTENTRY
+        MOV     r4, #paletteV_2ndFlashState
+        B       SetFlashState
+
+        END
diff --git a/s/vdu/vduplot b/s/vdu/vduplot
new file mode 100644
index 0000000000000000000000000000000000000000..1a765a322fd7945868f173fae317cf2278c65577
--- /dev/null
+++ b/s/vdu/vduplot
@@ -0,0 +1,1506 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduPlot
+;
+; ARTHUR OPERATING SYSTEM - Vdu Drivers
+; =======================
+;
+; Vdu graphics code - Entry point for plotting & low level primitives
+;                     & Ecf pattern setting
+; Author R C Manby
+; Date   5.9.86
+;
+
+; *****************************************************************************
+;
+; EM
+; PLOT - Graphics operation, eg triangle, line etc.
+; ====
+;
+; The plot operation, and the co-ordinate to plot at given by bytes
+;  in the vdu queue, as follows
+;  QQ+0 - plot number
+;  QQ+1 - xLo
+;  QQ+2 - xHi
+;  QQ+3 - yLo
+;  QQ+4 - yHi
+;
+; The co-ordinate is in external 16 bit form.
+; If 2 or 3 are required, they are taken from ICursor & OldCs
+;
+; Also, both PLOT and EntryFromSWIPlot are entered with R6 = CursorFlags
+;
+EM
+PLOT    ROUT
+      [ {TRUE}
+        GraphicsMode R0                 ; Quit if not a graphics mode
+        MOVNE   PC, Link
+      |
+        LDR     R0, [WsPtr, #NPix]
+        CMP     R0, #0                  ; Quit if not a graphics mode
+        MOVEQ   PC, Link
+      ]
+
+        ASSERT  ((QQ+1):AND:3)=0
+        LDR     R2, [WsPtr, #QQ+1]      ; R2 = xlo,xhi,ylo,yhi
+        MOV     R0, R2, LSL #16         ; R0 = 0  ,0  ,xlo,xhi
+        MOV     R0, R0, ASR #16         ; R0 = xlo,xhi,sgn,sgn
+        MOV     R1, R2, ASR #16         ; R1 = ylo,yhi,sgn,sgn
+
+        LDRB    R2, [WsPtr, #QQ+0]      ; plot mode
+        MOV     R9, #0                  ; indicate coming from VDU Plot
+
+EntryFromSWIPlot
+        SaveRetAdr
+
+; now convert to internal coords (modified EIG code)
+
+        ASSERT  OrgY = OrgX +4
+        ASSERT  GCsX = OrgX +8
+        ASSERT  GCsY = OrgX +12
+        ASSERT  YEigFactor = XEigFactor +4
+
+        ADD     R3, WsPtr, #OrgX
+
+        LDMIA   R3!, {R4-R5}            ; R4 = OrgX: R5 = OrgY
+        TST     R2, #4                  ; If bit2 is clear, then relative
+        LDMEQIA R3, {R7-R8}             ; so load old cursor position
+        ADDEQ   R0, R0, R7              ; and add
+        ADDEQ   R1, R1, R8
+        STMIA   R3, {R0-R1}             ; store new coords in GCsX,GCsY
+
+        ADD     R3, WsPtr, #XEigFactor
+        LDMIA   R3, {R7-R8}             ; R7 = XEigFactor: R8 = YEigFactor
+        ADD     R0, R0, R4              ; add on origin
+        ADD     R1, R1, R5
+        MOV     R0, R0, ASR R7          ; and shift down
+        MOV     R1, R1, ASR R8
+
+        ADD     R3, WsPtr, #NewPtX
+        STMIA   R3, {R0-R1}
+
+;
+; The 2 LSBits of the plot code specify fg/bg colour and action as :-
+;  0 No effect eqv. of Gcol(5,c)
+;  1 Foreground colour using foreground Gcol action
+;  2 Invert eqv. of Gcol(4,c)
+;  3 Background colour using background Gcol action
+;
+
+; *****Change made by DJS
+;   Original code was:
+;        MOV     R3, R2, LSL #30         ; put bottom 2 bits into top 2 bits
+;        CMP     R3, #&40000000          ; set lots of flags
+;
+;        ADDMI   R4, WsPtr, #BgEcfOraEor ; if 0 or 3
+;        ADRCC   R4, NoEffect            ; if 0
+;        ADDEQ   R4, WsPtr, #FgEcfOraEor ; if 1
+;        ADRVS   R4, Invert              ; if 2
+
+        MOVS    R3, R2, LSL #31         ;Put bit 1 in C, NOT(bit 0) in Z
+        ADR     R4, NoEffect            ; if 0, 1, 2 or 3
+        ADDNE   R4, WsPtr, #FgEcfOraEor ; if 1 or 3
+        ADRCS   R4, Invert              ; if 2 or 3
+        ADDHI   R4, WsPtr, #BgEcfOraEor ; if CS & NE - i.e. 3
+
+; *****End of change made by DJS
+
+        STR     R4, [WsPtr, #GColAdr]   ; save address of Ecf to plot with
+
+        TST     R6, #ClipBoxEnableBit
+        BLNE    DoPlotClipBox
+
+        ADR     R14, CTidy + SVC_mode   ; set up return address
+                                        ; R0=X, R1=Y, R2=plot code
+        ADD     PC, PC, R2, LSR #1      ; jump to branch (bottom 2 bits of the
+                                        ; result are ignored!)
+        &       0                       ; dummy word
+
+ B LineDrawSolid           ;   0 - Solid line
+ B LineDrawSolid           ;   8 - Solid line, endpoint omitted
+ B LineDrawDotted          ;  16 - Dot-dash line, restart pattern
+ B LineDrawDotted          ;  24 - Dot-dash line, restart pattern,
+                           ;        endpoint omitted
+ B LineDrawSolid           ;  32 - Solid extension line
+ B LineDrawSolid           ;  40 - Solid extension line, endpoint omitted
+ B LineDrawDotted          ;  48 - Dot-dash extension line, continue pattern
+ B LineDrawDotted          ;  56 - Dot-dash extension line, continue pattern,
+                           ;        endpoint omitted
+ B PlotPoint               ;  64 - Point plot
+ B FillLRnonBg             ;  72 - Line fill L&R, upto non-background
+ B TriangleFill            ;  80 - Triangle fill
+ B FillLRtoBg              ;  88 - Fill right, upto background
+ B RectangleFill           ;  96 - Rectangle fill
+ B FillLRtoFg              ; 104 - Line fill L&R, upto foreground
+ B ParallelogramFill       ; 112 - Parallelogram Fill
+ B FillLRnonFg             ; 120 - Fill right, upto non-foreground
+ B FloodNonBg              ; 128 - Flood to non-bg (ie over bg)
+ B FloodToFg               ; 136 - Flood to fg (ie over non-fg)
+ B CircleOutline           ; 144 - Circle outline
+ B CircleFill              ; 152 - Circle fill
+ B CircleArc               ; 160 - Circular arc outline
+ B SegmentFill             ; 168 - Segment fill
+ B SectorFill              ; 176 - Sector (pie) fill
+ B BlockCopyMove           ; 184 - Block copy/move
+ B EllipseOutline          ; 192 - Ellipse outline
+ B EllipseFill             ; 200 - Ellipse fill
+ B   NYA                   ; 208 - Unassigned
+ B   NYA                   ; 216 - Unassigned
+ B   NYA                   ; 224 - Unassigned
+ B SpritePlot              ; 232 - Sprite plot
+ B   NYA                   ; 240 - Assigned to applications
+ B   NYA                   ; 248 - Assigned to applications
+
+CTidy                           ; The "normal" return point after a plot
+                                ; operation, any call that does not want
+                                ; the cursors moving should pull return
+                                ; address off the stack
+
+                                ; Shuffle the cursors along
+                                ;  NewPt -> ICursor -> OldCs -> OlderCs
+                                ;
+                                ; ECursor was set earlier by EIG
+
+        ADD     R0, WsPtr, #OldCsX
+        LDMIA   R0, {R1,R2, R3,R4, R5,R6} ; OldCs(X,Y) ICursor(X,Y) NewPt(X,Y)
+        ADD     R0, WsPtr, #OlderCsX
+        STMIA   R0, {R1,R2, R3,R4, R5,R6}
+        Return
+
+NYA
+        MOV     R0, R2                  ; R0 := plot code
+        MOV     R10, #UKPLOTV
+        Push    "R9, WsPtr, R14"        ; save SWIPlot indicator, WsPtr + link
+        BL      VduQQVec
+        Pull    "R9, WsPtr, PC", VC     ; return to CTidy if no error
+
+; error in UKPLOTV
+
+        Pull    "R9, WsPtr"     ; restore SWIPlot indicator and WsPtr
+        ADD     R13, R13, #8    ; throw away return to CTidy
+                                ; and return address stacked by PLOT
+        TEQ     R9, #0          ; called from Wrch ?
+        BEQ     VduBadExit      ; yes, then go to error exit code
+        B       SWIPlotBadExit  ; no, then go to SWIPlot error exit
+
+VduQQVec
+        ADD     R1, WsPtr, #QQ
+VduGoVec
+        CallAVector
+
+
+
+;
+; Words for forming ZGORA & ZGEOR for colour actions 0 to 7, where
+;  0=Store, 1=OR, 2=AND, 3=EOR, 4=Inverse, 5=No change,
+;  6=And Not(colour) ie BIC,   7=Or Not(colour)
+;
+;
+; The values correspond to TBzgoo etc on the 6502 and are stored
+; in the following order zgoo,zgeo,zgoe,zgee to allow LDM to be used
+;
+;                         Action
+;                       0 1 2 3 4 5 6 7
+; TBzgoo  OR the  OR :- F,0,0,F,F,F,0,0
+; TBzgeo EOR the  OR :- 0,0,F,F,F,F,0,F
+; TBzgoe  OR the EOR :- 0,F,0,0,F,F,0,F
+; TBzgee EOR the EOR :- F,F,F,0,0,F,0,F
+;
+
+TBscrmasks      *       2_10010111000011111110110001010011
+
+        [ {FALSE}
+TBscrmask
+ & &FFFFFFFF    ;Store colour to screen  ( OR the  OR)
+ & &00000000    ;                        (EOR the  OR)
+ & &00000000    ;                        ( OR the EOR)
+ & &FFFFFFFF    ;                        (EOR the EOR)
+
+ & &00000000    ;OR colour to screen
+ & &00000000
+ & &FFFFFFFF
+ & &FFFFFFFF
+
+ & &00000000    ;AND
+ & &FFFFFFFF
+ & &00000000
+ & &FFFFFFFF
+
+ & &FFFFFFFF    ;EOR
+ & &FFFFFFFF
+ & &00000000
+ & &00000000
+
+ & &FFFFFFFF    ;Invert
+ & &FFFFFFFF
+ & &FFFFFFFF
+ & &00000000
+
+ & &FFFFFFFF    ;No change
+ & &FFFFFFFF
+ & &FFFFFFFF
+ & &FFFFFFFF
+
+ & &00000000    ;BIC (ie AND NOTcol)
+ & &00000000
+ & &00000000
+ & &00000000
+
+ & &00000000    ;OR NOTcol
+ & &FFFFFFFF
+ & &FFFFFFFF
+ & &FFFFFFFF
+        ]
+
+        MALIGN  64      ; Invert and NoEffect must be aligned to a multiple
+                        ; of 64 for line drawing routines (TMD)
+
+                        ;Interleaved zgora & zgeor values to give invert
+                        ; and no effect plotting, used when plot code
+Invert                  ; overrides Fg/Bg Gcol colour and action.
+ & &00000000
+ & &FFFFFFFF
+ & &00000000
+ & &FFFFFFFF
+ & &00000000
+ & &FFFFFFFF
+ & &00000000
+ & &FFFFFFFF
+ & &00000000
+ & &FFFFFFFF
+ & &00000000
+ & &FFFFFFFF
+ & &00000000
+ & &FFFFFFFF
+ & &00000000
+ & &FFFFFFFF
+NoEffect
+ & &00000000
+ & &00000000
+ & &00000000
+ & &00000000
+ & &00000000
+ & &00000000
+ & &00000000
+ & &00000000
+ & &00000000
+ & &00000000
+ & &00000000
+ & &00000000
+ & &00000000
+ & &00000000
+ & &00000000
+ & &00000000
+
+; *****************************************************************************
+;
+; EIG - External to internal graphic coordinate conversion
+; ===
+;
+; Convert external coordinates (either relative or absolute) into
+; internal ones. No windowing is done.
+;
+;
+; On entry, R0 (X), R1 (Y) hold coordinate (external co-ords)
+;           R2 holds plot mode, where bit2 = 0 for relative (add ECursor)
+;                                          = 1 for absolute (add origin)
+;
+; On exit,  R0 (X), R1 (Y) hold internal representation
+;           R2 preserved
+;
+;           R3 corrupt
+;
+;           The external cursor (GCsX,GCsY) is updated to the new point,
+;           hence in triangle relative mode, the points are relative to
+;           the last point specified, not the original.
+;
+EIG
+                                ;Do Ycoord first
+        TST R2,#4               ;If bit2 is clear
+        LDREQ R3,[WsPtr,#GCsY]  ;then co-ord is relative
+        ADDEQ R1,R1,R3          ;     so add previous cursor (ext. rep.)
+
+        STR R1,[WsPtr,#GCsY]    ;Update previous cursor
+        LDR R3,[WsPtr,#OrgY]    ;Add origin
+        ADD R1,R1,R3
+
+        LDR R3, [WsPtr, #YEigFactor]
+        MOV R1,R1,ASR R3        ;Transform 0-1023 to 0-255 or 0-511
+
+                                ;Do Xcoord
+        LDREQ R3,[WsPtr,#GCsX]  ;If relative then
+        ADDEQ R0,R0,R3          ;  add previous cursor (ext. rep.)
+
+        STR R0,[WsPtr,#GCsX]    ;Update previous cursor
+        LDR R3,[WsPtr,#OrgX]    ;Add origin
+        ADD R0,R0,R3
+
+        LDR R3,[WsPtr,#XEigFactor]
+        MOV R0,R0,ASR R3        ;Transform 0-1279 to 0-639/0-319/0-159
+
+        MOVS PC,R14
+
+; *****************************************************************************
+;
+; IEG - Inverse of EIG. Convert ICursor to ECursor
+; ===
+;
+; On exit, R0 (X), R1 (Y) holds ECursor, ECursor updated
+;       R2, R3 hold XEigFactor, YEigFactor
+;       R4, R5 corrupt
+;
+        ASSERT  GCsIY = GCsIX +4
+        ASSERT  YEigFactor = XEigFactor +4
+        ASSERT  OrgY = OrgX +4
+        ASSERT  GCsY = GCsX +4
+IEG
+        ADD     R0, WsPtr, #GCsIX
+        LDMIA   R0, {R0,R1}             ; load graphics cursor (internal)
+IEGB
+        ADD     R2, WsPtr, #XEigFactor
+        LDMIA   R2, {R2, R3}            ; R2 = XEigFactor; R3 = YEigFactor
+        ADD     R4, WsPtr, #OrgX
+        LDMIA   R4, {R4, R5}            ; R4 = OrgX; R5 = OrgY
+        RSB     R0, R4, R0, LSL R2      ; R0 = (X << XEigFactor)-OrgX
+        RSB     R1, R5, R1, LSL R3      ; R1 = (Y << YEigFactor)-OrgY
+        ADD     R4, WsPtr, #GCsX
+        STMIA   R4, {R0,R1}             ; write graphics cursor (external)
+
+        MOVS PC,R14
+
+        [ {FALSE}
+; *****************************************************************************
+;
+; Window - Check a coordinate against the graphics window
+; ======
+;
+; On entry, R0 (X), R1 (Y) holds coordinate to window
+; On exit,  R0,R1 preserved,
+;           R3 corrupt
+;           R2 holds result, as follows:
+;
+;                |      |
+;           1001 | 1000 | 1010
+;                |      |
+;           -----+------+-----
+;                |      |
+;           0001 | 0000 | 0010
+;                |      |
+;           -----+------+-----
+;                |      |
+;           0101 | 0100 | 0110
+;                |      |
+;
+;
+;
+Window
+        MOV R2,#0
+        LDR R3,[WsPtr,#GWBRow]          ;Test ycoord against window
+        CMP R1,R3
+        ORRLT R2,R2,#4                  ;Set bit 2 if Y < window
+
+        LDR R3,[WsPtr,#GWTRow]
+        CMP R3,R1
+        ORRLT R2,R2,#8                  ;Set bit 3 if Y > window
+
+        LDR R3,[WsPtr,#GWLCol]          ;Test xcoord against window
+        CMP R0,R3
+        ORRLT R2,R2,#1                  ;Set bit 0 if X < window
+
+        LDR R3,[WsPtr,#GWRCol]
+        CMP R3,R0
+        ORRLT R2,R2,#2                  ;Set bit 1 if X > window
+
+        MOVS PC,R14                     ;Return to whence we came
+
+        ]
+; *****************************************************************************
+;
+; ScreenAddr - Generate screen address of coordinate
+; ==========
+;
+; On entry, R0 (X), R1 (Y) holds coordinate - must be within graphics window
+; On exit,  R0,R1 preserved,
+;           R2 holds word address
+;           R3 holds pixel mask
+;
+;           R7,R8 corrupt
+ScreenAddr
+        ASSERT  LineLength = YWindLimit +4
+
+        ADD     R7, WsPtr, #YWindLimit
+        LDMIA   R7, {R7,R8}                     ; R7=YWindLimit,R8=LineLength
+        SUB     R2, R7, R1                      ; flip ycoord into R2
+
+        LDR     R7, [WsPtr, #ScreenStart]       ; add the screen start
+        MLA     R2, R8, R2, R7                  ; to Ycoord * bytes per row
+
+        LDR     R3, [WsPtr, #XShftFactor]       ; R7 := 2,3,4 or 5
+        MOV     R8, R0, ASR R3                  ; R8 := word offset
+        ADD     R2, R2, R8, LSL #2              ; add on to screen address
+
+        EOR     R7, R0, R8, LSL R3              ; R7 := pixel offset
+        ADD     R3, WsPtr, #RAMMaskTb
+        LDR     R3, [R3, R7, LSL #2]            ; R3 := mask for this pixel
+
+        MOVS    PC, R14                         ; and return
+
+; *****************************************************************************
+;
+; PlotPoint - Plot a point in the current colour
+; =========
+;
+; On entry, R0 (X), R1 (Y) holds coordinate of point
+; On exit,  R0,R1 preserved
+;           R2-R8 corrupt
+;           R9-R11 preserved
+;
+
+PlotPoint
+        WINDow  R0,R1, R2,R3,R4,R5
+        MOVLTS  PC, R14                         ; If outside window, give up
+
+        ASSERT  LineLength = YWindLimit +4
+
+        ADD     R7, WsPtr, #YWindLimit
+        LDMIA   R7, {R7,R8}                     ; R7=YWindLimit; R8=LineLength
+        SUB     R2, R7, R1                      ; flip ycoord into R2
+
+; *****Change made by DJS
+; Original code was:
+;        ASSERT  GColAdr = XShftFactor +4
+;        ASSERT  ScreenStart = XShftFactor +8
+;
+;        ADD     R4, WsPtr, #XShftFactor         ; R4=XShftFactor; R5=GColAdr
+;        LDMIA   R4, {R4, R5, R7}                ; R7=ScreenStart
+;
+;        MLA     R2, R8, R2, R7                  ; R2=ScreenStart+Y*LineLength
+;        MOV     R8, R0, ASR R4                  ; R8 := XCoord DIV 4,8,16
+;                                                ;   or 32
+;        EOR     R7, R0, R8, LSL R4              ; R7 := pixel offset in word
+;        ADD     R3, WsPtr, #RAMMaskTb
+;        LDR     R3, [R3, R7, LSL #2]            ; R3 := mask for this pixel
+;
+;        EOR     R6, R1, #7                      ; flip Ycoord
+;        AND     R6, R6, #7                      ; line within ecf
+; This does not calculate the ecf line correctly, resulting in bugs if (e.g.)
+; a destination sprite is not a multiple of 8 pixels high.
+
+        AND     R6, R2, #7                      ; R6 := line with ecf
+
+        ASSERT  GColAdr = XShftFactor +4
+        ASSERT  ScreenStart = XShftFactor +8
+
+        ADD     R4, WsPtr, #XShftFactor         ; R4=XShftFactor; R5=GColAdr
+        LDMIA   R4, {R4, R5, R7}                ; R7=ScreenStart
+
+        MLA     R2, R8, R2, R7                  ; R2=ScreenStart+Y*LineLength
+        MOV     R8, R0, ASR R4                  ; R8 := XCoord DIV 4,8,16 or 32
+        EOR     R7, R0, R8, LSL R4              ; R7 := pixel offset in word
+        ADD     R3, WsPtr, #RAMMaskTb
+        LDR     R3, [R3, R7, LSL #2]            ; R3 := mask for this pixel
+
+; *****End of change made by DJS
+
+        ADD     R5, R5, R6, LSL #3
+        LDMIA   R5, {R5, R6}                    ; get zgora,zgeor
+
+        AND     R5, R5, R3                      ; mask zgora
+        AND     R6, R6, R3                      ; & zgeor for this pixel
+        LDR     R7, [R2, R8, LSL #2]!           ;
+        ORR     R7, R7, R5                      ; and hit the screen
+        EOR     R7, R7, R6
+        STR     R7, [R2]
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+; ComplexEcfPattern
+; =================
+;
+; On entry, R0 holds pattern number, where 2..6 means Ecf1..4
+;
+; Vdu queue holds
+;
+;          QQ+0  pattern number
+;          QQ+1  pattern bytes 0
+;           |                  |
+;          QQ+8                7
+;
+; ComplexEcfPat10 is an entry point from simple Ecf setting for 8 bpp modes
+;                 ie simple=complex in 8 bpp modes
+;                 R1 points to Ecf(n) to be programmed.
+;
+; Corrupts R0..R3
+;
+
+ComplexEcfPattern ROUT                  ; R0 holds 2,3,4,5 for Ecfs 1,2,3,4
+        ADD     R1, WsPtr, #(Ecf1-2*8)
+        ADD     R1, R1, R0, LSL #3      ; point R1 at Ecf(n)
+ComplexEcfPat10
+        ADD     R0, WsPtr, #(QQ+1)
+        LDR     R2, [WsPtr, #BBCcompatibleECFs] ; if in BBC mode
+        CMP     R2, #0
+        BEQ     ComplexEcfPat20                 ; then unmangle the interleaved
+                                                ;      pixels
+        LDMIA   R0, {R2,R3}                     ; else (native) use as given
+        STMIA   R1, {R2,R3}
+        B       SetColour       ; update FgEcf & BgEcf incase they use this Ecf
+
+; R1 points at Ecf(n)
+; R0 points at QQ+1
+;
+; Uses R2 - pointer into InterleaveTB(BitsPerPix)
+;      R3 - byte to process 7..0
+;      R4 - #1
+;      R5 - bit within byte to process 7..0
+;      R6 - result byte
+;      R7 - byte from queue
+;      R8 - bit mask from InterleaveTB(BitsPerPix,R5)
+
+ComplexEcfPat20
+        ADR     R2, InterleaveTB
+        LDR     R3, [WsPtr, #Log2BPP]   ; 0,1,2,3 means 1,2,4,8 bits per pixel
+        ADD     R2, R2, R3, LSL #3      ; point R2 at entry in interleave table
+        MOV     R3, #7                  ; 7..0 bytes to process
+        MOV     R4, #1
+ComplexEcfPat30
+        MOV     R5, #7                  ; 7..0 bits per byte
+        MOV     R6, #0                  ; clear result byte
+        LDRB    R7, [R0, R3]            ; byte from queue
+ComplexEcfPat40
+        LDRB    R8, [R2, R5]
+        TST     R8, R7
+        ORRNE   R6, R6, R4, LSL R5      ; set bit in result byte
+        SUBS    R5, R5, #1
+        BGE     ComplexEcfPat40         ; process next bit
+
+        STRB    R6, [R1, R3]            ; write de-interleaved byte to Ecf(n)
+        SUBS    R3, R3, #1
+        BGE     ComplexEcfPat30         ; process next byte from queue
+
+        B       SetColour       ; update FgEcf & BgEcf incase they use this Ecf
+
+;
+; InterleaveTB - Values used to unpack BBC style interleaved pixels
+;
+InterleaveTB
+; 1 bit  per pixel eg Mode 0 & 4 - Log2BPP is 0
+        =       &80, &40, &20, &10, &08, &04, &02, &01
+; 2 bits per pixel eg Mode 1 & 5 - Log2BPP is 1
+        =       &08, &80, &04, &40, &02, &20, &01, &10
+; 4 bits per pixel eg Mode 2 - Log2BPP is 2
+        =       &02, &08, &20, &80, &01, &04, &10, &40
+; 8 bits per pixel - no effect - Log2BPP is 3
+        =       &01, &02, &04, &08, &10, &20, &40, &80
+
+        ALIGN
+
+; *****************************************************************************
+;
+; LineStyle - Setup dotted line style (does not affect repeat length)
+; =========
+;
+; Corrupts R0..R3
+;
+
+LineStyle ROUT
+        MOV     R3, #8                          ; Copy 8 bytes in reverse order
+        ADD     R1, WsPtr, #QQ+9
+        ADD     R2, WsPtr, #DotLineStyle        ; from Queue into DotLineStyle
+LineSty10
+        LDRB    R0, [R1, #-1]!
+        STRB    R0, [R2], #1
+        SUBS    R3, R3, #1
+        BNE     LineSty10
+        MOVS    PC, R14
+
+        LTORG   ;limited offsets
+
+; *****************************************************************************
+;
+;       DefaultLineStyle - Setup default line style and length
+;
+;       Internal routine, called by mode change, SwitchOutputToSprite and
+;        FX163,242,n code
+;
+; out:  Can corrupt R0-R3,
+;       PSR preserved
+;
+
+DefaultLineStyle ROUT
+        MOV     R0, #8                  ; dot pattern repeats after 8 pixels
+        STR     R0, [WsPtr, #DotLineLength]
+        LDR     R0, =&AAAAAAAA          ; on-off pattern
+        STR     R0, [WsPtr, #DotLineStyle+0] ; 64 bits of pattern
+        STR     R0, [WsPtr, #DotLineStyle+4]
+        MOV     R0, #0                  ; force a restart of the pattern
+        STR     R0, [WsPtr, #LineDotCnt] ; if the user does a "continue"
+        MOVS    PC, R14
+
+; *****************************************************************************
+;
+;       SetPatLength - Set dotted line repeat length
+;
+;       Internal routine, called by DoOsbyte163_242
+;
+; in:   R2 = 1..64 => set pattern length to this
+;            0     => set default length and pattern
+;
+; out:  Can corrupt R0-R3,
+;       PSR preserved
+;
+
+SetPatLength ROUT
+        CMP     R2, #0
+        BEQ     DefaultLineStyle
+        STR     R2, [WsPtr, #DotLineLength]     ; 1..64
+        MOV     R2, #0                          ; force restart of the pattern
+        STR     R2, [WsPtr, #LineDotCnt]        ; if the user does a "continue"
+        MOVS    PC, R14
+
+; *****************************************************************************
+;
+;       DefaultEcfPattern - Setup all 4 ecf patterns for this mode
+;       (does not affect line style)
+;
+;       Internal routine, called by mode change, SwitchOutputToSprite and
+;        VDU 23_11
+;
+
+DefaultEcfPattern ROUT
+        MOV     R0, #0
+        STR     R0, [WsPtr, #BBCcompatibleECFs] ;Select BBC compatible ECF mode
+
+        LDR     R0, [WsPtr, #ECFIndex]
+        ADR     R1, DefEcfTb
+        ADD     R0, R1, R0, LSL #4      ; 16 bytes per entry
+        ADD     R1, WsPtr, #Ecf1
+
+        LDMIA   R0, {R2,R4,R6,R8}       ; Get all four patterns for each mode
+        LDMIA   R0, {R3,R5,R7,R9}       ; duplicate top half into bottom half
+        STMIA   R1, {R2-R9}             ; Write all 4 Ecfs
+        B       SetColour       ; update FgEcf & BgEcf in case they are Ecfs
+
+
+DefEcfTb        ; Table of default ECFs, indexed by ECFIndex (a mode variable)
+
+; Modes 3,6,7
+ & 0
+ & 0
+ & 0
+ & 0
+
+; Mode 0
+ & &00330033     ;Dark grey      (3 black, 1 white)
+ & &CC33CC33     ;Grey           (2 black, 2 white)
+ & &CCFFCCFF     ;Light grey     (1 black, 3 white)
+ & &030C30C0     ;Hatching
+
+; Modes 1,5,8,11,19
+ & &55665566     ;Red orange     (3 red, 1 yellow)
+ & &99669966     ;Orange         (2 red, 2 yellow)
+ & &99AA99AA     ;Yellow orange  (1 red, 3 yellow)
+ & &BBEEBBEE     ;Cream          (2 white, 2 yellow)
+
+; Modes 2,9,12,14,16,17,20(,21)
+ & &31133113     ;Orange         (2 red, 2 yellow)
+ & &51155115     ;Pink           (2 red, 2 magenta)
+ & &32233223     ;Yellow green   (2 green, 2 yellow)
+ & &37733773     ;Cream          (2 white, 2 yellow)
+
+; Modes 4,18,22,23
+ & &00550055     ;Dark grey      (3 black, 1 white)
+ & &AA55AA55     ;Grey           (2 black, 2 white)
+ & &AAFFAAFF     ;Light grey     (1 black, 3 white)
+ & &11224488     ;Hatching
+
+; Modes 10,13,15
+ & &FFFEFDFC     ;Its runnier than yoy'll lik itt
+ & &00010203
+ & &20212223
+ & &DFDEDDDC
+
+; *****************************************************************************
+;
+;       SimpleEcfPattern - Setup simple ecf pattern
+;
+;       Internal routine, called by VDU23_12..15
+;
+; in:   R0 = 12..15 for ecfs 1..4
+;       Vdu queue holds
+;          QQ+0  pattern number
+;          QQ+1  pattern bytes 0
+;           |                  |
+;          QQ+8                7
+;
+
+SimpleEcfPattern ROUT
+        ADD     R1, WsPtr, #(Ecf1-12*8)
+        ADD     R1, R1, R0, LSL #3      ; Point R1 at ECF(n)
+        ADD     R2, WsPtr, #(QQ+1)      ; Point R2 at first colour in queue
+        ADR     R3, SimpEcfTb
+        LDR     R4, [WsPtr, #ECFIndex]
+        LDRB    R10, [R3, R4]           ; Mask for this mode
+        CMP     R10, #&FF
+        BEQ     SimplEc20               ; 256 colour modes are different
+        LDR     R5, [WsPtr, #NColour]
+        ADRL    R4, TBFullCol           ; Base of full colour table (of bytes)
+        ADD     R4, R4, R5              ; Access to table is always
+                                        ; TBFullCol(NColour+(byte AND NColour))
+                                        ; so add NColour in now !
+        MOV     R7, #4                  ; There are 4 pairs of bytes in queue
+SimplEc10
+        LDRB    R6, [R2], #1            ; Get first colour of pair from queue
+        AND     R6, R6, R5              ; (byte AND NColour)
+        LDRB    R6, [R4, R6]            ; The full byte for this colour
+        AND     R6, R6, R10             ; Extract required pixels
+        LDRB    R9, [R2], #1            ; Get next colour of pair from queue
+        AND     R9, R9, R5              ; (byte AND Ncolour)
+        LDRB    R9, [R4, R9]
+        BIC     R9, R9, R10             ; Extract required pixels and
+        ORR     R6, R6, R9              ; build into final pattern
+        STRB    R6, [R1, #4]            ; Replicate pattern in both halves
+        STRB    R6, [R1], #1            ; of ECF
+        SUBS    R7, R7, #1
+        BNE     SimplEc10
+        B       SetColour       ; update FgEcf & BgEcf in case they use this
+
+; R1 -> ECF(n)
+; R2 -> first colour in queue
+
+SimplEc20
+        Push    R14
+        MOV     R5, #8
+SimplEc30
+        LDRB    R0, [R2], #1            ; Get byte from queue
+        AND     R3, R0, #&C0            ; Extract Tint
+        AND     R0, R0, #&3F            ;         Colour
+
+        BL      AddTintToColour         ; Recombine in our funny fashion
+        STRB    R0, [R1], #1            ; And store in the Ecf
+        SUBS    R5, R5, #1
+        BNE     SimplEc30
+        Pull    R14
+        B       SetColour       ; Update FgEcf & BgEcf incase they use this Ecf
+
+SimpEcfTb       ; Table of masks for simple ECFs, indexed by ECFIndex
+                ; (bit set => use left pixel, bit clear => use right pixel)
+
+        =       &00     ; Modes 3,6,7
+        =       &33     ; Mode 0
+        =       &33     ; Modes 1,5,8,11,19
+        =       &0F     ; Modes 2,9,12,14,16,17,20
+        =       &55     ; Modes 4,18,21,22
+        =       &FF     ; Modes 10,13,15
+
+        ALIGN
+
+; *****************************************************************************
+;
+;       ExportedHLine - Routine exported via VDU variable HLineAddr
+;
+; in:   R0,R2 = X coords in some order (internal coords)
+;       R1 = Y coord (internal coord)
+;       R3 = 0 => no effect
+;       R3 = 1 => foreground colour/action
+;       R3 = 2 => invert
+;       R3 = 3 => background colour/action
+;       R3 >= 4 => R3 -> eight word pairs
+;
+
+ExportedHLine ROUT
+        Push    "R0,R2,R4-R10,WsPtr,R14"
+        VDWS    WsPtr
+        CMP     R3, #4
+        BCS     %FT10
+
+; *****Change made by DJS
+;   Original code was:
+;        MOV     R3, R3, LSL #30         ; put bottom 2 bits into top 2 bits
+;        CMP     R3, #&40000000          ; set lots of flags
+;
+;        ADDMI   R3, WsPtr, #BgEcfOraEor ; if 0 or 3
+;        ADRCC   R3, NoEffect            ; if 0
+;        ADDEQ   R3, WsPtr, #FgEcfOraEor ; if 1
+;        ADRVS   R3, Invert              ; if 2
+
+        MOVS    R3, R3, LSL #31         ;Put bit 1 in C, NOT(bit 0) in Z
+        ADR     R3, NoEffect            ; if 0, 1, 2 or 3
+        ADDNE   R3, WsPtr, #FgEcfOraEor ; if 1 or 3
+        ADRCS   R3, Invert              ; if 2 or 3
+        ADDHI   R3, WsPtr, #BgEcfOraEor ; if CS & NE - i.e. 3
+
+; *****End of change made by DJS
+
+10
+        STR     R3, [WsPtr, #GColAdr]   ; save address of Ecf to plot with
+        BL      HLine
+        CLRV
+        Pull    "R0,R2,R4-R10,WsPtr,PC"
+
+; *****************************************************************************
+;
+; NewHLine - Horizontal line draw   an even newer version what does 8 words
+; ========                          at a time if it can
+;
+; On entry, R0 (X), R1 (Y) holds coordinate of point
+;           R2             holds right hand XCoord
+; On exit,  R3 preserved
+;           R4..R10 corrupt
+;           PSR preserved
+;
+; N.B. R11 WILL BE PRESERVED - This is assumed by rectangle fill and all circle
+;                              operations
+;
+
+HLine
+        SortT   R0, R2, R4              ; Sort the Xcoord into order R0<=R2
+
+NewHLine ROUT                           ; Entry point for sorted Xcoords
+
+        ADD     R4, WsPtr, #GWLCol
+        LDMIA   R4, {R4-R7}             ; GWLCol,GWBRow,GWRCol,GWTRow
+
+        CMP     R7, R1                  ; Test ycoord against window
+        CMPGE   R1, R5
+
+        CMPGE   R6, R0                  ; Test xcoords against window
+        CMPGE   R2, R4
+        MOVLTS  PC, R14                 ; Quit if above,below,left or right
+                                        ; of window
+
+        Greatest R0, R0, R4             ; If start of line to left of window
+                                        ; pull to window edge
+        Least   R2, R2, R6              ; If end of line to right of window
+                                        ; pull to window edge
+
+        Push    "R0-R3,R11,Link"
+
+;
+; Now, R0 holds Start (leftX), R1 holds Y and R2 holds End (rightX)
+;
+; Now pick up ecf word and form the ZGORA & ZGEOR masks
+;
+
+        ASSERT  LineLength = YWindLimit +4
+        ASSERT  GColAdr = XShftFactor +4
+        ASSERT  ScreenStart = XShftFactor +8
+
+        ADD     R11, WsPtr, #YWindLimit
+        LDMIA   R11, {R10-R11}          ; R10 = YWindLimit; R11 = LineLength
+
+        ADD     R9, WsPtr, #XShftFactor
+        LDMIA   R9, {R7-R9}             ; R7 = XShftFactor; R8 = GColAdr
+                                        ; R9 = ScreenStart
+
+        SUB     R1, R10, R1             ; flip ycoord into R1
+        AND     R6, R1, #7              ; R6 = line within ecf
+        ADD     R8, R8, R6, LSL #3      ; R8 -> zgora, zgeor
+        LDMIA   R8, {R8, R10}           ; R8 = zgora; R10 = zgeor
+
+        MLA     R9, R11, R1, R9         ; R9 -> start of this scan line
+
+        MOV     R6, R0, LSR R7          ; R6 = X1Coord word offset
+        MOV     R11, R2, LSR R7         ; R11 = X2Coord word offset
+        ADD     R9, R9, R6, LSL #2      ; R9 = address of lefthand word
+
+; *****Change made by DJS
+; Original code was:
+;
+;        EOR     R4, R0, R6, LSL R7      ; R4 = X1 pixel offset
+;        EOR     R5, R2, R11, LSL R7     ; R5 = X2 pixel offset
+;
+;        SUBS    R11, R11, R6            ; R11 = number of words -1
+;                                        ; and set Z on it
+;
+;        ADD     R7, WsPtr, #RAMMaskTb   ; R7 -> MaskTb for this mode
+;        LDR     R6, [R7, R4, LSL #2]    ; R6 = left mask
+;        LDR     R14, [R7, R5, LSL #2]   ; R14 = right mask
+;
+;        SUB     R7, R14, #1             ; in right mask set all bits lower
+;        ORR     R14, R14, R7            ; by RM = RM OR (RM-1)
+;
+;        RSB     R7, R6, #0              ; in left mask, set all bits higher
+;        ORR     R6, R6, R7              ; by LM = LM OR (-LM)
+;
+; The following code is shorter and faster.
+
+        SUBS    R11, R11, R6            ; R11 = number of words -1
+                                        ; and set Z on it
+
+        RSB     R7, R7, #5              ; R7 = Log2BPC (quicker than loading!)
+        MOV     R14, #31                ; constant to extract bit offsets
+        AND     R4, R14, R0, LSL R7     ; R4 = start of X1 pixel offset
+        AND     R5, R14, R2, LSL R7     ; R5 = start of X2 pixel offset
+        MOV     R14, #-1                ; Useful both as -1 and as &FFFFFFFF
+        SUB     R5, R5, R14, LSL R7     ; R5 = end of X2 pixel offset
+        MOV     R6, R14, LSL R4         ; R6 = left mask
+        MVN     R14, R14, LSL R5        ; R14 = right mask
+
+; *****End of change made by DJS
+
+        ANDEQ   R14, R14, R6            ; if start word = end word
+        BEQ     %FT40                   ; then combine masks
+
+        LDR     R0, [R9]                ; do left hand word of line
+        AND     R1, R8, R6              ; zgora AND left mask
+        AND     R2, R10, R6             ; zgeor AND left mask
+        ORR     R0, R0, R1              ; screen OR or mask
+        EOR     R0, R0, R2              ; EOR eor mask
+        STR     R0, [R9], #4            ; store to screen
+
+        SUBS    R11, R11, #1            ; decrement word count, if =0
+        BEQ     %FT40                   ; then plot RH partial word
+
+        CMP     R8, #-1                 ; if R8 = -1 then store action
+        BNE     %FT05                   ; else do it the slow way
+
+        MVN     R0, R10                 ; R0 = word of colour to plot
+        MOV     R1, R0
+        MOV     R2, R0
+        MOV     R3, R0
+        ADDS    R8, R8, R11, LSR #3     ; R8 = (no. of words DIV 8)-1
+        BCC     %FT60                   ; must be fewer than 8
+        MOV     R4, R0
+        MOV     R5, R0
+        MOV     R6, R0
+        MOV     R7, R0
+
+; *****Additional message inserted by DJS
+
+        [       (. :AND: 15) = 12
+        !       0, "HLine critical loop has bad alignment for running in RAM"
+        ]
+
+; *****End of message inserted by DJS
+
+50
+        STMCSIA R9!, {R0-R7}            ; write 8 words to screen if >=0
+        STMHIIA R9!, {R0-R7}            ; write 8 words to screen if > 0
+        SUBS    R8, R8, #2              ; try for another 16
+        BCS     %BT50
+60
+        TST     R11, #4                 ; can we do 4 words ?
+        STMNEIA R9!, {R0-R3}            ; write 4 words to screen
+        TST     R11, #2                 ; can we do 2 words ?
+        STMNEIA R9!, {R0-R1}            ; write 2 words to screen
+        TST     R11, #1                 ; can we do 1 word ?
+        STMNEIA R9!, {R0}               ; write 1 word to screen
+        AND     R10, R10, R14           ; do partial word at end
+        LDR     R0, [R9]
+        OrrEor  R0,R0, R14,R10
+        STR     R0, [R9],#4
+
+        Pull    "R0-R3,R11,PC",,^
+
+; code for when not store action
+
+05
+        SUBS    R11, R11, #8            ; else try for 8 words at a time
+        BMI     %FT20                   ; failed, so try for 4 at a time
+10
+        LDMIA   R9, {R0-R7}
+        OrrEor  R0,R0, R8,R10           ; Write 8 words to screen
+        OrrEor  R1,R1, R8,R10
+        OrrEor  R2,R2, R8,R10
+        OrrEor  R3,R3, R8,R10
+        OrrEor  R4,R4, R8,R10
+        OrrEor  R5,R5, R8,R10
+        OrrEor  R6,R6, R8,R10
+        OrrEor  R7,R7, R8,R10
+        STMIA   R9!, {R0-R7}
+        SUBS    R11, R11, #8            ; try for another 8
+        BPL     %BT10
+
+20
+        ADDS    R11, R11, #4            ; can we do 4 words ?
+        BMI     %FT30                   ; no, then do 1 word at a time
+
+        LDMIA   R9, {R0-R3}
+        OrrEor  R0,R0, R8,R10           ; Write 4 words to screen
+        OrrEor  R1,R1, R8,R10
+        OrrEor  R2,R2, R8,R10
+        OrrEor  R3,R3, R8,R10
+        STMIA   R9!, {R0-R3}
+
+        SUB     R11, R11, #4
+30
+        ADDS    R11, R11, #4            ; Correct for earlier SUB #4
+
+40                                      ; Plot single words
+        ANDEQ   R8, R8, R14             ; If EQ, this is RH word,
+        ANDEQ   R10, R10, R14           ; so mask down to required pixels
+        LDR     R0, [R9]
+        OrrEor  R0,R0, R8,R10
+        STR     R0, [R9],#4
+
+        SUBS    R11,R11,#1
+        BPL     %BT40
+
+        Pull    "R0-R3,R11,PC",,^
+
+; *****************************************************************************
+;
+;       NewVLine - Vertical line draw for non-dotted solid pattern lines
+;
+; in:   R0 = R2 = X coord
+;       R1 = bottom Y coord
+;       R3 = top Y coord
+;
+; out:  R4-R10 corrupt
+;       PSR preserved
+;
+
+NewVLine ROUT                           ; Entry point for sorted Ycoords
+
+        ADD     R4, WsPtr, #GWLCol
+        LDMIA   R4, {R4-R7}             ; GWLCol,GWBRow,GWRCol,GWTRow
+
+        CMP     R7, R1                  ; Test ycoord against window
+        CMPGE   R3, R5
+
+        CMPGE   R6, R0                  ; Test xcoords against window
+        CMPGE   R2, R4
+        MOVLTS  PC, R14                 ; Quit if above,below,left or right
+                                        ; of window
+
+        Greatest R1, R1, R5             ; If bottom of line below window
+                                        ; pull to window edge
+        Least   R3, R3, R7              ; If top of line above window
+                                        ; pull to window edge
+
+
+        Push    "R0-R3,R11,Link"
+
+; Now, R0 holds X, R1 holds bottom Y and R3 holds top Y
+;
+; Now pick up ecf word and form the ZGORA & ZGEOR masks
+;
+
+        ASSERT  LineLength = YWindLimit +4
+        ASSERT  GColAdr = XShftFactor +4
+        ASSERT  ScreenStart = XShftFactor +8
+
+        ADD     R11, WsPtr, #YWindLimit
+        LDMIA   R11, {R10-R11}          ; R10 = YWindLimit; R11 = LineLength
+
+        ADD     R9, WsPtr, #XShftFactor
+        LDMIA   R9, {R7-R9}             ; R7 = XShftFactor; R8 = GColAdr
+                                        ; R9 = ScreenStart
+        SUB     R1, R3, R1              ; R1 = number of dots to do -1
+
+        SUB     R3, R10, R3             ; R3 = flipped top ycoord
+        MLA     R9, R11, R3, R9         ; R9 -> start of this scan line
+
+        LDMIA   R8, {R8, R10}           ; R8 = zgora; R10 = zgeor
+                                        ; (no need to index with Y cos we
+                                        ; know there's no ECF pattern)
+
+        MOV     R6, R0, LSR R7          ; R6 = X coord word offset
+        ADD     R9, R9, R6, LSL #2      ; R9 = address of top word
+
+        EOR     R4, R0, R6, LSL R7      ; R4 = X pixel offset
+
+        ADD     R7, WsPtr, #RAMMaskTb   ; R7 -> MaskTb for this mode
+        LDR     R6, [R7, R4, LSL #2]    ; R6 = pixel mask
+        AND     R8, R8, R6              ; zgora = zgora AND pixelmask
+        AND     R10, R10, R6            ; zgeor = zgeor AND pixelmask
+
+; now do the plotting
+
+10
+        LDR     R6, [R9]
+        ORR     R6, R6, R8
+        EOR     R6, R6, R10
+        STR     R6, [R9], R11
+        SUBS    R1, R1, #1
+        BPL     %BT10
+
+        Pull    "R0-R3,R11,PC",,^
+
+
+
+; *****************************************************************************
+;
+; DoubleHLine - Draw 2 horizontal lines
+; ===========
+;
+; On entry, R0 (X) - Left most point
+;           R1 (Y) - y ordinate of line
+;           R2 (X) - Right most point
+;           R3 (X) - end of left most line
+;           R4 (X) - start of right most line
+;
+; On exit,  R0..R10 preserved  } subject
+;           R11 corrupt        }  to change
+;
+DoubleHLine
+        Push    "R0-R10, R14"
+        MOV     R2, R3                  ; draw left line, R0->R3 inc.
+        BL      HLine
+        LDMIB   R13, {R1-R4}
+        MOV     R0, R4                  ; draw right line, R4->R2 inc.
+        BL      HLine
+        Pull    "R0-R10, PC"
+
+; *****************************************************************************
+;
+;       ExtractTintAndColour - Convert 256-colour mode byte value into
+;        colour and tint
+;
+;       Internal routine, called by ReadPixelColour, SwiReadPoint
+;
+; in:   R0 = single screen pixel (ie 'half' user pixel in double modes)
+;
+; out:  R0 corrupt
+;       R2 = colour value (GCOL a,colour)
+;       R3 = tint Vdu 23,17 etc
+;
+
+ExtractTintAndColour ROUT
+                                        ; R0 := B3 G3 G2 R3 B2 R2 T1 T0
+        MOV     R3, R0, LSL #6          ; R3 := T1 T0  0  0  0  0  0  0
+        AND     R3, R3, #&C0
+        AND     R2, R0, #&84            ; R2 := B3  0  0  0  0 R2  0  0
+        TST     R0, #8
+        ORRNE   R2, R2, #&40            ; R2 := B3 B2  0  0  0 R2  0  0
+        AND     R0, R0, #&70            ; R0 :=  0 G3 G2 R3  0  0  0  0
+        ORR     R2, R2, R0, LSR #1      ; R2 := B3 B2 G3 G2 R3 R2  0  0
+        MOV     R2, R2, LSR #2          ; R2 :=  0  0 B3 B2 G3 G2 R3 R2
+        MOVS    PC, R14
+
+; *****************************************************************************
+;
+;       SwiReadPoint - Read colour of screen pixel
+;
+;       External routine - entry point for SWI OS_ReadPoint
+;
+; in:   R0, R1 = X, Y coordinate of point
+;
+; out:  R2 = colour (as in GCOL a,colour)       } -1 if off screen
+;       R3 = tint value (as in VDU 23,17 etc)   }  0 if off screen
+;       R4 = 0/-1 for On/Off screen
+;       R0,R1, R5-R9 preserved (R10-R12 preserved by MOS)
+;
+
+SwiReadPoint ROUT
+        TEQP    PC, #SVC_mode           ; re-enable interrupts
+        VDWS    WsPtr                   ; point R12 at vdu driver workspace
+
+      [ {TRUE}
+        GraphicsMode R10                ; if not a graphics mode then give up now!
+        BNE     %FT20
+      |
+        LDR     R10, [WsPtr, #NPix]     ; if not a graphics mode then
+        CMP     R10, #0                 ; indicate off screen
+        BEQ     %FT20
+      ]
+
+        Push    "R0-R9, R14"            ; save registers
+
+        ADD     R7, WsPtr, #GCsX
+        LDMIA   R7, {R8,R9}             ; preserve GCsX,GCsY around EIG
+        MOV     R2, #4                  ; absolute coord
+        BL      EIG
+        STMIA   R7, {R8,R9}             ; restore GcsX,GCsY
+
+        WINDow  R0,R1, R2,R3,R4,R5
+        BLT     %FT10                   ; outside window
+
+        Push    "R0,R1"                 ; save internal coords
+        BL      PreWrchCursor           ; remove any split cursors etc
+        Pull    "R0,R1"
+                                        ;     R0,R1,R2 ,R3 ,R4 ,R5 ,R6 ,R7 ,R8
+        BL      ScreenAddr              ; in :X ,Y
+                                        ; out:X ,Y ,Adr,Msk,   ,   ,   ,crp,crp
+        LDR     R8, [WsPtr, #NColour]
+
+;amg - changes here to cope with 16/32bpp which will also return an NCOL with b4-b7 set
+
+;        TST     R8, #&F0                ; if NColour=63
+;        MOVNE   R8, #&FF                ; then use 255
+
+        CMP     R8,#63
+        MOVEQ   R8,#255
+
+        LDR     R9, [WsPtr, #XShftFactor]
+        LDR     R10, [WsPtr, #NPix]
+        LDR     R11, [WsPtr, #Log2BPC]
+
+        BitLOffset R7,R0, R9,R10,R11    ; R7 := bit position to align to
+
+        LDR     R0, [R2]
+        AND     R0, R8, R0, LSR R7      ; extract one pixel
+
+        TEQ     R8,#255
+        MOVNE   R2,R0
+        MOVNE   R3,#0
+        BLEQ    ExtractTintAndColour
+
+;        TST     R8, #&F0                ; if not a 256 colour mode
+;        MOVEQ   R2, R0                  ; colour = pixel
+;        MOVEQ   R3, #0                  ; tint   = 0
+;        BLNE    ExtractTintAndColour    ; else extract colour & tint from pixel
+
+        MOV     R4, #0                  ; indicate on screen
+
+        ADD     R0, R13, #2*4           ; point to stacked R2
+        STMIA   R0, {R2-R4}
+
+        BL      PostWrchCursor
+
+        Pull    "R0-R9, R14"            ; restore R0-R9 & return address
+        BIC     R14, R14, #V_bit
+        ExitSWIHandler
+
+10
+        Pull    "R0-R9, R14"            ; restore R0-R9 & return address
+20
+        MOV     R2, #-1
+        MOV     R3, #0
+        MOV     R4, #-1
+
+        BIC     R14, R14, #V_bit
+        ExitSWIHandler
+
+; *****************************************************************************
+;
+;       GenCircleParm - Generate a control block for a circle
+;
+;       Internal routine, called by CircleOutline, CircleFill, GenArcParmBlk
+;
+; in:   R0 (X), R1(Y) centre of circle
+;       R2 (X), R3(Y) point on circumference
+;
+; out:  R0-R7 hold the following control block
+;         R0 - xPnt   (CurrentX - relative to centre)
+;         R1 - yPnt   (CurrentY - relative to centre)
+;         R2 - sum (Bres)
+;         R3 - upcnt
+;         R4 - downcnt
+;         R5 - CentreX
+;         R6 - CentreY
+;         R7 - Aspect (pixel shape : 0 square, 1 horz rect, 2 vert rect)
+;       R8-R11 undefined
+;
+GenCircleParm
+        Push    R14
+        LDR     R11, [WsPtr, #AspectRatio]
+        SUB     R7, R2, R0
+        TST     R11, #1                 ; if pixels are horz rects, adjust
+        MOVNE   R7, R7, LSL #1          ; x distance
+        MUL     R2, R7, R7              ; R2 = (x-cx)^2
+
+        SUB     R7, R3, R1
+        TST     R11, #2                 ; if pixels are vert rects, adjust
+        MOVNE   R7, R7, LSL #1          ; y distance
+
+        MLA     R2, R7, R7, R2          ; rawradsqr=(x-cx)^2 + (y-cy)^2
+        MOV     R7, R2
+        BL      SquareRoot
+
+        ADD     R2, R2, R8              ; radsqr=rawradsqr+rawrad
+        STR     R2, [WsPtr, #CircleRadSquare]   ; needed in seg. line calc
+
+        MOV     R7, R2
+        BL      SquareRoot
+        MOV     R4, R8                  ; R4=rad, R2=radsqr
+
+;
+; Now build the parameter block proper
+;
+;  R0 = CentreX, R1 = CentreY, R2 = radsqr, R4 = rad
+;
+        MUL     R9, R4, R4              ; R9=rad*rad
+        SUB     R2, R2, R9              ; Sum=radsqu-rad*rad
+
+        MOV     R5, R0                  ; CentreX
+        MOV     R6, R1                  ; CentreY
+
+        MOV     R0, R4                  ; xPnt starts at rad
+
+        ADD     R4, R4, R4
+        SUB     R4, R4, #1              ; downcnt = 2*rad-1
+
+        MOV     R1, #0                  ; yPnt starts at 0
+        MOV     R3, #1                  ; upcnt = 1
+
+        LDR     R7, [WsPtr,#AspectRatio] ; taking account of pixel shape
+        LDR     R11, [WsPtr, #CursorFlags]
+        TST     R11, #ClipBoxEnableBit
+        BLNE    ClipCircle
+
+        CMP     R7, #1
+
+        MOVEQ   R0, R0, ASR #1          ; if horz pixel (ie like mode2)
+        ADDEQ   R2, R2, R4
+        SUBEQ   R4, R4, #2
+
+        SUBGT   R2, R2, R3              ; if vert pixel (ie like mode0)
+        ADDGT   R3, R3, #2
+
+        Pull    PC
+
+; *****************************************************************************
+;
+;       AdvCircleParm - Advance a set of circle parameters
+;
+;       Internal routine, called by CircleOutline, CircleFill, CircleArc,
+;        SegmentFill, SectorFill
+;
+; in:   R0..R7 hold a circle parameter block
+;
+; out:  R0 (X), R1 (Y) updated
+;       C=1 <=> R1 (Y) has changed
+;       Z preserved
+;
+;       Format of a control block
+;         R0 - xPnt   (CurrentX - relative to centre)
+;         R1 - yPnt   (CurrentY - relative to centre)
+;         R2 - sum (Bres)
+;         R3 - upcnt
+;         R4 - downcnt
+;         R5 - CentreX
+;         R6 - CentreY
+;         R7 - Aspect (pixel shape : 0 square, 1 horz rect, 2 vert rect)
+;
+
+AdvCircleParm ROUT
+        CMP     R2, R3                  ; if sum >= upcnt advance Y only
+        BGE     %FT10
+
+        SUB     R0, R0, #1              ; else step xPnt inward one point
+
+        ADD     R2, R2, R4              ; Sum := Sum + DownCnt
+        SUB     R4, R4, #2              ; DownCnt = next lower odd number
+
+        TST     R7, #1                  ; if pixels are horizontal rectangles
+        ADDNE   R2, R2, R4              ; modify sum again, so x steps at
+        SUBNE   R4, R4, #2              ; half normal rate
+
+        CMP     R2, R3
+        BICLTS  PC, R14, #C_bit         ; if not doing Y, indicate with C=0
+10
+                                        ; if sum >= upcnt then advance Y
+        ADD     R1, R1, #1              ; step yPnt up a line
+
+        SUB     R2, R2, R3              ; Sum := Sum - UpCnt
+        ADD     R3, R3, #2              ; UpCnt = next higher odd number
+
+        TST     R7, #2                  ; if pixels are vertical rectangles
+        SUBNE   R2, R2, R3              ; modify sum again, so y steps
+        ADDNE   R3, R3, #2              ; at half normal rate
+
+        ORRS    PC, R14, #C_bit         ; Y modified, so return C=1
+
+; *****************************************************************************
+;
+;       SquareRoot - Calculate the square root of a 32-bit number
+;
+;       Internal routine, called by GenSegParmBlk, GenCircleParm
+;
+;       SquareRootAlt is a alternative routine which specifies the precision
+;        of the result in R11 (SquareRoot produces a 16-bit result)
+;
+; in:   R7 = number to squareroot
+;
+; out:  R8 = result
+;       R9-R11 corrupted
+;         R9 temp
+;         R10 sqdiff
+;         R11 counter
+;
+
+SquareRoot ROUT
+        MOV     R11, #16                ; 16 bit result
+SquareRootAlt
+        MOV     R8, #0                  ; result=0
+        MOV     R10, #0                 ; sqdiff=0
+10
+
+; *****Change made by DJS
+; Original code was:
+;        ADDS    R7, R7, R7              ; (sqdiff,number) = (sqdiff,number)*4
+;        ADC     R10, R10, R10
+;        ADDS    R7, R7, R7
+;        ADCS    R10, R10, R10           ; (C:=0 we hope!)
+
+        MOVS    R10, R10, LSL #2        ; C:=0 (we hope!) while doing (sqdiff,
+        ORR     R10, R10, R7, LSR #30   ;   number) := (sqdiff, number) * 4
+        MOV     R7, R7, LSL #2
+
+; *****End of change made by DJS
+
+        SBCS    R9, R10, R8, LSL #2     ; C=0 here, so try to subtract
+                                        ; result*4 +1 from sqdiff
+        MOVCS   R10, R9                 ; if successful then shift in a "1" bit
+        ADC     R8, R8, R8              ; else shift in a "0" bit
+        SUBS    R11, R11, #1            ; decrement loop counter
+        BNE     %BT10
+
+        MOVS    PC, R14
+
+; *****************************************************************************
+;
+;       DoOsWord13 - Read graphics cursors (in external coords)
+;
+; in:   R1 -> control block
+;
+; out:  [R1+0..1] = old cursor X
+;       [R1+2..3] = old cursor Y
+;       [R1+4..5] = current cursor X
+;       [R1+6..7] = current cursor Y
+;
+        ASSERT  OldCsY = OldCsX +4
+        ASSERT  YEigFactor = XEigFactor +4
+        ASSERT  OrgY = OrgX +4
+        ASSERT  GCsY = GCsX +4
+
+DoOsWord13 ROUT
+        Push    "R0-R6"
+        MOV     R6, R1                  ; pointer to control block
+
+        ADD     R0, WsPtr, #OldCsX
+        LDMIA   R0, {R0, R1}
+        ADD     R2, WsPtr, #XEigFactor
+        LDMIA   R2, {R2, R3}            ; R2 = XEigFactor; R3 = YEigFactor
+        ADD     R4, WsPtr, #OrgX
+        LDMIA   R4, {R4, R5}            ; R4 = OrgX; R5 = OrgY
+        RSB     R0, R4, R0, LSL R2      ; R0 = (X << XEigFactor)-OrgX
+        RSB     R1, R5, R1, LSL R3      ; R1 = (Y << YEigFactor)-OrgY
+
+        STRB    R0, [R6], #1
+        MOV     R0, R0, LSR #8
+        STRB    R0, [R6], #1
+        STRB    R1, [R6], #1
+        MOV     R1, R1, LSR #8
+        STRB    R1, [R6], #1
+
+        ADD     R0, WsPtr, #GCsX
+        LDMIA   R0, {R0, R1}            ; get current cursor
+
+        STRB    R0, [R6], #1
+        MOV     R0, R0, LSR #8
+        STRB    R0, [R6], #1
+        STRB    R1, [R6], #1
+        MOV     R1, R1, LSR #8
+        STRB    R1, [R6], #1
+
+        Pull    "R0-R6"
+        MOV     PC, R14
+
+        LTORG
+        END
diff --git a/s/vdu/vdupointer b/s/vdu/vdupointer
new file mode 100644
index 0000000000000000000000000000000000000000..bc7b20d0fe40b0d085a6fb61a7679453c329f23a
--- /dev/null
+++ b/s/vdu/vdupointer
@@ -0,0 +1,657 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduPointer
+
+; *****************************************************************************
+;
+;       DoPointerStuff - Entry point for OSWORD nn
+;
+; in:   R1 -> control block
+;       [R1, #0] : Reason code
+;
+;       Reason code 0 - Define pointer size, shape and active point
+;
+;       [R1, #1] : Shape number (1..4)
+;       [R1, #2] : Width (w) in bytes (0..8)
+;       [R1, #3] : Height (h) in pixels (0..32)
+;       [R1, #4] : ActiveX in pixels from left (0..w*4-1)
+;       [R1, #5] : ActiveY in pixels from top (0..h-1)
+;       [R1, #6..9] : Pointer (P) to data
+;       [P, #0..w*h-1] : Data bytes in rows from top to bottom,
+;       left to right in each row.
+;
+;       Reason code 1 - Define mouse coordinate bounding box
+;
+;       [R1, #1..2] : left      ; all treated as
+;       [R1, #3..4] : bottom    ; signed 16-bit values,
+;       [R1, #5..6] : right     ; relative to screen origin at the time
+;       [R1, #7..8] : top       ; the command is issued
+;
+;       If (left > right) or (bottom > top) then the command is ignored
+;       An infinite box can be obtained by setting
+;       left=&8000, right=&7FFF, bottom=&8000, top=&7FFF
+;
+;       If the current mouse position is outside the box, it is moved to
+;       the nearest point inside the box
+;
+;       The mouse buffer is NOT flushed - any buffered coords will be moved
+;       inside the bounding box when they are read.
+;
+;       Reason code 2 - Define mouse multipliers
+;
+;       [R1, #1] : X multiplier         ; both treated as
+;       [R1, #2] : Y multiplier         ; signed 8-bit values
+;
+;       Reason code 3 - Set mouse position
+;
+;       [R1, #1..2] : X position        ; both treated as
+;       [R1, #3..4] : Y position        ; signed 16-bit values
+;       The mouse buffer is flushed
+;
+;       Reason code 4 - Read mouse position (not buffered)
+;
+;       out: [R1, #1..2] : X position   ; both treated as
+;            [R1, #3..4] : Y position   ; signed 16-bit values
+;
+;       Reason code 5 - Set pointer position
+;
+;       [R1, #1..2] : X position        ; both treated as
+;       [R1, #3..4] : Y position        ; signed 16-bit values
+;
+;       Reason code 6 - Read pointer position
+;
+;       out: [R1, #1..2] : X position   ; both treated as
+;            [R1, #3..4] : Y position   ; signed 16-bit values
+;
+
+DoPointerStuff ROUT
+        LDRB    R0, [R1, #0]
+
+; *****Change made by DJS & SKS
+; Original code was:
+;        CMP     R0, #1
+;        BCC     DoDefinePointer
+;        BEQ     DoMouseBox
+;        CMP     R0, #3
+;        BCC     SetMouseMult
+;        BEQ     SetMousePosn
+;        CMP     R0, #5
+;        BCC     ReadMousePosn
+;        BEQ     SetPointerPosn
+;        CMP     R0, #7
+;        BCC     ReadPointerPosn
+;        MOV     PC, R14
+
+        CMP     R0, #7
+
+        [       Module
+
+        ADDCC   PC, PC, R0, LSL #2
+        MOV     PC, R14                 ; ***** WHY NO ERROR????????
+        B       DoDefinePointer
+        B       DoMouseBox
+        B       SetMouseMult
+        B       SetMousePosn
+        B       ReadMousePosn
+        B       SetPointerPosn
+        B       ReadPointerPosn
+
+        |
+
+        LDRCC   PC, [PC, R0, LSL #2]
+        MOV     PC, R14                 ; ***** WHY NO ERROR????????
+        DCD     DoDefinePointer
+        DCD     DoMouseBox
+        DCD     SetMouseMult
+        DCD     SetMousePosn
+        DCD     ReadMousePosn
+        DCD     SetPointerPosn
+        DCD     ReadPointerPosn
+
+        ]
+
+; *****End of change made by DJS & SKS
+
+; *****************************************************************************
+
+DoDefinePointer
+        Push    "R1-R6, R14"
+ [ Fix8
+        CLRPSR  I_bit, R0                       ; could take some time
+ ]
+        MOV     R0, #19                         ; wait for vsync, so we change
+        SWI     XOS_Byte                        ; cleanly (ignore errors)
+
+        LDMFD   R13, {R1}                       ; get back pointer to block
+
+        LDRB    R6, [R1, #1]                    ; shape number
+        SUB     R6, R6, #1
+        CMP     R6, #4                          ; now in range 0..3 ?
+        BCS     %FT10                           ; bad shape number
+
+        ADD     WsPtr, WsPtr, R6                ; point to table entries
+
+        LDRB    R0, [R1, #2]                    ; R0 = width
+        LDRB    R2, [R1, #3]                    ; R2 = height
+        LDRB    R3, [R1, #4]                    ; R3 = ActiveX
+        LDRB    R4, [R1, #5]                    ; R4 = ActiveY
+
+        CMP     R2, #0                          ; C=1 if EQ
+        STREQB  R2, [WsPtr, #PointerHeights]
+        BEQ     %FT10
+
+        CMP     R0, #0                          ; C=1 if EQ
+        STREQB  R0, [WsPtr, #PointerHeights]
+        CMPNE   R0, #8+1
+        BCS     %FT10                           ; bad width
+
+        CMP     R2, #32+1                       ; C=1 => bad height
+        CMPCC   R3, R0, LSL #2                  ; ActiveX >= (width * 4) ?
+        CMPCC   R4, R2                          ; ActiveY >= height
+
+        BCS     %FT10                           ; bad definition
+
+        STRB    R2, [WsPtr, #PointerHeights]
+        STRB    R3, [WsPtr, #PointerActiveXs]
+        STRB    R4, [WsPtr, #PointerActiveYs]
+
+        ADD     R4, R1, #6
+        LDW     R1, R4, R3, R5                  ; load word from
+                                                ; unknown alignment
+; Now R1 -> user's data
+
+        LDR     R3, =CursorData                 ; &200 per shape, so
+        ADD     R3, R3, R6, LSL #9              ; R3 -> first copy
+        ADD     R6, R3, #&F8                    ; R6 -> second copy
+20
+        ADD     R4, R3, R0                      ; terminating R3 for this row
+30
+        LDRB    R5, [R1], #1
+40
+        STRB    R5, [R3], #1                    ; store to 1st copy
+        STRB    R5, [R6], #1                    ; store to 2nd copy
+        CMP     R3, R4                          ; still within user data
+        BCC     %BT30                           ; for this row ?
+
+; now fill up rest of row
+
+        MOV     R5, #0
+        TST     R3, #7                          ; are we on a multiple of 8
+        BNE     %BT40                           ; no, then store 0
+
+        SUBS    R2, R2, #1                      ; done all rows ?
+        BNE     %BT20                           ; no, then loop
+
+10
+        Pull    "R1-R6,PC"
+
+; *****************************************************************************
+;
+;       SetMouseRectangle - Called on mode change to set appropriate mouse
+;                           rectangle and mouse position
+;
+; in:   WsPtr -> VDWS
+;
+
+SetMouseRectangle ROUT
+        Push    R14
+
+        ASSERT  DisplayYWindLimit = DisplayXWindLimit +4
+        ASSERT  DisplayXEigFactor = DisplayXWindLimit +8
+        ASSERT  DisplayYEigFactor = DisplayXWindLimit +12
+
+        ADD     R2, WsPtr, #DisplayXWindLimit
+        LDMIA   R2, {R2-R5}
+
+        ADD     R2, R2, #1              ; XWindLimit+1
+        MOV     R2, R2, LSL R4          ; (XWindLimit+1) << XEigFactor
+        SUB     R4, R2, #1              ; ((XWindLimit+1) << XEigFactor)-1
+        MOV     R2, R2, LSR #1          ; centre x of window
+
+        ADD     R3, R3, #1              ; YWindLimit+1
+        MOV     R3, R3, LSL R5          ; (YWindLimit+1) << YEigFactor
+        SUB     R5, R3, #1              ; ((YWindLimit+1) << YEigFactor)-1
+        MOV     R3, R3, LSR #1          ; centre y of window
+
+        BL      SetMousePosnRegs
+
+        MOV     R2, #0                  ; left = 0
+        MOV     R3, #0                  ; bottom = 0
+
+        Push    "R1-R6"
+        B       DoMouseBoxRegs
+
+
+DoMouseBox ROUT
+        Push    "R1-R6, R14"
+
+        LDRB    R2, [R1, #1]            ; R2 = left
+        LDRB    R0, [R1, #2]
+        ORR     R2, R2, R0, LSL #8
+
+        LDRB    R3, [R1, #3]            ; R3 = bottom
+        LDRB    R0, [R1, #4]
+        ORR     R3, R3, R0, LSL #8
+
+        LDRB    R4, [R1, #5]            ; R4 = right
+        LDRB    R0, [R1, #6]
+        ORR     R4, R4, R0, LSL #8
+
+        LDRB    R5, [R1, #7]            ; R5 = top
+        LDRB    R0, [R1, #8]
+        ORR     R5, R5, R0, LSL #8
+
+DoMouseBoxRegs
+
+; now add on graphics origin
+
+        LDR     R0, [WsPtr, #OrgX]
+        ADD     R2, R2, R0
+        ADD     R4, R4, R0
+        LDR     R0, [WsPtr, #OrgY]
+        ADD     R3, R3, R0
+        ADD     R5, R5, R0
+
+; now sign extend all coords
+
+        MOV     R2, R2, LSL #16
+        MOV     R2, R2, ASR #16
+        MOV     R3, R3, LSL #16
+        MOV     R3, R3, ASR #16
+        MOV     R4, R4, LSL #16
+        MOV     R4, R4, ASR #16
+        MOV     R5, R5, LSL #16
+        MOV     R5, R5, ASR #16
+
+; now check right >= left and top >= bottom
+
+        CMP     R4, R2
+        CMPGE   R5, R3
+        BLT     %FT10                   ; bad definition
+
+; everything seems OK, so disable IRQs while we update vars
+
+        MOV     R14, PC
+        ORR     R0, R14, #I_bit
+        TEQP    R0, #0
+
+        Push    R11
+        MOV     R11, #KeyWorkSpace
+
+        ADR     R0, MouseBounds
+        STMIA   R0, {R2-R5}
+
+; check mouse position is within box
+
+        LDR     R0, MouseX
+        CMP     R0, R2                  ; if X < left
+        STRLT   R2, MouseX              ; then X := left
+        CMP     R4, R0                  ; if right < X
+        STRLT   R4, MouseX              ; then X := right
+
+        LDR     R0, MouseY
+        CMP     R0, R3                  ; if Y < bottom
+        STRLT   R3, MouseY              ; then Y := bottom
+        CMP     R5, R0                  ; if top < Y
+        STRLT   R5, MouseY              ; then Y := top
+
+        Pull    R11
+
+;        BL      FlushMouse              ; flush mouse buffer no longer done
+
+; *****Change made by DJS and SKS
+; Bug fix. Original code was:
+;        TEQP    R14, #0                 ; restore old IRQ state
+;10
+;        Pull    "R1-R6, PC"
+; This is making use of a nonsense value of R14. It also looks as if this
+; can be made shorter:
+
+10
+        Pull    "R1-R6, PC",,^          ;Return, restoring old IRQ state
+
+; *****************************************************************************
+;
+;       UpdatePointer - Called on vsync to update pointer position
+;
+; in:   WsPtr (R12) -> VduDriverWorkSpace
+;
+
+UpdatePointer ROUT
+        MOV     R11, #KeyWorkSpace
+        MOV     R10, #VIDC
+
+        LDRB    R5, [WsPtr, #PointerShapeNumber]
+        TST     R5, #&80                ; pointer unlinked if bit 7 set
+
+        LDREQ   R6, MouseX
+        STREQ   R6, [WsPtr, #PointerX]
+        LDREQ   R6, MouseY
+        STREQ   R6, [WsPtr, #PointerY]
+
+        ANDS    R5, R5, #&7F            ; clear bit 7 and set Z if 0 ie off
+        SUBNE   R5, R5, #1
+        ADDNE   R6, WsPtr, R5           ; R6 is WsPtr + index
+
+        LDRNEB  R0, [R6, #PointerHeights]
+        TEQNE   R0, #0                  ; if shape 0 or height 0
+        BNE     %FT07
+05
+
+; don't display pointer
+
+        MOV     R1, #0                  ; Y := 0
+        MOV     R2, #0                  ; height := 0
+        MOV     R4, #0                  ; LAG offset := 0
+        B       %FT10
+
+07
+ [ VIDC_Type = "VIDC20"
+
+        LDR     R2, [WsPtr, #PointerXEigFactor]
+        LDR     R3, [WsPtr, #PointerX]
+        MOV     R3, R3, ASR R2
+
+        LDRB    R2, [R6, #PointerActiveXs]
+        SUB     R3, R3, R2              ; subtract off active point coord
+
+        CMP     R3, #-31                ; if LHS < -31, ie RHS < 0
+        BLT     %BT05                   ; then remove pointer completely (fixes MED-00523)
+
+        LDR     R1, [WsPtr, #CursorFudgeFactor]
+        ADDS    R3, R3, R1              ; X := X + CursFudgeFactor
+        MOVLT   R3, #0                  ; if < 0 then make =0
+
+        CMP     R3, #16*1024            ; if >= limit then make =limit-1
+        MOVGE   R3, #16*1024
+        SUBGE   R3, R3, #1
+
+        ORR     R3, R3, #HorizCursorStart
+ |
+        LDR     R1, [WsPtr, #CursorFudgeFactor]
+        LDRB    R2, [R6, #PointerActiveXs]
+        SUB     R1, R1, R2
+
+        LDR     R2, [WsPtr, #PointerXEigFactor]
+        LDR     R3, [WsPtr, #PointerX]
+        MOV     R3, R3, ASR R2
+
+        LDR     R4, [WsPtr, #DisplayModeFlags]
+        TST     R4, #Flag_HiResMono     ; if not hi-res-mono
+        MOVEQ   R3, R3, LSL #2          ; then multiply by 4
+
+        ADD     R3, R3, R1, LSL #2      ; X := X + (CursFudgeFactor-ActiveX)<<2
+
+        CMP     R3, #0                  ; if < 0 then make =0
+        MOVLT   R3, #0
+
+        CMP     R3, #8*1024             ; if >= limit then make =limit-1
+        MOVGE   R3, #8*1024
+        SUBGE   R3, R3, #1
+
+        MOV     R3, R3, LSL #11
+        ORR     R3, R3, #HorizCursorStart
+ ]
+
+ [ {FALSE} ; debug horiz pointer programming
+        Push    "r0,lr"
+        MOV     r0, r3
+        BL      TubeDumpR0
+        BL      TubeNewl
+        Pull    "r0,lr"
+ ]
+
+        STR     R3, [R10]
+
+; now process Y
+
+        LDR     R1, [WsPtr, #PointerY]
+        LDR     R2, [WsPtr, #DisplayYEigFactor]
+
+        LDR     R3, [WsPtr, #DisplayYWindLimit]
+                                        ; Total height of screen in pixels-1
+        SUB     R1, R3, R1, ASR R2      ; convert Y to pixels and invert
+
+        LDRB    R2, [R6, #PointerActiveYs]
+        SUB     R1, R1, R2
+
+        MOV     R2, #0                  ; height
+        MOV     R4, R5, LSL #9          ; LAG offset from CursorData
+
+        ADD     R5, R3, #1              ; Total vertical pixels
+
+        ADDS    R3, R1, R0              ; R3 = Y + PointerHeight(shape)
+        MOVLE   R1, #0                  ; if <= 0 then off, ie Y=0, height=0
+        BLE     %FT10
+
+        CMP     R1, R5                  ; Y >= TotalHeight
+        MOVGE   R1, #0                  ; then off
+        BGE     %FT10
+
+        CMP     R3, R5                  ; Y + PointerHeight > TotalHeight
+        RSBGT   R2, R1, R5              ; then height=TotalHeight-Y
+        BGT     %FT10
+
+        CMP     R1, #0                  ; Y >=0
+        MOVGE   R2, R0                  ; then height=PointerHeight
+        BGE     %FT10
+
+        TST     R1, #1                  ; Y odd ?
+        ADDNE   R4, R4, #&100           ; then use 2nd copy
+
+        ADD     R2, R1, R0              ; height = PointerHeight+Y
+
+        RSB     R1, R1, #0              ; Y := -Y
+        BIC     R1, R1, #1              ; Y := (-Y AND NOT 1)
+        ADD     R4, R4, R1, LSL #3      ; add on offset into shape
+        MOV     R1, #0                  ; Y := 0
+10
+        Push    "r1, r14"
+ [ MEMC_Type = "IOMD"
+        MOV     r0, #0
+        LDR     r0, [r0, #DRAMPhysAddrA]
+        LDR     r1, =DRAMOffset_CursorChunk + (CursorData - CursorChunkAddress)
+        ADD     r0, r0, r1
+ |
+        LDR     r0, =PhysCursorStartAdr
+ ]
+        ADD     r0, r0, r4
+        MOV     r1, #MEMCDAG_CInit
+        BL      SetDAG
+        Pull    "r1, r14"
+
+ [ VIDC_Type <> "VIDC20"
+        MOV     R1, R1, LSL #14         ; shift up
+ ]
+        LDR     R3, [WsPtr, #VertAdjust] ; get display start value
+        ADD     R1, R1, R3              ; and add on
+
+        ORR     R3, R1, #VertiCursorStart
+        STR     R3, [R10]
+
+ [ VIDC_Type = "VIDC20"
+        ADD     R1, R1, R2              ; add height
+ |
+        ADD     R1, R1, R2, LSL #14     ; add height
+ ]
+        ORR     R3, R1, #VertiCursorEnd
+        STR     R3, [R10]
+
+        MOV     PC, R14
+
+        LTORG
+
+; *****************************************************************************
+
+SetMouseMult ROUT
+        Push    "R11,R14"
+        MOV     R11, #KeyWorkSpace
+
+        LDRB    R0, [R1, #1]
+        MOV     R0, R0, LSL #24         ; sign extend to 32 bits
+        MOV     R0, R0, ASR #24
+        STR     R0, MouseXMult
+
+        LDRB    R0, [R1, #2]
+        MOV     R0, R0, LSL #24         ; sign extend to 32 bits
+        MOV     R0, R0, ASR #24
+        STR     R0, MouseYMult
+
+        Pull    "R11,PC"
+
+; *****************************************************************************
+;
+;       GetCoordPair - get pair of 2-byte coords from R1+1..R1+4
+;       adds on graphics origin and sign extends to 32 bits
+;       and puts X into R2, Y into R3
+;
+
+GetCoordPair ROUT
+        LDRB    R0, [R1, #1]            ; get X coordinate
+        LDRB    R2, [R1, #2]
+        ORR     R0, R0, R2, LSL #8
+
+        LDR     R2, [WsPtr, #OrgX]      ; add on origin
+        ADD     R0, R0, R2
+
+        MOV     R0, R0, LSL #16         ; sign extend 16 to 32
+        MOV     R2, R0, ASR #16
+
+        LDRB    R0, [R1, #3]            ; get Y coordinate
+        LDRB    R3, [R1, #4]
+        ORR     R0, R0, R3, LSL #8
+
+        LDR     R3, [WsPtr, #OrgY]      ; add on origin
+        ADD     R0, R0, R3
+
+        MOV     R0, R0, LSL #16         ; sign extend 16 to 32
+        MOV     R3, R0, ASR #16
+
+        MOV     PC, R14
+
+; *****************************************************************************
+
+SetMousePosn ROUT
+        Push    "R2, R3, R11, R14"
+        MOV     R11, #KeyWorkSpace
+
+        BL      GetCoordPair
+
+; now check point is within bounding box
+
+        LDR     R0, MouseBoundLCol
+        CMP     R2, R0
+        LDRGE   R0, MouseBoundRCol
+        CMPGE   R0, R2
+        LDRGE   R0, MouseBoundBRow
+        CMPGE   R3, R0
+        LDRGE   R0, MouseBoundTRow
+        CMPGE   R0, R3
+
+        BLGE    SetMousePosnRegs
+
+        Pull    "R2, R3, R11, PC"
+
+SetMousePosnRegs
+        MOV     R11, #KeyWorkSpace
+        STR     R2, MouseX
+        STR     R3, MouseY
+        B       FlushMouse
+
+; *****************************************************************************
+;
+;       StoreCoordPair - Stores X,Y coords in R2,R3 in R1+1..R1+4
+;       subtracts graphics origin
+
+StoreCoordPair ROUT
+
+        LDR     R0, [WsPtr, #OrgX]      ; subtract off origin
+        SUB     R2, R2, R0
+
+        STRB    R2, [R1, #1]            ; store lo-byte of X
+        MOV     R2, R2, LSR #8
+        STRB    R2, [R1, #2]            ; store hi-byte of X
+
+        LDR     R0, [WsPtr, #OrgY]      ; subtract off origin
+        SUB     R3, R3, R0
+
+        STRB    R3, [R1, #3]            ; store lo-byte of Y
+        MOV     R3, R3, LSR #8
+        STRB    R3, [R1, #4]            ; store hi-byte of Y
+
+        MOV     PC, R14
+
+; *****************************************************************************
+
+ReadMousePosn ROUT
+ [ AssemblePointerV :LAND: {TRUE}
+        Push    "r0-r3, r9-r11, lr"
+        BL      PollPointer             ; update mouse position on a read
+        LDR     r1, [sp, #1*4]          ; reload pointer to buffer
+        MOV     R11, #KeyWorkSpace
+
+        LDR     R2, MouseX              ; get mouse X
+        LDR     R3, MouseY              ; get mouse Y
+        BL      StoreCoordPair
+        Pull    "r0-r3, r9-r11, pc"
+
+ |
+        Push    "R2, R3, R11, R14"
+        MOV     R11, #KeyWorkSpace
+
+        LDR     R2, MouseX              ; get mouse X
+        LDR     R3, MouseY              ; get mouse Y
+        BL      StoreCoordPair
+
+        Pull    "R2, R3, R11, PC"
+ ]
+
+; *****************************************************************************
+
+SetPointerPosn ROUT
+        Push    "R2, R3, R14"
+
+        BL      GetCoordPair
+
+        STR     R2, [WsPtr, #PointerX]
+        STR     R3, [WsPtr, #PointerY]
+
+        Pull    "R2, R3, PC"
+
+; *****************************************************************************
+
+ReadPointerPosn ROUT
+        Push    "R2, R3, R14"
+
+        LDR     R2, [WsPtr, #PointerX]
+        LDR     R3, [WsPtr, #PointerY]
+        BL      StoreCoordPair
+
+        Pull    "R2, R3, PC"
+
+; *****************************************************************************
+;
+;       FlushMouse - Flush mouse buffer
+;
+; out:  All registers preserved
+
+FlushMouse ROUT
+        Push    "R0-R2, R14"
+        MOV     R0, #21
+        MOV     R1, #Buff_Mouse
+        SWI     XOS_Byte
+        Pull    "R0-R2, PC"
+
+        END
diff --git a/s/vdu/vduswis b/s/vdu/vduswis
new file mode 100644
index 0000000000000000000000000000000000000000..131efe1e9e49268ed86e4c7776a49ef1b8c37a27
--- /dev/null
+++ b/s/vdu/vduswis
@@ -0,0 +1,2001 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduSWIs
+
+        GBLL    FixRemovePages
+FixRemovePages  SETL {TRUE}
+
+        MACRO
+        BranchNotJustUs $label, $vector, $tmp1, $tmp2
+        MOV     R11, #0
+        [ AssemblingArthur
+         [ $tmp1=R11
+         LDR     R12, [R11, #VecPtrTab+$vector*4]! ; load vector node
+         |
+         LDR     R12, [R11, #VecPtrTab+$vector*4]  ; load vector, leave R11=0
+         ]
+        CMP     R12, #&03800000
+        BCC     $label
+        |
+         [ Module
+         LDR     $tmp1, [R11, #VecPtrTab+$vector*4]  ; load vector node
+         LDR     R12, [$tmp1]                    ; get first address on chain
+         EOR     $tmp2, R12, #&01000000
+         CMP     $tmp2, #&00020000
+         BCS     $label
+         |
+         LDR     $tmp1, [R11, #VecPtrTab+$vector*4]  ; load vector node
+         LDR     R12, [$tmp1]                    ; get first address on chain
+         [ $vector=SpriteV
+         EOR     $tmp2, R12, #&01600000
+         |
+         RSB     $tmp2, R12, #&01E00000
+         ]
+         CMP     $tmp2, #&10000
+         BCS     $label
+         ]
+        ]
+        MEND
+
+        MACRO
+        BranchNotJustUsWrch $label
+        BranchNotJustUs $label, WrchV, R11, R14
+        MEND
+
+        MACRO
+        SWIEntry $swiname
+        &       OS_$swiname
+        &       SWI$swiname-(.+4)
+        MEND
+
+        [ :LNOT: AssemblingArthur
+SetUpSWIs ROUT
+        LDR     R1, =SvcTable
+        ADR     R2, SWITable
+10
+        LDMIA   R2!, {R3, R4}           ; R3 = SWI number; R4 = address offset
+        CMP     R3, #-1                 ; end of table ?
+        ADDNE   R4, R4, R2              ; no, then R4 = address
+        STRNE   R4, [R1, R3, LSL #2]
+        BNE     %BT10
+        MOV     PC, R14
+
+SWITable
+        SWIEntry        ReadVduVariables
+        SWIEntry        ReadPalette
+        SWIEntry        ReadModeVariable
+        SWIEntry        RemoveCursors
+        SWIEntry        RestoreCursors
+        SWIEntry        CheckModeValid
+        SWIEntry        ClaimScreenMemory
+        SWIEntry        Plot
+        SWIEntry        WriteN
+        SWIEntry        Write0
+        SWIEntry        WriteS
+        SWIEntry        SetECFOrigin
+        SWIEntry        ChangedBox
+        &       -1
+        ]
+
+; *****************************************************************************
+;
+;       SWIRVV - SWI ReadVduVariables handler
+;
+; in:   R0 -> input block
+;       R1 -> output block
+;
+; out:  All registers preserved
+;
+;       The input block consists of a string of words, terminated by -1
+;       Each word indicates which VDU variable to read.
+;       The (word) values of these variables are put in sequence into the
+;       output block (no terminator is put in this block). Invalid variables
+;       are returned as zero.
+
+SWIRVV  ROUT
+SWIReadVduVariables                     ; alternative symbol used for init'ing
+        TST     R0, #3                  ; input block not aligned
+        TSTEQ   R1, #3                  ; or output block not aligned
+        ExitSWIHandler NE               ; then exit
+        Push    R9
+        VDWS    WsPtr
+        MOV     R10, #0                 ; offset into both blocks
+10
+        LDR     R11, [R0, R10]
+        CMP     R11, #-1                ; end of block ?
+        Pull    R9, EQ
+        ExitSWIHandler EQ
+
+        CMP     R11, #&100              ; is it fudged "code" variable
+        BCS     %FT30                   ; yes, then use special code
+
+        CMP     R11, #(SWIRVVTabModeEnd-SWIRVVTab) ; a mode variable
+        ADRCC   R9, SWIRVVTab           ; if so then point to them
+        BCC     %FT20
+        SUB     R11, R11, #&80          ; base for non-mode variables
+        CMP     R11, #(SWIRVVTabEnd-SWIRVVTabModeEnd)
+        ADRCCL  R9, SWIRVVTabModeEnd
+20
+        LDRCCB  R11, [R9, R11]          ; yes, then load offset
+        LDRCC   R11, [WsPtr, R11, LSL #2] ; and then load variable
+        MOVCS   R11, #0                 ; no, then put zero in it
+25
+        STR     R11, [R1, R10]
+        ADD     R10, R10, #4
+        B       %BT10
+
+; Special code to read window width for Bruce et al.
+
+30
+        SUB     R11, R11, #&100
+        CMP     R11, #2                 ; 0 => WindowWidth, 1 => WindowHeight
+        BCS     %BT20                   ; not one of those, so illegal
+
+        Push    "R0-R7"
+        LDR     R6, [WsPtr, #CursorFlags]
+        TST     R6, #Vdu5Bit
+        ADDEQ   R4, WsPtr, #TWLCol      ; if VDU 4 mode, use text window
+        ADDNE   R4, WsPtr, #GWLCol      ; else use graphics window
+        LDMIA   R4, {R0-R3}
+        SUB     R0, R2, R0              ; R0 = horizontal width -1
+        SUB     R1, R1, R3              ; R1 = vertical height -1
+
+        BEQ     %FT32
+
+        ADD     R2, WsPtr, #GCharSizes  ; R2=GCharSizeX; R3=GCharSizeY
+        LDMIA   R2, {R2-R5}             ; R4=GCharSpaceX; R5=GCharSpaceY
+
+        TST     R6, #2                  ; if going from right to left
+        SUBNE   R2, R2, #1              ; then width reduction = sizex-1
+        MOVEQ   R2, #0                  ; else 0
+
+        TST     R6, #4                  ; if going from bottom to top
+        RSBNE   R3, R3, #1              ; then -height reduction = 1-sizey
+        MOVEQ   R3, #0                  ; else 0
+
+        SUB     R7, R0, R2              ; R7 = width-1-(width reduction)
+        DivRem  R0, R7, R4, R2          ; R0 = (width-(1 or sizex))DIV spacex
+
+        SUB     R7, R3, R1              ; R7 = height-(1-height reduction)
+        DivRem  R1, R7, R5, R3          ; R1 = (height-(1 or sizey))DIV spacey
+32
+        TST     R6, #8                  ; are X and Y reversed ?
+        EORNE   R0, R0, R1              ; yes, then swap
+        EORNE   R1, R0, R1
+        EORNE   R0, R0, R1
+
+        TST     R6, #Vdu5Bit
+        BNE     %FT35
+        TST     R6, #1                  ; is it "81 column mode ?" and VDU 4
+        ADDNE   R0, R0, #1              ; yes, then extra column
+35
+        TEQ     R11, #0                 ; reading width ?
+        MOVEQ   R11, R0                 ; yes, then use X
+        MOVNE   R11, R1                 ; no, use Y
+
+        Pull    "R0-R7"                 ; restore registers
+        B       %BT25                   ; and store result away
+
+
+SWIRVVTab
+
+; Note these first variables should be the same as in SWI ReadModeVariable
+
+        RVVT    ModeFlags
+        RVVT    ScrRCol
+        RVVT    ScrBRow
+        RVVT    NColour
+        RVVT    XEigFactor
+        RVVT    YEigFactor
+        RVVT    LineLength
+        RVVT    ScreenSize
+        RVVT    YShftFactor
+        RVVT    Log2BPP
+        RVVT    Log2BPC
+        RVVT    XWindLimit
+        RVVT    YWindLimit
+SWIRVVTabModeEnd
+
+        RVVT    GWLCol
+        RVVT    GWBRow
+        RVVT    GWRCol
+        RVVT    GWTRow
+        RVVT    TWLCol
+        RVVT    TWBRow
+        RVVT    TWRCol
+        RVVT    TWTRow
+        RVVT    OrgX
+        RVVT    OrgY
+        RVVT    GCsX
+        RVVT    GCsY
+        RVVT    OlderCsX
+        RVVT    OlderCsY
+        RVVT    OldCsX
+        RVVT    OldCsY
+        RVVT    GCsIX
+        RVVT    GCsIY
+        RVVT    NewPtX
+        RVVT    NewPtY
+        RVVT    ScreenStart
+        RVVT    DisplayStart
+        RVVT    TotalScreenSize
+        RVVT    GPLFMD
+        RVVT    GPLBMD
+        RVVT    GFCOL
+        RVVT    GBCOL
+        RVVT    TForeCol
+        RVVT    TBackCol
+        RVVT    GFTint
+        RVVT    GBTint
+        RVVT    TFTint
+        RVVT    TBTint
+        RVVT    MaxMode
+        RVVT    GCharSizeX
+        RVVT    GCharSizeY
+        RVVT    GCharSpaceX
+        RVVT    GCharSpaceY
+        RVVT    HLineAddr
+        RVVT    TCharSizeX
+        RVVT    TCharSizeY
+        RVVT    TCharSpaceX
+        RVVT    TCharSpaceY
+        RVVT    GcolOraEorAddr
+        RVVT    VIDCClockSpeed
+SWIRVVTabEnd
+
+        ALIGN
+
+        [ {FALSE}
+
+; *****************************************************************************
+;
+;       SWISetVduVariables - SWI OS_SetVduVariables handler
+;
+; in:   R0 -> block of variable numbers (word aligned)
+;       R1 -> block of variable values (word aligned)
+;
+; out:  All registers preserved
+;
+;       The block pointed to by R0 consists of a string of words, terminated
+;       by -1. Each word indicates which variable to write. The corresponding
+;       value in the R1 block is written to this variable. Invalid variables
+;       are ignored.
+;
+
+SWISetVduVariables ROUT
+        TST     R0, #3                  ; index block not aligned
+        TSTEQ   R1, #3                  ; or value block not aligned
+        ExitSWIHandler NE               ; then exit
+        Push    R9
+        VDWS    WsPtr
+        MOV     R10, #0                 ; offset into both blocks
+10
+        LDR     R11, [R0, R10]
+        CMP     R11, #-1                ; end of block ?
+        Pull    R9, EQ
+        ExitSWIHandler EQ
+
+        TEQ     R11, #VduExt_XEigFactor
+        TEQNE   R11, #VduExt_YEigFactor
+        BNE     %FT25
+
+        CMP     R11, #(SWIRVVTabModeEnd-SWIRVVTab) ; a mode variable
+        ADRCC   R9, SWIRVVTab           ; if so then point to them
+        SUBCS   R11, R11, #&80          ; base for non-mode variables
+        ADRCS   R9, SWIRVVTabModeEnd
+20
+        LDRB    R11, [R9, R11]          ; yes, then load offset
+        LDR     R9, [R1, R10]           ; load value
+        STR     R9, [WsPtr, R11, LSL #2] ; store in variable
+        Push    "R0-R5,R14"
+        BL      IEG                     ; update ext. cursor
+                                        ; on exit, R2=XEigFactor, R3=YEigFactor
+        SUBS    R0, R2, R3              ; XEigFactor-YEigFactor
+        MOVLT   R0, #2                  ; X<Y => 2 (vert rect)
+        MOVGT   R0, #1                  ; X>Y => 1 (horz rect) else 0 (square)
+        STR     R0, [WsPtr, #AspectRatio]
+
+; mouse bounding box, mouse position ???
+
+        Pull    "R0-R5,R14"
+25
+        ADD     R10, R10, #4
+        B       %BT10
+
+        ]
+
+; *****************************************************************************
+;
+;       SWIReadModeVar - Handler for SWI OS_ReadModeVariable
+;
+; in:   R0 = screen mode you want the information for, -1 => current mode
+;       R1 = number of variable to read
+;
+; out:  R2 = value of variable
+;
+
+; Note: the algorithms used to derive the variables for mode selectors
+; are duplicated in source.vdudriver, in GenerateModeSelectorVars
+
+SWIReadModeVar ROUT
+SWIReadModeVariable                     ; alternative symbol used for init'ing
+        Push    "R0,R1,R14"
+
+        CMP     R1, #(RMVTabEnd-RMVTab) ; valid variable number ?
+        BCS     BadReadModeVar          ; no, then exit
+
+        ; AMG - note that the new sprite mode word code assumes that this
+        ;       test has been done!
+
+        VDWS    WsPtr
+
+; AMG - add readmodevariable returns for new format sprite mode words. Much of
+;       this is munging the word passed in. A new sprite mode word can be
+;       distinguished from a pointer to a mode selector structure because
+;       b0 will be set in the new sprite mode word. 11/3/93
+
+; TMD - serious optimisation introduced 17-Feb-93
+; TMD - additional optimisation for current mode added 16-Mar-93
+
+; AMG - Fix bug MED-00414 ... need to support eig=0 modes here 26-Oct-93
+
+        LDR     r11, [WsPtr, #ModeNo]   ; get current mode
+
+        CMP     r0, #-1                 ; if explicitly asking for current mode
+        BEQ     RMVForCurrentMode       ; then use optimised code
+
+        CMP     r0, #&100
+        BCC     %FT11                   ; an old style mode number, branch past
+                                        ; the new code below
+        TST     r0, #&01
+        BNE     NewSpriteModeWord       ; b0 set, so it is a NewSpriteModeWord
+                                        ; rather than a pointer to a mode selector
+
+  [ ModeSelectors
+
+; it's a mode selector, so check if valid first
+
+        BL      ValidateModeSelector
+        BVS     BadReadModeVar
+
+; check if variable is in workspace list
+
+        ADD     r10, r0, #ModeSelector_ModeVars         ; point at list
+        BL      CheckWorkspaceList                      ; check list
+        BCC     GoodReadModeVar                         ; [it was in list, so exit]
+
+; not in list, so deduce from other parms
+; (we know the variable number is in range)
+
+        LDR     pc, [pc, r1, LSL #2]
+        NOP
+        &       RMVMS_ModeFlags
+        &       RMVMS_ScrRCol
+        &       RMVMS_ScrBRow
+        &       RMVMS_NColour
+        &       RMVMS_XEigFactor
+        &       RMVMS_YEigFactor
+        &       RMVMS_LineLength
+        &       RMVMS_ScreenSize
+        &       RMVMS_YShftFactor
+        &       RMVMS_Log2BPP
+        &       RMVMS_Log2BPC
+        &       RMVMS_XWindLimit
+        &       RMVMS_YWindLimit
+
+RMVMS_ModeFlags
+RMVMS_YShftFactor
+        MOV     r2, #0                                  ; default modeflags, yshftfactor = 0
+        B       GoodReadModeVar
+
+RMVMS_ScrRCol
+        LDR     r2, [r0, #ModeSelector_XRes]            ; default scrcol = (xres>>3)-1
+        MOV     r2, r2, LSR #3
+        SUB     r2, r2, #1
+        B       GoodReadModeVar
+
+RMVMS_ScrBRow
+        LDR     r2, [r0, #ModeSelector_YRes]            ; default scrbrow = (yres>>3)-1
+        MOV     r2, r2, LSR #3
+        SUB     r2, r2, #1
+        B       GoodReadModeVar
+
+RMVMS_NColour
+        LDR     r2, [r0, #ModeSelector_PixelDepth]
+        CMP     r2, #6                                  ; if pixel depth is sensible
+        ADRCCL  r11, NColourTable                       ; then lookup in table
+        LDRCC   r2, [r11, r2, LSL #2]
+        MOVCS   r2, #1                                  ; else return 1
+        B       GoodReadModeVar
+
+; TMD 09-Dec-93
+; New algorithms for xeig, yeig from Roger:
+;       xeig = 1: yeig = 1
+;       if yres<xres/2 OR yres<400 then yeig = 2
+;       if (xres<<xeig)<(yres<<yeig) then xeig = 2
+
+ [ RogerEXEY
+RMVMS_XEigFactor
+        LDR     r2, [r0, #ModeSelector_XRes]
+        LDR     r11, [r0, #ModeSelector_YRes]
+        CMP     r11, r2, LSR #1                         ; if yres < xres/2
+        CMPCS   r11, #400                               ; or yres < 400
+        MOVCC   r11, r11, LSL #2                        ; then yeig = 2
+        MOVCS   r11, r11, LSL #1                        ; else yeig = 1
+        CMP     r11, r2, LSL #1                         ; if (xres<<1) < (yres<<yeig)
+        MOVHI   r2, #2                                  ; then xeig = 2
+        MOVLS   r2, #1                                  ; else xeig = 1
+        B       GoodReadModeVar
+
+RMVMS_YEigFactor
+        LDR     r2, [r0, #ModeSelector_XRes]
+        LDR     r11, [r0, #ModeSelector_YRes]
+        CMP     r11, r2, LSR #1                         ; if yres < xres/2
+        CMPCS   r11, #400                               ; or yres < 400
+        MOVCC   r2, #2                                  ; then yeig = 2
+        MOVCS   r2, #1                                  ; else yeig = 1
+        B       GoodReadModeVar
+ |
+RMVMS_XEigFactor
+        MOV     r2, #1                                  ; default xeig = 1
+        B       GoodReadModeVar
+
+RMVMS_YEigFactor
+        LDR     r2, [r0, #ModeSelector_XRes]
+        LDR     r11, [r0, #ModeSelector_YRes]
+        CMP     r11, r2, LSR #1                         ; if yres < xres/2
+        MOVCC   r2, #2                                  ; then yeig = 2
+        MOVCS   r2, #1                                  ; else yeig = 1
+        B       GoodReadModeVar
+ ]
+
+RMVMS_LineLength
+        LDR     r2, [r0, #ModeSelector_XRes]
+RMVMS_ShiftByPixDepthMinus3
+        LDR     r11, [r0, #ModeSelector_PixelDepth]
+        CMP     r11, #6                                 ; if out of range
+        MOVCS   r11, #0                                 ; use log2bpp=0
+        MOV     r2, r2, LSL r11
+        MOV     r2, r2, LSR #3                          ; ll = (xres << pixdepth) >> 3
+        B       GoodReadModeVar
+
+RMVMS_ScreenSize
+        LDR     r2, [r0, #ModeSelector_XRes]
+        LDR     r11, [r0, #ModeSelector_YRes]
+        MUL     r2, r11, r2                             ; xres * yres
+        B       RMVMS_ShiftByPixDepthMinus3
+
+RMVMS_Log2BPP
+RMVMS_Log2BPC
+        LDR     r2, [r0, #ModeSelector_PixelDepth]
+        CMP     r2, #6                                  ; range check
+        MOVCS   r2, #0
+        B       GoodReadModeVar
+
+RMVMS_XWindLimit
+        LDR     r2, [r0, #ModeSelector_XRes]            ; default xwindlimit = xres-1
+        SUB     r2, r2, #1
+        B       GoodReadModeVar
+
+RMVMS_YWindLimit
+        LDR     r2, [r0, #ModeSelector_YRes]            ; default ywindlimit = yres-1
+        SUB     r2, r2, #1
+        B       GoodReadModeVar
+  |
+
+; else drop thru into ...
+
+  ]
+
+11
+        CMP     r0, r11                 ; if implicitly asking for current mode (and mode <> mode selector)
+        BEQ     RMVForCurrentMode       ; then use optimised code
+
+        BIC     r11, r0, #&80           ; clear shadow bit
+
+        BranchIfKnownMode r11, %FA50
+
+; not known mode, so look
+
+        Push    "r2-r4"
+        MOV     r2, r11
+        BL      OfferModeExtensionAnyMonitor
+        MOVEQ   r11, r4                 ; if service responded to, save pointer to workspace list
+        Pull    "r2-r4"
+        BNE     BadReadModeVar          ; exit if mode not known about
+
+; now search down list checking for variable number
+
+        ADD     r10, r11, #8            ; skip list type and base mode
+        BL      CheckWorkspaceList      ; look up variable in list
+        BCC     GoodReadModeVar         ; if there then exit
+
+; not in workspace list provided, so use base mode to look up in MOS's table
+
+        LDR     r11, [r11, #4]          ; load workspace list base mode
+        BranchIfKnownMode r11, %FA50
+
+; panic - base mode unrecognised
+; existing code simply loads off end of table! - instead of that, return value zero
+; alternatively we could return carry set, but that might cause backward compatibility problems (maybe)
+
+        MOV     r2, #0
+        B       GoodReadModeVar
+
+50
+        ADR     r10, RMVTab
+        LDRB    r10, [r10, r1]          ; R10 = offset in each mode table * 2
+                                        ; if bit 0 set, then word value
+        ADRL    r14, Vwstab
+        LDR     r11, [r14, r11, LSL #2] ; get offset to table for this mode
+        ADD     r11, r11, r14           ; convert to pointer
+        MOVS    r10, r10, LSR #1        ; put byte/word flag into carry
+        LDRCCB  r2, [r11, r10]          ; load either a byte
+        LDRCS   r2, [r11, r10]          ; or a word out of table
+
+; and drop thru to GoodModeVar
+
+GoodReadModeVar
+        Pull    "R0,R1,R14"
+        BIC     R14, R14, #C_bit        ; indicate successful read
+        ExitSWIHandler
+
+BadReadModeVar
+        Pull    "R0,R1,R14"
+        ORR     R14, R14, #C_bit        ; indicate bad read
+        ExitSWIHandler
+
+; CheckWorkspaceList - Check a mode variable (index, value) list for a match
+; in:   r1 = variable number
+;       r10 -> list
+;
+; out:  If match found, then
+;         r2 = value
+;         C=0
+;       else
+;         C=1
+;       endif
+;       r10 corrupted in both cases
+
+CheckWorkspaceList ENTRY
+10
+        LDR     r14, [r10], #8          ; load next index (and skip index+value)
+        CMP     r14, #-1                ; if end of list
+        EXIT    EQ                      ; then not in workspace list, so exit (C=1 from CMP)
+        TEQ     r14, r1
+        BNE     %BT10
+
+        LDR     r2, [r10, #-4]          ; load value of variable
+        CLC                             ; clear carry
+        EXIT
+
+RMVForCurrentMode
+        ADR     r10, MVToVVTable
+        LDR     r10, [r10, r1, LSL #2]
+        LDR     r2, [WsPtr, r10]
+        B       GoodReadModeVar
+
+MVToVVTable
+        &       ModeFlags
+        &       ScrRCol
+        &       ScrBRow
+        &       NColour
+        &       XEigFactor
+        &       YEigFactor
+        &       LineLength
+        &       ScreenSize
+        &       YShftFactor
+        &       Log2BPP
+        &       Log2BPC
+        &       XWindLimit
+        &       YWindLimit
+
+; Note these should be the same as the first few in SWI ReadVduVariables
+
+RMVTab
+        RMVT    ModeFlags, W    ; was B
+        RMVT    ScrRCol, W      ; was B
+        RMVT    ScrBRow, W      ; was B
+        RMVT    NColour, W
+        RMVT    XEigFactor, W   ; was B
+        RMVT    YEigFactor, W   ; was B
+        RMVT    LineLength, W
+        RMVT    ScreenSize, W
+        RMVT    YShftFactor, W  ; was B
+        RMVT    Log2BPP, W      ; was B
+        RMVT    Log2BPC, W      ; was B
+        RMVT    XWindLimit, W
+        RMVT    YWindLimit, W
+RMVTabEnd
+        ALIGN
+
+; *****************************************************************************
+;
+;       NewSpriteModeWord, called from ReadModeVariable
+;
+; in:   R0 = new sprite mode word
+;       R1 = number of variable to read
+;       (R0,R1,R14 stacked)
+;
+; out:  R2 = value of variable
+;
+; Return parameters as follows:
+
+; (Unknown types will return an error. Invalid mode variable numbers will already
+;  have been weeded out at the entry to ReadModeVariable)
+
+;        0 ModeFlags          Error
+;        1 ScrRCol            Error
+;        2 ScrBRow            Error
+;        3 NColour            derived from bpp passed in (255 for 8bpp)
+;        4 XEigFactor         returns 0,1,2 for 180,90,45 dpi, error otherwise
+;        5 YEigFactor         as XEigFactor
+;        6 LineLength         Error
+;        7 ScreenSize         Error
+;        8 YShftFactor        Error
+;        9 Log2BPP            returns 0,1,2,3,4,5 for T=1-6, error otherwise
+;       10 Log2BPC            as Log2BPP
+;       11 XWindLimit         Error
+;       12 YWindLimit         Error
+
+NewSpriteModeWord ROUT
+
+; validate the sprite type. Types 1-6 only at present. Type 0 is a
+; mode number, but if it comes this way, it's bad, since mode >= 256
+
+        MOVS    r14, r0, LSR #27        ; shift type word into b4-b0
+        BEQ     BadReadModeVar          ; zero is bad cos mode >= 256
+        ;if it's an unknown one, apply a substitute
+        CMP     r14, #SpriteType_MAX
+        MOVCS   r14, #SpriteType_Substitute
+
+; it's a valid type, now branch by the mode variable number
+
+        ADR     r2, NewSpriteModeWordRoutines
+        ADD     pc, r2, r1, LSL #2      ; and despatch it
+
+NewSpriteModeWordRoutines
+        B       NSM_modeflags  ;        0 ModeFlags             (zero)
+        B       BadReadModeVar ;        1 ScrRCol               Error
+        B       BadReadModeVar ;        2 ScrBRow               Error
+        B       NSM_ncol       ;        3 NColour
+        B       NSM_xeig       ;        4 XEigFactor
+        B       NSM_yeig       ;        5 YEigFactor
+        B       BadReadModeVar ;        6 LineLength            Error
+        B       BadReadModeVar ;        7 ScreenSize            Error
+        B       NSM_yshftfactor ;       8 YShftFactor           (zero)
+        B       NSM_bpp        ;        9 Log2BPP
+        B       NSM_bpp        ;       10 Log2BPC               as Log2BPP
+        B       BadReadModeVar ;       11 XWindLimit            Error
+        B       BadReadModeVar ;       12 YWindLimit            Error
+
+; entry conditions here
+; r14 - sprite type in b0-b6 for a word offset
+; r0  - mode word
+; r1  - mode variable (no longer needed at this point)
+
+NSM_ncol ; r14 is already the type bits shifted down to b0-b6, ie a word offset
+        ADRL    r2, NColourTable -4     ; results table (adjust for T=0 never occurring here)
+        LDR     r2, [r2, r14, LSL #2]   ; pull the correct value
+        CMP     r2, #63
+        MOVEQ   r2, #255                ; make sure we return 255 not 63
+        B       GoodReadModeVar         ; and return happily
+
+NSM_bpp
+        ADR     r2, NSM_bpptable-4      ; (adjusted for T=0 never occurring here)
+        LDR     r2, [r2, r14, LSL #2]
+        B       GoodReadModeVar
+
+NSM_bpptable
+         ; note, yes - I know this could be type-1, but at some point some new type
+         ; will break the relationship so it's a table from day 1 to cope with this
+        &       0, 1, 2, 3, 4, 5
+
+NSM_eig_mask
+        &       &00001FFF
+
+NSM_yeig
+        MOV     r0, r0, LSR #14         ; move ydpi into b0-b12
+        B       %FT10
+
+NSM_xeig
+        MOV     r0, r0, LSR #1          ; move xdpi into b0-b12
+10
+        LDR     r14, =&00001FFF         ; mask for dpi bits
+        AND     r0, r0, r14
+
+        CMP     r0, #180
+        MOVEQ   r2, #0
+        BEQ     GoodReadModeVar
+
+        CMP     r0, #22
+        CMPNE   r0, #23
+        MOVEQ   r2, #3
+        BEQ     GoodReadModeVar
+
+        TEQ     r0, #(45 :SHL: 2), 2    ; check if 45   (EQ,CC if so)
+        CMPNE   r0, #90                 ; or 90         (EQ,CS if so)
+        BNE     BadReadModeVar
+        MOVCC   r2, #2                  ; 45 => xeig=2
+        MOVCS   r2, #1                  ; 90 => xeig=1
+        B       GoodReadModeVar
+
+NSM_modeflags
+NSM_yshftfactor
+        MOV     r2, #0                  ; both these return zero
+        B       GoodReadModeVar
+
+; *****************************************************************************
+;
+;       SWICheckModeValid - The 'Can I get into this mode?' call
+;
+; in:   r0 = mode you want to get into (may be pointer to mode selector)
+; out:  C=0 => yes you can, r0 preserved
+;       C=1 => no you can't ...
+;         r0 = -1 => ... because the mode doesn't exist
+;           r1 = substitute mode
+;           r1 = -2 => not enough memory for even the substitute mode !
+;         r0 = -2 => ... because not enough memory
+;
+
+SWICheckModeValid ROUT
+        VDWS    WsPtr
+        Push    "r1,r9,lr"
+        BL      FindOKMode              ; out: r1 = substitute mode
+        BVS     %FT90
+
+ [ ModeSelectors
+        CMP     r1, #&100               ; if it's a mode number
+        BICCC   r10, r1, #&80           ; then knock off shadow bit
+        MOVCS   r10, r1                 ; else don't
+ |
+        BIC     r10, r1, #&80           ; only use substitute mode (11/8/88)
+ ]
+        MOV     r11, r10
+        BL      PushModeInfo
+        BVS     %FT90
+
+        LDR     r11, [r13, #wkScreenSize] ; get screen size for this mode
+        ADD     r13, r13, #PushedInfoSize ; junk stacked mode table + VIDC info
+
+        MOV     r10, r1, LSR #7         ; 'shadow' bit (NB will be 0 or 1)
+ [ ModeSelectors
+        CMP     r10, #2                 ; except if its a mode selector
+        MOVCS   r10, #0                 ; in which case no shadow
+ ]
+        LDROSB  r9, Shadow
+        TEQ     r0, r1                  ; if substitute different from original
+        MOV     r0, r1                  ; (always set r0 to be substitute mode)
+        MOVNE   r14, #-1                ; then indicate original is silly
+        MOVEQ   r14, #0                 ; else indicate sensible
+        TEQEQ   r9, #0                  ; and if shadow 0
+        MOVEQ   r10, #1                 ; then force shadow mode
+
+        LDR     r9, [WsPtr, #TotalScreenSize] ; maximum allowed amount
+        CMP     r9, r11, LSL r10        ; compare with this (*2 if shadow)
+                                        ; C=0 => No room
+        TEQ     r14, #0                 ; NZ => silly, CC => no room
+
+        MOV     r1, #-2                 ; for if silly mode and bad space
+        Pull    r1, EQ                  ; if not silly, restore old R1
+        ADDNE   r13, r13, #4            ; else junk stacked R1
+        MOVHI   r1, r0                  ; silly mode, ok space, R1=subst. mode
+
+        MOVCC   r0, #-2                 ; if no room, indicate it
+        MOVNE   r0, #-1                 ; but silly overrides this
+
+        Pull    "r9, r14"
+        CMP     r0, #-2                 ; C=1 => bad exit
+        BICCC   r14, r14, #C_bit
+        ORRCS   r14, r14, #C_bit
+        ExitSWIHandler
+
+; exit point in case of error from FindOKMode or PushModeInfo
+
+90
+        Pull    "r1, r9, r14"
+        MOV     r0, #-1                 ; indicate no such mode
+        MOV     r1, #-2                 ; and no substitute mode
+        ORR     r14, r14, #C_bit
+        ExitSWIHandler
+
+; *****************************************************************************
+;
+;       FindOKMode - Convert mode number into an appropriate one for
+;                    this monitor type
+;
+; in:   r0 = original mode specifier
+;
+; out:  If no error, then
+;         r0 preserved
+;         r1 = appropriate mode specifier
+;         V = 0
+;       else
+;         r0 -> error
+;         r1 corrupted
+;         V = 1
+;       endif
+;       All other registers preserved
+;
+
+FindOKMode ROUT
+        Push    "r0,r2-r4,r10,r11,lr"
+        BL      ReadMonitorType
+ [ ModeSelectors
+        CMP     r0, #&100               ; if it's a mode number
+        BICCC   r2, r0, #&80            ; then knock off shadow bit
+        MOVCS   r2, r0                  ; else don't
+ |
+        BIC     r2, r0, #&80
+ ]
+        BL      OfferModeExtension
+        BNE     %FT05
+
+; service claimed, so return with this number
+
+        MOV     r1, r0
+        CLRV
+        Pull    "r0,r2-r4,r10,r11,pc"
+
+05
+
+; not claimed, so r2 (=mode without shadow) and r3 (=monitortype) are preserved
+
+        Pull    "r0"
+ [ ModeSelectors
+        CMP     r0, #&100               ; if a mode selector and not responded to
+        BCS     %FT30                   ; then return error
+ ]
+
+        MOV     r10, r2                 ; mode without shadow bits
+        MOV     r1, r0                  ; start from existing mode
+
+        CMP     r3, #NumMonitorTypes    ; monitor type must be in range
+        BCS     %FT10                   ; if not then must issue service
+        CMPCC   r10, #NumModes          ; and mode must be in range
+        MOVCC   r11, #NumModes
+        MLACC   r11, r3, r11, r10       ; then form monitortype*numberofmodes + modenumber
+        ADRCCL  r14, BigVIDCTable       ; point to big table
+        LDRCC   r11, [r14, r11, LSL #2] ; and load offset
+        CMPCC   r11, #-1                ; CS if mode number or monitor type out of range, or if not known in table
+        BCC     %FT20                   ; else it's known about, so OK
+
+; known monitor type, but unknown mode, so find substitute
+
+        ADR     r14, SubstModeTable
+        LDR     r11, [r14, r3, LSL #2]
+        ADD     r1, r11, r14
+05
+        BL      FindSubstitute
+        Pull    "r2-r4,r10,r11, pc"     ; exit VC or VS
+
+; unknown monitor type, so offer service
+
+10
+        MOV     r2, r10
+        MOV     r1, #Service_ModeTranslation
+        IssueService
+        TEQ     r1, #0
+        MOVEQ   r1, r2                  ; if claimed, then use module's mode
+        BEQ     %FT20
+
+; unknown monitor type
+; if monitor type 7 (file), use substitution table for VGA (reasonable assumption)
+
+        TEQ     r3, #7
+        ADREQ   r1, SubstType3
+        BEQ     %BT05
+
+        MOV     r1, #0                  ; else panic and use mode 0
+
+20
+        CLRV
+        Pull    "r2-r4,r10,r11, pc"
+
+30
+        ADRL    r0, ErrorBlock_ModeNotAvailable ; then return error
+ [ International
+        BL      TranslateError
+ ]
+        SETV                            ; error exit
+        Pull    "r2-r4,r10,r11, pc"
+
+SubstModeTable
+        &       SubstType01-SubstModeTable
+        &       SubstType01-SubstModeTable
+        &       SubstType2-SubstModeTable
+        &       SubstType3-SubstModeTable
+        &       SubstType4-SubstModeTable
+
+SubstType01
+        =        0,  8, 12, 15
+SubstType2
+        =       23, 23, 23, 23
+SubstType3
+        =       25, 26, 27, 28
+SubstType4
+        =       29, 30, 31, 32
+
+; *****************************************************************************
+;
+;       FindSubstitute - Find substitute mode with right no. of bpp
+;
+; in:   r1 -> table of 4 bytes; subst. modes for 1, 2, 4, 8 bpp respectively
+;       r10 = mode specifier to be tested (shadow bit clear)
+;
+; out:  If no error, then
+;         r0 preserved
+;         r1 = substitute mode
+;         V=0
+;       else
+;         r0 -> error
+;         r1 preserved
+;       endif
+;       r11 corrupted, all other registers preserved
+;
+
+FindSubstitute ENTRY
+        MOV     r11, #0
+        BL      PushModeInfoAnyMonitor
+        EXIT    VS                              ; if error, then exit now
+        LDR     r11, [r13, #wkLog2BPP]
+        ADD     r13, r13, #PushedInfoSize
+        CMP     r11, #4
+        MOVCS   r11, #0
+        LDRB    r1, [r1, r11]
+        CLRV
+        EXIT
+
+; *****************************************************************************
+;
+;       ReadMonitorType - Read monitor type
+;
+; out:  R3 = monitor type
+;       All other registers preserved
+;
+
+ReadMonitorType ENTRY "r0-r2"
+        MOV     r0, #1
+        SWI     XOS_ReadSysInfo         ; out: r0 = mode, r1 = monitortype, r2 = sync
+        MOV     r3, r1                  ; move into r3
+        EXIT
+
+; *****************************************************************************
+;
+;       ReadSyncType - Read sync type
+;
+; out:  R4 = sync type (0 or 1)
+;       Z set/clear on R4
+;       All other registers preserved
+;
+
+ReadSyncType ENTRY "r0-r2"
+        MOV     r0, #1
+        SWI     XOS_ReadSysInfo         ; out: r0 = mode, r1 = monitortype, r2 = sync
+        MOVS    r4, r2                  ; move into r4
+        EXIT
+
+; *****************************************************************************
+;
+;       SWIClaimScreenMemory - Claim unused screen memory (for ADFS etc.)
+;
+; in:   R0 = 0 => release, 1 => claim
+;       R1 = length you require
+;
+; out:  (for claim)
+;       C=0 => success
+;         R1 = actual length
+;         R2 = address
+;
+;       C=1 => failure
+;         R1 = length you could have
+;
+
+SWIClaimScreenMemory ROUT
+        MOV     R11, PC                 ; disable IRQs, so can be called from
+        TST     R11, #I_bit             ; an IRQ routine
+        TEQEQP  R11, #I_bit
+
+        VDWS    WsPtr
+        TEQ     R0, #0                  ; 0 => release
+        STREQB  R0, [WsPtr, #ScreenMemoryClaimed] ; indicate free again
+        ExitSWIHandler EQ
+
+; is claim
+
+        LDRB    R10, [WsPtr, #ScreenMemoryClaimed]
+        TEQ     R10, #0                 ; already claimed (NZ)
+        MOVNE   R1, #0                  ; indicate you could have zero bytes
+        BNE     %FT10                   ; failure exit
+
+        LDR     R10, [WsPtr, #TotalScreenSize]
+        LDR     R11, [WsPtr, #ScreenSize]
+        SUB     R10, R10, R11           ; amount available
+        CMP     R1, R10                 ; is this enough
+        MOV     R1, R10                 ; tell him how much he could have
+        BHI     %FT10                   ; if not enough, then exit
+
+        MOV     R10, #1                 ; indicate now claimed
+        STRB    R10, [WsPtr, #ScreenMemoryClaimed]
+
+        LDR     R2, [WsPtr, #DisplayStart]
+        ADD     R2, R2, R11             ; R2 -> start of usable area
+
+        BIC     R14, R14, #C_bit
+        ExitSWIHandler
+
+10
+        ORR     R14, R14, #C_bit
+        ExitSWIHandler
+
+; *****************************************************************************
+;
+;       SWIPlot - PLOT R0,R1,R2
+;
+; in:   R0 = plot code
+;       R1 = X coordinate
+;       R2 = Y coordinate
+;
+; out:  -
+;
+
+SWIPlot ROUT
+        CMP     R0, #256                ; is plot code >= 256 ?
+        ExitSWIHandler CS               ; yes, then do nothing
+                                        ; (for future expansion)
+        BranchNotJustUs %F10, WrchV, R12, R12
+
+        LDRB    R12, [R11, #OsbyteVars + :INDEX: WrchDest]
+        LDRB    R10, [R11, #OsbyteVars + :INDEX: SpoolFileH]
+        ORRS    R10, R10, R12
+        LDREQB  R10, [R11, #OsbyteVars + :INDEX: VDUqueueItems]
+        TEQEQ   R10, #0
+
+        VDWS    WsPtr
+        LDREQ   R10, [WsPtr, #CursorFlags]
+        TSTEQ   R10, #VduDisabled       ; if VDU disabled, go thru normal stuff
+        LDREQ   R10, [WsPtr, #ModeFlags] ; if non-graphic, then send
+        TSTEQ   R10, #Flag_NonGraphic   ; thru normal chans
+        BNE     %FT10
+
+        Push    "R0-R9,R14"
+        ADR     R14, %FT05 + SVC_mode   ; R14 will have I_bit clear
+
+; TMD 16-May-89: Next instruction used to be B PreWrchCursor2, a secondary
+; entry point that didn't disable IRQs as they were assumed to be already
+; off. However this caused a bug in RISC OS 2.00, since SWIs are now entered
+; with the IRQ state of the caller, so this has now been changed back to
+; use the primary entry point (PreWrchCursor) which does disable them.
+
+        B       PreWrchCursor           ; this exits by MOVS PC, R14 so it
+                                        ; clears the I_bit for us
+; PreWrchCursor  also exits with R6 = CursorFlags, needed for the
+; EntryFromSWIPlot routine to do clip window calculations if necessary
+
+05
+        LDMFD   R13, {R0-R2}
+        MOV     R3, R0                  ; save plot code
+        MOV     R0, R1, LSL #16
+        MOV     R0, R0, ASR #16         ; R0 := sign extended X coord
+        MOV     R1, R2, LSL #16
+        MOV     R1, R1, ASR #16         ; R1 := sign extended Y coord
+        MOV     R2, R3
+        MOV     R9, #1                  ; indicate entry from SWI Plot
+        BL      EntryFromSWIPlot
+        BL      PostWrchCursor
+        Pull    "R0-R9,R14"
+        ExitSWIHandler
+
+SWIPlotBadExit
+        STR     R0, [R13]               ; save error block pointer in saved R0
+        BL      PostWrchCursor
+        Pull    "R0-R9,R14"
+        ORR     R14, R14, #V_bit
+        ExitSWIHandler
+
+10
+        Push    "R0,R14"
+        SWI     XOS_WriteI+25           ; send 25
+        SWIVC   XOS_WriteC              ; send plot code
+        ANDVC   R0, R1, #&FF
+        SWIVC   XOS_WriteC              ; send X (lo)
+        MOVVC   R0, R1, LSR #8
+        SWIVC   XOS_WriteC              ; send X (hi)
+        ANDVC   R0, R2, #&FF
+        SWIVC   XOS_WriteC              ; send Y (lo)
+        MOVVC   R0, R2, LSR #8
+        SWIVC   XOS_WriteC              ; send Y (hi)
+
+        Pull    "R0,R14", VC            ; if no error, pull stacked R0 and R14
+        ExitSWIHandler VC
+
+        ADD     R13, R13, #4            ; if error, junk stacked R0
+        Pull    "R14"
+        ORR     R14, R14, #V_bit        ; and set V bit in link
+        ExitSWIHandler
+
+; *****************************************************************************
+;
+;       SWIRemoveCursors - Remove input and output cursors for screen bashing
+;
+; out:  All registers preserved (R10-R12 preserved by Sam)
+;
+
+SWIRemoveCursors
+        Push    "R0-R4,R6,R8-R9,R14"
+        VDWS    WsPtr
+        BL      PreWrchCursor
+        Pull    "R0-R4,R6,R8-R9,R14"
+        ExitSWIHandler
+
+; *****************************************************************************
+;
+;       SWIRestoreCursors - Restore input and output cursors after screen bash
+;
+; out:  All registers preserved (R10-R12 preserved by Sam)
+;
+
+SWIRestoreCursors
+        Push    "R0-R4,R6,R8-R9,R14"
+        VDWS    WsPtr
+        BL      PostWrchCursor
+        Pull    "R0-R4,R6,R8-R9,R14"
+        ExitSWIHandler
+
+; *****************************************************************************
+;
+;       SWIWriteN - Write R1 bytes from address R0 to wrch
+;
+; in:   R0 -> string
+;       R1 -> number of chars to print
+;
+; out:  -
+;
+
+SWIWriteN ROUT
+        Push    "R0,R1,R14"
+        TEQP    PC, #SVC_mode               ; enable interrupts
+
+        BranchNotJustUsWrch %F70
+
+; R11 now points to either vector node or 1st address on chain, as appropriate
+; R12 holds current value of this location, to be checked each time
+
+        MOV     R10, R0
+10
+        SUBS    R1, R1, #1
+        BCC     %FT90                   ; count has expired (V=0)
+        Push    PC                      ; push address of %FT20
+        LDRB    R0, [R10], #1
+        [ AssemblingArthur
+        B       PMFWrchDirect
+        |
+        LDR     PC, =(MOSDriver + MOSPMFWrch)
+        ]
+20
+        BVS     %FT90
+        LDR     R0, [R11]
+        TEQ     R0, R12                 ; vector still the same ?
+        BEQ     %BT10                   ; yes, then loop
+        B       %FT75                   ; no, do rest with wrch
+
+70
+        ADDS    R10, R0, #0             ; R10 := R0 and V := 0
+75
+        ADD     R11, R10, R1
+        TEQ     R10, R11
+80
+        LDRNEB  R0, [R10], #1
+        SWINE   XOS_WriteC
+        MOVVS   R10, R11
+        TEQ     R10, R11
+        BNE     %BT80
+90
+        Pull    "R0,R1,R14", VC         ; if no error, pull stacked R0,R1 & R14
+        ExitSWIHandler VC
+
+        ADD     R13, R13, #4            ; if error, junk stacked R0
+        Pull    "R1,R14"
+        ORR     R14, R14, #V_bit        ; and set V bit in link
+        ExitSWIHandler
+
+
+; *****************************************************************************
+;
+;       SWIWrite0 - Write a zero-terminated string pointed to by R0
+;
+; in:   R0 -> string
+;
+; out:  R0 -> char after the zero
+;
+
+SWIWrite0 ROUT
+        Push    "R14"
+        TEQP    PC, #SVC_mode                   ; enable interrupts
+
+        MOV     R10, R0                         ; R10 -> string
+
+        BranchNotJustUsWrch %F70
+
+; R11 now points to either vector node or 1st address on chain, as appropriate
+; R12 holds current value of this location, to be checked each time
+
+10
+        LDRB    R0, [R10], #1
+        CMP     R0, #0
+        Push    PC, NE                  ; push address of %FT20
+        [ AssemblingArthur
+        BNE     PMFWrchDirect
+        |
+        LDRNE   PC, =(MOSDriver + MOSPMFWrch)
+        ]
+        B       %FT80                   ; no more characters
+20
+        BVS     %FT80
+        LDR     R0, [R11]
+        TEQ     R0, R12                 ; vector still the same ?
+        BEQ     %BT10                   ; yes, then loop
+                                        ; no, then drop thru
+                                        ; and do rest with Wrch
+70
+        LDRB    R0, [R10], #1
+        CMP     R0, #0                  ; (V:=0)
+        SWINE   XOS_WriteC
+        BGT     %BT70                   ; branch if no error and not terminated
+80
+        MOVVC   R0, R10                 ; if no error, R0 -> char after zero
+
+        Pull    "R14"
+        ORRVS   R14, R14, #V_bit
+        ExitSWIHandler
+
+; *****************************************************************************
+;
+;       SWIWriteS - Write a zero-terminated in-line string
+;
+; in:   R14 -> first char of string (but has PSR bits in it)
+;
+; out:  -
+;
+
+SWIWriteS ROUT
+        Push    "R0, R14"
+        TEQP    PC, #SVC_mode                   ; enable interrupts
+
+        BIC     R10, R14, #ARM_CC_Mask          ; R10 -> string
+
+        BranchNotJustUsWrch %F70
+
+; R11 now points to either vector node or 1st address on chain, as appropriate
+; R12 holds current value of this location, to be checked each time
+
+10
+        LDRB    R0, [R10], #1
+        CMP     R0, #0
+        Push    PC, NE                  ; push address of %FT20
+        [ AssemblingArthur
+        BNE     PMFWrchDirect
+        |
+        LDRNE   PC, =(MOSDriver + MOSPMFWrch)
+        ]
+        B       %FT80                   ; no more characters
+20
+        BVS     %FT80
+        LDR     R0, [R11]
+        TEQ     R0, R12                 ; vector still the same ?
+        BEQ     %BT10                   ; yes, then loop
+                                        ; no, then drop thru
+                                        ; and do rest with Wrch
+70
+        LDRB    R0, [R10], #1
+        CMP     R0, #0                  ; (V:=0)
+        SWINE   XOS_WriteC
+        BGT     %BT70                   ; branch if no error and not terminated
+80
+        BVS     %FT90
+        Pull    "R0, R14"
+85
+        ADD     R10, R10, #3
+        BIC     R10, R10, #3            ; round up to next word boundary
+        AND     R14, R14, #ARM_CC_Mask  ; R14 = user PSR
+        ORR     R14, R14, R10
+        ExitSWIHandler
+
+90
+        Pull    "R11, R14"              ; junk the stacked R0
+        ORR     R14, R14, #V_bit
+95
+        LDRB    R11, [R10], #1          ; skip to the zero terminator
+        TEQ     R11, #0
+        BNE     %BT95
+        B       %BT85
+
+        [ AssemblingArthur
+
+; *****************************************************************************
+;
+;       RemovePages - Called by MOS when ChangeDynamicArea reduces the
+;                     amount of screen memory
+;
+; in:   R0 = - number of bytes being removed
+;
+; out:  All registers preserved
+;
+
+RemovePages ROUT
+        Push    "R0-R8,R12,R14"
+        BL      InsertRemovePagesCommon
+        ADD     R2, R0, #ScreenEndAdr   ; end of remaining screen memory
+ [ :LNOT: FixRemovePages
+        CMP     R3, R2
+        BCS     %FT50                   ; display starts in disappearing pages
+ ]
+
+; display starts in pages that remain
+
+        SUB     R5, R3, R0              ; new DisplayStart
+
+ [ FixRemovePages
+05
+        CMP     R5, #ScreenEndAdr       ; if off end of 1st copy
+        SUBCS   R5, R5, R4              ; then repeatedly subtract off new size
+        BCS     %BT05                   ; until in range
+
+        Push    "R0-R2"
+        MOV     R0, R5                  ; put new display start in r0
+        BL      SetVinit                ; this updates DisplayStart
+        Pull    "R0-R2"
+
+ |
+        STR     R5, [WsPtr, #DisplayStart]
+ ]
+        ADD     R3, R3, R4              ; end of area to copy +1
+        CMP     R3, R2
+        BLS     %FT20                   ; nothing to copy
+
+        SUB     R4, R3, R0              ; destination end
+10
+        LDMDB   R3!, {R5-R8}            ; load 4 words (minimum amount)
+        STMDB   R4!, {R5-R8}            ; store 4 words
+        TEQ     R3, R2
+        BNE     %BT10
+
+20
+InsertRemovePagesExit
+        LDR     R4, [WsPtr, #DisplayScreenStart]
+        SUB     R0, R4, R0
+ [ FixRemovePages
+        LDR     R3, [WsPtr, #TotalScreenSize]
+25
+        CMP     R0, #ScreenEndAdr       ; ensure displayscreenstart in range too
+        SUBCS   R0, R0, R3
+        BCS     %BT25
+ ]
+        BL      NewScreenStart
+30
+        BL      SetVendDefault
+
+        Pull    "R0-R8,R12,PC",,^
+
+ [ :LNOT: FixRemovePages
+
+; display starts in disappearing pages
+
+50
+        ADD     R4, R3, R0              ; destination start
+        MOV     R0, R3
+        BL      SetVinit                ; set vinit and DisplayStart
+60
+        LDMIA   R3!, {R5-R8}
+        STMIA   R4!, {R5-R8}
+        TEQ     R3, #ScreenEndAdr
+        BNE     %BT60
+        B       %BT30
+ ]
+
+; *****************************************************************************
+;
+;       InsertPages - Called by MOS when ChangeDynamicArea increases the
+;                     amount of screen memory
+;
+; in:   R0 = number of bytes being added
+;
+
+InsertPages ROUT
+        Push    "R0-R8,R12,R14"
+        BL      InsertRemovePagesCommon
+        SUB     R5, R3, R0              ; new DisplayStart
+        STR     R5, [WsPtr, #DisplayStart]
+        ADD     R2, R3, R1              ; end of block to copy
+
+        MOV     R3, #ScreenEndAdr
+        SUB     R4, R3, R0
+10
+        TEQ     R3, R2
+        LDMNEIA R3!, {R5-R8}
+        STMNEIA R4!, {R5-R8}
+        BNE     %BT10
+        B       InsertRemovePagesExit
+
+; *****************************************************************************
+
+InsertRemovePagesCommon ROUT
+        VDWS    WsPtr
+        LDR     R1, [WsPtr, #TotalScreenSize]   ; old size
+
+        MOV     R4, #0
+        STRB    R4, [R4, #OsbyteVars + :INDEX:MemDriver]  ; indicate default
+        STRB    R4, [R4, #OsbyteVars + :INDEX:MemDisplay] ; for both of these
+
+        LDR     R4, [WsPtr, #VduStatus] ; not shadowing any more
+        BIC     R4, R4, #Shadowing
+        STR     R4, [WsPtr, #VduStatus]
+
+        ADD     R4, R1, R0              ; length of remaining screen memory
+        STR     R4, [WsPtr, #TotalScreenSize] ; new size
+        RSB     R5, R4, #ScreenEndAdr   ; start of remaining screen memory
+        STR     R5, [WsPtr, #DriverBankAddr]
+        STR     R5, [WsPtr, #DisplayBankAddr]
+
+        LDR     R3, [WsPtr, #DisplayStart]
+        MOV     PC, R14
+
+        ]
+
+; *****************************************************************************
+;
+;       SWIChangedBox - Entry point for SWI OS_ChangedBox
+;
+; in:   R0 = 0 => disable clip box calculations
+;            1 => enable clip box calculations
+;            2 => reset clip box to null
+;            -1 => do nothing
+;
+; out:  R0 = old enable state (0 => disabled, 1 => enabled)
+;       R1 -> clipbox info, consisting of 5 words
+;        [R1, #0] = disable/enable flag (in bit 0)
+;        [R1, #4] = internal X-coord of left edge of box
+;        [R1, #8] = internal Y-coord of bottom edge of box
+;        [R1, #12] = internal X-coord of right edge of box
+;        [R1, #16] = internal Y-coord of top edge of box
+;
+
+SWIChangedBox ROUT
+        VDWS    WsPtr
+        MOV     R1, WsPtr
+        LDR     R10, [R1, #ClipBoxEnable]! ; R10 = old state, R1 -> state
+        CMP     R0, #2                  ; known reason ?
+        BHI     %FT10                   ; no, then just read state
+        BEQ     %FT20                   ; reset rectangle to null
+
+        STR     R0, [R1]                ; then store R0 in ClipBoxEnable
+        TEQP    PC, #SVC_mode + I_bit   ; disable IRQs to update CursorFlags
+        TST     R0, #1
+        LDR     R0, [WsPtr, #CursorFlags]
+        BICEQ   R0, R0, #ClipBoxEnableBit
+        ORRNE   R0, R0, #ClipBoxEnableBit
+        STR     R0, [WsPtr, #CursorFlags]
+10
+        MOV     R0, R10                 ; R0 = old state
+        ExitSWIHandler
+
+20
+        Push    "R4-R7"
+        ADR     R0, NullRectangle
+        LDMIA   R0, {R4-R7}
+        STMIB   R1, {R4-R7}             ; store over coordinates
+        Pull    "R4-R7"
+        B       %BT10
+
+NullRectangle
+        &       &7FFFFFFF, &7FFFFFFF, &80000000, &80000000
+
+; *****************************************************************************
+;
+;       SetClipBoxToFullScreen - Called by FF
+;
+; in:   WsPtr -> VduDriverWorkSpace
+;
+; out:  R0-R4 corrupted
+;       PSR preserved
+;
+
+        ASSERT  YWindLimit = XWindLimit +4
+
+SetClipBoxToFullScreen ROUT
+        ADD     R4, WsPtr, #XWindLimit
+        LDMIA   R4, {R2, R3}
+        MOV     R0, #0
+        MOV     R1, #0
+        ADD     R4, WsPtr, #ClipBoxCoords
+        STMIA   R4, {R0-R3}
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       MergeClipBox - Merge a given rectangle into clip box
+;
+; in:   R0..R3 = Left, bottom, right, top of rectangle to merge
+;       WsPtr -> VduDriverWorkSpace
+;
+; out:  All registers preserved
+;
+
+MergeClipBox ROUT
+        Push    "R4-R8, R14"
+        ADD     R8, WsPtr, #ClipBoxCoords
+        LDMIA   R8, {R4-R7}
+        BL      MergeClipBoxes
+        STMIA   R8, {R4-R7}
+        Pull    "R4-R8, PC"
+
+MergeClipBoxes ROUT
+        CMP     R0, R4
+        MOVLT   R4, R0
+        CMP     R1, R5
+        MOVLT   R5, R1
+        CMP     R2, R6
+        MOVGT   R6, R2
+        CMP     R3, R7
+        MOVGT   R7, R3
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       DoPlotClipBox - Compute clip box for a PLOT command
+;
+; in:   R2 = plot code
+;
+; out:  R0-R2 preserved
+;
+
+DoPlotClipBox ROUT
+        TST     R2, #3                  ; (R2 AND 3)=0 => move operation
+        ADRNE   R11, ClipBoxPlotTable
+        LDRNEB  R11, [R11, R2, LSR #3]  ; get 'type' of this plot
+        TEQNE   R11, #0                 ; 0 => don't change clip window
+        MOVEQ   PC, R14
+        Push    "R0-R2, R14"
+        CMP     R11, #3                 ; if 1..3
+        BLS     ClipLastR11Points       ; then use last R11 points
+        CMP     R11, #ClipIndex_Ellipse
+        BCC     ClipFullWindow          ; flood fill => merge graphics window
+        BEQ     ClipEllipse
+        CMP     R11, #ClipIndex_LineFill
+        BCC     ClipParallelogram
+        BEQ     ClipLineFill
+        Pull    "R0-R2, PC"
+
+ClipIndex_FloodFill     * 4
+ClipIndex_Ellipse       * 5
+ClipIndex_Parallelogram * 6
+ClipIndex_LineFill      * 7
+
+ClipBoxPlotTable
+        =       2,2,2,2,2,2,2,2         ; 00..38 line drawing
+        =       1                       ; 40 point plot
+        =       ClipIndex_LineFill      ; 48 line fill
+        =       3                       ; 50 triangle
+        =       ClipIndex_LineFill      ; 58 line fill
+        =       2                       ; 60 rectangle fill
+        =       ClipIndex_LineFill      ; 68 line fill
+        =       ClipIndex_Parallelogram ; 70 parallelogram
+        =       ClipIndex_LineFill      ; 78 line fill
+        =       ClipIndex_FloodFill     ; 80 flood fill
+        =       ClipIndex_FloodFill     ; 88 flood fill
+        =       0,0,0,0,0               ; 90..B0 circle things
+                                        ; (done in GenCircleParm)
+        =       0                       ; B8 block copy/move (done in code)
+        =       ClipIndex_Ellipse       ; C0 ellipse outline
+        =       ClipIndex_Ellipse       ; C8 ellipse fill
+        =       0, 0, 0                 ; D0..E0 do nothing
+        =       0                       ; E8 sprite plot (done in SWI SpriteOp)
+        =       0, 0                    ; F0,F8 do nothing
+        ALIGN
+
+ClipLastR11Points ROUT
+        ADD     R10, WsPtr, #NewPtX
+        BL      MergeR11PointsFromR10
+        Pull    "R0-R2, PC"
+
+MergeR11PointsFromR10 ROUT
+        Push    R14
+        LDMIA   R10, {R4,R5}            ; get last point
+        MOV     R6, R4                  ; right=left
+        MOV     R7, R5                  ; top=bottom
+        SUBS    R11, R11, #1
+        BEQ     %FT10
+05
+        LDMDB   R10!, {R0,R1}           ; get another point (X,Y)
+        MOV     R2, R0
+        MOV     R3, R1
+        BL      MergeClipBoxes
+        SUBS    R11, R11, #1            ; one less point to do
+        BNE     %BT05
+10
+
+; now clip this to graphics window
+
+        ADD     R10, WsPtr, #GWLCol
+        LDMIA   R10, {R0-R3}
+        CMP     R4, R0
+        MOVGE   R0, R4
+        CMP     R5, R1
+        MOVGE   R1, R5
+        CMP     R6, R2
+        MOVLE   R2, R6
+        CMP     R7, R3
+        MOVLE   R3, R7
+        CMP     R2, R0
+        CMPGE   R3, R1
+        BLGE    MergeClipBox            ; if R>=L and T>=B then merge
+        Pull    PC
+
+
+ClipLineFill ROUT
+        ADD     R10, WsPtr, #GWLCol
+        LDMIA   R10, {R0-R3}
+        ADD     R10, WsPtr, #NewPtX
+        LDMIA   R10, {R4-R5}
+        CMP     R4, R0                  ; if point is inside window
+        CMPGE   R2, R4
+        CMPGE   R5, R1
+        CMPGE   R3, R5
+        MOVGE   R1, R5                  ; then top=bottom=Y
+        MOVGE   R3, R5                  ; and left=GWLCol, right=GWRCol
+        BLGE    MergeClipBox
+        Pull    "R0-R2, PC"
+
+ClipParallelogram ROUT
+        ADD     R10, WsPtr, #OldCsX
+        LDMIA   R10, {R0-R5}            ; load up last 3 points
+ClipParallelRegs
+        ADD     R6, R0, R4              ; 4th point = 1st + 3rd - 2nd
+        SUB     R6, R6, R2
+        ADD     R7, R1, R5
+        SUB     R7, R7, R3
+        Push    "R0-R7"                 ; stack all four points
+        ADD     R10, R13, #6*4          ; point R10 at last point
+        MOV     R11, #4                 ; 4 points to merge
+        BL      MergeR11PointsFromR10
+        ADD     R13, R13, #8*4          ; junk stacked points
+        Pull    "R0-R2, PC"
+
+ClipEllipse ROUT
+        ADD     R10, WsPtr, #OldCsX
+        LDMIA   R10, {R0-R5}            ; last 3 points (AX,AY,BX,BY,CX,CY)
+        SUB     R6, R2, R0              ; R6 = BX-AX
+        ADD     R0, R0, R2              ; R0 = AX+BX
+        SUB     R0, R0, R4              ; R0 = AX+BX-CX
+        ADD     R2, R4, R6              ; R2 = CX+BX-AX
+        SUB     R4, R4, R6              ; R4 = CX-(BX-AX) = CX+AX-BX
+        RSB     R1, R5, R1, LSL #1      ; R1 = 2*AY-CY
+        MOV     R3, R5                  ; R3 = CY
+        B       ClipParallelRegs
+
+ClipFullWindow ROUT
+        ADD     R10, WsPtr, #GWLCol     ; merge graphics window with
+        LDMIA   R10, {R0-R3}            ; clip rectangle (which can be larger)
+        BL      MergeClipBox
+        Pull    "R0-R2, PC"
+
+; *****************************************************************************
+;
+;       ClipBlockCopyMove - Calculate clip box for block copy/move
+;
+; in:   R0-R7 = SrcL, SrcB, SrcR, SrcT, DestL, DestB, DestR, DestT
+;       R8 = 0 => move, 2 => copy
+;
+; out:  R0-R7 preserved
+;       R8-R11 undefined
+;
+
+ClipBlockCopyMove ROUT
+        Push    "R0-R7, R14"
+        ADD     R10, R13, #6*4          ; R10 -> last point (DestR,DestT)
+        RSB     R11, R8, #4             ; R11 = 4 if move, 2 if copy
+                                        ; (number of points to merge)
+        BL      MergeR11PointsFromR10
+        Pull    "R0-R7, PC"
+
+; *****************************************************************************
+;
+;       ClipCircle - Add circle bounding box to clip box
+;
+; in:   R0 = radius of circle in square pixels
+;       R5, R6 = CentreX, CentreY
+;       R7 = AspectRatio (0 => square, 1 => flat rect, 2 => tall rect)
+;
+; out:  R0-R7 preserved
+;       R8-R11 undefined
+;
+
+ClipCircle ROUT
+        Push    "R0-R7,R14"
+        CMP     R7, #1
+        MOVEQ   R8, R0, LSR #1          ; if flat then dX = rad/2
+        MOVNE   R8, R0                  ; else dX = rad
+        MOVHI   R9, R0, LSR #1          ; if tall then dY = rad/2
+        MOVLS   R9, R0                  ; else dY = rad
+        SUB     R0, R5, R8              ; left
+        SUB     R1, R6, R9              ; bottom
+        ADD     R2, R5, R8              ; right
+        ADD     R3, R6, R9              ; top
+        Push    "R0-R3"
+        ADD     R10, R13, #2*4          ; point R10 at last point
+        MOV     R11, #2                 ; 2 points to merge
+        BL      MergeR11PointsFromR10
+        ADD     R13, R13, #4*4          ; junk stacked points
+        Pull    "R0-R7,PC"
+
+; *****************************************************************************
+;
+;       ClipCursorCell - Add current cursor cell to clip box
+;
+; in:   -
+;
+; out:  All registers preserved
+;
+
+ClipCursorCell ROUT
+        ASSERT  CursorY = CursorX +4
+        Push    "R0-R3, R14"
+        ADD     R0, WsPtr, #CursorX
+        LDMIA   R0, {R0, R1}
+        MOV     R2, R0                  ; RCol = LCol
+        MOV     R3, R1                  ; TRow = BRow
+        BL      ClipTextArea
+        Pull    "R0-R3, PC"
+
+; *****************************************************************************
+;
+;       ClipTextArea - Add a text area to the clip box
+;
+; in:   R0 = LCol of area
+;       R1 = BRow of area
+;       R2 = RCol of area
+;       R3 = TRow of area
+;
+; out:  All registers preserved
+;
+
+ClipTextArea ROUT
+        Push    "R0-R3, R14"
+        MOV     R0, R0, LSL #3                  ; left = LCol*8
+        MOV     R2, R2, LSL #3
+        ADD     R2, R2, #7                      ; right = RCol*8 + 7
+        LDR     R14, [WsPtr, #RowMult]
+        MUL     R3, R14, R3                     ; TRow * RowMult
+        MLA     R1, R14, R1, R14                ; (BRow+1) * RowMult
+        LDR     R14, [WsPtr, #YWindLimit]
+        SUB     R3, R14, R3             ; top = YWindLimit-TRow*RowMult
+        SUB     R1, R14, R1
+        ADD     R1, R1, #1              ; bot = YWindLimit-(BRow+1)*Mult+1
+        BL      MergeClipBox
+        Pull    "R0-R3, PC"
+
+; *****************************************************************************
+;
+;       ClipScroll - Add clip box when scrolling
+;
+; in:   R0 = 0 => add text window
+;       R0 <> 0 => set to full screen
+;
+; out:  All registers preserved
+;
+
+ClipScroll ROUT
+        Push    "R0-R4, R14"
+        CMP     R0, #1                  ; if scrolling screen
+        BLCS    SetClipBoxToFullScreen  ; set to full screen
+
+        ADDCC   R4, WsPtr, #TWLCol      ; else add text window
+        LDMCCIA R4, {R0-R3}
+        BLCC    ClipTextArea
+
+        Pull    "R0-R4, PC"
+
+; *****************************************************************************
+;
+;       ClipSpritePlot - Compute and merge sprite plot bounding box
+;
+; in:   R0 = unclipped X-coord (internal)
+;       R1 = clipped topY (internal)
+;       R2 = width of sprite in words
+;       R3 = height of sprite -1
+;       R4 = GWLCol
+;       R5 = height reduction
+;       R6 = GWRCol
+;       R8 -> sprite
+;
+; out:  All registers preserved
+;
+
+ClipSpritePlot ROUT
+        Push    "R0-R11, R14"
+        ADD     R9, R8, #spLBit
+        LDMIA   R9, {R9, R10}           ; R9 = spLBit; R10 = spRBit
+        ADD     R2, R9, R2, LSL #5      ; R2 = width*32+spLBit
+        RSB     R10, R10, #32           ; R10 = 32-spRBit
+        SUB     R2, R2, R10             ; R2 = width in bits-1
+        LDR     R9, [WsPtr, #Log2BPC]
+        ADD     R2, R0, R2, LSR R9      ; R2 = unclipped rightX
+        CMP     R0, R4
+        MOVLT   R0, R4                  ; R0 = clipped leftX
+        CMP     R2, R6
+        MOVGT   R2, R6                  ; R2 = clipped rightX
+        SUB     R3, R3, R5              ; R3 = no. of lines on screen -1
+        SUB     R1, R1, R3              ; R1 = clipped botY
+        ADD     R3, R1, R3              ; R3 = clipped topY
+
+        CMP     R2, R0                  ; if right>=left
+        CMPGE   R3, R1                  ; and top>=bot
+        BLGE    MergeClipBox            ; then add rectangle
+        Pull    "R0-R11, PC"
+
+ [ ModeSelectors
+
+; *****************************************************************************
+;
+;       ScreenModeSWI - Entry point for SWI OS_ScreenMode
+;
+; in:   r0 = reason code
+;       Other registers depend on reason code
+;
+; out:  Depends on reason code
+;
+
+ScreenModeSWI ENTRY
+        BL      ScreenModeSub
+        PullEnv
+        ORRVS   lr, lr, #V_bit
+        ExitSWIHandler
+
+        ASSERT  ScreenModeReason_SelectMode = 0
+        ASSERT  ScreenModeReason_ReturnMode = 1
+        ASSERT  ScreenModeReason_EnumerateModes = 2
+        ASSERT  ScreenModeReason_SelectMonitorType = 3
+        ASSERT  ScreenModeReason_Limit = 4
+
+ScreenModeSub
+        CMP     r0, #ScreenModeReason_Limit
+        ADDCC   pc, pc, r0, LSL #2
+        B       ScreenMode_Unknown
+        B       ScreenMode_SelectMode
+        B       ScreenMode_ReturnMode
+        B       ScreenMode_EnumerateModes
+        B       ScreenMode_SelectMonitorType
+
+; unknown OS_ScreenMode reason code
+
+ScreenMode_Unknown
+        ADR     r0, ErrorBlock_ScreenModeBadReason
+ScreenMode_TranslateAndReturnError
+      [ International
+        Push    lr
+        BL      TranslateError
+        Pull    lr
+      ]
+ScreenMode_ReturnError
+        SETV
+        MOV     pc, lr
+
+; Temporary error blocks, so we don't have to claim Hdr:NewErrors every 5 mins.
+
+ErrorBlock_ScreenModeBadReason
+        &       0
+        =       "Zonk:Unknown OS_ScreenMode reason code", 0
+        ALIGN
+
+;**************************************************************************
+;
+;       ScreenMode_SelectMode - Select a screen mode
+;
+;       Internal routine called by ScreenModeSWI
+;
+; in:   r0 = reason code (0)
+;       r1 = mode specifier
+;
+; out:  r10-r12 may be corrupted
+;       All other registers preserved
+;
+
+ScreenMode_SelectMode ENTRY "r0-r9"
+        TEQP    pc, #SVC_mode           ; enable IRQs
+        VDWS    WsPtr
+        BL      PreWrchCursor           ; remove cursor
+        LDR     r2, [sp, #1*4]          ; reload mode specifier
+        BL      ModeChangeSub           ; perform mode change
+        BVS     %FT90
+        BL      PostWrchCursor          ; if no error, then restore cursor
+        CLRV                            ; indicate no error
+        EXIT                            ; and exit
+
+90
+        STR     r0, [sp]                ; overwrite stacked r0 with error ptr
+        BL      PostWrchCursor          ; restore cursor
+        SETV                            ; indicate error
+        EXIT                            ; and exit
+
+;**************************************************************************
+;
+;       ScreenMode_ReturnMode - Return current screen mode specifier
+;
+;       Internal routine called by ScreenModeSWI
+;
+; in:   r0 = reason code (1)
+;
+; out:  r1 = mode specifier
+;       r10-r12 may be corrupted
+;       All other registers preserved
+;
+
+ScreenMode_ReturnMode ROUT
+        VDWS    WsPtr
+        LDR     r1, [WsPtr, #ModeNo]
+        CLRV
+        MOV     pc, lr
+
+;**************************************************************************
+;
+;       ScreenMode_EnumerateModes - Enumerate screen modes
+;
+;       Internal routine called by ScreenModeSWI
+;
+; in:   r0 = reason code (2)
+;       r2 = enumeration index (0 to start from beginning)
+;       r6 -> block to return data into, or 0 to just count entries
+;       r7 = size of block if r6<>0, or zero if r6=0
+;
+; out:  r1 = 0 if service claimed, otherwise r1<>0
+;       r2 = updated enumeration index
+;       r6 = updated block pointer
+;       r7 = size of remaining free area in block
+;       r10-r12 may be corrupted
+;       All other registers are preserved
+;
+
+ScreenMode_EnumerateModes ENTRY "r3-r5"
+        MOV     r1, #Service_EnumerateScreenModes
+        BL      ReadMonitorType
+        GetBandwidthAndSize     r4, r5
+        BL      Issue_Service
+        EXIT
+
+;**************************************************************************
+;
+;       ScreenMode_SelectMonitorType - Select current monitor type
+;
+;       Internal routine called by ScreenModeSWI
+;
+; in:   r0 = reason code (3)
+;       r1 = monitor type to set, or -1 to restore from configured value
+;
+; out:  r10-r12 may be corrupted
+;       All other registers preserved
+;
+
+ScreenMode_SelectMonitorType ENTRY "r0"
+        VDWS    WsPtr
+        CMP     r1, #-1                                 ; if not restoring configured value
+        BNE     %FT10                                   ; then skip
+        BL      Read_Configd_MonitorType                ; else read CMOS value (returns in r0)
+        MOV     r1, r0
+10
+        STR     r1, [WsPtr, #CurrentMonitorType]        ; update current value
+        EXIT
+
+ ]
+        END
diff --git a/s/vdu/vduttx b/s/vdu/vduttx
new file mode 100644
index 0000000000000000000000000000000000000000..b930183fdfcc11c957cb70361fb07045775c1187
--- /dev/null
+++ b/s/vdu/vduttx
@@ -0,0 +1,1235 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduTTX
+
+;       Teletext (MODE 7) emulation
+;       ---------------------------
+
+;       Author          Tim Dobson
+;       Started         24-Feb-87
+
+; *****************************************************************************
+
+; Teletext control codes
+
+TTX_AlphaRed     *      &01
+TTX_AlphaGreen   *      &02
+TTX_AlphaYellow  *      &03
+TTX_AlphaBlue    *      &04
+TTX_AlphaMagenta *      &05
+TTX_AlphaCyan    *      &06
+TTX_AlphaWhite   *      &07
+TTX_Flash        *      &08
+TTX_Steady       *      &09
+TTX_EndBox       *      &0A
+TTX_StartBox     *      &0B
+TTX_NormalHeight *      &0C
+TTX_DoubleHeight *      &0D
+
+TTX_GraphRed     *      &11
+TTX_GraphGreen   *      &12
+TTX_GraphYellow  *      &13
+TTX_GraphBlue    *      &14
+TTX_GraphMagenta *      &15
+TTX_GraphCyan    *      &16
+TTX_GraphWhite   *      &17
+TTX_Conceal      *      &18
+TTX_Contiguous   *      &19
+TTX_Separated    *      &1A
+TTX_BlackBackgd  *      &1C
+TTX_NewBackgd    *      &1D
+TTX_HoldGraph    *      &1E
+TTX_RelGraph     *      &1F
+
+TTXGraphContFontA * TTXSoftFonts +0    ; Contiguous graphics font (&20-&3F)
+TTXGraphSepaFontA * TTXSoftFonts +&140 ; Separated graphics font  (&20-&3F)
+TTXGraphContFontB * TTXSoftFonts +&280 ; Contiguous graphics font (&60-&7F)
+TTXGraphSepaFontB * TTXSoftFonts +&3C0 ; Separated graphics font  (&60-&7F)
+
+; Bits in the map
+
+;       Bits 0-7        8 bit character
+;       Bit  8          0 => Alpha, 1 => Graphics
+;       Bit  9          0 => Contiguous, 1 => Separated
+;       Bit  10         0 => Steady, 1 => Flash
+;       Bit  11         0 => Release, 1 => Hold
+;       Bit  12         0 => Reveal, 1 => Conceal
+;       Bits 13-14      Bit 13  Bit 14
+;                       0       0       Single Height
+;                       1       0       Undefined
+;                       0       1       Double Height Top
+;                       1       1       Double Height Bottom
+;       Bit  15         1 => Pending Start Box
+;       Bit  16         1 => Pending End Box
+;       Bits 17-19      Foreground colour
+;       Bit  20         0 => Unboxed, 1 => Boxed
+;       Bits 21-23      Background colour
+;       Bit  24         0 => Unboxed, 1 => Boxed
+;       Bits 25-29,31   Held graphic
+;       Bit  30         0 => Held graphic contiguous, 1 => separated
+
+MapBit_Char     *       1 :SHL: 0
+MapBit_Graph    *       1 :SHL: 8
+MapBit_Separated *      1 :SHL: 9
+MapBit_Flash    *       1 :SHL: 10
+MapBit_Hold     *       1 :SHL: 11
+MapBit_Conceal  *       1 :SHL: 12
+MapBit_Bottom   *       1 :SHL: 13
+MapBit_Double   *       1 :SHL: 14
+MapBit_PendingStart *   1 :SHL: 15
+
+MapBit_PendingEnd *     1 :SHL: 16
+MapBit_ForeMask *       7 :SHL: 17
+MapBit_BackMask *       7 :SHL: 21
+MapBits_Boxed   *       (1 :SHL: 20) :OR: (1 :SHL: 24)
+MapBit_HeldMask *       &7F :SHL: 25
+MapBit_HeldSeparated *  1 :SHL: 30
+
+MapForeShift    *       17
+MapBackShift    *       21
+MapHeldShift    *       25
+
+MapBit_Default  *       (7 :SHL: MapForeShift)+32 ; default at start of line
+
+; *****************************************************************************
+;
+;       TeletextInit - Initialise teletext workspace
+;       Called when MODE 7 (or 135) is selected
+;
+
+TeletextInit ROUT
+        MOV     R0, #1                          ; set to flash immediately
+        STR     R0, [WsPtr, #TeletextCount]
+
+; compute the graphics fonts
+
+        ADD     R0, WsPtr, #TTXSoftFonts ; R0 -> contiguous font
+        MOV     R2, #&20                ; R2 = character number
+        MOV     R3, #0                  ; R3 = byte to store
+10
+        ADD     R1, R0, #&140           ; R1 -> separated font
+20
+        TST     R2, #1                  ; top left
+        ORRNE   R3, R3, #&F0
+        TST     R2, #2                  ; top right
+        ORRNE   R3, R3, #&0F
+        STRB    R3, [R0], #1            ; 3 pixel rows for top
+        STRB    R3, [R0], #1
+        STRB    R3, [R0], #1
+        AND     R3, R3, #&77            ; do separated
+        STRB    R3, [R1], #1
+        STRB    R3, [R1], #1
+        MOV     R3, #0
+        STRB    R3, [R1], #1
+
+        TST     R2, #4                  ; middle left
+        ORRNE   R3, R3, #&F0
+        TST     R2, #8                  ; middle right
+        ORRNE   R3, R3, #&0F
+        STRB    R3, [R0], #1            ; 4 pixel rows for middle
+        STRB    R3, [R0], #1
+        STRB    R3, [R0], #1
+        STRB    R3, [R0], #1
+        AND     R3, R3, #&77            ; do separated
+        STRB    R3, [R1], #1
+        STRB    R3, [R1], #1
+        STRB    R3, [R1], #1
+        MOV     R3, #0
+        STRB    R3, [R1], #1
+
+        TST     R2, #&10                ; bottom left
+        ORRNE   R3, R3, #&F0
+        TST     R2, #&40                ; bottom right
+        ORRNE   R3, R3, #&0F
+        STRB    R3, [R0], #1            ; 3 pixel rows for bottom
+        STRB    R3, [R0], #1
+        STRB    R3, [R0], #1
+        AND     R3, R3, #&77            ; do separated
+        STRB    R3, [R1], #1
+        STRB    R3, [R1], #1
+        MOV     R3, #0
+        STRB    R3, [R1], #1
+
+        ADD     R2, R2, #1
+        TEQ     R2, #&40                ; if at end of 1st part
+        MOVEQ   R2, #&60                ; then start 2nd
+        ADDEQ   R0, R0, #&140           ; skipping separated already done
+        BEQ     %BT10                   ; and resetting R1 too
+
+        TEQ     R2, #&80                ; finished
+        BNE     %BT20
+
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       TTXFastCLS - Called when clearing whole screen
+;       Clears the teletext map to default
+;
+
+TTXFastCLS ROUT
+        ADD     R0, WsPtr, #TTXMap      ; R0 -> map
+        ADD     R1, R0, #TTXMapSize-4   ; R1 -> last word of map
+        LDR     R2, =MapBit_Default     ; R2 = default status at start of line
+10
+        STR     R2, [R0], #4
+        CMP     R0, R1
+        BLS     %BT10
+
+        ADD     R0, WsPtr, #TTXDoubleCounts   ; zero double counts on each line
+        ADD     R1, R0, #25
+        MOV     R2, #0
+20
+        STRB    R2, [R0], #1
+        TEQ     R0, R1
+        BNE     %BT20
+
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       TTXUpdateColours - Update colour table for new colours
+;
+; in:   R5 = new foregd colour
+;       R6 = new backgd colour
+;
+; out:  R0, R2 preserved
+;
+
+TTXUpdateColours ROUT
+        Push    "R0,R2,R14"
+        ADD     R14, WsPtr, #TForeCol
+        STMIA   R14, {R5,R6}
+        MOV     fore, R5
+        MOV     back, R6
+        LDR     bpp, [WsPtr, #BitsPerPix]
+        BL      SetColours
+        Pull    "R0,R2,PC"
+
+; *****************************************************************************
+;
+;       PrintDoubleHeight - Process font for double height
+;
+; in:   R0 = char + attributes
+;       tophalf    contains bytes 0123
+;       bottomhalf contains bytes 4567
+;       R10        contains bytes xx89
+;       We know that at least one of MapBit_Bottom or MapBit_Double is set
+;
+
+PrintDoubleHeight ROUT
+        TST     R0, #MapBit_Double              ; if not double height,
+        MOVEQ   tophalf, #0                     ; then must be single height
+        MOVEQ   bottomhalf, #0                  ; part on line below double,
+        MOVEQ   R10, #0                         ; so make it invisible
+        MOVEQ   PC, R14
+
+        TST     R0, #MapBit_Bottom
+        BNE     %FT10                           ; [bottom half of double]
+
+        [ 1=1 ;                 0 1              2 3         4
+; do top half, we want tophalf=0112, bottomhalf=2334, R10=xx45
+        MOV     R10, bottomhalf, LSL #16        ; R10 := o o 4 5
+        ORR     R10, R10, R10, LSL #8           ; R10 := o o 4 4/5
+        AND     R3, tophalf, #&FF000000         ; R3 := o o o 3
+        ORR     bottomhalf, R3, bottomhalf, LSL #24 ; bot := o o o 3/4
+        MOV     R3, tophalf, LSR #16            ; R3 := 2 3 o o
+        ORR     R3, R3, R3, LSL #8              ; R3 := 2 2/3 3 o
+        ORR     bottomhalf, bottomhalf, R3      ; bot := 2 2/3 3 3/4
+        MOV     tophalf, tophalf, LSL #16       ; top := o o 0 1
+        ORR     tophalf, tophalf, tophalf, LSR #8 ; top := o 0 0/1 1
+        MOV     tophalf, tophalf, LSR #8        ; top := 0 0/1 1 o
+        ORR     tophalf, tophalf, bottomhalf, LSL #24 ; top := 0 0/1 1 2
+        AND     R3, tophalf, #&00FF0000         ; R3 := o o 1 o
+        ORR     tophalf, tophalf, R3, LSL #8    ; top := 0 0/1 1 1/2
+        MOV     PC, R14
+
+10 ;                               5 6              7 8         9
+; do bottom half, we want tophalf=5667, bottomhalf=7889, R10=xx9o
+        AND     tophalf, bottomhalf, #&FF000000 ; top := o o o 7
+        MOV     R3, bottomhalf, LSL #8          ; R3 := o 4 5 6
+        MOV     R3, R3, LSR #16                 ; R3 := 5 6 o o
+        ORR     tophalf, tophalf, R3            ; top := 5 6 o 7
+        ORR     tophalf, tophalf, R3, LSL #8    ; top := 5 5/6 6 7
+        AND     R3, R3, #&0000FF00              ; R3 := o 6 o o
+        ORR     tophalf, tophalf, R3, LSL #16   ; top := 5 5/6 6 6/7
+        MOV     bottomhalf, bottomhalf, LSR #24 ; bot := 7 o o o
+        AND     R3, R10, #&00FF0000             ; R3 := o o 8 o
+        ORR     bottomhalf, bottomhalf, R3, LSR #8 ; bot := 7 8 o o
+        ORR     bottomhalf, bottomhalf, bottomhalf, LSL #8 ; bot := 7 7/8 8 o
+        ORR     R3, R10, R10, LSL #8            ; R3 := x x x 8/9
+        AND     R3, R3, #&FF000000              ; R3 := o o o 8/9
+        ORR     bottomhalf, bottomhalf, R3      ; bot := 7 7/8 8 8/9
+        MOV     R10, R10, LSR #24               ; R10 := 9 o o o
+        ORR     R10, R10, R10, LSL #8           ; R10 := 9 9 o o
+        MOV     R10, R10, LSL #16               ; R10 := o o 9 9
+        MOV     PC, R14
+        |
+; do top half, we want tophalf=0011, bottomhalf=2233, R10=xx44
+
+        MOV     R10, bottomhalf, LSL #24        ; R10 := ooo4
+        ORR     R10, R10, R10, LSR #8           ; R10 := oo44
+        AND     bottomhalf, tophalf, #&00FF0000 ; bottom := oo2o
+        ORR     bottomhalf, bottomhalf, tophalf, LSR #24 ; bottom := 3o2o
+        ORR     bottomhalf, bottomhalf, bottomhalf, LSL #8 ; bottom := 3322
+        MOV     bottomhalf, bottomhalf, ROR #16 ; bottom := 2233
+        AND     R3, tophalf, #&0000FF00         ; R3 := o1oo
+        AND     tophalf, tophalf, #&FF          ; top := 0ooo
+        ORR     tophalf, tophalf, R3, LSL #8    ; top := 0o1o
+        ORR     tophalf, tophalf, tophalf, LSL #8 ; top := 0011
+        MOV     PC, R14
+
+; do bottom half, we want tophalf=5566, bottomhalf=7788, R10=xx99
+
+10
+        AND     tophalf, bottomhalf, #&0000FF00         ; top := o5oo
+        MOV     bottomhalf, bottomhalf, LSR #16         ; bot := 67oo
+        ORR     tophalf, tophalf, bottomhalf, LSL #24   ; top := o5o6
+        ORR     tophalf, tophalf, tophalf, LSR #8       ; top := 5566
+        MOV     bottomhalf, bottomhalf, LSR #8          ; bot := 7ooo
+        MOV     R10, R10, LSR #16                       ; R10 := 89oo
+        ORR     bottomhalf, bottomhalf, R10, LSL #16    ; bot := 7o89
+        BIC     bottomhalf, bottomhalf, #&FF000000      ; bot := 7o8o
+        ORR     bottomhalf, bottomhalf, bottomhalf, LSL #8 ; bot := 7788
+        BIC     R10, R10, #&FF                          ; R10 := o9oo
+        ORR     R10, R10, R10, LSR #8                   ; R10 := 99oo
+        MOV     R10, R10, LSL #16                       ; R10 := oo99
+        MOV     PC, R14
+        ]
+
+; *****************************************************************************
+;
+;       TTXWrch - Print a character in the range &20-&FF
+;
+; in:   R0 = character
+; out:  cursor has been moved on if appropriate
+;
+
+TTXWrch ROUT
+        Push    R14
+        BL      TTXDoChar
+        Pull    R14
+        B       PostCharMove
+
+; *****************************************************************************
+;
+;       TTXDoChar - Print a character (don't move cursor)
+;
+; in:   R0 = character
+;
+
+TTXDoChar ROUT
+        Push    R14
+
+        MOV     R3, R0
+        TEQ     R3, #"#"                        ; swap around the three
+        MOVEQ   R0, #"_"                        ; old favourites
+        TEQ     R3, #"_"
+        MOVEQ   R0, #"`"
+        TEQ     R3, #"`"
+        MOVEQ   R0, #"#"
+
+        LDR     R11, [WsPtr, #CursorY]          ; R11 = current Y position
+        LDR     R3, [WsPtr, #CursorX]           ; R3=start X posn on this line
+        LDR     R2, [WsPtr, #CursorAddr]        ; screen address
+TTXScanFromHere
+        MOV     R4, #0                          ; Xmin and
+        MOV     R5, #0                          ; Xmax are irrelevant
+        MOV     R10, #0                         ; Ymax always <= Y so no zap
+TTXScanZap
+        ADRL    R1, TTXLineStarts               ; R1 -> table of line starts
+        LDR     R8, [R1, R11, LSL #2]           ; R8 -> map entry at st.of line
+        ADD     R7, R8, #40*4                   ; R7 -> end of this line
+        LDR     R1, [R8, R3, LSL #2]!           ; R1 = prev. char+attr
+08
+        ADD     R3, WsPtr, #TTXDoubleCounts
+        LDRB    R9, [R3, R11]                   ; R9 = no. of dbls on this line
+10
+        BIC     R1, R1, #&FF                    ; clear char bits
+        ORR     R0, R1, R0                      ; store new char
+        AND     R1, R0, #&7F                    ; just look at char bits
+
+        Push    R14
+        CMP     R1, #&20                        ; is it a control char
+        BLCC    DoPreControl                    ; [do pre-control things]
+
+        Push    "R1,R4-R11"
+        BL      TTXPaintChar
+        Pull    "R1,R4-R11"
+
+        BIC     R0, R0, #(MapBit_PendingStart :OR: MapBit_PendingEnd)
+
+        CMP     R1, #&20
+        BLCC    DoPostControl                   ; [do post-control things]
+        Pull    R14                             ; restore zap flag
+
+        LDR     R1, [R8, #4]!                   ; get old character
+        STR     R0, [R8]                        ; store character away
+
+        AND     R3, R1, #&7F                    ; if overwriting double height
+        TEQ     R3, #TTX_DoubleHeight
+        SUBEQ   R9, R9, #1                      ; then one less
+
+        EOR     R1, R0, R1                      ; get difference
+        BIC     R3, R1, #&FF                    ; difference in attributes
+        MOV     R1, R0                          ; R1 = prev char + new attr
+
+        CMP     R11, R10                        ; if Y >= Ymax
+        BCS     %FT20                           ; then load from map
+        CMPCC   R8, R5                          ; else if X <= Xmax
+        MOVLS   R3, #1                          ; then pretend attr different
+        CMPCC   R4, R8                          ; if Xmin < X < Xmax
+        CMPCC   R14, #1                         ; and we're zapping (not scan)
+        MOVCC   R0, #32                         ; then zap to space
+20
+        LDRCSB  R0, [R8, #4]                    ; else load from map
+
+        TEQ     R3, #0                          ; if attributes different
+        TEQNE   R8, R7                          ; and not at end of line
+        ADDNE   R2, R2, #4                      ; then move to next char
+        BNE     %BT10                           ; and loop
+
+        ADD     R3, WsPtr, #TTXDoubleCounts
+        STRB    R9, [R3, R11]                   ; update no. of doubles
+
+        ADD     R11, R11, #1                    ; go to next line
+        TEQ     R11, #25                        ; if off bottom of screen
+        Pull    PC, EQ                          ; then finished
+
+        MOVS    R3, R9                          ; if no doubles
+                                                ; then next line is top line
+        EORNE   R3, R1, #MapBit_Bottom          ; else next line is opposite
+                                                ; to this line
+
+        LDR     R1, [R7, #4]                    ; get dummy word on next line
+        EOR     R3, R1, R3                      ; difference
+        ANDS    R3, R3, #MapBit_Bottom          ; difference in 'bottom' bit
+        EORNE   R1, R1, #MapBit_Bottom          ; if different then toggle bit
+        STR     R1, [R7, #4]!                   ; always store back
+        BNE     %FT30                           ; and do another row
+
+        CMP     R11, R10                        ; else if finished zap
+        Pull    PC, CS                          ; then exit
+30
+
+; now compute new R2
+
+        SUB     R3, R7, R8                      ; no. of chars before eol
+        RSB     R3, R3, #160                    ; current char number
+        SUB     R2, R2, R3                      ; back to start of old line
+        LDR     R3, [WsPtr, #RowLength]
+        ADD     R2, R2, R3                      ; move down a row
+
+        MOV     R8, R7                          ; R8 -> dummy char on new line
+        ADD     R7, R7, #40*4                   ; R7 -> last char on new line
+
+        ADD     R4, R4, #41*4                   ; move Xmin to next line
+        ADD     R5, R5, #41*4                   ; move Xmax to next line
+
+        CMP     R11, R10                        ; if Y < Ymax
+        CMPCC   R8, R5                          ; and X < Xmax
+        CMPCC   R4, R8                          ; if also Xmin < X
+        CMPCC   R14, #1                         ; & we're zapping not scanning
+        MOVCC   R0, #32                         ; then zap to space
+        LDRCSB  R0, [R8, #4]                    ; else load from map
+        B       %BT08
+
+; *****************************************************************************
+;
+;       TTXClearBox - Fill a rectangle with spaces, and update screen
+;
+; in:   R0 = left column
+;       R1 = bottom row
+;       R2 = right column
+;       R3 = top row
+;       Return address already stacked
+;
+
+TTXClearBox
+        ADD     R10, R1, #1                     ; R10 := bottom +1
+        MOV     R11, R3                         ; R11 := top
+        ADD     R5, R2, #1                      ; R5 := right + 1
+
+        MOV     R1, R3
+        BL      AddressR0R1                     ; R2 := address(topleft)
+                                                ; R1, R3, R4 corrupted
+        MOV     R3, R0                          ; R3 := left
+
+        ADRL    R4, TTXLineStarts
+        LDR     R0, [R4, R11, LSL #2]           ; R0 -> dummy(top)
+        ADD     R5, R0, R5, LSL #2              ; R5 := map(topright)
+        ADD     R4, R0, R3, LSL #2              ; R4 := map(topleft)-4
+        SUB     R4, R4, #4                      ; R4 := map(topleft)-8
+        MOV     R0, #32                         ; start with a space
+        MOV     R14, #0                         ; indicate zapping
+        B       TTXScanZap                      ; go and do it
+
+; *****************************************************************************
+;
+;       TTXHardScrollUp - Scroll teletext screen upwards
+;
+
+TTXHardScrollUp ROUT
+
+; first scroll map up
+
+        LDR     R0, [WsPtr, #RowLength]         ; save real RowLength
+        Push    "R0, R6, R14"                   ; and save CursorFlags
+
+        MOV     R0, #1                          ; pretend rowmult = 1
+        LDR     R2, TTXLineStarts               ; R2 -> TTXMap
+        MOV     R5, #41*4                       ; R5 = no. of bytes horiz
+        STR     R5, [WsPtr, #RowLength]         ; pretend rowlength
+        MOV     R6, #25                         ; no. of 'pixel' rows
+        MOV     R7, R5                          ; linelength
+        BL      SoftScrollUp2
+
+        Pull    "R0, R6"
+        STR     R0, [WsPtr, #RowLength]         ; restore RowLength
+
+; now 'scroll' DoubleCounts
+
+        ADD     R0, WsPtr, #TTXDoubleCounts
+        ADD     R1, R0, #24
+10
+        LDRB    R2, [R0, #1]
+        STRB    R2, [R0], #1
+        TEQ     R0, R1
+        BNE     %BT10
+
+; now see if top line was a 'bottom' row
+; if so, we need to rescan from the top
+
+        LDR     R8, TTXLineStarts               ; R8 -> top left map
+        LDR     R1, [R8]                        ; R1 = dummy word
+        TST     R1, #MapBit_Bottom              ; if not bottom
+        BEQ     %FT20                           ; then OK
+
+        BIC     R1, R1, #MapBit_Bottom          ; make into a 'top' line
+        STR     R1, [R8]                        ; store back
+        LDRB    R0, [R8, #4]                    ; R0 = first char
+        MOV     R3, #0                          ; X = 0
+        MOV     R11, #0                         ; Y = 0
+        LDR     R2, [WsPtr, #ScreenStart]       ; screen address for top-left
+
+        ADR     R14, %FT20
+        Push    R14
+        B       TTXScanFromHere
+
+20
+
+; now see if new bottom line should be a 'bottom' or a 'top' line
+
+        ADRL    R0, TTXLineStarts
+        LDR     R0, [R0, #23*4]                 ; R0 -> dummy word on line 23
+        LDRB    R1, [WsPtr, #TTXDoubleCounts+23] ; no. of dbls on line 23
+        TEQ     R1, #0                          ; if R1=0 then line 24 is 'top'
+        LDRNE   R1, [R0]                        ; else line 24 is opposite
+        EORNE   R1, R1, #MapBit_Bottom          ; of line 23
+        ANDNE   R1, R1, #MapBit_Bottom
+        LDR     R2, [R0, #41*4]                 ; R2 = dummy word on line 24
+        BIC     R2, R2, #MapBit_Bottom          ; clear that bit
+        ORR     R2, R2, R1                      ; OR in new bit
+        STR     R2, [R0, #41*4]                 ; and store back
+
+        Pull    PC
+
+; *****************************************************************************
+;
+;       TTXSoftScrollUp - Scroll screen up by software in teletext mode
+;
+
+TTXSoftScrollUp ROUT
+
+; first scroll map up
+
+        LDR     R0, [WsPtr, #RowLength]         ; save real RowLength
+        LDR     R1, [WsPtr, #TWTRow]            ; top row
+        LDR     R3, [WsPtr, #TWBRow]            ; bottom row
+
+        ADRL    R2, TTXLineStarts
+        LDR     R2, [R2, R1, LSL #2]            ; R2 -> dummy char top row
+        ADD     R2, R2, #4                      ; R2 -> 0th char top row
+        LDR     R4, [WsPtr, #TWLCol]
+        LDR     R5, [WsPtr, #TWRCol]
+
+        Push    "R0-R6,R14"                     ; and save CursorFlags
+
+        ADD     R2, R2, R4, LSL #2              ; R2 -> top left char
+
+        SUB     R5, R5, R4
+        ADD     R5, R5, #1                      ; R5 = no. of chars wide
+        MOV     R5, R5, LSL #2                  ; R5 = no. of bytes / line
+
+        SUB     R6, R3, R1
+        ADD     R6, R6, #1                      ; R6 = no. of 'pixel' rows
+
+        MOV     R7, #41*4                       ; R7 = line length
+        STR     R7, [WsPtr, #RowLength]         ; pretend row length
+        MOV     R0, #1                          ; pretend rowmult =1
+
+        BL      SoftScrollUp2
+
+        Pull    "R0-R6"
+        STR     R0, [WsPtr, #RowLength]         ; restore RowLength
+
+; now R1=top row, R2 -> char 0 on row R1, R3=bottom row, R4=left, R5=right
+
+; now clear bottom row
+
+        ADR     R7, TTXLineStarts
+        LDR     R7, [R7, R3, LSL #2]            ; R7 -> dummy word(bottom)
+        ADD     R8, R7, R5, LSL #2              ; R8 -> char before bottom rt
+        ADD     R7, R7, R4, LSL #2              ; R7 -> char before bottom left
+        MOV     R0, #32
+30
+        STRB    R0, [R7, #4]!                   ; zap to space
+        CMP     R7, R8                          ; if <= char before bottom rt
+        BLS     %BT30                           ; then loop
+
+        MOV     R9, R2
+CountAndRescan
+        BL      CountDoubles
+
+; now rescan from top of window
+
+        ADD     R10, R3, #1                     ; R10 = bottom + 1
+        MOV     R11, R1                         ; R11 = top
+        ADD     R7, R2, R4, LSL #2              ; R7 = map(left,top)
+        ADD     R5, R2, R5, LSL #2              ; R5 = map(right,top)
+        MOV     R0, R4                          ; R0 = left
+TTXScanZap2
+        BL      AddressR0R1                     ; R2 = screen(left,top)
+                                                ; (R1,R3,R4 corrupted)
+        SUB     R4, R7, #8                      ; R4 = map(left,top)-8
+        MOV     R3, R0                          ; R3 = left
+        LDRB    R0, [R4, #8]                    ; R0 = first char
+        MOV     R14, #1                         ; scan not zap
+        B       TTXScanZap
+
+; *****************************************************************************
+;
+;       CountDoubles - Count double height characters in a range of rows
+;
+; in:   R1 = top row to count
+;       R3 = bottom row to count
+;       R9 -> map(0,top)
+;
+; out:  R1-R6 preserved
+;
+
+CountDoubles ROUT
+        ADD     R7, WsPtr, #TTXDoubleCounts
+        ADD     R7, R7, R1                      ; R7 -> current double count
+        MOV     R11, R1
+10
+        ADD     R8, R9, #40*4                   ; R8 -> dummy char next row
+        MOV     R10, #0                         ; count so far
+20
+        LDR     R0, [R9], #4                    ; load char word
+        AND     R0, R0, #&7F                    ; only look at bottom 7 bits
+        TEQ     R0, #TTX_DoubleHeight           ; if double height
+        ADDEQ   R10, R10, #1                    ; then increment count
+        TEQ     R9, R8                          ; if not at end of row
+        BNE     %BT20                           ; then loop
+
+        STRB    R10, [R7], #1                   ; store double count
+        ADD     R9, R9, #4                      ; skip dummy char
+        ADD     R11, R11, #1                    ; goto next row
+        CMP     R11, R3                         ; if <= bottom
+        BLS     %BT10                           ; then loop
+
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       TTXHardScrollDown - Scroll teletext screen downwards
+;
+
+TTXHardScrollDown ROUT
+
+; first scroll map down
+
+        LDR     R0, [WsPtr, #RowLength]         ; save real RowLength
+        Push    "R0, R6, R14"                   ; and save CursorFlags
+
+        MOV     R0, #1                          ; pretend rowmult = 1
+        LDR     R2, TTXLineStarts+24*4          ; R2 -> dummy char on bottom
+        MOV     R5, #41*4                       ; R5 = no. of bytes horiz
+        STR     R5, [WsPtr, #RowLength]         ; pretend rowlength
+        MOV     R6, #25                         ; no. of 'pixel' rows
+        MOV     R7, R5                          ; linelength
+        BL      SoftScrollDown2
+
+        Pull    "R0, R6"
+        STR     R0, [WsPtr, #RowLength]         ; restore RowLength
+
+; now 'scroll' DoubleCounts
+
+        ADD     R0, WsPtr, #TTXDoubleCounts
+        ADD     R1, R0, #24
+10
+        LDRB    R2, [R1, #-1]
+        STRB    R2, [R1], #-1
+        TEQ     R1, R0
+        BNE     %BT10
+
+; now make top row a 'top' row
+
+        LDR     R0, TTXLineStarts               ; R0 -> top line dummy word
+        LDR     R1, [R0]                        ; R1 = dummy word
+        BIC     R1, R1, #MapBit_Bottom          ; clear 'bottom' bit
+        STR     R1, [R0]                        ; and store back
+
+        Pull    PC
+
+; *****************************************************************************
+;
+;       TTXSoftScrollDown - Scroll screen down by software in teletext mode
+;
+
+TTXSoftScrollDown ROUT
+
+; first scroll map down
+
+        LDR     R0, [WsPtr, #RowLength]         ; save real RowLength
+        LDR     R1, [WsPtr, #TWTRow]            ; top row
+        LDR     R3, [WsPtr, #TWBRow]            ; bottom row
+
+        ADR     R2, TTXLineStarts
+        LDR     R2, [R2, R3, LSL #2]            ; R2 -> dummy char bottom row
+        ADD     R2, R2, #4                      ; R2 -> 0th char bottom row
+        LDR     R4, [WsPtr, #TWLCol]
+        LDR     R5, [WsPtr, #TWRCol]
+
+        Push    "R0-R6,R14"                     ; and save CursorFlags
+
+        ADD     R2, R2, R4, LSL #2              ; R2 -> bottom left char
+
+        SUB     R5, R5, R4
+        ADD     R5, R5, #1                      ; R5 = no. of chars wide
+        MOV     R5, R5, LSL #2                  ; R5 = no. of bytes / line
+
+        SUB     R6, R3, R1
+        ADD     R6, R6, #1                      ; R6 = no. of 'pixel' rows
+
+        MOV     R7, #41*4                       ; R7 = line length
+        STR     R7, [WsPtr, #RowLength]         ; pretend row length
+        MOV     R0, #1                          ; pretend rowmult =1
+
+        BL      SoftScrollDown2
+
+        Pull    "R0-R6"
+        STR     R0, [WsPtr, #RowLength]         ; restore RowLength
+
+; now R1=top row, R2 -> char 0 on row R3, R3=bottom row, R4=left, R5=right
+
+; now clear top row
+
+        ADR     R7, TTXLineStarts
+        LDR     R7, [R7, R1, LSL #2]            ; R7 -> dummy word(top)
+        ADD     R8, R7, R5, LSL #2              ; R8 -> char before top rt
+        ADD     R7, R7, R4, LSL #2              ; R7 -> char before top left
+        MOV     R0, #32
+30
+        STRB    R0, [R7, #4]!                   ; zap to space
+        CMP     R7, R8                          ; if <= char before bottom rt
+        BLS     %BT30                           ; then loop
+
+        SUB     R9, R8, R5, LSL #2              ; R9 -> dummy word(top)
+        ADD     R9, R9, #4                      ; R9 -> map(0,top)
+        MOV     R2, R9
+        B       CountAndRescan                  ; count doubles and rescan
+                                                ; from top of window
+
+; *****************************************************************************
+;
+;       TTXScrollLeft - Scroll left (by software) in Teletext mode
+;
+; in:   R0 bit0=0 => scroll window
+;               1 => scroll screen
+;
+
+TTXScrollLeft ROUT
+        Push    R14
+        BL      TTXSideScroll1
+        MOV     R5, R0                          ; R5 = left = column to check
+                                                ; for double height chars
+        BL      TTXSideScroll2                  ; do second part
+        BL      ScrollLeft2
+
+; now store spaces in right hand column
+
+        Pull    "R0-R3, R6, R9"
+        ADD     R4, R9, R2, LSL #2              ; R4 -> map(right,top)
+TTXSideScroll3
+        MOV     R7, R3                          ; R7 = current row
+        MOV     R8, #32                         ; poke spaces
+20
+        STRB    R8, [R4], #41*4                 ; store space and move down
+        ADD     R7, R7, #1                      ; next row
+        CMP     R7, R1                          ; if row <= bottom
+        BLS     %BT20                           ; then loop
+
+; now rescan from top of window/screen
+
+        ADD     R10, R1, #1                     ; R10 = bottom+1
+        MOV     R11, R3                         ; R11 = top
+        ADD     R7, R9, R0, LSL #2              ; R7 -> map(left,top)
+        ADD     R5, R9, R2, LSL #2              ; R5 -> map(right,top)
+        MOV     R1, R3                          ; R1 = top
+        B       TTXScanZap2
+
+; *****************************************************************************
+;
+;       TTXScrollRight - Scroll right (by software) in Teletext mode
+;
+; in:   R0 bit0=0 => scroll window
+;               1 => scroll screen
+;
+
+TTXScrollRight ROUT
+        Push    R14
+        BL      TTXSideScroll1
+        MOV     R5, R2                          ; R5 = right = column to check
+                                                ; for double height chars
+        BL      TTXSideScroll2
+        BL      ScrollRight2
+
+; now store spaces in left hand column
+
+        Pull    "R0-R3, R6, R9"
+        ADD     R4, R9, R0, LSL #2              ; R4 -> map(left,top)
+        B       TTXSideScroll3
+
+; *****************************************************************************
+;
+;       TTXSideScroll1 - Do first part of sideways scroll
+;
+
+TTXSideScroll1 ROUT
+        MOVS    R0, R0, LSR #1
+
+; C=0 => scroll window
+
+        ADDCC   R0, WsPtr, #TWLCol              ; R0 = left, R1 = bottom
+        LDMCCIA R0, {R0-R3}                     ; R2 = right, R3 = top
+
+; C=1 => scroll screen
+
+        MOVCS   R0, #0                          ; left
+        LDRCS   R1, [WsPtr, #ScrBRow]           ; bottom
+        LDRCS   R2, [WsPtr, #ScrRCol]           ; right
+        MOVCS   R3, #0                          ; top
+
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       TTXSideScroll2 - Do second part of sideways scroll
+;
+; in:   R5 = left or right hand column for left or right scroll respectively
+;
+
+TTXSideScroll2 ROUT
+
+; first check char R5 on each line and decrement double count if a double
+
+        ADR     R4, TTXLineStarts
+        LDR     R4, [R4, R3, LSL #2]            ; R4 -> map(dummy,top)
+        ADD     R9, R4, #4                      ; R9 -> map(0,top)
+        ADD     R4, R9, R5, LSL #2              ; R4 -> map(left or right,top)
+
+        ADD     R5, WsPtr, #TTXDoubleCounts
+        MOV     R7, R3                          ; R7 = current row
+10
+        LDR     R8, [R4], #41*4                 ; get char+attr
+        AND     R8, R8, #&7F
+        TEQ     R8, #TTX_DoubleHeight           ; test if double height char
+
+        LDREQB  R8, [R5, R7]                    ; if so then load double count
+        SUBEQ   R8, R8, #1                      ; decrement it
+        STREQB  R8, [R5, R7]                    ; and store back
+
+        ADD     R7, R7, #1                      ; next row
+        CMP     R7, R1                          ; if row <= bottom
+        BLS     %BT10                           ; then loop
+
+; now scroll map
+
+        Push    "R0-R3, R6, R9"
+        SUB     R5, R2, R0                      ; R5 = right-left
+        ADD     R5, R5, #1                      ; R5 = right-left+1
+        MOV     R5, R5, LSL #2                  ; R5 = (right-left+1)*4
+        ADD     R2, R9, R0, LSL #2              ; R2 -> map(left,top)
+        SUB     R6, R1, R3                      ; R6 = bottom-top
+        ADD     R6, R6, #1                      ; R6 = bottom-top+1
+        MOV     R7, #41*4                       ; linelength
+        MOV     R9, #4                          ; no. of bytes to scroll by
+
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       TTXPaintChar - Paint char according to attributes
+;
+; in:   R0 = character + attributes word
+;       R2 -> screen (for the time being)
+;
+; out:  R2 preserved
+;       R0 largely preserved, but held graphic bits may have been changed
+;
+
+TTXPaintChar ROUT
+        Push    R14
+        VDWS    WsPtr                           ; for now
+
+; first set up the colours
+
+        ADD     R1, WsPtr, #TForeCol
+        LDMIA   R1, {R1, R3}                    ; R1 = TForeCol; R3 = TBackCol
+        MOV     R4, #&0F
+        AND     R5, R4, R0, LSR #MapForeShift   ; R5 = new foregd colour
+        AND     R6, R4, R0, LSR #MapBackShift   ; R6 = new backgd colour
+
+        TEQ     R1, R5                          ; if foregd different
+        TEQEQ   R3, R6                          ; or backgd different
+        BLNE    TTXUpdateColours                ; then update colour table
+
+        ADR     R1, TTXHardFont-32*10           ; R1 -> base for font
+        MOVS    R3, R0, LSL #26                 ; C := bit6, N := bit5
+        AND     R3, R0, #&7F                    ; R3 = char
+        BMI     %FT10                           ; [&20-&3F or &60-&7F]
+        BCS     %FT20                           ; [&40-&5F (definitely Alpha)]
+
+; control code, so display as space or held graphic
+
+        TST     R0, #MapBit_Hold                ; zero if not holding
+        MOVEQ   R3, #&20                        ; pretend to be space
+        BICEQ   R0, R0, #MapBit_HeldMask        ; and cancel held graphic
+        BEQ     %FT20                           ; [display as space]
+
+; next instruction assumes no valid bits above held graphic bits
+
+        MOV     R3, R0, LSR #MapHeldShift       ; R3 = char to display
+        B       %FT30                           ; [display as held graphic]
+
+; char in range &20-&3F or &60-&7F, so check for alpha/graphics
+
+10
+        TST     R0, #MapBit_Graph               ; in graphics mode
+        BEQ     %FT20                           ; [no, so definitely Alpha]
+        TST     R0, #MapBit_Separated           ; separated graphics ?
+        BICEQ   R3, R3, #&20                    ; no, then make &00-&1F,&40-&5F
+        BIC     R0, R0, #MapBit_HeldMask        ; cancel current held graphic
+        ORR     R0, R0, R3, LSL #MapHeldShift   ; and put new one in
+30
+        ADD     R1, WsPtr, #TTXSoftFonts
+20
+        ADD     R1, R1, R3, LSL #3              ; add 8*char
+        ADD     R1, R1, R3, LSL #1              ; add 2*char
+
+        TST     R0, #MapBit_Conceal             ; concealing ?
+        ADRNE   R1, TTXHardFont                 ; yes, then display as space
+
+; now load font bytes
+; 0..3 into tophalf
+; 4..7 into bottomhalf
+; 8..9 into R10 (top 2 bytes)
+
+        TST     R1, #2                          ; starting on a word bdy ?
+
+        SUBNE   R1, R1, #2                      ; if not, move back
+        LDMIA   R1, {tophalf, bottomhalf, R10}  ; yes, so load all 3 up
+        MOVEQ   R10, R10, LSL #16
+        MOVNE   tophalf, tophalf, LSR #16
+        ORRNE   tophalf, tophalf, bottomhalf, LSL #16
+        MOVNE   bottomhalf, bottomhalf, LSR #16
+        ORRNE   bottomhalf, bottomhalf, R10, LSL #16
+
+        TST     R0, #(MapBit_Bottom :OR: MapBit_Double)
+        BLNE    PrintDoubleHeight
+
+      [ VIDC_Type = "VIDC20"
+        LDR     bigfont, [WsPtr, #TextExpandArea]
+      |
+        ADD     bigfont, WsPtr, #TextExpand
+      ]
+        MOV     mask, #&FF000000
+        LDR     linelen, [WsPtr, #LineLength]
+        Push    "R0, screen"
+        MOV     R0, #0                          ; indicate 10 rows
+        BL      Wrch4bitTTX                     ; do 1st bank
+
+        LDMFD   R13, {R0, screen}               ; restore char + scr. addr
+        ADD     screen, screen, #40*1024
+        TST     R0, #MapBit_Flash
+        MOVNE   mask, #0                        ; flash => 2nd bank is space
+        MOV     R0, #0
+        BL      Wrch4bitTTX
+        Pull    "R0, screen, PC"
+
+; *****************************************************************************
+
+TTXLineStarts
+        GBLA    lineno
+lineno  SETA    0
+        WHILE   lineno < 25
+        &       VduDriverWorkSpace+TTXMap+4*41*lineno
+lineno  SETA    lineno +1
+        WEND
+
+        LTORG
+
+; *****************************************************************************
+;
+;       DoPreControl  - Process 'at'    action of control char
+;       DoPostControl - Process 'after' action of control char
+;
+; in:   R0 = new char + attributes of current previous char
+;       R1 = new char AND &7F
+;       (0 <= R1 <= 31)
+;
+
+DoPreControl ROUT
+        ADD     PC, PC, R1, LSL #3
+DoPostControl ROUT
+        ADD     PC, PC, R1, LSL #3
+        MOV     PC, R14                 ; &00 Pre
+        MOV     PC, R14                 ; &00 Post
+        MOV     PC, R14                 ; &01 Pre
+        B       DoAlphaColour           ; &01 Post
+        MOV     PC, R14                 ; &02 Pre
+        B       DoAlphaColour           ; &02 Post
+        MOV     PC, R14                 ; &03 Pre
+        B       DoAlphaColour           ; &03 Post
+        MOV     PC, R14                 ; &04 Pre
+        B       DoAlphaColour           ; &04 Post
+        MOV     PC, R14                 ; &05 Pre
+        B       DoAlphaColour           ; &05 Post
+        MOV     PC, R14                 ; &06 Pre
+        B       DoAlphaColour           ; &06 Post
+        MOV     PC, R14                 ; &07 Pre
+        B       DoAlphaColour           ; &07 Post
+        MOV     PC, R14                 ; &08 Pre
+        B       DoFlash                 ; &08 Post
+        BIC     R0, R0, #MapBit_Flash   ; &09 Pre       ; clear flash mode
+        MOV     PC, R14                 ; &09 Post
+        B       DoPreEndBox             ; &0A Pre
+        B       DoPostEndBox            ; &0A Post
+        B       DoPreStartBox           ; &0B Pre
+        B       DoPostStartBox          ; &0B Post
+        B       DoSingleHeight          ; &0C Pre
+        MOV     PC, R14                 ; &0C Post
+        B       DoDoubleHeight          ; &0D Pre
+        MOV     PC, R14                 ; &0D Post
+        MOV     PC, R14                 ; &0E Pre
+        MOV     PC, R14                 ; &0E Post
+        MOV     PC, R14                 ; &0F Pre
+        MOV     PC, R14                 ; &0F Post
+        MOV     PC, R14                 ; &10 Pre
+        MOV     PC, R14                 ; &10 Post
+        MOV     PC, R14                 ; &11 Pre
+        B       DoGraphColour           ; &11 Post
+        MOV     PC, R14                 ; &12 Pre
+        B       DoGraphColour           ; &12 Post
+        MOV     PC, R14                 ; &13 Pre
+        B       DoGraphColour           ; &13 Post
+        MOV     PC, R14                 ; &14 Pre
+        B       DoGraphColour           ; &14 Post
+        MOV     PC, R14                 ; &15 Pre
+        B       DoGraphColour           ; &15 Post
+        MOV     PC, R14                 ; &16 Pre
+        B       DoGraphColour           ; &16 Post
+        MOV     PC, R14                 ; &17 Pre
+        B       DoGraphColour           ; &17 Post
+        ORR     R0, R0, #MapBit_Conceal ; &18 Pre       ; set conceal mode
+        MOV     PC, R14                 ; &18 Post
+        MOV     PC, R14                 ; &19 Pre
+        BIC     R0, R0, #MapBit_Separated ; &19 Post    ; clear separated
+        MOV     PC, R14                 ; &1A Pre
+        ORR     R0, R0, #MapBit_Separated ; &1A Post    ; set separated
+        MOV     PC, R14                 ; &1B Pre
+        MOV     PC, R14                 ; &1B Post
+        BIC     R0, R0, #MapBit_BackMask ; &1C Pre      ; clear backgd colour
+        MOV     PC, R14                 ; &1C Post
+        B       DoNewBackgd             ; &1D Pre
+        MOV     PC, R14                 ; &1D Post
+        ORR     R0, R0, #MapBit_Hold    ; &1E Pre       ; set hold graph mode
+        MOV     PC, R14                 ; &1E Post
+        MOV     PC, R14                 ; &1F Pre
+        BIC     R0, R0, #MapBit_Hold    ; &1F Post      ; clear hold graph mode
+        MOV     PC, R14
+
+; *****************************************************************************
+
+DoAlphaColour
+        BIC     R0, R0, #MapBit_HeldMask        ; clear held graphic
+        BIC     R0, R0, #(MapBit_ForeMask :OR: MapBit_Conceal)
+                                                ; clear colour + conceal
+        ORR     R0, R0, R1, LSL #MapForeShift   ; put in new colour
+        BIC     R0, R0, #MapBit_Graph           ; set alpha mode
+        MOV     PC, R14
+
+DoGraphColour
+        BIC     R0, R0, #(MapBit_ForeMask :OR: MapBit_Conceal)
+                                                ; clear colour + conceal
+        AND     R3, R1, #&07                    ; ensure only colour bits
+        ORR     R0, R0, R3, LSL #MapForeShift   ; put in new colour
+        ORR     R0, R0, #MapBit_Graph           ; set graph mode
+        MOV     PC, R14
+
+DoFlash
+        ORR     R0, R0, #MapBit_Flash           ; set flash mode
+        MOV     PC, R14
+
+DoNewBackgd
+        AND     R3, R0, #MapBit_ForeMask        ; R5 = fore colour
+        BIC     R0, R0, #MapBit_BackMask        ; clear old backgd
+        ORR     R0, R0, R3, LSL #(MapBackShift-MapForeShift) ; new backgd
+        MOV     PC, R14
+
+DoDoubleHeight
+        TST     R0, #MapBit_Double              ; if currently single height
+        BICEQ   R0, R0, #MapBit_HeldMask        ; then cancel held graphic
+        ORR     R0, R0, #MapBit_Double          ; set double height mode
+        ADD     R9, R9, #1                      ; one more double char
+        MOV     PC, R14
+
+DoSingleHeight
+        TST     R0, #MapBit_Double              ; if currently double height
+        BICNE   R0, R0, #MapBit_HeldMask        ; then cancel held graphic
+        BIC     R0, R0, #MapBit_Double          ; set single height mode
+        MOV     PC, R14
+
+DoPreStartBox
+        TST     R0, #MapBit_PendingStart        ; if prev char was start box
+        ORRNE   R0, R0, #MapBits_Boxed          ; then start boxed area
+        MOV     PC, R14
+
+DoPostStartBox
+        ORR     R0, R0, #MapBit_PendingStart    ; "Previous char is Start Box"
+        MOV     PC, R14
+
+DoPreEndBox
+        TST     R0, #MapBit_PendingEnd          ; if prev char was end box
+        BICNE   R0, R0, #MapBits_Boxed          ; then end boxed area
+        MOV     PC, R14
+
+DoPostEndBox
+        ORR     R0, R0, #MapBit_PendingEnd      ; "Previous char is End Box"
+        MOV     PC, R14
+
+; *****************************************************************************
+
+TTXHardFont
+        =       &00,&00,&00,&00,&00,&00,&00,&00,&00,&00 ; space
+        =       &00,&08,&08,&08,&08,&08,&00,&08,&00,&00 ; !
+        =       &00,&14,&14,&14,&00,&00,&00,&00,&00,&00 ; "
+        =       &00,&0C,&12,&10,&38,&10,&10,&3E,&00,&00 ; `
+        =       &00,&1C,&2A,&28,&1C,&0A,&2A,&1C,&00,&00 ; $
+        =       &00,&30,&32,&04,&08,&10,&26,&06,&00,&00 ; %
+        =       &00,&10,&28,&28,&10,&2A,&24,&1A,&00,&00 ; &
+        =       &00,&08,&08,&08,&00,&00,&00,&00,&00,&00 ; '
+        =       &00,&04,&08,&10,&10,&10,&08,&04,&00,&00 ; (
+        =       &00,&10,&08,&04,&04,&04,&08,&10,&00,&00 ; )
+        =       &00,&08,&2A,&1C,&08,&1C,&2A,&08,&00,&00 ; *
+        =       &00,&00,&08,&08,&3E,&08,&08,&00,&00,&00 ; +
+        =       &00,&00,&00,&00,&00,&00,&08,&08,&10,&00 ; ,
+        =       &00,&00,&00,&00,&1C,&00,&00,&00,&00,&00 ; -
+        =       &00,&00,&00,&00,&00,&00,&00,&08,&00,&00 ; .
+        =       &00,&00,&02,&04,&08,&10,&20,&00,&00,&00 ; /
+        =       &00,&08,&14,&22,&22,&22,&14,&08,&00,&00 ; 0
+        =       &00,&08,&18,&08,&08,&08,&08,&1C,&00,&00 ; 1
+        =       &00,&1C,&22,&02,&0C,&10,&20,&3E,&00,&00 ; 2
+        =       &00,&3E,&02,&04,&0C,&02,&22,&1C,&00,&00 ; 3
+        =       &00,&04,&0C,&14,&24,&3E,&04,&04,&00,&00 ; 4
+        =       &00,&3E,&20,&3C,&02,&02,&22,&1C,&00,&00 ; 5
+        =       &00,&0C,&10,&20,&3C,&22,&22,&1C,&00,&00 ; 6
+        =       &00,&3E,&02,&04,&08,&10,&10,&10,&00,&00 ; 7
+        =       &00,&1C,&22,&22,&1C,&22,&22,&1C,&00,&00 ; 8
+        =       &00,&1C,&22,&22,&1E,&02,&04,&18,&00,&00 ; 9
+        =       &00,&00,&00,&08,&00,&00,&08,&00,&00,&00 ; :
+        =       &00,&00,&00,&08,&00,&00,&08,&08,&10,&00 ; ;
+        =       &00,&04,&08,&10,&20,&10,&08,&04,&00,&00 ; <
+        =       &00,&00,&00,&3E,&00,&3E,&00,&00,&00,&00 ; =
+        =       &00,&10,&08,&04,&02,&04,&08,&10,&00,&00 ; >
+        =       &00,&1C,&22,&04,&08,&08,&00,&08,&00,&00 ; ?
+        =       &00,&1C,&22,&2E,&2A,&2E,&20,&1C,&00,&00 ; @
+        =       &00,&08,&14,&22,&22,&3E,&22,&22,&00,&00 ; A
+        =       &00,&3C,&22,&22,&3C,&22,&22,&3C,&00,&00 ; B
+        =       &00,&1C,&22,&20,&20,&20,&22,&1C,&00,&00 ; C
+        =       &00,&3C,&22,&22,&22,&22,&22,&3C,&00,&00 ; D
+        =       &00,&3E,&20,&20,&3C,&20,&20,&3E,&00,&00 ; E
+        =       &00,&3E,&20,&20,&3C,&20,&20,&20,&00,&00 ; F
+        =       &00,&1C,&22,&20,&20,&26,&22,&1E,&00,&00 ; G
+        =       &00,&22,&22,&22,&3E,&22,&22,&22,&00,&00 ; H
+        =       &00,&1C,&08,&08,&08,&08,&08,&1C,&00,&00 ; I
+        =       &00,&02,&02,&02,&02,&02,&22,&1C,&00,&00 ; J
+        =       &00,&22,&24,&28,&30,&28,&24,&22,&00,&00 ; K
+        =       &00,&20,&20,&20,&20,&20,&20,&3E,&00,&00 ; L
+        =       &00,&22,&36,&2A,&22,&22,&22,&22,&00,&00 ; M
+        =       &00,&22,&22,&32,&2A,&26,&22,&22,&00,&00 ; N
+        =       &00,&1C,&22,&22,&22,&22,&22,&1C,&00,&00 ; O
+        =       &00,&3C,&22,&22,&3C,&20,&20,&20,&00,&00 ; P
+        =       &00,&1C,&22,&22,&22,&2A,&24,&1A,&00,&00 ; Q
+        =       &00,&3C,&22,&22,&3C,&28,&24,&22,&00,&00 ; R
+        =       &00,&1C,&22,&20,&1C,&02,&22,&1C,&00,&00 ; S
+        =       &00,&3E,&08,&08,&08,&08,&08,&08,&00,&00 ; T
+        =       &00,&22,&22,&22,&22,&22,&22,&1C,&00,&00 ; U
+        =       &00,&22,&22,&22,&14,&14,&08,&08,&00,&00 ; V
+        =       &00,&22,&22,&22,&2A,&2A,&2A,&14,&00,&00 ; W
+        =       &00,&22,&22,&14,&08,&14,&22,&22,&00,&00 ; X
+        =       &00,&22,&22,&14,&08,&08,&08,&08,&00,&00 ; Y
+        =       &00,&3E,&02,&04,&08,&10,&20,&3E,&00,&00 ; Z
+        =       &00,&00,&08,&10,&3E,&10,&08,&00,&00,&00 ; [
+        =       &00,&20,&20,&20,&20,&2C,&02,&04,&08,&0E ; \
+        =       &00,&00,&08,&04,&3E,&04,&08,&00,&00,&00 ; ]
+        =       &00,&00,&08,&1C,&2A,&08,&08,&00,&00,&00 ; ^
+        =       &00,&14,&14,&3E,&14,&3E,&14,&14,&00,&00 ; #
+        =       &00,&00,&00,&00,&3E,&00,&00,&00,&00,&00 ; _
+        =       &00,&00,&00,&1C,&02,&1E,&22,&1E,&00,&00 ; a
+        =       &00,&20,&20,&3C,&22,&22,&22,&3C,&00,&00 ; b
+        =       &00,&00,&00,&1E,&20,&20,&20,&1E,&00,&00 ; c
+        =       &00,&02,&02,&1E,&22,&22,&22,&1E,&00,&00 ; d
+        =       &00,&00,&00,&1C,&22,&3E,&20,&1C,&00,&00 ; e
+        =       &00,&04,&08,&08,&1C,&08,&08,&08,&00,&00 ; f
+        =       &00,&00,&00,&1E,&22,&22,&22,&1E,&02,&1C ; g
+        =       &00,&20,&20,&3C,&22,&22,&22,&22,&00,&00 ; h
+        =       &00,&08,&00,&18,&08,&08,&08,&1C,&00,&00 ; i
+        =       &00,&08,&00,&08,&08,&08,&08,&08,&08,&10 ; j
+        =       &00,&10,&10,&12,&14,&18,&14,&12,&00,&00 ; k
+        =       &00,&18,&08,&08,&08,&08,&08,&1C,&00,&00 ; l
+        =       &00,&00,&00,&34,&2A,&2A,&2A,&2A,&00,&00 ; m
+        =       &00,&00,&00,&3C,&22,&22,&22,&22,&00,&00 ; n
+        =       &00,&00,&00,&1C,&22,&22,&22,&1C,&00,&00 ; o
+        =       &00,&00,&00,&3C,&22,&22,&22,&3C,&20,&20 ; p
+        =       &00,&00,&00,&1E,&22,&22,&22,&1E,&02,&02 ; q
+        =       &00,&00,&00,&16,&18,&10,&10,&10,&00,&00 ; r
+        =       &00,&00,&00,&1E,&20,&1C,&02,&3C,&00,&00 ; s
+        =       &00,&08,&08,&1C,&08,&08,&08,&04,&00,&00 ; t
+        =       &00,&00,&00,&22,&22,&22,&22,&1E,&00,&00 ; u
+        =       &00,&00,&00,&22,&22,&14,&14,&08,&00,&00 ; v
+        =       &00,&00,&00,&22,&22,&2A,&2A,&14,&00,&00 ; w
+        =       &00,&00,&00,&22,&14,&08,&14,&22,&00,&00 ; x
+        =       &00,&00,&00,&22,&22,&22,&22,&1E,&02,&1C ; y
+        =       &00,&00,&00,&3E,&04,&08,&10,&3E,&00,&00 ; z
+        =       &00,&10,&10,&10,&10,&12,&06,&0A,&0E,&02 ; {
+        =       &00,&14,&14,&14,&14,&14,&14,&14,&00,&00 ; |
+        =       &00,&30,&08,&30,&08,&32,&06,&0A,&0E,&02 ; }
+        =       &00,&00,&08,&00,&3E,&00,&08,&00,&00,&00 ; ~
+        =       &00,&3E,&3E,&3E,&3E,&3E,&3E,&3E,&00,&00 ; &FF
+
+        END
diff --git a/s/vdu/vduwrch b/s/vdu/vduwrch
new file mode 100644
index 0000000000000000000000000000000000000000..5ffecd2de6cadc79f057145f35f54745d6ac95a2
--- /dev/null
+++ b/s/vdu/vduwrch
@@ -0,0 +1,3119 @@
+; Copyright 1996 Acorn Computers Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; > $.Source.VduWrch
+
+;       Text printing etc.
+;       ------------------
+
+;       Author          Tim Dobson
+;       Started         01-Sep-86
+;       Status          Mostly Arm-less
+
+; *****************************************************************************
+
+; CursorFlags bits
+
+; Bit   0       '81 column' mode
+; Bits  1-3     Cursor movement directions
+; Bit   4       Wrap instead of scroll in VDU4 mode
+; Bit   5       Don't move cursor after printing character
+; Bit   6       Don't wrap in VDU 5 mode
+; Bit   7       Unused but could be set by user
+; Bit   8       When in Wrch or screen update operation, set to 1 and forms
+;               part of mask for ROR #10.
+;               Otherwise is set to 0.
+;               Tested by cursor code to see if it can update CursorFlags.
+; Bits  9-17    Set to 101111000 so we can TST CursorFlags with itself ROR #10
+;               to test for OR of C81Bit, Vdu5Bit, silly movement bits in 1 go
+;               in order to optimise the case when none of these are present
+; Bit   18      Set when cursors are split - since bit 8 is set this triggers
+; Bit   19      Set when in teletext mode
+; Bit   20      Set when in page mode
+; Bit   21      Set when clip box calculations are enabled - this triggers
+; Bits  22-24   Unused, but probably have to be zero because of ROR #10
+; Bit   25      Actual cursor state  (1 => cursor is on screen)
+; Bit   26      Set if VDU disabled
+; Bits  27-28   Unused, but probably have to be zero because of ROR #10
+; Bit   29      TextExpand not up-to-date
+; Bit   30      VDU 5 mode
+; Bit   31      In the '81st' column
+
+InWrchBitPosn * 8
+InWrchBit        *  1 :SHL: InWrchBitPosn
+CursorsSplit     *  1 :SHL: 18
+TeletextMode     *  1 :SHL: 19
+PageMode         *  1 :SHL: 20
+ClipBoxEnableBit *  1 :SHL: 21
+ActualState      *  1 :SHL: 25
+VduDisabled      *  1 :SHL: 26
+TEUpdate         *  1 :SHL: 29
+Vdu5Bit          *  1 :SHL: 30
+C81Bit           *  1 :SHL: 31
+InitialCursorFlags * (&1E :SHL: 10) :OR: (C81Bit :SHR: (32-10))
+
+; The TST R6, R6, ROR #10 returns non-zero if
+;
+;       C81Bit OR Vdu5 OR (any of bits 1-4) OR CursorsSplit
+;       OR ClipBoxEnableBit OR (TEUpdate AND bit7) OR Teletext
+;
+; The last two of these are slightly undesirable, but fairly harmless since:
+;   (a) bit 7 is not normally set, and TEUpdate is only set when a colour
+;       change happens.
+;   (b) Teletext mode takes quite a long time anyway.
+; NB We don't believe that the TST needs to detect Teletext mode, but if you
+; want to stop it doing so, it's on your own head. Check everything carefully!
+
+; *****************************************************************************
+;
+;       Set default logical and physical colours
+
+bpp     RN      0
+fore    RN      1
+back    RN      2
+tabaddr RN      3
+index   RN      4
+source  RN      5
+dest    RN      6
+col     RN      7
+orrbits RN      8
+addbits RN      9
+bit0    RN      10
+
+hiaddr  RN      4
+loaddr  RN      5
+; dest          6
+hiword  RN      7       ; must be higher number than dest (for Colour16Bit)
+loword  RN      8
+cbyte   RN      9
+spare1  RN      10
+spare2  RN      11
+
+; *****************************************************************************
+;
+;       VDU 20 - Set default colours (if output mode not teletext)
+;        and palette (if display mode not teletext)
+;
+;       External routine, and DefaultColours called by mode change
+;        and SwitchOutputToSprite
+;
+; in:   R6 = CursorFlags (for output mode)
+;
+
+VDU20   ROUT
+        LDR     R0, [WsPtr, #DisplayModeFlags]  ; if display mode is
+        TST     R0, #Flag_Teletext              ; not teletext, then restore
+        BNE     %FT10                           ; default palette
+
+        Push    R14
+        BL      PalInit                         ; R6 is preserved over the call
+        Pull    R14
+10
+        TST     R6, #TeletextMode               ; if output mode is teletext
+        MOVNE   PC, R14                         ; then don't reset colours
+
+; else drop thru to ...
+
+DefaultColours ROUT
+        Push    R14
+
+        ASSERT  GPLBMD = GPLFMD +4
+        ASSERT  GFCOL = GPLFMD +8
+        ASSERT  GBCOL = GPLFMD +12
+
+        ASSERT  TBTint = TFTint +4
+        ASSERT  GFTint = TFTint +8
+        ASSERT  GBTint = TFTint +12
+
+        ASSERT  TBackCol = TForeCol +4
+        ASSERT  back > fore
+
+        MOV     R0, #0                          ; foreground action := store
+        MOV     R1, #0                          ; background action := store
+        LDR     R2, [WsPtr, #NColour]   ; GCOL(0,(NColour AND 7)) except
+        TST     R2, #&F0                ; for 256 colour modes
+        ANDEQ   R2, R2, #7              ; when we use colour 63 (=NColour)
+        MOV     R3, #0                          ; background colour := black
+        ADD     R4, WsPtr, #GPLFMD      ; store GPLFMD, GPLBMD, GFCOL, GBCOL
+        STMIA   R4, {R0-R3}
+
+        MOV     R0, #&FF                ; R0 = TFTint := &FF; R1 = TBTint := 0
+        MOV     R2, #&FF                ; R2 = GFTint := &FF; R3 = GBTint := 0
+        ADD     R4, WsPtr, #TFTint
+        STMIA   R4, {R0-R3}
+
+        BL      SetColour                       ; Update FgEcf & BgEcf
+
+        LDR     bpp, [WsPtr, #BitsPerPix]
+        LDR     fore, [WsPtr, #NColour]         ; Number of colours allowed -1
+        TEQ     fore, #63                       ; are we in bodgy 256 colour mode?
+        MOVEQ   fore, #255                      ; then use colour 255 (cringe!)
+        TEQ     fore, #15                       ; 16 colour mode
+        MOVEQ   fore, #7                        ; Default is 7 for this depth of mode
+
+ [ {TRUE} ; TMD 25-Jun-93 - change default text colour in 16bpp to &7FFF, and 32bpp to &00FFFFFF
+        LDR     r14, =&FFFF
+        CMP     fore, r14
+        BICEQ   fore, fore, #&8000              ;     &FFFF     ->      &7FFF
+        BICHI   fore, fore, #&FF000000          ; &FFFFFFFF     ->  &00FFFFFF
+ ]
+        MOV     back, #0
+        ADD     R14, WsPtr, #TForeCol
+        STMIA   R14, {fore, back}               ; save fgd + bgd text colours
+        Pull    R14
+
+;       and drop thru to ...
+
+SetColours ROUT
+        Push    R14
+
+        MOV     R8, back, ROR bpp               ; move bits to top of word
+        MOV     R9, fore, ROR bpp
+
+        LDR     bpp, [WsPtr, #BytesPerChar]     ; fudge some more bits
+        ORR     back, R8, back, ROR bpp
+        ORR     fore, R9, fore, ROR bpp
+
+        MOV     LR, bpp
+10
+        TEQ     LR, #32                         ; have we finished replicating through word yet?
+        ORRNE   back, back, back, LSR LR        ;   if not then expand again through the word
+        ORRNE   fore, fore, fore, LSR LR
+        MOVNE   LR, LR, LSL #1                  ; double the shift ready for the next pass
+        BNE     %BT10                           ;   and loop again!
+
+        STR     fore, [WsPtr, #TextFgColour]
+        STR     back, [WsPtr, #TextBgColour]    ; store bit expanded fore / background colour
+
+;       New colour change code
+;       entered with fore, back expanded to <bytes_per_char> bits
+;       and bpp set to be <bytes_per_char>
+
+        TEQ     bpp, #16
+        BEQ     Colour16Bit     ; can't optimise
+
+      [ VIDC_Type = "VIDC20"
+        TEQ     bpp, #32
+        BEQ     Colour32Bit     ; cannie optimise captin!
+      ]
+
+        EOR     fore, fore, back                ; so we can AND and EOR
+      [ VIDC_Type = "VIDC20"
+        LDR     loaddr, [WsPtr, #TextExpandArea]
+      |
+        ADD     loaddr, WsPtr, #TextExpand
+      ]
+        ADD     dest, loaddr, bpp, LSL #8       ; end+1 destination addr
+        MOV     hiaddr, dest                    ; TextPlain now moves around,
+                                                ; depending on bytes-per-char
+10
+        LDR     cbyte, [hiaddr], #4
+        AND     cbyte, cbyte, fore
+        EOR     cbyte, cbyte, back
+        STR     cbyte, [loaddr], #4
+        TEQ     loaddr, dest
+        BNE     %BT10
+
+        Pull    PC
+        LTORG
+
+; *****************************************************************************
+;
+;       Colour16Bit - Set up colour table for MODE 10
+;       Entered with R14 already pushed
+;
+
+Colour16Bit
+      [ VIDC_Type = "VIDC20"
+        LDR     tabaddr, [WsPtr, #TextExpandArea]
+      |
+        ADD     tabaddr, WsPtr, #TextExpandArea
+      ]
+
+        ADR     hiaddr, C16BTab
+        ADD     R10, hiaddr, #128
+C16B20
+        ADR     loaddr, C16BTab
+C16B30
+        LDMIA   hiaddr, {dest, hiword}
+        BL      OutputColour
+        MOV     dest, hiword
+        BL      OutputColour
+
+        LDMIA   loaddr!, {dest, loword}
+        BL      OutputColour
+        MOV     dest, loword
+        BL      OutputColour
+
+        TEQ     loaddr, R10
+        BNE     C16B30
+
+        ADD     hiaddr, hiaddr, #8
+        TEQ     hiaddr, R10
+        BNE     C16B20
+
+        Pull    PC
+
+C16BTab
+        &       &00000000, &00000000
+        &       &00000000, &FFFF0000
+        &       &00000000, &0000FFFF
+        &       &00000000, &FFFFFFFF
+
+        &       &FFFF0000, &00000000
+        &       &FFFF0000, &FFFF0000
+        &       &FFFF0000, &0000FFFF
+        &       &FFFF0000, &FFFFFFFF
+
+        &       &0000FFFF, &00000000
+        &       &0000FFFF, &FFFF0000
+        &       &0000FFFF, &0000FFFF
+        &       &0000FFFF, &FFFFFFFF
+
+        &       &FFFFFFFF, &00000000
+        &       &FFFFFFFF, &FFFF0000
+        &       &FFFFFFFF, &0000FFFF
+        &       &FFFFFFFF, &FFFFFFFF
+
+      [ VIDC_Type = "VIDC20"
+
+; *****************************************************************************
+;
+;       Colour32Bit - Set up colour table for MODE 48 (32 bit per pixel)
+;       Entered with R14 already pushed
+;
+
+Colour32Bit
+        LDR     tabaddr, [WsPtr, #TextExpandArea]
+        MOV     dest, #0
+C32B20
+
+; Expand the value in in 'hiaddr' so that each bit is stored as a word
+; zero bits are stored in the background and non-zero bits are stored in
+; foreground.  This is indexed when expanding the 1BPP VDU font out to
+; the current depth.
+
+        TST     dest, #1 <<7
+        STREQ   back, [tabaddr], #4
+        STRNE   fore, [tabaddr], #4
+        TST     dest, #1 <<6
+        STREQ   back, [tabaddr], #4
+        STRNE   fore, [tabaddr], #4
+        TST     dest, #1 <<5
+        STREQ   back, [tabaddr], #4
+        STRNE   fore, [tabaddr], #4
+        TST     dest, #1 <<4
+        STREQ   back, [tabaddr], #4
+        STRNE   fore, [tabaddr], #4
+        TST     dest, #1 <<3
+        STREQ   back, [tabaddr], #4
+        STRNE   fore, [tabaddr], #4
+        TST     dest, #1 <<2
+        STREQ   back, [tabaddr], #4
+        STRNE   fore, [tabaddr], #4
+        TST     dest, #1 <<1
+        STREQ   back, [tabaddr], #4
+        STRNE   fore, [tabaddr], #4
+        TST     dest, #1 <<0
+        STREQ   back, [tabaddr], #4
+        STRNE   fore, [tabaddr], #4
+
+        ADD     dest, dest, #1
+        TEQ     dest, #256
+        BNE     C32B20
+
+        Pull    "PC"
+
+      ]
+
+; *****************************************************************************
+;
+;       Fast CLS used when no text window defined
+;
+
+FastCLS ROUT
+        Push    R14
+        BL      CheckTEUpdate
+
+        TST     R6, #TeletextMode               ; teletext mode ?
+        LDREQ   R0, [WsPtr, #TextBgColour]
+        BEQ     %FT10                           ; and skip
+
+        BL      TTXFastCLS                      ; else clear teletext map
+        MOV     R0, #0                          ; and clear to zero
+10
+        LDR     R8, [WsPtr, #ScreenStart]
+        MOV     R1, R0
+        MOV     R2, R0
+        MOV     R3, R0
+        MOV     R4, R0
+        MOV     R5, R0
+        MOV     R6, R0
+        MOV     R7, R0
+        LDR     R9, [WsPtr, #ScreenSize]        ; screen size in bytes
+20
+        SUBS    R9, R9, #256                    ; if another 256 to do
+        STMCSIA R8!, {R0 - R7}                  ; a bit excessive, I know !
+        STMCSIA R8!, {R0 - R7}
+        STMCSIA R8!, {R0 - R7}
+        STMCSIA R8!, {R0 - R7}
+        STMCSIA R8!, {R0 - R7}
+        STMCSIA R8!, {R0 - R7}
+        STMCSIA R8!, {R0 - R7}
+        STMCSIA R8!, {R0 - R7}
+        BHI     %BT20                           ; only loop if more to do
+        ADDCC   R9, R9, #256                    ; add back the last 256
+30
+        SUBS    R9, R9, #4
+        STMCSIA R8!, {R0}
+        BHI     %BT30
+
+        Pull    PC
+
+; *****************************************************************************
+;
+;       Home cursor to "top left"
+
+RS
+Home
+        MOV     R0, #0
+        MOV     R1, #0
+        B       TabR0R1
+
+; *****************************************************************************
+;
+;       Address text cursor position
+; in:   R0 = X position
+;       R1 = Y position
+;
+; out:  CursorAddr contains screen address
+
+CursorR0R1
+        STR     R0, [WsPtr, #CursorX]
+        STR     R1, [WsPtr, #CursorY]
+CTADDR10
+        Push    R14
+        BL      AddressR0R1
+        STR     R2, [WsPtr, #CursorAddr]
+        Pull    PC
+
+;       Calculate cursor address
+
+AddressCursor
+        LDR     R0, [WsPtr, #CursorX]
+        LDR     R1, [WsPtr, #CursorY]
+        B       CTADDR10
+
+
+;       Address an X,Y text position in R0,R1
+;       R2 is screen address on exit
+;       R1, R3, R4 corrupted; R0, R5-R13 preserved
+
+AddressR0R1     ROUT
+        LDR     R4, [WsPtr, #RowLength]
+        LDR     R3, [WsPtr, #Log2BPC]
+        LDR     R2, [WsPtr, #ScreenStart] ; start address of top of screen
+        ADD     R2, R2, R0, LSL R3        ; add in X offset
+        MLA     R2, R4, R1, R2            ; add in Y*RowLength
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       Write character in R0 (>=32) to the screen
+;       R6 = CursorFlags on entry
+;
+
+font    RN      1
+screen  RN      2
+bigfont RN      3
+mask    RN      4
+tophalf RN      5
+bottomhalf RN   6
+lbpp    RN      7
+byte    RN      8
+scrbyte RN      9
+scrbyte2 RN     10
+linelen RN      11
+
+; *****Comment by DJS: Given we want applications to use the windowing world,
+;   shouldn't we be optimising VDU 5 characters a bit more here? - even if it
+;   makes VDU 4 characters a bit less optimal?
+
+TimWrch ROUT
+        LDR     R1, [WsPtr, #VduStatus]         ; test all silly flags at once
+        TST     R1, #Vdu2Mode
+        TSTEQ   R6, #(VduDisabled :OR: TEUpdate :OR: C81Bit :OR: Vdu5Bit)
+        TSTEQ   R6, #(TeletextMode :OR: ClipBoxEnableBit)
+10
+        ADDEQ   font, WsPtr, #(Font-32*8)
+        ADDEQ   font, font, R0, LSL #3
+        LDMEQIA font, {tophalf, bottomhalf}
+        Push    R14, EQ
+        ADREQ   R14, %FT15
+        LDREQ   screen, [WsPtr, #CursorAddr]
+      [ VIDC_Type = "VIDC20"
+        LDREQ   bigfont, [WsPtr, #TextExpandArea]
+      |
+        ADDEQ   bigfont, WsPtr, #TextExpand
+      ]
+        LDREQ   linelen, [WsPtr, #LineLength]
+        LDREQ   PC, [WsPtr, #WrchNbit]
+
+; *****Change made by DJS
+; Moved the following code down to a place where we don't have to branch
+; around it!
+;        B       %FT20
+;15
+;        Pull    R14
+;PostCharMove
+;        LDR     R6, [WsPtr, #CursorFlags]
+;        TST     R6, #32                         ; move cursor after char ?
+;        BEQ     CHT                             ; move "right" & test for C81
+;        MOV     PC, R14
+;
+;20
+; *****End of change made by DJS
+
+; if printing enabled then we want to print this character,
+; so pull the old R14 off the stack
+
+        TST     R1, #Vdu2Mode
+        Pull    R14, NE                         ; in VDU 2 mode, so pull R14
+        TST     R6, #VduDisabled                ; if VDU disabled
+        MOVNE   PC, R14                         ; then don't print it
+        TST     R6, #TEUpdate                   ; if colours need updating
+        BNE     %FT30                           ; then do it + return to %25
+25
+        TST     R6, #Vdu5Bit
+        BNE     Vdu5Wrch
+        TST     R6, #C81Bit
+        BNE     %FT40
+35
+        TST     R6, #TeletextMode
+        BNE     TTXWrch
+        TST     R6, #ClipBoxEnableBit
+        BEQ     %BT10                           ; must enter with EQ
+
+; now update clip box
+
+        Push    R14
+        BL      ClipCursorCell
+        Pull    R14
+        TST     R0, #0                          ; set EQ
+        B       %BT10
+
+; *****Change made by DJS
+; Code moved down from above.
+
+15
+        Pull    R14
+PostCharMove
+        LDR     R6, [WsPtr, #CursorFlags]
+        TST     R6, #32                         ; move cursor after char ?
+        BEQ     CHT                             ; move "right" & test for C81
+        MOV     PC, R14
+
+; *****End of change made by DJS
+
+30
+        Push    "R0,R14"
+        BL      ReallySetColours                ; update colour table
+        Pull    "R0,R14"
+        B       %BT25
+
+40
+        Push    "R0, R14"
+        BL      RCRLFR6                         ; do pending CRLF
+        Pull    "R0, R14"
+        B       %BT35
+
+; *****************************************************************************
+;
+;       Write character in 1 bit-per-pixel mode
+
+Wrch1bit
+
+; *****Change made by DJS
+; Original code was:
+;        MOV     mask, #&FF000000
+;
+;        AND     byte, mask, tophalf, LSL #24
+;        LDRB    scrbyte, [bigfont, byte, LSR #24]
+;        STRB    scrbyte, [screen], linelen
+;        AND     byte, mask, tophalf, LSL #16
+;        LDRB    scrbyte, [bigfont, byte, LSR #24]
+;        STRB    scrbyte, [screen], linelen
+;        AND     byte, mask, tophalf, LSL #8
+;        LDRB    scrbyte, [bigfont, byte, LSR #24]
+;        STRB    scrbyte, [screen], linelen
+;        LDRB    scrbyte, [bigfont, tophalf, LSR #24]
+;        STRB    scrbyte, [screen], linelen
+;
+;        AND     byte, mask, bottomhalf, LSL #24
+;        LDRB    scrbyte, [bigfont, byte, LSR #24]
+;        STRB    scrbyte, [screen], linelen
+;        AND     byte, mask, bottomhalf, LSL #16
+;        LDRB    scrbyte, [bigfont, byte, LSR #24]
+;        STRB    scrbyte, [screen], linelen
+;        AND     byte, mask, bottomhalf, LSL #8
+;        LDRB    scrbyte, [bigfont, byte, LSR #24]
+;        STRB    scrbyte, [screen], linelen
+;        LDRB    scrbyte, [bigfont, bottomhalf, LSR #24]
+;        STRB    scrbyte, [screen], linelen
+;
+; There is no need to use "mask" at all in this...
+
+        MOV     byte, tophalf, LSL #24
+        LDRB    scrbyte, [bigfont, byte, LSR #24]
+        STRB    scrbyte, [screen], linelen
+        MOV     byte, tophalf, LSL #16
+        LDRB    scrbyte, [bigfont, byte, LSR #24]
+        STRB    scrbyte, [screen], linelen
+        MOV     byte, tophalf, LSL #8
+        LDRB    scrbyte, [bigfont, byte, LSR #24]
+        STRB    scrbyte, [screen], linelen
+        LDRB    scrbyte, [bigfont, tophalf, LSR #24]
+        STRB    scrbyte, [screen], linelen
+
+        MOV     byte, bottomhalf, LSL #24
+        LDRB    scrbyte, [bigfont, byte, LSR #24]
+        STRB    scrbyte, [screen], linelen
+        MOV     byte, bottomhalf, LSL #16
+        LDRB    scrbyte, [bigfont, byte, LSR #24]
+        STRB    scrbyte, [screen], linelen
+        MOV     byte, bottomhalf, LSL #8
+        LDRB    scrbyte, [bigfont, byte, LSR #24]
+        STRB    scrbyte, [screen], linelen
+        LDRB    scrbyte, [bigfont, bottomhalf, LSR #24]
+        STRB    scrbyte, [screen], linelen
+
+; *****End of change made by DJS
+
+        [ 1=0 ; *** No 1 bpc non-BBC gap modes at present ***
+        LDR     R0, [WsPtr, #ModeFlags]         ; now test for non-BBC gap mode
+        AND     R0, R0, #(Flag_GapMode :OR: Flag_BBCGapMode) ; we want R0=0 iff
+        EORS    R0, R0, #Flag_GapMode           ; (gapmode AND NOT bbcgapmode)
+        MOVNE   PC, R14
+        LDRB    scrbyte, [bigfont]              ; store backgd in next 2
+        STRB    scrbyte, [screen], linelen
+        STRB    scrbyte, [screen], linelen
+        ]
+        MOV     PC, R14
+
+Wrch1bitDouble
+
+; *****Change made by DJS
+; Original code was:
+;        MOV     mask, #&FF000000
+;
+;        AND     byte, mask, tophalf, LSL #24
+;        LDRB    scrbyte, [bigfont, byte, LSR #24]
+;        STRB    scrbyte, [screen], linelen
+;        STRB    scrbyte, [screen], linelen
+;        AND     byte, mask, tophalf, LSL #16
+;        LDRB    scrbyte, [bigfont, byte, LSR #24]
+;        STRB    scrbyte, [screen], linelen
+;        STRB    scrbyte, [screen], linelen
+;        AND     byte, mask, tophalf, LSL #8
+;        LDRB    scrbyte, [bigfont, byte, LSR #24]
+;        STRB    scrbyte, [screen], linelen
+;        STRB    scrbyte, [screen], linelen
+;        LDRB    scrbyte, [bigfont, tophalf, LSR #24]
+;        STRB    scrbyte, [screen], linelen
+;        STRB    scrbyte, [screen], linelen
+;
+;        AND     byte, mask, bottomhalf, LSL #24
+;        LDRB    scrbyte, [bigfont, byte, LSR #24]
+;        STRB    scrbyte, [screen], linelen
+;        STRB    scrbyte, [screen], linelen
+;        AND     byte, mask, bottomhalf, LSL #16
+;        LDRB    scrbyte, [bigfont, byte, LSR #24]
+;        STRB    scrbyte, [screen], linelen
+;        STRB    scrbyte, [screen], linelen
+;        AND     byte, mask, bottomhalf, LSL #8
+;        LDRB    scrbyte, [bigfont, byte, LSR #24]
+;        STRB    scrbyte, [screen], linelen
+;        STRB    scrbyte, [screen], linelen
+;        LDRB    scrbyte, [bigfont, bottomhalf, LSR #24]
+;        STRB    scrbyte, [screen], linelen
+;        STRB    scrbyte, [screen], linelen
+;
+; As above, "mask" is not needed.
+
+        MOV     byte, tophalf, LSL #24
+        LDRB    scrbyte, [bigfont, byte, LSR #24]
+        STRB    scrbyte, [screen], linelen
+        STRB    scrbyte, [screen], linelen
+        MOV     byte, tophalf, LSL #16
+        LDRB    scrbyte, [bigfont, byte, LSR #24]
+        STRB    scrbyte, [screen], linelen
+        STRB    scrbyte, [screen], linelen
+        MOV     byte, tophalf, LSL #8
+        LDRB    scrbyte, [bigfont, byte, LSR #24]
+        STRB    scrbyte, [screen], linelen
+        STRB    scrbyte, [screen], linelen
+        LDRB    scrbyte, [bigfont, tophalf, LSR #24]
+        STRB    scrbyte, [screen], linelen
+        STRB    scrbyte, [screen], linelen
+
+        MOV     byte, bottomhalf, LSL #24
+        LDRB    scrbyte, [bigfont, byte, LSR #24]
+        STRB    scrbyte, [screen], linelen
+        STRB    scrbyte, [screen], linelen
+        MOV     byte, bottomhalf, LSL #16
+        LDRB    scrbyte, [bigfont, byte, LSR #24]
+        STRB    scrbyte, [screen], linelen
+        STRB    scrbyte, [screen], linelen
+        MOV     byte, bottomhalf, LSL #8
+        LDRB    scrbyte, [bigfont, byte, LSR #24]
+        STRB    scrbyte, [screen], linelen
+        STRB    scrbyte, [screen], linelen
+        LDRB    scrbyte, [bigfont, bottomhalf, LSR #24]
+        STRB    scrbyte, [screen], linelen
+        STRB    scrbyte, [screen], linelen
+
+; *****End of change made by DJS
+
+        [ 1=0 ; *** No 1 bpc non-BBC gap modes at present ***
+        LDR     R0, [WsPtr, #ModeFlags]         ; now test for non-BBC gap mode
+        AND     R0, R0, #(Flag_GapMode :OR: Flag_BBCGapMode) ; we want R0=0 iff
+        EORS    R0, R0, #Flag_GapMode           ; (gapmode AND NOT bbcgapmode)
+        MOVNE   PC, R14
+        LDRB    scrbyte, [bigfont]              ; store backgd in next 2
+        STRB    scrbyte, [screen], linelen
+        STRB    scrbyte, [screen], linelen
+        ]
+        MOV     PC, R14
+
+Wrch2bit
+
+; *****Change made by DJS
+; Original code was:
+;
+;        MOV     mask, #&FE000000
+;        SUB     linelen, linelen, #1
+;
+;        AND     byte, mask, tophalf, LSL #24
+;        LDR     scrbyte, [bigfont, byte, LSR #23]
+;        MOVS    byte, tophalf, LSR #1
+;        MOVCS   scrbyte, scrbyte, LSR #16
+;        STRB    scrbyte, [screen], #1
+;        MOV     scrbyte, scrbyte, LSR #8
+;        STRB    scrbyte, [screen], linelen
+;
+;        AND     byte, mask, tophalf, LSL #16
+;        LDR     scrbyte, [bigfont, byte, LSR #23]
+;        MOVS    byte, tophalf, LSR #9
+;        MOVCS   scrbyte, scrbyte, LSR #16
+;        STRB    scrbyte, [screen], #1
+;        MOV     scrbyte, scrbyte, LSR #8
+;        STRB    scrbyte, [screen], linelen
+;
+;        AND     byte, mask, tophalf, LSL #8
+;        LDR     scrbyte, [bigfont, byte, LSR #23]
+;        MOVS    byte, tophalf, LSR #17
+;        MOVCS   scrbyte, scrbyte, LSR #16
+;        STRB    scrbyte, [screen], #1
+;        MOV     scrbyte, scrbyte, LSR #8
+;        STRB    scrbyte, [screen], linelen
+;
+;        AND     byte, mask, tophalf
+;        LDR     scrbyte, [bigfont, byte, LSR #23]
+;        MOVS    byte, tophalf, LSR #25
+;        MOVCS   scrbyte, scrbyte, LSR #16
+;        STRB    scrbyte, [screen], #1
+;        MOV     scrbyte, scrbyte, LSR #8
+;        STRB    scrbyte, [screen], linelen
+;
+;        AND     byte, mask, bottomhalf, LSL #24
+;        LDR     scrbyte, [bigfont, byte, LSR #23]
+;        MOVS    byte, bottomhalf, LSR #1
+;        MOVCS   scrbyte, scrbyte, LSR #16
+;        STRB    scrbyte, [screen], #1
+;        MOV     scrbyte, scrbyte, LSR #8
+;        STRB    scrbyte, [screen], linelen
+;
+;        AND     byte, mask, bottomhalf, LSL #16
+;        LDR     scrbyte, [bigfont, byte, LSR #23]
+;        MOVS    byte, bottomhalf, LSR #9
+;        MOVCS   scrbyte, scrbyte, LSR #16
+;        STRB    scrbyte, [screen], #1
+;        MOV     scrbyte, scrbyte, LSR #8
+;        STRB    scrbyte, [screen], linelen
+;
+;        AND     byte, mask, bottomhalf, LSL #8
+;        LDR     scrbyte, [bigfont, byte, LSR #23]
+;        MOVS    byte, bottomhalf, LSR #17
+;        MOVCS   scrbyte, scrbyte, LSR #16
+;        STRB    scrbyte, [screen], #1
+;        MOV     scrbyte, scrbyte, LSR #8
+;        STRB    scrbyte, [screen], linelen
+;
+;        AND     byte, mask, bottomhalf
+;        LDR     scrbyte, [bigfont, byte, LSR #23]
+;        MOVS    byte, bottomhalf, LSR #25
+;        MOVCS   scrbyte, scrbyte, LSR #16
+;        STRB    scrbyte, [screen], #1
+;        MOV     scrbyte, scrbyte, LSR #8
+;        STRB    scrbyte, [screen], linelen
+;
+; Messing around with this a bit, I found the following shorter & faster
+; code:
+
+        MOV     mask, #&7F
+        SUB     linelen, linelen, #1
+
+        ANDS    byte, mask, tophalf, LSR #1             ;C := bit 0 of tophalf
+        LDR     scrbyte, [bigfont, byte, LSL #2]
+        MOVCS   scrbyte, scrbyte, LSR #16
+        STRB    scrbyte, [screen], #1
+        MOV     scrbyte, scrbyte, LSR #8
+        STRB    scrbyte, [screen], linelen
+
+        ANDS    byte, mask, tophalf, LSR #9             ;C := bit 8 of tophalf
+        LDR     scrbyte, [bigfont, byte, LSL #2]
+        MOVCS   scrbyte, scrbyte, LSR #16
+        STRB    scrbyte, [screen], #1
+        MOV     scrbyte, scrbyte, LSR #8
+        STRB    scrbyte, [screen], linelen
+
+        ANDS    byte, mask, tophalf, LSR #17            ;C := bit 16 of tophalf
+        LDR     scrbyte, [bigfont, byte, LSL #2]
+        MOVCS   scrbyte, scrbyte, LSR #16
+        STRB    scrbyte, [screen], #1
+        MOV     scrbyte, scrbyte, LSR #8
+        STRB    scrbyte, [screen], linelen
+
+        ANDS    byte, mask, tophalf, LSR #25            ;C := bit 24 of tophalf
+        LDR     scrbyte, [bigfont, byte, LSL #2]
+        MOVCS   scrbyte, scrbyte, LSR #16
+        STRB    scrbyte, [screen], #1
+        MOV     scrbyte, scrbyte, LSR #8
+        STRB    scrbyte, [screen], linelen
+
+        ANDS    byte, mask, bottomhalf, LSR #1          ;C := bit 0 of b'half
+        LDR     scrbyte, [bigfont, byte, LSL #2]
+        MOVCS   scrbyte, scrbyte, LSR #16
+        STRB    scrbyte, [screen], #1
+        MOV     scrbyte, scrbyte, LSR #8
+        STRB    scrbyte, [screen], linelen
+
+        ANDS    byte, mask, bottomhalf, LSR #9          ;C := bit 8 of b'half
+        LDR     scrbyte, [bigfont, byte, LSL #2]
+        MOVCS   scrbyte, scrbyte, LSR #16
+        STRB    scrbyte, [screen], #1
+        MOV     scrbyte, scrbyte, LSR #8
+        STRB    scrbyte, [screen], linelen
+
+        ANDS    byte, mask, bottomhalf, LSR #17         ;C := bit 16 of b'half
+        LDR     scrbyte, [bigfont, byte, LSL #2]
+        MOVCS   scrbyte, scrbyte, LSR #16
+        STRB    scrbyte, [screen], #1
+        MOV     scrbyte, scrbyte, LSR #8
+        STRB    scrbyte, [screen], linelen
+
+        ANDS    byte, mask, bottomhalf, LSR #25         ;C := bit 24 of b'half
+        LDR     scrbyte, [bigfont, byte, LSL #2]
+        MOVCS   scrbyte, scrbyte, LSR #16
+        STRB    scrbyte, [screen], #1
+        MOV     scrbyte, scrbyte, LSR #8
+        STRB    scrbyte, [screen], linelen
+
+; *****End of change made by DJS
+
+        LDR     R0, [WsPtr, #ModeFlags]         ; now test for non-BBC gap mode
+        AND     R0, R0, #(Flag_GapMode :OR: Flag_BBCGapMode) ; we want R0=0 iff
+        EORS    R0, R0, #Flag_GapMode           ; (gapmode AND NOT bbcgapmode)
+        MOVNE   PC, R14
+        LDRB    scrbyte, [bigfont]              ; store backgd in next 2
+        STRB    scrbyte, [screen], #1
+        STRB    scrbyte, [screen], linelen
+        STRB    scrbyte, [screen], #1
+        STRB    scrbyte, [screen], linelen
+        MOV     PC, R14
+
+Wrch4bit
+        MOV     R10, #0                         ; extra rows are 0 if not TTX
+        LDR     R0, [WsPtr, #ModeFlags]         ; now test for non-BBC gap mode
+        AND     R0, R0, #(Flag_GapMode :OR: Flag_BBCGapMode) ; we want R0=0 iff
+        EOR     R0, R0, #Flag_GapMode           ; (gapmode AND NOT bbcgapmode)
+        MOV     mask, #&FF000000                ; don't set mask in Wrch4bitTTX
+Wrch4bitTTX
+        AND     byte, mask, tophalf, LSL #24
+        LDR     scrbyte, [bigfont, byte, LSR #22]
+        STR     scrbyte, [screen], linelen
+        AND     byte, mask, tophalf, LSL #16
+        LDR     scrbyte, [bigfont, byte, LSR #22]
+        STR     scrbyte, [screen], linelen
+        AND     byte, mask, tophalf, LSL #8
+        LDR     scrbyte, [bigfont, byte, LSR #22]
+        STR     scrbyte, [screen], linelen
+        AND     byte, mask, tophalf
+        LDR     scrbyte, [bigfont, byte, LSR #22]
+        STR     scrbyte, [screen], linelen
+
+        AND     byte, mask, bottomhalf, LSL #24
+        LDR     scrbyte, [bigfont, byte, LSR #22]
+        STR     scrbyte, [screen], linelen
+        AND     byte, mask, bottomhalf, LSL #16
+        LDR     scrbyte, [bigfont, byte, LSR #22]
+        STR     scrbyte, [screen], linelen
+        AND     byte, mask, bottomhalf, LSL #8
+        LDR     scrbyte, [bigfont, byte, LSR #22]
+        STR     scrbyte, [screen], linelen
+        AND     byte, mask, bottomhalf
+        LDR     scrbyte, [bigfont, byte, LSR #22]
+        STR     scrbyte, [screen], linelen
+
+        TEQ     R0, #0
+        MOVNE   PC, R14
+        AND     byte, mask, R10, LSL #8
+        LDR     scrbyte, [bigfont, byte, LSR #22] ;get 1st extra (0 or for TTX)
+        STR     scrbyte, [screen], linelen
+        AND     byte, mask, R10
+        LDR     scrbyte, [bigfont, byte, LSR #22] ;get 2nd extra (0 or for TTX)
+        STR     scrbyte, [screen], linelen
+        MOV     PC, R14
+
+Wrch8bit
+        MOV     mask, #&FF000000
+
+        AND     byte, mask, tophalf, LSL #24
+        ADD     byte, bigfont, byte, LSR #21
+        LDMIA   byte, {scrbyte, scrbyte2}
+        STMIA   screen, {scrbyte, scrbyte2}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, tophalf, LSL #16
+        ADD     byte, bigfont, byte, LSR #21
+        LDMIA   byte, {scrbyte, scrbyte2}
+        STMIA   screen, {scrbyte, scrbyte2}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, tophalf, LSL #8
+        ADD     byte, bigfont, byte, LSR #21
+        LDMIA   byte, {scrbyte, scrbyte2}
+        STMIA   screen, {scrbyte, scrbyte2}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, tophalf
+        ADD     byte, bigfont, byte, LSR #21
+        LDMIA   byte, {scrbyte, scrbyte2}
+        STMIA   screen, {scrbyte, scrbyte2}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, bottomhalf, LSL #24
+        ADD     byte, bigfont, byte, LSR #21
+        LDMIA   byte, {scrbyte, scrbyte2}
+        STMIA   screen, {scrbyte, scrbyte2}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, bottomhalf, LSL #16
+        ADD     byte, bigfont, byte, LSR #21
+        LDMIA   byte, {scrbyte, scrbyte2}
+        STMIA   screen, {scrbyte, scrbyte2}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, bottomhalf, LSL #8
+        ADD     byte, bigfont, byte, LSR #21
+        LDMIA   byte, {scrbyte, scrbyte2}
+        STMIA   screen, {scrbyte, scrbyte2}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, bottomhalf
+        ADD     byte, bigfont, byte, LSR #21
+        LDMIA   byte, {scrbyte, scrbyte2}
+        STMIA   screen, {scrbyte, scrbyte2}
+
+        [ 1=0 ; *** No 8 bpc non-BBC gap modes at present ***
+        LDR     R0, [WsPtr, #ModeFlags]         ; now test for non-BBC gap mode
+        AND     R0, R0, #(Flag_GapMode :OR: Flag_BBCGapMode) ; we want R0=0 iff
+        EORS    R0, R0, #Flag_GapMode           ; (gapmode AND NOT bbcgapmode)
+        MOVNE   PC, R14
+        LDMIA   bigfont, {scrbyte, scrbyte2}    ; store backgd in next 2
+        ADD     screen, screen, linelen
+        STMIA   screen, {scrbyte, scrbyte2}
+        ADD     screen, screen, linelen
+        STMIA   screen, {scrbyte, scrbyte2}
+        ]
+        MOV     PC, R14
+
+Wrch16bit
+        MOV     mask, #&FF000000
+        SUB     linelen, linelen, #16
+
+        AND     byte, mask, tophalf, LSL #24
+        ADD     byte, bigfont, byte, LSR #20
+        LDMIA   byte!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        LDMIA   byte!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, tophalf, LSL #16
+        ADD     byte, bigfont, byte, LSR #20
+        LDMIA   byte!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        LDMIA   byte!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, tophalf, LSL #8
+        ADD     byte, bigfont, byte, LSR #20
+        LDMIA   byte!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        LDMIA   byte!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, tophalf
+        ADD     byte, bigfont, byte, LSR #20
+        LDMIA   byte!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        LDMIA   byte!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, bottomhalf, LSL #24
+        ADD     byte, bigfont, byte, LSR #20
+        LDMIA   byte!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        LDMIA   byte!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, bottomhalf, LSL #16
+        ADD     byte, bigfont, byte, LSR #20
+        LDMIA   byte!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        LDMIA   byte!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, bottomhalf, LSL #8
+        ADD     byte, bigfont, byte, LSR #20
+        LDMIA   byte!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        LDMIA   byte!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, bottomhalf
+        ADD     byte, bigfont, byte, LSR #20
+        LDMIA   byte!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        LDMIA   byte!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+
+        [ 1=0 ; *** No 16 bpc non-BBC gap modes at present ***
+        LDR     R0, [WsPtr, #ModeFlags]         ; now test for non-BBC gap mode
+        AND     R0, R0, #(Flag_GapMode :OR: Flag_BBCGapMode) ; we want R0=0 iff
+        EORS    R0, R0, #Flag_GapMode           ; (gapmode AND NOT bbcgapmode)
+        MOVNE   PC, R14
+        LDMIA   bigfont, {scrbyte, scrbyte2}    ; store backgd in next 2
+        ADD     screen, screen, linelen
+        STMIA   screen!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        ADD     screen, screen, linelen
+        STMIA   screen!, {scrbyte, scrbyte2}
+        STMIA   screen!, {scrbyte, scrbyte2}
+        ]
+        MOV     PC, R14
+
+; *****************************************************************************
+
+; Write a character at 32 bit per pixel
+;
+; NB: This code assumes that we have no concept of gap modes.
+
+Wrch32bit
+        Push    "R0-R1,R5-R7,R9-R10,R12,LR"
+
+        MOV     mask, #&FF000000
+
+        AND     byte, mask, tophalf, LSL #24
+        ADD     byte, bigfont, byte, LSR #19
+        LDMIA   byte,   {R0,R1, R6,R7, R9,R10, R12,LR}
+        STMIA   screen, {R0,R1, R6,R7, R9,R10, R12,LR}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, tophalf, LSL #16
+        ADD     byte, bigfont, byte, LSR #19
+        LDMIA   byte,   {R0,R1, R6,R7, R9,R10, R12,LR}
+        STMIA   screen, {R0,R1, R6,R7, R9,R10, R12,LR}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, tophalf, LSL #8
+        ADD     byte, bigfont, byte, LSR #19
+        LDMIA   byte,   {R0,R1, R6,R7, R9,R10, R12,LR}
+        STMIA   screen, {R0,R1, R6,R7, R9,R10, R12,LR}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, tophalf
+        ADD     byte, bigfont, byte, LSR #19
+        LDMIA   byte,   {R0,R1, R6,R7, R9,R10, R12,LR}
+        STMIA   screen, {R0,R1, R6,R7, R9,R10, R12,LR}
+        ADD     screen, screen, linelen
+
+        LDR     bottomhalf, [SP, #4*3]                  ; restore 'bottomhalf' (R6 pushed onto stack)
+
+        AND     byte, mask, bottomhalf, LSL #24
+        ADD     byte, bigfont, byte, LSR #19
+        LDMIA   byte,   {R0,R1, R5,R7, R9,R10, R12,LR}
+        STMIA   screen, {R0,R1, R5,R7, R9,R10, R12,LR}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, bottomhalf, LSL #16
+        ADD     byte, bigfont, byte, LSR #19
+        LDMIA   byte,   {R0,R1, R5,R7, R9,R10, R12,LR}
+        STMIA   screen, {R0,R1, R5,R7, R9,R10, R12,LR}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, bottomhalf, LSL #8
+        ADD     byte, bigfont, byte, LSR #19
+        LDMIA   byte,   {R0,R1, R5,R7, R9,R10, R12,LR}
+        STMIA   screen, {R0,R1, R5,R7, R9,R10, R12,LR}
+        ADD     screen, screen, linelen
+
+        AND     byte, mask, bottomhalf
+        ADD     byte, bigfont, byte, LSR #19
+        LDMIA   byte,   {R0,R1, R5,R7, R9,R10, R12,LR}
+        STMIA   screen, {R0,R1, R5,R7, R9,R10, R12,LR}
+        ADD     screen, screen, linelen
+
+        Pull    "R0-R1,R5,R6,R7,R9-R10,R12,PC"
+
+
+; *****************************************************************************
+;
+;       BS - Backspace
+;       move cursor "left"
+;
+; in:   R6 = CursorFlags
+;
+
+BS
+        TST     R6, R6, ROR #10                 ; test &1E or C81Bit or Vdu5
+        BNE     SpecialBS
+
+        LDR     R0, [WsPtr, #CursorX]
+        LDR     R2, [WsPtr, #CursorAddr]
+        LDR     R3, [WsPtr, #BytesPerChar]
+        LDR     R4, [WsPtr, #TWLCol]
+
+        SUB     R0, R0, #1                      ; no Master wazerks yet !
+        SUB     R2, R2, R3
+        CMP     R0, R4
+        STRGE   R0, [WsPtr, #CursorX]           ; I do mean GE !
+        STRGE   R2, [WsPtr, #CursorAddr]
+        MOVGE   PC, R14
+
+        MOV     R0, #0
+        LDRB    R1, [R0, #OsbyteVars + :INDEX: PageModeLineCount]
+        TEQ     R1, #0
+        SUBNE   R1, R1, #1
+        STRB    R1, [R0, #OsbyteVars + :INDEX: PageModeLineCount]
+
+        LDR     R0, [WsPtr, #TWRCol]
+        LDR     R1, [WsPtr, #CursorY]
+        LDR     R4, [WsPtr, #TWTRow]
+
+        SUB     R1, R1, #1
+        CMP     R1, R4
+
+        BGE     CursorR0R1
+
+        STR     R0, [WsPtr, #CursorX]
+        BSR     ScrollDown
+        B       AddressCursor
+
+; *****************************************************************************
+;
+;       Horizontal TAB - ie move cursor "right"
+;
+; in:   R6 = CursorFlags
+;
+
+HT
+        TST     R6, R6, ROR #10 ; test for &1E or C81Bit or Vdu5
+        BNE     SpecialHT
+
+        LDR     R0, [WsPtr, #CursorX]
+        LDR     R2, [WsPtr, #CursorAddr]
+        LDR     R3, [WsPtr, #BytesPerChar]
+        LDR     R4, [WsPtr, #TWRCol]
+
+        ADD     R0, R0, #1
+        ADD     R2, R2, R3
+        CMP     R0, R4
+        STRLS   R0, [WsPtr, #CursorX]
+        STRLS   R2, [WsPtr, #CursorAddr]
+        MOVLS   PC, R14
+
+        BSR     PageTest
+
+        LDR     R0, [WsPtr, #TWLCol]
+        LDR     R1, [WsPtr, #CursorY]
+        LDR     R4, [WsPtr, #TWBRow]
+
+        ADD     R1, R1, #1
+        CMP     R1, R4
+        BLS     CursorR0R1                       ; not on bottom line
+
+        STR     R0, [WsPtr, #CursorX]
+        BSR     ScrollUp
+        B       AddressCursor                    ; re-address cursor position
+
+; *****************************************************************************
+;
+;       VduLF - Line feed
+;
+; in:   R6 = CursorFlags
+;
+
+VduLF
+        TST     R6, #Vdu5Bit
+        BNE     Vdu5LF
+
+        BSR     PageTest                ; check for CTRL/SHIFT, page mode
+
+        TST     R6, R6, ROR #10
+        BNE     SpecialLF
+
+        LDR     R1, [WsPtr, #CursorY]
+        LDR     R2, [WsPtr, #CursorAddr]
+        LDR     R3, [WsPtr, #RowLength]
+        LDR     R4, [WsPtr, #TWBRow]
+
+        ADD     R1, R1, #1                      ; no Master wazerks
+        ADD     R2, R2, R3
+        CMP     R1, R4
+        STRLS   R1, [WsPtr, #CursorY]
+        STRLS   R2, [WsPtr, #CursorAddr]
+        MOVLS   PC, R14
+
+        BSR     ScrollUp
+        B       AddressCursor
+
+; *****************************************************************************
+;
+;       VT - Cursor up
+;
+
+VT
+        LDR     R6, [WsPtr, #CursorFlags]
+        TST     R6, #Vdu5Bit
+        BNE     Vdu5VT
+
+        MOV     R0, #0
+        LDRB    R1, [R0, #OsbyteVars + :INDEX: PageModeLineCount]
+        TEQ     R1, #0
+        SUBNE   R1, R1, #1
+        STRB    R1, [R0, #OsbyteVars + :INDEX: PageModeLineCount]
+
+        TST     R6, R6, ROR #10
+        BNE     SpecialVT
+
+        LDR     R1, [WsPtr, #CursorY]
+        LDR     R2, [WsPtr, #CursorAddr]
+        LDR     R3, [WsPtr, #RowLength]
+        LDR     R4, [WsPtr, #TWTRow]
+
+        SUB     R1, R1, #1
+        SUB     R2, R2, R3
+        CMP     R1, R4
+        STRGE   R1, [WsPtr, #CursorY]
+        STRGE   R2, [WsPtr, #CursorAddr]
+        MOVGE   PC, R14
+
+        BSR     ScrollDown
+        B       AddressCursor
+
+; *****************************************************************************
+;
+;       VduCR - Carriage return
+;       move to "left" boundary
+;
+; in:   R6 = CursorFlags
+;
+; out:  R6 = updated CursorFlags
+
+VduCR
+CR10                            ; entry point for releasing pending CRLF
+        TST     R6, #Vdu5Bit
+        BNE     Vdu5CR
+
+        BIC     R6, R6, #C81Bit                 ; destroy pending CRLF
+        STR     R6, [WsPtr, #CursorFlags]
+        Push    R14
+        BL      CursorB0                        ; preserves R6
+        BL      AddressCursor                   ; preserves R6
+        Pull    PC
+
+; *****************************************************************************
+;
+;       FS - Define text window
+;
+
+FS      ROUT
+        LDRB    R0, [WsPtr, #QQ+0]              ; left
+        LDRB    R1, [WsPtr, #QQ+1]              ; bottom
+        LDRB    R2, [WsPtr, #QQ+2]              ; right
+        LDRB    R3, [WsPtr, #QQ+3]              ; top
+        LDR     R4, [WsPtr, #ScrRCol]           ; max right
+        LDR     R5, [WsPtr, #ScrBRow]           ; max bottom
+
+        LDR     R6, [WsPtr, #VduStatus]
+        ORR     R6, R6, #Windowing              ; we are windowing
+        STR     R6, [WsPtr, #VduStatus]
+
+; Secondary entry point, for validating unpacked context variables
+; NB doesn't want to set windowing bit
+
+FSRegs
+        CMP     R2, R0                          ; right >= left
+        CMPCS   R4, R2                          ; max right >= right
+        CMPCS   R1, R3                          ; bottom >= top
+        CMPCS   R5, R1                          ; max bottom >= bottom
+        MOVCC   PC, R14                         ; invalid window
+
+        ADD     R6, WsPtr, #TWLCol
+        STMIA   R6, {R0-R3}                     ; write new window settings
+
+; now check for input cursor being outside window
+
+        LDR     R6, [WsPtr, #CursorFlags]
+        TST     R6, #CursorsSplit
+        BEQ     %FT10                           ; [cursors not split]
+
+        ASSERT  InputCursorY = InputCursorX +4
+        ADD     R6, WsPtr, #InputCursorX        ; R6 := InputCursorX
+        LDMIA   R6, {R6, R7}                    ; R7 := InputCursorY
+
+        CMP     R6, R0                          ; X >= left
+        CMPCS   R2, R6                          ; right >= X
+        CMPCS   R7, R3                          ; Y >= top
+        CMPCS   R1, R7                          ; bottom >= Y
+
+        BCS     %FT10                           ; [not outside window]
+
+        LDR     R6, [WsPtr, #CursorX]           ; get output cursor posn
+        LDR     R7, [WsPtr, #CursorY]
+        LDR     R4, [WsPtr, #CursorAddr]
+        Push    "R0-R4, R6, R7, R14"            ; save window and output cursor
+
+        BL      HomeVdu4                        ; Home output cursor
+
+        LDR     R6, [WsPtr, #CursorX]           ; Copy output ...
+        LDR     R7, [WsPtr, #CursorY]
+        LDR     R4, [WsPtr, #CursorAddr]
+
+        STR     R6, [WsPtr, #InputCursorX]      ; ... to input
+        STR     R7, [WsPtr, #InputCursorY]
+        STR     R4, [WsPtr, #InputCursorAddr]
+
+        Pull    "R0-R4, R6, R7, R14"            ; restore old registers
+        STR     R6, [WsPtr, #CursorX]
+        STR     R7, [WsPtr, #CursorY]
+        STR     R4, [WsPtr, #CursorAddr]
+10
+
+; now check output cursor is inside window
+
+        LDR     R6, [WsPtr, #CursorX]           ; get output cursor posn
+        LDR     R7, [WsPtr, #CursorY]
+
+        CMP     R6, R0                          ; X >= left
+        CMPCS   R2, R6                          ; right >= X
+        CMPCS   R7, R3                          ; Y >= top
+        CMPCS   R1, R7                          ; bottom >= Y
+
+        MOVCS   PC, R14                         ; cursor inside window
+
+; and drop thru to ...
+
+HomeVdu4                                        ; home TEXT cursor, even in
+                                                ; VDU 5 mode
+        MOV     R0, #0                          ; move to "0,0"
+        MOV     R1, #0
+        LDR     R6, [WsPtr, #CursorFlags]
+
+        B       TabR0R1NotVdu5                  ; (destroys any pending CRLF)
+
+; *****************************************************************************
+;
+;       TCOL - Set text colour (foreground or background)
+;
+        ASSERT  TBackCol = TForeCol +4
+        ASSERT  back > fore
+
+TCOL
+DC1
+        TST     R6, #TeletextMode               ; if in teletext
+        MOVNE   PC, R14                         ; then ignore
+
+        LDR     bpp, [WsPtr, #BitsPerPix]
+        ADD     fore, WsPtr, #TForeCol
+        LDMIA   fore, {fore, back}
+
+        LDR     R3, [WsPtr, #NColour]
+        LDRB    R4, [WsPtr, #QQ+0]              ; get colour specified
+        CMP     R4, #128                        ; C=1 => set background
+        AND     R4, R4, R3
+        AND     R4, R4, #63                     ; oh no not again!
+        STRCC   R4, [WsPtr, #TForeCol]
+        STRCS   R4, [WsPtr, #TBackCol]
+        MOVCC   R5, fore
+        MOVCS   R5, back                        ; R5 is old appropriate colour
+        MOVCC   fore, R4
+        MOVCS   back, R4
+
+        BCS     %FT31                           ; branch for background
+
+        ; amg: only update the appropriate one
+        CMP     R4,R5
+        MOVEQ   PC, R14
+        Push    "LR"
+        BL      CompileTextFg
+        Pull    "LR"
+        B       %FT32
+31
+        CMP     R4, R5
+        MOVEQ   PC, R14                         ; same as last time
+        Push    "LR"
+        BL      CompileTextBg                   ; ensure that TextBg is kosher
+        Pull    "LR"                            ;   preserving the return address
+32
+        LDR     R6, [WsPtr, #CursorFlags]
+        ORR     R6, R6, #TEUpdate
+R6toCursorFlags
+        STR     R6, [WsPtr, #CursorFlags]
+
+        MOV     PC, R14
+
+CheckTEUpdate
+        LDR     R6, [WsPtr, #CursorFlags]
+        TST     R6, #TEUpdate
+        MOVEQ   PC, R14
+ReallySetColours
+        BIC     R6, R6, #TEUpdate
+        STR     R6, [WsPtr, #CursorFlags]       ; clear update flag
+
+        Push    "R6, R14"
+        LDR     bpp, [WsPtr, #BitsPerPix]
+        LDR     fore, [WsPtr, #TextFgColour]
+        LDR     back, [WsPtr, #TextBgColour]
+        BL      SetColours
+        Pull    "R6, PC"
+
+; *****************************************************************************
+;
+;       FF - Clear text window (CLS)
+;
+
+FF
+        LDR     R6, [WsPtr, #CursorFlags]
+        TST     R6, #Vdu5Bit
+        BNE     Vdu5FF
+
+        STROSB  R0, PageModeLineCount, R0       ; zero page mode line count
+
+        LDR     R0, [WsPtr, #VduStatus]
+        TST     R0, #Windowing
+        BNE     SlowCLS                         ; windowing, so do slowly
+
+        Push    R14
+        TST     R6, #ClipBoxEnableBit
+        BLNE    SetClipBoxToFullScreen
+        LDR     R0, [WsPtr, #DriverBankAddr]    ; set driver's screen start
+        STR     R0, [WsPtr, #DisplayScreenStart]
+        STR     R0, [WsPtr, #ScreenStart]
+
+        LDR     R0, [WsPtr, #DisplayBankAddr]
+        BL      SetVinit                        ; program Vinit
+                                                ; (and set DisplayStart)
+        BL      Home       ; home cursor after ScreenStart initialised
+        TST     R6, #CursorsSplit
+        BLNE    AddressInputCursor
+        Pull    R14
+
+        LDR     R0, [WsPtr, #ModeFlags]
+        TST     R0, #Flag_BBCGapMode            ; if not BBC gap mode
+        BEQ     FastCLS                         ; then use fast code
+
+SlowCLS
+        BSR     Home
+
+        ADD     R0, WsPtr, #TWLCol              ; R0 := TWLCol; R1 := TWBRow
+        LDMIA   R0, {R0-R3}                     ; R2 := TWRCol; R3 := TWTRow
+
+;       and drop thru to ...
+
+;       ClearBox - Clears a box of text chars on the screen
+;
+; in:   R0 = left column
+;       R1 = bottom row
+;       R2 = right column
+;       R3 = top row
+
+ClearBox ROUT
+        Push    R14
+        TST     R6, #ClipBoxEnableBit
+        BLNE    ClipTextArea
+
+        TST     R6, #TeletextMode
+        BNE     TTXClearBox
+
+        BL      GetBoxInfo
+        Pull    R14
+
+ClearThisBox
+        STR     R8, [WsPtr, #RowsToDo]
+
+        LDR     R9, [WsPtr, #CursorFlags]
+        TST     R9, #TEUpdate
+        BNE     %FT99
+
+05
+        LDR     R8, [WsPtr, #TextBgColour]
+        LDRB    R1, [WsPtr, #ModeFlags]
+        TST     R1, #Flag_BBCGapMode    ; is it a BBC gap mode ?
+        LDRNE   R1, =&AAAAAAAA          ; use colour 2 for gaps if so
+        MOVEQ   R1, R8                  ; else use background colour
+        EOR     R1, R1, R8              ; EOR toggle for colour
+        STR     R1, [WsPtr, #EORtoggle]
+        LDR     R0, [WsPtr, #RowMult]   ; 8 or 10
+
+ClearRow
+        MOV     R6, #0
+ClearLineNew
+        MOV     R9, R8
+        MOV     R10, R8
+        MOV     R11, R8
+ClearLine
+        MOV     R1, R2                  ; R1 is current address
+        ADD     R3, R2, R5              ; R3 is byte after last one
+10
+        CMP     R1, R3
+        BEQ     DoneLine
+        TST     R1, #3
+        STRNEB  R8, [R1], #1            ; store if not on word boundary
+        BNE     %BT10
+
+        SUB     R4, R3, R1              ; number of bytes left on this line
+        MOV     R4, R4, LSR #4          ; number of 4-words left on this line
+        SUBS    R4, R4, #1              ; C=0 if 0, C=1 if >0
+14
+        STMHIIA R1!, {R8-R11}           ; done if R4 > 0
+        STMCSIA R1!, {R8-R11}           ; done if R4 >= 0
+        SUBCSS  R4, R4, #2              ; this code dropped thru if R4 was 0
+        BCS     %BT14
+
+        BIC     R4, R3, #3
+20
+        CMP     R1, R4
+        STRNE   R8, [R1], #4
+        BNE     %BT20
+
+30
+        CMP     R1, R3
+        STRNEB  R8, [R1], #1
+        BNE     %BT30
+
+DoneLine
+        ADD     R2, R2, R7
+        ADD     R6, R6, #1
+        CMP     R6, #8
+        BCC     ClearLine
+        LDR     R1, [WsPtr, #EORtoggle]
+        EOREQ   R8, R8, R1
+        CMP     R6, R0
+        BCC     ClearLineNew
+
+        EOR     R8, R8, R1
+        LDR     R1, [WsPtr, #RowsToDo]
+        SUBS    R1, R1, #1
+        STR     R1, [WsPtr, #RowsToDo]
+        BNE     ClearRow
+
+        MOV     PC, R14
+
+99
+        Push    "R2, R5-R7, R14"
+        MOV     R6, R9
+        BL      ReallySetColours
+        Pull    "R2, R5-R7, R14"
+        B       %BT05
+
+        LTORG
+
+; *****************************************************************************
+;
+;       GetWindowInfo - sets up some info for current text window
+;
+;       GetBoxInfo - sets up some info for a given box
+;
+; in:   R0 = left
+;       R1 = bottom
+;       R2 = right
+;       R3 = top
+;
+; out:  R2 = address of top left
+;       R5 = number of bytes horizontally
+;       R6 = number of pixel rows vertically
+;       R7 = LineLength
+;       R8 = number of character rows vertically
+
+GetWindowInfo
+        ADD     R0, WsPtr, #TWLCol              ; R0 := TWLCol; R1 := TWBRow
+        LDMIA   R0, {R0-R3}                     ; R2 := TWRCol; R3 := TWTRow
+GetBoxInfo
+        SUB     R5, R2, R0
+        ADD     R5, R5, #1                      ; number of chars horiz
+        LDR     R6, [WsPtr, #Log2BPC]           ; should be log2 bytes/char
+        MOV     R5, R5, LSL R6                  ; number of bytes horiz
+
+        SUB     R6, R1, R3
+        ADD     R8, R6, #1                      ; number of char rows vert
+
+        LDR     R7, [WsPtr, #ModeFlags]
+
+        MOV     R6, R8, LSL #3                  ; *8
+        TST     R7, #Flag_GapMode               ; if gap mode
+        ADDNE   R6, R6, R8, LSL #1              ; (+*2) = *10
+        TST     R7, #Flag_DoubleVertical        ; if double mode
+        ADDNE   R6, R6, R6                      ; then double
+
+        LDR     R7, [WsPtr, #LineLength]
+
+        MOV     R1, R3                          ; prepare to address top left
+        B       AddressR0R1
+
+
+; *****************************************************************************
+;
+;       US - TAB(X,Y)
+;
+
+US
+        LDRB    R0, [WsPtr, #QQ+0]
+        LDRB    R1, [WsPtr, #QQ+1]
+TabR0R1
+
+        LDR     R6, [WsPtr, #CursorFlags]
+        TST     R6, #Vdu5Bit
+        BNE     Vdu5TAB
+
+TabR0R1NotVdu5
+        LDR     R9,  [WsPtr, #CursorX]
+        LDR     R10, [WsPtr, #CursorY]
+        Push    "R0,R9,R10"             ; save old X,Y in case it doesn't work
+
+        EOR     R6, R6, #8              ; update Y position
+        MOV     R0, R1
+        BSR     CursorBdy
+
+        EOR     R6, R6, #8              ; now try X position
+        Pull    R0
+        BSR     CursorBdyCheck
+        MOV     R7, #0                  ; will be clearing C81Bit
+        BCS     US10                    ; was in window, so OK
+
+        TEQ     R6, R6, LSR #1          ; are we in 81 column mode
+                                        ; (just want to set carry)
+        BCC     US20                    ; can't do it
+
+        SUB     R0, R0, #1              ; could be attempt to move to col 81
+        BSR     CursorBdyCheck
+        BCC     US20                    ; still can't do it
+        MOV     R7, #C81Bit             ; set C81Bit
+US10
+        BIC     R6, R6, #C81Bit
+        ORR     R6, R6, R7
+        STR     R6, [WsPtr, #CursorFlags]
+
+US20
+        Pull    "R9,R10"
+
+        STRCC   R9,  [WsPtr, #CursorX]  ; couldn't do it, so restore position
+        STRCC   R10, [WsPtr, #CursorY]
+
+        B       AddressCursor
+
+; *****************************************************************************
+;
+;       ScrollUp - Scroll the current text window up
+;
+
+ScrollUp
+        LDR     R0, [WsPtr, #VduStatus]
+        TST     R0, #Windowing
+        BNE     SoftScrollUp
+HardScrollUp
+        LDR     R1, [WsPtr, #ModeFlags]
+        TST     R1, #Flag_HardScrollDisabled
+        BNE     HardScrollSpriteUp
+
+        LDR     R1, [WsPtr, #RowLength]
+        LDR     R2, [WsPtr, #TotalScreenSize]
+
+        LDR     R0, [WsPtr, #DisplayScreenStart]
+        ADD     R0, R0, R1
+        CMP     R0, #ScreenEndAdr
+        SUBCS   R0, R0, R2
+        STR     R0, [WsPtr, #DisplayScreenStart]
+        STR     R0, [WsPtr, #ScreenStart]
+
+        LDR     R0, [WsPtr, #DisplayStart]
+        ADD     R0, R0, R1
+        CMP     R0, #ScreenEndAdr
+        SUBCS   R0, R0, R2
+
+        Push    R14
+        BL      SetVinit                ; program vinit and set DisplayStart
+
+        TST     R6, #TeletextMode
+        BLNE    TTXHardScrollUp
+        Pull    R14
+
+ClearBottomScreenLine
+        MOV     R0, #0                          ; Don't use window coords -
+        LDR     R1, [WsPtr, #ScrBRow]           ; code also used in VDU23,7
+        LDR     R2, [WsPtr, #ScrRCol]
+        MOV     R3, R1
+        B       ClearBox
+
+; Code to scroll whole 'screen' up, when hard scroll disabled
+; (ie outputting to sprite)
+
+HardScrollSpriteUp
+        Push    R14
+        BL      GetScreenInfo           ; get box info for whole 'screen'
+        LDR     R0, [WsPtr, #RowMult]
+        BL      SoftScrollUp2
+        Pull    R14
+        B       ClearBottomScreenLine
+
+; Clear bottom line of window
+
+ClearBottomLine
+        ADD     R0, WsPtr, #TWLCol              ; R0 := TWLCol; R1 := TWBRow
+        LDMIA   R0, {R0-R2}                     ; R2 := TWRCol
+        MOV     R3, R1                          ; R3 := TWBRow
+        B       ClearBox
+
+SoftScrollUp
+        Push    R14
+        TST     R6, #TeletextMode
+        BNE     TTXSoftScrollUp
+
+        BL      GetWindowInfo
+
+;       R2 = address of top left
+;       R5 = number of bytes horizontally
+;       R6 = number of pixel rows in box
+;       R7 = linelength
+;       R8 = number of character rows in box
+
+        LDR     R0, [WsPtr, #RowMult]
+        BL      SoftScrollUp2
+        Pull    R14
+        B       ClearBottomLine
+
+; *****************************************************************************
+;
+;       SoftScrollUp2 - Called by SoftScrollUp and by Teletext to scroll map
+;
+; in:   R0 = RowMult
+;       R2 = screen address of top left of area to scroll
+;       R5 = number of bytes horizontally
+;       R6 = number of pixel rows vertically
+;       R7 = linelength
+;
+
+SoftScrollUp2 ROUT
+        SUBS    R6, R6, R0              ; scroll number of rows-1
+        MOVEQ   PC, R14                 ; single row window, nowt to scroll
+
+ScrollLineUp
+        MOV     R1, R2
+        LDR     R3, [WsPtr, #RowLength]
+        ADD     R0, R1, R3              ; R0 -> line below
+        ADD     R3, R2, R5              ; R3 -> byte after last one on upper
+10
+        CMP     R1, R3
+        BEQ     %FT40                   ; finished
+        TST     R1, #3                  ; if not word aligned
+        LDRNEB  R8, [R0], #1            ; then copy a byte
+        STRNEB  R8, [R1], #1
+        BNE     %BT10
+
+        SUB     R4, R3, R1              ; number of bytes left on this line
+        MOVS    R4, R4, LSR #4          ; number of 4-words left on this line
+        SUBS    R4, R4, #1              ; C=0 if 0, C=1 if >0
+14
+        LDMHIIA R0!, {R8-R11}           ; this code dropped thru if was 0
+        STMHIIA R1!, {R8-R11}
+        LDMCSIA R0!, {R8-R11}
+        STMCSIA R1!, {R8-R11}
+        SUBCSS  R4, R4, #2
+        BCS     %BT14
+
+        BIC     R4, R3, #3
+20
+        CMP     R1, R4
+        LDRNE   R8, [R0], #4
+        STRNE   R8, [R1], #4
+        BNE     %BT20
+
+30
+        CMP     R1, R3
+        LDRNEB  R8, [R0], #1
+        STRNEB  R8, [R1], #1
+        BNE     %BT30
+
+40
+        ADD     R2, R2, R7
+        SUBS    R6, R6, #1
+        BNE     ScrollLineUp
+
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       ScrollDown - Scroll the current text window down
+;
+
+ScrollDown
+        LDR     R0, [WsPtr, #VduStatus]
+        TST     R0, #Windowing
+        BNE     SoftScrollDown
+HardScrollDown
+        LDR     R1, [WsPtr, #ModeFlags]
+        TST     R1, #Flag_HardScrollDisabled
+        BNE     HardScrollSpriteDown
+
+        LDR     R1, [WsPtr, #RowLength]
+        LDR     R2, [WsPtr, #TotalScreenSize]
+
+        LDR     R0, [WsPtr, #DisplayScreenStart]
+        SUB     R0, R0, R1                      ; down one row
+        ADD     R3, R0, R2
+        CMP     R3, #ScreenEndAdr               ; if < then need wrap
+        MOVCC   R0, R3
+        STR     R0, [WsPtr, #DisplayScreenStart]
+        STR     R0, [WsPtr, #ScreenStart]
+
+        LDR     R0, [WsPtr, #DisplayStart]
+        SUB     R0, R0, R1
+        ADD     R3, R0, R2
+        CMP     R3, #ScreenEndAdr
+        MOVCC   R0, R3
+
+        Push    R14
+        BL      SetVinit                ; program vinit and set DisplayStart
+
+        TST     R6, #TeletextMode
+        BLNE    TTXHardScrollDown
+        Pull    R14
+
+ClearTopScreenLine
+        MOV     R0, #0                          ; don't use window coords -
+        MOV     R1, #0                          ; code also used by VDU23,7
+        LDR     R2, [WsPtr, #ScrRCol]
+        MOV     R3, #0
+        B       ClearBox
+
+; Code to scroll whole 'screen' down, when hard scroll disabled
+; (ie outputting to sprite)
+
+HardScrollSpriteDown
+        Push    R14
+        BL      GetScreenInfo           ; get box info for whole 'screen'
+
+        MOV     R0, #0
+        LDR     R1, [WsPtr, #ScrBRow]
+        BL      AddressR0R1             ; R2 -> top line of bottom left char
+
+        LDR     R0, [WsPtr, #RowMult]
+        BL      SoftScrollDown2
+        Pull    R14
+        B       ClearTopScreenLine
+
+; Clear top line of window
+
+ClearTopLine
+        ADD     R0, WsPtr, #TWLCol              ; R0 := TWLCol; (R1 := TWBRow)
+        LDMIA   R0, {R0-R3}                     ; R2 := TWRCol; R3 := TWTRow
+        MOV     R1, R3                          ; R1 := TWTRow
+        B       ClearBox
+
+SoftScrollDown
+        Push    R14
+        TST     R6, #TeletextMode
+        BNE     TTXSoftScrollDown
+
+        BL      GetWindowInfo
+
+;       R2 = address of top left
+;       R5 = number of bytes horizontally
+;       R6 = number of pixel rows in box
+;       R7 = linelength
+;       R8 = number of character rows in box
+
+        ADD     R0, WsPtr, #TWLCol              ; R0 := TWLCol; R1 := TWBRow
+        LDMIA   R0, {R0-R1}
+        BL      AddressR0R1             ; R2 -> top line of bottom left char
+
+        LDR     R0, [WsPtr, #RowMult]
+        BL      SoftScrollDown2
+        Pull    R14
+        B       ClearTopLine
+
+; *****************************************************************************
+;
+;       SoftScrollDown2 - Called by SoftScrollDown and by TTX to scroll map
+;
+; in:   R0 = RowMult
+;       R2 = screen address of top line of bottom left char
+;       R5 = number of bytes horizontally
+;       R6 = number of pixel rows vertically
+;       R7 = linelength
+;
+
+SoftScrollDown2 ROUT
+        SUBS    R6, R6, R0              ; scroll number of rows-1
+        MOVEQ   PC, R14                 ; single row window, nowt to scroll
+
+        SUB     R2, R2, R7              ; R2 -> bottom line of next-to-bottom
+        LDR     R1, [WsPtr, #RowLength]
+        ADD     R2, R2, R1              ; R2 -> bottom line of bottom
+
+ScrollLineDown
+        MOV     R1, R2
+        LDR     R3, [WsPtr, #RowLength]
+        SUB     R0, R1, R3              ; R0 -> line above,needs fudging
+        ADD     R3, R2, R5              ; R3 -> byte after last one on upper
+10
+        CMP     R1, R3
+        BEQ     %FT40                   ; finished
+        TST     R1, #3
+        LDRNEB  R8, [R0], #1
+        STRNEB  R8, [R1], #1
+        BNE     %BT10
+
+        SUB     R4, R3, R1              ; number of bytes left on this line
+        MOVS    R4, R4, LSR #4          ; number of 4-words left on this line
+        SUBS    R4, R4, #1              ; C=0 if 0, C=1 if >0
+14
+        LDMHIIA R0!, {R8-R11}           ; this code dropped thru if was 0
+        STMHIIA R1!, {R8-R11}
+        LDMCSIA R0!, {R8-R11}
+        STMCSIA R1!, {R8-R11}
+        SUBCSS  R4, R4, #2
+        BCS     %BT14
+
+        BIC     R4, R3, #3
+20
+        CMP     R1, R4
+        LDRNE   R8, [R0], #4
+        STRNE   R8, [R1], #4
+        BNE     %BT20
+
+30
+        CMP     R1, R3
+        LDRNEB  R8, [R0], #1
+        STRNEB  R8, [R1], #1
+        BNE     %BT30
+
+40
+        SUB     R2, R2, R7
+        SUBS    R6, R6, #1
+        BNE     ScrollLineDown
+
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       SetVinit  - Program Vinit  with address in R0
+;       SetVstart - Program Vstart --------""--------
+;       SetVend   - Program Vend   --------""--------
+;
+; out:  R0-R2 corrupted
+;
+
+SetVstart
+        MOV     R1, #MEMCDAG_VStart
+SetLag
+        SUB     R0, R0, #ScreenEndAdr
+        LDR     R2, [WsPtr, #TotalScreenSize]
+        ADD     R0, R0, R2                      ; make startofscreen 0
+        B       SetDAGOffset                    ; call generic interface
+
+SetVendDefault
+ [ MEMC_Type = "IOMD"
+        MOV     R0, #0
+        LDR     R0, [R0, #VRAMWidth]
+        CMP     R0, #1
+        MOVCC   R0, #16                         ; DRAM-only, subtract 16
+        MOVEQ   R0, #SAMLength/2                ; 1 bank of VRAM - 1/2 SAM
+        MOVHI   R0, #SAMLength                  ; 2 banks of VRAM - 1/2 SAM * 2
+        RSB     R0, R0, #ScreenEndAdr
+ |
+        MOV     R0, #ScreenEndAdr
+        SUB     R0, R0, #16
+ ]
+        MOV     R1, #MEMCDAG_VEnd
+        B       SetLag
+
+SetVinit
+        STR     R0, [WsPtr, #DisplayStart]
+        SUB     R0, R0, #ScreenEndAdr
+        LDR     R2, [WsPtr, #TotalScreenSize]
+        ADD     R0, R0, R2                      ; make start of screen 0
+        LDR     R1, [WsPtr, #TeletextOffset]
+        ADD     R0, R0, R1                      ; add on teletext bank offset
+        CMP     R0, R2                          ; if out of range
+        SUBCS   R0, R0, R2                      ; then subtract total size
+SetVinitPhys
+        STR     R0, [WsPtr, #VinitCopy]
+        MOV     R1, #MEMCDAG_VInit
+SetDAGOffset
+ [ MEMC_Type = "IOMD"
+        MOV     R2, #0
+        LDR     R2, [R2, #VideoPhysAddr]        ; add on physical address of start of video RAM
+        ADD     R0, R0, R2
+ ]
+        B       SetDAG                          ; call generic interface
+
+; *****************************************************************************
+;
+;       ConvertBankToAddress - Convert bank number into default start address
+;
+; in:   R2 = screen bank number (0..n)
+;
+; out:  R3 = default start address for that bank
+;       R0-R2 preserved
+;       R4,R5 corrupted
+;
+
+ConvertBankToAddress ROUT
+        MOV     R4, R2
+        LDR     R3, [WsPtr, #TotalScreenSize]
+        RSB     R3, R3, #ScreenEndAdr           ; R3 := start of all screen mem
+        LDR     R5, [WsPtr, #ScreenSize]
+10
+        MOVS    R4, R4, LSR #1                  ; add on R4*ScreenSize
+        ADDCS   R3, R3, R5
+        ADD     R5, R5, R5
+        BNE     %BT10
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       Delete - delete a character
+;
+; in:   R6 = CursorFlags
+;
+
+Delete  ROUT
+        Push    R14
+
+        TST     R6, #TEUpdate                   ; if colours dirty
+        BLNE    ReallySetColours                ; then update them
+
+        TST     R6, #32                         ; Bit 5 set => no cursor move
+        BLEQ    BS
+        LDR     R6, [WsPtr, #CursorFlags]       ; reload in case BS corrupts it
+
+        TST     R6, #Vdu5Bit
+        TSTEQ   R6, #(TeletextMode :OR: ClipBoxEnableBit)
+        BNE     %FT20
+10
+        Pull    R14
+        MOV     tophalf, #0                     ; Print with space
+        MOV     bottomhalf, #0
+        LDR     screen, [WsPtr, #CursorAddr]
+      [ VIDC_Type = "VIDC20"
+        LDR     bigfont, [WsPtr, #TextExpandArea]
+      |
+        ADD     bigfont, WsPtr, #TextExpand
+      ]
+        LDR     linelen, [WsPtr, #LineLength]
+        LDR     PC, [WsPtr, #WrchNbit]
+
+20
+        TST     R6, #Vdu5Bit
+        Pull    R14, NE
+        BNE     Vdu5Delete
+
+        TST     R6, #TeletextMode
+        MOVNE   R0, #32                         ; wipe out with space
+        Pull    R14, NE
+        BNE     TTXDoChar
+
+        BL      ClipCursorCell                  ; must be ClipBoxEnable
+        B       %BT10                           ; so clip cursor and continue
+
+      [ {FALSE}
+
+; *****************************************************************************
+;
+;       Convert colours if in 256 colour mode
+;
+; in:   bpp = BitsPerPix
+;       fore = foreground colour (in 'user' format)
+;       back = background colour (-------""-------)
+;
+; out:  fore, back = adjusted colours (if necessary)
+
+ConvertCol
+        CMP     bpp, #8
+        MOVNE   PC, R14
+
+        Push    R14
+        MOV     col, fore
+        LDR     index, [WsPtr, #TFTint]
+        BL      FudgeColour
+        MOV     fore, col
+
+        MOV     col, back
+        LDR     index, [WsPtr, #TBTint]
+        BL      FudgeColour
+        MOV     back, col
+
+        Pull    PC
+
+FudgeColour                             ; col   =   0  0 B3 B2 G3 G2 R3 R2
+                                        ; index =  t1 t0  0  0  0  0  0  0
+
+        MOV     R3, col, LSL #2         ; R3 :=  B3 B2 G3 G2 R3 R2  0  0
+        AND     R3, R3, #&84            ; R3 :=  B3  0  0  0  0 R2  0  0
+        MOVS    col, col, LSL #28       ; C :=   B2
+        MOV     col, col, LSR #29       ; col :=  0  0  0  0  0 G3 G2 R3
+        ORR     col, R3, col, LSL #4    ; col := B3 G3 G2 R3  0 R2  0  0
+        ORRCS   col, col, #&08          ; col := B3 G3 G2 R3 B2 R2  0  0
+        ORR     col, col, index, LSR #6 ; col := B3 G3 G2 R3 B2 R2 T1 T0
+        MOV     PC, R14
+
+      ]
+
+; *****************************************************************************
+
+PlainBit ROUT
+
+; first set up RAMMaskTb
+
+        ASSERT  bpp=0
+
+        LDR     bpp, [WsPtr, #BytesPerChar]
+        MOV     R1, #1
+
+; *****Change made by DJS
+; Original code was:
+;        MOV     R1, R1, LSL bpp         ; first form mask for leftmost pixel
+;        SUB     R1, R1, #1              ; = (2^BytesPerChar)-1
+
+        RSB     R1, R1, R1, LSL bpp      ; first form mask for leftmost pixel
+                                         ; = (2^BytesPerChar)-1
+
+; *****End of change made by DJS
+
+        ADD     R3, WsPtr, #RAMMaskTb
+10
+        STR     R1, [R3], #4            ; store mask
+        MOVS    R1, R1, LSL bpp         ; shift to next pixel
+        BNE     %BT10                   ; loop until all shifted out
+
+; DDV: Original code used to read:
+;
+;       TEQ     bpp, #16
+;       MOVEQ   PC, R14     ; nothing to do on a mode change in this mode
+;
+; In 32 bit per pixel modes this used to cause an overflow, new function
+; drops out if the bpp >= 16.
+;
+        CMP     bpp, #16
+        MOVGT   PC, R14     ; nothing to do on a mode change in this mode
+        Push    R14
+
+      [ VIDC_Type = "VIDC20"
+        LDR     tabaddr, [WsPtr, #TextExpandArea]
+      |
+        ADD     tabaddr, WsPtr, #TextExpand
+      ]
+        ADD     tabaddr, tabaddr, bpp, LSL #8   ; TextPlain now dynamic
+
+        MOV     dest, #&80000000
+
+        CMP     bpp, #4
+        BHI     Plain8Bit
+        BEQ     Plain4Bit
+        CMP     bpp, #1
+        BHI     Plain2Bit
+
+Plain1Bit
+        ADR     hiaddr, P1BTab
+        ADD     R10, hiaddr, #8                ; end address
+P1B20
+        LDR     hiword, [hiaddr], #4
+P1B25
+        ADR     loaddr, P1BTab
+P1B30
+        LDR     loword, [loaddr], #4
+P1B40
+        MOV     cbyte, hiword, LSL #28
+        ORR     dest, cbyte, dest, LSR #4
+        MOV     cbyte, loword, LSL #28
+        ORRS    dest, cbyte, dest, LSR #4
+        BLCS    OutputNoColour
+
+        MOVS    loword, loword, LSR #4
+        BNE     P1B40
+        TEQ     loaddr, R10
+        BNE     P1B30
+
+        MOVS    hiword, hiword, LSR #4
+        BNE     P1B25
+        TEQ     hiaddr, R10
+        BNE     P1B20
+
+        Pull    PC
+
+OutputColour
+        AND     cbyte, dest, fore
+        BIC     dest, back, dest
+        ORR     dest, dest, cbyte
+OutputNoColour
+        STR     dest, [tabaddr], #4
+        MOV     dest, #&80000000
+        MOV     PC, R14
+
+P1BTab
+        &       &E6A2C480
+        &       &F7B3D591
+
+; *****************************************************************************
+
+Plain2Bit
+        ADR     hiaddr, P2BTab
+        ADD     R10, hiaddr, #16
+P2B20
+        LDR     hiword, [hiaddr], #4
+P2B25
+        ADR     loaddr, P2BTab
+P2B30
+        LDR     loword, [loaddr], #4
+P2B40
+        MOV     cbyte, hiword, LSL #24
+        ORR     dest, cbyte, dest, LSR #8
+        MOV     cbyte, loword, LSL #24
+        ORRS    dest, cbyte, dest, LSR #8
+        BLCS    OutputNoColour
+
+        MOVS    loword, loword, LSR #8
+        BNE     P2B40
+        TEQ     loaddr, R10
+        BNE     P2B30
+
+        MOVS    hiword, hiword, LSR #8
+        BNE     P2B25
+        TEQ     hiaddr, R10
+        BNE     P2B20
+
+        Pull    PC
+
+P2BTab
+        &       &F030C000
+        &       &FC3CCC0C
+        &       &F333C303
+        &       &FF3FCF0F
+
+; *****************************************************************************
+
+Plain4Bit
+        ADR     hiaddr, P4BTab
+        ADD     R10, hiaddr, #32
+P4B20
+        LDR     hiword, [hiaddr], #4
+P4B25
+        ADR     loaddr, P4BTab
+P4B30
+        LDR     loword, [loaddr], #4
+P4B40
+        MOV     cbyte, hiword, LSL #16
+        ORR     dest, cbyte, dest, LSR #16
+        MOV     cbyte, loword, LSL #16
+        ORRS    dest, cbyte, dest, LSR #16
+        BLCS    OutputNoColour
+
+        MOVS    loword, loword, LSR #16
+        BNE     P4B40
+        TEQ     loaddr, R10
+        BNE     P4B30
+
+        MOVS    hiword, hiword, LSR #16
+        BNE     P4B25
+        TEQ     hiaddr, R10
+        BNE     P4B20
+
+        Pull    PC
+
+P4BTab
+        &       &F0000000
+        &       &FF000F00
+        &       &F0F000F0
+        &       &FFF00FF0
+        &       &F00F000F
+        &       &FF0F0F0F
+        &       &F0FF00FF
+        &       &FFFF0FFF
+
+; *****************************************************************************
+
+Plain8Bit
+        ADR     hiaddr, P8BTab
+        ADD     R10, hiaddr, #64
+P8B20
+        LDR     hiword, [hiaddr], #4
+        ADR     loaddr, P8BTab
+P8B30
+        MOV     dest, hiword
+        BL      OutputNoColour
+        LDR     dest, [loaddr], #4
+        BL      OutputNoColour
+
+        TEQ     loaddr, R10
+        BNE     P8B30
+
+        TEQ     hiaddr, R10
+        BNE     P8B20
+
+        Pull    PC
+
+P8BTab
+        &       &00000000
+        &       &FF000000
+        &       &00FF0000
+        &       &FFFF0000
+        &       &0000FF00
+        &       &FF00FF00
+        &       &00FFFF00
+        &       &FFFFFF00
+        &       &000000FF
+        &       &FF0000FF
+        &       &00FF00FF
+        &       &FFFF00FF
+        &       &0000FFFF
+        &       &FF00FFFF
+        &       &00FFFFFF
+        &       &FFFFFFFF
+
+; *****************************************************************************
+;
+;       ReadCharacter - Read character at (input) text cursor position
+;
+; out:  R0 = character, 0 if unrecognised
+;
+
+ReadCharacter
+        Push    R14
+
+        BL      PreWrchCursor           ; remove both cursors
+        BL      CheckTEUpdate           ; update TextExpand if necessary
+                                        ; R6 = CursorFlags on exit
+        TST     R6, #TeletextMode
+        BNE     TTXReadCharacter
+
+        TST     R6, #CursorsSplit
+        LDREQ   R2, [WsPtr, #CursorAddr]        ; point to correct address
+        LDRNE   R2, [WsPtr, #InputCursorAddr]
+
+        LDR     R4, [WsPtr, #TextBgColour]
+        LDR     R8, [WsPtr, #LineLength]
+
+        LDR     R1, [WsPtr, #ModeFlags]
+        TST     R1, #Flag_DoubleVertical
+        BNE     RdCh1BitDouble
+
+        LDR     R1, [WsPtr, #Log2BPC]
+        CMP     R1, #1
+        BCC     RdCh1Bit
+        BEQ     RdCh2Bit
+        CMP     R1, #3
+        BCC     RdCh4Bit
+        BEQ     RdCh8Bit
+      [ VIDC_Type = "VIDC20"
+        CMP     R1, #4
+        BEQ     RdCh16Bit
+
+; Read character from cursor position for 32 bit per pixel
+
+; in    R2 -> cursor address
+;       R4 = background colour
+;       R8 = line length to be used
+;
+; out   R6 = first four bytes of char defn
+;       R7 = last four bytes of char defn
+
+; used  R0,R1,R3,R5,R9,R10,R11,LR = loading screen data!
+
+        MACRO
+        ConvertTo1BPP $source, $dest, $bit
+        EORS    $source, $source, R4
+        ORRNE   $dest, $dest, #$bit
+        MEND
+
+RdChr32Bit
+        Push    "R0-R1,R3,R5,R9,R10-R12"
+
+        BL      RdCh32Bit_GetData
+        MOV     R6,R7
+        BL      RdCh32Bit_GetData
+
+        Pull    "R0-R1,R3,R5,R9,R10-R12"
+        B       RDCH14
+
+; ***********************************************************************
+
+RdCh32Bit_GetData
+        MOV     R7, #1:SHL:31
+
+RdCh32Bit_Loop
+        MOVS    R7, R7, LSR #8
+
+        LDMIA   R2,{R0,R1,R3,R5,R9,R10,R11,R12}
+        ConvertTo1BPP R0,  R7, 1<<31
+        ConvertTo1BPP R1,  R7, 1<<30
+        ConvertTo1BPP R3,  R7, 1<<29
+        ConvertTo1BPP R5,  R7, 1<<28
+        ConvertTo1BPP R9,  R7, 1<<27
+        ConvertTo1BPP R10, R7, 1<<26
+        ConvertTo1BPP R11, R7, 1<<25
+        ConvertTo1BPP R12, R7, 1<<24
+
+        ADD     R2, R2, R8
+        BCC     RdCh32Bit_Loop
+
+        MOVS    PC,LR
+
+      ]
+
+RdCh16Bit
+        ADD     R2, R2, #12
+        ADD     R5, R2, R8, LSL #2              ; half way
+        ADD     R3, R2, R8, LSL #3              ; one-after-finishing R2
+        ADD     R8, R8, #12
+        MOV     R7, #0
+RDCH90
+        LDR     R1, [R2], #-4
+        EOR     R1, R1, R4
+        CMP     R1, #&00010000
+        MOV     R7, R7, RRX
+        MOV     R1, R1, LSL #16
+        CMP     R1, #&00010000
+        MOV     R7, R7, RRX
+
+        LDR     R1, [R2], #-4
+        EOR     R1, R1, R4
+        CMP     R1, #&00010000
+        MOV     R7, R7, RRX
+        MOV     R1, R1, LSL #16
+        CMP     R1, #&00010000
+        MOV     R7, R7, RRX
+
+        LDR     R1, [R2], #-4
+        EOR     R1, R1, R4
+        CMP     R1, #&00010000
+        MOV     R7, R7, RRX
+        MOV     R1, R1, LSL #16
+        CMP     R1, #&00010000
+        MOV     R7, R7, RRX
+
+        LDR     R1, [R2], R8
+        EOR     R1, R1, R4
+        CMP     R1, #&00010000
+        MOV     R7, R7, RRX
+        MOV     R1, R1, LSL #16
+        CMP     R1, #&00010000
+        MOV     R7, R7, RRX
+
+        TEQ     R2, R5                          ; half-way, so copy out word
+        MOVEQ   R6, R7                          ; top word
+        MOVEQ   R7, #0
+
+        TEQ     R2, R3                          ; finished ?
+        BNE     RDCH90
+        BEQ     RDCH14
+
+
+RdCh8Bit
+        ADD     R2, R2, #4
+        ADD     R5, R2, R8, LSL #2              ; half way
+        ADD     R3, R2, R8, LSL #3              ; one-after-finishing R2
+        MOV     R7, #0
+        ADD     R8, R8, #4                      ; alternate between -4 and LL+4
+        MVN     R9, R8
+        EOR     R9, R9, #3                      ; thing to EOR R8 with
+RDCH84
+        EOR     R8, R8, R9
+        LDR     R1, [R2], R8
+        EOR     R1, R1, R4
+        CMP     R1, #&01000000
+        MOV     R7, R7, RRX
+        MOV     R1, R1, LSL #8
+        ORR     R1, R1, #1                      ; dummy bit
+        CMP     R1, #&01000000
+RDCH88
+        MOV     R7, R7, RRX
+        MOV     R1, R1, LSL #8
+        CMP     R1, #&01000000                  ; Z => finish, C = output bit
+        BNE     RDCH88
+
+        TEQ     R2, R5                          ; half-way, so copy out word
+        MOVEQ   R6, R7                          ; top word
+        MOVEQ   R7, #0
+
+        TEQ     R2, R3                          ; finished ?
+        BNE     RDCH84
+        BEQ     RDCH14
+
+
+RdCh4Bit
+        ADD     R5, R2, R8, LSL #2              ; half way
+        ADD     R3, R2, R8, LSL #3              ; one-after-finishing R2
+        MOV     R7, #0
+RDCH44
+        LDR     R1, [R2], R8
+        EOR     R1, R1, R4
+        CMP     R1, #&10000000
+        MOV     R7, R7, RRX
+        MOV     R1, R1, LSL #4
+        ORR     R1, R1, #1                      ; dummy bit
+        CMP     R1, #&10000000
+RDCH48
+        MOV     R7, R7, RRX
+        MOV     R1, R1, LSL #4
+        CMP     R1, #&10000000                  ; Z => finish, C = output bit
+        BNE     RDCH48
+
+        TEQ     R2, R5                          ; half-way, so copy out word
+        MOVEQ   R6, R7                          ; top word
+        MOVEQ   R7, #0
+
+        TEQ     R2, R3                          ; finished ?
+        BNE     RDCH44
+        BEQ     RDCH14
+
+
+RdCh2Bit
+        ANDS    R0, R2, #3
+        EOR     R2, R2, R0                      ; make R2 -> word boundary
+        MOVNE   R0, #16                         ; shift adjust
+        ADD     R5, R2, R8, LSL #2              ; half way
+        ADD     R3, R2, R8, LSL #3              ; one-after-finishing R2
+        MOV     R7, #0
+RDCH24
+        LDR     R1, [R2], R8
+        EOR     R1, R1, R4
+        MOV     R1, R1, ROR R0
+        MOV     R1, R1, LSL #16                 ; important bits at top
+        ORR     R1, R1, #&4000                  ; dummy bit
+        CMP     R1, #&40000000
+RDCH28
+        MOV     R7, R7, RRX
+        MOV     R1, R1, LSL #2
+        CMP     R1, #&40000000                  ; Z => finish, C = output bit
+        BNE     RDCH28
+
+        TEQ     R2, R5                          ; half-way, so copy out word
+        MOVEQ   R6, R7                          ; top word
+        MOVEQ   R7, #0
+
+        TEQ     R2, R3                          ; finished ?
+        BNE     RDCH24
+        BEQ     RDCH14
+
+RdCh1Bit
+        LDRB    R6, [R2], R8
+        MOV     R6, R6, LSL #24
+        LDRB    R0, [R2], R8
+        ORR     R6, R6, R0, LSL #16
+        LDRB    R0, [R2], R8
+        ORR     R6, R6, R0, LSL #8
+        LDRB    R0, [R2], R8
+        ORR     R0, R6, R0
+        EOR     R0, R0, R4                      ; make background zero
+
+        MOV     R6, #1                          ; now invert order of bits
+RDCH10
+        MOVS    R0, R0, LSR #1
+        ADCS    R6, R6, R6
+        BCC     RDCH10
+
+        LDRB    R7, [R2], R8
+        MOV     R7, R7, LSL #24
+        LDRB    R0, [R2], R8
+        ORR     R7, R7, R0, LSL #16
+        LDRB    R0, [R2], R8
+        ORR     R7, R7, R0, LSL #8
+        LDRB    R0, [R2], R8
+        ORR     R0, R7, R0
+        EOR     R0, R0, R4
+
+        MOV     R7, #1                          ; now invert order of bits
+RDCH12
+        MOVS    R0, R0, LSR #1
+        ADCS    R7, R7, R7
+        BCC     RDCH12
+
+RDCH14
+        MOV     R0, #32
+        ADD     R1, WsPtr, # Font
+RDCH16
+        LDMIA   R1!, {R2,R3}
+        TEQ     R2, R6
+        TEQEQ   R3, R7
+        BEQ     RDCH17                          ; successful match
+        ADD     R0, R0, #1
+        TEQ     R0, #127
+        ADDEQ   R0, R0, #1
+        ADDEQ   R1, R1, #8
+        ANDS    R0, R0, #&FF                    ; 0 if finished
+        BNE     RDCH16
+
+RDCH17
+        Push    R0                              ; save char
+        BL      PostWrchCursor
+        Pull    "R0, PC"
+
+; *****************************************************************************
+
+RdCh1BitDouble                                  ; double height mode
+        LDRB    R0, [R2], R8
+        LDRB    R3, [R2], R8
+        TEQ     R0, R3
+        MOVEQ   R6, R0, LSL #24
+        LDREQB  R0, [R2], R8
+        LDREQB  R3, [R2], R8
+        TEQEQ   R0, R3
+        ORREQ   R6, R6, R0, LSL #16
+        LDREQB  R0, [R2], R8
+        LDREQB  R3, [R2], R8
+        TEQEQ   R0, R3
+        ORREQ   R6, R6, R0, LSL #8
+        LDREQB  R0, [R2], R8
+        LDREQB  R3, [R2], R8
+        TEQEQ   R0, R3
+        ORREQ   R0, R6, R0
+
+        MOVNE   R0, #0                          ; indicate bad character
+        BNE     RDCH17                          ; and branch
+
+        EOR     R0, R0, R4                      ; make background zero
+
+        MOV     R6, #1                          ; now invert order of bits
+RDCH10D
+        MOVS    R0, R0, LSR #1
+        ADCS    R6, R6, R6
+        BCC     RDCH10D
+
+        LDRB    R0, [R2], R8
+        LDRB    R3, [R2], R8
+        TEQ     R0, R3
+        MOVEQ   R7, R0, LSL #24
+        LDREQB  R0, [R2], R8
+        LDREQB  R3, [R2], R8
+        TEQEQ   R0, R3
+        ORREQ   R7, R7, R0, LSL #16
+        LDREQB  R0, [R2], R8
+        LDREQB  R3, [R2], R8
+        TEQEQ   R0, R3
+        ORREQ   R7, R7, R0, LSL #8
+        LDREQB  R0, [R2], R8
+        LDREQB  R3, [R2], R8
+        TEQEQ   R0, R3
+        ORREQ   R0, R7, R0
+
+        MOVNE   R0, #0                          ; indicate bad character
+        BNE     RDCH17                          ; and branch
+
+        EOR     R0, R0, R4
+
+        MOV     R7, #1                          ; now invert order of bits
+RDCH12D
+        MOVS    R0, R0, LSR #1
+        ADCS    R7, R7, R7
+        BCC     RDCH12D
+        B       RDCH14
+
+; *****************************************************************************
+
+TTXReadCharacter
+        TST     R6, #CursorsSplit
+        ADRL    R1, TTXLineStarts
+        LDREQ   R2, [WsPtr, #CursorY]
+        LDRNE   R2, [WsPtr, #InputCursorY]
+        LDR     R1, [R1, R2, LSL #2]
+        LDREQ   R2, [WsPtr, #CursorX]
+        LDRNE   R2, [WsPtr, #InputCursorX]
+        ADD     R2, R2, #1                      ; skip dummy
+        LDRB    R1, [R1, R2, LSL #2]
+        MOV     R0, R1
+        TEQ     R1, #"#"                        ; not those again !
+        MOVEQ   R0, #"`"
+        TEQ     R1, #"`"
+        MOVEQ   R0, #"_"
+        TEQ     R1, #"_"
+        MOVEQ   R0, #"#"
+        B       RDCH17
+
+; *****************************************************************************
+;
+;       DoOSBYTE87 - OSBYTE &87 entry point
+;
+; in:   -
+;
+; out:  R0 = &87
+;       R1 = character at text cursor, 0 if unrecognised
+;       R2 = screen mode
+;
+
+DoOSBYTE87
+        Push    "R3-R11,R14"
+        BL      ReadCharacter
+        MOV     R1, R0
+        MOV     R0, #&87
+        LDR     R2, [WsPtr, #ModeNo]
+        CMP     R0, #0                          ; clear V for any wallies!
+        Pull    "R3-R11,PC"
+
+; *****************************************************************************
+;
+;       PageTest - check for CTRL/SHIFT, page mode
+;
+; in:   R6 = CursorFlags
+;
+
+PageTest
+        Push    R14
+
+        CLC                             ; don't set leds first time
+        BL      CtrlShiftTest           ; on exit, C=CTRL, N=SHIFT
+        BCC     Page20                  ; CTRL up, then branch
+        BPL     Page20                  ; SHIFT up, then branch
+
+; CTRL and SHIFT are down
+
+        BL      ClearLines              ; CTRL+SHIFT down, so clear lines
+        BL      PostWrchCursor          ; we may be some time, so enable cursor
+CSWaitLoop
+        SEC                             ; set leds
+        BL      CtrlShiftTest
+        BCC     Page18
+        BMI     CSWaitLoop              ; and wait for change (NB C=1 now)
+Page18
+        BL      PreWrchCursor           ; get rid of cursor again
+
+; CTRL and SHIFT are not both down
+
+Page20
+        CLC                             ; don't set leds first time
+        BL      CtrlShiftTest
+        BCC     Page40                  ; [CTRL not down]
+
+; CTRL down, so wait for auto repeat delay time before continuing
+
+        BL      PostWrchCursor          ; we may be some time, so enable cursor
+
+        LDROSB  R1, KeyRepRate
+        STROSB  R1, CentiCounter, R0
+        CLC
+Page30
+        BL      CtrlShiftTest
+        BCC     Page35                  ; CTRL no longer down
+        LDROSB  R1, CentiCounter
+        CMP     R1, #1
+        BCS     Page30                  ; loop with carry set
+Page35
+        BL      PreWrchCursor           ; remove cursor again
+
+; CTRL not down, test for page mode
+
+Page40
+        EOR     R0, R6, #PageMode
+        TST     R0, #(PageMode :OR: CursorsSplit)
+        Pull    PC, NE                  ; cursors split or not in page mode
+
+        LDROSB  R3, PageModeLineCount
+        BL      BotRowCheck             ; are we on bottom row ?
+        BNE     IncLinesExit
+        TST     R6, #8
+        LDREQ   R0, [WsPtr, #TWBRow]
+        LDREQ   R1, [WsPtr, #TWTRow]
+        LDRNE   R0, [WsPtr, #TWRCol]
+        LDRNE   R1, [WsPtr, #TWLCol]
+        SUB     R0, R0, R1              ; get number of lines in window
+        SUB     R0, R0, R0, LSR #2      ; * 3/4 (rounded up)
+        CMP     R0, R3                  ; does PageModeLineCount exceed this ?
+        BCC     Page50                  ; yes, then wait until SHIFT up
+IncLinesExit
+        ADD     R3, R3, #1
+        STROSB  R3, PageModeLineCount, R0
+        Pull    PC
+
+Page50                                  ; NB C=0 on entry from above
+        BL      CtrlShiftTest
+        BMI     Page55
+
+; Waiting for shift
+
+        BL      PostWrchCursor          ; put cursor back on for now
+PageWaitLoop
+        SEC
+        BL      CtrlShiftTest
+        BPL     PageWaitLoop
+        BL      PreWrchCursor
+
+Page55
+        Pull    R14
+ClearLines
+        MOV     R0, #1                  ; fudge for MASTER compatibility
+        STROSB  R0, PageModeLineCount, R1
+        MOV     PC, R14
+
+BotRowCheck
+        TST     R6, #2
+        LDREQ   R1, [WsPtr, #TWRCol]
+        LDRNE   R1, [WsPtr, #TWLCol]
+        TST     R6, #4
+        LDREQ   R0, [WsPtr, #TWBRow]
+        LDRNE   R0, [WsPtr, #TWTRow]
+        TST     R6, #8
+        EORNE   R0, R0, R1              ; swap R0, R1
+        EORNE   R1, R0, R1
+        EORNE   R0, R0, R1
+        LDREQ   R2, [WsPtr, #CursorY]
+        LDRNE   R2, [WsPtr, #CursorX]
+        TEQ     R0, R2
+        MOV     PC, R14
+
+CtrlShiftTest ROUT
+        MOV     R0, #0
+        LDRB    R0, [R0, #ESC_Status]
+        TST     R0, #&40                ; escape condition ?
+        LDROSB  R0, KeyBdStatus         ; (preserves PSR)
+        BEQ     %FT10                   ; [no escape]
+
+        Push    R14
+        BIC     R0, R0, #KBStat_ScrollLock ; escape, so cancel scroll lock
+        STROSB  R0, KeyBdStatus, R14    ; and store back
+        MOV     R0, #&20                ; pretend shift down, ctrl up
+        MOVS    R0, R0, LSL #(32-6)     ; C=CTRL, N=SHIFT
+        Pull    PC
+
+10
+        TST     R0, #&08                ; shift bit
+        ORRNE   R0, R0, #&20            ; move it to bit below ctrl (bit 6)
+        BICEQ   R0, R0, #&20
+        TST     R0, #KBStat_ScrollLock  ; if scroll lock on
+        ORRNE   R0, R0, #&60            ; then pretend ctrl and shift down
+        MOVS    R0, R0, LSL #(32-6)     ; C=CTRL, N=SHIFT
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       SO - Page mode on
+;
+; in:   R6 = CursorFlags
+;
+
+SO
+        MOV     R0, #0
+        STRB    R0, [R0, #OsbyteVars + :INDEX: PageModeLineCount]
+
+        ORR     R6, R6, #PageMode
+        STR     R6, [WsPtr, #CursorFlags]
+
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       SI - Page mode off
+;
+; in:   R6 = CursorFlags
+;
+
+SI
+        BIC     R6, R6, #PageMode
+        STR     R6, [WsPtr, #CursorFlags]
+
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       DoResetFont - Reset some or all of the soft font from the hard font
+;
+; in:   R1*32 = start character, R2 = number of pages to copy
+;
+;       NB no range checking is done on these numbers
+
+DoResetFont     ROUT
+
+        ADRL    R0, HardFont-32*8
+        ADD     R3, WsPtr, #(Font-32*8)
+
+        ADD     R0, R0, R1, LSL #8      ; start source
+        ADD     R3, R3, R1, LSL #8      ; start dest
+        ADD     R1, R0, R2, LSL #8      ; end source
+
+10
+        LDR     R2, [R0], #4
+        STR     R2, [R3], #4
+        TEQ     R0, R1
+        BNE     %BT10
+
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       DoReadFont - Read character font
+;
+; in:   R1 -> control block
+;       [R1, #0] = character to read (2..5 => read ecf, 6=> read dotdash)
+;
+; out:  [R1, #1..8] = font for that character
+;
+
+DoReadFont
+        LDRB    R0, [R1]
+        CMP     R0, #32
+        ADD     R0, WsPtr, R0, LSL #3
+        ADDCS   R0, R0, #(Font-32*8)           ; R0 -> font
+        ADDCC   R0, R0, #(Ecf1-2*8)
+        LDMIA   R0, {R2,R3}
+
+        STRB    R2, [R1, #1]
+        MOV     R2, R2, LSR #8
+        STRB    R2, [R1, #2]
+        MOV     R2, R2, LSR #8
+        STRB    R2, [R1, #3]
+        MOV     R2, R2, LSR #8
+        STRB    R2, [R1, #4]
+
+        STRB    R3, [R1, #5]
+        MOV     R3, R3, LSR #8
+        STRB    R3, [R1, #6]
+        MOV     R3, R3, LSR #8
+        STRB    R3, [R1, #7]
+        MOV     R3, R3, LSR #8
+        STRB    R3, [R1, #8]
+
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       NAK - Disable VDU
+;
+; in:   R6 = CursorFlags
+;
+
+NAK
+        ORR     R6, R6, #VduDisabled
+        STR     R6, [WsPtr, #CursorFlags]
+        MOV     PC, R14
+
+; *****************************************************************************
+;
+;       STX - Turn printer on
+;       ETX - Turn printer off
+;
+; in:   R0 = 2 or 3
+;
+
+STX
+ETX
+
+; insert code here to call UPTVEC or NETVEC
+; (probably need to restore cursor while doing so in case of 'Not listening')
+
+        LDR     R1, [WsPtr, #VduStatus]
+        TEQ     R0, #2                          ; turning on ?
+        ORREQ   R1, R1, #Vdu2Mode               ; yes, then set bit
+        BICNE   R1, R1, #Vdu2Mode               ; no, then clear bit
+        STR     R1, [WsPtr, #VduStatus]
+        MOVEQ   PC, R14                         ; exit if not turning off
+
+        Push    R14
+        MOV     R0, #&7B                        ; make printer dormant
+        SWI     XOS_Byte
+        Pull    PC, VC
+
+; bad exit from the OSBYTE
+
+        Pull    R14
+        B       VduBadExit
+
+; *****************************************************************************
+;
+;       BEL - Do VDU 7
+;
+; in:   BELLchannel, BELLinfo, BELLfreq, BELLdur contain info for bell
+;       BELLinfo:       Bits 0,1        S bits
+;                       Bit  2          H bit
+;                       Bits 3-6        (envelope-1) OR (volume+15)
+;                       Bit  7          0 => envelope, 1 => volume
+;
+; out:  SOUND &HFSC, A, P, D
+;
+
+BEL     ROUT
+        Push    R14
+        BYTEWS  R0
+        ADD     R1, WsPtr, #(BeepBlock :AND: &FF)
+        ADD     R1, R1, #(BeepBlock :AND: &FF00)
+
+        MOV     R2, #0
+        STRB    R2, [R1, #5]                    ; zero hi-byte of pitch
+        STRB    R2, [R1, #7]                    ; zero hi-byte of duration
+
+        LDRB    R2, [R0, #:INDEX: BELLchannel]  ; copy channel
+        STRB    R2, [R1, #0]                    ; into OSWORD block
+
+        LDRB    R2, [R0, #:INDEX: BELLinfo]     ; get info
+        AND     R3, R2, #7                      ; bit 2 of R3 is H, bits 0,1=S
+        TST     R3, #4
+        EORNE   R3, R3, #(4 :EOR: &10)          ; put H into bit 4
+        STRB    R3, [R1, #1]                    ; store H and S
+
+        MOV     R2, R2, LSL #24
+        MOV     R2, R2, ASR #(24+3)             ; shift down, sign extending
+        ADD     R2, R2, #1
+        STRB    R2, [R1, #2]                    ; store lo-byte of env/vol
+        MOV     R2, R2, LSR #8
+        STRB    R2, [R1, #3]                    ; store hi-byte of env/vol
+
+        LDRB    R2, [R0, #:INDEX: BELLfreq]
+        STRB    R2, [R1, #4]                    ; copy pitch
+
+        LDRB    R2, [R0, #:INDEX: BELLdur]
+        STRB    R2, [R1, #6]                    ; copy duration
+
+        MOV     R0, #7                          ; OSWORD SOUND
+        SWI     XOS_Word
+        Pull    PC
+
+; *****************************************************************************
+
+; Compile the text fg / bg (and tint info if required) into a sensible set of
+; colour words.
+
+; The routines take the TextFg and TextBg colours, if NColour >= 63 then
+; we attempt to combine the tint information, this is then passed onto
+; ColourTrans to force the colour change.
+
+; in    -
+; out   [TextFgColour] / [TextBgColour] updated to contain new values
+
+; amg: 30/11/93 split these into separate routines to sort out a problem.
+; When OS_SetColour was being used for text colours in >=8bpp TForeCol/TBackCol
+; and TForeTint/TBackTint went out of step. Thus when a VDU 17 or VDU 23,17,0 | 1
+; came along both fore and back text colours were getting changed. This solution
+; is not perfect, since doing a TINT command before a colour change will still
+; go wrong but I consider that worth leaving against the alternative of changing
+; the function of this area of the code by having some magic value.
+
+CompileTextBg ROUT
+
+        Push    "R0-R1,LR"
+
+        LDR     R1, [WsPtr, #NColour]
+        CMP     R1, #63                         ; is this a daft mode?
+
+        LDR     R0, [WsPtr, #TBackCol]
+        LDRCS   LR, [WsPtr, #TBTint]
+        ANDCS   LR, LR, #&C0                    ; only look at 2 bits of tint
+        ORRCS   R0, R0, LR                      ; combine tint value to bg colour
+        BLCS    ConvertGCOLToColourNumber
+        STRVC   R0, [WsPtr, #TextBgColour]
+
+        Pull    "R0-R1,PC",,^
+
+CompileTextFg   ROUT
+
+        Push    "R0-R1,LR"
+
+        LDR     R1, [WsPtr, #NColour]
+        CMP     R1, #63                         ; is this a daft mode?
+
+        LDR     R0, [WsPtr, #TForeCol]
+        LDRCS   LR, [WsPtr, #TFTint]
+        ANDCS   LR, LR, #&C0                    ; only look at 2 bits of tint
+        ORRCS   R0, R0, LR                      ; combine in the tint value
+        BLCS    ConvertGCOLToColourNumber       ; convert colour using ColourTrans
+        STRVC   R0, [WsPtr, #TextFgColour]      ;   store the value away - assume it worked!?!@?
+
+        Pull    "R0-R1,PC",,^
+
+
+; *****************************************************************************
+;
+; Convert a GCOL including Tint information to a physical colour, returning
+; the colour number.  This is used for backwards compatibility with the
+; existing 8 bit interfaces providided within the kernel.
+;
+; in    R0 = GCOL ( + tint ) := t t b b g g r r
+; out   R0 = colour number (at current depth)
+;       V set if no ColourTrans module.
+
+; don't call CTrans if in 8bpp since otherwise GCOL numbers vary with
+; palette changes!
+
+ConvertGCOLToColourNumber ROUT
+
+        Push    "R1-R2, LR"
+
+        LDR     R1, [WsPtr, #NColour]
+        CMP     R1, #63
+        CMPNE   R1, #255
+        BEQ     %FT20
+
+        AND     LR, R0, #4_3000                 ; extract the tint information
+        MOV     R0, R0, LSL #14                 ; convert to a sensible bit location
+        AND     R2, R0, #4_0003 :SHL: 14        ; extract the red
+        ORR     R2, R2, LR, LSL #6              ; and then combine the tint information
+        AND     R1, R0, #4_0030 :SHL: 14        ; extract the green
+        ORR     R2, R2, R1, LSL #6
+        ORR     R2, R2, LR, LSL #14             ; and combine with tint and green
+        AND     R1, R0, #4_0300 :SHL: 14        ; finally extract the blue component
+        ORR     R2, R2, R1, LSL #12
+        ORR     R2, R2, LR, LSL #22             ; combine in the tint and blue
+        ORR     R0, R2, R2, LSR #4              ; ensure physcial colour yeilds pure-white!
+
+        SWI     XColourTrans_ReturnColourNumber
+
+        Pull    "R1-R2, PC",,^
+20
+        MOV     r1, r0, LSR #6                  ; r1 =   0  0  0  0  0  0 T1 T0
+        AND     r2, r0, #2_00100001             ; r2 =   0  0 B3  0  0  0  0 R2
+        ORR     r1, r1, r2, LSL #2              ; r1 =  B3  0  0  0  0 R2 T1 T0
+        AND     r2, r0, #2_00010000             ; r2 =   0  0  0 B2  0  0  0  0
+        ORR     r1, r1, r2, LSR #1              ; r1 =  B3  0  0  0 B2 R2 T1 T0
+        AND     r2, r0, #2_00001110             ; r2 =   0  0  0  0 G3 G2 R3  0
+        ORR     r0, r1, r2, LSL #3              ; r0 =  B3 G3 G2 R3 B2 R2 T1 T0
+        Pull    "R1-R2, PC",,^
+
+        LTORG
+
+        END