Browse Source

Fleshing out the 6502 Assembler.

master
Bryan Miller 5 years ago
parent
commit
5a94e35213
1 changed files with 243 additions and 3 deletions
  1. +243
    -3
      src/MOS6502.js

+ 243
- 3
src/MOS6502.js View File

return r; return r;
} }


function tokenize(l){
var m = l.match(/([^()]+)|([()])/g);
var ip = false;
t = [];
m.forEach((mi)=>{
if (mi === "("){
ip = true;
} else {
if (ip){
mi.split(/[\s,]+/).forEach((i)=>{
if (i !== "")
t.push("+" + i);
});
} else {
mi.split(/[\s,]+/).forEach((i)=>{
if (i !== "")
t.push(i);
});
}
}
});
}

class Assembler{ class Assembler{
constructor(initpc){ constructor(initpc){
// Labels that hold variable values. // Labels that hold variable values.
this.__PC = (initpc >= 0) ? initpc : 0; this.__PC = (initpc >= 0) ? initpc : 0;
} }


function __AddrModeVal(tA, tB){
var mode = "";
var v = Number.NaN;
if (tA[0] === '#'){
mode = "i"; // Immediate
if (tA[1] === '$'){
v = parseInt(tA.substr(2), 16);
} else if (tA[1] === 'b'){
v = parseInt(tA.substr(2), 2);
} else {
v = parseInt(tA.substr(1));
if (isNaN(v)){
let lbl = tA.substr(1);
if (lbl in this.__varlabels){
if (this.__varlabels[lbl] < 256)
v = this.__varlabels[lbl]
}
}
}
} else if (tA[0] === '$'){
v = parseInt(tA.substr(1), 16);
if (tA.length === 3){
// Zero Page
if (tB !== null){
if (tB === "X")
mode = "zX";
} else {
mode = "z";
}
} else if (tA.length === 5){
// Absolute
if (tB !== null){
if (tB === "X"){
mode = "aX";
} else if (tB === "Y"){
mode = "aY";
}
} else {
mode = "a";
}
}
} else if (tA.startsWith("+")){
if (tB !== null){
if (tB === "+X"){ // The plus exists due to how tokenize() works.
mode = "nX";
} else if (tB === "Y"){
mode = "nY";
}

if (mode !== ""){
if (tA.startsWith("+$")){
v = parseInt(tA.substr(2));
} else {
let lbl = tA.substr(1);
if (lbl in this.__varlabels){
if (this.__varlabels[lbl] < 256)
v = vlb[lbl];
}
}
}
}
} else {
// We've been given a variable name. Need to look it up.
// if the var is 1 byte, assume "Zero Page". If var 2 bytes, assume "Absolute"
if (tA in this.__varlabel){
v = this.__varlabel[tA];
if (v < 256){
if (tB !== null){
if (tB === "X")
mode = "zX";
} else {
mode = "z";
}
} else {
if (tB !== null){
if (tB === "X"){
mode = "aX";
} else if (tB === "Y"){
mode = "aY";
}
} else {
mode = "a";
}
}
}
}

if (mode !== "" && ! isNaN(v))
return [mode, v];
throw new Error("Malformed op-code or value on program address " + toHexString(this.__PC));
}

compile(src){ compile(src){
var op = []; var op = [];
src.split("\n").forEach((line)=>{ src.split("\n").forEach((line)=>{
line = line.trim(); line = line.trim();
if (line[0] !== ";"){ // Skip comment lines. if (line[0] !== ";"){ // Skip comment lines.
line = line.split(";")[0].trim(); // Take out any trailing comments. line = line.split(";")[0].trim(); // Take out any trailing comments.
var tokens = line.split(/\s+/);
var tokens = tokenize(line);


if (tokens[0] === 'define'){ if (tokens[0] === 'define'){
// Variable label!! // Variable label!!
} else if (tokens[0][tokens[0].length - 1] === ':'){ } else if (tokens[0][tokens[0].length - 1] === ':'){
// Jump label!! // Jump label!!
} else if (tokens[0].length === 3){
} else if (tokens[0].length === 3){
let procFailed = false;
let mv = null;
// Possible op code. // Possible op code.
switch(tokens[0].toLowerCase()){ switch(tokens[0].toLowerCase()){
// --- ADC
case 'adc': case 'adc':
break;
if (tokens.length >= 2 && tokens.length <= 3){
mv = addrModeVal(tokens[1]);
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;
// --- AND
case 'and': case 'and':
break; break;
// --- ASL
case 'asl': case 'asl':
break; break;
// --- BCC
case 'bcc': case 'bcc':
break; break;
// --- BCS
case 'bcs': case 'bcs':
break; break;
// --- BEQ
case 'beq': case 'beq':
break; break;
case 'bit': case 'bit':
break; break;
case 'bpl': case 'bpl':
break; break;
// --- BRK
case 'brk': case 'brk':
if (tokens.length === 1){
op.push(0x00);
this.__PC += 1;
} else { procFailed = true; }
break; break;
case 'bvc': case 'bvc':
break; break;
case 'bvs': case 'bvs':
break; break;
// --- CLC
case 'clc': case 'clc':
if (tokens.length === 1){
op.push(0x18);
this.__PC += 1;
} else { procFailed = true; }
break; break;
// --- CLD
case 'cld': case 'cld':
if (tokens.length === 1){
op.push(0xD8);
this.__PC += 1;
} else { procFailed = true; }
break; break;
// --- CLI
case 'cli': case 'cli':
if (tokens.length === 1){
op.push(0x58);
this.__PC += 1;
} else { procFailed = true; }
break; break;
// --- CLV
case 'clv': case 'clv':
if (tokens.length === 1){
op.push(0xB8);
this.__PC += 1;
} else { procFailed = true; }
break; break;
case 'cmp': case 'cmp':
break; break;
break; break;
case 'dec': case 'dec':
break; break;
// --- DEX
case 'dex': case 'dex':
if (tokens.length === 1){
op.push(0xCA);
this.__PC += 1;
} else { procFailed = true; }
break; break;
// --- DEY
case 'dey': case 'dey':
if (tokens.length === 1){
op.push(0x88);
this.__PC += 1;
} else { procFailed = true; }
break; break;
case 'eor': case 'eor':
break; break;
case 'inc': case 'inc':
break; break;
// --- INX
case 'inx': case 'inx':
if (tokens.length === 1){
op.push(0xE8);
this.__PC += 1;
} else { procFailed = true; }
break; break;
// --- INY
case 'iny': case 'iny':
if (tokens.length === 1){
op.push(0xC8);
this.__PC += 1
} else { procFailed = true; }
break; break;
case 'jmp': case 'jmp':
break; break;
break; break;
case 'lsr': case 'lsr':
break; break;
// --- NOP
case 'nop': case 'nop':
if (tokens.length === 1){
op.push(0xEA);
this.__PC += 1;
} else { procFailed = true; }
break; break;
case 'ora': case 'ora':
break; break;
break; break;
case 'sbc': case 'sbc':
break; break;
// --- SEC
case 'sec': case 'sec':
if (tokens.length === 1){
op.push(0x38);
this.__PC += 1;
} else { procFailed = true; }
break; break;
// --- SED
case 'sed': case 'sed':
if (tokens.length === 1){
op.push(0xF8);
this.__PC += 1;
} else { procFailed = true; }
break; break;
// --- SEI
case 'sei': case 'sei':
op.push(0x78);
this.__PC += 1;
break; break;
case 'sta': case 'sta':
break; break;
break; break;
case 'sty': case 'sty':
break; break;
// --- TAX
case 'tax': case 'tax':
if (tokens.length === 1){
op.push(0xAA);
this.__PC += 1;
} else { procFailed = true; }
break; break;
// --- TAY
case 'tay': case 'tay':
if (tokens.length === 1){
op.push(0xA8);
this.__PC += 1;
} else { procFailed = true; }
break; break;
// --- TSX
case 'tsx': case 'tsx':
break; break;
// --- TXA
case 'txa': case 'txa':
if (tokens.length === 1){
op.push(0x8A);
this.__PC += 1;
} else { procFailed = true; }
break; break;
// --- TXS
case 'txs': case 'txs':
break; break;
// --- TYA
case 'tya': case 'tya':
if (tokens.length === 1){
op.push(0x98);
this.__PC += 1;
} else { procFailed = true; }
break; break;
// --- ---
default: default:
throw new Error("Unknown op-code '" + tokens[0].toUpperCase() + "' at program address " + toHexString(this.__PC)); throw new Error("Unknown op-code '" + tokens[0].toUpperCase() + "' at program address " + toHexString(this.__PC));
} }

if (procFailed)
throw new Error("Malformed op-code on program address " + toHexString(this.__PC));
} else { } else {
throw new Error("Failed to compile line '" + line + "' at program address " + toHexString(this.__PC)); throw new Error("Failed to compile line '" + line + "' at program address " + toHexString(this.__PC));
} }

Loading…
Cancel
Save