Browse Source

Tokenizer handles strings. Directives use handlers. Updated tests.

master
Bryan Miller 5 years ago
parent
commit
7f25738377
2 changed files with 87 additions and 57 deletions
  1. +72
    -57
      src/chip/MOS6502/assembler.js
  2. +15
    -0
      test/unit.src.chip.MOS6502.assembler.spec.js

+ 72
- 57
src/chip/MOS6502/assembler.js View File

@@ -8,9 +8,23 @@ function ToHexString(v, l){
return r;
}

function GetStrings(s, rc){
s = (typeof(s) === 'string') ? [s] : s;
var m = s[0].match(/"((?:\\.|[^"\\])*)"/);
if (m && m.length > 0){
let i = s[0].indexOf(m[0]);
if (i > 0){
s[0] = s[0].substr(0, i) + rc + s[0].substr(i + m[0].length);
s.push(m[0]);
return GetStrings(s, rc);
}
}
return s;
}

function Tokenize(l){
var m = l.match(/([^()]+)|([()])/g);
var ip = false;
var ip = false;
var t = [];
m.forEach((mi)=>{
if (mi === "("){
@@ -24,9 +38,15 @@ function Tokenize(l){
t.push("+" + i);
});
} else {
mi.split(/[\s,]+/).forEach((i)=>{
if (i !== "")
var strs = GetStrings(mi, "*");
var stri = 1;
strs[0].split(/[\s,]+/).forEach((i)=>{
if (i === "*" && stri < strs.length){
t.push(strs[stri].substr(1, strs[stri].length - 2));
stri += 1;
} else if (i !== ""){
t.push(i);
}
});
}
}
@@ -280,6 +300,35 @@ function StoreOp(data, cmp, codes, mint, maxt, zpm){
};


// --------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------


function directive_DEFINE(data, cmp){
// Variable label!!
StoreVarLabel(data, cmp.tokens[1], cmp.tokens[2]);
}


function directive_BYTES(data, cmp){
// Compiler directive. Treat all proceeding tokens as values and store them raw.
for(let i=1; i < cmp.tokens.length; i++){
let v = Number.NaN;
if (cmp.tokens[i].startsWith("$")){
v = parseInt(cmp.tokens[i].substr(1), 16);
} else if (cmp.tokens[i].startsWith("b")){
v = parseInt(cmp.tokens[i].substr(1), 2);
} else {
v = parseInt(cmp.tokens[i]);
}
if (isNaN(v) || v < 0 || v >= 256)
throw new Error("Byte list value is malformed or out of bounds at program address " + ToHexString(data.PC));
cmp.op.push(v);
data.PC += 1;
};
}



// --------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------
@@ -300,6 +349,12 @@ class Assembler{
// Currently compiled code.
result: []
};
this.__directives = {
".bytes":directive_BYTES,
".byt":directive_BYTES,
".define":directive_DEFINE,
".def":directive_DEFINE
};
}

get PC(){return this.__data.PC;}
@@ -336,7 +391,7 @@ class Assembler{
let s = line.split(":");
let lbl = s[0].trim();
if (lbl.length <= 0)
throw new Error("Malformatted jump label at program address " + toHexString(this.__PC));
throw new Error("Malformatted jump label at program address " + ToHexString(this.__data.PC));
StoreJmpLabel(this.__data, lbl);
line = s[1].trim();
if (line.length <= 0){return this;} // Nothing left to process.
@@ -344,64 +399,23 @@ class Assembler{
// Finally... tokenize the main command.
cmp.tokens = Tokenize(line);

if (cmp.tokens[0].toLowerCase() === '.define'){
// Variable label!!
StoreVarLabel(this.__data, cmp.tokens[1], cmp.tokens[2]);
} else if (cmp.tokens[0].toLowerCase() === '.bytes'){
// Compiler directive. Treat all proceeding tokens as values and store them raw.
for(let i=1; i < cmp.tokens.length; i++){
let v = Number.NaN;
if (cmp.tokens[i].startsWith("$")){
v = parseInt(cmp.tokens[i].substr(1), 16);
} else if (cmp.tokens[i].startsWith("b")){
v = parseInt(cmp.tokens[i].substr(1), 2);
} else {
v = parseInt(cmp.tokens[i]);
}
if (isNaN(v) || v < 0 || v >= 256)
throw new Error("Byte list value is malformed or out of bounds at program address " + toHexString(this.__PC));
cmp.op.push(v);
this.__data.PC += 1;
};
} else if (cmp.tokens[0].length === 3){
var directivesHandled = false;
if (cmp.tokens[0].startsWith(".")){
let d = cmp.tokens[0].toLowerCase();
if (d in this.__directives){
this.__directives[d](this.__data, cmp);
directivesHandled = true;
}
}

if (!directivesHandled && cmp.tokens[0].length === 3){
let procFailed = false;
let mv = null;
// Possible op code.
switch(cmp.tokens[0].toLowerCase()){
// --- ADC
case 'adc':
procFailed = StoreOp(this.__data, cmp, [0x69, 0x65, 0x75, 0x6D, 0x7D, 0x79, 0x61, 0x71], 2, 3);
/*
if (tokens.length >= 2 && tokens.length <= 3){
mv = addrModeVal(tokens[1], (tokens.length === 3) ? tokens[2].toUpperCase(), null);
this.__PC += 2;
switch(mv[0]){
case "i":
op.push(0x69);
op.push(mv[1]);
break;
case "z":
case "zX":
op.push((mv[0] === "z") ? 0x65 : 0x75);
op.push(mv[1]);
break;
case "a":
case "aX":
case "aY":
op.push((mv[0] === "a") ? 0x6D : ((mv[0] === "aX") ? 0x7D : 0x79));
op.push(mv[1] & 0x000000FF);
op.push((mv[1] & 0x0000FF00) >> 8);
this.__PC += 1;
break;
case "nX":
case "nY":
op.push((mv[0] === "nX") ? 0x61 : 0x71);
op.push(mv[1]);
break;
}
} else { procFailed = true; }
*/
break;
procFailed = StoreOp(this.__data, cmp, [0x69, 0x65, 0x75, 0x6D, 0x7D, 0x79, 0x61, 0x71], 2, 3); break;
// --- AND
case 'and':
procFailed = StoreOp(this.__data, cmp, [0x29, 0x25, 0x35, 0x2D, 0x3D, 0x39, 0x21, 0x31], 2, 3);
@@ -652,7 +666,8 @@ class Assembler{
if (procFailed)
throw new Error("Malformed op-code on program address " + ToHexString(this.__data.PC));
} else {
throw new Error("Failed to compile line '" + line + "' at program address " + ToHexString(this.__data.PC));
if (!directivesHandled)
throw new Error("Failed to compile line '" + line + "' at program address " + ToHexString(this.__data.PC));
}
}
});

+ 15
- 0
test/unit.src.chip.MOS6502.assembler.spec.js View File

@@ -28,6 +28,13 @@ describe("Testing MOS6502 Assembler...", function(){
expect(vars['TAG']).to.equal(0x44);
});

it(".def directive (.define shorthand)", function(){
asm.compile(".define TAG2 $55");
let vars = asm.variables;
expect(vars['TAG2']).to.exist;
expect(vars['TAG2']).to.equal(0x55);
});

it("Jump labels", function(){
let pc = asm.PC;
asm.compile("loop:\nBNE loop");
@@ -47,6 +54,14 @@ describe("Testing MOS6502 Assembler...", function(){
expect(res[5]).to.equal(128);
});

it(".byt directive (.bytes shorthand)", function(){
let res = asm.reset().compile(".BYT $44, $55, $66").result();
expect(res.length).to.equal(3);
expect(res[0]).to.equal(0x44);
expect(res[1]).to.equal(0x55);
expect(res[2]).to.equal(0x66);
});

it("Jump label before and after .byte directive", function(){
asm.reset();
let pc1 = asm.PC;

Loading…
Cancel
Save