+/*
+ * We use RELOPPC to relocate hi16[expr] or ha16[expr]. This requires
+ * a matching lo16[expr] in the next instruction, because RELOPPC
+ * handles a hi16/lo16 or ha16/lo16 pair. RELOPPC only works with
+ * certain specific opcodes.
+ */
+
+/* Info about the current hi16, ha16, or lo16 */
static int hl_token;
static expr_t hl_expr;
+/* Info about the last hi16 or ha16 */
+static int hi_token = 0;
+static expr_t hi_expr;
+static valu_t hi_relonami;
+static long hi_lineno;
+static ADDR_T hi_dotval;
+static short hi_dottyp;
+
void no_hl(void) {
hl_token = 0;
}
}
}
+static const char *hi_name(void)
+{
+ return hi_token == OP_HI ? "hi16" : "ha16";
+}
+
+static void cant_use_hl(const char *what)
+{
+ serror("can't use %s in this instruction", what);
+}
+
+static void check_hl_expr(void)
+{
+ int und = ((hi_expr.typ & S_TYP) == S_UND);
+ int com = (hi_expr.typ & S_COM);
+
+ if (hi_expr.typ != hl_expr.typ ||
+ hi_expr.val != hl_expr.val ||
+ ((und || com) && hi_relonami != relonami)) {
+
+ long old = lineno;
+ lineno = hi_lineno;
+ serror("%s and lo16 must use same expression", hi_name());
+ lineno = old;
+ }
+}
+
+static void hi_missing_lo(void)
+{
+ long old = lineno;
+ lineno = hi_lineno;
+ serror("%s needs lo16 in next instruction", hi_name());
+ lineno = old;
+}
+
+static void need_hi(const char *needed, const char *got)
+{
+ long old = lineno;
+ lineno = hi_lineno;
+ serror("need %s, not %s, here", needed, got);
+ lineno = old;
+}
+
void emit_hl(word_t in)
{
+ word_t opcode;
int type;
switch (hl_token) {
case OP_HI:
case OP_HA:
+ /* hi16[...] or ha16[...] */
type = hl_expr.typ & S_TYP;
- if (type != S_ABS)
+ if (type != S_ABS && PASS_RELO) {
+ if (hi_token)
+ hi_missing_lo();
+
+ opcode = in >> 26;
+ if (opcode != 15 /* addis */)
+ cant_use_hl(hi_name());
+
+ /* This hi16 or ha16 needs a lo16 in the next instruction.
+ * Save this info until we find the lo16.
+ */
+ hi_token = hl_token;
+ hi_expr = hl_expr;
+ hi_relonami = relonami;
+ hi_lineno = lineno;
+ hi_dotval = DOTVAL;
+ hi_dottyp = DOTTYP;
+
+ /* Relocate this hi16 or ha16 and the next lo16. */
newrelo(hl_expr.typ, RELOPPC | FIXUPFLAGS);
- break;
+ }
+ break;
case OP_LO:
- /* If the assembler stored a symbol for relocation later, we
- * need to abandon it (because the relocation was generated by
- * hi16 or ha16). */
+ /* lo16[...] */
type = hl_expr.typ & S_TYP;
- if (type != S_ABS)
- relonami = 0;
- break;
+ if (type != S_ABS && PASS_RELO) {
+ if (hi_token) {
+ /* The last hi16 or ha16 needs this lo16 in the next
+ * instruction, to complete the pair for RELOPPC.
+ */
+ if (DOTVAL != hi_dotval + 4 || DOTTYP != hi_dottyp)
+ hi_missing_lo();
+
+ check_hl_expr();
+
+ opcode = in >> 26;
+ switch (opcode) {
+ case 14: /* addi */
+ case 34: /* lbz */
+ case 48: /* lfs */
+ case 50: /* lfd */
+ case 42: /* lha */
+ case 40: /* lhz */
+ case 32: /* lwz */
+ case 38: /* stb */
+ case 52: /* stfs */
+ case 54: /* stfd */
+ case 44: /* sth */
+ case 36: /* stw */
+ if (hi_token == OP_HI)
+ need_hi("ha16", "hi16");
+ break;
+ case 24: /* ori */
+ if (hi_token == OP_HA)
+ need_hi("hi16", "ha16");
+ break;
+ default:
+ cant_use_hl("lo16");
+ }
+
+ hi_token = 0;
+
+ /* If we have a symbol for relocation, we need to
+ * abandon it (because the relocation was generated by
+ * hi16 or ha16).
+ */
+ relonami = 0;
+ } else {
+ serror("lo16 without hi16 or ha16");
+ }
+ }
+ break;
}
emit4(in);
}
+
+void machfinish(int n) {
+ if (hi_token) {
+ hi_missing_lo();
+ hi_token = 0;
+ }
+}