+#define MAX_HISTORY 100
+
+/* The .machine pseudo op allows to switch to a different CPU level in
+ the asm listing. The current CPU setting can be stored on a stack
+ with .machine push and restored with .machine pop. */
+
+static void
+s390_machine (int ignore ATTRIBUTE_UNUSED)
+{
+ char *cpu_string;
+ static struct cpu_history
+ {
+ unsigned int cpu;
+ unsigned int flags;
+ } *cpu_history;
+ static int curr_hist;
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '"')
+ {
+ int len;
+ cpu_string = demand_copy_C_string (&len);
+ }
+ else
+ {
+ char c;
+
+ cpu_string = input_line_pointer;
+ do
+ {
+ char * str;
+
+ c = get_symbol_name (&str);
+ c = restore_line_pointer (c);
+ if (c == '+')
+ ++ input_line_pointer;
+ }
+ while (c == '+');
+
+ c = *input_line_pointer;
+ *input_line_pointer = 0;
+ cpu_string = xstrdup (cpu_string);
+ (void) restore_line_pointer (c);
+ }
+
+ if (cpu_string != NULL)
+ {
+ unsigned int new_cpu = current_cpu;
+ unsigned int new_flags = current_flags;
+
+ if (strcmp (cpu_string, "push") == 0)
+ {
+ if (cpu_history == NULL)
+ cpu_history = XNEWVEC (struct cpu_history, MAX_HISTORY);
+
+ if (curr_hist >= MAX_HISTORY)
+ as_bad (_(".machine stack overflow"));
+ else
+ {
+ cpu_history[curr_hist].cpu = current_cpu;
+ cpu_history[curr_hist].flags = current_flags;
+ curr_hist++;
+ }
+ }
+ else if (strcmp (cpu_string, "pop") == 0)
+ {
+ if (curr_hist <= 0)
+ as_bad (_(".machine stack underflow"));
+ else
+ {
+ curr_hist--;
+ new_cpu = cpu_history[curr_hist].cpu;
+ new_flags = cpu_history[curr_hist].flags;
+ }
+ }
+ else
+ new_cpu = s390_parse_cpu (cpu_string, &new_flags, TRUE);
+
+ if (new_cpu == S390_OPCODE_MAXCPU)
+ as_bad (_("invalid machine `%s'"), cpu_string);
+
+ if (new_cpu != current_cpu || new_flags != current_flags)
+ {
+ current_cpu = new_cpu;
+ current_flags = new_flags;
+ s390_setup_opcodes ();
+ }
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .machinemode pseudo op allows to switch to a different
+ architecture mode in the asm listing. The current architecture
+ mode setting can be stored on a stack with .machinemode push and
+ restored with .machinemode pop. */
+
+static void
+s390_machinemode (int ignore ATTRIBUTE_UNUSED)
+{
+ char *mode_string;
+ static unsigned int *mode_history;
+ static int curr_hist;
+
+ SKIP_WHITESPACE ();
+
+ {
+ char c;
+
+ c = get_symbol_name (&mode_string);
+ mode_string = xstrdup (mode_string);
+ (void) restore_line_pointer (c);
+ }
+
+ if (mode_string != NULL)
+ {
+ unsigned int old_mode_mask = current_mode_mask;
+ char *p;
+
+ for (p = mode_string; *p != 0; p++)
+ *p = TOLOWER (*p);
+
+ if (strcmp (mode_string, "push") == 0)
+ {
+ if (mode_history == NULL)
+ mode_history = XNEWVEC (unsigned int, MAX_HISTORY);
+
+ if (curr_hist >= MAX_HISTORY)
+ as_bad (_(".machinemode stack overflow"));
+ else
+ mode_history[curr_hist++] = current_mode_mask;
+ }
+ else if (strcmp (mode_string, "pop") == 0)
+ {
+ if (curr_hist <= 0)
+ as_bad (_(".machinemode stack underflow"));
+ else
+ current_mode_mask = mode_history[--curr_hist];
+ }
+ else
+ {
+ if (strcmp (mode_string, "esa") == 0)
+ current_mode_mask = 1 << S390_OPCODE_ESA;
+ else if (strcmp (mode_string, "zarch") == 0)
+ {
+ if (s390_arch_size == 32)
+ set_highgprs_p = TRUE;
+ current_mode_mask = 1 << S390_OPCODE_ZARCH;
+ }
+ else if (strcmp (mode_string, "zarch_nohighgprs") == 0)
+ current_mode_mask = 1 << S390_OPCODE_ZARCH;
+ else
+ as_bad (_("invalid machine mode `%s'"), mode_string);
+ }
+
+ if (current_mode_mask != old_mode_mask)
+ s390_setup_opcodes ();
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+#undef MAX_HISTORY
+
+const char *