REM >MillisecondTimerReassembly REM REM Unpicked from assembled module REM by Rick Murray 2012/01/26 REM REM I was bored and forget I had Chihayafuru to watch. ;-) REM REM NOTE: *UNTESTED* - written on a non-HAL emulator with an older REM version of RISC OS. The files "look" the same; sadly Acorn's REM diff doesn't cope with binary files so I haven't checked it REM for parity with the original... REM ON ERROR PRINT REPORT$+" at "+STR$(ERL/10) : END DIM code% 1024 FOR pass% = 4 TO 7 STEP 3 P% = 0 O% = code% [ OPT pass% ; using offset assembly as it is a module! .beginning EQUD 0 ; Start offset EQUD (init - beginning) ; Initialisation offset EQUD (fini - beginning) ; Finalisation offset EQUD 0 ; Service call handler offset EQUD (title - beginning) ; Title string offset EQUD (help - beginning) ; Help string offset EQUD (hlpcmd- beginning) ; Help/Command Keyword table offset EQUD &58100 ; SWI chunk base EQUD (swi - beginning) ; SWI handler code offset EQUD (switbl- beginning) ; SWI decoding table offset EQUD 0 ; SWI decoding code offset EQUD 0 ; Messages filename offset EQUD (flags - beginning) ; Flags word offset .flags EQUD 1 ; Bit zero set = 32 bit compliant ; ### TITLES AND SUCH ### .title EQUS "MillisecondTimer"+CHR$(0) ALIGN .help EQUS "MillisecondTimer Module"+CHR$(9)+"1.00 (08 Jun 2005) [32bit] tim@rowledge.org"+CHR$(0) ALIGN ; ### STAR COMMANDS ### .hlpcmd EQUS "MillisecondTimer"+CHR$(0) ; *command ALIGN EQUD 0 ; code offset EQUD 0 ; information word EQUD 0 ; syntax message offset (0=default) EQUD (cmdonehelp - beginning) ; help message offset EQUS "MillisecondTimerValue"+CHR$(0) ALIGN EQUD (cmdtwocode - beginning) ; code offset EQUD 0 ; information word EQUD (cmdtwosyntx- beginning) ; syntax message offset EQUD (cmdtwohelp - beginning) ; help message offset EQUD 0 ; end of list .cmdonehelp ; wall of text ;-) EQUS "Provides a SWI and *command to get a millisecond timer value."+CHR$(10)+CHR$(13) EQUS "SWI chunk base &58100."+CHR$(10)+CHR$(13)+CHR$(10)+CHR$(13) EQUS "SWI "+CHR$(34)+"MillisecondTimer_Value"+CHR$(34)+CHR$(9) ; continues... EQUS "Returns monotonic time in milliseconds "+CHR$(10)+CHR$(13) EQUS "SWI "+CHR$(34)+"MillisecondTimer_Address"+CHR$(34)+CHR$(9) ; continues... EQUS "Returns RMA address of the counter value "+CHR$(10)+CHR$(13) EQUS "*MillisecondTimerValue"+CHR$(9)+"Prints monotonic time to millisecond resolution." EQUS CHR$(10)+CHR$(13)+CHR$(10)+CHR$(13)+CHR$(0) ALIGN .cmdtwohelp ; it's a shame we don't point to the above text, to save duplication of essentially the ; same content... EQUS "*MillisecondTimerValue prints the system monotonic time to millisecond resolution."+CHR$(10)+CHR$(13) ; DO NOT ALTER THE ABOVE TEXT WITHOUT UNDERSTANDING THAT THIS DIRECTLY FOLLOWS ON (so must be word aligned!) .cmdtwosyntx EQUS "Syntax: *MillisecondTimerValue"+CHR$(10)+CHR$(13)+CHR$(0) ALIGN ; ### INITIALISATION CODE ### .init STMFD R13!, {R14} MOV R0, #6 ; ##TODO## POTENTIAL MEMORY LEAK ON RMREINIT, should check private word first! MOV R3, #12 SWI "XOS_Module" ; claim twelve bytes workspace in RMA BVS initdone STR R2, [R12, #0] ; store workspace pointer in private word MOV R12, R2 MOV R0, #0 STR R0, [R12, #0] ; this appears to be a dummy call - doesn't read anything, just checks the HALTimer_Limits SWI works MOV R0, #0 MVN R1, #0 ; -1 MVN R2, #0 ; -1 SWI &77100 ; SWI "XHALTimer_Limits" BVC initsetcallback MSR CPSR_f, #(1<<28) ; set V, clear others [side effect? doesn't this dump us into USR mode?] B initdone ; ##TODO## why not just "LDMFD R13!,{PC}" here and save the branch? .initsetcallback MOV R0, #0 ; Flags MOV R1, #1 ; Mantissa of interval SUB R2, R1, #4 ; Exponent of interval, is -3 so timer to be called every 10ms [##TODO## MVN immediate?] ADR R3, callbackcode ; Pointer to handler routine MOV R4, R12 ; R12 value to pass handler MOV R5, #0 ; Call infinite number of times SWI &77101 ; SWI "XHALTimer_AddCallBack" MSR CPSR_f, #0 ; clear flags [side effect? doesn't this dump us into USR mode?] .initdone LDMFD R13!, {PC} ; ### CALLBACK CODE ### .callbackcode ; seems to just increment a counter ;-) STMFD R13!, {R0, R14} LDR R0, [R12, #0] ADD R0, R0, #1 ; the ARM is a load/store architecture ;-) STR R0, [R12, #0] LDMFD R13!, {R0, PC} ; ### FINALISATION CODE ### .fini STMFD R13!, {R14} MOV R0, #0 ; Flags ADR R1, callbackcode ; Callback routine pointer LDR R2, [R12, #0] ; R12 value SWI &77102 ; SWI "XHALTimer_RemoveCallBack" MOV R0, #7 ; R2 previously set SWI "XOS_Module" ; Free claimed memory block LDMFD R13!, {PC} ; ### SWI TABLE ### .switbl EQUS "MillisecondTimer"+CHR$(0) EQUS "Value"+CHR$(0) ; MillisecondTimer_Value EQUS "Address"+CHR$(0) ; MillisecondTimer_Address EQUB 0 ; end of list ; doesn't need align, it is already and the BASIC I am using is ; not smart enough to do nothing if already aligned... ; ### SWI CODE ### .swi ; This is implemented as a standard jump table. ; However, as there are only two SWIs, I would have been inclined to just test ; for them as follows: ; CMP R11, #0 ; BEQ swi_value ; CMP R11, #1 ; BEQ swi_address ; ...fall through to unknown SWI code (instead of a jump) LDR R12, [R12, #0] ; load private block pointer into R12 CMP R11, #2 ; validate input ADDCC PC, PC, R11, LSL #2 ; the ARM rocks, eh? ;-) B swi_unknown B swi_value B swi_address .swi_unknown ADR R0, swi_errorblock MSR CPSR_f, #(1<<28) ; set V (and possibly revert to USR?) MOV PC, R14 ; return from SWI call .swi_errorblock EQUD &1E6 ; ##TODO## Use a REAL error number, this is "BadTime Invalid time interval" EQUS "Unknown MillisecondTimer module operation"+CHR$(0) ALIGN .swi_value STMFD R13!, {R14} ; ##TODO## If we're only loading a value, why are we stacking/unstacking R14? LDR R0, [R12, #0] LDMFD R13!, {PC} ; ##TODO## [pedantic] And why STM/LDM? "STR R14, [R13, #-4]!" then "LDR PC, [R13], #4" .swi_address STMFD R13!, {R14} ; ditto the above MOV R0, R12 LDMFD R13!, {PC} ; ### COMMAND CODE [for MillisecondTimerValue] ### .cmdtwocode STMFD R13!, {R14} LDR R12, [R12, #0] ; resolve workspace pointer BL swi_value BL printit LDMFD R13!, {PC} ; ##TODO## There is no conditional exit below, so why doesn't this just fall through after ; calling swi_value and then use the final LDMFD to do the exit? There seems to ; be a lot of extraneous jumping around here... ; Not to mention using load/store multiple to read/write single registers. .printit STMFD R13!, {R14} SUB R13, R13, #24 ; we're going to dump the result in the stack! MOV R11, R13 ; R0 already set (from swi_value call) MOV R1, R11 ; Pointer to block ##TODO## Why not MOV R1, R13 omitting R11? Am I missing something? MOV R2, #24 ; Size of block ##TODO## Why 24 bytes? ConvertCardinal4's maximum is 10+terminator SWI "XOS_ConvertCardinal4" ; "Cardinal4" means an unsigned number beween 0 and 4294967295. SWI "XOS_Write0" SWI "XOS_NewLine" ADD R13, R13, #24 ; fix up SP LDMFD R13!, {PC} ; FIN ] NEXT pass% OSCLI("%Save MillisecondTimerReassembly "+STR$~code%+" "+STR$~O%) OSCLI("%SetType MillisecondTimerReassembly FFA") END