|
|
@@ -2,7 +2,7 @@ |
|
|
|
* Emulate a basic 6502 (MOS) chip. |
|
|
|
*/ |
|
|
|
|
|
|
|
var Memory = require('src/memory'); |
|
|
|
var Memory = require('./memory.js'); |
|
|
|
|
|
|
|
class CPU{ |
|
|
|
constructor(){ |
|
|
@@ -74,10 +74,12 @@ function toHexString(v, l){ |
|
|
|
function tokenize(l){ |
|
|
|
var m = l.match(/([^()]+)|([()])/g); |
|
|
|
var ip = false; |
|
|
|
t = []; |
|
|
|
var t = []; |
|
|
|
m.forEach((mi)=>{ |
|
|
|
if (mi === "("){ |
|
|
|
ip = true; |
|
|
|
} else if (mi === ")"){ |
|
|
|
ip = false; |
|
|
|
} else { |
|
|
|
if (ip){ |
|
|
|
mi.split(/[\s,]+/).forEach((i)=>{ |
|
|
@@ -92,6 +94,7 @@ function tokenize(l){ |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
return t; |
|
|
|
} |
|
|
|
|
|
|
|
class Assembler{ |
|
|
@@ -194,7 +197,7 @@ class Assembler{ |
|
|
|
let lbl = tA.substr(1); |
|
|
|
if (lbl in this.__varlabels){ |
|
|
|
if (this.__varlabels[lbl] < 256) |
|
|
|
v = vlb[lbl]; |
|
|
|
v = this.__varlabels[lbl]; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@@ -234,31 +237,32 @@ class Assembler{ |
|
|
|
var op = []; |
|
|
|
src.split("\n").forEach((line)=>{ |
|
|
|
line = line.trim(); |
|
|
|
if (line === ""){return;} |
|
|
|
if (line[0] !== ";"){ // Skip comment lines. |
|
|
|
line = line.split(";")[0].trim(); // Take out any trailing comments. |
|
|
|
var tokens = tokenize(line); |
|
|
|
|
|
|
|
if (tokens[0] === 'define'){ |
|
|
|
if (tokens[0].toLowerCase() === 'define'){ |
|
|
|
// Variable label!! |
|
|
|
this.__StoreVarLabel(tokens[1], tokens[2]); |
|
|
|
} else if (tokens[0][tokens[0].length - 1] === ':'){ |
|
|
|
// Jump labels! |
|
|
|
this.__StoreJmpLabel(tokens[0].substr(0, tokens[0].length - 1);); |
|
|
|
this.__StoreJmpLabel(tokens[0].substr(0, tokens[0].length - 1)); |
|
|
|
} else if (tokens[0].length === 3){ |
|
|
|
|
|
|
|
let StoreSingleOp = (code)=>{ |
|
|
|
if (tokens.length === 1){ |
|
|
|
op.push(code); |
|
|
|
this.__PC += 1; |
|
|
|
return true; |
|
|
|
return false; |
|
|
|
} |
|
|
|
return false; |
|
|
|
return true; |
|
|
|
}; |
|
|
|
|
|
|
|
let StoreBranchOp = (code) => { |
|
|
|
if (tokens.length === 2){ |
|
|
|
let v = Number.NaN; |
|
|
|
if (token[1][0] === '$'){ |
|
|
|
if (tokens[1][0] === '$'){ |
|
|
|
if (tokens[1].length === 3 || tokens[1].length === 5){ |
|
|
|
v = parseInt(token[1].substr(1), 16); |
|
|
|
} |
|
|
@@ -275,81 +279,82 @@ class Assembler{ |
|
|
|
op.push(code); |
|
|
|
op.push((v >= 0) ? v : v + 255); |
|
|
|
this.__PC += 2; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
return false; |
|
|
|
return true; |
|
|
|
}; |
|
|
|
|
|
|
|
let StoreOp = (codes, mint, maxt) => { |
|
|
|
if (tokens.length >= mint && tokens.length <= maxt){ |
|
|
|
let mv = addrModeVal(tokens[1], (tokens.length === 3) ? tokens[2].toUpperCase(), null); |
|
|
|
let modea = true; |
|
|
|
let mv = this.__AddrModeVal(tokens[1], (tokens.length === 3) ? tokens[2].toUpperCase() : null); |
|
|
|
let modena = true; |
|
|
|
switch(mv[0]){ |
|
|
|
case "i": |
|
|
|
modea = codes[0] !== null; |
|
|
|
if (modea){ |
|
|
|
modena = codes[0] === null; |
|
|
|
if (!modena){ |
|
|
|
op.push(codes[0]); |
|
|
|
op.push(mv[1]); |
|
|
|
this.__PC += 2; |
|
|
|
} break; |
|
|
|
case "z": |
|
|
|
modea = codes[1] !== null; |
|
|
|
if (modea){ |
|
|
|
modena = codes[1] === null; |
|
|
|
if (!modena){ |
|
|
|
op.push(codes[1]); |
|
|
|
op.push(mv[1]); |
|
|
|
this.__PC += 2; |
|
|
|
} break; |
|
|
|
case "zX": |
|
|
|
modea = codes[2] !== null; |
|
|
|
if (modea){ |
|
|
|
modena = codes[2] === null; |
|
|
|
if (!modena){ |
|
|
|
op.push(codes[2]); |
|
|
|
op.push(mv[1]); |
|
|
|
this.__PC += 2; |
|
|
|
} break; |
|
|
|
case "a": |
|
|
|
modea = codes[3] !== null; |
|
|
|
if (modea){ |
|
|
|
modena = codes[3] === null; |
|
|
|
if (!modena){ |
|
|
|
op.push(codes[3]); |
|
|
|
op.push(mv[1] & 0x000000FF); |
|
|
|
op.push((mv[1] & 0x0000FF00) >> 8); |
|
|
|
this.__PC += 3; |
|
|
|
} break; |
|
|
|
case "aX": |
|
|
|
modea = codes[4] !== null; |
|
|
|
if (modea){ |
|
|
|
modena = codes[4] === null; |
|
|
|
if (!modena){ |
|
|
|
op.push(codes[4]); |
|
|
|
op.push(mv[1] & 0x000000FF); |
|
|
|
op.push((mv[1] & 0x0000FF00) >> 8); |
|
|
|
this.__PC += 3; |
|
|
|
} break; |
|
|
|
case "aY": |
|
|
|
modea = codes[5] !== null; |
|
|
|
if (modea){ |
|
|
|
modena = codes[5] === null; |
|
|
|
if (!modena){ |
|
|
|
op.push(codes[5]); |
|
|
|
op.push(mv[1] & 0x000000FF); |
|
|
|
op.push((mv[1] & 0x0000FF00) >> 8); |
|
|
|
this.__PC += 3; |
|
|
|
} break; |
|
|
|
case "nX": |
|
|
|
modea = codes[6] !== null; |
|
|
|
if (modea){ |
|
|
|
modena = codes[6] === null; |
|
|
|
if (!modena){ |
|
|
|
op.push(codes[6]); |
|
|
|
op.push(mv[1]); |
|
|
|
this.__PC += 2; |
|
|
|
} break; |
|
|
|
case "nY": |
|
|
|
modea = codes[7] !== null; |
|
|
|
if (modea){ |
|
|
|
modena = codes[7] === null; |
|
|
|
if (!modena){ |
|
|
|
op.push(codes[7]); |
|
|
|
op.push(mv[1]); |
|
|
|
this.__PC += 2; |
|
|
|
} break; |
|
|
|
} |
|
|
|
if (modea === false) |
|
|
|
if (modena) |
|
|
|
throw new Error("Op-code does not support implied mode on program address " + toHexString(this.__PC)); |
|
|
|
return true; |
|
|
|
return false; |
|
|
|
} |
|
|
|
return false; |
|
|
|
return true; |
|
|
|
}; |
|
|
|
|
|
|
|
let procFailed = false; |
|
|
@@ -528,16 +533,13 @@ class Assembler{ |
|
|
|
break; |
|
|
|
// --- LDA |
|
|
|
case 'lda': |
|
|
|
procFailed = StoreOp([0xA9, 0xA5, 0xB5, 0xAD, 0xBD, 0xB9, 0xA1, 0xB1], 2, 3); break; |
|
|
|
break; |
|
|
|
procFailed = StoreOp([0xA9, 0xA5, 0xB5, 0xAD, 0xBD, 0xB9, 0xA1, 0xB1], 2, 3); break; |
|
|
|
// --- LDX |
|
|
|
case 'ldx': |
|
|
|
procFailed = StoreOp([0xA2, 0xA6, 0xB6, 0xAE, 0xBE, null, null, null], 2, 3); break; |
|
|
|
break; |
|
|
|
// --- LDY |
|
|
|
case 'ldy': |
|
|
|
procFailed = StoreOp([0xA0, 0xA4, 0xB4, 0xAC, 0xBC, null, null, null], 2, 3); break; |
|
|
|
break; |
|
|
|
// --- LSR |
|
|
|
case 'lsr': |
|
|
|
if (tokens.length === 2){ |