csim.xul  |  csim.js  |  options.xul  |  options.js  |  about.xul  |  consts.js  |  screen.js  |  platform.js  |  csim.dtd  |  csim.css  |  b2a.c

include(jslib_file);
include(jslib_fileutils);
include(jslib_dirutils);
include('chrome://csim/content/consts.js');
include('chrome://csim/content/platform.js');
include('chrome://csim/content/screen.js');

const
  cmdBitMaskStr = Math.pow(2, 5).toString(2),
  nsBitOprRange = 2147483647,
  regSize       = 32,
  regNum        = 32;

var
  intDelay          = 200,
  intHistorySize    = 100,
  scrDelayMul       = 2,
  intHistoryLimit   = true,
  intHistoryLastTop = true,
  
  binFile,
  cheapMem          = new Array,
  cheapMemStr       = new Array,
  cheapReg          = new Array(32),
  cheapTmr          = new Array,
  cheapIrq          = new Array(16),
  cheapPrt          = new Array(4096),

  mpdiFile          = { loc: 'mpdiFile', inUse: false },
  mpdoFile          = { loc: 'mpdoFile', inUse: false },
  instrParam        = { rg_alp_n: 0, rg_bet_n: 0, rg_alp_v: 0, rg_bet_v: 0, im_v: 0 },
  intvInterpr       = { id: 0, run: false},
  intvScreen        = { id: 0 },
  cursor            = { x: 0, y: 0 },
  scrBuffer         = new Array,
  scrBufferSize     = 128,
  memOffsetMulti    = 8, // 8 for 32 bit word, 32 for byte
  irqEnabledCnt, servedInt, cnt, pause;
  
chrInitMem(64);
chrInitReg();
chrInitIrq();
chrInitScr();

var mainTab = document.getElementById('main-vbox').removeChild(document.getElementById('main-tab'));
mainTab.setAttribute('hidden', 'false');

var instrTreeItem = document.createElement('treeitem')
with (instrTreeItem) {
  appendChild(document.createElement('treerow'));   
  firstChild.appendChild(document.createElement('treecell'));
  firstChild.appendChild(document.createElement('treecell'));
  firstChild.appendChild(document.createElement('treecell'));
}                                                   

function getNiceStr(numStr, base) {
  var tmp = (numStr - 0).toString(base);
  return '00000000'.substr(tmp.length) + tmp.toUpperCase();
}

function getBundledZeros() {
  var tmpStr = '';
  for (var i = 0; i != 8; i++)
    tmpStr += '00000000 ';
  return tmpStr.slice(0, -1);
}

function chrInitProgMem() {
  var
    memPos = 0,
    treeChilds;
  for (var treeItem = 0; memPos < cheapMem.length; treeItem++) {
    var tmpStr = '';
    for (memPos = treeItem * 8; memPos < treeItem * 8 + 8; memPos++)
      tmpStr += getNiceStr(cheapMem[memPos] ? cheapMem[memPos] : '0', 16) + ' ';

    if (treeItem == (treeChilds = document.getElementById('mem-treech')).childNodes.length)
      chrInitMem(64);

    treeChilds.childNodes[treeItem].lastChild.lastChild.setAttribute('label', tmpStr.slice(0, -1));
  }
}

function chrInitMem(addLinesNum) {
  var
    treeItem, memChilds = document.getElementById('mem-treech'),
    bundledZeros = getBundledZeros(),
    addLinesOffset = memChilds.childNodes.length;

  for (var i = addLinesOffset; i < addLinesOffset + addLinesNum; i++) {
    with (treeItem = document.createElement('treeitem')) {
      appendChild(document.createElement('treerow'));
      lastChild.appendChild(document.createElement('treecell'));
      lastChild.lastChild.setAttribute('label', getNiceStr(i * memOffsetMulti, 10));
      lastChild.appendChild(document.createElement('treecell'));
      lastChild.lastChild.setAttribute('label', bundledZeros);
    }
    memChilds.appendChild(treeItem);
  }
}

function chrInitReg() {
  var treeItem, regChilds = document.getElementById('reg-treech');
  for (var i = 0; i != 32; i++) {
    with (treeItem = document.createElement('treeitem')) {
      appendChild(document.createElement('treerow'));
      lastChild.appendChild(document.createElement('treecell'));
      lastChild.lastChild.setAttribute('label', i);
      lastChild.appendChild(document.createElement('treecell'));
      lastChild.lastChild.setAttribute('label', '0');
    }
    regChilds.appendChild(treeItem);
  }
}

function chrInitIrq() {
  var newRow, irqRows = document.getElementById('irq-rows');
  for (var i = 0; i != 8; i++) {
    with (newRow = document.createElement('row')) {
      appendChild(document.createElement('label'));
      lastChild.setAttribute('value', i);
      appendChild(document.createElement('spacer'));
      appendChild(document.createElement('image'));
      lastChild.setAttribute('id', 'irq_ri');
      lastChild.setAttribute('style', 'max-height: 10px');
      appendChild(document.createElement('spacer'));
      appendChild(document.createElement('image'));
      lastChild.setAttribute('id', 'irq_si');
      lastChild.setAttribute('style', 'max-height: 10px');
      appendChild(document.createElement('spacer'));

      appendChild(document.createElement('label'));
      lastChild.setAttribute('value', i + 8);
      appendChild(document.createElement('spacer'));
      appendChild(document.createElement('image'))
      lastChild.setAttribute('id', 'irq_ri');
      lastChild.setAttribute('style', 'max-height: 10px');
      appendChild(document.createElement('spacer'));
      appendChild(document.createElement('image'))
      lastChild.setAttribute('id', 'irq_si');
      lastChild.setAttribute('style', 'max-height: 10px');
      appendChild(document.createElement('spacer'));
    }
    irqRows.appendChild(newRow);
  }
}

function updControls(ctrlStr) {
  var controls = document.getElementById('controls').childNodes, pos, name;
  for (var i = 0; i != controls.length; i++) {
    if ((name = controls[i].getAttribute('id').slice(4)).length == 0)
      continue;
    if ((pos = ctrlStr.search(name)) != -1 || (pos = ctrlStr.search('all')) != -1)
      document.getElementById('btn-' + name).setAttribute('disabled', ctrlStr.charAt(pos - 1) == '-');
  }
}

function initRegisters() {
  for (var i = 0; i != cheapReg.length; cheapReg[i++] = 0);
}

function initMem() {
  cheapMem.length = 0;
  for (var i = 0; i != cheapMemStr.length; cheapMem[i] = cheapMemStr[i] - 0, i++);
}

function initInterrupts() {
  for (var i = 0; i != cheapIrq.length; cheapIrq[i++] = { running: false, isrPos: 0 });
}

function initPorts() {
  for (var i = 0; i != cheapPrt.length; cheapPrt[i++] = 0);
}

function initTimers() {
  cheapTmr[regTA] = { active: false, actVal: 0 };
  cheapTmr[regTB] = { active: false, actVal: 0 };
}

function pickFile() {
  var nsIFilePicker = Components.interfaces.nsIFilePicker;
  var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  fp.init(window, "Select a File", nsIFilePicker.modeOpen);
  fp.appendFilter("Cheap Binary", "*.chb");
  fp.appendFilters(nsIFilePicker.filterAll);

  var res = fp.show();
  if (res == nsIFilePicker.returnOK) return fp.file;
  else return false;
}

function openFile() {
  var pickedFile = pickFile();
  if (pickedFile) {
    /*
    var b2aDirCpl = dSep + 'csim' + dSep + 'bin' + dSep;
    var dirUtil = new DirUtils;
    var fileUtil = new FileUtils;
    fileUtil.spawn(dirUtil.getChromeDir() + b2aDirCpl + b2aBin,
                   [pickedFile.path, dirUtil.getChromeDir() + b2aDirCpl + 'b2a.output']);

    var badFile = new File(pickedFile.path);
    binFile = new File(dirUtil.getChromeDir() + b2aDirCpl + 'b2a.output');
    */
    
    var
      fileUtil = new FileUtils,
      dirUtil  = new DirUtils;
    fileUtil.spawn(fileUtil.append(loca.natBin, b2aBin),
                   [pickedFile.path, fileUtil.append(dirUtil.getTmpDir(), 'b2a.output') ]);

    var badFile = new File(pickedFile.path);
    binFile = new File(fileUtil.append(dirUtil.getTmpDir(), 'b2a.output'));
    
    initRegisters();
    initInterrupts();    
    initPorts();
    initTimers();
    
    while (!(binFile.exists && binFile.size == badFile.size * 2.50));
    binFile.open();
    // cheapMemStr = binFile.read().slice(0, -1).split(String.fromCharCode(10));
    
    cheapMemStr.length = 0;
    cheapMemStr = binFile.read().match(/........../g);

    binFile.close();
    binFile.remove();
    initMem();

    document.getElementById('main-vbox').insertBefore(mainTab.cloneNode('true'), document.getElementById('status-bar'));
    chrInitProgMem();
    
    updControls('+start');
    document.getElementById('status-text').setAttribute('label', badFile.path + ', ' + badFile.size + ' bytes');
    
    document.getElementById('menu-help-doc_tab:command').setAttribute('disabled', false);
    document.getElementById('menu-file-close:command').setAttribute('disabled', false);
    document.getElementById('menu-file-open:command').setAttribute('disabled', true);
  
    intvScreen.id =
      window.setInterval('if (scrBuffer.length) chrUpdateScr(scrBuffer.shift())', intDelay * scrDelayMul);
  }
}

function closeFile() {
  if (mpdoFile.inUse) mpdoFile.close();
  mpdoFile.inUse = mpdiFile.inUse = false;

  scrBuffer.length = 0;
  window.clearInterval(intvScreen.id);
  with (intvInterpr) if (run) window.clearInterval((run = false, id));
  document.getElementById('main-vbox').removeChild(document.getElementById('main-tab'));
  updControls('-all');
  document.getElementById('status-text').setAttribute('label', '');
  
  document.getElementById('menu-help-doc_tab').setAttribute('checked', '');
  document.getElementById('menu-help-doc_tab:command').setAttribute('disabled', true);
  document.getElementById('menu-file-close:command').setAttribute('disabled', true);
  document.getElementById('menu-file-open:command').setAttribute('disabled', false);
}

function startRun() {
  irqEnabledCnt = cnt = 1;
  servedInt = [cheapIrq.length];
  with (cursor) { x = 0; y = 0; };
  with (curr)   { bc = colors.bc[40]; tc = colors.tc[37]; bold = 0 };
  escape.status = 'off';
  
  pause = false;
  updControls('-start +pause +reset');
  intvInterpr.id = window.setInterval('interprete()', intDelay);
  intvInterpr.run = true;
}

function pauseRun() {
  updControls('-pause +conti');
  pause = true;
}

function contiRun() {
  updControls('+pause -conti');
  pause = false;
}

function resetRun() {
  with (intvInterpr) if (run) window.clearInterval((run = false, id));
  window.clearInterval(intvScreen.id);
  document.getElementById('menu-help-doc_tab').setAttribute('checked', '');
  document.getElementById('main-vbox').removeChild(document.getElementById('main-tab'));
  document.getElementById('main-vbox').insertBefore(mainTab.cloneNode('true'), document.getElementById('status-bar'));
  
  initRegisters();
  initInterrupts();
  initPorts();
  initTimers();
  initMem();
    
  if (mpdoFile.inUse) mpdoFile.close();
  mpdoFile.inUse = mpdiFile.inUse = false;
  scrBuffer.length = 0;

  chrInitProgMem();
  updControls('+start -all');

  intvScreen.id =
    window.setInterval('if (scrBuffer.length) chrUpdateScr(scrBuffer.shift())', intDelay * scrDelayMul);
}

function interprete() {
  
  if (!pause) {
    var
      cmdStr, oprStr, oprHexStr,
      treeItem = instrTreeItem.cloneNode(true),
      instrChilds = document.getElementById('instr-treech');
    
    if (irqEnabledCnt == -1 || irqEnabledCnt == 0) irqEnabledCnt++;
    
    if (cheapTmr[regTA].active && (new Date().getTime() >= cheapReg[regTA] + cheapTmr[regTA].actVal)) {
      cheapTmr[regTA].active = false;
      cheapIrq[1].running = true;
      chrUpdIrqState(1, 'r', 'a');
    }
    
    if (cheapTmr[regTB].active && (new Date().getTime() >= cheapReg[regTB] + cheapTmr[regTB].actVal)) {
      cheapTmr[regTB].active = false;
      cheapIrq[2].running = true;
      chrUpdIrqState(2, 'r', 'a');
    }

    if (document.getElementById('cheap-input').value.length) {
      cheapIrq[3].running = true;
      chrUpdIrqState(3, 'r', 'a');
    }

    if (scrBuffer.length >= scrBufferSize) {
      cheapIrq[4].running = true;
      chrUpdIrqState(4, 'r', 'a');
    }
    
    if (irqEnabledCnt == 1)
      for (i = 0; i < servedInt[servedInt.length - 1]; i++)
        if (cheapIrq[i].running) {
          cheapReg[regRI] = cheapReg[regPC];
          cheapReg[regPC] = cheapIrq[i].isrPos;
          servedInt.push(i);
          cheapIrq[i].running = false;
          chrUpdIrqState(i, 'r', 'i');
          chrUpdIrqState(i, 's', 'a');
        }
    
    with (document.getElementById('reg-treech').childNodes[regPC].lastChild.lastChild)  
      setAttribute('label', ++cheapReg[regPC]);
    
    cmdStr = getCurr_cmdStr();
    oprStr = instr[cmdStr]['name'] + '/' + instr[cmdStr]['type'] + ' ';
    oprHexStr = getNiceStr(cheapMem[cheapReg[regPC] - 1], 16);
    
    with (instrParam)                                   
      switch (instr[cmdStr]['type']) {                  
        case 'i':                                       
          rg_alp_n = getCurr_rgn('alp');                
          rg_alp_v = cheapReg[rg_alp_n] - 0;            
          im_v     = getCurr_im('sho');                 
          oprStr += 'r' + rg_alp_n + '[' + rg_alp_v + '], ' + im_v;
          break;                                        
        case 'r':                                       
          rg_alp_n = getCurr_rgn('alp');                
          rg_alp_v = cheapReg[rg_alp_n] - 0;            
          rg_bet_n = getCurr_rgn('bet');                
          rg_bet_v = cheapReg[rg_bet_n] - 0;
          oprStr += 'r' + rg_alp_n + '[' + rg_alp_v + '], r' + rg_bet_n + '[' + rg_bet_v + ']';
          break;                                        
        case 'j':                                       
          im_v     = getCurr_im('lon');                 
          oprStr += im_v;
          break;                                        
        case 's':                                       
          rg_alp_n = getCurr_rgn('alp');                
          rg_alp_v = cheapReg[rg_alp_n] - 0;            
          oprStr += 'r' + rg_alp_n + '[' + rg_alp_v + ']';
          break;                                        
      }                                                 
    
    eval('with (instrParam) {' + instr[cmdStr]['code'] + '}');
    
    with (treeItem.firstChild) {
      firstChild.setAttribute('label', cnt);
      firstChild.nextSibling.setAttribute('label', oprHexStr);
      lastChild.setAttribute('label', oprStr);
    }                                                   

    while (intHistoryLimit && instrChilds.childNodes.length >= intHistorySize)
      instrChilds.removeChild(instrChilds[intHistoryLastTop ? 'lastChild' : 'firstChild']);
    if (intHistoryLastTop)
      instrChilds.insertBefore(treeItem, instrChilds.firstChild);
    else
      instrChilds.appendChild(treeItem);                  
                                                        
    if (cheapReg[regPC] == 4095) {                      
      document.getElementById('btn-pause').setAttribute('disabled', 'true');
      with (intvInterpr) window.clearInterval((run = false, id));
    }
    cnt++;
  }
}                                                     

function getCurr_cmdStr() {
  var
    opCode = opr_shr(cheapMem[cheapReg[regPC] - 1], 27),
    tmpStr = (opCode ? opCode : cheapMem[cheapReg[regPC] - 1] & (Math.pow(2, 6) - 1)).toString(2);
  return (opCode ? 'oc_' : 'sc_') + cmdBitMaskStr.substr(tmpStr.length) + tmpStr;
}

function getCurr_im(imType) {
  return cheapMem[cheapReg[regPC] - 1] & (Math.pow(2, imType == 'sho' ? 22 : 27) - 1);
}

function getCurr_rgn(rgType) {
  return rgType == 'alp' ? opr_shr(cheapMem[cheapReg[regPC] - 1] & 130023424 , 22)
                         : opr_shr(cheapMem[cheapReg[regPC] - 1] &   4063232, 17);
}

function setReg(value) {
  var regSel = arguments.length == 2 ? arguments[1] : instrParam.rg_alp_n;
  with (document.getElementById('reg-treech').childNodes[regSel].lastChild.lastChild)
    setAttribute('label', cheapReg[regSel] = value);
  if (regSel == regTA || regSel == regTB)
    with (cheapTmr[regSel]) {
      active = true;
      actVal = new Date().getTime();
    }
}

function updStrSection(str, pos, section) {
  return str.slice(0, pos) + section + str.slice(pos + section.length);
}

function setMem(value, offset) {
  while (document.getElementById('mem-treech').childNodes.length <= Math.floor(offset / 8))
    chrInitMem(128);
  
  var cellToChange = document.getElementById('mem-treech').childNodes[Math.floor(offset / 8)].lastChild.lastChild;
  cellToChange.setAttribute('label', updStrSection(cellToChange.getAttribute('label'), offset % 8 * 9, getNiceStr(value, 16)));
  cheapMem[offset] = value;
}

function subPdOut(value, port) {
  switch (port) {
    case 1:
      scrBuffer.push(value);
      break;

    case 3:
      if ( !mpdoFile.inUse ) {
        var loc = mpdoFile.loc;
        mpdoFile = new File((new FileUtils).append((new DirUtils).getHomeDir(), loc));
        mpdoFile.loc = loc;   // dirty !!
        mpdoFile.inUse = true;
        mpdoFile.open('w');
      }
      mpdoFile.write(String.fromCharCode(value));
      break;

    default:
      alert('no device at port ' + port);
  }
}

function subPdIn(port) {
  switch (port) {
    case 0:
      var kbdBuffer = document.getElementById('cheap-input');
      if (kbdBuffer.value.length) {
        cheapPrt[port] = kbdBuffer.value.charCodeAt(0);
        kbdBuffer.value = kbdBuffer.value.slice(1);
      }
      else
        cheapPrt[port] = 0;
      break;
      
    case 2:
      if ( !mpdiFile.inUse ) {
        var loc = mpdiFile.loc;
        mpdiFile = new File((new FileUtils).append((new DirUtils).getHomeDir(), loc));
        mpdiFile.loc = loc;   // dirty !!
        mpdiFile.inUse = true;
        mpdiFile.open();
        mpdiFile.content = mpdiFile.read();
        mpdiFile.close();
        cheapPrt[port] = mpdiFile.size;
        mpdiFile.pointer = 0;
        break;
      }
      if (mpdiFile.pointer == mpdiFile.size) {
        cheapPrt[port] = 0;
        break;
      }
      cheapPrt[port] = mpdiFile.content.charCodeAt(mpdiFile.pointer++);
      break;

    case 4:
      cheapPrt[port] = Math.floor(Math.random() * (Math.pow(2, 32) - 1));
      break;

    default:
      alert('no device at port ' + port);
  }
  return cheapPrt[port];
}

function chrUpdIrqState(irqNum, irqState, aState) {
  var
    row = irqNum % 8,
    col = irqNum < 8 ? 2 : 8,
    res = 'irq_' + irqState + aState;
  if (irqState == 's')
    col += 2;
  document.getElementById('irq-rows').childNodes[row].childNodes[col].setAttribute('id', res);
}

function resetIntDelay(val) {
  switch (val) {
    case 'inc':
      intDelay *= 2; break;
    case 'dec':
      intDelay = Math.round(intDelay / 2); break;
    default:
      intDelay = val;
  }
  if (intvInterpr.run) {
    clearInterval(intvInterpr.id);
    intvInterpr.id = setInterval('interprete()', intDelay);
  }
  clearInterval(intvScreen.id);
  intvScreen.id = setInterval('if (scrBuffer.length) chrUpdateScr(scrBuffer.shift())', intDelay * scrDelayMul);
}

function newFile() {

/*  
  var localFile = "/tmp/fubar.dat";
  var f = new File(localFile);
  if (!(f.exists())) f.create();
*/

/*
  var tmpStr = '';
  for (var prop in cheapReg) tmpStr += prop + ' => ' + cheapReg[prop] + '\n';
  alert(tmpStr);
*/

  alert('functionality not implemented yet.');
}

function openOptionsDlg() {
  window.openDialog('chrome://csim/content/options.xul', 'win-options', 'chrome, dependent, alwaysRaised');
}

function openDoc() {
  window.open(loca.urlDoc + '/csim.html', 'win-doc');
}

function openAbout() {
  window.openDialog('chrome://csim/content/about.xul', 'win-about', 'chrome, dependent, alwaysRaised');
}

function addDocTab() {
  if (document.getElementById('menu-help-doc_tab').getAttribute('checked') != 'true') {
    document.getElementById('main-tab').firstChild.removeChild(document.getElementById('doc-panel-tab'));
    with (document.getElementById('doc-panel'))
      removeChild(firstChild);
  }
  else {
    with (document.getElementById('main-tab').firstChild) {
      appendChild(document.createElement('tab'));
      lastChild.setAttribute('id', 'doc-panel-tab');
      lastChild.setAttribute('label', 'Documentation');
    }
    with (document.getElementById('doc-panel')) {
      appendChild(document.createElement('iframe'));
      lastChild.contentWindow.window.name = '_blank';
      lastChild.setAttribute('flex', '1');
      lastChild.setAttribute('src', loca.urlDoc + '/csim.html');
    }
  }
}

function createBitArray(val) {
  for (var bitArray = new Array, i = 0; i < regSize; i++)
    bitArray[i] = val & Math.pow(2, i) ? 2 : 0;
  return bitArray;
}

function opr_shl(val, pos) {
  return val * Math.pow(2, pos) % Math.pow(2, 32);
}

function opr_shr(val, pos) {
  return Math.floor(val / Math.pow(2, pos));
}

function opr_or(val_alp, val_bet) {
  // one operand out of nsBitOprRange
  if (Math.max(val_alp, val_bet) > nsBitOprRange) {
    var
      bitArrayAlp = createBitArray(val_alp),
      bitArrayBet = createBitArray(val_bet),
      result = 0;
    for (var i = 1; i < regSize; i++)
      result += Math.pow(bitArrayAlp[i] || bitArrayBet[i] ? 2 : 0, i);
    return bitArrayAlp[0] || bitArrayBet[0] ? ++result : result;
  }
  else
    return val_alp | val_bet;
}

function opr_xor(val_alp, val_bet) {
  // one operand out of nsBitOprRange
  if (Math.max(val_alp, val_bet) > nsBitOprRange) {
    var
      bitArrayAlp = createBitArray(val_alp),
      bitArrayBet = createBitArray(val_bet),
      result = 0;
    for (var i = 1; i < regSize; i++)
      result += Math.pow(bitArrayAlp[i] - bitArrayBet[i] ? 2 : 0, i);
    return bitArrayAlp[0] - bitArrayBet[0] ? ++result : result;
  }    
  else
    return val_alp ^ val_bet;
}

function opr_and(val_alp, val_bet) {
  // both operands out of nsBitOprRange
  if (Math.min(val_alp, val_bet) > nsBitOprRange) {
    var
      bitArrayAlp = createBitArray(val_alp),
      bitArrayBet = createBitArray(val_bet),
      result = 0;
    for (var i = 1; i < regSize; i++)
      result += Marh.pow(bitArrayAlp[i] && bitArrayBet[i] ? 2 : 0, i);
    return bitArrayAlp[0] && bitArrayBet[0] ? ++result : result;
  }
  else
    return val_alp & val_bet;
}