Friday 1 April 2016

Part 1: Humble Beginnings

Foreword

In my previous post I gave a brief introduction on what I am planning  in this series of Blog posts on writing an emulator from scratch.

In this post, I will start writing initial code for the emulator with only two instructions implemented.

Keeping it this small will aid in keeping the focus on figuring out what intitial components are required for the emulator.

Writing a basic 6502 Assembler program and converting it to Machine Code

I think a good starting point would be to write a very simple assembly program and take it from there.

So, here it goes:

LDA #$20
STA $08

Next, let us see what this program does.

The first line LDA #$20 basically instruct the CPU to LoaD the Acculator with the hexadecimal value 20.

The second line STA $08 instructs the CPU to store the contents of the accumulator in memory location 8.

Let us know try to convert this assembly listing to machine code.

There is a helpful resource on the internet that provides you with necessary  info for this task:

http://www.masswerk.at/6502/6502_instruction_set.html

Scroll down to the LDA section. For your convenience, I have included it here:

LDA  Load Accumulator with Memory
     M -> A                           N Z C I D V
                                      + + - - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     immediate     LDA #oper     A9    2     2
     zeropage      LDA oper      A5    2     3
     zeropage,X    LDA oper,X    B5    2     4
     absolute      LDA oper      AD    3     4
     absolute,X    LDA oper,X    BD    3     4*
     absolute,Y    LDA oper,Y    B9    3     4*
     (indirect,X)  LDA (oper,X)  A1    2     6
     (indirect),Y  LDA (oper),Y  B1    2     5*


Lets briefly discuss the contents in this section. First, M->A explains the basic operation of the instruction, move memory to accumulator.

Next to M->A, an indication is given on which flags can be possibly effected by this instruction. For this particular instruction there a plus (+) below the N flag and the Z flag, meaning this instruction can potentially set the negative flag or the zero flag. If the number assigned the accumulator is positive, both the negative and zero flag will be set to zero.

Next, a list of opcodes for the various addressing modes is provided. Addressing modes is an important part of the 6502 operation, so in the next part of this series, we will dedicate some time explaining the different addressing modes of the 6502.

For now, we will only identify the address mode applicable to our assembly program. In our case, this is immediate mode, identified by the hash # in the assembly instruction. So the opcode of the first instruction is A9. The first line of our program therefore translates to:

A9 20

Next, let us try to locate the opcode for the second line of our program. Again lets retrieve the section for STA from above mentioned website:

STA  Store Accumulator in Memory
     A -> M                           N Z C I D V
                                      - - - - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     zeropage      STA oper      85    2     3
     zeropage,X    STA oper,X    95    2     4
     absolute      STA oper      8D    3     4
     absolute,X    STA oper,X    9D    3     5
     absolute,Y    STA oper,Y    99    3     5
     (indirect,X)  STA (oper,X)  81    2     6
     (indirect),Y  STA (oper),Y  91    2     6


First, notice no flags are effected by this instruction.

Now, from the list of opcodes there is two possible opcodes that can fullfil the second line of our program: zeropage and absolute.

Zeropage you can use if the applicable memory location falls within the first 256 locations of memory.

Absolute you will use if applicable memory location is at memory location number 256 and above.

The difference between these two modes is that zeropage instruction occupies two bytes in memory( instruction opcode and one byte memory location) and absolute takes up three bytes in memory (instruction opcode and two bytes for memory location)

In our case, for simplicity, we will use zeropage addressing. So the instruction STA $08 will translate to the following:

85 08

Our machine language program is thus: A9 20 85 08

We can now start to write our emulator that will execute this snippet .

Designing

We start off with creating two classes for our emulator: CPU and Memory. We will create a JavaScript file for each one: Memory.js and Cpu.js

Let us focus on the memory part first. The contents of the memory we will implement as an array of numbers. To get the ball rolling, we will hardcode our machine language program to the memory array on memory object instantiation.

With this in mind, create the file Memory.js with a text editor and type the following:

function memory()

{
  var mainMem = new Uint8Array ([0xa9, 0x20, 0x85, 0x08, 0, 0, 0, 0, 0, 0]);
}
 

For the array we we use the Uint8Array type. With this type each element is a single unsigned byte. This closely models the the size of storage elements on a 6502 CPU.

There is an assumption we are making at this point: Our emulator emulator will start executing instructions at memory location 0. This is not the case with a real 6502 CPU, but this assumption is good enough to get the ball rolling.

Back to our memory code. As it stands, the mainMenu array is declared as a private variable. So, we will surface its contents to the other parts of the system with getters and setters. With this implemented, the memory class will look like this:

function memory()

{
  var mainMem = new Uint8Array ([0xa9, 0x20, 0x85, 0x08, 0, 0, 0, 0, 0, 0]);
  this.readMem = function (address) {
    return mainMem[address];
  }

  this.writeMem = function (address, byteval) {
    mainMem[address] = byteval;
  }

}


This is enough of memory class for now.

Next, we should create our cpu class. This will happen in Cpu.js

We start again of what private variables is required for our object state. The first private variable that comes to mind we should add to the cpu class is the accumulator register. In the 6502, there is also two similar registers: X and Y register.

We will also need a register that will keep track of where in memory we are at any point during execution. In the 6502, this called the Program Counter (or PC for short). This register differ a bit from our A, X an Y registers. in that it is 16-bit register instead of a 8-bit register.

At this point our Cpu class will look like this:

function cpu() {
  var acc = 0;
  var x = 0;
  var y = 0;
  var pc = 0;
}




One thing we are still missing, is access for our CPU class to a memory object to retrieve instructions to execute.

The simplest way would be to create another private variable and instantiate there and then. However, there might be other components that might require access to the memory object. The best would therefore be to pass the memory object as a parameter when cpu object is created and assign it to a private variable. The following will do:

function cpu(memory) {
  var localMem = memory;
...
}


Next, we need to decide how we are going to code the logic for our emulator. For now, we will implement a step function in the CPU class. Each time you call this function, it will execute a single instruction. In this function we will do more or less the following:

  • Retrieve instruction opcode from memory at location pointed to by register PC
  • Create a switch statement to decode instruction, with opcode determining execution path
  • Remember to increment PC register each time you retrieve the next element from memory.
Keeping this in mind, our step function will look this:

  this.step = function () {
    var opcode = localMem.readMem(pc);
    pc = pc + 1;
    switch (opcode)
    {
      case 0xa9:
        acc = localMem.readMem(pc);
        pc = pc + 1;
      break;

      case 0x85:
        address = localMem.readMem(pc);
        localMem.writeMem(address, acc);
        pc = pc + 1;
      break;
    }
  }


One remaining thing is to implement the flag operation for the LDA instruction (e.g. opcode A9). We create two private variables for the flags zero and negative and modify the case statement for LDA as follows:

      case 0xa9:
        acc = localMem.readMem(pc);
        zeroflag = (acc == 0) ? 1 : 0;
        negativeflag = ((acc & 0x80) != 0) ? 1 : 0;
        pc = pc + 1;
      break;

I will give a quick explanation for those not familiar with the ?-operator in the two assignments. First of all the ? is called the ternary operator. It gets preceded by a condition. If condition evaluates true, the first value is the effective value, otherwise the second value.

The assignment for zeroflag is fairly straightforward. There might be some doubts regarding the negativeflag assignment, so let me quickly explain. The 6502 uses the most significant bit of a register (left most digit) as negative indicator. If it is zero it is a positive number. If it is a one, it is is negative number. When the value of register changes the 6502 will update the negative status flag with whether the new number is positive number or negative. I will cover more in detail in a future post on how the 6502 works with negative numbers.

Putting everything together

We have now written all the code bits for executing our assembly language we wrote earlier in the article. It is now time to put every thing together.

First let us view our finished off CPU class:


function cpu(memory) {
  var localMem = memory;
  var acc = 0;
  var x = 0;
  var y = 0;
  var pc = 0;
  var zeroflag = 0;
  var negativeflag = 0;

  this.step = function () {
    var opcode = localMem.readMem(pc);
    pc = pc + 1;
    switch (opcode)
    {
      case 0xa9:
        acc = localMem.readMem(pc);
        zeroflag = (acc == 0) ? 1 : 0;
        negativeflag = ((acc & 0x80) != 0) ? 1 : 0;
        pc = pc + 1;
      break;

      case 0x85:
        address = localMem.readMem(pc);
        localMem.writeMem(address, acc);
        pc = pc + 1;
      break;
    }
  }
} 


Next, it is important to remember that a browser can't just execute the javascript on its own. It needs an html page as a starting point.So here is a suitable html page for our javascript code:

<!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>
    <script language="JavaScript">
      var mymem = new memory();
      var mycpu = new cpu(mymem);
      mycpu.step();
      mycpu.step();
      alert(mymem.readMem(8));
    </script>
  </body>
</html>

Save this html file under the name index.html

As you can see, this html file also has an embedded script snippet. It creates an instance of a memory object and CPU object. On the created CPU object we call the step function twice.

The alert function call pops up a message Box with the showing the contents of memory location 8. This will give us an indication of whether our program executed correctly.

Time to start up up our emulator. Save all three files (index.html, Cpu.js and Memory.js) in the same folder and open index.html with a browser. If all goes well, you will see a Message Box popping up showing "32". This is the decimal equivalent of 20.

That is it for this Post!

In Summary

In this Post we created a very simple 6502 emulator in JavaScript.

In the next post I will be explaining the different address modes of the 6502. We will then end it off by extending our emulator to include all the Load instructions (LDA, LDX, LDY) and all the Store instructions (STA, STX, STY) together with all applicable address mode variants of these instructions.

Take care!

No comments:

Post a Comment