############################################################################## # # # IAR ARM ANSI C/C++ Compiler V4.42A/W32 EVALUATION 03/Apr/2008 22:30:04 # # Copyright 1999-2005 IAR Systems. All rights reserved. # # # # Cpu mode = interwork # # Endian = little # # Stack alignment = 4 # # Source file = C:\ARM\Write flasher Rus\main.c # # Command line = "C:\ARM\Write flasher Rus\main.c" -D NEWSGOLD -D # # ELKA -lC "C:\ARM\Write flasher Rus\Release\List\" # # -o "C:\ARM\Write flasher Rus\Release\Obj\" -s9 # # --no_unroll --no_clustering --cpu_mode arm --endian # # little --cpu ARM926EJ-S --stack_align 4 --interwork # # -e --fpu None -I "C:\Program Files\Embedded # # Workbench 4.0 Evaluation\ARM\INC\" # # --inline_threshold=2 # # List file = C:\ARM\Write flasher Rus\Release\List\main.lst # # Object file = C:\ARM\Write flasher Rus\Release\Obj\main.r79 # # # # # ############################################################################## C:\ARM\Write flasher Rus\main.c 1 #include "..\inc\swilib.h" 2 #include "..\inc\cfg_items.h" 3 #include "conf_loader.h" 4 #define MIN_UPTIME 1 5 #define MAX(a,b) (a)>(b)?(a):(b) 6 \ In segment DATA_Z, align 4, align-sorted 7 GBSTMR timer; \ timer: \ 00000000 DS8 16 8 extern const int ENA_HELLO_MSG; \ In segment DATA_C, align 4, align-sorted 9 const int minus11=-11; \ minus11: \ 00000000 F5FFFFFF DC32 -11 10 11 extern const int dis; 12 extern const int key; 13 extern const int lig; 14 extern const int dyn; 15 16 extern const int lockdis; 17 extern const int lockkey; 18 extern const int locklig; 19 extern const int lockdyn; 20 21 extern const unsigned int light; 22 extern const unsigned int delay; \ In segment DATA_Z, align 4, align-sorted 23 unsigned int uiUpdateTime; \ uiUpdateTime: \ 00000000 DS8 4 24 extern const unsigned int cfgUpTime; 25 extern const int key_patch; 26 \ In segment DATA_Z, align 4, align-sorted 27 char b[256]; \ `b`: \ 00000000 DS8 256 \ In segment DATA_Z, align 4, align-sorted 28 int prespace0, space0, \ prespace0: \ 00000000 DS8 4 \ In segment DATA_Z, align 4, align-sorted \ space0: \ 00000000 DS8 4 \ In segment DATA_Z, align 4, align-sorted 29 prespace1, space1, \ prespace1: \ 00000000 DS8 4 \ In segment DATA_Z, align 4, align-sorted \ space1: \ 00000000 DS8 4 \ In segment DATA_Z, align 4, align-sorted 30 prespace2, space2, \ prespace2: \ 00000000 DS8 4 \ In segment DATA_Z, align 4, align-sorted \ space2: \ 00000000 DS8 4 \ In segment DATA_Z, align 4, align-sorted 31 prespace4, space4, count, Oldillum; \ prespace4: \ 00000000 DS8 4 \ In segment DATA_Z, align 4, align-sorted \ space4: \ 00000000 DS8 4 \ In segment DATA_Z, align 4, align-sorted \ count: \ 00000000 DS8 4 \ In segment DATA_Z, align 4, align-sorted \ Oldillum: \ 00000000 DS8 4 32 33 typedef struct 34 {CSM_RAM csm; 35 }MAIN_CSM; 36 \ In segment DATA_Z, align 4, align-sorted 37 CSM_RAM *under_idle; \ under_idle: \ 00000000 DS8 4 38 39 extern void kill_data(void *p, void (*func_p)(void *)); 40 41 #pragma inline=forced 42 43 int toupper(int c) 44 {if ((c>='a')&&(c<='z')) c+='A'-'a'; 45 return(c); 46 } 47 \ In segment CODE, align 4, keep-with-next 48 int strcmp_nocase(const char *s1,const char *s2) 49 {int i,c; 50 while(!(i=(c=toupper(*s1++))-toupper(*s2++))) if (!c) break; \ strcmp_nocase: \ ??strcmp_nocase_0: \ 00000000 0020D0E5 LDRB R2,[R0, #+0] \ 00000004 610052E3 CMP R2,#+97 \ 00000008 010000BA BLT ??strcmp_nocase_1 \ 0000000C 7B0052E3 CMP R2,#+123 \ 00000010 202042B2 SUBLT R2,R2,#+32 \ ??strcmp_nocase_1: \ 00000014 00C0D1E5 LDRB R12,[R1, #+0] \ 00000018 010080E2 ADD R0,R0,#+1 \ 0000001C 0230A0E1 MOV R3,R2 \ 00000020 61005CE3 CMP R12,#+97 \ 00000024 010000BA BLT ??strcmp_nocase_2 \ 00000028 7B005CE3 CMP R12,#+123 \ 0000002C 20C04CB2 SUBLT R12,R12,#+32 \ ??strcmp_nocase_2: \ 00000030 011081E2 ADD R1,R1,#+1 \ 00000034 0C2052E0 SUBS R2,R2,R12 \ 00000038 0100001A BNE ??strcmp_nocase_3 \ 0000003C 000053E3 CMP R3,#+0 \ 00000040 EEFFFF1A BNE ??strcmp_nocase_0 51 return(i); \ ??strcmp_nocase_3: \ 00000044 0200A0E1 MOV R0,R2 \ 00000048 1EFF2FE1 BX LR ;; return 52 } 53 \ In segment CODE, align 4, keep-with-next 54 int maincsm_onmessage(CSM_RAM* data,GBS_MSG* msg) 55 {if(msg->msg == MSG_RECONFIGURE_REQ) \ maincsm_onmessage: \ 00000000 00402DE9 PUSH {LR} \ 00000004 040091E5 LDR R0,[R1, #+4] \ 00000008 AF20A0E3 MOV R2,#+175 \ 0000000C DE2C82E3 ORR R2,R2,#0xDE00 \ 00000010 020050E1 CMP R0,R2 \ 00000014 0900001A BNE ??maincsm_onmessage_0 56 {extern const char *successed_config_filename; 57 if(strcmp_nocase(successed_config_filename,(char *)msg->data0)==0) \ 00000018 28009FE5 LDR R0,??maincsm_onmessage_1 ;; successed_config_filename \ 0000001C 0C1091E5 LDR R1,[R1, #+12] \ 00000020 000090E5 LDR R0,[R0, #+0] \ 00000024 ........ BL strcmp_nocase \ 00000028 000050E3 CMP R0,#+0 \ 0000002C 0300001A BNE ??maincsm_onmessage_0 58 {ShowMSG(1,(int)"Настройки Write flasher обновлены!"); \ 00000030 14109FE5 LDR R1,??maincsm_onmessage_1+0x4 ;; `?0) \ 00000140 ........ LDR R4,??DataTable35 ;; timer \ 00000144 001090E5 LDR R1,[R0, #+0] \ 00000148 011041E2 SUB R1,R1,#+1 \ 0000014C 001080E5 STR R1,[R0, #+0] \ 00000150 010051E3 CMP R1,#+1 149 {GBS_StartTimerProc(&timer,delay,LightOn); \ 00000154 6C209FA5 LDRGE R2,??LightOff_8+0x4 ;; LightOn \ 00000158 ........ LDRGE R0,??DataTable20 ;; delay \ 0000015C 140000AA BGE ??LightOff_9 150 } 151 else 152 {SetIllumination(0,1,Oldillum,0); \ 00000160 ........ LDR R5,??DataTable25 ;; Oldillum \ 00000164 0030A0E3 MOV R3,#+0 \ 00000168 002095E5 LDR R2,[R5, #+0] \ 0000016C 0110A0E3 MOV R1,#+1 \ 00000170 0228A0E1 MOV R2,R2, LSL #+16 \ 00000174 2228A0E1 MOV R2,R2, LSR #+16 \ 00000178 0000A0E3 MOV R0,#+0 \ 0000017C 080000EF SWI +8 153 if(!key_patch) SetIllumination(1,1,Oldillum,0); \ 00000180 44009FE5 LDR R0,??LightOff_8+0x8 ;; key_patch \ 00000184 000090E5 LDR R0,[R0, #+0] \ 00000188 000050E3 CMP R0,#+0 \ 0000018C 0600001A BNE ??LightOff_10 \ 00000190 002095E5 LDR R2,[R5, #+0] \ 00000194 0030A0E3 MOV R3,#+0 \ 00000198 0228A0E1 MOV R2,R2, LSL #+16 \ 0000019C 2228A0E1 MOV R2,R2, LSR #+16 \ 000001A0 0110A0E3 MOV R1,#+1 \ 000001A4 0100A0E3 MOV R0,#+1 \ 000001A8 080000EF SWI +8 154 GBS_StartTimerProc(&timer, uiUpdateTime, StartWorkTimer); \ ??LightOff_10: \ 000001AC ........ LDR R2,??DataTable34 ;; StartWorkTimer \ 000001B0 ........ LDR R0,??DataTable36 ;; uiUpdateTime \ ??LightOff_9: \ 000001B4 001090E5 LDR R1,[R0, #+0] \ 000001B8 0400A0E1 MOV R0,R4 \ 000001BC 4D0000EF SWI +77 155 } 156 } \ 000001C0 3080BDE8 POP {R4,R5,PC} ;; return \ ??LightOff_8: \ 000001C4 ........ DC32 count \ 000001C8 ........ DC32 LightOn \ 000001CC ........ DC32 key_patch 157 \ In segment CODE, align 4, keep-with-next 158 void StartWorkTimer(void) 159 { Oldillum=GetIlluminationDataTable()[3]; \ StartWorkTimer: \ 00000000 F0412DE9 PUSH {R4-R8,LR} 160 space0=GetFreeFlexSpace(0,0); \ 00000004 9C419FE5 LDR R4,??StartWorkTimer_0 ;; space0 161 space1=GetFreeFlexSpace(1,0); \ 00000008 9C519FE5 LDR R5,??StartWorkTimer_0+0x4 ;; space1 162 space2=GetFreeFlexSpace(2,0); \ 0000000C 9C619FE5 LDR R6,??StartWorkTimer_0+0x8 ;; space2 163 space4=GetFreeFlexSpace(4,0); \ 00000010 9C719FE5 LDR R7,??StartWorkTimer_0+0xC ;; space4 164 if(prespace0!=space0) \ 00000014 ........ LDR R8,??DataTable37 ;; prespace0 \ 00000018 528000EF SWI +32850 \ 0000001C 0300D0E5 LDRB R0,[R0, #+3] \ 00000020 ........ LDR R1,??DataTable25 ;; Oldillum \ 00000024 000081E5 STR R0,[R1, #+0] \ 00000028 0010A0E3 MOV R1,#+0 \ 0000002C 0000A0E3 MOV R0,#+0 \ 00000030 8A0000EF SWI +138 \ 00000034 000084E5 STR R0,[R4, #+0] \ 00000038 0010A0E3 MOV R1,#+0 \ 0000003C 0100A0E3 MOV R0,#+1 \ 00000040 8A0000EF SWI +138 \ 00000044 000085E5 STR R0,[R5, #+0] \ 00000048 0010A0E3 MOV R1,#+0 \ 0000004C 0200A0E3 MOV R0,#+2 \ 00000050 8A0000EF SWI +138 \ 00000054 000086E5 STR R0,[R6, #+0] \ 00000058 0010A0E3 MOV R1,#+0 \ 0000005C 0400A0E3 MOV R0,#+4 \ 00000060 8A0000EF SWI +138 \ 00000064 ........ LDR R1,??DataTable26 ;; lig \ 00000068 ........ LDR R2,??DataTable27 ;; dis \ 0000006C ........ LDR R3,??DataTable28 ;; key \ 00000070 00C098E5 LDR R12,[R8, #+0] \ 00000074 00E094E5 LDR LR,[R4, #+0] \ 00000078 001091E5 LDR R1,[R1, #+0] \ 0000007C 002092E5 LDR R2,[R2, #+0] \ 00000080 003093E5 LDR R3,[R3, #+0] \ 00000084 000087E5 STR R0,[R7, #+0] \ 00000088 ........ LDR R0,??DataTable29 ;; dyn \ 0000008C 0E005CE1 CMP R12,LR \ 00000090 000090E5 LDR R0,[R0, #+0] \ 00000094 0B00000A BEQ ??StartWorkTimer_1 165 {if((key||dis||lig||dyn)&&(!IsCalling())) LightOn(); \ 00000098 000053E3 CMP R3,#+0 \ 0000009C 00005203 CMPEQ R2,#+0 \ 000000A0 00005103 CMPEQ R1,#+0 \ 000000A4 00005003 CMPEQ R0,#+0 \ 000000A8 0300000A BEQ ??StartWorkTimer_2 \ 000000AC 6E0000EF SWI +110 \ 000000B0 000050E3 CMP R0,#+0 \ 000000B4 0000001A BNE ??StartWorkTimer_2 \ 000000B8 ........ BL LightOn 166 prespace0=space0; \ ??StartWorkTimer_2: \ 000000BC 000094E5 LDR R0,[R4, #+0] \ 000000C0 000088E5 STR R0,[R8, #+0] \ 000000C4 F081BDE8 POP {R4-R8,PC} 167 } 168 else 169 if(prespace1!=space1) \ ??StartWorkTimer_1: \ 000000C8 ........ LDR R4,??DataTable38 ;; prespace1 \ 000000CC 00C095E5 LDR R12,[R5, #+0] \ 000000D0 008094E5 LDR R8,[R4, #+0] \ 000000D4 0C0058E1 CMP R8,R12 \ 000000D8 0A00000A BEQ ??StartWorkTimer_3 170 {if((key||dis||lig||dyn)&&(!IsCalling())) LightOn(); \ 000000DC 000053E3 CMP R3,#+0 \ 000000E0 00005203 CMPEQ R2,#+0 \ 000000E4 00005103 CMPEQ R1,#+0 \ 000000E8 00005003 CMPEQ R0,#+0 \ 000000EC 0300000A BEQ ??StartWorkTimer_4 \ 000000F0 6E0000EF SWI +110 \ 000000F4 000050E3 CMP R0,#+0 \ 000000F8 0000001A BNE ??StartWorkTimer_4 \ 000000FC ........ BL LightOn 171 prespace1=space1; \ ??StartWorkTimer_4: \ 00000100 000095E5 LDR R0,[R5, #+0] \ 00000104 1F0000EA B ??StartWorkTimer_5 172 } 173 else 174 if(prespace2!=space2) \ ??StartWorkTimer_3: \ 00000108 ........ LDR R4,??DataTable39 ;; prespace2 \ 0000010C 008096E5 LDR R8,[R6, #+0] \ 00000110 005094E5 LDR R5,[R4, #+0] \ 00000114 080055E1 CMP R5,R8 \ 00000118 0B00000A BEQ ??StartWorkTimer_6 175 {if((key||dis||lig||dyn)&&(!IsCalling())) LightOn(); \ 0000011C 000053E3 CMP R3,#+0 \ 00000120 00005203 CMPEQ R2,#+0 \ 00000124 00005103 CMPEQ R1,#+0 \ 00000128 00005003 CMPEQ R0,#+0 \ 0000012C 0300000A BEQ ??StartWorkTimer_7 \ 00000130 6E0000EF SWI +110 \ 00000134 000050E3 CMP R0,#+0 \ 00000138 0000001A BNE ??StartWorkTimer_7 \ 0000013C ........ BL LightOn 176 prespace2=space2; \ ??StartWorkTimer_7: \ 00000140 000096E5 LDR R0,[R6, #+0] \ 00000144 000084E5 STR R0,[R4, #+0] \ 00000148 F081BDE8 POP {R4-R8,PC} 177 } 178 else 179 if(prespace4!=space4) \ ??StartWorkTimer_6: \ 0000014C ........ LDR R4,??DataTable40 ;; prespace4 \ 00000150 006097E5 LDR R6,[R7, #+0] \ 00000154 005094E5 LDR R5,[R4, #+0] \ 00000158 060055E1 CMP R5,R6 \ 0000015C 0B00000A BEQ ??StartWorkTimer_8 180 {if((key||dis||lig||dyn)&&(!IsCalling())) LightOn(); \ 00000160 000053E3 CMP R3,#+0 \ 00000164 00005203 CMPEQ R2,#+0 \ 00000168 00005103 CMPEQ R1,#+0 \ 0000016C 00005003 CMPEQ R0,#+0 \ 00000170 0300000A BEQ ??StartWorkTimer_9 \ 00000174 6E0000EF SWI +110 \ 00000178 000050E3 CMP R0,#+0 \ 0000017C 0000001A BNE ??StartWorkTimer_9 \ 00000180 ........ BL LightOn 181 prespace4=space4; \ ??StartWorkTimer_9: \ 00000184 000097E5 LDR R0,[R7, #+0] \ ??StartWorkTimer_5: \ 00000188 000084E5 STR R0,[R4, #+0] \ 0000018C F081BDE8 POP {R4-R8,PC} 182 } 183 else GBS_StartTimerProc(&timer, uiUpdateTime, StartWorkTimer); \ ??StartWorkTimer_8: \ 00000190 ........ LDR R0,??DataTable36 ;; uiUpdateTime \ 00000194 ........ LDR R2,??DataTable34 ;; StartWorkTimer \ 00000198 001090E5 LDR R1,[R0, #+0] \ 0000019C ........ LDR R0,??DataTable35 ;; timer \ 000001A0 4D0000EF SWI +77 184 } \ 000001A4 F081BDE8 POP {R4-R8,PC} ;; return \ ??StartWorkTimer_0: \ 000001A8 ........ DC32 space0 \ 000001AC ........ DC32 space1 \ 000001B0 ........ DC32 space2 \ 000001B4 ........ DC32 space4 185 \ In segment CODE, align 4, keep-with-next 186 void InitSettings() 187 {InitConfig(); \ InitSettings: \ 00000000 00402DE9 PUSH {LR} \ 00000004 ........ _BLF InitConfig,??InitConfig??rA 188 uiUpdateTime = MAX(((216*cfgUpTime)/10),MIN_UPTIME); \ 00000008 98009FE5 LDR R0,??InitSettings_0 ;; cfgUpTime \ 0000000C D810A0E3 MOV R1,#+216 \ 00000010 000090E5 LDR R0,[R0, #+0] \ 00000014 910002E0 MUL R2,R1,R0 \ 00000018 8C109FE5 LDR R1,??InitSettings_0+0x4 ;; 0xffffffffcccccccd \ 0000001C 913280E0 UMULL R3,R0,R1,R2 \ 00000020 ........ LDR R1,??DataTable36 ;; uiUpdateTime \ 00000024 A001A0E1 LSR R0,R0,#+3 \ 00000028 020050E3 CMP R0,#+2 \ 0000002C 0100A033 MOVCC R0,#+1 \ 00000030 000081E5 STR R0,[R1, #+0] 189 prespace0=GetFreeFlexSpace(0,0); \ 00000034 0010A0E3 MOV R1,#+0 \ 00000038 0000A0E3 MOV R0,#+0 \ 0000003C 8A0000EF SWI +138 \ 00000040 ........ LDR R1,??DataTable37 ;; prespace0 \ 00000044 000081E5 STR R0,[R1, #+0] 190 prespace1=GetFreeFlexSpace(1,0); \ 00000048 0010A0E3 MOV R1,#+0 \ 0000004C 0100A0E3 MOV R0,#+1 \ 00000050 8A0000EF SWI +138 \ 00000054 ........ LDR R1,??DataTable38 ;; prespace1 \ 00000058 000081E5 STR R0,[R1, #+0] 191 prespace2=GetFreeFlexSpace(2,0); \ 0000005C 0010A0E3 MOV R1,#+0 \ 00000060 0200A0E3 MOV R0,#+2 \ 00000064 8A0000EF SWI +138 \ 00000068 ........ LDR R1,??DataTable39 ;; prespace2 \ 0000006C 000081E5 STR R0,[R1, #+0] 192 prespace4=GetFreeFlexSpace(4,0); \ 00000070 0010A0E3 MOV R1,#+0 \ 00000074 0400A0E3 MOV R0,#+4 \ 00000078 8A0000EF SWI +138 \ 0000007C ........ LDR R1,??DataTable40 ;; prespace4 \ 00000080 000081E5 STR R0,[R1, #+0] 193 if(ENA_HELLO_MSG) ShowMSG(1,(int)"Write flasher установлен (c)SimaFish!"); \ 00000084 24009FE5 LDR R0,??InitSettings_0+0x8 ;; ENA_HELLO_MSG \ 00000088 000090E5 LDR R0,[R0, #+0] \ 0000008C 000050E3 CMP R0,#+0 \ 00000090 0200000A BEQ ??InitSettings_1 \ 00000094 18109FE5 LDR R1,??InitSettings_0+0xC ;; `?` \ 00000014 280084E2 ADD R0,R4,#+40 \ 00000018 240100EF SWI +292 203 LockSched(); \ 0000001C 460100EF SWI +326 204 save_cmpc=CSM_root()->csm_q->current_msg_processing_csm; \ 00000020 068100EF SWI +33030 \ 00000024 080090E5 LDR R0,[R0, #+8] \ 00000028 045090E5 LDR R5,[R0, #+4] 205 CSM_root()->csm_q->current_msg_processing_csm=CSM_root()->csm_q->csm.first; \ 0000002C 068100EF SWI +33030 \ 00000030 0060A0E1 MOV R6,R0 \ 00000034 068100EF SWI +33030 \ 00000038 080090E5 LDR R0,[R0, #+8] \ 0000003C 081096E5 LDR R1,[R6, #+8] \ 00000040 080090E5 LDR R0,[R0, #+8] 206 CreateCSM(&MAINCSM.maincsm,dummy,0); \ 00000044 0020A0E3 MOV R2,#+0 \ 00000048 040081E5 STR R0,[R1, #+4] \ 0000004C 0D10A0E1 MOV R1,SP \ 00000050 0400A0E1 MOV R0,R4 \ 00000054 070100EF SWI +263 207 CSM_root()->csm_q->current_msg_processing_csm=save_cmpc; \ 00000058 068100EF SWI +33030 \ 0000005C 080090E5 LDR R0,[R0, #+8] \ 00000060 045080E5 STR R5,[R0, #+4] 208 UnlockSched(); \ 00000064 470100EF SWI +327 209 } \ 00000068 28D08DE2 ADD SP,SP,#+40 \ 0000006C 7080BDE8 POP {R4-R6,PC} ;; return \ ??main_0: \ 00000070 ........ DC32 MAINCSM \ 00000074 ........ DC32 `?` \ In segment CODE, align 4, keep-with-next \ ??DataTable15: \ 00000000 ........ DC32 lockkey \ In segment CODE, align 4, keep-with-next \ ??DataTable16: \ 00000000 ........ DC32 lockdis \ In segment CODE, align 4, keep-with-next \ ??DataTable17: \ 00000000 ........ DC32 lockdyn \ In segment CODE, align 4, keep-with-next \ ??DataTable18: \ 00000000 ........ DC32 locklig \ In segment CODE, align 4, keep-with-next \ ??DataTable20: \ 00000000 ........ DC32 delay \ In segment CODE, align 4, keep-with-next \ ??DataTable25: \ 00000000 ........ DC32 Oldillum \ In segment CODE, align 4, keep-with-next \ ??DataTable26: \ 00000000 ........ DC32 lig \ In segment CODE, align 4, keep-with-next \ ??DataTable27: \ 00000000 ........ DC32 dis \ In segment CODE, align 4, keep-with-next \ ??DataTable28: \ 00000000 ........ DC32 key \ In segment CODE, align 4, keep-with-next \ ??DataTable29: \ 00000000 ........ DC32 dyn \ In segment CODE, align 4, keep-with-next \ ??DataTable34: \ 00000000 ........ DC32 StartWorkTimer \ In segment CODE, align 4, keep-with-next \ ??DataTable35: \ 00000000 ........ DC32 timer \ In segment CODE, align 4, keep-with-next \ ??DataTable36: \ 00000000 ........ DC32 uiUpdateTime \ In segment CODE, align 4, keep-with-next \ ??DataTable37: \ 00000000 ........ DC32 prespace0 \ In segment CODE, align 4, keep-with-next \ ??DataTable38: \ 00000000 ........ DC32 prespace1 \ In segment CODE, align 4, keep-with-next \ ??DataTable39: \ 00000000 ........ DC32 prespace2 \ In segment CODE, align 4, keep-with-next \ ??DataTable40: \ 00000000 ........ DC32 prespace4 \ In segment DATA_C, align 4, align-sorted \ `?`: \ 00000000 577269746520 DC8 "Write flasher - (c)SimaFish" \ 666C61736865 \ 72202D202863 \ 2953696D6146 \ 69736800 \ In segment DATA_C, align 4, align-sorted \ `? 28 ?