home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ARM Club 3
/
TheARMClub_PDCD3.iso
/
hensa
/
misc
/
tornado
/
tndemo2
/
_TRender
< prev
next >
Wrap
Text File
|
1996-05-16
|
7KB
|
139 lines
Tornado renderers
-=-=-=-=-=-=-=-=-
A tornado renderer provides facilities for rendering files. They are
automatically called upon by the shell to render a file as it presents itself
and should they not be already in memory they can be loaded in and cached by
tornado during the execution of the rendition.
A tornado renderer is similar to a normal tornado extension module, except:
(i) In it's initialise entry, it must declare itself as a renderer.
(ii) It must live in <Tornado$Renderers> rather than <Tornado$Extensions>.
(iii) It must provide all the mandatory entries as specified by the file
type it is supporting [1].
(iv) It must be able to render more than one file at once [2]
(v) It must be able to render more than one part of a file at once [2]
(vi) It must be able to handle being loaded in and quitted repeatedly, and
at any stage - even if that be in the middle of a rendition [3].
[1]: These entries are laid out in a list issued by me, the guy who's
designing and writing all this stuff. If you want to add a filetype, you draw
up a suitable addendum to the list, and email/post it to me. I review it, and
if I don't like it I'll refuse it.
This leaves you with two options. (i) You can go away and improve it or
(ii) you can go ahead and implement it anyway.
The first option is the correct one. The second will land you in trouble.
I will hinder any developments/improvements on any filetype not approved by
me. Is this not a bit arrogant? Yes, it is, but it's my operating system and
I'll have it go the way *I* want it. You don't like it - go away and write
your own operating system.
Anyway, there's always the general filetypes which you can extend if you
wish.
[2]: In fact, this is implemented by you writing a very clever piece of code
capable of being multithreaded (not fun). Tornado will handle all the hard
work of getting your code to do the work, although this requires cooperation
from you:
(i) You cannot use any static workspace unless you have developed some
brilliant way of allowing any parts of your code to access it at any time and
even while another thread of your code is in the middle of using it.
(ii) Thus, you should use dynamic workspace allocation. See below.
[3]: Ie; your renderer must be able to tidy itself and all its threads up
should .finalise suddenly be called. As your renderer code may be
initialised, quit, and then initialised again many times in one second, it
must *not* leave any wkspace unfreed as this will obviously fill up memory
very quickly.
Technical stuff:
-=-=-=-=-=-=-=-=
Right, here's how tornado interfaces with your renderer:
In the tornado .initialise, you must call Tornado_RegisterRenderer, passing
your details (see below for parameter block).
In the .threadinit entry, you should claim a piece of wkspace that will be
enough for a thread to work with entirely if possible. However, if this
is not possible (max memory that might be required is several K for each
thread for example), check what the tornado config says - use more memory, or
use less memory (Tornado_Config). If it says use less memory, assume that
there is more CPU power available and dynamically alter the block's size
during the thread's execution (even though this is a non-preemptable action
and everything will be held up as a result). If it doesn't, allocate the most
the thread could possibly need. Later versions of tornado may automate this
regulation of memory for you later but currently it is up to you to mark what
method you are following and follow it accordingly.
The .threadexec entry will be entered as follows:
R8 = undefined as yet, but can be ignored by your code (ie; overwritable)
R9 = a binary set of flags:
bits 0,1: unset
bits 2-25: reserved
bits 26-31: unset
You may overwrite these, but it is advisable you don't (see below).
R10 = a pointer to the file data you are rendering
R11 = a pointer to your thread's workspace as set up by your init routine
R12 = a pointer to the workspace of your module (should only be used for
reference!)
R13 = a private stack peculiar to your thread which is empty (64 bytes long,
so don't push too much onto it, and allow for routines you call to use it)
R14 = a link back to tornado when you have finished the rendition
Code is in USR mode (no need to stack R14), all interrupts enabled.
This leaves you R0-R7 for your own private use. This is admittedly grim, but
there's not much that can be done about it.
There are a few things that should be noted about multithreading as most
Acorn programmer's probably aren't familiar with it (nor am I - I'm not even
sure if this will work yet!):
Firstly, your code can be interrupted at *any* time. You cannot rely on
registers R10-R13 to remain static. In other words, you can't do this:
ADD R0,R11,R3
STR R5,[R0,#44]
You see, if R10 gets changed between the ADD and the STR, the R5 will be
stored at the wrong address, possibly causing an Address Exception or at the
least corrupting someone's data (usually your own).
Here's how you would rewrite this:
ADD R0,R3,#44
STR R5,[R11,R0]
A simple little change, no difference normally, but when multitasking
preemptively makes a world of difference. The same goes for offsetting into
the file, using a register as an offset pointer - always LDR/STR with R10 as
one of the parameters.
But what happens if this isn't possible eg; building coordinates for plotting
onto the screen?
Well, the solution is to halt multitasking. It's not a good idea, and should
be avoided if at all possible, but often it simply isn't. Simply place a SWI
"XOS_EnterOS" in front of your non-preemptable code, and a TEQP R9,#0; MOV
R0,R0 after it, assuming you've left R9 intact.
Another point in all this is speed. Renditions must be *FAST*. That's why you
*MUST* write your rendition code in assembler (you'll note how C or anything
else would die horribly in the above circumstances). Optimising your code
(rolling out loops, organising instructions in the most efficient manner etc)
really does pay dividends in redraw speed, and it is this above all that
marks out one application over another (eg; Zap versus Edit - redraw is so
blindingly fast it hurts, especially on the older versions without
ZapRedraw). To this end, it is important to note the CPU cost of disabling
preemption - calling SWI's is a notoriously slow affair (much worse on RO3+
than RO2 I might add) at around 50-70 instructions; added to this is the cost
in everything being held up, followed by another two otherwise unnecessary
instructions.
Another instance of this is using LDR/STR against LDM/STM. The latter is much
quicker for multiple regs. So much quicker in fact that for any decent amount
of memory shifting it may be quicker to disable preemption for it (you must
as you can't index the position register). There may be support for multiple
get/put's in later versions of the multitasker, but as yet you can usually
not use them, and such support will not affect threads written this way now.