Foreword
In the previous post we have setup the Android Studio development and create a very simple screen for our emulator.
This screen was just an empty shell with no code implement at all behind the buttons.
In this blog we are going to add a bit more meat to our application. In end of this blog our application should be able to execute a very simple 6502 machine language program.
But, before we start coding, I am just going to show how we can run our application on a real Android device, as promised in the previous post.
Running our app on a real Android device
In my previous blog we got our Android application to run within an emulator.Let us now try to get this application to run on a real Android Device.
The instructions I am going to present will be again for a Linux based operating system.
Before we start, we need to enable adb debugging on the Android Device. On your device go to Settings/About device. One of the items in this screen is Build Number:
Tap this item 7 times. This will make a new menu item visible in the previous screen called Developer Options:
Select this option and on the screen that pops up, ensure that USB debugging is selected:
Next, we need to do some configuration on the Linux side. As a root user, create the following file: /etc/udev/rules.d/51-android.rules
The contents of this file should be as follows:
SUBSYSTEM=="usb", ATTR{idVendor}=="04e8", MODE="0666", GROUP="plugdev"
Just a note on the contents. The 04e8 is the USB vendor ID. This application ID is the USB vendor ID for a Samsung device. On the Android developer site is a list of the vendors which you can use to find your particular device.
With this file created, execute the following command, also as a root user:
chmod a+r /etc/udev/rules.d/51-android.rules
Finally, reboot your PC. Your installation of Android Studio will now work with your device.
There is just a small quirk you should be aware of. Start Android Studio with the following steps:
- Ensure that your Android Device is unplugged
- Start up Android Studio
- Ensure that the screen of your android device is unlocked and the display visible (e.g. not in standby mode)
- Now plug in your Android device
- After some time there will be a popup on your Android device, showing a finger print value and whether you want to accept the device. Just tap on yes.
- Within Android studio, your Android device now will be available as an online device.
Adding Instructions
Up to this point in time our Emulator application is only an empty shell. It has buttons, but no code behind it.In this section we will start to add some meat to our application.
For adding the meat I will follow the same approach as in my Javascript emulator, here. So, we start off by creating a Memory class and a CPU class.
First, we start with the Memory class:
package com.johan.emulator; /** * Created by johan on 2016/08/18. */ public class Memory { private byte[] mainMem = {(byte) 0xa9, 0x20, (byte)0x85,0x8,0,0,0,0,0,0,0}; public int readMem(int address) { return mainMem[address] & 0xff; } public void writeMem (int address, int value) { mainMem[address] = (byte) (value & 0xff); } public String getMemDump() { String result = ""; byte[] temp = new byte[48]; for (int i=0; i < mainMem.length; i++) { temp[i] = mainMem[i]; } for (int i = 0; i < temp.length; i++) { if ((i % 16) == 0) { String numberStr = Integer.toHexString(i); numberStr = "0000" + numberStr; result = result + "\n" + numberStr.substring(numberStr.length() - 4); } String number = "0" + Integer.toHexString(temp[i] & 0xff); number = number.substring(number.length() - 2); result = result + " " + number; } return result; } }
As seen from the package name, this class lives in the same package as our Android MainActivity class.
Let us go into the details of this class. You will see I have defined an array called mainMem. Within it I have actually defined a very small 6502 machine code program. More on this in a moment.
You will also notice that for some elements within the array I have preceded with (byte). The reason for this is that the byte primitive type in Java is a signed data type limiting the range to -128..127. The hexadecimal values a9 and 85 is outside this range, henge the casting to a byte. Obviously when you inspect the value of these locations, it will be presented as a negative value.
Just to avoid some confusion. To save space, we have defined the memory array as a byte array. Otherwise, when working with the content of a memory location, we work with integers. This is evident in the readMem and writeMem methods.
One thing you will also notice is that when we return the contents of a memory location with readMem we first AND the contents with ff. Reason for this is because of the signed nature of byte.
Finally, the getMemDump return the contents of the memory as a string. We will use this method to populate our Android screen.
Let us zoom into the machine language program stored in the array mainMem. If we convert this program to assembly language, it will look as follows:
LDA #$20 STA $08
It first load the CPU register accumulator with the value $20 and then store this value at memory location.
Of course we haven't implemented CPU functionality yet, so let us create another class callled Cpu:
package com.johan.emulator; /** * Created by johan on 2016/08/18. */ public class Cpu { private Memory mem; private int acc = 0; private int xReg = 0; private int yReg = 0; private int pc = 0; private int zeroFlag; private int negativeFlag; public Cpu(Memory mem) { this.mem = mem; } public void step() { int opCode = mem.readMem(pc) & 0xff; int address; pc++; switch (opCode) { case 0xa9: acc = mem.readMem(pc); zeroFlag = (acc == 0) ? 1 : 0; negativeFlag = ((acc & 0x80) != 0) ? 1 : 0; pc++; break; case 0x85: address = mem.readMem(pc); mem.writeMem(address, acc); pc++; break; } } }
This class contain some private variables storing the state of the Cpu. One of these variables, for instance is the Accumulator, which I mentioned just now that our machine code program uses.
The Cpu retrieves the machine code instructions from Main mem, so part of its contructor parameters is the Memory object.
The step method is where all the magic is happening. In effect our step button on the screen should be calling this method. As you can, we currently have implemented only two instructions, opcode A9 and opCode 85.
We will be adding more instructions in comming blog posts.
To keep focussed, I am not going to go into more details on what the step method does. If you wish to get more info please read part 1 of my JavaScript emulator series, here.
Next, we should wire the Cpu class and the Memory class to the Step button. Firstly, we need to we need to create a private variable for both within the MainActivity class:
package com.johan.emulator; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.View; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; public class MainActivity extends AppCompatActivity { ... private Memory mem = new Memory(); private Cpu myCpu = new Cpu(mem); ... }
Now we need to define a click event for the step button, also within the MainActivity class:
package com.johan.emulator; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.View; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; public class MainActivity extends AppCompatActivity { ... private void refreshControls() { TextView view = (TextView) findViewById(R.id.memoryDump); view.setText(mem.getMemDump()); } public void onStepClick(View v) { myCpu.step(); refreshControls(); } ... }
The onStepClick method calls the step method on the Cpu class, which executes one Cpu instruction. refreshControls then refresh the visual controls with the new contents of the memory.
Finally we need to assign the onStepClick method to the Step button. We do this within the layout xml file:
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="STEP" android:id="@+id/button2" android:layout_row="3" android:layout_column="0" android:onClick="onStepClick" />
The Test Run
With all the code changes let us give our emulator a test run. Currently we don't show the state of the CPU, but we do show the content of the memory after each instruction.So, if we click the step button twice, we can expect the following output after each step:
So, we know that our simple machine language instruction works!
This conclude this blog post. I just would like to mention that I made available the source code for this blog on Github here. If you click ch2, you will be able to download the source code as a zip file.
I will try to follow this convention for each of the following blog posts.
In Summary
In this blog we added some meat to our Android emulator application. We ended off with a version capable of running a very simple 6502 program.We also managed to get the application to run on a real Android device.
In the next blog we will be exploring the NDK. This is ultimately where we want to head for. The NDK allows you to create a application running on the native instruction set of the physical hardware of your Android device. This will hopefully give us a performance boost.
Till next time!
No comments:
Post a Comment