From 611889b118c32aa97dee2c54dbb278aa0f5837ff Mon Sep 17 00:00:00 2001 From: Kevin Buettner Date: Fri, 2 Sep 2005 22:53:35 +0000 Subject: [PATCH] * mn10300-linux-tdep.c: New file. * Makefile.in (mn10300-linux-tdep.o): New Makefile target. * config/mn10300/linux.mt (TDEPFILES): Add mn10300-linux-tdep.o and corelow.o. * mn10300-tdep.h (E_E8_REGNUM, E_E9_REGNUM, E_E10_REGNUM) (E_FPCR_REGNUM, E_FS0_REGNUM): New constants. --- gdb/ChangeLog | 10 + gdb/Makefile.in | 2 + gdb/config/mn10300/linux.mt | 2 +- gdb/mn10300-linux-tdep.c | 486 ++++++++++++++++++++++++++++++++++++++++++++ gdb/mn10300-tdep.h | 7 +- 5 files changed, 504 insertions(+), 3 deletions(-) create mode 100644 gdb/mn10300-linux-tdep.c diff --git a/gdb/ChangeLog b/gdb/ChangeLog index e11f4aa..d3715bc 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,13 @@ +2005-08-15 Michael Snyder + Kevin Buettner + + * mn10300-linux-tdep.c: New file. + * Makefile.in (mn10300-linux-tdep.o): New Makefile target. + * config/mn10300/linux.mt (TDEPFILES): Add mn10300-linux-tdep.o and + corelow.o. + * mn10300-tdep.h (E_E8_REGNUM, E_E9_REGNUM, E_E10_REGNUM) + (E_FPCR_REGNUM, E_FS0_REGNUM): New constants. + 2005-09-02 Mark Kettenis * infcall.c (call_function_by_hand): Remove code handling diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 6f86a64..92c8ad1 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -2305,6 +2305,8 @@ mips-tdep.o: mips-tdep.c $(defs_h) $(gdb_string_h) $(gdb_assert_h) \ $(floatformat_h) mipsv4-nat.o: mipsv4-nat.c $(defs_h) $(inferior_h) $(gdbcore_h) $(target_h) \ $(regcache_h) $(gregset_h) +mn10300-linux-tdep.o: mn10300-linux-tdep.c $(defs_h) $(gdbcore_h) \ + $(gdb_string_h) $(regcache_h) $(mn10300_tdep_h) mn10300-tdep.o: mn10300-tdep.c $(defs_h) $(arch_utils_h) $(dis_asm_h) \ $(gdbtypes_h) $(regcache_h) $(gdb_string_h) $(gdb_assert_h) \ $(frame_h) $(frame_unwind_h) $(frame_base_h) $(trad_frame_h) \ diff --git a/gdb/config/mn10300/linux.mt b/gdb/config/mn10300/linux.mt index aa47c29..277ed76 100644 --- a/gdb/config/mn10300/linux.mt +++ b/gdb/config/mn10300/linux.mt @@ -1,3 +1,3 @@ # Target: Matsushita mn10300 (AM33) running Linux -TDEPFILES= mn10300-tdep.o solib.o solib-svr4.o +TDEPFILES= mn10300-tdep.o mn10300-linux-tdep.o corelow.o solib.o solib-svr4.o diff --git a/gdb/mn10300-linux-tdep.c b/gdb/mn10300-linux-tdep.c new file mode 100644 index 0000000..e0f557c --- /dev/null +++ b/gdb/mn10300-linux-tdep.c @@ -0,0 +1,486 @@ +/* Target-dependent code for the Matsushita MN10300 for GDB, the GNU debugger. + Copyright 2003, 2004, 2005 + Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "gdbcore.h" +#include "gdb_string.h" +#include "regcache.h" +#include "mn10300-tdep.h" +#include "gdb_assert.h" +#include "bfd.h" +#include "elf-bfd.h" +#include "osabi.h" +#include "regset.h" + +#include + +/* Transliterated from ... */ +#define MN10300_ELF_NGREG 28 +#define MN10300_ELF_NFPREG 32 + +typedef gdb_byte mn10300_elf_greg_t[4]; +typedef mn10300_elf_greg_t mn10300_elf_gregset_t[MN10300_ELF_NGREG]; + +typedef gdb_byte mn10300_elf_fpreg_t[4]; +typedef struct +{ + mn10300_elf_fpreg_t fpregs[MN10300_ELF_NFPREG]; + gdb_byte fpcr[4]; +} mn10300_elf_fpregset_t; + +/* elf_gregset_t register indices stolen from include/asm-mn10300/ptrace.h. */ +#define MN10300_ELF_GREGSET_T_REG_INDEX_A3 0 +#define MN10300_ELF_GREGSET_T_REG_INDEX_A2 1 +#define MN10300_ELF_GREGSET_T_REG_INDEX_D3 2 +#define MN10300_ELF_GREGSET_T_REG_INDEX_D2 3 +#define MN10300_ELF_GREGSET_T_REG_INDEX_MCVF 4 +#define MN10300_ELF_GREGSET_T_REG_INDEX_MCRL 5 +#define MN10300_ELF_GREGSET_T_REG_INDEX_MCRH 6 +#define MN10300_ELF_GREGSET_T_REG_INDEX_MDRQ 7 +#define MN10300_ELF_GREGSET_T_REG_INDEX_E1 8 +#define MN10300_ELF_GREGSET_T_REG_INDEX_E0 9 +#define MN10300_ELF_GREGSET_T_REG_INDEX_E7 10 +#define MN10300_ELF_GREGSET_T_REG_INDEX_E6 11 +#define MN10300_ELF_GREGSET_T_REG_INDEX_E5 12 +#define MN10300_ELF_GREGSET_T_REG_INDEX_E4 13 +#define MN10300_ELF_GREGSET_T_REG_INDEX_E3 14 +#define MN10300_ELF_GREGSET_T_REG_INDEX_E2 15 +#define MN10300_ELF_GREGSET_T_REG_INDEX_SP 16 +#define MN10300_ELF_GREGSET_T_REG_INDEX_LAR 17 +#define MN10300_ELF_GREGSET_T_REG_INDEX_LIR 18 +#define MN10300_ELF_GREGSET_T_REG_INDEX_MDR 19 +#define MN10300_ELF_GREGSET_T_REG_INDEX_A1 20 +#define MN10300_ELF_GREGSET_T_REG_INDEX_A0 21 +#define MN10300_ELF_GREGSET_T_REG_INDEX_D1 22 +#define MN10300_ELF_GREGSET_T_REG_INDEX_D0 23 +#define MN10300_ELF_GREGSET_T_REG_INDEX_ORIG_D0 24 +#define MN10300_ELF_GREGSET_T_REG_INDEX_EPSW 25 +#define MN10300_ELF_GREGSET_T_REG_INDEX_PC 26 + +/* New gdbarch API for corefile registers. + Given a section name and size, create a struct reg object + with a supply_register and a collect_register method. */ + +/* Copy register value of REGNUM from regset to regcache. + If REGNUM is -1, do this for all gp registers in regset. */ + +static void +am33_supply_gregset_method (const struct regset *regset, + struct regcache *regcache, + int regnum, const void *gregs, size_t len) +{ + char zerobuf[MAX_REGISTER_SIZE]; + const mn10300_elf_greg_t *regp = (const mn10300_elf_greg_t *) gregs; + int i; + + gdb_assert (len == sizeof (mn10300_elf_gregset_t)); + + switch (regnum) { + case E_D0_REGNUM: + regcache_raw_supply (regcache, E_D0_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_D0)); + break; + case E_D1_REGNUM: + regcache_raw_supply (regcache, E_D1_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_D1)); + break; + case E_D2_REGNUM: + regcache_raw_supply (regcache, E_D2_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_D2)); + break; + case E_D3_REGNUM: + regcache_raw_supply (regcache, E_D3_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_D3)); + break; + case E_A0_REGNUM: + regcache_raw_supply (regcache, E_A0_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_A0)); + break; + case E_A1_REGNUM: + regcache_raw_supply (regcache, E_A1_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_A1)); + break; + case E_A2_REGNUM: + regcache_raw_supply (regcache, E_A2_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_A2)); + break; + case E_A3_REGNUM: + regcache_raw_supply (regcache, E_A3_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_A3)); + break; + case E_SP_REGNUM: + regcache_raw_supply (regcache, E_SP_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_SP)); + break; + case E_PC_REGNUM: + regcache_raw_supply (regcache, E_PC_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_PC)); + break; + case E_MDR_REGNUM: + regcache_raw_supply (regcache, E_MDR_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_MDR)); + break; + case E_PSW_REGNUM: + regcache_raw_supply (regcache, E_PSW_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_EPSW)); + break; + case E_LIR_REGNUM: + regcache_raw_supply (regcache, E_LIR_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_LIR)); + break; + case E_LAR_REGNUM: + regcache_raw_supply (regcache, E_LAR_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_LAR)); + break; + case E_MDRQ_REGNUM: + regcache_raw_supply (regcache, E_MDRQ_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_MDRQ)); + break; + case E_E0_REGNUM: + regcache_raw_supply (regcache, E_E0_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_E0)); + break; + case E_E1_REGNUM: + regcache_raw_supply (regcache, E_E1_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_E1)); + break; + case E_E2_REGNUM: + regcache_raw_supply (regcache, E_E2_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_E2)); + break; + case E_E3_REGNUM: + regcache_raw_supply (regcache, E_E3_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_E3)); + break; + case E_E4_REGNUM: + regcache_raw_supply (regcache, E_E4_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_E4)); + break; + case E_E5_REGNUM: + regcache_raw_supply (regcache, E_E5_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_E5)); + break; + case E_E6_REGNUM: + regcache_raw_supply (regcache, E_E6_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_E6)); + break; + case E_E7_REGNUM: + regcache_raw_supply (regcache, E_E7_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_E7)); + break; + + /* ssp, msp, and usp are inaccessible. */ + case E_E8_REGNUM: + memset (zerobuf, 0, MAX_REGISTER_SIZE); + regcache_raw_supply (regcache, E_E8_REGNUM, zerobuf); + break; + case E_E9_REGNUM: + memset (zerobuf, 0, MAX_REGISTER_SIZE); + regcache_raw_supply (regcache, E_E9_REGNUM, zerobuf); + break; + case E_E10_REGNUM: + memset (zerobuf, 0, MAX_REGISTER_SIZE); + regcache_raw_supply (regcache, E_E10_REGNUM, zerobuf); + + break; + case E_MCRH_REGNUM: + regcache_raw_supply (regcache, E_MCRH_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_MCRH)); + break; + case E_MCRL_REGNUM: + regcache_raw_supply (regcache, E_MCRL_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_MCRL)); + break; + case E_MCVF_REGNUM: + regcache_raw_supply (regcache, E_MCVF_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_MCVF)); + break; + case E_FPCR_REGNUM: + /* FPCR is numbered among the GP regs, but handled as an FP reg. + Do nothing. */ + break; + case E_FPCR_REGNUM + 1: + /* The two unused registers beyond fpcr are inaccessible. */ + memset (zerobuf, 0, MAX_REGISTER_SIZE); + regcache_raw_supply (regcache, E_FPCR_REGNUM + 1, zerobuf); + break; + case E_FPCR_REGNUM + 2: + memset (zerobuf, 0, MAX_REGISTER_SIZE); + regcache_raw_supply (regcache, E_FPCR_REGNUM + 2, zerobuf); + break; + default: /* An error, obviously, but should we error out? */ + break; + case -1: + for (i = 0; i < MN10300_ELF_NGREG; i++) + am33_supply_gregset_method (regset, regcache, i, gregs, len); + break; + } + return; +} + +/* Copy fp register value of REGNUM from regset to regcache. + If REGNUM is -1, do this for all fp registers in regset. */ + +static void +am33_supply_fpregset_method (const struct regset *regset, + struct regcache *regcache, + int regnum, const void *fpregs, size_t len) +{ + const mn10300_elf_fpregset_t *fpregset = fpregs; + + gdb_assert (len == sizeof (mn10300_elf_fpregset_t)); + + if (regnum == -1) + { + int i; + + for (i = 0; i < MN10300_ELF_NFPREG; i++) + am33_supply_fpregset_method (regset, regcache, + E_FS0_REGNUM + i, fpregs, len); + am33_supply_fpregset_method (regset, regcache, + E_FPCR_REGNUM, fpregs, len); + } + else if (regnum == E_FPCR_REGNUM) + regcache_raw_supply (current_regcache, E_FPCR_REGNUM, + &fpregset->fpcr); + else if (E_FS0_REGNUM <= regnum && regnum < E_FS0_REGNUM + MN10300_ELF_NFPREG) + regcache_raw_supply (current_regcache, regnum, + &fpregset->fpregs[regnum - E_FS0_REGNUM]); + + return; +} + +/* Copy register values from regcache to regset. */ + +static void +am33_collect_gregset_method (const struct regset *regset, + const struct regcache *regcache, + int regnum, void *gregs, size_t len) +{ + mn10300_elf_gregset_t *regp = gregs; + int i; + + gdb_assert (len == sizeof (mn10300_elf_gregset_t)); + + switch (regnum) { + case E_D0_REGNUM: + regcache_raw_collect (regcache, E_D0_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_D0)); + break; + case E_D1_REGNUM: + regcache_raw_collect (regcache, E_D1_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_D1)); + break; + case E_D2_REGNUM: + regcache_raw_collect (regcache, E_D2_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_D2)); + break; + case E_D3_REGNUM: + regcache_raw_collect (regcache, E_D3_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_D3)); + break; + case E_A0_REGNUM: + regcache_raw_collect (regcache, E_A0_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_A0)); + break; + case E_A1_REGNUM: + regcache_raw_collect (regcache, E_A1_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_A1)); + break; + case E_A2_REGNUM: + regcache_raw_collect (regcache, E_A2_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_A2)); + break; + case E_A3_REGNUM: + regcache_raw_collect (regcache, E_A3_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_A3)); + break; + case E_SP_REGNUM: + regcache_raw_collect (regcache, E_SP_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_SP)); + break; + case E_PC_REGNUM: + regcache_raw_collect (regcache, E_PC_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_PC)); + break; + case E_MDR_REGNUM: + regcache_raw_collect (regcache, E_MDR_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_MDR)); + break; + case E_PSW_REGNUM: + regcache_raw_collect (regcache, E_PSW_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_EPSW)); + break; + case E_LIR_REGNUM: + regcache_raw_collect (regcache, E_LIR_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_LIR)); + break; + case E_LAR_REGNUM: + regcache_raw_collect (regcache, E_LAR_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_LAR)); + break; + case E_MDRQ_REGNUM: + regcache_raw_collect (regcache, E_MDRQ_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_MDRQ)); + break; + case E_E0_REGNUM: + regcache_raw_collect (regcache, E_E0_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_E0)); + break; + case E_E1_REGNUM: + regcache_raw_collect (regcache, E_E1_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_E1)); + break; + case E_E2_REGNUM: + regcache_raw_collect (regcache, E_E2_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_E2)); + break; + case E_E3_REGNUM: + regcache_raw_collect (regcache, E_E3_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_E3)); + break; + case E_E4_REGNUM: + regcache_raw_collect (regcache, E_E4_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_E4)); + break; + case E_E5_REGNUM: + regcache_raw_collect (regcache, E_E5_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_E5)); + break; + case E_E6_REGNUM: + regcache_raw_collect (regcache, E_E6_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_E6)); + break; + case E_E7_REGNUM: + regcache_raw_collect (regcache, E_E7_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_E7)); + break; + + /* ssp, msp, and usp are inaccessible. */ + case E_E8_REGNUM: + /* The gregset struct has noplace to put this: do nothing. */ + break; + case E_E9_REGNUM: + /* The gregset struct has noplace to put this: do nothing. */ + break; + case E_E10_REGNUM: + /* The gregset struct has noplace to put this: do nothing. */ + break; + case E_MCRH_REGNUM: + regcache_raw_collect (regcache, E_MCRH_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_MCRH)); + break; + case E_MCRL_REGNUM: + regcache_raw_collect (regcache, E_MCRL_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_MCRL)); + break; + case E_MCVF_REGNUM: + regcache_raw_collect (regcache, E_MCVF_REGNUM, + (regp + MN10300_ELF_GREGSET_T_REG_INDEX_MCVF)); + break; + case E_FPCR_REGNUM: + /* FPCR is numbered among the GP regs, but handled as an FP reg. + Do nothing. */ + break; + case E_FPCR_REGNUM + 1: + /* The gregset struct has noplace to put this: do nothing. */ + break; + case E_FPCR_REGNUM + 2: + /* The gregset struct has noplace to put this: do nothing. */ + break; + default: /* An error, obviously, but should we error out? */ + break; + case -1: + for (i = 0; i < MN10300_ELF_NGREG; i++) + am33_collect_gregset_method (regset, regcache, i, gregs, len); + break; + } + return; +} + +/* Copy fp register values from regcache to regset. */ + +static void +am33_collect_fpregset_method (const struct regset *regset, + const struct regcache *regcache, + int regnum, void *fpregs, size_t len) +{ + mn10300_elf_fpregset_t *fpregset = fpregs; + + gdb_assert (len == sizeof (mn10300_elf_fpregset_t)); + + if (regnum == -1) + { + int i; + for (i = 0; i < MN10300_ELF_NFPREG; i++) + am33_collect_fpregset_method (regset, regcache, E_FS0_REGNUM + i, + fpregs, len); + am33_collect_fpregset_method (regset, regcache, + E_FPCR_REGNUM, fpregs, len); + } + else if (regnum == E_FPCR_REGNUM) + regcache_raw_collect (current_regcache, E_FPCR_REGNUM, + &fpregset->fpcr); + else if (E_FS0_REGNUM <= regnum + && regnum < E_FS0_REGNUM + MN10300_ELF_NFPREG) + regcache_raw_collect (current_regcache, regnum, + &fpregset->fpregs[regnum - E_FS0_REGNUM]); + + return; +} + +/* Create a struct regset from a corefile register section. */ + +static const struct regset * +am33_regset_from_core_section (struct gdbarch *gdbarch, + const char *sect_name, + size_t sect_size) +{ + /* We will call regset_alloc, and pass the names of the supply and + collect methods. */ + + if (sect_size == sizeof (mn10300_elf_fpregset_t)) + return regset_alloc (gdbarch, + am33_supply_fpregset_method, + am33_collect_fpregset_method); + else + return regset_alloc (gdbarch, + am33_supply_gregset_method, + am33_collect_gregset_method); +} + +/* AM33 Linux osabi has been recognized. + Now's our chance to register our corefile handling. */ + +static void +am33_linux_init_osabi (struct gdbarch_info gdbinfo, struct gdbarch *gdbarch) +{ + set_gdbarch_regset_from_core_section (gdbarch, + am33_regset_from_core_section); +} + +void +_initialize_mn10300_linux_tdep (void) +{ + gdbarch_register_osabi (bfd_arch_mn10300, 0, + GDB_OSABI_LINUX, am33_linux_init_osabi); +} + diff --git a/gdb/mn10300-tdep.h b/gdb/mn10300-tdep.h index baabfb6..42d990e 100644 --- a/gdb/mn10300-tdep.h +++ b/gdb/mn10300-tdep.h @@ -44,9 +44,14 @@ enum { E_E5_REGNUM = 20, E_E6_REGNUM = 21, E_E7_REGNUM = 22, + E_E8_REGNUM = 23, + E_E9_REGNUM = 24, + E_E10_REGNUM = 25, E_MCRH_REGNUM = 26, E_MCRL_REGNUM = 27, E_MCVF_REGNUM = 28, + E_FPCR_REGNUM = 29, + E_FS0_REGNUM = 32, E_NUM_REGS = 32 }; @@ -76,5 +81,3 @@ struct gdbarch_tdep }; #define AM33_MODE (gdbarch_tdep (current_gdbarch)->am33_mode) - - -- 2.7.4