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

return r; 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){ function Tokenize(l){
var m = l.match(/([^()]+)|([()])/g); var m = l.match(/([^()]+)|([()])/g);
var ip = false;
var ip = false;
var t = []; var t = [];
m.forEach((mi)=>{ m.forEach((mi)=>{
if (mi === "("){ if (mi === "("){
t.push("+" + i); t.push("+" + i);
}); });
} else { } 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); t.push(i);
}
}); });
} }
} }
}; };




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


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;
};
}




// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
// Currently compiled code. // Currently compiled code.
result: [] result: []
}; };
this.__directives = {
".bytes":directive_BYTES,
".byt":directive_BYTES,
".define":directive_DEFINE,
".def":directive_DEFINE
};
} }


get PC(){return this.__data.PC;} get PC(){return this.__data.PC;}
let s = line.split(":"); let s = line.split(":");
let lbl = s[0].trim(); let lbl = s[0].trim();
if (lbl.length <= 0) 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); StoreJmpLabel(this.__data, lbl);
line = s[1].trim(); line = s[1].trim();
if (line.length <= 0){return this;} // Nothing left to process. if (line.length <= 0){return this;} // Nothing left to process.
// Finally... tokenize the main command. // Finally... tokenize the main command.
cmp.tokens = Tokenize(line); 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 procFailed = false;
let mv = null; let mv = null;
// Possible op code. // Possible op code.
switch(cmp.tokens[0].toLowerCase()){ switch(cmp.tokens[0].toLowerCase()){
// --- ADC // --- ADC
case '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 // --- AND
case 'and': case 'and':
procFailed = StoreOp(this.__data, cmp, [0x29, 0x25, 0x35, 0x2D, 0x3D, 0x39, 0x21, 0x31], 2, 3); procFailed = StoreOp(this.__data, cmp, [0x29, 0x25, 0x35, 0x2D, 0x3D, 0x39, 0x21, 0x31], 2, 3);
if (procFailed) if (procFailed)
throw new Error("Malformed op-code on program address " + ToHexString(this.__data.PC)); throw new Error("Malformed op-code on program address " + ToHexString(this.__data.PC));
} else { } 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

expect(vars['TAG']).to.equal(0x44); 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(){ it("Jump labels", function(){
let pc = asm.PC; let pc = asm.PC;
asm.compile("loop:\nBNE loop"); asm.compile("loop:\nBNE loop");
expect(res[5]).to.equal(128); 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(){ it("Jump label before and after .byte directive", function(){
asm.reset(); asm.reset();
let pc1 = asm.PC; let pc1 = asm.PC;

Loading…
Cancel
Save