if (typeof yasp == 'undefined') yasp = { };
(function () {
/** Generates the machine code that can be executed by the emulator
* @class
*/
yasp.Generator = function () {
this.bitWriter = null;
this.assembler = null;
this.labelMachinePosition = { };
this.linkPos = 0; // position during linking
};
/**
* Generates machine code out of the AST
* @param assembler
* @param input
* @returns {number}
*/
yasp.Generator.prototype.pass = function (assembler, input) {
this.assembler = assembler;
this.labelMachinePosition = { };
if (assembler.jobs.indexOf("bitcode") != -1 || assembler.jobs.indexOf("map") != -1) {
// 1 pass: get position in machine code for every Ast Node (for labels)
this.linkPos = 0;
for (var i = 0; i < input.length; i++) {
var node = input[i];
if (!!node) {
node.machinePosition = ~~(this.linkPos / 8);
if (node.type == yasp.AstNodeTypes.NODE_LABEL) {
node.type.generate.call(node, this)
}
var increment = node.type.calculateBitSize.call(node, this);
this.linkPos += increment;
}
}
}
if (assembler.jobs.indexOf("map") != -1) {
// generate map
if (assembler.jobs.indexOf('map') != -1) {
var map = { };
for (var i = 0; i < input.length; i++) {
var node = input[i];
if (node.type == yasp.AstNodeTypes.NODE_COMMAND) {
// put into map if its a command
if (!!map[node.token.line]) {
throw "Duplicate entry in map";
} else {
map[node.token.line] = node.machinePosition;
}
}
}
assembler.map = map;
}
}
if (assembler.jobs.indexOf("bitcode") != -1) {
this.bitWriter = new yasp.BitWriter();
// 2 pass: real generation
for (var i = 0; i < input.length; i++) {
var node = input[i];
if (!!node) {
node.type.generate.call(node, this);
}
}
return this.bitWriter.toUint8Array();
}
return null;
};
/**
* What types exist in an AST? The generate functions are all called in the context of the AstNode.
* @type {{NODE_LABEL: {name: string, generate: Function}, NODE_COMMAND: {name: string, generate: Function}}}
*/
yasp.AstNodeTypes = {
NODE_DUMP: {
name: "dump",
generate: function(generator) {
if (this.params.data instanceof String) {
if (!!this.params.len) {
// label address
var labelToken = generator.assembler.getLabel(this.params.data);
generator.bitWriter.append(generator.labelMachinePosition[labelToken.text.toUpperCase()], this.params.len);
} else {
var str = this.params.data.substring(1, this.params.data.length-1); // remove '"'
for (var i = 0; i < str.length; i++) {
generator.bitWriter.append(str.charCodeAt(i), 8);
}
generator.bitWriter.append('\0'.charCodeAt(0), 8);
}
} else {
generator.bitWriter.append(this.params.data, this.params.len);
}
},
calculateBitSize: function() {
if (this.params.data instanceof String && !this.params.len) {
return this.params.data.substring(1, this.params.data.length-1).length*8 + 8; // data + \0
} else {
return this.params.len;
}
}
},
NODE_ORG: {
name: "org",
generate: function(generator) {
generator.bitWriter.jumpTo(this.params.len*8);
},
calculateBitSize: function(generator) {
generator.linkPos = 0;
return this.params.len*8;
}
},
NODE_LABEL: {
name: "label",
generate: function(generator) {
// update machine position
generator.labelMachinePosition[this.params.label.text.toUpperCase()] = this.machinePosition;
},
calculateBitSize: function() {
return 0;
}
},
NODE_UNKNOWNCOMMAND: {
name: "unknowncommand",
generate: function(generator) {
throw "Cannot generate bitcode for unknown command";
},
calculateBitSize: function() {
return 42;
}
},
NODE_COMMAND: {
name: "command",
generate: function(generator) {
var writer = generator.bitWriter;
var commandCode = this.params.command.code;
var commandParam = this.params.command.params;
var params = this.params.params;
for (var i = 0; i < commandCode.length; i++) {
var code = commandCode[i];
var data, len;
if (typeof code.value == "string") {
data = +parseInt(code.value, 2);
len = code.value.length;
} else {
data = +code.value;
len = 8;
}
writer.append(data, len);
}
// params
for (var i = 0; i < params.length; i++) {
var param = params[i].text;
var type = yasp.ParamType[commandParam[i].type.toLowerCase()];
writer.append(type.data(param, generator), type.len);
}
},
calculateBitSize: function() {
var size = 0;
var commandCode = this.params.command.code;
var commandParam = this.params.command.params;
var params = this.params.params;
for (var i = 0; i < commandCode.length; i++) {
var code = commandCode[i];
var len;
if (typeof code.value == "string") {
len = code.value.length;
} else {
len = 8;
}
size += len;
}
// params
for (var i = 0; i < params.length; i++) {
var type = yasp.ParamType[commandParam[i].type.toLowerCase()];
size += type.len;
}
return size;
}
}
};
yasp.ParamType = {
"r_byte": {
len: 5,
check: function(cur, assembler) {
return cur.getType() == yasp.TokenType.BYTE_REGISTER;
},
data: function(data, generator) {
return +(data.substr(1)); // skip b from b2 for example
}
},
"r_word": {
len: 5,
check: function(cur, assembler) {
return cur.getType() == yasp.TokenType.WORD_REGISTER;
},
data: function(data, generator) {
return +(data.substr(1)); // skip w from w2 for example
}
},
"l_byte": {
len: 8,
check: function(cur, assembler) {
return cur.getType() == yasp.TokenType.NUMBER && +cur.text < Math.pow(2, 8);
},
data: function(data, generator) {
return data;
}
},
"l_word": {
len: 16,
check: function(cur, assembler) {
return cur.getType() == yasp.TokenType.NUMBER && +cur.text < Math.pow(2, 16);
},
data: function(data, generator) {
return data;
}
},
"pin": {
len: 5,
check: function(cur, assembler) {
return cur.getType() == yasp.TokenType.NUMBER && +cur.text < Math.pow(2, 5);
},
data: function(data, generator) {
return data;
}
},
"address": {
len: 11,
check: function(cur, assembler) {
return cur.getType() == yasp.TokenType.LABEL && assembler.getLabel(cur.text);
},
data: function(data, generator) {
var result = generator.labelMachinePosition[data.toUpperCase()];
if (!isNaN(result) ) {
return result;
} else {
throw "Unknown label in generator";
}
}
}
};
/**
* An AST Node
* @param type
* @param token
* @param params
* @constructor
*/
yasp.AstNode = function(type, token, params) {
this.type = type;
this.params = params;
this.token = token;
this.machinePosition = 0;
};
/**
* A writer class that makes writing bit data easy
* @constructor
*/
yasp.BitWriter = function() {
this.bits = "";
this.pointer = 0;
};
/**
* Appends data to the array
* @param data Which data (is converted to a number)
* @param length How many bits?
*/
yasp.BitWriter.prototype.append = function(data, length) {
var bits = (+data).toString(2); // convert to binary
if (bits.length < length) { // if its too short => add
var origLen = bits.length;
for (var i = origLen; i < length; i++) {
bits = "0" + bits;
}
} else if (bits.length > length) {
bits = bits.substr(bits.length - length, length); // if its too long => cut
}
// check if really appending
if (this.bits.length == this.pointer) {
this.bits += bits; // append
} else {
this.bits = this.bits.substr(0, this.pointer) + bits + ((this.bits.length > (this.pointer + bits.length)) ? this.bits.substr(this.pointer + bits.length) : ""); // replace
}
this.pointer += bits.length;
};
/**
* Returns the Uint8Array representation of the data
* This function is quite costly so dont call it too often
*/
yasp.BitWriter.prototype.toUint8Array = function() {
var bits = this.bits;
// normalize bits to 8
var overflow = this.bits.length % 8;
if (overflow != 0) {
for (var i = 0; i < 8 - overflow; i++) {
bits += "0";
}
}
// now create array
var array = new Uint8Array(bits.length / 8);
// put data into the array
for (var i = 0; i < array.length; i++) {
var block = bits.substr(i*8, 8);
var num = parseInt(block, 2);
if (isNaN(num)) {
throw "Block contained not a number "+i;
} else {
array[i] = num;
}
}
// finish \o/
return array;
};
/**
* Jumps to a specific positon in the bitcode
* If the position does not exist yet, it is created
* @param pos Position where it jumps to
*/
yasp.BitWriter.prototype.jumpTo = function(pos) {
// does this position already exists?
if (pos >= this.bits.length) {
var l = this.bits.length;
for (var i = 1; i <= (pos - l); i++) {
this.bits += "0";
}
}
this.pointer = pos;
};
})();