fma-snes65816
Version:
SNES 65816 assembler backend for FMA
301 lines (248 loc) • 8.88 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _fma = require('fma');
var _InstructionParameter = require('./InstructionParameter');
var ParameterType = _interopRequireWildcard(_InstructionParameter);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var Code = function () {
function Code(code) {
(0, _classCallCheck3.default)(this, Code);
this.code = code;
this.writeUInt8 = this._write({
static: function _static(code, value) {
code.writeUInt8(value);
},
calc: function calc(code, value) {
code.writeCalculation(value, 1);
}
});
this.writeUInt16 = this._write({
static: function _static(code, value) {
code.writeUInt16LE(value);
},
calc: function calc(code, value) {
code.writeCalculation(value, 2);
}
});
this.writeUInt24 = this._write({
static: function _static(code, value) {
code.writeUInt16LE(value & 0xFFFF);
code.writeUInt8(value >> 16);
},
calc: function calc(code, value) {
code.writeCalculation(value, 3);
}
});
}
(0, _createClass3.default)(Code, [{
key: '_write',
value: function _write(callbacks) {
var writeStatic = callbacks.static;
var writeCalc = callbacks.calc;
var code = this.code;
return function (number) {
if (typeof number === 'number') {
writeStatic(code.code, number);
} else {
switch (number.getClassName()) {
case 'Number':
writeStatic(code.code, number.getMember('__value').value);
break;
case 'FutureNumber':
writeCalc(code, number.getCalculation());
break;
default:
throw new Error('Class ' + number.getClassName() + ' can not be written directly');
}
}
};
}
}]);
return Code;
}();
var Parameter = function () {
function Parameter(context, object) {
(0, _classCallCheck3.default)(this, Parameter);
this.context = context;
this.rawValue = object;
this.parse(context, object);
}
(0, _createClass3.default)(Parameter, [{
key: 'toNumber',
value: function toNumber(context, object) {
if (object.hasMember('to_future_number')) {
return object.getMember('to_future_number').callWithParameters(context);
} else if (object.hasMember('to_n')) {
return object.getMember('to_n').callWithParameters(context);
} else {
throw new Error('Unknown how to convert to number: ' + object.getClassName());
}
}
}, {
key: 'parse',
value: function parse(context, object) {
if (object.isNil()) {
this.type = ParameterType.NONE;
return;
}
var klass = object.getClassName();
if (klass === 'TypedNumber') {
var type = object.getMember('type').getMember('__value').value;
this.value = object.getMember('number');
switch (type) {
case 'constant':
this.type = ParameterType.CONSTANT;
break;
case 'direct_page':
this.type = ParameterType.DP;
break;
case 'long_address':
if (this.value.getClassName() === 'TypedNumber') {
var other = new Parameter(context, this.value);
this.value = other.value;
switch (other.type) {
case ParameterType.INDIRECT:
this.type = ParameterType.LONG_INDIRECT;break;
default:
throw new Error('Can not convert: ' + other.type);
}
} else {
this.type = ParameterType.LONG_ADDRESS;
return;
}
break;
case 'indirect':
var param = object.getMember('parameter').getClassName();
switch (param) {
case 'Nil':
this.type = ParameterType.INDIRECT;break;
case 'Register':
var name = object.getMember('parameter').getMember('name').getMember('__value').value;
switch (name) {
case 'X':
this.type = ParameterType.INDIRECT_X;break;
case 'Y':
this.type = ParameterType.INDIRECT_Y;break;
case 'S':
this.type = ParameterType.INDIRECT_S;break;
default:
throw new Error('Unknown register: ' + name);
}
break;
default:
throw new Error('Unknown indirect type: ' + param);
}
break;
default:
throw new Error('Unknown parameter for instruction: ' + object.getClassName());
}
} else if (klass === 'Register') {
switch (object.getMember('name').getMember('__value').value) {
case 'A':
this.type = ParameterType.A;break;
case 'X':
this.type = ParameterType.X;break;
case 'Y':
this.type = ParameterType.Y;break;
case 'S':
this.type = ParameterType.S;break;
default:
throw new Error('Unknown register');
}
} else {
this.type = ParameterType.ADDRESS;
this.value = this.toNumber(context, object);
}
}
}]);
return Parameter;
}();
var Instruction = function () {
function Instruction(name) {
(0, _classCallCheck3.default)(this, Instruction);
this.name = name;
this.alternatives = {};
}
(0, _createClass3.default)(Instruction, [{
key: 'add',
value: function add(type, opcode, callback) {
if (!this.alternatives.hasOwnProperty(type.p1)) {
this.alternatives[type.p1] = {};
}
this.alternatives[type.p1][type.p2] = { type: type, opcode: opcode, callback: callback };
}
}, {
key: 'invoke',
value: function invoke(context, p1, p2) {
if (!this.alternatives.hasOwnProperty(p1.type) || !this.alternatives[p1.type].hasOwnProperty(p2.type)) {
throw new Error('The instruction ' + this.name + ' does not support the given parameters: ' + p1.type + ', ' + p2.type);
}
var scope = context.getRoot().getObject().getMember('Compiler').getMember('current_scope');
var _alternatives$p1$type = this.alternatives[p1.type][p2.type],
type = _alternatives$p1$type.type,
opcode = _alternatives$p1$type.opcode,
callback = _alternatives$p1$type.callback;
if (scope.isUndefined()) {
throw new Error('Instructions can only be used within function context');
}
scope.setMember('is_return_opcode', new _fma.BooleanObject(false));
var counter = Instruction.getCounter();
var code = new Code(scope.block.code);
if (counter) {
counter.add(opcode);
}
type.writer(scope, code, opcode, p1, p2, context);
if (callback) {
callback(scope, opcode, p1, p2, context);
}
}
}, {
key: 'invokeRaw',
value: function invokeRaw(context) {
var obj = context.getObject();
var p1 = new Parameter(context, obj.getMember('p1'));
var p2 = new Parameter(context, obj.getMember('p2'));
this.invoke(context, p1, p2);
}
}, {
key: 'build',
value: function build() {
var _this = this;
var macro = new _fma.MacroObject(this.name);
var args = new _fma.ArgumentList();
args.addArgument('p1', _fma.ArgumentList.TYPE_ARGUMENT, _fma.Nil);
args.addArgument('p2', _fma.ArgumentList.TYPE_ARGUMENT, _fma.Nil);
macro.setArguments(args);
macro.setCallback(function (context, self) {
_this.invokeRaw(context);
});
return macro;
}
}], [{
key: 'getCounter',
value: function getCounter() {
return Instruction.counter[0] || null;
}
}, {
key: 'pushCounter',
value: function pushCounter(counter) {
Instruction.counter.unshift(counter);
}
}, {
key: 'popCounter',
value: function popCounter() {
return Instruction.counter.shift();
}
}]);
return Instruction;
}();
exports.default = Instruction;
Instruction.counter = [];
//# sourceMappingURL=Instruction.js.map