           		+-----------+
           	  	|           |
	                | PseudoMem |
		        |           |
        		+-----------+
                          
              (c) Gordon Rogers, 1996/7

WARNING: Use of this program is entirely at your own risk (although it is
fairly robust, unless you do something stupid {you have to be pretty clever
to do something stupid}). Do NOT attempt to use this program from interrupt
driven routines (such as with tracker-style modules) as your machine will
crash (and kill all your programs in it).

What is PseudoMem?
~~~~~~~~~~~~~~~~~
PseudoMem is a simplified alternative to virtual memory. It provides,
simply, a means of accessing the contents of files without loading them.
You're probably thinking that you can do this with the OS's excellent file
handling swis anyway, but you can't run Basic programs or plot sprites
without loading them...until now.

Unfortunately, because the program makes use of a new (although slightly
bugged) feature of Risc OS 3.50+ (even more bugged on Risc OS 3.70, but who
cares), it would require much more code to program it on earlier machines.
It also makes use of dynamic areas, so there's another modification that
would be needed.

So what exactly does PseudoMem provide? Well, a replacement to OS_Find which
allows access to a file (via any STR, LDR, STM or LDM instruction - the only
exception is SWP which the OS doesn't cater for) in a dynamic memory area.
It also corrects the bugs mentioned above. What this means is that anything
in the OS which uses any of the 4 main memory access instructions can now
access files. This includes the *Memory commands, sprite access and even
running or listing Basic programs.

It's also a very nice little module which tidies up after one of the
horrible bugs in SpriteExtend (the one which prevents you from plotting
JPEGs from <24k wimpslots). It also provides a convenient command line
interface to the main aspects of its swis.



Why not just use Virtualise from Clares?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Seeing as how Virtualise is a full virtual memory system, why not just use
that? Compare the basics (based on the demo version on the SA-CD):

The Virtualise module takes over 30k, whilst PseudoMem uses just over 5k,
yet, as far as I'm concerned, PMem is easier to use. After all, you can use
it from the command line or an Obey script.

Ok, so Virtualise traps SWPs and all FPU memory accesses, whilst PMem only
deals with STR/LDR/STM/LDM instructions. But few programs use these other
instructions anyway. Furthermore, if you're using the FPEmulator, all FP
memory accesses are via the 4 main instructions anyway. Hence, no problem
there.

Both PMem and Virtualise only trap Data Aborts (there's not much point in
trapping pre-aborts as the OS isn't too keen on them).

The main things PMem doesn't allow, that I haven't already mentioned are
paging, because I can't be bothered for the moment, and handling interrupt
code satisfactorily, but for efficiency you'd just stick data to be accessed
from interrupt code in proper memory anyway.

This kind of begs the question, why use Virtualise? (probably cause it is,
in the long run, faster, but that depends on what you're using it for).



The * commands:
~~~~~~~~~~~~~~
*MemOpen <filename> [<maximum size of area>] [TO [<address variable> [<file
handle variable>]]]

*MemOpenR <filename> [<maximum size of area>] [TO [<address variable> [<file
handle variable>]]]

If no maximum size is given, then the size of the file would be used instead
(don't try this with a freshly created file). With the optional TO
construct, the results can be returned in system variables. This is
primarily intended for use in Obey scripts. With neither variable name
given, the output is just swallowed by a sea of nothingness. You can still
get rid of such areas by their filetype, but it's not wholly recommended.

*MemOpen calls PMem_Find (see below) with r0=&CC to open a file for update,
and displays the file handle and address returned. The file can then be
accessed by adding offsets into the file to this address. *MemOpenR does
exactly the same, but for read only files. So if you want to disassemble
bytes &100 to (&170-1) in the file 'File', you might type (things following
the *'s):

*MemOpen File
File handle: 253
Address: &05D49000 (for example)
*MemoryI 5D49100 +70

and those words would be disassembled. See below for further examples of
what you can do.



*MemClose <area descriptor> [-handle|-area|-type|-address] [-full|-one]

Closes an PMem area or a file. The area desciptor can be any of (prepare
yourself):

a) a file handle, and the file will be closed and it's PMem area removed.
Although no error is produced for non-PMem files, the file won't be closed.

b) address of the area, as given by MemOpen or MemOpenR (or the swis), and
the file will also be closed.

c) dynamic area number (although this isn't given by the commands, it is
returned by the swis). The file will also be closed.

d) a filetype (name or number) and all files with that filetype with a
corresponding PMem area will be closed.

e) 'ALL' and all files will be closed.

All open files must be closed before killing the module. If you append any
(number of any) of the switches, then only those descriptors will be
checked. This is of more use in an obey script where you don't necessarily
want a prompt returned because another program has a similarly numbered
object in use. If you offer a filetype name, but include some other switch,
the type will not be checked (for consistency). No such limitation applies
to 'All' for obvious reasons. If more than one object type (either of all
types, or all those whose switch is present) matches a given number, you
will be asked which type you wish to remove.

I will just stress again, YOU DO NOT HAVE TO TELL MemClose WHICH TYPE OF
OBJECT YOU ARE GIVING IT.

Switches '-full' and '-one' remove all levels and one level of links
respectively. '-one' takes priority and is the default.

To get rid of ALL PMem areas, use

*MemClose all -full

Note: The module itself defines a few extra hidden areas to correct various
OS bugs. These are immune to MemClose and swi 'PMem_Close' detailed below.

Note: If only one file has different object types matching a particular
number (eg. a file typed &0F2 gets opened with that handle) you will still
be prompted. This basically because such an occurence is deemed too rare,
you could use one of the other types to remove it, and because it's too
esoteric to expend so much energy writing around it. So there!

For example:

*MemClose Basic		closes all basic programs.
*MemClose &FFB		does the same, if no area has number &FFB, for which
			you would then be prompted.

*MemClose <address>	closes a file refered to by the contents of system
			variable 'address'



*PBasic [-help|-chain|-load|-quit] <filename>

With the -chain and -quit options, this creates a PMem area for your basic
program, reduces the wimpslot by the size of the program (so you don't need
to if you've got your program to work otherwise), and runs your program.

With -help and -load, or anything else, it just passes the command on to
basic (ie. removes the P)

PBasic is primarily intended for programs where it doesn't matter if it runs
slowly, for example a multi-tasking front-end to a module routine (which
runs in ARM code anyway). ChangeFSI, for example, is perfect (especially as
it takes up 160k of memory in the main program).



The swis:
~~~~~~~~
PMem_Find (&4E900 - officially allocated)

-> r0    Reason code and flags as for OS_Find (see appropriate documentation)
   r1    File handle (r0=0) or filename (r0<>0) as for OS_Find
   r2    Path (optional) (r0<>0) as for OS_Find
   r3    Maximum size of dynamic area to create (or 0 to use file's own
	 length) (r0<>0)
   
<- All registers are preserved if r0=0 on entry, otherwise:
<- r0    File handle
   r1,r2 preserved
   r3	 Address of area allocated
   r4    Area number allocated (just for reference)

PMem_Find acts as a replacement for OS_Find when open a file for memory
access. Apart from r3 and r4, everything else is as per OS_Find. The only
point worth mentioning about it's use otherwise are that r3 will be rounded
up to the next multiple of the page size (ie. 4k), so you needn't worry
about that. 

Also note that the main routine which provides the file access provides a
special extension for sprite files. The file is accessed at offset <address
returned>+4, with !<address returned> containing the length of the area.
This is so that the area can then be used as a sprite area, and the sprites
plotted directly from your disc.

Use of r0=0 to close the file will only work if the file's link counter has
reached zero (see PMem_Link below)

For obvious reasons, it is recommended you only use the swis in programs,
and leave the *commands to the command line.


PMem_Open (&4E901)
PMem_OpenR (&4E903)

-> r0    Filename
   r1    Maximum size of dynamic area to create (or 0 to use file's own
	 length)
   
<- r1    Area number allocated (just for reference)
   r2    Address of area allocated
   r3    Size of file (for size of area, use OS_ReadDynamicArea)
   
   
PMem_Close (&4E902)

-> r0    Either area number, or start address of area

<- All registers preserved

PMem_Open[R] and PMem_Close are intended for use if you do not wish to use
the usual file access swis on the file, and can thus use less registers when
opening and closing files. They're also handy for plotting jpegs - see
below. PMem_Close determines the file handle from that stored in the
internal list corresponding to the information given.

PMem_OpenR opens the file for read-only access, whereas PMem_Open opens it
for update. If the file has already been opened, it's link counter will just
be incremented instead.

The file will only be removed with PMem_Close if it's link counter is zero
(see below).


PMem_Link (&4E904)

-> r0    File handle or Address of area
   r3    Size of file or 0 (given file handle)

<- r1	 Area number allocated (for reference)
   r2    Address of area allocated
   r3	 Size of file (for size of area, use OS_ReadDynamicArea)
   
Whilst modifying another module to use PseudoMem, I discovered that if a
program had already opened a file, there was no easy way of using PMem on
it. PMem_Link provides a way of avoiding this problem. Instead of just
closing and re-opening the file, which would lose the current file pointer,
and possibly alter the file handle, PMem_Link allows you to link a
previously opened file into the PMem chain. This features is primarily
intended for module coding, for which it's use if recommended. Ordinary
programs are expected to use PMem_Open[R] and produce an error if it fails
(file open).

When a file is linked in, it's link counter is incremented (to a depth of
16, which should be plenty enough) so the file can only be removed after an
equivalent number of closings. As the caller of the module routine
(typically an swi) may have opened the file via PMem or OS_File, to save the
swi routine from checking itself, PMem will do this for you, enabling you to
find the full information about a previously opened file.

Note: *MemClose will remove the file no matter how many times it's been
linked or opened. This is because *MemClose is intended to be used for brute
force removal of files if they linger, or simple command line usage, which
typically wouldn't involve multiple openings of the same file.



Semi-hidden features:
~~~~~~~~~~~~~~~~~~~~
The module provides a subtle modification to OS_ReadDynamicArea and
OS_DynamicArea to return the max size of the area in place of the actual
(zero) size of the area. This is so you can easily see how big the area is,
although it may cause confusion with the task manager. I urge you not to
fall for believing that an area whose name begins 'File:' actually takes up
memory - IT TAKES NO MEMORY AT ALL.

As well as this, the module traps the Validate_Address service call, thus
any attempts to Validate your memory will also work as expected for ordinary
memory.

This note is for hackers: swi PMem_63 is for internal use only. It's use is
not particular useful to anybody but me.



Examples of usage:
~~~~~~~~~~~~~~~~~
As mentioned above, sprites can now be plotted directly from a file. As a
simple demonstration of this, find a suitable sprite file (preferably one
with a sprite in the current mode) (called, for example 'SpriteFile') and
type in the following (after *'s and >'s, ignoring the comments in brackets)
at the CLI prompt (to avoid problems with the graphics origin whilst in the
desktop):

*MemOpen 'SpriteFile'
File handle: 249
Address: &05D49000         (obviously these may be different on your system)
*MemoryA 1554 5D49000      (this redirects the system sprite area to your
			    new dynamic area, ie. so the following works)
*SList sprite *SChoose sprite
*Basic
ARM BBC BASIC V version 1.16 (C) Acorn 1989

Starting with 1044732 bytes free

>PLOT&ED,0,0
>*MemClose 249             (if you don't want to do anything else)

And lo, your sprite has been plotted, without loading it into memory first.


As a further demonstration of graphics plotting, and some of the swis, enter
Basic and type in the following:

X=640:Y=512		   (plug in your own prefered coordinates)
SYS"PMem_Open","jpeg_file" TO ,,Address,Size
SYS"JPEG_PlotScaled",Address,X,Y,0,Size,%11
SYS"PMem_Close",Address    (to remove the area)

and your JPEG is plotted.

As a third example, which is seriously impressive (I'm easily impressed),
find a nice Basic program (ie. ANY Basic program), and type in (as before):

*PBasic -quit BasicProg

Your program will now be running.

As an even more impressive example, find any old Wimp App for which the main
program is written in Basic (eg. ChangeFSI) and make the following
modification to a copy of it (an example of ChangeFSI v1.12 is given
afterwards for clarity).

Unlock !Run (if necessary)
Put a 'P' in front of the Basic instructions, or if there isn't one (eg.
you've just got Run !RunImage), replace the Run with 'PBasic -quit '.

Your app should be running in a potentially drastically reduced wimpslot. An
additional advantage of this on a multitasking machine is that as your
program may not be running all the time, it doesn't waste memory while not
being used, and with ADFSBuffers, it won't be slowed too much either.
Single-tasking programs may be noticeably slowed however. If this is a
problem on a ARM 610/710, you should get a StrongARM.

Applying the above to ChangeFSI(v1.12) we have:

Make a copy of the application first (just in case)
Unlock !Run
Change the last line to

   PBasic -quit <ChangeFSI$Dir>.ChangeFSI -wimp %*0
   
Run the app as usual and prepare to be impressed at saving nearly 160k of
memory.
When the application exits, the PMem area will be automatically removed.

As an alternative to all this business about running Basic programs, you
could just enter

*Set Alias$@RunType_FFB P<Alias$@RunType_FFB>

and be done with it. Now all programs will run from PMem areas. Some
programs (notably single tasking ones) will run noticeable slower, so if
you're not prepared for this, change it back by entering:

*Show Alias$@RunType_FFB
Alias$@RunType_FFB : P{whatever was there before, the default if unaltered}
*Set Alias$@RunType_FFB {whatever was there before ...}


<Additional - 20th Feb 1997>

For an example of PMem at work, check !IBrowse from Dave Thomas (see the end
of this helpfile for details of where you can get it from).




History-type-of-thing
~~~~~~~~~~~~~~~~~~~~~

Dec 1996

First attempts. Discover OS provides most of the code for me already, just
entailing me to include the front end. Unfortunately had problems with
sprites (even after patching in a length word). This was due to a bug in the
OS code.

v.0.01 - 11 Jan 1997

First fully working version (correcting the OS bug), save for a few bits of
tidying up. Included extended support for spritefiles (including a length of
area word at the start so sprites could be accessed without amendment).

v.0.02 - 15 Jan 1997

After favourable responses from Dave Thomas, start tidying up loose ends,
and including some extra bits. These include:
Included dynamic area handler.
Made the module use the system heap instead of RMA.

v.0.10 - 18 Jan 1997 (major alterations)

Patched OS_DynamicArea (r0=2) and OS_ReadDynamicArea to return maximum size
instead of zero size in r2 and r1 respectively.
Answered Service_ValidateAddress, as otherwise my new memory isn't
validated.

The main intention of these alterations, although useful anyway, was to try
and get !Zap to edit PMem areas without filling extra memory. Unfortunately
this doesn't work - !Zap copies the contents of the area to edit, and also
doesn't seem to be able to read the contents anyway (see below, !Zap also
uses writeback in copying the areas)

v.0.11 - 25 Jan 1997

Corrected for another bug in the OS code which (if what I surmise from Dave
Thomas is correct) only applies to ARM 810 and SA-110 machines. The OS code
didn't bother to correct for write-back (on earlier processors, the
instruction is allowed to finish without data transfer, and the appropriate
address written back {start or end}; ARM 810 and SA-110 don't do this . . . 
neither did the code). 

{6 Feb 1997 - Acorn have been told about this and the other bug (mentioned
above)}

This produced some very bizarre problems.

Known bug: JPEGs plot outside of desktop, but not inside desktop in windows
(check !IBrowse). This is similar to a problem encountered with sprites on
my SA-110 machine (plotted OK out of desktop {doesn't use write-back in
plotting routine} but not in). I don't understand what the difference is
either.

!Zap will now work in this respect with it, but it still copies the area,
so, in that respect is not of much use.

v 0.12 - 26 Jan 1997

Further bug correction for the OS:

SpriteExtend has a bug from versions 0.99 to 1.04 which crashes any task
with a wimpslot less than 24K when trying to plot a scaled JPEG. My
wonderful module corrects this with less than 100 bytes of code (most of
this is just me being neat).

Got around to (completing) sticking the basic patch to enable basic proggies
to run straight from PMem areas with the minimum of fuss. Included putting a
subsidiary program into ResourceFS. Tidied up PMem_Open (ie. made it work).

Also discovered the JPEG plotting problem is due to Dave Thomas forgetting
to find out how big the JPEG is, so his program thinks all JPEGs are of zero
size. Thanks Dave (after I spent last Sunday going through SpriteExtend with
a fine toothed comb).

v 0.14 - 8/9 Feb 1997

Expanded the scope of MemClose.
Tidied up the code for producing the da name.
Included dynamic area renumber service code.

Only one known bug remains: When a basic program produces an error, my exit
handler is by-passed and so the PMem area remains. Basic DOES exit via the
exit handler in all circumstances, so this bug remains a mystery.

10 Feb 1997

Fixed the one remaining big bug. Everything works (so far as I know), except
a possible problem with '-chain'-ing a Basic program and it producing an
error. Sometimes, it quits and hangs the machine. As few programs are
'-chain'-ed anyway, it can be regarded as unimportant.

v 0.15 - 14 Feb 1997

Got chunk number from Pineapple Software. First official release version.

15 Feb 1997

Ahem, correct something I hadn't noticed before. PMem leaves little deposits
of memory all over the RMA when you run Basic programs because I forgot to
remove them. Now they are removed.

v0.16 - 16 Feb 1997

Added PMem_Link, and altered the code for other swis to allow for links.

Only feature I'm not fully happy with is that {*/P}Mem[_]Open[R] don't just
link if you give the filename of an already open file. This is tricky as
you can't easily (so far as I can tell) retrieve a file handle given a
previously opened pathname. Workaround code is icky. (File handle is needed
to make code a lot easier. Otherwise code is longwinded.)

v0.17 - 20 Feb 1997

Corrected a tiny little bug (in error trapping of all places) that most folk
won't ever notice in PMem_Link. It is now all fixed OK (honest).
As Dave Thomas had put PMem on his webpage a few days ago, although
correcting the bug doesn't really justify a new version number, it didn't
give much choice in that respect. Anyway, it gives a chance it mention some
future developments thought up since then.


So what are these future developments then?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Major development (when I crease out all the wrinkly bits):

ROM hacking without wasting 4k patching a page in over a ROM page. Yes, the
ability to insert code into ROM modules without having to copy them to RAM
or pasting over them. (I know your thinking that this must be impossible,
but you probably thought running Basic programs without loading them was
impossible as well).


Additionally, allowing the user to create their own PseudoMem areas whereby
the contents are controlled by your own program instead of from a file.
(Obviously this would only be used where speed didn't matter).


Also, look out for further modules-and-the-like making full use of PseudoMem
from me in the future.



Copyright & how to contact me
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This module and helpfile are the copyright of Gordon Rogers, and is supplied
"as is". You may pass the module on to anyone you please, so long as it's
not for personal gain, and it is delivered with this accompanying helpfile.

Should anybody reading this (far) want to contact me with bug reports,
suggesions for improvements, or anything else, I can be reached at either

    Email: gdr@staru1.livjm.ac.uk

SnailMail: Mr G. Rogers
           15 Albert Road
           Tuebrook
           Liverpool
           L13 8DZ
           
If you wish to use PseudoMem in any of your own programs, please include the
helpfile, and contact me for the latest version. Also, it would be nice if
you could send me a copy of your program as well as an aid to the future
development of the module.

The latest version of PseudoMem (and !IBrowse) may be available via Dave
Thomas homepage at http://java.cms.livjm.ac.uk/cmsdthom/ subject to Dave
uploading stuff there for me. (Thanks Dave)