Example 7
|
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.
...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.
FileV
, ArgsV
, BGetV
, BPutV
,
GBPBV
, and FindV
.
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
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 onIf you run this, you'll notice that nothing happens. This is good. This is what is supposed to happen.
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 00000000which 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 way to return from vector code when passing on.
\ 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.
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.
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.
Go to the part where the timed callback is set up, and try for a shorter delay. Drastically shorter, like 2cs.
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, R14but it made a negligible difference.
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