From cmarshall@acorn.co.uk  Wed Apr 15 03:48:28 1992
Received: from unido.Germany.EU.net by helpdesk.rus.uni-stuttgart.de (5.52/BelWue-1.0SG(subsidiary))
	(for zrzm0111) id AA11509; Wed, 15 Apr 92 03:48:28 MST
Received: from eros.uknet.ac.uk 
	by mail.Germany.EU.net with SMTP (5.65+/UNIDO-2.1.0.b)
	via EUnet for helpdesk.rus.uni-stuttgart.de
	id AA28243; Wed, 15 Apr 92 03:49:54 +0200
Received: from acorn.co.uk by eros.uknet.ac.uk with UUCP 
          id <23398-0@eros.uknet.ac.uk>; Wed, 15 Apr 1992 02:48:15 +0100
Received: from oak.acorn.co.uk by acorn.co.uk (4.1/Am32) id AA29108;
          Tue, 14 Apr 92 10:52:30 BST
Date: Tue, 14 Apr 92 10:52:53 +0100
From: cmarshall@acorn.co.uk (Chris Marshall)
To: zrzm0111
Subject: Re: Wanted: Information about the preempter
Message-Id: <29EAB985@cmarshall>
Status: RO


In article <1992Apr13.183936.3762@news.uni-stuttgart.de> you wrote:

>Hi
>
>A few weeks ago, someone told here, that he wrote a tool for preemptive
>multitasking and will public it in comp.binaries.acorn.
>Unfortunatly comp.binaries.acorn is closed for a while. Could someone 
>give me informations about the author ?

It's included here...

>thanks
>so long
>MUFTI
>
>ps:
>    Shouldn't the moderator of comp.binaries.acorn lay a forward to 
>    one of the ftp/mail-servers, if he isn't on duty for a long time ?
>    The software he gets is not for private Acorn ltd. use .....

We could forward all stuff to Albert at Newcastle. The reasons that 
nothing has been posted for a while are as follows:-

        a) Phil Colmer is ill at present; he would normally handle
           comp.sources.acorn

        b) Alan Glover and myself are v. busy right now. I expect to
           begin posting things again soon, maybe withing the next week
           or two. As for Alan, I don't know. Unfortunately, moderating
           comp.*.acorn isn't really seen as part as the 'job' that we
           do here, so it has to be a spare time activity. 

Chris.

______________________________________________________________________
Chris Marshall                                   cmarshall@acorn.co.uk

>From pc123@phx.cam.ac.uk Tue Feb 25 19:29:59 1992
Received: by oak.acorn.co.uk (4.1/Ai1.6)
	id AA11229; Tue, 25 Feb 92 19:29:56 GMT
Received: by acorn.co.uk (4.1/Am32)
	id AA13477; Tue, 25 Feb 92 19:29:02 GMT
Received: from phx.cam.ac.uk by eros.uknet.ac.uk via JANET with NIFTP (PP) 
          id <25975-0@eros.uknet.ac.uk>; Tue, 25 Feb 1992 18:22:59 +0000
Date: Tue, 25 Feb 92 18:22:08 GMT
>From: Pete <pc123@phx.cam.ac.uk>
To: submit@acorn.co.uk
Subject: Code for pre-empting a non-desktop task
Message-Id: <A54D6B55FDE90040@UK.AC.CAMBRIDGE.PHOENIX>

Newsgroups: comp.sources.acorn

Here is the code to demonstrate how you can pre-empt a non-desktop program
without running it in a taskwindow or under FrontEnd, as I promised on c-s-a
recently.  The code implements a module which acts a bit like FrontEnd but is a
tinsy bit more efficient in its use of RMA; it should be possible to convert it
for multitasking raytracers or other code you are writing without too much
difficulty.

The module writes all output generated by the program into a circular buffer at
a fixed offset in its workspace - a desktop task then uses the OS_Module call
to find out where the workspace is, and reads data out.  The effect is that
text produced by a program is not stored in the RMA, so excessive fragmentation
does not occur.  This also implies that this module is only the "business end"
of a complete FrontEnd replacement, and you have to write a desktop client
program as well, to display output produced by a task in a window.  I am not
posting the client here, because this source is really for illustration only.

The module also allows the task it is running to be stopped and restarted under
control of the controlling program.  This is accomplished by writing various
values into another word at a fixed offset in the module's workspace.
Similarly the module may be asked to kill the program it is pre-empting (never
scheduling it again, and closing all its files) or terminate it (call its exit
handler).

As I wrote Express Assembler, the module is written in Express format.  You
shouldn't have too much trouble understanding it, as it does not use any fancy
features.

It is well known that only Quiche Eaters comment their programs well.  I have
added a few, but...

...if you find you need help following it, send me mail and I will do my best
to help.  Or flame about it on c-s-a instead  :-)  .


        SETTYPE "MODULE"
        OBJECT "!YAMU.Initiator"
        ORG 0
        ASSUME PC,0
        ASSUME R12,&10000000

        SET VSTR,"1.00"

        DD LANGUAGE,0,0,SERVICE,TITLE,HELP,0,0,0,0,0

TITLE   DB "Initiator",0
        ALIGN

HELP    DB "Initiator",9,"`VSTR` (",OSVAR "Sys$Date" RIGHT 6," ",OSVAR "Sys$Year",")",0
        ALIGN

TASKDESC
        DB "Init",0
        ALIGN

LANGUAGE
        LOCALS
        MOV R11,R0

        ; Claim memory

        MOV R0,#6
        LIT R3,WEND-WSTART
        SWI "OS_Module"
        STR R2,[R12]
        STR R12,[R2]
        MOV R12,R2

        ; Initialise variables

        MOV R0,#0
        ST R0,MEMORYMAPVETOED
        ST R0,OUTBUFFERREAD
        ST R0,OUTBUFFERWRITE
        ST R0,MESSAGES
        ST R0,SUSPENDED
        ST R0,TERMINATING
        ST R0,HANDLESUSED

        ; Register as Wimp task

        MOV R0,#200
        LIT R1,&4B534154
        ADR R2,TASKDESC
        SWI "Wimp_Initialise"
        ST R1,TASKHANDLE

        ; Calculate appropriate (initial) slot size.  This is not calculated
        ; in any special way, it just tries to allocate a reasonable amount
        ; of space.  The newer applications ask the Wimp for more space if
        ; they run out.

        MVN R0,#0
        MVN R1,#0
        SWI "Wimp_SlotSize"
        ADD R0,R0,R1
        ADD R0,R0,R2
        CMP R0,#512*1024
        MOVGE R0,#512*1024
        SUBLT R0,R0,#64*1024
        CMP R0,#0
        MOVLE R0,#16*1024
        MVN R1,#0
        SWI "Wimp_SlotSize"

        ; Initialise environment, so that funny things don't happen when the
        ; background task calls the exit handler, etc.  In fact the exit
        ; handler has to come back into this code in order for us to tidy up.

        MOV R0,#6 ; Error handler
        ADR R1,ERRORHANDLER
        MOV R2,R12
        ADR R3,ERRORBUFFER
        SWI "OS_ChangeEnvironment"
        ADR R0,OLDERRORHANDLER
        STMIA R0,{R1-R3}

        MOV R0,#11 ; Exit handler
        ADR R1,EXITHANDLER
        MOV R2,R12
        MOV R3,#0
        SWI "OS_ChangeEnvironment"
        ADR R0,OLDEXITHANDLER
        STMIA R0,{R1-R3}

        ; Set interruption of background task

        BL SETCALLAFTER
        BL CLAIMVECS

        ; And start the task off

        MOV R0,R11
        SWI "OS_Write0"         ; This just writes the name of the command
        SWI "OS_NewLine"        ; run into the output buffer.
        MOV R0,R11
        SWI "OS_CLI"            ; Execute the * command that runs the task.

        ; This may return, if no application is started.  In this case, there
        ; is an outstanding ticker event to cancel, but apart from that we
        ; just exit.

        ADR R13,USRSTACK
        BL CANCELTICKER         ; Cancel the descheduling time-out.
        BL RELEASEVECS          ; Release vectors we have claimed.
        B KILLMODULE

MAINLOOP
        BL RELEASEVECS          ; Must release all vectors before polling.
A
        MOV R0,#0
        ADR R1,POLLBLOCK
        SWI "Wimp_Poll"
        TEQ R0,#0
        BLNE NONIDLEPOLL
        ADR R13,USRSTACK
        BL RECEIVEMESSAGES
        BNE A

        ; See whether the output buffer is empty - if not we wait until it
        ; has been emptied before running the task again.

        LD R0,OUTBUFFERREAD
        LD R1,OUTBUFFERWRITE
        TEQ R0,R1
        BNE A

        ; If the task has been stopped by the controlling task, don't
        ; reschedule it.

        LD R0,SUSPENDED
        TEQ R0,#0
        BNE A

        ; Set up conditions for re-entering task

        BL SETCALLAFTER         ; Create a new CallAfter event
        BL CLAIMVECS            ; and claim the vectors again.

        ; If the task is being terminated, get ready to call OS_Exit

        LD R0,TERMINATING
        TEQ R0,#0
        MOV R0,#0
        ST R0,TERMINATING

        ; Back into the task again.  When restoring the registers, note that
        ; the correct method for doing this is very dependent on the current
        ; processor mode - what is shown is NOT a general way of restoring
        ; a register dump after an exception.

        ADR R0,REGBUF
        LDMEQIA R0,{R0-PC}^     ; Restore the task's registers.
        LDMIA R0,{R0-R14}
        MOV R0,#0
        LIT R1,&58454241
        MOV R2,#2
        SWI "OS_Exit"           ; Task is being terminated.  Note how we can
                            ; even call OS_Exit "within" a CallBack handler.

        ; General rubbish to do with being a Wimp task, nothing amazing here.

NONIDLEPOLL
        TEQ R0,#17
        TEQNE R0,#18
        MOVNES PC,R14
        LD R0,POLLBLOCK+16
        TEQ R0,#0 ; Message_Quit
        MOVNES PC,R14
        ADR R13,USRSTACK
        BL CANCELTICKER
        MOV R0,#0
        ST R0,OUTBUFFERREAD
        ST R0,OUTBUFFERWRITE

        ; and continue into KILLMODULE, below.

        ; Code to kill the module off.  Nothing here that you don't get in
        ; other modules, the only point worthy of note is that we hang around
        ; in this routine until all the characters have been read out of the
        ; circular buffer.

KILLMODULE
        LOCALS

        ; Restore environment

        MOV R0,#6 ; Error handler
        ADR R1,OLDERRORHANDLER
        LDMIA R1,{R1-R3}
        SWI "OS_ChangeEnvironment"

        MOV R0,#11 ; Exit handler
        ADR R1,OLDEXITHANDLER
        LDMIA R1,{R1-R3}
        SWI "OS_ChangeEnvironment"

        ; Write the return code into the message word, for the foreground
        ; task

        LD R0,RETURNCODE
        ORR R0,R0,#&80000000
        ST R0,MESSAGES

        ; Now call Wimp_Poll until all the characters have been removed from
        ; the buffer and the return code has been read

A
        MOV R0,#0
        ADR R1,POLLBLOCK
        SWI "Wimp_Poll"
        LD R0,OUTBUFFERREAD
        LD R1,OUTBUFFERWRITE
        TEQ R0,R1
        BNE A
        LD R0,MESSAGES
        TEQ R0,#0
        BNE A

        ; Finish off as a Wimp task

        LD R0,TASKHANDLE
        LIT R1,&4B534154
        SWI "Wimp_CloseDown"

        ; Release workspace

        MOV R0,#7
        MOV R2,R12
        SWI "OS_Module"

        ; Zero private word so RISC OS doesn't free it again!!

        LD R0,PRIVATEWORD
        MOV R1,#0
        STR R1,[R0]

        ; ExitAndDie to end program and kill module; don't set return code
        ; as this was set by foreground task.

        MOV R0,#0
        MOV R1,#0
        MOV R2,#0
        ADR R3,TITLE
        SWI "OS_ExitAndDie"

        ; Just a standard service call handler.  Traps Service_Memory so we
        ; get to keep the memory that the pre-empted task will run in!

SERVICE
        ; Nothing to do here except veto attempts by the Wimp to remove all
        ; our memory - this is tried once at the start.

        TEQ R1,#&11 ; Service_Memory
        MOVNES PC,R14
        STMFD R13!,{R0,R12,R14}
        LDR R12,[R12]
        LD R0,MEMORYMAPVETOED
        TEQ R0,#0
        LDMNEFD R13!,{R0,R12,PC}^
        MOV R1,#0
        MOV R0,#1
        ST R0,MEMORYMAPVETOED
        LDMFD R13!,{R0,R12,PC}^

        ; Claim write character (so we redirect the program's output into
        ; the circular buffer) and open file (so we know what files the
        ; program has open, so we can close them again if it is killed).

CLAIMVECS
        MOV R0,#3 ; WrchV
        ADR R1,WRCHV
        MOV R2,R12
        SWI "OS_Claim"

        MOV R0,#&D ; FindV
        ADR R1,FINDV
        MOV R2,R12
        SWI "OS_Claim"

        MOVS PC,R14

RELEASEVECS
        MOV R0,#3 ; WrchV
        ADR R1,WRCHV
        MOV R2,R12
        SWI "OS_Release"

        MOV R0,#&D ; FindV
        ADR R1,FINDV
        MOV R2,R12
        SWI "OS_Release"

        MOVS PC,R14

        ; Schedule a descheduling event.

SETCALLAFTER
        MOV R0,#20
        ADR R1,INTERRUPT
        MOV R2,R12
        SWI "OS_CallAfter"
        MOVS PC,R14

CANCELTICKER
        STMFD R13!,{R0,R1,R14}
        ADR R0,INTERRUPT
        MOV R1,R12
        SWI "OS_RemoveTickerEvent"
        LDMFD R13!,{R0,R1,PC}^

WRCHV
        STMFD R13!,{R1-R2}
        LD R1,OUTBUFFERREAD
        LD R2,OUTBUFFERWRITE
        ADD R2,R2,#2
        BIC R2,R2,#&400
        TEQ R1,R2
        BLEQ CANCELTICKER
        BLEQ SETCALLBACK
        LD R1,OUTBUFFERWRITE
        ADR R2,OUTBUFFER
        STRB R0,[R1,R2]
        ADD R1,R1,#1
        BIC R1,R1,#&400
        ST R1,OUTBUFFERWRITE
        LDMFD R13!,{R1-R2}
        LDMFD R13!,{PC}^

FINDV
        LOCALS
        TEQ R0,#0 ; Closing a file
        BEQ A
        STMFD R13!,{R12}
        STMFD R13!,{PC}
        MOVS PC,R14
        MOV R0,R0 ; Our code is re-entered a word further down, so we waste
                  ; this word.
        LDMFD R13!,{R12}
        LDMVSFD R13!,{PC}
        STMFD R13!,{R2,R3}
        ADR R2,HANDLES
        LD R3,HANDLESUSED
        TEQ R3,#32
        MOVEQ R3,#31
        STR R0,[R2,R3,LSL #2]
        ADD R3,R3,#1
        ST R3,HANDLESUSED
        LDMFD R13!,{R2,R3}
        LDMFD R13!,{PC}^
A
        TEQ R1,#0 ; Closing all files
        BEQ B
        STMFD R13!,{R2,R8,R9,R14}
        LD R8,HANDLESUSED
        ADR R9,HANDLES
C
        TEQ R8,#0
        LDMEQFD R13!,{R2,R8,R9,PC}^
        LDR R2,[R9],#4
        SUB R8,R8,#1
        TEQ R1,R2
        BNE C
        MOV R2,#0
        STR R2,[R9,#-4]
        LDMFD R13!,{R2,R8,R9,PC}^
B
        STMFD R13!,{R0,R14}
        MOV R0,#0
        ST R0,HANDLESUSED
        LDMFD R13!,{R0,PC}^

INTERRUPT
        ; Remember that we must not corrupt R14svc, so we have to mess about
        ; with changing processor mode.

        STMFD R13!,{R0,R14}     ; R14irq goes onto irq stack.
        MOVS R0,PC              ; Preserve old processor mode.
        TEQP PC,#3
        MOV R0,R0
        STMFD R13!,{R14}
        BL SETCALLBACK          ; Use CallBack code.
        LDMFD R13!,{R14}
        TEQP R0,#0
        MOV R0,R0
        LDMFD R13!,{R0,PC}^

        ; The following is just standard CallBack code, it restores the
        ; handler address to its old value before returning to the main
        ; program loop.

SETCALLBACK
        STMFD R13!,{R0-R3,R14}
        MOV R0,#7               ; CallBack
        ADR R1,CALLBACKHANDLER
        MOV R2,R12
        ADR R3,REGBUF
        SWI "OS_ChangeEnvironment"
        ADR R14,OLDCALLBACK
        STMIA R14,{R1-R3}
        SWI "OS_SetCallBack"
        LDMFD R13!,{R0-R3,PC}^

CALLBACKHANDLER
        TEQP PC,#0
        MOV R0,R0
        MOV R0,#7
        ADR R1,OLDCALLBACK
        LDMIA R1,{R1-R3}
        SWI "OS_ChangeEnvironment"
        B MAINLOOP

        ; If we get an error reported, we write it into the circular buffer
        ; and kill the program.

ERRORHANDLER
        MOV R12,R0
        MOV R0,#2
        ST R0,RETURNCODE
        SWI "OS_NewLine"
        ADR R0,ERRORBUFFER+8
        SWI "OS_Write0"
        SWI "OS_NewLine"
        ADR R13,USRSTACK
        BL CANCELTICKER
        BL RELEASEVECS
        B KILLMODULE

        ; If the program exits, we tidy up.

EXITHANDLER
        ST R2,RETURNCODE
        ADR R13,USRSTACK
        BL CANCELTICKER
        BL RELEASEVECS
        B KILLMODULE

        ; These messages control the pre-empted task.  They allow the
        ; controlling process to stop and restart it, terminate it (which
        ; invokes the program's exit handler) and kill it (which will never
        ; reschedule it, and just closes all its files before shutting down).

RECEIVEMESSAGES
        LOCALS
        STMFD R13!,{R0,R14}
        LD R0,MESSAGES
        STMFD R13!,{R0}
        TST R0,#&80000000
        MOVEQ R0,#0
        ST R0,MESSAGES
        LDMFD R13!,{R0}
        TEQ R0,#1 ; Suspend
        BEQ A
        TEQ R0,#2 ; Restart
        BEQ B
        TEQ R0,#3 ; Terminate
        BEQ C
        TEQ R0,#4 ; Kill
        BEQ D
        LDMFD R13!,{R0,PC}^
A
        MOV R0,#1
        ST R0,SUSPENDED
        LDMFD R13!,{R0,PC}^
B
        MOV R0,#0
        ST R0,SUSPENDED
        LDMFD R13!,{R0,PC}^
C
        MOV R0,#1
        ST R0,TERMINATING
        LDMFD R13!,{R0,PC}^
D
        MOV R9,#0
        LD R10,HANDLESUSED
        ADR R11,HANDLES
E
        TEQ R10,#0
        BEQ F
        MOV R0,#0
        LDR R1,[R11],#4
        TEQ R1,#0
        ADDNE R9,R9,#1
        SWINE "OS_Find"
        SUB R10,R10,#1
        B E
F
        BL CLAIMVECS
        SWI "OS_WriteS"
        DB 13,10,10,"Killed",13,10,0
        ALIGN
        MOV R0,R9
        ADR R1,ERRORBUFFER
        MOV R2,#16
        SWI "OS_ConvertCardinal4"
        SWI "OS_Write0"
        SWI "OS_WriteS"
        DB " file(s) closed",13,10,0
        ALIGN
        BL RELEASEVECS
        MOV R0,#2
        ST R0,RETURNCODE
        B KILLMODULE

        DSECT
        ORG &10000000
WSTART


PRIVATEWORD     ALLOC 4
MESSAGES        ALLOC 4         ; Messages are written here by a controlling
                                ; task.
OUTBUFFERREAD   ALLOC 4
OUTBUFFERWRITE  ALLOC 4
OUTBUFFER       ALLOC 1024      ; The output circular buffer
TASKHANDLE      ALLOC 4
MEMORYMAPVETOED ALLOC 4
SUSPENDED       ALLOC 4
TERMINATING     ALLOC 4
HANDLESUSED     ALLOC 4
RETURNCODE      ALLOC 4
REGBUF          ALLOC 4*16
OLDCALLBACK     ALLOC 4*3
OLDERRORHANDLER ALLOC 4*3
OLDEXITHANDLER  ALLOC 4*3
HANDLES         ALLOC 4*32
POLLBLOCK       ALLOC 256
ERRORBUFFER     ALLOC 256+4+4

                ALLOC 64
USRSTACK


WEND
        DEND

        END

