For this project, you will be completing the implementation of the Hawknest emulator. This emulator will be built around the 6502v processor, a version of the MOS 6502 CPU which I've modified slightly for our convenience. The v indicates that this CPU supports paravirtual extensions (namely, a new instruction).
The goals of this project are to:
Keep in mind that this is an experimental project, and it is the first time that it is being offered. If we encounter bugs or hiccups, we will work through them together.
You will first need to download the Hawknest skeleton code. We will be working primarily with git, so make sure you are familiar with it and that you have it installed on your system. I would recommend working on this project either on a Linux machine or a Linux VM, ideally something you have sufficient permissions on to install software packages. To get the code, run the following in your VM:
$> git clone http://cs.iit.edu/~khale/class/vm-class/f17/hawknest-skeleton
This will fetch the code from a public git repo. You should see something
like this:
$> cd hawknest-skeleton
$> ls
Makefile README.md cfg/ depend.sh* fonts/ include/ src/
test/
If you try to build the project at this point, it will likely fail because
you haven't installed the requisite libraries. See the next section for how
to proceed.
This section will get you to the point where you can build and run the Hawknest emulator.
Before we build Hawknest, we need to make sure that we have the required graphics libraries installed on the system. These include:
$> sudo dnf install mesa-libGL SDL2 SDL2-devel SDL2_ttf SDL2_ttf-devel
Here, mesa-libGL is the OpenGL package. The rest are for SDL (a graphics
library). Make sure to get the SDL version 2 packages, as version 1 will not
work for us.
Once you have installed the prerequisites, you should be able to build the emulator as follows:
$> make
src/main.o <- src/main.c
src/sys.o <- src/sys.c
src/memctrl.o <- src/memctrl.c
src/cartrom.o <- src/cartrom.c
src/sysrom.o <- src/sysrom.c
src/io.o <- src/io.c
src/shell.o <- src/shell.c
src/mos6502/cpu.o <- src/mos6502/cpu.c
src/mos6502/vmcall.o <- src/mos6502/vmcall.c
src/gui/gui.o <- src/gui/gui.c
src/dev/controller.o <- src/dev/controller.c
Linking...[hawknest <- src/main.o]
You should now have a binary called hawknest
in the main
directory. If you run it without any arguments (or with the -h
flag), you will get a help message.
$> ./hawknest
I've supplied you with a simple test program in the test/
directory in the main source tree. There is a C file called
test.c
and a 6502 assembly file called testwrite.s
which tests a file writing routine. If you look at the
Makefile
in that directory, you can see how this is built in the
same way as the code in your preliminary project, but
using a library called hawknest.lib
and using a special linker
script called hawknest.cfg
. If you want to add your own tests,
a good idea is to just add them to that Makefile
and put the
source in the test directory.
When you compile this testcode by running make
, you'll notice
that two files are produced. One is the cartridge ROM (in this case called
testsuite
and the other is the System ROM binary image (here
called testsuite-sysrom.bin
. You'll need both of these for any
program you compile for Hawknest, and as long as you compile in the same way
as is dictated in the Makefile, the compiler will automatically produce
both.
Once you have your two ROM files, you can run them in Hawknest. For example, with the ROM files above, we could run like this:
$> ./hawknest -c test/testsuite -s test/testsuite-sysrom.bin
This will run Hawknest in graphical mode by default. Notice that nothing
happens. This is because there's no CPU to run the program! You will
implement this, and when you do, you'll start to see some output here. You
can slo run Hawknest in an interactive shell, which is more useful for
debugging purposes:
$> ./hawknest -i -c test/testsuite -s test/testsuite-sysrom.bin
Again, here you can type commands into the interactive shell, but aside from
the memory peek, poke, and dump commands, they won't do anything. The memory
commands will work just fine since I've already implemented the memory
controller, RAM, and ROM subsystems for you.
While working on your project, you may want to have debugging prints that
only appear when you are debugging your CPU. I've provided a macro for you
to achieve this. The macro is called DEBUG_PRINT
. You should
use it just like you would printf
, but it will only produce
output when the code is compiled for debugging. To compile with debug output
enabled, open the file .config
in your editor and change the
line DEBUG_ENABLE
to 1
.
This section will outline some strategies for building your 6502v and how to write readable code.
Before getting started writing code, I would recommend that you spend
some time getting familiar with the codebase. I've already given a few high
level overviews of how the system works, so you should have a vague idea at
this point. The very first piece of code that is executed is in
src/main.c
. This code parses arguments and calls the system
initialization routine, at which point we're off to the races. In
src/sys.c
you'll see calls to routines which you will
implement, e.g. mos6502_init()
and mos6502_step()
.
Unless you're curious, I wouldn't spend too much time worrying about the
graphics code or the controller code, as we won't be dealing with that until
later.
When getting started, the first priority is to get your CPU initialized
(reset) and executing instructions. See the my comment at the top of
cpu.c
for more instructions on how you should proceed. Note
that I've given you some of the function definitions you'll need (primarily
the ones that will be called by other code), and you'll need to fill them
in.
When navigating and working with a large codebase, there are several tools you might want to
get familiar with. The first is ctags
. This is a utility which
creates an index of all the functions and symbols in your source tree, which
you can then use to navigate easily within a text editor like
vi
.
For example, to use ctags
for this project, in the Hawknest
directory, run the following (you may have to install the ctags package):
$> ctags -R
This will create a tags
file in this directory. To use this in
vi or vim, add the following line to your .vimrc
file in your
home directory:
tags=tags;/
Now, within vi or vim, if you hover over a symbol that you want to see the
definition of, e.g. a function, you can navigate to it simply by pressing
ctrl + ]. You can go back to where you were before by typing ctrl + t.
Emacs has similar capabilities.
Other tools that might be useful:
grep
/ack
/the_silver_searcher
- all tools that search for text
strings in a source tree (in order from slowest to fastest)tmux
- useful for having multiple terminal windows open at oncetig
- command-line graphical interface for git
reposAs we progress on this project, I will likely be making updates to the sections of code that you are not working on (e.g. graphics code, interrupt handling, libraries etc.). If I do make updates, I will ask you to get them from the repo using:
$> git pull
Because of this, I would advise against adding code to parts of the system
outside of cpu.c
unless absolutely necessary.
Once you're convinced that your 6502v is complete, you will handin your code as a git patch or as a set of git patches. In order for this to work, you want to commit changes to your code in logical chunks as you go. DO NOT just hand me one big commit containing all your code. Your commits should be clearly described with a log message, and should clearly show authorship. Here is how I make a commit.
...add stuff to git index using git add...
$> git commit --author="Kyle Hale <khale@cs.iit.edu>"
Then include a desriptive line such as "Added implementation of JMP
instruction."
When you're finished, you can turn your commits into patches using git
format-patch
. This will produce several .patch
files
which you should be able to group into a tarball which you can then send to
me directly.
Note that the due date for this assignment is October 13 at 11:59PM.