Thursday 21 April 2016

Part 3: Viewing emulator state

Foreword

In the previous section we implemented all address mode variants of the LOAD ans STORE instructions.

Up to this point in time our emulator doesn't give much information about its state while executing instructions. We will give some attention to this shortfall in this post.

Something else I was busy with the last week or so, was to put the source code of our JavaScript emulator on GitHub.

After each part in my blog series, I will commit my code to GitHub and tag it. Doing it like this you can immediately access the source code covered in any part of my series.

To get to the source code, please follow this link and click releases:




What we are working to

As mentioned previously, in this post we will be jacking up our html page so that we can have more info on the state of our emulator while it is executing instructions.

So, let us start with a picture showing what we will be working to:

As you can see the screen will be divided in the regions. The first region will show to values of all the registers: Accumulator, X, Y and Program counter.

The second region will show a small dump of memory starting at memory location 0. I have plans for future posts that will allow users to specify a different region in memory, but for now this hardcoding will be sufficient.

The last region will show a disassembled version of the next instruction to be executed.

Finally, we will be adding a button called Step, allowing us to single step through a 6502 assembly program and viewing the state of our emulator. This is in contrast with the earlier posts where we actually hardcoded a couple of calls to step().

Getting HTML layout right

Now that we something to work from, let us get the HTML together so a browser will show this layout.

The HTML is fairly straightforward and I present it here:


<!DOCTYPE html>
<html>
  <head>
    <title>6502 Emulator From Scratch</title>
    <script src="Memory.js"></script>
    <script src="Cpu.js"></script>
  </head>
  <body>
    <h1>6502 Emulator From Scratch</h1>
    <p>This is JavaScript Test</p>
<textarea id="registers" name="reg" rows="1" cols="60"></textarea>
<br/>
<textarea id="memory" name="mem" rows="15" cols="60"></textarea>
<br/>
<textarea id="diss" name="diss" rows="11" cols="60"></textarea>
<button>Step</button>
  </body>
</html>

I have highlighted the three regions and the button for clarity.

Let us proceed with a small discussion on this piece of HTML.

First of all, to display each region you will be making use of the html element textarea. This element have a couple of important attributes:
  • id: This property allows JavaScript code to reach a particular element on a html page in order to do some manipulation on a given element, like outputting text
  • rows: specify height of textarea as in number of textlines
  • cols: specify width of textarea as in number of characters
Finally, to display our Step button, we make use of the html element button. For now we will not be adding any attributes to our button. In a later section, however, we will be adding the attribute onclick, which will inform the browser which JavaScript Function to invoke when the button is clicked.

Extracting Emulator State Data

Next, we need to consider how to generate the data for populating our three textareas with.

Register State

Firstly, how do we get a string showing the content of all our registers? We can create a function on our CPU class that will generate this for us. We will call this function getDebugReg. Here is the implementation:


  this.getDebugReg = function ()  {
    var astr = "00" + acc.toString(16); astr = astr.slice(-2);
    var xstr = "00" + x.toString(16); xstr = xstr.slice(-2);
    var ystr = "00" + y.toString(16); ystr = ystr.slice(-2);
    var pcstr = "0000" + pc.toString(16); pcstr = pcstr.slice(-4);
    var result = "";
    result = result + "Acc:" + astr + " X:" + xstr + " Y:" + ystr + " PC:" + pcstr;
    return result;
  } 


Lets quickly discuss this code. First, you will see lots of string concatenations done throughout the method (the plus operator). This is pretty much expected.

Next, you will see also see the frequent use of toString(16). Numbers in JavaScript is provided with the function, which as expected, return the string representation of the number.

When you call the toString method without any parameters, it will return the string representation of the number as a decimal. In our Emulator project, however, we are more interested in dealing with numbers as hexadecimal.

Luckily, JavaScript cater for this requirement. The toString method on a number provides an optional parameter called radix. So, if you call to toString with 16, you get a hexadecimal number. If you wanted to, you could even pass 2 to this method, and you will get back the number in Binary!

You will also notice the use of method call slice() and with a  negative number like slice(-2). Slice(-2) means take only the last two characters of the string. We use this technique to ensure our outputted register values is always two characters in length, padding it with a zero if necessary.This will create a consistent look.

Memory State

As discussed earlier we will output the contents of memory starting at location 0.

Here is a snippet of code that will return the contents of memory as a String:


        tempmemstr = ""
        for (i = 0; i < 160; i++) {
          if ((i % 16) == 0) {
            labelstr = "";
            labelstr = labelstr + "0000" + i.toString(16);
            labelstr = labelstr.slice(-4);

            tempmemstr = tempmemstr + "\n" + labelstr;
          }
          currentByte = "00" + mymem.readMem(i).toString(16);        
          currentByte = currentByte.slice(-2);
          tempmemstr = tempmemstr + " " + currentByte;
        }


This snippet will return a string representing the contents of the first 160 bytes in memory.

The if statement in the code check for every 16th byte. If it is a newline character is inserted to the String (\n) and and a line address added.

Next Instruction Disassembled

To disassemble machine language is not hard. We can write a case statement using address mode as a selector. In each case statement we will then format the operand in the correct way.

What we are missing at the moment is a lookup table that will give us applicable mnemonic for a given opcode. We can adjust our CreateSource program mentioned in my previous blog post to create this table for us.

Here is the resulting table:

const opCodeDesc = 
["BRK", "ORA", "", "", "", "ORA", "ASL", "", "PHP", "ORA", "ASL", "", "", "ORA", "ASL", "", 
"BPL", "ORA", "", "", "", "ORA", "ASL", "", "CLC", "ORA", "", "", "", "ORA", "ASL", "", 
"JSR", "AND", "", "", "BIT", "AND", "ROL", "", "PHP", "AND", "ROL", "", "BIT", "AND", "ROL", "", 
"BMI", "AND", "", "", "", "AND", "ROL", "", "SEC", "AND", "", "", "", "AND", "ROL", "", 
"RTI", "EOR", "", "", "", "EOR", "LSR", "", "PHA", "EOR", "LSR", "", "JMP", "EOR", "LSR", "", 
"BVC", "EOR", "", "", "", "EOR", "LSR", "", "CLI", "EOR", "", "", "", "EOR", "LSR", "", 
"RTS", "ADC", "", "", "", "ADC", "ROR", "", "PLA", "ADC", "ROR", "", "JMP", "ADC", "ROR", "", 
"BVC", "ADC", "", "", "", "ADC", "ROR", "", "SEI", "ADC", "", "", "", "ADC", "ROR", "", 
"", "STA", "", "", "STY", "STA", "STX", "", "DEC", "", "TXA", "", "STY", "STA", "STX", "", 
"BCC", "STA", "", "", "STY", "STA", "STX", "", "TYA", "STA", "TXS", "", "", "STA", "", "", 
"LDY", "LDA", "LDX", "", "LDY", "LDA", "LDX", "", "TAY", "LDA", "TAX", "", "LDY", "LDA", "LDX", "", 
"BCS", "LDA", "", "", "LDY", "LDA", "LDX", "", "CLV", "LDA", "TSX", "", "LDY", "LDA", "LDX", "", 
"CPY", "CMP", "", "", "CPY", "CMP", "DEC", "", "INY", "CMP", "DEC", "", "CPY", "CMP", "DEC", "", 
"BNE", "CMP", "", "", "", "CMP", "DEC", "", "CLD", "CMP", "", "", "", "CMP", "DEC", "", 
"CPX", "SBC", "", "", "CPX", "SBC", "INC", "", "INX", "SBC", "NOP", "", "CPX", "SBC", "INC", "", 
"BEQ", "SBC", "", "", "", "SBC", "INC", "", "SED", "SBC", "", "", "", "SBC", "INC", ""];

We now have enough information to start writing our disassemble method. We will call our method getDecodedStr. The logical place to add this method is within the cpu class, where it will have access to the pc register in order to know where to dissassemble from.

Here is the resulting function:

  this.getDecodedStr = function () {
    opCode = localMem.readMem (pc);
    mode = addressModes[opCode];
    numArgs = instructionLengths[opCode] - 1;
    if (numArgs > 0) {
      argbyte1 = localMem.readMem (pc + 1);
    }

    if (numArgs > 1) {
      argbyte2 = localMem.readMem (pc + 2);
    }
    
    
    address = 0;
    addrStr = "";
    var result = getAsFourDigit(pc);
    result = result + " " + opCodeDesc[opCode] + " ";
    switch (mode) {
      case ADDRESS_MODE_ACCUMULATOR: return 0; 
      break;

      case ADDRESS_MODE_ABSOLUTE: addrStr = getAsFourDigit(argbyte2 * 256 + argbyte1);
        result = result + "$" + addrStr;
        return result;
      break;

      case ADDRESS_MODE_ABSOLUTE_X_INDEXED: addrStr = getAsFourDigit(argbyte2 * 256 + argbyte1);
        result = result + "$" + addrStr + ",X";
        return result;
      break;

      case ADDRESS_MODE_ABSOLUTE_Y_INDEXED: addrStr = getAsFourDigit(argbyte2 * 256 + argbyte1);
        result = result + "$" + addrStr + ",Y";
        return result;
      break;

      case ADDRESS_MODE_IMMEDIATE: addrStr = getAsTwoDigit(argbyte1);
        result = result + "#$" + addrStr;
        return result; 
      break;

      case ADDRESS_MODE_IMPLIED:
      break;

      case ADDRESS_MODE_INDIRECT:
        tempAddress = (argbyte2 * 256 + argbyte1);
        return (localMem.readMem(tempAddress + 1) * 256 + localMem.readMem(tempAddress));
      break;

      case ADDRESS_MODE_X_INDEXED_INDIRECT:
        addrStr = getAsTwoDigit(argbyte2 * 256 + argbyte1);
        result = result + "($" + addrStr + ",X)";
        return result;      break;

      case ADDRESS_MODE_INDIRECT_Y_INDEXED:
        addrStr = getAsTwoDigit(argbyte1);
        result = result + "($" + addrStr + "),Y";
        return result;
      break;

      case ADDRESS_MODE_RELATIVE:
      break;

      case ADDRESS_MODE_ZERO_PAGE:
        addrStr = getAsTwoDigit(argbyte1);
        result = result + "$" + addrStr;
        return result;
      break;

      case ADDRESS_MODE_ZERO_PAGE_X_INDEXED:
        addrStr = getAsTwoDigit(argbyte1);
        result = result + "$" + addrStr + ",X";
        return result;
      break;

      case ADDRESS_MODE_ZERO_PAGE_Y_INDEXED:
        addrStr = getAsTwoDigit(argbyte1);
        result = result + "$" + addrStr + ",Y";
        return result;
      break;

    }
  }

When looking into this function, you will see some similarities to our step function and calcEffectiveAdd function.

You will also see the use of the functions getAsTwoDigit and getAsFourDigit. This a glorified toString-hex function that will return the number as a string padded with zeros where applicable. Here is the implementation of getAsTwoDigit and getAsFourDigit:

 function getAsTwoDigit(number) {
    result = "00" + number.toString(16);
    result = result.slice(-2);
    return result;
  }

  function getAsFourDigit(number) {
    result = "0000" + number.toString(16);
    result = result.slice(-4);
    return result;
  }


Putting it all together

Ok, let's put everything together.

Firstly, lets add a script tag to the end of our html as presented in the beginning of this post.

<!DOCTYPE html>
<html>
  <head>
    <title>6502 Emulator From Scratch</title>
    <script src="Memory.js"></script>
    <script src="Cpu.js"></script>
  </head>
  <body>
    <h1>6502 Emulator From Scratch</h1>
    <p>This is JavaScript Test</p>
<textarea id="registers" name="reg" rows="1" cols="60"></textarea>
<br/>
<textarea id="memory" name="mem" rows="15" cols="60"></textarea>
<br/>
<textarea id="diss" name="diss" rows="11" cols="60"></textarea>
<button>Step</button>

    <script language="JavaScript">
      var mymem = new memory();
      var mycpu = new cpu(mymem);
    </script>

  </body>
</html>

When compared to the html as of end of Part 1 of this series, you will that I stripped out hardcoded calls to step and the alert.

As mentioned previously the button will invoke the step() function when you click it. So, we will need to add a onClick attribute on our button element. So, your button tag element will now look like this:

<button onclick="step()">Step</button>

Please note that this step function is not the step function on the cpu class. We still need to declare this step function in the script tag of our html page. One of the tasks of this method, however, will be to call the step function on the cpu class. For starters, our step function will look like this

    <script language="JavaScript">
      var mymem = new memory();
      var mycpu = new cpu(mymem);
      function step() {
        mycpu.step();
      }

    </script>

This is not the only thing we want our step method todo. It should also populate our textareas with the the current state of our emulator.

Let's start with our register textarea. First we should give JavaScript a handle to this element on the page. This is where the id attribute of the textarea element come in handy:

var t = document.getElementById("registers");

We are now free to access all properties of the registers textarea. The property we are interested in is value. This property allows you to read or set the string contents of the cell.

Therefore, to populate this textarea with the contents of the cpu registers you will issue:

t.value = mycpu.getDebugReg();

You need to do a similar exercise to populate the textarea showing next instruction to be executed:

        var ins = document.getElementById("diss");
        ins.value = mycpu.getDecodedStr();

Finally, we need to populate the memory dump text area. We include memory dump code snippet inline in our script tag:

        var m = document.getElementById("memory");
        tempmemstr = ""
        for (i = 0; i < 160; i++) {
          if ((i % 16) == 0) {
            labelstr = "";
            labelstr = labelstr + "0000" + i.toString(16);
            labelstr = labelstr.slice(-4);
            tempmemstr = tempmemstr + "\n" + labelstr;
          }
          currentByte = "00" + mymem.readMem(i).toString(16);        
          currentByte = currentByte.slice(-2);
          tempmemstr = tempmemstr + " " + currentByte;
        }
        m.value = tempmemstr;

In Summary

In this part we extended the html page of our emulator so that more state is visible. You can find the source code for this Part via this link: https://github.com/ovalcode/c64jscript/releases/tag/ch3

In the next part we will be implementing the ADd with Carry and Subtract with Carry instructions in our emulator.

Till next time!

No comments:

Post a Comment