CORE_ADDR obsd_skip_solib_resolver (struct gdbarch *gdbarch, CORE_ADDR pc) { struct bound_minimal_symbol msym; msym = lookup_minimal_symbol("_dl_bind", NULL, NULL); if (msym.minsym && BMSYMBOL_VALUE_ADDRESS (msym) == pc) return frame_unwind_caller_pc (get_current_frame ()); else return find_solib_trampoline_target (get_current_frame (), pc); }
struct frame_info * block_innermost_frame (struct block *block) { struct frame_info *frame; CORE_ADDR start; CORE_ADDR end; CORE_ADDR calling_pc; if (block == NULL) return NULL; start = BLOCK_START (block); end = BLOCK_END (block); frame = get_current_frame (); while (frame != NULL) { calling_pc = get_frame_address_in_block (frame); if (calling_pc >= start && calling_pc < end) return frame; frame = get_prev_frame (frame); } return NULL; }
CORE_ADDR glibc_skip_solib_resolver (struct gdbarch *gdbarch, CORE_ADDR pc) { /* The GNU dynamic linker is part of the GNU C library, and is used by all GNU systems (GNU/Hurd, GNU/Linux). An unresolved PLT entry points to "_dl_runtime_resolve", which calls "fixup" to patch the PLT, and then passes control to the function. We look for the symbol `_dl_runtime_resolve', and find `fixup' in the same objfile. If we are at the entry point of `fixup', then we set a breakpoint at the return address (at the top of the stack), and continue. It's kind of gross to do all these checks every time we're called, since they don't change once the executable has gotten started. But this is only a temporary hack --- upcoming versions of GNU/Linux will provide a portable, efficient interface for debugging programs that use shared libraries. */ struct objfile *objfile; struct minimal_symbol *resolver = find_minsym_and_objfile ("_dl_runtime_resolve", &objfile); if (resolver) { struct minimal_symbol *fixup = lookup_minimal_symbol ("fixup", NULL, objfile); if (fixup && SYMBOL_VALUE_ADDRESS (fixup) == pc) return frame_pc_unwind (get_current_frame ()); } return 0; }
enum mi_cmd_result mi_cmd_stack_info_depth (char *command, char **argv, int argc) { int frame_high; int i; struct frame_info *fi; if (!target_has_stack) error ("mi_cmd_stack_info_depth: No stack."); if (argc > 1) error ("mi_cmd_stack_info_depth: Usage: [MAX_DEPTH]"); if (argc == 1) frame_high = atoi (argv[0]); else /* Called with no arguments, it means we want the real depth of the stack. */ frame_high = -1; for (i = 0, fi = get_current_frame (); fi && (i < frame_high || frame_high == -1); i++, fi = get_prev_frame (fi)) QUIT; ui_out_field_int (uiout, "depth", i); return MI_CMD_DONE; }
struct frame_info * block_innermost_frame (const struct block *block) { struct frame_info *frame; CORE_ADDR start; CORE_ADDR end; if (block == NULL) return NULL; start = BLOCK_START (block); end = BLOCK_END (block); frame = get_selected_frame_if_set (); if (frame == NULL) frame = get_current_frame (); while (frame != NULL) { struct block *frame_block = get_frame_block (frame, NULL); if (frame_block != NULL && contained_in (frame_block, block)) return frame; frame = get_prev_frame (frame); } return NULL; }
static CORE_ADDR skip_hurd_resolver (CORE_ADDR pc) { /* The HURD dynamic linker is part of the GNU C library, so many GNU/Linux distributions use it. (All ELF versions, as far as I know.) An unresolved PLT entry points to "_dl_runtime_resolve", which calls "fixup" to patch the PLT, and then passes control to the function. We look for the symbol `_dl_runtime_resolve', and find `fixup' in the same objfile. If we are at the entry point of `fixup', then we set a breakpoint at the return address (at the top of the stack), and continue. It's kind of gross to do all these checks every time we're called, since they don't change once the executable has gotten started. But this is only a temporary hack --- upcoming versions of GNU/Linux will provide a portable, efficient interface for debugging programs that use shared libraries. */ struct objfile *objfile; struct minimal_symbol *resolver = find_minsym_and_objfile ("_dl_runtime_resolve", &objfile); if (resolver) { struct minimal_symbol *fixup = lookup_minimal_symbol ("fixup", NULL, objfile); if (fixup && SYMBOL_VALUE_ADDRESS (fixup) == pc) return (SAVED_PC_AFTER_CALL (get_current_frame ())); } return 0; }
static int amd64_linux_siginfo_fixup (siginfo_t *native, gdb_byte *inf, int direction) { struct gdbarch *gdbarch = get_frame_arch (get_current_frame ()); /* Is the inferior 32-bit? If so, then do fixup the siginfo object. */ if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32) { gdb_assert (sizeof (siginfo_t) == sizeof (compat_siginfo_t)); if (direction == 0) compat_siginfo_from_siginfo ((struct compat_siginfo *) inf, native); else siginfo_from_compat_siginfo (native, (struct compat_siginfo *) inf); return 1; } /* No fixup for native x32 GDB. */ else if (gdbarch_addr_bit (gdbarch) == 32 && sizeof (void *) == 8) { gdb_assert (sizeof (siginfo_t) == sizeof (compat_x32_siginfo_t)); if (direction == 0) compat_x32_siginfo_from_siginfo ((struct compat_x32_siginfo *) inf, native); else siginfo_from_compat_x32_siginfo (native, (struct compat_x32_siginfo *) inf); return 1; } else return 0; }
void generic_push_dummy_frame (void) { struct dummy_frame *dummy_frame; CORE_ADDR fp = (get_current_frame ())->frame; /* check to see if there are stale dummy frames, perhaps left over from when a longjump took us out of a function that was called by the debugger */ dummy_frame = dummy_frame_stack; while (dummy_frame) if (INNER_THAN (dummy_frame->fp, fp)) /* stale -- destroy! */ { dummy_frame_stack = dummy_frame->next; xfree (dummy_frame->registers); xfree (dummy_frame); dummy_frame = dummy_frame_stack; } else dummy_frame = dummy_frame->next; dummy_frame = xmalloc (sizeof (struct dummy_frame)); dummy_frame->registers = xmalloc (REGISTER_BYTES); dummy_frame->pc = read_pc (); dummy_frame->sp = read_sp (); dummy_frame->top = dummy_frame->sp; dummy_frame->fp = fp; read_register_bytes (0, dummy_frame->registers, REGISTER_BYTES); dummy_frame->next = dummy_frame_stack; dummy_frame_stack = dummy_frame; }
/* ** _tuiUpdateLocation_command(). ** Command to update the display with the current execution point */ static void _tuiUpdateLocation_command (char *arg, int fromTTY) { #ifndef TRY extern void frame_command (char *, int); frame_command ("0", FALSE); #else struct frame_info *curFrame; /* Obtain the current execution point */ if ((curFrame = get_current_frame ()) != (struct frame_info *) NULL) { struct frame_info *frame; int curLevel = 0; for (frame = get_prev_frame (curLevel); (frame != (struct frame_info *) NULL && (frame != curFrame)); frame = get_prev_frame (frame)) curLevel++; if (curFrame != (struct frame_info *) NULL) print_frame_info (frame, curLevel, 0, 1); } #endif return; } /* _tuiUpdateLocation_command */
static int wrap_get_current_frame (char *opaque_arg) { struct gdb_wrapper_arguments **args = (struct gdb_wrapper_arguments **) opaque_arg; (*args)->result.ptr = get_current_frame (); return 1; }
/* Print a list of the stack frames. Args can be none, in which case we want to print the whole backtrace, or a pair of numbers specifying the frame numbers at which to start and stop the display. If the two numbers are equal, a single frame will be displayed. */ enum mi_cmd_result mi_cmd_stack_list_frames (char *command, char **argv, int argc) { int frame_low; int frame_high; int i; struct cleanup *cleanup_stack; struct frame_info *fi; if (!target_has_stack) error ("mi_cmd_stack_list_frames: No stack."); if (argc > 2 || argc == 1) error ("mi_cmd_stack_list_frames: Usage: [FRAME_LOW FRAME_HIGH]"); if (argc == 2) { frame_low = atoi (argv[0]); frame_high = atoi (argv[1]); } else { /* Called with no arguments, it means we want the whole backtrace. */ frame_low = -1; frame_high = -1; } /* Let's position fi on the frame at which to start the display. Could be the innermost frame if the whole stack needs displaying, or if frame_low is 0. */ for (i = 0, fi = get_current_frame (); fi && i < frame_low; i++, fi = get_prev_frame (fi)); if (fi == NULL) error ("mi_cmd_stack_list_frames: Not enough frames in stack."); cleanup_stack = make_cleanup_ui_out_list_begin_end (uiout, "stack"); /* Now let;s print the frames up to frame_high, or until there are frames in the stack. */ for (; fi && (i <= frame_high || frame_high == -1); i++, fi = get_prev_frame (fi)) { QUIT; /* Print the location and the address always, even for level 0. args == 0: don't print the arguments. */ print_frame_info (fi, 1, LOC_AND_ADDRESS, 0 /* args */ ); } do_cleanups (cleanup_stack); if (i < frame_high) error ("mi_cmd_stack_list_frames: Not enough frames in stack."); return MI_CMD_DONE; }
static void fbsd_convert_siginfo (siginfo_t *si) { #ifdef __LP64__ struct gdbarch *gdbarch = get_frame_arch (get_current_frame ()); /* Is the inferior 32-bit? If not, nothing to do. */ if (gdbarch_long_bit (gdbarch) != 32) return; struct siginfo32 si32; si32.si_signo = si->si_signo; si32.si_errno = si->si_errno; si32.si_code = si->si_code; si32.si_pid = si->si_pid; si32.si_uid = si->si_uid; si32.si_status = si->si_status; si32.si_addr = (uintptr_t) si->si_addr; /* If sival_ptr is being used instead of sival_int on a big-endian platform, then sival_int will be zero since it holds the upper 32-bits of the pointer value. */ #if _BYTE_ORDER == _BIG_ENDIAN if (si->si_value.sival_int == 0) si32.si_value.sival_ptr = (uintptr_t) si->si_value.sival_ptr; else si32.si_value.sival_int = si->si_value.sival_int; #else si32.si_value.sival_int = si->si_value.sival_int; #endif /* Always copy the spare fields and then possibly overwrite them for signal-specific or code-specific fields. */ si32._reason.__spare__.__spare1__ = si->_reason.__spare__.__spare1__; for (int i = 0; i < 7; i++) si32._reason.__spare__.__spare2__[i] = si->_reason.__spare__.__spare2__[i]; switch (si->si_signo) { case SIGILL: case SIGFPE: case SIGSEGV: case SIGBUS: si32.si_trapno = si->si_trapno; break; } switch (si->si_code) { case SI_TIMER: si32.si_timerid = si->si_timerid; si32.si_overrun = si->si_overrun; break; case SI_MESGQ: si32.si_mqd = si->si_mqd; break; } memcpy(si, &si32, sizeof (si32)); #endif }
/* Print a list of the arguments for the current frame. With argument of 0, print only the names, with argument of 1 print also the values. */ enum mi_cmd_result mi_cmd_stack_list_args (char *command, char **argv, int argc) { int frame_low; int frame_high; int i; struct frame_info *fi; struct cleanup *cleanup_stack_args; if (argc < 1 || argc > 3 || argc == 2) error ("mi_cmd_stack_list_args: Usage: PRINT_VALUES [FRAME_LOW FRAME_HIGH]"); if (argc == 3) { frame_low = atoi (argv[1]); frame_high = atoi (argv[2]); } else { /* Called with no arguments, it means we want args for the whole backtrace. */ frame_low = -1; frame_high = -1; } /* Let's position fi on the frame at which to start the display. Could be the innermost frame if the whole stack needs displaying, or if frame_low is 0. */ for (i = 0, fi = get_current_frame (); fi && i < frame_low; i++, fi = get_prev_frame (fi)); if (fi == NULL) error ("mi_cmd_stack_list_args: Not enough frames in stack."); cleanup_stack_args = make_cleanup_ui_out_list_begin_end (uiout, "stack-args"); /* Now let's print the frames up to frame_high, or until there are frames in the stack. */ for (; fi && (i <= frame_high || frame_high == -1); i++, fi = get_prev_frame (fi)) { struct cleanup *cleanup_frame; QUIT; cleanup_frame = make_cleanup_ui_out_tuple_begin_end (uiout, "frame"); ui_out_field_int (uiout, "level", i); list_args_or_locals (0, atoi (argv[0]), fi); do_cleanups (cleanup_frame); } do_cleanups (cleanup_stack_args); if (i < frame_high) error ("mi_cmd_stack_list_args: Not enough frames in stack."); return MI_CMD_DONE; }
void Sprite::draw(float x, float y) { time = SDL_GetTicks(); unsigned int frame = get_current_frame(); if (frame < surfaces.size()) surfaces[frame]->draw(x - x_hotspot, y - y_hotspot); }
void Sprite::draw_part(float sx, float sy, float x, float y, float w, float h) { time = SDL_GetTicks(); unsigned int frame = get_current_frame(); if (frame < surfaces.size()) surfaces[frame]->draw_part(sx, sy, x - x_hotspot, y - y_hotspot, w, h); }
void generic_pop_current_frame (void (*popper) (struct frame_info * frame)) { struct frame_info *frame = get_current_frame (); if (PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame)) generic_pop_dummy_frame (); else (*popper) (frame); }
CORE_ADDR sol2_skip_solib_resolver (struct gdbarch *gdbarch, CORE_ADDR pc) { struct bound_minimal_symbol msym; msym = lookup_minimal_symbol("elf_bndr", NULL, NULL); if (msym.minsym && BMSYMBOL_VALUE_ADDRESS (msym) == pc) return frame_unwind_caller_pc (get_current_frame ()); return 0; }
void reinit_frame_cache (void) { flush_cached_frames (); /* FIXME: The inferior_ptid test is wrong if there is a corefile. */ if (PIDGET (inferior_ptid) != 0) { select_frame (get_current_frame (), 0); } }
static CORE_ADDR mips_linux_skip_resolver (struct gdbarch *gdbarch, CORE_ADDR pc) { struct minimal_symbol *resolver; resolver = lookup_minimal_symbol ("__dl_runtime_resolve", NULL, NULL); if (resolver && SYMBOL_VALUE_ADDRESS (resolver) == pc) return frame_unwind_caller_pc (get_current_frame ()); return glibc_skip_solib_resolver (gdbarch, pc); }
static size_t fbsd_siginfo_size () { #ifdef __LP64__ struct gdbarch *gdbarch = get_frame_arch (get_current_frame ()); /* Is the inferior 32-bit? If so, use the 32-bit siginfo size. */ if (gdbarch_long_bit (gdbarch) == 32) return sizeof (struct siginfo32); #endif return sizeof (siginfo_t); }
/* Show the registers of the given group in the data window and refresh the window. */ void tui_show_registers (struct reggroup *group) { enum tui_status ret = TUI_FAILURE; struct tui_data_info *display_info; /* Make sure the curses mode is enabled. */ tui_enable (); /* Make sure the register window is visible. If not, select an appropriate layout. */ if (TUI_DATA_WIN == NULL || !TUI_DATA_WIN->generic.is_visible) tui_set_layout_for_display_command (DATA_NAME); display_info = &TUI_DATA_WIN->detail.data_display_info; if (group == 0) group = general_reggroup; /* Say that registers should be displayed, even if there is a problem. */ display_info->display_regs = TRUE; if (target_has_registers && target_has_stack && target_has_memory) { ret = tui_show_register_group (current_gdbarch, group, get_current_frame (), group == display_info->current_group); } if (ret == TUI_FAILURE) { display_info->current_group = 0; tui_erase_data_content (NO_REGS_STRING); } else { int i; /* Clear all notation of changed values */ for (i = 0; i < display_info->regs_content_count; i++) { struct tui_gen_win_info *data_item_win; struct tui_win_element *win; data_item_win = &display_info->regs_content[i] ->which_element.data_window; win = (struct tui_win_element *) data_item_win->content[0]; win->which_element.data.highlight = FALSE; } display_info->current_group = group; tui_display_all_data (); } }
/* Generic prepare_to_proceed(). This one should be suitable for most targets that support threads. */ int generic_prepare_to_proceed (int select_it) { ptid_t wait_ptid; struct target_waitstatus wait_status; /* Get the last target status returned by target_wait(). */ get_last_target_status (&wait_ptid, &wait_status); /* Make sure we were stopped either at a breakpoint, or because of a Ctrl-C. */ if (wait_status.kind != TARGET_WAITKIND_STOPPED || (wait_status.value.sig != TARGET_SIGNAL_TRAP && wait_status.value.sig != TARGET_SIGNAL_INT)) { return 0; } if (!ptid_equal (wait_ptid, minus_one_ptid) && !ptid_equal (inferior_ptid, wait_ptid)) { /* Switched over from WAIT_PID. */ CORE_ADDR wait_pc = read_pc_pid (wait_ptid); if (wait_pc != read_pc ()) { if (select_it) { /* Switch back to WAIT_PID thread. */ inferior_ptid = wait_ptid; /* FIXME: This stuff came from switch_to_thread() in thread.c (which should probably be a public function). */ flush_cached_frames (); registers_changed (); stop_pc = wait_pc; select_frame (get_current_frame ()); } /* We return 1 to indicate that there is a breakpoint here, so we need to step over it before continuing to avoid hitting it straight away. */ if (breakpoint_here_p (wait_pc)) { return 1; } } } return 0; }
static void store_regs (struct type *regs_type, CORE_ADDR regs_base) { struct gdbarch *gdbarch = target_gdbarch (); struct regcache *regcache = get_thread_regcache (inferior_ptid); int fieldno; for (fieldno = 0; fieldno < TYPE_NFIELDS (regs_type); fieldno++) { const char *reg_name = TYPE_FIELD_NAME (regs_type, fieldno); ULONGEST reg_bitpos = TYPE_FIELD_BITPOS (regs_type, fieldno); ULONGEST reg_bitsize = TYPE_FIELD_BITSIZE (regs_type, fieldno); ULONGEST reg_offset; struct type *reg_type = check_typedef (TYPE_FIELD_TYPE (regs_type, fieldno)); ULONGEST reg_size = TYPE_LENGTH (reg_type); int regnum; struct value *regval; CORE_ADDR inferior_addr; if (strcmp (reg_name, COMPILE_I_SIMPLE_REGISTER_DUMMY) == 0) continue; if ((reg_bitpos % 8) != 0 || reg_bitsize != 0) error (_("Invalid register \"%s\" position %s bits or size %s bits"), reg_name, pulongest (reg_bitpos), pulongest (reg_bitsize)); reg_offset = reg_bitpos / 8; if (TYPE_CODE (reg_type) != TYPE_CODE_INT && TYPE_CODE (reg_type) != TYPE_CODE_PTR) error (_("Invalid register \"%s\" type code %d"), reg_name, TYPE_CODE (reg_type)); regnum = compile_register_name_demangle (gdbarch, reg_name); regval = value_from_register (reg_type, regnum, get_current_frame ()); if (value_optimized_out (regval)) error (_("Register \"%s\" is optimized out."), reg_name); if (!value_entirely_available (regval)) error (_("Register \"%s\" is not available."), reg_name); inferior_addr = regs_base + reg_offset; if (0 != target_write_memory (inferior_addr, value_contents (regval), reg_size)) error (_("Cannot write register \"%s\" to inferior memory at %s."), reg_name, paddress (gdbarch, inferior_addr)); } }
static CORE_ADDR mips_linux_syscall_next_pc (struct frame_info *frame) { CORE_ADDR pc = get_frame_pc (frame); ULONGEST v0 = get_frame_register_unsigned (frame, MIPS_V0_REGNUM); /* If we are about to make a sigreturn syscall, use the unwinder to decode the signal frame. */ if (v0 == MIPS_NR_sigreturn || v0 == MIPS_NR_rt_sigreturn || v0 == MIPS_NR_N64_rt_sigreturn || v0 == MIPS_NR_N32_rt_sigreturn) return frame_unwind_caller_pc (get_current_frame ()); return pc + 4; }
static void metrowerks_step (CORE_ADDR range_start, CORE_ADDR range_stop, int step_into) { struct frame_info *frame = NULL; CORE_ADDR pc = 0; /* When single stepping in assembly, the plugin passes (start + 1) as the stop address. Round the stop address up to the next valid instruction */ if ((range_stop & ~0x3) != range_stop) range_stop = ((range_stop + 4) & ~0x3); pc = read_pc(); if (range_start >= range_stop) error ("invalid step range (the stop address must be greater than the start address)"); if (pc < range_start) error ("invalid step range ($pc is 0x%lx, less than the stop address of 0x%lx)", (unsigned long) pc, (unsigned long) range_start); if (pc == range_stop) error ("invalid step range ($pc is 0x%lx, equal to the stop address of 0x%lx)", (unsigned long) pc, (unsigned long) range_stop); if (pc > range_stop) error ("invalid step range ($pc is 0x%lx, greater than the stop address of 0x%lx)", (unsigned long) pc, (unsigned long) range_stop); clear_proceed_status (); frame = get_current_frame (); if (frame == NULL) error ("No current frame"); step_frame_address = FRAME_FP (frame); step_sp = read_sp (); step_range_start = range_start; step_range_end = range_stop; step_over_calls = step_into ? STEP_OVER_NONE : STEP_OVER_ALL; step_multi = 0; metrowerks_stepping = 1; proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1); make_exec_cleanup (metrowerks_stepping_cleanup, NULL); }
void h8500_pop_frame (void) { unsigned regnum; struct frame_saved_regs fsr; struct frame_info *frame = get_current_frame (); get_frame_saved_regs (frame, &fsr); for (regnum = 0; regnum < 8; regnum++) { if (fsr.regs[regnum]) write_register (regnum, read_memory_short (fsr.regs[regnum])); flush_cached_frames (); } }
unsigned int ldp::get_adjusted_current_frame() { #ifdef DEBUG // this function assumes that the disc's FPS is in line with vblank assert((g_game->get_disc_fpks() << 1) == VBLANKS_PER_KILOSECOND); #endif // DEBUG // because get_current_frame() is a virtual function unsigned int uResult = get_current_frame(); // if the disc is playing and we've already displayed the 2nd field of the frame, then advance the current frame if ((m_status == LDP_PLAYING) && (m_uVblankMiniCount != 0)) { ++uResult; } return uResult; }
static int derive_stack_segment (bfd_vma *bottom, bfd_vma *top) { struct frame_info *fi, *tmp_fi; gdb_assert (bottom); gdb_assert (top); /* Can't succeed without stack and registers. */ if (!target_has_stack || !target_has_registers) return 0; /* Can't succeed without current frame. */ fi = get_current_frame (); if (fi == NULL) return 0; /* Save frame pointer of TOS frame. */ *top = get_frame_base (fi); /* If current stack pointer is more "inner", use that instead. */ if (gdbarch_inner_than (current_gdbarch, get_frame_sp (fi), *top)) *top = get_frame_sp (fi); /* Find prev-most frame. */ while ((tmp_fi = get_prev_frame (fi)) != NULL) fi = tmp_fi; /* Save frame pointer of prev-most frame. */ *bottom = get_frame_base (fi); /* Now canonicalize their order, so that BOTTOM is a lower address (as opposed to a lower stack frame). */ if (*bottom > *top) { bfd_vma tmp_vma; tmp_vma = *top; *top = *bottom; *bottom = tmp_vma; } return 1; }
void m68k_pop_frame () { register FRAME frame = get_current_frame (); register CORE_ADDR fp; register int regnum; struct frame_saved_regs fsr; struct frame_info *fi; char raw_buffer[12]; fi = get_frame_info (frame); fp = fi -> frame; get_frame_saved_regs (fi, &fsr); #if defined (HAVE_68881) for (regnum = FP0_REGNUM + 7 ; regnum >= FP0_REGNUM ; regnum--) { if (fsr.regs[regnum]) { read_memory (fsr.regs[regnum], raw_buffer, 12); write_register_bytes (REGISTER_BYTE (regnum), raw_buffer, 12); } } #endif for (regnum = FP_REGNUM - 1 ; regnum >= 0 ; regnum--) { if (fsr.regs[regnum]) { write_register (regnum, read_memory_integer (fsr.regs[regnum], 4)); } } if (fsr.regs[PS_REGNUM]) { write_register (PS_REGNUM, read_memory_integer (fsr.regs[PS_REGNUM], 4)); } write_register (FP_REGNUM, read_memory_integer (fp, 4)); write_register (PC_REGNUM, read_memory_integer (fp + 4, 4)); write_register (SP_REGNUM, fp + 8); flush_cached_frames (); set_current_frame (create_new_frame (read_register (FP_REGNUM), read_pc ())); }
static int get_time_analog(void) { int frame, start_frame, length; int track = cdda_playing.track; if (is_paused && pause_time != -1) return pause_time; frame = get_current_frame(); if (frame == -1) return -1; start_frame = LBA(cdda_playing.cd_toc.track[track]); length = cdda_calculate_track_length(&cdda_playing.cd_toc, track); if (frame - start_frame >= length - 20) /* 20 seems to work better */ return -1; return ((frame - start_frame) * 1000) / 75; }