Skip to content

Instantly share code, notes, and snippets.

@matanlurey
Created June 21, 2020 23:07
Show Gist options
  • Save matanlurey/0f6019c1e7535f14f9f306cd9e83e307 to your computer and use it in GitHub Desktop.
Save matanlurey/0f6019c1e7535f14f9f306cd9e83e307 to your computer and use it in GitHub Desktop.
import 'package:binary/binary.dart';
import 'package:collection/collection.dart';
import 'package:meta/meta.dart';
/// An **internal** package representing all known `THUMB` instruction sets.
///
/// For testing, individual [BitPattern] implementations are accessible as
/// static fields (e.g. `[$01$moveShiftedRegister]`), and a sorted
/// [BitPatternGrpup] is accessible as [allFormats].
abstract class ThumbInstructionSet {
/// Move shifted register.
static final $01$moveShiftedRegister = BitPatternBuilder.parse(
'000P_POOO_OOSS_SDDD',
).build('01:MOVE_SHIFTED_REGISTER');
/// Add and subtract.
static final $02$addAndSubtract = BitPatternBuilder.parse(
'0001_11PN_NNSS_SDDD',
).build('02:ADD_AND_SUBTRACT');
/// Move, compare, add, and subtract immediate.
static final $03$moveCompareAddAndSubtractImmediate = BitPatternBuilder.parse(
'001P_PDDD_OOOO_OOOO',
).build('03:MOVE_COMPARE_ADD_AND_SUBTRACT_IMMEDIATE');
/// ALU operation.
static final $04$aluOperation = BitPatternBuilder.parse(
'0100_00PP_PPSS_SDDD',
).build('04:ALU_OPERATION');
/// High register operations and branch exchange.
static final $05$highRegisterOperationsAndBranch = BitPatternBuilder.parse(
'0100_01PP_HJSS_SDDD',
).build('05:HIGH_REGISTER_OPERATIONS_AND_BRANCH_EXCHANGE');
/// PC-relkative load.
static final $06$pcRelativeLoad = BitPatternBuilder.parse(
'0100_1DDD_WWWW_WWWW',
).build('06:PC_RELATIVE_LOAD');
/// Load and store with relative offset.
static final $07$loadAndStoreWithRelativeOffset = BitPatternBuilder.parse(
'0101_LB0O_OONN_NDDD',
).build('07:LOAD_AND_STORE_WITH_RELATIVE_OFFSET');
/// Load and store sign-extended byte and half-word.
static final $08$loadAndStoreSignExtended = BitPatternBuilder.parse(
'0101_HS1O_OOBB_BDDD',
).build('08:LOAD_AND_STORE_SIGN_EXTENDED_BYTE_AND_HALFWORD');
/// Load and store with immediate offset.
static final $09$loadAndStoreWithImmediateOffset = BitPatternBuilder.parse(
'011B_LOOO_OONN_NDDD',
).build('09:LOAD_AND_STORE_WITH_IMMEDIATE_OFFSET');
/// Load and store halfword.
static final $10$loadAndStoreHalfword = BitPatternBuilder.parse(
'1000_LOOO_OOBB_BDDD',
).build('10:LOAD_AND_STORE_HALFWORD');
/// SP-relative load and store.
static final $11$spRelativeLoadAndStore = BitPatternBuilder.parse(
'1001_LDDD_WWWW_WWWW',
).build('11:SP_RELATIVE_LOAD_AND_STORE');
/// Load address.
static final $12$loadAddress = BitPatternBuilder.parse(
'1010_SDDD_WWWW_WWWW',
).build('12:LOAD_ADDRESS');
/// Add offset to stack pointer.
static final $13$addOffsetToStackPointer = BitPatternBuilder.parse(
'1011_0000_SWWW_WWWW',
).build('13:ADD_OFFSET_TO_STACK_POINTER');
/// Push and pop registers.
static final $14$pushAndPopRegisters = BitPatternBuilder.parse(
'1011_L10R_TTTT_TTTT',
).build('14:PUSH_AND_POP_REGISTERS');
/// Multiple load and store.
static final $15$multipleLoadAndStore = BitPatternBuilder.parse(
'1100_LBBB_TTTT_TTTT',
).build('15:MULTIPLE_LOAD_AND_STORE');
/// Conditional branch.
static final $16$conditionalBranch = BitPatternBuilder.parse(
'1101_CCCC_SSSS_SSSS',
).build('16:CONDITIONAL_BRANCH');
/// Software interrupt.
static final $17$softwareInterrupt = BitPatternBuilder.parse(
'1101_1111_VVVV_VVVV',
).build('17:SOFTWARE_INTERRUPT');
/// Unconditional branch.
static final $18$unconditionalBranch = BitPatternBuilder.parse(
'1110_0OOO_OOOO_OOOO',
).build('18:UNCONDITIONAL_BRANCH');
/// Long branch with link.
static final $19$longBranchWithLink = BitPatternBuilder.parse(
'1111_HOOO_OOOO_OOOO',
).build('19:LONG_BRANCH_WITH_LINK');
/// A collection of all the known formats in [ThumbInstructionSet], sorted.
static final BitPatternGroup<List<int>, BitPattern<List<int>>> allFormats = [
$01$moveShiftedRegister,
$02$addAndSubtract,
$03$moveCompareAddAndSubtractImmediate,
$04$aluOperation,
$05$highRegisterOperationsAndBranch,
$06$pcRelativeLoad,
$07$loadAndStoreWithRelativeOffset,
$08$loadAndStoreSignExtended,
$09$loadAndStoreWithImmediateOffset,
$10$loadAndStoreHalfword,
$11$spRelativeLoadAndStore,
$12$loadAddress,
$13$addOffsetToStackPointer,
$14$pushAndPopRegisters,
$15$multipleLoadAndStore,
$16$conditionalBranch,
$17$softwareInterrupt,
$18$unconditionalBranch,
$19$longBranchWithLink,
].toGroup();
/// Format used to match and decode this instruction.
final BitPattern<List<int>> format;
const ThumbInstructionSet._(this.format) : assert(format != null);
@override
bool operator ==(Object o) {
if (identical(this, o)) {
return true;
} else if (o is ThumbInstructionSet && identical(format, o.format)) {
return const MapEquality<Object, Object>().equals(toJson(), o.toJson());
} else {
return false;
}
}
@override
int get hashCode => const MapEquality<Object, Object>().hash(toJson());
/// Must provide a JSON representation.
Map<String, Object> toJson();
@override
String toString() => '$runtimeType $toJson()';
}
/// Decoded object from [ThumbInstructionSet.$01$moveShiftedRegister].
class MoveShiftedRegister extends ThumbInstructionSet {
static final _format = ThumbInstructionSet.$01$moveShiftedRegister;
/// OpCode (2-bits).
final int opcode;
/// Offset (5-bits).
final int offset;
/// Register `S` (3-bits).
final int registerS;
/// Register `D` (3-bits).
final int registerD;
/// Creates a [MoveShiftedRegister] by decoding [bits].
factory MoveShiftedRegister.decodeBits(int bits) {
return MoveShiftedRegister.fromList(_format.capture(bits));
}
/// Creates a [MoveShiftedRegister] converting a previously [decoded] list.
factory MoveShiftedRegister.fromList(List<int> decoded) {
return MoveShiftedRegister(
opcode: decoded[0],
offset: decoded[1],
registerS: decoded[2],
registerD: decoded[3],
);
}
/// Creates a [MoveShiftedRegister] from the provided variables.
///
/// > **NOTE**: Bits are **not** checked for correctness or size!
MoveShiftedRegister({
@required this.opcode,
@required this.offset,
@required this.registerS,
@required this.registerD,
}) : assert(opcode != null),
assert(offset != null),
assert(registerS != null),
assert(registerD != null),
super._(_format);
@override
Map<String, Object> toJson() {
return {
'Op': opcode,
'Offset5': offset,
'Rs': registerS,
'Rd': registerD,
};
}
}
/// Decoded object from [ThumbInstructionSet.$02$addAndSubtract].
class AddAndSubtract extends ThumbInstructionSet {
static final _format = ThumbInstructionSet.$02$addAndSubtract;
/// OpCode (2-bits).
final int opcode;
/// Register `N` or Offset (3-bits).
final int registerNOrOffset3;
/// Register `S`.
final int registerS;
/// Register `D`.
final int registerD;
/// Creates a [AddAndSubtract] by decoding [bits].
factory AddAndSubtract.decodeBits(int bits) {
return AddAndSubtract.fromList(_format.capture(bits));
}
/// Creates a [AddAndSubtract] converting a previously [decoded] list.
factory AddAndSubtract.fromList(List<int> decoded) {
return AddAndSubtract(
opcode: decoded[0],
registerNOrOffset3: decoded[1],
registerS: decoded[2],
registerD: decoded[3],
);
}
/// Creates a [AddAndSubtract] from the provided variables.
///
/// > **NOTE**: Bits are **not** checked for correctness or size!
AddAndSubtract({
@required this.opcode,
@required this.registerNOrOffset3,
@required this.registerS,
@required this.registerD,
}) : assert(opcode != null),
assert(registerNOrOffset3 != null),
assert(registerS != null),
assert(registerD != null),
super._(_format);
@override
Map<String, Object> toJson() {
return {
'Op': opcode,
'Rn/Offset3': registerNOrOffset3,
'Rs': registerS,
'Rd': registerD,
};
}
}
/// Decoded object from [ThumbInstructionSet.$03$moveCompareAddAndSubtractImmediate].
class MoveCompareAddAndSubtractImmediate extends ThumbInstructionSet {
static final _format =
ThumbInstructionSet.$03$moveCompareAddAndSubtractImmediate;
/// OpCode (2-bits).
final int opcode;
/// Register `D` (3-bits).
final int registerD;
/// Offset (8-bits).
final int offset;
/// Creates a [MoveCompareAddAndSubtractImmediate] by decoding [bits].
factory MoveCompareAddAndSubtractImmediate.decodeBits(int bits) {
return MoveCompareAddAndSubtractImmediate.fromList(_format.capture(bits));
}
/// Creates a [MoveCompareAddAndSubtractImmediate] converting a previously [decoded] list.
factory MoveCompareAddAndSubtractImmediate.fromList(List<int> decoded) {
return MoveCompareAddAndSubtractImmediate(
opcode: decoded[0],
registerD: decoded[1],
offset: decoded[2],
);
}
/// Creates a [MoveCompareAddAndSubtractImmediate] from the provided variables.
///
/// > **NOTE**: Bits are **not** checked for correctness or size!
MoveCompareAddAndSubtractImmediate({
@required this.opcode,
@required this.registerD,
@required this.offset,
}) : assert(opcode != null),
assert(offset != null),
assert(registerD != null),
super._(_format);
@override
Map<String, Object> toJson() {
return {
'Op': opcode,
'Rd': registerD,
'Offset8': offset,
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment