Skip to content

Commit

Permalink
[MC68000] Add 32-bit branch
Browse files Browse the repository at this point in the history
  • Loading branch information
tgtakaoka committed Mar 4, 2025
1 parent d139bc2 commit 24f36fb
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 54 deletions.
88 changes: 44 additions & 44 deletions src/asm_mc68000.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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<uint8_t>(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
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/asm_mc68000.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/config_mc68000.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion src/dis_mc68000.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
32 changes: 25 additions & 7 deletions test/test_asm_mc68000.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)");
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
28 changes: 26 additions & 2 deletions test/test_dis_mc68000.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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");

Expand Down

0 comments on commit 24f36fb

Please sign in to comment.