Example 7
DiscAccess

(and DiscAccess Revisited)

 

 

Introduction

When you are using a machine that was never designed to have a harddisc with a harddisc fitted, ie an A3000, then something that is monumentally annoying is the lack of a harddisc light.
You see, the HD light on an A5000, RiscPC etc is a very useful thing. I wired my Simtec IDE interface to hijack the HD light along with the internal disc.
Because the blinking is VERY reassuring. Also, you can judge if anything has gone wrong by how it blinks, or what is happening.
Sorta, <blink><blink>, ah yeah - the autosave just kicked in...

 

But when you are using a machine with no indicator, particularly a machine slower than you are used to, you start to get worried when nothing appears to be happening.

 

So for the purposes of this tutorial, we shall create a blinking LED light.

 

The indicator

Our first task is to determine what we should do. An on-screen icon? Excuse my American, but that sucks, as well as limiting us to desktop use. It would be nice to steal the floppy light, but that appears to be rather tricky.
So that leaves us with custom hardware or the keyboard LEDs.
So you'll need four BC108 transistors and a piece of veroboard...

...only joking. The sane people among us would see that the least used keyboard LED is Scroll Lock. Why can't we just use that?

Why not indeed.

 

The disc activity

The next thing to consider is how to get information of disc activity from RISC OS. This is a trivial matter, as RISC OS bends over backwards to allow you access to it's innards. A quick eyeball of the PRMs shows us:
FileV, ArgsV, BGetV, BPutV, GBPBV, and FindV.

So we have six vectors which should turn our keyboard LED on.

 

Our plan of attack

Let's think about how we're going to do this.

Firstly, we hook some code up to all of the vectors. It can be the exact same code. Dump registers that must be preserved, then switch on the LED, restore registers, and return without claiming the vector.

So our LED is on. Good-oh. What we NOW need is a way to turn it off. We cannot guarantee any specific number of calls to the LED switcher oner, so we'll get more complicated.

We shall arrange for a callback to kick in after 20cs to turn off the LED.
When the LED is turned on, the callback will be set up. If the callback is ALREADY set up, it will have been unset. When the callback is called, it will be unset.

So it goes something like:

  Vector handler:
    Preserve registers
    Is callback set up?
      Yes? Remove it.
    Switch on Scroll Lock LED
    Set up callback handler
    Restore registers

  Callback handler
    Switch off Scroll Lock LED
    Remove callback

 

Finally, some code

Obviously, this will be implemented as a module. So let's go with a little bit of code:
    EQUD    0                      ; Start-up code
    EQUD    initialise             ; Initialisation
    EQUD    finalise               ; Finalisation
    EQUD    0                      ; Service call handler
    EQUD    module_title           ; Module title
    EQUD    module_help            ; Module help
    EQUD    0                      ; Help and command decoding table
    EQUD    0                      ; SWI chunk base number
    EQUD    0                      ; SWI handling code
    EQUD    0                      ; SWI decoding code
    EQUD    0                      ; SWI decoding code


  .module_title
    EQUS    "DiscAccess"
    EQUB    0
    ALIGN


  .module_help
    EQUS    "DiscAccess"+CHR$(9)+"0.01 (14 Oct 2000)"
    EQUB    0
    ALIGN


  .initialise
    STMFD   R13!, {R14}

    \ Attach vector handler to the six vectors that deal with file ops.
    MOV     R0, #&8                ; FileV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_AddToVector"

    MOV     R0, #&9                ; ArgsV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_AddToVector"

    MOV     R0, #&A                ; BGetV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_AddToVector"

    MOV     R0, #&B                ; BPutV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_AddToVector"

    MOV     R0, #&C                ; GBPBV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_AddToVector"

    MOV     R0, #&D                ; FindV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_AddToVector"

    LDMFD   R13!, {PC}


  .finalise
    STMFD   R13!, {R14}

    MOV     R0, #&8                ; FileV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_Release"

    MOV     R0, #&9                ; ArgsV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_Release"

    MOV     R0, #&A                ; BGetV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_Release"

    MOV     R0, #&B                ; BPutV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_Release"

    MOV     R0, #&C                ; GBPBV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_Release"

    MOV     R0, #&D                ; FindV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_Release"

    LDMFD   R13!, {PC}


  .vector_handler
    \ IMPORTANT! WE ARE IN SVC MODE!
    STMFD   R13!, {R0 - R2, R14}   ; Preserve registers

    \ Our code will go here

    LDMFD   R13!, {R0 - R2, R14}   ; Restore registers
    MOVS    PC, R14                ; Pass this one on
If you run this, you'll notice that nothing happens. This is good. This is what is supposed to happen.
If you have a utility that displays vector claimants, you will see something similar to:
    Vector FileV (&08)
      01863af0 00000100 DiscAccess
      018287f0 00000000 Filer+Util
      038310cc 01800204 FileSwitch
      0380a5b8 00000000 
    Vector ArgsV (&09)
      01863af0 00000100 DiscAccess
      03832b98 01800204 FileSwitch
      0380a5b8 00000000 
    Vector BGetV (&0a)
      01863af0 00000100 DiscAccess
      03831c04 01800204 FileSwitch
      0380a5b8 00000000 
    Vector BPutV (&0b)
      01863af0 00000100 DiscAccess
      03831d94 01800204 FileSwitch
      0380a5b8 00000000 
    Vector GBPBV (&0c)
      01863af0 00000100 DiscAccess
      0182882c 00000000 Filer+Util
      03831f6c 01800204 FileSwitch
      0380a5b8 00000000 
    Vector FindV (&0d)
      01863af0 00000100 DiscAccess
      038317bc 01800204 FileSwitch
      0380a5b8 00000000 
which clearly shows that we are hanging on to the file vectors.

Please notice two things. Firstly, we use "OS_AddToVector" so that we add ourselves to the vector list, rather than claiming the vector (which could cause problems with other software!).
Also note that the "MOVS PC,R14" is not 32 bit compliant, however it is the official (documented) way to return from vector code when passing on.

 

Fleshing it out

Our next task is to add code to the section marked "\ Our code will go here" to invert the status of the Scroll Lock key, whatever it is currently.
    \ Read
    MOV     R0, #202               ; Update keyboard status, but using EOR
    MOV     R1, #0                 ; mask 0 and AND mask 255, we can read
    MOV     R2, #255               ; the state...
    SWI     "OS_Byte"              ; New value in R1

    \ Invert
    EOR     R1, R1, #2             ; Bit 1 is Scroll Lock, invert it.

    \ Write
    MOV     R2, #0                 ; AND mask is 0, so EOR value is used.
    SWI     "OS_Byte"

    \ Update keyboard LEDs
    MOV     R0, #118
    SWI     "OS_Byte"
Now assemble the module, and try loading something fairly complicated. Not necessarily large, but something that does a bunch of file accessing, like a word processor or email client.
See the Scroll Lock indicator blinking away? Yeah, that is MUCH more reassuring isn't it?

In case you are unsure, there should be a "MOV R0, #202" following the "\ Write" marker. However this is unnecessary as OS_Byte preserves R0, and it is not altered between then and when we need it again.

 

Callbacks

The next step is to seperate the switching off from the switching on. The way we shall do this is to set up a callback.

First, change:

    \ Invert
    EOR     R1, R1, #2             ; Bit 1 is Scroll Lock, invert it.
to:
    \ Switch on Scroll Lock
    ORR     R1, R1, #2             ; Bit 1 is Scroll Lock, set it.
Now after the "\ Update keyboard LEDs" code, add:
    \ Set up timed callback to switch LED off again
    MOV    R0, #20                 ; After 20 centiseconds
    ADR    R1, callback_handler
    MOV    R2, #0
    SWI    "OS_CallAfter"
Then at the bottom add:
  .callback_handler
    \ This isn't quite interrupt-level code, but getting close!

    STMFD   R13!, {R0 - R2, R14}

    \ Unset the keyboard LED
    \ Read
    MOV     R0, #202
    MOV     R1, #0
    MOV     R2, #255
    SWI     "OS_Byte"

    \ Switch off Scroll Lock
    AND     R1, R1, #253           ; 253 preserves every bit EXCEPT bit 2.

    \ Write
    MOV     R2, #0
    SWI     "OS_Byte"

    \ Update LEDs
    MOV     R0, #118
    SWI     "OS_Byte"

    \ Remove callback
    ADR     R0, callback_handler
    MOV     R1, #0
    SWI     "OS_RemoveTickerEvent"

    \ Return
    LDMFD   R13!, {R0 - R2, PC}
Assemble and run this.
Something is wrong, isn't it?

Go to the part where the timed callback is set up, and try for a shorter delay. Drastically shorter, like 2cs.

 

To wrap up...

Well, I did intend to check if a callback was pending in the vector handler, but now I don't think I'll bother as this module now does exactly what I want.

All in an hour's work! :-)

 

Please be aware that this utility, logically, will add a delay to file operations. I have measured the delay to be something in the region of 28%. I tried some optimisations such as:

    LDMFD  R13!, {R0-R2, PC}^
rather than:
    LDMFD  R13!, {R0-R2, R14}
    MOVS   PC, R14
but it made a negligible difference.
This is really a case of deciding if the delay introduced outweighs the benefit of having a blinking HD light. For me, it does. For you? Your choice...
The LDMFD R13!, { ... }^ is not 32-bit friendly.

 

Full listing of the module

This is a full listing of the source required to create this module. It includes some additional help... Oh, and it isn't 32-bit friendly.

You will need to set <DiscAcc$Dir>, or simply edit the code...

REM >SourceCode
REM
REM DiscAccess module source
REM
REM Version 0.01
REM
REM Assembler programming example 7
REM Downloaded from: http://www.heyrick.co.uk/assembler/
:
ON ERROR PRINT REPORT$+" at "+STR$(ERL/10) : END
:
DIM code% 2048
:
FOR pass% = 4 TO 6 STEP 2
P%=0
O%=code%
[ OPT pass%
    EQUD    0                      ; Start-up code
    EQUD    initialise             ; Initialisation
    EQUD    finalise               ; Finalisation
    EQUD    0                      ; Service call handler
    EQUD    module_title           ; Module title
    EQUD    module_help            ; Module help
    EQUD    help_table             ; Help and command decoding table
    EQUD    0                      ; SWI chunk base number
    EQUD    0                      ; SWI handling code
    EQUD    0                      ; SWI decoding code
    EQUD    0                      ; SWI decoding code


  .module_title
    EQUS    "DiscAccess"
    EQUB    0
    ALIGN


  .module_help
    EQUS    "DiscAccess"+CHR$(9)+"0.01 (14 Oct 2000)"
    EQUB    0
    ALIGN


  .help_table
    EQUS    "DiscAccess"           ; Keyword string
    EQUB    0
    ALIGN
    EQUD    0                      ; Pointer to code (there is no code!)
    EQUD    0                      ; Parameter information (no parameters)
    EQUD    0                      ; Pointer to syntax string
    EQUD    discaccess_help        ; Pointer to help string
    EQUD    0                      ; End of command table


  .discaccess_help
    ; Only put a linefeed where one is required, else put a trailing space.
    ; RISC OS will wrap the text as appropriate to fit the dimensions in use...
    EQUS    "DiscAccess is a simple little module that blinks your Scroll Lock "
    EQUS    "key when files are accessed."+CHR$13
    EQUS    "It is designed to take the place of a dedicated HD indicator on "
    EQUS    "machines that don't have such a thing (ie, the A3000); so you "
    EQUS    "probably won't need it on a RiscPC!"+CHR$13+CHR$13
    EQUS    "DiscAccess was written by Richard Murray as a tutorial for the "
    EQUS    CHR$34+"Teach yourself ARM assembler"+CHR$34+", which is freely "
    EQUS    "available at:"+CHR$13
    EQUS    CHR$160+CHR$160+"http://www.heyrick.co.uk/assembler/"+CHR$13
    EQUS    CHR$160+CHR$160+"(this is example seven!)"+CHR$13+CHR$13
    EQUB    0
    ALIGN
    ; The hard spaces (CHR$160) force a small indent.


  .initialise
    STMFD   R13!, {R14}

    \ Attach vector handler to the six vectors that deal with file ops.
    MOV     R0, #&8                ; FileV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_AddToVector"

    MOV     R0, #&9                ; ArgsV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_AddToVector"

    MOV     R0, #&A                ; BGetV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_AddToVector"

    MOV     R0, #&B                ; BPutV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_AddToVector"

    MOV     R0, #&C                ; GBPBV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_AddToVector"

    MOV     R0, #&D                ; FindV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_AddToVector"

    LDMFD   R13!, {PC}


  .finalise
    STMFD   R13!, {R14}

    MOV     R0, #&8                ; FileV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_Release"

    MOV     R0, #&9                ; ArgsV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_Release"

    MOV     R0, #&A                ; BGetV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_Release"

    MOV     R0, #&B                ; BPutV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_Release"

    MOV     R0, #&C                ; GBPBV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_Release"

    MOV     R0, #&D                ; FindV
    ADR     R1, vector_handler
    MOV     R2, #0
    SWI     "OS_Release"

    LDMFD   R13!, {PC}


  .vector_handler
    \ IMPORTANT! WE ARE IN SVC MODE!
    STMFD   R13!, {R0 - R2, R14}   ; Preserve registers

    \ Read
    MOV     R0, #202               ; Update keyboard status, but using EOR
    MOV     R1, #0                 ; mask 0 and AND mask 255, we can read
    MOV     R2, #255               ; the state...
    SWI     "OS_Byte"              ; New value in R1

    \ Switch on Scroll Lock
    ORR     R1, R1, #2             ; Bit 1 is Scroll Lock, set it.

    \ Write
    MOV     R2, #0                 ; AND mask is 0, so EOR value is used.
    SWI     "OS_Byte"

    \ Update keyboard LEDs
    MOV     R0, #118
    SWI     "OS_Byte"

    \ Set up timed callback to switch LED off again
    MOV    R0, #2                  ; After 2 centiseconds
    ADR    R1, callback_handler
    MOV    R2, #0
    SWI    "OS_CallAfter"

    LDMFD   R13!, {R0 - R2, R14}   ; Restore registers
    MOVS    PC, R14                ; Pass this one on


  .callback_handler
    \ This isn't quite interrupt-level code, but getting close!

    STMFD   R13!, {R0 - R2, R14}

    \ Unset the keyboard LED
    \ Read
    MOV     R0, #202
    MOV     R1, #0
    MOV     R2, #255
    SWI     "OS_Byte"

    \ Switch off Scroll Lock
    AND     R1, R1, #253           ; 253 preserves every bit EXCEPT bit 2.

    \ Write
    MOV     R2, #0
    SWI     "OS_Byte"

    \ Update LEDs
    MOV     R0, #118
    SWI     "OS_Byte"

    \ Remove callback
    ADR     R0, callback_handler
    MOV     R1, #0
    SWI     "OS_RemoveTickerEvent"

    \ Return
    LDMFD   R13!, {R0 - R2, PC}


  .stuff_at_the_end
    EQUB    10
    EQUB    10
    EQUS    "DiscAccess module © 2000 Richard Murray"
    EQUB    10
    EQUS    "http://www.heyrick.co.uk/assembler/  (example 7)"
    EQUB    10
]
NEXT pass%
:
OSCLI("Save <DiscAcc$Dir>.DiscAccess "+STR$~code%+" +"+STR$~P%)
OSCLI("SetType <DiscAcc$Dir>.DiscAccess FFA")
:
END

Download example source: da001src.basic


 

 

DiscAccess Revisited

There is a difference between knowing the optimisations, and coding the optimisations...
The DiscAccess module presented is not as optimal as it could be. It was written with the view that you, the reader, optimise it yourself.

Now that it has been a good year since it was first posted, I shall here 'revisit' the project and detail some of the optimisations made.

It was considered to bump the callback delay up to ten centiseconds (instead of two) so the keyboard got less of a clobbering. In fact, given the new code, there should be a lot less data passing to the keyboard now. The LED will no longer flicker like crazy on a debatch (like the harddisc LED does), but instead it'll 'pulse' several times a second.

Don't just take my word for it:

Little BASIC program to block-read a news file of 5,163,098 bytes.

                 4096 byte blocks      1024 byte block

No DiscAccess     3.93 seconds          7.41 seconds

DiscAccess v0.01  4.85 seconds         11.31 seconds

DiscAccess v0.10  3.99 seconds          7.53 seconds

 

You will need to set <DiscAcc$Dir>, or simply edit the code...

Version 0.10 source

REM >SourceCode
REM
REM DiscAccess module source [revisited]
REM
REM Version 0.10  28th February 2002
REM
REM Extended from assembler programming example 7
REM Downloaded from: http://www.heyrick.co.uk/assembler/
REM
:
ON ERROR PRINT REPORT$+" at "+STR$(ERL/10) : END
:
DIM code% 2048
:
FOR pass% = 4 TO 6 STEP 2
P%=0
O%=code%
[   OPT pass%
    EQUD    0                                      ; Start-up code
    EQUD    initialise                             ; Initialisation
    EQUD    finalise                               ; Finalisation
    EQUD    0                                      ; Service call handler
    EQUD    module_title                           ; Module title
    EQUD    module_help                            ; Module help
    EQUD    help_table                             ; Help and command decoding table
    EQUD    0                                      ; SWI chunk base number
    EQUD    0                                      ; SWI handling code
    EQUD    0                                      ; SWI decoding code
    EQUD    0                                      ; SWI decoding code


  .module_title
    EQUS    "DiscAccess"
    EQUB    0
    ALIGN


  .module_help
    EQUS    "DiscAccess"+CHR$(9)+"0.10 (28 Feb 2002) [http://www.heyrick.co.uk/assembler/]"
    EQUB    0
    ALIGN


  .help_table
    EQUS    "DiscAccess"                           ; Keyword string
    EQUB    0
    ALIGN
    EQUD    0                                      ; Pointer to code (there is no code!)
    EQUD    0                                      ; Parameter information (no parameters)
    EQUD    0                                      ; Pointer to syntax string
    EQUD    discaccess_help                        ; Pointer to help string
    EQUD    0                                      ; End of command table


  .initialise
    STR     R14, [R13, #-4]!

    \ Set up the OS version
    BL      get_os_version
    \ Use "MOV     R2, R0" instead of the BL if you don't want hacky. <g>

    \ Attach vector handler to the six vectors that deal with file ops.
    \ Using OS_Claim now, instead of OS_AddToVector.

    \ Constants...
    ADR     R1, vector_handler
    ADR     R0, release_code
    STR     R2, [R0]

    MOV     R0, #&8                                ; FileV
    SWI     "OS_Claim"
    BVS     release_eight

    MOV     R0, #&9                                ; ArgsV
    SWI     "OS_Claim"
    BVS     release_nine

    MOV     R0, #&A                                ; BGetV
    SWI     "OS_Claim"
    BVS     release_ten

    MOV     R0, #&B                                ; BPutV
    SWI     "OS_Claim"
    BVS     release_eleven

    MOV     R0, #&C                                ; GBPBV
    SWI     "OS_Claim"
    BVS     release_twelve

    MOV     R0, #&D                                ; FindV
    SWI     "OS_Claim"
    BVS     release_thirteen

    LDMFD   R13!, {PC}


  .vector_handler
    \ IMPORTANT! WE ARE IN SVC MODE!

    \ MAKE THIS *FAST* - NO BRANCHES (AT ALL) FOR
    \                    FASTEST VARIANT...

    STMFD   R13!, {R0 - R2, R14}                   ; Preserve registers

    \ Check LED state - if LED currently on, skip all this...
    MOV     R0, #1
    LDR     R0, led_state
    CMP     R0, #1
    BEQ     vector_handler_exit

    CMP     R12, #0
    B       update_old_method

    ; Set Scroll Lock bit
    LDRB    R0, [R12, #0]
    ORR     R0, R0, #2                             ; Bit 1 is Scroll Lock, set it.
    STRB    R0, [R12, #0]

    ; Force keyboard status update
    MOV     R0, #118
    SWI     "OS_Byte"

    ; Flag LEDs are on
    MOV     R0, #1
    STR     R0, led_state

    ; Exit point here, so we can lose one branch.
    LDMFD   R13!, {R0 - R2, R14}
    MOVS    PC, R14                                ; Got to keep V and C flags.


  .update_old_method
    \ Read
    MOV     R0, #202                               ; Update keyboard status, but using EOR
    MOV     R1, #0                                 ; mask 0 and AND mask 255, we can read
    MOV     R2, #255                               ; the state...
    SWI     "OS_Byte"                              ; New value in R1

    \ Switch on Scroll Lock
    ORR     R1, R1, #2                             ; Bit 1 is Scroll Lock, set it.

    \ Write
    MOV     R2, #0                                 ; AND mask is 0, so EOR value is used.
    SWI     "OS_Byte"

    \ Update keyboard LEDs
    MOV     R0, #118
    SWI     "OS_Byte"

    \ Flag LEDs are on
    MOV     R0, #1
    STR     R0, led_state

    \ Set up timed callback to switch LED off again
    MOV    R0, #10                                  ; After 10 centiseconds
    ADR    R1, callback_handler
    MOV    R2, #0
    SWI    "OS_CallAfter"

  .vector_handler_exit
    LDMFD   R13!, {R0 - R2, R14}                   ; Restore registers
    MOVS    PC, R14                                ; Pass this one on (preserving V and C)


  .callback_handler
    STMFD   R13!, {R0 - R2, R14}

    \ Unset the keyboard LED
    \ Read
    MOV     R0, #202
    MOV     R1, #0
    MOV     R2, #255
    SWI     "OS_Byte"

    \ Switch off Scroll Lock
    AND     R1, R1, #253                           ; 253 preserves every bit EXCEPT bit 2.

    \ Write
    MOV     R2, #0
    SWI     "OS_Byte"

    \ Update LEDs
    MOV     R0, #118
    SWI     "OS_Byte"

    \ Flag LEDs are off
    MOV     R0, #0
    ADR     R1, led_state
    STR     R0, [R1]

    \ Remove callback
    ADR     R0, callback_handler
    MOV     R1, #0
    SWI     "OS_RemoveTickerEvent"

    \ Return
    LDMFD   R13!, {R0 - R2, PC}


  .finalise
    STR     R14, [R13, #-4]!

    ADR     R1, vector_handler
    ADR     R0, release_code
    LDR     R2, [R0]

    MOV     R0, #&8                                ; FileV
    ADR     R1, vector_handler
    SWI     "OS_Release"
    BVS     frelease_failed

    MOV     R0, #&9                                ; ArgsV
    SWI     "OS_Release"
    BVS     frelease_failed

    MOV     R0, #&A                                ; BGetV
    SWI     "OS_Release"
    BVS     frelease_failed

    MOV     R0, #&B                                ; BPutV
    SWI     "OS_Release"
    BVS     frelease_failed

    MOV     R0, #&C                                ; GBPBV
    SWI     "OS_Release"
    BVS     frelease_failed

    MOV     R0, #&D                                ; FindV
    SWI     "OS_Release"
    BVS     frelease_failed

    LDR     PC, [R13], #4


  .get_os_version
    STR     R14, [R13, #-4]!

    MOV     R0, #129
    MOV     R1, #0
    MOV     R2, #255
    SWI     "OS_Byte"
    MOVVS   R0, #0                                 ; Can this OS_Byte fail?

    CMP     R0, #&A4
    B       status_address
    CMP     R0, #&A5
    B       status_address
    CMP     R0, #&A6
    B       status_address
    CMP     R0, #&A7
    B       status_address
    CMP     R0, #&A8
    B       status_address                         ; And RISC OS 4.00 IIRC

    MOV     R2, #0                                 ; OS version not known
    LDR     PC, [R13], #4


  .status_address
    MOV     R2, #&900                              ; Keyboard status byte is &9C4, byte 0.
    ADD     R2, R2, #&C4
    LDR     PC, [R13], #4


  .led_state
    EQUD    0

  .release_code
    EQUD    0


    \ Error release
  .release_thirteen
    MOV     R0, #&C
    SWI     "OS_Release"
    BVS     release_failed

  .release_twelve
    MOV     R0, #&B
    SWI     "OS_Release"
    BVS     release_failed

  .release_eleven
    MOV     R0, #&A
    SWI     "OS_Release"
    BVS     release_failed

  .release_ten
    MOV     R0, #&9
    SWI     "OS_Release"
    BVS     release_failed

  .release_nine
    MOV     R0, #&8
    SWI     "OS_Release"
    BVS     release_failed

  .release_eight
    ADR     R0, claim_fail_message
    LDR     R14, [R13], #4
    ORRS    PC, R14, #1<<28

  .release_failed
    ADR     R0, release_fail_message
    LDR     R14, [R13], #4
    ORRS    PC, R14, #1<<28

  .frelease_failed
    ADR     R0, frelease_fail_message
    LDR     R14, [R13], #4
    ORRS    PC, R14, #1<<28


  .claim_fail_message
    EQUD    17
    EQUS    "Unable to claim required vectors, cannot start." + CHR$(0)
    ALIGN


  .release_fail_message
    EQUD    17
    EQUS    "Unable to claim required vectors, and unable to release those claimed. Uh-oh!"
    EQUB    0
    ALIGN

  .frelease_fail_message
    EQUD    17
    EQUS    "Unable to release claimed vectors. Uh-oh!"
    EQUB    0
    ALIGN

  .discaccess_help
    ; Only put a linefeed where one is required, else put a trailing space.
    ; RISC OS will wrap the text as appropriate to fit the screen dimensions in use...
    EQUS    "DiscAccess is a simple little module that blinks your Scroll Lock key when "
    EQUS    "files are accessed."+CHR$13
    EQUS    "It is designed to take the place of a dedicated HD indicator on machines that "
    EQUS    "don't have such a thing (ie, the A3000); so you probably won't need it on a "
    EQUS    "RiscPC!"+CHR$13+CHR$13
    EQUS    "DiscAccess was written by Richard Murray as a tutorial for the "+CHR$34+"Teach "
    EQUS    "yourself ARM assembler"+CHR$34+", which is freely available at:"+CHR$13
    EQUS    CHR$160+CHR$160+"http://www.heyrick.co.uk/assembler/"+CHR$13
    EQUS    CHR$160+CHR$160+"(this is the extended version of example seven!)"+CHR$13+CHR$13
    EQUB    0
    ALIGN
    ; The hard spaces (CHR$160) force a small indent.


  .stuff_at_the_end
    EQUB    10
    EQUB    10
    EQUS    "DiscAccess module © 2002 Richard Murray"
    EQUB    10
    EQUS    "http://www.heyrick.co.uk/assembler/  (example 7)"
    EQUB    10
]
NEXT pass%
:
OSCLI("Save <DiscAcc$Dir>.DiscAccess "+STR$~code%+" +"+STR$~P%)
OSCLI("SetType <DiscAcc$Dir>.DiscAccess FFA")
:
END

Download example source: da010src.basic

 

Further optimisations?

This are for you to do...
...if LED is on when entered, reschedule the callback? This might have a negative performance hit though, you'll need to weigh up bashing the keyboard LED with calling some potentially lengthy SWIs in 'fast' code.
You could set a timer flag in the vector handler, and then check it in the callback code to see if sufficient time has passed. If not, keep the LED on and reschedule the callback?

 

Regular blinking? (and other notes)

If your Scroll Lock blinks every few seconds - check your CD-ROM drive's LED. You'll probably find they co-incide. Are you using Jeremy Poulter's CDViewer (or something similar?). Somehow CDViewer checks every so often (as set by you) the status of the CD-ROM drive. This blinks both the CD-ROM drive and the Scroll Lock (as DiscAccess picks it up).

You might exhibit behaviour when no harddiscs (or floppies) are being accessed. This is because DiscAccess works at a high level so picks up on filing system activity (rather than disc activity). To see what I mean, open a TaskWindow and type:

   Filer_OpenDir Resources:$.Resources
and watch the Scroll Lock flicker as information is read from a pseudo-filing system containing a bunch of stuff held in ROM. :-)

 


Return to assembler index
Copyright © 2002 Richard Murray