Skip to content
This repository has been archived by the owner on Apr 29, 2021. It is now read-only.

Commit

Permalink
Merge 59e09f9 into b321a82
Browse files Browse the repository at this point in the history
  • Loading branch information
matanlurey committed Jun 11, 2017
2 parents b321a82 + 59e09f9 commit c34a477
Show file tree
Hide file tree
Showing 7 changed files with 331 additions and 36 deletions.
39 changes: 39 additions & 0 deletions pkg/armv4t_asm/README.md
@@ -0,0 +1,39 @@
<p align="center">
<h1 align="center">armv4t_asm</h1>
<p align="center">
Assembler for the ARMv4T CPU Architecture.
</p>
</p>

_Recommended reading: ["An Introduction to the GNU Assembler"][intro]._

[intro]: doc/gnu_assembler.pdf

## Installation

This package can be used as a standalone executable, or as a dependency.

### CLI

Use [`pub global activate`](https://www.dartlang.org/tools/pub/cmd/pub-global)
to install the assembler as a local program to be used on the command-line:

```bash
$ pub global activate armv4t_asm
$ armv4t_asm filename.s
> Assembling filename.s as filename.o...
> Wrote filename.o in 10ms.
```

Use `armv4t_asm --help` to get full usage information.

### Pub

```yaml
dependencies:
armv4t_asm:
```

## Usage

TBD.
Binary file added pkg/armv4t_asm/doc/gnu_assembler.pdf
Binary file not shown.
1 change: 0 additions & 1 deletion pkg/armv4t_asm/lib/armv4t_asm.dart
@@ -1 +0,0 @@

22 changes: 22 additions & 0 deletions pkg/armv4t_asm/lib/src/evaluate.dart
@@ -0,0 +1,22 @@
import 'package:func/func.dart';
import 'package:math_expressions/math_expressions.dart';

/// Returns the result of evaluating a mathematical [expression].
dynamic eval(String expression, int lookup(String varName)) => new Parser()
.parse(expression)
.evaluate(EvaluationType.REAL, new _DynamicContext(lookup));

class _DynamicContext extends ContextModel {
final Func1<String, int> _lookup;

_DynamicContext(this._lookup);

@override
Expression getExpression(String varName) {
final result = _lookup(varName);
if (result != null) {
return new Parser().parse('$result');
}
return super.getExpression(varName);
}
}
215 changes: 181 additions & 34 deletions pkg/armv4t_asm/lib/src/parser.dart
@@ -1,9 +1,11 @@
import 'package:meta/meta.dart';

import 'evaluate.dart';

/// Operation codes implemented by the ARMv4T Architecture.
///
/// _See ARM7TDMI-S Data Sheet, Chapter 4 (ARM Instruction Set)._
const List<String> mnemonics = const [
const List<String> _mnemonics = const <String>[
'ADC',
'ADD',
'AND',
Expand Down Expand Up @@ -56,7 +58,7 @@ const List<String> mnemonics = const [
/// Condition codes implemented by the ARMv4T Architecture.
///
/// _See ARM7TDMI-S Data Sheet, 4.2 Condition Fields._
const List<String> conditions = const [
const List<String> _conditions = const <String>[
'EQ',
'NE',
'CS',
Expand All @@ -74,15 +76,15 @@ const List<String> conditions = const [
'AL',
];

class Suffix {
class _Suffix {
/// A standard 'S' suffix.
static const Suffix $S = const Suffix._(suffixes: const ['S']);
static const _Suffix $S = const _Suffix(suffixes: const ['S']);

final List<String> suffixes;
final bool isMode;
final bool isRequired;

const Suffix._({
const _Suffix({
@required this.suffixes,
this.isMode: false,
this.isRequired: false,
Expand All @@ -92,28 +94,28 @@ class Suffix {
/// Suffix each mnemonic can or must have.
///
/// Any mnemonic not listed doesn't have supported suffixes.
const Map<String, Suffix> suffixes = const {
'AND': Suffix.$S,
'EOR': Suffix.$S,
'SUB': Suffix.$S,
'RSB': Suffix.$S,
'ADD': Suffix.$S,
'ADC': Suffix.$S,
'SBC': Suffix.$S,
'RSC': Suffix.$S,
'ORR': Suffix.$S,
'BIC': Suffix.$S,
'MUL': Suffix.$S,
'MLA': Suffix.$S,
'MOV': Suffix.$S,
'MVN': Suffix.$S,
'ASR': Suffix.$S,
'ROR': Suffix.$S,
'RRX': Suffix.$S,
'SWP': const Suffix._(suffixes: const ['B']),
'LDC': const Suffix._(suffixes: const ['L']),
'STC': const Suffix._(suffixes: const ['L']),
'LDR': const Suffix._(suffixes: const [
const Map<String, _Suffix> _suffixes = const <String, _Suffix>{
'AND': _Suffix.$S,
'EOR': _Suffix.$S,
'SUB': _Suffix.$S,
'RSB': _Suffix.$S,
'ADD': _Suffix.$S,
'ADC': _Suffix.$S,
'SBC': _Suffix.$S,
'RSC': _Suffix.$S,
'ORR': _Suffix.$S,
'BIC': _Suffix.$S,
'MUL': _Suffix.$S,
'MLA': _Suffix.$S,
'MOV': _Suffix.$S,
'MVN': _Suffix.$S,
'ASR': _Suffix.$S,
'ROR': _Suffix.$S,
'RRX': _Suffix.$S,
'SWP': const _Suffix(suffixes: const ['B']),
'LDC': const _Suffix(suffixes: const ['L']),
'STC': const _Suffix(suffixes: const ['L']),
'LDR': const _Suffix(suffixes: const [
'FD',
'ED',
'FA',
Expand All @@ -123,7 +125,7 @@ const Map<String, Suffix> suffixes = const {
'DA',
'DB',
], isMode: true, isRequired: true),
'STM': const Suffix._(suffixes: const [
'STM': const _Suffix(suffixes: const [
'FD',
'ED',
'FA',
Expand All @@ -133,10 +135,155 @@ const Map<String, Suffix> suffixes = const {
'DA',
'DB',
], isMode: true, isRequired: true),
'UMULL': Suffix.$S,
'UMLAL': Suffix.$S,
'SMULL': Suffix.$S,
'SMLAL': Suffix.$S,
'LSL': Suffix.$S,
'LSR': Suffix.$S,
'UMULL': _Suffix.$S,
'UMLAL': _Suffix.$S,
'SMULL': _Suffix.$S,
'SMLAL': _Suffix.$S,
'LSL': _Suffix.$S,
'LSR': _Suffix.$S,
};

List<String> _sortedByLength(Iterable<String> strings) =>
strings.toList()..sort((a, b) => a.length.compareTo(b.length));

/// Implements parsing expressions, assembler directives, operands, mnemonics.
class Armv4tParser {
static List<String> _sortedMnemonics;

const Armv4tParser();

/// Parses a mnemonic and condition field from the [input] string.
///
/// * [input]: String to parse the mnemonic, condition, and suffix from.
///
/// Returns a [ParsedMnemonic] object.
ParsedMnemonic parseMnemonic(String input) {
final line = input.replaceAll('\t', ' ').trim().split(' ');
final mnemonic = _parseMnemonic(line[0]);
final condition = _parseCondition(line[0], mnemonic);
final suffix = _parseSuffix(line[0], mnemonic, condition);
return new ParsedMnemonic(mnemonic, suffix, condition);
}

// Returns the mnemonic of the instruction to be assembled.
static String _parseMnemonic(String input) {
input = input.toUpperCase();
_sortedMnemonics ??= _sortedByLength(_mnemonics);
return _sortedMnemonics.firstWhere((m) => input.startsWith(m),
orElse: () => throw new StateError('Invalid mnemonic: "$input"'));
}

// Returns the suffix, if any, of a mnemonic of an instruction.
static String _parseSuffix(String input, String mnemonic, String condition) {
condition ??= '';
final info = _suffixes[mnemonic];
if (info == null) {
return null;
}
final look =
input.substring(mnemonic.length + condition.length).toUpperCase();
for (final suffix in info.suffixes) {
if (look.startsWith(suffix)) {
return suffix;
}
}
if (info.isRequired) {
throw new StateError('Expected suffix: "$input"');
} else {
return null;
}
}

// Returns the condition, if any, of a mnemonic of an instruction.
static String _parseCondition(String input, String mnemonic) {
final look = input.substring(mnemonic.length).toUpperCase();
for (final condition in _conditions) {
if (look.startsWith(condition)) {
return condition;
}
}
return null;
}

/// Parses an ARM register identifier from the specified [input].
///
/// * [input]: String to parse the ARM register identifier from.
///
/// Returns an ARM register identifier (R0 to R15).
String parseRegister(String input) {
input = input.trim().toUpperCase();
const alias = const {
'FP': 'R11',
'SP': 'R13',
'LR': 'R14',
'PC': 'R15',
};
if (input.length == 2) {
if (input[0] == 'R' &&
int.parse(input[1], onError: (_) => null) != null) {
return input;
}
final aliased = alias[input];
if (aliased != null) {
return aliased;
}
} else if (input.length == 3) {
if (input[0] == 'R' && input[1] == '1' && int.parse(input[2]) < 6) {
return input;
}
}
throw new FormatException('Unexpected ARM register identifier: "$input"');
}

/// Parses an ARM co-processor register identifier from the specified [input].
///
/// * [input]: String to parse the ARM register identifier from.
///
/// Returns an ARM co-processor register identifier (R0 to R15).
String parseCpRegister(String input) {
input = input.trim().toUpperCase();
if (input.length == 2) {
if (input[0] == 'C' &&
int.parse(input[1], onError: (_) => null) != null) {
return input;
}
} else if (input.length == 3) {
if (input[0] == 'C' && input[1] == '1' && int.parse(input[2]) < 6) {
return input;
}
}
throw new FormatException('Unexpcted ARM coprocessor identifier: "$input"');
}

/// Parses an [expression].
///
/// Returns the parsed and evaluated expression result.
int parseExpression(String expression, int lookup(String variable)) {
if (expression[0] == '#') {
return int.parse(expression.substring(1));
}
return (eval(expression, lookup) as num).toInt();
}
}

class ParsedMnemonic {
final String mnemonic;
final String suffix;
final String condition;

const ParsedMnemonic(this.mnemonic, [this.suffix, this.condition]);

@override
int get hashCode => mnemonic.hashCode ^ suffix.hashCode ^ condition.hashCode;

@override
bool operator ==(Object o) =>
o is ParsedMnemonic &&
o.mnemonic == mnemonic &&
o.suffix == suffix &&
o.condition == condition;

@override
String toString() =>
condition != null ? '$mnemonic${suffix ?? ''}: $condition' : mnemonic;
}
7 changes: 6 additions & 1 deletion pkg/armv4t_asm/pubspec.yaml
Expand Up @@ -11,4 +11,9 @@ environment:
sdk: '>=1.22.0 <2.0.0'

dependencies:
meta: ^1.0.5
func: ^1.0.0
math_expressions: '>=0.2.0 <0.4.0'
meta: ^1.0.5

dev_dependencies:
test: '^0.12.18+1'

0 comments on commit c34a477

Please sign in to comment.