From 24f36fbeaebf4c3769ea3b04f9c9f09614779af4 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Tue, 4 Mar 2025 16:39:02 +0900 Subject: [PATCH] [MC68000] Add 32-bit branch --- src/asm_mc68000.cpp | 88 +++++++++++++++++++-------------------- src/asm_mc68000.h | 1 + src/config_mc68000.h | 1 + src/dis_mc68000.cpp | 4 +- test/test_asm_mc68000.cpp | 32 ++++++++++---- test/test_dis_mc68000.cpp | 28 ++++++++++++- 6 files changed, 100 insertions(+), 54 deletions(-) diff --git a/src/asm_mc68000.cpp b/src/asm_mc68000.cpp index 56c894e7..fdd7b22f 100644 --- a/src/asm_mc68000.cpp +++ b/src/asm_mc68000.cpp @@ -213,6 +213,41 @@ void AsmMc68000::encodeDisplacement( insn.emitOperand16(disp); } +AddrMode AsmMc68000::branchType(AddrMode mode, InsnSize size, Config::ptrdiff_t delta) const { + if (mode == M_REL8) { + if (size == ISZ_NONE) { + if (!overflowDelta(delta, 8)) + return M_REL8; + if (!overflowDelta(delta, 16)) + return M_REL16; + return hasLongBranch() ? M_REL32 : M_REL8; + } + // Mnemonics has suffix 'B' or 'S' + if (size == ISZ_BYTE || size == ISZ_SNGL) + return M_REL8; + // Mnemonics has suffix 'W' or 'L' + if (size == ISZ_WORD || size == ISZ_LONG) + return M_REL16; + // Mnemonics has suffix 'X' + if (size == ISZ_XTND && hasLongBranch()) + return M_REL32; + } else if (mode == M_REL16) { + // Mnemonics has suffix 'W' or 'L' + if (size == ISZ_NONE || size == ISZ_WORD || size == ISZ_LONG) + return M_REL16; + } else { // mode == M_REL32 + if (size == ISZ_NONE) + return overflowDelta(delta, 16) ? M_REL32 : M_REL16; + // Mnemonics has suffix 'W' or 'L' + if (size == ISZ_WORD || size == ISZ_LONG) + return M_REL16; + // Mnemonics has suffix ''X' + if (size == ISZ_XTND) + return M_REL32; + } + return M_NONE; +} + void AsmMc68000::encodeRelativeAddr(AsmInsn &insn, AddrMode mode, const Operand &op) const { // FDBcc has different base address const auto FDBcc = insn.hasPostVal(); @@ -223,61 +258,26 @@ void AsmMc68000::encodeRelativeAddr(AsmInsn &insn, AddrMode mode, const Operand // Catch overwrap case if ((delta < 0 && target >= base) || (delta >= 0 && target < base)) insn.setErrorIf(op, OVERFLOW_RANGE); - auto type = mode; - const auto size = insn.insnSize(); - if (mode == M_REL8) { - if (size == ISZ_NONE) { - if (overflowDelta(delta, 16)) - insn.setErrorIf(op, OPERAND_TOO_FAR); - if (overflowDelta(delta, 8)) - type = M_REL16; - } else if (size == ISZ_BYTE || size == ISZ_SNGL) { - // Mnemonics has suffix 'B' or 'S' - type = M_REL8; - if (overflowDelta(delta, 8)) - insn.setErrorIf(op, OPERAND_TOO_FAR); - } else if (size == ISZ_WORD || size == ISZ_LONG) { - // Mnemonics has suffix 'W' or 'L' - type = M_REL16; - } else { - type = M_NONE; - } - } else if (mode == M_REL16) { - if (size == ISZ_NONE || size == ISZ_WORD || size == ISZ_LONG) { - // Mnemonics has suffix 'W' or 'L' - if (overflowDelta(delta, 16)) - insn.setErrorIf(op, OPERAND_TOO_FAR); - } else { - type = M_NONE; - } - } else { // mode == M_REL32 - if (size == ISZ_NONE) { - if (!overflowDelta(delta, 16)) - type = M_REL16; - insn.setErrorIf(OK); // never overflow - } else if (size == ISZ_WORD || size == ISZ_LONG) { - // Mnemonics has suffix 'W' or 'L' - type = M_REL16; - if (overflowDelta(delta, 16)) - insn.setErrorIf(op, OPERAND_TOO_FAR); - } else if (size == ISZ_XTND) { - // Mnemonics has suffix ''X' - } else { - type = M_NONE; - } - } + auto type = branchType(mode, insn.insnSize(), delta); if (type == M_NONE) { insn.setErrorIf(op, ILLEGAL_SIZE); type = mode; } if (type == M_REL8) { + if (overflowDelta(delta, 8)) + insn.setErrorIf(op, OPERAND_TOO_FAR); insn.embed(static_cast(delta)); } else if (type == M_REL16) { + if (overflowDelta(delta, 16)) + insn.setErrorIf(op, OPERAND_TOO_FAR); insn.emitOperand16(delta); } else { // type == M_REL32 insn.emitOperand32(delta); - if (mode == M_REL32) + if (mode == M_REL8) { + insn.embed(0xFF); + } else if (mode == M_REL32) { insn.embed(1 << 6); // set SIZE bit + } } } diff --git a/src/asm_mc68000.h b/src/asm_mc68000.h index 60da06b4..b8d4747e 100644 --- a/src/asm_mc68000.h +++ b/src/asm_mc68000.h @@ -40,6 +40,7 @@ struct AsmMc68000 final : Assembler, Config { void encodeBriefExtension(AsmInsn &insn, const Operand &op, Config::ptrdiff_t disp) const; void encodeDisplacement(AsmInsn &insn, const Operand &op, Config::ptrdiff_t disp) const; + AddrMode branchType(AddrMode mode, InsnSize size, Config::ptrdiff_t delta) const; void encodeRelativeAddr(AsmInsn &insn, AddrMode mode, const Operand &op) const; void encodeImmediate(AsmInsn &insn, const Operand &op, OprSize size) const; void encodeFloatControlList(AsmInsn &insn, const Operand &o) const; diff --git a/src/config_mc68000.h b/src/config_mc68000.h index 5f2354cd..ac9f7125 100644 --- a/src/config_mc68000.h +++ b/src/config_mc68000.h @@ -75,6 +75,7 @@ struct Config CpuSpec _cpuSpec; bool firstGen() const { return _cpuSpec.cpu == MC68000 || _cpuSpec.cpu == MC68010; } + bool hasLongBranch() const { return !firstGen(); } }; } // namespace mc68000 diff --git a/src/dis_mc68000.cpp b/src/dis_mc68000.cpp index 8acde8fd..4bd727a5 100644 --- a/src/dis_mc68000.cpp +++ b/src/dis_mc68000.cpp @@ -297,8 +297,10 @@ void DisMc68000::decodeRelative(DisInsn &insn, StrBuffer &out, AddrMode mode) co } if (mode == M_REL8) { const auto rel8 = insn.opCode() & 0xFF; - if (rel8 == 0) + if (rel8 == 0x00) type = M_REL16; + if (rel8 == 0xFF && hasLongBranch()) + type = M_REL32; } // FDBcc has different base address const auto FDBcc = insn.hasPostVal(); diff --git a/test/test_asm_mc68000.cpp b/test/test_asm_mc68000.cpp index 17ec2fb2..3d1391e8 100644 --- a/test/test_asm_mc68000.cpp +++ b/test/test_asm_mc68000.cpp @@ -766,7 +766,7 @@ void test_data_move() { ERRT("UNLK -(A2)", OPERAND_NOT_ALLOWED, "-(A2)"); ERRT("UNLK ($1234,A2)", OPERAND_NOT_ALLOWED, "($1234,A2)"); ERRT("UNLK ($12,A2,D3.W)", OPERAND_NOT_ALLOWED, "($12,A2,D3.W)"); - ERRT("UNLK ($FFFF00).W", OPERAND_NOT_ALLOWED, "($FFFF00).W"); + ERRT("UNLK ($7F00).W", OPERAND_NOT_ALLOWED, "($7F00).W"); ERRT("UNLK ($001234).L", OPERAND_NOT_ALLOWED, "($001234).L"); ERRT("UNLK (*+$1234,PC)", OPERAND_NOT_ALLOWED, "(*+$1234,PC)"); ERRT("UNLK (*+$12,PC,A3.L)", OPERAND_NOT_ALLOWED, "(*+$12,PC,A3.L)"); @@ -2469,9 +2469,15 @@ void test_program() { ATEST(0x010000, "BLE *+$82", 0060000 | 0xF00, 0x0080); AERRT(0x010000, "BLE.B *-$80", OPERAND_TOO_FAR, "*-$80", 0060000 | 0xF00 | 0x7E); AERRT(0x010000, "BLE.S *+$82", OPERAND_TOO_FAR, "*+$82", 0060000 | 0xF00 | 0x80); - AERRT(0x010000, "BLE.X *+$80", ILLEGAL_SIZE, "BLE.X *+$80", 0060000 | 0xF00 | 0x7E); - AERRT(0x010000, "BLE *-$8000", OPERAND_TOO_FAR, "*-$8000", 0060000 | 0xF00, 0x7FFE); - AERRT(0x010000, "BLE *+$8002", OPERAND_TOO_FAR, "*+$8002", 0060000 | 0xF00, 0x8000); + if (firstGen()) { + AERRT(0x010000, "BLE.X *+$80", ILLEGAL_SIZE, "BLE.X *+$80", 0060000 | 0xF00 | 0x7E); + AERRT(0x010000, "BLE *-$8000", OPERAND_TOO_FAR, "*-$8000", 0060000 | 0xF00 | 0xFE); + AERRT(0x010000, "BLE *+$8002", OPERAND_TOO_FAR, "*+$8002", 0060000 | 0xF00 | 0x00); + } else { + ATEST(0x010000, "BLE.X *+$80", 0060000 | 0xF00 | 0xFF, 0x0000, 0x007E); + ATEST(0x010000, "BLE *-$8000", 0060000 | 0xF00 | 0xFF, 0xFFFF, 0x7FFE); + ATEST(0x010000, "BLE *+$8002", 0060000 | 0xF00 | 0xFF, 0x0000, 0x8000); + } AERRT(0x001000, "BLE *-$1002", OVERFLOW_RANGE, "*-$1002", 0060000 | 0xF00, 0xEFFC); if (firstGen()) { AERRT(0xFFF000, "BLE *+$1000", OVERFLOW_RANGE, "*+$1000", 0060000 | 0xF00, 0x0FFE); @@ -2727,10 +2733,18 @@ void test_program() { AERRT(0x10000, "BRA.B *-$80", OPERAND_TOO_FAR, "*-$80", 0060000 | 0x7E); AERRT(0x10000, "BRA.B *+$82", OPERAND_TOO_FAR, "*+$82", 0060000 | 0x80); AERRT(0x10000, "BRA.S *+$82", OPERAND_TOO_FAR, "*+$82", 0060000 | 0x80); - AERRT(0x10000, "BRA.X *+$82", ILLEGAL_SIZE, "BRA.X *+$82", 0060000 | 0x80); + if (firstGen()) { + AERRT(0x10000, "BRA.X *+$82", ILLEGAL_SIZE, "BRA.X *+$82", 0060000 | 0x80); + } else { + ATEST(0x10000, "BRA.X *+$82", 0060000 | 0xFF, 0x0000, 0x0080); + } // BSR label: 00604|disp - AERRT(0x010000, "BSR *-$8000", OPERAND_TOO_FAR, "*-$8000", 0060400, 0x7FFE); + if (firstGen()) { + AERRT(0x010000, "BSR *-$8000", OPERAND_TOO_FAR, "*-$8000", 0060400 | 0xFE); + } else { + ATEST(0x010000, "BSR *-$8000", 0060400 | 0xFF, 0xFFFF, 0x7FFE); + } ATEST(0x010000, "BSR *-$7FFE", 0060400, 0x8000); ATEST(0x010000, "BSR *-$80", 0060400, 0xFF7E); ATEST(0x010000, "BSR.W *-$007E", 0060400, 0xFF80); @@ -2743,7 +2757,11 @@ void test_program() { ATEST(0x010000, "BSR.W *+$80" , 0060400, 0x007E); ATEST(0x010000, "BSR *+$82", 0060400, 0x0080); ATEST(0x010000, "BSR.W *+$8000", 0060400, 0x7FFE); - AERRT(0x010000, "BSR *+$8002", OPERAND_TOO_FAR, "*+$8002", 0060400, 0x8000); + if (firstGen()) { + AERRT(0x010000, "BSR *+$8002", OPERAND_TOO_FAR, "*+$8002", 0060400 | 0x00); + } else { + ATEST(0x010000, "BSR *+$8002", 0060400 | 0xFF, 0x0000, 0x8000); + } AERRT(0x001000, "BSR *-$1002", OVERFLOW_RANGE, "*-$1002", 0060400, 0xEFFC); if (firstGen()) { AERRT(0xFFF000, "BSR *+$1000", OVERFLOW_RANGE, "*+$1000", 0060400, 0x0FFE); diff --git a/test/test_dis_mc68000.cpp b/test/test_dis_mc68000.cpp index 0d1d32e7..7974006c 100644 --- a/test/test_dis_mc68000.cpp +++ b/test/test_dis_mc68000.cpp @@ -32,6 +32,10 @@ bool mc68010() { return strcmp_P("68010", disassembler.config().cpu_P()) == 0; } +bool mc68020() { + return strcmp_P("68020", disassembler.config().cpu_P()) == 0; +} + bool firstGen() { return mc68k00() || mc68010(); } @@ -2073,6 +2077,12 @@ void test_program() { ATEST(0x10000, "BRA", ".-0x7FFE", 0060000 | 0x000, 0x8000); disassembler.setOption("gnu-as", "off"); + if (firstGen()) { + AERRT(0x10000, "BRA", "*+1", OPERAND_NOT_ALIGNED, "*+1", 0060000 | 0x000 | 0xFF); + } else { + ATEST(0x10000, "BRA.L", "*+2", 0060000 | 0x000 | 0xFF, 0x0000, 0x0000); + } + // DBcc Dn,labelL 005|cc|31|Dn ATEST(0x10000, "DBRA", "D2, *-$7FFE", 0050312 | 0x100, 0x8000); ATEST(0x10000, "DBRA", "D2, *-$007E", 0050312 | 0x100, 0xFF80); @@ -2304,7 +2314,11 @@ void test_program() { ATEST(0x10000, "BRA", "*+$8000", 0060000, 0x7FFE); AERRT(0x10000, "BRA", "*-$7FFD", OPERAND_NOT_ALIGNED, "*-$7FFD", 0060000, 0x8001); AERRT(0x10000, "BRA", "*-125", OPERAND_NOT_ALIGNED, "*-125", 0060000 | 0x81); - AERRT(0x10000, "BRA", "*+1", OPERAND_NOT_ALIGNED, "*+1", 0060000 | 0xFF); + if (firstGen()) { + AERRT(0x10000, "BRA", "*+1", OPERAND_NOT_ALIGNED, "*+1", 0060000 | 0xFF); + } else { + ATEST(0x10000, "BRA.L", "*", 0060000 | 0xFF, 0xFFFF, 0xFFFE); + } AERRT(0x10000, "BRA", "*+3", OPERAND_NOT_ALIGNED, "*+3", 0060000 | 0x01); AERRT(0x10000, "BRA", "*+129", OPERAND_NOT_ALIGNED, "*+129", 0060000 | 0x7F); AERRT(0x10000, "BRA", "*+$8001", OPERAND_NOT_ALIGNED, "*+$8001", 0060000, 0x7FFF); @@ -2321,7 +2335,11 @@ void test_program() { ATEST(0x10000, "BSR", "*+$8000", 0060400, 0x7FFE); AERRT(0x10000, "BSR", "*-$7FFD", OPERAND_NOT_ALIGNED, "*-$7FFD", 0060400, 0x8001); AERRT(0x10000, "BSR", "*-125", OPERAND_NOT_ALIGNED, "*-125", 0060400 | 0x81); - AERRT(0x10000, "BSR", "*+1", OPERAND_NOT_ALIGNED, "*+1", 0060400 | 0xFF); + if (firstGen()) { + AERRT(0x10000, "BSR", "*+1", OPERAND_NOT_ALIGNED, "*+1", 0060400 | 0xFF); + } else { + ATEST(0x10000, "BSR.L", "*+6", 0060400 | 0xFF, 0x0000, 0x0004); + } AERRT(0x10000, "BSR", "*+3", OPERAND_NOT_ALIGNED, "*+3", 0060400 | 0x01); AERRT(0x10000, "BSR", "*+129", OPERAND_NOT_ALIGNED, "*+129", 0060400 | 0x7F); AERRT(0x10000, "BSR", "*+$8001", OPERAND_NOT_ALIGNED, "*+$8001", 0060400, 0x7FFF); @@ -2337,6 +2355,9 @@ void test_program() { ATEST(0x10000, "BRAS", ".+128", 0060000 | 0x7E); ATEST(0x10000, "BRA", ".+0x0080", 0060000, 0x007E); ATEST(0x10000, "BRA", ".+0x8000", 0060000, 0x7FFE); + if (!firstGen()) { + ATEST(0x10000, "BRAL", ".", 0060000 | 0xFF, 0xFFFF, 0xFFFE); + } ATEST(0x10000, "BSR", ".-0x7FFE", 0060400, 0x8000); ATEST(0x10000, "BSR", ".-0x007E", 0060400, 0xFF80); @@ -2347,6 +2368,9 @@ void test_program() { ATEST(0x10000, "BSRS", ".+128", 0060400 | 0x7E); ATEST(0x10000, "BSR", ".+0x0080", 0060400, 0x007E); ATEST(0x10000, "BSR", ".+0x8000", 0060400, 0x7FFE); + if (!firstGen()) { + ATEST(0x10000, "BSRL", ".+6", 0060400 | 0xFF, 0x0000, 0x0004); + } disassembler.setOption("gnu-as", "off");