From ee793f0fcbd2d7e4ad08e61d242ba178e8909b45 Mon Sep 17 00:00:00 2001 From: oharboe Date: Wed, 7 May 2008 14:25:34 +0000 Subject: [PATCH] This matters for embedded devices, but is probably not observably better for PC hosted OpenOCD. git-svn-id: svn://svn.berlios.de/openocd/trunk@647 b42882b7-edfa-0310-969c-e2dbd0fdcd60 --- src/helper/binarybuffer.c | 37 --------------- src/helper/binarybuffer.h | 43 ++++++++++++++++- src/helper/log.h | 2 +- src/jtag/jtag.c | 4 +- src/jtag/jtag.h | 8 ++-- src/server/gdb_server.c | 5 +- src/target/arm7_9_common.c | 24 ++++------ src/target/arm7tdmi.c | 96 ++++++++++++-------------------------- src/target/arm_jtag.c | 36 +++++--------- src/target/armv4_5.c | 36 -------------- src/target/armv4_5.h | 41 +++++++++++++++- src/target/embeddedice.h | 50 +------------------- 12 files changed, 142 insertions(+), 240 deletions(-) diff --git a/src/helper/binarybuffer.c b/src/helper/binarybuffer.c index 6aa847aa..184d166c 100644 --- a/src/helper/binarybuffer.c +++ b/src/helper/binarybuffer.c @@ -49,43 +49,6 @@ const unsigned char bit_reverse_table256[] = 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF }; -int buf_set_u32(u8* buffer, unsigned int first, unsigned int num, u32 value) -{ - unsigned int i; - - if (!buffer) - return ERROR_INVALID_ARGUMENTS; - - for (i=first; i> (i-first))&1) == 1) - buffer[i/8] |= 1 << (i%8); - else - buffer[i/8] &= ~(1 << (i%8)); - } - - return ERROR_OK; -} - -u32 buf_get_u32(u8* buffer, unsigned int first, unsigned int num) -{ - u32 result = 0; - unsigned int i; - - if (!buffer) - { - LOG_ERROR("buffer not initialized"); - return 0; - } - - for (i=first; i>(i%8))&1) == 1) - result |= 1 << (i-first); - } - - return result; -} u8* buf_cpy(u8 *from, u8 *to, int size) { diff --git a/src/helper/binarybuffer.h b/src/helper/binarybuffer.h index 34a167d4..47ae193e 100644 --- a/src/helper/binarybuffer.h +++ b/src/helper/binarybuffer.h @@ -26,8 +26,47 @@ * flip_u32 inverses the bit order inside a 32-bit word (31..0 -> 0..31) */ -extern int buf_set_u32(u8* buffer, unsigned int first, unsigned int num, u32 value); -extern u32 buf_get_u32(u8* buffer, unsigned int first, unsigned int num); +/* inlining this will help show what fn that is taking time during profiling. */ +static __inline void buf_set_u32(u8* buffer, unsigned int first, unsigned int num, u32 value) +{ + if ((num==32)&&(first==0)) + { + buffer[3]=(value>>24)&0xff; + buffer[2]=(value>>16)&0xff; + buffer[1]=(value>>8)&0xff; + buffer[0]=(value>>0)&0xff; + } else + { + unsigned int i; + + for (i=first; i> (i-first))&1) == 1) + buffer[i/8] |= 1 << (i%8); + else + buffer[i/8] &= ~(1 << (i%8)); + } + } +} +static __inline u32 buf_get_u32(u8* buffer, unsigned int first, unsigned int num) +{ + if ((num==32)&&(first==0)) + { + return (((u32)buffer[3])<<24)|(((u32)buffer[2])<<16)|(((u32)buffer[1])<<8)|(((u32)buffer[0])<<0); + } else + { + u32 result = 0; + unsigned int i; + + for (i=first; i>(i%8))&1) == 1) + result |= 1 << (i-first); + } + + return result; + } +} extern u32 flip_u32(u32 value, unsigned int num); diff --git a/src/helper/log.h b/src/helper/log.h index 9929d420..6d61cf5f 100644 --- a/src/helper/log.h +++ b/src/helper/log.h @@ -80,7 +80,7 @@ extern int debug_level; #define LOG_DEBUG(expr ...) \ - log_printf_lf (LOG_LVL_DEBUG, __FILE__, __LINE__, __FUNCTION__, expr) + if (debug_level >= LOG_LVL_DEBUG) { log_printf_lf (LOG_LVL_DEBUG, __FILE__, __LINE__, __FUNCTION__, expr); } #define LOG_INFO(expr ...) \ log_printf_lf (LOG_LVL_INFO, __FILE__, __LINE__, __FUNCTION__, expr) diff --git a/src/jtag/jtag.c b/src/jtag/jtag.c index a2af81eb..82b9affc 100644 --- a/src/jtag/jtag.c +++ b/src/jtag/jtag.c @@ -667,8 +667,8 @@ int MINIDRIVER(interface_jtag_add_dr_scan)(int num_fields, scan_field_t *fields, void MINIDRIVER(interface_jtag_add_dr_out)(int device_num, int num_fields, - int *num_bits, - u32 *value, + const int *num_bits, + const u32 *value, enum tap_state end_state) { int i; diff --git a/src/jtag/jtag.h b/src/jtag/jtag.h index bb6818e3..d8092ca7 100644 --- a/src/jtag/jtag.h +++ b/src/jtag/jtag.h @@ -432,8 +432,8 @@ extern int jtag_verify_capture_ir; */ extern void interface_jtag_add_dr_out(int device, int num_fields, - int *num_bits, - u32 *value, + const int *num_bits, + const u32 *value, enum tap_state end_state); #endif @@ -442,8 +442,8 @@ extern void interface_jtag_add_dr_out(int device, static __inline__ void jtag_add_dr_out(int device, int num_fields, - int *num_bits, - u32 *value, + const int *num_bits, + const u32 *value, enum tap_state end_state) { if (end_state != -1) diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index 7c28dc08..c2c18baf 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -1850,11 +1850,14 @@ static void gdb_log_callback(void *priv, const char *file, int line, gdb_output_con(connection, string); } +/* Do not allocate this on the stack */ +char gdb_packet_buffer[GDB_BUFFER_SIZE]; + int gdb_input_inner(connection_t *connection) { gdb_service_t *gdb_service = connection->service->priv; target_t *target = gdb_service->target; - char packet[GDB_BUFFER_SIZE]; + char *packet=gdb_packet_buffer; int packet_size; int retval; gdb_connection_t *gdb_con = connection->priv; diff --git a/src/target/arm7_9_common.c b/src/target/arm7_9_common.c index 7d00a1d7..61484ccc 100644 --- a/src/target/arm7_9_common.c +++ b/src/target/arm7_9_common.c @@ -685,7 +685,7 @@ int arm7_9_poll(target_t *target) if (buf_get_u32(dbg_stat->value, EICE_DBG_STATUS_DBGACK, 1)) { -/* LOG_DEBUG("DBGACK set, dbg_state->value: 0x%x", buf_get_u32(dbg_stat->value, 0, 32)); */ +/* LOG_DEBUG("DBGACK set, dbg_state->value: 0x%x", buf_get_u32(dbg_stat->value, 0, 32));*/ if (target->state == TARGET_UNKNOWN) { target->state = TARGET_RUNNING; @@ -1009,15 +1009,7 @@ int arm7_9_debug_entry(target_t *target) if ((retval = jtag_execute_queue()) != ERROR_OK) { - switch (retval) - { - case ERROR_JTAG_QUEUE_FAILED: - LOG_ERROR("JTAG queue failed while writing EmbeddedICE control register"); - exit(-1); - break; - default: - break; - } + return retval; } if ((retval = arm7_9->examine_debug_reason(target)) != ERROR_OK) @@ -2068,6 +2060,12 @@ int arm7_9_write_memory(struct target_s *target, u32 address, u32 size, u32 coun return ERROR_OK; } +static const u32 dcc_code[] = +{ + /* MRC TST BNE MRC STR B */ + 0xee101e10, 0xe3110001, 0x0afffffc, 0xee111e10, 0xe4801004, 0xeafffff9 +}; + int arm7_9_bulk_write_memory(target_t *target, u32 address, u32 count, u8 *buffer) { armv4_5_common_t *armv4_5 = target->arch_info; @@ -2078,12 +2076,6 @@ int arm7_9_bulk_write_memory(target_t *target, u32 address, u32 count, u8 *buffe u32 pc = buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32); int i; - u32 dcc_code[] = - { - /* MRC TST BNE MRC STR B */ - 0xee101e10, 0xe3110001, 0x0afffffc, 0xee111e10, 0xe4801004, 0xeafffff9 - }; - if (!arm7_9->dcc_downloads) return target->type->write_memory(target, address, 4, count, buffer); diff --git a/src/target/arm7tdmi.c b/src/target/arm7tdmi.c index 53559a23..549f6a67 100644 --- a/src/target/arm7tdmi.c +++ b/src/target/arm7tdmi.c @@ -148,66 +148,30 @@ int arm7tdmi_examine_debug_reason(target_t *target) return ERROR_OK; } -/* put an instruction in the ARM7TDMI pipeline or write the data bus, and optionally read data */ -int arm7tdmi_clock_out(arm_jtag_t *jtag_info, u32 out, u32 *in, int breakpoint) +static int arm7tdmi_num_bits[]={1, 32}; +static __inline int arm7tdmi_clock_out_inner(arm_jtag_t *jtag_info, u32 out, int breakpoint) { - scan_field_t fields[2]; - u8 out_buf[4]; - u8 breakpoint_buf; - - buf_set_u32(out_buf, 0, 32, flip_u32(out, 32)); - buf_set_u32(&breakpoint_buf, 0, 1, breakpoint); + u32 values[2]={breakpoint, flip_u32(out, 32)}; + + jtag_add_dr_out(jtag_info->chain_pos, + 2, + arm7tdmi_num_bits, + values, + -1); + + jtag_add_runtest(0, -1); + return ERROR_OK; +} + +/* put an instruction in the ARM7TDMI pipeline or write the data bus, and optionally read data */ +static __inline int arm7tdmi_clock_out(arm_jtag_t *jtag_info, u32 out, u32 *deprecated, int breakpoint) +{ jtag_add_end_state(TAP_PD); arm_jtag_scann(jtag_info, 0x1); arm_jtag_set_instr(jtag_info, jtag_info->intest_instr, NULL); - fields[0].device = jtag_info->chain_pos; - fields[0].num_bits = 1; - fields[0].out_value = &breakpoint_buf; - fields[0].out_mask = NULL; - fields[0].in_value = NULL; - fields[0].in_check_value = NULL; - fields[0].in_check_mask = NULL; - fields[0].in_handler = NULL; - fields[0].in_handler_priv = NULL; - - fields[1].device = jtag_info->chain_pos; - fields[1].num_bits = 32; - fields[1].out_value = out_buf; - fields[1].out_mask = NULL; - fields[1].in_value = NULL; - if (in) - { - fields[1].in_handler = arm_jtag_buf_to_u32_flip; - fields[1].in_handler_priv = in; - } - else - { - fields[1].in_handler = NULL; - fields[1].in_handler_priv = NULL; - } - fields[1].in_check_value = NULL; - fields[1].in_check_mask = NULL; - - jtag_add_dr_scan(2, fields, -1); - - jtag_add_runtest(0, -1); - -#ifdef _DEBUG_INSTRUCTION_EXECUTION_ -{ - jtag_execute_queue(); - - if (in) - { - LOG_DEBUG("out: 0x%8.8x, in: 0x%8.8x", out, *in); - } - else - LOG_DEBUG("out: 0x%8.8x", out); -} -#endif - - return ERROR_OK; + return arm7tdmi_clock_out_inner(jtag_info, out, breakpoint); } /* clock the target, reading the databus */ @@ -534,17 +498,17 @@ void arm7tdmi_write_core_regs(target_t *target, u32 mask, u32 core_regs[16]) arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, mask & 0xffff, 0, 0), NULL, 0); /* fetch NOP, LDM in DECODE stage */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out_inner(jtag_info, ARMV4_5_NOP, 0); /* fetch NOP, LDM in EXECUTE stage (1st cycle) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out_inner(jtag_info, ARMV4_5_NOP, 0); for (i = 0; i <= 15; i++) { if (mask & (1 << i)) /* nothing fetched, LDM still in EXECUTE (1+i cycle) */ - arm7tdmi_clock_out(jtag_info, core_regs[i], NULL, 0); + arm7tdmi_clock_out_inner(jtag_info, core_regs[i], 0); } - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out_inner(jtag_info, ARMV4_5_NOP, 0); } @@ -644,19 +608,19 @@ void arm7tdmi_write_pc(target_t *target, u32 pc) */ arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, 0x8000, 0, 0), NULL, 0); /* fetch NOP, LDM in DECODE stage */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out_inner(jtag_info, ARMV4_5_NOP, 0); /* fetch NOP, LDM in EXECUTE stage (1st cycle) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out_inner(jtag_info, ARMV4_5_NOP, 0); /* nothing fetched, LDM in EXECUTE stage (1st cycle) load register */ - arm7tdmi_clock_out(jtag_info, pc, NULL, 0); + arm7tdmi_clock_out_inner(jtag_info, pc, 0); /* nothing fetched, LDM in EXECUTE stage (2nd cycle) load register */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out_inner(jtag_info, ARMV4_5_NOP, 0); /* nothing fetched, LDM in EXECUTE stage (3rd cycle) load register */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out_inner(jtag_info, ARMV4_5_NOP, 0); /* fetch NOP, LDM in EXECUTE stage (4th cycle) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out_inner(jtag_info, ARMV4_5_NOP, 0); /* fetch NOP, LDM in EXECUTE stage (5th cycle) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out_inner(jtag_info, ARMV4_5_NOP, 0); } void arm7tdmi_branch_resume(target_t *target) @@ -667,7 +631,7 @@ void arm7tdmi_branch_resume(target_t *target) arm_jtag_t *jtag_info = &arm7_9->jtag_info; arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); - arm7tdmi_clock_out(jtag_info, ARMV4_5_B(0xfffffa, 0), NULL, 0); + arm7tdmi_clock_out_inner(jtag_info, ARMV4_5_B(0xfffffa, 0), 0); } diff --git a/src/target/arm_jtag.c b/src/target/arm_jtag.c index 02dc2e19..77c81871 100644 --- a/src/target/arm_jtag.c +++ b/src/target/arm_jtag.c @@ -40,10 +40,11 @@ int arm_jtag_set_instr(arm_jtag_t *jtag_info, u32 new_instr, in_handler_t handl if (buf_get_u32(device->cur_instr, 0, device->ir_length) != new_instr) { scan_field_t field; + u8 t[4]; field.device = jtag_info->chain_pos; field.num_bits = device->ir_length; - field.out_value = calloc(CEIL(field.num_bits, 8), 1); + field.out_value = t; buf_set_u32(field.out_value, 0, field.num_bits, new_instr); field.out_mask = NULL; field.in_value = NULL; @@ -52,9 +53,6 @@ int arm_jtag_set_instr(arm_jtag_t *jtag_info, u32 new_instr, in_handler_t handl field.in_handler = handler; field.in_handler_priv = NULL; jtag_add_ir_scan(1, &field, -1); - - - free(field.out_value); } return ERROR_OK; @@ -64,32 +62,20 @@ int arm_jtag_scann(arm_jtag_t *jtag_info, u32 new_scan_chain) { if(jtag_info->cur_scan_chain != new_scan_chain) { -#ifdef _ARM_JTAG_SCAN_N_CHECK_ - u8 scan_n_check_value = 1 << (jtag_info->scann_size - 1); -#endif - scan_field_t field; - - field.device = jtag_info->chain_pos; - field.num_bits = jtag_info->scann_size; - field.out_value = calloc(CEIL(field.num_bits, 8), 1); - buf_set_u32(field.out_value, 0, field.num_bits, new_scan_chain); - field.out_mask = NULL; - field.in_value = NULL; -#ifdef _ARM_JTAG_SCAN_N_CHECK_ -#error FIX!!! this is broken, scan_n_check_value goes out of scope. - jtag_set_check_value(&field, &scan_n_check_value, NULL, NULL, NULL); -#else - field.in_handler = NULL; - field.in_handler_priv = NULL; -#endif + u32 values[1]; + int num_bits[1]; + values[0]=new_scan_chain; + num_bits[0]=jtag_info->scann_size; arm_jtag_set_instr(jtag_info, jtag_info->scann_instr, NULL); - jtag_add_dr_scan(1, &field, -1); + jtag_add_dr_out(jtag_info->chain_pos, + 1, + num_bits, + values, + -1); jtag_info->cur_scan_chain = new_scan_chain; - - free(field.out_value); } return ERROR_OK; diff --git a/src/target/armv4_5.c b/src/target/armv4_5.c index 07033bfb..cb5f8758 100644 --- a/src/target/armv4_5.c +++ b/src/target/armv4_5.c @@ -169,42 +169,6 @@ reg_t armv4_5_gdb_dummy_fps_reg = "GDB dummy floating-point status register", armv4_5_gdb_dummy_fps_value, 0, 1, 32, NULL, 0, NULL, 0 }; -/* map psr mode bits to linear number */ -int armv4_5_mode_to_number(enum armv4_5_mode mode) -{ - switch (mode) - { - case ARMV4_5_MODE_USR: return 0; break; - case ARMV4_5_MODE_FIQ: return 1; break; - case ARMV4_5_MODE_IRQ: return 2; break; - case ARMV4_5_MODE_SVC: return 3; break; - case ARMV4_5_MODE_ABT: return 4; break; - case ARMV4_5_MODE_UND: return 5; break; - case ARMV4_5_MODE_SYS: return 6; break; - case ARMV4_5_MODE_ANY: return 0; break; /* map MODE_ANY to user mode */ - default: - LOG_ERROR("invalid mode value encountered"); - return -1; - } -} - -/* map linear number to mode bits */ -enum armv4_5_mode armv4_5_number_to_mode(int number) -{ - switch(number) - { - case 0: return ARMV4_5_MODE_USR; break; - case 1: return ARMV4_5_MODE_FIQ; break; - case 2: return ARMV4_5_MODE_IRQ; break; - case 3: return ARMV4_5_MODE_SVC; break; - case 4: return ARMV4_5_MODE_ABT; break; - case 5: return ARMV4_5_MODE_UND; break; - case 6: return ARMV4_5_MODE_SYS; break; - default: - LOG_ERROR("mode index out of bounds"); - return -1; - } -}; int armv4_5_get_core_reg(reg_t *reg) { diff --git a/src/target/armv4_5.h b/src/target/armv4_5.h index f04a4113..51f42b9b 100644 --- a/src/target/armv4_5.h +++ b/src/target/armv4_5.h @@ -22,6 +22,7 @@ #include "register.h" #include "target.h" +#include "log.h" typedef enum armv4_5_mode { @@ -95,8 +96,44 @@ typedef struct armv4_5_core_reg_s } armv4_5_core_reg_t; extern reg_cache_t* armv4_5_build_reg_cache(target_t *target, armv4_5_common_t *armv4_5_common); -extern enum armv4_5_mode armv4_5_number_to_mode(int number); -extern int armv4_5_mode_to_number(enum armv4_5_mode mode); + +/* map psr mode bits to linear number */ +static __inline int armv4_5_mode_to_number(enum armv4_5_mode mode) +{ + switch (mode) + { + case ARMV4_5_MODE_USR: return 0; break; + case ARMV4_5_MODE_FIQ: return 1; break; + case ARMV4_5_MODE_IRQ: return 2; break; + case ARMV4_5_MODE_SVC: return 3; break; + case ARMV4_5_MODE_ABT: return 4; break; + case ARMV4_5_MODE_UND: return 5; break; + case ARMV4_5_MODE_SYS: return 6; break; + case ARMV4_5_MODE_ANY: return 0; break; /* map MODE_ANY to user mode */ + default: + LOG_ERROR("invalid mode value encountered"); + return -1; + } +} + +/* map linear number to mode bits */ +static __inline enum armv4_5_mode armv4_5_number_to_mode(int number) +{ + switch(number) + { + case 0: return ARMV4_5_MODE_USR; break; + case 1: return ARMV4_5_MODE_FIQ; break; + case 2: return ARMV4_5_MODE_IRQ; break; + case 3: return ARMV4_5_MODE_SVC; break; + case 4: return ARMV4_5_MODE_ABT; break; + case 5: return ARMV4_5_MODE_UND; break; + case 6: return ARMV4_5_MODE_SYS; break; + default: + LOG_ERROR("mode index out of bounds"); + return -1; + } +}; + extern int armv4_5_arch_state(struct target_s *target); extern int armv4_5_get_gdb_reg_list(target_t *target, reg_t **reg_list[], int *reg_list_size); diff --git a/src/target/embeddedice.h b/src/target/embeddedice.h index 8452fc33..971f5fb7 100644 --- a/src/target/embeddedice.h +++ b/src/target/embeddedice.h @@ -105,66 +105,20 @@ extern int embeddedice_handshake(arm_jtag_t *jtag_info, int hsbit, u32 timeout); /* If many embeddedice_write_reg() follow eachother, then the >1 invocations can be this faster version of * embeddedice_write_reg */ +static const int embeddedice_num_bits[]={32,5,1}; static __inline__ void embeddedice_write_reg_inner(int chain_pos, int reg_addr, u32 value) { -#if 1 u32 values[3]; - int num_bits[3]; values[0]=value; - num_bits[0]=32; values[1]=reg_addr; - num_bits[1]=5; values[2]=1; - num_bits[2]=1; jtag_add_dr_out(chain_pos, 3, - num_bits, + embeddedice_num_bits, values, -1); -#else - scan_field_t fields[3]; - u8 field0_out[4]; - u8 field1_out[1]; - u8 field2_out[1]; - - fields[0].device = ice_reg->jtag_info->chain_pos; - fields[0].num_bits = 32; - fields[0].out_value = field0_out; - buf_set_u32(fields[0].out_value, 0, 32, value); - fields[0].out_mask = NULL; - fields[0].in_value = NULL; - fields[0].in_check_value = NULL; - fields[0].in_check_mask = NULL; - fields[0].in_handler = NULL; - fields[0].in_handler_priv = NULL; - - fields[1].device = ice_reg->jtag_info->chain_pos; - fields[1].num_bits = 5; - fields[1].out_value = field1_out; - buf_set_u32(fields[1].out_value, 0, 5, reg_addr); - fields[1].out_mask = NULL; - fields[1].in_value = NULL; - fields[1].in_check_value = NULL; - fields[1].in_check_mask = NULL; - fields[1].in_handler = NULL; - fields[1].in_handler_priv = NULL; - - fields[2].device = ice_reg->jtag_info->chain_pos; - fields[2].num_bits = 1; - fields[2].out_value = field2_out; - buf_set_u32(fields[2].out_value, 0, 1, 1); - fields[2].out_mask = NULL; - fields[2].in_value = NULL; - fields[2].in_check_value = NULL; - fields[2].in_check_mask = NULL; - fields[2].in_handler = NULL; - fields[2].in_handler_priv = NULL; - - jtag_add_dr_scan(3, fields, -1); - -#endif } -- 2.39.5