Mercurial > ecos
annotate packages/hal/arm/arch/current/src/arm_stub.c @ 36:e97d78785e2d ecos-sw-1999-09-12
Merge from eCos master repository on 1999-09-12-15:40:34-BST
| author | jlarmour |
|---|---|
| date | Sun, 12 Sep 1999 15:26:05 +0000 |
| parents | 29bc183297e1 |
| children | e7ba79f6d3a8 |
| rev | line source |
|---|---|
| 2 | 1 /* arm_stub.c - helper functions for stub, generic to all ARM processors |
| 2 * | |
| 3 * Copyright (c) 1998,1999 Cygnus Solutions | |
| 4 * | |
| 5 * The authors hereby grant permission to use, copy, modify, distribute, | |
| 6 * and license this software and its documentation for any purpose, provided | |
| 7 * that existing copyright notices are retained in all copies and that this | |
| 8 * notice is included verbatim in any distributions. No written agreement, | |
| 9 * license, or royalty fee is required for any of the authorized uses. | |
| 10 * Modifications to this software may be copyrighted by their authors | |
| 11 * and need not follow the licensing terms described here, provided that | |
| 12 * the new terms are clearly indicated on the first page of each file where | |
| 13 * they apply. | |
| 14 */ | |
| 15 | |
| 16 #include <stddef.h> | |
| 17 | |
| 18 #include <pkgconf/hal.h> | |
| 19 | |
| 20 #ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS | |
| 21 | |
| 22 #ifdef CYG_HAL_ARM_SIM | |
| 23 #error "GDB Stub support not implemented for ARM SIM" | |
| 24 #endif | |
| 25 | |
| 26 #include <cyg/hal/hal_stub.h> | |
| 27 #include <cyg/hal/hal_arch.h> | |
| 28 #include <cyg/hal/hal_intr.h> | |
| 29 | |
| 30 #ifndef FALSE | |
| 31 #define FALSE 0 | |
| 32 #define TRUE 1 | |
| 33 #endif | |
| 34 | |
|
36
e97d78785e2d
Merge from eCos master repository on 1999-09-12-15:40:34-BST
jlarmour
parents:
34
diff
changeset
|
35 |
| 2 | 36 #ifdef CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT |
| 37 #include <cyg/hal/dbg-threads-api.h> // dbg_currthread_id | |
| 38 #endif | |
| 39 | |
| 40 /* Given a trap value TRAP, return the corresponding signal. */ | |
| 41 | |
| 42 int __computeSignal (unsigned int trap_number) | |
| 43 { | |
|
36
e97d78785e2d
Merge from eCos master repository on 1999-09-12-15:40:34-BST
jlarmour
parents:
34
diff
changeset
|
44 // should also catch CYGNUM_HAL_VECTOR_UNDEF_INSTRUCTION here but we |
|
e97d78785e2d
Merge from eCos master repository on 1999-09-12-15:40:34-BST
jlarmour
parents:
34
diff
changeset
|
45 // can't tell the different between a real one and a breakpoint :-( |
| 2 | 46 switch (trap_number) { |
|
36
e97d78785e2d
Merge from eCos master repository on 1999-09-12-15:40:34-BST
jlarmour
parents:
34
diff
changeset
|
47 case CYGNUM_HAL_VECTOR_ABORT_PREFETCH: // Fall through |
|
e97d78785e2d
Merge from eCos master repository on 1999-09-12-15:40:34-BST
jlarmour
parents:
34
diff
changeset
|
48 case CYGNUM_HAL_VECTOR_ABORT_DATA: // Fall through |
|
e97d78785e2d
Merge from eCos master repository on 1999-09-12-15:40:34-BST
jlarmour
parents:
34
diff
changeset
|
49 case CYGNUM_HAL_VECTOR_reserved: |
|
e97d78785e2d
Merge from eCos master repository on 1999-09-12-15:40:34-BST
jlarmour
parents:
34
diff
changeset
|
50 return SIGBUS; |
|
e97d78785e2d
Merge from eCos master repository on 1999-09-12-15:40:34-BST
jlarmour
parents:
34
diff
changeset
|
51 case CYGNUM_HAL_VECTOR_IRQ: |
|
e97d78785e2d
Merge from eCos master repository on 1999-09-12-15:40:34-BST
jlarmour
parents:
34
diff
changeset
|
52 case CYGNUM_HAL_VECTOR_FIQ: |
|
e97d78785e2d
Merge from eCos master repository on 1999-09-12-15:40:34-BST
jlarmour
parents:
34
diff
changeset
|
53 return SIGINT; |
| 2 | 54 default: |
| 55 return SIGTRAP; | |
| 56 } | |
| 57 } | |
| 58 | |
| 59 | |
| 60 /* Return the trap number corresponding to the last-taken trap. */ | |
|
36
e97d78785e2d
Merge from eCos master repository on 1999-09-12-15:40:34-BST
jlarmour
parents:
34
diff
changeset
|
61 volatile int jifl; |
| 2 | 62 int __get_trap_number (void) |
| 63 { | |
| 64 // The vector is not not part of the GDB register set so get it | |
| 65 // directly from the save context. | |
|
36
e97d78785e2d
Merge from eCos master repository on 1999-09-12-15:40:34-BST
jlarmour
parents:
34
diff
changeset
|
66 return _hal_registers->vector; |
| 2 | 67 } |
| 68 | |
| 69 /* Set the currently-saved pc register value to PC. This also updates NPC | |
| 70 as needed. */ | |
| 71 | |
| 72 void set_pc (target_register_t pc) | |
| 73 { | |
| 74 put_register (PC, pc); | |
| 75 } | |
| 76 | |
| 77 | |
| 78 /*---------------------------------------------------------------------- | |
| 79 * Single-step support | |
| 80 */ | |
| 81 | |
| 82 /* Set things up so that the next user resume will execute one instruction. | |
| 83 This may be done by setting breakpoints or setting a single step flag | |
| 84 in the saved user registers, for example. */ | |
| 85 | |
| 86 #define NULL_PTR (unsigned long *)0 | |
| 87 static unsigned long *ss_saved_pc = NULL_PTR; | |
| 88 static unsigned long ss_saved_instr; | |
| 89 | |
|
36
e97d78785e2d
Merge from eCos master repository on 1999-09-12-15:40:34-BST
jlarmour
parents:
34
diff
changeset
|
90 |
| 2 | 91 #define FIXME() {diag_printf("FIXME - %s\n", __FUNCTION__); } |
| 92 | |
| 93 static int | |
| 94 ins_will_execute(unsigned long ins) | |
| 95 { | |
| 96 unsigned long psr = get_register(PS); // condition codes | |
| 97 int res = 0; | |
| 98 switch ((ins & 0xF0000000) >> 28) { | |
| 99 case 0x0: // EQ | |
| 100 res = (psr & PS_Z) != 0; | |
| 101 break; | |
| 102 case 0x1: // NE | |
| 103 res = (psr & PS_Z) == 0; | |
| 104 break; | |
| 105 case 0x2: // CS | |
| 106 res = (psr & PS_C) != 0; | |
| 107 break; | |
| 108 case 0x3: // CC | |
| 109 res = (psr & PS_C) == 0; | |
| 110 break; | |
| 111 case 0x4: // MI | |
| 112 res = (psr & PS_N) != 0; | |
| 113 break; | |
| 114 case 0x5: // PL | |
| 115 res = (psr & PS_N) == 0; | |
| 116 break; | |
| 117 case 0x6: // VS | |
| 118 res = (psr & PS_V) != 0; | |
| 119 break; | |
| 120 case 0x7: // VC | |
| 121 res = (psr & PS_V) == 0; | |
| 122 break; | |
| 123 case 0x8: // HI | |
|
34
29bc183297e1
Merge from eCos master repository on 1999-09-02-16:26:10-BST
jlarmour
parents:
2
diff
changeset
|
124 res = ((psr & PS_C) != 0) && ((psr & PS_Z) == 0); |
| 2 | 125 break; |
| 126 case 0x9: // LS | |
|
34
29bc183297e1
Merge from eCos master repository on 1999-09-02-16:26:10-BST
jlarmour
parents:
2
diff
changeset
|
127 res = ((psr & PS_C) == 0) || ((psr & PS_Z) != 0); |
| 2 | 128 break; |
| 129 case 0xA: // GE | |
| 130 res = ((psr & (PS_N|PS_V)) == (PS_N|PS_V)) || | |
| 131 ((psr & (PS_N|PS_V)) == 0); | |
| 132 break; | |
| 133 case 0xB: // LT | |
| 134 res = ((psr & (PS_N|PS_V)) == PS_N) || | |
| 135 ((psr & (PS_N|PS_V)) == PS_V); | |
| 136 break; | |
| 137 case 0xC: // GT | |
| 138 res = ((psr & (PS_N|PS_V)) == (PS_N|PS_V)) || | |
| 139 ((psr & (PS_N|PS_V)) == 0); | |
| 140 res = ((psr & PS_Z) == 0) && res; | |
| 141 break; | |
| 142 case 0xD: // LE | |
| 143 res = ((psr & (PS_N|PS_V)) == PS_N) || | |
| 144 ((psr & (PS_N|PS_V)) == PS_V); | |
| 145 res = ((psr & PS_Z) == PS_Z) || res; | |
| 146 break; | |
| 147 case 0xE: // AL | |
| 148 res = TRUE; | |
| 149 break; | |
| 150 case 0xF: // NV | |
| 151 res = FALSE; | |
| 152 break; | |
| 153 } | |
| 154 return res; | |
| 155 } | |
| 156 | |
| 157 static unsigned long | |
| 158 RmShifted(int shift) | |
| 159 { | |
| 160 unsigned long Rm = get_register(shift & 0x00F); | |
| 161 int shift_count; | |
| 162 if ((shift & 0x010) == 0) { | |
| 163 shift_count = (shift & 0xF80) >> 7; | |
| 164 } else { | |
| 165 shift_count = get_register((shift & 0xF00) >> 8); | |
| 166 } | |
| 167 switch ((shift & 0x060) >> 5) { | |
| 168 case 0x0: // Logical left | |
| 169 Rm <<= shift_count; | |
| 170 break; | |
| 171 case 0x1: // Logical right | |
| 172 Rm >>= shift_count; | |
| 173 break; | |
| 174 case 0x2: // Arithmetic right | |
| 175 Rm = (unsigned long)((long)Rm >> shift_count); | |
| 176 break; | |
| 177 case 0x3: // Rotate right | |
| 178 if (shift_count == 0) { | |
| 179 // Special case, RORx | |
| 180 Rm >>= 1; | |
| 181 if (get_register(PS) & PS_C) Rm |= 0x80000000; | |
| 182 } else { | |
| 183 Rm = (Rm >> shift_count) | (Rm << (32-shift_count)); | |
| 184 } | |
| 185 break; | |
| 186 } | |
| 187 return Rm; | |
| 188 } | |
| 189 | |
| 190 // Decide the next instruction to be executed for a given instruction | |
| 191 static unsigned long * | |
| 192 target_ins(unsigned long *pc, unsigned long ins) | |
| 193 { | |
| 194 unsigned long new_pc, offset, op2; | |
| 195 unsigned long Rn; | |
| 196 int i, reg_count, c; | |
| 197 switch ((ins & 0x0C000000) >> 26) { | |
| 198 case 0x0: | |
| 199 // Data processing | |
| 200 new_pc = (unsigned long)(pc+1); | |
| 201 if ((ins & 0x0000F000) == 0x0000F000) { | |
| 202 // Destination register is PC | |
| 203 if ((ins & 0x0FBF0000) != 0x010F0000) { | |
| 204 Rn = (unsigned long)get_register((ins & 0x000F0000) >> 16); | |
| 205 if ((ins & 0x02000000) == 0) { | |
| 206 op2 = RmShifted(ins & 0x00000FFF); | |
| 207 } else { | |
| 208 op2 = ins & 0x000000FF; | |
| 209 i = (ins & 0x00000F00) >> 8; // Rotate count | |
| 210 op2 = (op2 >> (i*2)) | (op2 << (32-(i*2))); | |
| 211 } | |
| 212 switch ((ins & 0x01E00000) >> 21) { | |
| 213 case 0x0: // AND | |
| 214 new_pc = Rn & op2; | |
| 215 break; | |
| 216 case 0x1: // EOR | |
| 217 new_pc = Rn ^ op2; | |
| 218 break; | |
| 219 case 0x2: // SUB | |
| 220 new_pc = Rn - op2; | |
| 221 break; | |
| 222 case 0x3: // RSB | |
| 223 new_pc = op2 - Rn; | |
| 224 break; | |
| 225 case 0x4: // ADD | |
| 226 new_pc = Rn + op2; | |
| 227 break; | |
| 228 case 0x5: // ADC | |
| 229 c = (get_register(PS) & PS_C) != 0; | |
| 230 new_pc = Rn + op2 + c; | |
| 231 break; | |
| 232 case 0x6: // SBC | |
| 233 c = (get_register(PS) & PS_C) != 0; | |
| 234 new_pc = Rn - op2 + c - 1; | |
| 235 break; | |
| 236 case 0x7: // RSC | |
| 237 c = (get_register(PS) & PS_C) != 0; | |
| 238 new_pc = op2 - Rn +c - 1; | |
| 239 break; | |
| 240 case 0x8: // TST | |
| 241 case 0x9: // TEQ | |
| 242 case 0xA: // CMP | |
| 243 case 0xB: // CMN | |
| 244 break; // PC doesn't change | |
| 245 case 0xC: // ORR | |
| 246 new_pc = Rn | op2; | |
| 247 break; | |
| 248 case 0xD: // MOV | |
| 249 new_pc = op2; | |
| 250 break; | |
| 251 case 0xE: // BIC | |
| 252 new_pc = Rn & ~op2; | |
| 253 break; | |
| 254 case 0xF: // MVN | |
| 255 new_pc = ~op2; | |
| 256 break; | |
| 257 } | |
| 258 } | |
| 259 } | |
| 260 return ((unsigned long *)new_pc); | |
| 261 case 0x1: | |
| 262 if ((ins & 0x02000010) == 0x02000010) { | |
| 263 // Undefined! | |
| 264 return (pc+1); | |
| 265 } else { | |
| 266 if ((ins & 0x00100000) == 0) { | |
| 267 // STR | |
| 268 return (pc+1); | |
| 269 } else { | |
| 270 // LDR | |
| 271 if ((ins & 0x0000F000) != 0x0000F000) { | |
| 272 // Rd not PC | |
| 273 return (pc+1); | |
| 274 } else { | |
| 275 Rn = (unsigned long)get_register((ins & 0x000F0000) >> 16); | |
| 276 if ((ins & 0x000F0000) == 0x000F0000) Rn += 8; // PC prefetch! | |
| 277 if (ins & 0x01000000) { | |
| 278 // Add/subtract offset before | |
| 279 if ((ins & 0x02000000) == 0) { | |
| 280 // Immediate offset | |
| 281 if (ins & 0x00800000) { | |
| 282 // Add offset | |
| 283 Rn += (ins & 0x00000FFF); | |
| 284 } else { | |
| 285 // Subtract offset | |
| 286 Rn -= (ins & 0x00000FFF); | |
| 287 } | |
| 288 } else { | |
| 289 // Offset is in a register | |
| 290 if (ins & 0x00800000) { | |
| 291 // Add offset | |
| 292 Rn += RmShifted(ins & 0x00000FFF); | |
| 293 } else { | |
| 294 // Subtract offset | |
| 295 Rn -= RmShifted(ins & 0x00000FFF); | |
| 296 } | |
| 297 } | |
| 298 } | |
| 299 return ((unsigned long *)*(unsigned long *)Rn); | |
| 300 } | |
| 301 } | |
| 302 } | |
| 303 return (pc+1); | |
| 304 case 0x2: // Branch, LDM/STM | |
| 305 if ((ins & 0x02000000) == 0) { | |
| 306 // LDM/STM | |
| 307 if ((ins & 0x00100000) == 0) { | |
| 308 // STM | |
| 309 return (pc+1); | |
| 310 } else { | |
| 311 // LDM | |
| 312 if ((ins & 0x00008000) == 0) { | |
| 313 // PC not in list | |
| 314 return (pc+1); | |
| 315 } else { | |
| 316 Rn = (unsigned long)get_register((ins & 0x000F0000) >> 16); | |
| 317 if ((ins & 0x000F0000) == 0x000F0000) Rn += 8; // PC prefetch! | |
| 318 offset = ins & 0x0000FFFF; | |
| 319 reg_count = 0; | |
| 320 for (i = 0; i < 15; i++) { | |
| 321 if (offset & (1<<i)) reg_count++; | |
| 322 } | |
| 323 if (ins & 0x00800000) { | |
| 324 // Add offset | |
| 325 Rn += reg_count*4; | |
| 326 } else { | |
| 327 // Subtract offset | |
| 328 Rn -= 4; | |
| 329 } | |
| 330 return ((unsigned long *)*(unsigned long *)Rn); | |
| 331 } | |
| 332 } | |
| 333 } else { | |
| 334 // Branch | |
|
34
29bc183297e1
Merge from eCos master repository on 1999-09-02-16:26:10-BST
jlarmour
parents:
2
diff
changeset
|
335 if (ins_will_execute(ins)) { |
|
29bc183297e1
Merge from eCos master repository on 1999-09-02-16:26:10-BST
jlarmour
parents:
2
diff
changeset
|
336 offset = (ins & 0x00FFFFFF) << 2; |
|
29bc183297e1
Merge from eCos master repository on 1999-09-02-16:26:10-BST
jlarmour
parents:
2
diff
changeset
|
337 if (ins & 0x00800000) offset |= 0xFC000000; // sign extend |
|
29bc183297e1
Merge from eCos master repository on 1999-09-02-16:26:10-BST
jlarmour
parents:
2
diff
changeset
|
338 new_pc = (unsigned long)(pc+2) + offset; |
|
29bc183297e1
Merge from eCos master repository on 1999-09-02-16:26:10-BST
jlarmour
parents:
2
diff
changeset
|
339 return ((unsigned long *)new_pc); |
|
29bc183297e1
Merge from eCos master repository on 1999-09-02-16:26:10-BST
jlarmour
parents:
2
diff
changeset
|
340 } else { |
|
29bc183297e1
Merge from eCos master repository on 1999-09-02-16:26:10-BST
jlarmour
parents:
2
diff
changeset
|
341 // Falls through |
|
29bc183297e1
Merge from eCos master repository on 1999-09-02-16:26:10-BST
jlarmour
parents:
2
diff
changeset
|
342 return (pc+1); |
|
29bc183297e1
Merge from eCos master repository on 1999-09-02-16:26:10-BST
jlarmour
parents:
2
diff
changeset
|
343 } |
| 2 | 344 } |
| 345 case 0x3: // Coprocessor & SWI | |
| 346 return (pc+1); | |
| 347 } | |
| 348 } | |
| 349 | |
| 350 void __single_step (void) | |
| 351 { | |
| 352 unsigned long *pc = (unsigned long *)get_register(PC); | |
| 353 unsigned long curins = *pc; | |
| 354 if (ins_will_execute(curins)) { | |
| 355 // Decode instruction to decide what the next PC will be | |
| 356 ss_saved_pc = target_ins(pc, curins); | |
| 357 } else { | |
| 358 // The current instruction will not execute (the conditions don't hold) | |
| 359 ss_saved_pc = (pc+1); | |
| 360 } | |
| 361 // For now | |
| 362 ss_saved_instr = *ss_saved_pc; | |
| 363 *ss_saved_pc = 0xE7FFDEFF; // As used by ARM | |
| 364 } | |
| 365 | |
| 366 /* Clear the single-step state. */ | |
| 367 | |
| 368 void __clear_single_step (void) | |
| 369 { | |
| 370 if (ss_saved_pc != NULL_PTR) { | |
| 371 *ss_saved_pc = ss_saved_instr; | |
| 372 ss_saved_pc = NULL_PTR; | |
| 373 } | |
| 374 } | |
| 375 | |
| 376 void __install_breakpoints (void) | |
| 377 { | |
| 378 // FIXME(); | |
| 379 } | |
| 380 | |
| 381 void __clear_breakpoints (void) | |
| 382 { | |
| 383 // FIXME(); | |
| 384 } | |
| 385 | |
| 386 /* If the breakpoint we hit is in the breakpoint() instruction, return a | |
| 387 non-zero value. */ | |
| 388 | |
| 389 int | |
| 390 __is_breakpoint_function () | |
| 391 { | |
| 392 return get_register (PC) == (target_register_t)&CYG_LABEL_NAME(breakinst); | |
| 393 } | |
| 394 | |
| 395 | |
| 396 /* Skip the current instruction. Since this is only called by the | |
| 397 stub when the PC points to a breakpoint or trap instruction, | |
| 398 we can safely just skip 4. */ | |
| 399 | |
| 400 void __skipinst (void) | |
| 401 { | |
| 402 put_register (PC, get_register (PC) + 4); | |
| 403 } | |
| 404 | |
|
36
e97d78785e2d
Merge from eCos master repository on 1999-09-12-15:40:34-BST
jlarmour
parents:
34
diff
changeset
|
405 |
| 2 | 406 #endif // CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS |
