home *** CD-ROM | disk | FTP | other *** search
- ; ----------------------------------------------------------------------
- ; Copyright Adam Goodfellow 1994, all rights reserved.
- ;
- ; Fast ARM code implementation of unix gettimeofday(2) function.
- ;
- ; C structures and function prototype:
- ;
- ; struct timeval {
- ; long tv_sec; /* seconds */
- ; long tv_usec; /* and microseconds */
- ; };
- ;
- ; struct timezone {
- ; int tz_minuteswest; /* minutes west of Greenwich */
- ; int tz_dsttime; /* type of dst correction */
- ; };
- ;
- ; int gettimeofday(struct timeval *tv, struct timezone *tz);
- ;
-
- IOC_Base EQU &3200000
- IOC_T0CountLow EQU &40
- IOC_T0CountHigh EQU &44
- IOC_T0LatchLow EQU &40
- IOC_T0LatchHigh EQU &44
- IOC_T0GoCommand EQU &48
- IOC_T0GetCount EQU &4C
-
- XOS_EnterOS EQU &020016
- XOS_Word EQU &020007
-
- r0 RN 0
- r1 RN 1
- r2 RN 2
- r3 RN 3
- r4 RN 4
- r5 RN 5
- r6 RN 6
- r7 RN 7
- r8 RN 8
- r9 RN 9
- r10 RN 10
- r11 RN 11
- r12 RN 12
- r13 RN 13
- r14 RN 14
- r15 RN 15
- sp RN 13
- lr RN 14
- pc RN 15
- psr RN 15
-
- i_flag EQU &08000000
- svc_mode EQU &00000003
-
- AREA |C$$code|,CODE,READONLY
-
- r_s RN 0 ;seconds
- r_u RN 1 ;micro seconds
- r_a RN 2 ;LS 32 bits of 40bit Risc OS time of day
- r_b RN 3 ;MS 8 bits of 40bit Risc OS time of day
- r_c RN 4 ;1/2 micro-secs read for IOC timer 0
- r_p RN 5 ;Temp reg }
- r_t RN 6 ;Temp reg } On of these is not needed
- r_v RN 7 ;Temp reg }
- r_tv RN 8 ;struct timeval *tv, or NULL
- r_tz RN 9 ;struct timezone *tz, or NULL
-
- ;Uses 8 bytes of stack for temp space
- ; 44 bytes for register storage.
- ; could be reduced to 28 by fiddling temp regs and not saving r0-r3
- ;
-
- ; -------------------------------------------------------------------------
- ; Read time of day and us from IOC timer 0
- ;
- ;On entry:
- ; R0 = pointer to buffer for timeval struct
- ; R1 = pointer to buffer for timezone struct
- ;
- EXPORT gettimeofday
- gettimeofday
- STMFD sp!, {r0-r9, lr}
-
- MOV r_tz, r1
- MOVS r_tv, r0 ;timeval wanted?
- BEQ tod_tz ;no - straight to timezone
-
- SUB sp, sp, #8
- MOV r1, sp ;Take SP here as wont be avilable later
-
- MOV r_p, psr ;Keep PSR from current processor mode
- SWI XOS_EnterOS
- TEQP psr, #(i_flag :OR: svc_mode) ;Disable IRQs, stay in SVC mode
-
- MOV r_a, #IOC_Base ;Read IOC timer 0 value
- STRB r_a, [r_a, #IOC_T0GetCount]
- LDRB r_b, [r_a, #IOC_T0LatchLow]
- LDRB r_c, [r_a, #IOC_T0LatchHigh]
- ORR r_b, r_b, r_c, LSL #8
- MOV r_a, #&4e00 ;20000-n
- ORR r_a, r_a, #&20
- SUB r_b, r_a, r_b
-
- MOV r0, #3 ;Get real time while IRQs still
- STRB r0, [r1, #0] ;disabled to stop it ticking over
- MOV r0, #14 ;Returns 40 bit binary
- SWI XOS_Word
-
- TEQP psr, r_p ;Back to processor mode and IRQ state on entry
- MOV r_p, r_p
-
- LDMIA sp!, {r_a, r_b} ;t[4]...t[0] are bytes in r_a and r_b
- AND r_b, r_b, #&ff ;Just want one byte from r_b
-
- ; r_a, r_b = Real time, r_c counter time (micro secs)
- ; ----------
- LDR r_v, =&6e996a00 ;To Unix and C time range
- SUBS r_a, r_a, r_v ;5 byte time - 0x336e996a00
- SBC r_b, r_b, #&33 ;Risc OS time of 1st Jan 1970
-
- ; ----------
- ; r_t is a temp, r_v is a temp, r_u is usecs, r_s is secs
-
- ; The following is a 40 bit div/mod by 100
-
- MOV r_s, #0 ;tv->tv_sec = 0
- MOV r_u, #0 ;tv->tv_usec = 0
-
- MOV r_t, r_b ;t[4]
- ORR r_u, r_t, r_u, LSL #8 ;tv->tv_usec = (tv->tv_usec<<8) | t[4];
- CMP r_u, #100 ;if (tv->tv_usec>=100)
- BLGE divmod100 ; tv->tv_usec = tv->tv_usec % 100;
- ORRGE r_s, r_t, r_s, LSL #8 ; tv->tv_sec = (tv->tv_sec<<8) | (tv->tv_usec/100);
-
- MOV r_t, r_a, LSR #24 ;t[3]
- ORR r_u, r_t, r_u, LSL #8 ;tv->tv_usec = (tv->tv_usec<<8) | t[4];
- CMP r_u, #100 ;if (tv->tv_usec>=100)
- BLGE divmod100 ; tv->tv_usec = tv->tv_usec % 100;
- ORRGE r_s, r_t, r_s, LSL #8 ; tv->tv_sec = (tv->tv_sec<<8) | (tv->tv_usec/100);
-
- MOV r_t, r_a, LSR #16
- AND r_t, r_t, #&ff ;t[2]
- ORR r_u, r_t, r_u, LSL #8 ;tv->tv_usec = (tv->tv_usec<<8) | t[4];
- CMP r_u, #100 ;if (tv->tv_usec>=100)
- BLGE divmod100 ; tv->tv_usec = tv->tv_usec % 100;
- ORRGE r_s, r_t, r_s, LSL #8 ; tv->tv_sec = (tv->tv_sec<<8) | (tv->tv_usec/100);
-
- MOV r_t, r_a, LSR #8
- AND r_t, r_t, #&ff ;t[1]
- ORR r_u, r_t, r_u, LSL #8 ;tv->tv_usec = (tv->tv_usec<<8) | t[4];
- CMP r_u, #100 ;if (tv->tv_usec>=100)
- BLGE divmod100 ; tv->tv_usec = tv->tv_usec % 100;
- ORRGE r_s, r_t, r_s, LSL #8 ; tv->tv_sec = (tv->tv_sec<<8) | (tv->tv_usec/100);
-
- MOV r_t, r_a
- AND r_t, r_t, #&ff ;t[0]
- ORR r_u, r_t, r_u, LSL #8 ;tv->tv_usec = (tv->tv_usec<<8) | t[4];
- CMP r_u, #100 ;if (tv->tv_usec>=100)
- BLGE divmod100 ; tv->tv_usec = tv->tv_usec % 100;
- ORRGE r_s, r_t, r_s, LSL #8 ; tv->tv_sec = (tv->tv_sec<<8) | (tv->tv_usec/100);
-
- RSB r_t, r_u, r_u, LSL #7 ;cs x 10000 to get usecs
- SUB r_u, r_t, r_u, LSL #1
- ADD r_u, r_u, r_u, LSL #2
- MOV r_u, r_u, LSL #4
-
- ADD r_u, r_u, r_c, LSR #1 ;Add in usecs/2 from IOC Timer
-
- STMIA r_tv, {r_s, r_u} ;Write to timeval struct
-
- tod_tz TEQ r_tz, #0 ;Optionally write time zone (zero for now)
- MOVNE r_s, #0
- MOVNE r_u, #0
- STMNEIA r_tz, {r_s, r_u}
-
- LDMFD sp!, {r0-r9, pc}^
-
- ; ----------
- ; Highly optimised 16 bit div/mod 100
- ; could inline this with conditionals to give constant execution time.
- divmod100
- MOV r_v, r_u, LSR #1 ;Divide by 100 to get seconds
- RSB r_v, r_v, r_v, LSL #3
- RSB r_v, r_v, r_v, LSL #3
- RSB r_t, r_v, r_v, LSL #7
- ADD r_v, r_v, r_v, LSL #2
- SUB r_v, r_t, r_v, LSL #2
- MOV r_t, r_v, LSR #18 ;result in r_t
-
- ADD r_v, r_t, r_t, LSL #2 ;Remainder of division (0-99)
- ADD r_v, r_v, r_v, LSL #2
- SUB r_u, r_u, r_v, LSL #2 ;remainder back in r_u
-
- MOVS pc, lr
-
- END
-